docs: 登记 F-125~F-130 并同步脚本数 37→38
三个 P0 本轮只测不改、先记账:F-125 学生被驳回后改不了任何东西(改字段/传材料/ 删材料全 40030、撤回 40001,而重新提交是允许的,四处信号都说他能改)、 F-128 扩展字段存得进读不出、必填扩展字段会让整个任务永久无法提交(内置表单一个 扩展字段都没有,所以从来没有数据走过这条路)、F-130 小程序端登录断成两截 (前端从没调过 /mp/login,后端 scene 带 taskId 必然 500)。 README 补 docs/11 的入口,AGENTS.md 补小程序端现状与 38 个脚本的新口径, 问题清单补 F-126/F-127/F-129 三条与 F-125/F-128/F-130 的修法方向。
This commit is contained in:
@@ -91,6 +91,10 @@ pnpm dev:mp-weixin # → dist/dev/mp-weixin(导入微信开发者工具)
|
|||||||
- `vue-tsc` 有 2 条**已知**报错(`src/config/index.ts` 的 `#ifdef` 重复导出),不是新引入的。
|
- `vue-tsc` 有 2 条**已知**报错(`src/config/index.ts` 的 `#ifdef` 重复导出),不是新引入的。
|
||||||
⚠️ PC 端不同:重搭后 `pnpm typecheck` 是 **0 错** —— 那边一有报错就是新引入的
|
⚠️ PC 端不同:重搭后 `pnpm typecheck` 是 **0 错** —— 那边一有报错就是新引入的
|
||||||
- 当前节奏:**先做 H5**(可自动化测试、不依赖小程序资质);小程序端代码同步维护
|
- 当前节奏:**先做 H5**(可自动化测试、不依赖小程序资质);小程序端代码同步维护
|
||||||
|
- ⚠️ **小程序端目前学生用不了(F-130,P0)**:前端**从来没有调用过 `/mp/login`**(token 永远是空 → 门禁 40101),
|
||||||
|
而后端那条路也不通 —— `scene` 带 taskId 时 `/mp/login` 必然 500(`MpLoginService.java:127` 匿名查租户表被租户拦截器拒)。
|
||||||
|
实测细节见 `docs/11` §5.4 / §6。**除登录外整条链路(落地页/门禁/表单/压缩上传/红黄校验/提交)都是好的**
|
||||||
|
—— 用开发者工具自动化(`wmp_*` 工具族)可以跑,`wx.login()` 在页面上下文里能拿到**真 code**
|
||||||
|
|
||||||
## 三、怎么验证(本仓库最不显然的一点)
|
## 三、怎么验证(本仓库最不显然的一点)
|
||||||
|
|
||||||
@@ -101,8 +105,9 @@ pnpm dev:mp-weixin # → dist/dev/mp-weixin(导入微信开发者工具)
|
|||||||
python scripts/e2e-m3-file.py # 单跑一个;前提是后端 8080 在跑、xt-pg 容器在跑
|
python scripts/e2e-m3-file.py # 单跑一个;前提是后端 8080 在跑、xt-pg 容器在跑
|
||||||
```
|
```
|
||||||
|
|
||||||
- 全量:把 `scripts/e2e-*.py` 依次跑一遍(**37 个脚本 / 1617 条断言**,以最近一次
|
- 全量:把 `scripts/e2e-*.py` 依次跑一遍(**38 个脚本 / 1712 条断言**,以最近一次
|
||||||
`verify-all --with-e2e` 的输出为准;2026-10 需求收敛删掉了 `e2e-m2-join.py` 与 `e2e-m7-cleanup.py`),
|
`verify-all --with-e2e` 的输出为准;2026-10 需求收敛删掉了 `e2e-m2-join.py` 与 `e2e-m7-cleanup.py`,
|
||||||
|
之后又补了 `e2e-m3-flow.py` —— 填报链路的覆盖缺口,见 `docs/11-填报流程端到端测试计划.md`),
|
||||||
或一条命令 `python scripts/verify-all.py --with-e2e`
|
或一条命令 `python scripts/verify-all.py --with-e2e`
|
||||||
- 脚本**自己造 MP 的 JWT**:读 `application-dev.yml` 的 `fill.jwt.secret` +
|
- 脚本**自己造 MP 的 JWT**:读 `application-dev.yml` 的 `fill.jwt.secret` +
|
||||||
`application.yml` 的 `issuer` 手搓 HS512 —— 不需要先登录
|
`application.yml` 的 `issuer` 手搓 HS512 —— 不需要先登录
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
| **产品 / 需求** | `数据信息填报系统PRD.md` → `数据信息填报系统PRD-功能逻辑卷.md` | 前者含库表接口,后者是纯功能逻辑 |
|
| **产品 / 需求** | `数据信息填报系统PRD.md` → `数据信息填报系统PRD-功能逻辑卷.md` | 前者含库表接口,后者是纯功能逻辑 |
|
||||||
| **后端开发** | `03-接口设计.md` → `04-核心模块设计说明.md` → `db/schema-postgresql.sql` | 接口收发 → 引擎怎么做 → 建表 |
|
| **后端开发** | `03-接口设计.md` → `04-核心模块设计说明.md` → `db/schema-postgresql.sql` | 接口收发 → 引擎怎么做 → 建表 |
|
||||||
| **前端开发** | `prototype/` → `03-接口设计.md` | 原型是视觉与交互的**唯一基准** |
|
| **前端开发** | `prototype/` → `03-接口设计.md` | 原型是视觉与交互的**唯一基准** |
|
||||||
| **测试** | `06-验收测试清单.md` → `04-核心模块设计说明.md` 附录 B | 36 条 Golden 断言 + 111 条功能验收 |
|
| **测试** | `11-填报流程端到端测试计划.md`(填报链路 E2E)→ `06-验收测试清单.md` → `04-核心模块设计说明.md` 附录 B | 链路覆盖矩阵 + 判据口径;36 条 Golden 断言 + 111 条功能验收 |
|
||||||
| **新加入的人** | `交接摘要.md` → 本文件 | 一份文档看清全部状态 |
|
| **新加入的人** | `交接摘要.md` → 本文件 | 一份文档看清全部状态 |
|
||||||
|
|
||||||
### 全部文档
|
### 全部文档
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
| 04 | `04-核心模块设计说明.md` | 56 KB | **6 个模块**伪代码级说明 + 依赖图 + 26 项回归基线 | ✅ |
|
| 04 | `04-核心模块设计说明.md` | 56 KB | **6 个模块**伪代码级说明 + 依赖图 + 26 项回归基线 | ✅ |
|
||||||
| 05 | `05-开发任务拆解.md` | 31 KB | **100 个任务 / 387 人日 / 15 周**,含关键路径、一期范围与风险 | ✅ |
|
| 05 | `05-开发任务拆解.md` | 31 KB | **100 个任务 / 387 人日 / 15 周**,含关键路径、一期范围与风险 | ✅ |
|
||||||
| 06 | `06-验收测试清单.md` | 29 KB | **36 条 Golden 断言** + 111 条功能验收 + 签署表 | ✅ |
|
| 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 条铁律、已完成、待办、环境、教训 | ✅ |
|
| — | `交接摘要.md` | 11 KB | **状态源**:客户回执、13 条铁律、已完成、待办、环境、教训 | ✅ |
|
||||||
| — | `数据信息填报系统PRD.md` | 190 KB | 主 PRD V2.3+:**125 条 FR**、**21 条 VAL**、**20 张表 DDL**、验收标准 | ✅ |
|
| — | `数据信息填报系统PRD.md` | 190 KB | 主 PRD V2.3+:**125 条 FR**、**21 条 VAL**、**20 张表 DDL**、验收标准 | ✅ |
|
||||||
| — | `数据信息填报系统PRD-功能逻辑卷.md` | 88 KB | 纯功能运转逻辑(**不含库表/接口/代码**) | ✅ |
|
| — | `数据信息填报系统PRD-功能逻辑卷.md` | 88 KB | 纯功能运转逻辑(**不含库表/接口/代码**) | ✅ |
|
||||||
@@ -197,7 +198,7 @@ backend/ Java 21 + Spring Boot 4.1.1
|
|||||||
├── xt-common/ 统一响应 / 54 个错误码 / AES-GCM / HMAC / RSA
|
├── xt-common/ 统一响应 / 54 个错误码 / AES-GCM / HMAC / RSA
|
||||||
└── xt-server/ 配置 / 鉴权 / 多租户拦截 / 业务模块(26 张表,Flyway 已到 V19)
|
└── xt-server/ 配置 / 鉴权 / 多租户拦截 / 业务模块(26 张表,Flyway 已到 V19)
|
||||||
deploy/ docker-compose + RSA 开发公钥
|
deploy/ docker-compose + RSA 开发公钥
|
||||||
scripts/ E2E 验收(37 个脚本)、接线检查、DDL 同步、图标抽取
|
scripts/ E2E 验收(38 个脚本)、接线检查、DDL 同步、图标抽取
|
||||||
frontend-pc/ PC 管理端:**纯 Vue 3 + Naive UI**(pnpm dev → 5173)
|
frontend-pc/ PC 管理端:**纯 Vue 3 + Naive UI**(pnpm dev → 5173)
|
||||||
frontend-mp/ 学生端 / 教师端:uni-app(H5 与微信小程序同一套代码)
|
frontend-mp/ 学生端 / 教师端:uni-app(H5 与微信小程序同一套代码)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
> | PC 业务菜单 | 12 | **10** |
|
> | PC 业务菜单 | 12 | **10** |
|
||||||
> | 小程序页面 | 10 | **9** |
|
> | 小程序页面 | 10 | **9** |
|
||||||
> | 原型屏 | 32 | **29** |
|
> | 原型屏 | 32 | **29** |
|
||||||
> | E2E 脚本 | 39 | **37**(断言 1698 → **1617 条**,0 失败 0 跳过) |
|
> | E2E 脚本 | 39 | **37**(断言 1698 → **1617 条**,0 失败 0 跳过);**2026-10 新增 `e2e-m3-flow.py` → 38 个 / 1712 条** |
|
||||||
>
|
>
|
||||||
> **删除的三块**:① 内置角色「副班主任 `DEPUTY_TEACHER`」「审核员 `AUDITOR`」;
|
> **删除的三块**:① 内置角色「副班主任 `DEPUTY_TEACHER`」「审核员 `AUDITOR`」;
|
||||||
> ② **历史整理器**(`module/cleanup`,5 接口 + `cleanup:run` + `collect_cleanup_job` 表 + PC 页 + 原型屏);
|
> ② **历史整理器**(`module/cleanup`,5 接口 + `cleanup:run` + `collect_cleanup_job` 表 + PC 页 + 原型屏);
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
| 管理端(PC) | 登录/工作台/组织/账号/租户/收集任务 + 3 个配置抽屉 + 名册与报考范围 + 审核台 + 导出中心 / 通知记录 / 操作日志 / 字典管理 / **角色与授权**(**纯 Vue 3 + Naive UI**,2026-10 重搭);**已删除**:加入申请页、历史整理器页(2026-10) | 表单设计器已上线 | 约 100% |
|
| 管理端(PC) | 登录/工作台/组织/账号/租户/收集任务 + 3 个配置抽屉 + 名册与报考范围 + 审核台 + 导出中心 / 通知记录 / 操作日志 / 字典管理 / **角色与授权**(**纯 Vue 3 + Naive UI**,2026-10 重搭);**已删除**:加入申请页、历史整理器页(2026-10) | 表单设计器已上线 | 约 100% |
|
||||||
| 学生端(H5/小程序) | 落地页、门禁核验、填报、**我的任务 / 撤回 / 进度 / 消息中心**(**加入申请页已删除**,门禁失败只提示联系老师) | 临期提醒页、催报 | 约 90% |
|
| 学生端(H5/小程序) | 落地页、门禁核验、填报、**我的任务 / 撤回 / 进度 / 消息中心**(**加入申请页已删除**,门禁失败只提示联系老师) | 临期提醒页、催报 | 约 90% |
|
||||||
| 班主任端(小程序) | 看板 / 审核卡片流 / 详情与驳回(右滑通过、左滑不绑驳回)+ **代传材料 / 催报 / 学生材料预览**(后端与 MP 页面均已就绪) | 临期提醒入口 | 约 95% |
|
| 班主任端(小程序) | 看板 / 审核卡片流 / 详情与驳回(右滑通过、左滑不绑驳回)+ **代传材料 / 催报 / 学生材料预览**(后端与 MP 页面均已就绪) | 临期提醒入口 | 约 95% |
|
||||||
| 基础设施 | PG 16 + Redis 7 + 七牛(`provider=qiniu` 已验)、Flyway **V1~V19**、本地存储兜底、导出产物落盘 + 流式下载 + 分卷与过期清理、**37 个 E2E 脚本(断言数以 `verify-all` 为准)+ 4 个机械校验脚本** | CI/CD、test/prod 环境、压测、灰度 | 约 58% |
|
| 基础设施 | PG 16 + Redis 7 + 七牛(`provider=qiniu` 已验)、Flyway **V1~V19**、本地存储兜底、导出产物落盘 + 流式下载 + 分卷与过期清理、**38 个 E2E 脚本(断言数以 `verify-all` 为准)+ 4 个机械校验脚本** | CI/CD、test/prod 环境、压测、灰度 | 约 58% |
|
||||||
|
|
||||||
**契约接口实现度(按 §4 分类)**
|
**契约接口实现度(按 §4 分类)**
|
||||||
|
|
||||||
@@ -498,6 +498,12 @@
|
|||||||
| F-122 | 📌 **只在平台身份下可达、且查业务表的接口 = 租户越界的风险面**:F-112 暴露的是一个**模式**,不是一处笔误 —— 凡是"平台专属权限码 + 查带 `tenant_id` 的表",都会因为 `ignoreTable()` 对平台返回 true 而**静默跨租户** | 本轮把 `tenant` 模块逐接口过了一遍(`list` / `detail` / `create` / `update` / `usage`),**只有 `usage` 统计业务表**,已修;`logs` 模块自己显式处理了范围(`logScopeTenantId`)。**留下的排查线索**:以后新增平台专属接口时,先问一句"它查的是不是带 tenant_id 的表" |
|
| F-122 | 📌 **只在平台身份下可达、且查业务表的接口 = 租户越界的风险面**:F-112 暴露的是一个**模式**,不是一处笔误 —— 凡是"平台专属权限码 + 查带 `tenant_id` 的表",都会因为 `ignoreTable()` 对平台返回 true 而**静默跨租户** | 本轮把 `tenant` 模块逐接口过了一遍(`list` / `detail` / `create` / `update` / `usage`),**只有 `usage` 统计业务表**,已修;`logs` 模块自己显式处理了范围(`logScopeTenantId`)。**留下的排查线索**:以后新增平台专属接口时,先问一句"它查的是不是带 tenant_id 的表" |
|
||||||
| F-123 | ⚠️ **登录日志没有租户列 → 平台视角下这些行"不属于任何学校"**:`LoginLogVO` 11 个字段里没有 `tenantId`,而平台运营看到的日志是**跨单位**的(`log:viewAll`)。于是"某个账号什么时候登进来的"这个问题答不完整 —— 筛得了单位,却看不出每行属于哪个单位。`sys_login_log.tenant_id` 从 V2 建表起就有(`NOT NULL DEFAULT 0`),只是 VO 没带出来 | 已修:`LoginLogVO` 加 `tenantId`、`LoginLogMapper.listLogins` 的 SELECT 加 `l.tenant_id AS tenantId`、契约 §13.4 同步;界面上登录日志表在「时间」后加「单位」列(`0` → 平台),**仅平台身份显示**(学校管理员自己就是那个单位,加了是噪音) |
|
| F-123 | ⚠️ **登录日志没有租户列 → 平台视角下这些行"不属于任何学校"**:`LoginLogVO` 11 个字段里没有 `tenantId`,而平台运营看到的日志是**跨单位**的(`log:viewAll`)。于是"某个账号什么时候登进来的"这个问题答不完整 —— 筛得了单位,却看不出每行属于哪个单位。`sys_login_log.tenant_id` 从 V2 建表起就有(`NOT NULL DEFAULT 0`),只是 VO 没带出来 | 已修:`LoginLogVO` 加 `tenantId`、`LoginLogMapper.listLogins` 的 SELECT 加 `l.tenant_id AS tenantId`、契约 §13.4 同步;界面上登录日志表在「时间」后加「单位」列(`0` → 平台),**仅平台身份显示**(学校管理员自己就是那个单位,加了是噪音) |
|
||||||
| F-124 | ⚠️ **`LoginLogVO.userType` 的 Javadoc 与真实枚举错位**:写着「1 超管 / 2 管理员 / 3 教师 / 4 学生」,而权威口径(`BuiltinRole.UserType`、`UserService.USER_TYPE_NAMES`、库列注释、《03》§2.7)是 **1 平台运营 / 2 学校管理员 / 3 班主任 / 4 学生**。实测库里 `user_type=1` 的行就是 `platform` 账号。这是**同一个坑的第三次出现**(该错位枚举曾导致"把班主任当成社会人员拒掉") | 已修:Javadoc 改为正确枚举 + 写明"权威来源是 `BuiltinRole.UserType`,别按模板那套映射"。前端本来就用对了(复用 `USER_TYPE_NAMES`,没按评审意见里那句错的口径写),所以这处只影响读代码的人 |
|
| F-124 | ⚠️ **`LoginLogVO.userType` 的 Javadoc 与真实枚举错位**:写着「1 超管 / 2 管理员 / 3 教师 / 4 学生」,而权威口径(`BuiltinRole.UserType`、`UserService.USER_TYPE_NAMES`、库列注释、《03》§2.7)是 **1 平台运营 / 2 学校管理员 / 3 班主任 / 4 学生**。实测库里 `user_type=1` 的行就是 `platform` 账号。这是**同一个坑的第三次出现**(该错位枚举曾导致"把班主任当成社会人员拒掉") | 已修:Javadoc 改为正确枚举 + 写明"权威来源是 `BuiltinRole.UserType`,别按模板那套映射"。前端本来就用对了(复用 `USER_TYPE_NAMES`,没按评审意见里那句错的口径写),所以这处只影响读代码的人 |
|
||||||
|
| F-125 | ⚠️ **学生被驳回后「改不了任何东西」(P0 · 阻断闭环)**:`status=4` 时改字段 → `40030`(`SubmissionService.java:158-161`)、传材料 → `40030`(`MaterialFileService.java:103-105`)、删材料 → `40030`(`:338-341`)、撤回 → `40001`(只放行 1/2,`StudentTaskService.java:190-196`),而**重新提交是允许的**(`SubmissionService.java:293-297`)—— 于是学生能看到"被驳回,请修改后重新提交"却什么都没法改。**四处信号同时说他能改**:`statusText`(`StudentTaskService.java:142`)、`canFillNow=true`(`:105-106`,`underReview` 只算 1/2/3)、`progress.canEdit=true`(`:311`)、`progress.canSubmit=true`(`:313`)。契约站学生一边:《03》`§2.4`(`docs/03-接口设计.md:239-248`)写 `4 已驳回 → 可流转到 1(学生重提交)` | **本轮只测不改**,已登记 `docs/11` F-125 + `scripts/e2e-m3-flow.py` 三条 XFAIL(`EP-E09`/`EP-F10`/`EP-F10b`/`EP-I09`)。**为什么 1617 条断言没抓到**:`scripts/e2e-m5-audit.py:531-538` 重交前只改了库里的 `reject_count`,**没改任何业务字段** —— 它证明的是"原样重交能过",而问题在"改了反而过不去"。**建议最小改法**(改业务口径,须确认):三处写守卫从 `!= STATUS_DRAFT` 放宽为 `∉ {STATUS_DRAFT, STATUS_REJECTED}`,与 `submit`、`progress.canEdit` 对齐 |
|
||||||
|
| F-126 | 📌 **`scripts/e2e-m3-draft.py` 的 docstring 自述虚高**:`:11` 写着 `[x] …已提交的不能直接改`,但该文件**全文没有这条断言**(全仓唯一涉及 `40030` 的是 `scripts/e2e-m5-proxy.py:277`,测的是**代传**路径)。docstring 的 `[x]` 清单在本仓库被当作覆盖率的**唯一账本**(`verify-all` 的汇总与多份文档的"37 个脚本 / N 条断言"都建立在它上面),所以虚高比 F-84 更隐蔽:断言缺失不会让任何东西变红,反而让覆盖率**看起来更好** | 本轮已补断言:`EP-E08`(已提交后 `PUT` → `40030`)与 `EP-F09`(已提交后上传 → `40030`),均通过。**教训固化成规程**:`docs/11` §3.1 要求用 `grep 'check(\|ok('` 核对既有脚本的**实际**断言而不是读自述 —— 这条规程一上来就抓到了本项 |
|
||||||
|
| F-127 | 📌 **契约 §9.10 的口径与实现不一致,且未登记在「有意偏离」清单里**:《03》`§9.10`(`docs/03-接口设计.md:1986-1996`)写"校验失败返回 `40001` + `data.blocking[]/warnings[]/canForceSubmit`",实现是 **HTTP 200 + `code:0`** + `data{submitted:false,status,statusText,redCount,yellowCount,infoCount,issues[]}`(`FillController.java:59-70`,代码注释给了理由:校验不通过是正常业务结果、要一次给全清单)。实现口径与项目约定一致、37 个脚本也都按它写,**但 `§17.2` 的 D-1~D-12(`docs/03-接口设计.md:2972-2988`)里没有它** | 本轮只提议:在 `§17.2` 补一行 **D-13**(表格仍 5 列,不影响 `verify-docs.py`)。**后果**:文档读者会写出 `code != 0` 的判据,而那是**恒假**的 —— 测试静默变绿 |
|
||||||
|
| F-128 | ⚠️ **扩展字段「存得进、读不出」,必填扩展字段会让整个任务永久无法提交(P0 · 阻断功能)**:`PUT /mp/submissions/{id} {"extrasJson":{...}}` → `code=0` 且库里 `extras_json` 确实写进去了,但 `GET /mp/tasks/{id}/form` 该字段 `value` 恒为 `null`,提交时 `VAL-22` 恒报"还有必填项没填"。**根因**:`FillService.assemble`(`FillService.java:106-159`)构造取值表只有两个来源 —— 名册列、以及 `schema.getFields()` 中 `SubValues.hasColumn(key)` 成立的**列**,**从不把 `draft.extrasJson` 合进 `values`**;而扩展字段的值只存在 `extras_json` 里。于是 `SubmissionValidator.validate`(`SubmissionValidator.java:104-109`)重建的 `values` 里也没有它,`checkRequiredFields` 的 `blank()` 恒为真 | **本轮只测不改**,已登记 `docs/11` F-128 + 两条 XFAIL(`EP-D09` 回显、`EP-J02e` 提交)。**为什么至今没暴露**:内置表单(`BuiltinFields.java`)**一个扩展字段都没有**,既有 37 个脚本用的全是内置表单 → 这条路径从来没有数据走过。**建议最小改法**(改读取口径,须确认):在 `FillService.java:144-158` 那段循环之后,把 `schema.getExtras()` 的每个键从 `draft.getExtrasJson()` 解析后 `values.put(key, v)`;⚠️ 不要顺手把扩展字段塞进 `changed_fields`(那是给内置列用的) |
|
||||||
|
| F-129 | 📌 **把已声明的扩展字段放进 `formData` 不会报错,值被静默丢弃**:`{"formData":{"extra_upgrade_note":"..."}}` → `code=0`,`extras_json` 仍是 `{}`。对照:**未声明**的键放进 `formData` 会明确 `40001`(`SubmissionService.java:178-182`,注释写着"不静默忽略");而**已声明**的扩展键在 `defs` 里查得到(`:169-171` 把 extras 也放进了 `defs`),于是躲过那道检查,接着 `SubValues.apply(sub, key, value)` 对它**没有对应列、静默什么都不做** | 契约要求扩展字段走 `extrasJson`(`DraftDtos.java:44`),属"用错参数";但同一个方法里"不静默忽略"的原则对两种塞错口袋的待遇不一致,前端写错一次就是学生白填一遍。已登记 `docs/11` F-129 + `EP-E16`(钉住现状)。**建议**:`SubValues.apply` 遇到"既不是列、又确实是声明过的扩展键"时抛 `40001`,提示"扩展字段请放在 `extrasJson`" |
|
||||||
|
| F-130 | ⚠️ **微信小程序端的学生完全进不来,登录链路断成两截(P0 · 功能不可用)**:开发者工具模拟器里实测,扫任务码进小程序 → 落地页正常 → 点「开始填报」→ 填姓名 + 身份证 → 点「核对并继续」→ 屏幕只有**「未登录或登录已失效」**。**断点 A(前端)**:小程序端**从来没有调用过 `/mp/login`** —— 全仓 `frontend-mp/src` 里唯一写 token 的地方是 `pages/index/index.vue:222`(从 **H5 授权回调**的 `options.token` 取),而 9 个页面的 `token()` 都读 `uni.getStorageSync('token')`;搜不到 `wx.login`/`uni.login`/`/mp/login` 的**调用**(只有 `utils/request.ts:22` 一句注释「登录流程见契约 §5.x 的 /mp/login,**属 M3**」),也**没有 `web-view` 兜底**。**断点 B(后端)**:`/mp/login` 只要 `scene` 带 taskId 就**必然 500** —— `MpLoginService.java:127` 用普通 `BaseMapper.selectOne` 查 `collect_roster`,而该接口是 `@PublicApi`(匿名)→ **租户拦截器默认拒绝** → `BizException(40101)` → 被 MyBatis 包成 `PersistenceException` → `GlobalExceptionHandler` → **HTTP 500 / code 50000「系统繁忙,请稍后再试」** | **本轮只测不改**,已登记 `docs/11` F-130 与 §5.4。**对照实验**(同秒同端点,只改 scene):`t1` → **200 + token**;`t1-k2313` → **500** —— 即"只在永远用不到的场景下能用"(学生扫的码必然带 taskId)。日志铁证:`已建学生账号:uid=1389` 之后紧跟 `Cause: BizException: 未登录或登录已失效`,栈顶 `MpLoginService.login(MpLoginService.java:127)`。**副作用已核实**:`login()` 是 `@Transactional`,账号随异常**回滚**(库里只有 `scene=t1` 成功建出的 uid 1390),所以是"每次点都 500、什么也没留下",不是"攒下孤儿账号"。**为什么至今没暴露**:① `/mp/login` 没有任何 E2E 覆盖(`scripts/e2e-m3-gate.py:18` 写着"脚本拿不到 wx.login code" —— **本轮证明它拿得到**,开发者工具自动化里可在页面上下文调 `wx.login()` 换到真 code);② 文档互相矛盾(`docs/08:147` 记「学生端 `/mp/login` + 手机号绑定 ✅ 已完成」指的是**后端**,`docs/交接摘要.md:563` 还留着「`/mp/login`(小程序登录)仍未做」),**没有一处说"前端没接"**。**修法方向**(未实施):该分支改走**专用 Mapper + `@InterceptorIgnore(tenantLine=\"true\")` + 显式带 tenant_id**(AGENTS.md 那条铁律,`MpH5Mapper.selectByOpenId(tenantId, openId)` 就是这么做的,这里漏了);另外 40101 不该被包成 50000 对外 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user