From 08af90e732c6cea05942db1f846ef3a43b673fb4 Mon Sep 17 00:00:00 2001 From: lzh Date: Fri, 9 Jan 2026 16:46:16 +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=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=99=A8=E9=87=8D=E6=96=B0=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/lifecycle/OrderLifecycleManager.java | 56 ++- .../lifecycle/OrderLifecycleManagerImpl.java | 351 ++++++++---------- .../handler/EventPublishHandler.java | 89 +++++ .../lifecycle/handler/QueueSyncHandler.java | 158 ++++++++ .../handler/StateTransitionHandler.java | 59 +++ .../lifecycle/handler/TransitionHandler.java | 79 ++++ .../model/OrderTransitionRequest.java | 148 ++++++++ .../model/OrderTransitionResult.java | 97 +++++ .../lifecycle/model/TransitionContext.java | 102 +++++ 9 files changed, 942 insertions(+), 197 deletions(-) create mode 100644 viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/EventPublishHandler.java create mode 100644 viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/QueueSyncHandler.java create mode 100644 viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/StateTransitionHandler.java create mode 100644 viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/TransitionHandler.java create mode 100644 viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/model/OrderTransitionRequest.java create mode 100644 viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/model/OrderTransitionResult.java create mode 100644 viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/model/TransitionContext.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 index 81a221d..7a8b45f 100644 --- 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 @@ -1,7 +1,10 @@ package com.viewsh.module.ops.core.lifecycle; +import com.viewsh.module.ops.core.lifecycle.model.OrderTransitionRequest; +import com.viewsh.module.ops.core.lifecycle.model.OrderTransitionResult; import com.viewsh.module.ops.enums.OperatorTypeEnum; import com.viewsh.module.ops.enums.PriorityEnum; +import com.viewsh.module.ops.enums.WorkOrderStatusEnum; /** * 工单生命周期管理器接口 @@ -12,14 +15,60 @@ import com.viewsh.module.ops.enums.PriorityEnum; * 3. 提供事务保证,确保状态一致性 *

* 设计原则: - * - 单一职责:只负责生命周期状态管理 + * - 单一职责:只负责工单和队列状态管理 + * - 业务特定状态(如保洁员状态)通过事件监听实现 * - 事务保证:所有状态变更在同一事务中完成 - * - 扩展性:通过事件发布机制解耦业务逻辑 + * - 事件驱动:通过事件发布解耦业务逻辑 * * @author lzh */ public interface OrderLifecycleManager { + // ==================== 通用状态转换方法 ==================== + + /** + * 执行状态转换(核心方法) + *

+ * 使用责任链模式处理: + *

    + *
  1. 状态转换处理器 - 工单状态变更
  2. + *
  3. 队列同步处理器 - 队列状态同步
  4. + *
  5. 事件发布处理器 - 发布领域事件
  6. + *
+ *

+ * 业务特定状态(如保洁员状态)由事件监听器处理 + * + * @param request 状态转换请求 + * @return 转换结果 + */ + OrderTransitionResult transition(OrderTransitionRequest request); + + // ==================== 入队/出队 ==================== + + /** + * 工单入队:PENDING → QUEUED + *

+ * 状态转换: + * - 工单状态:PENDING → QUEUED + * - 队列:创建记录,状态 WAITING + * + * @param request 状态转换请求 + * @return 转换结果 + */ + OrderTransitionResult enqueue(OrderTransitionRequest request); + + /** + * 工单出队并派单:QUEUED → DISPATCHED + *

+ * 状态转换: + * - 工单状态:QUEUED → DISPATCHED + * - 队列状态:WAITING → PROCESSING + * + * @param request 状态转换请求 + * @return 转换结果 + */ + OrderTransitionResult dispatch(OrderTransitionRequest request); + // ==================== 暂停/恢复 ==================== /** @@ -32,7 +81,6 @@ public interface OrderLifecycleManager { * @param orderId 工单ID * @param operatorId 操作人ID * @param reason 暂停原因 - * @throws IllegalStateException 如果工单状态不允许暂停 */ void pauseOrder(Long orderId, Long operatorId, String reason); @@ -45,7 +93,6 @@ public interface OrderLifecycleManager { * * @param orderId 工单ID * @param operatorId 操作人ID - * @throws IllegalStateException 如果工单状态不允许恢复 */ void resumeOrder(Long orderId, Long operatorId); @@ -63,7 +110,6 @@ public interface OrderLifecycleManager { * @param orderId 被打断的工单ID * @param urgentOrderId 紧急工单ID(P0任务) * @param operatorId 操作人ID - * @throws IllegalStateException 如果工单状态不允许被打断 */ void interruptOrder(Long orderId, Long urgentOrderId, Long operatorId); 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 index d7a5414..d6c349a 100644 --- 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 @@ -4,12 +4,20 @@ 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.core.lifecycle.handler.EventPublishHandler; +import com.viewsh.module.ops.core.lifecycle.handler.QueueSyncHandler; +import com.viewsh.module.ops.core.lifecycle.handler.StateTransitionHandler; +import com.viewsh.module.ops.core.lifecycle.handler.TransitionHandler; +import com.viewsh.module.ops.core.lifecycle.model.OrderTransitionRequest; +import com.viewsh.module.ops.core.lifecycle.model.OrderTransitionResult; +import com.viewsh.module.ops.core.lifecycle.model.TransitionContext; 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.PostConstruct; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -26,9 +34,10 @@ import java.util.Objects; * 3. 提供事务保证,确保状态一致性 *

* 实现说明: + * - 使用责任链模式处理状态转换的各个关注点 * - 所有状态变更方法都在事务中执行 * - 状态变更后发布领域事件,由业务方订阅处理 - * - 确保工单状态与队列状态的一致性 + * - 业务特定状态(如保洁员状态)由事件监听器处理 * * @author lzh */ @@ -48,6 +57,96 @@ public class OrderLifecycleManagerImpl implements OrderLifecycleManager { @Resource private OrderEventPublisher eventPublisher; + @Resource + private StateTransitionHandler stateTransitionHandler; + + @Resource + private QueueSyncHandler queueSyncHandler; + + @Resource + private EventPublishHandler eventPublishHandler; + + /** + * 责任链处理器 + */ + private TransitionHandler responsibilityChain; + + @PostConstruct + public void init() { + // 构建责任链 + responsibilityChain = stateTransitionHandler + .setNext(queueSyncHandler) + .setNext(eventPublishHandler); + + log.info("生命周期管理器责任链已构建: StateTransitionHandler -> QueueSyncHandler -> EventPublishHandler"); + } + + // ==================== 通用状态转换方法 ==================== + + @Override + @Transactional(rollbackFor = Exception.class) + public OrderTransitionResult transition(OrderTransitionRequest request) { + log.info("开始状态转换: orderId={}, targetStatus={}", request.getOrderId(), request.getTargetStatus()); + + // 1. 查询工单 + OpsOrderDO order = getOrderByOrderId(request.getOrderId()); + WorkOrderStatusEnum oldStatus = WorkOrderStatusEnum.valueOf(order.getStatus()); + + // 2. 构建转换上下文 + TransitionContext context = TransitionContext.builder() + .order(order) + .request(request) + .oldStatus(oldStatus) + .build(); + + // 3. 执行责任链 + responsibilityChain.handle(context); + + // 4. 检查结果 + if (context.hasError()) { + log.error("状态转换失败: orderId={}, error={}", order.getId(), context.getErrorMessage()); + return OrderTransitionResult.fail(order.getId(), context.getErrorMessage()); + } + + log.info("状态转换成功: orderId={}, {} -> {}", order.getId(), oldStatus, request.getTargetStatus()); + return OrderTransitionResult.builder() + .success(true) + .orderId(order.getId()) + .oldStatus(oldStatus) + .newStatus(request.getTargetStatus()) + .queueId(context.getQueueId()) + .build(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public OrderTransitionResult enqueue(OrderTransitionRequest request) { + log.info("工单入队: orderId={}, assigneeId={}", request.getOrderId(), request.getAssigneeId()); + + // 设置目标状态 + request.setTargetStatus(WorkOrderStatusEnum.QUEUED); + + // 先更新工单的执行人 + OpsOrderDO order = getOrderByOrderId(request.getOrderId()); + order.setAssigneeId(request.getAssigneeId()); + opsOrderMapper.updateById(order); + + // 执行状态转换 + return transition(request); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public OrderTransitionResult dispatch(OrderTransitionRequest request) { + log.info("工单派发: orderId={}, queueId={}", request.getOrderId(), request.getQueueId()); + + // 设置目标状态 + request.setTargetStatus(WorkOrderStatusEnum.DISPATCHED); + + // 执行状态转换 + return transition(request); + } + // ==================== 暂停/恢复 ==================== @Override @@ -55,31 +154,21 @@ public class OrderLifecycleManagerImpl implements OrderLifecycleManager { 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); + // 构建请求 + OrderTransitionRequest request = OrderTransitionRequest.builder() + .orderId(orderId) + .targetStatus(WorkOrderStatusEnum.PAUSED) + .operatorType(OperatorTypeEnum.CLEANER) + .operatorId(operatorId) + .reason(reason) + .build(); - // 2. 验证状态 - validateCanPause(order, queueDTO); + // 执行状态转换 + OrderTransitionResult result = transition(request); - 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); + if (!result.isSuccess()) { + throw new IllegalStateException("暂停工单失败: " + result.getMessage()); } - - // 4. 发布事件 - publishStateChangedEvent(order, oldStatus, WorkOrderStatusEnum.PAUSED, - OperatorTypeEnum.CLEANER, operatorId, reason); - - log.info("工单暂停完成: orderId={}, oldStatus={}, newStatus=PAUSED", orderId, oldStatus); } @Override @@ -87,31 +176,21 @@ public class OrderLifecycleManagerImpl implements OrderLifecycleManager { public void resumeOrder(Long orderId, Long operatorId) { log.info("开始恢复工单: orderId={}, operatorId={}", orderId, operatorId); - // 1. 查询工单和队列 - OpsOrderDO order = getOrderByOrderId(orderId); - OrderQueueDTO queueDTO = orderQueueService.getByOpsOrderId(orderId); + // 构建请求 + OrderTransitionRequest request = OrderTransitionRequest.builder() + .orderId(orderId) + .targetStatus(WorkOrderStatusEnum.DISPATCHED) + .operatorType(OperatorTypeEnum.CLEANER) + .operatorId(operatorId) + .reason("恢复工单") + .build(); - // 2. 验证状态 - validateCanResume(order, queueDTO); + // 执行状态转换 + OrderTransitionResult result = transition(request); - 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); + if (!result.isSuccess()) { + throw new IllegalStateException("恢复工单失败: " + result.getMessage()); } - - // 4. 发布事件 - publishStateChangedEvent(order, oldStatus, WorkOrderStatusEnum.DISPATCHED, - OperatorTypeEnum.CLEANER, operatorId, "恢复工单"); - - log.info("工单恢复完成: orderId={}, oldStatus={}, newStatus=DISPATCHED", orderId, oldStatus); } // ==================== 打断/恢复 ==================== @@ -122,44 +201,22 @@ public class OrderLifecycleManagerImpl implements OrderLifecycleManager { 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() + // 构建请求 + OrderTransitionRequest request = OrderTransitionRequest.builder() .orderId(orderId) - .orderType(order.getOrderType()) - .oldStatus(currentStatus) - .newStatus(WorkOrderStatusEnum.PAUSED) + .targetStatus(WorkOrderStatusEnum.PAUSED) + .urgentOrderId(urgentOrderId) .operatorType(OperatorTypeEnum.SYSTEM) .operatorId(operatorId) - .remark("被P0任务打断: " + urgentOrderId) + .reason("被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); + // 执行状态转换 + OrderTransitionResult result = transition(request); + + if (!result.isSuccess()) { + throw new IllegalStateException("打断工单失败: " + result.getMessage()); + } } @Override @@ -180,33 +237,21 @@ public class OrderLifecycleManagerImpl implements OrderLifecycleManager { 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); + // 构建请求 + OrderTransitionRequest request = OrderTransitionRequest.builder() + .orderId(orderId) + .targetStatus(WorkOrderStatusEnum.COMPLETED) + .operatorType(OperatorTypeEnum.CLEANER) + .operatorId(operatorId) + .reason(remark) + .build(); - WorkOrderStatusEnum oldStatus = WorkOrderStatusEnum.valueOf(order.getStatus()); + // 执行状态转换 + OrderTransitionResult result = transition(request); - // 2. 验证状态 - if (!canCompleteStatus(oldStatus)) { - throw new IllegalStateException("工单状态不允许完成: orderId=" + orderId + ", status=" + oldStatus); + if (!result.isSuccess()) { + throw new IllegalStateException("完成工单失败: " + result.getMessage()); } - - // 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 @@ -215,33 +260,21 @@ public class OrderLifecycleManagerImpl implements OrderLifecycleManager { log.info("开始取消工单: orderId={}, operatorId={}, operatorType={}, reason={}", orderId, operatorId, operatorType, reason); - // 1. 查询工单和队列 - OpsOrderDO order = getOrderByOrderId(orderId); - OrderQueueDTO queueDTO = orderQueueService.getByOpsOrderId(orderId); + // 构建请求 + OrderTransitionRequest request = OrderTransitionRequest.builder() + .orderId(orderId) + .targetStatus(WorkOrderStatusEnum.CANCELLED) + .operatorType(operatorType) + .operatorId(operatorId) + .reason(reason) + .build(); - WorkOrderStatusEnum oldStatus = WorkOrderStatusEnum.valueOf(order.getStatus()); + // 执行状态转换 + OrderTransitionResult result = transition(request); - // 2. 验证状态(已完成的工单不能取消) - if (WorkOrderStatusEnum.COMPLETED == oldStatus) { - throw new IllegalStateException("已完成的工单不能取消: orderId=" + orderId); + if (!result.isSuccess()) { + throw new IllegalStateException("取消工单失败: " + result.getMessage()); } - - // 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); } // ==================== 查询方法 ==================== @@ -289,43 +322,6 @@ public class OrderLifecycleManagerImpl implements OrderLifecycleManager { 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); - } - } - } - /** * 判断状态是否可以暂停 */ @@ -341,33 +337,4 @@ public class OrderLifecycleManagerImpl implements OrderLifecycleManager { || 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); - } } diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/EventPublishHandler.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/EventPublishHandler.java new file mode 100644 index 0000000..2336914 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/EventPublishHandler.java @@ -0,0 +1,89 @@ +package com.viewsh.module.ops.core.lifecycle.handler; + +import com.viewsh.module.ops.core.event.OrderCompletedEvent; +import com.viewsh.module.ops.core.event.OrderEventPublisher; +import com.viewsh.module.ops.core.event.OrderStateChangedEvent; +import com.viewsh.module.ops.core.lifecycle.model.TransitionContext; +import com.viewsh.module.ops.core.lifecycle.model.OrderTransitionRequest; +import com.viewsh.module.ops.enums.WorkOrderStatusEnum; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 事件发布处理器 + *

+ * 责任链中的最后一个处理器,负责发布领域事件 + * + * @author lzh + */ +@Slf4j +@Component +public class EventPublishHandler extends TransitionHandler { + + @Resource + private OrderEventPublisher orderEventPublisher; + + @Override + protected void doHandle(TransitionContext context) { + OrderTransitionRequest request = context.getRequest(); + WorkOrderStatusEnum targetStatus = request.getTargetStatus(); + WorkOrderStatusEnum oldStatus = context.getOldStatus(); + + log.debug("事件发布处理器: orderId={}, {} -> {}", context.getOrder().getId(), oldStatus, targetStatus); + + try { + // 发布状态变更事件 + OrderStateChangedEvent event = OrderStateChangedEvent.builder() + .orderId(context.getOrder().getId()) + .orderCode(context.getOrder().getOrderCode()) + .orderType(context.getOrder().getOrderType()) + .oldStatus(oldStatus) + .newStatus(targetStatus) + .operatorId(request.getOperatorId()) + .operatorType(request.getOperatorType()) + .eventTime(java.time.LocalDateTime.now()) + .remark(request.getReason()) + .payload(request.getPayload()) + .build(); + + orderEventPublisher.publishStateChanged(event); + + // 如果是完成状态,额外发布完成事件 + if (targetStatus == WorkOrderStatusEnum.COMPLETED) { + OrderCompletedEvent completedEvent = OrderCompletedEvent.builder() + .orderId(context.getOrder().getId()) + .orderCode(context.getOrder().getOrderCode()) + .orderType(context.getOrder().getOrderType()) + .assigneeId(request.getAssigneeId()) + .workDuration(calculateWorkDuration(context)) + .build(); + + orderEventPublisher.publishOrderCompleted(completedEvent); + } + + log.debug("事件发布成功: orderId={}, eventType={}", context.getOrder().getId(), + targetStatus == WorkOrderStatusEnum.COMPLETED ? "COMPLETED" : "STATE_CHANGED"); + + } catch (Exception e) { + // 事件发布失败不影响主流程,只记录日志 + log.error("事件发布失败: orderId={}", context.getOrder().getId(), e); + } + } + + /** + * 计算作业时长(秒) + *

+ * 从扩展信息中获取,如果没有则返回0 + * + * @param context 转换上下文 + * @return 作业时长(秒) + */ + private Integer calculateWorkDuration(TransitionContext context) { + Object duration = context.getRequest().getPayload("workDuration"); + if (duration instanceof Integer) { + return (Integer) duration; + } + return 0; + } +} diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/QueueSyncHandler.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/QueueSyncHandler.java new file mode 100644 index 0000000..25f7892 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/QueueSyncHandler.java @@ -0,0 +1,158 @@ +package com.viewsh.module.ops.core.lifecycle.handler; + +import com.viewsh.module.ops.api.queue.OrderQueueDTO; +import com.viewsh.module.ops.api.queue.OrderQueueService; +import com.viewsh.module.ops.core.lifecycle.model.TransitionContext; +import com.viewsh.module.ops.core.lifecycle.model.OrderTransitionRequest; +import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO; +import com.viewsh.module.ops.enums.OrderQueueStatusEnum; +import com.viewsh.module.ops.enums.WorkOrderStatusEnum; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 队列同步处理器 + *

+ * 责任链中的第二个处理器,负责同步队列状态 + * + * @author lzh + */ +@Slf4j +@Component +public class QueueSyncHandler extends TransitionHandler { + + @Resource + private OrderQueueService orderQueueService; + + @Override + protected void doHandle(TransitionContext context) { + OrderTransitionRequest request = context.getRequest(); + WorkOrderStatusEnum targetStatus = request.getTargetStatus(); + Long queueId = request.getQueueId(); + + log.debug("队列同步处理器: orderId={}, targetStatus={}, queueId={}", + context.getOrder().getId(), targetStatus, queueId); + + try { + // 根据目标状态同步队列状态 + switch (targetStatus) { + case QUEUED: + handleQueued(context); + break; + + case DISPATCHED: + handleDispatched(context); + break; + + case ARRIVED: + // 队列状态保持 PROCESSING + break; + + case COMPLETED: + case CANCELLED: + handleCompletedOrCancelled(context); + break; + + case PAUSED: + handlePaused(context); + break; + + default: + log.debug("目标状态无需同步队列: targetStatus={}", targetStatus); + } + + log.debug("队列同步成功: orderId={}, targetStatus={}", context.getOrder().getId(), targetStatus); + + } catch (Exception e) { + error(context, "队列同步失败: " + e.getMessage(), e); + log.error("队列同步失败: orderId={}, targetStatus={}", + context.getOrder().getId(), targetStatus, e); + } + } + + /** + * 处理入队状态 + */ + private void handleQueued(TransitionContext context) { + OrderTransitionRequest request = context.getRequest(); + Long assigneeId = request.getAssigneeId(); + + if (assigneeId == null) { + log.warn("入队时缺少执行人ID: orderId={}", context.getOrder().getId()); + return; + } + + // 创建队列记录 + Long queueId = orderQueueService.enqueue( + context.getOrder().getId(), + assigneeId, + context.getOrder().getPriorityEnum(), + request.getQueueIndex() + ); + + context.setQueueId(queueId); + log.debug("队列记录已创建: queueId={}", queueId); + } + + /** + * 处理已派发状态 + */ + private void handleDispatched(TransitionContext context) { + OrderTransitionRequest request = context.getRequest(); + Long queueId = request.getQueueId(); + + if (queueId == null) { + log.warn("派发时缺少队列ID: orderId={}", context.getOrder().getId()); + return; + } + + // 队列状态从 WAITING 变更为 PROCESSING + orderQueueService.startExecution(queueId); + log.debug("队列状态已更新为PROCESSING: queueId={}", queueId); + } + + /** + * 处理完成或取消状态 + */ + private void handleCompletedOrCancelled(TransitionContext context) { + OrderTransitionRequest request = context.getRequest(); + Long queueId = request.getQueueId(); + + if (queueId == null) { + // 尝试通过工单ID查找队列记录 + OrderQueueDTO queueDTO = orderQueueService.getByOpsOrderId(context.getOrder().getId()); + if (queueDTO != null) { + queueId = queueDTO.getId(); + } + } + + if (queueId != null) { + // 队列状态变更为 REMOVED + orderQueueService.updateStatus(queueId, OrderQueueStatusEnum.REMOVED); + log.debug("队列状态已更新为REMOVED: queueId={}", queueId); + } + } + + /** + * 处理暂停状态 + */ + private void handlePaused(TransitionContext context) { + OrderTransitionRequest request = context.getRequest(); + Long queueId = request.getQueueId(); + + if (queueId == null) { + // 尝试通过工单ID查找队列记录 + OrderQueueDTO queueDTO = orderQueueService.getByOpsOrderId(context.getOrder().getId()); + if (queueDTO != null) { + queueId = queueDTO.getId(); + } + } + + if (queueId != null) { + // 队列状态变更为 PAUSED + orderQueueService.updateStatus(queueId, OrderQueueStatusEnum.PAUSED); + log.debug("队列状态已更新为PAUSED: queueId={}", queueId); + } + } +} diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/StateTransitionHandler.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/StateTransitionHandler.java new file mode 100644 index 0000000..b4bdaab --- /dev/null +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/StateTransitionHandler.java @@ -0,0 +1,59 @@ +package com.viewsh.module.ops.core.lifecycle.handler; + +import com.viewsh.module.ops.core.lifecycle.model.TransitionContext; +import com.viewsh.module.ops.core.lifecycle.model.OrderTransitionRequest; +import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO; +import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper; +import com.viewsh.module.ops.service.fsm.OrderStateMachine; +import com.viewsh.module.ops.enums.OperatorTypeEnum; +import com.viewsh.module.ops.enums.WorkOrderStatusEnum; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 状态转换处理器 + *

+ * 责任链中的第一个处理器,负责工单状态转换 + * + * @author lzh + */ +@Slf4j +@Component +public class StateTransitionHandler extends TransitionHandler { + + @Resource + private OrderStateMachine orderStateMachine; + + @Resource + private OpsOrderMapper orderMapper; + + @Override + protected void doHandle(TransitionContext context) { + OrderTransitionRequest request = context.getRequest(); + OpsOrderDO order = context.getOrder(); + WorkOrderStatusEnum targetStatus = request.getTargetStatus(); + + log.debug("状态转换处理器: orderId={}, targetStatus={}", order.getId(), targetStatus); + + // 通过状态机执行状态转换 + try { + orderStateMachine.transition( + order, + targetStatus, + request.getOperatorType() != null ? request.getOperatorType() : OperatorTypeEnum.SYSTEM, + request.getOperatorId(), + request.getReason() + ); + + // 更新上下文中的状态 + context.setNewStatus(targetStatus); + + log.debug("状态转换成功: orderId={}, newStatus={}", order.getId(), targetStatus); + + } catch (Exception e) { + error(context, "状态转换失败: " + e.getMessage(), e); + log.error("状态转换失败: orderId={}, targetStatus={}", order.getId(), targetStatus, e); + } + } +} diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/TransitionHandler.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/TransitionHandler.java new file mode 100644 index 0000000..d648844 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/handler/TransitionHandler.java @@ -0,0 +1,79 @@ +package com.viewsh.module.ops.core.lifecycle.handler; + +import com.viewsh.module.ops.core.lifecycle.model.TransitionContext; +import com.viewsh.module.ops.enums.WorkOrderStatusEnum; + +/** + * 状态转换处理器基类 + *

+ * 责任链模式中的处理器基类,用于处理状态转换的各个关注点 + * + * @author lzh + */ +public abstract class TransitionHandler { + + /** + * 下一个处理器 + */ + protected TransitionHandler next; + + /** + * 设置���一个处理器 + * + * @param next 下一个处理器 + * @return this + */ + public TransitionHandler setNext(TransitionHandler next) { + this.next = next; + return this; + } + + /** + * 处理状态转换 + * + * @param context 转换上下文 + */ + public void handle(TransitionContext context) { + // 执行当前处理器的逻辑 + doHandle(context); + + // 如果有错误,停止处理 + if (context.hasError()) { + return; + } + + // 执行下一个处理器 + if (next != null) { + next.handle(context); + } + } + + /** + * 执行处理逻辑(由子类实现) + * + * @param context 转换上下文 + */ + protected abstract void doHandle(TransitionContext context); + + /** + * 记录错误 + * + * @param context 转换上下文 + * @param message 错误信息 + */ + protected void error(TransitionContext context, String message) { + context.setError(message); + } + + /** + * 记录错误 + * + * @param context 转换上下文 + * @param message 错误信息 + * @param cause 异常原因 + */ + protected void error(TransitionContext context, String message, Throwable cause) { + context.setError(message); + context.setCause(cause); + } +} diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/model/OrderTransitionRequest.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/model/OrderTransitionRequest.java new file mode 100644 index 0000000..c3bec36 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/model/OrderTransitionRequest.java @@ -0,0 +1,148 @@ +package com.viewsh.module.ops.core.lifecycle.model; + +import com.viewsh.module.ops.enums.OperatorTypeEnum; +import com.viewsh.module.ops.enums.WorkOrderStatusEnum; +import com.viewsh.module.ops.enums.PriorityEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +/** + * 状态转换请求 + *

+ * 生命周期管理器的通用请求模型 + * + * @author lzh + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OrderTransitionRequest { + + /** + * 工单ID(必填) + */ + private Long orderId; + + /** + * 目标状态(必填) + */ + private WorkOrderStatusEnum targetStatus; + + /** + * 执行人ID(可选) + *

+ * 当需要分配给执行人时填写 + */ + private Long assigneeId; + + /** + * 操作人类型(可选) + */ + private OperatorTypeEnum operatorType; + + /** + * 操作人ID(可选) + */ + private Long operatorId; + + /** + * 转换原因(可选) + */ + private String reason; + + /** + * 队列ID(可选) + *

+ * 当工单已入队时填写 + */ + private Long queueId; + + /** + * 队列索引(可选) + *

+ * 入队时的排序位置 + */ + private Integer queueIndex; + + /** + * 紧急工单ID(可选) + *

+ * 当是打断场景时,填写紧急工单ID + */ + private Long urgentOrderId; + + /** + * 工单优先级(可选) + */ + private PriorityEnum priority; + + /** + * 扩展信息(可选) + *

+ * 用于支持业务特定的扩展信息 + *

+ * 注意:通用层不处理这些扩展信息,由业务层在监听器中处理 + */ + private Map payload; + + /** + * 获取扩展信息 + * + * @param key 键 + * @return 值 + */ + public Object getPayload(String key) { + return payload != null ? payload.get(key) : null; + } + + /** + * 设置扩展信息 + * + * @param key 键 + * @param value 值 + */ + public void putPayload(String key, Object value) { + if (payload == null) { + payload = new java.util.HashMap<>(); + } + payload.put(key, value); + } + + /** + * 获取扩展信息(String) + */ + public String getPayloadString(String key) { + Object value = getPayload(key); + return value != null ? value.toString() : null; + } + + /** + * 获取扩展信息(Long) + */ + public Long getPayloadLong(String key) { + Object value = getPayload(key); + if (value instanceof Long) { + return (Long) value; + } + if (value instanceof Integer) { + return ((Integer) value).longValue(); + } + return null; + } + + /** + * 获取扩展信息(Integer) + */ + public Integer getPayloadInteger(String key) { + Object value = getPayload(key); + if (value instanceof Integer) { + return (Integer) value; + } + return null; + } +} diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/model/OrderTransitionResult.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/model/OrderTransitionResult.java new file mode 100644 index 0000000..e32a9c9 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/model/OrderTransitionResult.java @@ -0,0 +1,97 @@ +package com.viewsh.module.ops.core.lifecycle.model; + +import com.viewsh.module.ops.enums.WorkOrderStatusEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 状态转换结果 + * + * @author lzh + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OrderTransitionResult { + + /** + * 是否成功 + */ + private boolean success; + + /** + * 工单ID + */ + private Long orderId; + + /** + * 原状态 + */ + private WorkOrderStatusEnum oldStatus; + + /** + * 新状态 + */ + private WorkOrderStatusEnum newStatus; + + /** + * 结果消息 + */ + private String message; + + /** + * 队列ID(如果已创建或更新) + */ + private Long queueId; + + /** + * 成功结果 + */ + public static OrderTransitionResult success(Long orderId, WorkOrderStatusEnum oldStatus, + WorkOrderStatusEnum newStatus) { + return OrderTransitionResult.builder() + .success(true) + .orderId(orderId) + .oldStatus(oldStatus) + .newStatus(newStatus) + .build(); + } + + /** + * 成功结果(带队列ID) + */ + public static OrderTransitionResult success(Long orderId, WorkOrderStatusEnum oldStatus, + WorkOrderStatusEnum newStatus, Long queueId) { + return OrderTransitionResult.builder() + .success(true) + .orderId(orderId) + .oldStatus(oldStatus) + .newStatus(newStatus) + .queueId(queueId) + .build(); + } + + /** + * 失败结果 + */ + public static OrderTransitionResult fail(String message) { + return OrderTransitionResult.builder() + .success(false) + .message(message) + .build(); + } + + /** + * 失败结果(带工单ID) + */ + public static OrderTransitionResult fail(Long orderId, String message) { + return OrderTransitionResult.builder() + .success(false) + .orderId(orderId) + .message(message) + .build(); + } +} diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/model/TransitionContext.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/model/TransitionContext.java new file mode 100644 index 0000000..4e2b20c --- /dev/null +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/lifecycle/model/TransitionContext.java @@ -0,0 +1,102 @@ +package com.viewsh.module.ops.core.lifecycle.model; + +import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO; +import com.viewsh.module.ops.enums.WorkOrderStatusEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 状态转换上下文 + *

+ * 责任链处理器之间传递的上下文信息 + * + * @author lzh + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TransitionContext { + + /** + * 工单对象 + */ + private OpsOrderDO order; + + /** + * 转换请求 + */ + private OrderTransitionRequest request; + + /** + * 原状态 + */ + private WorkOrderStatusEnum oldStatus; + + /** + * 新状态 + */ + private WorkOrderStatusEnum newStatus; + + /** + * 队列ID + *

+ * 由处理器在执行过程中设置 + */ + private Long queueId; + + /** + * 错误信息 + */ + private String errorMessage; + + /** + * 异常原因 + */ + private Throwable cause; + + /** + * 是否有错误 + */ + public boolean hasError() { + return errorMessage != null; + } + + /** + * 获取工单ID + */ + public Long getOrderId() { + return order != null ? order.getId() : null; + } + + /** + * 设置错误 + * + * @param message 错误信息 + */ + public void setError(String message) { + this.errorMessage = message; + } + + /** + * 设置错误 + * + * @param message 错误信息 + * @param cause 异常原因 + */ + public void setError(String message, Throwable cause) { + this.errorMessage = message; + this.cause = cause; + } + + /** + * 设置原因 + * + * @param cause 异常原因 + */ + public void setCause(Throwable cause) { + this.cause = cause; + } +}