package com.kcim.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kcim.common.constants.NumberConstant;
import com.kcim.common.constants.ParameterConstant;
import com.kcim.common.constants.RedisKeyConstant;
import com.kcim.common.enums.EmpCostComputeTypeEnum;
import com.kcim.common.enums.ItemTypeEnum;
import com.kcim.common.enums.ReportItemTypeEnum;
import com.kcim.common.exception.CostException;
import com.kcim.common.token.RedisUtil;
import com.kcim.common.util.BeanUtil;
import com.kcim.common.util.PageUtils;
import com.kcim.dao.model.*;
import com.kcim.dao.model.dto.ComputeItemCostPageDto;
import com.kcim.dao.repository.*;
import com.kcim.service.CenterService;
import com.kcim.service.ComputeItemCostService;
import com.kcim.vo.CommonParameterVo;
import com.kcim.vo.SessionUserVO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.sql.DataSource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @program: CostAccount
 * @description: 收费项目成本计算接口实现类
 * @author: Wang.YS
 * @create: 2023-10-25 14:39
 **/
@Service("ComputeItemCostService")
@Slf4j
@AllArgsConstructor
public class ComputeItemCostServiceImpl implements ComputeItemCostService {
    /**
     * 项目成本计算主表
     */
    ComputeItemCostRepository repository;
    /**
     * 项目成本计算明细表
     */
    ComputeItemCostDetailRepository detailRepository;
    /**
     * 项目导入数据
     */
    ImportPatientItemRepository importPatientItemRepository;
    /**
     * 人事成本计算数据
     */
    ComputeEmpCostRepository computeEmpCostRepository;
    /**
     * 药品成本
     */
    DrugRepository drugRepository;
    /**
     * 材料成本
     */
    MaterialRepository materialRepository;
    /**
     * 设备字典
     */
    EquipmentRepository equipmentRepository;
    /**
     * 空间字典
     */
    SpaceRepository spaceRepository;
    /**
     * 标准项目字典
     */
    StandItemRepository standItemRepository;
    /**
     * 标准项目与人员对照
     */
    StandItemEmpMapRepository standItemEmpMapRepository;
    /**
     * 标准项目与设备对照
     */
    StandItemEquipmentMapRepository standItemEquipmentMapRepository;
    /**
     * 标准项目与空间对照
     */
    StandItemSpaceMapRepository standItemSpaceMapRepository;
    /**
     * 人事分类字典
     */
    EmpCostTypeRepository empCostTypeRepository;
    /**
     * 人事分类人员对照
     */
    EmpCostMapRepository empCostMapRepository;
    /**
     * 收费项目管理
     */
    ItemRepository itemRepository;

    CenterService centerService;

    DataSourceTransactionManager dataSourceTransactionManager;

    TransactionDefinition transactionDefinition;

    DataSource dataSource;

    RedisUtil redisUtil;
    private static final Integer LIMIT = 1000;


    /**
     * 成本核算-收费项目成本计算-获取列表
     *
     * @param current     当前页
     * @param pageSize    页容量
     * @param computeDate 核算年月
     * @param itemType    项目类别
     * @param itemName    项目名称
     * @return 列表
     */
    @Override
    public Object getItemCostCalculateList(Integer current, Integer pageSize, String computeDate, String itemType, String itemName) {
        Page<ComputeItemCostPageDto> page = repository.getByPage(current, pageSize, computeDate, itemType, itemName);
        if (CollectionUtils.isEmpty(page.getRecords())) {
            return new PageUtils(new ArrayList<>(), NumberConstant.ZERO, pageSize, current);
        }
        return new PageUtils(page.getRecords(), Math.toIntExact(page.getTotal()), pageSize, current);
    }

    /**
     * 成本核算-收费项目成本计算-计算
     *
     * @param computeDate       核算年月
     * @param currentUser
     * @param requestAttributes
     * @param progressKey
     */
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
    public void itemCostCalculate(String computeDate, SessionUserVO currentUser, RequestAttributes requestAttributes, String progressKey) {
        // 从主线程获取用户数据 放到局部变量中
        try {
            RequestContextHolder.setRequestAttributes(requestAttributes, true);
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 1.0);
//            connection.setAutoCommit(false);
            long itemSize = importPatientItemRepository.getCount(computeDate, currentUser);
            if (Objects.equals(itemSize, NumberConstant.ZERO_L)) {
                log.error("当前核算年月【" + computeDate + "】未导入收费项目数据，计算中止");
            throw new CostException("当前核算年月【" + computeDate + "】未导入收费项目数据，计算中止");
            }
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 2.0);

            //把当月需要计算数据置成0
            importPatientItemRepository.updateCalculateFlag(computeDate, currentUser);
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 4.0);

            List<Integer> integers = repository.removeByComputeDate(computeDate, currentUser);
            if (!CollectionUtils.isEmpty(integers)) {
                detailRepository.removeByItemCostId(integers, currentUser);
                redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 10.0);
            }
//            redisUtil.set(RedisKeyConstant.PROGRESS+ progressKey,100.0);

//        List<PatientItemVo> items = importPatientItemRepository.getAllGroupByCompute(computeDate);

            //人事项目成本
            List<ComputeEmpCost> computeEmpCosts = computeEmpCostRepository.getByComputeDate(computeDate, currentUser);
            if (CollectionUtils.isEmpty(computeEmpCosts)) {
                log.error("当前核算年月【" + computeDate + "】人事成本未完成计算，请先完成人事成本计算，当前项目计算中止");

            throw new CostException("当前核算年月【" + computeDate + "】人事成本未完成计算，请先完成人事成本计算，当前项目计算中止");
            }
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 12.0);

            CommonParameterVo parameter = centerService.getParameter(ParameterConstant.ITEM_CONTAINS_SPACE);
            String ITEM_CONTAINS_SPACE = Objects.nonNull(parameter.getValue()) ? parameter.getValue() : "0";
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 14.0);
            //药品成本
            Map<String, BigDecimal> drugCostMap = drugRepository.getCostMap(currentUser);
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 15.0);
            //材料成本
            Map<String, BigDecimal> materialCostMap = materialRepository.getCostMap(currentUser);
            //人事项目成本
//        List<ComputeEmpCost> computeEmpCosts = computeEmpCostRepository.getByComputeDate(computeDate,currentUser);
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 16.0);
            //按人事分类进行分组
            Map<String, List<ComputeEmpCost>> empComputeCostGroup = computeEmpCosts.stream().collect(Collectors.groupingBy(ComputeEmpCost::getEmpType));
            //标准项目字典
            Map<String, StandItem> standItemMap = getStandItem(currentUser);
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 17.0);
            //人事成本分类
            Map<String, EmpCostType> empCostType = getEmpCostType(currentUser);
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 18.0);
            Map<String, String> itemStandMap = getItemStandMap(currentUser);
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 20.0);
            //获取设备每分钟成本
            Map<String, BigDecimal> equipmentMinuteMap = equipmentRepository.getEquipmentMinuteMap(currentUser);
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 21.0);

            Map<String, BigDecimal> spaceMinuteMap = spaceRepository.getSpaceMinuteMap(currentUser);
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 22.0);
            //主表公共对象
            ComputeItemCost computeItemCost = new ComputeItemCost();
            computeItemCost.setComputeDate(computeDate);
            computeItemCost.setHospId(currentUser.getHospId());
            computeItemCost.setCreateUser(String.valueOf(currentUser.getId()));
            computeItemCost.setCreateTime(new Date());
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 24.0);
            //明细表公共对象
            ComputeItemCostDetail costDetail = new ComputeItemCostDetail();
            costDetail.setHospId(currentUser.getHospId());
            costDetail.setCreateUser(String.valueOf(currentUser.getId()));
            costDetail.setCreateTime(new Date());
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 25.0);
            long count = (itemSize + LIMIT - 1) / LIMIT;
            long l = 0;
            for (int j = 0; j < count; j++) {
                List<ImportPatientItem> items = importPatientItemRepository.getByNoCalculateComputeDate(computeDate, currentUser);
                List<ComputeItemCostDetail> saveDetails = new ArrayList<>();
                List<ComputeItemCost> saveCost = new ArrayList<>();
                if (!CollectionUtils.isEmpty(items)) {
                    for (int i = 0; i < items.size(); i++) {
                        ImportPatientItem item = items.get(i);
                        ComputeItemCost itemCost = BeanUtil.convertObj(computeItemCost, ComputeItemCost.class);
                        itemCost.setVisitNo(item.getVisitNo());
                        itemCost.setCode(item.getItemCode());
                        itemCost.setName(item.getItemName());
                        String typeCode = item.getItemTypeCode();
                        itemCost.setItemTypeCode(typeCode);
                        itemCost.setItemType(item.getItemType());
                        itemCost.setOrderCode(item.getOrderCode());
                        itemCost.setOrderName(item.getOrderName());
                        itemCost.setNum(item.getNum());
                        itemCost.setIndex(i);
                        saveCost.add(itemCost);
                        costDetail.setIndex(i);
                        List<ComputeItemCostDetail> details = setDetails(drugCostMap, materialCostMap, empComputeCostGroup, standItemMap,
                                empCostType, equipmentMinuteMap, spaceMinuteMap, costDetail, item, typeCode, ITEM_CONTAINS_SPACE, itemStandMap);
                        if (!CollectionUtils.isEmpty(details)) {
                            saveDetails.addAll(details);
                        }
                        double percent = (l + 1) / (double) itemSize * 100;
                        //这里对小数采用向下取整，例如99.5会取整为99
                        percent = Math.floor(percent / 2 + 25.0);
                        if (l == itemSize - 1) {
                            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 98.0);
                        } else {
                            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, percent);
                        }
                        System.out.println(l + "-" + itemSize);
                        l++;

                    }
                    if (!CollectionUtils.isEmpty(saveCost)) {
                        //作废当前核算年月已有数据
                        repository.saveBatch(saveCost, 100);
                        if (!CollectionUtils.isEmpty(saveDetails)) {
                            Map<Integer, Integer> indexId = saveCost.stream().collect(Collectors.toMap(ComputeItemCost::getIndex, ComputeItemCost::getId, (a, b) -> b));
                            Map<Integer, List<ComputeItemCostDetail>> collect = saveDetails.stream().collect(Collectors.groupingBy(ComputeItemCostDetail::getIndex));
                            List<ComputeItemCostDetail> saveCostDetails = new ArrayList<>();
                            collect.keySet().forEach(integer -> {
                                Integer costId = indexId.get(integer);
                                List<ComputeItemCostDetail> details = collect.get(integer);
                                details.forEach(detail -> detail.setItemCostId(costId));
                                saveCostDetails.addAll(details);
                            });
                            detailRepository.saveBatch(saveCostDetails, 100);
                        }
                    }
                    //保存完把已计算过的置1
                    importPatientItemRepository.updateCalculateFlag(items);
//                    connection.commit();
                }
            }
            redisUtil.set(RedisKeyConstant.PROGRESS + progressKey, 100.0);
        } catch (Exception e) {
            throw new CostException("收费项目计算错误："+ e.getMessage());
        }

//        Result result = new Result();
//        result.setStatus(200);
//        result.setMsg("计算完成");
//        return result;

    }

    private Map<String, String> getItemStandMap(SessionUserVO currentUser) {
        List<Item> list = itemRepository.getList(currentUser);
        if (!CollectionUtils.isEmpty(list)) {
            List<Item> collect = list.stream().filter(f -> Objects.nonNull(f.getStandItemCode())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(collect)) {
                return collect.stream().collect(Collectors.toMap(Item::getCode, Item::getStandItemCode, (a, b) -> b));
            }
        }
        return new HashMap<>();
    }

    /**
     * 计算收费项目明细
     *
     * @param drugCostMap         药品成本字典
     * @param materialCostMap     材料成本字典
     * @param empComputeCostGroup 人员成本计算
     * @param standItemMap        标准项目字典
     * @param empCostType         人员类别字典
     * @param equipmentMinuteMap  设备每分钟成本
     * @param spaceMinuteMap      空间每分钟成本
     * @param costDetail          明细表入参
     * @param item                收费项目
     * @param typeCode            项目类别
     * @param ITEM_CONTAINS_SPACE
     * @param itemStandMap
     * @return 项目明细出参
     */
    private List<ComputeItemCostDetail> setDetails(Map<String, BigDecimal> drugCostMap, Map<String, BigDecimal> materialCostMap,
                                                   Map<String, List<ComputeEmpCost>> empComputeCostGroup, Map<String, StandItem> standItemMap,
                                                   Map<String, EmpCostType> empCostType, Map<String, BigDecimal> equipmentMinuteMap,
                                                   Map<String, BigDecimal> spaceMinuteMap, ComputeItemCostDetail costDetail,
                                                   ImportPatientItem item, String typeCode, String ITEM_CONTAINS_SPACE,
                                                   Map<String, String> itemStandMap) {
        List<ComputeItemCostDetail> details = new ArrayList<>();
        if (typeCode.equals(ItemTypeEnum.DRUG.getCode())) {
            // 药品  取药品收入 药品成本
            //取药品收入
            ComputeItemCostDetail income = getIncome(costDetail, item);
            income.setType(ReportItemTypeEnum.DRUG_INCOME.getCode());
            details.add(income);
            //药品成本
            ComputeItemCostDetail cost = getCost(costDetail, item, drugCostMap);
            cost.setType(ReportItemTypeEnum.DRUG_COST.getCode());
            details.add(cost);
        } else if (typeCode.equals(ItemTypeEnum.MATERIAL.getCode())) {
            //材料  取材料收入 材料成本
            //取材料收入
            ComputeItemCostDetail income = getIncome(costDetail, item);
            income.setType(ReportItemTypeEnum.MATERIAL_INCOME.getCode());
            details.add(income);
            //材料成本
            ComputeItemCostDetail cost = getCost(costDetail, item, materialCostMap);
            cost.setType(ReportItemTypeEnum.MATERIAL_COST.getCode());
            details.add(cost);
        } else if (typeCode.equals(ItemTypeEnum.ITEM.getCode())) {
            //项目 取项目收入  项目成本
            //取项目收费
            ComputeItemCostDetail income = getIncome(costDetail, item);
            income.setType(ReportItemTypeEnum.ITEM_INCOME.getCode());
            details.add(income);
            //当前项目的标准项目
            BigDecimal num = item.getNum();
            if (!CollectionUtils.isEmpty(itemStandMap)) {
                StandItem standItem = standItemMap.get(itemStandMap.get(item.getItemCode()));
                if (Objects.nonNull(standItem)) {
                    BigDecimal weight = new BigDecimal("0.005");
                    //人力成本
                    List<StandItemEmpMap> empMaps = standItem.getEmpMaps();
                    BigDecimal empCost = getEmpCost(empMaps, empComputeCostGroup, empCostType, item);
                    ComputeItemCostDetail empCostResult = BeanUtil.convertObj(costDetail, ComputeItemCostDetail.class);
                    BigDecimal empCostNum = new BigDecimal("0.0000");
                    if (num != null) {
                        empCostNum = empCost.multiply(num);
                    }
                    empCostResult.setComputeResult(empCostNum);
                    empCostResult.setComputeSingleResult(empCost);
                    empCostResult.setType(ReportItemTypeEnum.WEIGHT_EMP_COST.getCode());
                    details.add(empCostResult);

                    // 2023.12.18 添加加成算法
                    BigDecimal riskDegree = standItem.getRiskDegree();
                    BigDecimal technicalDifficulty = standItem.getTechnicalDifficulty();
                    if (riskDegree == null) {
                        riskDegree = BigDecimal.ZERO;
                    }
                    if (technicalDifficulty == null) {
                        technicalDifficulty = BigDecimal.ZERO;
                    }
                    BigDecimal technical = technicalDifficulty.multiply(weight).add(BigDecimal.ONE);
                    BigDecimal risk = riskDegree.multiply(weight).add(BigDecimal.ONE);
                    BigDecimal bigDecimal = empCost.multiply(technical).multiply(risk).setScale(4, RoundingMode.HALF_UP);
                    BigDecimal bigDecimalNum = empCostNum.multiply(technical).multiply(risk).setScale(4, RoundingMode.HALF_UP);

                    ComputeItemCostDetail empWeightCostResult = BeanUtil.convertObj(costDetail, ComputeItemCostDetail.class);
                    empWeightCostResult.setComputeResult(bigDecimalNum);
                    empWeightCostResult.setComputeSingleResult(bigDecimal);
                    empWeightCostResult.setType(ReportItemTypeEnum.EMP_COST.getCode());
                    details.add(empWeightCostResult);
                    //设备成本
                    List<StandItemEquipmentMap> equipmentMaps = standItem.getEquipmentMaps();
                    BigDecimal equipmentCost = getEquipmentCost(equipmentMaps, equipmentMinuteMap);
                    ComputeItemCostDetail equipmentCostResult = BeanUtil.convertObj(costDetail, ComputeItemCostDetail.class);
                    BigDecimal equipmentCostNum = new BigDecimal("0.0000");

                    if (num != null) {
                        equipmentCostNum = equipmentCost.multiply(num);
                    }
                    equipmentCostResult.setComputeResult(equipmentCostNum);
                    equipmentCostResult.setComputeSingleResult(equipmentCost);
                    equipmentCostResult.setType(ReportItemTypeEnum.EQUIPMENT_COST.getCode());
                    details.add(equipmentCostResult);
                    //空间成本
                    List<StandItemSpaceMap> spaceMaps = standItem.getSpaceMaps();
                    BigDecimal spaceCost = getSpaceCost(spaceMaps, spaceMinuteMap);
                    ComputeItemCostDetail spaceCostResult = BeanUtil.convertObj(costDetail, ComputeItemCostDetail.class);
                    BigDecimal spaceCostNum = new BigDecimal("0.0000");

                    if (num != null) {
                        spaceCostNum = spaceCost.multiply(num);
                    }
                    spaceCostResult.setComputeSingleResult(spaceCost);
                    spaceCostResult.setComputeResult(spaceCostNum);
                    spaceCostResult.setType(ReportItemTypeEnum.SPACE_COST.getCode());
                    details.add(spaceCostResult);
                    //项目成本=人力成本+设备成本+空间成本
                    ComputeItemCostDetail cost = getCost(costDetail, item, materialCostMap);
                    cost.setType(ReportItemTypeEnum.ITEM_COST.getCode());

                    if (ITEM_CONTAINS_SPACE.equals(NumberConstant.ONE_S)) {
                        //项目成本=人力成本+设备成本
                        cost.setComputeResult(bigDecimalNum.add(equipmentCostNum));
                        cost.setComputeSingleResult(bigDecimal.add(equipmentCost));
                    } else {
                        //项目成本=人力成本+设备成本+空间成本
                        cost.setComputeResult(bigDecimalNum.add(equipmentCostNum).add(spaceCostNum));
                        cost.setComputeSingleResult(bigDecimal.add(equipmentCost).add(spaceCost));
                    }
                    details.add(cost);
                } else {
                    //人力成本
                    ComputeItemCostDetail empCostResult = BeanUtil.convertObj(costDetail, ComputeItemCostDetail.class);
                    if (empCostResult != null) {
                        empCostResult.setComputeResult(BigDecimal.ZERO);
                        empCostResult.setComputeSingleResult(BigDecimal.ZERO);

                        empCostResult.setType(ReportItemTypeEnum.EMP_COST.getCode());
                    }
                    details.add(empCostResult);
                    //设备成本
                    ComputeItemCostDetail equipmentCostResult = BeanUtil.convertObj(costDetail, ComputeItemCostDetail.class);
                    if (equipmentCostResult != null) {
                        equipmentCostResult.setComputeResult(BigDecimal.ZERO);
                        equipmentCostResult.setComputeSingleResult(BigDecimal.ZERO);

                        equipmentCostResult.setType(ReportItemTypeEnum.EQUIPMENT_COST.getCode());
                    }
                    details.add(equipmentCostResult);
                    //空间成本
                    ComputeItemCostDetail spaceCostResult = BeanUtil.convertObj(costDetail, ComputeItemCostDetail.class);
                    if (spaceCostResult != null) {
                        spaceCostResult.setComputeResult(BigDecimal.ZERO);
                        spaceCostResult.setComputeSingleResult(BigDecimal.ZERO);

                        spaceCostResult.setType(ReportItemTypeEnum.SPACE_COST.getCode());
                    }

                    details.add(spaceCostResult);
                    //项目成本=人力成本+设备成本+空间成本
                    ComputeItemCostDetail cost = getCost(costDetail, item, materialCostMap);
                    cost.setType(ReportItemTypeEnum.ITEM_COST.getCode());
                    cost.setComputeResult(BigDecimal.ZERO);
                    cost.setComputeSingleResult(BigDecimal.ZERO);

                    details.add(cost);
                }
            } else {
                //人力成本
                ComputeItemCostDetail empCostResult = BeanUtil.convertObj(costDetail, ComputeItemCostDetail.class);
                if (empCostResult != null) {
                    empCostResult.setComputeResult(BigDecimal.ZERO);
                    empCostResult.setComputeSingleResult(BigDecimal.ZERO);
                    empCostResult.setType(ReportItemTypeEnum.EMP_COST.getCode());
                }
                details.add(empCostResult);
                //设备成本
                ComputeItemCostDetail equipmentCostResult = BeanUtil.convertObj(costDetail, ComputeItemCostDetail.class);
                if (equipmentCostResult != null) {
                    equipmentCostResult.setComputeResult(BigDecimal.ZERO);
                    equipmentCostResult.setComputeSingleResult(BigDecimal.ZERO);
                    equipmentCostResult.setType(ReportItemTypeEnum.EQUIPMENT_COST.getCode());
                }
                details.add(equipmentCostResult);
                //空间成本
                ComputeItemCostDetail spaceCostResult = BeanUtil.convertObj(costDetail, ComputeItemCostDetail.class);
                if (spaceCostResult != null) {
                    spaceCostResult.setComputeResult(BigDecimal.ZERO);
                    spaceCostResult.setComputeSingleResult(BigDecimal.ZERO);
                    spaceCostResult.setType(ReportItemTypeEnum.SPACE_COST.getCode());
                }

                details.add(spaceCostResult);
                //项目成本=人力成本+设备成本+空间成本
                ComputeItemCostDetail cost = getCost(costDetail, item, materialCostMap);
                cost.setType(ReportItemTypeEnum.ITEM_COST.getCode());
                cost.setComputeResult(BigDecimal.ZERO);
                cost.setComputeSingleResult(BigDecimal.ZERO);
                details.add(cost);
            }

        } //其他 目前不知算法
        return details;
    }

    /**
     * 获取空间成本
     *
     * @param spaceMaps      标准项目空间成本对照
     * @param spaceMinuteMap 每分钟空间成本
     * @return 空间成本
     */
    private BigDecimal getSpaceCost(List<StandItemSpaceMap> spaceMaps, Map<String, BigDecimal> spaceMinuteMap) {
        if (!CollectionUtils.isEmpty(spaceMaps)) {
            BigDecimal totalCost = new BigDecimal("0.0000");
            for (StandItemSpaceMap space : spaceMaps) {
                //设备每分钟成本
                BigDecimal bigDecimal = spaceMinuteMap.get(space.getSpaceCode());
                //设备成本=每分钟成本*数量*执行时间
                totalCost = bigDecimal != null ? totalCost.add(bigDecimal.multiply(space.getExecuteTime()).multiply(space.getNum())) : totalCost.add(BigDecimal.ZERO);
            }
            return totalCost;
        } else {
            return new BigDecimal("0.0000");
        }
    }


    /**
     * 获取设备成本
     *
     * @param equipmentMaps      标准项目设备对照
     * @param equipmentMinuteMap 设备每分钟成字典
     * @return 设备成本
     */
    private BigDecimal getEquipmentCost(List<StandItemEquipmentMap> equipmentMaps, Map<String, BigDecimal> equipmentMinuteMap) {
        if (!CollectionUtils.isEmpty(equipmentMaps)) {
            BigDecimal totalCost = new BigDecimal("0.0000");
            for (StandItemEquipmentMap equipmentMap : equipmentMaps) {
                //设备每分钟成本
                BigDecimal bigDecimal = equipmentMinuteMap.get(equipmentMap.getEquipmentCode());
                //设备成本=每分钟成本*数量*执行时间
                totalCost = totalCost.add(bigDecimal.multiply(equipmentMap.getExecuteTime()).multiply(equipmentMap.getNum()));
            }
            return totalCost;
        } else {
            return new BigDecimal("0.0000");
        }
    }

    private BigDecimal getEmpCost(List<StandItemEmpMap> empMaps, Map<String, List<ComputeEmpCost>> empCostGroup,
                                  Map<String, EmpCostType> empCostType, ImportPatientItem item) {
        /*
          单位人事成本获取规则：
          人员类别的计算方式为科室的：按执行科室取对应科室的每分钟成本，取不到则取执行人对应人事分类的每分钟成本
          人员类别的计算方式为岗位的：取执行人对应岗位的每分钟成本，取不到则取执行人对应人事分类的每分钟成本
          人员类别的计算方式为人事分类的：取执行人对应人事分类的每分钟成本
          人力成本=每分钟成本*数量*执行时间
         */
        if (!CollectionUtils.isEmpty(empMaps)) {
            BigDecimal totalCost = new BigDecimal("0.0000");
            for (StandItemEmpMap empMap : empMaps) {
                //标准项目人员对照
                String empTypeCode = empMap.getEmpTypeCode();
                BigDecimal num = empMap.getNum();
                BigDecimal executeTime = empMap.getExecuteTime();
                //人事分类
                EmpCostType costType = empCostType.get(empTypeCode);
                //计算方式
                String computeType = costType.getComputeType();
                //人事成本计算数据
                List<ComputeEmpCost> computeEmpCosts = empCostGroup.get(empTypeCode);
                //每分钟成本
                Map<String, BigDecimal> minuteCost = computeEmpCosts.stream().collect(Collectors.toMap(ComputeEmpCost::getCode, ComputeEmpCost::getMinuteCost, (a, b) -> b));
                //人事分类对应的人员信息

                if (computeType.equals(EmpCostComputeTypeEnum.DEPARTMENT.getCode())) {
                    //人员类别的计算方式为科室的：按执行科室取对应科室的每分钟成本，取不到当前人事分类的每分钟成本
                    //当前项目执行科室
                    String departmentCode = item.getExecuteDepartmentCode();
                    BigDecimal bigDecimal = minuteCost.get(departmentCode);
                    if (bigDecimal == null) {
                        bigDecimal = minuteCost.get(empTypeCode);
                    }
                    totalCost = totalCost.add(bigDecimal.multiply(executeTime).multiply(num));
                } else if (computeType.equals(EmpCostComputeTypeEnum.POSITION.getCode())) {
                    String userCode = item.getExecuteUserCode();
                    List<EmpCostMap> empCostTypeMaps = costType.getEmpCostMaps();
                    if (!CollectionUtils.isEmpty(empCostTypeMaps)) {
                        Map<String, String> empPositionMap = empCostTypeMaps.stream().collect(Collectors.toMap(EmpCostMap::getAccount, EmpCostMap::getPosition, (a, b) -> b));
                        String position = empPositionMap.get(userCode);
                        BigDecimal bigDecimal = minuteCost.get(position);
                        if (bigDecimal == null) {
                            bigDecimal = minuteCost.get(empTypeCode);
                        }
                        totalCost = totalCost.add(bigDecimal.multiply(executeTime).multiply(num));
                    } else {
                        BigDecimal bigDecimal = minuteCost.get(empTypeCode);
                        totalCost = totalCost.add(bigDecimal.multiply(executeTime).multiply(num));
                    }

                } else {
                    BigDecimal bigDecimal = minuteCost.get(empTypeCode);
                    totalCost = totalCost.add(bigDecimal.multiply(executeTime).multiply(num));
                }
            }
            return totalCost;
        } else {
            return new BigDecimal("0.0000");
        }
    }

    private Map<String, EmpCostType> getEmpCostType(SessionUserVO currentUser) {
        List<EmpCostType> empCostTypes = empCostTypeRepository.getList(currentUser);
        if (CollectionUtils.isEmpty(empCostTypes)) {
            throw new CostException("人事成本分类，请先添加人事成本分类再进行计算，当前项目计算中止");
        }
        List<EmpCostMap> empCostMaps = empCostMapRepository.getList(currentUser);
        Map<String, List<EmpCostMap>> empMapGroup = new HashMap<>();
        if (!CollectionUtils.isEmpty(empCostMaps)) {
            empMapGroup = empCostMaps.stream().collect(Collectors.groupingBy(EmpCostMap::getCostTypeCode));
        }
        Map<String, EmpCostType> map = new HashMap<>();
        for (EmpCostType empCostType : empCostTypes) {
            empCostType.setEmpCostMaps(empMapGroup.get(empCostType.getCostTypeCode()));
            map.put(empCostType.getCostTypeCode(), empCostType);
        }
        return map;

    }

    /**
     * 获取标准项目字典
     *
     * @return 字典列表
     */
    private Map<String, StandItem> getStandItem(SessionUserVO currentUser) {
        //标准项目字典
        List<StandItem> list = standItemRepository.getList(currentUser);
        if (CollectionUtils.isEmpty(list)) {
            throw new CostException("标准项目未维护，请先添加标准项目再进行计算，当前项目计算中止");
        }
        //标准项目字典人员对照
        List<StandItemEmpMap> empMapList = standItemEmpMapRepository.getList(currentUser);
        Map<String, List<StandItemEmpMap>> empMapGroup = new HashMap<>();
        if (!CollectionUtils.isEmpty(empMapList)) {
            empMapGroup = empMapList.stream().collect(Collectors.groupingBy(StandItemEmpMap::getStandItemCode));
        }
        //标准项目字典空间对照
        List<StandItemSpaceMap> spaceMapList = standItemSpaceMapRepository.getList(currentUser);
        Map<String, List<StandItemSpaceMap>> spaceMapGroup = new HashMap<>();
        if (!CollectionUtils.isEmpty(spaceMapList)) {
            spaceMapGroup = spaceMapList.stream().collect(Collectors.groupingBy(StandItemSpaceMap::getStandItemCode));
        }
        //标准项目字典设备对照
        List<StandItemEquipmentMap> equipmentMapList = standItemEquipmentMapRepository.getList(currentUser);
        Map<String, List<StandItemEquipmentMap>> equipmentMapGroup = new HashMap<>();
        if (!CollectionUtils.isEmpty(equipmentMapList)) {
            equipmentMapGroup = equipmentMapList.stream().collect(Collectors.groupingBy(StandItemEquipmentMap::getStandItemCode));
        }
        Map<String, StandItem> map = new HashMap<>();
        for (StandItem standItem : list) {
            standItem.setEmpMaps(empMapGroup.get(standItem.getCode()));
            standItem.setEquipmentMaps(equipmentMapGroup.get(standItem.getCode()));
            standItem.setSpaceMaps(spaceMapGroup.get(standItem.getCode()));
            map.put(standItem.getCode(), standItem);
        }
        return map;
    }

    /**
     * 获取收入对象
     *
     * @param detail 公用明细
     * @param item   导入项目
     * @return 收入对象
     */
    private ComputeItemCostDetail getIncome(ComputeItemCostDetail detail, ImportPatientItem item) {
        ComputeItemCostDetail result = BeanUtil.convertObj(detail, ComputeItemCostDetail.class);
        result.setComputeResult(item.getAmount());
        result.setComputeSingleResult(item.getPrice());
        return result;
    }

    /**
     * 获取成本对象
     *
     * @param detail  公用明细
     * @param item    导入项目
     * @param costMap 成本字典
     * @return 成本对象
     */
    private ComputeItemCostDetail getCost(ComputeItemCostDetail detail, ImportPatientItem item, Map<String, BigDecimal> costMap) {
        ComputeItemCostDetail result = BeanUtil.convertObj(detail, ComputeItemCostDetail.class);
        BigDecimal bigDecimal = costMap.get(item.getItemCode());
        BigDecimal num = item.getNum();
        if (bigDecimal != null && num != null) {
            result.setComputeResult(bigDecimal.multiply(num));
            result.setComputeSingleResult(bigDecimal);
        } else if(bigDecimal != null){
            result.setComputeSingleResult(bigDecimal);
        }else {
            log.info("【" + item.getItemName() + "】未找到成本信息，默认为 0");
            result.setComputeResult(BigDecimal.ZERO);
            result.setComputeSingleResult(BigDecimal.ZERO);
        }
        return result;
    }

    @Override
    public List<String> getItemTypeList() {
        List<String> s = new ArrayList<>();
        s.add(ReportItemTypeEnum.DRUG_INCOME.getCode());
        s.add(ReportItemTypeEnum.MATERIAL_INCOME.getCode());
        s.add(ReportItemTypeEnum.ITEM_INCOME.getCode());
        s.add(ReportItemTypeEnum.DRUG_COST.getCode());
        s.add(ReportItemTypeEnum.MATERIAL_COST.getCode());
        s.add(ReportItemTypeEnum.ITEM_COST.getCode());
        s.add(ReportItemTypeEnum.EMP_COST.getCode());
        s.add(ReportItemTypeEnum.EQUIPMENT_COST.getCode());
        s.add(ReportItemTypeEnum.SPACE_COST.getCode());
        return s;
    }
}
