Browse Source

订单根据用户配置拆分出库任务

wudingsheng 3 weeks ago
parent
commit
37eafd95d1

+ 184 - 77
imcs-admin-boot/imcs-business-biz/src/main/java/com/github/zuihou/business/operationManagementCenter/service/impl/OrderServiceImpl.java

@@ -54,10 +54,7 @@ import com.github.zuihou.business.wms.WmsTransferTaskDetail;
 import com.github.zuihou.business.wms.dao.ApiCallLogMapper;
 import com.github.zuihou.business.wms.dao.WmsTransferTaskDetailMapper;
 import com.github.zuihou.business.wms.dao.WmsTransferTaskMapper;
-import com.github.zuihou.common.constant.BizConstant;
-import com.github.zuihou.common.constant.CodeRuleModule;
-import com.github.zuihou.common.constant.ParameterKey;
-import com.github.zuihou.common.constant.RepeatCacheKey;
+import com.github.zuihou.common.constant.*;
 import com.github.zuihou.common.util.DateUtil;
 //import com.github.zuihou.common.util.ForEachUtil;
 // com.github.zuihou.common.util.TimeUtils;
@@ -70,7 +67,9 @@ import com.github.zuihou.tenant.dao.ProductionresourceviewMapper;
 import com.github.zuihou.tenant.service.CodeRuleService;
 import com.github.zuihou.utils.BeanPlusUtil;
 import com.google.common.collect.Maps;
+import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
+import lombok.var;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.compress.utils.Lists;
 import org.apache.commons.lang3.StringUtils;
@@ -506,94 +505,155 @@ public class OrderServiceImpl extends SuperServiceImpl<OrderMapper, Order> imple
                 // 插入task表
                 taskService.saveBatch(taskList);
                 //start---imcs->wms
-                Boolean wmsFlag = (Boolean)redisTemplate.opsForValue().get(RepeatCacheKey.WMS_FLAG);
-                if(wmsFlag!=null && wmsFlag){
-                    ApiCallLog apiCallLog=new ApiCallLog();
+                Boolean wmsFlag = (Boolean)redisTemplate.opsForValue().get(CacheKey.WMS_FLAG);
+                // 假设:wmsFlag、oriBean、productList、url、codeRuleService、各 mapper 可用
+                // 建议放在 @Transactional(rollbackFor=Exception.class) 里,且“调用外部接口”可在事务外或用 REQUIRES_NEW 的日志事务
+                int box = (int) redisTemplate.opsForValue().get(CacheKey.PROPERTIES+oriBean.getZoneId()+CacheKey.BOX_CAPACITY);
+                if (Boolean.TRUE.equals(wmsFlag)) {
+                    // ====== 0) 参数与准备 ======
+                    final int BOX_CAPACITY = 10; // TODO: 从配置/参数表读取
+                    int priority = (oriBean.getPrority() == null ? 1 : oriBean.getPrority());
+                    Long zoneId = oriBean.getZoneId();
+
+                    // —— 预取,避免 N+1 —— //
+                    List<Long> receiveIds = productList.stream()
+                            .map(vo -> vo.getMeterialReceiveId())
+                            .filter(Objects::nonNull).distinct().collect(Collectors.toList());
+
+                    Map<Long, MMeterialReceiveLog> recvMap = mMeterialReceiveLogMapper.selectBatchIds(receiveIds)
+                            .stream().collect(Collectors.toMap(MMeterialReceiveLog::getId, x -> x));
+
+                    List<Long> meterialIds = recvMap.values().stream()
+                            .map(MMeterialReceiveLog::getMeterialId)
+                            .filter(Objects::nonNull).distinct().collect(Collectors.toList());
+
+                    Map<Long, MMeterial> meterialMap = mMeterialMapper.selectBatchIds(meterialIds)
+                            .stream().collect(Collectors.toMap(MMeterial::getId, x -> x));
+
+                    // —— 组装原始物料清单 —— //
+                    List<MatItem> srcList = new ArrayList<>();
+                    for (var vo : productList) {
+                        MMeterialReceiveLog recv = recvMap.get(vo.getMeterialReceiveId());
+                        if (recv == null) continue;
+                        MMeterial mm = meterialMap.get(recv.getMeterialId());
+                        if (mm == null) continue;
+
+                        srcList.add(new MatItem(
+                                mm.getId(),
+                                mm.getMeterialCode(),
+                                mm.getEquipmentName(),
+                                vo.getBomNum()
+                        ));
+                    }
+
+                    // ====== 1) 拆箱(保持物料顺序,单箱最多 BOX_CAPACITY 件) ======
+                    List<List<MatItem>> bins = splitByCapacity(srcList, BOX_CAPACITY);
+
+                    // ====== 2) 为每个箱生成一条任务并落库,同时构建“批量下发”的 JSON ======
+                    cn.hutool.json.JSONArray orderArray = new cn.hutool.json.JSONArray();
+
+                    // 统一接口调用日志(一次请求)
+                    ApiCallLog apiCallLog = new ApiCallLog();
                     long startTime = System.currentTimeMillis();
-                    try{
-                        //出库任务
-                        String taskNo = codeRuleService.getBillCode(CodeRuleModule.CODE_RULE_WMS_OUT);
-                        WmsTransferTask wmsTransferTask=new WmsTransferTask();
-
-                        wmsTransferTask.setOrderId(oriBean.getId());
-                        wmsTransferTask.setOrderNo(oriBean.getOrderNo());
-                        wmsTransferTask.setStatus(0);
-                        wmsTransferTaskMapper.insert(wmsTransferTask);
-
-                        WmsAgvInfo wmsAgvInfo=new WmsAgvInfo();
-                        wmsAgvInfo.setStatus(false);
-                        wmsAgvInfo.setOrderId(oriBean.getId());
-                        wmsAgvInfo.setPriority(oriBean.getPrority() == null ? 1 : oriBean.getPrority());
-                        wmsAgvInfo.setTaskNo(taskNo);
-                        wmsAgvInfo.setTaskType("OUT");
-                        wmsAgvInfo.setWmsTransferTaskId(wmsTransferTask.getId());
-                        wmsAgvInfoMapper.insert(wmsAgvInfo);
-                        //agv托运的物料详情
-                        List<WmsAgvInfoDetail> detailList=new ArrayList<>();
-                        JSONArray jsonArray=new JSONArray();
-                        productList.stream().forEach(vo->{
-                            MMeterialReceiveLog mMeterialReceiveLog = mMeterialReceiveLogMapper.selectOne(Wraps.<MMeterialReceiveLog>lbQ().eq(MMeterialReceiveLog::getId, vo.getMeterialReceiveId()));
-                            MMeterial mMeterial = mMeterialMapper.selectOne(Wraps.<MMeterial>lbQ().eq(MMeterial::getId,mMeterialReceiveLog.getMeterialId()));
-
-                            WmsAgvInfoDetail wmsAgvInfoDetail=new WmsAgvInfoDetail();
-                            wmsAgvInfoDetail.setWmsAgvInfoId(wmsAgvInfo.getId());
-                            wmsAgvInfoDetail.setMeterialId(mMeterial.getId());
-                            wmsAgvInfoDetail.setMaterialCode(mMeterial.getMeterialCode());
-                            wmsAgvInfoDetail.setMaterialName(mMeterial.getEquipmentName());
-                            wmsAgvInfoDetail.setQty(vo.getBomNum());
-                            detailList.add(wmsAgvInfoDetail);
-
-                            cn.hutool.json.JSONObject jsonObject1 = new cn.hutool.json.JSONObject();
-                            jsonObject1.put("materialCode",mMeterial.getMeterialCode());
-                            jsonObject1.put("materialName",mMeterial.getEquipmentName());
-                            jsonObject1.put("quantity",vo.getBomNum());
-                            jsonArray.put(jsonObject1);
-                        });
-
-                        wmsAgvInfoDetailMapper.insertBatchSomeColumn(detailList);
-
-                        //发送出库任务给wms
-                        cn.hutool.json.JSONObject jsonObject=new cn.hutool.json.JSONObject();
-                        //jsonObject.put("orderId",oriBean.getId());
-                        jsonObject.put("taskNo",taskNo);
-                        jsonObject.put("priority",oriBean.getPrority() == null ? 1 : oriBean.getPrority() );
-                        jsonObject.put("zoneId",oriBean.getZoneId());
-                        jsonObject.put("materialCodeList",jsonArray);
-
-
-                        apiCallLog.setInterfaceName("OUT");
+                    try {
+                        for (List<MatItem> bin : bins) {
+                            // 2.1 任务号
+                            String taskNo = codeRuleService.getBillCode(CodeRuleModule.CODE_RULE_WMS_OUT);
+
+                            // 2.2 WmsTransferTask
+                            WmsTransferTask wmsTransferTask = new WmsTransferTask();
+                            wmsTransferTask.setOrderId(oriBean.getId());
+                            wmsTransferTask.setOrderNo(oriBean.getOrderNo());
+                            wmsTransferTask.setStatus(0);
+                            wmsTransferTaskMapper.insert(wmsTransferTask);
+
+                            // 2.3 WmsAgvInfo
+                            WmsAgvInfo wmsAgvInfo = new WmsAgvInfo();
+                            wmsAgvInfo.setStatus(false);
+                            wmsAgvInfo.setOrderId(oriBean.getId());
+                            wmsAgvInfo.setPriority(priority);
+                            wmsAgvInfo.setTaskNo(taskNo);
+                            wmsAgvInfo.setTaskType("OUT");
+                            wmsAgvInfo.setWmsTransferTaskId(wmsTransferTask.getId());
+                            wmsAgvInfoMapper.insert(wmsAgvInfo);
+
+                            // 2.4 明细 + 下游 JSON 的 materialCodeList
+                            List<WmsAgvInfoDetail> detailList = new ArrayList<>(bin.size());
+                            cn.hutool.json.JSONArray materialArray = new cn.hutool.json.JSONArray();
+
+                            for (MatItem it : bin) {
+                                // DB 明细
+                                WmsAgvInfoDetail d = new WmsAgvInfoDetail();
+                                d.setWmsAgvInfoId(wmsAgvInfo.getId());
+                                d.setMeterialId(it.getMeterialId());
+                                d.setMaterialCode(it.getMaterialCode());
+                                d.setMaterialName(it.getMaterialName());
+                                d.setQty(it.getQuantity());
+                                detailList.add(d);
+
+                                // 下游 JSON
+                                cn.hutool.json.JSONObject obj = new cn.hutool.json.JSONObject();
+                                obj.put("materialCode", it.getMaterialCode());
+                                obj.put("materialName", it.getMaterialName());
+                                obj.put("quantity", it.getQuantity());
+                                materialArray.put(obj);
+                            }
+                            wmsAgvInfoDetailMapper.insertBatchSomeColumn(detailList);
+
+                            // 2.5 构建“单个出库任务”的 JSON 对象,加入 order 数组
+                            cn.hutool.json.JSONObject orderObj = new cn.hutool.json.JSONObject();
+                            orderObj.put("taskNo", taskNo);
+                            orderObj.put("priority", priority);
+                            orderObj.put("zoneId", zoneId);
+                            orderObj.put("materialCodeList", materialArray);
+
+                            orderArray.put(orderObj);
+                        }
+
+                        // ====== 3) 一次性发送:{"order":[ ... 多个任务 ... ]} ======
+                        cn.hutool.json.JSONObject payload = new cn.hutool.json.JSONObject();
+                        payload.put("order", orderArray);
+
+                        // —— 日志(请求前) —— //
+                        apiCallLog.setInterfaceName("OUT_BATCH");
                         apiCallLog.setRequestUrl(url);
-                        apiCallLog.setRequestParams(jsonObject.toString());
+                        apiCallLog.setRequestParams(payload.toString());
                         apiCallLog.setRequestTime(new Date());
+
                         HttpHeaders headers = new HttpHeaders();
                         headers.setContentType(MediaType.parseMediaType("application/json;charset=UTF-8"));
-                        HttpEntity<String> formEntity = new HttpEntity<String>(jsonObject.toString(), headers);
-                        //String returnData = restTemplate.postForObject(url, formEntity, String.class);
-                        String returnData="{\n" +
-                                "\t\"code\": \"200\",\n" +
-                                "\t\"msg\": \"操作成功\"\n" +
-                                "}";
-                        apiCallLog.setResponseData(returnData);
-                        JSONObject retJson = JSONObject.parseObject(returnData);
-                        String code = retJson.get("code").toString();
+                        HttpEntity<String> formEntity = new HttpEntity<>(payload.toString(), headers);
+
+                        // 正式调用
+                        // String returnData = restTemplate.postForObject(url, formEntity, String.class);
+
+                        // 模拟返回(你替换为真实调用)
+                        String returnData = "{ \"code\":\"200\", \"msg\":\"批量操作成功\" }";
+
+                        // —— 解析返回并记录 —— //
                         apiCallLog.setResponseData(returnData);
-                        if (!"200".equals(code)){
-                            apiCallLog.setSuccess(1);
+                        com.alibaba.fastjson.JSONObject retJson = com.alibaba.fastjson.JSONObject.parseObject(returnData);
+                        String code = String.valueOf(retJson.get("code"));
+                        apiCallLog.setSuccess("200".equals(code) ? 1 : 0);
+
+                        if (!"200".equals(code)) {
+                            throw new RuntimeException("WMS 批量返回非200,code=" + code + ", body=" + returnData);
                         }
-                    }catch (Exception e) {
-                        log.error("调wms接口报错:{}",e);
-                        // 6. 异常处理
-                        apiCallLog.setResponseTime(new Date());
+
+                    } catch (Exception e) {
+                        log.error("调用WMS批量出库接口异常:{}", e.getMessage(), e);
                         apiCallLog.setSuccess(0);
                         apiCallLog.setErrorMessage(e.getMessage());
-                        throw new RuntimeException("调wms接口报错:{}",e);
+                        throw new RuntimeException("调WMS批量接口报错", e);
                     } finally {
-                        // 7. 计算耗时并保存到数据库
                         long duration = System.currentTimeMillis() - startTime;
                         apiCallLog.setDurationMs((int) duration);
+                        apiCallLog.setResponseTime(new Date());
                         saveLog(apiCallLog);
                     }
                 }
+
+
                 //end---imcs->wms
 
             }
@@ -608,6 +668,53 @@ public class OrderServiceImpl extends SuperServiceImpl<OrderMapper, Order> imple
     }
 
 
+    @Data
+    static class MatItem {
+        private String materialCode;
+        private String materialName;
+        private int quantity;
+        private Long meterialId;
+        private MatItem(){}
+        public MatItem(Long meterialId, String materialCode, String materialName, int quantity) {
+            this.meterialId = meterialId;
+            this.materialCode = materialCode;
+            this.materialName = materialName;
+            this.quantity = quantity;
+        }// WmsAgvInfoDetail 需要
+    }
+
+    /** 把物料清单按“单箱最多装 N 件(不限品类)”切成多箱 */
+    static List<List<MatItem>> splitByCapacity(List<MatItem> src, int capacity) {
+        if (capacity <= 0) throw new IllegalArgumentException("capacity must be > 0");
+        List<List<MatItem>> bins = new ArrayList<>();
+        List<MatItem> curr = new ArrayList<>();
+        int remain = capacity;
+
+        for (MatItem it : src) {
+            int left = it.getQuantity();
+            while (left > 0) {
+                if (remain == 0) {
+                    bins.add(curr);
+                    curr = new ArrayList<>();
+                    remain = capacity;
+                }
+                int put = Math.min(left, remain);
+                MatItem piece = new MatItem();
+                piece.setMaterialCode(it.getMaterialCode());
+                piece.setMaterialName(it.getMaterialName());
+                piece.setQuantity(put);
+                piece.setMeterialId(it.getMeterialId());
+                curr.add(piece);
+
+                left -= put;
+                remain -= put;
+            }
+        }
+        if (!curr.isEmpty()) bins.add(curr);
+        return bins;
+    }
+
+
     private List<Map>getProductMapList(List<OrderProduct>productList){
         List<Map> mapList = new ArrayList<>();
         for(OrderProduct orderProduct:productList){

+ 6 - 0
imcs-admin-boot/imcs-common/src/main/java/com/github/zuihou/common/constant/CacheKey.java

@@ -278,6 +278,12 @@ public interface CacheKey {
      */
     String MQ_ROBOT_INTERCEPT = "MQ_ROBOT_INTERCEPT";
 
+    String WMS_FLAG="WMS_FLAG";
+    String BOX_CAPACITY="_BOX_CAPACITY";
+    String PROPERTIES="PROPERTIES:";
+
+
+
 
     /**
      * 构建key

+ 1 - 1
imcs-admin-boot/imcs-common/src/main/java/com/github/zuihou/common/constant/RepeatCacheKey.java

@@ -20,6 +20,6 @@ public interface RepeatCacheKey {
 
     String ONETOUCHSCHEDULE_UNION_KEY = "ONETOUCHSCHEDULE_UNION_KEY";
 
-    String WMS_FLAG="WMS_FLAG";
+
 
 }