2 天以前 f7273277023f3497ac071bf98f556a569cb4dd25
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/batch/MesProBatchServiceImpl.java
@@ -7,7 +7,7 @@
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.controller.admin.pro.pallet.vo.MesProPalletBindBatchReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchWorkerDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.line.MesProLineDO;
@@ -18,8 +18,8 @@
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
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;
import cn.iocoder.yudao.module.mes.service.pro.line.MesProLineService;
import cn.iocoder.yudao.module.mes.service.pro.pallet.MesProPalletService;
import cn.iocoder.yudao.module.mes.service.trace.integrity.MesTraceIntegrityService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import jakarta.annotation.Resource;
@@ -59,11 +59,12 @@
    private MesProPalletMapper palletMapper;
    @Resource
    private MesProPalletService palletService;
    @Resource
    private MesMdAutoCodeRecordService autoCodeRecordService;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private MesMdmItemService mdmItemService;
    @Resource
    private MesProLineService lineService;
    @Resource
@@ -72,27 +73,30 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createBatch(MesProBatchSaveReqVO createReqVO) {
        // 1. 校验关联数据(品种、产线、生产人员)
        MesMdmItemDO item = mdmItemService.validateItemExists(createReqVO.getItemId());
        // 1. 校验关联数据(产线、生产人员)
        MesProLineDO line = lineService.validateLineExistsAndEnable(createReqVO.getLineId());
        validateRelations(createReqVO);
        // 2. 生成批次号(编码规则:前缀 + 日期 + 产线编码 + 品种编码 + 流水号)
        String code = generateBatchCode(line.getCode(), item.getCode());
        // 2. 生成批次号(编码规则:前缀 + 日期 + 产线编码 + 流水号)
        String code = generateBatchCode(line.getCode());
        // 3. 插入批次(产线编码/名称冗余回填)
        // 3. 插入批次(产线编码/名称冗余回填;勾选绑定托盘时批次直接进入生产中)
        boolean bindPallets = CollUtil.isNotEmpty(createReqVO.getPalletIds());
        MesProBatchDO batch = BeanUtils.toBean(createReqVO, MesProBatchDO.class)
                .setCode(code)
                .setLineCode(line.getCode())
                .setLineName(line.getName())
                .setBagQuantity(0)
                .setPalletQuantity(0)
                .setStatus(MesProBatchStatusEnum.DRAFT.getStatus());
                .setStatus(bindPallets ? MesProBatchStatusEnum.PRODUCING.getStatus()
                        : MesProBatchStatusEnum.DRAFT.getStatus());
        batchMapper.insert(batch);
        // 4. 插入生产人员配置
        insertBatchWorkers(batch.getId(), createReqVO.getWorkers());
        // 5. 生成批次完整性摘要
        // 5. 绑定托盘(级联生成正式托盘码并录入托盘下袋码,失败整体回滚)
        bindPalletsToBatch(batch.getId(), createReqVO.getPalletIds());
        // 6. 生成批次完整性摘要
        integrityService.recordBatchIntegrity(batch.getId());
        return batch.getId();
    }
@@ -130,21 +134,24 @@
            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. 落库业务信息(保留临时批次码用于历史扫码追溯;产线编码/名称冗余回填)
        String code = generateBatchCode(line.getCode());
        // 3. 落库业务信息(保留临时批次码用于历史扫码追溯;产线编码/名称冗余回填;勾选绑定托盘时批次直接进入生产中)
        boolean bindPallets = CollUtil.isNotEmpty(fillReqVO.getPalletIds());
        MesProBatchDO updateObj = BeanUtils.toBean(fillReqVO, MesProBatchDO.class)
                .setCode(code)
                .setLineCode(line.getCode())
                .setLineName(line.getName())
                .setStatus(MesProBatchStatusEnum.DRAFT.getStatus());
                .setStatus(bindPallets ? MesProBatchStatusEnum.PRODUCING.getStatus()
                        : MesProBatchStatusEnum.DRAFT.getStatus());
        batchMapper.updateById(updateObj);
        // 4. 重建生产人员配置
        batchWorkerMapper.deleteByBatchId(fillReqVO.getId());
        insertBatchWorkers(fillReqVO.getId(), fillReqVO.getWorkers());
        // 5. 生成批次完整性摘要(补录正式信息后覆盖)
        // 5. 绑定托盘(级联生成正式托盘码并录入托盘下袋码,失败整体回滚)
        bindPalletsToBatch(fillReqVO.getId(), fillReqVO.getPalletIds());
        // 6. 生成批次完整性摘要(补录正式信息后覆盖)
        integrityService.recordBatchIntegrity(fillReqVO.getId());
    }
@@ -157,29 +164,33 @@
        // 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 (codeEmpty || itemChanged || lineChanged) {
            MesMdmItemDO item = mdmItemService.validateItemExists(updateReqVO.getItemId());
        if (codeEmpty || lineChanged) {
            // 变更产线时,目标产线必须启用;未变更则仅校验存在(避免停用产线的历史批次无法编辑其他字段)
            MesProLineDO line = (lineChanged || codeEmpty)
                    ? lineService.validateLineExistsAndEnable(updateReqVO.getLineId())
                    : lineService.validateLineExists(updateReqVO.getLineId());
            code = generateBatchCode(line.getCode(), item.getCode());
            code = generateBatchCode(line.getCode());
            updateObj.setLineCode(line.getCode()).setLineName(line.getName());
        }
        // 4. 更新批次
        // 4. 更新批次(勾选绑定托盘时批次直接进入生产中;取消勾选不回退,已绑定托盘不可解绑)
        boolean bindPallets = CollUtil.isNotEmpty(updateReqVO.getPalletIds());
        updateObj.setCode(code);
        if (bindPallets) {
            updateObj.setStatus(MesProBatchStatusEnum.PRODUCING.getStatus());
        }
        batchMapper.updateById(updateObj);
        // 5. 重建生产人员配置
        batchWorkerMapper.deleteByBatchId(updateReqVO.getId());
        insertBatchWorkers(updateReqVO.getId(), updateReqVO.getWorkers());
        // 6. 摘要覆盖范围含品种/产线/生产日期/计划数量,编辑后重新生成避免误报篡改
        // 6. 绑定新增勾选的托盘(级联生成正式托盘码并录入托盘下袋码,失败整体回滚)
        bindPalletsToBatch(updateReqVO.getId(), updateReqVO.getPalletIds());
        // 7. 摘要覆盖范围含品种/产线/生产日期/计划数量,编辑后重新生成避免误报篡改
        integrityService.recordBatchIntegrity(updateReqVO.getId());
    }
@@ -283,7 +294,7 @@
    /**
     * 校验关联数据(生产人员:责任人 + 生产人员配置)
     * <p>
     * 品种、产线的校验在创建/更新流程中内联执行(需取编码参与批次号生成)
     * 产线的校验在创建/更新流程中内联执行(需取编码参与批次号生成)
     */
    private void validateRelations(MesProBatchSaveReqVO reqVO) {
        Set<Long> userIds = new HashSet<>();
@@ -293,16 +304,29 @@
    }
    /**
     * 批次绑定托盘(可选):同事务内复用托盘绑定批次逻辑,
     * 生成正式托盘码并级联录入托盘下未绑批次的袋码,失败整体回滚
     */
    private void bindPalletsToBatch(Long batchId, List<Long> palletIds) {
        if (CollUtil.isEmpty(palletIds)) {
            return;
        }
        MesProPalletBindBatchReqVO bindReqVO = new MesProPalletBindBatchReqVO();
        bindReqVO.setBatchId(batchId);
        bindReqVO.setIds(palletIds);
        palletService.bindBatch(bindReqVO);
    }
    /**
     * 生成批次号
     * <p>
     * 编码规则:固定前缀(PC) + 日期(yyyyMMdd) + 输入字符(产线编码 + 品种编码) + 流水号(按日循环)
     * 编码规则:固定前缀(PC) + 日期(yyyyMMdd) + 输入字符(产线编码) + 流水号(按日循环)
     *
     * @param lineCode 产线编码
     * @param itemCode 品种(物料)编码
     * @return 批次号
     */
    private String generateBatchCode(String lineCode, String itemCode) {
        String inputChar = StrUtil.emptyToDefault(lineCode, "") + StrUtil.emptyToDefault(itemCode, "");
    private String generateBatchCode(String lineCode) {
        String inputChar = StrUtil.emptyToDefault(lineCode, "");
        return autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PRO_BATCH_CODE.getCode(), inputChar);
    }