# T585 — followup-model-repository-and-hooks

> Status: `done`  
> Milestone: `M5`  
> Kind: `feat`  
> Est. change: `~170 lines`

## Goal

完成後系統應：FollowUp domain／repository 就位，且在送出回覆或階段進入 `engaged`／`dm_sent` 時自動建立（`due_at = 最後接觸 + N 天`）。

## Depends on

- T550、T566

## Inputs

- Spec：`../spec.md` §3.5、§4.7.1、§8（`crm_followups`）

## Outputs

### 程式變更（預期路徑）

| 路徑 | 動作 | 說明 |
|------|------|------|
| `apps/backend/internal/module/crm/domain/followup.go` | add | 狀態 `scheduled|notified|done|snoozed|escalated`＋`notified_count` |
| `apps/backend/internal/module/crm/repository/followup_mongo.go` | add | Upsert、ListDue(now)、ListByOwner、UpdateStatus |
| `apps/backend/internal/module/crm/repository/followup_memory.go` | add | 測試用 |
| `apps/backend/internal/module/crm/usecase/followup.go` | add | 建立／重算 `due_at`；hook 於 stage 轉移與回覆送出 |

### 行為變更

- 每個 Contact 同時最多一筆活躍 FollowUp（`scheduled`／`notified`／`snoozed`）。
- 天數取 Contact `follow_up_days`（預設 3）。
- 階段推進到 `replied` 或更後 → FollowUp 轉 `done`。

## Out of scope

- 掃描 Job 與通知（T586）
- API（T587）

## Acceptance

- [x] unit test：送出回覆 → 建立 `scheduled`，`due_at` 為最後接觸 +3 天
- [x] 階段轉 `replied` → 自動 `done`
- [x] 指令：

```bash
cd apps/backend && go test ./internal/module/crm/... -count=1
```
