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