From ce1261dcbf3a5cdd51ab942ba48ff8c861ba029a Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 30 Apr 2026 18:08:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AD=94=E5=A4=AB=E5=AD=90=E5=88=86=E7=B1=BB?= =?UTF-8?q?=E7=AD=9B=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- es/es_search.go | 177 +++++++++++++++++++++++++++++++++++------- main.go | 2 + model/request/book.go | 70 +++++++++-------- service/book.go | 78 +++++++++++++++++-- 4 files changed, 255 insertions(+), 72 deletions(-) diff --git a/es/es_search.go b/es/es_search.go index 35c2fa3..cc86d6e 100644 --- a/es/es_search.go +++ b/es/es_search.go @@ -1692,7 +1692,7 @@ func (svc *ESSearchService) BatchGetBookBaseInfoES(c *gin.Context) ([]ESBook, in continue } // 不作为查询条件的字段 - if key == "page" || key == "pageSize" || key == "per_page" || key == "saleSelect" || key == "picType" || key == "shopType" { + if key == "page" || key == "pageSize" || key == "per_page" || key == "saleSelect" || key == "picType" || key == "shopType" || key == "kongfz_include" { continue } @@ -1714,7 +1714,6 @@ func (svc *ESSearchService) BatchGetBookBaseInfoES(c *gin.Context) ([]ESBook, in key = nk } - //TODO ===== is_suit ===== if key == "is_suit" { fmt.Printf("[DEBUG] is_suit val=%q\n", val) if num, err := strconv.Atoi(val); err == nil { @@ -1729,7 +1728,6 @@ func (svc *ESSearchService) BatchGetBookBaseInfoES(c *gin.Context) ([]ESBook, in continue } - //TODO ===== is_return ===== if key == "is_return" { fmt.Printf("[DEBUG] is_return val=%q\n", val) if num, err := strconv.Atoi(val); err == nil { @@ -1744,7 +1742,6 @@ func (svc *ESSearchService) BatchGetBookBaseInfoES(c *gin.Context) ([]ESBook, in continue } - // ===== is_filter ===== // ===== is_filter(按 shopType 位匹配)===== if key == "is_filter" { fmt.Printf("[DEBUG] is_filter val=%q\n", val) @@ -1951,32 +1948,6 @@ func (svc *ESSearchService) BatchGetBookBaseInfoES(c *gin.Context) ([]ESBook, in }, }, } - //if minVal == 0 { - // cond = map[string]interface{}{ - // "bool": map[string]interface{}{ - // "should": []map[string]interface{}{ - // // 情况 1: total_sale 字段不存在或为 null - // { - // "bool": map[string]interface{}{ - // "must_not": []map[string]interface{}{ - // {"exists": map[string]interface{}{"field": "total_sale"}}, - // }, - // }, - // }, - // // 情况 2: total_sale 在范围内 - // { - // "range": map[string]interface{}{ - // "total_sale": map[string]interface{}{ - // "gte": minVal, - // "lte": maxVal, - // }, - // }, - // }, - // }, - // "minimum_should_match": 1, - // }, - // } - //} must = append(must, cond) fmt.Printf("[DEBUG] must += %v\n", cond) continue @@ -2025,6 +1996,70 @@ func (svc *ESSearchService) BatchGetBookBaseInfoES(c *gin.Context) ([]ESBook, in } } + // ===== kongfz_categories(孔夫子分类)===== + if key == "kongfz_categories" { + kongfzInclude := c.DefaultQuery("kongfz_include", "1") + + // 分割多个分类ID + categories := strings.Split(val, ",") + if len(categories) > 0 { + // 过滤空值 + var validCategories []string + for _, catID := range categories { + catID = strings.TrimSpace(catID) + if catID != "" { + validCategories = append(validCategories, catID) + } + } + + if len(validCategories) > 0 { + if kongfzInclude == "2" { + // 否:查询不包含这些分类的数据(must_not) + var mustNotQueries []map[string]interface{} + for _, catID := range validCategories { + mustNotQueries = append(mustNotQueries, map[string]interface{}{ + "prefix": map[string]interface{}{ + "cat_id.kong_fu_zi_cat_id": catID, + }, + }) + } + + if len(mustNotQueries) > 0 { + cond := map[string]interface{}{ + "bool": map[string]interface{}{ + "must_not": mustNotQueries, + }, + } + must = append(must, cond) + fmt.Printf("[DEBUG] must += %v (kongfz exclude: %d categories)\n", cond, len(mustNotQueries)) + } + } else { + // 是:查询包含这些分类的数据(should + minimum_should_match=1) + var shouldQueries []map[string]interface{} + for _, catID := range validCategories { + shouldQueries = append(shouldQueries, map[string]interface{}{ + "prefix": map[string]interface{}{ + "cat_id.kong_fu_zi_cat_id": catID, + }, + }) + } + + if len(shouldQueries) > 0 { + cond := map[string]interface{}{ + "bool": map[string]interface{}{ + "should": shouldQueries, + "minimum_should_match": 1, + }, + } + must = append(must, cond) + fmt.Printf("[DEBUG] must += %v (kongfz include: %d categories)\n", cond, len(shouldQueries)) + } + } + } + } + continue + } + // ===== 数值范围 ===== if key == "sell_counts" || strings.HasPrefix(key, "day_sale_") || @@ -2454,6 +2489,88 @@ func (svc *ESSearchService) SearchBookByISBNHandler(c *gin.Context) { }) } +func (svc *ESSearchService) SearchByISBNByXyCallBack(c *gin.Context) { + + isbn := c.Query("isbn") + if isbn == "" { + log.Printf("[SearchBookByISBNHandler] 缺少 isbn 参数") + c.JSON(400, gin.H{"error": "缺少 isbn 参数"}) + return + } + + log.Printf("[SearchBookByISBNHandler] 查询 ISBN: %s", isbn) + + ctx := context.Background() + endpoint := c.FullPath() + + // ES 查询(使用监控) + query := map[string]interface{}{ + "query": map[string]interface{}{ + "term": map[string]interface{}{ + "isbn": isbn, + }, + }, + "_source": true, + } + + body, err := json.Marshal(query) + if err != nil { + log.Printf("[SearchBookByISBNHandler] 构建查询 JSON 失败:%v", err) + c.JSON(500, gin.H{"error": "构建查询失败"}) + return + } + + req := esapi.SearchRequest{ + Index: []string{ESIndex}, + Body: bytes.NewReader(body), + TrackTotalHits: true, + Pretty: true, + } + + // 创建监控客户端并执行查询 + monitoredES := monitor.NewMonitoredESClient(svc.ES.Client, endpoint) + resp, duration, err := monitoredES.Search(ctx, &req) + + log.Printf("[SearchBookByISBNHandler] ES 查询耗时:%dms", duration.Milliseconds()) + + if err != nil { + log.Printf("[SearchBookByISBNHandler] ES 查询失败:%v", err) + c.JSON(500, gin.H{"error": "ES 查询失败:" + err.Error()}) + return + } + defer resp.Body.Close() + + if resp.IsError() { + log.Printf("[SearchBookByISBNHandler] ES 返回错误:%s", resp.String()) + c.JSON(500, gin.H{"error": "ES 返回错误:" + resp.String()}) + return + } + + var parsed esHitsWrapper + if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil { + log.Printf("[SearchBookByISBNHandler] 解析 ES 响应失败:%v", err) + c.JSON(500, gin.H{"error": "解析响应失败"}) + return + } + + var result *ESBook + if len(parsed.Hits.Hits) > 0 { + result = &parsed.Hits.Hits[0].Source + } + + if result == nil { + c.JSON(200, gin.H{ + "data": nil, + }) + return + } + + responseData := result.ConvertToResponse() + c.JSON(200, gin.H{ + "data": responseData, + }) +} + func (svc *ESSearchService) SearchBookByISBNHandlerToPsi(c *gin.Context) { isbn := c.Query("isbn") if isbn == "" { diff --git a/main.go b/main.go index dafe024..5fbda6a 100644 --- a/main.go +++ b/main.go @@ -311,6 +311,8 @@ func main() { r.GET("/api/es/searchByISBNLike", esService.SearchBooksHandler) //监控 // ISBN 精确搜索 r.GET("/api/es/searchByISBN", esService.SearchBookByISBNHandler) //监控 + // ISBN 精确搜索 为咸鱼商品下架回调 + r.GET("/api/es/searchByISBNByXyCallBack", esService.SearchByISBNByXyCallBack) //监控 // ISBN 精确搜索 为psi提供 r.GET("/api/es/searchByISBNtoPsi", esService.SearchBookByISBNHandlerToPsi) //监控 // 书名搜索 diff --git a/model/request/book.go b/model/request/book.go index e2bc92f..a72a56d 100644 --- a/model/request/book.go +++ b/model/request/book.go @@ -2,40 +2,42 @@ package request // BookSearchRequest ES 搜索图书请求参数 type BookSearchRequest struct { - Page int `form:"page"` // 页码 - PageSize int `form:"pageSize"` // 每页数量 - PerPage int `form:"per_page"` // 每页数量 (兼容字段) - SaleSelect string `form:"saleSelect"` // 销量筛选类型 - PicType string `form:"picType"` // 图片类型 - ShopType string `form:"shopType"` // 店铺类型 - BookName string `form:"book_name"` // 书名 - BookPic string `form:"book_pic"` // 图片筛选 - ISBN string `form:"isbn"` // ISBN - Author string `form:"author"` // 作者 - Category string `form:"category"` // 分类 - CategoryType string `form:"categoryType"` // ISBN分类类型 - Publisher string `form:"publisher"` // 出版社 - PublicationTime string `form:"publication_time"` // 出版时间 - BindingLayout string `form:"binding_layout"` // 装帧 - FixPrice string `form:"fix_price"` // 定价 - IsSuit string `form:"isSuit"` // 是否套装 - IsReturn string `form:"is_return"` // 是否驳回 - IsFilter string `form:"is_filter"` // 过滤字段 - BuyCounts string `form:"buy_counts"` // 购买次数 - SellCounts string `form:"sell_counts"` // 在售数量 - DaySale7 string `form:"day_sale_7"` // 7 天销量 - DaySale15 string `form:"day_sale_15"` // 15 天销量 - DaySale30 string `form:"day_sale_30"` // 30 天销量 - DaySale60 string `form:"day_sale_60"` // 60 天销量 - DaySale90 string `form:"day_sale_90"` // 90 天销量 - DaySale180 string `form:"day_sale_180"` // 180 天销量 - DaySale365 string `form:"day_sale_365"` // 365 天销量 - ThisYearSale string `form:"this_year_sale"` // 今年销量 - LastYearSale string `form:"last_year_sale"` // 去年销量 - TotalSaleRange string `form:"totalSale_range"` // 总销量范围 - ID string `form:"id"` // ID - PageCount string `form:"page_count"` // 页数 - WordCount string `form:"word_count"` // 字数 + Page int `form:"page"` // 页码 + PageSize int `form:"pageSize"` // 每页数量 + PerPage int `form:"per_page"` // 每页数量 (兼容字段) + SaleSelect string `form:"saleSelect"` // 销量筛选类型 + PicType string `form:"picType"` // 图片类型 + ShopType string `form:"shopType"` // 店铺类型 + BookName string `form:"book_name"` // 书名 + BookPic string `form:"book_pic"` // 图片筛选 + ISBN string `form:"isbn"` // ISBN + Author string `form:"author"` // 作者 + Category string `form:"category"` // 分类 + CategoryType string `form:"categoryType"` // ISBN分类类型 + Publisher string `form:"publisher"` // 出版社 + PublicationTime string `form:"publication_time"` // 出版时间 + BindingLayout string `form:"binding_layout"` // 装帧 + FixPrice string `form:"fix_price"` // 定价 + IsSuit string `form:"isSuit"` // 是否套装 + IsReturn string `form:"is_return"` // 是否驳回 + IsFilter string `form:"is_filter"` // 过滤字段 + BuyCounts string `form:"buy_counts"` // 购买次数 + SellCounts string `form:"sell_counts"` // 在售数量 + DaySale7 string `form:"day_sale_7"` // 7 天销量 + DaySale15 string `form:"day_sale_15"` // 15 天销量 + DaySale30 string `form:"day_sale_30"` // 30 天销量 + DaySale60 string `form:"day_sale_60"` // 60 天销量 + DaySale90 string `form:"day_sale_90"` // 90 天销量 + DaySale180 string `form:"day_sale_180"` // 180 天销量 + DaySale365 string `form:"day_sale_365"` // 365 天销量 + ThisYearSale string `form:"this_year_sale"` // 今年销量 + LastYearSale string `form:"last_year_sale"` // 去年销量 + TotalSaleRange string `form:"totalSale_range"` // 总销量范围 + ID string `form:"id"` // ID + PageCount string `form:"page_count"` // 页数 + WordCount string `form:"word_count"` // 字数 + KongfzCategories string `form:"kongfz_categories"` // 孔夫子分类列表,多个用逗号分隔 + KongfzInclude int8 `form:"kongfz_include"` // 是否涵盖:1-是,2-否 } // BookUpdateRequest 更新图书请求参数 diff --git a/service/book.go b/service/book.go index a0c0391..686cb95 100644 --- a/service/book.go +++ b/service/book.go @@ -122,6 +122,7 @@ func (svc *BookService) SearchBookBaseInfo(request *request.BookSearchRequest) ( svc.buildIsFilterCondition(queryBuilder, request.IsFilter, request.ShopType) svc.buildCategoryTypeCondition(queryBuilder, request.CategoryType) svc.buildCategoryCondition(queryBuilder, request.Category) + svc.buildKongfzCategoryCondition(queryBuilder, request.KongfzCategories, request.KongfzInclude) svc.buildBookPicCondition(queryBuilder, request.BookPic, request.PicType) svc.buildBuyCountsCondition(queryBuilder, request.BuyCounts, saleField) svc.buildTotalSaleRangeCondition(queryBuilder, request.TotalSaleRange) @@ -298,14 +299,10 @@ func (svc *BookService) UpdateBookFieldsByISBN(request *request.BookUpdateReques if isOne(isSuitValue) { params["is_filter"] = "100100" scriptParts = append(scriptParts, fmt.Sprintf("ctx._source.is_filter = params.is_filter;")) - log.Printf("[UpdateBookFieldsByISBN] 检测到 is_suit=1,自动设置 is_filter=100100 | ISBN=%s | 原始类型:%T", - request.ISBN, isSuitValue) } else { // is_suit 不为 1 时,清空 is_filter params["is_filter"] = "000000" scriptParts = append(scriptParts, fmt.Sprintf("ctx._source.is_filter = params.is_filter;")) - log.Printf("[UpdateBookFieldsByISBN] 检测到 is_suit!=1,清空 is_filter | ISBN=%s | is_suit 值:%v", - request.ISBN, isSuitValue) } } else { // 未传递 is_suit,自动检测并设置 @@ -317,17 +314,15 @@ func (svc *BookService) UpdateBookFieldsByISBN(request *request.BookUpdateReques if isSuitValue == 1 { params["is_filter"] = "100100" scriptParts = append(scriptParts, fmt.Sprintf("ctx._source.is_filter = params.is_filter;")) - log.Printf("[UpdateBookFieldsByISBN] 自动检测到 is_suit=1,设置 is_filter=100100 | ISBN=%s", request.ISBN) } } - for field, value := range request.Data { if field == "is_suit" { continue } // 使用配置检查字段是否允许更新 if !fieldConfig.IsAllowUpdate(field) { - log.Printf("[UpdateBookFieldsByISBN] 字段 %s 不允许更新,已跳过", field) + fmt.Printf("[UpdateBookFieldsByISBN] 字段 %s 不允许更新,已跳过", field) continue } // 对 publication_time 字段做特殊处理 @@ -349,7 +344,6 @@ func (svc *BookService) UpdateBookFieldsByISBN(request *request.BookUpdateReques } pubTime += 5364000000 params[field] = strconv.FormatInt(pubTime, 10) - log.Printf("[UpdateBookFieldsByISBN] publication_time 偏移处理:%v -> %v | ISBN=%s", value, pubTime, request.ISBN) } else { params[field] = value } @@ -1514,6 +1508,74 @@ func (svc *BookService) buildDefaultPrefixConditions(builder *ESQueryBuilder, re } } +// buildKongfzCategoryCondition 构建孔夫子分类查询条件 +func (svc *BookService) buildKongfzCategoryCondition(builder *ESQueryBuilder, kongfzCategories string, kongfzInclude int8) { + if kongfzCategories == "" { + return + } + + log.Printf("[DEBUG] kongfz_categories=%s, kongfz_include=%s", kongfzCategories, kongfzInclude) + + // 分割多个分类ID + categories := strings.Split(kongfzCategories, ",") + if len(categories) == 0 { + return + } + + // 过滤空值 + var validCategories []string + for _, catID := range categories { + catID = strings.TrimSpace(catID) + if catID != "" { + validCategories = append(validCategories, catID) + } + } + + if len(validCategories) == 0 { + return + } + + // 根据 kongfzInclude 参数决定查询逻辑 + if kongfzInclude == 2 { + // 否:查询不包含这些分类的数据(must_not) + var mustNotQueries []map[string]interface{} + for _, catID := range validCategories { + mustNotQueries = append(mustNotQueries, map[string]interface{}{ + "prefix": map[string]interface{}{ + "cat_id.kong_fu_zi_cat_id": catID, + }, + }) + } + + if len(mustNotQueries) > 0 { + builder.AddBoolQuery("must_not", mustNotQueries) + log.Printf("[DEBUG] Added %d kongfz category exclude queries (must_not)", len(mustNotQueries)) + } + } else { + // 是:查询包含这些分类的数据(should + minimum_should_match=1) + var shouldQueries []map[string]interface{} + for _, catID := range validCategories { + shouldQueries = append(shouldQueries, map[string]interface{}{ + "prefix": map[string]interface{}{ + "cat_id.kong_fu_zi_cat_id": catID, + }, + }) + } + + if len(shouldQueries) > 0 { + // 将 should 条件包装成内层 bool 对象,作为 must 条件添加 + innerBoolQuery := map[string]interface{}{ + "bool": map[string]interface{}{ + "should": shouldQueries, + "minimum_should_match": 1, + }, + } + builder.AddBoolQuery("must", []map[string]interface{}{innerBoolQuery}) + log.Printf("[DEBUG] Added kongfz category include query as nested bool in must (should match at least 1)") + } + } +} + // NewESQueryBuilder 创建 ES 查询构建器 func NewESQueryBuilder() *ESQueryBuilder { return &ESQueryBuilder{