package com.pictc.utils;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

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.exceptions.OrmException;
import com.pictc.jdbc.JdbcContext;
import com.pictc.jdbc.ResultSetUtils;
import com.pictc.jdbc.model.JdbcParam;
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.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 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("tenantId","dataVersion","tenantId","tenantId","tenantId");
	
	
	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 <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;
	}
	
	public static boolean deleteByIds(Class<?> klazz, @Valid List<Long> ids) {
		for (Long id : ids) {
			deleteById(klazz, id);
		}
		return true;
	}
	
	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;
	}
	
	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 void delete(Object entity,LogTableInfo tableinfo,BaseMapper mapper){
		Long idValue = tableinfo.getIdValue(entity);
		if(idValue!=null && idValue > 0) {
//			QueryChainWrapper wrapper = new QueryChainWrapper(mapper);
//			wrapper.eq(tableinfo.getIdColumn(),tableinfo.getIdValue(entity));
//			mapper.delete(wrapper);
			mapper.deleteById(idValue);
		}else {
			throw new RuntimeException("删除数据时，表【"+tableinfo.getName()+"】的主键为空，删除失败！");
		}
		
	}
	
	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;
		}
	}
	
	

	/**
	 * @Description: 根据数据id获取操作日志
	 * @param klass 数据java类型
	 * @param dataId 数据id
	 * @return List<DataChangeLog> 返回类型
	 */
	public static <T>List<DataChangeLog> findLogsByEntityId(Class<T> klass,@NonNull Long dataId) {
		LogTableInfo tabInfo = getAnnotation(klass);
		String tableName = parseLogTableName(tabInfo);
		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);
			}
			return result;
		}  catch (SQLException e) {
			throw context.createException(e);
		} finally {
			context.end();
		}
	}
	
	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();
		}
	}



}
