From 47c6b2ca67688336698aedbecf74c91f1ed37fb3 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Thu, 25 Sep 2025 14:18:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(device):=20=E6=96=B0=E5=A2=9E=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86=E9=9B=86=E6=88=90?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加设备状态客户端接口,支持设备在线状态查询与设置 - 创建设备状态相关 DTO,包括设备状态、在线状态和状态动作枚举 - 实现设备状态集成服务,封装设备状态操作与异常处理逻辑 - 支持单个及批量设备在线状态检查与设置功能 - 提供 --- .../device/client/DeviceStatusClient.java | 51 +++++ .../dto/status/DeviceStatusActionEnum.java | 41 ++++ .../device/dto/status/DeviceStatusDTO.java | 49 ++++ .../dto/status/OnlineStatusResponseDTO.java | 24 ++ .../DeviceStatusIntegrationService.java | 211 ++++++++++++++++++ 5 files changed, 376 insertions(+) create mode 100644 src/main/java/com/ycwl/basic/integration/device/client/DeviceStatusClient.java create mode 100644 src/main/java/com/ycwl/basic/integration/device/dto/status/DeviceStatusActionEnum.java create mode 100644 src/main/java/com/ycwl/basic/integration/device/dto/status/DeviceStatusDTO.java create mode 100644 src/main/java/com/ycwl/basic/integration/device/dto/status/OnlineStatusResponseDTO.java create mode 100644 src/main/java/com/ycwl/basic/integration/device/service/DeviceStatusIntegrationService.java diff --git a/src/main/java/com/ycwl/basic/integration/device/client/DeviceStatusClient.java b/src/main/java/com/ycwl/basic/integration/device/client/DeviceStatusClient.java new file mode 100644 index 00000000..48c5bf64 --- /dev/null +++ b/src/main/java/com/ycwl/basic/integration/device/client/DeviceStatusClient.java @@ -0,0 +1,51 @@ +package com.ycwl.basic.integration.device.client; + +import com.ycwl.basic.integration.common.response.CommonResponse; +import com.ycwl.basic.integration.device.dto.status.DeviceStatusDTO; +import com.ycwl.basic.integration.device.dto.status.OnlineStatusResponseDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + +import java.util.List; + +@FeignClient(name = "zt-device", contextId = "deviceStatusClient", path = "/api/device/status") +public interface DeviceStatusClient { + + /** + * 获取设备状态 + */ + @GetMapping("/{deviceNo}") + CommonResponse getDeviceStatus(@PathVariable("deviceNo") String deviceNo); + + /** + * 检查设备是否在线 + */ + @GetMapping("/{deviceNo}/online") + CommonResponse isDeviceOnline(@PathVariable("deviceNo") String deviceNo); + + /** + * 获取所有在线设备 + */ + @GetMapping("/online") + CommonResponse> getAllOnlineDevices(); + + /** + * 设置设备离线 + */ + @PostMapping("/{deviceNo}/offline") + CommonResponse setDeviceOffline(@PathVariable("deviceNo") String deviceNo); + + /** + * 设置设备在线 + */ + @PostMapping("/{deviceNo}/online") + CommonResponse setDeviceOnline(@PathVariable("deviceNo") String deviceNo); + + /** + * 清理过期设备状态 + */ + @PostMapping("/clean") + CommonResponse cleanExpiredDevices(); +} \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/integration/device/dto/status/DeviceStatusActionEnum.java b/src/main/java/com/ycwl/basic/integration/device/dto/status/DeviceStatusActionEnum.java new file mode 100644 index 00000000..fd347104 --- /dev/null +++ b/src/main/java/com/ycwl/basic/integration/device/dto/status/DeviceStatusActionEnum.java @@ -0,0 +1,41 @@ +package com.ycwl.basic.integration.device.dto.status; + +/** + * 设备状态动作枚举 + */ +public enum DeviceStatusActionEnum { + + /** + * 设备注册 + */ + REGISTER("register"), + + /** + * 设备保活 + */ + KEEPALIVE("keepalive"), + + /** + * 设备注销 + */ + UNREGISTER("unregister"); + + private final String action; + + DeviceStatusActionEnum(String action) { + this.action = action; + } + + public String getAction() { + return action; + } + + public static DeviceStatusActionEnum fromString(String action) { + for (DeviceStatusActionEnum statusAction : values()) { + if (statusAction.action.equalsIgnoreCase(action)) { + return statusAction; + } + } + throw new IllegalArgumentException("Unknown device status action: " + action); + } +} \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/integration/device/dto/status/DeviceStatusDTO.java b/src/main/java/com/ycwl/basic/integration/device/dto/status/DeviceStatusDTO.java new file mode 100644 index 00000000..26807ab9 --- /dev/null +++ b/src/main/java/com/ycwl/basic/integration/device/dto/status/DeviceStatusDTO.java @@ -0,0 +1,49 @@ +package com.ycwl.basic.integration.device.dto.status; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * 设备状态信息 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DeviceStatusDTO { + + /** + * 设备编号 + */ + private String deviceNo; + + /** + * 是否在线 + */ + private Boolean isOnline; + + /** + * 最后活动时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'") + private LocalDateTime lastActiveTime; + + /** + * 最后动作(register/keepalive/unregister) + */ + private String lastAction; + + /** + * 客户端IP + */ + private String clientIP; + + /** + * 状态更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'") + private LocalDateTime updateTime; +} \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/integration/device/dto/status/OnlineStatusResponseDTO.java b/src/main/java/com/ycwl/basic/integration/device/dto/status/OnlineStatusResponseDTO.java new file mode 100644 index 00000000..8d42363f --- /dev/null +++ b/src/main/java/com/ycwl/basic/integration/device/dto/status/OnlineStatusResponseDTO.java @@ -0,0 +1,24 @@ +package com.ycwl.basic.integration.device.dto.status; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 在线状态响应 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OnlineStatusResponseDTO { + + /** + * 设备编号 + */ + private String deviceNo; + + /** + * 是否在线 + */ + private Boolean isOnline; +} \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/integration/device/service/DeviceStatusIntegrationService.java b/src/main/java/com/ycwl/basic/integration/device/service/DeviceStatusIntegrationService.java new file mode 100644 index 00000000..01d4ca8c --- /dev/null +++ b/src/main/java/com/ycwl/basic/integration/device/service/DeviceStatusIntegrationService.java @@ -0,0 +1,211 @@ +package com.ycwl.basic.integration.device.service; + +import com.ycwl.basic.integration.common.exception.IntegrationException; +import com.ycwl.basic.integration.common.response.CommonResponse; +import com.ycwl.basic.integration.common.service.IntegrationFallbackService; +import com.ycwl.basic.integration.device.client.DeviceStatusClient; +import com.ycwl.basic.integration.device.dto.status.DeviceStatusDTO; +import com.ycwl.basic.integration.device.dto.status.OnlineStatusResponseDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DeviceStatusIntegrationService { + + private final DeviceStatusClient deviceStatusClient; + private final IntegrationFallbackService fallbackService; + + private static final String SERVICE_NAME = "zt-device"; + + public DeviceStatusDTO getDeviceStatus(String deviceNo) { + log.debug("获取设备状态, deviceNo: {}", deviceNo); + return fallbackService.executeWithFallback( + SERVICE_NAME, + "device:status:" + deviceNo, + () -> { + CommonResponse response = deviceStatusClient.getDeviceStatus(deviceNo); + return handleResponse(response, "获取设备状态失败"); + }, + DeviceStatusDTO.class + ); + } + + public OnlineStatusResponseDTO isDeviceOnline(String deviceNo) { + log.debug("检查设备是否在线, deviceNo: {}", deviceNo); + return fallbackService.executeWithFallback( + SERVICE_NAME, + "device:online:" + deviceNo, + () -> { + CommonResponse response = deviceStatusClient.isDeviceOnline(deviceNo); + return handleResponse(response, "检查设备在线状态失败"); + }, + OnlineStatusResponseDTO.class + ); + } + + public List getAllOnlineDevices() { + log.debug("获取所有在线设备"); + return fallbackService.executeWithFallback( + SERVICE_NAME, + "devices:online:all", + () -> { + CommonResponse> response = deviceStatusClient.getAllOnlineDevices(); + return handleResponse(response, "获取所有在线设备失败"); + }, + List.class + ); + } + + public void setDeviceOffline(String deviceNo) { + log.debug("设置设备离线, deviceNo: {}", deviceNo); + CommonResponse response = deviceStatusClient.setDeviceOffline(deviceNo); + handleResponse(response, "设置设备离线失败"); + } + + public void setDeviceOnline(String deviceNo) { + log.debug("设置设备在线, deviceNo: {}", deviceNo); + CommonResponse response = deviceStatusClient.setDeviceOnline(deviceNo); + handleResponse(response, "设置设备在线失败"); + } + + public void cleanExpiredDevices() { + log.debug("清理过期设备状态"); + CommonResponse response = deviceStatusClient.cleanExpiredDevices(); + handleResponse(response, "清理过期设备状态失败"); + } + + /** + * 安全地获取设备状态 + */ + public Optional getDeviceStatusSafely(String deviceNo) { + try { + DeviceStatusDTO status = getDeviceStatus(deviceNo); + return Optional.ofNullable(status); + } catch (Exception e) { + log.warn("获取设备状态异常: deviceNo={}, error={}", deviceNo, e.getMessage()); + return Optional.empty(); + } + } + + /** + * 安全地检查设备是否在线 + */ + public boolean isDeviceOnlineSafely(String deviceNo) { + try { + OnlineStatusResponseDTO response = isDeviceOnline(deviceNo); + return response != null && Boolean.TRUE.equals(response.getIsOnline()); + } catch (Exception e) { + log.warn("检查设备在线状态异常: deviceNo={}, error={}", deviceNo, e.getMessage()); + return false; + } + } + + /** + * 批量检查设备是否在线 + */ + public boolean areAllDevicesOnline(List deviceNos) { + if (deviceNos == null || deviceNos.isEmpty()) { + return true; + } + + log.debug("批量检查设备在线状态, deviceNos: {}", deviceNos); + for (String deviceNo : deviceNos) { + if (!isDeviceOnlineSafely(deviceNo)) { + return false; + } + } + return true; + } + + /** + * 批量设置设备离线 + */ + public void setDevicesOffline(List deviceNos) { + if (deviceNos == null || deviceNos.isEmpty()) { + return; + } + + log.debug("批量设置设备离线, deviceNos: {}", deviceNos); + for (String deviceNo : deviceNos) { + try { + setDeviceOffline(deviceNo); + } catch (Exception e) { + log.error("设置设备离线失败: deviceNo={}, error={}", deviceNo, e.getMessage()); + } + } + } + + /** + * 批量设置设备在线 + */ + public void setDevicesOnline(List deviceNos) { + if (deviceNos == null || deviceNos.isEmpty()) { + return; + } + + log.debug("批量设置设备在线, deviceNos: {}", deviceNos); + for (String deviceNo : deviceNos) { + try { + setDeviceOnline(deviceNo); + } catch (Exception e) { + log.error("设置设备在线失败: deviceNo={}, error={}", deviceNo, e.getMessage()); + } + } + } + + /** + * 获取在线设备数量 + */ + public int getOnlineDeviceCount() { + try { + List onlineDevices = getAllOnlineDevices(); + return onlineDevices != null ? onlineDevices.size() : 0; + } catch (Exception e) { + log.warn("获取在线设备数量失败: {}", e.getMessage()); + return 0; + } + } + + /** + * 获取指定设备编号列表中的在线设备 + */ + public List getOnlineDeviceNos(List deviceNos) { + if (deviceNos == null || deviceNos.isEmpty()) { + return List.of(); + } + + return deviceNos.stream() + .filter(this::isDeviceOnlineSafely) + .toList(); + } + + /** + * 获取指定设备编号列表中的离线设备 + */ + public List getOfflineDeviceNos(List deviceNos) { + if (deviceNos == null || deviceNos.isEmpty()) { + return List.of(); + } + + return deviceNos.stream() + .filter(deviceNo -> !isDeviceOnlineSafely(deviceNo)) + .toList(); + } + + private T handleResponse(CommonResponse response, String errorMessage) { + if (response == null || !response.isSuccess()) { + String msg = response != null && response.getMessage() != null + ? response.getMessage() + : errorMessage; + Integer code = response != null ? response.getCode() : 5000; + throw new IntegrationException(code, msg, SERVICE_NAME); + } + return response.getData(); + } +} \ No newline at end of file