66 lines
3.0 KiB
Markdown
66 lines
3.0 KiB
Markdown
|
|
# 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
|
|||
|
|
|
|||
|
|
- [x] unit test:active↔paused 可往返;archived 後 resume 被拒;`ListActive` 不含 paused/archived
|
|||
|
|
- [x] 指令:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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` 留空不代入服務檔案的值。** 判定時才解析,否則之後改服務檔案,舊訂閱會繼續用舊地區而使用者不會知道。
|