62 lines
2.3 KiB
Markdown
62 lines
2.3 KiB
Markdown
|
|
# T525 — opportunity-model-repository
|
|||
|
|
|
|||
|
|
> Status: `done`
|
|||
|
|
> Milestone: `M2`
|
|||
|
|
> Kind: `feat`
|
|||
|
|
> Est. change: `~190 lines`
|
|||
|
|
|
|||
|
|
## Goal
|
|||
|
|
|
|||
|
|
完成後系統應:Opportunity domain 與 repository 就位,狀態機依 spec §3.2,同 owner 同 `external_id` 去重且可記錄多個觸發 term。
|
|||
|
|
|
|||
|
|
## Depends on
|
|||
|
|
|
|||
|
|
- T501
|
|||
|
|
|
|||
|
|
## Inputs
|
|||
|
|
|
|||
|
|
- Spec:`../spec.md` §3.2、§3.3、§8、§9.2(OP-06、SW-06)
|
|||
|
|
- 既有 module 模式:`apps/backend/internal/module/scout/**`
|
|||
|
|
|
|||
|
|
## Outputs
|
|||
|
|
|
|||
|
|
### 程式變更(預期路徑)
|
|||
|
|
|
|||
|
|
| 路徑 | 動作 | 說明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `apps/backend/internal/module/radar/domain/opportunity.go` | add | struct+status 轉移+`intent_band` 由 score 推導(80/50)+`reasons[]` 五維度驗證 |
|
|||
|
|
| `apps/backend/internal/module/radar/repository/opportunity_mongo.go` | add | `UpsertByExternalID`、`ListByOwner`(band/status/watch/日期 filter)、`CountToday`、`Get`、`UpdateStatus`、`SetOverride` |
|
|||
|
|
| `apps/backend/internal/module/radar/repository/opportunity_memory.go` | add | 測試用 |
|
|||
|
|
|
|||
|
|
### 行為變更
|
|||
|
|
|
|||
|
|
- `reasons[]` 不足五維度時 repository **拒絕寫入**(回明確錯誤)——OP-06。
|
|||
|
|
- `UpsertByExternalID`:命中既有則只把新的觸發 term 併入 `matched_terms[]`,不重複建立、不重跑判定。
|
|||
|
|
- `region_match` 為三態 `match|mismatch|unknown`;`status` 含 `judging|qualified|rejected|accepted|dismissed`。
|
|||
|
|
- `override` 記 `{from_band, to_band, from_status, to_status, actor_uid, at}`。
|
|||
|
|
|
|||
|
|
## Out of scope
|
|||
|
|
|
|||
|
|
- 判定演算法(T529)與寫入流程(T530)
|
|||
|
|
- HTTP logic(T545/T546/T547)
|
|||
|
|
|
|||
|
|
## Acceptance
|
|||
|
|
|
|||
|
|
- [x] unit test:缺一條 reason 被拒;同 `external_id` 兩次 upsert 只有一筆且 `matched_terms` 有兩個
|
|||
|
|
- [x] score 79→`mid`、80→`high`、49→`low`
|
|||
|
|
- [x] 指令:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd apps/backend && go test ./internal/module/radar/... -count=1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Notes
|
|||
|
|
|
|||
|
|
band 門檻 80/50 為 spec 鎖定值,**不可**在此 task 調整。
|
|||
|
|
|
|||
|
|
### 實作落點
|
|||
|
|
|
|||
|
|
- `domain/opportunity.go`:status 轉移、`BandFromScore`(80/50)、`ValidateReasons` 五維度、`UTCDayBounds`
|
|||
|
|
- `repository/opportunity_{memory,mongo}.go`:`UpsertByExternalID` 命中只併 `matched_terms`;`CountToday`/`ListOpportunities`/`SetOpportunityOverride`
|
|||
|
|
- 測試:`domain/opportunity_test.go`、`repository/opportunity_memory_test.go`
|