From 1a0bdd8df58435ec37e9d8345e67cd092902b5e4 Mon Sep 17 00:00:00 2001
From: cl <1442464845@qq.com>
Date: 星期六, 04 四月 2026 00:27:18 +0800
Subject: [PATCH] 序号控制
---
src/main/java/com/zy/api/controller/WcsApiController.java | 4
src/main/java/com/zy/asrs/service/WrkMastService.java | 5
src/main/java/com/zy/asrs/task/WcsDeviceStatusScheduler.java | 7
src/main/java/com/zy/integration/iot/client/impl/PahoIotMqttClient.java | 48 ++++
src/main/java/com/zy/asrs/controller/OpenController.java | 40 +++
src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java | 411 ++++++++++++++++++++++++++++++++++++-----
src/main/java/com/zy/asrs/service/impl/WrkMastServiceImpl.java | 32 +++
src/main/java/com/zy/asrs/mapper/WrkMastLogMapper.java | 7
src/main/java/com/zy/asrs/mapper/WrkMastMapper.java | 6
src/main/java/com/zy/asrs/task/WorkMastScheduler.java | 14
src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java | 7
src/main/java/com/zy/api/service/WcsApiService.java | 9
12 files changed, 516 insertions(+), 74 deletions(-)
diff --git a/src/main/java/com/zy/api/controller/WcsApiController.java b/src/main/java/com/zy/api/controller/WcsApiController.java
index 0c86a2d..68997fc 100644
--- a/src/main/java/com/zy/api/controller/WcsApiController.java
+++ b/src/main/java/com/zy/api/controller/WcsApiController.java
@@ -15,6 +15,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
+import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;
@@ -48,10 +49,11 @@
// @ManagerAuth
@ApiOperation("璁惧鎵ц鐘舵�佸洖鍐�-wcs浠诲姟瀹屾垚鍥炲啓")
@PostMapping("/openapi/report")
- public R receviceTaskFromWcs(@RequestBody ReceviceTaskParams params) {
+ public R receviceTaskFromWcs(@RequestBody ReceviceTaskParams params, HttpServletRequest request) {
if (Objects.isNull(params)) {
return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
}
+ request.setAttribute("cache", params);
return wcsApiService.receviceTaskFromWcs(params);
}
diff --git a/src/main/java/com/zy/api/service/WcsApiService.java b/src/main/java/com/zy/api/service/WcsApiService.java
index a820563..6a2d08c 100644
--- a/src/main/java/com/zy/api/service/WcsApiService.java
+++ b/src/main/java/com/zy/api/service/WcsApiService.java
@@ -44,7 +44,14 @@
* @date 2026/3/21 11:30
* @return com.core.common.R
*/
- R syncDeviceStatusFromWcs();
+ default R syncDeviceStatusFromWcs() {
+ return syncDeviceStatusFromWcs(true);
+ }
+
+ /**
+ * @param logOnFailure false 鏃朵笉杈撳嚭 ERROR 绾уけ璐ユ棩蹇楋紙渚涘畾鏃朵换鍔¢檷鍣級
+ */
+ R syncDeviceStatusFromWcs(boolean logOnFailure);
/**
* batch pause out tasks
diff --git a/src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java b/src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java
index 3c89459..5d658fb 100644
--- a/src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java
+++ b/src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java
@@ -1,6 +1,7 @@
package com.zy.api.service.impl;
import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.core.common.Cools;
@@ -38,10 +39,18 @@
private static final String YES = "Y";
private static final String NO = "N";
+ /** 鍚屼竴 WCS 璺緞銆佸悓涓�鍗曞彿涓嬩竴缁勪笅鍙戠殑浠诲姟鏉℃暟涓婇檺 */
+ private static final int WCS_PUB_BATCH_SIZE = 20;
+
+ /** 涓夋柟鎺ュ彛缁熻锛氭湰绯荤粺璋冪敤 WCS 鐨� namespace 绾﹀畾 */
+ private static final String NS_WMS_TO_WCS = "鏈郴缁熻姹俉CS";
+
@Autowired
private LocMastService locMastService;
@Autowired
private WrkMastService wrkMastService;
+ @Autowired
+ private WrkMastLogService wrkMastLogService;
@Autowired
private WorkService workService;
@Autowired
@@ -69,6 +78,8 @@
private String stopOutTask;
@Value("${wcs.address.getDeviceStatus:/openapi/getDeviceStatus}")
private String getDeviceStatus;
+ @Value("${wcs.address.queryTask:/openapi/queryTask}")
+ private String queryTaskPath;
@Value("${wcs.status-sync.method:GET}")
private String deviceStatusMethod;
@Autowired
@@ -77,6 +88,8 @@
private BasDevpService basDevpService;
@Autowired
private BasCrnpService basCrnpService;
+ @Autowired
+ private ApiLogService apiLogService;
/**
@@ -98,8 +111,11 @@
return R.error(validateMsg);
}
String url = resolveTaskPath(params);
- String response;
+ String requestJson = JSON.toJSONString(params);
+ String response = null;
R r = R.ok();
+ Throwable wcsThrown = null;
+ boolean wcsBizOk = false;
try {
log.info("涓嬪彂鎼繍浠诲姟缁檞cs="+JSON.toJSONString(params));
response = new HttpHandler.Builder()
@@ -107,12 +123,13 @@
.setPath(url)
.setHttps(wcs_address != null && wcs_address.startsWith("https://"))
.setTimeout(10, TimeUnit.SECONDS)
- .setJson(JSON.toJSONString(params))
+ .setJson(requestJson)
.build()
.doPost();
JSONObject jsonObject = JSON.parseObject(response);
log.info("涓嬪彂浠诲姟缁檞cs鐨勮繑鍥炲��="+response);
Integer code = jsonObject.getInteger("code");
+ wcsBizOk = code != null && code == 200;
if (code==200) {
updateWrkMastAfterPublish(wrkMast);
@@ -121,7 +138,10 @@
r =R.error();
}
} catch (IOException e) {
+ wcsThrown = e;
throw new RuntimeException(e);
+ } finally {
+ logWcsToApiLog(url, requestJson, response, wcsThrown, wcsBizOk);
}
return r;
}
@@ -132,9 +152,8 @@
return R.error("浠诲姟涓嶈兘涓虹┖锛侊紒");
}
- // 鍏堜竴娆℃�ф妸鏈壒浠诲姟瀵瑰簲鐨勫伐浣滄。鎹炲嚭鏉ワ紝閬垮厤寰幆鍐呴噸澶嶆煡搴撱��
Map<String, WrkMast> wrkMastMap = getWrkMastMap(paramsList);
- Map<String, List<WorkTaskParams>> groupedTasks = new LinkedHashMap<>();
+ List<WorkTaskParams> accepted = new ArrayList<>();
List<String> skipMsgs = new ArrayList<>();
for (WorkTaskParams params : paramsList) {
@@ -148,58 +167,64 @@
skipMsgs.add(buildTaskMsg(params, validateMsg));
continue;
}
-
- // 鍒嗙粍涓婚敭 = 鎺ュ彛璺緞 + userNo銆�
- // 杩欐牱鏃㈣兘淇濊瘉鍏ュ簱/鍑哄簱/绉诲簱涓嶄細娣峰彂锛屼篃鑳戒繚璇佺浉鍚� userNo 鐨勪换鍔′細鎵撳寘鍒板悓涓�娆� WCS 璇锋眰涓��
- String groupKey = buildBatchGroupKey(params, wrkMast);
- groupedTasks.computeIfAbsent(groupKey, key -> new ArrayList<>()).add(params);
+ accepted.add(params);
}
- if (groupedTasks.isEmpty()) {
+ if (accepted.isEmpty()) {
return R.error(skipMsgs.isEmpty() ? "鏃犲彲涓嬪彂浠诲姟" : skipMsgs.get(0)).add(skipMsgs);
}
+ accepted = filterOutboundByContiguousPlt(accepted, wrkMastMap, skipMsgs);
+ if (accepted.isEmpty()) {
+ return R.error(skipMsgs.isEmpty() ? "鏃犲彲涓嬪彂浠诲姟" : skipMsgs.get(0)).add(skipMsgs);
+ }
+
+ accepted.sort(pubWcsSortComparator(wrkMastMap));
+ List<List<WorkTaskParams>> chunks = buildPubChunks(accepted, wrkMastMap);
+
int successCount = 0;
List<String> failMsgs = new ArrayList<>();
- for (List<WorkTaskParams> group : groupedTasks.values()) {
- if (group == null || group.isEmpty()) {
+ List<WorkTaskParams> lastSentChunk = null;
+ String skipGroupKey = null;
+
+ for (List<WorkTaskParams> chunk : chunks) {
+ if (chunk == null || chunk.isEmpty()) {
+ continue;
+ }
+ WorkTaskParams head = chunk.get(0);
+ WrkMast headMast = wrkMastMap.get(head.getTaskNo());
+ String key = buildBatchGroupKey(head, headMast);
+
+ if (skipGroupKey != null && skipGroupKey.equals(key)) {
continue;
}
- // 鍚屼竴缁勫唴鐨勪换鍔$被鍨嬩竴鑷达紝鍥犳鍙栫涓�鏉″嵆鍙‘瀹氭湰缁勫簲璇ヨ皟鐢ㄥ摢涓� WCS 鎺ュ彛銆�
- String path = resolveTaskPath(group.get(0));
- Map<String, Object> payload = new HashMap<>();
- // WCS 鎵归噺涓嬪彂鎶ユ枃缁熶竴浣跨敤 {"tasks":[...]} 缁撴瀯銆�
- payload.put("taskList", buildTaskPayloads(group));
- String response = null;
- try {
- log.info("鎵归噺涓嬪彂鎼繍浠诲姟缁檞cs={}", JSON.toJSONString(payload));
- response = new HttpHandler.Builder()
- .setUri(wcs_address)
- .setPath(path)
-// .setHttps(wcs_address != null && wcs_address.startsWith("https://"))
- .setTimeout(10, TimeUnit.SECONDS)
- .setJson(JSON.toJSONString(payload))
- .build()
- .doPost();
- JSONObject jsonObject = JSON.parseObject(response == null ? "{}" : response);
- log.info("鎵归噺涓嬪彂浠诲姟缁檞cs鐨勮繑鍥炲��={}", response);
- Integer code = jsonObject.getInteger("code");
- if (code != null && code == 200) {
- successCount += group.size();
- // 鍙湁鏁寸粍涓嬪彂鎴愬姛锛屾墠鍥炲啓鏈湴宸ヤ綔妗g姸鎬侊紝閬垮厤 WMS/WCS 鐘舵�佸垎鍙夈��
- for (WorkTaskParams params : group) {
- updateWrkMastAfterPublish(wrkMastMap.get(params.getTaskNo()));
- }
- } else {
- String msg = jsonObject.getString("msg");
- failMsgs.add("path=" + path + ", msg=" + (Cools.isEmpty(msg) ? "WCS涓嬪彂浠诲姟澶辫触" : msg));
- log.error("鎵归噺涓嬪彂浠诲姟缁檞cs澶辫触, path:{}, request:{}, response:{}", path, JSON.toJSONString(payload), response);
- }
- } catch (IOException e) {
- failMsgs.add("path=" + path + ", msg=" + e.getMessage());
- log.error("鎵归噺涓嬪彂浠诲姟缁檞cs寮傚父, path:{}, request:{}, response:{}", path, JSON.toJSONString(payload), response, e);
+ if (!outboundChunkPredecessorPltReady(chunk, wrkMastMap)) {
+ skipGroupKey = key;
+ continue;
}
+
+ if (lastSentChunk != null) {
+ WorkTaskParams lastHead = lastSentChunk.get(0);
+ String lastKey = buildBatchGroupKey(lastHead, wrkMastMap.get(lastHead.getTaskNo()));
+ if (lastKey.equals(key)) {
+ if (!sameOrderNextChunkAllowed(lastSentChunk)) {
+ skipGroupKey = key;
+ continue;
+ }
+ }
+ if (!sleepOneMinuteBeforeNextChunk()) {
+ break;
+ }
+ }
+
+ int ok = postWcsBatchChunk(chunk, wrkMastMap, failMsgs);
+ if (ok <= 0) {
+ skipGroupKey = key;
+ continue;
+ }
+ successCount += ok;
+ lastSentChunk = chunk;
}
Map<String, Object> result = new HashMap<>();
@@ -218,6 +243,268 @@
return R.error(msg).add(result);
}
return R.ok(failMsgs.isEmpty() && skipMsgs.isEmpty() ? "鎿嶄綔鎴愬姛" : "閮ㄥ垎浠诲姟涓嬪彂鎴愬姛").add(result);
+ }
+
+ private int postWcsBatchChunk(List<WorkTaskParams> chunk, Map<String, WrkMast> wrkMastMap, List<String> failMsgs) {
+ if (chunk == null || chunk.isEmpty()) {
+ return 0;
+ }
+ String path = resolveTaskPath(chunk.get(0));
+ Map<String, Object> payload = new HashMap<>();
+ payload.put("taskList", buildTaskPayloads(chunk));
+ String requestJson = JSON.toJSONString(payload);
+ String response = null;
+ Throwable wcsThrown = null;
+ boolean wcsBizOk = false;
+ try {
+ log.info("鎵归噺涓嬪彂鎼繍浠诲姟缁檞cs={}", requestJson);
+ response = new HttpHandler.Builder()
+ .setUri(wcs_address)
+ .setPath(path)
+ .setTimeout(60, TimeUnit.SECONDS)
+ .setJson(requestJson)
+ .build()
+ .doPost();
+ JSONObject jsonObject = JSON.parseObject(response == null ? "{}" : response);
+ log.info("鎵归噺涓嬪彂浠诲姟缁檞cs鐨勮繑鍥炲��={}", response);
+ Integer code = jsonObject.getInteger("code");
+ wcsBizOk = code != null && code == 200;
+ if (wcsBizOk) {
+ for (WorkTaskParams params : chunk) {
+ updateWrkMastAfterPublish(wrkMastMap.get(params.getTaskNo()));
+ }
+ return chunk.size();
+ }
+ String msg = jsonObject.getString("msg");
+ failMsgs.add("path=" + path + ", msg=" + (Cools.isEmpty(msg) ? "WCS涓嬪彂浠诲姟澶辫触" : msg));
+ log.error("鎵归噺涓嬪彂浠诲姟缁檞cs澶辫触, path:{}, request:{}, response:{}", path, requestJson, response);
+ } catch (IOException e) {
+ wcsThrown = e;
+ failMsgs.add("path=" + path + ", msg=" + e.getMessage());
+ log.error("鎵归噺涓嬪彂浠诲姟缁檞cs寮傚父, path:{}, request:{}, response:{}", path, requestJson, response, e);
+ } finally {
+ logWcsToApiLog(path, requestJson, response, wcsThrown, wcsBizOk);
+ }
+ return 0;
+ }
+
+ /**
+ * 鍑哄簱锛氫粎褰撳崟鍙枫�佸簭鍙峰潎鏈夋晥鏃跺仛璺冲彿鏍¢獙锛涘崟鍙风┖鎴栧簭鍙锋棤鏁堜粛涓嬪彂銆傚叆搴�/绉诲簱涓嶅鐞嗐��
+ */
+ private List<WorkTaskParams> filterOutboundByContiguousPlt(List<WorkTaskParams> accepted, Map<String, WrkMast> wrkMastMap, List<String> skipMsgs) {
+ Map<String, Integer> reachCache = new HashMap<>();
+ List<WorkTaskParams> kept = new ArrayList<>();
+ for (WorkTaskParams p : accepted) {
+ if (!"out".equalsIgnoreCase(p.getType())) {
+ kept.add(p);
+ continue;
+ }
+ WrkMast w = wrkMastMap.get(p.getTaskNo());
+ String userNo = sortUserNoForPub(p, w);
+ Integer plt = sortPltForPub(p, w);
+ if (Cools.isEmpty(userNo) || plt == null || plt <= 0) {
+ kept.add(p);
+ continue;
+ }
+ int maxReach = reachCache.computeIfAbsent(userNo, wrkMastService::outboundSeqMaxContiguousPlt);
+ if (plt > maxReach) {
+ skipMsgs.add(buildTaskMsg(p, "鍑哄簱搴忓彿璺冲彿锛岃烦杩�"));
+ continue;
+ }
+ kept.add(p);
+ }
+ return kept;
+ }
+
+ private List<List<WorkTaskParams>> buildPubChunks(List<WorkTaskParams> accepted, Map<String, WrkMast> wrkMastMap) {
+ List<List<WorkTaskParams>> chunks = new ArrayList<>();
+ int index = 0;
+ while (index < accepted.size()) {
+ WorkTaskParams head = accepted.get(index);
+ WrkMast headMast = wrkMastMap.get(head.getTaskNo());
+ String headGroupKey = buildBatchGroupKey(head, headMast);
+ List<WorkTaskParams> chunk = new ArrayList<>();
+ while (index < accepted.size() && chunk.size() < WCS_PUB_BATCH_SIZE) {
+ WorkTaskParams cur = accepted.get(index);
+ WrkMast curMast = wrkMastMap.get(cur.getTaskNo());
+ if (!headGroupKey.equals(buildBatchGroupKey(cur, curMast))) {
+ break;
+ }
+ chunk.add(cur);
+ index++;
+ }
+ chunks.add(chunk);
+ }
+ return chunks;
+ }
+
+ private boolean sleepOneMinuteBeforeNextChunk() {
+ try {
+ TimeUnit.MINUTES.sleep(1);
+ return true;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ log.warn("鎵归噺涓嬪彂WCS缁勯棿绛夊緟琚腑鏂�", e);
+ return false;
+ }
+ }
+
+ /**
+ * 鍚屽崟涓嬩竴缁勶細浼樺厛 WCS queryTask锛涘け璐ユ垨鏃犳暟鎹垯涓昏〃宸查潪 11 鎴栧凡杩涘巻鍙茶〃銆�
+ */
+ private boolean sameOrderNextChunkAllowed(List<WorkTaskParams> lastSentChunk) {
+ if (lastSentChunk == null || lastSentChunk.isEmpty()) {
+ return false;
+ }
+ if (!Boolean.parseBoolean(String.valueOf(switchValue))) {
+ return true;
+ }
+ WorkTaskParams last = lastSentChunk.get(lastSentChunk.size() - 1);
+ if (last != null && !Cools.isEmpty(last.getTaskNo()) && wcsQueryTaskShowsTask(last.getTaskNo())) {
+ return true;
+ }
+ if (last != null && !Cools.isEmpty(last.getTaskNo())) {
+ log.info("WCS queryTask 鏃犳暟鎹垨澶辫触锛屽洖閫� WMS 涓昏〃/鍘嗗彶鏍¢獙, taskNo={}", last.getTaskNo());
+ }
+ return previousChunkTasksReleasedInWms(lastSentChunk);
+ }
+
+ private boolean wcsQueryTaskShowsTask(String taskNo) {
+ Map<String, Object> body = new HashMap<>();
+ body.put("taskNo", taskNo);
+ try {
+ String response = new HttpHandler.Builder()
+ .setUri(wcs_address)
+ .setPath(queryTaskPath)
+ .setHttps(wcs_address != null && wcs_address.startsWith("https://"))
+ .setTimeout(60, TimeUnit.SECONDS)
+ .setJson(JSON.toJSONString(body))
+ .build()
+ .doPost();
+ JSONObject jo = JSON.parseObject(response == null ? "{}" : response);
+ Integer code = jo.getInteger("code");
+ return code != null && code == 200 && queryTaskDataNonEmpty(jo.get("data"));
+ } catch (IOException e) {
+ log.warn("WCS queryTask 寮傚父, taskNo={}", taskNo, e);
+ return false;
+ }
+ }
+
+ /**
+ * 涓婁竴缁勬瘡鏉★細涓昏〃鏃犲垯鐪嬪巻鍙茶〃锛涗富琛ㄦ湁鍒� wrk_sts 涓嶈兘浠嶄负 11銆�
+ */
+ private boolean previousChunkTasksReleasedInWms(List<WorkTaskParams> chunk) {
+ for (WorkTaskParams p : chunk) {
+ if (p == null || Cools.isEmpty(p.getTaskNo())) {
+ return false;
+ }
+ Integer wrkNo = Integer.valueOf(p.getTaskNo());
+ WrkMast m = wrkMastService.selectById(wrkNo);
+ if (m == null) {
+ int logCnt = wrkMastLogService.selectCount(new EntityWrapper<WrkMastLog>().eq("wrk_no", wrkNo));
+ if (logCnt <= 0) {
+ return false;
+ }
+ } else if (m.getWrkSts() != null && Objects.equals(m.getWrkSts(), 11L)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 鍑哄簱姣忕粍涓嬪彂鍓嶏細鏈粍鏈夋湁鏁堟渶灏忓簭鍙蜂笖>1 鏃讹紝鍙牎楠屻�屾渶灏忓簭鍙�-1銆嶄竴妗o紱搴忓彿鍏ㄦ棤鍒欒烦杩囨湰鏉′欢銆�
+ */
+ private boolean outboundChunkPredecessorPltReady(List<WorkTaskParams> chunk, Map<String, WrkMast> wrkMastMap) {
+ if (chunk == null || chunk.isEmpty()) {
+ return true;
+ }
+ WorkTaskParams head = chunk.get(0);
+ if (!"out".equalsIgnoreCase(head.getType())) {
+ return true;
+ }
+ WrkMast headMast = wrkMastMap.get(head.getTaskNo());
+ String userNo = sortUserNoForPub(head, headMast);
+ if (Cools.isEmpty(userNo)) {
+ return true;
+ }
+ int minPlt = Integer.MAX_VALUE;
+ for (WorkTaskParams p : chunk) {
+ if (!"out".equalsIgnoreCase(p.getType())) {
+ continue;
+ }
+ Integer plt = sortPltForPub(p, wrkMastMap.get(p.getTaskNo()));
+ if (plt != null && plt > 0 && plt < minPlt) {
+ minPlt = plt;
+ }
+ }
+ if (minPlt == Integer.MAX_VALUE || minPlt <= 1) {
+ return true;
+ }
+ return outboundPltSlotReleasedInWms(userNo, minPlt - 1);
+ }
+
+ private boolean outboundPltSlotReleasedInWms(String userNo, int pltType) {
+ List<WrkMast> rows = wrkMastService.selectList(new EntityWrapper<WrkMast>()
+ .eq("user_no", userNo)
+ .eq("io_type", 101)
+ .eq("plt_type", pltType));
+ if (rows != null && !rows.isEmpty()) {
+ for (WrkMast m : rows) {
+ if (m != null && m.getWrkSts() != null && Objects.equals(m.getWrkSts(), 11L)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ int logCnt = wrkMastLogService.selectCount(new EntityWrapper<WrkMastLog>()
+ .eq("user_no", userNo)
+ .eq("io_type", 101)
+ .eq("plt_type", pltType));
+ return logCnt > 0;
+ }
+
+ private static boolean queryTaskDataNonEmpty(Object data) {
+ if (data == null) {
+ return false;
+ }
+ if (data instanceof JSONArray) {
+ return !((JSONArray) data).isEmpty();
+ }
+ if (data instanceof Collection) {
+ return !((Collection<?>) data).isEmpty();
+ }
+ if (data instanceof String) {
+ String s = (String) data;
+ if (Cools.isEmpty(s)) {
+ return false;
+ }
+ JSONArray arr = JSON.parseArray(s);
+ return arr != null && !arr.isEmpty();
+ }
+ return true;
+ }
+
+ private Comparator<WorkTaskParams> pubWcsSortComparator(Map<String, WrkMast> wrkMastMap) {
+ return Comparator
+ .comparing((WorkTaskParams p) -> Optional.ofNullable(p.getType()).orElse(""), String.CASE_INSENSITIVE_ORDER)
+ .thenComparing(p -> sortUserNoForPub(p, wrkMastMap.get(p.getTaskNo())), Comparator.nullsLast(String::compareTo))
+ .thenComparing(p -> sortPltForPub(p, wrkMastMap.get(p.getTaskNo())), Comparator.nullsLast(Integer::compareTo));
+ }
+
+ private static String sortUserNoForPub(WorkTaskParams p, WrkMast wrkMast) {
+ String userNo = wrkMast == null ? null : wrkMast.getUserNo();
+ if (Cools.isEmpty(userNo)) {
+ userNo = p.getBatch();
+ }
+ return Cools.isEmpty(userNo) ? null : userNo;
+ }
+
+ private static Integer sortPltForPub(WorkTaskParams p, WrkMast wrkMast) {
+ if (wrkMast != null && wrkMast.getPltType() != null) {
+ return wrkMast.getPltType();
+ }
+ return p.getBatchSeq();
}
/**
@@ -284,7 +571,7 @@
}
@Override
- public R syncDeviceStatusFromWcs() {
+ public R syncDeviceStatusFromWcs(boolean logOnFailure) {
if (!Boolean.parseBoolean(String.valueOf(switchValue))) {
return R.ok("WCS寮�鍏冲叧闂�");
}
@@ -311,7 +598,11 @@
log.info("鍚屾WCS璁惧鐘舵�佹垚鍔�, stationCount={}, crnCount={}", stationCount, crnCount);
return R.ok("鍚屾鎴愬姛").add(result);
} catch (Exception e) {
- log.error("鍚屾WCS璁惧鐘舵�佸紓甯�, response={}", response, e);
+ if (logOnFailure) {
+ log.error("鍚屾WCS璁惧鐘舵�佸紓甯�, response={}", response, e);
+ } else {
+ log.debug("鍚屾WCS璁惧鐘舵�佸紓甯�, response={}", response, e);
+ }
return R.error("鍚屾WCS璁惧鐘舵�佸け璐�: " + e.getMessage());
}
}
@@ -504,30 +795,48 @@
}
HashMap<String,Object> map = new HashMap<>();
map.put("taskList", params);
- String response;
+ String requestJson = JSON.toJSONString(map);
+ String response = null;
+ Throwable wcsThrown = null;
+ boolean wcsBizOk = false;
try {
- log.info("璋冪敤WCS鍙栨秷鍑哄簱浠诲姟, request={}", JSON.toJSONString(params));
+ log.info("璋冪敤WCS鍙栨秷鍑哄簱浠诲姟, request={}", requestJson);
response = new HttpHandler.Builder()
.setUri(wcs_address)
.setPath(stopOutTask)
// .setHttps(wcs_address != null && wcs_address.startsWith("https://"))
.setTimeout(10, TimeUnit.SECONDS)
- .setJson(JSON.toJSONString(map))
+ .setJson(requestJson)
.build()
.doPost();
JSONObject jsonObject = JSON.parseObject(response == null ? "{}" : response);
log.info("WCS鍙栨秷鍑哄簱浠诲姟杩斿洖, response={}", response);
Integer code = jsonObject.getInteger("code");
- if (code == null || !Objects.equals(code, 200)) {
+ wcsBizOk = code != null && Objects.equals(code, 200);
+ if (!wcsBizOk) {
String msg = jsonObject.getString("msg");
throw new CoolException(Cools.isEmpty(msg) ? "WCS鍙栨秷鍑哄簱浠诲姟澶辫触" : msg);
}
return R.ok(Cools.isEmpty(jsonObject.getString("msg")) ? "鎿嶄綔鎴愬姛" : jsonObject.getString("msg"));
} catch (IOException e) {
+ wcsThrown = e;
throw new CoolException("璋冪敤WCS鍙栨秷鍑哄簱浠诲姟澶辫触: " + e.getMessage());
+ } finally {
+ logWcsToApiLog(stopOutTask, requestJson, response, wcsThrown, wcsBizOk);
}
}
+ private void logWcsToApiLog(String path, String requestJson, String response, Throwable thrown, boolean wcsBizOk) {
+ String fullUrl = (wcs_address == null ? "" : wcs_address) + (path == null ? "" : path);
+ boolean success = thrown == null && wcsBizOk;
+ String resp = response == null ? "" : response;
+ if (thrown != null && Cools.isEmpty(resp)) {
+ resp = thrown.getMessage() == null ? "" : thrown.getMessage();
+ }
+ apiLogService.save(NS_WMS_TO_WCS, fullUrl, "-", "-",
+ requestJson == null ? "" : requestJson, resp, success);
+ }
+
private String requestDeviceStatusFromWcs() throws IOException {
HttpHandler.Builder builder = new HttpHandler.Builder()
.setUri(wcs_address)
diff --git a/src/main/java/com/zy/asrs/controller/OpenController.java b/src/main/java/com/zy/asrs/controller/OpenController.java
index ca3fb5e..14571f4 100644
--- a/src/main/java/com/zy/asrs/controller/OpenController.java
+++ b/src/main/java/com/zy/asrs/controller/OpenController.java
@@ -57,6 +57,8 @@
private MatService matService;
@Autowired
+ private LocMastService locMastService;
+ @Autowired
private ReportQueryMapper reportQueryMapper;
// @PostMapping("/order/matSync/default/v1")
//// @AppAuth(memo = "鍟嗗搧淇℃伅鍚屾鎺ュ彛")
@@ -486,6 +488,38 @@
}
orderIds.add(outTaskParam.getOrderId());
}
+
+ Map<String, List<OutTaskParam>> linesByOrder = new LinkedHashMap<>();
+ for (OutTaskParam outTaskParam : params) {
+ linesByOrder.computeIfAbsent(outTaskParam.getOrderId(), k -> new ArrayList<>()).add(outTaskParam);
+ }
+ for (Map.Entry<String, List<OutTaskParam>> entry : linesByOrder.entrySet()) {
+ String oid = entry.getKey();
+ List<OutTaskParam> lines = entry.getValue();
+ List<Integer> seqs = new ArrayList<>(lines.size());
+ for (OutTaskParam line : lines) {
+ if (line.getSeq() == null) {
+ return R.error("鍑哄簱鍗曘��" + oid + "銆嶅簭鍙蜂笉鑳戒负绌�");
+ }
+ seqs.add(line.getSeq());
+ }
+ Collections.sort(seqs);
+ for (int i = 0; i < seqs.size(); i++) {
+ if (!String.valueOf(seqs.get(i)).equals(String.valueOf(i + 1))) {
+ return R.error("鍑哄簱鍗曘��" + oid + "銆嶅簭鍙蜂笉杩炵画");
+ }
+ }
+ }
+
+ Set<String> seenPallet = new LinkedHashSet<>();
+ for (OutTaskParam outTaskParam : params) {
+ String pid = outTaskParam.getPalletId();
+ String palletKey = pid == null ? "" : pid;
+ if (!seenPallet.add(palletKey)) {
+ return R.error("鎵樼洏鍙烽噸澶嶏細" + (Cools.isEmpty(pid) ? "锛堢┖锛�" : pid));
+ }
+ }
+
// if (!orderIds.isEmpty()) {
// Set<String> existedOrderIds = new LinkedHashSet<>();
// List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>().in("user_no", orderIds));
@@ -518,9 +552,9 @@
}
for (OutTaskParam outTaskParam : validOutOrders) {
- R r = openService.outOrder(outTaskParam,validOutOrders.size());
- if (!r.get("code").equals(200)){
- return r;
+ LocMast locMast = locMastService.selectOne(new EntityWrapper<LocMast>().eq("loc_sts", "F").eq("barcode", outTaskParam.getPalletId()));
+ if (locMast == null) {
+ throw new CoolException("娌℃湁鎵惧埌鎵樼洏鐮�=" + outTaskParam.getPalletId() + "瀵瑰簲鐨勫簱浣�");
}
}
diff --git a/src/main/java/com/zy/asrs/mapper/WrkMastLogMapper.java b/src/main/java/com/zy/asrs/mapper/WrkMastLogMapper.java
index 27eaca7..7afe961 100644
--- a/src/main/java/com/zy/asrs/mapper/WrkMastLogMapper.java
+++ b/src/main/java/com/zy/asrs/mapper/WrkMastLogMapper.java
@@ -6,6 +6,7 @@
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import java.util.List;
@@ -19,6 +20,12 @@
int save(Integer workNo);
/**
+ * 鍑哄簱 101 涓嬭鍗曞彿宸插嚭鐜扮殑 plt_type锛堝巻鍙茶〃锛�
+ */
+ @Select("SELECT plt_type FROM asr_wrk_mast_log WHERE io_type = 101 AND plt_type IS NOT NULL AND plt_type > 0 AND user_no = #{userNo}")
+ List<Integer> listOutboundPltTypesByUserNo(@Param("userNo") String userNo);
+
+ /**
* 鏌ヨ搴撳瓨绉诲姩娴佹按璁板綍
*/
List<InventoryFlowDto> inventoryFlowList(@Param("curr") Integer curr,@Param("limit") Integer limit, @Param("param") Map<String, Object> param);
diff --git a/src/main/java/com/zy/asrs/mapper/WrkMastMapper.java b/src/main/java/com/zy/asrs/mapper/WrkMastMapper.java
index f593fa3..9eeac0e 100644
--- a/src/main/java/com/zy/asrs/mapper/WrkMastMapper.java
+++ b/src/main/java/com/zy/asrs/mapper/WrkMastMapper.java
@@ -27,4 +27,10 @@
, @Param("standby1")String standby1, @Param("standby2")String standby2, @Param("standby3")String standby3
, @Param("boxType1")String boxType1, @Param("boxType2")String boxType2, @Param("boxType3")String boxType3, @Param("crnNo") Integer crnNo);
+ /**
+ * 鍑哄簱 101 涓嬭鍗曞彿宸插嚭鐜扮殑 plt_type锛堜富琛級
+ */
+ @Select("SELECT plt_type FROM asr_wrk_mast WHERE io_type = 101 AND plt_type IS NOT NULL AND plt_type > 0 AND user_no = #{userNo}")
+ List<Integer> listOutboundPltTypesByUserNo(@Param("userNo") String userNo);
+
}
diff --git a/src/main/java/com/zy/asrs/service/WrkMastService.java b/src/main/java/com/zy/asrs/service/WrkMastService.java
index 258d5dc..030e590 100644
--- a/src/main/java/com/zy/asrs/service/WrkMastService.java
+++ b/src/main/java/com/zy/asrs/service/WrkMastService.java
@@ -29,4 +29,9 @@
WrkMast selectWrkMast(Integer workNo,String barcode);
+ /**
+ * 鍑哄簱(io_type=101)鍚屼竴 user_no锛氫富琛ㄤ笌鍘嗗彶琛ㄥ苟闆嗕笅锛岃嚜 1 璧疯繛缁瓨鍦ㄧ殑鏈�澶� plt_type銆�
+ */
+ int outboundSeqMaxContiguousPlt(String userNo);
+
}
diff --git a/src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java b/src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
index 13256d9..36dae29 100644
--- a/src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
+++ b/src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
@@ -33,7 +33,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import javax.rmi.CORBA.Util;
import java.util.*;
import java.util.stream.Collectors;
@@ -428,6 +427,10 @@
for (WrkMast wrkMast : activeTasks) {
HashMap<String,Object> hashMap = new HashMap<>();
hashMap.put("taskNo", wrkMast.getWrkNo());
+ if (!Cools.isEmpty(wrkMast) && wrkMast.getWrkSts() ==11L) {
+ workService.cancelWrkMast(wrkMast.getWrkNo()+"", 9955L);
+ continue;
+ }
taskList.add(hashMap);
}
wcsApiService.pauseOutTasks(taskList);
@@ -1300,7 +1303,7 @@
public R outOrder(OutTaskParam param,int count) {
LocMast locMast = locMastService.selectOne(new EntityWrapper<LocMast>().eq("loc_sts", "F").eq("barcode", param.getPalletId()));
if (locMast == null) {
- return R.error("娌℃湁鎵惧埌鎵樼洏鐮�=" + param.getPalletId() + "瀵瑰簲鐨勫簱浣�");
+ throw new CoolException("娌℃湁鎵惧埌鎵樼洏鐮�=" + param.getPalletId() + "瀵瑰簲鐨勫簱浣�");
}
Integer ioType = 101;
// 鑾峰彇璺緞
diff --git a/src/main/java/com/zy/asrs/service/impl/WrkMastServiceImpl.java b/src/main/java/com/zy/asrs/service/impl/WrkMastServiceImpl.java
index 9911553..82374e7 100644
--- a/src/main/java/com/zy/asrs/service/impl/WrkMastServiceImpl.java
+++ b/src/main/java/com/zy/asrs/service/impl/WrkMastServiceImpl.java
@@ -5,16 +5,23 @@
import com.core.common.Cools;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.entity.result.FindLocNoAttributeVo;
+import com.zy.asrs.mapper.WrkMastLogMapper;
import com.zy.asrs.mapper.WrkMastMapper;
import com.zy.asrs.service.WrkMastService;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
@Slf4j
@Service("wrkMastService")
public class WrkMastServiceImpl extends ServiceImpl<WrkMastMapper, WrkMast> implements WrkMastService {
+
+ @Autowired
+ private WrkMastLogMapper wrkMastLogMapper;
@Override
public int getWorkingMast(Integer devpNo) {
@@ -63,4 +70,29 @@
public WrkMast selectWrkMast(Integer workNo, String barcode) {
return this.baseMapper.selectWrkMast(workNo, barcode);
}
+
+ @Override
+ public int outboundSeqMaxContiguousPlt(String userNo) {
+ List<Integer> fromMast = baseMapper.listOutboundPltTypesByUserNo(userNo);
+ List<Integer> fromLog = wrkMastLogMapper.listOutboundPltTypesByUserNo(userNo);
+ Set<Integer> filled = new HashSet<>();
+ addPositivePlt(fromMast, filled);
+ addPositivePlt(fromLog, filled);
+ int h = 0;
+ while (filled.contains(h + 1)) {
+ h++;
+ }
+ return h;
+ }
+
+ private static void addPositivePlt(List<Integer> list, Set<Integer> target) {
+ if (list == null) {
+ return;
+ }
+ for (Integer p : list) {
+ if (p != null && p > 0) {
+ target.add(p);
+ }
+ }
+ }
}
diff --git a/src/main/java/com/zy/asrs/task/WcsDeviceStatusScheduler.java b/src/main/java/com/zy/asrs/task/WcsDeviceStatusScheduler.java
index 901adb0..db6497d 100644
--- a/src/main/java/com/zy/asrs/task/WcsDeviceStatusScheduler.java
+++ b/src/main/java/com/zy/asrs/task/WcsDeviceStatusScheduler.java
@@ -20,6 +20,9 @@
@Value("${wcs.status-sync.enabled:true}")
private Boolean enabled;
+ @Value("${wcs.status-sync.log-on-failure:true}")
+ private Boolean logOnFailure;
+
@Scheduled(
initialDelayString = "${wcs.status-sync.initial-delay:10000}",
fixedDelayString = "${wcs.status-sync.fixed-delay:5000}"
@@ -28,8 +31,8 @@
if (!Boolean.TRUE.equals(enabled)) {
return;
}
- R result = wcsApiService.syncDeviceStatusFromWcs();
- if (!Objects.equals(result.get("code"), 200)) {
+ R result = wcsApiService.syncDeviceStatusFromWcs(Boolean.TRUE.equals(logOnFailure));
+ if (Boolean.TRUE.equals(logOnFailure) && !Objects.equals(result.get("code"), 200)) {
log.warn("杞鍚屾WCS璁惧鐘舵�佸け璐�, result={}", result);
}
}
diff --git a/src/main/java/com/zy/asrs/task/WorkMastScheduler.java b/src/main/java/com/zy/asrs/task/WorkMastScheduler.java
index b61a4aa..5af485f 100644
--- a/src/main/java/com/zy/asrs/task/WorkMastScheduler.java
+++ b/src/main/java/com/zy/asrs/task/WorkMastScheduler.java
@@ -5,7 +5,6 @@
import com.core.common.R;
import com.zy.api.controller.params.WorkTaskParams;
import com.zy.api.service.WcsApiService;
-import com.zy.asrs.entity.LocMast;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.service.WrkMastService;
import com.zy.asrs.task.core.ReturnT;
@@ -19,7 +18,6 @@
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -70,20 +68,20 @@
* @author Ryan
* @date 2026/1/10 14:42
*/
- @Scheduled(cron = "0/3 * * * * ? ")
+ @Scheduled(cron = "0/10 * * * * ? ")
private void autoPubTasks() {
// 浠呭鐞嗗緟涓嬪彂/宸茬敓鎴愪笅鍙戝彿鐨勫伐浣滄。銆�
- List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>().in("wrk_sts", Arrays.asList(1L, 11L)));
+ List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>().in("wrk_sts", Arrays.asList(1L, 11L))
+ .orderBy("user_no", true)
+ .orderBy("plt_type", true));
if (wrkMasts.isEmpty()) {
return;
}
- // 鎵撴暎椤哄簭锛岄伩鍏嶅浐瀹氭帓搴忎笅鍚屼竴鎵逛换鍔¢暱鏈熷崰鐢ㄨ皟搴︽満浼氥��
- Collections.shuffle(wrkMasts);
List<WorkTaskParams> paramsList = new ArrayList<>();
for (WrkMast wrkMast : wrkMasts) {
// 鍑哄簱绫讳换鍔★紙ioType > 100锛夐粯璁ら渶瑕� ERP 纭锛涙湭纭鐨勪换鍔″湪杩欓噷鐩存帴璺宠繃銆�
- if (wrkMast.getIoType()>100&& !wrkMast.getPdcType().equals("Y")) {
+ if (wrkMast.getIoType() > 100 && !"Y".equalsIgnoreCase(wrkMast.getPdcType())) {
continue;
}
@@ -103,7 +101,7 @@
.setBatchSeq(wrkMast.getPltType())
.setBarcode(wrkMast.getBarcode());
// 2: 鍏ュ簱銆傚叆搴撴帴鍙d娇鐢� sourceStaNo + 鐩爣搴撲綅銆�
- }else if(wrkMast.getIoType()==2&& !Cools.isEmpty(wrkMast.getSourceStaNo())){
+ } else if (wrkMast.getIoType() == 2 && !Cools.isEmpty(wrkMast.getSourceStaNo())) {
params.setType("in")
.setTaskNo(wrkMast.getWrkNo()+"")
.setSourceStaNo(String.valueOf(wrkMast.getSourceStaNo()))
diff --git a/src/main/java/com/zy/integration/iot/client/impl/PahoIotMqttClient.java b/src/main/java/com/zy/integration/iot/client/impl/PahoIotMqttClient.java
index 231666b..21b7f8d 100644
--- a/src/main/java/com/zy/integration/iot/client/impl/PahoIotMqttClient.java
+++ b/src/main/java/com/zy/integration/iot/client/impl/PahoIotMqttClient.java
@@ -6,6 +6,7 @@
import com.zy.integration.iot.util.IotMqttSslUtils;
import com.zy.iot.config.IotProperties;
import com.zy.iot.entity.IotTopicConfig;
+import com.zy.iot.service.IotDbConfigService;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
@@ -33,13 +34,15 @@
@Autowired
private IotProperties iotProperties;
@Autowired
+ private IotDbConfigService iotDbConfigService;
+ @Autowired
private IotInboundMessageHandler inboundMessageHandler;
private volatile MqttClient mqttClient;
@PostConstruct
public void init() {
- if (!iotProperties.isEnabled()) {
+ if (!iotDbConfigService.isMqttEnabled()) {
return;
}
try {
@@ -68,8 +71,33 @@
* 鎳掑姞杞藉缓杩烇紝棣栨鍙戦�佹垨鍚姩鏃惰嚜鍔ㄨ繛鎺ュ苟琛ヨ闃呬富棰樸��
*/
@Override
+ public synchronized void reconnectFromDbConfig() {
+ iotDbConfigService.refreshCache();
+ try {
+ if (mqttClient != null) {
+ try {
+ if (mqttClient.isConnected()) {
+ mqttClient.disconnect();
+ }
+ mqttClient.close();
+ } catch (Exception e) {
+ log.warn("IoT MQTT disconnect before reconnect", e);
+ }
+ mqttClient = null;
+ }
+ if (!iotDbConfigService.isMqttEnabled()) {
+ log.info("IoT MQTT 宸茬敱閰嶇疆鍏抽棴锛屾湭寤虹珛杩炴帴");
+ return;
+ }
+ ensureConnected();
+ } catch (Exception e) {
+ log.error("IoT MQTT 閲嶈繛澶辫触", e);
+ }
+ }
+
+ @Override
public synchronized void ensureConnected() throws Exception {
- if (!iotProperties.isEnabled()) {
+ if (!iotDbConfigService.isMqttEnabled()) {
return;
}
if (!hasRequiredConfig()) {
@@ -84,6 +112,8 @@
}
mqttClient.connect(buildOptions());
subscribeTopics();
+ log.info("IoT MQTT 宸茶繛鎺� serverURI={} clientId={} tls={}",
+ iotProperties.getServerUri(), iotProperties.getResolvedClientId(), iotProperties.isTlsEnabled());
}
/**
@@ -91,7 +121,7 @@
*/
@Override
public void publish(String topic, String payload) throws Exception {
- if (!iotProperties.isEnabled() || Cools.isEmpty(topic) || payload == null) {
+ if (!iotDbConfigService.isMqttEnabled() || Cools.isEmpty(topic) || payload == null) {
return;
}
ensureConnected();
@@ -101,6 +131,7 @@
MqttMessage mqttMessage = new MqttMessage(payload.getBytes(StandardCharsets.UTF_8));
mqttMessage.setQos(1);
mqttMessage.setRetained(false);
+ log.info("IoT MQTT 鍙戦�� topic={} payload={}", topic, payload);
mqttClient.publish(topic, mqttMessage);
}
@@ -135,7 +166,9 @@
@Override
public void messageArrived(String topic, MqttMessage message) {
- inboundMessageHandler.handleMessage(topic, new String(message.getPayload(), StandardCharsets.UTF_8));
+ String body = new String(message.getPayload(), StandardCharsets.UTF_8);
+ log.info("IoT MQTT 鎺ユ敹 topic={} payload={}", topic, body);
+ inboundMessageHandler.handleMessage(topic, body);
}
@Override
@@ -146,7 +179,10 @@
public void connectComplete(boolean reconnect, String serverURI) {
try {
subscribeTopics();
- log.info("iot mqtt connected, reconnect={}, serverURI={}", reconnect, serverURI);
+ if (reconnect) {
+ log.info("IoT MQTT 鑷姩閲嶈繛鎴愬姛 serverURI={} clientId={}",
+ serverURI, iotProperties.getResolvedClientId());
+ }
} catch (Exception e) {
log.error("subscribe iot topics failed", e);
}
@@ -178,7 +214,7 @@
if (mqttClient == null || !mqttClient.isConnected()) {
return;
}
- IotTopicConfig topics = iotProperties.getTopics();
+ IotTopicConfig topics = iotDbConfigService.getEffectiveTopics();
subscribe(topics.getEgressStow());
subscribe(topics.getEgressPick());
subscribe(topics.getEgressFeedback());
--
Gitblit v1.9.1