This commit is contained in:
Leo
2025-12-23 15:59:05 +08:00
parent 40dd64482c
commit d98711f06a
3 changed files with 126 additions and 0 deletions

View File

@@ -29,10 +29,12 @@ public class LogisticsServiceImpl implements ILogisticsService {
private static final String REDIS_WAYBILL_KEY_PREFIX = "logistics:waybill:order:";
private static final String REDIS_LOCK_KEY_PREFIX = "logistics:lock:order:";
private static final String REDIS_HEALTH_CHECK_ALERT_KEY = "logistics:health:alert:";
private static final String PUSH_URL = "https://wxts.van333.cn/wx/send/pdd";
private static final String PUSH_TOKEN = "super_token_b62190c26";
private static final String CONFIG_KEY_PREFIX = "logistics.push.touser.";
private static final long LOCK_EXPIRE_SECONDS = 300; // 锁过期时间5分钟防止死锁
private static final long HEALTH_CHECK_ALERT_INTERVAL_MINUTES = 30; // 健康检查失败提醒间隔30分钟避免频繁推送
@Value("${jarvis.server.logistics.base-url:http://127.0.0.1:5001}")
private String logisticsBaseUrl;
@@ -40,7 +42,11 @@ public class LogisticsServiceImpl implements ILogisticsService {
@Value("${jarvis.server.logistics.fetch-path:/fetch_logistics}")
private String logisticsFetchPath;
@Value("${jarvis.server.logistics.health-path:/health}")
private String logisticsHealthPath;
private String externalApiUrlTemplate;
private String healthCheckUrl;
@Resource
private StringRedisTemplate stringRedisTemplate;
@@ -51,7 +57,9 @@ public class LogisticsServiceImpl implements ILogisticsService {
@PostConstruct
public void init() {
externalApiUrlTemplate = logisticsBaseUrl + logisticsFetchPath + "?tracking_url=";
healthCheckUrl = logisticsBaseUrl + logisticsHealthPath;
logger.info("物流服务地址已初始化: {}", externalApiUrlTemplate);
logger.info("物流服务健康检查地址已初始化: {}", healthCheckUrl);
}
@Override
@@ -63,6 +71,114 @@ public class LogisticsServiceImpl implements ILogisticsService {
return stringRedisTemplate.hasKey(redisKey);
}
/**
* 检查物流服务健康状态
* @return 是否健康
*/
private boolean checkLogisticsServiceHealth() {
try {
logger.debug("开始检查物流服务健康状态 - URL: {}", healthCheckUrl);
String healthResult = HttpUtils.sendGet(healthCheckUrl);
if (healthResult == null || healthResult.trim().isEmpty()) {
logger.warn("物流服务健康检查返回空结果");
handleHealthCheckFailure("健康检查返回空结果");
return false;
}
// 尝试解析JSON响应
try {
JSONObject response = JSON.parseObject(healthResult);
if (response != null) {
// 检查常见的健康状态字段
String status = response.getString("status");
Boolean healthy = response.getBoolean("healthy");
Integer code = response.getInteger("code");
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;
}
}
} catch (Exception e) {
// 如果不是JSON格式检查是否包含成功标识
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;
}
}
logger.warn("物流服务健康检查失败 - 响应: {}", healthResult);
handleHealthCheckFailure("健康检查返回异常状态: " + healthResult);
return false;
} catch (Exception e) {
logger.error("物流服务健康检查异常 - URL: {}, 错误: {}", healthCheckUrl, e.getMessage(), e);
handleHealthCheckFailure("健康检查异常: " + e.getMessage());
return false;
}
}
/**
* 处理健康检查失败,推送提醒消息
* @param reason 失败原因
*/
private void handleHealthCheckFailure(String reason) {
try {
// 检查是否在提醒间隔内已经推送过
String alertKey = REDIS_HEALTH_CHECK_ALERT_KEY;
String lastAlertTime = stringRedisTemplate.opsForValue().get(alertKey);
if (lastAlertTime != null) {
// 如果30分钟内已经推送过不再重复推送
logger.debug("健康检查失败提醒已在30分钟内推送过跳过本次推送");
return;
}
// 构建推送消息
StringBuilder pushContent = new StringBuilder();
pushContent.append("【物流服务异常提醒】\n");
pushContent.append("服务地址:").append(healthCheckUrl).append("\n");
pushContent.append("失败原因:").append(reason).append("\n");
pushContent.append("时间:").append(new Date()).append("\n");
pushContent.append("请及时检查服务状态!");
JSONObject pushParam = new JSONObject();
pushParam.put("title", "物流服务健康检查失败");
pushParam.put("text", pushContent.toString());
// 尝试获取系统管理员接收人配置,如果没有则使用默认接收人
String adminTouser = sysConfigService.selectConfigByKey("logistics.push.touser.ADMIN");
if (StringUtils.hasText(adminTouser)) {
pushParam.put("touser", adminTouser.trim());
logger.info("使用管理员接收人配置推送健康检查失败提醒");
} else {
logger.info("未配置管理员接收人,将使用远程接口默认接收人推送健康检查失败提醒");
}
String jsonBody = pushParam.toJSONString();
String pushResult = sendPostWithHeaders(PUSH_URL, jsonBody, PUSH_TOKEN);
if (pushResult != null && !pushResult.trim().isEmpty()) {
logger.info("健康检查失败提醒已推送 - 响应: {}", pushResult);
// 记录推送时间30分钟内不再重复推送
Long currentTime = System.currentTimeMillis();
stringRedisTemplate.opsForValue().set(alertKey, currentTime.toString(),
HEALTH_CHECK_ALERT_INTERVAL_MINUTES, TimeUnit.MINUTES);
} else {
logger.warn("健康检查失败提醒推送失败 - 响应为空");
}
} catch (Exception e) {
logger.error("推送健康检查失败提醒异常", e);
}
}
@Override
public boolean fetchLogisticsAndPush(JDOrder order) {
if (order == null || order.getId() == null) {
@@ -100,6 +216,14 @@ public class LogisticsServiceImpl implements ILogisticsService {
logger.info("订单暂无物流链接,跳过处理 - 订单ID: {}", orderId);
return false;
}
// 先进行健康检查
boolean healthCheckPassed = checkLogisticsServiceHealth();
if (!healthCheckPassed) {
logger.error("物流服务健康检查失败,跳过处理订单 - 订单ID: {}", orderId);
return false;
}
// 构建外部接口URL
String externalUrl = externalApiUrlTemplate + URLEncoder.encode(logisticsLink, "UTF-8");
logger.info("调用外部接口获取物流信息 - 订单ID: {}, URL: {}", orderId, externalUrl);