package com.kcim.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kcim.common.constants.NumberConstant;
import com.kcim.common.constants.ParameterConstant;
import com.kcim.common.constants.SQLParameter;
import com.kcim.common.enums.CustomSqlTypeEnum;
import com.kcim.common.exception.CostException;
import com.kcim.common.util.BeanUtil;
import com.kcim.common.util.JacksonUtil;
import com.kcim.common.util.PageUtils;
import com.kcim.common.util.UserContext;
import com.kcim.dao.mapper.AllocationMapper;
import com.kcim.dao.model.*;
import com.kcim.dao.model.dto.StartDTO;
import com.kcim.dao.repository.CostAccountShareDetailRepository;
import com.kcim.dao.repository.CostAccountShareParamRepository;
import com.kcim.dao.repository.CostAccountShareTargetRepository;
import com.kcim.dao.repository.ResponsibilityRepository;
import com.kcim.service.*;
import com.kcim.vo.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@AllArgsConstructor
@Service("allocationService")
public class AllocationServiceImpl extends BaseBatchServiceImpl<AllocationMapper, Allocation> implements AllocationService {
    //    @Value("${file.serverPath}")
//    private String fileTempPath;
//
//    @Value("${file.serverUrl}")
//    private String FILE_PATH;
    private final CostCostingGroupService costCostingGroupService;
    private final CostShareLevelService shareLevelService;
    private final ResponsibilityService responsibilityService;
    private final CostAccountShareService accountShareService;
    private final ShareParamValueService shareParamValueService;
    private final CostShareParamService shareParamService;
    private final AllocationQueryService allocationQueryService;
    private final ResponsibilityRepository responsibilityRepository;
    private final CenterService centerService;

    private final SqlService sqlService;

    private final CostAccountShareDetailRepository costAccountShareDetailRepository;
    private final CostAccountShareParamRepository costAccountShareParamRepository;
    private final CostAccountShareTargetRepository costAccountShareTargetRepository;

//    public AllocationServiceImpl(CostCostingGroupService costCostingGroupService, CostShareLevelService shareLevelService, ResponsibilityService responsibilityService, CostAccountShareService accountShareService, ShareParamValueService shareParamValueService, CostShareParamService shareParamService, AllocationQueryService allocationQueryService, ResponsibilityRepository responsibilityRepository, CenterService centerService, SqlService sqlService) {
//        this.costCostingGroupService = costCostingGroupService;
//        this.shareLevelService = shareLevelService;
//        this.responsibilityService = responsibilityService;
//        this.accountShareService = accountShareService;
//        this.shareParamValueService = shareParamValueService;
//        this.shareParamService = shareParamService;
//        this.allocationQueryService = allocationQueryService;
//        this.responsibilityRepository = responsibilityRepository;
//        this.centerService = centerService;
//        this.sqlService = sqlService;
//    }

    /**
     * 分摊成本数据
     *
     * @param startDTO {@link StartDTO}
     * @param hospId   医院id
     */
    @Override
    public void startAllocation(StartDTO startDTO, Long hospId) {
        //获取分摊方式
        String parameterValue = centerService.getParameterValue(ParameterConstant.ALLOCATION_TYPE);
        if (NumberConstant.ONE_S.equals(parameterValue)) {
            //开始按责任中心分摊成本
            startCostAllocation(startDTO, hospId);
        }
        else if (NumberConstant.TWO_S.equals(parameterValue)) {
            //按会计科目分摊
            startAccountAllocation(startDTO, hospId);
        }
        //执行分摊后续处理脚本
        execAllocationSQL(startDTO);
    }

    /**
     * 按会计科目分摊
     * @param startDTO
     */
    @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
    public void startAccountAllocation(StartDTO startDTO, Long hospId){
        long timeMillis = System.currentTimeMillis();
        // 得到这个月的所有导入的成本数据
        List<CostCostingGroup> costingGroups = costCostingGroupService.getByYearAndDate(startDTO.getYear(), startDTO.getMonth(), hospId);
        // 没有重新导入
        if (costingGroups.isEmpty()) {
            throw new CostException("本月分摊数据未导入");
        }
        // 导入数据按责任中心归类
        Map<String, List<CostCostingGroup>> respDirectCostMap = costingGroups.stream().collect(Collectors.groupingBy(CostCostingGroup::getResponsibilityCode));
        // 得到这个月导入的成本分摊参数值列表
        List<ShareParamValue> respParamValueList = shareParamValueService.getListByYearAndMonth(startDTO.getYear(), startDTO.getMonth(), hospId);
        if (respParamValueList.isEmpty()) {
            throw new CostException("本月成本分摊参数值未导入");
        }
        Map<String, Map<String, List<ShareParamValue>>> respParamValueGroup = respParamValueList.stream()
                .collect(Collectors.groupingBy(
                        ShareParamValue::getResponsibilityCode,
                        Collectors.groupingBy(ShareParamValue::getShareParamCode)
                ));
        // 得到这个医院所有的分摊层级列表排序
        List<CostShareLevelVO> shareLevelVOs = shareLevelService.getAll(hospId);
        if (CollUtil.isEmpty(shareLevelVOs)) {
            throw new CostException("请先设置医院分摊层级");
        }
        //优化循环调用  责任中心  分摊层级对应
        //成本分摊字典
        List<CostShareParamVO> costShareParamList = shareParamService.getAll(hospId);
        if (CollUtil.isEmpty(costShareParamList)) {
            throw new CostException("请先设置分摊参数字典");
        }
        Map<String,String> ShareParamNameMap = costShareParamList.stream().collect(Collectors.toMap(CostShareParamVO::getShareParamCode, CostShareParamVO::getShareParamName, (a, b) -> b));

        //责任中心
        List<Responsibility> responsibilityList = responsibilityRepository.getList(hospId);
        if (CollUtil.isEmpty(responsibilityList)) {
            throw new CostException("请先设置责任中心");
        }
        //按shareId分组
        Map<Long, List<Responsibility>> responsibilityShareIdMap = responsibilityList.stream().collect(Collectors.groupingBy(Responsibility::getShareId));
        //按责任中心代码分组
        Map<String, List<Responsibility>> responsibilityCodeMap = responsibilityList.stream().collect(Collectors.groupingBy(Responsibility::getResponsibilityCode));
        //分摊参数对应
        List<CostAccountShare> accountShareList = accountShareService.getAll();
        if (CollUtil.isEmpty(accountShareList)) {
            throw new CostException("请先设置责任中心分摊参数对应");
        }
        //责任中心的分摊参数对应设置
        Map<String, List<CostAccountShare>> accountShareResponsibilityMap = accountShareList.stream().collect(Collectors.groupingBy(CostAccountShare::getResponsibilityCode));
        //分摊设置对应的分摊参数明细
        List<CostAccountShareParam> costAccountShareParamList = costAccountShareParamRepository.getCostAccountShareParam(null);
        if (CollUtil.isEmpty(costAccountShareParamList)) {
            throw new CostException("请先设置责任中心的分摊参数");
        }
        //设置分摊参数名称
        costAccountShareParamList.forEach(shareParam->shareParam.setShareParamName(ShareParamNameMap.get(shareParam.getShareParamCode())));
        //按分摊设置ID分组
        Map<Long, List<CostAccountShareParam>> costAccountShareParamMap = costAccountShareParamList.stream().collect(Collectors.groupingBy(CostAccountShareParam::getAccountShareId));
        //分摊设置对应的会计科目明细
        List<CostAccountShareDetail> costAccountShareDetailList = costAccountShareDetailRepository.getCostAccountShareDetail(null);
        Map<Long, List<CostAccountShareDetail>> costAccountShareDetailMap =new HashMap<>();
        //全部都是合并计算时会有会计科目明细为空的情况
        if(!CollectionUtils.isEmpty(costAccountShareDetailList)) {
            costAccountShareDetailMap=costAccountShareDetailList.stream().collect(Collectors.groupingBy(CostAccountShareDetail::getAccountShareId));
        }

        //分摊设置对应的目标责任中心
        List<CostAccountShareTarget> costAccountShareTargetList = costAccountShareTargetRepository.getCostAccountShareTarget(null);
        Map<Long, List<CostAccountShareTarget>> costAccountShareTargetMap =new HashMap<>();
        //全部没有指定分摊时会有目标责任中心为空的情况
        if(!CollectionUtils.isEmpty(costAccountShareTargetList)) {
            costAccountShareTargetMap=costAccountShareTargetList.stream().collect(Collectors.groupingBy(CostAccountShareTarget::getAccountShareId));
        }
        //分摊成本列表
        List<Allocation> allocationCostList=new ArrayList<>();
        //逐级分摊
        for (CostShareLevelVO shareLevelVO : shareLevelVOs) {
            List<Allocation> shareLevelAllocationList = allocationShareLevelCost(startDTO, shareLevelVO, responsibilityList, responsibilityShareIdMap, respDirectCostMap, respParamValueGroup,
                    accountShareResponsibilityMap, costAccountShareParamMap, costAccountShareDetailMap, costAccountShareTargetMap,allocationCostList, timeMillis,hospId);
            allocationCostList.addAll(shareLevelAllocationList);
        }
        //没有任何分摊数据
        if(CollectionUtils.isEmpty(allocationCostList)){
            log.error("没有任何分摊数据......");
            return;
        }
        // 删除该年月已经分摊过的数据
        removeData(startDTO, hospId);
        //保存分摊结果信息
        this.batchInsert(allocationCostList);
        //保存分摊结果查询表
        applyAllocationQuery(startDTO,respDirectCostMap,allocationCostList,responsibilityCodeMap);
    }

    public void batchSaveAllocation(List<Allocation> allocationList) {
        if (CollUtil.isEmpty(allocationList)) {
            return;
        }
        // 调整批次大小，根据数据库性能测试确定最佳值
        int batchSize = 5000;
        // 分批次保存
        for (int i = 0; i < allocationList.size(); i += batchSize) {
            int end = Math.min(i + batchSize, allocationList.size());
            this.saveBatch(allocationList.subList(i, end));
        }
    }

    /**
     * 一个分摊层级的分摊计算
     * @param startDTO 分摊年月所在对象
     * @param shareLevelVO 分摊层级
     * @param responsibilityList 责任中心字典
     * @param responsibilityShareIdMap 各分摊层级的责任中心
     * @param respDirectCostMap 直接成本
     * @param respParamValueGroup 分摊参数数值
     * @param accountShareResponsibilityMap 分摊参数对应
     * @param costAccountShareParamMap 分摊设置对应的分摊参数
     * @param costAccountShareDetailMap 分摊设置对应的会计科目
     * @param allocationCostList 间接成本
     * @param timeMillis 时间戳
     * @return
     */
    public List<Allocation> allocationShareLevelCost(StartDTO startDTO, CostShareLevelVO shareLevelVO,
                                                     List<Responsibility> responsibilityList,
                                                     Map<Long, List<Responsibility>> responsibilityShareIdMap,
                                                     Map<String, List<CostCostingGroup>> respDirectCostMap ,
                                                     Map<String, Map<String, List<ShareParamValue>>> respParamValueGroup,
                                                     Map<String,List<CostAccountShare>> accountShareResponsibilityMap,
                                                     Map<Long, List<CostAccountShareParam>> costAccountShareParamMap,
                                                     Map<Long, List<CostAccountShareDetail>> costAccountShareDetailMap,
                                                     Map<Long, List<CostAccountShareTarget>> costAccountShareTargetMap,
                                                     List<Allocation> allocationCostList,
                                                     long timeMillis, Long hospId){
        //分摊成本列表
        List<Allocation> allocationList=new ArrayList<>();
        // 分摊层级id
        Long levelId = shareLevelVO.getId();
        // 目标分摊层级，可能不存在
        String targetLevel = shareLevelVO.getTargetLevel();
        if (StrUtil.isBlank(targetLevel)) {
            throw new CostException("未设置目标层级");
        }
        //目标分摊层级
        List<String> targetLevelList = Arrays.stream(targetLevel.split(StrUtil.COMMA)).collect(Collectors.toList());
        //目标层级是自己的不需要做分摊处理
        if(NumberConstant.ONE.equals(targetLevelList.size())&&Integer.valueOf(targetLevelList.get(NumberConstant.ZERO)).equals(shareLevelVO.getLeverSort())){
            return allocationList;
        }
        // 得到该分摊层级下责任中心列表，如果不存在，下一个
        List<Responsibility> responsibilities = responsibilityShareIdMap.get(levelId);
        if (CollectionUtils.isEmpty(responsibilities)) {
            return allocationList;
        }

        //目标责任中心列表 - 优化：预先创建Map以提高查找效率
        Map<String, Responsibility> targetResponsibilityMap = responsibilityList.stream()
                .filter(responsibility -> targetLevelList.contains(String.valueOf(responsibility.getShareLevel())))
                .collect(Collectors.toMap(Responsibility::getResponsibilityCode, Function.identity(), (a, b) -> a));

        if (targetResponsibilityMap.isEmpty()) {
            throw new CostException(String.format("[%s-%s]的分摊目标层级没有对应的责任中心",shareLevelVO.getLeverSort(),shareLevelVO.getShareName()));
        }

        // 优化：预先创建间接成本的Map以提高查找效率
        Map<String, List<Allocation>> indirectCostMap = allocationCostList.stream()
                .collect(Collectors.groupingBy(Allocation::getTargetResponsibilityCode));

        // 按责任中心逐个开始分摊（并行）
        allocationList = responsibilities.parallelStream().flatMap(responsibility ->  allocationResponsibilityCost(startDTO, shareLevelVO, responsibility, targetResponsibilityMap, respDirectCostMap, respParamValueGroup,
                    accountShareResponsibilityMap, costAccountShareParamMap, costAccountShareDetailMap,costAccountShareTargetMap, indirectCostMap, timeMillis,hospId).stream()).collect(Collectors.toList());
        return allocationList;
    }


    /**
     * 分摊一个责任中心的成本
     * @param startDTO 分摊年月所在对象
     * @param shareLevelVO 分摊层级
     * @param responsibility 责任中心
     * @param respDirectCostMap 直接成本
     * @param respParamValueGroup 分摊参数数值
     * @param accountShareResponsibilityMap 分摊参数对应
     * @param costAccountShareParamMap 分摊设置对应的分摊参数
     * @param costAccountShareDetailMap 分摊设置对应的会计科目
     * @param indirectCostMap 间接成本
     * @param timeMillis 时间戳
     */
    public List<Allocation> allocationResponsibilityCost(StartDTO startDTO,
                                             CostShareLevelVO shareLevelVO,
                                             Responsibility responsibility,
                                             Map<String, Responsibility> targetResponsibilityMap ,
                                             Map<String, List<CostCostingGroup>> respDirectCostMap ,
                                             Map<String, Map<String, List<ShareParamValue>>> respParamValueGroup,
                                             Map<String,List<CostAccountShare>> accountShareResponsibilityMap,
                                             Map<Long, List<CostAccountShareParam>> costAccountShareParamMap,
                                             Map<Long, List<CostAccountShareDetail>> costAccountShareDetailMap,
                                             Map<Long, List<CostAccountShareTarget>> costAccountShareTargetMap,
                                             Map<String, List<Allocation>> indirectCostMap,
                                             long timeMillis, Long hospId){
        //分摊结果列表
        List<Allocation> allocationList=new ArrayList<>();
        // 得到分摊参数对应记录,不存在，下一个
        List<CostAccountShare> accountShares = accountShareResponsibilityMap.get(responsibility.getResponsibilityCode());
        if (CollectionUtils.isEmpty(accountShares)) {
            return allocationList;
        }
        // 计算方式 0是合并计算  1是分开计算
        Integer calcType = shareLevelVO.getCalcType();
        //合并计算时不应该有多个分摊设置
        if(NumberConstant.ZERO.equals(calcType) &&NumberConstant.ONE!=accountShares.size()){
            throw new CostException(String.format("[%s-%s]的分摊设置有问题，合并计算的责任中心只允许设置一个分摊配置",responsibility.getResponsibilityCode(),responsibility.getResponsibilityName()));
        }
        //间接成本
        List<Allocation> respIndirectCostList =indirectCostMap.get(responsibility.getResponsibilityCode());
        //直接成本
        List<CostCostingGroup> respDirectCostList = respDirectCostMap.get(responsibility.getResponsibilityCode());
        //按分摊设置逐个计算
        for (CostAccountShare accountShare : accountShares) {
            //分摊设置对应的直接成本
            List<CostCostingGroup> paramDirectCostList=new ArrayList<>();
            //分摊设置对应的间接成本
            List<Allocation> paramIndirectCostList=new ArrayList<>();
            //获取分摊设置对应的目标责任中心
            List<Responsibility> shareTargetRespCodeList = getShareTargetResponsibility(accountShare, targetResponsibilityMap, costAccountShareTargetMap);
            //所有目标责任中心的责任中心代码列表
            List<String> targetResponsibilityCodeList = shareTargetRespCodeList.stream().map(Responsibility::getResponsibilityCode).collect(Collectors.toList());
            //合并计算
            if(NumberConstant.ZERO.equals(calcType)){
                //合并计算时分摊参数的成本=责任中心的成本
                paramDirectCostList=respDirectCostList;
                paramIndirectCostList=respIndirectCostList;
            }else{
                //分摊设置对应的会计科目
                List<String> accountList = costAccountShareDetailMap.get(accountShare.getId()).stream().map(CostAccountShareDetail::getAccountingCode).collect(Collectors.toList());
                if(!CollectionUtils.isEmpty(respDirectCostList)) {
                    //获取分摊设置对应的直接成本
                    paramDirectCostList = respDirectCostList.stream().filter(respDirectCost -> accountList.contains(respDirectCost.getAccountCode())).collect(Collectors.toList());
                }
                //只有包含分摊成本的才有间接成本
                if(NumberConstant.ONE.equals(accountShare.getIsShareCost())){
                    paramIndirectCostList=respIndirectCostList;
                }
            }
            //分摊设置对应的分摊参数
            List<CostAccountShareParam> costAccountShareParams = costAccountShareParamMap.get(accountShare.getId());
            //按分摊参数逐个计算所有目标责任中心的分摊金额
            for (CostAccountShareParam shareParam : costAccountShareParams) {
                //获取所有目标责任中心的分摊参数数值信息
                List<ShareParamValue> targetResponsibilityParamList =getTargetResponsibilityParamList( respParamValueGroup,targetResponsibilityCodeList,shareParam);
                if(CollectionUtils.isEmpty(targetResponsibilityParamList)){
                    throw new CostException(String.format("[%s-%s]的目标责任中心找不到对应的参数数值，无法继续分摊",responsibility.getResponsibilityName(),shareParam.getShareParamName()));
                }
                //按分摊参数计算每个会计科目分摊到的金额
                List<Allocation> allocationParamCosts = allocationParamCostCalc(startDTO, shareLevelVO, responsibility, shareTargetRespCodeList, paramDirectCostList,
                        paramIndirectCostList, accountShares.get(NumberConstant.ZERO), shareParam, targetResponsibilityParamList, timeMillis, hospId);
                allocationList.addAll(allocationParamCosts);
            }
        }
        return allocationList;
    }

    /**
     * 获取所有目标责任中心的分摊参数数值信息
     * @param respParamValueGroup
     * @param targetResponsibilityCodeList
     * @param shareParam
     * @return
     */
    public List<ShareParamValue> getTargetResponsibilityParamList(
            Map<String, Map<String, List<ShareParamValue>>> respParamValueGroup,
            List<String> targetResponsibilityCodeList,
            CostAccountShareParam shareParam) {

        List<ShareParamValue> targetResponsibilityParamList = new ArrayList<>();
        String shareParamCode = shareParam.getShareParamCode();

        for (String targetResponsibilityCode : targetResponsibilityCodeList) {
            Map<String, List<ShareParamValue>> shareParamValueMap = respParamValueGroup.get(targetResponsibilityCode);
            if (shareParamValueMap != null) {
                List<ShareParamValue> shareParamValues = shareParamValueMap.get(shareParamCode);
                if (shareParamValues != null) {
                    targetResponsibilityParamList.addAll(shareParamValues);
                }
            }
        }
        return targetResponsibilityParamList;
    }


    /**
     * 获取分摊设置的目标责任中心
     * @param accountShare
     * @param levelTargetResponsibilityMap
     * @param costAccountShareTargetMap
     * @return
     */
    public List<Responsibility> getShareTargetResponsibility(CostAccountShare accountShare,
                                                             Map<String, Responsibility> levelTargetResponsibilityMap,
                                                             Map<Long, List<CostAccountShareTarget>> costAccountShareTargetMap){
        //获取分摊设置对应的目标责任中心
        List<CostAccountShareTarget> shareTargetResponsibilityList=costAccountShareTargetMap.get(accountShare.getId());
        if(CollectionUtils.isEmpty(shareTargetResponsibilityList)){
            return new ArrayList<>(levelTargetResponsibilityMap.values());
        }
        //筛选出指定的目标责任中心
        List<String> shareTargetRespCodeList = shareTargetResponsibilityList.stream().map(CostAccountShareTarget::getTargetResponsibilityCode).collect(Collectors.toList());
        List<Responsibility> shareTargetRespList = new ArrayList<>();
        for (String shareTargetRespCode : shareTargetRespCodeList) {
            Responsibility levelTargetResponsibility = levelTargetResponsibilityMap.get(shareTargetRespCode);
            if (levelTargetResponsibility == null) {
                continue;
            }
            shareTargetRespList.add(levelTargetResponsibility);
        }
        if(CollectionUtils.isEmpty(shareTargetRespList)){
            throw new CostException(String.format("[%s-%s]找不到对应的目标责任中心，无法继续分摊",accountShare.getResponsibilityName(),accountShare.getAlias()));
        }
        return shareTargetRespList;
    }

    /**
     * 按分摊参数计算每个会计科目分摊到的金额
     * @param startDTO 分摊年月所在对象
     * @param shareLevelVO 分摊层级
     * @param responsibility 责任中心
     * @param targetResponsibilityList 目标责任中心列表
     * @param respDirectCostList 直接成本
     * @param respIndirectCostList 间接成本
     * @param costAccountShare 分摊参数对应
     * @param shareParam 分摊参数
     * @param targetResponsibilityParamList 目标责任中心的分摊参数数值列表
     * @param timeMillis 时间戳
     * @return
     */
    public List<Allocation> allocationParamCostCalc(StartDTO startDTO,
                                               CostShareLevelVO shareLevelVO,
                                               Responsibility responsibility,
                                               List<Responsibility> targetResponsibilityList,
                                               List<CostCostingGroup> respDirectCostList,
                                               List<Allocation> respIndirectCostList,
                                               CostAccountShare costAccountShare,
                                               CostAccountShareParam shareParam,
                                               List<ShareParamValue> targetResponsibilityParamList,
                                               long timeMillis, Long hospId){
        //重新定义一个列表用于合并直接成本和间接成本
        List<CostCostingGroup> respCostList=new ArrayList<>();
        //添加直接成本
        if(!CollectionUtils.isEmpty(respDirectCostList)){
            respCostList.addAll(respDirectCostList);
        }
        //添加间接成本
        if(!CollectionUtils.isEmpty(respIndirectCostList)){
            //转成直接成本的对象
            List<CostCostingGroup> respIndirectCosts = BeanUtil.convertList(respIndirectCostList, CostCostingGroup.class);
            respCostList.addAll(respIndirectCosts);
        }
        //计算所有目标责任中心的分摊参数加总
        BigDecimal totalParamValue= targetResponsibilityParamList.stream().map(ShareParamValue::getValueNum).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (NumberConstant.ZERO.equals(totalParamValue.compareTo(BigDecimal.ZERO))){
            throw new CostException(String.format("[%s-%s]的目标责任中心参数数值加总为0，无法继续分摊",responsibility.getResponsibilityName(),shareParam.getShareParamName()));
        }
        //按责任中心分组，方便取到对应责任中心的分摊参数数值
        Map<String, List<ShareParamValue>> targetRespParamGroup = targetResponsibilityParamList.stream().collect(Collectors.groupingBy(ShareParamValue::getResponsibilityCode));
        //创建accountCode到accountName的映射
        Map<String, String> accountCodeNameMap = respCostList.stream().collect(Collectors.toMap(CostCostingGroup::getAccountCode,
                CostCostingGroup::getAccountName,
                (existing, replacement) -> replacement // 处理重复键的情况，保留最后一个值
                ));
        //按会计科目分组，相同会计科目一起分摊
        Map<String, BigDecimal> respDirectCostGroup = respCostList.stream().collect(Collectors.groupingBy(CostCostingGroup::getAccountCode,
                        Collectors.reducing(BigDecimal.ZERO,CostCostingGroup::getAmount,BigDecimal::add)));
        //参数占比
        BigDecimal paramRate = shareParam.getShareParamProportion().divide(new BigDecimal(NumberConstant.ONE_HUNDRED), NumberConstant.FOUR, RoundingMode.HALF_UP);
        //分摊结果列表
        List<Allocation> allocationList=new ArrayList<>();
        for (Responsibility targetResponsibility : targetResponsibilityList){
            List<ShareParamValue> targetRespParamValues = targetRespParamGroup.get(targetResponsibility.getResponsibilityCode());
            //目标责任中心的分摊参数数值
            BigDecimal targetRespParamValue = BigDecimal.ZERO;
            if(!CollectionUtils.isEmpty(targetRespParamValues)) {
                targetRespParamValue = targetRespParamValues.stream().map(ShareParamValue::getValueNum).reduce(BigDecimal.ZERO, BigDecimal::add);
            }
            //没有参数数值就不分摊计算了，不然数据会很多
            if(NumberConstant.ZERO.equals(BigDecimal.ZERO.compareTo(targetRespParamValue))){
                continue;
            }
            //计算分摊参数比例
            BigDecimal shareParamRate =  targetRespParamValue.divide(totalParamValue, NumberConstant.FOUR, RoundingMode.HALF_UP);

            for (Map.Entry<String, BigDecimal> entry : respDirectCostGroup.entrySet()) {
                String accountCode = entry.getKey();
                BigDecimal totalAmount = entry.getValue();
                //没有金额的会计科目就不分摊计算了，不然数据会很多
                if(NumberConstant.ZERO.equals(BigDecimal.ZERO.compareTo(totalAmount))){
                    continue;
                }
                //计算分摊到的金额=目标责任中心的分摊参数数值*会计科目总金额/所有目标责任中心的分摊参数数值加总*参数比例
                BigDecimal targetAmount = targetRespParamValue.multiply(totalAmount).multiply(paramRate).divide(totalParamValue, NumberConstant.FOUR, RoundingMode.HALF_UP);
                //生成分摊结果的对象
                Allocation targetAllocation =  createAllocation(startDTO, shareLevelVO, responsibility, targetResponsibility, costAccountShare,  shareParam,timeMillis,hospId);
                targetAllocation.setAmount(targetAmount)
                        .setTotalAmount(totalAmount)
                        .setShareParamValueNum(targetRespParamValue)
                        .setShareParamRate(shareParamRate)
                        .setAccountCode(accountCode)
                        .setAccountName(accountCodeNameMap.get(accountCode));
                allocationList.add(targetAllocation);
            }
        }
        return allocationList;
    }

    /**
     * 按分摊参数计算每个会计科目分摊到的金额（多线程）
     * @param startDTO 分摊年月所在对象
     * @param shareLevelVO 分摊层级
     * @param responsibility 责任中心
     * @param targetResponsibilityList 目标责任中心列表
     * @param respDirectCostList 直接成本
     * @param respIndirectCostList 间接成本
     * @param costAccountShare 分摊参数对应
     * @param shareParam 分摊参数
     * @param targetResponsibilityParamList 目标责任中心的分摊参数数值列表
     * @param timeMillis 时间戳
     * @return
     */
    public List<Allocation> synAllocationParamCostCalc(StartDTO startDTO,
                                                    CostShareLevelVO shareLevelVO,
                                                    Responsibility responsibility,
                                                    List<Responsibility> targetResponsibilityList,
                                                    List<CostCostingGroup> respDirectCostList,
                                                    List<Allocation> respIndirectCostList,
                                                    CostAccountShare costAccountShare,
                                                    CostAccountShareParam shareParam,
                                                    List<ShareParamValue> targetResponsibilityParamList,
                                                    long timeMillis, Long hospId){
        //重新定义一个列表用于合并直接成本和间接成本
        List<CostCostingGroup> respCostList=new ArrayList<>();
        //添加直接成本
        if(!CollectionUtils.isEmpty(respDirectCostList)){
            respCostList.addAll(respDirectCostList);
        }
        //添加间接成本
        if(!CollectionUtils.isEmpty(respIndirectCostList)){
            //转成直接成本的对象
            List<CostCostingGroup> respIndirectCosts = BeanUtil.convertList(respIndirectCostList, CostCostingGroup.class);
            respCostList.addAll(respIndirectCosts);
        }
        //计算所有目标责任中心的分摊参数加总
        BigDecimal totalParamValue= targetResponsibilityParamList.stream().map(ShareParamValue::getValueNum).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (NumberConstant.ZERO.equals(totalParamValue.compareTo(BigDecimal.ZERO))){
            throw new CostException(String.format("[%s-%s]的目标责任中心参数数值加总为0，无法继续分摊",responsibility.getResponsibilityName(),shareParam.getShareParamName()));
        }

        //按责任中心分组，方便取到对应责任中心的分摊参数数值
        Map<String, BigDecimal> targetRespParamGroup=targetResponsibilityParamList.stream().collect(Collectors.groupingBy(ShareParamValue::getResponsibilityCode,Collectors.reducing(BigDecimal.ZERO, ShareParamValue::getValueNum, BigDecimal::add)));

        //创建accountCode到accountName的映射
        Map<String, String> accountCodeNameMap = respCostList.stream().collect(Collectors.toMap(
                CostCostingGroup::getAccountCode,
                CostCostingGroup::getAccountName,
                (existing, replacement) -> replacement // 处理重复键的情况，保留最后一个值
        ));

        //按会计科目分组，相同会计科目一起分摊并计算总金额
        Map<String, BigDecimal> respDirectCostGroup = respCostList.stream().collect(Collectors.groupingBy(
                CostCostingGroup::getAccountCode,
                Collectors.reducing(BigDecimal.ZERO, CostCostingGroup::getAmount, BigDecimal::add)
        ));

        //预计算参数比例，避免在循环中重复计算
        BigDecimal paramRate = shareParam.getShareParamProportion()
                .divide(new BigDecimal(NumberConstant.ONE_HUNDRED), NumberConstant.FOUR, RoundingMode.HALF_UP);

        //分摊结果列表（使用线程安全的列表）
        List<Allocation> allocationList = Collections.synchronizedList(new ArrayList<>());

        //使用并行流处理目标责任中心
        targetResponsibilityList.parallelStream().forEach(targetResponsibility -> {
            //目标责任中心的分摊参数数值
            BigDecimal targetRespParamValue = targetRespParamGroup.get(targetResponsibility.getResponsibilityCode());
            if(ObjectUtils.isEmpty(targetRespParamValue)){
                return;
            }
            //计算分摊参数比例
            BigDecimal shareParamRate = targetRespParamValue.divide(totalParamValue, NumberConstant.FOUR, RoundingMode.HALF_UP);
            //预计算基础金额，避免在内层循环中重复计算
            BigDecimal baseAmount = targetRespParamValue.multiply(paramRate).divide(totalParamValue, NumberConstant.SIX, RoundingMode.HALF_UP);
            //遍历每个会计科目
            respDirectCostGroup.entrySet().parallelStream().forEach(entry -> {
                String accountCode = entry.getKey();
                BigDecimal totalAmount = entry.getValue();
                //计算分摊到的金额=目标责任中心的分摊参数数值*会计科目总金额/所有目标责任中心的分摊参数数值加总*参数比例
                //优化：使用预计算的baseAmount
                BigDecimal targetAmount = totalAmount.multiply(baseAmount);
                //生成分摊结果的对象
                Allocation targetAllocation = createAllocation(startDTO, shareLevelVO, responsibility, targetResponsibility, costAccountShare,  shareParam,timeMillis,hospId);
                targetAllocation.setAmount(targetAmount)
                        .setTotalAmount(totalAmount)
                        .setShareParamValueNum(targetRespParamValue)
                        .setShareParamRate(shareParamRate)
                        .setAccountCode(accountCode)
                        .setAccountName(accountCodeNameMap.get(accountCode));
                //同步添加到结果列表
                synchronized(allocationList) {
                    allocationList.add(targetAllocation);
                }
            });
        });

        return new ArrayList<>(allocationList); //返回普通ArrayList
    }


    /**
     * 创建分摊结果对象
     * @param startDTO
     * @param shareLevelVO
     * @param responsibility
     * @param targetResponsibility
     * @param costAccountShare
     * @param shareParam
     * @param timeMillis
     * @param hospId
     * @return
     */
    public Allocation createAllocation(StartDTO startDTO,
                                       CostShareLevelVO shareLevelVO,
                                       Responsibility responsibility,
                                       Responsibility targetResponsibility,
                                       CostAccountShare costAccountShare,
                                       CostAccountShareParam shareParam,
                                       long timeMillis,
                                       Long hospId) {
        Allocation targetAllocation = new Allocation();
        targetAllocation.setDateMonth(startDTO.getMonth())
                .setDateYear(startDTO.getYear())
                .setLevelSort(shareLevelVO.getLeverSort())
                .setLevelName(shareLevelVO.getShareName())
                .setHospId(hospId)
                .setResponsibilityCode(responsibility.getResponsibilityCode())
                .setResponsibilityName(responsibility.getResponsibilityName())
                .setAccountShareId(costAccountShare.getId())
//                .setAmount(targetAmount)
                .setCreateTime(timeMillis)
                .setTargetResponsibilityCode(targetResponsibility.getResponsibilityCode())
                .setTargetResponsibilityName(targetResponsibility.getResponsibilityName())
                .setShareParamCode(shareParam.getShareParamCode())
                .setShareParamName(shareParam.getShareParamName())
//                .setTotalAmount(totalAmount)
//                .setShareParamValueNum(targetRespParamValue)
//                .setShareParamRate(shareParamRate)
                .setShareLevelId(responsibility.getShareId())
                .setTargetShareLevelId(targetResponsibility.getShareId());
//                .setAccountCode(accountCode)
//                .setAccountName(accountCodeNameMap.get(accountCode));
        return targetAllocation;
    }


    /**
     * 保存分摊结果查询表
     * @param startDTO 分摊年月所在对象
     * @param respDirectCostMap 责任中心的直接成本
     * @param allocationCostList 所有责任中心的间接成本
     * @param responsibilityCodeMap 责任中心字典
     */
    public void applyAllocationQuery(StartDTO startDTO, Map<String, List<CostCostingGroup>> respDirectCostMap ,List<Allocation> allocationCostList,Map<String, List<Responsibility>> responsibilityCodeMap){
        List<AllocationQuery> allocationQueryList=new ArrayList<>();
        //处理直接成本,直接成本可能是多次导入的没有按会计科目汇总的，需按责任中心+会计科目汇总保存
        respDirectCostMap.forEach((respCode,respCostList)->{
            List<AllocationQuery> directAllocationQueryList = createDirectAllocationQuery(startDTO, respCode, respCostList, responsibilityCodeMap);
            allocationQueryList.addAll(directAllocationQueryList);
        });
        //处理间接成本,间接成本肯定是按会计科目汇总的，可直接生成对象
        for (Allocation indirectCost : allocationCostList) {
            AllocationQuery indirectAllocationQuery = createIndirectAllocationQuery(startDTO, indirectCost);
            allocationQueryList.add(indirectAllocationQuery);
        }
        if(CollectionUtils.isEmpty(allocationQueryList)){
            log.error("没有任何分摊结果数据......");
            return;
        }
        //保存分摊结果查询对象信息
        allocationQueryService.batchInsertAllocationQuery(allocationQueryList);
    }

    /**
     * 创建直接成本的分摊结果查询对象
     * @param startDTO 分摊年月所在对象
     * @param respCode 责任中心代码
     * @param respCostList 责任中心的直接成本数据
     * @param responsibilityCodeMap 责任中心字典
     * @return
     */
    public List<AllocationQuery> createDirectAllocationQuery(StartDTO startDTO,String respCode,List<CostCostingGroup> respCostList, Map<String, List<Responsibility>> responsibilityCodeMap){
        List<AllocationQuery> allocationQueryList=new ArrayList<>();
        if(!responsibilityCodeMap.containsKey(respCode)){
            log.error(String.format("[%s-%s]的责任中心数据已过期",respCode,respCostList.get(NumberConstant.ZERO).getResponsibilityName()));
            return allocationQueryList;
        }
        //责任中心数据
        Responsibility responsibility=responsibilityCodeMap.get(respCode).get(NumberConstant.ZERO);
        //按会计科目代码分组
        Map<String, List<CostCostingGroup>> respDirectCostGroup = respCostList.stream().collect(Collectors.groupingBy(CostCostingGroup::getAccountCode));
        //按会计科目逐个生成分摊结果查询对象
        respDirectCostGroup.forEach((respAccountCode,respAccountCostList)->{
            CostCostingGroup firtsDirectCost = respAccountCostList.get(NumberConstant.ZERO);
            //汇总金额
            BigDecimal totalAccountCost = respAccountCostList.stream().map(CostCostingGroup::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            //创建分摊结果查询对象
            AllocationQuery allocationQuery = new AllocationQuery();
            allocationQuery.setDateYear(startDTO.getYear()).setDateMonth(startDTO.getMonth())
                    .setHospId(UserContext.getHospId()).setResponsibilityCode(responsibility.getResponsibilityCode()).setResponsibilityName(responsibility.getResponsibilityName())
                    .setCreateTime(System.currentTimeMillis())
                    .setLevelSort(responsibility.getShareLevel()).setLevelName(responsibility.getShareName())
                    .setTargetResponsibilityCode(responsibility.getResponsibilityCode())
                    .setTargetResponsibilityName(responsibility.getResponsibilityName())
                    .setShareLevelId(responsibility.getShareId())
                    .setOriginId(firtsDirectCost.getId()).setOriginType(Long.valueOf(NumberConstant.ONE)).setAmount(totalAccountCost)
                    .setAccountingCode(firtsDirectCost.getAccountCode()).setAccountingName(firtsDirectCost.getAccountName());
            allocationQueryList.add(allocationQuery);
        });
        return allocationQueryList;
    }

    /**
     * 创建间接成本的分摊结果查询对象
     * @param startDTO 分摊年月所在对象
     * @param indirectCost 间接成本对象
     * @return
     */
    public AllocationQuery createIndirectAllocationQuery(StartDTO startDTO,Allocation indirectCost){
        AllocationQuery allocationQuery = new AllocationQuery();
        allocationQuery.setDateYear(startDTO.getYear()).setDateMonth(startDTO.getMonth())
                .setHospId(UserContext.getHospId()).setResponsibilityCode(indirectCost.getResponsibilityCode())
                .setResponsibilityName(indirectCost.getResponsibilityName())
                .setOriginId(indirectCost.getId()).setOriginType(Long.valueOf(NumberConstant.TWO)).setAmount(indirectCost.getAmount())
                .setAccountingCode(indirectCost.getAccountCode()).setAccountingName(indirectCost.getAccountName())
                .setCreateTime(System.currentTimeMillis())
                .setLevelSort(indirectCost.getLevelSort()).setLevelName(indirectCost.getLevelName())
                .setTargetResponsibilityCode(indirectCost.getTargetResponsibilityCode())
                .setTargetResponsibilityName(indirectCost.getTargetResponsibilityName())
                .setShareLevelId(indirectCost.getShareLevelId());
        return allocationQuery;
    }

    /**
     * 执行分摊后续处理脚本
     * @param startDTO
     */
    public void execAllocationSQL(StartDTO startDTO){
        //执行分摊后续处理脚本
        Map<String,String> sqlParameter = new HashMap<>();
        if(startDTO.getMonth()<10){
            sqlParameter.put(SQLParameter.COMPUTE_DATE_CODE,startDTO.getYear()+"-0"+startDTO.getMonth());
        }else{
            sqlParameter.put(SQLParameter.COMPUTE_DATE_CODE,startDTO.getYear()+"-"+startDTO.getMonth());
        }
        sqlService.autoExecuteSql(CustomSqlTypeEnum.COST_ALLOCATION_CALC.getCode(),sqlParameter);
    }


    /**
     * 按责任中心分摊
     * @param startDTO
     * @param hospId
     */
    @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
    public  void startCostAllocation(StartDTO startDTO, Long hospId){
        long timeMillis = System.currentTimeMillis();
        // 得到这个月的所有导入的成本数据
        List<CostCostingGroup> costingGroups = costCostingGroupService.getByYearAndDate(startDTO.getYear(), startDTO.getMonth(), hospId);
        // 没有重新导入
        if (costingGroups.isEmpty()) {
            throw new CostException("本月分摊数据未导入");
        }
        // 导入数据按责任中心归类
        Map<String, List<CostCostingGroup>> responsibilityCodeMap = costingGroups.stream().collect(Collectors.groupingBy(CostCostingGroup::getResponsibilityCode));
        // 得到这个月导入的成本分摊参数值列表
        List<ShareParamValue> shareParamValues = shareParamValueService.getListByYearAndMonth(startDTO.getYear(), startDTO.getMonth(), hospId);
        if (shareParamValues.isEmpty()) {
            throw new CostException("本月成本分摊参数值未导入");
        }
        Map<String, List<ShareParamValue>> paramValueRespCodeMap = shareParamValues.stream().collect(Collectors.groupingBy(ShareParamValue::getResponsibilityCode));
        // 删除该年月已经分摊过的数据
        removeData(startDTO, hospId);
        // 得到这个医院所有的分摊层级列表排序
        List<CostShareLevelVO> shareLevelVOs = shareLevelService.getAll(hospId);
        if (CollUtil.isEmpty(shareLevelVOs)) {
            throw new CostException("请先设置医院分摊层级");
        }
        //优化循环调用  责任中心  分摊层级对应
        //成本分摊字典
        List<CostShareParamVO> costShareParamList = shareParamService.getAll(hospId);
        if (CollUtil.isEmpty(costShareParamList)) {
            throw new CostException("请先设置分摊参数字典");
        }
        Map<String,String> ShareParamNameMap = costShareParamList.stream().collect(Collectors.toMap(CostShareParamVO::getShareParamCode, CostShareParamVO::getShareParamName, (a, b) -> b));

        //责任中心
        List<Responsibility> responsibilityList = responsibilityRepository.getList(hospId);
        if (CollUtil.isEmpty(responsibilityList)) {
            throw new CostException("请先设置责任中心");
        }
        //按shareId分组
        Map<Long, List<Responsibility>> responsibilityShareIdMap = responsibilityList.stream().collect(Collectors.groupingBy(Responsibility::getShareId));
        Map<String,String> responsibilityNameMap = responsibilityList.stream().collect(Collectors.toMap(Responsibility::getResponsibilityCode, Responsibility::getResponsibilityName, (a, b) -> b));
        Map<String,Long> responsibilityShareIdDictMap = responsibilityList.stream().collect(Collectors.toMap(Responsibility::getResponsibilityCode, Responsibility::getShareId, (a, b) -> b));
        //分摊参数对应
        List<CostAccountShare> accountShareList = accountShareService.getAll();
        if (CollUtil.isEmpty(accountShareList)) {
            throw new CostException("请先设置责任中心分摊参数对应");
        }
        Map<String, List<CostAccountShare>> accountShareResponsibilityMap = accountShareList.stream().collect(Collectors.groupingBy(CostAccountShare::getResponsibilityCode));
        List<Allocation> allocations = new LinkedList<>();
        // key  责任中心代码， value 分摊过来的钱
//        Map<String, BigDecimal> costMap = new ConcurrentReaderHashMap();
        List<Allocation> costList = new ArrayList<>();
        for (CostShareLevelVO shareLevelVO : shareLevelVOs) {
            // 分摊层级id
            Long levelId = shareLevelVO.getId();
            // 目标分摊层级，可能不存在
            String targetLevel = shareLevelVO.getTargetLevel();
            if (StrUtil.isBlank(targetLevel)) {
                throw new CostException("未设置目标层级");
            }
            // 计算方式 0是合并计算  1是分开计算
            Integer calcType = shareLevelVO.getCalcType();
            // 得到该分摊层级下责任中心列表，如果不存在，下一个
            List<Responsibility> responsibilities = responsibilityShareIdMap.get(levelId);
//            List<Responsibility> responsibilities = responsibilityService.getLevelIdByCode(levelId, hospId);
//            if (responsibilities.isEmpty()) {
//                continue;
//            }
            if(CollectionUtils.isEmpty(responsibilities)){
                continue;
            }
            // 优化
            // 遍历责任中心得到对应的分摊参数对应
            for (Responsibility responsibility : responsibilities) {
//                String responsibilityCode = responsibility.getResponsibilityCode();
                // 得到分摊参数对应记录,不存在，下一个
                List<CostAccountShare> accountShares = accountShareResponsibilityMap.get(responsibility.getResponsibilityCode());
//                List<CostAccountShare> accountShares = accountShareService.getByResponsibility(responsibilityCode, hospId);
                if (CollectionUtils.isEmpty(accountShares)) {
                    continue;
                }
                // 遍历分摊参数对应记录
                for (CostAccountShare accountShare : accountShares) {
                    Long accountShareId = accountShare.getId();
                    String paramList = accountShare.getParamList();
                    // 如果分摊比例未设置直接报错
                    if (StrUtil.isBlank(paramList)) {
                        throw new CostException("责任中心：" + accountShare.getResponsibilityName() + ";会计科目为：" + accountShare.getAccountingNames() + ";未设置分摊参数比例");
                    }
                    List<AccountShareVO> accountShareVOs = JacksonUtil.str2ObjList(paramList, List.class, AccountShareVO.class);
//                    List<CostCostingGroup> groups = responsibilityCodeMap.get(responsibilityCode);
//                    if (CollUtil.isEmpty(groups)) {
//                        continue;
//                    }
                    // 计算本次分摊的钱
                    BigDecimal totalAmount = this.getCostAmount(accountShare, calcType, responsibilityCodeMap, costList);
                    if (totalAmount.equals(BigDecimal.ZERO)) {
                        continue;
                    }
                    // 相关的分摊参数比例
                    if (!CollectionUtils.isEmpty(accountShareVOs)) {
                        for (AccountShareVO accountShareVO : accountShareVOs) {
//                            String paramCode = accountShareVO.getShareParamCode();
                            String shareParamPopout = accountShareVO.getShareParamPopout();
                            BigDecimal bigDecimal = new BigDecimal(shareParamPopout);
                            BigDecimal rate = bigDecimal.divide(new BigDecimal(100), 4, RoundingMode.HALF_UP);
//                            BigDecimal rate = new BigDecimal("1");
//                            if (!"100".equals(shareParamPopout)) {
//                                rate = new BigDecimal("0." + shareParamPopout);
//                            }
                            // 本次的分摊比例计算
                            BigDecimal thisAmount = rate.multiply(totalAmount);
                            // 得到目标层级责任中心列表
                            List<Responsibility> targetResponsibilities = this.getTargetResponsibility(targetLevel, hospId, shareLevelVO.getLeverSort());
                            if (targetResponsibilities.isEmpty()) {
                                throw new CostException("找不到目标责任中心");
                            }
                            // 目标责任中心得到对应
                            List<ShareParamValue> targetShareParamValue = getTarget(targetResponsibilities, accountShareVO, paramValueRespCodeMap);
                            if (CollUtil.isEmpty(targetShareParamValue)) {
                                throw new CostException("找不到目标责任中心对应的分摊参数【"+accountShareVO.getShareParamCode()+"】值");
                            }
                            // 分母
                            BigDecimal reduce = targetShareParamValue.stream().map(ShareParamValue::getValueNum).reduce(BigDecimal.ZERO, BigDecimal::add);
                            for (ShareParamValue paramValue : targetShareParamValue) {
                                // 分子
                                BigDecimal numerator = paramValue.getValueNum();
                                BigDecimal targetAmount = reduce.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : thisAmount.multiply(numerator).divide(reduce, RoundingMode.HALF_UP);
                                BigDecimal percent = reduce.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : numerator.divide(reduce, RoundingMode.HALF_UP);
                                Allocation targetAllocation = new Allocation();
                                String valueResponsibilityCode = paramValue.getResponsibilityCode();
                                //优化循环调用
                                String targetRespName = responsibilityNameMap.get(valueResponsibilityCode);
                                //                            String targetRespName = responsibilityService.getByCode(valueResponsibilityCode, hospId);
                                Long targetShareLevelId = responsibilityShareIdDictMap.get(valueResponsibilityCode);
                                //                            Long targetShareLevelId = responsibilityService.getLevelIdByCode(valueResponsibilityCode, hospId);
                                if (Objects.isNull(targetShareLevelId)) {
                                    throw new CostException("目标责任中心分摊层级异常");
                                }
                                String shareParamName = ShareParamNameMap.get(paramValue.getShareParamCode());
                                //                            String shareParamName = shareParamService.getByCode(paramValue.getShareParamCode(), hospId);
                                targetAllocation.setDateMonth(startDTO.getMonth()).setDateYear(startDTO.getYear()).setLevelSort(shareLevelVO.getLeverSort())
                                        .setLevelName(shareLevelVO.getShareName()).setHospId(hospId).setResponsibilityCode(responsibility.getResponsibilityCode())
                                        .setResponsibilityName(responsibility.getResponsibilityName()).setAccountShareId(accountShareId).setAmount(targetAmount)
                                        .setCreateTime(timeMillis).setTargetResponsibilityCode(valueResponsibilityCode).setTargetResponsibilityName(targetRespName)
                                        .setShareParamCode(paramValue.getShareParamCode()).setShareParamName(shareParamName).setTotalAmount(totalAmount).setShareParamValueNum(paramValue.getValueNum())
                                        .setShareParamRate(percent).setShareLevelId(levelId).setTargetShareLevelId(targetShareLevelId);

                                // todo 目标分摊层级责任中心 就是当前列个表中的责任中心
                                allocations.add(targetAllocation);
                                costList.add(targetAllocation);
                            }
                        }
                    }
                }
            }
        }
        this.saveBatch(allocations);
        List<Allocation> list = this.list(
                new LambdaQueryWrapper<Allocation>()
                        .eq(Allocation::getHospId, hospId).eq(Allocation::getDateYear, startDTO.getYear()).eq(Allocation::getDateMonth, startDTO.getMonth())
        );
        if (list.isEmpty()) {
            log.error("未分摊到数据......");
            return;
        }

        // 入cost_allocation_query 表 便于后续操作
        this.saveAllocationQuery(list, hospId, startDTO.getYear(), startDTO.getMonth(),shareLevelVOs,accountShareList);

    }

    private void removeData(StartDTO startDTO, Long hospId) {
        this.remove(
                new LambdaQueryWrapper<Allocation>()
                        .eq(Allocation::getDateYear, startDTO.getYear())
                        .eq(Allocation::getDateMonth, startDTO.getMonth())
                        .eq(Allocation::getHospId, hospId)
        );
        allocationQueryService.remove(
                new LambdaQueryWrapper<AllocationQuery>()
                        .eq(AllocationQuery::getDateYear, startDTO.getYear())
                        .eq(AllocationQuery::getDateMonth, startDTO.getMonth())
                        .eq(AllocationQuery::getHospId, hospId)
        );
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public void saveAllocationQuery(List<Allocation> list, Long hospId,
                                    Integer year, Integer month, List<CostShareLevelVO> shareLevelVOs, List<CostAccountShare> accountShareList) {
        List<AllocationQuery> saveList = new ArrayList<>();
        List<Long> maxId = shareLevelService.getMaxId(hospId);
//        List<Allocation> maxList = list.stream().filter(i -> maxId.contains(i.getTargetShareLevelId())).collect(Collectors.toList());
        Map<Long, List<Allocation>> collect = list.stream().collect(Collectors.groupingBy(Allocation::getTargetShareLevelId));
        //优化 去除循环调用
//        List<CostAccountShare> all = accountShareService.getAll();
        Map<Long,CostAccountShare> accountShareMap = accountShareList.stream().collect(Collectors.toMap(CostAccountShare::getId, costAccountShare -> costAccountShare, (a, b) -> b));
        // 得到这个月的所有导入的成本数据用责任中心关联取得shareId 优化 去除循环调用
        List<CostCostingGroup> costingGroups = costCostingGroupService.getRealData(CollUtil.newArrayList(), hospId, year, month);
        Map<Long, List<CostCostingGroup>> costShareIdGroup = costingGroups.stream().collect(Collectors.groupingBy(CostCostingGroup::getShareId));
        for (CostShareLevelVO shareLevelVO : shareLevelVOs) {
            List<Allocation> allocations = collect.get(shareLevelVO.getId());
            if(!CollectionUtils.isEmpty(allocations)){
                setAfterAllocation(allocations, hospId, saveList,accountShareMap);

            }
            if(!CollectionUtils.isEmpty(costShareIdGroup.get(shareLevelVO.getId()))){
                setPreAllocation(hospId,saveList,shareLevelVO,costShareIdGroup.get(shareLevelVO.getId()));
            }
        }


        allocationQueryService.saveBatch(saveList);
    }

    private void setPreAllocation(Long hospId,  List<AllocationQuery> saveList,
                                  CostShareLevelVO shareLevelVO, List<CostCostingGroup> costingGroups) {
//        List<CostCostingGroup> costingGroups = costCostingGroupService.getRealData(CollUtil.newArrayList(maxId), hospId, year, month);
        costingGroups.forEach(i -> {
            AllocationQuery allocationQuery = new AllocationQuery();
            allocationQuery.setDateYear(i.getDateYear()).setDateMonth(i.getDateMonth())
                    .setHospId(hospId).setResponsibilityCode(i.getResponsibilityCode()).setResponsibilityName(i.getResponsibilityName())
                    .setOriginId(i.getId()).setOriginType(1L).setAmount(i.getAmount())
                    .setAccountingCode(i.getAccountCode()).setAccountingName(i.getAccountName())
                    .setCreateTime(System.currentTimeMillis())
                    .setLevelSort(shareLevelVO.getLeverSort()).setLevelName(shareLevelVO.getShareName())
                    .setTargetResponsibilityCode(i.getResponsibilityCode())
                    .setTargetResponsibilityName(i.getResponsibilityName())
                    .setShareLevelId(0L);
            saveList.add(allocationQuery);
        });
    }

    private void setAfterAllocation(List<Allocation> list, Long hospId, List<AllocationQuery> saveList, Map<Long,
            CostAccountShare> accountShareMap) {
        list.forEach(i -> {
            Long accountShareId = i.getAccountShareId();
            CostAccountShare byId = accountShareMap.get(accountShareId);
            if (Objects.isNull(byId)) {
                return;
            }
            String accountingCodes = byId.getAccountingCodes();
            if (StrUtil.isBlank(accountingCodes)) {
                return;
            }
            String accountingNames = byId.getAccountingNames();
            String alias = byId.getAlias();
            if (StrUtil.isNotBlank(alias)) {
                accountingNames = alias;
            }
            AllocationQuery allocationQuery = new AllocationQuery();
            allocationQuery.setDateYear(i.getDateYear()).setDateMonth(i.getDateMonth())
                    .setHospId(hospId).setResponsibilityCode(i.getResponsibilityCode())
                    .setResponsibilityName(i.getResponsibilityName())
                    .setOriginId(i.getId()).setOriginType(2L).setAmount(i.getAmount())
                    .setAccountingCode(accountingCodes).setAccountingName(accountingNames)
                    .setCreateTime(System.currentTimeMillis())
                    .setLevelSort(i.getLevelSort()).setLevelName(i.getLevelName())
                    .setTargetResponsibilityCode(i.getTargetResponsibilityCode())
                    .setTargetResponsibilityName(i.getTargetResponsibilityName())
                    .setShareLevelId(i.getShareLevelId());
            saveList.add(allocationQuery);
        });
    }

    /**
     * 得到目标月成本分摊参数值数据
     *
     * @param targetResponsibilities
     * @param map
     * @return
     */
    private List<ShareParamValue> getTarget(List<Responsibility> targetResponsibilities, AccountShareVO accountShareVO, Map<String, List<ShareParamValue>> map) {
        // 目标的责任中心
        List<ShareParamValue> shareParamValues = map.entrySet().stream().map(Map.Entry::getValue).flatMap(Collection::stream).collect(Collectors.toList());
        List<String> originRespCodes = targetResponsibilities.stream().map(Responsibility::getResponsibilityCode).collect(Collectors.toList());
        return shareParamValues.stream().filter(j -> originRespCodes.contains(j.getResponsibilityCode()))
                .filter(i -> i.getShareParamCode().equals(accountShareVO.getShareParamCode())).collect(Collectors.toList());
    }

    /**
     * 计算本次的成本金额
     *
     * @return
     */
    private BigDecimal getCostAmount(CostAccountShare accountShare, Integer calcType, Map<String, List<CostCostingGroup>> map, List<Allocation> costList) {
        // 是否包含分摊成本 0不包含 1 包含
        Integer isShareCost = accountShare.getIsShareCost();
        String accountingCodes = accountShare.getAccountingCodes();
        String responsibilityCode = accountShare.getResponsibilityCode();
        List<CostCostingGroup> costingGroups = map.get(responsibilityCode);
        if (CollUtil.isEmpty(costingGroups)) {
            List<Allocation> all = costList.stream().filter(i -> i.getTargetResponsibilityCode().equals(responsibilityCode)).collect(Collectors.toList());

            if (CollUtil.isEmpty(all) || isShareCost == 0) {
                return BigDecimal.ZERO;
            }
            BigDecimal reduce = all.stream().map(Allocation::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            return reduce;
        } else {
            BigDecimal costAmount = BigDecimal.ZERO;
            List<Allocation> all = new ArrayList<>();
            if (!costList.isEmpty()) {
                all = costList.stream().filter(i -> i.getTargetResponsibilityCode().equals(responsibilityCode)).collect(Collectors.toList());
            }
            // 计算方式 0是合并计算  1是分开计算
            if (calcType == 0) {
                costAmount = costingGroups.stream().map(CostCostingGroup::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (!all.isEmpty()) {

                    BigDecimal bigDecimal = all.stream()
                            .map(Allocation::getAmount)
                            .reduce(BigDecimal.ZERO, BigDecimal::add);

                    if (Objects.nonNull(bigDecimal)) {
                        costAmount = costAmount.add(bigDecimal);
                    }
                }

            } else {
                if (StrUtil.isBlank(accountingCodes)) {
                    return BigDecimal.ZERO;
                }

                ArrayList<String> accountCodes = CollUtil.newArrayList(accountingCodes.split(StrUtil.COMMA));
                List<CostCostingGroup> costGroups = costingGroups.stream().filter(i -> accountCodes.contains(i.getAccountCode())).collect(Collectors.toList());
                costAmount = costGroups.stream().map(CostCostingGroup::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (isShareCost == 1) {
                    if (!all.isEmpty()) {
                        BigDecimal bigDecimal = all.stream()
                                .map(Allocation::getAmount)
                                .reduce(BigDecimal.ZERO, BigDecimal::add);
                        if (Objects.nonNull(bigDecimal)) {
                            costAmount = costAmount.add(bigDecimal);
                        }
                    }
                }
            }

            return costAmount;
        }
    }


    /**
     * 通过目标层级获取目标责任中心
     *
     * @param targetLevel 目标层级 2,3,4
     * @param leverSort
     * @return List
     */
    private List<Responsibility> getTargetResponsibility(String targetLevel, Long hospId, Integer leverSort) {
        ArrayList<String> targetLevelList = CollUtil.newArrayList(StrUtil.split(targetLevel, StrUtil.COMMA));
        if (targetLevelList.size() == 1) {
            if (leverSort.equals(Integer.parseInt(targetLevelList.get(0)))) {
                return new ArrayList<>();
            }
        }
        List<CostShareLevel> shareLevels = shareLevelService.getListByLevelSort(targetLevelList, hospId);
        if (shareLevels.isEmpty()) {
            throw new CostException("请重新设置分摊层级");
        }
        List<Long> shareLevelIds = shareLevels.stream().map(CostShareLevel::getId).collect(Collectors.toList());
        return responsibilityService.getByLevelIds(shareLevelIds, hospId);
    }


    /**
     * 分摊后查询列表
     *
     * @param year               年月 （yyyy-MM-dd)
     * @param responsibilityCode 责任中心代码
     * @param current            当前页
     * @param pageSize           当前页展示的数据大小
     * @param hospId             医院id
     * @return PageUtils
     */
    @Override
    public PageUtils queryAfterAllocation(String year, String responsibilityCode, Integer current, Integer pageSize, Long hospId) {
        Integer dateYear = null;
        Integer dateMonth = null;
        if (StrUtil.isNotBlank(year)) {
            DateTime dateTime = DateUtil.parseDate(year);
            dateYear = DateUtil.year(dateTime);
            dateMonth = DateUtil.month(dateTime) + 1;
        }
        Integer startIndex = (current - 1) * pageSize;
        List<AfterAllocationVO> list = baseMapper.queryAfterAllocationList(dateYear, dateMonth, responsibilityCode, startIndex, pageSize, hospId);
        int totalCount = baseMapper.queryAfterAllocationListCount(dateYear, dateMonth, responsibilityCode, hospId);
        BigDecimal sum = baseMapper.queryAfterAllocationListSum(dateYear, dateMonth, responsibilityCode, hospId);
        return new PageUtils(list, totalCount, pageSize, current, sum);
    }


    /**
     * 分摊报表导出
     *
     * @param writer       {@link ExcelWriter}
     * @param levelSort    分摊层级  就是第几次分摊
     * @param sheet        报表
     * @param year         年
     * @param month        月
     * @param shareLevelId
     * @return
     */
    @Override
    public ExcelWriter getShareReportTemplate(ExcelWriter writer, Integer levelSort, Sheet sheet, Integer year, Integer month, Long shareLevelId) {
        // 获取数据
        Map<String,List<String>> targetTotalMoneys = new HashMap<>();
        List<AllocationReportVO> allocationReportVOList = getAllocationReportVOS(levelSort, year, month, shareLevelId,targetTotalMoneys);
        // 设置导出
        Map<String, List<AllocationReportVO>> responsibilityMap = allocationReportVOList.stream().collect(Collectors.groupingBy(AllocationReportVO::getResponsibilityCode));
        Map<String, List<AllocationReportVO>> targetResponsibilityMap = allocationReportVOList.stream().collect(Collectors.groupingBy(AllocationReportVO::getTargetResponsibilityCode));
        // 以会计科目查
        Map<String, AllocationReportVO> allAccMap = allocationReportVOList.stream().collect(Collectors.toMap(k -> k.getResponsibilityName() + k.getAccountName() + k.getTargetResponsibilityName() + k.getShareParamName(), synOne -> synOne));
        // 用别名查 过滤别名不为空的
        Map<String, AllocationReportVO> allAliMap = allocationReportVOList.stream().filter(i -> StrUtil.isNotBlank(i.getAlias())).collect(Collectors.toMap(k -> k.getResponsibilityName() + k.getAlias() + k.getTargetResponsibilityName() + k.getShareParamName(), synOne -> synOne));
//        Map<String, AllocationReportVO> allAliMap = allocationReportVOList.stream().collect(Collectors.toMap(k -> k.getResponsibilityName() + k.getAlias() + k.getTargetResponsibilityName() + k.getShareParamName(), synOne -> synOne));
        // 当前责任中心下面有几个会计科目  后面进行合并使用
        final int[] numResponsibility = new int[1];
        levelSort=levelSort+1;
//        // 从第几列开始编写数据
        final int[] column = {levelSort + 3};
        responsibilityMap.forEach((key,value)->{
            Map<String, AllocationReportVO> linkedHashMap = new LinkedHashMap<>();
            value.forEach(i -> {
                String s = i.getResponsibilityCode() + i.getAccountCode();
                if (!linkedHashMap.containsKey(s)) {
                    linkedHashMap.put(s, i);
                }
            });
            numResponsibility[0] = linkedHashMap.size();
            if (numResponsibility[0] >= NumberConstant.TWO) {
                Set<String> strings = linkedHashMap.keySet();
                for (String s : strings) {
                    AllocationReportVO allocationReportVO = linkedHashMap.get(s);
                    if (StrUtil.isBlank(allocationReportVO.getAlias())) {
                        writer.writeCellValue(column[0], 0, allocationReportVO.getResponsibilityName());
                        // 别名不存在
                        writer.writeCellValue(column[0], 1, allocationReportVO.getAccountName());
                    } else {
                        // 不为空 设置别名
                        writer.writeCellValue(column[0], 0, allocationReportVO.getResponsibilityName());
                        writer.writeCellValue(column[0], 1, allocationReportVO.getAlias());
                    }
                    writer.writeCellValue(column[0], 2, allocationReportVO.getTotalAmounts());
                    column[0]++;
                }
            } else {
                // 不需要合并单元格
                writer.writeCellValue(column[0], 0, value.get(0).getResponsibilityName());
                if (StrUtil.isNotBlank(value.get(0).getAlias())) {
                    writer.writeCellValue(column[0], 1, value.get(0).getAlias());
                } else {
                    writer.writeCellValue(column[0], 1, value.get(0).getAccountName());
                }
                writer.writeCellValue(column[0], 2, value.get(0).getTotalAmounts());
                column[0]++;
            }
        });
//        for (String key : keySet) {
//            List<AllocationReportVO> allocationReportVOS = responsibilityMap.get(key);
//            Map<String, AllocationReportVO> linkedHashMap = new LinkedHashMap<>();
//            allocationReportVOS.forEach(i -> {
//                String s = i.getResponsibilityCode() + i.getAccountCode();
//                if (!linkedHashMap.containsKey(s)) {
//                    linkedHashMap.put(s, i);
//                }
//            });
//            numResponsibility[0] = linkedHashMap.size();
//            if (numResponsibility[0] >= NumberConstant.TWO) {
//                Set<String> strings = linkedHashMap.keySet();
//                for (String s : strings) {
//                    AllocationReportVO allocationReportVO = linkedHashMap.get(s);
//                    if (StrUtil.isBlank(allocationReportVO.getAlias())) {
//                        writer.writeCellValue(column[0], 0, allocationReportVO.getResponsibilityName());
//                        // 别名不存在
//                        writer.writeCellValue(column[0], 1, allocationReportVO.getAccountName());
//                    } else {
//                        // 不为空 设置别名
//                        writer.writeCellValue(column[0], 0, allocationReportVO.getResponsibilityName());
//                        writer.writeCellValue(column[0], 1, allocationReportVO.getAlias());
//                    }
//                    writer.writeCellValue(column[0], 2, allocationReportVO.getTotalAmounts());
//                    column[0]++;
//                }
//            } else {
//                // 不需要合并单元格
//                writer.writeCellValue(column[0], 0, allocationReportVOS.get(0).getResponsibilityName());
//                if (StrUtil.isNotBlank(allocationReportVOS.get(0).getAlias())) {
//                    writer.writeCellValue(column[0], 1, allocationReportVOS.get(0).getAlias());
//                } else {
//                    writer.writeCellValue(column[0], 1, allocationReportVOS.get(0).getAccountName());
//                }
//                writer.writeCellValue(column[0], 2, allocationReportVOS.get(0).getTotalAmounts());
//                column[0]++;
//            }
//        }
        // 设置单元格合并
        for (int j = 1; j < levelSort; j++) {
            writer.merge(0, 1, j, j, "第" + j + "次分摊", false);
        }
        // 目标责任集合
        writer.passCurrentRow();
        // 从第三行开始
        int num = 3;
        Set<String> targetSet = targetResponsibilityMap.keySet();
        int t = 0;
        for (String target : targetSet) {
            List<AllocationReportVO> allocationReportVOS = targetResponsibilityMap.get(target);
            Map<String, AllocationReportVO> linkedHashMap = new LinkedHashMap<>();
            allocationReportVOS.forEach(i -> {
                String s = i.getTargetResponsibilityCode() + i.getShareParamName();
                if (!linkedHashMap.containsKey(s)) {
                    linkedHashMap.put(s, i);
                }
            });
            int shareParamSize = linkedHashMap.size();
            if (shareParamSize >= NumberConstant.TWO) {
                // 责任中心
                AllocationReportVO costCostingVO = allocationReportVOS.get(0);
                // 设置第几次分摊的值
                List<String> targetShareMoneyList = targetTotalMoneys.get(target);
//                for (int k = 0; k < levelSort - 1; k++) {
//                    writer.merge(num, num + shareParamSize - 1, k + 1, k + 1, costCostingVO.getTargetShareMoneys().get(t++), false);
//
//                }
                for (int k = 0; k < levelSort - 1; k++) {
                    Object allocationAmount = getAllocationAmount(levelSort, k, allocationReportVOS, targetShareMoneyList);
                    writer.merge(num, num + shareParamSize - 1, k + 1, k + 1, allocationAmount, false);
                }
//                if(!CollectionUtils.isEmpty(targetShareMoneyList)){
//                    for (int k = 0; k < levelSort - 1; k++) {
//                        if(k==levelSort-2){
//                            BigDecimal totalAmount = allocationReportVOS.stream().map(AllocationReportVO::getAmount)
//                                    .reduce(BigDecimal.ZERO, BigDecimal::add);
//                            writer.merge(num, num + shareParamSize - 1, k + 1, k + 1, totalAmount, false);
//                        }else {
//                            writer.merge(num, num + shareParamSize - 1, k + 1, k + 1, targetShareMoneyList.get(k), false);
//                        }
//                    }
//                }else {
//                    for (int k = 0; k < levelSort - 1; k++) {
//                        if(k==levelSort-2){
//                            BigDecimal totalAmount = allocationReportVOS.stream().map(AllocationReportVO::getAmount)
//                                    .reduce(BigDecimal.ZERO, BigDecimal::add);
//                            writer.merge(num, num + shareParamSize - 1, k + 1, k + 1, totalAmount, false);
//                        }else {
//                            writer.merge(num, num + shareParamSize - 1, k + 1, k + 1, "0.0000", false);
//                        }
//                    }
//                }

                // 设置对应的分摊参数值
                Set<String> strings = linkedHashMap.keySet();
                for (String s : strings) {
                    AllocationReportVO allocationReportVO = linkedHashMap.get(s);
                    writer.writeCellValue(0, num, allocationReportVO.getTargetResponsibilityName());
                    writer.writeCellValue(levelSort, num, allocationReportVO.getShareParamName());
                    writer.writeCellValue(levelSort + 1, num, allocationReportVO.getShareParamValueNums());
                    writer.writeCellValue(levelSort + 2, num, allocationReportVO.getShareParamRates());
                    for (int m = levelSort + 3; m < column[0]; m++) {
                        // x是m y是num
                        // 获取当前这一列对应的责任中心 以及会计科目
                        // 第一行责任中心
                        String responsibilityName = sheet.getRow(0).getCell(m).getStringCellValue();
                        // 第二行的会计科目或者
                        String accountNameOrAlias = sheet.getRow(1).getCell(m).getStringCellValue();
                        // 这一行的目标责任中心
                        String otherResponsibilityName = sheet.getRow(num).getCell(0).getStringCellValue();
                        // 分摊参数
                        String shareName = sheet.getRow(num).getCell(levelSort).getStringCellValue();
                        String sss = responsibilityName + accountNameOrAlias + otherResponsibilityName + shareName;
                        AllocationReportVO vo = allAliMap.get(sss);
                        AllocationReportVO allocationReportVO2 = allAccMap.get(responsibilityName + accountNameOrAlias + otherResponsibilityName + shareName);
                        AllocationReportVO allocationReportVO3 = allAliMap.get(responsibilityName + accountNameOrAlias + otherResponsibilityName + shareName);

                        if (Objects.nonNull(allocationReportVO2)) {
                            writer.writeCellValue(m, num, allocationReportVO2.getAmounts());
                        } else if (Objects.nonNull(allocationReportVO3)) {
                            writer.writeCellValue(m, num, allocationReportVO3.getAmounts());
                        } else {
                            writer.writeCellValue(m, num, 0);
                        }
                    }
                    num++;
                }
            }
            if (shareParamSize < NumberConstant.TWO) {
                writer.writeCellValue(0, num, allocationReportVOS.get(0).getTargetResponsibilityName());
//                for (int k = 0; k < levelSort - 1; k++) {
//                    writer.writeCellValue(k + 1, num, allocationReportVOS.get(0).getTargetShareMoneys().get(k));
//                }
                List<String> targetShareMoneyList = targetTotalMoneys.get(target);

                for (int k = 0; k < levelSort - 1; k++) {
                    Object allocationAmount = getAllocationAmount(levelSort, k, allocationReportVOS, targetShareMoneyList);
                    writer.writeCellValue(k + 1, num, allocationAmount);
                }

//                if(!CollectionUtils.isEmpty(targetShareMoneyList)){
//                    for (int k = 0; k < levelSort - 1; k++) {
//                        if(k==levelSort-2){
//                            BigDecimal totalAmount = allocationReportVOS.stream().map(AllocationReportVO::getAmount)
//                                    .reduce(BigDecimal.ZERO, BigDecimal::add);
//                            writer.writeCellValue(k + 1, num, totalAmount);
//                        }else {
//                            writer.writeCellValue(k + 1, num, targetShareMoneyList.get(k));
//                        }
//                    }
//                }else {
//                    for (int k = 0; k < levelSort - 1; k++) {
//                        if(k==levelSort-2){
//                            BigDecimal totalAmount = allocationReportVOS.stream().map(AllocationReportVO::getAmount)
//                                    .reduce(BigDecimal.ZERO, BigDecimal::add);
//                            writer.writeCellValue(k + 1, num, totalAmount);
//                        }else {
//                            writer.writeCellValue(k + 1, num, "0.0000");
//                        }
//                    }
//                }

                writer.writeCellValue(levelSort, num, allocationReportVOS.get(0).getShareParamName());
                writer.writeCellValue(levelSort + 1, num, allocationReportVOS.get(0).getShareParamValueNums());
                writer.writeCellValue(levelSort + 2, num, allocationReportVOS.get(0).getShareParamRates());
                for (int m = levelSort + 3; m < column[0]; m++) {
                    // x是m y是num
                    // 获取当前这一列对应的责任中心 以及会计科目
                    // 第一行责任中心
                    String responsibilityName = sheet.getRow(0).getCell(m).getStringCellValue();
                    // 第二行的会计科目或者
                    String accountNameOrAlias = sheet.getRow(1).getCell(m).getStringCellValue();
                    // 这一行的目标责任中心
                    String otherResponsibilityName = sheet.getRow(num).getCell(0).getStringCellValue();
                    // 分摊参数
                    String shareName = sheet.getRow(num).getCell(levelSort).getStringCellValue();
                    AllocationReportVO allocationReportVO2 = allAccMap.get(responsibilityName + accountNameOrAlias + otherResponsibilityName + shareName);
                    AllocationReportVO allocationReportVO3 = allAliMap.get(responsibilityName + accountNameOrAlias + otherResponsibilityName + shareName);
                    if (Objects.nonNull(allocationReportVO2)) {
                        writer.writeCellValue(m, num, allocationReportVO2.getAmounts());
                    } else if (Objects.nonNull(allocationReportVO3)) {
                        writer.writeCellValue(m, num, allocationReportVO3.getAmounts());
                    } else {
                        writer.writeCellValue(m, num, 0);
                    }
                }
                num++;
            }
        }
        Map<String, Integer> rowMap = new HashMap<>();
        int cc = levelSort + 3;
        // 合并行
        boolean otherData = false;
        for (int m = levelSort + 3; m < column[0] - 1; m++) {
//            String cellValue = sheet.getRow(0).getCell(m).getStringCellValue();
//            if (!rowMap.containsKey(cellValue)){
//                rowMap.put(cellValue,m);
//                Integer integer = rowMap.get(sheet.getRow(0).getCell(m).getStringCellValue());
//                String value = sheet.getRow(0).getCell(m ).getStringCellValue();
//                writer.merge(0, 0, rowMap.get(sheet.getRow(0).getCell(m-1).getStringCellValue()), m-1, sheet.getRow(0).getCell(m-1).getStringCellValue(), false);
//            }
            String cellValue1 = sheet.getRow(0).getCell(m).getStringCellValue();
            String cellValue2 = sheet.getRow(0).getCell(m + 1).getStringCellValue();
            if (!cellValue1.equals(cellValue2)) {
                if (cc != m) {
                    writer.merge(0, 0, cc, m, cellValue1, false);
                } else {
                    writer.writeCellValue(cc, 0, cellValue1);
                }

                cc = m + 1;
            } else if (m == column[0] - 2) {
                writer.merge(0, 0, cc, m + 1, sheet.getRow(0).getCell(m + 1).getStringCellValue(), false);
            }
        }
        // 合并列
        int jj = 3;

        for (int i = 3; i < num - 1; i++) {
            String cellValue1 = sheet.getRow(i).getCell(0).getStringCellValue();
            String cellValue2 = sheet.getRow(i + 1).getCell(0).getStringCellValue();
            if (!cellValue1.equals(cellValue2)) {
                if (jj != i) {
                    writer.merge(jj, i, 0, 0, cellValue1, false);
                } else {
                    writer.writeCellValue(0, jj, cellValue1);
                }
                jj = i + 1;
            } else if (i == num - 2) {
                writer.merge(jj, i + 1, 0, 0, cellValue1, false);
            }
        }
        for (int i = 0; i < 30; i++) {
            // 调整每一列宽度
            sheet.autoSizeColumn((short) i);
            // 解决自动设置列宽中文失效的问题
            sheet.setColumnWidth(i, sheet.getColumnWidth(i) * 14 / 10);
        }
        return writer;
    }

    /**
     * 获取责任中心对应层级的分摊金额
     * @param levelSort
     * @param index
     * @param allocationReportVOS
     * @param targetShareMoneyList
     * @return
     */
    public Object getAllocationAmount(int levelSort,int index,List<AllocationReportVO> allocationReportVOS,List<String> targetShareMoneyList){
        //当前层级取分摊金额加总
        if(index==levelSort-2){
            BigDecimal totalAmount = allocationReportVOS.stream().map(AllocationReportVO::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            return totalAmount;
        }
        //取不到对应层级的分摊总金额
        if(CollectionUtils.isEmpty(targetShareMoneyList)||targetShareMoneyList.size()<=index){
            return  BigDecimal.valueOf(NumberConstant.ZERO,NumberConstant.FOUR);
        }
        //取对应层级的分摊总金额
        return targetShareMoneyList.get(index);
    }

    /**
     * 查询数据
     *
     * @param levelSort
     * @param year
     * @param month
     * @param targetTotalMoneys
     * @return
     */
    private List<AllocationReportVO> getAllocationReportVOS(Integer levelSort, Integer year, Integer month, Long shareLevelId,
                                                            Map<String, List<String>> targetTotalMoneys) {
        Long hospId = UserContext.getCurrentLoginHospId();
        if (Objects.isNull(levelSort) || Objects.isNull(year) || Objects.isNull(month)) {
            throw new CostException(500, "参数异常");
        }
        // 处理第几次分摊计算值
//        List<Allocation> allocationList = baseMapper.selectList(new QueryWrapper<Allocation>().lambda()
//                .eq(Allocation::getHospId, hospId).eq(Allocation::getLevelSort, levelSort).eq(Allocation::getShareLevelId, shareLevelId).eq(Allocation::getDateYear, year)
//                .eq(Allocation::getDateMonth, month));
        List<Allocation> allocationList =baseMapper.getLevelSortAllocation(hospId,year,month,shareLevelId);
        // 找会计科室的时候使用的
        List<Allocation> allocations = baseMapper.selectList(new QueryWrapper<Allocation>().lambda()
                .eq(Allocation::getHospId, hospId).eq(Allocation::getDateYear, year)
                .eq(Allocation::getDateMonth, month));
        Map<String, List<Allocation>> accrepMap = allocations.stream().collect(Collectors.groupingBy(k -> k.getLevelSort() + "cost" + k.getTargetResponsibilityCode()));
        LinkedList<String> shareMoney = new LinkedList<>();

        List<CostAccountShare> costAccountShareList = accountShareService.list(new QueryWrapper<CostAccountShare>().lambda()
                .eq(CostAccountShare::getHospId, hospId));
        Map<Long, CostAccountShare> accountShareMap = costAccountShareList.stream().collect(Collectors.toMap(CostAccountShare::getId, synOne -> synOne));
        List<AllocationReportVO> allocationReportVOList = BeanUtil.convertList(allocationList, AllocationReportVO.class);
        List<CostShareParamVO> shareParamVOList = shareParamService.getAll(hospId);
        Map<String,Integer> shareParamStatusMap = shareParamVOList.stream().collect(Collectors.toMap(CostShareParamVO::getShareParamCode, paramVO -> paramVO.getStatus() == null ? NumberConstant.ONE : paramVO.getStatus(), (a, b) -> b));
        List<AllocationReportVO> removeList = new ArrayList<>();
        // 设置会计科目的
        allocationReportVOList.forEach(i -> {

            Long accountShareId = i.getAccountShareId();
            CostAccountShare costAccountShare = accountShareMap.get(accountShareId);
            if (Objects.isNull(costAccountShare)) {
                throw new CostException(500, "成本参数参数设置对应不存在");
            }
            i.setAccountCode(costAccountShare.getAccountingCodes());
            i.setAccountName(costAccountShare.getAccountingNames());
            i.setAlias(costAccountShare.getAlias());
            if (levelSort > 1) {
                List<String> targetMoney = new ArrayList<>();
                for (int j = 1; j < levelSort; j++) {
                    // 每一次计算要设置为0
                    AtomicReference<BigDecimal> money = new AtomicReference<>(new BigDecimal("0.0000"));

                    List<Allocation> allocations1 = accrepMap.get(j + "cost" + i.getTargetResponsibilityCode());
                    if (CollUtil.isNotEmpty(allocations1)) {
                        allocations1.forEach(m -> {
                            money.updateAndGet(v -> v.add(m.getAmount()));
//                            System.out.println(m.getAmount());
                        });
                    } else {
                        // TODO 封装测试数据
                        shareMoney.add("0");
                    }
//                    System.out.println("第"+j+"次"+money);
                    shareMoney.add(money.toString());
                    targetMoney.add(money.toString());
                }
                if(!CollectionUtils.isEmpty(targetTotalMoneys)){
                    List<String> strings = targetTotalMoneys.get(i.getTargetResponsibilityCode());
                    if(CollectionUtils.isEmpty(strings)){
                        targetTotalMoneys.put(i.getTargetResponsibilityCode(),targetMoney);
                    }
                }else {
                    targetTotalMoneys.put(i.getTargetResponsibilityCode(),targetMoney);

                }
            }
            i.setTargetShareMoneys(shareMoney);
            // 设置字符串类型数据
            i.setTotalAmounts(i.getTotalAmount().toString());
            i.setShareParamValueNums(i.getShareParamValueNum().toString());
            i.setShareParamRates(i.getShareParamRate().toString());
            i.setAmounts(i.getAmount().toString());
            //校验是否有分摊参数未启用
            Integer status = shareParamStatusMap.get(i.getShareParamCode());
            if(status != null&&status.equals(NumberConstant.ZERO)){
                removeList.add(i);
            }
        });
        //校验是否有分摊参数未启用 未启用分摊参数 移除

        if(!CollectionUtils.isEmpty(removeList)){
            allocationReportVOList.removeAll(removeList);
        }
        return allocationReportVOList;
    }

    /**
     * 分摊后报表
     *
     * @param year               年月（yyyy-MM-dd）
     * @param responsibilityCode 责任中心代码
     * @param hospId             医院id
     * @return List
     */
    @Override
    public CollectDataFormVO queryAfterAllocationForm(String year, String responsibilityCode, Long hospId) {
        DateTime dateTime = DateUtil.parseDate(year);
        int dateYear = DateUtil.year(dateTime);
        int month = DateUtil.month(dateTime) + 1;
        List<CodeAndNameVO> responsibilityCodeAndNames = allocationQueryService.getRespCodeAndName(hospId, dateYear, month);
        List<CodeAndNameVO> accountCodeAndNames = allocationQueryService.getAccountCodeAndName(hospId, dateYear, month);
        // todo 校验两个List是否为空
        // 填充
        responsibilityCodeAndNames.add(0, new CodeAndNameVO("#", "#"));
        responsibilityCodeAndNames.add(responsibilityCodeAndNames.size(), new CodeAndNameVO("合计", "合计"));

        List<String> titleData = responsibilityCodeAndNames.stream().map(CodeAndNameVO::getName).collect(Collectors.toList());
        List<String> respCodes = responsibilityCodeAndNames.stream().map(CodeAndNameVO::getCode).collect(Collectors.toList());
        Map<Integer, String> titleMap = new HashMap<>();
        for (int i = 0; i < titleData.size(); i++) {
            titleMap.put(i + 1, titleData.get(i));
        }

        List<Map<Integer, Object>> realDatas = new ArrayList<>();
        List<String> accountCodes = accountCodeAndNames.stream().map(CodeAndNameVO::getCode).collect(Collectors.toList());
        for (CodeAndNameVO account : accountCodeAndNames) {
            Map<Integer, Object> map = new HashMap<>();
            for (int i = 0; i < responsibilityCodeAndNames.size(); i++) {
                if (i == 0) {
                    map.put(i + 1, account.getName());
                    continue;
                } else if (i == responsibilityCodeAndNames.size() - 1) {
                    // todo 计算最右侧合计的钱
                    BigDecimal amount = allocationQueryService.getTotalByAccountAndResps(hospId, dateYear, month, account.getCode(), respCodes);
                    map.put(i + 1, amount);
                    continue;
                }
                // TODO: 2021/8/26 计算 中间的钱
                BigDecimal amount = allocationQueryService.getTotalByAccountAndRespCode(hospId, dateYear, month, account.getCode(), respCodes.get(i));
                map.put(i + 1, amount);

            }
            realDatas.add(map);
        }

        // 尾栏计算
        Map<Integer, Object> map = new HashMap<>();
        for (int i = 0; i < titleData.size(); i++) {
            if (i == 0) {
                map.put(i + 1, "合计");
                continue;
            } else if (i == titleData.size() - 1) {
                // TODO: 2021/8/26 计算
                BigDecimal bigDecimal = allocationQueryService.getTotalMoney(dateYear, month, hospId);
                map.put(i + 1, bigDecimal);
                continue;
            }
            BigDecimal bigDecimal = allocationQueryService.getCountByRespAndAccounts(hospId, dateYear, month, responsibilityCodeAndNames.get(i).getCode(), accountCodes);
            map.put(i + 1, bigDecimal);
        }
        return new CollectDataFormVO(titleMap, realDatas, map);
    }

    /**
     * 分摊后报表输出
     *
     * @param date   yyyy-MM-dd
     * @param hospId 医院id
     * @return List
     */
    @Override
    public List<AfterAllocationFormVO> afterAllocationFormList(String date, Long hospId) {

        DateTime parse = DateUtil.parse(date);
        int year = DateUtil.year(parse);
        int month = DateUtil.month(parse) + 1;
        // 得到这个月的分摊过的分摊层级
        List<Allocation> list = baseMapper.getAllSortLevel(hospId, year, month);
        Map<Long, CostShareLevel> map = shareLevelService.list(new QueryWrapper<CostShareLevel>().lambda().eq(CostShareLevel::getHospId, hospId)).stream().collect(Collectors.toMap(CostShareLevel::getId, synOne -> synOne));
//        list.
        List<AfterAllocationFormVO> vos = list.stream().map(i -> {
            AfterAllocationFormVO vo = new AfterAllocationFormVO();
            vo.setId(i.getId());
            vo.setYear(i.getDateYear());
            vo.setMonth(i.getDateMonth());
            vo.setShareLevel(i.getLevelSort());
            vo.setShareReportName(map.get(i.getShareLevelId()).getShareName() + "分摊");
            vo.setShareTime(DateUtil.date(i.getCreateTime()));
            vo.setShareLevelId(i.getShareLevelId());
            return vo;
        }).collect(Collectors.toList());
        return vos;
    }

    /**
     * 按时间计算分摊数据
     *
     * @param year   年月
     * @param month  月
     * @param hospId
     * @return
     */
    @Override
    public List<Allocation> getByDate(int year, int month, Long hospId) {
        return this.list(
                new LambdaQueryWrapper<Allocation>()
                        .eq(Allocation::getDateYear, year)
                        .eq(Allocation::getDateMonth, month)
                        .eq(Allocation::getHospId, hospId)
        );
    }
}