|
@@ -1,8 +1,14 @@
|
|
|
package com.zksy.web.controller.cockpit;
|
|
package com.zksy.web.controller.cockpit;
|
|
|
|
|
|
|
|
|
|
+import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
|
|
|
|
+import org.apache.ibatis.builder.MapperBuilderAssistant;
|
|
|
import org.mockito.ArgumentCaptor;
|
|
import org.mockito.ArgumentCaptor;
|
|
|
|
|
+import com.zksy.base.alarm.domain.AlarmData;
|
|
|
|
|
+import com.zksy.base.alarm.service.AlarmDataService;
|
|
|
import com.zksy.base.domain.EquipmentBase;
|
|
import com.zksy.base.domain.EquipmentBase;
|
|
|
import com.zksy.base.domain.EquipmentStatus;
|
|
import com.zksy.base.domain.EquipmentStatus;
|
|
|
import com.zksy.base.service.EquipmentStatusService;
|
|
import com.zksy.base.service.EquipmentStatusService;
|
|
@@ -19,6 +25,7 @@ import org.junit.jupiter.api.Test;
|
|
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
import java.math.BigDecimal;
|
|
|
import java.time.LocalDateTime;
|
|
import java.time.LocalDateTime;
|
|
|
|
|
+import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
import java.util.Collections;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
|
|
|
@@ -40,6 +47,7 @@ class BusinessCockpitDeviceFactResolverTest {
|
|
|
private EquipmentStatusService equipmentStatusService;
|
|
private EquipmentStatusService equipmentStatusService;
|
|
|
private EarlyWarningService earlyWarningService;
|
|
private EarlyWarningService earlyWarningService;
|
|
|
private WorkOrderService workOrderService;
|
|
private WorkOrderService workOrderService;
|
|
|
|
|
+ private AlarmDataService alarmDataService;
|
|
|
private BusinessCockpitDeviceFactResolver resolver;
|
|
private BusinessCockpitDeviceFactResolver resolver;
|
|
|
|
|
|
|
|
@BeforeEach
|
|
@BeforeEach
|
|
@@ -47,8 +55,9 @@ class BusinessCockpitDeviceFactResolverTest {
|
|
|
equipmentStatusService = mock(EquipmentStatusService.class);
|
|
equipmentStatusService = mock(EquipmentStatusService.class);
|
|
|
earlyWarningService = mock(EarlyWarningService.class);
|
|
earlyWarningService = mock(EarlyWarningService.class);
|
|
|
workOrderService = mock(WorkOrderService.class);
|
|
workOrderService = mock(WorkOrderService.class);
|
|
|
|
|
+ alarmDataService = mock(AlarmDataService.class);
|
|
|
resolver = new BusinessCockpitDeviceFactResolver(
|
|
resolver = new BusinessCockpitDeviceFactResolver(
|
|
|
- equipmentStatusService, earlyWarningService, workOrderService);
|
|
|
|
|
|
|
+ equipmentStatusService, earlyWarningService, workOrderService, alarmDataService);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
@Test
|
|
@@ -75,10 +84,168 @@ class BusinessCockpitDeviceFactResolverTest {
|
|
|
assertEquals("VALID", fact.getCoordinateValidation().getState());
|
|
assertEquals("VALID", fact.getCoordinateValidation().getState());
|
|
|
assertNull(fact.getCoordinateValidation().getReason());
|
|
assertNull(fact.getCoordinateValidation().getReason());
|
|
|
assertTrue(fact.getCoordinateValidation().isRenderable());
|
|
assertTrue(fact.getCoordinateValidation().isRenderable());
|
|
|
|
|
+ // 本例未桩 alarm_data → 未处理报警不可知 → 保守为「未承接」,仍判 ALARM。
|
|
|
|
|
+ assertFalse(fact.isAlarmsCoveredByOpenWorkOrders(),
|
|
|
|
|
+ "读不到未处理报警行时不得推定已承接");
|
|
|
|
|
+ assertEquals(CockpitMonitoringPointStatus.ALARM,
|
|
|
|
|
+ CockpitMonitoringPointStatusResolver.deviceStatus(fact));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ---- 票 52:单设备「全部未处理报警都由未结束工单承接」时判工单处置 ----
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void allUnhandledAlarmsCoveredByOpenWorkOrdersResolveToWorkOrder() {
|
|
|
|
|
+ when(equipmentStatusService.getByDeviceCode("DEV-T52-1")).thenReturn(status(1, 1, 1));
|
|
|
|
|
+ when(earlyWarningService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+ // 两条未处理报警都指向未结束工单(0 待派单 / 3 处理中)。
|
|
|
|
|
+ when(alarmDataService.list(any(Wrapper.class)))
|
|
|
|
|
+ .thenReturn(Arrays.asList(alarm("A-1", 0, 101L), alarm("A-2", 0, 102L)));
|
|
|
|
|
+ when(workOrderService.list(any(Wrapper.class)))
|
|
|
|
|
+ .thenReturn(Arrays.asList(order(101L, 0), order(102L, 3)));
|
|
|
|
|
+
|
|
|
|
|
+ CockpitMonitoringPointDeviceSnapshot fact = resolver.resolve(device("DEV-T52-1"), null);
|
|
|
|
|
+
|
|
|
|
|
+ assertTrue(fact.isAlarmsCoveredByOpenWorkOrders(), "全部未处理报警都有未结束工单承接");
|
|
|
|
|
+ assertEquals(CockpitMonitoringPointStatus.WORK_ORDER,
|
|
|
|
|
+ CockpitMonitoringPointStatusResolver.deviceStatus(fact),
|
|
|
|
|
+ "承接完整时单设备状态=工单处置(高于报警/预警)");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void unhandledAlarmWithoutWorkOrderStaysAlarm() {
|
|
|
|
|
+ when(equipmentStatusService.getByDeviceCode("DEV-T52-2")).thenReturn(status(1, 1, 1));
|
|
|
|
|
+ when(earlyWarningService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+ when(alarmDataService.list(any(Wrapper.class)))
|
|
|
|
|
+ .thenReturn(Collections.singletonList(alarm("A-1", 0, null)));
|
|
|
|
|
+ when(workOrderService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+
|
|
|
|
|
+ CockpitMonitoringPointDeviceSnapshot fact = resolver.resolve(device("DEV-T52-2"), null);
|
|
|
|
|
+
|
|
|
|
|
+ assertFalse(fact.isAlarmsCoveredByOpenWorkOrders(), "有未处理报警无工单承接");
|
|
|
|
|
+ assertEquals(CockpitMonitoringPointStatus.ALARM,
|
|
|
|
|
+ CockpitMonitoringPointStatusResolver.deviceStatus(fact));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void mixedCoverageStaysAlarm() {
|
|
|
|
|
+ when(equipmentStatusService.getByDeviceCode("DEV-T52-3")).thenReturn(status(1, 1, 1));
|
|
|
|
|
+ when(earlyWarningService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+ // 一条有未结束工单、一条没有 → 混合,不成立(要求「每一条」)。
|
|
|
|
|
+ when(alarmDataService.list(any(Wrapper.class)))
|
|
|
|
|
+ .thenReturn(Arrays.asList(alarm("A-1", 0, 101L), alarm("A-2", 0, null)));
|
|
|
|
|
+ when(workOrderService.list(any(Wrapper.class)))
|
|
|
|
|
+ .thenReturn(Collections.singletonList(order(101L, 0)));
|
|
|
|
|
+
|
|
|
|
|
+ CockpitMonitoringPointDeviceSnapshot fact = resolver.resolve(device("DEV-T52-3"), null);
|
|
|
|
|
+
|
|
|
|
|
+ assertFalse(fact.isAlarmsCoveredByOpenWorkOrders(), "混合覆盖不满足「全部」");
|
|
|
|
|
+ assertEquals(CockpitMonitoringPointStatus.ALARM,
|
|
|
|
|
+ CockpitMonitoringPointStatusResolver.deviceStatus(fact));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void closedWorkOrderDoesNotCountAsCoverage() {
|
|
|
|
|
+ when(equipmentStatusService.getByDeviceCode("DEV-T52-4")).thenReturn(status(1, 1, 1));
|
|
|
|
|
+ when(earlyWarningService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+ // 关联的是已结案工单(5)→ 不算承接,处置并未在进行。
|
|
|
|
|
+ when(alarmDataService.list(any(Wrapper.class)))
|
|
|
|
|
+ .thenReturn(Collections.singletonList(alarm("A-1", 0, 101L)));
|
|
|
|
|
+ when(workOrderService.list(any(Wrapper.class)))
|
|
|
|
|
+ .thenReturn(Collections.singletonList(order(101L, 5)));
|
|
|
|
|
+
|
|
|
|
|
+ CockpitMonitoringPointDeviceSnapshot fact = resolver.resolve(device("DEV-T52-4"), null);
|
|
|
|
|
+
|
|
|
|
|
+ assertFalse(fact.isAlarmsCoveredByOpenWorkOrders(), "已结案工单不是未结束承接");
|
|
|
|
|
+ assertEquals(CockpitMonitoringPointStatus.ALARM,
|
|
|
|
|
+ CockpitMonitoringPointStatusResolver.deviceStatus(fact));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void cancelledWorkOrderDoesNotCountAsCoverage() {
|
|
|
|
|
+ when(equipmentStatusService.getByDeviceCode("DEV-T52-5")).thenReturn(status(1, 1, 1));
|
|
|
|
|
+ when(earlyWarningService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+ when(alarmDataService.list(any(Wrapper.class)))
|
|
|
|
|
+ .thenReturn(Collections.singletonList(alarm("A-1", 0, 101L)));
|
|
|
|
|
+ when(workOrderService.list(any(Wrapper.class)))
|
|
|
|
|
+ .thenReturn(Collections.singletonList(order(101L, 7)));
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(CockpitMonitoringPointStatus.ALARM,
|
|
|
|
|
+ CockpitMonitoringPointStatusResolver.deviceStatus(
|
|
|
|
|
+ resolver.resolve(device("DEV-T52-5"), null)),
|
|
|
|
|
+ "0-based 的 7=已取消 是终态,不算承接");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void alarmStatusFlagWithoutUnhandledAlarmRowsStaysAlarm() {
|
|
|
|
|
+ when(equipmentStatusService.getByDeviceCode("DEV-T52-6")).thenReturn(status(1, 1, 1));
|
|
|
|
|
+ when(earlyWarningService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+ // 设备报警标志为真,但查不到未处理报警行(历史流水全部已处理)→ 不推定已承接。
|
|
|
|
|
+ when(alarmDataService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+ when(workOrderService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+
|
|
|
|
|
+ CockpitMonitoringPointDeviceSnapshot fact = resolver.resolve(device("DEV-T52-6"), null);
|
|
|
|
|
+
|
|
|
|
|
+ assertFalse(fact.isAlarmsCoveredByOpenWorkOrders(), "查不到未处理报警 ≠ 已承接");
|
|
|
assertEquals(CockpitMonitoringPointStatus.ALARM,
|
|
assertEquals(CockpitMonitoringPointStatus.ALARM,
|
|
|
CockpitMonitoringPointStatusResolver.deviceStatus(fact));
|
|
CockpitMonitoringPointStatusResolver.deviceStatus(fact));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void alarmDataReadFailureDegradesToAlarmNotWorkOrder() {
|
|
|
|
|
+ when(equipmentStatusService.getByDeviceCode("DEV-T52-7")).thenReturn(status(1, 1, 1));
|
|
|
|
|
+ when(earlyWarningService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+ when(alarmDataService.list(any(Wrapper.class)))
|
|
|
|
|
+ .thenThrow(new RuntimeException("db down"));
|
|
|
|
|
+
|
|
|
|
|
+ CockpitMonitoringPointDeviceSnapshot fact = resolver.resolve(device("DEV-T52-7"), null);
|
|
|
|
|
+
|
|
|
|
|
+ assertFalse(fact.isAlarmsCoveredByOpenWorkOrders(), "读失败按未承接保守降级");
|
|
|
|
|
+ assertEquals(CockpitMonitoringPointStatus.ALARM,
|
|
|
|
|
+ CockpitMonitoringPointStatusResolver.deviceStatus(fact));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void noAlarmFlagKeepsExistingOrder() {
|
|
|
|
|
+ when(equipmentStatusService.getByDeviceCode("DEV-T52-8")).thenReturn(status(1, 0, 1));
|
|
|
|
|
+ when(earlyWarningService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+ when(alarmDataService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+ when(workOrderService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(CockpitMonitoringPointStatus.NORMAL,
|
|
|
|
|
+ CockpitMonitoringPointStatusResolver.deviceStatus(
|
|
|
|
|
+ resolver.resolve(device("DEV-T52-8"), null)),
|
|
|
|
|
+ "无报警时维持现状(正常)");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 报警范围必须**只取未处理**({@code alarm_status = 0})。夹具用同一个 mock 返回,
|
|
|
|
|
+ * 若不钉住查询条件,把过滤去掉也测不出来(变异 M5 就是这么暴露的)。
|
|
|
|
|
+ */
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void unhandledAlarmQueryFiltersOnAlarmStatusZero() {
|
|
|
|
|
+ when(equipmentStatusService.getByDeviceCode("DEV-T52-9")).thenReturn(status(1, 1, 1));
|
|
|
|
|
+ when(earlyWarningService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+ when(alarmDataService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+ when(workOrderService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
|
|
|
|
|
+
|
|
|
|
|
+ resolver.resolve(device("DEV-T52-9"), null);
|
|
|
|
|
+
|
|
|
|
|
+ // 纯单测没有 Spring/MyBatis 启动,LambdaQueryWrapper 的实体缓存需手动登记,
|
|
|
|
|
+ // 否则 getSqlSegment() 取列名会抛 "can not find lambda cache"。
|
|
|
|
|
+ TableInfoHelper.initTableInfo(
|
|
|
|
|
+ new MapperBuilderAssistant(new MybatisConfiguration(), ""), AlarmData.class);
|
|
|
|
|
+
|
|
|
|
|
+ ArgumentCaptor<Wrapper> alarmQuery = ArgumentCaptor.forClass(Wrapper.class);
|
|
|
|
|
+ verify(alarmDataService).list(alarmQuery.capture());
|
|
|
|
|
+ // 现场实现用的是 LambdaQueryWrapper,与 QueryWrapper 是并列实现;
|
|
|
|
|
+ // 条件与取值都在共同父类 AbstractWrapper 上。
|
|
|
|
|
+ AbstractWrapper<?, ?, ?> captured = (AbstractWrapper<?, ?, ?>) alarmQuery.getValue();
|
|
|
|
|
+ String segment = captured.getSqlSegment();
|
|
|
|
|
+ assertTrue(segment.contains("device_code"), "报警按设备编码取:" + segment);
|
|
|
|
|
+ assertTrue(segment.contains("alarm_status"), "必须只取未处理报警(alarm_status):" + segment);
|
|
|
|
|
+ assertTrue(captured.getParamNameValuePairs().containsValue(0), "未处理=alarm_status 0");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@Test
|
|
@Test
|
|
|
void offlineStatusResolvesToOffline() {
|
|
void offlineStatusResolvesToOffline() {
|
|
|
when(equipmentStatusService.getByDeviceCode("DEV-2")).thenReturn(status(1, 0, 0));
|
|
when(equipmentStatusService.getByDeviceCode("DEV-2")).thenReturn(status(1, 0, 0));
|
|
@@ -182,8 +349,22 @@ class BusinessCockpitDeviceFactResolverTest {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private static WorkOrder order(int status) {
|
|
private static WorkOrder order(int status) {
|
|
|
|
|
+ return order(null, status);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static WorkOrder order(Long orderId, int status) {
|
|
|
WorkOrder order = new WorkOrder();
|
|
WorkOrder order = new WorkOrder();
|
|
|
|
|
+ order.setOrderId(orderId);
|
|
|
order.setOrderStatus(status);
|
|
order.setOrderStatus(status);
|
|
|
return order;
|
|
return order;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ private static AlarmData alarm(String id, int alarmStatus, Long workOrderId) {
|
|
|
|
|
+ AlarmData alarm = new AlarmData();
|
|
|
|
|
+ alarm.setId(id);
|
|
|
|
|
+ alarm.setDeviceCode("DEV");
|
|
|
|
|
+ alarm.setAlarmStatus(alarmStatus);
|
|
|
|
|
+ alarm.setWorkOrderId(workOrderId);
|
|
|
|
|
+ return alarm;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|