test(ops): 新增安保模块单元测试
覆盖 SecurityOrderService、SecurityOrderEventListener、 SecurityAreaAssignStrategy、SecurityOrderExtQueryHandler、 OpsAreaSecurityUserService 的核心逻辑测试。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
package com.viewsh.module.ops.security.integration.listener;
|
||||
|
||||
import com.viewsh.module.ops.core.dispatch.DispatchEngine;
|
||||
import com.viewsh.module.ops.core.dispatch.model.OrderDispatchContext;
|
||||
import com.viewsh.module.ops.core.event.OrderCompletedEvent;
|
||||
import com.viewsh.module.ops.core.event.OrderCreatedEvent;
|
||||
import com.viewsh.module.ops.core.event.OrderStateChangedEvent;
|
||||
import com.viewsh.module.ops.enums.PriorityEnum;
|
||||
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
|
||||
import com.viewsh.module.ops.security.dal.dataobject.workorder.OpsOrderSecurityExtDO;
|
||||
import com.viewsh.module.ops.security.dal.mysql.workorder.OpsOrderSecurityExtMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* 安保工单事件监听器测试
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class SecurityOrderEventListenerTest {
|
||||
|
||||
@InjectMocks
|
||||
private SecurityOrderEventListener listener;
|
||||
|
||||
@Mock
|
||||
private OpsOrderSecurityExtMapper securityExtMapper;
|
||||
|
||||
@Mock
|
||||
private DispatchEngine dispatchEngine;
|
||||
|
||||
private static final Long TEST_ORDER_ID = 10001L;
|
||||
private static final String TEST_ORDER_CODE = "SECURITY-20260310-0001";
|
||||
|
||||
// ==================== onOrderCreated 测试 ====================
|
||||
|
||||
@Test
|
||||
void testOnOrderCreated_SecurityType_TriggersDispatch() {
|
||||
OrderCreatedEvent event = OrderCreatedEvent.builder()
|
||||
.orderId(TEST_ORDER_ID)
|
||||
.orderType("SECURITY")
|
||||
.orderCode(TEST_ORDER_CODE)
|
||||
.title("入侵告警")
|
||||
.areaId(100L)
|
||||
.priority(PriorityEnum.P1.getPriority())
|
||||
.build();
|
||||
|
||||
// 执行
|
||||
listener.onOrderCreated(event);
|
||||
|
||||
// 验证触发了派单(asyncDispatchAfterCreated 是自调用,实际同步执行)
|
||||
ArgumentCaptor<OrderDispatchContext> contextCaptor = ArgumentCaptor.forClass(OrderDispatchContext.class);
|
||||
verify(dispatchEngine).dispatch(contextCaptor.capture());
|
||||
OrderDispatchContext ctx = contextCaptor.getValue();
|
||||
assertEquals(TEST_ORDER_ID, ctx.getOrderId());
|
||||
assertEquals(TEST_ORDER_CODE, ctx.getOrderCode());
|
||||
assertEquals("入侵告警", ctx.getOrderTitle());
|
||||
assertEquals("SECURITY", ctx.getBusinessType());
|
||||
assertEquals(100L, ctx.getAreaId());
|
||||
assertEquals(PriorityEnum.P1, ctx.getPriority());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOnOrderCreated_CleanType_Ignored() {
|
||||
OrderCreatedEvent event = OrderCreatedEvent.builder()
|
||||
.orderId(TEST_ORDER_ID)
|
||||
.orderType("CLEAN")
|
||||
.build();
|
||||
|
||||
// 执行
|
||||
listener.onOrderCreated(event);
|
||||
|
||||
// 验证不触发派单
|
||||
verify(dispatchEngine, never()).dispatch(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOnOrderCreated_DispatchException_Caught() {
|
||||
OrderCreatedEvent event = OrderCreatedEvent.builder()
|
||||
.orderId(TEST_ORDER_ID)
|
||||
.orderType("SECURITY")
|
||||
.orderCode(TEST_ORDER_CODE)
|
||||
.areaId(100L)
|
||||
.priority(PriorityEnum.P1.getPriority())
|
||||
.build();
|
||||
|
||||
when(dispatchEngine.dispatch(any())).thenThrow(new RuntimeException("调度引擎异常"));
|
||||
|
||||
// 执行:不应抛出异常
|
||||
assertDoesNotThrow(() -> listener.onOrderCreated(event));
|
||||
}
|
||||
|
||||
// ==================== onOrderStateChanged 测试 ====================
|
||||
|
||||
@Test
|
||||
void testOnOrderStateChanged_Dispatched_RecordsTime() {
|
||||
OrderStateChangedEvent event = buildStateChangedEvent(
|
||||
WorkOrderStatusEnum.PENDING, WorkOrderStatusEnum.DISPATCHED);
|
||||
|
||||
lenient().when(securityExtMapper.insertOrUpdateSelective(any())).thenReturn(1);
|
||||
|
||||
// 执行
|
||||
listener.onOrderStateChanged(event);
|
||||
|
||||
// 验证扩展表写入 dispatchedTime
|
||||
ArgumentCaptor<OpsOrderSecurityExtDO> captor = ArgumentCaptor.forClass(OpsOrderSecurityExtDO.class);
|
||||
verify(securityExtMapper).insertOrUpdateSelective(captor.capture());
|
||||
OpsOrderSecurityExtDO ext = captor.getValue();
|
||||
assertEquals(TEST_ORDER_ID, ext.getOpsOrderId());
|
||||
assertNotNull(ext.getDispatchedTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOnOrderStateChanged_Confirmed_RecordsTime() {
|
||||
OrderStateChangedEvent event = buildStateChangedEvent(
|
||||
WorkOrderStatusEnum.DISPATCHED, WorkOrderStatusEnum.CONFIRMED);
|
||||
|
||||
lenient().when(securityExtMapper.insertOrUpdateSelective(any())).thenReturn(1);
|
||||
|
||||
// 执行
|
||||
listener.onOrderStateChanged(event);
|
||||
|
||||
// 验证扩展表写入 confirmedTime
|
||||
ArgumentCaptor<OpsOrderSecurityExtDO> captor = ArgumentCaptor.forClass(OpsOrderSecurityExtDO.class);
|
||||
verify(securityExtMapper).insertOrUpdateSelective(captor.capture());
|
||||
OpsOrderSecurityExtDO ext = captor.getValue();
|
||||
assertEquals(TEST_ORDER_ID, ext.getOpsOrderId());
|
||||
assertNotNull(ext.getConfirmedTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOnOrderStateChanged_Completed_RecordsTime() {
|
||||
OrderStateChangedEvent event = buildStateChangedEvent(
|
||||
WorkOrderStatusEnum.ARRIVED, WorkOrderStatusEnum.COMPLETED);
|
||||
|
||||
OpsOrderSecurityExtDO existingExt = OpsOrderSecurityExtDO.builder()
|
||||
.id(1L).opsOrderId(TEST_ORDER_ID).build();
|
||||
when(securityExtMapper.selectByOpsOrderId(TEST_ORDER_ID)).thenReturn(existingExt);
|
||||
lenient().when(securityExtMapper.insertOrUpdateSelective(any())).thenReturn(1);
|
||||
|
||||
// 执行
|
||||
listener.onOrderStateChanged(event);
|
||||
|
||||
// 验证扩展表写入 completedTime
|
||||
ArgumentCaptor<OpsOrderSecurityExtDO> captor = ArgumentCaptor.forClass(OpsOrderSecurityExtDO.class);
|
||||
verify(securityExtMapper).insertOrUpdateSelective(captor.capture());
|
||||
OpsOrderSecurityExtDO ext = captor.getValue();
|
||||
assertEquals(TEST_ORDER_ID, ext.getOpsOrderId());
|
||||
assertNotNull(ext.getCompletedTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOnOrderStateChanged_Completed_NoExt_Skipped() {
|
||||
OrderStateChangedEvent event = buildStateChangedEvent(
|
||||
WorkOrderStatusEnum.ARRIVED, WorkOrderStatusEnum.COMPLETED);
|
||||
|
||||
when(securityExtMapper.selectByOpsOrderId(TEST_ORDER_ID)).thenReturn(null);
|
||||
|
||||
// 执行
|
||||
listener.onOrderStateChanged(event);
|
||||
|
||||
// 验证:扩展记录不存在时不写入
|
||||
verify(securityExtMapper, never()).insertOrUpdateSelective(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOnOrderStateChanged_CleanType_Ignored() {
|
||||
OrderStateChangedEvent event = OrderStateChangedEvent.builder()
|
||||
.orderId(TEST_ORDER_ID)
|
||||
.orderType("CLEAN")
|
||||
.oldStatus(WorkOrderStatusEnum.PENDING)
|
||||
.newStatus(WorkOrderStatusEnum.DISPATCHED)
|
||||
.build();
|
||||
|
||||
// 执行
|
||||
listener.onOrderStateChanged(event);
|
||||
|
||||
// 验证不触发任何扩展表操作
|
||||
verify(securityExtMapper, never()).insertOrUpdateSelective(any());
|
||||
verify(securityExtMapper, never()).selectByOpsOrderId(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOnOrderStateChanged_OtherStatus_NoAction() {
|
||||
// CANCELLED 等状态无需额外处理
|
||||
OrderStateChangedEvent event = buildStateChangedEvent(
|
||||
WorkOrderStatusEnum.PENDING, WorkOrderStatusEnum.CANCELLED);
|
||||
|
||||
// 执行
|
||||
listener.onOrderStateChanged(event);
|
||||
|
||||
// 验证无扩展表操作
|
||||
verify(securityExtMapper, never()).insertOrUpdateSelective(any());
|
||||
}
|
||||
|
||||
// ==================== onOrderCompleted 测试 ====================
|
||||
|
||||
@Test
|
||||
void testOnOrderCompleted_HasAssignee_DispatchNext() {
|
||||
OrderCompletedEvent event = OrderCompletedEvent.builder()
|
||||
.orderId(TEST_ORDER_ID)
|
||||
.orderType("SECURITY")
|
||||
.assigneeId(2001L)
|
||||
.build();
|
||||
|
||||
// 执行
|
||||
listener.onOrderCompleted(event);
|
||||
|
||||
// 验证调用自动派送下一个
|
||||
verify(dispatchEngine).autoDispatchNext(TEST_ORDER_ID, 2001L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOnOrderCompleted_NoAssignee_SkipDispatch() {
|
||||
OrderCompletedEvent event = OrderCompletedEvent.builder()
|
||||
.orderId(TEST_ORDER_ID)
|
||||
.orderType("SECURITY")
|
||||
.assigneeId(null) // 无分配人
|
||||
.build();
|
||||
|
||||
// 执行
|
||||
listener.onOrderCompleted(event);
|
||||
|
||||
// 验证不调用自动派送
|
||||
verify(dispatchEngine, never()).autoDispatchNext(anyLong(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOnOrderCompleted_CleanType_Ignored() {
|
||||
OrderCompletedEvent event = OrderCompletedEvent.builder()
|
||||
.orderId(TEST_ORDER_ID)
|
||||
.orderType("CLEAN")
|
||||
.assigneeId(2001L)
|
||||
.build();
|
||||
|
||||
// 执行
|
||||
listener.onOrderCompleted(event);
|
||||
|
||||
// 验证不触发
|
||||
verify(dispatchEngine, never()).autoDispatchNext(anyLong(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOnOrderCompleted_DispatchException_Caught() {
|
||||
OrderCompletedEvent event = OrderCompletedEvent.builder()
|
||||
.orderId(TEST_ORDER_ID)
|
||||
.orderType("SECURITY")
|
||||
.assigneeId(2001L)
|
||||
.build();
|
||||
|
||||
doThrow(new RuntimeException("派送异常")).when(dispatchEngine)
|
||||
.autoDispatchNext(anyLong(), anyLong());
|
||||
|
||||
// 执行:不应抛出异常
|
||||
assertDoesNotThrow(() -> listener.onOrderCompleted(event));
|
||||
}
|
||||
|
||||
// ==================== 辅助方法 ====================
|
||||
|
||||
private OrderStateChangedEvent buildStateChangedEvent(
|
||||
WorkOrderStatusEnum oldStatus, WorkOrderStatusEnum newStatus) {
|
||||
return OrderStateChangedEvent.builder()
|
||||
.orderId(TEST_ORDER_ID)
|
||||
.orderType("SECURITY")
|
||||
.oldStatus(oldStatus)
|
||||
.newStatus(newStatus)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.viewsh.module.ops.security.service.area;
|
||||
|
||||
import com.viewsh.framework.common.exception.ServiceException;
|
||||
import com.viewsh.module.ops.security.dal.dataobject.area.OpsAreaSecurityUserDO;
|
||||
import com.viewsh.module.ops.security.dal.mysql.area.OpsAreaSecurityUserMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* 区域安保人员绑定服务单元测试
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class OpsAreaSecurityUserServiceTest {
|
||||
|
||||
@InjectMocks
|
||||
private OpsAreaSecurityUserServiceImpl service;
|
||||
|
||||
@Mock
|
||||
private OpsAreaSecurityUserMapper areaSecurityUserMapper;
|
||||
|
||||
@Test
|
||||
void bindUser_success() {
|
||||
when(areaSecurityUserMapper.selectByAreaIdAndUserId(100L, 2001L)).thenReturn(null);
|
||||
when(areaSecurityUserMapper.insert(any(OpsAreaSecurityUserDO.class))).thenReturn(1);
|
||||
|
||||
Long id = service.bindUser(100L, 2001L, "张三", 10L, 0);
|
||||
|
||||
ArgumentCaptor<OpsAreaSecurityUserDO> captor = ArgumentCaptor.forClass(OpsAreaSecurityUserDO.class);
|
||||
verify(areaSecurityUserMapper).insert(captor.capture());
|
||||
OpsAreaSecurityUserDO saved = captor.getValue();
|
||||
assertEquals(100L, saved.getAreaId());
|
||||
assertEquals(2001L, saved.getUserId());
|
||||
assertEquals("张三", saved.getUserName());
|
||||
assertEquals(10L, saved.getTeamId());
|
||||
assertTrue(saved.getEnabled());
|
||||
assertEquals(0, saved.getSort());
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindUser_duplicate_throws() {
|
||||
OpsAreaSecurityUserDO existing = OpsAreaSecurityUserDO.builder()
|
||||
.id(1L).areaId(100L).userId(2001L).build();
|
||||
when(areaSecurityUserMapper.selectByAreaIdAndUserId(100L, 2001L)).thenReturn(existing);
|
||||
|
||||
assertThrows(ServiceException.class, () -> service.bindUser(100L, 2001L, "张三", null, null));
|
||||
verify(areaSecurityUserMapper, never()).insert(any(OpsAreaSecurityUserDO.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unbindUser_success() {
|
||||
OpsAreaSecurityUserDO existing = OpsAreaSecurityUserDO.builder()
|
||||
.id(1L).areaId(100L).userId(2001L).build();
|
||||
when(areaSecurityUserMapper.selectById(1L)).thenReturn(existing);
|
||||
when(areaSecurityUserMapper.deleteById(1L)).thenReturn(1);
|
||||
|
||||
service.unbindUser(1L);
|
||||
|
||||
verify(areaSecurityUserMapper).deleteById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unbindUser_notFound_throws() {
|
||||
when(areaSecurityUserMapper.selectById(999L)).thenReturn(null);
|
||||
|
||||
assertThrows(ServiceException.class, () -> service.unbindUser(999L));
|
||||
verify(areaSecurityUserMapper, never()).deleteById(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void listByAreaId_returnsEnabledOnly() {
|
||||
OpsAreaSecurityUserDO user1 = OpsAreaSecurityUserDO.builder()
|
||||
.id(1L).areaId(100L).userId(2001L).userName("张三").enabled(true).build();
|
||||
OpsAreaSecurityUserDO user2 = OpsAreaSecurityUserDO.builder()
|
||||
.id(2L).areaId(100L).userId(2002L).userName("李四").enabled(true).build();
|
||||
|
||||
when(areaSecurityUserMapper.selectListByAreaId(100L)).thenReturn(Arrays.asList(user1, user2));
|
||||
|
||||
List<OpsAreaSecurityUserDO> result = service.listByAreaId(100L);
|
||||
|
||||
assertEquals(2, result.size());
|
||||
verify(areaSecurityUserMapper).selectListByAreaId(100L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateBinding_success() {
|
||||
OpsAreaSecurityUserDO existing = OpsAreaSecurityUserDO.builder()
|
||||
.id(1L).areaId(100L).userId(2001L).enabled(true).sort(0).build();
|
||||
when(areaSecurityUserMapper.selectById(1L)).thenReturn(existing);
|
||||
when(areaSecurityUserMapper.updateById(any(OpsAreaSecurityUserDO.class))).thenReturn(1);
|
||||
|
||||
service.updateBinding(1L, false, 5, 20L);
|
||||
|
||||
ArgumentCaptor<OpsAreaSecurityUserDO> captor = ArgumentCaptor.forClass(OpsAreaSecurityUserDO.class);
|
||||
verify(areaSecurityUserMapper).updateById(captor.capture());
|
||||
OpsAreaSecurityUserDO updated = captor.getValue();
|
||||
assertEquals(1L, updated.getId());
|
||||
assertFalse(updated.getEnabled());
|
||||
assertEquals(5, updated.getSort());
|
||||
assertEquals(20L, updated.getTeamId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateBinding_notFound_throws() {
|
||||
when(areaSecurityUserMapper.selectById(999L)).thenReturn(null);
|
||||
|
||||
assertThrows(ServiceException.class, () -> service.updateBinding(999L, true, null, null));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.viewsh.module.ops.security.service.dispatch;
|
||||
|
||||
import com.viewsh.module.ops.core.dispatch.model.AssigneeRecommendation;
|
||||
import com.viewsh.module.ops.core.dispatch.model.OrderDispatchContext;
|
||||
import com.viewsh.module.ops.security.dal.dataobject.area.OpsAreaSecurityUserDO;
|
||||
import com.viewsh.module.ops.security.dal.mysql.area.OpsAreaSecurityUserMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* 安保区域分配策略单元测试
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class SecurityAreaAssignStrategyTest {
|
||||
|
||||
@InjectMocks
|
||||
private SecurityAreaAssignStrategy strategy;
|
||||
|
||||
@Mock
|
||||
private OpsAreaSecurityUserMapper areaSecurityUserMapper;
|
||||
|
||||
@Test
|
||||
void recommend_noAreaId_returnsNone() {
|
||||
OrderDispatchContext context = OrderDispatchContext.builder()
|
||||
.orderId(1001L)
|
||||
.areaId(null)
|
||||
.build();
|
||||
|
||||
AssigneeRecommendation result = strategy.recommend(context);
|
||||
|
||||
assertFalse(result.hasRecommendation());
|
||||
verify(areaSecurityUserMapper, never()).selectListByAreaId(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void recommend_noUsersInArea_returnsNone() {
|
||||
Long areaId = 100L;
|
||||
OrderDispatchContext context = OrderDispatchContext.builder()
|
||||
.orderId(1001L)
|
||||
.areaId(areaId)
|
||||
.build();
|
||||
|
||||
when(areaSecurityUserMapper.selectListByAreaId(areaId)).thenReturn(Collections.emptyList());
|
||||
|
||||
AssigneeRecommendation result = strategy.recommend(context);
|
||||
|
||||
assertFalse(result.hasRecommendation());
|
||||
verify(areaSecurityUserMapper).selectListByAreaId(areaId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void recommend_hasUsers_returnsRecommendation() {
|
||||
Long areaId = 100L;
|
||||
OrderDispatchContext context = OrderDispatchContext.builder()
|
||||
.orderId(1001L)
|
||||
.areaId(areaId)
|
||||
.build();
|
||||
|
||||
OpsAreaSecurityUserDO user1 = OpsAreaSecurityUserDO.builder()
|
||||
.id(1L).areaId(areaId).userId(2001L).userName("张三").enabled(true).build();
|
||||
OpsAreaSecurityUserDO user2 = OpsAreaSecurityUserDO.builder()
|
||||
.id(2L).areaId(areaId).userId(2002L).userName("李四").enabled(true).build();
|
||||
|
||||
when(areaSecurityUserMapper.selectListByAreaId(areaId)).thenReturn(Arrays.asList(user1, user2));
|
||||
|
||||
AssigneeRecommendation result = strategy.recommend(context);
|
||||
|
||||
assertTrue(result.hasRecommendation());
|
||||
assertNotNull(result.getAssigneeId());
|
||||
assertTrue(result.getAssigneeId().equals(2001L) || result.getAssigneeId().equals(2002L));
|
||||
assertEquals(50, result.getScore());
|
||||
assertEquals("区域随机分配", result.getReason());
|
||||
assertEquals(areaId, result.getAreaId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void recommend_singleUser_returnsThatUser() {
|
||||
Long areaId = 100L;
|
||||
OrderDispatchContext context = OrderDispatchContext.builder()
|
||||
.orderId(1001L)
|
||||
.areaId(areaId)
|
||||
.build();
|
||||
|
||||
OpsAreaSecurityUserDO user = OpsAreaSecurityUserDO.builder()
|
||||
.id(1L).areaId(areaId).userId(2001L).userName("张三").enabled(true).build();
|
||||
|
||||
when(areaSecurityUserMapper.selectListByAreaId(areaId)).thenReturn(Collections.singletonList(user));
|
||||
|
||||
AssigneeRecommendation result = strategy.recommend(context);
|
||||
|
||||
assertTrue(result.hasRecommendation());
|
||||
assertEquals(2001L, result.getAssigneeId());
|
||||
assertEquals("张三", result.getAssigneeName());
|
||||
assertEquals(areaId, result.getAreaId());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package com.viewsh.module.ops.security.service.securityorder;
|
||||
|
||||
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
|
||||
import com.viewsh.module.ops.enums.PriorityEnum;
|
||||
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
|
||||
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.OrderDetailVO;
|
||||
import com.viewsh.module.ops.service.OrderSummaryVO;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* 安保工单扩展查询处理器测试
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class SecurityOrderExtQueryHandlerTest {
|
||||
|
||||
@InjectMocks
|
||||
private SecurityOrderExtQueryHandler handler;
|
||||
|
||||
@Mock
|
||||
private OpsOrderSecurityExtMapper securityExtMapper;
|
||||
|
||||
private static final Long TEST_ORDER_ID = 10001L;
|
||||
private OpsOrderSecurityExtDO testSecurityExt;
|
||||
private OpsOrderDO testOrder;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
testSecurityExt = OpsOrderSecurityExtDO.builder()
|
||||
.id(1L)
|
||||
.opsOrderId(TEST_ORDER_ID)
|
||||
.alarmId("ALM20260310001")
|
||||
.alarmType("intrusion")
|
||||
.cameraId("CAM_001")
|
||||
.roiId("ROI_001")
|
||||
.imageUrl("https://oss.example.com/snapshot.jpg")
|
||||
.assignedUserId(2001L)
|
||||
.assignedUserName("张安保")
|
||||
.assignedTeamId(301L)
|
||||
.result("已排查,系误报")
|
||||
.resultImgUrls("[\"https://oss/r1.jpg\"]")
|
||||
.dispatchedTime(LocalDateTime.of(2026, 3, 10, 10, 0))
|
||||
.confirmedTime(LocalDateTime.of(2026, 3, 10, 10, 2))
|
||||
.completedTime(LocalDateTime.of(2026, 3, 10, 10, 30))
|
||||
.build();
|
||||
|
||||
testOrder = OpsOrderDO.builder()
|
||||
.id(TEST_ORDER_ID)
|
||||
.orderCode("SECURITY-20260310-0001")
|
||||
.orderType("SECURITY")
|
||||
.sourceType("ALARM")
|
||||
.title("A栋3层入侵告警")
|
||||
.description("摄像头检测到异常人员")
|
||||
.priority(PriorityEnum.P1.getPriority())
|
||||
.status(WorkOrderStatusEnum.COMPLETED.getStatus())
|
||||
.areaId(100L)
|
||||
.location("A栋3层东侧走廊")
|
||||
.assigneeId(2001L)
|
||||
.assigneeName("张安保")
|
||||
.startTime(LocalDateTime.of(2026, 3, 10, 10, 5))
|
||||
.endTime(LocalDateTime.of(2026, 3, 10, 10, 30))
|
||||
.responseSeconds(300)
|
||||
.completionSeconds(1500)
|
||||
.build();
|
||||
}
|
||||
|
||||
// ==================== supports() 测试 ====================
|
||||
|
||||
@Test
|
||||
void testSupports_Security_ReturnsTrue() {
|
||||
assertTrue(handler.supports("SECURITY"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSupports_Clean_ReturnsFalse() {
|
||||
assertFalse(handler.supports("CLEAN"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSupports_Null_ReturnsFalse() {
|
||||
assertFalse(handler.supports(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSupports_Empty_ReturnsFalse() {
|
||||
assertFalse(handler.supports(""));
|
||||
}
|
||||
|
||||
// ==================== enrichWithExtInfo() 测试 ====================
|
||||
|
||||
@Test
|
||||
void testEnrichWithExtInfo_HasExt_FieldsPopulated() {
|
||||
when(securityExtMapper.selectByOpsOrderId(TEST_ORDER_ID)).thenReturn(testSecurityExt);
|
||||
|
||||
OrderSummaryVO vo = new OrderSummaryVO();
|
||||
handler.enrichWithExtInfo(vo, TEST_ORDER_ID);
|
||||
|
||||
// 验证 extInfo 非空
|
||||
assertNotNull(vo.getExtInfo());
|
||||
Map<String, Object> extInfo = vo.getExtInfo();
|
||||
|
||||
// 验证各字段映射
|
||||
assertEquals("ALM20260310001", extInfo.get("alarmId"));
|
||||
assertEquals("intrusion", extInfo.get("alarmType"));
|
||||
assertEquals("CAM_001", extInfo.get("cameraId"));
|
||||
assertEquals("https://oss.example.com/snapshot.jpg", extInfo.get("imageUrl"));
|
||||
assertEquals("张安保", extInfo.get("assignedUserName"));
|
||||
assertEquals(testSecurityExt.getConfirmedTime(), extInfo.get("confirmedTime"));
|
||||
assertEquals(testSecurityExt.getCompletedTime(), extInfo.get("completedTime"));
|
||||
assertEquals("已排查,系误报", extInfo.get("result"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEnrichWithExtInfo_NoExt_ExtInfoNotSet() {
|
||||
when(securityExtMapper.selectByOpsOrderId(TEST_ORDER_ID)).thenReturn(null);
|
||||
|
||||
OrderSummaryVO vo = new OrderSummaryVO();
|
||||
handler.enrichWithExtInfo(vo, TEST_ORDER_ID);
|
||||
|
||||
// 无扩展记录时,extInfo 未被填充
|
||||
assertTrue(vo.getExtInfo() == null || vo.getExtInfo().isEmpty());
|
||||
}
|
||||
|
||||
// ==================== buildDetailVO() 测试 ====================
|
||||
|
||||
@Test
|
||||
void testBuildDetailVO_HasExt_FullVO() {
|
||||
when(securityExtMapper.selectByOpsOrderId(TEST_ORDER_ID)).thenReturn(testSecurityExt);
|
||||
|
||||
OrderDetailVO vo = handler.buildDetailVO(testOrder);
|
||||
|
||||
// 验证主表字段
|
||||
assertNotNull(vo);
|
||||
assertEquals(TEST_ORDER_ID, vo.getId());
|
||||
assertEquals("SECURITY-20260310-0001", vo.getOrderCode());
|
||||
assertEquals("SECURITY", vo.getOrderType());
|
||||
assertEquals("ALARM", vo.getSourceType());
|
||||
assertEquals("A栋3层入侵告警", vo.getTitle());
|
||||
assertEquals(PriorityEnum.P1.getPriority(), vo.getPriority());
|
||||
assertEquals(WorkOrderStatusEnum.COMPLETED.getStatus(), vo.getStatus());
|
||||
assertEquals(100L, vo.getAreaId());
|
||||
assertEquals("A栋3层东侧走廊", vo.getLocation());
|
||||
|
||||
// 验证扩展字段
|
||||
assertNotNull(vo.getExtInfo());
|
||||
Map<String, Object> extInfo = vo.getExtInfo();
|
||||
assertEquals("ALM20260310001", extInfo.get("alarmId"));
|
||||
assertEquals("intrusion", extInfo.get("alarmType"));
|
||||
assertEquals("CAM_001", extInfo.get("cameraId"));
|
||||
assertEquals("ROI_001", extInfo.get("roiId"));
|
||||
assertEquals("https://oss.example.com/snapshot.jpg", extInfo.get("imageUrl"));
|
||||
assertEquals(2001L, extInfo.get("assignedUserId"));
|
||||
assertEquals("张安保", extInfo.get("assignedUserName"));
|
||||
assertEquals(301L, extInfo.get("assignedTeamId"));
|
||||
assertEquals("已排查,系误报", extInfo.get("result"));
|
||||
assertEquals("[\"https://oss/r1.jpg\"]", extInfo.get("resultImgUrls"));
|
||||
assertEquals(testSecurityExt.getDispatchedTime(), extInfo.get("dispatchedTime"));
|
||||
assertEquals(testSecurityExt.getConfirmedTime(), extInfo.get("confirmedTime"));
|
||||
assertEquals(testSecurityExt.getCompletedTime(), extInfo.get("completedTime"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBuildDetailVO_NoExt_ExtInfoNull() {
|
||||
when(securityExtMapper.selectByOpsOrderId(TEST_ORDER_ID)).thenReturn(null);
|
||||
|
||||
OrderDetailVO vo = handler.buildDetailVO(testOrder);
|
||||
|
||||
// 主表字段正常
|
||||
assertNotNull(vo);
|
||||
assertEquals(TEST_ORDER_ID, vo.getId());
|
||||
assertEquals("SECURITY", vo.getOrderType());
|
||||
|
||||
// 无扩展记录时,extInfo 未被填充
|
||||
assertTrue(vo.getExtInfo() == null || vo.getExtInfo().isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
package com.viewsh.module.ops.security.service.securityorder;
|
||||
|
||||
import com.viewsh.framework.common.exception.ServiceException;
|
||||
import com.viewsh.module.ops.core.event.OrderCreatedEvent;
|
||||
import com.viewsh.module.ops.core.event.OrderEventPublisher;
|
||||
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.SourceTypeEnum;
|
||||
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
|
||||
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 org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* 安保工单服务单元测试
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class SecurityOrderServiceTest {
|
||||
|
||||
@InjectMocks
|
||||
private SecurityOrderServiceImpl securityOrderService;
|
||||
|
||||
@Mock
|
||||
private OpsOrderMapper opsOrderMapper;
|
||||
@Mock
|
||||
private OpsOrderSecurityExtMapper securityExtMapper;
|
||||
@Mock
|
||||
private OrderIdGenerator orderIdGenerator;
|
||||
@Mock
|
||||
private OrderCodeGenerator orderCodeGenerator;
|
||||
@Mock
|
||||
private OrderEventPublisher orderEventPublisher;
|
||||
@Mock
|
||||
private OrderStateMachine orderStateMachine;
|
||||
|
||||
// 模拟数据库
|
||||
private Map<Long, OpsOrderDO> orderDB;
|
||||
|
||||
private static final Long TEST_ORDER_ID = 10001L;
|
||||
private static final String TEST_ORDER_CODE = "SECURITY-20260310-0001";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
orderDB = new HashMap<>();
|
||||
|
||||
// 配置 ID/编号 生成器
|
||||
lenient().when(orderIdGenerator.generate()).thenReturn(TEST_ORDER_ID);
|
||||
lenient().when(orderCodeGenerator.generate("SECURITY")).thenReturn(TEST_ORDER_CODE);
|
||||
|
||||
// 配置 Mapper 模拟
|
||||
lenient().when(opsOrderMapper.insert(any(OpsOrderDO.class))).thenAnswer(i -> {
|
||||
OpsOrderDO order = i.getArgument(0);
|
||||
orderDB.put(order.getId(), order);
|
||||
return 1;
|
||||
});
|
||||
lenient().when(opsOrderMapper.selectById(anyLong())).thenAnswer(i -> orderDB.get(i.getArgument(0)));
|
||||
lenient().when(opsOrderMapper.updateById(any(OpsOrderDO.class))).thenReturn(1);
|
||||
lenient().when(securityExtMapper.insert(any(OpsOrderSecurityExtDO.class))).thenReturn(1);
|
||||
lenient().when(securityExtMapper.insertOrUpdateSelective(any(OpsOrderSecurityExtDO.class))).thenReturn(1);
|
||||
}
|
||||
|
||||
// ==================== 创建工单测试 ====================
|
||||
|
||||
@Test
|
||||
void testCreateSecurityOrder_AlarmSource_Success() {
|
||||
// 准备:告警来源请求
|
||||
SecurityOrderCreateReqDTO req = new SecurityOrderCreateReqDTO();
|
||||
req.setTitle("A栋3层入侵告警");
|
||||
req.setDescription("摄像头检测到异常人员");
|
||||
req.setPriority(PriorityEnum.P1.getPriority());
|
||||
req.setAreaId(100L);
|
||||
req.setLocation("A栋3层东侧走廊");
|
||||
req.setAlarmId("ALM20260310001");
|
||||
req.setAlarmType("intrusion");
|
||||
req.setCameraId("CAM_001");
|
||||
req.setRoiId("ROI_001");
|
||||
req.setImageUrl("https://oss.example.com/alarm/snapshot.jpg");
|
||||
|
||||
// 执行
|
||||
Long orderId = securityOrderService.createSecurityOrder(req);
|
||||
|
||||
// 验证返回值
|
||||
assertEquals(TEST_ORDER_ID, orderId);
|
||||
|
||||
// 验证主表写入
|
||||
ArgumentCaptor<OpsOrderDO> orderCaptor = ArgumentCaptor.forClass(OpsOrderDO.class);
|
||||
verify(opsOrderMapper).insert(orderCaptor.capture());
|
||||
OpsOrderDO savedOrder = orderCaptor.getValue();
|
||||
assertEquals(TEST_ORDER_ID, savedOrder.getId());
|
||||
assertEquals(TEST_ORDER_CODE, savedOrder.getOrderCode());
|
||||
assertEquals("SECURITY", savedOrder.getOrderType());
|
||||
assertEquals(SourceTypeEnum.ALARM.getType(), savedOrder.getSourceType());
|
||||
assertEquals("A栋3层入侵告警", savedOrder.getTitle());
|
||||
assertEquals(PriorityEnum.P1.getPriority(), savedOrder.getPriority());
|
||||
assertEquals(WorkOrderStatusEnum.PENDING.getStatus(), savedOrder.getStatus());
|
||||
assertEquals(100L, savedOrder.getAreaId());
|
||||
|
||||
// 验证扩展表写入
|
||||
ArgumentCaptor<OpsOrderSecurityExtDO> extCaptor = ArgumentCaptor.forClass(OpsOrderSecurityExtDO.class);
|
||||
verify(securityExtMapper).insert(extCaptor.capture());
|
||||
OpsOrderSecurityExtDO savedExt = extCaptor.getValue();
|
||||
assertEquals(TEST_ORDER_ID, savedExt.getOpsOrderId());
|
||||
assertEquals("ALM20260310001", savedExt.getAlarmId());
|
||||
assertEquals("intrusion", savedExt.getAlarmType());
|
||||
assertEquals("CAM_001", savedExt.getCameraId());
|
||||
assertEquals("ROI_001", savedExt.getRoiId());
|
||||
assertEquals("https://oss.example.com/alarm/snapshot.jpg", savedExt.getImageUrl());
|
||||
|
||||
// 验证事件发布
|
||||
ArgumentCaptor<OrderCreatedEvent> eventCaptor = ArgumentCaptor.forClass(OrderCreatedEvent.class);
|
||||
verify(orderEventPublisher).publishOrderCreated(eventCaptor.capture());
|
||||
OrderCreatedEvent event = eventCaptor.getValue();
|
||||
assertEquals(TEST_ORDER_ID, event.getOrderId());
|
||||
assertEquals("SECURITY", event.getOrderType());
|
||||
assertEquals(TEST_ORDER_CODE, event.getOrderCode());
|
||||
assertEquals(100L, event.getAreaId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateSecurityOrder_ManualSource_Success() {
|
||||
// 准备:手动创建(无告警ID)
|
||||
SecurityOrderCreateReqDTO req = new SecurityOrderCreateReqDTO();
|
||||
req.setTitle("保安巡查发现异常");
|
||||
req.setAreaId(200L);
|
||||
|
||||
// 执行
|
||||
Long orderId = securityOrderService.createSecurityOrder(req);
|
||||
|
||||
// 验证来源类型为 MANUAL
|
||||
ArgumentCaptor<OpsOrderDO> orderCaptor = ArgumentCaptor.forClass(OpsOrderDO.class);
|
||||
verify(opsOrderMapper).insert(orderCaptor.capture());
|
||||
assertEquals(SourceTypeEnum.MANUAL.getType(), orderCaptor.getValue().getSourceType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateSecurityOrder_ExplicitSourceType_Success() {
|
||||
// 准备:显式指定来源类型
|
||||
SecurityOrderCreateReqDTO req = new SecurityOrderCreateReqDTO();
|
||||
req.setTitle("手动创建");
|
||||
req.setAreaId(200L);
|
||||
req.setAlarmId("ALM001"); // 有告警ID
|
||||
req.setSourceType("CUSTOM_SOURCE"); // 但显式指定了sourceType
|
||||
|
||||
// 执行
|
||||
securityOrderService.createSecurityOrder(req);
|
||||
|
||||
// 验证:显式指定的 sourceType 优先于自动推断
|
||||
ArgumentCaptor<OpsOrderDO> orderCaptor = ArgumentCaptor.forClass(OpsOrderDO.class);
|
||||
verify(opsOrderMapper).insert(orderCaptor.capture());
|
||||
assertEquals("CUSTOM_SOURCE", orderCaptor.getValue().getSourceType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateSecurityOrder_DefaultPriority_P2() {
|
||||
// 准备:不设置优先级
|
||||
SecurityOrderCreateReqDTO req = new SecurityOrderCreateReqDTO();
|
||||
req.setTitle("默认优先级工单");
|
||||
req.setAreaId(100L);
|
||||
|
||||
// 执行
|
||||
securityOrderService.createSecurityOrder(req);
|
||||
|
||||
// 验证默认 P2
|
||||
ArgumentCaptor<OpsOrderDO> orderCaptor = ArgumentCaptor.forClass(OpsOrderDO.class);
|
||||
verify(opsOrderMapper).insert(orderCaptor.capture());
|
||||
assertEquals(PriorityEnum.P2.getPriority(), orderCaptor.getValue().getPriority());
|
||||
}
|
||||
|
||||
// ==================== 确认工单测试 ====================
|
||||
|
||||
@Test
|
||||
void testConfirmOrder_Success() {
|
||||
// 准备:已存在的 SECURITY 工单
|
||||
OpsOrderDO order = buildSecurityOrder(TEST_ORDER_ID, WorkOrderStatusEnum.DISPATCHED);
|
||||
orderDB.put(TEST_ORDER_ID, order);
|
||||
|
||||
Long userId = 2001L;
|
||||
|
||||
// 执行
|
||||
securityOrderService.confirmOrder(TEST_ORDER_ID, userId);
|
||||
|
||||
// 验证状态机调用
|
||||
verify(orderStateMachine).transition(
|
||||
eq(order),
|
||||
eq(WorkOrderStatusEnum.CONFIRMED),
|
||||
eq(OperatorTypeEnum.SECURITY_GUARD),
|
||||
eq(userId),
|
||||
eq("安保人员确认接单")
|
||||
);
|
||||
|
||||
// 验证不再直接写扩展表时间(由 EventListener 统一处理)
|
||||
verify(securityExtMapper, never()).insertOrUpdateSelective(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConfirmOrder_OrderNotFound_ThrowsException() {
|
||||
// 执行 + 验证:工单不存在抛异常
|
||||
ServiceException exception = assertThrows(ServiceException.class,
|
||||
() -> securityOrderService.confirmOrder(999L, 2001L));
|
||||
assertTrue(exception.getMessage().contains("工单不存在"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConfirmOrder_WrongOrderType_ThrowsException() {
|
||||
// 准备:CLEAN 类型工单
|
||||
OpsOrderDO cleanOrder = OpsOrderDO.builder()
|
||||
.id(TEST_ORDER_ID)
|
||||
.orderType("CLEAN")
|
||||
.status(WorkOrderStatusEnum.DISPATCHED.getStatus())
|
||||
.build();
|
||||
orderDB.put(TEST_ORDER_ID, cleanOrder);
|
||||
|
||||
// 执行 + 验证:类型不匹配抛异常
|
||||
ServiceException exception = assertThrows(ServiceException.class,
|
||||
() -> securityOrderService.confirmOrder(TEST_ORDER_ID, 2001L));
|
||||
assertTrue(exception.getMessage().contains("工单类型不匹配"));
|
||||
|
||||
// 验证状态机未被调用
|
||||
verify(orderStateMachine, never()).transition(
|
||||
any(), any(), any(), anyLong(), anyString());
|
||||
}
|
||||
|
||||
// ==================== 自动完单测试 ====================
|
||||
|
||||
@Test
|
||||
void testAutoCompleteOrder_WithRemark_Success() {
|
||||
// 准备
|
||||
OpsOrderDO order = buildSecurityOrder(TEST_ORDER_ID, WorkOrderStatusEnum.ARRIVED);
|
||||
orderDB.put(TEST_ORDER_ID, order);
|
||||
|
||||
// 执行
|
||||
securityOrderService.autoCompleteOrder(TEST_ORDER_ID, "告警自动解除");
|
||||
|
||||
// 验证状态机调用
|
||||
verify(orderStateMachine).transition(
|
||||
eq(order),
|
||||
eq(WorkOrderStatusEnum.COMPLETED),
|
||||
eq(OperatorTypeEnum.SYSTEM),
|
||||
isNull(),
|
||||
eq("告警自动解除")
|
||||
);
|
||||
|
||||
// 验证主表 endTime 更新
|
||||
ArgumentCaptor<OpsOrderDO> updateCaptor = ArgumentCaptor.forClass(OpsOrderDO.class);
|
||||
verify(opsOrderMapper).updateById(updateCaptor.capture());
|
||||
OpsOrderDO updated = updateCaptor.getValue();
|
||||
assertEquals(TEST_ORDER_ID, updated.getId());
|
||||
assertNotNull(updated.getEndTime());
|
||||
|
||||
// 验证不再直接写扩展表时间
|
||||
verify(securityExtMapper, never()).insertOrUpdateSelective(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAutoCompleteOrder_WithoutRemark_DefaultReason() {
|
||||
// 准备
|
||||
OpsOrderDO order = buildSecurityOrder(TEST_ORDER_ID, WorkOrderStatusEnum.ARRIVED);
|
||||
orderDB.put(TEST_ORDER_ID, order);
|
||||
|
||||
// 执行:remark 为空
|
||||
securityOrderService.autoCompleteOrder(TEST_ORDER_ID, null);
|
||||
|
||||
// 验证使用默认备注
|
||||
verify(orderStateMachine).transition(
|
||||
any(), eq(WorkOrderStatusEnum.COMPLETED),
|
||||
eq(OperatorTypeEnum.SYSTEM), isNull(),
|
||||
eq("系统自动完单")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAutoCompleteOrder_BlankRemark_DefaultReason() {
|
||||
// 准备
|
||||
OpsOrderDO order = buildSecurityOrder(TEST_ORDER_ID, WorkOrderStatusEnum.ARRIVED);
|
||||
orderDB.put(TEST_ORDER_ID, order);
|
||||
|
||||
// 执行:remark 为空白字符串
|
||||
securityOrderService.autoCompleteOrder(TEST_ORDER_ID, " ");
|
||||
|
||||
// 验证使用默认备注
|
||||
verify(orderStateMachine).transition(
|
||||
any(), eq(WorkOrderStatusEnum.COMPLETED),
|
||||
eq(OperatorTypeEnum.SYSTEM), isNull(),
|
||||
eq("系统自动完单")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAutoCompleteOrder_OrderNotFound_ThrowsException() {
|
||||
assertThrows(ServiceException.class,
|
||||
() -> securityOrderService.autoCompleteOrder(999L, "test"));
|
||||
}
|
||||
|
||||
// ==================== 人工完单测试 ====================
|
||||
|
||||
@Test
|
||||
void testManualCompleteOrder_WithImages_Success() {
|
||||
// 准备
|
||||
OpsOrderDO order = buildSecurityOrder(TEST_ORDER_ID, WorkOrderStatusEnum.ARRIVED);
|
||||
orderDB.put(TEST_ORDER_ID, order);
|
||||
|
||||
// 准备完单请求,包含result和图片
|
||||
SecurityOrderCompleteReqDTO req = new SecurityOrderCompleteReqDTO();
|
||||
req.setOrderId(TEST_ORDER_ID);
|
||||
req.setResult("已到现场排查,系误报");
|
||||
req.setResultImgUrls(Arrays.asList("https://oss/result1.jpg", "https://oss/result2.jpg"));
|
||||
req.setOperatorId(2001L);
|
||||
|
||||
// 执行
|
||||
securityOrderService.manualCompleteOrder(req);
|
||||
|
||||
// 验证状态机调用
|
||||
verify(orderStateMachine).transition(
|
||||
eq(order),
|
||||
eq(WorkOrderStatusEnum.COMPLETED),
|
||||
eq(OperatorTypeEnum.SECURITY_GUARD),
|
||||
eq(2001L),
|
||||
eq("安保人员提交处理结果")
|
||||
);
|
||||
|
||||
// 验证扩展表:写入 result + resultImgUrls(不含 completedTime)
|
||||
ArgumentCaptor<OpsOrderSecurityExtDO> extCaptor = ArgumentCaptor.forClass(OpsOrderSecurityExtDO.class);
|
||||
verify(securityExtMapper).insertOrUpdateSelective(extCaptor.capture());
|
||||
OpsOrderSecurityExtDO extUpdate = extCaptor.getValue();
|
||||
assertEquals(TEST_ORDER_ID, extUpdate.getOpsOrderId());
|
||||
assertEquals("已到现场排查,系误报", extUpdate.getResult());
|
||||
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
|
||||
void testManualCompleteOrder_WithoutImages_Success() {
|
||||
// 准备
|
||||
OpsOrderDO order = buildSecurityOrder(TEST_ORDER_ID, WorkOrderStatusEnum.ARRIVED);
|
||||
orderDB.put(TEST_ORDER_ID, order);
|
||||
|
||||
SecurityOrderCompleteReqDTO req = new SecurityOrderCompleteReqDTO();
|
||||
req.setOrderId(TEST_ORDER_ID);
|
||||
req.setResult("已处理完毕");
|
||||
req.setResultImgUrls(null); // 无图片
|
||||
req.setOperatorId(2001L);
|
||||
|
||||
// 执行
|
||||
securityOrderService.manualCompleteOrder(req);
|
||||
|
||||
// 验证扩展表:result 有值,resultImgUrls 为 null
|
||||
ArgumentCaptor<OpsOrderSecurityExtDO> extCaptor = ArgumentCaptor.forClass(OpsOrderSecurityExtDO.class);
|
||||
verify(securityExtMapper).insertOrUpdateSelective(extCaptor.capture());
|
||||
assertEquals("已处理完毕", extCaptor.getValue().getResult());
|
||||
assertNull(extCaptor.getValue().getResultImgUrls());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testManualCompleteOrder_EmptyImagesList_NoJsonWrite() {
|
||||
// 准备
|
||||
OpsOrderDO order = buildSecurityOrder(TEST_ORDER_ID, WorkOrderStatusEnum.ARRIVED);
|
||||
orderDB.put(TEST_ORDER_ID, order);
|
||||
|
||||
SecurityOrderCompleteReqDTO req = new SecurityOrderCompleteReqDTO();
|
||||
req.setOrderId(TEST_ORDER_ID);
|
||||
req.setResult("已处理");
|
||||
req.setResultImgUrls(Arrays.asList()); // 空列表
|
||||
req.setOperatorId(2001L);
|
||||
|
||||
// 执行
|
||||
securityOrderService.manualCompleteOrder(req);
|
||||
|
||||
// 验证:空列表不写入 resultImgUrls
|
||||
ArgumentCaptor<OpsOrderSecurityExtDO> extCaptor = ArgumentCaptor.forClass(OpsOrderSecurityExtDO.class);
|
||||
verify(securityExtMapper).insertOrUpdateSelective(extCaptor.capture());
|
||||
assertNull(extCaptor.getValue().getResultImgUrls());
|
||||
}
|
||||
|
||||
// ==================== 查询测试 ====================
|
||||
|
||||
@Test
|
||||
void testGetSecurityExt_Exists() {
|
||||
OpsOrderSecurityExtDO ext = OpsOrderSecurityExtDO.builder()
|
||||
.id(1L).opsOrderId(TEST_ORDER_ID).alarmId("ALM001").build();
|
||||
when(securityExtMapper.selectByOpsOrderId(TEST_ORDER_ID)).thenReturn(ext);
|
||||
|
||||
OpsOrderSecurityExtDO result = securityOrderService.getSecurityExt(TEST_ORDER_ID);
|
||||
assertNotNull(result);
|
||||
assertEquals("ALM001", result.getAlarmId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetSecurityExt_NotExists() {
|
||||
when(securityExtMapper.selectByOpsOrderId(999L)).thenReturn(null);
|
||||
|
||||
OpsOrderSecurityExtDO result = securityOrderService.getSecurityExt(999L);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
// ==================== 辅助方法 ====================
|
||||
|
||||
private OpsOrderDO buildSecurityOrder(Long orderId, WorkOrderStatusEnum status) {
|
||||
return OpsOrderDO.builder()
|
||||
.id(orderId)
|
||||
.orderCode(TEST_ORDER_CODE)
|
||||
.orderType("SECURITY")
|
||||
.sourceType(SourceTypeEnum.ALARM.getType())
|
||||
.title("安保测试工单")
|
||||
.priority(PriorityEnum.P1.getPriority())
|
||||
.status(status.getStatus())
|
||||
.areaId(100L)
|
||||
.location("A栋3层")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user