2026-05-08-REQ-USR-001.md 5.83 KB

req_id: REQ-USR-001 date: 2026-05-08

module: module_usr

Spec: REQ-USR-001 — 增加用户

目标

超级管理员在用户管理页新建用户账号,填写基本信息并分配权限组,账号保存后立即生效,可使用初始密码 666666 登录系统。

输入 / 触发

触发:超级管理员点击用户管理页「新增」按钮,弹出 Drawer,填写表单并提交。

POST /api/usr/users 请求体

{
  "userCode": "string(必填,用户号)",
  "username": "string(必填,用户名,同一 brand 内唯一)",
  "userType": "普通用户|超级管理员(必填)",
  "language": "中文|英文|繁体(必填)",
  "canEditDoc": false,
  "employeeId": "string|null(可选,tStaff.sId)",
  "permGroupIds": ["string"]
}

前端 Drawer 表单字段: | 字段 | 组件 | 必填 | 来源/选项 | |---|---|---|---| | 用户号 | Input | 是 | 手工输入 | | 用户名 | Input | 是 | 手工输入 | | 类型 | Select | 是 | 普通用户 / 超级管理员 | | 语言 | Select | 是 | 中文 / 英文 / 繁体 | | 单据修改权限 | Checkbox | 否 | 默认不勾 | | 员工 | Select | 否 | GET /api/usr/staffs 返回列表,label=sStaffName, value=sId | | 权限组 | Table+Checkbox | 否 | GET /api/usr/permission-groups 返回列表,列:权限分类/权限名称 |

输出 / 结果

成功响应

{
  "code": 200,
  "message": "操作成功",
  "data": {
    "userId": "string",
    "userCode": "string",
    "username": "string"
  }
}

前端收到成功响应后:message.success("新增用户成功") → 关闭 Drawer。

业务规则

  1. 权限控制:请求者 JWT 中 userType == 超级管理员,否则抛 BizException(40300, "权限不足")
  2. 唯一性检查(同 brand 内)
    • sUserCode 全库唯一(索引 uk_usr_user_usercode);重复抛 BizException(40902, "用户号已存在")
    • sUsername 在同一 sBrandsId 内唯一(索引 uk_usr_user_username_tenant);重复抛 BizException(40901, "用户名已存在")
  3. 密码初始化sPasswordHash = BCryptPasswordEncoder.encode("666666"),禁止存明文。
  4. 系统字段自动填充(Controller 或 Service 层注入 UserPrincipal):
    • sId = UUID.randomUUID().toString()
    • sBrandsId = JWT claim brandId
    • sCreatorUsername = JWT claim username
    • tCreateDate = LocalDateTime.now()
    • bIsDisabled = 0
    • iLoginFailCount = 0
  5. employeeId 校验:若不为 null,需验证 tStaff.sId == employeeId && tStaff.sBrandsId == brandId;不存在抛 BizException(40001, "员工不存在")
  6. 权限组写入permGroupIds 非空时,批量插入 usr_user_permissionsUserId=新用户 sId, sPermGroupId=item);permGroupIds 为空/null 时跳过。
  7. 事务createUser() 方法标注 @Transactional,usr_user 插入 + usr_user_permission 批量插入在同一事务中。

边界与约束

  • 初始密码仅后端生成,不通过请求体传入,前端不展示。
  • sUsername 一旦创建不可修改(REQ-USR-002 约束)。
  • userType 枚举只有 普通用户超级管理员 两种合法值;language 枚举只有 中文英文繁体;后端用 @Pattern/@NotNull + @Valid 校验,违规触发 40001
  • 跨模块改动:需修改 JwtAuthenticationFilter.java(config 层),将 JWT claims 包装为 UserPrincipal 存入 SecurityContext,使 UserController 可通过 @AuthenticationPrincipal UserPrincipal 取到 brandIdusername。此修改属必要基础设施扩展(CLAUDE.md S2)。

依赖的 schema 表 / 字段

写入表

  • usr_usersIdsBrandsIdsCreatorUsernametCreateDatesUserCodesUsernamesPasswordHashsUserTypesLanguagebCanEditDocbIsDisabledsEmployeeIdiLoginFailCount
  • usr_user_permissionsId(UUID)、sBrandsIdtCreateDatesUserIdsPermGroupId

只读表

  • tStaffsIdsStaffName(员工下拉列表及 employeeId 校验)
  • usr_permission_groupsIdsGroupCodesGroupNamesCategory(权限组复选列表)

依赖的接口

  • POST /api/auth/login(REQ-USR-004)— 前端登录获取 JWT,携带 Bearer Token 才可调用本接口
  • GET /api/usr/staffs(本 REQ 新增,不在 docs/05 原始清单)— 员工下拉数据源,鉴权接口
    • 响应:{ "data": [{ "sId": "...", "sStaffName": "..." }] }
  • GET /api/usr/permission-groups(本 REQ 新增,不在 docs/05 原始清单)— 权限组复选数据源,鉴权接口
    • 响应:{ "data": [{ "sId": "...", "sGroupCode": "...", "sGroupName": "...", "sCategory": "..." }] }
  • POST /api/usr/users(docs/05 已定义)— 本 REQ 核心创建接口

验收标准

  1. 超级管理员提交合法数据 → HTTP 200,data.userId 非空,数据库 usr_user 表新增一条记录,密码字段为 BCrypt 哈希(不含 "666666" 明文)。
  2. 新建用户可立即用 POST /api/auth/login(brandNo + username + "666666")登录成功。
  3. 重复用户名(同 brand)→ 返回 code=40901,用户名已存在。
  4. 重复用户号 → 返回 code=40902,用户号已存在。
  5. 普通用户调用本接口 → 返回 code=40300,权限不足。
  6. 缺必填字段(userCode / username / userType / language 为空)→ 返回 code=40001
  7. 选中权限组后提交 → usr_user_permission 表有对应关联记录。
  8. 关联无效 employeeId → 返回 code=40001,员工不存在。
  9. 前端:新增 Drawer 表单加载时,员工下拉和权限组 Table 已从对应 API 拉取数据;提交成功后 Drawer 关闭并显示 message.success
  10. 前端:普通用户登录后,用户管理页「新增」按钮不显示(PermButton permission="usr:create" 隐藏逻辑)。