package placement import ( "strings" branddomain "haixun-backend/internal/model/brand/domain/usecase" topicdomain "haixun-backend/internal/model/placement_topic/domain/usecase" ) // PlacementTopicContext bundles brand, product, and user-entered topic fields for prompts. type PlacementTopicContext struct { BrandDisplayName string TopicName string SeedQuery string Brief string Goals string TargetAudience string ProductContext string ProductBrief string ProductLabel string } func PlacementTopicContextFromTopic(topic *topicdomain.TopicSummary, brand *branddomain.BrandSummary, productBrief string) PlacementTopicContext { if topic == nil { return PlacementTopicContextFromBrand(brand, productBrief) } merged := brandSummaryForTopic(topic, brand) return PlacementTopicContextFromBrand(merged, productBrief) } func brandSummaryForTopic(topic *topicdomain.TopicSummary, brand *branddomain.BrandSummary) *branddomain.BrandSummary { if brand == nil { return nil } out := *brand out.TopicName = strings.TrimSpace(topic.TopicName) out.SeedQuery = strings.TrimSpace(topic.SeedQuery) out.Brief = strings.TrimSpace(topic.Brief) out.ProductID = strings.TrimSpace(topic.ProductID) out.ResearchMap = topic.ResearchMap return &out } func PlacementTopicContextFromBrand(brand *branddomain.BrandSummary, productBrief string) PlacementTopicContext { if brand == nil { return PlacementTopicContext{} } ctx := PlacementTopicContext{ BrandDisplayName: strings.TrimSpace(brand.DisplayName), TopicName: strings.TrimSpace(brand.TopicName), SeedQuery: strings.TrimSpace(brand.SeedQuery), Brief: strings.TrimSpace(brand.Brief), Goals: strings.TrimSpace(brand.Goals), TargetAudience: strings.TrimSpace(brand.TargetAudience), ProductContext: strings.TrimSpace(brand.ProductContext), ProductBrief: strings.TrimSpace(productBrief), ProductLabel: productLabelFromBrand(brand), } if ctx.ProductBrief == "" { ctx.ProductBrief = strings.TrimSpace(brand.ProductBrief) } return ctx } 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 { return strings.TrimSpace(item.Label) } } return "" } func (c PlacementTopicContext) ToResearchMapInput() ResearchMapInput { return ResearchMapInput{ BrandDisplayName: c.BrandDisplayName, TopicName: c.TopicName, SeedQuery: c.SeedQuery, Brief: c.Brief, Goals: c.Goals, TargetAudience: c.TargetAudience, ProductContext: c.ProductContext, ProductBrief: c.ProductBrief, ProductLabel: c.ProductLabel, } } func (c PlacementTopicContext) WritePromptBlock(b *strings.Builder) { writePromptSection(b, "品牌", c.BrandDisplayName) writePromptSection(b, "置入產品", c.ProductDisplayName()) writePromptSection(b, "主題名稱", c.TopicName) writePromptSection(b, "種子關鍵字", c.SeedQuery) writePromptSection(b, "主題目標", c.Brief) writePromptSection(b, "置入目標/備註", c.Goals) writePromptSection(b, "已知受眾描述", c.TargetAudience) productDetail := FormatProductContextForPrompt(c.ProductContext) if productDetail == "" && c.ProductBrief != "" { productDetail = c.ProductBrief } if productDetail != "" { b.WriteString("【產品詳情】\n") b.WriteString(productDetail) b.WriteString("\n") } } func (c PlacementTopicContext) ProductDisplayName() string { fields := ParseProductContext(c.ProductContext) if fields.Product != "" { return fields.Product } return c.ProductLabel } func writePromptSection(b *strings.Builder, label, value string) { value = strings.TrimSpace(value) if value == "" { return } b.WriteString("【") b.WriteString(label) b.WriteString("】") b.WriteString(value) b.WriteString("\n") } // PromptLines returns optional labeled lines for knowledge graph template vars. func (c PlacementTopicContext) PromptLines() map[string]string { productName := c.ProductDisplayName() return map[string]string{ "brand_line": optionalPromptLine("品牌", c.BrandDisplayName), "topic_line": optionalPromptLine("主題名稱", c.TopicName), "product_line": optionalPromptLine("置入產品", productName), "goals_line": optionalPromptLine("置入目標", c.Goals), } } func optionalPromptLine(label, value string) string { value = strings.TrimSpace(value) if value == "" { return "" } return label + ":" + value + "\n" }