zhou zhou
11 小时以前 80a6d9236ade191a5de0975abe4de5a6e7e63915
rsf-server/src/main/java/com/vincent/rsf/server/ai/tool/RsfWmsStockTools.java
@@ -27,23 +27,30 @@
    private final LocItemService locItemService;
    private final DeviceSiteService deviceSiteService;
    @Tool(name = "rsf_query_available_inventory", description = "根据物料编码或物料名称查询当前在库且可用于出库的库存明细。")
    /**
     * 查询当前可用于出库的库存明细。
     * 该工具只允许按物料编码或物料名称做定向查询,不允许无条件扫描库存表。
     */
    @Tool(name = "rsf_query_available_inventory", description = "只读查询工具。根据物料编码或物料名称查询当前在库且可用于出库的库存明细。")
    public List<Map<String, Object>> queryAvailableInventory(
            @ToolParam(description = "物料编码,优先使用") String matnr,
            @ToolParam(description = "物料名称,当没有物料编码时使用") String maktx) {
        if (!StringUtils.hasText(matnr) && !StringUtils.hasText(maktx)) {
            throw new CoolException("物料编码或物料名称至少需要提供一个");
        }
            @ToolParam(description = "物料名称,当没有物料编码时使用") String maktx,
            @ToolParam(description = "返回条数,默认 10,最大 50") Integer limit) {
        String normalizedMatnr = BuiltinToolGovernanceSupport.sanitizeQueryText(matnr, "物料编码", 64);
        String normalizedMaktx = BuiltinToolGovernanceSupport.sanitizeQueryText(maktx, "物料名称", 100);
        BuiltinToolGovernanceSupport.requireAnyFilter("物料编码或物料名称至少需要提供一个", normalizedMatnr, normalizedMaktx);
        int finalLimit = BuiltinToolGovernanceSupport.normalizeLimit(limit, 10, 50);
        LambdaQueryWrapper<LocItem> queryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.hasText(matnr)) {
            queryWrapper.eq(LocItem::getMatnrCode, matnr);
        if (StringUtils.hasText(normalizedMatnr)) {
            queryWrapper.eq(LocItem::getMatnrCode, normalizedMatnr);
        } else {
            queryWrapper.eq(LocItem::getMaktx, maktx);
            queryWrapper.like(LocItem::getMaktx, normalizedMaktx);
        }
        queryWrapper.apply(
                "EXISTS (SELECT 1 FROM man_loc ml WHERE ml.use_status = {0} AND ml.id = man_loc_item.loc_id)",
                LocStsType.LOC_STS_TYPE_F.type
        );
        queryWrapper.orderByDesc(LocItem::getId).last("LIMIT " + finalLimit);
        List<LocItem> locItems = locItemService.list(queryWrapper);
        List<Map<String, Object>> result = new ArrayList<>();
        for (LocItem locItem : locItems) {
@@ -69,14 +76,21 @@
        return result;
    }
    @Tool(name = "rsf_query_station_list", description = "根据作业类型列表查询可用站点,返回站点编号、名称、目标位置和状态等信息。")
    /**
     * 查询指定作业类型可用的设备站点。
     * 返回的是模型更容易消费的扁平结构,而不是直接暴露完整实体对象。
     */
    @Tool(name = "rsf_query_station_list", description = "只读查询工具。根据作业类型列表查询可用站点,返回站点编号、名称、目标位置和状态等信息。")
    public List<Map<String, Object>> queryStationList(
            @ToolParam(required = true, description = "作业类型列表") List<String> types) {
        if (types == null || types.isEmpty()) {
            throw new CoolException("站点类型列表不能为空");
        }
            @ToolParam(required = true, description = "作业类型列表") List<String> types,
            @ToolParam(description = "返回条数,默认 20,最大 50") Integer limit) {
        List<String> normalizedTypes = BuiltinToolGovernanceSupport.sanitizeStringList(types, "站点类型列表", 10, 32);
        int finalLimit = BuiltinToolGovernanceSupport.normalizeLimit(limit, 20, 50);
        List<DeviceSite> sites = deviceSiteService.list(new LambdaQueryWrapper<DeviceSite>()
                .in(DeviceSite::getType, types));
                .in(DeviceSite::getType, normalizedTypes)
                .orderByAsc(DeviceSite::getType)
                .orderByAsc(DeviceSite::getSite)
                .last("LIMIT " + finalLimit));
        List<Map<String, Object>> result = new ArrayList<>();
        for (DeviceSite site : sites) {
            Map<String, Object> item = new LinkedHashMap<>();