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:
+8
-2
@@ -11,6 +11,7 @@
|
||||
|
||||
| 版本 | 日期 | 变更内容 | 变更人 |
|
||||
|---|---|---|---|
|
||||
| **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 定时任务 | 产品 |
|
||||
| **V1.2** | 2026-09-24 | **压缩决策 D1 落定**:① §6.3 P3 扫描件档由"建议放宽至 2M"改为 **≤1M 硬约束、无例外**;② 压缩机制改为"体积优先、清晰度可牺牲,但必须标记 + 人工抽检";③ 风险 R1 由"要不要压"改为"压完机构认不认";④ 待确认项 Q1 / Q2-d1 关闭;⑤ **拆出《功能逻辑卷》**,本文不再承载功能运转细节 | 产品 |
|
||||
@@ -2911,8 +2912,8 @@ export/{tenantId}/{taskId}/{exportId}/{批次号}-{专业}-{班级}.zip
|
||||
| `40005` | 证件号与名册不一致 | 展示名册值,要求确认 |
|
||||
| `40006` | 疑似串档(校验位均通过但与他人不符) | 转人工,不自动修正 |
|
||||
| `40007` | 门禁未匹配(姓名或身份证号与名单不符) | **统一提示**"没有找到你的信息",提供**重新填写 / 申请加入名单**(不透露是哪一项错) |
|
||||
| `40007b` | 门禁尝试过于频繁(触发限流) | 提示"操作过于频繁,请稍后再试" |
|
||||
| `40007c` | 名单项已被他人绑定 | 提示"该信息已被登记,如有疑问请联系老师" |
|
||||
| `40071` | 门禁尝试过于频繁(触发限流) | 提示"操作过于频繁,请稍后再试" |
|
||||
| `40072` | 名单项已被他人绑定 | 提示"该信息已被登记,如有疑问请联系老师" |
|
||||
| `40008` | 任务已截止 / 已归档 | 禁止提交,提示联系管理员 |
|
||||
| `40009` | 重复提交(幂等键冲突) | 返回既有结果 |
|
||||
| `40010` | 驳回次数超上限 | 提示联系班主任 |
|
||||
@@ -3010,3 +3011,8 @@ ERROR: relation "collect_task" does not exist ← 上面第 1 个错误的
|
||||
---
|
||||
|
||||
**文档结束**
|
||||
|
||||
---
|
||||
|
||||
> **实现说明(2026-09-28)**:本文件中的 `40007b` / `40007c` 已规范化为 **`40071`** / **`40072`**。
|
||||
> 原因:`code` 按 §1.3 约定是数字,带字母后缀会在 JSON 里变成字符串,破坏前端 `code === 0` 的判断。
|
||||
|
||||
Reference in New Issue
Block a user