云
4 小时以前 e09f103259fa602382bb5ce2a03361a6397af46a
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/card/MesProCardServiceImpl.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.mes.service.pro.card;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -7,11 +8,14 @@
import cn.iocoder.yudao.module.mes.controller.admin.pro.card.vo.MesProCardPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.card.vo.MesProCardSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.card.MesProCardDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.card.MesProCardProcessDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.card.MesProCardMapper;
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.BarcodeBizTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.wm.barcode.MesWmBarcodeService;
import jakarta.annotation.Resource;
@@ -19,6 +23,12 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -44,33 +54,46 @@
    private MesMdItemService itemService;
    @Resource
    private MesWmBarcodeService barcodeService;
    @Resource
    private MesProWorkOrderProcessService workOrderProcessService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createCard(MesProCardSaveReqVO createReqVO) {
        // 1. 校验关联数据
        validateCardSaveData(createReqVO);
        MesProWorkOrderDO workOrder = validateCardSaveData(createReqVO);
        // 2. 插入
        MesProCardDO card = BeanUtils.toBean(createReqVO, MesProCardDO.class)
                .setStatus(MesProWorkOrderStatusEnum.PREPARE.getStatus());
        cardMapper.insert(card);
        // 3. 自动生成条码
        // 3. 按工单工序 + 收率生成工序记录
        generateCardProcesses(card.getId(), workOrder);
        // 4. 自动生成条码
        barcodeService.autoGenerateBarcode(BarcodeBizTypeEnum.PROCARD.getValue(),
                card.getId(), card.getCode(), card.getCode());
        return card.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCard(MesProCardSaveReqVO updateReqVO) {
        // 1.1 校验存在 + 草稿状态
        validateCardExistsAndPrepare(updateReqVO.getId());
        MesProCardDO card = validateCardExistsAndPrepareInternal(updateReqVO.getId());
        // 1.2 校验关联数据
        validateCardSaveData(updateReqVO);
        MesProWorkOrderDO workOrder = validateCardSaveData(updateReqVO);
        // 2. 更新
        MesProCardDO updateObj = BeanUtils.toBean(updateReqVO, MesProCardDO.class);
        cardMapper.updateById(updateObj);
        // 3. 更换了生产订单:原工序记录已失效,按新工单重建
        if (ObjUtil.notEqual(card.getWorkOrderId(), updateReqVO.getWorkOrderId())) {
            cardProcessService.deleteCardProcessByCardId(card.getId());
            generateCardProcesses(card.getId(), workOrder);
        }
    }
    @Override
@@ -139,10 +162,15 @@
    @Override
    public void validateCardExistsAndPrepare(Long id) {
        validateCardExistsAndPrepareInternal(id);
    }
    private MesProCardDO validateCardExistsAndPrepareInternal(Long id) {
        MesProCardDO card = validateCardExistsInternal(id);
        if (ObjUtil.notEqual(MesProWorkOrderStatusEnum.PREPARE.getStatus(), card.getStatus())) {
            throw exception(PRO_CARD_NOT_PREPARE);
        }
        return card;
    }
    private MesProCardDO validateCardExistsInternal(Long id) {
@@ -174,7 +202,7 @@
        }
    }
    private void validateCardSaveData(MesProCardSaveReqVO reqVO) {
    private MesProWorkOrderDO validateCardSaveData(MesProCardSaveReqVO reqVO) {
        // 校验编码唯一
        validateCardCodeUnique(reqVO.getId(), reqVO.getCode());
        // 校验工单存在
@@ -184,6 +212,61 @@
        if (ObjUtil.notEqual(workOrder.getProductId(), reqVO.getItemId())) {
            throw exception(PRO_WORK_ORDER_PRODUCT_MISMATCH);
        }
        return workOrder;
    }
    /**
     * 按工单工序与工序收率生成流转卡工序记录
     *
     * <p>数量口径与一键排产一致(见 MesProBatchScheduleService#calcTargetQuantities):
     * 末道工序产出 = 工单计划数量,前面各道逐级放大后向上取整;
     * 本道投入 = 上道产出(首道投入 = 首道产出 / 收率,向上取整),不合格品 = 投入 - 产出。
     *
     * <p>进入/出工序时间、工位、操作人留空,待生产实际发生时填写。
     */
    private void generateCardProcesses(Long cardId, MesProWorkOrderDO workOrder) {
        List<MesProWorkOrderProcessDO> processes = workOrderProcessService
                .getWorkOrderProcessListByWorkOrderId(workOrder.getId());
        if (CollUtil.isEmpty(processes) || workOrder.getQuantity() == null) {
            return; // 工单未维护工序或数量缺失,工序记录留待手工维护
        }
        processes = new ArrayList<>(processes);
        processes.sort(Comparator.comparing(MesProWorkOrderProcessDO::getSort,
                Comparator.nullsLast(Comparator.naturalOrder())));
        // 1. 按收率从末道往前倒推各道产出量(向上取整为整数件)
        int size = processes.size();
        BigDecimal[] outputs = new BigDecimal[size];
        BigDecimal cumulativeYield = BigDecimal.ONE;
        for (int i = size - 1; i >= 0; i--) {
            outputs[i] = workOrder.getQuantity().divide(cumulativeYield, 0, RoundingMode.CEILING);
            BigDecimal yieldRate = processes.get(i).getYieldRate();
            if (yieldRate != null && yieldRate.signum() > 0) {
                cumulativeYield = cumulativeYield.multiply(yieldRate);
            }
        }
        // 2. 逐道生成记录:投入 = 上道产出(首道 = 本道产出 / 收率,向上取整)
        List<MesProCardProcessDO> cardProcesses = new ArrayList<>(size);
        BigDecimal previousOutput = null;
        for (int i = 0; i < size; i++) {
            MesProWorkOrderProcessDO process = processes.get(i);
            BigDecimal input;
            if (previousOutput != null) {
                input = previousOutput;
            } else {
                BigDecimal firstYield = process.getYieldRate();
                input = firstYield == null || firstYield.signum() <= 0
                        ? outputs[i] : outputs[i].divide(firstYield, 0, RoundingMode.CEILING);
            }
            cardProcesses.add(MesProCardProcessDO.builder()
                    .cardId(cardId).sort(process.getSort()).processId(process.getProcessId())
                    .inputQuantity(input).outputQuantity(outputs[i])
                    .unqualifiedQuantity(BigDecimal.ZERO)
                    .build());
            previousOutput = outputs[i];
        }
        cardProcessService.createCardProcessBatch(cardProcesses);
    }
}