This commit is contained in:
钱涛 2023-12-20 21:00:48 +08:00
parent e8223a832b
commit b20a3ef103
20 changed files with 1529 additions and 0 deletions

View File

@ -0,0 +1,23 @@
package com.engine.salary.elog.annotation;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Elog {
String module();
String function();
String operateType();
String operateTypeName();
String sql() default "";
boolean isLocal() default true;
String infoMethod() default "";
}

View File

@ -0,0 +1,14 @@
package com.engine.salary.elog.annotation;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface ElogDetailField {
String fieldType() default "varchar";
String length() default "50";
String fieldName();
String desc() default "自定义字段";
}

View File

@ -0,0 +1,13 @@
package com.engine.salary.elog.annotation;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface ElogDetailTable {
String module();
String function() default "common";
}

View File

@ -0,0 +1,23 @@
package com.engine.salary.elog.annotation;
import com.engine.salary.elog.dto.DateTypeEnum;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface ElogField {
DateTypeEnum dataType() default DateTypeEnum.VARCHAR;
int length() default 50;
String comment() default "自定义字段";
String defaultValue() default "";
boolean isNull() default true;
boolean isKey() default false;
}

View File

@ -0,0 +1,9 @@
package com.engine.salary.elog.annotation;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface ElogPrimaryKey {
}

View File

@ -0,0 +1,16 @@
package com.engine.salary.elog.annotation;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Component
public @interface ElogTable {
String module();
String function() default "common";
}

View File

@ -0,0 +1,24 @@
package com.engine.salary.elog.annotation;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* @ClassName: LoggerTarget
* @Description 日志构造器-自定义注解
* @Author tanghj
* @Date 2021/2/10 14:18
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface LoggerTarget {
@AliasFor("module")
String value() default "";
@AliasFor("value")
String module() default "";
String function() default "common";
}

View File

@ -0,0 +1,177 @@
package com.engine.salary.elog.annotation.handle;
import com.weaver.common.async.producer.client.AsyncClient;
import com.weaver.common.distribution.genid.IdGenerator;
import com.weaver.common.elog.annotation.Elog;
import com.weaver.common.elog.annotation.ElogPrimaryKey;
import com.weaver.common.elog.dao.QueryCurretValusMapper;
import com.weaver.common.elog.dto.LoggerContext;
import com.weaver.common.elog.util.LoggerTemplate;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @ClassName: LoggerTargetHandler
* @Description 从Spring扫描到的类中获取到Elog自定义注解类设置function属性
* @Author tanghj
* @Date 2021/2/10 14:18
*/
@Aspect
@Component
public class ElogHandler {
@Autowired
ApplicationContext applicationContext;
@Autowired
protected AsyncClient asyncClient;
@Autowired
QueryCurretValusMapper queryCurretValusMapper;
/**
* 切面写入日志
*/
@Pointcut("@annotation(com.weaver.common.elog.annotation.Elog)" )
public void writeLog(){}
@Around("writeLog()")
public Object writeLog(ProceedingJoinPoint pjp){
Object[] args = pjp.getArgs();
Signature signature = pjp.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
Elog elog = method.getAnnotation(Elog.class);
String moduleName = elog.module();
String funtionName = elog.function();
String operateType = elog.operateType();
String operateTypeName = elog.operateTypeName();
boolean isLocal = elog.isLocal();
Annotation[][] annos = method.getParameterAnnotations();
Object id = null;
int keyPosition = -1;
int index = 0;
Class idClass = null;
// 获取主键id注解
for(Annotation[] anno : annos) {
if(anno.length > 0) {
for(Annotation annotation : anno) {
if(annotation instanceof ElogPrimaryKey) {
idClass = method.getParameters()[index].getType();
id = args[index];
if(StringUtils.isEmpty(id+"")) {
id = idClass.cast(IdGenerator.generate() + "");
}
keyPosition = index;
break;
}
}
}
index ++;
}
LoggerContext loggerContext = null;
// 获取日志实体类
for(Object arg: args) {
if(arg instanceof LoggerContext) {
loggerContext = (LoggerContext) arg;
break;
}
}
if(loggerContext == null) {
loggerContext = new LoggerContext();
}
// 日志实体类的初始化
// loggerContext.setOperateType("UPDATE");
loggerContext.setFunctionName(funtionName);
loggerContext.setModuleName(moduleName);
loggerContext.setOperateType(operateType);
loggerContext.setOperateTypeName(operateTypeName);
loggerContext.setDate(new Date());
loggerContext.setDevice("IOS");
String sql = elog.sql();
String infoMethod = elog.infoMethod();
boolean isSql = false;
boolean isMethod = false;
Object currentClass = null;
Method infoMtd = null;
if(StringUtils.isNotEmpty(id+"")) {
if(StringUtils.isNotEmpty(sql)) {
isSql = true;
Map oldValue = queryCurretValusMapper.queryValues(String.format(sql, id));
loggerContext.setOldValues(oldValue);
} else if(StringUtils.isNotEmpty(infoMethod)){
isMethod = true;
currentClass = applicationContext.getBean(pjp.getTarget().getClass());
// 获取方法
infoMtd = ReflectionUtils.findMethod(pjp.getTarget().getClass(),infoMethod, idClass);
// todo 为空的情况加异常提醒
// 反射执行方法
Object res= ReflectionUtils.invokeMethod(infoMtd,currentClass, id);
if(res != null) {
if(res instanceof List) {
loggerContext.setOldValueList((List) res);
} else {
loggerContext.setOldValues(res);
}
}
}
}
LoggerTemplate loggerTemplate = new LoggerTemplate();
loggerTemplate.setFunction(funtionName);
loggerTemplate.setModule(moduleName);
loggerTemplate.setAsyncClient(asyncClient);
Object result = null;
try {
args[keyPosition] = id;
result = pjp.proceed(args);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
if(isSql) {
Map oldValue = queryCurretValusMapper.queryValues(String.format(sql, id));
loggerContext.setNewValues(oldValue);
} else if(isMethod) {
Object res= ReflectionUtils.invokeMethod(infoMtd,currentClass, id);
if(res != null) {
if(res instanceof List) {
loggerContext.setNewValueList((List) res);
} else {
loggerContext.setNewValues(res);
}
}
}
if(isLocal)
loggerTemplate.write(loggerContext);
else
loggerTemplate.write(loggerContext,false);
return result;
}
}

View File

@ -0,0 +1,172 @@
package com.engine.salary.elog.annotation.handle;
import com.engine.salary.elog.annotation.ElogField;
import com.engine.salary.elog.annotation.ElogTable;
import com.engine.salary.elog.dto.TableColumnBean;
import com.engine.salary.elog.util.ElogUtils;
import com.weaver.common.elog.dao.TableCheckerMapper;
import com.weaver.common.elog.dto.DateTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
/**
* @ClassName: ElogTableScanner
* @Description 日志操作表扫描
* @Author tanghj
* @Date 2021/3/12 13:31
*/
@Slf4j
public class ElogTableScanner {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private ApplicationContext applicationContext;
@Autowired
private TableCheckerMapper tableCheckerMapper;
@Override
public void run(String... args) throws Exception {
// todo 需要考虑集群下控制一台机器来跑
scanElogTable();
}
private void scanElogTable() {
// todo 是否还需要扫描Elog因为可能不需要本地存储 ELogDetailTable
Map<String, Object> tableBeans = this.applicationContext.getBeansWithAnnotation(ElogTable.class);
Reflections reflections = new Reflections("com.example.controller");
Set<Class<?>> restController = reflections.getTypesAnnotatedWith(RestController.class);
List<TableColumnBean> baseColumns = new ArrayList<>();
Map<String, List<TableColumnBean>> tableColumns = elogTableHandle(tableBeans,baseColumns);
for(String tableName : tableColumns.keySet()) {
// todo 需要处理明细表如果没有直接初始化原始明细表如果有加上自定义的
List<TableColumnBean> columns = tableColumns.get(tableName);
if(columns == null) {
columns = baseColumns;
} else {
columns.addAll(baseColumns);
}
tableCheck(tableName, columns);
}
}
private Map<String, List<TableColumnBean>> elogTableHandle(Map<String, Object> tableBeans, List<TableColumnBean> baseColumns) {
Map<String, List<TableColumnBean>> tableMap = new HashMap<>();
for (Map.Entry<String, Object> entry : tableBeans.entrySet()) {//遍历每个controller层
List<TableColumnBean> list = new ArrayList<>();
System.out.println(entry.getKey());//demo1Controller
Object value = entry.getValue();
Class<?> aClass = AopUtils.getTargetClass(value);//获取class
ElogTable elogTable = aClass.getAnnotation(ElogTable.class);
List<Field> fields = Arrays.asList(aClass.getDeclaredFields());//获取方法
for (Field f : fields) {
ElogField field = f.getAnnotation(ElogField.class);
if(field == null) {
continue;
}
TableColumnBean tableColumnBean = new TableColumnBean();
tableColumnBean.setColumnName(f.getName());
tableColumnBean.setColumnComment(field.comment());
tableColumnBean.setColumnDefault(field.defaultValue());
tableColumnBean.setFieldLength(field.length());
tableColumnBean.setDataType(field.dataType());
tableColumnBean.setNullable(field.isNull());
list.add(tableColumnBean);
}
if(!ElogUtils.BASE_TABLE.equals(elogTable.module())) {
tableMap.put(ElogUtils.getTableName(elogTable.module(), elogTable.function()), list);
} else {
baseColumns.addAll(list);
}
}
return tableMap;
}
private void tableCheck(String tableName, List<TableColumnBean> columns) {
List<TableColumnBean> oldColumns = tableCheckerMapper.getTableStructure(tableName);
// 表不存在
if(oldColumns == null || oldColumns.size() == 0) {
createTable(tableName,columns );
} else {
Map<String, TableColumnBean> newcolMap = new HashMap<>();
Map<String, TableColumnBean> oldcolMap = new HashMap<>();
columns.stream().forEach(tableColumnBean -> newcolMap.put(tableColumnBean.getColumnName().toLowerCase(), tableColumnBean));
oldColumns.stream().forEach(tableColumnBean -> {
tableColumnBean.setDataType(ElogUtils.getEnumFromString(DateTypeEnum.class, tableColumnBean.getDataTypeStr()));
tableColumnBean.setNullable("YES".equalsIgnoreCase(tableColumnBean.getIsNullableStr()));
oldcolMap.put(tableColumnBean.getColumnName().toLowerCase(), tableColumnBean);
});
// 只增加或者修改不删除字段
for(String key : newcolMap.keySet()) {
if(oldcolMap.containsKey(key)) {
// 字段变动则修改
if(!(newcolMap.get(key).toSql()).equals(oldcolMap.get(key).toSql()))
this.modifyColumn(tableName, newcolMap.get(key));
} else {
this.addColumn(tableName, newcolMap.get(key));
}
}
}
}
private void createTable(String tableName, List<TableColumnBean> columns) {
StringBuilder sb = new StringBuilder("create table ").append(tableName).append(" ( ");
sb.append(columns.stream().map( bean -> bean.toSql()).collect(Collectors.joining(",")));
sb.append(")");
logger.info("创建sql:{}",sb.toString());
tableCheckerMapper.createElogTable(sb.toString());
}
private void addColumn(String tableName, TableColumnBean tableColumnBean) {
StringBuilder sb = new StringBuilder("alter table ")
.append(tableName).append(" ")
.append(" add ")
.append(" column ")
.append(tableColumnBean.toSql());
logger.info("新增字段sql:{}",sb.toString());
tableCheckerMapper.createElogTable(sb.toString());
}
private void modifyColumn(String tableName, TableColumnBean tableColumnBean) {
StringBuilder sb = new StringBuilder("alter table ")
.append(tableName).append(" ")
.append(" modify ")
.append(" column ")
.append(tableColumnBean.toSql());
logger.info("修改字段sql:{}",sb.toString());
tableCheckerMapper.createElogTable(sb.toString());
}
}

View File

@ -0,0 +1,40 @@
package com.engine.salary.elog.annotation.handle;
import com.weaver.common.elog.annotation.LoggerTarget;
import com.weaver.common.elog.util.LoggerTemplate;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @ClassName: LoggerTargetHandler
* @Description 从Spring扫描到的类中获取到LoggerTarget自定义注解类设置function属性
* @Author tanghj
* @Date 2021/2/10 14:18
*/
@Component
public class LoggerTargetHandler implements CommandLineRunner {
@Autowired
ApplicationContext applicationContext;
@Override
public void run(String... args) throws Exception {
Map<String, Object> loggtemplateMap = applicationContext.getBeansWithAnnotation(LoggerTarget.class);
for(Object obj : loggtemplateMap.values()) {
if(obj instanceof LoggerTemplate) {
LoggerTarget loggerTarget = obj.getClass().getAnnotation(LoggerTarget.class);
((LoggerTemplate) obj).setFunction(loggerTarget.function());
((LoggerTemplate) obj).setModule(StringUtils.isNotEmpty(loggerTarget.value()) ? loggerTarget.value() : loggerTarget.module());
}
}
}
}

View File

@ -0,0 +1,25 @@
package com.engine.salary.elog.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* @ClassName: CancelContext
* @Description 撤销实体类
* @Author tanghj
* @Date 2021/2/10 14:18
*/
@ApiModel("撤销实体类")
public class CancelContext<T> {
@ApiModelProperty("撤销参数")
private T cancleParams;
public T getCancleParams() {
return cancleParams;
}
public void setCancleParams(T cancleParams) {
this.cancleParams = cancleParams;
}
}

View File

@ -0,0 +1,16 @@
package com.engine.salary.elog.dto;
public enum DateTypeEnum {
VARCHAR,
BIGINT,
INT,
DATETIME,
TEXT,
LONGTEXT,
DOUBLE,
DECIMAL,
TINYINT,
FLOAT;
}

View File

@ -0,0 +1,408 @@
package com.engine.salary.elog.dto;
import com.engine.salary.elog.annotation.ElogField;
import com.engine.salary.elog.annotation.ElogTable;
import com.engine.salary.elog.util.ElogUtils;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @ClassName: LoggerContext
* @Description 日志实体类支持通过泛型扩展日志字段
* @Author tanghj
* @Date 2021/2/10 14:18
*/
@ElogTable(module = ElogUtils.BASE_TABLE)
@ApiModel("日志实体类")
public class LoggerContext<T> {
@ElogField(comment = "ID", dataType = DateTypeEnum.VARCHAR, isKey = true, isNull = false)
@ApiModelProperty("日志ID")
private long id;
@ElogField(dataType = DateTypeEnum.VARCHAR, length = 36,comment = "日志UUID")
@ApiModelProperty("日志UUID")
private String uuid;
@ApiModelProperty("自定义日志字段信息")
private T customInfo;
@ElogField(dataType = DateTypeEnum.DATETIME,comment = "操作时间")
@ApiModelProperty("操作时间")
private Date date;
@ElogField(dataType = DateTypeEnum.VARCHAR,length = 50,comment = "终端信息")
@ApiModelProperty("终端信息")
private String device;
@ElogField(dataType = DateTypeEnum.VARCHAR,length = 50,comment = "操作人")
@ApiModelProperty("操作人")
private String operator;
@ElogField(dataType = DateTypeEnum.VARCHAR,length = 100,comment = "操作人姓名")
@ApiModelProperty("操作人姓名")
private String operatorName;
@ElogField(dataType = DateTypeEnum.VARCHAR,length = 10,comment = "租户id")
@ApiModelProperty("租户id")
private String tenant_key;
/**
* 要操作的对象在表中的主键值
*/
@ElogField(dataType = DateTypeEnum.VARCHAR,length = 50,comment = "操作目标id")
@ApiModelProperty("操作目标id")
private String targetId;
@ElogField(dataType = DateTypeEnum.TEXT ,comment = "操作目标名称")
@ApiModelProperty("操作目标名称(用于显示)")
private String targetName;
@ElogField(dataType = DateTypeEnum.VARCHAR, length = 100,comment = "模块")
@ApiModelProperty("目标对象类型(大分类,模块,服务)")
private String moduleName;// 模块
/**
* 目标对象类型小分类模块/服务下的子功能子项目
* 数据存储是以模块名_子项目名作为最基本的存储单元
* 如果是子项目下的子项目 命名为子项目名_子项目名_子项目名全部小写
*/
@ElogField(dataType = DateTypeEnum.VARCHAR, length = 100,comment = "服务(方法)")
@ApiModelProperty("目标对象类型(小分类,模块/服务下的子功能。子项目)")
private String functionName;
@ElogField(dataType = DateTypeEnum.VARCHAR, length = 100,comment = "访问接口名")
@ApiModelProperty("访问接口名")
private String interfaceName;
@ElogField(dataType = DateTypeEnum.VARCHAR, length = 50,comment = "操作类型")
@ApiModelProperty("操作类型(增删改查等)")
private String operateType;
@ElogField(dataType = DateTypeEnum.TEXT,comment = "操作类型")
@ApiModelProperty("操作类型名称")
private String operateTypeName;
/**
* 每个TableChangeBean 为一张表支持记录多张表的前后值
*/
@ApiModelProperty("修改前、后的值")
private List<TableChangeBean> changeValues;// 操作表名,[字段名,]
@ElogField(dataType = DateTypeEnum.TEXT,comment = "操作类型")
@ApiModelProperty("操作详细说明")
private String operatedesc;
@ApiModelProperty("涉及的相关参数")
private Map<String, Object> params;
/*@ApiModelProperty("主日志")
private String mainId;//当作为主表belongMainId不赋值
@ApiModelProperty("从表日志")
private String belongMainId;*/
@ElogField(dataType = DateTypeEnum.VARCHAR, length = 50,comment = "操作IP")
@ApiModelProperty("操作IP")
private String clientIp;
@ElogField(dataType = DateTypeEnum.VARCHAR, length = 50,comment = "分组")
@ApiModelProperty("分组")
private String groupId;
/*@ApiModelProperty("是否明显表")
private boolean isDetail;*/
@ElogField(dataType = DateTypeEnum.TEXT, comment = "分组标题")
@ApiModelProperty("分组标题")
private int groupNameLabel;
@ApiModelProperty("重做业务接口")
private String redoService;
@ElogField(dataType = DateTypeEnum.LONGTEXT, comment = "重做参数")
@ApiModelProperty("重做参数")
private RedoContext redoContext;
@ElogField(dataType = DateTypeEnum.VARCHAR, length = 200, comment = "撤销业务接口")
@ApiModelProperty("撤销业务接口")
private String cancelService;
@ElogField(dataType = DateTypeEnum.LONGTEXT, comment = "撤销参数")
@ApiModelProperty("撤销参数")
private CancelContext cancelContext;
@ApiModelProperty("日志明细列表(值变化列表-自动赋值、或者自定义字段)")
private List<LoggerDetailContext> detailContexts;
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public T getCustomInfo() {
return customInfo;
}
public void setCustomInfo(T customInfo) {
this.customInfo = customInfo;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getDevice() {
return device;
}
public void setDevice(String device) {
this.device = device;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public String getTenant_key() {
return tenant_key;
}
public void setTenant_key(String tenant_key) {
this.tenant_key = tenant_key;
}
public String getTargetId() {
return targetId;
}
public void setTargetId(String targetId) {
this.targetId = targetId;
}
public String getTargetName() {
return targetName;
}
public void setTargetName(String targetName) {
this.targetName = targetName;
}
public String getModuleName() {
return moduleName;
}
public void setModuleName(String moduleName) {
this.moduleName = moduleName;
}
public String getFunctionName() {
return functionName;
}
public void setFunctionName(String functionName) {
this.functionName = functionName;
}
public String getInterfaceName() {
return interfaceName;
}
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
public String getOperateType() {
return operateType;
}
public void setOperateType(String operateType) {
this.operateType = operateType;
}
public List<TableChangeBean> getChangeValues() {
return changeValues;
}
public void setChangeValues(List<TableChangeBean> changeValues) {
this.changeValues = changeValues;
}
public String getOperatedesc() {
return operatedesc;
}
public void setOperatedesc(String operatedesc) {
this.operatedesc = operatedesc;
}
public Map<String, Object> getParams() {
return params;
}
public void setParams(Map<String, Object> params) {
this.params = params;
}
/*public String getMainId() {
return mainId;
}
public void setMainId(String mainId) {
this.mainId = mainId;
}
public String getBelongMainId() {
return belongMainId;
}
public void setBelongMainId(String belongMainId) {
this.belongMainId = belongMainId;
}*/
public String getClientIp() {
return clientIp;
}
public void setClientIp(String clientIp) {
this.clientIp = clientIp;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
/* public boolean isDetail() {
return isDetail;
}
public void setDetail(boolean detail) {
isDetail = detail;
}*/
public int getGroupNameLabel() {
return groupNameLabel;
}
public void setGroupNameLabel(int groupNameLabel) {
this.groupNameLabel = groupNameLabel;
}
public String getRedoService() {
return redoService;
}
public void setRedoService(String redoService) {
this.redoService = redoService;
}
public RedoContext getRedoContext() {
return redoContext;
}
public void setRedoContext(RedoContext redoContext) {
this.redoContext = redoContext;
}
public CancelContext getCancelContext() {
return cancelContext;
}
public void setCancelContext(CancelContext cancelContext) {
this.cancelContext = cancelContext;
}
public String getCancelService() {
return cancelService;
}
public void setCancelService(String cancelService) {
this.cancelService = cancelService;
}
public List<LoggerDetailContext> getDetailContexts() {
return detailContexts;
}
public void setDetailContexts(List<LoggerDetailContext> detailContexts) {
this.detailContexts = detailContexts;
}
public String getOperatorName() {
return operatorName;
}
public void setOperatorName(String operatorName) {
this.operatorName = operatorName;
}
public void setOldValues(Object object) {
TableChangeBean bean = new TableChangeBean();
bean.setOldValue(object);
getChangeList().add(bean);
}
public void setOldValueList(List<Object> list) {
if(list != null)
list.stream().forEach(obj -> setOldValues(obj));
}
public void setNewValueList(List<Object> list) {
if(list != null)
list.stream().forEach(obj -> setNewValues(obj));
}
public void setNewValues(Object object) {
List<TableChangeBean> list = getChangeList();
boolean handled = false;
for(TableChangeBean bean : list) {
if(bean.getNewValue() == null) {
bean.setNewValue(object);
handled = true;
break;
}
}
if(!handled) {
TableChangeBean bean = new TableChangeBean();
bean.setOldValue(object);
list.add(bean);
}
}
public List<TableChangeBean> getChangeList() {
if(this.changeValues == null) {
this.changeValues = new ArrayList<>();
}
return this.changeValues;
}
public String getOperateTypeName() {
return operateTypeName;
}
public void setOperateTypeName(String operateTypeName) {
this.operateTypeName = operateTypeName;
}
}

View File

@ -0,0 +1,102 @@
package com.engine.salary.elog.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* @ClassName: ValueChangeBean
* @Description 值变化实体类自动将表字段值变更类转换为该类
* @Author tanghj
* @Date 2021/3/9 11:06
*/
@ApiModel("值变化实体类")
public class LoggerDetailContext<T> {
@ApiModelProperty("同一个bean转换的数据uuid相同作为区分标识")
private String uuid;
@ApiModelProperty("表名")
private String tableName;
@ApiModelProperty("字段名")
private String fieldName;
@ApiModelProperty("更新后的值")
private String newValue;
@ApiModelProperty("更新前的值")
private String oldValue;
@ApiModelProperty("字段描述")
private String fieldDesc;
@ApiModelProperty("字段展示顺序")
private int showorder;
@ApiModelProperty("自定义字段")
private T customDetailInfo;
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public String getNewValue() {
return newValue;
}
public void setNewValue(String newValue) {
this.newValue = newValue;
}
public String getOldValue() {
return oldValue;
}
public void setOldValue(String oldValue) {
this.oldValue = oldValue;
}
public String getFieldDesc() {
return fieldDesc;
}
public void setFieldDesc(String fieldDesc) {
this.fieldDesc = fieldDesc;
}
public int getShoworder() {
return showorder;
}
public void setShoworder(int showorder) {
this.showorder = showorder;
}
public T getCustomDetailInfo() {
return customDetailInfo;
}
public void setCustomDetailInfo(T customDetailInfo) {
this.customDetailInfo = customDetailInfo;
}
}

View File

@ -0,0 +1,24 @@
package com.engine.salary.elog.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* @ClassName: RedoContext
* @Description 重做实体类
* @Author tanghj
* @Date 2021/2/10 14:18
*/
@ApiModel("重做实体类")
public class RedoContext<T> {
@ApiModelProperty("重做参数")
private T redoParams;
public T getRedoParams() {
return redoParams;
}
public void setRedoParams(T redoParams) {
this.redoParams = redoParams;
}
}

View File

@ -0,0 +1,53 @@
package com.engine.salary.elog.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* @ClassName: TableChangeBean
* @Description 表更新前后值类
* @Author tanghj
* @Date 2021/3/8 15:58
*/
@ApiModel("表更新前后值类")
public class TableChangeBean<T> {
@ApiModelProperty("表名")
private String tableName;
/**
* 泛型必须是具体的实体类
*/
@ApiModelProperty("更新前的值")
private T oldValue;
/**
* 泛型必须是具体的实体类
*/
@ApiModelProperty("更新后的值")
private T newValue;
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public T getOldValue() {
return oldValue;
}
public void setOldValue(T oldValue) {
this.oldValue = oldValue;
}
public T getNewValue() {
return newValue;
}
public void setNewValue(T newValue) {
this.newValue = newValue;
}
}

View File

@ -0,0 +1,155 @@
package com.engine.salary.elog.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* @ClassName: TableColumnBean
* @Description 表字段类
* @Author tanghj
* @Date 2021/3/12 11:44
*/
@ApiModel("表字段类")
public class TableColumnBean {
@ApiModelProperty("列名")
private String columnName;
/**
* varchar(20)
*/
@ApiModelProperty("数据类型")
private String columnType;
@ApiModelProperty("字段类型-字符串")
private String dataTypeStr;
@ApiModelProperty("字段类型")
private DateTypeEnum dataType;
@ApiModelProperty("长度")
private long fieldLength;
@ApiModelProperty("是否为空")
private boolean isNullable;
@ApiModelProperty("是否为空-字符串")
private String isNullableStr;
@ApiModelProperty("默认值")
private Object columnDefault;
@ApiModelProperty("备注")
private String columnComment;
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getColumnType() {
return columnType;
}
public void setColumnType(String columnType) {
this.columnType = columnType;
}
public DateTypeEnum getDataType() {
return dataType;
}
public void setDataType(DateTypeEnum dataType) {
this.dataType = dataType;
}
public long getFieldLength() {
return fieldLength;
}
public void setFieldLength(long fieldLength) {
this.fieldLength = fieldLength;
}
public boolean isNullable() {
return isNullable;
}
public void setNullable(boolean nullable) {
isNullable = nullable;
}
public Object getColumnDefault() {
return columnDefault;
}
public void setColumnDefault(Object columnDefault) {
this.columnDefault = columnDefault;
}
public String getColumnComment() {
return columnComment;
}
public void setColumnComment(String columnComment) {
this.columnComment = columnComment;
}
public String getDataTypeStr() {
return dataTypeStr;
}
public void setDataTypeStr(String dataTypeStr) {
this.dataTypeStr = dataTypeStr;
}
public String getIsNullableStr() {
return isNullableStr;
}
public void setIsNullableStr(String isNullableStr) {
this.isNullableStr = isNullableStr;
}
public boolean equals(TableColumnBean tableColumnBean) {
return this.toSql().equals(tableColumnBean.toSql());
}
public String toSql() {
StringBuilder sb = new StringBuilder(this.columnName.toLowerCase()).append(" ");
if (this.dataType == null) {
return "类型为空";
}
switch (this.dataType) {
case BIGINT:
case INT:
case TEXT:
case LONGTEXT:
case DATETIME:
case FLOAT:
case TINYINT:
case DOUBLE:
sb.append(this.dataType.name().toLowerCase()).append(" ");
break;
case VARCHAR:
sb.append(this.dataType.name().toLowerCase()).append("(").append(this.fieldLength).append(") ");
break;
case DECIMAL:
long length = (this.fieldLength < 0 || this.fieldLength > 38) ? 0 : this.fieldLength;
sb.append(this.dataType.name().toLowerCase()).append("(").append(38 - length).append(",").append(length).append(") ");
break;
default:
sb.append("varchar").append("(").append(10).append(") ");
}
sb.append("comment ").append("'").append(this.columnComment).append("' ");
return sb.toString();
}
}

View File

@ -0,0 +1,50 @@
package com.engine.salary.elog.util;
import com.engine.salary.elog.dto.DateTypeEnum;
/**
* @ClassName: ElogUtils
* @Description TODO
* @Author tanghj
* @Date 2021/3/12 14:17
*/
public class ElogUtils {
private static final String TABLE_SPACER = "_";
private static final String TABLE_SUFFIX = "logs";
private static final String DETAIL_TABLE_SUFFIX = "detail";
public static final String BASE_TABLE = "BASE_ELOG_TABLE";
public static String getTableName(String module, String function) {
return getTableName(module, function, false);
}
public static String getTableName(String module, String function, boolean isDetail){
return module + TABLE_SPACER + function + TABLE_SUFFIX + (isDetail ? DETAIL_TABLE_SUFFIX : "");
}
/**
* String 转枚举
* @param c
* @param string
* @param <T>
* @return
*/
public static <T extends Enum<T>> T getEnumFromString(Class<T> c, String string) {
if (c != null && string != null) {
try {
return Enum.valueOf(c, string.trim().toUpperCase());
} catch (IllegalArgumentException ex) {
}
}
return null;
}
public static void main(String[] args) {
System.out.println(DateTypeEnum.BIGINT.name());
DateTypeEnum columnTypeEnum = getEnumFromString(DateTypeEnum.class, "varchar");
System.out.println(columnTypeEnum.equals(DateTypeEnum.VARCHAR));
}
}

View File

@ -0,0 +1,164 @@
package com.engine.salary.elog.util;
import com.alibaba.fastjson.JSONObject;
import com.weaver.common.async.bean.AsyncBean;
import com.weaver.common.async.producer.client.AsyncClient;
import com.weaver.common.elog.dto.LoggerContext;
import com.weaver.common.elog.dto.LoggerDetailContext;
import com.weaver.common.elog.dto.TableChangeBean;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.reflect.Field;
import java.util.*;
/**
* @ClassName: LoggerTemplate
* @Description 日志基本功能类
* @Author tanghj
* @Date 2021/2/10 14:18
*/
public class LoggerTemplate {
protected String function = "common";
protected String module;
protected String logCenterQueue = "Elog_cneterlogQueue";
protected String localQueue;
@Autowired
protected AsyncClient asyncClient;
/**
* 写入日志消息队列
* @param context 日志实体
*/
public void write(LoggerContext context){
this.write(context, true);
}
/**
* 写入日志消息队列
* @param context 日志实体
* @param isLocal true 存储本地 false不存储本地
*/
public void write(LoggerContext context, boolean isLocal){
AsyncBean<LoggerContext> asyncBean = new AsyncBean<>();
handleContext(context);
asyncBean.setMessage(context);
asyncBean.setQueue(logCenterQueue);
asyncClient.send(asyncBean);
if(isLocal) {
asyncBean.setQueue(localQueue);
asyncClient.send(asyncBean);
}
}
private void handleContext(LoggerContext context) {
context.setModuleName(module);
context.setFunctionName(function);
List<TableChangeBean> changeBeans = context.getChangeValues();
List<LoggerDetailContext> valueChangeList = new ArrayList<>();
int showOrder = 0;
if(changeBeans != null)
for(TableChangeBean changeBean : changeBeans) {
if(changeBean != null && (changeBean.getNewValue() != null || changeBean.getOldValue() != null)) {
ApiModel apiModel = null;
JSONObject newJo = new JSONObject();
JSONObject oldJo = new JSONObject();
JSONObject valueChange = JSONObject.parseObject(JSONObject.toJSONString(changeBean));
if(changeBean.getNewValue() != null) {
apiModel = changeBean.getNewValue().getClass().getAnnotation(ApiModel.class);
newJo = valueChange.getJSONObject("newValue");
} else {
apiModel = changeBean.getOldValue().getClass().getAnnotation(ApiModel.class);
}
if(changeBean.getOldValue() != null) {
oldJo = valueChange.getJSONObject("oldValue");
}
if(apiModel != null) {
Field[] fields = changeBean.getNewValue().getClass().getDeclaredFields();
for(Field field : fields) {
ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
LoggerDetailContext valueChangeBean = new LoggerDetailContext();
valueChangeBean.setTableName(changeBean.getTableName());
valueChangeBean.setFieldName(field.getName());
valueChangeBean.setFieldDesc(apiModelProperty.value());
valueChangeBean.setNewValue(newJo.getString(field.getName()));
valueChangeBean.setOldValue(oldJo.getString(field.getName()));
valueChangeBean.setShoworder(showOrder++);
valueChangeList.add(valueChangeBean);
}
} else {
Set<String> keys = new HashSet<>();
for(String key : newJo.keySet()) {
keys.add(key);
LoggerDetailContext valueChangeBean = new LoggerDetailContext();
valueChangeBean.setTableName(changeBean.getTableName());
valueChangeBean.setFieldName(key);
valueChangeBean.setFieldDesc(key);
valueChangeBean.setNewValue(newJo.getString(key));
valueChangeBean.setOldValue(oldJo.getString(key));
valueChangeBean.setShoworder(showOrder++);
valueChangeList.add(valueChangeBean);
}
for(String key : oldJo.keySet()) {
if(keys.contains(key))
continue;
keys.add(key);
LoggerDetailContext valueChangeBean = new LoggerDetailContext();
valueChangeBean.setTableName(changeBean.getTableName());
valueChangeBean.setFieldName(key);
valueChangeBean.setFieldDesc(key);
valueChangeBean.setNewValue(newJo.getString(key));
valueChangeBean.setOldValue(oldJo.getString(key));
valueChangeBean.setShoworder(showOrder++);
valueChangeList.add(valueChangeBean);
}
}
}
}
if(valueChangeList.size() > 0)
context.setDetailContexts(valueChangeList);
}
public String getFunction() {
return function;
}
public void setFunction(String function) {
this.function = function;
}
public String getModule() {
return module;
}
/**
* 获取日志实体类
* @return
*/
public LoggerContext getContext() {
LoggerContext loggerContext = new LoggerContext();
loggerContext.setUuid(UUID.randomUUID().toString().replace("-",""));
loggerContext.setModuleName(this.module);
loggerContext.setFunctionName(this.function);
return loggerContext;
}
public void setModule(String module) {
this.localQueue = module + "LocalQueue";
this.module = module;
}
public void setAsyncClient(AsyncClient asyncClient) {
this.asyncClient = asyncClient;
}
}

View File

@ -0,0 +1,21 @@
package com.engine.salary.elog.util;
/**
* @ClassName: LoggerTemplateBuilder
* @Description 日志基本类构造器
* @Author tanghj
* @Date 2021/2/10 14:18
*/
public class LoggerTemplateBuilder {
public static LoggerTemplate build(String module){
return build(module, "common");
}
public static LoggerTemplate build(String module, String function){
LoggerTemplate loggerTemplate = new LoggerTemplate();
loggerTemplate.setFunction(function);
loggerTemplate.setModule(module);
return loggerTemplate;
}
}