thread-master/old/backend/internal/library/placement/product_match.go

116 lines
3.0 KiB
Go

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
}