From db06b944e7886832d20b8e3ae62b2cb70bcba30f Mon Sep 17 00:00:00 2001
From: 1 <1@123>
Date: 星期五, 17 四月 2026 13:59:50 +0800
Subject: [PATCH] lsh#订单完结

---
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/impl/ErpReportServiceImpl.java |  262 +++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 198 insertions(+), 64 deletions(-)

diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/impl/ErpReportServiceImpl.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/impl/ErpReportServiceImpl.java
index 7c96629..d62a7be 100644
--- a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/impl/ErpReportServiceImpl.java
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/impl/ErpReportServiceImpl.java
@@ -14,7 +14,6 @@
 import com.vincent.rsf.openApi.entity.app.OpenApiOrder;
 import com.vincent.rsf.openApi.entity.app.OpenApiOrderItem;
 import com.vincent.rsf.openApi.entity.app.OpenApiOrderItemMap;
-import com.vincent.rsf.openApi.entity.app.OpenApiOrderReportEvent;
 import com.vincent.rsf.openApi.entity.constant.WmsConstant;
 import com.vincent.rsf.openApi.entity.dto.CommonResponse;
 import com.vincent.rsf.openApi.entity.phyz.*;
@@ -23,7 +22,6 @@
 import com.vincent.rsf.openApi.mapper.OpenApiOrderItemMapMapper;
 import com.vincent.rsf.openApi.mapper.OpenApiOrderItemMapper;
 import com.vincent.rsf.openApi.mapper.OpenApiOrderMapper;
-import com.vincent.rsf.openApi.mapper.OpenApiOrderReportEventMapper;
 import com.vincent.rsf.openApi.service.WmsErpService;
 import com.vincent.rsf.openApi.service.phyz.ErpReportService;
 import com.vincent.rsf.openApi.utils.ParamsMapUtils;
@@ -83,8 +81,6 @@
     private OpenApiOrderItemMapper openApiOrderItemMapper;
     @Resource
     private OpenApiOrderItemMapMapper openApiOrderItemMapMapper;
-    @Resource
-    private OpenApiOrderReportEventMapper openApiOrderReportEventMapper;
 
     private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
 
@@ -442,62 +438,185 @@
         }
         JSONObject root = JSONObject.parseObject(JSON.toJSONString(params));
 
-        String eventId = pickString(root, "eventId", "EventId");
-        String taskNo = pickString(root, "taskNo", "TaskNo");
-        String reportNo = pickString(root, "reportNo", "ReportNo");
-        if (StringUtils.isBlank(eventId) && StringUtils.isBlank(taskNo) && StringUtils.isBlank(reportNo)) {
-            throw new CoolException("骞傜瓑閿笉鑳戒负绌猴紙eventId/taskNo/reportNo锛�");
+        String orderNo = pickString(root, "orderNo", "OrderNo", "WMSNO", "wmsno");
+        if (StringUtils.isBlank(orderNo)) {
+            return CommonResponse.error("orderNo涓嶈兘涓虹┖");
         }
 
-        if (existsReportEvent(eventId, taskNo, reportNo)) {
-            return CommonResponse.ok("閲嶅鍥炰紶宸插拷鐣�");
+        // 瑙f瀽鏄庣粏锛氫紭鍏堝彇 orderItems锛圤rder鏍煎紡锛夛紝鍏煎 Data/data锛圧eportParams鏍煎紡锛�
+        JSONArray dataArray = pickArray(root, "orderItems", "orderItems");
+        if (Objects.isNull(dataArray) || dataArray.isEmpty()) {
+            dataArray = pickArray(root, "Data", "data");
         }
-        OpenApiOrderReportEvent reportEvent = saveReportEvent(eventId, taskNo, reportNo, pickString(root, "orderNo", "WMSNO"), root.toJSONString());
-
-        JSONArray dataArray = pickArray(root, "Data", "data");
         if (Objects.isNull(dataArray) || dataArray.isEmpty()) {
             return CommonResponse.error("鍥炰紶鏄庣粏涓虹┖");
         }
 
-        int allocateCount = 0;
-        int erpReportCount = 0;
-        List<String> errors = new ArrayList<>();
+        // 瀵瑰洖浼犳暟鎹仛鍙嶆槧灏勶紙WMS瀛楁鍚� 鈫� ERP瀛楁鍚嶏級锛屽吋瀹规棫鏍煎紡
+        JSONObject mappedRoot = ParamsMapUtils.reverseApiMaps("erp", "orderId", root);
+        JSONArray mappedDataArray = pickArray(mappedRoot, "orderItems", "orderItems");
+        if (Objects.isNull(mappedDataArray) || mappedDataArray.isEmpty()) {
+            mappedDataArray = pickArray(mappedRoot, "Data", "data");
+        }
+        if (Objects.isNull(mappedDataArray) || mappedDataArray.isEmpty()) {
+            mappedDataArray = dataArray;
+        }
 
-        for (int i = 0; i < dataArray.size(); i++) {
-            JSONObject row = dataArray.getJSONObject(i);
-            String orderNo = pickString(row, "WMSNO", "wmsNo", "orderNo", "OrderNo");
-            String matnrCode = pickString(row, "ItemCode", "itemCode", "matNr", "MatNr");
-            String batch = pickString(row, "Batch", "batch", "GoodsNO", "goodsNo");
-            BigDecimal doneQty = pickDecimal(row, "qty", "doneQty", "InQty", "OutQty");
-            if (StringUtils.isBlank(orderNo) || StringUtils.isBlank(matnrCode) || doneQty.compareTo(ZERO) <= 0) {
-                errors.add("绗�" + (i + 1) + "琛岀己灏戝繀瑕佸瓧娈碉紙WMSNO/ItemCode/鏁伴噺锛�");
+        int allocateCount = 0;
+        List<String> errors = new ArrayList<>();
+        Set<String> affectedOrderNos = new HashSet<>();
+        Map<String, OpenApiOrder> orderCache = new HashMap<>();
+
+        for (int i = 0; i < mappedDataArray.size(); i++) {
+            JSONObject row = mappedDataArray.getJSONObject(i);
+            String matnrCode = pickString(row, "matNr", "MatNr", "ItemCode", "itemCode");
+            String batch = pickString(row, "batch", "Batch", "GoodsNO", "goodsNo");
+            String lineId = pickString(row, "lineId", "LineId", "sourceLineId");
+            BigDecimal doneQty = pickDecimal(row, "doneQty", "anfme", "qty", "InQty", "inQty", "OutQty", "outQty", "pdqty", "PDQty");
+            if (StringUtils.isBlank(matnrCode) || doneQty.compareTo(ZERO) <= 0) {
+                errors.add("绗�" + (i + 1) + "琛岀己灏戝繀瑕佸瓧娈碉紙matNr/鏁伴噺锛�");
                 continue;
             }
             doneQty = doneQty.setScale(2, RoundingMode.HALF_UP);
 
             try {
-                int singleAllocated = allocateMergedDoneQty(orderNo, matnrCode, batch, doneQty);
+                int singleAllocated = allocateMergedDoneQty(orderNo, matnrCode, batch, lineId, doneQty, orderCache);
                 allocateCount += singleAllocated;
-                erpReportCount += reportReadyLinesToErp(orderNo);
-                refreshOrderFinishStatus(orderNo);
+                affectedOrderNos.add(orderNo);
             } catch (Exception e) {
-                log.error("澶勭悊鍥炰紶澶辫触锛宱rderNo={}, matnr={}, batch={}", orderNo, matnrCode, batch, e);
+                log.error("澶勭悊鍥炰紶澶辫触锛宱rderNo={}, matnr={}, batch={}, lineId={}", orderNo, matnrCode, batch, lineId, e);
                 errors.add("绗�" + (i + 1) + "琛屽鐞嗗け璐ワ細" + e.getMessage());
             }
         }
 
+        // 寰幆缁撴潫鍚庯紝姣忎釜鍙楀奖鍝嶈鍗曞彧鍒锋柊涓�娆$姸鎬�
+        for (String no : affectedOrderNos) {
+            refreshOrderFinishStatus(no);
+        }
+
         if (!errors.isEmpty()) {
-            reportEvent.setStatus(2);
-            openApiOrderReportEventMapper.updateById(reportEvent);
             return CommonResponse.error("澶勭悊瀹屾垚锛屼絾瀛樺湪寮傚父锛�" + String.join(" | ", errors));
         }
-        reportEvent.setStatus(1);
-        openApiOrderReportEventMapper.updateById(reportEvent);
 
         Map<String, Object> result = new HashMap<>();
         result.put("allocatedCount", allocateCount);
-        result.put("erpReportCount", erpReportCount);
         return CommonResponse.ok(result);
+    }
+
+    /**
+     * 鏌ヨ宸插畬鎴愪絾鏈笂鎶RP鐨勮鍗曪紝缁勮ReportParams涓婃姤ERP
+     * 鐢盿srs-schedule瀹氭椂浠诲姟瑙﹀彂
+     */
+    @Override
+    public CommonResponse reportOrdersToErp() {
+        List<OpenApiOrder> pendingOrders = openApiOrderMapper.selectList(new LambdaQueryWrapper<OpenApiOrder>()
+                .eq(OpenApiOrder::getExceStatus, 2)
+                .eq(OpenApiOrder::getNtyStatus, 0)
+                .last("limit 50"));
+        if (pendingOrders.isEmpty()) {
+            return CommonResponse.ok("鏃犲緟涓婃姤ERP鐨勮鍗�");
+        }
+
+        List<String> errors = new ArrayList<>();
+        int successCount = 0;
+        for (OpenApiOrder order : pendingOrders) {
+            try {
+                List<OpenApiOrderItem> items = openApiOrderItemMapper.selectList(new LambdaQueryWrapper<OpenApiOrderItem>()
+                        .eq(OpenApiOrderItem::getOrderId, order.getId()));
+                if (items.isEmpty()) {
+                    log.warn("璁㈠崟鏃犳槑缁嗭紝璺宠繃涓婃姤ERP锛宱rderNo={}", order.getCode());
+                    continue;
+                }
+
+                ReportParams erpParams = buildReportParams(order, items);
+                CommonResponse erpResp = wmsErpService.reportOrders(erpParams);
+                if (Objects.nonNull(erpResp) && Objects.equals(erpResp.getCode(), 200)) {
+                    order.setNtyStatus(1);
+                    openApiOrderMapper.updateById(order);
+                    successCount++;
+                    log.info("涓婃姤ERP鎴愬姛锛宱rderNo={}", order.getCode());
+                } else {
+                    String msg = Objects.isNull(erpResp) ? "ERP鍝嶅簲涓虹┖" : erpResp.getMsg();
+                    log.warn("涓婃姤ERP澶辫触锛宱rderNo={}锛宮sg={}", order.getCode(), msg);
+                    errors.add(order.getCode() + ":" + msg);
+                }
+            } catch (Exception e) {
+                log.error("涓婃姤ERP寮傚父锛宱rderNo={}", order.getCode(), e);
+                errors.add(order.getCode() + ":" + e.getMessage());
+            }
+        }
+
+        if (!errors.isEmpty()) {
+            Map<String, Object> result = new HashMap<>();
+            result.put("successCount", successCount);
+            result.put("totalCount", pendingOrders.size());
+            return CommonResponse.error("閮ㄥ垎璁㈠崟涓婃姤ERP澶辫触锛�" + String.join(" | ", errors));
+        }
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("successCount", successCount);
+        result.put("totalCount", pendingOrders.size());
+        return CommonResponse.ok(result);
+    }
+
+    /**
+     * 浠嶰penApiOrder+OpenApiOrderItem缁勮ReportParams
+     */
+    private ReportParams buildReportParams(OpenApiOrder order, List<OpenApiOrderItem> items) {
+        String erpOrderType = resolveErpOrderType(order.getType(), order.getWkType());
+
+        List<ReportDataParam> reportDataList = new ArrayList<>();
+        for (OpenApiOrderItem item : items) {
+            BigDecimal qty = defaultQty(item.getQty());
+            if (qty.compareTo(ZERO) <= 0) {
+                continue;
+            }
+
+            ReportDataParam dataParam = new ReportDataParam()
+                    .setWMSNO(order.getCode())
+                    .setPONO(order.getPoCode())
+                    .setOrderNO(order.getCode())
+                    .setGoodsNO(item.getBatch())
+                    .setItemCode(item.getMatnrCode())
+                    .setEditUser("schedule")
+                    .setEditDate(new Date())
+                    .setMemoDtl(item.getMemo());
+
+            if ("in".equalsIgnoreCase(order.getType())) {
+                dataParam.setInQty(qty.doubleValue());
+            } else {
+                dataParam.setOutQty(qty.doubleValue());
+            }
+
+            reportDataList.add(dataParam);
+        }
+
+        return new ReportParams()
+                .setOrderType(erpOrderType)
+                .setAction("Update")
+                .setData(reportDataList);
+    }
+
+    /**
+     * 鏍规嵁type鍜寃kType瑙f瀽ERP涓婃姤鐨勮鍗曠被鍨�
+     */
+    private String resolveErpOrderType(String type, String wkType) {
+        if (StringUtils.isBlank(wkType)) {
+            return "in".equalsIgnoreCase(type) ? "PO_Instock" : "WO_Outstock";
+        }
+        switch (wkType) {
+            case "PUR_ReceiveBill": return "PO_Instock";
+            case "PUR_MRB": return "PR_Outstock";
+            case "PRD_ReturnMtrl": return "WR_Instock";
+            case "PRD_PickMtrl": return "WO_Outstock";
+            case "PRD_FeedMtrl": return "WR_Instock_BL";
+            case "PRD_INSTOCK":
+            case "PRD_MORPT": return "WO_Outstock";
+            case "STK_MISCELLANEOUS": return "In_Instock";
+            case "STK_MisDelivery": return "Io_Outstock";
+            default:
+                return "in".equalsIgnoreCase(type) ? "PO_Instock" : "WO_Outstock";
+        }
     }
 
     private OpenApiOrder saveOpenApiOrder(Order order) {
@@ -515,6 +634,7 @@
                 .setQty(ZERO)
                 .setWorkQty(ZERO)
                 .setExceStatus(0)
+                .setNtyStatus(0)
                 .setStatus(1)
                 .setBusinessTime(businessTime)
                 .setOrderInternalCode(order.getOrderInternalCode())
@@ -678,43 +798,43 @@
         return merged;
     }
 
-    private boolean existsReportEvent(String eventId, String taskNo, String reportNo) {
-        return openApiOrderReportEventMapper.selectCount(new LambdaQueryWrapper<OpenApiOrderReportEvent>()
-                .eq(OpenApiOrderReportEvent::getEventId, defaultString(eventId))
-                .eq(OpenApiOrderReportEvent::getTaskNo, defaultString(taskNo))
-                .eq(OpenApiOrderReportEvent::getReportNo, defaultString(reportNo))
-                .eq(OpenApiOrderReportEvent::getStatus, 1)) > 0;
-    }
-
-    private OpenApiOrderReportEvent saveReportEvent(String eventId, String taskNo, String reportNo, String orderCode, String payload) {
-        OpenApiOrderReportEvent event = new OpenApiOrderReportEvent()
-                .setEventId(defaultString(eventId))
-                .setTaskNo(defaultString(taskNo))
-                .setReportNo(defaultString(reportNo))
-                .setOrderCode(orderCode)
-                .setPayload(payload)
-                .setStatus(0);
-        openApiOrderReportEventMapper.insert(event);
-        return event;
-    }
-
-    private int allocateMergedDoneQty(String orderNo, String matnrCode, String batch, BigDecimal mergedDoneQty) {
-        OpenApiOrder order = openApiOrderMapper.selectOne(new LambdaQueryWrapper<OpenApiOrder>()
-                .eq(OpenApiOrder::getCode, orderNo)
-                .last("limit 1"));
+    private int allocateMergedDoneQty(String orderNo, String matnrCode, String batch, String lineId, BigDecimal mergedDoneQty, Map<String, OpenApiOrder> orderCache) {
+        OpenApiOrder order = orderCache.computeIfAbsent(orderNo, k ->
+                openApiOrderMapper.selectOne(new LambdaQueryWrapper<OpenApiOrder>()
+                        .eq(OpenApiOrder::getCode, k)
+                        .last("limit 1")));
         if (Objects.isNull(order)) {
             throw new CoolException("鏈壘鍒拌鍗曪細" + orderNo);
         }
 
         LambdaQueryWrapper<OpenApiOrderItemMap> queryWrapper = new LambdaQueryWrapper<OpenApiOrderItemMap>()
                 .eq(OpenApiOrderItemMap::getOrderId, order.getId())
-                .eq(OpenApiOrderItemMap::getMergeMatnrCode, defaultString(matnrCode))
                 .orderByAsc(OpenApiOrderItemMap::getSeqNo)
                 .orderByAsc(OpenApiOrderItemMap::getId);
-        if (StringUtils.isNotBlank(batch)) {
-            queryWrapper.eq(OpenApiOrderItemMap::getMergeBatch, batch);
+
+        // 浼樺厛鐢╨ineId绮剧‘鍖归厤鍘熷琛�
+        if (StringUtils.isNotBlank(lineId)) {
+            queryWrapper.eq(OpenApiOrderItemMap::getSourceLineId, lineId);
+        } else {
+            queryWrapper.eq(OpenApiOrderItemMap::getMergeMatnrCode, defaultString(matnrCode));
+            if (StringUtils.isNotBlank(batch)) {
+                queryWrapper.eq(OpenApiOrderItemMap::getMergeBatch, batch);
+            }
         }
+
         List<OpenApiOrderItemMap> mappingRows = openApiOrderItemMapMapper.selectList(queryWrapper);
+        if (mappingRows.isEmpty() && StringUtils.isNotBlank(lineId)) {
+            // lineId鍖归厤涓嶅埌鏃讹紝鍥為��鍒癿atNr+batch鍖归厤
+            queryWrapper = new LambdaQueryWrapper<OpenApiOrderItemMap>()
+                    .eq(OpenApiOrderItemMap::getOrderId, order.getId())
+                    .eq(OpenApiOrderItemMap::getMergeMatnrCode, defaultString(matnrCode))
+                    .orderByAsc(OpenApiOrderItemMap::getSeqNo)
+                    .orderByAsc(OpenApiOrderItemMap::getId);
+            if (StringUtils.isNotBlank(batch)) {
+                queryWrapper.eq(OpenApiOrderItemMap::getMergeBatch, batch);
+            }
+            mappingRows = openApiOrderItemMapMapper.selectList(queryWrapper);
+        }
         if (mappingRows.isEmpty()) {
             throw new CoolException("鏈壘鍒版槧灏勫叧绯伙紝璁㈠崟锛�" + orderNo + "锛岀墿鏂欙細" + matnrCode);
         }
@@ -740,7 +860,7 @@
             openApiOrderItemMapMapper.updateById(row);
             openApiOrderItemMapper.update(null, new LambdaUpdateWrapper<OpenApiOrderItem>()
                     .eq(OpenApiOrderItem::getId, row.getSourceItemId())
-                    .setSql("qty = IFNULL(qty,0) + " + toAllocate));
+                    .setSql("qty = IFNULL(qty,0) + " + toAllocate.toPlainString()));
             remaining = remaining.subtract(toAllocate).setScale(2, RoundingMode.HALF_UP);
             allocateRows++;
         }
@@ -762,8 +882,22 @@
                 .eq(OpenApiOrderItemMap::getOrderId, order.getId())
                 .orderByAsc(OpenApiOrderItemMap::getSeqNo)
                 .orderByAsc(OpenApiOrderItemMap::getId));
-        int successCount = 0;
 
+        // 鎵归噺鏌ヨ鎵�鏈� sourceItemId 瀵瑰簲鐨� OpenApiOrderItem锛屾秷闄� N+1
+        List<Long> itemIds = mappings.stream()
+                .map(OpenApiOrderItemMap::getSourceItemId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .toList();
+        Map<Long, OpenApiOrderItem> itemMap = new HashMap<>();
+        if (!itemIds.isEmpty()) {
+            List<OpenApiOrderItem> items = openApiOrderItemMapper.selectBatchIds(itemIds);
+            for (OpenApiOrderItem item : items) {
+                itemMap.put(item.getId(), item);
+            }
+        }
+
+        int successCount = 0;
         for (OpenApiOrderItemMap mapping : mappings) {
             BigDecimal allocatedQty = defaultQty(mapping.getAllocatedQty());
             BigDecimal reportedQty = defaultQty(mapping.getReportedQty());
@@ -772,7 +906,7 @@
                 continue;
             }
 
-            OpenApiOrderItem item = openApiOrderItemMapper.selectById(mapping.getSourceItemId());
+            OpenApiOrderItem item = itemMap.get(mapping.getSourceItemId());
             if (Objects.isNull(item)) {
                 continue;
             }

--
Gitblit v1.9.1