package placement import ( "strings" brandentity "haixun-backend/internal/model/brand/domain/entity" ) func ScoreProductForTag(searchTag string, matchTags []string) int { tag := strings.TrimSpace(strings.ToLower(searchTag)) if tag == "" || len(matchTags) == 0 { return 0 } best := 0 for _, raw := range matchTags { candidate := strings.TrimSpace(strings.ToLower(raw)) if candidate == "" { continue } switch { case tag == candidate: best = maxInt(best, 100) case strings.Contains(tag, candidate) || strings.Contains(candidate, tag): best = maxInt(best, 70) default: tagWords := strings.Fields(tag) candWords := strings.Fields(candidate) overlap := 0 for _, w := range tagWords { for _, c := range candWords { if strings.Contains(c, w) || strings.Contains(w, c) { overlap++ break } } } if overlap > 0 { best = maxInt(best, 40+overlap*10) } } } return best } // ScoreProductForPost ranks a SKU against the search tag and post body. func ScoreProductForPost(productLabel string, matchTags []string, searchTag, postText string) int { tagScore := ScoreProductForTag(searchTag, matchTags) if tagScore == 0 && strings.TrimSpace(productLabel) != "" { tagScore = ScoreProductForTag(searchTag, []string{productLabel}) } postScore := ScoreProductForTag(postText, matchTags) if postScore == 0 && strings.TrimSpace(productLabel) != "" { postScore = ScoreProductForTag(postText, []string{productLabel}) } for _, token := range categoryTokens(productLabel, matchTags) { if strings.Contains(strings.ToLower(postText), token) { postScore = maxInt(postScore, 78) } if strings.Contains(strings.ToLower(searchTag), token) { tagScore = maxInt(tagScore, 82) } } switch { case tagScore > 0 && postScore > 0: return (tagScore*2 + postScore*3) / 5 case postScore > 0: return postScore default: return tagScore } } // RecommendProductForPost picks the best SKU for a single post. func RecommendProductForPost(products []brandentity.Product, searchTag, postText, defaultProductID string) *brandentity.Product { if len(products) == 0 { return nil } bestScore := 0 var best *brandentity.Product for i := range products { score := ScoreProductForPost(products[i].Label, products[i].MatchTags, searchTag, postText) if score > bestScore { bestScore = score best = &products[i] } } if best != nil && bestScore >= 55 { return best } return RecommendProduct(products, searchTag, defaultProductID) } func categoryTokens(productLabel string, matchTags []string) []string { blob := strings.ToLower(strings.TrimSpace(productLabel + " " + strings.Join(matchTags, " "))) tokens := []string{ "洗衣精", "洗衣粉", "洗衣劑", "洗衣液", "沐浴乳", "沐浴露", "洗髮精", "洗髮乳", "洗碗精", "洗碗劑", } out := make([]string, 0, 4) for _, token := range tokens { if strings.Contains(blob, token) { out = append(out, token) } } return out } func maxInt(a, b int) int { if a > b { return a } return b }