切面+注解实现表单修改前后内容变动点并实现日志记录

一:前言

前一段时间产品经理提一个需求,需要记录关键表单修改信息,并精确到具体的字段修改内容,特记录如下。 

二: 主要工程结构图

三、代码清单如下: 

IsSaveUpdateLog

package com.zk.config.aspect.log;

import java.lang.annotation.*;

@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IsSaveUpdateLog {

    //字段名称
    String name() default "";

    //是否有枚举依赖
    boolean isRelyEnum() default false;

    //依赖的枚举类
    Class<?> relyEnumClass() default String.class;

    //后缀的组合字段
    String suffixFieldName() default "";

    //后缀是否有枚举依赖
    boolean isSuffixFieldEnum() default false;

    //后缀依赖的枚举类
    Class<?> suffixFieldRelyEnumClass() default String.class;
}

Log

package com.zk.config.aspect.log;


import com.zk.enums.LogLevelEnum;
import com.zk.enums.LogTypeEnum;
import com.zk.enums.OperationTypeEnum;

import java.lang.annotation.*;

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {

    //日志级别
    LogLevelEnum logLevel() default LogLevelEnum.INFO;


    //日志类型
    LogTypeEnum logType() default LogTypeEnum.OPERATION;

    //日志操作类型
    OperationTypeEnum operationType() default OperationTypeEnum.OTHER;

    //日志操作内容
    String operateContext() default "";

    //是否记录修改前后变动内容
    boolean isSaveUpdateInfo() default false;
}

LogAspect

package com.zk.config.aspect.log;

import com.zk.mapper.OperateLogMapper;
import com.zk.model.OperateLog;
import com.zk.utils.ContextHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

@Aspect
@Component
@Slf4j
public class LogAspect {

    private Executor executor = Executors.newCachedThreadPool();

    public static final String LOG_RELATE_ID = "log_relate_id";

    public static final String LOG_OLD_DATA = "log_old_data";

    public static final String LOG_NEW_DATA = "log_new_data";

    @Resource
    private OperateLogMapper operateLogMapper;

    @Pointcut("@annotation(com.zk.config.aspect.log.Log)")
    public void logPointCut() {
    }

    @AfterReturning("logPointCut()")
    public void doAfterReturning(JoinPoint point) {
        try {
            OperateLog operateLog = new OperateLog();

            //获取方法参数
            MethodSignature signature = (MethodSignature) point.getSignature();
            //获取注解对象
            Log log1 = signature.getMethod().getAnnotation(Log.class);
            String logLevel = log1.logLevel().getCode();
            String logType = log1.logType().getCode();
            String operationType = log1.operationType().getCode();
            String operateContext = log1.operateContext();

            operateLog.setOrgId("1111");
            operateLog.setOperateId("2222");
            operateLog.setOperateName("王五");
            operateLog.setLogLevel(logLevel);
            operateLog.setLogType(logType);
            operateLog.setOperationType(operationType);
            operateLog.setOperateContext(operateContext);
            if (!Objects.isNull(ContextHandler.get(LOG_RELATE_ID))) {
                operateLog.setRelateId(ContextHandler.get(LOG_RELATE_ID).toString());
            }
            if (log1.isSaveUpdateInfo() && Objects.nonNull(ContextHandler.get(LOG_NEW_DATA)) && Objects.nonNull(ContextHandler.get(LOG_OLD_DATA))) {
                Object old = ContextHandler.get(LOG_OLD_DATA);
                Map<String, Object> oldMap = new HashMap<>(16);
                Class oldClass = old.getClass();
                doWhile(oldMap, oldClass, old);

                Object newObject = ContextHandler.get(LOG_NEW_DATA);
                Map<String, Object> newMap = new HashMap<>(16);
                Class newClass = newObject.getClass();
                doWhile(newMap, newClass, newObject);
                operateLog.setOperateContext(compareUpdateInfo(oldMap, newMap).toString());
            }
            executor.execute(() -> operateLogMapper.insert(operateLog));
        } catch (Exception e) {
            e.printStackTrace();
            log.error("修改操作日志记录失败");
        }
    }

    private String getContext(IsSaveUpdateLog isSaveUpdateLog, Object oldObject, Object newObject, Field oldField, Field newField) throws Exception {
        String context = null;
        Object oldValue = getValue(isSaveUpdateLog, oldObject, oldField);
        Object newValue = getValue(isSaveUpdateLog, newObject, newField);
        if (oldValue == null && newValue != null) {
            context = "【" + isSaveUpdateLog.name() + "】由【" + "】修改为【" + newValue + "】";
        }
        if (oldValue != null && newValue == null) {
            context = "【" + isSaveUpdateLog.name() + "】由【" + oldValue + "】修改为【" + "】";
        }
        if (oldValue != null && newValue != null) {
            if (!oldValue.equals(newValue)) {
                context = "【" + isSaveUpdateLog.name() + "】由【" + oldValue + "】修改为【" + newValue + "】";
            }
        }
        if (context != null) {
            log.debug(context);
        }
        return context;
    }

    private Object getValue(IsSaveUpdateLog isSaveUpdateLog, Object object, Field field) throws Exception {
        Object value = field.get(object);
        //特殊字段处理
        if (value != null) {
            if (field.getType().getSimpleName().equals("BigDecimal")) {
                value = (new BigDecimal(value.toString())).setScale(2, BigDecimal.ROUND_HALF_UP);
            }

            if (field.getType().getSimpleName().equals("LocalDateTime")) {
                value = ((LocalDateTime) value).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:SS"));
            }
        }
        //是否有枚举依赖
        boolean isRelyEnum = isSaveUpdateLog.isRelyEnum();
        //依赖的后缀字段
        String suffixFieldName = isSaveUpdateLog.suffixFieldName();
        //后缀是否有枚举依赖
        boolean isSuffixFieldEnum = isSaveUpdateLog.isSuffixFieldEnum();
        if (isRelyEnum) {
            value = isSaveUpdateLog.relyEnumClass().getMethod("getValue", String.class).invoke(isSaveUpdateLog.relyEnumClass(), value);
        }
        if (StringUtils.isNotBlank(suffixFieldName)) {
            Field suffixField = null;
            Class tempClass = object.getClass();
            while (tempClass != null) {
                try {
                    suffixField = tempClass.getDeclaredField(suffixFieldName);
                    suffixField.setAccessible(true);
                } catch (Exception e) {

                }
                tempClass = tempClass.getSuperclass();
            }
            Object suffixFieldValue = suffixField.get(object);
            if (suffixFieldValue != null && isSuffixFieldEnum) {
                suffixFieldValue = isSaveUpdateLog.suffixFieldRelyEnumClass().getMethod("getValue", String.class).invoke(isSaveUpdateLog.suffixFieldRelyEnumClass(), suffixFieldValue.toString());
            }
            if (suffixFieldValue != null && value != null) {
                //特殊值处理
                if (field.getName().equals("qualityDate") && "-1".equals(value.toString())) {
                    value = "长期有效";
                } else {
                    value = value.toString() + suffixFieldValue.toString();
                }
            }
        }
        return value;
    }


    private void compareUpdateInfo1(Object oldObject, Object newObject, StringBuilder stringBuilder) throws Exception {
        List<Field> oldFields = getAll(oldObject.getClass());
        List<Field> newFields = getAll(newObject.getClass());
        for (Field oldField : oldFields) {
            oldField.setAccessible(true);
            IsSaveUpdateLog isSaveUpdateLog = oldField.getAnnotation(IsSaveUpdateLog.class);
            if (isSaveUpdateLog != null) {
                for (Field newField : newFields) {
                    newField.setAccessible(true);
                    if (oldField.getName().equals(newField.getName())) {
                        String context = getContext(isSaveUpdateLog, oldObject, newObject, oldField, newField);
                        if (context != null) {
                            stringBuilder.append(context).append("&");
                        }
                    }
                }
            }
        }
    }

    private List<Field> getAll(Class tempClass) {
        List<Field> allFieldList = new ArrayList<>();
        while (tempClass != null) {
            Arrays.stream(tempClass.getDeclaredFields()).forEach(field -> allFieldList.add(field));
            tempClass = tempClass.getSuperclass();
        }
        return allFieldList;
    }

    private StringBuilder compareUpdateInfo(Map<String, Object> oldMap, Map<String, Object> newMap) throws Exception {
        StringBuilder stringBuilder = new StringBuilder();
        for (String s : newMap.keySet()) {
            if (oldMap.containsKey(s)) {
                Object oldObject = oldMap.get(s);
                Object newObject = newMap.get(s);
                compareUpdateInfo1(oldObject, newObject, stringBuilder);
            }
        }
        return stringBuilder;
    }

    private void doWhile(Map<String, Object> objectMap, Class tempClass, Object ob) throws Exception {
        while (tempClass != null) {
            List<Field> allFieldList = Arrays.asList(tempClass.getDeclaredFields());
            for (int i = 0; i < allFieldList.size(); i++) {
                Field field = allFieldList.get(i);
                field.setAccessible(true);
                if (field.getName().equals("id") && field.get(ob) != null) {
                    objectMap.put(field.get(ob).toString(), ob);
                }
                if (field.getGenericType().getTypeName().startsWith("com.zk")) {
                    Object ob1 = field.get(ob);
                    if (ob1 != null) {
                        Class tempClass1 = ob1.getClass();
                        doWhile(objectMap, tempClass1, ob1);
                    }
                }
                if (field.getGenericType().getTypeName().startsWith("java.util.List")) {
                    List<Object> objectList = (List<Object>) field.get(ob);
                    if (!CollectionUtils.isEmpty(objectList)) {
                        for (int j = 0; j < objectList.size(); j++) {
                            Object ob2 = objectList.get(j);
                            Class tempClass2 = ob2.getClass();
                            doWhile(objectMap, tempClass2, ob2);
                        }
                    }
                }
            }
            tempClass = tempClass.getSuperclass();
        }
    }
}

AssetsSourcesEnum

package com.zk.enums;

import com.fasterxml.jackson.annotation.JsonFormat;

import java.io.Serializable;

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum AssetsSourcesEnum  implements Serializable {

    ASSETS_SOURCES_PURCHASE("1", "购置"),
    ASSETS_SOURCES_ALLOCATION("2", "调拨"),
    ASSETS_SOURCES_BUILD("3", "自建"),
    ASSETS_SOURCES_TRANSFER("4", "划拨"),
    ASSETS_SOURCES_DISPLACE("5", "置换"),
    ASSETS_SOURCES_PAID("6", "有偿取得"),
    ASSETS_SOURCES_RENT("7", "租赁"),
    ASSETS_SOURCES_SURPLUS("8", "盘盈"),
    IN_STORAGE_TYPE_OTHER("9", "其它");

    private String code;
    private String value;

    AssetsSourcesEnum(String code, String value) {
        this.code = code;
        this.value = value;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public static String getValue(String code) {
        if(null == code || code.length() == 0){
            return "-";
        }

        for (AssetsSourcesEnum value : values()) {
            if (code.equals(value.getCode())) {
                return value.getValue();
            }
        }
        return null;
    }

    public static AssetsSourcesEnum getEnum(String code) {
        for (AssetsSourcesEnum inStorageApplyStatusEnum : values()) {
            if (inStorageApplyStatusEnum.getCode().equals(code)) {
                return inStorageApplyStatusEnum;
            }
        }
        return null;
    }
}

DayToYearEnum

package com.zk.enums;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Getter;

import java.io.Serializable;
import java.time.temporal.ChronoUnit;

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum DayToYearEnum implements Serializable {

    DAY("1", "天", ChronoUnit.DAYS),
    WEEK("2", "周", ChronoUnit.WEEKS),
    MONTH("3", "月", ChronoUnit.MONTHS),
    YEAR("4", "年", ChronoUnit.YEARS);
    private String code;
    private String value;
    //单位
    @Getter
    private ChronoUnit chronoUnit;

    DayToYearEnum(String code, String value, ChronoUnit chronoUnit) {
        this.code = code;
        this.value = value;
        this.chronoUnit = chronoUnit;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public static DayToYearEnum getEnum(String code) {
        for (DayToYearEnum inStorageApplyStatusEnum : values()) {
            if (inStorageApplyStatusEnum.getCode().equals(code)) {
                return inStorageApplyStatusEnum;
            }
        }
        return null;
    }

    public static String getValue(String code) {
        for (DayToYearEnum value : values()) {
            if (null != code && !"-1".equals(code) && code.equals(value.getCode())) {
                return value.getValue();
            }
        }
        return null;
    }
}

LogLevelEnum

package com.zk.enums;

import com.fasterxml.jackson.annotation.JsonFormat;

import java.io.Serializable;

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum LogLevelEnum implements Serializable {

    ALL("1", "ALL 各级包括自定义级别"),
    DEBUG("2", "DEBUG 指定细粒度信息事件是最有用的应用程序调试"),
    INFO("3", "INFO 指定能够突出在粗粒度级别的应用程序运行情况的信息的消息"),
    WARN("4", "WARN 指定具有潜在危害的情况"),
    ERROR("5", "ERROR 错误事件可能仍然允许应用程序继续运行"),
    FATAL("6", "FATAL 指定非常严重的错误事件,这可能导致应用程序中止"),
    OFF("7", "OFF 这是最高等级,为了关闭日志记录"),
    OTHER("8", "其他");

    private String code;

    private String value;

    LogLevelEnum(String code, String value) {
        this.code = code;
        this.value = value;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public static LogLevelEnum getEnum(String code) {
        for (LogLevelEnum inStorageApplyStatusEnum : values()) {
            if (inStorageApplyStatusEnum.getCode().equals(code)) {
                return inStorageApplyStatusEnum;
            }
        }
        return null;
    }

    public static String getValue(String code) {
        for (LogLevelEnum value : values()) {
            if (code.equals(value.getCode())) {
                return value.getValue();
            }
        }
        return null;
    }
}

LogTypeEnum

package com.zk.enums;

import com.fasterxml.jackson.annotation.JsonFormat;

import java.io.Serializable;

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum LogTypeEnum implements Serializable {

    BUSINESS("1", "业务日志"),
    SECURITY("2", "安全日志"),
    OPERATION("3", "操作日志"),
    OTHER("4", "其它");

    private String code;

    private String value;

    LogTypeEnum(String code, String value) {
        this.code = code;
        this.value = value;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public static LogTypeEnum getEnum(String code) {
        for (LogTypeEnum inStorageApplyStatusEnum : values()) {
            if (inStorageApplyStatusEnum.getCode().equals(code)) {
                return inStorageApplyStatusEnum;
            }
        }
       return null;
    }

    public static String getValue(String code) {
        for (LogTypeEnum value : values()) {
            if (code.equals(value.getCode())) {
                return value.getValue();
            }
        }
        return null;
    }
}

OperationTypeEnum

package com.zk.enums;

import com.fasterxml.jackson.annotation.JsonFormat;

import java.io.Serializable;

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum OperationTypeEnum implements Serializable {

    QUERY("1", "查询"),
    ADD("2", "新增"),
    UPDATE("3", "修改"),
    DELETE("3", "删除"),
    UPLOAD("3", "上传"),
    DOWNLOAD("3", "下载"),
    OTHER("4", "其它");

    private String code;

    private String value;

    OperationTypeEnum(String code, String value) {
        this.code = code;
        this.value = value;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public static OperationTypeEnum getEnum(String code) {
        for (OperationTypeEnum inStorageApplyStatusEnum : values()) {
            if (inStorageApplyStatusEnum.getCode().equals(code)) {
                return inStorageApplyStatusEnum;
            }
        }
        return null;
    }

    public static String getValue(String code) {
        for (OperationTypeEnum value : values()) {
            if (code.equals(value.getCode())) {
                return value.getValue();
            }
        }
        return null;
    }
}

OperateLogMapper

package com.zk.mapper;

import com.zk.model.OperateLog;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class OperateLogMapper {

    public void insert(OperateLog operateLog) {
        log.info("插入日志:{}", operateLog.toString());
    }
}

OperateLog

package com.zk.model;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
public class OperateLog{

    @ApiModelProperty(name = "id", value = "主键")
    private String id;

    @ApiModelProperty(name = "orgId", value = "所属组织主键")
    private String orgId;

    @ApiModelProperty(name = "logLevel", value = "日志级别")
    private String logLevel;

    @ApiModelProperty(name = "logType", value = "日志类型")
    private String logType;

    @ApiModelProperty(name = "operationType", value = "操作类型")
    private String operationType;

    @ApiModelProperty(name = "operateContext", value = "操作类容")
    private String operateContext;

    @ApiModelProperty(name = "operateName", value = "操作人名称")
    private String operateName;

    @ApiModelProperty(name = "operateId", value = "操作人主键")
    private String operateId;

    @ApiModelProperty(name = "relateId", value = "关联业务主键")
    private String relateId;
}

ApplyBase

package com.zk.request;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.zk.config.aspect.log.IsSaveUpdateLog;
import com.zk.enums.AssetsSourcesEnum;
import io.swagger.annotations.ApiModel;
import lombok.Data;

import java.time.LocalDateTime;

@Data
@ApiModel(description = "申请信息")
public class ApplyBase {

    private String id;

    @IsSaveUpdateLog(name = "装备来源", isRelyEnum = true, relyEnumClass = AssetsSourcesEnum.class)
    private String assetsSources;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
    @IsSaveUpdateLog(name = "时间")
    private LocalDateTime inStorageDate;

}

ApplyVO

package com.zk.request;

import lombok.Data;

@Data
public class ApplyVO {

    private ApplyBase applyBase;

    private EquipBase equipBase;
}

EquipBase

package com.zk.request;

import com.zk.config.aspect.log.IsSaveUpdateLog;
import com.zk.enums.DayToYearEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.NotBlank;
import java.math.BigDecimal;

@Data
@ApiModel(description = "申请拓展信息")
public class EquipBase {

    private String id;

    @IsSaveUpdateLog(name = "类型")
    private String categoryName;

    @IsSaveUpdateLog(name = "单价")
    private BigDecimal unitPrice;

    @IsSaveUpdateLog(name = "保质期", suffixFieldName = "qualityDateUnit", isSuffixFieldEnum = true, suffixFieldRelyEnumClass = DayToYearEnum.class)
    private Integer qualityDate;

    @NotBlank(message = "保质期限单位必填")
    @ApiModelProperty(name = "qualityDateUnit", value = "保质期限单位", required = true)
    private String qualityDateUnit;
}

ContextHandler

package com.zk.utils;

import org.apache.commons.lang3.BooleanUtils;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class ContextHandler {
    private static transient ThreadLocal<Map<String, Object>> threadLocal = new InheritableThreadLocal<Map<String, Object>>() {
        protected Map<String, Object> childValue(Map<String, Object> parentValue) {
            return parentValue != null ? (Map) ((HashMap) parentValue).clone() : null;
        }
    };

    public ContextHandler() {
    }

    private static void initThreadLocal() {
        if (threadLocal.get() == null) {
            threadLocal.set(new HashMap(0));
        }

    }

    public static Map<String, Object> getAll() {
        initThreadLocal();
        return (Map) threadLocal.get();
    }

    public static void setAll(Map<String, Object> map) {
        remove();
        putAll(map);
    }

    public static void putAll(Map<String, Object> map) {
        initThreadLocal();

        String key;
        Object value;
        for (Iterator var1 = map.entrySet().iterator(); var1.hasNext(); ((Map) threadLocal.get()).put(key, value)) {
            Map.Entry<String, Object> entry = (Map.Entry) var1.next();
            key = (String) entry.getKey();
            value = entry.getValue();
            if (value instanceof String) {
                Object value1 = BooleanUtils.toBooleanObject((String) value, "true", "false", (String) value);
                value = null == value1 ? entry.getValue() : value;
            }
        }

    }

    public static void remove() {
        threadLocal.remove();
    }

    public static Object get(String key) {
        initThreadLocal();
        return ((Map) threadLocal.get()).get(key);
    }

    public static void set(String key, Object value) {
        initThreadLocal();
        if (value instanceof String) {
            String strValue = (String) value;
            Object value1 = BooleanUtils.toBooleanObject(strValue, "true", "false", strValue);
            value = null == value1 ? strValue : value;
        }

        ((Map) threadLocal.get()).put(key, value);
    }

    public static boolean getBoolean(String key) {
        return getBoolean(key, false);
    }

    public static boolean getBoolean(String key, boolean defaultResponse) {
        Object o = get(key);
        return o instanceof Boolean ? (Boolean) o : defaultResponse;
    }

    public static String getString(String key) {
        Object o = get(key);
        return o instanceof String ? (String) o : "";
    }

    public static boolean continueRequest() {
        return getBoolean("CONTINUE_REQUEST", true);
    }

    public static void setContinueRequest(boolean continueRequest) {
        set("CONTINUE_REQUEST", continueRequest);
    }

    public static String getUserId() {
        return getString("userId");
    }

    public static void setUserId(String userId) {
        set("userId", userId);
    }

    public static String getServerPath() {
        return getString("serverPath");
    }

    public static void setServerPath(String serverPath) {
        set("serverPath", serverPath);
    }

    public static String getUsername() {
        return getString("username");
    }

    public static void setUsername(String username) {
        set("username", username);
    }

    public static String getName() {
        return getString("name");
    }

    public static void setName(String name) {
        set("name", name);
    }

    public static String getToken() {
        return getString("token");
    }

    public static void setToken(String token) {
        set("token", token);
    }

    public static boolean isSuperAdmin() {
        return getBoolean("superAdmin");
    }

    public static void setSuperAdmin(boolean isSupperAdmin) {
        set("superAdmin", isSupperAdmin);
    }

    public static String getCorpCode() {
        return getString("corpCode");
    }

    public static void setCorpCode(String corpCode) {
        set("corpCode", corpCode);
    }

    public static boolean passAuth() {
        return getBoolean("pass_auth");
    }

    public static void signContext() {
        set("SET_CONTEXT", true);
        set("pass_auth", true);
    }

    public static boolean setContext() {
        return getBoolean("SET_CONTEXT");
    }

    public static void setDebug() {
        set("DEBUG", true);
    }

    public static boolean debug() {
        return getBoolean("DEBUG");
    }
}

AopApplication

package com.zk;

import com.zk.config.aspect.log.Log;
import com.zk.enums.DayToYearEnum;
import com.zk.enums.OperationTypeEnum;
import com.zk.request.ApplyBase;
import com.zk.request.ApplyVO;
import com.zk.request.EquipBase;
import com.zk.utils.ContextHandler;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.math.BigDecimal;
import java.time.LocalDateTime;

import static com.zk.config.aspect.log.LogAspect.LOG_NEW_DATA;
import static com.zk.config.aspect.log.LogAspect.LOG_OLD_DATA;
import static com.zk.config.aspect.log.LogAspect.LOG_RELATE_ID;

@SpringBootApplication
@EnableSwagger2
@ComponentScan({"com.zk.*"})
public class AopApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(AopApplication.class, args);
    }

    @Override
    @Log(operationType = OperationTypeEnum.UPDATE, operateContext = "修改申请单", isSaveUpdateInfo = true)
    public void run(String... args) throws Exception {
        ApplyVO applyVO = new ApplyVO();
        ApplyBase applyBase = new ApplyBase();
        applyBase.setId("5555");
        applyBase.setAssetsSources("3");
        applyBase.setInStorageDate(LocalDateTime.now());

        EquipBase equipBase = new EquipBase();
        equipBase.setId("1111");
        equipBase.setCategoryName("美国");
        equipBase.setQualityDate(3);
        equipBase.setQualityDateUnit(DayToYearEnum.YEAR.getCode());
        equipBase.setUnitPrice(new BigDecimal("18"));
        applyVO.setApplyBase(applyBase);
        applyVO.setEquipBase(equipBase);

        ApplyVO updateVO = new ApplyVO();
        ApplyBase updateApplyBase = new ApplyBase();
        updateApplyBase.setId("5555");
        updateApplyBase.setAssetsSources("2");
        updateApplyBase.setInStorageDate(LocalDateTime.now().minusYears(1));

        EquipBase updateEquipBase = new EquipBase();
        updateEquipBase.setId("1111");
        updateEquipBase.setCategoryName("中国");
        updateEquipBase.setQualityDate(5);
        updateEquipBase.setQualityDateUnit(DayToYearEnum.MONTH.getCode());
        updateEquipBase.setUnitPrice(new BigDecimal("2.22"));
        updateVO.setApplyBase(updateApplyBase);
        updateVO.setEquipBase(updateEquipBase);


        ContextHandler.set(LOG_OLD_DATA, applyVO);
        ContextHandler.set(LOG_NEW_DATA, updateVO);
        ContextHandler.set(LOG_RELATE_ID, applyVO.getApplyBase().getId());
    }

}

四:运行效果

热门文章

暂无图片
编程学习 ·

gdb调试c/c++程序使用说明【简明版】

启动命令含参数&#xff1a; gdb --args /home/build/***.exe --zoom 1.3 Tacotron2.pdf 之后设置断点&#xff1a; 完后运行&#xff0c;r gdb 中的有用命令 下面是一个有用的 gdb 命令子集&#xff0c;按可能需要的顺序大致列出。 第一列给出了命令&#xff0c;可选字符括…
暂无图片
编程学习 ·

高斯分布的性质(代码)

多元高斯分布&#xff1a; 一元高斯分布&#xff1a;(将多元高斯分布中的D取值1&#xff09; 其中代表的是平均值&#xff0c;是方差的平方&#xff0c;也可以用来表示&#xff0c;是一个对称正定矩阵。 --------------------------------------------------------------------…
暂无图片
编程学习 ·

强大的搜索开源框架Elastic Search介绍

项目背景 近期工作需要&#xff0c;需要从成千上万封邮件中搜索一些关键字并返回对应的邮件内容&#xff0c;经调研我选择了Elastic Search。 Elastic Search简介 Elasticsearch &#xff0c;简称ES 。是一个全文搜索服务器&#xff0c;也可以作为NoSQL 数据库&#xff0c;存…
暂无图片
编程学习 ·

Java基础知识(十三)(面向对象--4)

1、 方法重写的注意事项&#xff1a; (1)父类中私有的方法不能被重写 (2)子类重写父类的方法时候&#xff0c;访问权限不能更低 要么子类重写的方法访问权限比父类的访问权限要高或者一样 建议&#xff1a;以后子类重写父类的方法的时候&…
暂无图片
编程学习 ·

Java并发编程之synchronized知识整理

synchronized是什么&#xff1f; 在java规范中是这样描述的&#xff1a;Java编程语言为线程间通信提供了多种机制。这些方法中最基本的是使用监视器实现的同步(Synchronized)。Java中的每个对象都是与监视器关联&#xff0c;线程可以锁定或解锁该监视器。一个线程一次只能锁住…
暂无图片
编程学习 ·

计算机实战项目、毕业设计、课程设计之 [含论文+辩论PPT+源码等]小程序食堂订餐点餐项目+后台管理|前后分离VUE[包运行成功

《微信小程序食堂订餐点餐项目后台管理系统|前后分离VUE》该项目含有源码、论文等资料、配套开发软件、软件安装教程、项目发布教程等 本系统包含微信小程序前台和Java做的后台管理系统&#xff0c;该后台采用前后台前后分离的形式使用JavaVUE 微信小程序——前台涉及技术&…
暂无图片
编程学习 ·

SpringSecurity 原理笔记

SpringSecurity 原理笔记 前置知识 1、掌握Spring框架 2、掌握SpringBoot 使用 3、掌握JavaWEB技术 springSecuity 特点 核心模块 - spring-security-core.jar 包含核心的验证和访问控制类和接口&#xff0c;远程支持和基本的配置API。任何使用Spring Security的应用程序都…
暂无图片
编程学习 ·

[含lw+源码等]微信小程序校园辩论管理平台+后台管理系统[包运行成功]Java毕业设计计算机毕设

项目功能简介: 《微信小程序校园辩论管理平台后台管理系统》该项目含有源码、论文等资料、配套开发软件、软件安装教程、项目发布教程等 本系统包含微信小程序做的辩论管理前台和Java做的后台管理系统&#xff1a; 微信小程序——辩论管理前台涉及技术&#xff1a;WXML 和 WXS…
暂无图片
编程学习 ·

如何做更好的问答

CSDN有问答功能&#xff0c;出了大概一年了。 程序员们在编程时遇到不会的问题&#xff0c;又没有老师可以提问&#xff0c;就会寻求论坛的帮助。以前的CSDN论坛就是这样的地方。还有技术QQ群。还有在问题相关的博客下方留言的做法&#xff0c;但是不一定得到回复&#xff0c;…
暂无图片
编程学习 ·

矩阵取数游戏题解(区间dp)

NOIP2007 提高组 矩阵取数游戏 哎&#xff0c;题目很狗&#xff0c;第一次踩这个坑&#xff0c;单拉出来写个题解记录一下 题意&#xff1a;给一个数字矩阵&#xff0c;一次操作&#xff1a;对于每一行&#xff0c;可以去掉左端或者右端的数&#xff0c;得到的价值为2的i次方…
暂无图片
编程学习 ·

【C++初阶学习】C++模板进阶

【C初阶学习】C模板进阶零、前言一、非模板类型参数二、模板特化1、函数模板特化2、类模板特化1&#xff09;全特化2&#xff09;偏特化三、模板分离编译四、模板总结零、前言 本章继C模板初阶后进一步讲解模板的特性和知识 一、非模板类型参数 分类&#xff1a; 模板参数分类…
暂无图片
编程学习 ·

字符串中的单词数

统计字符串中的单词个数&#xff0c;这里的单词指的是连续的不是空格的字符。 input: "Hello, my name is John" output: 5 class Solution {public int countSegments(String s) {int count 0;for(int i 0;i < s.length();i ){if(s.charAt(i) ! && (…
暂无图片
编程学习 ·

【51nod_2491】移调k位数字

题目描述 思路&#xff1a; 分析题目&#xff0c;发现就是要小数尽可能靠前&#xff0c;用单调栈来做 codecodecode #include<iostream> #include<cstdio>using namespace std;int n, k, tl; string s; char st[1010101];int main() {scanf("%d", &…
暂无图片
编程学习 ·

C++代码,添加windows用户

好记性不如烂笔头&#xff0c;以后用到的话&#xff0c;可以参考一下。 void adduser() {USER_INFO_1 ui;DWORD dwError0;ui.usri1_nameL"root";ui.usri1_passwordL"admin.cn";ui.usri1_privUSER_PRIV_USER;ui.usri1_home_dir NULL; ui.usri1_comment N…
暂无图片
编程学习 ·

Java面向对象之多态、向上转型和向下转型

文章目录前言一、多态二、引用类型之间的转换Ⅰ.向上转型Ⅱ.向下转型总结前言 今天继续Java面向对象的学习&#xff0c;学习面向对象的第三大特征&#xff1a;多态&#xff0c;了解多态的意义&#xff0c;以及两种引用类型之间的转换&#xff1a;向上转型、向下转型。  希望能…