自动化立体仓库 - WMS系统
#
zwl
23 小时以前 7aba2103c4b3ea72e8e0fd79dc88be0e251c1c33
src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
@@ -87,6 +87,10 @@
    private String mesUrl;
    @Value("${mes.stationaddress}")
    private String stationAddress;
    @Value("${erp.address.URL:}")
    private String erpUrl;
    @Value("${erp.address.OutErroraddress:}")
    private String erpOutErrorAddress;
    @Autowired
    private WaitPakinService waitPakinService;
    @Autowired
@@ -417,7 +421,7 @@
            return R.ok(confirmedCount == 0 ? "任务已确认执行" : "ERP确认执行出库成功").add(result);
        }
        if (Objects.equals(param.getExecute(), 2)) {
            // ERP请求取消任务:按 plt_type 从大到小先 WCS 再 WMS;失败或异常则停止后续,接口仍返回原成功结构。
            // ERP请求取消任务:直接收集任务号,按 taskList 格式发送给 WCS。
            Map<String, Object> result = new HashMap<>();
            result.put("orderNo", param.getOrderId());
            result.put("execute", param.getExecute());
@@ -425,26 +429,17 @@
            if (activeTasks.isEmpty()) {
                return R.ok("无有效出库任务").add(result);
            }
            List<WrkMast> sorted = new ArrayList<>(activeTasks);
            sorted.sort(Comparator.comparing(WrkMast::getPltType, Comparator.nullsLast(Comparator.reverseOrder())));
            for (WrkMast wrkMast : sorted) {
                try {
                    if (!Cools.isEmpty(wrkMast) && wrkMast.getWrkSts() == 11L) {
                        workService.cancelWrkMast(wrkMast.getWrkNo() + "", 9955L);
                    }else{
                    HashMap<String, Object> hashMap = new HashMap<>();
                    hashMap.put("taskNo", wrkMast.getWrkNo());
                    List<HashMap<String, Object>> one = new ArrayList<>();
                    one.add(hashMap);
                    R wcsR = wcsApiService.pauseOutTasks(one);
                    requireWcsPauseOk(wcsR);
                    workService.cancelWrkMast(wrkMast.getWrkNo() + "", 9955L);
                    }
                } catch (Exception e) {
                    log.warn("[pakoutOrderPause] execute=2 取消中止, orderNo={}, err={}", param.getOrderId(), e.getMessage());
                    break;
            List<HashMap<String,Object>> taskList = new ArrayList<>();
            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);
            return R.ok("取消任务已发送至WCS").add(result);
        }
        throw new CoolException("reason仅支持1或2");
@@ -1307,6 +1302,9 @@
        return mesUrl + stationAddress;
    }
    /**
     * 7.3 组托信息下发。
     */
    @Override
    public R mesToComb(MesToCombParam param) {
        if (Cools.isEmpty(param.getPalletId())) {
@@ -1344,10 +1342,16 @@
        waitPakin.setAppeTime(now);
        waitPakin.setModiUser(9995L);
        waitPakin.setModiTime(now);
        waitPakin.setOrderNo(String.valueOf(param.getOrderId()));
        waitPakin.setOrderNo(String.valueOf(param.getBizNo()));
        waitPakin.setOrigin(String.valueOf(param.getStorageArea()));//建议入库区域
        waitPakin.setManu(String.valueOf(param.getLocId()));//mes具体库位编号
        waitPakin.setThreeCode(param.getBizNo());
        // 7.3 新增字段复用现有组托档承载位,不新增列。
        waitPakin.setSuppCode(param.getCustomerId());
        waitPakin.setSupp(param.getCustomerName());
        waitPakin.setStandby3(param.getItem());
        waitPakin.setStandby1(param.getEntryWmsCode());
        waitPakin.setStandby2(param.getOutDoorNo());
        waitPakin.setBeBatch(param.getPackage1());//是否散货,0 非散货;1 散货;为了管控出货速率,散货可以出慢点。
        // ERP 入口默认打 erp,MQTT 组托会在参数里显式传 aws。
        waitPakin.setBoxType1(Cools.isEmpty(param.getBoxType1()) ? "erp" : param.getBoxType1());
@@ -1357,15 +1361,36 @@
        return R.ok().add(Cools.add("palletId", param.getPalletId()).add("orderId", param.getOrderId()));
    }
    /**
     * 7.11 出库通知单(传递有序无序规则)单条建单。
     */
    @Override
    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) {
            throw new CoolException("没有找到托盘码=" + param.getPalletId() + "对应的库位");
        }
        if (Cools.isEmpty(param.getStationId())) {
            throw new CoolException("出库口编码不能为空");
        }
        if (Cools.isEmpty(param.getBatchSeq())) {
            throw new CoolException("批次标识不能为空");
        }
        if (param.getSeq() == null || param.getSeq() < 0) {
            throw new CoolException("出库顺序不能为空且不能小于0");
        }
        Integer ioType = 101;
        // 获取路径
        StaDesc staDesc = staDescService.queryCrnStn(ioType, locMast.getCrnNo(), Integer.valueOf(param.getStationId()));
        Integer stationId;
        try {
            stationId = Integer.valueOf(param.getStationId());
        } catch (NumberFormatException ex) {
            throw new CoolException("出库口编码格式错误:" + param.getStationId());
        }
        StaDesc staDesc = staDescService.queryCrnStn(ioType, locMast.getCrnNo(), stationId);
        if (staDesc == null) {
            throw new CoolException("未找到出库口=" + param.getStationId() + "对应路径");
        }
        Date now = new Date();
        // 生成工作号
        int workNo = commonService.getWorkNo(WorkNoType.getWorkNoType(ioType));
@@ -1387,7 +1412,9 @@
        wrkMast.setEmptyMk("N"); // 空板
        wrkMast.setLinkMis("N");
        wrkMast.setPdcType("N");
        // 7.11:orderId 存 userNo,batchSeq 存批次标识,seq 存批次内顺序。
        wrkMast.setUserNo(param.getOrderId());//订单号
        wrkMast.setBatchSeq(param.getBatchSeq());//订单内批次标识
        wrkMast.setPltType(param.getSeq());//出库顺序,从1开始
        wrkMast.setTakeNone("0");  //0非自动
        wrkMast.setAppeUser(9995L); // 操作人员数据
@@ -1414,6 +1441,9 @@
            wrkDetl.setAppeUser(9995L);
            wrkDetl.setModiTime(now);
            wrkDetl.setModiUser(9995L);
            // 7.11:entryWmsCode、outDoorNo 复用明细备用字段。
            wrkDetl.setStandby1(param.getEntryWmsCode());
            wrkDetl.setStandby2(param.getOutDoorNo());
            wrkDetl.setSupp(param.getSeq()+"/"+count);
            if (!wrkDetlService.insert(wrkDetl)) {
@@ -1436,18 +1466,217 @@
        return R.ok().add(Cools.add("wrkNo", workNo).add("orderId", param.getOrderId()));
    }
    /**
     * 7.11 出库通知单(传递有序无序规则)批量建单。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R outOrderBatch(List<OutTaskParam> params) {
        int n = params.size();
        Map<String, Integer> batchLineCounts = new HashMap<>();
        for (OutTaskParam outTaskParam : params) {
            R r = outOrder(outTaskParam, n);
            batchLineCounts.merge(buildOutOrderBatchKey(outTaskParam), 1, Integer::sum);
        }
        for (OutTaskParam outTaskParam : params) {
            int count = batchLineCounts.getOrDefault(buildOutOrderBatchKey(outTaskParam), n);
            R r = outOrder(outTaskParam, count);
            if (!Objects.equals(r.get("code"), 200)) {
                throw new CoolException("出库建单失败");
            }
        }
        return R.ok();
    }
    /**
     * 7.9 出库异常变动上报。
     * WCS 只传 palletId、errorMsg,WMS 解析 orderId 后转发 ERP。
     */
    @Override
    public R outOrderAbnormalReport(OutOrderAbnormalReportParam param) {
        if (param == null || Cools.isEmpty(param.getPalletId())) {
            return R.error("palletId不能为空");
        }
        String orderId = resolveOutboundOrderId(param.getPalletId());
        if (Cools.isEmpty(orderId)) {
            return R.error("未找到托盘对应出库单号:" + param.getPalletId());
        }
        if (Cools.isEmpty(erpOutErrorAddress)) {
            return R.error("ERP出库异常上报地址未配置");
        }
        ErpOutOrderAbnormalReportParam erpParam = new ErpOutOrderAbnormalReportParam();
        erpParam.setPalletId(param.getPalletId());
        erpParam.setErrorMsg(param.getErrorMsg());
        erpParam.setOrderId(orderId);
        String requestJson = JSON.toJSONString(erpParam);
        String response = "";
        boolean pushOk = false;
        String errorMsg = null;
        String pushUrl = buildErpOutErrorRequestUrl();
        try {
            response = new HttpHandler.Builder()
                    .setUri(erpUrl)
                    .setPath(erpOutErrorAddress)
                    .setJson(requestJson)
                    .build()
                    .doPost();
            pushOk = isErpCallSuccess(response);
            if (!pushOk) {
                errorMsg = extractErpCallError(response);
            }
        } catch (Exception e) {
            errorMsg = e.getMessage();
            log.error("推ERP出库异常上报失败, palletId={}, orderId={}", param.getPalletId(), orderId, e);
        } finally {
            try {
                apiLogService.save(
                        "推ERP-出库异常上报",
                        pushUrl,
                        null,
                        "127.0.0.1",
                        requestJson,
                        response,
                        pushOk,
                        "palletId=" + param.getPalletId() + ",orderId=" + orderId
                );
            } catch (Exception logEx) {
                log.error("save out abnormal api log failed", logEx);
            }
        }
        if (!pushOk) {
            return R.error(Cools.isEmpty(errorMsg) ? "ERP出库异常上报失败" : errorMsg);
        }
        return R.ok().add(Cools.add("palletId", param.getPalletId()).add("orderId", orderId));
    }
    /**
     * 7.10 出库异常变动处理。
     * 本期只接收、校验和记录,不改本地任务,也不转发 WCS。
     */
    @Override
    public R outOrderAbnormalHandle(OutOrderAbnormalHandleParam param) {
        if (param == null || Cools.isEmpty(param.getPalletId())) {
            return R.error("palletId不能为空");
        }
        if (param.getOperateType() == null || !(Objects.equals(param.getOperateType(), 0) || Objects.equals(param.getOperateType(), 2))) {
            return R.error("operateType只允许0或2");
        }
        if (!existsOutboundPallet(param.getPalletId())) {
            return R.error("未找到托盘:" + param.getPalletId());
        }
        log.info("接收出库异常处理指令, palletId={}, operateType={}", param.getPalletId(), param.getOperateType());
        return R.ok().add(Cools.add("palletId", param.getPalletId()).add("operateType", param.getOperateType()));
    }
    private String buildOutOrderBatchKey(OutTaskParam param) {
        return param.getOrderId() + "#" + param.getBatchSeq();
    }
    private String resolveOutboundOrderId(String palletId) {
        List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>()
                .eq("io_type", 101)
                .eq("barcode", palletId)
                .orderBy("io_time", false)
                .orderBy("wrk_no", false));
        if (!Cools.isEmpty(wrkMasts)) {
            for (WrkMast wrkMast : wrkMasts) {
                if (!Cools.isEmpty(wrkMast.getUserNo())) {
                    return wrkMast.getUserNo();
                }
            }
        }
        List<WrkDetl> wrkDetls = wrkDetlService.selectList(new EntityWrapper<WrkDetl>()
                .eq("zpallet", palletId)
                .orderBy("io_time", false)
                .orderBy("wrk_no", false));
        if (!Cools.isEmpty(wrkDetls)) {
            for (WrkDetl wrkDetl : wrkDetls) {
                if (!Cools.isEmpty(wrkDetl.getOrderNo())) {
                    return wrkDetl.getOrderNo();
                }
            }
        }
        return null;
    }
    private boolean existsOutboundPallet(String palletId) {
        if (locDetlService.selectCount(new EntityWrapper<LocDetl>().eq("zpallet", palletId)) > 0) {
            return true;
        }
        if (wrkMastService.selectCount(new EntityWrapper<WrkMast>().eq("io_type", 101).eq("barcode", palletId)) > 0) {
            return true;
        }
        return wrkDetlService.selectCount(new EntityWrapper<WrkDetl>().eq("zpallet", palletId)) > 0;
    }
    private boolean isErpCallSuccess(String response) {
        if (Cools.isEmpty(response)) {
            return false;
        }
        try {
            JSONObject jsonObject = JSON.parseObject(response);
            if (jsonObject == null) {
                return false;
            }
            Boolean success = jsonObject.getBoolean("success");
            if (success != null) {
                return success;
            }
            Integer code = jsonObject.getInteger("code");
            if (code != null) {
                return code == 200 || code == 0;
            }
            Integer status = jsonObject.getInteger("status");
            if (status != null) {
                return status == 200 || status == 0;
            }
            String result = jsonObject.getString("result");
            if (!Cools.isEmpty(result)) {
                return "success".equalsIgnoreCase(result) || "ok".equalsIgnoreCase(result) || "true".equalsIgnoreCase(result);
            }
        } catch (Exception ignore) {
            return response.toLowerCase().contains("success") && response.toLowerCase().contains("true");
        }
        return false;
    }
    private String extractErpCallError(String response) {
        if (Cools.isEmpty(response)) {
            return "empty erp response";
        }
        try {
            JSONObject jsonObject = JSON.parseObject(response);
            if (jsonObject == null) {
                return response;
            }
            String[] keys = new String[]{"msg", "message", "error", "errMsg"};
            for (String key : keys) {
                String value = jsonObject.getString(key);
                if (!Cools.isEmpty(value)) {
                    return value;
                }
            }
        } catch (Exception ignore) {
        }
        return response;
    }
    private String buildErpOutErrorRequestUrl() {
        if (Cools.isEmpty(erpUrl)) {
            return erpOutErrorAddress;
        }
        if (erpOutErrorAddress == null) {
            return erpUrl;
        }
        if (erpUrl.endsWith("/") && erpOutErrorAddress.startsWith("/")) {
            return erpUrl + erpOutErrorAddress.substring(1);
        }
        if (!erpUrl.endsWith("/") && !erpOutErrorAddress.startsWith("/")) {
            return erpUrl + "/" + erpOutErrorAddress;
        }
        return erpUrl + erpOutErrorAddress;
    }
}