From 48e1a91fd536e40c6a34a0d88602f9f104e99155 Mon Sep 17 00:00:00 2001 From: lzh Date: Thu, 8 Jan 2026 15:06:08 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E3=80=90ops=E3=80=91=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E7=94=9F=E5=91=BD=E5=91=A8=E6=9C=9F=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=EF=BC=88=E6=9A=82=E5=81=9C=E3=80=81=E5=9B=9E=E5=A4=8D=E3=80=81?= =?UTF-8?q?=E5=AE=8C=E6=88=90=E3=80=81=E5=8F=96=E6=B6=88=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/lifecycle/OrderLifecycleManager.java | 137 +++++++ .../lifecycle/OrderLifecycleManagerImpl.java | 373 ++++++++++++++++++ 2 files changed, 510 insertions(+) create mode 100644 viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/OrderLifecycleManager.java create mode 100644 viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/OrderLifecycleManagerImpl.java diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/OrderLifecycleManager.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/OrderLifecycleManager.java new file mode 100644 index 0000000..81a221d --- /dev/null +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/OrderLifecycleManager.java @@ -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; + +/** + * 工单生命周期管理器接口 + *

+ * 职责: + * 1. 统一管理工单状态和队列状态的同步 + * 2. 处理工单的暂停/恢复/打断等复杂状态变更 + * 3. 提供事务保证,确保状态一致性 + *

+ * 设计原则: + * - 单一职责:只负责生命周期状态管理 + * - 事务保证:所有状态变更在同一事务中完成 + * - 扩展性:通过事件发布机制解耦业务逻辑 + * + * @author lzh + */ +public interface OrderLifecycleManager { + + // ==================== 暂停/恢复 ==================== + + /** + * 暂停工单(同步更新队列状态和工单状态) + *

+ * 状态转换: + * - 工单状态:ARRIVED → PAUSED + * - 队列状态:PROCESSING → PAUSED + * + * @param orderId 工单ID + * @param operatorId 操作人ID + * @param reason 暂停原因 + * @throws IllegalStateException 如果工单状态不允许暂停 + */ + void pauseOrder(Long orderId, Long operatorId, String reason); + + /** + * 恢复工单(同步更新队列状态和工单状态) + *

+ * 状态转换: + * - 工单状态:PAUSED → DISPATCHED + * - 队列状态:PAUSED → PROCESSING + * + * @param orderId 工单ID + * @param operatorId 操作人ID + * @throws IllegalStateException 如果工单状态不允许恢复 + */ + void resumeOrder(Long orderId, Long operatorId); + + // ==================== 打断/恢复 ==================== + + /** + * 打断工单(P0紧急任务场景) + *

+ * 状态转换: + * - 工单状态:DISPATCHED/CONFIRMED/ARRIVED → PAUSED + * - 队列状态:PROCESSING → PAUSED + *

+ * 使用场景:当P0紧急任务需要插队时,打断当前正在执行的任务 + * + * @param orderId 被打断的工单ID + * @param urgentOrderId 紧急工单ID(P0任务) + * @param operatorId 操作人ID + * @throws IllegalStateException 如果工单状态不允许被打断 + */ + void interruptOrder(Long orderId, Long urgentOrderId, Long operatorId); + + /** + * 恢复被打断的工单(P0任务完成后自动恢复) + *

+ * 状态转换: + * - 工单状态:PAUSED → DISPATCHED + * - 队列状态:PAUSED → PROCESSING + * + * @param orderId 被打断的工单ID + * @param operatorId 操作人ID + */ + void resumeInterruptedOrder(Long orderId, Long operatorId); + + // ==================== 完工/取消 ==================== + + /** + * 完成工单(清理队列记录) + *

+ * 状态转换: + * - 工单状态:ARRIVED → COMPLETED + * - 队列状态:PROCESSING → REMOVED + * + * @param orderId 工单ID + * @param operatorId 操作人ID + * @param remark 完成备注 + */ + void completeOrder(Long orderId, Long operatorId, String remark); + + /** + * 取消工单 + *

+ * 状态转换: + * - 工单状态:任何非终态 → 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); +} diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/OrderLifecycleManagerImpl.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/OrderLifecycleManagerImpl.java new file mode 100644 index 0000000..d7a5414 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/OrderLifecycleManagerImpl.java @@ -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; + +/** + * 工单生命周期管理器实现 + *

+ * 职责: + * 1. 统一管理工单状态和队列状态的同步 + * 2. 处理工单的暂停/恢复/打断等复杂状态变更 + * 3. 提供事务保证,确保状态一致性 + *

+ * 实现说明: + * - 所有状态变更方法都在事务中执行 + * - 状态变更后发布领域事件,由业务方订阅处理 + * - 确保工单状态与队列状态的一致性 + * + * @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); + } +}