107 lines
3.3 KiB
Go
107 lines
3.3 KiB
Go
|
|
package outreach
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"fmt"
|
|||
|
|
"strings"
|
|||
|
|
|
|||
|
|
libkg "haixun-backend/internal/library/knowledge"
|
|||
|
|
libplacement "haixun-backend/internal/library/placement"
|
|||
|
|
brandentity "haixun-backend/internal/model/brand/domain/entity"
|
|||
|
|
branddomain "haixun-backend/internal/model/brand/domain/usecase"
|
|||
|
|
scanpostusecase "haixun-backend/internal/model/scan_post/domain/usecase"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func FindGraphNode(nodes []libkg.Node, nodeID string) *libkg.Node {
|
|||
|
|
nodeID = strings.TrimSpace(nodeID)
|
|||
|
|
if nodeID == "" {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
for i := range nodes {
|
|||
|
|
if nodes[i].ID == nodeID {
|
|||
|
|
return &nodes[i]
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func BuildPlacementReason(post *scanpostusecase.ScanPostSummary) string {
|
|||
|
|
if post == nil {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
parts := []string{}
|
|||
|
|
if post.Priority == "gold" {
|
|||
|
|
parts = append(parts, "雙軌命中(相關+近期)")
|
|||
|
|
} else if post.Priority == "recent" {
|
|||
|
|
parts = append(parts, "近期軌命中")
|
|||
|
|
}
|
|||
|
|
if post.SolvedByProduct {
|
|||
|
|
parts = append(parts, "產品可解此需求")
|
|||
|
|
}
|
|||
|
|
if post.ProductFitScore > 0 {
|
|||
|
|
parts = append(parts, fmt.Sprintf("產品匹配 %d", post.ProductFitScore))
|
|||
|
|
}
|
|||
|
|
return strings.Join(parts, ";")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func MergePlacementReason(base string, node *libkg.Node, post *scanpostusecase.ScanPostSummary) string {
|
|||
|
|
parts := []string{}
|
|||
|
|
if strings.TrimSpace(base) != "" {
|
|||
|
|
parts = append(parts, strings.TrimSpace(base))
|
|||
|
|
}
|
|||
|
|
if node != nil {
|
|||
|
|
if strings.TrimSpace(node.PlacementValue) != "" {
|
|||
|
|
parts = append(parts, "置入價值 "+strings.TrimSpace(node.PlacementValue))
|
|||
|
|
}
|
|||
|
|
if node.ProductFitScore > 0 && (post == nil || post.ProductFitScore == 0) {
|
|||
|
|
parts = append(parts, fmt.Sprintf("節點產品匹配 %d", node.ProductFitScore))
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return strings.Join(parts, ";")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func ProductBriefForBrand(brand *branddomain.BrandSummary, productID, searchTag, postText string) string {
|
|||
|
|
if brand == nil {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
if product := PickProductForOutreach(brand, productID, searchTag, postText); product != nil {
|
|||
|
|
merged := libplacement.BuildMergedProductContext(brand.DisplayName, product.ProductContext, product.Label)
|
|||
|
|
merged = libplacement.ApplyPlacementURL(merged, product.PlacementURL)
|
|||
|
|
if pb := libplacement.ProductBriefFromContext(merged); pb != "" {
|
|||
|
|
return pb
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if pb := libplacement.ProductBriefFromContext(brand.ProductContext); pb != "" {
|
|||
|
|
return pb
|
|||
|
|
}
|
|||
|
|
return strings.TrimSpace(brand.ProductBrief)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func PickProductForOutreach(brand *branddomain.BrandSummary, preferredProductID, searchTag, postText string) *branddomain.ProductSummary {
|
|||
|
|
if brand == nil || len(brand.Products) == 0 {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
entities := make([]brandentity.Product, len(brand.Products))
|
|||
|
|
for i := range brand.Products {
|
|||
|
|
entities[i] = brandentity.Product{
|
|||
|
|
ID: brand.Products[i].ID,
|
|||
|
|
Label: brand.Products[i].Label,
|
|||
|
|
ProductContext: brand.Products[i].ProductContext,
|
|||
|
|
MatchTags: append([]string(nil), brand.Products[i].MatchTags...),
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if picked := libplacement.RecommendProductForPost(entities, searchTag, postText, preferredProductID); picked != nil {
|
|||
|
|
for i := range brand.Products {
|
|||
|
|
if brand.Products[i].ID == picked.ID {
|
|||
|
|
return &brand.Products[i]
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if id := strings.TrimSpace(preferredProductID); id != "" {
|
|||
|
|
for i := range brand.Products {
|
|||
|
|
if brand.Products[i].ID == id {
|
|||
|
|
return &brand.Products[i]
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return &brand.Products[0]
|
|||
|
|
}
|