package domain import "strings" const ( SuggestUsageInclude = "include" SuggestUsageExclude = "exclude" // 建議數量上限:清單要能一眼看完並逐條決定,不是丟一大串讓人放棄。 MaxSuggestions = 20 DefaultSuggestions = 8 ) /* WatchTermSuggestion 是一則關鍵字建議。 Reason 是必填的:使用者要逐條決定採不採用,看不到「為什麼建議這個詞」就只能全採或全不採, 那這個功能就退化成一個猜測產生器。 */ type WatchTermSuggestion struct { Term string `json:"term"` Reason string `json:"reason"` Usage string `json:"usage"` } func NormalizeSuggestUsage(s string) string { if strings.EqualFold(strings.TrimSpace(s), SuggestUsageExclude) { return SuggestUsageExclude } return SuggestUsageInclude } /* CleanSuggestions 收掉空白與重複,丟掉沒有理由的項目,並套用數量上限。 沒有理由的項目直接丟:補一句「AI 建議」等於假裝有理由,比少一則更糟。 */ func CleanSuggestions(in []WatchTermSuggestion, limit int) []WatchTermSuggestion { if limit <= 0 || limit > MaxSuggestions { limit = MaxSuggestions } out := make([]WatchTermSuggestion, 0, len(in)) seen := map[string]bool{} for _, s := range in { term := strings.ToLower(strings.Join(strings.Fields(strings.ReplaceAll(s.Term, "\u3000", " ")), " ")) reason := strings.TrimSpace(s.Reason) if term == "" || reason == "" { continue } if len([]rune(term)) < MinTermLen || len([]rune(term)) > MaxTermLen { continue } if seen[term] { continue } seen[term] = true out = append(out, WatchTermSuggestion{Term: term, Reason: reason, Usage: NormalizeSuggestUsage(s.Usage)}) if len(out) >= limit { break } } return out }