| | |
| | | } |
| | | |
| | | /** |
| | | * 7.11 出库通知单(传递有序无序规则) |
| | | * 7.11 出库通知单(传递有序无序规则)。 |
| | | * |
| | | * stationId <= 600:沿用原实时任务模式,接口校验通过后直接生成 WrkMast/WrkDetl。 |
| | | * stationId > 600:进入“先建出库订单、后续定时生成任务”的延迟模式。 |
| | | * |
| | | * 延迟模式的关键点: |
| | | * 1. orderId 作为出库订单号落到 man_order_pakout.order_no。 |
| | | * 2. entryWmsCode 作为进仓编号保存到订单明细,后续生成任务时强制作为 WrkMast.batchSeq。 |
| | | * 3. 建单阶段只校验库存和满库位,不锁库位、不改 loc_sts,避免提前占用设备任务资源。 |
| | | */ |
| | | @PostMapping("/outOrder") |
| | | public synchronized R outOrder(@RequestBody ArrayList<OutTaskParam> params, HttpServletRequest request) { |
| | | int count = params.size(); |
| | | if (Cools.isEmpty(params)) { |
| | | return R.error("请求参数不能为空"); |
| | | } |
| | | int count = params.size(); |
| | | log.info("[outOrder] cache: {}", JSON.toJSONString(params)); |
| | | request.setAttribute("cache", params); |
| | | // stationId <= 600 的原始实时出库任务分组,后面直接传给 outOrderBatch。 |
| | | Map<String, List<OutTaskParam>> linesByBatchSeq = new LinkedHashMap<>(); |
| | | // 统一做有序/无序校验的分组: |
| | | // 低站点按 orderId + batchSeq;高站点按 orderId + entryWmsCode。 |
| | | // 高站点后续会把 entryWmsCode 转成任务 batchSeq,因此这里先按同一维度校验 seq。 |
| | | Map<String, List<OutTaskParam>> linesByValidateKey = new LinkedHashMap<>(); |
| | | // stationId > 600 的参数只建出库订单,定时器再按进仓编号逐批生成任务。 |
| | | List<OutTaskParam> pendingOrderParams = new ArrayList<>(); |
| | | for (OutTaskParam outTaskParam : params) { |
| | | if (Cools.isEmpty(outTaskParam) || Cools.isEmpty(outTaskParam.getOrderId())) { |
| | | return R.error("出库单号不能为空"); |
| | |
| | | if (Cools.isEmpty(outTaskParam.getStationId())) { |
| | | return R.error("托盘「" + outTaskParam.getPalletId() + "」出库口编码不能为空"); |
| | | } |
| | | linesByBatchSeq.computeIfAbsent(outTaskParam.getBatchSeq(), k -> new ArrayList<>()).add(outTaskParam); |
| | | Integer stationId; |
| | | try { |
| | | stationId = Integer.valueOf(outTaskParam.getStationId()); |
| | | } catch (NumberFormatException ex) { |
| | | return R.error("托盘「" + outTaskParam.getPalletId() + "」出库口编码格式错误:" + outTaskParam.getStationId()); |
| | | } |
| | | if (stationId > 600) { |
| | | // 高站点任务必须带进仓编号;这是定时生成任务时的批次边界, |
| | | // 也是 OutboundBatchSeqReleaseGuard 判断能否放行下一进仓编号的依据。 |
| | | if (Cools.isEmpty(outTaskParam.getEntryWmsCode())) { |
| | | return R.error("托盘「" + outTaskParam.getPalletId() + "」进仓编号不能为空"); |
| | | } |
| | | pendingOrderParams.add(outTaskParam); |
| | | linesByValidateKey.computeIfAbsent(outTaskParam.getOrderId() + "#" + outTaskParam.getEntryWmsCode(), k -> new ArrayList<>()).add(outTaskParam); |
| | | } else { |
| | | linesByBatchSeq.computeIfAbsent(outTaskParam.getBatchSeq(), k -> new ArrayList<>()).add(outTaskParam); |
| | | linesByValidateKey.computeIfAbsent(buildOutOrderBatchKey(outTaskParam), k -> new ArrayList<>()).add(outTaskParam); |
| | | } |
| | | } |
| | | |
| | | for (Map.Entry<String, List<OutTaskParam>> entry : linesByBatchSeq.entrySet()) { |
| | | // 仍保留原接口的有序/无序规则。高站点虽然暂不生成任务,也要在建单前保证 |
| | | // 同一进仓编号内的明细顺序合法,否则后续定时生成任务时才失败会更难追溯。 |
| | | for (Map.Entry<String, List<OutTaskParam>> entry : linesByValidateKey.entrySet()) { |
| | | List<OutTaskParam> lines = entry.getValue(); |
| | | OutTaskParam head = lines.get(0); |
| | | String oid = head.getOrderId(); |
| | | String batchSeq = head.getBatchSeq(); |
| | | String batchSeq = isPendingOutOrderStation(head.getStationId()) ? head.getEntryWmsCode() : head.getBatchSeq(); |
| | | boolean hasZero = false; |
| | | boolean hasPositive = false; |
| | | List<Integer> orderedSeqs = new ArrayList<>(lines.size()); |
| | |
| | | } |
| | | } |
| | | |
| | | // 重新按校验分组顺序展开,保证重复托盘、库存、库位校验与上面的批次校验看到同一批数据。 |
| | | List<OutTaskParam> groupedParams = new ArrayList<>(params.size()); |
| | | for (List<OutTaskParam> lines : linesByBatchSeq.values()) { |
| | | for (List<OutTaskParam> lines : linesByValidateKey.values()) { |
| | | groupedParams.addAll(lines); |
| | | } |
| | | |
| | |
| | | // } |
| | | // } |
| | | |
| | | // 延迟建单模式也必须确认托盘有库存且当前库位为满库位。 |
| | | // 这里只做准入校验,不在 controller 层提前锁库位;真正锁库位仍由后续 outOrderBatch 生成任务时处理。 |
| | | List<OutTaskParam> missingStock = Lists.newArrayList(); |
| | | List<OutTaskParam> missingLoc = Lists.newArrayList(); |
| | | for (OutTaskParam outTaskParam : groupedParams) { |
| | |
| | | } |
| | | return R.error("没有找到托盘码对应库位:" + String.join(",", badPalletIds)).add(missingLoc); |
| | | } |
| | | |
| | | return openService.outOrderBatch(linesByBatchSeq,count); |
| | | // 混合请求下先创建高站点出库订单,再创建低站点实时任务。 |
| | | // 这样同一个 orderId 同时包含高、低站点时,高站点建单不会被低站点刚生成的任务误判为“已有任务”。 |
| | | R orderResult = R.ok(); |
| | | if (!pendingOrderParams.isEmpty()) { |
| | | orderResult = openService.outOrderCreatePakoutOrder(pendingOrderParams); |
| | | } |
| | | R directResult = R.ok(); |
| | | if (!linesByBatchSeq.isEmpty()) { |
| | | directResult = openService.outOrderBatch(linesByBatchSeq, count - pendingOrderParams.size()); |
| | | } |
| | | if (!linesByBatchSeq.isEmpty() && !pendingOrderParams.isEmpty()) { |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("directTaskCount", count - pendingOrderParams.size()); |
| | | result.put("pendingOrderCount", pendingOrderParams.size()); |
| | | result.put("directResult", directResult); |
| | | result.put("orderResult", orderResult); |
| | | return R.ok().add(result); |
| | | } |
| | | return pendingOrderParams.isEmpty() ? directResult : orderResult; |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | private String buildOutOrderBatchKey(OutTaskParam param) { |
| | | return param.getOrderId() + "#" + param.getBatchSeq(); |
| | | } |
| | | |
| | | private boolean isPendingOutOrderStation(String stationId) { |
| | | try { |
| | | return Integer.valueOf(stationId) > 600; |
| | | } catch (Exception ignored) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | Integer sum = 0; |
| | | if (ioType != null && ioType < 100) { |
| | | supp = String.valueOf(resolveInboundSupp(wrkMast)); |
| | | map.put("supp", supp); |
| | | }else { |
| | | String[] split = wrkDetls.get(0).getSupp().split("/"); |
| | | if (split != null && split.length > 0) { |
| | |
| | | sum = Integer.valueOf(wrkDetls.get(0).getSupp()); |
| | | } |
| | | List<WrkMast> userNo = wrkMastService.selectList(new EntityWrapper<WrkMast>().eq("user_no", wrkMast.getUserNo()).in("wrk_sts",11,12,21,22,25)); |
| | | suppCount = sum - userNo.size()+1; |
| | | suppCount = sum - userNo.size(); |
| | | map.put("supp", suppCount + "/" + sum); |
| | | } |
| | | map.put("supp", suppCount + "/" + sum); |
| | | |
| | | //耗时 |
| | | Long costTime = resolveCostTime(wrkMast); |