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()); } } }