--- name: "add-state-transition" description: "为实体添加状态流转接口(如:完成、取消等)。遵循 ERPTurbo 项目的 DDD 分层架构规范。" --- # Add State Transition 为实体添加状态流转接口,遵循 DDD 分层架构规范。 ## ⚠️ 重要规则 **仅操作 `erp-turbo-business` 和 `erp-turbo-admin` 模块,禁止修改 `erp-turbo-svc` 模块!** 详见:`.claude/PROJECT_RULES.md` ## 功能说明 为实体添加状态流转接口,例如:订单完成、对账取消、审核通过等。 ## 输入格式 ``` Entity: {实体名称} Action: {操作名称} TargetState: {目标状态值} Comment: {操作中文描述} ``` **示例:** ``` Entity: Reconciliation Action: Complete TargetState: RECONCILED Comment: 对账完成 ``` ## 工作流程 ### 1. 创建 Cmd DTO 路径:`erp-turbo-common/erp-turbo-api/src/main/java/com/xunhong/erp/turbo/api/biz/dto/cmd/` **文件名:** `{Entity}{Action}Cmd.java` ```java package com.xunhong.erp.turbo.api.biz.dto.cmd; import com.xunhong.erp.turbo.base.dto.Command; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; /** * @author shenyifei */ @Data @Schema(title = "{Comment}") @EqualsAndHashCode(callSuper = true) public class {Entity}{Action}Cmd extends Command { @Schema(title = "{EntityComment}ID", requiredMode = Schema.RequiredMode.REQUIRED, type = "string") private Long {entityFieldName}Id; } ``` ### 2. 更新 Service 接口 路径:`erp-turbo-common/erp-turbo-api/src/main/java/com/xunhong/erp/turbo/api/biz/api/{Entity}ServiceI.java` 添加方法签名: ```java {Entity}VO {action}({Entity}{Action}Cmd {entity}{Action}Cmd); ``` ### 3. 更新 Gateway 接口 路径:`erp-turbo-business/erp-turbo-biz/src/main/java/com/xunhong/erp/turbo/biz/domain/gateway/{Entity}Gateway.java` 添加方法签名: ```java {Entity} {action}({Entity}{Action}Cmd {entity}{Action}Cmd); ``` ### 4. 实现 Gateway 路径:`erp-turbo-business/erp-turbo-biz/src/main/java/com/xunhong/erp/turbo/biz/infrastructure/gateway/{Entity}GatewayImpl.java` 添加 import: ```java import com.xunhong.erp.turbo.api.biz.dto.cmd.{Entity}{Action}Cmd; import com.xunhong.erp.turbo.api.biz.dto.enums.{Entity}StateEnum; ``` 实现方法: ```java @Override public {Entity} {action}({Entity}{Action}Cmd cmd) { LambdaQueryWrapper<{Entity}DO> queryWrapper = Wrappers.lambdaQuery({Entity}DO.class); queryWrapper.eq({Entity}DO::get{Entity}Id, cmd.get{Entity}Id()); queryWrapper.select({Entity}DO::get{Entity}Id, {Entity}DO::getState); queryWrapper.last("limit 1"); {Entity}DO {entity}DO = {entity}Mapper.selectOne(queryWrapper); if (!{entity}DO.getState().equals({Entity}StateEnum.{CURRENT_STATE})) { throw new BizException(BizErrorCode.B_BIZ_{ENTITY_UPPER}_NOT_{CURRENT_STATE_UPPER}); } {entity}DO.setState({Entity}StateEnum.{TARGET_STATE}); {entity}Mapper.updateById({entity}DO); return {entity}Convert.to{Entity}({entity}DO); } ``` ### 5. 创建 Executor 路径:`erp-turbo-business/erp-turbo-biz/src/main/java/com/xunhong/erp/turbo/biz/app/executor/cmd/{Entity}{Action}CmdExe.java` ```java package com.xunhong.erp.turbo.biz.app.executor.cmd; import com.xunhong.erp.turbo.api.biz.dto.cmd.{Entity}{Action}Cmd; import com.xunhong.erp.turbo.api.biz.dto.vo.{Entity}VO; import com.xunhong.erp.turbo.biz.app.assembler.{Entity}Assembler; import com.xunhong.erp.turbo.biz.domain.gateway.{Entity}Gateway; import com.xunhong.erp.turbo.biz.domain.entity.{Entity}; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * @author shenyifei */ @Slf4j @Component @RequiredArgsConstructor public class {Entity}{Action}CmdExe { private final {Entity}Gateway {entity}Gateway; private final {Entity}Assembler {entity}Assembler; public {Entity}VO execute({Entity}{Action}Cmd cmd) { {Entity} {entity} = {entity}Gateway.{action}(cmd); return {entity}Assembler.to{Entity}VO({entity}); } } ``` ### 6. 更新 ServiceImpl 路径:`erp-turbo-business/erp-turbo-biz/src/main/java/com/xunhong/erp/turbo/biz/app/service/{Entity}ServiceImpl.java` 添加字段注入: ```java private final {Entity}{Action}CmdExe {entity}{Action}CmdExe; ``` 添加方法实现: ```java @Override public {Entity}VO {action}({Entity}{Action}Cmd cmd) { return {entity}{Action}CmdExe.execute(cmd); } ``` ### 7. 更新 Controller 路径:`erp-turbo-admin/src/main/java/com/xunhong/erp/turbo/admin/controller/{Entity}Controller.java` 添加 import: ```java import com.xunhong.erp.turbo.api.biz.dto.cmd.{Entity}{Action}Cmd; ``` 添加接口方法: ```java @SaCheckLogin @PostMapping("{action}{Entity}") @Operation(summary = "{Comment}", method = "POST") public SingleResponse<{Entity}VO> {action}{Entity}( @RequestBody @Validated {Entity}{Action}Cmd cmd) { return SingleResponse.of({entity}Service.{action}(cmd)); } ``` ## 命名规范 | 类型 | 格式 | 示例 | |-----|------|-----| | 实体名 | PascalCase | Reconciliation, Order | | 操作名 | PascalCase | Complete, Cancel, Approve | | 方法名 | camelCase | complete, cancel, approve | | 字段名 | camelCase | reconciliationId, orderId | | 常量 | UPPER_SNAKE_CASE | RECONCILIATION, ORDER | ## 使用示例 ### 示例 1:对账完成 ``` Entity: Reconciliation Action: Complete TargetState: RECONCILED Comment: 对账完成 CurrentState: PENDING ``` 生成接口:`POST /operation/completeReconciliation` ### 示例 2:订单取消 ``` Entity: Order Action: Cancel TargetState: CANCELLED Comment: 取消订单 CurrentState: PENDING ``` 生成接口:`POST /operation/cancelOrder` ### 示例 3:审核通过 ``` Entity: PaymentTask Action: Approve TargetState: APPROVED Comment: 审核通过 CurrentState: PENDING_APPROVAL ``` 生成接口:`POST /operation/approvePaymentTask` ## 输出示例 ``` ✅ 已完成状态流转接口添加 ======================================== 创建文件: ✅ api/.../cmd/{Entity}{Action}Cmd.java (erp-turbo-common) ✅ biz/.../executor/cmd/{Entity}{Action}CmdExe.java (erp-turbo-business) 更新文件: ✅ api/.../api/{Entity}ServiceI.java (erp-turbo-common) ✅ biz/.../gateway/{Entity}Gateway.java (erp-turbo-business) ✅ biz/.../gateway/{Entity}GatewayImpl.java (erp-turbo-business) ✅ biz/.../service/{Entity}ServiceImpl.java (erp-turbo-business) ✅ admin/.../controller/{Entity}Controller.java (erp-turbo-admin) 接口路径: POST /{basePath}/{action}{Entity} 请求体: {"{entityFieldName}Id": 123} 状态流转: {CURRENT_STATE} → {TARGET_STATE} ``` ## 注意事项 1. **状态验证**: - Gateway 实现中应验证当前状态 - 使用 `BizException` 抛出业务异常 - 错误码格式:`B_BIZ_{ENTITY}_NOT_{STATE}` 2. **查询优化**: - 使用 `queryWrapper.select()` 只查询必要字段 - 减少数据传输和内存占用 3. **事务处理**: - 根据需要添加 `@Transactional` 注解 - 涉及多表操作时务必使用事务 4. **接口一致性**: - 使用 `@PostMapping` - 使用 `@RequestBody @Validated` 接收参数 - 返回类型统一为 `SingleResponse<{Entity}VO>` 5. **权限控制**: - 所有接口添加 `@SaCheckLogin` 注解 - 根据需要添加其他权限验证 6. **Assemble 方法**: - 使用 `to{Entity}VO()` 而非 `toVO()` - 保持命名一致性 ## 文件搜索规则 使用 Glob 工具时,**限定在特定模块**: ```bash # ✅ 正确:限定路径 erp-turbo-business/**/{Entity}*.java erp-turbo-common/**/{Entity}*.java erp-turbo-admin/**/{Entity}*.java # ❌ 错误:会匹配到 svc 模块 **/*{Entity}*.java ``` ## 常见状态流转模式 | 业务场景 | 当前状态 | 目标状态 | 操作名 | |---------|---------|---------|--------| | 对账完成 | PENDING | RECONCILED | Complete | | 部分开票 | RECONCILED | PARTIAL_INVOICE | PartialInvoice | | 完成开票 | PARTIAL_INVOICE | INVOICED | Invoice | | 部分回款 | INVOICED | PARTIAL_PAYMENT | PartialPayment | | 完成回款 | PARTIAL_PAYMENT | PAID | Payment | | 订单取消 | PENDING | CANCELLED | Cancel | | 审核通过 | PENDING | APPROVED | Approve | | 审核驳回 | PENDING | REJECTED | Reject |