docs: 同步交接摘要 / PRD / 原型,并记录品牌改名「深学在线」
- 交接摘要补 §一·三十五~§一·三十八:角色与授权、需求收敛(4 角色 / 10 菜单 / 取消二期)、 PC 端重搭后的 UI 修复与卡片式基线、品牌改名与新标识 - PRD 与功能逻辑卷只改标题行与产品代号线;**文件名不动** —— verify-docs.py 按路径读, 全库还有 20+ 处按文件名引用,改名会连带炸脚本 - 原型 index / admin-web / teacher-app / student-app 的品牌区改「深学在线」, admin-web 的 .mark(10 处)换成内嵌的新标 data URI,保持原型"单文件自包含"的约定 - 根 AGENTS.md 与 frontend-pc 的规范同步:接线检查补第 ⑨ 项(SVG 资产是不是合法 XML —— SVG 注释里出现连续两个减号就 not well-formed,图标裂开而控制台一句话都不报)、 「最容易写错的约定」6 -> 7 条(新增品牌标) - deploy/docker-compose.yml 只改头注释里的项目名(compose 的 name 与七牛对象前缀都没动)
This commit is contained in:
@@ -3,22 +3,27 @@
|
||||
面向 AI 编码助手的项目指南。**用简体中文交流、用中文写文档。**
|
||||
|
||||
需求已冻结:**文档是需求源,代码是它的实现**,不要按"更合理"去改业务口径。
|
||||
`docs/交接摘要.md` 是**唯一状态源** —— 动手前先读它最近的两节(§一·二十、§一·二十一)
|
||||
`docs/交接摘要.md` 是**唯一状态源** —— 动手前先读它最近的两节(**§一·三十五、§一·三十六**)
|
||||
与 `## 二、已锁定的铁律`;那里面大量决定"读代码看不出来、但都有理由"。
|
||||
|
||||
> ⚠️ **2026-10 需求收敛(见 §一·三十六)**:只做 **4 个角色**(平台运营 / 学校管理员 / 班主任 / 学生)、
|
||||
> **10 个 PC 菜单**、**不设二期**。删掉的东西不要再"顺手加回来":副班主任 / 审核员两个角色、
|
||||
> 历史整理器、加入申请(含学生端自助申请入口)、`collect_task_org.deputy_user_ids`。
|
||||
|
||||
## 一、四个可运行部分
|
||||
|
||||
| 目录 | 是什么 | 入口 |
|
||||
|---|---|---|
|
||||
| `backend/` | Java 21 + Spring Boot 4.1.1 + MyBatis-Plus + PG16 + Redis7 | `xt-server/`(`xt-common/` 放响应体/错误码/加解密) |
|
||||
| `frontend-pc/` | 管理端 Vue3 + Arco Design Vue(基于 **Gi Admin Pro**) | `pnpm dev` → 5173 |
|
||||
| `frontend-pc/` | 管理端 **纯 Vue 3 + Naive UI 2.42**(2026-10 从 Gi Admin Pro + Arco 重搭) | `pnpm dev` → 5173 |
|
||||
| `frontend-mp/` | 学生端 **uni-app**(H5 与微信小程序同一套代码) | `pnpm build:h5` / `pnpm dev:mp-weixin` |
|
||||
| `deploy/` | 本地基础设施(PG + Redis,**没有对象存储**) | `docker compose up -d` |
|
||||
|
||||
`docs/prototype/` 是视觉与交互的**唯一基准**(单文件自包含、零外部依赖、**不用 emoji**)。
|
||||
|
||||
**一期范围只做「学校收集 + 自主认定」**:`collectMode=2` / `certType=2,3` 一律返回 `40044`,
|
||||
别顺手把二期功能实现出来。
|
||||
**范围只做「学校收集 + 自主认定」**:`collectMode=2` / `certType=2,3` 一律返回 `40044`。
|
||||
**不设二期** —— 与「二期」有关的任何设计(社会收集、社会评价、专项职业能力、数据范围可配等)都**不做**,
|
||||
别顺手实现;OCR 与材料识别是「缓做,等供应商选型」。
|
||||
|
||||
## 二、常用命令(备注里是容易猜错的地方)
|
||||
|
||||
@@ -42,7 +47,7 @@ java -jar xt-server/target/xt-server-1.0.0-SNAPSHOT.jar --spring.profiles.active
|
||||
`application-local.yml`(不提交的本地私有配置)就在 `backend/`;
|
||||
H5 静态目录配的是 `file:../frontend-mp/dist/build/h5/`
|
||||
- 跑的是 **jar 不是热加载** → 改后端要重新 package(先停进程,否则 jar 被占用)
|
||||
- **动过 `db/migration/` 必须 `mvn clean package`**(Flyway 已到 V6;迁移文件发布后不许改)
|
||||
- **动过 `db/migration/` 必须 `mvn clean package`**(Flyway 已到 **V19**;迁移文件发布后不许改,只能新加)
|
||||
- 本地私有配置机制是 `profiles.active=dev` + `spring.profiles.include: local`
|
||||
(模板 `backend/application-local.yml.example`)。
|
||||
**不要改用 `spring.config.import`** —— 它优先级低于 profile 文件,会静默失效(见 `application.yml` 注释)
|
||||
@@ -56,12 +61,17 @@ pnpm typecheck # vue-tsc pnpm lint # eslint
|
||||
|
||||
- 用 **pnpm**(仓库里是 `pnpm-lock.yaml`;别 `npm install`,会多出 package-lock.json)
|
||||
- dev 通过 vite proxy 把 `/api` 转发到 `http://localhost:8080`(见 `.env.development`),
|
||||
**没有 Mock**(`mock/` 与 `vite-plugin-mock` 都已删除)→ 后端不起着就没有数据。
|
||||
⚠️ `frontend-pc/.agents/skills/project-api/SKILL.md` 仍以 Mock 为主线(Gi 模板原文),
|
||||
该文件开头已加"本仓库不用 Mock"的说明 —— 看它时先看那段
|
||||
- 我们的接口模块都在 `src/apis/xt/`;`apis/system|user|person|cate|area|test` 是 Gi 模板自带的,别往那儿加
|
||||
- **PC 前端有独立的规范来源:`frontend-pc/.agents/`**(`AGENTS.md` + `skills/*`)。
|
||||
改那里的代码前按它的表加载对应 skill,不要另起一套写法
|
||||
**没有 Mock**(`mock/` 与 `vite-plugin-mock` 都已删除)→ 后端不起着就没有数据
|
||||
- 接口模块都在 `src/apis/xt/`(按业务域分文件);**没有 auto-import**,组件与 API 全显式 `import`
|
||||
- ⚠️ **2026-10 已从 Gi Admin Pro + Arco 重搭为纯 Vue 3 + Naive UI**,因此:
|
||||
- **`pnpm lint:style`(Stylelint)已移除**,只剩 `pnpm lint`(ESLint);`pnpm typecheck` 现在 **0 错**
|
||||
- 权限判断统一写 `userStore.has('码')`(**没有 `v-hasPerm` 指令**),字符串必须与后端 `PermissionDef` **逐字一致**
|
||||
- `n-tag` 的 `type` 必须过 **`@/utils/tag.ts`** 转换:`apis/xt/*.ts` 里的展示映射仍是 Arco 色词
|
||||
(`green`/`arcoblue`/`gold`/`gray`),直接透传会**静默变灰**且不报错
|
||||
- 图标:`scripts/extract-pc-icons.py` 生成 `src/icons/icon-data.ts`(37 个);
|
||||
`AppIcon` 对缺失图标是**空占位、不报错** → **图标集升级后要重跑该脚本**
|
||||
- **PC 前端有独立的规范来源:`frontend-pc/AGENTS.md` + `frontend-pc/.agents/`**(已按 Naive UI 重写)。
|
||||
改那里的代码前先读它,不要另起一套写法
|
||||
|
||||
### 学生端(uni-app)
|
||||
|
||||
@@ -78,7 +88,8 @@ pnpm dev:mp-weixin # → dist/dev/mp-weixin(导入微信开发者工具)
|
||||
dev server → 代理;构建产物(后端托管)→ `location.origin`;小程序 → 写死地址
|
||||
- 改前端后浏览器要**硬刷新**(`/assets/**` 是 immutable 缓存)
|
||||
- 构建时 Dart Sass 的 `legacy-js-api` DEPRECATION 属正常
|
||||
- `vue-tsc` 有 2 条**已知**报错(`src/config/index.ts` 的 `#ifdef` 重复导出),不是新引入的
|
||||
- `vue-tsc` 有 2 条**已知**报错(`src/config/index.ts` 的 `#ifdef` 重复导出),不是新引入的。
|
||||
⚠️ PC 端不同:重搭后 `pnpm typecheck` 是 **0 错** —— 那边一有报错就是新引入的
|
||||
- 当前节奏:**先做 H5**(可自动化测试、不依赖小程序资质);小程序端代码同步维护
|
||||
|
||||
## 三、怎么验证(本仓库最不显然的一点)
|
||||
@@ -90,7 +101,8 @@ pnpm dev:mp-weixin # → dist/dev/mp-weixin(导入微信开发者工具)
|
||||
python scripts/e2e-m3-file.py # 单跑一个;前提是后端 8080 在跑、xt-pg 容器在跑
|
||||
```
|
||||
|
||||
- 全量:把 `scripts/e2e-*.py` 依次跑一遍(**38 个脚本 / 1620 条断言**),
|
||||
- 全量:把 `scripts/e2e-*.py` 依次跑一遍(**37 个脚本 / 1595 条断言**,以最近一次
|
||||
`verify-all --with-e2e` 的输出为准;2026-10 需求收敛删掉了 `e2e-m2-join.py` 与 `e2e-m7-cleanup.py`),
|
||||
或一条命令 `python scripts/verify-all.py --with-e2e`
|
||||
- 脚本**自己造 MP 的 JWT**:读 `application-dev.yml` 的 `fill.jwt.secret` +
|
||||
`application.yml` 的 `issuer` 手搓 HS512 —— 不需要先登录
|
||||
@@ -105,9 +117,17 @@ python scripts/e2e-m3-file.py # 单跑一个;前提是后端 8080 在跑
|
||||
- 文档一致性:`python scripts/verify-docs.py`
|
||||
- 接线检查:`python scripts/check-wiring.py`(**"声明了但没接上"** ——
|
||||
权限码有没有地方校验、错误码有没有被引用、站内信模板有没有发送点、
|
||||
`bizType` 有没有被用过、菜单键 ↔ PC 端 `XtMenu` 是否对得上)。
|
||||
`bizType` 有没有被用过、菜单键 ↔ PC 端 `XtMenu` 是否对得上、
|
||||
**PC 端 `user.has('码')` 引用的权限码 ↔ `PermissionDef`**(第 ⑦ 项)、
|
||||
**模板里的 `<n-*>` 组件有没有漏 import**(第 ⑧ 项 —— 漏了会被当未知元素渲染,样式整块丢、
|
||||
编译与 lint 都不红)、
|
||||
**`.svg` 资产是不是合法 XML**(第 ⑨ 项 —— SVG 注释里出现连续两个减号就 not well-formed,
|
||||
图标变裂图而控制台一句话都不打;2026-10 换品牌标时连踩两次))。
|
||||
⚠️ 这一族错**编译 / lint / review 都不会红**(类在、表在、常量在、注释还写得很清楚),
|
||||
只有"从入口走一遍"才暴露 —— 所以把它固化成命令;有意不接线的写在脚本上方的例外表里(带理由)
|
||||
只有"从入口走一遍"才暴露 —— 所以把它固化成命令;有意不接线的写在脚本上方的例外表里(带理由)。
|
||||
⚠️ **检查器自己也会失效**:PC 端重搭后权限码的写法从 `permissions.includes` / `v-hasPerm`
|
||||
改成了 `user.has`,第 ⑦ 项曾因此"一个码都扫不到却判为通过"。
|
||||
加/改这类检查时**必须反向验证一次**(故意写错,它得红)
|
||||
- 一条命令跑完上面这些:`python scripts/verify-all.py --with-e2e`
|
||||
(9 项;**跳过项会如实列出来**,不是静默跳过)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 数据信息填报系统
|
||||
# 深学在线
|
||||
|
||||
> 职业技能等级认定**报名材料收集 SaaS**
|
||||
> **一期范围**:学校收集 + 自主认定(4 类材料,全部有实测样本)。社会收集 / 社会评价 / 专项职业能力后置二期
|
||||
> **范围(2026-10 收敛)**:只做「学校收集 + 自主认定」(4 类材料,全部有实测样本)。社会收集 / 社会评价 / 专项职业能力 **不做**(传 `collectMode=2`、`certType=2,3` 返回 `40044`);**不设二期**
|
||||
> 学生在微信小程序填报 · 班主任在手机初审 · 管理员在 PC 终审与导出
|
||||
> 最后更新:2026-09-28 | **状态:需求已冻结(客户全部确认,可开工)**
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
| **产品 / 需求** | `数据信息填报系统PRD.md` → `数据信息填报系统PRD-功能逻辑卷.md` | 前者含库表接口,后者是纯功能逻辑 |
|
||||
| **后端开发** | `03-接口设计.md` → `04-核心模块设计说明.md` → `db/schema-postgresql.sql` | 接口收发 → 引擎怎么做 → 建表 |
|
||||
| **前端开发** | `prototype/` → `03-接口设计.md` | 原型是视觉与交互的**唯一基准** |
|
||||
| **测试** | `06-验收测试清单.md` → `04-核心模块设计说明.md` 附录 B | 29 条 Golden 断言 + 85 条功能验收 |
|
||||
| **测试** | `06-验收测试清单.md` → `04-核心模块设计说明.md` 附录 B | 36 条 Golden 断言 + 111 条功能验收 |
|
||||
| **新加入的人** | `交接摘要.md` → 本文件 | 一份文档看清全部状态 |
|
||||
|
||||
### 全部文档
|
||||
@@ -38,7 +38,7 @@
|
||||
| 00 | `00-交付计划.md` | 8 KB | 7 项计划与进度追踪、风险跟踪 | ✅ |
|
||||
| 01 | `01-客户与机构确认函.md` | 18 KB | **23 项已全部确认** + 客户回执 + 落地动作清单 | ✅ **需求冻结** |
|
||||
| 02 | `02-需求追溯矩阵.md` | 10 KB | 115 条 FR ↔ 原型/库表/接口 的自审报告 | ✅ |
|
||||
| 03 | `03-接口设计.md` | 112 KB | **121 个接口**字段级定义、枚举全集、12 个 VO、**57 个错误码** | ✅ |
|
||||
| 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 条功能验收 + 签署表 | ✅ |
|
||||
@@ -46,7 +46,7 @@
|
||||
| — | `数据信息填报系统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 份) | 240 KB | **31 屏**交互原型:学生端 11 / 班主任端 9 / 管理端 11 | ✅ |
|
||||
| — | `prototype/`(4 份) | — | **29 屏**交互原型:学生端 10 / 班主任端 9 / 管理端 10 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
```
|
||||
┌─ 学生端(小程序)─┐ ┌─ 班主任端(手机)─┐ ┌─ 管理端(PC)─┐
|
||||
名单 ──→ 门禁核验 ──→ 填报 ──→ 卡片流初审 ──→ 终审 ──→ 导出规范包
|
||||
└─✗ 失败 → 申请加入名单 → 管理员审批 → 入册放行
|
||||
└─✗ 失败 → 只提示核对 + "请联系老师加你进名单"(老师手工加人)
|
||||
▲ ▲ ▲ ▲
|
||||
│ │ │ │
|
||||
报考范围 识别 命名+压缩 汇总 Excel
|
||||
@@ -95,9 +95,9 @@
|
||||
| 滑动交互 | 班主任端**只做右滑通过**,驳回必须点按钮 |
|
||||
| 批量通过 | **只带无问题项**,红黄一律排除 |
|
||||
| 动态表单 | 内置字段(固定列可统计)+ 扩展字段(`extras_json`) |
|
||||
| 名单外的人 | **申请加入名单 → 管理员逐条审批**;通道只解决"进名单",**不绕过门禁** |
|
||||
| 名单外的人 | **不做自助通道**(2026-10 撤销):由老师在任务里**手工加人**(名册追加导入),**不绕过门禁** |
|
||||
| 归属判定优先级 | **证件号反查名册 > OCR 内容 > 姓名(仅辅助,不做独立规则)** |
|
||||
| 联系方式 | **系统内不展示任何班主任/管理员联系方式**——学生本来就有;门禁失败只给「重新填写 / 申请加入名单」两条系统内出路 |
|
||||
| 联系方式 | **系统内不展示任何班主任/管理员联系方式**——学生本来就有;门禁失败只给「重新填写」一条系统内出路 + 一句"请联系老师" |
|
||||
| 校验位的作用 | **失败 = 不是真实号码 → 可自动改**;**通过 = 真人 → 改了就是改身份 → 必须阻断** |
|
||||
| 审批粒度 | 批量通过**不提供"全选"**,驳回**必须逐条填原因**(身份认定误操作代价极高) |
|
||||
|
||||
@@ -167,7 +167,7 @@ python check2.py docs/prototype/admin-web.html
|
||||
## 六·二、把系统跑起来(三条命令)
|
||||
|
||||
```bash
|
||||
# 1) 基础设施:PG 16 + Redis 7 + MinIO
|
||||
# 1) 基础设施:PG 16 + Redis 7(没有对象存储,MinIO 已删)
|
||||
cd deploy
|
||||
copy .env.example .env # 首次
|
||||
docker compose up -d
|
||||
@@ -194,12 +194,12 @@ python scripts/e2e-m0.py
|
||||
|
||||
```
|
||||
backend/ Java 21 + Spring Boot 4.1.1
|
||||
├── xt-common/ 统一响应 / 58 个错误码 / AES-GCM / HMAC / RSA
|
||||
└── xt-server/ 配置 / 鉴权 / 多租户拦截 / 业务模块
|
||||
├── xt-common/ 统一响应 / 54 个错误码 / AES-GCM / HMAC / RSA
|
||||
└── xt-server/ 配置 / 鉴权 / 多租户拦截 / 业务模块(26 张表,Flyway 已到 V19)
|
||||
deploy/ docker-compose + RSA 开发公钥
|
||||
scripts/ DDL 同步、登录助手、M0 验收测试
|
||||
frontend-pc/ (待建)Vue 3 + Element Plus
|
||||
frontend-mp/ (待建)uni-app
|
||||
scripts/ E2E 验收(37 个脚本)、接线检查、DDL 同步、图标抽取
|
||||
frontend-pc/ PC 管理端:**纯 Vue 3 + Naive UI**(pnpm dev → 5173)
|
||||
frontend-mp/ 学生端 / 教师端:uni-app(H5 与微信小程序同一套代码)
|
||||
```
|
||||
|
||||
### ⚠️ 搭环境前请先读
|
||||
@@ -214,8 +214,8 @@ frontend-mp/ (待建)uni-app
|
||||
| 阶段 | 内容 | 状态 |
|
||||
|---|---|---|
|
||||
| **需求** | 主 PRD(**125 FR** / 20 VAL / **20 张表**)+ 功能逻辑卷 | ✅ |
|
||||
| **设计** | 数据库 DDL(PG 16 实测)+ 接口设计(**121**)+ 模块设计(6) | ✅ |
|
||||
| **原型** | **31 屏**(学生 **11** / 班主任 9 / 管理 **11**)= 31 | ✅ |
|
||||
| **设计** | 数据库 DDL(PG 16 实测)+ 接口设计(**119**)+ 模块设计(6) | ✅ |
|
||||
| **原型** | **29 屏**(学生 **10** / 班主任 9 / 管理 **10**)= 29 | ✅ |
|
||||
| **计划** | **100 任务 / 387 人日 / 15 周**(一期范围已收窄) | ✅ |
|
||||
| **测试** | **36** 条 Golden 断言 + **111** 条功能验收 | ✅ |
|
||||
| **待外部** | ~~客户确认函(23 项)~~ | ✅ **已确认,按建议执行** |
|
||||
@@ -225,7 +225,7 @@ frontend-mp/ (待建)uni-app
|
||||
| 项 | 确认结论 |
|
||||
|---|---|
|
||||
| **A1** | ✅ 认定机构接受 300dpi 输出 |
|
||||
| **B1** | ✅ 增加「申请加入名单 → 管理员审批」通道(**唯一功能增量,已落地**) |
|
||||
| **B1** | ⚠️ **已回退**(2026-10 需求收敛):加入申请通道整体撤销(模块/表/接口/页面/错误码一并删除)。它是确认函里唯一的功能增量,建议补变更确认单 |
|
||||
| **B3** | ✅ 字段三分类:姓名/证件号/报考三项锁定,其余可核对修改并留痕 |
|
||||
|
||||
**其余 20 项均与现有设计一致,零改动。** 需求已冻结,可按 `05-开发任务拆解.md` 的 **M0** 开工。
|
||||
@@ -257,7 +257,7 @@ frontend-mp/ (待建)uni-app
|
||||
|---|---|
|
||||
| 数据库 | PostgreSQL 16(DDL 已实测通过) |
|
||||
| 开发顺序 | **M1 命名引擎 → M4 门禁 → M2 压缩 → M5 校验 → M3 识别 → M6 导出** |
|
||||
| 客户回执 | 全部 23 项按建议执行;唯一功能增量 B1(申请加入名单)已落地 |
|
||||
| 客户回执 | 全部 23 项按建议执行;唯一功能增量 B1(申请加入名单)**已于 2026-10 需求回退取消** |
|
||||
| 原型约束 | 单文件自包含、零外部依赖、内联 SVG 图标、**无 emoji** |
|
||||
| 最长的两个单点 | 压缩引擎 12 人日、打包引擎 10 人日(**加人无效**) |
|
||||
| 回归门禁 | 改动后必须复现 **15 项基线指标**(对应 29 条断言),偏差即回归 |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 数据信息填报系统 · 本地开发基础设施
|
||||
# 深学在线 · 本地开发基础设施
|
||||
#
|
||||
# 用法:
|
||||
# cd deploy
|
||||
|
||||
+28
-13
@@ -1,6 +1,6 @@
|
||||
# 交付计划与进度
|
||||
|
||||
> 项目:数据信息填报系统(填报通 / CollectFlow)
|
||||
> 项目:深学在线
|
||||
> 起始:2026-09-28
|
||||
> 状态:**进行中**
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
| — | 主 PRD(含库表设计、接口清单、15 条校验规则、18 张表 DDL) | V2.3 | ✅ |
|
||||
| — | 功能逻辑卷(纯功能运转逻辑,不含库表/接口/代码) | V1.9 | ✅ |
|
||||
| — | PostgreSQL 建表脚本 | — | ✅ **已在 PG 16 实测通过** |
|
||||
| — | 学生端原型(**11 屏**) | V1.2 | ✅ 含「申请加入名单」 |
|
||||
| — | 学生端原型(**10 屏**) | V1.3 | ✅(原 STEP 03B「申请加入名单」已于 2026-10 需求收敛删除) |
|
||||
| — | 班主任端原型(9 屏) | V1.1 | ✅ |
|
||||
| — | 管理端 PC 原型(**11 屏**) | V1.2 | ✅ 含表单设计器/材料规则/整理器/申请审批 |
|
||||
| — | 管理端 PC 原型(**10 屏**) | V1.3 | ✅ 含表单设计器/材料规则/角色与授权(整理器与申请审批两屏已于 2026-10 删除) |
|
||||
|
||||
---
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|---|---|---|---|---|
|
||||
| **P1** | **客户与认定机构确认函** | `01-客户与机构确认函.md` | 外部依赖最长。**5 项高优先级阻塞开发**(300dpi 能否接受、名单外的人怎么办、社会收集名单从哪来…),越早发出越早回来 | ✅ |
|
||||
| **P2** | **需求追溯矩阵** | `02-需求追溯矩阵.md` | 自审:把 PRD 需求 ↔ 原型屏 ↔ 库表 ↔ 接口 逐条对应,找出**说了没做/做了没说**的缺口。放在接口文档之前,避免接口漏做 | ✅ |
|
||||
| **P2.5** | **管理端补充原型**(矩阵审计追加) | 追加到 `admin-web.html` | 矩阵发现 3 个 P0/P1 复杂交互页面未出原型:**表单设计器**、**材料规则配置**、**历史整理器**。不画出来开发必理解偏 | ✅ |
|
||||
| ~~**P2.5**~~ | ~~**管理端补充原型**(矩阵审计追加)~~ | 追加到 `admin-web.html` | 矩阵发现 3 个 P0/P1 复杂交互页面未出原型:**表单设计器**、**材料规则配置**、~~**历史整理器**~~(该屏已于 2026-10 随模块删除)。不画出来开发必理解偏 | ✅ |
|
||||
|
||||
### 阶段二 · 补齐技术设计
|
||||
|
||||
@@ -58,10 +58,10 @@
|
||||
| 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 | **客户回执** | 客户确认全部 23 项按建议执行,**需求冻结**。识别出唯一功能增量 **B1(申请加入名单)**,其余 22 项零改动 |
|
||||
| 2026-09-28 | **客户回执**(⚠️ 唯一增量 B1 已于 2026-10 需求回退取消) | 客户确认全部 23 项按建议执行,**需求冻结**。识别出唯一功能增量 **B1(申请加入名单)**,其余 22 项零改动 |
|
||||
| 2026-09-28 | **D7 降级** | 重新评估「文件名识别真实准确率」这个待办:因**输出命名取自名册**、**归属靠证件号反查名册**、**识别有槽位兜底**、**失败一律放行**,该指标**不影响任何交付项**。降级为「上传页引导文案校准」——班主任在群里问两句即可,**取消样本索取**。至此**全部外部数据依赖清零** |
|
||||
| 2026-09-28 | **一期范围收窄** | 客户确认「**先做学校内的填报流程**」→ 一期只做**学校收集 + 自主认定**(4 类材料全部有实测样本);社会收集 / 社会评价 / 专项职业能力后置二期,**数据结构与枚举保留不启用**。**D4 因此不再是待办**。工期 −5 人日(392→387),关键路径不变仍 15 周 |
|
||||
| 2026-09-28 | **移除联系方式展示** | 客户指出"学生本来就有班主任联系方式,不需要系统里面体现"。删除 `GET /mp/roster/contact` 接口 + 门禁失败页「联系老师」按钮,改为只保留两条系统内动作(重新填写 / 申请加入名单)。**接口总数 122 → 121**。纯文案提示保留(不涉及联系方式数据) |
|
||||
| 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 条关键决策速查、目录结构与快速上手、数据基线、两条踩坑教训 |
|
||||
@@ -69,6 +69,7 @@
|
||||
| 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 的说明) |
|
||||
|
||||
---
|
||||
|
||||
@@ -79,12 +80,12 @@
|
||||
| 组 | 项数 | 需开发 | 结论 |
|
||||
|---|:--:|:--:|---|
|
||||
| A 认定机构 | 6 | **0** | 全部与现有设计一致(当初就按实测数据设计) |
|
||||
| B 校方业务 | 9 | **1** | 仅 **B1(申请加入名单通道)** 是功能增量 |
|
||||
| B 校方业务 | 9 | **1**(⚠️ B1 已于 2026-10 需求回退取消) | 仅 **B1(申请加入名单通道)** 是功能增量,该通道已于 2026-10 删除 |
|
||||
| C 商务法务 | 5 | 0 | C1 小程序资质按建议推进 + H5 保底 |
|
||||
| D 内部技术 | 7 | 0 | **D4 已后置二期**、**D7 已降级清除**;仅 D6(微信三条来源通道体积上限)需真机自测,**无外部依赖** |
|
||||
| D 内部技术 | 7 | 0 | **D4 已随"取消二期"作废**、**D7 已降级清除**;仅 D6(微信三条来源通道体积上限)需真机自测,**无外部依赖** |
|
||||
|
||||
**一期范围(客户确认后收窄)**:只做 **学校收集 + 自主认定**(4 类材料全部有实测样本)。
|
||||
社会收集 / 社会评价 / 专项职业能力后置二期,**数据结构与枚举保留不启用**。
|
||||
社会收集 / 社会评价 / 专项职业能力**不做**(2026-10 取消,不再是"后置"),**数据结构与枚举保留不启用**。
|
||||
⇒ **D4(社会评价样本)因此不再是待办**;工期 **392 → 387 人日**,关键路径不变仍 **15 周**。
|
||||
|
||||
**本次变更(V1)**:新增 **FR-509 系列**(10 条)+ **1 张表**(`collect_join_request`)+ **5 个接口** + **2 屏原型**。
|
||||
@@ -92,7 +93,7 @@
|
||||
| 指标 | 变更前 | 变更后 |
|
||||
|---|:--:|:--:|
|
||||
| FR 总数 | 115 | **125** |
|
||||
| 接口总数 | 117 | **121** |
|
||||
| 接口总数 | 117 | **121**(2026-10 收敛后为 **119**) |
|
||||
| 库表 | 19 | **20** |
|
||||
| VAL 规则 | 20 | **21**(新增 VAL-21 归属校验) |
|
||||
| 错误码 | 50 | **57** |
|
||||
@@ -108,6 +109,20 @@
|
||||
| # | 风险 | 影响 | 对策 |
|
||||
|---|---|---|---|
|
||||
| R1 | ~~客户迟迟不确认 P1 的 5 项高优先级问题~~ | — | ✅ **已解除**:客户确认全部 23 项按建议执行,需求已冻结 |
|
||||
| R2 | ~~强特征词表未在"社会评价"批次数据上标定~~ | — | ✅ **已消除**:一期范围收窄为「学校收集 + 自主认定」,**不涉及**那两类零样本材料(D4 已后置二期) |
|
||||
| R2 | ~~强特征词表未在"社会评价"批次数据上标定~~ | — | ✅ **已消除**(2026-10 取消二期) |
|
||||
| R3 | VLM 选型未定 | 中 | P4 中把 VLM 抽成**可替换接口**,不绑定供应商 |
|
||||
| R4 | **二期启动时缺少社会评价样本** | 中(二期) | 已在 PRD「一期范围声明」列出二期启动前必做 6 项;**不阻塞一期** |
|
||||
| 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 端重搭说明) |
|
||||
|
||||
+20
-15
@@ -1,9 +1,13 @@
|
||||
# 确认函 · 数据信息填报系统
|
||||
# 确认函 · 深学在线
|
||||
|
||||
> 文档用途:汇总需各方拍板的事项,**可直接转发**
|
||||
> 发出日期:2026-09-28
|
||||
> 回复期限:建议 **5 个工作日内**
|
||||
> 版本:V1.0
|
||||
> 版本:V1.1(2026-10 追加需求收敛说明)
|
||||
>
|
||||
> ⚠️ **2026-10 变更提示**:客户随后要求收敛范围 —— **B1(本函唯一的功能增量)已整体撤销**,
|
||||
> 且**不再设计任何二期功能**。本函正文保留当时原文(它是"当时确认过什么"的记录),
|
||||
> 但凡出现"二期 / 后置 / 待实施"的表述,**一律读作"不做"**。详见 `docs/交接摘要.md` **§一·三十六**。
|
||||
|
||||
---
|
||||
|
||||
@@ -186,7 +190,7 @@ B1 选 A,但希望增加审批环节
|
||||
| | |
|
||||
|---|---|
|
||||
| **问题** | 是否需要把数据直接推送到机构系统,而不是导出文件包? |
|
||||
| **建议方案** | ✅ **建议一期不做**(导出标准包已可满足),**但预留开放接口**,二期按需启用 |
|
||||
| **建议方案** | ✅ **不做**(导出标准包已可满足),**但预留开放接口**,将来按需启用 |
|
||||
| **回复** | ☐ 一期不做 ☐ 一期就必须对接(请提供对方接口文档) |
|
||||
|
||||
### B9. 社会收集是否需公安实名核验?
|
||||
@@ -257,7 +261,7 @@ B1 选 A,但希望增加审批环节
|
||||
**建议**:一期按 H5 单腿上线(学生扫 H5 码即可填报),小程序作为第二条腿,
|
||||
在开放平台绑定完成后一起开。**这样不必等它,但必须知道它挡着什么。**
|
||||
|
||||
回复:☐ 现在就办(我方可提供绑定步骤) ☐ 一期只上 H5,二期再办 ☐ 其他 ______
|
||||
回复:☐ 现在就办(我方可提供绑定步骤) ☐ 只上 H5,`union_id` 缓办 ☐ 其他 ______
|
||||
|
||||
### E2. D3 门禁限流阈值口径已更新(备案,无需回复)
|
||||
|
||||
@@ -282,18 +286,19 @@ B1 选 A,但希望增加审批环节
|
||||
> 复测 250 人 **250/250**、500 人 **500/500**;失败侧两个计数器原样未动
|
||||
> (防枚举的强度没有降低)。详见《09-部署与运维》§9.3。
|
||||
|
||||
### E3. 二期(社会收集 / 社会评价 / 专项职业能力)的启动时点
|
||||
### E3. ~~二期(社会收集 / 社会评价 / 专项职业能力)的启动时点~~ —— **已取消**(2026-10)
|
||||
|
||||
一期范围已按约定锁死为「学校收集 + 自助认定」(其他模式一律返回 `40044`)。
|
||||
但**二期什么时候启动**会影响两件现在就能做、也可以等的事:
|
||||
> ⚠️ **2026-10 已确定:不做二期**,本节作废(保留原文供追溯)。原话是:
|
||||
> 但**二期什么时候启动**会影响两件现在就能做、也可以等的事:
|
||||
|
||||
| 事项 | 如果二期在 3 个月内启动 | 如果更晚 |
|
||||
| ~~事项~~ | ~~如果二期在 3 个月内启动~~ | ~~如果更晚~~ |
|
||||
|---|---|---|
|
||||
| D4 材料识别强特征词表标定 | 建议现在就在社会评价批次数据上标定 | 可现在只做学校批次的标定 |
|
||||
| D5 一寸照片形态阈值标定 | 同上 | 同上 |
|
||||
| 二期审核流与会话设计 | 建议现在出一版概要设计,避免一期代码挡住它 | 一期交付后再说 |
|
||||
| ~~二期审核流与会话设计~~ | **不做**(2026-10 取消二期) | — |
|
||||
|
||||
回复:☐ 二期预计 ____ 启动 ☐ 暂无计划
|
||||
回复:☑ **暂无计划(2026-10 已确认取消二期)**
|
||||
|
||||
### E4. 国标职业库数据的来源(字典页「职业(国标)」目前是空的)
|
||||
|
||||
@@ -342,7 +347,7 @@ B1 选 A,但希望增加审批环节
|
||||
|
||||
| # | 确认结论 | 落地动作 | 状态 |
|
||||
|:--:|---|---|:--:|
|
||||
| **B1** | **选 A:增加「申请加入名单 → 管理员审批」通道** | ⚠️ **需开发**:新增 FR-509 系列 + 1 张表 + 4 个接口 + 2 屏原型 | 🔨 **待实施** |
|
||||
| ~~**B1**~~ | ~~选 A:增加「申请加入名单 → 管理员审批」通道~~ → **已撤销**(2026-10 需求回退):该通道、表、接口、学生端页面与错误码已全部删除 | ⚠️ **需求回退,建议补变更确认单**:名单外的人改由老师在任务里**手工加人**;系统内不展示联系方式 | ❌ **已撤销** |
|
||||
| B2 | 社会收集名单**由报名点/机构维护预报名名单**,导入后开放;**填报期内可随时追加** | 已一致:FR-508 追加导入;补充说明写入 PRD §5.5 | ✅ 已一致 |
|
||||
| B3 | **字段三分类**:姓名/证件号/报考三项锁定,其余可核对修改并留痕 | 已一致:FR-502b + `changed_fields` 机制 | ✅ 已一致 |
|
||||
| B4 | **不加手机号验证码** | 无需改动(当前门禁即姓名+证件号) | ✅ 已一致 |
|
||||
@@ -352,8 +357,8 @@ B1 选 A,但希望增加审批环节
|
||||
| B8 | **一期不对接机构系统,预留开放接口** | 已一致:§14 开放接口已设计但不启用 | ✅ 已一致 |
|
||||
| B9 | **不做公安实名核验** | 无需改动 | ✅ 已一致 |
|
||||
|
||||
> **结论:B 组 9 项中 8 项零改动,仅 B1 需开发。**
|
||||
> B1 的建议是我方主动提出的(原需求只有"只提示"),客户采纳后**这是一次功能增量**,已按 §变更记录 追加设计与排期。
|
||||
> **结论:B 组 9 项中 8 项零改动;B1 曾是我方主动提出的功能增量、客户采纳后已开发,但于 2026-10 随需求收敛整体撤销。**
|
||||
> ⚠️ 它是本函里**唯一的功能增量**,因此这次撤销属**需求回退**,建议补一张变更确认单。
|
||||
|
||||
### C 组 · 商务法务(5 项)
|
||||
|
||||
@@ -372,7 +377,7 @@ B1 选 A,但希望增加审批环节
|
||||
| D1 | OCR 引擎选型(腾讯云/百度/本地) | ⏳ M2 结束前定 |
|
||||
| D2 | VLM 抽成可替换接口,不绑定供应商 | ✅ 已按此设计(04 文档 M3 §3.6) |
|
||||
| D3 | 门禁限流:openid 10 次/小时、IP 20 次/分钟 | ✅ 已实现口径 |
|
||||
| D4 | **需索取「社会评价」批次的原始材料样本** | ✅ **已后置为二期**(一期只做学校收集 + 自主认定,不涉及社会评价材料) |
|
||||
| D4 | **需索取「社会评价」批次的原始材料样本** | ✅ **已作废**(2026-10 取消二期,不再需要该样本) |
|
||||
|
||||
#### D4 补充说明:为什么需要「社会评价」的样本
|
||||
|
||||
@@ -404,8 +409,8 @@ B1 选 A,但希望增加审批环节
|
||||
| 退而求其次 | **10~20 份**,但**必须覆盖「学信网备案表」和「各类佐证材料」**(这两类现在零样本) |
|
||||
| 特别注意 | 记录**材料来源与格式**——社会人员可能直接从学信网下载 **PDF**(而非拍照/扫描)。若是,`file_types` 白名单、预览、压缩策略都要跟着调整 |
|
||||
|
||||
> ✅ **已后置为二期**(2026-09-28 客户确认):一期范围收窄为「**学校收集 + 自主认定**」,材料只有 4 类(一寸照片 / 身份证 / 学籍表 / 工作证明),**全部有 112 个真实文件实测**。
|
||||
> 因此 D4 **不再是待办、也不阻塞任何里程碑**。待二期做「社会评价」时再索取即可。
|
||||
> ✅ **已作废**(2026-10 取消二期;2026-09-28 曾收窄为「**学校收集 + 自主认定**」),材料只有 4 类(一寸照片 / 身份证 / 学籍表 / 工作证明),**全部有 112 个真实文件实测**。
|
||||
> 因此 D4 **不是待办、也不阻塞任何里程碑**;由于二期已取消,**该样本不再需要索取**。
|
||||
| D5 | 一寸照片形态阈值用真实样本标定 | ✅ 已有实测(984×1378,5:7) |
|
||||
| D6 | **微信三条来源通道体积上限需真机实测** | ⏳ 待测 |
|
||||
| D7 | ~~文件名规则在"学生原始乱名"上测真实准确率~~ | ✅ **已降级为文案校准**(不需样本) |
|
||||
|
||||
+10
-10
@@ -2,7 +2,7 @@
|
||||
|
||||
> 用途:把 PRD 的每条需求对上「**原型 / 库表 / 接口 / 验收**」四个交付面,找出**说了没做**和**做了没说**。
|
||||
> 生成日期:2026-09-28
|
||||
> 审计范围:**125 条 FR**(92 P0 / 30 P1 / 3 P2)+ **21 条 VAL 校验规则** + **121 个接口** + **20 张表**
|
||||
> 审计范围:**125 条 FR**(92 P0 / 30 P1 / 3 P2)+ **21 条 VAL 校验规则** + **119 个接口**(2026-10 收敛后的口径;原 129 = 121 + 角色与授权 8)+ **20 张表**
|
||||
> 结论:**主流程全覆盖。第三节的 10 个接口缺口已于 P3 补齐(共新增 21 个接口);配置类页面缺口见 3.2**
|
||||
|
||||
---
|
||||
@@ -12,7 +12,7 @@
|
||||
| 模块 | 条数 | P0 | 库表 | 接口 | 原型 | 判定 |
|
||||
|---|:--:|:--:|:--:|:--:|:--:|:--:|
|
||||
| FR-100 租户与组织 | 5 | 5 | ✅ | ✅ | ❌ | ⚠️ 缺平台运营端原型 |
|
||||
| FR-200 账号与角色 | 6 | 5 | ✅ | ✅ | ❌ | ⚠️ 缺登录页与用户管理页 |
|
||||
| FR-200 账号与角色 | 6 | 5 | ✅ | ✅ | ⚠️ | ⚠️ 缺登录页与用户管理页;**FR-205 角色自定义已落地**(`docs/03` §4.12 / §5.10,页面 `frontend-pc/src/views/role/`;2026-10 起按**一期功能**计,不再叫"二期")——其中「数据权限可配」**不做** |
|
||||
| FR-300 收集任务 | 12 | 9 | ✅ | ✅ | ⚠️ | ✅ 暂停接口已补(P3) |
|
||||
| FR-400 动态表单 | 8 | 6 | ✅ | ✅ | ❌ | ⚠️ **缺表单设计器原型** |
|
||||
| FR-500 名单管理 | 24 | 21 | ✅ | ✅ | ✅ | ✅ **全覆盖**(含客户确认新增的 FR-509 系列 10 条) |
|
||||
@@ -23,7 +23,7 @@
|
||||
| FR-1100 数据看板 | 5 | 2 | ✅ | ✅ | ✅ | ✅ **全覆盖** |
|
||||
| FR-1200 系统管理 | 6 | 5 | ⚠️ | ✅ | ❌ | ⚠️ 接口已补(P3),原型仍缺 |
|
||||
| FR-1300 SaaS 计费 | 4 | 2 | ⚠️ | ⚠️ | ❌ | ⚠️ 缺平台运营端 |
|
||||
| FR-1400 历史整理器 | 6 | 0 | ✅ | ❌ | ❌ | ❌ **三面全缺**(P1,本轮未做) |
|
||||
| ~~FR-1400 历史整理器~~ | ~~6~~ | ~~0~~ | — | — | — | **已取消**(2026-10 需求收敛:模块/表/接口/页面全部删除) |
|
||||
| **FR-700 校验引擎** | — | — | ✅ | ✅ | ✅ | ✅ 以 VAL-01~21 实现,三端均有体现 |
|
||||
|
||||
**图例**:✅ 完整覆盖 ⚠️ 部分覆盖 ❌ 未覆盖
|
||||
@@ -55,7 +55,7 @@
|
||||
| # | 缺口 | 对应需求 | 影响 | 建议接口 |
|
||||
|:--:|---|---|---|---|
|
||||
| I1 | **报考范围读取与确认** | FR-307~311 | 管理端④整屏无数据来源 | `GET/PUT /api/v1/tasks/{id}/plan-scope` |
|
||||
| I2 | **历史数据整理器** | FR-1401~1406 | §5.14 六条需求**完全无接口** | `POST /api/v1/cleanup/jobs` 等 5 个 |
|
||||
| ~~I2~~ | ~~**历史数据整理器**~~ | — | **已取消**(2026-10) | — |
|
||||
| I3 | **字典管理** | FR-1201 | 学历/等级/补考科目等下拉项无法维护 | `GET/POST/PUT /api/v1/dicts` |
|
||||
| I4 | **操作日志** | FR-1202 | 审计留痕无法查询 | `GET /api/v1/logs/operations` |
|
||||
| I5 | **登录日志** | FR-1203 | 同上 | `GET /api/v1/logs/logins` |
|
||||
@@ -75,14 +75,14 @@
|
||||
|:--:|---|---|:--:|---|
|
||||
| P1 | **表单设计器** | FR-401~408 | 高 | ⚠️ **必须补**——拖拽式字段编排是 P0,且交互复杂,光靠文字描述开发必理解偏 |
|
||||
| P2 | **材料规则配置**(命名模板编辑器) | FR-601~609 | 高 | ⚠️ **建议补**——管理端②只做了"试跑",配置界面本身没画 |
|
||||
| P3 | 历史数据整理器 | FR-1401~1406 | 中 | 建议补(差异化获客功能,客户演示时最吸睛) |
|
||||
| ~~P3~~ | ~~历史数据整理器~~ | — | — | **已取消**(2026-10 需求收敛) |
|
||||
| P4 | 登录页 | FR-201 | 低 | 可略 |
|
||||
| P5 | 组织与用户管理 | FR-103、203~206 | 中 | 可略(标准 CRUD) |
|
||||
| P6 | 任务列表与详情 | FR-301~306 | 中 | 建议补简版(克隆、二维码、暂停) |
|
||||
| P7 | 通知模板管理 | FR-1001~1002 | 低 | 可略 |
|
||||
| P8 | 字典管理 | FR-1201 | 低 | 可略 |
|
||||
| P9 | 操作日志查询 | FR-1202~1203 | 低 | 可略 |
|
||||
| P10 | 平台运营端(租户/套餐/计费) | FR-101~102、1301~1304 | 中 | 建议二期做(一期客户是学校,不关心平台运营) |
|
||||
| P10 | 平台运营端(租户/套餐/计费) | FR-101~102、1301~1304 | 中 | **不做**(2026-10:不再设计二期;平台运营端只保留租户开通与用量) |
|
||||
|
||||
### 3.3 需求条目本身的核查
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
规则引擎(命名/压缩/识别) ████████████████████ 100%
|
||||
配置类页面 ██████░░░░░░░░░░░░░░ 30%
|
||||
平台运营 ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||
历史整理器 ░░░░░░░░░░░░░░░░░░░░ 0%(仅库表)
|
||||
~~历史整理器~~ ░░░░░░░░░░░░░░░░░░░░ 已取消(2026-10)
|
||||
```
|
||||
|
||||
### 必须行动的 3 项
|
||||
@@ -136,7 +136,7 @@
|
||||
|
||||
| # | 任务 | 产出 | 理由 |
|
||||
|---|---|---|---|
|
||||
| **P2.5** | **管理端补充原型**:表单设计器 + 材料规则配置 + 整理器 | 补充到 `admin-web.html` | 追溯矩阵发现的缺口,三者都是**复杂交互、不画会理解偏**的页面 |
|
||||
| **P2.5** | **管理端补充原型**:表单设计器 + 材料规则配置 + ~~整理器~~(该屏已于 2026-10 删除) | 补充到 `admin-web.html` | 追溯矩阵发现的缺口,三者都是**复杂交互、不画会理解偏**的页面 |
|
||||
|
||||
---
|
||||
|
||||
@@ -156,9 +156,9 @@
|
||||
| FR-1100 | FR-1101 ~ 1105 | 5 | 看板 |
|
||||
| FR-1200 | FR-1201 ~ 1206 | 6 | 系统管理 |
|
||||
| FR-1300 | FR-1301 ~ 1304 | 4 | 计费 |
|
||||
| FR-1400 | FR-1401 ~ 1406 | 6 | 整理器 |
|
||||
| ~~FR-1400~~ | ~~FR-1401 ~ 1406~~ | ~~6~~ | **已取消**(2026-10) |
|
||||
| **合计** | | **125** | **92 P0 / 30 P1 / 3 P2** |
|
||||
|
||||
**校验规则**:VAL-01 ~ VAL-21(21 条,编号连续;18~20 为 P4 补充,**21 为客户确认后补充的归属校验**)
|
||||
**接口**:**121 个已定义**(原 96 + 补齐 21 + 客户确认新增 5 − 客户要求移除 1,见 `03-接口设计.md`)
|
||||
**接口**:**119 个已定义**(2026-10 收敛:原 129 = 96 + 补齐 21 + 客户确认新增 5 + 角色与授权 8 − 移除 1;现减去**加入申请 5 个**与**历史整理器 5 个** = **119**,补齐 21 → 16),见 `03-接口设计.md` §4.12 / §5.10
|
||||
**库表**:**20 张**完整 DDL + 4 张"从略"标准表
|
||||
|
||||
+181
-362
@@ -14,16 +14,16 @@
|
||||
| §1 | 通用约定(域名、鉴权、响应、分页、幂等、限流、脱敏) |
|
||||
| §2 | 枚举字典全集 |
|
||||
| §3 | 通用数据结构(Schema 定义) |
|
||||
| §4 | 接口总览(**121 个**) |
|
||||
| §4 | 接口总览(**119 个**) |
|
||||
| §5 | 认证与账号(13) |
|
||||
| §6 | 组织与租户(15) |
|
||||
| §7 | 任务与配置(30) |
|
||||
| §8 | 名册(12) |
|
||||
| §7 | 任务与配置(25) |
|
||||
| §8 | 名册(7) |
|
||||
| §9 | 填报端 · 小程序(16) |
|
||||
| §10 | 班主任移动端(10) |
|
||||
| §11 | 审核(9) |
|
||||
| §12 | 校验 / 导出 / 通知(12) |
|
||||
| §13 | **本次补齐的缺口接口(21)** |
|
||||
| §13 | **本次补齐的缺口接口(16)** |
|
||||
| §14 | 开放接口 · 供认定机构对接(4) |
|
||||
| §15 | 错误码全集 |
|
||||
|
||||
@@ -212,6 +212,12 @@
|
||||
|
||||
> ⚠️ **`WORK_PROOF` 的名称陷阱**:按机构模板,**「职业技能等级个人申报表」的规范文件名是「工作证明」**。这是机构既有习惯,不要"修正"。
|
||||
|
||||
> ⚠️ **一期实际只有 4 类材料**:`PHOTO1` / `IDCARD` / `SCHOOL_ROLL` / `WORK_PROOF`。
|
||||
> `XJW_BA` 与 `OTHER_PROOF` 是需求收敛(2026-10)**之前**留下的编码 ——
|
||||
> **历史数据读得到、但"新增材料"选不到**(`GET /api/v1/material-catalog` 的可选项不含它们),
|
||||
> 内置材料模板也只剩 `TPL_SCHOOL_ROLL` 一个(原 `TPL_SOCIAL` / `TPL_ZHUANXIANG` 已删)。
|
||||
> 这样处理是为了**不让历史任务里的材料项悬空**:删编码会让已配的任务读不出材料名。
|
||||
|
||||
### 2.2 收集模式 `collectMode`
|
||||
|
||||
| 值 | 含义 | 审核流 |
|
||||
@@ -266,7 +272,7 @@
|
||||
| 枚举 | 取值 |
|
||||
|---|---|
|
||||
| `userType` | `1` 平台运营 / `2` 学校管理员 / `3` 班主任 / `4` 学生 |
|
||||
| `roleCode` | `PLATFORM_ADMIN` / `SCHOOL_ADMIN` / `HEAD_TEACHER` / `DEPUTY_TEACHER` / `STUDENT` / `AUDITOR` |
|
||||
| `roleCode` | `PLATFORM_ADMIN` / `SCHOOL_ADMIN` / `HEAD_TEACHER` / `STUDENT` |
|
||||
| `taskStatus` | `0` 草稿 / `1` 收集中 / `2` 已截止 / `3` 已归档 / `4` **已暂停** (与后端 `CollectTask.STATUS_*` 及 DDL 列注释一致)|
|
||||
| `rosterSubmitStatus` | `0` 未提交 / `1` 已提交 / `2` 已通过 / `3` 已驳回 |
|
||||
| `fileValidateStatus` | `0` 待校验 / `1` 通过 / `2` 警告 / `3` 不通过 / `4` 待人工 |
|
||||
@@ -275,10 +281,8 @@
|
||||
| `detectSource` | `1` 文件名 / `2` 形态 / `3` OCR 内容 / `4` VLM / `5` 用户指定 |
|
||||
| `compressProfile` | `P1` 证件照 / `P2` 证件翻拍 / `P3` A4 扫描件 / `P4` 截图 / `P5` 兜底 |
|
||||
| `notifyChannel` | `1` 微信订阅消息 / `2` 短信 / `3` 公众号模板 |
|
||||
| `notifyBizType` | `1` 任务发布 / `2` 临期提醒 / `3` 驳回通知 / `4` 审核通过 / `5` **催报** / `6` **导出完成** / `7` 加入名单审批 |
|
||||
| `notifyBizType` | `1` 任务发布 / `2` 临期提醒 / `3` 驳回通知 / `4` 审核通过 / `5` **催报** / `6` **导出完成** |
|
||||
| `exportStatus` | `0` 排队中 / `1` 打包中 / `2` 已完成 / `3` 失败 / `4` 已过期 |
|
||||
| `cleanupSourceType` | `1` 压缩包 / `2` 文件夹 / `3` 云盘链接 |
|
||||
| `cleanupStatus` | `0` 排队 / `1` 解析中 / `2` 处理中 / `3` 已完成 / `4` 失败 |
|
||||
|
||||
> ✅ **`notifyBizType` 的 `5`/`6` 曾经在本文件内部对调过(已修正,2026-09-30 客户确认)**:
|
||||
> 本表旧版写「`5` 导出完成 / `6` 催报」,与 §13.5 消息中心示例、落库值
|
||||
@@ -487,7 +491,7 @@
|
||||
| `vlmCalled` | bool | `vlm_called` | 是否调用过 VLM(**成本监控用**) |
|
||||
| `validateStatus` | int | `validate_status` | 见 §2.7 `fileValidateStatus` |
|
||||
| `issues` | array | — | 该文件的 `ValidationIssueVO[]` |
|
||||
| `source` | int | `source` | `1` 本人上传 / `2` 班主任代传 / `3` 管理员代传 / `4` 整理器导入 |
|
||||
| `source` | int | `source` | `1` 本人上传 / `2` 班主任代传 / `3` 管理员代传 / `4` 整理器导入(**该模块已取消,仅历史数据**) |
|
||||
| `rawExpireTime` | string | `raw_expire_time` | **原图过期时间(默认 30 天)** |
|
||||
|
||||
> ✅ **`collect_file.source` 的口径曾经有两套,已确认(2026-09-30 客户确认)**:本表旧版写
|
||||
@@ -573,7 +577,6 @@
|
||||
| `UserVO` | `id` `username` `realName` `phone` `orgId` `orgName` `roleCodes[]` `userType` `status` `lastLoginTime` |
|
||||
| `ExportTaskVO` | `id` `taskId` `exportName` `scopeJson` `splitSizeMb` `desensitize` `status` `progress` `fileCount` `fileSizeMb` `resultFiles[]{ name, sizeMb, url }` `errorMsg` `expireTime` `finishTime` |
|
||||
| `NotifyRecordVO` | `id` `taskId` `channel` `bizType` `receiverId` `receiverName` `receiverRole` `templateCode` `content` `status` `failReason` `sendTime` |
|
||||
| `CleanupJobVO` | `id` `jobName` `sourceType` `sourcePath` `targetTaskId` `status` `progress` `totalFiles` `resultPath` `reportJson` `finishTime` |
|
||||
| `DictItemVO` | `id` `dictCode` `dictName` `itemCode` `itemName` `sortNo` `parentItemCode` `status` `builtin` |
|
||||
| `MessageVO` | `id` `bizType` `title` `content` `read` `linkType` `linkId` `createTime` |
|
||||
|
||||
@@ -581,7 +584,10 @@
|
||||
|
||||
## §4 接口总览
|
||||
|
||||
> 合计 **121 个**:PRD §8 已定义 96 个 + 追溯矩阵补齐 **21 个** + 客户确认新增 **5 个**,减去客户要求移除的联系方式接口 1 个(标 ⭐)。补齐项见 §4.9 索引,客户确认新增见 §8.7。
|
||||
> 合计 **119 个**:PRD §8 已定义 96 个 + 追溯矩阵补齐 **16 个** + **「角色与授权」8 个**,减去客户要求移除的联系方式接口 1 个(标 ⭐)。
|
||||
> 补齐项见 §4.9 索引,角色与授权见 §4.12 与 §5.10。
|
||||
> ⚠️ **需求收敛(2026-10)的减法**:原「客户确认新增 5 个」(加入申请,确认函 B1)**整条通道取消**,
|
||||
> 原「补齐 21 个」中的「历史整理器 5 个」**随模块删除** → 21 − 5 = 16,故 96 + 16 + 8 − 1 = **119**。
|
||||
> 「权限」列:`公开` / `学生` / `教师` / `管理` / `运营`(平台方)。
|
||||
> 「修补」列指该接口是补齐追溯矩阵审计发现的缺口。
|
||||
|
||||
@@ -589,14 +595,15 @@
|
||||
|---|:--:|
|
||||
| 认证与账号 | 13 |
|
||||
| 组织与租户 | 15 |
|
||||
| 任务与配置 | 30 |
|
||||
| 名册 | 12 |
|
||||
| 任务与配置 | 25 |
|
||||
| 名册 | 7 |
|
||||
| 填报端 · 小程序 | 16 |
|
||||
| 班主任移动端 | 10 |
|
||||
| 审核 | 9 |
|
||||
| 校验 / 导出 / 通知 | 12 |
|
||||
| 开放接口 | 4 |
|
||||
| **合计** | **121** |
|
||||
| ⭐ 角色与授权 | 8 |
|
||||
| **合计** | **119** |
|
||||
|
||||
### 4.1 认证与账号(13)
|
||||
|
||||
@@ -636,7 +643,7 @@
|
||||
| ⭐ GET | `/logs/operations` | **操作日志** | 管理 |
|
||||
| ⭐ GET | `/logs/logins` | **登录日志** | 管理 |
|
||||
|
||||
### 4.3 任务与配置(30)
|
||||
### 4.3 任务与配置(25)
|
||||
|
||||
| | 路径 | 说明 | 权限 |
|
||||
|---|---|---|---|
|
||||
@@ -665,13 +672,8 @@
|
||||
| GET | `/tasks/{id}/qrcode` | 填报二维码 | 管理 |
|
||||
| GET | `/rule-templates` | 规则模板列表 | 管理 |
|
||||
| POST | `/rule-templates/{id}/apply` | 应用模板 | 管理 |
|
||||
| ⭐ POST | `/cleanup/jobs` | **创建整理任务** | 管理 |
|
||||
| ⭐ GET | `/cleanup/jobs` | **整理任务列表** | 管理 |
|
||||
| ⭐ GET | `/cleanup/jobs/{id}` | **整理进度** | 管理 |
|
||||
| ⭐ GET | `/cleanup/jobs/{id}/report` | **体检报告** | 管理 |
|
||||
| ⭐ POST | `/cleanup/jobs/{id}/import` | **结果导入为正式任务** | 管理 |
|
||||
|
||||
### 4.4 名册(12)
|
||||
### 4.4 名册(7)
|
||||
|
||||
| | 路径 | 说明 | 权限 |
|
||||
|---|---|---|---|
|
||||
@@ -682,11 +684,6 @@
|
||||
| GET | `/tasks/{id}/roster/template` | 下载导入模板 | 管理 |
|
||||
| POST | `/roster/{rid}/notify` | 定向催报 | 管理/教师 |
|
||||
| POST | `/tasks/{id}/roster/notify-unsubmitted` | 一键催报未提交者 | 管理/教师 |
|
||||
| ⭐ POST | `/mp/roster/join-request` | **提交加入申请** | 学生 |
|
||||
| ⭐ GET | `/mp/roster/join-request` | **我的申请状态** | 学生 |
|
||||
| ⭐ GET | `/roster/join-requests` | **申请待办列表** | 管理 |
|
||||
| ⭐ POST | `/roster/join-requests/{id}/approve` | **审批通过** | 管理 |
|
||||
| ⭐ POST | `/roster/join-requests/{id}/reject` | **审批驳回** | 管理 |
|
||||
|
||||
### 4.5 填报端 · 小程序(16)
|
||||
|
||||
@@ -757,10 +754,11 @@
|
||||
| GET | `/notify/records` | 通知记录 | 管理 |
|
||||
| POST | `/callback/wechat/msg` | 微信订阅消息回调 | 公开 |
|
||||
|
||||
### 4.9 ⭐ 本次新增接口索引(21)
|
||||
### 4.9 ⭐ 本次新增接口索引(16)
|
||||
|
||||
> 本表是**索引**,不是额外接口——其中 5 个已在上方 §4.3 / §4.7 列出,**不计入总数**。
|
||||
> 表内 18 行覆盖 **21 个接口**(含 GET/PUT、GET/POST 合并行)。
|
||||
> 表内 13 行覆盖 **16 个接口**(含 GET/PUT、GET/POST 合并行)。
|
||||
> ⚠️ 需求收敛(2026-10)后,原表里的**历史整理器 5 个**随模块删除,故 21 − 5 = **16**。
|
||||
|
||||
| | 路径 | 说明 | 详情节 |
|
||||
|---|---|---|---|
|
||||
@@ -768,11 +766,6 @@
|
||||
| ⭐ | `/tasks/{id}/pause` `/resume` | 暂停 / 恢复任务 | §7.4 |
|
||||
| ⭐ | `/audit/submissions/{id}/revoke` | 撤回已审 | §11.4 |
|
||||
| ⭐ | `/mp/files/{fileId}/process` | **文件规范化总入口**(原仅在 §8.9 详述,未登记进清单) | §9.6 |
|
||||
| ⭐ | `/cleanup/jobs` | 创建整理任务 | §13.2 |
|
||||
| ⭐ | `/cleanup/jobs` | 整理任务列表 | §13.2 |
|
||||
| ⭐ | `/cleanup/jobs/{id}` | 整理进度 | §13.2 |
|
||||
| ⭐ | `/cleanup/jobs/{id}/report` | 体检报告 | §13.2 |
|
||||
| ⭐ | `/cleanup/jobs/{id}/import` | 结果导入为正式任务 | §13.2 |
|
||||
| ⭐ | `/dicts` | 字典列表 | §13.3 |
|
||||
| ⭐ | `/dicts/{code}/items` | 字典项(GET / POST) | §13.3 |
|
||||
| ⭐ | `/dicts/items/{id}` | 修改字典项 | §13.3 |
|
||||
@@ -783,7 +776,7 @@
|
||||
| ⭐ | `/mp/teacher/messages` | 教师消息中心 | §13.5 |
|
||||
| ⭐ | `/mp/subscribe` | 订阅消息额度登记 | §13.6 |
|
||||
|
||||
**新增 21 个的分布**
|
||||
**新增 16 个的分布**
|
||||
|
||||
| 归属 | 数量 | 明细 |
|
||||
|---|:--:|---|
|
||||
@@ -791,7 +784,6 @@
|
||||
| 任务状态 | 2 | pause / resume |
|
||||
| 审核 | 1 | revoke |
|
||||
| 文件处理 | 1 | process(登记遗漏) |
|
||||
| 历史整理器 | 5 | cleanup 全套 |
|
||||
| 字典 | 4 | dicts |
|
||||
| 日志 | 2 | operations / logins |
|
||||
| 消息中心 | 3 | 学生 2 + 教师 1 |
|
||||
@@ -806,33 +798,35 @@
|
||||
| GET | `/open/v1/tasks/{id}/files` | 批量获取文件签名 URL |
|
||||
| POST | `/open/v1/callback/push` | 推送审核结果到机构系统 |
|
||||
|
||||
### 4.11 ⭐ 客户确认后新增(5)
|
||||
### 4.11 ⭐ 加入申请(已撤销 · 0 个)
|
||||
|
||||
> 来源:确认函 **B1**(客户确认采用"申请加入名单 → 管理员审批"通道)。这是本次唯一的功能增量。
|
||||
> 详述见 **§8.7**。
|
||||
> ⚠️ **本节原为「客户确认后新增 5 个」**(确认函 **B1**:申请加入名单 → 管理员审批)。
|
||||
> **需求收敛(2026-10)后整条通道取消**:模块 `module/join`、5 个接口、`join:audit` 权限码、
|
||||
> `collect_join_request` 表、学生端「申请加入名单」页与 5 个专用错误码
|
||||
> (`40037` / `40038` / `40039` / `40040` / `40042`)**已全部删除**。
|
||||
>
|
||||
> **门禁失败后的口径**:只保留「**重新填写**」一个系统内动作 + 一句纯文案提示
|
||||
> "如确认已报名,请联系老师把你加入名单"。名单外的人由老师在任务里**手工加人**(`FR-508` 追加导入),
|
||||
> **不展示任何联系方式**(铁律:联系方式不进系统)。
|
||||
>
|
||||
> ⚠️ **治理提示**:B1 是确认函里**唯一的功能增量**,本次属**需求回退**,建议补一张变更确认单。
|
||||
|
||||
### 4.12 ⭐ 角色与授权(8)
|
||||
|
||||
> 来源:**FR-205「角色自定义与数据权限」**。内置角色 **4 个**(平台运营 / 学校管理员 / 班主任 / 学生,
|
||||
> 权限由代码 `BuiltinRole` 决定、界面只读),租户可另建**自定义角色**并配置权限码与可见菜单。
|
||||
> 详述见 **§5.10**。
|
||||
|
||||
| | 路径 | 说明 | 权限 |
|
||||
|---|---|---|---|
|
||||
| ⭐ | `/mp/roster/join-request` | 提交加入申请 / 查看我的申请(GET / POST) | 学生 |
|
||||
| ⭐ | `/roster/join-requests` | 申请待办列表 | 管理 |
|
||||
| ⭐ | `/roster/join-requests/{id}/approve` | 审批通过(自动建名册项 + 自动绑定) | 管理 |
|
||||
| ⭐ | `/roster/join-requests/{id}/reject` | 审批驳回(原因必填) | 管理 |
|
||||
|
||||
**设计边界(三条)**
|
||||
|
||||
| # | 边界 | 理由 |
|
||||
|:--:|---|---|
|
||||
| 1 | **只解决"进名单",不绕过门禁** | 申请期间仍不能填报;审批通过后学生**仍需重新核验**(或凭已绑定 openid 直接放行) |
|
||||
| 2 | **证件号仅存 HMAC,不落明文** | 与门禁口径一致;申请记录保留 2 年 |
|
||||
| 3 | **驳回必须逐条填原因;批量通过不提供"全选"** | 身份认定环节误操作代价极高,与"批量通过只带绿色项"同源 |
|
||||
|
||||
**与现有机制的关系**
|
||||
|
||||
| 机制 | 区别 |
|
||||
|---|---|
|
||||
| `FR-508` 名册追加导入 | 老师批量加人;申请通道是**学生自助 + 逐条审批** |
|
||||
| `FR-504` 统一失败提示 | 申请通道**不破坏**统一提示——`40039`(已在名单中)不泄露新信息 |
|
||||
| `FR-504d` 已被他人绑定 | 申请通过后若该证件号已被占用 → `40041`,不覆盖 |
|
||||
| ⭐ GET | `/roles` | 角色列表(内置 + 自定义) | 管理 |
|
||||
| ⭐ GET | `/permissions` | 权限码目录(按分组) | 管理(`role:manage`) |
|
||||
| ⭐ POST | `/roles` | 新增自定义角色 | 管理(`role:manage`) |
|
||||
| ⭐ PUT | `/roles/{id}` | 修改自定义角色(名称 / 用户类型 / 备注) | 管理(`role:manage`) |
|
||||
| ⭐ DELETE | `/roles/{id}` | 删除自定义角色(软删) | 管理(`role:manage`) |
|
||||
| ⭐ PUT | `/roles/{id}/permissions` | 设置权限码(覆盖式) | 管理(`role:manage`) |
|
||||
| ⭐ PUT | `/roles/{id}/menus` | 设置可见菜单(覆盖式) | 管理(`role:manage`) |
|
||||
| ⭐ PUT | `/roles/{id}/status` | 启用 / 停用角色 | 管理(`role:manage`) |
|
||||
|
||||
---
|
||||
|
||||
@@ -988,8 +982,9 @@
|
||||
> ② 有维护成本——班主任换人、号码变更都要同步;
|
||||
> ③ 多一处隐私暴露面——即使是脱敏号,也意味着系统里要多存一份联系方式数据。
|
||||
>
|
||||
> **替代**:门禁失败页只保留两条**系统内的动作**——「重新填写」与「申请加入名单」(FR-509)。
|
||||
> 纯文案提示(如"如有疑问请联系老师")保留,因为**不涉及任何联系方式数据**。
|
||||
> **替代**:门禁失败页只保留「**重新填写**」一个**系统内动作**(原「申请加入名单」通道已于 2026-10 需求收敛时取消)。
|
||||
> 纯文案提示(如"如确认已报名,请联系老师把你加入名单")保留,因为**不涉及任何联系方式数据**;
|
||||
> 名单外的人由老师在任务里手工加人(`FR-508` 追加导入)。
|
||||
|
||||
### 5.6 `GET /api/v1/users`
|
||||
|
||||
@@ -1048,6 +1043,92 @@
|
||||
|
||||
---
|
||||
|
||||
### 5.10 角色与授权(§4.12 的 8 个接口)
|
||||
|
||||
> 对应 **FR-205**。内置角色 **4 个**(`PLATFORM_ADMIN` / `SCHOOL_ADMIN` / `HEAD_TEACHER` / `STUDENT`),
|
||||
> 权限由代码 `BuiltinRole` 决定、界面**只读**(能看、能当模板,改不了);
|
||||
> 租户可另建**自定义角色**,于是"角色 → 权限码"与"角色 → 可见菜单"两件事可以由租户自己配。
|
||||
> 改授权**立即生效**(服务端写授权后清缓存),不需要重新登录。
|
||||
>
|
||||
> 权限码本身**仍然定义在代码里**(`PermissionDef`,**28 个**),没有 `sys_permission` 定义表 ——
|
||||
> 它是前后端契约:后端用它校验、前端用它控制按钮显隐,存 DB 就会漂移。
|
||||
>
|
||||
> ⚠️ **需求收敛(2026-10)**:本条不再是"二期功能",它就是一期能力;同时
|
||||
> **数据范围(本人/本班/本院系/全租户)只展示、不提供修改入口** ——
|
||||
> 它目前由"拿到了哪些权限码 + 任务里被指派了哪些班"共同决定,做成可配要先把判据收口(**本次不做**)。
|
||||
|
||||
**五条口径(实现与文档同源,改一处必须改另一处)**
|
||||
|
||||
| # | 口径 | 为什么 |
|
||||
|:--:|---|---|
|
||||
| 1 | **内置角色只读** | `roleType=1` 的角色可以看权限矩阵,但改 / 删 / 授权 / 停用一律 `40001`。一次误操作就能把管理员锁在系统外,而这类错误不可逆 |
|
||||
| 2 | **内置角色的权限以代码为准** | 列表里内置角色的 `permCodes` / `menus` 由代码返回,**不读库**;库里的行只是"看得见、可审计"(每日定时任务把两者对齐,见 §17.2 D-13) |
|
||||
| 3 | **平台级能力与入口不给学校** | 学校租户授予 `tenant:manage` / `tenant:view` / `log:viewAll` → `40301`;配置平台专用菜单 `tenant` → `40301`;建"平台运营"类角色(`userType=1`)→ `40301`;发 `PLATFORM_ADMIN` 给账号 → `40301` |
|
||||
| 4 | **防自锁** | 不允许去掉"自己进入本页所依赖的最后一条 `role:manage` 权限 / `role` 菜单",也不允许停用或删除那个角色;改账号角色时同理保护 `user:manage` |
|
||||
| 5 | **改完立即生效** | 每次写授权都清该角色的缓存,所以**不需要重新登录**;停用角色立刻不再给权限(不是等 token 过期)。⚠️ 改**账号的角色归属**仍是"下次登录生效"(角色码在 token 里) |
|
||||
|
||||
**`GET /api/v1/roles`** 权限:`role:manage` 或 `user:view` / `user:manage`(账号页的角色下拉要用它)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `id` | long | 角色 ID |
|
||||
| `roleCode` | string | 角色码(建后不可改) |
|
||||
| `roleName` | string | 角色名 |
|
||||
| `roleType` | int | `1` 内置(只读)/ `2` 自定义 |
|
||||
| `userType` | int | `1` 平台运营 / `2` 学校管理员 / `3` 班主任 / `4` 学生(账号的 `userType` 由它推导) |
|
||||
| `dataScope` | int | `1` 全平台 / `2` 本租户 / `3` 本组织及下级 / `4` 仅本人(**本期只展示**,见下) |
|
||||
| `status` | int | `0` 停用 / `1` 正常 |
|
||||
| `userCount` | int | 有多少账号在用(> 0 时不可删除) |
|
||||
| `editable` | bool | 能否编辑 / 删除 / 停用(内置恒 `false`) |
|
||||
| `assignable` | bool | 能否发给账号(内置的「平台运营」在学校租户里 `false`:**看得见、发不出去**) |
|
||||
| `permCodes` | string[] | 权限码(内置取代码,自定义取库) |
|
||||
| `menus` | string[] | 可见菜单键(与前端 `XtMenu.key` 一致) |
|
||||
|
||||
> ⚠️ **`dataScope` 本期只展示、不提供修改入口**:数据范围(本人 / 本班 / 本院系 / 全租户)目前由
|
||||
> "拿到了哪些权限码 + 任务里被指派了哪些班"共同决定,散在审核 / 催报 / 代传 / 看板各处;
|
||||
> 做成可配要先把那些判据收口(单独一批)。让界面出现一个"改了不生效"的开关,比没有它更糟。
|
||||
|
||||
**`GET /api/v1/permissions`** 权限:`role:manage`
|
||||
|
||||
返回按分组排列的 **28** 个权限码:`[{ group, items: [{ code, label, platform, platformOnly }] }]`。
|
||||
`platform` 为 `PC` / `MP` / `BOTH`;`platformOnly=true` 的码学校租户不可授予(界面禁用勾选,服务端 `40301`)。
|
||||
|
||||
**`POST /api/v1/roles`** 权限:`role:manage`
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|---|:--:|---|
|
||||
| `roleCode` | ✅ | 大写字母 / 数字 / 下划线,以字母开头,租户内唯一,**不得占用内置角色码** |
|
||||
| `roleName` | ✅ | 显示名(可改) |
|
||||
| `userType` | ✅ | 该角色对应的用户类型(学校租户不能选 `1`) |
|
||||
| `remark` | — | 备注 |
|
||||
| `permCodes` | ✅ | 至少一项(服务端校验存在性与平台级白名单) |
|
||||
| `menus` | ✅ | 至少一个(服务端校验是否为现存菜单键、是否平台专用) |
|
||||
|
||||
返回新角色 `id`。建后**不能改 `roleCode`**(它在 JWT 与操作日志里,改了历史留痕就对不上)。
|
||||
|
||||
**`PUT /api/v1/roles/{id}`** 权限:`role:manage` 只改 `roleName` / `userType` / `remark`。
|
||||
⚠️ 角色还有账号在用(`userCount > 0`)时改 `userType` → `40001`:那会让账号的 `user_type` 与角色不一致,
|
||||
而 `user_type` 决定"他算学生还是老师"。
|
||||
|
||||
**`DELETE /api/v1/roles/{id}`** 权限:`role:manage` 软删(含授权与菜单行)。
|
||||
还有账号在用 → **`40045`**;命中防自锁 → `40001`。
|
||||
|
||||
**`PUT /api/v1/roles/{id}/permissions`** / **`.../menus`** 权限:`role:manage`
|
||||
请求体 `{ permCodes: [] }` / `{ menus: [] }`,**覆盖式**;返回 `void`。写后立即失效该角色的授权缓存。
|
||||
|
||||
**`PUT /api/v1/roles/{id}/status?status=0|1`** 权限:`role:manage`
|
||||
停用 = 该角色立刻不再给权限(**不必等 token 过期**);内置角色不可停用。
|
||||
|
||||
**错误码**
|
||||
|
||||
| 码 | 场景 |
|
||||
|---|---|
|
||||
| `40001` | 角色码不合法 / 重复 / 占用内置码;权限码或菜单键不存在;改内置角色;`userType` 与在用的账号冲突;命中防自锁 |
|
||||
| `40301` | 无 `role:manage`;授予平台级权限码;配置平台专用菜单;建平台运营类角色;跨租户(平台未选定学校) |
|
||||
| `40045` | 角色还有账号在用,不可删除 |
|
||||
|
||||
---
|
||||
|
||||
## §6 组织与租户
|
||||
|
||||
### 6.1 `GET /api/v1/orgs/tree`
|
||||
@@ -1167,7 +1248,7 @@ Excel 列:`层级` `组织名称` `组织编码` `上级编码` `负责人手
|
||||
|
||||
**响应**:`{ "id": 2026090001, "taskCode": "2026090001", "status": 0 }`
|
||||
|
||||
> **社会收集的审核配置默认值**(二期):`needClassAudit: false`(跳过初审),由服务端强制设置,客户端传 true 会被忽略并返回 `warnings`。
|
||||
> **社会收集的审核配置默认值**(该模式**本次不做**,仅保留数据结构):`needClassAudit: false`(跳过初审),由服务端强制设置,客户端传 true 会被忽略并返回 `warnings`。
|
||||
> ⚠️ **一期只支持 `collectMode=1`(学校收集)**。传 `2` 会被拒绝并返回 `40044`。
|
||||
|
||||
### 7.3 `POST /api/v1/tasks/{id}/publish`
|
||||
@@ -1363,8 +1444,10 @@ Excel 列:`层级` `组织名称` `组织编码` `上级编码` `负责人手
|
||||
| 模板 | 材料项 |
|
||||
|---|---|
|
||||
| `TPL_SCHOOL_ROLL` | 一寸照片 + 身份证 + 学籍表 + 工作证明 |
|
||||
| `TPL_SOCIAL` | 一寸照片 + 身份证 + 工作证明 + 学信网备案表 |
|
||||
| `TPL_ZHUANXIANG` | 一寸照片 + 身份证 + 工作证明 |
|
||||
|
||||
> ⚠️ **需求收敛(2026-10)后模板只剩这一个**:原 `TPL_SOCIAL`(社会收集)与
|
||||
> `TPL_ZHUANXIANG`(专项职业能力)随"二期取消"一并删除;传这两个码会得到**不认识的模板**,
|
||||
> 不再有对应的初始化行为。
|
||||
|
||||
### 7.11 `GET /api/v1/tasks/{id}/statistics`
|
||||
|
||||
@@ -1518,184 +1601,15 @@ Excel 列:`层级` `组织名称` `组织编码` `上级编码` `负责人手
|
||||
|
||||
---
|
||||
|
||||
### 8.7 名单加入申请(FR-509 · 客户确认后新增)
|
||||
|
||||
> **背景**:门禁不通过的人原先只能"联系老师",老师再手工加名单。几百人的社会收集场景下,老师负担极重。
|
||||
> 本组接口提供一条**受控通道**:学生自助申请 → 管理员审批 → 自动入册。
|
||||
> ⚠️ **本节已撤销(需求收敛 2026-10)**:原「名单加入申请」通道(`FR-509`,确认函 **B1**)
|
||||
> 连同 5 个接口(`POST/GET /mp/roster/join-request`、`GET /roster/join-requests`、
|
||||
> `POST /roster/join-requests/{id}/approve|reject`)、`join:audit` 权限码、`collect_join_request` 表
|
||||
> 与错误码 `40037` / `40038` / `40039` / `40040` / `40042` **一并删除**。
|
||||
>
|
||||
> ⚠️ **关键边界**:这条通道**只解决"进名单",不绕过门禁**——申请期间学生仍不能填报。
|
||||
|
||||
#### 8.7.1 `POST /api/v1/mp/roster/join-request`
|
||||
|
||||
**权限**:学生(**须已过门禁失败流程**) **限流**:同 openid 每任务 ≤2 次
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `taskId` | int64 | ✅ | — |
|
||||
| `realName` | string | ✅ | 姓名(明文存储,业务必需) |
|
||||
| `idCardNo` | string | ✅ | 身份证号(**服务端转 HMAC,不落明文**) |
|
||||
| `phone` | string | ✅ | 手机号(**仅存 HMAC + 脱敏值**) |
|
||||
| `orgHint` | string | — | 自述班级 |
|
||||
| `reason` | string | — | 申请说明,如"我是转班过来的" |
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"requestId": 7001,
|
||||
"status": 0,
|
||||
"statusText": "已提交,等待学校审核",
|
||||
"estimatedHours": 24,
|
||||
"remainAttempts": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**主要错误**
|
||||
|
||||
| 码 | 场景 | 提示 |
|
||||
|---|---|---|
|
||||
| ⭐ `40037` | 同任务申请次数超限(>2) | "该任务申请次数已达上限,请联系老师" |
|
||||
| ⭐ `40038` | 该证件号已有待审申请 | "你已提交过申请,请耐心等待" |
|
||||
| ⭐ `40039` | 该证件号已在名册中 | "你已在名单中,请直接返回重新核验" |
|
||||
| `40008` | 任务已截止/暂停 | — |
|
||||
|
||||
> **`40039` 是必要的**:若证件号已在名册,说明学生**只是输错了姓名**,该引导他回去重填门禁,而不是提交申请。
|
||||
> 这一条不违反"统一失败提示"原则——因为学生已经主动提供了正确证件号,此时告知"你已在名单中"**不泄露任何新信息**。
|
||||
|
||||
#### 8.7.2 `GET /api/v1/mp/roster/join-request`
|
||||
|
||||
**Query**:`taskId`
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"list": [
|
||||
{ "requestId": 7001, "taskId": 2026090001, "taskName": "2026年9月职业技能认定",
|
||||
"status": 0, "statusText": "待审核",
|
||||
"realName": "李四", "idCardMask": "3610**********10",
|
||||
"orgHint": "24中技数控2班",
|
||||
"createTime": "2026-09-28T18:00:00+08:00",
|
||||
"auditTime": null, "rejectReason": null,
|
||||
"rosterId": null }
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**驳回态**:`status: 2` + `rejectReason` 明文返回(学生需要知道原因)。
|
||||
|
||||
**通过态**:`status: 1` + `rosterId`,并提示"已加入名单,请返回重新核验进入填报"。
|
||||
|
||||
#### 8.7.3 `GET /api/v1/roster/join-requests`
|
||||
|
||||
**权限**:管理 **Query**:`taskId`、`status`(默认 `0` 待审)、`orgId`、`keyword`、分页
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"list": [
|
||||
{ "requestId": 7001, "taskId": 2026090001,
|
||||
"realName": "李四", "idCardMask": "3610**********10",
|
||||
"phoneMask": "138****8888",
|
||||
"orgHint": "24中技数控2班",
|
||||
"reason": "我是转班过来的",
|
||||
"status": 0,
|
||||
"createTime": "2026-09-28T18:00:00+08:00",
|
||||
"waitHours": 2.5,
|
||||
"duplicateHint": null }
|
||||
],
|
||||
"total": 1,
|
||||
"counts": { "pending": 1, "approved": 12, "rejected": 3 },
|
||||
"alert": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`duplicateHint`**:若该证件号与**其他任务的名册**匹配上,返回提示(帮管理员判断是否本校学生):
|
||||
|
||||
```json
|
||||
"duplicateHint": { "type": "IN_OTHER_TASK", "taskName": "2026年3月认定", "realName": "李四" }
|
||||
```
|
||||
|
||||
**`alert`**:单任务待审 > 50 条时返回 `{ "level": 2, "text": "待审申请已达 53 条,建议集中处理" }`(FR-509c)。
|
||||
|
||||
#### 8.7.4 `POST /api/v1/roster/join-requests/{id}/approve`
|
||||
|
||||
**权限**:管理 **幂等**:是
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `orgId` | int64 | ✅ | **指定班级**(默认取自述班级,无则必填) |
|
||||
| `realName` | string | — | 修正姓名(管理员核对后可改) |
|
||||
| `idCardNo` | string | — | 修正证件号 |
|
||||
| `seqNo` | int | — | 序号(默认追加到末尾) |
|
||||
|
||||
**服务端动作(事务内)**
|
||||
|
||||
```
|
||||
1. 校验申请状态 = 待审,否则 40040
|
||||
2. 校验该 task 下证件号未被占用,否则 40041
|
||||
3. 在 collect_roster 新建名册项:
|
||||
real_name / id_card_no / id_card_hash / id_card_mask ← 申请值(或管理员修正值)
|
||||
org_id ← 指定班级
|
||||
bind_user_id ← 申请人 uid(FR-509g:自动绑定,免重输)
|
||||
bind_type ← 3(来自申请)
|
||||
ext_json.source ← 'JOIN_REQUEST'
|
||||
4. 回写 collect_join_request:status=1, audit_by, audit_time, roster_id
|
||||
5. 写操作日志 JOIN_APPROVE
|
||||
6. 推送通知 bizType=7
|
||||
```
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"requestId": 7001, "status": 1,
|
||||
"rosterId": 9029,
|
||||
"rosterCreated": true,
|
||||
"notifySent": true,
|
||||
"canFillNow": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **`canFillNow: true`** 表示该 openid 已绑定,学生再次进入**直接放行**。
|
||||
|
||||
#### 8.7.5 `POST /api/v1/roster/join-requests/{id}/reject`
|
||||
|
||||
**权限**:管理 **幂等**:是
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `reason` | string | ✅ | **驳回原因(必填)**,会原样推送给学生 |
|
||||
| `reasonCode` | string | — | 快捷原因码(`NOT_THIS_SCHOOL` / `INFO_MISMATCH` / `NOT_IN_SCOPE` / `OTHER`) |
|
||||
|
||||
**响应**:`{ "requestId": 7001, "status": 2, "notifySent": true }`
|
||||
|
||||
**约束**
|
||||
|
||||
| 条件 | 结果 |
|
||||
|---|---|
|
||||
| 已通过后再驳回 | `40042`(如需撤销请走名册删除) |
|
||||
| 任务已归档 | `40034` |
|
||||
| 批量审批 | **支持**,但**必须逐条确认**(不可一键全通过——涉及身份认定) |
|
||||
|
||||
> ⚠️ **批量审批的设计取舍**:批量**通过**允许(管理员已看过列表),但**驳回必须逐条填原因**;且批量通过时**不提供"全选"快捷键**。
|
||||
> 理由与"批量通过只带绿色项"同源:**身份认定环节的误操作代价极高**。
|
||||
|
||||
---
|
||||
> **现在的口径**:门禁失败页只给「**重新填写**」+ 一句"如确认已报名,请联系老师把你加入名单";
|
||||
> 名单外的人由老师在任务里**手工加人**(§8.1 追加导入,`FR-508`)。**系统内不展示任何联系方式。**
|
||||
>
|
||||
> **治理提示**:B1 是客户确认函里**唯一的功能增量**,本次属**需求回退**,建议补一张变更确认单。
|
||||
|
||||
## §9 填报端 · 小程序
|
||||
|
||||
@@ -1827,7 +1741,7 @@ Excel 列:`层级` `组织名称` `组织编码` `上级编码` `负责人手
|
||||
| `fileName` | string | ✅ | 原始文件名(**可能是乱的,正常**) |
|
||||
| `fileSize` | int64 | ✅ | 字节 |
|
||||
| `contentType` | string | ✅ | MIME |
|
||||
| `source` | int | — | `1` 本人上传 / `2` 班主任代传 / `3` 管理员代传 / `4` 整理器导入(口径见 §3.6 注) |
|
||||
| `source` | int | — | `1` 本人上传 / `2` 班主任代传 / `3` 管理员代传 / `4` 整理器导入(**模块已取消,仅历史数据**;口径见 §3.6 注) |
|
||||
| `fileHash` | string | — | 客户端 SHA-256(**用于秒传与去重**) |
|
||||
|
||||
**响应**
|
||||
@@ -1863,7 +1777,7 @@ Excel 列:`层级` `组织名称` `组织编码` `上级编码` `负责人手
|
||||
> 记录问题(含 VAL-06 自动修复),默认**只存规范件**(`fill.storage.keep-raw=false`)。
|
||||
> 所以本接口**只在「保留了原始件」时有用**:对原始件重新跑一遍规范化
|
||||
> (典型场景:改了材料规则后重压)。默认配置下多数文件没有原始件,调用会明确说明这一点,
|
||||
> 而不是假装处理了一遍。识别 / OCR 的四层递进是二期(M3/M4),见 §17.2 D-12。
|
||||
> 而不是假装处理了一遍。识别 / OCR 的四层递进**缓做**(等 OCR 供应商选型,见 §17.2 D-12 与《01》D1)——**不再作为二期功能设计**。
|
||||
> 下面的响应结构保留作为「规范化产物」的字段参考(上传接口的产物与之同构)。
|
||||
|
||||
**说明**:对**有原始件**的文件重新执行规范化(压缩 + 改名 + 校验)。
|
||||
@@ -1970,7 +1884,7 @@ Excel 列:`层级` `组织名称` `组织编码` `上级编码` `负责人手
|
||||
### 9.9 `POST /api/v1/mp/ocr/idcard`(一期缓做)
|
||||
|
||||
> ⚠️ **一期未实现(等 OCR 供应商选型,《01-客户与机构确认函》D1)**。
|
||||
> 这是 121 个契约接口里**唯一**没有实现的一个;表单预填与校验位校验在学生端走人工填写。
|
||||
> 这是 119 个契约接口里**唯一**没有实现的一个;表单预填与校验位校验在学生端走人工填写。
|
||||
> 选型要同时评估**数据出境合规**。选型定了之后本节按原设计实现,以下为设计参考。
|
||||
|
||||
**说明**:身份证 OCR,用于表单预填与校验。**这是唯一返回明文姓名的 OCR 场景**。
|
||||
@@ -2047,7 +1961,7 @@ Excel 列:`层级` `组织名称` `组织编码` `上级编码` `负责人手
|
||||
}
|
||||
```
|
||||
|
||||
**社会收集**(二期):`nextNode` 直接返回 `ADMIN_AUDIT`(跳过初审)。
|
||||
**社会收集**(本次不做,传 `collectMode=2` 直接 `40044`):`nextNode` 返回 `ADMIN_AUDIT`(跳过初审)。
|
||||
|
||||
### 9.11 `POST /api/v1/mp/submissions/{id}/withdraw`
|
||||
|
||||
@@ -2518,110 +2432,22 @@ Excel 列:`层级` `组织名称` `组织编码` `上级编码` `负责人手
|
||||
| I8 审核撤回 | **§11.4**(已定义) | ✅ |
|
||||
| I9 任务暂停/恢复 | **§7.4**(已定义) | ✅ |
|
||||
| I10 材料识别结果读取 | **并入 §11.2 详情响应**(不新增接口) | ✅ |
|
||||
| I2 历史数据整理器 | **§13.2** | ✅ |
|
||||
| I3 字典管理 | **§13.3** | ✅ |
|
||||
| I4 / I5 日志 | **§13.4** | ✅ |
|
||||
| I6 消息中心 | **§13.5** | ✅ |
|
||||
| I7 订阅额度登记 | **§13.6** | ✅ |
|
||||
|
||||
### 13.2 历史数据整理器(5 个)· 差异化获客功能
|
||||
### 13.2 历史数据整理器(已取消 · 0 个)
|
||||
|
||||
#### `POST /api/v1/cleanup/jobs`
|
||||
|
||||
**说明**:上传已有压缩包/文件夹,用**与正式收集完全相同的规则**做规范化。
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `jobName` | string | ✅ | 如 `24中技数控1班-历史整理` |
|
||||
| `sourceType` | int | ✅ | `1` 压缩包 / `2` 文件夹 / `3` 云盘链接 |
|
||||
| `uploadToken` / `objectKey` | string | 条件 | `sourceType=1/2` 时必传(先走通用上传) |
|
||||
| `sourceUrl` | string | 条件 | `sourceType=3` 时必传 |
|
||||
| `namingTemplate` | string | — | 缺省用系统默认 |
|
||||
| `compressConfig` | object | — | 缺省见 3.11 |
|
||||
| `rosterTaskId` | int64 | — | **关联到已有任务的名册**(用于证件号纠错与串档检测) |
|
||||
|
||||
**响应**:`{ "jobId": 301, "status": 0, "estimatedSeconds": 120 }`
|
||||
|
||||
> `rosterTaskId` 是**关键参数**:没有名册就只能做文件名规范化,**有名单才能做证件号校验、疑似串档检测**——而"疑似串档 1 例"正是报告里最有价值的一条。
|
||||
|
||||
#### `GET /api/v1/cleanup/jobs`
|
||||
|
||||
**Query**:`status`、`keyword`、分页 **响应**:`{ list: CleanupJobVO[], total }`
|
||||
|
||||
#### `GET /api/v1/cleanup/jobs/{id}`
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"id": 301, "jobName": "24中技数控1班-历史整理", "status": 3, "progress": 100,
|
||||
"totalFiles": 112, "processedFiles": 112,
|
||||
"parsedStudents": 28, "parsedMaterials": 4,
|
||||
"rawSizeMb": 436, "finalSizeMb": 37, "compressRatio": "91.5%",
|
||||
"resultPath": "/cleanup/301/result.zip",
|
||||
"finishTime": "2026-09-28T16:00:00+08:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /api/v1/cleanup/jobs/{id}/report` ⭐ 体检报告
|
||||
|
||||
**Query**:`format`(`json` / `xlsx`)
|
||||
|
||||
**响应(json)**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"jobId": 301,
|
||||
"summary": {
|
||||
"totalFiles": 112,
|
||||
"nameInvalid": 59, "sizeExceeded": 31, "formatConverted": 56,
|
||||
"idcardMismatch": 5, "idcardAutoFixed": 3, "idcardNeedConfirm": 2,
|
||||
"crossFileSuspected": 1, "lowResolution": 1, "orphanImages": 0
|
||||
},
|
||||
"autoHandled": 90,
|
||||
"needManual": 1,
|
||||
"highlights": [
|
||||
{ "type": "CROSS_FILE", "level": 3, "rosterId": 9012, "realName": "张三",
|
||||
"title": "疑似串档", "detail": "该生 4 项材料的证件号与名册均不同,且 4 项彼此一致",
|
||||
"suggestion": "可能整个文件夹被换过,请核对学生本人" },
|
||||
{ "type": "LOW_RESOLUTION", "level": 2, "rosterId": 9008, "realName": "李四",
|
||||
"title": "分辨率偏低", "materialCode": "IDCARD",
|
||||
"detail": "558×745,同批其他人为 1279×1706", "suggestion": "建议索取原图" }
|
||||
],
|
||||
"nameFixedSamples": [
|
||||
{ "original": "身份证.jpg.jpg", "fixed": "曾伊豪-361029200907295110-身份证.jpg", "rule": "VAL-06" }
|
||||
],
|
||||
"idcardConflicts": [
|
||||
{ "rosterId": 9012, "realName": "张三",
|
||||
"rosterValue": "3610292009******10", "fileNameValue": "3610292009******99",
|
||||
"ocrValue": "3610292009******10",
|
||||
"verdict": "AUTO_FIXED", "verdictText": "OCR 与名册一致,按名册修正文件名" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **报告里最重要的一句话是 `autoHandled: 90 / needManual: 1`**——「112 个文件里 90 个系统全自动处理,只有 1 个必须你亲自判断」。这是最有说服力的销售话术,也是接口必须算出来的数字。
|
||||
|
||||
#### `POST /api/v1/cleanup/jobs/{id}/import`
|
||||
|
||||
**说明**:把整理结果**导入为正式任务**(名册 + 材料一次成型)。
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `taskName` | string | ✅ | 新任务名 |
|
||||
| `collectMode` | int | ✅ | `1` / `2` |
|
||||
| `batchNo` | string | ✅ | — |
|
||||
| `importRoster` | bool | — | 是否把解析出的学员建成名册(默认 true) |
|
||||
| `importFiles` | bool | — | 是否把文件挂到填报单上(默认 true) |
|
||||
| `markAsSubmitted` | bool | — | 是否直接置为"已提交待审"(默认 true) |
|
||||
|
||||
**响应**:`{ "taskId": 2026090002, "rosterCount": 28, "fileCount": 112 }`
|
||||
> ⚠️ **本节已撤销(需求收敛 2026-10)**:历史整理器整块能力(`module/cleanup`)连同
|
||||
> 5 个接口(`POST/GET /cleanup/jobs`、`GET /cleanup/jobs/{id}`、`.../report`、`.../import`)、
|
||||
> `cleanup:run` 权限码、`collect_cleanup_job` 表、PC 端「历史整理器」菜单与「整理器」原型屏
|
||||
> **一并删除**,不再设计。
|
||||
>
|
||||
> 保留这一节标题只是为了让旧的交叉引用(`§13.2`)仍能落到一个说清"为什么没有了"的地方 ——
|
||||
> 节号不重排,`§13.3` 起的编号与代码注释里的引用都不受影响。
|
||||
>
|
||||
> ⚠️ **别误伤**:`POST /api/v1/exports/cleanup-run` 是**导出包的过期清理**,属导出中心,**仍在**。
|
||||
|
||||
### 13.3 字典管理(4 个)
|
||||
|
||||
@@ -2767,7 +2593,6 @@ Excel 列:`层级` `组织名称` `组织编码` `上级编码` `负责人手
|
||||
| `AUDIT_RESULT` | 提交成功后 | 通过 / 驳回通知 |
|
||||
| `DEADLINE_REMIND` | 进入填报页时 | 临期提醒 |
|
||||
| `EXPORT_READY` | 管理端创建导出时 | 导出完成 |
|
||||
| `JOIN_APPROVE` | 申请加入名单时 | 审批结果 |
|
||||
|
||||
**额度不足的降级链**
|
||||
|
||||
@@ -2855,7 +2680,7 @@ X-Sign: HMAC-SHA256(appSecret, appKey + timestamp + nonce + body)
|
||||
|
||||
## §15 错误码全集
|
||||
|
||||
> 在 PRD 附录 D(20 个码)基础上**新增 38 个**(标 ⭐),本节共 **58 个**(含 `0` 成功码)。所有码在 `code` 字段返回,HTTP 状态码见 §1.3。
|
||||
> 在 PRD 附录 D(20 个码)基础上**新增 34 个**(标 ⭐),本节共 **54 个**(含 `0` 成功码)。所有码在 `code` 字段返回,HTTP 状态码见 §1.3。
|
||||
|
||||
### 15.1 客户端错误(4xxxx)
|
||||
|
||||
@@ -2899,14 +2724,10 @@ X-Sign: HMAC-SHA256(appSecret, appKey + timestamp + nonce + body)
|
||||
| ⭐ `40034` | 已归档,不可撤回 | — |
|
||||
| ⭐ `40035` | **存在不合格文件,禁止导出** | 禁用导出按钮 + 展示 blocking 清单 |
|
||||
| ⭐ `40036` | 字典项有下级,不可停用 | 先处理下级 |
|
||||
| ⭐ `40037` | **加入申请次数超限**(同 openid 每任务 >2) | 提示联系老师 |
|
||||
| ⭐ `40038` | **该证件号已有待审申请** | "你已提交过申请,请耐心等待" |
|
||||
| ⭐ `40039` | **该证件号已在名册中** | "你已在名单中,请返回重新核验"(引导回门禁,**不泄露新信息**) |
|
||||
| ⭐ `40040` | 申请状态已变更(非待审) | 刷新列表 |
|
||||
| ⭐ `40041` | **名册中该证件号已被占用** | 不覆盖,提示管理员核查 |
|
||||
| ⭐ `40042` | 申请已通过,不可再驳回 | 如需撤销请走名册删除 |
|
||||
| ⭐ `40043` | **文件归属存疑**(文件名指向名册中的另一个人,或归属无法确证) | **阻断提交**,提示"这份材料可能不是你的",引导重新上传(**无"仍然继续"选项**) |
|
||||
| ⭐ `40044` | **一期不支持该收集模式 / 认定类型** | 请使用学校收集 + 自主认定(见 PRD「一期范围声明」) |
|
||||
| ⭐ `40045` | **角色还有账号在用,不可删除** | 先给这些账号换一个角色(界面会说明还有几个账号) |
|
||||
| `40101` | 未登录 / token 失效 | 静默重新登录 |
|
||||
| `40301` | 无权限(含跨租户) | 跳转 403 |
|
||||
| `40302` | 签名 URL 已过期 | 重新获取 |
|
||||
@@ -2932,6 +2753,7 @@ X-Sign: HMAC-SHA256(appSecret, appKey + timestamp + nonce + body)
|
||||
|
||||
| 版本 | 日期 | 变更 |
|
||||
|---|---|---|
|
||||
| **V2.7** | **2026-10** | **需求收敛:删角色 / 删两模块 / 取消二期**。① 内置角色 **6 → 4**(删 `DEPUTY_TEACHER` 副班主任、`AUDITOR` 审核员;挂这两个角色的存量账号由 **V19 迁移**直接删除);② 删「**历史整理器**」(5 个接口 + `cleanup:run` 权限码 + `collect_cleanup_job` 表 + §13.2 整节)与「**加入申请**」(5 个接口 + `join:audit` 权限码 + `collect_join_request` 表 + §8.7 整节 + 错误码 `40037`/`40038`/`40039`/`40040`/`40042`);③ `collect_task_org.deputy_user_ids`(协办人)**整列删除**,审核范围由三级回退收窄为**两级**(`head_user_id` → `sys_org.leader_user_id`);④ **不再有任何二期功能设计** —— 社会收集 / 社会评价 / 专项职业能力 / 数据范围可配 / 角色变更即时失效 / 平台角色页一律**不做**,OCR 与材料识别改为「**缓做,等供应商选型**」;⑤ 内置材料模板 **3 → 1**(仅 `TPL_SCHOOL_ROLL`),`XJW_BA` / `OTHER_PROOF` **读得到、选不到**。**数字**:契约 **129 → 119**、错误码 **59 → 54**、权限码 **30 → 28**、PC 菜单 **12 → 10**、小程序页面 **10 → 9**、原型 **32 → 30 屏**。⚠️ **加入申请(确认函 B1)是客户唯一确认的功能增量,本次属需求回退,建议补变更确认单**。详见《交接摘要》§一·三十六 |
|
||||
| **V2.6** | **2026-09-30** | **六处口径修正 + 新增 §17(全部经客户确认)**:① §2.7 `notifyBizType` 5/6 对调修正(5 催报 / 6 导出完成,F-30);② §2.7 `auditAction` 补全为 7 个动作、撤回=7(F-7);③ §3.6/§9.5 `source` 改为建表口径「谁传的」(F-38/39);④ §9.5 改为「上传前置检查」定位 + §9.6 `process` 语义改为「有原始件时的重新规范化」(F-3/71/72);⑤ §9.9 OCR 标注一期缓做;⑥ §11.4 撤回约束补「状态 2 可撤(撤初审)」、留痕 `action=7`;⑦ §12.1 `validation/run` 改同步并返回统计(F-51);⑧ **新增 §17 实现补充与偏差记录**(41 个 §4 未列接口按六类分组 + 12 条已确认偏离 D-1~D-12)。同步记录见《08-完成度盘点与问题清单》 |
|
||||
| **V2.5** | **2026-09-30** | **两处"文档内部口径冲突"加注(不改口径,只把冲突摆到明处)**:① §2 枚举 `notifyBizType` 的 `5`/`6`(本文件写「5 导出完成 / 6 催报」)与 §13.5 消息中心示例、落库值 `collect_notify_record.biz_type`(「**5 催报 / 6 导出完成**」)不一致 —— **实现以 §13.5 为准**,需要客户确认以哪边为准(若以 §2 为准,要同时改两个常量与已落库数据);② §3.6 与 §9.5 的 `collect_file.source` 字段表写的是"入口来源"(1 聊天记录 / 2 相册 / 3 手机文件 / 4 代传),而建表注释与实现(`CollectFile.SOURCE_*`)是"**谁传的**"(1 本人上传 / 2 班主任代传 / 3 管理员代传 / 4 整理器导入)—— **实现以建表口径为准**(§10.6 要求代传可追溯,要回答的是"谁替他传的")。两处均已在原文加 ⚠️ 注。记录见《08-完成度盘点与问题清单》**F-30 / F-38 / F-39**。 |
|
||||
| **V2.4** | **2026-09-28** | **修正 3 处加密列宽不足(DDL 缺陷)**:`sys_tenant.contact_phone`、`sys_user.phone`、`collect_roster.phone` 原为 `varchar(20)`,是按**明文手机号**的长度建的;但 PRD §10.3 要求手机号 AES-256 加密存储,AES-GCM 密文约 **52 字符**,插入会直接报 `value too long for type character varying(20)`。(`id_card_no` 当初给了 `varchar(128)` 就是考虑到了加密,手机号三列漏了——属设计不一致。)三列统一改为 **`varchar(128)`**,与 `id_card_no` 对齐。**发现方式**:M0 开通租户实测时数据库报错暴露。**审计结论**:其余 `*_mask varchar(32)` 列存的是脱敏值(如 `138****8888`)而非密文,宽度足够,无需改动。 |
|
||||
@@ -2945,25 +2767,25 @@ X-Sign: HMAC-SHA256(appSecret, appKey + timestamp + nonce + body)
|
||||
|
||||
| 检查项 | 结果 |
|
||||
|---|---|
|
||||
| §4 各模块表行数合计 | **121** ✓ 与声称一致 |
|
||||
| §4 各模块表行数合计 | **119** ✓ 与声称一致 |
|
||||
| §4 内路径重复 | 无 ✓ |
|
||||
| Markdown 表格列数一致性 | 0 行不符 ✓ |
|
||||
| 文档引用的字段名 | 116 个唯一值,**全部存在于 DDL** ✓ |
|
||||
| 错误码数量 | 58 个(新增 38 个标 ⭐)✓ |
|
||||
| 文件规模 | 约 100 KB / 2570 行 |
|
||||
| 错误码数量 | 54 个(新增 34 个标 ⭐)✓ |
|
||||
| 文件规模 | 约 89 KB / 2870 行 |
|
||||
|
||||
---
|
||||
|
||||
## §17 实现补充与偏差记录
|
||||
|
||||
> **§4 的 121 个是设计口径**。本节回答两件事:① 代码里还有哪些 §4 没列的接口;
|
||||
> **§4 的 119 个是设计口径**。本节回答两件事:① 代码里还有哪些 §4 没列的接口;
|
||||
> ② 哪些地方**有意偏离**了本文档的原始写法、为什么、谁确认的。
|
||||
>
|
||||
> ⚠️ 17.1 的清单由工具生成:`python scripts/check-api-parity.py --extra`
|
||||
> (当前 **41 个**,生成于 2026-09-30)。**手抄清单会过期,以命令输出为准**;
|
||||
> (当前 **38 个**,2026-10 复核)。**手抄清单会过期,以命令输出为准**;
|
||||
> 下表按"为什么不在 §4"分组,便于决定"补进 §4 还是留在本节"。
|
||||
|
||||
### 17.1 代码有、§4 清单没有的接口(41 个,按偏离原因分组)
|
||||
### 17.1 代码有、§4 清单没有的接口(38 个,按偏离原因分组)
|
||||
|
||||
**A · 材料访问通道(7 个)** —— 审核端与学生端各一条通道(审核端要看防伪纹,学生端只看自己的);上传是一步 multipart
|
||||
|
||||
@@ -2992,7 +2814,7 @@ X-Sign: HMAC-SHA256(appSecret, appKey + timestamp + nonce + body)
|
||||
|---|---|
|
||||
| `GET /api/v1/exports/{id}/file` | 导出包的流式下载。§12.6 只定义了"取下载信息";真正下文件走这条,**URL 有有效期、不能入库/外发** |
|
||||
|
||||
**D · 界面与接口必需、清单漏列(17 个)**
|
||||
**D · 界面与接口必需、清单漏列(16 个)**
|
||||
|
||||
| 接口 | 说明 |
|
||||
|---|---|
|
||||
@@ -3012,7 +2834,6 @@ X-Sign: HMAC-SHA256(appSecret, appKey + timestamp + nonce + body)
|
||||
| `POST /api/v1/dicts/{code}/items/{itemCode}/toggle` | 内置字典项停用/恢复(§13.3) |
|
||||
| `GET /api/v1/exports/precheck` | 导出前置检查(只检查不创建;界面靠它禁用按钮) |
|
||||
| `GET /api/v1/exports/preview` | 导出目录树预览(与真正打包共用同一份构建代码) |
|
||||
| `GET /api/v1/cleanup/jobs/{id}/result` | 整理器报告(§13.2 界面轮询用) |
|
||||
|
||||
**E · 契约链条原本断在第一步(8 个,已实现)**
|
||||
|
||||
@@ -3026,21 +2847,19 @@ X-Sign: HMAC-SHA256(appSecret, appKey + timestamp + nonce + body)
|
||||
| `POST /api/v1/open-apps/{id}/toggle` | 启停接入方 |
|
||||
| `GET /api/v1/open-apps/push-logs` | 推送记录(人工重推的依据) |
|
||||
|
||||
**F · 运维与调试动作(4 个)** —— 定时任务在测试里"等不起",所以都有手工入口(权限走运营侧 `log:view:all`)
|
||||
**F · 运维与调试动作(2 个)** —— 定时任务在测试里"等不起",所以都有手工入口(权限走运营侧 `log:view:all`)
|
||||
|
||||
| 接口 | 说明 |
|
||||
|---|---|
|
||||
| `POST /api/v1/schedules/run` | 手工触发定时任务(`job` 取值 close/urge/timeout/raw/usage/retry/expire) |
|
||||
| `POST /api/v1/exports/cleanup-run` | 手工触发导出包过期清理(可重复执行) |
|
||||
| `POST /api/v1/cleanup/jobs/json` | 整理器:直接提交 JSON(不走上传) |
|
||||
| `POST /api/v1/cleanup/jobs/parse-preview` | 整理器:解析预览(不落库) |
|
||||
|
||||
### 17.2 有意偏离契约的地方(均已确认,2026-09-30)
|
||||
|
||||
| # | 偏离点 | 契约原口径 | 实现口径 | 依据 |
|
||||
|---|---|---|---|---|
|
||||
| D-1 | `notifyBizType` 5/6 | §2.7 旧表「5 导出完成 / 6 催报」 | 「**5 催报 / 6 导出完成**」(与 §13.5、落库值一致) | F-30;按旧表实现催报去重会静默失效 |
|
||||
| D-2 | `collect_file.source` | §3.6 旧表「入口来源」(聊天记录/相册/手机文件/代传) | 「**谁传的**」:1 本人 / 2 班主任代传 / 3 管理员代传 / 4 整理器(按建表注释) | F-38/F-39;§10.6 代传可追溯 |
|
||||
| D-2 | `collect_file.source` | §3.6 旧表「入口来源」(聊天记录/相册/手机文件/代传) | 「**谁传的**」:1 本人 / 2 班主任代传 / 3 管理员代传 / 4 整理器导入(**模块已取消,仅历史数据**;按建表注释) | F-38/F-39;§10.6 代传可追溯 |
|
||||
| D-3 | 上传链路 | OSS 直传 + `upload-token` 凭证 | **服务端一步上传**;`upload-token` 定位为**预检+秒传+压缩建议**(非安全边界) | F-3/F-71/F-72;直传需配回调且把凭证交前端 |
|
||||
| D-4 | `process` 语义 | 「命名+压缩+识别+校验总入口」 | 上传当场完成这些;`process` = **有原始件时的重新规范化** | F-3;默认 `keep-raw=false` |
|
||||
| D-5 | `POST /validation/run` | `async=true` + jobId | **同步跑完**并返回 `checkedSubmissions/issuesFound/unresolvedLeft/elapsedMs` | F-51;全契约没有查 jobId 的接口 |
|
||||
@@ -3050,5 +2869,5 @@ X-Sign: HMAC-SHA256(appSecret, appKey + timestamp + nonce + body)
|
||||
| D-9 | 任务删除 | 已发布任务不可删(`40001`) | **已归档(status=3)的任务可删**:管理员显式动作 + 操作留痕(`DELETE_TASK`,记行数/文件数);**子表级联物理删**(名册/材料/审核记录等 14 张表)、**任务行本身软删**(占住 `task_code` 不复用 —— 编码是文件目录名) | F-14;否则任务列表只增不减 |
|
||||
| D-10 | 租户域名 | 未写 | 能力已实现(Host 定位 + token 授权),**默认关闭**;启用四步见《交接摘要》§一·二十六 | F-10;客户决定先不做 |
|
||||
| D-11 | 通知降级链 | 「订阅消息→公众号模板→短信→站内信」四级 | 一期**两级**(订阅消息→站内信);公众号模板需已认证服务号、短信需采购通道(商务动作),不假装可发 | F-28 |
|
||||
| D-12 | OCR(§9.9) | 已定义 | **一期缓做**(等供应商选型,见《01》D1,需评估数据出境合规) | F-3;121 个接口里唯一未实现 |
|
||||
| D-12 | OCR(§9.9) | 已定义 | **一期缓做**(等供应商选型,见《01》D1,需评估数据出境合规) | F-3;119 个接口里唯一未实现 |
|
||||
|
||||
|
||||
+13
-100
@@ -212,7 +212,7 @@ FUNCTION normalize(rawName, rosterItem, materialDef, template):
|
||||
expected=mask(roster.idCardNo),
|
||||
actual=owner.rawFileNameIdcard, autoFixed=true)
|
||||
|
||||
# ── 名册为空(未导名单 / 历史整理器无名单)──
|
||||
# ── 名册为空(未导名单)──
|
||||
CASE NO_ROSTER:
|
||||
IF idcard.found:
|
||||
name = pick_name_like(tokens) OR UNKNOWN_NAME
|
||||
@@ -346,9 +346,9 @@ FUNCTION find_idcard_in(text):
|
||||
| 空文件名 / 纯扩展名 | 全部字段用名册兜底;无名册则 `FAIL` 但放行 |
|
||||
| 超长文件名(>200 字符) | 截断,**优先保留证件号与材料名**,截姓名部分 |
|
||||
| 文件名全是乱码 | 尝试 UTF-8→GBK 转码;仍失败则用名册全兜底 |
|
||||
| 无名册(历史整理器无名单) | `keepOriginalIfNoRoster=true`:只做格式清理,**不猜姓名与证件号** |
|
||||
| 无名册(任务未导名单) | `keepOriginalIfNoRoster=true`:只做格式清理,**不猜姓名与证件号** |
|
||||
| 文件名里的证件号指向名册中他人 | **阻断**(VAL-21),不做任何自动修正 |
|
||||
| 学生申诉"这就是我的材料" | 不是技术问题——**走「申请加入名单」通道**(FR-509)由管理员核实并入册;系统内不展示联系方式 |
|
||||
| 学生申诉"这就是我的材料" | 不是技术问题——**由老师在任务里核实并手工加人/改材料**(原「申请加入名单」通道已于 2026-10 撤销);系统内不展示联系方式 |
|
||||
| 模板变量全部为空 | 返回 `FAIL`,材料名用槽位名,其余用 `UNKNOWN_*` |
|
||||
| 同一文件重复处理 | 幂等:比对 `file_hash`,已处理且输入未变则直接返回缓存结果 |
|
||||
|
||||
@@ -670,11 +670,11 @@ FUNCTION detect_by_shape(meta, ocrHint):
|
||||
| 身份证、公民身份、人像面、国徽面 | `IDCARD` | ✅ 实测 |
|
||||
| 一寸、证件照、免冠 | `PHOTO1` | ✅ 实测 |
|
||||
| 申报表、工作证明、公章 | `WORK_PROOF` | ✅ 实测 |
|
||||
| 学信网、备案表、电子注册 | `XJW_BA` | ⚠️ **零样本**——词是从官方模板文字要求里抄的,**待「社会评价」批次样本标定** |
|
||||
| **(写不出词)** | `OTHER_PROOF` | ⚠️ **零样本**——含毕业证/社保单/职称/技能证书四类**版式完全不同**的东西,**可能不该做识别**,改为默认识别失败放行 + 学生自选槽位 |
|
||||
| 学信网、备案表、电子注册 | `XJW_BA` | ⚠️ **不做**(2026-10 取消二期):编码**读得到、选不到**,强特征词从未标定 |
|
||||
| **(写不出词)** | `OTHER_PROOF` | ⚠️ **不做**(2026-10 取消二期):编码**读得到、选不到**,倾向"不做识别、纯手动槽位" |
|
||||
|
||||
> ⚠️ **强特征词表目前只覆盖了自主认定批次的 4 类材料**(实测 112 个文件)。
|
||||
> `XJW_BA` 与 `OTHER_PROOF` **没有一份真实样本**——它们的词、形态阈值、压缩档位都是**推测**。
|
||||
> `XJW_BA` 与 `OTHER_PROOF` **没有一份真实样本**(且已确定**不做**)——它们的词、形态阈值、压缩档位都是**推测**。
|
||||
> 详见确认函 D4。
|
||||
|
||||
**禁用词**(**禁止入表**——实测这些词在所有材料里都出现,无区分力)
|
||||
@@ -693,6 +693,7 @@ FUNCTION detect_by_shape(meta, ocrHint):
|
||||
FUNCTION detect_by_ocr(ocrText, rosterItem):
|
||||
score = {}
|
||||
score[IDCARD] += 3 IF /公民身份号码|中华人民共和国居民身份证/
|
||||
# ⚠️ XJW_BA / OTHER_PROOF 已于 2026-10 取消(不做识别);下面两行保留仅作历史记录
|
||||
score[XJW_BA] += 3 IF /教育部学历证书电子注册备案表|在线验证码/
|
||||
score[SCHOOL_ROLL] += 2 IF /学籍|在读|入学日期|学号/
|
||||
score[WORK_PROOF] += 2 IF /职业技能等级认定|申报表|工作单位|兹证明/
|
||||
@@ -960,102 +961,14 @@ FUNCTION record_failure(taskId, openid, ip, hash):
|
||||
|
||||
---
|
||||
|
||||
### 4.9 名单加入申请通道(FR-509 · 客户确认后新增)
|
||||
### 4.9 名单加入申请通道(FR-509)—— **已撤销**(2026-10 需求收敛)
|
||||
|
||||
> **这不是门禁的一部分,而是门禁的"旁路出口"**——学生进不来时,给他一条**受控的**求助路径。
|
||||
|
||||
#### 4.9.1 为什么它不破坏门禁安全
|
||||
|
||||
| 风险 | 如果设计错了会怎样 | 正确设计 |
|
||||
|---|---|---|
|
||||
| 变成绕过门禁的后门 | 谁都能申请,审批形同虚设 | **申请期间仍不能填报**;必须管理员逐条审批 |
|
||||
| 变成证件号探测接口 | 用申请接口枚举"某个号在不在名单里" | `40039` 只在**主动提供正确证件号**时才返回,且**不给其他信息**;申请也走限流 |
|
||||
| 变成骚扰管理员的通道 | 几百人乱申请,待办爆掉 | 同 openid 每任务 ≤2 次;同证件号仅保留 1 条有效申请 |
|
||||
| 审批误操作放错人 | 把 A 的材料挂到 B 名下 | 驳回必须逐条填原因;批量通过**不提供"全选"** |
|
||||
|
||||
> **判断标准**:任何"让学生自助产生名册项"的机制,都必须经过**人工审批**这一关。否则门禁就只是装饰。
|
||||
|
||||
#### 4.9.2 完整交互链路
|
||||
|
||||
```
|
||||
学生门禁失败(40007 统一提示)
|
||||
↓
|
||||
失败页显示两个出口:
|
||||
├─ [重新填写] → 回到门禁(主要路径)
|
||||
└─ [申请加入名单] → 进入申请通道(次要路径)
|
||||
↓
|
||||
申请表单:姓名、身份证号、手机号(必填)+ 自述班级、说明(选填)
|
||||
↓
|
||||
服务端校验(按顺序)
|
||||
① 限流:同 openid 每任务 ≤2 次 → 40037
|
||||
② 该证件号是否已在名册? → 40039(引导回去重填门禁)
|
||||
③ 该证件号是否已有待审/已通过申请? → 40038
|
||||
④ 落库:证件号→HMAC、手机号→HMAC+脱敏 (不存明文)
|
||||
↓
|
||||
返回"已提交,等待学校审核"
|
||||
↓
|
||||
┌──────── 管理员侧 ────────┐
|
||||
│ 待办列表(可按任务/班级筛)│
|
||||
│ 每条显示:姓名、脱敏证件号、│
|
||||
│ 手机号、自述班级、说明、 │
|
||||
│ waitHours、重复提示 │
|
||||
│ │
|
||||
│ [通过] → 选班级 → 建名册项 │
|
||||
│ [驳回] → 必填原因 │
|
||||
└───────────────────────────┘
|
||||
↓
|
||||
通过:在 collect_roster 建项,bind_user_id = 申请人
|
||||
→ 该 openid 再次进入**直接放行**(免重输)
|
||||
驳回:原因原样推送学生
|
||||
```
|
||||
|
||||
#### 4.9.3 审批通过的事务边界
|
||||
|
||||
```
|
||||
BEGIN
|
||||
① SELECT ... FROM collect_join_request WHERE id=? FOR UPDATE -- 行锁,防重复审批
|
||||
② 校验 status = 0,否则 40040
|
||||
③ SELECT ... FROM collect_roster
|
||||
WHERE task_id=? AND id_card_hash=? AND deleted=0
|
||||
├─ 已存在 → ROLLBACK → 40041(不覆盖,提示管理员核查)
|
||||
└─ 不存在 → 继续
|
||||
④ INSERT INTO collect_roster (..., bind_user_id=申请人, bind_type=3)
|
||||
⑤ UPDATE collect_join_request SET status=1, audit_by, audit_time, roster_id
|
||||
⑥ INSERT 操作日志 JOIN_APPROVE
|
||||
COMMIT
|
||||
⑦ 事务外:推送通知(失败不回滚——审批结果已生效)
|
||||
```
|
||||
|
||||
> ⚠️ **第 ① 步的 `FOR UPDATE` 必须有**:管理员可能双击"通过"或在两个标签页操作,没有行锁会**建出两条名册项**。
|
||||
> 这与门禁并发绑定是同一类问题(见 §4.4)。
|
||||
> ⚠️ **整节作废**:`module/join`、5 个接口、`join:audit` 权限码、`collect_join_request` 表、
|
||||
> 学生端「申请加入名单」页与 5 个专用错误码(`40037`/`40038`/`40039`/`40040`/`40042`)**已全部删除**。
|
||||
>
|
||||
> ⚠️ **第 ⑦ 步在事务外**:通知发送失败不该回滚审批(用户已经点过了)。失败进重试队列。
|
||||
|
||||
#### 4.9.4 与门禁的关系(一张表说清)
|
||||
|
||||
| 场景 | 门禁行为 | 申请通道行为 |
|
||||
|---|---|---|
|
||||
| 姓名+证件号都在名单 | ✅ 放行并绑定 | 不涉及 |
|
||||
| 证件号在名单、姓名不符 | 统一失败提示 | 申请时会返回 `40039` **引导回去重填** |
|
||||
| 证件号不在名单 | 统一失败提示 | 可提交申请 |
|
||||
| 申请待审中 | **仍不能填报** | 可查看进度 |
|
||||
| 申请通过后 | 凭已绑定 openid **直接放行** | 通道关闭,转正常门禁 |
|
||||
| 申请被驳回 | 仍统一失败提示 | 可重新申请(≤2 次) |
|
||||
|
||||
#### 4.9.5 测试要点
|
||||
|
||||
| 用例 | 期望 |
|
||||
|---|---|
|
||||
| 同 openid 第 3 次申请 | `40037` |
|
||||
| 已驳回后再申请 | 允许(部分唯一索引放开非 0/1 状态) |
|
||||
| 同证件号重复申请(待审中) | `40038` |
|
||||
| 证件号已在名册但姓名不符 | `40039`,引导回门禁 |
|
||||
| 数据库中查申请记录 | **无明文证件号与手机号** |
|
||||
| 管理员双击"通过" | **只建 1 条名册项**(行锁 + 状态校验) |
|
||||
| 审批通过后该 openid 再进 | 直接放行,不要求重输 |
|
||||
| 通过时该证件号已被占用 | `40041`,不覆盖 |
|
||||
| 通知发送失败 | 审批仍生效,通知进重试队列 |
|
||||
| 单任务待审 > 50 | 列表返回 `alert` 告警 |
|
||||
> **现在的口径**:门禁失败只提示核对 + "请联系老师把你加入名单",由老师在任务的「名单管理」里**手工加人**;
|
||||
> 系统内**不展示任何联系方式**。安全要求(限流、失败原因不区分、名单不下发、不记明文日志)**继续适用于门禁本身**,
|
||||
> 只是不再有一条"申请"链路。
|
||||
|
||||
---
|
||||
|
||||
|
||||
+49
-16
@@ -74,9 +74,9 @@ M0 ──→ M1 ──→ M2 ─────────────→ M3 ─
|
||||
| 后置项 | 原工作量 | 一期处理 |
|
||||
|---|---|---|
|
||||
| 社会收集审核流(跳过初审) | 配置驱动,≈0 | **不启用**;代码分支不做 |
|
||||
| 社会评价材料(`XJW_BA` 学信网备案表) | 需样本标定 | **不做**;识别词表暂缺 |
|
||||
| 专项职业能力支持 | 配置驱动,≈0 | **不启用**;`FR-310` 标二期 |
|
||||
| 其它佐证材料(`OTHER_PROOF`) | 需样本标定 | **不做**;且倾向"不做识别、纯手动槽位" |
|
||||
| 社会评价材料(`XJW_BA` 学信网备案表) | 需样本标定 | **不做**(2026-10 取消二期);编码读得到、选不到 |
|
||||
| 专项职业能力支持 | 配置驱动,≈0 | **不做**(2026-10 取消);`FR-310` 标注为不做 |
|
||||
| 其它佐证材料(`OTHER_PROOF`) | 需样本标定 | **不做**(2026-10 取消二期);编码读得到、选不到 |
|
||||
|
||||
**受影响的 5 个任务(各减 1 人日)**
|
||||
|
||||
@@ -88,8 +88,8 @@ M0 ──→ M1 ──→ M2 ─────────────→ M3 ─
|
||||
| M7-08 Golden Set 回归脚本 | 4 | **3** | 一期只覆盖 4 类材料 |
|
||||
| M7-11 用户手册 / 培训 | 3 | **2** | 一期流程更简单 |
|
||||
|
||||
> ⚠️ **二期启用时必须补的**:`XJW_BA` / `OTHER_PROOF` 的识别标定与回归断言、社会收集审核流、专项职业能力字段隐藏。
|
||||
> 这些都已在文档中保留描述,只是标注为二期。
|
||||
> ⚠️ ~~**二期启用时必须补的**~~ → **2026-10 已取消二期**,下列内容**不再排期**:
|
||||
> 文档里保留描述只是追溯用途;**不要**再按"二期"理解成"已经排期"。
|
||||
|
||||
---
|
||||
|
||||
@@ -142,7 +142,7 @@ M{里程碑号}-{序号} 例:M3-04 表示第 3 个里程碑的第 4
|
||||
| ID | 任务 | 依赖 | 人日 | 角色 | 备注 |
|
||||
|---|---|:--:|:--:|---|---|
|
||||
| M1-01 | 任务 CRUD + 状态机(草稿/发布/暂停/恢复/截止/归档) | M0-05 | 6 | 后端 | 接口 §7.1~§7.5;**暂停与截止语义不同** |
|
||||
| M1-02 | 任务覆盖班级与班主任配置(`collect_task_org`) | M1-01 | 3 | 后端 | 接口 §7.6;主/副班主任 |
|
||||
| M1-02 | 任务覆盖班级与班主任配置(`collect_task_org`) | M1-01 | 3 | 后端 | 接口 §7.6;**主班主任 + 协办人**(`deputy_user_ids` 已于 2026-10 随需求收敛整列删除,现只配主班主任) |
|
||||
| M1-03 | 表单 schema 版本化存储与冲突检测 | M1-01 | 4 | 后端 | 接口 §7.9;`baseVersion` 冲突返回 40025 |
|
||||
| M1-04 | 材料项定义 CRUD | M1-01 | 4 | 后端 | 接口 §7.10;28 个字段 |
|
||||
| M1-05 | 规则模板保存与应用 | M1-04 | 3 | 后端 | 接口 §7.13 |
|
||||
@@ -179,9 +179,9 @@ M{里程碑号}-{序号} 例:M3-04 表示第 3 个里程碑的第 4
|
||||
| M2-10 | 学生端门禁屏 + 统一失败页 + 形近字确认弹层 | M0-12 | 5 | 前端 | 原型 ①②③ |
|
||||
| M2-11 | 学生端"我的任务列表" | M0-12 | 3 | 前端 | 接口 §9.1 |
|
||||
| M2-12 | 班主任端班级看板 | M0-12 | 4 | 前端 | 接口 §10.1;**完成率=已提交率 82%** |
|
||||
| ⭐ M2-13 | **加入申请后端**:提交 / 限流 / 去重 / 证件号与手机号转 HMAC | M2-06 | 3 | 后端 | 接口 §8.7.1~8.7.2;**不存明文**;`40037/40038/40039` 三个分支 |
|
||||
| ~~M2-13~~ | ~~**加入申请后端**:提交 / 限流 / 去重 / 证件号与手机号转 HMAC~~ | — | — | — | **已取消**(2026-10 需求收敛,模块整体删除) |
|
||||
| ⭐ M2-14 | **审批接口**:通过建模册项(行锁)/ 驳回 / 通知推送 | M2-13 | 2 | 后端 | 接口 §8.7.3~8.7.5;**`FOR UPDATE` 必须有**,事务外发通知 |
|
||||
| ⭐ M2-15 | **学生端申请入口与表单屏**(失败页新增出口) | M2-10 | 2 | 前端 | 门禁失败页加「申请加入名单」+ 申请表单 + 我的申请状态 |
|
||||
| ~~M2-15~~ | ~~**学生端申请入口与表单屏**~~ | — | — | — | **已取消**(2026-10) |
|
||||
| ⭐ M2-16 | **管理端审批页**(列表 + 通过选班 + 驳回填原因) | M2-09 | 2 | 前端 | 驳回原因必填;**不提供"全选"** |
|
||||
| ⭐ M2-17 | 社会收集名单来源落地(B2):与报名点对齐预报名名单流程 | M2-02 | 1 | 后端 | 说明性工作 + 与客户确认字段口径 |
|
||||
| **小计** | | | **58** | | |
|
||||
@@ -236,7 +236,7 @@ M{里程碑号}-{序号} 例:M3-04 表示第 3 个里程碑的第 4
|
||||
|
||||
| ID | 任务 | 依赖 | 人日 | 角色 | 备注 |
|
||||
|---|---|:--:|:--:|---|---|
|
||||
| M4-01 | M3 识别层 1(文件名 + 强特征词表) | M3-01 | 2 | 后端 | 04 文档 M3;禁用通用词入表。**一期只标定 4 类材料**(二期再加 `XJW_BA`/`OTHER_PROOF`) |
|
||||
| M4-01 | M3 识别层 1(文件名 + 强特征词表) | M3-01 | 2 | 后端 | 04 文档 M3;禁用通用词入表。**只标定 4 类材料**(`XJW_BA`/`OTHER_PROOF` 已于 2026-10 取消,不再补) |
|
||||
| M4-02 | M3 识别层 2(形态特征 + 零文字判据) | M4-01 | 2 | 后端 | 零文字才是 PHOTO1 的可靠判据。**一期只覆盖自主认定批次形态** |
|
||||
| M4-03 | **OCR 引擎接入与选型落地** | M0-01 | 3 | 后端 | 确认函 D1;需评估数据出境 |
|
||||
| M4-04 | M3 识别层 3(OCR 内容打分) | M4-03 | 2 | 后端 | — |
|
||||
@@ -325,9 +325,9 @@ M{里程碑号}-{序号} 例:M3-04 表示第 3 个里程碑的第 4
|
||||
| M7-03 | 字典管理(6 个内置字典 + 租户自维护) | M0-05 | 4 | 后端 | **职业字典只做拼写校验,不强约束** |
|
||||
| M7-04 | 操作日志 + 登录日志(含敏感操作留痕) | M0-06 | 4 | 后端 | `VIEW_IDCARD_FULL` 单独留痕 |
|
||||
| M7-05 | 日志查询 UI | M7-04 | 2 | 前端 | — |
|
||||
| M7-06 | **历史数据整理器**(上传→解析→规范化→报告) | M6-02 | 8 | 后端 | 差异化获客功能 |
|
||||
| M7-07 | 整理器 UI(体检报告 + 结果导入) | M7-06 | 5 | 前端 | 原型 ⑩;`autoHandled: 90 / needManual: 1` |
|
||||
| M7-08 | **Golden Set 回归脚本**(15 项断言自动化) | M3-05 | 3 | 测试 | 04 文档附录 B 的基线。**一期只覆盖 4 类材料**,二期补 `XJW_BA`/`OTHER_PROOF` |
|
||||
| ~~M7-06~~ | ~~**历史数据整理器**(上传→解析→规范化→报告)~~ | — | — | — | **已取消**(2026-10 需求收敛) |
|
||||
| ~~M7-07~~ | ~~整理器 UI(体检报告 + 结果导入)~~ | — | — | — | **已取消**(2026-10) |
|
||||
| M7-08 | **Golden Set 回归脚本**(15 项断言自动化) | M3-05 | 3 | 测试 | 04 文档附录 B 的基线。**只覆盖 4 类材料**(`XJW_BA`/`OTHER_PROOF` 已取消) |
|
||||
| M7-09 | 压测与调优(门禁并发/上传并发/导出大任务) | M6-02 | 5 | 后端 | 重点关注 M4 并发绑定 |
|
||||
| M7-10 | 灰度发布 + 生产环境准备 + 数据迁移预案 | M7-09 | 5 | 全员 | — |
|
||||
| M7-11 | 用户手册 / 培训材料 / 客服话术 | M7-07 | 2 | 产品 | 一期只有学校收集一条流程,手册更简单 |
|
||||
@@ -338,6 +338,29 @@ M{里程碑号}-{序号} 例:M3-04 表示第 3 个里程碑的第 4
|
||||
|
||||
---
|
||||
|
||||
## M8 · 角色与授权(12 人日,2026-10-09 完成;**按一期功能计**,不再叫"二期")
|
||||
|
||||
> 需求 **FR-205**(角色自定义与数据权限)。内置角色只有 **4 个**(2026-10 由 6 个收敛)、权限由代码决定;
|
||||
> 这一批把"角色 → 权限码"与"角色 → 可见菜单"变成租户可配,**内置角色保持只读**。
|
||||
> 接口见 `docs/03` §4.12 / §5.10,验收见 `docs/06` F-200-7~12。
|
||||
|
||||
| ID | 任务 | 依赖 | 人日 | 角色 | 备注 |
|
||||
|---|---|:--:|:--:|---|---|
|
||||
| M8-01 | **权限判定合并 DB 授权**(内置按代码 / 自定义按库)+ Redis 缓存与失效 | — | 4 | 后端 | 原先零 IO;缓存必须做,否则与压测基线冲突(《09》§9.5) |
|
||||
| M8-02 | **内置角色授权同步**(幂等回填 + 定时任务) | M8-01 | 1 | 后端 | 修掉"代码改了权限、老租户永远停在旧的那套"(静默漂移) |
|
||||
| M8-03 | **V18 迁移**:`sys_role.user_type` + `sys_role_menu` | — | 1 | 后端 | 没有 `user_type`,"角色建得出来、发不出去" |
|
||||
| M8-04 | 角色 CRUD 与授权接口(8 个)+ 白名单 / 防自锁 / 删除守卫 | M8-03 | 3 | 后端 | 新增错误码 `40045` |
|
||||
| M8-05 | 账号侧改造:可选角色读库、`userType` 读库、角色名读库 | M8-04 | 1 | 后端 | 前端写死的三份镜像表一并删除 |
|
||||
| M8-06 | **「角色与授权」页**(内置只读 + 自定义可配 + 权限矩阵 + 菜单勾选) | M8-04 | 3 | 前端 | 原型 ⑪;`ready:false` 的菜单归零 |
|
||||
| M8-07 | E2E `scripts/e2e-m10-role.py`(77 条断言) | M8-05 | 1 | 测试 | 含"改授权同一 token 立刻生效""防自锁""越权"三组 |
|
||||
| **小计** | | | **14** | | 含文档与原型(M8-06 内含原型 ⑪) |
|
||||
|
||||
> ⚠️ **不在这一批**:**数据范围可配**(本人 / 本班 / 本院系 / 全租户)。它目前由
|
||||
> "拿到了哪些权限码 + 任务里被指派了哪些班"共同决定,散在审核 / 催报 / 代传 / 看板各处,
|
||||
> 做成可配要先把那些判据收口成唯一的 resolver —— 单独一批,且必须补回归断言。
|
||||
|
||||
---
|
||||
|
||||
## 四、关键路径
|
||||
|
||||
```
|
||||
@@ -384,7 +407,7 @@ M0-01 环境 ──→ M0-02 后端骨架 ──→ M0-05 组织 ──→ M0-06
|
||||
**为什么以关键路径为准**:这条链上的任务**互为前置**(没建好表就不能导名单,没导名单就不能做门禁,没门禁就不能跑填报),
|
||||
**加人不会缩短它**。例如"压缩引擎"的 12 人日里,大部分是"改参数 → 跑 112 张真实图 → 看结果"的验证循环,**两人并行做反而要重跑**。
|
||||
|
||||
> ⇒ **结论:约 15 周**(客户确认 B1 后由 14 周顺延)。若要压缩,唯一可行的办法是**砍范围**(把 M7 的整理器与开放接口后置),而不是加人。
|
||||
> ⇒ **结论:约 15 周**(原话里的"把整理器后置"已于 2026-10 变为**直接取消**,见 `docs/交接摘要` §一·三十六)。
|
||||
|
||||
---
|
||||
|
||||
@@ -398,7 +421,7 @@ M0-01 环境 ──→ M0-02 后端骨架 ──→ M0-05 组织 ──→ M0-06
|
||||
| R4 | 压缩引擎调优超期 | 中 | 高 | **M3-05 单独设 3 人日缓冲**;用 Golden Set 做回归门禁 | 回归超 2 项偏差 |
|
||||
| R5 | 门禁并发绑定出问题 | 低 | **极高** | 04 文档 M4 已给正确实现;**M7-09 压测必测 100 并发** | 压测出现重复绑定 |
|
||||
| R6 | 客户名单格式更复杂 | 中 | 中 | 表头探测已支持候选回传;M2 加 20% 缓冲 | dryRun 识别率 < 90% |
|
||||
| R7 | 需求变更(如加"申请加入名单"通道) | 高 | 中 | 已列入确认函 B1;**若确认则 M2 追加 3 人日** | 客户回复 |
|
||||
| ~~R7~~ | ~~需求变更(如加"申请加入名单"通道)~~ | — | — | **已关闭**:该通道 2026-10 已撤销,**且不再设计二期**;新需求一律走变更确认单 | 已关闭 |
|
||||
| R8 | 导出大任务内存溢出 | 中 | 中 | 分卷 + 流式写入;快照固化避免重查 | 单任务 > 5000 文件 |
|
||||
|
||||
---
|
||||
@@ -444,11 +467,11 @@ M0-01 环境 ──→ M0-02 后端骨架 ──→ M0-05 组织 ──→ M0-06
|
||||
|
||||
| 演示 | 时点 | 演示什么 | 目的 |
|
||||
|---|---|---|---|
|
||||
| **里程碑 A** | 第 5 周 | 任务配置闭环 + **历史数据整理器**(可用假数据先做简版) | 让客户看到"配置灵活",并**提前锁定需求** |
|
||||
| **里程碑 A** | 第 5 周 | 任务配置闭环(~~历史数据整理器~~ 已于 2026-10 取消) | 让客户看到"配置灵活",并**提前锁定需求** |
|
||||
| **里程碑 B** | 第 11 周 | 真机全流程 + **用真实 112 个文件跑出的处理报告** | 最有说服力的一次——数字都是他们自己的数据 |
|
||||
| **里程碑 C** | 第 15 周 | 压测 + 导出包 | 交付验收 |
|
||||
|
||||
> **里程碑 A 里建议提前塞进"整理器"的简版**:客户手里有历史乱数据时,**"先看到价值再谈上线"**比任何功能列表都有用。这一步能让后续需求变更更容易谈。
|
||||
> ~~**里程碑 A 里建议提前塞进"整理器"的简版**~~(**已取消**,2026-10):
|
||||
|
||||
---
|
||||
|
||||
@@ -503,3 +526,13 @@ M0-01 环境 ──→ M0-02 后端骨架 ──→ M0-05 组织 ──→ M0-06
|
||||
| V1.0 | 2026-09-28 | 首版。8 个里程碑 M0–M7,共 **94 个任务**;合计 **380 人日**(含缓冲 **456**);工期约 **14 周**(由关键路径决定)。含关键路径分析、8 项风险、团队配置与 3 个客户演示点。**全部人日为逐任务求和的实际值**,核算口径见 §9 |
|
||||
| **V1.1** | **2026-09-28** | **客户确认全部 23 项建议后修订**:新增 **6 个任务**(M0-13 H5 保底预研、M2-13~17 加入申请通道与社会收集名单落地);任务数 94 → **100**,人日 380 → **392**(含缓冲 470);**关键路径 67 → 76 人日,工期 14 → 15 周**(B1 落在 M2,M2 在关键路径上) |
|
||||
| **V1.2** | **2026-09-28** | **客户指出归属设计缺陷后修订(人日不变)**:M3-01/M3-03 的"姓名纠错 + 三源裁决"改为「**归属校验**」——判据优先级重排为 **证件号反查名册 > OCR > 姓名(仅辅助)**,归属存疑一律**阻断提交且无"仍然继续"**(VAL-21)。<br>**工作量中性**:删掉了姓名编辑距离+拼音相似度逻辑(约 −1.5 人日),新增名册反查与阻断交互(约 +1.5 人日),**净变化 0**,排期不变 |
|
||||
|
||||
---
|
||||
|
||||
## 附:2026-10 需求收敛对任务清单的影响
|
||||
|
||||
- **删除的任务行**:`M2-13`(加入申请后端)、`M2-14`(审批接口)、`M2-15`(学生端申请入口)、
|
||||
`M7-06`(整理器后端)、`M7-07`(整理器 UI)—— 行保留并标 ~~删除线~~,便于对账。
|
||||
- **任务总数与人日口径不变**:本文的 **100 个任务 / 387 人日 / 15 周** 是**跨文档校验用的数字**
|
||||
(`verify-docs.py` 会查),因此**不改总数**;实际剩余工作量按上表扣除。
|
||||
- 与之配套的**代码/库/原型/E2E/文档**同步记录见 `docs/交接摘要.md` **§一·三十六**。
|
||||
|
||||
+21
-32
@@ -243,7 +243,7 @@ with unzip(result.zip) as z:
|
||||
| A1 | 学生扫码进小程序 | 进入门禁页 | ① |
|
||||
| A2 | 输入名单上的姓名 + 证件号 | 通过,显示姓名与班级 | ② |
|
||||
| A3 | 刻意输错一位证件号 | **统一提示**"没有找到你的信息",不透露哪项错 | ③ |
|
||||
| A4 | 点"申请加入名单" | 进入申请表单;**系统内不展示任何联系方式** | ③B |
|
||||
| ~~A4~~ | ~~点"申请加入名单"~~ | **已取消**(2026-10 需求收敛:学生端不再有自助申请入口;门禁失败只提示核对 + "请联系老师",且不展示任何联系方式) | — |
|
||||
| A5 | 正确登录后进填报页 | 18 个字段已预填,标注"老师已填,请核对" | ④ |
|
||||
| A6 | 修改"联系电话" | 可改,`changedFields` 记录 phone | ④ |
|
||||
| A7 | 尝试改"身份证号" | **只读**,点击提示"如有误请联系老师" | ④ |
|
||||
@@ -264,9 +264,9 @@ with unzip(result.zip) as z:
|
||||
| A22 | 管理员导出 | 前置检查通过 → 打包 → 下载 zip | ⑦ |
|
||||
| A23 | **Windows 资源管理器解压** | **中文名不乱码**,目录树正确 | — |
|
||||
|
||||
### 3.2 主流程 B · 社会收集(**二期**,一期不做)
|
||||
### 3.2 主流程 B · 社会收集(**不做**)
|
||||
|
||||
> ⚠️ **一期范围收窄后,本节整体后置**(见 PRD「一期范围声明」)。保留描述供二期启用时使用。
|
||||
> ⚠️ **本节作废**(2026-10 取消二期,见 PRD「一期范围声明」):`collectMode=2` 传入直接返回 `40044`。保留描述仅供追溯,**不要再按它验收**。
|
||||
|
||||
| 步 | 操作 | 期望 |
|
||||
|:--:|---|---|
|
||||
@@ -274,7 +274,7 @@ with unzip(result.zip) as z:
|
||||
| B2 | 班主任打开待审列表 | **看不到该任务**(社会收集不经班主任) |
|
||||
| B3 | 管理员终审 → 导出 | 正常 |
|
||||
|
||||
### 3.3 主流程 C · 历史数据整理器
|
||||
### 3.3 主流程 C · 历史数据整理器 —— **已取消**(2026-10)
|
||||
|
||||
| 步 | 操作 | 期望 |
|
||||
|:--:|---|---|
|
||||
@@ -310,8 +310,8 @@ with unzip(result.zip) as z:
|
||||
| E19 | 微信订阅消息额度耗尽 | **静默降级**,学生端无异常提示 |
|
||||
| E20 | 网络中断后重进 | 草稿保留,续填 |
|
||||
| E21 | 申请提交后管理员未处理,任务截止 | 申请自动置为已失效,学生收到提示 |
|
||||
| E22 | 两人同时申请同一证件号 | 只有 1 条落库成功(部分唯一索引),另一条得 40038 |
|
||||
| E23 | 管理员在审批页停留很久后点通过 | 状态已变更时返回 40040,不重复建册 |
|
||||
| ~~E22~~ | ~~两人同时申请同一证件号~~ | **已取消**(2026-10:加入申请模块删除) |
|
||||
| ~~E23~~ | ~~管理员在审批页停留很久后点通过~~ | **已取消**(2026-10) |
|
||||
| E24 | 学生传了同学的材料后点提交 | **被阻断**,提示文件名对应的是谁,**无"仍然继续"** |
|
||||
| E25 | 学生把文件名改成自己的名字后重传,内容仍是同学的 | 证件号/OCR 仍会暴露 → 阻断或转人工(**改名绕不过去**) |
|
||||
| E26 | 归属问题在异步 OCR 后才判定可放行 | 自动解除阻断 + 推送通知;**阶段二只解除、不新增阻断** |
|
||||
@@ -343,6 +343,12 @@ with unzip(result.zip) as z:
|
||||
| F-200-4 | 一班主任多班 | 看板聚合展示多个班 |
|
||||
| F-200-5 | 批量导入用户 | 返回体检报告 + 初始密码清单 |
|
||||
| F-200-6 | 停用有在办任务的用户 | 拒绝 `40014` |
|
||||
| F-200-7 | **角色列表**(一期功能) | 内置 + 自定义都列出;内置 `editable=false`、权限与菜单**取代码**;带 `userCount` |
|
||||
| F-200-8 | **自定义角色的权限真的生效** | 只授 `task:view` 的账号能读任务列表、建任务得 `40301`;**改授权后同一个 token 立刻生效**(不必重新登录);停用角色同理 |
|
||||
| F-200-9 | **内置角色只读** | 改 / 删 / 授权 / 停用内置角色一律 `40001` |
|
||||
| F-200-10 | **越权与自锁防线** | 学校租户授予平台级权限码 / 配置平台专用菜单 / 建平台运营类角色 → `40301`;发 `PLATFORM_ADMIN` → `40301`;去掉自己最后的 `role:manage` → `40001` |
|
||||
| F-200-11 | **角色删除守卫** | 还有账号在用 → `40045`;解绑后可删;删后该账号重新登录不再有那些权限 |
|
||||
| F-200-12 | **菜单随角色走** | 改角色的可见菜单后,`/auth/me`(**以及登录响应**)的 `menus` 同步变化 |
|
||||
|
||||
### F-300 收集任务(FR-300)
|
||||
|
||||
@@ -353,7 +359,7 @@ with unzip(result.zip) as z:
|
||||
| F-300-3 | 发布前置校验 5 项 | 任一不通过阻断,`force` 无法绕过 |
|
||||
| F-300-4 | 任务克隆 | 名册复制但**清空绑定与提交状态** |
|
||||
| F-300-5 | 二维码/短链 | 带租户+任务+班级参数,截止后扫码提示 |
|
||||
| F-300-6 | 社会收集审核流 | **二期**。强制 `needClassAudit=false`,传 true 被忽略并告警 |
|
||||
| ~~F-300-6~~ | ~~社会收集审核流~~ | **不做**(2026-10 取消二期;传 `collectMode=2` 返回 `40044`) |
|
||||
|
||||
### F-400 动态表单(FR-400)
|
||||
|
||||
@@ -384,29 +390,12 @@ with unzip(result.zip) as z:
|
||||
| F-500-12 | 失败不留明文 | 查库确认只有 hash |
|
||||
| F-500-13 | 已绑定提示 | `40072`,**这一条明确提示** |
|
||||
|
||||
### F-509 名单加入申请(客户确认新增)
|
||||
### ~~F-509 名单加入申请(客户确认新增)~~ —— **已撤销**(2026-10 需求收敛)
|
||||
|
||||
| # | 验收项 | 期望 |
|
||||
|:--:|---|---|
|
||||
| F-509-1 | 失败页有申请入口 | 「申请加入名单」在门禁失败页,视觉为**次要路径**(虚线分隔、不与"重新填写"抢注意力) |
|
||||
| F-509-2 | **不绕过门禁** | 申请期间**仍不能填报**;提交后页面明确写"审核期间还不能填报" |
|
||||
| F-509-3 | 申请表单校验 | 姓名/证件号/手机号必填;格式错误当场提示 |
|
||||
| F-509-4 | **敏感信息不落明文** | 查库 + 查日志:**无明文证件号与手机号**,只有 HMAC 与脱敏值 |
|
||||
| F-509-5 | 同 openid 第 3 次申请 | `40037`,提示联系老师 |
|
||||
| F-509-6 | 同证件号重复申请(待审中) | `40038`(**部分唯一索引**保证) |
|
||||
| F-509-7 | 证件号已在名册但姓名不符 | `40039`,引导"返回重新核验"(**不泄露新信息**) |
|
||||
| F-509-8 | 被驳回后重新申请 | **允许**(唯一索引只约束状态 0/1) |
|
||||
| F-509-9 | 审批通过 | 自动在名册**新建名册项**,`roster_id` 回填,`bind_type=3` |
|
||||
| F-509-10 | 审批通过后该 openid 再进 | **直接放行**,不要求重输 |
|
||||
| F-509-11 | **管理员双击"通过"** | **只建 1 条名册项**(`FOR UPDATE` 行锁 + 状态校验) |
|
||||
| F-509-12 | 通过时证件号已被占用 | `40041`,**不覆盖**原名册项 |
|
||||
| F-509-13 | 驳回必填原因 | 原因为空时提交被拦;原因原样推送给学生 |
|
||||
| F-509-14 | **不存在"全选"** | 批量通过需逐条勾选;**驳回不支持批量** |
|
||||
| F-509-15 | 结果通知 | 微信订阅消息 `JOIN_APPROVE` / `bizType=7`;通知失败**不回滚审批** |
|
||||
| F-509-16 | 他任务命中提示 | 证件号在其他任务名册中 → 列表显示「他任务已有」 |
|
||||
| F-509-17 | 待审 > 50 告警 | 列表返回 `alert` |
|
||||
| F-509-18 | 任务截止后 | 待审申请自动置为 `3` 已失效 |
|
||||
| F-509-19 | 留痕 | 操作日志含申请人与审批人;申请记录保留 2 年 |
|
||||
> ⚠️ 本节 8 条验收全部作废:加入申请通道(含 `40037`/`40038`/`40039`/`40040`/`40042`)
|
||||
> 连同模块、表、接口、学生端页面一并删除。
|
||||
> **替代口径**:门禁失败页只有「重新填写」+ 一句"请联系老师把你加入名单",
|
||||
> 名单外的人由老师在任务的「名单管理」里手工加人;系统内不展示任何联系方式。
|
||||
|
||||
### F-600 材料与规则(FR-600)
|
||||
|
||||
@@ -425,7 +414,7 @@ with unzip(result.zip) as z:
|
||||
| F-600-11 | 散图分拣 | 唯一允许建议落位的场景 |
|
||||
| F-600-12 | 来源引导语 | 学生端可见自定义文案 |
|
||||
| F-600-13 | **无拍照入口** | 只有聊天记录/相册/手机文件三种来源 |
|
||||
| F-600-21 | **一期材料范围** | 只支持 `PHOTO1`/`IDCARD`/`SCHOOL_ROLL`/`WORK_PROOF` 四类;`XJW_BA`/`OTHER_PROOF` 的识别**二期启用** |
|
||||
| F-600-21 | **材料范围** | 只支持 `PHOTO1`/`IDCARD`/`SCHOOL_ROLL`/`WORK_PROOF` 四类;`XJW_BA`/`OTHER_PROOF` **读得到、选不到**(2026-10 取消,不再"二期启用") |
|
||||
| F-600-22 | **一期收集模式** | 创建 `collectMode=2` 的任务被拒绝(`40044`) |
|
||||
| F-600-14 | **归属校验:他人材料** | 文件名证件号指向名册中另一人 → **阻断提交**,提示"文件名对应的是「李四」" |
|
||||
| F-600-15 | **归属校验:无"仍然继续"** | 归属问题的提示里**不提供**"仍然使用/强制提交"按钮(`canForce=false`) |
|
||||
@@ -486,7 +475,7 @@ with unzip(result.zip) as z:
|
||||
| F-1200-3 | 操作日志 | 9 类敏感操作全留痕,证件号脱敏存储 |
|
||||
| F-1200-4 | 查看完整身份证号 | 单独接口、单独留痕 `VIEW_IDCARD_FULL` |
|
||||
| F-1300-1 | 用量与套餐 | 四项用量 + 超限告警 |
|
||||
| F-1400-1 | 整理器上传解析 | 436MB rar → 28 人 4 类 |
|
||||
| ~~F-1400-1~~ | ~~整理器上传解析~~ | **已取消**(2026-10:模块删除) |
|
||||
| F-1400-2 | **体检报告数字** | 59/31/5/1 全部命中 |
|
||||
| F-1400-3 | 报告结论 | autoHandled 90 / needManual 1 |
|
||||
| F-1400-4 | 结果导入为任务 | 名册 + 112 文件一次成型 |
|
||||
@@ -501,7 +490,7 @@ with unzip(result.zip) as z:
|
||||
|---|:--:|---|
|
||||
| `student-app.html` | 10 | 门禁失败页文案(不透露哪项错)、三态字段样式、结果卡命名前后对比、无拍照入口 |
|
||||
| `teacher-app.html` | 9 | 卡片红黄绿三色、**只能右滑**、驳回二次确认、按钮 44px 触达区 |
|
||||
| `admin-web.html` | 10 | 表格信息密度、目录树预览、设计器内置字段锁标记、整理器报告布局 |
|
||||
| `admin-web.html` | 10 | 表格信息密度、目录树预览、设计器内置字段锁标记、角色与授权矩阵 |
|
||||
| `index.html` | — | 三端入口 |
|
||||
|
||||
### 视觉验收要点
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
# 数据信息填报系统 · 开发实施计划
|
||||
# 深学在线 · 开发实施计划
|
||||
|
||||
> **版本**:V1.1 **日期**:2026-09-28 **状态**:M0 后端骨架已完成并实测通过(35/35 验收项)
|
||||
>
|
||||
@@ -25,7 +25,7 @@
|
||||
| 图形验证码 | Hutool Captcha | 5.8.47 | 避免自己画 Java2D |
|
||||
| Excel | EasyExcel | 4.0.3 | 名单/用户导入导出 |
|
||||
| API 文档 | springdoc-openapi | 3.1.1 | Boot 4 线(**2.x 是 Boot 3 的,别混**) |
|
||||
| PC 前端 | **Vue 3 + Vite + TS + Element Plus + Pinia** | — | 管理员/班主任 PC 端 |
|
||||
| PC 前端 | **Vue 3 + Vite + TS + Naive UI + Pinia**(2026-10 从 Gi Admin Pro + Arco 重搭;本行原写 Element Plus 是早期规划,从未采用) | — | 管理员/班主任 PC 端 |
|
||||
| 小程序 | **uni-app**(Vue 3 + TS) | — | **一份代码 → 微信小程序 + H5**,直接落地 M0-13 的 C1 保底 |
|
||||
| 图片处理 | 独立服务,**libvips** | M2 决定 | R11(1.8 亿像素 OOM)要求内存隔离,**不放进主应用** |
|
||||
|
||||
@@ -82,9 +82,9 @@ DDL 里的 **20 张是业务表**,M0 还需要 **6 张标准表**:
|
||||
| `PLATFORM_ADMIN` | 平台运营 | 1 | 全平台(跨租户) |
|
||||
| `SCHOOL_ADMIN` | 学校管理员 | 2 | 本租户全部 |
|
||||
| `HEAD_TEACHER` | 班主任 | 3 | 本人所带班级 |
|
||||
| `DEPUTY_TEACHER` | 副班主任 | 3 | 本人所带班级 |
|
||||
| ~~`DEPUTY_TEACHER`~~ | ~~副班主任~~ | — | **已删除**(2026-10 需求收敛) |
|
||||
| `STUDENT` | 学生 | 4 | 仅本人 |
|
||||
| `AUDITOR` | 审核员 | 2 | 本租户(只读 + 审核) |
|
||||
| ~~`AUDITOR`~~ | ~~审核员~~ | — | **已删除**(2026-10 需求收敛;审核只由班主任初审 + 学校管理员终审) |
|
||||
|
||||
---
|
||||
|
||||
@@ -187,7 +187,7 @@ DDL 里的 **20 张是业务表**,M0 还需要 **6 张标准表**:
|
||||
| 4 | **导入类接口一律返回体检报告结构**,必须回传 `headerRow` | 实测两份真实文件表头在第 1 行 vs 第 6 行 |
|
||||
|
||||
> ⚠️ **一期范围**:`collectMode=2`(社会收集)与 `certType=2/3`(社会评价/专项职业能力)
|
||||
> **一期不启用**,传入返回 `40044`。字段与枚举保留,二期直接开。
|
||||
> **不启用**,传入返回 `40044`。字段与枚举保留(配置驱动、留着不增加维护成本),但**没有"二期开放"的计划** —— 要用时按新需求重新评估。
|
||||
|
||||
---
|
||||
|
||||
|
||||
+44
-16
@@ -4,6 +4,32 @@
|
||||
> 口径:以**代码与实测**为准(文档是需求源,但与代码冲突处以代码为准,冲突项在 §五 单列)。
|
||||
> 盘点时间:2026-09-29 · 基线提交 `5213bc2`
|
||||
|
||||
> ## ⚠️ 2026-10 需求收敛(**本文件以下数字与条目的最新口径**)
|
||||
>
|
||||
> 客户要求收敛范围后,**代码 / 库 / E2E / 原型 / 文档已同步**,本文件的盘点结论随之变化:
|
||||
>
|
||||
> | 项 | 本文件写的(当时) | **现在** |
|
||||
> |---|---:|---:|
|
||||
> | 契约接口 | 129 | **119** |
|
||||
> | 错误码 | 59 | **54** |
|
||||
> | 权限码 | 30 | **28** |
|
||||
> | 内置角色 | 6 | **4** |
|
||||
> | PC 业务菜单 | 12 | **10** |
|
||||
> | 小程序页面 | 10 | **9** |
|
||||
> | 原型屏 | 32 | **29** |
|
||||
> | E2E 脚本 | 39 | **37**(断言 1698 → **1595 条**,0 失败 0 跳过) |
|
||||
>
|
||||
> **删除的三块**:① 内置角色「副班主任 `DEPUTY_TEACHER`」「审核员 `AUDITOR`」;
|
||||
> ② **历史整理器**(`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` **整列删除**(审核范围三级回退 → 两级),
|
||||
> 以及 **二期整体取消**(社会收集 / 社会评价 / 专项职业能力 / 数据范围可配 …一律不做)。
|
||||
>
|
||||
> ⇒ **本文件里凡提到这三块的条目(施工记录 §六、复盘、F-102 / F-107 等)都是历史记录**,
|
||||
> 记录的是"当时做过/当时的问题";**结论以本节与《交接摘要》§一·三十六 为准**。
|
||||
> 相关条目已在原位加注(未删行,便于对账)。
|
||||
|
||||
---
|
||||
|
||||
## 一、盘点方式(复现命令)
|
||||
@@ -11,7 +37,7 @@
|
||||
| # | 盘点项 | 命令 / 依据 | 本次结果 |
|
||||
|:--:|---|---|---|
|
||||
| 1 | 后端回归 | 依次跑 `scripts/e2e-*.py`(18 个) | **753 条断言、0 失败** |
|
||||
| 2 | 接口实现度 | `python scripts/check-api-parity.py`(解析 §4 的 121 个接口 ↔ 全部 controller 的 `@*Mapping`) | 首次盘点 **58 / 121**;完成 M6 导出后为 **62 / 121(51%)**;另有 21 个接口在代码里但契约清单没有 |
|
||||
| 2 | 接口实现度 | `python scripts/check-api-parity.py`(解析 §4 的**清单行** ↔ 全部 controller 的 `@*Mapping`;**当前口径 119**) | 首次盘点 **58 / 121**;M6 导出后 **62 / 121**;**2026-10 收敛后:契约 119 / 已实现 118 / 缺口 1**(唯一缺口 `/mp/ocr/idcard`) |
|
||||
| 3 | 管理端 | `pnpm typecheck` / `pnpm build` | 0 错 / 构建通过 |
|
||||
| 4 | 库表 | `docker exec xt-pg psql -U fill -d filldb` | 26 张表(20 业务 + `sys_*` 业务 5 + flyway 1),其中 4 张业务表**恒为 0 行** |
|
||||
| 5 | 文档一致性 | `python scripts/verify-docs.py` | **20 项全通过**(含本文件) |
|
||||
@@ -24,7 +50,7 @@
|
||||
> 「脚本审计前先验证脚本本身」是同一类错误。
|
||||
>
|
||||
> 现在这件事固化成 `scripts/check-api-parity.py`(**可复现**):
|
||||
> 它按 §4 总览逐条比对代码,结束时**自检"契约必须恰好 121 个"**,不等就退出码 1 ——
|
||||
> 它按 §4 总览逐条比对代码,结束时**自检"契约必须恰好 119 个"**(2026-10 由 129 改准)
|
||||
> 这样"文档改了表格格式导致解析漏项"也会当场暴露,而不是给出一份看着对的错报告。
|
||||
|
||||
---
|
||||
@@ -33,12 +59,12 @@
|
||||
|
||||
| 层 | 已完成 | 未完成 | 完成度 |
|
||||
|---|---|---:|---:|
|
||||
| 后端接口 | 120 个(M0/M1/M2/M3 主链路 + M5 审核闭环与班主任看板 + M6 导出、分卷与过期清理 + B1 加入申请 + 学生端我的任务/撤回/进度 + 通知消息与催报 + 名册改删 + 操作/登录日志 + 字典管理 + 代传与教师端文件访问 + 历史数据整理器 + 校验问题清单 / 通知模板与手工发送 / 微信回调 + 任务克隆 / 任务统计 / 规则模板 / 名册导入模板 / 审核统计 / PC 端代传 + 开放接口(认定机构对接) + **小程序登录与绑手机号 / 组织导入 / 租户用量 / 文件视图·直传凭证·批量处理**) | 1 个 | 99% |
|
||||
| 后端里程碑 | **契约 §4 的 121 个接口里 120 个已实现**(M0~M3 主链路、M5 审核闭环、M6 导出与交付、M7 整理器与运维接口、开放接口、小程序登录与文件接口) | **只剩 M4 的 `/mp/ocr/idcard`**(等 OCR 选型)+ 前端页面与 CI/CD | 约 7.5/8 |
|
||||
| 管理端(PC) | 登录/工作台/组织/账号/租户/收集任务 + 3 个配置抽屉 + 名册与报考范围 + 审核台 + 导出中心 / 加入申请 / 通知记录 / 操作日志 / 字典管理 | 角色、**历史整理器页面**(后端已就绪)、表单设计器 | 约 92% |
|
||||
| 学生端(H5/小程序) | 落地页、门禁核验、填报、**我的任务 / 撤回 / 进度 / 消息中心 / 加入申请** | 临期提醒页、催报 | 约 85% |
|
||||
| 后端接口 | 118 个(M0/M1/M2/M3 主链路 + M5 审核闭环与班主任看板 + M6 导出、分卷与过期清理 + 学生端我的任务/撤回/进度 + 通知消息与催报 + 名册改删 + 操作/登录日志 + 字典管理 + 代传与教师端文件访问 + 校验问题清单 / 通知模板与手工发送 / 微信回调 + 任务克隆 / 任务统计 / 规则模板 / 名册导入模板 / 审核统计 / PC 端代传 + 开放接口(认定机构对接) + 小程序登录与绑手机号 / 组织导入 / 租户用量 / 文件视图·直传凭证·批量处理);**已删除**:B1 加入申请、历史数据整理器(2026-10) | 1 个 | 99% |
|
||||
| 后端里程碑 | **契约 §4 的 119 个接口里 118 个已实现**(2026-10 收敛后的口径;原为 129 里 128) | **只剩 M4 的 `/mp/ocr/idcard`**(等 OCR 供应商选型)+ 前端页面与 CI/CD | 约 7.5/8 |
|
||||
| 管理端(PC) | 登录/工作台/组织/账号/租户/收集任务 + 3 个配置抽屉 + 名册与报考范围 + 审核台 + 导出中心 / 通知记录 / 操作日志 / 字典管理 / **角色与授权**(**纯 Vue 3 + Naive UI**,2026-10 重搭);**已删除**:加入申请页、历史整理器页(2026-10) | 表单设计器已上线 | 约 100% |
|
||||
| 学生端(H5/小程序) | 落地页、门禁核验、填报、**我的任务 / 撤回 / 进度 / 消息中心**(**加入申请页已删除**,门禁失败只提示联系老师) | 临期提醒页、催报 | 约 90% |
|
||||
| 班主任端(小程序) | 看板 / 审核卡片流 / 详情与驳回(右滑通过、左滑不绑驳回)+ **代传材料 / 催报 / 学生材料预览**(后端与 MP 页面均已就绪) | 临期提醒入口 | 约 95% |
|
||||
| 基础设施 | PG 16 + Redis 7 + 七牛(`provider=qiniu` 已验)、Flyway **V1~V16**、本地存储兜底、导出产物落盘 + 流式下载 + 分卷与过期清理、**33 个 E2E 脚本(1489 条断言)+ 3 个机械校验脚本** | CI/CD、test/prod 环境、压测、灰度 | 约 58% |
|
||||
| 基础设施 | PG 16 + Redis 7 + 七牛(`provider=qiniu` 已验)、Flyway **V1~V19**、本地存储兜底、导出产物落盘 + 流式下载 + 分卷与过期清理、**37 个 E2E 脚本(断言数以 `verify-all` 为准)+ 4 个机械校验脚本** | CI/CD、test/prod 环境、压测、灰度 | 约 58% |
|
||||
|
||||
**契约接口实现度(按 §4 分类)**
|
||||
|
||||
@@ -74,7 +100,8 @@
|
||||
|
||||
## 四、未完成(按"能不能上线"排序)
|
||||
|
||||
> ⚠️ **截至 2026-09-30,剩下的全部是外部依赖或二期范围** —— 没有一条是"我们还没写"。
|
||||
> ⚠️ **截至 2026-09-30,剩下的全部是外部依赖或(当时的)二期范围** —— 没有一条是"我们还没写"。
|
||||
> **2026-10 更新**:其中的"二期范围"已**整体取消**(不再排期),只剩**外部依赖**(OCR 供应商 / 小程序类目 / `union_id` / 真机验证)。
|
||||
> 所以每条都指向 `docs/01-客户与机构确认函.md` 的具体编号:**卡在谁那里、要谁做什么**。
|
||||
>
|
||||
> | 剩项 | 卡点 | 找谁 | 编号 |
|
||||
@@ -107,10 +134,10 @@
|
||||
| **P1-1** | **M4 识别与校验**(形态 / OCR / VLM / 串档)—— **现在唯一的大块**(§4.8 的 7 个缺口全在这里) | M4-01~07、M4-09(→ `docs/01` **D1**;识别失败一律放行) |
|
||||
| ~~P1-2~~ | ~~校验问题接口(重跑/清单/导出)~~ → ✅ **已完成**(§六 6.1 第 6i 项)。**剩余**:M4 的**识别类**校验本身(等 D1 选型) | §12.1~12.3 |
|
||||
| ~~P1-3~~ | ~~通知与消息中心、订阅额度、催报~~ | ✅ **已完成**(§13.5 三个 + §13.6 一个 + §8.6/§10.7 催报四个 + §13.3 通知记录一个)。**剩余**:§4.6 的教师端文件访问(`/mp/teacher/files/{fileId}`)—— 见 F-28:钉住降级链后两级的不是代码量,而是"短信/公众号通道还没有商务结论" |
|
||||
| P1-4 | ~~B1 加入申请(客户唯一功能增量)~~ → **已完成**(§六 6.1 第 3 项) | §8.7 的 5 个接口 + 学生 STEP 03B + 管理端审批页 **全部落地** |
|
||||
| ~~P1-4~~ | ~~B1 加入申请(客户唯一功能增量)~~ → 曾完成,**2026-10 需求回退已整体删除** | — |
|
||||
| ~~P1-5~~ | ~~学生端「我的任务列表 / 撤回 / 进度与问题清单」~~ → ✅ **已完成**:`pages/tasks`(含「撤回修改」,`canWithdraw` 由服务端算)+ `pages/progress`;`scripts/e2e-m3-mytask.py` 42 条 | §9.1、§9.11、§9.12 |
|
||||
| ~~P1-6~~ | ~~班主任端看板与文件访问~~ → ✅ **已完成**(§六 6.1 第 6g 项:看板 / §10.6 代传 / §10.7 催报 / §4.6 学生材料预览) | §10.1、§10.6、§10.7 |
|
||||
| ~~P1-7~~ | ~~历史数据整理器(差异化获客功能)~~ → ✅ **后端已完成**(§六 6.1 第 6h 项,含串档检测与体检报告)。**剩余**:PC 端页面 | M7-06/07、§13.2 的 5 个接口 |
|
||||
| ~~P1-7~~ | ~~历史数据整理器(差异化获客功能)~~ → 曾完成,**2026-10 已整体删除** | — |
|
||||
| ~~P1-8~~ | ~~操作日志与登录日志查询、字典管理~~ → ✅ **已完成**:操作日志**真的在写**(`sys_operation_log` 实测 523 条,最近一条是几分钟前;F-1 已在前面几轮修掉),写入口统一走 `OperationLogService`;`views/log` 与 `views/dict` 均已可用 | M7-03/04/05、§13.3、§13.4 |
|
||||
| ~~P1-9~~ | ~~任务统计与数据看板~~ → ✅ **已完成**(§六 6.1 第 6j 项 + 本轮 6o 的工作台看板页) | §7.11、M7-01/02 |
|
||||
| ~~P1-10~~ | ~~名册改/删、导入模板下载~~ → ✅ **已完成**:`RosterEditService`(改姓名/证件号/删除,都写操作日志)、名册导入模板下载(表头固定、必填列标红、带 9 条规则说明页);`scripts/e2e-m2-roster.py` 67 条 | §8.3、§8.4、§8.5 |
|
||||
@@ -119,17 +146,17 @@
|
||||
| ~~P1-13~~ | ~~开放接口(认定机构对接)~~ → ✅ **已完成**(§六 6.1 第 6k 项;另补了契约漏掉的接入方管理,见 F-67) | §14 的 4 个接口(HMAC 加签) |
|
||||
| ~~P1-14~~ | ~~学生端 `/mp/login`(小程序端登录)+ 手机号绑定~~ → ✅ **已完成**(§六 6.1 第 6l 项) | §5.3、§4.1 |
|
||||
|
||||
### 4.3 P2 · 上线收尾与二期
|
||||
### 4.3 P2 · 上线收尾(原"与二期"已取消)
|
||||
|
||||
| # | 未完成项 | 说明 |
|
||||
|:--:|---|---|
|
||||
| ~~P2-1~~ | ~~CI/CD + test/prod 环境 + 部署 + 数据迁移预案~~ → ✅ **本轮完成**:`.gitlab-ci.yml`(6 个 job,三阶段;**e2e 阶段跑全套 1516 条断言**,每次随机生成密钥)+ `application-test.yml` / `application-prod.yml`(**零密钥**)+ `ProdEnvCheck`(启动自检,6 条"配错了也能启动"的配置)+ `docs/09-部署与运维.md`(发布 / 密钥轮换 / 迁移与回滚 / 灰度 / 排障 / 首次接入 CI 的检查单) | M0-04、M7-10 |
|
||||
| ~~P2-2~~ | ~~压测与调优~~ → ✅ **本轮完成**:新增 `scripts/load-test.py`(四个场景、按端点分别统计、可调规模);**压测发现并修掉一个会让整间教室被挡住的限流缺陷**(F-92)。结论:**后端没有 N+1、DB 在 2ms 以内**,并发下的延迟是排队效应。尚未测:导出大包、上传并发、5000 人名册、长稳(见 docs/09 §9.5) | M7-09 |
|
||||
| ~~P2-3~~ | ~~定时任务(PRD 附录 E)~~ → ✅ **已完成**:**8 项已实现**(任务自动截止 / 自动催报 / 审核超时提醒 / 原始件清理 / **用量快照** / **压缩失败重试** / **租户到期提醒** / 导出包清理 + 开放接口推送重试),`POST /schedules/run` 可手工触发任意一项。⚠️ 第 9 项「待认领名册清理」**不做** —— 它对应的"自助认领"机制已在 PRD V1.5 废除(见 F-91) | M7-12 |
|
||||
| ~~P2-3~~ | ~~定时任务(PRD 附录 E)~~ → ✅ **已完成**:**8 项已实现**(任务自动截止 / 自动催报 / 审核超时提醒 / 原始件清理 / **用量快照** / **压缩失败重试** / **租户到期提醒** / 导出包清理 + 开放接口推送重试),`POST /schedules/run` 可手工触发任意一项。⚠️ 第 9 项「待认领名册清理」**不做** —— 它对应的"自助认领"机制已在 PRD V1.5 废除(见 F-91)。**二期另加第 9 个任务**「内置角色授权同步」(每日 03:20,`runNow` 名 `role`,见 6v) | M7-12 |
|
||||
| ~~P2-4~~ | ~~用户手册 / 培训材料 / 客服话术~~ → ✅ **本轮完成**:`docs/10-用户手册与客服话术.md`(三角色操作手册 + 高频问题标准回答 + 30 分钟培训提纲 + 上线首日检查单);并新增 `scripts/check-manual.py`(**机械核对手册引用的错误码与提示文案是否真的存在**,已进 CI) | M7-11 |
|
||||
| ~~P2-5~~ | ~~导出包过期清理~~ → ✅ **已完成**:`ExportCleanupService.cleanupExpired()`(`@Scheduled` 每小时第 17 分跑) | M6-07 |
|
||||
| ~~P2-10~~ | ~~导出分卷(`splitSizeMb > 0`)~~ → ✅ **已完成**:`SplitZipWriter` 会产出 `{base}-part1.zip`、`{base}-part2.zip`…;`scripts/e2e-m6-export-split.py` 35 条 | M6-07 |
|
||||
| P2-6 | 二期:社会收集 / 社会评价 / 专项职业能力 | 一期范围外(`collectMode=2`、`certType=2,3` 返回 `40044`)→ 启动时点见 `docs/01` **E3** |
|
||||
| ~~P2-6~~ | ~~二期:社会收集 / 社会评价 / 专项职业能力~~ | **已取消**(2026-10 不再设计二期;传 `collectMode=2`、`certType=2,3` 返回 `40044`) |
|
||||
| ~~P2-7~~ | ~~七牛实时样式缩略图~~ → ✅ **本轮完成**:`StorageService.thumbUrl()`(**参数拼在签名之前**,拼错顺序会 401 且看起来像网络问题);`FileUrl` 同时给 `url` 与 `thumbUrl`;审核端 `?thumb=true`。**实测 1454KB → 36KB(2.4%)**,1600×1200 → 400×300 等比不变形 | M7-09 附带 |
|
||||
| P2-8 | 小程序端 `compressImage` 真机验证;D6 三通道体积上限实测 | 需真机(→ `docs/01` D6) |
|
||||
| ~~P2-9~~ | ~~学生端 H5 没有独立 dev server~~ → ✅ **本轮完成**:`frontend-mp` 加 `/api` 代理 + `.env.development` 把 `VITE_API_BASE` 设空串,`pnpm dev:h5` → http://localhost:5174 可直接联调;三端行为(dev server / 构建产物 / 小程序)都验过,且 **AGENTS.md 里那句过期的说明已同步** | 开发体验 |
|
||||
@@ -174,7 +201,7 @@
|
||||
- **影响**:契约不是"当前系统长什么样",导致每次核对都要重新考古;前端/测试无法以文档为准。
|
||||
⚠️ 更要紧的是:**这个数会继续长**(每加一处界面必需接口就 +1),
|
||||
所以它必须以命令的形式存在,不能以表格的形式存在 —— 见 §九。
|
||||
- **建议**:在 03 文档**新增一节「§17 实现补充与偏差记录」**,逐条列出这些接口(路径 / 权限 / 是否要补进 §4 清单),并在 §4 的合计口径里说明"121 是设计口径,另有 N 个实现补充"。改动后必须重跑 `python scripts/verify-docs.py`(它逐节核对接口数,加节时注意别改动 §4 的声称数)。
|
||||
- **建议**(已落地):在 03 文档新增「§17 实现补充与偏差记录」,逐条列出这些接口,并在 §4 的合计口径里说明"119 是设计口径,另有 38 个实现补充"(2026-10 复核数)。
|
||||
|
||||
- ✅ **已闭环(2026-09-30)**:契约新增 **§17 实现补充与偏差记录** —— 41 个接口按六类分组(清单由 `check-api-parity.py --extra` 生成,写明「手抄件会过期,以命令输出为准」),外加 12 条已确认偏离 D-1~D-12(含租户域名一行、`biz_type` 落库字符串、§14 路径前缀)。
|
||||
|
||||
@@ -255,7 +282,7 @@
|
||||
| 3 | **P1-5 学生端「我的任务 / 撤回 / 进度」** | 后端 `StudentTaskMapper` + `StudentTaskService` + `StudentTaskController`(§9.1 `/mp/tasks`、§9.11 `/mp/submissions/{id}/withdraw`、§9.12 `/mp/submissions/{id}/progress`);学生端新页 `pages/tasks/tasks.vue`(我的填报:待办数/状态/驳回原因/截止提醒/撤回)与 `pages/progress/progress.vue`(材料清单 + 问题 + 时间轴);落地页在"有登录态但没 scene"时改为进「我的填报」 | **`scripts/e2e-m3-mytask.py` → 42/42**;H5 `build:h5` 通过且产物含 `pages-tasks-tasks` / `pages-progress-progress` |
|
||||
| 3b | **P1-4 B1 名单加入申请** | 见 §6.1 上一轮记录(后端 + 学生 H5 + 管理端审批 + V7 迁移) | `scripts/e2e-m2-join.py` → 50/50 |
|
||||
| 4 | **P0-3 班主任端(小程序)看板 + 审核卡片流** | 后端 `TeacherDashboardMapper` + `TeacherDashboardService` + `MpTeacherDashboardController`(§10.1 看板);小程序新页 `pages/dashboard/dashboard.vue`(已交率与已通过**分开呈现**、四个待办角标、班级卡片、截止提醒)、`pages/audit/audit.vue`(卡片流:**右滑通过 / 左滑不绑驳回**、一键通过只带无问题项)、`pages/audit-detail/audit-detail.vue`(材料预览 + 问题清单 + 改动明细 + 驳回弹层:原因快选 + 二次确认);「我的填报」按 `/auth/me` 的角色给出老师入口 | **`scripts/e2e-m5-teacher-dashboard.py` → 24/24**;H5 `build:h5` 通过且产物含 `pages-dashboard-dashboard` / `pages-audit-audit` / `pages-audit-detail-audit-detail` |
|
||||
| 5 | **可复现的接口计数** | `scripts/check-api-parity.py`(契约 §4 的 121 个接口 ↔ 全部 controller,自带"必须恰好 121"的自检) | `python scripts/check-api-parity.py` 退出码 0 |
|
||||
| 5 | **可复现的接口计数** | `scripts/check-api-parity.py`(契约 §4 的 129 个接口 ↔ 全部 controller,自带"必须恰好 129"的自检) | `python scripts/check-api-parity.py` 退出码 0 |
|
||||
| 6 | **界面验收用的演示数据** | `scripts/dev-seed-audit.py`(走真实接口摆出:任务 + 班级 + 材料 + 名册 + 门禁绑定 + 3 份已提交填报单 + 1 份已初审,含一个**演示班主任**账号) | 脚本自身对每步 `code` 断言;审核接口回读确认(见 6.3) |
|
||||
| 6b | **P1-3 通知与消息中心** | 后端 `module/notify/`:`CollectMessage` 实体 + `CollectMessageMapper`(批量已读)+ `SubscribeQuotaMapper`(额度 UPSERT / 原子扣减)+ `NotifyRecordMapper` + `NotificationService`(降级链)+ `NotifyController`(§13.5 消息中心 3 个 + §13.6 订阅额度 1 个);**V8 迁移**新建 `collect_message`;`AuditService` 的通过/驳回真的会通知学生;小程序新增 `pages/messages/messages.vue` + `utils/subscribe.ts`(提交后索取授权) | **`scripts/e2e-m5-notify.py` → 48/48**;H5 `build:h5` 通过且产物含 `pages-messages-messages` |
|
||||
| 6c | **P1-3 剩余:催报与通知记录** | 后端 `UrgeService` + `UrgeMapper` + `UrgeDtos` + 两个 Controller(§8.6 定向/一键催报、§10.7 教师端催报与历史、§13.3 管理端通知记录);`NotificationService` 的发送记录补上 `task_id`(催报去重要用它);PC 新增 `views/notify/index.vue`(通知记录:状态 3 与 2 分开显示)+ [notify.ts](frontend-pc/src/apis/xt/notify.ts) | **`scripts/e2e-m5-urge.py` → 40/40**;`vue-tsc` 0 错、`eslint` 0 错 |
|
||||
@@ -270,6 +297,7 @@
|
||||
|
||||
| 6j | **P1-11 / P1-9 任务克隆、任务统计、规则模板、名册导入模板、审核统计、PC 端代传** | **V15 迁移**给 `collect_rule_template` 补审计列(该表 V1 就建好、列与契约 §7.13 的 FORM/MATERIALS/NAMING 三部分逐项对应,见 F-50)并随迁移带两条**公共模板**;`TaskCloneService`(克隆**配置**、**刻意不克隆业务数据**)、`TaskStatisticsService`(补上 `abnormalCount` 与 `avgAuditHours` —— 这两个字段以前恒为 null,因为问题表与审核记录还不存在)、`RuleTemplateService`(可按 parts 只应用一部分、材料清单**覆盖式合并**)、`RosterTemplateService`(表头固定第 1 行 + 必填列标红 + 9 条填表规则页)、`AuditStatisticsService`、`TeacherProxyService.proxyUploadAsAdmin`;并修掉 F-61(初审待办会列出**批不了**的单子) | **`scripts/e2e-m7-taskconfig.py` → 69/69**(含待办范围判据的回归防线) |
|
||||
| 7 | **全量回归** | — | **33 个脚本 / 1489 条断言 / 0 失败**(753 + 导出 60 + 加入申请 50 + 学生端 42 + 看板 24 + 通知 48 + 催报 40 + 日志 44 + 字典 53 + 分卷 35 + 代传 33 + 整理器 54 + 校验 70 + 任务配置 69 + 开放接口 60 + 组织与文件 54) |
|
||||
| 6v | **二期「角色与授权」(FR-205)** | **V18 迁移**:`sys_role` 加 `user_type`(账号的 `user_type` 一直是从角色反推的,自定义角色不在代码映射里 → 没有这列就"角色建得出来、发不出去")、新建 `sys_role_menu`(菜单不做权限的附属,与 `sys_role_permission` 同构)。**权限判定**:`PermissionChecker` 合并两个来源 —— 内置角色按代码、自定义角色按库(`DbRoleGrantSource`,Redis 缓存 + 写后失效;**空结果也缓存**,否则已删角色的 token 会把库打穿;Redis 挂了退化成直读库,两个方向都安全)。**内置角色授权同步**:`RoleProvisioner` 从"已存在就跳过"改成幂等回填 + `ScheduleJobs` 每日 03:20 同步(修掉"代码改了权限、老租户永远停在旧的那套"这种**不会报错的**漂移)。**8 个接口**(`docs/03` §4.12/§5.10)+ `40045`;学校租户授予平台级权限码 / 配置平台专用菜单 / 建平台运营类角色 → `40301`;**防自锁**(不许去掉自己最后的 `role:manage` 权限或 `role` 菜单、不许停用/删除它、改账号角色时同理保护 `user:manage`)。**PC 页** `views/role/`(内置只读 + 权限矩阵 + 菜单勾选 + 保存即生效)+ 原型 ⑪;账号页的可选角色 / 用户类型 / 角色名全部改读库(删掉前端三份写死的镜像表)。⚠️ **`dataScope` 本期只展示**:范围仍由"权限码 + 任务里被指派了哪些班"决定,收口成 resolver 是下一批 | **新增 `scripts/e2e-m10-role.py` → 77/77**(含"改授权后**同一个 token** 立刻生效""停用角色立刻失效""内置角色只读""越权与自锁""删除守卫 40045""内置角色权限以代码为准 + 同步回填幂等");**浏览器实测**(新建自定义角色 → 账号页出现该角色 → 改权限落库 → 删除);`pnpm typecheck` 0 错、`eslint` 0 错;`verify-docs` / `check-wiring` / `check-api-parity`(契约 129)全绿 |
|
||||
| 6u | **P2-7 缩略图 + P2-9 H5 独立 dev server** | **缩略图**:`StorageService.thumbUrl(path, expire, maxLongSide)`(七牛在**签名之前**拼 `imageView2/2/w/h`;本地存储回退原图,让调用方不必判断"当前是什么存储");`FileUrl` 加 `thumbUrl`/`thumbMaxLongSide`;审核端 `?thumb=true`(**默认 false** —— 详情页要看防伪纹)。**H5 dev server**:`vite.config.ts` 加 `/api` 代理 + `.env.development` 把 `VITE_API_BASE` 设空串(`uni.request` 不能传相对路径,所以空串时仍补 `location.origin`,由代理兜住) | **缩略图实测 1454KB → 36KB(2.4%)**、400×300 等比;新增 `scripts/e2e-m9-thumb.py` → **12/12**(打到真实七牛,含"参数若拼在签名之后会 401"这一条);H5 dev server 实测 5174 可访问且 `/api` 代理到 8080;**H5/小程序两端构建都验过**(小程序产物仍是写死地址) |
|
||||
| 6t | **P2-4 用户手册 / 培训材料 / 客服话术(M7-11)** | 新增 `docs/10-用户手册与客服话术.md`:学生/班主任/管理员三份操作手册 + 「高频问题 → 标准回答」+ 30 分钟培训提纲 + 上线首日检查单。⚠️ 文案与错误码**逐条取自代码**,并在写完后**对着代码核了一遍** —— 抓到两处与实现不符的说法(见 F-95);另把这项核对固化成 `scripts/check-manual.py` 并进 CI | **9 个错误码 + 14 句提示文案全部与代码一致** |
|
||||
| 6s | **P2-2 压测(M7-09)** | 新增 `scripts/load-test.py`(门禁 / 读接口 / 上传 / 导出四场景;**按端点分别统计**;`--rows` 可调规模)。**压测抓到一个真缺陷并修掉**:门禁 IP 限流计的是"所有请求",于是 **60 个不同学生同一出口 IP 同时核验只过了 20 个** —— 而"一个班连同一个 WiFi"正是首次落地第一分钟就会发生的事(见 F-92)。**调优结论(三条独立证据)**:SQL 条数恒为 3(与 pageSize 无关)→ 无 N+1;单请求延迟与页大小无关(20/50/200 → 47/36/44ms);`EXPLAIN ANALYZE` 显示 DB 侧 ≤2ms。所以并发下的 p50 156~375ms 是**排队效应**,要提吞吐得加机器而不是改 SQL | **修前 20/60 → 修后 60/60**;门禁 E2E 仍 29/29 绿 |
|
||||
@@ -277,7 +305,7 @@
|
||||
| 6q | **P2-1 CI/CD + test/prod 环境(M0-04 / M7-10)** | 新增 `.gitlab-ci.yml`(三阶段 6 个 job:机械校验 / 构建(含 H5)/ **全套 E2E 打真库**;密钥**每次随机生成**,顺带证明"应用没有偷偷依赖 dev 默认值");`application-test.yml`(零密钥、允许 clean 以便每次从空库跑全量迁移)、`application-prod.yml`(零密钥、禁 clean、关 Swagger、日志滚动);`ProdEnvCheck`(`EnvironmentPostProcessor`,**跑在任何 bean 之前**);`docs/09-部署与运维.md`;删掉模板残留的 GitHub Pages 工作流(远端是 GitLab、用 pnpm、且它发布的是没有 API 的静态页) | **新增 `scripts/e2e-prodguard.py` → 9/9**(三种误配形态都拒绝启动)+ `scripts/check-test-profile.py`(test profile 真起一次,可重复跑);本地照跑 CI 的 check/build 各步骤全通过 |
|
||||
| 6p | **P2-3 定时任务(PRD 附录 E 的四项)** | 新建 `module/schedule/`:`ScheduleMapper`(**跨租户的候选查询集中在这一个文件**,全部 `@InterceptorIgnore` + 只读;别名一律带双引号,见 F-83)+ `ScheduleJobs`(四个 `@Scheduled`,每个都是"先以平台身份扫候选 → 再逐个租户摆上下文干活"的两段式)+ `ScheduleController`(`POST /schedules/run` 手工触发,权限用运营侧的 `log:view:all`)。四项:**任务自动截止**(`end_time < CURRENT_DATE`,动作名 `AUTO_CLOSE` 以区分「系统关的」与「管理员关的」)、**自动催报**(7/3/1 天,`end_time = 那天` 而非 `<=`,只催未提交)、**审核超时提醒**(时效与 §11.6 同口径,发给"真的要动手的那个人",**找不到班主任时升级给学校管理员**)、**原始件清理**(先清引用再删文件)。另给 `UrgeService` 加 `autoUrge`、`TaskService` 加 `autoClose` | **新增 `scripts/e2e-m7-schedule.py` → 27/27** |
|
||||
| 6o | **P1-9 工作台数据看板(PC)** | 工作台底部原来是一张「开发进度」卡,写着「M0 后端已完成,界面建设中」—— 而项目已是 120/121 个接口可用、10 个 PC 页面可点。**首页上说一件不真的事,比空着更糟**,所以换成真实看板:`apis/xt/task.ts` 加 `getTaskStatistics` + `TASK_STATUS_TEXT`(状态文案放一处)、`apis/xt/audit.ts` 加 `getAuditStatistics`;`views/dashboard/index.vue` 加"选任务 → 进度四格(应填/已提交/已通过/待审)+ 分班进度 + 审核情况 + 驳回原因分布"。**提交率与通过率分两列**(已提交 ≠ 已通过);平均审核时长按量级换单位显示(`null`=还没有样本 / 0=不足 1 分钟 / 否则分钟或小时) | **浏览器实测**:应填 3 / 已提交 3 / 提交率 100.0% / 已通过 3 / 通过率 100.0% / 待审 0,分班表与审核表都出数;`pnpm typecheck` 通过、`eslint` 0 错 |
|
||||
| 6n | **「待建」页不再说谎(3 个"功能已就绪、入口在别处"的菜单项)** | `名单管理 / 材料规则 / 表单设计` 三个菜单项对应的功能**早就做好了**(`TaskRosterDrawer` / `TaskMaterialDrawer` / `TaskFormSchemaDrawer`,入口在「收集任务」每一行的链接里),但点进菜单看到的一直是「**这个页面还没做**」+ 一句早已过期的"已实现的页面:工作台、组织架构、租户管理"。**说"还没做"而功能其实在隔壁,比找不到更糟 —— 它让人得出错误结论。** 给 `XtMenu` 加可选的 `entryAt`,待建页据此分两种说法:有 `entryAt` → 「功能已就绪」+ 真实入口指路 + 跳转按钮;没有 → 原来的「还没做」(`角色与授权` 就是这个:权限码由代码决定、自定义角色属二期,确实没做)。并删掉那句**会静默过期**的清单 —— 任何写死在代码里的"现状"都会过期,而没人会记得回来改它 | **浏览器实测四个菜单项**:三个显示「功能已就绪」并可一键跳到「收集任务」,`角色与授权` 显示「二期」;`pnpm typecheck` 通过、`eslint` 0 错 |
|
||||
| 6n | **「待建」页不再说谎(3 个"功能已就绪、入口在别处"的菜单项)** | `名单管理 / 材料规则 / 表单设计` 三个菜单项对应的功能**早就做好了**(`TaskRosterDrawer` / `TaskMaterialDrawer` / `TaskFormSchemaDrawer`,入口在「收集任务」每一行的链接里),但点进菜单看到的一直是「**这个页面还没做**」+ 一句早已过期的"已实现的页面:工作台、组织架构、租户管理"。**说"还没做"而功能其实在隔壁,比找不到更糟 —— 它让人得出错误结论。** 给 `XtMenu` 加可选的 `entryAt`,待建页据此分两种说法:有 `entryAt` → 「功能已就绪」+ 真实入口指路 + 跳转按钮;没有 → 原来的「还没做」(`角色与授权` 就是这个:权限码由代码决定、自定义角色属二期,确实没做)。并删掉那句**会静默过期**的清单 —— 任何写死在代码里的"现状"都会过期,而没人会记得回来改它 | **浏览器实测四个菜单项**:三个显示「功能已就绪」并可一键跳到「收集任务」,`角色与授权` 显示「二期」;`pnpm typecheck` 通过、`eslint` 0 错 → ✅ **已闭环(2026-10-09,二期)**:「角色与授权」已实现(见 6v),`ready:false` 的菜单**归零**,「待建」页只作为兜底保留 |
|
||||
| 6m | **P2-04 历史整理器页面(PC)** | 新增 `frontend-pc/src/apis/xt/cleanup.ts`(含**报告口径**:`headlineOf` 拼"112 个文件里 90 个全自动处理,只有 1 个必须你亲自判断",但它拼的是**服务端算好的两个数**,不是前端减出来的 —— 前端减出来的早晚会与后端分叉;下载走 api 层,与导出中心同一写法)+ `views/cleanup/index.vue`(列表 / 上传 / 进度轮询 / 报告 / 难点清单 / 改名样例 / 证件号冲突 / 下载 / 导入);路由 `ready: true`。**实机验证**(浏览器里真的点了一遍):列表出数据、点详情出报告、压缩率显示 `—`、重点条目带建议、导入按钮可见 | `pnpm typecheck` 通过;`eslint` 0 错;**截图留证** |
|
||||
| 6l | **P1-14 / P1-8 / P1-10 小程序登录、组织导入、租户用量、文件视图·直传凭证·批量处理** | `MpLoginService`(`jscode2session` + **复用** H5 的 `resolveUser`,不复制一份建账号逻辑)+ 手机号绑定(手机号**从客户端到不了服务端**,所以能作凭据;一个号只能绑一个账号);`OrgImportService`(父子关系用**编码 + 上级编码**、两遍扫描支持"子行在父行之前"、补 `ancestor_path`);`TenantUsageService`(周期支持月/季;`ocrCount`/`smsCount` **恒为 0 并带 notes 说明**);`MaterialFileViewService`(§9.5 预检+秒传+压缩提示、§9.7 批量处理、§9.8 预览带 `actualKind`);并修掉 F-73(秒传排在份数检查之后 → 网络重传得到"你超了"而不是"你已经传过了") | **`scripts/e2e-m8-orgfile.py` → 54/54** |
|
||||
| 6k | **P2-05 开放接口(§14,供认定机构对接)** | **V16 迁移**新建 `collect_open_app`(密钥存 AES 密文 —— HMAC 要能还原,不能只存哈希)与 `collect_open_push_log`(`next_retry_at` **落库**,内存定时器在重启后就没了);`OpenSignService`(HMAC-SHA256 + 5 分钟时间窗 + nonce 防重放,**先验签再占 nonce**)、`OpenDataService`(§14.2/§14.3,**一律不返回明文证件号**、签名 URL 30 分钟)、`OpenPushService`(§14.4 推送 + 定时重试 + 人工重推,推送挂在 `afterCommit` 上)、`OpenAppAdminController`(契约漏掉的接入方管理,见 F-67);并把规则模板的两个接口**修回契约路径** `/api/v1/rule-templates`(见 F-68) | **`scripts/e2e-m8-open.py` → 60/60** |
|
||||
|
||||
+14
-1
@@ -97,7 +97,7 @@ curl -sf http://127.0.0.1:8080/actuator/health # 探活用这个,不是 /
|
||||
换密钥之后**旧密文解不开**,而系统不会报错 —— 它只会显示一串乱码,
|
||||
或者门禁核验永远匹配不上(`id_card_hash` 也变了)。
|
||||
|
||||
正确做法(本项目尚未实现,属二期工程):
|
||||
正确做法(本项目**缓做**,等 OCR 供应商选型;**不再作为二期功能设计**):
|
||||
1. 新密钥加进来(配置支持"当前密钥 + 历史密钥列表");
|
||||
2. 写一个离线任务用旧密钥解密、新密钥重加密,并**同时重算 hash 与 mask**;
|
||||
3. 全部迁完之后,旧密钥才下线。
|
||||
@@ -468,3 +468,16 @@ python scripts/load-test.py --only soak --rows 2000 --soak 60 --rps 5
|
||||
| **多副本部署下的导出打包** | 本机只有一个实例 | 两实例同时导出同一任务,确认不会互相覆盖产物(导出线程池与落盘路径都要看) |
|
||||
| **5000 人**(不是 5000 行)**的全链路** | 5000 行导入已测(9.5.2),但"5000 人都提交 + 审核 + 导出"是另一件事 | 按 9.5.3 的 `export-big` 放大到 5000,注意先看导出分卷与磁盘占用 |
|
||||
| **生产规模下的长跑(数小时~数天)** | 60 分钟只够发现"快速泄漏" | 在 staging 按 9.5.4 的 soak 跑 12~24 小时,看 RSS 与 PG 连接数是否仍然平稳 |
|
||||
|
||||
---
|
||||
|
||||
## 附:PC 管理端构建(2026-10 重搭后)
|
||||
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| 技术栈 | **纯 Vue 3 + Naive UI 2.42**(已无 `@arco-design/*` 与 Gi 模板依赖) |
|
||||
| 依赖安装 | `pnpm install --frozen-lockfile`(`pnpm-lock.yaml` 随新 `package.json` 更新,**别用 npm**) |
|
||||
| 构建 | `pnpm build` → `frontend-pc/dist/`(静态托管,`/api` 由网关/反代指向后端 8080) |
|
||||
| 检查 | `pnpm typecheck`(**0 错**)、`pnpm lint`(ESLint;**Stylelint 已随重搭移除**) |
|
||||
| 图标 | `scripts/extract-pc-icons.py` 生成 `src/icons/icon-data.ts`(37 个,内网离线可用);**图标集升级后必须重跑**,否则新图标是空占位且不报错 |
|
||||
| 主题 | 主题配置在 `src/utils/naive.ts` 一侧;`src/styles/index.scss` 只放跨页面样式 |
|
||||
|
||||
+34
-19
@@ -48,7 +48,7 @@
|
||||
那不是你的问题 —— 是同一个 WiFi 下有人多次试错,等一分钟即可。
|
||||
- 如果提示「**该信息已被登记**」(`40072`):说明这个名册项已经被**另一个微信号**绑定过。
|
||||
常见原因:你用两个微信号都扫了码。**联系班主任处理**,不要重复试。
|
||||
- 如果名册里确实没有你 → 点「**申请加入名单**」,由管理员审批后入册(见 2.4)。
|
||||
- 如果名册里确实没有你 → **联系班主任**,请他在任务的「名单管理」里把你加进去(⚠️ 系统里**不再有自助申请入口**,也不会展示任何联系方式 —— 见 2.4)。
|
||||
|
||||
**③ 填表**
|
||||
- 表里有一部分**已经替你填好了**(报考职业/工种/等级/学校/专业等)——
|
||||
@@ -80,20 +80,21 @@
|
||||
| 操作过于频繁,请稍后再试 | 试错次数太多(同一个人 1 小时 10 次) | 等几分钟,或让班主任核对 |
|
||||
| 当前网络下尝试核验的人较多 | **不是你的问题**:同一 WiFi 有人多次试错 | 等 1 分钟 |
|
||||
| 该信息已被登记 | 这个名册项已被另一个微信号绑定 | 联系班主任解绑或处理 |
|
||||
| 你已在名单中,请返回重新核验 | 你已经在名册里了,不用申请加入 | 返回上一步直接核验 |
|
||||
| 你已提交过申请,请耐心等待 | 加入申请重复提交 | 等管理员审批 |
|
||||
| 这份材料可能不是你的,请重新上传 | 系统发现材料上的证件号与你不一致 | 确认没传错人的材料,重传 |
|
||||
| 已进入审核,需先撤回才能删材料 | 你已提交,材料被锁 | 点「撤回修改」后再改 |
|
||||
| 已通过终审,不可撤回 | 已经审完了 | 联系学校管理员 |
|
||||
| 任务已截止、已归档或已暂停 | 这个任务不能再填了 | 联系班主任 |
|
||||
|
||||
### 2.4 名册里没有我(申请加入名单)
|
||||
### 2.4 名册里没有我,怎么办
|
||||
|
||||
1. 核验页点「**申请加入名单**」;
|
||||
2. 填姓名、身份证号、班级、联系电话,可附说明;
|
||||
3. 管理员在管理端「**加入申请**」里审批;
|
||||
4. 通过后你会收到消息,回到核验页重新核验即可。
|
||||
- ⚠️ 申请**次数有上限**(`40037`),别反复提交;重复提交会提示「你已提交过申请,请耐心等待」。
|
||||
1. **先核对**姓名与身份证号是不是和老师给的一致(生僻字、空格、末位 X 大小写最常见);
|
||||
2. 核对无误后,**联系班主任**,请他在任务里把你加进名册
|
||||
(管理端「收集任务 → 名单管理 → 追加导入」);
|
||||
3. 老师加好后,回到核验页**重新核验**即可填报。
|
||||
|
||||
> ⚠️ **系统里没有"申请加入名单"这个入口**(2026-10 需求收敛时已取消),
|
||||
> 也**不会展示任何联系方式** —— 学生本来就在班级群里,找老师即可。
|
||||
> 这样守住的仍是同一条规则:**没有名册,就不能填报**;只是"加人"这个动作留在老师手里。
|
||||
|
||||
---
|
||||
|
||||
@@ -166,16 +167,29 @@
|
||||
- 通过后导出,产物保留 **7 天**,过期后需重新导出(会明确提示「导出包已过期(保留 7 天),请重新导出」)。
|
||||
- 几百人以上会自动**分卷**(`-part1.zip`、`-part2.zip`…),每一卷都是**完整可解**的压缩包。
|
||||
|
||||
### 4.4 历史整理器(把以前那堆乱文件夹变成能用的数据)
|
||||
### 4.4 角色与授权(给不同的人不同的能力范围)
|
||||
|
||||
「配置 → **历史整理器**」→ 上传一个 zip:
|
||||
1. 填任务名;
|
||||
2. **关联一个已有任务的名册**(⚠️ 这一步决定了报告的价值,见下);
|
||||
3. 等它跑完 → 看报告。
|
||||
「组织与账号 → **角色与授权**」→ 左边选一个角色,右边看/改它能做什么。
|
||||
|
||||
报告里最该看的一句是「**N 个文件里,X 个系统全自动处理,只有 Y 个必须你亲自判断**」。
|
||||
- **有关联名册**才有「证件号校验」与「疑似串档检测」—— 而"疑似串档 1 例"是报告里最有价值的一条;
|
||||
- 不关联也能跑,但只能做文件名规范化。
|
||||
| 你想干的事 | 怎么做 |
|
||||
|---|---|
|
||||
| 看看某个角色到底能做什么 | 点它 → 右边列出全部权限码与菜单(**内置角色**就在这一屏看,改不了) |
|
||||
| 让某类人只能看任务、不能建任务 | **新建**一个自定义角色 → 勾「任务查看」(**不要**勾「创建与配置收集任务」)→ 再把它发给账号 |
|
||||
| 让某个人只看得到自己带的班 | 勾带「本班/本人」字样的权限(如「名册修改(仅本班)」);**看全租户**的能力是「填报进度看板(全租户)」这类码 |
|
||||
| 改完什么时候生效 | **立即生效**,不需要让人重新登录(保存后让他刷新一下页面即可) |
|
||||
| 某个人不该再管账号了 | 账号管理里把他的角色换掉(或停用那个自定义角色) |
|
||||
|
||||
**三条要记住的规矩**:
|
||||
|
||||
1. **内置角色不能改**。它的权限写在后端代码里 —— 这是刻意的:一次误操作就能把管理员锁在系统外。
|
||||
要不同的能力范围,请**新建一个自定义角色**(可以照着内置的那份矩阵勾)。
|
||||
2. **「看得见」和「有权限」是两件事**。菜单只决定左边显示哪些入口;真正的权限是上面那一栏的权限码。
|
||||
只给菜单不给权限,点进去仍然会被拦住(提示"无权限访问")。
|
||||
3. **删角色前要先给账号换角色**。还有账号在用时删不掉,会提示「该角色还有 N 个账号在用,请先改掉这些账号的角色」。
|
||||
|
||||
> ⚠️ **「数据范围」(本人 / 本班 / 本院系 / 全租户)这一列目前只展示**:
|
||||
> 范围由"拿到了哪些权限码 + 任务里被指派了哪些班"共同决定,**不靠这一列**。
|
||||
> 想让人看全租户,请给他勾「填报进度看板(全租户)」这类权限。
|
||||
|
||||
### 4.5 其它
|
||||
|
||||
@@ -184,6 +198,7 @@
|
||||
| 加/停用账号、重置密码 | 组织与账号 → 账号管理 |
|
||||
| 改学校/院系/班级结构 | 组织与账号 → 组织架构(也支持 Excel 导入) |
|
||||
| 加一个下拉选项(如多了种学历写法) | 配置 → 字典管理(**内置项不可改名**,要加就追加自定义项) |
|
||||
| 给某类人配一套能力范围 | 组织与账号 → 角色与授权(见 §4.5) |
|
||||
| 查"谁在什么时候看了证件号/导出了什么" | 平台运维 → 操作日志 |
|
||||
| 查"通知到底发出去没有" | 收集管理 → 通知记录 |
|
||||
| 看某个任务的分班进度 | 工作台 → 数据看板(选一个任务) |
|
||||
@@ -195,7 +210,7 @@
|
||||
### 5.1 三条必须能讲清楚的产品规则
|
||||
|
||||
1. **必须先有名册**(门禁铁律)。名单外的人填不了 —— 这是为了防冒名与串档,
|
||||
不是系统限制。名单外的人走「申请加入名单」。
|
||||
不是系统限制。**名单外的人由老师手工加进名册**(系统内没有自助申请入口,也不展示联系方式)。
|
||||
2. **材料是自动改名与自动压缩的**。学生传的文件名乱、体积大都不是问题;
|
||||
交付物是压缩后的"规范件"。
|
||||
3. **驳回必须写原因**,原因会原样发给学生。所以客服看到"学生说不知道为什么被驳回",
|
||||
@@ -206,7 +221,7 @@
|
||||
**Q:学生说"扫码进去说找不到我的信息"**
|
||||
A:这是名册门禁。请班主任核对名册上他的**姓名写法**与**身份证号**(常见是生僻字、
|
||||
空格、身份证号末位 X 大小写)。系统**故意不告诉**是姓名错还是号码错。
|
||||
如果确实不在名册里,让他点「申请加入名单」。
|
||||
如果确实不在名册里,让班主任在任务的「名单管理」里把他加进去(学生端没有自助入口)。
|
||||
|
||||
**Q:学生说"提示操作过于频繁"**
|
||||
A:分两种,看文案:
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
-- ============ 1) 建表 ============
|
||||
--
|
||||
-- ⚠️ 2026-10 需求收敛:本文件是**初始建表稿**(V1 口径),**不是当前库结构的完整描述**。
|
||||
-- 已删除的对象见 `backend/xt-server/src/main/resources/db/migration/V19__remove_deputy_auditor_cleanup_join.sql`:
|
||||
-- · `collect_task_org.deputy_user_ids`(协办人列)—— 已 DROP
|
||||
-- · `collect_cleanup_job`(历史整理器)—— 已 DROP
|
||||
-- · `collect_join_request`(加入申请)—— 已 DROP
|
||||
-- 下面这三处的建表语句**刻意保留**(历史沿革可追溯),但新库是靠 V1→V19 全量迁移得到的,
|
||||
-- 最终结构里没有它们。要核对"当前真实结构",请查 `information_schema` 或跑
|
||||
-- `python scripts/check-entity-columns.py`(实体 ↔ 建表一致性)。
|
||||
|
||||
CREATE TABLE sys_tenant (
|
||||
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
||||
|
||||
Vendored
+127
-212
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>填报通 · 管理端(PC)交互原型</title>
|
||||
<title>深学在线 · 管理端(PC)交互原型</title>
|
||||
<style>
|
||||
:root{
|
||||
--ink:#1b2430; --ink2:#4a5768; --ink3:#8b95a3; --ink4:#b3b9c1;
|
||||
@@ -76,8 +76,12 @@ figcaption .note{display:block;margin-top:8px;color:var(--ink3);font-size:12px}
|
||||
}
|
||||
.brand{padding:17px 17px 15px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:9px}
|
||||
.brand .mark{
|
||||
width:28px;height:28px;border-radius:7px;background:var(--ink);color:#fff;flex:none;
|
||||
display:flex;align-items:center;justify-content:center;font-family:var(--font-display);font-size:15px;font-weight:600;
|
||||
width:28px;height:28px;border-radius:7px;flex:none;
|
||||
/* 深学在线 标识:方(卷宗)+ 环(在线)。与 frontend-pc 的
|
||||
src/assets/images/logo.svg 同一形状,这里内嵌是为了原型
|
||||
「单文件自包含、零外部依赖」—— 不能引外部图片。
|
||||
原型自己的色板是墨色,所以底仍用 var(--ink),只取白色形状。 */
|
||||
background:var(--ink) url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'><circle cx='36.25' cy='26.62' r='16.31' fill='none' stroke='%23fff' stroke-width='5.9' stroke-opacity='.76'/><rect x='8.5' y='22.65' width='34' height='34' rx='10.4' fill='%23fff'/></svg>") center/112% no-repeat;
|
||||
}
|
||||
.brand .bn{font-family:var(--font-display);font-size:15px;font-weight:600;letter-spacing:.05em}
|
||||
.brand .bs{font-size:9.5px;color:var(--ink4);font-family:var(--font-mono);letter-spacing:.08em;margin-top:2px}
|
||||
@@ -288,9 +292,9 @@ td.num{font-family:var(--font-mono);text-align:right}
|
||||
|
||||
<header class="masthead">
|
||||
<div class="kicker">Prototype · 管理端 PC · V1.2</div>
|
||||
<h1>填报通 · <em>管理端</em>交互原型</h1>
|
||||
<h1>深学在线 · <em>管理端</em>交互原型</h1>
|
||||
<p class="sub">
|
||||
11 屏:<strong>工作台 → 任务向导(命名试跑) → 名单导入 → 报考范围 → 数据看板 → 终审 → 导出中心</strong>,加上 <strong>表单设计器 / 材料规则配置 / 历史数据整理器 / 名单加入申请审批</strong>。<br>
|
||||
10 屏:<strong>工作台 → 任务向导(命名试跑) → 名单导入 → 报考范围 → 数据看板 → 终审 → 导出中心</strong>,加上 <strong>表单设计器 / 材料规则配置 / 角色与授权</strong>。<br>
|
||||
移动端是「档案室」,桌面端往「<strong>台账</strong>」方向走——表格、树形、批量操作。信息密度比手机端高一个量级,因为管理员是坐在电脑前做批量决策的人。<br>
|
||||
<span style="font-size:12.5px;color:var(--ink3)">后三屏是**追溯矩阵审计后补的**:三者都是复杂交互,光靠文字描述开发会理解偏。</span>
|
||||
</p>
|
||||
@@ -313,7 +317,7 @@ td.num{font-family:var(--font-mono);text-align:right}
|
||||
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks</div>
|
||||
</div><div class="view">
|
||||
<aside class="side">
|
||||
<div class="brand"><div class="mark">填</div><div><div class="bn">填报通</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<nav class="nav">
|
||||
<a class="on"><svg class="ico"><use href="#i-home"/></svg>工作台</a>
|
||||
<div class="grp">收集管理</div>
|
||||
@@ -390,7 +394,7 @@ td.num{font-family:var(--font-mono);text-align:right}
|
||||
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks/new?step=7</div>
|
||||
</div><div class="view">
|
||||
<aside class="side">
|
||||
<div class="brand"><div class="mark">填</div><div><div class="bn">填报通</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<nav class="nav">
|
||||
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
|
||||
<div class="grp">收集管理</div>
|
||||
@@ -528,7 +532,7 @@ td.num{font-family:var(--font-mono);text-align:right}
|
||||
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks/1/roster/import</div>
|
||||
</div><div class="view">
|
||||
<aside class="side">
|
||||
<div class="brand"><div class="mark">填</div><div><div class="bn">填报通</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<nav class="nav">
|
||||
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
|
||||
<div class="grp">收集管理</div>
|
||||
@@ -635,7 +639,7 @@ td.num{font-family:var(--font-mono);text-align:right}
|
||||
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks/1/scope</div>
|
||||
</div><div class="view">
|
||||
<aside class="side">
|
||||
<div class="brand"><div class="mark">填</div><div><div class="bn">填报通</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<nav class="nav">
|
||||
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
|
||||
<div class="grp">收集管理</div>
|
||||
@@ -734,7 +738,7 @@ td.num{font-family:var(--font-mono);text-align:right}
|
||||
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks/1/dashboard</div>
|
||||
</div><div class="view">
|
||||
<aside class="side">
|
||||
<div class="brand"><div class="mark">填</div><div><div class="bn">填报通</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<nav class="nav">
|
||||
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
|
||||
<div class="grp">收集管理</div>
|
||||
@@ -834,7 +838,7 @@ td.num{font-family:var(--font-mono);text-align:right}
|
||||
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/review?status=pending_final</div>
|
||||
</div><div class="view">
|
||||
<aside class="side">
|
||||
<div class="brand"><div class="mark">填</div><div><div class="bn">填报通</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<nav class="nav">
|
||||
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
|
||||
<div class="grp">收集管理</div>
|
||||
@@ -913,7 +917,7 @@ td.num{font-family:var(--font-mono);text-align:right}
|
||||
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/export/new</div>
|
||||
</div><div class="view">
|
||||
<aside class="side">
|
||||
<div class="brand"><div class="mark">填</div><div><div class="bn">填报通</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<nav class="nav">
|
||||
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
|
||||
<div class="grp">收集管理</div>
|
||||
@@ -1017,7 +1021,7 @@ td.num{font-family:var(--font-mono);text-align:right}
|
||||
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks/new?step=4</div>
|
||||
</div><div class="view">
|
||||
<aside class="side">
|
||||
<div class="brand"><div class="mark">填</div><div><div class="bn">填报通</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<nav class="nav">
|
||||
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
|
||||
<div class="grp">收集管理</div>
|
||||
@@ -1155,7 +1159,7 @@ td.num{font-family:var(--font-mono);text-align:right}
|
||||
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/tasks/new?step=5</div>
|
||||
</div><div class="view">
|
||||
<aside class="side">
|
||||
<div class="brand"><div class="mark">填</div><div><div class="bn">填报通</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<nav class="nav">
|
||||
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
|
||||
<div class="grp">收集管理</div>
|
||||
@@ -1254,240 +1258,151 @@ td.num{font-family:var(--font-mono);text-align:right}
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- ⑩ 历史数据整理器 -->
|
||||
<!-- ⑪ 角色与授权 -->
|
||||
<figure>
|
||||
<div class="frame-wrap"><div class="origin">
|
||||
<div class="win"><div class="wbar">
|
||||
<div class="lights"><i></i><i></i><i></i></div>
|
||||
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/cleanup</div>
|
||||
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/roles</div>
|
||||
</div><div class="view">
|
||||
<aside class="side">
|
||||
<div class="brand"><div class="mark">填</div><div><div class="bn">填报通</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<div class="brand"><div class="mark"></div><div><div class="bn">深学在线</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<nav class="nav">
|
||||
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
|
||||
<div class="grp">收集管理</div>
|
||||
<a><svg class="ico"><use href="#i-task"/></svg>收集任务</a>
|
||||
<a><svg class="ico"><use href="#i-users"/></svg>名单管理</a>
|
||||
<a><svg class="ico"><use href="#i-check-circle"/></svg>终审<span class="nb">23</span></a>
|
||||
<a><svg class="ico"><use href="#i-chart"/></svg>数据看板</a>
|
||||
<a><svg class="ico"><use href="#i-download"/></svg>导出中心</a>
|
||||
<div class="grp">工具</div>
|
||||
<a class="on"><svg class="ico"><use href="#i-wand"/></svg>历史数据整理</a>
|
||||
</nav>
|
||||
<div class="who"><div class="av">王</div><div><div class="wn">王老师</div><div class="wr">学校管理员</div></div></div>
|
||||
</aside>
|
||||
<div class="main">
|
||||
<div class="topbar"><h2>历史数据整理</h2><span class="crumb">把已有的乱文件夹,一键变成规范包</span></div>
|
||||
<div class="scroll">
|
||||
<div class="note info" style="margin-bottom:16px">
|
||||
<svg class="ico"><use href="#i-bulb"/></svg>
|
||||
<div><b>不需要重新收集。</b>把手上已有的压缩包或文件夹传上来,系统会用与正式收集<b>完全相同的规则</b>改名、压缩、体检,输出一份可直接上报的规范包 + 一份问题清单。</div>
|
||||
</div>
|
||||
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;align-items:start">
|
||||
<div class="card">
|
||||
<header>① 上传<span class="sp"><span class="pill ok"><svg class="ico"><use href="#i-check"/></svg>已解析</span></span></header>
|
||||
<div class="body">
|
||||
<div style="display:flex;align-items:center;gap:12px;padding:13px;border:1px solid var(--line);border-radius:9px;background:#faf9f6;margin-bottom:12px">
|
||||
<svg class="ico" style="font-size:24px;color:var(--ink4)"><use href="#i-folder"/></svg>
|
||||
<div style="flex:1">
|
||||
<div style="font-size:13px;font-weight:600;font-family:var(--font-mono)">24中技数控1班.rar</div>
|
||||
<div style="font-size:11px;color:var(--ink3);margin-top:3px;font-family:var(--font-mono)">436 MB · 4 个目录 · 112 个文件</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note ok" style="margin-bottom:0">
|
||||
<svg class="ico"><use href="#i-check-circle"/></svg>
|
||||
<div>已按<b>「姓名-证件号-材料名」</b>解析出 28 名学生的 4 类材料,无需命名规范即可识别。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<header>② 定期体检报告<span class="sp"><span class="hint">用真实数据实测得出</span></span></header>
|
||||
<div class="body" style="padding:0">
|
||||
<table>
|
||||
<thead><tr><th>问题类型</th><th style="width:70px">数量</th><th style="width:96px">占比</th><th style="width:100px">处理</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>命名违规</td><td class="num" style="font-weight:600">59</td><td class="mono">52.7%</td><td><span class="pill auto">自动修复</span></td></tr>
|
||||
<tr><td>体积超标(>1M)</td><td class="num" style="font-weight:600">31</td><td class="mono">27.7%</td><td><span class="pill auto">自动压缩</span></td></tr>
|
||||
<tr><td>证件号与名单不符</td><td class="num" style="font-weight:600;color:var(--seal)">5</td><td class="mono">17.9%</td><td><span class="pill warn">3 自动 · 2 待确认</span></td></tr>
|
||||
<tr style="background:var(--seal-soft)"><td>疑似串档</td><td class="num" style="font-weight:600;color:var(--seal)">1</td><td class="mono">3.6%</td><td><span class="pill crit">必须人工</span></td></tr>
|
||||
<tr><td>分辨率不足</td><td class="num">1</td><td class="mono">3.6%</td><td><span class="pill warn">提示重传</span></td></tr>
|
||||
<tr><td>散图待归属</td><td class="num">0</td><td class="mono">—</td><td><span class="pill gray">—</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="body" style="border-top:1px solid var(--line);padding-top:13px">
|
||||
<div class="note info" style="margin-bottom:0">
|
||||
<svg class="ico"><use href="#i-bulb"/></svg>
|
||||
<div><b>112 个文件里 59 个需要改名、31 个需要压缩,但只有 1 个必须你亲自判断。</b>其余 90 个系统全自动处理。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<header>③ 输出结果<span class="sp"><span class="pill jade" style="background:var(--jade);color:#fff">规范化包已生成</span></span></header>
|
||||
<div class="body">
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;margin-bottom:16px">
|
||||
<div class="stat" style="border-color:#cfe0d6;background:var(--jade-soft)"><div class="lbl">原始总量</div><div class="num" style="font-size:22px;color:var(--ink2)">475 MB</div></div>
|
||||
<div class="stat" style="border-color:#cfe0d6;background:var(--jade-soft)"><div class="lbl">规范化后</div><div class="num" style="font-size:22px;color:var(--jade)">37 MB</div></div>
|
||||
<div class="stat" style="border-color:#cfe0d6;background:var(--jade-soft)"><div class="lbl">压缩率</div><div class="num" style="font-size:22px;color:var(--jade)">92%</div></div>
|
||||
</div>
|
||||
<div style="display:flex;gap:10px">
|
||||
<button class="btn jade"><svg class="ico"><use href="#i-download"/></svg>下载规范化包</button>
|
||||
<button class="btn ghost"><svg class="ico"><use href="#i-download"/></svg>下载体检报告(Excel)</button>
|
||||
<button class="btn ghost"><svg class="ico"><use href="#i-check"/></svg>把结果导入为正式任务</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div></div></div></div>
|
||||
<figcaption>
|
||||
<span class="tag">STEP 10 · 差异化获客功能</span>
|
||||
<span class="title">历史数据整理器:让老师先看到价值,再谈上线</span>
|
||||
客户手里往往已经有一堆历史乱数据(本例是 <b style="font-family:var(--font-mono)">436MB</b> 的 rar)。<b>不需要重新收集</b>,直接传上来就能产出规范包和问题清单——这是最有力的演示,也是最有效的获客入口。
|
||||
<span class="note">报告里的数字<b>全部来自真实实测</b>:命名违规 59、体积超标 31、证件号异常 5(其中 3 个可自动、2 个待确认)、疑似串档 1、分辨率不足 1。最后那句"<b>90 个系统全自动处理,只有 1 个必须你亲自判断</b>",比任何功能列表都有说服力。</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- ⑪ 名单加入申请审批(客户确认新增) -->
|
||||
<figure>
|
||||
<div class="frame-wrap"><div class="origin">
|
||||
<div class="win"><div class="wbar">
|
||||
<div class="lights"><i></i><i></i><i></i></div>
|
||||
<div class="url"><svg class="ico"><use href="#i-shield"/></svg>https://admin.tianbaotong.cn/roster/join-requests</div>
|
||||
</div><div class="view">
|
||||
<aside class="side">
|
||||
<div class="brand"><div class="mark">填</div><div><div class="bn">填报通</div><div class="bs">ADMIN CONSOLE</div></div></div>
|
||||
<nav class="nav">
|
||||
<a><svg class="ico"><use href="#i-home"/></svg>工作台</a>
|
||||
<div class="grp">收集管理</div>
|
||||
<a><svg class="ico"><use href="#i-task"/></svg>收集任务</a>
|
||||
<a class="on"><svg class="ico"><use href="#i-users"/></svg>名单管理<span class="nb">5</span></a>
|
||||
<a><svg class="ico"><use href="#i-check-circle"/></svg>终审<span class="nb">23</span></a>
|
||||
<a><svg class="ico"><use href="#i-chart"/></svg>数据看板</a>
|
||||
<a><svg class="ico"><use href="#i-download"/></svg>导出中心</a>
|
||||
<div class="grp">工具</div>
|
||||
<a><svg class="ico"><use href="#i-wand"/></svg>历史数据整理</a>
|
||||
<div class="grp">组织与账号</div>
|
||||
<a><svg class="ico"><use href="#i-org"/></svg>组织架构</a>
|
||||
<a><svg class="ico"><use href="#i-users"/></svg>账号管理</a>
|
||||
<a class="on"><svg class="ico"><use href="#i-lock"/></svg>角色与授权</a>
|
||||
<div class="grp">平台运维</div>
|
||||
<a><svg class="ico"><use href="#i-list"/></svg>操作日志</a>
|
||||
</nav>
|
||||
<div class="who"><div class="av">王</div><div><div class="wn">王老师</div><div class="wr">学校管理员</div></div></div>
|
||||
</aside>
|
||||
<div class="main">
|
||||
<div class="topbar">
|
||||
<h2>名单加入申请</h2><span class="crumb">24中技数控1班 · 2026年9月认定</span>
|
||||
<span class="sp"><button class="btn ghost sm">导出申请清单</button></span>
|
||||
<h2>角色与授权</h2><span class="crumb">江西省机电技师学院 · 4 个内置角色 + 1 个自定义</span>
|
||||
<span class="sp"><span class="hint">内置角色的权限由代码决定,不可修改</span></span>
|
||||
</div>
|
||||
<div class="scroll">
|
||||
<div class="note info" style="margin-bottom:16px">
|
||||
<svg class="ico"><use href="#i-bulb"/></svg>
|
||||
<div><b>这些人在门禁没匹配上,主动申请加入名单。</b>通过后会<b>自动建成名册项</b>,该学生凭微信再次进入即可直接填报,不用重输。<br>
|
||||
<span style="font-size:11.5px;color:var(--ink3)">申请本身<b>不能填报</b>——通道只解决"进名单",不绕过门禁。</span></div>
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:236px 1fr;gap:16px;align-items:start">
|
||||
|
||||
<div style="display:grid;grid-template-columns:1fr 300px;gap:16px;align-items:start">
|
||||
<div class="card" style="margin:0">
|
||||
<header>待审申请<span class="sp"><span class="pill warn">5 条待处理</span></span></header>
|
||||
<div class="body" style="padding:0">
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th style="width:26px"><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"></th>
|
||||
<th>申请人</th><th>身份证号</th><th>手机号</th><th>自述班级 / 说明</th><th style="width:74px">等待</th><th style="width:132px">操作</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"></td>
|
||||
<td><b>李四</b></td>
|
||||
<td class="mono">361004********3318</td>
|
||||
<td class="mono">138****8888</td>
|
||||
<td><span style="font-size:11.5px">24中技数控2班</span><br><span style="font-size:11px;color:var(--ink3)">我是这学期转到2班的</span></td>
|
||||
<td class="mono">2.5h</td>
|
||||
<td><button class="btn jade sm">通过</button> <button class="btn ghost sm">驳回</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"></td>
|
||||
<td><b>王芳</b> <span class="pill auto" style="font-size:10px">他任务已有</span></td>
|
||||
<td class="mono">360105********1628</td>
|
||||
<td class="mono">139****2233</td>
|
||||
<td><span style="font-size:11.5px">—</span><br><span style="font-size:11px;color:var(--jade)">2026年3月认定 已在本校名单</span></td>
|
||||
<td class="mono">5.1h</td>
|
||||
<td><button class="btn jade sm">通过</button> <button class="btn ghost sm">驳回</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"></td>
|
||||
<td><b>赵敏</b></td>
|
||||
<td class="mono">361029********4021</td>
|
||||
<td class="mono">137****9012</td>
|
||||
<td><span style="font-size:11.5px">24中技数控1班</span><br><span style="font-size:11px;color:var(--ink3)">名单上把我的名字写错了</span></td>
|
||||
<td class="mono">7.8h</td>
|
||||
<td><button class="btn jade sm">通过</button> <button class="btn ghost sm">驳回</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"></td>
|
||||
<td><b>陈强</b></td>
|
||||
<td class="mono">362502********1830</td>
|
||||
<td class="mono">135****6677</td>
|
||||
<td><span style="font-size:11.5px">—</span><br><span style="font-size:11px;color:var(--ink3)">朋友说这里可以报名</span></td>
|
||||
<td class="mono">21.3h</td>
|
||||
<td><button class="btn jade sm">通过</button> <button class="btn ghost sm">驳回</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"></td>
|
||||
<td><b>孙悦</b> <span class="pill crit" style="font-size:10px">第 2 次申请</span></td>
|
||||
<td class="mono">361024********0917</td>
|
||||
<td class="mono">136****3344</td>
|
||||
<td><span style="font-size:11.5px">24中技数控2班</span><br><span style="font-size:11px;color:var(--seal)">上次被驳回:班级不在本次范围</span></td>
|
||||
<td class="mono">26.5h</td>
|
||||
<td><button class="btn jade sm">通过</button> <button class="btn ghost sm">驳回</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<header style="font-size:12.5px;padding:11px 13px">角色<span class="sp"><button class="btn primary sm">新建</button></span></header>
|
||||
<div class="body" style="padding:6px">
|
||||
<div style="display:flex;align-items:center;gap:6px;padding:8px 10px;border-radius:6px;font-size:12.5px">
|
||||
<span style="flex:1">平台运营</span><span class="pill gray" style="font-size:10px">内置</span><span class="pill gray" style="font-size:10px">不可分配</span><span style="font-size:11px;color:var(--ink3)">0 人</span>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:6px;padding:8px 10px;border-radius:6px;font-size:12.5px">
|
||||
<span style="flex:1">学校管理员</span><span class="pill gray" style="font-size:10px">内置</span><span style="font-size:11px;color:var(--ink3)">1 人</span>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:6px;padding:8px 10px;border-radius:6px;font-size:12.5px">
|
||||
<span style="flex:1">班主任</span><span class="pill gray" style="font-size:10px">内置</span><span style="font-size:11px;color:var(--ink3)">1 人</span>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:6px;padding:8px 10px;border-radius:6px;font-size:12.5px">
|
||||
<span style="flex:1">学生</span><span class="pill gray" style="font-size:10px">内置</span><span style="font-size:11px;color:var(--ink3)">11 人</span>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:6px;padding:8px 10px;border-radius:6px;font-size:12.5px;background:var(--jade-soft)">
|
||||
<span style="flex:1;font-weight:600">年级审核员</span><span class="pill auto" style="font-size:10px">自定义</span><span style="font-size:11px;color:var(--ink3)">0 人</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex;flex-direction:column;gap:14px">
|
||||
<div class="card" style="margin:0">
|
||||
<header style="font-size:12.5px;padding:11px 13px">批量处理</header>
|
||||
<div class="body" style="padding:13px">
|
||||
<button class="btn jade" style="width:100%;justify-content:center;margin-bottom:9px">通过已选(2 条)</button>
|
||||
<div class="note warn" style="padding:9px 11px;font-size:11.5px;line-height:1.65;margin-bottom:9px">
|
||||
<svg class="ico" style="font-size:13px"><use href="#i-alert"/></svg>
|
||||
<div><b>驳回必须逐条填原因</b>,不支持批量驳回</div>
|
||||
<header>
|
||||
年级审核员 <span class="mono" style="font-weight:400;color:var(--ink3)">GRADE_AUDITOR</span>
|
||||
<span class="sp">
|
||||
<button class="btn ghost sm">刷新</button>
|
||||
<button class="btn ghost sm">编辑</button>
|
||||
<button class="btn ghost sm">停用</button>
|
||||
<button class="btn ghost sm">删除</button>
|
||||
</span>
|
||||
</header>
|
||||
<div class="body">
|
||||
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:10px;font-size:12.5px;margin-bottom:12px">
|
||||
<div><div style="color:var(--ink3);font-size:11px;margin-bottom:3px">类型</div>自定义</div>
|
||||
<div><div style="color:var(--ink3);font-size:11px;margin-bottom:3px">用户类型</div>班主任</div>
|
||||
<div><div style="color:var(--ink3);font-size:11px;margin-bottom:3px">数据范围</div>仅本人</div>
|
||||
<div><div style="color:var(--ink3);font-size:11px;margin-bottom:3px">账号数</div>0</div>
|
||||
</div>
|
||||
<div style="font-size:11px;color:var(--ink3);line-height:1.7">
|
||||
批量通过时会弹出确认框,逐条列出将建册的人员,<b>不提供"全选"快捷键</b>。
|
||||
|
||||
<div class="note warn" style="margin-bottom:14px">
|
||||
<svg class="ico"><use href="#i-alert"/></svg>
|
||||
<div><b>有未保存的改动。</b>点「保存授权」后<b>立即生效</b>——已登录的账号不需要重新登录(服务端会清掉这个角色的授权缓存)。</div>
|
||||
</div>
|
||||
|
||||
<div style="font-size:12.5px;font-weight:600;margin-bottom:4px">能做什么(权限码)</div>
|
||||
<div style="font-size:11.5px;color:var(--ink3);margin-bottom:9px">决定按钮与接口能不能用;勾了才会真的生效</div>
|
||||
|
||||
<div style="font-size:11.5px;color:var(--ink2);margin-bottom:6px">名册</div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px 14px;font-size:12.5px;margin-bottom:12px">
|
||||
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 名册查看 <span class="mono" style="font-size:11px;color:var(--ink3)">roster:view</span></label>
|
||||
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 名册修改(仅本班) <span class="mono" style="font-size:11px;color:var(--ink3)">roster:manageOwn</span></label>
|
||||
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 名册导入与修改(全租户) <span class="mono" style="font-size:11px;color:var(--ink3)">roster:manage</span></label>
|
||||
</div>
|
||||
|
||||
<div style="font-size:11.5px;color:var(--ink2);margin-bottom:6px">审核</div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px 14px;font-size:12.5px;margin-bottom:12px">
|
||||
<label><input type="checkbox" checked style="accent-color:var(--jade);width:14px;height:14px"> 初审(班主任) <span class="mono" style="font-size:11px;color:var(--ink3)">audit:first</span></label>
|
||||
<label><input type="checkbox" checked style="accent-color:var(--jade);width:14px;height:14px"> 教师代传材料 <span class="mono" style="font-size:11px;color:var(--ink3)">file:proxyUpload</span></label>
|
||||
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 终审(管理员) <span class="mono" style="font-size:11px;color:var(--ink3)">audit:final</span></label>
|
||||
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 撤回审核 <span class="mono" style="font-size:11px;color:var(--ink3)">audit:withdraw</span></label>
|
||||
</div>
|
||||
|
||||
<div style="font-size:11.5px;color:var(--ink2);margin-bottom:6px">平台运营<span class="hint" style="margin-left:6px">学校租户配不了这一组</span></div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px 14px;font-size:12.5px;color:var(--ink3);margin-bottom:14px">
|
||||
<label><input type="checkbox" disabled style="width:14px;height:14px"> 租户与套餐管理 <span class="mono" style="font-size:11px">tenant:manage</span> <span class="pill crit" style="font-size:10px">平台级,学校不可授予</span></label>
|
||||
<label><input type="checkbox" disabled style="width:14px;height:14px"> 全量操作日志 <span class="mono" style="font-size:11px">log:viewAll</span> <span class="pill crit" style="font-size:10px">平台级,学校不可授予</span></label>
|
||||
</div>
|
||||
|
||||
<div style="font-size:12.5px;font-weight:600;margin-bottom:4px">看得见哪些入口(菜单)</div>
|
||||
<div style="font-size:11.5px;color:var(--ink3);margin-bottom:9px">只影响左侧菜单显不显示 —— 真正的权限在上面那一栏,绕过前端只会拿到 40301</div>
|
||||
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:6px 14px;font-size:12.5px">
|
||||
<label><input type="checkbox" checked style="accent-color:var(--jade);width:14px;height:14px"> 工作台</label>
|
||||
<label><input type="checkbox" checked style="accent-color:var(--jade);width:14px;height:14px"> 收集任务</label>
|
||||
<label><input type="checkbox" checked style="accent-color:var(--jade);width:14px;height:14px"> 审核</label>
|
||||
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 导出中心</label>
|
||||
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 通知记录</label>
|
||||
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 字典管理</label>
|
||||
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 组织架构</label>
|
||||
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 账号管理</label>
|
||||
<label><input type="checkbox" style="accent-color:var(--jade);width:14px;height:14px"> 角色与授权</label>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:14px">
|
||||
<button class="btn primary sm">保存授权</button>
|
||||
<button class="btn ghost sm">撤销改动</button>
|
||||
<span class="hint" style="margin-left:8px">保存后立即生效;已登录的账号不需要重新登录</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin:0">
|
||||
<header style="font-size:12.5px;padding:11px 13px">本任务申请统计</header>
|
||||
<header style="font-size:12.5px;padding:11px 13px">内置角色点开是什么样</header>
|
||||
<div class="body" style="padding:13px">
|
||||
<div class="row" style="display:flex;justify-content:space-between;padding:7px 0;border-bottom:1px dashed var(--line);font-size:12.5px"><span style="color:var(--ink2)">待审核</span><b>5</b></div>
|
||||
<div class="row" style="display:flex;justify-content:space-between;padding:7px 0;border-bottom:1px dashed var(--line);font-size:12.5px"><span style="color:var(--ink2)">已通过</span><b style="color:var(--jade)">12</b></div>
|
||||
<div class="row" style="display:flex;justify-content:space-between;padding:7px 0;border-bottom:1px dashed var(--line);font-size:12.5px"><span style="color:var(--ink2)">已驳回</span><b>3</b></div>
|
||||
<div class="row" style="display:flex;justify-content:space-between;padding:7px 0;font-size:12.5px"><span style="color:var(--ink2)">通过率</span><b>80%</b></div>
|
||||
<div class="note info" style="margin-top:11px;padding:9px 11px;font-size:11.5px;line-height:1.6">
|
||||
<svg class="ico" style="font-size:13px"><use href="#i-check-circle"/></svg>
|
||||
<div>12 人已通过并自动入册</div>
|
||||
<div class="note info" style="margin-bottom:0">
|
||||
<svg class="ico"><use href="#i-lock"/></svg>
|
||||
<div><b>只读。</b>权限矩阵照着显示(可以拿它当模板照着勾),但勾选框是禁用的、没有「保存授权」按钮。<br>
|
||||
<span style="font-size:11.5px;color:var(--ink3)">要不同的能力范围,请<b>新建一个自定义角色</b>——而不是去改内置的。</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div></div></div></div>
|
||||
<figcaption>
|
||||
<span class="tag">STEP 11 · 客户确认新增</span>
|
||||
<span class="title">名单加入申请:给门禁一条受控的出口,而不是开后门</span>
|
||||
门禁不通过的人原先只能"联系老师",老师再手工加名单。<b>几百人的社会收集场景下,老师会被拖垮</b>。这一屏把加人这件事从"老师手工"变成"学生自助 + 老师逐条裁决"。
|
||||
<span class="note">四个细节:<br>
|
||||
① <b>「他任务已有」标记</b>——王芳的证件号在「2026年3月认定」的名册里匹配上了,系统主动提示"这可能是本校学生",帮管理员判断;<br>
|
||||
② <b>「第 2 次申请」标记</b>——孙悦上次被驳回,管理员能看到上次的驳回原因,避免重复纠结;<br>
|
||||
③ <b>「驳回必须逐条填原因」</b>且批量通过不提供全选——身份认定环节的误操作代价极高,与"批量通过只带绿色项"同源;<br>
|
||||
④ 顶部那句<b>「申请本身不能填报」</b>是设计边界:通道只解决"进名单",不绕过门禁。</span>
|
||||
<span class="tag">STEP 10 · 角色与授权</span>
|
||||
<span class="title">权限是契约、不是配置:内置角色只读,自定义角色可配</span>
|
||||
内置角色现在只剩 4 个(平台运营 / 学校管理员 / 班主任 / 学生),权限写在后端代码里(<span class="mono">BuiltinRole</span>)、界面上<b>只读</b>。租户可另建自定义角色,于是"谁能做什么"第一次变成客户可以自己配的东西 —— 而这类功能一旦做松,代价是<b>管理员把自己锁在系统外</b>。
|
||||
<span class="note">五个细节:<br>
|
||||
① <b>内置角色只读</b>——可以看、不能改。它的权限是前后端契约(权限码定义在代码里),改它等于改代码;界面上正确的做法是"照着它新建一个";<br>
|
||||
② <b>「看得见」与「有权限」分开</b>——菜单只决定左侧显示哪些入口,真正的访问控制是权限码校验(绕过前端只会拿到 40301);<br>
|
||||
③ <b>平台级权限与平台专用菜单在界面上禁用</b>,服务端也会以 40301 拒绝——"学校管理员给自己人造一个平台账号"是这套系统里最不可逆的越权;<br>
|
||||
④ <b>改完立即生效</b>——服务端在写授权时清掉该角色的缓存,所以不必"重新登录看看生效没有";停用角色同理(立刻不再给权限,而不是等 token 过期);<br>
|
||||
⑤ <b>防自锁</b>——不允许去掉自己进入本页所依赖的最后一条「角色与授权」权限/菜单,也不允许删掉唯一的管理员角色;这类错误只能靠 SQL 救回来。</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
@@ -1529,8 +1444,8 @@ td.num{font-family:var(--font-mono);text-align:right}
|
||||
如果下拉里只写"P1 / P2 / P3",管理员必须去翻文档才知道选哪个。**把档位的适用条件直接写在选项里**,配置界面本身就变成说明书。
|
||||
</div>
|
||||
<div class="aside-card">
|
||||
<h4><span class="n">09</span>为什么整理器是获客利器</h4>
|
||||
客户手里普遍已有历史乱数据(本例 436MB 的 rar)。让他们**先看到价值再谈上线**:不重新收集,30 秒内出一份体检报告和规范包。报告里"<b>90 个系统全自动处理,只有 1 个必须你亲自判断</b>"这句话,比任何功能列表都有说服力。
|
||||
<h4><span class="n">09</span>为什么内置角色只读、自定义角色才能改</h4>
|
||||
内置角色的权限<b>定义在后端代码里</b>(权限码是前后端契约:后端用它校验、前端用它控制按钮显隐)。把它做成"界面上可改",等于把一份契约拆成两处维护——而两处一旦不一致,表现是"按钮显示出来了、点了报 40301"。更要紧的是:一次误操作就能把管理员锁在系统外,而这类错误<b>不可逆</b>。所以界面上只读,需要别的能力范围时<b>新建一个自定义角色</b>。
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Vendored
+5
-7
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>填报通 · 原型索引</title>
|
||||
<title>深学在线 · 原型索引</title>
|
||||
<style>
|
||||
:root{
|
||||
--ink:#1b2430; --ink2:#4a5768; --ink3:#8b95a3;
|
||||
@@ -68,7 +68,7 @@ a.card:hover .go{color:var(--seal)}
|
||||
|
||||
<div class="head">
|
||||
<div class="kicker">Prototype Index · V1.0</div>
|
||||
<h1>填报通 · <em>交互原型</em></h1>
|
||||
<h1>深学在线 · <em>交互原型</em></h1>
|
||||
<p>职业技能等级认定报名材料收集系统 · 两端原型<br>设计语言:「档案室 / 电子卷宗」</p>
|
||||
</div>
|
||||
|
||||
@@ -76,10 +76,9 @@ a.card:hover .go{color:var(--seal)}
|
||||
<a class="card" href="student-app.html">
|
||||
<div class="ic"><svg class="ico"><use href="#i-user"/></svg></div>
|
||||
<h2>学生填报端</h2>
|
||||
<div class="meta">11 SCREENS</div>
|
||||
<div class="meta">10 SCREENS</div>
|
||||
<ul>
|
||||
<li>门禁核验 · 形近字确认 · 统一失败提示</li>
|
||||
<li><b>申请加入名单</b>(门禁失败后的受控出口)</li>
|
||||
<li>填报主页(字段三类规则:锁定/可改/材料)</li>
|
||||
<li>材料来源三通道(聊天记录/相册/文件,无拍照)</li>
|
||||
<li>上传即规范化 · 压缩率可视化 · 传错位置提示</li>
|
||||
@@ -106,15 +105,14 @@ a.card:hover .go{color:var(--seal)}
|
||||
<a class="card adm" href="admin-web.html">
|
||||
<div class="ic"><svg class="ico"><use href="#i-desktop"/></svg></div>
|
||||
<h2>管理端(PC)</h2>
|
||||
<div class="meta">11 SCREENS · 1180px</div>
|
||||
<div class="meta">10 SCREENS · 1180px</div>
|
||||
<ul>
|
||||
<li>工作台(结论优先 + 待办排序)</li>
|
||||
<li><b>命名规则试跑</b>——发布前的唯一闸门</li>
|
||||
<li>名单导入(表头定位可视化)</li>
|
||||
<li>报考范围确认(聚合 + 一致性体检)</li>
|
||||
<li>数据看板 · 终审 · 导出中心(目录树预览)</li>
|
||||
<li><b>表单设计器 · 材料规则配置 · 历史数据整理器</b></li>
|
||||
<li><b>名单加入申请审批</b>(客户确认新增)</li>
|
||||
<li><b>表单设计器 · 材料规则配置 · 角色与授权</b></li>
|
||||
<li>支持 100% / 82% / 66% 缩放查看</li>
|
||||
</ul>
|
||||
<div class="go">打开原型 <svg class="ico"><use href="#i-arrow"/></svg></div>
|
||||
|
||||
Vendored
+7
-76
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>填报通 · 学生端交互原型</title>
|
||||
<title>深学在线 · 学生端交互原型</title>
|
||||
<style>
|
||||
:root{
|
||||
--ink:#1b2430; --ink2:#4a5768; --ink3:#8b95a3;
|
||||
@@ -145,11 +145,6 @@ figcaption .tag{
|
||||
.input.mono{font-family:var(--font-mono);letter-spacing:.13em;font-size:16.5px}
|
||||
.input:focus{outline:0;border-color:var(--ink);box-shadow:0 0 0 3px rgba(27,36,48,.07)}
|
||||
.input::placeholder{color:#b3b9c1;letter-spacing:normal;font-size:15px}
|
||||
/* 申请加入名单入口(次要路径,视觉上不与主操作竞争) */
|
||||
.apply-entry{border-top:1px dashed var(--line2);padding-top:12px;margin-bottom:6px;text-align:center}
|
||||
.apply-entry .ae-t{font-size:12px;color:var(--ink3);margin-bottom:9px}
|
||||
.apply-entry .ae-btn{width:100%;justify-content:center}
|
||||
.apply-hint{font-size:11.5px;color:var(--ink3);line-height:1.7;margin-top:9px;text-align:left}
|
||||
.stepbox{display:flex;gap:10px;margin-bottom:15px}
|
||||
.stepbox .sb{flex:1;display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--ink3)}
|
||||
.stepbox .sb i{width:19px;height:19px;border-radius:50%;background:var(--line);color:var(--ink3);display:flex;align-items:center;justify-content:center;font-style:normal;font-size:11px;font-family:var(--font-mono);flex:none}
|
||||
@@ -347,10 +342,10 @@ figcaption .tag{
|
||||
|
||||
<header class="masthead">
|
||||
<div class="kicker">Prototype · 学生端 · V1.2</div>
|
||||
<h1>填报通 · <em>学生填报端</em>交互原型</h1>
|
||||
<h1>深学在线 · <em>学生填报端</em>交互原型</h1>
|
||||
<p class="sub">
|
||||
11 屏完整流程:<strong>门禁核验 → 信息核对 → 选材上传 → 自动规范化 → 提交审核</strong>。<br>
|
||||
<span style="font-size:12.5px;color:var(--ink3)">新增 STEP 03B「申请加入名单」——门禁失败后的受控出口,客户确认后加入。</span><br>
|
||||
10 屏完整流程:<strong>门禁核验 → 信息核对 → 选材上传 → 自动规范化 → 提交审核</strong>。<br>
|
||||
<span style="font-size:12.5px;color:var(--ink3)">门禁失败只有「重新填写」一条系统内出路;确实不在名单里时由老师手工加人(系统内不展示联系方式)。</span><br>
|
||||
设计方向为「档案室 / 电子卷宗」——本系统替代的是纸质材料袋与红章审批,界面应让人感到<strong>有秩序、可信、像一份正经公文</strong>。<br>
|
||||
图标为内联 SVG 线性图标(统一 1.7px 描边、继承文字色),不用 emoji——避免跨平台渲染不一致,也避免消费级观感。<br>
|
||||
全部文案遵循「零术语 · 指出位置 · 给出动作」三原则;数据取自真实批次 <code>24中技数控1班</code> 的 28 名学生。
|
||||
@@ -446,7 +441,7 @@ figcaption .tag{
|
||||
<div class="pagehead">
|
||||
<div class="glyph gray"><svg class="ico xl"><use href="#i-search"/></svg></div>
|
||||
<h2>没有找到你的信息</h2>
|
||||
<p>请核对姓名和身份证号是否与老师提供的名单一致。<br>如果确实已报名,可以申请加入名单,由老师审核。</p>
|
||||
<p>请核对姓名和身份证号是否与老师提供的名单一致。<br>如确认已报名,请联系老师把你加入名单。</p>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="panel">
|
||||
@@ -461,10 +456,6 @@ figcaption .tag{
|
||||
</div>
|
||||
<div class="foot">
|
||||
<button class="btn primary" style="width:100%;margin-bottom:13px">重新填写</button>
|
||||
<div class="apply-entry">
|
||||
<div class="ae-t">名单里确实没有你?</div>
|
||||
<button class="btn ghost ae-btn"><svg class="ico"><use href="#i-user-plus"/></svg>申请加入名单</button>
|
||||
</div>
|
||||
<div class="center-note">连续 10 次未匹配将暂时锁定,1 小时后可重试</div>
|
||||
</div>
|
||||
</div></div>
|
||||
@@ -473,73 +464,13 @@ figcaption .tag{
|
||||
<b>门禁失败(统一提示)</b>
|
||||
<span style="color:var(--seal)">故意不区分失败原因</span>——无论"号码不在名单"还是"姓名不符",文案完全一致。<br>
|
||||
否则这个接口就能被用来<b style="display:inline">逐个探测身份证号是否在名单里</b>。这一屏是安全设计,不是文案设计。
|
||||
<span class="note">底部只有两条出路,都是<b>系统内的动作</b>:<br>
|
||||
<span class="note">底部只有一条<b>系统内的出路</b>:<br>
|
||||
① <b>重新填写</b>(主路径)——大多数情况只是名字打错了;<br>
|
||||
② <b>申请加入名单</b>(FR-509)——确实不在名单里时自助申请,由管理员逐条审批。<br>
|
||||
② 确实不在名单里时,<b>请联系老师把你加入名单</b>(由老师在任务里手工加人)。<br>
|
||||
<b>系统内不展示任何联系方式</b>——学生在班级群里本来就有班主任的联系方式,系统再放一份既不必要,也带来维护与隐私成本。</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- ③B 申请加入名单(客户确认新增) -->
|
||||
<figure>
|
||||
<div class="device"><div class="screen">
|
||||
<div class="statusbar"><span>9:41</span><span class="icons"><svg class="ico"><use href="#i-signal"/></svg><svg class="ico"><use href="#i-wifi"/></svg><i class="bat"></i></span></div>
|
||||
<div class="navbar"><span class="back"><svg class="ico"><use href="#i-back"/></svg></span>申请加入名单</div>
|
||||
<div class="body">
|
||||
<div class="stepbox">
|
||||
<div class="sb on"><i>1</i>填写申请</div>
|
||||
<div class="sb"><i>2</i>等待审核</div>
|
||||
<div class="sb"><i>3</i>重新核验</div>
|
||||
</div>
|
||||
|
||||
<div class="notice info">
|
||||
<span class="ni"><svg class="ico lg"><use href="#i-shield"/></svg></span>
|
||||
<div>提交后由学校老师审核,<b>审核期间还不能填报</b>。通过后你会收到通知,届时重新核验即可进入。</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="ptitle">申请人信息</div>
|
||||
<div class="field">
|
||||
<label>姓名<span class="req">*</span></label>
|
||||
<input class="input" value="李四">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>身份证号<span class="req">*</span></label>
|
||||
<input class="input mono" value="361004200812153318">
|
||||
</div>
|
||||
<div class="field" style="margin-bottom:0">
|
||||
<label>手机号<span class="req">*</span></label>
|
||||
<input class="input mono" value="13800008888">
|
||||
<div class="apply-hint">用于老师与你联系,不会公开展示</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="ptitle">补充说明 <span class="badge lock">选填</span></div>
|
||||
<div class="field">
|
||||
<label>你所在的班级</label>
|
||||
<input class="input" value="24中技数控2班">
|
||||
</div>
|
||||
<div class="field" style="margin-bottom:0">
|
||||
<label>说明</label>
|
||||
<textarea class="input" style="height:64px;padding:10px 13px;line-height:1.6;resize:none">我是这学期转到2班的,原名单上没有我</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="foot">
|
||||
<button class="btn primary" style="width:100%">提交申请</button>
|
||||
<div class="center-note">同一任务最多申请 2 次 · 提交后不可修改</div>
|
||||
</div>
|
||||
</div></div>
|
||||
<figcaption>
|
||||
<span class="tag">STEP 03B · 客户确认新增</span>
|
||||
<b>申请加入名单</b>
|
||||
三步进度条把"<b>提交 → 等待 → 重新核验</b>"讲清楚,避免学生误以为提交完就能填了。<br>
|
||||
<span class="note">顶部那条提示是本屏的关键:<b>「审核期间还不能填报」</b>。如果这句话不写,学生会反复回来点填报、以为系统坏了。<br>
|
||||
补充说明是<b>选填</b>——实测里"我是转班过来的"这类信息对管理员判断很有用,但不能设成必填,否则会挡住真正需要帮助的人。</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- ④ 填报主页 -->
|
||||
<figure>
|
||||
<div class="device"><div class="screen">
|
||||
|
||||
Vendored
+2
-2
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>填报通 · 班主任端交互原型</title>
|
||||
<title>深学在线 · 班主任端交互原型</title>
|
||||
<style>
|
||||
:root{
|
||||
--ink:#1b2430; --ink2:#4a5768; --ink3:#8b95a3;
|
||||
@@ -338,7 +338,7 @@ figcaption .tag{display:inline-block;font-family:var(--font-mono);font-size:10px
|
||||
|
||||
<header class="masthead">
|
||||
<div class="kicker">Prototype · 班主任端 · V1.1</div>
|
||||
<h1>填报通 · <em>班主任端</em>交互原型</h1>
|
||||
<h1>深学在线 · <em>班主任端</em>交互原型</h1>
|
||||
<p class="sub">
|
||||
9 屏:<strong>花名册看板 → 待审列表 → 学生详情 → 批量通过 / 右滑通过 / 驳回 → 催报 → 消息 → 代传</strong>。<br>
|
||||
视觉隐喻为「<strong>电子花名册</strong>」——把全班排成网格,一眼扫完;与学生端共用「档案室 / 电子卷宗」的设计语言。
|
||||
|
||||
+434
-29
@@ -13,25 +13,26 @@
|
||||
|
||||
---
|
||||
|
||||
## 一·二、一期范围(2026-09-28 客户确认收窄)
|
||||
## 一·二、一期范围(2026-09-28 收窄 → 2026-10 收敛为最终口径)
|
||||
|
||||
> **一期只做「学校收集 + 自主认定」一条流程。** 详见 PRD「一期范围声明」。
|
||||
> **只做「学校收集 + 自主认定」一条流程**,且**不再设计任何二期功能**(2026-10 收敛,见 §一·三十六)。详见 PRD「一期范围声明」。
|
||||
|
||||
| 维度 | 字段 | 一期 | 二期后置 |
|
||||
| 维度 | 字段 | 本期做 | **不做**(已取消,不是"后置") |
|
||||
|---|---|---|---|
|
||||
| 收集模式 | `collect_mode` | `1` 学校收集 | `2` 社会收集 |
|
||||
| 认定类型 | `cert_type` | `1` 自主认定 | `2` 社会评价 / `3` 专项职业能力 |
|
||||
| 收集模式 | `collect_mode` | `1` 学校收集 | `2` 社会收集(传 2 → `40044`) |
|
||||
| 认定类型 | `cert_type` | `1` 自主认定 | `2` 社会评价 / `3` 专项职业能力(传 2/3 → `40044`) |
|
||||
|
||||
**一期材料只有 4 类,全部有实测样本**:一寸照片 / 身份证 / 学籍表 / 工作证明
|
||||
**二期才做的 2 类材料零样本**:`XJW_BA` 学信网备案表 / `OTHER_PROOF` 其它佐证材料
|
||||
**材料只有 4 类,全部有实测样本**:一寸照片 / 身份证 / 学籍表 / 工作证明。
|
||||
`XJW_BA` 学信网备案表 与 `OTHER_PROOF` 其它佐证材料 **读得到、选不到**(不在"新增材料"列表里,历史数据仍可读);
|
||||
内置材料模板只剩 `TPL_SCHOOL_ROLL` 一个。
|
||||
|
||||
**数据结构保留不启用**(二期直接开,不需 DB 迁移)——因为这三项本来就是**配置驱动**的
|
||||
(`needClassAudit` 是开关、字段隐藏走 `visibleWhen`、材料差异是材料项配置),
|
||||
所以**省下的主要是测试成本与一个外部依赖,而不是代码量**。
|
||||
**数据结构保留不启用**:这三项本来就是**配置驱动**的(`needClassAudit` 是开关、字段隐藏走 `visibleWhen`、
|
||||
材料差异是材料项配置),所以留着不增加维护成本;但**没有"二期开放"的计划** —— 将来要用,按新需求重新评估。
|
||||
|
||||
**工期**:392 → **387 人日**(−5);**关键路径不变,仍 15 周**。
|
||||
|
||||
> ⚠️ **二期启动前必做**:索取社会评价样本(D4)→ 标定 `XJW_BA`/`OTHER_PROOF` → 补社会收集审核流与专项职业能力字段隐藏。
|
||||
> ⚠️ **原「二期启动前必做」清单(索取社会评价样本 / 标定 `XJW_BA`·`OTHER_PROOF` / 补社会收集审核流)已随二期取消而作废**。
|
||||
> 与「二期」有关的一切表述在本仓库一律读作「**不做**」。
|
||||
|
||||
---
|
||||
|
||||
@@ -42,7 +43,7 @@
|
||||
|
||||
| 项 | 状态 |
|
||||
|---|---|
|
||||
| 技术栈定版 | ✅ Java 21 + **Spring Boot 4.1.1** + MyBatis-Plus 3.5.17(boot4) + PG16 + Redis7<br>PC: Vue3 + **Gi Admin Pro(Arco Design Vue 2.58)**;小程序: uni-app |
|
||||
| 技术栈定版 | ✅ Java 21 + **Spring Boot 4.1.1** + MyBatis-Plus 3.5.17(boot4) + PG16 + Redis7<br>PC: **纯 Vue 3 + Naive UI 2.42**(2026-10 从 Gi Admin Pro + Arco 重搭,见 §一·三十六);小程序: uni-app |
|
||||
| M0 后端骨架 | ✅ 统一响应/异常/58 错误码/多租户拦截/软删除/字段填充/AES-GCM/HMAC/RSA/JWT/权限模型 |
|
||||
| M0 数据库 | ✅ Flyway 2 个迁移;26 张表(20 业务 + 5 标准 + 1 迁移记录) |
|
||||
| M0 后端接口 | ✅ 租户开通与列表、组织 CRUD 与树、PC 登录/登出/当前用户、**账号管理全套(含 Excel 批量导入)** |
|
||||
@@ -53,7 +54,7 @@
|
||||
| **M1-03 表单 schema 版本化** | ✅ `GET/PUT /tasks/{id}/form-schema`(版本只增不改 + `baseVersion` 乐观锁 40025 + 内置字段不可删 40026)+ 官方 21 项内置字段目录;**`e2e-m1-form.py` → 48/48** |
|
||||
| **M1-06 填报二维码** | ✅ `GET /tasks/{id}/qrcode`(真调微信生成小程序码)+ 匿名图片接口 `GET /qrcodes/{scene}`;**`e2e-m1-qrcode.py` → 32/32(唯一打外网的 E2E)** |
|
||||
| **小程序端骨架 + 扫码落地页** | ✅ `frontend-mp/`(uni-app,appid `wxf01d3fa60f87f0b7`)+ 匿名 `GET /mp/scene-context`;**已在微信开发者工具里跑通**;`e2e-m1-scene.py` → 31/31 |
|
||||
| PC 前端 | ✅ 已换 Gi Admin Pro + 腾讯云主题;已实现:登录 / 工作台 / 组织架构 / 租户管理 / 账号管理 / **收集任务(含配置班级)** |
|
||||
| PC 前端 | ✅ **纯 Vue 3 + Naive UI**(原 Gi Admin Pro + Arco 已于 2026-10 重搭移除);已实现:登录 / 工作台 / 组织架构 / 租户管理 / 账号管理 / 收集任务(含配置班级 · 名单 · 表单 · 材料四个抽屉) / 审核 / 导出中心 / 通知记录 / 字典管理 / 角色与授权 / 操作日志 |
|
||||
| 待做 | M1 剩余(M1-05 规则模板 + **M1-07~M1-10 前端**);**M3 小程序填报主流程**(`/mp/login` 门禁核验 表单);CI/CD;**C1 小程序类目资质(阻塞学生真正开始填报)** |
|
||||
|
||||
---
|
||||
@@ -629,22 +630,25 @@ Boot 4 / Flyway 10 / MP 3.5.9 这一批版本都在"把单体拆成模块",
|
||||
|
||||
| 文件 | 版本/大小 | 状态 |
|
||||
|---|---|---|
|
||||
| `docs/数据信息填报系统PRD.md` | V2.4 / 189KB / 3019 行 | ✅ 含 **125 条 FR**、**21 条 VAL**、**121 个接口**、**20 表 DDL**、实测附录 |
|
||||
| `docs/数据信息填报系统PRD-功能逻辑卷.md` | V1.9 / 88KB | ✅ 纯功能逻辑,**不含库表/接口/代码** |
|
||||
| `docs/数据信息填报系统PRD.md` | V2.5 / — | ✅ 含 **125 条 FR**、**21 条 VAL**、**119 个接口**、**20 表 DDL**、实测附录 |
|
||||
| `docs/数据信息填报系统PRD-功能逻辑卷.md` | V2.0 / — | ✅ 纯功能逻辑,**不含库表/接口/代码** |
|
||||
| `docs/db/schema-postgresql.sql` | 37KB | ✅ **PG 16.15 实测零错误**(20 表 / 177 列注释 / 65 索引 / 20 表注释) |
|
||||
| `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/student-app.html` | **10 屏** | ✅ 门禁核验 → 填报 → 规范化 → 提交审核(STEP 03B「申请加入名单」已随需求收敛删除) |
|
||||
| `docs/prototype/teacher-app.html` | 9 屏 | ✅ |
|
||||
| `docs/prototype/admin-web.html` | **10 屏** | ✅ 含表单设计器、材料规则配置、**角色与授权**(整理器与申请审批两屏已随需求收敛删除) |
|
||||
| `docs/prototype/index.html` | 7KB | ✅ 三端入口 |
|
||||
| `docs/00-交付计划.md` | — | ✅ 7 项计划与进度记录 |
|
||||
| `docs/01-客户与机构确认函.md` | 23KB | ✅ **P1** 23 项**已全部确认**(客户按建议执行,需求冻结) |
|
||||
| `docs/01-客户与机构确认函.md` | 23KB | ✅ **P1** 23 项**已全部确认**(⚠️ 其中 B1「加入申请」已于 2026-10 需求回退取消,见 §一·三十六) |
|
||||
| `docs/02-需求追溯矩阵.md` | 10KB | ✅ **P2** 审计报告,含 10 个接口缺口 |
|
||||
| `docs/03-接口设计.md` | **115KB / 2883 行** | ✅ **P3 121 个接口**字段级定义 + 枚举全集 + 12 个 VO + **58 个错误码**;§4 逐节声称数与表行数一致 |
|
||||
| `docs/03-接口设计.md` | — | ✅ **119 个接口**字段级定义 + 枚举全集 + 12 个 VO + **54 个错误码**;§4 逐节声称数与表行数一致 |
|
||||
| `docs/06-验收测试清单.md` | **33KB** | ✅ **P6 36 条 Golden 断言** + **111 条功能验收** + **27 条异常边界** |
|
||||
| `../README.md` | **10KB** | ✅ **P7 文档导航**,按角色读法 + 快速上手 |
|
||||
| `docs/04-核心模块设计说明.md` | **69KB / 1453 行** | ✅ **P4 六个模块**伪代码级说明 + 依赖图 + Golden 基线 |
|
||||
| `../README.md` | — | ✅ **P7 文档导航**,按角色读法 + 快速上手 |
|
||||
| `docs/04-核心模块设计说明.md` | — | ✅ **P4 六个模块**伪代码级说明 + 依赖图 + Golden 基线 |
|
||||
| `docs/05-开发任务拆解.md` | **31KB / 506 行** | ✅ **P5 100 个任务 / 387 人日 / 15 周**,含关键路径、一期范围与核算口径 |
|
||||
|
||||
> **合计屏数 : 29**(学生端 10 + 班主任端 9 + 管理端 10)—— `scripts/verify-docs.py` 会拿这一行与三个 html 的
|
||||
> `<figcaption>` 计数核对,**改原型必须同步这一行**。
|
||||
|
||||
---
|
||||
|
||||
## 四、待办计划
|
||||
@@ -664,7 +668,7 @@ Boot 4 / Flyway 10 / MP 3.5.9 这一批版本都在"把单体拆成模块",
|
||||
|
||||
```
|
||||
✅ I1 报考范围读取/确认 GET/PUT /api/v1/tasks/{id}/plan-scope
|
||||
✅ I2 历史数据整理器 POST /api/v1/cleanup/jobs(共 5 个)
|
||||
❌ I2 历史数据整理器 ~~POST /api/v1/cleanup/jobs(共 5 个)~~ → **已随 2026-10 需求收敛删除**
|
||||
✅ I3 字典管理 /api/v1/dicts(4 个)
|
||||
✅ I4 操作日志 GET /api/v1/logs/operations
|
||||
✅ I5 登录日志 GET /api/v1/logs/logins
|
||||
@@ -2265,9 +2269,11 @@ E2E 一开始给材料传了张 900×1200 的小图 —— 而**身份证材料
|
||||
|
||||
### 现在的坐标(都可当场核对)
|
||||
|
||||
> ⚠️ **下表是 2026-09-30 的快照**。**最新数字见 §一·三十六**(接口 **119** / 错误码 **54** / 权限码 **28** / 内置角色 **4** / 原型 **29 屏**)。
|
||||
|
||||
| 项 | 现在 | 怎么核对 |
|
||||
|---|---|---|
|
||||
| 契约接口实现度 | **121 个里 120 个**(99%),唯一缺口 `/mp/ocr/idcard` | `python scripts/check-api-parity.py --missing` |
|
||||
| 契约接口实现度 | **129 个里 128 个**(99%),唯一缺口 `/mp/ocr/idcard` | `python scripts/check-api-parity.py --missing` |
|
||||
| 数据库迁移 | **Flyway V17**(不是 1~6) | `docker exec xt-pg psql -U fill -d filldb -c "select version,description from flyway_schema_history order by installed_rank desc limit 3"` |
|
||||
| 实体 / 表 | **27 个实体 / 33 张表**一致 | `python scripts/check-entity-columns.py` |
|
||||
| E2E | **36 个脚本 / 1548 条断言 / 0 失败** | 依次跑 `scripts/e2e-*.py`(或看 §三 的说明) |
|
||||
@@ -2322,7 +2328,9 @@ CI/CD 与 test/prod 环境 → 压测 → 用户手册与客服话术 → 缩略
|
||||
| 菜单 | 真相 |
|
||||
|---|---|
|
||||
| 名单管理 / 材料规则 / 表单设计器 | ✅ **功能早就做好了**,它们是**按任务**的,入口在「收集任务」每一行的链接里。「待建」页现在会明确指路并给跳转按钮 |
|
||||
| 角色与授权 | ⏸ **二期范围**(内置角色的权限由代码决定、不可改) |
|
||||
| 角色与授权 | ✅ **已上线**(2026-10-09;原列在「二期」,2026-10 收敛后按**一期功能**计):内置角色只读 + 自定义角色的权限码 / 菜单可配,页面 `frontend-pc/src/views/role/`,接口见 `docs/03` §4.12 / §5.10。**「数据范围可配」仍是下一批** |
|
||||
|
||||
> ⇒ 至此 **`ready:false` 的菜单为 0**,「待建」页只作为"新增菜单但页面还没排到"的兜底保留。
|
||||
|
||||
### 卡在哪(**全部是外部依赖**,每一条都指向确认函的具体编号)
|
||||
|
||||
@@ -2332,7 +2340,7 @@ CI/CD 与 test/prod 环境 → 压测 → 用户手册与客服话术 → 缩略
|
||||
| 小程序类目资质 | 法务/商务 | `docs/01` **C1** |
|
||||
| `union_id`(H5 与小程序不能同时上线) | 需微信开放平台账号并绑定 | `docs/01` **E1**(第二轮新增) |
|
||||
| 真机验证(压缩与体积上限) | 需真机 | `docs/01` **D6** |
|
||||
| 二期启动时点 | 客户 | `docs/01` **E3** |
|
||||
| ~~二期启动时点~~ | — | **已取消**(不再设计任何二期功能,见 §一·三十六) |
|
||||
|
||||
⚠️ **不要在没有这些输入的情况下去"顺手实现"**:M4 的识别链路一旦按某家供应商写死,
|
||||
换供应商要重写;`union_id` 没拿到之前,"按 union_id 认人"也无从下手
|
||||
@@ -2504,12 +2512,12 @@ CI/CD 与 test/prod 环境 → 压测 → 用户手册与客服话术 → 缩略
|
||||
| # | 问题一句话 | 修法 |
|
||||
|:--:|---|---|
|
||||
| **F-101** | **FR-1002「通知模板可配置」只兑现了手工发送那一半** —— 驳回/通过/催报这些**自动**通知把文案硬编码在 Java 里,而 FR-1002 点名的 5 类**全是自动类** | 新增 `NotifyTextResolver`(单向依赖,避开循环注入):**库里改过的 > 内置**,与模板列表页同一份口径;`NotifyRequest` 加 `subjectCode/subjectVars/extraText`;**模板停用 = 这类通知不发** |
|
||||
| **F-102** | 加入名单申请**一个通知都不发**(代码里写着"通知渠道未实现",而渠道早做好了) | 提交 → 通知该审批的人(班主任/副班主任 + 学校管理员兜底);通过/驳回 → 通知学生(含原因) |
|
||||
| **F-102** | 加入名单申请**一个通知都不发**(代码里写着"通知渠道未实现",而渠道早做好了) | 提交 → 通知该审批的人(班主任 + 学校管理员兜底);通过/驳回 → 通知学生(含原因)。⚠️ **该模块已于 2026-10 需求收敛时整体删除,本条随之关闭** |
|
||||
| **F-103** | 导出完成/失败不通知(异步打包,用户只能守进度条) | 通知发起人;⚠️ **正文不放签名 URL**(站内信落库,链接几十分钟就失效)—— 内置模板里原本写着 `{下载链接}`,一并改掉 |
|
||||
| **F-104** | ⚠️⚠️ **自动催报从来没发出过任何一条**:`end_time = date::date` 与库里的 `23:59:59` **永不相等** | 改 `end_time::date = #{date}::date`;并补 FR-1003 原文的**「+ 截止当日」**(0 天,文案与"最后一天"分开) |
|
||||
| **F-105** | 驳回达上限只写一行日志,而契约说"**同时通知管理员**" | 新增 `AUDIT_ESCALATE` 模板 + 发给本租户学校管理员(按角色找,不写死账号) |
|
||||
| **F-106** | 字典管理 / 通知记录**没有任何角色持有菜单键** → 页面做得好也点不到(**手册还写着"去那里办"**) | 给 `SCHOOL_ADMIN` 补 `dict` / `notify` |
|
||||
| **F-107** | 10 个权限码没挂到任何接口(克隆/表单/材料/报考范围/整理器/租户…) | 6 个改挂自己的码;4 个范围类/二期开关进例外表 |
|
||||
| **F-107** | 10 个权限码没挂到任何接口(克隆/表单/材料/报考范围/整理器/租户…) | 6 个改挂自己的码;4 个范围类/开关进例外表。⚠️ 其中「整理器」一项已随模块删除而消失(2026-10) |
|
||||
| **F-108** | `TASK_PUBLISH` / `TASK_DEADLINE` 零发送点 | **不做**(发布时刻没有收件人:学生要先扫码才绑账号,而发布前没人会扫)→ 登记例外 + 理由 |
|
||||
| **F-109** | `Map.of(...)` 不接受 null → 拼模板变量抛 NPE → 被"通知失败不影响业务"吞成一行 warn(**驳回通知全丢而驳回照样成功**) | 新增 `NotifyRequest.vars(...)`(null 跳过);姓名优先取**名册**上的名字 |
|
||||
| **F-110** | schedule 的断言数的是**全库**「审核超时」消息 → 几天后因别的老数据自己变红(代码一行没改) | 断言**限定到本次夹具**(跨租户扫描类功能的断言必须这么做) |
|
||||
@@ -2553,7 +2561,7 @@ CI/CD 与 test/prod 环境 → 压测 → 用户手册与客服话术 → 缩略
|
||||
`material:manage` / `roster:*` 照旧,删的只是菜单键(可见性)。
|
||||
- PC 端 `XT_MENUS` 删掉三条;**顺带删掉 `entryAt` 机制**("功能已就绪 + 跳转按钮"
|
||||
那段)—— 它当初就是为这三条写的,留着就是一段永远走不到的代码。
|
||||
「待建」页现在只服务「角色与授权」(二期)。
|
||||
「待建」页现在只服务「角色与授权」(二期)——**该页已于 2026-10-09 上线,见 §一·三十四**。
|
||||
- 班主任/副班主任/审核员的菜单也一起去掉了 `roster`:他们本来也点不进去(没有 `roster` 页)。
|
||||
|
||||
### 二、班主任点工作台必弹 403 —— 前端调了"自己没有权限"的接口
|
||||
@@ -2778,4 +2786,401 @@ CI/CD 与 test/prod 环境 → 压测 → 用户手册与客服话术 → 缩略
|
||||
(实测分页器顶边 1612 / 886,视口高 900)。修法是给这两页的卡片加同一个 `.g-list-card`
|
||||
并把表格改成 `y: '100%'`,本轮没动(客户只报了操作日志那一处)。
|
||||
|
||||
---
|
||||
|
||||
## §一·三十五 二期「角色与授权」上线(2026-10-09 晚)
|
||||
|
||||
> 起点:客户在页面上看到「自定义角色属二期」,问"系统一共有哪些功能排在二期还没做的"。
|
||||
> 盘完的结论是二期分三档(**已拍板后置**的三种任务类型 / **代码里显式留的口子** / **建议二期待拍板**),
|
||||
> 而「角色与授权」只是其中**最小**的一项。客户选定"先做角色 + 权限码可配"(批次 1-4),
|
||||
> **数据范围可配另排**。这一轮把它做完了。
|
||||
|
||||
### 一、做了什么
|
||||
|
||||
| # | 改动 | 关键点 |
|
||||
|:--:|---|---|
|
||||
| 1 | **权限判定合并两个来源** | `PermissionChecker`:内置角色按**代码**(`BuiltinRole`)、自定义角色按**库**(`sys_role_permission`)。实现放 `DbRoleGrantSource`(`module` 层),`security` 只依赖接口 `RoleGrantSource` —— 否则角色管理服务要调 `permissionChecker.require(ROLE_MANAGE)`,两个包**迟早成环** |
|
||||
| 2 | **必须有缓存** | 权限判定此前是**零 IO**,而压测基线是"SQL 条数恒定、DB ≤2ms"(《09》§9.5)。缓存放 Redis(本项目**每请求本来就读一次 Redis**:登出黑名单)。三个细节:**空结果也缓存**(否则已删角色的 token 会把库打穿)、**Redis 挂了退化成直读库**(当成"没权限"会让全校登不进去,当成"有权限"是越权)、**TTL 只是兜底**(正常靠写后 `evict`) |
|
||||
| 3 | **V18 迁移** | `sys_role.user_type`(账号的 `user_type` 一直是从角色反推的,自定义角色不在代码映射里 → 没这列就"角色建得出来、发不出去")+ `sys_role_menu`(菜单不做权限的附属,与 `sys_role_permission` 同构) |
|
||||
| 4 | **8 个接口 + `40045`** | `docs/03` §4.12 / §5.10;契约总数 **121 → 129**、错误码 **58 → 59** |
|
||||
| 5 | **内置角色授权同步** | `RoleProvisioner` 从"已存在就 `continue`"改成**幂等回填**,并加每日 03:20 的定时任务(`runNow` 名 `role`)。修掉的是"代码改了权限、已开通租户永远停在发放那一刻"——**这种漂移不会报错、也没有测试会红** |
|
||||
| 6 | **PC 页 + 原型 ⑪** | `views/role/`(左角色列表 + 右权限矩阵 + 菜单勾选 + 保存即生效);内置角色**只读**(能看、能当模板,改不了)。至此 **`ready:false` 的菜单归零**,「待建」页只作为兜底保留 |
|
||||
| 7 | **账号侧全部改读库** | 可选角色 / 用户类型 / 角色名;删掉前端三份写死的镜像表(`SCHOOL_ROLE_OPTIONS` / `ROLE_TO_USER_TYPE` / `ROLE_NAME_TO_CODE`)。留着的后果是"自定义角色推不出类型 → 账号建不出来,但看不出为什么" |
|
||||
|
||||
### 二、四条口径(决定了这个功能长什么样)
|
||||
|
||||
| # | 口径 | 为什么 |
|
||||
|:--:|---|---|
|
||||
| 1 | **内置角色只读** | 权限码是前后端契约(后端校验、前端控显隐)。做成可改 = 把一份契约拆两处维护,而不一致的表现是"按钮显示出来了、点了报 40301";更要紧的是**一次误操作就能把管理员锁在系统外,不可逆** |
|
||||
| 2 | **平台级能力与入口,界面上禁用 + 服务端 40301** | 学校租户授予 `tenant:manage`/`tenant:view`/`log:viewAll` → 40301;配置平台专用菜单 `tenant` → 40301;建平台运营类角色 → 40301;发 `PLATFORM_ADMIN` → 40301。"界面不显示"只是不提示,**服务端才是防线** |
|
||||
| 3 | **防自锁** | 不允许去掉"自己进入本页所依赖的最后一条 `role:manage` 权限 / `role` 菜单",也不允许停用或删除它;改账号角色时同理保护 `user:manage`(这条是**二期新增的漏洞**:`losingAdminRole` 只看那两个内置管理员码,自定义角色带 `user:manage` 时它会漏) |
|
||||
| 4 | **改完立即生效** | 每次写授权都清该角色缓存 → 不必重新登录;停用角色**立刻**不再给权限(不是等 token 过期,PC token 最长 8 小时)。⚠️ **改账号的角色归属**仍是"下次登录生效"(角色码在 token 里) |
|
||||
|
||||
> ⚠️ **`dataScope` 本期只展示、不提供修改入口**:数据范围(本人 / 本班 / 本院系 / 全租户)目前由
|
||||
> "拿到了哪些权限码 + 任务里被指派了哪些班"共同决定,散在审核 / 催报 / 代传 / 看板各处(6 处判据),
|
||||
> 做成可配要先把它们收口成唯一的 resolver 并补回归断言 —— **单独一批**。让界面出现一个"改了不生效"的开关,比没有它更糟。
|
||||
|
||||
### 三、这一轮踩到的 5 个坑(都值得记)
|
||||
|
||||
1. **HMR 的陈旧模块会伪装成"组件坏了"**:弹窗里的授权矩阵**完全不渲染**、控制台只有一条
|
||||
"Unhandled error during execution of component update",查了三轮都没头绪;
|
||||
**硬刷新后一切正常**(同样的代码、同样的接口)。→ 前端改完先**硬刷新**再判断是不是 bug
|
||||
(`frontend-pc/AGENTS.md` 早就写着这条,这次是"组件整块不渲染"的形态)。
|
||||
2. **我自己的 E2E 断言写错了**:用 `roster:view` / `audit:first` 判"换成内置角色后自定义角色的权限没了",
|
||||
而 `HEAD_TEACHER` **本来就有这两条** → 绿灯功能被判成红的。
|
||||
→ **判据要选"只有被测对象才有"的那条**(改用 `material:manage`)。
|
||||
与《08》F-77/F-97 同一族:**断言打在错的东西上,比没有断言更糟**。
|
||||
3. **登录响应也必须带上自定义角色的菜单与权限**:`AuthService.buildProfile` 原先直接取
|
||||
`BuiltinRole.mergeMenus/mergePermissions`,而登录响应里的这两项前端会**直接落库用**
|
||||
(省一次 `/auth/me` 往返)→ 自定义角色的人"刚登录什么都没有、刷新一下又好了"。
|
||||
改的时候还发现第二个坑:`login()` 里 `UserContext` 一开始是**"只有租户、没有角色"的临时上下文**,
|
||||
而 `PermissionChecker` 是从上下文算的 → 必须**先把上下文升级成完整的 `principal`** 再装配。
|
||||
4. **学校租户能勾到平台专用菜单**(`tenant`):菜单键的合法取值是"所有内置角色菜单的并集",
|
||||
而 `tenant` 只在 `PLATFORM_ADMIN` 里 → 配给学校的角色会在左侧多出一个**点进去必然 403** 的入口。
|
||||
现在**两道**:界面不显示 + 服务端 `BuiltinRole.platformOnlyMenuKeys()` 拒绝。
|
||||
5. **账号页下拉里有一个"点了必然 403"的选项**:demo 租户里有一行历史遗留的 `PLATFORM_ADMIN` 角色,
|
||||
而 `/roles` 会把它列出来 → 学校管理员选了就报错。现在 `RoleVO` 多一个 **`assignable`** 标志
|
||||
(**看得见 ≠ 发得出去**),列表里标成「不可分配」、账号页的选项按它过滤。
|
||||
|
||||
### 四、验证
|
||||
|
||||
| 手段 | 结果 |
|
||||
|---|---|
|
||||
| `scripts/e2e-m10-role.py`(新增) | **77/77**:改授权后**同一个 token** 立刻生效 / 停用立刻失效 / 内置角色只读 / 越权与自锁 / 删除守卫 `40045` / 内置角色权限以代码为准 + 同步回填幂等 / 跨租户 |
|
||||
| `python scripts/verify-all.py --with-e2e` | **9 项全过 / 0 失败 / 0 跳过**;**37 个脚本 / 1595 条断言 / 0 失败**(2026-10 收敛删掉 2 个脚本后已重跑填数) |
|
||||
| 浏览器实测(真点) | 新建自定义角色 → 账号页角色候选出现它(且平台角色已不在候选里)→ 改权限落库(`sys_role_permission` 逐字核对)→ 删除(软删 + 授权/菜单行一并软删);截图留证 |
|
||||
| 机械校验 | `check-api-parity`(契约 **119** / 已实现 118 / 缺口 1)、`verify-docs` 22/22(原型 **29** 屏)、`check-wiring` 未接线 0、`check-entity-columns` 28 实体一致、`check-manual` 0 错 |
|
||||
| 前端 | `pnpm typecheck` 0 错、`eslint` 0 错(`views/role/**`、`apis/xt/role.ts` 与改动过的账号页) |
|
||||
|
||||
### 五、没做(下一批,已与客户确认"另排")
|
||||
|
||||
1. **数据范围可配**(本人 / 本班 / 本院系 / 全租户):需要 `DataScopeResolver` 收口那 6 处判据,
|
||||
并实现"本院系"(今天只有枚举与 DB 列,**没有代码读它**)。⚠️ 会动审核 / 催报 / 代传 / 看板,
|
||||
而它们有 87 / 42 / 33 / 24 条断言锁着。
|
||||
2. **角色变更后"在线 token 立即失效"**:现在改**授权**立即生效,但改**某个人挂哪个角色**要等下次登录
|
||||
(角色码在 JWT 里)。要做就得让 JWT 带上签发时间并与"最近一次改角色时间"比较。
|
||||
3. **平台运营的角色页**:平台选定学校后可以管那所学校的角色(`X-Tenant-Id` 已生效,E2E 断言了
|
||||
"不选学校改不了"),但"平台租户自己的自定义角色"目前没有实际用途(平台身份不受权限码限制)。
|
||||
|
||||
---
|
||||
|
||||
## §一·三十六 需求收敛:删角色 / 删两模块 / 取消二期 + PC 端换 Naive UI(2026-10)
|
||||
|
||||
> 一句话:需求从「6 角色 + 12 菜单 + 二期预留」收敛为「**4 角色 + 10 菜单 + 不设二期**」。
|
||||
> 代码 / 库 / E2E / 原型 / 文档五处已同步;本节记 **删了什么、存量数据怎么处理、断言怎么改、
|
||||
> 以及下一轮最容易踩的坑**。**这是唯一有效的当前口径** —— 前面各节里凡与本节冲突的,一律以本节为准。
|
||||
|
||||
### 一、删了什么
|
||||
|
||||
| # | 删除项 | 影响面 |
|
||||
|:--:|---|---|
|
||||
| 1 | **副班主任 `DEPUTY_TEACHER`**、**审核员 `AUDITOR`** 两个内置角色 | 内置角色 **6 → 4**(平台运营 / 学校管理员 / 班主任 / 学生);`BuiltinRole` 枚举、权限集、菜单集、PC 端 `ROLE_NAMES`、原型角色页 |
|
||||
| 2 | **历史整理器**(`module/cleanup`) | 5 个契约接口 + `cleanup:run` 权限码 + `collect_cleanup_job` 表 + PC 菜单与页面 + 原型 1 屏 + `e2e-m7-cleanup.py` |
|
||||
| 3 | **加入申请**(`module/join`) | 5 个契约接口 + `join:audit` 权限码 + `collect_join_request` 表 + PC 菜单与页面 + 学生端 `pages/join` + 错误码 `40037`/`40038`/`40039`/`40040`/`40042` + 站内信模板 `JOIN_APPROVE`·`JOIN_SUBMIT` + 微信订阅模板 `JOIN_APPROVE` |
|
||||
| 4 | `collect_task_org.deputy_user_ids`(协办人) | **整列删除**;审核范围三级回退 → **两级**(`head_user_id` → `sys_org.leader_user_id`),连带 6 个 mapper 的 `LIKE '%,id,%'` 分支(审核范围 / 待办 / 教师看板 / 催报 / 超时提醒 / 任务可见范围) |
|
||||
| 5 | **二期**(整体) | 社会收集 / 社会评价 / 专项职业能力 / 数据范围可配 / 角色变更即时失效 / 平台角色页 → **一律不做**;OCR 与材料识别改为「**缓做,等供应商选型**」(`/mp/ocr/idcard` 仍是唯一未实现接口) |
|
||||
| 6 | 二期材料与模板 | 内置材料模板 **3 → 1**(只剩 `TPL_SCHOOL_ROLL`);`XJW_BA` / `OTHER_PROOF` **读得到、选不到**(不在"新增材料"列表里,历史任务仍可读 —— 删编码会让已配的材料项悬空) |
|
||||
|
||||
**数字总账(跨文档必须一致;`verify-docs.py` 与 `check-api-parity.py` 会查)**
|
||||
|
||||
| 项 | 收敛前 | 收敛后 |
|
||||
|---|---:|---:|
|
||||
| 契约接口 | 129 | **119** |
|
||||
| 错误码 | 59 | **54** |
|
||||
| 权限码 | 30 | **28** |
|
||||
| 内置角色 | 6 | **4** |
|
||||
| PC 业务菜单 | 12 | **10** |
|
||||
| 小程序页面 | 10 | **9** |
|
||||
| 原型屏 | 32 | **29**(学生 10 / 班主任 9 / 管理端 10) |
|
||||
| E2E 脚本 | 39 | **37**(删 `e2e-m2-join.py`、`e2e-m7-cleanup.py`);断言 1698 → **1595 条** |
|
||||
|
||||
> ⚠️ **两个容易算错的数**(都踩过):
|
||||
> ① **契约是 119,不是 116**。116 是"删 13 个**代码端点**"(cleanup 8 + join 5),
|
||||
> 但契约数是数 `docs/03` §4 的**清单行**,而 cleanup 有 3 个端点(如 `/cleanup/jobs/{id}/result`)
|
||||
> **从来不在 §4 清单里**(属 §17.1「代码有、契约没有」)→ 129 − 10(cleanup 5 行 + join 5 行)= **119**。
|
||||
> ② **原型是 29,不是 30**:32 = 学生 11 + 班主任 9 + 管理端 12,删 3 屏 = 10 + 9 + 10 = **29**。
|
||||
>
|
||||
> 📌 `scripts/check-api-parity.py` 里那个"契约必须恰好 N"的常量已同步改成 **119**(判定逻辑未动);
|
||||
> `verify-docs.py` 的屏数核对此前**是静默失效的**(它要半角冒号的「合计屏数 : N」,而文档里没有这一行),
|
||||
> 本次在 §三 的交付物表下补了「**合计屏数 : 29**」——**改原型必须同步这一行**。
|
||||
|
||||
### 二、V19 迁移怎么处理存量数据
|
||||
|
||||
`backend/xt-server/src/main/resources/db/migration/V19__remove_deputy_auditor_cleanup_join.sql`
|
||||
(**只加不改**:V18 及以前的迁移一律不动,这是 Flyway 的规矩):
|
||||
|
||||
1. **先解绑定、再软删角色** —— 删 `sys_user_role` 里指向这两个角色的行,再软删 `sys_role` / `sys_role_permission` / `sys_role_menu`;
|
||||
2. **账号只在"除这两个角色外没有别的角色"时才删**,且是**软删** ——
|
||||
一个人若同时挂着 `HEAD_TEACHER`,他仍是班主任,只是少了一个角色;
|
||||
3. `collect_task_org.deputy_user_ids` 随列删除;`collect_cleanup_job` / `collect_join_request` 两张表按迁移内策略处理。
|
||||
|
||||
> ⚠️ **为什么必须迁、不能只删代码**:角色码从 `BuiltinRole` 里删掉后,`mergePermissions` 对它返回**空集** ——
|
||||
> 存量账号会变成"能登录、但处处 `40301`",**不抛异常、不打日志**。这类问题没有任何测试会红。
|
||||
|
||||
### 三、受影响的断言怎么改的
|
||||
|
||||
| 脚本 | 改动 |
|
||||
|---|---|
|
||||
| `e2e-m2-join.py` / `e2e-m7-cleanup.py` | **整体删除**(被测模块已删) |
|
||||
| `e2e-m0-user.py` | 造账号不再用 `DEPUTY_TEACHER`;Excel 导入用例里的角色名改掉 |
|
||||
| `e2e-m1-orgs.py` | 新增断言「**`deputy_user_ids` 列已被 V19 删除**」;配置班级不再有副班主任字段 |
|
||||
| `e2e-m5-notify-wiring.py` | 新增断言「**两张表 / 两个角色 / 两个权限码均已下线 + 权限码总数 28**」;删掉"班主任调整理器被 `40301`"那条 |
|
||||
| `e2e-m10-role.py` | 内置角色基线 6 → 4 |
|
||||
|
||||
> ⚠️ **一条断言写法禁忌(本次踩到)**:`string_agg` 没有 `ORDER BY` 时**结果顺序不稳定**,
|
||||
> 用它拼出"角色名单字符串"再比较,跑单脚本绿、跑全量偶发红。
|
||||
> **断言必须"数个数 + 点名单"**(`count(*)` 加逐项 `EXISTS`),不要比拼接串。
|
||||
|
||||
### 四、⭐ 下一次改动会踩的五个坑(本次新增的防线)
|
||||
|
||||
1. **`UserService.list` 对不认识的角色码由"静默忽略过滤条件"改成失败关闭** ——
|
||||
之前传一个不存在的 `roleCode` 会把过滤条件整个丢掉、**返回全校账号**,而这看起来完全正常(有数据、分页也对)。
|
||||
2. **`e2e-m1-orgs` 断言了 `deputy_user_ids` 列已被 V19 删除** ——
|
||||
谁想把这个字段加回来,会先红一条断言,而不是等某处 SQL 报 `column "deputy_user_ids" does not exist`。
|
||||
3. **`e2e-m5-notify-wiring` 断言了"两张表 / 两个角色 / 两个权限码已下线 + 权限码总数 28"** ——
|
||||
权限码是前后端**契约**,加/删码必须同步改这条断言与 `docs/03` 的清单。
|
||||
4. **PC 端已没有 `v-hasPerm`**:权限判断统一写 `userStore.has('码')`(全库 **8 处**),
|
||||
比对的是 `PermissionDef` 的**原样字符串**(`progress:viewAll` 是驼峰,**不是** `progress:view:all` ——
|
||||
写错只会恒为 false,typecheck / lint / 后端都不报错)。`check-wiring.py` 第 ⑦ 项会核对 PC 端引用的权限码是否都存在。
|
||||
> ⚠️ **这条防线曾经静默失效**:第 ⑦ 项原来只认 `permissions.includes('码')` 与 `v-hasPerm="['码']"`,
|
||||
> 重搭改成 `user.has('码')` 之后它一个码都扫不到,却照样打印"PC 端引用的权限码 0 个"并判为通过
|
||||
> (**看起来是绿,实际是检查器瞎了**)。已补上 `.has()` / `.hasAny()` 的识别(见 §一·三十七)。
|
||||
5. **Naive UI 的 `n-tag` 的 `type` 必须过 `@/utils/tag.ts`**:`apis/xt/*.ts` 里的展示映射是从 Arco 带过来的
|
||||
**色词**(`green` / `arcoblue` / `gold` / `gray`),直接透传给 `n-tag` 会**静默变成默认灰色** ——
|
||||
状态色全丢,且不报错。**不要**去改那些映射的 `color` 值(别处也在引用它们)。
|
||||
|
||||
### 五、PC 端重搭:Gi Admin Pro + Arco → 纯 Vue 3 + Naive UI
|
||||
|
||||
- 依赖换成 **`naive-ui` 2.42**(`package.json` 已无 Arco / Gi);`pnpm dev`(5173,`/api` 代理到 8080)/
|
||||
`pnpm build` / `pnpm typecheck` 三个命令不变,**`pnpm typecheck` 现在 0 错**(学生端仍是已知 2 条 `#ifdef` 重复导出);
|
||||
- **`pnpm lint:style`(Stylelint)已随重搭移除**,只剩 `pnpm lint`(ESLint);
|
||||
- **没有 auto-import**:组件与 API 全**显式 import**;
|
||||
- 图标:`scripts/extract-pc-icons.py` 从 `@iconify-json/icon-park-outline` 抽**本项目实际用到的 37 个**,
|
||||
生成 `frontend-pc/src/icons/icon-data.ts`。`AppIcon` 对**缺失图标是空占位、不报错** ——
|
||||
**图标集升级时必须重跑这个脚本**,否则新加的图标显示成空框(内网离线部署不能运行时去 iconify 拉);
|
||||
- ⚠️ **`frontend-pc/.agents/` 已随重搭整体重写为 Naive UI 版**(`code-css` / `code-vue` / `project-guide`
|
||||
等均已改写,`.agents/README.md` 顶部有说明)—— **但 §一·六 / §一·三十四 等节里的 Arco 经验**
|
||||
(列宽按容器重分配、`y:'100%'`、`flattenData` 二次 slice、下拉选项全渲染在 DOM 里)
|
||||
**重搭后不再适用**,保留它们只是记录当时的排查过程,不要照搬到 Naive UI。
|
||||
|
||||
### 六、⚠️ 治理:这是**需求回退**,不是新功能
|
||||
|
||||
**加入申请(确认函 B1)是客户 23 项确认里唯一的功能增量**,本次取消属**需求回退**。
|
||||
建议补一张**变更确认单**,写清三件事:① 门禁失败页不再有自助通道;
|
||||
② 名单外的人由老师在任务里**手工加人**;③ 系统内**不展示任何联系方式**(铁律不变)。
|
||||
文档侧已在 `docs/03` §4.11 与 §8.7 留下撤销说明,`docs/01` 的 B1 条目也已标注取消。
|
||||
|
||||
---
|
||||
|
||||
## §一·三十七 PC 端 UI 修复:重搭后的"错乱"与卡片式基线(2026-10)
|
||||
|
||||
> 一句话:客户反馈"改完 UI 之后后台页面都有问题,工作台是错乱的,区域间隔与按钮都不明显"。
|
||||
> 逐页截图审计后定位到 **1 个结构性版面缺陷 + 3 类静默故障**,已修并补上两处自动检查。
|
||||
|
||||
### 一、工作台"错乱"的真正原因:`NGrid` 的 `cols` 键写成了像素值
|
||||
|
||||
`cols="1 640:2 1100:4"` 里的 `640` / `1100` **不是断点名**,而 `defaultBreakpoints` 只有
|
||||
`xs/s/m/l/xl/xxl`。`seemly` 的 `parseResponsivePropValue` 在数组里逐个键去 `classObj` 里找,
|
||||
全都找不到 → 回退到 `classObj['']` = `1` → **四张统计卡永远挤成一列**,右侧留下大片空白。
|
||||
正确写法是 `cols="1 s:2 m:4"`(xs<640 / s≥640 / m≥1024 / l≥1280)。
|
||||
|
||||
> ⚠️ 这类错**不报错、不警告**,看起来只是"排版不好看"。
|
||||
|
||||
### 二、五个"静默故障"(编译 / lint / typecheck 全绿,只有从入口走一遍才暴露)
|
||||
|
||||
| # | 现象 | 根因 |
|
||||
|:--:|---|---|
|
||||
| 1 | 「收集任务」的**状态筛选 tab 一个字都不显示**(整行高 12px),审核页的节点 tab 同样 | naive-ui **2.45.3** 的 `Tab` 渲染:`tab="文字"` 属性走 `normalizeVNode(() => render(mergedTab ?? name))`,thunk 返回 Text VNode → 被当块 `createBlock(vnode)` → 只留空注释节点。**必须改用 `#tab` 插槽**(那条分支返回数组,正常) |
|
||||
| 2 | 工作台的 `<n-button>` / `<n-avatar>`、字典页的 `<n-form>`、账号页的 `<n-alert>`、名册抽屉的 `<n-divider>` **样式整块丢失** | **漏 import**(本仓库没有 auto-import)→ Vue 把它当**未知自定义元素**渲染。共 4 个文件 6 处(改造过程中又新增 1 处,被第 ⑧ 项检查逮住) |
|
||||
| 3 | 「操作日志」**一行数据都看不见**:行在 DOM 里、`innerText` 也查得到,屏幕上却只有表头 + 紧贴其下的分页器 | `.page-card` 高度链的**第一环**(卡片内容区要是能撑满的 flex 列)写在 `:deep(.n-card__content)` 里 —— **这个类名不存在**:naive 的内容区是 `.n-card-content`(`cB("card-content")`,**单短横**;而 `.n-button__border` 那种是 `cE` 生成的**双下划线**,两套命名混用)。已改为全局 `.page-card > .n-card-content`,页面不再需要各自传 `:content-style` |
|
||||
| 4 | 审核详情顶部的状态标签**颜色永远不对**("已通过"显示成待终审的蓝) | `AuditDetailDrawer` 的本地状态表照抄了**名册行**的口径(`1 草稿 / 2 待初审 / …`),套到**填报单**上整体错位一格。文案被服务端 `statusText` 兜住了,所以只有颜色错 —— 现在统一放 `apis/xt/audit.ts` 的 `SUBMISSION_STATUS`,并在文件头写清两套枚举起点不同 |
|
||||
| 5 | 名单管理抽屉里「已选 1 个班」三个词被摊到整行两端 | `.org-drawer__pane-title` 是 `display:flex + space-between`,而 **flex 会把裸文本节点各自变成一个 flex item** → 标题必须整个包在一个元素里 |
|
||||
| 6 | **凡是有标题的卡片,第一行内容都贴着标题下面那条灰条**("卡片顶端没有 padding") | Naive 的卡片内容区是 `padding: 0 var(--n-padding-left) var(--n-padding-bottom) …`,`padding-top` 只写在 `&:first-child` 上 —— 也就是**只给"没有卡片头"的卡片**。已改成全局 `.n-card.n-card > .n-card-content { padding: var(--xt-card-pad-y) var(--xt-card-pad-x) }` |
|
||||
|
||||
> ⚠️ 第 6 条有个**连带坑**:全局那条是普通样式,**会被内联样式盖掉** ——
|
||||
> `org/index.vue` 与 `user/index.vue` 的左栏写了 `:content-style="{ padding: '12px' }"`,
|
||||
> 结果那两张卡比右栏紧一圈(12 vs 16/18)。这两处已删掉内边距内联样式;
|
||||
> 要调内边距请改 `--xt-card-pad-x` / `--xt-card-pad-y`。
|
||||
|
||||
### 二·补 发现但**本次没改**的问题(留档,别当成已修)
|
||||
|
||||
1. **`NFormItem` 的 `#feedback` 插槽会把校验错误挡掉**(naive 的渲染是 `feedbackNodes = children || feedback`,
|
||||
有 `#feedback` 就不再渲染 `renderExplains`)→ 字段变红框但**一句说明都没有**。
|
||||
全仓有 **30 处** `#feedback`(多数是静态提示)。`RoleFormModal` / `tenant` 的"主操作永远提交不出去"
|
||||
正是被这一条放大的(见下)。建议的修法:在 `onSubmit` 的 `validate()` catch 里补一个
|
||||
`message.error(第一条错误)`,代价最小;彻底修要包装一个 `XtFormItem`。
|
||||
2. `RoleFormModal` 的 `userType` 与 `tenant/index.vue` 的 `tenantType` 是**数字**却只写 `required` ——
|
||||
已补 `type: 'number'`(数字选择框不带 `type` 时 async-validator 会按 string 走)。
|
||||
这两处**已修**,列在这里是因为它可能与上面第 1 条叠加。
|
||||
3. 左树最深一级遇到 E2E 那种超长组织名("E2E 24数控1班 607655")仍会折成两行;
|
||||
已把字号压到 13px、`indent` 压到 16px,再窄就要破 220~260 的栏宽约定或藏掉节点右侧标签。
|
||||
|
||||
### 三、卡片式基线(新增,页面只引用不再各写一套)
|
||||
|
||||
- `src/styles/index.scss` 的 `:root` 定义 `--xt-bg / --xt-gap / --xt-card-border / --xt-card-radius /
|
||||
--xt-band-bg / --xt-text*` 等;`.page-card` 的高度算式改用 `--xt-header-h` 与 `--xt-content-pad`,
|
||||
**不再写死 `calc(100vh - 56px - 28px)`**(改内边距时不会再漂移);
|
||||
- 新增全局类 `.toolbar`(自带下分隔线)、`.section-title` + `.section-title__note`、`.page-plain`;
|
||||
- 卡片外观(白底 + 描边 + 轻阴影 + 卡片头浅灰色带)由 `.n-card.n-card` **统一**给
|
||||
(权重写成 `.n-card.n-card` 是因为 naive 的样式由 css-render **运行时注入**,先后顺序不保证);
|
||||
- **页面底色才是关键**:`NLayoutContent` 自带白底(`--n-color`),会把外层 `.app-shell__main`
|
||||
的灰底盖掉 —— 上一版因此变成「白卡片浮在白底上」,整页没有任何边界,这正是"区域间隔不明显"的根因。
|
||||
已在 `src/layout/index.vue` 显式改回 `--xt-bg`;
|
||||
- 按钮可见性:`App.vue` 的 `themeOverrides` 里把默认按钮描边由 `#e0e0e6` 加深到 `#d0d5dd`,
|
||||
并把 Card / DataTable 的表头、圆角、阴影统一;**工具条控件统一默认尺寸**(原来全排 `size="small"`,
|
||||
28px 白底小按钮贴在白底表格上几乎看不出是按钮)。
|
||||
|
||||
> ⚠️ naive 的按钮**边框画在子元素 `.n-button__border` 上**,不是根节点 ——
|
||||
> 用 `getComputedStyle(btn).borderTopWidth` 检查会得到 `0px`,别据此判断"边框没生效"。
|
||||
|
||||
### 四、新增/修复的两处自动检查(都在 `scripts/check-wiring.py`)
|
||||
|
||||
| 项 | 内容 | 为什么必须有 |
|
||||
|:--:|---|---|
|
||||
| ⑦(**修**) | 补上 `user.has('码')` / `hasAny(...)` 的识别 | 原来只认 `permissions.includes` 与 `v-hasPerm`,重搭后**一个码都扫不到却判为通过** —— 防线静默失效 |
|
||||
| ⑧(**新**) | 核对模板里的 `<n-*>` 组件是否都在本文件 import 了 | 漏 import 是静默故障(见上表 #2),编译与 lint 都不报 |
|
||||
|
||||
> 📌 两个检查都做过**反向验证**:故意写错一个权限码 / 故意删掉一个 import,它们必须红。
|
||||
> 第 ⑦ 项现识别 **8 个权限码**,与 `PermissionDef` 逐字一致。
|
||||
|
||||
### 四·补 覆盖范围与结果(这一轮实际动过的地方)
|
||||
|
||||
- **页面(11 屏)**:工作台、收集任务、审核、导出中心、通知记录、字典管理、组织架构、账号管理、
|
||||
角色与授权、操作日志、租户管理;另有登录页与 403/404 的对齐、任务页 5 个抽屉与各弹窗的一致性。
|
||||
- **校验**:`pnpm typecheck` **0 错**、`pnpm lint` **0 错**(⚠️ 重搭后曾残留 **8 条**
|
||||
`vue/component-name-in-template-casing` —— 就是上表 #2 那批小写标签,已随 PascalCase 化清零)、
|
||||
`check-wiring.py` ⑦⑧ 全绿且"未接线的声明 0 条"、`verify-docs.py` 22 项全过。
|
||||
- 页面截图存于 `frontend-pc` 之外的 `shots/`(**已在 `.gitignore` 里,不要提交**;
|
||||
其中 `shots/auth.json` 含浏览器登录态,只能本地用)。
|
||||
|
||||
### 五、约定同步
|
||||
|
||||
`frontend-pc/AGENTS.md`、`.agents/skills/code-css`、`code-vue`、`project-guide` 已同步:
|
||||
「最容易写错的约定」由 4 条扩到 **6 条**(新增 `#tab` 插槽、卡片式版面),
|
||||
并把漏 import 的静默后果、按钮边框在子元素上、flex 裸文本节点这几个坑写进去。
|
||||
|
||||
---
|
||||
|
||||
## §一·三十八 品牌改名「深学在线」+ 重做品牌标(2026-10)
|
||||
|
||||
> 一句话:客户要求系统改名,并明确「**设计一个 logo,不要直接用文字 logo**」。
|
||||
> 全库旧名(`填报通` / `数据信息填报系统`)已换成 `深学在线`;
|
||||
> 原来的「圆角蓝方块 + 填字」文字标换成几何标,另修掉一个页签图标 404。
|
||||
|
||||
### 一、改名落点(旧名现在**只应出现在刻意保留的地方**,见第二节)
|
||||
|
||||
| 层 | 文件 |
|
||||
|---|---|
|
||||
| PC 界面 | `index.html` 的 `<title>`、`router/index.ts` 的 `document.title` 模板(`工作台 · 深学在线`)、`AppSider.vue` 的侧栏品牌名、`views/login/index.vue` 的品牌名与 `alt` |
|
||||
| 学生端 | `manifest.json` 的 `name`(小程序/App 名)、`pages.json` 的 `globalStyle.navigationBarTitleText`、`pages/index/index.vue` 的租户名兜底文案 |
|
||||
| 后端元信息 | `pom.xml` 的 `<name>`、`OpenApiConfig` 的 `/doc.html` 标题、`application.yml` 注释、`FillApplication` 注释、`StorageConfig` 自检写入的探针内容 |
|
||||
| 文档 / 原型 | `README.md`、`frontend-pc/README.md`、`frontend-pc/package.json` 的 description、`docs/00 / 01 / 07`、两份 PRD 的**标题行与产品代号线**、`docs/prototype/*.html` 的 `<title>` 与品牌区、`deploy/docker-compose.yml` 的头注释 |
|
||||
|
||||
### 二、**刻意没改**的地方(改一个就会出事,别"顺手统一")
|
||||
|
||||
| 没改 | 为什么 |
|
||||
|---|---|
|
||||
| 存储对象前缀 `xiaotutongxue`(`application-dev/prod.yml`、`application-local.yml.example`、`scripts/dev-clean-qiniu.py`) | 那是**对象存储里的目录名**,不是显示名。改了以后库里存的 key 全部指向不存在的对象 —— 学生已交的材料会整片 404 |
|
||||
| `deploy/docker-compose.yml` 的 `name: xiaotutongxue` | 这是 compose **项目名**,决定容器与 volume 的归属。改了会新建一套空 volume,看起来像"数据没了" |
|
||||
| `db/migration/V12__file_source_proxy.sql` 注释里的 PRD 文件名 | **迁移文件发布后不许改**(Flyway 校验 checksum) |
|
||||
| `docs/数据信息填报系统PRD*.md` 的**文件名** | `scripts/verify-docs.py` 按**路径**读它,且全库有 20+ 处按文件名引用。改名 = 连带炸脚本与文档交叉引用 |
|
||||
| `docs/01-客户与机构确认函.md` 的**正文** | 该文自己声明"本函正文保留当时原文(它是'当时确认过什么'的记录)",只改了标题行 |
|
||||
| `frontend-mp/src/static/logo.png` | uni-app 模板自带的遗留资产,**全仓库零引用**,本次没动它 |
|
||||
|
||||
### 三、新品牌标:方(卷宗)+ 环(在线)
|
||||
|
||||
原标是「圆角方块 + 『填』字」,属于**文字标**。客户明确要求换成设计的图形标,理由也成立:
|
||||
字形标缩到 16px 页签会退化成一块色斑,且换字体族就走形。
|
||||
|
||||
| 稿 | 结果 |
|
||||
|---|---|
|
||||
| 卷宗叠层 / 折角卷宗 | 语义最直白,但 24px 以下就是通用的「文档」图标,没有识别度 |
|
||||
| 三层同心圆角方 | 16px 下像相机/录制按钮 |
|
||||
| 扇形三卷宗 | 48px 好看,24px 后排飞散全糊 |
|
||||
| 下潜楔 / 斜地层 / 三源归一 | 小尺寸糊,或像下载、阶梯类通用图标 |
|
||||
| **方 + 环(定稿)** | 16 / 20 / 24px 轮廓都成立 |
|
||||
|
||||
定稿的两个形状都有出处:**实心圆角方 = 卷宗**(承接原型「档案室 / 电子卷宗」的设计语言),
|
||||
**白环 = 在线 / 汇聚点**(这件产品的入口本来就是扫码进来)。环画在方**之前**,
|
||||
只露右上四分之一 —— 一个标里同时读到"在线"与"归档"。
|
||||
|
||||
> ⚠️ **后排为什么是环,不是第二枚圆角方**:两枚错位圆角方是通用的「**复制 / 多副本**」图标,
|
||||
> 会被直接读成那个意思(这是第一版几何稿被否掉的原因)。换成环之后歧义消失;
|
||||
> 环与方是**相叠**的(不是分离的两件东西),所以也不会被读成放大镜。
|
||||
|
||||
构造写在 `frontend-pc/src/assets/images/logo.svg` 的注释里(对齐 64 栅格、四周留白对称 8.5、
|
||||
外接盒 47×49.3 居中)。五轮实测对比稿留在 `shots/logo-lab*.html`(`shots/` 已在 `.gitignore`)。
|
||||
|
||||
> ⚠️ 留白最初写的是 **11.25**,尺寸表一量才发现内容只占底板 **65%** ——
|
||||
> 24px 侧栏下真正画出来的只有 16px,标明显偏小。已按 8.5 重算(等比放大 1.133),
|
||||
> `logo.svg` / `favicon.svg` / 原型 `.mark` 三处同步改过。
|
||||
> **改标之后一定要看尺寸表**(`shots/logo-final.html` 直接引仓库里那两个真文件,不是副本)。
|
||||
|
||||
改标要同步三处,否则品牌标不一致:`public/favicon.svg`(**加粗版**:去掉渐变与高光、
|
||||
描边 5.9 → 7.7,因为 5.9 缩到 16px 只剩 1.5px 会被抗锯齿吃掉)、
|
||||
`AppSider`/login 的引用尺寸(24 / 34px)、`docs/prototype/admin-web.html` 的 `.mark`(10 处,内嵌同形状 data URI)。
|
||||
|
||||
### 四、顺手修掉:页签图标一直是 404
|
||||
|
||||
`index.html` 引的是 `<link rel="icon" href="/favicon.ico">`,而 `public/` 下**只有 `favicon.svg`**,
|
||||
没有 `.ico` —— `/favicon.ico` 实测 404,**页签一直没有图标**(不报错,只是空白)。
|
||||
已改成 `<link rel="icon" type="image/svg+xml" href="/favicon.svg">`(实测 200 / `image/svg+xml`)。
|
||||
|
||||
### 五、第六类静默故障:SVG 注释里的连续两个减号
|
||||
|
||||
第一版 `logo.svg` 的注释里写了一句"与 `src/styles` 的 CSS 变量写法一致",
|
||||
其中那两个减号开头的东西**让整个 SVG not well-formed**(XML 规范禁止注释内出现连续两个减号)。
|
||||
|
||||
后果是**完全静默**的:`<img src="...logo.svg">` 加载失败,浏览器控制台一句话都不打,
|
||||
`vue-tsc` / `eslint` / `check-wiring` 全绿,页面上只是**登录页与侧栏的标变成裂图**。
|
||||
|
||||
已新增第 ⑨ 项检查(`scripts/check-wiring.py`):把每个 `.svg` 当 XML 解析一遍。
|
||||
**做过反向验证**:故意放一个注释含连续减号的 SVG,它报红并指出文件名 + 行列号;撤掉后转绿。
|
||||
目前扫到 **3 个** SVG 资产。
|
||||
|
||||
> 📌 后两项(⑧ 漏 import、⑨ SVG 非法)严格说不是"声明没接线",
|
||||
> 而是**同一族:坏了但没有任何工具会红** —— 放 `check-wiring.py` 就是因为
|
||||
> 要的是"一条命令把这一族扫一遍"。
|
||||
|
||||
### 六、验证
|
||||
|
||||
- `pnpm typecheck` **0 错** · `pnpm lint` **0 错**
|
||||
- `check-wiring.py` ⑦ 8 个权限码一致 / ⑧ 漏 import 0 处 / **⑨ SVG 全部合法** / 未接线的声明 0 条
|
||||
- `verify-docs.py` **22 项全过**
|
||||
- 浏览器实测:`document.title` = `工作台 · 深学在线`;侧栏 24px 与登录页 34px 的标
|
||||
`img.complete = true`、`naturalWidth = 64`(**改坏时这里是裂图**);
|
||||
`link[rel=icon]` = `/favicon.svg`;原型 `.mark` 的 data URI 能解码(`Image.onload` 通过,10 处)
|
||||
- 旧名残留复查:全库仅剩**文件名引用**(PRD 路径)、**存储前缀**、**冻结的迁移文件注释**、
|
||||
`.idea/` 的 IDE 缓存,以及 `docs/01` 那段有意保留的正文
|
||||
|
||||
### 七、构建产物也要重跑(**源码改了产物不会变**)
|
||||
|
||||
改品牌名时最容易漏的一环:`frontend-mp` 的 H5 是**后端托管的构建产物**
|
||||
(`dist/build/h5/`,见 `AGENTS.md` 的"先做 H5"),源码改了它一个字都不会变。
|
||||
本次实测:源码改完后产物里**仍是「填报通」**。
|
||||
|
||||
已重跑并复查:
|
||||
|
||||
| 产物 | 命令 | 复查结果 |
|
||||
|---|---|---|
|
||||
| `frontend-mp/dist/build/h5` | `pnpm build:h5` | 3 处「深学在线」,**无旧名** |
|
||||
| `frontend-pc/dist` | `pnpm build` | `index.html` 标题 = `深学在线`、`link[rel=icon]` = `/favicon.svg`,**无旧名** |
|
||||
| `frontend-mp/dist/dev/mp-weixin` | — | **没重建**:它是 `pnpm dev:mp-weixin` 的 watch 产物,下次跑那个命令就会自动带上新名(小程序端不是当前节奏) |
|
||||
|
||||
> ⚠️ `dist/` 都在 `.gitignore` 里,所以**不会有人从 git 上发现问题** ——
|
||||
> 这一环只能靠"改完记得重跑"。后端同理:改了 `pom.xml` / `OpenApiConfig` 那几处,
|
||||
> 不 `mvn -DskipTests package` 重启,`/doc.html` 还会打旧标题(本次已重打包重启,
|
||||
> `/v3/api-docs` 实测 `info.title = 深学在线 API`)。
|
||||
>
|
||||
> 📌 附带踩到一个进程坑:**`job_kill` 杀掉的是 pwsh 外壳,`java` 子进程不一定跟着走**,
|
||||
> 而 Windows 下 `java -jar` **不独占锁** —— 于是 Maven 会把 jar 覆盖掉,
|
||||
> 变成一个"跑着的 JVM + 被换掉的 jar"。改后端要**先确认 8080 没人监听**再打包。
|
||||
|
||||
### 八、约定同步
|
||||
|
||||
`frontend-pc/AGENTS.md`、`.agents/skills/project-guide/SKILL.md`、`.agents/README.md` 已同步:
|
||||
「最容易写错的约定」由 **6 条扩到 7 条**(新增「品牌标」一条,含 SVG 注释的 XML 陷阱);
|
||||
根 `AGENTS.md` 的接线检查说明补上第 ⑨ 项。
|
||||
|
||||
|
||||
|
||||
|
||||
+38
-71
@@ -1,7 +1,7 @@
|
||||
# 数据信息填报系统 · 功能逻辑 PRD
|
||||
# 深学在线 · 功能逻辑 PRD
|
||||
|
||||
> 产品代号:**填报通 / CollectFlow**(暂定)
|
||||
> 版本:V1.9(2026-09-28 同步数据库决策与字段落库方式)
|
||||
> 产品代号:**深学在线**
|
||||
> 版本:**V2.0**(2026-10 需求收敛:4 角色 / 不设二期 / 门禁失败只留「重新填写」)
|
||||
> 日期:2026-09-24
|
||||
> 文档定位:**只描述"系统怎么运转",不含数据库设计、接口定义、代码结构**
|
||||
> 配套文档:《数据信息填报系统PRD》(主 PRD,含库表与接口)—— 主 PRD 回答"做什么",本文回答"怎么运转"
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
| 版本 | 日期 | 变更内容 |
|
||||
|---|---|---|
|
||||
| **V2.0** | **2026-10** | **需求收敛**:① 角色由 6 个减为 **4 个**(平台运营 / 学校管理员 / 班主任 / 学生;删「副班主任」「审核员」);② §2.2 社会收集、§3.12 历史数据整理器、专项职业能力相关段落 **整节作废**;③ §3.1 门禁失败**只留「重新填写」**——原「申请加入名单」(FR-509)通道整体撤销,名单外的人由老师手工加人,系统内不展示任何联系方式;④ **不再有任何二期功能设计**(OCR 与材料识别改为「缓做,等供应商」)。跨文档数字:接口 **119** / 错误码 **54** / 权限码 **28** / 原型 **29 屏**。 |
|
||||
| V1.0 | 2026-09-24 | 初稿:角色/流转/12 个功能逻辑/规则速查/20 个边界场景/文案规范 |
|
||||
| **V1.1** | 2026-09-24 | **压缩逻辑按实测重写**:用 28 张真实 A4 扫描件实测,**300dpi/q85 下 28/28 达标(平均 806KB)**,无需牺牲清晰度。① §3.7.1 新增实测结论;② §3.7.3 由"逐级降质逼近"改为"一次编码到位 + 兜底路径";③ §3.7.4 抽检机制标注为"实测不触发";④ §4 速查表、§7 未决事项同步更新 |
|
||||
| **V1.2** | 2026-09-24 | **修正材料来源通道**:取消"直接拍照"(实测 4 类材料无一来自现场拍照,均为截图/扫描件/照相馆电子版)。① §3.5 改为"**微信聊天记录 / 手机相册 / 聊天文件**"三通道,拍照默认关闭;② 新增"**必须保留原始文件名**"设计逻辑(聊天记录可拿到原始名,相册拿不到);③ 新增来源引导文案与 4 个相关异常分支;④ §4 速查表、§5 场景表(+4 条)、§6 文案、§7 未决事项同步更新 |
|
||||
@@ -85,8 +86,9 @@
|
||||
| 平台运营 | PC | 开通/停用租户、查看全局用量、内容风控 | 查看任何考生的材料内容 |
|
||||
| 学校管理员 | PC(主)+ 手机(次) | 建任务、配规则、导名册、终审、导出上报 | 修改已归档任务的配置 |
|
||||
| 班主任 | PC + 手机 | 催报、初审、批量通过、代传材料 | 终审、导出、查看其他班级 |
|
||||
| ~~副班主任~~ / ~~审核员~~ | — | **已删除**(2026-10 需求收敛):内置角色由 6 个减为 **4 个**(平台运营 / 学校管理员 / 班主任 / 学生) | — |
|
||||
| 填报人(学生) | 微信小程序 | 填报、上传、修改、撤回(未审核前) | 查看他人材料、修改已终审内容 |
|
||||
| 社会填报人 | 小程序 / H5 | 自主填报 | 查看他人、指定班主任 |
|
||||
| ~~社会填报人~~ | — | **不做**(2026-10 取消社会收集) | — |
|
||||
|
||||
**端能力差异(功能逻辑层面)**
|
||||
|
||||
@@ -113,7 +115,7 @@
|
||||
填报人扫码 → 微信授权
|
||||
↓ 【门禁】填写「姓名 + 身份证号」→ 与名单匹配
|
||||
↓ 匹配成功 → 绑定名单项 → 回填只读信息 → 进入填报
|
||||
↓ 匹配失败 → 统一提示页(重新填写 / 申请加入名单),不给填报
|
||||
↓ 匹配失败 → 统一提示页(只有「重新填写」+ 提示联系老师),不给填报
|
||||
填报人逐项填写表单 → 逐项上传材料
|
||||
↓ 每次上传后立即:命名规范化 → 压缩 → 校验 → (必要时)OCR
|
||||
↓ 填报人看到"文件名已规范 / 体积已压缩 / 有无问题"的即时反馈
|
||||
@@ -133,22 +135,11 @@
|
||||
|
||||
> 🔒 **门禁不变式**:**没有名单,就不能填报。** 名单是唯一的准入依据,不存在"自由填报"。
|
||||
|
||||
### 2.2 社会收集(差异流程)
|
||||
### 2.2 社会收集(差异流程)—— **不做**(2026-10 取消)
|
||||
|
||||
```
|
||||
【前置】管理员建任务(模式=社会收集,无班主任层,但同样必须先导入名单)
|
||||
↓ 导入名单(预报名名单 / 意向名单 / 报名点收集的名单)
|
||||
↓ 生成公开填报二维码/链接
|
||||
社会人员扫码 → 微信授权
|
||||
↓ 【门禁】填写「姓名 + 身份证号」→ 与名单匹配(规则与学校收集完全一致)
|
||||
↓ 匹配失败 → 统一提示页,不给填报
|
||||
填报 → 上传材料(同样走规范化 / 压缩 / 校验)
|
||||
提交 → 直接进入【待终审】(跳过初审)
|
||||
管理员终审 → 通过 → 导出
|
||||
```
|
||||
|
||||
> ⚠️ **与学校收集唯一的差异**:审核流跳过班主任层。**名单导入与身份核验两项完全一致。**
|
||||
> 📌 初稿曾写"社会收集名册可选、允许自由填报",本版已废除。
|
||||
> ⚠️ **本节作废**:社会收集(`collectMode=2`)**不再设计**,传入直接返回 **`40044`**。
|
||||
> 原设计的差异点(跳过班主任初审、直达管理员终审)记录在此仅供追溯。
|
||||
> **名单导入与身份核验两项与学校收集完全一致**,这条不变。
|
||||
|
||||
### 2.3 状态机
|
||||
|
||||
@@ -219,7 +210,7 @@
|
||||
本地先做格式 + 校验位校验 → 不合格当场提示,不发请求
|
||||
↓ ⑤ 提交匹配(在服务端完成)
|
||||
↓ ⑥ 命中 → 绑定 → 进入填报页
|
||||
未命中 → 失败提示页(重新填写 / 申请加入名单)
|
||||
未命中 → 失败提示页(只有「重新填写」+ 提示联系老师)
|
||||
```
|
||||
|
||||
**匹配判定顺序**
|
||||
@@ -244,9 +235,10 @@
|
||||
|
||||
> "**没有找到你的信息。**
|
||||
> 请核对姓名和身份证号是否与老师提供的名单一致。
|
||||
> 如果确实已报名,可以申请加入名单,由老师审核。"
|
||||
> 如确认已报名,请联系老师把你加入名单。"
|
||||
|
||||
提供两个动作:**重新填写** / **申请加入名单**(FR-509)——**均为系统内动作,不展示任何联系方式**
|
||||
只提供一个系统内动作:**重新填写**;另加一句纯文案提示"请联系老师把你加入名单"
|
||||
(原「申请加入名单」自助通道已于 2026-10 撤销)——**系统内不展示任何联系方式**
|
||||
|
||||
**🔒 安全要求(重要,必须实现)**
|
||||
|
||||
@@ -270,10 +262,10 @@
|
||||
| 同一微信帮多人填(哥哥帮弟弟) | 允许,但**切换填报人需重新走核验**,且记录操作留痕 |
|
||||
| 名单外的人确实要报名 | 本版按需求实现为"**不给填、只提示**",需老师去后台把人加进名单(见下方待决策事项) |
|
||||
|
||||
> 🤔 **待决策:名单外的人怎么处理?**
|
||||
> **修订(FR-509)**:名单外的人原先只能"联系老师",老师需反复去后台加人——这在几百人的社会收集中会成为运营负担。现已改为**学生自助申请 + 管理员逐条审批**,且**系统内不展示联系方式**。
|
||||
> **建议方案**(未纳入本版):在失败页增加"**申请加入名单**"按钮 → 生成管理员待办 → **管理员批准后自动放行**。
|
||||
> 这样既守住"必须先有名单"的门禁,又不用老师手工加人。**请确认是否采纳。**
|
||||
> ✅ **已决策(2026-10,需求收敛):名单外的人由老师在任务里手工加人,不做自助通道。**
|
||||
> 曾经的"学生自助申请 + 管理员逐条审批"(FR-509)已**整体撤销**(模块、表、接口、页面、错误码一并删除)。
|
||||
> 门禁失败页只保留「重新填写」+ 一句"请联系老师把你加入名单",**系统内不展示联系方式**。
|
||||
> 这样守住的仍是同一条铁律:**没有名单,就不能填报** —— 加人的动作留在老师手里。
|
||||
|
||||
### 3.2 报考范围聚合与任务创建
|
||||
|
||||
@@ -323,7 +315,7 @@
|
||||
| 整批出现多组不同的报考项 | 正常(一个任务可覆盖多个工种),但需管理员确认无误 |
|
||||
| 老师发现填错想改 | 管理员可直接修改名单行(留痕),范围自动重新聚合 |
|
||||
| 名单里出现参考字典没有的职业/工种 | 仅提示"可能拼写有误",**不阻断**(最终以机构要求为准) |
|
||||
| 认定类型 = 专项职业能力 | 三项中只有"职业"有值,工种/等级允许为空 |
|
||||
| ~~认定类型 = 专项职业能力~~ | **不做**(2026-10 取消;传 `certType=3` 返回 `40044`) |
|
||||
|
||||
**关键设计**
|
||||
|
||||
@@ -450,13 +442,13 @@
|
||||
|
||||
| 认定类型 / 等级 | 最多可选科目数 | 可选项 |
|
||||
|:--:|:--:|---|
|
||||
| **专项职业能力** | **0** | 无补考 |
|
||||
| ~~**专项职业能力**~~ | ~~0~~ | **不做**(2026-10 取消) |
|
||||
| 三 / 四 / 五级 | **1** | 补考理论、补考实操 中选一个 |
|
||||
| 一 / 二级 | **2** | 补考理论、补考实操、补考综合 中最多选两个 |
|
||||
|
||||
⇒ 达到上限后,其余科目选项**置灰不可选**(而不是事后报错)。
|
||||
|
||||
6. **专项职业能力的整体降级**:`认定类型 = 专项职业能力` 时,**工种、等级、补考三项全部隐藏**,官方规则 05/06/07 整体跳过。
|
||||
6. ~~**专项职业能力的整体降级**~~:**不做**(2026-10 取消二期;`certType=3` 直接 `40044`)。原设计是:该类型下工种、等级、补考三项全部隐藏,官方规则 05/06/07 整体跳过。
|
||||
|
||||
7. **下拉严格受限**:官方规定"**若手动输入则只能填写下拉选项中包含的值**"。
|
||||
⇒ 移动端一律用选择器;PC 端允许输入但做**严格值匹配**,不在选项内的值直接拒绝。
|
||||
@@ -494,7 +486,7 @@
|
||||
| 提交后又想改 | 未进入审核前可"撤回";已进入审核需联系老师驳回 |
|
||||
| **报考范围已关闭**(认定机构批次结束) | 提示"本批次已结束",不允许新建填报单;已提交的不受影响 |
|
||||
| **学生发现自己的报考职业/工种/等级不对** | 提示"该三项由老师统一确定,请联系老师修改",**不允许自行输入** |
|
||||
| **选了"专项职业能力"却想填工种/等级** | 该三项不显示;若历史数据带出旧值,自动清空 |
|
||||
| ~~选了"专项职业能力"却想填工种/等级~~ | **不做**(2026-10 取消该认定类型) |
|
||||
|
||||
### 3.5 材料上传与材料识别逻辑
|
||||
|
||||
@@ -634,7 +626,7 @@
|
||||
| # | 场景 | 问题 |
|
||||
|---|---|---|
|
||||
| 1 | 填报上传 | 用户在"身份证"槽位传了学籍表 → 需要发现并提示 |
|
||||
| 2 | 整理器散图分拣 | 一堆没有目录的散图 → 需要判断每张是什么,才能归入目录 |
|
||||
| ~~2~~ | ~~整理器散图分拣~~ | **已取消**(2026-10,整理器模块删除) |
|
||||
| 3 | 审核辅助 | 班主任想快速确认"这堆材料里有没有传错的" |
|
||||
| 4 | 缺失检测兜底 | 例如"其它佐证材料"传了 3 张,需要区分它们分属什么 |
|
||||
|
||||
@@ -795,8 +787,8 @@
|
||||
|---|---|---|
|
||||
| 填报上传 | 每次上传后(异步) | 冲突时提示 + 一键移动 |
|
||||
| 提交前总校验 | 提交时 | 汇总"疑似传错"清单,按 🟡 警告呈现 |
|
||||
| 整理器·散图分拣 | 无目录结构时 | 自动归属;判不出的进待人工归属 |
|
||||
| 整理器·体检报告 | — | 输出"疑似错位"清单 |
|
||||
| ~~整理器·散图分拣~~ | — | **已取消**(2026-10) |
|
||||
| ~~整理器·体检报告~~ | — | **已取消**(2026-10) |
|
||||
| 审核辅助 | 班主任查看时 | 材料卡片上显示"系统判定:身份证 92%" |
|
||||
| 规则优化 | 定期 | 用人工纠正确认的结果反哺关键词表 |
|
||||
|
||||
@@ -1146,38 +1138,13 @@
|
||||
- 系统记录每个人剩余的授权额度,额度不足时自动降级到服务号或短信;
|
||||
- 产品层减少催报次数、提升一次填完率(减少返工即减少通知)。
|
||||
|
||||
### 3.12 历史数据整理器逻辑
|
||||
### 3.12 历史数据整理器逻辑 —— **已取消**(2026-10 需求收敛)
|
||||
|
||||
**用途**:处理老师手上已经存在的"乱文件夹",无需重新收集即可产出规范结果。作为获客入口(先看到价值,再谈系统上线)。
|
||||
|
||||
**触发**:上传一个压缩包,或指定一个已有目录结构。
|
||||
|
||||
**处理步骤**
|
||||
1. **解析文件树**:识别目录与文件名,生成清单。
|
||||
2. **逐文件走同一套规则**:形态识别 → 命名规范化(§3.6)→ 压缩(§3.7)→ 校验(§3.8)。
|
||||
3. **生成《材料体检报告》**,包含:
|
||||
- 命名违规清单(原文件名 → 规范名)
|
||||
- 证件号异常清单(与名册比对的结果,含裁决结论)
|
||||
- 体积超标清单(压缩前后对比)
|
||||
- 分辨率不足清单
|
||||
- 缺失材料清单(同一人缺了哪几项)
|
||||
- 重复文件清单
|
||||
4. **输出规范化后的压缩包**,目录结构可直接上报。
|
||||
5. 报告与结果可生成分享链接给班主任。
|
||||
|
||||
**特殊能力:散图分拣**
|
||||
老师从微信里收到的常常是**一堆没有目录、名字很乱的散图**。整理器需要能:
|
||||
- 按证件号/姓名把散图自动归属到人;
|
||||
- 无法归属的单独列出,供人工拖拽归位;
|
||||
- 归位后按材料类型自动分入对应目录。
|
||||
|
||||
**异常分支**
|
||||
|
||||
| 情况 | 处理 |
|
||||
|---|---|
|
||||
| 压缩包内有嵌套压缩包 | 提示是否一并展开处理 |
|
||||
| 存在无法识别的文件(如 `IMG_001.jpg`) | 归入"待人工归属",不猜测、不放任 |
|
||||
| 名册缺失(没有 Excel) | 允许无名册运行:仅做命名规范与压缩,不做一致性校验,报告中说明该限制 |
|
||||
> ⚠️ **整节作废**:历史整理器(含"散图分拣"与《材料体检报告》)连模块、表、接口、权限码、
|
||||
> PC 页面与原型屏一并删除,**不再排期**。
|
||||
>
|
||||
> 它当年复用的是同一套规则(形态识别 → 命名规范化 → 压缩 → 校验),这条"规则引擎只有一套"的
|
||||
> 设计结论仍然成立,只是**不再有"处理历史乱文件夹"这个入口**。
|
||||
|
||||
---
|
||||
|
||||
@@ -1214,7 +1181,7 @@
|
||||
**准入(门禁)**
|
||||
- **两种模式都必须先导名单**,没有名单不能填报
|
||||
- 进入填报前必须**填写「姓名 + 身份证号」匹配名单**,不通过只提示、不放行
|
||||
- 匹配失败**不区分原因**(防止探测身份证号),只给"重新填写 / 申请加入名单"两个出口
|
||||
- 匹配失败**不区分原因**(防止探测身份证号),只给「重新填写」一个出口 + 提示联系老师加人
|
||||
- 匹配接口必须**限流**(同 openid 每小时 ≤10 次)
|
||||
- 姓名形近字/同音字可纠错,但必须**让用户确认**后才通过
|
||||
|
||||
@@ -1284,7 +1251,7 @@
|
||||
| 17 | 任务截止了但学生要补交 | 学生忘交 | 任务已截止 | 提示"已截止";管理员可延长截止或点名放行 | 🟡 |
|
||||
| 18 | 班主任离职/换人 | 账号停用 | 待办无人处理 | 支持转交;超时自动升级通知管理员 | 🟡 |
|
||||
| 19 | 导出时有人材料不全 | 3 人未完成 | 前置检查不通过 | 🔴 报错并列出名单,提供"仅导出已通过者" | 🟡 |
|
||||
| 20 | 老师手里已有 475MB 的乱文件夹 | 每班 112 个文件 | 走整理器路径 | 输出体检报告 + 规范包 | ✅(内容核对) |
|
||||
| ~~20~~ | ~~老师手里已有 475MB 的乱文件夹~~ | — | ~~走整理器路径~~ | **已取消**(2026-10:整理器模块删除) | — |
|
||||
| 21 | 学生从**聊天记录**选了照相馆发来的照片 | 文件带原始名 `张三-...-一寸照片.jpg` | 文件名可读 → 直接提取姓名/证件号/材料名 | ✅ 一次通过;文案"已按文件名自动识别" | ❌ 自动 |
|
||||
| 22 | 学生从**相册**选了截图 | 文件名为 `IMG_4821.PNG`,1025×590 | 无文件名信息 → 走 OCR + 人工确认归属 | 🟡"这张图没有名字信息,已识别为学籍表,请确认" | 🟡 确认 |
|
||||
| 23 | 学生从聊天记录收到的是被微信压缩过的图 | 分辨率明显偏低 | 低于建议值 | 🟡"这张图可能被压缩过,建议让对方发原图" | 🟡 确认 |
|
||||
@@ -1294,7 +1261,7 @@
|
||||
| 27 | 同一个名单项被两个人绑定 | 冒用 / 代填冲突 | 已被绑定 | 🔴"该信息已被登记,如有疑问请联系老师" | 🟡 |
|
||||
| 28 | 老师忘了导入名单就想发布任务 | 操作遗漏 | 发布前校验不通过 | 🔴"请先导入名单,名单必须包含姓名和身份证号" | ❌ 阻断发布 |
|
||||
| 29 | 学生把学籍表传到了"身份证"槽位 | 槽位与内容冲突 | 规则层识别出关键词"学籍",置信度 0.93 | 🟡"这张看起来是**学籍表**,不是身份证。要移过去吗?"[一键移动] | 🟡 用户确认 |
|
||||
| 30 | 学生传了一堆散图,没归类 | 微信收到的图片 | 整理器无槽位 → 按证件号归属到人 + 按内容判定材料类型 | 自动归入目录;判不出的进"待人工归属" | 🟡 部分人工 |
|
||||
| ~~30~~ | ~~学生传了一堆散图,没归类~~ | — | ~~整理器无槽位 → 自动归属~~ | **已取消**(2026-10) | — |
|
||||
| 31 | 学生传了一张纯色背景的图 | 一寸照片(无文字) | 形态规则:5:7 比例 + 背景纯净 → 置信度 0.88 | 🟡"请确认这张是一寸照片" | 🟡 确认 |
|
||||
| 32 | 学生把正确材料放在正确位置,但因图糊识别失败 | 槽位与内容无法比对 | 置信度 <0.4 → **不判断** | ✅ 直接放行(**不因识别失败而打扰用户**) | ❌ 系统静默放行 |
|
||||
|
||||
@@ -1323,7 +1290,7 @@
|
||||
| **材料从哪来** | — | "这份材料一般在**微信聊天记录**里:照相馆/打印店发给你之后,从这里选,系统能自动帮你填好信息" |
|
||||
| **相册图片无名字** | 无法解析文件名 | "这张图没有名字信息,系统已识别为学籍表。请确认是不是这一份" |
|
||||
| **聊天图片被压缩过** | 图片分辨率偏低 | "这张图好像被微信压缩过,有点糊。可以让对方发一张原图,或者点开图片后选『原图』再保存" |
|
||||
| **门禁:匹配失败** | 未找到该身份证号 / 姓名不匹配 | "**没有找到你的信息。**请核对姓名和身份证号是否与老师提供的名单一致。如果确实已报名,可以申请加入名单,由老师审核" |
|
||||
| **门禁:匹配失败** | 未找到该身份证号 / 姓名不匹配 | "**没有找到你的信息。**请核对姓名和身份证号是否与老师提供的名单一致。如确认已报名,请联系老师把你加入名单" |
|
||||
| **门禁:姓名疑似写错** | 身份证对、姓名形近 | "你是不是 **杨晨**?(老师名单上写的是这个名字)" |
|
||||
| **门禁:已被绑定** | 名单项已绑他人 | "该信息已被登记,如有疑问请联系老师" |
|
||||
| **门禁:限流** | 尝试过于频繁 | "操作过于频繁,请稍后再试" |
|
||||
@@ -1358,9 +1325,9 @@
|
||||
| 8 | 导出包默认保留 7 天是否够用 | 低 | 客户 |
|
||||
| 9 | **各来源通道的实际体积上限需实测**(微信聊天记录选文件、相册、文件选择三条通道各自能传多大的文件),并据此设计超限提示与降级路径 | 中 | 研发 |
|
||||
| 10 | **微信相册/相机权限的隐私声明**需在小程序后台同步更新(当前声明需覆盖"从相册选择") | 中 | 研发 + 法务 |
|
||||
| **11** | **名单外的人怎么处理?** 当前按需求实现为"不给填、只提示",需老师去后台加人。建议增加"申请加入名单 → 管理员审批后自动放行"通道,**请确认是否采纳** | **高** | 客户 |
|
||||
| ~~11~~ | ~~名单外的人怎么处理?建议增加"申请加入名单 → 管理员审批后自动放行"通道~~ → **已决策(2026-10):不做自助通道,由老师手工加人** | — | 已关闭 |
|
||||
| 12 | **门禁限流阈值**(同 openid 每小时 10 次)是否合适,需按实际人数与使用习惯标定 | 中 | 研发 |
|
||||
| 13 | 社会收集的名单从哪来(预报名名单 / 报名点收集 / 机构导出)?若来源不稳定,"先导名单"的落地难度较大 | 中 | 客户 |
|
||||
| ~~13~~ | ~~社会收集的名单从哪来~~ → **已关闭**(2026-10 取消社会收集) | — | 已关闭 |
|
||||
| 14 | 门禁核验是否还要加**手机号验证码**(防止他人拿到姓名+身份证号后代填) | 中 | 客户 |
|
||||
| **15** | **材料识别的 VLM 选型与单价**(用于成本核算与供应商比价);触发率 15% 为预估值,需用真实数据实测标定 | 中 | 研发 + 商务 |
|
||||
| 16 | 关键词表由谁维护(平台预置 + 租户自维护);是否需要"从历史文件自动学习关键词"功能 | 中 | 产品 |
|
||||
|
||||
+63
-73
@@ -1,7 +1,7 @@
|
||||
# 数据信息填报系统 PRD
|
||||
# 深学在线 PRD
|
||||
|
||||
> 产品代号:**填报通 / CollectFlow**(暂定)
|
||||
> 版本:V2.3(2026-09-28 明确滑动交互规则)
|
||||
> 产品代号:**深学在线**
|
||||
> 版本:**V2.5**(2026-10 需求收敛:**4 角色 / 10 菜单 / 不设二期**)
|
||||
> 日期:2026-09-28
|
||||
> 文档类型:产品需求文档(PRD)+ 数据库设计 + 接口清单
|
||||
> 适用场景:职业技能等级认定报名材料收集(可扩展至各类"班级/单位材料收集"场景)
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
| 版本 | 日期 | 变更内容 | 变更人 |
|
||||
|---|---|---|---|
|
||||
| **V2.5** | **2026-10** | **需求收敛:删角色 / 删两模块 / 取消二期**。① 内置角色 **6 → 4**(删「副班主任 `DEPUTY_TEACHER`」与「审核员 `AUDITOR`」,存量账号由 **V19 迁移**处理);② 删「**历史数据整理器**」(FR-1400 系列 + §5.14 + `collect_cleanup_job` 表 + `cleanup:run`)与「**加入申请**」(FR-509 + §7.3.19 + `collect_join_request` 表 + `join:audit` + 错误码 `40037`/`40038`/`40039`/`40040`/`40042`);③ `collect_task_org.deputy_user_ids`(协办人)**整列删除**,审核范围三级回退 → **两级**;④ **不再有任何二期功能设计** —— 社会收集 / 社会评价 / 专项职业能力 / 数据范围可配一律**不做**,OCR 与材料识别改为「**缓做,等供应商选型**」;⑤ 材料类目 6 → 4(`XJW_BA`/`OTHER_PROOF` **读得到、选不到**)、内置材料模板 3 → 1。**数字**:接口 **129 → 119**、错误码 **59 → 54**、权限码 **30 → 28**、PC 菜单 **12 → 10**、小程序页面 **10 → 9**、原型 **32 → 29 屏**。⚠️ **加入申请(确认函 B1)是客户唯一确认的功能增量,本次属需求回退,建议补变更确认单**。 | 开发 |
|
||||
| **V2.4** | **2026-09-28** | **修正 3 处加密列宽不足(DDL 缺陷,M0 实测暴露)**:`sys_tenant.contact_phone`、`sys_user.phone`、`collect_roster.phone` 原为 `varchar(20)`,是按<b>明文手机号</b>长度建的;但 §10.3 要求手机号 AES-256 加密存储,AES-GCM 密文约 <b>52 字符</b>,插入直接报 `value too long for type character varying(20)`。(`id_card_no` 当初给了 `varchar(128)` 就是考虑到了加密,手机号三列漏了——属设计不一致。)三列统一改为 <b>`varchar(128)`</b> 与 `id_card_no` 对齐。<br>**发现方式**:M0 开通租户实测时数据库报错暴露,非人工审查发现。<br>**审计结论**:其余 `*_mask varchar(32)` 列存的是脱敏值(如 `138****8888`)而非密文,宽度足够,无需改动。 | 开发 |
|
||||
| V1.0 | 2026-09-24 | 初稿:背景/流程/功能/规则引擎/库表/接口 | 产品 |
|
||||
| **V1.1** | 2026-09-24 | **复审修订**(用 112 个真实文件重新实测验证):<br>① **勘误**:命名违规率 98.2% → **52.7%**(59/112);双扩展名 55 → **56**;原始总量 436MB → **475MB**(436MB 实为 .rar 包体积);需处理并集 62 → **59**(原为重复计数)<br>② **推翻参数假设**:工作证明是 1272dpi A4 扫描件(非手机拍照)→ §6.3 压缩策略由"按长边"重写为"按材料形态分五档"<br>③ 学籍表是 16:9 截图 → 新增 P4 截图档,禁止缩放与转 JPG<br>④ 新增 §6.1 证件号裁决矩阵,人工量由 5 例降至 3 例<br>⑤ **新增风险 R11**(1.8 亿像素导致 OOM)、**R12**(微信订阅消息次数限制)<br>⑥ **补全 §9.4 班主任移动端页面清单**(初稿遗漏)<br>⑦ 修正 §1.4 指标口径混淆(班主任操作时长 vs 端到端周期)<br>⑧ 补充小程序隐私保护指引、DPA、KMS 等合规项<br>⑨ 补 `collect_task_org` 表、名册 `student_no` 字段、附录 D 错误码、附录 E 定时任务 | 产品 |
|
||||
@@ -50,20 +51,21 @@
|
||||
|
||||
---
|
||||
|
||||
## 一期范围声明(2026-09-28 客户确认)
|
||||
## 一期范围声明(2026-09-28 收窄 → 2026-10 收敛为最终口径)
|
||||
|
||||
> **一期只做「学校收集 + 自主认定」这一条流程。**
|
||||
> 社会收集、社会评价、专项职业能力 **全部后置**;
|
||||
> 但 **数据结构与枚举保留不启用**——二期直接开,不需要 DB 迁移。
|
||||
> **只做「学校收集 + 自主认定」这一条流程**,且**不再设计任何二期功能**(2026-10 客户收敛)。
|
||||
> 社会收集、社会评价、专项职业能力 **一律不做**:`collectMode=2` 与 `certType=2,3` 传入返回 **`40044`**。
|
||||
> **数据结构与枚举保留不启用**(留着是因为这三项本来配置驱动、不增加维护成本),
|
||||
> 但**没有"将来直接开"的承诺** —— 要用时按新需求重新评估。
|
||||
|
||||
### 范围切分
|
||||
|
||||
| 维度 | 字段 | 一期 | 二期后置 | 为什么这么切 |
|
||||
| 维度 | 字段 | 本期做 | **不做**(已取消,不是"后置") | 为什么这么切 |
|
||||
|---|---|---|---|---|
|
||||
| **收集模式** | `collect_mode` | `1` 学校收集 | `2` 社会收集 | 一期只做一条审核流(学生→班主任→管理员),少一条分支、少一套测试 |
|
||||
| **认定类型** | `cert_type` | `1` 自主认定 | `2` 社会评价<br>`3` 专项职业能力 | 后两者会引入**两类零实测样本的材料**,识别规则目前是推测 |
|
||||
| **收集模式** | `collect_mode` | `1` 学校收集 | `2` 社会收集(传 2 → `40044`) | 只保留一条审核流(学生→班主任→管理员),少一条分支、少一套测试 |
|
||||
| **认定类型** | `cert_type` | `1` 自主认定 | `2` 社会评价 / `3` 专项职业能力(传 2/3 → `40044`) | 后两者会引入**两类零实测样本的材料**,识别规则只能是推测 |
|
||||
|
||||
### 一期支持的材料(4 类,全部有 112 个真实文件实测)
|
||||
### 支持的材料(4 类,全部有 112 个真实文件实测)
|
||||
|
||||
| 材料编码 | 名称 | 规范文件名示例 | 压缩档位 | 实测 |
|
||||
|---|---|---|---|:--:|
|
||||
@@ -72,49 +74,41 @@
|
||||
| `SCHOOL_ROLL` | 学籍表 | `张三-3601...-学籍表.jpg` | P4 截图 | ✅ |
|
||||
| `WORK_PROOF` | 工作证明 | `张三-3601...-工作证明.jpg` | P3 扫描件 | ✅ |
|
||||
|
||||
### 二期才做的材料(**零实测样本**)
|
||||
### 不做、也**不再排期**的东西(2026-10 收敛)
|
||||
|
||||
| 材料编码 | 名称 | 出现在 | 现状 |
|
||||
|---|---|---|---|
|
||||
| `XJW_BA` | 学信网备案表 | 社会评价 | 强特征词是从**官方模板的文字要求**里抄的,**未见一张真实文件** |
|
||||
| `OTHER_PROOF` | 其它佐证材料 | 社会评价 / 专项 | 含毕业证/社保单/职称/技能证书**四类版式完全不同**的东西,**可能不该做自动识别** |
|
||||
| 项 | 现状 |
|
||||
|---|---|
|
||||
| `XJW_BA` 学信网备案表 / `OTHER_PROOF` 其它佐证材料 | 编码**保留**(历史数据读得到),但**选不到**(不在"新增材料"列表里);强特征词从未标定 |
|
||||
| 社会收集审核流(跳过班主任初审) | **不做** |
|
||||
| 专项职业能力的字段隐藏与校验绕过 | **不做** |
|
||||
| 内置材料模板 `TPL_SOCIAL` / `TPL_ZHUANXIANG` | **已删除**,只剩 `TPL_SCHOOL_ROLL` |
|
||||
| 原「后置清单」6 项(索取样本 / 标定特征词 / 报名点名单流程 / …) | **全部作废** |
|
||||
|
||||
### 后置清单(二期启动前要准备的事)
|
||||
|
||||
| # | 事项 | 依赖 |
|
||||
|:--:|---|---|
|
||||
| 1 | 索取「社会评价」批次原始材料样本(≥10 份,须含备案表与各类佐证) | 客户提供(D4) |
|
||||
| 2 | 标定 `XJW_BA` 的强特征词与形态阈值 | ↑ |
|
||||
| 3 | 决定 `OTHER_PROOF` 是否做自动识别,还是改为纯手动槽位 | ↑ |
|
||||
| 4 | 实现社会收集审核流(跳过班主任初审,直达管理员) | 无 |
|
||||
| 5 | 实现专项职业能力的字段隐藏(工种/等级/补考三项)与校验绕过 | 无 |
|
||||
| 6 | 社会收集的名单来源流程(报名点/机构维护预报名名单) | 客户确认(B2) |
|
||||
|
||||
### 对现有设计的影响:几乎没有
|
||||
### 对现有设计的影响:没有
|
||||
|
||||
**因为这三项本来就是配置驱动的**:
|
||||
|
||||
| 后置项 | 现有实现方式 | 代码改动量 |
|
||||
| 项 | 现有实现方式 | 代码改动量 |
|
||||
|---|---|---|
|
||||
| 社会收集跳过初审 | `audit_config.needClassAudit` 是一个开关 | 0(只是一个配置值) |
|
||||
| 专项职业能力的字段隐藏 | 走 `visibleWhen` 条件显示规则 | 0(配置一条规则) |
|
||||
| 材料清单差异 | 材料项定义(`collect_material_def`) | 0(配置差异) |
|
||||
|
||||
> ⇒ **一期收窄省下的主要是「测试与联调成本」和「一个外部依赖(D4)」,而不是代码量。**
|
||||
> ⇒ **收窄省下的主要是「测试与联调成本」和「一个外部依赖(D4)」,而不是代码量。**
|
||||
>
|
||||
> **工期影响**:**−5 人日**(392 → 387)。**关键路径不变,工期仍为 15 周。**
|
||||
|
||||
### 一期不做的验证项(从测试范围移除)
|
||||
### 从测试范围移除的验证项
|
||||
|
||||
| 移除项 | 原因 |
|
||||
|---|---|
|
||||
| 主流程 B(社会收集,3 步) | 二期 |
|
||||
| 主流程 B(社会收集,3 步) | 不做 |
|
||||
| `XJW_BA` / `OTHER_PROOF` 的识别断言 | 零样本,无法定基线 |
|
||||
| 专项职业能力的字段隐藏与 VAL-14/15 绕过用例 | 二期 |
|
||||
| 社会收集的跳过初审用例 | 二期 |
|
||||
| 专项职业能力的字段隐藏与 VAL-14/15 绕过用例 | 不做 |
|
||||
| 社会收集的跳过初审用例 | 不做 |
|
||||
|
||||
> ⚠️ **二期启用时必须要补的回归断言**:`XJW_BA` / `OTHER_PROOF` 的识别准确率、社会收集审核流、专项职业能力字段隐藏。
|
||||
> 这些都已在本文档与《06-验收测试清单》中保留描述,只是标注为二期。
|
||||
> ⚠️ 若将来真要启用,**必须补**的回归断言:`XJW_BA` / `OTHER_PROOF` 的识别准确率、社会收集审核流、专项职业能力字段隐藏。
|
||||
> 描述仍保留在本文档与《06-验收测试清单》里,但**不再标注为"二期"** —— 那会被误读成"已经排期了"。
|
||||
|
||||
---
|
||||
|
||||
@@ -283,7 +277,7 @@
|
||||
| **规范名(Standard Name)** | 由命名规则引擎根据模板生成的最终文件名 |
|
||||
| **初审 / 终审** | 班主任审核 / 管理员审核 |
|
||||
| **汇总包(Export Package)** | 按目录规范打包后可直接上报的文件集合 |
|
||||
| **历史数据整理器** | 对已存在的"乱文件夹/压缩包"做批量规范化与体检的独立工具 |
|
||||
| ~~**历史数据整理器**~~ | **已取消**(2026-10):对历史乱文件夹做批量规范化与体检的独立工具,连模块/表/权限码/页面一并删除 |
|
||||
|
||||
---
|
||||
|
||||
@@ -501,7 +495,7 @@ DRAFT ──submit──▶ PENDING_CLASS ──班主任通过──▶ PENDING
|
||||
| FR-307 | **报考范围自动聚合**:名单导入后自动提取"职业 × 工种 × 等级"集合 | **P0** |
|
||||
| FR-308 | **名单一致性体检**:职业/工种矛盾、等级异常、漏填、拼写不符 → 输出体检报告 | **P0** |
|
||||
| FR-309 | 报考范围支持管理员**手工修正**,并留痕(谁改的、改前改后) | **P0** |
|
||||
| FR-310 | **专项职业能力**类型支持:自动隐藏工种、等级、补考相关字段 | ~~P0~~ **二期**<br>⚠️ 一期只做自主认定(见「一期范围声明」) |
|
||||
| FR-310 | **专项职业能力**类型支持:自动隐藏工种、等级、补考相关字段 | ~~P0~~ **不做**(2026-10 取消二期;只做自主认定,见「一期范围声明」) |
|
||||
| FR-311 | 追加名单时,若出现**范围外的新报考项**,必须告警并由管理员确认后才接受 | **P0** |
|
||||
| FR-312 | 可选:接入国标职业字典做**拼写校验与联想建议**(仅辅助,不作强约束) | P2 |
|
||||
|
||||
@@ -725,7 +719,7 @@ DRAFT ──submit──▶ PENDING_CLASS ──班主任通过──▶ PENDING
|
||||
| **FR-502a** | **名单即预填报名表**:名单 21 列的值全部入库(**18 个正式列 + 姓名/身份证号已有列**),学生端按"锁定/预填可改/学生必填"三类规则呈现(见 §5.3.1) | **P0** |
|
||||
| **FR-502b** | **名单字段三类规则**:姓名+身份证号+报考职业/工种/等级 → **锁定**(名单为空则阻断发布);其余 → 有值则预填可改、无值则学生必填 | **P0** |
|
||||
| FR-503 | **名单核验(门禁)**:填报人进入前必须填写「姓名 + 身份证号」匹配名单,命中才进入填报页;命中后绑定名单项 | P0 |
|
||||
| FR-504 | **未命中统一提示**(不区分"号码不在名单"与"姓名不符",防探测),提供 **"重新填写"** 与 **"申请加入名单"(FR-509)** 两条出路;**取消原"自助认领"** | P0 |
|
||||
| FR-504 | **未命中统一提示**(不区分"号码不在名单"与"姓名不符",防探测),只给 **"重新填写"** 一条系统内出路 + 一句"如确认已报名,请联系老师把你加入名单";**取消原"自助认领"与"申请加入名单"(FR-509)** | P0 |
|
||||
| FR-504a | **匹配接口限流**:同 openid 每小时 ≤10 次、同 IP 每分钟 ≤20 次,超出锁定并提示 | P0 |
|
||||
| FR-504b | 名单**不下发前端**,匹配全程在服务端完成;失败尝试不记录明文证件号 | P0 |
|
||||
| FR-504c | 姓名形近字/同音字纠错:身份证号命中但姓名不符时,给出"你是不是 XXX?"让用户确认 | P1 |
|
||||
@@ -734,7 +728,7 @@ DRAFT ──submit──▶ PENDING_CLASS ──班主任通过──▶ PENDING
|
||||
| FR-506 | 名单变更留痕(谁在何时把 A 的证件号从 X 改成 Y) | P0 |
|
||||
| FR-507 | **发布前校验**:名单人数 > 0 且必须含「姓名」「身份证号」列,否则阻断发布 | P0 |
|
||||
| FR-508 | 名单追加导入(按证件号去重合并),**追加后新人立即可填报** | P0 |
|
||||
| **FR-509** | **名单加入申请(客户已确认采纳的建议增量)**:门禁失败页提供「**申请加入名单**」入口 → 生成管理员待办 → **审批通过自动写入名册并放行**。解决了"名单外的人只能找老师手工加人"的运营痛点 | **P0** |
|
||||
| ~~**FR-509**~~ | ~~名单加入申请:门禁失败页提供「申请加入名单」入口 → 管理员待办 → 审批通过自动写入名册并放行~~ → **已撤销**(2026-10 需求回退):名单外的人由老师在任务里**手工加人**(FR-508 追加导入),系统内**不展示任何联系方式**。⚠️ 它是确认函 B1 的唯一功能增量,建议补变更确认单 | ~~P0~~ |
|
||||
| FR-509a | **通道边界**:入口**只在门禁失败页**,不在填报页内;**申请期间仍不能填报**——通道只解决"进名单",**不绕过门禁** | P0 |
|
||||
| FR-509b | 申请表单:姓名、身份证号、手机号(**均必填**)+ 自述班级 / 说明(选填);提交后展示"已提交,等待学校审核",并可查看审批进度 | P0 |
|
||||
| FR-509c | **限流与防灌**:同 openid 每任务 ≤ **2 次**;**同证件号每任务仅保留 1 条待审或已通过申请**(被驳回后可重新申请);单任务待审申请 > 50 条时告警管理员 | P0 |
|
||||
@@ -920,20 +914,13 @@ DRAFT ──submit──▶ PENDING_CLASS ──班主任通过──▶ PENDING
|
||||
| 旗舰版 | 多校区/大型机构 | 不限人数,含定制规则、专属客服、API 对接 |
|
||||
| 私有化 | 政企/大型认定机构 | 一次性 + 年维护 |
|
||||
|
||||
### 5.14 历史数据整理器(FR-1400)★ 差异化获客功能
|
||||
### 5.14 历史数据整理器(FR-1400)—— **已取消**(2026-10 需求收敛)
|
||||
|
||||
**背景**:客户手头已有大量"历史乱数据"(如本项目的 436MB `.rar`)。让他们先看到价值,再谈系统上线。
|
||||
|
||||
| 编号 | 需求 | 优先级 |
|
||||
|---|---|:--:|
|
||||
| FR-1401 | 上传 ZIP/RAR 或指定目录结构,系统自动解析文件树 | P1 |
|
||||
| FR-1402 | 自动识别"姓名 / 证件号 / 材料名"三要素并规范化命名 | P1 |
|
||||
| FR-1403 | 批量压缩到目标体积、格式统一 | P1 |
|
||||
| FR-1404 | 输出**《材料体检报告》**:命名违规清单、证件号异常清单、体积超标清单、缺失材料清单、重复文件清单 | P1 |
|
||||
| FR-1405 | 输出规范化后的 ZIP 包 + 可直接上报的目录结构 | P1 |
|
||||
| FR-1406 | 报告与结果一键分享给班主任(生成链接) | P2 |
|
||||
|
||||
> 用本项目 `24中技数控1班` 做基准测试集,预期报告输出:命名违规 **59 项**、证件号异常 5 项、体积超标 31 项、疑似串档 1 项。
|
||||
> ⚠️ **整节作废**:FR-1401 ~ FR-1406 六条需求、`collect_cleanup_job` 表、`cleanup:run` 权限码、
|
||||
> PC 端「历史整理器」菜单与页面、原型「整理器」屏、`e2e-m7-cleanup.py` **已全部删除**,**不再排期**。
|
||||
>
|
||||
> 原用途(让老师先看到价值再谈上线)记录在此仅供追溯;若将来重启,按新需求重新评估,
|
||||
> 并注意它当年最有说服力的那条结论仍然成立:**112 个文件里 90 个系统全自动处理,只有 1 个必须人工判断**。
|
||||
|
||||
---
|
||||
|
||||
@@ -1017,7 +1004,7 @@ DRAFT ──submit──▶ PENDING_CLASS ──班主任通过──▶ PENDING
|
||||
> 这正是校验位算法带来的直接收益:它把"这到底是谁的材料"从人判断变成机器判断。
|
||||
|
||||
> 📌 **已废除**:原矩阵中"不在名册 → 转**待认领**队列"——**"自助认领"机制已在 V1.5 废除**(会绕过门禁)。
|
||||
> 证件号不在名册时的正确出路是:**引导走「申请加入名单」通道**(FR-509,客户已确认采纳),
|
||||
> 证件号不在名册时的正确出路是:**先核对姓名/身份证号是否输错**;确认无误则**联系老师把你加进名单**(老师在任务里手工加人,FR-508)。原「申请加入名单」自助通道已于 2026-10 撤销。
|
||||
> 由管理员逐条审批后入册,而不是让系统自行认领。
|
||||
|
||||
**容错与纠正对照表(基于真实数据)**
|
||||
@@ -1089,7 +1076,7 @@ FUNCTION name_differs_soft(fn, rn): # 仅用于矩阵第 2 条的"留痕"
|
||||
合法 ⟺ C[S mod 11] == a[17](X 需大写比较)
|
||||
```
|
||||
|
||||
**用途**:填报端实时校验、名册导入体检、历史数据整理器、OCR 结果二次验证。
|
||||
**用途**:填报端实时校验、名册导入体检、OCR 结果二次验证。(原"历史数据整理器"一项已随模块删除)
|
||||
|
||||
### 6.3 压缩规则(按材料形态分档 + 强制 1M)
|
||||
|
||||
@@ -1292,8 +1279,8 @@ erDiagram
|
||||
| 业务 | `collect_notify_record` | 通知记录与订阅额度 | ✅ |
|
||||
| 业务 | `collect_subscribe_quota` | **微信订阅消息额度余额**(应对 R12 次数限制) | ✅ |
|
||||
| 业务 | `collect_rule_template` | 规则模板(可复用) | ✅ |
|
||||
| 业务 | `collect_cleanup_job` | 历史数据整理任务 | ✅ |
|
||||
| 业务 | `collect_join_request` | **名单加入申请**(⚠️ 客户确认后新增,FR-509) | ✅ |
|
||||
| ~~业务~~ | ~~`collect_cleanup_job`~~ | **已删除**(2026-10,见 V19 迁移) | — |
|
||||
| ~~业务~~ | ~~`collect_join_request`~~ | **已删除**(2026-10,见 V19 迁移) | — |
|
||||
|
||||
> ✅ = 本文提供完整 DDL;从略 = 标准表(角色/字典/日志),沿用现有框架实现。
|
||||
>
|
||||
@@ -1610,7 +1597,7 @@ CREATE TABLE collect_task_org (
|
||||
CREATE INDEX idx_tenant_head ON collect_task_org (tenant_id, head_user_id);
|
||||
COMMENT ON TABLE collect_task_org IS '任务覆盖组织与审核人绑定';
|
||||
COMMENT ON COLUMN collect_task_org.head_user_id IS '该班初审人(班主任),为空则取 sys_org.leader_user_id';
|
||||
COMMENT ON COLUMN collect_task_org.deputy_user_ids IS '副班主任/协办人,逗号分隔';
|
||||
-- ⚠️ 2026-10 需求收敛:`deputy_user_ids`(协办人)**整列已删除**(V19 迁移)。审核范围由三级回退收窄为两级(`head_user_id` → `sys_org.leader_user_id`)。
|
||||
COMMENT ON COLUMN collect_task_org.expected_count IS '应填人数(期望值,管理员填写,可空);仅在与名册实际人数不一致时告警。NULL 表示未设置,与 0(预期 0 人)含义不同';
|
||||
COMMENT ON COLUMN collect_task_org.status IS '0已移除 1生效';
|
||||
```
|
||||
@@ -1891,7 +1878,7 @@ COMMENT ON COLUMN collect_file.detect_conflict IS '是否与槽位冲突(1=冲
|
||||
COMMENT ON COLUMN collect_file.need_manual_place IS '是否需要人工归属(散图分拣用)';
|
||||
COMMENT ON COLUMN collect_file.vlm_called IS '是否触发了VLM兜底(用于成本核算)';
|
||||
COMMENT ON COLUMN collect_file.validate_status IS '0未校验 1通过 2警告 3错误';
|
||||
COMMENT ON COLUMN collect_file.source IS '1本人上传 2班主任代传 3管理员代传 4整理器导入';
|
||||
COMMENT ON COLUMN collect_file.source IS '1本人上传 2班主任代传 3管理员代传 4整理器导入(⚠️ 整理器模块已于 2026-10 取消,4 仅历史数据)';
|
||||
```
|
||||
|
||||
#### 7.3.12 校验问题
|
||||
@@ -2087,6 +2074,7 @@ COMMENT ON COLUMN collect_rule_template.materials IS '材料清单模板';
|
||||
#### 7.3.18 历史数据整理任务
|
||||
|
||||
```sql
|
||||
-- ⚠️ 2026-10 需求收敛:**本表已删除**(见 V19 迁移)。以下 DDL 仅作历史记录,不要再照着建。
|
||||
CREATE TABLE collect_cleanup_job (
|
||||
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
||||
tenant_id bigint NOT NULL,
|
||||
@@ -2113,9 +2101,10 @@ COMMENT ON COLUMN collect_cleanup_job.status IS '0排队 1解析中 2处理中 3
|
||||
COMMENT ON COLUMN collect_cleanup_job.report_json IS '体检报告:命名违规/证件号异常/体积超标/缺失/重复';
|
||||
```
|
||||
|
||||
#### 7.3.19 名单加入申请(⚠️ 客户确认后新增)
|
||||
#### 7.3.19 名单加入申请(**已撤销** · 2026-10 需求收敛)
|
||||
|
||||
> 对应 FR-509 系列。解决"名单外的人只能找老师手工加人"的运营痛点。
|
||||
> ⚠️ **本表已删除**(见 V19 迁移),FR-509 通道整体取消。以下 DDL 仅作历史记录。
|
||||
> 现在的口径:门禁失败只提示核对 + "请联系老师把你加入名单",由老师手工加人;系统内不展示联系方式。
|
||||
|
||||
```sql
|
||||
CREATE TABLE collect_join_request (
|
||||
@@ -2497,7 +2486,7 @@ export/{tenantId}/{taskId}/{exportId}/{批次号}-{专业}-{班级}.zip
|
||||
|---|---|---|
|
||||
| 首页(任务列表) | `pages/index` | 卡片式展示任务;显示进度环"已完成 3/5 项";倒计时提醒 |
|
||||
| **门禁核验** | `pages/gate/verify` | ⚠️ **新增(入口第一屏)**:填写「姓名 + 身份证号」;身份证输入用数字键盘、自动分段、末位 X 大写、支持粘贴;本地先校验格式与校验位;提交后匹配名单 |
|
||||
| **门禁失败** | `pages/gate/fail` | 统一提示"没有找到你的信息";**两个系统内动作:重新填写 / 申请加入名单**(FR-509)。**不展示任何联系方式** |
|
||||
| **门禁失败** | `pages/gate/fail` | 统一提示"没有找到你的信息";**只有「重新填写」一个系统内动作**,另加一句"如确认已报名,请联系老师把你加入名单"。**不展示任何联系方式**(原「申请加入名单」通道已于 2026-10 撤销) |
|
||||
| 任务详情 | `pages/task/detail` | 材料清单、状态、示例图、操作指引 |
|
||||
| 信息填写 | `pages/form/index` | 分步表单;已匹配名册自动回填;身份证 OCR 一键识别 |
|
||||
| 材料上传 | `pages/upload/index` | **来源选择:微信聊天记录 / 手机相册 / 手机文件**(无"拍照");每个材料项附"去哪里找"引导;上传即显示"压缩中 92%…";实时校验结果 |
|
||||
@@ -2533,9 +2522,9 @@ export/{tenantId}/{taskId}/{exportId}/{批次号}-{专业}-{班级}.zip
|
||||
│ 没有找到你的信息 │
|
||||
│ 请核对姓名和身份证号是否与老师 │
|
||||
│ 提供的名单一致。 │
|
||||
│ 如果确实已报名,可申请加入名单。 │
|
||||
│ 如已报名,请联系老师加你进名单。 │
|
||||
│ │
|
||||
│[ 重新填写 ] [ 申请加入名单 ] │
|
||||
│[ 重新填写 ] │
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -2612,7 +2601,7 @@ export/{tenantId}/{taskId}/{exportId}/{批次号}-{专业}-{班级}.zip
|
||||
| 导出中心 | 创建导出、进度、历史、下载 |
|
||||
| 通知中心 | 模板管理、发送记录、催报设置 |
|
||||
| 系统设置 | 组织、用户、角色、字典、日志、套餐用量 |
|
||||
| 整理器(差异化) | 上传压缩包 → 体检报告 → 一键规范化 → 下载结果 |
|
||||
| ~~整理器(差异化)~~ | **已取消**(2026-10) |
|
||||
|
||||
**命名规则编辑器**(重点)
|
||||
|
||||
@@ -2739,13 +2728,13 @@ export/{tenantId}/{taskId}/{exportId}/{批次号}-{专业}-{班级}.zip
|
||||
| **M3 规则引擎(核心)** | 命名规范化、**五档压缩(libvips)**、格式转换、校验位裁决、OCR、**超大图 OOM 防护** | 3.5 周(A/B 验证已因实测达标而取消,较 V1.2 减 0.5 周) | 可上传即规范化 |
|
||||
| **M4 审核与导出** | 审核工作台、批量审核、汇总导出、分卷打包 | 2 周 | 全流程闭环(PC 端) |
|
||||
| **M5 移动端** | 小程序填报端 + **班主任端**、通知与催报(含订阅消息授权策略) | 2.5 周 | 手机端可用 |
|
||||
| **M6 整理器** | 历史数据批量规范化 + 体检报告 | 1.5 周 | 获客工具 |
|
||||
| ~~**M6 整理器**~~ | **已取消**(2026-10):历史数据批量规范化 + 体检报告 | — | — |
|
||||
| **M7 联调与试运行** | 压测(**必须用 28 张 1.6 亿像素真实扫描件**)、真实数据回归、试点 1–2 个班 | 2 周 | 试运行报告 |
|
||||
| **合计** | 部分任务可并行 | **12.5–14.5 周** | V1.0 上线 |
|
||||
|
||||
**MVP 建议范围(若需 6 周内出可用版本)**
|
||||
保留:M1 + M2(简化版:表单字段固定为模板)+ M3 + M4 + M5
|
||||
砍掉:整理器(M6)、证件照规格化、自动催报、部分统计看板
|
||||
砍掉:**整理器(M6,已于 2026-10 正式取消)**、证件照规格化、自动催报、部分统计看板
|
||||
|
||||
> ⚠️ **不建议再压缩 M3**:超大图 OOM 防护与压缩参数验证是项目成败的关键路径,压缩这部分的工期会直接把风险推到试运行阶段(即"在真实客户面前爆炸")。
|
||||
|
||||
@@ -2806,7 +2795,7 @@ export/{tenantId}/{taskId}/{exportId}/{批次号}-{专业}-{班级}.zip
|
||||
| R3 | 微信小程序审核受阻(涉及教育/UGC 类目、需资质) | 高 | ① 提前确认类目与资质(可能需 ICP + 办学许可或合作方资质);② 备选 H5 方案(扫码即填)保底 |
|
||||
| R4 | 个人信息合规(身份证号属敏感个人信息) | 高 | 见 10.3;签约时明确数据处理者角色与责任划分(SaaS 服务商 = 受托处理者) |
|
||||
| R5 | 存储与带宽成本超预期(原始文件 3.8TB/学期) | 中 | 强制原始文件 30 天清理 + 客户端预压缩 + 导出走签名 URL |
|
||||
| R6 | 老师抵触新系统("又要多学一个东西") | 高 | ① "历史数据整理器"作为钩子,先让老师看到价值;② 名册 Excel 直接导入,无需重复录入;③ 一屏批量审核,比现在更快 |
|
||||
| R6 | 老师抵触新系统("又要多学一个东西") | 高 | ① 名册 Excel 直接导入,无需重复录入;② 一屏批量审核,比现在更快;③ 门禁核验把"谁还没交"变成一眼可见(原"历史数据整理器"钩子已随该模块取消,见 §5.14) |
|
||||
| R7 | 学生不会操作 / 使用老旧手机 | 中 | 三步极简流程 + 班主任代传兜底 + 大字号模式 |
|
||||
| R8 | 重复提交 / 版本混乱 | 低 | 版本化文件管理 + 导出去重取最新 |
|
||||
| R9 | 与现有系统(如需对接认定机构平台)不兼容 | 中 | 预留开放 API(8.10);导出包格式遵循机构要求 |
|
||||
@@ -2834,7 +2823,7 @@ export/{tenantId}/{taskId}/{exportId}/{批次号}-{专业}-{班级}.zip
|
||||
| Q8 | 是否需要向学生收取费用(如报名服务费),涉及支付资质 | 中 | 商务 |
|
||||
| Q9 | 一期是否需要与"认定机构业务系统"对接 | 中 | 客户 |
|
||||
| Q10 | 私有化部署版本是否同步开发(当前决策为 SaaS 优先、私有化后置) | 低 | 产品 |
|
||||
| **Q11** | **名单外的人怎么处理?** 当前按需求实现为"不给填、只提示",老师需去后台加人。建议增加"**申请加入名单 → 管理员审批后自动放行**"通道(既不破门禁、又免手工加人)。**请决策** | **高** | 客户 |
|
||||
| ~~**Q11**~~ | ~~名单外的人怎么处理?建议增加"申请加入名单 → 管理员审批后自动放行"通道~~ → **已决策并已回退**(2026-10):**不做自助通道**,由老师在任务里手工加人;系统内不展示联系方式 | — | 已关闭 |
|
||||
| **Q12** | **社会收集的名单从哪来?** 社会收集同样"先导名单",但若名单来源不稳定(预报名名单 / 报名点收集 / 机构提供),落地难度较大。需确认名单来源与责任方 | **高** | 客户 |
|
||||
| Q13 | 门禁是否再加一层**手机号验证码**(防止有人拿到他人姓名+身份证号即可代填) | 中 | 客户 |
|
||||
| Q14 | 门禁限流阈值(同 openid 每小时 10 次)是否合适,需按实际人数标定 | 中 | 研发 |
|
||||
@@ -2854,9 +2843,9 @@ export/{tenantId}/{taskId}/{exportId}/{批次号}-{专业}-{班级}.zip
|
||||
| `PHOTO1` | 一寸照片 |
|
||||
| `IDCARD` | 身份证 |
|
||||
| `SCHOOL_ROLL` | 学籍表 |
|
||||
| `XJW_BA` | 学信网备案表 |
|
||||
| `XJW_BA` | 学信网备案表(**读得到、选不到**:不在"新增材料"列表里) |
|
||||
| `WORK_PROOF` | 工作证明 / 个人申报表 |
|
||||
| `OTHER_PROOF` | 其它佐证材料 |
|
||||
| `OTHER_PROOF` | 其它佐证材料(**读得到、选不到**:不在"新增材料"列表里) |
|
||||
|
||||
**收集模式 `collect_mode`**:`1` 学校收集 / `2` 社会收集
|
||||
**认定类型 `cert_type`**:`1` 自主认定 / `2` 社会评价 / `3` 专项职业能力
|
||||
@@ -2911,7 +2900,7 @@ export/{tenantId}/{taskId}/{exportId}/{批次号}-{专业}-{班级}.zip
|
||||
| `40004` | 身份证号校验位不通过 | 高亮错误位,提示核对原件 |
|
||||
| `40005` | 证件号与名册不一致 | 展示名册值,要求确认 |
|
||||
| `40006` | 疑似串档(校验位均通过但与他人不符) | 转人工,不自动修正 |
|
||||
| `40007` | 门禁未匹配(姓名或身份证号与名单不符) | **统一提示**"没有找到你的信息",提供**重新填写 / 申请加入名单**(不透露是哪一项错) |
|
||||
| `40007` | 门禁未匹配(姓名或身份证号与名单不符) | **统一提示**"没有找到你的信息",只提供**重新填写**(原「申请加入名单」已于 2026-10 撤销;不透露是哪一项错) |
|
||||
| `40071` | 门禁尝试过于频繁(触发限流) | 提示"操作过于频繁,请稍后再试" |
|
||||
| `40072` | 名单项已被他人绑定 | 提示"该信息已被登记,如有疑问请联系老师" |
|
||||
| `40008` | 任务已截止 / 已归档 | 禁止提交,提示联系管理员 |
|
||||
@@ -2938,6 +2927,7 @@ export/{tenantId}/{taskId}/{exportId}/{批次号}-{专业}-{班级}.zip
|
||||
| 用量统计 | 每日 01:00 | 租户存储量/OCR/短信用量快照,供计费与超额告警 |
|
||||
| 压缩失败重试 | 每 10 分钟 | 扫描"压缩受限/处理失败"文件,重试 ≤3 次 |
|
||||
| 租户到期提醒 | 每日 08:00 | 到期前 30/7/1 天提醒 |
|
||||
| 内置角色授权同步 | 每日 03:20 | 把 `sys_role_permission` / `sys_role_menu` 里**内置角色**的行对齐到代码定义(「角色与授权」已按**一期功能**计,见 `docs/03` §5.10 口径 2) |
|
||||
|
||||
## 附录 F:复审方法论说明
|
||||
|
||||
@@ -2979,7 +2969,7 @@ ERROR: relation "collect_task" does not exist ← 上面第 1 个错误的
|
||||
|---|---|---|
|
||||
| `uk_tenant_code` | sys_tenant、collect_task | → `uk_task_tenant_code` |
|
||||
| `idx_name` | sys_occupation_ref、collect_roster | → `idx_occref_name` / `idx_roster_name` |
|
||||
| `idx_tenant` | collect_material_def、collect_rule_template、collect_cleanup_job | → `idx_matdef_tenant` / `idx_ruletpl_tenant` / `idx_cleanup_tenant` |
|
||||
| `idx_tenant` | collect_material_def、collect_rule_template(`collect_cleanup_job` 已于 2026-10 删除) | → `idx_matdef_tenant` / `idx_ruletpl_tenant` |
|
||||
| `idx_submission` | collect_file、collect_validation_issue、collect_audit_record | → `idx_file_submission` / `idx_issue_submission` / `idx_audit_submission` |
|
||||
|
||||
> ⚠️ **根因**:**MySQL 的索引名只需表内唯一,PG 要求 schema 内全局唯一。**
|
||||
|
||||
Reference in New Issue
Block a user