diff --git a/app/main/api/internal/service/1.md b/app/main/api/internal/service/1.md index f64b0ac..2122c64 100644 --- a/app/main/api/internal/service/1.md +++ b/app/main/api/internal/service/1.md @@ -1,10 +1,10 @@ 首页 -企业商务 -节假日 -节假日接口 -139 146929 -节假日正常服务支持MCP -判断指定日期是否为法定节假日、调休日或工作日,并返回薪资倍数。 +知识问答 +百科题库 +百科题库接口 +185 23883 +百科题库正常服务支持MCP +随机返回一道生活百科问答题,覆盖科学、健康、安全、常识等实用知识。 会员免费・升级会员畅享160+免费接口,立即升级>> 收藏 普通会员 @@ -29,32 +29,18 @@ MCP服务 帮助 生成小程序 -查询节假日信息,接口返回节日名称类型、农历信息、上班调休、工资倍数、假期范围、拼假建议、及工作日相关信息等。通过请求参数mode还可以指定是否返回当天有关的国内外节日信息。使用前建议阅读参考文章:节假日接口增加多种查询方式 -接口支持按年、按月、按日期范围和多个日期批量查询。如按年查询(type=1&date=2020)、按月查询(type=2&date=2020-10)、按日期范围查询(type=3&date=2020-11-1~2020-11-10)、按多个日期批量查询(date=2020-10-1,2020-11-12) - -请注意,按年查询只返回中国官方节假日信息;按月查询如只传年月,则为从该月第一天到最后一天;按范围查询,起点和终点用波浪号~分隔;按多个日期批量查询用英文半角逗号分隔。无论哪种查询方式,单次查询所包含的总日期数不能超过31天。 - -daycode表示日期类型,0表示工作日、1节假日、2双休日、3调休日(需上班)。判断是否需要上班建议用isnotwork字段,其中值为0表示上班,为1表示休息。wage表示薪资倍数,周末为两倍,法定节假日当天为三倍其他两倍(按年查询时返回三倍薪资的具体日期)。 - -提示:info字段和daycode并非对应关系,建议仅用于日期类型标记;节日指某个具体日期(如2021年1月1号),节假日指整个假期(如2021年1月1号至3号);节日范围宽泛(如会包含植树节、圣诞节),节假日指来自官方发布的有假节日,每年底政府公布后同步更新。 - +生活百科题库大全接口,部分结果返回详细解释。 接入点列表: -节假日查询 +百科题库查询 相关资源: -节假日功能演示 -节假日接口更新了,增加批量/范围按年按月查询 -如何利用节假日API让日程规划更轻松 -为什么节假日查询接口,按年查询返回250错误? -节假日接口数据有误,怎么回事? -节假日接口没有更新吗? -我想知道哪天是国家规定的3倍工资呢? -请问我如何知道节假日接口已经更新了下一年的数据呢? +百科题库功能演示 +百科题库接口不能查询指定题目的答案么? ▼ 接口信息 -判断日期是否为官方节假日支持按年、按月和范围批量查询 +默认随机返回一个百科问答带答案 -接口地址:https://apis.tianapi.com/jiejiari/index?key={apiKey} +接口地址:https://apis.tianapi.com/baiketiku/index?key={apiKey} 支持协议:http/https 请求方法:get/post 返回格式:utf-8 json @@ -67,9 +53,6 @@ daycode表示日期类型,0表示工作日、1节假日、2双休日、3调休 名称 类型 必须 示例值/默认值 说明 key string 是 您自己的ApiKey(注册账号后获得) API密钥 -date string 是 2021-01-01 查询日期或日期范围 -type int 是 0 查询类型,0批量、1按年、2按月、3范围 -mode int 否 0 查询模式,为1同时返回中外特殊节日信息 ▼ 返回示例 接口数据示例仅作为预览参考,请以实际测试结果为准 @@ -84,37 +67,13 @@ mode int 否 0 查询模式,为1同时返回中外特殊节日信息 "msg": "success", "code": 200, "result": { - "list": [ - { - "end": 2, - "now": 0, - "tip": "1月1日放假,共3天。", - "date": "2021-01-01", - "info": "节假日", - "name": "元旦节", - "rest": "2020年12月28日至2020年12月31日请假四天,与周末连休可拼七天长假。", - "wage": 3, - "start": 0, - "enname": "NewYear", - "remark": [ - "2021-12-26", - "2021-01-08" - ], - "daycode": 1, - "holiday": "1月1号", - "weekday": 5, - "lunarday": "十八", - "vacation": [ - "2021-01-01", - "2021-01-02", - "2021-01-03" - ], - "cnweekday": "星期五", - "isnotwork": 1, - "lunaryear": "庚子", - "lunarmonth": "冬月" - } - ] + "title": "新冠肺炎的最长潜伏期一般是多久?", + "answer": "C", + "answerA": "1-2天", + "answerB": "3-7天", + "answerC": "14天", + "answerD": "28天", + "analytic": "新型冠状病毒感染性肺炎属于呼吸道传播性疾病,该病一般最常见的传播途径有飞沫传播,气溶胶传播,粪口传播及眼部粘膜传播,潜伏期一般为3-5天,最长不超过14天左右,也有因人而异,超过以上天数。或许以无症状感染者,不发病。该病确诊有赖于核酸病毒检测,同时做好多饮水,勤洗手,出门戴口罩,避免人群聚集,导致交叉感染。" } } @@ -142,37 +101,10 @@ code int 200 状态码 msg string success 错误信息 result object {} 返回结果集 应用参数 -end int 6 假期终点计数 -now int 0 假期当前计数 -tip string 10月1日至7日放假调休,共7天...... 放假提示 -date string 2019-10-01 当前阳历日期 -info string 节假日 文字提示,工作日、节假日、节日、双休日、调休日 -name string 国庆节 节假日名称(中文) -rest string 10月8日至10月10日请假2天,与周末连休可拼11天长假。 拼假建议 -wage int 3 薪资法定倍数/按年查询时为具体日期 -start int 0 假期起点计数 -enname string National Day 节日名称(英文) -remark array ["2021-12-26","2021-01-08"] 调休日数组 -update boolean true/false 是否更新法定节假日(按年查询专有字段) -daycode int 1 日期类型,为0表示工作日、为1节假日、为2双休日、3为调休日(上班) -holiday string 10月1日 节日日期 -weekday int 2 星期(数字) -lunarday string 初三 农历日 -vacation array ["2021-01-01","2021-01-02","2021-01-03"] 节假日数组 -cnweekday string 星期二 星期(中文) -isnotwork int 1 是否需要上班,0为工作日,1为休息日 -lunaryear string 己亥 农历年 -lunarmonth string 九月 农历月 -▼ 接口价格 -本接口为会员免费类接口,可根据业务需求选择升级会员方案>> - - -不同会员方案仅每日调用量等配额上限不同,数据本身无区别 - - -会员方案 免费接口数 每日调用量 QPS 价格 -普通会员 10个 100次 3 免费 -高级会员 不限 1万次 20 29元/月、348元/年169元/年惠 -黄金会员 不限 50万次 30 89元/月、1068元/年529元/年惠 -钻石会员 不限 不限次 60 3380元/年1699元/年惠 -▼ 返回状态码 \ No newline at end of file +title string 新冠肺炎的最长潜伏期一般是多久? 问题 +answer string C 正确答案 +answerA string 1-2天 答案A +answerB string 3-7天 答案B +answerC string 14天 答案C +answerD string 28天 答案D +analytic string 新型冠状病毒感染性肺炎属于呼吸道传播性疾病... 分析结果 \ No newline at end of file diff --git a/app/main/api/internal/service/toolboxService.go b/app/main/api/internal/service/toolboxService.go index 3e4dcff..130eac9 100644 --- a/app/main/api/internal/service/toolboxService.go +++ b/app/main/api/internal/service/toolboxService.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "math" "net" "net/http" "regexp" @@ -38,7 +37,7 @@ import ( // 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, +// lexicon, enwords, ensentence, fanyi, dailytel, charconvert, // slogan, zimi // // 3️⃣ 文化知识类 (35个) @@ -100,7 +99,7 @@ func (s *ToolboxService) registerAllProcessors() { s.processors["beijing-time"] = processBeijingTime s.processors["bank-card"] = processBankCard s.processors["plate-parse"] = processPlateParse - s.processors["money-to-chinese"] = processMoneyToChinese + s.processors["money-to-chinese"] = s.processMoneyToChinese s.processors["password-strength"] = processPasswordStrength s.processors["days-between-dates"] = processDaysBetweenDates s.processors["file-size-format"] = processFileSizeFormat @@ -196,10 +195,10 @@ func (s *ToolboxService) registerAllProcessors() { // ─── 第二批新增工具 ─── s.processors["timezone"] = s.processTimeZone s.processors["domain"] = s.processDomainParse // 域名解析 - s.processors["duoyinzi"] = s.processDuoYinZi + s.processors["duoyinzi"] = s.processDuoYinZi //多音字 s.processors["dailytel"] = s.processDailyTel s.processors["obdcode"] = s.processObdCode - s.processors["chaizi"] = s.processChaiZi + s.processors["chaizi"] = s.processChaiZi //拆字 s.processors["huayu"] = s.processHuaYu s.processors["moodpoetry"] = s.processMoodPoetry s.processors["pcterm"] = s.processPcTerm @@ -680,74 +679,41 @@ func processPlateParse(ctx context.Context, params map[string]interface{}) (map[ } // processMoneyToChinese 金额转换为中文 -func processMoneyToChinese(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { - var amount float64 - switch v := params["money"].(type) { - case float64: - amount = v - case string: - amount, _ = strconv.ParseFloat(v, 64) - default: - return nil, xerr.NewErrMsg("金额不合法") +func (s *ToolboxService) processMoneyToChinese(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("金额不能为空") } - if amount < 0 || amount > 999999999999 { - return nil, xerr.NewErrMsg("金额不合法") + reqParams["money"] = money + + // 可选:单位类型,默认rmb(人民币元) + // type: usd美元单位元、usd_1美元单位分、rmb人民币单位元[默认]、rmb_1人民币单位分 + if typ, ok := params["type"].(string); ok && typ != "" { + reqParams["type"] = typ } - 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("元") + // 调用天行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)) } - 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("整") + // 解析result + result, ok := resp.Result.(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("解析金额转大写数据失败") } return map[string]interface{}{ - "amount": amount, - "chinese": result.String(), + "cnresult": result["cnresult"], // 中文大写金额 + "enresult": result["enresult"], // 英语大写金额 + "fnresult": result["fnresult"], // 西式三位分节法数字 }, nil } @@ -1665,10 +1631,12 @@ func (s *ToolboxService) processAreaNews(ctx context.Context, params map[string] } reqParams["areaname"] = areaname - // 可选参数:页码 - if page, ok := params["page"].(string); ok && page != "" { - reqParams["page"] = page + // 可选参数:页码(默认1) + page := "1" + if pageStr, ok := params["page"].(string); ok && pageStr != "" { + page = pageStr } + reqParams["page"] = page // 可选参数:关键词 if word, ok := params["word"].(string); ok && word != "" { @@ -1678,41 +1646,94 @@ func (s *ToolboxService) processAreaNews(ctx context.Context, params map[string] // 调用天行SDK 地区新闻接口 resp, err := s.tianxingjuheClient.Get("areanews/index", reqParams) if err != nil { + logx.WithContext(ctx).Errorf("[地区新闻] 请求失败: %v", err) return nil, xerr.NewErrMsg(fmt.Sprintf("请求天行数据地区新闻API失败: %v", err)) } if resp.Code != 200 { + logx.WithContext(ctx).Errorf("[地区新闻] API返回异常: code=%d, msg=%s", resp.Code, resp.Msg) return nil, xerr.NewErrMsg(fmt.Sprintf("地区新闻接口返回异常: %s", resp.Msg)) } + logx.WithContext(ctx).Infof("[地区新闻] API响应: %+v", resp.Result) + resultMap, ok := resp.Result.(map[string]interface{}) if !ok { + logx.WithContext(ctx).Errorf("[地区新闻] 解析result失败,类型: %T", resp.Result) return nil, xerr.NewErrMsg("解析地区新闻数据失败") } // 解析新闻列表 listArr, ok := resultMap["list"].([]interface{}) if !ok { + logx.WithContext(ctx).Errorf("[地区新闻] 解析list失败,result结构: %+v", resultMap) return nil, xerr.NewErrMsg("解析地区新闻列表失败") } + logx.WithContext(ctx).Infof("[地区新闻] 列表长度: %d", len(listArr)) + + // 字段白名单 + allowed := map[string]bool{ + "id": true, + "url": true, + "ctime": true, + "title": true, + "picUrl": true, + "source": true, + "description": true, + } + 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"], - }) + clean := map[string]interface{}{} + for k, v := range m { + if allowed[k] { + // 清理HTML标签(title和description) + if k == "title" || k == "description" { + if str, ok := v.(string); ok { + clean[k] = cleanHTMLContent(str) + } + } else { + clean[k] = v + } + } + } + // 至少要有标题(允许清理后为空,但原始必须有值) + if m["title"] != nil { + newsList = append(newsList, clean) + } } } + logx.WithContext(ctx).Infof("[地区新闻] 清理后列表长度: %d", len(newsList)) + + // 如果清理后列表为空,但原始有数据,可能是HTML清理导致的问题 + if len(newsList) == 0 && len(listArr) > 0 { + // logx.WithContext(ctx).Warnf("[地区新闻] 清理后列表为空,原始有%d条数据", len(listArr)) + // 返回部分数据,不清理HTML + for _, item := range listArr { + if m, ok := item.(map[string]interface{}); ok { + clean := map[string]interface{}{} + for k, v := range m { + if allowed[k] { + clean[k] = v + } + } + if m["title"] != nil { + newsList = append(newsList, clean) + } + } + } + } + + if len(newsList) == 0 { + return nil, xerr.NewErrMsg("未查询到该地区新闻") + } + return map[string]interface{}{ - "list": newsList, + "count": len(newsList), + "list": newsList, }, nil } @@ -1836,14 +1857,23 @@ func (s *ToolboxService) processZhongyao(ctx context.Context, params map[string] } reqParams["word"] = word - // 可选返回数量 - if num, ok := params["num"].(string); ok && num != "" { - reqParams["num"] = num + // 可选返回数量(默认1) + num := 1 + if numStr, ok := params["num"].(string); ok && numStr != "" { + if n, err := strconv.Atoi(numStr); err == nil && n > 0 { + num = n + } } - // 可选页码 - if page, ok := params["page"].(string); ok && page != "" { - reqParams["page"] = page + reqParams["num"] = num + + // 可选页码(默认1) + page := 1 + if pageStr, ok := params["page"].(string); ok && pageStr != "" { + if p, err := strconv.Atoi(pageStr); err == nil && p > 0 { + page = p + } } + reqParams["page"] = page // 调用天行SDK 中药大全接口 resp, err := s.tianxingjuheClient.Get("zhongyao/index", reqParams) @@ -1855,14 +1885,42 @@ func (s *ToolboxService) processZhongyao(ctx context.Context, params map[string] return nil, xerr.NewErrMsg(fmt.Sprintf("中药大全接口返回异常: %s", resp.Msg)) } + logx.WithContext(ctx).Infof("[中药大全] API响应: %+v", resp.Result) + + // 解析 result - 支持多种格式 resultMap, ok := resp.Result.(map[string]interface{}) if !ok { - return nil, xerr.NewErrMsg("解析中药大全数据失败") + return nil, xerr.NewErrMsg("解析中药大全数据失败:返回格式不是对象") + } + + // 检查是否是列表格式(有些天行API返回 { list: [...] }) + if listData, hasList := resultMap["list"].([]interface{}); hasList && len(listData) > 0 { + // 取第一条记录 + if firstItem, ok := listData[0].(map[string]interface{}); ok { + resultMap = firstItem + } + } + + // 检查是否有数据 + if resultMap["title"] == nil && resultMap["content"] == nil { + return nil, xerr.NewErrMsg("未查询到该中药信息") + } + + // 清理HTML标签 + title, _ := resultMap["title"].(string) + content, _ := resultMap["content"].(string) + + // 如果是字符串类型,清理HTML标签 + if title != "" { + title = cleanHTMLContent(title) + } + if content != "" { + content = cleanHTMLContent(content) } return map[string]interface{}{ - "title": resultMap["title"], - "content": resultMap["content"], + "title": title, + "content": content, }, nil } @@ -1877,15 +1935,23 @@ func (s *ToolboxService) processYaopin(ctx context.Context, params map[string]in } reqParams["word"] = word - // 可选参数:数量 - if num, ok := params["num"].(string); ok && num != "" { - reqParams["num"] = num + // 可选参数:数量(默认10) + num := 10 + if numStr, ok := params["num"].(string); ok && numStr != "" { + if n, err := strconv.Atoi(numStr); err == nil && n > 0 { + num = n + } } + reqParams["num"] = num - // 可选参数:页码 - if page, ok := params["page"].(string); ok && page != "" { - reqParams["page"] = page + // 可选参数:页码(默认1) + page := 1 + if pageStr, ok := params["page"].(string); ok && pageStr != "" { + if p, err := strconv.Atoi(pageStr); err == nil && p > 0 { + page = p + } } + reqParams["page"] = page // 调用天行SDK 药品说明书接口 resp, err := s.tianxingjuheClient.Get("yaopin/index", reqParams) @@ -1897,14 +1963,52 @@ func (s *ToolboxService) processYaopin(ctx context.Context, params map[string]in return nil, xerr.NewErrMsg(fmt.Sprintf("药品说明书接口返回异常: %s", resp.Msg)) } + logx.WithContext(ctx).Infof("[药品说明书] API响应: %+v", resp.Result) + + // 解析 result - 支持多种格式 + var list []map[string]interface{} + resultMap, ok := resp.Result.(map[string]interface{}) if !ok { - return nil, xerr.NewErrMsg("解析药品说明书数据失败") + return nil, xerr.NewErrMsg("解析药品说明书数据失败:返回格式不是对象") + } + + // 情况1:返回格式为 { list: [...] } + if listData, hasList := resultMap["list"].([]interface{}); hasList { + for _, item := range listData { + if m, ok := item.(map[string]interface{}); ok { + // 字段白名单 + 清理HTML标签 + clean := map[string]interface{}{} + if v, ok := m["title"].(string); ok { + clean["title"] = cleanHTMLContent(v) + } + if v, ok := m["content"].(string); ok { + clean["content"] = cleanHTMLContent(v) + } + if clean["title"] != nil { + list = append(list, clean) + } + } + } + } else if resultMap["title"] != nil { + // 情况2:返回格式为 { title: "...", content: "..." }(单条记录) + title, _ := resultMap["title"].(string) + content, _ := resultMap["content"].(string) + + clean := map[string]interface{}{ + "title": cleanHTMLContent(title), + "content": cleanHTMLContent(content), + } + list = append(list, clean) + } + + if len(list) == 0 { + return nil, xerr.NewErrMsg("未查询到该药品说明书") } return map[string]interface{}{ - "title": resultMap["title"], - "content": resultMap["content"], + "count": len(list), + "list": list, }, nil } @@ -2120,7 +2224,7 @@ func (s *ToolboxService) processRiddle(ctx context.Context, params map[string]in return map[string]interface{}{ "quest": resultMap["quest"], - "answer": resultMap["answer"], + "result": resultMap["answer"], }, nil } @@ -2159,8 +2263,20 @@ func (s *ToolboxService) processNutrient( ) (map[string]interface{}, error) { // 1. 参数提取(必填) - mode, ok := params["mode"].(float64) - if !ok { + var mode float64 + switch v := params["mode"].(type) { + case float64: + mode = v + case string: + if v == "" { + return nil, xerr.NewErrMsg("缺少必填参数 mode") + } + modeInt, err := strconv.Atoi(v) + if err != nil { + return nil, xerr.NewErrMsg("mode 参数必须是数字 0/1/2") + } + mode = float64(modeInt) + default: return nil, xerr.NewErrMsg("缺少必填参数 mode") } @@ -2198,13 +2314,25 @@ func (s *ToolboxService) processNutrient( return nil, xerr.NewErrMsg(fmt.Sprintf("营养成分接口异常: %s", resp.Msg)) } - // 5. 解析 result(数组 or 单对象都兼容) + // 5. 解析 result(天行 API 返回格式为 { list: [...] }) var list []map[string]interface{} + // 尝试从 result 中提取 list 字段 switch v := resp.Result.(type) { case map[string]interface{}: - list = append(list, v) + // 天行 API 的标准格式:result.list 是数组 + if dataList, ok := v["list"].([]interface{}); ok { + for _, item := range dataList { + if m, ok := item.(map[string]interface{}); ok { + list = append(list, m) + } + } + } else { + // 兼容单个对象的情况 + list = append(list, v) + } case []interface{}: + // 兼容直接返回数组的情况 for _, item := range v { if m, ok := item.(map[string]interface{}); ok { list = append(list, m) @@ -2214,6 +2342,10 @@ func (s *ToolboxService) processNutrient( return nil, xerr.NewErrMsg("解析营养成分数据失败") } + if len(list) == 0 { + return nil, xerr.NewErrMsg("未查询到相关数据") + } + // 6. 返回标准化结果 return map[string]interface{}{ "count": len(list), @@ -2580,9 +2712,10 @@ func (s *ToolboxService) processChengyuJielong( return nil, xerr.NewErrMsg("参数 word 不能为空,例如:一马当先") } + // 用户ID:如果没有提供,使用默认值(方便单人游戏) userID, ok := params["userid"].(string) if !ok || userID == "" { - return nil, xerr.NewErrMsg("参数 userid 不能为空,建议使用 openid / uuid") + userID = "1212" // 默认用户标识,单人游戏模式 } // 2. 请求参数 @@ -2656,60 +2789,36 @@ func (s *ToolboxService) processWeiboHot( 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)) - + var hotList []map[string]interface{} 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) - } + info, _ := item.(map[string]interface{}) + hotList = append(hotList, map[string]interface{}{ + "keyword": info["hotword"], // 搜索词 + "brief": info["hottag"], // 简介 + "index": info["hotwordnum"], // 热度 + }) } - // 6. 返回标准化结果 return map[string]interface{}{ "count": len(hotList), "list": hotList, @@ -4280,8 +4389,20 @@ func (s *ToolboxService) processRKL( // 字段白名单 clean := map[string]interface{}{} if content, ok := raw["content"]; ok && content != nil { - clean["content"] = content - list = append(list, clean) + // 去除HTML标签 + if contentStr, ok := content.(string); ok { + // 移除所有HTML标签 + re := regexp.MustCompile(`<[^>]+>`) + cleanContent := re.ReplaceAllString(contentStr, "") + // 替换

等换行标签为换行符 + cleanContent = regexp.MustCompile(`(?i)`).ReplaceAllString(cleanContent, "\n") + // 移除多余的空行 + cleanContent = regexp.MustCompile(`\n{3,}`).ReplaceAllString(cleanContent, "\n\n") + // 去除首尾空白 + cleanContent = strings.TrimSpace(cleanContent) + clean["content"] = cleanContent + list = append(list, clean) + } } } @@ -4615,16 +4736,28 @@ func (s *ToolboxService) processChengYu( // 返回数量(可选) if num, ok := params["num"].(float64); ok && num > 0 && num <= 50 { reqParams["num"] = int(num) + } else if numStr, ok := params["num"].(string); ok && numStr != "" { + if numInt, err := strconv.Atoi(numStr); err == nil && numInt > 0 && numInt <= 50 { + reqParams["num"] = numInt + } } // 搜索模式(可选) if mode, ok := params["mode"].(float64); ok && mode >= 0 && mode <= 2 { reqParams["mode"] = int(mode) + } else if modeStr, ok := params["mode"].(string); ok && modeStr != "" { + if modeInt, err := strconv.Atoi(modeStr); err == nil && modeInt >= 0 && modeInt <= 2 { + reqParams["mode"] = modeInt + } } // 页码(可选) if page, ok := params["page"].(float64); ok && page > 0 { reqParams["page"] = int(page) + } else if pageStr, ok := params["page"].(string); ok && pageStr != "" { + if pageInt, err := strconv.Atoi(pageStr); err == nil && pageInt > 0 { + reqParams["page"] = pageInt + } } // 调用天行 API @@ -4691,12 +4824,12 @@ func (s *ToolboxService) processNaoWan( reqParams := map[string]interface{}{} - // 返回数量(必填) - num, ok := params["num"].(float64) - if !ok || num <= 0 { - num = 5 // 给一个安全默认值 + // 返回数量(可选,默认5条) + if num, ok := params["num"].(float64); ok && num > 0 { + reqParams["num"] = int(num) + } else { + reqParams["num"] = 5 // 默认返回5条 } - reqParams["num"] = int(num) // 调用天行 API resp, err := s.tianxingjuheClient.Get("naowan/index", reqParams) @@ -4732,8 +4865,8 @@ func (s *ToolboxService) processNaoWan( if quest, ok := raw["quest"]; ok { clean["quest"] = quest } - if answer, ok := raw["result"]; ok { - clean["answer"] = answer + if result, ok := raw["result"]; ok { + clean["result"] = result } list = append(list, clean) @@ -4743,6 +4876,7 @@ func (s *ToolboxService) processNaoWan( "count": len(list), "list": list, }, nil + } // processDOB 生日性格查询 @@ -4753,19 +4887,49 @@ func (s *ToolboxService) processDOB( reqParams := map[string]interface{}{} - // 月份(必填) - m, ok := params["m"].(float64) - if !ok || m < 1 || m > 12 { + // 月份(必填)- 支持字符串和数字类型 + var m int + switch v := params["m"].(type) { + case float64: + if v < 1 || v > 12 { + return nil, xerr.NewErrMsg("生日性格查询缺少必填参数:m(月份 1-12)") + } + m = int(v) + case string: + if v == "" { + return nil, xerr.NewErrMsg("生日性格查询缺少必填参数:m(月份 1-12)") + } + mInt, err := strconv.Atoi(v) + if err != nil || mInt < 1 || mInt > 12 { + return nil, xerr.NewErrMsg("生日性格查询参数 m 必须是 1-12 的数字") + } + m = mInt + default: return nil, xerr.NewErrMsg("生日性格查询缺少必填参数:m(月份 1-12)") } - reqParams["m"] = int(m) + reqParams["m"] = m - // 日期(必填) - d, ok := params["d"].(float64) - if !ok || d < 1 || d > 31 { + // 日期(必填)- 支持字符串和数字类型 + var d int + switch v := params["d"].(type) { + case float64: + if v < 1 || v > 31 { + return nil, xerr.NewErrMsg("生日性格查询缺少必填参数:d(日期 1-31)") + } + d = int(v) + case string: + if v == "" { + return nil, xerr.NewErrMsg("生日性格查询缺少必填参数:d(日期 1-31)") + } + dInt, err := strconv.Atoi(v) + if err != nil || dInt < 1 || dInt > 31 { + return nil, xerr.NewErrMsg("生日性格查询参数 d 必须是 1-31 的数字") + } + d = dInt + default: return nil, xerr.NewErrMsg("生日性格查询缺少必填参数:d(日期 1-31)") } - reqParams["d"] = int(d) + reqParams["d"] = d // 调用天行 API resp, err := s.tianxingjuheClient.Get("dob/index", reqParams) @@ -4783,18 +4947,9 @@ func (s *ToolboxService) processDOB( 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}, + "title": result["title"], + "content": result["content"], }, nil } @@ -5178,12 +5333,25 @@ func (s *ToolboxService) processConstellation(ctx context.Context, params map[st } reqParams["astro"] = astro - // 可选:运势类型 (today/tomorrow/week/month) + // 可选:日期 (fortune参数转换为date) if fortune, ok := params["fortune"].(string); ok && fortune != "" { - reqParams["fortune"] = fortune + now := time.Now() + switch fortune { + case "tomorrow": + date := now.AddDate(0, 0, 1) + reqParams["date"] = date.Format("2006-01-02") + case "week": + // 本周返回当前日期 + reqParams["date"] = now.Format("2006-01-02") + case "month": + // 本月返回当前日期 + reqParams["date"] = now.Format("2006-01-02") + default: // today + reqParams["date"] = now.Format("2006-01-02") + } } - resp, err := s.tianxingjuheClient.Get("constellation/index", reqParams) + resp, err := s.tianxingjuheClient.Get("star/index", reqParams) if err != nil { return nil, xerr.NewErrMsg(fmt.Sprintf("请求星座运势API失败: %v", err)) } @@ -5197,28 +5365,54 @@ func (s *ToolboxService) processConstellation(ctx context.Context, params map[st 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, + // 解析 list + rawList, ok := resultMap["list"].([]interface{}) + if !ok { + return nil, xerr.NewErrMsg("星座运势列表数据异常") } - clean := map[string]interface{}{} - for k, v := range resultMap { - if allowed[k] { - clean[k] = v + // 将 list 转换为扁平化的 map + result := map[string]interface{}{ + "astro": astro, + } + + for _, item := range rawList { + raw, ok := item.(map[string]interface{}) + if !ok { + continue + } + + typeName, ok := raw["type"].(string) + content, hasContent := raw["content"].(string) + + if !ok || !hasContent { + continue + } + + // 将 type 映射到对应的字段名 + switch typeName { + case "综合指数": + result["summary"] = content + case "爱情指数": + result["love"] = content + case "工作指数": + result["work"] = content + case "财运指数": + result["money"] = content + case "健康指数": + result["health"] = content + case "幸运颜色": + result["color"] = content + case "幸运数字": + result["number"] = content + case "贵人星座": + result["qfriend"] = content + case "今日概述": + result["today_summary"] = content } } - return clean, nil + return result, nil } // processFanyi 在线翻译 @@ -5266,23 +5460,35 @@ func (s *ToolboxService) processFanyi(ctx context.Context, params map[string]int // processHoliday 节假日查询 func (s *ToolboxService) processHoliday(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) { + // 调试日志:打印接收到的所有参数 + logx.Infof("processHoliday 收到的参数: %+v", params) + reqParams := map[string]interface{}{} - date, ok := params["date"].(string) - if !ok || date == "" { - date = time.Now().Format("2006-01-02") - } - reqParams["date"] = date - - if typ, ok := params["type"].(float64); ok { - reqParams["type"] = int(typ) - } else { - reqParams["type"] = 0 + // 获取查询方式(1=按年,2=按月),默认按年 + var queryType int = 1 + if typ, ok := params["type"]; ok { + switch v := typ.(type) { + case float64: + queryType = int(v) + case string: + if val, err := strconv.Atoi(v); err == nil { + queryType = val + } + case json.Number: + if val, err := strconv.Atoi(v.String()); err == nil { + queryType = val + } + } } - if mode, ok := params["mode"].(float64); ok { - reqParams["mode"] = int(mode) - } + // 将type直接传给天行API + reqParams["type"] = queryType + + // 传当前日期 + reqParams["date"] = time.Now().Format("2006-01-02") + + logx.Infof("调用天行API: type=%d, date=%s", queryType, reqParams["date"]) resp, err := s.tianxingjuheClient.Get("jiejiari/index", reqParams) if err != nil { @@ -5627,9 +5833,29 @@ func (s *ToolboxService) processTarga( return nil, xerr.NewErrMsg("解析扩展名查询数据失败") } + // 清理HTML标签 + targa := "" + notes := "" + + if v, ok := result["targa"].(string); ok { + targa = v + } + + if v, ok := result["notes"].(string); ok { + // 移除HTML标签 + re := regexp.MustCompile(`<[^>]+>`) + notes = re.ReplaceAllString(v, "") + // 替换换行标签 + notes = regexp.MustCompile(`(?i)`).ReplaceAllString(notes, "\n") + // 去除多余空行 + notes = regexp.MustCompile(`\n{3,}`).ReplaceAllString(notes, "\n\n") + // 去除首尾空白 + notes = strings.TrimSpace(notes) + } + return map[string]interface{}{ - "targa": result["targa"], - "notes": result["notes"], + "targa": targa, + "notes": notes, }, nil } @@ -5815,15 +6041,20 @@ func (s *ToolboxService) processDuoYinZi( params map[string]interface{}, ) (map[string]interface{}, error) { + logx.Infof("processDuoYinZi 收到的参数: %+v", params) + reqParams := map[string]interface{}{} // 多音字(必填) word, ok := params["word"].(string) + logx.Infof("word 值: %v, 类型: %T, ok: %v", word, params["word"], ok) if !ok || word == "" { return nil, xerr.NewErrMsg("多音字查询缺少必填参数:word") } reqParams["word"] = word + logx.Infof("调用天行API: word=%s", word) + // 调用天行 API resp, err := s.tianxingjuheClient.Get("duoyinzi/index", reqParams) if err != nil { @@ -5831,7 +6062,7 @@ func (s *ToolboxService) processDuoYinZi( } if resp.Code != 200 { - return nil, xerr.NewErrMsg(fmt.Sprintf("多音字接口异常: %s", resp.Msg)) + return nil, xerr.NewErrMsg(fmt.Sprintf("该字无多音字.")) } // 解析 result @@ -5846,6 +6077,7 @@ func (s *ToolboxService) processDuoYinZi( return nil, xerr.NewErrMsg("多音字列表数据异常") } + // 将多音字信息合并成一个字符串 list := make([]string, 0, len(rawList)) for _, item := range rawList { if s, ok := item.(string); ok { @@ -5854,13 +6086,12 @@ func (s *ToolboxService) processDuoYinZi( } return map[string]interface{}{ - "hanzi": result["hanzi"], - "count": len(list), - "list": list, + "word": result["hanzi"], + "content": strings.Join(list, "\n"), }, nil } -// processDailyTel 全国常用电话查询 +// processDailyTel 全国常用电话 func (s *ToolboxService) processDailyTel( ctx context.Context, params map[string]interface{}, @@ -6015,9 +6246,24 @@ func (s *ToolboxService) processChaiZi( reqParams["word"] = word // 查询类型(可选,默认 0) - if typ, ok := params["type"].(float64); ok { - reqParams["type"] = int(typ) + var queryType int + if typ, ok := params["type"]; ok { + switch v := typ.(type) { + case float64: + queryType = int(v) + case string: + if val, err := strconv.Atoi(v); err == nil { + queryType = val + } + case json.Number: + if val, err := strconv.Atoi(v.String()); err == nil { + queryType = val + } + } } + reqParams["type"] = queryType + + logx.Infof("汉字拆解 - type: %d, word: %s", queryType, word) // 调用天行 API resp, err := s.tianxingjuheClient.Get("chaizi/index", reqParams) @@ -6056,9 +6302,20 @@ func (s *ToolboxService) processChaiZi( list = append(list, clean) } + // 将列表合并成文本格式 + contentList := make([]string, 0, len(list)) + for _, item := range list { + if texts, ok := item["texts"].(string); ok { + contentList = append(contentList, texts) + } + if words, ok := item["words"].(string); ok { + contentList = append(contentList, words) + } + } + return map[string]interface{}{ - "count": len(list), - "list": list, + "word": word, + "content": strings.Join(contentList, "\n"), }, nil } @@ -6214,10 +6471,24 @@ func (s *ToolboxService) processFlmj( return nil, xerr.NewErrMsg("解析分类名句数据失败") } + // 检查是否返回了列表(num > 1 时) + if list, hasList := result["list"]; hasList { + if listArray, isArray := list.([]interface{}); isArray && len(listArray) > 0 { + // 返回第一个结果 + if firstItem, ok := listArray[0].(map[string]interface{}); ok { + return map[string]interface{}{ + "source": firstItem["source"], + "content": firstItem["content"], + }, nil + } + } + } + return map[string]interface{}{ "source": result["source"], "content": result["content"], }, nil + } // processXhZd 新华字典查询 @@ -6265,16 +6536,43 @@ func (s *ToolboxService) processXhZd( return nil, xerr.NewErrMsg("解析新华字典数据失败") } + logx.Infof("新华字典API返回的result: %+v", result) + + // 解析 list + rawList, ok := result["list"].([]interface{}) + if !ok || len(rawList) == 0 { + return nil, xerr.NewErrMsg("新华字典列表数据异常") + } + + // 取第一个元素 + firstItem, ok := rawList[0].(map[string]interface{}) + if !ok { + return nil, xerr.NewErrMsg("新华字典数据格式异常") + } + + // 去除HTML标签 + htmlTagRegex := regexp.MustCompile(`<[^>]+>`) + + content := "" + if c, ok := firstItem["content"].(string); ok { + content = htmlTagRegex.ReplaceAllString(c, "") + } + + explain := "" + if e, ok := firstItem["explain"].(string); ok { + explain = htmlTagRegex.ReplaceAllString(e, "") + } + 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"], + "word": firstItem["hanzi"], + "py": firstItem["py"], + "pyyb": firstItem["pyyb"], + "wubi": firstItem["wubi"], + "bihua": firstItem["bihua"], + "bushou": firstItem["bushou"], + "bishun": firstItem["bishun"], + "content": content, + "explain": explain, }, nil } @@ -6868,11 +7166,14 @@ func (s *ToolboxService) processAqi( reqParams := map[string]interface{}{} // 地区名称(必填) - area, ok := params["area"].(string) - if !ok || area == "" { + city, ok := params["city"].(string) + logx.Infof("city 值: %v, 类型: %T, ok: %v", city, params["city"], ok) + if !ok || city == "" { return nil, xerr.NewErrMsg("空气质量指数查询缺少必填参数:area") } - reqParams["area"] = area + reqParams["area"] = city + + logx.Infof("调用天行API: area=%s", city) // 调用天行 API resp, err := s.tianxingjuheClient.Get("aqi/index", reqParams) @@ -6956,28 +7257,27 @@ func (s *ToolboxService) processPet( reqParams := map[string]interface{}{} - // 每页返回数量(必填) - num, ok := params["num"].(float64) - if !ok || num <= 0 { - return nil, xerr.NewErrMsg("宠物大全查询缺少必填参数:num") + // 每页返回数量(默认10,前端可选) + num := 10 + if v, ok := params["num"].(float64); ok && v > 0 { + num = int(v) } - reqParams["num"] = int(num) + reqParams["num"] = num - // 翻页(必填) - page, ok := params["page"].(float64) - if !ok || page <= 0 { - return nil, xerr.NewErrMsg("宠物大全查询缺少必填参数:page") + // 翻页(默认1,前端可选) + page := 1 + if v, ok := params["page"].(float64); ok && v > 0 { + page = int(v) } - reqParams["page"] = int(page) + reqParams["page"] = page - // 宠物名称(可选) + // 宠物名称(可选,用于搜索) if name, ok := params["name"].(string); ok && name != "" { reqParams["name"] = name } - // 宠物类型(可选) - if typ, ok := params["type"].(float64); ok && - typ >= 0 && typ <= 4 { + // 宠物类型(可选):0猫科、1犬类、2爬行类、3小宠物类、4水族类 + if typ, ok := params["type"].(float64); ok && typ >= 0 && typ <= 4 { reqParams["type"] = int(typ) } @@ -7002,6 +7302,24 @@ func (s *ToolboxService) processPet( return nil, xerr.NewErrMsg("宠物大全列表数据异常") } + // 字段白名单 + allowed := map[string]bool{ + "name": true, + "engName": true, + "pettype": true, + "nation": true, + "life": true, + "price": true, + "characters": true, + "feature": true, + "characterFeature": true, + "feedPoints": true, + "careKnowledge": true, + "easyOfDisease": true, + "coverURL": true, + "desc": true, + } + list := make([]map[string]interface{}, 0, len(rawList)) for _, item := range rawList { raw, ok := item.(map[string]interface{}) @@ -7010,50 +7328,20 @@ func (s *ToolboxService) processPet( } 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 + for k, v := range raw { + if allowed[k] { + clean[k] = v + } } - list = append(list, clean) + // 至少要有宠物名称 + if clean["name"] != nil { + list = append(list, clean) + } + } + + if len(list) == 0 { + return nil, xerr.NewErrMsg("未查询到相关宠物信息") } return map[string]interface{}{ @@ -7167,6 +7455,18 @@ func (s *ToolboxService) processDouYinHot( if v, ok := raw["hotindex"]; ok { clean["hotindex"] = v } + if v, ok := raw["label"]; ok { + // 转换label为可读文本 + labelMap := map[int]string{ + 0: "普通", + 1: "新", + 2: "荐", + 3: "热", + } + if labelInt, ok := v.(float64); ok { + clean["label_text"] = labelMap[int(labelInt)] + } + } list = append(list, clean) } @@ -7308,72 +7608,6 @@ func (s *ToolboxService) processAreaData( }, 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, @@ -7598,9 +7832,9 @@ func (s *ToolboxService) processQingShi( } return map[string]interface{}{ - "author": result["author"], - "source": result["source"], - "poem": result["content"], + "author": result["author"], + "source": result["source"], + "content": result["content"], }, nil } @@ -7657,49 +7891,6 @@ func (s *ToolboxService) processChengyuJieLong( }, 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, @@ -7947,7 +8138,7 @@ func (s *ToolboxService) ListTools() []ToolInfo { // ─── 第二批新增工具 ─── {Key: "timezone", Name: "时区查询", Desc: "查询全球各时区的当前时间"}, {Key: "duoyinzi", Name: "多音字查询", Desc: "查询汉字的多音字及不同读音"}, - {Key: "dailytel", Name: "每日一句", Desc: "每日一句励志语录"}, + {Key: "dailytel", Name: "全国常用电话", Desc: "汇总政府机关、银行、电商、快递、学校等全国常用机构对外联系电话。"}, {Key: "obdcode", Name: "OBD故障码", Desc: "查询汽车OBD故障码含义及解决方案"}, {Key: "chaizi", Name: "汉字拆解", Desc: "查询汉字的拆字结构及含义"}, {Key: "huayu", Name: "华语句子", Desc: "随机返回优美华语句子"}, @@ -7966,9 +8157,8 @@ func (s *ToolboxService) ListTools() []ToolInfo { {Key: "worldtime", Name: "世界时间", Desc: "查询全球主要城市当前时间"}, {Key: "wxhottopic", Name: "微信热点", Desc: "获取微信实时热点话题"}, {Key: "zmsc", Name: "字明说", Desc: "汉字谐音趣味解读"}, - {Key: "hsjz", 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: "智能对话内容生成"}, @@ -8010,3 +8200,57 @@ func (s *ToolboxService) RegisterTool(key string, processor ToolProcessor) error s.processors[key] = processor return nil } + +// stripHTMLTags 移除HTML标签,保留纯文本 +func stripHTMLTags(html string) string { + // 移除所有HTML标签 + re := regexp.MustCompile(`<[^>]*>`) + text := re.ReplaceAllString(html, "") + + // 移除多余的空白字符 + text = strings.TrimSpace(text) + text = regexp.MustCompile(`\s+`).ReplaceAllString(text, " ") + + return text +} + +// cleanHTMLContent 清理HTML内容,智能处理换行 +func cleanHTMLContent(html string) string { + if html == "" { + return html + } + + // 移除`).ReplaceAllString(html, "") + + // 移除`).ReplaceAllString(html, "") + + // 移除所有HTML标签 + re := regexp.MustCompile(`<[^>]*>`) + text := re.ReplaceAllString(html, "") + + // 替换HTML实体 + entityMap := map[string]string{ + "<": "<", + ">": ">", + "&": "&", + """: "\"", + " ": " ", + "'": "'", + "”": "”", + "“": "“", + "…": "…", + } + for entity, char := range entityMap { + text = strings.ReplaceAll(text, entity, char) + } + + // 规范化空白字符:将连续空白替换为单个空格 + text = regexp.MustCompile(`\s+`).ReplaceAllString(text, " ") + + // 清理首尾空白 + text = strings.TrimSpace(text) + + return text +} diff --git a/pkg/lzkit/crypto/ecb_test.go b/pkg/lzkit/crypto/ecb_test.go index f7cb2f2..6faa812 100644 --- a/pkg/lzkit/crypto/ecb_test.go +++ b/pkg/lzkit/crypto/ecb_test.go @@ -9,7 +9,7 @@ import ( func TestAesEcbMobileEncryption(t *testing.T) { // 测试手机号加密 - mobile := "18653052547" + mobile := "15029295957" key := []byte("ff83609b2b24fc73196aac3d3dfb874f") // 16字节AES-128密钥 keyStr := hex.EncodeToString(key) @@ -19,7 +19,8 @@ func TestAesEcbMobileEncryption(t *testing.T) { t.Fatalf("手机号加密失败: %v", err) } fmt.Printf("encrypted: %s\n", encrypted) - jmStr := "m9EEeW9ZBBJmi1hx1k1uIQ==" + + jmStr := "Am8/KpmBnsbXZoZOZq/oVQ==" // 测试解密 decrypted, err := DecryptMobile(jmStr, keyStr) if err != nil {