diff --git a/src/Oms.Application/Dtos/Ai/AiDtos.cs b/src/Oms.Application/Dtos/Ai/AiDtos.cs
index 7cb0f98..b217d5d 100644
--- a/src/Oms.Application/Dtos/Ai/AiDtos.cs
+++ b/src/Oms.Application/Dtos/Ai/AiDtos.cs
@@ -16,6 +16,9 @@ public class AiProviderOutput
public string? ApiKey { get; set; }
+ ///
+ /// 默认模型名(历史字段名 ApiSecret,实际不当密钥脱敏)。
+ ///
public string? ApiSecret { get; set; }
public bool IsDefault { get; set; }
@@ -39,6 +42,9 @@ public class AiProviderSaveInput
public string? ApiKey { get; set; }
+ ///
+ /// 默认文字模型 ID;空表示保持原值。
+ ///
public string? ApiSecret { get; set; }
public bool IsDefault { get; set; }
diff --git a/src/Oms.Application/Mcp/McpToolCatalog.cs b/src/Oms.Application/Mcp/McpToolCatalog.cs
index 4ab874f..1b76866 100644
--- a/src/Oms.Application/Mcp/McpToolCatalog.cs
+++ b/src/Oms.Application/Mcp/McpToolCatalog.cs
@@ -40,20 +40,16 @@ public static class McpToolCatalog
new(
ChannelGuide,
"查渠道对客说明",
- "按销售产品代码或名称查询对客说明,判断客户品名是否可承运(禁运/可寄)。",
+ "客户问某品名能不能寄、走什么渠道时调用。必填 cargoName;productCode/productName 可选(已知渠道时传入)。仅品名时按对客说明检索可走渠道。",
"""
{
"type": "object",
"properties": {
- "productCode": { "type": "string", "description": "销售产品代码,优先使用" },
- "productName": { "type": "string", "description": "销售产品名称关键词(无代码时用)" },
- "cargoName": { "type": "string", "description": "客户品名,用于对照说明判断可否承运" }
+ "productCode": { "type": "string", "description": "销售产品代码,可选;客户已指定渠道时优先" },
+ "productName": { "type": "string", "description": "销售产品/渠道名称关键词,可选" },
+ "cargoName": { "type": "string", "description": "客户品名,如 lipstick、化妆品;必填" }
},
- "required": ["cargoName"],
- "anyOf": [
- { "required": ["productCode"] },
- { "required": ["productName"] }
- ]
+ "required": ["cargoName"]
}
""",
20),
diff --git a/src/Oms.Application/Mcp/McpToolExecutor.cs b/src/Oms.Application/Mcp/McpToolExecutor.cs
index c9fb27e..1c075b8 100644
--- a/src/Oms.Application/Mcp/McpToolExecutor.cs
+++ b/src/Oms.Application/Mcp/McpToolExecutor.cs
@@ -153,6 +153,13 @@ public class McpToolExecutor(
var productName = GetString(root, "productName", "ProductName");
var cargoName = GetString(root, "cargoName", "CargoName");
+ if (string.IsNullOrWhiteSpace(cargoName)
+ && string.IsNullOrWhiteSpace(productCode)
+ && string.IsNullOrWhiteSpace(productName))
+ {
+ return (false, "请至少提供 cargoName(客户品名),或 productCode / productName");
+ }
+
var products = new List();
if (!string.IsNullOrWhiteSpace(productCode))
{
@@ -166,14 +173,51 @@ public class McpToolExecutor(
var list = await salesProductRepository.GetListAsync(
x => x.IsActive == EnableStatus.Enabled
&& (x.ProductName.Contains(keyword) || x.ProductCode.Contains(keyword)));
- products.AddRange(list.Take(5));
+ products.AddRange(list.Take(8));
+ }
+
+ // 仅品名:在已启用且配置了对客说明的产品中检索可走渠道
+ if (products.Count == 0 && !string.IsNullOrWhiteSpace(cargoName))
+ {
+ var keyword = cargoName.Trim();
+ var withGuide = await salesProductRepository.GetListAsync(
+ x => x.IsActive == EnableStatus.Enabled
+ && x.CustomerGuideMarkdown != null
+ && x.CustomerGuideMarkdown != "");
+
+ static bool ContainsIgnoreCase(string? haystack, string needle) =>
+ !string.IsNullOrEmpty(haystack)
+ && haystack.Contains(needle, StringComparison.OrdinalIgnoreCase);
+
+ var matched = withGuide
+ .Where(p =>
+ ContainsIgnoreCase(p.CustomerGuideMarkdown, keyword)
+ || ContainsIgnoreCase(p.ProductName, keyword)
+ || ContainsIgnoreCase(p.ProductCode, keyword))
+ .Take(10)
+ .ToList();
+
+ if (matched.Count > 0)
+ {
+ products.AddRange(matched);
+ }
+ else
+ {
+ return (true, JsonSerializer.Serialize(new
+ {
+ items = Array.Empty