| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- 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
- },
- )
- }
|