2026-05-06-REQ-MOD-001.md 10.2 KB

req_id: REQ-MOD-001 date: 2026-05-06

module: module_mod

Spec: REQ-MOD-001 — 模块新增

目标

实现后端 POST /api/modules 接口:将一条新的业务模块定义写入 tModule 表,作为 ERP 系统功能与权限分组的基础单位,返回新模块主键 iIncrement 与完整 VO。

本 REQ 是 module_mod 模块的第一个 REQ,也是整个 B 阶段的首个 REQ;需顺带建立 Spring Boot 项目骨架(最小可运行单元),仅落地能让本接口工作的横切组件。其余横切组件(JWT 鉴权、登录用户上下文)按 docs/02 § 三 约定由 REQ-USR-004 首次落地。

输入 / 触发

接口POST /api/modules,Content-Type application/json

Request bodyModuleCreateDTO)字段:

字段 类型 必填 校验 / 取值 落库列
sDisplayType String 枚举:手机端 / 前端业务 / 系统配置 / 接口 tModule.sDisplayType
sProcedureName String 长度 1-100;系统内唯一(bDeleted=0 范围内) tModule.sProcedureName
sModuleType String 长度 1-50(自由文本) tModule.sModuleType
sManageDeptEn String 长度 1-50(自由文本) tModule.sManageDeptEn
bShowPermission Boolean 默认 false tModule.bShowPermission
sModuleNameZh String 长度 1-100 tModule.sModuleNameZh
iParentId Integer 必须指向已存在且未软删除的 tModule.iIncrement tModule.iParentId
iSortOrder Integer 默认 0;非负整数 tModule.iSortOrder

鉴权:契约要求 Authorization: Bearer <accessToken> + 权限码 MOD:CREATE。本 REQ 暂不实施实际鉴权(见 § 边界与约束),但 Controller 仍按需要鉴权的形态编写(无 @AnonymousAccess 标注),以便 REQ-USR-004 加入 SecurityFilterChain 后零改动。

输出 / 结果

HTTP 200,响应体(统一响应格式):

{
  "code": 200,
  "message": "操作成功",
  "data": {
    "iIncrement": 12,
    "sDisplayType": "前端业务",
    "sProcedureName": "sp_audit_user_module",
    "sModuleType": "USR",
    "sManageDeptEn": "IT",
    "bShowPermission": false,
    "sModuleNameZh": "用户管理",
    "iParentId": null,
    "iSortOrder": 0,
    "tCreateDate": "2026-05-06T10:30:00",
    "bDeleted": false
  },
  "timestamp": 1746528600000
}

返回 VO(ModuleVO)字段:iIncrement / sDisplayType / sProcedureName / sModuleType / sManageDeptEn / bShowPermission / sModuleNameZh / iParentId / iSortOrder / tCreateDate / bDeleted。其他标准列(sId / sBrandsId / sSubsidiaryId / sCreatedBy / tDeletedDate / sDeletedBy)不对外暴露。

业务规则

  1. 唯一性sProcedureName 在未软删除范围内(bDeleted=0)系统内唯一。冲突返回错误码 40911
  2. 父模块校验:若 iParentId 非空,必须指向 tModule 中存在且 bDeleted=0 的记录;不存在或已删除返回错误码 40411
  3. bShowPermission 默认值:未传入或传 null → 落库 0
  4. iSortOrder 默认值:未传入 → 落库 0
  5. 新建记录初始状态bDeleted=0tDeletedDate=NULLsDeletedBy=NULLtCreateDate=now()(应用层取系统时间,不依赖 DB DEFAULT)。
  6. 多租户字段 (sBrandsId / sSubsidiaryId):本 REQ 不写入(落库 NULL,列已 nullable)。多租户上下文将由 REQ-USR-004 引入登录会话后注入;后续 REQ 通过迁移补齐已有数据。
  7. sCreatedBy:本 REQ 落库 NULL。等待 REQ-USR-004 引入登录用户上下文后从 SecurityContextHolder 取当前用户号回写。
  8. sId(业务 ID 标准列):本 REQ 落库 NULL,不在本接口分配。后续若有外部对接需求再以独立 migration 补齐策略。

边界与约束

鉴权策略(本 REQ 限定)

  • 项目首个 REQ,尚无 SecurityFilterChain。本 REQ 在 SecurityConfig 里配置 permitAll() 临时放行所有 /api/**
  • Controller 不写 @PreAuthorize("hasAuthority('MOD:CREATE')")(无 SecurityContext 时该注解会因 Authentication=nullAccessDeniedException);改为在 Controller 上方写一行说明性注释:// REQ-USR-004 完成后改为 @PreAuthorize("hasAuthority('MOD:CREATE')")
  • REQ-USR-004 完成后会回头给本接口加 @PreAuthorize,并把 permitAll 改为 authenticated()。这是已知技术债,仅在 REQ-USR-004 范围内偿还,本 REQ 不擅自前置。

字段长度与字符集

  • 字符集统一 utf8mb4(DDL 已约束),允许中文落库。
  • 字符串字段超出 DDL 长度限制视为参数错误(40010),不截断。

事务

  • Service 方法标注 @Transactional(rollbackFor = Exception.class),单表写入即可,事务范围最小化。

性能与并发

  • 本 REQ 是单条 INSERT,预期无高并发。uk_procedure_name 唯一约束兜底并发竞争;唯一冲突映射为 40911

项目骨架引导(首 REQ 一次性附带)

本 REQ 顺带建立最小可运行的 Spring Boot 项目骨架(仅引入服务于本 REQ 的部分;后续 REQ 按需扩充)。预期新增目录与文件:

backend/
├── pom.xml
└── src/main/
    ├── java/com/xly/erp/
    │   ├── ErpApplication.java
    │   ├── config/
    │   │   ├── MybatisPlusConfig.java
    │   │   └── SecurityConfig.java          // 临时 permitAll
    │   ├── common/
    │   │   ├── response/
    │   │   │   ├── ApiResponse.java
    │   │   │   └── ErrorCode.java
    │   │   └── exception/
    │   │       ├── BizException.java
    │   │       └── GlobalExceptionHandler.java
    │   └── module/mod/
    │       ├── controller/ModuleController.java
    │       ├── service/ModuleService.java
    │       ├── service/impl/ModuleServiceImpl.java
    │       ├── mapper/ModuleMapper.java
    │       ├── entity/Module.java
    │       ├── dto/ModuleCreateDTO.java
    │       └── vo/ModuleVO.java
    └── resources/
        ├── application.yml                  // ${DB_HOST}/${DB_PORT}/... 占位,从 .env.local 注入
        └── mapper/mod/ModuleMapper.xml

pom.xml 依赖:spring-boot-starter-web / -validation / -securitymybatis-plus-spring-boot3-starterflyway-core + flyway-mysqlmysql-connector-jmapstructhutool-allspring-boot-starter-testspring-security-test

application.yml 通过 Spring Profile + dotenv-java(或本地启动脚本)加载 .env.local;不在仓内硬编码任何凭据(与 docs/04 § 3.5 一致)。

依赖的 schema 表 / 字段

写表tModule(详见 docs/03-数据库设计文档.md § tModule)

字段 落库逻辑
iIncrement DB 自增分配
sId NULL(本 REQ 不分配业务 ID)
sBrandsId NULL(多租户 REQ-USR-004 后引入)
sSubsidiaryId NULL(同上)
tCreateDate 应用层 LocalDateTime.now()
sDisplayType 入参(必填)
sProcedureName 入参(必填,唯一)
sModuleType 入参(必填)
sManageDeptEn 入参(必填)
bShowPermission 入参(默认 false
sModuleNameZh 入参(必填)
iParentId 入参(可选;FK 校验通过的 tModule.iIncrement
iSortOrder 入参(默认 0
sCreatedBy NULL(REQ-USR-004 后引入登录上下文)
bDeleted 0
tDeletedDate NULL
sDeletedBy NULL

索引利用

  • uk_procedure_name UNIQUE:唯一性约束触发并发兜底
  • idx_parent:父模块查询场景(FK 校验时按 iParentIdtModule

外键:本 REQ 利用 fk_module_parent 作为 DB 层兜底;应用层在写入前先查父模块存在性,提前返回 40411,避免直接抛 SQL 完整性异常。

依赖的接口

无(本接口是 module_mod 的入口接口,不依赖其他业务接口)。

后续在 REQ-USR-004 完成后,需要回归补齐本接口的 @PreAuthorize("hasAuthority('MOD:CREATE')"),并要求请求携带有效 JWT。

验收标准

功能正确性

  1. 正向 — 根模块:提交完整字段、iParentId=null,返回 200 + data.iIncrement 非空;DB 中查询新记录字段与入参一致;bDeleted=0 / tCreateDate 已写入。
  2. 正向 — 子模块:先创建 root 再以其 iIncrement 作为 iParentId 创建子模块,返回 200。
  3. 唯一性冲突:用相同 sProcedureName 二次提交,返回 code=40911 + 中文 message;DB 不产生重复记录。
  4. 父模块不存在:传入 iParentId=999999,返回 code=40411
  5. 必填缺失 / 枚举非法 / 长度超限:返回 code=40010 + 错误字段名定位。
  6. 可选默认值:不传 bShowPermission / iSortOrder → DB 落 false / 0

接口契约一致性

  • 响应格式严格符合 {code, message, data, timestamp}(docs/05 § 全局约定)。
  • 错误码段位与 docs/05 一致:200 / 40010 / 40911 / 40411 / 500xx
  • 异常堆栈不出现在响应里(GlobalExceptionHandler 拦截并映射为友好错误,docs/04 § 1.4)。

测试覆盖(feature-tdd 阶段)

  • 单元测试ModuleServiceImpl#create 覆盖 6 类正/反路径(含唯一冲突的 DuplicateKeyException 映射)。
  • 集成测试(MockMvc + 真实 MySQL test schema)
    • 正向:根模块、子模块创建后查 DB 验证字段
    • 反向:唯一冲突、父模块不存在、必填缺失、枚举非法、长度超限 ≥ 5 个 case
    • 并发:两线程同时插入同 sProcedureName,断言一条成功 + 一条 40911(可选,若 CI 跑得动)
  • 测试数据隔离:每个测试方法 @Transactional 自动回滚;不污染其他测试。

代码与文档

  • // REQ-MOD-001 注释贴在 Controller / Service / Mapper / DTO / VO 关键类。
  • 提交按 feat(mod): add module create endpoint REQ-MOD-001 规范。
  • 不引入 docs/04 § 零 技术栈外的依赖(如需,按软规则 S1 经 AskUserQuestion)。