Files
geg-gas-pcitc/itc-pcitc-dependencies/itc-pcitc-dependencies-service/src/main/java/com/pictc/utils/DataLogTools.java
张秉卓 2a9f0bcd13 修改
2025-12-18 09:57:16 +08:00

1203 lines
40 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 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<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 Set<String> 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>T insert(T entity) {
return insert(entity, new DefaultDataOperationListener<T>());
}
public static <T>T insert(T entity,DataOperationListener<T> listener) {
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());
DataOperationContent<T> 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<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);
}
}
}
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 <T> void saveAttrs(LogTableInfo tabInfo, T entity) {
saveAttrs(tabInfo, entity, false);
}
private static <T> void saveAttrs(LogTableInfo tabInfo, T entity,boolean remove) {
List<LogAttrFieldInfo> 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>T update(T dto) {
return update(dto, new DefaultDataOperationListener<T>());
}
public static <T>T update(T dto,DataOperationListener<T> listener) {
Class<? extends Object> klazz = dto.getClass();
LogTableInfo tabInfo = getAnnotation(klazz);
Long idValue = tabInfo.getIdValue(dto);
BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType());
T old = findById(klazz, idValue);
DataOperationContent<T> content = null;
if(listener!=null) {
content = DataOperationContent.of(tabInfo,OperationType.UPDATE,dto,old);
listener.before(content);
}
try {
if(old!=null) {
List<DataChangeLog> 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<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);
}
}
}
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>T delete(T dto) {
return delete(dto, new DefaultDataOperationListener<T>());
}
public static <T>T delete(T entity,DataOperationListener<T> listener) {
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());
T old = findById(klazz, idValue);
DataOperationContent<T> 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<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);
}
}
}
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>boolean deleteByIds(Class<T> klazz, @Valid List<Long> ids) {
return deleteByIds(klazz, ids, new DefaultDataOperationListener<T>());
}
public static <T>boolean deleteByIds(Class<T> klazz, @Valid List<Long> ids,DataOperationListener<T> listener) {
for (Long id : ids) {
deleteById(klazz, id,listener);
}
return true;
}
public static <T>T deleteById(Class<T> klazz,Long id) {
return deleteById(klazz,id,new DefaultDataOperationListener<T>());
}
public static <T>T deleteById(Class<T> klazz,Long id,DataOperationListener<T> listener) {
LogTableInfo tabInfo = getAnnotation(klazz);
BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType());
T entity = findById(klazz, id);
if(entity==null) return null;
DataOperationContent<T> content = null;
if(listener!=null) {
content = DataOperationContent.of(tabInfo,OperationType.DELETE,entity,null);
listener.before(content);
}
List<DataChangeLog> 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<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);
}
}
}
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 <T>boolean enable(Class<T> klazz,@Valid List<Long> ids) {
return enable(klazz, ids, new DefaultDataOperationListener<T>());
}
public static <T>boolean enable(Class<T> klazz, @Valid List<Long> ids,DataOperationListener<T> listener) {
for (Long id : ids) {
enableById(klazz, id,listener);
}
return true;
}
public static <T>T enableById(Class<T> klazz,Long id) {
return enableById(klazz, id, new DefaultDataOperationListener<T>());
}
public static <T>T enableById(Class<T> klazz,Long id,DataOperationListener<T> 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<T> content = null;
if(listener!=null) {
content = DataOperationContent.of(tabInfo,OperationType.ENABLE,old,old);
listener.before(content);
}
T entity = null;
try {
List<DataChangeLog> 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 <T>boolean disable(Class<T> klazz,@Valid List<Long> ids) {
return disable(klazz, ids, new DefaultDataOperationListener<T>());
}
public static <T>boolean disable(Class<T> klazz, @Valid List<Long> ids,DataOperationListener<T> listener) {
for (Long id : ids) {
disableById(klazz, id,listener);
}
return true;
}
public static <T>T disableById(Class<T> klazz,Long id,DataOperationListener<T> 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<T> content = null;
if(listener!=null) {
content = DataOperationContent.of(tabInfo,OperationType.DISABLE,old,old);
listener.before(content);
}
T entity = null;
try {
List<DataChangeLog> 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>T findById(Class<?> klazz,Serializable id){
return findById(klazz,id,false);
}
public static <T>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<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(item, 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);
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 <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());
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<String, List<DataChangeLog>> pidMap) {
if(!pidMap.containsKey(item.getId())) {
return false;
}
boolean flag = false;
List<DataChangeLog> 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<DataChangeLog> logs) {
String tableName = parseLogTableName(tableInfo);
initTable(tableName);
// String sql = parseSql(SQL_INSERT, tableName);
// List<List<JdbcParam>> batchParams = CollectionUtils.newArrayList();
// Map<String, List<DataChangeLog>> pidMap = logs.stream().collect(Collectors.groupingBy(DataChangeLog::getPid));
//
// for (DataChangeLog item : logs) {
// //没有改变的属性,且没有子表修改时跳过
// if(!item.hasFieldChanges() && !hasChild(item, pidMap)) {
// continue;
// }
//
// List<JdbcParam> 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_timeDate 类型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<String> tabs = logDbService.getTableList();
logTables = new HashSet<String>(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<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;
}
String sql = parseSql(SQL_LIST, tableName);
JdbcContext context = null;
try {
List<DataChangeLog> result = CollectionUtils.newArrayList();
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();
}
}
private static String parseSql(String sql,String tableName) {
return sql.replaceAll(SQL_PLACEHOLDER,tableName);
}
}