背景:交接文档的定位是「给 AI 接手用的入口」,此前三份分散(项目根 交接文档.md
+ docs/交接文档-基金转换.md + docs/交接文档-架构改进.md),且 docs/ 两份停留在旧版
(v1.0 / v1.1,不含 T-0~T-2b、609 passed、D20 实施等进度),新会话极易被误导。
改动:
- 三份合并为项目根 交接文档.md v3.0(545 行 = §0 公共层 + §A 风控主线 + §B 基金转换线
+ §C 架构改进线);该文件在 .gitignore:47 内,按用户要求不入库(交接文档只留本地)
- 全仓指向统一到 交接文档.md §A/§B/§C:AGENTS.md(含顶部新增「接手先读交接文档.md」)、
docs/memory/{MEMORY,TODO,FRAMEWORK,ITERATION}、PRD-架构改进与稳定性加固、
开发计划-架构改进、TODO-架构改进、开发计划-基金转换交易
- 两份 docs/交接文档-*.md 加「已废弃(2026-09-10)· 勿读」横幅并指向新入口,
保留作历史留档(不删除)
- 记录事故:对 docs/ 下中文名文件使用 git rm 会静默抹除整个 docs/ 目录(复现 2 次、
退出码 0),已零损失恢复;纪律写入 交接文档.md §0.4 与工作区记忆
无代码改动;pytest 609 passed / 3 skipped。
13 KiB
交接文档 · 基金转换(convert)交易线
⛔ 本文件已废弃(2026-09-10)—— 请勿据此开工
内容已全部并入项目根
交接文档.md§B(三线合并版唯一交接入口),且本文件停留在 v1.0(设计阶段完结), 不含 T-0 / T-0b / T-1 / T-2 / T-2b 的落地进度与 609 passed 现状。 ⚠️ 下方「下一步是第 4 步开发计划」等表述早已过期(第 4 步、第 5 步前两批均已完成)。 正确入口:交接文档.md§B —— 本文件仅作历史留档,内容已过期,勿读。
版本:2026-09-10 v1.0(设计阶段完结,交接给开发会话) 用途:给接手本线的 AI / 人。读完本文即可开工,不需要重读全部代码。 代码基线:分支
risk-control-agent(HEAD 以git log -1为准);pytest 510 绿。 ⚠️ 本线代码尚未改动一行 —— 当前只完成了「讨论需求 → PRD → 架构设计」三步(AIcoding 流程第 1~3 步),下一步是第 4 步开发计划。
⚡ 当前状态(一句话)
| 项 | 状态 |
|---|---|
| 第 1 步 讨论需求 | ✅ 完成(多轮讨论 + 外部事实核验) |
| 第 2 步 PRD | ✅ v0.9 定稿(33 条外审闭环 + 2 处架构回填) |
| 第 3 步 架构设计 | ✅ v1.0 定稿(独立评审通过,13 条建议 0 悬空) |
| 门控 M-7(PRD 未定稿不得进第 4 步) | ✅ 已满足 |
| 第 4 步 开发计划 | ⬜ ← 下一步做这个 |
| 第 5 步 todo 开发 / 第 6 步 集成测试 | ⬜ 未开始 |
| 代码改动 | ⬜ 零(架构里的 T-1 之后才有代码) |
开工前必须先做的两件阻断前置:T-0(sqlite/MySQL 列名统一)与 T-0b(DB 账号分离)。
1. 五分钟背景
1.1 项目是什么
XingHuo 智能财富管家:金融四 Agent(客户财富 / 代理人 / 数据分析 / 风控)共用数据层与合规底座,四 Agent 不互调 LLM。
技术栈:Python 3.13 + FastAPI + SQLAlchemy Core(text SQL,无 ORM)+ MySQL 双库(jinrong_core 模拟 Core / jinrong_agent 业务)+ Redis + Milvus + Neo4j。
1.2 本线要做什么
docs/PRD/PRD-风控监测Agent.md 的 FR-1 一期显式拒收 convert(trade_gateway 里直接 400 拒绝)。
本线把它放开为可交易:新增基金转换(转出 A 基金 → 转入 B 基金)的完整链路。
1.3 业务本质(看懂这段才不会写错)
一次基金转换 = 一次赎回(转出)+ 一次申购(转入),但不是两笔独立交易:
- 未知价法:按申请当日净值计价,T 日申请 → T+1 权益登记(扣转出、登转入)→ T+2 可用
- 逐批次计费:按
core_share_lot的批次,每批按各自持有期查赎回费率(<7 日 1.5% / 7–30 日 1.0% / 30–180 日 0.5% …) - 先进先出(FIFO):注册日期在前的批次先转出
- 单笔计算法:当日多笔转换不合并计费
- 补差费:转入费率高于转出费率时补差额(口径 B 为默认,见 §3)
- 份额/金额一律 2 位四舍五入,误差「在基金资产列支」(
rounding_diff可正可负)
合规基准:证监会公告〔2025〕22 号《公开募集证券投资基金销售费用管理规定》(2026-01-01 施行) —— 申购费率上限(主动偏股 ≤0.8% / 其他混合 ≤0.5% / 指数·债券 ≤0.3% / 货基 0)、赎回费下限(<7 日 ≥1.5% …)、赎回费全额计入基金财产。
2. 设计资产(读这四份就够,不用翻代码)
| 顺序 | 文档 | 看什么 |
|---|---|---|
| 1 | 本文 | 全局、状态、坑、禁止事项 |
| 2 | docs/项目框架设计/架构设计-基金转换交易.md(v1.0) |
怎么实现:§2 目录 / §3 时序 / §4 决策 D1~D20 / §5 事务补偿 / §7 计算口径 / §8 契约 / §9 DDL 落点 / §10 测试 / §11 配置 / §12 风险门禁 / §15 任务映射 |
| 3 | docs/PRD/PRD-基金转换交易.md(v0.9) |
需求权威:场景 FR-C1~C16 / §4 表结构 / §5 接口 / §7 流程与事务 / §9 验收 |
| 4 | docs/项目框架设计/评审待办-风控主架构与基金转换.md |
评审逐条判定与核实证据(§二)+ 交叉点(§三)+ 执行期风险(§四) |
| 5 | docs/项目框架设计/基金转换-审查意见处置表.md |
33 条外审处置 + §九 用户拍板记录 |
3. 已拍板的关键决策(不能改,改了会破坏已验证方案)
| # | 决策 | 口径 |
|---|---|---|
| 补差费 | 口径 B(价外法两端差)为默认,A(费率差法)以 convert_diff_fee_mode='rate_diff' 保留 |
|
| 份额精度 | 2 位 ROUND_HALF_UP(不是 4 位、不是向下舍);金额同 |
|
| T+1 确认 | 建模:confirmed_at = T+1 自然日(近似,注释标明真实为工作日);持有期自确认日起算 |
|
| 最低持有余额 | 两种都做:min_hold_action ∈ {force_transfer, force_redeem},默认 force_transfer |
|
| 普通申赎无批次 | 不跳过 → 按 core_holding.as_of 兜底补建初始批次(与 rebuild_lots.py 同调 lot_bootstrap.bootstrap_lots,D18) |
|
| 转出归零 | 保留 qty=0 行,不删;持仓查询过滤 qty <= 0 |
|
| Core 侧明细 | 仅补偿,不对外查询(core_convert_lot_detail) |
|
| 引擎时序 | 阶段 1.5:阶段一提交后、阶段二之前同步跑 process_convert_event;失败 不阻断交易(D17) |
|
| 引擎异常钩子 | on_error_hook=None 预留(D19),二期注入 Redis 事件/补偿队列;hook 异常不得反噬主流程 |
|
| DB 账号分离 | D20:xh_core_ro(SELECT)/ xh_core_rw(4 表写,无 DELETE/DDL)/ xh_agent_rw(audit_log 只授 INSERT);见 §11.1 |
|
| 批次上限 | convert_batch_max_lots=200,超限 400 TOO_MANY_LOTS,不自动分拆;单笔 = 单事务 = 单 convert_group_id |
|
| 交易发起主体 | 仅客户本人或 risk_demo;代理人只能查、不能交易(simulate.py:42 现状即如此,convert 沿用) |
4. 开工前置(⛔ 阻断,两个都要绿)
| 任务 | 内容 | 门禁 |
|---|---|---|
| T-0 | 以 MySQL 为准统一 sqlite DDL:tests/_ddl.py 的 core_holding 改成 qty/cost_amount/as_of/pnl_pct + 主键(现状 tests/_ddl.py:50-54 只有 market_value/quantity 且无 PK);conftest.py 加启动期列名断言 |
pytest tests/test_db.py::test_core_holding_columns 必绿才准跑 T-1 之后 |
| T-0b | DB 账号分离(D20):scripts/core/00-grant.sql 建 3 账号授权;settings.py 加 3 组账号;db.py 改 get_engine(database, role="rw")(缓存键改 (库名, 角色));core_ro 走 ro、gateway 写路径走 rw(读仍 ro) |
3 个真 MySQL 权限断言:ro 账号 INSERT 被拒 · audit_log 改/删被拒 · rw 账号改第 5 张表被拒 |
账号未配置时回退单账号(
settings.mysql_user)——本地开发不被阻塞,但真 MySQL 集成测试必须跑在拆分账号下。 sqlite 测试路径零影响(无账号概念),现有 510 用例一行都不用改。
5. 任务清单(架构 §15 已定稿,可直接作为开发计划输入)
T-0 (⛔ 列名统一 + 门禁)─┐
T-0b(⛔ DB 账号分离 D20)─┴─► T-1(DDL/种子 00-grant·01-ddl·07·08·09·02-agent + reset.ps1)
└─► T-2(纯函数 types/calc/fee/nav/lot_bootstrap)─► T-2b(calc_convert_demo 实算回填)
├─► T-3 ─┐
├─► T-4 ─┼─► T-6(阶段一事务)─► T-7(编排 · 关键路径)
└─► T-5 ─┘ ├─► T-9(API + gateway 分派)
└─► T-12(补偿脚本)
T-8(引擎改造 · 可全程并行)─► T-11(工具汇总去重)
T-10(批次维护 · 回归风险最大:先跑 510 基线再动)
└─► T-13(全量回归 + 50 并发压测 + 性能补录)
- 并行组 A:T-3 / T-4 / T-5(只依赖 T-1,可与 T-2 同时开工)
- 并行组 B:T-8 与 T-2 之后任意阶段并行
- 关键路径:T-0 → T-1 → T-2 → T-6 → T-7 → T-13
- 高风险任务:T-10(改
trade_gateway主流程,直接影响现有 510 用例) - 基线:510 → 预计 580~600 用例
6. 执行期风险(用户 2026-09-10 提供,架构 §12 已并入三列表)
| # | 风险 | 必须守住的验收点 |
|---|---|---|
| 1 | T-0 未完成就跑 T-1~ | CI 门禁 test_core_holding_columns 必绿 |
| 2 | 批次补建规则写两处 → 漂移 | D18 抽 lot_bootstrap 纯函数,两侧同调;单测断言同源 |
| 3 | Decimal.quantize 默认 ROUND_HALF_EVEN |
calc/fee/nav 所有量化处显式传 ROUND_HALF_UP;.5 边界断言 |
| 4 | 阶段 1.5 引擎异常丢预警 | D19 预留 on_error_hook;hook 抛异常不得反噬主流程 |
| 5 | convert_batch_max_lots=200 超限 |
400 错误体带 batch_count/max_lots;前端提示「请拆分多笔申请」 |
7. 本线特有的坑
- sqlite 与 MySQL 列名不同名(T-0 根因):
tests/_ddl.py:50-54的core_holding用market_value/quantity,MySQL 用qty/cost_amount/as_of/pnl_pct,且无 PK。手工同步无迁移工具,不先统一一定在集成测试期才炸。 Decimal.quantize()默认是银行家舍入(ROUND_HALF_EVEN),必须显式传ROUND_HALF_UP,否则.5边界错。core_ro.py:390/:432写死了AND trade_type IN ('subscribe','redeem')—— 不改的话 convert 在 RISK-001/002/003 聚合里完全隐形且不报错(架构 D7_amount_view就是为此)。- 改路由必须同步
tests/test_main.py::test_all_routers_mounted的路径清单,否则必红。 - 测试/演示库重灌两步缺一不可:
scripts/core/reset.ps1+scripts/demo/prepare_risk_demo.sql(只跑第一步 → 27/33 客户风评过期,是种子设计不是 bug)。 - 用系统 Python 3.13.14 跑测试:
C:/Users/YUAN/AppData/Local/Programs/Python/Python313/python.exe(managed 3.13.12 未装依赖)。 - sqlite 不支持绑定
Decimal,测试插core_trade.amount须转 float。 - 双库无跨库事务(
jinrong_core/jinrong_agent各自 engine)→ 所以有「三阶段 + 补偿」,不要试图写一个跨库事务。
8. 禁止事项
项目五条红线(碰了就是事故):Core 表只读 · 审计表只 INSERT · 代理人草稿不外发 · 仅 R-02 可阻断交易 · 四 Agent 不互调 LLM。
本线纪律:
- ❌ 不改表结构 / 接口协议而不经用户确认(改表需确认是项目硬规则)
- ❌ 不做清单外的改动(看到别的问题记下来,别顺手改)
- ❌ 不动
tests/_ddl.py之外的第二份 sqlite DDL(单一事实源) - ❌ 不新增第三方依赖
- ❌ 所有阈值不得硬编码(走
settings.py,架构 §11) - ❌ 提交前必须
pytest全绿;推送前须用户目视验收
9. 另一条线的遗留(别混淆,不在本线范围)
架构改进与稳定性加固线(T-101~T-202)2026-09-10 已全部收尾闭环:
- §7.2 七项手工冒烟已补跑完毕,7/7 PASS(含「停 Redis → 退回进程内锁」「调换装饰器 → T-202 守卫变红」两项)
→ 本文原表述已过期:实测037ce7e已 commit 未 pushgit ls-remote,远程risk-control-agent=fffb78a= 本地 HEAD,037ce7e早在祖先链上,该线全部提交已推送,无待推内容- 冒烟产生的演示库残留已按 SOP §2 重灌清除,全量 510 passed 复绿
明细与入口见 交接文档.md §C(原 docs/交接文档-架构改进.md 已废弃为历史留档)。本线 T-0b 与其无关(那是基金转换自己的前置)。
10. 完成后要回写的文档
docs/memory/TODO.md—— 勾选进度docs/memory/MEMORY.md§0 —— 进度与仓库地图docs/项目框架设计/开发计划-基金转换交易.md—— 第 4 步产出(待创建)docs/memory/YYYY-MM-DD.md—— 当日工作日志- 本文件 —— 状态块与任务清单勾选
11. 有问题怎么办
- 发现文档与代码不符 → 以代码为准,并在 TODO 备注记下差异(先核实,别急着改文档)
- 对设计有疑问 → 查架构对应章节;仍不明确则停下问用户,不要猜
- 评审意见与自己的判断冲突 → 先核实证据(文件行号),不盲从也不护短;驳回要给出核实过的证据