|
@@ -510,111 +510,114 @@ public class OrderServiceImpl extends SuperServiceImpl<OrderMapper, Order> imple
|
|
// 建议放在 @Transactional(rollbackFor=Exception.class) 里,且“调用外部接口”可在事务外或用 REQUIRES_NEW 的日志事务
|
|
// 建议放在 @Transactional(rollbackFor=Exception.class) 里,且“调用外部接口”可在事务外或用 REQUIRES_NEW 的日志事务
|
|
int box = (int) redisTemplate.opsForValue().get(CacheKey.PROPERTIES+oriBean.getZoneId()+CacheKey.BOX_CAPACITY);
|
|
int box = (int) redisTemplate.opsForValue().get(CacheKey.PROPERTIES+oriBean.getZoneId()+CacheKey.BOX_CAPACITY);
|
|
if (Boolean.TRUE.equals(wmsFlag)) {
|
|
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);
|
|
|
|
|
|
+ final int BOX_CAPACITY = box;
|
|
|
|
|
|
- // ====== 2) 为每个箱生成一条任务并落库,同时构建“批量下发”的 JSON ======
|
|
|
|
- cn.hutool.json.JSONArray orderArray = new cn.hutool.json.JSONArray();
|
|
|
|
-
|
|
|
|
- // 统一接口调用日志(一次请求)
|
|
|
|
ApiCallLog apiCallLog = new ApiCallLog();
|
|
ApiCallLog apiCallLog = new ApiCallLog();
|
|
long startTime = System.currentTimeMillis();
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
+
|
|
try {
|
|
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 明细
|
|
|
|
|
|
+ // 预取,避免 N+1
|
|
|
|
+ List<Long> receiveIds = productList.stream()
|
|
|
|
+ .map(vo -> vo.getMeterialReceiveId())
|
|
|
|
+ .filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
|
|
|
+
|
|
|
|
+ Map<Long, MMeterialReceiveLog> recvMap = receiveIds.isEmpty()
|
|
|
|
+ ? Collections.emptyMap()
|
|
|
|
+ : 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 = meterialIds.isEmpty()
|
|
|
|
+ ? Collections.emptyMap()
|
|
|
|
+ : mMeterialMapper.selectBatchIds(meterialIds).stream()
|
|
|
|
+ .collect(Collectors.toMap(MMeterial::getId, x -> x));
|
|
|
|
+
|
|
|
|
+ // 构建一次性下发的总数组
|
|
|
|
+ cn.hutool.json.JSONArray allOrdersArray = new cn.hutool.json.JSONArray();
|
|
|
|
+
|
|
|
|
+ int priority = (oriBean.getPrority() == null ? 1 : oriBean.getPrority());
|
|
|
|
+ Long zoneId = oriBean.getZoneId();
|
|
|
|
+
|
|
|
|
+ // —— 逐个 OrderProduct 拆箱(不与其他 OrderProduct 混装)——
|
|
|
|
+ for (var op : productList) {
|
|
|
|
+ MMeterialReceiveLog recv = recvMap.get(op.getMeterialReceiveId());
|
|
|
|
+ if (recv == null) continue;
|
|
|
|
+ MMeterial mm = meterialMap.get(recv.getMeterialId());
|
|
|
|
+ if (mm == null) continue;
|
|
|
|
+
|
|
|
|
+ final Long orderProductId = op.getId();
|
|
|
|
+ final String materialCode = mm.getMeterialCode();
|
|
|
|
+ final String materialName = mm.getEquipmentName();
|
|
|
|
+ int qtyLeft = op.getBomNum();
|
|
|
|
+
|
|
|
|
+ while (qtyLeft > 0) {
|
|
|
|
+ int put = Math.min(qtyLeft, BOX_CAPACITY);
|
|
|
|
+ qtyLeft -= put;
|
|
|
|
+
|
|
|
|
+ // 1) 生成任务号
|
|
|
|
+ String taskNo = codeRuleService.getBillCode(CodeRuleModule.CODE_RULE_WMS_OUT);
|
|
|
|
+
|
|
|
|
+ // 2) WmsTransferTask(★ 绑定 order_product_id)
|
|
|
|
+ WmsTransferTask wmsTransferTask = new WmsTransferTask();
|
|
|
|
+ wmsTransferTask.setOrderId(oriBean.getId()); // 你的订单主ID(保持)
|
|
|
|
+ wmsTransferTask.setOrderNo(oriBean.getOrderNo());
|
|
|
|
+ wmsTransferTask.setOrderProductId(orderProductId); // ★ 新增:按订单明细分组
|
|
|
|
+ wmsTransferTask.setStatus(0);
|
|
|
|
+ wmsTransferTaskMapper.insert(wmsTransferTask);
|
|
|
|
+
|
|
|
|
+ // 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);
|
|
|
|
+
|
|
|
|
+ // 4) 明细(只放这一个 OrderProduct 的该批数量)
|
|
|
|
+ List<WmsAgvInfoDetail> detailList = new ArrayList<>(1);
|
|
WmsAgvInfoDetail d = new WmsAgvInfoDetail();
|
|
WmsAgvInfoDetail d = new WmsAgvInfoDetail();
|
|
d.setWmsAgvInfoId(wmsAgvInfo.getId());
|
|
d.setWmsAgvInfoId(wmsAgvInfo.getId());
|
|
- d.setMeterialId(it.getMeterialId());
|
|
|
|
- d.setMaterialCode(it.getMaterialCode());
|
|
|
|
- d.setMaterialName(it.getMaterialName());
|
|
|
|
- d.setQty(it.getQuantity());
|
|
|
|
|
|
+ d.setMeterialId(mm.getId());
|
|
|
|
+ d.setMaterialCode(materialCode);
|
|
|
|
+ d.setMaterialName(materialName);
|
|
|
|
+ d.setQty(put);
|
|
|
|
+ // 如果明细表也有 order_product_id(推荐),一并写入:
|
|
|
|
+ // d.setOrderProductId(orderProductId);
|
|
detailList.add(d);
|
|
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);
|
|
|
|
+
|
|
|
|
+ // 5) 本任务的 JSON(materialCodeList 仅该 OrderProduct)
|
|
|
|
+ cn.hutool.json.JSONArray materialArray = new cn.hutool.json.JSONArray();
|
|
|
|
+ cn.hutool.json.JSONObject item = new cn.hutool.json.JSONObject();
|
|
|
|
+ item.put("materialCode", materialCode);
|
|
|
|
+ item.put("materialName", materialName);
|
|
|
|
+ item.put("quantity", put);
|
|
|
|
+ materialArray.put(item);
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+
|
|
|
|
+ // 可选:把来源也带给WMS,便于对账/回滚(看WMS是否接受)
|
|
|
|
+ // orderObj.put("orderProductId", orderProductId);
|
|
|
|
+ // orderObj.put("orderId", oriBean.getId());
|
|
|
|
+ // orderObj.put("orderNo", oriBean.getOrderNo());
|
|
|
|
+
|
|
|
|
+ allOrdersArray.put(orderObj);
|
|
}
|
|
}
|
|
- 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":[ ... 多个任务 ... ]} ======
|
|
|
|
|
|
+ // 6) 一次性发送
|
|
cn.hutool.json.JSONObject payload = new cn.hutool.json.JSONObject();
|
|
cn.hutool.json.JSONObject payload = new cn.hutool.json.JSONObject();
|
|
- payload.put("order", orderArray);
|
|
|
|
|
|
+ payload.put("order", allOrdersArray);
|
|
|
|
|
|
- // —— 日志(请求前) —— //
|
|
|
|
apiCallLog.setInterfaceName("OUT_BATCH");
|
|
apiCallLog.setInterfaceName("OUT_BATCH");
|
|
apiCallLog.setRequestUrl(url);
|
|
apiCallLog.setRequestUrl(url);
|
|
apiCallLog.setRequestParams(payload.toString());
|
|
apiCallLog.setRequestParams(payload.toString());
|
|
@@ -624,13 +627,9 @@ public class OrderServiceImpl extends SuperServiceImpl<OrderMapper, Order> imple
|
|
headers.setContentType(MediaType.parseMediaType("application/json;charset=UTF-8"));
|
|
headers.setContentType(MediaType.parseMediaType("application/json;charset=UTF-8"));
|
|
HttpEntity<String> formEntity = new HttpEntity<>(payload.toString(), headers);
|
|
HttpEntity<String> formEntity = new HttpEntity<>(payload.toString(), headers);
|
|
|
|
|
|
- // 正式调用
|
|
|
|
// String returnData = restTemplate.postForObject(url, formEntity, String.class);
|
|
// String returnData = restTemplate.postForObject(url, formEntity, String.class);
|
|
|
|
+ String returnData = "{ \"code\":\"200\", \"msg\":\"批量操作成功\" }"; // mock
|
|
|
|
|
|
- // 模拟返回(你替换为真实调用)
|
|
|
|
- String returnData = "{ \"code\":\"200\", \"msg\":\"批量操作成功\" }";
|
|
|
|
-
|
|
|
|
- // —— 解析返回并记录 —— //
|
|
|
|
apiCallLog.setResponseData(returnData);
|
|
apiCallLog.setResponseData(returnData);
|
|
com.alibaba.fastjson.JSONObject retJson = com.alibaba.fastjson.JSONObject.parseObject(returnData);
|
|
com.alibaba.fastjson.JSONObject retJson = com.alibaba.fastjson.JSONObject.parseObject(returnData);
|
|
String code = String.valueOf(retJson.get("code"));
|
|
String code = String.valueOf(retJson.get("code"));
|
|
@@ -639,7 +638,6 @@ public class OrderServiceImpl extends SuperServiceImpl<OrderMapper, Order> imple
|
|
if (!"200".equals(code)) {
|
|
if (!"200".equals(code)) {
|
|
throw new RuntimeException("WMS 批量返回非200,code=" + code + ", body=" + returnData);
|
|
throw new RuntimeException("WMS 批量返回非200,code=" + code + ", body=" + returnData);
|
|
}
|
|
}
|
|
-
|
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
log.error("调用WMS批量出库接口异常:{}", e.getMessage(), e);
|
|
log.error("调用WMS批量出库接口异常:{}", e.getMessage(), e);
|
|
apiCallLog.setSuccess(0);
|
|
apiCallLog.setSuccess(0);
|
|
@@ -654,6 +652,7 @@ public class OrderServiceImpl extends SuperServiceImpl<OrderMapper, Order> imple
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+
|
|
//end---imcs->wms
|
|
//end---imcs->wms
|
|
|
|
|
|
}
|
|
}
|
|
@@ -668,51 +667,6 @@ 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){
|
|
private List<Map>getProductMapList(List<OrderProduct>productList){
|