ERPTurbo_Server/.claude/skills/add-state-transition/SKILL.md
shenyifei a66ca25cef feat(reconciliation): 添加对账完成功能
- 新增 ReconciliationCompleteCmd DTO用于对账完成请求
- 在 ReconciliationServiceI 中添加 complete 方法定义
- 在 ReconciliationGateway 中添加 complete 方法定义
- 实现 Gateway 层 complete 方法,包含状态校验逻辑
- 添加 ReconciliationCompleteCmdExe 执行器处理业务逻辑
- 在 ReconciliationServiceImpl 中注入并实现 complete 方法
- 在 Controller 中添加 completeReconciliation 接口
- 添加 B_BIZ_RECONCILIATION_NOT_PENDING 业务异常码
- 修复 ReconciliationGatewayImpl 中的插入顺序问题
2026-01-12 18:00:36 +08:00

8.1 KiB
Raw Blame History

name description
add-state-transition 为实体添加状态流转接口(如:完成、取消等)。遵循 ERPTurbo 项目的 DDD 分层架构规范。

Add State Transition

为实体添加状态流转接口,遵循 DDD 分层架构规范。

⚠️ 重要规则

仅操作 erp-turbo-businesserp-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

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

添加方法签名:

{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

添加方法签名:

{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

import com.xunhong.erp.turbo.api.biz.dto.cmd.{Entity}{Action}Cmd;
import com.xunhong.erp.turbo.api.biz.dto.enums.{Entity}StateEnum;

实现方法:

@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

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

添加字段注入:

private final {Entity}{Action}CmdExe {entity}{Action}CmdExe;

添加方法实现:

@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

import com.xunhong.erp.turbo.api.biz.dto.cmd.{Entity}{Action}Cmd;

添加接口方法:

@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 工具时,限定在特定模块

# ✅ 正确:限定路径
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