# 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/brands` 新增「服務檔案」分頁;新增 `/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` 八格視圖與篩選排序、`/app/crm/:contactId` 時間軸與成交回報。 - **建議 task 區間:** **T565–T584** - **驗證:** CR-01~CR-09;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)