package tools import ( "encoding/json" "fmt" "sync" "time" "git.x2erp.com/qdy/go-svc-mcp/internal/mcp" ) // 数据库字典表结构 type FieldDictionary struct { TableNameCN string `json:"table_name_cn"` TableNameEN string `json:"table_name_en"` FieldNameCN string `json:"field_name_cn"` FieldNameEN string `json:"field_name_en"` FieldType string `json:"field_type"` StandardName string `json:"standard_name"` Description string `json:"description"` Category string `json:"category"` IsCalculated bool `json:"is_calculated"` Aliases string `json:"aliases"` } // 缓存结构,添加过期时间和最后访问时间 type fieldDictionaryCache struct { data []FieldDictionary expireTime time.Time // 缓存过期时间 lastAccessTime time.Time // 最后访问时间 expireDuration time.Duration // 过期时长 mutex sync.RWMutex // 读写锁 } // 全局缓存实例 var cache *fieldDictionaryCache // 初始化缓存 func initCache() { if cache == nil { cache = &fieldDictionaryCache{ expireDuration: 20 * time.Minute, // 20分钟过期 data: make([]FieldDictionary, 0), } } } // 获取缓存数据(会更新最后访问时间) func getCache() ([]FieldDictionary, bool) { initCache() cache.mutex.RLock() defer cache.mutex.RUnlock() // 检查缓存是否有效 if len(cache.data) == 0 { return nil, false } // 检查是否过期(20分钟没有被访问) if time.Since(cache.lastAccessTime) > cache.expireDuration { return nil, false } // 检查是否到达过期时间 if time.Now().After(cache.expireTime) { return nil, false } return cache.data, true } // 设置缓存数据 func setCache(data []FieldDictionary) { initCache() cache.mutex.Lock() defer cache.mutex.Unlock() cache.data = data cache.lastAccessTime = time.Now() cache.expireTime = time.Now().Add(cache.expireDuration) } // 清除缓存 func clearCache() { initCache() cache.mutex.Lock() defer cache.mutex.Unlock() cache.data = make([]FieldDictionary, 0) cache.lastAccessTime = time.Time{} cache.expireTime = time.Time{} } // 更新最后访问时间 func updateLastAccessTime() { initCache() cache.mutex.Lock() defer cache.mutex.Unlock() cache.lastAccessTime = time.Now() } // 从数据库读取字段字典的方法 func getFieldDictionaryFromDB() ([]FieldDictionary, error) { // TODO: 你来实现这个数据库查询 // 这里返回示例数据用于测试 // 示例查询SQL(根据你的实际表结构调整): /* SELECT table_name_cn, table_name_en, field_name_cn, field_name_en, field_type, standard_name, description, category, is_calculated, aliases FROM system_field_dictionary WHERE tenant_id = ? AND is_active = true ORDER BY category, table_name_cn, field_name_cn */ return []FieldDictionary{ // 销售相关字段 { TableNameCN: "销售订单", TableNameEN: "sales_order", FieldNameCN: "销售数量", FieldNameEN: "sales_quantity", FieldType: "decimal(10,2)", StandardName: "销售数量", Description: "销售订单中的商品数量", Category: "销售", IsCalculated: false, Aliases: "销量,销售数", }, { TableNameCN: "销售订单", TableNameEN: "sales_order", FieldNameCN: "结算单价", FieldNameEN: "settlement_price", FieldType: "decimal(10,2)", StandardName: "结算单价", Description: "销售结算时的单价", Category: "销售", IsCalculated: false, Aliases: "单价,销售单价", }, { TableNameCN: "销售订单", TableNameEN: "sales_order", FieldNameCN: "销售金额", FieldNameEN: "sales_amount", FieldType: "decimal(10,2)", StandardName: "销售金额", Description: "销售总金额(计算字段:销售数量 × 结算单价)", Category: "销售", IsCalculated: true, Aliases: "销售额,销售总额", }, // 采购相关字段 { TableNameCN: "采购订单", TableNameEN: "purchase_order", FieldNameCN: "采购数量", FieldNameEN: "purchase_quantity", FieldType: "decimal(10,2)", StandardName: "采购数量", Description: "采购订单中的商品数量", Category: "采购", IsCalculated: false, Aliases: "进货数量,采购数", }, { TableNameCN: "采购订单", TableNameEN: "purchase_order", FieldNameCN: "采购单价", FieldNameEN: "purchase_price", FieldType: "decimal(10,2)", StandardName: "采购单价", Description: "采购商品单价", Category: "采购", IsCalculated: false, Aliases: "进价,采购价", }, // 库存相关字段 { TableNameCN: "库存表", TableNameEN: "inventory", FieldNameCN: "库存数量", FieldNameEN: "inventory_quantity", FieldType: "decimal(10,2)", StandardName: "库存数量", Description: "当前库存数量", Category: "库存", IsCalculated: false, Aliases: "库存,现存量", }, }, nil } // 加载字段字典(带缓存) func loadFieldDictionary(refresh bool) ([]FieldDictionary, error) { // 如果强制刷新,先清除缓存 if refresh { clearCache() } // 尝试从缓存获取 if data, ok := getCache(); ok { return data, nil } // 从数据库加载 dict, err := getFieldDictionaryFromDB() if err != nil { return nil, fmt.Errorf("加载字段字典失败: %v", err) } // 设置缓存 setCache(dict) return dict, nil } // 查找字段匹配 func findFieldMatch(fieldCN string, dictionary []FieldDictionary) (bool, *FieldDictionary, []string) { // 精确匹配 for _, dict := range dictionary { if dict.FieldNameCN == fieldCN { return true, &dict, nil } } // 别名匹配 var matchedDict *FieldDictionary for _, dict := range dictionary { if dict.Aliases != "" { // 简单的别名匹配(实际使用时你可能需要解析逗号分隔的字符串) if dict.Aliases == fieldCN { matchedDict = &dict break } } } if matchedDict != nil { return true, matchedDict, nil } // 收集所有字段名作为建议 var suggestions []string for _, dict := range dictionary { suggestions = append(suggestions, dict.FieldNameCN) } return false, nil, suggestions } func init() { mcp.Register("field_matcher", "根据中文字段名称匹配数据库字段信息,返回英文字段名、表名、类型等详细信息", map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "fields": map[string]interface{}{ "type": "array", "items": map[string]interface{}{ "type": "string", }, "description": "要匹配的中文字段名称数组", "minItems": 1, }, "refresh_cache": map[string]interface{}{ "type": "boolean", "description": "是否刷新字段字典缓存", "default": false, }, }, "required": []string{"fields"}, }, func(input json.RawMessage, deps *mcp.ToolDependencies) (interface{}, error) { var params struct { Fields []string `json:"fields"` RefreshCache bool `json:"refresh_cache"` } if len(input) > 0 { if err := json.Unmarshal(input, ¶ms); err != nil { return nil, err } } if len(params.Fields) == 0 { return nil, fmt.Errorf("fields 参数不能为空") } // 加载字段字典(带缓存) startTime := time.Now() dictionary, err := loadFieldDictionary(params.RefreshCache) if err != nil { return nil, err } loadTime := time.Since(startTime) // 更新缓存最后访问时间 updateLastAccessTime() // 处理每个字段的匹配结果 matches := make([]map[string]interface{}, 0, len(params.Fields)) foundCount := 0 for _, fieldCN := range params.Fields { isFound, fieldInfo, suggestions := findFieldMatch(fieldCN, dictionary) matchResult := map[string]interface{}{ "input_field_cn": fieldCN, "is_found": isFound, } if isFound && fieldInfo != nil { foundCount++ matchResult["match_info"] = map[string]interface{}{ "table_name_cn": fieldInfo.TableNameCN, "table_name_en": fieldInfo.TableNameEN, "field_name_en": fieldInfo.FieldNameEN, "field_type": fieldInfo.FieldType, "standard_name": fieldInfo.StandardName, "description": fieldInfo.Description, "category": fieldInfo.Category, "is_calculated": fieldInfo.IsCalculated, } } else { matchResult["suggestions"] = suggestions matchResult["note"] = "未找到匹配字段,请尝试其他名称或拆解字段" } matches = append(matches, matchResult) } // 获取缓存信息 cacheData, cacheValid := getCache() cacheInfo := map[string]interface{}{ "is_valid": cacheValid, "total_fields": len(cacheData), } if cacheValid { cacheInfo["last_access_time"] = cache.lastAccessTime.Format(time.RFC3339) cacheInfo["expire_time"] = cache.expireTime.Format(time.RFC3339) cacheInfo["will_expire_in"] = time.Until(cache.expireTime).String() } // 按照示例的返回格式 return map[string]interface{}{ "tenant_id": deps.ReqCtx.TenantID, "user_id": deps.ReqCtx.UserID, "input_fields": params.Fields, "matches": matches, "summary": map[string]interface{}{ "total_fields": len(params.Fields), "found_count": foundCount, "not_found_count": len(params.Fields) - foundCount, "success_rate": fmt.Sprintf("%.1f%%", float64(foundCount)/float64(len(params.Fields))*100), }, "cache_info": cacheInfo, "load_time": loadTime.String(), "timestamp": time.Now().Format(time.RFC3339), "suggestion": "如果未找到匹配字段,请尝试将复合字段拆解为ERP常用基本字段再次查询", }, nil }, ) }