11 小时以前 8655501ffb8dbdaf2508e575933063e6731be714
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/batch/MesProBatchServiceImpl.java
@@ -5,6 +5,7 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pro.batch.vo.MesProBatchPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.batch.vo.MesProBatchPreGenerateReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.batch.vo.MesProBatchSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
@@ -15,7 +16,6 @@
import cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchWorkerMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper;
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProBagTypeEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProBatchStatusEnum;
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
@@ -27,6 +27,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
@@ -68,7 +69,6 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createBatch(MesProBatchSaveReqVO createReqVO) {
        validateBagType(createReqVO.getBagType());
        // 1. 校验关联数据(品种、产线、生产人员)
        MesMdmItemDO item = mdmItemService.validateItemExists(createReqVO.getItemId());
        MesProLineDO line = lineService.validateLineExistsAndEnable(createReqVO.getLineId());
@@ -82,7 +82,6 @@
                .setCode(code)
                .setLineCode(line.getCode())
                .setLineName(line.getName())
                .setBagType(createReqVO.getBagType())
                .setBagQuantity(0)
                .setPalletQuantity(0)
                .setStatus(MesProBatchStatusEnum.DRAFT.getStatus());
@@ -95,28 +94,72 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<MesProBatchDO> preGenerateBatch(MesProBatchPreGenerateReqVO reqVO) {
        // 1. 生成临时批次码(全局 8 位序号,格式 PB-00000001)
        int startSeq = batchMapper.selectMaxTempCodeSeq() + 1;
        List<MesProBatchDO> list = new ArrayList<>(reqVO.getCount());
        for (int i = 0; i < reqVO.getCount(); i++) {
            list.add(new MesProBatchDO()
                    .setTempCode("PB-" + StrUtil.padPre(String.valueOf(startSeq + i), 8, '0'))
                    .setBagQuantity(0)
                    .setPalletQuantity(0)
                    .setStatus(MesProBatchStatusEnum.DRAFT.getStatus())
                    .setRemark(reqVO.getRemark()));
        }
        // 2. 批量插入(草稿状态,等待补录业务信息)
        for (MesProBatchDO batch : list) {
            batchMapper.insert(batch);
        }
        return list;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void fillBatchInfo(MesProBatchSaveReqVO fillReqVO) {
        // 1. 锁定批次,仅允许尚未补录正式信息的临时批次
        MesProBatchDO batch = batchMapper.selectByIdForUpdate(fillReqVO.getId());
        if (batch == null) {
            throw exception(PRO_BATCH_NOT_EXISTS);
        }
        if (StrUtil.isNotEmpty(batch.getCode())) {
            throw exception(PRO_BATCH_ALREADY_FILLED);
        }
        // 2. 校验关联数据并生成正式批次号
        MesMdmItemDO item = mdmItemService.validateItemExists(fillReqVO.getItemId());
        MesProLineDO line = lineService.validateLineExistsAndEnable(fillReqVO.getLineId());
        validateRelations(fillReqVO);
        String code = generateBatchCode(line.getCode(), item.getCode());
        // 3. 落库业务信息(保留临时批次码用于历史扫码追溯;产线编码/名称冗余回填)
        MesProBatchDO updateObj = BeanUtils.toBean(fillReqVO, MesProBatchDO.class)
                .setCode(code)
                .setLineCode(line.getCode())
                .setLineName(line.getName())
                .setStatus(MesProBatchStatusEnum.DRAFT.getStatus());
        batchMapper.updateById(updateObj);
        // 4. 重建生产人员配置
        batchWorkerMapper.deleteByBatchId(fillReqVO.getId());
        insertBatchWorkers(fillReqVO.getId(), fillReqVO.getWorkers());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateBatch(MesProBatchSaveReqVO updateReqVO) {
        // 1. 校验存在 + 状态(草稿/生产中才允许修改)
        MesProBatchDO oldBatch = validateBatchExists(updateReqVO.getId());
        validateBatchEditable(oldBatch);
        // 2. 已生成袋码或托盘码后不允许修改袋型(避免同一批次阈值变化导致归集错乱)
        if (updateReqVO.getBagType() != null && !Objects.equals(oldBatch.getBagType(), updateReqVO.getBagType())
                && (bagQuantityOrPalletCreated(oldBatch))) {
            throw exception(PRO_BATCH_BAG_TYPE_NOT_ALLOW_CHANGE);
        }
        validateBagType(updateReqVO.getBagType());
        // 2. 校验关联数据(生产人员)
        validateRelations(updateReqVO);
        // 3. 品种/产线变化时重新生成批次号(批次号包含产线编码 + 品种编码要素)
        // 3. 未生成正式批次号或品种/产线变化时重新生成批次号(批次号包含产线编码 + 品种编码要素)
        boolean codeEmpty = StrUtil.isEmpty(oldBatch.getCode());
        boolean itemChanged = !Objects.equals(oldBatch.getItemId(), updateReqVO.getItemId());
        boolean lineChanged = !Objects.equals(oldBatch.getLineId(), updateReqVO.getLineId());
        String code = oldBatch.getCode();
        MesProBatchDO updateObj = BeanUtils.toBean(updateReqVO, MesProBatchDO.class);
        if (itemChanged || lineChanged) {
        if (codeEmpty || itemChanged || lineChanged) {
            MesMdmItemDO item = mdmItemService.validateItemExists(updateReqVO.getItemId());
            // 变更产线时,目标产线必须启用;未变更则仅校验存在(避免停用产线的历史批次无法编辑其他字段)
            MesProLineDO line = lineChanged
            MesProLineDO line = (lineChanged || codeEmpty)
                    ? lineService.validateLineExistsAndEnable(updateReqVO.getLineId())
                    : lineService.validateLineExists(updateReqVO.getLineId());
            code = generateBatchCode(line.getCode(), item.getCode());
@@ -168,7 +211,10 @@
            throw exception(PRO_BATCH_STATUS_INVALID);
        }
        if (MesProBatchStatusEnum.PRODUCING.getStatus().equals(status)) {
            validateBagType(batch.getBagType());
            // 未补录业务信息的临时批次不允许进入生产
            if (StrUtil.isEmpty(batch.getCode())) {
                throw exception(PRO_BATCH_NOT_FILLED);
            }
        }
        // 3. 更新状态
        batchMapper.updateById(new MesProBatchDO().setId(id).setStatus(status));
@@ -181,7 +227,11 @@
    @Override
    public MesProBatchDO getBatchByCode(String code) {
        return batchMapper.selectByCode(code);
        MesProBatchDO batch = batchMapper.selectByCode(code);
        if (batch == null) {
            batch = batchMapper.selectByTempCode(code);
        }
        return batch;
    }
    @Override
@@ -255,23 +305,6 @@
                && !MesProBatchStatusEnum.PRODUCING.getStatus().equals(batch.getStatus())) {
            throw exception(PRO_BATCH_STATUS_NOT_DRAFT_OR_PRODUCING);
        }
    }
    /**
     * 校验包装袋型有效
     */
    private void validateBagType(Integer bagType) {
        if (bagType != null && MesProBagTypeEnum.valueOfType(bagType) == null) {
            throw exception(PRO_BATCH_BAG_TYPE_INVALID);
        }
    }
    /**
     * 判断批次是否已生成袋码或托盘码(用于禁止修改袋型)
     */
    private boolean bagQuantityOrPalletCreated(MesProBatchDO batch) {
        return (batch.getBagQuantity() != null && batch.getBagQuantity() > 0)
                || (batch.getPalletQuantity() != null && batch.getPalletQuantity() > 0);
    }
    /**