docs: 完成度盘点(F-1~F-97)、确认函 E 组、交接摘要 §一·二十八与 AGENTS 更新

docs/08 是计划与现实的对账:完成度表、未完成按「能不能上线」排序(P0/P1/P2 + 卡点→找谁→编号)、
97 条编号问题、施工记录 6.1~6.4(每条都带验证方式,不写没有证据的「已完成」)。

docs/01 新增 E 组第二轮待确认:E1 union_id(H5 与小程序不能同时上线的阻断项)、
E2 D3 限流口径更新、E3 二期时点;D3 那行划掉。

交接摘要 §一·二十八 把坐标从「M5 后端」刷新到「一期功能收口」:120/121、V17、
27 实体/33 表、36 脚本/1548 断言,以及这 20 多轮最该记住的 7 条(含 F-96:
三处「没断言就往下走」把排查引向了完全错误的方向)。

AGENTS.md:H5 那条「没有可直接联调的独立 dev server」已过时,改为三端行为的说明。
This commit is contained in:
2026-09-30 09:02:58 +08:00
parent b94db65ec4
commit 8debf371bf
4 changed files with 656 additions and 4 deletions
+83
View File
@@ -2254,3 +2254,86 @@ E2E 一开始给材料传了张 900×1200 的小图 —— 而**身份证材料
**M5 的前端**:班主任端卡片流(原型 ②,**只右滑通过**、左滑不绑定驳回)、
学生详情页(原型 ③)、驳回页(原型 ⑤,原因快选 + 二次确认)、
一键通过确认页(原型 ④)、管理端终审列表与详情(原型 ⑥)。
---
## §一·二十八 状态刷新:从「M5 后端」到「一期功能收口」(2026-09-30)
> ⚠️ **先读这一节**。上面 §一·二十七 停在 2026-09-29 的"M5 审核闭环后端",
> 之后又推进了 20 多轮。**本轮(§一·二十八)把坐标刷新到当前**,
> 细节在 `docs/08-完成度盘点与问题清单.md` §六 的施工记录(6a~6u,逐步有据)。
### 现在的坐标(都可当场核对)
| 项 | 现在 | 怎么核对 |
|---|---|---|
| 契约接口实现度 | **121 个里 120 个**(99%),唯一缺口 `/mp/ocr/idcard` | `python scripts/check-api-parity.py --missing` |
| 数据库迁移 | **Flyway V17**(不是 1~6) | `docker exec xt-pg psql -U fill -d filldb -c "select version,description from flyway_schema_history order by installed_rank desc limit 3"` |
| 实体 / 表 | **27 个实体 / 33 张表**一致 | `python scripts/check-entity-columns.py` |
| E2E | **36 个脚本 / 1548 条断言 / 0 失败** | 依次跑 `scripts/e2e-*.py`(或看 §三 的说明) |
| 文档一致性 | `verify-docs.py` **22 项全过** | `python scripts/verify-docs.py` |
| 手册与文案 | 手册引用的错误码/文案**逐条对得上代码** | `python scripts/check-manual.py` |
| CI | `.gitlab-ci.yml` 三阶段 6 个 job(校验/构建/**全套 E2E 打真库**) | 读文件;本地可验的部分见 `docs/09` §8 |
| 生产自检 | `ProdEnvCheck` 启动前拦 6 类"配错了也能启动"的配置 | `python scripts/e2e-prodguard.py`(真的起 jar) |
| 定时任务 | PRD 附录 E **8 项已实现**,`POST /schedules/run` 可手工触发 | `python scripts/e2e-m7-schedule.py` |
| PC 页面 | `ready:false` 只剩 4 个,其中 3 个是"入口在任务行里" | 见下面"页面到底还剩什么" |
### 从 §一·二十七 之后做完的(按施工记录 6a~6u)
M1 规则模板与任务克隆 → M2 报考范围与名册改删 → M3 提交与门禁限流口径 →
**M4**(未做,见"卡在哪")→ M5 代传/催报/看板/通知中心 → **M6 导出交付**
(前置检查/分卷/过期清理)→ **M7 历史整理器 + 定时任务 + 运维接口** →
**§14 开放接口(认定机构对接,HMAC)** → 小程序登录与组织导入 → 工作台数据看板 →
CI/CD 与 test/prod 环境 → 压测 → 用户手册与客服话术 → 缩略图与 H5 dev server。
**每一轮的"做了什么 / 发现了什么"都在 docs/08 §六**,包含 97 条编号问题(F-1~F-97)。
### 这 20 多轮里最该记住的几条
按"不知道会踩第二次"的排序(不是按严重程度):
1. **`Map<String,Object>` 结果的 SQL 别名会被 PostgreSQL 折成小写**(F-54、F-83)。
`AS tenantId` 在 Map 里是 `tenantid`,`row.get("tenantId")` 得到 `null` ——
而**返回 POJO 的 mapper 不受影响**(MyBatis 忽略大小写),所以"照抄别的 mapper"必中。
表现极其安静:定时任务照跑、日志一行 warn、**什么都没做**。
**规矩:这个场景的别名一律 `AS "camelCase"`。**
2. **「HTTP 200 + code 0」不等于业务动作成功**(F-84)。
提交、发布、导入、批量审核的**结论在 `data` 里**(`submitted` / `created` / `canExport`…)。
夹具断言"动作成功"必须断言那个结论字段 —— 判错的表现是"夹具通过了,
后面所有断言全红",看起来像被验的功能坏了。
3. **租户拦截器默认拒绝,平台身份(tenantId=0)才跨租户**。
定时任务与匿名接口都走"先以平台身份扫候选 → 再逐个租户摆上下文干活"两段式;
**写数据必须在第二段**,否则 `tenant_id` 被写成 0,
通知写进平台租户、那个学校的人永远看不到,**而且不报错**。
4. **配置检查的代码要能区分"没配"与"配了"**(F-86)。
`env.getProperty` 会解析占位符、失败就抛;只读 `PropertySource` 又读不到 env 的值。
正确做法是 `try { getProperty } catch { 当没配 }`。
5. **度量工具的偏差会伪装成产品缺陷**(F-60、F-77、F-97)。
三次都是"断言打在错的东西上":POI 与 openpyxl 的"红色"索引不同、
Arco 无 `scroll.y` 时 tbody 不带 class、`url` vs `thumbUrl`。
**断言之前先确认"你在量的那个东西是对的"。**
6. **并发压测的数字不能反推单请求的成本结构**(F-94)。
要回答"哪里慢"必须用"数 SQL 条数 + 单请求计时 + EXPLAIN"这三样。
7. **清理只在"正常结束"时跑,而失败路径才是残留的主要来源**(F-85)——
E2E 要**开跑前也清一次**。
### 页面到底还剩什么(`ready:false` 只剩 4 个)
| 菜单 | 真相 |
|---|---|
| 名单管理 / 材料规则 / 表单设计器 | ✅ **功能早就做好了**,它们是**按任务**的,入口在「收集任务」每一行的链接里。「待建」页现在会明确指路并给跳转按钮 |
| 角色与授权 | ⏸ **二期范围**(内置角色的权限由代码决定、不可改) |
### 卡在哪(**全部是外部依赖**,每一条都指向确认函的具体编号)
| 卡点 | 谁要动 | 在哪问 |
|---|---|---|
| `/mp/ocr/idcard`(唯一未实现接口)+ 形态识别 | 需先定 OCR 供应商 | `docs/01` **D1** |
| 小程序类目资质 | 法务/商务 | `docs/01` **C1** |
| `union_id`(H5 与小程序不能同时上线) | 需微信开放平台账号并绑定 | `docs/01` **E1**(第二轮新增) |
| 真机验证(压缩与体积上限) | 需真机 | `docs/01` **D6** |
| 二期启动时点 | 客户 | `docs/01` **E3** |
⚠️ **不要在没有这些输入的情况下去"顺手实现"**:M4 的识别链路一旦按某家供应商写死,
换供应商要重写;`union_id` 没拿到之前,"按 union_id 认人"也无从下手
(字段与方案都已就位,见 `MpH5AuthService` 类头注释与 `sys_user.union_id`)。