chore: 【ops】工单生命周期管理器重新调整

This commit is contained in:
lzh
2026-01-09 16:46:16 +08:00
parent 8feb778291
commit 08af90e732
9 changed files with 942 additions and 197 deletions

View File

@@ -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. 提供事务保证,确保状态一致性
* <p>
* 设计原则:
* - 单一职责:只负责生命周期状态管理
* - 单一职责:只负责工单和队列状态管理
* - 业务特定状态(如保洁员状态)通过事件监听实现
* - 事务保证:所有状态变更在同一事务中完成
* - 扩展性:通过事件发布机制解耦业务逻辑
* - 事件驱动:通过事件发布解耦业务逻辑
*
* @author lzh
*/
public interface OrderLifecycleManager {
// ==================== 通用状态转换方法 ====================
/**
* 执行状态转换(核心方法)
* <p>
* 使用责任链模式处理:
* <ol>
* <li>状态转换处理器 - 工单状态变更</li>
* <li>队列同步处理器 - 队列状态同步</li>
* <li>事件发布处理器 - 发布领域事件</li>
* </ol>
* <p>
* 业务特定状态(如保洁员状态)由事件监听器处理
*
* @param request 状态转换请求
* @return 转换结果
*/
OrderTransitionResult transition(OrderTransitionRequest request);
// ==================== 入队/出队 ====================
/**
* 工单入队PENDING → QUEUED
* <p>
* 状态转换:
* - 工单状态PENDING → QUEUED
* - 队列:创建记录,状态 WAITING
*
* @param request 状态转换请求
* @return 转换结果
*/
OrderTransitionResult enqueue(OrderTransitionRequest request);
/**
* 工单出队并派单QUEUED → DISPATCHED
* <p>
* 状态转换:
* - 工单状态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 紧急工单IDP0任务
* @param operatorId 操作人ID
* @throws IllegalStateException 如果工单状态不允许被打断
*/
void interruptOrder(Long orderId, Long urgentOrderId, Long operatorId);

View File

@@ -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. 提供事务保证,确保状态一致性
* <p>
* 实现说明:
* - 使用责任链模式处理状态转换的各个关注点
* - 所有状态变更方法都在事务中执行
* - 状态变更后发布领域事件,由业务方订阅处理
* - 确保工单状态与队列状态的一致性
* - 业务特定状态(如保洁员状态)由事件监听器处理
*
* @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);
}
}

View File

@@ -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;
/**
* 事件发布处理器
* <p>
* 责任链中的最后一个处理器,负责发布领域事件
*
* @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);
}
}
/**
* 计算作业时长(秒)
* <p>
* 从扩展信息中获取如果没有则返回0
*
* @param context 转换上下文
* @return 作业时长(秒)
*/
private Integer calculateWorkDuration(TransitionContext context) {
Object duration = context.getRequest().getPayload("workDuration");
if (duration instanceof Integer) {
return (Integer) duration;
}
return 0;
}
}

View File

@@ -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;
/**
* 队列同步处理器
* <p>
* 责任链中的第二个处理器,负责同步队列状态
*
* @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);
}
}
}

View File

@@ -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;
/**
* 状态转换处理器
* <p>
* 责任链中的第一个处理器,负责工单状态转换
*
* @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);
}
}
}

View File

@@ -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;
/**
* 状态转换处理器基类
* <p>
* 责任链模式中的处理器基类,用于处理状态转换的各个关注点
*
* @author lzh
*/
public abstract class TransitionHandler {
/**
* 下一个处理器
*/
protected TransitionHandler next;
/**
* 设置<E8AEBE><E7BDAE><EFBFBD>一个处理器
*
* @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);
}
}

View File

@@ -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;
/**
* 状态转换请求
* <p>
* 生命周期管理器的通用请求模型
*
* @author lzh
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderTransitionRequest {
/**
* 工单ID必填
*/
private Long orderId;
/**
* 目标状态(必填)
*/
private WorkOrderStatusEnum targetStatus;
/**
* 执行人ID可选
* <p>
* 当需要分配给执行人时填写
*/
private Long assigneeId;
/**
* 操作人类型(可选)
*/
private OperatorTypeEnum operatorType;
/**
* 操作人ID可选
*/
private Long operatorId;
/**
* 转换原因(可选)
*/
private String reason;
/**
* 队列ID可选
* <p>
* 当工单已入队时填写
*/
private Long queueId;
/**
* 队列索引(可选)
* <p>
* 入队时的排序位置
*/
private Integer queueIndex;
/**
* 紧急工单ID可选
* <p>
* 当是打断场景时填写紧急工单ID
*/
private Long urgentOrderId;
/**
* 工单优先级(可选)
*/
private PriorityEnum priority;
/**
* 扩展信息(可选)
* <p>
* 用于支持业务特定的扩展信息
* <p>
* 注意:通用层不处理这些扩展信息,由业务层在监听器中处理
*/
private Map<String, Object> 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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
/**
* 状态转换上下文
* <p>
* 责任链处理器之间传递的上下文信息
*
* @author lzh
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TransitionContext {
/**
* 工单对象
*/
private OpsOrderDO order;
/**
* 转换请求
*/
private OrderTransitionRequest request;
/**
* 原状态
*/
private WorkOrderStatusEnum oldStatus;
/**
* 新状态
*/
private WorkOrderStatusEnum newStatus;
/**
* 队列ID
* <p>
* 由处理器在执行过程中设置
*/
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;
}
}