package com.zt.plat.module.qms.resource.material.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.zt.plat.framework.common.exception.ServiceException;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.security.core.LoginUser;
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
import com.zt.plat.module.bpm.api.task.BpmProcessInstanceApi;
import com.zt.plat.module.bpm.api.task.BpmTaskApi;
import com.zt.plat.module.bpm.api.task.dto.*;
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFileWithUrlRespDTO;
import com.zt.plat.module.qms.api.task.BMPCallbackInterface;
import com.zt.plat.module.qms.api.task.dto.QmsBpmDTO;
import com.zt.plat.module.qms.business.bus.liteflow.param.SampleEntrustDetail;
import com.zt.plat.module.qms.business.bus.liteflow.param.SampleEntrustDetailProject;
import com.zt.plat.module.qms.business.bus.liteflow.param.SampleEntrustParam;
import com.zt.plat.module.qms.business.bus.liteflow.slot.SampleEntrustContext;
import com.zt.plat.module.qms.business.bus.service.SampleEntrustService;
import com.zt.plat.module.qms.business.config.dal.dataobject.ConfigWarehouseLocationDO;
import com.zt.plat.module.qms.business.config.service.ConfigWarehouseLocationService;
import com.zt.plat.module.qms.common.data.service.DataKeyCheckService;
import com.zt.plat.module.qms.common.dic.dal.dataobject.DictionaryBusinessDO;
import com.zt.plat.module.qms.common.dic.service.DictionaryBusinessService;
import com.zt.plat.module.qms.common.service.BusinessFileService;
import com.zt.plat.module.qms.core.code.SequenceUtil;
import com.zt.plat.module.qms.enums.AdjustType;
import com.zt.plat.module.qms.enums.LockType;
import com.zt.plat.module.qms.enums.QmsBpmConstant;
import com.zt.plat.module.qms.enums.QmsCommonConstant;
import com.zt.plat.module.qms.resource.material.constant.MaterialConstants;
import com.zt.plat.module.qms.resource.material.controller.vo.*;
import com.zt.plat.module.qms.resource.material.dal.dataobject.*;
import com.zt.plat.module.qms.resource.material.dal.mapper.MaterialLifecycleMapper;
import com.zt.plat.module.qms.resource.material.enums.*;
import com.zt.plat.module.qms.resource.material.utils.BpmnXmlUtil;
import com.zt.plat.module.system.api.dept.DeptApi;
import com.zt.plat.module.system.api.dept.dto.DeptRespDTO;
import com.zt.plat.module.system.api.user.AdminUserApi;
import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.zt.plat.module.qms.enums.ErrorCodeConstants.*;
import static com.zt.plat.module.qms.enums.QmsBpmConstant.BPM_CALLBACK_BEAN_NAME;

/**
 * 物料通用流程,物料验收、退换货 Service 实现类 
 *
 * @author 后台管理
 */
@Slf4j
@Service("materialLifecycleService")
@Validated
public class MaterialLifecycleServiceImpl implements MaterialLifecycleService , BMPCallbackInterface {

    @Resource
    private MaterialLifecycleMapper materialLifecycleMapper;
    @Autowired
    private MaterialBatchService materialBatchService;
    @Autowired
    private MaterialLifecycleDetailService materialLifecycleDetailService;
    @Autowired
    private BpmTaskApi bpmTaskApi;
    @Autowired
    private BpmProcessInstanceApi bpmProcessInstanceApi;
    @Autowired
    private DataKeyCheckService dataKeyCheckService;
    @Autowired
    private DictionaryBusinessService dictionaryBusinessService;
    @Autowired
    private MaterialProductService materialProductService;
    @Autowired
    private BusinessFileService businessFileService;
    @Autowired
    private MaterialInfomationService materialInfomationService;
    @Autowired
    private MaterialInventoryOutboundService materialInventoryOutboundService;
    @Autowired
    private DeptApi deptApi;
    @Autowired
    private ConfigWarehouseLocationService configWarehouseLocationService;
    @Autowired
    private AdminUserApi adminUserApi;
    @Autowired
    private MaterialInventoryCheckItemService materialInventoryCheckItemService;
    @Autowired
    private MaterialInventoryCheckDetailService materialInventoryCheckDetailService;
    @Autowired
    private MaterialInventoryInboundService materialInventoryInboundService;
    @Autowired
    private MaterialCorrelationAssayService materialCorrelationAssayService;
    @Autowired
    private SampleEntrustService sampleEntrustService;
    @Autowired
    private SequenceUtil sequenceUtil;

    @Transactional
    @Override
    public MaterialLifecycleRespVO createMaterialLifecycle(MaterialLifecycleSaveReqVO createReqVO) {

        MaterialLifecycleDO mtrlLfc = BeanUtils.toBean(createReqVO, MaterialLifecycleDO.class);
        mtrlLfc.setSubmitStatus(0);
        String loginUserNickname = SecurityFrameworkUtils.getLoginUserNickname();
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        assert loginUser != null;
        mtrlLfc.setApplyUser(loginUserNickname)
                .setApplyUserId(loginUser.getId())
                .setApplyDepartment(loginUser.getVisitDeptName())
                .setApplyDepartmentId(loginUser.getVisitDeptId())
                .setApplyTime(LocalDateTime.now());
        mtrlLfc.setFlowStatus(QmsCommonConstant.NOT_START).setProgress(QmsCommonConstant.NOT_START);
        // 插入
        materialLifecycleMapper.insert(mtrlLfc);
        MaterialFlowType flowType = MaterialFlowType.fromName(mtrlLfc.getBusinessType());
        List<MaterialLifecycleDetailSaveReqVO> detailList = createReqVO.getDetailList();
        if (flowType != MaterialFlowType.inventory_check) {
            if (CollUtil.isEmpty(detailList))
                return BeanUtils.toBean(mtrlLfc, MaterialLifecycleRespVO.class);
        }

        // 根据业务类型组装明细
        List<MaterialLifecycleDetailDO> detailDOS = getLifecycleDetailDOSByBusinessType(detailList, mtrlLfc);
        materialLifecycleDetailService.saveBatch(detailDOS);

        switch (flowType) {
            case return_material, replace_material -> lockReturnExchangeResources(detailDOS, LockType.lock);
            case verify_calibrate -> lockVerifyCalibrateResources(detailDOS, LockType.lock);
            case inventory_check -> {
                List<MaterialInventoryCheckItemSaveReqVO> checkItems = validProductAndOperator(createReqVO);
                JSONObject customConfigObj = getCheckCustomConfigObj(createReqVO);
                mtrlLfc.setCustomConfig(customConfigObj.toJSONString());
                materialLifecycleMapper.updateById(mtrlLfc);
                List<MaterialInventoryCheckItemDO> checkItemDOS = getCheckItemDOS(checkItems, mtrlLfc);
                materialInventoryCheckItemService.saveBatch(checkItemDOS);

            }
            case acceptance -> // 需要检查是否检化验
                    this.saveAssays(detailDOS, detailList);
        }

        return BeanUtils.toBean(mtrlLfc, MaterialLifecycleRespVO.class);
    }

    private List<MaterialInventoryCheckItemSaveReqVO> validProductAndOperator(MaterialLifecycleSaveReqVO createReqVO) {
        List<MaterialInventoryCheckItemSaveReqVO> checkItems = createReqVO.getCheckItems();
        if (CollUtil.isEmpty(checkItems)) throw new ServiceException(1_032_160_000, "盘点项不能为空");
        List<Long> productIds = checkItems.stream().map(MaterialInventoryCheckItemSaveReqVO::getProductId)
                .filter(Objects::nonNull).toList();
        if (checkItems.size() != productIds.size()) throw new ServiceException(1_032_160_000, "盘点项物料大类不能为空");
        List<MaterialProductDO> products = materialProductService.getListByIds(productIds);
        if (CollUtil.isEmpty(products) || products.size() != productIds.size())
            throw new ServiceException(1_032_160_000, "物料大类不存在或与传入的数量不匹配");
        Long operatorId = createReqVO.getOperatorId();
        CommonResult<AdminUserRespDTO> user = adminUserApi.getUser(operatorId);
        if (user.getData() == null) throw new ServiceException(1_032_160_000, "执行人不存在");
        return checkItems;
    }

    private List<MaterialInventoryCheckItemDO> getCheckItemDOS(List<MaterialInventoryCheckItemSaveReqVO> checkItems, MaterialLifecycleDO mtrlLfc) {
        List<Long> productIds = checkItems.stream().map(MaterialInventoryCheckItemSaveReqVO::getProductId).toList();
        cn.hutool.json.JSONObject customConfig = JSONUtil.parseObj(mtrlLfc.getCustomConfig());
        cn.hutool.json.JSONArray warehouseIds = customConfig.getJSONArray("warehouseIds");
        Map<Long, Long> stockQuantityByPdtIds = materialProductService.getStockQuantityByPdtIds(productIds, warehouseIds.toList(Long.class));
        return checkItems.stream().map(item ->
                new MaterialInventoryCheckItemDO()
                .setParentId(mtrlLfc.getId())
                .setProductId(item.getProductId())
                .setExpected(BigDecimal.valueOf(stockQuantityByPdtIds.get(item.getProductId()) == null ? 0 : stockQuantityByPdtIds.get(item.getProductId())))
                .setRemark(item.getRemark())).toList();
    }

    private JSONObject getCheckCustomConfigObj(MaterialLifecycleSaveReqVO createReqVO) {
        List<Long> departmentIds = createReqVO.getDepartmentIds();
        List<Long> warehouseIds = createReqVO.getWarehouseIds();
        if (CollUtil.isEmpty(departmentIds) || CollUtil.isEmpty(warehouseIds))
            throw new ServiceException(1_032_160_000, "部门、仓库不能为空");
        // 校验仓库、部门存在
        CommonResult<List<DeptRespDTO>> deptListResult = deptApi.getDeptList(departmentIds);
        List<DeptRespDTO> depts = deptListResult.getData();
        if (CollUtil.isEmpty(depts) || depts.size() != departmentIds.size())
            throw new ServiceException(1_032_160_000, "部门不存在或与传入的数量不匹配");
        List<ConfigWarehouseLocationDO> warehouseLocations = configWarehouseLocationService.getListByIds(warehouseIds);
        if (CollUtil.isEmpty(warehouseLocations) || warehouseLocations.size() != warehouseIds.size())
            throw new ServiceException(1_032_160_000, "仓库不存在或与传入的数量不匹配");

        JSONObject customConfigObj = new JSONObject();
        customConfigObj.put("departmentIds", departmentIds);
        customConfigObj.put("warehouseIds", warehouseIds);
        return customConfigObj;
    }

    private void lockVerifyCalibrateResources(List<MaterialLifecycleDetailDO> detailDOS, LockType lockType) {
        // 批次
        List<MaterialLifecycleDetailDO> batches = detailDOS.stream().filter(detail -> detail.getBatchId() != null).toList();
        if (CollUtil.isNotEmpty(batches)) {
            materialBatchService.lockBatchVerifyCalibrateCount(batches, lockType);
        }
    }

    private void lockReturnExchangeResources(List<MaterialLifecycleDetailDO> detailDOS, LockType lockType) {
        // 批次
        List<MaterialLifecycleDetailDO> batches = detailDOS.stream().filter(detail -> detail.getBatchId() != null).toList();
        // 工段
        List<MaterialLifecycleDetailDO> gongs = detailDOS.stream().filter(detail -> detail.getBatchGongduanId() != null).toList();
        // 实例
        List<MaterialLifecycleDetailDO> infomations = detailDOS.stream().filter(detail -> detail.getInfomationId() != null).toList();
        if (CollUtil.isNotEmpty(batches)) {
            materialBatchService.lockBatchReturnExchangeCount(batches, lockType);
        }
        if (CollUtil.isNotEmpty(gongs)) {
            materialBatchService.lockGongduanReturnExchangeCount(gongs, lockType);
        }
        if (CollUtil.isNotEmpty(infomations)) {
            materialInfomationService.lockInfomationInbOrOutbStatus(infomations, lockType);
        }
    }

    private void updateReturnExchangeResources(List<MaterialLifecycleDetailDO> detailDOS, AdjustType adjustType) {
        // 批次
        List<MaterialLifecycleDetailDO> batches = detailDOS.stream().filter(detail -> detail.getBatchId() != null).toList();
        // 工段
        List<MaterialLifecycleDetailDO> gongs = detailDOS.stream().filter(detail -> detail.getBatchGongduanId() != null).toList();
        // 实例
        List<MaterialLifecycleDetailDO> infomations = detailDOS.stream().filter(detail -> detail.getInfomationId() != null).toList();
        if (CollUtil.isNotEmpty(batches)) {
            materialBatchService.updateBatchReturnExchangeCount(batches, adjustType);
        }
        if (CollUtil.isNotEmpty(gongs)) {
            materialBatchService.updateGongduanReturnExchangeCount(gongs, adjustType);
        }
        if (CollUtil.isNotEmpty(infomations)) {
            materialInfomationService.updateInfomationInbOrOutbStatus(infomations, adjustType);
        }
    }

    private List<MaterialLifecycleDetailDO> getLifecycleDetailDOSByBusinessType(List<MaterialLifecycleDetailSaveReqVO> detailList, MaterialLifecycleDO mtrlLfc) {
        MaterialFlowType type = MaterialFlowType.fromName(mtrlLfc.getBusinessType());

        return switch (type) {
            case acceptance -> getLifecycleDetailDOSByAcceptance(detailList, mtrlLfc);
            case make_apply -> getLifecycleDetailDOSByMakeApply(detailList, mtrlLfc);
            case return_material,
                 replace_material ->
                    getLifecycleDetailDOSByReturnExchangeMaterial(detailList, mtrlLfc);
            case verify_calibrate -> getLifecycleDetailDOSByVerifyCalibrate(detailList, mtrlLfc);
            case inventory_check -> List.of();
        };
    }

    /**
     * 工段列表转流程明细列表
     *
     */
    private List<MaterialLifecycleDetailDO> getLifecycleDetailDOsByGongs(List<MaterialBatchDO> gongs, MaterialLifecycleDO mtrlLfc) {
        return gongs.stream().map(gong -> {
            MaterialLifecycleDetailDO detailDO = new MaterialLifecycleDetailDO();
            detailDO.setLifecycleId(mtrlLfc.getId())
                    .setProductId(gong.getProductId()).setBatchId(gong.getParentId())
                    .setBatchGongduanId(gong.getId()).setBusinessType(mtrlLfc.getBusinessType())
                    .setAssayFlag(gong.getAssayFlag())
                    .setTreatmentStatus(0);
            return detailDO;
        }).toList();
    }

    @Transactional
    @Override
    public MaterialLifecycleDO updateMaterialLifecycle(MaterialLifecycleSaveReqVO updateReqVO) {
        Long reqId = updateReqVO.getId();
        MaterialLifecycleDO lifecycleDO = materialLifecycleMapper.selectById(reqId);
        if (lifecycleDO == null) throw exception(MATERIAL_LIFECYCLE_NOT_EXISTS);
        if (lifecycleDO.getSubmitStatus() == 1) throw new ServiceException(1_032_160_000, "流程已提交，不可修改");
        // 更新
        businessFileService.deleteBusinessFileList(updateReqVO.getDeleteFileIdList());
        MaterialLifecycleDO mtrlLfc = BeanUtils.toBean(updateReqVO, MaterialLifecycleDO.class);
        MaterialFlowType flowType = MaterialFlowType.fromName(mtrlLfc.getBusinessType());
        if (flowType == MaterialFlowType.inventory_check) {
            List<MaterialInventoryCheckItemSaveReqVO> checkItems = validProductAndOperator(updateReqVO);
            // 删除盘点项
            materialInventoryCheckItemService.deleteItemsByLfcId(reqId);

            JSONObject customConfigObj = getCheckCustomConfigObj(updateReqVO);
            mtrlLfc.setCustomConfig(customConfigObj.toJSONString());
            materialLifecycleMapper.updateById(mtrlLfc);
            List<MaterialInventoryCheckItemDO> checkItemDOS = getCheckItemDOS(checkItems, mtrlLfc);
            materialInventoryCheckItemService.saveBatch(checkItemDOS);
            return mtrlLfc;
        }
        List<MaterialLifecycleDetailSaveReqVO> detailList = updateReqVO.getDetailList();
        if (CollUtil.isEmpty(detailList)) {
            materialLifecycleMapper.updateById(mtrlLfc);
            return mtrlLfc;
        }

        List<MaterialLifecycleDetailDO> oriDetailList = materialLifecycleDetailService.getDetailListByLfcId(reqId);
        // 释放原来退换货资源
        boolean isReturnExchangeMaterial =
                MaterialFlowType.return_material == flowType || MaterialFlowType.replace_material == flowType;
        if (isReturnExchangeMaterial)
            lockReturnExchangeResources(oriDetailList, LockType.unlock);
        // 释放原理物料检定资源
        boolean isVerifyCalibrate = MaterialFlowType.verify_calibrate == flowType;
        if (isVerifyCalibrate) lockVerifyCalibrateResources(oriDetailList, LockType.unlock);

        // 删除原来的明细
        materialLifecycleDetailService.deleteLifecycleDetailListByLfcId(reqId);

        List<MaterialLifecycleDetailDO> detailDOS = getLifecycleDetailDOSByBusinessType(detailList, mtrlLfc);
        // 保存新的明细
        materialLifecycleDetailService.saveBatch(detailDOS);
        if (isReturnExchangeMaterial) lockReturnExchangeResources(detailDOS, LockType.lock);
        if (isVerifyCalibrate) lockVerifyCalibrateResources(oriDetailList, LockType.lock);
        if (flowType == MaterialFlowType.acceptance) {
            this.saveAssays(detailDOS, detailList);
        }
        materialLifecycleMapper.updateById(mtrlLfc);
        return mtrlLfc;
    }

    private void saveAssays(List<MaterialLifecycleDetailDO> detailDOS, List<MaterialLifecycleDetailSaveReqVO> detailList) {
        // 需要检查是否检化验
        List<MaterialLifecycleDetailDO> assayDetails = detailDOS.stream().filter(detail -> detail.getAssayFlag() == 1).toList();
        if (CollUtil.isEmpty(assayDetails)) return;
        Map<Long, MaterialLifecycleDetailSaveReqVO> gongToReqMap = detailList.stream()
                .collect(Collectors.toMap(MaterialLifecycleDetailSaveReqVO::getBatchGongduanId, Function.identity()));
        List<Long> pdtIds = assayDetails.stream().map(MaterialLifecycleDetailDO::getProductId).toList();
        List<MaterialProductDO> pdts = materialProductService.getListByIds(pdtIds);
        Map<Long, MaterialProductDO> pdtMapById = pdts.stream().collect(Collectors.toMap(MaterialProductDO::getId, Function.identity()));
        List<MaterialCorrelationAssayDO> assayDOS = assayDetails.stream().map(detail -> {
            MaterialProductDO product = pdtMapById.get(detail.getProductId());
            MaterialLifecycleDetailSaveReqVO reqVO = gongToReqMap.get(detail.getBatchGongduanId());
            MaterialCorrelationAssaySaveReqVO assayInfo = reqVO.getAssayInfo();
            return new MaterialCorrelationAssayDO()
                    .setBusinessType(MaterialAssayBusinessType.accept_assay.name())
                    .setProductId(detail.getProductId())
                    .setBatchId(detail.getBatchId())
                    .setGongduanId(detail.getBatchGongduanId())
                    // 样品大类
                    .setSampleId(assayInfo != null && assayInfo.getSampleId() !=  null
                            ? assayInfo.getSampleId()
                            : Long.parseLong(StrUtil.isNotBlank(product.getSampleIds()) ? product.getSampleIds() : "0"))
                    .setSampleName(product.getName())
                    .setSampleCode(sequenceUtil.genCode(MaterialConstants.SEQUENCE_ASSAY_ACCEPT_SAMPLE_KEY))
                    // 检测项
                    .setItem(assayInfo != null && CollUtil.isNotEmpty(assayInfo.getItems())
                            ? JSONUtil.toJsonStr(assayInfo.getItems())
                            : null)
                    .setSource(String.valueOf(detail.getId()))
                    .setAssayStatus(QmsCommonConstant.NOT_START);
        }).toList();

        // 保存验收
        materialCorrelationAssayService.saveBatch(assayDOS);
        // 设置明细的 assayId
        for (int i = 0; i < assayDetails.size(); i++) {
            assayDetails.get(i).setAssayId(assayDOS.get(i).getId());
        }
        materialLifecycleDetailService.updateBatch(assayDetails);
    }

    private List<MaterialLifecycleDetailDO> getLifecycleDetailDOSByAcceptance(List<MaterialLifecycleDetailSaveReqVO> detailList, MaterialLifecycleDO mtrlLfc) {

        List<Long> gongIds = detailList.stream().map(MaterialLifecycleDetailSaveReqVO::getBatchGongduanId).toList();
        Map<Long, Integer> gongAssayMap = detailList.stream().collect(Collectors.toMap(
                MaterialLifecycleDetailSaveReqVO::getBatchGongduanId, MaterialLifecycleDetailSaveReqVO::getAssayFlag));
        List<MaterialBatchDO> gongs = materialBatchService.getGongduanListByGongIds(gongIds);
        if (CollUtil.isEmpty(gongs) || gongs.size() != gongIds.size())
            throw new ServiceException(1_032_160_000, "工段不存在或与传入的工段数量不匹配");
        for (MaterialBatchDO gong : gongs) {
            gong.setAssayFlag(gongAssayMap.get(gong.getId()));
        }

        return getLifecycleDetailDOsByGongs(gongs, mtrlLfc);
    }

    private List<MaterialLifecycleDetailDO> getLifecycleDetailDOSByMakeApply(List<MaterialLifecycleDetailSaveReqVO> detailList, MaterialLifecycleDO mtrlLfc) {
        List<Long> pdtIds = detailList.stream().map(MaterialLifecycleDetailSaveReqVO::getProductId).toList();
        List<MaterialProductDO> products = materialProductService.getListByIds(pdtIds);
        if (CollUtil.isEmpty(products) || products.size() != pdtIds.size())
            throw new ServiceException(1_032_160_000, "物料大类不存在或与传入的数量不匹配");

        return detailList.stream().map(detail -> {
            MaterialLifecycleDetailDO detailDO = new MaterialLifecycleDetailDO();
            detailDO.setLifecycleId(mtrlLfc.getId())
                    .setProductId(detail.getProductId())
                    .setInfluenceCount(detail.getInfluenceCount())
                    .setBusinessType(mtrlLfc.getBusinessType())
                    .setTreatmentStatus(0)
                    .setRemark(detail.getRemark());
            return detailDO;
        }).toList();
    }

    private List<MaterialLifecycleDetailDO> getLifecycleDetailDOSByReturnExchangeMaterial(List<MaterialLifecycleDetailSaveReqVO> detailList, MaterialLifecycleDO mtrlLfc) {
        // 批次
        List<MaterialLifecycleDetailSaveReqVO> reqBatches = detailList.stream().filter(detail -> detail.getBatchId() != null).toList();
        // 工段
        List<MaterialLifecycleDetailSaveReqVO> reqGongs = detailList.stream().filter(detail -> detail.getBatchGongduanId() != null).toList();
        // 实例
        List<MaterialLifecycleDetailSaveReqVO> reqInfomations = detailList.stream().filter(detail -> detail.getInfomationId() != null).toList();

        if (reqBatches.size() + reqGongs.size() + reqInfomations.size() != detailList.size())
            throw new ServiceException(1_032_160_000, "批次、工段、实例数量之和与传入的明细数量不匹配");
        List<MaterialLifecycleDetailDO> totalDetailDOS = new ArrayList<>();
        if (CollUtil.isNotEmpty(reqBatches)) {
            validBatchReturnExchangeMaterial(reqBatches);
            List<MaterialLifecycleDetailDO> detailDOS = getLifecycleDetailDOSByReturnExchangeMaterial(mtrlLfc, reqBatches);
            totalDetailDOS.addAll(detailDOS);
        }
        if (CollUtil.isNotEmpty(reqGongs)) {
            validGongReturnExchangeMaterial(reqGongs);
            List<MaterialLifecycleDetailDO> detailDOS = getLifecycleDetailDOSByReturnExchangeMaterial(mtrlLfc, reqGongs);

            totalDetailDOS.addAll(detailDOS);
        }
        if (CollUtil.isNotEmpty(reqInfomations)) {
            validInfomationReturnExchangeMaterial(reqInfomations);
            List<MaterialLifecycleDetailDO> detailDOS = getLifecycleDetailDOSByReturnExchangeMaterial(mtrlLfc, reqInfomations);
            totalDetailDOS.addAll(detailDOS);
        }
        return totalDetailDOS;
    }

    private List<MaterialLifecycleDetailDO> getLifecycleDetailDOSByVerifyCalibrate(List<MaterialLifecycleDetailSaveReqVO> detailList, MaterialLifecycleDO mtrlLfc) {
        List<MaterialLifecycleDetailSaveReqVO> reqDetails = detailList.stream().filter(detail -> detail.getBatchId() != null).toList();
        if (CollUtil.isEmpty(reqDetails) || reqDetails.size() != detailList.size())
            throw new ServiceException(1_032_160_000, "批次数量与传入的明细数量不匹配");
        List<Long> batIds = reqDetails.stream().map(MaterialLifecycleDetailSaveReqVO::getBatchId).toList();
        List<MaterialBatchDO> batches = materialBatchService.getBatchListByBatchIds(batIds);
        if (CollUtil.isEmpty(batches) || batches.size() != batIds.size())
            throw new ServiceException(1_032_160_000, "批次不存在或与传入的批次数量不匹配");
        for (MaterialBatchDO batch : batches) {
            if (batch.getSubmitStatus() != 1)
                throw new ServiceException(1_032_160_000, String.format("批次【%s】未提交，不可检定", batch.getBatchNo()));
        }
        // 检定数量校验
        Map<Long, MaterialBatchDO> batchDOMapById = batches.stream().collect(Collectors.toMap(MaterialBatchDO::getId, Function.identity()));
        for (MaterialLifecycleDetailSaveReqVO reqDetail : reqDetails) {
            MaterialBatchDO batchDO = batchDOMapById.get(reqDetail.getBatchId());
            BigDecimal remaineQuantity = batchDO.getRemaineQuantity();
            if (reqDetail.getInfluenceCount().compareTo(BigDecimal.ZERO) <= 0)
                throw new ServiceException(1_032_160_000, "检定数量不能小于等于0");
            if (reqDetail.getInfluenceCount().compareTo(remaineQuantity) > 0)
                // 可用数量
                throw new ServiceException(1_032_160_000, String.format("检定数量超过了批次【%s】可用数量【%s】", batchDO.getBatchNo(), remaineQuantity));
            if (reqDetail.getQualifiedCount().compareTo(reqDetail.getInfluenceCount()) > 0)
                throw new ServiceException(1_032_160_000, "合格数量超过了检定数量");
        }

        return reqDetails.stream().map(detail -> {
            MaterialLifecycleDetailDO detailDO = new MaterialLifecycleDetailDO();
            detailDO.setLifecycleId(mtrlLfc.getId())
                    .setBatchId(detail.getBatchId())
                    .setInfluenceCount(detail.getInfluenceCount())
                    .setQualifiedCount(detail.getQualifiedCount())
                    .setUnqualifiedCount(detail.getInfluenceCount().subtract(detail.getQualifiedCount()))
                    .setBusinessType(mtrlLfc.getBusinessType())
                    .setTreatmentStatus(0)
                    .setRemark(detail.getRemark());
            return detailDO;
        }).toList();
    }

    /**
     * 校验物料退换货
     *
     */
    private void validInfomationReturnExchangeMaterial(List<MaterialLifecycleDetailSaveReqVO> reqInfomations) {
        List<Long> infomationIds = reqInfomations.stream().map(MaterialLifecycleDetailSaveReqVO::getInfomationId).toList();
        List<MaterialInfomationDO> infomations = materialInfomationService.getListByIds(infomationIds);
        if (CollUtil.isEmpty(infomations) || infomations.size() != infomationIds.size())
            throw new ServiceException(1_032_160_000, "物料信息不存在或与传入的物料信息数量不匹配");
        // 已出库的物料不可退换货
        for (MaterialInfomationDO infomation : infomations) {
            if (infomation.getUsageStatus() == 1)
                throw new ServiceException(1_032_160_000, String.format("物料【%s】已出库，不可退换货", infomation.getCode()));
        }
    }

    /**
     *   校验工段退换货
     *
     */
    private void validGongReturnExchangeMaterial(List<MaterialLifecycleDetailSaveReqVO> reqGongs) {
        List<Long> gongIds = reqGongs.stream().map(MaterialLifecycleDetailSaveReqVO::getBatchGongduanId).toList();
        List<MaterialBatchDO> gongs = materialBatchService.getGongduanListByGongIds(gongIds);
        if (CollUtil.isEmpty(gongs) || gongs.size() != gongIds.size())
            throw new ServiceException(1_032_160_000, "工段不存在或与传入的工段数量不匹配");

        // 未提交的工段不可退换货
        for (MaterialBatchDO gong : gongs) {
            if (gong.getSubmitStatus() != 1)
                throw new ServiceException(1_032_160_000, String.format("工段【%s-%s】未提交，不可退换货", gong.getBatchNo(), gong.getAssignDepartmentName()));
        }

        // 退换货量不大于剩余量
        Map<Long, MaterialBatchDO> gongsMapById = gongs.stream().collect(Collectors.toMap(MaterialBatchDO::getId, Function.identity()));
        for (MaterialLifecycleDetailSaveReqVO reqGong : reqGongs) {
            MaterialBatchDO gong = gongsMapById.get(reqGong.getBatchGongduanId());
            BigDecimal remaine = gong.getRemaineQuantity();
            if (reqGong.getInfluenceCount().compareTo(remaine) > 0)
                throw new ServiceException(1_032_160_000, String.format("退换货数量超过了批次工段【%s-%s】可用数量",
                    gongsMapById.get(reqGong.getBatchGongduanId()).getBatchNo(),
                        gongsMapById.get(reqGong.getBatchGongduanId()).getAssignDepartmentName()));
        }
    }

    /**
     * 校验批次退换货
     *
     */
    private void validBatchReturnExchangeMaterial(List<MaterialLifecycleDetailSaveReqVO> reqBatches) {
        List<Long> batchIds = reqBatches.stream().map(MaterialLifecycleDetailSaveReqVO::getBatchId).toList();
        List<MaterialBatchDO> batches = materialBatchService.getBatchListByBatchIds(batchIds);
        if (CollUtil.isEmpty(batches) || batches.size() != batchIds.size())
            throw new ServiceException(1_032_160_000, "批次不存在或与传入的批次数量不匹配");
        // 已提交的批次不可退换货
        for (MaterialBatchDO batch : batches) {
            if (batch.getSubmitStatus() == 1)
                throw new ServiceException(1_032_160_000, String.format("批次【%s】已提交，不可退换货", batch.getBatchNo()));
        }
        Map<Long, MaterialBatchDO> batchesMapById = batches.stream().collect(Collectors.toMap(MaterialBatchDO::getId, Function.identity()));
        for (MaterialLifecycleDetailSaveReqVO reqBatch : reqBatches) {
            BigDecimal remaineQuantity = batchesMapById.get(reqBatch.getBatchId()).getRemaineQuantity();
            if (reqBatch.getInfluenceCount().compareTo(remaineQuantity) > 0)
                throw new ServiceException(1_032_160_000, String.format("退换货数量超过了批次【%s】可用数量【%s】", batchesMapById.get(reqBatch.getBatchId()).getBatchNo(), remaineQuantity));
        }
    }

    private List<MaterialLifecycleDetailDO> getLifecycleDetailDOSByReturnExchangeMaterial(MaterialLifecycleDO mtrlLfc, List<MaterialLifecycleDetailSaveReqVO> detailList) {
        return detailList.stream().map(detail -> {
            MaterialLifecycleDetailDO detailDO = new MaterialLifecycleDetailDO();
            detailDO.setLifecycleId(mtrlLfc.getId())
                    .setBatchId(detail.getBatchId())
                    .setBatchGongduanId(detail.getBatchGongduanId())
                    .setInfomationId(detail.getInfomationId())
                    .setInfluenceCount(detail.getInfluenceCount())
                    .setBusinessType(mtrlLfc.getBusinessType())
                    .setTreatmentStatus(0)
                    .setRemark(detail.getRemark());
            return detailDO;
        }).toList();
    }

    @Transactional
    @Override
    public void deleteMaterialLifecycle(Long id) {
        MaterialLifecycleDO lifecycleDO = materialLifecycleMapper.selectById(id);
        if (lifecycleDO == null) throw exception(MATERIAL_LIFECYCLE_NOT_EXISTS);
        // 已提交的流程不允许删除
        if (lifecycleDO.getSubmitStatus() == 1) throw new ServiceException(1_032_160_000, "流程已提交，不可删除");
        MaterialFlowType flowType = MaterialFlowType.fromName(lifecycleDO.getBusinessType());
        boolean isReturnExchangeMaterial = MaterialFlowType.return_material == flowType
                || MaterialFlowType.replace_material == flowType;
        boolean isVerifyCalibrate = MaterialFlowType.verify_calibrate == flowType;

        if (isReturnExchangeMaterial || isVerifyCalibrate) {
            List<MaterialLifecycleDetailDO> detailList = materialLifecycleDetailService.getDetailListByLfcId(id);
            if (isReturnExchangeMaterial) this.lockReturnExchangeResources(detailList, LockType.unlock);
            if (isVerifyCalibrate) this.lockVerifyCalibrateResources(detailList, LockType.unlock);
        }
        if (flowType == MaterialFlowType.inventory_check)
            materialInventoryCheckItemService.deleteItemsByLfcId(lifecycleDO.getId());
        // 删除明细
        materialLifecycleDetailService.deleteLifecycleDetailListByLfcId(id);
        // 删除流程
        materialLifecycleMapper.deleteById(id);
    }

    @Transactional
    @Override
        public void deleteMaterialLifecycleListByIds(List<Long> ids) {
        List<MaterialLifecycleDO> list = materialLifecycleMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(MATERIAL_LIFECYCLE_NOT_EXISTS);
        }
        // 存在已提交的流程不允许删除
        for (MaterialLifecycleDO lifecycle : list) {
            if (lifecycle.getSubmitStatus() == 1)
                throw new ServiceException(1_032_160_000, "存在已提交的流程，不可删除");
        }
        // 释放退换货锁定资源
        List<MaterialLifecycleDO> rtnRepLifecycleDOS = list.stream().filter(lifecycleDO ->
                MaterialFlowType.return_material.getName().equals(lifecycleDO.getBusinessType())
                        || MaterialFlowType.replace_material.getName().equals(lifecycleDO.getBusinessType())).toList();
        if (CollUtil.isNotEmpty(rtnRepLifecycleDOS)) {
            List<Long> rtnRepIds = rtnRepLifecycleDOS.stream().map(MaterialLifecycleDO::getId).toList();
            List<MaterialLifecycleDetailDO> detailList = materialLifecycleDetailService.getDetailListByLfcIds(rtnRepIds);
            this.lockReturnExchangeResources(detailList, LockType.unlock);
        }
        // 释放检定锁定资源
        List<MaterialLifecycleDO> verifyCalibrateLifecycleDOS = list.stream().filter(lifecycleDO ->
                MaterialFlowType.verify_calibrate.getName().equals(lifecycleDO.getBusinessType())).toList();
        if (CollUtil.isNotEmpty(verifyCalibrateLifecycleDOS)) {
            List<Long> verifyCalibrateIds = verifyCalibrateLifecycleDOS.stream().map(MaterialLifecycleDO::getId).toList();
            List<MaterialLifecycleDetailDO> detailList = materialLifecycleDetailService.getDetailListByLfcIds(verifyCalibrateIds);
            this.lockVerifyCalibrateResources(detailList, LockType.unlock);
        }
        // 删除明细
        materialLifecycleDetailService.deleteLifecycleDetailListByLfcIds(ids);
        // 删除流程
        materialLifecycleMapper.deleteByIds(ids);
        }

    private void validateMaterialLifecycleExists(List<Long> ids) {
        List<MaterialLifecycleDO> list = materialLifecycleMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(MATERIAL_LIFECYCLE_NOT_EXISTS);
        }
    }

    private void validateMaterialLifecycleExists(Long id) {
        if (materialLifecycleMapper.selectById(id) == null) {
            throw exception(MATERIAL_LIFECYCLE_NOT_EXISTS);
        }
    }

    @Override
    public MaterialLifecycleRespVO getMaterialLifecycleWithDetailList(Long id) {
        MaterialLifecycleDO lifecycleDO = materialLifecycleMapper.selectById(id);
        if (lifecycleDO == null) return null;
        MaterialLifecycleRespVO respVO = BeanUtils.toBean(lifecycleDO, MaterialLifecycleRespVO.class);
        // 获取明细
        List<MaterialLifecycleDetailRespVO> detailRespVOS = materialLifecycleDetailService.getDetailListWithPdtInfoByLfcId(lifecycleDO);
        respVO.setDetailList(detailRespVOS);
        CommonResult<List<BusinessFileWithUrlRespDTO>> result = businessFileService.getBusinessFileList(id);
        respVO.setBusinessFileRet(result);
        MaterialFlowType flowType = MaterialFlowType.fromName(lifecycleDO.getBusinessType());
        if (flowType == MaterialFlowType.inventory_check) {
            cn.hutool.json.JSONObject customConfig = JSONUtil.parseObj(lifecycleDO.getCustomConfig());
            cn.hutool.json.JSONArray departmentIds = customConfig.getJSONArray("departmentIds");
            cn.hutool.json.JSONArray warehouseIds = customConfig.getJSONArray("warehouseIds");
            if (CollUtil.isNotEmpty(departmentIds))
                respVO.setDepartmentIds(departmentIds.toList(Long.class));
            if (CollUtil.isNotEmpty(warehouseIds))
                respVO.setWarehouseIds(warehouseIds.toList(Long.class));
            List<MaterialInventoryCheckItemRespVO> checkItems = materialInventoryCheckItemService.getListWithPdtInfoByLfcId(lifecycleDO.getId());
            if (CollUtil.isEmpty(checkItems)) return respVO;
            if (lifecycleDO.getSubmitStatus() == 1) {
                respVO.setCheckItems(checkItems);
                return respVO;
            }
            List<Long> pdtIds = checkItems.stream().map(MaterialInventoryCheckItemRespVO::getProductId).toList();
            Map<Long, Long> stockQuantityByPdtId = materialProductService.getStockQuantityByPdtIds(pdtIds, warehouseIds.toList(Long.class));
            if (CollUtil.isEmpty(stockQuantityByPdtId)) return respVO;
            checkItems.forEach(item -> {
                Long qty = stockQuantityByPdtId.get(item.getProductId());
                item.setExpected(BigDecimal.valueOf(qty == null ? 0L : qty));
            });
            respVO.setCheckItems(checkItems);
        }
        return respVO;
    }

    @Override
    public PageResult<MaterialLifecycleDO> getMaterialLifecyclePage(MaterialLifecyclePageReqVO pageReqVO) {
        PageResult<MaterialLifecycleDO> pageResult = materialLifecycleMapper.selectPage(pageReqVO);
        List<MaterialLifecycleDO> lifecycleDOS = pageResult.getList();
        if (CollUtil.isEmpty(lifecycleDOS)) return pageResult;
        lifecycleDOS.forEach(mtrlLfc -> {
            String formData = mtrlLfc.getFormData();
            if (StrUtil.isNotEmpty(formData)) {
                String title = (String) JSONUtil.parseObj(formData).get("title");
                mtrlLfc.setTitle(title);
            }
        });
        pageResult.setList(lifecycleDOS);
        return pageResult;
    }

    @Transactional
    @Override
    public Boolean submitLifecycle(Long id) {
        MaterialLifecycleDO lifecycleDO = materialLifecycleMapper.selectById(id);
        if (lifecycleDO == null) throw exception(MATERIAL_LIFECYCLE_NOT_EXISTS);
        Integer submitStatus = lifecycleDO.getSubmitStatus();
        if (submitStatus == 1) throw new ServiceException(1_032_160_000, "申请已经提交过");
        String loginUserNickname = SecurityFrameworkUtils.getLoginUserNickname();
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        if (loginUser == null) throw new ServiceException(1_032_160_000, "登录用户不存在");
        lifecycleDO.setApplyUser(loginUserNickname)
                .setApplyUserId(loginUser.getId())
                .setApplyDepartment(loginUser.getVisitDeptName())
                .setApplyDepartmentId(loginUser.getVisitDeptId())
                .setApplyTime(LocalDateTime.now());
        MaterialFlowType flowType = MaterialFlowType.fromName(lifecycleDO.getBusinessType());
        switch (flowType) {
            case acceptance ->
                // 验收需要发起流程
                this.createProcessInstance(lifecycleDO);
            case return_material, replace_material -> {
                // 退换货需要释放锁定的资源并更新退换货数量或更新实例状态
                List<MaterialLifecycleDetailDO> detailList = materialLifecycleDetailService.getDetailListByLfcId(id);
                this.lockReturnExchangeResources(detailList, LockType.unlock);
                this.updateReturnExchangeResources(detailList, AdjustType.add);
                // 添加退换货出库记录
                addOutboundByLfc(lifecycleDO);
            }
            case verify_calibrate -> {
                // 检定需要发起检定流程
                this.createProcessInstance(lifecycleDO);
                // 释放锁定的资源
                List<MaterialLifecycleDetailDO> detailList = materialLifecycleDetailService.getDetailListByLfcId(id);
                this.lockVerifyCalibrateResources(detailList, LockType.unlock);
                // 更新批次相关数量
                this.updateVerifyCalibrateResources(detailList, AdjustType.add);
            }
            case inventory_check -> {
                // 盘点项不存在时不允许提交
                MaterialLifecycleRespVO lfcWithDtl = this.getMaterialLifecycleWithDetailList(id);
                List<MaterialInventoryCheckItemRespVO> checkItems = lfcWithDtl.getCheckItems();
                if (CollUtil.isEmpty(checkItems)) throw new ServiceException(1_032_160_000, "盘点项不存在，不可提交");
                // 更新盘点项库存数量
                List<MaterialInventoryCheckItemDO> checkItemDOS = BeanUtils.toBean(checkItems, MaterialInventoryCheckItemDO.class);
                List<Long> pdtIds = checkItemDOS.stream().map(MaterialInventoryCheckItemDO::getProductId).toList();
                cn.hutool.json.JSONObject customConfig = JSONUtil.parseObj(lifecycleDO.getCustomConfig());
                cn.hutool.json.JSONArray warehouseIds = customConfig.getJSONArray("warehouseIds");
                Map<Long, Long> stockQuantityByPdtId = materialProductService.getStockQuantityByPdtIds(pdtIds, warehouseIds.toList(Long.class));
                checkItemDOS.forEach(item -> {
                    Long qty = stockQuantityByPdtId.get(item.getProductId());
                    item.setExpected(BigDecimal.valueOf(qty == null ? 0L : qty));
                });
                materialInventoryCheckItemService.updateBatch(checkItemDOS);
                lifecycleDO.setProgress(QmsCommonConstant.IN_PROGRESS);
                // 查询出符合条件的物料明细
                List<MaterialInfomationDO> infs = materialInfomationService.getCheckInfsByCheckLfc(lfcWithDtl);
                // 生成盘点明细
                if (CollUtil.isNotEmpty(infs)) {
                    Map<Long, MaterialInventoryCheckItemDO> checkItemMapByPdtId = checkItemDOS.stream().collect(Collectors.toMap(MaterialInventoryCheckItemDO::getProductId, Function.identity()));
                    List<MaterialInventoryCheckDetailDO> checkDetailDOS = infs.stream().map(inf -> {
                        MaterialInventoryCheckDetailDO detail = new MaterialInventoryCheckDetailDO();
                        detail.setCheckId(id);
                        detail.setItemId(checkItemMapByPdtId.get(inf.getProductId()).getId());
                        detail.setInfomationId(inf.getId());
                        detail.setPresent(0);
                        return detail;
                    }).toList();
                    materialInventoryCheckDetailService.addBatch(checkDetailDOS);
                }

            }
        }

        lifecycleDO.setSubmitStatus(1);
        materialLifecycleMapper.updateById(lifecycleDO);
        return true;
    }

    private void addOutboundByLfc(MaterialLifecycleDO lifecycleDO) {
        List<MaterialLifecycleDetailDO> detailList = materialLifecycleDetailService.getDetailListByLfcId(lifecycleDO.getId());
        if (CollUtil.isEmpty(detailList)) return;
        Set<Long> infIds = detailList.stream().map(MaterialLifecycleDetailDO::getInfomationId).collect(Collectors.toSet());
        if (CollUtil.isEmpty(infIds)) return;
        MaterialFlowType flowType = MaterialFlowType.fromName(lifecycleDO.getBusinessType());
        String outboundType = switch (flowType) {
            case return_material -> MaterialOutboundType.return_outbound.getName();
            case replace_material -> MaterialOutboundType.replace_outbound.getName();
            case acceptance, make_apply, verify_calibrate, inventory_check -> null;
        };
        MaterialInventoryOutboundSaveReqVO outboundSaveReqVO = new MaterialInventoryOutboundSaveReqVO()
                .setTitle("退换货出库")
                .setBusinessType(outboundType)
                .setApplyUserId(lifecycleDO.getApplyUserId())
                .setInfomationIds(new ArrayList<>(infIds))
                .setRemark("退换货出库");
        materialInventoryOutboundService.createMaterialInventoryOutbound(outboundSaveReqVO);
    }

    private void updateVerifyCalibrateResources(List<MaterialLifecycleDetailDO> detailList, AdjustType adjustType) {
        // 批次
        List<MaterialLifecycleDetailDO> batches = detailList.stream().filter(detail -> detail.getBatchId() != null).toList();

        if (CollUtil.isNotEmpty(batches)) {
            materialBatchService.updateBatchVerifyCalibrateCount(batches, adjustType);
        }
    }

    @Override
    public PageResult<MaterialLifecycleRespVO> getMaterialLifecycleRespVOPage(MaterialLifecyclePageReqVO pageReqVO) {
        PageResult<MaterialLifecycleDO> lifecyclePage = getMaterialLifecyclePage(pageReqVO);
        List<MaterialLifecycleDO> lifecycleDOS = lifecyclePage.getList();
        PageResult<MaterialLifecycleRespVO> respVOPageResult = BeanUtils.toBean(lifecyclePage, MaterialLifecycleRespVO.class);
        if (CollUtil.isEmpty(lifecycleDOS)) return respVOPageResult;
        if (MaterialFlowType.make_apply.getName().equals(pageReqVO.getBusinessType())) {
            List<MaterialLifecycleRespVO> lifecycleRespVOS = respVOPageResult.getList();
            // 组装明细
            List<Long> lfcIds = lifecycleRespVOS.stream().map(MaterialLifecycleRespVO::getId).toList();
            List<MaterialLifecycleDetailRespVO> detailRespVOS = materialLifecycleDetailService.getDetailRespVOListByLfcIds(lfcIds);
            if (CollUtil.isNotEmpty(detailRespVOS)) {

                // 获取已配置的数量
                List<Long> detailIds = detailRespVOS.stream().map(MaterialLifecycleDetailRespVO::getId).toList();
                Map<Long, Long> madeCountMap = materialLifecycleDetailService.getMadeCountMapByDetailIds(detailIds);
                for (MaterialLifecycleDetailRespVO detailRespVO : detailRespVOS) {
                    Long finishedCount = madeCountMap.get(detailRespVO.getId());
                    detailRespVO.setFinishedCount(finishedCount == null ? 0L : finishedCount);
                }
                Map<Long, List<MaterialLifecycleDetailRespVO>> detailMapByLfcId = detailRespVOS.stream().collect(Collectors.groupingBy(MaterialLifecycleDetailRespVO::getLifecycleId));
                for (MaterialLifecycleRespVO lifecycleRespVO : lifecycleRespVOS) {
                    lifecycleRespVO.setDetailList(detailMapByLfcId.get(lifecycleRespVO.getId()));
                }

            }
            respVOPageResult.setList(lifecycleRespVOS);
        }
        return respVOPageResult;
    }

    @Override
    public MaterialLifecycleDO getMaterialLifecycle(Long id) {
        return materialLifecycleMapper.selectById(id);
    }

    @Override
    public List<MaterialLifecycleDetailDO> getDetailListByLfcId(Long lfcId) {
        return materialLifecycleDetailService.getDetailListByLfcId(lfcId);
    }

    @Transactional
    @Override
    public Boolean operatorSubmitLifecycle(Long id) {
        MaterialLifecycleDO lifecycleDO = materialLifecycleMapper.selectById(id);
        if (lifecycleDO == null) throw exception(MATERIAL_LIFECYCLE_NOT_EXISTS);
        String progress = lifecycleDO.getProgress();
        if (QmsCommonConstant.COMPLETED.equals(progress)) throw new ServiceException(1_032_160_000, "申请已经提交过");
        MaterialFlowType flowType = MaterialFlowType.fromName(lifecycleDO.getBusinessType());
        if (flowType == MaterialFlowType.inventory_check) {
            // 发起流程
            this.createProcessInstance(lifecycleDO);
            // 更新进度
            lifecycleDO.setProgress(QmsCommonConstant.COMPLETED);
            // 处理盘点结果
            List<MaterialInventoryCheckItemDO> items = materialInventoryCheckItemService.getListByLfcId(id);
            if (CollUtil.isEmpty(items)) throw new ServiceException(1_032_160_000, "盘点项不存在，请检查");
            items.forEach(item -> {
                if (item.getActual().compareTo(item.getExpected()) > 0) {
                    item.setDifference("盘盈");
                } else if (item.getActual().compareTo(item.getExpected()) == 0) {
                    item.setDifference("盘平");
                } else if (item.getActual().compareTo(item.getExpected()) < 0) {
                    item.setDifference("盘亏");
                }
            });
            materialInventoryCheckItemService.updateBatch(items);
        }
        materialLifecycleMapper.updateById(lifecycleDO);
        return true;
    }

    @Override
    public MaterialInventoryCheckDetailRespVO inventoryCheckInfomation(MaterialInventoryCheckDetailSaveReqVO inventoryReqVO) {
        Long infomationId = inventoryReqVO.getInfomationId();
        MaterialInfomationDO infomationDO = materialInfomationService.getMaterialInfomation(infomationId);
        if (infomationDO == null) throw exception(MATERIAL_INFOMATION_NOT_EXISTS);
        Long checkId = inventoryReqVO.getCheckId();
        MaterialLifecycleDO lifecycleDO = this.getMaterialLifecycle(checkId);
        if (lifecycleDO == null) throw exception(MATERIAL_LIFECYCLE_NOT_EXISTS);
        if (lifecycleDO.getSubmitStatus() != 1) throw new ServiceException(1_032_160_000, "盘点单未提交");

        LocalDateTime checkTime = lifecycleDO.getApplyTime();
        if (infomationDO.getCreateTime().isAfter(checkTime)) throw new ServiceException(1_032_160_000, "此物料在发起盘点后入库，不可盘点");

        // 检查是否已经盘点过
        MaterialInventoryCheckDetailDO checkDetailDO = materialInventoryCheckDetailService.getByCheckIdAndInfId(checkId, infomationId);
        MaterialInventoryCheckItemDO item = materialInventoryCheckItemService.getItemByCheckIdAndPdtId(checkId, infomationDO.getProductId());
        if (checkDetailDO != null) {
            if (checkDetailDO.getPresent() == 1) throw new ServiceException(1_032_160_000, "此物料已盘点过");
        } else {
            // 检查是否未领用了没拿走的情况
            if (infomationDO.getUsageStatus() != 1) throw new ServiceException(1_032_160_000, "此盘点单不可盘点该物料");
            // 根据仓库和盘点项匹配物料
            // 1. 匹配上 新建盘点明细（领用了没拿走）
            // 2. 匹配不上 新建盘点项和盘点明细（领用了没拿走且大类不在盘点项中）
            cn.hutool.json.JSONObject cstCfgObj = JSONUtil.parseObj(lifecycleDO.getCustomConfig());
            cn.hutool.json.JSONArray jsonWarehouseIds = cstCfgObj.getJSONArray("warehouseIds");
            List<Long> warehouseIds = CollUtil.isEmpty(jsonWarehouseIds) ? List.of() : jsonWarehouseIds.toList(Long.class);
            if (CollUtil.isEmpty(warehouseIds)) throw new ServiceException(1_032_160_000, "此盘点单没有配置盘点仓库");
            ConfigWarehouseLocationDO location = configWarehouseLocationService.getConfigWarehouseLocation(infomationDO.getLocationId());
            if (location == null) throw exception(CONFIG_WAREHOUSE_LOCATION_NOT_EXISTS);
            // 仓库匹配不成功，
            if (!warehouseIds.contains(location.getParentId())) throw new ServiceException(1_032_160_000, "该物料所在的仓库不在此盘点单中");
            if (item == null) {
                // 盘点项匹配不成功，新建盘点项
                item = new MaterialInventoryCheckItemDO()
                        .setParentId(checkId).setProductId(infomationDO.getProductId())
                        .setExpected(BigDecimal.valueOf(0))
                        .setRemark("盘点过程中新增的盘点项");
                materialInventoryCheckItemService.save(item);
            }
            checkDetailDO = new MaterialInventoryCheckDetailDO()
                    .setCheckId(checkId).setItemId(item.getId()).setInfomationId(infomationId)
                    .setCurrentQuantity(inventoryReqVO.getCurrentQuantity())
                    // 不在库
                    .setAtWarehouse(0)
                    .setRemark(StrUtil.isEmpty(inventoryReqVO.getRemark()) ? "已领用未拿走" : inventoryReqVO.getRemark());
            materialInventoryCheckDetailService.save(checkDetailDO);
        }
        checkDetailDO.setPresent(1);
        materialInventoryCheckDetailService.updateById(checkDetailDO);
        item.setActual(item.getActual().add(BigDecimal.valueOf(1)));
        materialInventoryCheckItemService.updateById(item);
        return BeanUtils.toBean(checkDetailDO, MaterialInventoryCheckDetailRespVO.class);
    }

    private void createProcessInstance(MaterialLifecycleDO lifecycleDO) {

        String flowInstanceId = lifecycleDO.getFlowInstanceId();
        // 流程已经发起过
        if (StrUtil.isNotEmpty(flowInstanceId)) {
            CommonResult<List<BpmTaskRespDTO>> taskRet = bpmTaskApi.getTaskListByProcessInstanceId(flowInstanceId);
            List<BpmTaskRespDTO> taskList = taskRet.getData();
            if(CollUtil.isEmpty(taskList))
                throw exception0(ERROR_CODE_MODULE_COMMON, "流程任务查询失败，请联系管理员处理");
            String taskId = taskList.get(taskList.size() - 1).getId();
            //驳回后重新提交
            BpmTaskApproveReqDTO reqVO = new BpmTaskApproveReqDTO();
            reqVO.setId(taskId);
            CommonResult<Boolean> result = bpmProcessInstanceApi.approveTask(reqVO);
            if(!result.isSuccess()){
                throw exception0(ERROR_CODE_MODULE_COMMON, result.getMsg());
            }
            lifecycleDO.setFlowStatus(QmsCommonConstant.IN_PROGRESS);
            return;
        }
        // 发起流程
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        if (loginUser == null) return;
        String loginUserNickname = SecurityFrameworkUtils.getLoginUserNickname();
        Long loginUserId = loginUser.getId();
        Map<String, Object> variables = new HashMap<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        variables.put("mainId",  lifecycleDO.getId());
        variables.put("applyUser",  loginUserNickname);
        variables.put("applyUserId", loginUserId);
        variables.put("applyDepartment",  loginUser.getVisitDeptName());
        variables.put("applyDepartmentId",  loginUser.getVisitDeptId());
        variables.put("applyTime",  sdf.format(new Date()));
        variables.put(BPM_CALLBACK_BEAN_NAME, "materialLifecycleService");
        variables.put("assayAssignee", "2038786007172124673");
        List<MaterialLifecycleDetailDO> detailList = materialLifecycleDetailService.getDetailListByLfcId(lifecycleDO.getId());
        for (MaterialLifecycleDetailDO detailDO : detailList) {
            if (detailDO.getAssayFlag() == 1) {
                variables.put("assayFlag", 1);
                break;
            }
        }
        BpmProcessInstanceCreateReqDTO reqDTO = new BpmProcessInstanceCreateReqDTO();
        reqDTO.setBusinessKey(String.valueOf(lifecycleDO.getId()))
                                .setVariables(variables);
        // 流程key 根据业务类型动态获取
        List<DictionaryBusinessDO> items = dictionaryBusinessService.queryDictItemsByKey(MaterialConstants.DICT_MATERIAL_FLOW_TYPE);
        if (CollUtil.isEmpty(items)) throw new ServiceException(1_032_160_000, "流程配置不存在");
        Map<String, String> processKeyMap = items.stream()
                .filter(item -> StrUtil.isNotEmpty(item.getName()) && StrUtil.isNotEmpty(item.getCustomConfig()))
                .collect(Collectors.toMap(DictionaryBusinessDO::getName, DictionaryBusinessDO::getCustomConfig));
        MaterialFlowType materialFlowType = MaterialFlowType.fromName(lifecycleDO.getBusinessType());
        String flowItem = processKeyMap.get(materialFlowType.getName());
        if (flowItem == null) throw new ServiceException(1_032_160_000, String.format("【%s】流程配置不存在", materialFlowType.getName()));
        Object processKey = JSONUtil.parseObj(flowItem).get("processKey");
        if (processKey == null) throw new ServiceException(1_032_160_000, "流程配置 key 不存在");

        reqDTO.setProcessDefinitionKey(processKey.toString());
        CommonResult<String> result = bpmProcessInstanceApi.createProcessInstance(loginUserId, reqDTO);
        if(!result.isSuccess()){
            throw exception0(ERROR_CODE_MODULE_COMMON, result.getMsg());
        }
        lifecycleDO.setFlowInstanceId(result.getData());
        lifecycleDO.setFlowStatus(QmsCommonConstant.IN_PROGRESS);
        lifecycleDO.setApplyUser(loginUserNickname)
                .setApplyUserId(loginUserId)
                .setApplyDepartment(loginUser.getVisitDeptName())
                .setApplyDepartmentId(loginUser.getVisitDeptId());
    }

    /**
     * 流程回调处理
     *
     */
    @Override
    public CommonResult<JSONObject> callback(QmsBpmDTO reqDTO) {
        log.info("物料流程回调信息：{}", reqDTO.toString());

        JSONObject variables = reqDTO.getVariables();
        String processInsId = reqDTO.getProcessInstanceId();
        String currentActivityInsId = variables.getString(QmsBpmConstant.BPM_CUR_ACTIVITY_INS_ID);
        String returnFlag = variables.getString(QmsBpmConstant.BPM_REJECT_TO_FIRST_FLAG);
        // 同一个节点需要避免重复处理
        String checkKey = currentActivityInsId;
        if(StrUtil.isEmpty(currentActivityInsId))
            checkKey = processInsId + "-create";
        if("1".equals(returnFlag))
            checkKey += "-reject";
        try{
            dataKeyCheckService.create(checkKey, this.getClass().getName());
        } catch (Exception e){
            log.error("checkKey 重复：key={}", checkKey);
            return CommonResult.success(new JSONObject());
        }
        // 流程状态处理 1-提交（含退回） 3-拒绝 4-取消流程
        String PROCESS_STATUS = variables.getString(QmsBpmConstant.PROCESS_INSTANCE_VARIABLE_STATUS);
        Integer assayFlag = variables.getInteger(MaterialConstants.ASSAY_FLAG);
        Integer assayResultAllOutput = variables.getInteger(MaterialConstants.ASSAY_RESULT_ALL_OUTPUT);
        String businessKey = reqDTO.getBusinessKey();
        JSONArray fieldExtensions = new JSONArray();
        if(variables.containsKey(QmsBpmConstant.BPM_FIELD_EXTENSIONS)){
            fieldExtensions = variables.getJSONArray(QmsBpmConstant.BPM_FIELD_EXTENSIONS);
        }
        MaterialLifecycleDO entity = materialLifecycleMapper.selectById(Long.valueOf(businessKey));
        // String currentActivityId = variables.getString(QmsBpmConstant.BPM_CALLBACK_ACTIVITY_ID);
        // 检查是否最后一个节点
        boolean lastActivityFlag = false;
        boolean firstActivityFlag = false;
        // 是否为需要发起检化验流程的节点
        boolean initAssayFlow = false;
        if(!fieldExtensions.isEmpty()){
            for(int i = 0; i < fieldExtensions.size(); i++){
                JSONObject fieldExtension = fieldExtensions.getJSONObject(i);
                if(fieldExtension.getString("fieldName").equalsIgnoreCase(QmsBpmConstant.BPM_LAST_ACTIVITY_FLAG)){
                    lastActivityFlag = true;
                }
                if(fieldExtension.getString("fieldName").equalsIgnoreCase(QmsBpmConstant.BPM_FIRST_ACTIVITY_FLAG)){
                    firstActivityFlag = true;
                }
                if(fieldExtension.getString("fieldName").equalsIgnoreCase(MaterialConstants.INIT_ASSAY_FLOW)){
                    initAssayFlow = true;
                }
            }
        }
        MaterialFlowType flowType = MaterialFlowType.fromName(entity.getBusinessType());
        // 下一节点是检化验判定节点时，设置业务流程变量值
        if (MaterialFlowType.acceptance == flowType) {
            CommonResult<List<BpmTaskRespDTO>> taskListRes = bpmTaskApi.getTaskListByProcessInstanceId(processInsId);
            List<BpmTaskRespDTO> taskList = taskListRes.getData();
            if (CollUtil.isEmpty(taskList)) throw new ServiceException(1_032_160_000, "流程任务不存在");
            // List<BpmTaskRespDTO> curTasks = taskList.stream().filter(task -> task.getStatus() != null && task.getStatus() == 1).toList();
            BpmTaskRespDTO curTask = taskList.get(taskList.size() - 1);
            BpmApprovalDetailReqDTO approvalDetailReqDTO = new BpmApprovalDetailReqDTO()
                    .setProcessDefinitionId(processInsId)
                    .setProcessInstanceId(processInsId)
                    .setTaskId(curTask.getId());
            CommonResult<List<BpmApprovalDetailRespDTO.ActivityNode>> nextApprovalNodes =
                    bpmProcessInstanceApi.getNextApprovalNodes(curTask.getAssignee(), approvalDetailReqDTO);
            List<BpmApprovalDetailRespDTO.ActivityNode> nextNodes = nextApprovalNodes.getData();
            // log.info("流程【{}】下一个流程节点：{}", processInsId, nextNodes);
            // 判断下一节点是否为检化验判定节点
            CommonResult<BpmProcessInstanceBpmnModelViewRespDTO> bpmnModelView = bpmProcessInstanceApi.getProcessInstanceBpmnModelView(processInsId);
            BpmProcessInstanceBpmnModelViewRespDTO modelView = bpmnModelView.getData();
            if (modelView == null || modelView.getBpmnXml() == null)
                throw new ServiceException(1_032_160_000, "流程模型不存在");
            // 解析 BPMN XML，提取节点扩展字段
            Map<String, Map<String, String>> nodeFieldExtensions = BpmnXmlUtil.parseBpmnFieldExtensions(modelView.getBpmnXml());
            // log.info("节点扩展字段：{}", nodeFieldExtensions);
            boolean isNextNodeAssayAssessment = false;
            if (CollUtil.isNotEmpty(nextNodes)) {
                for (BpmApprovalDetailRespDTO.ActivityNode node : nextNodes) {
                    Map<String, String> nextNodeFields = nodeFieldExtensions.get(node.getId());
                    // log.info("下一节点 {} 的扩展字段：{}", node.getId(), nextNodeFields);
                    if (nextNodeFields != null && "1".equals(nextNodeFields.get(MaterialConstants.ASSAY_RESULT_ASSESSMENT_NODE_MARK))) {
                        isNextNodeAssayAssessment = true;
                        break;
                    }
                }
            }
            if(isNextNodeAssayAssessment
                    && assayFlag != null && assayFlag == 1
                    && assayResultAllOutput != null && assayResultAllOutput == 1) {
                // 当下一个节点为检化验判定节点，且当前流程为待检化验结果，则设置流程变量值为1
                entity.setAssayResultAssessment(1);
            } else {
                entity.setAssayResultAssessment(0);
            }
        }
        // 根据流程状态处理业务数据
        if(("1").equals(returnFlag)){
            // 驳回。流程需要配置退回到发起节点
            materialLifecycleDetailService.updateDetailTreatStatusByLfcId(entity.getId(), true);
            entity.setFlowStatus(QmsCommonConstant.REJECTED);
        } else if("3".equals(PROCESS_STATUS)){
            // 拒绝
            materialLifecycleDetailService.updateDetailTreatStatusByLfcId(entity.getId(), true);
            entity.setFlowStatus(QmsCommonConstant.REFUSED);
        } else if("4".equals(PROCESS_STATUS)){
            // 作废
            materialLifecycleDetailService.updateDetailTreatStatusByLfcId(entity.getId(), true);
            entity.setFlowStatus(QmsCommonConstant.VOID);
        } else if("1".equals(PROCESS_STATUS) || "2".equals(PROCESS_STATUS)){
            // 通过
            if(firstActivityFlag)
                // 驳回后重新提交
                entity.setFlowStatus(QmsCommonConstant.IN_PROGRESS);

            if(initAssayFlow && assayFlag != null && assayFlag == 1) {
                // 走到检化验节点时需要发起检化验流程
                this.initAssayByLfcId(entity.getId());
            }
            if(lastActivityFlag) {
                // 结束审批
                entity.setFlowStatus(QmsCommonConstant.COMPLETED);
                // 更新明细处理状态
                materialLifecycleDetailService.updateDetailTreatStatusByLfcId(entity.getId(), true);
                switch (flowType) {
                    case acceptance:
                        // 更新批次工段验收状态
                        this.updateMaterialBatchesAcceptStatusByLfcId(entity.getId(), MaterialAcceptStatus.accepted.name());
                        // 更新批次工段是否可入库状态
                        this.updateMaterialBatchesInboundFlagByLfcId(entity.getId());
                    case verify_calibrate:
                        // 批次检定完成后生成批次拆分
                        this.createVerifyBatchAssignsByLfcId(entity.getId());
                        break;
                    case inventory_check:
                        // 库存盘点通过，对相应物料进行出入库
                        this.inventoryCheckPassHandle(entity.getId());
                }
            }

        }
        materialLifecycleMapper.updateById(entity);
        return CommonResult.success(new JSONObject());
    }

    private void updateMaterialBatchesInboundFlagByLfcId(Long lfcId) {
        // 获取明细批次拆分
        List<MaterialLifecycleDetailDO> details = this.getDetailListByLfcId(lfcId);
        // 筛选需要检化验的批次
        List<MaterialLifecycleDetailDO> assayDetails = details.stream()
                .filter(detail -> detail.getAssayId() != null).toList();
        if (CollUtil.isEmpty(assayDetails)) return;
        List<Long> asyIds = assayDetails.stream().map(MaterialLifecycleDetailDO::getAssayId).toList();
        List<MaterialCorrelationAssayDO> assays = materialCorrelationAssayService.getListByIds(asyIds);
        if (CollUtil.isEmpty(assays)) return;
        Map<Long, MaterialCorrelationAssayDO> assayMapByGongId = assays.stream().collect(Collectors.toMap(MaterialCorrelationAssayDO::getGongduanId, Function.identity()));
        List<Long> gongIds = assays.stream().map(MaterialCorrelationAssayDO::getGongduanId)
                .filter(Objects::nonNull).toList();
        List<MaterialBatchDO> gongs = materialBatchService.getListByIds(gongIds);
        if (CollUtil.isEmpty(gongs)) return;
        gongs.forEach(gong -> {
            MaterialCorrelationAssayDO assayDO = assayMapByGongId.get(gong.getId());
            MaterialAssayResultAssessmentType assayResultAssessment = MaterialAssayResultAssessmentType.fromName(assayDO.getQualified());
            if (MaterialAssayResultAssessmentType.not_inbound == assayResultAssessment)
                gong.setInboundFlag(0);
        });
        materialBatchService.updateBatch(gongs);
    }

    private void initAssayByLfcId(Long lfcId) {
        // 获取明细批次拆分
        List<MaterialLifecycleDetailDO> details = this.getDetailListByLfcId(lfcId);
        // 筛选需要检化验的批次
        List<MaterialLifecycleDetailDO> assayDetails = details.stream()
                .filter(detail -> detail.getAssayId() != null).toList();
        if (CollUtil.isEmpty(assayDetails)) return;
        List<Long> asyIds = assayDetails.stream().map(MaterialLifecycleDetailDO::getAssayId).toList();
        List<MaterialCorrelationAssayDO> assays = materialCorrelationAssayService.getListByIds(asyIds);
        if (CollUtil.isEmpty(assays)) return;
        Map<Long, MaterialCorrelationAssayDO> assayMapById = assays.stream().collect(Collectors.toMap(MaterialCorrelationAssayDO::getId, Function.identity()));
        SampleEntrustParam entrustParam = new SampleEntrustParam()
                .setCreateWay(QmsCommonConstant.INTERFACE_INTERNAL).setConfigEntrustSourceKey(QmsCommonConstant.ENTRUST_SOURCE_JYWT)
                .setEntrustType(QmsCommonConstant.ENTRUST_INSPECTION);
        List<MaterialCorrelationAssayDO> needUpdateAssays = new ArrayList<>();
        for (MaterialLifecycleDetailDO detail : assayDetails) {
            MaterialCorrelationAssayDO assay = assayMapById.get(detail.getAssayId());
            if (assay == null) continue;
            cn.hutool.json.JSONArray itemArray = JSONUtil.parseArray(assay.getItem());
            if (CollUtil.isEmpty(itemArray)) continue;
            List<Long> itemIds = itemArray.toList(Long.class);
            List<SampleEntrustDetailProject> projects = itemIds.stream().map(itemId ->
                    new SampleEntrustDetailProject().setMaterialAssayStandardDetailId(itemId)
                            .setIsEnabled(1)).toList();
            SampleEntrustDetail sampleEntrustDetail = new SampleEntrustDetail();
            sampleEntrustDetail
                    .setBaseSampleId(assay.getSampleId())
                    .setEntrustSampleCode(assay.getSampleCode())
                    .setSampleCode(assay.getSampleCode())
                    .setEntrustSampleName(assay.getSampleName())
                    .setIsWeighing(0)
                    .setSampleEntrustDetailProjectList(projects);
            entrustParam.setSampleEntrustDetailList(List.of(sampleEntrustDetail));
            LiteflowResponse assayRes = sampleEntrustService.create(entrustParam);
            if (!assayRes.isSuccess()) throw new ServiceException(1_032_160_000, "创建检化验单失败:" + assayRes.getMessage());
            assay.setEntrustId(assayRes.getContextBean(SampleEntrustContext.class).getSampleEntrustRegistration().getId())
                    .setAssayStatus(QmsCommonConstant.IN_PROGRESS);
            needUpdateAssays.add(assay);
            /* 获取明细
            assayRes.getContextBean(SampleEntrustContext.class).getSampleEntrustDetailList().forEach(detail -> {
                detail.getBusinessSampleEntrustRegistrationId()
            });
            */
        }
        materialCorrelationAssayService.updateBatch(needUpdateAssays);
    }

    private void createVerifyBatchAssignsByLfcId(Long id) {
        MaterialLifecycleDO lifecycleDO = this.getMaterialLifecycle(id);
        if (lifecycleDO == null) return;
        List<MaterialLifecycleDetailDO> lifecycleDetailDOs = this.getDetailListByLfcId(id);
        if (CollUtil.isEmpty(lifecycleDetailDOs)) return;
        String flowInstanceId = lifecycleDO.getFlowInstanceId();
        if (StrUtil.isEmpty(flowInstanceId)) return;
        CommonResult<BpmProcessInstanceRespDTO> processInstance = bpmProcessInstanceApi.getProcessInstance(flowInstanceId);
        BpmProcessInstanceRespDTO instanceData = processInstance.getData();
        UserSimpleDTO startUser = instanceData.getStartUser();
        List<Long> batIds = lifecycleDetailDOs.stream().map(MaterialLifecycleDetailDO::getBatchId).toList();
        List<MaterialBatchDO> batchDOS = materialBatchService.getBatchListByBatchIds(batIds);
        Map<Long, MaterialBatchDO> batchDOMap = batchDOS.stream().collect(Collectors.toMap(MaterialBatchDO::getId, Function.identity()));
        List<MaterialBatchDO> gongs = lifecycleDetailDOs.stream().map(detailDO -> {
            MaterialBatchDO batchDO = batchDOMap.get(detailDO.getBatchId());
            Long deptId = startUser.getDeptId();
            return new MaterialBatchDO()
                    .setParentId(detailDO.getBatchId())
                    .setProductId(batchDO.getProductId())
                    .setAssignDepartmentId(deptId)
                    .setAssignDepartmentName(startUser.getDeptName())
                    .setInboundQuantity(detailDO.getQualifiedCount())
                    .setBatchNo(batchDO.getBatchNo())
                    .setRemaineQuantity(detailDO.getQualifiedCount())
                    .setAcceptanceStatus(MaterialAcceptStatus.accepted.name())
                    .setSubmitStatus(1).setSubmitDate(batchDO.getSubmitDate())
                    .setRemark(String.format("【%s】于%s发起检定，检定数量：%s，合格数量：%s", startUser.getNickname(), instanceData.getStartTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                            detailDO.getInfluenceCount(), detailDO.getQualifiedCount()));
        }).toList();
        materialBatchService.saveBatch(gongs);
    }

    private void updateMaterialBatchesAcceptStatusByLfcId(Long lfcId, String status) {
        List<MaterialLifecycleDetailDO> detailList = this.getDetailListByLfcId(lfcId);
        if (CollUtil.isEmpty(detailList)) return;
        List<Long> gongIds = detailList.stream().map(MaterialLifecycleDetailDO::getBatchGongduanId).toList();
        if (CollUtil.isEmpty(gongIds)) return;
        MaterialBatchDO updateEntity = new MaterialBatchDO().setAcceptanceStatus(status);
        materialBatchService.updateByIds(gongIds, updateEntity);
    }

    /**
     * 库存盘点通过处理
     *
     * @param lfcId 流程id
     */
    @Transactional
    public void inventoryCheckPassHandle(Long lfcId) {
        MaterialLifecycleDO lifecycleDO = this.getMaterialLifecycle(lfcId);
        // 不在库的入库
        this.addInboundByInventoryCheck(lifecycleDO);
        // 不存在的出库
        this.addOutboundByInventoryCheck(lifecycleDO);
    }

    private void addOutboundByInventoryCheck(MaterialLifecycleDO lifecycleDO) {
        List<MaterialInventoryCheckDetailDO> outboundDetails = materialInventoryCheckDetailService.getListNotPresentAndAtWarehouse(lifecycleDO.getId());
        if (CollUtil.isEmpty(outboundDetails)) return;
        List<Long> infIds = outboundDetails.stream().map(MaterialInventoryCheckDetailDO::getInfomationId).toList();
        MaterialInventoryOutboundSaveReqVO outboundSaveReqVO = new MaterialInventoryOutboundSaveReqVO()
                .setTitle("盘点出库")
                .setBusinessType(MaterialOutboundType.check_outbound.getName())
                .setApplyUserId(lifecycleDO.getOperatorId())
                .setInfomationIds(infIds)
                .setRemark("盘点出库，可能是没登记就拿走");
        materialInventoryOutboundService.createMaterialInventoryOutbound(outboundSaveReqVO);
    }

    private void addInboundByInventoryCheck(MaterialLifecycleDO lifecycleDO) {
        List<MaterialInventoryCheckDetailDO> checkInbDetails = materialInventoryCheckDetailService.getListNotAtWarehouse(lifecycleDO.getId());
        if (CollUtil.isEmpty(checkInbDetails)) return;
        List<Long> inbInfIds = checkInbDetails.stream().map(MaterialInventoryCheckDetailDO::getInfomationId).toList();
        List<MaterialInfomationDO> infs = materialInfomationService.getListByIds(inbInfIds);
        // 需要按库位入库
        if (CollUtil.isEmpty(infs)) return;
        CommonResult<AdminUserRespDTO> userResult = adminUserApi.getUser(lifecycleDO.getOperatorId());
        AdminUserRespDTO user = userResult.getData();
        CommonResult<DeptRespDTO> deptResult = deptApi.getDept(user.getDeptId());
        DeptRespDTO dept = deptResult.getData();
        List<MaterialInventoryInboundDO> inbounds = new ArrayList<>();
        List<MaterialInventoryInboundDetailDO> inboundDetails = new ArrayList<>();
        Map<Long, List<MaterialInfomationDO>> infsGroupByLoc = infs.stream().collect(Collectors.groupingBy(MaterialInfomationDO::getLocationId));
        infsGroupByLoc.forEach((locationId, infList) -> {
            if (CollUtil.isNotEmpty(infList)) {
                MaterialInventoryInboundDO inbound = new MaterialInventoryInboundDO();
                inbound.setTitle("盘点入库").setBusinessType(MaterialInboundType.check_inbound.getName())
                        .setApplyUser(user.getNickname()).setApplyUserId(user.getId())
                        .setApplyDepartment(dept.getName()).setApplyDepartmentId(dept.getId())
                        .setApplyTime(lifecycleDO.getApplyTime())
                        .setLocationId(locationId).setQuantity(BigDecimal.valueOf(infList.size()))
                        .setRemark("盘点后不在库的入库");
                materialInventoryInboundService.inboundInfomation(inbound, null, null, infList);
            }
        });
    }
}