# T754 — primary product override api

> Status: `done`  
> Milestone: `M5` · Phase C  
> Kind: `feat` · Est. change: `~160 lines`

## Goal

會員可將既有 ProductMatch 設為主推產品，並以 audit 鎖定人工選擇與重算 fit 分數。

## Depends on

- T744、T750、T753

## Inputs

- goctl primary-product route from T700
- ProductMatch repository guarded update
- `../spec.md` §3.3、§4.4、§5.3

## Outputs

| 路徑 | 動作 | 說明 |
|------|------|------|
| `apps/backend/internal/module/radar/usecase/primary_product.go` | add | owner/match/override validation、score recompute |
| `apps/backend/internal/logic/radar/set_primary_product_logic.go` | edit | JWT owner、request、response mapping |
| primary tests | add | eligible、weak reason、excluded reason、cross owner、future merge |

## Behavior

- Product 不在 match 中失敗；weak／excluded 無 reason 失敗。
- 寫 actor uid、時間、reason；後續自動 merge 不覆蓋。
- 已送出回覆／touch 不在此 task 回寫。

## Out of scope

- 回覆／CRM propagation（T770/T771）
- ProductMatch 分數人工修改

## Acceptance

- [x] spec #6、#13 API cases 通過。

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

Implementation result (2026-08-10): the generated primary-product route now
validates owner and ProductMatch membership, requires an audit reason, stores
actor／UTC nanoseconds, and recomputes only the existing fit dimension. Weak or
excluded matches need an explicit reason; later automatic merges cannot replace
the human override.
