Files
group_fqcd_jr/docs/21-风控业务第二版迁移清单.md

41 KiB
Raw Permalink Blame History

风控业务第二版迁移清单

目标仓库:group_fqcd_jr_rm2

基线分支:origin/qyqy_develop,基线提交:6516ccb

开发分支:RM2_develop

迁移原则:只迁移风控业务能力,复用第二版公共底座;不复制第一版鉴权、同步数据库、私有兼容接口和演示数据。

唯一业务来源:C:\Users\Windows\PycharmProjects\python0626zsProject\nanfangjijin_demo

禁止来源:D:\项目\项目2代码\group_fqcd_jr。该目录是被放弃的第一次迁移,只允许用于查看历史问题,不再复制任何代码。

一、不可违反的边界

  • 数据库以 docs/00-新数据库基线设计.md 和 docs/02-数据库建表设计.md 为准;允许新增表、新增字段和新增 Alembic 迁移,不修改、重命名或删除已有表名和字段定义。
  • 已有业务代码只读:不修改、不重命名、不删除。迁移代码优先新增文件;接入主路由或 Agent 工厂时只允许追加不改变原逻辑的注册行。
  • HTTP 接口以 docs/05-接口文档.md 为准,使用 /api/v1、{data, meta} 信封、统一错误码和调用方提供的 trace。
  • 时间和日期存储统一按 UTC;对外时间使用 RFC 3339。
  • 金额、价格、数量和概率使用十进制字符串;BIGINT 主键对外使用字符串。
  • Controller 只调用 Service;Repository 负责数据库访问;Controller 不导入 ORM 或 SQLAlchemy Session。
  • 业务 Agent 必须继承 BaseAgent、由 AgentFactory 创建,并经过鉴权、配置、记忆、意图、模型、工具、合规、审计和 Outbox。
  • 风控 Agent 只读业务数据,不得确认、调查、排除、结案、升级预警或修改交易数据。
  • 不把 private_frontend、私有登录、私有兼容路由和演示数据脚本复制到公共代码。

二、旧迁移资产复用规则

原项目资产 第二版处理方式
app/model/entities.py 作为字段和业务关系参考;第二版优先复用 app/model/fund.py,缺失表再新增独立只读映射
app/service/risk_service.py 迁移状态机、行为分和查询规则,按第二版 MVC+S 重写
app/service/risk_scan_service.py 迁移规则算法、合并和防重,统一使用第二版 Session、UTC 和审计
app/tool/rule_engine.py 迁移规则定义和证据生成逻辑,不迁移同步会话
app/service/evidence_list_service.py 迁移八类证据列表查询和过滤,接入第二版只读 Repository
app/service/evidence_service.py 迁移预警证据详情聚合和脱敏
app/service/evidence_archive_service.py 迁移文件签名、扩展名、大小和归档安全逻辑
app/service/notification_service.py 迁移通知创建、分页和预警编号展示
app/service/daily_report_service.py 迁移九段式日报统计和降级建议
app/service/daily_report_mail_service.py 迁移多邮箱和邮件失败处理,接入第二版开关与 dry-run
app/service/agent_chat_service.py 作为对话行为参考,按第二版 Agent Run、工具治理和 SSE 重写
app/service/agent_service.py 迁移研判、话术、工单摘要和日报业务内容
app/prompt/agent_chat.py、app/prompt/daily_report.py 迁移业务提示词和边界,模型调用统一走第二版模型服务
app/api/risk.py、app/api/evidence.py、app/api/chat.py、app/api/admin.py 作为业务接口清单参考;正式接口全部重新映射到第二版 /api/v1 契约
app/static/index.html、app/static/app.js、app/static/style.css 迁移正式工作台页面,适配第二版 JWT、响应信封、分页和 Agent Run SSE
app/service/risk_scan_scheduler.py 迁移调度语义,后台执行接入第二版 Worker 和租约

三、执行阶段

R0 新基线核对

  • 从 origin/qyqy_develop 创建 RM2_develop。
  • 确认第二版接口文档、错误码、信封、UTC、游标分页和 Agent 接入规范。
  • 确认第二版已有 app/model/fund.py,完整映射风控使用的 fin_* 只读表。
  • 对照 qyqy_develop 现有 Fund 模型确认风控查询所需字段缺口。
  • 对照第二版基线确认所有风控表、字段、唯一键和主键生成方式。
  • 建立第二版风控专项测试目录和测试夹具。

验收:能够回答风控数据的来源、写入点、权限校验、事务边界和审计位置,且不改数据库结构。

R1 风控只读模型与 Repository

状态:[-] 进行中

  • 复用 app/model/fund.py,不再重复映射已有 Fund 模型。
  • 为数据库已存在的 sys_user、sys_login_record、biz_work_order 新增最小只读 ORM 映射。
  • R1.1 模型字段专项测试通过:3 passed。
  • 新增 RiskRepository,统一客户范围、脱敏、分页和稳定排序。
  • 支持风险概览、未闭环预警列表、预警详情。
  • R1.2 Repository 专项测试通过:5 passed。
  • 支持八类证据查询:客户、产品、交易、资金、持仓、登录、预警、通知。
  • 所有列表遵循第二版 {data, meta} 和分页规范;预警队列业务限制每页 5 条,其余证据每页 10 条。
  • 不向 Agent 或接口泄露非必要内部主键。

验收:Repository 在授权范围内只返回允许数据;越权、空范围和无数据场景均失败关闭或返回空集。

R2 风控公共查询接口

  • 新增风控 Controller、请求参数和响应 Mapper。
  • 实现风险概览。
  • 实现预警队列和详情。
  • 实现八类证据分页查询。
  • 行为分、风险等级、规则编号、客户、产品、时间筛选。
  • 使用 AuthorizationService 校验权限和客户范围。
  • 所有响应符合 {data, meta}、错误码和 X-Trace-ID。

验收:Controller 不访问 Model/Repository;非法分页、非法筛选和越权请求有明确错误;接口测试通过。

R3 规则扫描、人工处置和行为分

  • 迁移当前实际启用的 RW 规则和证据生成逻辑。
  • 迁移同交易合并、重复扫描防重和事务边界。
  • 迁移手动扫描接口。
  • 迁移确认接收、调查、误报关闭、结案和升级标记。
  • 结案时按低 3、中 5、高 20 扣减行为分,最低为 0。
  • 误报和未闭环不扣分。
  • 所有状态和行为分变化写入审计。

验收:非法状态跳转失败;重复请求幂等;预警、行为分和审计一致。

R4 证据归档、通知和日报

  • 迁移证据文件上传,校验扩展名、签名、大小和文件名。
  • 文件成功写入后再更新预警证据快照;失败回滚并清理文件。
  • 迁移高风险通知创建和通知分页。
  • 通知开关和邮件开关接入第二版配置中心。
  • 迁移九段式日报,历史未闭环事项完整统计。
  • 日报建议模型调用统一走第二版模型服务,失败时规则降级。
  • 支持多邮箱校验和 dry-run,不建立额外邮件业务留痕。

验收:文件、通知和日报失败不会误写主业务状态;开关关闭时不调用外部服务。

R5 奶龙风控智能助手

  • 按第二版 BaseAgent 实现 RiskAgent。
  • 在 register_business_agents() 注册 agent_type=risk。
  • 声明只读工具:风险概览、预警查询、预警证据。
  • 支持客户、产品、风险等级、规则编号和时间筛选。
  • 生成研判、话术和工单摘要时使用不同输出合同。
  • 增加功能边界、免责声明、空内容校验和分类降级。
  • 发布 agent_tools 和 agent_intent_config 配置。
  • 使用真实 Agent Run、Worker、结果查询和 SSE 验收。

验收:没有发布配置时工具失败关闭;模型异常不能绕过合规审查;Agent 不能执行任何处置。

R6 定时扫描与后台任务

  • 将定时扫描接入第二版 Worker 和租约机制。
  • 扫描开关、周期、重试和并发策略使用环境变量配置。
  • 手动扫描和定时扫描共用同一 Service。
  • 默认关闭,不在 Web 进程启动后台线程。

验收:多次并发扫描不重复生成预警;关闭开关不执行;失败可重试。

R7 正式单页工作台

  • 将完整 HTML、CSS 和 JavaScript 迁入主项目静态资源目录。
  • 适配正式 JWT、统一错误信封、/api/v1/risk 和 Agent Run SSE。
  • 保留概览、八类证据、预警队列、详情弹窗、处置、日报和 Agent 对话。
  • 预警队列每页 5 条,其余表每页 10 条。
  • 桌面和常用窄屏无文字重叠、无布局跳变。

验收:从登录到证据查询、预警处置、Agent 对话和日报发送均可通过正式接口完整操作。

R8 回归、清理和交付

  • 风控单元测试、接口测试和数据库集成测试通过。
  • Agent 正常路径、越权、工具拒绝、模型失败和 SSE 恢复通过。
  • 主项目全量测试、Ruff、MyPy、架构检查和 schema audit 通过。
  • 删除私有兼容依赖,私有页面不再参与正式迁移。
  • 更新需求文档、接口文档、字段映射和回滚说明。

四、当前下一步

R0.1 第二版模型缺口核对

状态:[x] 已完成

  • 确认第二版已有 app/model/fund.py。
  • 确认风险预警、通知、客户画像、交易、资金、持仓等表已有模型。
  • 确认 sys_user 数据库表已存在;当前由 IdentityRepository 和 SuitabilityService 通过原生 SQL 访问,仅有通用 ORM 映射缺口。
  • 确认 sys_login_record 数据库表已存在,当前缺少 ORM 映射。
  • 确认 biz_work_order 数据库表已存在,当前缺少 ORM 映射。
  • 确认 R1 只新增上述三张已有表的最小只读 ORM 映射,不新增数据库表。

结论:app/model/fund.py 已覆盖大部分 fin_* 风控查询表;不得重复映射。R1 需要新增一个风控专用 ORM 模块,只容纳上述三张当前缺失映射的已有表,或优先通过现有 Repository 查询方式接入。此步骤不执行数据库迁移、不改变表结构。

R0.2 风控查询仓储设计

状态:[x] 已完成

  • 明确哪些查询复用 FundQueryRepository。
  • 明确哪些查询需要新增风控专用 Repository。
  • 明确客户范围和字段脱敏的调用链。
  • 输出最小实现方案和测试清单。

确认点:R0.2 方案完成并测试后停下,等待确认再进入 R1。

可直接复用

以下实体已由 app/model/fund.py 映射,并由 FundQueryRepository 提供静默拒绝的客户范围:

  • fin_product
  • fin_customer_profile
  • fin_risk_assessment
  • fin_transaction
  • fin_capital_flow
  • fin_holding
  • fin_risk_alert
  • fin_risk_notification

以下查询可以直接使用 FundQueryRepository:

  • 风险测评分页。
  • 交易、资金、持仓基础分页。
  • 预警和通知基础分页。
  • 按客户范围限制的简单单表查询。

需要新增

新增 app/model/risk.py,只映射以下数据库已存在、但当前缺少 ORM 的只读表,不执行建表或改字段:

  • sys_user:客户编号、用户名、用户类型、客户分层、投资人类型、状态。
  • sys_login_record:登录时间、结果、地区、设备、常用设备和失败原因。
  • biz_work_order:工单编号、客户、产品、渠道、状态、风险揭示、二次确认、录音编号、提交和处理时间。

新增 app/repository/risk_repository.py,原因如下:

  • 预警列表需要联表客户、画像、交易和产品,并在输出前统一脱敏。
  • 客户证据列表需要关联 sys_user + fin_customer_profile + fin_risk_assessment。
  • 交易证据需要关联客户、产品、工单和风险留痕字段。
  • 登录证据和工单证据需要访问第二版尚未映射的表。
  • 预警详情需要一次聚合预警、客户、交易、产品、工单和相关证据。
  • 风险概览需要风险等级、待处理数量、超时数量和重点事件。

最小文件计划

新增文件,不修改已有业务代码:

app/model/risk.py
app/repository/risk_repository.py
app/api/schemas/risk.py
app/service/risk_query_service.py
app/api/controllers/risk.py
tests/unit/repository/test_risk_repository.py
tests/unit/api/test_risk_controller.py

接入正式路由时,app/main.py 只允许追加一行 import 和一行 include_router,不改变已有中间件、异常处理和其他路由。

序列化和分页约定

  • 主键和数据库 BIGINT 对外返回字符串。
  • 金额、价格、数量和概率返回十进制字符串。
  • 时间在数据库内按 UTC,输出 RFC 3339。
  • 客户姓名只显示首字,其余替换为 *。
  • 预警队列业务默认 limit=5,其他证据默认 limit=10。
  • 公共列表接口使用 {data, meta};游标和过滤条件绑定,非法游标返回 INVALID_CURSOR。

R1 测试清单

  • 未授权角色、缺少权限和空客户范围返回拒绝或空集。
  • 客户姓名、手机号和敏感编号脱敏。
  • 风险概览统计与未闭环状态一致。
  • 预警队列按风险等级和主键稳定排序。
  • 八类证据查询分别覆盖有数据、空数据和越权范围。
  • 金额、时间、布尔值和 BIGINT 序列化符合第二版契约。
  • 非法游标、非法排序和非法筛选返回明确错误。

五、逐步执行门禁

后续每一项都必须严格按以下顺序执行:

  1. 思考:阅读原项目对应文件、第二版目标模块、调用链、数据来源、写入点和权限来源。
  2. 方案:写出最小迁移范围、不修改的边界、影响模块和验证方式。
  3. 迁移:只实现当前一个步骤,不顺手迁移下一步。
  4. 测试:运行当前步骤的专项测试;失败先修复,不带问题进入下一步。
  5. 确认:汇报修改文件、测试结果、剩余风险和下一步,等待用户确认。

未完成当前步骤的“测试 + 确认”,不得开始下一步。

六、执行记录

R1.1 补充只读 ORM 映射

状态:[x] 已完成,已确认

新增文件:

  • app/model/risk.py
  • tests/unit/model/test_risk_models.py

验证结果:使用 Python 3.13.5 运行专项测试,3 passed。

下一步:R1.2 新增 RiskRepository,实现风险概览、未闭环预警列表和预警详情查询。等待确认后执行。

R1.2 风控核心查询 Repository

状态:[x] 已完成,已确认

新增文件:

  • app/repository/risk_repository.py
  • tests/unit/repository/test_risk_repository.py

已实现:

  • overview():未闭环总数、等级分布、待处理、超时和重点预警。
  • list_alerts():客户、产品、风险等级、规则编号、关键字和时间筛选。
  • get_alert_detail():聚合预警、客户、交易、产品、工单和证据快照。
  • 客户与交易账号范围校验,范围缺失时失败关闭。
  • 客户姓名脱敏和高风险优先排序。

验证结果:使用 Python 3.13.5 运行专项测试,5 passed。

下一步:R1.3 检查 Repository 只读契约和现有 FundQueryRepository 的复用边界。等待确认后执行。

R1.3 Repository 只读契约与复用边界

状态:[x] 已完成,已确认

新增文件:

  • tests/unit/repository/test_risk_repository_contract.py

已确认:

  • RiskRepository 不导入 insert/update/delete/merge 等写 SQL。
  • 不调用 session.add/commit/flush/rollback/with_for_update 等写事务方法。
  • 对外只暴露 overview、list_alerts、get_alert_detail 三类只读用例。
  • 风险概览、预警列表和预警详情需要多表联表,保留在 RiskRepository。
  • 后续单表或简单实体查询必须复用 FundQueryRepository,不得在 RiskRepository 重复实现。

验证结果:使用 Python 3.13.5 运行契约测试,3 passed。

下一步:R1.4 实现八类证据查询。优先复用 FundQueryRepository;只有需要客户身份、登录记录、工单联表或跨表拼装时才扩展 RiskRepository。等待确认后执行。

R1.4 八类证据查询

状态:[x] 已完成,已确认

已在 RiskRepository 实现:

  • list_customers():客户画像、最新风测、行为和状态。
  • list_products():直接复用 FundQueryRepository。
  • list_transactions():交易、客户、产品、工单和留痕字段。
  • list_capital_flows():资金流水。
  • list_holdings():持仓、持有天数和持仓占比。
  • list_login_records():登录结果、地区和设备。
  • list_alerts():预警队列和筛选。
  • list_notifications():通知和预警编号。

验证结果:八类证据查询和只读契约专项测试共 13 passed。

下一步:R2.1 新增风控查询 Schema 与 Service,把 Repository 记录转换为第二版 {data, meta} 响应投影。等待确认后执行。

R2.1 风控查询 Schema、游标和 Service

状态:[x] 已完成,待确认

新增文件:

  • app/core/risk_cursor.py
  • app/api/schemas/risk.py
  • app/service/risk_query_service.py
  • tests/unit/service/test_risk_query_service.py

已实现:

  • 预警队列和证据列表参数校验,队列最多 5 条,证据最多 10 条。
  • 不透明偏移游标及非法游标错误。
  • 客户数据范围转换和失败关闭。
  • Repository 记录到第二版响应字段的统一投影。
  • BIGINT、Decimal、datetime、tuple 的序列化。
  • 风险概览、预警列表、预警详情和八类证据 Service 方法。

验证结果:Schema、游标和 Service 专项测试共 6 passed。

下一步:R2.2 新增风控 Controller 和正式路由,只负责参数绑定、调用 Service 并包装 {data, meta}。等待确认后执行。

R2.2 风控只读 Controller 与正式路由

状态:[x] 已完成,待确认

新增和接入:

  • 新增 app/api/controllers/risk.py。
  • app/main.py 仅追加一条 import 和一条 include_router。
  • 新增 tests/unit/api/test_risk_controller.py。

正式接口:

  • GET /api/v1/risk/overview
  • GET /api/v1/risk/alerts
  • GET /api/v1/risk/alerts/{alert_no}
  • GET /api/v1/risk/evidence/{source}

验证结果:风控模型、Repository、Service 和 Controller 合并专项测试共 26 passed。

下一步:R3.1 迁移规则扫描,先实现扫描 Service 和重复预警防重测试。等待确认后执行。

R3.1 规则扫描 Service 与 RW-003

状态:[x] 已完成,待确认

新增文件:

  • app/service/risk_scan_service.py
  • tests/unit/service/test_risk_scan_service.py

已实现:

  • 异步 RiskScanService 和 RiskRuleEngine。
  • 扫描权限校验、进程内并发保护、事务提交和失败回滚。
  • 显式生成 fin_risk_alert.id 业务主键。
  • 同交易多规则预警合并。
  • 按交易和规则编号防重。
  • RW-003、RW-007、RW-012、RW-015、RW-018 五条规则代码。
  • RW-003 事实型摘要、防重、合并和扫描事务专项测试。

验证结果:当前风控迁移全部专项测试共 31 passed。

下一步:R3.2 为 RW-007、RW-012、RW-015、RW-018 补齐正例、反例和边界测试。等待确认后执行。

R3.2 剩余规则边界测试

状态:[x] 已完成,待确认

补充覆盖:

  • RW-007:C2/R5 高风险、C4/R5 中风险、留痕完整不触发、等级匹配不触发。
  • RW-012:高龄大额赎回触发、历史均值三倍边界触发、超过三倍不触发、常用设备不触发。
  • RW-015:凌晨零点且刚好 10000 元触发、非凌晨不触发、超过 10000 元不触发。
  • RW-018:渠道“定投”和“自动定投”触发、普通手机渠道不触发。

验证结果:当前全部风控专项测试共 37 passed。

下一步:R3.3 新增扫描 Controller 和正式接口,将手动扫描映射到 POST /api/v1/risk/alerts/scan。等待确认后执行。

R3.3 手动扫描接口

状态:[x] 已完成,待确认

已接入:

  • POST /api/v1/risk/alerts/scan
  • Controller 调用 RiskScanService,只包装 {data, meta}。
  • 未认证请求返回统一 401。
  • 请求体为空,扫描权限由 Service 层校验。

验证结果:扫描 Controller 与 Service 联合专项测试共 16 passed。

下一步:R4.1 迁移人工处置状态机,覆盖确认接收、调查、误报关闭、结案、升级和行为分。等待确认后执行。

R4.1 人工处置状态机与行为分

状态:[x] 已完成,待确认

新增文件:

  • app/service/risk_action_service.py
  • tests/unit/service/test_risk_action_service.py

已实现:

  • 确认接收。
  • 进入调查。
  • 关闭误报并记录理由。
  • 完成结案并记录处置结论。
  • 升级处理,升级不是终态。
  • 结案行为分扣减:低 3、中 5、高 20,最低为 0。
  • 误报和未闭环不扣分。
  • 每次状态变化写入 interaction_audit。
  • 重复确认、未确认进入调查、重复升级和非法状态转换拒绝。

验证结果:当前全部风控专项测试共 46 passed。

下一步:R4.2 新增人工处置 Schema、Controller 和正式接口,将状态机映射到 /api/v1/risk/alerts/{alert_no} 子资源。等待确认后执行。

R4.2 人工处置 Controller 与接口

状态:[x] 已完成,待确认

新增接口:

  • POST /api/v1/risk/alerts/{alert_no}/acknowledgements
  • POST /api/v1/risk/alerts/{alert_no}/investigations
  • POST /api/v1/risk/alerts/{alert_no}/exclusions
  • POST /api/v1/risk/alerts/{alert_no}/resolutions
  • POST /api/v1/risk/alerts/{alert_no}/escalations

已实现:

  • 处置请求参数校验。
  • JWT、限流和 {data, meta} 响应信封。
  • 误报理由、处置结论和升级理由不能为空。
  • Controller 只调用 RiskActionService。

验证结果:当前全部风控专项测试共 48 passed。

下一步:R5.1 迁移证据文件归档,覆盖类型、签名、大小、重复上传和失败回滚。等待确认后执行。

R5.1 证据文件归档 Service

状态:[x] 已完成,待确认

新增文件:

  • app/service/risk_evidence_archive_service.py
  • tests/unit/service/test_risk_evidence_archive_service.py

已实现:

  • 仅允许 JPG、PNG、WEBP、PDF、Word、Excel、PPT 和 UTF-8 TXT。
  • 扩展名、文件签名、Office ZIP 结构和 TXT 编码校验。
  • 文件大小限制,默认 10 MB。
  • 文件名统一替换为预警编号加原扩展名。
  • 项目目录内路径校验,禁止目录穿越。
  • 同一预警禁止重复归档或覆盖。
  • 文件先写临时文件再原子替换。
  • 数据库提交失败时回滚并删除目标文件。
  • 归档信息写入 evidence_snapshot.evidence_archive。
  • 归档动作写入 interaction_audit。

验证结果:当前全部风控专项测试共 54 passed。

下一步:R5.2 新增证据上传 Controller 和正式接口。等待确认后执行。

R5.2 证据上传 Controller 与接口

状态:[x] 已完成,待确认

新增接口:

  • POST /api/v1/risk/alerts/{alert_no}/evidence

已实现:

  • multipart 文件上传。
  • Controller 只调用 RiskEvidenceArchiveService。
  • 上传对象在请求结束后关闭。
  • {data, meta} 响应信封。
  • Service 校验和失败回滚继续生效。

验证结果:证据上传接口与归档 Service 联合专项测试共 14 passed。

下一步:R6.1 迁移通知记录创建和分页查询,通知内容必须包含预警编号。等待确认后执行。

R6.1 通知记录创建与分页

状态:[x] 已完成,待确认

新增文件:

  • app/service/risk_notification_service.py
  • tests/unit/service/test_risk_notification_service.py

已实现:

  • 站内通知记录创建。
  • 邮件通知记录创建;迁移阶段当时尚未接入 SMTP。
  • 通知主键和通知编号显式生成。
  • 通知内容自动包含“预警编号:xxx”。
  • 高风险批量通知记录。
  • 通知分页复用 RiskQueryService 和 RiskRepository。

2026-09-12 更新:本节“本阶段不外发 SMTP”只描述 R6.1 当时状态。当前手动扫描和定时扫描命中高风险后已恢复真实邮件发送,通知状态会回写 已发送 或 发送失败。当前高风险预警邮件只支持一个收件人。

验证结果:当前全部风控专项测试共 58 passed。

下一步:R6.2 将高风险通知创建接入扫描事务,并验证通知失败不破坏预警主事务。等待确认后执行。

R6.2 高风险通知接入扫描

状态:[x] 已完成,待确认

已实现:

  • 扫描生成高风险预警后自动创建站内通知记录。
  • 高风险邮件通知记录和真实 SMTP 外发,发送失败不回滚预警。
  • 高风险预警邮件当前只支持一个收件人,多个邮箱值只取第一个。
  • 通知创建使用嵌套保存点隔离。
  • 通知创建失败只记录日志,不回滚预警扫描主事务。
  • 扫描结果增加 notification_count。

验证结果:当前全部风控专项测试共 60 passed。

下一步:R6.3 新增通知分页查询接口,复用统一游标和响应信封。等待确认后执行。

R6.3 通知分页查询接口

状态:[x] 已完成,待确认

新增接口:

  • GET /api/v1/risk/notifications

已实现:

  • 通知专用分页参数。
  • 关键字、发送状态、时间范围和游标筛选。
  • 每页最多 10 条。
  • {data, meta} 响应信封。
  • 复用通知 Service、Repository 和统一序列化。

验证结果:通知查询接口与 Service 联合专项测试共 12 passed。

下一步:R7.1 迁移九段式日报和日报邮件 Service。等待确认后执行。

R7.1 九段式日报和邮件 Service

状态:[x] 已完成,待确认

新增文件:

  • app/service/risk_daily_report_service.py
  • app/service/risk_daily_report_mail_service.py
  • tests/unit/service/test_risk_daily_report_service.py

已实现:

  • 九段式日报统计模板。
  • 当日预警、历史未闭环、误报、处置结果和规则效果统计。
  • 历史未闭环不受分页限制。
  • 模型建议走第二版模型服务,失败、空内容或越权表述时规则降级。
  • 日报生成审计。
  • 日报异步流式事件。
  • 邮件 Service 默认禁用、支持 dry-run、不主动连接 SMTP。

验证结果:当前全部风控专项测试共 65 passed。

下一步:R7.2 新增日报 Controller 和正式接口,覆盖结构化生成、流式生成和邮件发送。等待确认后执行。

R7.2 日报 Controller 与接口

状态:[x] 已完成,待确认

新增接口:

  • POST /api/v1/risk/daily-report
  • POST /api/v1/risk/daily-report/stream
  • POST /api/v1/risk/daily-report/mail

已实现:

  • 结构化日报生成。
  • SSE 流式日报事件。
  • 日报邮件发送。
  • 多收件人去重和邮箱校验。
  • {data, meta} 响应信封。
  • 邮件默认禁用或 dry-run,不主动连接 SMTP。

验证结果:当前全部风控专项测试共 66 passed。

下一步:R8.1 迁移奶龙风控智能助手,按第二版 BaseAgent 和 AgentFactory 注册。等待确认后执行。

R8.1 奶龙风控智能助手

状态:[x] 已完成,待确认

新增和接入:

  • app/core/risk_contracts.py
  • app/service/risk_tools.py
  • app/service/agent/implementations/risk_agent.py
  • app/service/agent/bootstrap.py 追加 RiskAgent 和三个只读工具注册
  • tests/contract/test_risk_agent_contract.py

已实现:

  • agent_type=risk,展示名称为“奶龙风控智能助手”。
  • 四个意图:风险概览、风险查询、预警证据、通用边界。
  • 三个只读工具:风险概览、预警查询、预警证据。
  • 工具权限 risk:alert:read,允许角色 risk_operator/admin。
  • Agent 不执行确认、调查、误报关闭、结案或升级。
  • 未发布工具白名单时失败关闭。
  • 返回功能边界和只读说明。

验证结果:当前全部风控专项测试和 Agent 契约测试共 71 passed。

下一步:R8.2 发布 risk Agent 的 agent_tools 和 agent_intent_config 配置,并执行真实 Agent Run/SSE 验收。等待确认后执行。

R8.2 配置发布与真实 Agent 验收

状态:[x] 已完成,待确认

已执行:

  • 生成并验证本地 RS256 JWT 公私钥。
  • 对空库执行 Alembic 基线迁移,51 张表结构审计通过。
  • 写入 9001/9002/9003 测试身份和基础 RBAC。
  • 补充并授予风控读取、写入、扫描权限。
  • 写入并激活 DeepSeek deepseek-flash 模型端点。
  • 发布 risk Agent 工具白名单。
  • 发布四条 risk 意图配置。
  • 新增 tools/seed_risk_agent_config.py 和 tools/risk_agent_e2e.py。

真实验收结果:

  • Agent Run 受理成功。
  • Worker 执行终态为 succeeded。
  • 工具调用为 get_risk_overview/succeeded。
  • 工具审计已写入。
  • SSE 返回 text/event-stream 并包含 done。
  • Milvus 不可用、Redis 模块不可用时按设计降级,没有阻塞风险概览查询。

下一步:R9 正式前端工作台迁移。等待确认后执行。

R9.1 正式工作台静态资源迁移

状态:[x] 已完成,待确认

已迁移:

  • app/static/index.html
  • app/static/style.css
  • app/static/app.js

已接入:

  • app/main.py 追加 /static 静态资源挂载。
  • 新增静态资源测试 tests/unit/api/test_risk_workbench_static.py。

验证结果:页面、CSS 和 JavaScript 均可由第二版应用提供,专项测试 1 passed。

下一步:R9.2 适配前端 JWT、/api/v1/risk 接口、统一响应信封和 Agent Run SSE。等待确认后执行。

R9.2 私有前端 REST、JWT 和日报 SSE 适配

状态:[x] 已完成,待确认

只在 private_frontend 中完成:

  • 增加浏览器 Bearer 令牌管理和本地保存。
  • /api/risk/** 迁移到 /api/v1/risk/**。
  • 统一拆包 {data, meta} 响应。
  • 适配游标分页的上一页/下一页。
  • 适配八类证据、预警、通知、详情和处置子资源。
  • 适配证据上传、扫描、日报生成、日报流和日报邮件。
  • 流式解析同时支持 NDJSON 和 SSE。
  • 新增私有静态代理服务器 private_server.py。
  • 新增短期令牌生成脚本 generate_token.py。

验证结果:JavaScript 和 Python 语法检查通过,短期令牌生成成功。

剩余:Agent 对话仍使用原项目 /api/risk/agent/chat/stream,将在 R9.3 改为第二版 Agent Run/SSE 编排。

R9.3 私有前端 Agent Run 对话

状态:[x] 已完成,待确认

私有前端对话已改为:

  1. POST /api/v1/agent-runs 创建 agent_type=risk 的真实运行。
  2. 使用浏览器生成的 session_id 和 idempotency_key。
  3. 订阅 /api/v1/agent-runs/{run_id}/events SSE。
  4. 展示工具调用、replace/delta、done/error。
  5. 当前预警自动将预警编号拼入 Agent 消息。

运行依赖:

  • Web 服务。
  • 独立 Worker:python -m app.worker。
  • 9002 访问令牌。

验证结果:私有前端 JavaScript 语法检查通过,旧对话接口已移除。

R10.1 结构化输出恢复

状态:[x] 已完成,已确认

新增或修改:

  • app/service/risk_analysis_service.py
  • app/service/agent/implementations/risk_agent.py 追加结构化分析路由
  • tests/unit/service/test_risk_analysis_service.py
  • tests/contract/test_risk_agent_contract.py

已实现:

  • 恢复预警研判、回访话术、工单摘要三种独立输出。
  • 三种输出使用不同的模型任务、提示词和模板降级内容。
  • 模型返回空内容、超长内容或包含越权处置声明时自动降级。
  • 生成结果写入 fund_risk_alert.ai_analysis,并追加交互审计。
  • 生成前校验 risk:alert:read 权限和数据范围。
  • 自然语言携带预警编号时,可路由到对应结构化输出。

验证结果:语法检查通过,结构化输出与 Agent 契约专项测试 10 passed。

遗留告警:Pydantic 对公共配置中 model_ 前缀字段给出命名警告,不属于本次业务逻辑故障。

下一步:R10.2 模型自主选择工具和多轮工具调用。等待确认后执行。

R10.2 模型自主选择工具与多轮工具调用

状态:[x] 已完成,已确认

新增或修改:

  • app/service/risk_agent_model_client.py
  • app/service/agent/implementations/risk_agent.py
  • app/service/risk_tools.py
  • tests/unit/service/test_risk_agent_model_client.py
  • tests/contract/test_risk_agent_contract.py

已实现:

  • 奶龙风控智能助手由模型自主选择风险概览、预警查询和预警证据工具。
  • 支持最多 4 轮模型调用和累计 6 次工具调用。
  • 工具名、工具类型、调用编号和 JSON 参数均执行严格校验。
  • 只允许调用当前 Agent 配置中已授权的只读工具。
  • 工具仍通过公共 ToolExecutor 执行,权限校验、超时和审计规则不变。
  • 工具结果移除内部数据库主键,并限制单次返回长度。
  • 模型最终回复校验空内容、超长内容、越权处置声明和工具协议残留。
  • 模型或工具编排失败时切换到既有确定性查询降级路径。
  • search_risk_alerts 改为按每页 10 条循环读取全部未闭环预警,不再只取首批 10 条。

验证结果:R10.2 专项测试 10 passed,完整风控专项测试 77 passed。

遗留告警:Pydantic 对公共配置中 model_ 前缀字段给出命名警告,不属于本次业务逻辑故障。

下一步:R10.3 恢复误报、可放行、疑似误判等业务研判意图。等待确认后执行。

R10.3 误报、可放行与疑似误判研判

状态:[x] 已完成,已确认

新增或修改:

  • app/service/risk_judgement_service.py
  • app/service/risk_tools.py
  • app/repository/risk_repository.py
  • app/service/agent/implementations/risk_agent.py
  • tests/unit/service/test_risk_judgement_service.py
  • tests/contract/test_risk_agent_contract.py
  • tests/unit/repository/test_risk_repository.py

已实现:

  • 预警列表增加只读 disposition_hint 复核方向。
  • 预警证据增加只读 disposition_assessment 研判草案。
  • 恢复有效定投场景的可放行候选识别。
  • 恢复低风险小金额非正常时段操作的疑似误报识别。
  • RW-007 会重新核对客户等级、产品等级和风险揭示、二次确认、录音留痕。
  • RW-003 会重新核对大额门槛和赎回比例。
  • RW-012 会核对年龄、赎回金额、历史门槛和登录设备常用性。
  • 预警证据详情补充资金流、持仓和登录记录。
  • 模型不可用时可使用规则化只读研判草案降级。
  • 所有结论均明确标注为复核草案,Agent 不执行误报关闭、放行、结案或升级。

验证结果:R10.3 专项测试 14 passed,完整风控专项测试 83 passed。

遗留告警:Pydantic 对公共配置中 model_ 前缀字段给出命名警告,不属于本次业务逻辑故障。

下一步:R10.4 恢复自然语言客户、产品、规则和时间筛选,并补齐完整回答场景。等待确认后执行。

R10.4 自然语言筛选与完整回答

状态:[x] 已完成,已确认

新增或修改:

  • app/service/risk_natural_language.py
  • app/service/risk_tools.py
  • app/service/agent/implementations/risk_agent.py
  • tests/unit/service/test_risk_natural_language.py
  • tests/unit/service/test_risk_search_result.py
  • tests/contract/test_risk_agent_contract.py

已实现:

  • 支持自然语言客户编号筛选。
  • 支持产品代码和产品名称筛选。
  • 支持风险等级和 RW 规则编号筛选。
  • 支持今天、本月、最近 N 天、明确起止日期、日期以来和截至日期。
  • 中文本地时间统一在 Asia/Shanghai 解析,再转换为 UTC 查询时间。
  • 模型调用工具前会收到系统预解析的筛选条件,降低日期和编号解析偏差。
  • 预警查询返回完整分组汇总和精简明细。
  • 汇总包含全部客户、产品、风险等级、规则和研判分布,不会因明细截断而声称只覆盖部分记录。
  • 模型不可用且问题属于预警列表查询时,可使用本地筛选和完整汇总规则化回答。

验证结果:R10.4 专项测试 14 passed,完整风控专项测试 88 passed。

遗留告警:Pydantic 对公共配置中 model_ 前缀字段给出命名警告,不属于本次业务逻辑故障。

下一步:R10.5 对话历史、会话归属、审计和分类降级。等待确认后执行。

R10.5 对话历史、会话归属与长期留存

状态:[-] 暂缓,全部迁移完成后再做

已确认的边界:

  • 当前 MySQL svc_conversation_session、conversation_message、agent_run 和审计写入继续保留。
  • 不在本阶段改为 Redis-only。
  • 不在本阶段修改公共 Run、Worker、消息持久化和记忆抽取链路。
  • 后续优先采用 MySQL 权威保存、Redis 缓存最近会话、原始消息分级保留的方案。
  • 长期留存、脱敏、归档和删除策略作为独立迁移任务处理。

下一步:R10.6 真实 Agent Run、Worker、结果查询、SSE 和业务对话端到端验收。

R10.6 真实 Agent Run 与业务对话端到端验收

状态:[x] 已完成,已确认

新增:

  • tools/risk_agent_business_e2e.py

验收场景:

  • 风险概览对话。
  • 低风险客户和产品完整筛选回答。
  • 误报、可放行和疑似误判研判对话。

通过正式链路验证:

  • POST /api/v1/agent-runs 受理为 queued。
  • WorkerRuntime 实际执行 Run。
  • 模型自主调用 get_risk_overview、search_risk_alerts 和 get_alert_evidence。
  • 查询结果通过 summary 表示完整分组,未因明细截断声称只覆盖部分记录。
  • GET /api/v1/agent-runs/{run_id} 返回 succeeded。
  • SSE 返回 text/event-stream 并包含 done。
  • 工具调用审计和运行完成审计均可追溯。
  • Agent 未执行确认、误报关闭、放行、结案或升级动作。

验收结果:

  • 风险概览:PASSED,运行编号 40190585-2aad-4af5-b600-d3bf72471830。
  • 完整筛选回答:PASSED,运行编号 b314ad10-1926-4ada-8b1c-a5facf96beca。
  • 误报研判:PASSED,运行编号 cbcb3758-0b8c-4967-9582-ff7ab66de256。

环境降级:

  • Milvus 未启动,语义记忆关闭,结构化流程继续运行。
  • Redis 缓存读取降级,不影响 Agent Run 结果。
  • .env 第 12 行存在 dotenv 解析警告,不影响本次业务验收。

下一步:R11 全量回归、代码质量检查、数据库结构审计和迁移收口。等待确认后执行。

R11 全量回归与迁移收口

状态:[x] 业务侧与静态检查已完成;公共底座 3 项类型问题确认不修改

全量测试:

  • python -m pytest -q --basetemp=.pytest_rm2_tmp -p no:cacheprovider
  • 结果:567 passed, 1 skipped。

类型检查:

  • 已修复本次风控迁移代码中的 30 项 MyPy 严格类型错误。
  • 当前 python -m mypy app 仍报告 3 项公共底座既有错误:
    • app/service/model_gateway.py 两个端点适配器类型错误。
    • app/worker/runtime.py 一个请求元数据类型错误。
  • 经确认,公共基座不修改,上述 3 项作为已知类型例外接受。
  • 该例外不影响当前业务功能、真实 Agent Run、工具调用和 SSE。

静态检查:

  • python -m pip check 通过。
  • python -m ruff check app tests tools alembic 通过。
  • 使用 Ruff 0.16.6 修复导入排序、未使用变量、调用默认值标记、超长行和未使用统计变量。

数据库结构:

  • python tools/audit_schema.py:51 张业务表通过,无缺失或多余业务表。
  • python tools/audit_constraints.py:唯一键和 ORM 映射与基线一致。
  • python tools/migration_state_check.py:数据库版本为 head 20260910_drop_review_separation,51 张业务表。
  • python -m alembic check 无法执行,原因是公共 alembic/env.py 未提供 MetaData。

剩余环境项:

  • .env 第 12 行存在 dotenv 解析警告。
  • Pydantic 对公共配置中 model_ 前缀字段给出命名警告。
  • 本地 Milvus 未启动;Redis 缓存读取降级。
  • R10.5 对话历史与长期留存继续暂缓,全部迁移完成后再处理。

结论:R11 业务迁移和质量门禁已完成。公共底座 3 项 MyPy 例外保持现状,Ruff、全量测试、数据库结构和迁移状态均已通过。

R6.4 定时规则扫描补迁移

状态:[x] 已完成,待确认

新增:

  • app/service/risk_scan_schedule_config.py
  • app/worker/risk_scan_scheduler.py
  • tests/unit/service/test_risk_scan_schedule_config.py
  • tests/unit/worker/test_risk_scan_scheduler.py

已实现:

  • 独立定时扫描 Worker,不在 Web 进程启动后台线程。
  • 从 .env 环境变量读取开关、周期、立即执行、重试次数和轮询间隔。
  • 默认关闭。
  • 使用 MySQL 咨询锁防止多进程重复扫描。
  • 手动和定时扫描共用同一 RiskScanService。
  • 扫描成功和失败写入系统审计。
  • 支持 --once 和 --force 本地验证参数。

环境变量:

  • RISK_SCAN_SCHEDULE_ENABLED
  • RISK_SCAN_INTERVAL_MINUTES
  • RISK_SCAN_RUN_IMMEDIATELY
  • RISK_SCAN_RETRY_LIMIT
  • RISK_SCAN_POLL_SECONDS

验证结果:专项测试 6 passed,Ruff 通过,MyPy 通过,全量测试 573 passed, 1 skipped。

本地运行方式:

python -m app.worker.risk_scan_scheduler
python -m app.worker.risk_scan_scheduler --once --force

下一步:将定时扫描配置写入当前 active 配置发布,并同步推送代码。等待确认后执行。