🐛 fix(search): 按 tab_type 过滤搜索结果

- 上游 search/tab/v/ 固定返回所有 tab 的 search_tabs,此前原样转发导致传 tab_type 仍返回全部结果
- 新增 filterSearchTabs 只保留匹配项,无匹配时保留原样兜底
- 过滤路径用 UseNumber 解析重组,避免 19 位 book_id 经 float64 丢精度;解析失败退回原样转发
- 新增测试:过滤正确性、无匹配兜底、大整数精度保持
This commit is contained in:
chenxin committed 2026-09-29 23:54:27 +08:00
1 parent bb0cc39950
commit 8bc6f5c2ae
2 files changed
+99 -1

No files matched your search

+36 -1
View File
@@ -1,6 +1,8 @@
package fqapi
import (
"bytes"
"encoding/json"
"fanqie/internal/common"
"fmt"
"net/url"
@@ -57,9 +59,42 @@ func fetchBookSearch(key string, offset int, tabType int) (string, error) {
}
if code, ok := respJSON["code"].(float64); ok && code == 0 {
return resp.Text, nil
// 用 UseNumber 重新解析后按 tab_type 过滤:
// 上游 search/tab/v/ 固定返回所有 tab 的 search_tabs;
// 大整数 ID 经 float64 重组会丢精度,必须 UseNumber
dec := json.NewDecoder(bytes.NewReader(resp.Content))
dec.UseNumber()
var numJSON map[string]interface{}
if err := dec.Decode(&numJSON); err != nil {
return resp.Text, nil // 解析失败时退回原样转发
}
filterSearchTabs(numJSON, tabType)
return ToJSON(numJSON)
}
return BuildErrorResponseWithCode(1, "搜索失败"), nil
}
// filterSearchTabs 按 tab_type 只保留 search_tabs 中匹配的项;无匹配时保留原样
func filterSearchTabs(respJSON map[string]interface{}, tabType int) {
tabs, ok := respJSON["search_tabs"].([]interface{})
if !ok {
return
}
filtered := make([]interface{}, 0, 1)
for _, t := range tabs {
m, ok := t.(map[string]interface{})
if !ok {
continue
}
if tt, ok := m["tab_type"].(json.Number); ok {
if v, err := tt.Int64(); err == nil && int(v) == tabType {
filtered = append(filtered, m)
}
}
}
if len(filtered) > 0 {
respJSON["search_tabs"] = filtered
}
}
+63
View File
@@ -0,0 +1,63 @@
package fqapi
import (
"bytes"
"encoding/json"
"strings"
"testing"
)
// TestFilterSearchTabs 上游返回全部 tab 时,应只保留请求的 tab_type;无匹配时保留原样
func TestFilterSearchTabs(t *testing.T) {
raw := `{"code":0,"search_tabs":[
{"tab_type":1,"title":"综合"},
{"tab_type":3,"title":"书籍","book_id":7123456789012345678}
]}`
decode := func(s string) map[string]interface{} {
dec := json.NewDecoder(strings.NewReader(s))
dec.UseNumber()
var m map[string]interface{}
if err := dec.Decode(&m); err != nil {
t.Fatal(err)
}
return m
}
m := decode(raw)
filterSearchTabs(m, 3)
tabs := m["search_tabs"].([]interface{})
if len(tabs) != 1 {
t.Fatalf("tab_type=3 应只剩 1 个 tab, got %d", len(tabs))
}
// 大整数 ID 不得丢精度
want := "7123456789012345678"
got := tabs[0].(map[string]interface{})["book_id"].(json.Number).String()
if got != want {
t.Errorf("book_id 精度丢失: got %s want %s", got, want)
}
// 无匹配 tab 时保留原样
m = decode(raw)
filterSearchTabs(m, 99)
if len(m["search_tabs"].([]interface{})) != 2 {
t.Error("无匹配时应保留原始 search_tabs")
}
}
// TestToJSONPreservesNumberID 重组后大整数 ID 字符串不变
func TestToJSONPreservesNumberID(t *testing.T) {
dec := json.NewDecoder(bytes.NewReader([]byte(`{"id":7123456789012345678}`)))
dec.UseNumber()
var m map[string]interface{}
if err := dec.Decode(&m); err != nil {
t.Fatal(err)
}
out, err := ToJSON(m)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(out, "7123456789012345678") {
t.Errorf("重组丢失精度: %s", out)
}
}