149 lines
3.7 KiB
Go
149 lines
3.7 KiB
Go
package knowledge
|
||
|
||
import (
|
||
"encoding/json"
|
||
"strings"
|
||
|
||
branddomain "haixun-backend/internal/model/brand/domain/usecase"
|
||
)
|
||
|
||
// PatrolTagInput grounds海巡 tag in研究地圖與品牌/產品輸入。
|
||
type PatrolTagInput struct {
|
||
BrandName string
|
||
ProductName string
|
||
ProductFeatures string
|
||
AudienceSummary string
|
||
MatchTags []string
|
||
Questions []string
|
||
Pillars []string
|
||
PatrolKeywords []string
|
||
}
|
||
|
||
type productContextFields struct {
|
||
Brand string `json:"brand"`
|
||
Product string `json:"product"`
|
||
Features string `json:"features"`
|
||
}
|
||
|
||
func parseProductContextFields(raw string) productContextFields {
|
||
raw = strings.TrimSpace(raw)
|
||
if raw == "" {
|
||
return productContextFields{}
|
||
}
|
||
var parsed productContextFields
|
||
if err := json.Unmarshal([]byte(raw), &parsed); err != nil {
|
||
return productContextFields{Features: raw}
|
||
}
|
||
parsed.Brand = strings.TrimSpace(parsed.Brand)
|
||
parsed.Product = strings.TrimSpace(parsed.Product)
|
||
parsed.Features = strings.TrimSpace(parsed.Features)
|
||
return parsed
|
||
}
|
||
|
||
func PatrolTagInputFromBrand(brand *branddomain.BrandSummary, productBrief string) PatrolTagInput {
|
||
if brand == nil {
|
||
return PatrolTagInput{}
|
||
}
|
||
fields := parseProductContextFields(brand.ProductContext)
|
||
productName := productLabelFromBrand(brand)
|
||
if productName == "" {
|
||
productName = fields.Product
|
||
}
|
||
brandName := strings.TrimSpace(brand.DisplayName)
|
||
if brandName == "" {
|
||
brandName = fields.Brand
|
||
}
|
||
features := fields.Features
|
||
if features == "" {
|
||
features = strings.TrimSpace(productBrief)
|
||
}
|
||
if features == "" {
|
||
features = strings.TrimSpace(brand.ProductBrief)
|
||
}
|
||
return PatrolTagInput{
|
||
BrandName: brandName,
|
||
ProductName: productName,
|
||
ProductFeatures: features,
|
||
AudienceSummary: strings.TrimSpace(brand.ResearchMap.AudienceSummary),
|
||
MatchTags: matchTagsFromBrand(brand),
|
||
Questions: append([]string{}, brand.ResearchMap.Questions...),
|
||
Pillars: append([]string{}, brand.ResearchMap.Pillars...),
|
||
PatrolKeywords: append([]string{}, brand.ResearchMap.PatrolKeywords...),
|
||
}
|
||
}
|
||
|
||
func productLabelFromBrand(brand *branddomain.BrandSummary) string {
|
||
if brand == nil {
|
||
return ""
|
||
}
|
||
productID := strings.TrimSpace(brand.ProductID)
|
||
if productID == "" {
|
||
return ""
|
||
}
|
||
for _, item := range brand.Products {
|
||
if item.ID == productID {
|
||
label := strings.TrimSpace(item.Label)
|
||
if label != "" {
|
||
return label
|
||
}
|
||
return parseProductContextFields(item.ProductContext).Product
|
||
}
|
||
}
|
||
return ""
|
||
}
|
||
|
||
func matchTagsFromBrand(brand *branddomain.BrandSummary) []string {
|
||
if brand == nil {
|
||
return nil
|
||
}
|
||
productID := strings.TrimSpace(brand.ProductID)
|
||
seen := map[string]struct{}{}
|
||
out := []string{}
|
||
add := func(tag string) {
|
||
tag = strings.TrimSpace(tag)
|
||
if tag == "" {
|
||
return
|
||
}
|
||
if _, ok := seen[tag]; ok {
|
||
return
|
||
}
|
||
seen[tag] = struct{}{}
|
||
out = append(out, tag)
|
||
}
|
||
for _, item := range brand.Products {
|
||
if productID != "" && item.ID != productID {
|
||
continue
|
||
}
|
||
for _, tag := range item.MatchTags {
|
||
add(tag)
|
||
}
|
||
if productID != "" {
|
||
break
|
||
}
|
||
}
|
||
return out
|
||
}
|
||
|
||
func (in PatrolTagInput) HasResearchMap() bool {
|
||
return len(in.Questions) > 0 || len(in.Pillars) > 0 || strings.TrimSpace(in.AudienceSummary) != ""
|
||
}
|
||
|
||
func (in PatrolTagInput) HasProductContext() bool {
|
||
return strings.TrimSpace(in.ProductName) != "" ||
|
||
strings.TrimSpace(in.ProductFeatures) != "" ||
|
||
len(in.MatchTags) > 0
|
||
}
|
||
|
||
func productCategoryHint(productName, features string) string {
|
||
combined := productName + " " + features
|
||
for _, hint := range []string{
|
||
"沐浴乳", "沐浴露", "洗髮精", "洗髮乳", "洗面乳", "洗面奶", "乳液", "精華",
|
||
"防曬", "卸妝", "潔面", "身體乳", "洗手乳", "清潔",
|
||
} {
|
||
if strings.Contains(combined, hint) {
|
||
return hint
|
||
}
|
||
}
|
||
return ""
|
||
}
|