如何实现操作操作日志记录

如何实现操作操作日志记录

为什么要记录操作日志?

项目中的业务需求,需要针对用户的一些业务操作做操作记录,
也就是标题中的操场日志记录,最近做的项目也有这个需求,
我也是第一次写,相信有很多开发者也有遇到这个需求的,所以
在这里做一个简单的记录,只是提供一个思路参考,代码什么的
其实是次要的!

业务需求如下,记录用户的重要操作,记录除查询外,如增加,修改,和删除等操作

在这里插入图片描述

实现思路

首先我肯定是用aop了,在后面的使用发现,apo的实现适合大部分
的单表操作,但是多表更改,例如先加后改是没法实现的,所以我决定
提供两种实现方式,另外一种使用service函数调用来解决了

表设计

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实现

0目标: 在业务代码函数上使用注解,通过注解实现执行时的环形切面,在切面前,切面后,做数据的变更记录操作
开始:
1创建注解
package com.csp.operationlog.aspect.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.csp.operationlog.aspect.enums.OperationType;

/**
 * 用来标注需要进行操作日志的服务函数上
 * @author taoken
 */
@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();
}
由于使用了一个枚举下面提供一个枚举,作用是分辨操作类型
package com.csp.operationlog.aspect.enums;
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下面开始实现切面
package com.csp.operationlog.aspect.aop;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import com.csp.operationlog.aspect.enums.OperationType;
import com.csp.operationlog.dto.ColumnComment;
import com.csp.operationlog.mapper.OperationLogDetailMapper;
import com.csp.operationlog.mapper.OperationLogMapper;
import com.csp.operationlog.model.OperationLog;
import com.csp.operationlog.model.OperationLogDetail;

@Aspect
@Component
public class OperationLogAop {
	@Autowired
	private OperationLogMapper operationLogMapper;
	@Autowired
	private OperationLogDetailMapper operationLogDetailMapper;
	@Autowired
	private TransactionTemplate txTemplate;

	@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);
		}
	}
	
	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<String, Object>();
				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<String>();
					for (String o : keySet) {
						list.add(o.toString());
					}
					cloum = list.toArray(new String[list.size()]);
				}
				sql.append("SELECT ");
				for (int i = 0; i < cloum.length; i++) {
					if (i == 0) {
						sql.append("`" + cloum[i] + "` ");
					} else {
						sql.append(",`" + cloum[i] + "` ");
					}
				}
				sql.append(" FROM " + logTable + " WHERE id=" + 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<OperationLogDetail>();
					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);
					}
				}
			}
		});
	}

	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<String, Object>();
				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<String>();
					for (String o : keySet) {
						list.add(o.toString());
					}
					cloum = list.toArray(new String[list.size()]);
				}
				sql.append("SELECT ");
				for (int i = 0; i < cloum.length; i++) {
					if (i == 0) {
						sql.append("`" + cloum[i] + "` ");
					} else {
						sql.append(",`" + cloum[i] + "` ");
					}
				}
				sql.append(" FROM " + logTable + " 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<OperationLogDetail>();
					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);
					}
					
				}
			}
		});
	}

	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<String, Object>();
				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<String>();
					for (String o : keySet) {
						list.add(o.toString());
					}
					cloum = list.toArray(new String[list.size()]);
				}
				sql.append("SELECT ");
				for (int i = 0; i < cloum.length; i++) {
					if (i == 0) {
						sql.append("`" + cloum[i] + "` ");
					} else {
						sql.append(",`" + cloum[i] + "` ");
					}
				}
				sql.append(" FROM " + logTable + " WHERE id=" + 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<OperationLogDetail>();
					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
package com.csp.operationlog.model;
import java.sql.Timestamp;
/**
 * 操作日志主信息模型
 * @author taoken
 */
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;
	}
}

package com.csp.operationlog.model;
/**
 * 操作日志详情模型
 * @author taoken
 */
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;
	}
}

package com.csp.operationlog.mapper;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.csp.operationlog.model.OperationLogDetail;
/**
 * 操作日志详情持久层
 * @author taoken
 */
@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);
}

package com.csp.operationlog.mapper;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.SelectProvider;
import com.csp.operationlog.dto.ColumnComment;
import com.csp.operationlog.model.OperationLog;

/**
 * 操作日志持久层
 * @author taoken
 */
@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);
}

package com.csp.operationlog.dto;
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;
	}
}

package com.csp.operationlog.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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();
	}
}

package com.csp.operationlog.util;
import java.beans.PropertyDescriptor;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
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;
	}
}

pom.xml
<?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>1.5.9.RELEASE</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>1.3.2</version>
		</dependency>
		<!-- mysql -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!--long3-->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.3.2</version>
		</dependency>
	</dependencies>
</project>

开始测试

创建springboot的测试demo项目

1启动相关配置与启动类,这里模拟我们的真实项目
pom.xml
<?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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.9.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.csp.service</groupId>
			<artifactId>service-operationlog</artifactId>
			<version>1.0</version>
		</dependency>
		<!-- mybatis -->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>
		<!-- mysql -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

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);
	}
}

2 我们使用一个账户表,用来测试操作账户,看看是否能够实现日志记录
表创建:
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

2 创建对应实体与mapper
package com.example.demo.domain;
import java.io.Serializable;
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;
    }
}

package com.example.demo.mapper;
import com.example.demo.domain.Account;
import org.apache.ibatis.annotations.*;
@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);
}

3 后面是具体的业务代码
package com.example.demo.service;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.csp.operationlog.aspect.annotation.OperationLog;
import com.csp.operationlog.aspect.enums.OperationType;
import com.csp.operationlog.service.OperationLogService;
import com.example.demo.domain.Account;
import com.example.demo.mapper.AccountMapper;
@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);
        }
    }
}
4 为了方便测试,我们写几个controller进行测试,有页面调用,模拟实际业务操作
package com.example.demo.controller;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.demo.service.AccountService;
@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";
    }
}

配置文件application.yml
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf-8&useSSL=false
    username: root
    password: root

备注

有了上面的基本实现,和测试demo,应该可以基本实现日志的记录,
对于相关细节,我都放到备注中,
1 apo实现,需要再建立在mysql事务级别在可重复读级别上(一般默认就是哈!)
2 服务调用实现,可以异步处理啦,如果有为了效率可以再我备注的地方实现即可,自己选择mq实现就行了
3 只能保证基本实现了,质量不保证,主要是提供思路和实现逻辑,有了思路,自己可以写的
4 已经有了记录,具体的表查询的业务代码就可以自己查表了哈
最后项目代码放到github: https://github.com/taopanwoaini/operation-log 自行下载吧,路过几点点个星,谢谢

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值