--- req_id: REQ-USR-001 date: 2026-05-08 module: module_usr --- # Spec: REQ-USR-001 — 增加用户 ## 目标 超级管理员在用户管理页新建用户账号,填写基本信息并分配权限组,账号保存后立即生效,可使用初始密码 666666 登录系统。 ## 输入 / 触发 **触发**:超级管理员点击用户管理页「新增」按钮,弹出 Drawer,填写表单并提交。 **POST /api/usr/users 请求体**: ```json { "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 返回列表,列:权限分类/权限名称 | ## 输出 / 结果 **成功响应**: ```json { "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_permission`(`sUserId=新用户 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` 取到 `brandId` 和 `username`。此修改属必要基础设施扩展(CLAUDE.md S2)。 ## 依赖的 schema 表 / 字段 **写入表**: - `usr_user`:`sId`、`sBrandsId`、`sCreatorUsername`、`tCreateDate`、`sUserCode`、`sUsername`、`sPasswordHash`、`sUserType`、`sLanguage`、`bCanEditDoc`、`bIsDisabled`、`sEmployeeId`、`iLoginFailCount` - `usr_user_permission`:`sId`(UUID)、`sBrandsId`、`tCreateDate`、`sUserId`、`sPermGroupId` **只读表**: - `tStaff`:`sId`、`sStaffName`(员工下拉列表及 employeeId 校验) - `usr_permission_group`:`sId`、`sGroupCode`、`sGroupName`、`sCategory`(权限组复选列表) ## 依赖的接口 - `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"` 隐藏逻辑)。