Springboot Aop之记录编辑前后的实体修改变更

编程 / 2024-06-03

需求

给定一个未知方法的一个入参实体,要求记录判断出入参实体所对应的数据库实体中的字段数据的前后变更,入参可能是一个List或者Set或者是单个vo。

解决方案

编写通用aop,在方法层面编写一个注解标记需要记录前后变化的方法,在实体层面编写一个注解标记需要记录前后变化的字段,并且选定字段的值需要进行的翻译方式。

代码

@DataName是标记选定字段的值需要进行的翻译方式,@DataFieldName是标记选定字段的注释需要进行的翻译方式,如果该字段的含义注释也是动态获取的话,就需要使用他进行翻译。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ChangeRecordLog {
    // 获取编辑信息的解析类,目前为使用id获取
    Class<? extends IChangeRecordLogContentParser> parseclass() default DefaultChangeRecordLogContentParse.class;

    // 对比结果处理器
    Class<? extends IChangeRecordLogResultHandler> dealResultclass();

    // 查询数据库所调用的class文件 selectById方法所在的Service类
    Class<? extends IService> serviceclass() default IService.class;

    // 是否需要比较新旧数据
    boolean needDefaultCompare() default false;

    // id的类型
    Class idType() default String.class;

    // 备注
    String remark() default "";

    // 操作对象的id字段名称
    String tableId() default "id";

    //操作类型 add update delete
    String type() default "update";
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
public @interface DataName {

    // 获取编辑信息的解析类,目前为使用id获取
    Class<? extends IDataNameContentParser> parseclass() default DefaultDataNameContentParse.class;

    // 查询数据库所调用的class文件 getById方法所在的Service类
    Class<? extends IService> serviceclass() default IService.class;

    // 是否需要调用服务翻译
    boolean needDefaultTranslate() default false;


    // 字段名称,如果为空的话,会取@ApiModelProperty 注解中的value
    String name() default "";

    String dictCode() default "";

    /**
     * 读取查询出来的实体中取哪个字段的值
     */
    String readEntityValue() default "";

    // id的类型
    Class<?> idType() default String.class;

    /**
     * 读取枚举内容转义表达式 (如: 0=男,1=女,2=未知)
     */
    String readConverterExp() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
public @interface DataFieldName {

    // 获取编辑信息的解析类,目前为使用id获取
    Class<? extends IDataFieldNameParser> parseclass() default DefaultDataFieldNameParse.class;

    // 查询数据库所调用的class文件 getById方法所在的Service类
    Class<? extends IService> serviceclass() default IService.class;

    // 是否需要调用服务翻译
    boolean needDefaultTranslate() default true;

    // 字段名称
    String value() default "";

    // 字段名称
    String additional() default "";

    // id的类型
    Class<?> idType() default String.class;

    /**
     * 读取查询出来的实体中取哪个字段的值
     */
    String readEntityValue() default "";
}
@Aspect
@Component
@Slf4j
@SuppressWarnings("all")
public class ChangeRecordAspect {

    @Autowired
    private ApplicationContext applicationContext;

    // 保存修改之前的数据
    private Map<String, Map<String, Object>> oldMap = new HashMap<>();

    // 保存修改之后的数据
    private Map<String, Map<String, Object>> newMap = new HashMap<>();

    /**
     * 处理请求前执行
     */
    @Before(value = "@annotation(operateLog)")
    public void boBefore(JoinPoint joinPoint, ChangeRecordLog operateLog) {
        IChangeRecordLogContentParser contentParser = (IChangeRecordLogContentParser) applicationContext.getBean(operateLog.parseclass());
        //旧值
        Collection<Object> oldObject = contentParser.getResult(joinPoint, operateLog, operateLog.tableId());
        if (oConvertUtils.listIsNotEmpty(oldObject)) {
            if (operateLog.needDefaultCompare()) {
                log.info("开始保存旧实体");
                oldMap = (Map<String, Map<String, Object>>) objectToMap(oldObject, operateLog); // 存储修改前的对象
            }
        }
    }

    /**
     * 处理请求后执行
     * @param joinPoint
     * @param operateLog
     * @throws Throwable
     */
    @After("@annotation(operateLog)")
    public void around(JoinPoint joinPoint, ChangeRecordLog operateLog) throws Throwable {
        try {
            if (oldMap.isEmpty()) {
                return;
            }
            LoginUser user= (LoginUser) SecurityUtils.getSubject().getPrincipal();
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod(); // 获取切入点所在的方法
            String meName = method.getName(); // 获取请求的方法名
            String className = joinPoint.getTarget().getClass().getName(); // 获取请求的类名
            String methodName = className + "." + meName;
            String uri = request.getRequestURL().toString(); // 请求uri
            Object[] args = joinPoint.getArgs();

            Collection<Object> objects = null;
            IChangeRecordLogContentParser contentParser;
            try {
                contentParser = (IChangeRecordLogContentParser) applicationContext.getBean(operateLog.parseclass());
                objects = contentParser.getResult(joinPoint, operateLog, operateLog.tableId());
                if (oConvertUtils.listIsNotEmpty(objects)) {
                    newMap = (Map<String, Map<String, Object>>) objectToMap(objects, operateLog);
                    log.info("异步对比开始");
                    EventBusFactory.getInstance().post(new ChangeRecordEvent()
                            .setObjects(objects)
                            .setOldMap(oldMap)
                            .setNewMap(newMap)
                            .setUser(user)
                            .setOperateLog(operateLog));
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error("service加载失败:", e);
            }
        } catch (Exception e) {
            log.info("自定义变更记录注解出现异常");
            e.printStackTrace();
        }
    }

    private Map<String, Map<String, Object>> objectToMap(Collection<Object> objs, ChangeRecordLog operateLog) {
        Map<String, Map<String, Object>> result = new HashMap<>();
        objs.forEach(obj -> {
            if (obj == null) {
                return;
            }
            ObjectMapper mapper = new ObjectMapper();
            mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            Map<?, ?> mappedObject = mapper.convertValue(obj, Map.class);
            result.put(String.valueOf(mappedObject.get(operateLog.tableId())), (Map<String, Object>) mappedObject);
        });
        return result;
    }
}

异步对比

@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class ChangeRecordEvent extends BaseEvent {

    private Collection<Object> objects;

    private Map<String, Map<String, Object>> oldMap;

    private Map<String, Map<String, Object>> newMap;

    private ChangeRecordLog operateLog;
}
@Slf4j
@EventListenerAnnotation
@Component
public class ChangeRecordListener {

    @Autowired
    private ApplicationContext applicationContext;

    @AllowConcurrentEvents
    @Subscribe
    protected void onEventBySync(ChangeRecordEvent event) {
        if (event.getOperateLog().needDefaultCompare()) {
            Object[] objectArray = event.getObjects().toArray();
            Map<String, List<ChangeItem>> result = new HashMap<>();
            //比较新数据与数据库原数据
            event.getOldMap().forEach((k, v) -> {
                Map<String, Object> newTempMap = event.getNewMap().get(k);
                if (null != v && newTempMap != null) {
                    List<ChangeItem> list = defaultDealUpdate(objectArray[0], v, newTempMap, event.getUser());
                    result.put(k, list);
                }
            });
            if (result.size() > 0) {
                IChangeRecordLogResultHandler resultHandler = (IChangeRecordLogResultHandler) applicationContext.getBean(event.getOperateLog().dealResultclass());
                Assert.assertISFalse(resultHandler.dealResult(result, event.getOldMap(), event.getNewMap(), event.getOperateLog(), event.getUser()), ResponseEnum.OPERATION_ERROR, "记录变更处理器出现异常");
            }
        }
    }

    private List<ChangeItem> defaultDealUpdate(Object newObject, Map<String, Object> oldTempMap, Map<String, Object> newTempMap, LoginUser user) {
        try {
            log.info("对比开始");
            List<ChangeItem> list = new ArrayList<>();
            oldTempMap.forEach((k, v) -> {
                Object newResult = newTempMap.get(k);
                if ((v == null && newResult != null) || (v != null && newResult == null) || (v != null && !v.equals(newResult))) {
                    Field field = ReflectionUtils.getAccessibleField(newObject, k);
                    if (field != null) {
                        DataName dataName = field.getAnnotation(DataName.class);
                        if (dataName != null) {
                            String fieldName = null;
                            ChangeItem result = new ChangeItem();
                            DataFieldName dataFieldName = field.getAnnotation(DataFieldName.class);
                            if (dataFieldName != null && dataFieldName.needDefaultTranslate()) {
                                IDataFieldNameParser fieldNameParser;
                                Object convertObject = null;
                                try {
                                    fieldNameParser = applicationContext.getBean(dataFieldName.parseclass());
                                    convertObject = fieldNameParser.getResult(oldTempMap, newTempMap, dataFieldName);
                                    if (convertObject != null) {
                                        if (oConvertUtils.isNotEmpty(dataFieldName.readEntityValue())) {
                                            Field accessibleField = ReflectionUtils.getAccessibleField(convertObject, dataFieldName.readEntityValue());
                                            if (accessibleField != null) {
                                                fieldName = String.valueOf(accessibleField.get(convertObject));
                                            }
                                        } else {
                                            fieldName = String.valueOf(convertObject);
                                        }
                                    }
                                } catch (Exception e) {
                                    log.error("service加载失败,无法翻译字段名:", e);
                                    throw new RuntimeException(e.getMessage());
                                }
                            }
                            if (oConvertUtils.isEmpty(fieldName)) {
                                ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
                                fieldName = oConvertUtils.isNotEmpty(dataName.name()) ? dataName.name() : apiModelProperty != null ? apiModelProperty.value() : null;
                            } else {
                                result.setChangeId(dataFieldName.value());
                            }
                            if (oConvertUtils.isNotEmpty(fieldName)) {
                                String old = v != null ? String.valueOf(v) : "";
                                String newString = newResult != null ? String.valueOf(newResult) : "";
                                // 翻译表达式 0=男,1=女
                                result.setChange(fieldName);
                                if (oConvertUtils.isNotEmpty(dataName.readConverterExp())) {
                                    String oldValue = convertByExp(
                                            old, dataName.readConverterExp(), ",");
                                    String newValue = convertByExp(
                                            newString, dataName.readConverterExp(), ",");
                                    result.setOld(oldValue);
                                    result.setNewValue(newValue);
                                } else if (dataName.needDefaultTranslate()) {
                                    IDataNameContentParser contentParser;
                                    Object oldConvertObject = null;
                                    Object newConvertObject = null;
                                    try {
                                        contentParser = applicationContext.getBean(dataName.parseclass());
                                        if (oConvertUtils.isNotEmpty(old)) {
                                            oldConvertObject = contentParser.getResult(oldTempMap, newTempMap, dataName, old);
                                        }
                                        if (oConvertUtils.isNotEmpty(newString)) {
                                            newConvertObject = contentParser.getResult(oldTempMap, newTempMap, dataName, newString);
                                        }

                                        if (oConvertUtils.isNotEmpty(dataName.readEntityValue())) {
                                            if (oldConvertObject != null) {
                                                if (oConvertUtils.isNotEmpty(dataName.readEntityValue())) {
                                                    Field oldfield = ReflectionUtils.getAccessibleField(oldConvertObject, dataName.readEntityValue());
                                                    if (oldfield != null) {
                                                        Object oldObject = oldfield.get(oldConvertObject);
                                                        result.setOld(oldObject != null ? String.valueOf(oldfield.get(oldConvertObject)) : "");
                                                    }
                                                } else {
                                                    result.setOld(String.valueOf(oldConvertObject));
                                                }
                                            } else {
                                                result.setOld("");
                                            }
                                            if (newConvertObject != null) {
                                                if (oConvertUtils.isNotEmpty(dataName.readEntityValue())) {
                                                    Field newfield = ReflectionUtils.getAccessibleField(newConvertObject, dataName.readEntityValue());
                                                    if (newfield != null) {
                                                        result.setNewValue(String.valueOf(newfield.get(newConvertObject)));
                                                    }
                                                } else {
                                                    result.setNewValue(String.valueOf(newConvertObject));
                                                }
                                            } else {
                                                result.setNewValue("");
                                            }
                                        } else {
                                            result.setOld(oldConvertObject != null ? String.valueOf(oldConvertObject) : "");
                                            result.setNewValue(newConvertObject != null ? String.valueOf(newConvertObject) : "");
                                        }
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                        log.error("service加载失败:", e);
                                    }
                                } else {
                                    result.setOld(old);
                                    result.setNewValue(newString);
                                }
                                if (result.getOld() != null && result.getNewValue() != null) {
                                    list.add(result);
                                }
                            }
                        }
                    }
                }
            });
            log.info("对比结束");
            return list;
        } catch (Exception e) {
            log.error("比较异常", e);
            e.printStackTrace();
            throw new RuntimeException("比较异常", e);
        }
    }

    /**
     * 翻译
     *
     * @param propertyValue 参数值如:0
     * @param converterExp  翻译注解的值如:0=男,1=女,2=未知
     * @param separator     分隔符
     * @return 解析后值
     */
    public static String convertByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(propertyValue, separator)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[0].equals(value)) {
                        propertyString.append(itemArray[1]).append(separator);
                        break;
                    }
                }
            } else {
                if (itemArray[0].equals(propertyValue)) {
                    return itemArray[1];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
}
public class EventBusFactory {

	private static EventBusFactory FACTORY;

	private AsyncEventBus asyncEventBus; // 异步事件处理总线

	private EventBus eventBus; // 同步事件处理总线


	private EventBusFactory() {
		Integer pool = 1;
		asyncEventBus = new AsyncEventBus(Executors.newFixedThreadPool(pool));
		eventBus = new EventBus();
	}

	/**
	 * 获取事务工厂实例
	 *
	 * @return
	 */
	public static EventBusFactory getInstance() {
		if (FACTORY == null) {
			synchronized (EventBusFactory.class) {
				if (FACTORY == null) {
					FACTORY = new EventBusFactory();
				}
			}
		}
		return FACTORY;
	}

	/**
	 * 发布事件
	 *
	 * @param event
	 */
	public void post(BaseEvent event) {
		asyncEventBus.post(event);
		eventBus.post(event);
	}


	/**
	 * 事件监听注册
	 * @param listener
	 */
	public void register(Object listener){
		EventListenerAnnotation eventListenerAnnotation = listener.getClass().getAnnotation(EventListenerAnnotation.class);
		if(eventListenerAnnotation.isAsync()){
			asyncEventBus.register(listener);
		}else{
			eventBus.register(listener);
		}
	}

}


字段翻译器

public interface IDataNameContentParser {

    /**
     * 获取信息返回查询出的对象
     *
     * @param operateLog 注解
     * @return 获得的结果
     */
    Object getResult(Map<String, Object> oldMap, Map<String, Object> newMap, DataName operateLog, Object id);
}

@Component
public class DefaultDataNameContentParse implements IDataNameContentParser {

    @Autowired
    private ApplicationContext applicationContext;
    @Override
    public Object getResult(Map<String, Object> oldMap, Map<String, Object> newMap, DataName operateLog, Object id) {
        if (operateLog.idType().isInstance(id)) {
            IService service = (IService) applicationContext.getBean(operateLog.serviceclass());
            Object result = service.getById((Serializable) id);
            return result;
        } else {
            throw new RuntimeException("请核实id type");
        }
    }
}


public interface IDataFieldNameParser {

    /**
     * 获取信息返回查询出的对象
     *
     * @param operateLog 注解
     * @return 获得的结果
     */
    Object getResult(Map<String, Object> oldMap, Map<String, Object> newMap, DataFieldName operateLog);

}

@Component
public class DefaultDataFieldNameParse implements IDataFieldNameParser {

    @Autowired
    private ApplicationContext applicationContext;
    @Override
    public Object getResult(Map<String, Object> oldMap, Map<String, Object> newMap, DataFieldName operateLog) {
        if (operateLog.idType().isInstance(operateLog.value())) {
            IService service = (IService) applicationContext.getBean(operateLog.serviceclass());
            Object result = service.getById(operateLog.value());
            return result;
        } else {
            throw new RuntimeException("请核实id type");
        }
    }
}

自定义翻译器

比如字典翻译

@Component
public class SysBusinessDataNameDictContentParse implements IDataNameContentParser {

    @Autowired
    private ISysBusinessDictService sysBusinessDictService;

    @Override
    public Object getResult(Map<String, Object> oldMap, Map<String, Object> newMap, DataName operateLog, Object id) {
        if (operateLog.idType().isInstance(id)) {
            Object result = sysBusinessDictService.queryDictValueList(operateLog.dictCode(), Integer.valueOf(String.valueOf(id)));
            return result;
        } else {
            throw new RuntimeException("请核实id type");
        }
    }

}

自定义注释翻译器

@Component
public class CrmTitleDataFieldNameParse implements IDataFieldNameParser {

    @Autowired
    private CrmTitleService crmTitleService;

    @Override
    public Object getResult(Map<String, Object> oldMap, Map<String, Object> newMap, DataFieldName operateLog) {
        return crmTitleService.getField(operateLog.value(), Integer.valueOf(operateLog.additional()));
    }
}

入参处理器

public interface IChangeRecordLogContentParser {
    /**
     * 获取信息返回查询出的对象
     *
     * @param joinPoint  查询条件的参数
     * @param operateLog 注解
     * @return 获得的结果
     */
    Collection<Object> getResult(JoinPoint joinPoint, ChangeRecordLog operateLog, String tableId);
}

@Component
public class DefaultChangeRecordLogContentParse implements IChangeRecordLogContentParser {

    @Autowired
    private ApplicationContext applicationContext;
    @Override
    public Collection<Object> getResult(JoinPoint joinPoint, ChangeRecordLog operateLog, String tableId) {
        Object info = joinPoint.getArgs()[0];
        Object id = null;
        if (info instanceof List<?>) {
            Collection<Serializable> ids = new ArrayList<>();
            @SuppressWarnings("unchecked")
            Collection<Object> entityList = (Collection<Object>) info;
            entityList.forEach(entity -> {
                Object idTemp = ReflectionUtils.getFieldValue(entity, tableId);
                if (operateLog.idType().isInstance(idTemp)) {
                    ids.add((Serializable) idTemp);
                } else {
                    throw new RuntimeException("请核实id type");
                }
            });
            if (oConvertUtils.listIsNotEmpty(ids)) {
                IService service = (IService) applicationContext.getBean(operateLog.serviceclass());
                return service.listByIds(ids);
            }
            throw new RuntimeException("ids获取失败");
        } else {
            id = ReflectionUtils.getFieldValue(info, tableId);
            if (id == null) {
                return null;
            }
            if (operateLog.idType().isInstance(id)) {
                IService service = (IService) applicationContext.getBean(operateLog.serviceclass());
                Object result = service.getById((Serializable) id);
                return Collections.singletonList(result);
            } else {
                throw new RuntimeException("请核实id type");
            }
        }
    }
}

对比结果处理器

@Data
public class ChangeItem {

    // 修改的字段值id
    private String changeId;

    // 修改的字段值
    private String change;
    // 旧值
    private String old;
    // 新值
    private String newValue;

    public ChangeItem(){

    }
    public ChangeItem(String change,String old,String newValue) {
        this.change = change;
        this.old = old;
        this.newValue = newValue;
    }
}

public interface IChangeRecordLogResultHandler {

    /**
     * 对比结果处理器
     * 参数中Map的String保存的都是实体的id转成了字符串格式保存,用于做索引
     */
    boolean dealResult(Map<String, List<ChangeItem>> result, Map<String, Map<String, Object>> oldMap, Map<String, Map<String, Object>> newMap, ChangeRecordLog operateLog);
}
@Component
public class DefaultChangeRecordLogResultHandler implements IChangeRecordLogResultHandler {

    @Autowired
    private IJhTcClueEvolveService jhTcClueEvolveService;

    @Override
    public boolean dealResult(Map<String, List<ChangeItem>> result, Map<String, Map<String, Object>> oldMap, Map<String, Map<String, Object>> newMap, ChangeRecordLog operateLog) {
        LoginUser user= (LoginUser) SecurityUtils.getSubject().getPrincipal();
        List<ClueEvolveAddVo> list = new ArrayList<>();
        result.forEach((k, v) -> {
            List<ChangeItem> itemList = result.get(k);
            if (oConvertUtils.listIsNotEmpty(itemList)) {
                Map<String, Object> newTempMap = newMap.get(k);
                ClueEvolveAddVo jhTcClueEvolve = new ClueEvolveAddVo();
                //  变更数据保存数据库
                list.add(jhTcClueEvolve);
            }
        });
        if (oConvertUtils.listIsNotEmpty(list)) {
            ClueEvolveService.addBatch(list);
        }
        return true;
    }
}

使用

 @ChangeRecordLog(dealResultclass = DefaultChangeRecordLogResultHandler.class,remark = "编辑合同", serviceclass = IAgreementService.class, needDefaultCompare = true)
    @ApiOperation(value="合同管理-添加或编辑", notes="合同管理-添加或编辑",tags = "合同管理-添加或编辑")
    @PostMapping(value = "/addOrEdit")
    public Result<AgreementAddDto> addOrEdit(@RequestBody @Validated AgreementAddVo addVo) throws Exception {
        List<AgreementRuleDto> ruleList =agreementRuleService.getList(0);
        if(ObjectUtil.isNotEmpty(ruleList)&&ruleList.size()>0) {
            return Result.OK(AgreementService.addOrEdit(addVo));
        }else{
            return Result.error(500,"请添加合同编号规则!");
        }
    }

@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="AgreementAddVo对象", description="合同添加")
public class AgreementAddVo {

    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "id")
    private String id;

    @ApiModelProperty(value = "类型(0:支出1:收入)")
    private String type;

    @ApiModelProperty(value = "合同编号")
    private String agreementNumber;

    @ApiModelProperty(value = "合同文件id")
    private String fileId;
    
    @ApiModelProperty(value = "合同类型(0:项目1:开发2:软件3:培训4:其他)")
    private Integer agreementType;
    
        @ApiModelProperty(value = "变更客户id")
    private String alterConnectionId;

    @ApiModelProperty(value = "合同名称")
    private String name;

    @ApiModelProperty(value = "单位id")
    private String unitId;

    @ApiModelProperty(value = "客户id")
    private String connectionId;

    @ApiModelProperty(value = "合同金额")
    private BigDecimal agreementMoney;

    @ApiModelProperty(value = "负责人ID")
    private String leadUserId;

@Data
@TableName("crm_agreement")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="Agreement对象", description="合同")
public class Agreement implements Serializable {
@TableId(type = IdType.ASSIGN_ID)
    @ApiModelProperty(value = "id")
    private String id;

    @ApiModelProperty(value = "类型(0:支出1:收入)")
    @DataName(name = "类型", readConverterExp = "0=支付,1=收入")
    private String type;

    @ApiModelProperty(value = "合同编号")
    @DataName
    private String agreementNumber;

    @ApiModelProperty(value = "合同文件id")
    private String fileId;

    @ApiModelProperty(value = "合同名称")
    @DataName
    private String name;

    @ApiModelProperty(value = "单位id")
    @DataName(name = "单位", serviceclass = ISysAffiliatedCompanyService.class, readEntityValue = "itemText", needDefaultTranslate = true)
    private String unitId;
      @ApiModelProperty(value = "合同金额")
    @DataName
    private BigDecimal agreementMoney;

    @ApiModelProperty(value = "负责人ID")
    @DataName(name = "负责人", serviceclass = ISysUserSaasService.class, readEntityValue = "realname", needDefaultTranslate = true)
    private String leadUserId;
       // 翻译字典
    @DataFieldName(value = "agreementType", additional = "15", parseclass = CrmTitleDataFieldNameParse.class, readEntityValue = "showName")
    @DataName(name = "合同类型", idType = Integer.class, needDefaultTranslate = true,
            parseclass = SysBusinessDataNameDictContentParse.class, dictCode = "agreement_type", readEntityValue = "itemText")
    private Integer agreementType;
      @ApiModelProperty(value = "变更客户id")
    @DataName(name = "客户", serviceclass = IJhTcClueConnectionService.class, readEntityValue = "name", needDefaultTranslate = true)
    private String alterConnectionId;
}

工具类


@Slf4j
public class ReflectionUtils {
    /**
     * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
     * @param obj 读取的对象
     * @param fieldName 读取的列
     * @return 属性值
     */
    public static Object getFieldValue(final Object obj, final String fieldName) {
        Field field = getAccessibleField(obj, fieldName);
        if (field == null) {
            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
        }

        Object result = null;
        try {
            result = field.get(obj);
        } catch (IllegalAccessException e) {
            log.error("不可能抛出的异常{}", e.getMessage());
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.如向上转型到Object仍无法找到, 返回null.
     * @param obj  查找的对象
     * @param fieldName  列名
     * @return 列
     */
    public static Field getAccessibleField(final Object obj, final String fieldName) {
        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                Field field = superClass.getDeclaredField(fieldName);
                makeAccessible(field);
                return field;
            } catch (NoSuchFieldException e) { // NOSONAR
                // Field不在当前类定义,继续向上转型
                e.printStackTrace();
                continue; // new add
            }
        }
        return null;
    }

    /**
     * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
     * @param
     */
    public static void makeAccessible(Field field) {
        if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier
                .isFinal(field.getModifiers())) && !field.isAccessible()) {
            field.setAccessible(true);
        }
    }

    /**
     * 获取两个对象同名属性内容不相同的列表
     * @param class1 old对象
     * @param class2 new对象
     * @return  区别列表
     * @throws ClassNotFoundException 异常
     * @throws IllegalAccessException 异常
     */
    public static List<Map<String ,Object>> compareTwoClass(Object class1, Object class2) throws ClassNotFoundException, IllegalAccessException {
        List<Map<String,Object>> list=new ArrayList<Map<String, Object>>();
        // 获取对象的class
        Class<?> clazz1 = class1.getClass();
        Class<?> clazz2 = class2.getClass();
        // 获取对象的属性列表
        Field[] field1 = clazz1.getDeclaredFields();
        Field[] field2 = clazz2.getDeclaredFields();
        StringBuilder sb=new StringBuilder();
        // 遍历属性列表field1
        for(int i=0;i<field1.length;i++) {
            // 遍历属性列表field2
            for (int j = 0; j < field2.length; j++) {
                // 如果field1[i]属性名与field2[j]属性名内容相同
                if (field1[i].getName().equals(field2[j].getName())) {
                    if (field1[i].getName().equals(field2[j].getName())) {
                        field1[i].setAccessible(true);
                        field2[j].setAccessible(true);
                        // 如果field1[i]属性值与field2[j]属性值内容不相同
                        if (!compareTwo(field1[i].get(class1), field2[j].get(class2))) {
                            Map<String, Object> map2 = new HashMap<String, Object>();
                            DataName name=field1[i].getAnnotation(DataName.class);
                            String fieldName="";
                            if(name!=null){
                                fieldName=name.name();
                            } else {
                                fieldName=field1[i].getName();
                            }
                            map2.put("name", fieldName);
                            map2.put("old", field1[i].get(class1));
                            map2.put("new", field2[j].get(class2));
                            list.add(map2);
                        }
                        break;
                    }
                }
            }
        }
        return list;
    }

    /**
     * 对比两个数据是否内容相同
     * @param  object1  比较对象1
     * @param  object2  比较对象2
     * @return boolean类型
     */
    public static boolean compareTwo(Object object1,Object object2){
        if(object1==null&&object2==null){
            return true;
        }
        if(object1==null&&object2!=null){
            return false;
        }
        if(object1.equals(object2)){
            return true;
        }
        return false;
    }
}
@Slf4j
public class oConvertUtils {
	public static boolean isEmpty(Object object) {
		if (object == null) {
			return (true);
		}
		if ("".equals(object)) {
			return (true);
		}
		if ("null".equals(object)) {
			return (true);
		}
		return (false);
	}
	
	public static boolean isNotEmpty(Object object) {
		if (object != null && !object.equals("") && !object.equals("null")) {
			return (true);
		}
		return (false);
	}

	public static String decode(String strIn, String sourceCode, String targetCode) {
		String temp = code2code(strIn, sourceCode, targetCode);
		return temp;
	}

	public static String StrToUTF(String strIn, String sourceCode, String targetCode) {
		strIn = "";
		try {
			strIn = new String(strIn.getBytes("ISO-8859-1"), "GBK");
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return strIn;

	}

	private static String code2code(String strIn, String sourceCode, String targetCode) {
		String strOut = null;
		if (strIn == null || (strIn.trim()).equals("")) {
			return strIn;
		}
		try {
			byte[] b = strIn.getBytes(sourceCode);
			for (int i = 0; i < b.length; i++) {
				System.out.print(b[i] + "  ");
			}
			strOut = new String(b, targetCode);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return strOut;
	}

	public static int getInt(String s, int defval) {
		if (s == null || s == "") {
			return (defval);
		}
		try {
			return (Integer.parseInt(s));
		} catch (NumberFormatException e) {
			return (defval);
		}
	}

	public static int getInt(String s) {
		if (s == null || s == "") {
			return 0;
		}
		try {
			return (Integer.parseInt(s));
		} catch (NumberFormatException e) {
			return 0;
		}
	}

	public static int getInt(String s, Integer df) {
		if (s == null || s == "") {
			return df;
		}
		try {
			return (Integer.parseInt(s));
		} catch (NumberFormatException e) {
			return 0;
		}
	}

	public static Integer[] getInts(String[] s) {
		Integer[] integer = new Integer[s.length];
		if (s == null) {
			return null;
		}
		for (int i = 0; i < s.length; i++) {
			integer[i] = Integer.parseInt(s[i]);
		}
		return integer;

	}

	public static double getDouble(String s, double defval) {
		if (s == null || s == "") {
			return (defval);
		}
		try {
			return (Double.parseDouble(s));
		} catch (NumberFormatException e) {
			return (defval);
		}
	}

	public static double getDou(Double s, double defval) {
		if (s == null) {
			return (defval);
		}
		return s;
	}

	/*public static Short getShort(String s) {
		if (StringUtil.isNotEmpty(s)) {
			return (Short.parseShort(s));
		} else {
			return null;
		}
	}*/

	public static int getInt(Object object, int defval) {
		if (isEmpty(object)) {
			return (defval);
		}
		try {
			return (Integer.parseInt(object.toString()));
		} catch (NumberFormatException e) {
			return (defval);
		}
	}
	
	public static Integer getInt(Object object) {
		if (isEmpty(object)) {
			return null;
		}
		try {
			return (Integer.parseInt(object.toString()));
		} catch (NumberFormatException e) {
			return null;
		}
	}

	public static int getInt(BigDecimal s, int defval) {
		if (s == null) {
			return (defval);
		}
		return s.intValue();
	}

	public static Integer[] getIntegerArry(String[] object) {
		int len = object.length;
		Integer[] result = new Integer[len];
		try {
			for (int i = 0; i < len; i++) {
				result[i] = new Integer(object[i].trim());
			}
			return result;
		} catch (NumberFormatException e) {
			return null;
		}
	}

	public static String getString(String s) {
		return (getString(s, ""));
	}

	/**
	 * 转义成Unicode编码
	 * @param s
	 * @return
	 */
	/*public static String escapeJava(Object s) {
		return StringEscapeUtils.escapeJava(getString(s));
	}*/
	
	public static String getString(Object object) {
		if (isEmpty(object)) {
			return "";
		}
		return (object.toString().trim());
	}

	public static String getString(int i) {
		return (String.valueOf(i));
	}

	public static String getString(float i) {
		return (String.valueOf(i));
	}

	public static String getString(String s, String defval) {
		if (isEmpty(s)) {
			return (defval);
		}
		return (s.trim());
	}

	public static String getString(Object s, String defval) {
		if (isEmpty(s)) {
			return (defval);
		}
		return (s.toString().trim());
	}

	public static long stringToLong(String str) {
		Long test = new Long(0);
		try {
			test = Long.valueOf(str);
		} catch (Exception e) {
		}
		return test.longValue();
	}

	/**
	 * 获取本机IP
	 */
	public static String getIp() {
		String ip = null;
		try {
			InetAddress address = InetAddress.getLocalHost();
			ip = address.getHostAddress();

		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		return ip;
	}

	/**
	 * 判断一个类是否为基本数据类型。
	 * 
	 * @param clazz
	 *            要判断的类。
	 * @return true 表示为基本数据类型。
	 */
	private static boolean isBaseDataType(Class clazz) throws Exception {
		return (clazz.equals(String.class) || clazz.equals(Integer.class) || clazz.equals(Byte.class) || clazz.equals(Long.class) || clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Character.class) || clazz.equals(Short.class) || clazz.equals(BigDecimal.class) || clazz.equals(BigInteger.class) || clazz.equals(Boolean.class) || clazz.equals(Date.class) || clazz.isPrimitive());
	}

	/**
	 * @param request
	 *            IP
	 * @return IP Address
	 */
	public static String getIpAddrByRequest(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

	/**
	 * @return 本机IP
	 * @throws SocketException
	 */
	public static String getRealIp() throws SocketException {
		String localip = null;// 本地IP,如果没有配置外网IP则返回它
		String netip = null;// 外网IP

		Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
		InetAddress ip = null;
		boolean finded = false;// 是否找到外网IP
		while (netInterfaces.hasMoreElements() && !finded) {
			NetworkInterface ni = netInterfaces.nextElement();
			Enumeration<InetAddress> address = ni.getInetAddresses();
			while (address.hasMoreElements()) {
				ip = address.nextElement();
				if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 外网IP
					netip = ip.getHostAddress();
					finded = true;
					break;
				} else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 内网IP
					localip = ip.getHostAddress();
				}
			}
		}

		if (netip != null && !"".equals(netip)) {
			return netip;
		} else {
			return localip;
		}
	}

	/**
	 * java去除字符串中的空格、回车、换行符、制表符
	 * 
	 * @param str
	 * @return
	 */
	public static String replaceBlank(String str) {
		String dest = "";
		if (str != null) {
			Pattern p = Pattern.compile("\\s*|\t|\r|\n");
			Matcher m = p.matcher(str);
			dest = m.replaceAll("");
		}
		return dest;

	}

	/**
	 * 判断元素是否在数组内
	 * 
	 * @param substring
	 * @param source
	 * @return
	 */
	public static boolean isIn(String substring, String[] source) {
		if (source == null || source.length == 0) {
			return false;
		}
		for (int i = 0; i < source.length; i++) {
			String aSource = source[i];
			if (aSource.equals(substring)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 获取Map对象
	 */
	public static Map<Object, Object> getHashMap() {
		return new HashMap<Object, Object>();
	}

	/**
	 * SET转换MAP
	 * 
	 * @param str
	 * @return
	 */
	public static Map<Object, Object> SetToMap(Set<Object> setobj) {
		Map<Object, Object> map = getHashMap();
		for (Iterator iterator = setobj.iterator(); iterator.hasNext();) {
			Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) iterator.next();
			map.put(entry.getKey().toString(), entry.getValue() == null ? "" : entry.getValue().toString().trim());
		}
		return map;

	}

	public static boolean isInnerIP(String ipAddress) {
		boolean isInnerIp = false;
		long ipNum = getIpNum(ipAddress);
		/**
		 * 私有IP:A类 10.0.0.0-10.255.255.255 B类 172.16.0.0-172.31.255.255 C类 192.168.0.0-192.168.255.255 当然,还有127这个网段是环回地址
		 **/
		long aBegin = getIpNum("10.0.0.0");
		long aEnd = getIpNum("10.255.255.255");
		long bBegin = getIpNum("172.16.0.0");
		long bEnd = getIpNum("172.31.255.255");
		long cBegin = getIpNum("192.168.0.0");
		long cEnd = getIpNum("192.168.255.255");
		isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd) || ipAddress.equals("127.0.0.1");
		return isInnerIp;
	}

	private static long getIpNum(String ipAddress) {
		String[] ip = ipAddress.split("\\.");
		long a = Integer.parseInt(ip[0]);
		long b = Integer.parseInt(ip[1]);
		long c = Integer.parseInt(ip[2]);
		long d = Integer.parseInt(ip[3]);

		long ipNum = a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
		return ipNum;
	}

	private static boolean isInner(long userIp, long begin, long end) {
		return (userIp >= begin) && (userIp <= end);
	}
	
	/**
	 * 将下划线大写方式命名的字符串转换为驼峰式。
	 * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
	 * 例如:hello_world->helloWorld
	 * 
	 * @param name
	 *            转换前的下划线大写方式命名的字符串
	 * @return 转换后的驼峰式命名的字符串
	 */
	public static String camelName(String name) {
		StringBuilder result = new StringBuilder();
		// 快速检查
		if (name == null || name.isEmpty()) {
			// 没必要转换
			return "";
		} else if (!name.contains("_")) {
			// 不含下划线,仅将首字母小写
			//update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
			//update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
			return name.substring(0, 1).toLowerCase() + name.substring(1).toLowerCase();
			//update-end--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
		}
		// 用下划线将原始字符串分割
		String camels[] = name.split("_");
		for (String camel : camels) {
			// 跳过原始字符串中开头、结尾的下换线或双重下划线
			if (camel.isEmpty()) {
				continue;
			}
			// 处理真正的驼峰片段
			if (result.length() == 0) {
				// 第一个驼峰片段,全部字母都小写
				result.append(camel.toLowerCase());
			} else {
				// 其他的驼峰片段,首字母大写
				result.append(camel.substring(0, 1).toUpperCase());
				result.append(camel.substring(1).toLowerCase());
			}
		}
		return result.toString();
	}
	
	/**
	 * 将下划线大写方式命名的字符串转换为驼峰式。
	 * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
	 * 例如:hello_world,test_id->helloWorld,testId
	 * 
	 * @param name
	 *            转换前的下划线大写方式命名的字符串
	 * @return 转换后的驼峰式命名的字符串
	 */
	public static String camelNames(String names) {
		if(names==null||names.equals("")){
			return null;
		}
		StringBuffer sf = new StringBuffer();
		String[] fs = names.split(",");
		for (String field : fs) {
			field = camelName(field);
			sf.append(field + ",");
		}
		String result = sf.toString();
		return result.substring(0, result.length() - 1);
	}
	
	//update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
	/**
	 * 将下划线大写方式命名的字符串转换为驼峰式。(首字母写)
	 * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
	 * 例如:hello_world->HelloWorld
	 * 
	 * @param name
	 *            转换前的下划线大写方式命名的字符串
	 * @return 转换后的驼峰式命名的字符串
	 */
	public static String camelNameCapFirst(String name) {
		StringBuilder result = new StringBuilder();
		// 快速检查
		if (name == null || name.isEmpty()) {
			// 没必要转换
			return "";
		} else if (!name.contains("_")) {
			// 不含下划线,仅将首字母小写
			return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();
		}
		// 用下划线将原始字符串分割
		String camels[] = name.split("_");
		for (String camel : camels) {
			// 跳过原始字符串中开头、结尾的下换线或双重下划线
			if (camel.isEmpty()) {
				continue;
			}
			// 其他的驼峰片段,首字母大写
			result.append(camel.substring(0, 1).toUpperCase());
			result.append(camel.substring(1).toLowerCase());
		}
		return result.toString();
	}
	//update-end--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
	
	/**
	 * 将驼峰命名转化成下划线
	 * @param para
	 * @return
	 */
	public static String camelToUnderline(String para){
        if(para.length()<3){
        	return para.toLowerCase(); 
        }
        StringBuilder sb=new StringBuilder(para);
        int temp=0;//定位
        //从第三个字符开始 避免命名不规范 
        for(int i=2;i<para.length();i++){
            if(Character.isUpperCase(para.charAt(i))){
                sb.insert(i+temp, "_");
                temp+=1;
            }
        }
        return sb.toString().toLowerCase(); 
	}

	/**
	 * 随机数
	 * @param place 定义随机数的位数
	 */
	public static String randomGen(int place) {
		String base = "qwertyuioplkjhgfdsazxcvbnmQAZWSXEDCRFVTGBYHNUJMIKLOP0123456789";
		StringBuffer sb = new StringBuffer();
		Random rd = new Random();
		for(int i=0;i<place;i++) {
			sb.append(base.charAt(rd.nextInt(base.length())));
		}
		return sb.toString();
	}
	
	/**
	 * 获取类的所有属性,包括父类
	 * 
	 * @param object
	 * @return
	 */
	public static Field[] getAllFields(Object object) {
		Class<?> clazz = object.getClass();
		List<Field> fieldList = new ArrayList<>();
		while (clazz != null) {
			fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
			clazz = clazz.getSuperclass();
		}
		Field[] fields = new Field[fieldList.size()];
		fieldList.toArray(fields);
		return fields;
	}
	
	/**
	  * 将map的key全部转成小写
	 * @param list
	 * @return
	 */
	public static List<Map<String, Object>> toLowerCasePageList(List<Map<String, Object>> list){
		List<Map<String, Object>> select = new ArrayList<>();
		for (Map<String, Object> row : list) {
			 Map<String, Object> resultMap = new HashMap<>();
			 Set<String> keySet = row.keySet(); 
			 for (String key : keySet) { 
				 String newKey = key.toLowerCase(); 
				 resultMap.put(newKey, row.get(key)); 
			 }
			 select.add(resultMap);
		}
		return select;
	}

	/**
	 * 将Map中的key由下划线转换为驼峰
	 *
	 * @param map
	 * @return
	 */
	public static Map<String, Object> formatHumpName(Map<String, Object> map) {
		Map<String, Object> newMap = new HashMap<String, Object>();
		Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry<String, Object> entry = it.next();
			String key = entry.getKey();
			String newKey = camelName(key);
			newMap.put(newKey, entry.getValue());
		}
		return newMap;
	}

	/**
	 * 将entityList转换成modelList
	 * @param fromList
	 * @param tClass
	 * @param <F>
	 * @param <T>
	 * @return
	 */
	public static<F,T> List<T> entityListToModelList(List<F> fromList, Class<T> tClass){
		if(fromList == null || fromList.isEmpty()){
			return null;
		}
		List<T> tList = new ArrayList<>();
		for(F f : fromList){
			T t = entityToModel(f, tClass);
			tList.add(t);
		}
		return tList;
	}

	public static<F,T> T entityToModel(F entity, Class<T> modelClass) {
		log.debug("entityToModel : Entity属性的值赋值到Model");
		Object model = null;
		if (entity == null || modelClass ==null) {
			return null;
		}

		try {
			model = modelClass.newInstance();
		} catch (InstantiationException e) {
			log.error("entityToModel : 实例化异常", e);
		} catch (IllegalAccessException e) {
			log.error("entityToModel : 安全权限异常", e);
		}
		BeanUtils.copyProperties(entity, model);
		return (T)model;
	}

	/**
	 * 判断 list 是否为空
	 *
	 * @param list
	 * @return true or false
	 * list == null		: true
	 * list.size() == 0	: true
	 */
	public static boolean listIsEmpty(Collection list) {
		return (list == null || list.size() == 0);
	}

	/**
	 * 判断 list 是否不为空
	 *
	 * @param list
	 * @return true or false
	 * list == null		: false
	 * list.size() == 0	: false
	 */
	public static boolean listIsNotEmpty(Collection list) {
		return !listIsEmpty(list);
	}

}

结果

image

粤ICP备2022112743号 粤公网安备 44010502002407号