package com.zksy.web.controller.cockpit; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.zksy.base.alarm.domain.AlarmData; import com.zksy.base.alarm.service.AlarmDataService; import com.zksy.base.domain.EquipmentBase; import com.zksy.base.mapper.EquipmentBaseMapper; import com.zksy.base.warning.mapper.AlarmWarningRelMapper; import com.zksy.base.warning.service.EarlyWarningService; import com.zksy.cockpit.workorder.BusinessCockpitWorkOrderReadAdapter; import com.zksy.cockpit.workorder.CockpitWorkOrderReadService; import com.zksy.ops.domain.WorkOrder; import com.zksy.ops.service.WorkOrderService; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.apache.ibatis.session.Configuration; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.time.Clock; import java.time.Instant; import java.time.ZoneOffset; import java.util.Arrays; import java.util.Collections; import java.util.Date; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** * 全量工单列表端点(票 65):报警效率面板的「当前工单列表」。 * *

锁四件事,都是**会真的画错界面**的那几件: *

    *
  1. 不按设备/事件筛——全量口径(查询条件里不得出现 device_id);
  2. *
  3. 总数取独立 count,不由本页条数反推(票 61 纪律);
  4. *
  5. 非法 page/pageSize/stateCode 被夹到合法区间并**回显实际生效值**;
  6. *
  7. 状态字典与每张工单的关联标记照常给出(卡片要按同一份词表上色)。
  8. *
*/ class CockpitWorkOrderListContractTest { private static final String BASE = "/api/cockpit/v1"; private static final String ORDER_CODE = "WO202610101511303516"; private static final String DEVICE_CODE = "21119740"; private static final String ALARM_ID = "cd57be18d02f7d3390fb98539b93"; private static final String CREATE_TIME = "2026-10-10T15:11:30+08:00"; private WorkOrderService workOrderService; private AlarmDataService alarmDataService; private MockMvc mockMvc; @BeforeEach void setUp() { for (Class entity : Arrays.asList(EquipmentBase.class, WorkOrder.class, AlarmData.class)) { TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new Configuration(), ""), entity); } workOrderService = mock(WorkOrderService.class); alarmDataService = mock(AlarmDataService.class); EquipmentBaseMapper equipmentMapper = mock(EquipmentBaseMapper.class); EquipmentBase equipment = new EquipmentBase(); equipment.setEquipmentId("equipment-id"); equipment.setEquipmentCode(DEVICE_CODE); when(equipmentMapper.selectById("equipment-id")).thenReturn(equipment); WorkOrder order = new WorkOrder(); order.setOrderId(279L); order.setOrderNo(ORDER_CODE); order.setDeviceId("equipment-id"); order.setOrderLevel(2); order.setOrderDesc("监测报警,请现场核实处理"); order.setOrderStatus(0); order.setCreateTime(Date.from(Instant.parse("2026-10-10T07:11:30Z"))); order.setUpdateTime(Date.from(Instant.parse("2026-10-10T07:11:30Z"))); when(workOrderService.count(any(LambdaQueryWrapper.class))).thenReturn(7L); when(workOrderService.list(any(LambdaQueryWrapper.class))) .thenReturn(Collections.singletonList(order)); AlarmData alarm = new AlarmData(); alarm.setId(ALARM_ID); alarm.setWorkOrderId(279L); when(alarmDataService.list(any(LambdaQueryWrapper.class))) .thenReturn(Collections.singletonList(alarm)); BusinessCockpitWorkOrderReadAdapter adapter = new BusinessCockpitWorkOrderReadAdapter( workOrderService, mock(EarlyWarningService.class), mock(AlarmWarningRelMapper.class), alarmDataService, equipmentMapper); CockpitWorkOrderReadService service = new CockpitWorkOrderReadService( adapter, Clock.fixed(Instant.parse("2026-10-10T07:11:30Z"), ZoneOffset.UTC)); mockMvc = MockMvcBuilders.standaloneSetup(new CockpitWorkOrderReadController(service)).build(); } @Test void listIsGlobalNotScopedToDeviceOrEvent() throws Exception { mockMvc.perform(get(BASE + "/work-orders")) .andExpect(status().isOk()) .andExpect(jsonPath("$.data.state").value("READY")) .andExpect(jsonPath("$.data.workOrders[0].workOrderCode").value(ORDER_CODE)) // 总数是 count 的结果(7),不是本页 1 条 .andExpect(jsonPath("$.data.pagination.total").value(7)) .andExpect(jsonPath("$.data.pagination.page").value(1)) .andExpect(jsonPath("$.data.pagination.pageSize").value(10)) .andExpect(jsonPath("$.data.pagination.totalPages").value(1)) // 状态字典必须给(卡片按同一词表上色) .andExpect(jsonPath("$.data.stateDictionary[0].code").value(0)); } @Test void pageAndSizeAreClampedAndEchoedBack() throws Exception { mockMvc.perform(get(BASE + "/work-orders").param("page", "0").param("pageSize", "9999")) .andExpect(status().isOk()) .andExpect(jsonPath("$.data.pagination.page").value(1)) .andExpect(jsonPath("$.data.pagination.pageSize").value(100)); // 夹取后才下到适配器:offset 必须由生效值算出,不能让非法值进 SQL verify(workOrderService, org.mockito.Mockito.atLeastOnce()) .list(any(LambdaQueryWrapper.class)); } @Test void invalidStateCodeFallsBackToNoFilter() throws Exception { mockMvc.perform(get(BASE + "/work-orders").param("stateCode", "99")) .andExpect(status().isOk()); // 越界状态码当成"不筛",仍然返回数据(而不是空列表假读数) verify(workOrderService, org.mockito.Mockito.atLeastOnce()).count(any(LambdaQueryWrapper.class)); } @Test void validStateCodeIsApplied() throws Exception { mockMvc.perform(get(BASE + "/work-orders").param("stateCode", "3")) .andExpect(status().isOk()); verify(workOrderService).count(any(LambdaQueryWrapper.class)); } }