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:
+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` 的接线检查说明补上第 ⑨ 项。
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user