Files
railwaysystem/docs/development/rbac-r2/01_identity_binding_design.md
T

133 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RailwaySystem 身份绑定与账号生命周期设计 (Identity Binding Design)
- **设计版本**: R.2A Design Freeze
- **设计日期**: 2026-09-15
- **核心目标**: 建立 `sys_user` 系统登录账户与 `train_teacher` (教师)、`train_student` (学员) 业务主体之间的确定性逻辑绑定模型,规范生命周期流转与唯一性约束。
---
## 1. 业务主体关联方案比较与最终选型
为了将系统登录账户(`sys_user`)与铁路培训业务实体绑定,针对系统架构演进,深入比较以下两个设计方案:
### 1.1 方案 A:在业务主体表中直联 `user_id` (推荐方案)
- **模型设计**:
- `train_teacher` 表中直接维护 `user_id BIGINT` 列,逻辑关联 `sys_user.user_id`。
- `train_student` 表中直接维护 `user_id BIGINT` 列,逻辑关联 `sys_user.user_id`。
- **架构优势**:
1. **零新增物理表**: 在前置 V2 数据库迁移脚本 `003_alter_master_tables.sql` 中,已在 `train_teacher` 和 `train_student` 表物理添加了 `user_id` 列,现有实体类和 Mapper XML 均已就绪。
2. **极速单表/外联查询**: 获取当前登录人的教师 ID 或学员 ID 时,直接执行 `SELECT t_id FROM train_teacher WHERE user_id = #{userId}`,单索引点查,响应时延 < 1ms。
3. **改造面最小**: 领域模型与既有 MyBatis 实体完全兼容,无需重构现有领域对象树。
- **劣势与局限**:
- 若未来同一个自然人同时具备“讲师”和“学员”双重身份,直联模式需要跨两张表查询或处理两份主体数据。
### 1.2 方案 B:引入统一业务身份映射表 `train_user_identity`
- **模型设计**:
- 新建映射表 `train_user_identity (id, user_id, identity_type, business_id, status, create_time)`。
- `identity_type`: `TEACHER` 对应 `t_id`, `STUDENT` 对应 `s_id`。
- **架构优势**:
1. 结构高度解耦,天然支持未来“一人多身份”、“多端身份动态切换”等超大规模平台需求。
- **劣势与局限**:
1. 需要新建物理表、维护新的外键与索引,增加数据库 Migration 的复杂度和风险。
2. 每次根据登录人推导数据范围均需多一层 JOIN 或一次额外的主键转换查询。
3. 大幅增加现有 Service 和 Mapper 的代码改造量,过度设计(Over-Engineering)。
### 1.3 综合决策对比矩阵与最终推荐
| 评估维度 | 方案 A:直联 `user_id` (现有预留) | 方案 B:统一映射表 `train_user_identity` | 权重 / 判定 |
| :--- | :---: | :---: | :---: |
| **与现有 DDL 契合度** | **100% (列已存在,实体已映射)** | 0% (需新建表与迁移脚本) | 方案 A 显著优胜 |
| **查询性能与简单度** | **极佳 (单表直查,无多余 JOIN)** | 一般 (多一层关系表关联) | 方案 A 胜 |
| **现有代码修改量** | **极小 (仅需在 Service 层赋值)** | 较大 (需新增 Mapper/Service) | 方案 A 胜 |
| **数据一致性与约束** | **单列唯一索引即刻保证** | 需要多列复合唯一约束 | 方案 A 胜 |
| **一期交付稳定性** | **极高 (稳定无副作用)** | 中 (存在映射脏数据风险) | 方案 A 胜 |
| **未来多身份扩展性** | 弱 (仅支持单一身份绑定) | **强 (天生支持多重身份)** | 方案 B 胜 |
**最终冻结决策**: **采用方案 A(RECOMMENDED_IDENTITY_MODEL = DIRECT_USER_ID)**。
以最小的改动、最高的稳定性,完美利用已有的 Kingbase DDL 物理列,达成一期业务 100% 闭环。
---
## 2. 多身份策略与唯一性约束 (Multi-Identity Policy)
### 2.1 一期多身份决策:严格单身份互斥
- 在铁路职工技能培训体系中,角色的职责边界极为森严:
- **教务管理员 (Admin)**: 负责全局资源调配、排课、分班与监控。
- **任课教师 (Teacher)**: 专职负责授课、课次考勤与教学答疑。
- **参训职工 (Student)**: 专职参与培训、打卡签到与考核画像。
- **冻结决策**: **一期系统严格不支持一个账号具备多重业务身份**。
- 严禁同一个 `sys_user` 既绑定教师又绑定学员。
- 严禁超级管理员账号 `admin (userId=1)` 绑定为普通业务教师或学员。
### 2.2 数据库物理约束与保障机制
为了在数据库持久层杜绝脏数据与多重绑定,在数据库 Migration 脚本中为两张表增加唯一索引:
```sql
-- 1. 教师账号唯一索引 (忽略 NULL,只对已绑定的 user_id 施加唯一约束)
CREATE UNIQUE INDEX IF NOT EXISTS uq_train_teacher_user_id
ON train_teacher(user_id)
WHERE user_id IS NOT NULL AND del_flag = '0';
-- 2. 学员账号唯一索引 (忽略 NULL,只对已绑定的 user_id 施加唯一约束)
CREATE UNIQUE INDEX IF NOT EXISTS uq_train_student_user_id
ON train_student(user_id)
WHERE user_id IS NOT NULL AND del_flag = '0';
```
### 2.3 业务服务层互斥前置校验防线
在新建或绑定账号时,在 Java Service 事务中实施互斥断言:
```java
public void validateUserIdentityExclusive(Long userId) {
if (userId == null) return;
if (SecurityUtils.isAdmin(userId)) {
throw new ServiceException("系统超级管理员不可绑定为业务教师或学员");
}
if (teacherMapper.countByUserId(userId) > 0) {
throw new ServiceException("该登录账号已绑定为教师身份,不可重复绑定");
}
if (studentMapper.countByUserId(userId) > 0) {
throw new ServiceException("该登录账号已绑定为学员身份,不可重复绑定");
}
}
```
---
## 3. 用户创建与账号生命周期联动机制
### 3.1 账号创建推荐模式:表单内可选“同时开通系统账号”
- **业务流程**:
1. 管理员在录入教师或学员档案时,表单底部勾选【开通系统登录账号】开关(默认勾选)。
2. 系统自动推荐登录账号格式:
- **教师账号**: 优先采用工号(如 `T2026001`),若未填工号则采用手机号。
- **学员账号**: 优先采用学员编号/工号(如 `GH001`),若无则采用手机号。
3. **初始密码策略**:
- 统一设置初始受控密码:`Railway@2026`。
- 采用 BCrypt 强哈希加密存储入库。
- 强制将 `sys_user.pwd_update_date` 设为 `NULL`,触发前端首次登录时强制要求用户修改密码的安全拦截。
4. **角色自动赋予**:
- 创建教师账号时,自动关联向 `sys_user_role` 插入 `role_id = <train_teacher>`。
- 创建学员账号时,自动关联向 `sys_user_role` 插入 `role_id = <train_student>`。
5. 整个创建过程置于单个本地数据库事务中(`@Transactional(rollbackFor = Exception.class)`),若任意一步失败,整体回滚,杜绝孤立僵尸账号。
### 3.2 补录与独立绑定能力 (兜底机制)
- 对于历史导入或初次创建时未开通账号的主体,在教师/学员管理列表的操作列增加【开通账号】或【重置密码】按钮,支持补录生成 `sys_user` 并回填 `user_id`。
---
## 4. 删除、停用与离职审计策略
在国家关键信息基础设施与铁路行业规范中,系统必须满足操作可追溯与数据终身审计要求:
### 4.1 离职与停用策略 (Status Synchronization)
- 当业务教师被设置为停用 (`train_teacher.status = 0`),或学员由于违反纪律被停用 (`train_student.status = 0`):
- 业务 Service 在同一事务中联动更新关联的 `sys_user.status = '1'` (停用)。
- 清理该用户在 Redis 中的在线 Token 会话缓存,实现“秒级下线”。
- 当业务教师重新启用 (`status = 1`),联动更新 `sys_user.status = '0'` (正常)。
### 4.2 软删除与历史归档策略 (Zero Physical Deletion)
- **红线要求**: **系统严禁对任何已产生过教学、考勤、报到记录的账号执行物理 `DELETE`**!
- 当管理员在界面上删除教师或学员时:
- 业务表更新 `del_flag = '2'`。
- 关联系统用户表更新 `sys_user.del_flag = '2'`。
- 保留 `train_teacher.user_id` 与 `train_student.user_id` 的数值指针不变,确保历史系统操作日志 (`sys_oper_log`)、课次考勤打卡流水 (`train_student_checkin`) 中的 `create_by` 和 `user_id` 在任何时候均可完整追溯责任人。