From bc9a169ce3648467136a279d167f72c4fbab9595 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期六, 05 九月 2026 00:53:48 +0800
Subject: [PATCH] 完成设备发现登记与状态事实

---
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DeviceDiscoveryPersistenceService.java |   20 +++++
 ruoyi-system/src/main/java/com/ruoyi/system/domain/TCollector.java                              |   21 ++++
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DeviceAdapterServiceImpl.java          |    6 +
 ruoyi-system/src/main/java/com/ruoyi/system/domain/TDetectionCabinet.java                       |    3 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCollectorServiceImpl.java             |   14 +++
 ruoyi-system/src/main/resources/mapper/system/TCollectorMapper.xml                              |   45 ++++++++--
 ruoyi-system/src/main/resources/mapper/system/TDeviceAdapterMapper.xml                          |   14 +-
 ruoyi-system/src/main/java/com/ruoyi/system/domain/TDeviceAdapter.java                          |    1 
 ruoyi-system/src/main/resources/mapper/system/TDetectionCabinetMapper.xml                       |   45 +++++++++++
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/TDetectionCabinetMapper.java                 |   17 ++++
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TDetectionCabinetServiceImpl.java      |   41 +++++++++-
 11 files changed, 197 insertions(+), 30 deletions(-)

diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TCollector.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TCollector.java
index 43be90f..5de578e 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TCollector.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TCollector.java
@@ -5,6 +5,7 @@
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
+import java.time.Instant;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ruoyi.common.core.domain.BaseEntity;
@@ -20,6 +21,12 @@
 
     /** 采集器主键 */
     private Long collectorId;
+
+    /** 所属设备适配器实例;不表达测试占用。 */
+    private Long adapterId;
+
+    /** 适配器最近一次观测该采集器的时间(UTC)。 */
+    private Instant lastSeenAtUtc;
 
     /** 设备综合查询条件 */
     private String device;
@@ -67,13 +74,13 @@
     /** 在线状态:0离线、1在线 */
     private String onlineStatus;
 
-    /** 测试状态:0未测试、1测试中 */
+    /** 测试状态展示值,由测试绑定事实派生,不写入采集器表。 */
     private String testStatus;
 
-    /** 当前测试 */
+    /** 当前测试展示值,由测试绑定事实派生,不写入采集器表。 */
     private String currentTest;
 
-    /** 当前样品名称 */
+    /** 兼容旧页面的派生字段,不参与采集器登记持久化。 */
     private String currentSampleName;
 
     /** 状态:0启用、1禁用 */
@@ -92,6 +99,14 @@
         this.collectorId = collectorId;
     }
 
+    public Long getAdapterId() { return adapterId; }
+
+    public void setAdapterId(Long adapterId) { this.adapterId = adapterId; }
+
+    public Instant getLastSeenAtUtc() { return lastSeenAtUtc; }
+
+    public void setLastSeenAtUtc(Instant lastSeenAtUtc) { this.lastSeenAtUtc = lastSeenAtUtc; }
+
     public String getDevice()
     {
         return device;
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TDetectionCabinet.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TDetectionCabinet.java
index 82b9a37..849e1d6 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TDetectionCabinet.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TDetectionCabinet.java
@@ -74,9 +74,10 @@
     /** 在线状态:0正常,1异常。 */
     private String onlineStatus;
 
-    /** 测试状态:0未测试,1测试中。 */
+    /** 测试状态展示值,由通道绑定事实派生,不写入检测柜表。 */
     private String testStatus;
 
+    /** 当前测试展示值,由通道绑定事实派生,不写入检测柜表。 */
     @Size(max = 200, message = "当前测试内容长度不能超过200个字符")
     private String currentTest;
 
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TDeviceAdapter.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TDeviceAdapter.java
index d4d583f..5891cb0 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TDeviceAdapter.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TDeviceAdapter.java
@@ -50,6 +50,7 @@
 
     private String capabilities;
     private String status;
+    /** 适配器连接状态,仅允许 ONLINE/OFFLINE。 */
     private String connectionStatus;
     private String lastErrorCode;
     private String lastErrorMessage;
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TDetectionCabinetMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TDetectionCabinetMapper.java
index 7105265..948cc21 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TDetectionCabinetMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TDetectionCabinetMapper.java
@@ -74,6 +74,23 @@
             @Param("onlineStatus") String onlineStatus, @Param("workStatus") String workStatus,
             @Param("lastSeenAtUtc") java.time.Instant lastSeenAtUtc, @Param("updateLastSeen") boolean updateLastSeen);
 
+    /** 发现失败或空拓扑时清除该适配器下已登记设备的过期在线事实。 */
+    int markRegisteredFactsUnobservable(@Param("adapterCode") String adapterCode);
+
+    /** 同步检测柜在线事实为离线。 */
+    int markRegisteredCabinetsOffline(@Param("adapterCode") String adapterCode);
+
+    /** 仅推进成功发现的通道最近观测时间,不改变工作状态。 */
+    int touchRegisteredChannelObservation(@Param("adapterCode") String adapterCode,
+            @Param("devtype") int devtype, @Param("devid") int devid,
+            @Param("subdevid") int subdevid, @Param("chlid") int chlid,
+            @Param("lastSeenAtUtc") java.time.Instant lastSeenAtUtc);
+
+    /** 仅推进成功发现的检测柜最近观测时间,不改变在线状态。 */
+    int touchRegisteredCabinetObservation(@Param("adapterCode") String adapterCode,
+            @Param("devtype") int devtype, @Param("devid") int devid,
+            @Param("lastSeenAtUtc") java.time.Instant lastSeenAtUtc);
+
     /** 软删除正式检测柜对应的通道拓扑。 */
     int deleteDetectionChannelsByCabinetId(@Param("cabinetId") Long cabinetId);
 
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DeviceAdapterServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DeviceAdapterServiceImpl.java
index c3f3bfb..2b2567f 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DeviceAdapterServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DeviceAdapterServiceImpl.java
@@ -71,7 +71,7 @@
         if (capabilities.control())
             throw error("当前里程碑只允许只读设备适配器", DeviceAdapterErrorCodes.INVALID_CONFIG);
         adapter.setStatus(ENABLED);
-        adapter.setConnectionStatus("DISCONNECTED");
+        adapter.setConnectionStatus("OFFLINE");
         adapter.setDelFlag("0");
         mapper.upsertManagedDeviceAdapter(adapter);
         TDeviceAdapter registered = mapper.selectDeviceAdapterByCode(adapter.getAdapterCode());
@@ -89,7 +89,7 @@
             throw error("适配器编码已存在", DeviceAdapterErrorCodes.DUPLICATE_CODE);
         adapter.setCapabilities(READ_ONLY_CAPABILITIES);
         adapter.setStatus(DISABLED);
-        adapter.setConnectionStatus("DISCONNECTED");
+        adapter.setConnectionStatus("OFFLINE");
         adapter.setDelFlag("0");
         try { return mapper.insertDeviceAdapter(adapter); }
         catch (DuplicateKeyException exception) { throw error("适配器编码已存在", DeviceAdapterErrorCodes.DUPLICATE_CODE); }
@@ -143,10 +143,12 @@
         }
         catch (DeviceAdapterException exception)
         {
+            persistence.markUnavailable(adapter.getAdapterCode());
             return failDiscovery(adapterId, exception.failureCode(), exception);
         }
         catch (AdapterRegistryException exception)
         {
+            persistence.markUnavailable(adapter.getAdapterCode());
             return failDiscovery(adapterId, "ADAPTER_FAILURE", exception);
         }
     }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DeviceDiscoveryPersistenceService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DeviceDiscoveryPersistenceService.java
index 95e56b2..b0ebda9 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DeviceDiscoveryPersistenceService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/DeviceDiscoveryPersistenceService.java
@@ -65,6 +65,13 @@
         return testMapper == null || testMapper.hasReceivingTestForAdapter(adapterCode) > 0;
     }
 
+    /** 发现失败或返回空拓扑时,清除该适配器下已登记设备的过期运行事实。 */
+    public void markUnavailable(String adapterCode)
+    {
+        detectionCabinetMapper.markRegisteredFactsUnobservable(adapterCode);
+        detectionCabinetMapper.markRegisteredCabinetsOffline(adapterCode);
+    }
+
     private AdapterDiscoveryResult discoverNow(DeviceAdapterRegistry registry, DiscoverySource source)
     {
         DiscoveryObservation observation = registry.execute(source.adapterCode(), runtime -> {
@@ -97,6 +104,7 @@
                         observedAt, observedAt.plus(lifetime)))
                 .toList();
         candidateStore.recordCabinets(candidates);
+        if (detected.isEmpty()) markUnavailable(source.adapterCode());
         synchronizeRegisteredFacts(source, detected, snapshots, observedAt);
         return new AdapterDiscoveryResult(grouped.size(), detected.size(), observedAt);
     }
@@ -132,6 +140,9 @@
             detectionCabinetMapper.updateRegisteredChannelObservation(source.adapterCode(), identity.devtype(),
                     identity.devid(), identity.subdevid(), identity.chlid(), channel.available() ? "ONLINE" : "OFFLINE",
                     workStatus.name(), observedAt, successfulObservation);
+            if (channel.available() && !successfulObservation)
+                detectionCabinetMapper.touchRegisteredChannelObservation(source.adapterCode(), identity.devtype(),
+                        identity.devid(), identity.subdevid(), identity.chlid(), observedAt);
             CabinetKey cabinet = new CabinetKey(identity.devtype(), identity.devid());
             cabinetOnline.merge(cabinet, channel.available(), Boolean::logicalOr);
         }
@@ -141,6 +152,15 @@
                         && channel.identity().devtype() == cabinet.devtype()
                         && channel.identity().devid() == cabinet.devid()
                         && snapshots.containsKey(channel.identity()))));
+        cabinetOnline.forEach((cabinet, online) -> {
+            boolean successfulSnapshot = online && detected.stream().anyMatch(channel -> channel.available()
+                    && channel.identity().devtype() == cabinet.devtype()
+                    && channel.identity().devid() == cabinet.devid()
+                    && snapshots.containsKey(channel.identity()));
+            if (online && !successfulSnapshot)
+                detectionCabinetMapper.touchRegisteredCabinetObservation(source.adapterCode(), cabinet.devtype(),
+                        cabinet.devid(), observedAt);
+        });
     }
 
     private CabinetCandidate toCandidate(DiscoverySource source, CabinetKey key, List<DetectedChannel> channels,
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCollectorServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCollectorServiceImpl.java
index 24f941b..c6cf102 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCollectorServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCollectorServiceImpl.java
@@ -105,6 +105,7 @@
         collector.setChannelCount(candidate.channelCount());
         validateCollector(collector, false);
         collector.setOnlineStatus(OFFLINE);
+        // 测试占用由 t_test_collector_channel + t_test 派生,采集器登记表不写入这些字段。
         collector.setTestStatus(IDLE);
         collector.setCurrentTest(null);
         collector.setCurrentSampleName(null);
@@ -118,7 +119,11 @@
         }
         collector.setDelFlag("0");
         collector.setCreateTime(new Date());
-        return collectorMapper.insertCollector(collector);
+        int rows = collectorMapper.insertCollector(collector);
+        // 登记瞬间同步候选事实,避免正式记录暂时显示为离线且没有最近观测时间。
+        collectorMapper.updateRegisteredObservation(collector.getCollectorCode(),
+                candidate.online() ? "ONLINE" : "OFFLINE");
+        return rows;
     }
 
     /**
@@ -127,6 +132,11 @@
     @Override
     public int updateCollector(TCollector collector)
     {
+        TCollector current = collector == null || collector.getCollectorId() == null ? null
+                : collectorMapper.selectCollectorById(collector.getCollectorId());
+        if (current == null) throw new ServiceException("采集器不存在");
+        if (collector.getCollectorCode() != null && !collector.getCollectorCode().trim().equals(current.getCollectorCode()))
+            throw new ServiceException("设备编码不可修改");
         validateCollector(collector, true);
         validateStatus(collector.getStatus(), "状态");
         if (DISABLED.equals(collector.getStatus()))
@@ -354,6 +364,8 @@
     {
         CollectorCandidate candidate = candidates.get(collector.getCollectorCode());
         collector.setOnlineStatus(candidate != null && candidate.online() ? ONLINE : OFFLINE);
+        // 候选过期只改变当前在线展示,不能抹掉 MySQL 保存的最近观测时间。
+        if (candidate != null) collector.setLastSeenAtUtc(candidate.observedAt());
         return collector;
     }
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TDetectionCabinetServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TDetectionCabinetServiceImpl.java
index ee7e3df..680fcaf 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TDetectionCabinetServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TDetectionCabinetServiceImpl.java
@@ -164,11 +164,20 @@
                 value.setSubdevid(channel.identity().subdevid());
                 value.setChlid(channel.identity().chlid());
                 value.setDisplayNo(channel.identity().subdevid() + "-" + channel.identity().chlid());
-                value.setOnlineStatus("UNKNOWN");
-                value.setWorkStatus("UNKNOWN");
+                value.setOnlineStatus(channel.online() ? "ONLINE" : "OFFLINE");
+                value.setWorkStatus(persistedWorkStatus(channel.workStatus(), channel.online()));
                 value.setLastSeenAtUtc(null);
                 detectionCabinetMapper.insertDetectionChannel(value);
             }
+            // 登记瞬间的候选也是一次有效观测,立即写入正式事实,避免等待下一轮轮询才显示在线。
+            detectionCabinetMapper.updateRegisteredCabinetObservation(candidate.adapterCode(), candidate.devtype(),
+                    candidate.devid(), candidate.online() ? "ONLINE" : "OFFLINE", candidate.observedAt(),
+                    candidate.online());
+            candidate.channels().forEach(channel -> detectionCabinetMapper.updateRegisteredChannelObservation(
+                    candidate.adapterCode(), channel.identity().devtype(), channel.identity().devid(),
+                    channel.identity().subdevid(), channel.identity().chlid(), channel.online() ? "ONLINE" : "OFFLINE",
+                    channel.online() ? persistedWorkStatus(channel.workStatus(), true) : "UNOBSERVABLE",
+                    channel.observedAt(), channel.online()));
             return rows;
         }
         catch (DuplicateKeyException exception)
@@ -369,16 +378,24 @@
 
     private Map<String, CabinetCandidate> candidatesByCode()
     {
-        return candidateStore.snapshot(Instant.now()).cabinets().stream()
-                .collect(java.util.stream.Collectors.toMap(CabinetCandidate::deviceCode, item -> item));
+        return candidateStore.snapshot(Instant.now()).cabinets().stream().collect(java.util.stream.Collectors.toMap(
+                item -> stableCandidateKey(item.adapterCode(), item.devtype(), item.devid()), item -> item,
+                (left, right) -> right));
     }
 
     private TDetectionCabinet applyRealtimeState(TDetectionCabinet cabinet,
             Map<String, CabinetCandidate> candidates)
     {
-        CabinetCandidate candidate = candidates.get(cabinet.getCabinetCode());
+        CabinetCandidate candidate = candidates.get(stableCandidateKey(cabinet.getAdapterCode(),
+                cabinet.getVendorDeviceType(), cabinet.getVendorDeviceId()));
+        if (candidate == null) candidate = candidateStore.findCabinet(cabinet.getCabinetCode(), Instant.now()).orElse(null);
         cabinet.setOnlineStatus(candidate != null && candidate.online() ? ONLINE : OFFLINE);
         return cabinet;
+    }
+
+    private String stableCandidateKey(String adapterCode, Integer devtype, Integer devid)
+    {
+        return adapterCode + "-" + devtype + "-" + devid;
     }
 
     private boolean isActive(CabinetCandidate candidate)
@@ -386,6 +403,20 @@
         return candidate.channels().stream().anyMatch(channel -> ACTIVE_WORK_STATUSES.contains(channel.workStatus()));
     }
 
+    /** 将设备观测状态转换为最终数据库允许的通道事实状态。 */
+    private String persistedWorkStatus(DeviceWorkStatus status, boolean online)
+    {
+        if (!online || status == null || status == DeviceWorkStatus.UNKNOWN
+                || status == DeviceWorkStatus.UNOBSERVABLE) return "UNOBSERVABLE";
+        return switch (status)
+        {
+            case PAUSED -> "PAUSED";
+            case PROTECTED -> "ABNORMAL";
+            case FINISHED, STOPPED -> "ENDED";
+            default -> "RUNNING";
+        };
+    }
+
     private ServiceException buildDuplicateException(TDetectionCabinet detectionCabinet, DuplicateKeyException cause)
     {
         String message = checkCabinetCodeUnique(detectionCabinet) ? "MAC地址已存在" : "设备编码已存在";
diff --git a/ruoyi-system/src/main/resources/mapper/system/TCollectorMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TCollectorMapper.xml
index 70e19b1..fcf60f7 100644
--- a/ruoyi-system/src/main/resources/mapper/system/TCollectorMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/TCollectorMapper.xml
@@ -6,6 +6,7 @@
 
     <resultMap type="TCollector" id="TCollectorResult">
         <id property="collectorId" column="collector_id" />
+        <result property="adapterId" column="adapter_id" />
         <result property="collectorCode" column="collector_code" />
         <result property="ipAddress" column="ip_address" />
         <result property="macAddress" column="mac_address" />
@@ -16,7 +17,7 @@
         <result property="onlineStatus" column="online_status" />
         <result property="testStatus" column="test_status" />
         <result property="currentTest" column="current_test" />
-        <result property="currentSampleName" column="current_sample_name" />
+        <result property="lastSeenAtUtc" column="last_seen_at_utc" />
         <result property="status" column="status" />
         <result property="delFlag" column="del_flag" />
         <result property="createBy" column="create_by" />
@@ -27,11 +28,27 @@
     </resultMap>
 
     <sql id="selectCollectorVo">
-        select collector_id, collector_code, ip_address, mac_address, collector_name,
-               manufacturer, model, channel_count, online_status, test_status,
-               current_test, current_sample_name, status, del_flag, create_by,
-               create_time, update_by, update_time, remark
-        from t_collector
+        select c.collector_id, c.collector_code, c.adapter_id, c.ip_address, c.mac_address, c.collector_name,
+               c.manufacturer, c.model, c.channel_count,
+               case when upper(c.online_status) = 'ONLINE' or c.online_status = '1' then '1' else '0' end as online_status,
+               c.last_seen_at_utc,
+               case when exists (
+                   select 1 from t_test_collector_channel mapping
+                   join t_test test_fact on test_fact.test_id = mapping.test_id
+                   where mapping.collector_id = c.collector_id
+                     and test_fact.del_flag = 0
+                     and test_fact.test_status in ('RUNNING', 'PAUSED')
+               ) then '1' else '0' end as test_status,
+               case when exists (
+                   select 1 from t_test_collector_channel mapping
+                   join t_test test_fact on test_fact.test_id = mapping.test_id
+                   where mapping.collector_id = c.collector_id
+                     and test_fact.del_flag = 0
+                     and test_fact.test_status in ('RUNNING', 'PAUSED')
+               ) then '待确认' else null end as current_test,
+               c.status, c.del_flag, c.create_by,
+               c.create_time, c.update_by, c.update_time, c.remark
+        from t_collector c
     </sql>
 
     <select id="selectCollectorById" parameterType="Long" resultMap="TCollectorResult">
@@ -60,7 +77,13 @@
                 and online_status = #{onlineStatus}
             </if>
             <if test="testStatus != null and testStatus != ''">
-                and test_status = #{testStatus}
+                and (case when exists (
+                    select 1 from t_test_collector_channel mapping
+                    join t_test test_fact on test_fact.test_id = mapping.test_id
+                    where mapping.collector_id = c.collector_id
+                      and test_fact.del_flag = 0
+                      and test_fact.test_status in ('RUNNING', 'PAUSED')
+                ) then '1' else '0' end) = #{testStatus}
             </if>
             <if test="status != null and status != ''">
                 and status = #{status}
@@ -92,17 +115,19 @@
     <insert id="insertCollector" parameterType="TCollector" useGeneratedKeys="true" keyProperty="collectorId">
         insert into t_collector
         (collector_code, ip_address, mac_address, collector_name, manufacturer, model,
-         channel_count, online_status, test_status, current_test, current_sample_name,
+         channel_count, online_status, last_seen_at_utc,
          status, del_flag, create_by, create_time, remark)
         values
         (#{collectorCode}, #{ipAddress}, #{macAddress}, #{collectorName}, #{manufacturer}, #{model},
-         #{channelCount}, #{onlineStatus}, #{testStatus}, #{currentTest}, #{currentSampleName},
+         #{channelCount}, case when #{onlineStatus} in ('1', 'ONLINE') then 'ONLINE' else 'OFFLINE' end,
+         #{lastSeenAtUtc},
          #{status}, #{delFlag}, #{createBy}, #{createTime}, #{remark})
     </insert>
 
     <update id="updateRegisteredObservation">
         update t_collector
-        set online_status = #{onlineStatus}, update_time = sysdate()
+        set online_status = case when #{onlineStatus} in ('1', 'ONLINE') then 'ONLINE' else 'OFFLINE' end,
+            last_seen_at_utc = utc_timestamp(3), update_time = sysdate()
         where collector_code = #{collectorCode} and del_flag = 0
     </update>
 
diff --git a/ruoyi-system/src/main/resources/mapper/system/TDetectionCabinetMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TDetectionCabinetMapper.xml
index cea14c2..df326cf 100644
--- a/ruoyi-system/src/main/resources/mapper/system/TDetectionCabinetMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/TDetectionCabinetMapper.xml
@@ -123,7 +123,21 @@
 
     <update id="updateRegisteredChannelObservation">
         update t_detection_channel
-        set online_status = #{onlineStatus}, work_status = #{workStatus}, update_time = sysdate()
+        set online_status = case when #{onlineStatus} in ('ONLINE', '1') then 'ONLINE' else 'OFFLINE' end,
+            work_status = case #{workStatus}
+                when 'PAUSED' then 'PAUSED'
+                when 'PROTECTED' then 'ABNORMAL'
+                when 'FINISHED' then 'ENDED'
+                when 'STOPPED' then 'ENDED'
+                when 'WORKING' then 'RUNNING'
+                when 'SYNCHRONIZING' then 'RUNNING'
+                when 'LIT' then 'RUNNING'
+                when 'WAITING_TIMEOUT' then 'RUNNING'
+                when 'WAITING_START' then 'RUNNING'
+                when 'LOCKED' then 'RUNNING'
+                when 'RUNNING' then 'RUNNING'
+                else 'UNOBSERVABLE' end,
+            update_time = sysdate()
         <if test="updateLastSeen">, last_seen_at_utc = #{lastSeenAtUtc}</if>
         where adapter_code = #{adapterCode}
           and devtype = #{devtype} and devid = #{devid}
@@ -134,6 +148,35 @@
                         and cabinet.del_flag = 0 and cabinet.registration_status = 'REGISTERED')
     </update>
 
+    <update id="markRegisteredFactsUnobservable">
+        update t_detection_channel
+        set online_status = 'OFFLINE', work_status = 'UNOBSERVABLE', update_time = sysdate()
+        where adapter_code = #{adapterCode} and del_flag = 0
+    </update>
+
+    <update id="markRegisteredCabinetsOffline">
+        update t_detection_cabinet
+        set online_status = 'OFFLINE', update_time = sysdate()
+        where adapter_code = #{adapterCode} and del_flag = 0 and registration_status = 'REGISTERED'
+    </update>
+
+    <update id="touchRegisteredChannelObservation">
+        update t_detection_channel
+        set last_seen_at_utc = #{lastSeenAtUtc}, update_time = sysdate()
+        where adapter_code = #{adapterCode}
+          and devtype = #{devtype} and devid = #{devid}
+          and subdevid = #{subdevid} and chlid = #{chlid}
+          and online_status = 'ONLINE' and del_flag = 0
+    </update>
+
+    <update id="touchRegisteredCabinetObservation">
+        update t_detection_cabinet
+        set last_seen_at_utc = #{lastSeenAtUtc}, update_time = sysdate()
+        where adapter_code = #{adapterCode}
+          and vendor_device_type = #{devtype} and vendor_device_id = #{devid}
+          and online_status = 'ONLINE' and del_flag = 0 and registration_status = 'REGISTERED'
+    </update>
+
     <update id="updateDetectionCabinet" parameterType="TDetectionCabinet">
         update t_detection_cabinet
         <set>
diff --git a/ruoyi-system/src/main/resources/mapper/system/TDeviceAdapterMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TDeviceAdapterMapper.xml
index b092e59..3d29848 100644
--- a/ruoyi-system/src/main/resources/mapper/system/TDeviceAdapterMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/TDeviceAdapterMapper.xml
@@ -64,11 +64,11 @@
             timezone_id, capabilities, status, connection_status, create_by, create_time, update_by, update_time,
             remark, del_flag)
         values(#{adapterCode}, #{adapterName}, #{adapterType}, #{host}, #{port}, #{pollIntervalMs}, #{timezoneId},
-            #{capabilities}, '0', 'DISCONNECTED', 'system', sysdate(), 'system', sysdate(), #{remark}, 0)
+            #{capabilities}, '0', 'OFFLINE', 'system', sysdate(), 'system', sysdate(), #{remark}, 0)
         on duplicate key update adapter_name=values(adapter_name), adapter_type=values(adapter_type),
             host=values(host), port=values(port), poll_interval_ms=values(poll_interval_ms),
             timezone_id=values(timezone_id), capabilities=values(capabilities), status='0',
-            connection_status='DISCONNECTED', last_error_code=null, last_error_message=null,
+            connection_status='OFFLINE', last_error_code=null, last_error_message=null,
             update_by='system', update_time=sysdate(), remark=values(remark), del_flag=0
     </insert>
 
@@ -76,31 +76,31 @@
         insert into t_device_adapter(adapter_code, adapter_name, adapter_type, host, port, poll_interval_ms,
             timezone_id, capabilities, status, connection_status, create_by, create_time, remark, del_flag)
         values(#{adapterCode}, #{adapterName}, #{adapterType}, #{host}, #{port}, #{pollIntervalMs},
-            #{timezoneId}, #{capabilities}, #{status}, 'DISCONNECTED', #{createBy}, sysdate(), #{remark}, 0)
+            #{timezoneId}, #{capabilities}, #{status}, 'OFFLINE', #{createBy}, sysdate(), #{remark}, 0)
     </insert>
 
     <update id="updateDeviceAdapter" parameterType="TDeviceAdapter">
         update t_device_adapter set adapter_name=#{adapterName}, adapter_type=#{adapterType}, host=#{host}, port=#{port},
             poll_interval_ms=#{pollIntervalMs}, timezone_id=#{timezoneId}, capabilities=#{capabilities},
-            connection_status='DISCONNECTED', last_error_code=null, last_error_message=null,
+            connection_status='OFFLINE', last_error_code=null, last_error_message=null,
             update_by=#{updateBy}, update_time=sysdate()
         where adapter_id=#{adapterId} and del_flag=0
     </update>
 
     <update id="updateDeviceAdapterStatus">
-        update t_device_adapter set status=#{status}, connection_status='DISCONNECTED',
+        update t_device_adapter set status=#{status}, connection_status='OFFLINE',
             last_error_code=null, last_error_message=null, update_by=#{updateBy}, update_time=sysdate()
         where adapter_id=#{adapterId} and del_flag=0
     </update>
 
     <update id="updateDiscoverySuccess">
-        update t_device_adapter set connection_status='CONNECTED', last_error_code=null, last_error_message=null,
+        update t_device_adapter set connection_status='ONLINE', last_error_code=null, last_error_message=null,
             last_attempt_at_utc=#{observedAt}, last_observed_at_utc=#{observedAt}, update_time=sysdate()
         where adapter_id=#{adapterId} and del_flag=0
     </update>
 
     <update id="updateDiscoveryFailure">
-        update t_device_adapter set connection_status='ERROR', last_error_code=#{errorCode},
+        update t_device_adapter set connection_status='OFFLINE', last_error_code=#{errorCode},
             last_error_message=left(#{errorMessage}, 500), last_attempt_at_utc=utc_timestamp(3), update_time=sysdate()
         where adapter_id=#{adapterId} and del_flag=0
     </update>

--
Gitblit v1.7.1