自动化立体仓库 - WMS系统
src/main/java/com/zy/asrs/task/WorkMastScheduler.java
@@ -12,6 +12,7 @@
import com.zy.asrs.task.support.OutboundBatchSeqReleaseGuard;
import com.zy.asrs.task.support.WorkPublishLockKeys;
import com.zy.asrs.utils.Utils;
import com.zy.common.properties.SlaveProperties;
import com.zy.common.utils.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -37,6 +38,7 @@
    private static final Logger log = LoggerFactory.getLogger(WorkMastScheduler.class);
    private static final int MAX_PUBLISH_TASKS_ONCE = 20;
    private static final long OUTBOUND_USER_NO_LOCK_SECONDS = 60L;
    private static final int DOUBLE_EXTENSION_OUTBOUND_CRN_NO = 19;
    @Autowired
    private WcsApiService wcsApiService;
@@ -48,6 +50,14 @@
    private OutboundBatchSeqReleaseGuard outboundBatchSeqReleaseGuard;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private SlaveProperties slaveProperties;
    private enum LocDepthType {
        SHALLOW,
        DEEP,
        UNKNOWN
    }
    @Scheduled(cron = "0/3 * * * * ? ")
    private void execute(){
@@ -95,7 +105,7 @@
        List<WorkTaskParams> paramsList = new ArrayList<>();
        List<WorkTaskParams> moveParamsList = new ArrayList<>();
        Map<String, LinkedHashMap<String, List<WorkTaskParams>>> outboundTasksByUserNo = new LinkedHashMap<>();
        Map<String, LinkedHashMap<String, List<WrkMast>>> outboundTasksByUserNo = new LinkedHashMap<>();
        for (WrkMast wrkMast : wrkMasts) {
            // 出库类任务(ioType > 100)默认需要 ERP 确认;未确认的任务在这里直接跳过。
            if (wrkMast.getIoType() > 100 && !"Y".equalsIgnoreCase(wrkMast.getPdcType())) {
@@ -108,13 +118,12 @@
                            wrkMast.getWrkNo(), wrkMast.getUserNo());
                    continue;
                }
                WorkTaskParams params = buildWorkTaskParams(wrkMast);
                String userNo = normalizeGroupKey(wrkMast.getUserNo());
                String batchSeq = normalizeGroupKey(wrkMast.getBatchSeq());
                outboundTasksByUserNo
                        .computeIfAbsent(userNo, key -> new LinkedHashMap<>())
                        .computeIfAbsent(batchSeq, key -> new ArrayList<>())
                        .add(params);
                        .add(wrkMast);
            } else {
                WorkTaskParams params = buildWorkTaskParams(wrkMast);
                if (isMovePublishTask(params)) {
@@ -137,7 +146,7 @@
            return;
        }
        for (Map.Entry<String, LinkedHashMap<String, List<WorkTaskParams>>> userEntry : outboundTasksByUserNo.entrySet()) {
        for (Map.Entry<String, LinkedHashMap<String, List<WrkMast>>> userEntry : outboundTasksByUserNo.entrySet()) {
            String userNo = userEntry.getKey();
            List<String> batchSeqs = new ArrayList<>(userEntry.getValue().keySet());
            batchSeqs.sort(this::compareBatchSeqNatural);
@@ -149,16 +158,28 @@
                    break;
                }
                List<WorkTaskParams> batchParams = userEntry.getValue().get(batchSeq);
                if (batchParams == null || batchParams.isEmpty()) {
                List<WrkMast> batchMasts = selectOutboundBatchMasts(userEntry.getValue().get(batchSeq));
                if (batchMasts == null || batchMasts.isEmpty()) {
                    continue;
                }
                List<WorkTaskParams> batchParams = buildWorkTaskParams(batchMasts);
                if (publishOutboundTaskChunks(userNo, batchSeq, batchParams)) {
                    return;
                }
            }
        }
    }
    private List<WorkTaskParams> buildWorkTaskParams(List<WrkMast> wrkMasts) {
        List<WorkTaskParams> paramsList = new ArrayList<>();
        if (wrkMasts == null || wrkMasts.isEmpty()) {
            return paramsList;
        }
        for (WrkMast wrkMast : wrkMasts) {
            paramsList.add(buildWorkTaskParams(wrkMast));
        }
        return paramsList;
    }
    private WorkTaskParams buildWorkTaskParams(WrkMast wrkMast) {
@@ -206,6 +227,109 @@
        return params != null && "move".equalsIgnoreCase(params.getType());
    }
    private List<WrkMast> selectOutboundBatchMasts(List<WrkMast> batchMasts) {
        if (batchMasts == null || batchMasts.isEmpty()) {
            return new ArrayList<>();
        }
        List<WrkMast> ordered = new ArrayList<>(batchMasts);
        if (hasOrderedOutboundSeq(ordered)) {
            ordered.sort(this::compareOutboundSeq);
            return ordered;
        }
        if (!hasDoubleExtensionOutboundTask(ordered)) {
            return ordered;
        }
        List<WrkMast> shallowMasts = new ArrayList<>();
        List<WrkMast> deepMasts = new ArrayList<>();
        List<WrkMast> otherMasts = new ArrayList<>();
        for (WrkMast mast : ordered) {
            if (!isDoubleExtensionOutboundTask(mast)) {
                otherMasts.add(mast);
                continue;
            }
            LocDepthType locDepthType = resolveOutboundLocDepth(mast);
            if (locDepthType == LocDepthType.SHALLOW) {
                shallowMasts.add(mast);
            } else if (locDepthType == LocDepthType.DEEP) {
                deepMasts.add(mast);
            } else {
                otherMasts.add(mast);
            }
        }
        if (shallowMasts.isEmpty() && deepMasts.isEmpty()) {
            return ordered;
        }
        List<WrkMast> result = new ArrayList<>(ordered.size());
        result.addAll(shallowMasts);
        result.addAll(deepMasts);
        result.addAll(otherMasts);
        return result;
    }
    private boolean hasOrderedOutboundSeq(List<WrkMast> batchMasts) {
        for (WrkMast mast : batchMasts) {
            if (outboundSeq(mast) > 0) {
                return true;
            }
        }
        return false;
    }
    private int compareOutboundSeq(WrkMast left, WrkMast right) {
        int leftSeq = outboundSeq(left);
        int rightSeq = outboundSeq(right);
        boolean leftOrdered = leftSeq > 0;
        boolean rightOrdered = rightSeq > 0;
        if (leftOrdered && rightOrdered) {
            return Integer.compare(leftSeq, rightSeq);
        }
        if (leftOrdered) {
            return -1;
        }
        if (rightOrdered) {
            return 1;
        }
        return 0;
    }
    private int outboundSeq(WrkMast mast) {
        return mast == null || mast.getPltType() == null ? 0 : mast.getPltType();
    }
    private boolean hasDoubleExtensionOutboundTask(List<WrkMast> batchMasts) {
        for (WrkMast mast : batchMasts) {
            if (isDoubleExtensionOutboundTask(mast)) {
                return true;
            }
        }
        return false;
    }
    private boolean isDoubleExtensionOutboundTask(WrkMast mast) {
        return isOutboundPublishTask(mast)
                && mast.getCrnNo() != null
                && mast.getCrnNo() >= DOUBLE_EXTENSION_OUTBOUND_CRN_NO;
    }
    private LocDepthType resolveOutboundLocDepth(WrkMast mast) {
        if (mast == null || Cools.isEmpty(mast.getSourceLocNo()) || slaveProperties == null) {
            return LocDepthType.UNKNOWN;
        }
        try {
            if (Utils.isShallowLoc(slaveProperties, mast.getSourceLocNo())) {
                return LocDepthType.SHALLOW;
            }
            if (Utils.isDeepLoc(slaveProperties, mast.getSourceLocNo())) {
                return LocDepthType.DEEP;
            }
        } catch (Exception e) {
            log.warn("双伸出库库位深浅判断失败, wrkNo={}, sourceLocNo={}", mast.getWrkNo(), mast.getSourceLocNo(), e);
        }
        return LocDepthType.UNKNOWN;
    }
    private int compareBatchSeqNatural(String left, String right) {
        String safeLeft = Cools.isEmpty(left) ? "" : left;
        String safeRight = Cools.isEmpty(right) ? "" : right;