package com.pictc.utils; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.Charset; import java.sql.Date; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; 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.esotericsoftware.kryo.kryo5.minlog.Log; import com.google.api.client.util.Sets; import com.pictc.annotations.datalog.JoinCaseType; import com.pictc.annotations.datalog.JoinValueType; import com.pictc.annotations.datalog.LogJoin; import com.pictc.annotations.datalog.LogJoinColumn; import com.pictc.annotations.datalog.ValueDirectionType; import com.pictc.converts.ConverUtil; import com.pictc.datalog.LogFieldInfo; import com.pictc.datalog.LogJoinInfo; import com.pictc.datalog.LogTableInfo; 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.enums.YesOrNoEnum; 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.service.DatalogService; import com.xjrsoft.module.datalog.vo.DataChangeLogVo; import com.xjrsoft.module.datalog.vo.OperationType; import com.xjrsoft.module.organization.dto.UserDto; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.IdUtil; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; @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 String SQL_HAS_TABLE = "select count(*) from log_table_info where tbl_name = ?"; public static final String SQL_INST_TABLE = "INSERT INTO log_table_info (tbl_name) VALUES (?);"; public static final String SQL_LOG_TABLE_LIST = "select tbl_name FROM log_table_info"; public static final String SQL_PLACEHOLDER = "\\$\\{TBL_NAME\\}"; public static final String[] SQL_CREATE_TABLE = { "CREATE TABLE ${TBL_NAME} (\r\n" + " id VARCHAR(32) NOT NULL,\r\n" + " flow_id VARCHAR(32),\r\n" + " pid VARCHAR(32) DEFAULT '#',\r\n" + " entity_class_name VARCHAR(255),\r\n" + " bus_name VARCHAR(100),\r\n" + " entity_id BIGINT,\r\n" + " field_changes TEXT,\r\n" + " operation_type VARCHAR(20),\r\n" + " operator_id BIGINT,\r\n" + " operator_name VARCHAR(100),\r\n" + " operation_time TIMESTAMPTZ,\r\n" + " operation_ip VARCHAR(50),\r\n" + " PRIMARY KEY (id)\r\n" + " );", "COMMENT ON COLUMN ${TBL_NAME}.\"flow_id\" IS '流水id';", "COMMENT ON COLUMN ${TBL_NAME}.\"pid\" IS '父表ID,根节点#';", "COMMENT ON COLUMN ${TBL_NAME}.\"entity_class_name\" IS '实体类名称(全类名)';", "COMMENT ON COLUMN ${TBL_NAME}.\"bus_name\" IS '业务名称';", "COMMENT ON COLUMN ${TBL_NAME}.\"entity_id\" IS '操作的实体ID(主键值)';", "COMMENT ON COLUMN ${TBL_NAME}.\"field_changes\" IS '属性值记录(JSON格式存储字段变更详情)';", "COMMENT ON COLUMN ${TBL_NAME}.\"operation_type\" IS '操作类型:INSERT-新增,UPDATE-修改,DELETE-删除';", "COMMENT ON COLUMN ${TBL_NAME}.\"operator_id\" IS '操作人ID';", "COMMENT ON COLUMN ${TBL_NAME}.\"operator_name\" IS '操作人姓名';", "COMMENT ON COLUMN ${TBL_NAME}.\"operation_time\" IS '操作时间';", "COMMENT ON COLUMN ${TBL_NAME}.\"operation_ip\" IS '操作IP地址';", "CREATE INDEX idx_${TBL_NAME}_enityt_id ON ${TBL_NAME} (entity_id)" }; public static final String SQL_INSERT = "INSERT INTO ${TBL_NAME} (\r\n" + " id, flow_id, pid, entity_class_name, bus_name, \r\n" + " entity_id, field_changes, operation_type, operator_id, \r\n" + " operator_name, operation_time, operation_ip\r\n" + ") VALUES (?,?,?,?,?,?,?,?,?,?,?,?);"; public static final String SQL_LIST = "SELECT * FROM ${TBL_NAME} WHERE entity_id = ? ORDER BY operation_time DESC, flow_id DESC"; public static final Set excludeFields = SetUtils.of("tenantId","dataVersion","createUserId","createDate","modifyUserId","modifyDate","modifyDate","deleteMark"); 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) { 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()); mapper.insert(BeanUtil.toBean(entity,tabInfo.getEntityType())); 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); } } } saveLogs(tabInfo,logs); return entity; } public static T update(T dto) { Class klazz = dto.getClass(); LogTableInfo tabInfo = getAnnotation(klazz); Long idValue = tabInfo.getIdValue(dto); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); Object old = findById(klazz, idValue); if(old!=null) { List logs = CollectionUtils.newArrayList(); DataChangeLog datalog = createLog(klazz,OperationType.UPDATE); initJoinValue(dto,tabInfo,null); mapper.updateById(tabInfo.toEntity(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); } } } saveLogs(tabInfo,logs); }else { throw new OrmException("数据不存在"); } return dto; } public static T delete(T entity) { Class klazz = entity.getClass(); List logs = CollectionUtils.newArrayList(); LogTableInfo tabInfo = getAnnotation(klazz); Long idValue = tabInfo.getIdValue(entity); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); 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); } } } saveLogs(tabInfo,logs); return entity; } public static boolean deleteByIds(Class klazz, @Valid List ids) { for (Long id : ids) { deleteById(klazz, id); } return true; } public static T deleteById(Class klazz,Long id) { LogTableInfo tabInfo = getAnnotation(klazz); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); T entity = findById(klazz, id); if(entity==null) return null; List logs = CollectionUtils.newArrayList(); delete(entity, tabInfo, mapper); 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); } } } saveLogs(tabInfo,logs); 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) { for (Long id : ids) { enableById(klazz, id); } return true; } public static T enableById(Class klazz,Long id) { LogTableInfo tabInfo = getAnnotation(klazz); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); Object entity = findNativeById(klazz, id); if(entity==null) return null; T old = BeanUtils.newInstance(klazz); BeanUtil.copyProperties(entity,old,true); List logs = CollectionUtils.newArrayList(); tabInfo.setFieldValue(entity,"valid",YesOrNoEnum.YES.getTextCode()); mapper.updateById(entity); T dto = (T)tabInfo.toDto(entity); DataChangeLog datalog = createLog(klazz,OperationType.UPDATE); datalog.setEntityId(id); buildFields(datalog,tabInfo,dto,null); logs.add(datalog); saveLogs(tabInfo,logs); return dto; } public static boolean disable(Class klazz, @Valid List ids) { for (Long id : ids) { enableById(klazz, id); } return true; } public static T disableById(Class klazz,Long id) { LogTableInfo tabInfo = getAnnotation(klazz); BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType()); Object entity = findNativeById(klazz, id); if(entity==null) return null; T old = BeanUtils.newInstance(klazz); BeanUtil.copyProperties(entity,old,true); List logs = CollectionUtils.newArrayList(); tabInfo.setFieldValue(entity,"valid",YesOrNoEnum.NO.getTextCode()); mapper.updateById(entity); T dto = (T)tabInfo.toDto(entity); DataChangeLog datalog = createLog(klazz,OperationType.UPDATE); datalog.setEntityId(id); buildFields(datalog,tabInfo,dto,null); logs.add(datalog); saveLogs(tabInfo,logs); return dto; } 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(joinValue, 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); buildFields(datalog,joinTable,item,null); logs.add(datalog); } }else { for (Object item : listValue) { Long idValue = joinTable.getIdValue(item); Object voOjb = BeanUtil.toBean(item,joinTable.getKlazz()); DataChangeLog datalog = createLog(join.getTargetClass(),type,parent); datalog.setEntityId(idValue); delete(voOjb, joinTable, mapper); buildFields(datalog,joinTable,voOjb,null); logs.add(datalog); } } }else { for (Object item : listValue) { Long idValue = joinTable.getIdValue(item); Object nval = BeanUtil.toBean(item,joinTable.getKlazz()); DataChangeLog datalog = createLog(join.getTargetClass(),type,parent); datalog.setEntityId(idValue); Object old = findById(join.getTargetClass(), idValue); mapper.updateById(nval); buildFields(datalog,joinTable,nval,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) { Object voOjb = BeanUtil.toBean(item,joinTable.getKlazz()); Long idValue = joinTable.getIdValue(item); DataChangeLog datalog = createLog(join.getTargetClass(),OperationType.DELETE,parent); datalog.setEntityId(idValue); delete(voOjb, joinTable, mapper); buildFields(datalog,joinTable,voOjb,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); if(type==OperationType.INSERT || type==OperationType.DELETE) { if(type==OperationType.INSERT) { mapper.insert(val); }else { delete(val, joinTable, mapper); } buildFields(datalog,joinTable, val, null); }else { Object old = mapper.selectById(idValue); buildFields(datalog,joinTable,val,old); Object bean = BeanUtil.toBean(val,joinTable.getEntityType()); mapper.updateById(bean); } } else if(type==OperationType.UPDATE || type==OperationType.DELETE){ //进行修改或者删除时,级联对象为空时,需要删除旧数据 Object old = getJoinObj(joinTable, join, mapper, parentInfo, entity); if(old!=null) { Object voOjb = BeanUtil.toBean(old,joinTable.getKlazz()); Long idValue = joinTable.getIdValue(old); DataChangeLog datalog = createLog(join.getTargetClass(),OperationType.DELETE,parent); datalog.setEntityId(idValue); delete(voOjb, joinTable, mapper); buildFields(datalog,joinTable,voOjb,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()); if(old!=null) { Object newVal = BeanUtils.getFieldValue(item.getField(),entity); Object oldVal = BeanUtils.getFieldValue(item.getField(),old); boolean flag = false; if((newVal==null && oldVal!=null) || (newVal!=null && oldVal==null)) { flag = 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 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(); for (DataChangeLog log : logs) { List params = CollectionUtils.newArrayList(); params.add(JdbcParam.of(Types.VARCHAR,String.class, log.getId())); params.add(JdbcParam.of(Types.VARCHAR,String.class, log.getFlowId())); params.add(JdbcParam.of(Types.VARCHAR,String.class, log.getPid())); params.add(JdbcParam.of(Types.VARCHAR,String.class, log.getEntityClassName())); params.add(JdbcParam.of(Types.VARCHAR,String.class, log.getBusName())); params.add(JdbcParam.of(Types.BIGINT,long.class, log.getEntityId())); params.add(JdbcParam.of(Types.VARCHAR,String.class,JSON.toJSONString(log.getFieldChanges()))); params.add(JdbcParam.of(Types.VARCHAR,String.class,log.getOperationType().name())); params.add(JdbcParam.of(Types.VARCHAR,long.class,log.getOperatorId())); params.add(JdbcParam.of(Types.VARCHAR,String.class,log.getOperatorName())); params.add(JdbcParam.of(Types.TIMESTAMP,Timestamp.class,log.getOperationTime())); params.add(JdbcParam.of(Types.VARCHAR,String.class,log.getOperationIp())); batchParams.add(params); } executeBatch(sql, batchParams); } /** * @Description: 初始化日志表 * @param tableName * @return void 返回类型 */ private synchronized static boolean initTable(String tableName) { synchronized (lock) { //如果没有日志表时,重新创建 if(!hasTable(tableName)) { for (int i = 0; i < SQL_CREATE_TABLE.length; i++) { execute(parseSql(SQL_CREATE_TABLE[i], tableName)); } //将表名进行记录 executeUpdate(SQL_INST_TABLE,ListUtils.ofArray(JdbcParam.ofString(tableName))); logTables.add(tableName); log.info("新增数据日志表:{}",tableName); return true; } } return false; } private static String parseSql(String sql,String tableName) { return sql.replaceAll(SQL_PLACEHOLDER,tableName); } private static boolean hasTable(String tableName) { synchronized (lock) { if(logTables==null) { List tabs = queryListPrimitive(SQL_LOG_TABLE_LIST,String.class, null); logTables = new HashSet(tabs); } if(logTables.contains(tableName)) return true; Long count = queryPrimitive(SQL_HAS_TABLE,long.class,Lists.newArrayList(JdbcParam.ofString(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; } List result = CollectionUtils.newArrayList(); String sql = parseSql(SQL_LIST, tableName); JdbcContext context = null; try { context = new JdbcContext(true,false); 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 = new DataChangeLogVo(); result.add(vo); } } return result; } private static DataChangeLogVo toVo(DataChangeLog obj){ DataChangeLogVo vo = new DataChangeLogVo(); vo.setId(obj.getId()); vo.setPid(obj.getPid()); vo.setTableName(obj.getBusName()); vo.setOperationType(obj.getOperationType()); 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(); } } }