""" 风险检测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