10 小时以前 8655501ffb8dbdaf2508e575933063e6731be714
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeServiceImpl.java
@@ -4,8 +4,10 @@
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeBindBatchReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeBindReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeGenerateReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeGenerateRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeReplaceItemRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeReplaceReqVO;
@@ -62,43 +64,54 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String generateBagCodes(MesProBagCodeGenerateReqVO generateReqVO) {
        // 1. 校验批次存在且处于生产中状态
        MesProBatchDO batch = batchMapper.selectByIdForUpdate(generateReqVO.getBatchId());
        if (batch == null) {
            throw exception(PRO_BATCH_NOT_EXISTS);
        }
        if (!MesProBatchStatusEnum.PRODUCING.getStatus().equals(batch.getStatus())) {
            throw exception(PRO_BATCH_STATUS_NOT_GENERATE_CODE);
        }
    public MesProBagCodeGenerateRespVO generateBagCodes(MesProBagCodeGenerateReqVO generateReqVO) {
        if (generateReqVO.getCount() == null || generateReqVO.getCount() <= 0) {
            throw exception(PRO_BAG_CODE_GENERATE_COUNT_INVALID);
        }
        // 2. 计算起始序号(包含已逻辑删除记录,保证序号不回退、码值唯一)
        int startNo = bagCodeMapper.selectMaxBagNo(batch.getId()) + 1;
        // 3. 批量生成袋码:批次号 + - + 4 位袋序号,同一次生成共享同一 UUID(数据包)
        // 生成阶段只创建全局唯一袋码,生产批次在后续录入
        int startNo = bagCodeMapper.selectMaxBagNo() + 1;
        String uuid = IdUtil.fastSimpleUUID();
        List<MesProBagCodeDO> bagCodes = new ArrayList<>(generateReqVO.getCount());
        for (int i = 0; i < generateReqVO.getCount(); i++) {
            int bagNo = startNo + i;
            bagCodes.add(new MesProBagCodeDO()
                    .setUuid(uuid)
                    .setCode(batch.getCode() + "-" + StrUtil.padPre(String.valueOf(bagNo), 4, '0'))
                    .setCode("BAG-" + StrUtil.padPre(String.valueOf(bagNo), 8, '0'))
                    .setBagNo(bagNo)
                    .setBatchId(batch.getId())
                    .setBatchCode(batch.getCode())
                    .setStatus(MesProBagCodeStatusEnum.GENERATED.getStatus())
                    .setRemark(generateReqVO.getRemark()));
        }
        bagCodeMapper.insertBatch(bagCodes);
        return new MesProBagCodeGenerateRespVO().setUuid(uuid);
    }
        // 4. 回写批次袋码数量
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bindBatch(MesProBagCodeBindBatchReqVO bindReqVO) {
        MesProBatchDO batch = batchMapper.selectByIdForUpdate(bindReqVO.getBatchId());
        if (batch == null) {
            throw exception(PRO_BATCH_NOT_EXISTS);
        }
        if (!MesProBatchStatusEnum.PRODUCING.getStatus().equals(batch.getStatus())) {
            throw exception(PRO_BATCH_STATUS_NOT_GENERATE_CODE);
        }
        // 临时批次(未补录正式批次号)不允许袋码录入
        if (StrUtil.isEmpty(batch.getCode())) {
            throw exception(PRO_BATCH_NOT_FILLED);
        }
        List<Long> ids = bindReqVO.getIds().stream().distinct().toList();
        List<MesProBagCodeDO> bagCodes = bagCodeMapper.selectListByIdsForUpdate(ids);
        if (bagCodes.size() != ids.size() || bagCodes.stream().anyMatch(code ->
                code.getBatchId() != null || !MesProBagCodeStatusEnum.GENERATED.getStatus().equals(code.getStatus()))) {
            throw exception(PRO_BAG_CODE_STATUS_NOT_GENERATED);
        }
        if (bagCodeMapper.bindBatchByIds(ids, batch.getId(), batch.getCode(),
                MesProBagCodeStatusEnum.GENERATED.getStatus()) != ids.size()) {
            throw exception(PRO_BAG_CODE_STATUS_NOT_GENERATED);
        }
        batchMapper.updateById(new MesProBatchDO().setId(batch.getId())
                .setBagQuantity((batch.getBagQuantity() == null ? 0 : batch.getBagQuantity())
                        + generateReqVO.getCount()));
        return uuid;
                .setBagQuantity(bagCodeMapper.selectEffectiveCountByBatchId(batch.getId())));
    }
    @Override
@@ -144,7 +157,7 @@
        String replaceUuid = IdUtil.fastSimpleUUID();
        LocalDateTime replaceTime = LocalDateTime.now();
        int startNo = bagCodeMapper.selectMaxBagNo(batch.getId()) + 1;
        int startNo = bagCodeMapper.selectMaxBagNo() + 1;
        List<MesProBagCodeDO> newBagCodes = new ArrayList<>(oldBagCodes.size());
        List<MesProBagCodeReplaceDO> replacements = new ArrayList<>(oldBagCodes.size());
        List<MesProBagCodeReplaceItemRespVO> items = new ArrayList<>(oldBagCodes.size());
@@ -242,10 +255,13 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bindPallet(MesProBagCodeBindReqVO bindReqVO) {
        // 1. 校验托盘存在
        MesProPalletDO pallet = palletService.validatePalletExists(bindReqVO.getPalletId());
        // 2. 校验袋码
        List<MesProBagCodeDO> bagCodes = validateBagCodesForBind(bindReqVO.getIds(), pallet);
        // 1. 锁定托盘,避免与自动归集并发修改袋数
        MesProPalletDO pallet = palletMapper.selectByIdForUpdate(bindReqVO.getPalletId());
        if (pallet == null) {
            throw exception(PRO_PALLET_NOT_EXISTS);
        }
        // 2. 锁定并校验袋码
        List<MesProBagCodeDO> bagCodes = validateBagCodesForBind(bindReqVO.getIds(), pallet, true);
        // 3. 校验不超过托盘可装袋数
        int currentCount = pallet.getBagCount() == null ? 0 : pallet.getBagCount();
        if (pallet.getCapacity() != null && pallet.getCapacity() > 0
@@ -260,10 +276,13 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unbindPallet(MesProBagCodeBindReqVO bindReqVO) {
        // 1. 校验托盘存在
        MesProPalletDO pallet = palletService.validatePalletExists(bindReqVO.getPalletId());
        // 2. 校验袋码均绑定在当前托盘上
        List<MesProBagCodeDO> bagCodes = bagCodeMapper.selectListByIds(bindReqVO.getIds());
        // 1. 锁定托盘,避免与自动归集并发修改袋数
        MesProPalletDO pallet = palletMapper.selectByIdForUpdate(bindReqVO.getPalletId());
        if (pallet == null) {
            throw exception(PRO_PALLET_NOT_EXISTS);
        }
        // 2. 锁定并校验袋码均绑定在当前托盘上
        List<MesProBagCodeDO> bagCodes = bagCodeMapper.selectListByIdsForUpdate(bindReqVO.getIds());
        if (bagCodes.size() != bindReqVO.getIds().size()) {
            throw exception(PRO_BAG_CODE_NOT_EXISTS);
        }
@@ -334,13 +353,23 @@
        return bagCodeMapper.selectListByBatchId(batchId);
    }
    @Override
    public void markOutbound(Long id, Long salesId) {
        bagCodeMapper.markOutbound(id, salesId, LocalDateTime.now());
    }
    @Override
    public void clearOutbound(Long id) {
        bagCodeMapper.clearOutbound(id);
    }
    // ==================== 私有方法 ====================
    /**
     * 校验待绑定的袋码:存在、与托盘同批次、未绑定过托盘、状态有效(已生成/已导出印刷)
     */
    private List<MesProBagCodeDO> validateBagCodesForBind(List<Long> ids, MesProPalletDO pallet) {
        List<MesProBagCodeDO> bagCodes = bagCodeMapper.selectListByIds(ids);
    private List<MesProBagCodeDO> validateBagCodesForBind(List<Long> ids, MesProPalletDO pallet, boolean lock) {
        List<MesProBagCodeDO> bagCodes = lock ? bagCodeMapper.selectListByIdsForUpdate(ids) : bagCodeMapper.selectListByIds(ids);
        if (bagCodes.size() != ids.size()) {
            throw exception(PRO_BAG_CODE_NOT_EXISTS);
        }