文档:T-9 回写 + 全仓进度一致性(开发计划 DoD 全勾 + 执行记录 / 交接文档 v3.9 / TODO / 两份 MEMORY / AGENTS.md)
开发计划 T-9 新增执行记录(附 2 处口径缺陷细节);各入口进度统一
T-1~T-9 ✅ / 834 passed / 真库 verify_convert_api 88/88 / 下一步 T-10
This commit is contained in:
+20
-12
@@ -170,6 +170,8 @@
|
||||
| 8 | 引擎时机 | FR-C28 确认后跑 | §15 T-8 | **以 PRD 为准**:T-8 改时机 | 无冲突 |
|
||||
| 9 | 撤单范围 | 状态机表「`cancelled` ↔ accepted / nav_pending」(PRD :178/:181)与 §5.5 撤单接口(PRD :626:「T 日 15:00 前且状态 accepted / nav_pending → 200 置 cancelled」)**口径一致**——两者都允许 nav_pending 撤单。但 nav_pending 只在 T+1 确认时缺净值才出现,彼时 T 日 15:00 撤单窗口已过 | 架构任务 T-9(对接 R-9) | **裁定:仅 `accepted` 可撤(R-9)**;nav_pending 出现于 T+1,撤单窗口(T 日 15:00 前)早已关闭,PRD §5.5 所列条件在业务时序上不可达,保留 PRD 原表述但实现按 R-9 收紧为 `accepted` | 功能结果与「T 日 15:00 前」语义一致;第一轮审核曾误判「PRD 内部矛盾」(本行已订正:**矛盾不存在**,仅实现侧按窗口语义收紧) |
|
||||
| 10 | 15:00 截点 | PRD §2.6.1「`convert_cutoff_time` 为配置项(默认 15:00),**不得硬编码**」 | 架构任务 R-5(R-5 初稿写死 `before_cutoff(dt, 15:00)`) | **以 PRD 为准**:T-1 settings 新增 `convert_cutoff_time="15:00"`;TradingCalendar.before_cutoff 读 settings,禁硬编码 | 审核 B1 |
|
||||
| 11 | **T-9 三个接口路径** | §5.5 撤单 `POST /api/simulate/trade/convert/{convert_group_id}/cancel`;§5.7 确认 `POST /api/admin/convert/confirm?accept_date=…`、查询 `GET /api/simulate/trade/convert/{convert_group_id}` | 架构 §15 T-9 **未给路径**;**本计划 T-9 初稿自拟** `POST /api/convert/{gid}/cancel` / `POST /api/convert/confirm` / `GET /api/convert/{gid}` | **以 PRD 为准**(2026-09-11 用户拍板):T-9 按 PRD 三条路径实施,本计划初稿的自拟路径**作废** | PRD 是对外需求契约;其路径延续 `/api/simulate/trade/...` 与 `/api/admin/...` 既有风格,而初稿的 `/api/convert/...` 是新的顶格前缀、与全仓路由风格(`/api/risk`·`/api/simulate`·`/api/admin`·`/api/chat`)不一致。**根因留痕(审查为何未发现)**:计划的三轮独立审查均未发现本差异 —— §9 审核清单 9 条只对「架构 §15 / 代码事实行号 / tests 回归面 / PRD §9 **验收条目**」四类,**无一条对「对外契约逐字」**;且 §1.4 代码事实核对对的是**现有代码**、§1.3 差异裁定对的是「PRD **vs 架构**」,**都不覆盖「PRD vs 本计划自身」**。审查实际做的是「计划有没有**覆盖** PRD 的要求」,而非「计划有没有**擅自改** PRD 的承诺」—— 接口路径属后者,落在盲区。已补 §9 第 10 条 + 本表第 11/12 条 |
|
||||
| 12 | **确认接口参数 `accept_date` 的名与义** | §5.7 代码块注释写「按**受理日**批量确认」、参数名 `accept_date`;但同节表格口径写「可指定**业务日**」—— **PRD 内部字面冲突** | R-1:`confirm_batch(as_of)` 的 `as_of` 是**业务确认日(T+1)**,捞单窗口 = `[as_of 上推 sla_days 个交易日, as_of]` 的**受理日** | **裁定(2026-09-11)**:参数名**保留 PRD 的 `accept_date`**(对外契约字面不动),**语义取同节表格口径「业务日」**,直接映射 `confirm_batch(as_of=accept_date)`;接口响应回显实际生效业务日 | PRD 表格口径与 R-1 实现一致(业务日);若按参数名取「受理日」语义,需给 `confirm_batch` 增加「精确单日受理」能力 —— 会改动 T-7 已验收的捞单窗口逻辑,超出 T-9 范围,且无业务必要(窗口上推 SLA 本就是为消化净值晚公告造成的积压)。**遗留(已在接口 docstring 标注)**:窗口是「上推 SLA」的**批量**语义,非「精确某受理日」 |
|
||||
|
||||
### 1.4 开工前代码事实核对表(§2 产物 · 全部带 文件:行号)
|
||||
|
||||
@@ -572,10 +574,10 @@
|
||||
|
||||
**改法**:
|
||||
1. `simulate.py` convert 分支:返回改 `202 + {convert_group_id, status: 'accepted', requested_at}`(F-5 语义升级:202 从「未抢到执行权」改为「受理成功」;「未抢到执行权」仍 202 但并存 status 区分——**两类 202 需分辨**:`status='accepted'`=受理成功 / `status='processing'`=并发执行中,PRD §8.3)
|
||||
2. **新增 `app/api/convert_admin.py`**:
|
||||
- `POST /api/convert/{group_id}/cancel`:撤单(R-9;accepted + 窗口内 → cancelled;否则 409 `CANCEL_NOT_ALLOWED`);鉴权:**本人可撤(交易 owner 闸门,红线 7)**
|
||||
- `POST /api/convert/confirm`(body: `as_of`):触发批处理(R-1 → `confirm_service.confirm_batch(as_of)`;运维接口,鉴权同 admin 级)
|
||||
- `GET /api/convert/{group_id}`:查询状态 + 折算结果(确认后才有折算金额,Q2;未确认 → status + 无金额);鉴权:本人/代理人可查(查询 scope 闸门)
|
||||
2. **新增 `app/api/convert_admin.py`**(**路径以 PRD 为准**,见 §1.3 裁定 11/12):
|
||||
- `POST /api/simulate/trade/convert/{convert_group_id}/cancel`(PRD §5.5):撤单(R-9;accepted + 窗口内 → cancelled;否则 409 `CANCEL_NOT_ALLOWED`);鉴权:**本人可撤(交易 owner 闸门,红线 7)**
|
||||
- `POST /api/admin/convert/confirm?accept_date=<业务日>`(PRD §5.7):触发批处理(R-1 → `confirm_service.confirm_batch(as_of=accept_date)`;运维接口,鉴权同 admin 级);**参数名沿用 PRD `accept_date`、语义取 PRD §5.7 表格口径「业务日」**(裁定 12)
|
||||
- `GET /api/simulate/trade/convert/{convert_group_id}`(PRD §5.7):查询状态 + 折算结果(确认后才有折算金额,Q2;未确认 → status + 无金额);鉴权:本人/代理人可查(查询 scope 闸门)
|
||||
3. `trade_gateway.py`:**`SUPPORTED_TRADE_TYPES` 不动(F-25/O2)**——convert 走既有独立 `_submit_convert` 分派(trade_gateway.py:105),把内部实时确认改调 `accept_convert`(受理),**不把 CONVERT 加进支持元组**(避免改变申赎路径语义)
|
||||
4. `errors.py`:新增 `CANCEL_NOT_ALLOWED`(409)/ `CONCURRENT_CONFLICT`(锁冲突);既有错误码保留
|
||||
5. redeem 改份额申报(D26 · R-6):`TradeRequest` redeem 分支 `qty` 入参(替代 amount 反算);网关 redeem 调 `_maintain_lots` 新签名
|
||||
@@ -583,13 +585,15 @@
|
||||
**依赖**:T-7
|
||||
|
||||
**DoD**:
|
||||
- [ ] API 契约用例:受理 → 202 + accepted;**撤单**窗口内成功(cancelled + 占用释放)、窗口外 409(验收 23);确认后撤 → 409
|
||||
- [ ] 查询接口:确认前无折算金额字段、确认后有(Q2)
|
||||
- [ ] 两类 202 分辨用例存在
|
||||
- [ ] **`SUPPORTED_TRADE_TYPES` 维持 2 类型(SUBSCRIBE/REDEEM,O2)**;convert 走独立分派不再 400(验收 1)
|
||||
- [ ] redeem 份额申报用例:`qty` 入参与 `amount` 反算路径断言的既有用例**改写后**绿(回归面 F-25→R-回归 1)
|
||||
- [ ] 撤单/查询接口鉴权用例:本人可撤/可查、代理人可查不可撤、他人 403(红线 7)
|
||||
- [ ] pytest 全量绿(test_integration_risk 中旧 400 断言已改为受理 202;test_risk_api 无 convert 断言、零改动,C4)
|
||||
- [x] API 契约用例:受理 → 202 + accepted;**撤单**窗口内成功(cancelled + 占用释放)、窗口外 409(验收 23);确认后撤 → 409
|
||||
- [x] 查询接口:确认前无折算金额字段、确认后有(Q2)
|
||||
- [x] 两类 202 分辨用例存在
|
||||
- [x] **`SUPPORTED_TRADE_TYPES` 维持 2 类型(SUBSCRIBE/REDEEM,O2)**;convert 走独立分派不再 400(验收 1)
|
||||
- [x] redeem 份额申报用例:`qty` 入参与 `amount` 反算路径断言的既有用例**改写后**绿(回归面 F-25→R-回归 1)
|
||||
- [x] 撤单/查询接口鉴权用例:本人可撤/可查、代理人可查不可撤、他人 403(红线 7)
|
||||
- [x] pytest 全量绿(test_integration_risk 中旧 400 断言已改为受理 202;test_risk_api 无 convert 断言、零改动,C4)
|
||||
|
||||
**✅ T-9 完成(2026-09-11)**:`app/api/convert_admin.py`(**路径以 PRD 为准**,裁定 11/12)三接口 + `simulate.py` convert 分支 202 + `trade_gateway` 分派改调 `accept_convert`(`SUPPORTED_TRADE_TYPES` 维持 2 类型)+ redeem 份额申报 + 错误码新增 `CANCEL_NOT_ALLOWED`(409)/`CONCURRENT_CONFLICT`(409)。**验证**:① 端到端 `tests/test_convert_integration.py` 重写为 T+1 两段链路 + 新增 API 契约/鉴权用例(**13 passed**);② 全量 pytest **834 passed / 10 skipped**(基线 829 + 新增 5);③ 真库新脚本 `verify_convert_api.py`(HTTP 全链路)**88/88**,T-6/T-7/T-8/v1.0 四脚本复跑**零回归**(37/37 · 83/83 · 39/39 · 35/35);④ **3 组突变验证**(受理回执/未确认查询泄漏折算字段、顾问误放行撤单)**全部命中后还原**。**执行期发现并修正 2 处口径缺陷**:① `_rebuild_quote` 的 `out_nav` 原从「金额÷份额」反推(0.005 档 HALF_UP 舍入致 1.3604 → 1.3600 漂移)→ 新增 `_out_nav_rebuild` 优先直读 `core_convert_lot_detail.nav`(与确认段首次折算同源,缺失才回退反推);② `_accept_idempotent` 字段面扩展与受理回执对齐(补 `requested_qty/qty/forced_full_transfer/accept_date/confirm_date/available_date/cancel_deadline/actual_qty`,原仅 qty/actual_qty/requested_at → 幂等重试客户端否则拿不到预计确认日)。
|
||||
|
||||
**风险**:契约变更打穿 test_integration_risk 的 convert→400 断言 → 回归面 §5 处置(test_risk_api 无 convert 断言、非风险项,C4)
|
||||
**回滚点**:API 加字段不删旧字段(兼容期);撤单接口新增无破坏
|
||||
@@ -900,4 +904,8 @@
|
||||
6. **依赖排序是否与架构 §15 冲突**(P1 已剔除 T-8;T-16/T-17 依赖已改 T-7/T-6)
|
||||
7. **验收 31 条是否无落空**(对照 §6 表逐条;**C2b:FR-C29/C30 已补独立行 32/33**)
|
||||
8. **R-b / 红线 1/2/3 是否被无意违反**(trade_type 仍是 redeem/subscribe;amount_view 三处同步;_q 单点量化)
|
||||
9. **B1/B2 是否已闭环**(`convert_cutoff_time` 配置全链读 settings;确认段净值判定用 `get_nav_on` 精确匹配、禁 `get_nav_as_of` 回退)
|
||||
9. **B1/B2 是否已闭环**(`convert_cutoff_time` 配置全链读 settings;确认段净值判定用 `get_nav_on` 精确匹配、禁 `get_nav_as_of` 回退)
|
||||
10. **对外契约是否逐条与 PRD 比对**(**T-9 实测补** · 2026-09-11):凡本计划中**新出现或改写**的对外契约 —— **API 路径 / 参数名 / 响应字段名 / HTTP 状态码 / 错误码** —— 必须回到 PRD 原文逐条对齐,并注明 PRD 出处(章节或行号)。
|
||||
- **为什么单列一条**:T-9 初稿自拟的三个接口路径与 PRD §5.5/§5.7 给出的路径**完全不同**,而**三轮独立审查(挑毛病 / 验证型 / 白纸重审)全部未发现** —— 原因是本清单前 9 条对的是「架构 §15 / 代码事实行号 / tests 回归面 / PRD §9 验收**条目**」,**没有一条对接口契约本身**;审查做的是「计划是否**覆盖** PRD 的要求」,而非「计划是否**擅自改** PRD 的承诺」,契约改写恰好落在盲区。
|
||||
- **做法**:对本计划所有 `/api/...` 字符串、`FIELD_NAME` 常量、`409`/`422` 等状态码做一次 `grep`,逐条问「PRD 原文怎么写的?出处是哪一节?」,**自拟而无出处的必须标出并请用户裁定**。
|
||||
- **同源规律(与铁律 10 一致)**:**带「文件:行号」的断言会被逐条验;不带出处的自由书写没有锚点 → 必漏**。
|
||||
Reference in New Issue
Block a user