Compare commits
3 Commits
c0c9854e73
...
93bc1f10c3
| Author | SHA1 | Date | |
|---|---|---|---|
| 93bc1f10c3 | |||
| bd70f3bc8a | |||
| 8406a80655 |
@@ -14,9 +14,6 @@ import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
|
||||
import com.viewsh.module.ops.enums.OperatorTypeEnum;
|
||||
import com.viewsh.module.ops.enums.OrderQueueStatusEnum;
|
||||
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
|
||||
import com.viewsh.module.ops.infrastructure.log.annotation.BusinessLog;
|
||||
import com.viewsh.module.ops.infrastructure.log.enumeration.LogScope;
|
||||
import com.viewsh.module.ops.infrastructure.log.enumeration.LogType;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -76,17 +73,6 @@ public class DispatchEngineImpl implements DispatchEngine {
|
||||
// ==================== 核心调度方法 ====================
|
||||
|
||||
@Override
|
||||
@BusinessLog(
|
||||
type = LogType.ORDER_DISPATCHED,
|
||||
scope = LogScope.ORDER,
|
||||
description = "工单自动派发",
|
||||
includeParams = true,
|
||||
includeResult = true,
|
||||
result = "#result.success",
|
||||
params = {"#context.orderId", "#context.businessType", "#context.priority"},
|
||||
targetId = "#context.orderId",
|
||||
targetType = "order"
|
||||
)
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public DispatchResult dispatch(OrderDispatchContext context) {
|
||||
log.info("开始调度工单: orderId={}, businessType={}, areaId={}, priority={}",
|
||||
@@ -202,33 +188,60 @@ public class DispatchEngineImpl implements DispatchEngine {
|
||||
|
||||
if (waitingTasks.isEmpty()) {
|
||||
log.info("无等待任务,执行人变为空闲: assigneeId={}", assigneeId);
|
||||
// 发布事件,由业务层更新执行人状态
|
||||
return DispatchResult.success("无等待工单,执行人保持空闲", assigneeId);
|
||||
}
|
||||
|
||||
// 动态总分重排后,派发得分最低的等待工单
|
||||
OrderQueueDTO nextTask = waitingTasks.get(0);
|
||||
log.info("派发下一单: queueId={}, orderId={}, score={}, floorDiff={}, waitMinutes={}",
|
||||
nextTask.getId(), nextTask.getOpsOrderId(), nextTask.getQueueScore(),
|
||||
nextTask.getFloorDiff(), nextTask.getWaitMinutes());
|
||||
// 遍历等待任务列表,跳过不可派发的脏数据
|
||||
int maxSkip = 50;
|
||||
int skipped = 0;
|
||||
for (OrderQueueDTO nextTask : waitingTasks) {
|
||||
// 防御性校验:检查工单实际状态是否仍为 QUEUED
|
||||
OpsOrderDO order = orderMapper.selectById(nextTask.getOpsOrderId());
|
||||
if (order == null || !WorkOrderStatusEnum.QUEUED.getStatus().equals(order.getStatus())) {
|
||||
// 工单已不在 QUEUED 状态(可能被 forceTransition 直接完成),清理脏队列记录
|
||||
log.warn("跳过不可派发的队列记录: queueId={}, orderId={}, orderStatus={}",
|
||||
nextTask.getId(), nextTask.getOpsOrderId(),
|
||||
order != null ? order.getStatus() : "NOT_FOUND");
|
||||
try {
|
||||
orderQueueService.updateStatus(nextTask.getId(), OrderQueueStatusEnum.REMOVED);
|
||||
} catch (Exception e) {
|
||||
log.error("清理脏队列记录失败: queueId={}", nextTask.getId(), e);
|
||||
}
|
||||
skipped++;
|
||||
if (skipped >= maxSkip) {
|
||||
log.error("跳过脏数据超过上限,终止: assigneeId={}, skipped={}", assigneeId, skipped);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
OrderTransitionRequest request = OrderTransitionRequest.builder()
|
||||
.orderId(nextTask.getOpsOrderId())
|
||||
.targetStatus(WorkOrderStatusEnum.DISPATCHED)
|
||||
.queueId(nextTask.getId())
|
||||
.assigneeId(assigneeId)
|
||||
.operatorType(OperatorTypeEnum.SYSTEM)
|
||||
.operatorId(assigneeId)
|
||||
.reason("等待队列动态重排后自动派发")
|
||||
.build();
|
||||
log.info("派发下一单: queueId={}, orderId={}, score={}, floorDiff={}, waitMinutes={}",
|
||||
nextTask.getId(), nextTask.getOpsOrderId(), nextTask.getQueueScore(),
|
||||
nextTask.getFloorDiff(), nextTask.getWaitMinutes());
|
||||
|
||||
OrderTransitionResult result = orderLifecycleManager.transition(request);
|
||||
OrderTransitionRequest request = OrderTransitionRequest.builder()
|
||||
.orderId(nextTask.getOpsOrderId())
|
||||
.targetStatus(WorkOrderStatusEnum.DISPATCHED)
|
||||
.queueId(nextTask.getId())
|
||||
.assigneeId(assigneeId)
|
||||
.operatorType(OperatorTypeEnum.SYSTEM)
|
||||
.operatorId(assigneeId)
|
||||
.reason("等待队列动态重排后自动派发")
|
||||
.build();
|
||||
|
||||
if (result.isSuccess()) {
|
||||
return DispatchResult.success("已按队列总分派发下一单", assigneeId);
|
||||
} else {
|
||||
return DispatchResult.fail("按队列总分派发下一单失败: " + result.getMessage());
|
||||
OrderTransitionResult result = orderLifecycleManager.transition(request);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
return DispatchResult.success("已按队列总分派发下一单", assigneeId);
|
||||
} else {
|
||||
log.warn("派发下一单失败,尝试下一条: orderId={}, error={}",
|
||||
nextTask.getOpsOrderId(), result.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 所有等待任务都不可派发
|
||||
log.info("所有等待任务均不可派发,执行人变为空闲: assigneeId={}", assigneeId);
|
||||
return DispatchResult.success("无可派发工单,执行人保持空闲", assigneeId);
|
||||
}
|
||||
|
||||
// ==================== 策略注册 ====================
|
||||
|
||||
@@ -166,6 +166,25 @@ public interface OrderLifecycleManager {
|
||||
*/
|
||||
void cancelOrder(Long orderId, Long operatorId, OperatorTypeEnum operatorType, String reason);
|
||||
|
||||
/**
|
||||
* 强制完成工单(跳过状态规则校验,走完整责任链)
|
||||
* <p>
|
||||
* 适用于系统自动结单等场景:告警自动解除时工单可能处于任何非终态
|
||||
* (QUEUED / DISPATCHED / CONFIRMED / ARRIVED / PAUSED),
|
||||
* 需要直接跳转到 COMPLETED,同时确保队列清理和事件发布。
|
||||
* <p>
|
||||
* 与直接调用 {@code OrderStateMachine.forceTransition()} 的区别:
|
||||
* 本方法走完整责任链(状态转换 → 队列同步 → 事件发布),
|
||||
* 避免队列脏数据和 Redis 状态不一致。
|
||||
*
|
||||
* @param orderId 工单ID
|
||||
* @param operatorId 操作人ID
|
||||
* @param operatorType 操作人类型
|
||||
* @param remark 完成备注
|
||||
* @return 转换结果
|
||||
*/
|
||||
OrderTransitionResult forceComplete(Long orderId, Long operatorId, OperatorTypeEnum operatorType, String remark);
|
||||
|
||||
// ==================== 查询方法 ====================
|
||||
|
||||
/**
|
||||
|
||||
@@ -346,6 +346,32 @@ public class OrderLifecycleManagerImpl implements OrderLifecycleManager {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public OrderTransitionResult forceComplete(Long orderId, Long operatorId,
|
||||
OperatorTypeEnum operatorType, String remark) {
|
||||
log.info("强制完成工单: orderId={}, operatorId={}, operatorType={}, remark={}",
|
||||
orderId, operatorId, operatorType, remark);
|
||||
|
||||
OpsOrderDO order = opsOrderMapper.selectById(orderId);
|
||||
if (order == null) {
|
||||
log.warn("强制完成工单失败,工单不存在: orderId={}", orderId);
|
||||
return OrderTransitionResult.fail(orderId, "工单不存在");
|
||||
}
|
||||
|
||||
OrderTransitionRequest request = OrderTransitionRequest.builder()
|
||||
.orderId(orderId)
|
||||
.targetStatus(WorkOrderStatusEnum.COMPLETED)
|
||||
.operatorType(operatorType != null ? operatorType : OperatorTypeEnum.SYSTEM)
|
||||
.operatorId(operatorId)
|
||||
.reason(remark)
|
||||
.assigneeId(order.getAssigneeId())
|
||||
.forced(true)
|
||||
.build();
|
||||
|
||||
return transition(request);
|
||||
}
|
||||
|
||||
// ==================== 查询方法 ====================
|
||||
|
||||
@Override
|
||||
|
||||
@@ -38,13 +38,19 @@ public class StateTransitionHandler extends TransitionHandler {
|
||||
|
||||
// 通过状态机执行状态转换
|
||||
try {
|
||||
orderStateMachine.transition(
|
||||
order,
|
||||
targetStatus,
|
||||
request.getOperatorType() != null ? request.getOperatorType() : OperatorTypeEnum.SYSTEM,
|
||||
request.getOperatorId(),
|
||||
request.getReason()
|
||||
);
|
||||
OperatorTypeEnum operatorType = request.getOperatorType() != null
|
||||
? request.getOperatorType() : OperatorTypeEnum.SYSTEM;
|
||||
|
||||
if (Boolean.TRUE.equals(request.getForced())) {
|
||||
// 强制转换:跳过状态规则校验(用于系统自动结单等场景)
|
||||
orderStateMachine.forceTransition(
|
||||
order, targetStatus, operatorType,
|
||||
request.getOperatorId(), request.getReason());
|
||||
} else {
|
||||
orderStateMachine.transition(
|
||||
order, targetStatus, operatorType,
|
||||
request.getOperatorId(), request.getReason());
|
||||
}
|
||||
|
||||
// 更新上下文中的状态
|
||||
context.setNewStatus(targetStatus);
|
||||
|
||||
@@ -91,6 +91,14 @@ public class OrderTransitionRequest {
|
||||
*/
|
||||
private PriorityEnum priority;
|
||||
|
||||
/**
|
||||
* 是否强制转换(跳过状态规则校验)
|
||||
* <p>
|
||||
* 适用于系统自动结单等场景:告警自动解除时工单可能处于任何非终态,
|
||||
* 需要直接跳转到 COMPLETED / CANCELLED,不经过中间状态。
|
||||
*/
|
||||
private Boolean forced;
|
||||
|
||||
/**
|
||||
* 扩展信息(可选)
|
||||
* <p>
|
||||
|
||||
@@ -13,15 +13,16 @@ public enum LogType {
|
||||
// ========== 注解体系(@BusinessLog)使用 ==========
|
||||
|
||||
/** 派单 */
|
||||
ORDER_DISPATCHED("ORDER_DISPATCHED", "派单"),
|
||||
ORDER_DISPATCHED("ORDER_DISPATCHED", "工单派发"),
|
||||
/** 系统事件(注解默认值) */
|
||||
SYSTEM_EVENT("SYSTEM_EVENT", "系统"),
|
||||
|
||||
// ========== 工单生命周期 ==========
|
||||
|
||||
ORDER_CREATED("ORDER_CREATED", "工单创建"),
|
||||
ORDER_QUEUED("ORDER_QUEUED", "工单入队"),
|
||||
ORDER_CONFIRM("ORDER_CONFIRM", "工单确认"),
|
||||
ORDER_ARRIVED("ORDER_ARRIVED", "到岗确认"),
|
||||
ORDER_ARRIVED("ORDER_ARRIVED", "人员到达"),
|
||||
ORDER_COMPLETED("ORDER_COMPLETED", "工单完成"),
|
||||
ORDER_INTERRUPTED("ORDER_INTERRUPTED", "工单打断"),
|
||||
ORDER_CANCELLED("ORDER_CANCELLED", "工单取消"),
|
||||
|
||||
@@ -93,14 +93,9 @@ public class SecurityOrderEventListener {
|
||||
if (result.isSuccess()) {
|
||||
log.info("安保工单自动派单完成: orderId={}, assigneeId={}, path={}",
|
||||
event.getOrderId(), result.getAssigneeId(), result.getPath());
|
||||
// 记录派单成功日志
|
||||
recordLog(EventDomain.DISPATCH, LogType.ORDER_DISPATCHED,
|
||||
"自动派单成功,分配给: " + result.getAssigneeName(),
|
||||
event.getOrderId(), result.getAssigneeId());
|
||||
// 企微卡片通知统一在 handleDispatched 中发送(状态变为 DISPATCHED 时触发)
|
||||
// 成功日志由 handleQueued / handleDispatched 状态变更监听统一记录,此处不重复
|
||||
} else {
|
||||
log.warn("安保工单自动派单失败: orderId={}, reason={}", event.getOrderId(), result.getMessage());
|
||||
// 记录派单失败日志
|
||||
recordLog(EventDomain.DISPATCH, LogType.ORDER_DISPATCHED,
|
||||
"自动派单失败: " + result.getMessage(),
|
||||
event.getOrderId(), null);
|
||||
@@ -184,7 +179,7 @@ public class SecurityOrderEventListener {
|
||||
String message = assigneeName != null
|
||||
? String.format("工单已入队,分配给 %s,等待派发", assigneeName)
|
||||
: "工单已入队等待派发";
|
||||
recordLog(EventDomain.DISPATCH, LogType.ORDER_DISPATCHED, message, orderId, assigneeId);
|
||||
recordLog(EventDomain.DISPATCH, LogType.ORDER_QUEUED, message, orderId, assigneeId);
|
||||
}
|
||||
|
||||
private void handleDispatched(Long orderId, OrderStateChangedEvent event) {
|
||||
@@ -233,7 +228,7 @@ public class SecurityOrderEventListener {
|
||||
|
||||
private void handleArrived(Long orderId, OrderStateChangedEvent event) {
|
||||
Long operatorId = event.getOperatorId();
|
||||
recordLog(EventDomain.DISPATCH, LogType.ORDER_CONFIRM, "安保人员已到达现场", orderId, operatorId);
|
||||
recordLog(EventDomain.DISPATCH, LogType.ORDER_ARRIVED, "安保人员已到达现场", orderId, operatorId);
|
||||
}
|
||||
|
||||
private void handleCompleted(Long orderId, OrderStateChangedEvent event) {
|
||||
|
||||
@@ -22,7 +22,9 @@ import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecord;
|
||||
import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecorder;
|
||||
import com.viewsh.module.ops.security.dal.dataobject.workorder.OpsOrderSecurityExtDO;
|
||||
import com.viewsh.module.ops.security.dal.mysql.workorder.OpsOrderSecurityExtMapper;
|
||||
import com.viewsh.module.ops.service.fsm.OrderStateMachine;
|
||||
import com.viewsh.module.ops.core.lifecycle.OrderLifecycleManager;
|
||||
import com.viewsh.module.ops.core.lifecycle.model.OrderTransitionRequest;
|
||||
import com.viewsh.module.ops.core.lifecycle.model.OrderTransitionResult;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -62,7 +64,7 @@ public class SecurityOrderServiceImpl implements SecurityOrderService {
|
||||
private AreaPathBuilder areaPathBuilder;
|
||||
|
||||
@Resource
|
||||
private OrderStateMachine orderStateMachine;
|
||||
private OrderLifecycleManager orderLifecycleManager;
|
||||
|
||||
@Resource
|
||||
private EventLogRecorder eventLogRecorder;
|
||||
@@ -151,9 +153,15 @@ public class SecurityOrderServiceImpl implements SecurityOrderService {
|
||||
// 如果 userId 为 null(open-api 调用),取已分配人员
|
||||
Long effectiveUserId = resolveOperatorId(orderId, userId);
|
||||
|
||||
// 状态转换:DISPATCHED → CONFIRMED(扩展表时间 + 业务日志由 EventListener 统一记录)
|
||||
orderStateMachine.transition(order, WorkOrderStatusEnum.CONFIRMED,
|
||||
OperatorTypeEnum.SECURITY_GUARD, effectiveUserId, "安保人员确认接单");
|
||||
// 状态转换:DISPATCHED → CONFIRMED(走完整责任链,确保队列同步 + 事件发布)
|
||||
OrderTransitionRequest request = OrderTransitionRequest.builder()
|
||||
.orderId(orderId)
|
||||
.targetStatus(WorkOrderStatusEnum.CONFIRMED)
|
||||
.operatorType(OperatorTypeEnum.SECURITY_GUARD)
|
||||
.operatorId(effectiveUserId)
|
||||
.reason("安保人员确认接单")
|
||||
.build();
|
||||
orderLifecycleManager.transition(request);
|
||||
|
||||
log.info("安保工单确认: orderId={}, userId={}", orderId, effectiveUserId);
|
||||
}
|
||||
@@ -172,15 +180,19 @@ public class SecurityOrderServiceImpl implements SecurityOrderService {
|
||||
log.info("安保工单已处于终态,跳过自动完单: orderId={}, status={}", orderId, currentStatus);
|
||||
return;
|
||||
}
|
||||
// 2. PENDING:未派单,直接取消(PENDING → CANCELLED 规则已支持)
|
||||
// 2. PENDING:未派单,直接取消(PENDING → CANCELLED 是合法转换)
|
||||
if (currentStatus == WorkOrderStatusEnum.PENDING) {
|
||||
orderStateMachine.transition(order, WorkOrderStatusEnum.CANCELLED,
|
||||
OperatorTypeEnum.SYSTEM, null, effectiveRemark + "(工单未派单)");
|
||||
orderLifecycleManager.cancelOrder(orderId, null, OperatorTypeEnum.SYSTEM,
|
||||
effectiveRemark + "(工单未派单)");
|
||||
}
|
||||
// 3. 其他非终态(DISPATCHED / CONFIRMED / ARRIVED / PAUSED)→ 强制跳转 COMPLETED
|
||||
// 3. 其他非终态(QUEUED / DISPATCHED / CONFIRMED / ARRIVED / PAUSED)
|
||||
// 通过 forceComplete 走完整责任链(队列清理 + 事件发布),避免脏数据残留
|
||||
else {
|
||||
orderStateMachine.forceTransition(order, WorkOrderStatusEnum.COMPLETED,
|
||||
OperatorTypeEnum.SYSTEM, null, effectiveRemark);
|
||||
OrderTransitionResult result = orderLifecycleManager.forceComplete(
|
||||
orderId, null, OperatorTypeEnum.SYSTEM, effectiveRemark);
|
||||
if (!result.isSuccess()) {
|
||||
throw new IllegalStateException("强制完成工单失败: " + result.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
log.info("安保工单自动结单: orderId={}, 原始状态={}", orderId, currentStatus);
|
||||
@@ -211,9 +223,9 @@ public class SecurityOrderServiceImpl implements SecurityOrderService {
|
||||
// 如果 operatorId 为 null(open-api 调用),取已分配人员
|
||||
Long effectiveOperatorId = resolveOperatorId(req.getOrderId(), req.getOperatorId());
|
||||
|
||||
// 状态转换 → COMPLETED(扩展表 completedTime + 业务日志由 EventListener 统一记录,主表 endTime 由状态机统一设置)
|
||||
orderStateMachine.transition(order, WorkOrderStatusEnum.COMPLETED,
|
||||
OperatorTypeEnum.SECURITY_GUARD, effectiveOperatorId, "安保人员提交处理结果");
|
||||
// 状态转换 → COMPLETED(走完整责任链,确保队列同步 + 事件发布)
|
||||
orderLifecycleManager.completeOrder(req.getOrderId(), effectiveOperatorId,
|
||||
OperatorTypeEnum.SECURITY_GUARD, "安保人员提交处理结果");
|
||||
|
||||
// 更新扩展表:结果 + 图片
|
||||
OpsOrderSecurityExtDO extUpdate = new OpsOrderSecurityExtDO();
|
||||
|
||||
@@ -13,7 +13,9 @@ import com.viewsh.module.ops.infrastructure.code.OrderCodeGenerator;
|
||||
import com.viewsh.module.ops.infrastructure.id.OrderIdGenerator;
|
||||
import com.viewsh.module.ops.security.dal.dataobject.workorder.OpsOrderSecurityExtDO;
|
||||
import com.viewsh.module.ops.security.dal.mysql.workorder.OpsOrderSecurityExtMapper;
|
||||
import com.viewsh.module.ops.service.fsm.OrderStateMachine;
|
||||
import com.viewsh.module.ops.core.lifecycle.OrderLifecycleManager;
|
||||
import com.viewsh.module.ops.core.lifecycle.model.OrderTransitionRequest;
|
||||
import com.viewsh.module.ops.core.lifecycle.model.OrderTransitionResult;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -52,7 +54,7 @@ public class SecurityOrderServiceTest {
|
||||
@Mock
|
||||
private OrderEventPublisher orderEventPublisher;
|
||||
@Mock
|
||||
private OrderStateMachine orderStateMachine;
|
||||
private OrderLifecycleManager orderLifecycleManager;
|
||||
|
||||
// 模拟数据库
|
||||
private Map<Long, OpsOrderDO> orderDB;
|
||||
@@ -199,14 +201,14 @@ public class SecurityOrderServiceTest {
|
||||
// 执行
|
||||
securityOrderService.confirmOrder(TEST_ORDER_ID, userId);
|
||||
|
||||
// 验证状态机调用
|
||||
verify(orderStateMachine).transition(
|
||||
eq(order),
|
||||
eq(WorkOrderStatusEnum.CONFIRMED),
|
||||
eq(OperatorTypeEnum.SECURITY_GUARD),
|
||||
eq(userId),
|
||||
eq("安保人员确认接单")
|
||||
);
|
||||
// 验证走 orderLifecycleManager.transition()
|
||||
ArgumentCaptor<OrderTransitionRequest> reqCaptor = ArgumentCaptor.forClass(OrderTransitionRequest.class);
|
||||
verify(orderLifecycleManager).transition(reqCaptor.capture());
|
||||
OrderTransitionRequest capturedReq = reqCaptor.getValue();
|
||||
assertEquals(TEST_ORDER_ID, capturedReq.getOrderId());
|
||||
assertEquals(WorkOrderStatusEnum.CONFIRMED, capturedReq.getTargetStatus());
|
||||
assertEquals(OperatorTypeEnum.SECURITY_GUARD, capturedReq.getOperatorType());
|
||||
assertEquals(userId, capturedReq.getOperatorId());
|
||||
|
||||
// 验证不再直接写扩展表时间(由 EventListener 统一处理)
|
||||
verify(securityExtMapper, never()).insertOrUpdateSelective(any());
|
||||
@@ -235,9 +237,8 @@ public class SecurityOrderServiceTest {
|
||||
() -> securityOrderService.confirmOrder(TEST_ORDER_ID, 2001L));
|
||||
assertTrue(exception.getMessage().contains("工单类型不匹配"));
|
||||
|
||||
// 验证状态机未被调用
|
||||
verify(orderStateMachine, never()).transition(
|
||||
any(), any(), any(), anyLong(), anyString());
|
||||
// 验证 lifecycleManager 未被调用
|
||||
verify(orderLifecycleManager, never()).transition(any(OrderTransitionRequest.class));
|
||||
}
|
||||
|
||||
// ==================== 自动完单测试 ====================
|
||||
@@ -247,21 +248,21 @@ public class SecurityOrderServiceTest {
|
||||
// 准备
|
||||
OpsOrderDO order = buildSecurityOrder(TEST_ORDER_ID, WorkOrderStatusEnum.ARRIVED);
|
||||
orderDB.put(TEST_ORDER_ID, order);
|
||||
when(orderLifecycleManager.forceComplete(anyLong(), any(), any(), anyString()))
|
||||
.thenReturn(OrderTransitionResult.builder().success(true).orderId(TEST_ORDER_ID).build());
|
||||
|
||||
// 执行
|
||||
securityOrderService.autoCompleteOrder(TEST_ORDER_ID, "告警自动解除");
|
||||
|
||||
// 验证状态机调用(ARRIVED 非终态,走 forceTransition)
|
||||
verify(orderStateMachine).forceTransition(
|
||||
eq(order),
|
||||
eq(WorkOrderStatusEnum.COMPLETED),
|
||||
eq(OperatorTypeEnum.SYSTEM),
|
||||
// 验证走 forceComplete(完整责任链)
|
||||
verify(orderLifecycleManager).forceComplete(
|
||||
eq(TEST_ORDER_ID),
|
||||
isNull(),
|
||||
eq(OperatorTypeEnum.SYSTEM),
|
||||
eq("告警自动解除")
|
||||
);
|
||||
|
||||
// 验证不直接写扩展表
|
||||
verify(securityExtMapper, never()).insertOrUpdateSelective(any());
|
||||
// 验证不直接调用 orderStateMachine(已完全移除依赖)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -269,14 +270,16 @@ public class SecurityOrderServiceTest {
|
||||
// 准备
|
||||
OpsOrderDO order = buildSecurityOrder(TEST_ORDER_ID, WorkOrderStatusEnum.ARRIVED);
|
||||
orderDB.put(TEST_ORDER_ID, order);
|
||||
when(orderLifecycleManager.forceComplete(anyLong(), any(), any(), anyString()))
|
||||
.thenReturn(OrderTransitionResult.builder().success(true).orderId(TEST_ORDER_ID).build());
|
||||
|
||||
// 执行:remark 为空
|
||||
securityOrderService.autoCompleteOrder(TEST_ORDER_ID, null);
|
||||
|
||||
// 验证使用默认备注
|
||||
verify(orderStateMachine).forceTransition(
|
||||
any(), eq(WorkOrderStatusEnum.COMPLETED),
|
||||
eq(OperatorTypeEnum.SYSTEM), isNull(),
|
||||
verify(orderLifecycleManager).forceComplete(
|
||||
eq(TEST_ORDER_ID), isNull(),
|
||||
eq(OperatorTypeEnum.SYSTEM),
|
||||
eq("系统自动完单")
|
||||
);
|
||||
}
|
||||
@@ -286,14 +289,16 @@ public class SecurityOrderServiceTest {
|
||||
// 准备
|
||||
OpsOrderDO order = buildSecurityOrder(TEST_ORDER_ID, WorkOrderStatusEnum.ARRIVED);
|
||||
orderDB.put(TEST_ORDER_ID, order);
|
||||
when(orderLifecycleManager.forceComplete(anyLong(), any(), any(), anyString()))
|
||||
.thenReturn(OrderTransitionResult.builder().success(true).orderId(TEST_ORDER_ID).build());
|
||||
|
||||
// 执行:remark 为空白字符串
|
||||
securityOrderService.autoCompleteOrder(TEST_ORDER_ID, " ");
|
||||
|
||||
// 验证使用默认备注
|
||||
verify(orderStateMachine).forceTransition(
|
||||
any(), eq(WorkOrderStatusEnum.COMPLETED),
|
||||
eq(OperatorTypeEnum.SYSTEM), isNull(),
|
||||
verify(orderLifecycleManager).forceComplete(
|
||||
eq(TEST_ORDER_ID), isNull(),
|
||||
eq(OperatorTypeEnum.SYSTEM),
|
||||
eq("系统自动完单")
|
||||
);
|
||||
}
|
||||
@@ -313,16 +318,15 @@ public class SecurityOrderServiceTest {
|
||||
// 执行
|
||||
securityOrderService.autoCompleteOrder(TEST_ORDER_ID, "告警自动解除");
|
||||
|
||||
// 验证:走正常 transition → CANCELLED
|
||||
verify(orderStateMachine).transition(
|
||||
eq(order),
|
||||
eq(WorkOrderStatusEnum.CANCELLED),
|
||||
eq(OperatorTypeEnum.SYSTEM),
|
||||
// 验证:走 cancelOrder(完整责任链)
|
||||
verify(orderLifecycleManager).cancelOrder(
|
||||
eq(TEST_ORDER_ID),
|
||||
isNull(),
|
||||
eq(OperatorTypeEnum.SYSTEM),
|
||||
eq("告警自动解除(工单未派单)")
|
||||
);
|
||||
// 不应调用 forceTransition
|
||||
verify(orderStateMachine, never()).forceTransition(any(), any(), any(), any(), any());
|
||||
// 不应调用 forceComplete
|
||||
verify(orderLifecycleManager, never()).forceComplete(anyLong(), any(), any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -330,16 +334,17 @@ public class SecurityOrderServiceTest {
|
||||
// 准备:DISPATCHED 状态工单
|
||||
OpsOrderDO order = buildSecurityOrder(TEST_ORDER_ID, WorkOrderStatusEnum.DISPATCHED);
|
||||
orderDB.put(TEST_ORDER_ID, order);
|
||||
when(orderLifecycleManager.forceComplete(anyLong(), any(), any(), anyString()))
|
||||
.thenReturn(OrderTransitionResult.builder().success(true).orderId(TEST_ORDER_ID).build());
|
||||
|
||||
// 执行
|
||||
securityOrderService.autoCompleteOrder(TEST_ORDER_ID, "告警自动解除");
|
||||
|
||||
// 验证:走 forceTransition → COMPLETED
|
||||
verify(orderStateMachine).forceTransition(
|
||||
eq(order),
|
||||
eq(WorkOrderStatusEnum.COMPLETED),
|
||||
eq(OperatorTypeEnum.SYSTEM),
|
||||
// 验证:走 forceComplete → COMPLETED(通过完整责任链)
|
||||
verify(orderLifecycleManager).forceComplete(
|
||||
eq(TEST_ORDER_ID),
|
||||
isNull(),
|
||||
eq(OperatorTypeEnum.SYSTEM),
|
||||
eq("告警自动解除")
|
||||
);
|
||||
}
|
||||
@@ -353,9 +358,9 @@ public class SecurityOrderServiceTest {
|
||||
// 执行:不应抛异常
|
||||
securityOrderService.autoCompleteOrder(TEST_ORDER_ID, "告警自动解除");
|
||||
|
||||
// 验证:状态机未被调用
|
||||
verify(orderStateMachine, never()).transition(any(), any(), any(), any(), any());
|
||||
verify(orderStateMachine, never()).forceTransition(any(), any(), any(), any(), any());
|
||||
// 验证:生命周期管理器未被调用
|
||||
verify(orderLifecycleManager, never()).forceComplete(anyLong(), any(), any(), anyString());
|
||||
verify(orderLifecycleManager, never()).cancelOrder(anyLong(), any(), any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -363,16 +368,17 @@ public class SecurityOrderServiceTest {
|
||||
// 准备:DISPATCHED 状态工单
|
||||
OpsOrderDO order = buildSecurityOrder(TEST_ORDER_ID, WorkOrderStatusEnum.DISPATCHED);
|
||||
orderDB.put(TEST_ORDER_ID, order);
|
||||
when(orderLifecycleManager.forceComplete(anyLong(), any(), any(), anyString()))
|
||||
.thenReturn(OrderTransitionResult.builder().success(true).orderId(TEST_ORDER_ID).build());
|
||||
|
||||
// 执行
|
||||
securityOrderService.falseAlarmOrder(TEST_ORDER_ID);
|
||||
|
||||
// 验证:forceTransition 被调用(通过 autoCompleteOrder 委托)
|
||||
verify(orderStateMachine).forceTransition(
|
||||
eq(order),
|
||||
eq(WorkOrderStatusEnum.COMPLETED),
|
||||
eq(OperatorTypeEnum.SYSTEM),
|
||||
// 验证:forceComplete 被调用(通过 autoCompleteOrder 委托)
|
||||
verify(orderLifecycleManager).forceComplete(
|
||||
eq(TEST_ORDER_ID),
|
||||
isNull(),
|
||||
eq(OperatorTypeEnum.SYSTEM),
|
||||
eq("误报")
|
||||
);
|
||||
|
||||
@@ -403,12 +409,11 @@ public class SecurityOrderServiceTest {
|
||||
// 执行
|
||||
securityOrderService.manualCompleteOrder(req);
|
||||
|
||||
// 验证状态机调用
|
||||
verify(orderStateMachine).transition(
|
||||
eq(order),
|
||||
eq(WorkOrderStatusEnum.COMPLETED),
|
||||
eq(OperatorTypeEnum.SECURITY_GUARD),
|
||||
// 验证走 orderLifecycleManager.completeOrder()
|
||||
verify(orderLifecycleManager).completeOrder(
|
||||
eq(TEST_ORDER_ID),
|
||||
eq(2001L),
|
||||
eq(OperatorTypeEnum.SECURITY_GUARD),
|
||||
eq("安保人员提交处理结果")
|
||||
);
|
||||
|
||||
@@ -421,11 +426,6 @@ public class SecurityOrderServiceTest {
|
||||
assertNotNull(extUpdate.getResultImgUrls());
|
||||
assertTrue(extUpdate.getResultImgUrls().contains("result1.jpg"));
|
||||
assertNull(extUpdate.getCompletedTime()); // 时间由 EventListener 写入
|
||||
|
||||
// 验证主表 endTime 更新
|
||||
ArgumentCaptor<OpsOrderDO> orderUpdateCaptor = ArgumentCaptor.forClass(OpsOrderDO.class);
|
||||
verify(opsOrderMapper).updateById(orderUpdateCaptor.capture());
|
||||
assertNotNull(orderUpdateCaptor.getValue().getEndTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user