From 3b0d51d628c772c47845d9fd0b95ebde8a969d07 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期二, 01 九月 2026 11:59:12 +0800
Subject: [PATCH] 实现数据修订行为模型

---
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestConsoleDataServiceImpl.java |   94 +++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 86 insertions(+), 8 deletions(-)

diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestConsoleDataServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestConsoleDataServiceImpl.java
index d557873..4655e0d 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestConsoleDataServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestConsoleDataServiceImpl.java
@@ -28,6 +28,24 @@
     private TTestMapper mapper;
 
     @Override
+    public List<TTestDataRevisionAction> selectActions(TTestDataRevisionAction query)
+    {
+        if (query == null) query = new TTestDataRevisionAction();
+        if (query.getDataType() != null && !query.getDataType().isBlank()
+                && !DATA_TYPES.contains(query.getDataType())) throw new ServiceException("控制台数据类型无效");
+        if (query.getOperationType() != null && !query.getOperationType().isBlank()
+                && !OPERATIONS.contains(query.getOperationType())) throw new ServiceException("数据操作类型无效");
+        return mapper.selectDataRevisionActions(query);
+    }
+
+    @Override
+    public TTestDataRevisionAction selectActionById(Long actionId)
+    {
+        if (actionId == null || actionId < 1) throw new ServiceException("修订行为ID无效");
+        return mapper.selectDataRevisionActionById(actionId);
+    }
+
+    @Override
     public List<TTestDataOverride> selectOverrides(Long testId, String dataType)
     {
         validateType(testId, dataType);
@@ -50,6 +68,7 @@
         }
         if ("EDIT".equals(action.getOperationType())) validateEditableFields(action);
         if (action.getReason() == null || action.getReason().trim().isEmpty()) throw new ServiceException("数据操作原因不能为空");
+        validateActionSummaries(action, overrides);
         if (action.getOperatorName() == null || action.getOperatorName().trim().isEmpty()) action.setOperatorName("未知用户");
         action.setReason(action.getReason().trim());
         action.setCreatedAtUtc(new Date());
@@ -95,18 +114,31 @@
                 mapper.upsertDataOverride(item);
             }
         }
-        if ("DELETE".equals(action.getOperationType()) && "REALTIME".equals(action.getDataType()) && overrides != null)
+        if ("DELETE".equals(action.getOperationType()) && overrides != null)
         {
             List<Long> resolvedIds = new ArrayList<>();
             for (TTestDataOverride item : overrides)
             {
-                Long[] identity = parseRealtimeIdentity(item.getStableIdentity());
-                if (identity == null) continue;
-                Long sampleId = mapper.selectBtsSampleIdByChannelSequence(action.getTestId(), identity[0], identity[1]);
-                if (sampleId != null) resolvedIds.add(sampleId);
+                Long[] identity = parseDeleteIdentity(item.getStableIdentity(), action.getDataType());
+                if (identity == null) throw new ServiceException("删除数据稳定身份无效");
+                switch (action.getDataType())
+                {
+                    case "REALTIME" -> {
+                        Long sampleId = mapper.selectBtsSampleIdByDeviceChannelSequence(action.getTestId(),
+                                identity[0], identity[1], identity[2]);
+                        if (sampleId != null) resolvedIds.add(sampleId);
+                    }
+                    case "STEP" -> resolvedIds.addAll(mapper.selectBtsSampleIdsByDeviceChannelStep(action.getTestId(),
+                            identity[0], identity[1], identity[2].intValue(), identity[3].intValue()));
+                    case "CYCLE" -> resolvedIds.addAll(mapper.selectBtsSampleIdsByDeviceChannelCycle(action.getTestId(),
+                            identity[0], identity[1], identity[2].intValue()));
+                    default -> throw new ServiceException("控制台数据类型无效");
+                }
             }
             if (!resolvedIds.isEmpty())
             {
+                resolvedIds = new ArrayList<>(new HashSet<>(resolvedIds));
+                // 工步和循环是采样聚合视图,物理删除需在同一事务内清理对应底层采样。
                 mapper.deleteBtsMeasurementsBySampleIds(resolvedIds);
                 mapper.deleteBtsSamplesByIds(action.getTestId(), resolvedIds);
             }
@@ -175,6 +207,43 @@
         catch (Exception exception) { throw new ServiceException("修改字段格式无效"); }
     }
 
+    /** 防止客户端提交与实际覆盖身份不一致的伪行为摘要。 */
+    private void validateActionSummaries(TTestDataRevisionAction action, List<TTestDataOverride> overrides)
+    {
+        if (action.getScopeSummary() == null || action.getScopeSummary().trim().isEmpty())
+            throw new ServiceException("数据操作范围不能为空");
+        Set<String> summaryIdentities = new HashSet<>();
+        try
+        {
+            Object parsed = JSON.parse(action.getIdentitySummary());
+            if (!(parsed instanceof List<?> values)) throw new ServiceException("身份摘要格式无效");
+            for (Object value : values)
+                if (value == null || !summaryIdentities.add(String.valueOf(value)))
+                    throw new ServiceException("身份摘要存在重复或空值");
+        }
+        catch (ServiceException exception) { throw exception; }
+        catch (Exception exception) { throw new ServiceException("身份摘要格式无效"); }
+        if (overrides == null) return;
+        Set<String> overrideIdentities = new HashSet<>();
+        for (TTestDataOverride item : overrides)
+            if (item == null || !overrideIdentities.add(item.getStableIdentity())
+                    || !summaryIdentities.contains(item.getStableIdentity()))
+                throw new ServiceException("身份摘要与数据覆盖明细不一致");
+        if (!summaryIdentities.equals(overrideIdentities))
+            throw new ServiceException("身份摘要与数据覆盖明细不一致");
+        if ("EDIT".equals(action.getOperationType()))
+        {
+            try
+            {
+                Object parsed = JSON.parse(action.getChangesSummary());
+                if (!(parsed instanceof List<?> changes) || changes.size() < overrides.size())
+                    throw new ServiceException("字段变化摘要与数据覆盖明细不一致");
+            }
+            catch (ServiceException exception) { throw exception; }
+            catch (Exception exception) { throw new ServiceException("字段变化摘要格式无效"); }
+        }
+    }
+
     private Set<String> editableFields(String dataType)
     {
         return switch (dataType)
@@ -188,11 +257,20 @@
         };
     }
 
-    private Long[] parseRealtimeIdentity(String value)
+    private Long[] parseDeleteIdentity(String value, String dataType)
     {
         String[] parts = value.split(":", -1);
-        if (parts.length != 3) return null;
-        try { return new Long[] { Long.valueOf(parts[0]), Long.valueOf(parts[2]) }; }
+        int expectedLength = "STEP".equals(dataType) ? 5 : "CYCLE".equals(dataType) ? 4 : 3;
+        if (parts.length != expectedLength) return null;
+        int offset = "REALTIME".equals(dataType) ? 0 : 1;
+        try
+        {
+            if ("STEP".equals(dataType))
+                return new Long[] { Long.valueOf(parts[offset]), Long.valueOf(parts[offset + 1]),
+                        Long.valueOf(parts[offset + 2]), Long.valueOf(parts[offset + 3]) };
+            return new Long[] { Long.valueOf(parts[offset]), Long.valueOf(parts[offset + 1]),
+                    Long.valueOf(parts[offset + 2]) };
+        }
         catch (NumberFormatException exception) { return null; }
     }
 

--
Gitblit v1.7.1