Files
xiaotutongxue/docs/交接摘要.md
T
lovife 63aec55329 docs: 产品文档与交互原型(P1-P7 交付物)
一期范围:学校收集 + 自主认定(4 类材料全部有实测样本)
- 主 PRD 190KB:125 条 FR / 21 条 VAL / 20 张表 DDL / 121 个接口
- 功能逻辑卷 88KB:不含代码模块
- 接口设计 116KB:58 个错误码 / 12 个 VO
- 核心模块设计:M1 命名 / M2 压缩 / M3 识别 / M4 门禁 / M5 校验 / M6 导出
- 开发任务拆解:100 任务 / 387 人日 / 15 周
- 验收测试清单:36 条 Golden 断言 / 111 条功能验收
- PostgreSQL DDL:已在 PG 16.15 实测零错误
- 交互原型 31 屏:学生端 11 / 班主任端 9 / 管理端 11
2026-09-28 16:19:19 +08:00

272 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 交接摘要
> 用途:**压缩后的唯一状态源**。新会话读这一份即可接续,不必回看历史对话。
> 更新:2026-09-28
---
## 一、项目一句话
职业技能等级认定**报名材料收集 SaaS**。学生在微信小程序填报、班主任在手机初审、管理员在 PC 终审与导出。核心价值:**动态表单 + 规则引擎(命名/压缩/识别)+ 门禁核验 + 一键规范导出**。
工作目录 `D:\IDEA_Project\xiaotutongxue`(**空 git 仓库,无 commit,全部改动未提交**),交付物在 `docs/`。
---
## 一·二、一期范围(2026-09-28 客户确认收窄)
> **一期只做「学校收集 + 自主认定」一条流程。** 详见 PRD「一期范围声明」。
| 维度 | 字段 | 一期 | 二期后置 |
|---|---|---|---|
| 收集模式 | `collect_mode` | `1` 学校收集 | `2` 社会收集 |
| 认定类型 | `cert_type` | `1` 自主认定 | `2` 社会评价 / `3` 专项职业能力 |
**一期材料只有 4 类,全部有实测样本**:一寸照片 / 身份证 / 学籍表 / 工作证明
**二期才做的 2 类材料零样本**:`XJW_BA` 学信网备案表 / `OTHER_PROOF` 其它佐证材料
**数据结构保留不启用**(二期直接开,不需 DB 迁移)——因为这三项本来就是**配置驱动**的
(`needClassAudit` 是开关、字段隐藏走 `visibleWhen`、材料差异是材料项配置),
所以**省下的主要是测试成本与一个外部依赖,而不是代码量**。
**工期**:392 → **387 人日**(−5);**关键路径不变,仍 15 周**。
> ⚠️ **二期启动前必做**:索取社会评价样本(D4)→ 标定 `XJW_BA`/`OTHER_PROOF` → 补社会收集审核流与专项职业能力字段隐藏。
---
## 一·五、客户回执(2026-09-28 · 需求已冻结)
> **客户全部 23 项按建议方案确认。** 唯一功能增量是 **B1:名单加入申请通道**。
| 组 | 需开发 | 说明 |
|---|:--:|---|
| A 认定机构 6 项 | **0** | 与现有设计完全一致(`min_long_side=1400`、`min_quality=70`、PNG 保留等都是当初按实测设计的) |
| B 校方业务 9 项 | **1** | **B1** 已落地:FR-509 系列 + `collect_join_request` 表 + 5 接口 + 2 屏原型 |
| C 商务法务 5 项 | 0 | C1 小程序资质推进中,H5 保底已列入 M0-13 预研 |
| D 内部技术 7 项 | 0 | **D4 已后置为二期**(一期不涉及零样本材料);**D7 已降级**(文件名识别准确率不影响任何交付项,残留只是上传页文案校准,班主任群里问两句即可)。**均已清除,无外部依赖** |
**规模变化**:FR 115 → **125** / 接口 117 → **121** / 库表 19 → **20** / VAL 20 → **21** / 错误码 50 → **57** / Golden 断言 29 → **36** / 功能验收 85 → **111** / 原型 29 → **31** 屏 / 任务 94 → **100** / 人日 380 → **392** → **387**(一期收窄 −5)/ 工期 14 → **15 周**。
**B1 的设计要点(三条边界,不要在后继开发中放水)**
| # | 边界 | 理由 |
|:--:|---|---|
| 1 | **只解决"进名单",不绕过门禁** | 申请期间仍不能填报;审批通过后才自动绑定放行 |
| 2 | **证件号与手机号仅存 HMAC + 脱敏值** | 与门禁口径一致 |
| 3 | **驳回必须逐条填原因;批量通过不提供"全选"** | 身份认定环节误操作代价极高 |
---
## 二、已锁定的铁律(不要重开讨论)
| # | 决策 | 关键理由 |
|---|---|---|
| 1 | **扫描件强制压到 ≤1M,无例外** | 认定机构硬要求 |
| 2 | 默认参数 **300dpi / q85** | 实测 28/28 达标,平均 806KB,清晰度零损失 |
| 3 | 材料来源只有 **聊天记录 / 相册 / 手机文件**,「直接拍照」默认关闭 | 实测 4 类材料无一来自现场拍照 |
| 4 | **必须先导名单**,姓名+身份证匹配不通过只提示、不放行(学校与社会收集皆然) | 门禁是产品根基 |
| 5 | 报考范围**从名单聚合**,不存在独立"计划清单"文件 | 真实名单验证 |
| 6 | 动态表单 = **内置字段(固定列,可统计)+ 扩展字段(extras_json,仅展示导出)** | 兼顾统计能力与灵活性 |
| 7 | 数据库 **PostgreSQL**(纯 PG);软删除字段名为 **`deleted`** | 客户已确认 |
| 8 | 班主任端**只做右滑通过**,驳回必须点按钮 | 误触后果不对称 |
| 9 | 批量通过**只带无问题项**,红黄一律排除 | 防"手快全选" |
| 10 | 有不合格文件时**直接禁用导出按钮**,不允许带问题打包 | 铁律 |
| 11 | 识别策略 **规则为主 + VLM 兜底**(触发率 2%~10%) | 成本可控 |
| 12 | 识别失败**一律放行**,不得阻断提交 | 可用性优先 |
| 13 | 原型**不用 emoji**(`→` `↔` `·` 等排版字符保留) | 设计约束 |
---
## 三、已完成交付物
| 文件 | 版本/大小 | 状态 |
|---|---|---|
| `docs/数据信息填报系统PRD.md` | V2.3 / 173KB / 2816 行 | ✅ 含 115 条 FR、17 条 VAL、96 接口、19 表 DDL、实测附录 |
| `docs/数据信息填报系统PRD-功能逻辑卷.md` | V1.9 / 88KB | ✅ 纯功能逻辑,**不含库表/接口/代码** |
| `docs/db/schema-postgresql.sql` | 34KB | ✅ **PG 16.15 实测零错误**(19 表 / 167 列注释 / 61 索引) |
| `docs/prototype/student-app.html` | **11 屏** / 57KB | ✅ 含 STEP 03B「申请加入名单」 |
| `docs/prototype/teacher-app.html` | 9 屏 / 63KB | ✅ |
| `docs/prototype/admin-web.html` | **11 屏** / 114KB | ✅ 含表单设计器、材料规则配置、历史整理器、**申请审批** |
| `docs/prototype/index.html` | 7KB | ✅ 三端入口 |
| `docs/00-交付计划.md` | — | ✅ 7 项计划与进度记录 |
| `docs/01-客户与机构确认函.md` | 18KB | ✅ **P1** 23 项**已全部确认**(客户按建议执行,需求冻结) |
| `docs/02-需求追溯矩阵.md` | 10KB | ✅ **P2** 审计报告,含 10 个接口缺口 |
| `docs/03-接口设计.md` | **102KB / 2626 行** | ✅ **P3 117 个接口**字段级定义 + 枚举全集 + 12 个 VO + 50 个错误码;**自校验通过** |
| `docs/06-验收测试清单.md` | **29KB** | ✅ **P6 36 条 Golden 断言** + **111 条功能验收** + **27 条异常边界** |
| `../README.md` | **10KB** | ✅ **P7 文档导航**,按角色读法 + 快速上手 |
| `docs/04-核心模块设计说明.md` | **56KB / 1218 行** | ✅ **P4 六个模块**伪代码级说明 + 依赖图 + Golden 基线 |
| `docs/05-开发任务拆解.md` | **28KB / 468 行** | ✅ **P5 94 个任务 / 380 人日 / 14 周**,含关键路径与核算口径 |
---
## 四、待办计划
| # | 任务 | 产出 | 状态 |
|---|---|---|---|
| ~~P1~~ | 客户与认定机构确认函 | `01-客户与机构确认函.md` | ✅ |
| ~~P2~~ | 需求追溯矩阵 | `02-需求追溯矩阵.md` | ✅ |
| ~~P2.5~~ | 管理端补 3 屏原型 | 并入 `admin-web.html` | ✅ |
| **P3** | **接口设计(OpenAPI)** | `03-接口设计.md` | ✅ **已完成**(117 个接口) |
| P4 | 核心模块设计说明(伪代码级) | `04-核心模块设计说明.md` | ✅ **已完成**(6 个模块) |
| P5 | 开发任务拆解 WBS(M0–M7) | `05-开发任务拆解.md` | ✅ **已完成**(94 任务 / 380 人日 / 14 周) |
| P6 | 验收测试清单(Golden Test Set) | `06-验收测试清单.md` | ✅ **已完成**(29 条 Golden 断言 + 85 条功能验收) |
| P7 | 文档导航 README | `../README.md` | ✅ **已完成** |
### P3 已补齐的缺口(21 个接口,原审计发现 10 项)
```
✅ I1 报考范围读取/确认 GET/PUT /api/v1/tasks/{id}/plan-scope
✅ I2 历史数据整理器 POST /api/v1/cleanup/jobs(共 5 个)
✅ I3 字典管理 /api/v1/dicts(4 个)
✅ I4 操作日志 GET /api/v1/logs/operations
✅ I5 登录日志 GET /api/v1/logs/logins
✅ I6 消息中心 /mp/messages、/mp/messages/read、/mp/teacher/messages
✅ I7 订阅额度登记 POST /api/v1/mp/subscribe
✅ I8 审核撤回 POST /api/v1/audit/submissions/{id}/revoke
✅ I9 任务暂停/恢复 POST /api/v1/tasks/{id}/pause、/resume
✅ I10 材料识别结果读取 已并入 GET /api/v1/audit/submissions/{id} 响应
➕ 文件规范化 process 原仅在 PRD §8.9 详述、未登记进清单,已补登
```
**接口总数:96(PRD 原有)+ 21 = 117**
---
## 五、关键事实速查
### 真实基准数据集(Golden Test Set)
`D:\项目\202609机电填报系统\24中技数控1班\` —— **28 人 × 4 类材料 = 112 文件**
| 实测项 | 结果 |
|---|---|
| 原始总量 | 475 MB |
| 命名违规 | 59 / 112(52.7%) |
| 双扩展名 | 56 |
| 体积超标(>1M) | 31 |
| 证件号异常 | 5 例(3 可自动、2 待人工) |
| 疑似串档 | 1(**唯一必须人工判断的**) |
| 压缩后 | 300dpi/q85 → 平均 806KB、最大 831KB、**0 张超标** |
| 材料识别 | 文件名层 112/112;形态层 99.1%;OCR 内容层 32/32 |
### 官方模板要点
- 21 列(A–U):**红=必填 11 项、绿=级联 6 项、黑=选填 4 项**
- **「报考职业」「报考工种」在 Excel 里没有下拉,是自由填写** ← 我方产品的核心增量
- 「职业技能等级个人申报表」的规范文件名却是 **「工作证明」**(机构既有习惯)
- **表头行与工作表名都不固定**(实测一份在第 1 行、一份在第 6 行)
### 三个易错实现陷阱
1. **「无」是合法业务值,不是空值**
2. **专项职业能力:无工种 / 无等级 / 无补考科目(0 科)**
3. 补考约束:三/四/五级最多 1 科,一/二级最多 2 科,专项 0 科
### 压缩五档(PRD §6.3)
A4 扫描件 300dpi(2480×3508)/q85 · 一寸照片固定 295×413 · 学籍表为 16:9 截图**保持 PNG 不缩放**
### 教师端数字口径(原型必须自洽)
28 人 = 已通过 15 + 待审 4 + 待核对 3 + 有问题 1 + 未提交 5;**完成率 82% 是已提交率(23/28)**
---
## 六、环境与工具
| 项 | 内容 |
|---|---|
| PG 验证容器 | `docker exec -it pg-verify psql -U postgres -d filldb`;清理 `docker rm -f pg-verify` |
| 原型校验脚本 | `python C:\Users\93292\AppData\Local\Temp\opencode\check2.py <html路径>` |
| DDL 同步方式 | 从主 PRD 的 ` ```sql ` 代码块自动抽取生成,改文档后重跑抽取脚本 |
| 原型约束 | 单文件自包含、零外部依赖、内联 SVG(`<symbol>`+`<use>`,1.7px 描边 currentColor)、无 emoji |
| 原型校验基线 | 四份全通过:标签配对 ✓ / CSS 变量 ✓ / SVG 无冗余 ✓ / emoji 0 ✓ |
---
## 七、重要教训
### 教训一:脚本审计前必须先验证脚本本身
P2 审计时脚本因未匹配加粗编号 ``| **FR-614** |``,误报 3 个"需求条目问题"(实为假警报);若直接采信并"修复",会引入 3 处新错误。同时 FR 总数由误算的 102 修正为 **115**。
### 教训二:写新文档必须回查已有编号体系,不能凭记忆
P4 写 M5 校验引擎时,我凭记忆用了一套 VAL 编号,**与 PRD 权威清单整体错位**:
| P4 原写法 | 我以为的含义 | PRD 实际定义 |
|---|---|---|
| VAL-01 | 姓名与名册一致 | **证件号格式与校验位** |
| VAL-03 | 证件号格式 | **姓名与名册一致** |
| VAL-06 | 双扩展名 | **文件格式白名单** |
| VAL-07 | 多扩展名冲突 | **文件体积上限** |
**后果**:若不修正,开发按 P4 实现会把规则完全接错,且两份文档互相矛盾。
**修正**:已全部重映射到 PRD 体系(单次扫描替换避免 01↔03 碰撞),并给 PRD 补了 P4 需要的三条新规则 **VAL-18 文件名规范化 / VAL-19 疑似串档 / VAL-20 体积下限保护**。
**规则**:新增文档引用任何已有编号(FR/VAL/错误码/接口)前,**先脚本抽取权威清单核对**,不凭记忆。
### 教训三:系统里的数据不会错,错的一定是「归属」
客户指出:**"系统应该不存在名称错误的情况。是先录入了有这个姓名和身份证,然后才能填写的。"**
这指出一个真实的设计缺陷:
| 位置 | 会不会错 | 说明 |
|---|---|---|
| **填报单里的姓名/证件号** | **永远不会错** | 门禁已核验 + 字段锁定。客户说得对 |
| 文件名里的姓名/证件号 | 可能不同 | **但这不是"名称错误",而是"这份文件可能不属于这个人"** |
**原设计的错误**:把"文件名姓证件号与名册不符"处理为「自动按名册修正」(信息级),
这会把"A 传了 B 的材料"**静默洗白**成 A 的材料——审核时完全看不出来,事后无从追溯。
```
学生 A 在微信里误选了同学 B 的材料
↓ 文件名:李四-3610...(B 的号码,校验位通过)
错误做法:判"与名册不符" → 改成 张三-3610... → 信息级,A 看不到
↓ 审核:班主任看到一份名字正确的材料 → 通过
↓ 最终:B 的工作证明被认定成 A 的 ← 灾难
```
**修正(客户已确认)**:
1. **判定优先级重排**为 `证件号反查名册 > OCR 内容 > 姓名(仅辅助)`
2. 新增 **VAL-21 文件归属校验**:归属存疑一律**阻断提交**,且**不提供"仍然继续"**
3. **姓名不再作为独立规则**——只在证件号佐证时按名册取值,仅留痕不打扰学生
4. 核心判据:**文件名证件号能否在名册里反查到"另一个人"**(一次查询,走唯一索引,< 1ms)
**精髓在于两条的区分**:
- **校验位失败** = 不是一个真实号码 → 只是写错了 → **可以自动改**
- **校验位通过** = 一个真实存在的自然人 → 改了就是改了身份 → **必须阻断**
### 教训五:联系方式不进系统
客户指出:**"学生本来就有班主任联系方式,不需要系统里面体现。"**
原设计在门禁失败页放了一个「联系老师」按钮(脱敏手机号 + 一键拨打),并为此专门开了一个接口 `GET /mp/roster/contact`。
已移除:
| 移除项 | 原因 |
|---|---|
| `GET /mp/roster/contact` 接口 | 不必要(用户已知道怎么找人)+ 有维护成本(换人/换号要同步)+ 多一处隐私暴露面 |
| 门禁失败页「联系老师」按钮 | 同上。改为只保留两条**系统内动作**:重新填写 / 申请加入名单 |
| 功能逻辑卷中的联系方式展示描述 | 同步 |
**保留**:纯文案提示(如"如有疑问请联系老师")——**不涉及任何联系方式数据**。
**接口总数**:122 → **121**。
### 教训四:「社会评价」与「社会收集」只差一个字
`cert_type`(认定类型:自主认定 / **社会评价** / 专项职业能力)决定**填什么材料**;
`collect_mode`(收集模式:学校收集 / **社会收集**)决定**谁审核**。二者可任意组合,已在 PRD §5.3 与接口 §2.3 分别写明。
另外补全了 `cert_type` 的第三个取值(专项职业能力)在 DDL 注释与附录中的遗漏。
### 附带修正
- **体积口径**:原型导出中心曾写"475MB → 22.4MB(95%)",与 PRD 及历史整理器屏(37MB/92%)矛盾。实为混淆——**444→22MB 是"工作证明"单项,475→37MB 是全班 112 文件合计**。已统一为 37MB / 92%。
---
## 八、外部依赖(未回)
`01-客户与机构确认函.md` 已发出待回,其中**最高优先级 3 项**:
- **A1** 认定机构是否书面接受 300dpi 扫描件
- **B1** 名单外的人怎么处理(建议加"申请加入名单→管理员审批"通道)
- **B3** 是否允许学生修改老师预填字段(建议:姓名/证件号/报考三项锁定,其余可改留痕)
未回复则按文档内建议方案推进。