chen.lin
昨天 c67e3d0295858a61122354a15dec8835044bac0d
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
@@ -92,44 +92,96 @@
    @Override
    public PageParam<Matnr, BaseParam> pageMatnrForOutStock(PageParam<Matnr, BaseParam> pageParam, Map<String, Object> params) {
        // 在 getMatnrPage 之前取出 locUseStatus:getMatnrPage 会从 where.map 中 remove 掉该键(与 params 同引用),导致后续取不到
        String locUseStatus = params.get("locUseStatus") != null ? params.get("locUseStatus").toString().trim() : null;
        if (locUseStatus != null && locUseStatus.isEmpty()) locUseStatus = null;
        PageParam<Matnr, BaseParam> page = matnrService.getMatnrPage(pageParam, params);
        List<Matnr> records = page.getRecords();
        if (records == null || records.isEmpty()) {
            return page;
        }
        List<Long> matnrIds = records.stream().map(Matnr::getId).collect(Collectors.toList());
        String locUseStatus = params.get("locUseStatus") != null ? params.get("locUseStatus").toString() : null;
        List<Map<String, Object>> stockList = locItemMapper.listStockByMatnrIds(matnrIds, locUseStatus);
        Map<Long, Double> stockQtyMap = new HashMap<>();
        Map<Long, String> locStatusDescMap = new HashMap<>();
        Map<Long, String> locCodesMap = new HashMap<>();
        for (Map<String, Object> row : stockList) {
            Long matnrId = getLong(row, "matnrId", "matnrid");
            if (matnrId == null) continue;
            Object qty = getAny(row, "stockQty", "stockqty");
            double v = qty instanceof Number ? ((Number) qty).doubleValue() : 0d;
            stockQtyMap.put(matnrId, v);
            String locCodes = getStr(row, "locCodes", "loccodes");
            if (locCodes != null && !locCodes.isEmpty()) {
                locCodesMap.put(matnrId, locCodes);
            }
            String locStatuses = getStr(row, "locStatuses", "locstatuses");
            if (locStatuses != null && !locStatuses.isEmpty()) {
                String desc = Arrays.stream(locStatuses.split(","))
                        .map(String::trim)
                        .map(LocStsType::getDescByType)
                        .collect(Collectors.joining(","));
                locStatusDescMap.put(matnrId, desc);
            } else if (locUseStatus != null && !locUseStatus.isEmpty()) {
                locStatusDescMap.put(matnrId, LocStsType.getDescByType(locUseStatus));
            }
        }
        List<Map<String, Object>> stockByLocList = locItemMapper.listStockByMatnrIdsGroupByStatusAndLoc(matnrIds, locUseStatus);
        Map<Long, List<Map<String, Object>>> rowsByMatnr = buildRowsByMatnrFromPerLoc(stockByLocList);
        List<Matnr> expanded = new ArrayList<>();
        for (Matnr record : records) {
            record.setStockQty(stockQtyMap.getOrDefault(record.getId(), 0d));
            record.setLocUseStatus$(locStatusDescMap.get(record.getId()));
            record.setLocCodes$(locCodesMap.get(record.getId()));
            List<Map<String, Object>> statusRows = rowsByMatnr.get(record.getId());
            if (statusRows == null || statusRows.isEmpty()) {
                record.setStockQty(0d);
                record.setLocUseStatus$(null);
                record.setLocCodes$(null);
                expanded.add(record);
                continue;
        }
            for (Map<String, Object> row : statusRows) {
                double v = row.get("stockQty") instanceof Number ? ((Number) row.get("stockQty")).doubleValue() : 0d;
                String useStatus = getStr(row, "useStatus", "usestatus");
                String statusDesc = useStatus != null ? LocStsType.getDescByType(useStatus) : null;
                String locCodesWithQty = getStr(row, "locCodes$", "loccodes$");
                Matnr copy = cloneMatnrForRow(record);
                copy.setStockQty(v);
                copy.setLocUseStatus$(statusDesc);
                copy.setLocCodes$(locCodesWithQty);
                expanded.add(copy);
            }
        }
        page.setRecords(expanded);
        return page;
    }
    /** 复制物料用于按状态展开行(仅复制展示用字段,id 保持原样供前端选行用) */
    private static Matnr cloneMatnrForRow(Matnr source) {
        Matnr copy = new Matnr();
        BeanUtils.copyProperties(source, copy, "stockQty", "locUseStatus$", "locCodes$");
        return copy;
    }
    /**
     * 仅用「按库位明细」查询结果在内存中分组汇总:按 (matnrId, useStatus) 聚合,
     * 得到 stockQty、locCodes$(库位(数量),...),减轻数据库压力。
     */
    private static Map<Long, List<Map<String, Object>>> buildRowsByMatnrFromPerLoc(List<Map<String, Object>> stockByLocList) {
        Map<String, List<Map<String, Object>>> perLocByMatnrAndStatus = new HashMap<>();
        for (Map<String, Object> locRow : stockByLocList) {
            Long mid = getLong(locRow, "matnrId", "matnrid");
            String us = getStr(locRow, "useStatus", "usestatus");
            if (mid == null || us == null) continue;
            String key = mid + ":" + us;
            perLocByMatnrAndStatus.computeIfAbsent(key, k -> new ArrayList<>()).add(locRow);
        }
        Map<Long, List<Map<String, Object>>> rowsByMatnr = new HashMap<>();
        for (Map.Entry<String, List<Map<String, Object>>> e : perLocByMatnrAndStatus.entrySet()) {
            String[] parts = e.getKey().split(":", 2);
            if (parts.length != 2) continue;
            Long matnrId;
            try { matnrId = Long.parseLong(parts[0]); } catch (NumberFormatException ex) { continue; }
            String useStatus = parts[1];
            List<Map<String, Object>> locs = e.getValue();
            double stockQty = 0d;
            StringBuilder sb = new StringBuilder();
            for (Map<String, Object> locRow : locs) {
                Object q = getAny(locRow, "locQty", "locqty");
                double qty = q instanceof Number ? ((Number) q).doubleValue() : 0d;
                stockQty += qty;
                String code = getStr(locRow, "locCode", "loccode");
                if (sb.length() > 0) sb.append(",");
                sb.append(code != null ? code : "").append("(").append(formatQtyForLoc(qty)).append(")");
            }
            Map<String, Object> statusRow = new HashMap<>();
            statusRow.put("matnrId", matnrId);
            statusRow.put("useStatus", useStatus);
            statusRow.put("stockQty", stockQty);
            statusRow.put("locCodes$", sb.toString());
            rowsByMatnr.computeIfAbsent(matnrId, k -> new ArrayList<>()).add(statusRow);
        }
        return rowsByMatnr;
    }
    private static String formatQtyForLoc(double qty) {
        if (qty == (long) qty) return String.valueOf((long) qty);
        String s = BigDecimal.valueOf(qty).setScale(6, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString();
        return s;
    }
    private static Long getLong(Map<String, Object> map, String... keys) {
@@ -748,11 +800,13 @@
                locItems.add(locItem);
                LocToTaskParams taskParams = new LocToTaskParams();
                // 出库单下发任务时,出库口未传则默认 1001
                String siteNo = StringUtils.isNotBlank(param.getSiteNo()) ? param.getSiteNo() : "1001";
                taskParams.setType(Constants.TASK_TYPE_ORDER_OUT_STOCK)
                        .setOrgLoc(loc.getCode())
                        .setItems(locItems)
                        .setSourceId(outId)
                        .setSiteNo(param.getSiteNo());
                        .setSiteNo(siteNo);
                try {
                    //生成出库任务
                    locItemService.generateTask(TaskResouceType.TASK_RESOUCE_ORDER_TYPE.val, taskParams, loginUserId);
@@ -892,6 +946,14 @@
                            .eq(DeviceSite::getChannel, loc.getChannel())
                            .eq(DeviceSite::getType, issued.doubleValue() >= locItem.getAnfme() && itemList.size() == 1 ? TaskType.TASK_TYPE_OUT.type : TaskType.TASK_TYPE_PICK_AGAIN_OUT.type)
                    );
                    // 出库口列表排序:1001 排第一,作为默认
                    deviceSites.sort((a, b) -> {
                        boolean a1001 = "1001".equals(a.getSite());
                        boolean b1001 = "1001".equals(b.getSite());
                        if (a1001 && !b1001) return -1;
                        if (!a1001 && b1001) return 1;
                        return 0;
                    });
                    if (!deviceSites.isEmpty()) {
                        List<OrderOutItemDto.staListDto> maps = new ArrayList<>();
@@ -904,7 +966,7 @@
                        orderOutItemDto.setStaNos(maps);
                        //默认获取第一站点
                        DeviceSite deviceSite = deviceSites.stream().findFirst().get();
                        orderOutItemDto.setSiteNo(deviceSite.getSite());
                        orderOutItemDto.setSitesNo(deviceSite.getSite());
                    }
                    list.add(orderOutItemDto);