package placement import ( "fmt" "strings" "time" "haixun-backend/internal/library/websearch" ) type QueryDimension string const ( QueryRelevance QueryDimension = "relevance" QueryRecency QueryDimension = "recency" ) type TagQuery struct { Tag string Query string Dimension QueryDimension GraphNodeID string ProductFitScore int RecencyDays int // 0 = no after filter; 7 or 30 for recency track } func BuildRelevanceQuery(provider websearch.Provider, tag string) string { tag = strings.TrimSpace(tag) if tag == "" { return "" } if websearch.ParseProvider(string(provider)) == websearch.ProviderExa { return fmt.Sprintf("Threads 貼文 繁體中文 %s", tag) } return `site:threads.net "` + tag + `"` } func BuildRecencyQuery(provider websearch.Provider, tag string, maxAgeDays int) string { tag = strings.TrimSpace(tag) if tag == "" { return "" } if websearch.ParseProvider(string(provider)) == websearch.ProviderExa { return fmt.Sprintf("Threads 近期貼文 繁體中文 %s", tag) } after := FormatAfterDate(maxAgeDays, timeNow()) return `site:threads.net "` + tag + `" 請問 after:` + after } func PublishedAfterForRecency(provider websearch.Provider, maxAgeDays int) string { if maxAgeDays <= 0 || websearch.ParseProvider(string(provider)) != websearch.ProviderExa { return "" } return FormatPublishedAfterISO(maxAgeDays, timeNow()) } var timeNow = func() time.Time { return time.Now() } // SetTimeNowForTest overrides time source in tests. func SetTimeNowForTest(fn func() time.Time) { timeNow = fn }