chore: 【ops】工单生命周期操作(暂停、回复、完成、取消)

This commit is contained in:
lzh
2026-01-08 15:06:08 +08:00
parent 5e9dc8b104
commit 48e1a91fd5
2 changed files with 510 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
package com.viewsh.module.ops.core.lifecycle;
import com.viewsh.module.ops.enums.OperatorTypeEnum;
import com.viewsh.module.ops.enums.PriorityEnum;
/**
* 工单生命周期管理器接口
* <p>
* 职责:
* 1. 统一管理工单状态和队列状态的同步
* 2. 处理工单的暂停/恢复/打断等复杂状态变更
* 3. 提供事务保证,确保状态一致性
* <p>
* 设计原则:
* - 单一职责:只负责生命周期状态管理
* - 事务保证:所有状态变更在同一事务中完成
* - 扩展性:通过事件发布机制解耦业务逻辑
*
* @author lzh
*/
public interface OrderLifecycleManager {
// ==================== 暂停/恢复 ====================
/**
* 暂停工单(同步更新队列状态和工单状态)
* <p>
* 状态转换:
* - 工单状态ARRIVED → PAUSED
* - 队列状态PROCESSING → PAUSED
*
* @param orderId 工单ID
* @param operatorId 操作人ID
* @param reason 暂停原因
* @throws IllegalStateException 如果工单状态不允许暂停
*/
void pauseOrder(Long orderId, Long operatorId, String reason);
/**
* 恢复工单(同步更新队列状态和工单状态)
* <p>
* 状态转换:
* - 工单状态PAUSED → DISPATCHED
* - 队列状态PAUSED → PROCESSING
*
* @param orderId 工单ID
* @param operatorId 操作人ID
* @throws IllegalStateException 如果工单状态不允许恢复
*/
void resumeOrder(Long orderId, Long operatorId);
// ==================== 打断/恢复 ====================
/**
* 打断工单P0紧急任务场景
* <p>
* 状态转换:
* - 工单状态DISPATCHED/CONFIRMED/ARRIVED → PAUSED
* - 队列状态PROCESSING → PAUSED
* <p>
* 使用场景当P0紧急任务需要插队时打断当前正在执行的任务
*
* @param orderId 被打断的工单ID
* @param urgentOrderId 紧急工单IDP0任务
* @param operatorId 操作人ID
* @throws IllegalStateException 如果工单状态不允许被打断
*/
void interruptOrder(Long orderId, Long urgentOrderId, Long operatorId);
/**
* 恢复被打断的工单P0任务完成后自动恢复
* <p>
* 状态转换:
* - 工单状态PAUSED → DISPATCHED
* - 队列状态PAUSED → PROCESSING
*
* @param orderId 被打断的工单ID
* @param operatorId 操作人ID
*/
void resumeInterruptedOrder(Long orderId, Long operatorId);
// ==================== 完工/取消 ====================
/**
* 完成工单(清理队列记录)
* <p>
* 状态转换:
* - 工单状态ARRIVED → COMPLETED
* - 队列状态PROCESSING → REMOVED
*
* @param orderId 工单ID
* @param operatorId 操作人ID
* @param remark 完成备注
*/
void completeOrder(Long orderId, Long operatorId, String remark);
/**
* 取消工单
* <p>
* 状态转换:
* - 工单状态:任何非终态 → CANCELLED
* - 队列状态:任何状态 → REMOVED
*
* @param orderId 工单ID
* @param operatorId 操作人ID
* @param operatorType 操作人类型
* @param reason 取消原因
*/
void cancelOrder(Long orderId, Long operatorId, OperatorTypeEnum operatorType, String reason);
// ==================== 查询方法 ====================
/**
* 检查工单是否可以暂停
*
* @param orderId 工单ID
* @return 是否可以暂停
*/
boolean canPause(Long orderId);
/**
* 检查工单是否可以恢复
*
* @param orderId 工单ID
* @return 是否可以恢复
*/
boolean canResume(Long orderId);
/**
* 检查工单是否可以打断
*
* @param orderId 工单ID
* @param urgentPriority 紧急任务优先级
* @return 是否可以打断
*/
boolean canInterrupt(Long orderId, PriorityEnum urgentPriority);
}

View File

@@ -0,0 +1,373 @@
package com.viewsh.module.ops.core.lifecycle;
import com.viewsh.module.ops.api.queue.OrderQueueDTO;
import com.viewsh.module.ops.api.queue.OrderQueueService;
import com.viewsh.module.ops.core.event.OrderEventPublisher;
import com.viewsh.module.ops.core.event.OrderStateChangedEvent;
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
import com.viewsh.module.ops.enums.OperatorTypeEnum;
import com.viewsh.module.ops.enums.PriorityEnum;
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
import com.viewsh.module.ops.service.fsm.OrderStateMachine;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Objects;
/**
* 工单生命周期管理器实现
* <p>
* 职责:
* 1. 统一管理工单状态和队列状态的同步
* 2. 处理工单的暂停/恢复/打断等复杂状态变更
* 3. 提供事务保证,确保状态一致性
* <p>
* 实现说明:
* - 所有状态变更方法都在事务中执行
* - 状态变更后发布领域事件,由业务方订阅处理
* - 确保工单状态与队列状态的一致性
*
* @author lzh
*/
@Slf4j
@Service
public class OrderLifecycleManagerImpl implements OrderLifecycleManager {
@Resource
private OrderStateMachine orderStateMachine;
@Resource
private OrderQueueService orderQueueService;
@Resource
private OpsOrderMapper opsOrderMapper;
@Resource
private OrderEventPublisher eventPublisher;
// ==================== 暂停/恢复 ====================
@Override
@Transactional(rollbackFor = Exception.class)
public void pauseOrder(Long orderId, Long operatorId, String reason) {
log.info("开始暂停工单: orderId={}, operatorId={}, reason={}", orderId, operatorId, reason);
// 1. 查询工单和队列
OpsOrderDO order = getOrderByOrderId(orderId);
OrderQueueDTO queueDTO = orderQueueService.getByOpsOrderId(orderId);
// 2. 验证状态
validateCanPause(order, queueDTO);
WorkOrderStatusEnum oldStatus = WorkOrderStatusEnum.valueOf(order.getStatus());
// 3. 同步转换状态
// 工单状态ARRIVED → PAUSED
orderStateMachine.transition(order, WorkOrderStatusEnum.PAUSED,
OperatorTypeEnum.CLEANER, operatorId, reason);
// 队列状态PROCESSING → PAUSED
if (queueDTO != null) {
orderQueueService.pauseTask(queueDTO.getId());
log.info("队列状态已更新为PAUSED: queueId={}, orderId={}", queueDTO.getId(), orderId);
}
// 4. 发布事件
publishStateChangedEvent(order, oldStatus, WorkOrderStatusEnum.PAUSED,
OperatorTypeEnum.CLEANER, operatorId, reason);
log.info("工单暂停完成: orderId={}, oldStatus={}, newStatus=PAUSED", orderId, oldStatus);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void resumeOrder(Long orderId, Long operatorId) {
log.info("开始恢复工单: orderId={}, operatorId={}", orderId, operatorId);
// 1. 查询工单和队列
OpsOrderDO order = getOrderByOrderId(orderId);
OrderQueueDTO queueDTO = orderQueueService.getByOpsOrderId(orderId);
// 2. 验证状态
validateCanResume(order, queueDTO);
WorkOrderStatusEnum oldStatus = WorkOrderStatusEnum.valueOf(order.getStatus());
// 3. 同步转换状态
// 工单状态PAUSED → DISPATCHED
orderStateMachine.transition(order, WorkOrderStatusEnum.DISPATCHED,
OperatorTypeEnum.CLEANER, operatorId, "恢复工单");
// 队列状态PAUSED → PROCESSING
if (queueDTO != null) {
orderQueueService.resumeTask(queueDTO.getId());
log.info("队列状态已更新为PROCESSING: queueId={}, orderId={}", queueDTO.getId(), orderId);
}
// 4. 发布事件
publishStateChangedEvent(order, oldStatus, WorkOrderStatusEnum.DISPATCHED,
OperatorTypeEnum.CLEANER, operatorId, "恢复工单");
log.info("工单恢复完成: orderId={}, oldStatus={}, newStatus=DISPATCHED", orderId, oldStatus);
}
// ==================== 打断/恢复 ====================
@Override
@Transactional(rollbackFor = Exception.class)
public void interruptOrder(Long orderId, Long urgentOrderId, Long operatorId) {
log.warn("开始打断工单: orderId={}, urgentOrderId={}, operatorId={}",
orderId, urgentOrderId, operatorId);
// 1. 查询工单和队列
OpsOrderDO order = getOrderByOrderId(orderId);
OrderQueueDTO queueDTO = orderQueueService.getByOpsOrderId(orderId);
// 2. 验证状态(只有 DISPATCHED/CONFIRMED/ARRIVED 可以被打断)
WorkOrderStatusEnum currentStatus = WorkOrderStatusEnum.valueOf(order.getStatus());
if (!canInterruptStatus(currentStatus)) {
throw new IllegalStateException("工单状态不允许被打断: orderId=" + orderId + ", status=" + currentStatus);
}
// 3. 同步转换状态
// 工单状态DISPATCHED/CONFIRMED/ARRIVED → PAUSED
orderStateMachine.transition(order, WorkOrderStatusEnum.PAUSED,
OperatorTypeEnum.SYSTEM, operatorId, "被P0任务打断: " + urgentOrderId);
if (queueDTO != null) {
orderQueueService.pauseTask(queueDTO.getId());
orderQueueService.updateEventMessage(queueDTO.getId(),
"被P0任务打断紧急工单ID: " + urgentOrderId);
log.info("队列状态已更新为PAUSED: queueId={}, orderId={}", queueDTO.getId(), orderId);
}
// 4. 发布事件(带打断信息)
OrderStateChangedEvent event = OrderStateChangedEvent.builder()
.orderId(orderId)
.orderType(order.getOrderType())
.oldStatus(currentStatus)
.newStatus(WorkOrderStatusEnum.PAUSED)
.operatorType(OperatorTypeEnum.SYSTEM)
.operatorId(operatorId)
.remark("被P0任务打断: " + urgentOrderId)
.build();
event.addPayload("urgentOrderId", urgentOrderId);
event.addPayload("interruptReason", "P0_TASK_INTERRUPT");
eventPublisher.publishStateChanged(event);
log.warn("工单打断完成: orderId={}, urgentOrderId={}, oldStatus={}, newStatus=PAUSED",
orderId, urgentOrderId, currentStatus);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void resumeInterruptedOrder(Long orderId, Long operatorId) {
log.info("开始恢复被打断的工单: orderId={}, operatorId={}", orderId, operatorId);
// 复用恢复方法
resumeOrder(orderId, operatorId);
log.info("被打断的工单已恢复: orderId={}", orderId);
}
// ==================== 完工/取消 ====================
@Override
@Transactional(rollbackFor = Exception.class)
public void completeOrder(Long orderId, Long operatorId, String remark) {
log.info("开始完成工单: orderId={}, operatorId={}, remark={}", orderId, operatorId, remark);
// 1. 查询工单和队列
OpsOrderDO order = getOrderByOrderId(orderId);
OrderQueueDTO queueDTO = orderQueueService.getByOpsOrderId(orderId);
WorkOrderStatusEnum oldStatus = WorkOrderStatusEnum.valueOf(order.getStatus());
// 2. 验证状态
if (!canCompleteStatus(oldStatus)) {
throw new IllegalStateException("工单状态不允许完成: orderId=" + orderId + ", status=" + oldStatus);
}
// 3. 转换工单状态
orderStateMachine.transition(order, WorkOrderStatusEnum.COMPLETED,
OperatorTypeEnum.CLEANER, operatorId, remark);
// 4. 清理队列记录
if (queueDTO != null) {
orderQueueService.updateStatus(queueDTO.getId(),
com.viewsh.module.ops.enums.OrderQueueStatusEnum.REMOVED);
log.info("队列记录已移除: queueId={}, orderId={}", queueDTO.getId(), orderId);
}
// 5. 发布事件
publishStateChangedEvent(order, oldStatus, WorkOrderStatusEnum.COMPLETED,
OperatorTypeEnum.CLEANER, operatorId, remark);
log.info("工单完成: orderId={}, oldStatus={}, newStatus=COMPLETED", orderId, oldStatus);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelOrder(Long orderId, Long operatorId, OperatorTypeEnum operatorType, String reason) {
log.info("开始取消工单: orderId={}, operatorId={}, operatorType={}, reason={}",
orderId, operatorId, operatorType, reason);
// 1. 查询工单和队列
OpsOrderDO order = getOrderByOrderId(orderId);
OrderQueueDTO queueDTO = orderQueueService.getByOpsOrderId(orderId);
WorkOrderStatusEnum oldStatus = WorkOrderStatusEnum.valueOf(order.getStatus());
// 2. 验证状态(已完成的工单不能取消)
if (WorkOrderStatusEnum.COMPLETED == oldStatus) {
throw new IllegalStateException("已完成的工单不能取消: orderId=" + orderId);
}
// 3. 转换工单状态
orderStateMachine.transition(order, WorkOrderStatusEnum.CANCELLED,
operatorType, operatorId, reason);
// 4. 清理队列记录
if (queueDTO != null) {
orderQueueService.updateStatus(queueDTO.getId(),
com.viewsh.module.ops.enums.OrderQueueStatusEnum.REMOVED);
log.info("队列记录已移除: queueId={}, orderId={}", queueDTO.getId(), orderId);
}
// 5. 发布事件
publishStateChangedEvent(order, oldStatus, WorkOrderStatusEnum.CANCELLED,
operatorType, operatorId, reason);
log.info("工单已取消: orderId={}, oldStatus={}, newStatus=CANCELLED", orderId, oldStatus);
}
// ==================== 查询方法 ====================
@Override
public boolean canPause(Long orderId) {
OpsOrderDO order = opsOrderMapper.selectById(orderId);
if (order == null) {
return false;
}
WorkOrderStatusEnum status = WorkOrderStatusEnum.valueOf(order.getStatus());
return canPauseStatus(status);
}
@Override
public boolean canResume(Long orderId) {
OpsOrderDO order = opsOrderMapper.selectById(orderId);
if (order == null) {
return false;
}
WorkOrderStatusEnum status = WorkOrderStatusEnum.valueOf(order.getStatus());
return WorkOrderStatusEnum.PAUSED == status;
}
@Override
public boolean canInterrupt(Long orderId, PriorityEnum urgentPriority) {
OpsOrderDO order = opsOrderMapper.selectById(orderId);
if (order == null) {
return false;
}
WorkOrderStatusEnum status = WorkOrderStatusEnum.valueOf(order.getStatus());
return canInterruptStatus(status);
}
// ==================== 私有方法 ====================
/**
* 获取工单对象
*/
private OpsOrderDO getOrderByOrderId(Long orderId) {
OpsOrderDO order = opsOrderMapper.selectById(orderId);
if (order == null) {
throw new IllegalArgumentException("工单不存在: orderId=" + orderId);
}
return order;
}
/**
* 验证是否可以暂停
*/
private void validateCanPause(OpsOrderDO order, OrderQueueDTO queueDTO) {
WorkOrderStatusEnum status = WorkOrderStatusEnum.valueOf(order.getStatus());
if (!canPauseStatus(status)) {
throw new IllegalStateException("工单状态不允许暂停: orderId=" + order.getId() + ", status=" + status);
}
if (queueDTO != null) {
String queueStatus = queueDTO.getQueueStatus();
if (!com.viewsh.module.ops.enums.OrderQueueStatusEnum.PROCESSING.getStatus().equals(queueStatus)) {
log.warn("队列状态不是PROCESSING可能已暂停: queueId={}, status={}",
queueDTO.getId(), queueStatus);
}
}
}
/**
* 验证是否可以恢复
*/
private void validateCanResume(OpsOrderDO order, OrderQueueDTO queueDTO) {
WorkOrderStatusEnum status = WorkOrderStatusEnum.valueOf(order.getStatus());
if (WorkOrderStatusEnum.PAUSED != status) {
throw new IllegalStateException("工单状态不允许恢复: orderId=" + order.getId() + ", status=" + status);
}
if (queueDTO != null) {
String queueStatus = queueDTO.getQueueStatus();
if (!com.viewsh.module.ops.enums.OrderQueueStatusEnum.PAUSED.getStatus().equals(queueStatus)) {
log.warn("队列状态不是PAUSED: queueId={}, status={}", queueDTO.getId(), queueStatus);
}
}
}
/**
* 判断状态是否可以暂停
*/
private boolean canPauseStatus(WorkOrderStatusEnum status) {
return WorkOrderStatusEnum.ARRIVED == status;
}
/**
* 判断状态是否可以打断
*/
private boolean canInterruptStatus(WorkOrderStatusEnum status) {
return WorkOrderStatusEnum.DISPATCHED == status
|| WorkOrderStatusEnum.CONFIRMED == status
|| WorkOrderStatusEnum.ARRIVED == status;
}
/**
* 判断状态是否可以完成
*/
private boolean canCompleteStatus(WorkOrderStatusEnum status) {
return WorkOrderStatusEnum.ARRIVED == status
|| WorkOrderStatusEnum.PAUSED == status;
}
/**
* 发布状态变更事件
*/
private void publishStateChangedEvent(OpsOrderDO order,
WorkOrderStatusEnum oldStatus,
WorkOrderStatusEnum newStatus,
OperatorTypeEnum operatorType,
Long operatorId,
String remark) {
OrderStateChangedEvent event = OrderStateChangedEvent.builder()
.orderId(order.getId())
.orderType(order.getOrderType())
.oldStatus(oldStatus)
.newStatus(newStatus)
.operatorType(operatorType)
.operatorId(operatorId)
.remark(remark)
.build();
eventPublisher.publishStateChanged(event);
}
}