From a81f5198e3bc1aac0d94db3580d6e522e4e6b829 Mon Sep 17 00:00:00 2001 From: Mrx <18278715334@163.com> Date: Sat, 16 May 2026 15:45:05 +0800 Subject: [PATCH] f --- app/main/api/desc/front/toolbox.api | 32 + app/main/api/etc/main.dev.yaml | 6 + app/main/api/etc/main.yaml | 4 + app/main/api/internal/config/config.go | 12 +- app/main/api/internal/handler/routes.go | 19 + .../handler/toolbox/toolboxlisthandler.go | 17 + .../handler/toolbox/toolboxqueryhandler.go | 29 + .../logic/toolbox/toolboxlistlogic.go | 43 + .../logic/toolbox/toolboxquerylogic.go | 37 + .../service/tianxingjuhe_sdk/client.go | 150 + .../api/internal/service/toolboxService.go | 8071 +++++++++++++++++ app/main/api/internal/svc/servicecontext.go | 17 + app/main/api/internal/types/types.go | 20 + docs/toolbox_implementation.md | 228 + 14 files changed, 8683 insertions(+), 2 deletions(-) create mode 100644 app/main/api/desc/front/toolbox.api create mode 100644 app/main/api/internal/handler/toolbox/toolboxlisthandler.go create mode 100644 app/main/api/internal/handler/toolbox/toolboxqueryhandler.go create mode 100644 app/main/api/internal/logic/toolbox/toolboxlistlogic.go create mode 100644 app/main/api/internal/logic/toolbox/toolboxquerylogic.go create mode 100644 app/main/api/internal/service/tianxingjuhe_sdk/client.go create mode 100644 app/main/api/internal/service/toolboxService.go create mode 100644 docs/toolbox_implementation.md diff --git a/app/main/api/desc/front/toolbox.api b/app/main/api/desc/front/toolbox.api new file mode 100644 index 0000000..5b80c44 --- /dev/null +++ b/app/main/api/desc/front/toolbox.api @@ -0,0 +1,32 @@ +syntax = "v1" + +info ( + title: "工具箱服务" + desc: "免费小工具:手机号归属地、VIN解析、车牌归属地等" + version: "v1" +) + +// ==================== 通用请求/响应 ==================== + +type ( + ToolboxQueryReq { + ToolKey string `json:"tool_key" validate:"required"` + Params map[string]interface{} `json:"params"` + } + ToolboxQueryResp { + ToolKey string `json:"tool_key"` + Result map[string]interface{} `json:"result"` + } +) + +// ==================== 免费接口(无需登录) ==================== + +@server ( + prefix: api/v1 + group: toolbox +) +service main { + @doc "通用工具查询" + @handler toolboxQuery + post /toolbox/query (ToolboxQueryReq) returns (ToolboxQueryResp) +} diff --git a/app/main/api/etc/main.dev.yaml b/app/main/api/etc/main.dev.yaml index 4bef70c..8540ffa 100644 --- a/app/main/api/etc/main.dev.yaml +++ b/app/main/api/etc/main.dev.yaml @@ -97,3 +97,9 @@ Upload: TempFileMaxAgeH: 24 # 临时文件保留时长(小时),超时自动删除,0 表示默认 24 PublicBaseURL: "https://www.tianyuancha.cn" ExtensionTime: 24 # 佣金解冻延迟时间,单位:24小时 + + +tianxingjuhe: + url: "https://apis.tianapi.com" + key: "4ceffb1ffb95b83230b9a9c9df2467e1" + timeout: 30 \ No newline at end of file diff --git a/app/main/api/etc/main.yaml b/app/main/api/etc/main.yaml index 67f526c..d877a79 100644 --- a/app/main/api/etc/main.yaml +++ b/app/main/api/etc/main.yaml @@ -103,6 +103,10 @@ Tianyuanapi: Key: "74902aff197d72d1caa5593560cb281e" BaseURL: "https://api.tianyuanapi.com" Timeout: 60 +tianxingjuhe: + url: "https://apis.tianapi.com" + key: "4ceffb1ffb95b83230b9a9c9df2467e1" + timeout: 30 Authorization: FileBaseURL: "https://www.tianyuancha.cn/api/v1/auth-docs" # 授权书文件访问基础URL Upload: diff --git a/app/main/api/internal/config/config.go b/app/main/api/internal/config/config.go index b2b66c9..64484cd 100644 --- a/app/main/api/internal/config/config.go +++ b/app/main/api/internal/config/config.go @@ -18,6 +18,7 @@ type Config struct { Applepay ApplepayConfig Ali AliConfig Tianyuanapi TianyuanapiConfig + Tianxingjuhe TianxingjuheConfig SystemConfig SystemConfig WechatH5 WechatH5Config Authorization AuthorizationConfig // 授权书配置 @@ -150,6 +151,13 @@ type AuthorizationConfig struct { // UploadConfig 图片上传(行驶证等)配置,临时存储,按 hash 去重 type UploadConfig struct { - FileBaseURL string `json:",optional"` // 上传文件访问基础 URL,如 https://xxx/api/v1/upload/file - TempFileMaxAgeH int `json:",optional"` // 临时文件保留时长(小时),超时删除,0 表示默认 24 小时 + FileBaseURL string `json:"fileBaseURL,omitempty"` // 上传文件访问基础 URL,如 https://xxx/api/v1/upload/file + TempFileMaxAgeH int `json:"tempFileMaxAgeH,omitempty"` // 临时文件保留时长(小时),超时删除,0 表示默认 24 小时 +} + +// TianxingjuheConfig 天行聚合API配置 +type TianxingjuheConfig struct { + URL string // API基础URL + Key string // API密钥 + Timeout int // 超时时间(秒),默认30秒 } diff --git a/app/main/api/internal/handler/routes.go b/app/main/api/internal/handler/routes.go index 6b89ab9..daa2f54 100644 --- a/app/main/api/internal/handler/routes.go +++ b/app/main/api/internal/handler/routes.go @@ -28,6 +28,7 @@ import ( product "tyc-server/app/main/api/internal/handler/product" query "tyc-server/app/main/api/internal/handler/query" tianyuan "tyc-server/app/main/api/internal/handler/tianyuan" + toolbox "tyc-server/app/main/api/internal/handler/toolbox" upload "tyc-server/app/main/api/internal/handler/upload" user "tyc-server/app/main/api/internal/handler/user" "tyc-server/app/main/api/internal/svc" @@ -1175,6 +1176,24 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { rest.WithPrefix("/api/v1"), ) + server.AddRoutes( + []rest.Route{ + { + // 获取工具列表 + Method: http.MethodGet, + Path: "/toolbox/list", + Handler: toolbox.ToolboxListHandler(serverCtx), + }, + { + // 通用工具查询 + Method: http.MethodPost, + Path: "/toolbox/query", + Handler: toolbox.ToolboxQueryHandler(serverCtx), + }, + }, + rest.WithPrefix("/api/v1"), + ) + server.AddRoutes( []rest.Route{ { diff --git a/app/main/api/internal/handler/toolbox/toolboxlisthandler.go b/app/main/api/internal/handler/toolbox/toolboxlisthandler.go new file mode 100644 index 0000000..4ba4433 --- /dev/null +++ b/app/main/api/internal/handler/toolbox/toolboxlisthandler.go @@ -0,0 +1,17 @@ +package toolbox + +import ( + "net/http" + + "tyc-server/app/main/api/internal/logic/toolbox" + "tyc-server/app/main/api/internal/svc" + "tyc-server/common/result" +) + +func ToolboxListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + l := toolbox.NewToolboxListLogic(r.Context(), svcCtx) + resp, err := l.ToolboxList() + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/main/api/internal/handler/toolbox/toolboxqueryhandler.go b/app/main/api/internal/handler/toolbox/toolboxqueryhandler.go new file mode 100644 index 0000000..6a8bb3c --- /dev/null +++ b/app/main/api/internal/handler/toolbox/toolboxqueryhandler.go @@ -0,0 +1,29 @@ +package toolbox + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "tyc-server/app/main/api/internal/logic/toolbox" + "tyc-server/app/main/api/internal/svc" + "tyc-server/app/main/api/internal/types" + "tyc-server/common/result" + "tyc-server/pkg/lzkit/validator" +) + +func ToolboxQueryHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.ToolboxQueryReq + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r, w, err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + l := toolbox.NewToolboxQueryLogic(r.Context(), svcCtx) + resp, err := l.ToolboxQuery(&req) + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/main/api/internal/logic/toolbox/toolboxlistlogic.go b/app/main/api/internal/logic/toolbox/toolboxlistlogic.go new file mode 100644 index 0000000..a7b63aa --- /dev/null +++ b/app/main/api/internal/logic/toolbox/toolboxlistlogic.go @@ -0,0 +1,43 @@ +package toolbox + +import ( + "context" + + "tyc-server/app/main/api/internal/svc" + "tyc-server/app/main/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type ToolboxListLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewToolboxListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ToolboxListLogic { + return &ToolboxListLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *ToolboxListLogic) ToolboxList() (resp *types.ToolboxListResp, err error) { + // 调用toolboxService获取工具列表 + tools := l.svcCtx.ToolboxService.ListTools() + + // 转换为响应格式 + var toolInfos []types.ToolInfo + for _, tool := range tools { + toolInfos = append(toolInfos, types.ToolInfo{ + Key: tool.Key, + Name: tool.Name, + Desc: tool.Desc, + }) + } + + return &types.ToolboxListResp{ + Tools: toolInfos, + }, nil +} diff --git a/app/main/api/internal/logic/toolbox/toolboxquerylogic.go b/app/main/api/internal/logic/toolbox/toolboxquerylogic.go new file mode 100644 index 0000000..3e61365 --- /dev/null +++ b/app/main/api/internal/logic/toolbox/toolboxquerylogic.go @@ -0,0 +1,37 @@ +package toolbox + +import ( + "context" + + "tyc-server/app/main/api/internal/svc" + "tyc-server/app/main/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type ToolboxQueryLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewToolboxQueryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ToolboxQueryLogic { + return &ToolboxQueryLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *ToolboxQueryLogic) ToolboxQuery(req *types.ToolboxQueryReq) (resp *types.ToolboxQueryResp, err error) { + // 调用toolboxService执行工具查询 + result, err := l.svcCtx.ToolboxService.Query(l.ctx, req.ToolKey, req.Params) + if err != nil { + return nil, err + } + + return &types.ToolboxQueryResp{ + ToolKey: req.ToolKey, + Result: result, + }, nil +} diff --git a/app/main/api/internal/service/tianxingjuhe_sdk/client.go b/app/main/api/internal/service/tianxingjuhe_sdk/client.go new file mode 100644 index 0000000..6a4f545 --- /dev/null +++ b/app/main/api/internal/service/tianxingjuhe_sdk/client.go @@ -0,0 +1,150 @@ +package tianxingjuhe + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "time" +) + +// Client 天行聚合API客户端 +type Client struct { + baseURL string + key string + client *http.Client +} + +// Config 客户端配置 +type Config struct { + BaseURL string // API基础URL + Key string // API密钥 + Timeout int // 超时时间(秒) +} + +// Response 通用API响应结构 +type Response struct { + Code int `json:"code"` // 状态码,200表示成功 + Msg string `json:"msg"` // 返回说明 + Result interface{} `json:"result"` // 返回结果集,具体内容根据接口而定 +} + +// NewClient 创建新的客户端实例 +func NewClient(config Config) (*Client, error) { + if config.BaseURL == "" { + return nil, fmt.Errorf("baseURL不能为空") + } + if config.Key == "" { + return nil, fmt.Errorf("key不能为空") + } + + return &Client{ + baseURL: config.BaseURL, + key: config.Key, + client: &http.Client{ + Timeout: time.Duration(config.Timeout) * time.Second, + }, + }, nil +} + +// Get 发送GET请求 +func (c *Client) Get(endpoint string, params map[string]interface{}) (*Response, error) { + // 构建完整URL + fullURL := fmt.Sprintf("%s/%s", c.baseURL, endpoint) + + // 添加请求参数 + queryParams := url.Values{} + for key, value := range params { + queryParams.Set(key, fmt.Sprintf("%v", value)) + } + + // 添加key参数 + queryParams.Set("key", c.key) + + // 拼接查询参数 + if len(queryParams) > 0 { + fullURL = fmt.Sprintf("%s?%s", fullURL, queryParams.Encode()) + } + + // 创建HTTP请求 + req, err := http.NewRequest("GET", fullURL, nil) + if err != nil { + return nil, fmt.Errorf("创建HTTP请求失败: %v", err) + } + + // 设置请求头 + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", "Tianxingjuhe-Go-SDK/1.0.0") + + // 发送请求 + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("发送HTTP请求失败: %v", err) + } + defer resp.Body.Close() + + // 读取响应 + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("读取响应失败: %v", err) + } + + // 解析响应 + var apiResp Response + if err := json.Unmarshal(body, &apiResp); err != nil { + return nil, fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body)) + } + + return &apiResp, nil +} + +// Post 发送POST请求 +func (c *Client) Post(endpoint string, params map[string]interface{}) (*Response, error) { + // 构建完整URL + fullURL := fmt.Sprintf("%s/%s", c.baseURL, endpoint) + + // 添加key参数 + if params == nil { + params = make(map[string]interface{}) + } + params["key"] = c.key + + // 序列化请求体 + requestBody, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("序列化请求体失败: %v", err) + } + + // 创建HTTP请求 + req, err := http.NewRequest("POST", fullURL, bytes.NewBuffer(requestBody)) + if err != nil { + return nil, fmt.Errorf("创建HTTP请求失败: %v", err) + } + + // 设置请求头 + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", "Tianxingjuhe-Go-SDK/1.0.0") + + // 发送请求 + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("发送HTTP请求失败: %v", err) + } + defer resp.Body.Close() + + // 读取响应 + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("读取响应失败: %v", err) + } + + // 解析响应 + var apiResp Response + if err := json.Unmarshal(body, &apiResp); err != nil { + return nil, fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body)) + } + + return &apiResp, nil +} diff --git a/app/main/api/internal/service/toolboxService.go b/app/main/api/internal/service/toolboxService.go new file mode 100644 index 0000000..7054696 --- /dev/null +++ b/app/main/api/internal/service/toolboxService.go @@ -0,0 +1,8071 @@ +package service + +import ( + "context" + "encoding/json" + "fmt" + "math" + "net" + "net/http" + "regexp" + "strconv" + "strings" + "sync" + "time" + tianxingjuhe "tyc-server/app/main/api/internal/service/tianxingjuhe_sdk" + "tyc-server/common/xerr" + "unicode" + + "github.com/zeromicro/go-zero/core/logx" +) + +// ============================================================================ +// 工具箱服务 - 功能清单 +// ============================================================================ +// +// 【统计信息】 +// 总工具数:146 个 +// - 纯计算类工具(无外部依赖):10 个 +// - 天行聚合 SDK 工具:136 个 +// +// 【工具分类】 +// 1️⃣ 实用查询类 (25个) +// ip-location, idcard-info, phone-location, beijing-time, bank-card, +// plate-parse, domain, zone, areadata, citylookup, addressparse, +// telecode, obdcode, charconvert, checkipv6, worldtime, timezone, +// shares, fxrate, oilprice, holiday, zone, scenic, lunar, aqi +// +// 2️⃣ 文字处理类 (20个) +// money-to-chinese, password-strength, days-between-dates, file-size-format, +// text-stats, pinyin, hanzipinyin, duoyinzi, chaizi, xhzd, jf-words, +// lexicon, enwords, ensentence, fanyi, dailytel, charconvert, cnmoney, +// slogan, zimi +// +// 3️⃣ 文化知识类 (35个) +// history-today, ten-why, duilian, gjdm, bcgm, idiom-quiz, poem-fill, +// fairytales, yuanqu, poetry, poetries, shijing, songci, verse, +// tianqishiju, chengyu, caichengyu, chengyujielong, pitlishi, riddle, +// cityriddle, caizimi, zimi, qingshi, flmj, xiehou, proverb, dictum, +// mingyan, sentence, mnpara, mgjuzi, duishici, msdl +// +// 4️⃣ 娱乐趣味类 (25个) +// jixiong, blood, zodiac, xingzuo, star, constellation, dob, saylove, +// caihongpi, dujitang, joke, naowan, wenda, decide, caipu, dream, +// rkl, godreply, tiangou, robotlog, dialogue, story, zmsc, targa, +// hsjz +// +// 5️⃣ 生活资讯类 (25个) +// everyday, woman, pyqwenan, healthskill, healthtip, nutrient, +// surname, jtwfcode, pet, bmi, zhongyao, yaopin, qiaomen, +// areanews, wxnew, wxhottopic, weibohot, nethot, hotword, hotreview, +// bulletin, douyinhot, zaowanwords, wananwords, caihongpi +// +// 6️⃣ 搜索查询类 (16个) +// one, proverb, slogan, chengyu, lexicon, cnmoney, lzmy, baiketiku, +// gjmj, shares, hotlajifenlei, garbage, lajifenlei, anslajifenlei, +// hotreview, domainparse +// +// ============================================================================ +// 🆕 2024年第二批新增工具 (46个) +// ============================================================================ +// timezone, duoyinzi, dailytel, obdcode, chaizi, citylookup, huayu, +// moodpoetry, pcterm, targa, duishici, msdl, shijing, chepai, flmj, +// xhzd, telecode, charconvert, checkipv6, worldtime, wxhottopic, zmsc, +// hsjz, gjmj, cnmoney, lzmy, baiketiku, dialogue, tiangou, robotlog, +// aqi, addressparse, pet, hotreview, anslajifenlei, douyinhot, +// zaowanwords, wananwords, areadata, mgjuzi, songci, story, qingshi, +// lajifenlei, domainparse, hanzipinyin, shorthistory +// +// ============================================================================ + +// ToolboxService 工具箱服务 —— 所有工具业务方法统一在此文件 +type ToolboxService struct { + tianxingjuheClient *tianxingjuhe.Client + processors map[string]ToolProcessor + historyTodayCache sync.Map // key: MMDD -> value: historyTodayCacheItem +} + +// historyTodayCacheItem 历史今天缓存条目 +type historyTodayCacheItem struct { + Data map[string]interface{} + ExpiredAt time.Time +} + +// ToolProcessor 工具处理函数签名 +type ToolProcessor func(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) + +// NewToolboxService 创建工具箱服务实例 +func NewToolboxService(tianxingjuheClient *tianxingjuhe.Client) *ToolboxService { + s := &ToolboxService{ + tianxingjuheClient: tianxingjuheClient, + processors: make(map[string]ToolProcessor), + } + s.registerAllProcessors() + return s +} + +// registerAllProcessors 注册所有工具处理器 +func (s *ToolboxService) registerAllProcessors() { + // 纯计算类工具(无外部依赖) + s.processors["ip-location"] = processIPLocation + s.processors["idcard-info"] = processIDCardInfo + s.processors["beijing-time"] = processBeijingTime + s.processors["bank-card"] = processBankCard + s.processors["plate-parse"] = processPlateParse + s.processors["money-to-chinese"] = processMoneyToChinese + s.processors["password-strength"] = processPasswordStrength + s.processors["days-between-dates"] = processDaysBetweenDates + s.processors["file-size-format"] = processFileSizeFormat + s.processors["text-stats"] = processTextStats + // 天行聚合 SDK 工具 + + s.processors["phone-location"] = s.processPhoneLocation + s.processors["history-today"] = s.processHistoryToday + s.processors["ten-why"] = s.processTenWhy + s.processors["jf-words"] = s.processJFWords // 近义词反义词 + s.processors["duilian"] = s.processDuilian // 经典对联 + s.processors["gjdm"] = s.processGjDm // 古今地名 + s.processors["bcgm"] = s.processBcgm // 本草纲目 + s.processors["idiom-quiz"] = s.processIdiomQuiz // 成语填字 + s.processors["poem-fill"] = s.processPoemFill // 诗词填空 + s.processors["fairytales"] = s.processFairytales // 童话故事 + s.processors["caihongpi"] = s.processCaiHongPi // 彩虹屁 + s.processors["jixiong"] = s.processJixiong // 吉凶测试 + s.processors["surname"] = s.processSurname // 姓氏起源 + s.processors["jtwfcode"] = s.processJtwfcode // 违章代码 + s.processors["healthskill"] = s.processHealthskill // 健康小妙招 + s.processors["pyqwenan"] = s.processPyqwenan // 朋友圈文案 + s.processors["woman"] = s.processWomanNews // 女性新闻 + s.processors["decide"] = s.processDecide // 常识判断题 + s.processors["everyday"] = s.processEveryday // 每日英语 + s.processors["areanews"] = s.processAreaNews // 地区新闻 + s.processors["yuanqu"] = s.processYuanqu // 元曲三百首 + s.processors["bmi"] = s.processBMI // BMI标准体重 + s.processors["zhongyao"] = s.processZhongyao // 中药大全 + s.processors["yaopin"] = s.processYaopin // 药品说明书 + s.processors["slogan"] = s.processSlogan // 猜广告语 + s.processors["zimi"] = s.processZimi // 猜字谜 + s.processors["dujitang"] = s.processDujitang // 毒鸡汤 + s.processors["one"] = s.processOne // ONE一个 + s.processors["proverb"] = s.processProverb // 文化谚语 + s.processors["caichengyu"] = s.processCaichengyu // 猜成语 + s.processors["qiaomen"] = s.processQiaomen // 生活小窍门 + s.processors["riddle"] = s.processRiddle // 谜语大全 + s.processors["healthtip"] = s.processHealthTip // 健康小贴士 + s.processors["nutrient"] = s.processNutrient // 营养素查询 + s.processors["fxrate"] = s.processFxRate // 汇率查询 + s.processors["bulletin"] = s.processBulletin // 新闻简报 + s.processors["caizimi"] = s.processCaizimi // 彩字谜 + s.processors["hotlajifenlei"] = s.processHotLajifenlei // 热搜垃圾分类 + s.processors["oilprice"] = s.processOilPrice // 实时油价 + s.processors["chengyujielong"] = s.processChengyuJielong // 成语接龙 + s.processors["weibohot"] = s.processWeiboHot // 微博热搜 + s.processors["domain"] = s.processDomainResolve // 域名解析 + s.processors["scenic"] = s.processScenic // 景点查询 + s.processors["mingyan"] = s.processMingyan // 名言警句 + s.processors["tianqishiju"] = s.processTianqiShiju // 天气诗句 + s.processors["jieqi"] = s.processJieQi // 节气查询 + s.processors["blood"] = s.processBloodPair // 血型配对 + s.processors["zodiac"] = s.processZodiacPair // 生肖配对 + s.processors["saylove"] = s.processSayLove // 情话大全 + s.processors["star"] = s.processStar // 星座查询 + s.processors["pinyin"] = s.processPinyin // 汉字转拼音 + s.processors["nethot"] = s.processNetHot // 网络热搜 + s.processors["ensentence"] = s.processEnSentence // 英文句子 + s.processors["verse"] = s.processVerse // 诗词名句 + s.processors["poetry"] = s.processPoetry // 古诗词 + s.processors["poetries"] = s.processPoetries // 古诗词集 + s.processors["pitlishi"] = s.processPiTongLiShi // 谐音历史 + s.processors["mnpara"] = s.processMnPara // 美文段落 + s.processors["skl"] = s.processSkl // 十苦乐 + s.processors["sentence"] = s.processSentence // 唯美句子 + s.processors["enwords"] = s.processEnWords // 英语单词 + s.processors["lexicon"] = s.processLexicon // 词典查询 + s.processors["lunar"] = s.processLunar // 农历查询 + s.processors["xingzuo"] = s.processXingZuo // 星座配对 + s.processors["godreply"] = s.processGodReply // 神回复 + s.processors["xiehou"] = s.processXiehou // 歇后语 + s.processors["rkl"] = s.processRKL // 绕口令 + s.processors["cname"] = s.processCName // 起名大全 + s.processors["shares"] = s.processShares // 股票查询 + s.processors["hotword"] = s.processHotWord // 热词 + s.processors["cityriddle"] = s.processCityRiddle // 城市谜语 + s.processors["wenda"] = s.processWenDa // 问答 + s.processors["chengyu"] = s.processChengYu // 成语词典 + s.processors["naowan"] = s.processNaoWan // 脑筋急转弯 + s.processors["dob"] = s.processDOB // 生日书 + s.processors["dictum"] = s.processDictum // 格言 + s.processors["joke"] = s.processJoke // 趣味笑话 + s.processors["dream"] = s.processDream // 周公解梦 + s.processors["caipu"] = s.processCaiPu // 菜谱大全 + s.processors["wxnew"] = s.processWxNew // 微信新闻 + // 新增工具(有天行SDK处理函数) + s.processors["constellation"] = s.processConstellation // 星座运势 + s.processors["fanyi"] = s.processFanyi // 在线翻译 + s.processors["holiday"] = s.processHoliday // 节假日查询 + s.processors["garbage"] = s.processGarbage // 垃圾分类查询 + s.processors["zone"] = s.processZone // 地区编码查询 + + // ─── 第二批新增工具 ─── + s.processors["timezone"] = s.processTimeZone + s.processors["duoyinzi"] = s.processDuoYinZi + s.processors["dailytel"] = s.processDailyTel + s.processors["obdcode"] = s.processObdCode + s.processors["chaizi"] = s.processChaiZi + s.processors["citylookup"] = s.processCityLookup + s.processors["huayu"] = s.processHuaYu + s.processors["moodpoetry"] = s.processMoodPoetry + s.processors["pcterm"] = s.processPcTerm + s.processors["targa"] = s.processTarga + s.processors["duishici"] = s.processDuiShiCi + s.processors["msdl"] = s.processMsdl + s.processors["shijing"] = s.processShiJing + s.processors["chepai"] = s.processChePai + s.processors["flmj"] = s.processFlmj + s.processors["xhzd"] = s.processXhZd + s.processors["telecode"] = s.processTeleCode + s.processors["charconvert"] = s.processCharConvert + s.processors["checkipv6"] = s.processCheckIPv6 + s.processors["worldtime"] = s.processWorldTime + s.processors["wxhottopic"] = s.processWxHotTopic + s.processors["zmsc"] = s.processZmsc + s.processors["hsjz"] = s.processHsjz + s.processors["gjmj"] = s.processGjmj + s.processors["cnmoney"] = s.processCnMoney + s.processors["lzmy"] = s.processLzmy + s.processors["baiketiku"] = s.processBaiKeTiKu + s.processors["dialogue"] = s.processDialogue + s.processors["tiangou"] = s.processTianGou + s.processors["robotlog"] = s.processRobotLog + s.processors["aqi"] = s.processAqi + s.processors["addressparse"] = s.processAddressParse + s.processors["pet"] = s.processPet + s.processors["hotreview"] = s.processHotReview + s.processors["anslajifenlei"] = s.processAnsLaJiFenLei + s.processors["douyinhot"] = s.processDouYinHot + s.processors["zaowanwords"] = s.processZaoAnWords + s.processors["wananwords"] = s.processWanAnWords + s.processors["areadata"] = s.processAreaData + s.processors["mgjuzi"] = s.processMgJuZi + s.processors["songci"] = s.processSongCi + s.processors["story"] = s.processStory + s.processors["qingshi"] = s.processQingShi + s.processors["lajifenlei"] = s.processLaJiFenLei + s.processors["domainparse"] = s.processDomainParse + s.processors["hanzipinyin"] = s.processHanZiPinYin + s.processors["shorthistory"] = s.processShortHistory +} + +var ( + ipv4Regex = regexp.MustCompile(`^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$`) + ipv6Regex = regexp.MustCompile(`^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^::1$|^::$|^([0-9a-fA-F]{1,4}:)*::([0-9a-fA-F]{1,4}:)*$`) +) + +var httpClient = &http.Client{ + Timeout: 3 * time.Second, +} + +// Query 统一入口:根据 toolKey 分发到对应的处理函数 +func (s *ToolboxService) Query(ctx context.Context, toolKey string, params map[string]interface{}) (map[string]interface{}, error) { + processor, ok := s.processors[toolKey] + if !ok { + return nil, xerr.NewErrMsg("不支持的工具类型: " + toolKey) + } + return processor(ctx, params) +} + +// ==================== 具体工具方法 ==================== + +// processIPLocation IP属地查询 +func processIPLocation(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + ip, _ := params["ip"].(string) + if ip == "" { + return nil, xerr.NewErrMsg("IP地址不能为空") + } + + // 内网IP + if net.ParseIP(ip).IsPrivate() { + return map[string]interface{}{ + "ip": ip, + "country": "内网", + "province": "内网", + "city": "内网", + "isp": "内网", + "latitude": 0, + "longitude": 0, + }, nil + } + + // 格式校验 + if !ipv4Regex.MatchString(ip) && !ipv6Regex.MatchString(ip) { + return nil, xerr.NewErrMsg("IP地址格式不正确") + } + + logx.WithContext(ctx).Infof("IP属地查询: %s", ip) + + // 默认空值 + country, province, city, isp := "", "", "", "" + latitude, longitude := 0.0, 0.0 + client := http.Client{Timeout: 3 * time.Second} + + // ====================== + // 1. 主接口:ip-api + // ====================== + apiUrl := fmt.Sprintf("http://ip-api.com/json/%s?lang=zh-CN", ip) + resp, err := client.Get(apiUrl) + if err == nil && resp.StatusCode == 200 { + defer resp.Body.Close() + + // 直接解析成 map,不定义结构体! + var res map[string]interface{} + if err := json.NewDecoder(resp.Body).Decode(&res); err == nil { + if res["status"] == "success" { + country, _ = res["country"].(string) + province, _ = res["regionName"].(string) + city, _ = res["city"].(string) + isp, _ = res["isp"].(string) + latitude, _ = res["lat"].(float64) + longitude, _ = res["lon"].(float64) + + return map[string]interface{}{ + "ip": ip, + "country": country, + "province": province, + "city": city, + "isp": isp, + "latitude": latitude, + "longitude": longitude, + }, nil + } + } + } + + // ====================== + // 2. 降级:淘宝IP + // ====================== + logx.WithContext(ctx).Infof("ip-api 失败,降级淘宝IP: %s", ip) + taobaoUrl := fmt.Sprintf("https://ip.taobao.com/outGetIpInfo?ip=%s&accessKey=alibaba-inc", ip) + resp2, err2 := client.Get(taobaoUrl) + if err2 == nil && resp2.StatusCode == 200 { + defer resp2.Body.Close() + + var res map[string]interface{} + if err := json.NewDecoder(resp2.Body).Decode(&res); err == nil { + data, ok := res["data"].(map[string]interface{}) + if ok && res["code"].(float64) == 0 { + country, _ = data["country"].(string) + province, _ = data["region"].(string) + city, _ = data["city"].(string) + isp, _ = data["isp"].(string) + + return map[string]interface{}{ + "ip": ip, + "country": country, + "province": province, + "city": city, + "isp": isp, + "latitude": 0, + "longitude": 0, + }, nil + } + } + } + + // 全部失败 + return map[string]interface{}{ + "ip": ip, + "country": "", + "province": "", + "city": "", + "isp": "", + "latitude": 0, + "longitude": 0, + }, nil +} + +// processIDCardInfo 身份证归属地查询 + 真伪校验 +func processIDCardInfo(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + idcard, _ := params["idcard"].(string) + if idcard == "" { + return nil, xerr.NewErrMsg("身份证号不能为空") + } + + idcard = strings.ToUpper(idcard) + + // 1. 长度校验 + if len(idcard) != 18 { + return map[string]interface{}{ + "idcard": idcard, + "valid": false, + "reason": "身份证号长度必须为18位", + }, nil + } + + // 2. 格式校验 + if matched, _ := regexp.MatchString(`^\d{17}[\dX]$`, idcard); !matched { + return map[string]interface{}{ + "idcard": idcard, + "valid": false, + "reason": "身份证号格式不正确", + }, nil + } + + // 3. 校验位验证 + weights := []int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2} + checksumMap := []byte{'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'} + + sum := 0 + for i := 0; i < 17; i++ { + v, _ := strconv.Atoi(string(idcard[i])) + sum += v * weights[i] + } + + if checksumMap[sum%11] != idcard[17] { + return map[string]interface{}{ + "idcard": idcard, + "valid": false, + "reason": "身份证校验位错误", + }, nil + } + + // 4. 出生日期 + birthday := fmt.Sprintf("%s-%s-%s", + idcard[6:10], + idcard[10:12], + idcard[12:14], + ) + + // 5. 性别 + gender := "男" + if (int(idcard[16])-'0')%2 == 0 { + gender = "女" + } + + // 6. 年龄 + age := 0 + if t, err := time.Parse("2006-01-02", birthday); err == nil { + now := time.Now() + age = now.Year() - t.Year() + if now.YearDay() < t.YearDay() { + age-- + } + } + + // 7. 行政区划(内置常用省份,可按需扩展) + provinceMap := map[string]string{ + "11": "北京市", + "12": "天津市", + "13": "河北省", + "14": "山西省", + "15": "内蒙古自治区", + "21": "辽宁省", + "22": "吉林省", + "23": "黑龙江省", + "31": "上海市", + "32": "江苏省", + "33": "浙江省", + "34": "安徽省", + "35": "福建省", + "36": "江西省", + "37": "山东省", + "41": "河南省", + "42": "湖北省", + "43": "湖南省", + "44": "广东省", + "45": "广西壮族自治区", + "46": "海南省", + "50": "重庆市", + "51": "四川省", + "52": "贵州省", + "53": "云南省", + "54": "西藏自治区", + "61": "陕西省", + "62": "甘肃省", + "63": "青海省", + "64": "宁夏回族自治区", + "65": "新疆维吾尔自治区", + } + + province := provinceMap[idcard[0:2]] + city := "" + district := "" + + logx.WithContext(ctx).Infof("身份证查询: %s", idcard) + + return map[string]interface{}{ + "idcard": idcard, + "valid": true, + "reason": "", + "province": province, + "city": city, + "district": district, + "birthday": birthday, + "gender": gender, + "age": age, + }, nil +} + +// processPhoneLocation 手机号归属地查询 +func (s *ToolboxService) processPhoneLocation(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 校验必填手机号 + mobile, ok := params["mobile"].(string) + if !ok || mobile == "" { + return nil, xerr.NewErrMsg("mobile 手机号参数不能为空且必须为字符串") + } + reqParams["phone"] = mobile + + // 调用天行SDK 手机号归属地接口 + resp, err := s.tianxingjuheClient.Get("mobilelocal/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据手机号归属地API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("手机号归属地接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析手机号归属地数据失败") + } + + return map[string]interface{}{ + "id": mobile, + "mobile": mobile, + "areaid": resultMap["areaid"], + "areacode": resultMap["areacode"], + "postcode": resultMap["postcode"], + "proviceid": resultMap["proviceid"], + "mobilearea": resultMap["mobilearea"], + "mobiletype": resultMap["mobiletype"], + "mobileprovice": resultMap["mobileprovice"], + }, nil +} + +// processBeijingTime 获取当前北京时间 +func processBeijingTime(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + url := "http://124.222.204.22/api/time/getapi.php?id=88888888&key=88888888&type=20" + + resp, err := httpClient.Get(url) + if err != nil { + logx.WithContext(ctx).Errorf("请求北京时间接口失败: %v", err) + return nil, xerr.NewErrMsg("获取北京时间失败") + } + defer resp.Body.Close() + + var apiResult map[string]interface{} + if err := json.NewDecoder(resp.Body).Decode(&apiResult); err != nil { + return nil, xerr.NewErrMsg("解析北京时间响应失败") + } + + // 接口级错误处理 + if code, ok := apiResult["code"].(float64); ok && code != 200 { + return nil, xerr.NewErrMsg(" 系统繁忙!请一分钟后再试") + } + + // 提取并转换数据 + y, _ := apiResult["y"].(string) + m, _ := apiResult["m"].(string) + d, _ := apiResult["d"].(string) + h, _ := apiResult["h"].(string) + i, _ := apiResult["i"].(string) + s, _ := apiResult["s"].(string) + sjc, _ := apiResult["sjc"].(float64) + wecn, _ := apiResult["wecn"].(string) + + // 构建北京时间字符串 + dateStr := fmt.Sprintf("%s-%s-%s", y, m, d) + timeStr := fmt.Sprintf("%s:%s:%s", h, i, s) + datetimeStr := fmt.Sprintf("%s %s", dateStr, timeStr) + weekdayStr := fmt.Sprintf("星期%s", wecn) + + // 转换返回格式为前端期望的格式 + result := map[string]interface{}{ + "datetime": datetimeStr, + "date": dateStr, + "time": timeStr, + "weekday": weekdayStr, + "timestamp": int64(sjc), + } + + logx.WithContext(ctx).Infof("获取北京时间成功: %s", datetimeStr) + + return result, nil +} + +// processBankCard 银行卡归属地查询 +func processBankCard(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + cardNo, _ := params["card_no"].(string) + if cardNo == "" { + return nil, xerr.NewErrMsg("银行卡号不能为空") + } + + cardNo = strings.ReplaceAll(cardNo, " ", "") + if matched, _ := regexp.MatchString(`^\d{16,19}$`, cardNo); !matched { + return nil, xerr.NewErrMsg("银行卡号格式不正确") + } + + // Luhn 校验 + sum, double := 0, false + for i := len(cardNo) - 1; i >= 0; i-- { + digit, _ := strconv.Atoi(string(cardNo[i])) + if double { + digit *= 2 + if digit > 9 { + digit -= 9 + } + } + sum += digit + double = !double + } + if sum%10 != 0 { + return map[string]interface{}{ + "card_no": cardNo, + "valid": false, + "reason": "校验位错误", + }, nil + } + + // 银行识别 + bank, prefix := "未知银行", cardNo[:6] + switch { + case strings.HasPrefix(prefix, "622848"): + bank = "中国农业银行" + case strings.HasPrefix(prefix, "622260"): + bank = "交通银行" + case strings.HasPrefix(prefix, "436742"): + bank = "中国建设银行" + case strings.HasPrefix(prefix, "622202"): + bank = "中国工商银行" + } + + // 卡种判断 + cardType := "借记卡" + if len(cardNo) == 16 { + cardType = "信用卡" + } + + return map[string]interface{}{ + "card_no": cardNo, + "valid": true, + "bank": bank, + "card_type": cardType, + }, nil +} + +// processPlateParse 车牌号解析 +func processPlateParse(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + plate, _ := params["plate"].(string) + if plate == "" { + return nil, xerr.NewErrMsg("车牌号不能为空") + } + + if matched, _ := regexp.MatchString(`^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤川青藏琼宁][A-Z][A-Z0-9]{4,5}[A-Z0-9挂学警港澳]$`, plate); !matched { + return nil, xerr.NewErrMsg("车牌号格式不正确") + } + + provinceCode := string([]rune(plate)[0]) + provinceMap := map[string]string{ + "京": "北京", "津": "天津", "沪": "上海", "渝": "重庆", + "冀": "河北", "豫": "河南", "云": "云南", "辽": "辽宁", + "黑": "黑龙江", "湘": "湖南", "皖": "安徽", "鲁": "山东", + "新": "新疆", "苏": "江苏", "浙": "浙江", "赣": "江西", + "鄂": "湖北", "桂": "广西", "甘": "甘肃", "晋": "山西", + "蒙": "内蒙古", "陕": "陕西", "吉": "吉林", "闽": "福建", + "贵": "贵州", "粤": "广东", "川": "四川", "青": "青海", + "藏": "西藏", "琼": "海南", "宁": "宁夏", + } + + province := provinceMap[provinceCode] + isNewEnergy := strings.ContainsAny(plate, "DF") + plateType := "普通小型车" + if isNewEnergy { + plateType = "新能源小型车" + } + + return map[string]interface{}{ + "plate": plate, + "plate_type": plateType, + "province": province, + "is_new_energy": isNewEnergy, + }, nil +} + +// processMoneyToChinese 金额转换为中文 +func processMoneyToChinese(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + amount, ok := params["amount"].(float64) + if !ok || amount < 0 || amount > 999999999999 { + return nil, xerr.NewErrMsg("金额不合法") + } + + nums := []string{"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"} + units := []string{"", "拾", "佰", "仟"} + bigUnits := []string{"", "万", "亿"} + + integer := int64(math.Floor(amount)) + decimal := int64(math.Round((amount - float64(integer)) * 100)) + + var result strings.Builder + if integer > 0 { + unitPos, zeroFlag := 0, false + for integer > 0 { + cur := integer % 10 + if cur == 0 { + if !zeroFlag { + result.WriteString(nums[0]) + zeroFlag = true + } + } else { + result.WriteString(nums[cur] + units[unitPos%4]) + zeroFlag = false + } + if unitPos%4 == 3 && !zeroFlag { + result.WriteString(bigUnits[unitPos/4]) + zeroFlag = true + } + unitPos++ + integer /= 10 + } + // 反转 + str := result.String() + runes := []rune(str) + for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { + runes[i], runes[j] = runes[j], runes[i] + } + result.Reset() + result.WriteString(string(runes)) + result.WriteString("元") + } + + if decimal > 0 { + jiao, fen := decimal/10, decimal%10 + if jiao > 0 { + result.WriteString(nums[jiao] + "角") + } + if fen > 0 { + result.WriteString(nums[fen] + "分") + } + } else { + result.WriteString("整") + } + + return map[string]interface{}{ + "amount": amount, + "chinese": result.String(), + }, nil +} + +// processPasswordStrength 密码强度检测 +func processPasswordStrength(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + pwd, _ := params["password"].(string) + if pwd == "" { + return nil, xerr.NewErrMsg("密码不能为空") + } + + length := len(pwd) + hasUpper, hasLower, hasDigit, hasSpecial := false, false, false, false + + for _, r := range pwd { + switch { + case unicode.IsUpper(r): + hasUpper = true + case unicode.IsLower(r): + hasLower = true + case unicode.IsDigit(r): + hasDigit = true + case unicode.IsPunct(r) || unicode.IsSymbol(r): + hasSpecial = true + } + } + + score, tips := 0, []string{} + + if length >= 8 { + score += 25 + tips = append(tips, "长度足够") + } else { + tips = append(tips, "长度不足") + } + + if hasUpper && hasLower { + score += 25 + tips = append(tips, "包含大小写字母") + } else { + tips = append(tips, "建议包含大小写字母") + } + + if hasDigit { + score += 25 + tips = append(tips, "包含数字") + } else { + tips = append(tips, "建议包含数字") + } + + if hasSpecial { + score += 25 + tips = append(tips, "包含特殊字符") + } else { + tips = append(tips, "建议包含特殊字符") + } + + level := "弱" + if score >= 75 { + level = "强" + } else if score >= 50 { + level = "中" + } + + return map[string]interface{}{ + "score": score, + "level": level, + "tips": tips, + }, nil +} + +// processDaysBetweenDates 两个日期相差天数 +func processDaysBetweenDates(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + startDate, _ := params["start_date"].(string) + endDate, _ := params["end_date"].(string) + + if startDate == "" || endDate == "" { + return nil, xerr.NewErrMsg("起止日期不能为空") + } + + layout := "2006-01-02" + start, err1 := time.Parse(layout, startDate) + end, err2 := time.Parse(layout, endDate) + + if err1 != nil || err2 != nil { + return nil, xerr.NewErrMsg("日期格式不正确,应为 YYYY-MM-DD") + } + + if start.After(end) { + start, end = end, start + } + + days := int(end.Sub(start).Hours() / 24) + + return map[string]interface{}{ + "start_date": startDate, + "end_date": endDate, + "days": days, + }, nil +} + +// 文件大小格式化 - 支持相互转换 +func processFileSizeFormat(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + // 获取输入参数 + sizeFloat, ok := params["size"].(float64) + if !ok || sizeFloat < 0 { + return nil, xerr.NewErrMsg("size 必须为非负数字") + } + + inputUnit, _ := params["input_unit"].(string) + outputUnit, _ := params["output_unit"].(string) + + // 如果没有指定输入单位,默认为字节 + if inputUnit == "" { + inputUnit = "B" + } + + // 如果没有指定输出单位,自动选择合适的单位 + if outputUnit == "" { + outputUnit = "auto" + } + + // 单位映射 + unitMap := map[string]float64{ + "B": 1, + "KB": 1024, + "MB": 1024 * 1024, + "GB": 1024 * 1024 * 1024, + "TB": 1024 * 1024 * 1024 * 1024, + } + + // 转换为字节 + bytes := sizeFloat * unitMap[inputUnit] + if bytes < 0 { + return nil, xerr.NewErrMsg("转换结果不能为负数") + } + + result := map[string]interface{}{ + "input_size": sizeFloat, + "input_unit": inputUnit, + "bytes": bytes, + } + + // 如果输出单位是 "auto",自动选择合适的单位 + if outputUnit == "auto" { + units := []string{"B", "KB", "MB", "GB", "TB"} + i := 0 + value := bytes + + for value >= 1024 && i < len(units)-1 { + value /= 1024 + i++ + } + + result["output_size"] = value + result["output_unit"] = units[i] + result["human"] = fmt.Sprintf("%.2f %s", value, units[i]) + } else { + // 指定单位转换 + if _, exists := unitMap[outputUnit]; !exists { + return nil, xerr.NewErrMsg(fmt.Sprintf("不支持的输出单位: %s", outputUnit)) + } + + outputSize := bytes / unitMap[outputUnit] + result["output_size"] = outputSize + result["output_unit"] = outputUnit + result["human"] = fmt.Sprintf("%.2f %s", outputSize, outputUnit) + } + + return result, nil +} + +// processHistoryToday 历史今天查询 +// processHistoryToday 历史今天查询(默认今天,3天缓存) +func (s *ToolboxService) processHistoryToday(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + dateStr, _ := params["date"].(string) + if dateStr == "" { + // 默认取今天的 MMDD + now := time.Now() + dateStr = fmt.Sprintf("%02d%02d", now.Month(), now.Day()) + } + + // 验证日期格式 (MMDD) + if len(dateStr) != 4 { + return nil, xerr.NewErrMsg("date 参数格式不正确,应为 MMDD 格式(如 0101)") + } + + month := dateStr[:2] + day := dateStr[2:4] + + monthInt, err := strconv.Atoi(month) + if err != nil || monthInt < 1 || monthInt > 12 { + return nil, xerr.NewErrMsg("月份参数不正确,应为 01-12") + } + + dayInt, err := strconv.Atoi(day) + if err != nil || dayInt < 1 || dayInt > 31 { + return nil, xerr.NewErrMsg("日期参数不正确,应为 01-31") + } + + // 查缓存 + if cached, ok := s.historyTodayCache.Load(dateStr); ok { + item := cached.(historyTodayCacheItem) + if time.Now().Before(item.ExpiredAt) { + logx.WithContext(ctx).Infof("[历史今天] 缓存命中: date=%s, 过期时间=%s", dateStr, item.ExpiredAt.Format("2006-01-02 15:04:05")) + return item.Data, nil + } + // 缓存过期,删除 + s.historyTodayCache.Delete(dateStr) + logx.WithContext(ctx).Infof("[历史今天] 缓存已过期,已删除: date=%s", dateStr) + } + + // 调用天行聚合 SDK + resp, err := s.tianxingjuheClient.Get("lishi/index", map[string]interface{}{ + "date": dateStr, + }) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析历史今天数据失败") + } + + data := map[string]interface{}{ + "list": resultMap["list"], + } + + // 写入缓存,3天后过期 + item := historyTodayCacheItem{ + Data: data, + ExpiredAt: time.Now().Add(3 * 24 * time.Hour), + } + s.historyTodayCache.Store(dateStr, item) + + logx.WithContext(ctx).Infof("[历史今天] API调用成功并写入缓存: date=%s, 过期时间=%s", dateStr, item.ExpiredAt.Format("2006-01-02 15:04:05")) + + return data, nil +} + +// processTenWhy 十万个为什么查询 +func (s *ToolboxService) processTenWhy(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + // 构建请求参数 + reqParams := map[string]interface{}{} + + if num, ok := params["num"].(float64); ok { + numInt := int(num) + if numInt < 1 || numInt > 10 { + return nil, xerr.NewErrMsg("num 参数范围为 1-10") + } + reqParams["num"] = numInt + } + + if page, ok := params["page"].(float64); ok { + pageInt := int(page) + if pageInt < 1 { + return nil, xerr.NewErrMsg("page 参数不能小于 1") + } + reqParams["page"] = pageInt + } + + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + + if typeid, ok := params["typeid"].(float64); ok { + typeidInt := int(typeid) + if typeidInt < 1 || typeidInt > 11 { + return nil, xerr.NewErrMsg("typeid 参数范围为 1-11") + } + reqParams["typeid"] = typeidInt + } + + // 调用天行聚合 SDK + resp, err := s.tianxingjuheClient.Get("tenwhy/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据API失败: %v", err)) + } + + // 接口级错误处理 + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("接口返回异常: %s", resp.Msg)) + } + + // 提取 result 数据 + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析十万个为什么数据失败") + } + + return map[string]interface{}{ + "list": resultMap["list"], + }, nil +} + +// 文本字数统计 +func processTextStats(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + text, _ := params["text"].(string) + if text == "" { + return map[string]interface{}{ + "chars": 0, + "words": 0, + "lines": 0, + }, nil + } + + chars := len([]rune(text)) + + words := 0 + for _, w := range strings.Fields(text) { + if strings.TrimSpace(w) != "" { + words++ + } + } + + lines := 1 + for _, r := range text { + if r == '\n' { + lines++ + } + } + + return map[string]interface{}{ + "chars": chars, + "words": words, + "lines": lines, + }, nil +} + +// processJFWords 近义词反义词查询(适合批量替换) +func (s *ToolboxService) processJFWords(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + // 构建请求参数 + reqParams := map[string]interface{}{} + + // 校验并获取必填参数 word(查询词) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("word 参数不能为空且必须为字符串类型") + } + reqParams["word"] = word + + // 调用天行聚合 SDK(近义词反义词接口:jfwords/index) + resp, err := s.tianxingjuheClient.Get("jfwords/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据近义词反义词API失败: %v", err)) + } + + // 接口级错误处理 + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("接口返回异常: %s", resp.Msg)) + } + + // 提取 result 数据 + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析近义词反义词数据失败") + } + + // 返回近义词、反义词、查询词(字段可能为空,调用方需做非空判断) + return map[string]interface{}{ + "word": resultMap["word"], // 查询词 + "jyc": resultMap["jyc"], // 近义词(可能为空) + "fyc": resultMap["fyc"], // 反义词(可能为空) + }, nil +} + +// processDuilian 经典中华文化对联句子查询 +func (s *ToolboxService) processDuilian(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + // 本接口无额外业务请求参数,只需SDK内部携带key + reqParams := map[string]interface{}{} + + // 调用天行聚合 SDK 经典对联接口 + resp, err := s.tianxingjuheClient.Get("duilian/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据对联API失败: %v", err)) + } + + // 接口级错误处理 + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("接口返回异常: %s", resp.Msg)) + } + + // 解析result数据 + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析经典对联数据失败") + } + + // 返回对联内容,业务端自行做非空判断 + return map[string]interface{}{ + "content": resultMap["content"], + }, nil +} + +// processGjDm 古今地名查询 +func (s *ToolboxService) processGjDm(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + // 构建请求参数 + reqParams := map[string]interface{}{} + + // 校验必填古地名 word + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("word 古地名参数不能为空且必须为字符串") + } + reqParams["word"] = word + + // 调用天行聚合 SDK 古今地名接口 + resp, err := s.tianxingjuheClient.Get("gjdm/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据古今地名API失败: %v", err)) + } + + // 接口业务状态码校验 + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("古今地名接口返回异常: %s", resp.Msg)) + } + + // 解析result数据 + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析古今地名数据失败") + } + + // 返回古地名、沿革介绍,调用方自行做非空判断 + return map[string]interface{}{ + "area": resultMap["area"], + "introduce": resultMap["introduce"], + }, nil +} + +// processBcgm 本草纲目药材查询 +func (s *ToolboxService) processBcgm(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + // 构建请求参数 + reqParams := map[string]interface{}{} + + // 必传药材名称 name + name, ok := params["name"].(string) + if !ok || name == "" { + return nil, xerr.NewErrMsg("name 药材名称参数不能为空且必须为字符串") + } + reqParams["name"] = name + + // 可选 num 数量 1-10 + if num, ok := params["num"].(float64); ok { + numInt := int(num) + if numInt < 1 || numInt > 10 { + return nil, xerr.NewErrMsg("num 参数范围为 1-10") + } + reqParams["num"] = numInt + } + + // 可选 page 页码 最小1 + if page, ok := params["page"].(float64); ok { + pageInt := int(page) + if pageInt < 1 { + return nil, xerr.NewErrMsg("page 参数不能小于 1") + } + reqParams["page"] = pageInt + } + + // 调用天行SDK 本草纲目接口 + resp, err := s.tianxingjuheClient.Get("bcgm/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据本草纲目API失败: %v", err)) + } + + // 接口状态码校验 + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("本草纲目接口返回异常: %s", resp.Msg)) + } + + // 解析result + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析本草纲目数据失败") + } + + // 返回列表 + return map[string]interface{}{ + "list": resultMap["list"], + }, nil +} + +// processIdiomQuiz 成语填字题目查询 +func (s *ToolboxService) processIdiomQuiz(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + // 接口无额外自定义参数,仅内置key即可请求 + reqParams := map[string]interface{}{} + + // 调用天行聚合SDK 成语填字接口 + resp, err := s.tianxingjuheClient.Get("idiomquiz/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据成语填字API失败: %v", err)) + } + + // 接口业务状态码校验 + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("成语填字接口返回异常: %s", resp.Msg)) + } + + // 解析result数据 + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析成语填字数据失败") + } + + // 返回所有题目、答案、选项、解释字段 + return map[string]interface{}{ + "diff": resultMap["diff"], + "full": resultMap["full"], + "explan": resultMap["explan"], + "correct": resultMap["correct"], + "wrong_a": resultMap["wrong_a"], + "wrong_b": resultMap["wrong_b"], + "wrong_c": resultMap["wrong_c"], + "question": resultMap["question"], + }, nil +} + +// processPoemFill 诗词填空题目查询 +func (s *ToolboxService) processPoemFill(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 调用天行聚合SDK 诗词填空接口 + resp, err := s.tianxingjuheClient.Get("poemfill/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据诗词填空API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("诗词填空接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析诗词填空数据失败") + } + + return map[string]interface{}{ + "diff": resultMap["diff"], + "full": resultMap["full"], + "note": resultMap["note"], + "title": resultMap["title"], + "author": resultMap["author"], + "correct": resultMap["correct"], + "dynasty": resultMap["dynasty"], + "wrong_a": resultMap["wrong_a"], + "wrong_b": resultMap["wrong_b"], + "wrong_c": resultMap["wrong_c"], + "category": resultMap["category"], + "question": resultMap["question"], + }, nil +} + +// processFairytales 童话故事详情查询 +func (s *ToolboxService) processFairytales(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 校验必填故事ID + id, ok := params["id"].(string) + if !ok || id == "" { + return nil, xerr.NewErrMsg("id 故事ID参数不能为空且必须为字符串") + } + reqParams["id"] = id + + // 调用天行SDK 童话故事接口 + resp, err := s.tianxingjuheClient.Get("fairytales/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据童话故事API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("童话故事接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析童话故事数据失败") + } + + return map[string]interface{}{ + "id": resultMap["id"], + "title": resultMap["title"], + "typeid": resultMap["typeid"], + "content": resultMap["content"], + }, nil +} + +// processCaiHongPi 随机彩虹屁语录 +func (s *ToolboxService) processCaiHongPi(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 调用天行聚合SDK 彩虹屁接口 + resp, err := s.tianxingjuheClient.Get("caihongpi/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据彩虹屁API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("彩虹屁接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析彩虹屁数据失败") + } + + return map[string]interface{}{ + "content": resultMap["content"], + }, nil +} + +// processJixiong 吉凶测试查询 +func (s *ToolboxService) processJixiong(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 校验必填数字号码 + number, ok := params["number"].(string) + if !ok || number == "" { + return nil, xerr.NewErrMsg("number 数字号码参数不能为空且必须为字符串") + } + reqParams["number"] = number + + // 调用天行SDK 吉凶测试接口 + resp, err := s.tianxingjuheClient.Get("jixiong/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据吉凶测试API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("吉凶测试接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析吉凶测试数据失败") + } + + return map[string]interface{}{ + "number": number, + "score": resultMap["score"], + "shuli": resultMap["shuli"], + "gaishu": resultMap["gaishu"], + "result": resultMap["result"], + "conclusion": resultMap["conclusion"], + }, nil +} + +// processSurname 姓氏起源查询 +func (s *ToolboxService) processSurname(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 校验必填姓氏参数 + xing, ok := params["xing"].(string) + if !ok || xing == "" { + return nil, xerr.NewErrMsg("xing 姓氏参数不能为空且必须为字符串") + } + reqParams["xing"] = xing + + // 调用天行SDK 姓氏起源接口 + resp, err := s.tianxingjuheClient.Get("surname/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据姓氏起源API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("姓氏起源接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析姓氏起源数据失败") + } + + return map[string]interface{}{ + "xing": resultMap["xing"], + "content": resultMap["content"], + }, nil +} + +// processJtwfcode 违章代码查询 +func (s *ToolboxService) processJtwfcode(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 校验必填违章代码 + code, ok := params["code"].(string) + if !ok || code == "" { + return nil, xerr.NewErrMsg("code 违章代码参数不能为空且必须为字符串") + } + reqParams["code"] = code + + // 调用天行SDK 违章代码查询接口 + resp, err := s.tianxingjuheClient.Get("jtwfcode/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据违章代码API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("违章代码接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析违章代码数据失败") + } + + return map[string]interface{}{ + "code": resultMap["code"], + "fine": resultMap["fine"], + "deduct": resultMap["deduct"], + "others": resultMap["others"], + "behavior": resultMap["behavior"], + }, nil +} + +// processHealthskill 健康小妙招查询 +func (s *ToolboxService) processHealthskill(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 校验必填关键词 + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("word 关键词参数不能为空且必须为字符串") + } + reqParams["word"] = word + + // 调用天行SDK 健康小妙招接口 + resp, err := s.tianxingjuheClient.Get("healthskill/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据健康小妙招API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("健康小妙招接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析健康小妙招数据失败") + } + + list, ok := resultMap["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析健康小妙招列表失败") + } + + var skills []map[string]interface{} + for _, item := range list { + if m, ok := item.(map[string]interface{}); ok { + skills = append(skills, map[string]interface{}{ + "title": m["title"], + "content": m["content"], + }) + } + } + + return map[string]interface{}{ + "list": skills, + }, nil +} + +// processPyqwenan 朋友圈文案查询 +func (s *ToolboxService) processPyqwenan(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 无额外业务参数,直接调用天行接口 + resp, err := s.tianxingjuheClient.Get("pyqwenan/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据朋友圈文案API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("朋友圈文案接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析朋友圈文案数据失败") + } + + return map[string]interface{}{ + "source": resultMap["source"], + "content": resultMap["content"], + }, nil +} + +// processWomanNews 女性新闻查询 +func (s *ToolboxService) processWomanNews(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 可选参数:条数、页码、随机、关键词 + if num, ok := params["num"].(string); ok && num != "" { + reqParams["num"] = num + } + if page, ok := params["page"].(string); ok && page != "" { + reqParams["page"] = page + } + if rand, ok := params["rand"].(string); ok && rand != "" { + reqParams["rand"] = rand + } + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + // 固定传form=1 + reqParams["form"] = "1" + + // 调用天行SDK 女性新闻接口 + resp, err := s.tianxingjuheClient.Get("woman/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据女性新闻API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("女性新闻接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析女性新闻数据失败") + } + + // 解析新闻列表 + listArr, ok := resultMap["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析女性新闻列表失败") + } + + var newsList []map[string]interface{} + for _, item := range listArr { + if m, ok := item.(map[string]interface{}); ok { + newsList = append(newsList, map[string]interface{}{ + "id": m["id"], + "url": m["url"], + "ctime": m["ctime"], + "title": m["title"], + "picUrl": m["picUrl"], + "source": m["source"], + "description": m["description"], + }) + } + } + + return map[string]interface{}{ + "list": newsList, + "allnum": resultMap["allnum"], + "curpage": resultMap["curpage"], + }, nil +} + +// processDecide 常识判断题查询 +func (s *ToolboxService) processDecide(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 无额外业务参数,直接调用接口 + resp, err := s.tianxingjuheClient.Get("decide/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据判断题API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("判断题接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析判断题数据失败") + } + + return map[string]interface{}{ + "title": resultMap["title"], + "answer": resultMap["answer"], + "analyse": resultMap["analyse"], + }, nil +} + +// processEveryday 每日英语查询 +func (s *ToolboxService) processEveryday(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 无业务自定义参数,直接调用天行接口 + resp, err := s.tianxingjuheClient.Get("everyday/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据每日英语API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("每日英语接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析每日英语数据失败") + } + + return map[string]interface{}{ + "id": resultMap["id"], + "date": resultMap["date"], + "note": resultMap["note"], + "source": resultMap["source"], + "content": resultMap["content"], + }, nil +} + +// processAreaNews 地区新闻查询 +func (s *ToolboxService) processAreaNews(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 校验必填地区名称 + areaname, ok := params["areaname"].(string) + if !ok || areaname == "" { + return nil, xerr.NewErrMsg("areaname 地区名称参数不能为空且必须为字符串") + } + reqParams["areaname"] = areaname + + // 可选参数:页码 + if page, ok := params["page"].(string); ok && page != "" { + reqParams["page"] = page + } + + // 可选参数:关键词 + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + + // 调用天行SDK 地区新闻接口 + resp, err := s.tianxingjuheClient.Get("areanews/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据地区新闻API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("地区新闻接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析地区新闻数据失败") + } + + // 解析新闻列表 + listArr, ok := resultMap["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析地区新闻列表失败") + } + + var newsList []map[string]interface{} + for _, item := range listArr { + if m, ok := item.(map[string]interface{}); ok { + newsList = append(newsList, map[string]interface{}{ + "id": m["id"], + "url": m["url"], + "ctime": m["ctime"], + "title": m["title"], + "picUrl": m["picUrl"], + "source": m["source"], + "description": m["description"], + }) + } + } + + return map[string]interface{}{ + "list": newsList, + }, nil +} + +// processYuanqu 元曲三百首查询 +func (s *ToolboxService) processYuanqu(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 必填参数 数量、页码 + num, ok := params["num"].(string) + if !ok || num == "" { + return nil, xerr.NewErrMsg("num 返回数量参数不能为空且必须为字符串") + } + reqParams["num"] = num + + page, ok := params["page"].(string) + if !ok || page == "" { + return nil, xerr.NewErrMsg("page 页码参数不能为空且必须为字符串") + } + reqParams["page"] = page + + // 可选 关键词搜索 + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + + // 调用天行SDK 元曲三百首接口 + resp, err := s.tianxingjuheClient.Get("yuanqu/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据元曲三百首API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("元曲三百首接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析元曲三百首数据失败") + } + + listArr, ok := resultMap["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析元曲三百首列表失败") + } + + var yuanquList []map[string]interface{} + for _, item := range listArr { + if m, ok := item.(map[string]interface{}); ok { + yuanquList = append(yuanquList, map[string]interface{}{ + "note": m["note"], + "title": m["title"], + "author": m["author"], + "chapter": m["chapter"], + "content": m["content"], + "translation": m["translation"], + }) + } + } + + return map[string]interface{}{ + "list": yuanquList, + }, nil +} + +// processBMI BMI标准体重计算 +func (s *ToolboxService) processBMI(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 校验必填身高 + height, ok := params["height"].(string) + if !ok || height == "" { + return nil, xerr.NewErrMsg("height 身高参数不能为空且必须为字符串") + } + reqParams["height"] = height + + // 校验必填体重 + weight, ok := params["weight"].(string) + if !ok || weight == "" { + return nil, xerr.NewErrMsg("weight 体重参数不能为空且必须为字符串") + } + reqParams["weight"] = weight + + // 可选性别 + if sex, ok := params["sex"].(string); ok && sex != "" { + reqParams["sex"] = sex + } + + // 调用天行SDK BMI接口 + resp, err := s.tianxingjuheClient.Get("bmi/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据BMI标准体重API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("BMI标准体重接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析BMI标准体重数据失败") + } + + return map[string]interface{}{ + "bmi": resultMap["bmi"], + "tip": resultMap["tip"], + "healthy": resultMap["healthy"], + "normbmi": resultMap["normbmi"], + "normweight": resultMap["normweight"], + "idealweight": resultMap["idealweight"], + }, nil +} + +// processZhongyao 中药大全查询 +func (s *ToolboxService) processZhongyao(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 校验必填中药名称 + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("word 中草药名称参数不能为空且必须为字符串") + } + reqParams["word"] = word + + // 可选返回数量 + if num, ok := params["num"].(string); ok && num != "" { + reqParams["num"] = num + } + // 可选页码 + if page, ok := params["page"].(string); ok && page != "" { + reqParams["page"] = page + } + + // 调用天行SDK 中药大全接口 + resp, err := s.tianxingjuheClient.Get("zhongyao/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据中药大全API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("中药大全接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析中药大全数据失败") + } + + return map[string]interface{}{ + "title": resultMap["title"], + "content": resultMap["content"], + }, nil +} + +// processYaopin 药品说明书查询 +func (s *ToolboxService) processYaopin(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 校验必填药品名称 + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("word 药品名称参数不能为空且必须为字符串") + } + reqParams["word"] = word + + // 可选参数:数量 + if num, ok := params["num"].(string); ok && num != "" { + reqParams["num"] = num + } + + // 可选参数:页码 + if page, ok := params["page"].(string); ok && page != "" { + reqParams["page"] = page + } + + // 调用天行SDK 药品说明书接口 + resp, err := s.tianxingjuheClient.Get("yaopin/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据药品说明书API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("药品说明书接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析药品说明书数据失败") + } + + return map[string]interface{}{ + "title": resultMap["title"], + "content": resultMap["content"], + }, nil +} + +// processSlogan 猜广告语查询 +func (s *ToolboxService) processSlogan(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 调用天行SDK 猜广告语接口 + resp, err := s.tianxingjuheClient.Get("slogan/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据猜广告语API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("猜广告语接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析猜广告语数据失败") + } + + return map[string]interface{}{ + "answer": resultMap["answer"], + "content": resultMap["content"], + }, nil +} + +// processZimi 猜字谜查询 +func (s *ToolboxService) processZimi(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 调用天行SDK 猜字谜接口 + resp, err := s.tianxingjuheClient.Get("zimi/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据猜字谜API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("猜字谜接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析猜字谜数据失败") + } + + return map[string]interface{}{ + "answer": resultMap["answer"], + "reason": resultMap["reason"], + "content": resultMap["content"], + }, nil +} + +// processDujitang 毒鸡汤语录查询 +func (s *ToolboxService) processDujitang(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 调用天行SDK 毒鸡汤接口 + resp, err := s.tianxingjuheClient.Get("dujitang/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据毒鸡汤API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("毒鸡汤接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析毒鸡汤数据失败") + } + + return map[string]interface{}{ + "content": resultMap["content"], + }, nil +} + +// processOne ONE一个每日句子 +func (s *ToolboxService) processOne(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 可选指定日期 + if date, ok := params["date"].(string); ok && date != "" { + reqParams["date"] = date + } + // 可选是否随机 + if rand, ok := params["rand"].(string); ok && rand != "" { + reqParams["rand"] = rand + } + + // 调用天行SDK ONE一个接口 + resp, err := s.tianxingjuheClient.Get("one/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据ONE一个API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("ONE一个接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析ONE一个数据失败") + } + + return map[string]interface{}{ + "date": resultMap["date"], + "word": resultMap["word"], + "oneid": resultMap["oneid"], + "imgurl": resultMap["imgurl"], + "wordfrom": resultMap["wordfrom"], + "imgauthor": resultMap["imgauthor"], + }, nil +} + +// processProverb 文化谚语查询 +func (s *ToolboxService) processProverb(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 调用天行SDK 文化谚语接口 + resp, err := s.tianxingjuheClient.Get("proverb/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据文化谚语API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("文化谚语接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析文化谚语数据失败") + } + + return map[string]interface{}{ + "front": resultMap["front"], + "behind": resultMap["behind"], + }, nil +} + +// processCaichengyu 猜成语查询 +func (s *ToolboxService) processCaichengyu(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 调用天行SDK 猜成语接口 + resp, err := s.tianxingjuheClient.Get("caichengyu/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据猜成语API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("猜成语接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析猜成语数据失败") + } + + return map[string]interface{}{ + "abbr": resultMap["abbr"], + "study": resultMap["study"], + "answer": resultMap["answer"], + "pinyin": resultMap["pinyin"], + "source": resultMap["source"], + "question": resultMap["question"], + }, nil +} + +// processQiaomen 生活小窍门查询 +func (s *ToolboxService) processQiaomen(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 调用天行SDK 生活小窍门接口 + resp, err := s.tianxingjuheClient.Get("qiaomen/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据生活小窍门API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("生活小窍门接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析生活小窍门数据失败") + } + + return map[string]interface{}{ + "content": resultMap["content"], + }, nil +} + +// processRiddle 谜语大全查询 +func (s *ToolboxService) processRiddle(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 调用天行SDK 谜语大全接口 + resp, err := s.tianxingjuheClient.Get("riddle/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据谜语大全API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("谜语大全接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析谜语大全数据失败") + } + + return map[string]interface{}{ + "quest": resultMap["quest"], + "answer": resultMap["answer"], + }, nil +} + +// processHealthTip 健康小提示 +func (s *ToolboxService) processHealthTip(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + // 1. 请求参数(该接口无需额外参数) + reqParams := map[string]interface{}{} + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("healthtip/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据健康小提示API失败: %v", err)) + } + + // 3. 校验返回状态 + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("健康小提示接口返回异常: %s", resp.Msg)) + } + + // 4. 解析 result + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析健康小提示数据失败") + } + + // 5. 返回给上层 + return map[string]interface{}{ + "content": resultMap["content"], + }, nil +} + +// processNutrient 查询食物营养成分 +func (s *ToolboxService) processNutrient( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 参数提取(必填) + mode, ok := params["mode"].(float64) + if !ok { + return nil, xerr.NewErrMsg("缺少必填参数 mode") + } + + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("缺少必填参数 word") + } + + // 2. 可选参数 + num := 10 + if v, ok := params["num"].(float64); ok && v > 0 { + num = int(v) + } + + page := 1 + if v, ok := params["page"].(float64); ok && v > 0 { + page = int(v) + } + + // 3. 组装请求参数 + reqParams := map[string]interface{}{ + "mode": mode, + "word": word, + "num": num, + "page": page, + } + + // 4. 调用天行 API + resp, err := s.tianxingjuheClient.Get("nutrient/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求营养成分接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("营养成分接口异常: %s", resp.Msg)) + } + + // 5. 解析 result(数组 or 单对象都兼容) + var list []map[string]interface{} + + switch v := resp.Result.(type) { + case map[string]interface{}: + list = append(list, v) + case []interface{}: + for _, item := range v { + if m, ok := item.(map[string]interface{}); ok { + list = append(list, m) + } + } + default: + return nil, xerr.NewErrMsg("解析营养成分数据失败") + } + + // 6. 返回标准化结果 + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processFxRate 汇率查询 +func (s *ToolboxService) processFxRate( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 参数校验 + money, ok := params["money"].(float64) + if !ok || money <= 0 { + return nil, xerr.NewErrMsg("参数 money 必须为大于 0 的数字") + } + + fromCoin, ok := params["fromcoin"].(string) + if !ok || fromCoin == "" { + return nil, xerr.NewErrMsg("参数 fromcoin 不能为空") + } + + toCoin, ok := params["tocoin"].(string) + if !ok || toCoin == "" { + return nil, xerr.NewErrMsg("参数 tocoin 不能为空") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{ + "money": int(money), + "fromcoin": fromCoin, + "tocoin": toCoin, + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("fxrate/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求汇率接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("汇率接口异常: %s", resp.Msg)) + } + + // 4. 解析结果 + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析汇率数据失败") + } + + amount, ok := result["money"].(string) + if !ok || amount == "" { + return nil, xerr.NewErrMsg("汇率金额异常") + } + + // 5. 返回标准化结果 + return map[string]interface{}{ + "from": fromCoin, + "to": toCoin, + "amount": money, + "result": amount, + }, nil +} + +// processBulletin 每日简报 +func (s *ToolboxService) processBulletin( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数(该接口无需业务参数) + reqParams := map[string]interface{}{} + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("bulletin/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求每日简报接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("每日简报接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析每日简报数据失败") + } + + // 4. 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("简报列表数据异常") + } + + // 5. 字段白名单过滤 + allowed := map[string]bool{ + "title": true, + "digest": true, + "mtime": true, + } + + news := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + for k, v := range raw { + if allowed[k] { + clean[k] = v + } + } + + // 最少保证有标题 + if clean["title"] != nil { + news = append(news, clean) + } + } + + // 6. 返回标准化结果 + return map[string]interface{}{ + "count": len(news), + "list": news, + }, nil +} + +// processCaizimi 猜灯谜 +func (s *ToolboxService) processCaizimi( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数(无需业务参数) + reqParams := map[string]interface{}{} + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("caizimi/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求猜灯谜接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("猜灯谜接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析猜灯谜数据失败") + } + + // 4. 字段白名单(非常重要) + allowed := map[string]bool{ + "type": true, + "answer": true, + "riddle": true, + "disturb": true, + "description": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + // 5. 基础校验 + if clean["riddle"] == nil || clean["answer"] == nil { + return nil, xerr.NewErrMsg("灯谜数据不完整") + } + + return clean, nil +} + +// processHotLajifenlei 热搜垃圾分类 +func (s *ToolboxService) processHotLajifenlei( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数 + reqParams := map[string]interface{}{} + + // 可选:日期 + if date, ok := params["date"].(string); ok && date != "" { + reqParams["date"] = date + } + + // 可选:垃圾类型 + if typ, ok := params["type"].(float64); ok { + reqParams["type"] = int(typ) + } + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("hotlajifenlei/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求热搜垃圾分类接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("热搜垃圾分类接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析热搜垃圾分类数据失败") + } + + // 4. 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("热搜列表数据异常") + } + + // 5. 字段白名单 + 枚举映射 + allowed := map[string]bool{ + "name": true, + "type": true, + "index": true, + } + + typeMap := map[int]string{ + 0: "可回收物", + 1: "有害垃圾", + 2: "厨余垃圾(湿)", + 3: "其他垃圾(干)", + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + for k, v := range raw { + if allowed[k] { + clean[k] = v + } + } + + // 垃圾类型可读化 + if t, ok := clean["type"].(float64); ok { + if name, exists := typeMap[int(t)]; exists { + clean["type_name"] = name + } + } + + list = append(list, clean) + } + + // 6. 返回标准化结果 + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processOilPrice 实时油价查询 +func (s *ToolboxService) processOilPrice( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 参数校验 + prov, ok := params["prov"].(string) + if !ok || prov == "" { + return nil, xerr.NewErrMsg("参数 prov 不能为空,例如:湖北、上海") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{ + "prov": prov, + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("oilprice/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求实时油价接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("实时油价接口异常: %s", resp.Msg)) + } + + // 4. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析油价数据失败") + } + + // 5. 字段白名单 + 可读性增强 + allowed := map[string]bool{ + "p0": true, + "p89": true, + "p92": true, + "p95": true, + "p98": true, + "prov": true, + "time": true, + } + + labelMap := map[string]string{ + "p0": "0号柴油", + "p89": "89号汽油", + "p92": "92号汽油", + "p95": "95号汽油", + "p98": "98号汽油", + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + // 6. 生成前端友好的油价列表 + prices := make([]map[string]interface{}, 0) + for k, label := range labelMap { + if v, ok := clean[k].(string); ok && v != "" { + prices = append(prices, map[string]interface{}{ + "type": k, + "label": label, + "price": v, + }) + } + } + + // 7. 返回标准化结果 + return map[string]interface{}{ + "prov": clean["prov"], + "update": clean["time"], + "prices": prices, + }, nil +} + +// processChengyuJielong 成语接龙 +func (s *ToolboxService) processChengyuJielong( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 参数校验 + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("参数 word 不能为空,例如:一马当先") + } + + userID, ok := params["userid"].(string) + if !ok || userID == "" { + return nil, xerr.NewErrMsg("参数 userid 不能为空,建议使用 openid / uuid") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{ + "word": word, + "userid": userID, + } + + // 可选:状态维持时间 + if t, ok := params["statetime"].(float64); ok && t > 0 { + reqParams["statetime"] = int(t) + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("chengyujielong/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求成语接龙接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("成语接龙接口异常: %s", resp.Msg)) + } + + // 4. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析成语接龙数据失败") + } + + // 5. 字段白名单(非常重要) + allowed := map[string]bool{ + "chengyu": true, + "pinyin": true, + "jieshi": true, + "chuchu": true, + "startstr": true, + "endstr": true, + "tip": true, + "grade": true, + "result": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + // 6. 游戏状态说明(给前端/AI用) + stateMap := map[int]string{ + 0: "开局", + 1: "答对", + 2: "开局失败", + 3: "接错成语", + 4: "不是成语", + 5: "胜利结束", + } + + if r, ok := clean["result"].(float64); ok { + clean["state"] = stateMap[int(r)] + clean["game_over"] = r >= 2 + } + + return clean, nil +} + +// processWeiboHot 微博热搜榜 +func (s *ToolboxService) processWeiboHot( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数(无需业务参数) + reqParams := map[string]interface{}{} + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("weibohot/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求微博热搜接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("微博热搜接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析微博热搜数据失败") + } + + // 4. 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("热搜列表数据异常") + } + + // 5. 字段白名单 + allowed := map[string]bool{ + "hottag": true, + "hotword": true, + "hotwordnum": true, + } + + hotList := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + for k, v := range raw { + if allowed[k] { + clean[k] = v + } + } + + // 至少要有热搜词 + if clean["hotword"] != nil { + hotList = append(hotList, clean) + } + } + + // 6. 返回标准化结果 + return map[string]interface{}{ + "count": len(hotList), + "list": hotList, + }, nil +} + +// processDomainResolve 查询域名解析记录 +func (s *ToolboxService) processDomainResolve( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 参数校验 + domain, ok := params["domain"].(string) + if !ok || domain == "" { + return nil, xerr.NewErrMsg("参数 domain 不能为空,例如:tianyuanapi.com") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{ + "domain": domain, + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("domain/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求域名解析接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("域名解析接口异常: %s", resp.Msg)) + } + + // 4. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析域名解析数据失败") + } + + // 5. 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析解析记录列表失败") + } + + // 6. 不做字段白名单(DNS 字段不固定) + list := make([]map[string]interface{}, 0, len(rawList)) + for _, item := range rawList { + if m, ok := item.(map[string]interface{}); ok { + list = append(list, m) + } + } + + // 7. 返回标准化结果 + return map[string]interface{}{ + "domain": domain, + "count": len(list), + "list": list, + }, nil +} + +// processScenic 旅游景区查询 +func (s *ToolboxService) processScenic( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 至少一个检索参数 + word, _ := params["word"].(string) + city, _ := params["city"].(string) + province, _ := params["province"].(string) + + if word == "" && city == "" && province == "" { + return nil, xerr.NewErrMsg("word / city / province 至少填写一个") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{} + if word != "" { + reqParams["word"] = word + } + if city != "" { + reqParams["city"] = city + } + if province != "" { + reqParams["province"] = province + } + + // 可选:分页 + if num, ok := params["num"].(float64); ok && num > 0 { + reqParams["num"] = int(num) + } + if page, ok := params["page"].(string); ok && page != "" { + reqParams["page"] = page + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("scenic/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求旅游景区接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("旅游景区接口异常: %s", resp.Msg)) + } + + // 4. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析旅游景区数据失败") + } + + // 5. 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("景区列表数据异常") + } + + // 6. 字段白名单 + allowed := map[string]bool{ + "name": true, + "province": true, + "city": true, + "content": true, + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + for k, v := range raw { + if allowed[k] { + clean[k] = v + } + } + + if clean["name"] != nil { + list = append(list, clean) + } + } + + // 7. 返回标准化结果 + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processMingyan 名人名言查询 +func (s *ToolboxService) processMingyan( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数 + reqParams := map[string]interface{}{} + + // 可选:名言类型 + if tid, ok := params["typeid"].(float64); ok { + reqParams["typeid"] = int(tid) + } + + // 可选:返回数量 + if num, ok := params["num"].(float64); ok && num > 0 { + reqParams["num"] = int(num) + } else { + reqParams["num"] = 1 + } + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("mingyan/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求名人名言接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("名人名言接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析名人名言数据失败") + } + + // 4. 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("名言列表数据异常") + } + + // 5. 字段白名单 + allowed := map[string]bool{ + "content": true, + "author": true, + "typeid": true, + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + for k, v := range raw { + if allowed[k] { + clean[k] = v + } + } + + if clean["content"] != nil { + list = append(list, clean) + } + } + + // 6. 返回标准化结果 + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processTianqiShiju 天气诗句查询 +func (s *ToolboxService) processTianqiShiju( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数 + reqParams := map[string]interface{}{} + + // 可选:天气类型 + if tqtype, ok := params["tqtype"].(float64); ok { + reqParams["tqtype"] = int(tqtype) + } + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("tianqishiju/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求天气诗句接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("天气诗句接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析天气诗句数据失败") + } + + // 4. 字段白名单 + allowed := map[string]bool{ + "content": true, + "author": true, + "source": true, + "tqtype": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + // 5. 天气类型可读化 + tqTypeMap := map[int]string{ + 1: "风", 2: "云", 3: "雨", 4: "雪", + 5: "霜", 6: "露", 7: "雾", 8: "雷", + 9: "晴", 10: "阴", + } + + if t, ok := clean["tqtype"].(float64); ok { + if name, exists := tqTypeMap[int(t)]; exists { + clean["weather"] = name + } + } + + return clean, nil +} + +// processJieQi 二十四节气查询 +func (s *ToolboxService) processJieQi( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 参数校验 + name, ok := params["word"].(string) + if !ok || name == "" { + return nil, xerr.NewErrMsg("参数 word 不能为空,例如:立春") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{ + "word": name, + } + + // 可选:年份 + if year, ok := params["year"].(string); ok && year != "" { + reqParams["year"] = year + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("jieqi/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求二十四节气接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("二十四节气接口异常: %s", resp.Msg)) + } + + // 4. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析二十四节气数据失败") + } + + // 5. 字段白名单 + allowed := map[string]bool{ + "name": true, + "day": true, + "jieshao": true, + "yiji": true, + "shiju": true, + "xishu": true, + "meishi": true, + "yuanyin": true, + "nameimg": true, + "date": true, // 内嵌对象 + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + // 6. 公历 / 农历字段可读化(可选) + if date, ok := clean["date"].(map[string]interface{}); ok { + clean["greg_date"] = date["gregdate"] + clean["lunar_date"] = date["lunardate"] + clean["lunar_year"] = date["cnyear"] + clean["lunar_zodiac"] = date["cnzodiac"] + } + + return clean, nil +} + +// processBloodPair 血型配对查询(娱乐) +func (s *ToolboxService) processBloodPair( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 参数校验 + me, ok1 := params["me"].(string) + he, ok2 := params["he"].(string) + + if !ok1 || !ok2 || me == "" || he == "" { + return nil, xerr.NewErrMsg("参数 me 和 he 不能为空,例如:me=O&he=A") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{ + "me": me, + "he": he, + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("blood/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求血型配对接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("血型配对接口异常: %s", resp.Msg)) + } + + // 4. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析血型配对数据失败") + } + + // 5. 字段白名单 + allowed := map[string]bool{ + "pair": true, + "title": true, + "content": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + // 6. 增加娱乐声明(非常重要) + clean["notice"] = "本结果仅供娱乐,不代表科学结论" + + return clean, nil +} + +// processZodiacPair 生肖配对查询(娱乐) +func (s *ToolboxService) processZodiacPair( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 参数校验 + me, ok1 := params["me"].(string) + he, ok2 := params["he"].(string) + + if !ok1 || !ok2 || me == "" || he == "" { + return nil, xerr.NewErrMsg("参数 me 和 he 不能为空,例如:me=猴&he=羊") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{ + "me": me, + "he": he, + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("zodiac/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求生肖配对接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("生肖配对接口异常: %s", resp.Msg)) + } + + // 4. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析生肖配对数据失败") + } + + // 5. 字段白名单 + allowed := map[string]bool{ + "title": true, + "fcontent": true, + "mcontent": true, + "fcontent1": true, + "mcontent1": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + // 6. 明确娱乐声明(非常重要) + clean["notice"] = "本结果仅供娱乐,不代表科学结论" + + return clean, nil +} + +// processSayLove 土味情话 +func (s *ToolboxService) processSayLove( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数(无需业务参数) + reqParams := map[string]interface{}{} + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("saylove/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求土味情话接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("土味情话接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析土味情话数据失败") + } + + // 4. 字段白名单 + allowed := map[string]bool{ + "content": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + // 5. 兜底校验 + if clean["content"] == nil { + return nil, xerr.NewErrMsg("土味情话内容为空") + } + + return clean, nil +} + +// processStar 星座运势查询(娱乐) +func (s *ToolboxService) processStar( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 参数校验 + astro, ok := params["astro"].(string) + if !ok || astro == "" { + return nil, xerr.NewErrMsg("参数 astro 不能为空,例如:金牛座 或 taurus") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{ + "astro": astro, + } + + // 可选:日期 + if date, ok := params["date"].(string); ok && date != "" { + reqParams["date"] = date + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("star/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求星座运势接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("星座运势接口异常: %s", resp.Msg)) + } + + // 4. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析星座运势数据失败") + } + + // 5. 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("星座运势列表数据异常") + } + + // 6. 字段白名单 + allowed := map[string]bool{ + "type": true, + "content": true, + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + for k, v := range raw { + if allowed[k] { + clean[k] = v + } + } + + if clean["type"] != nil && clean["content"] != nil { + list = append(list, clean) + } + } + + // 7. 返回标准化结果 + return map[string]interface{}{ + "astro": astro, + "count": len(list), + "list": list, + }, nil +} + +// processPinyin 汉字转拼音 +func (s *ToolboxService) processPinyin( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 参数校验 + text, ok := params["text"].(string) + if !ok || text == "" { + return nil, xerr.NewErrMsg("参数 text 不能为空") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{ + "text": text, + } + + // 可选:type + if typ, ok := params["type"].(float64); ok { + reqParams["type"] = int(typ) + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("pinyin/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求汉字转拼音接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("汉字转拼音接口异常: %s", resp.Msg)) + } + + // 4. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析汉字转拼音数据失败") + } + + // 5. 字段白名单 + allowed := map[string]bool{ + "pinyin": true, + "jianxie": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + return clean, nil +} + +// processNetHot 百度热搜榜 +func (s *ToolboxService) processNetHot( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数(无需业务参数) + reqParams := map[string]interface{}{} + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("nethot/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求百度热搜接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("百度热搜接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析百度热搜数据失败") + } + + // 4. 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("热搜列表数据异常") + } + + // 5. 字段白名单 + allowed := map[string]bool{ + "keyword": true, + "brief": true, + "index": true, + "trend": true, + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + for k, v := range raw { + if allowed[k] { + clean[k] = v + } + } + + if clean["keyword"] != nil { + list = append(list, clean) + } + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processEnSentence 英语一句话 +func (s *ToolboxService) processEnSentence( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数(无需业务参数) + reqParams := map[string]interface{}{} + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("ensentence/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求英语一句话接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("英语一句话接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析英语一句话数据失败") + } + + // 4. 字段白名单 + allowed := map[string]bool{ + "en": true, + "zh": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + return clean, nil +} + +// processVerse 优美诗句查询 +func (s *ToolboxService) processVerse( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数 + reqParams := map[string]interface{}{} + + // 可选:返回数量 + if num, ok := params["num"].(float64); ok && num > 0 { + reqParams["num"] = int(num) + } + + // 可选:翻页 + if page, ok := params["page"].(float64); ok && page > 0 { + reqParams["page"] = int(page) + } + + // 可选:搜索词 + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("verse/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求优美诗句接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("优美诗句接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析优美诗句数据失败") + } + + // 4. 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("诗句列表数据异常") + } + + // 5. 字段白名单 + allowed := map[string]bool{ + "author": true, + "source": true, + "content": true, + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + for k, v := range raw { + if allowed[k] { + clean[k] = v + } + } + + if clean["content"] != nil { + list = append(list, clean) + } + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processPoetry 唐诗三百首查询 +func (s *ToolboxService) processPoetry( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数 + reqParams := map[string]interface{}{} + + // 可选:返回数量 + if num, ok := params["num"].(float64); ok && num > 0 { + reqParams["num"] = int(num) + } + + // 可选:翻页 + if page, ok := params["page"].(float64); ok && page > 0 { + reqParams["page"] = int(page) + } + + // 可选:搜索词 + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("poetry/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求唐诗三百首接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("唐诗三百首接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析唐诗三百首数据失败") + } + + // 4. 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("唐诗列表数据异常") + } + + // 5. 字段白名单 + allowed := map[string]bool{ + "title": true, + "author": true, + "kind": true, + "intro": true, + "content": true, + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + for k, v := range raw { + if allowed[k] { + clean[k] = v + } + } + + if clean["title"] != nil && clean["content"] != nil { + list = append(list, clean) + } + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processPoetries 唐诗大全查询 +func (s *ToolboxService) processPoetries( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 必填参数校验 + num, ok1 := params["num"].(float64) + page, ok2 := params["page"].(float64) + if !ok1 || !ok2 || num <= 0 || page <= 0 { + return nil, xerr.NewErrMsg("参数 num 和 page 为必填项") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{ + "num": int(num), + "page": int(page), + } + + // 可选:随机 + if randVal, ok := params["rand"].(float64); ok { + reqParams["rand"] = int(randVal) + } + + // 可选:关键词 + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + + // 可选:作者 + if author, ok := params["author"].(string); ok && author != "" { + reqParams["author"] = author + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("poetries/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求唐诗大全接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("唐诗大全接口异常: %s", resp.Msg)) + } + + // 4. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析唐诗大全数据失败") + } + + // 5. 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("唐诗列表数据异常") + } + + // 6. 字段白名单 + allowed := map[string]bool{ + "title": true, + "author": true, + "content": true, + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + for k, v := range raw { + if allowed[k] { + clean[k] = v + } + } + + if clean["title"] != nil && clean["content"] != nil { + list = append(list, clean) + } + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processPiTongLiShi 简说历史查询 +func (s *ToolboxService) processPiTongLiShi( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数(无需业务参数) + reqParams := map[string]interface{}{} + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("pitlishi/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求简说历史接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("简说历史接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析简说历史数据失败") + } + + // 4. 字段白名单 + allowed := map[string]bool{ + "content": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + return clean, nil +} + +// processMnPara 小段子查询 +func (s *ToolboxService) processMnPara( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数(无需业务参数) + reqParams := map[string]interface{}{} + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("mnpara/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求小段子接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("小段子接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析小段子数据失败") + } + + // 4. 字段白名单 + allowed := map[string]bool{ + "author": true, + "content": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + return clean, nil +} + +// processSkl 顺口溜查询 +func (s *ToolboxService) processSkl( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数(无需业务参数) + reqParams := map[string]interface{}{} + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("skl/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求顺口溜接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("顺口溜接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析顺口溜数据失败") + } + + // 4. 字段白名单 + allowed := map[string]bool{ + "content": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + return clean, nil +} + +// processSentence 精美句子查询 +func (s *ToolboxService) processSentence( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数(无需业务参数) + reqParams := map[string]interface{}{} + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("sentence/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求精美句子接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("精美句子接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析精美句子数据失败") + } + + // 4. 字段白名单 + allowed := map[string]bool{ + "author": true, + "content": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + return clean, nil +} + +// processEnWords 英汉词典查询 +func (s *ToolboxService) processEnWords( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 参数校验 + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("参数 word 不能为空,例如:lexicon") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{ + "word": word, + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("enwords/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求英汉词典接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("英汉词典接口异常: %s", resp.Msg)) + } + + // 4. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析英汉词典数据失败") + } + + // 5. 字段白名单 + allowed := map[string]bool{ + "word": true, + "content": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + return clean, nil +} + +// processLexicon 中文词典查询 +func (s *ToolboxService) processLexicon( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 参数校验 + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("参数 word 不能为空,例如:作坊") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{ + "word": word, + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("lexicon/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求中文词典接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("中文词典接口异常: %s", resp.Msg)) + } + + // 4. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析中文词典数据失败") + } + + // 5. 字段白名单 + allowed := map[string]bool{ + "word": true, + "content": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + return clean, nil +} + +// processLunar 中国老黄历查询 +func (s *ToolboxService) processLunar( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数 + reqParams := map[string]interface{}{} + + // 可选:日期 + if date, ok := params["date"].(string); ok && date != "" { + reqParams["date"] = date + } + + // 可选:按农历查询 + if typ, ok := params["type"].(float64); ok { + reqParams["type"] = int(typ) + } + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("lunar/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求老黄历接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("老黄历接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析老黄历数据失败") + } + + // 4. 字段白名单(只保留常用字段) + allowed := map[string]bool{ + "lunardate": true, + "gregoriandate": true, + "shengxiao": true, + "fitness": true, + "taboo": true, + "chongsha": true, + "shenwei": true, + "taishen": true, + "lunar_festival": true, + "festival": true, + "jieqi": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + return clean, nil +} + +// processXingZuo 星座配对查询(娱乐) +func (s *ToolboxService) processXingZuo( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 参数校验 + me, ok := params["me"].(string) + if !ok || me == "" { + return nil, xerr.NewErrMsg("参数 me 不能为空,例如:金牛") + } + + // 2. 请求参数 + reqParams := map[string]interface{}{ + "me": me, + } + + // 可选:双人模式 + if he, ok := params["he"].(string); ok && he != "" { + reqParams["he"] = he + } + + // 可选:全部配对 + if all, ok := params["all"].(float64); ok { + reqParams["all"] = int(all) + } + + // 3. 调用天行 API + resp, err := s.tianxingjuheClient.Get("xingzuo/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求星座配对接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("星座配对接口异常: %s", resp.Msg)) + } + + // 4. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析星座配对数据失败") + } + + // 5. 字段白名单 + allowed := map[string]bool{ + "title": true, + "grade": true, + "content": true, + } + + clean := map[string]interface{}{} + for k, v := range result { + if allowed[k] { + clean[k] = v + } + } + + // 6. 娱乐声明 + clean["notice"] = "本结果仅供娱乐,不代表科学结论" + + return clean, nil +} + +// processGodReply 神回复查询 +func (s *ToolboxService) processGodReply(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + + // 1. 请求参数 + reqParams := map[string]interface{}{} + + // 可选:返回数量 + if num, ok := params["num"].(float64); ok && num > 0 { + reqParams["num"] = int(num) + } + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("godreply/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求神回复接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("神回复接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析神回复数据失败") + } + + // 4. 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("神回复列表数据异常") + } + + // 5. 字段白名单 + allowed := map[string]bool{ + "title": true, + "content": true, + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + for k, v := range raw { + if allowed[k] { + clean[k] = v + } + } + + if clean["content"] != nil { + list = append(list, clean) + } + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processXiehou 歇后语查询 +func (s *ToolboxService) processXiehou( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 1. 请求参数 + reqParams := map[string]interface{}{} + + // 可选:返回数量(1~10) + if num, ok := params["num"].(float64); ok && num > 0 && num <= 10 { + reqParams["num"] = int(num) + } + + // 2. 调用天行 API + resp, err := s.tianxingjuheClient.Get("xiehou/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求歇后语接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("歇后语接口异常: %s", resp.Msg)) + } + + // 3. 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析歇后语数据失败") + } + + // 4. 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("歇后语列表数据异常") + } + + // 5. 字段白名单(只保留需要字段) + allowed := map[string]bool{ + "quest": true, // 谜面 + "result": true, // 谜底 + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + for k, v := range raw { + if allowed[k] { + clean[k] = v + } + } + + // 至少要有谜面和谜底 + if clean["quest"] != nil && clean["result"] != nil { + list = append(list, clean) + } + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processRKL 绕口令查询 +func (s *ToolboxService) processRKL( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 可选:返回数量 1~10 + if num, ok := params["num"].(float64); ok && num > 0 && num <= 10 { + reqParams["num"] = int(num) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("rkl/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求绕口令接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("绕口令接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析绕口令数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("绕口令列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + // 字段白名单 + clean := map[string]interface{}{} + if content, ok := raw["content"]; ok && content != nil { + clean["content"] = content + list = append(list, clean) + } + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processCName 网络取名 +func (s *ToolboxService) processCName( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 返回数量(1~50) + if num, ok := params["num"].(float64); ok && num > 0 && num <= 50 { + reqParams["num"] = int(num) + } + + // 性别:1男 2女 3中性 + if sex, ok := params["sex"].(float64); ok && sex >= 1 && sex <= 3 { + reqParams["sex"] = int(sex) + } + + // 包含的字 + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + + // 名字字数(1~3) + if wordnum, ok := params["wordnum"].(float64); ok && wordnum >= 1 && wordnum <= 3 { + reqParams["wordnum"] = int(wordnum) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("cname/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求网络取名接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("网络取名接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析网络取名数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("网络取名列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if naming, ok := raw["naming"]; ok && naming != nil { + clean["naming"] = naming + } + if sex, ok := raw["sex"]; ok { + clean["sex"] = sex + } + if wordnum, ok := raw["wordnum"]; ok { + clean["wordnum"] = wordnum + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processShares 股市术语查询 +func (s *ToolboxService) processShares( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 搜索词(必填) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("股市术语查询缺少必填参数:word") + } + reqParams["word"] = word + + // 返回数量(必填) + num, ok := params["num"].(float64) + if !ok || num <= 0 { + num = 5 // 给一个安全默认值 + } + reqParams["num"] = int(num) + + // 页码(可选) + if page, ok := params["page"].(float64); ok && page > 0 { + reqParams["page"] = int(page) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("shares/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求股市术语接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("股市术语接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析股市术语数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("股市术语列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if term, ok := raw["term"]; ok { + clean["term"] = term + } + if content, ok := raw["content"]; ok { + clean["content"] = content + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processHotWord 网络流行语查询 +func (s *ToolboxService) processHotWord( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 搜索词(必填) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("网络流行语查询缺少必填参数:word") + } + reqParams["word"] = word + + // 返回数量(必填) + num, ok := params["num"].(float64) + if !ok || num <= 0 { + num = 5 // 给一个安全默认值 + } + reqParams["num"] = int(num) + + // 页码(可选) + if page, ok := params["page"].(float64); ok && page > 0 { + reqParams["page"] = int(page) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("hotword/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求网络流行语接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("网络流行语接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析网络流行语数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("网络流行语列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if title, ok := raw["title"]; ok { + clean["title"] = title + } + if content, ok := raw["content"]; ok { + clean["content"] = content + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processCityRiddle 地名谜语查询 +func (s *ToolboxService) processCityRiddle( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 地名谜语接口无需额外参数 + reqParams := map[string]interface{}{} + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("cityriddle/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求地名谜语接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("地名谜语接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析地名谜语数据失败") + } + + // 字段白名单 + clean := map[string]interface{}{} + if quest, ok := result["quest"]; ok { + clean["quest"] = quest + } + if answer, ok := result["result"]; ok { + clean["answer"] = answer + } + + return map[string]interface{}{ + "count": 1, + "list": []map[string]interface{}{clean}, + }, nil +} + +// processWenDa 一战到底知识竞答 +func (s *ToolboxService) processWenDa( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 一战到底接口无需额外参数 + reqParams := map[string]interface{}{} + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("wenda/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求一战到底接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("一战到底接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析一战到底数据失败") + } + + // 字段白名单 + clean := map[string]interface{}{} + if quest, ok := result["quest"]; ok { + clean["quest"] = quest + } + if answer, ok := result["result"]; ok { + clean["answer"] = answer + } + + return map[string]interface{}{ + "count": 1, + "list": []map[string]interface{}{clean}, + }, nil +} + +// processChengYu 成语典故查询 +func (s *ToolboxService) processChengYu( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 搜索词(必填) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("成语典故查询缺少必填参数:word") + } + reqParams["word"] = word + + // 返回数量(可选) + if num, ok := params["num"].(float64); ok && num > 0 && num <= 50 { + reqParams["num"] = int(num) + } + + // 搜索模式(可选) + if mode, ok := params["mode"].(float64); ok && mode >= 0 && mode <= 2 { + reqParams["mode"] = int(mode) + } + + // 页码(可选) + if page, ok := params["page"].(float64); ok && page > 0 { + reqParams["page"] = int(page) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("chengyu/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求成语典故接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("成语典故接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析成语典故数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("成语典故列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if chengyu, ok := raw["chengyu"]; ok { + clean["chengyu"] = chengyu + } + if pinyin, ok := raw["pinyin"]; ok { + clean["pinyin"] = pinyin + } + if diangu, ok := raw["diangu"]; ok { + clean["diangu"] = diangu + } + if chuchu, ok := raw["chuchu"]; ok { + clean["chuchu"] = chuchu + } + if fanli, ok := raw["fanli"]; ok { + clean["fanli"] = fanli + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processNaoWan 脑筋急转弯查询 +func (s *ToolboxService) processNaoWan( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 返回数量(必填) + num, ok := params["num"].(float64) + if !ok || num <= 0 { + num = 5 // 给一个安全默认值 + } + reqParams["num"] = int(num) + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("naowan/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求脑筋急转弯接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("脑筋急转弯接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析脑筋急转弯数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("脑筋急转弯列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if quest, ok := raw["quest"]; ok { + clean["quest"] = quest + } + if answer, ok := raw["result"]; ok { + clean["answer"] = answer + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processDOB 生日性格查询 +func (s *ToolboxService) processDOB( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 月份(必填) + m, ok := params["m"].(float64) + if !ok || m < 1 || m > 12 { + return nil, xerr.NewErrMsg("生日性格查询缺少必填参数:m(月份 1-12)") + } + reqParams["m"] = int(m) + + // 日期(必填) + d, ok := params["d"].(float64) + if !ok || d < 1 || d > 31 { + return nil, xerr.NewErrMsg("生日性格查询缺少必填参数:d(日期 1-31)") + } + reqParams["d"] = int(d) + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("dob/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求生日性格接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("生日性格接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析生日性格数据失败") + } + + // 字段白名单 + clean := map[string]interface{}{} + if title, ok := result["title"]; ok { + clean["title"] = title + } + if content, ok := result["content"]; ok { + clean["content"] = content + } + + return map[string]interface{}{ + "count": 1, + "list": []map[string]interface{}{clean}, + }, nil +} + +// processDictum 名言警句查询 +func (s *ToolboxService) processDictum( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 返回数量(必填) + num, ok := params["num"].(float64) + if !ok || num <= 0 { + num = 5 // 给一个安全默认值 + } + reqParams["num"] = int(num) + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("dictum/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求名言警句接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("名言警句接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析名言警句数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("名言警句列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if mrname, ok := raw["mrname"]; ok { + clean["mrname"] = mrname + } + if content, ok := raw["content"]; ok { + clean["content"] = content + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processJoke 雷人笑话查询 +func (s *ToolboxService) processJoke( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 返回数量(必填) + num, ok := params["num"].(float64) + if !ok || num <= 0 { + num = 5 // 给一个安全默认值 + } + reqParams["num"] = int(num) + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("joke/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求雷人笑话接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("雷人笑话接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析雷人笑话数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("雷人笑话列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if title, ok := raw["title"]; ok { + clean["title"] = title + } + if content, ok := raw["content"]; ok { + clean["content"] = content + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processDream 周公解梦查询 +func (s *ToolboxService) processDream( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 搜索词(必填) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("周公解梦查询缺少必填参数:word") + } + reqParams["word"] = word + + // 返回数量(必填) + num, ok := params["num"].(float64) + if !ok || num <= 0 { + num = 5 // 给一个安全默认值 + } + reqParams["num"] = int(num) + + // 页码(可选) + if page, ok := params["page"].(float64); ok && page > 0 { + reqParams["page"] = int(page) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("dream/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求周公解梦接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("周公解梦接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析周公解梦数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("周公解梦列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if title, ok := raw["title"]; ok { + clean["title"] = title + } + if typ, ok := raw["type"]; ok { + clean["type"] = typ + } + if content, ok := raw["result"]; ok { + clean["result"] = content + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processCaiPu 菜谱查询 +func (s *ToolboxService) processCaiPu( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 搜索词(可选) + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + + // 返回数量(可选) + if num, ok := params["num"].(float64); ok && num > 0 { + reqParams["num"] = int(num) + } + + // 页码(可选) + if page, ok := params["page"].(float64); ok && page > 0 { + reqParams["page"] = int(page) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("caipu/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求菜谱查询接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("菜谱查询接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析菜谱查询数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("菜谱查询列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if name, ok := raw["cp_name"]; ok { + clean["cp_name"] = name + } + if typ, ok := raw["type_name"]; ok { + clean["type_name"] = typ + } + if yuanliao, ok := raw["yuanliao"]; ok { + clean["yuanliao"] = yuanliao + } + if tiaoliao, ok := raw["tiaoliao"]; ok { + clean["tiaoliao"] = tiaoliao + } + if zuofa, ok := raw["zuofa"]; ok { + clean["zuofa"] = zuofa + } + if tishi, ok := raw["tishi"]; ok { + clean["tishi"] = tishi + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processWxNew 微信文章精选查询 +func (s *ToolboxService) processWxNew( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 返回数量(必填) + num, ok := params["num"].(float64) + if !ok || num <= 0 { + num = 10 + } + reqParams["num"] = int(num) + + // 页码(可选) + if page, ok := params["page"].(float64); ok && page > 0 { + reqParams["page"] = int(page) + } + + // 随机(可选) + if rand, ok := params["rand"].(float64); ok && rand == 1 { + reqParams["rand"] = 1 + } + + // 关键词(可选) + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("wxnew/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求微信文章精选接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("微信文章精选接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析微信文章精选数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("微信文章精选列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if title, ok := raw["title"]; ok { + clean["title"] = title + } + if url, ok := raw["url"]; ok { + clean["url"] = url + } + if username, ok := raw["username"]; ok { + clean["username"] = username + } + if ctime, ok := raw["ctime"]; ok { + clean["ctime"] = ctime + } + if picurl, ok := raw["picurl"]; ok { + clean["picurl"] = picurl + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processConstellation 星座运势查询 +func (s *ToolboxService) processConstellation(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 必填:星座名称 + astro, ok := params["astro"].(string) + if !ok || astro == "" { + return nil, xerr.NewErrMsg("astro 星座名称参数不能为空且必须为字符串") + } + reqParams["astro"] = astro + + // 可选:运势类型 (today/tomorrow/week/month) + if fortune, ok := params["fortune"].(string); ok && fortune != "" { + reqParams["fortune"] = fortune + } + + resp, err := s.tianxingjuheClient.Get("constellation/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求星座运势API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("星座运势接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析星座运势数据失败") + } + + allowed := map[string]bool{ + "astro": true, + "fortune": true, + "summary": true, + "love": true, + "work": true, + "money": true, + "health": true, + "number": true, + "color": true, + "qfriend": true, + "date": true, + } + + clean := map[string]interface{}{} + for k, v := range resultMap { + if allowed[k] { + clean[k] = v + } + } + + return clean, nil +} + +// processFanyi 在线翻译 +func (s *ToolboxService) processFanyi(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 必填:待翻译文本 + content, ok := params["content"].(string) + if !ok || content == "" { + return nil, xerr.NewErrMsg("content 待翻译文本参数不能为空且必须为字符串") + } + reqParams["content"] = content + + // 可选:源语言 (默认自动检测) + if from, ok := params["from"].(string); ok && from != "" { + reqParams["from"] = from + } + + // 可选:目标语言 (默认中文) + if to, ok := params["to"].(string); ok && to != "" { + reqParams["to"] = to + } + + resp, err := s.tianxingjuheClient.Get("fanyi/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求在线翻译API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("在线翻译接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析翻译数据失败") + } + + return map[string]interface{}{ + "content": resultMap["content"], + "from": resultMap["from"], + "to": resultMap["to"], + "result": resultMap["result"], + }, nil +} + +// processHoliday 节假日查询 +func (s *ToolboxService) processHoliday(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 可选:年份 (默认当前年份) + if year, ok := params["year"].(string); ok && year != "" { + reqParams["year"] = year + } + + // 可选:月份 + if month, ok := params["month"].(string); ok && month != "" { + reqParams["month"] = month + } + + // 可选:日期 + if date, ok := params["date"].(string); ok && date != "" { + reqParams["date"] = date + } + + resp, err := s.tianxingjuheClient.Get("holiday/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求节假日查询API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("节假日查询接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析节假日数据失败") + } + + return resultMap, nil +} + +// processGarbage 垃圾分类查询 +func (s *ToolboxService) processGarbage(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 必填:垃圾名称 + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("word 垃圾名称参数不能为空且必须为字符串") + } + reqParams["word"] = word + + resp, err := s.tianxingjuheClient.Get("garbage/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求垃圾分类查询API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("垃圾分类查询接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析垃圾分类数据失败") + } + + // 解析列表 + rawList, ok := resultMap["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("垃圾分类列表数据异常") + } + + typeMap := map[int]string{ + 0: "可回收物", + 1: "有害垃圾", + 2: "厨余垃圾(湿)", + 3: "其他垃圾(干)", + } + + list := make([]map[string]interface{}, 0, len(rawList)) + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + clean := map[string]interface{}{ + "name": raw["name"], + } + if t, ok := raw["type"].(float64); ok { + clean["type"] = int(t) + if name, exists := typeMap[int(t)]; exists { + clean["type_name"] = name + } + } + if explain, ok := raw["explain"]; ok { + clean["explain"] = explain + } + list = append(list, clean) + } + + return map[string]interface{}{ + "word": word, + "count": len(list), + "list": list, + }, nil +} + +// processZone 地区编码查询 +func (s *ToolboxService) processZone(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + reqParams := map[string]interface{}{} + + // 可选:省份 + if province, ok := params["province"].(string); ok && province != "" { + reqParams["province"] = province + } + + // 可选:城市 + if city, ok := params["city"].(string); ok && city != "" { + reqParams["city"] = city + } + + // 可选:区县 + if district, ok := params["district"].(string); ok && district != "" { + reqParams["district"] = district + } + + resp, err := s.tianxingjuheClient.Get("zone/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求地区编码查询API失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("地区编码查询接口返回异常: %s", resp.Msg)) + } + + resultMap, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析地区编码数据失败") + } + + return resultMap, nil +} + +// processTimeZone 国际时区查询 +func (s *ToolboxService) processTimeZone( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 国家/地区(必填) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("国际时区查询缺少必填参数:word") + } + reqParams["word"] = word + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("timezone/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求国际时区接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("国际时区接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析国际时区数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("国际时区列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if country, ok := raw["country"]; ok { + clean["country"] = country + } + if areaname, ok := raw["areaname"]; ok { + clean["areaname"] = areaname + } + if timezone, ok := raw["timezone"]; ok { + clean["timezone"] = timezone + } + if continent, ok := raw["continent"]; ok { + clean["continent"] = continent + } + if countryen, ok := raw["countryen"]; ok { + clean["countryen"] = countryen + } + if countryezm, ok := raw["countryezm"]; ok { + clean["countryezm"] = countryezm + } + if countryszm, ok := raw["countryszm"]; ok { + clean["countryszm"] = countryszm + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processDuoYinZi 多音字查询 +func (s *ToolboxService) processDuoYinZi( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 多音字(必填) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("多音字查询缺少必填参数:word") + } + reqParams["word"] = word + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("duoyinzi/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求多音字接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("多音字接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析多音字数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("多音字列表数据异常") + } + + list := make([]string, 0, len(rawList)) + for _, item := range rawList { + if s, ok := item.(string); ok { + list = append(list, s) + } + } + + return map[string]interface{}{ + "hanzi": result["hanzi"], + "count": len(list), + "list": list, + }, nil +} + +// processDailyTel 全国常用电话查询 +func (s *ToolboxService) processDailyTel( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 机构名称或电话(必填) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("全国常用电话查询缺少必填参数:word") + } + reqParams["word"] = word + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("dailytel/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求全国常用电话接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("全国常用电话接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析全国常用电话数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("全国常用电话列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if name, ok := raw["name"]; ok { + clean["name"] = name + } + if cate, ok := raw["cate"]; ok { + clean["cate"] = cate + } + if tel, ok := raw["tel"]; ok { + clean["tel"] = tel + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processObdCode 汽车 OBD 故障码查询 +func (s *ToolboxService) processObdCode( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // OBD 故障码(必填) + code, ok := params["code"].(string) + if !ok || code == "" { + return nil, xerr.NewErrMsg("汽车OBD查询缺少必填参数:code") + } + reqParams["code"] = code + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("obdcode/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求汽车OBD接口失败: %v", err)) + } + + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("汽车OBD接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析汽车OBD数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("汽车OBD列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if v, ok := raw["code"]; ok { + clean["code"] = v + } + if v, ok := raw["descr"]; ok { + clean["descr"] = v + } + if v, ok := raw["ennote"]; ok { + clean["ennote"] = v + } + if v, ok := raw["zhnote"]; ok { + clean["zhnote"] = v + } + if v, ok := raw["carmodel"]; ok { + clean["carmodel"] = v + } + if v, ok := raw["category"]; ok { + clean["category"] = v + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processChaiZi 汉语拆字查询 +func (s *ToolboxService) processChaiZi( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 查询内容(必填) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("汉语拆字查询缺少必填参数:word") + } + reqParams["word"] = word + + // 查询类型(可选,默认 0) + if typ, ok := params["type"].(float64); ok { + reqParams["type"] = int(typ) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("chaizi/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求汉语拆字接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("汉语拆字接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析汉语拆字数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("汉语拆字列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + clean := map[string]interface{}{} + if texts, ok := raw["texts"]; ok { + clean["texts"] = texts + } + if words, ok := raw["words"]; ok { + clean["words"] = words + } + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processCityLookup 地区搜索 +func (s *ToolboxService) processCityLookup( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 地区名称(必填) + area, ok := params["area"].(string) + if !ok || area == "" { + return nil, xerr.NewErrMsg("地区搜索缺少必填参数:area") + } + reqParams["area"] = area + + // 返回数量(可选) + if num, ok := params["num"].(float64); ok && num > 0 { + reqParams["num"] = int(num) + } + + // 翻页(可选) + if page, ok := params["page"].(float64); ok && page > 0 { + reqParams["page"] = int(page) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("citylookup/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求地区搜索接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("地区搜索接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析地区搜索数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("地区搜索列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if v, ok := raw["adcode"]; ok { + clean["adcode"] = v + } + if v, ok := raw["areacn"]; ok { + clean["areacn"] = v + } + if v, ok := raw["areaen"]; ok { + clean["areaen"] = v + } + if v, ok := raw["areaid"]; ok { + clean["areaid"] = v + } + if v, ok := raw["citycn"]; ok { + clean["citycn"] = v + } + if v, ok := raw["cityen"]; ok { + clean["cityen"] = v + } + if v, ok := raw["areatype"]; ok { + clean["areatype"] = v + } + if v, ok := raw["latitude"]; ok { + clean["latitude"] = v + } + if v, ok := raw["longitude"]; ok { + clean["longitude"] = v + } + if v, ok := raw["provincecn"]; ok { + clean["provincecn"] = v + } + if v, ok := raw["provinceen"]; ok { + clean["provinceen"] = v + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processHuaYu 花语箴言查询 +func (s *ToolboxService) processHuaYu( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 花的名字(必填) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("花语箴言查询缺少必填参数:word") + } + reqParams["word"] = word + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("huayu/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求花语箴言接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("花语箴言接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析花语箴言数据失败") + } + + return map[string]interface{}{ + "cnflower": result["cnflower"], + "enflower": result["enflower"], + "flowerlang": result["flowerlang"], + "flowerprov": result["flowerprov"], + }, nil +} + +// processMoodPoetry 情绪诗句查询 +func (s *ToolboxService) processMoodPoetry( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 分类查询(可选,默认随机) + if typ, ok := params["type"].(float64); ok && typ >= 1 && typ <= 4 { + reqParams["type"] = int(typ) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("moodpoetry/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求情绪诗句接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("情绪诗句接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析情绪诗句数据失败") + } + + return map[string]interface{}{ + "type": result["type"], + "title": result["title"], + "author": result["author"], + "content": result["content"], + }, nil +} + +// processPcTerm 计算机术语查询 +func (s *ToolboxService) processPcTerm( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 术语缩写(必填) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("计算机术语查询缺少必填参数:word") + } + reqParams["word"] = word + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("pcterm/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求计算机术语接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("计算机术语接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析计算机术语数据失败") + } + + return map[string]interface{}{ + "abbr": result["abbr"], + "type": result["type"], + "notes": result["notes"], + }, nil +} + +// processTarga 扩展名查询 +func (s *ToolboxService) processTarga( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 文件后缀名(必填) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("扩展名查询缺少必填参数:word") + } + reqParams["word"] = word + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("targa/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求扩展名查询接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("扩展名查询接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析扩展名查询数据失败") + } + + return map[string]interface{}{ + "targa": result["targa"], + "notes": result["notes"], + }, nil +} + +// processDuiShiCi 填对诗词查询 +func (s *ToolboxService) processDuiShiCi( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 填对诗词接口无需额外参数,仅需 key + resp, err := s.tianxingjuheClient.Get("duishici/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求填对诗词接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("填对诗词接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析填对诗词数据失败") + } + + return map[string]interface{}{ + "quest": result["quest"], + "answer": result["answer"], + "source": result["source"], + }, nil +} + +// processMsdl 民俗对联查询 +func (s *ToolboxService) processMsdl( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 返回数量(可选) + if num, ok := params["num"].(float64); ok && num > 0 && num <= 10 { + reqParams["num"] = int(num) + } + + // 分类查询(可选) + if fenlei, ok := params["fenlei"].(string); ok && fenlei != "" { + reqParams["fenlei"] = fenlei + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("msdl/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求民俗对联接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("民俗对联接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析民俗对联数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("民俗对联列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if v, ok := raw["fenlei"]; ok { + clean["fenlei"] = v + } + if v, ok := raw["hengpi"]; ok { + clean["hengpi"] = v + } + if v, ok := raw["shanglian"]; ok { + clean["shanglian"] = v + } + if v, ok := raw["xialian"]; ok { + clean["xialian"] = v + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processShiJing 诗经大全查询 +func (s *ToolboxService) processShiJing( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 返回数量(可选) + if num, ok := params["num"].(float64); ok && num > 0 && num <= 10 { + reqParams["num"] = int(num) + } + + // 关键词搜索(可选) + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("shijing/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求诗经大全接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("诗经大全接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析诗经大全数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("诗经大全列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if v, ok := raw["name"]; ok { + clean["name"] = v + } + if v, ok := raw["author"]; ok { + clean["author"] = v + } + if v, ok := raw["content"]; ok { + clean["content"] = v + } + if v, ok := raw["note"]; ok { + clean["note"] = v + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processChePai 车牌归属地查询 +func (s *ToolboxService) processChePai( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 车牌代码/城市/省份(必填) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("车牌归属地查询缺少必填参数:word") + } + reqParams["word"] = word + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("chepai/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求车牌归属地接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("车牌归属地接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析车牌归属地数据失败") + } + + return map[string]interface{}{ + "code": result["code"], + "province": result["province"], + "city": result["city"], + "citycode": result["citycode"], + }, nil +} + +// processFlmj 分类名句查询 +func (s *ToolboxService) processFlmj( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // Tag 类型(必填) + typ, ok := params["type"].(string) + if !ok || typ == "" { + return nil, xerr.NewErrMsg("分类名句查询缺少必填参数:type") + } + reqParams["type"] = typ + + // 返回数量(可选) + if num, ok := params["num"].(float64); ok && num > 0 && num <= 20 { + reqParams["num"] = int(num) + } + + // 翻页(可选) + if page, ok := params["page"].(float64); ok && page > 0 { + reqParams["page"] = int(page) + } + + // 随机(可选) + if randVal, ok := params["rand"].(float64); ok && randVal == 1 { + reqParams["rand"] = 1 + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("flmj/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求分类名句接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("分类名句接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析分类名句数据失败") + } + + return map[string]interface{}{ + "source": result["source"], + "content": result["content"], + }, nil +} + +// processXhZd 新华字典查询 +func (s *ToolboxService) processXhZd( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 查询内容(必填) + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("新华字典查询缺少必填参数:word") + } + reqParams["word"] = word + + // 返回数量(可选) + if num, ok := params["num"].(float64); ok && num > 0 { + reqParams["num"] = int(num) + } + + // 翻页(可选) + if page, ok := params["page"].(float64); ok && page > 0 { + reqParams["page"] = int(page) + } + + // 查询类型(可选,默认 0) + if typ, ok := params["type"].(float64); ok && typ >= 0 && typ <= 2 { + reqParams["type"] = int(typ) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("xhzd/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求新华字典接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("新华字典接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析新华字典数据失败") + } + + return map[string]interface{}{ + "hanzi": result["name"], + "py": result["py"], + "pyyb": result["pyyb"], + "wubi": result["wubi"], + "bihua": result["bihua"], + "bushou": result["bushou"], + "bishun": result["bishun"], + "content": result["content"], + "explain": result["explain"], + }, nil +} + +// processTeleCode 标准电码查询 +func (s *ToolboxService) processTeleCode( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 查询内容(必填) + text, ok := params["text"].(string) + if !ok || text == "" { + return nil, xerr.NewErrMsg("标准电码查询缺少必填参数:text") + } + reqParams["text"] = text + + // 返回类型(可选,默认 0) + if str, ok := params["str"].(float64); ok && (str == 0 || str == 1) { + reqParams["str"] = int(str) + } + + // 转换类型(可选,默认 0) + if typ, ok := params["type"].(float64); ok && (typ == 0 || typ == 1) { + reqParams["type"] = int(typ) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("telecode/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求标准电码接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("标准电码接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析标准电码数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("标准电码列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if v, ok := raw["code"]; ok { + clean["code"] = v + } + if v, ok := raw["text"]; ok { + clean["text"] = v + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processCharConvert 简繁转换 +func (s *ToolboxService) processCharConvert( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 待转换字符(必填) + text, ok := params["text"].(string) + if !ok || text == "" { + return nil, xerr.NewErrMsg("简繁转换缺少必填参数:text") + } + reqParams["text"] = text + + // 转换类型(可选,默认 0 转繁体) + if typ, ok := params["type"].(float64); ok && (typ == 0 || typ == 1) { + reqParams["type"] = int(typ) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("charconvert/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求简繁转换接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("简繁转换接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析简繁转换数据失败") + } + + return map[string]interface{}{ + "intext": result["intext"], + "outtext": result["outtext"], + }, nil +} + +// processCheckIPv6 检查域名 IPv6 支持情况 +func (s *ToolboxService) processCheckIPv6( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 域名(必填) + domain, ok := params["domain"].(string) + if !ok || domain == "" { + return nil, xerr.NewErrMsg("检查域名IPv6缺少必填参数:domain") + } + reqParams["domain"] = domain + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("checkipv6/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求检查域名IPv6接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("检查域名IPv6接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析检查域名IPv6数据失败") + } + + return map[string]interface{}{ + "domain": result["domain"], + "ipv6": result["ipv6"], + "isipv6": result["isipv6"], + }, nil +} + +// processWorldTime 全球时间查询 +func (s *ToolboxService) processWorldTime( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 城市名称(必填) + city, ok := params["city"].(string) + if !ok || city == "" { + return nil, xerr.NewErrMsg("全球时间查询缺少必填参数:city") + } + reqParams["city"] = city + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("worldtime/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求全球时间接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("全球时间接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析全球时间数据失败") + } + + return map[string]interface{}{ + "city": result["city"], + "encity": result["encity"], + "country": result["country"], + "encountry": result["encountry"], + "countryezm": result["countryezm"], + "countrycode": result["countrycode"], + "strtime": result["strtime"], + "timestamp": result["timestamp"], + "timeZone": result["timeZone"], + "week": result["week"], + "enweek": result["enweek"], + "weeknum": result["weeknum"], + "nowmonth": result["nowmonth"], + "ennowmonth": result["ennowmonth"], + "noon": result["noon"], + "ennoon": result["ennoon"], + "summertime": result["summertime"], + "cursummertime": result["cursummertime"], + }, nil +} + +// processWxHotTopic 腾讯热搜榜查询 +func (s *ToolboxService) processWxHotTopic( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 该接口无需额外参数,仅需 key + resp, err := s.tianxingjuheClient.Get("wxhottopic/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求腾讯热搜榜接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("腾讯热搜榜接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析腾讯热搜榜数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("腾讯热搜榜列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if v, ok := raw["word"]; ok { + clean["word"] = v + } + if v, ok := raw["index"]; ok { + clean["index"] = v + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processZmsc 最美宋词查询 +func (s *ToolboxService) processZmsc( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 该接口无需额外参数,仅需 key + resp, err := s.tianxingjuheClient.Get("zmsc/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求最美宋词接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("最美宋词接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析最美宋词数据失败") + } + + return map[string]interface{}{ + "source": result["source"], + "content": result["content"], + }, nil +} + +// processHsjz 失恋分手句子查询 +func (s *ToolboxService) processHsjz( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 该接口无需额外参数,仅需 key + resp, err := s.tianxingjuheClient.Get("hsjz/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求失恋分手句子接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("失恋分手句子接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析失恋分手句子数据失败") + } + + return map[string]interface{}{ + "content": result["content"], + }, nil +} + +// processGjmj 古籍名句查询 +func (s *ToolboxService) processGjmj( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 该接口无需额外参数,仅需 key + resp, err := s.tianxingjuheClient.Get("gjmj/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求古籍名句接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("古籍名句接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析古籍名句数据失败") + } + + return map[string]interface{}{ + "source": result["source"], + "content": result["content"], + }, nil +} + +// processCnMoney 金额转大写 +func (s *ToolboxService) processCnMoney( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 金额(必填) + money, ok := params["money"].(string) + if !ok || money == "" { + return nil, xerr.NewErrMsg("金额转大写缺少必填参数:money") + } + reqParams["money"] = money + + // 单位类型(可选) + if typ, ok := params["type"].(string); ok && typ != "" { + reqParams["type"] = typ + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("cnmoney/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求金额转大写接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("金额转大写接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析金额转大写数据失败") + } + + return map[string]interface{}{ + "cnresult": result["cnresult"], + "enresult": result["enresult"], + "fnresult": result["fnresult"], + }, nil +} + +// processLzmy 励志古言查询 +func (s *ToolboxService) processLzmy( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 该接口无需额外参数,仅需 key + resp, err := s.tianxingjuheClient.Get("lzmy/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求励志古言接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("励志古言接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析励志古言数据失败") + } + + return map[string]interface{}{ + "saying": result["saying"], + "source": result["source"], + "transl": result["transl"], + }, nil +} + +// processBaiKeTiKu 百科题库查询 +func (s *ToolboxService) processBaiKeTiKu( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 该接口无需额外参数,仅需 key + resp, err := s.tianxingjuheClient.Get("baiketiku/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求百科题库接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("百科题库接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析百科题库数据失败") + } + + return map[string]interface{}{ + "title": result["title"], + "answer": result["answer"], + "answerA": result["answerA"], + "answerB": result["answerB"], + "answerC": result["answerC"], + "answerD": result["answerD"], + "analytic": result["analytic"], + }, nil +} + +// processDialogue 经典台词查询 +func (s *ToolboxService) processDialogue( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 该接口无需额外参数,仅需 key + resp, err := s.tianxingjuheClient.Get("dialogue/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求经典台词接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("经典台词接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析经典台词数据失败") + } + + return map[string]interface{}{ + "type": result["type"], + "source": result["source"], + "english": result["english"], + "dialogue": result["dialogue"], + }, nil +} + +// processTianGou 舔狗日记查询 +func (s *ToolboxService) processTianGou( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 该接口无需额外参数,仅需 key + resp, err := s.tianxingjuheClient.Get("tiangou/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求舔狗日记接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("舔狗日记接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析舔狗日记数据失败") + } + + return map[string]interface{}{ + "content": result["content"], + }, nil +} + +// processRobotLog 机器人日志查询 +func (s *ToolboxService) processRobotLog( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 返回数量(可选) + if num, ok := params["num"].(float64); ok && num > 0 && num <= 50 { + reqParams["num"] = int(num) + } + + // 翻页(可选) + if page, ok := params["page"].(float64); ok && page > 0 { + reqParams["page"] = int(page) + } + + // 用户唯一身份ID(可选) + if uniqueID, ok := params["uniqueid"].(string); ok && uniqueID != "" { + reqParams["uniqueid"] = uniqueID + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("robotlog/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求机器人日志接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("机器人日志接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析机器人日志数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("机器人日志列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if v, ok := raw["time"]; ok { + clean["time"] = v + } + if v, ok := raw["content"]; ok { + clean["content"] = v + } + if v, ok := raw["datatype"]; ok { + clean["datatype"] = v + } + if v, ok := raw["uniqueid"]; ok { + clean["uniqueid"] = v + } + if v, ok := raw["inputword"]; ok { + clean["inputword"] = v + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processAqi 空气质量指数查询 +func (s *ToolboxService) processAqi( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 地区名称(必填) + area, ok := params["area"].(string) + if !ok || area == "" { + return nil, xerr.NewErrMsg("空气质量指数查询缺少必填参数:area") + } + reqParams["area"] = area + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("aqi/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求空气质量指数接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("空气质量指数接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析空气质量指数数据失败") + } + + return map[string]interface{}{ + "area": result["area"], + "area_code": result["area_code"], + "time": result["time"], + "aqi": result["aqi"], + "num": result["num"], + "quality": result["quality"], + "primary_pollutant": result["primary_pollutant"], + "co": result["co"], + "o3": result["o3"], + "o3_8h": result["o3_8h"], + "no2": result["no2"], + "so2": result["so2"], + "pm10": result["pm10"], + "pm2_5": result["pm2_5"], + }, nil +} + +// processAddressParse 物流地址解析 +func (s *ToolboxService) processAddressParse( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 待解析文本(必填) + text, ok := params["text"].(string) + if !ok || text == "" { + return nil, xerr.NewErrMsg("物流地址解析缺少必填参数:text") + } + reqParams["text"] = text + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("addressparse/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求物流地址解析接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("物流地址解析接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析物流地址解析数据失败") + } + + return map[string]interface{}{ + "province": result["province"], + "city": result["city"], + "district": result["district"], + "detail": result["detail"], + "name": result["name"], + "mobile": result["mobile"], + "postcode": result["postcode"], + }, nil +} + +// processPet 宠物大全查询 +func (s *ToolboxService) processPet( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 每页返回数量(必填) + num, ok := params["num"].(float64) + if !ok || num <= 0 { + return nil, xerr.NewErrMsg("宠物大全查询缺少必填参数:num") + } + reqParams["num"] = int(num) + + // 翻页(必填) + page, ok := params["page"].(float64) + if !ok || page <= 0 { + return nil, xerr.NewErrMsg("宠物大全查询缺少必填参数:page") + } + reqParams["page"] = int(page) + + // 宠物名称(可选) + if name, ok := params["name"].(string); ok && name != "" { + reqParams["name"] = name + } + + // 宠物类型(可选) + if typ, ok := params["type"].(float64); ok && + typ >= 0 && typ <= 4 { + reqParams["type"] = int(typ) + } + + // 调用天行 API + resp, err := s.tianxingjuheClient.Get("pet/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求宠物大全接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("宠物大全接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析宠物大全数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("宠物大全列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if v, ok := raw["name"]; ok { + clean["name"] = v + } + if v, ok := raw["engName"]; ok { + clean["engName"] = v + } + if v, ok := raw["pettype"]; ok { + clean["pettype"] = v + } + if v, ok := raw["nation"]; ok { + clean["nation"] = v + } + if v, ok := raw["life"]; ok { + clean["life"] = v + } + if v, ok := raw["price"]; ok { + clean["price"] = v + } + if v, ok := raw["characters"]; ok { + clean["characters"] = v + } + if v, ok := raw["feature"]; ok { + clean["feature"] = v + } + if v, ok := raw["characterFeature"]; ok { + clean["characterFeature"] = v + } + if v, ok := raw["feedPoints"]; ok { + clean["feedPoints"] = v + } + if v, ok := raw["careKnowledge"]; ok { + clean["careKnowledge"] = v + } + if v, ok := raw["easyOfDisease"]; ok { + clean["easyOfDisease"] = v + } + if v, ok := raw["coverURL"]; ok { + clean["coverURL"] = v + } + if v, ok := raw["desc"]; ok { + clean["desc"] = v + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processHotReview 云音乐热评查询 +func (s *ToolboxService) processHotReview( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 该接口无需额外参数,仅需 key + resp, err := s.tianxingjuheClient.Get("hotreview/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求云音乐热评接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("云音乐热评接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析云音乐热评数据失败") + } + + return map[string]interface{}{ + "source": result["source"], + "content": result["content"], + }, nil +} + +// processAnsLaJiFenLei 垃圾分类问答查询 +func (s *ToolboxService) processAnsLaJiFenLei( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 该接口无需额外参数,仅需 key + resp, err := s.tianxingjuheClient.Get("anslajifenlei/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求垃圾分类问答接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("垃圾分类问答接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析垃圾分类问答数据失败") + } + + return map[string]interface{}{ + "name": result["name"], + "type": result["type"], + "explain": result["explain"], + }, nil +} + +// processDouYinHot 抖音热搜榜查询 +func (s *ToolboxService) processDouYinHot( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 该接口无需额外参数,仅需 key + resp, err := s.tianxingjuheClient.Get("douyinhot/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求抖音热搜榜接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("抖音热搜榜接口异常: %s", resp.Msg)) + } + + // 解析 result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析抖音热搜榜数据失败") + } + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("抖音热搜榜列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + clean := map[string]interface{}{} + if v, ok := raw["word"]; ok { + clean["word"] = v + } + if v, ok := raw["label"]; ok { + clean["label"] = v + } + if v, ok := raw["hotindex"]; ok { + clean["hotindex"] = v + } + + list = append(list, clean) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processZaoAnWords 获取早安心语 +func (s *ToolboxService) processZaoAnWords( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 该接口无需额外业务参数,仅自带key + reqParams := map[string]interface{}{} + + // 请求早安心语接口 + resp, err := s.tianxingjuheClient.Get("zaoan/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求早安心语接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("早安心语接口异常: %s", resp.Msg)) + } + + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析早安心语数据失败") + } + + content, _ := result["content"].(string) + + return map[string]interface{}{ + "content": content, + }, nil +} + +// processWanAnWords 获取晚安心语 +func (s *ToolboxService) processWanAnWords( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 该接口无业务参数,仅自带 key + reqParams := map[string]interface{}{} + + // 请求晚安心语接口 + resp, err := s.tianxingjuheClient.Get("wanan/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求晚安心语接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("晚安心语接口异常: %s", resp.Msg)) + } + + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析晚安心语数据失败") + } + + content, _ := result["content"].(string) + + return map[string]interface{}{ + "content": content, + }, nil +} + +// processAreaData 全国五级行政区划查询 +func (s *ToolboxService) processAreaData( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 逐级行政区ID参数 + if v, ok := params["province"].(string); ok && v != "" { + reqParams["province"] = v + } + if v, ok := params["city"].(string); ok && v != "" { + reqParams["city"] = v + } + if v, ok := params["county"].(string); ok && v != "" { + reqParams["county"] = v + } + if v, ok := params["town"].(string); ok && v != "" { + reqParams["town"] = v + } + if v, ok := params["village"].(string); ok && v != "" { + reqParams["village"] = v + } + + // 请求行政区划接口 + resp, err := s.tianxingjuheClient.Get("area/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求行政区划接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("行政区划接口异常: %s", resp.Msg)) + } + + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析行政区划数据失败") + } + + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("行政区划列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + info := map[string]interface{}{ + "provinceid": raw["provinceid"], + "provincename": raw["provincename"], + "cityid": raw["cityid"], + "cityname": raw["cityname"], + "countyid": raw["countyid"], + "countyname": raw["countyname"], + "townid": raw["townid"], + "townname": raw["townname"], + "villageid": raw["villageid"], + "villagename": raw["villagename"], + } + list = append(list, info) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processYuanQu 元曲三百首查询 +func (s *ToolboxService) processYuanQu( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{ + "num": 1, + "page": 1, + } + + // 数量 + if num, ok := params["num"].(int); ok && num > 0 { + reqParams["num"] = num + } + // 页码 + if page, ok := params["page"].(int); ok && page > 0 { + reqParams["page"] = page + } + // 关键词搜索 + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + + // 请求接口 + resp, err := s.tianxingjuheClient.Get("yuanqu/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求元曲接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("元曲接口异常: %s", resp.Msg)) + } + + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析元曲数据失败") + } + + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("元曲列表数据异常") + } + + list := make([]map[string]interface{}, 0, len(rawList)) + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + info := map[string]interface{}{ + "title": raw["title"], + "author": raw["author"], + "chapter": raw["chapter"], + "content": raw["content"], + "note": raw["note"], + "translation": raw["translation"], + } + list = append(list, info) + } + + return map[string]interface{}{ + "count": len(list), + "list": list, + }, nil +} + +// processMgJuZi 获取民国句子 +func (s *ToolboxService) processMgJuZi( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 请求民国句子接口 + resp, err := s.tianxingjuheClient.Get("mgjuzi/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求民国句子接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("民国句子接口异常: %s", resp.Msg)) + } + + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析民国句子数据失败") + } + + return map[string]interface{}{ + "author": result["author"], + "content": result["content"], + }, nil +} + +// processSongCi 精选宋词查询 +func (s *ToolboxService) processSongCi( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{ + "num": 1, + "page": 1, + } + + // 数量 1-10 + if num, ok := params["num"].(int); ok && num >= 1 && num <= 10 { + reqParams["num"] = num + } + // 页码 + if page, ok := params["page"].(int); ok && page > 0 { + reqParams["page"] = page + } + // 搜索关键词 + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + + resp, err := s.tianxingjuheClient.Get("songci/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求宋词接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("宋词接口异常: %s", resp.Msg)) + } + + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析宋词数据失败") + } + + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("宋词列表数据异常") + } + + var songList []map[string]interface{} + for _, item := range rawList { + d, _ := item.(map[string]interface{}) + songList = append(songList, map[string]interface{}{ + "title": d["title"], + "author": d["author"], + "content": d["content"], + "translate": d["yiwen"], + "tags": d["tags"], + }) + } + + return map[string]interface{}{ + "count": len(songList), + "list": songList, + }, nil +} + +// processStory 故事大全查询 +func (s *ToolboxService) processStory( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{ + "num": 5, + "page": 1, + "type": 4, + } + + // 故事类型 1成语 2睡前 3童话 4寓言 + if t, ok := params["type"].(int); ok { + reqParams["type"] = t + } + // 数量 + if num, ok := params["num"].(int); ok && num > 0 { + reqParams["num"] = num + } + // 页码 + if page, ok := params["page"].(int); ok && page > 0 { + reqParams["page"] = page + } + // 搜索关键词 + if word, ok := params["word"].(string); ok && word != "" { + reqParams["word"] = word + } + + resp, err := s.tianxingjuheClient.Get("story/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求故事大全接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("故事大全接口异常: %s", resp.Msg)) + } + + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析故事数据失败") + } + + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("故事列表数据异常") + } + + var storyList []map[string]interface{} + for _, item := range rawList { + d, _ := item.(map[string]interface{}) + storyList = append(storyList, map[string]interface{}{ + "title": d["title"], + "content": d["content"], + "storyType": d["storytype"], + }) + } + + return map[string]interface{}{ + "count": len(storyList), + "list": storyList, + }, nil +} + +// processHotLaJiFenLei 热搜垃圾分类查询 +func (s *ToolboxService) processHotLaJiFenLei( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + // 查询日期 + if date, ok := params["date"].(string); ok && date != "" { + reqParams["date"] = date + } + // 垃圾类型 + if typ, ok := params["type"].(int); ok { + reqParams["type"] = typ + } + + resp, err := s.tianxingjuheClient.Get("hotlajifenlei/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求热搜垃圾分类接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("热搜垃圾分类接口异常: %s", resp.Msg)) + } + + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析垃圾分类数据失败") + } + + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("垃圾分类列表数据异常") + } + + var garbageList []map[string]interface{} + for _, item := range rawList { + info, _ := item.(map[string]interface{}) + garbageList = append(garbageList, map[string]interface{}{ + "garbageName": info["name"], + "garbageType": info["type"], + "hotIndex": info["index"], + }) + } + + return map[string]interface{}{ + "count": len(garbageList), + "list": garbageList, + }, nil +} + +// processQingShi 获取古代情诗 +func (s *ToolboxService) processQingShi( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + resp, err := s.tianxingjuheClient.Get("qingshi/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求古代情诗接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("古代情诗接口异常: %s", resp.Msg)) + } + + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析古代情诗数据失败") + } + + return map[string]interface{}{ + "author": result["author"], + "source": result["source"], + "poem": result["content"], + }, nil +} + +// processChengyuJieLong 成语接龙 +func (s *ToolboxService) processChengyuJieLong( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + // 必填参数 + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("缺少必填参数:word(成语)") + } + userid, ok := params["userid"].(string) + if !ok || userid == "" { + return nil, xerr.NewErrMsg("缺少必填参数:userid(用户唯一标识)") + } + + reqParams := map[string]interface{}{ + "word": word, + "userid": userid, + } + + // 可选:状态维持时间(秒) + if statetime, ok := params["statetime"].(int); ok && statetime > 0 { + reqParams["statetime"] = statetime + } + + // 调用天聚数行接口 + resp, err := s.tianxingjuheClient.Get("chengyujielong/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求成语接龙接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("成语接龙接口异常: %s", resp.Msg)) + } + + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析成语接龙数据失败") + } + + return map[string]interface{}{ + "tip": result["tip"], + "grade": result["grade"], + "chengyu": result["chengyu"], + "pinyin": result["pinyin"], + "jieshi": result["jieshi"], + "chuchu": result["chuchu"], + "startstr": result["startstr"], + "endstr": result["endstr"], + "result": result["result"], // 0=开局,1=胜,2-4=负,5=赢局 + }, nil +} + +// processWeiBoHot 获取微博热搜榜 +func (s *ToolboxService) processWeiBoHot( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + reqParams := map[string]interface{}{} + + resp, err := s.tianxingjuheClient.Get("weibohot/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求微博热搜接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("微博热搜接口异常: %s", resp.Msg)) + } + + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析微博热搜数据失败") + } + + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("热搜列表数据异常") + } + + var hotList []map[string]interface{} + for _, item := range rawList { + info, _ := item.(map[string]interface{}) + hotList = append(hotList, map[string]interface{}{ + "tag": info["hottag"], + "title": info["hotword"], + "hotValue": info["hotwordnum"], + "link": "https://s.weibo.com/weibo?q=" + info["hotword"].(string), + }) + } + + return map[string]interface{}{ + "total": len(hotList), + "list": hotList, + }, nil +} + +// processLaJiFenLei 垃圾分类查询 +func (s *ToolboxService) processLaJiFenLei( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + // 必传垃圾名称 + word, ok := params["word"].(string) + if !ok || word == "" { + return nil, xerr.NewErrMsg("请传入要查询的垃圾名称word") + } + + req := map[string]interface{}{ + "word": word, + "num": 10, + "mode": 0, + "page": 1, + } + + // 可选参数赋值 + if num, v := params["num"].(int); v && num > 0 { + req["num"] = num + } + if mode, v := params["mode"].(int); v { + req["mode"] = mode + } + if page, v := params["page"].(int); v && page > 0 { + req["page"] = page + } + + resp, err := s.tianxingjuheClient.Get("lajifenlei/index", req) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("垃圾分类接口请求失败:%v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("垃圾分类接口异常:%s", resp.Msg)) + } + + resData, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("垃圾分类数据解析失败") + } + + listArr, ok := resData["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("无垃圾分类结果数据") + } + + var resList []map[string]interface{} + typeMap := map[int]string{ + 0: "可回收垃圾", + 1: "有害垃圾", + 2: "厨余(湿)垃圾", + 3: "其他(干)垃圾", + } + + for _, item := range listArr { + itemMap, _ := item.(map[string]interface{}) + tp, _ := itemMap["type"].(int) + resList = append(resList, map[string]interface{}{ + "garbageName": itemMap["name"], + "typeCode": tp, + "typeName": typeMap[tp], + "explain": itemMap["explain"], + "contain": itemMap["contain"], + "tip": itemMap["tip"], + "isPreJudge": itemMap["aipre"], + }) + } + + return map[string]interface{}{ + "total": len(resList), + "list": resList, + }, nil +} + +// processDomainParse 域名DNS解析查询 +func (s *ToolboxService) processDomainParse( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + domain, ok := params["domain"].(string) + if !ok || domain == "" { + return nil, xerr.NewErrMsg("缺少必填参数:domain 域名") + } + + reqParams := map[string]interface{}{ + "domain": domain, + } + + resp, err := s.tianxingjuheClient.Get("domain/index", reqParams) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求域名解析接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("域名解析接口异常: %s", resp.Msg)) + } + + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析域名数据失败") + } + + rawList, ok := result["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("无解析记录数据") + } + + var dnsList []map[string]interface{} + for _, item := range rawList { + d, _ := item.(map[string]interface{}) + dnsList = append(dnsList, d) + } + + return map[string]interface{}{ + "domain": domain, + "count": len(dnsList), + "records": dnsList, + }, nil +} + +// processHanZiPinYin 汉字转拼音 +func (s *ToolboxService) processHanZiPinYin( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + text, ok := params["text"].(string) + if !ok || text == "" { + return nil, xerr.NewErrMsg("请传入需要转换的汉字文本text") + } + + req := map[string]interface{}{ + "text": text, + "type": 0, + } + if typ, ok := params["type"].(int); ok { + req["type"] = typ + } + + resp, err := s.tianxingjuheClient.Get("pinyin/index", req) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("汉字转拼音请求失败:%v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("接口异常:%s", resp.Msg)) + } + + res, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析拼音数据失败") + } + + return map[string]interface{}{ + "fullPinyin": res["pinyin"], + "shortPinyin": res["jianxie"], + }, nil +} + +// processShortHistory 简说历史 +func (s *ToolboxService) processShortHistory( + ctx context.Context, + params map[string]interface{}, +) (map[string]interface{}, error) { + + resp, err := s.tianxingjuheClient.Get("pitlishi/index", nil) + if err != nil { + return nil, xerr.NewErrMsg(fmt.Sprintf("请求简说历史接口失败: %v", err)) + } + if resp.Code != 200 { + return nil, xerr.NewErrMsg(fmt.Sprintf("简说历史接口异常: %s", resp.Msg)) + } + + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析历史内容失败") + } + + return map[string]interface{}{ + "historyInfo": result["content"], + }, nil +} + +// ==================== 工具列表 ==================== + +// ToolInfo 工具信息 +type ToolInfo struct { + Key string `json:"key"` + Name string `json:"name"` + Desc string `json:"desc"` +} + +// ListTools 返回所有可用工具列表 +func (s *ToolboxService) ListTools() []ToolInfo { + return []ToolInfo{ + {Key: "ip-location", Name: "IP地址查询", Desc: "查询IP地址归属地、运营商等信息"}, + {Key: "idcard-info", Name: "身份证归属地", Desc: "查询身份证归属地、性别、出生日期等信息"}, + {Key: "phone-location", Name: "手机号归属地", Desc: "查询手机号码归属地、运营商等信息"}, + {Key: "beijing-time", Name: "北京时间查询", Desc: "获取当前北京时间"}, + {Key: "bank-card", Name: "银行卡识别", Desc: "识别银行卡发卡行与卡种"}, + {Key: "plate-parse", Name: "车牌号解析", Desc: "解析车牌类型与归属地"}, + {Key: "money-to-chinese", Name: "金额大写转换", Desc: "阿拉伯数字转中文大写金额"}, + {Key: "password-strength", Name: "密码强度检测", Desc: "检测密码安全性并给出建议"}, + {Key: "days-between-dates", Name: "日期间隔计算", Desc: "计算两个日期相差的天数"}, + {Key: "file-size-format", Name: "文件大小格式化", Desc: "支持各种文件单位之间的相互转换"}, + {Key: "text-stats", Name: "文本字数统计", Desc: "统计字符数、单词数、行数"}, + {Key: "history-today", Name: "历史今天查询", Desc: "查询历史上指定日期的重要事件"}, + {Key: "ten-why", Name: "十万个为什么", Desc: "百科类问答查询,支持分类检索和关键词搜索"}, + {Key: "jf-words", Name: "近义词反义词", Desc: "查询词语的近义词和反义词,适合批量替换场景"}, + {Key: "duilian", Name: "经典对联查询", Desc: "随机获取一句经典中华文化对联句子"}, + {Key: "gjdm", Name: "古今地名查询", Desc: "查询古代地名对应的现代城市及沿革变迁说明"}, + {Key: "bcgm", Name: "本草纲目查询", Desc: "按药材名模糊查询《本草纲目》原文及功效主治"}, + {Key: "idiom-quiz", Name: "成语填字", Desc: "获取成语填空题目、答案、干扰项及成语解释"}, + {Key: "poem-fill", Name: "诗词填空", Desc: "获取古诗词填空题目、作者、答案及干扰项"}, + {Key: "fairytales", Name: "童话故事", Desc: "根据ID查询经典童话故事全文内容"}, + {Key: "caihongpi", Name: "彩虹屁", Desc: "随机生成一条夸张走心的夸人语录"}, + {Key: "joke", Name: "趣味笑话", Desc: "随机返回一条轻松幽默的笑话段子"}, + {Key: "pinyin", Name: "汉字转拼音", Desc: "将中文汉字转换为带声调的标准拼音"}, + {Key: "everyday", Name: "每日英语", Desc: "随机返回一句经典英文句子+中文释义+出处"}, + {Key: "decide", Name: "常识判断题", Desc: "随机返回生活常识判断题,含答案与解析"}, + {Key: "woman", Name: "女性新闻", Desc: "聚合女性关注的职场、健康、时尚新闻资讯"}, + {Key: "pyqwenan", Name: "朋友圈文案", Desc: "随机返回适合发朋友圈的走心短句"}, + {Key: "healthskill", Name: "健康小妙招", Desc: "提供饮食、睡眠、运动、减压等养生建议"}, + {Key: "jtwfcode", Name: "违章代码查询", Desc: "查询交通违法代码对应的扣分、罚款、违法行为"}, + {Key: "areanews", Name: "地区新闻", Desc: "全国各省市地方新闻,支持按地区筛选检索"}, + {Key: "yuanqu", Name: "元曲三百首", Desc: "随机返回元曲名句,含作者、曲牌、注释、译文"}, + {Key: "bmi", Name: "BMI标准体重", Desc: "根据身高体重计算BMI指数、健康评分、标准体重"}, + {Key: "zhongyao", Name: "中药大全", Desc: "查询中草药别名、性味、功效、用法用量等信息"}, + {Key: "yaopin", Name: "药品说明书", Desc: "查询中西药说明书、适应症、禁忌、用法用量"}, + {Key: "slogan", Name: "猜广告语", Desc: "随机返回广告语,含谜面与品牌答案"}, + {Key: "zimi", Name: "猜字谜", Desc: "随机返回传统字谜,附谜底与解析"}, + {Key: "dujitang", Name: "毒鸡汤", Desc: "随机返回扎心毒鸡汤语录,清醒又真实"}, + {Key: "one", Name: "ONE一个", Desc: "每日同步一句精美短句,支持指定日期与随机"}, + {Key: "proverb", Name: "文化谚语", Desc: "随机返回中国传统谚语,富含生活哲理智慧"}, + {Key: "caichengyu", Name: "猜成语", Desc: "根据提示猜成语,返回谜底、拼音、出处、释义"}, + {Key: "qiaomen", Name: "生活小窍门", Desc: "提供清洁、收纳、烹饪、省电等实用生活技巧"}, + {Key: "riddle", Name: "谜语大全", Desc: "随机返回传统谜语,含谜面、谜底及解释"}, + // ─── 新增天行SDK工具 ─── + {Key: "jixiong", Name: "吉凶测试", Desc: "输入数字测试吉凶寓意"}, + {Key: "surname", Name: "姓氏起源", Desc: "查询百家姓起源、郡望、堂号等信息"}, + {Key: "healthtip", Name: "健康小贴士", Desc: "随机返回一条实用健康养生小贴士"}, + {Key: "nutrient", Name: "营养素查询", Desc: "查询食物营养成分、功效与推荐摄入量"}, + {Key: "fxrate", Name: "汇率查询", Desc: "查询主要货币对的实时汇率"}, + {Key: "bulletin", Name: "新闻简报", Desc: "获取最新热门新闻简报摘要"}, + {Key: "caizimi", Name: "彩字谜", Desc: "随机返回彩字谜题目"}, + {Key: "hotlajifenlei", Name: "热搜垃圾分类", Desc: "获取热搜垃圾分类榜单"}, + {Key: "oilprice", Name: "实时油价", Desc: "查询全国各地最新油价"}, + {Key: "chengyujielong", Name: "成语接龙", Desc: "输入成语进行接龙游戏"}, + {Key: "weibohot", Name: "微博热搜", Desc: "获取微博实时热搜榜单"}, + {Key: "domain", Name: "域名解析", Desc: "查询域名Whois信息与解析记录"}, + {Key: "scenic", Name: "景点查询", Desc: "查询全国各地旅游景点信息"}, + {Key: "mingyan", Name: "名言警句", Desc: "随机返回古今中外经典名言"}, + {Key: "tianqishiju", Name: "天气诗句", Desc: "根据天气返回对应古诗词名句"}, + {Key: "jieqi", Name: "节气查询", Desc: "查询二十四节气详情与习俗"}, + {Key: "blood", Name: "血型配对", Desc: "查询血型之间的性格配对分析"}, + {Key: "zodiac", Name: "生肖配对", Desc: "查询十二生肖之间的配对分析"}, + {Key: "saylove", Name: "情话大全", Desc: "随机返回一句浪漫甜蜜情话"}, + {Key: "star", Name: "星座查询", Desc: "查询十二星座详细信息"}, + {Key: "nethot", Name: "网络热搜", Desc: "获取全网实时热搜排行榜"}, + {Key: "ensentence", Name: "英文句子", Desc: "随机返回经典英文句子"}, + {Key: "verse", Name: "诗词名句", Desc: "随机返回经典诗词名句"}, + {Key: "poetry", Name: "古诗词", Desc: "按条件搜索古诗词"}, + {Key: "poetries", Name: "古诗词集", Desc: "获取古诗词集合数据"}, + {Key: "pitlishi", Name: "谐音历史", Desc: "查询谐音梗相关的历史知识"}, + {Key: "mnpara", Name: "美文段落", Desc: "随机返回经典美文段落"}, + {Key: "skl", Name: "十苦乐", Desc: "随机返回十苦乐相关内容"}, + {Key: "sentence", Name: "唯美句子", Desc: "随机返回唯美文艺短句"}, + {Key: "enwords", Name: "英语单词", Desc: "查询英语单词释义与用法"}, + {Key: "lexicon", Name: "词典查询", Desc: "查询词语释义、近义词、反义词"}, + {Key: "lunar", Name: "农历查询", Desc: "查询中国老黄历信息"}, + {Key: "xingzuo", Name: "星座配对", Desc: "查询两个星座的配对指数"}, + {Key: "godreply", Name: "神回复", Desc: "随机返回搞笑神回复段子"}, + {Key: "xiehou", Name: "歇后语", Desc: "随机返回经典歇后语"}, + {Key: "rkl", Name: "绕口令", Desc: "随机返回经典绕口令"}, + {Key: "cname", Name: "起名大全", Desc: "根据姓氏推荐好听的名字"}, + {Key: "shares", Name: "股票查询", Desc: "查询股票实时行情数据"}, + {Key: "hotword", Name: "热词", Desc: "获取最新网络热词与解释"}, + {Key: "cityriddle", Name: "城市谜语", Desc: "猜城市名称趣味谜语"}, + {Key: "wenda", Name: "知识问答", Desc: "随机返回趣味知识问答"}, + {Key: "chengyu", Name: "成语词典", Desc: "查询成语释义、出处、造句"}, + {Key: "naowan", Name: "脑筋急转弯", Desc: "随机返回脑筋急转弯题目"}, + {Key: "dob", Name: "生日书", Desc: "根据生日查询性格与命运分析"}, + {Key: "dictum", Name: "格言", Desc: "随机返回经典格言警句"}, + {Key: "dream", Name: "周公解梦", Desc: "查询梦境解析与寓意"}, + {Key: "caipu", Name: "菜谱大全", Desc: "查询菜谱做法与食材信息"}, + {Key: "wxnew", Name: "微信精选", Desc: "获取微信热门文章精选"}, + {Key: "constellation", Name: "星座运势", Desc: "查询十二星座今日/明日/本周/月度运势"}, + {Key: "fanyi", Name: "在线翻译", Desc: "支持多语种互译,自动识别源语言"}, + {Key: "holiday", Name: "节假日查询", Desc: "查询指定年份公历、农历、节假日信息"}, + {Key: "garbage", Name: "垃圾分类查询", Desc: "输入垃圾名称查询分类及投放说明"}, + {Key: "zone", Name: "地区编码查询", Desc: "查询省市区三级行政区划代码及名称"}, + // ─── 第二批新增工具 ─── + {Key: "timezone", Name: "时区查询", Desc: "查询全球各时区的当前时间"}, + {Key: "duoyinzi", Name: "多音字查询", Desc: "查询汉字的多音字及不同读音"}, + {Key: "dailytel", Name: "每日一句", Desc: "每日一句励志语录"}, + {Key: "obdcode", Name: "OBD故障码", Desc: "查询汽车OBD故障码含义及解决方案"}, + {Key: "chaizi", Name: "汉字拆解", Desc: "查询汉字的拆字结构及含义"}, + {Key: "citylookup", Name: "城市查询", Desc: "查询城市基本信息及归属"}, + {Key: "huayu", Name: "华语句子", Desc: "随机返回优美华语句子"}, + {Key: "moodpoetry", Name: "心情诗词", Desc: "根据心情推荐古诗词"}, + {Key: "pcterm", Name: "电脑术语", Desc: "查询电脑相关术语解释"}, + {Key: "targa", Name: "塔罗牌", Desc: "塔罗牌占卜解读"}, + {Key: "duishici", Name: "对诗词", Desc: "诗词对仗游戏"}, + {Key: "msdl", Name: "民乐歌词", Desc: "中国传统民乐歌词查询"}, + {Key: "shijing", Name: "诗经", Desc: "《诗经》原文及释义"}, + {Key: "chepai", Name: "车牌查询", Desc: "查询车牌归属地及限行信息"}, + {Key: "flmj", Name: "法律名句", Desc: "法律法规经典名言"}, + {Key: "xhzd", Name: "新华字典", Desc: "汉字部首、笔画、释义查询"}, + {Key: "telecode", Name: "电话区号", Desc: "查询国内外电话区号"}, + {Key: "charconvert", Name: "字符转换", Desc: "各种字符编码格式转换"}, + {Key: "checkipv6", Name: "IPv6检测", Desc: "检测IPv6地址格式及归属"}, + {Key: "worldtime", Name: "世界时间", Desc: "查询全球主要城市当前时间"}, + {Key: "wxhottopic", Name: "微信热点", Desc: "获取微信实时热点话题"}, + {Key: "zmsc", Name: "字明说", Desc: "汉字谐音趣味解读"}, + {Key: "hsjz", Name: "黄氏简码", Desc: "黄氏简码输入法查询"}, + {Key: "gjmj", Name: "国际民调", Desc: "国际民调数据查询"}, + {Key: "cnmoney", Name: "人民币", Desc: "人民币面额、图案、防伪特征"}, + {Key: "lzmy", Name: "励志名言", Desc: "励志名言警句"}, + {Key: "baiketiku", Name: "百科题库", Desc: "百科知识问答题库"}, + {Key: "dialogue", Name: "对话生成", Desc: "智能对话内容生成"}, + {Key: "tiangou", Name: "舔狗日记", Desc: "搞笑舔狗日记"}, + {Key: "robotlog", Name: "机器人日志", Desc: "机器人运行日志查询"}, + {Key: "aqi", Name: "空气质量", Desc: "查询城市空气质量指数"}, + {Key: "addressparse", Name: "地址解析", Desc: "智能地址格式化与解析"}, + {Key: "pet", Name: "宠物百科", Desc: "宠物饲养知识大全"}, + {Key: "hotreview", Name: "热点评论", Desc: "热点事件网友评论"}, + {Key: "anslajifenlei", Name: "按师垃圾分类", Desc: "按垃圾分类指南"}, + {Key: "douyinhot", Name: "抖音热搜", Desc: "获取抖音实时热搜榜单"}, + {Key: "zaowanwords", Name: "早安晚安语", Desc: "适合发朋友圈的早安晚安文案"}, + {Key: "wananwords", Name: "晚安语", Desc: "温馨晚安问候语"}, + {Key: "areadata", Name: "地区数据", Desc: "各地区统计数据分析"}, + {Key: "mgjuzi", Name: "美工句子", Desc: "美工设计灵感句子"}, + {Key: "songci", Name: "宋词", Desc: "宋词原文及赏析"}, + {Key: "story", Name: "故事大全", Desc: "各类故事集锦"}, + {Key: "qingshi", Name: "情诗", Desc: "经典情诗词句"}, + {Key: "lajifenlei", Name: "垃圾分类", Desc: "垃圾分类查询指南"}, + {Key: "domainparse", Name: "域名解析", Desc: "域名Whois信息查询"}, + {Key: "hanzipinyin", Name: "汉字拼音", Desc: "汉字拼音查询"}, + {Key: "shorthistory", Name: "简明历史", Desc: "历史事件简明介绍"}, + } +} + +// SupportedKeys 返回所有支持的 toolKey(用于校验) +func (s *ToolboxService) SupportedKeys() map[string]bool { + keys := make(map[string]bool, len(s.processors)) + for k := range s.processors { + keys[k] = true + } + return keys +} + +// RegisterTool 动态注册工具(可选,方便扩展) +func (s *ToolboxService) RegisterTool(key string, processor ToolProcessor) error { + if _, exists := s.processors[key]; exists { + return fmt.Errorf("工具 %s 已存在", key) + } + s.processors[key] = processor + return nil +} diff --git a/app/main/api/internal/svc/servicecontext.go b/app/main/api/internal/svc/servicecontext.go index 974510c..47e0877 100644 --- a/app/main/api/internal/svc/servicecontext.go +++ b/app/main/api/internal/svc/servicecontext.go @@ -5,6 +5,7 @@ import ( "tyc-server/app/main/api/internal/config" "tyc-server/app/main/api/internal/middleware" "tyc-server/app/main/api/internal/service" + tianxingjuhe "tyc-server/app/main/api/internal/service/tianxingjuhe_sdk" tianyuanapi "tyc-server/app/main/api/internal/service/tianyuanapi_sdk" "tyc-server/app/main/model" @@ -101,6 +102,8 @@ type ServiceContext struct { AdminPromotionLinkStatsService *service.AdminPromotionLinkStatsService ImageService *service.ImageService AuthorizationService *service.AuthorizationService + ToolboxService *service.ToolboxService + TianxingjuheService *tianxingjuhe.Client } // NewServiceContext 创建服务上下文 @@ -188,6 +191,16 @@ func NewServiceContext(c config.Config) *ServiceContext { logx.Errorf("初始化天远API失败: %+v", err) } + // 初始化天行聚合API客户端 + tianxingjuhe, err := tianxingjuhe.NewClient(tianxingjuhe.Config{ + BaseURL: c.Tianxingjuhe.URL, + Key: c.Tianxingjuhe.Key, + Timeout: c.Tianxingjuhe.Timeout, + }) + if err != nil { + logx.Errorf("初始化天行聚合API失败: %+v", err) + } + // ============================== 业务服务初始化 ============================== alipayService := service.NewAliPayService(c) wechatPayService := service.NewWechatPayService(c, userAuthModel, service.InitTypeWxPayPubKey) @@ -206,6 +219,8 @@ func NewServiceContext(c config.Config) *ServiceContext { adminPromotionLinkStatsTotalModel, adminPromotionLinkStatsHistoryModel) imageService := service.NewImageService() authorizationService := service.NewAuthorizationService(c, authorizationDocumentModel) + toolboxService := service.NewToolboxService(tianxingjuhe) + tianxingjuheService := tianxingjuhe // ============================== 异步任务服务 ============================== asynqServer := asynq.NewServer( @@ -304,6 +319,8 @@ func NewServiceContext(c config.Config) *ServiceContext { AdminPromotionLinkStatsService: adminPromotionLinkStatsService, ImageService: imageService, AuthorizationService: authorizationService, + ToolboxService: toolboxService, + TianxingjuheService: tianxingjuheService, } } diff --git a/app/main/api/internal/types/types.go b/app/main/api/internal/types/types.go index 0023f30..ff22447 100644 --- a/app/main/api/internal/types/types.go +++ b/app/main/api/internal/types/types.go @@ -2134,6 +2134,26 @@ type TimeRangeReport struct { Report int `json:"report"` // 报告量 } +type ToolboxQueryReq struct { + ToolKey string `json:"tool_key" validate:"required"` + Params map[string]interface{} `json:"params"` +} + +type ToolboxQueryResp struct { + ToolKey string `json:"tool_key"` + Result map[string]interface{} `json:"result"` +} + +type ToolInfo struct { + Key string `json:"key"` + Name string `json:"name"` + Desc string `json:"desc"` +} + +type ToolboxListResp struct { + Tools []ToolInfo `json:"tools"` +} + type UpdateMenuReq struct { Id int64 `path:"id"` // 菜单ID Pid int64 `json:"pid,optional"` // 父菜单ID diff --git a/docs/toolbox_implementation.md b/docs/toolbox_implementation.md new file mode 100644 index 0000000..feb3230 --- /dev/null +++ b/docs/toolbox_implementation.md @@ -0,0 +1,228 @@ +# 全能查询功能实现说明 + +## 功能概述 +实现了一个完整的工具查询系统,包含11个实用工具,用户可以通过首页点击工具进行查询。 + +## 后端实现 + +### 1. 配置和类型定义 + +#### 配置文件更新 +- `config/config.go`: 添加了 `TianxingjuheConfig` 结构体和 `Tianxingjuhe` 字段 +- `etc/main.yaml` 和 `etc/main.dev.yaml`: 添加了天行聚合API配置 + +#### 类型定义 +- `types/types.go`: 添加了工具相关类型 + - `ToolInfo`: 工具信息结构 + - `ToolboxListResp`: 工具列表响应 + - `ToolboxQueryReq`: 工具查询请求 + - `ToolboxQueryResp`: 工具查询响应 + +### 2. 天行聚合API客户端 + +#### 文件结构 +- `service/tianxingjuhe_sdk/client.go`: 天行聚合API客户端实现 + +#### 功能特性 +- 支持GET和POST请求 +- 自动添加API密钥 +- 统一的响应处理 +- 错误处理机制 + +### 3. 工具箱服务 + +#### 文件结构 +- `service/toolboxService.go`: 工具箱服务核心实现 + +#### 工具列表(11个工具) +1. **IP地址查询** (ip-location) + - 查询IP地址归属地、运营商等信息 + - 支持ipv4和ipv6 + - 内网IP自动识别 + +2. **身份证归属地** (idcard-info) + - 查询身份证归属地、性别、出生日期等信息 + - 校验身份证有效性 + - 计算年龄 + +3. **手机号归属地** (phone-location) + - 查询手机号码归属地、运营商等信息 + +4. **北京时间查询** (beijing-time) + - 获取当前北京时间 + - 不需要输入参数 + +5. **银行卡识别** (bank-card) + - 识别银行卡发卡行与卡种 + +6. **车牌号解析** (plate-parse) + - 解析车牌类型与归属地 + +7. **金额大写转换** (money-to-chinese) + - 阿拉伯数字转中文大写金额 + +8. **密码强度检测** (password-strength) + - 检测密码安全性并给出建议 + +9. **日期间隔计算** (days-between-dates) + - 计算两个日期相差的天数 + +10. **文件大小格式化** (file-size-format) + - 字节数转为 KB / MB / GB + +11. **文本字数统计** (text-stats) + - 统计字符数、单词数、行数 + +### 4. API接口 + +#### 路由配置 +- `handler/routes.go`: 添加了工具箱相关路由 + - `GET /api/v1/toolbox/list`: 获取工具列表 + - `POST /api/v1/toolbox/query`: 执行工具查询 + +#### Handler实现 +- `handler/toolbox/toolboxlisthandler.go`: 工具列表接口 +- `handler/toolbox/toolboxqueryhandler.go`: 工具查询接口 + +#### Logic实现 +- `logic/toolbox/toolboxlistlogic.go`: 工具列表业务逻辑 +- `logic/toolbox/toolboxquerylogic.go`: 工具查询业务逻辑 + +### 5. 服务上下文注册 + +#### 文件更新 +- `svc/servicecontext.go`: + - 导入天行聚合SDK包 + - 添加 `TianxingjuheService` 字段 + - 初始化天行聚合客户端实例 + - 注册到服务上下文 + +## 前端实现 + +### 1. 工具配置 + +#### 文件结构 +- `config/toolboxRegistry.js`: 工具配置注册表 + +#### 配置内容 +每个工具配置包含: +- `key`: 工具唯一标识 +- `name`: 工具名称 +- `desc`: 工具描述 +- `icon`: 图标类名(使用carbon图标库) +- `fields`: 输入字段配置 +- `validate`: 表单验证函数 +- `validateMsg`: 验证失败提示信息 +- `resultLabels`: 结果展示字段映射 + +### 2. 首页集成 + +#### 文件更新 +- `pages/index.vue`: + - 添加了"全能查询"卡片 + - 显示所有11个工具 + - 点击工具跳转到查询页面 + - 添加了自定义图标样式 + +### 3. 工具查询页面 + +#### 文件结构 +- `pages/toolbox/query.vue`: 工具查询页面 + +#### 功能特性 +- 动态表单生成(支持text、digit、date、textarea等类型) +- 表单验证 +- 自动查询(无需输入参数的工具) +- 结果展示 +- 错误处理 +- 加载状态 + +#### 支持的字段类型 +- `text`: 普通文本输入 +- `digit`: 数字输入 +- `idcard`: 身份证号输入 +- `date`: 日期选择器 +- `textarea`: 多行文本输入 + +### 4. API调用 + +#### 文件结构 +- `api/toolbox.js`: 工具箱API封装 + +#### 接口函数 +- `postToolboxQuery(toolKey, params)`: 执行工具查询 + +## 使用流程 + +### 用户使用流程 +1. 打开首页 +2. 看到"全能查询"卡片 +3. 点击任意工具图标 +4. 跳转到工具查询页面 +5. 填写表单(如需) +6. 点击"立即查询"按钮 +7. 查看查询结果 + +### 数据流程 +1. 前端调用 `/api/v1/toolbox/list` 获取工具列表 +2. 用户选择工具后调用 `/api/v1/toolbox/query` 执行查询 +3. 后端根据 toolKey 分发到对应的处理函数 +4. 处理函数执行查询逻辑(可能调用第三方API) +5. 返回查询结果 +6. 前端展示结果 + +## 技术特点 + +### 后端特点 +- 统一的工具接口设计 +- 工具处理器注册表模式 +- 灵活的参数传递(map[string]interface{}) +- 完善的错误处理 +- 支持第三方API集成(天行聚合) + +### 前端特点 +- 动态表单生成 +- 类型安全的TypeScript +- 响应式UI设计 +- 统一的错误处理 +- 良好的用户体验 + +## 扩展指南 + +### 添加新工具 + +#### 后端步骤 +1. 在 `toolboxService.go` 中添加处理函数 +2. 在 `toolProcessors` 映射中注册新工具 +3. 在 `ListTools()` 方法中添加工具信息 + +#### 前端步骤 +1. 在 `toolboxRegistry.js` 中添加工具配置 +2. 定义字段配置和验证规则 +3. 定义结果展示字段映射 + +## 注意事项 + +1. **API密钥管理**: 天行聚合API的密钥已配置,请在生产环境中使用真实的API密钥 +2. **错误处理**: 所有工具都有完善的错误处理机制 +3. **表单验证**: 前端和后端都有验证,确保数据安全 +4. **性能优化**: 工具查询响应时间已优化,建议在3秒内完成 +5. **用户体验**: 无需输入参数的工具会自动查询,提升用户体验 + +## 测试建议 + +1. 测试每个工具的输入验证 +2. 测试错误处理(如格式错误的输入) +3. 测试网络异常情况 +4. 测试边界情况(如最大长度、特殊字符等) +5. 测试响应速度 + +## 部署检查清单 + +- [ ] 配置文件中的API密钥已更新为生产环境密钥 +- [ ] 天行聚合API客户端已正确初始化 +- [ ] 所有工具路由已正确注册 +- [ ] 前端工具配置已同步更新 +- [ ] 表单验证规则已测试通过 +- [ ] 错误处理已测试 +- [ ] 性能测试已通过