package cn.iocoder.yudao.module.mes.service.pro.pallet; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.mes.controller.admin.pro.pallet.vo.MesProPalletBatchCreateReqVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.pallet.vo.MesProPalletBindBatchReqVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.pallet.vo.MesProPalletPageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.pallet.vo.MesProPalletPreGenerateReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.pallet.MesProPalletDO; import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeMapper; import cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchMapper; import cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper; import cn.iocoder.yudao.module.mes.enums.pro.MesProBatchStatusEnum; import cn.iocoder.yudao.module.mes.enums.pro.MesProPalletStatusEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.ArrayList; import java.util.Collection; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** * MES 托盘码 Service 实现类 * * @author 超级管理员 */ @Service @Validated @Slf4j public class MesProPalletServiceImpl implements MesProPalletService { @Resource private MesProPalletMapper palletMapper; @Resource private MesProBagCodeMapper bagCodeMapper; @Resource private MesProBatchMapper batchMapper; @Override @Transactional(rollbackFor = Exception.class) public List batchCreatePallet(MesProPalletBatchCreateReqVO createReqVO) { // 1. 校验批次存在且处于生产中状态 MesProBatchDO batch = batchMapper.selectById(createReqVO.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 (createReqVO.getCount() == null || createReqVO.getCount() <= 0) { throw exception(PRO_PALLET_GENERATE_COUNT_INVALID); } // 2. 计算起始序号(包含已逻辑删除记录,保证序号不回退、码值唯一) int startNo = palletMapper.selectMaxPalletNo(batch.getId()) + 1; // 3. 批量生成托盘码:批次号 + -P + 2 位托盘序号 List pallets = new ArrayList<>(createReqVO.getCount()); for (int i = 0; i < createReqVO.getCount(); i++) { int palletNo = startNo + i; pallets.add(new MesProPalletDO() .setCode(batch.getCode() + "-P" + StrUtil.padPre(String.valueOf(palletNo), 2, '0')) .setBatchId(batch.getId()) .setBatchCode(batch.getCode()) .setPalletNo(palletNo) .setCapacity(createReqVO.getCapacity()) .setBagCount(0) .setAutoGenerated(false) .setStatus(MesProPalletStatusEnum.IN_USE.getStatus()) .setRemark(createReqVO.getRemark())); } palletMapper.insertBatch(pallets); // 4. 回写批次托盘码数量 batchMapper.updateById(new MesProBatchDO().setId(batch.getId()) .setPalletQuantity((batch.getPalletQuantity() == null ? 0 : batch.getPalletQuantity()) + createReqVO.getCount())); return pallets.stream().map(MesProPalletDO::getId).toList(); } @Override @Transactional(rollbackFor = Exception.class) public List preGeneratePallet(MesProPalletPreGenerateReqVO reqVO) { // 1. 生成临时托盘码(全局 8 位序号,格式 PL-00000001) int startSeq = palletMapper.selectMaxTempCodeSeq() + 1; List list = new ArrayList<>(reqVO.getCount()); for (int i = 0; i < reqVO.getCount(); i++) { list.add(new MesProPalletDO() .setTempCode("PL-" + StrUtil.padPre(String.valueOf(startSeq + i), 8, '0')) .setCapacity(reqVO.getCapacity()) .setBagCount(0) .setAutoGenerated(false) .setStatus(MesProPalletStatusEnum.IN_USE.getStatus()) .setRemark(reqVO.getRemark())); } // 2. 批量插入(未绑定生产批次,等待绑定) palletMapper.insertBatch(list); return list; } @Override @Transactional(rollbackFor = Exception.class) public int bindBatch(MesProPalletBindBatchReqVO bindReqVO) { // 1. 锁定目标批次并校验已补录正式信息 + 生产中 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); } // 2. 锁定托盘,校验均未绑定批次且未生成正式托盘码 List ids = bindReqVO.getIds().stream().distinct().sorted().toList(); if (ids.isEmpty()) { throw exception(PRO_PALLET_BIND_IDS_EMPTY); } List pallets = new ArrayList<>(ids.size()); for (Long id : ids) { MesProPalletDO pallet = palletMapper.selectByIdForUpdate(id); if (pallet == null) { throw exception(PRO_PALLET_NOT_EXISTS); } if (pallet.getBatchId() != null || StrUtil.isNotEmpty(pallet.getCode())) { throw exception(PRO_PALLET_ALREADY_BIND_BATCH); } pallets.add(pallet); } // 3. 分配正式托盘码:批次号 + -P + 2 位托盘序号(含逻辑删除记录参与序号,保证不回退、码值唯一) int startNo = palletMapper.selectMaxPalletNo(batch.getId()) + 1; for (int i = 0; i < pallets.size(); i++) { int palletNo = startNo + i; palletMapper.updateById(new MesProPalletDO() .setId(pallets.get(i).getId()) .setCode(batch.getCode() + "-P" + StrUtil.padPre(String.valueOf(palletNo), 2, '0')) .setBatchId(batch.getId()) .setBatchCode(batch.getCode()) .setPalletNo(palletNo)); } // 4. 回写批次托盘码数量 batchMapper.updateById(new MesProBatchDO().setId(batch.getId()) .setPalletQuantity((batch.getPalletQuantity() == null ? 0 : batch.getPalletQuantity()) + pallets.size())); return pallets.size(); } @Override @Transactional(rollbackFor = Exception.class) public void deletePallet(Long id) { // 1. 校验存在 MesProPalletDO pallet = validatePalletExists(id); // 2. 校验未绑定袋码 if (bagCodeMapper.selectCountByPalletId(id) > 0) { throw exception(PRO_PALLET_HAS_BAG_NOT_DELETE); } // 3. 删除托盘码 palletMapper.deleteById(id); // 4. 回减批次托盘码数量(临时托盘未绑定批次无需回减) MesProBatchDO batch = pallet.getBatchId() == null ? null : batchMapper.selectById(pallet.getBatchId()); if (batch != null && batch.getPalletQuantity() != null && batch.getPalletQuantity() > 0) { batchMapper.updateById(new MesProBatchDO().setId(batch.getId()) .setPalletQuantity(batch.getPalletQuantity() - 1)); } } @Override public MesProPalletDO getPallet(Long id) { return palletMapper.selectById(id); } @Override public MesProPalletDO getPalletByCode(String code) { MesProPalletDO pallet = palletMapper.selectByCode(code); if (pallet == null) { pallet = palletMapper.selectByTempCode(code); } return pallet; } @Override public MesProPalletDO validatePalletExists(Long id) { MesProPalletDO pallet = palletMapper.selectById(id); if (pallet == null) { throw exception(PRO_PALLET_NOT_EXISTS); } return pallet; } @Override public PageResult getPalletPage(MesProPalletPageReqVO pageReqVO) { return palletMapper.selectPage(pageReqVO); } @Override public List getPalletListByBatchId(Long batchId) { return palletMapper.selectListByBatchId(batchId); } @Override public List getPalletListByIds(Collection ids) { if (CollUtil.isEmpty(ids)) { return List.of(); } return palletMapper.selectBatchIds(ids); } @Override public void updatePalletBagCount(Long palletId, Integer bagCount) { palletMapper.updateById(new MesProPalletDO().setId(palletId).setBagCount(bagCount)); } @Override public void markOutbound(Long id, Long salesId) { palletMapper.markOutbound(id, salesId, java.time.LocalDateTime.now()); } @Override public void clearOutbound(Long id) { palletMapper.clearOutbound(id); } }