From 81203488c85adaf15ebe416ff8ab6543d40b7f40 Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 5 Jan 2026 22:02:15 +0800 Subject: [PATCH] 1 --- .../controller/monitor/ServerController.java | 60 +++++++ .../jarvis/service/ILogisticsService.java | 55 ++++++ .../ruoyi/jarvis/service/IWxSendService.java | 62 +++++++ .../service/impl/LogisticsServiceImpl.java | 50 +++--- .../service/impl/WxSendServiceImpl.java | 169 ++++++++++++++++++ 5 files changed, 375 insertions(+), 21 deletions(-) create mode 100644 ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IWxSendService.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/WxSendServiceImpl.java diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java index cc805ad..49911cd 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java @@ -6,6 +6,12 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.framework.web.domain.Server; +import com.ruoyi.jarvis.service.ILogisticsService; +import com.ruoyi.jarvis.service.IWxSendService; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Map; /** * 服务器监控 @@ -16,6 +22,12 @@ import com.ruoyi.framework.web.domain.Server; @RequestMapping("/monitor/server") public class ServerController { + @Resource + private ILogisticsService logisticsService; + + @Resource + private IWxSendService wxSendService; + @PreAuthorize("@ss.hasPermi('monitor:server:list')") @GetMapping() public AjaxResult getInfo() throws Exception @@ -24,4 +36,52 @@ public class ServerController server.copyTo(); return AjaxResult.success(server); } + + /** + * 获取服务健康度检测 + */ + @PreAuthorize("@ss.hasPermi('monitor:server:list')") + @GetMapping("/health") + public AjaxResult getHealth() throws Exception + { + Map healthMap = new HashMap<>(); + + // 物流服务健康检测 + try { + ILogisticsService.HealthCheckResult logisticsHealth = logisticsService.checkHealth(); + Map logisticsMap = new HashMap<>(); + logisticsMap.put("healthy", logisticsHealth.isHealthy()); + logisticsMap.put("status", logisticsHealth.getStatus()); + logisticsMap.put("message", logisticsHealth.getMessage()); + logisticsMap.put("serviceUrl", logisticsHealth.getServiceUrl()); + healthMap.put("logistics", logisticsMap); + } catch (Exception e) { + Map logisticsMap = new HashMap<>(); + logisticsMap.put("healthy", false); + logisticsMap.put("status", "异常"); + logisticsMap.put("message", "健康检测异常: " + e.getMessage()); + logisticsMap.put("serviceUrl", ""); + healthMap.put("logistics", logisticsMap); + } + + // 微信推送服务健康检测 + try { + IWxSendService.HealthCheckResult wxSendHealth = wxSendService.checkHealth(); + Map wxSendMap = new HashMap<>(); + wxSendMap.put("healthy", wxSendHealth.isHealthy()); + wxSendMap.put("status", wxSendHealth.getStatus()); + wxSendMap.put("message", wxSendHealth.getMessage()); + wxSendMap.put("serviceUrl", wxSendHealth.getServiceUrl()); + healthMap.put("wxSend", wxSendMap); + } catch (Exception e) { + Map wxSendMap = new HashMap<>(); + wxSendMap.put("healthy", false); + wxSendMap.put("status", "异常"); + wxSendMap.put("message", "健康检测异常: " + e.getMessage()); + wxSendMap.put("serviceUrl", ""); + healthMap.put("wxSend", wxSendMap); + } + + return AjaxResult.success(healthMap); + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/ILogisticsService.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/ILogisticsService.java index 3857239..0e46bc9 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/ILogisticsService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/ILogisticsService.java @@ -19,5 +19,60 @@ public interface ILogisticsService { * @return 如果已处理返回true,否则返回false */ boolean isOrderProcessed(Long orderId); + + /** + * 检查物流服务健康状态 + * @return 健康状态信息,包含是否健康、状态描述等 + */ + HealthCheckResult checkHealth(); + + /** + * 健康检测结果 + */ + class HealthCheckResult { + private boolean healthy; + private String status; + private String message; + private String serviceUrl; + + public HealthCheckResult(boolean healthy, String status, String message, String serviceUrl) { + this.healthy = healthy; + this.status = status; + this.message = message; + this.serviceUrl = serviceUrl; + } + + public boolean isHealthy() { + return healthy; + } + + public void setHealthy(boolean healthy) { + this.healthy = healthy; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getServiceUrl() { + return serviceUrl; + } + + public void setServiceUrl(String serviceUrl) { + this.serviceUrl = serviceUrl; + } + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IWxSendService.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IWxSendService.java new file mode 100644 index 0000000..0c82eb1 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IWxSendService.java @@ -0,0 +1,62 @@ +package com.ruoyi.jarvis.service; + +/** + * 微信推送服务接口 + */ +public interface IWxSendService { + /** + * 检查微信推送服务健康状态 + * @return 健康状态信息,包含是否健康、状态描述等 + */ + HealthCheckResult checkHealth(); + + /** + * 健康检测结果 + */ + class HealthCheckResult { + private boolean healthy; + private String status; + private String message; + private String serviceUrl; + + public HealthCheckResult(boolean healthy, String status, String message, String serviceUrl) { + this.healthy = healthy; + this.status = status; + this.message = message; + this.serviceUrl = serviceUrl; + } + + public boolean isHealthy() { + return healthy; + } + + public void setHealthy(boolean healthy) { + this.healthy = healthy; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getServiceUrl() { + return serviceUrl; + } + + public void setServiceUrl(String serviceUrl) { + this.serviceUrl = serviceUrl; + } + } +} + diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/LogisticsServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/LogisticsServiceImpl.java index 1709952..3306baf 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/LogisticsServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/LogisticsServiceImpl.java @@ -71,19 +71,15 @@ public class LogisticsServiceImpl implements ILogisticsService { return stringRedisTemplate.hasKey(redisKey); } - /** - * 检查物流服务健康状态 - * @return 是否健康 - */ - private boolean checkLogisticsServiceHealth() { + @Override + public ILogisticsService.HealthCheckResult checkHealth() { try { logger.debug("开始检查物流服务健康状态 - URL: {}", healthCheckUrl); String healthResult = HttpUtils.sendGet(healthCheckUrl); if (healthResult == null || healthResult.trim().isEmpty()) { logger.warn("物流服务健康检查返回空结果"); - handleHealthCheckFailure("健康检查返回空结果"); - return false; + return new ILogisticsService.HealthCheckResult(false, "异常", "健康检查返回空结果", healthCheckUrl); } // 尝试解析JSON响应 @@ -98,9 +94,7 @@ public class LogisticsServiceImpl implements ILogisticsService { if ("ok".equalsIgnoreCase(status) || "healthy".equalsIgnoreCase(status) || Boolean.TRUE.equals(healthy) || (code != null && code == 200)) { logger.debug("物流服务健康检查通过"); - // 清除健康检查失败标记 - stringRedisTemplate.delete(REDIS_HEALTH_CHECK_ALERT_KEY); - return true; + return new ILogisticsService.HealthCheckResult(true, "正常", "服务运行正常", healthCheckUrl); } } } catch (Exception e) { @@ -108,26 +102,40 @@ public class LogisticsServiceImpl implements ILogisticsService { String lowerResult = healthResult.toLowerCase(); if (lowerResult.contains("ok") || lowerResult.contains("healthy") || lowerResult.contains("success")) { logger.debug("物流服务健康检查通过(非JSON格式)"); - stringRedisTemplate.delete(REDIS_HEALTH_CHECK_ALERT_KEY); - return true; + return new ILogisticsService.HealthCheckResult(true, "正常", "服务运行正常", healthCheckUrl); } } logger.warn("物流服务健康检查失败 - 响应: {}", healthResult); - handleHealthCheckFailure("健康检查返回异常状态: " + healthResult); - return false; + return new ILogisticsService.HealthCheckResult(false, "异常", "健康检查返回异常状态: " + healthResult, healthCheckUrl); } catch (Exception e) { logger.error("物流服务健康检查异常 - URL: {}, 错误: {}", healthCheckUrl, e.getMessage(), e); - handleHealthCheckFailure("健康检查异常: " + e.getMessage()); - return false; + return new ILogisticsService.HealthCheckResult(false, "异常", "健康检查异常: " + e.getMessage(), healthCheckUrl); } } + /** + * 检查物流服务健康状态(内部方法,用于业务逻辑) + * @return 是否健康 + */ + @SuppressWarnings("unused") + private boolean checkLogisticsServiceHealth() { + ILogisticsService.HealthCheckResult result = checkHealth(); + if (result.isHealthy()) { + // 清除健康检查失败标记 + stringRedisTemplate.delete(REDIS_HEALTH_CHECK_ALERT_KEY); + } else { + handleHealthCheckFailure(result.getMessage()); + } + return result.isHealthy(); + } + /** * 处理健康检查失败,推送提醒消息 * @param reason 失败原因 */ + @SuppressWarnings("null") private void handleHealthCheckFailure(String reason) { try { // 检查是否在提醒间隔内已经推送过 @@ -167,8 +175,8 @@ public class LogisticsServiceImpl implements ILogisticsService { if (pushResult != null && !pushResult.trim().isEmpty()) { logger.info("健康检查失败提醒已推送 - 响应: {}", pushResult); // 记录推送时间,30分钟内不再重复推送 - Long currentTime = System.currentTimeMillis(); - stringRedisTemplate.opsForValue().set(alertKey, currentTime.toString(), + long currentTime = System.currentTimeMillis(); + stringRedisTemplate.opsForValue().set(alertKey, Long.toString(currentTime), HEALTH_CHECK_ALERT_INTERVAL_MINUTES, TimeUnit.MINUTES); } else { logger.warn("健康检查失败提醒推送失败 - 响应为空"); @@ -218,9 +226,9 @@ public class LogisticsServiceImpl implements ILogisticsService { } // 先进行健康检查 - boolean healthCheckPassed = checkLogisticsServiceHealth(); - if (!healthCheckPassed) { - logger.error("物流服务健康检查失败,跳过处理订单 - 订单ID: {}", orderId); + ILogisticsService.HealthCheckResult healthResult = checkHealth(); + if (!healthResult.isHealthy()) { + logger.error("物流服务健康检查失败,跳过处理订单 - 订单ID: {}, 原因: {}", orderId, healthResult.getMessage()); return false; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/WxSendServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/WxSendServiceImpl.java new file mode 100644 index 0000000..33bf2e0 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/WxSendServiceImpl.java @@ -0,0 +1,169 @@ +package com.ruoyi.jarvis.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.jarvis.service.IWxSendService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; + +/** + * 微信推送服务实现类 + */ +@Service +public class WxSendServiceImpl implements IWxSendService { + private static final Logger logger = LoggerFactory.getLogger(WxSendServiceImpl.class); + + private static final String PUSH_TOKEN = "super_token_b62190c26"; + + @Value("${jarvis.server.wxsend.base-url:https://wxts.van333.cn}") + private String wxSendBaseUrl; + + @Value("${jarvis.server.wxsend.health-path:/wx/send/pdd}") + private String wxSendHealthPath; + + private String healthCheckUrl; + + @PostConstruct + public void init() { + healthCheckUrl = wxSendBaseUrl + wxSendHealthPath; + logger.info("微信推送服务健康检查地址已初始化: {}", healthCheckUrl); + } + + @Override + public IWxSendService.HealthCheckResult checkHealth() { + try { + logger.debug("开始检查微信推送服务健康状态 - URL: {}", healthCheckUrl); + + // 构建一个测试请求(使用最小的有效请求体) + JSONObject testRequest = new JSONObject(); + testRequest.put("title", "健康检查"); + testRequest.put("text", "健康检查测试消息"); + testRequest.put("vanToken", PUSH_TOKEN); + testRequest.put("messageType", "PDD"); + + String jsonBody = testRequest.toJSONString(); + + // 发送POST请求进行健康检查 + String healthResult = sendPostWithHeaders(healthCheckUrl, jsonBody, PUSH_TOKEN); + + if (healthResult == null || healthResult.trim().isEmpty()) { + logger.warn("微信推送服务健康检查返回空结果"); + return new IWxSendService.HealthCheckResult(false, "异常", "健康检查返回空结果", healthCheckUrl); + } + + // 尝试解析JSON响应 + try { + JSONObject response = JSON.parseObject(healthResult); + if (response != null) { + Integer code = response.getInteger("code"); + Boolean success = response.getBoolean("success"); + String msg = response.getString("msg"); + + // 检查是否成功(code为200或0,或者success为true) + if ((code != null && (code == 200 || code == 0)) || Boolean.TRUE.equals(success)) { + logger.debug("微信推送服务健康检查通过"); + return new IWxSendService.HealthCheckResult(true, "正常", "服务运行正常", healthCheckUrl); + } else { + // 检查是否是token验证失败(说明服务可用,只是token问题) + if (msg != null && msg.contains("vanToken")) { + logger.debug("微信推送服务健康检查通过(服务可用,token验证失败是正常的)"); + return new IWxSendService.HealthCheckResult(true, "正常", "服务运行正常(token验证失败是正常的)", healthCheckUrl); + } + logger.warn("微信推送服务健康检查失败 - 响应: {}", healthResult); + return new IWxSendService.HealthCheckResult(false, "异常", "健康检查返回异常状态: " + msg, healthCheckUrl); + } + } + } catch (Exception e) { + // 如果不是JSON格式,检查是否包含成功标识 + String lowerResult = healthResult.toLowerCase(); + if (lowerResult.contains("ok") || lowerResult.contains("success") || lowerResult.contains("正常")) { + logger.debug("微信推送服务健康检查通过(非JSON格式)"); + return new IWxSendService.HealthCheckResult(true, "正常", "服务运行正常", healthCheckUrl); + } + } + + logger.warn("微信推送服务健康检查失败 - 响应: {}", healthResult); + return new IWxSendService.HealthCheckResult(false, "异常", "健康检查返回异常状态: " + healthResult, healthCheckUrl); + + } catch (Exception e) { + logger.error("微信推送服务健康检查异常 - URL: {}, 错误: {}", healthCheckUrl, e.getMessage(), e); + return new IWxSendService.HealthCheckResult(false, "异常", "健康检查异常: " + e.getMessage(), healthCheckUrl); + } + } + + /** + * 发送POST请求,支持自定义header(用于健康检查) + * @param url 请求URL + * @param jsonBody JSON请求体 + * @param token 认证token + * @return 响应结果 + */ + private String sendPostWithHeaders(String url, String jsonBody, String token) { + java.io.BufferedReader in = null; + java.io.PrintWriter out = null; + StringBuilder result = new StringBuilder(); + try { + java.net.URL realUrl = new java.net.URL(url); + java.net.URLConnection conn = realUrl.openConnection(); + + // 设置请求头 + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setRequestProperty("vanToken", token); + conn.setRequestProperty("source", "XZJ_UBUNTU"); + + conn.setDoOutput(true); + conn.setDoInput(true); + + // 设置超时时间(健康检查需要快速响应) + conn.setConnectTimeout(5000); // 5秒连接超时 + conn.setReadTimeout(5000); // 5秒读取超时 + + // 发送请求体 + out = new java.io.PrintWriter(conn.getOutputStream()); + out.print(jsonBody); + out.flush(); + + // 读取响应 + in = new java.io.BufferedReader(new java.io.InputStreamReader(conn.getInputStream(), java.nio.charset.StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + + logger.debug("微信推送服务健康检查请求成功 - URL: {}, 响应: {}", url, result.toString()); + } catch (java.net.ConnectException e) { + logger.error("微信推送服务健康检查连接失败 - URL: {}", url, e); + throw new RuntimeException("健康检查连接失败: " + e.getMessage(), e); + } catch (java.net.SocketTimeoutException e) { + logger.error("微信推送服务健康检查超时 - URL: {}", url, e); + throw new RuntimeException("健康检查请求超时: " + e.getMessage(), e); + } catch (java.io.IOException e) { + logger.error("微信推送服务健康检查IO异常 - URL: {}", url, e); + throw new RuntimeException("健康检查IO异常: " + e.getMessage(), e); + } catch (Exception e) { + logger.error("微信推送服务健康检查异常 - URL: {}", url, e); + throw new RuntimeException("健康检查异常: " + e.getMessage(), e); + } finally { + try { + if (out != null) { + out.close(); + } + if (in != null) { + in.close(); + } + } catch (java.io.IOException ex) { + logger.error("关闭流异常", ex); + } + } + return result.toString(); + } +} +