Files
lovife 50ad577b29 fix(cors): 修复生产登录 403「Invalid CORS request」
- 根因:浏览器对**同源**的 POST/PUT/DELETE 也会带 Origin,而 Spring MVC 只看
  "请求里有没有 Origin"(Spring 7.0.9 的 DefaultCorsProcessor 没有同源短路),
  带了又不在白名单里 → 进 Controller 之前直接 403
- WebConfig 的白名单写死为 localhost/内网;注释声称"生产配置见 application-prod.yml
  的 fill.cors.allowed-origins",但该属性根本不存在、也没人读它 ——
  于是"前后端同源部署"的登录/上传/提交必然登不上(生产 2026-10-20 事故)
- 改为配置驱动:FillProperties.Cors + fill.cors.allowed-origins
  (可用 XT_CORS_ALLOWED_ORIGINS 覆盖);prod 默认
  shenxue.jxit.com / admin.shenxue.jxit.com / *.shenxue.jxit.com;
  dev 补上花生壳穿透域名(H5 走穿透时 POST 同样会被拦)
- 启动日志打印生效白名单,排障先看那一行
- 新增 CorsWhitelistTest(3 条):生产 yml 真能绑定、通配符匹配租户子域、陌生源仍被拒
- docs/09 §2.4:本站地址必须在白名单 + 反代必须传
  Host/X-Real-IP/X-Forwarded-For/X-Forwarded-Proto(缺了登录日志 IP 全是 127.0.0.1);
  交接摘要 §一·四十四 记录事故,并写清"1700+ 条 e2e 全绿为什么还是漏了"

为什么 e2e 没抓到:脚本是 Python requests,不带 Origin 头,永远走不到 CORS 分支;
本地浏览器用的 localhost 正好命中写死的 dev 白名单。唯一能暴露它的是"浏览器 + 正式域名"。

验证:生产白名单下 admin / 主域名 / t1 / t12 → 200,evil / 后缀拼接 / http → 403;
真实 RSA 登录 + Origin: https://admin.shenxue.jxit.com → 200、code=0、拿到 token;
mvn test 15/15;verify-all --quick 6/6;check-test-profile 通过。
接口、库表、契约均无变化。
2026-10-20 09:22:16 +08:00

130 lines
15 KiB
Markdown
Raw Permalink 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
> 状态:**进行中**
---
## 一、当前已交付
| # | 交付物 | 版本 | 状态 |
|---|---|---|---|
| — | 主 PRD(含库表设计、接口清单、15 条校验规则、18 张表 DDL) | V2.3 | ✅ |
| — | 功能逻辑卷(纯功能运转逻辑,不含库表/接口/代码) | V1.9 | ✅ |
| — | PostgreSQL 建表脚本 | — | ✅ **已在 PG 16 实测通过** |
| — | 学生端原型(**10 屏**) | V1.3 | ✅(原 STEP 03B「申请加入名单」已于 2026-10 需求收敛删除) |
| — | 班主任端原型(9 屏) | V1.1 | ✅ |
| — | 管理端 PC 原型(**10 屏**) | V1.3 | ✅ 含表单设计器/材料规则/角色与授权(整理器与申请审批两屏已于 2026-10 删除) |
---
## 二、待执行计划
> 排序原则:**先解阻塞,再补设计,最后备交付**。
> 客户确认链是最长的外部依赖,必须最先发出去。
### 阶段一 · 打通确认链
| # | 任务 | 产出 | 为什么这个顺序 | 状态 |
|---|---|---|---|---|
| **P1** | **客户与认定机构确认函** | `01-客户与机构确认函.md` | 外部依赖最长。**5 项高优先级阻塞开发**(300dpi 能否接受、名单外的人怎么办、社会收集名单从哪来…),越早发出越早回来 | ✅ |
| **P2** | **需求追溯矩阵** | `02-需求追溯矩阵.md` | 自审:把 PRD 需求 ↔ 原型屏 ↔ 库表 ↔ 接口 逐条对应,找出**说了没做/做了没说**的缺口。放在接口文档之前,避免接口漏做 | ✅ |
| ~~**P2.5**~~ | ~~**管理端补充原型**(矩阵审计追加)~~ | 追加到 `admin-web.html` | 矩阵发现 3 个 P0/P1 复杂交互页面未出原型:**表单设计器**、**材料规则配置**、~~**历史整理器**~~(该屏已于 2026-10 随模块删除)。不画出来开发必理解偏 | ✅ |
### 阶段二 · 补齐技术设计
| # | 任务 | 产出 | 说明 | 状态 |
|---|---|---|---|---|
| **P3** | **接口设计(OpenAPI)** | `03-接口设计.md` | PRD §8 已有 96 个接口清单,但缺**完整请求/响应字段定义**。补齐后前端可 mock、后端可并行开工。**含补追溯矩阵发现的 21 个缺口** | ✅ |
| **P4** | **核心模块设计说明** | `04-核心模块设计说明.md` | 6 个模块的**伪代码级**说明:M1 命名 / M2 压缩 / M3 识别 / M4 门禁 / M5 校验 / M6 导出;含依赖图、开发顺序、Golden 基线 | ✅ |
### 阶段三 · 交付准备
| # | 任务 | 产出 | 说明 | 状态 |
|---|---|---|---|---|
| **P5** | **开发任务拆解(WBS)** | `05-开发任务拆解.md` | 8 个里程碑拆到可派工粒度(94 个任务 / 380 人日 / 工期 14 周),含关键路径、8 项风险、团队配置、3 个客户演示点 | ✅ |
| **P6** | **验收测试清单** | `06-验收测试清单.md` | 29 条 Golden 断言(可直接自动化)+ 85 条功能验收 + 20 条异常边界 + 9 项性能 + 12 项安全 + 签署表 | ✅ |
| **P7** | **文档导航(README)** | `../README.md` | 统一入口:按角色读法、三分钟看懂产品、关键决策速查、快速上手 | ✅ |
---
## 三、执行记录
| 时间 | 任务 | 结果 |
|---|---|---|
| 2026-09-28 | 立计划 | 7 项,分三阶段 |
| 2026-09-28 | **P1 确认函** | 完成 `01-客户与机构确认函.md`:23 项待确认按**拍板方**分 4 组(认定机构 6 / 客户业务 9 / 商务法务 5 / 内部技术 7);**每项给出建议默认值**,客户不回复也能推进;附 9 条已闭环决策表防止反复讨论 |
| 2026-09-28 | **P2 追溯矩阵** | 完成 `02-需求追溯矩阵.md`。**审计发现**:① 修正统计口径——实际 **115 条 FR**(83 P0/29 P1/3 P2)+ 17 条 VAL,此前漏算加粗编号;② **10 个接口缺口**(报考范围、整理器、字典、日志、消息中心、订阅额度、审核撤回、任务暂停…);③ **管理端 10 个原型页面缺口**,其中表单设计器为 P0;④ 4 个疑似需求问题中 **3 个是脚本正则缺陷、1 个为真**(已补 FR-616);⑤ **追加 P2.5** 补 3 个复杂交互页面原型 |
| 2026-09-28 | **P2.5 补充原型** | 管理端 `admin-web.html` 由 7 屏扩至 **10 屏**:⑧**表单设计器**(字段库/画布/属性三栏 + 条件规则行,内置字段带锁不可删)⑨**材料规则配置**(材料清单侧栏 + 命名模板 + 档位选择 + 来源引导语)⑩**历史数据整理器**(用真实实测数字:475MB→37MB、59 项命名违规、仅 1 项需人工)。四份原型全部通过结构校验 |
| 2026-09-28 | **P6 验收测试清单** | 完成 `06-验收测试清单.md`(25KB / 518 行)。把 P4 的 15 项基线转成 **29 条可自动化断言 G-01~G-29**;另含 3 条端到端主流程(32 步)、20 条异常边界、85 条功能验收、6 条视觉、9 项性能、12 项安全;含里程碑签署表与 **7 条 Go/No-Go 条件**;点出 3 个易漏盲区(Windows 解压中文名 / 并发绑定临界窗口 / 「无」是合法值) |
| 2026-09-28 | **客户回执**(⚠️ 唯一增量 B1 已于 2026-10 需求回退取消) | 客户确认全部 23 项按建议执行,**需求冻结**。识别出唯一功能增量 **B1(申请加入名单)**,其余 22 项零改动 |
| 2026-09-28 | **D7 降级** | 重新评估「文件名识别真实准确率」这个待办:因**输出命名取自名册**、**归属靠证件号反查名册**、**识别有槽位兜底**、**失败一律放行**,该指标**不影响任何交付项**。降级为「上传页引导文案校准」——班主任在群里问两句即可,**取消样本索取**。至此**全部外部数据依赖清零** |
| 2026-09-28 | **范围收窄** | 客户确认「**先做学校内的填报流程**」→ 只做**学校收集 + 自主认定**(4 类材料全部有实测样本);社会收集 / 社会评价 / 专项职业能力**不做**(2026-10 进一步明确为取消,不再叫"后置二期"),**数据结构与枚举保留不启用**。工期 −5 人日(392→387),关键路径不变仍 15 周 |
| 2026-09-28 | **移除联系方式展示** | 客户指出"学生本来就有班主任联系方式,不需要系统里面体现"。删除 `GET /mp/roster/contact` 接口 + 门禁失败页「联系老师」按钮。**接口总数 122 → 121**。⚠️ 当时保留的两条系统内动作里,「申请加入名单」已于 2026-10 撤销,现在只剩「重新填写」+ 一句"请联系老师" |
| 2026-09-28 | **归属校验修正** | 客户指出"系统不存在名称错误"——纠正原设计中「把文件名不符自动按名册修正」的缺陷(会把传错的文件静默洗白)。改为 **VAL-21 文件归属校验 + 阻断提交**;判定优先级重排为 证件号反查名册 > OCR > 姓名(仅辅助);姓名不再作为独立规则。另补全「社会评价」定义与 `cert_type` 第三取值 |
| 2026-09-28 | **B1 落地** | 新增 FR-509 系列 10 条 / `collect_join_request` 表(**PG 16 实测通过 + 部分唯一索引业务规则验证**)/ 5 个接口 / 错误码 40037~40042 / 学生端 STEP 03B + 管理端 STEP 11 两屏原型 / F-509 验收项 19 条 / WBS 新增 6 个任务 |
| 2026-09-28 | **P7 文档导航** | 完成 ../README.md(10KB)。按角色给出读法、三分钟看懂产品、13 条关键决策速查、目录结构与快速上手、数据基线、两条踩坑教训 |
| 2026-09-28 | **P5 开发任务拆解** | 完成 `05-开发任务拆解.md`(27.6KB / 468 行)。**94 个任务 / 380 人日 / 含缓冲 456**,工期 **14 周**。关键结论:**工期由关键路径(67 人日串行)决定,不能按人日总数摊人**;压缩周期只能砍范围不能加人。含 3 个客户演示点(A 第 5 周 / B 第 10 周 / C 第 14 周)与 §9 可复现核算口径 |
| 2026-09-28 | **P4 核心模块设计** | 完成 `04-核心模块设计说明.md`(54KB / 1218 行)。六个模块全部伪代码级:M1 命名(含证件号三源裁决表)、M2 压缩(五档+二分降质+二次有损防护)、M3 识别(四层递进+成本闸)、M4 门禁(**并发绑定必须 UPDATE...WHERE bind_user_id IS NULL**)、M5 校验、M6 导出(快照先固化+中文文件名编码)。**校验中发现并修正三处真问题**,见下条教训 |
| 2026-09-28 | **P3 接口设计** | 完成 `03-接口设计.md`(102KB / 2626 行)。**117 个接口**全部字段级定义(PRD 原 96 + 新增 21);新增 §2 枚举全集(含「无」的三条铁律)、§3 通用数据结构(12 个 VO,字段名与 DDL 列一一对应)、§15 错误码全集(50 个,新增 30);**自校验通过**:模块表合计=声称=实际唯一数=117,无真重复,引用字段名 116 个全部存在于 DDL |
| 2026-10-09 | **验收走查后的四项修正** | ① **去掉 3 条无用菜单**(名单管理 / 表单设计器 / 材料规则)—— 功能都在「收集任务」行内入口,菜单里那一条只是指路;后端菜单键同步删除;② **修掉班主任点工作台必弹 403**(工作台无条件调了要 `org:view` / `progress:viewAll` 的两个接口,班主任都没这两个权限)→ 改为按权限决定发不发请求;③ **新增请求头 `X-Tenant-Id`(§1.2)**:平台运营的「组织架构 / 账号管理 / 工作台」原先拿到的是**所有租户混在一起**的数据,现在必须先在页面上选定学校;④ **修掉开放接口的"推送重试"从未生效**(F-112:定时扫描缺 `@InterceptorIgnore`,异常被 warn 吞掉;E2E 补了"真的重试了"的断言)。详见 `交接摘要` §一·三十三 |
| 2026-10-09 | **二期「角色与授权」上线**(FR-205) | 内置角色**只读** + 自定义角色的权限码 / 菜单可配:新增 **8 个接口**(`docs/03` §4.12 / §5.10,契约总数 121 → **129**)、**V18 迁移**(`sys_role.user_type` + `sys_role_menu`)、错误码 `40045`;权限判定从"零 IO"改为"内置按代码 / 自定义按库 + Redis 缓存";新增每日**内置角色授权同步**(修掉代码与库的静默漂移);PC 新增「角色与授权」页(原型 ⑪)→ **`ready:false` 的菜单归零**;账号页的可选角色 / 用户类型 / 角色名全部改读库(删掉前端三份写死的镜像表)。验证:新增 `scripts/e2e-m10-role.py` **77 条**、`verify-all --with-e2e` 全绿。**「数据范围可配」仍是下一批**(见 `docs/05` M8 的说明) |
| 2026-10-20 | **修生产登录 403「Invalid CORS request」** | 生产 `POST /api/v1/auth/login` 返回 403:**浏览器的同源 POST 也带 `Origin`,而 Spring 只看"有没有 Origin"**,`WebConfig` 的 CORS 白名单却**写死**成 localhost/内网(注释声称读 `fill.cors.allowed-origins`,该属性其实不存在)。改为配置驱动:`FillProperties.Cors` + 各 profile 的 `fill.cors.allowed-origins`(可用 `XT_CORS_ALLOWED_ORIGINS` 覆盖),生产默认含 `admin.shenxue.jxit.com` 与 `*.shenxue.jxit.com`,启动日志打印生效名单。**接口 / 库表 / 契约均无变化**。`docs/09` 新增 §2.4:本站地址必须在白名单 + 反代必须传 `Host`/`X-Real-IP`/`X-Forwarded-For`/`X-Forwarded-Proto`(缺了登录日志 IP 全是 127.0.0.1)。详见 `交接摘要` §一·四十四 |
---
## 三、客户回执(2026-09-28)
> **客户全部 23 项按建议方案确认。** 详见 `01-客户与机构确认函.md` 的客户回执章节。
| 组 | 项数 | 需开发 | 结论 |
|---|:--:|:--:|---|
| A 认定机构 | 6 | **0** | 全部与现有设计一致(当初就按实测数据设计) |
| B 校方业务 | 9 | **1**(⚠️ B1 已于 2026-10 需求回退取消) | 仅 **B1(申请加入名单通道)** 是功能增量,该通道已于 2026-10 删除 |
| C 商务法务 | 5 | 0 | C1 小程序资质按建议推进 + H5 保底 |
| D 内部技术 | 7 | 0 | **D4 已随"取消二期"作废**、**D7 已降级清除**;仅 D6(微信三条来源通道体积上限)需真机自测,**无外部依赖** |
**一期范围(客户确认后收窄)**:只做 **学校收集 + 自主认定**(4 类材料全部有实测样本)。
社会收集 / 社会评价 / 专项职业能力**不做**(2026-10 取消,不再是"后置"),**数据结构与枚举保留不启用**。
⇒ **D4(社会评价样本)因此不再是待办**;工期 **392 → 387 人日**,关键路径不变仍 **15 周**。
**本次变更(V1)**:新增 **FR-509 系列**(10 条)+ **1 张表**(`collect_join_request`)+ **5 个接口** + **2 屏原型**。
| 指标 | 变更前 | 变更后 |
|---|:--:|:--:|
| FR 总数 | 115 | **125** |
| 接口总数 | 117 | **121**(2026-10 收敛后为 **119**) |
| 库表 | 19 | **20** |
| VAL 规则 | 20 | **21**(新增 VAL-21 归属校验) |
| 错误码 | 50 | **57** |
| 原型屏数 | 29 | **31** |
| 任务数 | 94 | **100** |
| 人日 | 380 | **392** → **387**(一期收窄 −5) |
| 工期 | 14 周 | **15 周** |
---
## 四、已知风险(跟踪中)
| # | 风险 | 影响 | 对策 |
|---|---|---|---|
| R1 | ~~客户迟迟不确认 P1 的 5 项高优先级问题~~ | — | ✅ **已解除**:客户确认全部 23 项按建议执行,需求已冻结 |
| R2 | ~~强特征词表未在"社会评价"批次数据上标定~~ | — | ✅ **已消除**(2026-10 取消二期) |
| R3 | VLM 选型未定 | 中 | P4 中把 VLM 抽成**可替换接口**,不绑定供应商 |
| R4 | ~~二期启动时缺少社会评价样本~~ | — | **已消除**(2026-10 取消二期,不再需要该样本) |
---
## 2026-10 需求收敛(执行记录)
| 项 | 内容 |
|---|---|
| 起因 | 客户认为功能面过宽,要求收敛为「总后台开学校 → 学校管理员发任务 → 学生填 → 班主任 + 学校管理员审 → 归档导出 ZIP」 |
| 删除 | ① 内置角色 **6 → 4**(删副班主任 `DEPUTY_TEACHER`、审核员 `AUDITOR`,存量账号由 **V19 迁移**处理);② **历史整理器**(`module/cleanup`,5 接口 + `cleanup:run` + `collect_cleanup_job` 表 + PC 页 + 原型屏);③ **加入申请**(`module/join`,5 接口 + `join:audit` + `collect_join_request` 表 + PC 页 + 学生端 `pages/join` + 错误码 `40037/40038/40039/40040/40042` + 两个站内信模板);④ `collect_task_org.deputy_user_ids` **整列**(审核范围三级回退 → **两级**) |
| 取消 | **二期整体取消**:社会收集 / 社会评价 / 专项职业能力 / 数据范围可配 / 角色变更即时失效 / 平台角色页 → **不做**;OCR 与材料识别 → **缓做,等供应商选型**;材料模板 **3 → 1**;`XJW_BA`/`OTHER_PROOF` **读得到、选不到** |
| 数字 | 接口 **129 → 119**、错误码 **59 → 54**、权限码 **30 → 28**、PC 菜单 **12 → 10**、小程序页面 **10 → 9**、原型 **32 → 29 屏**、E2E 脚本 **39 → 37** |
| 迁移 | `V19__remove_deputy_auditor_cleanup_join.sql`(先解绑定再软删角色;账号只在"除这两个角色外没有别的角色"时软删) |
| ⚠️ 治理 | **加入申请(确认函 B1)是客户唯一确认的功能增量,本次属需求回退**,建议补变更确认单 |
| 详见 | `docs/交接摘要.md` **§一·三十六**(含 5 条新增防线与 PC 端重搭说明) |