@Log注解实现接口操作日志保存

 1.日志注解类@Log

package com.dstcar.entitys.sys;

import com.dstcar.common.constant.BusinessModelEnum;
import com.dstcar.common.constant.BusinessTypeEnum;

import java.lang.annotation.*;

/**
 * 包名路径: com.dstcar.entitys.sys
 * 功能说明:
 * 开发人员:liu wei ping
 * 开发时间:2022年08月05日 09:29
 * 修改记录:修改日期 修改人员 修改说明
 */
@Target({ ElementType.METHOD ,ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /**
     * 模块
     */
//    BusinessModelEnum businessModel() default BusinessModelEnum.OTHER;

    /**
     * 模块_常量
     * @return
     */
    String businessModelConstant() default "OTHER";

    /**
     * 功能
     */
    BusinessTypeEnum businessType() default BusinessTypeEnum.NORMAL;

    /**
     * 功能_常量
     * @return
     */
    String businessTypeConstant() default "NORMAL";

    /**
     * 备注
     */
    String remark() default "";

    /**
     * 是否保存请求的参数
     */
    boolean isSaveRequestData() default true;
}

2.业务操作日志切面类

package com.dstcar.common.log;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import com.dstcar.common.constant.LogConstant;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.dstcar.common.base.ApiController;
import com.dstcar.common.base.Results;
import com.dstcar.common.constant.BusinessModelEnum;
import com.dstcar.common.constant.BusinessStatusEnum;
import com.dstcar.common.constant.BusinessTypeEnum;
import com.dstcar.common.utils.http.NetworkUtil;
import com.dstcar.common.utils.string.StringUtil;
import com.dstcar.entitys.sso.SSOUser;
import com.dstcar.entitys.sys.Log;
import com.dstcar.entitys.sys.LogResult;
import com.dstcar.entitys.sys.SysOperLog;

import lombok.extern.slf4j.Slf4j;

/**
 * 包名路径: com.dstcar.common.utils.log 功能说明: 开发人员:liu wei ping
 * 开发时间:2022年08月15日 14:02
 * 修改记录:修改日期 修改人员 修改说明
 */
@Slf4j
@Aspect
@Component
public class SysOperLogAspect extends ApiController {

	@Autowired
	SysOperLogService sysOperLogService;

	@Pointcut("@annotation(com.dstcar.entitys.sys.Log)")
	public void logPointCut() {
	}

	/**
	 * 处理完请求后执行
	 *
	 * @param joinPoint 切点
	 */
	@AfterReturning(pointcut = "logPointCut()", returning = "results")
	public void doAfterReturning(JoinPoint joinPoint, Object results) {
		handleLog(joinPoint, null, results, null);
	}

	/**
	 * 作用于所有类
	 * 
	 * @param joinPoint
	 * @param l
	 */
	@AfterReturning(pointcut = "@within(l)", returning = "results")
	public void bindTypeAnno(JoinPoint joinPoint, Log l, Object results) {
		if (results != null && results instanceof Results && ((Results) 
             results).getLogResult() != null
				&& getAnnotationLog(joinPoint) == null) {
			LogResult logResult = ((Results) results).getLogResult();
			if (!CollectionUtils.isEmpty(logResult.getLogResultList())
					|| !StringUtil.isEmpty(logResult.getBusinessId()))
				handleLog(joinPoint, null, results, l);
		}
	}

	/**
	 * 拦截异常操作
	 *
	 * @param joinPoint
	 * @param e
	 */
	@AfterThrowing(value = "logPointCut()", throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
		handleLog(joinPoint, e, null, null);
	}

	protected void handleLog(final JoinPoint joinPoint, final Exception e, Object results, Log l) {
		// 获取当前的用户
		SSOUser currentUser = null;
		try {
			currentUser = getCurrentUser();
		} catch (Exception e1) {
//            log.error("切面获取token异常捕获"); 屏蔽登录错误日志
		}

		// 日志
		SysOperLog sysOperLog = new SysOperLog();
		boolean isFlag = true;
		try {
			String operIp = NetworkUtil.getIpAddress(request); // 记录ip
			sysOperLog.setOperIp(operIp);
			sysOperLog.setOperUrl(request.getServletPath());
			sysOperLog.setOperLocation(operIp);
		} catch (Exception e1) {
			sysOperLog.setOperIp("127.0.0.1");
			sysOperLog.setOperUrl("/job");
			sysOperLog.setOperLocation("127.0.0.1");
			isFlag = false;
		}

		try {
			if (l == null) {
				l = getAnnotationLog(joinPoint);
			}
			// 获得注解
			Log controllerLog = l;
			if (controllerLog == null) {
				return;
			}

			sysOperLog.setStatus(BusinessStatusEnum.SUCCESS.name());

			// 处理设置注解上的参数
			getControllerMethodDescription(controllerLog, sysOperLog);

			// 操作人员
			sysOperLog.setOperNameId("1");
			sysOperLog.setOperName("系统管理员");
			if (currentUser != null) {
				sysOperLog.setOperNameId(getCurrentUserId() + "");
				sysOperLog.setOperName(getOperationUserName());
			}

			// 设置方法名称
			String className = joinPoint.getTarget().getClass().getName();
			String methodName = joinPoint.getSignature().getName();
			sysOperLog.setMethod(className + "." + methodName + "()");

			// 异常
			if (e != null) {
				sysOperLog.setStatus(BusinessStatusEnum.FAIL.name());
				String error = ExceptionUtils.getStackTrace(e);
				sysOperLog.setErrorMsg(StringUtil.substring(error, 0, 2000));
				log.error("[模块:{}]-[方法:{}]-[发生异常:{}]", sysOperLog.getModel(), sysOperLog.getMethod(),
						sysOperLog.getErrorMsg());
				return; // 不保存
			}

			// 业务
			Boolean flag = false;
			if (results != null && controllerLog.businessType().getKey().equals(BusinessTypeEnum.NORMAL.getKey())) {
				if (results instanceof Results) {
					Results temp = (Results) results;
					if (temp.getLogResult() != null) {
						LogResult logResult = temp.getLogResult();
						// 单条日志
						setSysOperLog(sysOperLog, logResult);
						// 批量日志
						setSysOperLogList(sysOperLog, logResult);
						flag = true;
					}
				}
				if (results instanceof LogResult) {
					LogResult logResult = (LogResult) results;

					// 单条日志
					setSysOperLog(sysOperLog, logResult);

					if (!isNull(logResult.getBusinessModel())) {
						sysOperLog.setModel(logResult.getBusinessModel());
					}

					// 批量日志
					setSysOperLogList(sysOperLog, logResult);
					flag = true;
				}
			}

			if (flag) {
				// 保存数据
//            AsyncManager.me().execute(AsyncFactory.recordOper(sysOperLog));
				sysOperLogService.addSysLog(sysOperLog);
			}
		} catch (Exception exp) {
			// 记录本地异常日志
			log.error("异常信息:{}", exp.getMessage());
			exp.printStackTrace();
		}
	}

	/**
	 * 单条
	 * 
	 * @param sysOperLog
	 */
	private void setSysOperLog(SysOperLog sysOperLog, LogResult logResult) {
		sysOperLog.setBusinessId(logResult.getBusinessId());
		sysOperLog.setRemark(logResult.getRemark());

		// 操作人及操作人id
		if (!isNull(logResult.getOperName())) {
			sysOperLog.setOperName(logResult.getOperName());
		}
		if (!isNull(logResult.getOperNameId())) {
			sysOperLog.setOperNameId(logResult.getOperNameId());
		}
	}

	/**
	 * 批量
	 */
	private void setSysOperLogList(SysOperLog sysOperLog, LogResult logResult) {
		List<LogResult> logResultList = logResult.getLogResultList();
		if (logResultList != null && logResultList.size() > 0) {
			List<SysOperLog> sysOperLogList = new ArrayList<>();
			for (int i = 0; i < logResultList.size(); i++) {
				SysOperLog temp = new SysOperLog();
				BeanUtils.copyProperties(sysOperLog, temp);
				LogResult log = logResultList.get(i);
				temp.setBusinessId(log.getBusinessId());
				temp.setRemark(log.getRemark());
				if (!isNull(log.getBusinessModel())) {
					temp.setModel(log.getBusinessModel());
				}
				sysOperLogList.add(temp);
			}
			sysOperLog.setSysOperLogList(sysOperLogList);
		}
	}

	/**
	 * 是否存在注解,如果存在就获取
	 */
	private Log getAnnotationLog(JoinPoint joinPoint) {
		Signature signature = joinPoint.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Method method = methodSignature.getMethod();
		if (method != null) {
			return method.getAnnotation(Log.class);
		}

		return null;
	}

	/**
	 * 获取注解中对方法的描述信息 用于Controller层注解
	 *
	 * @param log     日志
	 * @param operLog 操作日志
	 * @throws Exception
	 */
	private void getControllerMethodDescription(Log log, SysOperLog operLog) {
		// 设置模块
		operLog.setModel(log.businessModelConstant());
		// 业务
		operLog.setBusinessType(log.businessType().getKey());
		// 备注
		operLog.setRemark(log.remark());

		// 设置模块_使用常量
		if (!LogConstant.OTHER.equals(log.businessModelConstant())) {
			operLog.setModel(log.businessModelConstant());
		}

		// 设置业务_使用常量
		if (!BusinessTypeEnum.NORMAL.getKey().equals(log.businessTypeConstant())) {
			operLog.setBusinessType(log.businessTypeConstant());
		}
	}
}

3.操作日志执行发送mq

package com.dstcar.common.log;

import com.alibaba.fastjson.JSONObject;
import com.dstcar.apis.base.log.SysOperLogApi;
import com.dstcar.common.mq.constant.QueueExchangeContant;
import com.dstcar.entitys.sys.SysOperLog;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 包名路径: com.dstcar.common.utils.log
 * 功能说明:
 * 开发人员:liu wei ping
 * 开发时间:2022年08月15日 13:57
 * 修改记录:修改日期 修改人员 修改说明
 */
@Slf4j
@Service
public class SysOperLogService {

	@Autowired
	private SysOperLogApi sysOperLogApi;

	@Autowired
	AmqpTemplate amqpTemplate;

	@Value("-${spring.profiles.active}")
	private String active;

	@Async
	public void addSysLog(SysOperLog sysOperLog) {
//        AsyncManager.me().execute(AsyncFactory.recordOper(sysOperLog));
		Message message = MessageBuilder
				.withBody(JSONObject.toJSONBytes(sysOperLog))
				.setContentType(MessageProperties.CONTENT_TYPE_JSON)
				.build();
		amqpTemplate.convertAndSend(QueueExchangeContant.BASIC_BASE_LOG_QUEUE + active, 
        message);
	}

	public List<SysOperLog> selectList(SysOperLog sysOperLog) {
		return sysOperLogApi.selectList(sysOperLog);
	}

	public PageInfo<SysOperLog> selectListPage(SysOperLog sysOperLog) {
		return sysOperLogApi.selectListPage(sysOperLog);

	}
}

4.在Controller方法上加上@Log注解即可

@OSSExport(moduleName = "车辆档案",fileName = "车辆档案工单",dataUrl = "/operatingVehicleArchive/task/vehicleArchiveExportTask")
    @Log(businessModelConstant = BusinessModelConstant.VEHICLE_ITEM_EXPORT, businessType = BusinessTypeEnum.EXPORT, remark =
            "车辆档案工单导出")
    @GetMapping(value = "/vehicleArchiveExport")
    public Results vehicleArchiveExport(VehicleArchiveVO archiveVO) {
        return succeed();
    }

 5.日志表表结构

CREATE TABLE `sys_oper_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '日志主键',
  `model` varchar(100) DEFAULT '' COMMENT '操作模块(BusinessModelEnum)',
  `business_id` varchar(64) DEFAULT '' COMMENT '业务id(表id)',
  `business_type` varchar(100) DEFAULT '' COMMENT '业务类型(BusinessTypeEnum)',
  `oper_name` varchar(100) DEFAULT '' COMMENT '操作人员',
  `oper_name_id` varchar(20) NOT NULL DEFAULT '' COMMENT '操作人员id',
  `oper_url` varchar(255) DEFAULT '' COMMENT '请求URL',
  `oper_ip` varchar(50) DEFAULT '' COMMENT '主机地址',
  `oper_location` varchar(255) DEFAULT '' COMMENT '操作地点',
  `remark` mediumtext COMMENT '备注信息',
  `oper_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `id` (`id`) USING BTREE,
  KEY `index` (`model`,`business_id`,`business_type`,`oper_name_id`)
) ENGINE=InnoDB AUTO_INCREMENT=108625 DEFAULT CHARSET=utf8mb4 COMMENT='操作日志记录';

6.效果如图: