package placement import ( "encoding/json" "strings" ) type CtaType string const ( CtaNone CtaType = "none" CtaLink CtaType = "link" CtaDM CtaType = "dm" CtaFollow CtaType = "follow" ) type ProductContextFields struct { Brand string `json:"brand"` Product string `json:"product"` Features string `json:"features"` PlacementTone string `json:"placementTone,omitempty"` CtaType CtaType `json:"ctaType,omitempty"` CtaUrl string `json:"ctaUrl,omitempty"` } func ParseProductContext(raw string) ProductContextFields { raw = strings.TrimSpace(raw) if raw == "" { return ProductContextFields{CtaType: CtaNone} } var parsed ProductContextFields if err := json.Unmarshal([]byte(raw), &parsed); err != nil { return ProductContextFields{CtaType: CtaNone, Features: raw} } parsed.Brand = strings.TrimSpace(parsed.Brand) parsed.Product = strings.TrimSpace(parsed.Product) parsed.Features = strings.TrimSpace(parsed.Features) parsed.PlacementTone = strings.TrimSpace(parsed.PlacementTone) parsed.CtaUrl = strings.TrimSpace(parsed.CtaUrl) if parsed.CtaType == "" { parsed.CtaType = CtaNone } return parsed } func SerializeProductContext(fields ProductContextFields) string { brand := strings.TrimSpace(fields.Brand) product := strings.TrimSpace(fields.Product) features := strings.TrimSpace(fields.Features) tone := strings.TrimSpace(fields.PlacementTone) ctaType := fields.CtaType if ctaType == "" { ctaType = CtaNone } ctaUrl := strings.TrimSpace(fields.CtaUrl) if brand == "" && product == "" && features == "" && tone == "" && ctaType == CtaNone && ctaUrl == "" { return "" } payload, _ := json.Marshal(ProductContextFields{ Brand: brand, Product: product, Features: features, PlacementTone: tone, CtaType: ctaType, CtaUrl: ctaUrl, }) return string(payload) } func FormatProductContextForPrompt(raw string) string { fields := ParseProductContext(raw) lines := []string{} if fields.Brand != "" { lines = append(lines, "品牌:"+fields.Brand) } if fields.Product != "" { lines = append(lines, "產品:"+fields.Product) } if fields.Features != "" { lines = append(lines, "特色/能幫上忙的地方:"+fields.Features) } if fields.PlacementTone != "" { lines = append(lines, "置入語氣偏好:"+fields.PlacementTone) } if fields.CtaType == CtaLink && fields.CtaUrl != "" { lines = append(lines, "留言 CTA 連結:"+fields.CtaUrl) } else if fields.CtaType == CtaDM { lines = append(lines, "留言 CTA:引導私訊") } else if fields.CtaType == CtaFollow { lines = append(lines, "留言 CTA:引導追蹤") } if len(lines) == 0 { return "" } return strings.Join(lines, "\n") } func ProductBriefFromContext(raw string) string { formatted := FormatProductContextForPrompt(raw) if formatted != "" { return formatted } return strings.TrimSpace(raw) } func HasProductContext(raw string) bool { fields := ParseProductContext(raw) return fields.Brand != "" || fields.Product != "" || fields.Features != "" }