3.0 KiB
3.0 KiB
T512 — radar-watch-model-repository
Status:
done(2026-07-31)
Milestone:M1
Kind:feat
Est. change:~180 lines
Goal
完成後系統應:RadarWatch domain 與 repository 就位,狀態機 active | paused | archived 只允許 spec §3.1 的轉移。
Depends on
- T501
Inputs
- Spec:
../spec.md§3.1、§4.2、§8 - 既有 module 模式:
apps/backend/internal/module/scout/**
Outputs
程式變更(預期路徑)
| 路徑 | 動作 | 說明 |
|---|---|---|
apps/backend/internal/module/radar/domain/watch.go |
add | struct+狀態轉移函式+terms[]/exclude_terms[]/regions[] 正規化 |
apps/backend/internal/module/radar/repository/watch_mongo.go |
add | CRUD、ListActive、CountActive(owner)、TouchLastSweptAt |
apps/backend/internal/module/radar/repository/watch_memory.go |
add | 測試用 |
apps/backend/internal/module/radar/usecase/watch.go |
add | Create/Update/Pause/Resume/Archive/List |
行為變更
archived為終態:不得再 resume,也不再被ListActive取到;既有商機與統計不刪。regions[]留空 = 沿用服務檔案地區(在判定時解析,不在此填值)。terms[]去空白、去重、大小寫正規化;空terms[]不得建立。
Out of scope
- HTTP logic 與配額閘(T513)
- 每日巡排程(T527)
Acceptance
- unit test:active↔paused 可往返;archived 後 resume 被拒;
ListActive不含 paused/archived - 指令:
cd apps/backend && go test ./internal/module/radar/... -count=1
Notes(2026-07-31)
| 路徑 | 動作 |
|---|---|
internal/module/radar/domain/watch.go |
add(struct、CanTransitionWatch、Transition、Normalize) |
internal/module/radar/repository/watch_{mongo,memory}.go |
add |
internal/module/radar/usecase/watch.go |
add(Create/Get/List/Update/Pause/Resume/Archive/MarkSwept) |
internal/module/radar/usecase/watch_test.go |
add(12 則,含狀態轉移矩陣) |
- term 一律轉小寫並收斂空白(含全形空白)。 Threads 搜尋不分大小寫,不正規化的話「Wedding」與「wedding」會被當兩個 term,M6 的關鍵字轉換率就會把同一個詞拆成兩列。
- 同一個詞同時出現在
terms與exclude_terms直接拒絕,那種訂閱永遠不會命中任何東西,讓它建起來只會讓人以為系統壞了。 archived不可逆。 封存後歷史商機與統計都還在,若允許復活,「這批統計是哪個訂閱在什麼期間跑的」就失去單一解釋。要再監控同一組詞請建新的。UpdateWatch用指標欄位表達「不動」。 nil = 不動、[]= 清空,這樣「只改地區」不會把關鍵字一起清掉。狀態不走 patch。regions留空不代入服務檔案的值。 判定時才解析,否則之後改服務檔案,舊訂閱會繼續用舊地區而使用者不會知道。