thread-master/apps/backend/internal/module/radar/domain/suggest.go

97 lines
2.7 KiB
Go
Raw Normal View History

2026-08-03 05:52:02 +00:00
package domain
import "strings"
const (
SuggestUsageInclude = "include"
SuggestUsageExclude = "exclude"
2026-08-13 02:22:24 +00:00
SuggestBasisAudience = "audience"
SuggestBasisPain = "pain"
SuggestBasisContext = "context"
SuggestBasisTag = "tag"
SuggestBasisCapability = "capability"
SuggestBasisExclude = "exclude"
2026-08-03 05:52:02 +00:00
// 建議數量上限:清單要能一眼看完並逐條決定,不是丟一大串讓人放棄。
MaxSuggestions = 20
DefaultSuggestions = 8
)
2026-08-13 02:22:24 +00:00
func IsSuggestionBasisKind(kind string) bool {
switch strings.ToLower(strings.TrimSpace(kind)) {
case SuggestBasisAudience, SuggestBasisPain, SuggestBasisContext, SuggestBasisTag, SuggestBasisCapability, SuggestBasisExclude:
return true
default:
return false
}
}
2026-08-03 05:52:02 +00:00
/*
WatchTermSuggestion 是一則關鍵字建議
Reason 是必填的使用者要逐條決定採不採用看不到為什麼建議這個詞就只能全採或全不採
那這個功能就退化成一個猜測產生器
*/
type WatchTermSuggestion struct {
2026-08-13 02:22:24 +00:00
Term string `json:"term"`
Reason string `json:"reason"`
Usage string `json:"usage"`
BasisKind string `json:"basis_kind,omitempty"`
BasisText string `json:"basis_text,omitempty"`
2026-08-03 05:52:02 +00:00
}
func NormalizeSuggestUsage(s string) string {
if strings.EqualFold(strings.TrimSpace(s), SuggestUsageExclude) {
return SuggestUsageExclude
}
return SuggestUsageInclude
}
/*
CleanSuggestions 收掉空白與重複丟掉沒有理由的項目並套用數量上限
沒有理由的項目直接丟補一句AI 建議等於假裝有理由比少一則更糟
2026-08-09 07:57:35 +00:00
include 關鍵字必須通過 Threads 短詞規則IsThreadsSearchable不合規整條丟掉不截短
避免產出半截怪詞exclude 仍用較寬的長度界線訂閱排除詞可能較長
2026-08-03 05:52:02 +00:00
*/
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 {
2026-08-09 07:57:35 +00:00
term := NormalizeSearchTerm(s.Term)
2026-08-03 05:52:02 +00:00
reason := strings.TrimSpace(s.Reason)
if term == "" || reason == "" {
continue
}
2026-08-09 07:57:35 +00:00
usage := NormalizeSuggestUsage(s.Usage)
if usage == SuggestUsageInclude {
if !IsThreadsSearchable(term) {
continue
}
} else {
// exclude: keep broader length; still reject empty after normalize
n := len([]rune(term))
if n < MinTermLen || n > MaxTermLen {
continue
}
2026-08-03 05:52:02 +00:00
}
2026-08-09 07:57:35 +00:00
key := strings.ToLower(term)
if seen[key] {
2026-08-03 05:52:02 +00:00
continue
}
2026-08-09 07:57:35 +00:00
seen[key] = true
2026-08-13 02:22:24 +00:00
out = append(out, WatchTermSuggestion{
Term: term, Reason: reason, Usage: usage,
BasisKind: strings.TrimSpace(s.BasisKind), BasisText: strings.TrimSpace(s.BasisText),
})
2026-08-03 05:52:02 +00:00
if len(out) >= limit {
break
}
}
return out
}