512 lines
23 KiB
Java
512 lines
23 KiB
Java
package com.zhgd.xmgl.config;
|
||
|
||
import cn.hutool.core.bean.BeanUtil;
|
||
import cn.hutool.core.collection.CollUtil;
|
||
import cn.hutool.core.convert.Convert;
|
||
import cn.hutool.core.date.DateUtil;
|
||
import cn.hutool.core.util.ReflectUtil;
|
||
import cn.hutool.core.util.StrUtil;
|
||
import cn.hutool.json.JSONObject;
|
||
import cn.hutool.json.JSONUtil;
|
||
import com.alibaba.fastjson.JSON;
|
||
import com.baomidou.mybatisplus.annotation.TableId;
|
||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||
import com.zhgd.annotation.OperLog;
|
||
import com.zhgd.jeecg.common.util.SpringContextUtils;
|
||
import com.zhgd.xmgl.constant.Cts;
|
||
import com.zhgd.xmgl.entity.dto.OperLogDataChange;
|
||
import com.zhgd.xmgl.entity.dto.OperLogDataChangeField;
|
||
import com.zhgd.xmgl.entity.dto.OperLogInsertChange;
|
||
import com.zhgd.xmgl.modules.basicdata.entity.OperationLog;
|
||
import com.zhgd.xmgl.modules.basicdata.service.IOperationLogService;
|
||
import com.zhgd.xmgl.security.util.SecurityUtils;
|
||
import com.zhgd.xmgl.util.IpUtil;
|
||
import com.zhgd.xmgl.util.ThreadLocalUtil;
|
||
import io.swagger.annotations.ApiModelProperty;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.apache.commons.lang3.ArrayUtils;
|
||
import org.apache.commons.lang3.StringUtils;
|
||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||
import org.apache.commons.lang3.tuple.Pair;
|
||
import org.aspectj.lang.JoinPoint;
|
||
import org.aspectj.lang.annotation.AfterReturning;
|
||
import org.aspectj.lang.annotation.Aspect;
|
||
import org.aspectj.lang.annotation.Pointcut;
|
||
import org.aspectj.lang.reflect.MethodSignature;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.stereotype.Component;
|
||
import org.springframework.web.context.request.RequestAttributes;
|
||
import org.springframework.web.context.request.RequestContextHolder;
|
||
import org.springframework.web.multipart.MultipartFile;
|
||
|
||
import javax.servlet.http.HttpServletRequest;
|
||
import javax.servlet.http.HttpServletResponse;
|
||
import java.io.Serializable;
|
||
import java.lang.reflect.Field;
|
||
import java.lang.reflect.Method;
|
||
import java.util.*;
|
||
import java.util.function.Function;
|
||
import java.util.regex.Matcher;
|
||
import java.util.regex.Pattern;
|
||
import java.util.stream.Collectors;
|
||
import java.util.stream.Stream;
|
||
|
||
/**
|
||
* @program: wisdomSite
|
||
* @description: 切面处理类,操作日志记录处理
|
||
* @author: Mr.Peng
|
||
* @create: 2021-05-06 18:04
|
||
**/
|
||
@Aspect
|
||
@Component
|
||
@Slf4j
|
||
public class OperLogAspect {
|
||
|
||
Pattern logNamePattern = Pattern.compile("(.+):(\\d+:.+;)+");
|
||
Pattern logNamePattern1 = Pattern.compile("(\\d+)(\\D+)");
|
||
@Autowired
|
||
private IOperationLogService operationLogService;
|
||
|
||
|
||
/**
|
||
* 设置操作日志切入点 记录操作日志 在注解的位置切入代码
|
||
*/
|
||
@Pointcut("@annotation(com.zhgd.annotation.OperLog)")
|
||
public void operLogPoinCut() {
|
||
}
|
||
|
||
/**
|
||
* 设置操作异常切入点记录异常日志 扫描所有controller包下操作
|
||
*/
|
||
@Pointcut("execution(* com.*.*.*.*.controller..*.*(..))")
|
||
public void operExceptionLogPoinCut() {
|
||
}
|
||
|
||
/**
|
||
* 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
|
||
*
|
||
* @param joinPoint 切入点
|
||
* @param keys 返回结果
|
||
*/
|
||
@AfterReturning(value = "operLogPoinCut()", returning = "keys")
|
||
public void saveOperLog(JoinPoint joinPoint, Object keys) {
|
||
// 获取RequestAttributes
|
||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||
// 从获取RequestAttributes中获取HttpServletRequest的信息
|
||
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
|
||
OperationLog operlog = new OperationLog();
|
||
try {
|
||
// 主键ID
|
||
//operlog.setOperId(IdGenerator.getUUID());
|
||
// 从切面织入点处通过反射机制获取织入点处的方法
|
||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||
// 获取切入点所在的方法
|
||
Method method = signature.getMethod();
|
||
// 获取操作
|
||
OperLog opLog = method.getAnnotation(OperLog.class);
|
||
if (opLog != null) {
|
||
String operModul = opLog.operModul();
|
||
String operType = opLog.operType();
|
||
String operDesc = opLog.operDesc();
|
||
// 操作模块
|
||
operlog.setOperModul(operModul);
|
||
// 操作类型
|
||
operlog.setOperType(operType);
|
||
// 操作描述
|
||
operlog.setOperDesc(operDesc);
|
||
}
|
||
// 获取请求的类名
|
||
String className = joinPoint.getTarget().getClass().getName();
|
||
// 获取请求的方法名
|
||
String methodName = method.getName();
|
||
methodName = className + "." + methodName;
|
||
|
||
operlog.setOperMethod(methodName);
|
||
|
||
// 请求的参数
|
||
Map<String, String> rtnMap = converMap(request.getParameterMap());
|
||
// 将参数所在的数组转换成json
|
||
String params = JSON.toJSONString(rtnMap);
|
||
// 请求参数
|
||
operlog.setOperRequParam(params);
|
||
Object[] args = joinPoint.getArgs();
|
||
if (ArrayUtils.isNotEmpty(args)) {
|
||
List<Object> logArgs = Arrays.stream(args).filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse) && !(arg instanceof MultipartFile))).collect(Collectors.toList());
|
||
operlog.setBody(JSON.toJSONString(logArgs));
|
||
} else {
|
||
log.info("Request Args : null");
|
||
}
|
||
|
||
// 请求用户ID
|
||
String operUserId = request.getHeader("operateId");
|
||
operlog.setOperUserId(operUserId);
|
||
if (StringUtils.isBlank(operUserId) && SecurityUtils.getUser() != null) {
|
||
operlog.setOperUserId(String.valueOf(SecurityUtils.getUser().getUserId()));
|
||
}
|
||
//operlog.setOperUserName(); // 请求用户名称
|
||
// 请求IP
|
||
operlog.setOperIp(IpUtil.getRemortIP(request));
|
||
// 请求URI
|
||
operlog.setOperUri(request.getRequestURI());
|
||
// 请求方法
|
||
operlog.setOperMethod(request.getMethod());
|
||
// 创建时间
|
||
operlog.setOperCreateTime(new Date());
|
||
if ("com.zhgd.xmgl.modules.basicdata.controller.LoginController.login".equals(operlog.getOperMethod())) {
|
||
if (keys != null) {
|
||
JSONObject json = JSONUtil.parseObj(keys);
|
||
if ("200".equals(json.getStr("code"))) {
|
||
JSONObject jsonObject = json.getJSONObject("result");
|
||
String userId = jsonObject.getStr("userId");
|
||
operlog.setOperUserId(userId);
|
||
}
|
||
}
|
||
}
|
||
|
||
try {
|
||
//前后数据变化
|
||
List<OperLogDataChange> beforeList = ThreadLocalUtil.getByKey(Cts.TL_UPDATE_DEL_BEFORE_PARAM, List.class);
|
||
List<OperLogInsertChange> insertList = ThreadLocalUtil.getByKey(Cts.TL_INSERT_BEFORE_PARAM, List.class);
|
||
if (beforeList != null || insertList != null) {
|
||
if (beforeList == null) {
|
||
beforeList = new ArrayList<>();
|
||
}
|
||
List<OperLogDataChange> afterList = new ArrayList<>();
|
||
for (OperLogDataChange jo : beforeList) {
|
||
String mapperName = jo.getMapperName();
|
||
String whereSql = jo.getWhereSql();
|
||
List result = (List) jo.getResult();
|
||
if (CollUtil.isEmpty(result)) {
|
||
continue;
|
||
}
|
||
//根据修改之前的数据,取出id列表查询之后的数据
|
||
Field idField = this.getIdField(result);
|
||
List idList = (List) result.stream().map(o -> {
|
||
try {
|
||
return idField.get(o);
|
||
} catch (IllegalAccessException e) {
|
||
log.error("", e);
|
||
}
|
||
return null;
|
||
}).collect(Collectors.toList());
|
||
QueryWrapper<Object> wrapper = Wrappers.query().in(StrUtil.toUnderlineCase(idField.getName()), idList);
|
||
Object mapperObj = SpringContextUtils.getBean(Class.forName(mapperName));
|
||
Method selectListMethod = ReflectUtil.getMethod(mapperObj.getClass(), "selectList", QueryWrapper.class);
|
||
Object queryResult = ReflectUtil.invoke(mapperObj, selectListMethod, wrapper);
|
||
OperLogDataChange operLogDataChange = new OperLogDataChange();
|
||
operLogDataChange.setMapperName(mapperName);
|
||
operLogDataChange.setWhereSql(whereSql);
|
||
operLogDataChange.setResult(queryResult);
|
||
operLogDataChange.setTimestamp(System.currentTimeMillis());
|
||
afterList.add(operLogDataChange);
|
||
}
|
||
if (insertList != null) {
|
||
//补充完整 insertList,根据主键id查询插入后的数据
|
||
for (OperLogInsertChange change : insertList) {
|
||
String mapperName = change.getMapperName();
|
||
List result = (List) change.getResult();
|
||
Object mapperObj = SpringContextUtils.getBean(Class.forName(mapperName));
|
||
Method selectIdMethod = ReflectUtil.getMethod(mapperObj.getClass(), "selectById", Serializable.class);
|
||
Field idField = this.getIdField(result);
|
||
Object obj = result.get(0);
|
||
BeanUtil.copyProperties(ReflectUtil.invoke(mapperObj, selectIdMethod, idField.get(obj)), obj);
|
||
}
|
||
}
|
||
Pair<String, String> changePair = getBeforeAndAfterDataChange(beforeList, afterList);
|
||
operlog.setDataChangeBefore(changePair.getLeft());
|
||
operlog.setDataChangeAfter(changePair.getRight());
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("处理前后数据异常", e);
|
||
}
|
||
operationLogService.save(operlog);
|
||
} catch (Exception e) {
|
||
log.error("error:", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取前后数据变化
|
||
*
|
||
* @param beforeList
|
||
* @param afterList
|
||
* @return
|
||
*/
|
||
private Pair<String, String> getBeforeAndAfterDataChange(List<OperLogDataChange> beforeList, List<OperLogDataChange> afterList) {
|
||
Map<String, List<OperLogDataChange>> beforeMapperNameMap = beforeList.stream().collect(Collectors.groupingBy(OperLogDataChange::getMapperName));
|
||
Map<String, List<OperLogDataChange>> afterMapperNameMap = afterList.stream().collect(Collectors.groupingBy(OperLogDataChange::getMapperName));
|
||
Set<String> mapperNames = new HashSet<>(CollUtil.addAll(new HashSet<>(beforeMapperNameMap.keySet()), new HashSet<>(afterMapperNameMap.keySet())));
|
||
List<OperLogInsertChange> insertList = ThreadLocalUtil.getByKey(Cts.TL_INSERT_BEFORE_PARAM, List.class);
|
||
Map<String, List<OperLogInsertChange>> insertMapperNameMap = new HashMap<>();
|
||
if (CollUtil.isNotEmpty(insertList)) {
|
||
List<String> names = insertList.stream().map(OperLogInsertChange::getMapperName).collect(Collectors.toList());
|
||
mapperNames = new HashSet<>(CollUtil.addAll(mapperNames, names));
|
||
insertMapperNameMap = insertList.stream().collect(Collectors.groupingBy(OperLogInsertChange::getMapperName));
|
||
}
|
||
List<List<OperLogDataChangeField>> resultBeforeList = new ArrayList<>();
|
||
List<List<OperLogDataChangeField>> resultAfterList = new ArrayList<>();
|
||
for (String mapperName : mapperNames) {
|
||
List<OperLogDataChange> befores = beforeMapperNameMap.get(mapperName);
|
||
Field idField;
|
||
List beforeDataList = new ArrayList();
|
||
if (befores != null) {
|
||
//更新或删除时候
|
||
idField = getIdField((List) befores.get(0).getResult());
|
||
beforeDataList = getBeforeDataList(befores, idField);
|
||
} else {
|
||
//新增时候
|
||
List<OperLogInsertChange> operLogInsertChanges = insertMapperNameMap.get(mapperName);
|
||
idField = getIdField((List) operLogInsertChanges.get(0).getResult());
|
||
}
|
||
List afterDataList = getAfterDataList(idField, afterMapperNameMap.get(mapperName), insertMapperNameMap.get(mapperName));
|
||
Map<String, Object> beforeDataIdMap = (Map<String, Object>) beforeDataList.stream().collect(Collectors.toMap(o -> {
|
||
try {
|
||
return idField.get(o).toString();
|
||
} catch (IllegalAccessException e) {
|
||
e.printStackTrace();
|
||
}
|
||
return "";
|
||
}, Function.identity()));
|
||
Map<String, Object> afterDataIdMap = (Map<String, Object>) afterDataList.stream().collect(Collectors.toMap(o -> {
|
||
try {
|
||
return idField.get(o).toString();
|
||
} catch (IllegalAccessException e) {
|
||
e.printStackTrace();
|
||
}
|
||
return "";
|
||
}, Function.identity()));
|
||
//对比集合的数据
|
||
Field finalIdField = idField;
|
||
List<String> idList = (List) CollUtil.addAll(beforeDataList, afterDataList).stream().map(o -> {
|
||
try {
|
||
return finalIdField.get(o).toString();
|
||
} catch (IllegalAccessException e) {
|
||
e.printStackTrace();
|
||
}
|
||
return "";
|
||
}).distinct().collect(Collectors.toList());
|
||
for (String id : idList) {
|
||
Object afterObj = afterDataIdMap.get(id);
|
||
Object beforeObj = beforeDataIdMap.get(id);
|
||
List<OperLogDataChangeField> resultBeforeOneObjList = new ArrayList<>();
|
||
List<OperLogDataChangeField> resultAfterOneObjList = new ArrayList<>();
|
||
if (beforeObj != null && afterObj != null) {
|
||
//更新时候,都不为null
|
||
Field[] fields = afterObj.getClass().getDeclaredFields();
|
||
for (Field field : fields) {
|
||
field.setAccessible(true);
|
||
if (!field.isAnnotationPresent(ApiModelProperty.class)) {
|
||
continue;
|
||
}
|
||
try {
|
||
Object oldValue = field.get(beforeObj);
|
||
Object newValue = field.get(afterObj);
|
||
if (!Objects.equals(newValue, oldValue) || idField.getName().equals(field.getName())) {
|
||
resultBeforeOneObjList.add(getOperLogDataChangeField(field, oldValue));
|
||
resultAfterOneObjList.add(getOperLogDataChangeField(field, newValue));
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("更新时候报错", e);
|
||
}
|
||
}
|
||
} else if (beforeObj != null && afterObj == null) {
|
||
//删除时候
|
||
Field[] fields = beforeObj.getClass().getDeclaredFields();
|
||
for (Field field : fields) {
|
||
field.setAccessible(true);
|
||
if (!field.isAnnotationPresent(ApiModelProperty.class)) {
|
||
continue;
|
||
}
|
||
try {
|
||
Object oldValue = field.get(beforeObj);
|
||
if (oldValue != null) {
|
||
resultBeforeOneObjList.add(getOperLogDataChangeField(field, oldValue));
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("删除时候报错", e);
|
||
}
|
||
}
|
||
} else if (beforeObj == null && afterObj != null) {
|
||
//新增时候
|
||
Field[] fields = afterObj.getClass().getDeclaredFields();
|
||
for (Field field : fields) {
|
||
field.setAccessible(true);
|
||
if (!field.isAnnotationPresent(ApiModelProperty.class)) {
|
||
continue;
|
||
}
|
||
try {
|
||
Object newValue = field.get(afterObj);
|
||
if (newValue != null) {
|
||
resultAfterOneObjList.add(getOperLogDataChangeField(field, newValue));
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("新增时候报错", e);
|
||
}
|
||
}
|
||
}
|
||
if (CollUtil.isNotEmpty(resultBeforeOneObjList) && resultBeforeOneObjList.size() > 1) {
|
||
resultBeforeList.add(resultBeforeOneObjList);
|
||
}
|
||
if (CollUtil.isNotEmpty(resultAfterOneObjList) && resultAfterOneObjList.size() > 1) {
|
||
resultAfterList.add(resultAfterOneObjList);
|
||
}
|
||
}
|
||
}
|
||
return new ImmutablePair(StrUtil.join(";", resultBeforeList.stream().map(fields -> StrUtil.join(",", fields.stream().map(map -> map.getName() + ":" + map.getVal()).collect(Collectors.toList()))).collect(Collectors.toList())),
|
||
StrUtil.join(";", resultAfterList.stream().map(fields -> StrUtil.join(",", fields.stream().map(map -> map.getName() + ":" + map.getVal()).collect(Collectors.toList()))).collect(Collectors.toList())));
|
||
}
|
||
|
||
/**
|
||
* 获取前后变化的字段名和值
|
||
*
|
||
* @param field
|
||
* @param val
|
||
* @return
|
||
*/
|
||
private OperLogDataChangeField getOperLogDataChangeField(Field field, Object val) {
|
||
OperLogDataChangeField bm = new OperLogDataChangeField();
|
||
String name = field.getAnnotation(ApiModelProperty.class).value();
|
||
String v = null;
|
||
if (val instanceof Integer) {
|
||
Matcher matcher = logNamePattern.matcher(name);
|
||
if (matcher.matches()) {
|
||
//如:类型:1:人员;2:人员照片;3:人员权限;4:固定车辆;5:固定车辆群组;6:临时车;7:车辆布防;
|
||
for (String s : matcher.group(2).split(";")) {
|
||
String[] split = s.split(":");
|
||
if (Objects.equals(split[0], val.toString())) {
|
||
// v = split[1];
|
||
name = name.split(":")[0];
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
boolean isMatch = false;
|
||
Matcher matcher1 = logNamePattern1.matcher(name);
|
||
while (matcher1.find()) {
|
||
isMatch = true;
|
||
if (matcher1.group(1).equals(val.toString())) {
|
||
if (name.contains(":")) {
|
||
name = StrUtil.subBefore(name, ":", false);
|
||
} else {
|
||
name = field.getName();
|
||
}
|
||
// v = matcher1.group(2);
|
||
break;
|
||
}
|
||
}
|
||
if (!isMatch) {
|
||
name = field.getName();
|
||
}
|
||
}
|
||
}
|
||
if (v == null) {
|
||
v = getVal(val);
|
||
}
|
||
bm.setName(name);
|
||
bm.setVal(v);
|
||
return bm;
|
||
}
|
||
|
||
/**
|
||
* 获取值
|
||
*
|
||
* @param val
|
||
* @return
|
||
*/
|
||
private String getVal(Object val) {
|
||
if (val instanceof Date) {
|
||
return DateUtil.formatDateTime((Date) val);
|
||
} else {
|
||
return Convert.toStr(val);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取主键id的属性
|
||
*
|
||
* @param result
|
||
* @return
|
||
*/
|
||
private Field getIdField(List result) {
|
||
Field idField = null;
|
||
Object obj = result.get(0);
|
||
Field[] fields = obj.getClass().getDeclaredFields();
|
||
for (Field field : fields) {
|
||
field.setAccessible(true);
|
||
if (field.isAnnotationPresent(TableId.class)) {
|
||
idField = field;
|
||
break;
|
||
}
|
||
}
|
||
return idField;
|
||
}
|
||
|
||
/**
|
||
* 获取之后的数据
|
||
*
|
||
* @param idField
|
||
* @param afterList
|
||
* @param insertList
|
||
* @return
|
||
*/
|
||
private List getAfterDataList(Field idField, List<OperLogDataChange> afterList, List<OperLogInsertChange> insertList) {
|
||
Stream stream1;
|
||
if (afterList != null) {
|
||
stream1 = afterList.stream().flatMap(map -> ((List) map.getResult()).stream());
|
||
} else {
|
||
stream1 = Collections.emptyList().stream();
|
||
}
|
||
if (CollUtil.isNotEmpty(insertList)) {
|
||
Stream stream2 = insertList.stream().flatMap(map -> ((List) map.getResult()).stream());
|
||
stream1 = Stream.concat(stream1, stream2);
|
||
}
|
||
List rtList = (List) stream1.collect(Collectors.collectingAndThen(
|
||
Collectors.toCollection(() -> new TreeSet<>(
|
||
Comparator.comparing(
|
||
o -> {
|
||
try {
|
||
return idField.get(o).toString();
|
||
} catch (IllegalAccessException e) {
|
||
e.printStackTrace();
|
||
}
|
||
return "";
|
||
}))), ArrayList::new));
|
||
return rtList;
|
||
}
|
||
|
||
/**
|
||
* 获取之前的数据
|
||
*
|
||
* @param list
|
||
* @param idField
|
||
* @return
|
||
*/
|
||
private List getBeforeDataList(List<OperLogDataChange> list, Field idField) {
|
||
return (List) list.stream().flatMap(map -> ((List) map.getResult()).stream()).collect(Collectors.collectingAndThen(
|
||
Collectors.toCollection(() -> new TreeSet<>(
|
||
Comparator.comparing(
|
||
o -> {
|
||
try {
|
||
return idField.get(o).toString();
|
||
} catch (IllegalAccessException e) {
|
||
e.printStackTrace();
|
||
}
|
||
return "";
|
||
}))), ArrayList::new));
|
||
}
|
||
|
||
/**
|
||
* 转换request 请求参数
|
||
*
|
||
* @param paramMap request获取的参数数组
|
||
*/
|
||
public Map<String, String> converMap(Map<String, String[]> paramMap) {
|
||
Map<String, String> rtnMap = new HashMap<String, String>();
|
||
for (String key : paramMap.keySet()) {
|
||
rtnMap.put(key, paramMap.get(key)[0]);
|
||
}
|
||
return rtnMap;
|
||
}
|
||
}
|