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

980 lines
35 KiB
Java
Raw Normal View History

2025-10-10 09:20:48 +08:00
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;
2025-10-22 10:42:00 +08:00
import java.util.Map.Entry;
2025-10-10 09:20:48 +08:00
import java.util.Objects;
import java.util.Set;
2025-10-22 10:42:00 +08:00
import java.util.function.Function;
import java.util.stream.Collectors;
2025-10-10 09:20:48 +08:00
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;
2025-10-21 17:28:41 +08:00
import com.pictc.enums.BusinessCode;
import com.pictc.enums.ExceptionCommonCode;
2025-10-10 09:20:48 +08:00
import com.pictc.exceptions.OrmException;
import com.pictc.jdbc.JdbcContext;
import com.pictc.jdbc.ResultSetUtils;
import com.pictc.jdbc.model.JdbcParam;
2025-10-21 17:28:41 +08:00
import com.xjrsoft.common.enums.YesOrNoEnum;
import com.xjrsoft.common.exception.BusinessException;
2025-10-10 09:20:48 +08:00
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;
2025-10-22 10:42:00 +08:00
import com.xjrsoft.module.datalog.vo.DataChangeLogVo;
2025-10-10 09:20:48 +08:00
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();
2025-10-20 17:00:33 +08:00
private static Map<String,Class<?>> entitys = Maps.newHashMap();
2025-10-10 09:20:48 +08:00
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";
2025-10-21 17:28:41 +08:00
public static final Set<String> excludeFields = SetUtils.of("tenantId","dataVersion","createUserId","createDate","modifyUserId","modifyDate","modifyDate","deleteMark");
2025-10-10 09:20:48 +08:00
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;
}
2025-10-21 17:28:41 +08:00
public static boolean deleteByIds(Class<?> klazz, @Valid List<Long> ids) {
for (Long id : ids) {
deleteById(klazz, id);
}
return true;
}
2025-10-10 09:20:48 +08:00
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;
}
2025-10-21 17:28:41 +08:00
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) {
2025-10-10 09:20:48 +08:00
for (Long id : ids) {
2025-10-21 17:28:41 +08:00
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();
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<Long> ids) {
for (Long id : ids) {
enableById(klazz, id);
2025-10-10 09:20:48 +08:00
}
return true;
}
2025-10-21 17:28:41 +08:00
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();
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;
}
2025-10-10 09:20:48 +08:00
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;
}
2025-10-21 17:28:41 +08:00
2025-10-10 09:20:48 +08:00
2025-10-21 17:28:41 +08:00
private static Object findNativeById(Class<?> klazz, Serializable id) {
LogTableInfo tabInfo = getAnnotation(klazz);
BaseMapper mapper = MybatisTools.getMapper(tabInfo.getEntityType());
return mapper.selectById(id);
}
2025-10-10 09:20:48 +08:00
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());
}
}
}
2025-10-21 17:28:41 +08:00
2025-10-10 09:20:48 +08:00
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;
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<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;
}
}
2025-10-20 17:00:33 +08:00
/**
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param clazz
* @param dataId
* @return List<DataChangeLog> 返回类型
*/
2025-10-22 10:42:00 +08:00
public static <T>List<DataChangeLogVo> findLogsByEntityId(Class<?> clazz,@NonNull Long dataId) {
2025-10-20 17:00:33 +08:00
LogTableInfo tabInfo = getAnnotation(clazz);
String tableName = parseLogTableName(tabInfo);
return findLogsByEntityId(tableName, dataId);
}
2025-10-10 09:20:48 +08:00
/**
* @Description: 根据数据id获取操作日志
2025-10-20 17:00:33 +08:00
* @param tableName 表名
2025-10-10 09:20:48 +08:00
* @param dataId 数据id
* @return List<DataChangeLog> 返回类型
*/
2025-10-22 10:42:00 +08:00
public static <T>List<DataChangeLogVo> findLogsByEntityId(String tableName,@NonNull Long dataId) {
2025-10-10 09:20:48 +08:00
if(initTable(tableName)) {
return null;
}
List<DataChangeLog> 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<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);
}
2025-10-22 10:42:00 +08:00
return toVo(result);
2025-10-10 09:20:48 +08:00
} catch (SQLException e) {
throw context.createException(e);
} finally {
context.end();
}
}
2025-10-22 10:42:00 +08:00
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 = 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;
}
2025-10-10 09:20:48 +08:00
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();
}
}
2025-10-21 17:28:41 +08:00
2025-10-10 09:20:48 +08:00
}