|
@@ -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){
|