diff --git a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/config/FeiShuClientConfig.java b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/config/FeiShuClientConfig.java index fa88d22..6ad0093 100644 --- a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/config/FeiShuClientConfig.java +++ b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/config/FeiShuClientConfig.java @@ -1,7 +1,4 @@ package com.tashow.cloud.app.config; - -import com.tashow.cloud.sdk.feishu.client.FeiShuAlertClient; -import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.StringRedisTemplate; @@ -12,11 +9,8 @@ import org.springframework.data.redis.core.StringRedisTemplate; */ @Configuration public class FeiShuClientConfig { - @Autowired private StringRedisTemplate stringRedisTemplate; - - /* @PostConstruct public void initFeiShuClient() { FeiShuAlertClient.setRedisTemplate(stringRedisTemplate); diff --git a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/config/FeishuConfig.java b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/config/FeishuConfig.java deleted file mode 100644 index cf7478b..0000000 --- a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/config/FeishuConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.tashow.cloud.app.config; - -import com.tashow.cloud.app.service.feishu.FeiShuCardDataService; -import com.tashow.cloud.sdk.feishu.client.FeiShuAlertClient; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import jakarta.annotation.PostConstruct; - -/** - * 飞书配置类 - * 用于初始化飞书SDK与应用层的集成 - */ -@Configuration -public class FeishuConfig { - - private final FeiShuAlertClient feiShuAlertClient; - private final FeiShuCardDataService feiShuCardDataService; - - @Autowired - public FeishuConfig(FeiShuAlertClient feiShuAlertClient, FeiShuCardDataService feiShuCardDataService) { - this.feiShuAlertClient = feiShuAlertClient; - this.feiShuCardDataService = feiShuCardDataService; - } - - -} \ No newline at end of file diff --git a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/controller/FeishuController.java b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/controller/FeishuController.java index 8fc964f..e374d81 100644 --- a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/controller/FeishuController.java +++ b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/controller/FeishuController.java @@ -1,6 +1,5 @@ package com.tashow.cloud.app.controller; import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; import com.lark.oapi.core.utils.Decryptor; import com.tashow.cloud.app.service.feishu.FeiShuCardDataService; import com.tashow.cloud.sdk.feishu.client.FeiShuAlertClient; @@ -16,7 +15,8 @@ import java.util.Map; @RestController public class FeishuController { - private final Logger log = LoggerFactory.getLogger(FeishuController.class); + private static final Logger log = LoggerFactory.getLogger(FeishuController.class); + private static final String ACTION_COMPLETE_ALARM = "complete_alarm"; private final FeiShuAlertClient feiShuAlertClient; private final FeiShuCardDataService feiShuCardDataService; @@ -29,7 +29,6 @@ public class FeishuController { this.feiShuAlertClient = feiShuAlertClient; this.feiShuCardDataService = feiShuCardDataService; this.larkConfig = larkConfig; - } @RequestMapping("/card1") @@ -39,23 +38,33 @@ public class FeishuController { if (data.containsKey("app_id") && data.containsKey("action")) { JSONObject action = data.getJSONObject("action"); JSONObject value = action.getJSONObject("value"); - if (value != null && "complete_alarm".equals(value.getStr("action"))) { + if (value != null && ACTION_COMPLETE_ALARM.equals(value.getStr("action"))) { String messageId = data.getStr("open_message_id"); - Map templateData = feiShuCardDataService.getCardData(messageId); - log.info("从Redis获取的模板数据: {}", templateData); - return feiShuAlertClient.buildCardWithData("AAqdp4Mrvf2V9", templateData); + return feiShuAlertClient.buildCardWithData(larkConfig.getSuccessCards(), templateData); } } + if (data.containsKey("encrypt")) { Decryptor decryptor = new Decryptor(larkConfig.getEncryptKey()); - String encrypt = decryptor.decrypt(data.getStr("encrypt")); - return encrypt; + return decryptor.decrypt(data.getStr("encrypt")); } + return "{}"; } catch (Exception e) { log.error("卡片处理异常", e); return "{\"code\":1,\"msg\":\"处理异常: " + e.getMessage() + "\"}"; } } + + /** + * 发送并存储卡片消息 + */ + public String sendAndStoreCardMessage(String chatId, String templateId, Map templateData) throws Exception { + String messageId = feiShuAlertClient.sendCardMessage(chatId, templateId, templateData); + if (messageId != null) { + feiShuCardDataService.saveCardData(messageId, templateData); + } + return messageId; + } } diff --git a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/mq/config/BuriedPointConfiguration.java b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/mq/config/BuriedPointConfiguration.java index 35a8eca..7db76e9 100644 --- a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/mq/config/BuriedPointConfiguration.java +++ b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/mq/config/BuriedPointConfiguration.java @@ -71,6 +71,7 @@ public class BuriedPointConfiguration implements WebMvcConfigurer { "/v3/api-docs/**", "/webjars/**", "/static/**", + "/card1", "/error" ); } diff --git a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/mq/consumer/buriedPoint/BuriedPointConsumer.java b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/mq/consumer/buriedPoint/BuriedPointConsumer.java index 03a8734..8e5b7dc 100644 --- a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/mq/consumer/buriedPoint/BuriedPointConsumer.java +++ b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/mq/consumer/buriedPoint/BuriedPointConsumer.java @@ -1,13 +1,11 @@ package com.tashow.cloud.app.mq.consumer.buriedPoint; -import com.tashow.cloud.sdk.feishu.client.FeiShuAlertClient; import com.tashow.cloud.app.mapper.BuriedPointMapper; import com.tashow.cloud.app.mapper.BuriedPointFailRecordMapper; -import com.tashow.cloud.app.mq.message.BuriedMessages; import com.tashow.cloud.app.model.BuriedPoint; import com.tashow.cloud.app.model.BuriedPointFailRecord; -import com.tashow.cloud.sdk.feishu.config.LarkConfig; -import com.tashow.cloud.sdk.feishu.util.ChartImageGenerator; +import com.tashow.cloud.app.mq.message.BuriedMessages; +import com.tashow.cloud.app.service.feishu.BuriedPointMonitorService; import com.rabbitmq.client.Channel; import com.tashow.cloud.mq.rabbitmq.consumer.AbstractRabbitMQConsumer; import lombok.RequiredArgsConstructor; @@ -15,12 +13,11 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.support.AmqpHeaders; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; -import java.text.SimpleDateFormat; -import java.util.*; + +import java.util.Date; import org.springframework.dao.DuplicateKeyException; import com.tashow.cloud.common.util.json.JsonUtils; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -36,8 +33,7 @@ public class BuriedPointConsumer extends AbstractRabbitMQConsumer queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(BuriedPointFailRecord::getCorrelationId, correlationId); - BuriedPointFailRecord failRecord = buriedPointFailRecordMapper.selectOne(queryWrapper); - return failRecord != null ? failRecord.getRetryCount() : 0; } + + String correlationId = String.valueOf(message.getId()); + BuriedPointFailRecord failRecord = findFailRecord(correlationId); + return failRecord != null ? failRecord.getRetryCount() : 0; } catch (Exception e) { - log.warn("[埋点消费者] 获取消息重试次数失败: {}", e.getMessage()); + log.warn("[埋点消费者] 获取重试次数失败", e); return 0; } } + /** + * 根据关联ID查找失败记录 + */ + private BuriedPointFailRecord findFailRecord(String correlationId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(BuriedPointFailRecord::getCorrelationId, correlationId); + return buriedPointFailRecordMapper.selectOne(queryWrapper); + } + @Override public void updateMessageStatus(BuriedMessages message) { try { @@ -90,11 +92,9 @@ public class BuriedPointConsumer extends AbstractRabbitMQConsumer queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(BuriedPointFailRecord::getCorrelationId, correlationId); - BuriedPointFailRecord failRecord = buriedPointFailRecordMapper.selectOne(queryWrapper); - if (failRecord != null) { - failRecord.setRetryCount(message.getRetryCount()); - failRecord.setUpdateTime(new Date()); - failRecord.setMessageContent(JsonUtils.toJsonString(message)); - buriedPointFailRecordMapper.updateById(failRecord); - } else { - saveToFailRecord(message, ""); - } + saveToFailRecord(message, ""); } } catch (Exception e) { - log.error("[埋点消费者] 更新重试次数失败: {}, 错误: {}", message.getId(), e.getMessage()); + log.error("[埋点消费者] 更新重试次数失败", e); } } + /** + * 更新埋点表中的重试次数 + */ + private void updateBuriedPointRetryCount(BuriedPoint buriedPoint, BuriedMessages message) { + buriedPoint.setRetryCount(message.getRetryCount()); + buriedPoint.setUpdateTime(new Date()); + buriedPointMapper.updateById(buriedPoint); + } + + /** + * 更新失败记录表中的重试次数 + */ + private void updateFailRecordRetryCount(BuriedPointFailRecord failRecord, BuriedMessages message) { + failRecord.setRetryCount(message.getRetryCount()); + failRecord.setUpdateTime(new Date()); + failRecord.setMessageContent(JsonUtils.toJsonString(message)); + buriedPointFailRecordMapper.updateById(failRecord); + } + @Override public boolean saveToDatabase(BuriedMessages message) { try { - log.debug("[埋点消费者] 准备保存埋点数据,事件ID: {}", message.getId()); BuriedPoint existingPoint = buriedPointMapper.selectByEventId(message.getId()); if (existingPoint != null) { - existingPoint.setStatus(message.getStatusCode()); - existingPoint.setUpdateTime(new Date()); - if (message.getRetryCount() != null) { - int newRetryCount = Math.max(existingPoint.getRetryCount(), message.getRetryCount()); - existingPoint.setRetryCount(newRetryCount); - message.setRetryCount(newRetryCount); - } - int result = buriedPointMapper.updateById(existingPoint); - return result > 0; + return updateExistingBuriedPoint(existingPoint, message); } - BuriedPoint buriedPoint = new BuriedPoint(); - buriedPoint.setEventId(message.getId()); - buriedPoint.setEventTime(System.currentTimeMillis()); - - buriedPoint.setUserId(message.getUserId()); - buriedPoint.setEventType(message.getEventType()); - buriedPoint.setService(applicationName); - buriedPoint.setMethod(message.getMethod()); - buriedPoint.setSessionId(message.getSessionId()); - buriedPoint.setClientIp(message.getClientIp()); - buriedPoint.setServerIp(message.getServerIp()); - buriedPoint.setStatus(message.getStatusCode()); - buriedPoint.setRetryCount(message.getRetryCount()); - buriedPoint.setPagePath(message.getPagePath()); - buriedPoint.setElementId(message.getElementId()); - buriedPoint.setDuration(message.getDuration()); - buriedPoint.setCreateTime(new Date()); - buriedPoint.setUpdateTime(new Date()); - - log.debug("[埋点消费者] 埋点实体数据: eventId={}, eventType={}, userId={}, service={}, method={}, status={}, retryCount={}", - buriedPoint.getEventId(), buriedPoint.getEventType(), buriedPoint.getUserId(), - buriedPoint.getService(), buriedPoint.getMethod(), buriedPoint.getStatus(), - buriedPoint.getRetryCount()); - - buriedPointMapper.insert(buriedPoint); - log.info("[埋点消费者] 埋点数据已保存到数据库, 事件ID: {}, 状态: {}", message.getId(), message.getStatusCode()); - return true; + return createNewBuriedPoint(message); } catch (DuplicateKeyException e) { - log.warn("[埋点消费者] 埋点数据已存在, 事件ID: {}", message.getId()); - return true; // 数据已存在也视为成功 + return true; } catch (Exception e) { - log.error("[埋点消费者] 保存埋点数据到数据库失败, 事件ID: {}, 错误: {}", message.getId(), e.getMessage(), e); + log.error("[埋点消费者] 保存数据失败", e); throw e; } } + /** + * 更新已存在的埋点记录 + */ + private boolean updateExistingBuriedPoint(BuriedPoint existingPoint, BuriedMessages message) { + existingPoint.setStatus(message.getStatusCode()); + existingPoint.setUpdateTime(new Date()); + + if (message.getRetryCount() != null) { + int newRetryCount = Math.max(existingPoint.getRetryCount(), message.getRetryCount()); + existingPoint.setRetryCount(newRetryCount); + message.setRetryCount(newRetryCount); + } + + return buriedPointMapper.updateById(existingPoint) > 0; + } + + /** + * 创建新的埋点记录 + */ + private boolean createNewBuriedPoint(BuriedMessages message) { + BuriedPoint buriedPoint = new BuriedPoint(); + buriedPoint.setEventId(message.getId()); + buriedPoint.setEventTime(System.currentTimeMillis()); + buriedPoint.setUserId(message.getUserId()); + buriedPoint.setEventType(message.getEventType()); + buriedPoint.setService(applicationName); + buriedPoint.setMethod(message.getMethod()); + buriedPoint.setSessionId(message.getSessionId()); + buriedPoint.setClientIp(message.getClientIp()); + buriedPoint.setServerIp(message.getServerIp()); + buriedPoint.setStatus(message.getStatusCode()); + buriedPoint.setRetryCount(message.getRetryCount()); + buriedPoint.setPagePath(message.getPagePath()); + buriedPoint.setElementId(message.getElementId()); + buriedPoint.setDuration(message.getDuration()); + buriedPoint.setCreateTime(new Date()); + buriedPoint.setUpdateTime(new Date()); + + buriedPointMapper.insert(buriedPoint); + return true; + } + @Override public void saveToFailRecord(BuriedMessages message, String cause) { try { String correlationId = String.valueOf(message.getId()); - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(BuriedPointFailRecord::getCorrelationId, correlationId); - BuriedPointFailRecord existingRecord = buriedPointFailRecordMapper.selectOne(queryWrapper); + BuriedPointFailRecord existingRecord = findFailRecord(correlationId); if (existingRecord != null) { - log.info("[埋点消费者] 发现已有失败记录,将更新: {}", correlationId); - existingRecord.setExchange(BuriedMessages.EXCHANGE); - existingRecord.setRoutingKey(BuriedMessages.ROUTING_KEY); - existingRecord.setCause(message.getErrorMessage()+cause); - existingRecord.setMessageContent(JsonUtils.toJsonString(message)); - existingRecord.setRetryCount(message.getRetryCount()); - existingRecord.setStatus(BuriedPointFailRecord.STATUS_UNPROCESSED); - existingRecord.setUpdateTime(new Date()); - buriedPointFailRecordMapper.updateById(existingRecord); - log.info("[埋点消费者] 已更新失败记录, 事件ID: {}", correlationId); + updateExistingFailRecord(existingRecord, message, cause); } else { - BuriedPointFailRecord failRecord = new BuriedPointFailRecord(); - failRecord.setCorrelationId(correlationId); - failRecord.setExchange(BuriedMessages.EXCHANGE); - failRecord.setRoutingKey(BuriedMessages.ROUTING_KEY); - failRecord.setCause(message.getErrorMessage()+cause); - failRecord.setMessageContent(JsonUtils.toJsonString(message)); - failRecord.setRetryCount(message.getRetryCount()); - failRecord.setStatus(BuriedPointFailRecord.STATUS_UNPROCESSED); - failRecord.setCreateTime(new Date()); - failRecord.setUpdateTime(new Date()); - buriedPointFailRecordMapper.insert(failRecord); - log.info("[埋点消费者] 已将失败消息保存到失败记录表, 事件ID: {}", message.getId()); - - // 查询最近12小时的失败记录数量 + createNewFailRecord(message, cause); checkFailRecordsAndAlert(); } } catch (Exception e) { - log.error("[埋点消费者] 保存失败记录失败: {}, 错误: {}", message.getId(), e.getMessage(), e); + log.error("[埋点消费者] 保存失败记录失败", e); } } + /** + * 更新已存在的失败记录 + */ + private void updateExistingFailRecord(BuriedPointFailRecord record, BuriedMessages message, String cause) { + record.setExchange(BuriedMessages.EXCHANGE); + record.setRoutingKey(BuriedMessages.ROUTING_KEY); + record.setCause(message.getErrorMessage() + cause); + record.setMessageContent(JsonUtils.toJsonString(message)); + record.setRetryCount(message.getRetryCount()); + record.setStatus(BuriedPointFailRecord.STATUS_UNPROCESSED); + record.setUpdateTime(new Date()); + buriedPointFailRecordMapper.updateById(record); + } + + /** + * 创建新的失败记录 + */ + private void createNewFailRecord(BuriedMessages message, String cause) { + String correlationId = String.valueOf(message.getId()); + BuriedPointFailRecord failRecord = new BuriedPointFailRecord(); + failRecord.setCorrelationId(correlationId); + failRecord.setExchange(BuriedMessages.EXCHANGE); + failRecord.setRoutingKey(BuriedMessages.ROUTING_KEY); + failRecord.setCause(message.getErrorMessage() + cause); + failRecord.setMessageContent(JsonUtils.toJsonString(message)); + failRecord.setRetryCount(message.getRetryCount()); + failRecord.setStatus(BuriedPointFailRecord.STATUS_UNPROCESSED); + failRecord.setCreateTime(new Date()); + failRecord.setUpdateTime(new Date()); + buriedPointFailRecordMapper.insert(failRecord); + } + /** * 检查失败记录数量并发送告警 */ private void checkFailRecordsAndAlert() { try { - Date now = new Date(); - Date twelveHoursAgo = new Date(now.getTime() - 12 * 60 * 60 * 1000L); - LambdaQueryWrapper failRecordQuery = new LambdaQueryWrapper<>(); - failRecordQuery.ge(BuriedPointFailRecord::getCreateTime, twelveHoursAgo) - .le(BuriedPointFailRecord::getCreateTime, now) - .eq(BuriedPointFailRecord::getStatus, BuriedPointFailRecord.STATUS_UNPROCESSED); - - Long failCountLast12Hours = buriedPointFailRecordMapper.selectCount(failRecordQuery); - log.warn("[埋点配置] 最近12小时埋点失败数量: {}", failCountLast12Hours); - - // 如果失败数量过多,记录警告日志 - if (failCountLast12Hours > 3) { - // 查询最近12小时的埋点失败数据,按小时统计 - List monitoringData = queryHourlyFailRecordData(twelveHoursAgo, now); - - try { - // 发送飞书告警消息 - feiShuAlertClient.sendBuriedPointAlertMessage(larkConfig.getChatId(), - monitoringData, - failCountLast12Hours.intValue(), - "埋点处理异常,请检查系统"); - } catch (Exception e) { - log.error("[埋点配置] 发送飞书告警失败", e); - } - log.error("[埋点配置] 警告:最近12小时埋点失败数量过多,请检查系统!失败数量: {}", failCountLast12Hours); - } + buriedPointMonitorService.checkFailRecordsAndAlert("埋点处理异常,请检查系统"); } catch (Exception e) { - log.error("[埋点配置] 检查失败记录数量异常", e); - } - } - - /** - * 查询失败记录数据,按小时统计 - */ - private List queryHourlyFailRecordData(Date startDate, Date endDate) { - List result = new ArrayList<>(); - - try { - // 只取最近12个小时的数据 - Calendar calendar = Calendar.getInstance(); - calendar.setTime(endDate); - calendar.add(Calendar.HOUR_OF_DAY, -12); - Date twelveHoursAgo = calendar.getTime(); - - SimpleDateFormat sdf = new SimpleDateFormat("HH:00"); - - // 从12小时前开始,每小时一个数据点 - for (int i = 0; i < 12; i++) { - calendar.setTime(twelveHoursAgo); - calendar.add(Calendar.HOUR_OF_DAY, i); - Date currentHourStart = calendar.getTime(); - calendar.add(Calendar.HOUR_OF_DAY, 1); - Date nextHourStart = calendar.getTime(); - - // 查询处理成功的记录数量 - LambdaQueryWrapper successQuery = new LambdaQueryWrapper<>(); - successQuery.ge(BuriedPointFailRecord::getCreateTime, currentHourStart) - .lt(BuriedPointFailRecord::getCreateTime, nextHourStart) - .eq(BuriedPointFailRecord::getStatus, BuriedPointFailRecord.STATUS_SUCCESS); // 处理成功 - Long successCount = buriedPointFailRecordMapper.selectCount(successQuery); - - // 查询处理失败或未处理的记录数量 - LambdaQueryWrapper failQuery = new LambdaQueryWrapper<>(); - failQuery.ge(BuriedPointFailRecord::getCreateTime, currentHourStart) - .lt(BuriedPointFailRecord::getCreateTime, nextHourStart) - .in(BuriedPointFailRecord::getStatus, - Arrays.asList(BuriedPointFailRecord.STATUS_UNPROCESSED, BuriedPointFailRecord.STATUS_FAILED)); // 未处理或处理失败 - Long failCount = buriedPointFailRecordMapper.selectCount(failQuery); - - // 添加到结果列表,无论是否有数据 - String hourLabel = sdf.format(currentHourStart); - result.add(new ChartImageGenerator.MonitoringDataPoint(hourLabel, successCount.intValue(), failCount.intValue())); - } - - return result; - } catch (Exception e) { - log.error("[埋点配置] 查询每小时失败记录数据失败", e); - // 返回空列表 - return Collections.emptyList(); + log.error("[埋点消费者] 检查失败记录异常", e); } } } diff --git a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/mq/handler/BuriedPointFailRecordHandler.java b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/mq/handler/BuriedPointFailRecordHandler.java index 524ddf0..79108b8 100644 --- a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/mq/handler/BuriedPointFailRecordHandler.java +++ b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/mq/handler/BuriedPointFailRecordHandler.java @@ -3,154 +3,91 @@ package com.tashow.cloud.app.mq.handler; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.tashow.cloud.app.mapper.BuriedPointFailRecordMapper; import com.tashow.cloud.app.model.BuriedPointFailRecord; +import com.tashow.cloud.app.service.feishu.BuriedPointMonitorService; import com.tashow.cloud.mq.handler.FailRecordHandler; -import com.tashow.cloud.sdk.feishu.client.FeiShuAlertClient; -import com.tashow.cloud.sdk.feishu.config.LarkConfig; -import com.tashow.cloud.sdk.feishu.util.ChartImageGenerator; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; import java.util.Date; -import java.util.List; /** * 埋点失败记录处理器 - * - * @author tashow */ @Slf4j @Component @RequiredArgsConstructor public class BuriedPointFailRecordHandler implements FailRecordHandler { + private final BuriedPointFailRecordMapper buriedPointFailRecordMapper; + private final BuriedPointMonitorService buriedPointMonitorService; - @Autowired - private BuriedPointFailRecordMapper buriedPointFailRecordMapper; - @Autowired - FeiShuAlertClient feiShuAlertClient; - @Autowired - LarkConfig larkConfig; /** * 保存消息发送失败记录 */ @Override public void saveFailRecord(String correlationId, String exchange, String routingKey, String cause, String messageContent) { try { - log.info("[埋点处理器] 保存发送失败记录: correlationId={}", correlationId); // 先查询是否已存在记录 - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(BuriedPointFailRecord::getCorrelationId, correlationId); - BuriedPointFailRecord existingRecord = buriedPointFailRecordMapper.selectOne(queryWrapper); + BuriedPointFailRecord existingRecord = findExistingRecord(correlationId); + if (existingRecord != null) { - log.info("[埋点处理器] 发现已有失败记录,将更新: {}", correlationId); - existingRecord.setExchange(exchange); - existingRecord.setRoutingKey(routingKey); - existingRecord.setCause(cause); - existingRecord.setMessageContent(messageContent); - existingRecord.setStatus(BuriedPointFailRecord.STATUS_UNPROCESSED); - existingRecord.setUpdateTime(new Date()); - buriedPointFailRecordMapper.updateById(existingRecord); - log.info("[埋点处理器] 发送失败记录已更新: correlationId={}", correlationId); + updateExistingRecord(existingRecord, exchange, routingKey, cause, messageContent); } else { - BuriedPointFailRecord failRecord = new BuriedPointFailRecord(); - failRecord.setCorrelationId(correlationId); - failRecord.setExchange(exchange); - failRecord.setRoutingKey(routingKey); - failRecord.setCause(cause); - failRecord.setMessageContent(messageContent); - failRecord.setRetryCount(0); - failRecord.setStatus(BuriedPointFailRecord.STATUS_UNPROCESSED); - failRecord.setCreateTime(new Date()); - failRecord.setUpdateTime(new Date()); - buriedPointFailRecordMapper.insert(failRecord); - log.info("[埋点处理器] 发送失败记录已保存: correlationId={}", correlationId); + createNewFailRecord(correlationId, exchange, routingKey, cause, messageContent); checkAlertThreshold(cause); } } catch (Exception e) { - log.error("[埋点处理器] 保存发送失败记录异常", e); + log.error("[埋点处理器] 保存失败记录异常", e); } } + /** + * 查找已存在的失败记录 + */ + private BuriedPointFailRecord findExistingRecord(String correlationId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(BuriedPointFailRecord::getCorrelationId, correlationId); + return buriedPointFailRecordMapper.selectOne(queryWrapper); + } + + /** + * 更新已存在的失败记录 + */ + private void updateExistingRecord(BuriedPointFailRecord record, String exchange, String routingKey, + String cause, String messageContent) { + record.setExchange(exchange); + record.setRoutingKey(routingKey); + record.setCause(cause); + record.setMessageContent(messageContent); + record.setStatus(BuriedPointFailRecord.STATUS_UNPROCESSED); + record.setUpdateTime(new Date()); + buriedPointFailRecordMapper.updateById(record); + } + + /** + * 创建新的失败记录 + */ + private void createNewFailRecord(String correlationId, String exchange, String routingKey, + String cause, String messageContent) { + BuriedPointFailRecord failRecord = new BuriedPointFailRecord(); + failRecord.setCorrelationId(correlationId); + failRecord.setExchange(exchange); + failRecord.setRoutingKey(routingKey); + failRecord.setCause(cause); + failRecord.setMessageContent(messageContent); + failRecord.setRetryCount(0); + failRecord.setStatus(BuriedPointFailRecord.STATUS_UNPROCESSED); + failRecord.setCreateTime(new Date()); + failRecord.setUpdateTime(new Date()); + buriedPointFailRecordMapper.insert(failRecord); + } + /** * 检查是否达到告警阈值 */ @Override public boolean checkAlertThreshold(String cause) { - try { - Date now = new Date(); - Date twelveHoursAgo = new Date(now.getTime() - 12 * 60 * 60 * 1000L); - LambdaQueryWrapper failRecordQuery = new LambdaQueryWrapper<>(); - failRecordQuery.ge(BuriedPointFailRecord::getCreateTime, twelveHoursAgo).le(BuriedPointFailRecord::getCreateTime, now).eq(BuriedPointFailRecord::getStatus, BuriedPointFailRecord.STATUS_UNPROCESSED); - Long failCountLast12Hours = buriedPointFailRecordMapper.selectCount(failRecordQuery); - // 如果失败数量过多,记录警告日志 - if (failCountLast12Hours > 3) { - List monitoringData = queryHourlyFailRecordData(twelveHoursAgo, now); - try { - // 发送飞书告警消息 - feiShuAlertClient.sendBuriedPointAlertMessage(larkConfig.getChatId(), monitoringData, failCountLast12Hours.intValue(), cause); - } catch (Exception e) { - log.error("[埋点处理器] 发送飞书告警失败", e); - } - return true; - } - return false; - } catch (Exception e) { - log.error("[埋点处理器] 检查告警阈值异常", e); - return false; - } - } - - /** - * 查询失败记录数据,按小时统计 - * 仅查询最近12个小时的数据 - */ - private List queryHourlyFailRecordData(Date startDate, Date endDate) { - List result = new ArrayList<>(); - - try { - // 只取最近12个小时的数据 - Calendar calendar = Calendar.getInstance(); - calendar.setTime(endDate); - calendar.add(Calendar.HOUR_OF_DAY, -12); - Date twelveHoursAgo = calendar.getTime(); - - SimpleDateFormat sdf = new SimpleDateFormat("HH:00"); - - // 从12小时前开始,每小时一个数据点 - for (int i = 0; i < 12; i++) { - calendar.setTime(twelveHoursAgo); - calendar.add(Calendar.HOUR_OF_DAY, i); - Date currentHourStart = calendar.getTime(); - calendar.add(Calendar.HOUR_OF_DAY, 1); - Date nextHourStart = calendar.getTime(); - - // 查询处理成功的记录数量 - LambdaQueryWrapper successQuery = new LambdaQueryWrapper<>(); - successQuery.ge(BuriedPointFailRecord::getCreateTime, currentHourStart).lt(BuriedPointFailRecord::getCreateTime, nextHourStart).eq(BuriedPointFailRecord::getStatus, BuriedPointFailRecord.STATUS_SUCCESS); // 处理成功 - Long successCount = buriedPointFailRecordMapper.selectCount(successQuery); - - // 查询处理失败或未处理的记录数量 - LambdaQueryWrapper failQuery = new LambdaQueryWrapper<>(); - failQuery.ge(BuriedPointFailRecord::getCreateTime, currentHourStart).lt(BuriedPointFailRecord::getCreateTime, nextHourStart).in(BuriedPointFailRecord::getStatus, Arrays.asList(BuriedPointFailRecord.STATUS_UNPROCESSED, BuriedPointFailRecord.STATUS_FAILED)); // 未处理或处理失败 - Long failCount = buriedPointFailRecordMapper.selectCount(failQuery); - - // 添加到结果列表,无论是否有数据 - String hourLabel = sdf.format(currentHourStart); - result.add(new ChartImageGenerator.MonitoringDataPoint(hourLabel, successCount.intValue(), failCount.intValue())); - } - - return result; - } catch (Exception e) { - log.error("[埋点处理器] 查询每小时失败记录数据失败", e); - // 返回空列表 - return Collections.emptyList(); - } + return buriedPointMonitorService.checkFailRecordsAndAlert(cause); } } \ No newline at end of file diff --git a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/service/feishu/BuriedPointMonitorService.java b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/service/feishu/BuriedPointMonitorService.java new file mode 100644 index 0000000..9239792 --- /dev/null +++ b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/service/feishu/BuriedPointMonitorService.java @@ -0,0 +1,166 @@ +package com.tashow.cloud.app.service.feishu; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.tashow.cloud.app.mapper.BuriedPointFailRecordMapper; +import com.tashow.cloud.app.model.BuriedPointFailRecord; +import com.tashow.cloud.sdk.feishu.client.FeiShuAlertClient; +import com.tashow.cloud.sdk.feishu.config.LarkConfig; +import com.tashow.cloud.sdk.feishu.util.ChartImageGenerator; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * 埋点监控服务 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class BuriedPointMonitorService { + + private static final int ALERT_THRESHOLD = 3; + private static final int MONITORING_HOURS = 12; + + private final BuriedPointFailRecordMapper buriedPointFailRecordMapper; + private final FeiShuAlertClient feiShuAlertClient; + private final FeiShuCardDataService feiShuCardDataService; + private final LarkConfig larkConfig; + + /** + * 检查失败记录并发送告警 + */ + public boolean checkFailRecordsAndAlert(String cause) { + try { + Date now = new Date(); + Date hoursAgo = getDateHoursAgo(now, MONITORING_HOURS); + + Long failCount = getUnprocessedFailCount(hoursAgo, now); + + if (failCount > ALERT_THRESHOLD) { + sendAlertMessage(failCount.intValue(), hoursAgo, now, cause); + return true; + } + return false; + } catch (Exception e) { + log.error("[埋点监控] 检查失败记录异常", e); + return false; + } + } + + /** + * 获取未处理的失败记录数量 + */ + public Long getUnprocessedFailCount(Date startDate, Date endDate) { + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.ge(BuriedPointFailRecord::getCreateTime, startDate) + .le(BuriedPointFailRecord::getCreateTime, endDate) + .eq(BuriedPointFailRecord::getStatus, BuriedPointFailRecord.STATUS_UNPROCESSED); + return buriedPointFailRecordMapper.selectCount(query); + } + + /** + * 发送告警消息 + */ + private void sendAlertMessage(int failCount, Date startDate, Date endDate, String errorMessage) { + try { + List monitoringData = + queryHourlyFailRecordData(startDate, endDate); + + HashMap templateData = new HashMap<>(); + String chatId = larkConfig.getChatId(); + + String imageKey = feiShuAlertClient.uploadImage(monitoringData, errorMessage); + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + templateData.put("alert_title", "埋点数据异常告警"); + templateData.put("image_key", imageKey); + templateData.put("current_time", sdf.format(new Date())); + templateData.put("fail_count", failCount); + + String messageId = feiShuAlertClient.sendCardMessage( + chatId, + larkConfig.getExceptionCards(), + templateData + ); + feiShuCardDataService.saveCardData(messageId, templateData); + } catch (Exception e) { + log.error("[埋点监控] 发送告警失败", e); + } + } + + /** + * 查询按小时统计的失败记录数据 + */ + public List queryHourlyFailRecordData(Date startDate, Date endDate) { + List result = new ArrayList<>(); + + try { + Date limitedStartDate = getDateHoursAgo(endDate, MONITORING_HOURS); + Date actualStartDate = startDate.after(limitedStartDate) ? startDate : limitedStartDate; + + SimpleDateFormat sdf = new SimpleDateFormat("HH:00"); + Calendar calendar = Calendar.getInstance(); + + long hoursDiff = (endDate.getTime() - actualStartDate.getTime()) / (60 * 60 * 1000) + 1; + int hours = (int) Math.min(hoursDiff, MONITORING_HOURS); + + for (int i = 0; i < hours; i++) { + calendar.setTime(actualStartDate); + calendar.add(Calendar.HOUR_OF_DAY, i); + Date currentHourStart = calendar.getTime(); + calendar.add(Calendar.HOUR_OF_DAY, 1); + Date nextHourStart = calendar.getTime(); + + int successCount = getHourlyRecordCount(currentHourStart, nextHourStart, BuriedPointFailRecord.STATUS_SUCCESS); + int failCount = getHourlyFailedCount(currentHourStart, nextHourStart); + + result.add(new ChartImageGenerator.MonitoringDataPoint( + sdf.format(currentHourStart), + successCount, + failCount + )); + } + + return result; + } catch (Exception e) { + log.error("[埋点监控] 查询小时数据失败", e); + return Collections.emptyList(); + } + } + + /** + * 获取指定状态的记录数量 + */ + private int getHourlyRecordCount(Date startHour, Date endHour, int status) { + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.ge(BuriedPointFailRecord::getCreateTime, startHour) + .lt(BuriedPointFailRecord::getCreateTime, endHour) + .eq(BuriedPointFailRecord::getStatus, status); + return buriedPointFailRecordMapper.selectCount(query).intValue(); + } + + /** + * 获取失败记录数量 + */ + private int getHourlyFailedCount(Date startHour, Date endHour) { + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.ge(BuriedPointFailRecord::getCreateTime, startHour) + .lt(BuriedPointFailRecord::getCreateTime, endHour) + .in(BuriedPointFailRecord::getStatus, + Arrays.asList(BuriedPointFailRecord.STATUS_UNPROCESSED, BuriedPointFailRecord.STATUS_FAILED)); + return buriedPointFailRecordMapper.selectCount(query).intValue(); + } + + /** + * 获取指定时间前N小时的时间 + */ + private Date getDateHoursAgo(Date date, int hours) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(Calendar.HOUR_OF_DAY, -hours); + return calendar.getTime(); + } +} \ No newline at end of file diff --git a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/service/feishu/FeiShuCardDataService.java b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/service/feishu/FeiShuCardDataService.java index 1cb7ea4..4c238cd 100644 --- a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/service/feishu/FeiShuCardDataService.java +++ b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/service/feishu/FeiShuCardDataService.java @@ -2,7 +2,6 @@ package com.tashow.cloud.app.service.feishu; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.tashow.cloud.sdk.feishu.client.FeiShuAlertClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -15,12 +14,14 @@ import java.util.concurrent.TimeUnit; /** * 飞书卡片数据处理服务 - * 负责卡片数据的存储和获取 */ @Service -public class FeiShuCardDataService implements FeiShuAlertClient.CardDataHandler { +public class FeiShuCardDataService { - private final Logger log = LoggerFactory.getLogger(FeiShuCardDataService.class); + private static final Logger log = LoggerFactory.getLogger(FeiShuCardDataService.class); + private static final String REDIS_KEY_PREFIX = "feishu:card:"; + private static final int CARD_EXPIRATION_DAYS = 30; + private final StringRedisTemplate stringRedisTemplate; private final ObjectMapper objectMapper; @@ -32,34 +33,41 @@ public class FeiShuCardDataService implements FeiShuAlertClient.CardDataHandler /** * 保存卡片数据到Redis - * @param messageId 消息ID - * @param data 卡片数据 */ - @Override - public void saveCardData(String messageId, Map data) { + public boolean saveCardData(String messageId, Map data) { + if (messageId == null || data == null) return false; + try { String jsonData = objectMapper.writeValueAsString(data); - stringRedisTemplate.opsForValue().set(messageId, jsonData, 30, TimeUnit.DAYS); - log.debug("卡片数据已保存到Redis, messageId: {}", messageId); + stringRedisTemplate.opsForValue().set( + REDIS_KEY_PREFIX + messageId, + jsonData, + CARD_EXPIRATION_DAYS, + TimeUnit.DAYS + ); + return true; } catch (JsonProcessingException e) { - log.error("保存卡片数据到Redis失败", e); + log.error("保存卡片数据失败: {}", e.getMessage()); + return false; } } /** * 从Redis获取卡片数据 - * @param messageId 消息ID - * @return 卡片数据 */ - @Override public Map getCardData(String messageId) { try { - String jsonData = stringRedisTemplate.opsForValue().get(messageId); - return objectMapper.readValue(jsonData, Map.class); + String redisKey = REDIS_KEY_PREFIX + messageId; + String jsonData = stringRedisTemplate.opsForValue().get(redisKey); + + if (jsonData == null) return new HashMap<>(); + + @SuppressWarnings("unchecked") + Map result = objectMapper.readValue(jsonData, Map.class); + return result; } catch (Exception e) { - throw new RuntimeException(e); - + log.error("获取卡片数据失败: {}", e.getMessage()); + return new HashMap<>(); } } - } \ No newline at end of file diff --git a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/service/impl/BuriedPointFailRecordService.java b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/service/impl/BuriedPointFailRecordService.java index f458aaa..ecd2006 100644 --- a/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/service/impl/BuriedPointFailRecordService.java +++ b/tashow-module/tashow-module-app/src/main/java/com/tashow/cloud/app/service/impl/BuriedPointFailRecordService.java @@ -44,25 +44,19 @@ public class BuriedPointFailRecordService implements MessageRetryService 0; + return buriedPointFailRecordMapper.updateById(record) > 0; } catch (Exception e) { - log.error("[埋点重试] 更新状态失败: {}", record.getCorrelationId(), e); + log.error("[埋点重试] 更新状态失败", e); return false; } } diff --git a/tashow-sdk/tashow-feishu-sdk/src/main/java/com/tashow/cloud/sdk/feishu/client/FeiShuAlertClient.java b/tashow-sdk/tashow-feishu-sdk/src/main/java/com/tashow/cloud/sdk/feishu/client/FeiShuAlertClient.java index f1e378c..b4cb488 100644 --- a/tashow-sdk/tashow-feishu-sdk/src/main/java/com/tashow/cloud/sdk/feishu/client/FeiShuAlertClient.java +++ b/tashow-sdk/tashow-feishu-sdk/src/main/java/com/tashow/cloud/sdk/feishu/client/FeiShuAlertClient.java @@ -9,7 +9,6 @@ import java.text.SimpleDateFormat; import java.util.*; import com.tashow.cloud.sdk.feishu.config.LarkConfig; import com.tashow.cloud.sdk.feishu.util.LarkClientUtil; -import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -25,32 +24,15 @@ public class FeiShuAlertClient { private final Client client; private final LarkConfig larkConfig; private final ChartImageGenerator chartImageGenerator; - public interface CardDataHandler { - /** - * 保存卡片数据 - * @param messageId 消息ID - * @param data 卡片数据 - */ - void saveCardData(String messageId, Map data); - /** - * 获取卡片数据 - * @param messageId 消息ID - * @return 卡片数据 - */ - Map getCardData(String messageId); - } - private CardDataHandler cardDataHandler; @Autowired public FeiShuAlertClient(LarkClientUtil larkClientUtil, LarkConfig larkConfig, - ChartImageGenerator chartImageGenerator, ObjectMapper objectMapper) { + ChartImageGenerator chartImageGenerator) { this.client = larkClientUtil.getLarkClient(); this.larkConfig = larkConfig; this.chartImageGenerator = chartImageGenerator; } - - /** * 创建报警群并拉人入群 * @@ -74,18 +56,6 @@ public class FeiShuAlertClient { return resp.getData().getChatId(); } - /** - * 发送埋点报警消息 - * - * @param chatId 会话ID - * @param buriedPointData 埋点数据 - * @param failCount 失败数量 - * @throws Exception 异常信息 - */ - public void sendBuriedPointAlertMessage(String chatId, List buriedPointData, int failCount) throws Exception { - sendBuriedPointAlertMessage(chatId, buriedPointData, failCount, null); - } - /** * 发送带错误信息的埋点报警消息 * @@ -93,9 +63,10 @@ public class FeiShuAlertClient { * @param buriedPointData 埋点数据 * @param failCount 失败数量 * @param errorMessage 错误信息 + * @return 发送的消息ID * @throws Exception 异常信息 */ - public void sendBuriedPointAlertMessage(String chatId, List buriedPointData, int failCount, String errorMessage) throws Exception { + public String sendBuriedPointAlertMessage(String chatId, List buriedPointData, int failCount, String errorMessage) throws Exception { HashMap templateData = new HashMap<>(); String imageKey = uploadImage(buriedPointData, errorMessage); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @@ -104,7 +75,7 @@ public class FeiShuAlertClient { templateData.put("image_key", imageKey); templateData.put("current_time", currentTime); templateData.put("fail_count", failCount); - sendCardMessage(chatId, "AAqdpjayeOVp2", templateData); + return sendCardMessage(chatId, "AAqdpjayeOVp2", templateData); } /** @@ -113,11 +84,13 @@ public class FeiShuAlertClient { * @param buriedPointData 埋点数据 * @param failCount 失败数量 * @param errorMessage 错误信息 + * @return 创建的群ID和消息ID,格式为 "chatId:messageId" * @throws Exception 异常信息 */ - public void sendBuriedPointAlertMessage(List buriedPointData, int failCount, String errorMessage) throws Exception { + public String sendBuriedPointAlertMessage(List buriedPointData, int failCount, String errorMessage) throws Exception { String chatId = createAlertChat(); - sendBuriedPointAlertMessage(chatId, buriedPointData, failCount, errorMessage); + String messageId = sendBuriedPointAlertMessage(chatId, buriedPointData, failCount, errorMessage); + return chatId + ":" + messageId; } /** @@ -214,47 +187,10 @@ public class FeiShuAlertClient { .build(); } - /** - * 构建埋点异常卡片 - * - * @param buttonName 按钮名称 - * @param buriedPointData 埋点数据 - * @param failCount 失败数量 - * @return 卡片JSON - * @throws Exception 异常信息 - */ - private String buildBuriedPointCard(String buttonName, List buriedPointData, int failCount) throws Exception { - return buildBuriedPointCard(buttonName, buriedPointData, failCount, null); - } + /** - * 构建埋点异常卡片(带错误信息) - * - * @param buttonName 按钮名称 - * @param buriedPointData 埋点数据 - * @param failCount 失败数量 - * @param errorMessage 错误信息 - * @return 卡片JSON - * @throws Exception 异常信息 - */ - private String buildBuriedPointCard(String buttonName, List buriedPointData, int failCount, String errorMessage) throws Exception { - String imageKey = uploadImage(buriedPointData, errorMessage); - // 获取当前时间 - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - String currentTime = sdf.format(new Date()); - - HashMap templateData = new HashMap<>(); - templateData.put("alert_title", "埋点数据异常告警"); - templateData.put("image_key", imageKey); - templateData.put("current_time", currentTime); - templateData.put("fail_count", failCount); - templateData.put("button_name", buttonName); - - return buildCardWithData("AAqdpjayeOVp2", templateData); - } - - /** - * 发送卡片消息并保存数据 + * 发送卡片消息 * * @param chatId 会话ID * @param templateId 卡片模板ID @@ -264,11 +200,6 @@ public class FeiShuAlertClient { */ public String sendCardMessage(String chatId, String templateId, Map templateData) throws Exception { String cardContent = buildCardWithData(templateId, templateData); - String messageId = sendMessage(chatId, "interactive", cardContent); - if (cardDataHandler != null && messageId != null) { - cardDataHandler.saveCardData(messageId, templateData); - } - - return messageId; + return sendMessage(chatId, "interactive", cardContent); } } diff --git a/tashow-sdk/tashow-feishu-sdk/src/main/java/com/tashow/cloud/sdk/feishu/client/FeiShuMessageClient.java b/tashow-sdk/tashow-feishu-sdk/src/main/java/com/tashow/cloud/sdk/feishu/client/FeiShuMessageClient.java index e2dd2a1..a0084b1 100644 --- a/tashow-sdk/tashow-feishu-sdk/src/main/java/com/tashow/cloud/sdk/feishu/client/FeiShuMessageClient.java +++ b/tashow-sdk/tashow-feishu-sdk/src/main/java/com/tashow/cloud/sdk/feishu/client/FeiShuMessageClient.java @@ -1,16 +1,13 @@ package com.tashow.cloud.sdk.feishu.client; import com.lark.oapi.Client; -import com.lark.oapi.core.utils.Jsons; import com.lark.oapi.service.im.v1.model.*; -import com.tashow.cloud.sdk.feishu.config.LarkConfig; import com.tashow.cloud.sdk.feishu.util.LarkClientUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; - -import java.io.*; -import java.io.File; -import java.text.SimpleDateFormat; import java.util.*; +import java.util.Arrays; /** * 飞书普通消息客户端 @@ -18,92 +15,73 @@ import java.util.*; */ @Service public class FeiShuMessageClient { - + private final Logger log = LoggerFactory.getLogger(FeiShuMessageClient.class); private final Client client; - private final LarkConfig larkConfig; + + @Autowired - public FeiShuMessageClient(LarkClientUtil larkClientUtil, LarkConfig larkConfig) { + public FeiShuMessageClient(LarkClientUtil larkClientUtil) { this.client = larkClientUtil.getLarkClient(); - this.larkConfig = larkConfig; } /** - * 发送文本消息 + * 发送通用消息 * @param chatId 会话ID - * @param text 消息文本 - * @return 发送结果 + * @param msgType 消息类型 + * @param content 消息内容(不同消息类型有不同的格式要求) + * @return 发送结果,包含消息ID和是否成功 * @throws Exception 异常信息 */ - public boolean sendTextMessage(String chatId, String text) throws Exception { - Map content = new HashMap<>(); - content.put("text", text); + public Map sendMessage(String chatId, String msgType, String content) throws Exception { CreateMessageReq req = CreateMessageReq.newBuilder() .receiveIdType("chat_id") .createMessageReqBody(CreateMessageReqBody.newBuilder() .receiveId(chatId) - .msgType("text") - .content(Jsons.DEFAULT.toJson(content)) + .msgType(msgType) + .content(content) .build()) .build(); + CreateMessageResp resp = client.im().message().create(req); - if (!resp.success()) { - System.out.println("发送失败原因: " + resp.getMsg() + ", 错误码: " + resp.getCode()); + Map result = new HashMap<>(); + result.put("success", resp.success()); + + if (resp.success()) { + result.put("messageId", resp.getData().getMessageId()); + } else { + result.put("errorCode", resp.getCode()); + result.put("errorMessage", resp.getMsg()); + result.put("requestId", resp.getRequestId()); + log.error("发送消息失败: 类型={}, 错误码={}, 错误信息={}", msgType, resp.getCode(), resp.getMsg()); } - return resp.success(); + + return result; } - /** - * 发送富文本消息 - * @param chatId 会话ID() - * @param title 标题 - * @param content 内容 - * @return 发送结果 - * @throws Exception 异常信息 - */ - public boolean sendPostMessage(String chatId, String title, String content) throws Exception { - // 正确的富文本消息格式 - String postJson = String.format("{\"zh_cn\":{\"title\":\"%s\",\"content\":[[{\"tag\":\"text\",\"text\":\"%s\"}]]}}", - title, content); - CreateMessageReq req = CreateMessageReq.newBuilder() - .receiveIdType("chat_id") - .createMessageReqBody(CreateMessageReqBody.newBuilder() - .receiveId(chatId) - .msgType("post") - .content(postJson) - .build()) - .build(); - CreateMessageResp resp = client.im().message().create(req); - if (!resp.success()) { - System.out.println("发送失败原因: " + resp.getMsg() + ", 错误码: " + resp.getCode()); - } - return resp.success(); - } /** * 获取会话历史消息 * @param chatId 会话ID + * @return 消息列表 * @throws Exception 异常信息 */ - public void listChatHistory(String chatId) throws Exception { - ListMessageReq req = ListMessageReq.newBuilder().containerIdType("chat").containerId(chatId).build(); + public List listChatHistory(String chatId) throws Exception { + ListMessageReq req = ListMessageReq.newBuilder() + .containerIdType("chat") + .containerId(chatId) + .build(); ListMessageResp resp = client.im().message().list(req); if (!resp.success()) { - throw new Exception(String.format("client.im.message.list failed, code: %d, msg: %s, logId: %s", resp.getCode(), resp.getMsg(), resp.getRequestId())); + throw new Exception(String.format("获取消息历史失败,错误码: %d, 错误信息: %s, 请求ID: %s", + resp.getCode(), resp.getMsg(), resp.getRequestId())); } - File file = new File("./src/main/java/com/larksuite/oapi/quick_start/robot/chat_history.txt"); - FileWriter writer = new FileWriter(file); - for (Message item : resp.getData().getItems()) { - String senderId = item.getSender().getId(); - String content = item.getBody().getContent(); - String createTime = item.getCreateTime(); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - createTime = sdf.format(new Date(Long.parseLong(createTime))); - writer.write(String.format("chatter(%s) at (%s) send: %s\n", senderId, createTime, content)); - } - writer.close(); + + return Arrays.asList(resp.getData().getItems()); } + + } \ No newline at end of file diff --git a/tashow-sdk/tashow-feishu-sdk/src/main/java/com/tashow/cloud/sdk/feishu/config/LarkConfig.java b/tashow-sdk/tashow-feishu-sdk/src/main/java/com/tashow/cloud/sdk/feishu/config/LarkConfig.java index f283442..d6b8559 100644 --- a/tashow-sdk/tashow-feishu-sdk/src/main/java/com/tashow/cloud/sdk/feishu/config/LarkConfig.java +++ b/tashow-sdk/tashow-feishu-sdk/src/main/java/com/tashow/cloud/sdk/feishu/config/LarkConfig.java @@ -31,5 +31,9 @@ public class LarkConfig { @Value("${lark.alert.user-open-ids}") private String[] alertUserOpenIds; + @Value("${lark.alert.exception-card}") + private String exceptionCards; + @Value("${lark.alert.success-card}") + private String successCards; } \ No newline at end of file