From a2a8908f2d9d4e19201423ff58ee23c65a8df670 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期六, 29 八月 2026 20:51:10 +0800
Subject: [PATCH] 实现测试原子开始接收

---
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestServiceImpl.java |  147 ++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 133 insertions(+), 14 deletions(-)

diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestServiceImpl.java
index d077b3d..e2fedad 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestServiceImpl.java
@@ -10,6 +10,7 @@
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONException;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DuplicateKeyException;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import com.ruoyi.common.exception.ServiceException;
@@ -21,7 +22,10 @@
 import com.ruoyi.system.domain.TTestCollectorChannel;
 import com.ruoyi.system.domain.TTestPlanStep;
 import com.ruoyi.system.domain.TTestResult;
+import com.ruoyi.system.domain.TTestFieldSnapshot;
 import com.ruoyi.system.domain.TTestWarningThreshold;
+import com.ruoyi.system.domain.TDetectionChannel;
+import com.ruoyi.system.domain.TTestReceptionInterval;
 import com.ruoyi.system.domain.vo.TestConfigurationOptionsVo;
 import com.ruoyi.system.mapper.TTestMapper;
 import com.ruoyi.system.service.ITTestService;
@@ -146,7 +150,109 @@
     @Override
     public int startTest(Long testId, String updateBy)
     {
-        throw new ServiceException("开始接收由后续原子动作处理");
+        throw new ServiceException("旧开始入口已停用,请使用原子开始接收接口并提供操作人");
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int startReception(Long testId, Long operatorId, String updateBy)
+    {
+        if (testId == null) throw new ServiceException("测试ID不能为空");
+        if (operatorId == null) throw new ServiceException("操作人不能为空");
+        TTest current = testMapper.selectTestByIdForUpdate(testId);
+        if (current == null) throw new ServiceException("测试不存在");
+        if (!NOT_STARTED.equals(current.getReceptionStatus()))
+            throw new ServiceException("当前测试不是未开始接收状态");
+        loadConfiguration(current);
+        if (current.getBatteries().isEmpty()) throw new ServiceException("测试未配置电池号");
+
+        Date actionAt = new Date();
+        List<TTestReceptionInterval> intervals = new ArrayList<>();
+        Set<Long> channelIds = new HashSet<>();
+        Set<String> collectorChannels = new HashSet<>();
+        boolean runtimeAttach = false;
+        for (TTestBattery battery : current.getBatteries())
+        {
+            TTestChannelBinding binding = battery.getBinding();
+            if (binding == null || binding.getChannelId() == null)
+                throw new ServiceException("电池号 " + battery.getBatteryNo() + " 未绑定检测柜通道");
+            if (!channelIds.add(binding.getChannelId()))
+                throw new ServiceException("检测柜通道重复绑定,无法开始接收");
+            TDetectionChannel channel = testMapper.selectReceptionChannelForUpdate(binding.getChannelId());
+            if (channel == null || !isEnabled(channel.getStatus()) || !isOnline(channel.getOnlineStatus()))
+                throw new ServiceException("检测柜通道 " + binding.getChannelId() + " 当前离线或不可观测");
+            if (channel.getLastSeenAtUtc() == null)
+                throw new ServiceException("检测柜通道 " + binding.getChannelId() + " 尚未取得成功观测边界");
+            runtimeAttach |= "WORKING".equalsIgnoreCase(channel.getWorkStatus())
+                    || "PAUSED".equalsIgnoreCase(channel.getWorkStatus());
+            if (testMapper.selectChannelLeaseForUpdate(binding.getChannelId()) != null)
+                throw new ServiceException("检测柜通道 " + binding.getChannelId() + " 已被其他测试占用");
+            if (battery.getCollectorId() == null || testMapper.countCollectorEnabled(battery.getCollectorId()) == 0)
+                throw new ServiceException("电池号 " + battery.getBatteryNo() + " 的采集器不存在或未启用");
+            if (!testMapper.collectorOnline(battery.getCollectorId()))
+                throw new ServiceException("采集器 " + battery.getCollectorId() + " 当前离线或不可观测");
+            Integer collectorChannelCount = testMapper.selectCollectorChannelCount(battery.getCollectorId());
+            validateCollectorChannels(battery.getCollectorId(), collectorChannelCount == null ? 0 : collectorChannelCount,
+                    battery.getCollectorChannels(), collectorChannels);
+
+            intervals.add(interval(current, binding.getBindingId(), "CABINET_CHANNEL", binding.getChannelId(), null,
+                    actionAt, Date.from(channel.getLastSeenAtUtc()), runtimeAttach
+                            ? "运行中接入:采用检测柜最近成功观测边界" : "采用检测柜最近成功观测边界"));
+            for (TTestCollectorChannel mapping : mutable(battery.getCollectorChannels()))
+            {
+                intervals.add(interval(current, binding.getBindingId(), "COLLECTOR_CHANNEL", battery.getCollectorId(),
+                        mapping.getChannelCode(), actionAt, actionAt, "采集器开始接收确认边界"));
+            }
+        }
+
+        try
+        {
+            for (TTestBattery battery : current.getBatteries())
+            {
+                TTestChannelBinding binding = battery.getBinding();
+                testMapper.insertChannelLease(binding.getChannelId(), testId, binding.getBindingId(), operatorId, actionAt);
+                for (TTestCollectorChannel mapping : mutable(battery.getCollectorChannels()))
+                    testMapper.insertCollectorChannelLease(battery.getCollectorId(), mapping.getChannelCode(), testId,
+                            binding.getBindingId(), operatorId, actionAt);
+            }
+            for (TTestReceptionInterval interval : intervals) testMapper.insertReceptionInterval(interval);
+            if (testMapper.updateReceptionStarted(testId, actionAt, updateBy) != 1)
+                throw new ServiceException("测试状态已变更,请刷新后重试");
+            testMapper.insertTestAudit(testId, "RECEPTION_STARTED", operatorId, updateBy, null,
+                    JSON.toJSONString(Map.of("receptionStatus", "RECEIVING", "intervalCount", intervals.size(),
+                            "runtimeAttach", runtimeAttach, "intervals", intervals)));
+            return 1;
+        }
+        catch (DuplicateKeyException exception)
+        {
+            throw new ServiceException("数据来源已被其他测试占用,请刷新后重试");
+        }
+    }
+
+    private TTestReceptionInterval interval(TTest test, Long bindingId, String sourceType, Long sourceId,
+            String sourceChannelCode, Date actionAt, Date boundaryAt, String note)
+    {
+        TTestReceptionInterval interval = new TTestReceptionInterval();
+        interval.setTestId(test.getTestId());
+        interval.setBindingId(bindingId);
+        interval.setSourceType(sourceType);
+        interval.setSourceId(sourceId);
+        interval.setSourceChannelCode(sourceChannelCode);
+        interval.setActionAtUtc(actionAt);
+        interval.setStartBoundaryAtUtc(boundaryAt);
+        interval.setBoundaryNote(note);
+        interval.setIntervalStatus("OPEN");
+        return interval;
+    }
+
+    private boolean isOnline(String status)
+    {
+        return status != null && ("ONLINE".equalsIgnoreCase(status) || "0".equals(status));
+    }
+
+    private boolean isEnabled(String status)
+    {
+        return status == null || "0".equals(status);
     }
 
     @Override
@@ -181,7 +287,8 @@
     private void loadConfiguration(TTest test)
     {
         Long testId = test.getTestId();
-        List<TTestPlanStep> planSteps = mutable(testMapper.selectTestPlanSteps(testId));
+        List<TTestPlanStep> loadedPlanSteps = testMapper.selectTestPlanSteps(testId);
+        List<TTestPlanStep> planSteps = loadedPlanSteps == null ? mutable(test.getPlanSteps()) : mutable(loadedPlanSteps);
         for (TTestPlanStep step : planSteps)
         {
             try
@@ -194,27 +301,39 @@
             }
         }
 
-        List<TTestBattery> batteries = mutable(testMapper.selectTestBatteries(testId));
-        List<TTestBatteryField> fields = mutable(testMapper.selectTestBatteryFields(testId));
-        List<TTestCollectorChannel> collectorChannels = mutable(testMapper.selectTestCollectorChannels(testId));
+        List<TTestBattery> loadedBatteries = testMapper.selectTestBatteries(testId);
+        List<TTestBattery> batteries = loadedBatteries == null || (loadedBatteries.isEmpty() && test.getBatteries() != null
+                && !test.getBatteries().isEmpty()) ? mutable(test.getBatteries()) : mutable(loadedBatteries);
+        List<TTestBatteryField> loadedFields = testMapper.selectTestBatteryFields(testId);
+        List<TTestBatteryField> fields = loadedFields == null ? List.of() : mutable(loadedFields);
+        List<TTestCollectorChannel> loadedCollectorChannels = testMapper.selectTestCollectorChannels(testId);
+        List<TTestCollectorChannel> collectorChannels = loadedCollectorChannels == null ? List.of() : mutable(loadedCollectorChannels);
         Map<Long, TTestChannelBinding> bindings = new HashMap<>();
-        for (TTestChannelBinding binding : mutable(testMapper.selectTestChannelBindings(testId)))
+        List<TTestChannelBinding> loadedBindings = testMapper.selectTestChannelBindings(testId);
+        if (loadedBindings == null || (loadedBindings.isEmpty() && batteries.stream().anyMatch(item -> item.getBinding() != null)))
+            for (TTestBattery battery : batteries)
+                if (battery.getBinding() != null) bindings.put(battery.getRelationId(), battery.getBinding());
+        for (TTestChannelBinding binding : mutable(loadedBindings))
             bindings.put(binding.getBatteryRelationId(), binding);
         for (TTestBattery battery : batteries)
         {
             battery.setBinding(bindings.get(battery.getRelationId()));
-            battery.setFields(fields.stream()
-                    .filter(field -> battery.getRelationId().equals(field.getRelationId()))
-                    .toList());
-            battery.setCollectorChannels(collectorChannels.stream()
-                    .filter(mapping -> battery.getRelationId().equals(mapping.getBatteryRelationId()))
-                    .toList());
+            if (loadedFields != null && (!loadedFields.isEmpty() || battery.getFields() == null))
+                battery.setFields(fields.stream()
+                        .filter(field -> battery.getRelationId().equals(field.getRelationId()))
+                        .toList());
+            if (loadedCollectorChannels != null && (!loadedCollectorChannels.isEmpty() || battery.getCollectorChannels() == null))
+                battery.setCollectorChannels(collectorChannels.stream()
+                        .filter(mapping -> battery.getRelationId().equals(mapping.getBatteryRelationId()))
+                        .toList());
         }
         test.setPlanSteps(planSteps);
         test.setBatteries(batteries);
         test.setBatteryNo(joinBatteryNos(batteries));
-        test.setThresholds(mutable(testMapper.selectTestThresholds(testId)));
-        test.setFieldSnapshots(sharedFieldSnapshotService.selectByTestId(testId));
+        List<TTestWarningThreshold> loadedThresholds = testMapper.selectTestThresholds(testId);
+        test.setThresholds(loadedThresholds == null ? mutable(test.getThresholds()) : mutable(loadedThresholds));
+        List<TTestFieldSnapshot> loadedSnapshots = sharedFieldSnapshotService.selectByTestId(testId);
+        test.setFieldSnapshots(loadedSnapshots == null ? mutable(test.getFieldSnapshots()) : loadedSnapshots);
     }
 
     private void validateBase(TTest test, boolean update)

--
Gitblit v1.7.1