| ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TTestController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| ruoyi-admin/src/main/resources/db/migration/V32__test_console_detail.sql | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/TTestControllerContractTest.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| ruoyi-system/src/main/java/com/ruoyi/system/domain/TTest.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestConsoleDetail.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
@@ -21,6 +21,7 @@ import com.ruoyi.system.domain.TTest; import com.ruoyi.system.domain.TTestResult; import com.ruoyi.system.domain.TTestLogicStepMapping; import com.ruoyi.system.domain.TTestConsoleDetail; import com.ruoyi.system.domain.TTestDataRevisionAction; import com.ruoyi.system.domain.TTestDataOverride; import com.ruoyi.system.service.ITTestService; @@ -157,6 +158,15 @@ return success(testService.selectTestById(testId)); } /** 保存工步图表测试详情当前最新版,不参与测试配置、统计或审计。 */ @PreAuthorize("@ss.hasPermi('system:test:edit')") @PutMapping("/{testId}/console-detail") public AjaxResult saveConsoleDetail(@PathVariable Long testId, @RequestBody TTestConsoleDetail detail) { testService.saveConsoleDetail(testId, detail, getUserId(), getUsername()); return success(testService.selectTestById(testId)); } /** 返回当前测试某类数据的人工覆盖和删除状态,供刷新后恢复展示。 */ @PreAuthorize("@ss.hasPermi('system:test:query')") @GetMapping("/{testId}/console-data/{dataType}/overrides") ruoyi-admin/src/main/resources/db/migration/V32__test_console_detail.sql
New file @@ -0,0 +1,3 @@ -- Ticket03:工步图表测试详情只保留当前测试的最新版,不建立版本或审计记录。 ALTER TABLE t_test ADD COLUMN console_detail_json JSON NULL COMMENT '工步图表测试详情当前记录' AFTER logic_step_mappings_json; ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/TTestControllerContractTest.java
@@ -4,6 +4,7 @@ import java.lang.reflect.Method; import org.junit.jupiter.api.Test; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.security.access.prepost.PreAuthorize; class TTestControllerContractTest @@ -39,6 +40,10 @@ assertPermission("persistedCycles", "@ss.hasPermi('system:test:query')", Long.class); assertEquals("/{testId}/data/cycles", TTestController.class.getMethod("persistedCycles", Long.class) .getAnnotation(org.springframework.web.bind.annotation.GetMapping.class).value()[0]); assertPermission("saveConsoleDetail", "@ss.hasPermi('system:test:edit')", Long.class, com.ruoyi.system.domain.TTestConsoleDetail.class); assertEquals("/{testId}/console-detail", TTestController.class.getMethod("saveConsoleDetail", Long.class, com.ruoyi.system.domain.TTestConsoleDetail.class).getAnnotation(PutMapping.class).value()[0]); } private void assertPermission(String methodName, String permission, Class<?>... parameterTypes) throws Exception ruoyi-system/src/main/java/com/ruoyi/system/domain/TTest.java
@@ -58,6 +58,9 @@ private List<TTestResult> resultSummary; private String logicStepMappingsJson; private List<TTestLogicStepMapping> logicStepMappings; /** 工步图表中的测试补充详情,仅保存当前最新版。 */ private String consoleDetailJson; private TTestConsoleDetail consoleDetail; public Long getTestId() { return testId; } public void setTestId(Long value) { testId = value; } @@ -115,4 +118,8 @@ public void setLogicStepMappingsJson(String value) { logicStepMappingsJson = value; } public List<TTestLogicStepMapping> getLogicStepMappings() { return logicStepMappings; } public void setLogicStepMappings(List<TTestLogicStepMapping> value) { logicStepMappings = value; } public String getConsoleDetailJson() { return consoleDetailJson; } public void setConsoleDetailJson(String value) { consoleDetailJson = value; } public TTestConsoleDetail getConsoleDetail() { return consoleDetail; } public void setConsoleDetail(TTestConsoleDetail value) { consoleDetail = value; } } ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestConsoleDetail.java
New file @@ -0,0 +1,23 @@ package com.ruoyi.system.domain; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** 工步图表中的测试补充详情,仅保留当前测试的一份记录。 */ public class TTestConsoleDetail { private String experimentName; private List<String> batteryFields = new java.util.ArrayList<>(); private List<String> specialFields = new java.util.ArrayList<>(); private Map<String, Map<String, String>> fieldValues = new LinkedHashMap<>(); public String getExperimentName() { return experimentName; } public void setExperimentName(String value) { experimentName = value; } public List<String> getBatteryFields() { return batteryFields; } public void setBatteryFields(List<String> value) { batteryFields = value; } public List<String> getSpecialFields() { return specialFields; } public void setSpecialFields(List<String> value) { specialFields = value; } public Map<String, Map<String, String>> getFieldValues() { return fieldValues; } public void setFieldValues(Map<String, Map<String, String>> value) { fieldValues = value == null ? new LinkedHashMap<>() : value; } } ruoyi-system/src/main/java/com/ruoyi/system/mapper/TTestMapper.java
@@ -75,6 +75,8 @@ String selectLogicStepMappingsJson(Long testId); int updateLogicStepMappings(@Param("testId") Long testId, @Param("mappingsJson") String mappingsJson, @Param("updateBy") String updateBy, @Param("updateTime") Date updateTime); int updateConsoleDetail(@Param("testId") Long testId, @Param("detailJson") String detailJson, @Param("updateBy") String updateBy, @Param("updateTime") Date updateTime); 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
@@ -5,6 +5,7 @@ import com.ruoyi.system.domain.TTestResult; import com.ruoyi.system.domain.vo.TestConfigurationOptionsVo; import com.ruoyi.system.domain.TTestLogicStepMapping; import com.ruoyi.system.domain.TTestConsoleDetail; /** 测试配置业务接口。 */ public interface ITTestService @@ -46,5 +47,7 @@ List<TTestResult> selectTestResults(TTestResult result); /** 保存当前测试的逻辑工步合并规则。 */ int saveLogicStepMappings(Long testId, List<TTestLogicStepMapping> mappings, Long operatorId, String updateBy); /** 保存测试详情当前最新版,允许所有测试状态且不产生审计记录。 */ int saveConsoleDetail(Long testId, TTestConsoleDetail detail, Long operatorId, String updateBy); } ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestServiceImpl.java
@@ -26,6 +26,7 @@ import com.ruoyi.system.domain.TTestFieldSnapshot; import com.ruoyi.system.domain.TTestWarningThreshold; import com.ruoyi.system.domain.TTestLogicStepMapping; import com.ruoyi.system.domain.TTestConsoleDetail; import com.ruoyi.system.domain.TDetectionChannel; import com.ruoyi.system.domain.vo.TestConfigurationOptionsVo; import com.ruoyi.system.mapper.TTestMapper; @@ -330,6 +331,57 @@ return rows; } @Override @Transactional(rollbackFor = Exception.class) public int saveConsoleDetail(Long testId, TTestConsoleDetail detail, Long operatorId, String updateBy) { if (testId == null || operatorId == null || detail == null) throw new ServiceException("测试详情不能为空"); TTest current = testMapper.selectTestByIdForUpdate(testId); if (current == null) throw new ServiceException("测试不存在"); String experimentName = trimToNull(detail.getExperimentName()); if (experimentName != null && experimentName.length() > 200) throw new ServiceException("实验名称长度不能超过200个字符"); detail.setExperimentName(experimentName); validateConsoleFields(detail.getBatteryFields()); validateConsoleFields(detail.getSpecialFields()); validateConsoleValues(testId, detail); int rows = testMapper.updateConsoleDetail(testId, JSON.toJSONString(detail), updateBy, new Date()); if (rows != 1) throw new ServiceException("测试详情保存失败,请刷新后重试"); return rows; } private void validateConsoleFields(List<String> fields) { if (fields == null) return; Set<String> names = new HashSet<>(); for (String field : fields) { String normalized = trimToNull(field); if (normalized == null || normalized.length() > 100 || !names.add(normalized)) throw new ServiceException("测试详情字段不能为空、长度不能超过100且不可重复"); } } private void validateConsoleValues(Long testId, TTestConsoleDetail detail) { Set<String> allowedFields = new HashSet<>(); if (detail.getBatteryFields() != null) allowedFields.addAll(detail.getBatteryFields()); if (detail.getSpecialFields() != null) allowedFields.addAll(detail.getSpecialFields()); Set<String> batteryNos = new HashSet<>(); List<TTestBattery> batteries = testMapper.selectTestBatteries(testId); if (batteries != null) for (TTestBattery battery : batteries) batteryNos.add(battery.getBatteryNo()); if (detail.getFieldValues() == null) return; for (Map.Entry<String, Map<String, String>> entry : detail.getFieldValues().entrySet()) { if (!allowedFields.contains(entry.getKey()) || entry.getValue() == null) throw new ServiceException("测试详情字段值结构无效"); for (Map.Entry<String, String> value : entry.getValue().entrySet()) { if (!batteryNos.contains(value.getKey()) || (value.getValue() != null && value.getValue().length() > 500)) throw new ServiceException("测试详情电池号或字段值无效"); } } } private void loadConfiguration(TTest test) { Long testId = test.getTestId(); @@ -384,6 +436,10 @@ try { test.setLogicStepMappings(mappingJson == null || mappingJson.isBlank() ? new ArrayList<>() : JSON.parseArray(mappingJson, TTestLogicStepMapping.class)); } catch (JSONException exception) { throw new ServiceException("逻辑工步设置无法解析"); } String detailJson = test.getConsoleDetailJson(); try { test.setConsoleDetail(detailJson == null || detailJson.isBlank() ? null : JSON.parseObject(detailJson, TTestConsoleDetail.class)); } catch (JSONException exception) { throw new ServiceException("测试详情无法解析"); } } private void validateBase(TTest test, boolean update) ruoyi-system/src/main/resources/mapper/system/TTestMapper.xml
@@ -26,6 +26,7 @@ <result property="updateTime" column="update_time" /> <result property="remark" column="remark" /> <result property="logicStepMappingsJson" column="logic_step_mappings_json" /> <result property="consoleDetailJson" column="console_detail_json" /> <result property="batteryCount" column="battery_count" /> <result property="batteryNo" column="battery_no" /> </resultMap> @@ -120,6 +121,7 @@ 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, cast(t.logic_step_mappings_json as char) logic_step_mappings_json, cast(t.console_detail_json as char) console_detail_json, (select count(1) from t_test_battery b where b.test_id = t.test_id and b.del_flag = 0) battery_count, t.runtime_attach runtime_attach from t_test t @@ -478,6 +480,10 @@ update_by = #{updateBy}, update_time = #{updateTime} where test_id = #{testId} and del_flag = 0 </update> <update id="updateConsoleDetail"> update t_test set console_detail_json = cast(#{detailJson} as json) where test_id = #{testId} and del_flag = 0 </update> <select id="countTestTypeEnabled" resultType="int"> select count(1) from t_test_type where test_type_id = #{testTypeId} and status = '0' and del_flag = 0