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,零回归。
14 KiB
基金转换 · 外部审查意见处置表
⚠️ 历史参考文档(v0.x 模型,2026-09-10 产物):本表结论对应的 PRD v0.8 / 架构 v1.0 已被 T+1 受理/确认分离模型(PRD v1.1 / 架构 v2.1 / 开发计划 v2.0)取代,保留作审查方法论留档,当前以 v1.1/v2.1/v2.0 为准。
日期:2026-09-10 · 审查结论原文:❌ 不建议进入编码(33 条:B×5 / I×7 / M×7 / L×7 / S×3 + 自检九问复核) 处置原则(用户红线):逐条判定 = 接受 / 修正性接受 / 驳回;驳回与修正性接受必须给出核实过的证据。 判定基准:贴近真实业务(证监会公告〔2025〕22 号 + 10 家基金公司转换公告,见架构 §0.5 来源清单) 结果:接受 27 · 修正性接受 5 · 驳回 0 · 合并 1(S-1 并入 B-1),另自查补 2 条(X-1 / X-2) 后续:本表结论 → PRD v0.8 → 架构设计 v1.0 → 独立 AI 评审 → 第 4 步开发计划
一、阻断级(B-1~B-5)
| # | 审查意见 | 判定 | 处置与证据 |
|---|---|---|---|
| B-1 | PRD「已定稿」与架构 §0 合规查证矛盾 | 接受 | PRD 前言改「v0.7 已冻结(架构校准发现合规问题),待出 v0.8」;架构 v0.2 加冻结声明;新增门控:PRD v0.8 未产出不得进第 4 步 |
| B-2 | 申购费率种子全超法定上限 | 接受 | 按架构 §13.3 重定:货基 0 / 债基·指数 0.0030 / 其他混合 0.0050 / 主动偏股 0.0080。依据 22 号文 §8。§5.3 示例、§9 第 19 条同步重算 |
| B-3 | 赎回费 7–30 日档低于法定下限、边界与 180 日不符 | 接受 | 新档:<7 日 1.5% / 7–30 日 1.0% / 30–180 日 0.5% / 180–365 日 0.25% / ≥365 日 0。依据 22 号文 §10。PRD §2.1、07 种子、§5.3、§9 同步 |
| B-4 | 份额 4 位向下舍去,与真实(2 位四舍五入)不符 | 接受 | 改 2 位 ROUND_HALF_UP;rounding_diff 语义改「可正可负,在基金资产列支」。core_share_lot.remain_qty 存储精度保持 DECIMAL(18,4)(真实 TA 内部精度高于展示),计算与展示按 2 位 |
| B-5 | T+1 确认时序与真实不符 | 修正性接受(范围需收窄,方向需澄清) | 只改「转入端新批次」的 confirmed_at = T+1。证据:PRD §4.4 表格已区分三行(转入新批次 / 普通申购新批次 / 转出·赎回不产新批次);转出批次是历史日期,hold_days = (交易日 − confirmed_at).days 恒为正,不受影响。方向澄清:审查称「当前设计使持有期比真实多 1 天」——实际相反。真实持有期自确认日(T+1)起算,比从 T 起算少 1 天(持有期更短、费率档更严格)。PRD Q1 精确化为:「不模拟 T+1 的权益登记延迟(转入份额立即可用),但 confirmed_at 取 T+1」 |
二、重要级(I-1~I-7)
| # | 审查意见 | 判定 | 处置与证据 |
|---|---|---|---|
| I-1 | 补差费口径未拍板(A 费率差 vs B 价外法两端差) | 接受 | PRD 新增 Q9:明确本期选 B(价外法两端差),理由:中国申购费标准算法即价外法(净申购额 = 金额/(1+费率)),B 与之数学自洽,且为国投瑞银/东海/华商/金鹰/鹏华新版主流;A 以 convert_diff_fee_mode 配置保留,两口径均有单测。PRD §2.1 补双口径公式与数值差异示例 |
| I-2 | hold_days 缺边界定义 |
接受 | PRD §12 补:hold_days = (交易日 − confirmed_at).days(不含申请日);分档左闭右开 [min, max);满 7 日归 7–30 日档(1.0%),满 30 日归 30–180 日档(0.5%)。与架构 §7 同步 |
| I-3 | 阶段二补偿自动/手动未声明 | 接受 | PRD §7.1 补:一期为「监控告警 + 人工脚本补偿」(可接受简化),SLA 24h;cleanup_pending_convert.py 由定时/手动触发。另补审查未提的缺口 X-1:引擎执行时序——阶段一提交后、阶段二之前同步跑引擎;引擎失败沿用现有 engine_error 模式(架构 §X) |
| I-4 | 强制全转触发条件不清晰 | 接受 | PRD §12 补判断流程图:① 份额足够(Σ remain_qty)→ ② 最低转出份额(申请 == 全部可转份额时豁免 min_redeem)→ ③ 余额 < min_hold_qty 才触发强制全转。审查例(持 6000、申请 5000、min_hold_qty=1000):余额 1000 不低于下限 → 不触发,正常转 5000 |
| I-5 | min_hold_action 两种模式 PRD 未回应 |
接受 | 两种都做:core_product 加 min_hold_action ENUM('force_transfer','force_redeem') DEFAULT 'force_transfer'。依据:阈值大(100/500/1000 份)→ 强制赎回(财通/汇添富);阈值小(0.1/1 份)→ 强制全转(中银/东海) |
| I-6 | 份额精度对现有 510 用例影响未评估 | 接受(已实测,影响 = 0) | 实测 grep:现有测试仅 2 个文件触碰 core_holding(test_chat_tools.py、test_concentration_c4.py),且只断言 market_value / quantity,无任何份额精度断言 → 改动影响为 0。但实测发现真坑:sqlite _ddl.py:53 列名 quantity,MySQL DDL 为 qty——convert 首次要 UPDATE core_holding.qty,该失配会在本次开发第一次暴露。T-1 必须补 qty/cost_amount/as_of/pnl_pct |
| I-7 | RISK-006 转换后集中度未定义 | 接受 | 明确:引擎在阶段一提交后执行,concentration_profile 读到的是已更新持仓(含转入端新持仓、已扣减的转出端)→ 转换的集中度连带效应天然被覆盖,无需新增规则 |
三、中等级(M-1~M-7)
| # | 意见 | 判定 | 处置 |
|---|---|---|---|
| M-1 | 重试判定 SQL 依赖 idx_convert_group |
接受 | 开发计划门控:T-1(DDL 含索引)必须先于 T-7(幂等重试逻辑) |
| M-2 | 409 LOT_CONFLICT 重试策略缺失 |
接受 | PRD §5.4 增行:调用方自行重试,建议最多 3 次,间隔 100/200/400ms |
| M-3 | 幂等键格式未指定 | 接受 | 客户端生成、格式自由、≤64 字符、建议含时间戳便于排查 |
| M-4 | out_amount 与 convert_amount 语义重叠 |
接受 | §5.3 注释分组:out_amount = 原始转出额;convert_amount = 扣赎回费后可用于转入的金额 |
| M-5 | convert_batch_max_lots=200 超限无错误码 |
接受 | 超限返回 400 TOO_MANY_LOTS |
| M-6 | pnl_pct 更新口径未同步 |
接受 | PRD FR-C13 补:pnl_pct = (market_value − cost_amount) / cost_amount,cost_amount = 0 时置 0 |
| M-7 | PRD 与架构同步依赖未管理 | 接受 | 硬性门控写入 PRD v0.8 前言 + 架构冻结声明(已加) |
四、低优先级(L-1~L-7)
| # | 意见 | 判定 | 处置 |
|---|---|---|---|
| L-1 | 架构校准结论未回流 PRD 评审记录 | 接受 | PRD v0.8 §13 新增「架构校准轮(2026-09-10)」记录,附本处置表索引 |
| L-2 | 主审计应关联组而非单条 | 接受 | input_summary 以 convert_group_id 为主键关联,附 out_trade_id / in_trade_id |
| L-3 | 响应示例字段过多 | 接受(可选) | §5.3 按「基础 / 转出端 / 转入端 / 规则引擎 / 审计」分组注释 |
| L-4 | 「仅 R-02 可阻断」表述歧义 | 接受 | 加注:503 / 409 为技术故障码,不适用「仅 R-02 可阻断」业务规则 |
| L-5 | 撤单未预留字段 | 接受 | risk_convert_detail 建表即加 status ENUM(...,'cancelled') 与 cancelled_at(新表,零 ALTER 成本) |
| L-6 | 种子版本管理 | 修正性接受 | 不单独建 CHANGELOG 文件(维护成本高、易与 SQL 漂移);改为每个种子 SQL 文件头加版本注释块(版本/日期/依据/变更摘要) |
| L-7 | rebuild_lots.py「回滚」措辞误导 |
接受 | 改措辞为「批次快照重建,不是交易回滚」 |
五、结构问题(S-1~S-3)
| # | 意见 | 判定 | 处置 |
|---|---|---|---|
| S-1 | 前言「已定稿」不符 | — | 并入 B-1 处理 |
| S-2 | 评审记录过长(≈200 行,占 22%) | 修正性接受 | 不移到附录。理由:用户红线要求「PRD 必须经独立 AI 审查、评审闭环可追溯」,删详录会破坏可追溯性。折中:正文前加「阅读导航」(新读者路径 + 每轮 3 行决策摘要表),详录保留原处 |
| S-3 | 待确认项需新增 | 接受 | 新增 Q9 补差费口径 / Q10 份额精度 / Q11 T+1 建模 / Q12 min_hold_action |
六、自查补充(审查未覆盖,本次一并收口)
| # | 发现 | 判定 | 处置 |
|---|---|---|---|
| X-1 | 引擎执行时序未定义——PRD §7.0 七步里没有「跑规则引擎」这一步 | 补充 | 插入为第 6.5 步:阶段一(Core 提交)→ 阶段 1.5:同步跑引擎(读全量两条流水 + 更新后持仓) → 阶段二(回写 + 审计)。引擎异常沿用 engine_error + 本地日志,不阻断已成立的交易 |
| X-2 | 两条流水的 trade_status——真实 T 日申请为「待确认」,T+1 才 confirmed;而 _eligible 只认 confirmed |
补充 | 定:两条流水直接写 confirmed(沿用模拟库现有行为,否则规则引擎读不到),折算结果以 estimated=true 标识;PRD 注明此为「不模拟 T+1 权益登记延迟」的连带简化,与 Q1 一致 |
七、自检九问复核(审查评价 + 本轮补正)
| # | 问 | 审查评价 | 本轮补正 |
|---|---|---|---|
| 1~4 | 谁写 / 谁读 / 枚举 DDL / 跨库事务 | ✓ | 第 4 问「完整但补偿路径为手动」→ I-3 已明确一期手动 + SLA 24h |
| 5 | 种子数据 | ⚠️ 费率超上限 | B-2 / B-3 已重定合规值 |
| 6~8 | 并发 / 汇总语义 / 向后兼容 | ✓ | — |
| 9 | 示例自证 | ⚠️ 数字基于错误口径 | T-2b:scripts/dev/calc_convert_demo.py 用生产同一套 calc.py 实算回填,禁止手算 |
| 10 | (新增)外部事实核验 | — | 本次 4 项合规硬伤全部源于「只做内部自洽、未做外部核验」。新增第 10 问:涉及费率 / 时限 / 资质 / 精度等行业有明文规定的量,必须联网核对现行法规与真实业务公告并列来源 |
八、PRD v0.8 修订范围(下一步执行清单)
| 序 | 修订点 | 对应条目 |
|---|---|---|
| 1 | 前言状态改「v0.7 已冻结,待 v0.8」+ 门控声明 | B-1 / M-7 |
| 2 | §1.4 非目标:撤单改「二期,字段已预留」 | L-5 |
| 3 | §2.1 补差费:双口径公式 + 选 B 的理由 + 数值差异例 | I-1 |
| 4 | §2.1 赎回费档位按 22 号文重排 | B-3 |
| 5 | §2.5 份额 2 位 ROUND_HALF_UP,误差在基金资产列支,rounding_diff 可正可负 |
B-4 |
| 6 | §4.1 risk_convert_detail 加 cancelled / cancelled_at;core_fee_rule 加 to_fund_ratio |
L-5 / D16 |
| 7 | §4.2 core_product 加 min_hold_action |
I-5 |
| 8 | §4.3 种子费率改合规值;赎回费种子改新档 | B-2 / B-3 |
| 9 | §4.4 / Q1:confirmed_at = T+1(仅新批次),精确化表述 |
B-5 |
| 10 | §5.3 响应:示例由脚本实算回填 + 分组注释 + 字段语义 | B-2 / M-4 / L-3 / T-2b |
| 11 | §5.4 增 TOO_MANY_LOTS + 409 重试策略 + 503/409 非业务阻断注 |
M-2 / M-5 / L-4 |
| 12 | §7.0 插入「阶段 1.5 跑引擎」 | X-1 |
| 13 | §7.1 补偿路径:一期手动 + SLA 24h + 触发者 | I-3 |
| 14 | §7.3 审计关联 convert_group_id |
L-2 |
| 15 | §10 新增 Q9~Q12 | S-3 |
| 16 | §12 补 hold_days 定义 + 强制全转判断流程图 |
I-2 / I-4 |
| 17 | §13 新增「架构校准轮」评审记录 | L-1 |
| 18 | 种子 SQL 文件头加版本注释块 | L-6 |
| 19 | FR-C13 补 pnl_pct 口径;rebuild_lots.py 措辞订正 |
M-6 / L-7 |
| 20 | 全文:示例数字按新费率/精度/时序重算(脚本实算) | B-2~B-5 |
执行状态(2026-09-10):以上 20 项已全部落于 PRD v0.8。 示例数字由
scripts/dev/calc_convert_demo.py实算回填(口径 B:diff_fee = 252.40、in_qty = 53456.95、rounding_diff = -0.0026;口径 A 对照253.91/53455.36)。 下一步:架构设计 v0.2 按本表结论转 v1.0 → 送独立 AI 评审 → 第 4 步开发计划。
九、用户拍板记录(2026-09-10)
行业多口径 2 项 + 架构取舍 3 项,用户确认「按建议」:
| # | 事项 | 拍板结果 | 依据 |
|---|---|---|---|
| 1 | 补差费默认口径 | 口径 B(价外法两端差) | 与申购费价外法数学自洽;A 属近似写法,保留为 convert_diff_fee_mode 配置项 |
| 2 | 最低持有余额处置 | 两种都做(min_hold_action ENUM 两值 + 种子按产品设定) |
法规无强制(22 号文不涉及最低持有余额),两种均为真实基金合同约定 |
| 3 | P1 普通申赎无批次是否跳过 | 不跳过:批次表覆盖全部交易类型;遇历史持仓无批次 → 兜底补建,保证 FIFO 可执行、两表一致 | 跳过会导致批次表与持仓表永久失配 |
| 4 | P2 转出归零保留还是删行 | 保留(remain_qty = 0),不删行 |
与「审计只 INSERT」精神一致,保可追溯;避免 core_convert_lot_detail 断链 |
| 5 | P3 Core 侧明细是否对外查询 | 仅补偿,不对外查询 | 保持 Core 只读边界最小;对话侧走 agent 库粗粒度详情 |
落地位置:第 1、2 项属业务口径 → 已落 PRD v0.8(§2.1.1 / §4.2 / §10 Q9·Q12,§10 已加拍板状态说明); 第 3
5 项属架构层 → 将在架构设计 v1.0 落实(对应架构 v0.2 的 P1P3 待拍板项)。