package com.zzsmart.qomo.kn.cost.manage.plugin;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zzsmart.qomo.kn.cost.manage.entity.*;
import com.zzsmart.qomo.kn.cost.manage.enums.FeeTypeEnum;
import com.zzsmart.qomo.kn.cost.manage.enums.HourOrRateTypeEnum;
import com.zzsmart.qomo.kn.cost.manage.enums.TotalCostEnum;
import com.zzsmart.qomo.kn.cost.manage.mapper.AppSceneCostMaterialBomMapper;
import com.zzsmart.qomo.kn.cost.manage.service.*;
import com.zzsmart.qomo.kn.cost.manage.vo.FlowTaskInfoVO;
import com.zzsmart.qomo.kn.cost.manage.vo.TotalResultValue;
import com.zzsmart.qomo.plugin.task.api.TaskExecutionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
@Transactional
public class StandardCostService {
    /**
     * 物料BOM信息
     */
    @Autowired
    AppSceneCostMaterialBomMapper costMaterialBomMapper;
    @Autowired
    IAppSceneCostMaterialBomService costMaterialBomService;

    /**
     * 小时费率
     */
    @Autowired
    IAppSceneCostHourRateService iHourRateService;
    /**
     * 采购单价
     */
    @Autowired
    IAppSceneCostPurchasePriceService costPurchasePriceService;
    /**
     * 工序
     */
    @Autowired
    IAppSceneCostMaterialProcessHoursService costMaterialProcessHoursService;
    /**
     * 标准成本计算结果
     */
    @Autowired
    IAppSceneCostResultValueService iAppSceneCostResultValueService;
    /**
     * 标准成本服务
     */
    public static StandardCostService standardCostService;
    /**
     * 成本计算服务
     */
    @Autowired
    IAppSceneCostCountService appSceneCostCountService;
    /**
     * 标准成本明细服务（单行查询服务）
     */
    @Autowired
    private IAppSceneCostStandardDetailService iAppSceneCostStandardDetailService;

    @PostConstruct
    public void init() {
        standardCostService = this;
        standardCostService.costMaterialBomMapper = this.costMaterialBomMapper;
        standardCostService.appSceneCostCountService = this.appSceneCostCountService;
        standardCostService.costMaterialBomService = this.costMaterialBomService;
        standardCostService.costPurchasePriceService = this.costPurchasePriceService;
        standardCostService.iHourRateService = this.iHourRateService;
        standardCostService.costMaterialProcessHoursService = this.costMaterialProcessHoursService;
        standardCostService.iAppSceneCostResultValueService = this.iAppSceneCostResultValueService;
        standardCostService.iAppSceneCostStandardDetailService = this.iAppSceneCostStandardDetailService;
    }

    /**
     * 更新成本计算模型任务状态
     */
    public static void updateCostCountTaskStatus(Boolean isSuccess, Integer costCountTaskId) {
        log.info("更新成本计算模型任务状态");
        standardCostService.appSceneCostCountService.updateById(AppSceneCostCount.builder().id(costCountTaskId).status(isSuccess != null && isSuccess ? 1 : 0).build());
    }

    /**
     * 1.物料编号输入任务
     */
    public static void materialInputTask(TaskExecutionContext parameters) {
        try {
            //获取任务流程相关信息
            FlowTaskInfoVO flowTaskInfoVO = analysisFlowTaskInfo(parameters);
            //获取最上层的物料号
            String topMaterialCode = flowTaskInfoVO.getTopMaterialCode();
            //获取任务类型
            String taskType = parameters.getTaskType();
            //获取任务代码
            String taskCode = flowTaskInfoVO.getTaskCode();
            //获取流程id
            String flowInstanceId = flowTaskInfoVO.getFlowInstanceId();
            //1.查询出所有满足物料编号的物料（BOM物料）
            String sql = flowTaskInfoVO.getSqlString();
            log.info("传递过来的SQL语句：" + sql);
            //TODO 查询最顶部的BOM物料编号下所有的物料并进行层级排序
            List<AppSceneCostMaterialBom> list = standardCostService.costMaterialBomService.queryAllBomInfoBytopMaterialCode(topMaterialCode);
            log.info("Full EmptyTask list: {}", list);
            //2.把查询的结果存放在临时表里（表命名规则：前缀_流程id_组件code）
            for (int i = 0; list != null && i < list.size(); i++) {
                AppSceneCostMaterialBom material = list.get(i);
                AppSceneCostResultValue appSceneCostResultValue = AppSceneCostResultValue.builder().belongTopMaterialNo(topMaterialCode).marterialNo(material.getMaterialCode()).parentMarterialNo(material.getParentMaterialCode()).taskType(taskType).taskCode(taskCode).flowInstanceId(flowInstanceId).num(material.getNumber()).build();
                standardCostService.iAppSceneCostResultValueService.save(appSceneCostResultValue);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取任务流程相关信息
     *
     * @param parameters
     * @return
     */
    private static FlowTaskInfoVO analysisFlowTaskInfo(TaskExecutionContext parameters) {
        FlowTaskInfoVO flowTaskInfoVO = new FlowTaskInfoVO();
        if (parameters != null) {
            String s = parameters.toString();
            String[] split = s.split(",");
            for (int i = 0; i < split.length; i++) {
                String s1 = split[i];
                if (s1.contains("taskType")) {
                    String[] split1 = s1.split("=");
                    if (split1[1].contains("\"")) {
                        split1[1] = split1[1].replaceAll("^\"|\"$", ""); // 去除首尾引号
                        flowTaskInfoVO.setTaskType(split1[1]);
                    } else {
                        flowTaskInfoVO.setTaskType(split1[1]);
                    }
                } else if (s1.contains("taskCode")) {
                    String[] split1 = s1.split(":");
                    if (split1[1].contains("\"")) {
                        split1[1] = split1[1].replaceAll("^\"|\"$", ""); // 去除首尾引号
                        flowTaskInfoVO.setTaskCode(split1[1]);
                    } else {
                        flowTaskInfoVO.setTaskCode(split1[1]);
                    }
                } else if (s1.contains("flowInstanceId")) {
                    String[] split1 = s1.split("=");
                    if (split1[1].contains("\"")) {
                        split1[1] = split1[1].replaceAll("^\"|\"$", ""); // 去除首尾引号
                        flowTaskInfoVO.setFlowInstanceId(split1[1]);
                    } else {
                        flowTaskInfoVO.setFlowInstanceId(split1[1]);
                    }
                } else if (s1.contains("sqlString")) {
                    String[] split1 = s1.split(":");
                    if (split1[1].contains("\"")) {
                        split1[1] = split1[1].replaceAll("^\"|\"$", ""); // 去除首尾引号
                        flowTaskInfoVO.setSqlString(split1[1]);
                    } else {
                        flowTaskInfoVO.setSqlString(split1[1]);
                    }
                } else if (s.contains("feeType") && s1.contains("feeType")) {
                    String[] split1 = s1.split(":");
                    if (split1[1].contains("\"")) {
                        split1[1] = split1[1].replaceAll("^\"|\"$", ""); // 去除首尾引号
                        flowTaskInfoVO.setFeeType(split1[1]);
                    } else {
                        flowTaskInfoVO.setFeeType(split1[1]);
                    }
                }
            }
        }
        //通过任务流程id查询流程定义信息（qomo_flow_definition）
        flowTaskInfoVO = queryFlowDefinitionByFlowInstanceId(flowTaskInfoVO);
        return flowTaskInfoVO;
    }

    /**
     * @param flowTaskInfoVO
     */
    private static FlowTaskInfoVO queryFlowDefinitionByFlowInstanceId(FlowTaskInfoVO flowTaskInfoVO) {
        String sql = "SELECT A.model_json from qomo_flow_definition as A LEFT JOIN   qomo_flow_instance AS B ON A.`code`=B.flow_definition_code  WHERE B.id=" + flowTaskInfoVO.getFlowInstanceId() + " limit 1";
        String blob = standardCostService.costMaterialBomMapper.customSelectBlobModelJson(sql);
        if (blob != null) {
            if (blob != null && blob.contains("topMaterialCode")) {
                JSONObject jsonObject1 = JSON.parseObject(blob);
                String topMaterialCode = jsonObject1.getString("topMaterialCode");
                flowTaskInfoVO.setTopMaterialCode(topMaterialCode);
            }
        }
        return flowTaskInfoVO;
    }

    /**
     * 2. 物料成本任务
     */
    public static void materialCostTask(TaskExecutionContext parameters) {
        try {
            //获取任务流程相关信息
            FlowTaskInfoVO flowTaskInfoVO = analysisFlowTaskInfo(parameters);
            //获取最上层的物料号
            String topMaterialCode = flowTaskInfoVO.getTopMaterialCode();
            //获取任务类型
            String taskType = parameters.getTaskType();
            //获取任务代码
            String taskCode = flowTaskInfoVO.getTaskCode();
            //获取流程id
            String flowInstanceId = flowTaskInfoVO.getFlowInstanceId();
            //成本类型
            String feeType = FeeTypeEnum.MaterialCost.getCode();
            //1.查询出物料编号对应的下所有的BOM信息
            List<AppSceneCostResultValue> list = standardCostService.iAppSceneCostResultValueService.getResultValueByTopMaterialNo(topMaterialCode, "MaterialInput", flowInstanceId);
            //2.计算出所有的单个物料的物料成本
            //2.1查询出物料的单价和采购数量
            List<String> MarterialNoList = new ArrayList<>();
            if (list != null && list.size() > 0) {
                MarterialNoList = list.stream().filter(s -> s.getMarterialNo() != null).map(AppSceneCostResultValue::getMarterialNo).collect(Collectors.toList());
            }
            //查询物料的采购价
            Map<String, BigDecimal> purchasePriceMap = standardCostService.costPurchasePriceService.getMinCostPurchasePriceByMaterialNo(MarterialNoList);
            for (int i = 0; i < list.size(); i++) {
                AppSceneCostResultValue appSceneCostResultValue = list.get(i);
                String marterialNo = appSceneCostResultValue.getMarterialNo();
                Integer num = appSceneCostResultValue.getNum();
                if (marterialNo != null && num != null) {
                    BigDecimal price = purchasePriceMap.get(marterialNo);
                    BigDecimal number = new BigDecimal(num.toString());
                    //单价乘以数量
                    BigDecimal totalCost = price.multiply(number);
                    //2.2计算出人工成本并存储到成本结果数据表中
                    AppSceneCostResultValue resultValue = AppSceneCostResultValue.builder().belongTopMaterialNo(topMaterialCode).marterialNo(marterialNo).parentMarterialNo(appSceneCostResultValue.getParentMarterialNo()).taskType(taskType).costType(feeType).taskCode(taskCode).flowInstanceId(flowInstanceId).countValue(totalCost + "").priceOrRate(price).build();
                    standardCostService.iAppSceneCostResultValueService.save(resultValue);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 3.人工成本任务
     */
    public static void laborCostTask(TaskExecutionContext parameters) {
        try {
            //获取任务流程相关信息
            FlowTaskInfoVO flowTaskInfoVO = analysisFlowTaskInfo(parameters);
            //获取最上层的物料号
            String topMaterialCode = flowTaskInfoVO.getTopMaterialCode();
            //获取任务类型
            String taskType = parameters.getTaskType();
            //获取任务代码
            String taskCode = flowTaskInfoVO.getTaskCode();
            //获取流程id
            String flowInstanceId = flowTaskInfoVO.getFlowInstanceId();
            //成本类型
            String feeType = FeeTypeEnum.LaborCost.getCode();
            String sql1 = flowTaskInfoVO.getSqlString();
            log.info("sql1:" + sql1);
            String sql = sql1.replaceAll("^\"|\"$", ""); // 去除首尾引号
            log.info("sql:" + sql);
            JSONObject jsonObject = standardCostService.costMaterialBomMapper.customSelectOne(sql);
            BigDecimal hourRate = new BigDecimal("0");
            if (jsonObject != null) {
                AppSceneCostHourRate rate = JSON.toJavaObject(jsonObject, AppSceneCostHourRate.class);
                hourRate = rate.getLaborHourRate();
            }
            //1.查询出物料编号对应的下所有的BOM信息
            List<AppSceneCostResultValue> list = standardCostService.iAppSceneCostResultValueService.getResultValueByTopMaterialNo(topMaterialCode, "MaterialInput", flowInstanceId);
            //2.计算出所有的单个物料的人工成本
            //2.1查询出物料的人员工时和人工小时费率（涉及：小时费率表和工序表）
            List<String> MarterialNoList = new ArrayList<>();
            if (list != null && list.size() > 0) {
                MarterialNoList = list.stream().filter(s -> s.getMarterialNo() != null).map(AppSceneCostResultValue::getMarterialNo).collect(Collectors.toList());
            }
            //查询工序工时
            Map<String, List<AppSceneCostMaterialProcessHours>> processHoursMap = standardCostService.costMaterialProcessHoursService.getProcessHoursByMaterialNo(MarterialNoList);
            for (int i = 0; i < list.size(); i++) {
                AppSceneCostResultValue appSceneCostResultValue = list.get(i);
                String marterialNo = appSceneCostResultValue.getMarterialNo();
                if (marterialNo != null) {
                    BigDecimal totalCost = new BigDecimal("0.0");
                    BigDecimal laborHours = new BigDecimal("0.0");
                    List<AppSceneCostMaterialProcessHours> costMaterialProcessHours = processHoursMap.get(marterialNo);
                    if (costMaterialProcessHours != null && costMaterialProcessHours.size() > 0) {
                        for (int j = 0; j < costMaterialProcessHours.size(); j++) {
                            AppSceneCostMaterialProcessHours processHours = costMaterialProcessHours.get(j);
                            laborHours = laborHours.add(processHours.getLaborHours());
                            //计算总人工成本
                            totalCost = totalCost.add(processHours.getLaborHours().multiply(hourRate));
                        }
                    }
                    //2.2计算出人工成本并存储到成本结果数据表中
                    AppSceneCostResultValue resultValue = AppSceneCostResultValue.builder().belongTopMaterialNo(topMaterialCode).marterialNo(marterialNo).parentMarterialNo(appSceneCostResultValue.getParentMarterialNo()).taskType(taskType).taskCode(taskCode).flowInstanceId(flowInstanceId).countValue(totalCost + "").costType(feeType).priceOrRate(hourRate).hour(laborHours).build();
                    standardCostService.iAppSceneCostResultValueService.save(resultValue);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 4.制造费用成本任务
     */
    public static void manufacturingCostTask(TaskExecutionContext parameters) {
        try {
            //获取任务流程相关信息
            FlowTaskInfoVO flowTaskInfoVO = analysisFlowTaskInfo(parameters);
            //获取最上层的物料号
            String topMaterialCode = flowTaskInfoVO.getTopMaterialCode();
            //获取任务类型
            String taskType = parameters.getTaskType();
            //获取任务代码
            String taskCode = flowTaskInfoVO.getTaskCode();
            //获取流程id
            String flowInstanceId = flowTaskInfoVO.getFlowInstanceId();
            /**
             * 费用类别
             */
            String feeType = flowTaskInfoVO.getFeeType();
            /**
             * 小时费率
             */
            String sql1 = flowTaskInfoVO.getSqlString();
            log.info("sql1:" + sql1);
            String sql = sql1.replaceAll("^\"|\"$", ""); // 去除首尾引号
            log.info("sql:" + sql);
            JSONObject jsonObject = standardCostService.costMaterialBomMapper.customSelectOne(sql);
            BigDecimal hourRate = new BigDecimal("0");
            if (jsonObject != null) {
                AppSceneCostHourRate rate = JSON.toJavaObject(jsonObject, AppSceneCostHourRate.class);
                hourRate = rate.getLaborHourRate();
            }
            //1.查询出物料编号对应的下所有的BOM信息
            List<AppSceneCostResultValue> list = standardCostService.iAppSceneCostResultValueService.getResultValueByTopMaterialNo(topMaterialCode, "MaterialInput", flowInstanceId);
            //2.根据费用类型进行不同的费用计算
            //查询工序工时
            List<String> MarterialNoList = new ArrayList<>();
            if (list != null && list.size() > 0) {
                MarterialNoList = list.stream().filter(s -> s.getMarterialNo() != null).map(AppSceneCostResultValue::getMarterialNo).collect(Collectors.toList());
            }
            Map<String, List<AppSceneCostMaterialProcessHours>> processHoursMap = standardCostService.costMaterialProcessHoursService.getProcessHoursByMaterialNo(MarterialNoList);
            //2.1机器折旧费（机器工时*费率）
            if (FeeTypeEnum.EquipmentFee.getCode().equals(feeType)) {
                for (int i = 0; i < list.size(); i++) {
                    AppSceneCostResultValue appSceneCostResultValue = list.get(i);
                    String marterialNo = appSceneCostResultValue.getMarterialNo();
                    if (marterialNo != null) {
                        BigDecimal totalCost = new BigDecimal("0.0");
                        BigDecimal laborHours = new BigDecimal("0.0");
                        List<AppSceneCostMaterialProcessHours> costMaterialProcessHours = processHoursMap.get(marterialNo);
                        if (costMaterialProcessHours != null && costMaterialProcessHours.size() > 0) {
                            for (int j = 0; j < costMaterialProcessHours.size(); j++) {
                                AppSceneCostMaterialProcessHours processHours = costMaterialProcessHours.get(j);
                                laborHours = laborHours.add(processHours.getDeviceHours());
                                //计算机器成本
                                totalCost = totalCost.add(processHours.getDeviceHours().multiply(hourRate));
                            }
                        }
                        //2.2计算机器成本并存储到成本结果数据表中
                        AppSceneCostResultValue resultValue = AppSceneCostResultValue.builder().belongTopMaterialNo(topMaterialCode).marterialNo(marterialNo).parentMarterialNo(appSceneCostResultValue.getParentMarterialNo()).taskType(taskType).taskCode(taskCode).flowInstanceId(flowInstanceId).countValue(totalCost + "").costType(feeType).priceOrRate(hourRate).hour(laborHours).build();
                        standardCostService.iAppSceneCostResultValueService.save(resultValue);
                    }
                }
            } else if (FeeTypeEnum.SupplyMaterialFee.getCode().equals(feeType)) {
                //机物料消耗（辅料工时*费率）
                for (int i = 0; i < list.size(); i++) {
                    AppSceneCostResultValue appSceneCostResultValue = list.get(i);
                    String marterialNo = appSceneCostResultValue.getMarterialNo();
                    if (marterialNo != null) {
                        BigDecimal totalCost = new BigDecimal("0.0");
                        BigDecimal laborHours = new BigDecimal("0.0");
                        List<AppSceneCostMaterialProcessHours> costMaterialProcessHours = processHoursMap.get(marterialNo);
                        if (costMaterialProcessHours != null && costMaterialProcessHours.size() > 0) {
                            for (int j = 0; j < costMaterialProcessHours.size(); j++) {
                                AppSceneCostMaterialProcessHours processHours = costMaterialProcessHours.get(j);
                                //TODO 辅料工时来源
                                laborHours = laborHours.add(processHours.getDeviceHours());
                                //机物料消耗
                                totalCost = totalCost.add(processHours.getDeviceHours().multiply(hourRate));
                            }
                        }
                        //2.2计算机物料消耗存储到成本结果数据表中
                        AppSceneCostResultValue resultValue = AppSceneCostResultValue.builder().belongTopMaterialNo(topMaterialCode).marterialNo(marterialNo).parentMarterialNo(appSceneCostResultValue.getParentMarterialNo()).taskType(taskType).taskCode(taskCode).flowInstanceId(flowInstanceId).countValue(totalCost + "").costType(feeType).priceOrRate(hourRate).hour(laborHours).build();
                        standardCostService.iAppSceneCostResultValueService.save(resultValue);
                    }
                }
            } else if (FeeTypeEnum.DriverFee.getCode().equals(feeType)) {
                //2.3水电费（燃动力工时*费率）
                for (int i = 0; i < list.size(); i++) {
                    AppSceneCostResultValue appSceneCostResultValue = list.get(i);
                    String marterialNo = appSceneCostResultValue.getMarterialNo();
                    if (marterialNo != null) {
                        BigDecimal totalCost = new BigDecimal("0.0");
                        BigDecimal laborHours = new BigDecimal("0.0");
                        List<AppSceneCostMaterialProcessHours> costMaterialProcessHours = processHoursMap.get(marterialNo);
                        if (costMaterialProcessHours != null && costMaterialProcessHours.size() > 0) {
                            for (int j = 0; j < costMaterialProcessHours.size(); j++) {
                                AppSceneCostMaterialProcessHours processHours = costMaterialProcessHours.get(j);
                                //TODO 燃动力工时来源
                                laborHours = laborHours.add(processHours.getDeviceHours());
                                //水电费（燃动力工时*费率）成本
                                totalCost = totalCost.add(processHours.getDeviceHours().multiply(hourRate));
                            }
                        }
                        //2.2计算机物料消耗存储到成本结果数据表中
                        AppSceneCostResultValue resultValue = AppSceneCostResultValue.builder().belongTopMaterialNo(topMaterialCode).marterialNo(marterialNo).parentMarterialNo(appSceneCostResultValue.getParentMarterialNo()).taskType(taskType).taskCode(taskCode).flowInstanceId(flowInstanceId).countValue(totalCost + "").costType(feeType).priceOrRate(hourRate).hour(laborHours).build();
                        standardCostService.iAppSceneCostResultValueService.save(resultValue);
                    }
                }
            } else if (FeeTypeEnum.OtherFee.getCode().equals(feeType)) {
                //2.4其他制费（其他工时*费率）
                for (int i = 0; i < list.size(); i++) {
                    AppSceneCostResultValue appSceneCostResultValue = list.get(i);
                    String marterialNo = appSceneCostResultValue.getMarterialNo();
                    if (marterialNo != null) {
                        BigDecimal totalCost = new BigDecimal("0.0");
                        BigDecimal laborHours = new BigDecimal("0.0");
                        List<AppSceneCostMaterialProcessHours> costMaterialProcessHours = processHoursMap.get(marterialNo);
                        if (costMaterialProcessHours != null && costMaterialProcessHours.size() > 0) {
                            for (int j = 0; j < costMaterialProcessHours.size(); j++) {
                                AppSceneCostMaterialProcessHours processHours = costMaterialProcessHours.get(j);
                                //TODO 其他工时来源
                                laborHours = laborHours.add(processHours.getDeviceHours());
                                //其他制费成本
                                totalCost = totalCost.add(processHours.getDeviceHours().multiply(hourRate));
                            }
                        }
                        //2.2计算机物料消耗存储到成本结果数据表中
                        AppSceneCostResultValue resultValue = AppSceneCostResultValue.builder().belongTopMaterialNo(topMaterialCode).marterialNo(marterialNo).parentMarterialNo(appSceneCostResultValue.getParentMarterialNo()).taskType(taskType).taskCode(taskCode).flowInstanceId(flowInstanceId).countValue(totalCost + "").costType(feeType).priceOrRate(hourRate).hour(laborHours).build();
                        standardCostService.iAppSceneCostResultValueService.save(resultValue);
                    }
                }
            } else if (FeeTypeEnum.LogisticsFee.getCode().equals(feeType)) {
                //查出所有的机器折旧费、机物料消耗、水电费、其他制费
                list = standardCostService.iAppSceneCostResultValueService.getResultValueByTopMaterialNo(topMaterialCode, "ManufacturingCost", flowInstanceId);
                //2.5物流费（（机器折旧+机物料消耗+水电费+其他制费）*10.16%）
                //按照物料编号进行分组
                Map<String, List<AppSceneCostResultValue>> materialNoMap = list.stream().collect(Collectors.groupingBy(AppSceneCostResultValue::getMarterialNo));
                for (String key : materialNoMap.keySet()) {
                    List<AppSceneCostResultValue> appSceneCostResultValues = materialNoMap.get(key);
                    BigDecimal totalCost = new BigDecimal("0.0");
                    for (int j = 0; j < appSceneCostResultValues.size(); j++) {
                        AppSceneCostResultValue appSceneCostResultValue = appSceneCostResultValues.get(j);
                        String countValue = appSceneCostResultValue.getCountValue();
                        if (countValue != null) {
                            totalCost = totalCost.add(new BigDecimal(countValue));
                        }
                    }
                    AppSceneCostResultValue resultValue = AppSceneCostResultValue.builder().belongTopMaterialNo(topMaterialCode).marterialNo(key).parentMarterialNo(appSceneCostResultValues.get(0).getParentMarterialNo()).taskType(taskType).taskCode(taskCode).flowInstanceId(flowInstanceId).countValue(totalCost.multiply(new BigDecimal("0.1016")).toString()).costType(feeType).build();
                    standardCostService.iAppSceneCostResultValueService.save(resultValue);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 5.标准成本计算任务
     */
    public static void standardCostTask(TaskExecutionContext parameters) {
        try {
            //获取任务流程相关信息
            FlowTaskInfoVO flowTaskInfoVO = analysisFlowTaskInfo(parameters);
            //获取最上层的物料号
            String topMaterialCode = flowTaskInfoVO.getTopMaterialCode();
            //获取任务类型
            String taskType = parameters.getTaksType();
            //获取任务代码
            String taskCode = flowTaskInfoVO.getTaskCode();
            //获取流程id
            String flowInstanceId = flowTaskInfoVO.getFlowInstanceId();
            String costType = FeeTypeEnum.StandardCost.getCode();
            //1.查询出根物料编号对应的下所有的BOM信息结果值
            List<AppSceneCostResultValue> list = standardCostService.iAppSceneCostResultValueService.getAllResultValueByTopMaterialNo(topMaterialCode, flowInstanceId);
            //2.计算出所有的单个物料的本阶的标准成本(标准成本=物料成本+人工成本+制造费用)
            //按照物料编号进行分组
            Map<String, List<AppSceneCostResultValue>> materialNoMap = list.stream().collect(Collectors.groupingBy(AppSceneCostResultValue::getMarterialNo));
            //遍历根物料下所有的物料进行该物料本阶成本和费用类别的统计
            for (String key : materialNoMap.keySet()) {
                List<AppSceneCostResultValue> appSceneCostResultValues = materialNoMap.get(key);
                //按照费用类别进行分组
                Map<String, List<AppSceneCostResultValue>> costTypeMap = appSceneCostResultValues.stream().filter(s -> s.getCostType() != null).collect(Collectors.groupingBy(AppSceneCostResultValue::getCostType));
                //【本阶标准成本】
                BigDecimal currentCost = new BigDecimal("0.0");
                //1.本阶物料成本
                BigDecimal materialCost = new BigDecimal("0.0");
                //2.本阶人工成本
                BigDecimal laborCost = new BigDecimal("0.0");
                //3.本阶制造费（设备费）
                BigDecimal equipmentCost = new BigDecimal("0.0");
                //4.本阶制造费（辅料费用）
                BigDecimal supplyMaterialCost = new BigDecimal("0.0");
                //5.本阶制造费（水电费、燃动费）
                BigDecimal driveCost = new BigDecimal("0.0");
                //6.本阶制造费（其他制造费用）
                BigDecimal otherCost = new BigDecimal("0.0");
                //7.本阶制造费（物流费）
                BigDecimal logisticsCost = new BigDecimal("0.0");
                //物料单价
                BigDecimal materialUnitPrice = new BigDecimal("0.0");
                //人员工时、小时费率
                BigDecimal laborHour = new BigDecimal("0.0");
                BigDecimal laborHourRate = new BigDecimal("0.0");
                //设备工时、小时费率
                BigDecimal equipmentHour = new BigDecimal("0.0");
                BigDecimal equipmentHourRate = new BigDecimal("0.0");
                //辅料工时、小时费率
                BigDecimal supplyMaterialHour = new BigDecimal("0.0");
                BigDecimal supplyMaterialHourRate = new BigDecimal("0.0");
                //燃动工时、小时费率
                BigDecimal driveHour = new BigDecimal("0.0");
                BigDecimal driveHourRate = new BigDecimal("0.0");
                //其他工时、小时费率
                BigDecimal otherHour = new BigDecimal("0.0");
                BigDecimal otherHourRate = new BigDecimal("0.0");
                AppSceneCostResultValue currentMaterialCost = null;
                //遍历所有费用类别计算本阶标准成本
                for (int j = 0; j < appSceneCostResultValues.size(); j++) {
                    if (j == 0) {
                        currentMaterialCost = appSceneCostResultValues.get(0);
                    }
                    AppSceneCostResultValue appSceneCostResultValue = appSceneCostResultValues.get(j);
                    String countValue = appSceneCostResultValue.getCountValue();
                    if (countValue != null) {
                        currentCost = currentCost.add(new BigDecimal(countValue));
                    }
                }
                //计算本阶各个费用类别费用以及记录本阶物料的工时和小时费率
                for (String feeType : costTypeMap.keySet()) {
                    List<AppSceneCostResultValue> appSceneCostResultValues1 = costTypeMap.get(feeType);
                    for (int m = 0; m < appSceneCostResultValues1.size(); m++) {
                        if (FeeTypeEnum.MaterialCost.getCode().equals(feeType)) {
                            //物料成本
                            materialCost = materialCost.add(new BigDecimal(appSceneCostResultValues1.get(m).getCountValue()));
                            //物料单价
                            materialUnitPrice = appSceneCostResultValues1.get(m).getPriceOrRate();
                        } else if (FeeTypeEnum.LaborCost.getCode().equals(feeType)) {
                            //人工成本
                            laborCost = laborCost.add(new BigDecimal(appSceneCostResultValues1.get(m).getCountValue()));
                            //人工工时、小时费率
                            laborHour = laborHour.add(appSceneCostResultValues1.get(m).getHour());
                            laborHourRate = laborHourRate.add(appSceneCostResultValues1.get(m).getPriceOrRate());
                        } else if (FeeTypeEnum.EquipmentFee.getCode().equals(feeType)) {
                            //设备费用
                            equipmentCost = equipmentCost.add(new BigDecimal(appSceneCostResultValues1.get(m).getCountValue()));
                            //设备工时、小时费率
                            equipmentHour = equipmentHour.add(appSceneCostResultValues1.get(m).getHour());
                            equipmentHourRate = equipmentHourRate.add(appSceneCostResultValues1.get(m).getPriceOrRate());
                        } else if (FeeTypeEnum.SupplyMaterialFee.getCode().equals(feeType)) {
                            //辅料费用
                            supplyMaterialCost = supplyMaterialCost.add(new BigDecimal(appSceneCostResultValues1.get(m).getCountValue()));
                            //辅料工时、小时费率
                            supplyMaterialHour = supplyMaterialHour.add(appSceneCostResultValues1.get(m).getHour());
                            supplyMaterialHourRate = supplyMaterialHourRate.add(appSceneCostResultValues1.get(m).getPriceOrRate());
                        } else if (FeeTypeEnum.DriverFee.getCode().equals(feeType)) {
                            //燃动费用
                            driveCost = driveCost.add(new BigDecimal(appSceneCostResultValues1.get(m).getCountValue()));
                            //燃动工时、小时费率
                            driveHour = driveHour.add(appSceneCostResultValues1.get(m).getHour());
                            driveHourRate = driveHourRate.add(appSceneCostResultValues1.get(m).getPriceOrRate());
                        } else if (FeeTypeEnum.LogisticsFee.getCode().equals(feeType)) {
                            //物流费用
                            logisticsCost = logisticsCost.add(new BigDecimal(appSceneCostResultValues1.get(m).getCountValue()));
                        } else if (FeeTypeEnum.OtherFee.getCode().equals(feeType)) {
                            //其他费用
                            otherCost = otherCost.add(new BigDecimal(appSceneCostResultValues1.get(m).getCountValue()));
                            //其他工时、小时费率
                            otherHour = otherHour.add(appSceneCostResultValues1.get(m).getHour());
                            otherHourRate = otherHourRate.add(appSceneCostResultValues1.get(m).getPriceOrRate());
                        }
                    }
                }
                //本阶不同费用类别成本统计
                JSONObject currentLevelCostTypeDetail = new JSONObject();
                currentLevelCostTypeDetail.put(FeeTypeEnum.MaterialCost.getCode(), materialCost);
                currentLevelCostTypeDetail.put(FeeTypeEnum.LaborCost.getCode(), laborCost);
                currentLevelCostTypeDetail.put(FeeTypeEnum.EquipmentFee.getCode(), equipmentCost);
                currentLevelCostTypeDetail.put(FeeTypeEnum.SupplyMaterialFee.getCode(), supplyMaterialCost);
                currentLevelCostTypeDetail.put(FeeTypeEnum.DriverFee.getCode(), driveCost);
                currentLevelCostTypeDetail.put(FeeTypeEnum.LogisticsFee.getCode(), logisticsCost);
                currentLevelCostTypeDetail.put(FeeTypeEnum.OtherFee.getCode(), otherCost);
                //统计本阶不同费用类别工时、小时费率
                currentLevelCostTypeDetail.put(HourOrRateTypeEnum.MaterialUnitPrice.getCode(), materialUnitPrice);
                currentLevelCostTypeDetail.put(HourOrRateTypeEnum.LaborHour.getCode(), laborHour);
                currentLevelCostTypeDetail.put(HourOrRateTypeEnum.LaborHourRate.getCode(), laborHourRate);
                currentLevelCostTypeDetail.put(HourOrRateTypeEnum.EquipmentHour.getCode(), equipmentHour);
                currentLevelCostTypeDetail.put(HourOrRateTypeEnum.EquipmentHourRate.getCode(), equipmentHourRate);
                currentLevelCostTypeDetail.put(HourOrRateTypeEnum.SupplyMaterialHour.getCode(), supplyMaterialHour);
                currentLevelCostTypeDetail.put(HourOrRateTypeEnum.SupplyMaterialHourRate.getCode(), supplyMaterialHourRate);
                currentLevelCostTypeDetail.put(HourOrRateTypeEnum.DriverHour.getCode(), driveHour);
                currentLevelCostTypeDetail.put(HourOrRateTypeEnum.DriverHourRate.getCode(), driveHourRate);
                currentLevelCostTypeDetail.put(HourOrRateTypeEnum.OtherHour.getCode(), otherHour);
                currentLevelCostTypeDetail.put(HourOrRateTypeEnum.OtherHourRate.getCode(), otherHourRate);
                AppSceneCostResultValue resultValue = AppSceneCostResultValue.builder().belongTopMaterialNo(topMaterialCode).marterialNo(key).parentMarterialNo(currentMaterialCost.getParentMarterialNo()).taskType(taskType).taskCode(taskCode).flowInstanceId(flowInstanceId).countValue(currentCost.toString()).costDetail(JSON.toJSONString(currentLevelCostTypeDetail)).costType(costType).build();
                standardCostService.iAppSceneCostResultValueService.save(resultValue);
            }
            //3.计算出所有的单个物料的累计标准成本
            List<AppSceneCostResultValue> standardCostList = standardCostService.iAppSceneCostResultValueService.getResultValueByTopMaterialNo(topMaterialCode, taskType, flowInstanceId);
            //【累计成本】的计算
            totalCountCost(standardCostList, topMaterialCode, taskType, taskCode, flowInstanceId);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("计算成本失败");
        }
    }

    /**
     * 循环算出各个物料的累计成本
     *
     * @param standardCostList
     * @param topMaterialCode
     * @param taskType
     * @param taskCode
     * @param flowInstanceId
     */
    private static void totalCountCost(List<AppSceneCostResultValue> standardCostList, String topMaterialCode, String taskType, String taskCode, String flowInstanceId) {
        try {
            List<AppSceneCostStandardDetail> costStandardDetails = new ArrayList<>();
            //父类对应的子节点（除了最上层节点）
            Map<String, List<AppSceneCostResultValue>> parentMaterialNoMap = standardCostList.stream().filter(appSceneCostResultValue -> appSceneCostResultValue.getParentMarterialNo() != null).collect(Collectors.groupingBy(AppSceneCostResultValue::getParentMarterialNo));
            for (int i = 0; i < standardCostList.size(); i++) {
                //当前节点及其对应的本阶成本
                AppSceneCostResultValue oldAppSceneCostResultValue = standardCostList.get(i);
                BigDecimal totalCost = new BigDecimal(oldAppSceneCostResultValue.getCountValue());
                //循环计算出当前节点的累计成本
                String costDetail = oldAppSceneCostResultValue.getCostDetail();
                //查询当前节点各个类型的成本
                BigDecimal totalMaterialCost = new BigDecimal(0);
                BigDecimal totalLabourCost = new BigDecimal(0);
                BigDecimal totalEquipmentCost = new BigDecimal(0);
                BigDecimal totalSupplyMaterialCost = new BigDecimal(0);
                BigDecimal totalDriveCost = new BigDecimal(0);
                BigDecimal totalLogisticsCost = new BigDecimal(0);
                BigDecimal totalOtherCost = new BigDecimal(0);
                JSONObject costDetailJson = JSON.parseObject(costDetail);
                TotalResultValue hourOrRateResultValue = loopCount(oldAppSceneCostResultValue, parentMaterialNoMap, totalMaterialCost, totalLabourCost, totalEquipmentCost, totalSupplyMaterialCost, totalDriveCost, totalLogisticsCost, totalOtherCost, totalCost);
                costDetailJson.put(TotalCostEnum.TotalMaterialCost.getCode(), hourOrRateResultValue.getTotalMaterialCost());
                costDetailJson.put(TotalCostEnum.TotalLaborCost.getCode(), hourOrRateResultValue.getTotalLabourCost());
                costDetailJson.put(TotalCostEnum.TotalEquipmentFee.getCode(), hourOrRateResultValue.getTotalEquipmentCost());
                costDetailJson.put(TotalCostEnum.TotalSupplyMaterialFee.getCode(), hourOrRateResultValue.getTotalSupplyMaterialCost());
                costDetailJson.put(TotalCostEnum.TotalDriverFee.getCode(), hourOrRateResultValue.getTotalDriveCost());
                costDetailJson.put(TotalCostEnum.TotalLogisticsFee.getCode(), hourOrRateResultValue.getTotalLogisticsCost());
                costDetailJson.put(TotalCostEnum.TotalOtherFee.getCode(), hourOrRateResultValue.getTotalOtherCost());
                costDetailJson.put(TotalCostEnum.TotalStandardCost.getCode(), hourOrRateResultValue.getTotalCost());
                oldAppSceneCostResultValue.setCostDetail(costDetailJson.toJSONString());
                oldAppSceneCostResultValue.setTotalValue(hourOrRateResultValue.getTotalCost().toString());
                standardCostService.iAppSceneCostResultValueService.updateById(oldAppSceneCostResultValue);
                //构造单行成本查询记录
                AppSceneCostStandardDetail costStandardDetail = AppSceneCostStandardDetail.builder().materialNumber(oldAppSceneCostResultValue.getMarterialNo()).materialName(oldAppSceneCostResultValue.getMarterialNo()).parentMaterialNumber(oldAppSceneCostResultValue.getParentMarterialNo())
//                    .versionNumber()
//                    .level()
//                    .sort()
//                    .figureNumber()
                        .dosage(oldAppSceneCostResultValue.getNum() != null ? new Double(oldAppSceneCostResultValue.getNum()) : 0).unit(oldAppSceneCostResultValue.getUnit()).materialCost(costDetailJson.getBigDecimal(FeeTypeEnum.MaterialCost.getCode())).laborCost(costDetailJson.getBigDecimal(FeeTypeEnum.LaborCost.getCode())).equipmentCost(costDetailJson.getBigDecimal(FeeTypeEnum.EquipmentFee.getCode())).supplyMaterialCost(costDetailJson.getBigDecimal(FeeTypeEnum.SupplyMaterialFee.getCode())).driveCost(costDetailJson.getBigDecimal(FeeTypeEnum.DriverFee.getCode())).logisticsCost(costDetailJson.getBigDecimal(FeeTypeEnum.LogisticsFee.getCode())).otherCost(costDetailJson.getBigDecimal(FeeTypeEnum.OtherFee.getCode())).currentStandardCost(new BigDecimal(oldAppSceneCostResultValue.getCountValue())).totalMaterialCost(costDetailJson.getBigDecimal(TotalCostEnum.TotalMaterialCost.getCode())).totalLaborCost(costDetailJson.getBigDecimal(TotalCostEnum.TotalLaborCost.getCode())).totalEquipmentCost(costDetailJson.getBigDecimal(TotalCostEnum.TotalEquipmentFee.getCode())).totalSupplyMaterialCost(costDetailJson.getBigDecimal(TotalCostEnum.TotalSupplyMaterialFee.getCode())).totalDriveCost(costDetailJson.getBigDecimal(TotalCostEnum.TotalDriverFee.getCode())).totalLogisticsCost(costDetailJson.getBigDecimal(TotalCostEnum.TotalLogisticsFee.getCode())).totalOtherCost(costDetailJson.getBigDecimal(TotalCostEnum.TotalOtherFee.getCode())).totalStandardCost(costDetailJson.getBigDecimal(TotalCostEnum.TotalStandardCost.getCode())).createTime(new Date()).build();
                costStandardDetails.add(costStandardDetail);
            }
            //更新结果集到标准成本单行查询中去
            standardCostService.iAppSceneCostStandardDetailService.saveBatch(costStandardDetails);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * //循环算出各个物料的累计成本
     * 递归计算出累计成本
     *
     * @param appSceneCostResultValue
     * @param parentMaterialNoMap
     */
    private static TotalResultValue loopCount(AppSceneCostResultValue appSceneCostResultValue, Map<String, List<AppSceneCostResultValue>> parentMaterialNoMap, BigDecimal totalMaterialCost, BigDecimal totalLabourCost, BigDecimal totalEquipmentCost, BigDecimal totalSupplyMaterialCost, BigDecimal totalDriveCost, BigDecimal totalLogisticsCost, BigDecimal totalOtherCost, BigDecimal totalCost) {
        String marterialNo = appSceneCostResultValue.getMarterialNo();
        if (parentMaterialNoMap.containsKey(marterialNo)) {
            //获取当前节点的子节点并遍历
            List<AppSceneCostResultValue> appSceneCostResultValues = parentMaterialNoMap.get(marterialNo);
            for (int j = 0; j < appSceneCostResultValues.size(); j++) {
                AppSceneCostResultValue appSceneCostResultValue1 = appSceneCostResultValues.get(j);
                String countValue = appSceneCostResultValue1.getCountValue();
                //标准成本累计
                if (countValue != null) {
                    totalCost = totalCost.add(new BigDecimal(countValue));
                }
                //各个费用类别成本累计
                if (appSceneCostResultValue1.getCostDetail() != null) {
                    JSONObject costDetailJson = JSON.parseObject(appSceneCostResultValue1.getCostDetail());
                    if (costDetailJson != null) {
                        totalMaterialCost = totalMaterialCost.add(costDetailJson.getBigDecimal(FeeTypeEnum.MaterialCost.getCode()));
                        totalLabourCost = totalLabourCost.add(costDetailJson.getBigDecimal(FeeTypeEnum.LaborCost.getCode()));
                        totalEquipmentCost = totalEquipmentCost.add(costDetailJson.getBigDecimal(FeeTypeEnum.EquipmentFee.getCode()));
                        totalSupplyMaterialCost = totalSupplyMaterialCost.add(costDetailJson.getBigDecimal(FeeTypeEnum.SupplyMaterialFee.getCode()));
                        totalDriveCost = totalDriveCost.add(costDetailJson.getBigDecimal(FeeTypeEnum.DriverFee.getCode()));
                        totalLogisticsCost = totalLogisticsCost.add(costDetailJson.getBigDecimal(FeeTypeEnum.LogisticsFee.getCode()));
                        totalOtherCost = totalOtherCost.add(costDetailJson.getBigDecimal(FeeTypeEnum.OtherFee.getCode()));
                    }
                }
                //判断当前子节点是否还有子节点
                if (parentMaterialNoMap.containsKey(appSceneCostResultValue1.getMarterialNo())) {
                    //如果还有子节点则继续递归
                    loopCount(appSceneCostResultValue1, parentMaterialNoMap, totalMaterialCost, totalLabourCost, totalEquipmentCost, totalSupplyMaterialCost, totalDriveCost, totalLogisticsCost, totalOtherCost, totalCost);
                }
            }
        }
        TotalResultValue hourOrRateResultValue = TotalResultValue.builder().totalMaterialCost(totalMaterialCost).totalLabourCost(totalLabourCost).totalEquipmentCost(totalEquipmentCost).totalSupplyMaterialCost(totalSupplyMaterialCost).totalDriveCost(totalDriveCost).totalLogisticsCost(totalLogisticsCost).totalOtherCost(totalOtherCost).totalCost(totalCost).build();
        appSceneCostResultValue.setTotalValue(totalCost.toString());
        return hourOrRateResultValue;
    }
}
