package com.pictc.utils; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.IdUtil; import com.alibaba.fastjson.JSON; import com.alibaba.nacos.shaded.com.google.common.collect.Lists; import com.alibaba.nacos.shaded.com.google.common.collect.Maps; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper; import com.pictc.annotations.datalog.*; import com.pictc.converts.ConverUtil; import com.pictc.datalog.*; import com.pictc.enums.BusinessCode; import com.pictc.enums.ExceptionCommonCode; import com.pictc.exceptions.OrmException; import com.pictc.jdbc.JdbcContext; import com.pictc.jdbc.ResultSetUtils; import com.pictc.jdbc.model.JdbcParam; import com.xjrsoft.common.exception.BusinessException; import com.xjrsoft.common.utils.SecureUtil; import com.xjrsoft.module.datalog.entity.DataChangeLog; import com.xjrsoft.module.datalog.entity.json.FieldChange; import com.xjrsoft.module.datalog.mapper.DatalogMapper; import com.xjrsoft.module.datalog.vo.DataChangeLogVo; import com.xjrsoft.module.datalog.vo.OperationType; import com.xjrsoft.module.organization.dto.UserDto; import com.xjrsoft.module.system.client.IFileClient; import com.xjrsoft.module.system.dto.LngFileUploadBindDto; import com.xjrsoft.module.system.dto.UpdateLngFileUploadDto; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.Charset; import java.sql.Date; import java.sql.*; import java.time.*; import java.util.*; import java.util.Map.Entry; import java.util.stream.Collectors; @Slf4j @SuppressWarnings({"unused","rawtypes","unchecked"}) @Component public class DataLogTools { private static Map,LogTableInfo> annotations = Maps.newHashMap(); private static Map> entitys = Maps.newHashMap(); private static Set logTables = null; private static Object lock = new Object(); public static final Set excludeFields = SetUtils.of("id","tenantId","dataVersion","createUserId","createDate","modifyUserId","modifyDate","modifyDate","deleteMark"); public static final String SQL_LIST = "SELECT * FROM ${TBL_NAME} WHERE entity_id = ? ORDER BY operation_time DESC, flow_id DESC"; public static final String SQL_PLACEHOLDER = "\\$\\{TBL_NAME\\}"; private static DatalogMapper logDbService; private static IFileClient fileClient; @Autowired public void setLogDbService(DatalogMapper logDbService) { DataLogTools.logDbService = logDbService; } @Autowired public void setFileClient(IFileClient fileClient) { DataLogTools.fileClient = fileClient; } public static DataChangeLog createLog(Class klazz,OperationType type) { DataChangeLog createLog = createLog(klazz, type, null); return createLog.setFlowId(IdUtil.getSnowflakeNextIdStr()); } public static DataChangeLog createLog(Class klazz,OperationType type,DataChangeLog parent) { UserDto currentUser = SecureUtil.getCurrentUser(); LogTableInfo annotation = getAnnotation(klazz); //变更人的IP地址 HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); String ipAddress = request.getRemoteAddr(); DataChangeLog changeLog = new DataChangeLog() .setEntityClassName(klazz.getCanonicalName()) .setBusName(annotation.getName()) .setId(IdUtil.getSnowflakeNextIdStr()) .setOperationIp(ipAddress) .setOperationType(type) .setOperatorId(currentUser.getId()) .setOperatorName(currentUser.getName()) .setOperationTime(LocalDateTime.now()); if(parent!=null) { changeLog.setFlowId(parent.getFlowId()); changeLog.setPid(parent.getId()); } return changeLog; } public static LogTableInfo getAnnotation(Class klazz) { synchronized (lock) { if(annotations.containsKey(klazz)) { return annotations.get(klazz); } LogTableInfo tableInfo = new LogTableInfo(klazz); if(tableInfo.isValid()) { annotations.put(klazz,tableInfo); return tableInfo; } return null; } } public static T insert(T entity) { return insert(entity, new DefaultDataOperationListener()); } public static T insert(T entity,DataOperationListener listener) { Class klazz = entity.getClass(); List logs = CollectionUtils.newArrayList(); DataChangeLog datalog = createLog(klazz,OperationType.INSERT); LogTableInfo tabInfo = getAnnotation(klazz); initJoinValue(entity,tabInfo,null); Long idValue = tabInfo.getIdValue(entity); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); DataOperationContent content = null; if(listener!=null) { content = DataOperationContent.of(tabInfo,OperationType.INSERT,entity,null); listener.before(content); } try { mapper.insert(BeanUtil.toBean(entity,tabInfo.getEntityType())); saveAttrs(tabInfo,entity); datalog.setEntityId(idValue); buildFields(datalog,tabInfo,entity,null); logs.add(datalog); List joins = tabInfo.getJoins(); if(CollectionUtils.isNotEmpty(joins)) { for (LogJoinInfo join : joins) { if(join.getJoin().caseType()==JoinCaseType.FULL) { buildJoins(logs,datalog,OperationType.INSERT,tabInfo,join,entity); } } } if(listener!=null) { listener.after(content); } saveLogs(tabInfo,logs); } catch (Exception e) { if(listener!=null) { content.setError(e); listener.onError(content); } throw e; }finally { if(listener!=null) { listener.onFinally(content); } } return entity; } private static void saveAttrs(LogTableInfo tabInfo, T entity) { saveAttrs(tabInfo, entity, false); } private static void saveAttrs(LogTableInfo tabInfo, T entity,boolean remove) { List attrs = tabInfo.getAttrs(); if(CollectionUtils.isNotEmpty(attrs)) { for (LogAttrFieldInfo item : attrs) { LngFileUploadBindDto bindDto = new LngFileUploadBindDto(); bindDto.setTableId(tabInfo.getIdValue(entity)); bindDto.setTableName(item.getTableName()); bindDto.setColumnName(item.getField().getName()); bindDto.setRemove(remove); if(item.isList()) { bindDto.setFiles(tabInfo.getFieldValue(entity,item.getField())); }else { UpdateLngFileUploadDto vo = tabInfo.getFieldValue(entity,item.getField()); if(vo!=null) { bindDto.setFiles(Lists.newArrayList(vo)); } } fileClient.bindTableData(bindDto); } } } public static T update(T dto) { return update(dto, new DefaultDataOperationListener()); } public static T update(T dto,DataOperationListener listener) { Class klazz = dto.getClass(); LogTableInfo tabInfo = getAnnotation(klazz); Long idValue = tabInfo.getIdValue(dto); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); T old = findById(klazz, idValue); DataOperationContent content = null; if(listener!=null) { content = DataOperationContent.of(tabInfo,OperationType.UPDATE,dto,old); listener.before(content); } try { if(old!=null) { List logs = CollectionUtils.newArrayList(); DataChangeLog datalog = createLog(klazz,OperationType.UPDATE); initJoinValue(dto,tabInfo,null); mapper.updateById(tabInfo.toEntity(dto)); saveAttrs(tabInfo,dto); datalog.setEntityId(idValue); buildFields(datalog,tabInfo,dto,old); logs.add(datalog); List joins = tabInfo.getJoins(); if(CollectionUtils.isNotEmpty(joins)) { for (LogJoinInfo join : joins) { if(join.getJoin().caseType()==JoinCaseType.FULL) { buildJoins(logs,datalog,OperationType.UPDATE,tabInfo,join,dto); } } } if(listener!=null) { listener.after(content); } saveLogs(tabInfo,logs); }else { throw new OrmException("数据不存在"); } } catch (Exception e) { if(listener!=null) { content.setError(e); listener.onError(content); } throw e; } finally { if(listener!=null) { listener.onFinally(content); } } return dto; } public static T delete(T dto) { return delete(dto, new DefaultDataOperationListener()); } public static T delete(T entity,DataOperationListener listener) { Class klazz = entity.getClass(); List logs = CollectionUtils.newArrayList(); LogTableInfo tabInfo = getAnnotation(klazz); Long idValue = tabInfo.getIdValue(entity); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); T old = findById(klazz, idValue); DataOperationContent content = null; if(listener!=null) { content = DataOperationContent.of(tabInfo,OperationType.DELETE,old,null); listener.before(content); } try { DataChangeLog datalog = createLog(klazz,OperationType.DELETE); datalog.setEntityId(idValue); buildFields(datalog,tabInfo,entity,null); logs.add(datalog); delete(entity, tabInfo, mapper); List joins = tabInfo.getJoins(); if(CollectionUtils.isNotEmpty(joins)) { for (LogJoinInfo join : joins) { if(join.getJoin().caseType()==JoinCaseType.FULL) { buildJoins(logs,datalog,OperationType.DELETE,tabInfo,join,entity); } } } if(listener!=null) { listener.after(content); } saveLogs(tabInfo,logs); } catch (Exception e) { if(listener!=null) { content.setError(e); listener.onError(content); } throw e; }finally { if(listener!=null) { listener.onFinally(content); } } return entity; } public static boolean deleteByIds(Class klazz, @Valid List ids) { return deleteByIds(klazz, ids, new DefaultDataOperationListener()); } public static boolean deleteByIds(Class klazz, @Valid List ids,DataOperationListener listener) { for (Long id : ids) { deleteById(klazz, id,listener); } return true; } public static T deleteById(Class klazz,Long id) { return deleteById(klazz,id,new DefaultDataOperationListener()); } public static T deleteById(Class klazz,Long id,DataOperationListener listener) { LogTableInfo tabInfo = getAnnotation(klazz); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); T entity = findById(klazz, id); if(entity==null) return null; DataOperationContent content = null; if(listener!=null) { content = DataOperationContent.of(tabInfo,OperationType.DELETE,entity,null); listener.before(content); } List logs = CollectionUtils.newArrayList(); try { delete(entity, tabInfo, mapper); saveAttrs(tabInfo,entity,true); DataChangeLog datalog = createLog(klazz,OperationType.DELETE); datalog.setEntityId(id); buildFields(datalog,tabInfo,entity,null); logs.add(datalog); List joins = tabInfo.getJoins(); if(CollectionUtils.isNotEmpty(joins)) { for (LogJoinInfo join : joins) { if(join.getJoin().caseType()==JoinCaseType.FULL) { buildJoins(logs,datalog,OperationType.DELETE,tabInfo,join,entity); } } } if(listener!=null) { listener.after(content); } saveLogs(tabInfo,logs); } catch (Exception e) { if(listener!=null) { content.setError(e); listener.onError(content); } throw e; }finally { if(listener!=null) { listener.onFinally(content); } } return entity; } private static void delete(Object entity,LogTableInfo tableinfo,BaseMapper mapper){ Long idValue = tableinfo.getIdValue(entity); if(idValue!=null && idValue > 0) { mapper.deleteById(idValue); }else { throw new BusinessException(BusinessCode.ofArgs(ExceptionCommonCode.DATA_DEL_ID_IS_NULL,tableinfo.getTableName())); } } public static boolean enable(Class klazz,@Valid List ids) { return enable(klazz, ids, new DefaultDataOperationListener()); } public static boolean enable(Class klazz, @Valid List ids,DataOperationListener listener) { for (Long id : ids) { enableById(klazz, id,listener); } return true; } public static T enableById(Class klazz,Long id) { return enableById(klazz, id, new DefaultDataOperationListener()); } public static T enableById(Class klazz,Long id,DataOperationListener listener) { LogTableInfo tabInfo = getAnnotation(klazz); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); T old = findById(klazz, id); if(old==null || tabInfo.isEnable(old)) return null; DataOperationContent content = null; if(listener!=null) { content = DataOperationContent.of(tabInfo,OperationType.ENABLE,old,old); listener.before(content); } T entity = null; try { List logs = CollectionUtils.newArrayList(); entity = findById(klazz, id); content.setObj(entity); DataChangeLog datalog = createLog(klazz,OperationType.ENABLE); datalog.setEntityId(id); buildFields(datalog,tabInfo,entity,old); logs.add(datalog); if(listener!=null) { listener.after(content); } saveLogs(tabInfo,logs); } catch (Exception e) { if(listener!=null) { content.setError(e); listener.onError(content); } throw e; }finally { if(listener!=null) { listener.onFinally(content); } } return entity; } public static boolean disable(Class klazz,@Valid List ids) { return disable(klazz, ids, new DefaultDataOperationListener()); } public static boolean disable(Class klazz, @Valid List ids,DataOperationListener listener) { for (Long id : ids) { disableById(klazz, id,listener); } return true; } public static T disableById(Class klazz,Long id,DataOperationListener listener) { LogTableInfo tabInfo = getAnnotation(klazz); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); T old = findById(klazz, id); if(old==null || tabInfo.isDisable(old)) return null; DataOperationContent content = null; if(listener!=null) { content = DataOperationContent.of(tabInfo,OperationType.DISABLE,old,old); listener.before(content); } T entity = null; try { List logs = CollectionUtils.newArrayList(); entity = findById(klazz, id); content.setObj(entity); DataChangeLog datalog = createLog(klazz,OperationType.DISABLE); datalog.setEntityId(id); buildFields(datalog,tabInfo,entity,old); logs.add(datalog); if(listener!=null) { listener.after(content); } saveLogs(tabInfo,logs); } catch (Exception e) { if(listener!=null) { content.setError(e); listener.onError(content); } throw e; }finally { if(listener!=null) { listener.onFinally(content); } } return entity; } public static T findById(Class klazz,Serializable id){ return findById(klazz,id,false); } public static T findById(Class klazz,Serializable id,boolean full){ LogTableInfo tabInfo = getAnnotation(klazz); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); Object old = mapper.selectById(id); if(old==null) return null; T dto = (T) tabInfo.toDto(old); initJoinValues(tabInfo,dto,SetUtils.of(klazz), full); return dto; } private static Object findNativeById(Class klazz, Serializable id) { LogTableInfo tabInfo = getAnnotation(klazz); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); return mapper.selectById(id); } public static void initJoinValues(LogTableInfo tabInfo,Object entity,Set> classPath,boolean full){ List joins = tabInfo.getJoins(); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); if(CollectionUtils.isNotEmpty(joins)) { for (LogJoinInfo join : joins) { LogTableInfo joinTable = join.getTableInfo(); if(full) { if(join.isList()) { List joinList = getJoinList(joinTable,join, mapper,tabInfo,entity); if(CollectionUtils.isNotEmpty(joinList)) { List listVal = new ArrayList(); for (Object item : joinList) { Object voOjb = BeanUtil.toBean(item,joinTable.getKlazz()); if(!classPath.contains(joinTable.getKlazz())) { Set> classes = SetUtils.ofCollection(classPath); classes.add(joinTable.getKlazz()); initJoinValues(joinTable,voOjb,classes,full); } listVal.add(voOjb); } tabInfo.setFieldValue(entity,join.getField(),listVal); } }else { Object objVal = getJoinObj(joinTable,join, mapper,tabInfo,entity); Object voOjb = BeanUtil.toBean(objVal,joinTable.getKlazz()); if(!classPath.contains(joinTable.getKlazz())) { Set> classes = SetUtils.ofCollection(classPath); classes.add(joinTable.getKlazz()); initJoinValues(joinTable,voOjb,classes,full); } tabInfo.setFieldValue(entity,join.getField(),voOjb); } }else { if(join.getJoin().caseType()==JoinCaseType.FULL) { if(join.isList()) { List joinList = getJoinList(joinTable,join, mapper,tabInfo,entity); if(CollectionUtils.isNotEmpty(joinList)) { List listVal = new ArrayList(); for (Object item : joinList) { Object voOjb = BeanUtil.toBean(item,joinTable.getKlazz()); if(!classPath.contains(joinTable.getKlazz())) { Set> classes = SetUtils.ofCollection(classPath); classes.add(joinTable.getKlazz()); initJoinValues(joinTable,voOjb,classes,full); } listVal.add(voOjb); } tabInfo.setFieldValue(entity,join.getField(),listVal); } }else { Object objVal = getJoinObj(joinTable,join, mapper,tabInfo,entity); Object voOjb = BeanUtil.toBean(objVal,joinTable.getKlazz()); if(!classPath.contains(joinTable.getKlazz())) { Set> classes = SetUtils.ofCollection(classPath); classes.add(joinTable.getKlazz()); initJoinValues(joinTable,voOjb,classes,full); } tabInfo.setFieldValue(entity,join.getField(),voOjb); } } } classPath.add(joinTable.getKlazz()); } } } private static void initJoinValue(T entity,LogTableInfo tabInfo,Set> classes) { if(classes==null) { classes = CollectionUtils.newHashSet(); } if(classes.contains(tabInfo.getEntityType())) return ; List joins = tabInfo.getJoins(); Long idValue = tabInfo.getIdValue(entity); if(idValue == null || idValue <=0) { idValue = IdWorker.getId(); tabInfo.setIdValue(entity,idValue); } classes.add(tabInfo.getEntityType()); if(CollectionUtils.isNotEmpty(joins)) { for (LogJoinInfo join : joins) { LogJoinColumn[] columns = join.getJoin().columns(); LogTableInfo joinTable = join.getTableInfo(); if(classes.contains(joinTable.getEntityType())) { continue; } Object joinValue = tabInfo.getFieldValue(entity, join.getField()); if(joinValue!=null && joinValue instanceof List) { List listValue = (List) joinValue; for (LogJoinColumn joinColumn : columns) { if (joinColumn.valueType() == JoinValueType.FEILD && joinColumn.valueDirection() == ValueDirectionType.RIGHT) { Object joinFieldValue = tabInfo.getFieldValue(entity, joinColumn.field()); for (Object item : listValue) { Long idValue2 = joinTable.getIdValue(item); if(idValue2==null || idValue2 <=0) { joinTable.setIdValue(item, IdWorker.getId()); } joinTable.setFieldValue(item, joinColumn.relatedField(), joinFieldValue); Set> joinClasses = SetUtils.ofCollection(classes); initJoinValue(item,joinTable,joinClasses); } } } }else { Long idValue2 = joinTable.getIdValue(joinValue); if(idValue2==null || idValue2 <=0) { joinTable.setIdValue(joinValue,idValue2); } for (LogJoinColumn joinColumn : columns) { if(joinColumn.valueType()==JoinValueType.FEILD && joinColumn.valueDirection()==ValueDirectionType.LEFT) { Object joinFieldValue = joinTable.getFieldValue(joinValue, joinColumn.relatedField()); tabInfo.setFieldValue(entity, joinColumn.field(),joinFieldValue); }else { Object joinFieldValue = tabInfo.getFieldValue(entity, joinColumn.field()); joinTable.setFieldValue(joinValue, joinColumn.relatedField(), joinFieldValue); } } Set> joinClasses = SetUtils.ofCollection(classes); initJoinValue(joinValue,joinTable,joinClasses); } } } } private static void buildJoins(List logs,DataChangeLog parent,OperationType type,LogTableInfo parentInfo, LogJoinInfo join, T entity) { LogTableInfo joinTable = join.getTableInfo(); BaseMapper mapper = MybatisTools.getMapper(joinTable.getEntityType()); if(joinTable!=null) { if(join.isList()) { List listValue = BeanUtils.getFieldValue(join.getField(),entity); if(CollectionUtils.isNotEmpty(listValue)) { if(type==OperationType.INSERT || type==OperationType.DELETE) { if(type==OperationType.INSERT) { for (Object item : listValue) { Long idValue = joinTable.getIdValue(item); DataChangeLog datalog = createLog(join.getTargetClass(),type,parent); datalog.setEntityId(idValue); Object bean = BeanUtil.toBean(item,joinTable.getEntityType()); mapper.insert(bean); saveAttrs(joinTable,item); buildFields(datalog,joinTable,item,null); logs.add(datalog); } }else { for (Object item : listValue) { Long idValue = joinTable.getIdValue(item); DataChangeLog datalog = createLog(join.getTargetClass(),type,parent); datalog.setEntityId(idValue); Object pojo = BeanUtil.toBean(item,joinTable.getKlazz()); delete(pojo, joinTable, mapper); saveAttrs(joinTable,item,true); buildFields(datalog,joinTable,item,null); logs.add(datalog); } } }else { for (Object item : listValue) { Long idValue = joinTable.getIdValue(item); DataChangeLog datalog = createLog(join.getTargetClass(),type,parent); datalog.setEntityId(idValue); Object old = findById(join.getTargetClass(), idValue); Object pojo = BeanUtil.toBean(item,joinTable.getKlazz()); mapper.updateById(pojo); saveAttrs(joinTable,item); buildFields(datalog,joinTable,item,old); logs.add(datalog); } } } else if(type==OperationType.UPDATE || type==OperationType.DELETE){ //进行修改或者删除时,级联对象为空时,需要删除旧数据 List oldList = getJoinList(joinTable, join, mapper, parentInfo, entity); if(oldList!=null) { for (Object item : oldList) { Long idValue = joinTable.getIdValue(item); DataChangeLog datalog = createLog(join.getTargetClass(),OperationType.DELETE,parent); datalog.setEntityId(idValue); Object pojo = BeanUtil.toBean(item,joinTable.getKlazz()); delete(pojo, joinTable, mapper); saveAttrs(joinTable,item,true); buildFields(datalog,joinTable,item,null); logs.add(datalog); } } } } else { Object val = BeanUtils.getFieldValue(join.getField(),entity); if(val!=null) { Long idValue = joinTable.getIdValue(val); DataChangeLog datalog = createLog(join.getTargetClass(),type,parent); logs.add(datalog); datalog.setEntityId(idValue); Object pojo = BeanUtil.toBean(val,joinTable.getKlazz()); if(type==OperationType.INSERT || type==OperationType.DELETE) { if(type==OperationType.INSERT) { mapper.insert(pojo); saveAttrs(joinTable,val); }else { delete(pojo, joinTable, mapper); saveAttrs(joinTable,val,true); } buildFields(datalog,joinTable, val, null); }else { Object old = mapper.selectById(idValue); mapper.updateById(pojo); saveAttrs(joinTable,val,true); buildFields(datalog,joinTable,val,old); } } else if(type==OperationType.UPDATE || type==OperationType.DELETE){ //进行修改或者删除时,级联对象为空时,需要删除旧数据 Object old = getJoinObj(joinTable, join, mapper, parentInfo, entity); if(old!=null) { Object dto = BeanUtil.toBean(old,joinTable.getKlazz()); Long idValue = joinTable.getIdValue(old); DataChangeLog datalog = createLog(join.getTargetClass(),OperationType.DELETE,parent); datalog.setEntityId(idValue); delete(dto, joinTable, mapper); saveAttrs(joinTable,dto,true); buildFields(datalog,joinTable,dto,null); } } } } } private static List getJoinList(LogTableInfo joinTable, LogJoinInfo join, BaseMapper mapper, LogTableInfo parentInfo,Object parent) { LogJoin joinAnnotation = join.getJoin(); LogJoinColumn[] columns = joinAnnotation.columns(); QueryChainWrapper queryWrapper = new QueryChainWrapper(mapper); for (LogJoinColumn joinCol : columns) { if(joinCol.valueType()==JoinValueType.FEILD) { Object value = parentInfo.getFieldValue(parent,joinCol.field()); if(value==null) return null; queryWrapper.eq(joinTable.getColumn(joinCol.relatedField()),value); }else { if(StringUtils.isNotEmpty(joinCol.relatedField())) { queryWrapper.eq(joinTable.getColumn(joinCol.relatedField()),ConverUtil.jsonToValue(joinCol.staticType(),joinCol.staticValue())); } } } return mapper.selectList(queryWrapper); } private static Object getJoinObj(LogTableInfo joinTable,LogJoinInfo join,BaseMapper mapper,LogTableInfo parentInfo,Object parent) { LogJoin joinAnnotation = join.getJoin(); LogJoinColumn[] columns = joinAnnotation.columns(); QueryChainWrapper query = new QueryChainWrapper(mapper); for (LogJoinColumn joinCol : columns) { if(joinCol.valueType()==JoinValueType.FEILD) { Object value = parentInfo.getFieldValue(parent,joinCol.field()); if(value==null) return null; query.eq(joinTable.getColumn(joinCol.relatedField()),value); }else { if(StringUtils.isNotEmpty(joinCol.relatedField())) { query.eq(joinTable.getColumn(joinCol.relatedField()),ConverUtil.jsonToValue(joinCol.staticType(),joinCol.staticValue())); } } } return mapper.selectObjs(query); } private static void buildFields(DataChangeLog datalog,LogTableInfo tableInfo,T entity,T old) { List fields = tableInfo.getFields(); for (LogFieldInfo item : fields) { if(excludeFields.contains(item.getFieldName())) { continue; } FieldChange change = new FieldChange(); change.setField(item.getFieldName()); change.setName(item.getName()); Object newVal = BeanUtils.getFieldValue(item.getField(),entity); if(old!=null) { Object oldVal = BeanUtils.getFieldValue(item.getField(),old); boolean flag = false; boolean isString = String.class.equals(item.getJavaType()); if(newVal==null && oldVal!=null) { flag = isString?StringUtils.isNotEmpty((String)oldVal):true; }else if(newVal!=null && oldVal==null) { flag = isString?StringUtils.isNotEmpty((String)newVal):true; }else if(newVal!=null && oldVal!=null && !newVal.equals(oldVal)) { flag = true; } change.setOldValue(parseValue(oldVal,item.getJavaType())); change.setNewValue(parseValue(newVal,item.getJavaType())); if(flag) { datalog.addFiledChnage(change); } }else { change.setNewValue(parseValue(BeanUtils.getFieldValue(item.getField(),entity),item.getField().getType())); datalog.addFiledChnage(change); } } } private static String parseValue(Object val,Class type) { if(val==null) return null; if (type.equals(String.class)) { return String.class.cast(val); } else if (type.equals(BigDecimal.class)) { return BigDecimal.class.cast(val).toString(); } else if (type.equals(BigInteger.class)) { return BigInteger.class.cast(val).toString(); } else if (type.equals(Boolean.class) || type.equals(Boolean.TYPE) || type.equals(Integer.class) || type.equals(Integer.TYPE) || type.equals(Long.class) || type.equals(Long.TYPE) || type.equals(Float.class) || type.equals(Float.TYPE) || type.equals(Double.class) || type.equals(Double.TYPE) ) { return val.toString(); } else if (type.equals(byte[].class)) { byte[] buf = (byte[])val; return new String(buf, Charset.forName("UTF-8")); } else if (type.equals(Date.class) || type.equals(Time.class) || type.equals(Timestamp.class) || type.equals(java.util.Date.class)) { String fmt = null; if(type.equals(Date.class)) { fmt = DateUtils.GENERAL; }else if(type.equals(Time.class)) { fmt = DateUtils.FMT_TIME; }else { fmt = DateUtils.LONG_FORMAT; } return DateUtils.format(java.util.Date.class.cast(val),fmt); }else if (type.equals(LocalDate.class)) { return DateUtils.format(LocalDate.class.cast(val)); } else if (type.equals(LocalDateTime.class)) { return DateUtils.format(LocalDateTime.class.cast(val)); } else if (type.equals(LocalTime.class)) { return DateUtils.format(LocalDateTime.class.cast(val)); } else if (type.equals(OffsetDateTime.class)) { return DateUtils.format(OffsetDateTime.class.cast(val)); } else if (type.equals(OffsetTime.class)) { return DateUtils.format(OffsetTime.class.cast(val)); }else if(type.isEnum()) { return val.toString(); } return null; } /** * @Description: 判断是否有子表数据日志 * @param item * @param pidMap * @return boolean 返回类型 */ private static boolean hasChild(DataChangeLog item,Map> pidMap) { if(!pidMap.containsKey(item.getId())) { return false; } boolean flag = false; List childs = pidMap.get(item.getId()); for (DataChangeLog child : childs) { if(child.hasFieldChanges() || hasChild(child,pidMap)) { flag = true; break; } } return flag; } /** * @Description: 保存数据日志 * @param logs 日志记录对象 * @return void 返回类型 */ public static void saveLogs(LogTableInfo tableInfo,List logs) { String tableName = parseLogTableName(tableInfo); initTable(tableName); // String sql = parseSql(SQL_INSERT, tableName); // List> batchParams = CollectionUtils.newArrayList(); // Map> pidMap = logs.stream().collect(Collectors.groupingBy(DataChangeLog::getPid)); // // for (DataChangeLog item : logs) { // //没有改变的属性,且没有子表修改时跳过 // if(!item.hasFieldChanges() && !hasChild(item, pidMap)) { // continue; // } // // List params = CollectionUtils.newArrayList(); // params.add(JdbcParam.of(Types.VARCHAR,String.class, item.getId())); // params.add(JdbcParam.of(Types.VARCHAR,String.class, item.getFlowId())); // params.add(JdbcParam.of(Types.VARCHAR,String.class, item.getPid())); // params.add(JdbcParam.of(Types.VARCHAR,String.class, item.getEntityClassName())); // params.add(JdbcParam.of(Types.VARCHAR,String.class, item.getBusName())); // params.add(JdbcParam.of(Types.BIGINT,long.class, item.getEntityId())); // params.add(JdbcParam.of(Types.VARCHAR,String.class,JSON.toJSONString(item.getFieldChanges()))); // params.add(JdbcParam.of(Types.VARCHAR,String.class,item.getOperationType().name())); // params.add(JdbcParam.of(Types.VARCHAR,long.class,item.getOperatorId())); // params.add(JdbcParam.of(Types.VARCHAR,String.class,item.getOperatorName())); // params.add(JdbcParam.of(Types.TIMESTAMP,Timestamp.class,item.getOperationTime())); // params.add(JdbcParam.of(Types.VARCHAR,String.class,item.getOperationIp())); // batchParams.add(params); // } // executeBatch(sql, batchParams); for (DataChangeLog item : logs) { if(item.hasFieldChanges()) { logDbService.insertDataLog(tableName,item.getId(), item.getFlowId(), // 2. flow_id item.getPid(), // 3. pid item.getEntityClassName(), // 4. entity_class_name item.getBusName(), // 5. bus_name item.getEntityId(), // 6. entity_id JSON.toJSONString(item.getFieldChanges()), // 7. field_changes(可传 JSON 字符串) item.getOperationType().name(), // 8. operation_type(如 "INSERT/UPDATE/DELETE") item.getOperatorId(), // 9. operator_id item.getOperatorName(), // 10. operator_name item.getOperationTime(), // 11. operation_time(Date 类型,MyBatis 自动转数据库时间类型) item.getOperationIp()); } } } /** * @Description: 初始化日志表 * @param tableName * @return void 返回类型 */ private synchronized static boolean initTable(String tableName) { synchronized (lock) { //如果没有日志表时,重新创建 if(!hasTable(tableName)) { logDbService.createDatalogTable(tableName); logDbService.addTableInfo(tableName); logTables.add(tableName); log.info("新增数据日志表:{}",tableName); return true; } } return false; } private static boolean hasTable(String tableName) { synchronized (lock) { if(logTables==null) { List tabs = logDbService.getTableList(); logTables = new HashSet(tabs); } if(logTables.contains(tableName)) return true; Long count = logDbService.getCountByTableName(tableName); boolean flag = count!=null && count > 0; if(flag) { logTables.add(tableName); } return flag; } } /** * @Description: TODO(这里用一句话描述这个方法的作用) * @param clazz * @param dataId * @return List 返回类型 */ public static List findLogsByEntityId(Class clazz,@NonNull Long dataId) { LogTableInfo tabInfo = getAnnotation(clazz); String tableName = parseLogTableName(tabInfo); return findLogsByEntityId(tableName, dataId); } /** * @Description: 根据数据id获取操作日志 * @param tableName 表名 * @param dataId 数据id * @return List 返回类型 */ public static List findLogsByEntityId(String tableName,@NonNull Long dataId) { if(initTable(tableName)) { return null; } String sql = parseSql(SQL_LIST, tableName); JdbcContext context = null; try { List result = CollectionUtils.newArrayList(); context = new JdbcContext(true,true); context.init(sql); context.setParams(ListUtils.ofArray(JdbcParam.ofLong(dataId))); ResultSet set = context.executeQuery(); Map names = ResultSetUtils.getMapColumns(set); while (set.next()) { DataChangeLog log = new DataChangeLog(); log.setId(set.getString("id")); log.setFlowId(set.getString("flow_id")); log.setPid(set.getString("pid")); log.setEntityClassName(set.getString("entity_class_name")); log.setBusName(set.getString("bus_name")); log.setEntityId(set.getLong("entity_id")); log.setOperationType(OperationType.valueOf(set.getString("operation_type"))); log.setOperationIp(set.getString("operation_ip")); log.setOperatorId(set.getLong("operator_id")); log.setOperatorName(set.getString("operator_name")); log.setOperationTime(ResultSetUtils.getObject(set,"operation_time",LocalDateTime.class)); String json = set.getString("field_changes"); if(StringUtils.isNotEmpty(json)) { log.setFieldChanges(JSON.parseArray(json).toJavaList(FieldChange.class)); } result.add(log); } return toVo(result); } catch (SQLException e) { throw context.createException(e); } finally { context.end(); } } private static List toVo(List list){ List result = CollectionUtils.newArrayList(); if(CollectionUtils.isNotEmpty(list)) { Map> flowGroup = list.stream().collect(Collectors.groupingBy(DataChangeLog::getFlowId)); for (Entry> entry : flowGroup.entrySet()) { Map> pidMap = entry.getValue().stream().collect(Collectors.groupingBy(DataChangeLog::getPid)); DataChangeLog root = pidMap.get("#").get(0); DataChangeLogVo vo = toVo(root); addChildVo(root,vo,pidMap); result.add(vo); } } Collections.sort(result,new Comparator() { @Override public int compare(DataChangeLogVo o1, DataChangeLogVo o2) { return Long.valueOf(o2.getSort()-o1.getSort()).intValue(); } }); return result; } private static void addChildVo(DataChangeLog current,DataChangeLogVo parentVo,Map> pidMap){ List childs = pidMap.get(current.getId()); if(CollectionUtils.isNotEmpty(childs)) { for (DataChangeLog item : childs) { DataChangeLogVo logVo = toVo(item,parentVo.getId()); parentVo.addChild(logVo); addChildVo(item, logVo, pidMap); } } } private static DataChangeLogVo toVo(DataChangeLog obj){ return toVo(obj, obj.getPid()); } private static long toMills(LocalDateTime datetime) { ZonedDateTime systemZoned = datetime.atZone(ZoneId.systemDefault()); return systemZoned.toInstant().toEpochMilli(); } private static DataChangeLogVo toVo(DataChangeLog obj,String pid){ DataChangeLogVo vo = new DataChangeLogVo(); vo.setId(obj.getId()); vo.setPid(pid); vo.setTableName(obj.getBusName()); vo.setOperationType(obj.getOperationType()); vo.setOperatorName(obj.getOperatorName()); vo.setOperationIp(obj.getOperationIp()); vo.setOperationTime(DateUtils.format(obj.getOperationTime())); vo.setSort(toMills(obj.getOperationTime())); List fieldChanges = obj.getFieldChanges(); for (FieldChange fieldChange : fieldChanges) { DataChangeLogVo fvo = new DataChangeLogVo(); fvo.setId(obj.getId()+"_"+fieldChange.getField()); fvo.setPid(obj.getId()); fvo.setName(fieldChange.getName()); fvo.setOldValue(fieldChange.getOldValue()); fvo.setNewValue(fieldChange.getNewValue()); fvo.setOperationType(obj.getOperationType()); fvo.setOperatorName(obj.getOperatorName()); fvo.setOperationIp(obj.getOperationIp()); fvo.setOperationTime(DateUtils.format(obj.getOperationTime())); vo.addChild(fvo); } return vo; } private static String parseLogTableName(LogTableInfo tableInfo) { String name = tableInfo.getTableName(); return "datalog_"+name; } /** * 执行ddl语句 * */ public static boolean execute(String sql){ JdbcContext context = null; try { context = new JdbcContext(true,true); context.init(sql); return context.execute(); } catch (SQLException e) { throw context.createException(e); }finally { context.end(); } } /** * 修改操作 * */ public static int executeUpdate(String sql,List params){ JdbcContext context = null; try { context = new JdbcContext(true,true); context.init(sql); context.setParams(params); return context.executeUpdate(); } catch (SQLException e) { throw context.createException(e); }finally { context.end(); } } /** * 批量操作 * */ public static int executeBatch(String sql,List> batchParams){ JdbcContext context = null; try { context = new JdbcContext(true,true); context.init(sql); context.setBatchParams(batchParams); return context.executeBatch(-1); } catch (SQLException e) { throw context.createException(e); }finally { context.end(); } } /** * 查询基础数据类型 * */ public static T queryPrimitive(String sql,Class type,List params){ JdbcContext context = null; try { context = new JdbcContext(true,true); context.init(sql); context.setParams(params); ResultSet set = context.executeQuery(); while (set.next()) { return ResultSetUtils.getObject(set,1,type); } } catch (SQLException e) { throw context.createException(e); }finally { context.end(); } return null; } /** * 查询基础数据类型 * */ public static List queryListPrimitive(String sql,Class type,List params){ JdbcContext context = null; try { context = new JdbcContext(true,true); context.init(sql); context.setParams(params); ResultSet set = context.executeQuery(); List res = CollectionUtils.newArrayList(); while (set.next()) { res.add(ResultSetUtils.getObject(set,1,type)); } return res; } catch (SQLException e) { throw context.createException(e); }finally { context.end(); } } private static String parseSql(String sql,String tableName) { return sql.replaceAll(SQL_PLACEHOLDER,tableName); } }