chen.lin
9 小时以前 a38d4a8619b886e2544cdefe421f171765ad2229
库存汇总
8个文件已修改
483 ■■■■ 已修改文件
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/ERPController.java 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/WmsConstant.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventorySummary.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/WmsServerFeignClient.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/fallback/WmsServerFeignClientFallback.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/ErpQueryController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/service/ReceiveMsgService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/ReceiveMsgServiceImpl.java 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/ERPController.java
@@ -2,17 +2,16 @@
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.openApi.entity.dto.CommonResponse;
import com.vincent.rsf.openApi.entity.phyz.*;
import com.vincent.rsf.openApi.feign.wms.WmsServerFeignClient;
import com.vincent.rsf.openApi.feign.wms.fallback.WmsServerFeignClientFallback;
import com.vincent.rsf.openApi.service.phyz.ErpReportService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -257,87 +256,114 @@
            }
        } catch (Exception e) {
            log.error("库存查询明细失败", e);
            // 过滤错误消息中的URL,只保留错误类型
            String errorMessage = e.getMessage();
            if (errorMessage != null) {
                // 如果包含"executing",说明是HTTP请求错误,去掉URL部分
                if (errorMessage.contains("executing")) {
                    int executingIndex = errorMessage.indexOf("executing");
                    if (executingIndex > 0) {
                        // 提取"executing"之前的部分(如"Read timed out")
                        errorMessage = errorMessage.substring(0, executingIndex).trim();
                    } else {
                        // 如果"executing"在开头,使用默认错误消息
                        errorMessage = "请求超时";
                    }
                }
                // 如果包含"http://"或"https://",也尝试去掉URL部分
                else if (errorMessage.contains("http://") || errorMessage.contains("https://")) {
                    // 使用正则表达式去掉URL
                    errorMessage = errorMessage.replaceAll("https?://[^\\s]+", "").trim();
                    if (errorMessage.isEmpty()) {
                        errorMessage = "请求失败";
                    }
                }
            }
            return CommonResponse.error("查询失败:" + (errorMessage != null && !errorMessage.isEmpty() ? errorMessage : "未知错误"));
            String errorMessage = WmsServerFeignClientFallback.filterErrorMessage(e);
            return CommonResponse.error(errorMessage);
        }
    }
    @ApiOperation("库存查询汇总(表单提交)")
    @PostMapping(value = "/inventory/summary", consumes = "application/x-www-form-urlencoded")
    public CommonResponse queryInventorySummaryForm(HttpServletRequest request) {
        // 从表单参数构建查询条件
        InventoryQueryCondition condition = buildConditionFromRequestParams(request);
        // 调用JSON处理方法
        return queryInventorySummary(condition);
    }
    @ApiOperation("库存查询汇总")
    @PostMapping("/inventory/summary")
    public CommonResponse queryInventorySummary(@RequestBody JSONObject params) {
    @PostMapping(value = "/inventory/summary", consumes = "application/json")
    public CommonResponse queryInventorySummary(@RequestBody(required = false) InventoryQueryCondition condition) {
        // JSON方式:支持不传递参数({}或null或空请求体),创建默认的空查询条件对象
        if (condition == null) {
            condition = new InventoryQueryCondition();
        }
        if (SIMULATED_DATA_ENABLE.equals("1")) {
            String s = "{\n" +
                    "  \"code\": 200,\n" +
                    "  \"msg\": \"操作成功\",\n" +
                    "  \"data\": [\n" +
                    "    {\n" +
                    "      \"wareHouseId\": \"WH001\",\n" +
                    "      \"wareHouseName\": \"原料仓库\",\n" +
                    "      \"matNr\": \"MAT10001\",\n" +
                    "      \"makTx\": \"钢材Q235\",\n" +
                    "      \"spec\": \"国标GB/T700-2006\",\n" +
                    "      \"anfme\": 10.5,\n" +
                    "      \"unit\": \"吨\",\n" +
                    "      \"stockOrgId\": \"ORG001\",\n" +
                    "      \"batch\": \"BATCH20260106001\",\n" +
                    "      \"planNo\": \"Plan20260106006\"\n" +
                    "    },\n" +
                    "    {\n" +
                    "      \"wareHouseId\": \"WH001\",\n" +
                    "      \"wareHouseName\": \"原料仓库\",\n" +
                    "      \"matNr\": \"MAT10002\",\n" +
                    "      \"makTx\": \"铝型材6061\",\n" +
                    "      \"spec\": \"国标GB/T3190-2008\",\n" +
                    "      \"anfme\": 20.3,\n" +
                    "      \"unit\": \"吨\",\n" +
                    "      \"stockOrgId\": \"ORG001\",\n" +
                    "      \"batch\": \"BATCH20260106002\",\n" +
                    "      \"planNo\": \"Plan20260106005\"\n" +
                    "    },\n" +
                    "    {\n" +
                    "      \"wareHouseId\": \"WH002\",\n" +
                    "      \"wareHouseName\": \"成品仓库\",\n" +
                    "      \"matNr\": \"MAT30001\",\n" +
                    "      \"makTx\": \"电机成品\",\n" +
                    "      \"spec\": \"380V 50Hz 15KW\",\n" +
                    "      \"anfme\": 100,\n" +
                    "      \"unit\": \"台\",\n" +
                    "      \"stockOrgId\": \"ORG001\",\n" +
                    "      \"batch\": \"BATCH20260106003\",\n" +
                    "      \"planNo\": \"Plan20260106004\"\n" +
                    "    }\n" +
                    "  ]\n" +
                    "}";
            return JSONObject.parseObject(s, CommonResponse.class);
            String x = "[\n" +
                    "  {\n" +
                    "    \"wareHouseId\": \"WH001\",\n" +
                    "    \"wareHouseName\": \"原料仓库\",\n" +
                    "    \"matNr\": \"MAT10001\",\n" +
                    "    \"matTx\": \"钢材Q235\",\n" +
                    "    \"spec\": \"国标GB/T700-2006\",\n" +
                    "    \"anfme\": 10.5,\n" +
                    "    \"unit\": \"吨\",\n" +
                    "    \"stockOrgId\": \"ORG001\",\n" +
                    "    \"batch\": \"BATCH20260106001\",\n" +
                    "    \"planNo\": \"Plan20260106006\"\n" +
                    "  },\n" +
                    "  {\n" +
                    "    \"wareHouseId\": \"WH001\",\n" +
                    "    \"wareHouseName\": \"原料仓库\",\n" +
                    "    \"matNr\": \"MAT10002\",\n" +
                    "    \"matTx\": \"铝型材6061\",\n" +
                    "    \"spec\": \"国标GB/T3190-2008\",\n" +
                    "    \"anfme\": 20.3,\n" +
                    "    \"unit\": \"吨\",\n" +
                    "    \"stockOrgId\": \"ORG001\",\n" +
                    "    \"batch\": \"BATCH20260106002\",\n" +
                    "    \"planNo\": \"Plan20260106005\"\n" +
                    "  },\n" +
                    "  {\n" +
                    "    \"wareHouseId\": \"WH002\",\n" +
                    "    \"wareHouseName\": \"成品仓库\",\n" +
                    "    \"matNr\": \"MAT30001\",\n" +
                    "    \"matTx\": \"电机成品\",\n" +
                    "    \"spec\": \"380V 50Hz 15KW\",\n" +
                    "    \"anfme\": 100,\n" +
                    "    \"unit\": \"台\",\n" +
                    "    \"stockOrgId\": \"ORG001\",\n" +
                    "    \"batch\": \"BATCH20260106003\",\n" +
                    "    \"planNo\": \"Plan20260106004\"\n" +
                    "  }\n" +
                    "]";
            return CommonResponse.ok(JSONArray.parseArray(x, InventorySummary.class));
        }
        InventoryQueryCondition condition = JSON.parseObject(params.toJSONString(), InventoryQueryCondition.class);
        // 数据处理,转发server
        List<InventorySummary> inventorySummaries = Lists.newArrayList();
        return new CommonResponse().setCode(200).setData(inventorySummaries);
        try {
            if (wmsServerFeignClient == null) {
                log.warn("WmsServerFeignClient未注入,无法进行调用");
                return CommonResponse.error("服务未初始化");
            }
            log.info("库存查询汇总请求参数: {}", JSON.toJSONString(condition));
            // 直接传递实体类,Feign会自动序列化为JSON
            R result = wmsServerFeignClient.queryInventorySummary(condition);
            log.info("库存查询汇总返回结果: {}", JSON.toJSONString(result));
            if (result != null) {
                // R类继承自HashMap,使用get方法获取值
                Integer code = (Integer) result.get("code");
                String msg = (String) result.get("msg");
                Object data = result.get("data");
                if (code != null && code == 200) {
                    // 将Map列表转换为InventorySummary列表
                    if (data != null) {
                        @SuppressWarnings("unchecked")
                        List<Map<String, Object>> dataList = (List<Map<String, Object>>) data;
                        List<InventorySummary> inventorySummaries = new ArrayList<>();
                        for (Map<String, Object> item : dataList) {
                            InventorySummary summary = JSON.parseObject(JSON.toJSONString(item), InventorySummary.class);
                            inventorySummaries.add(summary);
                        }
                        return CommonResponse.ok(inventorySummaries);
                    } else {
                        return CommonResponse.ok(new ArrayList<>());
                    }
                } else {
                    return CommonResponse.error(msg != null ? msg : "查询失败");
                }
            } else {
                return CommonResponse.error("查询失败:返回结果为空");
            }
        } catch (Exception e) {
            log.error("库存查询汇总失败", e);
            String errorMessage = WmsServerFeignClientFallback.filterErrorMessage(e);
            return CommonResponse.error(errorMessage);
        }
    }
    @ApiOperation("盘点结果确认")
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/WmsConstant.java
@@ -43,5 +43,8 @@
    //库存查询明细(ERP接口,对应open-api的/inventory/details)
    public static final String QUERY_INVENTORY_DETAILS = "/rsf-server/erp/inventory/details";
    //库存查询汇总(ERP接口,对应open-api的/inventory/summary)
    public static final String QUERY_INVENTORY_SUMMARY = "/rsf-server/erp/inventory/summary";
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventorySummary.java
@@ -18,7 +18,7 @@
    // 物料编码
    private String matNr;
    // 物料名称
    private String makTx;
    private String matTx;
    // 规格
    private String spec;
    // 数量,小数点最长6位
rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/WmsServerFeignClient.java
@@ -31,4 +31,12 @@
     */
    @PostMapping(WmsConstant.QUERY_INVENTORY_DETAILS)
    R queryInventoryDetails(@RequestBody InventoryQueryCondition condition);
    /**
     * 库存查询汇总(调用server端的erpQueryInventorySummary方法)
     * @param condition 查询条件实体类
     * @return 库存汇总列表
     */
    @PostMapping(WmsConstant.QUERY_INVENTORY_SUMMARY)
    R queryInventorySummary(@RequestBody InventoryQueryCondition condition);
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/fallback/WmsServerFeignClientFallback.java
@@ -15,7 +15,88 @@
    @Override
    public R queryInventoryDetails(InventoryQueryCondition condition) {
        log.error("调用WMS Server库存查询明细接口失败,触发降级处理");
        return R.error("服务调用失败,请稍后重试");
        return queryInventoryDetails(condition, null);
    }
    /**
     * 库存查询明细降级处理(带异常信息)
     * @param condition 查询条件
     * @param throwable 异常信息(可选)
     * @return 错误响应
     */
    public R queryInventoryDetails(InventoryQueryCondition condition, Throwable throwable) {
        log.error("调用WMS Server库存查询明细接口失败,触发降级处理", throwable);
        String errorMessage = filterErrorMessage(throwable);
        return R.error(errorMessage);
    }
    @Override
    public R queryInventorySummary(InventoryQueryCondition condition) {
        return queryInventorySummary(condition, null);
    }
    /**
     * 库存查询汇总降级处理(带异常信息)
     * @param condition 查询条件
     * @param throwable 异常信息(可选)
     * @return 错误响应
     */
    public R queryInventorySummary(InventoryQueryCondition condition, Throwable throwable) {
        log.error("调用WMS Server库存查询汇总接口失败,触发降级处理", throwable);
        String errorMessage = filterErrorMessage(throwable);
        return R.error(errorMessage);
    }
    /**
     * 过滤错误消息中的URL,只保留错误类型
     * @param throwable 异常对象(可选)
     * @return 过滤后的完整错误消息(包含"查询失败:"前缀)
     */
    public static String filterErrorMessage(Throwable throwable) {
        if (throwable == null) {
            return "查询失败:服务调用失败,请稍后重试";
        }
        return filterErrorMessage(throwable.getMessage());
    }
    /**
     * 过滤错误消息中的URL,只保留错误类型
     * @param errorMessage 错误消息字符串(可选)
     * @return 过滤后的完整错误消息(包含"查询失败:"前缀)
     */
    public static String filterErrorMessage(String errorMessage) {
        if (errorMessage == null || errorMessage.isEmpty()) {
            return "查询失败:未知错误";
        }
        String filteredMessage = errorMessage;
        // 如果包含"executing",说明是HTTP请求错误,去掉URL部分
        if (filteredMessage.contains("executing")) {
            int executingIndex = filteredMessage.indexOf("executing");
            if (executingIndex > 0) {
                // 提取"executing"之前的部分(如"Read timed out")
                filteredMessage = filteredMessage.substring(0, executingIndex).trim();
            } else {
                // 如果"executing"在开头,使用默认错误消息
                filteredMessage = "请求超时";
            }
        }
        // 如果包含"http://"或"https://",也尝试去掉URL部分
        else if (filteredMessage.contains("http://") || filteredMessage.contains("https://")) {
            // 使用正则表达式去掉URL
            filteredMessage = filteredMessage.replaceAll("https?://[^\\s]+", "").trim();
            if (filteredMessage.isEmpty()) {
                filteredMessage = "请求失败";
            }
        }
        // 如果过滤后的消息为空,使用默认错误消息
        if (filteredMessage.isEmpty()) {
            filteredMessage = "未知错误";
        }
        // 返回包含"查询失败:"前缀的完整错误消息
        return "查询失败:" + filteredMessage;
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/ErpQueryController.java
@@ -121,4 +121,22 @@
        return receiveMsgService.erpQueryInventoryDetails(condition);
    }
    /**
     * ERP库存查询汇总(供open-api模块调用)
     * 对应open-api的 /inventory/summary 接口
     * @param condition 查询条件
     * @return 库存汇总列表
     */
    @PostMapping("/inventory/summary")
    @ApiOperation(value = "ERP库存查询汇总", hidden = true)
    @OperationLog("ERP库存查询汇总")
    public R erpQueryInventorySummary(@RequestBody InventoryQueryConditionParam condition) {
        // 参数验证
        if (condition == null) {
            return R.error("查询条件不能为空");
        }
        return receiveMsgService.erpQueryInventorySummary(condition);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/api/service/ReceiveMsgService.java
@@ -159,4 +159,11 @@
     * @return 库存明细列表
     */
    R erpQueryInventoryDetails(InventoryQueryConditionParam condition);
    /**
     * 库存查询汇总(供open-api调用)
     * @param condition 查询条件
     * @return 库存汇总列表
     */
    R erpQueryInventorySummary(InventoryQueryConditionParam condition);
}
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/ReceiveMsgServiceImpl.java
@@ -1200,4 +1200,186 @@
        return details;
    }
    /**
     * 库存查询汇总(供open-api调用)
     *
     * @param condition 查询条件实体类
     * @return 库存汇总列表
     */
    @Override
    public R erpQueryInventorySummary(InventoryQueryConditionParam condition) {
        try {
            // 参数验证
            if (condition == null) {
                return R.error("查询条件不能为空");
            }
            // 将ERP参数映射为数据库字段名(下划线格式)
            Map<String, Object> queryMap = new HashMap<>();
            // 从实体类中提取查询条件,映射为真实的数据库字段名
            if (StringUtils.isNotBlank(condition.getMatNr())) {
                queryMap.put("matnr_code", condition.getMatNr());
            }
            if (StringUtils.isNotBlank(condition.getPlanNo())) {
                queryMap.put("track_code", condition.getPlanNo());
            }
            if (StringUtils.isNotBlank(condition.getBatch())) {
                queryMap.put("batch", condition.getBatch());
            }
            BaseParam baseParam = new BaseParam();
            baseParam.syncMap(queryMap);
            PageParam<LocItem, BaseParam> pageParam = new PageParam<>(baseParam, LocItem.class);
            QueryWrapper<LocItem> wrapper = pageParam.buildWrapper(false);
            // 物料组(需要通过物料表关联查询)
            if (StringUtils.isNotBlank(condition.getMatGroup())) {
                // 调用物料Service查询物料组对应的物料ID列表(复用已有方法)
                LambdaQueryWrapper<Matnr> matnrWrapper = new LambdaQueryWrapper<>();
                matnrWrapper.eq(Matnr::getGroupId, condition.getMatGroup());
                List<Matnr> matnrs = matnrService.list(matnrWrapper);
                if (!matnrs.isEmpty()) {
                    List<Long> matnrIds = matnrs.stream().map(Matnr::getId).collect(Collectors.toList());
                    wrapper.in("matnr_id", matnrIds);
                } else {
                    // 如果没有找到物料,返回空结果
                    return R.ok().add(new ArrayList<>());
                }
            }
            // 只查询正常状态的库存(status=1表示正常)
            wrapper.eq("status", 1);
            // 设置分页参数,查询所有数据用于汇总
            pageParam.setCurrent(1);
            pageParam.setSize(Integer.MAX_VALUE);
            PageParam<LocItem, BaseParam> pageResult = locItemService.page(pageParam, wrapper);
            List<LocItem> locItems = pageResult.getRecords();
            if (locItems.isEmpty()) {
                return R.ok().add(new ArrayList<>());
            }
            // 获取所有需要关联的ID
            List<Long> locIds = locItems.stream()
                    .map(LocItem::getLocId)
                    .filter(Objects::nonNull)
                    .distinct()
                    .collect(Collectors.toList());
            List<Long> warehouseIds = new ArrayList<>();
            // 调用LocService查询库位信息(复用Service层方法)
            Map<Long, Loc> locMap = new HashMap<>();
            if (!locIds.isEmpty()) {
                List<Loc> locs = locService.listByIds(locIds);
                locMap = locs.stream().collect(Collectors.toMap(Loc::getId, loc -> loc));
                // 收集仓库ID
                warehouseIds = locs.stream()
                        .map(Loc::getWarehouseId)
                        .filter(Objects::nonNull)
                        .distinct()
                        .collect(Collectors.toList());
            }
            // 仓库编码过滤
            if (StringUtils.isNotBlank(condition.getWareHouseId())) {
                String wareHouseId = condition.getWareHouseId();
                LambdaQueryWrapper<Warehouse> whWrapper = new LambdaQueryWrapper<>();
                whWrapper.eq(Warehouse::getCode, wareHouseId);
                // 调用WarehouseService查询仓库信息(复用Service层方法)
                List<Warehouse> warehouses = warehouseService.list(whWrapper);
                if (!warehouses.isEmpty()) {
                    Long targetWarehouseId = warehouses.get(0).getId();
                    // 过滤库位,只保留目标仓库的库位
                    locMap = locMap.entrySet().stream()
                            .filter(entry -> Objects.equals(entry.getValue().getWarehouseId(), targetWarehouseId))
                            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
                    // 过滤locItems,只保留目标仓库的
                    Set<Long> validLocIds = locMap.keySet();
                    locItems = locItems.stream()
                            .filter(item -> item.getLocId() != null && validLocIds.contains(item.getLocId()))
                            .collect(Collectors.toList());
                    warehouseIds = Collections.singletonList(targetWarehouseId);
                } else {
                    return R.ok().add(new ArrayList<>());
                }
            }
            // 调用WarehouseService查询仓库信息(复用Service层方法)
            Map<Long, Warehouse> warehouseMap = new HashMap<>();
            if (!warehouseIds.isEmpty()) {
                List<Warehouse> warehouses = warehouseService.listByIds(warehouseIds);
                warehouseMap = warehouses.stream().collect(Collectors.toMap(Warehouse::getId, wh -> wh));
            }
            // 按仓库、物料、批号、库存组织分组汇总
            // 使用Map的key作为分组键:wareHouseId_matNr_batch_stockOrgId
            Map<String, Map<String, Object>> summaryMap = new HashMap<>();
            for (LocItem locItem : locItems) {
                // 获取仓库信息
                Loc loc = null;
                if (locItem.getLocId() != null) {
                    loc = locMap.get(locItem.getLocId());
                }
                if (loc == null) {
                    continue;
                }
                Warehouse warehouse = null;
                if (loc.getWarehouseId() != null && warehouseMap.containsKey(loc.getWarehouseId())) {
                    warehouse = warehouseMap.get(loc.getWarehouseId());
                }
                if (warehouse == null) {
                    continue;
                }
                // 构建分组键:wareHouseId_matNr_batch_stockOrgId
                String wareHouseId = warehouse.getCode();
                String matNr = locItem.getMatnrCode();
                String batch = locItem.getBatch() != null ? locItem.getBatch() : "";
                String stockOrgId = locItem.getUseOrgId() != null ? locItem.getUseOrgId() : "";
                String groupKey = wareHouseId + "_" + matNr + "_" + batch + "_" + stockOrgId;
                // 如果该分组已存在,累加数量
                if (summaryMap.containsKey(groupKey)) {
                    Map<String, Object> summary = summaryMap.get(groupKey);
                    Double currentAnfme = (Double) summary.get("anfme");
                    Double newAnfme = currentAnfme + (locItem.getAnfme() != null ? locItem.getAnfme() : 0.0);
                    summary.put("anfme", newAnfme);
                } else {
                    // 创建新的汇总记录
                    Map<String, Object> summary = new HashMap<>();
                    summary.put("wareHouseId", wareHouseId);
                    summary.put("wareHouseName", warehouse.getName());
                    summary.put("matNr", matNr);
                    summary.put("matTx", locItem.getMaktx());
                    summary.put("spec", locItem.getSpec());
                    summary.put("unit", locItem.getUnit());
                    summary.put("anfme", locItem.getAnfme() != null ? locItem.getAnfme() : 0.0);
                    summary.put("batch", locItem.getBatch());
                    summary.put("stockOrgId", locItem.getUseOrgId());
                    summary.put("planNo", locItem.getTrackCode());
                    summaryMap.put(groupKey, summary);
                }
            }
            // 转换为列表
            List<Map<String, Object>> result = new ArrayList<>(summaryMap.values());
            return R.ok().add(result);
        } catch (Exception e) {
            log.error("库存查询汇总失败", e);
            return R.error("查询失败:" + e.getMessage());
        }
    }
}