本篇基于下面两篇博客的内容整合实现:
之前的博客中已经捋清了在只有日期和一列数据的情况下动态生成环比、同步数据的逻辑,
但只是一个demo,本次要在生产环境使用,就要进行进一步的改造。
首先我们定义一个注解,标记那个方法需要进行增强处理,month指定日期列,value就是数据列,limit指定要保留的行数
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RateGenerator { String month(); String value(); int limit(); }
然后我们定义一个AOP,在有上面注解的地方进行拦截处理
@Slf4j @Component @Aspect public class RateGeneratorAOP { @Pointcut("@annotation(com.pangu.utils.format.RateGenerator)") public void ServiceAspect() { } @Autowired private RateGeneratorProcessor processor; @Around("ServiceAspect()") public Object around(ProceedingJoinPoint proceedingJoinPoint) { Object[] args = proceedingJoinPoint.getArgs(); MethodSignature signature =(MethodSignature) proceedingJoinPoint.getSignature(); Method method = signature.getMethod(); RateGenerator annotation = method.getAnnotation(RateGenerator.class); Object proceed = null; List<Object> newResultList=null; try { proceed = proceedingJoinPoint.proceed(args); if(proceed instanceof List&& annotation!=null){ List<Object> resultList = (List<Object>) proceed; newResultList = processor.process(resultList, annotation); } } catch (Throwable e) { log.error(e.getMessage()); } return newResultList; } }
当然核心代码就在processor里面,processor的逻辑不复杂
1.拿到原始数据,根据指定的日期列和数据列,收集成一个map
2.对应每一行数据的日期,计算得到上月日期和去年同月日期,并从map中获取对应value
3.计算同比、环比,分母为零时,同比环比也设为0
4.基于旧object建立动态bean,target,将新生成的同步、环比放进target中
5.将target保留最后几个元素后返回
import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.lang.reflect.Field; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * @Author : wangbin * @Date : 2023/3/16 9:22 * @Description: */ @Slf4j @Service public class RateGeneratorProcessor { private static final java.text.DecimalFormat decimalFormat = new java.text.DecimalFormat("#.##"); private static final java.text.DecimalFormat decimalFormatV3 = new java.text.DecimalFormat("#.###"); public List<Object> process(List<Object> objectList,RateGenerator annotation) throws IllegalAccessException { Map<String, Double> map = new HashMap<>(); Map<Object, Tuple> objectMap = new HashMap<>(); String monthFieldName = annotation.month(); String valueFieldName = annotation.value(); int limit = annotation.limit(); int size = objectList.size(); Class<?> aClass = objectList.get(0).getClass(); for (Object o : objectList) { Tuple tuple = new Tuple(); Field[] fields = aClass.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if(field.getName().equals(monthFieldName)){ tuple.setMonth(field.get(o).toString()); } if(field.getName().equals(valueFieldName)){ tuple.setValue(field.get(o).toString()); } } objectMap.put(o,tuple); map.put(tuple.getMonth(),Double.parseDouble( tuple.getValue())); } return objectList.stream().map(o -> { Tuple tuple = objectMap.get(o); String key = tuple.getMonth(); Double value = Double.valueOf(tuple.getValue()); String lastMonth = getLastMonth(key); String lastYearMonth = getLastYearMonth(key); Double basisRate = basisRate(map.getOrDefault(lastYearMonth, 0d), value); Double loopRate = basisRate(map.getOrDefault(lastMonth, 0d), value); Map<String, Object> newMap = new HashMap<>(); //百分比保留两位小数 newMap.put("tb", decimalFormat.format(basisRate)); newMap.put("hb", decimalFormat.format(loopRate)); return DynamicBean.getTarget(o, newMap); }).skip(size - limit).collect(Collectors.toList()); } @Data static class Tuple{ String month; String value; } public static Double basisRate(Double lastVal,Double thisVal){ if(lastVal==null||lastVal==0d){ return 0d; } return 100*(thisVal-lastVal)/lastVal; } //上月 public String getLastMonth(String month){ DateTimeFormatter format = new DateTimeFormatterBuilder() .appendPattern("yyyy-MM") .parseDefaulting(ChronoField.DAY_OF_MONTH, 1) .toFormatter(); LocalDate parse = LocalDate.from(format.parse(month)); LocalDate lastMonth = parse.minusMonths(1); return format.format(lastMonth); } //去年同月 public String getLastYearMonth(String month){ DateTimeFormatter format = new DateTimeFormatterBuilder() .appendPattern("yyyy-MM") .parseDefaulting(ChronoField.DAY_OF_MONTH, 1) .toFormatter(); LocalDate parse = LocalDate.from(format.parse(month)); LocalDate lastMonth = parse.minusYears(1); return format.format(lastMonth); } }
最后是动态bean的生成,使用了spring的cglib包
import org.apache.commons.beanutils.PropertyUtilsBean; import org.springframework.cglib.beans.BeanGenerator; import org.springframework.cglib.beans.BeanMap; import java.beans.PropertyDescriptor; import java.util.HashMap; import java.util.Map; /** * @Author : wangbin * @Date : 2023/3/16 9:12 * @Description: */ public class DynamicBean { /** * 目标对象 */ private Object target; /** * 属性集合 */ private BeanMap beanMap; public DynamicBean(Class superclass, Map<String, Class> propertyMap) { this.target = generateBean(superclass, propertyMap); this.beanMap = BeanMap.create(this.target); } /** * bean 添加属性和值 * * @param property * @param value */ public void setValue(String property, Object value) { beanMap.put(property, value); } /** * 获取属性值 * * @param property * @return */ public Object getValue(String property) { return beanMap.get(property); } /** * 获取对象 * * @return */ public Object getTarget() { return this.target; } /** * 根据属性生成对象 * * @param superclass * @param propertyMap * @return */ private Object generateBean(Class superclass, Map<String, Class> propertyMap) { BeanGenerator generator = new BeanGenerator(); if (null != superclass) { generator.setSuperclass(superclass); } BeanGenerator.addProperties(generator, propertyMap); return generator.create(); } public static Object getTarget(Object dest, Map<String, Object> addProperties) { // 获取属性map PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean(); PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(dest); Map<String, Class> propertyMap = new HashMap<>(); for (PropertyDescriptor d : descriptors) { if (!"class".equalsIgnoreCase(d.getName())) { propertyMap.put(d.getName(), d.getPropertyType()); } } // 添加新属性 addProperties.forEach((k, v) -> propertyMap.put(k, v.getClass())); // 创建动态bean DynamicBean dynamicBean = new DynamicBean(dest.getClass(), propertyMap); // 给bean添加旧数据 propertyMap.forEach((k, v) -> { try { if (!addProperties.containsKey(k)) { dynamicBean.setValue(k, propertyUtilsBean.getNestedProperty(dest, k)); } } catch (Exception e) { e.printStackTrace(); } }); // 添加新数据 addProperties.forEach((k, v) -> { try { dynamicBean.setValue(k, v); } catch (Exception e) { e.printStackTrace(); } }); return dynamicBean.getTarget(); } }
标签:终极版,java,String,一组,format,return,import,public From: https://www.cnblogs.com/wangbin2188/p/17222088.html