| ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TTestController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/Ticket15ResultQueryContractTest.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestResultQuery.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestResultRecord.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| ruoyi-system/src/main/java/com/ruoyi/system/mapper/TTestMapper.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| ruoyi-system/src/main/java/com/ruoyi/system/service/ITTestService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| ruoyi-system/src/main/resources/mapper/system/TTestMapper.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TTestController.java
@@ -20,6 +20,7 @@ import com.ruoyi.common.enums.BusinessType; import com.ruoyi.system.domain.TTest; import com.ruoyi.system.domain.TTestResult; import com.ruoyi.system.domain.TTestResultQuery; import com.ruoyi.system.service.ITTestService; /** 测试管理 Controller。 */ @@ -151,4 +152,13 @@ startPage(); return getDataTable(testService.selectTestResults(result)); } /** 统一历史结果查询;仅返回检测完成测试的区间原始采样。 */ @PreAuthorize("@ss.hasPermi('system:test:query')") @GetMapping("/results") public TableDataInfo historicalResults(TTestResultQuery query) { startPage(); return getDataTable(testService.selectHistoricalResultRecords(query)); } } ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/Ticket15ResultQueryContractTest.java
New file @@ -0,0 +1,32 @@ package com.ruoyi.web.controller.system; import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import org.junit.jupiter.api.Test; /** Ticket15:结果查询必须从区间历史表读取,并排除实时和未完成测试。 */ class Ticket15ResultQueryContractTest { @Test void historicalResultQueryUsesCompletedIntervalHistory() { Path mapper = Path.of("../ruoyi-system/src/main/resources/mapper/system/TTestMapper.xml"); String xml; try { xml = Files.readString(mapper, StandardCharsets.UTF_8); } catch (Exception exception) { throw new AssertionError(exception); } assertTrue(xml.contains("id=\"selectHistoricalResultRecords\"")); assertTrue(xml.contains("t_test_sample s")); assertTrue(xml.contains("t_test_collector_sample cs")); assertTrue(xml.contains("t.test_status = 'COMPLETED'")); assertTrue(xml.contains("i.interval_status = 'CLOSED'")); assertTrue(xml.contains("union all")); } } ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestResultQuery.java
New file @@ -0,0 +1,38 @@ package com.ruoyi.system.domain; import java.util.Date; /** 结果查询条件;仅用于历史结果筛选,不承载实时快照。 */ public class TTestResultQuery { private Long testId; private String testName; private String batteryNo; private String sourceType; private Long sourceId; private String sourceChannelCode; private Date beginDeviceTime; private Date endDeviceTime; private Integer cycleNo; private Integer stepNo; public Long getTestId() { return testId; } public void setTestId(Long value) { testId = value; } public String getTestName() { return testName; } public void setTestName(String value) { testName = value; } public String getBatteryNo() { return batteryNo; } public void setBatteryNo(String value) { batteryNo = value; } public String getSourceType() { return sourceType; } public void setSourceType(String value) { sourceType = value; } public Long getSourceId() { return sourceId; } public void setSourceId(Long value) { sourceId = value; } public String getSourceChannelCode() { return sourceChannelCode; } public void setSourceChannelCode(String value) { sourceChannelCode = value; } public Date getBeginDeviceTime() { return beginDeviceTime; } public void setBeginDeviceTime(Date value) { beginDeviceTime = value; } public Date getEndDeviceTime() { return endDeviceTime; } public void setEndDeviceTime(Date value) { endDeviceTime = value; } public Integer getCycleNo() { return cycleNo; } public void setCycleNo(Integer value) { cycleNo = value; } public Integer getStepNo() { return stepNo; } public void setStepNo(Integer value) { stepNo = value; } } ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestResultRecord.java
New file @@ -0,0 +1,99 @@ package com.ruoyi.system.domain; import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; /** 结果查询统一记录;检测柜核心采样和采集器扩展测量使用同一分页契约。 */ public class TTestResultRecord implements Serializable { private static final long serialVersionUID = 1L; private Long resultId; private String sourceType; private Long testId; private String testName; private String testStatus; private String receptionStatus; private Long relationId; private String batteryNo; private Long sourceId; private String sourceIdentity; private Long intervalId; private Date intervalStartAtUtc; private Date intervalEndAtUtc; private Integer cycleNo; private Integer stepNo; private String stepType; private String workStatus; private BigDecimal voltageV; private BigDecimal currentA; private BigDecimal capacityAh; private BigDecimal energyWh; private String metricCode; private BigDecimal metricValue; private String unit; private String unitMetadata; private String integrityConclusion; private Date deviceTimeUtc; private String deviceTimeRaw; private Date serverReceivedAtUtc; public Long getResultId() { return resultId; } public void setResultId(Long value) { resultId = value; } public String getSourceType() { return sourceType; } public void setSourceType(String value) { sourceType = value; } public Long getTestId() { return testId; } public void setTestId(Long value) { testId = value; } public String getTestName() { return testName; } public void setTestName(String value) { testName = value; } public String getTestStatus() { return testStatus; } public void setTestStatus(String value) { testStatus = value; } public String getReceptionStatus() { return receptionStatus; } public void setReceptionStatus(String value) { receptionStatus = value; } public Long getRelationId() { return relationId; } public void setRelationId(Long value) { relationId = value; } public String getBatteryNo() { return batteryNo; } public void setBatteryNo(String value) { batteryNo = value; } public Long getSourceId() { return sourceId; } public void setSourceId(Long value) { sourceId = value; } public String getSourceIdentity() { return sourceIdentity; } public void setSourceIdentity(String value) { sourceIdentity = value; } public Long getIntervalId() { return intervalId; } public void setIntervalId(Long value) { intervalId = value; } public Date getIntervalStartAtUtc() { return intervalStartAtUtc; } public void setIntervalStartAtUtc(Date value) { intervalStartAtUtc = value; } public Date getIntervalEndAtUtc() { return intervalEndAtUtc; } public void setIntervalEndAtUtc(Date value) { intervalEndAtUtc = value; } public Integer getCycleNo() { return cycleNo; } public void setCycleNo(Integer value) { cycleNo = value; } public Integer getStepNo() { return stepNo; } public void setStepNo(Integer value) { stepNo = value; } public String getStepType() { return stepType; } public void setStepType(String value) { stepType = value; } public String getWorkStatus() { return workStatus; } public void setWorkStatus(String value) { workStatus = value; } public BigDecimal getVoltageV() { return voltageV; } public void setVoltageV(BigDecimal value) { voltageV = value; } public BigDecimal getCurrentA() { return currentA; } public void setCurrentA(BigDecimal value) { currentA = value; } public BigDecimal getCapacityAh() { return capacityAh; } public void setCapacityAh(BigDecimal value) { capacityAh = value; } public BigDecimal getEnergyWh() { return energyWh; } public void setEnergyWh(BigDecimal value) { energyWh = value; } public String getMetricCode() { return metricCode; } public void setMetricCode(String value) { metricCode = value; } public BigDecimal getMetricValue() { return metricValue; } public void setMetricValue(BigDecimal value) { metricValue = value; } public String getUnit() { return unit; } public void setUnit(String value) { unit = value; } public String getUnitMetadata() { return unitMetadata; } public void setUnitMetadata(String value) { unitMetadata = value; } public String getIntegrityConclusion() { return integrityConclusion; } public void setIntegrityConclusion(String value) { integrityConclusion = value; } public Date getDeviceTimeUtc() { return deviceTimeUtc; } public void setDeviceTimeUtc(Date value) { deviceTimeUtc = value; } public String getDeviceTimeRaw() { return deviceTimeRaw; } public void setDeviceTimeRaw(String value) { deviceTimeRaw = value; } public Date getServerReceivedAtUtc() { return serverReceivedAtUtc; } public void setServerReceivedAtUtc(Date value) { serverReceivedAtUtc = value; } } ruoyi-system/src/main/java/com/ruoyi/system/mapper/TTestMapper.java
@@ -10,6 +10,8 @@ import com.ruoyi.system.domain.TTestCollectorChannel; import com.ruoyi.system.domain.TTestPlanStep; import com.ruoyi.system.domain.TTestResult; import com.ruoyi.system.domain.TTestResultQuery; import com.ruoyi.system.domain.TTestResultRecord; import com.ruoyi.system.domain.TTestWarningThreshold; import com.ruoyi.system.domain.TTestCollectorSource; import com.ruoyi.system.domain.TTestCollectorExceptionInterval; @@ -55,6 +57,7 @@ /** 按来源和起始边界顺序返回测试接收区间。 */ List<TTestReceptionInterval> selectTestReceptionIntervals(Long testId); List<TTestResult> selectTestResults(TTestResult result); List<TTestResultRecord> selectHistoricalResultRecords(TTestResultQuery query); int countTestTypeEnabled(Long testTypeId); int countUserEnabled(Long userId); int countBatteryInEnabledGroup(@Param("groupId") Long groupId, @Param("batteryNo") String batteryNo); ruoyi-system/src/main/java/com/ruoyi/system/service/ITTestService.java
@@ -3,6 +3,8 @@ import java.util.List; import com.ruoyi.system.domain.TTest; import com.ruoyi.system.domain.TTestResult; import com.ruoyi.system.domain.TTestResultQuery; import com.ruoyi.system.domain.TTestResultRecord; import com.ruoyi.system.domain.vo.TestConfigurationOptionsVo; /** 测试配置业务接口。 */ @@ -43,4 +45,7 @@ /** 查询指定测试已持久化的结果记录。 */ List<TTestResult> selectTestResults(TTestResult result); /** 查询检测完成测试的区间历史结果,实时快照和暂停数据不会返回。 */ List<TTestResultRecord> selectHistoricalResultRecords(TTestResultQuery query); } ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestServiceImpl.java
@@ -23,6 +23,8 @@ import com.ruoyi.system.domain.TTestCollectorChannel; import com.ruoyi.system.domain.TTestPlanStep; import com.ruoyi.system.domain.TTestResult; import com.ruoyi.system.domain.TTestResultQuery; import com.ruoyi.system.domain.TTestResultRecord; import com.ruoyi.system.domain.TTestFieldSnapshot; import com.ruoyi.system.domain.TTestWarningThreshold; import com.ruoyi.system.domain.TDetectionChannel; @@ -333,6 +335,12 @@ return testMapper.selectTestResults(result); } @Override public List<TTestResultRecord> selectHistoricalResultRecords(TTestResultQuery query) { return testMapper.selectHistoricalResultRecords(query == null ? new TTestResultQuery() : query); } private void loadConfiguration(TTest test) { Long testId = test.getTestId(); ruoyi-system/src/main/resources/mapper/system/TTestMapper.xml
@@ -97,6 +97,8 @@ <result property="minValue" column="min_value" /> <result property="maxValue" column="max_value" /> <result property="unit" column="unit" /> <result property="unitMetadata" column="unit_metadata" /> <result property="integrityConclusion" column="integrity_conclusion" /> <result property="severity" column="severity" /> <result property="delFlag" column="del_flag" /> </resultMap> @@ -110,6 +112,35 @@ <result property="resultTime" column="result_time" /> <result property="exceptionInfo" column="exception_info" /> <result property="delFlag" column="del_flag" /> </resultMap> <resultMap id="TTestResultRecordMap" type="TTestResultRecord"> <id property="resultId" column="result_id" /> <result property="sourceType" column="source_type" /> <result property="testId" column="test_id" /> <result property="testName" column="test_name" /> <result property="testStatus" column="test_status" /> <result property="receptionStatus" column="reception_status" /> <result property="relationId" column="relation_id" /> <result property="batteryNo" column="battery_no" /> <result property="sourceId" column="source_id" /> <result property="sourceIdentity" column="source_identity" /> <result property="intervalId" column="interval_id" /> <result property="intervalStartAtUtc" column="interval_start_at_utc" /> <result property="intervalEndAtUtc" column="interval_end_at_utc" /> <result property="cycleNo" column="cycle_no" /> <result property="stepNo" column="step_no" /> <result property="stepType" column="step_type" /> <result property="workStatus" column="work_status" /> <result property="voltageV" column="voltage_v" /> <result property="currentA" column="current_a" /> <result property="capacityAh" column="capacity_ah" /> <result property="energyWh" column="energy_wh" /> <result property="metricCode" column="metric_code" /> <result property="metricValue" column="metric_value" /> <result property="unit" column="unit" /> <result property="deviceTimeUtc" column="device_time_utc" /> <result property="deviceTimeRaw" column="device_time_raw" /> <result property="serverReceivedAtUtc" column="server_received_at_utc" /> </resultMap> <resultMap id="TTestCollectorExceptionIntervalResultMap" type="TTestCollectorExceptionInterval"> <id property="intervalId" column="interval_id" /> @@ -351,6 +382,58 @@ order by result.result_time, result.result_id </select> <select id="selectHistoricalResultRecords" parameterType="TTestResultQuery" resultMap="TTestResultRecordMap"> select * from ( select s.sample_id result_id, 'CABINET_CHANNEL' source_type, t.test_id, t.test_name, t.test_status, t.reception_status, b.relation_id, b.battery_no, s.channel_id source_id, concat('CABINET:', coalesce(cab.adapter_code, ''), ':', coalesce(cab.devtype, ''), ':', coalesce(cab.devid, ''), ':', coalesce(cab.subdevid, ''), ':', coalesce(cab.chlid, '')) source_identity, s.reception_interval_id interval_id, i.start_boundary_at_utc interval_start_at_utc, i.end_boundary_at_utc interval_end_at_utc, s.cycle_no, s.step_no, s.step_type, s.step_type work_status, s.voltage_v, s.current_a, s.capacity_ah, s.energy_wh, null metric_code, null metric_value, 'V/A/Ah/Wh' unit, 'voltage=V,current=A,capacity=Ah,energy=Wh' unit_metadata, s.device_time_utc, s.device_time_raw, s.received_at_utc server_received_at_utc, case when exists (select 1 from t_test_integrity_issue x where x.test_id = t.test_id and x.issue_status = 'OPEN') then '完成确认中' else '完整性已通过' end integrity_conclusion from t_test_sample s join t_test t on t.test_id = s.test_id and t.test_status = 'COMPLETED' and t.del_flag = 0 join t_test_channel_binding tb on tb.binding_id = s.binding_id join t_test_battery b on b.relation_id = tb.battery_relation_id and b.del_flag = 0 join t_test_reception_interval i on i.interval_id = s.reception_interval_id and i.interval_status = 'CLOSED' join t_detection_channel cab on cab.channel_id = s.channel_id union all select cs.sample_id result_id, 'COLLECTOR_CHANNEL' source_type, t.test_id, t.test_name, t.test_status, t.reception_status, b.relation_id, b.battery_no, cs.collector_id source_id, concat('COLLECTOR:', coalesce(col.collector_code, cs.collector_id), ':', cs.collector_channel_code) source_identity, cs.reception_interval_id interval_id, i.start_boundary_at_utc interval_start_at_utc, i.end_boundary_at_utc interval_end_at_utc, null cycle_no, null step_no, null step_type, null work_status, null voltage_v, null current_a, null capacity_ah, null energy_wh, cs.metric_code, cs.metric_value, cs.unit, concat('metric=', cs.metric_code, ',unit=', cs.unit) unit_metadata, cs.device_time_utc, cs.device_time_raw, cs.server_received_at_utc, case when exists (select 1 from t_test_integrity_issue x where x.test_id = t.test_id and x.issue_status = 'OPEN') then '完成确认中' else '完整性已通过' end integrity_conclusion from t_test_collector_sample cs join t_test t on t.test_id = cs.test_id and t.test_status = 'COMPLETED' and t.del_flag = 0 join t_test_channel_binding tb on tb.binding_id = cs.binding_id join t_test_battery b on b.relation_id = tb.battery_relation_id and b.del_flag = 0 join t_collector col on col.collector_id = cs.collector_id join t_test_reception_interval i on i.interval_id = cs.reception_interval_id and i.interval_status = 'CLOSED' ) result <where> <if test="testId != null">and result.test_id = #{testId}</if> <if test="testName != null and testName != ''">and result.test_name like concat('%', #{testName}, '%')</if> <if test="batteryNo != null and batteryNo != ''">and result.battery_no like concat('%', #{batteryNo}, '%')</if> <if test="sourceType != null and sourceType != ''">and result.source_type = #{sourceType}</if> <if test="sourceId != null">and result.source_id = #{sourceId}</if> <if test="sourceChannelCode != null and sourceChannelCode != ''">and result.source_identity like concat('%:', #{sourceChannelCode})</if> <if test="beginDeviceTime != null">and result.device_time_utc >= #{beginDeviceTime}</if> <if test="endDeviceTime != null">and result.device_time_utc <= #{endDeviceTime}</if> <if test="cycleNo != null">and result.cycle_no = #{cycleNo}</if> <if test="stepNo != null">and result.step_no = #{stepNo}</if> </where> order by result.device_time_utc, result.source_type, result.result_id </select> <select id="countTestTypeEnabled" resultType="int"> select count(1) from t_test_type where test_type_id = #{testTypeId} and status = '0' and del_flag = 0 </select>