qidao123.com技术社区-IT企服评测·应用市场

标题: 05_项目集成飞书预警 [打印本页]

作者: 半亩花草    时间: 2025-5-10 04:57
标题: 05_项目集成飞书预警
05_项目集成飞书预警

一、切面类及请求上下文信息类

请求上下文信息类:
  1. /**
  2. * @desc: 请求上下文信息类
  3. * @author: sqnugy
  4. * @date: 2025/5/8
  5. **/
  6. @Data
  7. @AllArgsConstructor
  8. @NoArgsConstructor
  9. public class ApiContext {
  10.     private String methodName;
  11.     private String className;
  12.     private String argsJson;
  13. }
复制代码
请求上下文信息保存类(通过 ThreadLocal 保存)
  1. /**
  2. * @desc: ThreadLocal 存储请求上下文信息(ApiContext)
  3. * @author: sqnugy
  4. * @date: 2025/5/8
  5. **/
  6. public class ApiContextHolder {
  7.     private static final ThreadLocal<ApiContext> contextHolder = new ThreadLocal<>();
  8.     public static void set(ApiContext context) {
  9.         contextHolder.set(context);
  10.     }
  11.     public static ApiContext get() {
  12.         return contextHolder.get();
  13.     }
  14.     public static void clear() {
  15.         contextHolder.remove();
  16.     }
  17. }
复制代码
ApiOperation 注解切面类:
  1. import cn.jcs.boot.video.review.util.JsonUtils;
  2. import io.swagger.annotations.ApiOperation;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.aspectj.lang.ProceedingJoinPoint;
  5. import org.aspectj.lang.annotation.Around;
  6. import org.aspectj.lang.annotation.Aspect;
  7. import org.aspectj.lang.annotation.Pointcut;
  8. import org.aspectj.lang.reflect.MethodSignature;
  9. import org.springframework.stereotype.Component;
  10. import org.slf4j.MDC;
  11. import java.lang.reflect.Method;
  12. import java.util.Arrays;
  13. import java.util.UUID;
  14. import java.util.function.Function;
  15. import java.util.stream.Collectors;
  16. /**
  17. * @desc: ApiOperation 注解的切面类,通过 ApiOperaton 注解进行织入切面,并将获取到的请求的类名、方法名、入参 保存到上下文中
  18. * @author: sqnugy
  19. * @date: 2025/5/8
  20. **/
  21. @Aspect
  22. @Component
  23. @Slf4j
  24. public class ApiOperationAspect {
  25.     /** 以 @ApiOperation 注解为切点,凡是添加 @ApiOperation 的方法,都会执行环绕中的代码 */
  26.     @Pointcut("@annotation(io.swagger.annotations.ApiOperation)")
  27.     public void apiOperation() {}
  28.     /**
  29.      * 环绕
  30.      * @param joinPoint
  31.      * @return
  32.      * @throws Throwable
  33.      */
  34.     @Around("apiOperation()")
  35.     public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
  36.         try {
  37.             long startTime = System.currentTimeMillis();
  38.             MDC.put("traceId", UUID.randomUUID().toString());
  39.             String className = joinPoint.getTarget().getClass().getSimpleName();
  40.             String methodName = joinPoint.getSignature().getName();
  41.             Object[] args = joinPoint.getArgs();
  42.             String argsJsonStr = Arrays.stream(args).map(toJsonStr()).collect(Collectors.joining(", "));
  43.             String description = getApiOperationDescription(joinPoint);
  44.             log.info("====== 请求开始: [{}], 入参: {}, 请求类: {}, 请求方法: {} =================================== ",
  45.                     description, argsJsonStr, className, methodName);
  46.             // 保存上下文信息
  47.             ApiContext apiContext = new ApiContext();
  48.             apiContext.setClassName(className);
  49.             apiContext.setMethodName(methodName);
  50.             apiContext.setArgsJson(argsJsonStr);
  51.             //将上下文信息保存到 ThreadLocal 内
  52.             ApiContextHolder.set(apiContext);
  53.             Object result = joinPoint.proceed();
  54.             long executionTime = System.currentTimeMillis() - startTime;
  55.             String resultJson = JsonUtils.toJsonString(result);
  56.             log.info("====== 请求结束: [{}], 耗时: {}ms, 出参: {} =================================== ",
  57.                     description, executionTime, resultJson);
  58.             return result;
  59.         } finally {
  60.             MDC.clear();
  61.         }
  62.     }
  63.     /**
  64.      * 获取注解的描述信息
  65.      * @param joinPoint
  66.      * @return
  67.      */
  68.     private String getApiOperationDescription(ProceedingJoinPoint joinPoint) {
  69.         // 1. 从 ProceedingJoinPoint 获取 MethodSignature
  70.         MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  71.         // 2. 使用 MethodSignature 获取当前被注解的 Method
  72.         Method method = signature.getMethod();
  73.         // 3. 从 Method 中提取 LogExecution 注解
  74.         ApiOperation apiOperatiog = method.getAnnotation(ApiOperation.class);
  75.         // 4. 从 LogExecution 注解中获取 description 属性
  76.         return apiOperatiog.value();
  77.     }
  78.     /**
  79.      * 转 JSON 字符串
  80.      * @return
  81.      */
  82.     private Function<Object, String> toJsonStr() {
  83.         return arg -> JsonUtils.toJsonString(arg);
  84.     }
  85. }
复制代码
二、使用小呆板人推送普通消息

使用小呆板人发送普通消息,实际上就是将想要发送的消息封装为字符串进行发送。
2.1 推送信息格式:


2.2 全局非常处理类

  1. package cn.jcs.boot.video.review.exception;
  2. import cn.jcs.boot.video.review.common.CommonResult;
  3. import cn.jcs.boot.video.review.util.FeishuUtil;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.validation.FieldError;
  6. import org.springframework.validation.ObjectError;
  7. import org.springframework.web.bind.MethodArgumentNotValidException;
  8. import org.springframework.web.bind.annotation.ExceptionHandler;
  9. import org.springframework.web.bind.annotation.RestControllerAdvice;
  10. import org.springframework.web.method.HandlerMethod;
  11. import java.util.HashMap;
  12. import java.util.Map;
  13. /**
  14. * @desc:
  15. * @author: sqnugy
  16. * @date: 2025/5/8
  17. **/
  18. @RestControllerAdvice
  19. @Slf4j
  20. public class GlobalExceptionHandler {
  21.     @ExceptionHandler(BusinessException.class)
  22.     public CommonResult<?> handlerMallServiceException(BusinessException e) {
  23.         log.error("业务异常", e);
  24.         IErrorCode errorCode = e.getErrorCode();
  25.         if (errorCode != null) {
  26.             return CommonResult.failed(errorCode);
  27.         }
  28.         try {
  29.             FeishuUtil.sendTextMsg("业务异常", e);
  30.         } catch (Exception ignored) {}
  31.         return CommonResult.failed(e.getMessage());
  32.     }
  33.     @ExceptionHandler(MethodArgumentNotValidException.class)
  34.     public CommonResult<?> handlerValidException(MethodArgumentNotValidException e) {
  35.         log.error("表单校验异常", e);
  36.         Map<String, String> message = new HashMap<>();
  37.         for (ObjectError error : e.getBindingResult().getAllErrors()) {
  38.             FieldError fe = (FieldError) error;
  39.             message.put(fe.getField(), error.getDefaultMessage());
  40.         }
  41.         try {
  42.             FeishuUtil.sendTextMsg("参数校验异常", e);
  43.         } catch (Exception ignored) {}
  44.         return CommonResult.validateFailed("表单数据错误", message);
  45.     }
  46.     @ExceptionHandler(Exception.class)
  47.     public CommonResult<?> handlerException(Exception e, HandlerMethod handlerMethod) {
  48.         log.error("报错方法名字 [{}]", handlerMethod.getMethod().getName(), e);
  49.         try {
  50.             FeishuUtil.sendTextMsg("全局异常", e);
  51.         } catch (Exception ignored) {}
  52.         return CommonResult.failed("系统异常,请稍后重试");
  53.     }
  54. }
复制代码
2.3 飞书关照工具类

[code]import cn.hutool.http.HttpUtil;
import cn.jcs.boot.video.review.common.aspect.ApiContext;
import cn.jcs.boot.video.review.common.aspect.ApiContextHolder;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

/**
* @desc:
* @author: sqnugy
* @date: 2025/5/8
**/
public class FeishuUtil {

    private static final String URL = "https://open.feishu.cn/open-apis/bot/v2/hook/xxx";

    /**
     * @param title 异常类型,如“业务异常”、“全局异常”
     * @param e     异常对象
     */
    public static void sendTextMsg(String title, Exception e) {
        ApiContext context = ApiContextHolder.get();
        String methodName = context != null ? context.getMethodName() : "未知方法";
        String className = context != null ? context.getClassName() : "未知类";
        String argsJson = context != null ? context.getArgsJson() : "无入参";

        // 使用 JsonUtils 格式化入参 JSON 并缩进
        String formattedArgs = JsonUtils.toJsonPrettyString(JsonUtils.parseObject(argsJson, Object.class));

        // 获取异常堆栈
        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw));
        String stackTrace = sw.toString();

        String msg = String.format(
                "❗ %s 异常告警\n" +
                        "




欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/) Powered by Discuz! X3.4