Files
group_xinghuo_jinrong/docs/项目框架设计/评审待办-风控主架构与基金转换.md
GaoYiYuan_0626 f056fcd007 基金转换 T+1 模型:T-1~T-6 落地(DDL/数据层/语义收窄/锁/受理事务)
AIcoding 第 5 步 todo 开发(开发计划 v2.0)前半段:

- T-1 3 张新表 DDL(core_convert_request 6 态 ENUM / core_trade_calendar /
  core_share_rule)+ 种子 + sqlite 单一事实源同步 + 列清单断言
- T-2/T-2b calc 扩展(product_round/redeem_amount/partial_qty)+ 真实净值实算回填
- T-3 convert_request_repository(6 态 + 条件 UPDATE 守卫)+ core_ro 三读方法
  + share_lot_repository.available_qty_with_inflight(R-3 在途占用推导)
- T-4 convert_repository.sync_mirror 成为 risk_convert_detail 唯一进度镜像写入口
  (旧三方法标 Deprecated,T-7 后删)
- T-5 locks.py 锁键构造器 convert_req_lock_key / convert_confirm_lock_key
- T-6 convert_service.accept_convert 受理事务(八步:锁→幂等→校验→受理日顺延
  →在途占用校验→落单→镜像+审计→202;不扣份额/不折算/不写流水)
  + tests/test_convert_accept.py(16 用例)
  + scripts/dev/verify_convert_accept.py(真库 36/36 一致)
  + trading_calendar.py 纯函数包(R-5)+ 21 用例

T-6 真库实测暴露并修复:confirm_eta 在日历数据边界抛 ValueError,会让已落库
的受理单在调用方眼里变 500;改为展示性字段容错 + 单测守护。

基线:798 passed / 10 skipped,零回归。
2026-09-11 19:21:15 +08:00

10 KiB
Raw Permalink Blame History

评审待办 · 风控主架构 + 基金转换架构

⚠️ 历史参考文档(2026-09-10 产物):文中「基金转换架构 v1.0 评审」对应的是 v0.x 模型,已被 T+1 受理/确认分离模型(架构 v2.1 / PRD v1.1 / 开发计划 v2.0)取代;当前评审结论见各文档「独立审查」节,当前以 v1.1/v2.1/v2.0 为准。

用途:两份架构评审意见合并处理的执行清单。 用户指示(2026-09-10):「等基金架构的评审也出来了一起改」。 当前状态:基金转换架构 v1.0 评审 13 条已全部落地(落点索引见架构 §13.2,0 条悬空,2026-09-10); P0-1 账号方案已拍板(§三 → 采纳独立账号方案,落 D20 / T-0b);其余风控主架构 P0/P1 待处置(见 §一)。 关联文档:架构设计-基金转换交易.md(v1.0)· 架构设计-风控模块.md · 基金转换-审查意见处置表.md


一、风控主架构评审(2026-09-10 收到 · 已抽查核实)

P0 · 安全与正确性底线

# 风险点 核实结论 建议修复
1 Core 只读靠约定、无 DB 级强制 ✅ 属实(settings.py:17 单 root 账号;db.py:23 仅按库名缓存 → core_ro:55 与 gateway_repository:24 共用同一连接池) ✅ 已拍板并落地设计:D20 双账号分离(见 §三 + 架构 §11.1)—— xh_core_ro(SELECT) / xh_core_rw(4 表写) / xh_agent_rw(audit 仅 INSERT);get_engine(db, role);DDL 走管理员。T-0b 阻断前置
2 agent_message(seq_no) 并发重号 ✅ 属实:表设计/01-mysql-共用底座.sql:47 是普通 KEY idx_session_seq,非 UNIQUE 加 UNIQUE(session_id, seq_no) + insert_turn 捕获 IntegrityError 重试
3 dev debug 通道双闸门 ⚠️ 已实现,且外审行号引错:双闸门实际在 app/api/deps.py:258(非 237-238) 属加固:非 development 或配了公钥时加显式启动断言
4 scoring.py 仅预留桩,L3 risk_score 恒 NULL ✅ 属实,但是设计意图:scoring.py:18 抛 NotImplementedError,docstring 明写「FR-7 本期静态映射、评分模型后置」,签名已冻结 列入下一里程碑实现(PRD R-05)
5 跨库最终一致无自动补偿 未实证(按架构判断基本属实) rebuild_alerts.py 纳入 cron + 监控告警

P1 · 可维护性债务

# 问题 核实结论 建议
1 C5 红线权限逻辑分散三处(deps.py 矩阵 / chat.py:122 / tool_service.py:138) 未核 抽 PermissionPolicy 类集中,或至少加文档交叉引用清单
2 意图识别仅关键词 属实(架构已声明为有意取舍) 先做同义词扩展表(低成本),再评估 LLM 路由(会损失可复现性)
3 entities.py 31 个 ORM 类闲置 ✅ 属实(实测 ^class 计数 = 31),查询走 text SQL + dict 渐进引入 TypedDict,先从高频 Tool 入手
4 注入词表硬编码 45 条 未核 配置化(表或 .env 多行)+ 热加载
5 日志基建缺失 ✅ 属实:app/utils/logger.py 全文仅一行 """日志模块。""" 接入 structlog + logging.config.dictConfig,统一 JSON 落盘

无需反悔(外审已确认的既有架构决策)

四 Agent 差异表达(一张图 + 数据驱动)· Tool 与 LLM 流式(Tool 同步跑完再流式)· 意图识别(关键词,可复现可单测)· 适当性矩阵(查表数据驱动)· 降级策略(限流 fail-open、Embedding fail-closed)· 阻断逻辑(网关专用分支,物理隔离防误扩权,仅 R-02 可阻断)。


二、基金转换架构 v1.0 独立评审(2026-09-10 已出 · 已逐条判定 · 已全部落地)

评审结论:✅ 通过(M-7 满足),唯一硬前置 = R1。 判定结果:接受 10 · 修正性接受 3 · 驳回 0。 落地状态:✅ 13 条全部闭合(R2R5 / S1S5 / Q4·Q6·Q10 落架构正文各节 + 架构 §13.2 索引表; R1 升级为 T-0 阻断前置;S2 另回填 PRD v0.9 枚举;R2 回填 PRD v0.9 响应字段)。

硬风险(R1~R5)

# 意见 判定 处置与证据
R1 sqlite/MySQL core_holding 列名不同名 接受(升级为 T-0 阻断前置) 已核实属实:tests/_ddl.py:50-54 只有 market_value/quantity,且无 PK/唯一约束(额外发现)。处置:T-0 先于 T-1 —— 以 MySQL 为准统一 + conftest.py 启动期列名断言
R2 T+1 自然日近似 vs 真实工作日 接受 响应与审计加 confirm_basis: "natural_day_approx";真实 Core 接入后补交易日历
R3 阶段 1.5 异常不阻断、不重试 接受(一期备注) 一期人工 SLA 24h;评审建议的「补偿队列 / Redis 事件 + cleanup 顺带巡检补跑」记二期
R4 convert_batch_max_lots=200 超限 400 接受 保留 400 TOO_MANY_LOTS + 注释「自动分拆多笔留二期」
R5 to_fund_ratio=1.0 不参与计算 接受 已在 §0.4 / D16 注明「本期不参与计算」;二期启用时同步改 fee.py

设计建议(S1~S5)

# 建议 判定 处置与证据
S1 lot_id 改 BIGINT + UNIQUE 修正性接受 理由部分不成立:FIFO 排序依据是 confirmed_at(架构 §7 + PRD §4.1 已建 KEY(customer_id,product_id,confirmed_at)),不是 lot_id;且 lot_id 已是 PK(唯一)。处置:ORDER BY confirmed_at, lot_id 作确定性 tiebreaker,主键类型不动(真实 TA 注册登记批次号是字符串)
S2 cleanup 改 status='expired',不硬删 接受 建表即含 'expired'(新表零 ALTER);同步改 PRD §4.1 ENUM
S3 PlanResult 加 dataclass/TypedDict 接受 防 dict 键拼写错误;呼应主架构 entities.py 闲置问题
S4 client_request_id 加正则 接受 有现成参照:app/main.py:44 _TRACE_ID_PATTERN = re.compile(r"^[A-Za-z0-9._-]{1,64}$"),直接复用
S5 nav stale 按产品类型区分 修正性接受 模拟库 14 产品均日频、无频次差异;真实 Core 接入后再按产品类型分档

自检十问追问(Q4/Q6/Q10)

# 追问 判定 处置与证据
Q4 rebuild_alerts.py 是否幂等 修正性接受 已幂等:scripts/demo/rebuild_alerts.py:8-10 docstring + :54-66 的 find_alerts_by_trade(trade_id) 命中即 skipped,不需要「再加 uk_idem」。但 convert 补偿须以转出端 out_trade_id 为幂等锚点(一次转换两条流水)
Q6 并发重试间隔是否够 接受 §10 增「50 并发压力用例」验证重试成功率
Q10 rounding_diff 谁承担 接受 审计落 rounding_diff 正负向与金额,供二期对账

任务优先级

接受:插入 T-0(列名统一 + 启动断言) 于 T-1 之前。


三、交叉点(✅ 已拍板 · 2026-09-10)

P0-1 / C1 · Core 读写账号分离 —— 用户拍板结论:「按真实项目走,贴近真实项目做法」= 采纳独立账号方案。

历史脉络(重要 · 勿重复设计):本项在主架构线已评审过 —— 改进方案评审-问题清单与对比.md §C1(core_ro 无 DB 级只读账号,红线相关,推荐方案 A:独立只读用户 GRANT SELECT ONLY, 否决 B「代码层 SQL 白名单」:易被注释/子查询绕过),并已挂进 开发计划-架构改进.md **§5.3**(标注「需运维配合」,未实施)。 本次由基金转换线接手落地。

落地清单 → 架构设计-基金转换交易.md §11.1(账号矩阵 / 配置 / db.py 改造 / 3 个权限断言)+ D20 + T-0b:

账号 授权 使用者
xh_core_ro SELECT 全库 core_ro / core_tools / deps / risk / gateway 的校验读
xh_core_rw 4 表 SELECT/INSERT/UPDATE,无 DELETE / DDL 仅 gateway 写路径
xh_agent_rw 业务表读写 + audit_log 只授 INSERT Agent 侧全部
root 全权 仅 00-grant.sql / 01-ddl.sql / reset.ps1

对基金转换的意义:app/gateway/convert_core_repository.py 是唯一写 jinrong_core 的层 → 必须持有 rw 账号。若按「只读连接池」一刀切,T-1/T-6 会直接返工 —— 该风险已由 D20 消除。 附带收益:把项目红线「审计表只 INSERT」从代码约定升级为 DB 级强制(audit_log 回收 UPDATE/DELETE)。


四、处理时机

阶段 动作 状态
基金转换评审出来 两份合并,逐条判定 → 更新各自架构文档 ✅ 已完成(13 条落地,架构 §13.2 索引)
基金转换开工前(T-1 之前) 必须定 P0-1 账号方案(见 §三) ✅ 已拍板:按真实项目走 = 独立账号方案(D20 / T-0b)
基金转换 T-0 sqlite/MySQL 列名统一 + conftest.py 启动断言(R1) ⏳ 待开发
基金转换 T-0b DB 账号分离:00-grant.sql + db.py(db, role) + 3 权限断言(D20) ⏳ 待开发
下一迭代 P0-4 scoring.py 实现 · P1-5 日志基建 · P1-2 同义词表 · §一其余 P0/P1 ⏳ 排期

收口结论:基金转换线文档阶段已闭环(PRD v0.9 + 架构 v1.0 + 评审 0 悬空), 门控 M-7 满足、可进第 4 步开发计划;唯一开工前置是 §三 的 gateway 可写账号方案(用户拍板)+ T-0(开发首个任务)。

补充:执行期风险 5 条(用户 2026-09-10 提供 · 已并入架构)

# 风险 架构落点
1 T-0 未完成就跑 T-1~ 架构 §12 #1(CI 门禁 tests/test_db.py::test_core_holding_columns)+ §15 T-0 依赖
2 批次补建规则写两处漂移 架构 D18(抽 lot_bootstrap.bootstrap_lots() 纯函数)+ §12 #2 + §15 T-2/T-10
3 Decimal 量化未传 ROUND_HALF_UP 架构 §1 原则 10 + §12 #3(.5 边界断言)
4 阶段 1.5 异常不丢预警 架构 D19(on_error_hook 可插拔)+ §6.2 + §12 #4
5 convert_batch_max_lots 超限 架构 §1 原则 12 + §8.2 batch_count/§8.3 响应体 + §12 #5(前端文案)

另:用户提供的任务拓扑已落 架构 §15.1(并行组 A/B、关键路径、高风险任务 T-10、门禁与基线)。