# T565 — contact-model-repository-unique-key > Status: `done` > Milestone: `M4` > Kind: `feat` > Est. change: `~180 lines` ## Goal 完成後系統應:Contact domain 與 repository 就位,唯一鍵(`owner_uid`+`source_platform`+`author_handle`)保證同一人只有一筆。 ## Depends on - T501 ## Inputs - Spec:`../spec.md` §3.4、§4.6、§8、§9.4(CR-01、CR-02) ## Outputs ### 程式變更(預期路徑) | 路徑 | 動作 | 說明 | |------|------|------| | `apps/backend/internal/module/crm/domain/contact.go` | add | 七階段枚舉+`needs_follow_up`/`follow_up_days`+`opportunity_ids[]`/`merged_from[]` | | `apps/backend/internal/module/crm/repository/contact_mongo.go` | add | `FindOrCreate`、Get、List(stage/follow_up/band/last_touch 排序)、UpdateStage、SetFollowUp、AttachOpportunity | | `apps/backend/internal/module/crm/repository/contact_memory.go` | add | 測試用 | | `apps/backend/internal/module/crm/usecase/contact.go` | add | usecase 殼 | ### 行為變更 - `FindOrCreate` 以唯一鍵原子操作,併發下不產生兩筆。 - `stage` 初值 `new_found`;`needs_follow_up` 預設 `false`,與 stage **並存**不互斥。 - `last_touch_at` 隨接觸更新(由 T566/T568 寫入)。 ## Out of scope - Touch 時間軸(T566) - 階段轉移規則(T568) - API(T569 起) ## Acceptance - [x] unit test:同鍵兩次 `FindOrCreate` 只有一筆;不同平台同 handle 為兩筆 - [x] `needs_follow_up=true` 不影響 `stage` 值 - [x] 指令: ```bash cd apps/backend && go test ./internal/module/crm/... -count=1 ``` ## Notes 命名紅線:本 module 不得出現以 `lead` 指稱銷售線索(RG-05)。