chen.lin
12 小时以前 a56420ff2042a3f6b1e824341a717a28c692cad4
查询库存明细
8个文件已修改
3个文件已添加
634 ■■■■■ 已修改文件
rsf-open-api/pom.xml 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/OpenApi.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/ERPController.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/WmsConstant.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Station.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/WmsServerFeignClient.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/fallback/WmsServerFeignClientFallback.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/ErpQueryController.java 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventoryQueryConditionParam.java 129 ●●●●● 补丁 | 查看 | 原始文档 | 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 284 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/pom.xml
@@ -14,6 +14,7 @@
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.proc>full</maven.compiler.proc>
    </properties>
    <dependencies>
        <dependency>
@@ -44,6 +45,18 @@
            <artifactId>httpclient</artifactId>
            <version>4.5.13</version>
        </dependency>
        <!-- OpenFeign依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- Lombok依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>rsf-open-api</finalName>
@@ -64,6 +77,22 @@
                    </nonFilteredFileExtensions>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                    <encoding>UTF-8</encoding>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.18.30</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
rsf-open-api/src/main/java/com/vincent/rsf/openApi/OpenApi.java
@@ -4,8 +4,12 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class })
@SpringBootApplication(
    exclude = {SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class}
)
@EnableFeignClients(basePackages = "com.vincent.rsf.openApi.feign")
public class OpenApi {
    public static void main(String[] args) {
        SpringApplication.run(OpenApi.class, args);
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/ERPController.java
@@ -3,21 +3,26 @@
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.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;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static com.vincent.rsf.openApi.controller.AuthController.SIMULATED_DATA_ENABLE;
@@ -32,6 +37,9 @@
    @Resource
    private ErpReportService erpReportService;
    @Autowired(required = false)
    private WmsServerFeignClient wmsServerFeignClient;
    @ApiOperation("仓库信息同步")
@@ -147,7 +155,7 @@
    @ApiOperation("库存查询明细")
    @PostMapping("/inventory/details")
    public CommonResponse queryInventoryDetails(@RequestBody JSONObject params) {
    public CommonResponse queryInventoryDetails(@RequestBody InventoryQueryCondition condition) {
        if (SIMULATED_DATA_ENABLE.equals("1")) {
            String x = "[\n" +
                    "  {\n" +
@@ -193,10 +201,77 @@
            return CommonResponse.ok(JSONArray.parseArray(map.toJSONString(), InventoryDetails.class));
        }
        InventoryQueryCondition condition = JSON.parseObject(params.toJSONString(), InventoryQueryCondition.class);
        // 数据处理,转发server
        List<InventoryDetails> inventoryDetails = Lists.newArrayList();
        return new CommonResponse().setCode(200).setData(inventoryDetails);
        try {
            if (wmsServerFeignClient == null) {
                log.warn("WmsServerFeignClient未注入,无法进行调用");
                return CommonResponse.error("服务未初始化");
            }
            // 参数验证
            if (condition == null) {
                return CommonResponse.error("查询条件不能为空");
            }
            log.info("库存查询明细请求参数: {}", JSON.toJSONString(condition));
            // 直接传递实体类,Feign会自动序列化为JSON
            R result = wmsServerFeignClient.queryInventoryDetails(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列表转换为InventoryDetails列表
                    if (data != null) {
                        @SuppressWarnings("unchecked")
                        List<Map<String, Object>> dataList = (List<Map<String, Object>>) data;
                        List<InventoryDetails> inventoryDetails = new ArrayList<>();
                        for (Map<String, Object> item : dataList) {
                            InventoryDetails details = JSON.parseObject(JSON.toJSONString(item), InventoryDetails.class);
                            inventoryDetails.add(details);
                        }
                        return CommonResponse.ok(inventoryDetails);
                    } else {
                        return CommonResponse.ok(new ArrayList<>());
                    }
                } else {
                    return CommonResponse.error(msg != null ? msg : "查询失败");
                }
            } else {
                return CommonResponse.error("查询失败:返回结果为空");
            }
        } 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 : "未知错误"));
        }
    }
    @ApiOperation("库存查询汇总")
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/WmsConstant.java
@@ -40,6 +40,8 @@
    //移库任务申请
    public static String WCS_CREATE_LOC_MOVE_TASK = "/rsf-server/wcs/createLocMoveTask";
    //库存查询明细(ERP接口,对应open-api的/inventory/details)
    public static final String QUERY_INVENTORY_DETAILS = "/rsf-server/erp/inventory/details";
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Station.java
@@ -47,5 +47,5 @@
    // 是否可用
    @JsonProperty("IsValid")
    @JSONField(name = "IsValid")
    private boolean isValid;
    private Boolean valid;
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/WmsServerFeignClient.java
New file
@@ -0,0 +1,34 @@
package com.vincent.rsf.openApi.feign.wms;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.openApi.entity.constant.WmsConstant;
import com.vincent.rsf.openApi.entity.phyz.InventoryQueryCondition;
import com.vincent.rsf.openApi.feign.wms.fallback.WmsServerFeignClientFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
 * WMS Server模块Feign客户端
 * 用于调用rsf-server模块的接口
 *
 * 注意:url配置从application.yml中读取platform.wms.host和platform.wms.port
 * 如果open-api和server在同一应用中运行,可以配置为本地地址
 * 如果分开部署,url应该配置为server模块的实际地址,如:http://127.0.0.1:8085
 */
@FeignClient(
    name = "wms-server",
    url = "${platform.wms.host:http://127.0.0.1}:${platform.wms.port:8085}",
    path = "",
    fallback = WmsServerFeignClientFallback.class
)
public interface WmsServerFeignClient {
    /**
     * 库存查询明细(调用server端的erpQueryInventoryDetails方法)
     * @param condition 查询条件实体类
     * @return 库存明细列表
     */
    @PostMapping(WmsConstant.QUERY_INVENTORY_DETAILS)
    R queryInventoryDetails(@RequestBody InventoryQueryCondition condition);
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/fallback/WmsServerFeignClientFallback.java
New file
@@ -0,0 +1,21 @@
package com.vincent.rsf.openApi.feign.wms.fallback;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.openApi.entity.phyz.InventoryQueryCondition;
import com.vincent.rsf.openApi.feign.wms.WmsServerFeignClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
 * WMS Server Feign客户端降级处理
 */
@Slf4j
@Component
public class WmsServerFeignClientFallback implements WmsServerFeignClient {
    @Override
    public R queryInventoryDetails(InventoryQueryCondition condition) {
        log.error("调用WMS Server库存查询明细接口失败,触发降级处理");
        return R.error("服务调用失败,请稍后重试");
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/ErpQueryController.java
@@ -1,25 +1,24 @@
package com.vincent.rsf.server.api.controller.erp;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.vincent.rsf.server.api.service.ReceiveMsgService;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.server.api.controller.erp.params.*;
import com.vincent.rsf.server.api.controller.erp.params.InventoryQueryConditionParam;
import com.vincent.rsf.server.api.controller.erp.params.QueryOrderParam;
import com.vincent.rsf.server.api.service.ReceiveMsgService;
import com.vincent.rsf.server.common.annotation.OperationLog;
import com.vincent.rsf.server.common.domain.BaseParam;
import com.vincent.rsf.server.common.domain.PageParam;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.server.manager.entity.Loc;
import com.vincent.rsf.server.manager.entity.Transfer;
import com.vincent.rsf.server.manager.service.MatnrGroupService;
import com.vincent.rsf.server.manager.service.*;
import com.vincent.rsf.server.system.controller.BaseController;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import java.util.Objects;
@@ -104,4 +103,22 @@
        return receiveMsgService.queryTransfer(queryParams);
    }
    /**
     * ERP库存查询明细(供open-api模块调用)
     * 对应open-api的 /inventory/details 接口
     * @param condition 查询条件
     * @return 库存明细列表
     */
    @PostMapping("/inventory/details")
    @ApiOperation(value = "ERP库存查询明细", hidden = true)
    @OperationLog("ERP库存查询明细")
    public R erpQueryInventoryDetails(@RequestBody InventoryQueryConditionParam condition) {
        // 参数验证
        if (condition == null) {
            return R.error("查询条件不能为空");
        }
        return receiveMsgService.erpQueryInventoryDetails(condition);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventoryQueryConditionParam.java
New file
@@ -0,0 +1,129 @@
package com.vincent.rsf.server.api.controller.erp.params;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Map;
/**
 * 库存查询明细条件参数(供ERP调用)
 * 对应open-api的InventoryQueryCondition
 */
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@Accessors(chain = true)
@ApiModel(value = "InventoryQueryConditionParam", description = "库存查询明细条件参数")
public class InventoryQueryConditionParam {
    /**
     * 仓库编码
     */
    @ApiModelProperty(value = "仓库编码")
    private String wareHouseId;
    /**
     * 库位编码
     */
    @ApiModelProperty(value = "库位编码")
    private String locId;
    /**
     * 物料编码
     */
    @ApiModelProperty(value = "物料编码")
    private String matNr;
    /**
     * 订单号/工单号/MES工单号
     */
    @ApiModelProperty(value = "订单号/工单号/MES工单号")
    private String orderNo;
    /**
     * 计划跟踪号
     */
    @ApiModelProperty(value = "计划跟踪号")
    private String planNo;
    /**
     * 批次号
     */
    @ApiModelProperty(value = "批次号")
    private String batch;
    /**
     * 物料组
     */
    @ApiModelProperty(value = "物料组")
    private String matGroup;
    /**
     * 从Map构造InventoryQueryConditionParam对象
     * 用于接收其他方法返回的Map参数并自动转换
     *
     * @param map 查询条件Map
     * @return InventoryQueryConditionParam对象
     */
    public static InventoryQueryConditionParam fromMap(Map<String, Object> map) {
        if (map == null) {
            return null;
        }
        InventoryQueryConditionParam param = new InventoryQueryConditionParam();
        if (map.containsKey("wareHouseId")) {
            param.setWareHouseId((String) map.get("wareHouseId"));
        }
        if (map.containsKey("locId")) {
            param.setLocId((String) map.get("locId"));
        }
        if (map.containsKey("matNr")) {
            param.setMatNr((String) map.get("matNr"));
        }
        if (map.containsKey("orderNo")) {
            param.setOrderNo((String) map.get("orderNo"));
        }
        if (map.containsKey("planNo")) {
            param.setPlanNo((String) map.get("planNo"));
        }
        if (map.containsKey("batch")) {
            param.setBatch((String) map.get("batch"));
        }
        if (map.containsKey("matGroup")) {
            param.setMatGroup((String) map.get("matGroup"));
        }
        return param;
    }
    /**
     * 转换为Map(用于兼容需要Map参数的方法)
     *
     * @return Map对象
     */
    public Map<String, Object> toMap() {
        Map<String, Object> map = new java.util.HashMap<>();
        if (wareHouseId != null) {
            map.put("wareHouseId", wareHouseId);
        }
        if (locId != null) {
            map.put("locId", locId);
        }
        if (matNr != null) {
            map.put("matNr", matNr);
        }
        if (orderNo != null) {
            map.put("orderNo", orderNo);
        }
        if (planNo != null) {
            map.put("planNo", planNo);
        }
        if (batch != null) {
            map.put("batch", batch);
        }
        if (matGroup != null) {
            map.put("matGroup", matGroup);
        }
        return map;
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/api/service/ReceiveMsgService.java
@@ -152,4 +152,11 @@
     * @return
     */
    R matUpdate(BaseMatParms baseMatParms);
    /**
     * 库存查询明细(供open-api调用)
     * @param condition 查询条件
     * @return 库存明细列表
     */
    R erpQueryInventoryDetails(InventoryQueryConditionParam condition);
}
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/ReceiveMsgServiceImpl.java
@@ -47,6 +47,8 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import static com.vincent.rsf.server.manager.enums.OrderWorkType.*;
/**
 * @author Ryan
 * @version 1.0
@@ -108,6 +110,8 @@
    private WaitPakinService waitPakinService;
    @Autowired
    private WarehouseAreasItemServiceImpl warehouseAreasItemService;
    @Autowired
    private LocItemService locItemService;
    /**
@@ -418,7 +422,7 @@
                if (Objects.isNull(one)) {
                    throw new CoolException("单据:" + syncOrder.getOrderNo() + ", 业务类型不存在!!");
                }
                 WkOrder order = asnOrderService.getOne(new LambdaQueryWrapper<WkOrder>()
                WkOrder order = asnOrderService.getOne(new LambdaQueryWrapper<WkOrder>()
                        .eq(!Objects.isNull(syncOrder.getOrderId()), WkOrder::getPoId, syncOrder.getOrderId())
                        .eq(WkOrder::getPoCode, syncOrder.getOrderNo()));
                if (!Objects.isNull(order)) {
@@ -439,7 +443,7 @@
                //银座特供
                String orderNs = null;
                if (rule.equals(SerialRuleCode.SYS_ASN_ORDER) || rule.equals(SerialRuleCode.SYS_OUT_STOCK_CODE) ){
                if (rule.equals(SerialRuleCode.SYS_ASN_ORDER) || rule.equals(SerialRuleCode.SYS_OUT_STOCK_CODE)) {
                    StringBuffer buffer = new StringBuffer();
                    Object poCode = syncOrder.getOrderNo();
                    orderNs = poCode == null ? "" : buffer.append(poCode).toString();
@@ -461,7 +465,7 @@
                        .setCreateBy(loginUserId)
                        .setUpdateBy(loginUserId);
                if (syncOrder.getType().equals(OrderType.ORDER_OUT.type)){
                if (syncOrder.getType().equals(OrderType.ORDER_OUT.type)) {
                    wkOrder.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val);
                }
                if (!asnOrderService.save(wkOrder)) {
@@ -469,10 +473,10 @@
                }
                AtomicReference<String> palletId = new AtomicReference<>();
                syncOrder.getOrderItems().forEach(orderItem -> {
                    if (Cools.isEmpty(palletId.get())){
                    if (Cools.isEmpty(palletId.get())) {
                        palletId.set(orderItem.getPalletId());
                    } else {
                        if (!palletId.get().equals(orderItem.getPalletId())){
                        if (!palletId.get().equals(orderItem.getPalletId())) {
                            throw new CoolException("同一个单据明细中的托盘码必须一致!!!");
                        }
                    }
@@ -549,7 +553,7 @@
            int i = 0;
            while (true) {
                i++;
                if (i>5) return;
                if (i > 5) return;
                Thread.sleep(3000);
                List<WarehouseAreasItem> list = warehouseAreasItemService.list(new LambdaQueryWrapper<WarehouseAreasItem>()
                        .eq(WarehouseAreasItem::getAsnCode, orderNo));
@@ -575,6 +579,7 @@
            log.error("订单 {} 自动组托失败: {}", orderNo, e.getMessage(), e);
        }
    }
    /**
     * @author Ryan
     * @date 2025/8/19
@@ -863,6 +868,7 @@
    /**
     * 基础物料信息变更
     *
     * @param baseMatParms
     * @return
     */
@@ -908,4 +914,270 @@
        return R.ok();
    }
    /**
     * 库存查询明细(供open-api调用)
     *
     * @param condition 查询条件实体类
     * @return 库存明细列表
     */
    @Override
    public R erpQueryInventoryDetails(InventoryQueryConditionParam condition) {
        try {
            // 参数验证
            if (condition == null) {
                return R.error("查询条件不能为空");
            }
            // 将ERP参数映射为Java实体字段名(驼峰格式),PageParam会自动转换为数据库字段名(下划线格式)
            Map<String, Object> queryMap = new HashMap<>();
            // 从实体类中提取查询条件,映射为数据库字段名
            if (StringUtils.isNotBlank(condition.getLocId())) {
                queryMap.put("locCode", condition.getLocId());
            }
            if (StringUtils.isNotBlank(condition.getMatNr())) {
                queryMap.put("matnrCode", condition.getMatNr());
            }
            if (StringUtils.isNotBlank(condition.getPlanNo())) {
                queryMap.put("trackCode", 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);
            // 订单号/工单号/MES工单号
            if (StringUtils.isNotBlank(condition.getOrderNo())) {
                String orderNo = condition.getOrderNo();
                wrapper.and(w -> w.eq("plat_order_code", orderNo)
                        .or().eq("plat_work_code", orderNo));
            }
            // 物料组(需要通过物料表关联查询)
            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<>();
            List<Long> orderIds = locItems.stream()
                    .map(LocItem::getOrderId)
                    .filter(Objects::nonNull)
                    .distinct()
                    .collect(Collectors.toList());
            // 调用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));
            }
            // 调用AsnOrderService查询订单信息(复用Service层方法)
            Map<Long, WkOrder> orderMap = new HashMap<>();
            if (!orderIds.isEmpty()) {
                List<WkOrder> orders = asnOrderService.listByIds(orderIds);
                orderMap = orders.stream().collect(Collectors.toMap(WkOrder::getId, order -> order));
            }
            // 转换结果
            List<Map<String, Object>> result = new ArrayList<>();
            for (LocItem locItem : locItems) {
                Map<String, Object> details = convertToInventoryDetails(locItem, locMap, warehouseMap, orderMap);
                if (details != null) {
                    result.add(details);
                }
            }
            return R.ok().add(result);
        } catch (Exception e) {
            log.error("库存查询明细失败", e);
            return R.error("查询失败:" + e.getMessage());
        }
    }
    /**
     * 转换为库存明细对象
     */
    private Map<String, Object> convertToInventoryDetails(LocItem locItem, Map<Long, Loc> locMap,
                                                          Map<Long, Warehouse> warehouseMap,
                                                          Map<Long, WkOrder> orderMap) {
        Map<String, Object> details = new HashMap<>();
        // 库位编码
        details.put("locId", locItem.getLocCode());
        // 仓库信息
        Loc loc = null;
        if (locItem.getLocId() != null) {
            loc = locMap.get(locItem.getLocId());
        }
        if (loc != null) {
            // 托盘码(从库位的barcode获取)
            details.put("palletId", loc.getBarcode());
            // 仓库信息
            if (loc.getWarehouseId() != null && warehouseMap.containsKey(loc.getWarehouseId())) {
                Warehouse warehouse = warehouseMap.get(loc.getWarehouseId());
                if (warehouse != null) {
                    details.put("wareHouseId", warehouse.getCode());
                    details.put("wareHouseName", warehouse.getName());
                }
            }
        }
        // 物料信息
        details.put("matNr", locItem.getMatnrCode());
        details.put("makTx", locItem.getMaktx());
        details.put("spec", locItem.getSpec());
        details.put("anfme", locItem.getAnfme());
        details.put("unit", locItem.getUnit());
        // 库存状态:1-正常(可用),0-冻结
        if (locItem.getStatus() != null) {
            details.put("status", locItem.getStatus() == 1 ? "可用" : "冻结");
        }
        // 批次号
        details.put("batch", locItem.getBatch());
        // 计划跟踪号
        details.put("planNo", locItem.getTrackCode());
        // 订单信息
        if (locItem.getOrderId() != null && orderMap.containsKey(locItem.getOrderId())) {
            WkOrder order = orderMap.get(locItem.getOrderId());
            if (order != null) {
                // 订单号
                details.put("orderNo", order.getCode());
                details.put("orderType", null);
                // 订单类型:1-出库单,2-入库单,3-调拔单
                // 字符串类型映射:out->1(出库单), in->2(入库单), revise->(调拔单), check->(盘点单))
                if (StringUtils.isNotBlank(order.getType())) {
                    String orderTypeStr = order.getType().toLowerCase().trim();
                    switch (orderTypeStr) {
                        case "out":
                            details.put("orderType", 1);
                            break;
                        case "in":
                            details.put("orderType", 2);
                            break;
                        case "revise":
                            break;
                        case "check":
                            break;
                        default:
                            break;
                    }
                    //包含 备料单关键词就变成3
                    if (StringUtils.isNotBlank(order.getWkType())) {
                        String workDesc = getWorkDesc(order.getWkType());
                        if (workDesc != null && workDesc.contains("备料单")) {
                            details.put("orderType", 3);
                        }
                    }
                }
                // 备料类型:根据业务类型判断
                // 正常领料(1),生产补料(2)
                details.put("prepareType", 1);
                if (StringUtils.isNotBlank(order.getWkType())) {
                    String workDesc = getWorkDesc(order.getWkType());
                    if (workDesc != null && workDesc.contains("生产补料")) {
                        details.put("prepareType", 2);
                    }
                }
            }
        }
        // 如果订单号为空,尝试从platOrderCode或platWorkCode获取
        if (!details.containsKey("orderNo") || details.get("orderNo") == null) {
            if (StringUtils.isNotBlank(locItem.getPlatOrderCode())) {
                details.put("orderNo", locItem.getPlatOrderCode());
            } else if (StringUtils.isNotBlank(locItem.getPlatWorkCode())) {
                details.put("orderNo", locItem.getPlatWorkCode());
            }
        }
        // 库存组织(从useOrgId获取)
        details.put("stockOrgId", locItem.getUseOrgId());
        return details;
    }
}