From f7273277023f3497ac071bf98f556a569cb4dd25 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期四, 17 九月 2026 16:24:10 +0800
Subject: [PATCH] feat(mes): 重构袋码生成与物料分类功能
---
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeServiceImpl.java | 211 ++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 193 insertions(+), 18 deletions(-)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeServiceImpl.java
index 1611599..c8f11c4 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeServiceImpl.java
@@ -3,15 +3,20 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi;
+import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO;
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.MesProBagCodeRawGenerateReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeReplaceItemRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeReplaceReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeReplaceRespVO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeReplaceDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
@@ -23,6 +28,7 @@
import cn.iocoder.yudao.module.mes.enums.pro.MesProBagCodeStatusEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProBatchStatusEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProPalletStatusEnum;
+import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
import cn.iocoder.yudao.module.mes.service.pro.pallet.MesProPalletService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@@ -34,8 +40,13 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -61,17 +72,92 @@
@Resource
private MesProPalletService palletService;
+ @Resource
+ private MesMdmItemService mdmItemService;
+ @Resource
+ private MdmUnitMeasureApi unitMeasureApi;
+
+ /**
+ * 璁¢噺鍗曚綅绫诲瀷銆屽惃銆嶏紙鏃犳墭鐩樻ā寮忥級
+ */
+ private static final String UNIT_TYPE_TON = "鍚�";
+ /**
+ * 璁¢噺鍗曚綅绫诲瀷銆孨鍗冨厠銆嶏細姣忔墭鐩樿 N 涓鐮�
+ */
+ private static final Pattern KG_TYPE_PATTERN = Pattern.compile("^(\\d{1,5})鍗冨厠$");
@Override
@Transactional(rollbackFor = Exception.class)
public MesProBagCodeGenerateRespVO generateBagCodes(MesProBagCodeGenerateReqVO generateReqVO) {
- if (generateReqVO.getCount() == null || generateReqVO.getCount() <= 0) {
- throw exception(PRO_BAG_CODE_GENERATE_COUNT_INVALID);
+ // 1. 鏍¢獙鍝佺骞惰鍙栦富璁¢噺鍗曚綅绫诲瀷
+ MesMdmItemDO item = mdmItemService.validateItemExists(generateReqVO.getItemId());
+ String type = getUnitType(item);
+
+ // 2. 鎸夌被鍨嬫崲绠楄鐮佹暟閲忥細N鍗冨厠 = 姣忔墭 N 琚� 脳 鎵樼洏鏁帮紱鍚� = 鐩存帴杈撳叆琚嬬爜鏁帮紙鏃犳墭鐩橈級
+ boolean ton = UNIT_TYPE_TON.equals(type);
+ int perPallet = ton ? 0 : parseKgPerPallet(type);
+ int totalCount;
+ int palletCount = 0;
+ if (ton) {
+ if (generateReqVO.getBagCount() == null || generateReqVO.getBagCount() <= 0) {
+ throw exception(PRO_BAG_CODE_GENERATE_COUNT_INVALID);
+ }
+ totalCount = generateReqVO.getBagCount();
+ } else {
+ if (generateReqVO.getPalletCount() == null || generateReqVO.getPalletCount() <= 0) {
+ throw exception(PRO_BAG_CODE_PALLET_COUNT_REQUIRED);
+ }
+ palletCount = generateReqVO.getPalletCount();
+ totalCount = perPallet * palletCount;
}
- // 鐢熸垚闃舵鍙垱寤哄叏灞�鍞竴琚嬬爜锛岀敓浜ф壒娆″湪鍚庣画褰曞叆
- int startNo = bagCodeMapper.selectMaxBagNo() + 1;
+ // 3. 鐢熸垚闃舵锛氳鐮佺洿鎺ョ粦瀹氬搧绉嶏紱鐢熶骇鎵规鍦ㄥ悗缁綍鍏�
String uuid = IdUtil.fastSimpleUUID();
+ List<MesProPalletDO> pallets = new ArrayList<>(palletCount);
+ if (!ton) {
+ int startSeq = palletMapper.selectMaxTempCodeSeq() + 1;
+ for (int i = 0; i < palletCount; i++) {
+ pallets.add(new MesProPalletDO()
+ .setTempCode("PL-" + StrUtil.padPre(String.valueOf(startSeq + i), 8, '0'))
+ .setBagCount(perPallet)
+ .setAutoGenerated(true)
+ .setStatus(MesProPalletStatusEnum.IN_USE.getStatus()));
+ }
+ palletMapper.insertBatch(pallets);
+ }
+ int startNo = bagCodeMapper.selectMaxBagNo() + 1;
+ List<MesProBagCodeDO> bagCodes = new ArrayList<>(totalCount);
+ for (int i = 0; i < totalCount; i++) {
+ int bagNo = startNo + i;
+ MesProPalletDO pallet = ton ? null : pallets.get(i / perPallet);
+ bagCodes.add(new MesProBagCodeDO()
+ .setUuid(uuid)
+ .setCode("BAG-" + StrUtil.padPre(String.valueOf(bagNo), 8, '0'))
+ .setBagNo(bagNo)
+ .setItemId(item.getId())
+ .setItemCode(item.getCode())
+ .setItemName(item.getName())
+ .setPalletId(pallet == null ? null : pallet.getId())
+ .setPalletCode(pallet == null ? null : pallet.getTempCode())
+ .setStatus(MesProBagCodeStatusEnum.GENERATED.getStatus())
+ .setRemark(generateReqVO.getRemark()));
+ }
+ bagCodeMapper.insertBatch(bagCodes);
+ return new MesProBagCodeGenerateRespVO()
+ .setUuid(uuid)
+ .setType(type)
+ .setPerPallet(perPallet)
+ .setTotalCount(totalCount)
+ .setPalletIds(pallets.stream().map(MesProPalletDO::getId).toList());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public MesProBagCodeGenerateRespVO generateRawBagCodes(MesProBagCodeRawGenerateReqVO generateReqVO) {
+ // 鍘熷琚嬬爜涓嶄緷璧栬閲忓崟浣嶇被鍨嬩笌鎵樼洏锛氫粎鎸夋暟閲忕敓鎴愶紝琚嬬爜甯﹀搧绉嶅啑浣欙紝鍚庣画鍙甯稿綍鍏ユ壒娆�/缁戝畾鎵樼洏
+ MesMdmItemDO item = mdmItemService.validateItemExists(generateReqVO.getItemId());
+ String uuid = IdUtil.fastSimpleUUID();
+ int startNo = bagCodeMapper.selectMaxBagNo() + 1;
List<MesProBagCodeDO> bagCodes = new ArrayList<>(generateReqVO.getCount());
for (int i = 0; i < generateReqVO.getCount(); i++) {
int bagNo = startNo + i;
@@ -79,11 +165,48 @@
.setUuid(uuid)
.setCode("BAG-" + StrUtil.padPre(String.valueOf(bagNo), 8, '0'))
.setBagNo(bagNo)
+ .setItemId(item.getId())
+ .setItemCode(item.getCode())
+ .setItemName(item.getName())
.setStatus(MesProBagCodeStatusEnum.GENERATED.getStatus())
.setRemark(generateReqVO.getRemark()));
}
bagCodeMapper.insertBatch(bagCodes);
- return new MesProBagCodeGenerateRespVO().setUuid(uuid);
+ return new MesProBagCodeGenerateRespVO()
+ .setUuid(uuid)
+ .setTotalCount(bagCodes.size())
+ .setPalletIds(List.of());
+ }
+
+ /**
+ * 璇诲彇鍝佺涓昏閲忓崟浣嶇殑绫诲瀷锛�48鍗冨厠/100鍗冨厠/鍚級锛屾湭閰嶇疆鎴栨棤娉曡瘑鍒椂鎶ラ敊
+ */
+ private String getUnitType(MesMdmItemDO item) {
+ String type = null;
+ if (item.getUnitMeasureId() != null) {
+ CommonResult<MdmUnitMeasureRespDTO> result = unitMeasureApi.getUnitMeasure(item.getUnitMeasureId());
+ if (result != null && result.isSuccess() && result.getData() != null) {
+ type = StrUtil.trimToNull(result.getData().getType());
+ }
+ }
+ if (type == null) {
+ throw exception(PRO_BAG_CODE_ITEM_TYPE_NOT_CONFIGURED);
+ }
+ if (!UNIT_TYPE_TON.equals(type) && !KG_TYPE_PATTERN.matcher(type).matches()) {
+ throw exception(PRO_BAG_CODE_ITEM_TYPE_NOT_CONFIGURED);
+ }
+ return type;
+ }
+
+ /**
+ * 瑙f瀽銆孨鍗冨厠銆嶇被鍨嬪緱鍑烘瘡鎵樼洏琚嬫暟
+ */
+ private int parseKgPerPallet(String type) {
+ Matcher matcher = KG_TYPE_PATTERN.matcher(type);
+ if (!matcher.matches()) {
+ throw exception(PRO_BAG_CODE_ITEM_TYPE_NOT_CONFIGURED);
+ }
+ return Integer.parseInt(matcher.group(1));
}
@Override
@@ -103,15 +226,60 @@
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()))) {
+ code.getBatchId() != null
+ || (!MesProBagCodeStatusEnum.GENERATED.getStatus().equals(code.getStatus())
+ && !MesProBagCodeStatusEnum.PRINTED.getStatus().equals(code.getStatus())))) {
throw exception(PRO_BAG_CODE_STATUS_NOT_GENERATED);
}
+ // 褰曞叆鐨勮鐮佸繀椤诲睘浜庡悓涓�鍝佺锛屼笖涓庢壒娆″凡鏈夎鐮佸搧绉嶄竴鑷达紙鍘嗗彶绌虹櫧琚嬬爜 item 涓虹┖涓嶅弬涓庢牎楠岋級
+ Set<Long> itemIds = new LinkedHashSet<>();
+ bagCodes.stream().map(MesProBagCodeDO::getItemId).filter(Objects::nonNull).forEach(itemIds::add);
+ itemIds.addAll(bagCodeMapper.selectDistinctItemIdsByBatchId(batch.getId()));
+ if (itemIds.size() > 1) {
+ throw exception(PRO_BAG_CODE_BIND_BATCH_ITEM_MISMATCH);
+ }
if (bagCodeMapper.bindBatchByIds(ids, batch.getId(), batch.getCode(),
- MesProBagCodeStatusEnum.GENERATED.getStatus()) != ids.size()) {
+ List.of(MesProBagCodeStatusEnum.GENERATED.getStatus(),
+ MesProBagCodeStatusEnum.PRINTED.getStatus())) != ids.size()) {
throw exception(PRO_BAG_CODE_STATUS_NOT_GENERATED);
}
batchMapper.updateById(new MesProBatchDO().setId(batch.getId())
.setBagQuantity(bagCodeMapper.selectEffectiveCountByBatchId(batch.getId())));
+ // 绾ц仈锛氭湰缁勮鐮佺粦瀹氱殑鏈叆鎵规鎵樼洏锛屽垎閰嶆寮忔墭鐩樼爜骞剁粦瀹氭壒娆�
+ bindPalletsToBatch(bagCodes, batch);
+ }
+
+ /**
+ * 琚嬬爜褰曞叆鎵规鏃讹紝灏嗗叾缁戝畾鐨勪复鏃舵墭鐩橈紙鏈粦瀹氭壒娆★級绾ц仈缁戝畾鍒板悓涓�鎵规锛�
+ * 鍒嗛厤姝e紡鎵樼洏鐮侊紙鎵规鍙�-P搴忓彿锛夈�佸洖鍐欒鐮佷笂鐨勫啑浣欐墭鐩樼爜銆佺疮璁℃壒娆℃墭鐩樻暟
+ */
+ private void bindPalletsToBatch(List<MesProBagCodeDO> bagCodes, MesProBatchDO batch) {
+ List<Long> palletIds = bagCodes.stream().map(MesProBagCodeDO::getPalletId)
+ .filter(Objects::nonNull).distinct().sorted().toList();
+ if (palletIds.isEmpty()) {
+ return;
+ }
+ int nextPalletNo = palletMapper.selectMaxPalletNo(batch.getId()) + 1;
+ int boundCount = 0;
+ for (Long palletId : palletIds) {
+ MesProPalletDO pallet = palletMapper.selectByIdForUpdate(palletId);
+ if (pallet == null || pallet.getBatchId() != null || StrUtil.isNotEmpty(pallet.getCode())) {
+ continue;
+ }
+ String palletCode = batch.getCode() + "-P" + StrUtil.padPre(String.valueOf(nextPalletNo++), 2, '0');
+ palletMapper.updateById(new MesProPalletDO().setId(palletId)
+ .setCode(palletCode)
+ .setBatchId(batch.getId())
+ .setBatchCode(batch.getCode())
+ .setPalletNo(nextPalletNo - 1));
+ // 琚嬬爜涓婄殑鍐椾綑鎵樼洏鐮佺敱涓存椂鐮佹洿鏂颁负姝e紡鐮�
+ bagCodeMapper.updatePalletCodeByPalletId(palletId, palletCode);
+ boundCount++;
+ }
+ if (boundCount > 0) {
+ batchMapper.updateById(new MesProBatchDO().setId(batch.getId())
+ .setPalletQuantity((batch.getPalletQuantity() == null ? 0 : batch.getPalletQuantity()) + boundCount));
+ }
}
@Override
@@ -175,6 +343,9 @@
.setBagNo(newBagNo)
.setBatchId(batch.getId())
.setBatchCode(batch.getCode())
+ .setItemId(oldBagCode.getItemId())
+ .setItemCode(oldBagCode.getItemCode())
+ .setItemName(oldBagCode.getItemName())
.setPalletId(pallet == null ? null : pallet.getId())
.setPalletCode(pallet == null ? null : pallet.getCode())
.setStatus(MesProBagCodeStatusEnum.GENERATED.getStatus())
@@ -234,17 +405,26 @@
if (CollUtil.isEmpty(bagCodes)) {
throw exception(PRO_BAG_CODE_UUID_NOT_EXISTS);
}
- // 2. 鏍¢獙鍧囦负宸茬敓鎴愮姸鎬佷笖鏈粦瀹氭墭鐩�
+ // 2. 鏍¢獙鍧囦负宸茬敓鎴愮姸鎬�
if (bagCodes.stream().anyMatch(bagCode ->
!MesProBagCodeStatusEnum.GENERATED.getStatus().equals(bagCode.getStatus()))) {
throw exception(PRO_BAG_CODE_STATUS_NOT_GENERATED);
}
- if (bagCodes.stream().anyMatch(bagCode -> bagCode.getPalletId() != null)) {
- throw exception(PRO_BAG_CODE_BIND_PALLET_BOUND);
+ // 3. 缁戝畾鐨勬墭鐩樹粎闄�"鑷姩鐢熸垚涓旀湭缁戝畾鎵规"锛堢敓鎴愭暟鎹寘鏃惰嚜鍔ㄧ粦瀹氾級锛岀骇鑱斿垹闄わ紱宸插叆鎵规鐨勬墭鐩樹笉鍏佽闅忓寘鍒犻櫎
+ Set<Long> palletIds = new LinkedHashSet<>();
+ bagCodes.stream().map(MesProBagCodeDO::getPalletId).filter(Objects::nonNull).forEach(palletIds::add);
+ for (Long palletId : palletIds) {
+ MesProPalletDO pallet = palletMapper.selectById(palletId);
+ if (pallet != null && (pallet.getBatchId() != null || StrUtil.isNotEmpty(pallet.getCode()))) {
+ throw exception(PRO_BAG_CODE_BIND_PALLET_BOUND);
+ }
}
- // 3. 鍒犻櫎鐢熸垚缁�
+ // 4. 鍒犻櫎鐢熸垚缁勪笌鍏惰嚜鍔ㄧ敓鎴愮殑涓存椂鎵樼洏
bagCodeMapper.deleteByIds(bagCodes.stream().map(MesProBagCodeDO::getId).toList());
- // 4. 鍥炲噺鎵规琚嬬爜鏁伴噺
+ if (!palletIds.isEmpty()) {
+ palletMapper.deleteByIds(palletIds);
+ }
+ // 5. 鍥炲噺鎵规琚嬬爜鏁伴噺
MesProBatchDO batch = batchMapper.selectById(bagCodes.get(0).getBatchId());
if (batch != null && batch.getBagQuantity() != null) {
batchMapper.updateById(new MesProBatchDO().setId(batch.getId())
@@ -262,13 +442,8 @@
}
// 2. 閿佸畾骞舵牎楠岃鐮�
List<MesProBagCodeDO> bagCodes = validateBagCodesForBind(bindReqVO.getIds(), pallet, true);
- // 3. 鏍¢獙涓嶈秴杩囨墭鐩樺彲瑁呰鏁�
+ // 3. 缁戝畾琚嬬爜骞剁疮鍔犳墭鐩樺凡瑁呰鏁帮紙鎵樼洏鏃犲閲忎笂闄愶級
int currentCount = pallet.getBagCount() == null ? 0 : pallet.getBagCount();
- if (pallet.getCapacity() != null && pallet.getCapacity() > 0
- && currentCount + bagCodes.size() > pallet.getCapacity()) {
- throw exception(PRO_BAG_CODE_BIND_PALLET_OVER_CAPACITY);
- }
- // 4. 缁戝畾琚嬬爜骞剁疮鍔犳墭鐩樺凡瑁呰鏁�
bagCodeMapper.bindPalletByIds(bindReqVO.getIds(), pallet.getId(), pallet.getCode());
palletService.updatePalletBagCount(pallet.getId(), currentCount + bagCodes.size());
}
--
Gitblit v1.9.3