Files
xiaotutongxue/README.md
T
lovife 2ff17cd0c9 refactor(h5)!: H5 改由 nginx 托管,后端退回纯 API
用户决定:后端不再对外发静态文件,学生端 H5 交给网关。两者**仍然同源**
(微信只给一个授权回调域名;H5 的接口地址取自 location.origin),
所以前端业务代码、微信后台、application-dev.yml 的 h5-base-url 都不用改。

后端
- 删掉 dev/test/prod 三份 profile 里的 spring.web.resources.static-locations
- 主配置加 spring.web.resources.add-mappings=false —— 用开关而不是"指到空目录":
  后者会连带启用 Spring Boot 的默认位置(classpath:/static 等),谁丢个文件进去就悄悄对外可见
- StaticCacheHeaderFilter → ApiNoStoreFilter,只剩 /api/** 的 no-store
  (静态资源的缓存策略整体归 nginx,避免"改一处漏一处")
- 补 NoHandlerFoundException → 404:关掉静态映射后未匹配路径不再经过
  ResourceHttpRequestHandler,抛的是 NoHandlerFoundException,只处理
  NoResourceFoundException 时访问 / 会变成 500「系统繁忙」并刷 ERROR 日志

deploy
- 新增 xt-nginx(80):发 frontend-mp/dist/build/h5 + /api 反代 8080(host.docker.internal,
  Linux 靠 extra_hosts host-gateway),端口与产物目录走 .env 的 H5_PORT/H5_DIR
- 三条"删了就会出事"的都已带上理由:client_max_body_size 50m(默认 1m,超了回 413
  而后端日志一个字都没有)、/assets 的 immutable 与 *.html 的 no-cache(§一·十 那个
  "发版后学生看到旧页面"的坑)、健康检查写 127.0.0.1(容器里 localhost 先解析到 IPv6,
  nginx 只听 IPv4 → 永远 starting 而首页是好的)

CI 与文档
- CI:学生端 H5 构建从"e2e 的输入"改为编译检查,不再留 artifact(e2e 不消费它)
- 同步 AGENTS.md / README.md / docs/09(启动步骤、CI 表、发布注意、排障表)/ docs/11
- 交接摘要新增 §一·四十三(含理由、验证数字、要人工做的一步与环境限制)

验证
- mvn clean package 通过(必须 clean:删掉的旧类会留在 target/classes 被组件扫描)
- 后端直连:/ 与 /assets/* → 404({"code":40001,"msg":"接口不存在"})、
  /api/.../captcha → 200 + no-store、/doc.html → 302→200、/v3/api-docs 与 /actuator/health → 200
- 经 nginx(80):首页 200 + no-cache、/assets/*.js 200 + immutable、/nope.js 404(无回退)、
  /api 200 + no-store、gzip 生效;2MB multipart 上传 → 401(与直连一致 ⇒ 未踩 1m 的坑)、
  55MB → 413
- e2e(XT_BASE=http://localhost,全程走 nginx):e2e-m0.py 37/37、e2e-m3-h5-auth.py 24/24、
  e2e-m2-scope.py 38/38
- verify-all.py --quick 6/6;check-test-profile.py:test profile 仍能起来
2026-10-19 17:35:33 +08:00

282 lines
14 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.
# 深学在线
> 职业技能等级认定**报名材料收集 SaaS**
> **范围(2026-10 收敛)**:只做「学校收集 + 自主认定」(4 类材料,全部有实测样本)。社会收集 / 社会评价 / 专项职业能力 **不做**(传 `collectMode=2`、`certType=2,3` 返回 `40044`);**不设二期**
> 学生在微信小程序填报 · 班主任在手机初审 · 管理员在 PC 终审与导出
> 最后更新:2026-09-28 | **状态:需求已冻结(客户全部确认,可开工)**
---
## 一、这是什么
客户(职业院校 / 认定机构)每年要收集几百名学生的认定报名材料(一寸照片、身份证、学籍表、工作证明)。现状是靠班主任在微信群里收、手工改名、手工压缩、手工核对——**一个班 28 人就有 112 个文件、59 个命名违规、31 个超体积**,全靠人眼。
本系统把这件事做成流程:**门禁核验 → 按规则自动命名与压缩 → 材料识别 → 校验 → 分级审核 → 一键导出规范包**。
**核心增量**:官方 Excel 模板里「报考职业」「报考工种」是自由填写、没有下拉——我们把它变成**从名单聚合 + 一致性体检**。
---
## 二、文档地图
### 按角色读
| 你是 | 从这几份开始 | 顺序 |
|---|---|---|
| **客户 / 业务方** | `01-客户与机构确认函.md` → `prototype/index.html` | 先看确认函(有 23 项待拍板),再点开原型 |
| **项目经理** | `00-交付计划.md` → `05-开发任务拆解.md` → `交接摘要.md` | 先看进度,再看 94 个任务与 14 周排期 |
| **产品 / 需求** | `数据信息填报系统PRD.md` → `数据信息填报系统PRD-功能逻辑卷.md` | 前者含库表接口,后者是纯功能逻辑 |
| **后端开发** | `03-接口设计.md` → `04-核心模块设计说明.md` → `db/schema-postgresql.sql` | 接口收发 → 引擎怎么做 → 建表 |
| **前端开发** | `prototype/` → `03-接口设计.md` | 原型是视觉与交互的**唯一基准** |
| **测试** | `11-填报流程端到端测试计划.md`(填报链路 E2E)→ `06-验收测试清单.md` → `04-核心模块设计说明.md` 附录 B | 链路覆盖矩阵 + 判据口径;36 条 Golden 断言 + 111 条功能验收 |
| **新加入的人** | `交接摘要.md` → 本文件 | 一份文档看清全部状态 |
### 全部文档
| # | 文档 | 规模 | 内容 | 状态 |
|:--:|---|:--:|---|:--:|
| 00 | `00-交付计划.md` | 8 KB | 7 项计划与进度追踪、风险跟踪 | ✅ |
| 01 | `01-客户与机构确认函.md` | 18 KB | **23 项已全部确认** + 客户回执 + 落地动作清单 | ✅ **需求冻结** |
| 02 | `02-需求追溯矩阵.md` | 10 KB | 115 条 FR ↔ 原型/库表/接口 的自审报告 | ✅ |
| 03 | `03-接口设计.md` | — | **119 个接口**字段级定义、枚举全集、12 个 VO、**54 个错误码** | ✅ |
| 04 | `04-核心模块设计说明.md` | 56 KB | **6 个模块**伪代码级说明 + 依赖图 + 26 项回归基线 | ✅ |
| 05 | `05-开发任务拆解.md` | 31 KB | **100 个任务 / 387 人日 / 15 周**,含关键路径、一期范围与风险 | ✅ |
| 06 | `06-验收测试清单.md` | 29 KB | **36 条 Golden 断言** + 111 条功能验收 + 签署表 | ✅ |
| 11 | `11-填报流程端到端测试计划.md` | 45 KB | **填报链路覆盖矩阵(A~J 十组)+ 判据速查 + 已定案缺陷 F-125~F-129**;配套 `scripts/e2e-m3-flow.py` | ✅ |
| — | `交接摘要.md` | 11 KB | **状态源**:客户回执、13 条铁律、已完成、待办、环境、教训 | ✅ |
| — | `数据信息填报系统PRD.md` | 190 KB | 主 PRD V2.3+:**125 条 FR**、**21 条 VAL**、**20 张表 DDL**、验收标准 | ✅ |
| — | `数据信息填报系统PRD-功能逻辑卷.md` | 88 KB | 纯功能运转逻辑(**不含库表/接口/代码**) | ✅ |
| — | `db/schema-postgresql.sql` | 36 KB | 可执行 PG DDL(20 表),**已在 PostgreSQL 16 实测零错误** | ✅ |
| — | `prototype/`(4 份) | — | **29 屏**交互原型:学生端 10 / 班主任端 9 / 管理端 10 | ✅ |
---
## 三、三分钟看懂产品
### 一条主线
```
┌─ 学生端(小程序)─┐ ┌─ 班主任端(手机)─┐ ┌─ 管理端(PC)─┐
名单 ──→ 门禁核验 ──→ 填报 ──→ 卡片流初审 ──→ 终审 ──→ 导出规范包
└─✗ 失败 → 只提示核对 + "请联系老师加你进名单"(老师手工加人)
▲ ▲ ▲ ▲
│ │ │ │
报考范围 识别 命名+压缩 汇总 Excel
从名单聚合 校验 (≤1M 无例外) + 目录树
```
### 两个必须知道的数字
| 数字 | 含义 |
|---|---|
| **59 / 112** | 真实批次里的命名违规率(52.7%)。系统自动修 |
| **475 MB → 37 MB** | 原始总量 → 导出包,压缩率 92%。**300dpi 清晰度零损失** |
### 三条铁律(不要试图绕过)
| # | 铁律 | 理由 |
|:--:|---|---|
| 1 | **必须先导名单**,姓名+身份证匹配不通过不放行 | 门禁是产品根基 |
| 2 | **有不合格文件时直接禁用导出按钮** | 带问题打包出去,返工成本远高于当场修 |
| 3 | **识别失败一律放行** | 不阻断提交是完成率的底线 |
| 4 | **归属存疑一律阻断,且无"仍然继续"** | 文件名的姓/证号指向别人 = 传错材料。**自动改名会把错传洗白**,审核看不出来、事后无从追溯 |
---
## 四、关键决策速查
> 完整 13 条见 `交接摘要.md` 第二节。这里只列最容易被反复讨论的。
| 决策 | 内容 |
|---|---|
| 压缩 | **强制 ≤1M,无例外**;默认 300dpi/q85(实测 28/28 达标,均值 806KB) |
| 材料来源 | 只有**聊天记录 / 相册 / 手机文件**,「直接拍照」默认关闭 |
| 报考范围 | **从名单聚合**,不存在独立的"计划清单"文件 |
| 数据库 | **PostgreSQL**(纯 PG);软删除字段名 `deleted` |
| 滑动交互 | 班主任端**只做右滑通过**,驳回必须点按钮 |
| 批量通过 | **只带无问题项**,红黄一律排除 |
| 动态表单 | 内置字段(固定列可统计)+ 扩展字段(`extras_json`) |
| 名单外的人 | **不做自助通道**(2026-10 撤销):由老师在任务里**手工加人**(名册追加导入),**不绕过门禁** |
| 归属判定优先级 | **证件号反查名册 > OCR 内容 > 姓名(仅辅助,不做独立规则)** |
| 联系方式 | **系统内不展示任何班主任/管理员联系方式**——学生本来就有;门禁失败只给「重新填写」一条系统内出路 + 一句"请联系老师" |
| 校验位的作用 | **失败 = 不是真实号码 → 可自动改**;**通过 = 真人 → 改了就是改身份 → 必须阻断** |
| 审批粒度 | 批量通过**不提供"全选"**,驳回**必须逐条填原因**(身份认定误操作代价极高) |
---
## 五、目录结构
```
xiaotutongxue/
├── README.md ← 本文件
└── docs/
├── 00-交付计划.md
├── 01-客户与机构确认函.md
├── 02-需求追溯矩阵.md
├── 03-接口设计.md
├── 04-核心模块设计说明.md
├── 05-开发任务拆解.md
├── 06-验收测试清单.md
├── 交接摘要.md
├── 数据信息填报系统PRD.md
├── 数据信息填报系统PRD-功能逻辑卷.md
├── db/
│ └── schema-postgresql.sql
└── prototype/
├── index.html ← 三端入口,从这里打开
├── student-app.html
├── teacher-app.html
└── admin-web.html
```
---
## 六、快速上手
### 看原型
直接双击 `docs/prototype/index.html`。**单文件自包含、零外部依赖**,不需要服务器。
管理端原型支持 100% / 82% / 66% 缩放查看。
### 验证数据库
```bash
# 起一个 PG 容器
docker run -d --name pg-verify -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=filldb -p 5432:5432 postgres:16
# 执行 DDL
docker exec -i pg-verify psql -U postgres -d filldb < docs/db/schema-postgresql.sql
# 验证结果:20 张表 / 177 列注释 / 65 索引,零错误
docker exec -it pg-verify psql -U postgres -d filldb
```
> 容器仍在本机运行时可 `docker exec -it pg-verify psql -U postgres -d filldb` 直接进入;
> 不需要了用 `docker rm -f pg-verify` 清理。
### 跑原型结构校验
```bash
python check2.py docs/prototype/admin-web.html
# 输出:标签配对 / CSS 变量 / SVG 图标 / emoji / 外部依赖 五项检查
```
---
---
## 六·二、把系统跑起来(三条命令)
```bash
# 1) 基础设施:PG 16 + Redis 7 + nginx(没有对象存储,MinIO 已删)
# nginx(:80)发学生端 H5 并把 /api 反代到后端 8080 —— 起它之前先
# `cd frontend-mp && pnpm build:h5`,否则它挂的是个空目录(页面 404)
cd deploy
copy .env.example .env # 首次
docker compose up -d
# 2) 后端:Flyway 自动建库 + 自动初始化账号
cd ../backend
mvn spring-boot:run
# 3) 验收自测(35 项)
cd ..
python scripts/e2e-m0.py
```
开发环境初始账号(仅本地,由 `fill.seed.enabled` 控制):
| 账号 | 密码 | 角色 |
|---|---|---|
| `admin` | `Admin@123` | 学校管理员(租户 `demo`) |
| `platform` | `Platform@123` | 平台运营 |
接口文档:<http://localhost:8080/doc.html>
### 代码结构
```
backend/ Java 21 + Spring Boot 4.1.1
├── xt-common/ 统一响应 / 54 个错误码 / AES-GCM / HMAC / RSA
└── xt-server/ 配置 / 鉴权 / 多租户拦截 / 业务模块(26 张表,Flyway 已到 V19)
deploy/ docker-compose(PG/Redis/nginx)+ nginx/xt.conf(H5 站点)+ RSA 开发公钥
scripts/ E2E 验收(38 个脚本)、接线检查、DDL 同步、图标抽取
frontend-pc/ PC 管理端:**纯 Vue 3 + Naive UI**(pnpm dev → 5173)
frontend-mp/ 学生端 / 教师端:uni-app(H5 与微信小程序同一套代码)
```
### ⚠️ 搭环境前请先读
**`docs/07-开发实施计划.md` §9「版本兼容踩坑记录」**——
那里记了 10 条 Boot 4 / Flyway 10 / MyBatis-Plus 3.5.9 的真实坑,
其中多数是「**依赖在、功能不在,且不报错**」的类型,靠读文档和凭经验都躲不掉。
## 七、交付状态
| 阶段 | 内容 | 状态 |
|---|---|---|
| **需求** | 主 PRD(**125 FR** / 20 VAL / **20 张表**)+ 功能逻辑卷 | ✅ |
| **设计** | 数据库 DDL(PG 16 实测)+ 接口设计(**119**)+ 模块设计(6) | ✅ |
| **原型** | **29 屏**(学生 **10** / 班主任 9 / 管理 **10**)= 29 | ✅ |
| **计划** | **100 任务 / 387 人日 / 15 周**(一期范围已收窄) | ✅ |
| **测试** | **36** 条 Golden 断言 + **111** 条功能验收 | ✅ |
| **待外部** | ~~客户确认函(23 项)~~ | ✅ **已确认,按建议执行** |
### 客户已确认(2026-09-28 · 可开工)
| 项 | 确认结论 |
|---|---|
| **A1** | ✅ 认定机构接受 300dpi 输出 |
| **B1** | ⚠️ **已回退**(2026-10 需求收敛):加入申请通道整体撤销(模块/表/接口/页面/错误码一并删除)。它是确认函里唯一的功能增量,建议补变更确认单 |
| **B3** | ✅ 字段三分类:姓名/证件号/报考三项锁定,其余可核对修改并留痕 |
**其余 20 项均与现有设计一致,零改动。** 需求已冻结,可按 `05-开发任务拆解.md` 的 **M0** 开工。
---
## 八、数据与测试基线
| 项 | 值 |
|---|---|
| **Golden Test Set** | `D:\项目\202609机电填报系统\24中技数控1班\` |
| 规模 | 28 人 × 4 类材料 = **112 个文件**,原始 **475 MB** |
| 命名违规 | **59**(52.7%) |
| 双扩展名 | **56** |
| 体积超标 | **31** |
| 证件号异常 | **5**(自动修 3 + 待人工 2) |
| 疑似串档 | **1**(唯一必须人工判断的) |
| 压缩后 | 均值 **806 KB**,最大 **831 KB**,**0 张超标** |
| 导出包 | **≈37 MB**,压缩率 **92%** |
> ⚠️ **这个数据集不可替代**。所有"实测"结论都来自它;换成构造数据,双扩展名 56/112 这种数字就复现不出来。
> 必须纳入版本管理或保留只读副本。
---
## 九、开发环境
| 项 | 说明 |
|---|---|
| 数据库 | PostgreSQL 16(DDL 已实测通过) |
| 开发顺序 | **M1 命名引擎 → M4 门禁 → M2 压缩 → M5 校验 → M3 识别 → M6 导出** |
| 客户回执 | 全部 23 项按建议执行;唯一功能增量 B1(申请加入名单)**已于 2026-10 需求回退取消** |
| 原型约束 | 单文件自包含、零外部依赖、内联 SVG 图标、**无 emoji** |
| 最长的两个单点 | 压缩引擎 12 人日、打包引擎 10 人日(**加人无效**) |
| 回归门禁 | 改动后必须复现 **15 项基线指标**(对应 29 条断言),偏差即回归 |
---
## 十、两条踩过的坑
| # | 教训 |
|:--:|---|
| 1 | **脚本审计前先验证脚本本身**。追溯矩阵审计时脚本漏匹配加粗编号,误报 3 个"需求问题",若直接采信会引入 3 处错误 |
| 2 | **引用已有编号前先脚本核对,不凭记忆**。模块设计里把 VAL 编号整体写错位(VAL-01 是姓名 vs 证件号格式),若不查会与 PRD 互相矛盾 |
> 详见 `交接摘要.md` 第七节。
---
**文档中心维护约定**:任何改动后,请更新 `00-交付计划.md` 的执行记录与 `交接摘要.md` 的状态;改动接口/库表/FR 编号时,**必须重跑跨文档一致性核对**。