package com.retdata.yaoyibi.service.impl;

import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.retdata.yaoyibi.domain.*;
import com.retdata.yaoyibi.mapper.CsoSysDeptMapper;
import com.retdata.yaoyibi.mapper.WmScorePackageMapper;
import com.retdata.yaoyibi.mapper.WmScorePackageSettleNoteMapper;
import com.retdata.yaoyibi.service.CsoSysDictItemService;
import com.retdata.yaoyibi.service.WmDaDrugEntDrugtableService;
import com.retdata.yaoyibi.service.WmScorePackageService;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.common.enums.DataSourceType;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.WeekFields;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional(readOnly = true)
@DataSource(DataSourceType.CSO)
public class WmScorePackageServiceImpl extends ServiceImpl<WmScorePackageMapper, WmScorePackage>
        implements WmScorePackageService {

    private final CsoSysDeptMapper csoSysDeptMapper;

    private final WmDaDrugEntDrugtableService wmDaDrugEntDrugtableService;

    private final WmScorePackageSettleNoteMapper wmScorePackageSettleNoteMapper;

    private final CsoSysDictItemService csoSysDictItemService;

    private static final Map<Integer, String> SYS_DEPT_LEVEL_DICT = new HashMap<>();

    private static final Map<Integer, String> SETTLE_STATUS_DICT = new HashMap<>();

    private static final Map<String, String> SUB_TYPE_DICT = new HashMap<>();

    private static final Map<String, String> PACKAGE_FINISH_STATUS_DICT = new HashMap<>();

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static final SimpleDateFormat DAY_FORMAT = new SimpleDateFormat("yyyy-MM-dd");

    private static final SimpleDateFormat MONTH_FORMAT = new SimpleDateFormat("yyyy-MM");

    private static final SimpleDateFormat YEAR_FORMAT = new SimpleDateFormat("yyyy");

    static {
        SYS_DEPT_LEVEL_DICT.put(1, "要易业务管理平台");
        SYS_DEPT_LEVEL_DICT.put(2, "药企");
        SYS_DEPT_LEVEL_DICT.put(3, "一级CSO");
        SYS_DEPT_LEVEL_DICT.put(4, "二级CSO");
        SYS_DEPT_LEVEL_DICT.put(5, "其他");
        SETTLE_STATUS_DICT.put(1, "结算成功");
        SETTLE_STATUS_DICT.put(2, "结算失败");
        SETTLE_STATUS_DICT.put(3, "已提交");
        SETTLE_STATUS_DICT.put(4, "未结算");
        SETTLE_STATUS_DICT.put(9, "待结算");
        SETTLE_STATUS_DICT.put(5, "部分成功");
        SETTLE_STATUS_DICT.put(8, "结算成功（线下）");
        SETTLE_STATUS_DICT.put(10, "复核不通过");
        SETTLE_STATUS_DICT.put(11, "待复核");
        SETTLE_STATUS_DICT.put(12, "结算异常");
        SUB_TYPE_DICT.put("0", "人力家");
        SUB_TYPE_DICT.put("1", "税邦云");
        SUB_TYPE_DICT.put("2", "阿拉丁");
        SUB_TYPE_DICT.put("5", "浪潮");
        SUB_TYPE_DICT.put("6", "汇企云");
        SUB_TYPE_DICT.put("7", "众蚁云");
        SUB_TYPE_DICT.put("9", "企赋");
        SUB_TYPE_DICT.put("4", "安之慧");
        SUB_TYPE_DICT.put("10", "深圳之光");
        SUB_TYPE_DICT.put("8", "中智");
        SUB_TYPE_DICT.put("11", "天津易捷");
        SUB_TYPE_DICT.put("12", "薪事易");
        SUB_TYPE_DICT.put("13", "合创众盈");
        SUB_TYPE_DICT.put("14", "海南飞亿");
        SUB_TYPE_DICT.put("15", "深圳产业园");
        SUB_TYPE_DICT.put("16", "全思福");
        SUB_TYPE_DICT.put("100", "证据链采集模块");
        SUB_TYPE_DICT.put("18", "正启之星");
        SUB_TYPE_DICT.put("19", "全思福-灵就宫");
        SUB_TYPE_DICT.put("20", "上海鹊诊-江西");
        SUB_TYPE_DICT.put("21", "原力-东进人才");
        PACKAGE_FINISH_STATUS_DICT.put("0", "未完成");
        PACKAGE_FINISH_STATUS_DICT.put("1", "已完成");
        PACKAGE_FINISH_STATUS_DICT.put("10", "未发布");

    }

    public WmScorePackageServiceImpl(CsoSysDeptMapper csoSysDeptMapper,
                                     WmDaDrugEntDrugtableService wmDaDrugEntDrugtableService,
                                     WmScorePackageSettleNoteMapper wmScorePackageSettleNoteMapper,
                                     CsoSysDictItemService csoSysDictItemService) {
        this.csoSysDeptMapper = csoSysDeptMapper;
        this.wmDaDrugEntDrugtableService = wmDaDrugEntDrugtableService;
        this.wmScorePackageSettleNoteMapper = wmScorePackageSettleNoteMapper;
        this.csoSysDictItemService = csoSysDictItemService;
    }

    @Override
    public Page<WmScorePackageSearchDto> search(WmScorePackageSearchParams searchParams, Pageable pageable) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<WmScorePackageSearchDto> page =
                new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageable.getPageNumber() + 1,
                        pageable.getPageSize());
        page.addOrder(OrderItem.desc("t1.create_time"));
        page = getBaseMapper().selectByParams(page, searchParams);
        if (page.getRecords().isEmpty()) {
            return new PageImpl<>(page.getRecords(), pageable, page.getTotal());
        }
        // 补充接包对象
        MultiValueMap<Integer, WmScorePackageSearchDto> deptIds = new LinkedMultiValueMap<>();
        Map<Integer, WmScorePackageSearchDto> userPackageIds = new HashMap<>();
        Map<Integer, WmScorePackageSearchDto> entPackageIds = new HashMap<>();
        for (WmScorePackageSearchDto dto : page.getRecords()) {
            if (WmScorePackage.USER_PACKAGE_TYPEID_SET.contains(dto.getTypeid())) {
                calculateSearchDtoTotalAmount(dto);
                if (WmScorePackage.APPROVED_SCORE_PACKAGE_STATUS_SET.contains(dto.getScorePackageStatus())) {
                    dto.setApprovedScore(dto.getScore());
                }
                userPackageIds.put(dto.getId(), dto);
            } else {
                entPackageIds.put(dto.getId(), dto);
                if (dto.getDept() != null) {
                    deptIds.add(dto.getDept().getDeptId(), dto);
                }
            }
        }
        if (!deptIds.isEmpty()) {
            List<CsoSysDeptSummary> deptList = csoSysDeptMapper.selectSummariesByIds(deptIds.keySet());
            for (CsoSysDeptSummary dept : deptList) {
                for (WmScorePackageSearchDto dto : deptIds.get(dept.getDeptId())) {
                    dto.setDept(dept);
                }
            }
        }
        if (!userPackageIds.isEmpty()) {
            List<WmScorePackageStatusSummary> wmScorePackageStatusList =
                    getBaseMapper().selectWmScorePackageStatusByPackageIds(userPackageIds.keySet());
            MultiValueMap<String, String> packageIdToUsers = new LinkedMultiValueMap<>();
            for (WmScorePackageStatusSummary wmScorePackageStatus : wmScorePackageStatusList) {
                packageIdToUsers.add(wmScorePackageStatus.getPackageId(), wmScorePackageStatus.getUser().getRealname());
            }
            packageIdToUsers.forEach((packageId, users) -> userPackageIds.get(Integer.valueOf(packageId))
                    .setUser(users.stream().distinct().collect(Collectors.joining(","))));
        }
        // 补充企业包的结算金额信息、核准积分值
        if (!entPackageIds.isEmpty()) {
            Map<Integer, WmScorePackageSearchDto> mergedEntPackages = mergeSearchDtoList(entPackageIds.values());
            Map<Integer, WmScorePackageSettleTreeNode> settleTreeNodeMap = new HashMap<>();
            List<WmScorePackageSettleTreeNode> rootNodes = new ArrayList<>(mergedEntPackages.size());
            Map<Integer, WmScorePackageSettleTreeNode> entPackageSettleTreeNodes = new HashMap<>();
            for (WmScorePackageSearchDto rootEntPackage : mergedEntPackages.values()) {
                WmScorePackageSettleTreeNode node = new WmScorePackageSettleTreeNode();
                node.setId(rootEntPackage.getId());
                node.setTypeid(rootEntPackage.getTypeid());
                node.setSettleAmount(rootEntPackage.getSettleAmount());
                node.setP2pAmount(rootEntPackage.getP2pAmount());
                node.setP2pServiceAmount(rootEntPackage.getP2pServiceAmount());
                node.setScore(rootEntPackage.getScore());
                node.setScorePackageStatus(rootEntPackage.getScorePackageStatus());
                rootNodes.add(node);
                settleTreeNodeMap.put(node.getId(), node);
                entPackageSettleTreeNodes.put(node.getId(), node);
            }
            Set<Integer> toFetchIds = new HashSet<>(mergedEntPackages.keySet());
            while (!toFetchIds.isEmpty()) {
                List<WmScorePackageSettleTreeNode> children = batchFetchSettleTreeNode(toFetchIds);
                toFetchIds.clear();
                for (WmScorePackageSettleTreeNode node : children) {
                    WmScorePackageSettleTreeNode parent = settleTreeNodeMap.get(Integer.valueOf(node.getRelationScoreId()));
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(node);
                    settleTreeNodeMap.put(node.getId(), node);
                    if (WmScorePackage.ENT_PACKAGE_TYPEID_SET.contains(node.getTypeid())) {
                        toFetchIds.add(node.getId());
                        entPackageSettleTreeNodes.put(node.getId(), node);
                    }
                }
            }
            if (!entPackageSettleTreeNodes.isEmpty()) {
                List<ScorePackageEntTaskScoreStat> approvedEntTaskScoreStats = batchFetchApprovedEntTaskScoreStat(entPackageSettleTreeNodes.keySet());
                for (ScorePackageEntTaskScoreStat approvedEntTaskScoreStat : approvedEntTaskScoreStats) {
                    WmScorePackageSettleTreeNode node = entPackageSettleTreeNodes.get(approvedEntTaskScoreStat.getPackageId());
                    node.setApprovedEntTaskScore(approvedEntTaskScoreStat.getTotalScore());
                }
            }
            for (WmScorePackageSettleTreeNode rootNode : rootNodes) {
                calculateSettleTreeNodeTotalAmount(rootNode);
            }
            for (WmScorePackageSearchDto dto : entPackageIds.values()) {
                WmScorePackageSettleTreeNode node = settleTreeNodeMap.get(dto.getId());
                dto.setApprovedScore(node.getApprovedScore());
                dto.setTotalAmount(node.getTotalAmount());
            }
        }
        // 补充子包数量
        if (searchParams.isFetchChildrenCount() && !entPackageIds.isEmpty()) {
            List<WmScorePackageChildrenCount> childrenCountList = getBaseMapper().selectWmScorePackageChildrenCount(entPackageIds.keySet());
            for (WmScorePackageChildrenCount childrenCount : childrenCountList) {
                entPackageIds.get(Integer.valueOf(childrenCount.getPackageId()))
                        .setChildrenCount(childrenCount.getChildrenCount());
            }
        }
        // 积分转换
        for (WmScorePackageSearchDto dto : page.getRecords()) {
            if (Objects.equals(dto.getRootPackageSendPackageDeptId(), WmScorePackage.SPECIAL_DEPT_ID)) {
                if (dto.getScore() != null) {
                    dto.setScore(dto.getScore() / 100);
                }
                if (dto.getApprovedScore() != null) {
                    dto.setApprovedScore(dto.getApprovedScore() / 100);
                }
            }
        }
        return new PageImpl<>(page.getRecords(), pageable, page.getTotal());
    }

    private Map<Integer, WmScorePackageSearchDto> mergeSearchDtoList(Collection<WmScorePackageSearchDto> searchDtoList) {
        Map<List<String>, WmScorePackageSearchDto> mergedMap = new HashMap<>();
        Map<Integer, WmScorePackageSearchDto> result = new HashMap<>();
        searchDtoList.stream().sorted(Comparator.comparing(WmScorePackageSearchDto::getLevel)).forEach(dto -> {
            List<String> ids = getIdPath(dto);
            for (int i = 0; i < ids.size(); i++) {
                if (mergedMap.containsKey(ids.subList(0, i + 1))) {
                    return;
                }
            }
            mergedMap.put(ids, dto);
            result.put(dto.getId(), dto);
        });
        return result;
    }

    private List<String> getIdPath(WmScorePackageSearchDto dto) {
        List<String> ids = new ArrayList<>(3);
        if (StringUtils.hasText(dto.getGrandParentId())) {
            ids.add(dto.getGrandParentId());
        }
        if (StringUtils.hasText(dto.getRelationScoreId())) {
            ids.add(dto.getRelationScoreId());
        }
        ids.add(dto.getId().toString());
        return ids;
    }

    @Override
    public List<CsoSysDeptSummary> listRootScorePackageSenders(String keyword) {
        return csoSysDeptMapper.selectRootScorePackageSenders(keyword);
    }

    @Override
    public XSSFWorkbook export(WmScorePackageSearchParams searchParams) {
        Map<Integer, WmScorePackageExportDto> exportDtoCache = new HashMap<>();
        List<WmScorePackageExportDto> exportDtoList = getBaseMapper().selectExportDto(searchParams);
        MultiValueMap<Integer, WmScorePackageExportDto> parentToChildren = new LinkedMultiValueMap<>();
        for (WmScorePackageExportDto dto : exportDtoList) {
            exportDtoCache.put(dto.getId(), dto);
            if (StringUtils.hasText(dto.getRelationScoreId())) {
                parentToChildren.add(Integer.valueOf(dto.getRelationScoreId()), dto);
            }
        }
        // 获取所有上级包
        Set<Integer> toFetchParentIds = new HashSet<>(parentToChildren.keySet());
        toFetchParentIds.removeAll(exportDtoCache.keySet());
        while (!toFetchParentIds.isEmpty()) {
            List<WmScorePackageExportDto> parentList = batchFetchExportDto(toFetchParentIds);
            toFetchParentIds.clear();
            parentList.forEach(parent -> {
                exportDtoCache.put(parent.getId(), parent);
                if (StringUtils.hasText(parent.getRelationScoreId())) {
                    Integer relationScoreId = Integer.valueOf(parent.getRelationScoreId());
                    if (!exportDtoCache.containsKey(relationScoreId)) {
                        toFetchParentIds.add(relationScoreId);
                    }
                }
            });
        }
        List<WmScorePackageExportRecord> exportRecords = new ArrayList<>(exportDtoList.size());
        // 只导出叶子节点
        Map<Integer, WmDaDrugEntDrugtableSummary> drugEntDrugtableMap = new HashMap<>();
        Set<Integer> rootEntPackageIds = new HashSet<>();
        Map<Integer, WmScorePackageExportDto> userPackageIds = new HashMap<>();
        Map<Integer, CsoSysDeptSummary> deptIdToSubjectType = new HashMap<>();
        for (WmScorePackageExportDto dto : exportDtoList) {
            if (parentToChildren.containsKey(dto.getId())) {
                continue;// 跳过非叶子节点
            }
            WmScorePackageExportRecord exportRecord = new WmScorePackageExportRecord();
            WmScorePackageExportDto rootPackage = dto;
            if (WmScorePackage.USER_PACKAGE_TYPEID_SET.contains(dto.getTypeid())) {
                calculateExportDtoTotalAmount(dto);
                exportRecord.setUserPackage(dto);
                wmDaDrugEntDrugtableService.parseDrugtable(dto.getDrugtable(), drugEntDrugtableMap);
                userPackageIds.put(dto.getId(), dto);
            } else if ("2".equals(dto.getTypeid())) {
                exportRecord.setSecondAgentPackage(dto);
                wmDaDrugEntDrugtableService.parseDrugtable(dto.getDrugtable(), drugEntDrugtableMap);
            } else if ("1".equals(dto.getTypeid())) {
                exportRecord.setFirstAgentPackage(dto);
                wmDaDrugEntDrugtableService.parseDrugtable(dto.getDrugtable(), drugEntDrugtableMap);
            }
            // 设置上级包
            while (StringUtils.hasText(dto.getRelationScoreId())) {
                dto = exportDtoCache.get(Integer.valueOf(dto.getRelationScoreId()));
                rootPackage = dto;
                if ("2".equals(dto.getTypeid())) {
                    if (exportRecord.getSecondAgentPackage() != null) {
                        throw new RuntimeException("出现多个二级代理");
                    }
                    exportRecord.setSecondAgentPackage(dto);
                    wmDaDrugEntDrugtableService.parseDrugtable(dto.getDrugtable(), drugEntDrugtableMap);
                } else if ("1".equals(dto.getTypeid())) {
                    if (exportRecord.getFirstAgentPackage() != null) {
                        throw new RuntimeException("出现多个一级代理");
                    }
                    exportRecord.setFirstAgentPackage(dto);
                    wmDaDrugEntDrugtableService.parseDrugtable(dto.getDrugtable(), drugEntDrugtableMap);
                }
            }
            // 设置源头包发包方
            exportRecord.setRootDept(rootPackage.getSendPackageDept());
            if (WmScorePackage.ENT_PACKAGE_TYPEID_SET.contains(rootPackage.getTypeid())) {
                rootEntPackageIds.add(rootPackage.getId());
            }
            if (exportRecord.getRootDept() != null) {
                deptIdToSubjectType.put(exportRecord.getRootDept().getDeptId(), null);
            }
            if (exportRecord.getFirstAgentPackage() != null && exportRecord.getFirstAgentPackage().getDept() != null) {
                deptIdToSubjectType.put(exportRecord.getFirstAgentPackage().getDept().getDeptId(), null);
            }
            if (exportRecord.getSecondAgentPackage() != null && exportRecord.getSecondAgentPackage().getDept() != null) {
                deptIdToSubjectType.put(exportRecord.getSecondAgentPackage().getDept().getDeptId(), null);
            }
            exportRecords.add(exportRecord);
        }
        // 补充个人包的用户信息
        if (!userPackageIds.isEmpty()) {
            List<WmScorePackageStatusSummary> wmScorePackageStatusList = batchFetchWmScorePackageStatus(userPackageIds.keySet());
            MultiValueMap<String, String> packageIdToUsers = new LinkedMultiValueMap<>();
            for (WmScorePackageStatusSummary wmScorePackageStatus : wmScorePackageStatusList) {
                packageIdToUsers.add(wmScorePackageStatus.getPackageId(), wmScorePackageStatus.getUser().getRealname());
            }
            packageIdToUsers.forEach((packageId, users) -> userPackageIds.get(Integer.valueOf(packageId))
                    .setUser(users.stream().distinct().collect(Collectors.joining(","))));
        }
        // 获取药品信息
        if (!drugEntDrugtableMap.isEmpty()) {
            List<WmDaDrugEntDrugtableSummary> drugtableSummaries = wmDaDrugEntDrugtableService.batchFetchDrugs(drugEntDrugtableMap.keySet());
            for (WmDaDrugEntDrugtableSummary drugtableSummary : drugtableSummaries) {
                drugEntDrugtableMap.put(drugtableSummary.getId(), drugtableSummary);
            }
        }
        // 补充一级代理和二级代理积分包(企业级包)的结算金额信息、核准积分值
        Map<Integer, WmScorePackageSettleTreeNode> settleTreeNodeMap = new HashMap<>();
        if (!rootEntPackageIds.isEmpty()) {
            List<WmScorePackageSettleTreeNode> rootNodes = new ArrayList<>(rootEntPackageIds.size());
            Map<Integer, WmScorePackageSettleTreeNode> entPackageSettleTreeNodes = new HashMap<>();
            for (Integer rootEntPackageId : rootEntPackageIds) {
                WmScorePackageExportDto exportDto = exportDtoCache.get(rootEntPackageId);
                WmScorePackageSettleTreeNode node = new WmScorePackageSettleTreeNode();
                node.setId(exportDto.getId());
                node.setTypeid(exportDto.getTypeid());
                node.setSettleAmount(exportDto.getSettleAmount());
                node.setP2pAmount(exportDto.getP2pAmount());
                node.setP2pServiceAmount(exportDto.getP2pServiceAmount());
                node.setScorePackageStatus(exportDto.getScorePackageStatus());
                node.setScore(exportDto.getScore());
                rootNodes.add(node);
                settleTreeNodeMap.put(node.getId(), node);
                if (WmScorePackage.ENT_PACKAGE_TYPEID_SET.contains(node.getTypeid())) {
                    entPackageSettleTreeNodes.put(node.getId(), node);
                }
            }
            Set<Integer> toFetchIds = new HashSet<>(rootEntPackageIds);
            while (!toFetchIds.isEmpty()) {
                List<WmScorePackageSettleTreeNode> children = batchFetchSettleTreeNode(toFetchIds);
                toFetchIds.clear();
                for (WmScorePackageSettleTreeNode node : children) {
                    WmScorePackageSettleTreeNode parent = settleTreeNodeMap.get(Integer.valueOf(node.getRelationScoreId()));
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(node);
                    settleTreeNodeMap.put(node.getId(), node);
                    if (WmScorePackage.ENT_PACKAGE_TYPEID_SET.contains(node.getTypeid())) {
                        toFetchIds.add(node.getId());
                        entPackageSettleTreeNodes.put(node.getId(), node);
                    }
                }
            }
            if (!entPackageSettleTreeNodes.isEmpty()) {
                List<ScorePackageEntTaskScoreStat> approvedEntTaskScoreStats = batchFetchApprovedEntTaskScoreStat(entPackageSettleTreeNodes.keySet());
                for (ScorePackageEntTaskScoreStat approvedEntTaskScoreStat : approvedEntTaskScoreStats) {
                    WmScorePackageSettleTreeNode node = entPackageSettleTreeNodes.get(approvedEntTaskScoreStat.getPackageId());
                    node.setApprovedEntTaskScore(approvedEntTaskScoreStat.getTotalScore());
                }
            }
            for (WmScorePackageSettleTreeNode rootNode : rootNodes) {
                calculateSettleTreeNodeTotalAmount(rootNode);
            }
        }
        // 补充个人包的结算状态、结算平台、结算税源地、提交至财务时间、提交至灵工时间
        if (!userPackageIds.isEmpty()) {
            Set<Integer> toFetchPackageIds = new HashSet<>(userPackageIds.keySet());
            String[] timeFields = {"notify_time", "sub_to_gig_time", "sub_time"};
            for (String timeField : timeFields) {
                List<WmScorePackageSettleNote> list = batchFetchLatestSettleNode(toFetchPackageIds, timeField);
                for (WmScorePackageSettleNote settleNote : list) {
                    Integer packageId = Integer.valueOf(settleNote.getPackageId());
                    WmScorePackageExportDto dto = userPackageIds.get(packageId);
                    dto.setLatestSettleNote(settleNote);
                    toFetchPackageIds.remove(packageId);
                }
                if (toFetchPackageIds.isEmpty()) {
                    break;
                }
            }
        }
        // 补充签约灵工状态
        if (!deptIdToSubjectType.isEmpty()) {
            List<CsoSysDeptSummary> list = batchFetchDeptSubjectType(deptIdToSubjectType.keySet());
            for (CsoSysDeptSummary dept : list) {
                deptIdToSubjectType.put(dept.getDeptId(), dept);
            }
        }
        // 加载企业签约产品
        Map<Integer, String> deptSignProducts = getDeptSignProducts();
        // 创建xlsx
        XSSFWorkbook workbook = new XSSFWorkbook();
        XSSFSheet sheet = workbook.createSheet("积分包数据");
        int rowNo = 0;
        int columnNo = 0;
        // 创建表头
        XSSFRow row = sheet.createRow(rowNo++);
        String[] headers = {"签约企业ID", "签约企业名称", "企业类型", "签约产品", "签约灵工状态", "一级接包方名称", "一级包付款方", "一级积分包名称",
                "一级积分包值", "一级发包时间", "积分包状态", "推广药品", "推广药品企业", "核准积分值", "完成时间", "结算金额", "二级接包方名称", "二级包付款方", "二级积分包名称",
                "二级积分包值", "二级发包时间", "积分包状态", "推广药品", "推广药品企业", "核准积分值", "完成时间", "结算金额", "三级接包方", "三级积分包名称",
                "三级积分包值", "积分包状态", "三级发包时间", "推广药品", "推广药品企业", "核准积分值", "完成时间", "完成年份", "完成月份", "完成周数", "结算状态",
                "结算平台", "结算税源地", "提交至财务时间", "提交至灵工时间", "结算成功时间", "结算成功年份", "结算成功月份", "结算成功周数", "结算金额", "实际支付金额",
                "结算记录条数"};
        for (String header : headers) {
            row.createCell(columnNo++).setCellValue(header);
        }
        // 写入数据
        if (!exportRecords.isEmpty()) {
            Map<String, String> scorePackageStatusDict = csoSysDictItemService.getDictMap("score_package_status");
            Map<String, String> settleStatusDict = csoSysDictItemService.getDictMap("settle_status");
            Map<String, String> subTypeDict = csoSysDictItemService.getDictMap("sub_type");
            Map<String, String> subjectLocationDict = csoSysDictItemService.getDictMap("subject_location");
            for (WmScorePackageExportRecord exportRecord : exportRecords) {
                row = sheet.createRow(rowNo++);
                columnNo = 0;
                if (exportRecord.getRootDept() != null) {
                    row.createCell(columnNo++).setCellValue(exportRecord.getRootDept().getDeptId().toString());
                    row.createCell(columnNo++).setCellValue(exportRecord.getRootDept().getName());
                    row.createCell(columnNo++).setCellValue(SYS_DEPT_LEVEL_DICT.get(exportRecord.getRootDept().getLevel()));
                    row.createCell(columnNo++).setCellValue(deptSignProducts.get(exportRecord.getRootDept().getDeptId()));
                    CsoSysDeptSummary subjectType = deptIdToSubjectType.get(exportRecord.getRootDept().getDeptId());
                    row.createCell(columnNo++).setCellValue(subjectType == null ? "" : subjectType.getSubjectType());
                } else {
                    columnNo += 5;
                }
                if (exportRecord.getFirstAgentPackage() != null) {
                    if (exportRecord.getFirstAgentPackage().getDept() != null) {
                        row.createCell(columnNo++).setCellValue(exportRecord.getFirstAgentPackage().getDept().getName());
                        CsoSysDeptSummary subjectType = deptIdToSubjectType.get(exportRecord.getFirstAgentPackage().getDept().getDeptId());
                        if (subjectType != null && "AT".equals(subjectType.getAgentType())) {
                            // 如果是代理商团队，付款公司均是上级发包方
                            row.createCell(columnNo++).setCellValue(exportRecord.getFirstAgentPackage().getSendPackageDept().getName());
                        } else if (subjectType != null && StringUtils.hasText(subjectType.getSubjectType())) {
                            // 如果是代理商公司，根据他是否配置了结算渠道，配置了且不包含“证据链采集模块”则付款公司为自己，没配置则是上级发包方
                            if (subjectType.getSubjectType().contains("证据链采集模块")) {
                                row.createCell(columnNo++).setCellValue(exportRecord.getFirstAgentPackage().getSendPackageDept() != null
                                        ? exportRecord.getFirstAgentPackage().getSendPackageDept().getName() : "");
                            } else {
                                row.createCell(columnNo++).setCellValue(exportRecord.getFirstAgentPackage().getDept().getName());
                            }
                        } else if (exportRecord.getFirstAgentPackage().getSendPackageDept() != null) {
                            row.createCell(columnNo++).setCellValue(exportRecord.getFirstAgentPackage().getSendPackageDept().getName());
                        } else {
                            row.createCell(columnNo++).setCellValue("");
                        }
                    } else {
                        row.createCell(columnNo++).setCellValue("");
                        if (exportRecord.getFirstAgentPackage().getSendPackageDept() != null) {
                            row.createCell(columnNo++).setCellValue(exportRecord.getFirstAgentPackage().getSendPackageDept().getName());
                        } else {
                            row.createCell(columnNo++).setCellValue("");
                        }
                    }
                    row.createCell(columnNo++).setCellValue(exportRecord.getFirstAgentPackage().getScorePackageName());
                    row.createCell(columnNo++).setCellValue(formatScore(exportRecord, exportRecord.getFirstAgentPackage().getScore()));
                    row.createCell(columnNo++).setCellValue(DATE_FORMAT.format(exportRecord.getFirstAgentPackage().getCreateTime()));
                    row.createCell(columnNo++).setCellValue(PACKAGE_FINISH_STATUS_DICT.getOrDefault(exportRecord.getFirstAgentPackage().getPackageFinishStatus(), exportRecord.getFirstAgentPackage().getPackageFinishStatus()));
                    String[] drugInfo = wmDaDrugEntDrugtableService.getDrugtableInfo(exportRecord.getFirstAgentPackage().getDrugtable(), drugEntDrugtableMap);
                    row.createCell(columnNo++).setCellValue(drugInfo[0]);
                    row.createCell(columnNo++).setCellValue(drugInfo[1]);
                    WmScorePackageSettleTreeNode settleTreeNode = settleTreeNodeMap.get(exportRecord.getFirstAgentPackage().getId());
                    if (settleTreeNode != null) {
                        row.createCell(columnNo++).setCellValue(formatScore(exportRecord, settleTreeNode.getApprovedScore()));
                    } else {
                        row.createCell(columnNo++).setCellValue("");
                    }
                    if(exportRecord.getFirstAgentPackage().getPackageFinishTime()!=null){
                        row.createCell(columnNo++).setCellValue(DATE_FORMAT.format(exportRecord.getFirstAgentPackage().getPackageFinishTime()));
                    }else{
                        row.createCell(columnNo++).setCellValue("");
                    }
                    if (settleTreeNode != null && settleTreeNode.getTotalAmount() != null) {
                        row.createCell(columnNo++).setCellValue(settleTreeNode.getTotalAmount().toString());
                    } else {
                        row.createCell(columnNo++).setCellValue("");
                    }
                } else {
                    columnNo += 11;
                }
                if (exportRecord.getSecondAgentPackage() != null) {
                    if (exportRecord.getSecondAgentPackage().getDept() != null) {
                        row.createCell(columnNo++).setCellValue(exportRecord.getSecondAgentPackage().getDept().getName());
                        CsoSysDeptSummary subjectType = deptIdToSubjectType.get(exportRecord.getSecondAgentPackage().getDept().getDeptId());
                        if (subjectType != null && "AT".equals(subjectType.getAgentType())) {
                            // 如果是代理商团队，付款公司均是上级发包方
                            row.createCell(columnNo++).setCellValue(exportRecord.getSecondAgentPackage().getSendPackageDept().getName());
                        } else if (subjectType != null && StringUtils.hasText(subjectType.getSubjectType())) {
                            // 如果是代理商公司，根据他是否配置了结算渠道，配置了且不包含“证据链采集模块”则付款公司为自己，没配置则是上级发包方
                            if (subjectType.getSubjectType().contains("证据链采集模块")) {
                                row.createCell(columnNo++).setCellValue(exportRecord.getSecondAgentPackage().getSendPackageDept() != null
                                        ? exportRecord.getSecondAgentPackage().getSendPackageDept().getName() : "");
                            } else {
                                row.createCell(columnNo++).setCellValue(exportRecord.getSecondAgentPackage().getDept().getName());
                            }
                        } else if (exportRecord.getSecondAgentPackage().getSendPackageDept() != null) {
                            row.createCell(columnNo++).setCellValue(exportRecord.getSecondAgentPackage().getSendPackageDept().getName());
                        } else {
                            row.createCell(columnNo++).setCellValue("");
                        }
                    } else {
                        row.createCell(columnNo++).setCellValue("");
                        if (exportRecord.getSecondAgentPackage().getSendPackageDept() != null) {
                            row.createCell(columnNo++).setCellValue(exportRecord.getSecondAgentPackage().getSendPackageDept().getName());
                        } else {
                            row.createCell(columnNo++).setCellValue("");
                        }
                    }
                    row.createCell(columnNo++).setCellValue(exportRecord.getSecondAgentPackage().getScorePackageName());
                    row.createCell(columnNo++).setCellValue(formatScore(exportRecord, exportRecord.getSecondAgentPackage().getScore()));
                    row.createCell(columnNo++).setCellValue(DATE_FORMAT.format(exportRecord.getSecondAgentPackage().getCreateTime()));
                    row.createCell(columnNo++).setCellValue(PACKAGE_FINISH_STATUS_DICT.getOrDefault(exportRecord.getSecondAgentPackage().getPackageFinishStatus(), exportRecord.getSecondAgentPackage().getPackageFinishStatus()));
                    String[] drugInfo = wmDaDrugEntDrugtableService.getDrugtableInfo(exportRecord.getSecondAgentPackage().getDrugtable(), drugEntDrugtableMap);
                    row.createCell(columnNo++).setCellValue(drugInfo[0]);
                    row.createCell(columnNo++).setCellValue(drugInfo[1]);
                    WmScorePackageSettleTreeNode settleTreeNode = settleTreeNodeMap.get(exportRecord.getSecondAgentPackage().getId());
                    if (settleTreeNode != null) {
                        row.createCell(columnNo++).setCellValue(formatScore(exportRecord, settleTreeNode.getApprovedScore()));
                    } else {
                        row.createCell(columnNo++).setCellValue("");
                    }
                    if(exportRecord.getSecondAgentPackage().getPackageFinishTime()!=null){
                        row.createCell(columnNo++).setCellValue(DATE_FORMAT.format(exportRecord.getSecondAgentPackage().getPackageFinishTime()));
                    }else{
                        row.createCell(columnNo++).setCellValue("");
                    }
                    if (settleTreeNode != null && settleTreeNode.getTotalAmount() != null) {
                        row.createCell(columnNo++).setCellValue(settleTreeNode.getTotalAmount().toString());
                    } else {
                        row.createCell(columnNo++).setCellValue("");
                    }
                } else {
                    columnNo += 11;
                }
                if (exportRecord.getUserPackage() != null) {
                    row.createCell(columnNo++).setCellValue(exportRecord.getUserPackage().getUser());
                    row.createCell(columnNo++).setCellValue(exportRecord.getUserPackage().getScorePackageName());
                    row.createCell(columnNo++).setCellValue(formatScore(exportRecord, exportRecord.getUserPackage().getScore()));
                    row.createCell(columnNo++).setCellValue(scorePackageStatusDict.get(exportRecord.getUserPackage().getScorePackageStatus()));
                    row.createCell(columnNo++).setCellValue(DATE_FORMAT.format(exportRecord.getUserPackage().getCreateTime()));
                    String[] drugInfo = wmDaDrugEntDrugtableService.getDrugtableInfo(exportRecord.getUserPackage().getDrugtable(), drugEntDrugtableMap);
                    row.createCell(columnNo++).setCellValue(drugInfo[0]);
                    row.createCell(columnNo++).setCellValue(drugInfo[1]);
                    if (WmScorePackage.APPROVED_SCORE_PACKAGE_STATUS_SET.contains(exportRecord.getUserPackage().getScorePackageStatus())) {
                        row.createCell(columnNo++).setCellValue(formatScore(exportRecord, exportRecord.getUserPackage().getScore()));
                    } else {
                        row.createCell(columnNo++).setCellValue("");
                    }
                    if (exportRecord.getUserPackage().getPackageFinishTime() != null) {
                        row.createCell(columnNo++).setCellValue(DATE_FORMAT.format(exportRecord.getUserPackage().getPackageFinishTime()));
                        ZonedDateTime zdt = exportRecord.getUserPackage().getPackageFinishTime().toInstant().atZone(ZoneId.systemDefault());
                        row.createCell(columnNo++).setCellValue(zdt.getYear());
                        row.createCell(columnNo++).setCellValue(zdt.getMonthValue());
                        row.createCell(columnNo++).setCellValue(zdt.get(WeekFields.of(Locale.CHINA).weekOfMonth()));
                    } else {
                        columnNo += 4;
                    }
                    if (exportRecord.getUserPackage().getSettleStatus() != null) {
                        row.createCell(columnNo++).setCellValue(settleStatusDict.get(
                                exportRecord.getUserPackage().getSettleStatus().toString()));
                    } else {
                        row.createCell(columnNo++).setCellValue("");
                    }
                    if (exportRecord.getUserPackage().getLatestSettleNote() != null) {
                        row.createCell(columnNo++).setCellValue(subTypeDict.get(
                                exportRecord.getUserPackage().getLatestSettleNote().getSubType()));
                        row.createCell(columnNo++).setCellValue(subjectLocationDict.get(
                                exportRecord.getUserPackage().getLatestSettleNote().getSubjectLocation()));
                        if (exportRecord.getUserPackage().getLatestSettleNote().getSubTime() != null) {
                            row.createCell(columnNo++).setCellValue(DATE_FORMAT.format(
                                    exportRecord.getUserPackage().getLatestSettleNote().getSubTime()));
                        } else {
                            row.createCell(columnNo++).setCellValue("");
                        }
                        if (exportRecord.getUserPackage().getLatestSettleNote().getSubToGigTime() != null) {
                            row.createCell(columnNo++).setCellValue(DATE_FORMAT.format(
                                    exportRecord.getUserPackage().getLatestSettleNote().getSubToGigTime()));
                        } else {
                            row.createCell(columnNo++).setCellValue("");
                        }
                    } else {
                        columnNo += 4;
                    }
                    if (exportRecord.getUserPackage().getSettleNotifyTime() != null) {
                        row.createCell(columnNo++).setCellValue(DATE_FORMAT.format(
                                exportRecord.getUserPackage().getSettleNotifyTime()));
                        ZonedDateTime zdt = exportRecord.getUserPackage().getSettleNotifyTime().toInstant().atZone(ZoneId.systemDefault());
                        row.createCell(columnNo++).setCellValue(zdt.getYear());
                        row.createCell(columnNo++).setCellValue(zdt.getMonthValue());
                        row.createCell(columnNo++).setCellValue(zdt.get(WeekFields.of(Locale.CHINA).weekOfMonth()));
                    } else {
                        columnNo += 4;
                    }
                    if (exportRecord.getUserPackage().getTotalAmount() != null) {
                        row.createCell(columnNo++).setCellValue(exportRecord.getUserPackage().getTotalAmount().toString());
                    } else {
                        row.createCell(columnNo++).setCellValue("");
                    }
                    if (exportRecord.getUserPackage().getSettleActualAmount() != null) {
                        row.createCell(columnNo++).setCellValue(exportRecord.getUserPackage().getSettleActualAmount().toString());
                    } else {
                        row.createCell(columnNo++).setCellValue("");
                    }
                    if (exportRecord.getUserPackage().getSettleCount() != null) {
                        row.createCell(columnNo++).setCellValue(exportRecord.getUserPackage().getSettleCount().toString());
                    } else {
                        row.createCell(columnNo++).setCellValue("");
                    }
                }
            }
        }
        return workbook;
    }

    private String formatScore(WmScorePackageExportRecord exportRecord, Integer score) {
        if (score == null) {
            return "";
        }
        if (exportRecord.getRootDept() != null && Objects.equals(exportRecord.getRootDept().getDeptId(), WmScorePackage.SPECIAL_DEPT_ID)) {
            // 上海问云生物科技有限公司的积分值需要除以100
            return Integer.toString(score / 100);
        }
        return score.toString();
    }

    private void calculateSettleTreeNodeTotalAmount(WmScorePackageSettleTreeNode node) {
        BigDecimal totalAmount = BigDecimal.ZERO;
        if (node.getP2pAmount() != null) {
            totalAmount = totalAmount.add(node.getP2pAmount());
        }
        if (node.getP2pServiceAmount() != null) {
            totalAmount = totalAmount.add(node.getP2pServiceAmount());
        }
        if (node.getSettleAmount() != null) {
            totalAmount = totalAmount.add(node.getSettleAmount());
        }
        BigDecimal totalActualAmount = BigDecimal.ZERO;
        if (node.getSettleActualAmount() != null) {
            totalActualAmount = totalActualAmount.add(node.getSettleActualAmount());
        }
        int approvedScore = 0;
        if (WmScorePackage.USER_PACKAGE_TYPEID_SET.contains(node.getTypeid())
                && WmScorePackage.APPROVED_SCORE_PACKAGE_STATUS_SET.contains(node.getScorePackageStatus())
                && node.getScore() != null) {
            approvedScore += node.getScore();
        }
        if (node.getApprovedEntTaskScore() != null) {
            approvedScore += node.getApprovedEntTaskScore();
        }
        if (node.getChildren() != null) {
            for (WmScorePackageSettleTreeNode child : node.getChildren()) {
                calculateSettleTreeNodeTotalAmount(child);
                if (child.getTotalAmount() != null) {
                    totalAmount = totalAmount.add(child.getTotalAmount());
                }
                if (child.getTotalActualAmount() != null) {
                    totalActualAmount = totalActualAmount.add(child.getTotalActualAmount());
                }
                if (child.getApprovedScore() != null) {
                    approvedScore += child.getApprovedScore();
                }
            }
        }
        if (totalAmount.compareTo(BigDecimal.ZERO) > 0) {
            node.setTotalAmount(totalAmount);
        }
        if (totalActualAmount.compareTo(BigDecimal.ZERO) > 0) {
            node.setTotalActualAmount(totalActualAmount);
        }
        if (approvedScore != 0) {
            node.setApprovedScore(approvedScore);
        }
    }

    private List<WmScorePackageExportDto> batchFetchExportDto(Collection<Integer> packageIds) {
        List<Integer> batchIds = new ArrayList<>(packageIds);
        List<WmScorePackageExportDto> result = new ArrayList<>(packageIds.size());
        int batchSize = 1000;
        for (int i = 0; i < batchIds.size(); i += batchSize) {
            int end = Math.min(i + batchSize, batchIds.size());
            List<WmScorePackageExportDto> batchResult = getBaseMapper().selectExportDtoByPackageIds(batchIds.subList(i, end));
            result.addAll(batchResult);
        }
        return result;
    }

    private List<WmScorePackageStatusSummary> batchFetchWmScorePackageStatus(Collection<Integer> packageIds) {
        List<Integer> batchIds = new ArrayList<>(packageIds);
        List<WmScorePackageStatusSummary> result = new ArrayList<>(packageIds.size());
        int batchSize = 1000;
        for (int i = 0; i < batchIds.size(); i += batchSize) {
            int end = Math.min(i + batchSize, batchIds.size());
            List<WmScorePackageStatusSummary> batchResult = getBaseMapper()
                    .selectWmScorePackageStatusByPackageIds(batchIds.subList(i, end));
            result.addAll(batchResult);
        }
        return result;
    }

    private List<WmScorePackageSettleTreeNode> batchFetchSettleTreeNode(Collection<Integer> packageIds) {
        List<Integer> batchIds = new ArrayList<>(packageIds);
        List<WmScorePackageSettleTreeNode> result = new ArrayList<>(packageIds.size());
        int batchSize = 1000;
        for (int i = 0; i < batchIds.size(); i += batchSize) {
            int end = Math.min(i + batchSize, batchIds.size());
            List<WmScorePackageSettleTreeNode> batchResult = getBaseMapper().selectSubPackageSettleTreeNodesByPackageIds(batchIds.subList(i, end));
            result.addAll(batchResult);
        }
        return result;
    }

    private List<ScorePackageEntTaskScoreStat> batchFetchApprovedEntTaskScoreStat(Collection<Integer> packageIds) {
        List<Integer> batchIds = new ArrayList<>(packageIds);
        List<ScorePackageEntTaskScoreStat> result = new ArrayList<>(packageIds.size());
        int batchSize = 1000;
        for (int i = 0; i < batchIds.size(); i += batchSize) {
            int end = Math.min(i + batchSize, batchIds.size());
            List<ScorePackageEntTaskScoreStat> batchResult = getBaseMapper().selectApprovedEntTaskScoreStat(batchIds.subList(i, end));
            result.addAll(batchResult);
        }
        return result;
    }

    private void calculateSearchDtoTotalAmount(WmScorePackageSearchDto exportDto) {
        BigDecimal totalAmount = BigDecimal.ZERO;
        if (exportDto.getSettleAmount() != null) {
            totalAmount = totalAmount.add(exportDto.getSettleAmount());
        }
        if (exportDto.getP2pAmount() != null) {
            totalAmount = totalAmount.add(exportDto.getP2pAmount());
        }
        if (exportDto.getP2pServiceAmount() != null) {
            totalAmount = totalAmount.add(exportDto.getP2pServiceAmount());
        }
        if (totalAmount.compareTo(BigDecimal.ZERO) > 0) {
            exportDto.setTotalAmount(totalAmount);
        }
    }

    private void calculateExportDtoTotalAmount(WmScorePackageExportDto exportDto) {
        BigDecimal totalAmount = BigDecimal.ZERO;
        if (exportDto.getSettleAmount() != null) {
            totalAmount = totalAmount.add(exportDto.getSettleAmount());
        }
        if (exportDto.getP2pAmount() != null) {
            totalAmount = totalAmount.add(exportDto.getP2pAmount());
        }
        if (exportDto.getP2pServiceAmount() != null) {
            totalAmount = totalAmount.add(exportDto.getP2pServiceAmount());
        }
        if (totalAmount.compareTo(BigDecimal.ZERO) > 0) {
            exportDto.setTotalAmount(totalAmount);
        }
    }

    private List<WmScorePackageSettleNote> batchFetchLatestSettleNode(Collection<Integer> packageIds, String timeField) {
        List<Integer> batchIds = new ArrayList<>(packageIds);
        List<WmScorePackageSettleNote> result = new ArrayList<>(packageIds.size());
        int batchSize = 1000;
        for (int i = 0; i < batchIds.size(); i += batchSize) {
            int end = Math.min(i + batchSize, batchIds.size());
            List<WmScorePackageSettleNote> batchResult =
                    wmScorePackageSettleNoteMapper.selectLatestSettleNote(batchIds.subList(i, end), timeField);
            result.addAll(batchResult);
        }
        return result;
    }

    private List<CsoSysDeptSummary> batchFetchDeptSubjectType(Collection<Integer> ids) {
        List<Integer> batchIds = new ArrayList<>(ids);
        List<CsoSysDeptSummary> result = new ArrayList<>(ids.size());
        int batchSize = 1000;
        for (int i = 0; i < batchIds.size(); i += batchSize) {
            int end = Math.min(i + batchSize, batchIds.size());
            List<CsoSysDeptSummary> batchResult =
                    csoSysDeptMapper.selectSubjectTypeAndAgentType(batchIds.subList(i, end));
            result.addAll(batchResult);
        }
        return result;
    }

    @Override
    public ScoreStat getScoreStatByParams(WmScorePackageSearchParams searchParams) {
        return getBaseMapper().selectScoreStatByParams(searchParams);
    }

    @Override
    public List<ScoreTimeStat> getScoreTimeStatByParams(WmScorePackageSearchParams searchParams, String statUnit) {
        return getBaseMapper().selectScoreTimeStatByParams(searchParams, statUnit);
    }

    @Override
    public ScoreStat getApprovedScoreStatByParams(WmScorePackageSearchParams searchParams) {
        ScoreStat scoreStat = new ScoreStat();
        List<WmScorePackageSettleTreeNode> rootNodes = buildApprovedScoreTree(searchParams);
        if (rootNodes.isEmpty()) {
            return scoreStat;
        }
        Map<Integer, String> deptSignProducts = getDeptSignProducts();
        Set<Integer> entPackageIds = new HashSet<>();
        Set<Integer> userPackageIds = new HashSet<>();
        Set<Integer> cso1PackageIds = new HashSet<>();
        Set<Integer> cso2PackageIds = new HashSet<>();
        for (WmScorePackageSettleTreeNode rootNode : rootNodes) {
            calculateSettleTreeNodeTotalAmount(rootNode);
            if (rootNode.getApprovedScore() != null) {
                if (Objects.equals(rootNode.getSendPackageDeptId(), WmScorePackage.SPECIAL_DEPT_ID.toString())) {
                    rootNode.setApprovedScore(rootNode.getApprovedScore() / 100);
                }
                scoreStat.setTotalScore(scoreStat.getTotalScore() + rootNode.getApprovedScore());
                scoreStat.setTotalCount(scoreStat.getTotalCount() + 1);
                if (WmScorePackage.USER_PACKAGE_TYPEID_SET.contains(rootNode.getTypeid())) {
                    scoreStat.setUserScore(scoreStat.getUserScore() + rootNode.getApprovedScore());
                    scoreStat.setUserCount(scoreStat.getUserCount() + 1);
                    userPackageIds.add(rootNode.getId());
                } else if (WmScorePackage.ENT_PACKAGE_TYPEID_SET.contains(rootNode.getTypeid())) {
                    scoreStat.setEntScore(scoreStat.getEntScore() + rootNode.getApprovedScore());
                    scoreStat.setEntCount(scoreStat.getEntCount() + 1);
                    entPackageIds.add(rootNode.getId());
                    collectDescendentPackageIds(rootNode, entPackageIds);
                }
                String signProduct = deptSignProducts.get(rootNode.getDeptId());
                if (SysDeptSignProduct.CSO1.equals(signProduct)) {
                    scoreStat.setCso1Score(scoreStat.getCso1Score() + rootNode.getApprovedScore());
                    scoreStat.setCso1Count(scoreStat.getCso1Count() + 1);
                    cso1PackageIds.add(rootNode.getId());
                    collectDescendentPackageIds(rootNode, cso1PackageIds);
                } else if (SysDeptSignProduct.CSO2.equals(signProduct)) {
                    scoreStat.setCso2Score(scoreStat.getCso2Score() + rootNode.getApprovedScore());
                    scoreStat.setCso2Count(scoreStat.getCso2Count() + 1);
                    cso2PackageIds.add(rootNode.getId());
                    collectDescendentPackageIds(rootNode, cso2PackageIds);
                }
            }
        }
        // 统计用户数
        Set<Integer> allUserIds = new HashSet<>();
        if (!entPackageIds.isEmpty()) {
            Set<Integer> userIds = batchFetchUserIds(entPackageIds);
            scoreStat.setEntUserCount(userIds.size());
            allUserIds.addAll(userIds);
        }
        if (!userPackageIds.isEmpty()) {
            Set<Integer> userIds = batchFetchUserIds(userPackageIds);
            scoreStat.setUserUserCount(userIds.size());
            allUserIds.addAll(userIds);
        }
        if (!cso1PackageIds.isEmpty()) {
            Set<Integer> userIds = batchFetchUserIds(cso1PackageIds);
            scoreStat.setCso1UserCount(userIds.size());
        }
        if (!cso2PackageIds.isEmpty()) {
            Set<Integer> userIds = batchFetchUserIds(cso2PackageIds);
            scoreStat.setCso2UserCount(userIds.size());
        }
        scoreStat.setTotalUserCount(allUserIds.size());
        return scoreStat;
    }

    private Map<Integer, String> getDeptSignProducts() {
        return csoSysDeptMapper.selectAllSignProducts().stream()
                .collect(Collectors.toMap(SysDeptSignProduct::getDeptId, SysDeptSignProduct::getSignProduct));
    }

    private void collectDescendentPackageIds(WmScorePackageSettleTreeNode rootNode, Set<Integer> result) {
        if (rootNode.getChildren() != null) {
            rootNode.getChildren().forEach(childNode -> {
                result.add(childNode.getId());
                collectDescendentPackageIds(childNode, result);
            });
        }
    }

    private List<WmScorePackageSettleTreeNode> buildApprovedScoreTree(WmScorePackageSearchParams searchParams) {
        List<Integer> rootPackageIds = getBaseMapper().selectRootPackageIds(searchParams);
        if (rootPackageIds.isEmpty()) {
            return Collections.emptyList();
        }
        Map<Integer, WmScorePackageSettleTreeNode> settleTreeNodeMap = new HashMap<>();
        List<WmScorePackageSettleTreeNode> rootNodes = getBaseMapper().selectPackageSettleTreeNodesByPackageIdsForScore(rootPackageIds);
        Map<Integer, WmScorePackageSettleTreeNode> entPackageSettleTreeNodes = new HashMap<>();
        Set<Integer> toFetchIds = new HashSet<>();
        for (WmScorePackageSettleTreeNode node : rootNodes) {
            settleTreeNodeMap.put(node.getId(), node);
            if (WmScorePackage.ENT_PACKAGE_TYPEID_SET.contains(node.getTypeid())) {
                toFetchIds.add(node.getId());
                entPackageSettleTreeNodes.put(node.getId(), node);
            }
        }
        while (!toFetchIds.isEmpty()) {
            List<WmScorePackageSettleTreeNode> children = batchFetchSettleTreeNode(toFetchIds);
            toFetchIds.clear();
            for (WmScorePackageSettleTreeNode node : children) {
                WmScorePackageSettleTreeNode parent = settleTreeNodeMap.get(Integer.valueOf(node.getRelationScoreId()));
                if (parent.getChildren() == null) {
                    parent.setChildren(new ArrayList<>());
                }
                parent.getChildren().add(node);
                settleTreeNodeMap.put(node.getId(), node);
                if (WmScorePackage.ENT_PACKAGE_TYPEID_SET.contains(node.getTypeid())) {
                    toFetchIds.add(node.getId());
                    entPackageSettleTreeNodes.put(node.getId(), node);
                }
            }
        }
        if (!entPackageSettleTreeNodes.isEmpty()) {
            List<ScorePackageEntTaskScoreStat> approvedEntTaskScoreStats = batchFetchApprovedEntTaskScoreStat(entPackageSettleTreeNodes.keySet());
            for (ScorePackageEntTaskScoreStat approvedEntTaskScoreStat : approvedEntTaskScoreStats) {
                WmScorePackageSettleTreeNode node = entPackageSettleTreeNodes.get(approvedEntTaskScoreStat.getPackageId());
                node.setApprovedEntTaskScore(approvedEntTaskScoreStat.getTotalScore());
            }
        }
        return rootNodes;
    }

    @Override
    public GetApprovedScoreTimeStatByParamsResult getApprovedScoreTimeStatByParams(WmScorePackageSearchParams searchParams, String statUnit) {
        Map<String, ScoreTimeStat> map = new HashMap<>();
        List<WmScorePackageSettleTreeNode> rootNodes = buildApprovedScoreTree(searchParams);
        if (rootNodes.isEmpty()) {
            return new GetApprovedScoreTimeStatByParamsResult();
        }
        Map<String, Set<Integer>> entPackageIdsMap = new HashMap<>();
        Map<String, Set<Integer>> userPackageIdsMap = new HashMap<>();
        Map<String, Set<Integer>> cso1PackageIdsMap = new HashMap<>();
        Map<String, Set<Integer>> cso2PackageIdsMap = new HashMap<>();
        Map<Integer, String> deptSignProducts = getDeptSignProducts();
        for (WmScorePackageSettleTreeNode rootNode : rootNodes) {
            if (rootNode.getPackageFinishTime() == null) {
                continue;
            }
            calculateSettleTreeNodeTotalAmount(rootNode);
            String timeKey = formatTimeKey(rootNode.getPackageFinishTime(), statUnit);
            ScoreTimeStat scoreStat = map.get(timeKey);
            if (scoreStat == null) {
                scoreStat = new ScoreTimeStat();
                scoreStat.setTimeKey(timeKey);
                map.put(timeKey, scoreStat);
            }
            if (rootNode.getApprovedScore() != null) {
                if (Objects.equals(rootNode.getSendPackageDeptId(), WmScorePackage.SPECIAL_DEPT_ID.toString())) {
                    rootNode.setApprovedScore(rootNode.getApprovedScore() / 100);
                }
                scoreStat.setTotalScore(scoreStat.getTotalScore() + rootNode.getApprovedScore());
                scoreStat.setTotalCount(scoreStat.getTotalCount() + 1);
                if (WmScorePackage.USER_PACKAGE_TYPEID_SET.contains(rootNode.getTypeid())) {
                    scoreStat.setUserScore(scoreStat.getUserScore() + rootNode.getApprovedScore());
                    scoreStat.setUserCount(scoreStat.getUserCount() + 1);
                    Set<Integer> userPackageIds = userPackageIdsMap.computeIfAbsent(timeKey, (k) -> new HashSet<>());
                    userPackageIds.add(rootNode.getId());
                } else if (WmScorePackage.ENT_PACKAGE_TYPEID_SET.contains(rootNode.getTypeid())) {
                    scoreStat.setEntScore(scoreStat.getEntScore() + rootNode.getApprovedScore());
                    scoreStat.setEntCount(scoreStat.getEntCount() + 1);
                    Set<Integer> entPackageIds = entPackageIdsMap.computeIfAbsent(timeKey, (k) -> new HashSet<>());
                    entPackageIds.add(rootNode.getId());
                    collectDescendentPackageIds(rootNode, entPackageIds);
                }
                String signProduct = deptSignProducts.get(rootNode.getDeptId());
                if (SysDeptSignProduct.CSO1.equals(signProduct)) {
                    scoreStat.setCso1Score(scoreStat.getCso1Score() + rootNode.getApprovedScore());
                    scoreStat.setCso1Count(scoreStat.getCso1Count() + 1);
                    Set<Integer> cso1PackageIds = cso1PackageIdsMap.computeIfAbsent(timeKey, (k) -> new HashSet<>());
                    cso1PackageIds.add(rootNode.getId());
                    collectDescendentPackageIds(rootNode, cso1PackageIds);
                } else if (SysDeptSignProduct.CSO2.equals(signProduct)) {
                    scoreStat.setCso2Score(scoreStat.getCso2Score() + rootNode.getApprovedScore());
                    scoreStat.setCso2Count(scoreStat.getCso2Count() + 1);
                    Set<Integer> cso2PackageIds = cso2PackageIdsMap.computeIfAbsent(timeKey, (k) -> new HashSet<>());
                    cso2PackageIds.add(rootNode.getId());
                    collectDescendentPackageIds(rootNode, cso2PackageIds);
                }
            }
        }
        // 统计用户数
        Set<Integer> entUserIds = new HashSet<>();
        Set<Integer> userUserIds = new HashSet<>();
        Set<Integer> cso1UserIds = new HashSet<>();
        Set<Integer> cso2UserIds = new HashSet<>();
        for (Map.Entry<String, ScoreTimeStat> entry : map.entrySet()) {
            Set<Integer> entPackageIds = entPackageIdsMap.get(entry.getKey());
            if (entPackageIds != null) {
                Set<Integer> userIds = batchFetchUserIds(entPackageIds);
                entry.getValue().setEntUserCount(userIds.size());
                entUserIds.addAll(userIds);
            }
            Set<Integer> userPackageIds = userPackageIdsMap.get(entry.getKey());
            if (userPackageIds != null) {
                Set<Integer> userIds = batchFetchUserIds(userPackageIds);
                entry.getValue().setUserUserCount(userIds.size());
                userUserIds.addAll(userIds);
            }
            Set<Integer> cso1PackageIds = cso1PackageIdsMap.get(entry.getKey());
            if (cso1PackageIds != null) {
                Set<Integer> userIds = batchFetchUserIds(cso1PackageIds);
                entry.getValue().setCso1UserCount(userIds.size());
                cso1UserIds.addAll(userIds);
            }
            Set<Integer> cso2PackageIds = cso2PackageIdsMap.get(entry.getKey());
            if (cso2PackageIds != null) {
                Set<Integer> userIds = batchFetchUserIds(cso2PackageIds);
                entry.getValue().setCso2UserCount(userIds.size());
                cso2UserIds.addAll(userIds);
            }
        }
        GetApprovedScoreTimeStatByParamsResult result = new GetApprovedScoreTimeStatByParamsResult();
        result.setEntUserCount(entUserIds.size());
        result.setUserUserCount(userUserIds.size());
        result.setCso1UserCount(cso1UserIds.size());
        result.setCso2UserCount(cso2UserIds.size());
        entUserIds.addAll(userUserIds);
        result.setTotalUserCount(entUserIds.size());
        result.setScoreTimeStatList(map.values().stream().sorted(Comparator.comparing(ScoreTimeStat::getTimeKey))
                .collect(Collectors.toList()));
        return result;
    }

    private String formatTimeKey(Date date, String statUnit) {
        if ("day".equals(statUnit)) {
            return DAY_FORMAT.format(date);
        } else if ("week".equals(statUnit)) {
            return formatWeek(date);
        } else if ("month".equals(statUnit)) {
            return MONTH_FORMAT.format(date);
        } else if ("quarter".equals(statUnit)) {
            return YEAR_FORMAT.format(date) + "-" + (date.getMonth() / 3 + 1);
        } else {
            return YEAR_FORMAT.format(date);
        }
    }

    private static String formatWeek(Date date) {
        LocalDate ld = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate firstDay = getFirstDayOfFirstWeekOfYear(ld.getYear());
        if (!ld.isBefore(firstDay)) {
            int week = (int) (ChronoUnit.DAYS.between(firstDay, ld) / 7 + 1);
            if (week < 52) {
                return ld.getYear() + "-" + (week < 10 ? "0" : "") + week;
            }
            LocalDate firstDayOfNextYear = getFirstDayOfFirstWeekOfYear(ld.getYear() + 1);
            if (ld.isBefore(firstDayOfNextYear)) {
                return ld.getYear() + "-" + week;
            } else {
                week = (int) (ChronoUnit.DAYS.between(firstDayOfNextYear, ld) / 7 + 1);
                return (ld.getYear() + 1) + "-" + (week < 10 ? "0" : "") + week;
            }
        } else {
            LocalDate firstDayOfPreviousYear = getFirstDayOfFirstWeekOfYear(ld.getYear() - 1);
            int week = (int) (ChronoUnit.DAYS.between(firstDayOfPreviousYear, ld) / 7 + 1);
            return (ld.getYear() - 1) + "-" + (week < 10 ? "0" : "") + week;
        }
    }

    private static LocalDate getFirstDayOfFirstWeekOfYear(int year) {
        LocalDate firstDay = LocalDate.of(year, 1, 1);
        int weekDayOfFirstDay = firstDay.getDayOfWeek().getValue();
        LocalDate firstDayOfFirstWeek;
        if (weekDayOfFirstDay == 7) {
            firstDayOfFirstWeek = firstDay.plusDays(1);
        } else if (weekDayOfFirstDay == 1) {
            firstDayOfFirstWeek = firstDay;
        } else if (weekDayOfFirstDay == 2) {
            firstDayOfFirstWeek = firstDay.minusDays(1);
        } else if (weekDayOfFirstDay == 3) {
            firstDayOfFirstWeek = firstDay.minusDays(2);
        } else if (weekDayOfFirstDay == 4) {
            firstDayOfFirstWeek = firstDay.minusDays(3);
        } else if (weekDayOfFirstDay == 5) {
            firstDayOfFirstWeek = firstDay.plusDays(3);
        } else {
            firstDayOfFirstWeek = firstDay.plusDays(2);
        }
        return firstDayOfFirstWeek;
    }

    @Override
    public SettledStat getSettledStatByParams(WmScorePackageSearchParams searchParams) {
        SettledStat stat = new SettledStat();
        // 构造积分包树
        Set<Integer> specialPackageIds = new HashSet<>();
        List<WmScorePackageSettleTreeNode> rootNodes = buildSettledStatTree(searchParams, specialPackageIds);
        if (rootNodes.isEmpty()) {
            return stat;
        }
        // 统计积分包树的结算成功金额和结算成功积分
        Queue<WmScorePackageSettleTreeNode> stack = new ArrayDeque<>(rootNodes);
        while (!stack.isEmpty()) {
            WmScorePackageSettleTreeNode node = stack.remove();
            if (Objects.equals(node.getSettleStatus(), 1) || Objects.equals(node.getSettleStatus(), 8)) {
                if (node.getScore() != null) {
                    if (specialPackageIds.contains(node.getId())) {
                        node.setScore(node.getScore() / 100);
                    }
                    stat.setTotalScore(stat.getTotalScore() + node.getScore());
                }
                if (node.getTotalAmount() != null) {
                    stat.setTotalAmount(stat.getTotalAmount().add(node.getTotalAmount()));
                }
                if (node.getTotalActualAmount() != null) {
                    stat.setTotalActualAmount(stat.getTotalActualAmount().add(node.getTotalActualAmount()));
                }
            } else if (node.getChildren() != null) {
                stack.addAll(node.getChildren());
            }
        }
        // 统计结算成功的积分包数量和人员数量
        stack = new ArrayDeque<>(rootNodes);
        Set<Integer> settledPackageIds = new HashSet<>();
        while (!stack.isEmpty()) {
            WmScorePackageSettleTreeNode node = stack.remove();
            if (Objects.equals(node.getSettleStatus(), 1) || Objects.equals(node.getSettleStatus(), 8)) {
                stat.setTotalPackages(stat.getTotalPackages() + 1);
                settledPackageIds.add(node.getId());
            }
            if (node.getChildren() != null) {
                stack.addAll(node.getChildren());
            }
        }
        if (!settledPackageIds.isEmpty()) {
            stat.setTotalUsers(batchFetchUserIds(settledPackageIds).size());
        }
        return stat;
    }

    private List<WmScorePackageSettleTreeNode> buildSettledStatTree(WmScorePackageSearchParams searchParams,
                                                                    Set<Integer> specialPackageIds) {
        // 检索符合条件的积分包
        List<WmScorePackageSearchDto> list = getBaseMapper().selectByParamsForSettledStat(searchParams);
        if (list.isEmpty()) {
            return Collections.emptyList();
        }
        // 将下级积分包合并到上级积分包
        Map<Integer, WmScorePackageSearchDto> mergedMap = mergeSearchDtoList(list);
        // 构造积分包树
        List<WmScorePackageSettleTreeNode> rootNodes = new ArrayList<>(mergedMap.size());
        Map<Integer, WmScorePackageSettleTreeNode> settleTreeNodeMap = new HashMap<>(mergedMap.size());
        Set<Integer> toFetchIds = new HashSet<>();
        for (WmScorePackageSearchDto dto : mergedMap.values()) {
            WmScorePackageSettleTreeNode node = new WmScorePackageSettleTreeNode();
            node.setId(dto.getId());
            node.setTypeid(dto.getTypeid());
            node.setSettleAmount(dto.getSettleAmount());
            node.setSettleActualAmount(dto.getSettleActualAmount());
            node.setP2pAmount(dto.getP2pAmount());
            node.setP2pServiceAmount(dto.getP2pServiceAmount());
            node.setScore(dto.getScore());
            node.setSettleStatus(dto.getSettleStatus());
            node.setScorePackageStatus(dto.getScorePackageStatus());
            node.setSettleNotifyTime(dto.getSettleNotifyTime());
            rootNodes.add(node);
            settleTreeNodeMap.put(node.getId(), node);
            if (WmScorePackage.ENT_PACKAGE_TYPEID_SET.contains(dto.getTypeid())) {
                toFetchIds.add(node.getId());
            }
            if (Objects.equals(dto.getRootPackageSendPackageDeptId(), WmScorePackage.SPECIAL_DEPT_ID)) {
                specialPackageIds.add(dto.getId());
            }
        }
        while (!toFetchIds.isEmpty()) {
            List<WmScorePackageSettleTreeNode> children = batchFetchSettleTreeNode(toFetchIds);
            toFetchIds.clear();
            for (WmScorePackageSettleTreeNode node : children) {
                WmScorePackageSettleTreeNode parent = settleTreeNodeMap.get(Integer.valueOf(node.getRelationScoreId()));
                if (parent.getChildren() == null) {
                    parent.setChildren(new ArrayList<>());
                }
                parent.getChildren().add(node);
                settleTreeNodeMap.put(node.getId(), node);
                if (WmScorePackage.ENT_PACKAGE_TYPEID_SET.contains(node.getTypeid())) {
                    toFetchIds.add(node.getId());
                }
                if (specialPackageIds.contains(parent.getId())) {
                    specialPackageIds.add(node.getId());
                }
            }
        }
        // 计算积分包的结算金额
        for (WmScorePackageSettleTreeNode rootNode : rootNodes) {
            calculateSettleTreeNodeTotalAmount(rootNode);
        }
        return rootNodes;
    }

    private Set<Integer> batchFetchUserIds(Collection<Integer> packageIds) {
        List<Integer> batchIds = new ArrayList<>(packageIds);
        Set<Integer> result = new HashSet<>(packageIds.size());
        int batchSize = 1000;
        for (int i = 0; i < batchIds.size(); i += batchSize) {
            int end = Math.min(i + batchSize, batchIds.size());
            List<Integer> batchResult = getBaseMapper().selectUserIdsByPackageIds(batchIds.subList(i, end));
            result.addAll(batchResult);
        }
        return result;
    }

    @Override
    public SettledTimeStatResult getSettledTimeStatByParams(WmScorePackageSearchParams searchParams, String statUnit) {
        // 构造积分包树
        Set<Integer> specialPackageIds = new HashSet<>();
        List<WmScorePackageSettleTreeNode> rootNodes = buildSettledStatTree(searchParams, specialPackageIds);
        if (rootNodes.isEmpty()) {
            return new SettledTimeStatResult();
        }
        Map<String, SettledTimeStat> map = new HashMap<>();
        // 统计积分包树的结算成功金额和结算成功积分
        Queue<WmScorePackageSettleTreeNode> stack = new ArrayDeque<>(rootNodes);
        while (!stack.isEmpty()) {
            WmScorePackageSettleTreeNode node = stack.remove();
            if (node.getSettleNotifyTime() != null && (Objects.equals(node.getSettleStatus(), 1)
                    || Objects.equals(node.getSettleStatus(), 8))) {
                String timeKey = formatTimeKey(node.getSettleNotifyTime(), statUnit);
                SettledTimeStat stat = map.get(timeKey);
                if (stat == null) {
                    stat = new SettledTimeStat();
                    stat.setTimeKey(timeKey);
                    map.put(timeKey, stat);
                }
                if (node.getScore() != null) {
                    if (specialPackageIds.contains(node.getId())) {
                        node.setScore(node.getScore() / 100);
                    }
                    stat.setTotalScore(stat.getTotalScore() + node.getScore());
                }
                if (node.getTotalAmount() != null) {
                    stat.setTotalAmount(stat.getTotalAmount().add(node.getTotalAmount()));
                }
                if (node.getTotalActualAmount() != null) {
                    stat.setTotalActualAmount(stat.getTotalActualAmount().add(node.getTotalActualAmount()));
                }
            } else if (node.getChildren() != null) {
                stack.addAll(node.getChildren());
            }
        }
        // 统计结算成功的积分包数量和人员数量
        stack = new ArrayDeque<>(rootNodes);
        Map<Integer, WmScorePackageSettleTreeNode> settledPackageIds = new HashMap<>();
        while (!stack.isEmpty()) {
            WmScorePackageSettleTreeNode node = stack.remove();
            if (node.getSettleNotifyTime() != null && (Objects.equals(node.getSettleStatus(), 1) || Objects.equals(node.getSettleStatus(), 8))) {
                String timeKey = formatTimeKey(node.getSettleNotifyTime(), statUnit);
                SettledTimeStat stat = map.get(timeKey);
                if (stat == null) {
                    stat = new SettledTimeStat();
                    stat.setTimeKey(timeKey);
                    map.put(timeKey, stat);
                }
                stat.setTotalPackages(stat.getTotalPackages() + 1);
                settledPackageIds.put(node.getId(), node);
            }
            if (node.getChildren() != null) {
                stack.addAll(node.getChildren());
            }
        }
        SettledTimeStatResult result = new SettledTimeStatResult();
        if (!settledPackageIds.isEmpty()) {
            Set<WmScorePackageStatus> packageStatuses = batchFetchPackageStatus(settledPackageIds.keySet());
            Map<String, Set<String>> userIdsByPackageId = packageStatuses.stream().collect(
                    Collectors.groupingBy(WmScorePackageStatus::getPackageId,
                            Collectors.mapping(WmScorePackageStatus::getUserId, Collectors.toSet())));
            Map<String, Set<String>> userIdsByTimeKey = new HashMap<>();
            Set<String> totalUsers = new HashSet<>();
            for (WmScorePackageSettleTreeNode node : settledPackageIds.values()) {
                Set<String> packageUserIds = userIdsByPackageId.get(node.getId().toString());
                if (packageUserIds == null) {
                    continue;
                }
                String timeKey = formatTimeKey(node.getSettleNotifyTime(), statUnit);
                Set<String> userIds = userIdsByTimeKey.computeIfAbsent(timeKey, k -> new HashSet<>());
                userIds.addAll(packageUserIds);
            }
            for (Map.Entry<String, Set<String>> entry : userIdsByTimeKey.entrySet()) {
                SettledTimeStat stat = map.get(entry.getKey());
                if (stat == null) {
                    stat = new SettledTimeStat();
                    stat.setTimeKey(entry.getKey());
                    map.put(entry.getKey(), stat);
                }
                stat.setTotalUsers(entry.getValue().size());
                totalUsers.addAll(entry.getValue());
            }
            result.setTotalUsers(totalUsers.size());
        }
        result.setList(map.values().stream().sorted(Comparator.comparing(SettledTimeStat::getTimeKey))
                .collect(Collectors.toList()));
        return result;
    }

    private Set<WmScorePackageStatus> batchFetchPackageStatus(Collection<Integer> packageIds) {
        List<Integer> batchIds = new ArrayList<>(packageIds);
        Set<WmScorePackageStatus> result = new HashSet<>(packageIds.size());
        int batchSize = 1000;
        for (int i = 0; i < batchIds.size(); i += batchSize) {
            int end = Math.min(i + batchSize, batchIds.size());
            List<WmScorePackageStatus> batchResult = getBaseMapper().selectPackageStatusByPackageIds(batchIds.subList(i, end));
            result.addAll(batchResult);
        }
        return result;
    }

    @Override
    public List<String> listReceiverNames(String keyword, int limit) {
        return getBaseMapper().selectReceiverNames(keyword, limit);
    }
}
