feat(m0): 后端骨架 + 账号体系(M0 验收 36/36 通过)
技术栈(已在 4.1.1 上实测跑通,非纸面选型)
Java 21 + Spring Boot 4.1.1 + MyBatis-Plus 3.5.17(boot4) + PostgreSQL 16.15
+ Redis 7 + Flyway 12.4 + springdoc 3.1.1;前端定 Vue 3 + Element Plus + uni-app
交付内容
M0-01 基础设施 deploy/docker-compose.yml(PG + Redis + MinIO,容器名与端口避让本机既有项目)
M0-02 后端骨架 xt-common + xt-server;统一响应 / 全局异常 / 58 个错误码 /
多租户拦截 / 软删除 / 字段自动填充 / AES-GCM / HMAC 索引 / RSA / JWT
M0-05 租户 开通租户(一次完成建租户 + 发 6 个内置角色 + 建首个管理员)、列表、详情
M0-06 组织 CRUD + 组织树 + ancestor_path 自动维护 + 占用校验
M0-07 PC 登录 RSA 解密 + 图形验证码 + 失败锁定 + 等时比较防账号枚举 + 审计日志
M0-08 登出/当前用户 Token 黑名单(存 SHA-256,TTL 到自然过期)
M0-01 数据库 Flyway 2 个迁移 → 26 张表(20 业务 + 5 标准 + 1 迁移记录)
安全姿态
· 租户拦截器【默认拒绝】:匿名请求触及租户表直接抛 40101,不做「不加条件放行」
· 权限码定义在代码里(PermissionDef / BuiltinRole),不建 sys_permission 表,杜绝前后端漂移
· 匿名接口只靠 @PublicApi 放行,不做路径白名单,避免两套机制各说各话
· 账号不存在时也跑一次 BCrypt(假哈希),拉平响应时间防枚举
· 登录密码 RSA 传输,防止网关/APM 把明文密码记进日志
脚本
scripts/sync-ddl.py 文档 DDL → Flyway 迁移,带 --check 防漂移
scripts/e2e-m0.py M0 验收 36 项,可进 CI
scripts/verify-docs.py 文档一致性(接口数【逐节】核对、错误码、跨文档数字、表格列数)
scripts/dev-login.py RSA 加密登录助手
scripts/dev-clean-e2e.py 清理 E2E 测试数据
文档修正(实测暴露,非人工审查)
· DDL 缺陷:sys_tenant.contact_phone / sys_user.phone / collect_roster.phone
原 varchar(20) 按明文长度建,装不下 AES-GCM 密文(约 52 字符),改 varchar(128)(PRD V2.4)
· 错误码 40007b/40007c 带字母后缀与「code 为数字」的约定冲突,规范化为 40071/40072
· 07-开发实施计划 新增 §9 版本兼容踩坑记录(10 条实录)与 §10 M0 完成情况
This commit is contained in:
+57
-7
@@ -35,6 +35,39 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 一·三、开发进展(2026-09-28 起)
|
||||
|
||||
> **代码位置**:`backend/`(Java 21 + Spring Boot 4.1.1)、`deploy/`(基础设施)、`scripts/`(工具)
|
||||
|
||||
| 项 | 状态 |
|
||||
|---|---|
|
||||
| 技术栈定版 | ✅ Java 21 + **Spring Boot 4.1.1** + MyBatis-Plus 3.5.17(boot4) + PG16 + Redis7<br>PC: Vue3 + Element Plus;小程序: uni-app |
|
||||
| M0 后端骨架 | ✅ 统一响应/异常/58 错误码/多租户拦截/软删除/字段填充/AES-GCM/HMAC/RSA/JWT/权限模型 |
|
||||
| M0 数据库 | ✅ Flyway 2 个迁移;26 张表(20 业务 + 5 标准 + 1 迁移记录) |
|
||||
| M0 后端接口 | ✅ 租户开通与列表、组织 CRUD 与树、PC 登录/登出/当前用户 |
|
||||
| M0 验收 | ✅ **`python scripts/e2e-m0.py` → 35/35 通过** |
|
||||
| 待做 | 用户 CRUD 与 Excel 导入;PC/小程序前端;CI/CD;H5 预研 |
|
||||
|
||||
**关键实现决策(不要在后继开发中改回去)**
|
||||
|
||||
| 决策 | 理由 |
|
||||
|---|---|
|
||||
| **权限码定义在代码里**(`PermissionDef` / `BuiltinRole`),不建 `sys_permission` 表 | 权限码是前后端契约,放 DB 必然与代码漂移;一期无"权限配置界面"需求 |
|
||||
| **租户拦截器默认拒绝**:匿名请求触及租户表直接抛 40101 | 漏加租户条件 = 越租户泄露全校身份证号,不可接受的失败模式;宁可让开发者立刻撞墙 |
|
||||
| 匿名接口只靠 `@PublicApi` 放行,**不做路径白名单** | 两套机制会让"哪些接口公开"出现两个来源,改一处漏一处 |
|
||||
| **登录时账号不存在也跑一次 BCrypt**(假哈希) | 否则响应时间差会泄露"账号是否存在",直接把密码喷洒成功率提高几个数量级 |
|
||||
| 密码 RSA 加密传输 | 不是替代 HTTPS,而是防止网关/APM/链路追踪把明文密码记进日志 |
|
||||
| 登出黑名单**存 token 的 SHA-256**,TTL 到自然过期 | token 本身是凭证,明文写进 Redis 等于复制了一份到别处 |
|
||||
|
||||
**四个必须实测的坑**(详见 `07-开发实施计划.md` §9,10 条实录)
|
||||
|
||||
Boot 4 / Flyway 10 / MP 3.5.9 这一批版本都在"把单体拆成模块",
|
||||
导致**「依赖在、功能不在」的静默失败变多**。典型:只加 `flyway-core` 时迁移**不跑也不报错**。
|
||||
**规则:每加一个中间件,必须实测它的核心动作真的发生了,不能只看依赖解析成功。**
|
||||
|
||||
---
|
||||
|
||||
## 一·五、客户回执(2026-09-28 · 需求已冻结)
|
||||
|
||||
> **客户全部 23 项按建议方案确认。** 唯一功能增量是 **B1:名单加入申请通道**。
|
||||
@@ -82,21 +115,21 @@
|
||||
|
||||
| 文件 | 版本/大小 | 状态 |
|
||||
|---|---|---|
|
||||
| `docs/数据信息填报系统PRD.md` | V2.3 / 173KB / 2816 行 | ✅ 含 115 条 FR、17 条 VAL、96 接口、19 表 DDL、实测附录 |
|
||||
| `docs/数据信息填报系统PRD.md` | V2.4 / 189KB / 3019 行 | ✅ 含 **125 条 FR**、**21 条 VAL**、**121 个接口**、**20 表 DDL**、实测附录 |
|
||||
| `docs/数据信息填报系统PRD-功能逻辑卷.md` | V1.9 / 88KB | ✅ 纯功能逻辑,**不含库表/接口/代码** |
|
||||
| `docs/db/schema-postgresql.sql` | 34KB | ✅ **PG 16.15 实测零错误**(19 表 / 167 列注释 / 61 索引) |
|
||||
| `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/index.html` | 7KB | ✅ 三端入口 |
|
||||
| `docs/00-交付计划.md` | — | ✅ 7 项计划与进度记录 |
|
||||
| `docs/01-客户与机构确认函.md` | 18KB | ✅ **P1** 23 项**已全部确认**(客户按建议执行,需求冻结) |
|
||||
| `docs/01-客户与机构确认函.md` | 23KB | ✅ **P1** 23 项**已全部确认**(客户按建议执行,需求冻结) |
|
||||
| `docs/02-需求追溯矩阵.md` | 10KB | ✅ **P2** 审计报告,含 10 个接口缺口 |
|
||||
| `docs/03-接口设计.md` | **102KB / 2626 行** | ✅ **P3 117 个接口**字段级定义 + 枚举全集 + 12 个 VO + 50 个错误码;**自校验通过** |
|
||||
| `docs/06-验收测试清单.md` | **29KB** | ✅ **P6 36 条 Golden 断言** + **111 条功能验收** + **27 条异常边界** |
|
||||
| `docs/03-接口设计.md` | **115KB / 2883 行** | ✅ **P3 121 个接口**字段级定义 + 枚举全集 + 12 个 VO + **58 个错误码**;§4 逐节声称数与表行数一致 |
|
||||
| `docs/06-验收测试清单.md` | **33KB** | ✅ **P6 36 条 Golden 断言** + **111 条功能验收** + **27 条异常边界** |
|
||||
| `../README.md` | **10KB** | ✅ **P7 文档导航**,按角色读法 + 快速上手 |
|
||||
| `docs/04-核心模块设计说明.md` | **56KB / 1218 行** | ✅ **P4 六个模块**伪代码级说明 + 依赖图 + Golden 基线 |
|
||||
| `docs/05-开发任务拆解.md` | **28KB / 468 行** | ✅ **P5 94 个任务 / 380 人日 / 14 周**,含关键路径与核算口径 |
|
||||
| `docs/04-核心模块设计说明.md` | **69KB / 1453 行** | ✅ **P4 六个模块**伪代码级说明 + 依赖图 + Golden 基线 |
|
||||
| `docs/05-开发任务拆解.md` | **31KB / 506 行** | ✅ **P5 100 个任务 / 387 人日 / 15 周**,含关键路径、一期范围与核算口径 |
|
||||
|
||||
---
|
||||
|
||||
@@ -190,6 +223,23 @@ A4 扫描件 300dpi(2480×3508)/q85 · 一寸照片固定 295×413 · 学籍表
|
||||
### 教训一:脚本审计前必须先验证脚本本身
|
||||
P2 审计时脚本因未匹配加粗编号 ``| **FR-614** |``,误报 3 个"需求条目问题"(实为假警报);若直接采信并"修复",会引入 3 处新错误。同时 FR 总数由误算的 102 修正为 **115**。
|
||||
|
||||
> **⚠️ 这条教训在 2026-09-28 写开发实施计划时又复现了一次。**
|
||||
> 我为交接摘要重算交付物体量,用了三个正则去数"接口数",分别得到 **74 / 95 / 121**——
|
||||
> 三个都"看起来对",但没有一个经过验证。
|
||||
>
|
||||
> 定位过程:**逐节对比「标题声称数」与「实际表行数」**,发现
|
||||
> ① 我漏了 `| ⭐ GET | ... |` 这种 ⭐ 在方法**前面**的行;
|
||||
> ② §4.9 / §4.11 是**索引节**(交叉引用别处已计数的接口),本就不该计入。
|
||||
> 修正后逐节声称数与表行数**全部为 0 差**,合计 **121**。
|
||||
>
|
||||
> **加固**:`scripts/verify-docs.py` 现在会做「**逐节声称数 vs 实际行数**」的一致性检查,
|
||||
> 而不是只看总数对不对——总数相等也可能是个别节多算、个别节少算恰好抵消。
|
||||
>
|
||||
> **可复用的规则**:
|
||||
> 1. 数字类断言必须**交叉核对至少两种独立方法**;
|
||||
> 2. 核对要**分块**(逐节/逐表),不能只看总和;
|
||||
> 3. 相互矛盾的中间值**一个都不要先写进文档**——写错再改,比一开始就空着更容易误导人。
|
||||
|
||||
### 教训二:写新文档必须回查已有编号体系,不能凭记忆
|
||||
P4 写 M5 校验引擎时,我凭记忆用了一套 VAL 编号,**与 PRD 权威清单整体错位**:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user