package com.kcim.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ImmutableMap;
import com.kcim.common.constants.*;
import com.kcim.common.enums.*;
import com.kcim.common.exception.CostException;
import com.kcim.common.file.MinioConfig;
import com.kcim.common.file.MinioFileUtil;
import com.kcim.common.util.*;
import com.kcim.common.util.excel.ExcelPoiUtil;
import com.kcim.common.util.excel.entity.ColEntity;
import com.kcim.common.util.excel.entity.TitleEntity;
import com.kcim.dao.mapper.HospProfitAndLossMapper;
import com.kcim.dao.model.*;
import com.kcim.service.*;
import com.kcim.vo.*;
import com.kcim.web.reponse.BatchCostProfitResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.jetbrains.annotations.NotNull;
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 org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;

@Slf4j
@Service("hospProfitAndLossService")
public class HospProfitAndLossServiceImpl extends ServiceImpl<HospProfitAndLossMapper, HospProfitAndLoss> implements HospProfitAndLossService {

//    @Value("${file.filelocal}")
//    private String hospProfitReportUrl;
//
//    @Value("${file.serverUrl}")
//    private String serverUrl;

    private final ReportFormService reportFormService;
    private final IncomeCollectionService collectionService;
    private final AllocationQueryService allocationQueryService;
    private final AllocationService allocationService;
    private final ReportRelationService reportRelationService;
    private final CostShareLevelService shareLevelService;
    private final CostOtherPaymentsDataService otherPaymentsDataService;
    private final ResponsibilityService responsibilityService;
    private final CostAccountShareService accountShareService;
    private final FileRecordService fileRecordService;
    private final SqlService sqlService;

    private final CenterService centerService;

    private final MinioConfig minioConfig;

    private final MinioFileUtil minioFileUtil;


    public HospProfitAndLossServiceImpl(ReportFormService reportFormService,
                                        IncomeCollectionService collectionService,
                                        AllocationQueryService allocationQueryService,
                                        AllocationService allocationService, ReportRelationService reportRelationService,
                                        CostShareLevelService shareLevelService,
                                        CostOtherPaymentsDataService otherPaymentsDataService,
                                        ResponsibilityService responsibilityService,
                                        CostAccountShareService accountShareService,
                                        FileRecordService fileRecordService,
                                        MinioConfig minioConfig,
                                        MinioFileUtil minioFileUtil,
                                        SqlService sqlService,
                                        CenterService centerService
    ) {
        this.reportFormService = reportFormService;
        this.collectionService = collectionService;
        this.allocationQueryService = allocationQueryService;
        this.allocationService = allocationService;
        this.reportRelationService = reportRelationService;
        this.shareLevelService = shareLevelService;
        this.otherPaymentsDataService = otherPaymentsDataService;
        this.responsibilityService = responsibilityService;
        this.accountShareService = accountShareService;
        this.fileRecordService = fileRecordService;
        this.minioConfig = minioConfig;
        this.minioFileUtil = minioFileUtil;
        this.sqlService=sqlService;
        this.centerService=centerService;
    }


    /**
     * 计算全院损益
     *
     * @param date   yyyy-MM-dd 时间
     * @param hospId 医院id
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public void calc(String date, Long hospId) {
        reportFormService.checkExistLoss(hospId);

        DateTime parse = DateUtil.parse(date);
        int year = DateUtil.year(parse);
        int month = DateUtil.month(parse) + 1;
        this.remove(new LambdaQueryWrapper<HospProfitAndLoss>().eq(HospProfitAndLoss::getDateYear, year).eq(HospProfitAndLoss::getDateMonth, month).eq(HospProfitAndLoss::getHospId, hospId));
        // 得到全院损益计算报表
        List<ReportForm> reportForms = reportFormService.getListByReportType(hospId, ReportTypeEnum.HOSP_PROFIT_LOSS.getType());
        if (CollUtil.isEmpty(reportForms)) {
            throw new CostException("医院未设置全院损益计算报表");
        }

        // 得到这个月所有收入数据
        List<IncomeCollection> incomes = collectionService.getCollectionsByDate(year, month, hospId);
        if (incomes.isEmpty()) {
            throw new CostException("医院未归集本月收入数据");
        }

        // 得到这个月的所有成本数据
        List<AllocationQuery> allocationQueries = allocationQueryService.getAllByDate(hospId, year, month);
        if (allocationQueries.isEmpty()) {
            throw new CostException("医院未分摊本月数据");
        }
        List<HospProfitAndLoss> list = new ArrayList<>();
        List<ReportForm> parentForms = reportForms.stream().filter(i -> i.getParentId().equals(0L)).collect(Collectors.toList());
        for (ReportForm parentForm : parentForms) {
            Long parentId = parentForm.getId();
            List<ReportForm> children = reportForms.stream().filter(i -> i.getParentId().equals(parentId)).collect(Collectors.toList());
            for (ReportForm child : children) {
                Integer calcType = child.getCalcType();
                if (calcType == CalcTypeEnum.BY_ACCOUNT.getType()) {
                    // 按会计科目计算单的话
                    calcByAccount(hospId, child, incomes, list, allocationQueries, month, year);
                } else if (calcType == CalcTypeEnum.BY_SHARE_LEVEL.getType()) {
                    // 分摊层级计算
                    calcByShareLevel(hospId, child, list, year, month);
                } else if (calcType == CalcTypeEnum.LITTER_COUNT.getType()) {
                    // 处理小计 todo  默认认为 小计都是在同一个下面最后一个
                    calcByLitterCount(year, month, child, children, list, hospId);
                } else if (calcType == CalcTypeEnum.CALC_FORMULA.getType()) {
                    // 按公式 （要保证总合计放到最后呀）
                    calcByFormula(year, month, child, children, list);
                } else if (calcType == CalcTypeEnum.BY_RESPONSIBILITY.getType()) {
                    // 责任中心
                    calcByResponsibility(hospId, child, allocationQueries, list, year, month);
                } else if (calcType == CalcTypeEnum.NO_CONFIG.getType()) {
                    // 不设置不计算
                } else {

                }
            }
        }


        // 处理医院其他收支
        List<CostOtherPaymentsData> otherPaymentsDatas = otherPaymentsDataService.getByMonth(year, month, hospId);
        if (!otherPaymentsDatas.isEmpty()) {
            otherPaymentsDatas.forEach(ele -> {
                HospProfitAndLoss loss = new HospProfitAndLoss();
                loss.setDateYear(year).setDateMonth(month).setReportName(ele.getPaymentsName()).setReportNum(ele.getId().intValue())
                        .setCreateTime(System.currentTimeMillis()).setAmount(ele.getTotalAmount()).setHospId(hospId);
//                if (ele.getPaymentsType() == 2) {
//                    loss.setAmount(BigDecimal.ZERO.subtract(ele.getTotalAmount()));
//                }
                list.add(loss);
            });
        }
        this.saveBatch(list);
    }

    /**
     * 计算全院损益
     * @param date   yyyy-MM-dd 时间
     * @param hospId 医院id
     * @param reportType 报表类型
     */
    @Override
    public void calcHospProfit(String date, Long hospId, Integer reportType){
        //需要用代码计算全院损益时
        if(IsNeedCalc(reportType)){
            //计算全院损益
            calcHospProfitAction(date,hospId,reportType);
            //计算同环比
            handleSpecificMonthsCalculation(hospId, date, reportType);
        }
        execHospProfitSql(date,reportType);
    }
    @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
    public void calcHospProfitAction(String date, Long hospId, Integer reportType) {
        List<Responsibility> leafResp = responsibilityService.getLeafResp(hospId);
        DateTime parse = DateUtil.parse(date);
        int year = DateUtil.year(parse);
        int month = DateUtil.month(parse) + 1;
        this.remove(
                new LambdaQueryWrapper<HospProfitAndLoss>()
                        .eq(HospProfitAndLoss::getDateYear, year)
                        .eq(HospProfitAndLoss::getDateMonth, month)
                        .eq(HospProfitAndLoss::getHospId, hospId)
                        .eq(HospProfitAndLoss::getReportType, reportType)
        );
        // 得到全院损益计算报表
        List<ReportForm> reportForms = reportFormService.getListByReportType(hospId, reportType);
        if (CollUtil.isEmpty(reportForms)) {
            throw new CostException("医院未设置全院损益计算报表");
        }
        reportFormService.checkExistLoss(hospId,reportType);
        // 得到这个月所有收入数据
        List<IncomeCollection> incomes = collectionService.getCollectionsByDate(year, month, hospId);
        if (incomes.isEmpty()) {
            throw new CostException("医院未归集本月收入数据");
        }
        Map<Long, List<ReportRelation>> reportRelationMap = reportRelationService.list(new QueryWrapper<ReportRelation>().lambda().eq(ReportRelation::getHospId, hospId)).stream().collect(Collectors.groupingBy(ReportRelation::getReportId));

        // 得到这个月的所有成本数据
        List<AllocationQuery> allocationQueries = allocationQueryService.getAllByDate(hospId, year, month);
        if (allocationQueries.isEmpty()) {
            throw new CostException("医院未分摊本月数据");
        }
        List<AllocationQueryReportVO> allocationQueryReportVOList = BeanUtil.convertList(allocationQueries, AllocationQueryReportVO.class);
        allocationQueryReportVOList.forEach(i -> {
            i.setAccountingCodes(Arrays.asList(i.getAccountingCode().split(StrUtil.COMMA)));
            i.setAccountingNames(Arrays.asList(i.getAccountingName().split(StrUtil.COMMA)));
        });
        // 查询分摊的报表数据 后面的计算方式需要使用 小计等需要使用
        List<Allocation> allocationList = allocationService.list(new QueryWrapper<Allocation>().lambda().eq(Allocation::getHospId, hospId)
                .eq(year > 0, Allocation::getDateYear, year).eq(month > 0, Allocation::getDateMonth, month));
        if (CollUtil.isEmpty(allocationList)) {
            throw new CostException(500, "分摊报表数据不存在");
        }
        // 查询最后一个层级的责任中心
        List<CostShareLevel> costShareLevelList = shareLevelService.list(new QueryWrapper<CostShareLevel>().lambda()
                .eq(CostShareLevel::getHospId, hospId).orderByDesc(CostShareLevel::getLeverSort));
        if (CollUtil.isEmpty(costShareLevelList)) {
            throw new CostException(500, "分摊层级未设置");
        }
        List<HospProfitAndLossVo> listVo = new ArrayList<>();
//        List<ReportForm> parentForms = reportForms.stream().filter(i -> i.getParentId().equals(0L)).collect(Collectors.toList());
        int finalYear = year;
        int finalMonth = month;
        leafResp.forEach(i -> {
            reportForms.forEach(j -> {
                HospProfitAndLossVo hospProfitAndLossVo = new HospProfitAndLossVo();
                hospProfitAndLossVo.setDateYear(finalYear);
                hospProfitAndLossVo.setDateMonth(finalMonth);
                hospProfitAndLossVo.setReportNum(j.getNum());
                hospProfitAndLossVo.setReportName(j.getReportName());
                hospProfitAndLossVo.setResponsibilityCode(i.getResponsibilityCode());
                hospProfitAndLossVo.setResponsibilityName(i.getResponsibilityName());
                hospProfitAndLossVo.setCostType(NumberConstant.ZERO);
                hospProfitAndLossVo.setIncomeType(NumberConstant.ZERO);
                hospProfitAndLossVo.setHospId(hospId);
                hospProfitAndLossVo.setType(j.getCostType());
                hospProfitAndLossVo.setFraction(j.getFraction());
                hospProfitAndLossVo.setReportId(j.getId());
                hospProfitAndLossVo.setReportNum(j.getNum());
                hospProfitAndLossVo.setCalcType(j.getCalcType());
                hospProfitAndLossVo.setReportName(j.getReportName());
                hospProfitAndLossVo.setCalcFormula(j.getCalcFormula());
                hospProfitAndLossVo.setReportParentId(j.getParentId());
                listVo.add(hospProfitAndLossVo);
            });
        });

        Map<Long, List<HospProfitAndLossVo>> listMap = listVo.stream().collect(Collectors.groupingBy(HospProfitAndLossVo::getReportId));
        List<HospProfitAndLossVo> allList = BeanUtil.convertList(listVo, HospProfitAndLossVo.class);
        // 记录每一次计算的钱
        //所有小计 总计单独处理
        listVo.forEach(i -> {
            Integer calcType = i.getCalcType();
            if (NumberConstant.ONE.equals(calcType)) {
                // TODO 按照会计科目进行计算
                i.setAmount(setAccountReportData(i, incomes, allocationQueryReportVOList, reportRelationMap));
            } else if (NumberConstant.TWO.equals(calcType)) {
                // TODO 按照分摊层级进行计算
                i.setAmount(setShareLevelReportData(i, reportRelationMap, allocationList));
            } else if (NumberConstant.THREE.equals(calcType)) {
                // TODO 按照小计进行计算
                i.setAmount(setSubtotal(i, costShareLevelList, listMap, listVo, incomes, allocationQueryReportVOList, reportRelationMap, allocationList, allList));
            } else if (NumberConstant.FOUR.equals(calcType)) {
                // TODO 按照计算公式进行计算
//                i.setAmount(setCalculation(i, listVo, costShareLevelList, listMap, incomes, allocationQueryReportVOList, reportRelationMap, allocationList, allList));
            } else if (NumberConstant.FIVE.equals(calcType)) {
                // TODO  按照责任中心进行计算
                i.setAmount(setResponsibilityCode(i, reportRelationMap, allocationList, allList));
            } else {
                i.setAmount(new BigDecimal("0.000000"));
            }
        });
        //计算占比
        if (CollectionUtils.isEmpty(listVo)) {
            return;
        }

        List<HospProfitAndLossVo> listSum = new ArrayList<>();

        //把所有数据按报表项目汇总
        for (ReportForm j : reportForms) {
            HospProfitAndLossVo hospProfitAndLossVo = new HospProfitAndLossVo();
            hospProfitAndLossVo.setDateYear(finalYear);
            hospProfitAndLossVo.setDateMonth(finalMonth);
            hospProfitAndLossVo.setReportNum(j.getNum());
            hospProfitAndLossVo.setReportName(j.getReportName());
            hospProfitAndLossVo.setCostType(NumberConstant.ZERO);
            hospProfitAndLossVo.setIncomeType(NumberConstant.ZERO);
            hospProfitAndLossVo.setHospId(hospId);
            hospProfitAndLossVo.setType(j.getCostType());
            hospProfitAndLossVo.setFraction(j.getFraction());
            hospProfitAndLossVo.setReportId(j.getId());
            hospProfitAndLossVo.setReportNum(j.getNum());
            hospProfitAndLossVo.setCalcType(j.getCalcType());
            hospProfitAndLossVo.setReportName(j.getReportName());
            hospProfitAndLossVo.setCalcFormula(j.getCalcFormula());
            hospProfitAndLossVo.setReportParentId(j.getParentId());
            listSum.add(hospProfitAndLossVo);
        }
        Map<Integer, List<HospProfitAndLossVo>> reportNumGroup = listVo.stream().collect(Collectors.groupingBy(HospProfitAndLossVo::getReportNum));
        Map<Integer, BigDecimal> reportNumSumMap = new HashMap<>();
        reportNumGroup.forEach((reportNum, hospProfitAndLossList) -> {
            AtomicReference<BigDecimal> sum = new AtomicReference<>(new BigDecimal("0.000000"));
            for (HospProfitAndLossVo hospProfitAndLoss : hospProfitAndLossList) {
                if (hospProfitAndLoss.getAmount() != null) {
                    sum.updateAndGet(f -> f.add(hospProfitAndLoss.getAmount()));
                }
            }
            reportNumSumMap.put(reportNum, sum.get());
        });

        for (HospProfitAndLossVo costProfitVo : listSum) {
            BigDecimal bigDecimal = reportNumSumMap.get(costProfitVo.getReportNum());
            if (bigDecimal != null) {
                costProfitVo.setAmount(bigDecimal);

            } else {
                costProfitVo.setAmount(BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP));
            }
        }
        //单独计算计算公式数据
        setCalculationAmount(listSum);
        //收入
        List<HospProfitAndLossVo> profitIncomeList = listSum.stream().filter(f -> f.getType().equals(NumberConstant.ONE)).collect(Collectors.toList());
        List<HospProfitAndLossVo> profitIncome = costPercent(profitIncomeList);
        //成本
        List<HospProfitAndLossVo> profitCostList = listSum.stream().filter(f -> f.getType().equals(NumberConstant.TWO)).collect(Collectors.toList());

        List<HospProfitAndLossVo> profitCost = costPercent(profitCostList);

        //不计算项目
        List<HospProfitAndLossVo> noCompute = listSum.stream().filter(f -> f.getFraction().equals(NumberConstant.THREE)).collect(Collectors.toList());
        noCompute.forEach(profitVO -> profitVO.setPercent(null));

        List<HospProfitAndLossVo> listAllVo = new ArrayList<>();

        if (!CollectionUtils.isEmpty(profitIncome) && !CollectionUtils.isEmpty(profitCost) && !CollectionUtils.isEmpty(noCompute)) {
            listAllVo.addAll(profitIncome);
            listAllVo.addAll(profitCost);
            listAllVo.addAll(noCompute);
        } else if (!CollectionUtils.isEmpty(profitIncome) && !CollectionUtils.isEmpty(noCompute)) {
            listAllVo.addAll(noCompute);
            listAllVo.addAll(profitIncome);

        } else if (!CollectionUtils.isEmpty(profitCost) && !CollectionUtils.isEmpty(noCompute)) {
            listAllVo.addAll(noCompute);
            listAllVo.addAll(profitCost);
        } else if (!CollectionUtils.isEmpty(noCompute)) {
            listAllVo.addAll(noCompute);
        }

        List<HospProfitAndLoss> list = BeanUtil.convertList(listAllVo, HospProfitAndLoss.class);
        // 处理医院其他收支
        List<CostOtherPaymentsData> otherPaymentsDatas = otherPaymentsDataService.getByMonth(year, month, hospId);
        if (!otherPaymentsDatas.isEmpty()) {
            Optional<ReportForm> firstLossReport = reportForms.stream().filter(f -> NumberConstant.ONE.equals(f.getIsLoss())).findFirst();
            if(!firstLossReport.isPresent()){
                throw new CostException("全院损益报表配置有误，未设置损益项目");
            }
            Optional<HospProfitAndLoss> firstHospProfitAndLoss = list.stream().filter(f -> firstLossReport.get().getNum().equals(f.getReportNum())).findFirst();
            if(!firstHospProfitAndLoss.isPresent()){
                throw new CostException("全院损益报表配置有误，未找到配置的损益项目");
            }
            for (CostOtherPaymentsData ele : otherPaymentsDatas) {
                HospProfitAndLoss loss = new HospProfitAndLoss();
                loss.setDateYear(year).setDateMonth(month).setReportName(ele.getPaymentsName()).setReportNum((int) (-ele.getId()))
                        .setCreateTime(System.currentTimeMillis()).setAmount(ele.getTotalAmount()).setHospId(hospId)
                        .setResponsibilityName("全院").setResponsibilityCode("-1").setPercent(null).setOriginType(NumberConstant.ONE);
                if( NumberConstant.ONE.equals(ele.getPaymentsType()) ) {
                    //全院损益项目加上收入
                    firstHospProfitAndLoss.get().setAmount(firstHospProfitAndLoss.get().getAmount().add(ele.getTotalAmount()));
                }else{
                    //全院损益项目减去支出
                    firstHospProfitAndLoss.get().setAmount(firstHospProfitAndLoss.get().getAmount().subtract(ele.getTotalAmount()));
                }
                list.add(loss);
            }
        }
        long l = System.currentTimeMillis();
        list.forEach(i -> {
            i.setCreateTime(l);
            i.setReportType(reportType);
        });
        this.saveBatch(list);

    }

    /**
     * 是否需要代码计算
     * @return
     */
    public boolean IsNeedCalc(Integer reportType) {
        String parameterValue = centerService.getParameterValue(ParameterConstant.SQL_CALC_REPORT_TYPE);
        if(StringUtils.isEmpty(parameterValue)){
            return true;
        }
        //不需计算的报表类型列表
        List<String> nonCalcReportTypeList = Arrays.stream(parameterValue.split(SplitConstant.SEPARATOR_VERTICALLINE)).collect(Collectors.toList());
        return !nonCalcReportTypeList.contains(String.valueOf(reportType));
    }

    /**
     * 处理指定月份及关联月份的计算（当前月、下个月、明年同月）
     *
     * @param hospId      院区ID
     * @param computeDate 日期字符串，格式：YYYY-MM
     * @param shareType   报表类型
     */
    public void handleSpecificMonthsCalculation(Long hospId, String computeDate, Integer shareType) {
        // 解析输入的年月
        Integer currentYear = ComputeDateUtils.getComputeYear(computeDate);
        Integer currentMonth = ComputeDateUtils.getComputeMonth(computeDate);
        // 计算需要处理的期间信息
        List<PeriodInfoVO> periods = calculateAllRelatedPeriods(currentYear, currentMonth);

        // 获取所有相关的期间数据
        Map<String, List<HospProfitAndLoss>> allDataMap = loadAllPeriodData(hospId, shareType, periods);

        // 对每个期间执行计算
        for (PeriodInfoVO period : periods) {
            String currentKey = buildPeriodKey(period.getYear(), period.getMonth());
            if (allDataMap.containsKey(currentKey) && !CollectionUtils.isEmpty(allDataMap.get(currentKey))) {
                List<HospProfitAndLoss> currentRecords = allDataMap.get(currentKey);
                List<HospProfitAndLoss> prevRecords = getPeriodData(allDataMap, period.getPrevYear(), period.getPrevMonth());
                List<HospProfitAndLoss> lastYearRecords = getPeriodData(allDataMap, period.getLastYear(), period.getLastMonth());

                // 执行批量计算
                calculatePeriodComparisonWithPreloadedData(currentRecords, prevRecords, lastYearRecords);
            }
        }
    }

    /**
     * 构建所有需要处理的期间信息
     */
    private List<PeriodInfoVO> calculateAllRelatedPeriods(int baseYear, int baseMonth) {
        List<PeriodInfoVO> result = new ArrayList<>();

        // 添加当前期间
        result.add(calculatePeriodInfo(baseYear, baseMonth));

        // 下个月
        int nextMonth = baseMonth + 1;
        int nextMonthYear = baseYear;
        if (baseMonth == 12) {
            nextMonth = 1;
            nextMonthYear += 1;
        }
        result.add(calculatePeriodInfo(nextMonthYear, nextMonth));

        // 明年同月
        result.add(calculatePeriodInfo(baseYear + 1, baseMonth));

        return result;
    }

    /**
     * 构建单个期间的信息（当前月、上月、去年同期）
     */
    private PeriodInfoVO calculatePeriodInfo(int year, int month) {
        PeriodInfoVO info = new PeriodInfoVO();

        info.setYear( year);
        info.setMonth( month);

        // 上期
        if (month == 1) {
            info.setPrevYear(year - 1);
            info.setPrevMonth(12);
        } else {
            info.setPrevYear( year);
            info.setPrevMonth(month - 1);
        }

        // 同期
        info.setLastYear(year - 1);
        info.setLastMonth( month);

        return info;
    }

    /**
     * 加载所有相关期间的数据（去重处理）
     */
    private Map<String, List<HospProfitAndLoss>> loadAllPeriodData(Long hospId, Integer shareType, List<PeriodInfoVO> periods) {
        Set<String> allPeriodKeys = new HashSet<>();

        // 收集所有需要查询的年月组合
        for (PeriodInfoVO period : periods) {
            allPeriodKeys.add(buildPeriodKey(period.getYear(), period.getMonth()));
            allPeriodKeys.add(buildPeriodKey(period.getPrevYear(), period.getPrevMonth()));
            allPeriodKeys.add(buildPeriodKey(period.getLastYear(), period.getLastMonth()));
        }

        // 去重后查询
        Map<String, List<HospProfitAndLoss>> result = new HashMap<>();
        Set<String> processedKeys = new HashSet<>(); // 已处理的键集合

        for (String key : allPeriodKeys) {
            if (!processedKeys.contains(key)) {
                int[] ym = parsePeriodKey(key);
                List<HospProfitAndLoss> records = getPeriodRecords(hospId, shareType, ym[0], ym[1]);
                result.put(key, records);
                processedKeys.add(key); // 标记为已处理
            }
        }

        return result;
    }


    /**
     * 获取指定期间的数据
     *
     * @param hospId    院区ID
     * @param shareType 报表类型
     * @param year      年份
     * @param month     月份
     * @return 符合条件的数据列表
     */
    private List<HospProfitAndLoss> getPeriodRecords(Long hospId, Integer shareType, int year, int month) {
        return this.list(new QueryWrapper<HospProfitAndLoss>().lambda()
                .eq(HospProfitAndLoss::getHospId, hospId)
                .eq(HospProfitAndLoss::getReportType, shareType)
                .eq(HospProfitAndLoss::getDateYear, year)
                .eq(HospProfitAndLoss::getDateMonth, month)
                .eq(HospProfitAndLoss::getDeleteTime, 0));
    }

    /**
     * 根据年月获取对应的数据（从内存中查找）
     */
    private List<HospProfitAndLoss> getPeriodData(Map<String, List<HospProfitAndLoss>> dataMap, int year, int month) {
        String key = buildPeriodKey(year, month);
        return dataMap.getOrDefault(key, Collections.emptyList());
    }

    /**
     * 批量计算同环比及同期金额（使用预加载数据）
     */
    public void calculatePeriodComparisonWithPreloadedData(
            List<HospProfitAndLoss> currentRecords,
            List<HospProfitAndLoss> prevRecords,
            List<HospProfitAndLoss> lastYearRecords) {

        if (CollectionUtils.isEmpty(currentRecords)) {
            return;
        }

        // 创建报表编号到记录的映射
        Map<String, HospProfitAndLoss> prevMap = new HashMap<>();
        for (HospProfitAndLoss record : prevRecords) {
            String key = buildRecordKey(record);
            prevMap.put(key, record);
        }

        Map<String, HospProfitAndLoss> lastYearMap = new HashMap<>();
        for (HospProfitAndLoss record : lastYearRecords) {
            String key = buildRecordKey(record);
            lastYearMap.put(key, record);
        }

        // 批量更新数据
        for (HospProfitAndLoss record : currentRecords) {
            String key = buildRecordKey(record);
            HospProfitAndLoss prevRecord = prevMap.get(key);
            HospProfitAndLoss lastYearRecord = lastYearMap.get(key);

            // 设置上期和同期金额
            record.setPrevPeriodAmount(prevRecord != null ? prevRecord.getAmount() : BigDecimal.ZERO);
            record.setSamePeriodAmount(lastYearRecord != null ? lastYearRecord.getAmount() : BigDecimal.ZERO);

            // 计算环比和同比
            record.setMomRate(calculateProfitRate(record.getAmount().subtract(record.getSamePeriodAmount()), record.getPrevPeriodAmount()));
            record.setYoyRate(calculateProfitRate(record.getAmount().subtract(record.getSamePeriodAmount()), record.getSamePeriodAmount()));
        }

        // 批量更新数据库
        this.updateBatchById(currentRecords);
    }

    /**
     * 构建期间键（用于存储和检索数据）
     */
    private String buildPeriodKey(int year, int month) {
        return year + "_" + month;
    }

    /**
     * 解析期间键为年月数组
     */
    private int[] parsePeriodKey(String key) {
        String[] parts = key.split("_");
        return new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[1])};
    }

    /**
     * 构建记录的唯一标识键（责任码 + 报表编号）
     */
    private String buildRecordKey(HospProfitAndLoss record) {
        return String.valueOf(record.getReportNum());
    }

    /**
     * 安全地计算百分比：避免除零错误
     */
    private BigDecimal calculateProfitRate(BigDecimal current, BigDecimal base) {
        if (base == null || base.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal bigDecimal = current.divide(base, 4, RoundingMode.HALF_UP).setScale(4, RoundingMode.HALF_UP);
        return bigDecimal;
    }

    /**
     * 执行全院损益后续脚本
     * @param date
     * @param reportType
     */
    public void execHospProfitSql(String date,Integer reportType){
        Map<String,String> sqlParameter = new HashMap<>();
        sqlParameter.put(SQLParameter.COMPUTE_DATE_CODE, DateUtils.StringToString(date, DateStyleEnum.YYYY_MM));
        sqlParameter.put(SQLParameter.REPORT_TYPE_CODE,String.valueOf(reportType));
        sqlService.autoExecuteSql(CustomSqlTypeEnum.HOSP_PROFIT_CALC.getCode(),sqlParameter);

    }

    // 计算公式中钱
    private BigDecimal calcAmount(List<HospProfitAndLoss> list, String calcFormula, ReportForm reportForm) {
        // 得到所有的编号
        String replace = calcFormula.replace("[", "").replace("]", "").replace("-", ",").replace("+", ",");
        ArrayList<String> numList = CollUtil.newArrayList(replace.split(","));
        // 得到编号的map
        Map<Integer, Integer> numMap = new ConcurrentHashMap<>();
        for (int j = 0; j < numList.size(); j++) {
            numMap.put(j, Integer.parseInt(numList.get(j)));
        }
        List<String> expressions = ReUtil.findAll("[^0-9]", "+" + calcFormula.replace("[", "").replace("]", "").trim(), 0)
                .stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
        // 得到预算表达式 得到所有表达式的Map  +  -  相关的
        Map<Integer, String> expressionMap = new ConcurrentHashMap<>();
        for (int k = 0; k < expressions.size(); k++) {
            expressionMap.put(k, expressions.get(k));
        }
        // 数字的索引和表达式的索引累加计算
        Set<Integer> numSet = numMap.keySet();
        List<Integer> nums = new ArrayList<>(numSet);
        AtomicReference<BigDecimal> totalAmount = new AtomicReference<>();
        totalAmount.set(BigDecimal.ZERO);
        for (int i = 0; i < nums.size(); i++) {
            // 编号
            Integer num = numMap.get(i);
            List<HospProfitAndLoss> losses = list.stream().filter(item -> item.getReportNum().equals(num)).collect(Collectors.toList());
            if (CollUtil.isEmpty(losses)) {
                continue;
            }
//            log.info("data={}", reportForm);
//            if (losses.size() != 0 && losses.size() != 1) {
//                throw new CostException("数据异常");
//            }
            HospProfitAndLoss loss = losses.get(0);
            BigDecimal amount = loss.getAmount();
            String str = expressionMap.get(i);
            if (str.equals("+")) {
                totalAmount.set(totalAmount.get().add(amount));
            } else {
                totalAmount.set(totalAmount.get().subtract(amount));
            }
        }
        return totalAmount.get();
    }

    /**
     * 按责任中心计算
     *
     * @param hospId            医院id
     * @param reportForm        报表
     * @param allocationQueries 分摊成本数据
     * @param list
     * @param year
     * @param month
     */
    private void calcByResponsibility(Long hospId, ReportForm reportForm, List<AllocationQuery> allocationQueries, List<HospProfitAndLoss> list, int year, int month) {
        List<RelationVO> responsibilities = reportRelationService.getResponsibilities(reportForm.getId(), hospId);
        if (responsibilities.isEmpty()) {
            return;
        }
        List<String> accountCodes = responsibilities.stream().map(RelationVO::getCode).collect(Collectors.toList());
        AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
        calcTotal.set(BigDecimal.ZERO);
        accountCodes.forEach(i -> {
            BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getResponsibilityCode())).map(AllocationQuery::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            calcTotal.set(calcTotal.get().add(costAmount));
        });
        HospProfitAndLoss loss = new HospProfitAndLoss();
        loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum())
                .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month);
        list.add(loss);
    }


    /**
     * 按计算公式计算
     */
    private void calcByFormula(Integer year, Integer month, ReportForm child, List<ReportForm> reportForms, List<HospProfitAndLoss> list) {
        // 得到当前下按公式计算的
        List<ReportForm> calcFormulas = reportForms.stream().filter(i -> i.getCalcType() == CalcTypeEnum.CALC_FORMULA.getType()).collect(Collectors.toList());
        calcFormulas = calcFormulas.stream().filter(i -> i.getNum().equals(child.getNum())).collect(Collectors.toList());
        for (ReportForm i : calcFormulas) {
            String calcFormula = i.getCalcFormula();
            // TODO: 2021/8/27 校验公式合法性
            if (StrUtil.isBlank(calcFormula)) {
                throw new CostException("reportForm名称为" + i.getReportName() + "计算公式不正确");
            }
            BigDecimal bigDecimal = calcAmount(list, calcFormula, i);
            HospProfitAndLoss loss = new HospProfitAndLoss();
            loss.setDateYear(year).setDateMonth(month).setReportNum(i.getNum()).setReportName(i.getReportName())
                    .setAmount(bigDecimal).setCreateTime(System.currentTimeMillis()).setHospId(i.getHospId());
            list.add(loss);
        }

    }

    /**
     * 按小计计算
     */
    private void calcByLitterCount(Integer year, Integer month, ReportForm child, List<ReportForm> reportForms, List<HospProfitAndLoss> list, Long hospId) {
        // 其他的
        List<ReportForm> calcList = reportForms.stream().filter(i -> !i.getCalcType().equals(CalcTypeEnum.LITTER_COUNT.getType())).collect(Collectors.toList());
        List<HospProfitAndLoss> thisList = new ArrayList<>();
        list.forEach(i -> {
            calcList.forEach(j -> {
                if (i.getReportNum().equals(j.getNum())) {
                    thisList.add(i);
                }
            });
        });
        if (thisList.isEmpty()) {
            return;
        }
        BigDecimal reduce = thisList.stream().map(HospProfitAndLoss::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        HospProfitAndLoss loss = new HospProfitAndLoss();
        loss.setDateYear(year).setDateMonth(month).setAmount(reduce).setCreateTime(System.currentTimeMillis()).setHospId(hospId);
        // 小计只能有一个
        loss.setReportNum(child.getNum()).setReportName(child.getReportName());
        list.add(loss);
    }

    /**
     * 按分摊层级计算、
     *
     * @param hospId     医院id
     * @param reportForm 报表
     * @param list
     * @param year
     * @param month
     */
    private void calcByShareLevel(Long hospId, ReportForm reportForm, List<HospProfitAndLoss> list, int year, int month) {
        List<RelationVO> shareLevels = reportRelationService.getShareLevel(reportForm.getId(), hospId);
        List<Long> shareLevelId = shareLevels.stream().map(RelationVO::getCode).map(Long::valueOf).sorted(Long::compareTo).collect(Collectors.toList());
        if (CollUtil.isEmpty(shareLevelId)) {
            return;
        }
        List<CostShareLevel> costShareLevels = shareLevelService.listByIds(shareLevelId);
        if (costShareLevels.isEmpty()) {
            throw new CostException("医院分摊层级设置错误，" + reportForm.getReportName());
        }
        List<Integer> levelSorts = costShareLevels.stream().map(CostShareLevel::getLeverSort).collect(Collectors.toList());
        List<AllocationQuery> allocations = allocationQueryService.getByDate(year, month, hospId, shareLevelId);

        if (allocations.isEmpty()) {
            throw new CostException("医院未分摊本月数据");
        }
        List<CostAccountShare> accountShares = accountShareService.getByShareLevelSort(levelSorts, hospId);
        if (accountShares.isEmpty()) {
            return;
        }

//        allocations = allocations.stream().filter(i -> levelSorts.contains(i.getLevelSort())).collect(Collectors.toList());
        BigDecimal reduce = allocations.stream().map(AllocationQuery::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        HospProfitAndLoss loss = new HospProfitAndLoss();
        loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum())
                .setAmount(reduce).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month);
        list.add(loss);
    }

    /**
     * 计算按会计科目下的科目名称
     *
     * @param hospId            医院id
     * @param reportForm        报表
     * @param incomes           归集收入数据
     * @param list
     * @param allocationQueries 分摊成本数据
     * @param month
     * @param year
     */
    private void calcByAccount(Long hospId, ReportForm reportForm, List<IncomeCollection> incomes, List<HospProfitAndLoss> list, List<AllocationQuery> allocationQueries, int month, int year) {
        // check 这个医院是否有对应的损益标识
        reportFormService.checkExistLoss(hospId);
        // 报表项目关联的会计科目对象
        List<RelationVO> accountRelations = reportRelationService.getAccountRelation(reportForm.getId(), hospId);
        if (accountRelations.isEmpty()) {
            return;
        }
        List<String> accountCodes = accountRelations.stream().map(RelationVO::getCode).collect(Collectors.toList());
        AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
        calcTotal.set(BigDecimal.ZERO);
        accountCodes.forEach(i -> {
            BigDecimal incomeAmount = incomes.stream().filter(j -> i.equals(j.getAccountingCode())).map(IncomeCollection::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getAccountingCode())).map(AllocationQuery::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal total = incomeAmount.add(costAmount);
            calcTotal.set(calcTotal.get().add(total));
        });
        HospProfitAndLoss loss = new HospProfitAndLoss();
        loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum())
                .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month);
        list.add(loss);
    }


    /**
     * 全院损益列表
     *
     * @param current  当前页
     * @param pageSize 每页展示数据大小
     * @param date     日期
     * @param hospId   医院id
     * @return PageUtils
     */
    @Override
    public PageUtils getHospProfits(Integer current, Integer pageSize, String date, Long hospId) {
        reportFormService.checkExistLoss(hospId);
        DateTime parse = DateUtil.parse(date);
        int year = DateUtil.year(parse);
        int month = DateUtil.month(parse) + 1;
        int startIndex = (current - 1) * pageSize;
        List<HospProfitAndLoss> list = baseMapper.getPageList(startIndex, pageSize, year, month, hospId);
        // 损益的时候计算下
        list.forEach(i -> {
            ReportForm one = reportFormService.getOne(
                    new LambdaQueryWrapper<ReportForm>().select(ReportForm::getId)
                            .eq(ReportForm::getNum, i.getReportNum())
                            .eq(ReportForm::getHospId, hospId)
                            .eq(ReportForm::getReportType, 3)
                            .eq(ReportForm::getIsLoss, 1)
                            .last(Constant.LIMIT)
            );
            if (Objects.nonNull(one)) {
                List<CostOtherPaymentsData> byMonth = otherPaymentsDataService.getByMonth(year, month, hospId);
                AtomicReference<BigDecimal> total = new AtomicReference<>();
                total.set(BigDecimal.ZERO);
                byMonth.forEach(j -> {
                    if (j.getPaymentsType().equals(1)) {
                        total.set(total.get().add(j.getTotalAmount()));
                    } else {
                        total.set(total.get().subtract(j.getTotalAmount()));
                    }
                });
                i.setAmount(i.getAmount().add(total.get()));
            }
        });
        int totalCount = baseMapper.getPageCount(year, month, hospId);
        return new PageUtils(list, totalCount, pageSize, current);
    }

    /**
     * 导出全院损益计算
     *
     * @param date   yyyy-MM-dd
     * @param hospId
     */
    @Override
    public void hospProfitReport(String date, Long hospId) {
        DateTime parse = DateUtil.parse(date);
        int year = DateUtil.year(parse);
        int month = DateUtil.month(parse) + 1;

        // 得到所有责任中心的子节点
        List<Responsibility> responsibilityList = responsibilityService.list(new QueryWrapper<Responsibility>().lambda().eq(Responsibility::getHospId, hospId));

//        List<Responsibility> leafResp = responsibilityService.getLeafResp(hospId);
        List<CostOtherPaymentsData> byMonth = otherPaymentsDataService.getByMonth(year, month, hospId);
//        if (!byMonth.isEmpty()) {
//            Responsibility responsibility = new Responsibility("-1", "全院", -1L);
//            responsibilityList.add(responsibility);
//        }
        Map<String, Long> responseCodeParentIdMap = responsibilityList.stream().collect(Collectors.toMap(Responsibility::getResponsibilityCode, Responsibility::getParentId, (a, b) -> b));
        Map<Long, Responsibility> responsibilityIdMap = responsibilityList.stream().collect(Collectors.toMap(Responsibility::getId, responsibility -> responsibility, (a, b) -> b));

        // 查询所有的全院损益数据  内存溢出问题
        List<HospProfitAndLoss> hospProfitAndLosses = getAllDataByDate(year, month, hospId,NumberConstant.THREE);
        if (CollectionUtils.isEmpty(hospProfitAndLosses)) {
            throw new CostException(500, "未进行全院损益计算");
        }
        //判断是否有全院的数据  有单独取出另算
        List<HospProfitAndLoss> hospAllList = hospProfitAndLosses.stream().filter(f -> f.getResponsibilityCode().equals("-1")).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(hospAllList)) {
            hospProfitAndLosses.removeAll(hospAllList);
        }
        //多层级责任  中心 添加父类责任中心 方便excel分组
        for (HospProfitAndLoss hospProfitAndLoss : hospProfitAndLosses) {
            Long aLong = responseCodeParentIdMap.get(hospProfitAndLoss.getResponsibilityCode());
            if (aLong != null) {
                if (aLong.equals(NumberConstant.ZERO_L)) {
//                    Long aLong1 = responseCodeIdMap.get(departmentProfit.getResponsibilityCode());
                    hospProfitAndLoss.setParentResponsibilityCode(hospProfitAndLoss.getResponsibilityCode());
                    hospProfitAndLoss.setParentResponsibilityName(hospProfitAndLoss.getResponsibilityName());
                } else if (aLong.equals(-1L)) {
                    hospProfitAndLoss.setParentResponsibilityCode("-1");
                    hospProfitAndLoss.setParentResponsibilityName("全院");
                } else {
                    Responsibility responsibility = responsibilityIdMap.get(aLong);
                    if (Objects.nonNull(responsibility)) {
                        hospProfitAndLoss.setParentResponsibilityCode(responsibility.getResponsibilityCode());
                        hospProfitAndLoss.setParentResponsibilityName(responsibility.getResponsibilityName());
                    }
                }

            }
        }

        //损益表设置
        List<ReportForm> reportFormList = reportFormService.list(new QueryWrapper<ReportForm>().lambda()
                .eq(ReportForm::getHospId, hospId)
                .eq(ReportForm::getReportType, NumberConstant.THREE));
        if (CollUtil.isEmpty(reportFormList)) {
            throw new CostException(500, "损益表未找到");
        }
        if (!byMonth.isEmpty()) {
            // 手动构造全院数据表头 生成全院其他收支
            //添加全院其他报表项目
            //最上层
            ReportForm parentReport = new ReportForm();
            parentReport.setId(-1L);
            parentReport.setReportType(NumberConstant.THREE);
            parentReport.setParentId(NumberConstant.ZERO_L);
            parentReport.setSort(0);
            parentReport.setReportName("全院其他收支");
            parentReport.setNum(-1);
            reportFormList.add(parentReport);
            //第二层按全院收支数据自动生成
            for (int i = 0; i < byMonth.size(); i++) {
                CostOtherPaymentsData data = byMonth.get(i);
                ReportForm reportForm = new ReportForm();
//                reportForm.setId(-1L -i - byMonth.size());
                reportForm.setId(-System.currentTimeMillis() - i);

                reportForm.setReportType(NumberConstant.THREE);
                reportForm.setParentId(-1L);
                reportForm.setSort(0);
                reportForm.setReportName(data.getPaymentsName());
                reportForm.setNum((int) (-data.getId()));
                reportFormList.add(reportForm);
            }
        }

        Map<Long, List<ReportForm>> collect = reportFormList.stream().collect(Collectors.groupingBy(ReportForm::getParentId));
        Map<String, List<ColEntity>> heads = new HashMap<>();// 最终导出的多个sheet的表头
        Map<String, List<Map<String, String>>> datas = new HashMap<>();// 最终导出的多个sheet的内容
        Map<String, Integer> types = new HashMap<>();
        Map<String, List<Integer>> mergeindexs = new HashMap<>();// 最终导出的每个sheet的需要纵向合并的单元格列号
        //第一层
        List<ReportForm> firstTitle = collect.get(NumberConstant.ZERO_L);
        List<TitleEntity> titleList = new ArrayList<>();
        titleList.add(new TitleEntity("0", null, "全院损益 \n" + "制表时间：" + DateUtil.now(), false, NumberConstant.ZERO));
        //添加默认三列责任中心 父类责任中心 子类责任中心  项目名称
        setDefaultColumn(titleList);

        for (ReportForm reportForm : firstTitle) {
            TitleEntity entity = new TitleEntity();
            entity.setId(String.valueOf(reportForm.getId()));
            entity.setPid(String.valueOf(reportForm.getParentId()));
            entity.setContent(reportForm.getReportName());
            entity.setFieldName(String.valueOf(reportForm.getNum()));
            entity.setWidth(20);
            entity.setSort(reportForm.getSort());
            boolean child = getChild(titleList, reportForm, collect);
            entity.setLast(child);
            titleList.add(entity);
        }
        titleList.sort(Comparator.comparing(TitleEntity::getSort));

        hospProfitAndLosses.sort(Comparator.comparing(HospProfitAndLoss::getParentResponsibilityCode));
        //行
        Map<String, List<HospProfitAndLoss>> lineMap = hospProfitAndLosses.stream().collect(Collectors.groupingBy(HospProfitAndLoss::getParentResponsibilityCode));

        //列
        List<TitleEntity> titleEntities = titleList.stream().filter(TitleEntity::isLast).collect(Collectors.toList());
        List<Map<String, String>> rowList = new ArrayList<>();
        //行数据
        setRowList(lineMap, titleEntities, rowList);
        //添加 全院其他收支数据
        setHospRowList(hospAllList, titleEntities, rowList);

        try {
            ExcelPoiUtil excelTool = new ExcelPoiUtil();
            excelTool.setTitle("全院损益");
            Map<String, String> param = ImmutableMap.<String, String>builder().put("id", "id").put("pid", "pid")
                    .put("content", "content").put("fieldName", "fieldName").put("width", "width").build();

            List<ColEntity> titleData = excelTool.colEntityTransformer(titleList, param, "0");
            Map<String, List<Integer>> autoRowHeights = new HashMap<>();
            heads.put("全院损益", titleData);// 每个sheet的表头，sheet名称为key
            datas.put("全院损益", rowList);// 每个sheet的内容，sheet名称为key
            types.put("全院损益", 0);// 每个sheet的样式类型，sheet名称为key
            mergeindexs.put("全院损益", Arrays.asList(0, 1));// 每个sheet的默认行高，sheet名称为key
            autoRowHeights.put("全院损益", Arrays.asList(1, 2, 3, 4, 5));
            // 多个sheet导出
            HSSFWorkbook workbook = excelTool.exportWorkbook(heads, datas, types, autoRowHeights, mergeindexs);

//            excelTool.save(workbook, "D:\\全院损益.xls");

//        // 得到上一层的title子节点
////        ExcelWriter writer = ExcelUtil.getWriter();
//        String time = DateUtil.format(DateUtil.date(), "yyyy年MM月dd日HH时mm分ss秒");
//        //todo:后面调整为上传到文件服务器，先启动确认环境
//        String fileName =   "全院损益" + time + ".xlsx";
//        ExcelWriter writer = ExcelUtil.getWriter(fileName);
//
//        List<String> secondTitleListCode = leafResp.stream().map(Responsibility::getResponsibilityCode).collect(Collectors.toList());
//        writer.merge(leafResp.size() + 1, "全院损益计算导出 \n" + "制表时间：" + DateUtil.now());
//        // 得到两层结构
//        writer.setColumnWidth(-1, 20);
//        writer.passCurrentRow();
//        writer.merge(2, 2, 0, 1, "项目", true);
//        int oldSize = 2;
//        Map<Long, Object> map = new HashMap<>();
//        for (int i = 0; i < leafResp.size(); i++) {
//            Responsibility responsibility = leafResp.get(i);
//            String str = responsibility.getResponsibilityName();
//            writer.writeCellValue(i + 2, 2, str);
//            // 写父亲层级
//            Long parentId = responsibility.getParentId();
//            if (map.get(parentId) != null) {
//                // 如果parentId = 0
//                if (parentId != 0L) {
//                    continue;
//                }
//            }
//            Responsibility byId = responsibilityService.getById(parentId);
//            String name = "";
//            if (byId != null) {
//                name = byId.getResponsibilityName();
//            }
//            int count = (int) leafResp.stream().filter(o -> o.getParentId().equals(parentId)).count();
//
//            if (count == 1) {
//                writer.writeCellValue(oldSize, 1, name);
//            } else if (count == 0) {
//                writer.writeCellValue(oldSize, 1, "全院其他");
//            } else {
//                writer.merge(1, 1, oldSize, oldSize + count - 1, name, false);
//            }
//
//            oldSize = oldSize + count;
//            map.put(parentId, oldSize);
//
//        }
//        // 得到全院损益报表处理 树状结构（only 2层）
//        List<ReportFormVO> allHospList = reportFormService.getAllHospList(hospId);
//        if (allHospList.isEmpty()) {
//            throw new CostException("请先设置全院损益报表");
//        }
//
//        if (!byMonth.isEmpty()) {
//            // 构造假数据 生成全院其他收支
//            ReportFormVO vo = new ReportFormVO(-1L, -1, "全院其他收支", 0L, new ArrayList<>());
//            for (CostOtherPaymentsData data : byMonth) {
//                ReportFormVO reportFormVO = new ReportFormVO(-2L, (int) (-data.getId()), data.getPaymentsName(), -1L, null);
//                vo.getChildren().add(reportFormVO);
//            }
//            allHospList.add(allHospList.size() - 1, vo);
//        }
//        // 查询所有的全院损益数据  内存溢出问题
//        List<HospProfitAndLoss> list = getAllDataByDate(year, month, hospId);
//        int lastRow = 3;
//        for (int i = 0; i < allHospList.size(); i++) {
//            ReportFormVO parentFormVO = allHospList.get(i);
//            List<ReportFormVO> children = parentFormVO.getChildren();
//            if (CollUtil.isEmpty(children)) {
//                continue;
//            }
//            int size = children.size();
//            // 当最后一个的时候写上全院其他收支
//            if (allHospList.size() == 1) {
//                writer.writeCellValue(0, lastRow, parentFormVO.getReportName());
//            } else {
//                writer.merge(lastRow, lastRow + size - 1, 0, 0, parentFormVO.getReportName(), true);
//            }
//
//            // 具体的报表项目
//            for (int j = 0; j < size; j++) {
//                // todo  可以抽取出单独方法
//                ReportFormVO childFormVO = children.get(j);
//                // 第二列的数据
//                writer.writeCellValue(1, lastRow + j, childFormVO.getReportName());
//                // 单独计每个数据的责任中心对应的金额
//                for (int k = 0; k < secondTitleListCode.size(); k++) {
//
//                    String responsibilityCode = secondTitleListCode.get(k);
//                    Integer num = childFormVO.getNum();
//                    HospProfitAndLoss loss = list.stream().filter(o -> o.getReportNum().equals(num) && o.getResponsibilityCode().equals(responsibilityCode)).findAny()
//                            .orElse(null);
//                    BigDecimal bigDecimal = Objects.isNull(loss) ? BigDecimal.ZERO : loss.getAmount();
//                    writer.writeCellValue(k + 2, lastRow + j, bigDecimal);
//                    if (k == secondTitleListCode.size() - 1) {
//                        bigDecimal = list.stream().filter(o -> o.getReportNum().equals(num)).map(HospProfitAndLoss::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
//                        writer.writeCellValue(k + 2, lastRow + j, bigDecimal);
//
//                        if (j == size - 1 && i == allHospList.size() - 1) {
//                            // 计算 其他
//                            BigDecimal another = BigDecimal.ZERO;
//                            for (CostOtherPaymentsData costOtherPaymentsData : byMonth) {
//                                if (costOtherPaymentsData.getPaymentsType() == 1) {
//                                    another = another.add(costOtherPaymentsData.getTotalAmount());
//                                } else {
//                                    another = another.subtract(costOtherPaymentsData.getTotalAmount());
//                                }
//                            }
//                            bigDecimal = bigDecimal.add(another);
//                            writer.writeCellValue(k + 2, lastRow + j, bigDecimal);
//                        }
//                    }
//
//
//                }
//
//            }
//            lastRow = lastRow + size;
//        }
            String time = DateUtil.format(DateUtil.date(), "yyyy年MM月dd日HH时mm分ss秒");

            FileItemFactory factory = new DiskFileItemFactory(5242880, null);

            FileItem fileItem = factory.createItem("file", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8", true, "全院损益" + time + ".xls");
            String file = "";

            OutputStream outputStream = fileItem.getOutputStream();
//            writer.flush(outputStream,true);
            workbook.write(outputStream);
            MultipartFile multipartFile = new CommonsMultipartFile(fileItem);
            //上传 excel
            file = uploadFile(multipartFile);
            //保存文件记录
            FileRecord fileRecord = new FileRecord();
            fileRecord.setFileName("全院损益" + time + ".xls")
                    .setFileSource(1)
                    .setFileType("全院损益")
                    .setFileUrl(file)
                    .setHospId(hospId)
                    .setDateYear(year).setDateMonth(month)
                    .setCreateTime(System.currentTimeMillis());
            fileRecordService.save(fileRecord);

            workbook.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    private static void setHospRowList(List<HospProfitAndLoss> hospAllList, List<TitleEntity> titleEntities, List<Map<String, String>> rowList) {
        List<Map<String, String>> amountRowList = new ArrayList<>();
        for (Map<String, String> map : rowList) {
            String itemType = map.get("itemType");
            if (itemType.equals("金额")) {
                amountRowList.add(map);
            }
        }
        //相 同key 的数值 进行汇总
//        for (int i = 0; i < 2; i++) {
        Map<String, String> hospMap = new HashMap<>();
        for (TitleEntity titleEntity : titleEntities) {
            AtomicReference<BigDecimal> total = new AtomicReference<>(new BigDecimal("0.00"));
            for (Map<String, String> map : amountRowList) {
                String s = map.get(titleEntity.getFieldName());
                if (!titleEntity.getFieldName().equals("child") && !titleEntity.getFieldName().equals("parent") && !titleEntity.getFieldName().equals("itemType")) {
//                       if(StringUtils.isEmpty(s)){
//                           total.updateAndGet(v -> v.add(BigDecimal.ZERO));
//                       }else {
                    total.updateAndGet(v -> v.add(new BigDecimal(s)));
//                       }
                }
            }
            if (titleEntity.getFieldName().equals("parent")) {
                hospMap.put("parent", "全院");
            } else if (titleEntity.getFieldName().equals("child")) {
                hospMap.put("child", "全院");
            } else if (titleEntity.getFieldName().equals("itemType")) {
//                    if (i == 0) {
                hospMap.put("itemType", "金额");
//                    } else {
//                        hospMap.put("itemType", "占比");
//                    }
            } else {
//                    if (i == 0) {
                hospMap.put(titleEntity.getFieldName(), total.get().toString());
                if (!CollectionUtils.isEmpty(hospAllList)) {
                    for (HospProfitAndLoss data : hospAllList) {
                        hospMap.put(data.getReportNum().toString(), data.getAmount().setScale(2, RoundingMode.HALF_UP).toString());
                    }
                }
//                    } else {
//                        hospMap.put(titleEntity.getFieldName(), "");
//                    }
            }
        }
        rowList.add(hospMap);
//        }
    }

    private static void setRowList(Map<String, List<HospProfitAndLoss>> lineMap, List<TitleEntity> titleEntities, List<Map<String, String>> rowList) {
        DecimalFormat df = new DecimalFormat("0.00%");
        for (String parentCode : lineMap.keySet()) {
            List<HospProfitAndLoss> costDepartmentProfits1 = lineMap.get(parentCode);
            Map<String, List<HospProfitAndLoss>> listMap = costDepartmentProfits1.stream().collect(Collectors.groupingBy(HospProfitAndLoss::getResponsibilityCode));
            for (String s : listMap.keySet()) {
                List<HospProfitAndLoss> costDepartmentProfits2 = listMap.get(s);
                Map<String, HospProfitAndLoss> map = costDepartmentProfits2.stream().collect(Collectors.toMap(profit -> String.valueOf(profit.getReportNum()), profit -> profit, (a, b) -> b));
                for (int i = 0; i < 2; i++) {
                    Map<String, String> m = new HashMap<String, String>();
                    for (TitleEntity titleEntity : titleEntities) {
                        if (titleEntity.getId().equals("parentResponsibility")) {
                            m.put("parent", costDepartmentProfits2.get(0).getParentResponsibilityName());
                        } else if (titleEntity.getId().equals("childResponsibility")) {
                            m.put("child", costDepartmentProfits2.get(0).getResponsibilityName());
                        } else if (titleEntity.getId().equals("item")) {
                            if (i == 0) {
                                m.put("itemType", "金额");
                            } else {
                                m.put("itemType", "占比");
                            }
                        } else if (titleEntity.getId().contains("-")) {
                            HospProfitAndLoss profit = map.get(titleEntity.getFieldName());
                            if (Objects.nonNull(profit)) {
                                if (i == 0) {
                                    m.put(titleEntity.getFieldName(), profit.getAmount().setScale(2, RoundingMode.HALF_UP).toString());
                                } else {
                                    if (profit.getPercent() != null) {
                                        m.put(titleEntity.getFieldName(), df.format(profit.getPercent()));
                                    } else {
                                        m.put(titleEntity.getFieldName(), "");
                                    }
                                }
                            } else {
                                m.put(titleEntity.getFieldName(), "0.00");
                            }
                        } else {
                            HospProfitAndLoss profit = map.get(titleEntity.getFieldName());
                            if (Objects.nonNull(profit)) {
                                if (i == 0) {
                                    m.put(titleEntity.getFieldName(), profit.getAmount().setScale(2, RoundingMode.HALF_UP).toString());
                                } else {
                                    if (profit.getPercent() != null) {
                                        m.put(titleEntity.getFieldName(), df.format(profit.getPercent()));
                                    } else {
                                        m.put(titleEntity.getFieldName(), "");
                                    }
                                }
                            }
                        }
                    }
                    rowList.add(m);
                }
            }
        }
    }

    private void setDefaultColumn(List<TitleEntity> titleList) {
        TitleEntity parentResponsibility = new TitleEntity();
        parentResponsibility.setId("parentResponsibility");
        parentResponsibility.setPid("0");
        parentResponsibility.setContent("");
        parentResponsibility.setFieldName("parent");
        parentResponsibility.setWidth(15);
        parentResponsibility.setSort(NumberConstant.ZERO);
        parentResponsibility.setLast(true);
        titleList.add(parentResponsibility);
        TitleEntity childResponsibility = new TitleEntity();
        childResponsibility.setId("childResponsibility");
        childResponsibility.setPid("0");
        childResponsibility.setContent("");
        childResponsibility.setFieldName("child");
        childResponsibility.setWidth(15);
        childResponsibility.setSort(NumberConstant.ZERO);
        childResponsibility.setLast(true);
        titleList.add(childResponsibility);
        TitleEntity item = new TitleEntity();
        item.setId("item");
        item.setPid("0");
        item.setContent("");
        item.setFieldName("itemType");
        item.setWidth(10);
        item.setSort(NumberConstant.ZERO);
        item.setLast(true);
        titleList.add(item);
    }

    private boolean getChild(List<TitleEntity> titleList, ReportForm reportForm, Map<Long, List<ReportForm>> collect) {
        List<ReportForm> forms = collect.get(reportForm.getId());
        if (!CollectionUtils.isEmpty(forms)) {
            for (ReportForm form : forms) {
                TitleEntity entity = new TitleEntity();
                entity.setId(String.valueOf(form.getId()));
                entity.setPid(String.valueOf(form.getParentId()));
                entity.setContent(form.getReportName());
                entity.setFieldName(String.valueOf(form.getNum()));
                entity.setWidth(15);
                entity.setSort(form.getSort());
                boolean last = getChild(titleList, form, collect);
                entity.setLast(last);
                titleList.add(entity);
            }
            return false;
        } else {
            return true;
        }
    }


    public String uploadFile(MultipartFile file) {
        Long hospId = UserContext.getCurrentLoginHospId();
        String fileName = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf(".")) + System.currentTimeMillis() + ".xls";
        DateTime date = DateUtil.date();
        int month = DateUtil.month(date) + 1;
        int year = DateUtil.year(date);
        int day = DateUtil.dayOfMonth(date);
        String fileUrl = "";
        try {
//            file.transferTo(new File(localFilePath));
            String format = DateUtil.format(date, DatePattern.PURE_DATETIME_PATTERN);
            String originalFilename = format + fileName;
            String dataDirectory = minioConfig.getBucketName();
            String uploadFileName = "hospReport" + "/" + hospId + "/" + year + "/" + month + "/" + day + "/" + originalFilename;
            InputStream inputStream = file.getInputStream();
            minioFileUtil.putObject(dataDirectory, uploadFileName, inputStream);
            fileUrl = minioFileUtil.getObjectUrl(dataDirectory, uploadFileName);
        } catch (IOException e) {
            log.error("【文件上传至服务器】失败，绝对路径：{}", e.getMessage());
            throw new CostException(ErrorCodeEnum.FILE_UPLOAD_ERROR);
        }
        return fileUrl;
    }

    @Override
    public PageUtils hospProfitReports(Integer current, Integer pageSize, Long hospId, String date) {
        DateTime parse = DateUtil.parse(date);
        int year = DateUtil.year(parse);
        int month = DateUtil.month(parse) + 1;

        Page<FileRecord> page = new Page<>(current, pageSize);
        Page<FileRecord> pageUtils = fileRecordService.page(page,
                new LambdaQueryWrapper<FileRecord>()
                        .eq(FileRecord::getHospId, hospId)
                        .eq(FileRecord::getFileType, "全院损益")
                        .eq(FileRecord::getDateYear, year)
                        .eq(FileRecord::getDateMonth, month)
                        .orderByDesc(FileRecord::getCreateTime)
        );
        return new PageUtils(pageUtils);
    }

    @Override
    public Object getHospProfitResponsibilities(String responsibilityName) {
        List<Responsibility> responsibilityList = responsibilityService.list(new QueryWrapper<Responsibility>().lambda()
                .eq(Responsibility::getHospId, UserContext.getHospId())
                .and(q -> q.eq(Responsibility::getStatus, NumberConstant.ONE).or().eq(Responsibility::getStatus, null))
                .like(StrUtil.isNotBlank(responsibilityName), Responsibility::getResponsibilityName, responsibilityName));
        List<Responsibility> responsibilityAllList = responsibilityService.list(new QueryWrapper<Responsibility>().lambda()
                .eq(Responsibility::getHospId, UserContext.getHospId()));
        Map<Long, Responsibility> map = responsibilityAllList.stream().collect(Collectors.toMap(Responsibility::getId, responsibility -> responsibility, (a, b) -> b));
        List<Responsibility> addList = new ArrayList<>();
        for (Responsibility responsibility : responsibilityList) {
            Responsibility responsibility1 = map.get(responsibility.getParentId());
            if (Objects.nonNull(responsibility1)) {
                addList.add(responsibility1);
                if (!responsibility1.getParentId().equals(NumberConstant.ZERO_L)) {
                    getResponsibilityParent(responsibility1, map, addList);
                }
            }
        }
        if (!CollectionUtils.isEmpty(addList)) {
            //去重
            List<Responsibility> collect = addList.stream().distinct().collect(Collectors.toList());
            responsibilityList.addAll(collect);
        }


        Map<Long, List<Responsibility>> collect = responsibilityList.stream().collect(Collectors.groupingBy(Responsibility::getParentId));
        List<Responsibility> responsibilities = collect.get(NumberConstant.ZERO_L);
        collect.remove(NumberConstant.ZERO_L);
        for (Responsibility responsibility : responsibilities) {
            List<Responsibility> responsibilities1 = collect.get(responsibility.getId());
            if (!CollectionUtils.isEmpty(responsibilities1)) {
                responsibility.setChildren(setResponsibilityChildren(responsibilities1, collect));
            }
        }
        responsibilitySort(responsibilities);
        return responsibilities;
    }

    @Override
    public Object getHospProfitList(String computeDate, Long hospId, String reportType) {
        DateTime parse = DateUtil.parse(computeDate);
        int year = DateUtil.year(parse);
        int month = DateUtil.month(parse) + 1;
        //查询全院损益报表配置
        Integer integerReportType = Integer.valueOf(reportType);
//        Integer year = ComputeDateUtils.getComputeYear(computeDate);
//        Integer month = ComputeDateUtils.getComputeMonth(computeDate);
        // 查询所有的全院损益数据  内存溢出问题
        List<HospProfitAndLoss> hospProfitAndLosses = getAllDataByDate(year, month, hospId,integerReportType);
        if (CollectionUtils.isEmpty(hospProfitAndLosses)) {
            throw new CostException(500, "未进行全院损益计算");
        }
        //判断是否有全院的数据  有单独取出另算
        List<HospProfitAndLoss> hospAllList = hospProfitAndLosses.stream().filter(f -> f.getResponsibilityCode().equals("-1")).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(hospAllList)) {
            hospProfitAndLosses.removeAll(hospAllList);
        }
        List<ReportForm> reportFormList = reportFormService.list(new QueryWrapper<ReportForm>().lambda()
                .eq(ReportForm::getHospId, hospId)
                .eq(ReportForm::getReportType, integerReportType).eq(ReportForm::getHide, NumberConstant.ONE));
        if (CollUtil.isEmpty(reportFormList)) {
            throw new CostException(500, "未找到全院损益表配置");
        }

        List<HospProfitVO> costProfitVos = BeanUtil.convertList(reportFormList, HospProfitVO.class);
        //把所有数据按报表项目汇总
        Map<Integer, List<HospProfitAndLoss>> reportNumGroup = hospProfitAndLosses.stream().collect(Collectors.groupingBy(HospProfitAndLoss::getReportNum));
        Map<Integer, BigDecimal> reportNumSumMap = new HashMap<>();
        reportNumGroup.forEach((reportNum, hospProfitAndLossList) -> {
            AtomicReference<BigDecimal> sum = new AtomicReference<>(new BigDecimal("0.000000"));
            hospProfitAndLossList.stream().<UnaryOperator<BigDecimal>>map(hospProfitAndLoss -> f -> f.add(hospProfitAndLoss.getAmount())).forEach(sum::updateAndGet);
            reportNumSumMap.put(reportNum, sum.get());
        });

        for (HospProfitVO costProfitVo : costProfitVos) {
            BigDecimal bigDecimal = reportNumSumMap.get(costProfitVo.getNum());
            if (bigDecimal != null) {
                costProfitVo.setAmount(bigDecimal);
            } else {
                costProfitVo.setAmount(BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP));
            }
        }
        //判断有无其他全院数据  有组装进去
        if (!CollectionUtils.isEmpty(hospAllList)) {
            //全院其他收支
            HospProfitVO hospProfitVO = new HospProfitVO();
            hospProfitVO.setId(-1L);
            hospProfitVO.setNum(-1);
            hospProfitVO.setParentId(0L);
            hospProfitVO.setReportName("全院其他收支");
            hospProfitVO.setSort(costProfitVos.size());
            hospProfitVO.setReportType(integerReportType);
            hospProfitVO.setFraction(3);
            costProfitVos.add(hospProfitVO);
            for (int i = 0, hospAllListSize = hospAllList.size(); i < hospAllListSize; i++) {
                costProfitVos.add(getHospProfitVO(hospAllList, i,integerReportType));
            }
        }

        //取出所有成本项目
        List<HospProfitVO> costFractionList = costProfitVos.stream().filter(f -> f.getCostType() != null && f.getCostType().equals(NumberConstant.TWO) && f.getFraction().equals(NumberConstant.TWO)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(costFractionList)) {
            throw new CostException("全院损益未配置成本占比计算分母");
        } else {
            if (costFractionList.size() > 1) {
                throw new CostException("全院损益成本占比计算分母有且只能有一个");
            }
        }
        List<HospProfitVO> inComeFractionList = costProfitVos.stream().filter(f -> f.getCostType() != null && f.getCostType().equals(NumberConstant.ONE) && f.getFraction().equals(NumberConstant.TWO)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(inComeFractionList)) {
            throw new CostException("全院损益未配置收入占比计算分母");
        } else {
            if (inComeFractionList.size() > 1) {
                throw new CostException("全院损益收入占比计算分母有且只能有一个");
            }
        }
        HospProfitVO inComeFraction = inComeFractionList.get(0);
        HospProfitVO costFraction = costFractionList.get(0);
        for (HospProfitVO costProfitVo : costProfitVos) {
            if (costProfitVo.getCostType() != null) {
                if (costProfitVo.getCostType().equals(NumberConstant.ONE) && costProfitVo.getFraction().equals(NumberConstant.ONE)) {
                    //收入
                    BigDecimal denominator = inComeFraction.getAmount();
                    //收入分子
                    if (denominator.compareTo(BigDecimal.ZERO) == 0) {
                        costProfitVo.setPercent(BigDecimal.ZERO.setScale(4, RoundingMode.HALF_UP));
                    } else {
                        costProfitVo.setPercent(costProfitVo.getAmount().divide(denominator, 4, RoundingMode.HALF_UP));
                    }
                } else if (costProfitVo.getCostType().equals(NumberConstant.TWO) && costProfitVo.getFraction().equals(NumberConstant.ONE)) {
                    //成本
                    BigDecimal denominator = costFraction.getAmount();
                    if (denominator.compareTo(BigDecimal.ZERO) == 0) {
                        costProfitVo.setPercent(BigDecimal.ZERO.setScale(4, RoundingMode.HALF_UP));
                    } else {
                        costProfitVo.setPercent(costProfitVo.getAmount().divide(denominator, 4, RoundingMode.HALF_UP));
                    }
                }
            }
        }
        Map<Long, List<HospProfitVO>> collect = costProfitVos.stream().collect(Collectors.groupingBy(HospProfitVO::getParentId));
        List<HospProfitVO> costProfitParentVos = collect.get(NumberConstant.ZERO_L);
        collect.remove(NumberConstant.ZERO_L);
        for (HospProfitVO costProfitVo : costProfitParentVos) {
            List<HospProfitVO> costProfitVo1 = collect.get(costProfitVo.getId());
            if (!CollectionUtils.isEmpty(costProfitVo1)) {
                costProfitVo.setChild(setChildren(costProfitVo1, collect));
            }
        }
        if (!CollectionUtils.isEmpty(costProfitParentVos)) {
            costProfitParentVos.sort(Comparator.comparing(HospProfitVO::getSort, Comparator.nullsLast(Integer::compareTo)));
        }
        return costProfitParentVos;

    }

    @Override
    public Object getBatchHospProfitList(String beginComputeDate, String endComputeDate, Long hospId, Integer reportType) {
        List<HospProfitAndLoss> hospProfitAndLossByDateRange = getHospProfitAndLossByDateRange(beginComputeDate, endComputeDate,reportType);
        if (CollectionUtils.isEmpty(hospProfitAndLossByDateRange)) {
            throw new CostException(500, "未进行全院损益计算");
        }
        //获取全院损益表配置信息
        List<ReportForm> reportFormList=getReportFormList(hospId,reportType);
        if (CollectionUtils.isEmpty(reportFormList)) {
            throw new CostException(500, "未找到全院损益表配置");
        }
        //按年-月进行分组
        Map<String, List<HospProfitAndLoss>> groupedByYearMonth = hospProfitAndLossByDateRange.stream()
                .collect(Collectors.groupingBy(
                        item -> item.getDateYear() + "-" + (item.getDateMonth() < 10 ? "0" + item.getDateMonth() : item.getDateMonth())
                ));
        List<BatchCostProfitResponse> responses = new ArrayList<>();
        List<CostProfitVo> allCostProfitVo=new ArrayList<>();
        //按年-月处理科室损益数据
        for (Map.Entry<String, List<HospProfitAndLoss>> entry : groupedByYearMonth.entrySet()){
            List<HospProfitAndLoss> hospProfitAndLossList = entry.getValue();
            //转换成成本损益数据
            List<CostProfitVo> costProfitVos = convertToCostProfitVoList(hospProfitAndLossList, reportFormList,allCostProfitVo);
            //生成出参样式
            BatchCostProfitResponse response = new BatchCostProfitResponse();
            //第13月为审计月份
            if(hospProfitAndLossList.get(0).getDateMonth()==13){
                response.setComputeDate(String.format("%s-%s", hospProfitAndLossList.get(0).getDateYear(), "12A"));
                response.setAuditMonth(true);
                response.setMonthlyAccumulation(false);
            }else {
                response.setComputeDate(entry.getKey());
                response.setAuditMonth(false);
                response.setMonthlyAccumulation(false);
            }
            response.setProfitVoList(costProfitVos);
            response.setAllowDrillDown(false);
            responses.add(response);
        }
        responses.sort(Comparator.comparing(BatchCostProfitResponse::getComputeDate));
        //起止年月不同时代表有多月份需要处理月累计
        if(!beginComputeDate.equals(endComputeDate)){
            //获取月度汇总数据
            List<CostProfitVo> costProfitVos = generateSummaryForMultipleMonths(allCostProfitVo);
            //填充全院其他收支
            fillHopsOhterProfit(costProfitVos,responses);
            if(!CollectionUtils.isEmpty(costProfitVos)){
                BatchCostProfitResponse response = new BatchCostProfitResponse();
                response.setComputeDate(String.format("%s至%s", beginComputeDate, endComputeDate));
                response.setProfitVoList(costProfitVos);
                response.setAllowDrillDown(false);
                response.setMonthlyAccumulation(true);
                response.setAuditMonth(false);
                responses.add(NumberConstant.ZERO,response);
            }
        }

        return responses;
    }

    /**
     * 填充全院其他收支
     * @param multipleMonthsProfitVos
     * @param responses
     */
    public void fillHopsOhterProfit(List<CostProfitVo> multipleMonthsProfitVos,List<BatchCostProfitResponse> responses) {
        //获取累计月全院其他收支节点
        Optional<CostProfitVo> firstHopsOhterProfit = multipleMonthsProfitVos.stream().filter(costProfitVo -> costProfitVo.getReportId().equals(-1L)).findFirst();
        if(!firstHopsOhterProfit.isPresent()||CollectionUtils.isEmpty(firstHopsOhterProfit.get().getChildren())){
            return;
        }
        CostProfitVo standCostProfitVo = firstHopsOhterProfit.get();
        for (BatchCostProfitResponse response : responses) {
            //获取月度的全院其他收支节点
            Optional<CostProfitVo> firstOhterProfit = response.getProfitVoList().stream().filter(costProfitVo -> costProfitVo.getReportId().equals(-1L)).findFirst();
            //没有全院其他收支的添加一个空数据的全院其他收支
            if(!firstOhterProfit.isPresent()||CollectionUtils.isEmpty(firstOhterProfit.get().getChildren())){
                CostProfitVo costProfitVo = BeanUtil.convertObj(standCostProfitVo, CostProfitVo.class);
                costProfitVo.setChildren(new ArrayList<>());
                //添加缺失的全院其他收支项目到月度数据中
                for (CostProfitVo missChildCostProfitVo : standCostProfitVo.getChildren()) {
                    CostProfitVo newCostProfitVo = BeanUtil.convertObj(missChildCostProfitVo, CostProfitVo.class);
                    combineReportAndProfitAmount(newCostProfitVo,null);
                    costProfitVo.getChildren().add(newCostProfitVo);
                }
                response.getProfitVoList().add(costProfitVo);
                continue;
            }
            //找出月度缺失的全院其他收支
            List<CostProfitVo> missChilds = standCostProfitVo.getChildren().stream().filter(standChildCostProfitVo -> !firstOhterProfit.get().getChildren().stream().anyMatch(childCostProfitVo -> standChildCostProfitVo.getReportId().equals(childCostProfitVo.getReportId()))).collect(Collectors.toList());
            if(CollectionUtils.isEmpty(missChilds)){
                continue;
            }
            //添加缺失的全院其他收支项目到月度数据中
            for (CostProfitVo missChildCostProfitVo : missChilds) {
                CostProfitVo costProfitVo = BeanUtil.convertObj(missChildCostProfitVo, CostProfitVo.class);
                combineReportAndProfitAmount(costProfitVo,null);
                firstOhterProfit.get().getChildren().add(costProfitVo);
            }
        }
    }

    /**
     * 生成多月份汇总的科室损益数据
     *
     * @param allCostProfitVo 原始数据列表
     * @return 汇总后的科室损益数据
     */
    private List<CostProfitVo> generateSummaryForMultipleMonths(List<CostProfitVo> allCostProfitVo){
        // 参数校验
        if (CollectionUtils.isEmpty(allCostProfitVo)) {
            return Collections.emptyList();
        }
        //排除掉13月的数据
        List<CostProfitVo> activeCostProfitVo = allCostProfitVo.stream().filter(profitVo -> profitVo.getMonth() != 13).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(activeCostProfitVo)) {
            return Collections.emptyList();
        }
        List<CostProfitVo> profitVoList=new ArrayList<>() ;
        Map<Long, List<CostProfitVo>> reportGroup = allCostProfitVo.stream().collect(Collectors.groupingBy(CostProfitVo::getReportId));
        reportGroup.forEach((reportId, profitVos) -> {
            CostProfitVo costProfitVo = BeanUtil.convertObj(profitVos.get(NumberConstant.ZERO), CostProfitVo.class);
            BigDecimal totalAmount = profitVos.stream().map(vo -> Optional.ofNullable(vo.getAmount()).orElse(BigDecimal.ZERO))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal totalBudgetAmount = profitVos.stream().map(vo -> Optional.ofNullable(vo.getBudgetAmount()).orElse(BigDecimal.ZERO))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal totalPrevPeriodAmount = profitVos.stream().map(vo -> Optional.ofNullable(vo.getPrevPeriodAmount()).orElse(BigDecimal.ZERO))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal totalSamePeriodAmount = profitVos.stream().map(vo -> Optional.ofNullable(vo.getSamePeriodAmount()).orElse(BigDecimal.ZERO))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            costProfitVo.setChildren(null);
            costProfitVo.setAmount(totalAmount);
            costProfitVo.setBudgetAmount(totalBudgetAmount);
            costProfitVo.setPrevPeriodAmount(totalPrevPeriodAmount);
            costProfitVo.setSamePeriodAmount(totalSamePeriodAmount);
            costProfitVo.setCompletionRate(calculateProfitRate(costProfitVo.getAmount(), costProfitVo.getBudgetAmount()));
            costProfitVo.setMomRate(calculateProfitRate(costProfitVo.getAmount().subtract(costProfitVo.getPrevPeriodAmount()), costProfitVo.getPrevPeriodAmount()));
            costProfitVo.setYoyRate(calculateProfitRate(costProfitVo.getAmount().subtract(costProfitVo.getSamePeriodAmount()), costProfitVo.getSamePeriodAmount()));
            profitVoList.add(costProfitVo);
        });
        //转成树状结构
        List<CostProfitVo> costProfitVoTree = converToCostProfitVoTree(profitVoList);
        return costProfitVoTree;
    }

    /**
     * 转换为多层级样式全院损益数据
     * @param hospProfitAndLossList
     * @param reportFormList
     * @return
     */
    public  List<CostProfitVo> convertToCostProfitVoList(List<HospProfitAndLoss> hospProfitAndLossList,List<ReportForm> reportFormList,List<CostProfitVo> allCostProfitVo ){
        //全院其他收支项目
        List<HospProfitAndLoss> otherHospProfitAndLoss = hospProfitAndLossList.stream().filter(i -> NumberConstant.ONE.equals(i.getOriginType())).collect(Collectors.toList());
        //按报表项目编号map
        Map<Integer, HospProfitAndLoss> hospProfitAndLossMap = hospProfitAndLossList.stream().collect(Collectors.toMap(HospProfitAndLoss::getReportNum, b -> b));
        //报表项目转换成多层级损益对象
        List<CostProfitVo> costProfitVos =reportFormList.stream().map(reportForm -> reportFormToProfitVo(reportForm)).collect(Collectors.toList());
        //组装全院损益及报表项目信息
        for (CostProfitVo profitVo : costProfitVos) {
            //处理年月数据
            profitVo.setYear(hospProfitAndLossList.get(0).getDateYear());
            profitVo.setMonth(hospProfitAndLossList.get(0).getDateMonth());
            //添加损益数据
            HospProfitAndLoss hospProfitAndLoss = hospProfitAndLossMap.get(profitVo.getReportNum());
            //组装全院损益及报表项目金额信息
            combineReportAndProfitAmount(profitVo,hospProfitAndLoss);
        }
        //有全院其他收支时添加全院其他收支
        if(!CollectionUtils.isEmpty(otherHospProfitAndLoss)){
            //找到配置的损益项目
            Optional<ReportForm> firstLossReport = reportFormList.stream().filter(i -> NumberConstant.ONE.equals(i.getIsLoss())).findFirst();
            if(!firstLossReport.isPresent()){
                throw new CostException(500, "全院损益报表配置有误，未设置损益项目");
            }
            //生成一个虚拟的全院其他收支
            CostProfitVo otherProfit = getOtherProfit(reportFormList);
            //处理年月数据
            otherProfit.setYear(otherHospProfitAndLoss.get(NumberConstant.ZERO).getDateYear());
            otherProfit.setMonth(otherHospProfitAndLoss.get(NumberConstant.ZERO).getDateMonth());
            costProfitVos.add(otherProfit);
            ReportForm reportForm = firstLossReport.get();
            for (int i = 0; i < otherHospProfitAndLoss.size(); i++) {
                HospProfitAndLoss hospProfitAndLoss = otherHospProfitAndLoss.get(i);
                CostProfitVo costProfitVo =reportFormToProfitVo(reportForm);
                //组装全院损益及报表项目金额信息
                combineReportAndProfitAmount(costProfitVo,hospProfitAndLoss);
                //处理年月数据
                costProfitVo.setYear(hospProfitAndLoss.getDateYear());
                costProfitVo.setMonth(hospProfitAndLoss.getDateMonth());
                //全院其他收支项目放在虚拟的全院其他收支下
                costProfitVo.setSort(i);
                //添加一个负数作为虚拟的报表项目编号
                Integer reportNum =hospProfitAndLoss.getReportNum();
                costProfitVo.setReportNum(reportNum);
                costProfitVo.setId(reportNum.longValue());
                costProfitVo.setReportId(reportNum.longValue());
                costProfitVo.setReportName(hospProfitAndLoss.getReportName());
                costProfitVo.setReportParentId(otherProfit.getReportId());
                costProfitVos.add(costProfitVo);
            }
        }
        //记录所有月份的损益数据
        allCostProfitVo.addAll(costProfitVos);
        //转成树状结构的损益数据
        List<CostProfitVo> costProfitVoTree = converToCostProfitVoTree(costProfitVos);
        return costProfitVoTree;
    }

    /**
     * 生成一个虚拟的全院其他收支
     * @param reportFormList
     * @return
     */
    public CostProfitVo getOtherProfit(List<ReportForm> reportFormList) {
        //全院其他收支
        CostProfitVo hospProfitVO = reportFormToProfitVo(reportFormList.get(NumberConstant.ZERO));
        hospProfitVO.setId(-1L);
        hospProfitVO.setReportId(-1L);
        hospProfitVO.setReportNum(-1);
        hospProfitVO.setReportParentId(0L);
        hospProfitVO.setReportName("全院其他收支");
        hospProfitVO.setSort(reportFormList.size());
        return hospProfitVO;
    }

    /**
     * 报表项目转换成多层级损益对象
     * @param reportForm
     * @return
     */
    public CostProfitVo reportFormToProfitVo(ReportForm reportForm){
        CostProfitVo profitVo = new CostProfitVo();
        profitVo.setId(reportForm.getId());
        profitVo.setReportId(reportForm.getId());
        profitVo.setReportName(reportForm.getReportName());
        profitVo.setReportParentId(reportForm.getParentId());
        profitVo.setReportNum(reportForm.getNum());
        profitVo.setCalcType(reportForm.getCalcType());
        profitVo.setCalcFormula(reportForm.getCalcFormula());
        profitVo.setCostType(reportForm.getCostType());
        profitVo.setShareType(reportForm.getReportType());
        profitVo.setSort(reportForm.getSort());
        profitVo.setDescription(reportForm.getDescription());
        profitVo.setDataType(reportForm.getDataType());
        profitVo.setDecimalPlace(reportForm.getDecimalPlace());
        profitVo.setPermil(reportForm.getPermil());
        profitVo.setFontColor(reportForm.getFontColor());
        return profitVo;
    }

    /**
     * 组装全院损益及报表项目的金额信息
     * @param profitVo
     * @param hospProfitAndLoss
     */
    public void combineReportAndProfitAmount(CostProfitVo profitVo, HospProfitAndLoss hospProfitAndLoss) {
        if (ObjectUtils.isEmpty(hospProfitAndLoss)) {
            profitVo.setAmount(BigDecimal.ZERO);
            profitVo.setSamePeriodAmount(BigDecimal.ZERO);
            profitVo.setPrevPeriodAmount(BigDecimal.ZERO);
            profitVo.setBudgetAmount(BigDecimal.ZERO);
            profitVo.setYoyRate(BigDecimal.ZERO);
            profitVo.setMomRate(BigDecimal.ZERO);
            profitVo.setCompletionRate(BigDecimal.ZERO);
        } else {
            profitVo.setAmount(hospProfitAndLoss.getAmount());
            profitVo.setSamePeriodAmount(hospProfitAndLoss.getSamePeriodAmount());
            profitVo.setPrevPeriodAmount(hospProfitAndLoss.getPrevPeriodAmount());
            profitVo.setBudgetAmount(hospProfitAndLoss.getBudgetAmount());
            profitVo.setYoyRate(hospProfitAndLoss.getYoyRate());
            profitVo.setMomRate(hospProfitAndLoss.getMomRate());
            profitVo.setCompletionRate(hospProfitAndLoss.getCompletionRate());
        }
    }

    /**
     * 转成树状结构的损益数据
     * @param costProfitVos
     * @return
     */
    public List<CostProfitVo> converToCostProfitVoTree(List<CostProfitVo> costProfitVos){
        // 参数校验
        if (CollectionUtils.isEmpty(costProfitVos)) {
            return Collections.emptyList();
        }
        Map<Long, List<CostProfitVo>> collect = costProfitVos.stream().collect(Collectors.groupingBy(CostProfitVo::getReportParentId));
        List<CostProfitVo> costProfitParentVos = collect.get(NumberConstant.ZERO_L);
        collect.remove(NumberConstant.ZERO_L);
        for (CostProfitVo costProfitVo : costProfitParentVos) {
            List<CostProfitVo> costProfitVo1 = collect.get(costProfitVo.getReportId());
            if (!CollectionUtils.isEmpty(costProfitVo1)) {
                costProfitVo.setChildren(setChildrenProfitVo(costProfitVo1, collect));
            }
        }
        if (!CollectionUtils.isEmpty(costProfitParentVos)) {
            costProfitParentVos.sort(Comparator.comparing(CostProfitVo::getSort, Comparator.nullsLast(Integer::compareTo)));
        }
        return  costProfitParentVos;
    }

    private List<CostProfitVo> setChildrenProfitVo(List<CostProfitVo> child, Map<Long, List<CostProfitVo>> collect) {
        for (CostProfitVo costProfitVo : child) {
            if (!CollectionUtils.isEmpty(collect.get(costProfitVo.getReportId()))) {
                costProfitVo.setChildren(setChildrenProfitVo(collect.get(costProfitVo.getReportId()), collect));
            }
        }
        if (!CollectionUtils.isEmpty(child)) {
            child.sort(Comparator.comparing(CostProfitVo::getSort, Comparator.nullsLast(Integer::compareTo)));
        }
        return child;
    }

    /**
     * 获取全院损益表配置信息
     * @param hospId
     * @param reportType
     * @return
     */
    public List<ReportForm> getReportFormList(Long hospId,Integer reportType ) {
        List<ReportForm> reportFormList = reportFormService.list(new QueryWrapper<ReportForm>().lambda()
                .eq(ReportForm::getHospId, hospId)
                .eq(ReportForm::getReportType, reportType)
                .eq(ReportForm::getDeleteTime , NumberConstant.ZERO)
                .eq(ReportForm::getHide, NumberConstant.ONE));
        return  reportFormList;
    }


    /**
     * 获取指定年月区间的全院损益数据
     * @param beginComputeDate
     * @param endComputeDate
     * @return
     */
    public List<HospProfitAndLoss> getHospProfitAndLossByDateRange(String beginComputeDate, String endComputeDate, Integer reportType) {
        // 解析开始和结束时间
        int startYear = Integer.parseInt(beginComputeDate.split("-")[0]);
        int startMonth = Integer.parseInt(beginComputeDate.split("-")[1]);
        int endYear = Integer.parseInt(endComputeDate.split("-")[0]);
        int endMonth = Integer.parseInt(endComputeDate.split("-")[1]);

        if( startYear * 100 + startMonth> endYear * 100 + endMonth){
            throw new CostException("开始时间不能大于结束时间");
        }
        //如果截止月份是12月要自动添加第13月的数据
        if(endMonth==12)
        {
            endMonth=13;
        }
        List<HospProfitAndLoss> list = this.list(new QueryWrapper<HospProfitAndLoss>().lambda()
                .eq(HospProfitAndLoss::getDeleteTime, NumberConstant.ZERO)
                .eq(HospProfitAndLoss::getHospId, UserContext.getHospId())
                .eq(HospProfitAndLoss::getReportType, reportType)
                .apply("(date_year * 100 + date_month) >= {0}", startYear * 100 + startMonth)
                .apply("(date_year * 100 + date_month) <= {0}", endYear * 100 + endMonth));
        return list;
    }

    private static @NotNull HospProfitVO getHospProfitVO(List<HospProfitAndLoss> hospAllList, int i,int reportType) {
        HospProfitAndLoss hospProfitAndLoss = hospAllList.get(i);
        HospProfitVO hospProfitVO1 = new HospProfitVO();
        hospProfitVO1.setId(Long.valueOf(hospProfitAndLoss.getReportNum()));
        hospProfitVO1.setNum(hospProfitAndLoss.getReportNum());
        hospProfitVO1.setParentId(-1L);
        hospProfitVO1.setReportName(hospProfitAndLoss.getReportName());
        hospProfitVO1.setSort(i);
        hospProfitVO1.setReportType(reportType);
        hospProfitVO1.setAmount(hospProfitAndLoss.getAmount());
        hospProfitVO1.setFraction(3);
        return hospProfitVO1;
    }

    private List<HospProfitAndLoss> getAllDataByDate(int year, int month, Long hospId,int reportType) {
        return this.list(
                new LambdaQueryWrapper<HospProfitAndLoss>()
                        .eq(HospProfitAndLoss::getHospId, hospId)
                        .eq(HospProfitAndLoss::getDateMonth, month)
                        .eq(HospProfitAndLoss::getDateYear, year)
                        .eq(HospProfitAndLoss::getReportType, reportType)
        );
    }


    /**
     * @param date
     * @param hospId
     */
    @Override
    public void calcByResponsibility(String date, Long hospId) {
        calcHospProfit(date,hospId,ReportTypeEnum.HOSP_PROFIT_LOSS.getType());
    }

    private void setCalculationAmount(List<HospProfitAndLossVo> listVo) {
        Map<Integer, List<HospProfitAndLossVo>> collect = listVo.stream().collect(Collectors.groupingBy(HospProfitAndLossVo::getCalcType));
        //取出所有计算公式数据
        List<HospProfitAndLossVo> costDepartmentProfitVOS = collect.get(NumberConstant.FOUR);

        costDepartmentProfitVOS.sort(Comparator.comparing(HospProfitAndLossVo::getReportNum, Comparator.nullsLast(Integer::compareTo)));
        for (HospProfitAndLossVo profitVO : costDepartmentProfitVOS) {
            // 获取当前报表的计算方式  [1]+[2]类型/ [1]-[2] [1]*[2] [1]/[2]
            String formula = profitVO.getCalcFormula();
            //找出公式当中所有代码
            String replace = formula.replace("[", "")
                    .replace("]", "")
                    .replace("-", ",")
                    .replace("+", ",")
                    .replace("*", ",")
                    .replace("/", ",");
            ArrayList<String> codeList = CollUtil.newArrayList(replace.split(","));
            Map<Integer, String> codeMap = new ConcurrentHashMap<>();
            for (int j = 0; j < codeList.size(); j++) {
                codeMap.put(j, codeList.get(j));
            }
            List<String> expressions = ReUtil.findAll("[^0-9]", "+" + formula.replace("[", "")
                            .replace("]", "").trim(), 0)
                    .stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
            // 得到预算表达式 得到所有表达式的Map  +  -  相关的
            Map<Integer, String> expressionMap = new ConcurrentHashMap<>();
            for (int k = 0; k < expressions.size(); k++) {
                expressionMap.put(k, expressions.get(k));
            }
            // 数字的索引和表达式的索引累加计算
            Set<Integer> codeSet = codeMap.keySet();
            List<Integer> codes = new ArrayList<>(codeSet);
            AtomicReference<BigDecimal> totalAmount = new AtomicReference<>(new BigDecimal("0.000000"));
            for (int i = 0; i < codes.size(); i++) {
                // 编号
                String code = codeMap.get(i);
                BigDecimal amount = null;
                for (HospProfitAndLossVo costDepartmentProfitVO : listVo) {
                    if (costDepartmentProfitVO.getReportNum().equals(Integer.valueOf(code))) {
                        amount = costDepartmentProfitVO.getAmount();
                    }
                }
                if (amount == null) {
                    continue;
                }
//                    BigDecimal amount = new BigDecimal(o.toString());
                String str = expressionMap.get(i);
                if (str.equals("+")) {
                    totalAmount.set(totalAmount.get().add(amount));
                } else if (str.contains("-")) {
                    totalAmount.set(totalAmount.get().subtract(amount));
                } else if (str.contains("*")) {
                    totalAmount.set(totalAmount.get().multiply(amount));
                } else if (str.contains("/")) {
                    if (amount.compareTo(BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP)) == 0) {
                        totalAmount.set(BigDecimal.ZERO);
                        break;
                    } else {
                        totalAmount.set(totalAmount.get().divide(amount, 6, RoundingMode.HALF_UP));
                    }
                }
            }
            profitVO.setAmount(totalAmount.get());
        }

    }


    /**
     * 按照责任中心进行计算
     * 原始责任中心是设置的责任中心   目标责任中心是报表的责任中心
     * 查询分摊报表里面目标责任中心是当前责任中心  报表责任中心是当前设置的责任中心
     */
    public BigDecimal setResponsibilityCode(HospProfitAndLossVo costDepartmentProfitVO, Map<Long, List<ReportRelation>> reportRelationMap, List<Allocation> allocationList, List<HospProfitAndLossVo> allList) {
        // 获取当前报表对应的责任中心
        List<ReportRelation> reportRelationList = reportRelationMap.get(costDepartmentProfitVO.getReportId());
        AtomicReference<BigDecimal> sum = new AtomicReference<>(new BigDecimal("0.000"));
        if (CollUtil.isNotEmpty(reportRelationList)) {
            // 获取对应的责任中心的Code集合  这个是设置的责任中心
            List<String> responsibilityCodes = reportRelationList.stream().map(ReportRelation::getRelationCode).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(responsibilityCodes)) {
                // 查询报表里面是当前分摊层级的数据
                List<Allocation> allocations = allocationList.stream().filter(i -> i.getTargetResponsibilityCode().equals(costDepartmentProfitVO.getResponsibilityCode()) && responsibilityCodes.contains(i.getResponsibilityCode())).collect(Collectors.toList());
                if (CollUtil.isNotEmpty(allocations)) {
                    BigDecimal reduce = allocations.stream().map(Allocation::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                    sum.set(reduce);
//                    allocations.forEach(m -> {
//                        sum.updateAndGet(v -> v.add(m.getAmount()));
//                    });
                }
            }
        }
//        costDepartmentProfitVO.setAmount(new BigDecimal(sum.toString()));
        return sum.get();
    }

    /***
     * 按照计算方式进行计算
     */
    public BigDecimal setCalculation(HospProfitAndLossVo costDepartmentProfitVO, List<HospProfitAndLossVo> costDepartmentProfitVOList,
                                     List<CostShareLevel> costShareLevelList, Map<Long, List<HospProfitAndLossVo>> listMap,
                                     List<IncomeCollection> list, List<AllocationQueryReportVO> allocationQueryReportVOList,
                                     Map<Long, List<ReportRelation>> reportRelationMap, List<Allocation> allocationList, List<HospProfitAndLossVo> allList) {
        // 获取当前报表的计算方式  [1]+[2]类型/ [1]-[2]
        String calcFormula = costDepartmentProfitVO.getCalcFormula();
        String responsibilityCode = costDepartmentProfitVO.getResponsibilityCode();
        String replace = calcFormula.replace("[", "").replace("]", "").replace("+", ",").replace("-", ",-");
        List<Integer> calcFormulaList = Arrays.stream(replace.split(StrUtil.COMMA)).map(Integer::valueOf).collect(Collectors.toList());
        // 查询这个编号集合的报表
        AtomicReference<BigDecimal> bigDecimal = new AtomicReference<>(new BigDecimal("0.0000"));
        calcFormulaList.forEach(calc -> {
            Integer calcNum = Math.abs(calc);
            List<Long> reportIdList = allList.stream().filter(i -> i.getResponsibilityCode().equals(responsibilityCode) && calcNum.equals(i.getReportNum())).map(HospProfitAndLossVo::getReportId).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(reportIdList)) {
                reportIdList.forEach(i -> {
                    List<HospProfitAndLossVo> profitVOS = listMap.get(i);
                    if (CollUtil.isEmpty(profitVOS)) {
                        throw new CostException(500, "报表未找到");
                    }
                    BigDecimal amount = getAmount(profitVOS, costShareLevelList, responsibilityCode, list, allocationQueryReportVOList, reportRelationMap, allocationList, listMap, allList);
                    if (calc > 0) {
                        bigDecimal.updateAndGet(v -> v.add(amount));
                    } else {
                        bigDecimal.updateAndGet(v -> v.subtract(amount));
                    }
                });
            }

        });
        return bigDecimal.get();
    }


    /**
     * 按照小计的计算方式
     * 同一个目录下相同的其他金额的和
     */
    public BigDecimal setSubtotal(HospProfitAndLossVo costDepartmentProfitVO,
                                  List<CostShareLevel> costShareLevelList,
                                  Map<Long, List<HospProfitAndLossVo>> listMap,
                                  List<HospProfitAndLossVo> profitVOS,
                                  List<IncomeCollection> list,
                                  List<AllocationQueryReportVO> allocationQueryReportVOList,
                                  Map<Long, List<ReportRelation>> reportRelationMap, List<Allocation> allocationList, List<HospProfitAndLossVo> allList) {
        // 因为报表是按照报表项目的升序进行排序的  前面的报表是已经进行计算了的
        // 查询当前报表的父层级id
        Long reportParentId = costDepartmentProfitVO.getReportParentId();
        String responsibilityCode = costDepartmentProfitVO.getResponsibilityCode();
        // 查询报表里面在当前父层级下的并且不是自己的报表项目
        List<HospProfitAndLossVo> costDepartmentProfitVOS = allList.stream().filter(i -> i.getReportParentId().equals(reportParentId) && i.getResponsibilityCode().equals(responsibilityCode) && !NumberConstant.THREE.equals(i.getCalcType())).collect(Collectors.toList());
        AtomicReference<BigDecimal> sum = new AtomicReference<>(new BigDecimal("0.0000"));
        // 遍历数据 之前的已经经过了计算
        costDepartmentProfitVOS.forEach(i -> {
            Long reportId = i.getReportId();
            List<HospProfitAndLossVo> costDepartmentProfitVOS1 = listMap.get(reportId);
            if (CollUtil.isEmpty(costDepartmentProfitVOS1)) {
                throw new CostException(500, "报表未找到");
            }
            BigDecimal amount = getAmount(costDepartmentProfitVOS1, costShareLevelList, responsibilityCode, list, allocationQueryReportVOList, reportRelationMap, allocationList, listMap, allList);
            sum.set(amount.add(sum.get()));
            ;
        });
//        costDepartmentProfitVO.setAmount(new BigDecimal(sum.toString()));
        return sum.get();
    }

    /**
     * 判断是那种计算方式 调用方法进行计算
     */
    public BigDecimal getAmount(List<HospProfitAndLossVo> profitVOS,
                                List<CostShareLevel> costShareLevelList,
                                String responsibilityCode, List<IncomeCollection> list,
                                List<AllocationQueryReportVO> allocationQueryReportVOList,
                                Map<Long, List<ReportRelation>> reportRelationMap, List<Allocation> allocationList,
                                Map<Long, List<HospProfitAndLossVo>> listMap, List<HospProfitAndLossVo> allList) {
        BigDecimal bigDecimal = new BigDecimal("0.0000");
        // 在对这个报表进行过滤
        List<HospProfitAndLossVo> costDepartmentProfitVOS = profitVOS.stream().filter(i -> i.getResponsibilityCode().equals(responsibilityCode)).collect(Collectors.toList());
        // 都一个就是
        HospProfitAndLossVo costDepartmentProfitVO = costDepartmentProfitVOS.get(0);
        Integer calcType = costDepartmentProfitVO.getCalcType();
        if (NumberConstant.ONE.equals(calcType)) {
            // 调用计算的方法 按照会计科目
            bigDecimal = setAccountReportData(costDepartmentProfitVO, list, allocationQueryReportVOList, reportRelationMap);
        } else if (NumberConstant.TWO.equals(calcType)) {
            // 按照分摊层级
            bigDecimal = setShareLevelReportData(costDepartmentProfitVO, reportRelationMap, allocationList);
        } else if (NumberConstant.THREE.equals(calcType)) {
            // 小计
            bigDecimal = setSubtotal(costDepartmentProfitVO, costShareLevelList, listMap, profitVOS, list, allocationQueryReportVOList, reportRelationMap, allocationList, allList);
        } else if (NumberConstant.FIVE.equals(calcType)) {
            // 按照责任中心
            bigDecimal = setResponsibilityCode(costDepartmentProfitVO, reportRelationMap, allocationList, allList);
        } else if (NumberConstant.FOUR.equals(calcType)) {
            bigDecimal = setCalculation(costDepartmentProfitVO, costDepartmentProfitVOS, costShareLevelList, listMap, list, allocationQueryReportVOList, reportRelationMap, allocationList, allList);
        }
        return bigDecimal;
    }

    private BigDecimal setAccountReportData(HospProfitAndLossVo i, List<IncomeCollection> list, List<AllocationQueryReportVO> allocationQueryReportVOList, Map<Long, List<ReportRelation>> reportRelationMap) {
        // 在报表关联里面查询当前报表关联的
        Long reportId = i.getReportId();
        List<ReportRelation> reportRelationList = reportRelationMap.get(reportId);
        AtomicReference<BigDecimal> sum = new AtomicReference<>(new BigDecimal("0.000"));
        if (CollUtil.isNotEmpty(reportRelationList)) {
            // 获取对应的会计科目信息  筛选会计科目的Code
            List<String> accountList = reportRelationList.stream().map(ReportRelation::getRelationCode).collect(Collectors.toList());
            // 查找在归集数据里面当前责任中心对应的这些会计科目的金额
            List<IncomeCollection> incomeCollectionList = list.stream().filter(income -> income.getResponsibilityCode().equals(i.getResponsibilityCode()) && accountList.contains(income.getAccountingCode())).collect(Collectors.toList());
            // 需要查询分摊后的表
            List<AllocationQueryReportVO> reportVOList = allocationQueryReportVOList.stream().filter(m -> m.getTargetResponsibilityCode().equals(i.getResponsibilityCode()) && accountList.contains(m.getAccountingCode())).collect(Collectors.toList());

            if (CollUtil.isNotEmpty(incomeCollectionList)) {
                incomeCollectionList.forEach(m -> {
                    sum.updateAndGet(v -> v.add(m.getAmount()));
                });
            }
            // 成本减
            if (CollUtil.isNotEmpty(reportVOList)) {
                reportVOList.forEach(m -> {
                    sum.updateAndGet(v -> v.add(m.getAmount()));
                });
            }
        }
//        i.setAmount(new BigDecimal(sum.toString()));
//        numMap.put(i.getReportNum()+i.getResponsibilityCode(),new BigDecimal(sum.toString()));
        return sum.get();
    }

    private BigDecimal setShareLevelReportData(HospProfitAndLossVo i, Map<Long, List<ReportRelation>> reportRelationMap, List<Allocation> allocationList) {
        List<ReportRelation> reportRelationList = reportRelationMap.get(i.getReportId());
        AtomicReference<BigDecimal> sum = new AtomicReference<>(new BigDecimal("0.000"));
        if (CollUtil.isNotEmpty(reportRelationList)) {
            // 找到对应的分摊层级的Id  但是分摊报表里面存的是分摊层级的序号
            List<Long> shareLevelIds = reportRelationList.stream().map(ReportRelation::getRelationCode).map(Long::valueOf).collect(Collectors.toList());
//            List<Integer> levelShortList = costShareLevelList.stream()
//                    .filter(m -> shareLevelIds.contains(m.getId()))
//                    .map(CostShareLevel::getLeverSort).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(shareLevelIds)) {
                // 查询报表里面是当前分摊层级的数据
                List<Allocation> allocations = allocationList.stream().filter(m -> shareLevelIds.contains(m.getShareLevelId()) && m.getTargetResponsibilityCode().equals(i.getResponsibilityCode())).collect(Collectors.toList());
                if (CollUtil.isNotEmpty(allocations)) {
//                    allocations.forEach(m -> {
//                        sum.updateAndGet(v -> v.add(m.getAmount()));
//                    });
                    BigDecimal reduce = allocations.stream().map(Allocation::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                    sum.set(reduce);
                }
            }
        }
//        i.setAmount(new BigDecimal(sum.toString()));
        return sum.get();
    }


    private void calcByLitterCountResp(HospProfitAndLossVo lossVo, Map<Long, List<HospProfitAndLossVo>> listParentMap, List<HospProfitAndLossVo> listVo, Long hospId) {
        //lossVo  当前 小计对象
        //listParentMap   与小计同级别的对象
        //listVo 存储计算过的对象
        // 通地当前小计 找出 同级别需要合计的对象 再从计算过的对象取出数值合计
        Long reportParentId = lossVo.getReportParentId();
        List<HospProfitAndLossVo> hospProfitAndLossVos = listParentMap.get(reportParentId);
        //去除小计本身
        hospProfitAndLossVos.remove(lossVo);
        List<Integer> reportNums = hospProfitAndLossVos.stream().map(HospProfitAndLossVo::getReportNum).collect(Collectors.toList());
        List<HospProfitAndLossVo> collect = listVo.stream().filter(i -> reportNums.contains(i.getReportNum()) && i.getResponsibilityCode().equals(lossVo.getResponsibilityCode())).collect(Collectors.toList());
        if (collect.isEmpty()) {
            return;
        }
        BigDecimal reduce = collect.stream().map(HospProfitAndLossVo::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        lossVo.setAmount(reduce);
    }

    private List<HospProfitAndLossVo> costPercent(List<HospProfitAndLossVo> profitIncomeList) {
        List<HospProfitAndLossVo> list = new ArrayList<>();
        Map<Integer, List<HospProfitAndLossVo>> collect = profitIncomeList.stream().collect(Collectors.groupingBy(HospProfitAndLossVo::getFraction));
        //分母只有一个
        List<HospProfitAndLossVo> denominatorList = collect.get(NumberConstant.TWO);
        if (denominatorList.size() > 1) {
            throw new CostException("收入项目分母大于一个，请查证后再计算科室损益");
        }
        BigDecimal denominator = denominatorList.get(0).getAmount();
        //收入分子
        List<HospProfitAndLossVo> numeratorList = collect.get(NumberConstant.ONE);
        if (denominator.compareTo(BigDecimal.ZERO) == 0) {
            for (HospProfitAndLossVo profitVO : numeratorList) {
                profitVO.setPercent(BigDecimal.ZERO.setScale(4, RoundingMode.HALF_UP));
            }
            denominatorList.get(0).setPercent(BigDecimal.ZERO.setScale(4, RoundingMode.HALF_UP));

        } else {
            for (HospProfitAndLossVo profitVO : numeratorList) {
                profitVO.setPercent(profitVO.getAmount().divide(denominator, 4, RoundingMode.HALF_UP));
            }
            denominatorList.get(0).setPercent(BigDecimal.ONE.setScale(4, RoundingMode.HALF_UP));
        }
        list.add(denominatorList.get(0));
        list.addAll(numeratorList);



        return list;

    }

    private void calcByResponsibilityResp(Long hospId, HospProfitAndLossVo lossVo) {

        List<RelationVO> responsibilities = reportRelationService.getResponsibilities(lossVo.getReportId(), hospId);
        if (responsibilities.isEmpty()) {
            return;
        }
        List<String> responsibilityCodes = responsibilities.stream().map(RelationVO::getCode).collect(Collectors.toList());
        AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
        calcTotal.set(BigDecimal.ZERO);
        List<AllocationQuery> allocationQueries = allocationQueryService.getByDateAndResp(lossVo.getDateYear(), lossVo.getDateMonth(), hospId, lossVo.getResponsibilityCode());
        responsibilityCodes.forEach(i -> {
            BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getResponsibilityCode())).map(AllocationQuery::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            calcTotal.set(calcTotal.get().add(costAmount));
        });
        lossVo.setAmount(calcTotal.get());

    }

    private void calcByResponsibilityResp(Long hospId, ReportForm child, List<HospProfitAndLoss> list, int year, int month, Responsibility responsibility) {

        List<RelationVO> responsibilities = reportRelationService.getResponsibilities(child.getId(), hospId);
        if (responsibilities.isEmpty()) {
            return;
        }
        List<String> responsibilityCodes = responsibilities.stream().map(RelationVO::getCode).collect(Collectors.toList());
        AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
        calcTotal.set(BigDecimal.ZERO);
        List<AllocationQuery> allocationQueries = allocationQueryService.getByDateAndResp(year, month, hospId, responsibility.getResponsibilityCode());
        responsibilityCodes.forEach(i -> {
            BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getResponsibilityCode())).map(AllocationQuery::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            calcTotal.set(calcTotal.get().add(costAmount));
        });
        HospProfitAndLoss loss = new HospProfitAndLoss();
        loss.setReportName(child.getReportName()).setReportNum(child.getNum()).setResponsibilityCode(responsibility.getResponsibilityCode()).setResponsibilityName(responsibility.getResponsibilityName())
                .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month);
        list.add(loss);

    }

    private void calcByFormulaResp(HospProfitAndLossVo lossVo, List<HospProfitAndLossVo> list) {
        // 得到这个责任中心下所有的已经收集的数据
        List<HospProfitAndLossVo> hadCalcLosses = list.stream().filter(i -> i.getResponsibilityCode().equals(lossVo.getResponsibilityCode())).collect(Collectors.toList());
        String calcFormula = lossVo.getCalcFormula();
        // TODO: 2021/8/27 校验公式合法性
        if (StrUtil.isBlank(calcFormula)) {
            throw new CostException("reportForm名称为" + lossVo.getReportName() + "计算公式不正确");
        }
        BigDecimal bigDecimal = calcAmountRes(hadCalcLosses, calcFormula);
        lossVo.setAmount(bigDecimal);


    }

    private void calcByFormulaResp(int year, int month, ReportForm child, List<HospProfitAndLoss> list, Responsibility responsibility) {
        // 得到这个责任中心下所有的已经收集的数据
        List<HospProfitAndLoss> hadCalcLosses = list.stream().filter(i -> i.getResponsibilityCode().equals(responsibility.getResponsibilityCode())).collect(Collectors.toList());
        String calcFormula = child.getCalcFormula();
        // TODO: 2021/8/27 校验公式合法性
        if (StrUtil.isBlank(calcFormula)) {
            throw new CostException("reportForm名称为" + child.getReportName() + "计算公式不正确");
        }
        BigDecimal bigDecimal = calcAmountResp(hadCalcLosses, calcFormula);

        HospProfitAndLoss loss = new HospProfitAndLoss();
        loss.setDateYear(year).setDateMonth(month).setReportNum(child.getNum()).setReportName(child.getReportName())
                .setAmount(bigDecimal).setCreateTime(System.currentTimeMillis()).setHospId(child.getHospId()).setResponsibilityCode(responsibility.getResponsibilityCode()).setResponsibilityName(responsibility.getResponsibilityName());
        list.add(loss);

    }

    private BigDecimal calcAmountRes(List<HospProfitAndLossVo> hadCalcLosses, String calcFormula) {
        // 得到所有的编号
        String replace = calcFormula.replace("[", "").replace("]", "").replace("-", ",").replace("+", ",");
        ArrayList<String> numList = CollUtil.newArrayList(replace.split(","));
        // 得到编号的map
        Map<Integer, Integer> numMap = new ConcurrentHashMap<>();
        for (int j = 0; j < numList.size(); j++) {
            numMap.put(j, Integer.parseInt(numList.get(j)));
        }
        List<String> expressions = ReUtil.findAll("[^0-9]", "+" + calcFormula.replace("[", "").replace("]", "").trim(), 0)
                .stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
        // 得到预算表达式 得到所有表达式的Map  +  -  相关的
        Map<Integer, String> expressionMap = new ConcurrentHashMap<>();
        for (int k = 0; k < expressions.size(); k++) {
            expressionMap.put(k, expressions.get(k));
        }
        // 数字的索引和表达式的索引累加计算
        Set<Integer> numSet = numMap.keySet();
        List<Integer> nums = new ArrayList<>(numSet);
        AtomicReference<BigDecimal> totalAmount = new AtomicReference<>();
        totalAmount.set(BigDecimal.ZERO);
        for (int i = 0; i < nums.size(); i++) {
            // 编号
            Integer num = numMap.get(i);
            List<HospProfitAndLossVo> losses = hadCalcLosses.stream().filter(item -> item.getReportNum().equals(num)).collect(Collectors.toList());
            if (CollUtil.isEmpty(losses)) {
                continue;
            }
            HospProfitAndLossVo loss = losses.get(0);
            BigDecimal amount = loss.getAmount();
            String str = expressionMap.get(i);
            if (str.equals("+")) {
                totalAmount.set(totalAmount.get().add(amount));
            } else {
                totalAmount.set(totalAmount.get().subtract(amount));
            }
        }
        return totalAmount.get();

    }

    private BigDecimal calcAmountResp(List<HospProfitAndLoss> hadCalcLosses, String calcFormula) {
        // 得到所有的编号
        String replace = calcFormula.replace("[", "").replace("]", "").replace("-", ",").replace("+", ",");
        ArrayList<String> numList = CollUtil.newArrayList(replace.split(","));
        // 得到编号的map
        Map<Integer, Integer> numMap = new ConcurrentHashMap<>();
        for (int j = 0; j < numList.size(); j++) {
            numMap.put(j, Integer.parseInt(numList.get(j)));
        }
        List<String> expressions = ReUtil.findAll("[^0-9]", "+" + calcFormula.replace("[", "").replace("]", "").trim(), 0)
                .stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
        // 得到预算表达式 得到所有表达式的Map  +  -  相关的
        Map<Integer, String> expressionMap = new ConcurrentHashMap<>();
        for (int k = 0; k < expressions.size(); k++) {
            expressionMap.put(k, expressions.get(k));
        }
        // 数字的索引和表达式的索引累加计算
        Set<Integer> numSet = numMap.keySet();
        List<Integer> nums = new ArrayList<>(numSet);
        AtomicReference<BigDecimal> totalAmount = new AtomicReference<>();
        totalAmount.set(BigDecimal.ZERO);
        for (int i = 0; i < nums.size(); i++) {
            // 编号
            Integer num = numMap.get(i);
            List<HospProfitAndLoss> losses = hadCalcLosses.stream().filter(item -> item.getReportNum().equals(num)).collect(Collectors.toList());
            if (CollUtil.isEmpty(losses)) {
                continue;
            }
            HospProfitAndLoss loss = losses.get(0);
            BigDecimal amount = loss.getAmount();
            String str = expressionMap.get(i);
            if (str.equals("+")) {
                totalAmount.set(totalAmount.get().add(amount));
            } else {
                totalAmount.set(totalAmount.get().subtract(amount));
            }
        }
        return totalAmount.get();

    }


    private void calcByLitterCountResp(int year, int month, ReportForm child, List<ReportForm> children,
                                       List<HospProfitAndLoss> list, Long hospId, Responsibility responsibility) {
        List<HospProfitAndLoss> thisRespCodeList = list.stream().filter(i -> i.getResponsibilityCode().equals(responsibility.getResponsibilityCode())).collect(Collectors.toList());
        List<ReportForm> thisForEachList = children.stream().filter(i -> !i.getCalcType().equals(CalcTypeEnum.LITTER_COUNT.getType())).collect(Collectors.toList());
        List<HospProfitAndLoss> collect = thisRespCodeList.stream().filter(i -> thisForEachList.stream().map(ReportForm::getNum).collect(Collectors.toList()).contains(i.getReportNum())).collect(Collectors.toList());
        if (collect.isEmpty()) {
            return;
        }
        BigDecimal reduce = collect.stream().map(HospProfitAndLoss::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        HospProfitAndLoss loss = new HospProfitAndLoss();
        loss.setDateYear(year).setDateMonth(month)
                .setAmount(reduce).setCreateTime(System.currentTimeMillis()).setHospId(hospId);
        // 小计只能有一个
        loss.setReportNum(child.getNum()).setReportName(child.getReportName())
                .setResponsibilityCode(responsibility.getResponsibilityCode())
                .setResponsibilityName(responsibility.getResponsibilityName());
        list.add(loss);
    }

    private void calcByShareLevelResp(Long hospId, ReportForm child, List<HospProfitAndLoss> list, int year, int month, Responsibility responsibility) {
        List<RelationVO> shareLevels = reportRelationService.getShareLevel(child.getId(), hospId);
        List<Long> shareLevelId = shareLevels.stream().map(RelationVO::getCode).map(Long::valueOf).sorted(Long::compareTo).collect(Collectors.toList());
        if (CollUtil.isEmpty(shareLevelId)) {
            return;
        }
        List<CostShareLevel> costShareLevels = shareLevelService.listByIds(shareLevelId);
        if (costShareLevels.isEmpty()) {
            throw new CostException("医院分摊层级设置错误，" + child.getReportName());
        }
        List<Integer> levelSorts = costShareLevels.stream().map(CostShareLevel::getLeverSort).collect(Collectors.toList());
        List<AllocationQuery> allocations = allocationQueryService.getByDateRespn(year, month, hospId, shareLevelId, responsibility.getResponsibilityCode());

        if (allocations.isEmpty()) {
            throw new CostException("医院未分摊本月数据");
        }
        List<CostAccountShare> accountShares = accountShareService.getByShareLevelSort(levelSorts, hospId);
        if (accountShares.isEmpty()) {
            return;
        }

        BigDecimal reduce = allocations.stream().map(AllocationQuery::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        HospProfitAndLoss loss = new HospProfitAndLoss();
        loss.setReportName(child.getReportName()).setReportNum(child.getNum())
                .setAmount(reduce).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month)
                .setResponsibilityCode(responsibility.getResponsibilityCode()).setResponsibilityName(responsibility.getResponsibilityName())
        ;
        list.add(loss);


    }

    private void calcByShareLevelResp(Long hospId, HospProfitAndLossVo lossVo) {
        List<RelationVO> shareLevels = reportRelationService.getShareLevel(lossVo.getReportId(), hospId);
        List<Long> shareLevelId = shareLevels.stream().map(RelationVO::getCode).map(Long::valueOf).sorted(Long::compareTo).collect(Collectors.toList());
        if (CollUtil.isEmpty(shareLevelId)) {
            return;
        }
        List<CostShareLevel> costShareLevels = shareLevelService.listByIds(shareLevelId);
        if (costShareLevels.isEmpty()) {
            throw new CostException("医院分摊层级设置错误，" + lossVo.getReportName());
        }
        List<Integer> levelSorts = costShareLevels.stream().map(CostShareLevel::getLeverSort).collect(Collectors.toList());
        List<AllocationQuery> allocations = allocationQueryService.getByDateRespn(lossVo.getDateYear(), lossVo.getDateMonth(), hospId, shareLevelId, lossVo.getResponsibilityCode());

        if (allocations.isEmpty()) {
            throw new CostException("医院未分摊本月数据");
        }
        List<CostAccountShare> accountShares = accountShareService.getByShareLevelSort(levelSorts, hospId);
        if (accountShares.isEmpty()) {
            return;
        }

        BigDecimal reduce = allocations.stream().map(AllocationQuery::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        lossVo.setAmount(reduce);
//        HospProfitAndLoss loss = new HospProfitAndLoss();
//        loss.setReportName(child.getReportName()).setReportNum(child.getNum())
//                .setAmount(reduce).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month)
//                .setResponsibilityCode(responsibility.getResponsibilityCode()).setResponsibilityName(responsibility.getResponsibilityName())
//        ;
//        list.add(loss);


    }

    private void calcByAccountByResp(Long hospId, HospProfitAndLossVo lossVo) {

        // 报表项目关联的会计科目对象
        List<RelationVO> accountRelations = reportRelationService.getAccountRelation(lossVo.getReportId(), hospId);
        if (accountRelations.isEmpty()) {
            return;
        }
        List<String> accountCodes = accountRelations.stream().map(RelationVO::getCode).collect(Collectors.toList());
        AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
        calcTotal.set(BigDecimal.ZERO);
        List<IncomeCollection> incomes = collectionService.getCollectionsByDateAndResp(lossVo.getDateYear(), lossVo.getDateMonth(), hospId, lossVo.getResponsibilityCode());
        List<AllocationQuery> allocationQueries = allocationQueryService.getByDateAndResp(lossVo.getDateYear(), lossVo.getDateMonth(), hospId, lossVo.getResponsibilityCode());
        accountCodes.forEach(i -> {
            BigDecimal incomeAmount = incomes.stream().filter(j -> i.equals(j.getAccountingCode())).map(IncomeCollection::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getAccountingCode())).map(AllocationQuery::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal total = incomeAmount.add(costAmount);
            calcTotal.set(calcTotal.get().add(total));
        });
//        HospProfitAndLoss loss = new HospProfitAndLoss();
        lossVo.setAmount(calcTotal.get());
//        loss.setReportName(child.getReportName()).setReportNum(child.getNum())
//                .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month)
//                .setResponsibilityCode(responsibility.getResponsibilityCode()).setResponsibilityName(responsibility.getResponsibilityName());
//        list.add(loss);
    }


    private void calcByAccountByResp(Long hospId, ReportForm child, List<HospProfitAndLoss> list, int month, int year, Responsibility responsibility) {

        // 报表项目关联的会计科目对象
        List<RelationVO> accountRelations = reportRelationService.getAccountRelation(child.getId(), hospId);
        if (accountRelations.isEmpty()) {
            return;
        }
        List<String> accountCodes = accountRelations.stream().map(RelationVO::getCode).collect(Collectors.toList());
        AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
        calcTotal.set(BigDecimal.ZERO);
        List<IncomeCollection> incomes = collectionService.getCollectionsByDateAndResp(year, month, hospId, responsibility.getResponsibilityCode());
        List<AllocationQuery> allocationQueries = allocationQueryService.getByDateAndResp(year, month, hospId, responsibility.getResponsibilityCode());
        accountCodes.forEach(i -> {
            BigDecimal incomeAmount = incomes.stream().filter(j -> i.equals(j.getAccountingCode())).map(IncomeCollection::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getAccountingCode())).map(AllocationQuery::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal total = incomeAmount.add(costAmount);
            calcTotal.set(calcTotal.get().add(total));
        });
        HospProfitAndLoss loss = new HospProfitAndLoss();
        loss.setReportName(child.getReportName()).setReportNum(child.getNum())
                .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month)
                .setResponsibilityCode(responsibility.getResponsibilityCode()).setResponsibilityName(responsibility.getResponsibilityName())
        ;
        list.add(loss);
    }

    private void responsibilitySort(List<Responsibility> responsibilityList) {
        for (Responsibility responsibility : responsibilityList) {
            if (!CollectionUtils.isEmpty(responsibility.getChildren())) {
                responsibilitySort(responsibility.getChildren());
            }
        }
        responsibilityList.sort(Comparator.comparing(Responsibility::getSort, Comparator.nullsLast(Integer::compareTo)));
    }

    private List<Responsibility> setResponsibilityChildren(List<Responsibility> child, Map<Long, List<Responsibility>> collect) {
        for (Responsibility responsibility : child) {
            if (!CollectionUtils.isEmpty(collect.get(responsibility.getId()))) {
                responsibility.setChildren(setResponsibilityChildren(collect.get(responsibility.getId()), collect));
            }
        }
        return child;
    }

    private void getResponsibilityParent(Responsibility responsibility, Map<Long, Responsibility> collect, List<Responsibility> addList) {
        Responsibility responsibility1 = collect.get(responsibility.getParentId());
        if (Objects.nonNull(responsibility1)) {
            addList.add(responsibility1);
            if (!responsibility1.getParentId().equals(NumberConstant.ZERO_L)) {
                getResponsibilityParent(responsibility1, collect, addList);
            }
        }
    }

    private List<HospProfitVO> setChildren(List<HospProfitVO> child, Map<Long, List<HospProfitVO>> collect) {
        for (HospProfitVO costProfitVo : child) {
            if (!CollectionUtils.isEmpty(collect.get(costProfitVo.getId()))) {
                costProfitVo.setChild(setChildren(collect.get(costProfitVo.getId()), collect));
            }
        }
        if (!CollectionUtils.isEmpty(child)) {
            child.sort(Comparator.comparing(HospProfitVO::getSort, Comparator.nullsLast(Integer::compareTo)));
        }
        return child;
    }

}