From 5ad54547dc97b76dc3d689b9499dec7364968235 Mon Sep 17 00:00:00 2001
From: zss <zss@example.com>
Date: 星期四, 07 五月 2026 17:16:27 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_New_pro' into dev_New_pro

---
 src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java |  182 ++++++++++++++++++++++++++++++++++----------
 1 files changed, 139 insertions(+), 43 deletions(-)

diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
index f45c117..478ecd5 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
@@ -30,12 +30,8 @@
 import java.util.stream.Collectors;
 
 /**
- * <p>
- * 鐠併垹宕熸0鍡樻灐缁捐儻绔熸禒?閺堝秴濮熺�圭偟骞囩猾?
- * </p>
- *
- * @author 閼侯垰顕辨潪顖欐閿涘牊鐫欓懟蹇ョ礆閺堝妾洪崗顒�寰�
- * @since 2026-04-21 03:55:52
+ * 鐢熶骇璁㈠崟棰嗘枡鏈嶅姟瀹炵幇銆�
+ * 璐熻矗棰嗘枡鏂板銆佹洿鏂般�佽ˉ鏂欍�侀��鏂欏強搴撳瓨鑱斿姩銆�
  */
 @Service
 @RequiredArgsConstructor
@@ -53,17 +49,26 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Boolean savePick(ProductionOrderPickDto dto) {
+        // 棰嗘枡鏂板鎬绘祦绋嬶細
+        // 1) 瑙f瀽鍓嶇琛屾暟鎹苟閫愯鍚堝苟鍙傛暟锛�
+        // 2) 鏍¢獙鍙傛暟涓庢壒娆★紱
+        // 3) 鍏堟墸鍑忓簱瀛橈紝鍐嶈惤搴撻鏂欎富璁板綍锛�
+        // 4) 鍐欏叆棰嗘枡娴佹按锛岃褰曟暟閲忓彉鍖栬建杩广��
         List<ProductionOrderPickDto> pickItems = resolvePickItems(dto);
+        // 閫愯澶勭悊棰嗘枡鏁版嵁锛岃鍙风敤浜庢嫾瑁呯簿纭殑鎶ラ敊淇℃伅銆�
         for (int i = 0; i < pickItems.size(); i++) {
             int rowNo = i + 1;
             ProductionOrderPickDto resolvedDto = mergeDto(dto, pickItems.get(i));
+            // 姣忚閮藉仛瀹屾暣鏍¢獙锛屽紓甯镐俊鎭甫琛屽彿銆�
             validatePickParam(resolvedDto, rowNo);
 
+            // 缁熶竴澶勭悊鎵规锛堟敮鎸佸崟鎵规/澶氭壒娆★級銆�
             List<String> batchNoList = resolveBatchNoList(resolvedDto);
             String inventoryBatchNo = pickInventoryBatchNo(batchNoList);
             String storedBatchNo = formatBatchNoStorage(batchNoList);
             subtractInventory(resolvedDto.getProductModelId(), storedBatchNo, resolvedDto.getPickQuantity(), rowNo);
 
+            // 淇濆瓨棰嗘枡涓昏褰曞揩鐓с��
             ProductionOrderPick orderPick = new ProductionOrderPick();
             orderPick.setProductionOrderId(resolvedDto.getProductionOrderId());
             orderPick.setProductModelId(resolvedDto.getProductModelId());
@@ -75,8 +80,10 @@
             orderPick.setDemandedQuantity(resolvedDto.getDemandedQuantity());
             orderPick.setBom(resolvedDto.getBom());
             orderPick.setReturned(false);
+            // 鏂板涓昏褰曘��
             baseMapper.insert(orderPick);
 
+            // 璁板綍鏈棰嗘枡娴佹按锛坆efore=0锛宎fter=鏈棰嗘枡閲忥級銆�
             insertPickRecord(orderPick.getId(),
                     resolvedDto.getProductionOrderId(),
                     resolvedDto.getProductionOperationTaskId(),
@@ -95,8 +102,12 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Boolean updatePick(ProductionOrderPickDto dto) {
+        // 棰嗘枡鏇存柊鍏ュ彛锛堝悓鎺ュ彛鍏煎涓夌被涓氬姟锛夛細
+        // 1) 鏅�氶鏂欐敼閲�/澧炲垹锛�
+        // 2) 琛ユ枡锛坧ickType=2锛夛紱
+        // 3) 閫�鏂欙紙returned=true锛夈��
         if (dto == null) {
-            throw new ServiceException("鍙樻洿鍙傛暟涓嶈兘涓虹┖");
+            throw new ServiceException("鍙傛暟涓嶈兘涓虹┖");
         }
         Long productionOrderId = resolveProductionOrderId(dto);
         if (productionOrderId == null) {
@@ -107,26 +118,32 @@
             throw new ServiceException("鐢熶骇璁㈠崟涓嶅瓨鍦�");
         }
 
+        // 鏌ヨ璁㈠崟涓嬬幇鏈夐鏂欒褰曞苟鏋勫缓ID绱㈠紩銆�
         List<ProductionOrderPick> existingPickList = baseMapper.selectList(
                 Wrappers.<ProductionOrderPick>lambdaQuery()
                         .eq(ProductionOrderPick::getProductionOrderId, productionOrderId));
+        // 杞垚Map渚夸簬鍚庣画鎸塈D蹇�熸牎楠屼笌鏇存柊銆�
         Map<Long, ProductionOrderPick> existingPickMap = existingPickList.stream()
                 .filter(item -> item.getId() != null)
                 .collect(Collectors.toMap(ProductionOrderPick::getId, Function.identity(), (a, b) -> a));
 
+        // 琛ユ枡璇锋眰鍗曠嫭璧拌ˉ鏂欏垎鏀��
         if (isFeedingRequest(dto)) {
             processFeedingPickItems(dto, existingPickMap, productionOrderId);
             return true;
         }
+        // 閫�鏂欒姹傚崟鐙蛋閫�鏂欏垎鏀��
         if (isReturnRequest(dto)) {
             processReturnPickItems(dto, existingPickMap, productionOrderId);
             return true;
         }
 
+        // 鏅�氭洿鏂板満鏅厛澶勭悊鏄惧紡鍒犻櫎銆�
         processDeletePickIds(dto, existingPickMap, productionOrderId);
 
         List<ProductionOrderPickDto> pickItems = resolveUpdateItems(dto);
         Set<Long> keepPickIdSet = new HashSet<>();
+        // keepPickIdSet 鐢ㄤ簬鏍囪鏈鍓嶇浠嶇劧淇濈暀鐨勬棫璁板綍锛屽悗缁敤浜庤瘑鍒�滄湭鍥炰紶鍗冲垹闄も�濈殑琛屻��
         for (int i = 0; i < pickItems.size(); i++) {
             int rowNo = i + 1;
             ProductionOrderPickDto resolvedDto = mergeDto(dto, pickItems.get(i));
@@ -145,12 +162,14 @@
             keepPickIdSet.add(resolvedDto.getId());
             updateExistingPick(resolvedDto, rowNo, existingPickMap);
         }
+        // 娓呯悊鍓嶇鏈洖浼犳棫琛屽苟鍥炶ˉ搴撳瓨銆�
         processMissingPickItems(dto, existingPickMap, productionOrderId, keepPickIdSet);
         return true;
     }
 
     @Override
     public List<ProductionOrderPickVo> listPickedDetail(Long productionOrderId) {
+        // 鏌ヨ璁㈠崟棰嗘枡鏄庣粏锛屽苟琛ラ綈鎵规灞曠ず瀛楁銆�
         if (productionOrderId == null) {
             return Collections.emptyList();
         }
@@ -163,6 +182,11 @@
     private void processDeletePickIds(ProductionOrderPickDto rootDto,
                                       Map<Long, ProductionOrderPick> existingPickMap,
                                       Long productionOrderId) {
+        // 澶勭悊鍓嶇鏄惧紡鍒犻櫎ID锛�
+        // 1) 鏍¢獙鍒犻櫎鐩爣鏄惁灞炰簬褰撳墠璁㈠崟锛�
+        // 2) 鍥炶ˉ搴撳瓨锛�
+        // 3) 鍒犻櫎涓昏褰曪紱
+        // 4) 璁板綍鍒犻櫎娴佹按銆�
         if (rootDto.getDeletePickIds() == null || rootDto.getDeletePickIds().isEmpty()) {
             return;
         }
@@ -173,14 +197,14 @@
             }
             ProductionOrderPick existingPick = existingPickMap.get(deleteId);
             if (existingPick == null || !Objects.equals(existingPick.getProductionOrderId(), productionOrderId)) {
-                throw new ServiceException("瑕佸垹闄ょ殑棰嗘枡璁板綍涓嶅瓨鍦ㄦ垨涓嶅睘浜庡綋鍓嶈鍗曪紝ID=" + deleteId);
+                throw new ServiceException("鍒犻櫎澶辫触锛氶鏂欒褰曚笉瀛樺湪鎴栦笉灞炰簬褰撳墠璁㈠崟锛孖D=" + deleteId);
             }
             String oldBatchNo = resolveInventoryBatchNoFromStored(existingPick.getBatchNo());
             BigDecimal oldQuantity = defaultDecimal(existingPick.getQuantity());
             addInventory(existingPick.getProductModelId(), oldBatchNo, oldQuantity);
             int affected = baseMapper.deleteById(deleteId);
             if (affected <= 0) {
-                throw new ServiceException("鍒犻櫎棰嗘枡澶辫触锛孖D=" + deleteId);
+                throw new ServiceException("鍒犻櫎棰嗘枡璁板綍澶辫触锛孖D=" + deleteId);
             }
             insertPickRecord(existingPick.getId(),
                     existingPick.getProductionOrderId(),
@@ -201,6 +225,9 @@
                                          Map<Long, ProductionOrderPick> existingPickMap,
                                          Long productionOrderId,
                                          Set<Long> keepPickIdSet) {
+        // 澶勭悊鈥滃墠绔湭鍥炰紶鈥濈殑鏃ц锛�
+        // 瀵瑰簲鍦烘櫙鏄敤鎴峰湪鍓嶇鍒犻櫎琛屼絾鏈斁鍏� deletePickIds銆�
+        // 杩欓噷鍏滃簳璇嗗埆骞舵墽琛屽洖琛ュ簱瀛� + 鍒犻櫎涓昏褰� + 鍐欐祦姘淬��
         if (rootDto.getPickList() == null) {
             return;
         }
@@ -216,7 +243,7 @@
             addInventory(missingPick.getProductModelId(), oldBatchNo, oldQuantity);
             int affected = baseMapper.deleteById(missingPick.getId());
             if (affected <= 0) {
-                throw new ServiceException("鍒犻櫎棰嗘枡澶辫触锛孖D=" + missingPick.getId());
+                throw new ServiceException("鍒犻櫎鏈洖浼犻鏂欒褰曞け璐ワ紝ID=" + missingPick.getId());
             }
             insertPickRecord(missingPick.getId(),
                     missingPick.getProductionOrderId(),
@@ -234,6 +261,7 @@
     }
 
     private void addNewPickInUpdate(ProductionOrderPickDto dto, int rowNo) {
+        // 鏇存柊鍦烘櫙涓嬫柊澧炰竴鏉¢鏂欙細鎵e簱瀛� -> 鏂板涓昏褰� -> 鍐欐祦姘淬��
         List<String> batchNoList = resolveBatchNoList(dto);
         String inventoryBatchNo = pickInventoryBatchNo(batchNoList);
         String storedBatchNo = formatBatchNoStorage(batchNoList);
@@ -268,6 +296,8 @@
     private void processFeedingPickItems(ProductionOrderPickDto rootDto,
                                          Map<Long, ProductionOrderPick> existingPickMap,
                                          Long productionOrderId) {
+        // 琛ユ枡娴佺▼鍏ュ彛锛�
+        // 閫愯鏍¢獙琛ユ枡鍙傛暟锛屾牎楠屽師棰嗘枡褰掑睘锛屽啀鎵ц琛ユ枡搴撳瓨鎵e噺鍜屼富璁板綍鍥炲啓銆�
         List<ProductionOrderPickDto> pickItems = resolveUpdateItems(rootDto);
         for (int i = 0; i < pickItems.size(); i++) {
             int rowNo = i + 1;
@@ -276,7 +306,7 @@
                 continue;
             }
             if (!isFeedingPick(resolvedDto)) {
-                throw new ServiceException("琛ユ枡璇锋眰涓殑棰嗘枡绫诲瀷蹇呴』鍏ㄩ儴涓�2");
+                throw new ServiceException("琛ユ枡璇锋眰涓瓨鍦ㄩ潪琛ユ枡绫诲瀷鏁版嵁");
             }
             if (resolvedDto.getProductionOrderId() == null) {
                 resolvedDto.setProductionOrderId(productionOrderId);
@@ -285,15 +315,20 @@
 
             ProductionOrderPick oldPick = existingPickMap.get(resolvedDto.getId());
             if (oldPick == null || !Objects.equals(oldPick.getProductionOrderId(), productionOrderId)) {
-                throw new ServiceException("绗�" + rowNo + "鏉¢鏂欒褰曚笉瀛樺湪鎴栦笉灞炰簬褰撳墠璁㈠崟");
+                throw new ServiceException("绗�" + rowNo + "琛岃ˉ鏂欏け璐ワ細鏈壘鍒板搴旂殑棰嗘枡璁板綍");
             }
             addFeedingPick(resolvedDto, oldPick, rowNo);
         }
     }
 
     private void addFeedingPick(ProductionOrderPickDto dto, ProductionOrderPick oldPick, int rowNo) {
+        // 琛ユ枡鏍稿績锛�
+        // 1) 鏍¢獙瑙勬牸涓�鑷达紱
+        // 2) 鎵e噺琛ユ枡搴撳瓨锛�
+        // 3) 鍐欒ˉ鏂欐祦姘达紱
+        // 4) 鍥炲啓涓诲崟绱琛ユ枡閲忓拰瀹為檯閲忋��
         if (dto.getProductModelId() != null && !Objects.equals(dto.getProductModelId(), oldPick.getProductModelId())) {
-            throw new ServiceException("绗�" + rowNo + "鏉¤ˉ鏂欎骇鍝佽鏍间笌棰嗘枡璁板綍涓嶄竴鑷�");
+            throw new ServiceException("绗�" + rowNo + "琛岃ˉ鏂欏け璐ワ細浜у搧瑙勬牸涓庡師棰嗘枡璁板綍涓嶄竴鑷�");
         }
         Long productModelId = oldPick.getProductModelId();
         List<String> batchNoList = resolveBatchNoList(dto);
@@ -304,6 +339,7 @@
 
         subtractInventory(productModelId, inventoryBatchNo, feedingQuantity, rowNo);
 
+        // 璁$畻琛ユ枡鍓嶅悗鏁伴噺骞跺啓琛ユ枡娴佹按銆�
         BigDecimal beforeFeedingQty = sumFeedingQuantity(dto.getProductionOrderId(), oldPick.getId());
         BigDecimal afterFeedingQty = beforeFeedingQty.add(feedingQuantity);
         insertPickRecord(oldPick.getId(),
@@ -322,9 +358,10 @@
         updatePick.setId(oldPick.getId());
         updatePick.setFeedingQty(afterFeedingQty);
         updatePick.setActualQty(calculateActualQty(oldPick, afterFeedingQty));
+        // 鍥炲啓涓昏褰曠殑琛ユ枡绱鍊间笌瀹為檯鐢ㄩ噺銆�
         int affected = baseMapper.updateById(updatePick);
         if (affected <= 0) {
-            throw new ServiceException("绗�" + rowNo + "鏉¤ˉ鏂欐�婚噺鏇存柊澶辫触");
+            throw new ServiceException("绗�" + rowNo + "琛岃ˉ鏂欏け璐ワ細鏇存柊棰嗘枡涓昏褰曞け璐�");
         }
         oldPick.setFeedingQty(afterFeedingQty);
         oldPick.setActualQty(updatePick.getActualQty());
@@ -333,6 +370,8 @@
     private void processReturnPickItems(ProductionOrderPickDto rootDto,
                                         Map<Long, ProductionOrderPick> existingPickMap,
                                         Long productionOrderId) {
+        // 閫�鏂欐祦绋嬪叆鍙o細
+        // 閫愯鏍¢獙閫�鏂欏弬鏁颁笌棰嗘枡褰掑睘锛屽啀鏇存柊閫�鏂欓噺涓庡疄闄呴噺瀛楁銆�
         List<ProductionOrderPickDto> pickItems = resolveUpdateItems(rootDto);
         for (int i = 0; i < pickItems.size(); i++) {
             int rowNo = i + 1;
@@ -347,13 +386,14 @@
 
             ProductionOrderPick oldPick = existingPickMap.get(resolvedDto.getId());
             if (oldPick == null || !Objects.equals(oldPick.getProductionOrderId(), productionOrderId)) {
-                throw new ServiceException("绗�" + rowNo + "鏉¢鏂欒褰曚笉瀛樺湪鎴栦笉灞炰簬褰撳墠璁㈠崟");
+                throw new ServiceException("绗�" + rowNo + "琛岄��鏂欏け璐ワ細鏈壘鍒板搴旂殑棰嗘枡璁板綍");
             }
             updateReturnPick(resolvedDto, oldPick, rowNo);
         }
     }
 
     private void updateReturnPick(ProductionOrderPickDto dto, ProductionOrderPick oldPick, int rowNo) {
+        // 閫�鏂欐洿鏂板彧鏀逛富棰嗘枡璁板綍涓殑閫�鏂欏瓧娈典笌瀹為檯閲忋��
         ProductionOrderPick updatePick = new ProductionOrderPick();
         updatePick.setId(oldPick.getId());
         updatePick.setReturnQty(dto.getReturnQty());
@@ -361,7 +401,7 @@
         updatePick.setReturned(true);
         int affected = baseMapper.updateById(updatePick);
         if (affected <= 0) {
-            throw new ServiceException("绗�" + rowNo + "鏉¢��鏂欎俊鎭洿鏂板け璐�");
+            throw new ServiceException("绗�" + rowNo + "琛岄��鏂欏け璐ワ細鏇存柊棰嗘枡涓昏褰曞け璐�");
         }
         oldPick.setReturnQty(updatePick.getReturnQty());
         oldPick.setActualQty(updatePick.getActualQty());
@@ -371,9 +411,14 @@
     private void updateExistingPick(ProductionOrderPickDto dto,
                                     int rowNo,
                                     Map<Long, ProductionOrderPick> existingPickMap) {
+        // 鏅�氭洿鏂板崟琛屾牳蹇冩祦绋嬶細
+        // 1) 鏍¢獙鏃ц褰曞瓨鍦ㄤ笖灞炰簬褰撳墠璁㈠崟锛�
+        // 2) 姣旇緝鏂版棫鈥滆鏍�+鎵规鈥濓紝鍐冲畾搴撳瓨澶勭悊绛栫暐锛�
+        // 3) 鏇存柊涓昏褰曪紱
+        // 4) 鍐欏彉鏇存祦姘达紙璁板綍鍓嶅悗鏁伴噺鍙樺寲锛夈��
         ProductionOrderPick oldPick = existingPickMap.get(dto.getId());
         if (oldPick == null || !Objects.equals(oldPick.getProductionOrderId(), dto.getProductionOrderId())) {
-            throw new ServiceException("绗�" + rowNo + "鏉¢鏂欒褰曚笉瀛樺湪鎴栦笉灞炰簬褰撳墠璁㈠崟");
+            throw new ServiceException("绗�" + rowNo + "琛屾洿鏂板け璐ワ細鏈壘鍒板搴旂殑棰嗘枡璁板綍");
         }
 
         Long oldProductModelId = oldPick.getProductModelId();
@@ -386,9 +431,11 @@
         String newStoredBatchNo = formatBatchNoStorage(newBatchNoList);
         BigDecimal newQuantity = dto.getPickQuantity();
 
+        // 鍒ゆ柇瑙勬牸+鎵规鏄惁鍙樺寲锛屽喅瀹氬簱瀛樺鐞嗙瓥鐣ャ��
         boolean sameStockKey = Objects.equals(oldProductModelId, newProductModelId)
                 && Objects.equals(oldBatchNo, newBatchNo);
         if (sameStockKey) {
+            // 瑙勬牸涓庢壒娆′笉鍙橈細鍙寜宸�煎鍑忓簱瀛樸��
             BigDecimal delta = newQuantity.subtract(oldQuantity);
             if (delta.compareTo(BigDecimal.ZERO) > 0) {
                 subtractInventory(newProductModelId, newStoredBatchNo, delta, rowNo);
@@ -396,6 +443,7 @@
                 addInventory(oldProductModelId, oldBatchNo, delta.abs());
             }
         } else {
+            // 瑙勬牸鎴栨壒娆″彉鍖栵細鍏堝洖琛ユ棫搴撳瓨锛屽啀鎵e噺鏂板簱瀛樸��
             addInventory(oldProductModelId, oldBatchNo, oldQuantity);
             subtractInventory(newProductModelId, newStoredBatchNo, newQuantity, rowNo);
         }
@@ -414,9 +462,10 @@
         }
         int affected = baseMapper.updateById(oldPick);
         if (affected <= 0) {
-            throw new ServiceException("绗�" + rowNo + "鏉¢鏂欐洿鏂板け璐�");
+            throw new ServiceException("绗�" + rowNo + "琛屾洿鏂板け璐ワ細鏇存柊棰嗘枡璁板綍澶辫触");
         }
 
+        // 鍐欏叆鏇存柊娴佹按锛屼繚鐣欐湰娆℃暟閲忓彉鍖栬建杩广��
         BigDecimal recordQuantity = sameStockKey ? oldQuantity.subtract(newQuantity).abs() : newQuantity;
         if (recordQuantity.compareTo(BigDecimal.ZERO) > 0 || oldQuantity.compareTo(newQuantity) != 0 || !sameStockKey) {
             insertPickRecord(oldPick.getId(),
@@ -444,6 +493,7 @@
                                   Byte pickType,
                                   String remark,
                                   String feedingReason) {
+        // 鍐欓鏂欐祦姘磋褰曪細缁熶竴璁板綍棰嗘枡/琛ユ枡/閫�鏂欐暟閲忓彉鍖栬建杩广��
         ProductionOrderPickRecord pickRecord = new ProductionOrderPickRecord();
         pickRecord.setPickId(pickId);
         pickRecord.setProductionOrderId(productionOrderId);
@@ -460,7 +510,13 @@
     }
 
     private void subtractInventory(Long productModelId, String batchNo, BigDecimal quantity, int rowNo) {
+        // 鎵e噺搴撳瓨鎬绘祦绋嬶細
+        // 1) 瑙f瀽鎵规鍒楄〃锛�
+        // 2) 璁$畻姣忎釜鎵规鍙敤閲忎笌鎬诲彲鐢ㄩ噺锛�
+        // 3) 鎸夋壒娆¢『搴忛�愮瑪鎵e噺锛岀洿鍒版墸瀹岀洰鏍囨暟閲忥紱
+        // 4) 浠讳竴姝ュけ璐ュ嵆鎶涢敊骞跺洖婊氫簨鍔°��
         BigDecimal deductQuantity = defaultDecimal(quantity);
+        // 棰嗘枡鏁伴噺灏忎簬绛変簬0鏃讹紝涓嶉渶瑕佹墽琛屽簱瀛樻墸鍑忋��
         if (deductQuantity.compareTo(BigDecimal.ZERO) <= 0) {
             return;
         }
@@ -470,9 +526,12 @@
             batchNoList = Collections.singletonList(null);
         }
 
+        // 鍏堣绠楀悇鎵规鍙敤閲忥紝閬垮厤杈规墸杈圭畻瀵艰嚧鍒ゆ柇涓嶄竴鑷淬��
         Map<String, BigDecimal> availableQuantityMap = new LinkedHashMap<>();
         BigDecimal totalAvailableQuantity = BigDecimal.ZERO;
+        // 閬嶅巻鎵规锛岃绠楁瘡涓壒娆″彲鐢ㄥ簱瀛樸��
         for (String currentBatchNo : batchNoList) {
+            // 鏌ヨ褰撳墠瑙勬牸+鎵规鐨勫簱瀛樿褰曘��
             StockInventory stockInventory = stockInventoryMapper.selectOne(buildStockWrapper(productModelId, currentBatchNo));
             BigDecimal availableQuantity = BigDecimal.ZERO;
             if (stockInventory != null) {
@@ -488,10 +547,11 @@
 
         if (deductQuantity.compareTo(totalAvailableQuantity) > 0) {
             BigDecimal shortQuantity = deductQuantity.subtract(totalAvailableQuantity);
-            throw new ServiceException("棰嗘枡鍙敤搴撳瓨涓嶈冻锛屽彲鐢ㄥ簱瀛樹负" + formatQuantity(totalAvailableQuantity)
-                    + "锛岃繕宸�" + formatQuantity(shortQuantity));
+            throw new ServiceException("绗�" + rowNo + "琛屾墸鍑忓簱瀛樺け璐ワ細鍙敤搴撳瓨涓嶈冻锛屽綋鍓嶅彲鐢�"
+                    + formatQuantity(totalAvailableQuantity) + "锛屼粛缂哄皯" + formatQuantity(shortQuantity));
         }
 
+        // 鎸夋壒娆¢『搴忛�愮瑪鎵e噺搴撳瓨銆�
         BigDecimal remainingQuantity = deductQuantity;
         for (Map.Entry<String, BigDecimal> entry : availableQuantityMap.entrySet()) {
             if (remainingQuantity.compareTo(BigDecimal.ZERO) <= 0) {
@@ -508,17 +568,18 @@
             stockInventoryDto.setQualitity(currentDeductQuantity);
             int affected = stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto);
             if (affected <= 0) {
-                throw new ServiceException("绗�" + rowNo + "鏉¢鏂欐墸鍑忓簱瀛樺け璐�");
+                throw new ServiceException("绗�" + rowNo + "琛屾墸鍑忓簱瀛樺け璐ワ細搴撳瓨鏇存柊澶辫触");
             }
             remainingQuantity = remainingQuantity.subtract(currentDeductQuantity);
         }
 
         if (remainingQuantity.compareTo(BigDecimal.ZERO) > 0) {
-            throw new ServiceException("绗�" + rowNo + "鏉¢鏂欐墸鍑忓簱瀛樺け璐ワ紝鍓╀綑寰呮墸鍑忔暟閲忎负" + formatQuantity(remainingQuantity));
+            throw new ServiceException("绗�" + rowNo + "琛屾墸鍑忓簱瀛樺け璐ワ細浠嶆湁鏈墸鍑忔暟閲�" + formatQuantity(remainingQuantity));
         }
     }
 
     private void addInventory(Long productModelId, String batchNo, BigDecimal quantity) {
+        // 鍥炶ˉ搴撳瓨锛堢敤浜庡垹闄ら鏂欍�佹敼灏忛鏂欍�佸垏鎹㈡壒娆$瓑鍦烘櫙锛夈��
         BigDecimal addQuantity = defaultDecimal(quantity);
         if (addQuantity.compareTo(BigDecimal.ZERO) <= 0) {
             return;
@@ -533,8 +594,9 @@
     }
 
     private List<ProductionOrderPickDto> resolvePickItems(ProductionOrderPickDto dto) {
+        // 瑙f瀽鏂板鍦烘櫙鐨勯鏂欐槑缁嗛泦鍚堛��
         if (dto == null) {
-            throw new ServiceException("棰嗘枡鍙傛暟涓嶈兘涓虹┖");
+            throw new ServiceException("鍙傛暟涓嶈兘涓虹┖");
         }
         if (dto.getPickList() != null && !dto.getPickList().isEmpty()) {
             return dto.getPickList();
@@ -543,6 +605,7 @@
     }
 
     private List<ProductionOrderPickDto> resolveUpdateItems(ProductionOrderPickDto dto) {
+        // 瑙f瀽鏇存柊鍦烘櫙鐨勯鏂欐槑缁嗛泦鍚堛��
         if (dto.getPickList() != null) {
             return dto.getPickList();
         }
@@ -553,6 +616,7 @@
     }
 
     private boolean isEmptyUpdateItem(ProductionOrderPickDto dto) {
+        // 鍒ゆ柇鏇存柊琛屾槸鍚︿负绌虹櫧鍗犱綅琛屻��
         return dto.getId() == null
                 && dto.getProductModelId() == null
                 && dto.getPickQuantity() == null
@@ -573,6 +637,7 @@
     }
 
     private Long resolveProductionOrderId(ProductionOrderPickDto dto) {
+        // 浼樺厛浠庝富DTO瑙f瀽璁㈠崟ID锛屼笉瀛樺湪鏃跺啀浠庡瓙椤逛腑鍥為��鏌ユ壘銆�
         if (dto.getProductionOrderId() != null) {
             return dto.getProductionOrderId();
         }
@@ -588,7 +653,12 @@
     }
 
     private ProductionOrderPickDto mergeDto(ProductionOrderPickDto rootDto, ProductionOrderPickDto itemDto) {
+        // 鍚堝苟瑙勫垯锛�
+        // - itemDto 浼樺厛鎵胯浇琛岀骇杈撳叆锛�
+        // - itemDto 缂哄け瀛楁浠� rootDto 鍏滃簳缁ф壙锛�
+        // - 杈撳嚭 merged 浣滀负缁熶竴涓氬姟鍏ュ弬銆�
         ProductionOrderPickDto merged = new ProductionOrderPickDto();
+        // 鍏堟嫹璐濊绾у瓧娈点��
         if (itemDto != null) {
             merged.setId(itemDto.getId());
             merged.setProductionOrderId(itemDto.getProductionOrderId());
@@ -667,51 +737,55 @@
     }
 
     private void validatePickParam(ProductionOrderPickDto dto, int rowNo) {
+        // 鏍¢獙鏅�氶鏂欏弬鏁帮紙璁㈠崟銆佽鏍笺�佹暟閲忋�佺被鍨嬶級銆�
         if (dto.getProductionOrderId() == null) {
-            throw new ServiceException("绗�" + rowNo + "鏉$敓浜ц鍗旾D涓嶈兘涓虹┖");
+            throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細鐢熶骇璁㈠崟ID涓嶈兘涓虹┖");
         }
         if (dto.getProductModelId() == null) {
-            throw new ServiceException("绗�" + rowNo + "鏉′骇鍝佽鏍糏D涓嶈兘涓虹┖");
+            throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細浜у搧瑙勬牸涓嶈兘涓虹┖");
         }
         if (dto.getPickQuantity() == null || dto.getPickQuantity().compareTo(BigDecimal.ZERO) < 0) {
-            throw new ServiceException("绗�" + rowNo + "鏉¢鏂欐暟閲忎笉鑳藉皬浜�0");
+            throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細棰嗘枡鏁伴噺涓嶈兘灏忎簬0");
         }
         if (dto.getPickType() != null && dto.getPickType() != PICK_TYPE_NORMAL && dto.getPickType() != PICK_TYPE_FEEDING) {
-            throw new ServiceException("绗�" + rowNo + "鏉¢鏂欑被鍨嬪彧鑳芥槸1鎴�2");
+            throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細棰嗘枡绫诲瀷浠呮敮鎸�1(棰嗘枡)鎴�2(琛ユ枡)");
         }
     }
 
     private void validateFeedingParam(ProductionOrderPickDto dto, int rowNo) {
+        // 鏍¢獙琛ユ枡鍙傛暟锛堣鍗曘�侀鏂橧D銆佽ˉ鏂欐暟閲忋�佺被鍨嬶級銆�
         if (dto.getProductionOrderId() == null) {
-            throw new ServiceException("绗�" + rowNo + "鏉$敓浜ц鍗旾D涓嶈兘涓虹┖");
+            throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細鐢熶骇璁㈠崟ID涓嶈兘涓虹┖");
         }
         if (dto.getId() == null) {
-            throw new ServiceException("绗�" + rowNo + "鏉¢鏂橧D涓嶈兘涓虹┖");
+            throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細棰嗘枡璁板綍ID涓嶈兘涓虹┖");
         }
         if (dto.getFeedingQuantity() == null || dto.getFeedingQuantity().compareTo(BigDecimal.ZERO) < 0) {
-            throw new ServiceException("绗�" + rowNo + "鏉℃湰娆¤ˉ鏂欐暟閲忎笉鑳藉皬浜�0");
+            throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細琛ユ枡鏁伴噺涓嶈兘灏忎簬0");
         }
         if (!isFeedingPick(dto)) {
-            throw new ServiceException("绗�" + rowNo + "鏉¤ˉ鏂欑被鍨嬪繀椤讳负2");
+            throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細琛ユ枡鍦烘櫙涓嬮鏂欑被鍨嬪繀椤讳负2");
         }
     }
 
     private void validateReturnParam(ProductionOrderPickDto dto, int rowNo) {
+        // 鏍¢獙閫�鏂欏弬鏁帮紙璁㈠崟銆侀鏂橧D銆侀��鏂欓噺銆佸疄闄呴噺锛夈��
         if (dto.getProductionOrderId() == null) {
-            throw new ServiceException("绗�" + rowNo + "鏉$敓浜ц鍗旾D涓嶈兘涓虹┖");
+            throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細鐢熶骇璁㈠崟ID涓嶈兘涓虹┖");
         }
         if (dto.getId() == null) {
-            throw new ServiceException("绗�" + rowNo + "鏉¢鏂橧D涓嶈兘涓虹┖");
+            throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細棰嗘枡璁板綍ID涓嶈兘涓虹┖");
         }
         if (dto.getReturnQty() == null || dto.getReturnQty().compareTo(BigDecimal.ZERO) < 0) {
-            throw new ServiceException("绗�" + rowNo + "鏉¢��鏂欐暟閲忎笉鑳戒负绌轰笖涓嶈兘灏忎簬0");
+            throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細閫�鏂欐暟閲忎笉鑳藉皬浜�0");
         }
         if (dto.getActualQty() == null || dto.getActualQty().compareTo(BigDecimal.ZERO) < 0) {
-            throw new ServiceException("绗�" + rowNo + "鏉″疄闄呮暟閲忎笉鑳戒负绌轰笖涓嶈兘灏忎簬0");
+            throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細瀹為檯鏁伴噺涓嶈兘灏忎簬0");
         }
     }
 
     private boolean isFeedingRequest(ProductionOrderPickDto dto) {
+        // 鍒ゆ柇褰撳墠璇锋眰鏄惁灞炰簬琛ユ枡娴佺▼銆�
         if (isFeedingPick(dto)) {
             return true;
         }
@@ -724,10 +798,12 @@
     }
 
     private boolean isFeedingPick(ProductionOrderPickDto dto) {
+        // 鍒ゆ柇褰撳墠琛屾槸鍚︿负琛ユ枡绫诲瀷銆�
         return dto != null && Objects.equals(dto.getPickType(), PICK_TYPE_FEEDING);
     }
 
     private boolean isReturnRequest(ProductionOrderPickDto dto) {
+        // 鍒ゆ柇褰撳墠璇锋眰鏄惁灞炰簬閫�鏂欐祦绋嬨��
         if (isReturnPick(dto)) {
             return true;
         }
@@ -740,10 +816,12 @@
     }
 
     private boolean isReturnPick(ProductionOrderPickDto dto) {
+        // 鍒ゆ柇褰撳墠琛屾槸鍚︿负閫�鏂欑被鍨嬨��
         return dto != null && Boolean.TRUE.equals(dto.getReturned());
     }
 
     private BigDecimal sumFeedingQuantity(Long productionOrderId, Long pickId) {
+        // 姹囨�绘寚瀹氶鏂欏崟鐨勫巻鍙茶ˉ鏂欐�婚噺銆�
         List<ProductionOrderPickRecord> feedingRecords = productionOrderPickRecordMapper.selectList(
                 Wrappers.<ProductionOrderPickRecord>lambdaQuery()
                         .eq(ProductionOrderPickRecord::getProductionOrderId, productionOrderId)
@@ -756,12 +834,14 @@
     }
 
     private BigDecimal calculateActualQty(ProductionOrderPick pick, BigDecimal feedingQty) {
+        // 鎸夆�滈鏂�+琛ユ枡-閫�鏂欌�濊绠楀疄闄呯敤閲忋��
         return defaultDecimal(pick.getQuantity())
                 .add(defaultDecimal(feedingQty))
                 .subtract(defaultDecimal(pick.getReturnQty()));
     }
 
     private String normalizeBatchNo(String batchNo) {
+        // 鏍囧噯鍖栨壒娆″彿锛堝幓绌虹櫧銆佺┖涓茶浆null锛夈��
         if (StringUtils.isEmpty(batchNo)) {
             return null;
         }
@@ -769,6 +849,7 @@
         return trimBatchNo.isEmpty() ? null : trimBatchNo;
     }
     private List<String> resolveBatchNoList(ProductionOrderPickDto dto) {
+        // 浼樺厛瑙f瀽 batchNoList锛岀┖鍒欏洖閫�瑙f瀽 batchNo 瀛楃涓层��
         List<String> normalizedBatchNoList = normalizeBatchNoList(dto.getBatchNoList());
         if (!normalizedBatchNoList.isEmpty()) {
             return normalizedBatchNoList;
@@ -777,6 +858,7 @@
     }
 
     private String pickInventoryBatchNo(List<String> batchNoList) {
+        // 浠庢壒娆¢泦鍚堜腑鍙栧簱瀛樻墸鍑忎娇鐢ㄧ殑鎵规銆�
         if (batchNoList == null || batchNoList.isEmpty()) {
             return null;
         }
@@ -784,10 +866,12 @@
     }
 
     private String resolveInventoryBatchNoFromStored(String storedBatchNo) {
+        // 浠庢暟鎹簱瀛樺偍鎵规瀛楁涓弽瑙e彲鐢ㄦ壒娆°��
         return pickInventoryBatchNo(parseBatchNoValue(storedBatchNo));
     }
 
     private String formatBatchNoStorage(List<String> batchNoList) {
+        // 灏嗘壒娆¢泦鍚堟牸寮忓寲涓烘暟鎹簱瀛樺偍鍊笺��
         if (batchNoList == null || batchNoList.isEmpty()) {
             return null;
         }
@@ -798,6 +882,7 @@
     }
 
     private List<String> normalizeBatchNoList(List<String> batchNoList) {
+        // 鎵归噺鏍囧噯鍖栨壒娆″彿骞跺幓閲嶃��
         if (batchNoList == null || batchNoList.isEmpty()) {
             return Collections.emptyList();
         }
@@ -812,6 +897,7 @@
     }
 
     private void fillBatchNoList(List<ProductionOrderPickVo> detailList) {
+        // 灏嗗悓璁㈠崟+鍚岃鏍�+鍚屽伐搴忕殑鏁版嵁鎸夌粍鑱氬悎鎵规锛屼究浜庡墠绔粺涓�灞曠ず銆�
         if (detailList == null || detailList.isEmpty()) {
             return;
         }
@@ -832,9 +918,11 @@
     }
 
     private void fillSelectableBatchNoList(List<ProductionOrderPickVo> detailList) {
+        // 鍚堝苟鈥滃凡閫夋壒娆♀�濆拰鈥滃簱瀛樺彲閫夋壒娆♀�濓紝鐢ㄤ簬鍓嶇涓嬫媺銆�
         if (detailList == null || detailList.isEmpty()) {
             return;
         }
+        // 鍏堟敹闆嗘槑缁嗕腑娑夊強鐨勮鏍糏D锛屾壒閲忔煡璇㈠簱瀛樻壒娆°��
         Set<Long> productModelIdSet = detailList.stream()
                 .map(ProductionOrderPickVo::getProductModelId)
                 .filter(Objects::nonNull)
@@ -868,30 +956,35 @@
     }
 
     private String buildBatchNoGroupKey(ProductionOrderPickVo detail) {
-        return String.valueOf(detail.getProductionOrderId()) + "|"
-                + String.valueOf(detail.getProductModelId()) + "|"
-                + String.valueOf(detail.getTechnologyOperationId()) + "|"
-                + String.valueOf(detail.getOperationName());
+        // 鏋勫缓鎵规鑱氬悎鍒嗙粍閿��
+        return detail.getProductionOrderId() + "|"
+                + detail.getProductModelId() + "|"
+                + detail.getTechnologyOperationId() + "|"
+                + detail.getOperationName();
     }
 
     private List<String> parseBatchNoValue(String rawBatchNoValue) {
+        // 鎵规瑙f瀽鍏煎涓夌鏍煎紡锛�
+        // 1) 鍗曞�硷細A001
+        // 2) 閫楀彿鍒嗛殧锛欰001,A002
+        // 3) 绫籎SON鏁扮粍瀛楃涓诧細["A001","A002"]
         String normalizedValue = normalizeBatchNo(rawBatchNoValue);
         if (StringUtils.isEmpty(normalizedValue)) {
             return Collections.emptyList();
         }
-        if (normalizedValue.startsWith("[") && normalizedValue.endsWith("]")) {
+        if (normalizedValue != null && normalizedValue.startsWith("[") && normalizedValue.endsWith("]")) {
             String value = normalizedValue.substring(1, normalizedValue.length() - 1);
             if (StringUtils.isEmpty(value)) {
                 return Collections.emptyList();
             }
             List<String> parsed = Arrays.stream(value.split(","))
-                    .map(item -> item == null ? null : item.trim().replace("\"", "").replace("'", ""))
+                    .map(item -> item.trim().replace("\"", "").replace("'", ""))
                     .collect(Collectors.toList());
             return normalizeBatchNoList(parsed);
         }
-        if (normalizedValue.contains(",")) {
+        if (normalizedValue != null && normalizedValue.contains(",")) {
             List<String> parsed = Arrays.stream(normalizedValue.split(","))
-                    .map(item -> item == null ? null : item.trim())
+                    .map(item -> item.trim())
                     .collect(Collectors.toList());
             return normalizeBatchNoList(parsed);
         }
@@ -899,6 +992,7 @@
     }
 
     private LambdaQueryWrapper<StockInventory> buildStockWrapper(Long productModelId, String batchNo) {
+        // 鏋勫缓搴撳瓨鏌ヨ鏉′欢锛堣鏍� + 鎵规锛夈��
         LambdaQueryWrapper<StockInventory> wrapper = Wrappers.<StockInventory>lambdaQuery()
                 .eq(StockInventory::getProductModelId, productModelId);
         if (StringUtils.isEmpty(batchNo)) {
@@ -910,10 +1004,12 @@
     }
 
     private BigDecimal defaultDecimal(BigDecimal value) {
+        // BigDecimal 绌哄�煎厹搴曪紝缁熶竴鎸�0澶勭悊銆�
         return value == null ? BigDecimal.ZERO : value;
     }
 
     private String formatQuantity(BigDecimal value) {
+        // 鏁伴噺鏍煎紡鍖栬緭鍑猴紙鍘婚櫎鏈熬鏃犳晥0锛夈��
         return defaultDecimal(value).stripTrailingZeros().toPlainString();
     }
 }

--
Gitblit v1.9.3