# Plan: 商機雷達＋成交 CRM

> Status: `approved`  
> Status note: 使用者 2026-07-31「請繼續拆解」（plan 通過，進 tasks）  
> Source: `docs/product/demand-radar/spec.md`（**approved** 2026-07-31 使用者「好」）  
> Requirements: `docs/product/demand-radar/requirements.md`（**approved** 2026-07-31）  
> Last updated: `2026-07-31`  
> 底座：`apps/backend` + `apps/web`（haixun-backend M0–M6、growth-loop M0–M6 均已交付）

## 1. 交付策略

1. **先讓「每天有名單」成立。** M1–M3 一路做到今日商機頁能開出「今日找到 N 筆／高中低分級」，這是整個產品的價值主張；在此之前的任何 CRM 功能都沒有意義。
2. **配額閘與每日巡同批交付。** 每日自動巡是常駐 AI 成本，M2 若沒有配額截斷就上線，成本會在驗證價值之前先失控（spec §4.9、決策 #11）。
3. **判定準確度先於功能廣度。** M2 完成後、M3 上線前必須用真實貼文樣本人工抽驗一輪；名單不可信是本 run 的單點失敗風險，寧可延後 M3 也不要先擴功能。
4. **加值 hook，不重寫主流程。** 既有海巡三模式、抓取雙路徑、Outbox、AccountHealth 送出閘、Stripe 付費流程一律沿用；新能力以新 group 與新 collection 並存，橋接只做單向「升級為商機」。
5. **前後端同里程碑可驗。** 每個 M 結束都要有前端可操作的畫面並對應 spec §9 的 ID，禁止後端全做完最後才接前端。
6. **不新增 usage meter。** 沿用四個既有 meter＋`source` 標籤，避免重算三個方案的 `soft_caps` 配比（spec §5.5）。

驗收一律對照 **spec §9**（SP／RW／SW／OP／TD／RP／CR／FU／ST／QT／PR／RG）；每則 task 的完成定義必須寫明覆蓋哪些 ID。

## 2. 里程碑

### M0 — 領域殼與契約地基

- **目標：** `radar`／`crm` 兩個 module 與路由骨架就位，未實作能力回**明確未就緒錯誤**（禁止 102000 空成功）。
- **完成定義：**
  - `generate/api/` 新增 radar／crm 契約檔 → `make gen-api` 產出 handler／routes（不手寫）。
  - Mongo index init 擴充：`radar_watches`、`radar_opportunities`、`radar_replies`、`radar_sweeps`、`crm_contacts`、`crm_touches`、`crm_followups`；含 spec §8 列出的複合索引與唯一鍵。
  - 前端 `src/domain/types.ts` 新增 Radar／CRM 型別，`src/data/repos.ts` 新增 `radar`、`crm` 介面，live 實作分檔 `src/data/live/radarRepos.ts`（先回未就緒錯誤）。
  - 新增 `src/styles/radar.css` 空殼並於 `main.tsx` 匯入，`npm run check:tokens` 通過。
- **建議 task 區間：** **T500–T509**
- **驗證：** 既有回歸 RG-01～RG-04 不破；新路由未完成能力 ≠ 102000 空 data；`make build` 與 `npm run build` 綠。

### M1 — 服務檔案＋雷達訂閱

- **目標：** 使用者能填服務檔案、建立與管理關鍵字訂閱，並拿到 AI 建議的關鍵字。
- **完成定義：**
  - ServiceProfile 讀寫（每會員一份），`forbidden[]`／`service_areas[]`／`remote_ok` 完整。
  - RadarWatch CRUD／pause／resume／archive；**未建服務檔案不得建 active watch**。
  - 方案 `max_active_watches` 閘（Free 1／Starter 5／Pro 20）生效並回明確錯誤含升級提示。
  - `watches/suggest` 依服務檔案產關鍵字建議，計入 `ai_copy`／`source=radar.suggest`。
  - 前端：`/app/policy` 獨立承載工作區共用服務 Policy（舊 `/app/brands?tab=serviceProfile` 轉址）；新增 `/app/radar/watches` 頁。
- **建議 task 區間：** **T510–T524**
- **驗證：** SP-01～SP-02、RW-01～RW-04。

### M2 — 每日巡管線＋五問判定（後端閉環）

- **目標：** 排程自動巡 → 抓取 → 五問判定 → 商機入庫，含配額截斷、去重與可觀測性。
- **完成定義：**
  - Job template `radar_sweep`：每日 **UTC 22:00** 為每個 active watch 建一筆；手動觸發共用同一路徑；多台 worker 沿用既有 Redis lock（value = `workerID`）。
  - 抓取**復用既有海巡雙路徑**：`dev_mode_enabled` 決定 api／crawler，不新增第三種。
  - 五問判定產 `intent_score`／`intent_band`／`reasons[]`（五條齊全否則拒絕入庫）／`region_match` 三態／`freshness_hours`；硬否決寫 `rejected` 仍入庫。
  - 配額截斷：達 `max_daily_opportunities` 依分數保留，記 `truncated_count`，**非錯誤**。
  - 去重：同 `owner_uid` 同 `external_id` 只建一筆，記錄多個觸發 term。
  - RadarSweep 紀錄命中／判定／截斷／失敗原因／耗用點數；失敗發通知，**禁止空成功**。
  - 計費標籤：`web_search`／`radar.sweep`、`ai_research`／`radar.judge`。
- **建議 task 區間：** **T525–T544**
- **驗證：** SW-01～SW-06、OP-01～OP-07、QT-01～QT-02。
- **閘門（硬性）：** 本 M 結束後、進 M3 前，須以**真實貼文樣本人工抽驗判定準確度**並記錄結果；準確度不可接受時先調權重／提示詞，不得直接進 M3。

### M3 — 今日商機頁＋回覆五版本

- **目標：** 第一個可展示的完整每日價值：打開就看到分級名單與可用的回覆。
- **完成定義：**
  - `/api/v1/radar/today` 統計＋卡片；`opportunities` 列表／詳情／accept／dismiss／override。
  - 前端 `/app/radar`：統計列、高中低分組（低意向預設收合）、卡片五欄位、五個動作、**空狀態必須給原因與下一步**。
  - 回覆五版本：首次判定只生成 `public_comment`，其餘按需；`forbidden[]` 硬性過濾，連兩次命中回明確錯誤且不輸出。
  - 送出接線：公開留言版走既有海巡外展／Outbox，**必經 AccountHealth 送出閘**；私訊版 P0 僅提供複製，UI 不得宣稱已自動送出。
  - 計費標籤 `ai_copy`／`source=radar.reply`。
  - 側欄資訊架構定案：新增「商機」分組，手機底欄 `radar` 進主要四格、`scout` 移入「更多」。
- **建議 task 區間：** **T545–T564**
- **驗證：** TD-01～TD-02、RP-01～RP-05；AccountHealth throttle 回歸。

### M4 — 聯絡人與 CRM

- **目標：** 商機能加入名單、收斂成人、推進階段、回報成交。
- **完成定義：**
  - accept → 依（平台＋作者＋owner）綁定或建立 Contact，`stage=new_found`；同一人多筆商機收斂在同一 Contact。
  - 七階段任意跳轉（含回退、跳級、終態重開），每次轉移寫 `ContactTouch`；`needs_follow_up` 為**並存 boolean**，非階段值。
  - 手動 merge／unmerge，跨 owner 拒絕；跨平台不自動合併。
  - 成交回報：階段轉 `won` → 寫既有 `growth_outcomes`（`source_type=radar_opportunity`）；可改／刪並留痕；**系統永不自動判定成交**。
  - 前端 `/app/crm` 採可搜尋、篩選、排序與分頁的單一工作清單；待追蹤是篩選，不重複 Contact；同頁詳情含時間軸、成交回報與安全移除。
  - 安全移除只把 Contact 退出工作名單並關閉提醒；商機、接觸、成交歷史保留，同作者再加入時恢復原 Contact。
- **建議 task 區間：** **T565–T584**
- **驗證：** CR-01～CR-11；RG-03（既有成果卡不受影響、radar 成交併入同一彙總）。

### M5 — 追蹤提醒＋轉換統計

- **目標：** 名單不會躺著爛掉，並回答「哪個關鍵字最會成交、哪種回覆最有效」。
- **完成定義：**
  - Job `radar_followup_scan` 日排程（維護 tick＋Redis lock）：到期 → `notified` ＋站內通知（`ref_type=contact`，落地 Contact 詳情）。
  - AI 追蹤訊息（`ai_copy`／`source=radar.followup`）；snooze／done；通知達 2 次 → `escalated` 僅建議轉 `lost`，**不自動改階段**。
  - `crm/stats`：關鍵字轉換率、回覆版本成功率、成交來源三維度；**任一維度樣本 < 5 只顯示絕對數並標「樣本不足」**。
  - 用量頁可依 `radar.` source 前綴聚出雷達耗用（供 P1 價格校準）。
- **建議 task 區間：** **T585–T599**
- **驗證：** FU-01～FU-04、ST-01～ST-03、QT-03。

### M6 — 海巡橋接、整合回歸、缺口文件

- **目標：** 既有用戶無痛接上，且確認沒有弄壞底座。
- **完成定義：**
  - `POST /api/v1/scout/posts/:id/promote`：**複製**建立新 Opportunity 並記 `source_scout_post_id`，原 ScoutPost 狀態不變；跑同一套判定；前端命中列新增動作。
  - Integration tests 覆蓋 SP／RW／SW／OP／TD／RP／CR／FU／ST／QT／PR 主路徑（Threads／AI 走 fake）。
  - 回歸：既有海巡三模式行為一致、Outbox 僅平台成功才 published、`soft_caps` 四分項加總仍等於 `monthly_credits`、全 repo 無以 `lead` 指稱銷售線索。
  - `TESTING.md` 與 `GAPS.md`：列出 P1／P2 未做項與已知限制（私訊無 API、時區未個人化、跨平台不自動合併）。
- **建議 task 區間：** **T600–T614**
- **驗證：** PR-01～PR-02、RG-01～RG-05、spec §9 全表勾選。

## 3. 依賴

```text
M0 殼與地基
 └─► M1 服務檔案＋雷達訂閱
      └─► M2 每日巡＋五問判定  ──【判定準確度閘門】
           └─► M3 今日頁＋回覆五版本
                └─► M4 聯絡人與 CRM
                     └─► M5 追蹤提醒＋轉換統計
                          └─► M6 橋接＋整合回歸
```

| 關係 | 說明 |
|------|------|
| M1 → M2 | 判定需要 ServiceProfile 作輸入；無服務檔案不得建 active watch |
| M2 → M3 | 今日頁的資料來源是 M2 產出的 Opportunity |
| M3 → M4 | 「加入名單」是 CRM 的唯一入口 |
| M4 → M5 | 追蹤提醒與統計都掛在 Contact／階段之上 |
| M2 ＋ M4 → M6 | 橋接需要判定管線與 CRM 都到位 |
| M3 → 既有 AccountHealth | 送出閘為既有能力，M3 只接線不改行為 |
| M4 → 既有 growth_outcomes | 成交寫既有成果事件，不新建帳 |
| 硬性順序 | **不可**先做 M4／M5 而跳過 M2／M3（requirements 決策 #11） |

## 4. 風險與緩解

| 風險 | 緩解 |
|------|------|
| **判定不準，名單不可信**（單點失敗） | M2 結束設硬性閘門：真實樣本人工抽驗才可進 M3；`reasons[]` 強制五條；使用者覆寫留痕作為調校素材；band 門檻 80／50 鎖定，只調權重與提示詞 |
| Threads 需求貼文量體不足 | M2 完成即可實測每日筆數；不足時先強化 `watches/suggest`（放寬關鍵字），M3 空狀態必須給原因與建議而非空白頁；手動匯入為 P1 補位 |
| 每日巡 AI 成本失控 | 配額閘與 M2 同批交付，非後補；`radar.` source 聚合每 M 檢視一次耗用 |
| 多台 worker 重複跑每日巡 | 沿用既有 Redis lock 與 guarded job 更新，M2 以多實例情境驗證 |
| 判定 Job 中途失敗導致重複扣點 | Sweep 記錄已判定進度，重跑不重判已完成者 |
| 破壞既有海巡 | 新 group／新 collection 並存，橋接單向；每個 M 都跑 RG-01～RG-05 |
| 前端 `global.css` 已 6,171 行再膨脹 | 新頁樣式一律進 `radar.css`，`npm run check:tokens` 為必跑閘 |
| 側欄 13 項資訊過載 | M3 一併定案分組與手機底欄調整，不拖到最後 |
| 私訊版被誤解為可自動送 | M3 明確禁止 UI 宣稱自動送出；「已私訊」階段在 P0 是人工標記 |
| 範圍膨脹塞 P1／P2 | M6 只列缺口不實作；手動匯入、金額、LINE 一律不進本輪 task |

## 5. 刻意不做（本 plan 週期內）

- **P1：** 手動匯入（貼網址／CSV）、報價與成交金額欄位、價格帶校準。
- **P2：** LINE 官方帳號、Google 商家評論、網站表單 webhook、自有留言私訊收斂、空檔媒合、可公開搜尋的論壇來源。
- 每日巡的**個人化時區**（P0 固定 UTC 22:00）。
- **跨平台自動合併聯絡人**（只做手動 merge）。
- 新增第五個 usage meter；改動既有方案價格與 `soft_caps` 配比。
- 重做付費流程、重做海巡抓取、重做貼文分類器。
- Admin 全站商機檢視與跨會員分析。
- 任何繞過 AccountHealth 送出閘的新路徑。

## 6. 全域驗證指令

```bash
# 後端
cd apps/backend && go test ./... -count=1 && make build

# Mongo 索引（M0 之後）
cd apps/backend && make migrate-up && make init

# 前端
cd apps/web && npm run build && npm run check:tokens && npm run test && npm run lint
```

里程碑級：

| M | 額外驗證 |
|---|----------|
| M0 | 新路由未實作回明確錯誤（非 102000 空 data）；索引建立可重複執行 |
| M1 | 無服務檔案建 active watch 被擋；Free 建第 2 個 watch 被擋 |
| M2 | 多實例 worker 不重複巡；命中 40／上限 30 時 `truncated_count=10`；**判定抽驗報告存檔** |
| M3 | 今日頁 0 筆時顯示原因；forbidden 詞連兩次命中回錯誤且不輸出；throttle 帳號自動送被拒 |
| M4 | 同一作者兩筆商機收斂同一 Contact；`won` 後既有成果彙總含此筆 |
| M5 | 到期 3 天進通知；樣本 3 筆時只顯示絕對數 |
| M6 | promote 後原 ScoutPost 狀態不變；spec §9 全表勾選；`rg -w lead` 無新增銷售線索用法 |

## 7. Task 號段總表（供拆 task 用）

| 區間 | Milestone | 內容 |
|------|-----------|------|
| T500–T509 | M0 | radar／crm api 殼、Mongo 索引、FE 型別與 repo 介面、`radar.css` |
| T510–T524 | M1 | ServiceProfile、RadarWatch CRUD／配額閘、關鍵字建議、FE 兩處畫面 |
| T525–T544 | M2 | `radar_sweep` Job 與排程、抓取復用、五問判定、去重、配額截斷、Sweep 觀測 |
| T545–T564 | M3 | today／opportunities API、`/app/radar` 頁、回覆五版本、送出接線、側欄 IA |
| T565–T584 | M4 | Contact／Touch 模型、階段機、merge、成交回報接 `growth_outcomes`、FE CRM 兩頁 |
| T585–T599 | M5 | `radar_followup_scan`、通知、AI 追蹤訊息、`crm/stats`、用量 source 聚合 |
| T600–T614 | M6 | scout promote 橋接、integration tests、回歸、TESTING／GAPS |

單一 task 目標 **~100–200 行**生產碼變更；超大則再拆。拆 task 時每則必須寫：Goal、Depends on、Inputs、Outputs（路徑＋行為）、Out of scope、Acceptance（可執行指令）、對應 spec §9 ID。

## 8. 批准

- [x] Plan 已批准（2026-07-31／使用者「請繼續拆解」）→ tasks 見 [`tasks/INDEX.md`](./tasks/INDEX.md)（58 則，號段 T500–T604）
