diff --git a/docs/交接摘要.md b/docs/交接摘要.md index 1803de4..354c7b6 100644 --- a/docs/交接摘要.md +++ b/docs/交接摘要.md @@ -833,3 +833,86 @@ P4 写 M5 校验引擎时,我凭记忆用了一套 VAL 编号,**与 PRD 权 - **B3** 是否允许学生修改老师预填字段(建议:姓名/证件号/报考三项锁定,其余可改留痕) 未回复则按文档内建议方案推进。 + + +--- + +## §一·十三 材料上传 + 提交校验(最新一轮,提交 `9911ff0`) + +### 已完成的提交 +| 提交 | 内容 | +|---|---| +| `80a4050` | 材料上传与规范化(压缩到 1M 内 + 按规则改名) | +| `8ef0973` | 材料上传前端界面 + 列出/删除接口 | +| `9911ff0` | 提交 + 红黄绿校验(VAL 系列)+ 静态资源缓存头 | + +### 学生端主流程已打通 +``` +扫码 → 微信授权 → 落地页 → 核验身份 → 填报页(三态+级联) + → 存草稿 → 传材料(自动压缩改名)→ 提交(红黄绿校验) +``` + +### 实测结论(可直接引用的数字) +- **压缩**:11.6MB 的 2400×3200 原图 → **712KB**,1020×1360(P2 档长边限 1600),q70 +- **改名**:`身份证正面.jpg` → `张同学-361029XXXXXX0015-身份证.jpg` +- **校验**:`e2e-m3-submit.py` **30/30** + +### ⚠️ 本轮五个坑(都已写进代码注释,不要再犯) + +1. **校验器不能用列名当字段 key**。字段 key 是 **snake_case** + (`work_unit`/`job_level`/`enroll_date`),权威来源是 `BuiltinFields`。 + 且**报考三项存在 `ext_json`,不是独立列**(契约 §2.4)。 + 症状:"明明填了却说没填"。 + **更严重的是**:同一条数据被两条规则用两种方式读 → 两条规则互相矛盾 + (VAL-16 说缺、VAL-22 说不缺)。**统一用 `FillService.getForm` 装配值做唯一口径。** + +2. **`collect_roster.id_card_no` 是 AES 密文**(列注释写明)。 + `id_card_hash` 是 HMAC、`id_card_mask` 是脱敏展示。 + 明文只在**导入**与**门禁**时短暂存在 → 提交时**做不到**校验位校验。 + 曾拿它当明文算校验位 → **100% 阻断**(这种"规则很对但读错列"的错, + 反而会被当成"校验太严"而不是 bug)。 + +3. **`VAL-22` 必须判可见性**,求值口径与前端 `fill.vue` 的 `visible()` **逐字一致** + (`norm` 归一 `true/false`↔`1/0`;未知 `op` 一律**可见**=fail-open)。 + 不判可见会要求在校生填「补考理论」—— 而那个字段在他页面上根本不存在。 + +4. **静态资源必须有 `Cache-Control`**(新增 `StaticCacheHeaderFilter`)。 + 没有它浏览器走**启发式缓存**:旧 `index.html` → 旧 chunk → + **学生发版后看到的是上个月的页面**,而开发机硬刷新一次就没了, + 极易被当成"偶发"。现在:`/assets/**` 一年 `immutable`(文件名带内容哈希)、 + `*.html` `no-cache`、`/api/**` `no-store`。 + > 上一轮"浏览器加载旧 chunk"的悬案就是它 —— 已用 + > 「从 8080 下载 chunk 与磁盘文件比 SHA-256」证实不是构建问题。 + +5. **`edit` 工具的 `oldString` 是子串匹配**。上一轮我把 `function doSubmit() {` + 当锚点,而原文是 **`async function doSubmit() {`** —— 于是 `async` 被留在 + 注释前面,变成 `async // ── …`,编译**通过**、构建**通过**, + 但运行时 `ReferenceError: async is not defined` → **整页空白**。 + **教训:构建成功完全不能代替"跑一遍"。** 锚点要带足够上下文。 + +### 环境/数据注意事项 +- 演示名册(task 17)的 `id_card_no` 是 `placeholder`(某次手工 SQL 留下的, + 代码里搜不到这个串)。**这是正常的开发态**:门禁走 `id_card_hash`、 + 填报页走 `id_card_mask`,都不需要明文。**不要去"修"它。** +- `collect_form_schema` **是空的** → 演示任务走 `BuiltinFields` 的官方 21 项模板。 +- 字段 key 权威列表见 `BuiltinFields.java`(21 项,全 snake_case)。 + +### 契约层面的两处有意偏差(必须让客户知道) +| 项 | 偏差 | 原因 | +|---|---|---| +| 上传 | 契约是 `POST /mp/files/upload-token`(OSS 直传凭证),实际**后端代理接收**(multipart) | 后端还没接对象存储。硬做一个"返回假凭证"的接口只会让人以为能直传。接入后可并存:小文件走代理、大文件走直传 | +| 文件存储 | **本地磁盘**(`fill.upload.dir`) | 多实例部署时各存各的会丢文件 → **接入对象存储前不能上多副本** | + +### 新增规则(需客户确认) +`VAL-22 = 表单必填字段缺失`。PRD 的 VAL-01~21 是**具体业务规则**, +没有覆盖「动态表单的必填字段」(因为表单字段是配置出来的)。 +按系列顺延补的 —— **这是新增,不是 PRD 原有条目**,需要登记。 + +### 下一步 +1. **提交页在浏览器里的肉眼验证还没做**(问题清单浮层的渲染与交互)。 + 代码与产物都对,但**未经浏览器验证** —— 不要当成已验证。 +2. `M1-05` 规则模板、`M1-07~M1-10` 前端(表单设计器、材料规则界面、任务向导) +3. 材料与表单的条件联动(`visible_when` 后端已存,材料侧前端还没求值) +4. 接入对象存储(**阻塞多副本部署**) +5. 待办(非阻塞):C1 小程序资质法务确认(**已明确会阻塞学生真正开始填报**)、 + C5 隐私声明法务文案、D1 OCR 引擎选型、D5 一寸照片阈值标定、D6 微信三通道体积上限真机实测