实现测试必填设备绑定
新增独立接收状态、采集器通道映射和电池组字段快照。
补充数据库迁移、后端校验以及服务和映射契约测试。
| New file |
| | |
| | | -- 旧监测列保留给既有数据;新版业务从独立接收状态开始演进。 |
| | | ALTER TABLE t_test |
| | | ADD COLUMN reception_status VARCHAR(32) NOT NULL DEFAULT 'NOT_STARTED' COMMENT '数据接收状态' AFTER monitoring_status, |
| | | ADD CONSTRAINT chk_reception_status |
| | | CHECK (reception_status IN ('NOT_STARTED', 'RECEIVING', 'PAUSED', 'ENDED')); |
| | | |
| | | CREATE TABLE t_test_collector_channel ( |
| | | mapping_id BIGINT NOT NULL AUTO_INCREMENT, |
| | | test_id BIGINT NOT NULL, |
| | | battery_relation_id BIGINT NOT NULL, |
| | | collector_id BIGINT NOT NULL, |
| | | measurement_code VARCHAR(100) NOT NULL, |
| | | measurement_name VARCHAR(100) NOT NULL, |
| | | channel_code VARCHAR(32) NOT NULL, |
| | | PRIMARY KEY (mapping_id), |
| | | UNIQUE KEY uk_test_collector_measurement (battery_relation_id, measurement_code), |
| | | UNIQUE KEY uk_test_collector_channel (test_id, collector_id, channel_code), |
| | | KEY idx_test_collector_mapping (test_id, battery_relation_id), |
| | | CONSTRAINT fk_test_collector_mapping_test FOREIGN KEY (test_id) REFERENCES t_test (test_id), |
| | | CONSTRAINT fk_test_collector_mapping_battery FOREIGN KEY (battery_relation_id) REFERENCES t_test_battery (relation_id), |
| | | CONSTRAINT fk_test_collector_mapping_collector FOREIGN KEY (collector_id) REFERENCES t_collector (collector_id) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='测试采集器通道映射'; |
| | | |
| | | CREATE TABLE t_test_field_snapshot ( |
| | | snapshot_id BIGINT NOT NULL AUTO_INCREMENT, |
| | | test_id BIGINT NOT NULL, |
| | | field_definition_id BIGINT NOT NULL, |
| | | field_name VARCHAR(100) NOT NULL, |
| | | field_type VARCHAR(20) NOT NULL, |
| | | field_value VARCHAR(500) NULL, |
| | | PRIMARY KEY (snapshot_id), |
| | | UNIQUE KEY uk_test_field_snapshot (test_id, field_definition_id), |
| | | KEY idx_test_field_snapshot_test (test_id), |
| | | CONSTRAINT fk_test_field_snapshot_test FOREIGN KEY (test_id) REFERENCES t_test (test_id) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='测试共享业务字段快照'; |
| | |
| | | assertTrue(v12.contains("chk_battery_group_field_type")); |
| | | assertTrue(v12.contains("'text', 'number', 'time', 'remark'")); |
| | | assertFalse(v12.matches("(?s).*\\bINSERT\\s+INTO\\b.*")); |
| | | |
| | | String v13 = resource("db/migration/V13__test_required_device_bindings.sql"); |
| | | assertTrue(v13.contains("reception_status")); |
| | | assertTrue(v13.contains("DEFAULT 'NOT_STARTED'")); |
| | | assertTrue(v13.contains("CREATE TABLE t_test_collector_channel")); |
| | | assertTrue(v13.contains("uk_test_collector_channel")); |
| | | assertTrue(v13.contains("CREATE TABLE t_test_field_snapshot")); |
| | | assertFalse(v13.matches("(?s).*\\bINSERT\\s+INTO\\b.*")); |
| | | assertFalse(v13.matches("(?s).*\\bUPDATE\\s+t_test\\b.*")); |
| | | } |
| | | |
| | | private static String sha256(String path) |
| New file |
| | |
| | | package com.ruoyi.config; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertTrue; |
| | | |
| | | import java.io.InputStream; |
| | | import java.nio.charset.StandardCharsets; |
| | | import org.junit.jupiter.api.Test; |
| | | |
| | | /** 测试双设备绑定、字段快照和接收状态的 Mapper 契约。 */ |
| | | class TestDeviceBindingMapperContractTest |
| | | { |
| | | @Test |
| | | void persistsAndReadsCollectorMappingsAndFieldSnapshots() |
| | | { |
| | | String mapper = resource("mapper/system/TTestMapper.xml"); |
| | | |
| | | assertTrue(mapper.contains("id=\"insertTestCollectorChannel\"")); |
| | | assertTrue(mapper.contains("id=\"selectTestCollectorChannels\"")); |
| | | assertTrue(mapper.contains("id=\"insertTestFieldSnapshot\"")); |
| | | assertTrue(mapper.contains("id=\"selectTestFieldSnapshots\"")); |
| | | assertTrue(mapper.contains("reception_status = 'NOT_STARTED'")); |
| | | assertTrue(mapper.contains("channel_count channelCount")); |
| | | } |
| | | |
| | | private static String resource(String path) |
| | | { |
| | | try (InputStream stream = TestDeviceBindingMapperContractTest.class.getClassLoader().getResourceAsStream(path)) |
| | | { |
| | | if (stream == null) throw new AssertionError("缺少资源 " + path); |
| | | return new String(stream.readAllBytes(), StandardCharsets.UTF_8); |
| | | } |
| | | catch (Exception exception) |
| | | { |
| | | throw new AssertionError("无法读取资源 " + path, exception); |
| | | } |
| | | } |
| | | } |
| | |
| | | import static org.mockito.Mockito.never; |
| | | import static org.mockito.Mockito.verify; |
| | | import static org.mockito.Mockito.when; |
| | | import static org.mockito.Mockito.times; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Collections; |
| | |
| | | import org.mockito.junit.jupiter.MockitoExtension; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.system.domain.TTest; |
| | | import com.ruoyi.system.domain.TBatteryGroup; |
| | | import com.ruoyi.system.domain.TBatteryGroupRelationField; |
| | | import com.ruoyi.system.domain.TTestBattery; |
| | | import com.ruoyi.system.domain.TTestChannelBinding; |
| | | import com.ruoyi.system.domain.TTestCollectorChannel; |
| | | import com.ruoyi.system.domain.TTestFieldSnapshot; |
| | | import com.ruoyi.system.domain.TTestPlanStep; |
| | | import com.ruoyi.system.domain.TTestWarningThreshold; |
| | | import com.ruoyi.system.mapper.TTestMapper; |
| | | import com.ruoyi.system.service.ITBatteryGroupService; |
| | | |
| | | /** 测试配置服务的业务契约测试。 */ |
| | | @ExtendWith(MockitoExtension.class) |
| | |
| | | { |
| | | @Mock |
| | | private TTestMapper testMapper; |
| | | |
| | | @Mock |
| | | private ITBatteryGroupService batteryGroupService; |
| | | |
| | | @InjectMocks |
| | | private TTestServiceImpl testService; |
| | |
| | | { |
| | | TTest test = validTest(); |
| | | mockEnabledOptions(); |
| | | mockGroupSnapshot(); |
| | | when(testMapper.insertTest(any(TTest.class))).thenAnswer(invocation -> { |
| | | invocation.<TTest>getArgument(0).setTestId(10L); |
| | | return 1; |
| | |
| | | |
| | | assertEquals(10L, testService.createTest(test)); |
| | | assertEquals("WAITING", test.getTestStatus()); |
| | | assertEquals("NOT_MONITORED", test.getMonitoringStatus()); |
| | | assertEquals("NOT_STARTED", test.getReceptionStatus()); |
| | | assertEquals(7L, test.getCreatorId()); |
| | | verify(testMapper).insertTestPlanStep(any(TTestPlanStep.class)); |
| | | verify(testMapper).insertTestChannelBinding(any(TTestChannelBinding.class)); |
| | | verify(testMapper, times(3)).insertTestCollectorChannel(any(TTestCollectorChannel.class)); |
| | | verify(testMapper).insertTestFieldSnapshot(any()); |
| | | verify(testMapper).insertTestThreshold(any(TTestWarningThreshold.class)); |
| | | verify(testMapper).insertTestAudit(anyLong(), any(), anyLong(), any(), isNull(), any()); |
| | | } |
| | | |
| | | @Test |
| | | void createShouldAllowMissingCollector() |
| | | void createShouldRejectMissingCollector() |
| | | { |
| | | TTest test = validTest(); |
| | | test.getBatteries().get(0).setCollectorId(null); |
| | | mockEnabledPeopleAndType(); |
| | | when(testMapper.countBatteryInEnabledGroup(1L, "BAT-001")).thenReturn(1); |
| | | when(testMapper.countRegisteredChannel(100L)).thenReturn(1); |
| | | when(testMapper.insertTest(any(TTest.class))).thenAnswer(invocation -> { |
| | | invocation.<TTest>getArgument(0).setTestId(10L); |
| | | return 1; |
| | | }); |
| | | doAnswer(invocation -> { |
| | | invocation.<TTestBattery>getArgument(0).setRelationId(20L); |
| | | return 1; |
| | | }).when(testMapper).insertTestBattery(any(TTestBattery.class)); |
| | | |
| | | assertEquals(10L, testService.createTest(test)); |
| | | ServiceException exception = assertThrows(ServiceException.class, () -> testService.createTest(test)); |
| | | |
| | | verify(testMapper, never()).countCollectorEnabled(anyLong()); |
| | | assertEquals("采集器不能为空", exception.getMessage()); |
| | | verify(testMapper, never()).insertTest(any(TTest.class)); |
| | | } |
| | | |
| | | @Test |
| | | void createShouldRejectMissingRequiredCollectorChannel() |
| | | { |
| | | TTest test = validTest(); |
| | | test.getBatteries().get(0).getCollectorChannels().remove(0); |
| | | mockEnabledOptions(); |
| | | |
| | | ServiceException exception = assertThrows(ServiceException.class, () -> testService.createTest(test)); |
| | | |
| | | assertEquals("每个电池号必须绑定主温度、正电位主通道和负电位主通道", exception.getMessage()); |
| | | verify(testMapper, never()).insertTest(any(TTest.class)); |
| | | } |
| | | |
| | | @Test |
| | |
| | | TTest test = validTest(); |
| | | test.setPlanSteps(Collections.emptyList()); |
| | | mockEnabledOptions(); |
| | | mockGroupSnapshot(); |
| | | when(testMapper.insertTest(any(TTest.class))).thenAnswer(invocation -> { |
| | | invocation.<TTest>getArgument(0).setTestId(10L); |
| | | return 1; |
| | |
| | | when(testMapper.countBatteryInEnabledGroup(1L, "BAT-002")).thenReturn(1); |
| | | when(testMapper.countRegisteredChannel(100L)).thenReturn(1); |
| | | when(testMapper.countCollectorEnabled(8L)).thenReturn(1); |
| | | when(testMapper.selectCollectorChannelCount(8L)).thenReturn(4); |
| | | |
| | | ServiceException exception = assertThrows(ServiceException.class, () -> testService.createTest(test)); |
| | | |
| | | assertEquals("同一检测柜通道不可重复绑定", exception.getMessage()); |
| | | verify(testMapper, never()).insertTest(any(TTest.class)); |
| | | } |
| | | |
| | | @Test |
| | | void createShouldRejectDuplicateCollectorChannelMapping() |
| | | { |
| | | TTest test = validTest(); |
| | | TTestBattery second = validBattery("BAT-002", 2L, 101L); |
| | | test.setBatteries(java.util.List.of(test.getBatteries().get(0), second)); |
| | | mockEnabledPeopleAndType(); |
| | | when(testMapper.countBatteryInEnabledGroup(1L, "BAT-001")).thenReturn(1); |
| | | when(testMapper.countBatteryInEnabledGroup(1L, "BAT-002")).thenReturn(1); |
| | | when(testMapper.countRegisteredChannel(100L)).thenReturn(1); |
| | | when(testMapper.countRegisteredChannel(101L)).thenReturn(1); |
| | | when(testMapper.countCollectorEnabled(8L)).thenReturn(1); |
| | | when(testMapper.selectCollectorChannelCount(8L)).thenReturn(4); |
| | | |
| | | ServiceException exception = assertThrows(ServiceException.class, () -> testService.createTest(test)); |
| | | |
| | | assertEquals("同一采集器通道不可重复映射", exception.getMessage()); |
| | | verify(testMapper, never()).insertTest(any(TTest.class)); |
| | | } |
| | | |
| | |
| | | when(testMapper.countBatteryInEnabledGroup(1L, "BAT-001")).thenReturn(1); |
| | | when(testMapper.countRegisteredChannel(100L)).thenReturn(1); |
| | | when(testMapper.countCollectorEnabled(8L)).thenReturn(1); |
| | | when(testMapper.selectCollectorChannelCount(8L)).thenReturn(4); |
| | | |
| | | ServiceException exception = assertThrows(ServiceException.class, () -> testService.createTest(test)); |
| | | |
| | |
| | | } |
| | | |
| | | @Test |
| | | void updateShouldRejectConfigurationAfterMonitoringStarted() |
| | | void updateShouldRejectConfigurationAfterReceptionStarted() |
| | | { |
| | | TTest request = validTest(); |
| | | request.setTestId(10L); |
| | | TTest current = validTest(); |
| | | current.setTestId(10L); |
| | | current.setMonitoringStatus("MONITORING"); |
| | | current.setReceptionStatus("RECEIVING"); |
| | | when(testMapper.selectTestByIdForUpdate(10L)).thenReturn(current); |
| | | |
| | | ServiceException exception = assertThrows(ServiceException.class, () -> testService.updateTest(request)); |
| | | |
| | | assertEquals("开始监测后不能修改测试配置", exception.getMessage()); |
| | | assertEquals("开始接收后不能修改测试配置", exception.getMessage()); |
| | | verify(testMapper, never()).updateTest(any(TTest.class)); |
| | | } |
| | | |
| | | @Test |
| | | void createShouldRejectCollectorChannelOutsideRegisteredRange() |
| | | { |
| | | TTest test = validTest(); |
| | | test.getBatteries().get(0).getCollectorChannels().get(2).setChannelCode("S-05"); |
| | | mockEnabledPeopleAndType(); |
| | | when(testMapper.countBatteryInEnabledGroup(1L, "BAT-001")).thenReturn(1); |
| | | when(testMapper.countRegisteredChannel(100L)).thenReturn(1); |
| | | when(testMapper.countCollectorEnabled(8L)).thenReturn(1); |
| | | when(testMapper.selectCollectorChannelCount(8L)).thenReturn(4); |
| | | |
| | | ServiceException exception = assertThrows(ServiceException.class, () -> testService.createTest(test)); |
| | | |
| | | assertEquals("采集器通道不存在或超出登记范围", exception.getMessage()); |
| | | verify(testMapper, never()).insertTest(any(TTest.class)); |
| | | } |
| | | |
| | | @Test |
| | |
| | | TTest current = validTest(); |
| | | current.setTestId(10L); |
| | | current.setCreatorId(7L); |
| | | current.setMonitoringStatus("NOT_MONITORED"); |
| | | current.setReceptionStatus("NOT_STARTED"); |
| | | current.setMonitorTimeoutMinutes(30); |
| | | when(testMapper.selectTestByIdForUpdate(10L)).thenReturn(current); |
| | | mockEnabledOptions(); |
| | | mockGroupSnapshot(); |
| | | when(testMapper.updateTest(request)).thenReturn(1); |
| | | doAnswer(invocation -> { |
| | | invocation.<TTestBattery>getArgument(0).setRelationId(21L); |
| | |
| | | } |
| | | |
| | | @Test |
| | | void updateSameGroupShouldPreserveOriginalFieldSnapshot() |
| | | { |
| | | TTest request = validTest(); |
| | | request.setTestId(10L); |
| | | TTest current = validTest(); |
| | | current.setTestId(10L); |
| | | current.setCreatorId(7L); |
| | | current.setReceptionStatus("NOT_STARTED"); |
| | | TTestFieldSnapshot originalSnapshot = new TTestFieldSnapshot(); |
| | | originalSnapshot.setFieldDefinitionId(11L); |
| | | originalSnapshot.setFieldName("额定容量"); |
| | | originalSnapshot.setFieldType("number"); |
| | | originalSnapshot.setFieldValue("72"); |
| | | |
| | | when(testMapper.selectTestByIdForUpdate(10L)).thenReturn(current); |
| | | when(testMapper.selectTestBatteries(10L)).thenReturn(current.getBatteries()); |
| | | when(testMapper.selectTestFieldSnapshots(10L)).thenReturn(java.util.List.of(originalSnapshot)); |
| | | mockEnabledOptions(); |
| | | when(testMapper.updateTest(request)).thenReturn(1); |
| | | doAnswer(invocation -> { |
| | | invocation.<TTestBattery>getArgument(0).setRelationId(21L); |
| | | return 1; |
| | | }).when(testMapper).insertTestBattery(any(TTestBattery.class)); |
| | | |
| | | assertEquals(1, testService.updateTest(request)); |
| | | |
| | | verify(testMapper).insertTestFieldSnapshot(org.mockito.ArgumentMatchers.argThat(snapshot -> |
| | | Long.valueOf(11L).equals(snapshot.getFieldDefinitionId()) |
| | | && "额定容量".equals(snapshot.getFieldName()) |
| | | && "number".equals(snapshot.getFieldType()) |
| | | && "72".equals(snapshot.getFieldValue()))); |
| | | verify(batteryGroupService, never()).selectBatteryGroupById(anyLong()); |
| | | } |
| | | |
| | | @Test |
| | | void deleteBeforeMonitoringShouldAppendAudit() |
| | | { |
| | | TTest current = validTest(); |
| | | current.setTestId(10L); |
| | | current.setMonitoringStatus("NOT_MONITORED"); |
| | | current.setReceptionStatus("NOT_STARTED"); |
| | | when(testMapper.selectTestByIdForUpdate(10L)).thenReturn(current); |
| | | when(testMapper.deleteTestById(10L, "operator")).thenReturn(1); |
| | | |
| | |
| | | when(testMapper.countBatteryInEnabledGroup(1L, "BAT-001")).thenReturn(1); |
| | | when(testMapper.countRegisteredChannel(100L)).thenReturn(1); |
| | | when(testMapper.countCollectorEnabled(8L)).thenReturn(1); |
| | | when(testMapper.selectCollectorChannelCount(8L)).thenReturn(4); |
| | | } |
| | | |
| | | private void mockEnabledPeopleAndType() |
| | |
| | | when(testMapper.countUserEnabled(7L)).thenReturn(1); |
| | | when(testMapper.countUserEnabled(9L)).thenReturn(1); |
| | | when(testMapper.countTestTypeEnabled(3L)).thenReturn(1); |
| | | } |
| | | |
| | | private void mockGroupSnapshot() |
| | | { |
| | | TBatteryGroupRelationField field = new TBatteryGroupRelationField(); |
| | | field.setBatteryGroupFieldId(11L); |
| | | field.setFieldName("额定容量"); |
| | | field.setFieldType("number"); |
| | | field.setFieldValue("72"); |
| | | TBatteryGroup group = new TBatteryGroup(); |
| | | group.setGroupId(1L); |
| | | group.setTemplateFields(java.util.List.of(field)); |
| | | when(batteryGroupService.selectBatteryGroupById(1L)).thenReturn(group); |
| | | } |
| | | |
| | | private TTest validTest() |
| | |
| | | battery.setBatteryNo(batteryNo); |
| | | battery.setCollectorId(8L); |
| | | battery.setBinding(binding); |
| | | battery.setCollectorChannels(new java.util.ArrayList<>(java.util.List.of( |
| | | collectorChannel("PRIMARY_TEMPERATURE", "主温度", "S-01"), |
| | | collectorChannel("PRIMARY_POSITIVE_POTENTIAL", "正电位主通道", "S-02"), |
| | | collectorChannel("PRIMARY_NEGATIVE_POTENTIAL", "负电位主通道", "S-03")))); |
| | | battery.setFields(Collections.emptyList()); |
| | | return battery; |
| | | } |
| | | |
| | | private TTestCollectorChannel collectorChannel(String measurementCode, String measurementName, String channelCode) |
| | | { |
| | | TTestCollectorChannel mapping = new TTestCollectorChannel(); |
| | | mapping.setMeasurementCode(measurementCode); |
| | | mapping.setMeasurementName(measurementName); |
| | | mapping.setChannelCode(channelCode); |
| | | return mapping; |
| | | } |
| | | } |
| | |
| | | @NotBlank(message = "测试目的不能为空") |
| | | private String testPurpose; |
| | | private String testStatus; |
| | | private String monitoringStatus; |
| | | private String receptionStatus; |
| | | private Integer monitorTimeoutMinutes; |
| | | private Date startedAtUtc; |
| | | private Date endedAtUtc; |
| | |
| | | @Valid |
| | | @NotEmpty(message = "阈值配置不能为空") |
| | | private List<TTestWarningThreshold> thresholds; |
| | | private List<TTestFieldSnapshot> fieldSnapshots; |
| | | private List<TTestResult> resultSummary; |
| | | |
| | | public Long getTestId() { return testId; } |
| | |
| | | public void setTestPurpose(String value) { testPurpose = value; } |
| | | public String getTestStatus() { return testStatus; } |
| | | public void setTestStatus(String value) { testStatus = value; } |
| | | public String getMonitoringStatus() { return monitoringStatus; } |
| | | public void setMonitoringStatus(String value) { monitoringStatus = value; } |
| | | public String getReceptionStatus() { return receptionStatus; } |
| | | public void setReceptionStatus(String value) { receptionStatus = value; } |
| | | public Integer getMonitorTimeoutMinutes() { return monitorTimeoutMinutes; } |
| | | public void setMonitorTimeoutMinutes(Integer value) { monitorTimeoutMinutes = value; } |
| | | public Date getStartedAtUtc() { return startedAtUtc; } |
| | |
| | | public void setPlanSteps(List<TTestPlanStep> value) { planSteps = value; } |
| | | public List<TTestWarningThreshold> getThresholds() { return thresholds; } |
| | | public void setThresholds(List<TTestWarningThreshold> value) { thresholds = value; } |
| | | public List<TTestFieldSnapshot> getFieldSnapshots() { return fieldSnapshots; } |
| | | public void setFieldSnapshots(List<TTestFieldSnapshot> value) { fieldSnapshots = value; } |
| | | public List<TTestResult> getResultSummary() { return resultSummary; } |
| | | public void setResultSummary(List<TTestResult> value) { resultSummary = value; } |
| | | } |
| | |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotNull; |
| | | |
| | | /** 测试内的电池号、可选采集器与主要检测柜通道配置。 */ |
| | | /** 测试内的电池号、必填采集器与设备通道配置。 */ |
| | | public class TTestBattery implements Serializable |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | |
| | | @Valid |
| | | @NotNull(message = "主要检测柜通道不能为空") |
| | | private TTestChannelBinding binding; |
| | | @Valid |
| | | private List<TTestCollectorChannel> collectorChannels; |
| | | private List<TTestBatteryField> fields; |
| | | |
| | | public Long getRelationId() { return relationId; } |
| | |
| | | public void setDelFlag(String value) { delFlag = value; } |
| | | public TTestChannelBinding getBinding() { return binding; } |
| | | public void setBinding(TTestChannelBinding value) { binding = value; } |
| | | public List<TTestCollectorChannel> getCollectorChannels() { return collectorChannels; } |
| | | public void setCollectorChannels(List<TTestCollectorChannel> value) { collectorChannels = value; } |
| | | public List<TTestBatteryField> getFields() { return fields; } |
| | | public void setFields(List<TTestBatteryField> value) { fields = value; } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.domain; |
| | | |
| | | import java.io.Serializable; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | |
| | | /** 测试电池号与采集器演示通道的测量映射。 */ |
| | | public class TTestCollectorChannel implements Serializable |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | private Long mappingId; |
| | | private Long testId; |
| | | private Long batteryRelationId; |
| | | private Long collectorId; |
| | | @NotBlank(message = "采集器测量项编码不能为空") |
| | | private String measurementCode; |
| | | @NotBlank(message = "采集器测量项名称不能为空") |
| | | private String measurementName; |
| | | @NotBlank(message = "采集器通道不能为空") |
| | | private String channelCode; |
| | | |
| | | public Long getMappingId() { return mappingId; } |
| | | public void setMappingId(Long value) { mappingId = value; } |
| | | public Long getTestId() { return testId; } |
| | | public void setTestId(Long value) { testId = value; } |
| | | public Long getBatteryRelationId() { return batteryRelationId; } |
| | | public void setBatteryRelationId(Long value) { batteryRelationId = value; } |
| | | public Long getCollectorId() { return collectorId; } |
| | | public void setCollectorId(Long value) { collectorId = value; } |
| | | public String getMeasurementCode() { return measurementCode; } |
| | | public void setMeasurementCode(String value) { measurementCode = value; } |
| | | public String getMeasurementName() { return measurementName; } |
| | | public void setMeasurementName(String value) { measurementName = value; } |
| | | public String getChannelCode() { return channelCode; } |
| | | public void setChannelCode(String value) { channelCode = value; } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.domain; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** 测试创建或显式更换电池组时冻结的共享业务字段。 */ |
| | | public class TTestFieldSnapshot implements Serializable |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | private Long snapshotId; |
| | | private Long testId; |
| | | private Long fieldDefinitionId; |
| | | private String fieldName; |
| | | private String fieldType; |
| | | private String fieldValue; |
| | | |
| | | public Long getSnapshotId() { return snapshotId; } |
| | | public void setSnapshotId(Long value) { snapshotId = value; } |
| | | public Long getTestId() { return testId; } |
| | | public void setTestId(Long value) { testId = value; } |
| | | public Long getFieldDefinitionId() { return fieldDefinitionId; } |
| | | public void setFieldDefinitionId(Long value) { fieldDefinitionId = value; } |
| | | public String getFieldName() { return fieldName; } |
| | | public void setFieldName(String value) { fieldName = value; } |
| | | public String getFieldType() { return fieldType; } |
| | | public void setFieldType(String value) { fieldType = value; } |
| | | public String getFieldValue() { return fieldValue; } |
| | | public void setFieldValue(String value) { fieldValue = value; } |
| | | } |
| | |
| | | private Long collectorId; |
| | | private String collectorCode; |
| | | private String collectorName; |
| | | private Integer channelCount; |
| | | |
| | | public Long getCollectorId() { return collectorId; } |
| | | public void setCollectorId(Long value) { collectorId = value; } |
| | |
| | | public void setCollectorCode(String value) { collectorCode = value; } |
| | | public String getCollectorName() { return collectorName; } |
| | | public void setCollectorName(String value) { collectorName = value; } |
| | | public Integer getChannelCount() { return channelCount; } |
| | | public void setChannelCount(Integer value) { channelCount = value; } |
| | | } |
| | |
| | | import com.ruoyi.system.domain.TTestBattery; |
| | | import com.ruoyi.system.domain.TTestBatteryField; |
| | | import com.ruoyi.system.domain.TTestChannelBinding; |
| | | import com.ruoyi.system.domain.TTestCollectorChannel; |
| | | import com.ruoyi.system.domain.TTestFieldSnapshot; |
| | | import com.ruoyi.system.domain.TTestPlanStep; |
| | | import com.ruoyi.system.domain.TTestResult; |
| | | import com.ruoyi.system.domain.TTestWarningThreshold; |
| | |
| | | int updateTest(TTest test); |
| | | int deleteTestById(@Param("testId") Long testId, @Param("updateBy") String updateBy); |
| | | int deleteTestBindingsByTestId(Long testId); |
| | | int deleteTestCollectorChannelsByTestId(Long testId); |
| | | int deleteTestFieldSnapshotsByTestId(Long testId); |
| | | int deleteTestPlanStepsByTestId(Long testId); |
| | | int deleteTestBatteriesByTestId(Long testId); |
| | | int deleteTestFieldsByTestId(Long testId); |
| | |
| | | int insertTestBattery(TTestBattery battery); |
| | | int insertTestBatteryField(TTestBatteryField field); |
| | | int insertTestChannelBinding(TTestChannelBinding binding); |
| | | int insertTestCollectorChannel(TTestCollectorChannel mapping); |
| | | int insertTestFieldSnapshot(TTestFieldSnapshot snapshot); |
| | | int insertTestThreshold(TTestWarningThreshold threshold); |
| | | List<TTestPlanStep> selectTestPlanSteps(Long testId); |
| | | List<TTestBattery> selectTestBatteries(Long testId); |
| | | List<TTestBatteryField> selectTestBatteryFields(Long testId); |
| | | List<TTestChannelBinding> selectTestChannelBindings(Long testId); |
| | | List<TTestCollectorChannel> selectTestCollectorChannels(Long testId); |
| | | List<TTestFieldSnapshot> selectTestFieldSnapshots(Long testId); |
| | | List<TTestWarningThreshold> selectTestThresholds(Long testId); |
| | | List<TTestResult> selectTestResults(TTestResult result); |
| | | int countTestTypeEnabled(Long testTypeId); |
| | |
| | | int countBatteryInEnabledGroup(@Param("groupId") Long groupId, @Param("batteryNo") String batteryNo); |
| | | int countRegisteredChannel(Long channelId); |
| | | int countCollectorEnabled(Long collectorId); |
| | | Integer selectCollectorChannelCount(Long collectorId); |
| | | List<TestUserOptionVo> selectEnabledUserOptions(); |
| | | List<TestTypeOptionVo> selectEnabledTestTypeOptions(); |
| | | List<TestBatteryOptionVo> selectEnabledBatteryOptions(); |
| | |
| | | /** 测试配置业务接口。 */ |
| | | public interface ITTestService |
| | | { |
| | | /** 返回测试配置、正式测试状态和独立监测状态。 */ |
| | | /** 返回测试配置、正式测试状态和独立接收状态。 */ |
| | | TTest selectTestById(Long testId); |
| | | |
| | | /** 按正式测试状态、监测状态及配置条件查询测试。 */ |
| | | /** 按正式测试状态、接收状态及配置条件查询测试。 */ |
| | | List<TTest> selectTestList(TTest test); |
| | | |
| | | /** 返回当前启用且已正式登记的测试配置选项。 */ |
| | | TestConfigurationOptionsVo selectConfigurationOptions(); |
| | | |
| | | /** 创建未监测的待测试记录并返回测试标识。 */ |
| | | /** 创建未开始接收的待测试记录并返回测试标识。 */ |
| | | Long createTest(TTest test); |
| | | |
| | | /** 仅在尚未开始监测时替换测试配置。 */ |
| | | /** 仅在尚未开始接收时替换测试配置。 */ |
| | | int updateTest(TTest test); |
| | | |
| | | /** 拒绝旧通用启动入口,监测启动由后续原子动作实现。 */ |
| | | /** 拒绝旧通用启动入口,开始接收由后续原子动作实现。 */ |
| | | int startTest(Long testId, String updateBy); |
| | | |
| | | /** 仅删除未开始监测的测试并追加审计记录。 */ |
| | | /** 仅删除未开始接收的测试并追加审计记录。 */ |
| | | int deleteTestByIds(Long[] testIds, Long operatorId, String updateBy); |
| | | |
| | | /** 查询指定测试已持久化的结果记录。 */ |
| | |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.system.domain.TTest; |
| | | import com.ruoyi.system.domain.TBatteryGroup; |
| | | import com.ruoyi.system.domain.TBatteryGroupRelationField; |
| | | import com.ruoyi.system.domain.TTestBattery; |
| | | import com.ruoyi.system.domain.TTestBatteryField; |
| | | import com.ruoyi.system.domain.TTestChannelBinding; |
| | | import com.ruoyi.system.domain.TTestCollectorChannel; |
| | | import com.ruoyi.system.domain.TTestFieldSnapshot; |
| | | import com.ruoyi.system.domain.TTestPlanStep; |
| | | import com.ruoyi.system.domain.TTestResult; |
| | | import com.ruoyi.system.domain.TTestWarningThreshold; |
| | | import com.ruoyi.system.domain.vo.TestConfigurationOptionsVo; |
| | | import com.ruoyi.system.mapper.TTestMapper; |
| | | import com.ruoyi.system.service.ITTestService; |
| | | import com.ruoyi.system.service.ITBatteryGroupService; |
| | | |
| | | /** 测试配置服务,负责状态边界、关系校验、事务写入和只追加审计。 */ |
| | | @Service |
| | | public class TTestServiceImpl implements ITTestService |
| | | { |
| | | private static final String WAITING = "WAITING"; |
| | | private static final String NOT_MONITORED = "NOT_MONITORED"; |
| | | private static final String NOT_STARTED = "NOT_STARTED"; |
| | | private static final Set<String> TEST_STATUSES = Set.of( |
| | | WAITING, "RUNNING", "PAUSED", "RUNNING_ABNORMAL", "COMPLETION_PENDING", "COMPLETED", "TERMINATED"); |
| | | private static final Set<String> MONITORING_STATUSES = Set.of( |
| | | NOT_MONITORED, "WAITING_DEVICE_START", "MONITORING", "BACKFILLING", "MONITORING_ENDED", "CANCELLED"); |
| | | private static final Set<String> RECEPTION_STATUSES = Set.of(NOT_STARTED, "RECEIVING", "PAUSED", "ENDED"); |
| | | private static final Set<String> THRESHOLD_UNITS = Set.of("A", "V", "Ah", "Wh", "℃"); |
| | | private static final Set<String> REQUIRED_COLLECTOR_MEASUREMENTS = Set.of( |
| | | "PRIMARY_TEMPERATURE", "PRIMARY_POSITIVE_POTENTIAL", "PRIMARY_NEGATIVE_POTENTIAL"); |
| | | |
| | | @Autowired |
| | | private TTestMapper testMapper; |
| | | |
| | | @Autowired |
| | | private ITBatteryGroupService batteryGroupService; |
| | | |
| | | @Override |
| | | public TTest selectTestById(Long testId) |
| | |
| | | test.setTestName(trimToNull(test.getTestName())); |
| | | test.setBatteryNo(trimToNull(test.getBatteryNo())); |
| | | validateQueryStatus(test.getTestStatus(), TEST_STATUSES, "测试状态无效"); |
| | | validateQueryStatus(test.getMonitoringStatus(), MONITORING_STATUSES, "监测状态无效"); |
| | | validateQueryStatus(test.getReceptionStatus(), RECEPTION_STATUSES, "接收状态无效"); |
| | | return testMapper.selectTestList(test); |
| | | } |
| | | |
| | |
| | | validateBase(test, false); |
| | | validateConfiguration(test); |
| | | test.setTestStatus(WAITING); |
| | | test.setMonitoringStatus(NOT_MONITORED); |
| | | test.setReceptionStatus(NOT_STARTED); |
| | | test.setFieldSnapshots(snapshotGroupFields(test.getBatteries().get(0).getGroupId())); |
| | | test.setDelFlag("0"); |
| | | test.setCreateTime(new Date()); |
| | | testMapper.insertTest(test); |
| | |
| | | TTest current = testMapper.selectTestByIdForUpdate(test.getTestId()); |
| | | if (current == null) throw new ServiceException("测试不存在"); |
| | | loadConfiguration(current); |
| | | if (!NOT_MONITORED.equals(current.getMonitoringStatus())) |
| | | throw new ServiceException("开始监测后不能修改测试配置"); |
| | | if (!NOT_STARTED.equals(current.getReceptionStatus())) |
| | | throw new ServiceException("开始接收后不能修改测试配置"); |
| | | |
| | | test.setCreatorId(current.getCreatorId()); |
| | | test.setTestStatus(current.getTestStatus()); |
| | | test.setMonitoringStatus(current.getMonitoringStatus()); |
| | | test.setReceptionStatus(current.getReceptionStatus()); |
| | | if (test.getMonitorTimeoutMinutes() == null) |
| | | test.setMonitorTimeoutMinutes(current.getMonitorTimeoutMinutes()); |
| | | validateBase(test, true); |
| | | validateConfiguration(test); |
| | | test.setFieldSnapshots(resolveUpdatedFieldSnapshots(test, current)); |
| | | test.setUpdateTime(new Date()); |
| | | int rows = testMapper.updateTest(test); |
| | | if (rows != 1) throw new ServiceException("测试配置已变更,请刷新后重试"); |
| | | |
| | | // 监测开始前没有原始采集事实,配置关系可在同一事务内整体替换,审计记录仍只追加。 |
| | | // 开始接收前没有数据归属事实,配置关系可在同一事务内整体替换,审计记录仍只追加。 |
| | | testMapper.deleteTestCollectorChannelsByTestId(test.getTestId()); |
| | | testMapper.deleteTestBindingsByTestId(test.getTestId()); |
| | | testMapper.deleteTestPlanStepsByTestId(test.getTestId()); |
| | | testMapper.deleteTestFieldsByTestId(test.getTestId()); |
| | | testMapper.deleteTestBatteriesByTestId(test.getTestId()); |
| | | testMapper.deleteTestThresholdsByTestId(test.getTestId()); |
| | | testMapper.deleteTestFieldSnapshotsByTestId(test.getTestId()); |
| | | saveConfiguration(test); |
| | | testMapper.insertTestAudit(test.getTestId(), "TEST_CONFIGURATION_UPDATED", test.getOperatorId(), |
| | | test.getUpdateBy(), JSON.toJSONString(current), JSON.toJSONString(test)); |
| | |
| | | @Override |
| | | public int startTest(Long testId, String updateBy) |
| | | { |
| | | throw new ServiceException("开始监测由后续监测动作处理"); |
| | | throw new ServiceException("开始接收由后续原子动作处理"); |
| | | } |
| | | |
| | | @Override |
| | |
| | | { |
| | | TTest current = testMapper.selectTestByIdForUpdate(testId); |
| | | if (current == null) throw new ServiceException("测试不存在"); |
| | | if (!NOT_MONITORED.equals(current.getMonitoringStatus())) throw new ServiceException("开始监测后不能删除测试"); |
| | | if (!NOT_STARTED.equals(current.getReceptionStatus())) throw new ServiceException("开始接收后不能删除测试"); |
| | | loadConfiguration(current); |
| | | int deleted = testMapper.deleteTestById(testId, updateBy); |
| | | if (deleted != 1) throw new ServiceException("测试配置已变更,请刷新后重试"); |
| | |
| | | |
| | | List<TTestBattery> batteries = mutable(testMapper.selectTestBatteries(testId)); |
| | | List<TTestBatteryField> fields = mutable(testMapper.selectTestBatteryFields(testId)); |
| | | List<TTestCollectorChannel> collectorChannels = mutable(testMapper.selectTestCollectorChannels(testId)); |
| | | Map<Long, TTestChannelBinding> bindings = new HashMap<>(); |
| | | for (TTestChannelBinding binding : mutable(testMapper.selectTestChannelBindings(testId))) |
| | | bindings.put(binding.getBatteryRelationId(), binding); |
| | |
| | | battery.setFields(fields.stream() |
| | | .filter(field -> battery.getRelationId().equals(field.getRelationId())) |
| | | .toList()); |
| | | battery.setCollectorChannels(collectorChannels.stream() |
| | | .filter(mapping -> battery.getRelationId().equals(mapping.getBatteryRelationId())) |
| | | .toList()); |
| | | } |
| | | test.setPlanSteps(planSteps); |
| | | test.setBatteries(batteries); |
| | | test.setThresholds(mutable(testMapper.selectTestThresholds(testId))); |
| | | test.setFieldSnapshots(mutable(testMapper.selectTestFieldSnapshots(testId))); |
| | | } |
| | | |
| | | private void validateBase(TTest test, boolean update) |
| | |
| | | |
| | | private void validateConfiguration(TTest test) |
| | | { |
| | | // 新建测试只建立业务身份和通道关系,计划工步可在开始监测前补充。 |
| | | // 新建测试只建立业务身份和通道关系,计划工步可在开始接收前补充。 |
| | | if (test.getPlanSteps() == null) test.setPlanSteps(new ArrayList<>()); |
| | | validateBatteries(test.getBatteries()); |
| | | validatePlanSteps(test.getPlanSteps()); |
| | |
| | | if (batteries == null || batteries.isEmpty()) throw new ServiceException("测试至少绑定一个电池号"); |
| | | Set<String> batteryNos = new HashSet<>(); |
| | | Set<Long> channelIds = new HashSet<>(); |
| | | Set<String> collectorChannelIdentities = new HashSet<>(); |
| | | Map<Long, Integer> collectorChannelCounts = new HashMap<>(); |
| | | Long groupId = null; |
| | | for (TTestBattery battery : batteries) |
| | | { |
| | |
| | | binding.setExpectedBarcode(trimToNull(binding.getExpectedBarcode())); |
| | | if (binding.getExpectedBarcode() != null && binding.getExpectedBarcode().length() > 100) |
| | | throw new ServiceException("预期条码长度不能超过100个字符"); |
| | | if (battery.getCollectorId() != null && testMapper.countCollectorEnabled(battery.getCollectorId()) == 0) |
| | | if (battery.getCollectorId() == null) throw new ServiceException("采集器不能为空"); |
| | | if (testMapper.countCollectorEnabled(battery.getCollectorId()) == 0) |
| | | throw new ServiceException("采集器不存在或未启用"); |
| | | Integer channelCount = collectorChannelCounts.computeIfAbsent(battery.getCollectorId(), |
| | | testMapper::selectCollectorChannelCount); |
| | | if (channelCount == null || channelCount < 1) throw new ServiceException("采集器通道配置无效"); |
| | | validateCollectorChannels(battery.getCollectorId(), channelCount, battery.getCollectorChannels(), |
| | | collectorChannelIdentities); |
| | | validateBatteryFields(battery.getFields()); |
| | | } |
| | | } |
| | | |
| | | private void validateCollectorChannels(Long collectorId, int channelCount, List<TTestCollectorChannel> mappings, |
| | | Set<String> collectorChannelIdentities) |
| | | { |
| | | if (mappings == null || mappings.isEmpty()) |
| | | throw new ServiceException("每个电池号必须绑定主温度、正电位主通道和负电位主通道"); |
| | | Set<String> measurementCodes = new HashSet<>(); |
| | | for (TTestCollectorChannel mapping : mappings) |
| | | { |
| | | if (mapping == null) throw new ServiceException("采集器通道映射不完整"); |
| | | mapping.setMeasurementCode(trimToNull(mapping.getMeasurementCode())); |
| | | mapping.setMeasurementName(trimToNull(mapping.getMeasurementName())); |
| | | mapping.setChannelCode(normalizeCollectorChannel(mapping.getChannelCode())); |
| | | if (mapping.getMeasurementCode() == null || mapping.getMeasurementName() == null |
| | | || mapping.getChannelCode() == null) |
| | | throw new ServiceException("采集器通道映射不完整"); |
| | | if (mapping.getMeasurementCode().length() > 100 || mapping.getMeasurementName().length() > 100) |
| | | throw new ServiceException("采集器测量项长度不能超过100个字符"); |
| | | int channelNumber = Integer.parseInt(mapping.getChannelCode().substring(2)); |
| | | if (channelNumber < 1 || channelNumber > channelCount) |
| | | throw new ServiceException("采集器通道不存在或超出登记范围"); |
| | | if (!measurementCodes.add(mapping.getMeasurementCode())) |
| | | throw new ServiceException("同一电池号的采集器测量项不可重复"); |
| | | if (!collectorChannelIdentities.add(collectorId + ":" + mapping.getChannelCode())) |
| | | throw new ServiceException("同一采集器通道不可重复映射"); |
| | | } |
| | | if (!measurementCodes.containsAll(REQUIRED_COLLECTOR_MEASUREMENTS)) |
| | | throw new ServiceException("每个电池号必须绑定主温度、正电位主通道和负电位主通道"); |
| | | } |
| | | |
| | | private void validateBatteryFields(List<TTestBatteryField> fields) |
| | |
| | | if (field.getFieldName() == null || !fieldNames.add(field.getFieldName())) |
| | | throw new ServiceException("扩展属性名称不能为空且不可重复"); |
| | | } |
| | | } |
| | | |
| | | private List<TTestFieldSnapshot> resolveUpdatedFieldSnapshots(TTest test, TTest current) |
| | | { |
| | | Long nextGroupId = test.getBatteries().get(0).getGroupId(); |
| | | Long currentGroupId = current.getBatteries().isEmpty() ? null : current.getBatteries().get(0).getGroupId(); |
| | | if (nextGroupId.equals(currentGroupId)) |
| | | return mutable(current.getFieldSnapshots()).stream().map(this::copySnapshot).toList(); |
| | | return snapshotGroupFields(nextGroupId); |
| | | } |
| | | |
| | | private List<TTestFieldSnapshot> snapshotGroupFields(Long groupId) |
| | | { |
| | | TBatteryGroup group = batteryGroupService.selectBatteryGroupById(groupId); |
| | | List<TTestFieldSnapshot> snapshots = new ArrayList<>(); |
| | | for (TBatteryGroupRelationField field : mutable(group.getTemplateFields())) |
| | | { |
| | | TTestFieldSnapshot snapshot = new TTestFieldSnapshot(); |
| | | snapshot.setFieldDefinitionId(field.getBatteryGroupFieldId()); |
| | | snapshot.setFieldName(field.getFieldName()); |
| | | snapshot.setFieldType(field.getFieldType()); |
| | | snapshot.setFieldValue(field.getFieldValue()); |
| | | snapshots.add(snapshot); |
| | | } |
| | | return snapshots; |
| | | } |
| | | |
| | | private TTestFieldSnapshot copySnapshot(TTestFieldSnapshot source) |
| | | { |
| | | TTestFieldSnapshot copy = new TTestFieldSnapshot(); |
| | | copy.setFieldDefinitionId(source.getFieldDefinitionId()); |
| | | copy.setFieldName(source.getFieldName()); |
| | | copy.setFieldType(source.getFieldType()); |
| | | copy.setFieldValue(source.getFieldValue()); |
| | | return copy; |
| | | } |
| | | |
| | | private void validatePlanSteps(List<TTestPlanStep> planSteps) |
| | |
| | | binding.setBatteryRelationId(battery.getRelationId()); |
| | | binding.setClaimStatus("PENDING"); |
| | | testMapper.insertTestChannelBinding(binding); |
| | | for (TTestCollectorChannel mapping : battery.getCollectorChannels()) |
| | | { |
| | | mapping.setTestId(test.getTestId()); |
| | | mapping.setBatteryRelationId(battery.getRelationId()); |
| | | mapping.setCollectorId(battery.getCollectorId()); |
| | | testMapper.insertTestCollectorChannel(mapping); |
| | | } |
| | | } |
| | | for (TTestFieldSnapshot snapshot : mutable(test.getFieldSnapshots())) |
| | | { |
| | | snapshot.setTestId(test.getTestId()); |
| | | testMapper.insertTestFieldSnapshot(snapshot); |
| | | } |
| | | for (TTestWarningThreshold threshold : test.getThresholds()) |
| | | { |
| | |
| | | return StringUtils.isEmpty(trimmed) ? null : trimmed; |
| | | } |
| | | |
| | | private String normalizeCollectorChannel(String value) |
| | | { |
| | | String normalized = trimToNull(value); |
| | | if (normalized == null || !normalized.toUpperCase().matches("S-\\d+")) |
| | | throw new ServiceException("采集器通道必须使用S-01...S-N演示编码"); |
| | | try |
| | | { |
| | | int channelNumber = Integer.parseInt(normalized.substring(2)); |
| | | return String.format("S-%02d", channelNumber); |
| | | } |
| | | catch (NumberFormatException exception) |
| | | { |
| | | throw new ServiceException("采集器通道必须使用S-01...S-N演示编码"); |
| | | } |
| | | } |
| | | |
| | | private <T> List<T> mutable(List<T> values) |
| | | { |
| | | return values == null ? new ArrayList<>() : new ArrayList<>(values); |
| | |
| | | <result property="testTypeName" column="test_type_name" /> |
| | | <result property="testPurpose" column="test_purpose" /> |
| | | <result property="testStatus" column="test_status" /> |
| | | <result property="monitoringStatus" column="monitoring_status" /> |
| | | <result property="receptionStatus" column="reception_status" /> |
| | | <result property="monitorTimeoutMinutes" column="monitor_timeout_minutes" /> |
| | | <result property="startedAtUtc" column="started_at_utc" /> |
| | | <result property="endedAtUtc" column="ended_at_utc" /> |
| | |
| | | <result property="updateTime" column="update_time" /> |
| | | <result property="remark" column="remark" /> |
| | | <result property="batteryCount" column="battery_count" /> |
| | | <result property="batteryNo" column="battery_no" /> |
| | | </resultMap> |
| | | |
| | | <resultMap id="TTestBatteryResultMap" type="TTestBattery"> |
| | |
| | | <result property="displayNo" column="display_no" /> |
| | | </resultMap> |
| | | |
| | | <resultMap id="TTestCollectorChannelResultMap" type="TTestCollectorChannel"> |
| | | <id property="mappingId" column="mapping_id" /> |
| | | <result property="testId" column="test_id" /> |
| | | <result property="batteryRelationId" column="battery_relation_id" /> |
| | | <result property="collectorId" column="collector_id" /> |
| | | <result property="measurementCode" column="measurement_code" /> |
| | | <result property="measurementName" column="measurement_name" /> |
| | | <result property="channelCode" column="channel_code" /> |
| | | </resultMap> |
| | | |
| | | <resultMap id="TTestFieldSnapshotResultMap" type="TTestFieldSnapshot"> |
| | | <id property="snapshotId" column="snapshot_id" /> |
| | | <result property="testId" column="test_id" /> |
| | | <result property="fieldDefinitionId" column="field_definition_id" /> |
| | | <result property="fieldName" column="field_name" /> |
| | | <result property="fieldType" column="field_type" /> |
| | | <result property="fieldValue" column="field_value" /> |
| | | </resultMap> |
| | | |
| | | <resultMap id="TTestPlanStepResultMap" type="TTestPlanStep"> |
| | | <id property="planStepId" column="plan_step_id" /> |
| | | <result property="testId" column="test_id" /> |
| | |
| | | select t.test_id, t.test_name, t.creator_id, creator.nick_name creator_name, |
| | | t.principal_id, principal.nick_name principal_name, |
| | | t.test_type_id, ty.test_type_name, t.test_purpose, |
| | | t.test_status, t.monitoring_status, t.monitor_timeout_minutes, |
| | | t.test_status, t.reception_status, t.monitor_timeout_minutes, |
| | | t.started_at_utc, t.ended_at_utc, t.del_flag, |
| | | t.create_by, t.create_time, t.update_by, t.update_time, t.remark, |
| | | (select count(1) from t_test_battery b where b.test_id = t.test_id and b.del_flag = 0) battery_count |
| | | (select count(1) from t_test_battery b where b.test_id = t.test_id and b.del_flag = 0) battery_count, |
| | | (select group_concat(b.battery_no order by b.relation_id separator '、') |
| | | from t_test_battery b where b.test_id = t.test_id and b.del_flag = 0) battery_no |
| | | from t_test t |
| | | left join sys_user creator on creator.user_id = t.creator_id and creator.del_flag = '0' |
| | | left join sys_user principal on principal.user_id = t.principal_id and principal.del_flag = '0' |
| | |
| | | <if test="testTypeId != null">and t.test_type_id = #{testTypeId}</if> |
| | | <if test="batteryNo != null and batteryNo != ''">and exists (select 1 from t_test_battery fb where fb.test_id = t.test_id and fb.battery_no = #{batteryNo} and fb.del_flag = 0)</if> |
| | | <if test="testStatus != null and testStatus != ''">and t.test_status = #{testStatus}</if> |
| | | <if test="monitoringStatus != null and monitoringStatus != ''">and t.monitoring_status = #{monitoringStatus}</if> |
| | | <if test="receptionStatus != null and receptionStatus != ''">and t.reception_status = #{receptionStatus}</if> |
| | | <if test="beginCreateTime != null">and t.create_time >= #{beginCreateTime}</if> |
| | | <if test="endCreateTime != null">and t.create_time <= #{endCreateTime}</if> |
| | | </where> |
| | |
| | | |
| | | <insert id="insertTest" parameterType="TTest" useGeneratedKeys="true" keyProperty="testId"> |
| | | insert into t_test |
| | | (test_name, creator_id, principal_id, test_type_id, test_purpose, test_status, monitoring_status, |
| | | (test_name, creator_id, principal_id, test_type_id, test_purpose, test_status, reception_status, |
| | | monitor_timeout_minutes, del_flag, create_by, create_time, remark) |
| | | values |
| | | (#{testName}, #{creatorId}, #{principalId}, #{testTypeId}, #{testPurpose}, #{testStatus}, #{monitoringStatus}, |
| | | (#{testName}, #{creatorId}, #{principalId}, #{testTypeId}, #{testPurpose}, #{testStatus}, #{receptionStatus}, |
| | | #{monitorTimeoutMinutes}, 0, #{createBy}, #{createTime}, #{remark}) |
| | | </insert> |
| | | |
| | |
| | | set test_name = #{testName}, principal_id = #{principalId}, test_type_id = #{testTypeId}, |
| | | test_purpose = #{testPurpose}, monitor_timeout_minutes = #{monitorTimeoutMinutes}, |
| | | remark = #{remark}, update_by = #{updateBy}, update_time = #{updateTime} |
| | | where test_id = #{testId} and monitoring_status = 'NOT_MONITORED' and del_flag = 0 |
| | | where test_id = #{testId} and reception_status = 'NOT_STARTED' and del_flag = 0 |
| | | </update> |
| | | |
| | | <update id="deleteTestById"> |
| | | update t_test set del_flag = test_id, update_by = #{updateBy}, update_time = sysdate() |
| | | where test_id = #{testId} and monitoring_status = 'NOT_MONITORED' and del_flag = 0 |
| | | where test_id = #{testId} and reception_status = 'NOT_STARTED' and del_flag = 0 |
| | | </update> |
| | | |
| | | <delete id="deleteTestBindingsByTestId">delete from t_test_channel_binding where test_id = #{testId}</delete> |
| | | <delete id="deleteTestCollectorChannelsByTestId">delete from t_test_collector_channel where test_id = #{testId}</delete> |
| | | <delete id="deleteTestFieldSnapshotsByTestId">delete from t_test_field_snapshot where test_id = #{testId}</delete> |
| | | <delete id="deleteTestPlanStepsByTestId">delete from t_test_plan_step where test_id = #{testId}</delete> |
| | | <update id="deleteTestFieldsByTestId"> |
| | | update t_test_battery_field f join t_test_battery b on b.relation_id = f.relation_id |
| | |
| | | <insert id="insertTestChannelBinding" parameterType="TTestChannelBinding" useGeneratedKeys="true" keyProperty="bindingId"> |
| | | insert into t_test_channel_binding(test_id, battery_relation_id, channel_id, expected_barcode, claim_status) |
| | | values(#{testId}, #{batteryRelationId}, #{channelId}, #{expectedBarcode}, #{claimStatus}) |
| | | </insert> |
| | | <insert id="insertTestCollectorChannel" parameterType="TTestCollectorChannel" useGeneratedKeys="true" keyProperty="mappingId"> |
| | | insert into t_test_collector_channel |
| | | (test_id, battery_relation_id, collector_id, measurement_code, measurement_name, channel_code) |
| | | values(#{testId}, #{batteryRelationId}, #{collectorId}, #{measurementCode}, #{measurementName}, #{channelCode}) |
| | | </insert> |
| | | <insert id="insertTestFieldSnapshot" parameterType="TTestFieldSnapshot" useGeneratedKeys="true" keyProperty="snapshotId"> |
| | | insert into t_test_field_snapshot |
| | | (test_id, field_definition_id, field_name, field_type, field_value) |
| | | values(#{testId}, #{fieldDefinitionId}, #{fieldName}, #{fieldType}, #{fieldValue}) |
| | | </insert> |
| | | <insert id="insertTestThreshold" parameterType="TTestWarningThreshold" useGeneratedKeys="true" keyProperty="thresholdId"> |
| | | insert into t_test_warning_threshold(test_id, metric_code, min_value, max_value, unit, severity, del_flag) |
| | |
| | | join t_detection_channel channel_fact on channel_fact.channel_id = binding.channel_id |
| | | join t_detection_cabinet cabinet on cabinet.cabinet_id = channel_fact.cabinet_id |
| | | where binding.test_id = #{testId} order by binding.binding_id |
| | | </select> |
| | | <select id="selectTestCollectorChannels" parameterType="Long" resultMap="TTestCollectorChannelResultMap"> |
| | | select mapping_id, test_id, battery_relation_id, collector_id, |
| | | measurement_code, measurement_name, channel_code |
| | | from t_test_collector_channel where test_id = #{testId} |
| | | order by battery_relation_id, mapping_id |
| | | </select> |
| | | <select id="selectTestFieldSnapshots" parameterType="Long" resultMap="TTestFieldSnapshotResultMap"> |
| | | select snapshot_id, test_id, field_definition_id, field_name, field_type, field_value |
| | | from t_test_field_snapshot where test_id = #{testId} order by snapshot_id |
| | | </select> |
| | | <select id="selectTestThresholds" parameterType="Long" resultMap="TTestThresholdResultMap"> |
| | | select threshold_id, test_id, metric_code, min_value, max_value, unit, severity, del_flag |
| | |
| | | <select id="countCollectorEnabled" resultType="int"> |
| | | select count(1) from t_collector where collector_id = #{collectorId} and status = '0' and del_flag = 0 |
| | | </select> |
| | | <select id="selectCollectorChannelCount" resultType="int"> |
| | | select channel_count from t_collector where collector_id = #{collectorId} and status = '0' and del_flag = 0 |
| | | </select> |
| | | |
| | | <select id="selectEnabledUserOptions" resultType="com.ruoyi.system.domain.vo.TestUserOptionVo"> |
| | | select user_id userId, user_name userName, nick_name nickName |
| | |
| | | order by cabinet.cabinet_name, channel_fact.chlid, channel_fact.channel_id |
| | | </select> |
| | | <select id="selectEnabledCollectorOptions" resultType="com.ruoyi.system.domain.vo.TestCollectorOptionVo"> |
| | | select collector_id collectorId, collector_code collectorCode, collector_name collectorName |
| | | select collector_id collectorId, collector_code collectorCode, collector_name collectorName, |
| | | channel_count channelCount |
| | | from t_collector where status = '0' and del_flag = 0 order by collector_name, collector_id |
| | | </select> |
| | | |