From 8bc6f5c2aee6d565cc5c6ac5baf22b0f84f4d0f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=AD=86?= <2685475410@qq.com> Date: Tue, 29 Sep 2026 23:54:27 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(search):=20=E6=8C=89=20tab?= =?UTF-8?q?=5Ftype=20=E8=BF=87=E6=BB=A4=E6=90=9C=E7=B4=A2=E7=BB=93?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 上游 search/tab/v/ 固定返回所有 tab 的 search_tabs,此前原样转发导致传 tab_type 仍返回全部结果 - 新增 filterSearchTabs 只保留匹配项,无匹配时保留原样兜底 - 过滤路径用 UseNumber 解析重组,避免 19 位 book_id 经 float64 丢精度;解析失败退回原样转发 - 新增测试:过滤正确性、无匹配兜底、大整数精度保持 --- internal/fqapi/search.go | 37 +++++++++++++++++++- internal/fqapi/search_test.go | 63 +++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 internal/fqapi/search_test.go diff --git a/internal/fqapi/search.go b/internal/fqapi/search.go index 3ff37a6..8c2a248 100644 --- a/internal/fqapi/search.go +++ b/internal/fqapi/search.go @@ -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 + } +} + diff --git a/internal/fqapi/search_test.go b/internal/fqapi/search_test.go new file mode 100644 index 0000000..21f7696 --- /dev/null +++ b/internal/fqapi/search_test.go @@ -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) + } +}