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,零回归。
10 KiB
评审待办 · 风控主架构 + 基金转换架构
⚠️ 历史参考文档(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、门禁与基线)。