Files
geg-gas-pcitc/itc-pcitc-dependencies/itc-pcitc-dependencies-service/src/main/java/com/pictc/utils/DataLogTools.java

1033 lines
37 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
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<Class<?>,LogTableInfo> annotations = Maps.newHashMap();
private static Map<String,Class<?>> entitys = Maps.newHashMap();
private static Set<String> 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<String> excludeFields = SetUtils.of("id","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>T insert(T entity) {
Class<? extends Object> klazz = entity.getClass();
List<DataChangeLog> 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<LogJoinInfo> 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>T update(T dto) {
Class<? extends Object> 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<DataChangeLog> 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<LogJoinInfo> 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>T delete(T entity) {
Class<? extends Object> klazz = entity.getClass();
List<DataChangeLog> 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<LogJoinInfo> 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<Long> ids) {
for (Long id : ids) {
deleteById(klazz, id);
}
return true;
}
public static <T>T deleteById(Class<T> klazz,Long id) {
LogTableInfo tabInfo = getAnnotation(klazz);
BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType());
T entity = findById(klazz, id);
if(entity==null) return null;
List<DataChangeLog> 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<LogJoinInfo> 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<Long> ids) {
for (Long id : ids) {
enableById(klazz, id);
}
return true;
}
public static <T>T enableById(Class<T> 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<DataChangeLog> logs = CollectionUtils.newArrayList();
BeanUtils.setFieldValue("valid", YesOrNoEnum.YES.getTextCode(), entity);
mapper.updateById(entity);
T dto = (T)tabInfo.toDto(entity);
DataChangeLog datalog = createLog(klazz,OperationType.UPDATE);
datalog.setEntityId(id);
buildFields(datalog,tabInfo,dto,tabInfo.toDto(old));
logs.add(datalog);
saveLogs(tabInfo,logs);
return dto;
}
public static boolean disable(Class<?> klazz, @Valid List<Long> ids) {
for (Long id : ids) {
disableById(klazz, id);
}
return true;
}
public static <T>T disableById(Class<T> 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<DataChangeLog> logs = CollectionUtils.newArrayList();
BeanUtils.setFieldValue("valid",YesOrNoEnum.NO.getTextCode(), entity);
mapper.updateById(entity);
T dto = (T)tabInfo.toDto(entity);
DataChangeLog datalog = createLog(klazz,OperationType.UPDATE);
datalog.setEntityId(id);
buildFields(datalog,tabInfo,dto,tabInfo.toDto(old));
logs.add(datalog);
saveLogs(tabInfo,logs);
return dto;
}
public static <T>T findById(Class<T> klazz,Serializable id){
return findById(klazz,id,false);
}
public static <T>T findById(Class<T> 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<Class<?>> classPath,boolean full){
List<LogJoinInfo> 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<Class<?>> 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<Class<?>> 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<Class<?>> 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<Class<?>> 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 <T>void initJoinValue(T entity,LogTableInfo tabInfo,Set<Class<?>> classes) {
if(classes==null) {
classes = CollectionUtils.newHashSet();
}
if(classes.contains(tabInfo.getEntityType())) return ;
List<LogJoinInfo> 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<Class<?>> 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<Class<?>> joinClasses = SetUtils.ofCollection(classes);
initJoinValue(joinValue,joinTable,joinClasses);
}
}
}
}
private static <T>void buildJoins(List<DataChangeLog> 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 <T>void buildFields(DataChangeLog datalog,LogTableInfo tableInfo,T entity,T old) {
List<LogFieldInfo> 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;
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 logs 日志记录对象
* @return void 返回类型
*/
public static void saveLogs(LogTableInfo tableInfo,List<DataChangeLog> logs) {
String tableName = parseLogTableName(tableInfo);
initTable(tableName);
String sql = parseSql(SQL_INSERT, tableName);
List<List<JdbcParam>> batchParams = CollectionUtils.newArrayList();
for (DataChangeLog log : logs) {
List<JdbcParam> 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<String> tabs = queryListPrimitive(SQL_LOG_TABLE_LIST,String.class, null);
logTables = new HashSet<String>(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<DataChangeLog> 返回类型
*/
public static <T>List<DataChangeLogVo> 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<DataChangeLog> 返回类型
*/
public static <T>List<DataChangeLogVo> findLogsByEntityId(String tableName,@NonNull Long dataId) {
if(initTable(tableName)) {
return null;
}
List<DataChangeLog> result = CollectionUtils.newArrayList();
String sql = parseSql(SQL_LIST, tableName);
JdbcContext context = null;
try {
context = new JdbcContext(true,true);
context.init(sql);
context.setParams(ListUtils.ofArray(JdbcParam.ofLong(dataId)));
ResultSet set = context.executeQuery();
Map<String, String> 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<DataChangeLogVo> toVo(List<DataChangeLog> list){
List<DataChangeLogVo> result = CollectionUtils.newArrayList();
if(CollectionUtils.isNotEmpty(list)) {
Map<String, List<DataChangeLog>> flowGroup = list.stream().collect(Collectors.groupingBy(DataChangeLog::getFlowId));
for (Entry<String, List<DataChangeLog>> entry : flowGroup.entrySet()) {
Map<String, List<DataChangeLog>> 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<DataChangeLogVo>() {
@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<String, List<DataChangeLog>> pidMap){
List<DataChangeLog> 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<FieldChange> 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<JdbcParam> 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<List<JdbcParam>> 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>T queryPrimitive(String sql,Class<T> type,List<JdbcParam> 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 <T>List<T> queryListPrimitive(String sql,Class<T> type,List<JdbcParam> params){
JdbcContext context = null;
try {
context = new JdbcContext(true,true);
context.init(sql);
context.setParams(params);
ResultSet set = context.executeQuery();
List<T> 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();
}
}
}