# T571 — contact-merge-unmerge-owner-guard

> Status: `done`  
> Milestone: `M4`  
> Kind: `feat`  
> Est. change: `~180 lines`

## Goal

完成後系統應：可手動合併／取消合併聯絡人，時間軸保留兩邊，跨 owner 一律拒絕。

## Depends on

- T565、T566

## Inputs

- Spec：`../spec.md` §4.6.3、§5.6、§9.4（CR-06、CR-07）

## Outputs

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

| 路徑 | 動作 | 說明 |
|------|------|------|
| `apps/backend/generate/api/crm.api` | edit | merge／unmerge req／resp |
| `apps/backend/internal/logic/crm/mergeContactLogic.go` | edit | 合併端點 |
| `apps/backend/internal/module/crm/usecase/merge.go` | add | 搬移 `opportunity_ids`／touches 歸屬、記 `merged_from[]`、被併方標記為已併 |

### 行為變更

- 合併方向：目標 Contact 保留，來源 Contact 標記為已併並不再出現在列表。
- 時間軸合併後**兩邊紀錄都看得到**，各筆保留原本的來源平台資訊。
- 跨平台**不自動合併**，只有手動這一條路。
- 跨 owner 合併回明確越權錯誤。
- `unmerge` 還原被併方與其原有 touches／商機歸屬。

## Out of scope

- 自動身分推測（spec §7 明確禁止）
- 前端合併 UI（併入 T575）

## Acceptance

- [x] CR-06：兩個不同平台 Contact 合併成功，時間軸含兩邊
- [x] CR-07：跨 owner 合併回越權錯誤
- [x] unmerge 後兩筆資料回到合併前狀態
- [x] 指令：

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