content: package com.xiliu.server.common.aspect;
  
  import cn.hutool.extra.servlet.ServletUtil;
  import com.alibaba.fastjson2.JSON;
  import com.xiliu.common.annotation.Log;
  import com.xiliu.common.utils.ServletUtils;
  import com.xiliu.common.utils.StringUtils;
  import com.xiliu.common.utils.ip.IpUtils;
  import com.xiliu.server.common.domain.LoginUser;
  import com.xiliu.server.modules.system.entity.XlOperLog;
  import com.xiliu.server.modules.system.service.IXlOperLogService;
  import com.xiliu.server.utils.SecurityUtils;
  import lombok.extern.slf4j.Slf4j;
  import org.aspectj.lang.JoinPoint;
  import org.aspectj.lang.annotation.AfterReturning;
  import org.aspectj.lang.annotation.Aspect;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.http.HttpMethod;
  import org.springframework.stereotype.Component;
  import org.springframework.validation.BindingResult;
  import org.springframework.web.multipart.MultipartFile;
  import org.springframework.web.servlet.HandlerMapping;
  
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import java.util.Collection;
  import java.util.Date;
  import java.util.Map;
  
  /**
   * @author xiliu
   * @description 操作日志切面类
   * @date 2023/3/10
   */
  @Aspect
  @Component
  @Slf4j
  public class LogAspect {
  
      @Autowired
      private IXlOperLogService operLogService;
  
      /**
       * 处理完请求后执行
       * @param joinPoint 切点
       */
      @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
      public void doAfterReturnibng(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
          handleLog(joinPoint, controllerLog, null, jsonResult);
      }
  
      protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
          try {
              // 获取当前的用户
              LoginUser loginUser = SecurityUtils.getLoginUser();
  
              // 日志记录
              XlOperLog operLog = new XlOperLog();
              operLog.setStatus(0);
              // 请求的IP地址
              String iP = ServletUtil.getClientIP(ServletUtils.getRequest());
              //String iP = IpUtils.getIpAddr(ServletUtils.getRequest());
              if ("0:0:0:0:0:0:0:1".equals(iP)) {
                  iP = "127.0.0.1";
              }
              operLog.setOperIp(iP);
              operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
              if (loginUser != null) {
                  operLog.setOperName(loginUser.getUsername());
              }
              if (e != null) {
                  operLog.setStatus(1);
                  operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
              }
              // 设置方法名称
              String className = joinPoint.getTarget().getClass().getName();
              String methodName = joinPoint.getSignature().getName();
              operLog.setMethod(className + "." + methodName + "()");
              operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
              operLog.setOperTime(new Date());
              // 处理设置注解上的参数
              getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
              // 保存数据库
              operLogService.save(operLog);
  
          } catch (Exception exp) {
              log.error("异常信息:{}", exp.getMessage());
              exp.printStackTrace();
          }
      }
  
      /**
       * 获取注解中对方法的描述信息 用于Controller层注解
       * @param log 日志
       * @param operLog 操作日志
       * @throws Exception
       */
      public void getControllerMethodDescription(JoinPoint joinPoint, Log log, XlOperLog operLog, Object jsonResult) throws Exception {
          // 设置操作业务类型
          operLog.setBusinessType(log.businessType().ordinal());
          // 设置标题
          operLog.setTitle(log.title());
          // 是否需要保存request，参数和值
          if (log.isSaveRequestData()) {
              // 设置参数的信息
              setRequestValue(joinPoint, operLog);
          }
          // 是否需要保存response，参数和值
          if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) {
              operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
          }
      }
  
      /**
       * 获取请求的参数，放到log中
       * @param operLog 操作日志
       * @throws Exception 异常
       */
      private void setRequestValue(JoinPoint joinPoint, XlOperLog operLog) throws Exception {
          String requsetMethod = operLog.getRequestMethod();
          if (HttpMethod.PUT.name().equals(requsetMethod) || HttpMethod.POST.name().equals(requsetMethod)) {
              String parsams = argsArrayToString(joinPoint.getArgs());
              operLog.setOperParam(StringUtils.substring(parsams,0,2000));
          } else {
              Map<?,?> paramsMap = (Map<?,?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
              operLog.setOperParam(StringUtils.substring(paramsMap.toString(),0,2000));
          }
      }
  
      /**
       * 参数拼装
       */
      private String argsArrayToString(Object[] paramsArray) {
          String params = "";
          if (paramsArray != null && paramsArray.length > 0) {
              for (Object object : paramsArray) {
                  // 不为空 并且是不需要过滤的 对象
                  if (StringUtils.isNotNull(object) && !isFilterObject(object)) {
                      Object jsonObj = JSON.toJSON(object);
                      params += jsonObj.toString() + " ";
                  }
              }
          }
          return params.trim();
      }
  
      /**
       * 判断是否需要过滤的对象。
       * @param object 对象信息。
       * @return 如果是需要过滤的对象，则返回true；否则返回false。
       */
      @SuppressWarnings("rawtypes")
      public boolean isFilterObject(final Object object) {
          Class<?> clazz = object.getClass();
          if (clazz.isArray()) {
              return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
          } else if (Collection.class.isAssignableFrom(clazz)) {
              Collection collection = (Collection) object;
              for (Object value : collection) {
                  return value instanceof MultipartFile;
              }
          } else if (Map.class.isAssignableFrom(clazz)) {
              Map map = (Map) object;
              for (Object value : map.entrySet()) {
                  Map.Entry entry = (Map.Entry) value;
                  return entry.getValue() instanceof MultipartFile;
              }
          }
          return object instanceof MultipartFile || object instanceof HttpServletRequest
                  || object instanceof HttpServletResponse || object instanceof BindingResult;
      }
  }
  