package com.fjhx.stock.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.entity.scheduling.Scheduling;
import com.fjhx.entity.scheduling.SchedulingActual;
import com.fjhx.entity.stock.StockDetail;
import com.fjhx.params.stock.PickingTrackingNumStatisticsResult;
import com.fjhx.scheduling.service.ISchedulingService;
import com.fjhx.scheduling.service.SchedulingActualService;
import com.fjhx.stock.mapper.StockDetailMapper;
import com.fjhx.stock.service.StockDetailService;
import com.fjhx.utils.Assert;
import com.fjhx.utils.BigDecimalUtil;
import org.springblade.core.tenant.annotation.TenantIgnore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 物料库存明细 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2022-08-01
 */
@Service
@TenantIgnore
public class StockDetailServiceImpl extends ServiceImpl<StockDetailMapper, StockDetail> implements StockDetailService {

    @Autowired
    private ISchedulingService schedulingService;

    @Autowired
    private SchedulingActualService schedulingActualService;

    @Override
    public Map<String, Object> getMaterialInfoByQrCode(Map<String, String> condition) {
        String qrCode = condition.get("qrCode");
        Assert.notEmpty(qrCode, "二维码不能为空");

        Map<String, Object> result = baseMapper.getMaterialInfoByQrCode(qrCode);

        Assert.notEmpty(result, "没有找到此物料");

        return result;
    }

    @Override
    public void setAreaId(List<StockDetail> stockDetail) {
        updateBatchById(stockDetail);
    }

    @Override
    public List<Map<String, Object>> restrictedPicking(Map<String, String> condition) {

        String jobNo = condition.get("jobNo");

        Date date = new Date();
        if (Integer.parseInt(DateUtil.format(date, "H")) > 7 ||
                (Integer.parseInt(DateUtil.format(date, "H")) == 7 && Integer.parseInt(DateUtil.format(date, "m")) >= 30)) {
            date = DateUtil.beginOfDay(date);
        } else {
            date = DateUtil.beginOfDay(DateUtil.yesterday());
        }

        List<Map<String, Object>> list = baseMapper.restrictedPicking(jobNo, date);

        if (list.size() == 0) {
            return list;
        }

        // 获取物料编码集合
        List<String> materialCodeList = list.stream().map(item -> item.get("materialCode").toString()).collect(Collectors.toList());

        // 查询实时库存长度和现场库存长度
        Map<String, Map<String, BigDecimal>> map = baseMapper.getMaterialQuantity(Wrappers.query().in("s.MaterialCode", materialCodeList));

        for (Map<String, Object> item : list) {
            String materialCode = item.get("materialCode").toString(); // 物料编码
            float materialWidth = (float) item.get("materialWidth"); // 门幅
            BigDecimal availableQuantity = (BigDecimal) item.get("availableQuantity"); // 排班数量

            // 门幅保留0位小数
            item.put("availableQuantity", BigDecimalUtil.keepDecimals(availableQuantity, 0));

            // 计算排班面积
            BigDecimal availableArea = BigDecimalUtil.init(availableQuantity).multiply(materialWidth).divide(100, 1).getValue();
            item.put("availableArea", availableArea);

            Map<String, BigDecimal> materialCodeQuantityMap = map.get(materialCode);
            if (materialCodeQuantityMap == null) {
                item.put("totalArea", BigDecimal.ZERO);
                item.put("userArea", BigDecimal.ZERO);
                continue;
            }

            // 计算实时库存面积
            BigDecimal bigDecimal = materialCodeQuantityMap.get("totalQuantity"); // 实时库存长度
            if (bigDecimal == null) {
                item.put("totalArea", BigDecimal.ZERO);
            } else {
                BigDecimal totalArea = BigDecimalUtil.init(bigDecimal).multiply(materialWidth).divide(100, 1).getValue();
                item.put("totalArea", totalArea);
            }

            // 计算现场库存面积
            BigDecimal userQuantity = materialCodeQuantityMap.get("userQuantity");
            if (userQuantity == null) {
                item.put("userArea", BigDecimal.ZERO);
            } else {
                BigDecimal userArea = BigDecimalUtil.init(userQuantity).multiply(materialWidth).divide(100, 1).getValue();
                item.put("userArea", userArea);
            }
        }

        // 查询推荐物料
        Map<String, List<Map<String, Object>>> listMapByMaterialCode = list.stream()
                .collect(Collectors.groupingBy(item -> item.get("materialCode").toString()));

        // 对每种物料循环
        listMapByMaterialCode.forEach((k, v) -> {

            List<Map<String, Object>> itemList = baseMapper.selectRecommendMaterial(k);

            int size = Math.min(v.size(), itemList.size());

            // 物料循环
            for (int i = 0; i < size; i++) {
                BigDecimal availableQuantity = (BigDecimal) v.get(i).get("availableQuantity");

                // 是否需要匹配2卷
                boolean flag = true;

                // 匹配单卷
                for (int j = 0; j < itemList.size(); j++) {
                    Map<String, Object> itemMap = itemList.get(j);

                    if (BigDecimalUtil.ge(itemMap.get("quantity"), availableQuantity)) {
                        v.get(i).putAll(itemMap);
                        itemList.remove(itemMap);
                        flag = false;
                        break;
                    }

                }

                // 匹配2卷
                if (flag) {
                    for (int j = 0; j < itemList.size() - 1; j++) {
                        Map<String, Object> itemMap1 = itemList.get(j);

                        boolean matchingFlag = false;
                        for (int l = itemList.size() - j + 1; l < itemList.size(); l++) {
                            Map<String, Object> itemMap2 = itemList.get(l);

                            if (BigDecimalUtil.ge(
                                    BigDecimalUtil.add(itemMap1.get("quantity"), itemMap2.get("quantity")),
                                    availableQuantity)) {

                                Object areaName1 = itemMap1.get("areaName");
                                Object areaName2 = itemMap2.get("areaName");

                                if (ObjectUtil.isAllNotEmpty(areaName1, areaName2)) {
                                    itemMap1.put("areaName", areaName1 + "," + areaName2);
                                } else if (ObjectUtil.isNotEmpty(areaName2)) {
                                    itemMap1.put("areaName", "暂未绑定位置," + areaName2);
                                } else if (ObjectUtil.isNotEmpty(areaName1)) {
                                    itemMap1.put("areaName", areaName1 + ",暂未绑定位置");
                                }

                                itemMap1.put("rfidCode", itemMap1.get("rfidCode") + "," + itemMap2.get("rfidCode"));
                                itemMap1.put("quantity", itemMap1.get("quantity") + "," + itemMap2.get("quantity"));

                                // 添加推荐物料
                                v.get(i).putAll(itemMap1);

                                // 删除匹配到的数据
                                itemList.remove(itemMap1);
                                itemList.remove(itemMap2);
                                matchingFlag = true;
                                break;
                            }
                        }
                        if (matchingFlag) {
                            break;
                        }
                    }
                    v.get(i).putIfAbsent("rfidCode", "");
                    v.get(i).putIfAbsent("quantity", "1");

                }
            }
        });

        return list;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void submitRestrictedPicking(Map<String, Object> condition) {

//      =====================================
//         基本解析入参数据工作
//      =====================================

        // 推荐领料信息
        List<Map<String, Object>> appointInfoList = (List<Map<String, Object>>) condition.get("appointInfo");

        // 实际领料信息
        List<Map<String, Object>> actualList = (List<Map<String, Object>>) condition.get("actual");

        // 任意为空则返回
        if (ObjectUtil.isEmpty(appointInfoList) || ObjectUtil.isEmpty(actualList)) return;

        // 推荐领料信息按物料编码分组
        Map<Object, List<Map<String, Object>>> appointInfoByCodeMap = appointInfoList.stream().distinct()
                .collect(Collectors.groupingBy(item -> item.get("materialCode")));

        // 实际领料信息按物料编码分组
        Map<String, List<Map<String, Object>>> actualByCodeMap = actualList.stream().distinct()
                .collect(Collectors.groupingBy(item -> item.get("materialCode").toString()));

        // 工号
        String jobNo = condition.get("jobNo").toString();

        // 本次指定领料出库标识
        long flag = IdWorker.getId();

        // 创建时间
        Date date = new Date();

        // 需要修改的推荐出库物料信息
        List<Scheduling> schedulingList = new ArrayList<>();

        // 需要添加的出库物料信息
        List<SchedulingActual> schedulingActualList = new ArrayList<>();

//      =====================================
//         循环推荐领料信息，与实际领料信息做比对
//      =====================================

        appointInfoByCodeMap.forEach((k, v) -> {

            /*
            实际领料信息封装为map格式
            key：rfid
            value：详细详细
             */
            List<Map<String, Object>> maps = actualByCodeMap.get(k);
            Map<String, Map<String, Object>> actualByRfidMap = maps.stream().collect(
                    Collectors.toMap(item -> item.get("materialRfid").toString(), item -> item));

            /*
            推荐领料信息rfid去匹配实际领料rfid
            如果匹配到，则为推荐领料出库
             */
            for (int i = 0; i < v.size(); i++) {
                Map<String, Object> itemV = v.get(i);

                // 推荐领料信息的rfidCode
                String rfidCode = itemV.get("rfidCode").toString();

                // 匹配实际出库rfidCode
                Map<String, Object> map = actualByRfidMap.get(rfidCode);

                // 如果不为空，则为推荐领料出库
                if (map == null) {
                    continue;
                }

                Scheduling scheduling = createScheduling(itemV, flag);
                schedulingList.add(scheduling);
                v.remove(itemV);
                i--;

                SchedulingActual schedulingActual = createSchedulingActual(1, map, flag, jobNo, date);
                schedulingActualList.add(schedulingActual);
                actualByRfidMap.remove(rfidCode);
            }

            // 添加推荐未领取
            List<Map<String, Object>> actual = new ArrayList<>(actualByRfidMap.values());

            int size = Math.min(actual.size(), v.size());

            for (int i = 0; i < size; i++) {
                Map<String, Object> map = v.get(i);

                Scheduling scheduling = createScheduling(map, flag);
                schedulingList.add(scheduling);

                map.put("materialRfid", map.get("rfidCode"));
                SchedulingActual schedulingActual = createSchedulingActual(2, map, flag, jobNo, date);
                schedulingActualList.add(schedulingActual);
            }

            for (Map<String, Object> item : actual) {
                SchedulingActual schedulingActual = createSchedulingActual(3, item, flag, jobNo, date);
                schedulingActualList.add(schedulingActual);
            }

        });

        schedulingService.updateBatchById(schedulingList);
        schedulingActualService.saveBatch(schedulingActualList);
    }

    /**
     * 生成推荐领料信息对象
     *
     * @return
     */
    private Scheduling createScheduling(Map<String, Object> map, Long flag) {
        Scheduling scheduling = new Scheduling();

        scheduling.setId(map.get("id").toString()); // id
        String type = map.get("type").toString(); // 类型

        if (type.equals("1")) {
            scheduling.setMaterialStatus(1);
            scheduling.setMaterialFlag(flag);
        } else {
            scheduling.setPaperStatus(1);
            scheduling.setPaperFlag(flag);
        }

        return scheduling;
    }

    private SchedulingActual createSchedulingActual(Integer type, Map<String, Object> map, Long flag, String jobNo, Date date) {
        SchedulingActual schedulingActual = new SchedulingActual();
        schedulingActual.setMaterialCode(map.get("materialCode").toString());
        schedulingActual.setMaterialRfid(map.get("materialRfid").toString());
        schedulingActual.setQuantity(BigDecimalUtil.objToBigDecimal(map.get("quantity")));
        schedulingActual.setType(type);
        schedulingActual.setJobNo(jobNo);
        schedulingActual.setFlag(flag);
        schedulingActual.setCreateTime(date);
        return schedulingActual;
    }


    @Override
    public List<HashMap<String, Object>> pickingTrackingUserStatistics(Map<String, String> condition) {

        // 获取查询条件
        QueryWrapper<Object> wrapper = pickingTrackingStatisticsGetWrapper(condition);

        List<Map<String, Object>> list = baseMapper.getPlanList(wrapper);

        if (list.size() == 0) {
            HashMap<String, Object> countUserMap = new HashMap<>();
            countUserMap.put("sum", 0);
            countUserMap.put("userName", "全部");
            return Collections.singletonList(countUserMap);
        }

        // 用户统计
        return pickingTrackingUserStatistics(list);
    }

    @Override
    public PickingTrackingNumStatisticsResult pickingTrackingNumStatistics(Map<String, String> condition) {

        PickingTrackingNumStatisticsResult result = new PickingTrackingNumStatisticsResult();

        // 查询指定领料列表
        QueryWrapper<Object> wrapper = pickingTrackingStatisticsGetWrapper(condition);
        List<Map<String, Object>> list = baseMapper.getPlanListByUser(wrapper, condition.get("jobNo"));

        if (list.size() == 0) {
            return result;
        }

        // 物料编码去重集合
        List<String> code = list.stream().map(item -> item.get("code").toString()).distinct().collect(Collectors.toList());

        // 获取物料门幅键值对
        Map<String, Float> codeWidthMap = getCodeWidthMap(code);

        // 赋值排班领料
        setPlan(result, list, codeWidthMap);

        // 获取指定和推荐领料map
        List<Long> flagList = list.stream()
                .filter(item -> ObjectUtil.isNotEmpty(item.get("flag")))
                .map(item -> (Long) item.get("flag"))
                .distinct()
                .collect(Collectors.toList());

        List<SchedulingActual> schedulingActualListByFlag;

        if (flagList.size() == 0) {
            schedulingActualListByFlag = new ArrayList<>();
        } else {
            schedulingActualListByFlag = getSchedulingActualListByFlag(flagList, null);
        }

        // 赋值实际领料
        setActual(result, schedulingActualListByFlag, codeWidthMap);

        // 计算指定领料
        setAppoint(result, schedulingActualListByFlag, codeWidthMap);

        // 赋值比例
        setRatio(result);

        return result;
    }

    private List<SchedulingActual> getSchedulingActualListByFlag(List<Long> flagList, String materialCode) {
        return schedulingActualService.list(Wrappers.<SchedulingActual>lambdaQuery()
                .eq(ObjectUtil.isNotEmpty(materialCode), SchedulingActual::getMaterialCode, materialCode)
                .in(SchedulingActual::getFlag, flagList)
        );
    }

    @Override
    public List<Map<String, Object>> appointInfo(Map<String, String> condition) {
        String pickingType = condition.get("pickingType");
        Assert.notEmpty(pickingType, "领料类型不能为空");

        String jobNo = condition.get("jobNo");
        String materialCode = condition.get("materialCode");

        QueryWrapper<Object> wrapper = pickingTrackingStatisticsGetWrapper(condition);

        wrapper.eq(ObjectUtil.isNotEmpty(materialCode), "m.Code", materialCode);
        wrapper.eq(ObjectUtil.isNotEmpty(jobNo), "u.JobNo", jobNo);
        wrapper.eq("sca.type", pickingType);
        wrapper.groupBy("sca.id");

        return baseMapper.selectClaimed(wrapper);
    }

    @Override
    public List<Map<String, Object>> pickingTrackingList(Map<String, String> condition) {

        // 查询指定领料列表
        QueryWrapper<Object> wrapper = pickingTrackingStatisticsGetWrapper(condition);
        List<Map<String, Object>> list = baseMapper.getPlanListByUser(wrapper, condition.get("jobNo"));

        if (list.size() == 0) {
            return list;
        }

        // 指定物料列表通过物料编码分组
        Map<String, List<Map<String, Object>>> listGroupCode = list.stream()
                .collect(Collectors.groupingBy(item -> item.get("code").toString()));

        // 物料编码、门幅、名称集合
        List<Map<String, Object>> materialList = baseMapper.getMaterialWidthByCode(
                Wrappers.query().in("m.Code", listGroupCode.keySet()));

        // 物料名称分组前缀分组
        Map<String, List<Map<String, Object>>> materialGroupName = materialList.stream().collect(Collectors.groupingBy(
                item -> item.get("name").toString().split("-")[0]));

        // 物料
        Map<String, Float> codeWidthMap = materialList.stream().collect(Collectors.toMap(
                item -> item.get("code").toString(),
                item -> (Float) item.get("width")
        ));

        List<Map<String, Object>> result = new ArrayList<>();

        for (Map.Entry<String, List<Map<String, Object>>> entry : materialGroupName.entrySet()) {
            String key = entry.getKey(); // 面料名称
            List<Map<String, Object>> value = entry.getValue(); // 面料属性集合

            for (Map<String, Object> map : value) {
                String code = map.get("code").toString();
                List<Map<String, Object>> listByCode = listGroupCode.get(code);

                map.put("name", map.get("name").toString().replace(key + "-", ""));

                PickingTrackingNumStatisticsResult temp = new PickingTrackingNumStatisticsResult();

                // 赋值排班领料
                setPlan(temp, listByCode, codeWidthMap);

                // 获取指定和推荐领料map
                List<Long> flagList = listByCode.stream().map(item -> (Long) item.get("flag")).distinct().collect(Collectors.toList());
                List<SchedulingActual> schedulingActualListByFlag = getSchedulingActualListByFlag(flagList, code);

                // 赋值实际领料
                setActual(temp, schedulingActualListByFlag, codeWidthMap);

                // 计算指定领料
                setAppoint(temp, schedulingActualListByFlag, codeWidthMap);

                // 赋值比例
                setRatio(temp);

                map.putAll(BeanUtil.beanToMap(temp));
            }


            HashMap<String, Object> map = new HashMap<>();
            map.put(key, value);
            result.add(map);

        }

        return result;
    }

    /**
     * 赋值比例相关属性
     */
    private void setRatio(PickingTrackingNumStatisticsResult result) {
        // 指定产出比
        result.setAppointOutputRatio(BigDecimalUtil.init(result.getPlanArea()).divideTry(result.getAppointArea(), 4).getValue());

        // 实际产出比
        result.setActualOutputRatio(BigDecimalUtil.init(result.getPlanArea()).divideTry(result.getActualArea(), 4).getValue());

        // 产出比差额
        result.setOutputRatioDifference(result.getAppointOutputRatio().subtract(result.getActualOutputRatio()));
    }

    /**
     * 赋值排班领料
     */
    private void setPlan(PickingTrackingNumStatisticsResult result, List<Map<String, Object>> list, Map<String, Float> codeWidthMap) {
        // 排班米数
        BigDecimal planMeter = list.stream().map(item -> (BigDecimal) item.get("quantity"))
                .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_UP);
        result.setPlanMeter(planMeter);

        // 排班面积
        BigDecimal planArea = list.stream()
                .map(item -> BigDecimalUtil.init(item.get("quantity"))
                        .multiply(ObjectUtil.defaultIfNull(codeWidthMap.get(item.get("code").toString()), BigDecimal.ZERO))
                        .divide(100, 2)
                        .getValue())
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        result.setPlanArea(planArea);
    }

    /**
     * 赋值实际领料
     */
    private void setActual(PickingTrackingNumStatisticsResult result, List<SchedulingActual> schedulingActualListByFlag, Map<String, Float> codeWidthMap) {

        List<SchedulingActual> schedulingActualList = schedulingActualListByFlag.stream()
                .filter(item -> item.getType().equals(1) || item.getType().equals(3))
                .collect(Collectors.toList());

        // 实际领料卷数
        result.setActualNum(schedulingActualList.size());

        // 实际领料米数
        BigDecimal actualMeter = schedulingActualList.stream().map(SchedulingActual::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
        result.setActualMeter(actualMeter);

        // 实际领料面积
        BigDecimal actualArea = schedulingActualList.stream().map(
                item -> BigDecimalUtil.init(item.getQuantity())
                        .multiply(codeWidthMap.get(item.getMaterialCode()))
                        .divide(100, 2)
                        .getValue()
        ).reduce(BigDecimal.ZERO, BigDecimal::add);
        result.setActualArea(actualArea);
    }

    /**
     * 赋值指定领料
     */
    private void setAppoint(PickingTrackingNumStatisticsResult result, List<SchedulingActual> schedulingActualListByFlag, Map<String, Float> codeWidthMap) {

        List<SchedulingActual> schedulingActualList = schedulingActualListByFlag.stream()
                .filter(item -> item.getType().equals(1) || item.getType().equals(2))
                .collect(Collectors.toList());

        // 指定领料卷数
        int schedulingActualListSize = schedulingActualList.size();
        result.setAppointNum(schedulingActualListSize);

        // 指定未领料
        int appointNum = Integer.parseInt(schedulingActualList.stream().filter(item -> item.getType().equals(2)).count() + "");
        result.setAppointNotClaimedNum(appointNum);

        // 额外领料卷数
        result.setAdditionalNum(schedulingActualListByFlag.size() - schedulingActualListSize);

        // 指定领料米数
        BigDecimal appointMeter = schedulingActualList.stream().map(SchedulingActual::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
        result.setAppointMeter(appointMeter);

        // 指定领料面积
        BigDecimal appointArea = schedulingActualList.stream().map(
                item -> BigDecimalUtil.init(item.getQuantity())
                        .multiply(codeWidthMap.get(item.getMaterialCode()))
                        .divide(100, 2)
                        .getValue()
        ).reduce(BigDecimal.ZERO, BigDecimal::add);
        result.setAppointArea(appointArea);
    }

    /**
     * 获取物料门幅键值对
     */
    private Map<String, Float> getCodeWidthMap(List<String> codeList) {
        // 查询物料门幅
        List<Map<String, Object>> list = baseMapper.getMaterialWidthByCode(Wrappers.query().in("m.Code", codeList));
        return list.stream().collect(Collectors.toMap(
                item -> item.get("code").toString(),
                item -> (Float) item.get("width")
        ));
    }


    /**
     * 领料追踪统计获取wrapper
     */
    private QueryWrapper<Object> pickingTrackingStatisticsGetWrapper(Map<String, String> condition) {
        String type = condition.get("type");
        Assert.notEmpty(type, "时间类型不能为空");
        Date beginTime;
        Date endTime;
        Date date = new Date();
        switch (type) {
            case "1":
                beginTime = DateUtil.beginOfDay(date);
                endTime = DateUtil.endOfDay(date);
                break;
            case "2":
                beginTime = DateUtil.beginOfWeek(date);
                endTime = DateUtil.endOfWeek(date);
                break;
            case "3":
                beginTime = DateUtil.beginOfMonth(date);
                endTime = DateUtil.endOfMonth(date);
                break;
            case "4":
                beginTime = DateUtil.beginOfYear(date);
                endTime = DateUtil.endOfYear(date);
                break;
            default:
                beginTime = DateUtil.parse(condition.get("beginTime"));
                endTime = DateUtil.parse(condition.get("endTime"));
        }

        return Wrappers.query().between("ss.plan_date", beginTime, endTime);
    }

    /**
     * 领料追踪统计：用户统计
     */
    private List<HashMap<String, Object>> pickingTrackingUserStatistics(List<Map<String, Object>> list) {
        // 统计用户
        List<String> jobNoList = list.stream().map(item -> item.get("jobNo").toString()).distinct().collect(Collectors.toList());

        Map<String, Map<String, String>> JobNoMap = baseMapper.selectUserByJobNoList(Wrappers.query().in("JobNo", jobNoList));
        List<HashMap<String, Object>> userList = new ArrayList<>(
                list.stream().collect(Collectors.toMap(
                        item -> item.get("jobNo").toString(),
                        item -> {
                            HashMap<String, Object> map = new HashMap<>();
                            String jobNo = item.get("jobNo").toString();
                            map.put("jobNo", jobNo);

                            map.put("userName", JobNoMap.get(jobNo) == null ?
                                    "未知（工号：" + jobNo + ")" :
                                    JobNoMap.get(jobNo).get("userName"));
                            map.put("sum", 1);
                            return map;
                        },
                        (v1, v2) -> {
                            v1.put("sum", (Integer) v1.get("sum") + 1);
                            return v1;
                        }
                )).values());

        int sum = userList.stream().mapToInt(item -> (Integer) item.get("sum")).sum();
        HashMap<String, Object> countUserMap = new HashMap<>();
        countUserMap.put("sum", sum);
        countUserMap.put("userName", "全部");
        userList.add(0, countUserMap);

        return userList;
    }

}
