用AOP如何实现自定义注解去实现日志功能。

这一节,去讲解用AOP如何实现自定义注解去实现日志功能。代码过多,这里就是分享一下解决思路和流程,不会写太多注释。


前言

这一节更多的是AOP自定义注解实现日志功能思路上面的分享,不需要过度关注我的业务,因为我们的具体业务场景不同。

这里还是写一个场景吧,不然大家脑海里面没有印象!!!。
在这里插入图片描述
点击之后,我们能看到详细的操作内容!!!
在这里插入图片描述
就类似于这样的应用场景。


一、代码实现

表设计

@author CSDN_风中无我

CREATE TABLE operation_log (
`id` INT(10) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '主键id',
`name` VARCHAR(128) NULL DEFAULT NULL COMMENT '操作业务名',
`table_name` VARCHAR(16) NULL DEFAULT NULL COMMENT '操作表名',
`table_id` VARCHAR(16) NULL DEFAULT NULL COMMENT '操作表id',
`type` VARCHAR(8) NULL DEFAULT NULL COMMENT '操作类型,(添加ADD,删除DELETE,修改UPDATE)' ,
`operator_id` VARCHAR(16) NULL DEFAULT NULL COMMENT '操作人id',
`operator_name` VARCHAR(16) NULL DEFAULT NULL COMMENT '操作人名',
`operation_time` TIMESTAMP NULL DEFAULT NULL COMMENT '操作时间'
)ENGINE INNODB CHARSET utf8 COMMENT '用户操作日志记录表';
	
CREATE TABLE operation_log_detail (
`id` INT(10) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '主键id',
`operation_log_id` INT(10) NULL DEFAULT NULL COMMENT '操作日志id',
`clm_name` VARCHAR(16) NULL DEFAULT NULL COMMENT '字段名',
`clm_comment` VARCHAR(128) NULL DEFAULT NULL COMMENT '字段描述',
`old_string` VARCHAR(128) NULL DEFAULT NULL COMMENT '旧值',
`new_string` VARCHAR(128) NULL DEFAULT NULL COMMENT '新值'
)ENGINE INNODB CHARSET utf8 COMMENT '操作日志详情表';

AOP实现目标: 在业务代码函数上使用注解,通过注解实现执行时的环形切面,在切面前,切面后,做数据的变更记录操作。

1、创建自定义注解。✅✅✅

/**
 * 用来标注需要进行操作日志的服务函数上
 * @author CSDN_风中无我
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
	/** 业务名 */
	String name();
	/** 表名 */
	String table();
	/** id 在函数的字段名 */
	int idRef() default -1; 
	/** 需要记录的字段 */
	String[] cloum() default {};
	/** 操作类型 */
	OperationType type();
	/** 操作人 id 在函数的字段名*/
	int operatorIdRef();
	/** 操作人名称 在函数的字段名 */
	int operatorNameRef();
}

由于使用了一个枚举下面提供一个枚举,作用是分辨操作类型
public enum OperationType {
	ADD,
	UPDATE,
	DELETE;

	public String getType() {
		if (this.equals(ADD)) {
			return "ADD";
		}
		if (this.equals(UPDATE)) {
			return "UPDATE";
		}
		if (this.equals(DELETE)) {
			return "DELETE";
		}
		return null;
	};
}

2、使用注解,只是提前看看使用效果。✅✅✅


    @OperationLog(name = "更新账户",type = OperationType.UPDATE,operatorIdRef = 0,operatorNameRef = 1,idRef = 2,table = "account")
    public void updateAccount(String operatorId,String operatorName,Integer accountId){
        Account account = new Account();
        account.setId(accountId);
        account.setAccount(1100);
        accountMapper.updateAccount(account);
    }

3、下面开始实现AOP切面。✅✅✅

/**
@author CSDN_风中无我
这段代码是一个使用Spring AOP(面向方面编程)和事务管理来记录操作日志的示例。
通过拦截特定的注解,实现对数据库操作的日志记录。
*/


@Aspect
@Component
public class OperationLogAop {
    
  
	@Autowired
    //用于操作日志的数据库映射。
	private OperationLogMapper operationLogMapper;
    
	@Autowired
   //用于操作日志详细信息的数据库映射。
	private OperationLogDetailMapper operationLogDetailMapper;
    
	@Autowired
    //Spring提供的事务模板,用于管理事务。
	private TransactionTemplate txTemplate;

    /**logAround: 这是一个环绕通知方法,拦截被@OperationLog注解标记的方法,根据操作类型(增加、删除、更新)调用相应的日志记录方法。
    ⚠️ ⚠️ ⚠️ 注意哦!!!你可以去使用后置通知、返回后通知、抛出异常后通知等等,其他的类型的通知类型。根据具体业务场景去选择。我这里只使用了环绕通知。
    🚩🚩🚩后置通知(After advice):后置通知是在目标方法执行之后无论方法是否成功返回或者抛出异常都会执行的一种通知。这种通知可以用来进行资源清理等工作,不关心目标方法的返回结果。
    🚩🚩🚩返回后通知(After returning advice)返回后通知是在目标方法成功返回结果之后执行的一种通知。如果目标方法抛出异常,返回后通知不会执行。这种通知可以访问目标方法的返回值,适合对返回值进行处理或记录。
    🚩🚩🚩抛出异常后通知,当通知方法在原始切入点方法运行抛出异常后执行。
    注意区别!!!选择自己适用的类型。
    */
	@Around(value = "@annotation(operationlog)")
	public void logAround(final ProceedingJoinPoint p,final com.csp.operationlog.aspect.annotation.OperationLog operationlog) throws Throwable {
		OperationType type = operationlog.type();
        
        //如果是更新,去调用更新的相关业务逻辑。
		if (OperationType.UPDATE.equals(type)) {
			update(p, operationlog);
		}
        //如果是添加,去调用添加的相关业务逻辑。
		if (OperationType.ADD.equals(type)) {
			add(p, operationlog);
		}
        //如果是删除,去调用删除的相关业务逻辑。
		if (OperationType.DELETE.equals(type)) {
			delete(p, operationlog);
		}
	}
    
    
	/**
	delete 方法 用于记录删除操作的日志。
	主要的作用:获取操作的相关信息(表名、操作人ID、操作人名称等),构建SQL查询语句,获取被删除记录的详细信息。
	          然后执行实际的删除操作,并记录日志和日志详情。
        */
	public void delete(final ProceedingJoinPoint p, final com.csp.operationlog.aspect.annotation.OperationLog operationlog) {
    txTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            // 获取相关信息
            StringBuilder sql = new StringBuilder();
            OperationType type = operationlog.type();
            Object[] args = p.getArgs();
            String logName = operationlog.name();
            String logTable = operationlog.table();
            if (operationlog.idRef() == -1) {
                throw new RuntimeException();
            }
            String id = args[operationlog.idRef()].toString();
            String[] cloum = operationlog.cloum();
            String operatorId = args[operationlog.operatorIdRef()].toString();
            String operatorName = args[operationlog.operatorNameRef()].toString();

            // 获取列注释信息
            Map<String, Object> columnCommentMap = new HashMap<>();
            List<ColumnComment> columnCommentList = operationLogMapper.selectColumnCommentByTable(logTable);
            for (ColumnComment cc : columnCommentList) {
                columnCommentMap.put(cc.getColumn(), cc.getComment());
            }
            if (cloum.length == 0) {
                Set<String> keySet = columnCommentMap.keySet();
                List<String> list = new ArrayList<>();
                for (String o : keySet) {
                    list.add(o);
                }
                cloum = list.toArray(new String[0]);
            }

            // 构建查询语句
            sql.append("SELECT ");
            for (int i = 0; i < cloum.length; i++) {
                if (i == 0) {
                    sql.append("`").append(cloum[i]).append("` ");
                } else {
                    sql.append(",`").append(cloum[i]).append("` ");
                }
            }
            sql.append(" FROM ").append(logTable).append(" WHERE id=").append(id);
            Map<String, Object> oldMap = operationLogMapper.selectAnyTalbe(sql.toString());

            try {
                p.proceed();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }

            if (oldMap != null) {
                // 记录操作日志
                OperationLog op = new OperationLog();
                op.setName(logName);
                op.setTableName(logTable);
                op.setTableId(id);
                op.setType(type.getType());
                op.setOperatorId(operatorId);
                op.setOperatorName(operatorName);
                op.setOperationTime(new Timestamp(System.currentTimeMillis()));
                operationLogMapper.insertOperationLog(op);

                // 记录操作日志详情
                List<OperationLogDetail> opds = new ArrayList<>();
                for (String clm : cloum) {
                    Object oldclm = oldMap.get(clm);
                    OperationLogDetail opd = new OperationLogDetail();
                    opd.setOldString(oldclm.toString());
                    opd.setNewString("");
                    opd.setClmName(clm);
                    opd.setClmComment(columnCommentMap.get(clm).toString());
                    opd.setOperationLogId(op.getId());
                    opds.add(opd);
                }
                if (!opds.isEmpty()) {
                    operationLogDetailMapper.insertOperationLogDetail(opds);
                }
            }
        }
    });
}


    
    
    
    
    	/**
	add 方法 用于记录添加操作的日志。
	主要的作用:获取操作的相关信息(表名、操作人ID、操作人名称等),构建SQL查询语句,获取新插入记录的详细信息。
			  然后执行实际的添加操作,并记录日志和日志详情。
        */
  private void add(final ProceedingJoinPoint p, final com.csp.operationlog.aspect.annotation.OperationLog operationlog) {
    txTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            // 获取相关信息
            StringBuilder sql = new StringBuilder();
            OperationType type = operationlog.type();
            Object[] args = p.getArgs();
            String logName = operationlog.name();
            String logTable = operationlog.table();
            String[] cloum = operationlog.cloum();
            String operatorId = args[operationlog.operatorIdRef()].toString();
            String operatorName = args[operationlog.operatorNameRef()].toString();

            // 获取列注释信息
            Map<String, Object> columnCommentMap = new HashMap<>();
            List<ColumnComment> columnCommentList = operationLogMapper.selectColumnCommentByTable(logTable);
            for (ColumnComment cc : columnCommentList) {
                columnCommentMap.put(cc.getColumn(), cc.getComment());
            }
            if (cloum.length == 0) {
                Set<String> keySet = columnCommentMap.keySet();
                List<String> list = new ArrayList<>();
                for (String o : keySet) {
                    list.add(o);
                }
                cloum = list.toArray(new String[0]);
            }

            // 构建查询语句
            sql.append("SELECT ");
            for (int i = 0; i < cloum.length; i++) {
                if (i == 0) {
                    sql.append("`").append(cloum[i]).append("` ");
                } else {
                    sql.append(",`").append(cloum[i]).append("` ");
                }
            }
            sql.append(" FROM ").append(logTable).append(" ORDER BY id DESC LIMIT 1");
            Map<String, Object> oldMap = operationLogMapper.selectAnyTalbe(sql.toString());

            try {
                p.proceed();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }

            Map<String, Object> newMap = operationLogMapper.selectAnyTalbe(sql.toString());
            if (oldMap == null || !oldMap.get("id").toString().equals(newMap.get("id").toString())) {
                // 记录操作日志
                OperationLog op = new OperationLog();
                op.setName(logName);
                op.setTableName(logTable);
                op.setTableId("");
                op.setType(type.getType());
                op.setOperatorId(operatorId);
                op.setOperatorName(operatorName);
                op.setOperationTime(new Timestamp(System.currentTimeMillis()));
                operationLogMapper.insertOperationLog(op);

                // 记录操作日志详情
                List<OperationLogDetail> opds = new ArrayList<>();
                for (String clm : cloum) {
                    Object oldclm = "";
                    Object newclm = newMap.get(clm);
                    OperationLogDetail opd = new OperationLogDetail();
                    opd.setOldString(oldclm.toString());
                    opd.setNewString(newclm.toString());
                    opd.setClmName(clm);
                    opd.setClmComment(columnCommentMap.get(clm).toString());
                    opd.setOperationLogId(op.getId());
                    opds.add(opd);
                }
                if (!opds.isEmpty()) {
                    operationLogDetailMapper.insertOperationLogDetail(opds);
                }
            }
        }
    });
}


    
       /**
	update 方法 用于记录更新操作的日志。
	主要的作用:获取操作的相关信息(表名、操作人ID、操作人名称等),构建SQL查询语句,获取被更新记录的详细信息。
			  然后执行实际的添加操作,并记录日志和日志详情。
        */
public void update(final ProceedingJoinPoint p, final com.csp.operationlog.aspect.annotation.OperationLog operationlog) {
    txTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            // 获取相关信息
            StringBuilder sql = new StringBuilder();
            OperationType type = operationlog.type();
            Object[] args = p.getArgs();
            String logName = operationlog.name();
            String logTable = operationlog.table();
            if (operationlog.idRef() == -1) {
                throw new RuntimeException();
            }
            String id = args[operationlog.idRef()].toString();
            String[] cloum = operationlog.cloum();
            String operatorId = args[operationlog.operatorIdRef()].toString();
            String operatorName = args[operationlog.operatorNameRef()].toString();

            // 获取列注释信息
            Map<String, Object> columnCommentMap = new HashMap<>();
            List<ColumnComment> columnCommentList = operationLogMapper.selectColumnCommentByTable(logTable);
            for (ColumnComment cc : columnCommentList) {
                columnCommentMap.put(cc.getColumn(), cc.getComment());
            }
            if (cloum.length == 0) {
                Set<String> keySet = columnCommentMap.keySet();
                List<String> list = new ArrayList<>();
                for (String o : keySet) {
                    list.add(o);
                }
                cloum = list.toArray(new String[0]);
            }

            // 构建查询语句
            sql.append("SELECT ");
            for (int i = 0; i < cloum.length; i++) {
                if (i == 0) {
                    sql.append("`").append(cloum[i]).append("` ");
                } else {
                    sql.append(",`").append(cloum[i]).append("` ");
                }
            }
            sql.append(" FROM ").append(logTable).append(" WHERE id=").append(id);
            Map<String, Object> oldMap = operationLogMapper.selectAnyTalbe(sql.toString());

            try {
                p.proceed();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }

            Map<String, Object> newMap = operationLogMapper.selectAnyTalbe(sql.toString());
            if (oldMap != null && newMap != null) {
                // 记录操作日志
                OperationLog op = new OperationLog();
                op.setName(logName);
                op.setTableName(logTable);
                op.setTableId(id);
                op.setType(type.getType());
                op.setOperatorId(operatorId);
                op.setOperatorName(operatorName);
                op.setOperationTime(new Timestamp(System.currentTimeMillis()));
                operationLogMapper.insertOperationLog(op);

                // 记录操作日志详情
                List<OperationLogDetail> opds = new ArrayList<>();
                for (String clm : cloum) {
                    Object oldclm = oldMap.get(clm);
                    Object newclm = newMap.get(clm);
                    OperationLogDetail opd = new OperationLogDetail();
                    opd.setOldString(oldclm.toString());
                    opd.setNewString(newclm.toString());
                    opd.setClmName(clm);
                    opd.setClmComment(columnCommentMap.get(clm).toString());
                    opd.setOperationLogId(op.getId());
                    opds.add(opd);
                }
                if (!opds.isEmpty()) {
                    operationLogDetailMapper.insertOperationLogDetail(opds);
                }
            }
        }
    });
}

4、上面实现中用到了表对应的实体类,以及操作数据库的持久层mapper,还有一个数据对象
下面给出,注意说明一下,我用的是mybatis,最后提供pom.xml。✅✅✅

/**
 * 操作日志主信息模型
 * @author CSDN_风中无我
 */
public class OperationLog {
	/** 主键id */
	private String id;
	/** 操作业务名 */
	private String name;
	/** 操作表名 */
	private String tableName;
	/** 操作表id */
	private String tableId;
	/** 操作类型,(添加ADD,删除DELETE,修改UPDATE)' */
	private String type;
	/** 操作人id */
	private String operatorId;
	/** 操作人名 */
	private String operatorName;
	/** 操作时间 */
	private Timestamp operationTime;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getTableName() {
		return tableName;
	}
	public void setTableName(String tableName) {
		this.tableName = tableName;
	}
	public String getTableId() {
		return tableId;
	}
	public void setTableId(String tableId) {
		this.tableId = tableId;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	public String getOperatorId() {
		return operatorId;
	}
	public void setOperatorId(String operatorId) {
		this.operatorId = operatorId;
	}
	public String getOperatorName() {
		return operatorName;
	}
	public void setOperatorName(String operatorName) {
		this.operatorName = operatorName;
	}
	public Timestamp getOperationTime() {
		return operationTime;
	}
	public void setOperationTime(Timestamp operationTime) {
		this.operationTime = operationTime;
	}
}
/**
 * 操作日志详情模型
 * @author CSDN_风中无我
 */
public class OperationLogDetail {
	/** 主键id */
	private String id;
	/** 操作日志id */
	private String operationLogId;
	/** 字段名 */
	private String clmName;
	/** 字段描述 */
	private String clmComment;
	/** 旧值 */
	private String oldString;
	/** 新值 */
	private String newString;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getOperationLogId() {
		return operationLogId;
	}
	public void setOperationLogId(String operationLogId) {
		this.operationLogId = operationLogId;
	}
	public String getClmName() {
		return clmName;
	}
	public void setClmName(String clmName) {
		this.clmName = clmName;
	}
	public String getClmComment() {
		return clmComment;
	}
	public void setClmComment(String clmComment) {
		this.clmComment = clmComment;
	}
	public String getOldString() {
		return oldString;
	}
	public void setOldString(String oldString) {
		this.oldString = oldString;
	}
	public String getNewString() {
		return newString;
	}
	public void setNewString(String newString) {
		this.newString = newString;
	}
}
/**
 * 操作日志详情持久层
 * @author CSDN_风中无我
 */
@Mapper
public interface OperationLogDetailMapper {
	public static class OperationLogDetailMapperProvider{
		public String insertOperationLogDetailSQL(Map<String,List<OperationLogDetail>> map) {
			List<OperationLogDetail> ops = map.get("ops");
			StringBuilder sqlBuid = new StringBuilder("INSERT INTO operation_log_detail (operation_log_id,clm_name,clm_comment,old_string,new_string) VALUES ");
			for (int i = 0; i < ops.size(); i++) {
				OperationLogDetail o = ops.get(i);
				if (i==0) {
					sqlBuid.append(" ('"+o.getOperationLogId()+"','"+o.getClmName()+"','"+o.getClmComment()+"','"+o.getOldString()+"','"+o.getNewString()+"') ");
				}else {
					sqlBuid.append(" ,('"+o.getOperationLogId()+"','"+o.getClmName()+"','"+o.getClmComment()+"','"+o.getOldString()+"','"+o.getNewString()+"') ");
				}
			}
			return sqlBuid.toString();
		}
	}
	//批量添加操作详情
	@InsertProvider( type=OperationLogDetailMapperProvider.class, method="insertOperationLogDetailSQL" )
	public void insertOperationLogDetail(@Param("ops")List<OperationLogDetail> operationLogDetails);
}


/**
 * 操作日志持久层
 * @author CSDN_风中无我
 */
@Mapper
public interface OperationLogMapper {
	public static class OperationLogMapperProvider{
		public String selectAnyTalbeSQL(Map<String,String> map) {
			return map.get("sql");
		}
	}
	//添加操作日志
	@Insert("INSERT INTO operation_log (name,table_name,table_id,type,operator_id,operator_name,operation_time) VALUES (#{p.name},#{p.tableName},#{p.tableId},#{p.type},#{p.operatorId},#{p.operatorName},#{p.operationTime});")
	@Options(useGeneratedKeys=true,keyColumn="id",keyProperty="p.id")
	public void insertOperationLog(@Param("p")OperationLog operationLog);
	
	//查询任意sql
	@SelectProvider(type=OperationLogMapperProvider.class,method="selectAnyTalbeSQL")
	public Map<String,Object> selectAnyTalbe(@Param("sql")String sql);
	
	//查询任意表的字段与备注
	@Select("SELECT COLUMN_NAME `column`,column_comment `comment` FROM INFORMATION_SCHEMA.Columns WHERE table_name=#{table}")
	public List<ColumnComment> selectColumnCommentByTable(@Param("table")String tableName);
}
public class ColumnComment {
	private String column;
	private String comment;
	public String getColumn() {
		return column;
	}
	public void setColumn(String column) {
		this.column = column;
	}
	public String getComment() {
		return comment;
	}
	public void setComment(String comment) {
		this.comment = comment;
	}
}
public class HumpUtil {
	public static final char UNDERLINE = '_';
	/**
	 * (userId:user_id)
	 * @param param
	 * @return
	 */
	public static String camelToUnderline(String param) {
		if (param == null || "".equals(param.trim())) {
			return "";
		}
		int len = param.length();
		StringBuilder sb = new StringBuilder(len);
		for (int i = 0; i < len; i++) {
			char c = param.charAt(i);
			if (Character.isUpperCase(c)) {
				sb.append(UNDERLINE);
				sb.append(Character.toLowerCase(c));
			} else {
				sb.append(c);
			}
		}
		return sb.toString();
	}

	/**
	 * (user_id:userId)
	 * @param param
	 * @return
	 */
	public static String underlineToCamel(String param) {
		if (param == null || "".equals(param.trim())) {
			return "";
		}
		StringBuilder sb = new StringBuilder(param);
		Matcher mc = Pattern.compile(UNDERLINE + "").matcher(param);
		int i = 0;
		while (mc.find()) {
			int position = mc.end() - (i++);
			String.valueOf(Character.toUpperCase(sb.charAt(position)));
			sb.replace(position - 1, position + 1,
					sb.substring(position, position + 1).toUpperCase());
		}
		return sb.toString();
	}
}
public class ToMapUtil {
	@SuppressWarnings({ "unchecked"})
	public static <T> Map<String, Object> toMap(T bean) {
		if (bean instanceof Map) {
			return (Map<String, Object>)bean;
		}
		BeanWrapper beanWrapper = new BeanWrapperImpl(bean);
		Map<String, Object> map = new HashMap<String, Object>();
		PropertyDescriptor[] pds = beanWrapper.getPropertyDescriptors();
		for (PropertyDescriptor pd : pds) {
			if (!"class".equals(pd.getName())) {
				map.put(pd.getName(),
						beanWrapper.getPropertyValue(pd.getName()));
			}
		}
		return map;
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.csp.service</groupId>
	<artifactId>service-operationlog</artifactId>
	<version>1.0</version>
	<packaging>jar</packaging>

	<name>service-operationlog</name>
	<description>service-operationlog project for Spring Boot</description>
	
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.5</version>
		<relativePath />
	</parent>

	<dependencies>
		<!-- aop -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<!-- mybatis -->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.2.0</version> 
		</dependency>
		<!-- mysql -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.31</version>
		</dependency>
		<!-- commons-lang3 -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.12.0</version> 
		</dependency>
	</dependencies>
</project>

二、测试用例

5、测试。✅✅✅

启动类的配置。

package com.example.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = {"com.csp.**","com.example.**"})//这里是项目对SpringBean注入的扫描,前面是对operationlog项目中bean的扫描,后面是demo项目的bean的扫描
@MapperScan({"com.csp.operationlog.mapper","com.example.demo.**.mapper"})//这里com.csp.**是扫描我的operationlog项目的mapper,而com.example.**扫描的是我的demo项目的mapper
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

6、我们使用一个账户表,用来测试操作账户,看看是否能够实现日志记录✅✅✅

表创建:
CREATE TABLE `account` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `account` int(10) DEFAULT NULL COMMENT '账户',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

7、创建对应实体与mapper✅✅✅

public class Account implements Cloneable, Serializable {
	private static final long serialVersionUID = 1L;
	
	private Integer id;
    private Integer account;

    public Account clone() {
        try {
            Account proto = (Account) super.clone();
            return proto;
        }catch (CloneNotSupportedException e){
            return null;
        }
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public Integer getAccount() {
        return account;
    }
    public void setAccount(Integer account) {
        this.account = account;
    }
}
@Mapper
public interface AccountMapper {
    @Insert("INSERT INTO account (account) VALUES (#{a.account})")
    public void insertAccount(@Param("a") Account a);
    @Update("UPDATE account SET account=#{a.account} WHERE id=#{a.id}")
    public void updateAccount(@Param("a") Account a);
    @Select("DELETE FROM account WHERE id=#{id}")
    public void deleteAccountById(@Param("id") Integer id);
    @Select("SELECT id,account FROM account WHERE id=#{id}")
    public Account selectAccountById(@Param("id")Integer id);
}

8、后面是具体的业务代码✅✅✅

@Service
public class AccountService {
    @Resource
    AccountMapper accountMapper;
    @Resource
    OperationLogService operationLogService;

    //采用注解方式,实现操作日志的记录,适用于大多数简单服务,不涉及代码中多表更改的业务
    @OperationLog(name = "添加账户",type = OperationType.ADD,operatorIdRef = 0,operatorNameRef = 1,table = "account")
    public void addAccount(String operatorId,String operatorName){
        Account account = new Account();
        account.setAccount(181);
        accountMapper.insertAccount(account);
    }

    @OperationLog(name = "更新账户",type = OperationType.UPDATE,operatorIdRef = 0,operatorNameRef = 1,idRef = 2,table = "account")
    public void updateAccount(String operatorId,String operatorName,Integer accountId){
        Account account = new Account();
        account.setId(accountId);
        account.setAccount(1100);
        accountMapper.updateAccount(account);
    }

    @OperationLog(name = "删除账户",type = OperationType.DELETE,operatorIdRef = 0,operatorNameRef = 1,idRef = 2,table = "account")
    public void deleteAccount(String operatorId,String operatorName,Integer accountId){
        accountMapper.deleteAccountById(accountId);
    }

    //使用服务调用的方式,实现操作日志的记录,为了在注解无法解决业务代码中对多个表操作时的应对方法
    public void addAcccount2(){
        Account account=new Account();
        account.setAccount(181);
        accountMapper.insertAccount(account);
        operationLogService.logForAdd("添加账户","account","1","liutao",account);
    }

    public void updateAccount2(){
        Account account = accountMapper.selectAccountById(1);
        if (account!=null){
            Account accountOld = account.clone();
            account.setId(1);
            account.setAccount(0);
            accountMapper.updateAccount(account);
            operationLogService.logForUpd("更新账户","account",account.getId().toString(),"1","liutao",accountOld,account);
        }
    }

    public void deleteAccount2(){
        Account account = accountMapper.selectAccountById(1);
        if (account!=null){
            accountMapper.deleteAccountById(1);
            operationLogService.logForDel("删除账户","account",account.getId().toString(),"1","liutao",account);
        }
    }
}

8、为了方便测试,我们写几个controller进行测试,有页面调用,模拟实际业务操作。✅✅✅

@Controller
public class AccountControler {
    @Resource
    private AccountService accountService;
    @RequestMapping("/t1")
    @ResponseBody
    public String addTest(){
        accountService.addAccount("1","liutao");
        return "ok";
    }
    @RequestMapping("/t2")
    @ResponseBody
    public String updateTest(){
        accountService.updateAccount("1","liutao",1);
        return "ok";
    }
    @RequestMapping("/t3")
    @ResponseBody
    public String deleteTest(){
        accountService.deleteAccount("1","liutao",1);
        return "ok";
    }

    @RequestMapping("/s1")
    @ResponseBody
    public String addTest2(){
        accountService.addAcccount2();
        return "ok";
    }
    @RequestMapping("/s2")
    @ResponseBody
    public String updateTest2() {
        accountService.updateAccount2();
        return "ok";
    }
    @RequestMapping("/s3")
    @ResponseBody
    public String deleteTest2(){
        accountService.deleteAccount2();
        return "ok";
    }
}

OK!!!全部结束!!!可以看一下思路,具体的业务,可以结合自己的业务场景,去考虑的。以上代码仅供参考学习。

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值