java实现一组数据计算同比环比(终极版)

本篇基于下面两篇博客的内容整合实现:

java实现一组数据计算同比环比

CGLIB基于一个已有类动态生成一个子类bean(转)

之前的博客中已经捋清了在只有日期和一列数据的情况下动态生成环比、同步数据的逻辑,

但只是一个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();
    }
}
复制代码

 

posted @   Mars.wang  阅读(1773)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
历史上的今天:
2022-03-16 深入理解java虚拟机笔记(3)垃圾收集器与内存分配
2022-03-16 深入理解java虚拟机笔记(2)java虚拟机对象创建
2022-03-16 深入理解java虚拟机笔记(1)java虚拟机内存划分
点击右上角即可分享
微信分享提示