Calculate the percentage of the tree structure, construct the tree, sort the ranking

package org.jeecg.modules.statistics.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jodd.util.StringUtil;
import org.jeecg.common.config.IotDBSessionConfig;
import org.jeecg.common.utils.IotResultUtil;
import org.jeecg.modules.alarm.service.IEmElectricityAlarmRecordService;
import org.jeecg.modules.basic.entity.EmBasicRegion;
import org.jeecg.modules.basic.service.IEmBasicRegionService;
import org.jeecg.modules.basic.vo.EmBasicRegionVo;
import org.jeecg.modules.device.entity.EmElecMeter;
import org.jeecg.modules.device.service.IEmElecMeterService;
import org.jeecg.modules.statistics.mapper.DeviceRankingMapper;

import org.jeecg.modules.statistics.query.DeviceRankingQuery;
import org.jeecg.modules.statistics.service.DeviceRankingService;

import org.jeecg.modules.statistics.util.SankeyGraph;
import org.jeecg.modules.statistics.util.SankeyGraphConverter;
import org.jeecg.modules.statistics.vo.DeviceRankingVo;
import org.jeecg.modules.statistics.vo.ModbusRegisterVo;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class DeviceRankingServiceImpl extends ServiceImpl<DeviceRankingMapper, DeviceRankingQuery> implements DeviceRankingService {<!-- -->
    static DecimalFormat decimalFormat = new DecimalFormat("0.00");

    @Autowired
    IotDBSessionConfig iotDBSessionConfig;

    @Autowired
    IEmBasicRegionService emBasicRegionService;

    @Autowired
    IEmElecMeterService elecMeterService;

    @Autowired
    IEmElectricityAlarmRecordService emElectricityAlarmRecordService;

    /**
     * Query regional ranking
     * @param query
     * @return
     */
    @Override
    public List<EmBasicRegionVo> selectData(DeviceRankingQuery query) {<!-- -->
        long startTime = query.getStartTime().getTime();
        long endTime = query.getEndTime().getTime();
        List<EmBasicRegionVo> eleclist = this.baseMapper.selectElecMeter(query);
        List<EmBasicRegionVo> basicList = this.baseMapper.selectEmBasicRegionVo(query);
        Double parentSum = 0.0;
        for (EmBasicRegionVo basicRegionVo : eleclist) {<!-- -->
            String iotTableName = basicRegionVo.getCode();
            Double sum = 0.0;
            if (StringUtil.isNotEmpty(iotTableName)) {<!-- -->
                sum = IotResultUtil.elecSum(iotTableName, query.getName(), startTime, endTime, iotDBSessionConfig);
            }
            parentSum + = sum;
            basicRegionVo.setSum(sum);
        }
        basicList.addAll(eleclist);
        List<EmBasicRegionVo> treeList = getEmBasicRegionVoList(query, basicList, parentSum);
        return treeList;
    }

    @NotNull
    private List<EmBasicRegionVo> getEmBasicRegionVoList(DeviceRankingQuery query, List<EmBasicRegionVo> basicList, Double parentSum) {<!-- -->
        List<EmBasicRegionVo> treeList = getTree(basicList, query.getPid());
        int i = 0;
        for (EmBasicRegionVo emBasicRegionVo : treeList) {<!-- -->
            i + + ;
            Double sum = updateParentData(emBasicRegionVo);
            emBasicRegionVo.setSum(sum);
            sortTree(emBasicRegionVo);
            Double percent = Double.valueOf(decimalFormat.format(sum / parentSum * 100));
            emBasicRegionVo.setPercent(percent);
            calculatePercent(emBasicRegionVo, sum, i);
        }
        return treeList;
    }


    public static void sortTree(EmBasicRegionVo root) {<!-- -->
        if (root == null) {<!-- -->
            return;
        }
        // Sort the child nodes of the current node
        root.getChildren().sort((a, b) -> (int) (b.getSum() - a.getSum()));

        // Sort each child node recursively
        for (EmBasicRegionVo child : root.getChildren()) {<!-- -->
            sortTree(child);
        }
    }


    /**
     * Traverse the tree (multiple root nodes)
     *
     * @param menus
     */
    public List<EmBasicRegionVo> getTree(List<EmBasicRegionVo> menus, String code) {<!-- -->
        // Filter out the root node
        return menus.stream().filter(m -> Objects.equals(m.getPid(), code)).map(m -> {<!-- -->
            //Set the child nodes of each tree
            m.setChildren(getChildrens(m, menus));
            return m;
        }).collect(Collectors.toList());
    }

    /**
     * Get child nodes
     *
     * @param root
     * @param menus
     * @return
     */
    public List<EmBasicRegionVo> getChildrens(EmBasicRegionVo root, List<EmBasicRegionVo> menus) {<!-- -->
        //Filter direct child nodes
        return menus.stream().filter(m -> Objects.equals(m.getPid(), root.getId())).map(m -> {<!-- -->
            // Set child nodes recursively
            m.setChildren(getChildrens(m, menus));
            return m;
        }).collect(Collectors.toList());
    }


    /**
     * Recursively find the sum of data of each node
     *
     * @param root
     * @return
     */
    public Double updateParentData(EmBasicRegionVo root) {<!-- -->
        if (root == null) {<!-- -->
            return 0.0;
        }
        Double sum = root.getSum() == null ? 0.0 : root.getSum();
        for (EmBasicRegionVo child : root.getChildren()) {<!-- -->
            Double childSum = updateParentData(child); // Recursively update the data of child nodes
            sum + = childSum; // Accumulate the data of child nodes
        }
        root.setSum(sum);
        //Update the data of the parent node to the sum of the data of the child nodes
        return sum;
    }

    @Override
    public List<String> getElecMeterList(String pid) {<!-- -->
        Map map = new HashMap();
        map.put("deleted", 0);
        List<EmElecMeter> emElecMeters = elecMeterService.listByMap(map);
        List<EmBasicRegion> emBasicRegions = new ArrayList<>();
        for (EmElecMeter emElecMeter : emElecMeters) {<!-- -->
            emBasicRegions.add(convert(emElecMeter));
        }
        List<String> menusList = emElectricityAlarmRecordService.getMenusByCode(pid, emBasicRegions);
        return menusList;
    }

    /**
     * Query area tree. Tree application cannot be moved.
     *
     * @param pid
     * @return
     */
    @Override
    public List<EmBasicRegionVo> getElecMeterTree(String pid, Integer type, DeviceRankingQuery query) {<!-- -->
        List<EmBasicRegionVo>eleclist;
        List<EmBasicRegionVo> waterMeter;
        List<EmBasicRegionVo> basicList = this.baseMapper.selectEmBasicRegionVo(query);
        if ((type == null) || (type == 0) || (type == 1)) {<!-- -->
            eleclist = this.baseMapper.selectElecMeter(query);
            basicList.addAll(eleclist);
        }
        if ((type == null) || (type == 0) || (type == 2)) {<!-- -->
            waterMeter = this.baseMapper.selectWaterMeter(query);
            basicList.addAll(waterMeter);
        }
        List<EmBasicRegionVo> treeList = getTree(basicList, pid);
        return treeList;
    }

    /**
     * Query energy flow diagram
     * @param pid
     * @param type
     * @return
     */
    public SankeyGraph getDeviceData(String pid, Integer type) {<!-- -->
        List<EmBasicRegionVo>eleclist;
        List<EmBasicRegionVo> waterMeter;
        DeviceRankingQuery query = new DeviceRankingQuery();
        List<EmBasicRegionVo> basicList = this.baseMapper.selectEmBasicRegionVo(query);
        if ((type == null) || (type == 0) || (type == 1)) {<!-- -->
            eleclist = this.baseMapper.selectElecMeter(query);
            basicList.addAll(eleclist);
        }
        if ((type == null) || (type == 0) || (type == 2)) {<!-- -->
            waterMeter = this.baseMapper.selectWaterMeter(query);
            basicList.addAll(waterMeter);
        }
        SankeyGraph sankeyGraph = SankeyGraphConverter.convertToSankeyGraph(basicList);
        return sankeyGraph;
    }

    public static EmBasicRegion convert(EmElecMeter source) {<!-- -->
        EmBasicRegion target = new EmBasicRegion();
        target.setId(source.getId());
        target.setPid(source.getRegionId());
        return target;
    }


    /**
     * Get data for each device
     *
     * @param query
     * @return
     */
    @Override
    public List<DeviceRankingVo> getDeviceRanking(DeviceRankingQuery query) {<!-- -->
        //Create a DecimalFormat object for formatting percentage values
        DecimalFormat decimalFormat = new DecimalFormat("0.00");
        long startTime = query.getStartTime().getTime();
        long endTime = query.getEndTime().getTime();
        List<String> strings = getTreeList(query.getPid(), query);
        query.setRegionIdList(strings);
        List<DeviceRankingVo> eleclist = this.baseMapper.selectDeviceRanking(query);
        Double totalSum = 0.0;
        for (DeviceRankingVo deviceRankingVo : eleclist) {<!-- -->
            Double sum = IotResultUtil.elecSum(deviceRankingVo.getCode(), query.getName(), startTime, endTime, iotDBSessionConfig);
            deviceRankingVo.setValue(sum);
            totalSum + = sum;
        }
        //You can continue to abbreviate when the body of the expression has only one line of code
        coll(eleclist);
        for (int i = 0; i < eleclist.size(); i + + ) {<!-- -->
            DeviceRankingVo deviceRankingVo = eleclist.get(i);
            Double formattedPercentage = Double.valueOf(decimalFormat.format(deviceRankingVo.getValue() / totalSum * 100)); // Formatted percentage value
            deviceRankingVo.setPercent(formattedPercentage);
            deviceRankingVo.setUnit(query.getUnit());
            deviceRankingVo.setSort(i + 1);
        }
        return eleclist;
    }

    /**
     * Sort
     *
     * @param list
     */
    public void coll(List<DeviceRankingVo> list) {<!-- -->
        Collections.sort(list, new Comparator<DeviceRankingVo>() {<!-- -->
            @Override
            public int compare(DeviceRankingVo o1, DeviceRankingVo o2) {<!-- -->
                return (int) (o2.getValue() - o1.getValue()); //Sort by quantity from large to small
            }
        });
    }

    @Override
    public List<ModbusRegisterVo> selectModbusRegister(DeviceRankingQuery query) {<!-- -->
        return this.baseMapper.selectModbusRegister(query);
    }

    /**
     * Traverse the tree (multiple root nodes)
     *
     * @param query
     */
    List<String> getTreeList(String code, DeviceRankingQuery query) {<!-- -->
        List<String> str = new ArrayList<>();
        List<EmBasicRegionVo> eleclist = this.baseMapper.selectElecMeter(query);
        List<EmBasicRegionVo> basicList = this.baseMapper.selectEmBasicRegionVo(query);
        basicList.addAll(eleclist);
        getTree(basicList, code, str);
        return str;
    }

    public List<EmBasicRegionVo> getTree(List<EmBasicRegionVo> menus, String code, List<String> str) {<!-- -->
        // Filter out the root node
        return menus.stream().filter(m -> Objects.equals(m.getPid(), code)).map(m -> {<!-- -->
            //Set the child nodes of each tree
            str.add(m.getId());
            m.setChildren(getChildrens(m, menus, str));
            return m;
        }).collect(Collectors.toList());
    }


    /**
     * Get child nodes
     *
     * @param root
     * @param menus
     * @return
     */
    public List<EmBasicRegionVo> getChildrens(EmBasicRegionVo root, List<EmBasicRegionVo> menus, List<String> str) {<!-- -->
        //Filter direct child nodes
        return menus.stream().filter(m -> Objects.equals(m.getPid(), root.getId())).map(m -> {<!-- -->
            // Set child nodes recursively
            str.add(m.getId());
            m.setChildren(getChildrens(m, menus, str));
            return m;
        }).collect(Collectors.toList());
    }

    /**
     * Calculate percentage
     *
     * @param node
     */
    public static void calculatePercent(EmBasicRegionVo node, Double parentSum, Integer sort) {<!-- -->
        if (node == null) {<!-- -->
            return;
        }
        Double sum = node.getSum();
        if (parentSum != 0) {<!-- -->
            Double percent = Double.valueOf(decimalFormat.format(sum / parentSum * 100));
            node.setPercent(percent);
        } else {<!-- -->
            node.setPercent(0.0);
        }
        node.setSort(sort);
        for (int i = 0; i < node.getChildren().size(); i = i + 1) {<!-- -->
            EmBasicRegionVo child = node.getChildren().get(i);
            calculatePercent(child, sum, i + 1);
        }
    }
}