Files
java-template/utils/MessageLifecycleService.java
T
BrunoGaoandClaude e402548e45 fix: 修复customer_id字段缺失问题,完成数据库schema同步
数据库修复:
- t_user_health_data_daily: 添加customer_id字段(租户ID)
- t_user_health_data_weekly: 添加customer_id字段(租户ID)

ljwx-boot同步更新:
- TUserHealthDataDailyMapper.xml: 添加customer_id字段映射
- TUserHealthDataWeeklyMapper.xml: 添加customer_id字段映射

其他改进:
- 告警规则缓存优化
- Redis缓存性能提升
- 健康数据处理性能优化
- 添加完整的文档和工具脚本

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-10 13:49:59 +08:00

457 lines
18 KiB
Java

package com.ljwx.modules.health.service.impl;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ljwx.common.util.DateUtil;
import com.ljwx.infrastructure.redis.RedisService;
import com.ljwx.modules.health.domain.entity.TDeviceMessage;
import com.ljwx.modules.health.domain.entity.TDeviceMessageDetail;
import com.ljwx.modules.health.service.ITDeviceMessageService;
import com.ljwx.modules.health.service.ITDeviceMessageDetailService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
/**
* 消息生命周期管理服务
* 负责消息生命周期事件记录、里程碑跟踪、状态分析等功能
*
* @Author brunoGao
* @CreateTime 2025-09-10
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class MessageLifecycleService {
private final ITDeviceMessageService messageService;
private final ITDeviceMessageDetailService messageDetailService;
private final RedisService redisService;
private static final String LIFECYCLE_CACHE_PREFIX = "message:lifecycle:";
private static final String LIFECYCLE_STATS_PREFIX = "message:lifecycle:stats:";
/**
* 记录消息生命周期事件
*/
@Transactional
public void recordLifecycleEvent(Long messageId, String event, String operator, String platform) {
try {
// 获取消息信息
TDeviceMessage message = messageService.getById(messageId);
if (message == null) {
log.warn("消息不存在,无法记录生命周期事件: messageId={}, event={}", messageId, event);
return;
}
// 构建生命周期事件
JSONObject lifecycleEvent = new JSONObject();
lifecycleEvent.set("event", event);
lifecycleEvent.set("timestamp", LocalDateTime.now());
lifecycleEvent.set("operator", operator);
lifecycleEvent.set("platform", platform);
lifecycleEvent.set("messageId", messageId);
// 存储到Redis缓存
String cacheKey = LIFECYCLE_CACHE_PREFIX + messageId;
List<JSONObject> events = getLifecycleEvents(messageId);
events.add(lifecycleEvent);
redisService.set(cacheKey, JSONUtil.toJsonStr(events), 7 * 24 * 3600); // 7天过期
// 更新消息状态
updateMessageStatus(messageId, event);
// 发布生命周期事件到Redis
JSONObject eventNotification = new JSONObject();
eventNotification.set("type", "lifecycle_event");
eventNotification.set("messageId", messageId);
eventNotification.set("event", event);
eventNotification.set("timestamp", LocalDateTime.now());
redisService.publish("message:lifecycle:events", eventNotification.toString());
log.info("记录消息生命周期事件成功: messageId={}, event={}, operator={}", messageId, event, operator);
} catch (Exception e) {
log.error("记录消息生命周期事件失败: messageId={}, event={}", messageId, event, e);
}
}
/**
* 获取消息生命周期事件列表
*/
public List<JSONObject> getLifecycleEvents(Long messageId) {
try {
String cacheKey = LIFECYCLE_CACHE_PREFIX + messageId;
String eventsJson = redisService.get(cacheKey);
if (eventsJson != null) {
return JSONUtil.toList(eventsJson, JSONObject.class);
}
return new ArrayList<>();
} catch (Exception e) {
log.error("获取消息生命周期事件失败: messageId={}", messageId, e);
return new ArrayList<>();
}
}
/**
* 获取消息生命周期里程碑
*/
public JSONObject getLifecycleMilestones(Long messageId) {
try {
List<JSONObject> events = getLifecycleEvents(messageId);
JSONObject milestones = new JSONObject();
// 按事件类型分组
Map<String, List<JSONObject>> eventGroups = events.stream()
.collect(Collectors.groupingBy(event -> event.getStr("event")));
// 计算关键里程碑
JSONObject createdEvent = eventGroups.getOrDefault("created", new ArrayList<>())
.stream().findFirst().orElse(null);
JSONObject publishedEvent = eventGroups.getOrDefault("published", new ArrayList<>())
.stream().findFirst().orElse(null);
if (createdEvent != null) {
milestones.set("created", createdEvent);
if (publishedEvent != null) {
LocalDateTime createdTime = createdEvent.get("timestamp", LocalDateTime.class);
LocalDateTime publishedTime = publishedEvent.get("timestamp", LocalDateTime.class);
long publishDelay = ChronoUnit.SECONDS.between(createdTime, publishedTime);
milestones.set("publishDelay", publishDelay);
milestones.set("published", publishedEvent);
}
}
// 统计分发状态
List<JSONObject> deliveryEvents = eventGroups.getOrDefault("delivery_attempt", new ArrayList<>());
List<JSONObject> ackEvents = eventGroups.getOrDefault("acknowledged", new ArrayList<>());
milestones.set("totalDeliveryAttempts", deliveryEvents.size());
milestones.set("totalAcknowledgements", ackEvents.size());
// 计算完成进度
JSONObject progress = calculateProgress(messageId, events);
milestones.set("progress", progress);
return milestones;
} catch (Exception e) {
log.error("获取消息生命周期里程碑失败: messageId={}", messageId, e);
return new JSONObject();
}
}
/**
* 计算消息完成进度
*/
private JSONObject calculateProgress(Long messageId, List<JSONObject> events) {
JSONObject progress = new JSONObject();
try {
// 获取消息详情统计
QueryWrapper<TDeviceMessageDetail> wrapper = new QueryWrapper<>();
wrapper.eq("message_id", messageId);
List<TDeviceMessageDetail> details = messageDetailService.list(wrapper);
if (details.isEmpty()) {
progress.set("totalTargets", 0);
progress.set("delivered", 0);
progress.set("acknowledged", 0);
progress.set("pending", 0);
progress.set("failed", 0);
progress.set("completionRate", 0.0);
return progress;
}
int totalTargets = details.size();
long delivered = details.stream().mapToLong(d ->
"delivered".equals(d.getDeliveryStatus()) || "acknowledged".equals(d.getDeliveryStatus()) ? 1 : 0).sum();
long acknowledged = details.stream().mapToLong(d ->
"acknowledged".equals(d.getDeliveryStatus()) ? 1 : 0).sum();
long pending = details.stream().mapToLong(d ->
"pending".equals(d.getDeliveryStatus()) ? 1 : 0).sum();
long failed = details.stream().mapToLong(d ->
"failed".equals(d.getDeliveryStatus()) ? 1 : 0).sum();
double completionRate = totalTargets > 0 ? (double) acknowledged / totalTargets * 100 : 0.0;
progress.set("totalTargets", totalTargets);
progress.set("delivered", delivered);
progress.set("acknowledged", acknowledged);
progress.set("pending", pending);
progress.set("failed", failed);
progress.set("completionRate", Math.round(completionRate * 100.0) / 100.0);
// 计算平均响应时间
OptionalDouble avgResponseTime = details.stream()
.filter(d -> d.getResponseTime() != null && d.getResponseTime() > 0)
.mapToDouble(TDeviceMessageDetail::getResponseTime)
.average();
progress.set("avgResponseTime", avgResponseTime.orElse(0.0));
} catch (Exception e) {
log.error("计算消息完成进度失败: messageId={}", messageId, e);
}
return progress;
}
/**
* 生成消息生命周期报告
*/
public JSONObject generateLifecycleReport(Long messageId) {
try {
TDeviceMessage message = messageService.getById(messageId);
if (message == null) {
throw new RuntimeException("消息不存在: " + messageId);
}
JSONObject report = new JSONObject();
// 基本信息
report.set("messageId", messageId);
report.set("messageType", message.getMessageType());
report.set("subType", message.getSubType());
report.set("title", message.getTitle());
report.set("priority", message.getPriority());
report.set("urgency", message.getUrgency());
// 时间信息
report.set("createTime", message.getCreateTime());
report.set("publishTime", message.getSentTime());
// 生命周期事件
List<JSONObject> events = getLifecycleEvents(messageId);
report.set("lifecycleEvents", events);
// 里程碑信息
JSONObject milestones = getLifecycleMilestones(messageId);
report.set("milestones", milestones);
// 分发统计
QueryWrapper<TDeviceMessageDetail> wrapper = new QueryWrapper<>();
wrapper.eq("message_id", messageId);
List<TDeviceMessageDetail> details = messageDetailService.list(wrapper);
// 按状态分组统计
Map<String, Long> statusStats = details.stream()
.collect(Collectors.groupingBy(
d -> d.getDeliveryStatus() != null ? d.getDeliveryStatus() : "unknown",
Collectors.counting()
));
report.set("statusStatistics", statusStats);
// 按渠道分组统计
Map<String, Long> channelStats = details.stream()
.collect(Collectors.groupingBy(
d -> d.getChannel() != null ? d.getChannel() : "unknown",
Collectors.counting()
));
report.set("channelStatistics", channelStats);
// 性能指标
JSONObject performance = new JSONObject();
// 总体耗时
if (message.getCreateTime() != null && message.getSentTime() != null) {
long totalDuration = ChronoUnit.SECONDS.between(message.getCreateTime(), message.getSentTime());
performance.set("publishDuration", totalDuration);
}
// 响应时间统计
List<Integer> responseTimes = details.stream()
.filter(d -> d.getResponseTime() != null && d.getResponseTime() > 0)
.map(TDeviceMessageDetail::getResponseTime)
.collect(Collectors.toList());
if (!responseTimes.isEmpty()) {
performance.set("minResponseTime", Collections.min(responseTimes));
performance.set("maxResponseTime", Collections.max(responseTimes));
performance.set("avgResponseTime",
responseTimes.stream().mapToInt(Integer::intValue).average().orElse(0.0));
}
report.set("performance", performance);
// 异常统计
long failedCount = details.stream()
.mapToLong(d -> "failed".equals(d.getDeliveryStatus()) ? 1 : 0)
.sum();
JSONObject exceptions = new JSONObject();
exceptions.set("failedDeliveries", failedCount);
exceptions.set("failureRate",
details.size() > 0 ? (double) failedCount / details.size() * 100 : 0.0);
report.set("exceptions", exceptions);
return report;
} catch (Exception e) {
log.error("生成消息生命周期报告失败: messageId={}", messageId, e);
throw new RuntimeException("生成生命周期报告失败", e);
}
}
/**
* 获取组织消息生命周期统计
*/
public JSONObject getOrganizationLifecycleStats(Long orgId, LocalDateTime startTime, LocalDateTime endTime) {
try {
JSONObject stats = new JSONObject();
// 获取组织内消息
QueryWrapper<TDeviceMessage> messageWrapper = new QueryWrapper<>();
messageWrapper.eq("customer_id", orgId);
if (startTime != null) {
messageWrapper.ge("create_time", startTime);
}
if (endTime != null) {
messageWrapper.le("create_time", endTime);
}
List<TDeviceMessage> messages = messageService.list(messageWrapper);
if (messages.isEmpty()) {
stats.set("totalMessages", 0);
stats.set("avgCompletionTime", 0.0);
stats.set("avgCompletionRate", 0.0);
return stats;
}
// 基础统计
stats.set("totalMessages", messages.size());
// 按类型分组统计
Map<String, Long> typeStats = messages.stream()
.collect(Collectors.groupingBy(
m -> m.getMessageType() != null ? m.getMessageType() : "unknown",
Collectors.counting()
));
stats.set("messageTypeStats", typeStats);
// 按紧急程度分组统计
Map<String, Long> urgencyStats = messages.stream()
.collect(Collectors.groupingBy(
m -> m.getUrgency() != null ? m.getUrgency() : "unknown",
Collectors.counting()
));
stats.set("urgencyStats", urgencyStats);
// 计算平均完成率和完成时间
List<Double> completionRates = new ArrayList<>();
List<Long> completionTimes = new ArrayList<>();
for (TDeviceMessage message : messages) {
JSONObject progress = calculateProgress(message.getId(), getLifecycleEvents(message.getId()));
Double completionRate = progress.getDouble("completionRate");
if (completionRate != null) {
completionRates.add(completionRate);
}
// 计算完成时间(从创建到最后确认的时间)
if (message.getCreateTime() != null && message.getReceivedTime() != null) {
long duration = ChronoUnit.MINUTES.between(message.getCreateTime(), message.getReceivedTime());
completionTimes.add(duration);
}
}
double avgCompletionRate = completionRates.stream()
.mapToDouble(Double::doubleValue)
.average()
.orElse(0.0);
double avgCompletionTime = completionTimes.stream()
.mapToLong(Long::longValue)
.average()
.orElse(0.0);
stats.set("avgCompletionRate", Math.round(avgCompletionRate * 100.0) / 100.0);
stats.set("avgCompletionTime", Math.round(avgCompletionTime * 100.0) / 100.0); // 分钟
// 趋势分析(按天统计)
Map<String, Long> dailyStats = messages.stream()
.collect(Collectors.groupingBy(
m -> DateUtil.format(m.getCreateTime(), "yyyy-MM-dd"),
Collectors.counting()
));
stats.set("dailyTrend", dailyStats);
return stats;
} catch (Exception e) {
log.error("获取组织消息生命周期统计失败: orgId={}", orgId, e);
return new JSONObject();
}
}
/**
* 更新消息状态
*/
private void updateMessageStatus(Long messageId, String event) {
try {
TDeviceMessage message = messageService.getById(messageId);
if (message == null) return;
String newStatus = null;
switch (event) {
case "created":
newStatus = "created";
break;
case "published":
newStatus = "published";
break;
case "delivery_completed":
newStatus = "delivered";
break;
case "fully_acknowledged":
newStatus = "acknowledged";
break;
case "expired":
newStatus = "expired";
break;
}
if (newStatus != null && !newStatus.equals(message.getMessageStatus())) {
message.setMessageStatus(newStatus);
messageService.updateById(message);
log.info("更新消息状态: messageId={}, oldStatus={}, newStatus={}",
messageId, message.getMessageStatus(), newStatus);
}
} catch (Exception e) {
log.error("更新消息状态失败: messageId={}, event={}", messageId, event, e);
}
}
/**
* 清理过期的生命周期缓存
*/
public void cleanupExpiredLifecycleCache() {
try {
log.info("开始清理过期的消息生命周期缓存...");
// 这里可以添加具体的清理逻辑
// Redis的TTL会自动处理过期数据,这里主要做统计和日志
log.info("消息生命周期缓存清理完成");
} catch (Exception e) {
log.error("清理消息生命周期缓存失败", e);
}
}
}