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

314 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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 |