feat(ops): OrderLifecycleManager 支持 forceComplete 强制完成工单

新增 forced 字段到 OrderTransitionRequest,StateTransitionHandler 根据
该字段选择 transition 或 forceTransition,确保强制状态转换也走完整
责任链(队列同步 + 事件发布),避免绕过 QueueSyncHandler 产生脏数据。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
lzh
2026-03-26 15:29:29 +08:00
parent c0c9854e73
commit 8406a80655
4 changed files with 66 additions and 7 deletions

View File

@@ -166,6 +166,25 @@ public interface OrderLifecycleManager {
*/
void cancelOrder(Long orderId, Long operatorId, OperatorTypeEnum operatorType, String reason);
/**
* 强制完成工单(跳过状态规则校验,走完整责任链)
* <p>
* 适用于系统自动结单等场景:告警自动解除时工单可能处于任何非终态
* QUEUED / DISPATCHED / CONFIRMED / ARRIVED / PAUSED
* 需要直接跳转到 COMPLETED同时确保队列清理和事件发布。
* <p>
* 与直接调用 {@code OrderStateMachine.forceTransition()} 的区别:
* 本方法走完整责任链(状态转换 → 队列同步 → 事件发布),
* 避免队列脏数据和 Redis 状态不一致。
*
* @param orderId 工单ID
* @param operatorId 操作人ID
* @param operatorType 操作人类型
* @param remark 完成备注
* @return 转换结果
*/
OrderTransitionResult forceComplete(Long orderId, Long operatorId, OperatorTypeEnum operatorType, String remark);
// ==================== 查询方法 ====================
/**

View File

@@ -346,6 +346,32 @@ public class OrderLifecycleManagerImpl implements OrderLifecycleManager {
.build());
}
@Override
@Transactional(rollbackFor = Exception.class)
public OrderTransitionResult forceComplete(Long orderId, Long operatorId,
OperatorTypeEnum operatorType, String remark) {
log.info("强制完成工单: orderId={}, operatorId={}, operatorType={}, remark={}",
orderId, operatorId, operatorType, remark);
OpsOrderDO order = opsOrderMapper.selectById(orderId);
if (order == null) {
log.warn("强制完成工单失败,工单不存在: orderId={}", orderId);
return OrderTransitionResult.fail(orderId, "工单不存在");
}
OrderTransitionRequest request = OrderTransitionRequest.builder()
.orderId(orderId)
.targetStatus(WorkOrderStatusEnum.COMPLETED)
.operatorType(operatorType != null ? operatorType : OperatorTypeEnum.SYSTEM)
.operatorId(operatorId)
.reason(remark)
.assigneeId(order.getAssigneeId())
.forced(true)
.build();
return transition(request);
}
// ==================== 查询方法 ====================
@Override

View File

@@ -38,13 +38,19 @@ public class StateTransitionHandler extends TransitionHandler {
// 通过状态机执行状态转换
try {
orderStateMachine.transition(
order,
targetStatus,
request.getOperatorType() != null ? request.getOperatorType() : OperatorTypeEnum.SYSTEM,
request.getOperatorId(),
request.getReason()
);
OperatorTypeEnum operatorType = request.getOperatorType() != null
? request.getOperatorType() : OperatorTypeEnum.SYSTEM;
if (Boolean.TRUE.equals(request.getForced())) {
// 强制转换:跳过状态规则校验(用于系统自动结单等场景)
orderStateMachine.forceTransition(
order, targetStatus, operatorType,
request.getOperatorId(), request.getReason());
} else {
orderStateMachine.transition(
order, targetStatus, operatorType,
request.getOperatorId(), request.getReason());
}
// 更新上下文中的状态
context.setNewStatus(targetStatus);

View File

@@ -91,6 +91,14 @@ public class OrderTransitionRequest {
*/
private PriorityEnum priority;
/**
* 是否强制转换(跳过状态规则校验)
* <p>
* 适用于系统自动结单等场景:告警自动解除时工单可能处于任何非终态,
* 需要直接跳转到 COMPLETED / CANCELLED不经过中间状态。
*/
private Boolean forced;
/**
* 扩展信息(可选)
* <p>