操作日志,SpringBoot+AOP实现用户操作日志的记录

  • Post author:
  • Post category:其他


内容不算原创,是自己东拼西凑整理的,大致框架和操作流程就是这样,需要细化的可以自己再修改

具体实现步骤

1、在pom.xml中添加AOP依赖

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

2、设计操作日志记录表

在这里插入图片描述

3、创建一个操作日志记录的注解


import java.lang.annotation.*;

/**
 * @author mei
 * @since 2021/9/23
 */
 //注解放置的目标位置即方法级别
@Target(ElementType.METHOD)
//注解在哪个阶段执行
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemLogAnnotation {

    String optModul() default ""; // 操作模块

    String optType() default "";  // 操作类型

    String optDesc() default "";  // 操作说明

    String optParam() default "";  // 参数

    String optUpdateParam() default "";  // 修改内容
}

4、创建操作日志的切面类,将操作日志数据保存到数据库


/**
 * 操作日志切面处理类
 * @author mei
 */
@Aspect
@Component
public class WebLogAspect {

	@Autowired
    SystemLogService systemLogService;//保存记录的表service

    /**
     * 注解的全类名
     */
    @Pointcut("@annotation(com.*.*.*.SystemLogAnnotation)")
    public void webLogPoinCut() {
    }


    /**
     * 记录操作日志
     * @param joinPoint 方法的执行点
     * @param result  方法返回值
     * @throws Throwable
     */
    @AfterReturning(returning  = "result", value = "webLogPoinCut()")
    public void saveOptLog(JoinPoint joinPoint, Object result) throws Throwable {
		//判断是否操作成功
        JSONObject jsonObject =  (JSONObject) JSONObject.toJSON(result);
        if(!"1".equals(jsonObject.getString("code"))){
            return;
        }

        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        try {

            SystemLog log = new SystemLog();
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            //获取切入点所在的方法
            Method method = signature.getMethod();
            //获取切入点所在的类名
            //String className=joinPoint.getTarget().getClass().getSimpleName();

            //获取操作
            SystemLogAnnotation annotation = method.getAnnotation(SystemLogAnnotation.class);
            //模块名称
            if (annotation != null) {
                //操作人{optuser}{增删改查}{原内容}{模块名}为{修改内容}
                //操作用户  填自己的
                String content = "userName";
                //操作类型
                if(StringUtils.isNotEmpty(annotation.optType())){
                    String optType = getOptType(method.getName());
                    content += "-" + optType;
                }else {
                    content += "-" + annotation.optType();
                }
                //内容
                if(StringUtils.isNotEmpty(getAnnotationValue(joinPoint,annotation.optParam()))){
                    content += "-" + getAnnotationValue(joinPoint,annotation.optParam());
                }
                //操作模块和操作说明
                content += "-" + annotation.optModul() + "-" + annotation.optDesc();
                //修改内容
                if(StringUtils.isNotEmpty(getAnnotationValue(joinPoint,annotation.optUpdateParam()))){
                    content += "为" + getAnnotationValue(joinPoint,annotation.optUpdateParam());
                }

                log.setContent(content);
            }

            //操作用户  填自己的
            log.setOptUser("userId");
            //客户端真实IP
            log.setLoginIp(ClientIPUtil.getClientIP(request));
            //保存日志
            systemLogService.save(log);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取注解中传递的动态参数的参数值
     *
     * @param joinPoint
     * @param name
     * @return
     */
    public String getAnnotationValue(JoinPoint joinPoint, String name) {
        String paramName = name;
        // 获取方法中所有的参数
        Map<String, Object> params = getParams(joinPoint);
        // 参数是否是动态的:#{paramName}
        if (paramName.matches("^#\\{\\D*\\}")) {
            // 获取参数名
            paramName = paramName.replace("#{", "").replace("}", "");
            // 是否是复杂的参数类型:对象.参数名
            if (paramName.contains(".")) {
                String[] split = paramName.split("\\.");
                // 获取方法中对象的内容
                Object object = getValue(params, split[0]);
                // 转换为JsonObject
                JSONObject jsonObject =  (JSONObject) JSONObject.toJSON(object);
                // 获取值
                Object o = jsonObject.get(split[1]);

                return String.valueOf(o);
            }
            // 简单的动态参数直接返回
            return String.valueOf(getValue(params, paramName));
        }
        // 非动态参数直接返回
        return name;
    }

    /**
     * 根据参数名返回对应的值
     *
     * @param map
     * @param paramName
     * @return
     */
    public Object getValue(Map<String, Object> map, String paramName) {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getKey().equals(paramName)) {
                return entry.getValue();
            }
        }
        return null;
    }


    /**
     * 获取方法的参数名和值
     *
     * @param joinPoint
     * @return
     */
    public Map<String, Object> getParams(JoinPoint joinPoint) {
        Map<String, Object> params = new HashMap<>(8);
        Object[] args = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] names = signature.getParameterNames();
        for (int i = 0; i < args.length; i++) {
            params.put(names[i], args[i]);
        }
        return params;
    }

    public String getOptType(String methodName){

        String method = "";

        switch (methodName){
            case "save":
            case "create":
                method = "新增";
                break;
            case "update":
            case "edit":
                method =  "修改";
                break;
            case "del":
            case "delete":
                method =  "删除";
                break;
            case "get":
            case "list":
                method =  "查询";
                break;
            default:
                method =  "";
                break;
        }
        return method;
    }

}

5、客户端真实IP

import javax.servlet.http.HttpServletRequest;


/**
 * @author mei
 */
public class ClientIPUtil {

    public static String getClientIP(HttpServletRequest request) {

        String ip = "";
        String ipAddresses = request.getHeader("X-Forwarded-For");

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
                   ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {

            ipAddresses = request.getHeader("X-Real-IP");
        }

        if (ipAddresses != null && ipAddresses.length() != 0) {
            ip = ipAddresses.split(",")[0];
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ip = request.getRemoteAddr();
        }

        return ip;
    }
}

6、实际操作-在controller层的方法上加入@SystemLogAnnotation注解

    @PostMapping
    @SystemLogAnnotation(optModul = "区域模块",optType = "创建",optParam = "#{po}",optDesc = "创建区域")
    public Result create(@RequestBody @Validated(value={ValidAdd.class}) Area po) {
        areaService.save(po);
        return RespUtil.success();
    }

    @DeleteMapping("/{id}")
    @SystemLogAnnotation(optModul = "区域模块",optType = "删除",optParam = "#{id}",optDesc = "删除区域")
    public Result delete(@PathVariable Long id) {
        areaService.removeById(id);
        return RespUtil.success();
    }

7、操作结果

在这里插入图片描述



版权声明:本文为weixin_38006002原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。