2026-06-01-REQ-USR-001.md 10.9 KB

REQ-USR-001 增加用户 — 实现规格(后端)

阶段:后端(backend)。作用域限定 controller / service / repository / DTO / 校验 / SQL migration / REST 契约。 SSoT 引用:需求卡片 docs/01-需求清单/USR-用户管理/REQ-USR-001.md;DB 设计 docs/03-数据库设计文档.md;API 契约 docs/05-API接口契约.md;技术规范 docs/04-技术规范.md。 本规格只消费已锁定事实,忽略 UI 描述(控件类型/按钮位置/布局),但校验规则与业务规则全部下沉到后端 DTO + Service。


1. Goal(目标)

后台管理员新建用户账号:指定用户名、密码(默认初始化)、用户类型、语言、可选关联职员、可选权限组授权,保存后账号立即生效可用。对外仅提供一个端点:POST /api/usr/users


2. 输入 / 输出

2.1 输入(请求)

  • Method / PathPOST /api/usr/users
  • Auth:需要 Bearer JWT,且调用方必须为管理员 / 超级管理员(sUserType = 超级管理员)。普通用户调用返回 40301
  • 请求体(JSON)→ CreateUserDTO
DTO 字段 类型 必填 校验 落库列(usr_user 说明
sUserName String @NotBlank + @Pattern(regexp="^[A-Za-z0-9_]{3,20}$") sUserName 登录账号,3-20 位字母/数字/下划线,全局唯一
sUserNo String @Size(max=50) sUserNo 用户号,可由前端在选择职员后带出(后端按传入值落库;为空则存 null)
iEmployeeId Integer iEmployeeId 关联职员 ID;传入时必须为 usr_employee 中存在的记录,否则 40001
sUserType String @NotBlank + 取值 ∈ {普通用户,超级管理员},默认 普通用户 sUserType 用户类型;前端未传时由 Service 兜底为 普通用户
sLanguage String @NotBlank + 取值 ∈ {中文,英文,繁体} sLanguage 界面语言
iCanModifyBill Integer 取值 ∈ {0,1},默认 0 iCanModifyBill 单据修改权限
permissionIds List<Integer> 元素须为 usr_permission 中存在的 id(去重) usr_user_permission 权限组勾选;为空 / null 表示不授权
initialPassword String @Size(max=100),默认 666666 sPassword(BCrypt 哈希) 初始密码;未传时取默认 666666,BCrypt 哈希后入库

系统生成字段(不接受前端传入,由后端填充):tCreateDate=当前时间;sCreator=当前登录用户(从 JWT/SecurityContext 取 sUserName);sPassword=BCrypt(initialPassword);iIsVoid=0(新建即生效);sBrandsId/sSubsidiaryId=表默认值 1111111111(标准列默认,多租户隔离)。tLastLoginDate 保持 null。

2.2 输出(响应)

  • 成功:Result<{ id: number }>code=0data.id = 新建用户主键 usr_user.iIncrement
  • 失败:统一 Result(见 § 6 错误码),message 给可读中文提示,不抛栈。

3. 业务规则

  1. 用户名全局唯一:插入前按 sUserName 查重(命中唯一索引 uk_usr_user_username),存在则抛 BusinessException(40901)。即便并发命中唯一约束(DuplicateKeyException),也统一转换为 40901
  2. 密码初始化 + 哈希存储initialPassword 缺省为 666666;一律经 BCryptPasswordEncoder 哈希后写入 sPassword禁止明文落库 / 进日志 / 进响应。响应与任何查询均不返回密码。
  3. 用户类型默认与约束sUserType 缺省 普通用户;取值仅限 {普通用户,超级管理员},越界 40001
  4. 语言取值约束sLanguage 仅限 {中文,英文,繁体}(依据 docs/03 业务含义与 REQ 卡片下拉来源),越界 40001。默认值见 § 8 决策记录。
  5. 关联职员可选且需存在:传 iEmployeeId 时校验 usr_employee 存在;不存在 40001。职员删除时外键 ON DELETE SET NULL,与本 REQ 写入无冲突。
  6. 权限组授权(多对多)permissionIds 非空时,校验每个 id 在 usr_permission 存在(不存在 40001),去重后为新用户在 usr_user_permission 批量插入 (iUserId, iPermissionId) 行;唯一索引 uk_usr_user_permission 防重复授权。
  7. 新建即生效iIsVoid=0,无需额外激活流程,保存后即可登录(登录由 REQ-USR-004 负责)。
  8. 制单人 / 创建时间审计sCreator 取当前登录用户名,tCreateDate 取当前时间;按 docs/04 § 3.4 可由 BaseEntity + MP 自动填充,或在 Service 显式赋值(实现二选一,结果一致)。
  9. 权限校验前置:仅 超级管理员 / 管理员可调用;非管理员 40301(在 Spring Security 或 Service 入口判定,先于业务校验)。

4. 约束(技术 / 安全)

  • 分层(docs/04 § 1.2):UsrUserController(仅 @Valid 校验 + 委派)→ UsrUserService / UsrUserServiceImpl(业务)→ UsrUserMapper / UsrUserPermissionMapper(MyBatis-Plus)。Controller 禁止直接操作 Mapper。
  • 包路径com.xly.erp.modules.usr.{controller,service,service.impl,mapper,entity,dto,vo}。本 REQ 仅触及 modules/usr/**,不跨模块。
  • 命名(docs/04 § 1.3):类 UsrUserController / UsrUserServiceImpl / UsrUserMapper;方法 createUser;REST 路径 /api/usr/users
  • 统一响应(docs/04 § 1.4):返回 Result<T>code=0 成功;错误码集中在 ResultCode 枚举。
  • 异常处理(docs/04 § 1.5):业务错误抛 BusinessException(ResultCode,msg),由 GlobalExceptionHandlerResult@Valid 校验失败由全局处理器转 40001
  • 事务(docs/04 § 1.6):createUser 涉及 usr_user + usr_user_permission 多表写,方法上加 @Transactional(rollbackFor = Exception.class),任一步失败整体回滚。
  • 认证 / 密码(docs/04 § 1.7):受保护接口经 JwtAuthenticationFilter;密码用 BCryptPasswordEncoder,禁明文。
  • 数据访问(docs/04 § 3.4):只走 Mapper;查重 / 校验用 LambdaQueryWrapper 或 MP 内置。
  • 配置:JWT 密钥、DB 凭据只从 config-vars.yaml / application.yml 读取,不硬编码。admin_init(admin/666666)为系统初始管理员,与本 REQ 默认密码 666666 一致。
  • schema:本 REQ 所需表(usr_user / usr_employee / usr_permission / usr_user_permission)已由 sql/migrations/V1__initial_schema.sql 建好,无需新增 migration

5. Schema 引用(docs/03 SSoT)

  • usr_user(主键 iIncrement;唯一索引 uk_usr_user_username on sUserName;列见 docs/03 § usr_user)。
  • usr_user_permission(关联表;唯一索引 uk_usr_user_permission on (iUserId,iPermissionId);外键 fk_usr_up_user / fk_usr_up_permission 均 CASCADE)。
  • usr_employee(校验 iEmployeeId 存在);usr_permission(校验 permissionIds 存在)。
  • 实体:UsrUserusr_userUsrUserPermissionusr_user_permissionUsrEmployeeusr_employeeUsrPermissionusr_permission(匈牙利前缀列名,实体字段与列名映射保持一致)。

6. API 引用 / 错误码(docs/05 SSoT)

  • 端点契约见 docs/05-API接口契约.md § REQ-USR-001。
  • 错误码:
    • 40001 — 参数校验失败(字段格式 / 必填 / 枚举越界 / 关联 id 不存在)。
    • 40901 — 用户名已存在(sUserName 唯一冲突)。
    • 40301 — 无权限(非管理员调用)。
    • 0 — 成功,返回 data.id

7. 验收标准(Acceptance Criteria)

  1. 正常新增:管理员携带合法 body(sUserName 合规、sUserType/sLanguage 合法)调用 POST /api/usr/userscode=0data.id 为正整数;usr_user 新增一行,sPassword 为 BCrypt 哈希(非明文,可被 BCryptPasswordEncoder.matches("666666", hash) 校验通过),iIsVoid=0sCreator=调用者用户名,tCreateDate 已填。
  2. 重复用户名:以已存在的 sUserName 调用 → code=40901,无新增行(事务回滚)。
  3. 参数非法sUserName 不满足 3-20 位字母数字下划线 / sUserTypesLanguage 越界 / iEmployeeIdpermissionIds 引用不存在的 id → code=40001,无副作用。
  4. 权限组授权:传 permissionIds=[a,b](均存在)→ usr_user_permission 新增 2 行 (newUserId, a/b);重复 id 去重后仅写一次。
  5. 越权访问:普通用户(sUserType=普通用户)或无 / 失效 token 调用 → code=40301(无权限)/ 401(未认证),不创建记录。
  6. 默认密码:不传 initialPassword 时,按 666666 哈希入库;可用 666666 经后续登录流程通过。
  7. 响应不含密码:成功响应体仅含 data.id,绝不含 sPassword 或明文密码。

8. 自主决策记录(decisions)

# 问题 选择 依据 置信度
D1 sLanguage 默认值(REQ 卡片标"无默认",docs/03 标默认 中文 且带"需用户审阅"占位) 后端不强制默认sLanguage 为必填、必须由请求显式传入合法值;若 DB 层因 NOT NULL DEFAULT 中文 而存在兜底,仅作为防御性约束,不作为业务默认 REQ 卡片「语言 必填=是、默认值=—」优先表达业务意图;docs/03 的 中文 默认带审阅占位,不作为锁定事实。取必填可避免歧义 medium
D2 管理员判定口径 sUserType=超级管理员 视为有调用权限;普通用户禁止。具体角色映射若后续有 Spring Security 角色体系再细化 docs/05 标注"仅管理员/超级管理员可调用",本库用户类型枚举仅 {普通用户,超级管理员},无独立管理员角色表 medium
D3 sUserNo 来源 后端按请求传入值落库(前端在选择职员后带出员工编号/姓名填入),后端不主动从职员表反查覆盖 REQ 卡片业务规则"关联职员选择后自动输入"属前端交互;契约 sUserNo(可选) 直接接收,后端阶段不重复实现前端联动 high
D4 不新增 migration 复用 V1__initial_schema.sql 已建的 4 张表 4 张依赖表(usr_user 写、usr_employee/usr_permission 读、usr_user_permission 写)均已在 V1 建好,结构与 docs/03 一致,无 schema 变更 high
D5 密码字段命名 请求体用 initialPassword,落库列 sPassword(BCrypt) 与 docs/05 契约一致(initialPassword(可选,默认 666666));docs/04 § 1.7 要求 BCrypt high

注:docs/03 中 sLanguage / usr_company.sVersion / usr_permission 粒度的「需用户审阅」占位为 DB 文档遗留标记,不在本后端 REQ 作用域内消解;本规格按上表 D1 取最有依据解读继续,不阻塞。