deep-risk/backend/app/api/v1/endpoints/risk_detection.py
2025-12-14 20:08:27 +08:00

1005 lines
35 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
风险检测API路由
提供规则管理、任务管理、检测执行和结果查询接口
"""
from typing import List, Dict, Any, Optional
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.ext.asyncio import AsyncSession
from loguru import logger
from app.database import get_async_session
from app.schemas.risk_detection import (
DetectionRuleCreate,
DetectionRuleUpdate,
DetectionRuleResponse,
DetectionTaskCreate,
DetectionTaskResponse,
DetectionExecutionRequest,
DetectionExecutionResponse,
DetectionResultResponse,
DetectionSummaryResponse,
)
from app.services.risk_detection.task_manager import TaskManager, DetectionScheduler
from app.services.entity_service import EntityService
from app.utils.helpers import get_current_user
from app.models.user import User
router = APIRouter()
@router.post("/rules", response_model=DetectionRuleResponse, status_code=status.HTTP_201_CREATED)
async def create_rule(
rule: DetectionRuleCreate,
db: AsyncSession = Depends(get_async_session),
):
"""
创建检测规则
"""
logger.info(f"创建检测规则: {rule.rule_name}")
# TODO: 实现创建检测规则的逻辑
# 这里需要将Schema转换为模型并保存到数据库
return {
"rule_id": "rule_123",
"rule_name": rule.rule_name,
"algorithm_code": rule.algorithm_code,
"description": rule.description,
"parameters": rule.parameters,
"is_enabled": rule.is_enabled,
"created_at": "2024-01-01T00:00:00",
}
@router.get("/rules", response_model=List[DetectionRuleResponse])
async def list_rules(
algorithm_code: Optional[str] = Query(None, description="算法编码"),
is_enabled: Optional[bool] = Query(None, description="是否启用"),
db: AsyncSession = Depends(get_async_session),
):
"""
查询检测规则列表
"""
logger.info(f"查询检测规则: algorithm={algorithm_code}, enabled={is_enabled}")
# 获取算法列表并转换为规则格式
algorithms = await get_algorithm_list()
rules = []
for algo in algorithms:
# 根据算法代码过滤
if algorithm_code and algo['code'] != algorithm_code:
continue
# 创建规则对象
rule = {
"rule_id": f"rule_{algo['code'].lower()}",
"rule_name": algo['name'],
"algorithm_code": algo['code'],
"description": algo.get('description', ''),
"parameters": {param['name']: param.get('default', '') for param in algo.get('parameters', [])},
"is_enabled": is_enabled if is_enabled is not None else True,
"created_at": "2024-01-01T00:00:00",
}
rules.append(rule)
logger.info(f"返回规则列表,共 {len(rules)} 个规则")
return rules
async def get_algorithm_list():
"""
获取算法列表(模拟)
在实际项目中,这些数据可能存储在配置文件或数据库中
"""
algorithms = [
{
"code": "REVENUE_INTEGRITY_CHECK",
"name": "收入完整性检测",
"description": "检测平台充值与申报收入的匹配度",
"parameters": [
{
"name": "streamer_id",
"label": "主播ID",
"type": "string",
"required": True,
"placeholder": "请输入主播ID",
"default": "",
},
{
"name": "comparison_type",
"label": "比较类型",
"type": "select",
"required": False,
"default": "monthly",
"options": [
{"label": "月度", "value": "monthly"},
{"label": "季度", "value": "quarterly"},
{"label": "年度", "value": "yearly"}
],
}
]
},
{
"code": "PRIVATE_ACCOUNT_DETECTION",
"name": "私户收款检测",
"description": "识别使用私人账户收款的风险",
"parameters": [
{
"name": "account_no",
"label": "银行账号",
"type": "string",
"required": True,
"placeholder": "请输入银行账号",
"default": "",
},
{
"name": "threshold_amount",
"label": "私户转账金额阈值",
"type": "number",
"required": False,
"default": 10000,
}
]
},
{
"code": "INVOICE_FRAUD_DETECTION",
"name": "发票虚开检测",
"description": "检查发票与实际业务的匹配度",
"parameters": [
{
"name": "seller_tax_no",
"label": "销售方税号",
"type": "string",
"required": True,
"placeholder": "请输入销售方税号",
"default": "",
},
{
"name": "threshold_rate",
"label": "发票与订单差异率阈值",
"type": "number",
"required": False,
"default": 0.1,
}
]
},
{
"code": "EXPENSE_ANOMALY_DETECTION",
"name": "成本费用异常检测",
"description": "识别虚增成本、费用异常等问题",
"parameters": [
{
"name": "entity_id",
"label": "实体ID",
"type": "string",
"required": True,
"placeholder": "请输入实体ID",
"default": "",
},
{
"name": "entity_type",
"label": "实体类型",
"type": "select",
"required": False,
"default": "streamer",
"options": [
{"label": "主播", "value": "streamer"},
{"label": "MCN机构", "value": "mcn"},
{"label": "纳税人", "value": "taxpayer"}
],
}
]
},
{
"code": "TAX_RISK_ASSESSMENT",
"name": "税务风险综合评估",
"description": "综合分析各项检测结果",
"parameters": [
{
"name": "entity_id",
"label": "实体ID",
"type": "string",
"required": True,
"placeholder": "请输入实体ID",
"default": "",
},
{
"name": "include_algorithms",
"label": "包含算法",
"type": "checkbox-group",
"required": False,
"default": ["REVENUE_INTEGRITY_CHECK", "PRIVATE_ACCOUNT_DETECTION"],
"options": [
{"label": "收入完整性检测", "value": "REVENUE_INTEGRITY_CHECK"},
{"label": "私户收款检测", "value": "PRIVATE_ACCOUNT_DETECTION"},
{"label": "发票虚开检测", "value": "INVOICE_FRAUD_DETECTION"},
{"label": "成本费用异常检测", "value": "EXPENSE_ANOMALY_DETECTION"}
],
}
]
}
]
return algorithms
@router.get("/rules/{rule_id}", response_model=DetectionRuleResponse)
async def get_rule(
rule_id: str,
db: AsyncSession = Depends(get_async_session),
):
"""
获取检测规则详情
"""
logger.info(f"获取检测规则: {rule_id}")
# TODO: 实现获取检测规则的逻辑
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"规则不存在: {rule_id}",
)
@router.put("/rules/{rule_id}", response_model=DetectionRuleResponse)
async def update_rule(
rule_id: str,
rule_update: DetectionRuleUpdate,
db: AsyncSession = Depends(get_async_session),
):
"""
更新检测规则
"""
logger.info(f"更新检测规则: {rule_id}")
# TODO: 实现更新检测规则的逻辑
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"规则不存在: {rule_id}",
)
@router.delete("/rules/{rule_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_rule(
rule_id: str,
db: AsyncSession = Depends(get_async_session),
):
"""
删除检测规则
"""
logger.info(f"删除检测规则: {rule_id}")
# TODO: 实现删除检测规则的逻辑
@router.post("/tasks", response_model=DetectionTaskResponse, status_code=status.HTTP_201_CREATED)
async def create_task(
task: DetectionTaskCreate,
db: AsyncSession = Depends(get_async_session),
current_user: User = Depends(get_current_user),
):
"""
创建检测任务
"""
logger.info(f"创建检测任务: {task.task_name}")
try:
from app.models.risk_detection import TaskType
# 处理自动实体绑定
entity_ids = task.entity_ids
entity_type = task.entity_type
# 如果没有提供实体信息,自动绑定
if not entity_ids or not entity_type:
logger.info(f"用户 {current_user.username} 未提供实体信息,使用自动绑定")
entity_service = EntityService(db)
# 获取用户关联的实体
user_entities = await entity_service.get_user_entities(current_user)
if not user_entities:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"用户 {current_user.username} 未关联任何实体,请联系管理员设置用户实体关联"
)
entity_ids = user_entities
entity_type = "streamer" if current_user.entity_type == "streamer" else "mcn"
logger.info(f"自动绑定实体: {entity_ids}, 类型: {entity_type}")
task_manager = TaskManager(db)
created_task = await task_manager.create_task(
task_name=task.task_name,
task_type=TaskType(task.task_type),
entity_ids=entity_ids,
entity_type=entity_type,
period=task.period,
rule_ids=task.rule_ids,
parameters=task.parameters,
)
return {
"task_id": created_task.task_id,
"task_name": created_task.task_name,
"task_type": created_task.task_type.value,
"status": created_task.status.value,
"entity_type": created_task.entity_type,
"period": created_task.period,
"total_entities": created_task.total_entities,
"processed_entities": created_task.processed_entities,
"result_count": created_task.result_count,
"parameters": created_task.parameters,
"created_at": created_task.created_at.isoformat(),
"started_at": created_task.started_at.isoformat() if created_task.started_at else None,
"completed_at": created_task.completed_at.isoformat() if created_task.completed_at else None,
}
except HTTPException:
raise
except Exception as e:
logger.error(f"创建检测任务失败: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"创建任务失败: {str(e)}",
)
@router.post("/tasks/{task_id}/execute", response_model=DetectionExecutionResponse)
async def execute_task(
task_id: str,
db: AsyncSession = Depends(get_async_session),
):
"""
执行检测任务
"""
logger.info(f"执行检测任务: {task_id}")
try:
task_manager = TaskManager(db)
result = await task_manager.execute_task(task_id)
return {
"task_id": result["task_id"],
"status": result["status"],
"summary": result["summary"],
"result_count": len(result.get("results", [])),
"executed_at": result["executed_at"],
}
except Exception as e:
logger.error(f"执行检测任务失败: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"执行任务失败: {str(e)}",
)
@router.get("/tasks", response_model=List[DetectionTaskResponse])
async def list_tasks(
status: Optional[str] = Query(None, description="任务状态"),
task_type: Optional[str] = Query(None, description="任务类型"),
limit: int = Query(100, description="返回数量限制"),
db: AsyncSession = Depends(get_async_session),
):
"""
查询检测任务列表
"""
logger.info(f"查询检测任务: status={status}, type={task_type}")
try:
task_manager = TaskManager(db)
from app.models.risk_detection import TaskStatus, TaskType
status_filter = TaskStatus(status) if status else None
type_filter = TaskType(task_type) if task_type else None
tasks = await task_manager.list_tasks(
status=status_filter,
task_type=type_filter,
limit=limit,
)
return [
{
"task_id": task.task_id,
"task_name": task.task_name,
"task_type": task.task_type.value,
"status": task.status.value,
"entity_type": task.entity_type,
"period": task.period,
"total_entities": task.total_entities,
"processed_entities": task.processed_entities,
"result_count": task.result_count,
"parameters": task.parameters,
"created_at": task.created_at.isoformat(),
"started_at": task.started_at.isoformat() if task.started_at else None,
"completed_at": task.completed_at.isoformat() if task.completed_at else None,
}
for task in tasks
]
except Exception as e:
logger.error(f"查询检测任务失败: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"查询任务失败: {str(e)}",
)
@router.get("/tasks/{task_id}", response_model=DetectionTaskResponse)
async def get_task(
task_id: str,
db: AsyncSession = Depends(get_async_session),
):
"""
获取检测任务详情
"""
logger.info(f"获取检测任务: {task_id}")
try:
task_manager = TaskManager(db)
task = await task_manager.get_task(task_id)
if not task:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"任务不存在: {task_id}",
)
return {
"task_id": task.task_id,
"task_name": task.task_name,
"task_type": task.task_type.value,
"status": task.status.value,
"entity_type": task.entity_type,
"period": task.period,
"total_entities": task.total_entities,
"processed_entities": task.processed_entities,
"result_count": task.result_count,
"summary": task.summary,
"parameters": task.parameters,
"error_message": task.error_message,
"created_at": task.created_at.isoformat(),
"started_at": task.started_at.isoformat() if task.started_at else None,
"completed_at": task.completed_at.isoformat() if task.completed_at else None,
}
except HTTPException:
raise
except Exception as e:
logger.error(f"获取检测任务失败: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"获取任务失败: {str(e)}",
)
@router.post("/execute", response_model=DetectionExecutionResponse)
async def execute_detection(
request: DetectionExecutionRequest,
db: AsyncSession = Depends(get_async_session),
current_user: User = Depends(get_current_user),
):
"""
执行风险检测
自动获取当前用户关联的所有实体进行检测
"""
logger.info(
f"执行风险检测: user={current_user.username}, "
f"entity={request.entity_id}, period={request.period}, rules={len(request.rule_ids)}"
)
try:
from app.models.risk_detection import TaskType
# 获取实体服务
entity_service = EntityService(db)
# 如果请求中未提供entity_id自动使用当前用户的实体
target_entity_id = request.entity_id
target_entity_type = request.entity_type
# 如果未提供entity_id自动从当前用户获取
if not target_entity_id:
if not current_user.entity_id or not current_user.entity_type:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="当前用户未关联任何实体,请先完成企业认证"
)
target_entity_id = current_user.entity_id
target_entity_type = current_user.entity_type
logger.info(
f"自动使用当前用户的实体: {target_entity_id} ({target_entity_type})"
)
# 获取当前用户的实体列表(用于检测范围限制)
user_entities = await entity_service.get_user_entities_by_user(current_user)
if not user_entities:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="当前用户没有可检测的实体"
)
logger.info(
f"当前用户共有 {len(user_entities)} 个实体,"
f"将检测实体: {target_entity_id}"
)
# 如果是MCN用户且未指定entity_id检测所有关联主播
if current_user.entity_type == "mcn" and not request.entity_id:
# 检测所有主播
entity_ids = [
e["entity_id"]
for e in user_entities
if e["entity_type"] == "streamer"
]
if not entity_ids:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="当前MCN机构下没有活跃的主播"
)
logger.info(f"MCN用户自动检测所有主播: {len(entity_ids)}")
# 批量检测所有主播
task_manager = TaskManager(db)
task = await task_manager.create_task(
task_name=request.task_name
or f"即时检测-{current_user.username}-{request.period}",
task_type=TaskType.ON_DEMAND,
entity_ids=entity_ids,
entity_type="streamer", # 批量检测都是主播类型
period=request.period,
rule_ids=request.rule_ids,
parameters=request.parameters,
)
# 执行任务
result = await task_manager.execute_task(task.task_id)
return {
"task_id": result["task_id"],
"status": result["status"],
"summary": result["summary"],
"result_count": len(result.get("results", [])),
"executed_at": result["executed_at"],
}
else:
# 检测单个实体主播或MCN
task_manager = TaskManager(db)
task = await task_manager.create_task(
task_name=request.task_name
or f"即时检测-{target_entity_id}-{request.period}",
task_type=TaskType.ON_DEMAND,
entity_ids=[target_entity_id],
entity_type=target_entity_type,
period=request.period,
rule_ids=request.rule_ids,
parameters=request.parameters,
)
# 执行任务
result = await task_manager.execute_task(task.task_id)
return {
"task_id": result["task_id"],
"status": result["status"],
"summary": result["summary"],
"result_count": len(result.get("results", [])),
"executed_at": result["executed_at"],
}
except HTTPException:
raise
except Exception as e:
logger.error(f"执行风险检测失败: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"执行检测失败: {str(e)}",
)
@router.get("/results", response_model=List[DetectionResultResponse])
async def list_results(
task_id: Optional[str] = Query(None, description="任务ID"),
entity_id: Optional[str] = Query(None, description="实体ID"),
entity_type: Optional[str] = Query(None, description="实体类型"),
risk_level: Optional[str] = Query(None, description="风险等级"),
limit: int = Query(100, description="返回数量限制"),
db: AsyncSession = Depends(get_async_session),
):
"""
查询检测结果列表
"""
from app.models.risk_detection import DetectionResult
from sqlalchemy import select, and_
logger.info(
f"查询检测结果: task={task_id}, entity={entity_id}, "
f"level={risk_level}, limit={limit}"
)
try:
# 构建查询条件
conditions = []
if task_id:
conditions.append(DetectionResult.task_id == task_id)
if entity_id:
conditions.append(DetectionResult.entity_id == entity_id)
if entity_type:
conditions.append(DetectionResult.entity_type == entity_type)
if risk_level:
conditions.append(DetectionResult.risk_level == risk_level)
# 执行查询
stmt = select(DetectionResult)
if conditions:
stmt = stmt.where(and_(*conditions))
stmt = stmt.order_by(DetectionResult.detected_at.desc()).limit(limit)
result = await db.execute(stmt)
results = result.scalars().all()
# 转换为响应格式
response_list = []
for r in results:
# 构建结果项
result_item = {
"entity_id": r.entity_id,
"entity_type": r.entity_type,
"risk_level": r.risk_level.value if r.risk_level else "UNKNOWN",
"risk_score": r.risk_score or 0.0,
"description": r.description or "",
"suggestion": r.suggestion or "",
"risk_data": r.risk_data or {},
"evidence": r.evidence or [],
"detected_at": r.detected_at.isoformat() if r.detected_at else None,
}
response_list.append({
"id": r.id,
"task_id": r.task_id,
"rule_id": r.rule_id or "", # 如果为None则返回空字符串
"result": result_item
})
logger.info(f"查询到 {len(response_list)} 条检测结果")
return response_list
except Exception as e:
logger.error(f"查询检测结果失败: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"查询检测结果失败: {str(e)}",
)
@router.get("/results/{result_id}", response_model=DetectionResultResponse)
async def get_result(
result_id: int,
db: AsyncSession = Depends(get_async_session),
):
"""
获取检测结果详情
"""
from app.models.risk_detection import DetectionResult
from sqlalchemy import select
logger.info(f"获取检测结果: {result_id}")
try:
stmt = select(DetectionResult).where(DetectionResult.id == result_id)
result = await db.execute(stmt)
r = result.scalar_one_or_none()
if not r:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"结果不存在: {result_id}",
)
# 构建结果项
result_item = {
"entity_id": r.entity_id,
"entity_type": r.entity_type,
"risk_level": r.risk_level.value if r.risk_level else "UNKNOWN",
"risk_score": r.risk_score or 0.0,
"description": r.description or "",
"suggestion": r.suggestion or "",
"risk_data": r.risk_data or {},
"evidence": r.evidence or [],
"detected_at": r.detected_at.isoformat() if r.detected_at else None,
}
return {
"id": r.id,
"task_id": r.task_id,
"rule_id": r.rule_id or "", # 如果为None则返回空字符串
"result": result_item
}
except HTTPException:
raise
except Exception as e:
logger.error(f"获取检测结果失败: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"获取检测结果失败: {str(e)}",
)
@router.get("/summary", response_model=DetectionSummaryResponse)
async def get_detection_summary(
entity_id: str = Query(..., description="实体ID"),
entity_type: str = Query(..., description="实体类型"),
period: str = Query(..., description="检测期间"),
db: AsyncSession = Depends(get_async_session),
):
"""
获取检测结果汇总
"""
from app.models.risk_detection import DetectionResult, DetectionTask
from sqlalchemy import select, and_, func, distinct
logger.info(f"获取检测汇总: entity={entity_id}, period={period}")
try:
# 查询该实体在指定期间的所有检测结果
stmt = select(DetectionResult).where(
and_(
DetectionResult.entity_id == entity_id,
DetectionResult.entity_type == entity_type,
)
)
result = await db.execute(stmt)
results = result.scalars().all()
if not results:
return {
"entity_id": entity_id,
"entity_type": entity_type,
"period": period,
"total_detections": 0,
"risk_distribution": {},
"avg_risk_score": 0.0,
"high_risk_count": 0,
"recommendations": [],
}
# 统计风险分布
risk_distribution = {}
total_score = 0.0
high_risk_count = 0
for r in results:
level = r.risk_level.value if r.risk_level else "UNKNOWN"
risk_distribution[level] = risk_distribution.get(level, 0) + 1
score = r.risk_score or 0.0
total_score += score
if level in ["CRITICAL", "HIGH"]:
high_risk_count += 1
avg_risk_score = total_score / len(results) if results else 0.0
# 生成建议
recommendations = []
if risk_distribution.get("CRITICAL", 0) > 0:
recommendations.append("发现极高风险项目,建议立即处理")
if risk_distribution.get("HIGH", 0) > 0:
recommendations.append("发现高风险项目,建议优先处理")
if risk_distribution.get("MEDIUM", 0) > 0:
recommendations.append("发现中等风险项目,建议安排时间处理")
if risk_distribution.get("LOW", 0) > 0:
recommendations.append("存在低风险项目,建议定期关注")
if not recommendations:
recommendations.append("未发现明显风险,继续保持")
return {
"entity_id": entity_id,
"entity_type": entity_type,
"period": period,
"total_detections": len(results),
"risk_distribution": risk_distribution,
"avg_risk_score": round(avg_risk_score, 2),
"high_risk_count": high_risk_count,
"recommendations": recommendations,
}
except Exception as e:
logger.error(f"获取检测汇总失败: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"获取检测汇总失败: {str(e)}",
)
@router.get("/algorithms", response_model=List[Dict[str, Any]])
async def get_algorithms():
"""
获取算法列表和参数配置
"""
logger.info("获取算法列表")
algorithms = [
{
"code": "REVENUE_INTEGRITY_CHECK",
"name": "收入完整性检测",
"description": "检测平台充值与申报收入的匹配度",
"parameters": [
{
"name": "streamer_id",
"label": "主播ID",
"type": "string",
"required": True,
"placeholder": "请输入主播ID",
"description": "要检测的主播唯一标识"
},
{
"name": "comparison_type",
"label": "比较类型",
"type": "select",
"required": False,
"default": "monthly",
"options": [
{"label": "月度", "value": "monthly"},
{"label": "季度", "value": "quarterly"},
{"label": "年度", "value": "yearly"}
],
"description": "收入比较的时间维度"
}
]
},
{
"code": "PRIVATE_ACCOUNT_DETECTION",
"name": "私户收款检测",
"description": "识别使用私人账户收款的风险",
"parameters": [
{
"name": "account_no",
"label": "银行账号",
"type": "string",
"required": True,
"placeholder": "请输入银行账号",
"description": "要检测的银行账号"
},
{
"name": "threshold_amount",
"label": "私户转账金额阈值(元)",
"type": "number",
"required": False,
"default": 10000,
"min": 0,
"step": 1000,
"description": "超过此金额的私户转账将被标记为风险"
}
]
},
{
"code": "INVOICE_FRAUD_DETECTION",
"name": "发票虚开检测",
"description": "检查发票与实际业务的匹配度",
"parameters": [
{
"name": "seller_tax_no",
"label": "销售方税号",
"type": "string",
"required": True,
"placeholder": "请输入销售方税号",
"description": "销售方的税务登记号"
},
{
"name": "threshold_rate",
"label": "发票与订单差异率阈值",
"type": "number",
"required": False,
"default": 0.1,
"min": 0,
"max": 1,
"step": 0.05,
"description": "超过此差异率将被标记为异常"
}
]
},
{
"code": "EXPENSE_ANOMALY_DETECTION",
"name": "成本费用异常检测",
"description": "识别虚增成本、费用异常等问题",
"parameters": [
{
"name": "entity_id",
"label": "实体ID",
"type": "string",
"required": True,
"placeholder": "请输入实体ID",
"description": "要检测的实体唯一标识"
},
{
"name": "entity_type",
"label": "实体类型",
"type": "select",
"required": False,
"default": "streamer",
"options": [
{"label": "主播", "value": "streamer"},
{"label": "MCN机构", "value": "mcn"},
{"label": "纳税人", "value": "taxpayer"}
],
"description": "实体的类型"
},
{
"name": "threshold_multiplier",
"label": "异常倍数阈值",
"type": "number",
"required": False,
"default": 2.0,
"min": 1,
"step": 0.5,
"description": "费用增长超过此倍数将被标记为异常"
}
]
},
{
"code": "TAX_RISK_ASSESSMENT",
"name": "税务风险综合评估",
"description": "综合分析各项检测结果",
"parameters": [
{
"name": "entity_id",
"label": "实体ID",
"type": "string",
"required": True,
"placeholder": "请输入实体ID",
"description": "要检测的实体唯一标识"
},
{
"name": "entity_type",
"label": "实体类型",
"type": "select",
"required": False,
"default": "streamer",
"options": [
{"label": "主播", "value": "streamer"},
{"label": "MCN机构", "value": "mcn"},
{"label": "纳税人", "value": "taxpayer"}
],
"description": "实体的类型"
},
{
"name": "include_algorithms",
"label": "包含算法",
"type": "checkbox-group",
"required": False,
"default": [
"REVENUE_INTEGRITY_CHECK",
"PRIVATE_ACCOUNT_DETECTION",
"INVOICE_FRAUD_DETECTION",
"EXPENSE_ANOMALY_DETECTION"
],
"options": [
{"label": "收入完整性检测", "value": "REVENUE_INTEGRITY_CHECK"},
{"label": "私户收款检测", "value": "PRIVATE_ACCOUNT_DETECTION"},
{"label": "发票虚开检测", "value": "INVOICE_FRAUD_DETECTION"},
{"label": "成本费用异常检测", "value": "EXPENSE_ANOMALY_DETECTION"}
],
"description": "要综合评估的算法列表"
}
]
}
]
logger.info(f"返回算法列表,共 {len(algorithms)} 个算法")
return algorithms