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

90 lines
3.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package domain
import (
"fmt"
"strings"
)
// Sweep fetch path — reuses the dual-path split (api | crawler); no third path.
const (
SweepPathAPI = "api"
SweepPathCrawler = "crawler"
)
/*
RadarSweep 是一次每日巡(或手動觸發)的執行紀錄。
計數以累加更新為主Job 中途失敗時已寫入的進度保留,供續跑判斷
judged_external_ids 標記已判過的貼文,避免重跑)。
*/
type RadarSweep struct {
ID string `bson:"_id" json:"id"`
OwnerUID int64 `bson:"owner_uid" json:"owner_uid"`
WatchID string `bson:"watch_id" json:"watch_id"`
JobID string `bson:"job_id,omitempty" json:"job_id,omitempty"`
Path string `bson:"path" json:"path"`
HitCount int `bson:"hit_count" json:"hit_count"`
JudgedCount int `bson:"judged_count" json:"judged_count"`
CreatedCount int `bson:"created_count" json:"created_count"`
TruncatedCount int `bson:"truncated_count" json:"truncated_count"`
FailedReason string `bson:"failed_reason,omitempty" json:"failed_reason,omitempty"`
CreditsUsed int `bson:"credits_used" json:"credits_used"`
JudgedExternalIDs []string `bson:"judged_external_ids,omitempty" json:"judged_external_ids,omitempty"`
StartedAt int64 `bson:"started_at" json:"started_at"`
EndedAt int64 `bson:"ended_at,omitempty" json:"ended_at,omitempty"`
}
// SweepDelta is an incremental progress patch applied with $inc / $addToSet.
type SweepDelta struct {
HitCount int
JudgedCount int
CreatedCount int
TruncatedCount int
CreditsUsed int
JudgedExternalIDs []string
FailedReason *string // nil = leave unchanged; non-nil (incl. empty) = set
EndedAt int64 // 0 = leave unchanged
}
// SweepListFilter pages sweeps for an owner, optionally scoped to one watch.
type SweepListFilter struct {
WatchID string
Page int
PageSize int
}
func IsSweepPath(s string) bool {
switch s {
case SweepPathAPI, SweepPathCrawler:
return true
}
return false
}
func (s *RadarSweep) Normalize() error {
if s.OwnerUID <= 0 {
return fmt.Errorf("%w: owner_uid required", ErrValidation)
}
if strings.TrimSpace(s.WatchID) == "" {
return fmt.Errorf("%w: watch_id required", ErrValidation)
}
if s.Path == "" {
s.Path = SweepPathAPI
}
if !IsSweepPath(s.Path) {
return fmt.Errorf("%w: unknown sweep path %q", ErrValidation, s.Path)
}
// failed_reason must never hold tokens; strip obvious bearer-like blobs is out of scope —
// callers are responsible. We only reject empty path/owner.
if s.FailedReason != "" {
s.FailedReason = strings.TrimSpace(s.FailedReason)
}
s.JudgedExternalIDs = dedupeStrings(s.JudgedExternalIDs)
return nil
}
// MergeJudgedExternalIDs appends new IDs without duplicates (order preserved).
func MergeJudgedExternalIDs(existing, add []string) []string {
return dedupeStrings(append(append([]string{}, existing...), add...))
}