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

8.8 KiB
Raw Blame History

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 一期多身份决策:业务主体身份互斥 (Teacher XOR Student)

  • 在铁路职工技能培训体系中,明确区分角色权限(Role)与业务档案身份(Identity):
    • 教务管理员 (Admin): 是系统权限角色(Role),负责全局资源调配、排课、分班与监控。
    • 任课教师 (Teacher): 是教学业务身份(Identity),负责授课、课次考勤与教学答疑。
    • 参训职工 (Student): 是参训业务身份(Identity),专职参与培训、打卡签到与考核画像。
  • 冻结决策:
    • Teacher 与 Student 业务身份严格互斥:严禁同一个 sys_user 既绑定教师又绑定学员。
    • Admin 是角色而非业务身份:拥有 Admin 角色或管理权限的账号,若同时承担教学任务,允许绑定 Teacher 业务档案(Admin Role + Teacher Identity),并在自服务工作台中以当前教师身份推导数据范围。
    • 超级管理员账号环境纪律:对默认超级管理员测试账号(如 admin / userId=1)的保护应通过运维环境数据纪律落实,业务服务层严禁硬编码 userId=1 永久禁止绑定。

2.2 数据库物理约束与保障机制

为了在数据库持久层杜绝脏数据与多重绑定,在数据库 Migration 脚本中为两张表增加唯一索引:

-- 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 事务中实施悲观锁与互斥断言:

public void validateUserIdentityExclusive(Long userId, String targetType, String targetBusinessId) {
    if (userId == null) return;
    if ("TEACHER".equalsIgnoreCase(targetType)) {
        if (studentMapper.selectTrainStudentByUserId(userId) != null) {
            throw new ServiceException("该系统登录账号已绑定为学员身份,不可重复绑定为教师");
        }
    } else if ("STUDENT".equalsIgnoreCase(targetType)) {
        if (teacherMapper.selectTrainTeacherByUserId(userId) != null) {
            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 在任何时候均可完整追溯责任人。