2026-05-13-users-admins-merge.md 7.1 KB

Users + Admins 表合并设计文档

日期: 2026-05-13 状态: 已定稿,待实施

1. 背景

当前系统使用两张独立的认证表:

  • users — 小程序用户(家长/孩子/规划师),加密算法 xzyj_salt_ + MD5
  • admins — Web管理端用户(管理员/规划师),加密算法 xzyj_admin_salt_ + MD5

规划师需要在小程序端和 Web 管理端同时使用,但两套认证体系导致:

  • 规划师审核通过时需同步创建 admins 记录(AdminController.guide/approve
  • 密码不同步,规划师需要两套密码
  • 扩展新角色需要同时修改两套系统

2. 目标

将 admins 表的功能合并到 users 表,统一认证体系,废弃 admins 表。

3. 设计方案(4个模块)

Module 1: 数据模型合并

users 表新增字段

-- 迁移SQL (DatabaseInitializer)
ALTER TABLE users ADD COLUMN username VARCHAR(50) COMMENT '登录用户名(兼容admins迁移)';
ALTER TABLE users ADD COLUMN status VARCHAR(20) DEFAULT 'active' COMMENT '账号状态: active/inactive/actived';
ALTER TABLE users ADD INDEX idx_username (username);

-- status 字段说明:
--   active   - 正常(小程序用户默认)
--   actived  - 已激活(规划师设置密码后)
--   inactive - 已禁用

User.java 实体新增字段

private String username;  // 登录用户名
private String status;    // 账号状态: active/inactive/actived

数据迁移逻辑 (DatabaseInitializer.runMigrations)

遍历 admins 表所有记录:
  1. 按 phone 查询 users 表
  2. 如果 phone 已存在 users 中:
     - 更新 role(如果 admins.role=teacher 且 users 是 parent,添加 teacher 角色)
     - 设置 password(用 xzyj_salt_ + MD5 重新加密)
     - 设置 username = admins.username(如果有)
     - 设置 status = active
  3. 如果 phone 不存在 users 中:
     - 在 users 表创建新用户
     - 设置必要字段(phone, username, password, role, status)
     - 不创建家庭(admins 没有家庭概念)
  4. 跳过没有 phone 的 admins 记录

注意: admins 表不删除,仅停止使用,保留作为回滚备份。

Module 2: Web登录重建

AdminAuthController 重构

  • AdminMapper 替换为 UserMapper
  • 所有查询从 admins 表改为 users
  • 加密算法从 xzyj_admin_salt_ + MD5 改为 xzyj_salt_ + MD5(与 UserService 一致)
  • loginByPassword 改为用 phone 查询 users 表,支持 teacher 角色
  • 登录时检测 status = inactive → 账号被禁用
  • 登录时检测 role=teacher && password 为空/未设置 → 返回特殊标识让前端提示设置密码

删除文件

  • Admin.java 实体类
  • AdminMapper.java Mapper 接口

AdminController 调整

  • 移除 @Resource AdminMapper 注入
  • guide/approve 方法:删除同步创建/更新 admins 表的逻辑,审核通过直接更新 User 即可
  • 检查其他引用 AdminMapperAdmin 的地方并清理

UserService 修复

  • resetPassword 方法(第1021行)当前使用 xzyj_user_ 加密算法,改为统一的 xzyj_salt_ + MD5

Module 3: 规划师首次密码创建

触发场景

规划师审核通过后,登录 Web 管理端时:

  1. Web 登录页输入手机号 + 密码
  2. AdminAuthController.loginByPassword 查询 users 表
  3. 检测到 role=teacher && (password==null || password=="")
  4. 返回 Result.error(400, "请先设置密码") 附带 needsSetup: true 标识

小程序端设置密码

  1. 规划师在小程序 teacher-profile 页点击 "设置网页密码"
  2. 输入新密码 + 确认密码
  3. 调用后端 API POST /api/auth/set-web-password
  4. 后端执行:
    • UserService.setPassword(userId, newPassword) — 用 xzyj_salt_ + MD5 加密
    • UserService.updateUserStatus(userId, "actived") — 设置状态为已激活
  5. 成功后规划师可用密码登录 Web 管理端

新增后端 API

// AuthController 或 AdminAuthController 新增
@PostMapping("/api/auth/set-web-password")
public Result<Boolean> setWebPassword(@RequestAttribute("userId") Long userId,
                                       @RequestBody Map<String, String> params) {
    // 1. 校验密码强度 (6位以上)
    // 2. 设置密码 (xzyj_salt_ + MD5)
    // 3. 设置 status = actived
    // 4. 返回成功
}

Module 4: 规划师密码重置

小程序端修改密码

  1. 规划师在 teacher-profile 页 "修改网页密码"
  2. 输入旧密码 + 新密码 + 确认新密码
  3. 调用后端 API POST /api/auth/reset-web-password
  4. 后端执行:
    • 校验旧密码是否正确
    • 设置新密码(xzyj_salt_ + MD5 加密)
    • 状态保持不变

新增后端 API

// AuthController 或 AdminAuthController 新增
@PostMapping("/api/auth/reset-web-password")
public Result<Boolean> resetWebPassword(@RequestAttribute("userId") Long userId,
                                         @RequestBody Map<String, String> params) {
    // 1. 校验旧密码
    // 2. 设置新密码
    // 3. 返回成功
}

4. 加密算法统一

统一算法: DigestUtils.md5DigestAsHex(("xzyj_salt_" + password).getBytes(StandardCharsets.UTF_8))

位置 修改前 修改后
AdminAuthController.encryptPassword (L271) xzyj_admin_salt_ + MD5 xzyj_salt_ + MD5
UserService.encryptPassword (L749) xzyj_salt_ + MD5 ✅ 不变
UserService.resetPassword (L1021) xzyj_user_ + MD5 xzyj_salt_ + MD5

5. 影响范围

后端文件变更

文件 变更类型 说明
entity/User.java 修改 新增 username, status 字段
entity/Admin.java 删除 废弃
mapper/AdminMapper.java 删除 废弃
controller/admin/AdminAuthController.java 重写 UserMapper 替代 AdminMapper,统一加密
controller/admin/AdminController.java 修改 移除 AdminMapper 引用,清理同步逻辑
controller/auth/AuthController.java 修改 新增 set-web-password, reset-web-password 端点
service/UserService.java 修改 修复 resetPassword 算法,新增 setStatus 方法
config/DatabaseInitializer.java 修改 新增 username/status 迁移 + admins→users 数据迁移

前端文件变更

文件 变更类型 说明
zxyj-frontend/pages/teacher/teacher-profile.vue 修改 拆分密码设置为首次设置和修改密码两个入口
zxyj-frontend/utils/api.js 修改 新增 set-web-password, reset-web-password API

Web端文件变更

文件 变更类型 说明
zxyj-web/src/views/Login.vue 无需修改 AdminAuthController 端点不变(logic变化内部处理)

6. 安全考虑

  • 所有密码使用 MD5 + 固定 salt 传输(当前系统已有模式,暂不改 BCrypt 以保持兼容)
  • 小程序端不存储明文密码
  • Web 端密码传输使用 HTTPS(生产环境配置)

7. 回滚方案

  • admins 表保留不删除,数据完整
  • 如果合并后出现问题,可停止使用 users 表的新字段
  • 钥匙管理器中有 DatabaseInitializer 中的迁移代码,重新部署会自动跳过