yeg
2026-09-05 bc9a169ce3648467136a279d167f72c4fbab9595
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地址已存在" : "设备编码已存在";