目的:记录并审计通过应用系统修改的数据。
实现方案:实现org.apache.ibatis.plugin.Interceptor接口,拦截UPDATE 操作。
获取运行中,待执行的update sql及参数。解析为select column from table 的sql语句。
落地方案:
-
定义@interface DataAuditLogging ,在service public方法标注,明确需要拦截哪些方法
-
定义Aspect DataAuditLogAspect 。
-
通过@Before 明确识别并拦截service public方法,组装TransmittableThreadLocal上下文参数。
-
通过@AfterReturning 明确在业务方法执行成功完成后,再保存数据审计日志的记录至DB,并保证最终remove TransmittableThreadLocal 。
-
定义Interceptor DataAuditLogAOP,明确其拦截的UPDATE SQL,并组装select sql,将change对象临时存放至TransmittableThreadLocal 。
mybatis 3.5.6,mybatis-plus 3.3.2
附加核心业务代码:
DataAuditLogging
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataAuditLogging {
/**
* 标签
*/
String tag() default "";
/**
* 注释
*/
String note() default "";
/**
* 当前登录人ID
* @return
*/
String currUserId() default "";
/**
* 当前登录人name
* @return
*/
String currUsername() default "";
/**
* 当前业务主键
* @return
*/
String dataId() default "";
}
DataAuditLogAOP
import com.alibaba.druid.proxy.jdbc.JdbcParameter;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.TableNameParser;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import com.cbi.fund.common.dto.OperationDataChange;
import com.cbi.fund.common.utils.DataOperateLogThreadLocal;
import com.mysql.cj.jdbc.ClientPreparedStatement;
import com.stars.easyms.base.trace.EasyMsTraceSynchronizationManager;
import com.stars.easyms.base.util.ApplicationContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import java.sql.Statement;
import java.util.*;
import java.util.stream.Collectors;
/**
* 数据更新拦截器 SQL AOP
*/
@SuppressWarnings("ALL")
@Slf4j
@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "update", args = {Statement.class})})
public class DataAuditLogAOP extends AbstractSqlParserHandler implements Interceptor {
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();
@Autowired
@Qualifier("jdbcTemplateOne")
@Lazy
private JdbcTemplate jdbcTemplate;
@Override
public Object intercept(Invocation invocation) throws Exception {
String originalSql = null;
try{
// 判断是否需要记录日志
if (DataOperateLogThreadLocal.DATA_CHANGES.get() == null) {
return invocation.proceed();
}
StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
this.sqlParser(metaObject);
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
if(!SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())) {
//非更新数据
return invocation.proceed();
}
if(jdbcTemplate == null){
jdbcTemplate = ApplicationContextHolder.getBean(JdbcTemplate.class);
}
Object firstArg = invocation.getArgs()[0];
Statement statement = (Statement) firstArg;
MetaObject stmtMetaObj = SystemMetaObject.forObject(statement);
// 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环可以分离出最原始的的目标类)
while (stmtMetaObj.hasGetter("h")) {
Object object = stmtMetaObj.getValue("h");
stmtMetaObj = SystemMetaObject.forObject(object);
}
// 分离最后一个代理对象的目标类
while (stmtMetaObj.hasGetter("target")) {
Object object = stmtMetaObj.getValue("target");
stmtMetaObj = SystemMetaObject.forObject(object);
}
try {
statement = (Statement) stmtMetaObj.getValue("statement");
List<Object> columnValues = (List<Object>) stmtMetaObj.getValue("columnValues");
/*Map<Integer,Object> paramerters = (Map<Integer, Object>) stmtMetaObj.getValue("statement.stmt.parameters");
Object sql = stmtMetaObj.getValue("statement.stmt.sql");
//获取业务要执行的sql
originalSql = sql.toString();
originalSql = originalSql.replaceAll("[\\s]+", StringPool.SPACE);
//TODO 组装参数
originalSql = makeupExeSQL(originalSql,columnValues,paramerters);*/
Object raw = stmtMetaObj.getValue("statement.stmt.raw");
ClientPreparedStatement cps = (ClientPreparedStatement) raw;
originalSql = cps.asSql();
} catch (Exception e) {
}
if (stmtMetaObj.hasGetter("delegate")) {
try {
statement = (Statement) stmtMetaObj.getValue("delegate");
} catch (Exception ignored) {
}
}
if(originalSql == null){
try{
statement = (Statement) stmtMetaObj.getValue("stmt.statement");
}catch (Exception e1){
}
originalSql = statement.toString();
}
originalSql = originalSql.replaceAll("[\\s]+", StringPool.SPACE);
int index = indexOfSqlStart(originalSql);
if (index > 0) {
originalSql = originalSql.substring(index);
}
// 获取执行Sql
String sql = originalSql.replace("where", "WHERE");
if(sql.endsWith("]")){
sql = sql.replaceAll("]","");
}
// 使用mybatis-plus 工具解析sql获取表名
Collection<String> tables = new TableNameParser(sql).tables();
if (CollectionUtils.isEmpty(tables) || sql.indexOf("WHERE") < 0) {
return invocation.proceed();
}
String tableName = tables.iterator().next();
OperationDataChange change = new OperationDataChange();
change.setDataId(EasyMsTraceSynchronizationManager.getTraceId());
change.setTableName(tableName);
change.setJdbcTemplate(jdbcTemplate);
log.info("====biz pre exec sql:{}",sql);
// 设置sql用于执行完后查询新数据
String whereSql = sql.substring(sql.lastIndexOf("WHERE") + 5);
// 同表对同条数据操作多次只进行一次对比
if (DataOperateLogThreadLocal.DATA_CHANGES.get().stream().anyMatch(c -> tableName.equals(c.getTableName())
&& whereSql.equals(c.getWhereSql()))) {
return invocation.proceed();
}
change.setWhereSql(whereSql);
String querySql = "select * from "+tableName+" where "+whereSql;
StringBuffer sb = new StringBuffer();
try{
List<TableInfo> ts = TableInfoHelper.getTableInfos();
TableInfo tableInfo = ts.stream().filter(t -> {
if (t.getTableName().equalsIgnoreCase(tableName)) {
return true;
}else{
return false;
}
}).collect(Collectors.toList()).get(0);
String selectSql = tableInfo.getAllSqlSelect();
String[] realStrs = selectSql.split(",");
for(int i =0;i < realStrs.length;i ++){
String column = realStrs[i];
//UPDATE sys_user SET org_code = 'A01A01A14' WHERE username = '13235654896'
if(sql.toLowerCase().indexOf(""+column+"=") >=0 ){
sb.append(column).append(",");
}
}
//List<TableFieldInfo> fieldList = tableInfo.getFieldList();
//change.setTransferData(fieldList);
change.setEntityType(tableInfo.getEntityType());
}catch (Exception e){
}
//明确 具体的查询的字段
if(sb.toString().endsWith(",")){
querySql = "select "+sb.toString().substring(0,sb.length() - 1)+" from "+ tableName+" where "+whereSql;
}
log.info("====querySql:{}",querySql);
change.setQuerySql(querySql);
if(change.getEntityType() == null){
List<Map<String, Object>> maps = jdbcTemplate.queryForList(querySql);
change.setOldData(maps);
}else{
String clzzName = change.getEntityType().getName();
Class<?> clazz = Class.forName(clzzName);
List<?> oldList = jdbcTemplate.query(querySql,new BeanPropertyRowMapper<>(clazz));
change.setOldData(oldList);
}
DataOperateLogThreadLocal.DATA_CHANGES.get().add(change);
}catch (Exception e){
log.info("DataOperateInterceptor-intercept-error:{}", JSON.toJSONString(e));
}
return invocation.proceed();
}
private String makeupExeSQL(String originalSql, List<Object> columnValues, Map<Integer, Object> paramerters) {
if(originalSql.indexOf(" where ") >=0 ){
originalSql = originalSql.replace(" where "," WHERE ");
}
if(originalSql.indexOf("? WHERE ") >=0 ){
originalSql = originalSql.replace("? WHERE ","?, WHERE ");
}
StringBuffer sb = new StringBuffer(originalSql);
sb.append(",");
String sql = sb.toString();
//参数数量
// 入参个数与执行sql的占位符?, 相等
int paramSize = columnValues.size();
if(paramSize == 0){
return originalSql;
}
for(int i = 0;i < paramSize;i ++){
Object obj = columnValues.get(i);
JdbcParameter param = (JdbcParameter) paramerters.get(i);
String pa = param.getValue().toString();
obj.toString();
StringBuffer str = new StringBuffer("='").append(pa).append("',");
sql = sql.replaceFirst("=\\?,",str.toString());
}
if(sql.endsWith(",")){
sql = sql.substring(0,sql.length() - 1);
sql = sql.replace(", WHERE "," WHERE ");
}
log.info("makeupExeSQL sql:{}",sql);
return sql;
}
/**
* 获取sql语句开头部分
*/
private int indexOfSqlStart(String sql) {
String upperCaseSql = sql.toUpperCase();
Set<Integer> set = new HashSet<>();
set.add(upperCaseSql.indexOf("SELECT "));
set.add(upperCaseSql.indexOf("UPDATE "));
set.add(upperCaseSql.indexOf("INSERT "));
set.add(upperCaseSql.indexOf("DELETE "));
set.remove(-1);
if (CollectionUtils.isEmpty(set)) {
return -1;
}
List<Integer> list = new ArrayList<>(set);
list.sort(Comparator.naturalOrder());
return list.get(0);
}
}
DataAuditLogAspect
/**
* CG
*/
@SuppressWarnings("ALL")
@Slf4j
@Aspect
@Component
//@ConditionalOnProperty(value = "mylog.enable",havingValue = "true")
public class DataAuditLogAspect {
@Autowired
@Lazy
private ProductFeign logFeign;
/**
*
* 数据审核日志切面前执行
* @param joinPoint
* @param dataLog
*/
@Before("@annotation(dataLog)")
public void beforeDataAuditLogging(JoinPoint joinPoint, DataAuditLogging dataLog) {
Object obj = DataOperateLogThreadLocal.THREADDATA_ID.get();
if(obj == null){
obj = UUID.fastUUID().toString();
}
DataOperateLogThreadLocal.THREADDATA_ID.set(obj.toString());
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String ClassName = methodSignature.getDeclaringTypeName();
DataOperateLogThreadLocal.THREADDATA_METHOD.set(ClassName+"#"+joinPoint.getSignature().getName());
DataOperateLogThreadLocal.DATA_CHANGES.set(new LinkedList<>());
DataOperateLogThreadLocal.JOIN_POINT.set(joinPoint);
DataOperateLogThreadLocal.DATA_LOG.set(dataLog);
ExpressionParser parser = new SpelExpressionParser();
LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
Method targetMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();
String[] params = discoverer.getParameterNames(targetMethod);
Object[] args = joinPoint.getArgs();
EvaluationContext context = new StandardEvaluationContext();
for (int len = 0; len < params.length; len++) {
context.setVariable(params[len], args[len]);
}
String dataId = parser.parseExpression(dataLog.dataId()).getValue(context, String.class);
String userId = parser.parseExpression(dataLog.currUserId()).getValue(context, String.class);
String name = parser.parseExpression(dataLog.currUsername()).getValue(context, String.class);
AuditLogDTO log = new AuditLogDTO();
log.setDataId(dataId);
//TODO 补充 当前的登录人ID ,Name
log.setCurrUserId(userId);
log.setCurrUsername(name);
log.setTag(dataLog.tag());
DataOperateLogThreadLocal.DATA_REQ_LOG.set(log);
}
/**
* 数据审计日志切面后执行
* @param dataLog
*/
@AfterReturning("@annotation(dataLog)")
public void afterDataAuditLogging(DataAuditLogging dataLog) {
try{
List<OperationDataChange> list = DataOperateLogThreadLocal.DATA_CHANGES.get();
AuditLogDTO logDTO = DataOperateLogThreadLocal.DATA_REQ_LOG.get();
if (CollectionUtils.isEmpty(list)) {
return;
}
list.forEach(change -> {
List<?> oldData = change.getOldData();
if (CollectionUtils.isEmpty(oldData)) {
return;
}
if(change.getEntityType() == null){
//按 map处理
List<Map<String, Object>> maps = change.getJdbcTemplate().queryForList(change.getQuerySql());
change.setNewData(maps);
}else{
//按对象处理
try {
String clzzName = change.getEntityType().getName();
Class<?> clazz = Class.forName(clzzName);
List<?> newList = change.getJdbcTemplate().query(change.getQuerySql(),new BeanPropertyRowMapper<>(clazz));
change.setNewData(newList);
} catch (Exception e) {
}
}
//置空 jdbc
change.setJdbcTemplate(null);
});
//比对数据 并保存
this.compareAndTransfer(list,logDTO);
}catch (Exception e){
log.info("数据审计日志记录失败");
log.error(e.getMessage(),e);
}finally {
//删除此线程局部变量的当前线程
DataOperateLogThreadLocal.transfer();
}
}
private void compareAndTransfer(List<OperationDataChange> list, AuditLogDTO logDTO) {
log.info("===比对后,将变更的数据写入DB= list:{}",JSON.toJSON(list));
if(list == null || list.size() == 0){
return ;
}
list.stream().forEach( change->{
List<?> oldData = change.getOldData();
List<?> newData = change.getNewData();
// 更新前后数据量不对必定是删除(逻辑删除)不做处理
if (newData == null || oldData == null) {
return;
}
if (oldData.size() != newData.size()) {
return;
}
for (int i = 0; i < oldData.size(); i++) {
try{
SysDataLogDTO dto = CompareObjUtil.converterDataAuditRecord(change, oldData.get(i), newData.get(i),
logDTO.getDataId(), logDTO.getTag(), logDTO.getCurrUserId(), logDTO.getCurrUsername());
logFeign.saveSysDataLog(dto);
}catch (Exception e){
log.error(e.getMessage(),e);
}
}
});
}
}
CompareObjUtil
/**
*
*/
@SuppressWarnings("ALL")
@Slf4j
public class CompareObjUtil {
/**
* 比较两个对象的不同
*/
public static List<ComparisonDTO> compareObj(Object beforeObj, Object afterObj) throws Exception{
List<ComparisonDTO> diffs = new ArrayList<>();
if(beforeObj == null) {
throw new RuntimeException("原对象不能为空");
}
if(afterObj == null) {
throw new RuntimeException("新对象不能为空");
}
if(!beforeObj.getClass().isAssignableFrom(afterObj.getClass())){
throw new RuntimeException("两个对象不相同,无法比较");
}
//取出属性
Field[] beforeFields = beforeObj.getClass().getDeclaredFields();
Field[] afterFields = afterObj.getClass().getDeclaredFields();
Field.setAccessible(beforeFields, true);
Field.setAccessible(afterFields, true);
//遍历取出差异值
if(beforeFields != null && beforeFields.length > 0){
for(int i=0; i<beforeFields.length; i++){
Object beforeValue = beforeFields[i].get(beforeObj);
Object afterValue = afterFields[i].get(afterObj);
if((beforeValue != null && !"".equals(beforeValue) && !beforeValue.equals(afterValue)) || ((beforeValue == null || "".equals(beforeValue)) && afterValue != null)){
ComparisonDTO comparison = new ComparisonDTO();
comparison.setField(beforeFields[i].getName());
comparison.setBefore(beforeValue);
comparison.setAfter(afterValue);
// 获取指定的注解
ApiModelProperty api = beforeFields[i].getDeclaredAnnotation(ApiModelProperty.class);
if (api != null && StringUtils.isNotEmpty(api.value())) {
comparison.setFieldApiModelProperty(api.value());
}
diffs.add(comparison);
}
}
}
return diffs;
}
/**
* 比较两个json串的不同
*/
public static String campareJsonObject(String oldJsonStr, String newJsonStr1) {
//将字符串转换为json对象
JSON oldJson = JSON.parseObject(oldJsonStr);
JSON newJson = JSON.parseObject(newJsonStr1);
//递归遍历json对象所有的key-value,将其封装成path:value格式进行比较
Map<String, Object> oldMap = new LinkedHashMap<>();
Map<String, Object> newMap = new LinkedHashMap<>();
convertJsonToMap(oldJson, "", oldMap);
convertJsonToMap(newJson, "", newMap);
Map<String, Object> differenceMap = campareMap(oldMap, newMap);
//将最终的比较结果把不相同的转换为json对象返回
String jsonObject = convertMapToJson(differenceMap);
return jsonObject;
}
/**
* 将json数据转换为map存储用于比较
*/
private static void convertJsonToMap(Object json, String root, Map<String, Object> resultMap) {
if (json instanceof JSONObject) {
JSONObject jsonObject = ((JSONObject) json);
Iterator iterator = jsonObject.keySet().iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
Object value = jsonObject.get(key);
String newRoot = "".equals(root) ? key + "" : root + "." + key;
if (value instanceof JSONObject || value instanceof JSONArray) {
convertJsonToMap(value, newRoot, resultMap);
} else {
resultMap.put(newRoot, value);
}
}
} else if (json instanceof JSONArray) {
JSONArray jsonArray = (JSONArray) json;
for (int i = 0; i < jsonArray.size(); i++) {
Object vaule = jsonArray.get(i);
String newRoot = "".equals(root) ? "[" + i + "]" : root + ".[" + i + "]";
if (vaule instanceof JSONObject || vaule instanceof JSONArray) {
convertJsonToMap(vaule, newRoot, resultMap);
} else {
resultMap.put(newRoot, vaule);
}
}
}
}
/**
* 比较两个map,返回不同数据
*/
private static Map<String, Object> campareMap(Map<String, Object> oldMap, Map<String, Object> newMap) {
//遍历newMap,将newMap的不同数据装进oldMap,同时删除oldMap中与newMap相同的数据
campareNewToOld(oldMap, newMap);
//將舊的有新的沒有的數據封裝數據結構存在舊的裡面
campareOldToNew(oldMap);
return oldMap;
}
/**
* 將舊的有新的沒有的數據封裝數據結構存在舊的裡面
*/
private static void campareOldToNew(Map<String, Object> oldMap) {
//统一oldMap中newMap不存在的数据的数据结构,便于解析
for (Iterator<Map.Entry<String, Object>> it = oldMap.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Object> item = it.next();
String key = item.getKey();
Object value = item.getValue();
int lastIndex = key.lastIndexOf(".");
if (!(value instanceof Map)) {
Map<String, Object> differenceMap = new HashMap<>();
differenceMap.put("oldValue", value);
differenceMap.put("newValue", "");
oldMap.put(key, differenceMap);
}
}
}
/**
* 將新的map與舊的比較,並將數據統一存在舊的裡面
*/
private static void campareNewToOld(Map<String, Object> oldMap, Map<String, Object> newMap) {
for (Iterator<Map.Entry<String, Object>> it = newMap.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Object> item = it.next();
String key = item.getKey();
Object newValue = item.getValue();
Map<String, Object> differenceMap = new HashMap<>();
int lastIndex = key.lastIndexOf(".");
String lastPath = key.substring(lastIndex + 1).toLowerCase();
if (oldMap.containsKey(key)) {
Object oldValue = oldMap.get(key);
if (newValue.equals(oldValue)) {
oldMap.remove(key);
continue;
} else {
differenceMap.put("oldValue", oldValue);
differenceMap.put("newValue", newValue);
oldMap.put(key, differenceMap);
}
} else {
differenceMap.put("oldValue", "");
differenceMap.put("newValue", newValue);
oldMap.put(key, differenceMap);
}
}
}
/**
* 将已经找出不同数据的map根据key的层级结构封装成json返回
*/
private static String convertMapToJson(Map<String, Object> map) {
JSONObject resultJSONObject = new JSONObject();
for (Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Object> item = it.next();
String key = item.getKey();
Object value = item.getValue();
String[] paths = key.split("\\.");
int i = 0;
Object remarkObject = null;//用於深度標識對象
int indexAll = paths.length - 1;
while (i <= paths.length - 1) {
String path = paths[i];
if (i == 0) {
//初始化对象标识
if (resultJSONObject.containsKey(path)) {
remarkObject = resultJSONObject.get(path);
} else {
if (indexAll > i) {
if (paths[i + 1].matches("\\[[0-9]+\\]")) {
remarkObject = new JSONArray();
} else {
remarkObject = new JSONObject();
}
resultJSONObject.put(path, remarkObject);
} else {
resultJSONObject.put(path, value);
}
}
i++;
continue;
}
if (path.matches("\\[[0-9]+\\]")) {//匹配集合对象
int startIndex = path.lastIndexOf("[");
int endIndext = path.lastIndexOf("]");
int index = Integer.parseInt(path.substring(startIndex + 1, endIndext));
if (indexAll > i) {
if (paths[i + 1].matches("\\[[0-9]+\\]")) {
while (((JSONArray) remarkObject).size() <= index) {
if(((JSONArray) remarkObject).size() == index){
((JSONArray) remarkObject).add(index,new JSONArray());
}else{
((JSONArray) remarkObject).add(null);
}
}
} else {
while(((JSONArray) remarkObject).size() <= index){
if(((JSONArray) remarkObject).size() == index){
((JSONArray) remarkObject).add(index,new JSONObject());
}else{
((JSONArray) remarkObject).add(null);
}
}
}
remarkObject = ((JSONArray) remarkObject).get(index);
} else {
while(((JSONArray) remarkObject).size() <= index){
if(((JSONArray) remarkObject).size() == index){
((JSONArray) remarkObject).add(index, value);
}else{
((JSONArray) remarkObject).add(null);
}
}
}
} else {
if (indexAll > i) {
if (paths[i + 1].matches("\\[[0-9]+\\]")) {
if(!((JSONObject) remarkObject).containsKey(path)){
((JSONObject) remarkObject).put(path, new JSONArray());
}
} else {
if(!((JSONObject) remarkObject).containsKey(path)){
((JSONObject) remarkObject).put(path, new JSONObject());
}
}
remarkObject = ((JSONObject) remarkObject).get(path);
} else {
((JSONObject) remarkObject).put(path, value);
}
}
i++;
}
}
return JSON.toJSONString(resultJSONObject);
}
public static SysDataLogDTO converterDataAuditRecord(OperationDataChange change, Object oldData, Object newData,String dataId,String tag,String userId,String username) throws Exception {
String oldDataStr = JSON.toJSONString(oldData);
String newDataStr = JSON.toJSONString(newData);
String json = campareJsonObject(oldDataStr,newDataStr);
log.info("json:{}",json);
List<ComparisonDTO> diff = compareObj(oldData, newData);
String diffStr = JSON.toJSONString(diff);
log.info("diffStr:{}",diffStr);
SysDataLogDTO dto = new SysDataLogDTO();
dto.setDataContent(diffStr);
dto.setDataTable(change.getTableName());
dto.setDataId(dataId);
dto.setTags(tag);
dto.setMethodName(DataOperateLogThreadLocal.THREADDATA_METHOD.get().toString());
dto.setCreateTime(new Date());
dto.setCreateBy(userId);
dto.setCreatedName(username);
dto.setRemark(change.getWhereSql());
//dto.setId(String.valueOf(IdWorker.getId()));
return dto;
}
}
注解案例
@DataAuditLogging(tag = "产品信息",dataId = "1",currUserId="#input.adminId",currUsername="#input.adminName")