thread-master/docs/product/demand-radar/plan.md

199 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 M0M6、growth-loop M0M6 均已交付)
## 1. 交付策略
1. **先讓「每天有名單」成立。** M1M3 一路做到今日商機頁能開出「今日找到 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**SPRWSWOPTDRPCRFUSTQTPRRG每則 task 的完成定義必須寫明覆蓋哪些 ID。
## 2. 里程碑
### M0 — 領域殼與契約地基
- **目標:** `radar``crm` 兩個 module 與路由骨架就位,未實作能力回**明確未就緒錯誤**(禁止 102000 空成功)。
- **完成定義:**
- `generate/api/` 新增 radarcrm 契約檔 → `make gen-api` 產出 handlerroutes不手寫
- Mongo index init 擴充:`radar_watches`、`radar_opportunities`、`radar_replies`、`radar_sweeps`、`crm_contacts`、`crm_touches`、`crm_followups`;含 spec §8 列出的複合索引與唯一鍵。
- 前端 `src/domain/types.ts` 新增 RadarCRM 型別,`src/data/repos.ts` 新增 `radar`、`crm` 介面live 實作分檔 `src/data/live/radarRepos.ts`(先回未就緒錯誤)。
- 新增 `src/styles/radar.css` 空殼並於 `main.tsx` 匯入,`npm run check:tokens` 通過。
- **建議 task 區間:** **T500T509**
- **驗證:** 既有回歸 RG-01RG-04 不破;新路由未完成能力 ≠ 102000 空 data`make build` 與 `npm run build` 綠。
### M1 — 服務檔案+雷達訂閱
- **目標:** 使用者能填服務檔案、建立與管理關鍵字訂閱,並拿到 AI 建議的關鍵字。
- **完成定義:**
- ServiceProfile 讀寫(每會員一份),`forbidden[]``service_areas[]``remote_ok` 完整。
- RadarWatch CRUDpauseresumearchive**未建服務檔案不得建 active watch**。
- 方案 `max_active_watches`Free 1Starter 5Pro 20生效並回明確錯誤含升級提示。
- `watches/suggest` 依服務檔案產關鍵字建議,計入 `ai_copy``source=radar.suggest`。
- 前端:`/app/brands` 新增「服務檔案」分頁;新增 `/app/radar/watches` 頁。
- **建議 task 區間:** **T510T524**
- **驗證:** SP-01SP-02、RW-01RW-04。
### M2 — 每日巡管線+五問判定(後端閉環)
- **目標:** 排程自動巡 → 抓取 → 五問判定 → 商機入庫,含配額截斷、去重與可觀測性。
- **完成定義:**
- Job template `radar_sweep`:每日 **UTC 22:00** 為每個 active watch 建一筆;手動觸發共用同一路徑;多台 worker 沿用既有 Redis lockvalue = `workerID`)。
- 抓取**復用既有海巡雙路徑**`dev_mode_enabled` 決定 apicrawler不新增第三種。
- 五問判定產 `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 區間:** **T525T544**
- **驗證:** SW-01SW-06、OP-01OP-07、QT-01QT-02。
- **閘門(硬性):** 本 M 結束後、進 M3 前,須以**真實貼文樣本人工抽驗判定準確度**並記錄結果;準確度不可接受時先調權重/提示詞,不得直接進 M3。
### M3 — 今日商機頁+回覆五版本
- **目標:** 第一個可展示的完整每日價值:打開就看到分級名單與可用的回覆。
- **完成定義:**
- `/api/v1/radar/today` 統計+卡片;`opportunities` 列表詳情acceptdismissoverride。
- 前端 `/app/radar`:統計列、高中低分組(低意向預設收合)、卡片五欄位、五個動作、**空狀態必須給原因與下一步**。
- 回覆五版本:首次判定只生成 `public_comment`,其餘按需;`forbidden[]` 硬性過濾,連兩次命中回明確錯誤且不輸出。
- 送出接線公開留言版走既有海巡外展Outbox**必經 AccountHealth 送出閘**;私訊版 P0 僅提供複製UI 不得宣稱已自動送出。
- 計費標籤 `ai_copy``source=radar.reply`。
- 側欄資訊架構定案:新增「商機」分組,手機底欄 `radar` 進主要四格、`scout` 移入「更多」。
- **建議 task 區間:** **T545T564**
- **驗證:** TD-01TD-02、RP-01RP-05AccountHealth throttle 回歸。
### M4 — 聯絡人與 CRM
- **目標:** 商機能加入名單、收斂成人、推進階段、回報成交。
- **完成定義:**
- accept → 依平台作者owner綁定或建立 Contact`stage=new_found`;同一人多筆商機收斂在同一 Contact。
- 七階段任意跳轉(含回退、跳級、終態重開),每次轉移寫 `ContactTouch``needs_follow_up` 為**並存 boolean**,非階段值。
- 手動 mergeunmerge跨 owner 拒絕;跨平台不自動合併。
- 成交回報:階段轉 `won` → 寫既有 `growth_outcomes``source_type=radar_opportunity`);可改/刪並留痕;**系統永不自動判定成交**。
- 前端 `/app/crm` 八格視圖與篩選排序、`/app/crm/:contactId` 時間軸與成交回報。
- **建議 task 區間:** **T565T584**
- **驗證:** CR-01CR-09RG-03既有成果卡不受影響、radar 成交併入同一彙總)。
### M5 — 追蹤提醒+轉換統計
- **目標:** 名單不會躺著爛掉,並回答「哪個關鍵字最會成交、哪種回覆最有效」。
- **完成定義:**
- Job `radar_followup_scan` 日排程(維護 tickRedis lock到期 → `notified` +站內通知(`ref_type=contact`,落地 Contact 詳情)。
- AI 追蹤訊息(`ai_copy``source=radar.followup`snoozedone通知達 2 次 → `escalated` 僅建議轉 `lost`**不自動改階段**。
- `crm/stats`:關鍵字轉換率、回覆版本成功率、成交來源三維度;**任一維度樣本 < 5 只顯示絕對數並標樣本不足」**。
- 用量頁可依 `radar.` source 前綴聚出雷達耗用 P1 價格校準)。
- **建議 task 區間** **T585T599**
- **驗證** FU-01FU-04ST-01ST-03QT-03
### M6 — 海巡橋接、整合回歸、缺口文件
- **目標** 既有用戶無痛接上且確認沒有弄壞底座
- **完成定義**
- `POST /api/v1/scout/posts/:id/promote`**複製**建立新 Opportunity 並記 `source_scout_post_id` ScoutPost 狀態不變跑同一套判定前端命中列新增動作
- Integration tests 覆蓋 SPRWSWOPTDRPCRFUSTQTPR 主路徑ThreadsAI fake)。
- 回歸既有海巡三模式行為一致Outbox 僅平台成功才 published、`soft_caps` 四分項加總仍等於 `monthly_credits` repo 無以 `lead` 指稱銷售線索
- `TESTING.md` `GAPS.md`列出 P1P2 未做項與已知限制私訊無 API時區未個人化跨平台不自動合併)。
- **建議 task 區間** **T600T614**
- **驗證** PR-01PR-02RG-01RG-05spec §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 | 成交寫既有成果事件不新建帳 |
| 硬性順序 | **不可**先做 M4M5 而跳過 M2M3requirements 決策 #11 |
## 4. 風險與緩解
| 風險 | 緩解 |
|------|------|
| **判定不準,名單不可信**單點失敗 | M2 結束設硬性閘門真實樣本人工抽驗才可進 M3`reasons[]` 強制五條使用者覆寫留痕作為調校素材band 門檻 8050 鎖定只調權重與提示詞 |
| Threads 需求貼文量體不足 | M2 完成即可實測每日筆數不足時先強化 `watches/suggest`放寬關鍵字M3 空狀態必須給原因與建議而非空白頁手動匯入為 P1 補位 |
| 每日巡 AI 成本失控 | 配額閘與 M2 同批交付非後補`radar.` source 聚合每 M 檢視一次耗用 |
| 多台 worker 重複跑每日巡 | 沿用既有 Redis lock guarded job 更新M2 以多實例情境驗證 |
| 判定 Job 中途失敗導致重複扣點 | Sweep 記錄已判定進度重跑不重判已完成者 |
| 破壞既有海巡 | group collection 並存橋接單向每個 M 都跑 RG-01RG-05 |
| 前端 `global.css` 6,171 行再膨脹 | 新頁樣式一律進 `radar.css``npm run check:tokens` 為必跑閘 |
| 側欄 13 項資訊過載 | M3 一併定案分組與手機底欄調整不拖到最後 |
| 私訊版被誤解為可自動送 | M3 明確禁止 UI 宣稱自動送出;「已私訊階段在 P0 是人工標記 |
| 範圍膨脹塞 P1P2 | 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 | 內容 |
|------|-----------|------|
| T500T509 | M0 | radarcrm api Mongo 索引FE 型別與 repo 介面、`radar.css` |
| T510T524 | M1 | ServiceProfileRadarWatch CRUD配額閘關鍵字建議FE 兩處畫面 |
| T525T544 | M2 | `radar_sweep` Job 與排程抓取復用五問判定去重配額截斷Sweep 觀測 |
| T545T564 | M3 | todayopportunities API、`/app/radar` 回覆五版本送出接線側欄 IA |
| T565T584 | M4 | ContactTouch 模型階段機merge成交回報接 `growth_outcomes`FE CRM 兩頁 |
| T585T599 | M5 | `radar_followup_scan`通知AI 追蹤訊息、`crm/stats`、用量 source 聚合 |
| T600T614 | M6 | scout promote 橋接integration tests回歸TESTINGGAPS |
單一 task 目標 **~100200 **生產碼變更超大則再拆 task 時每則必須寫GoalDepends onInputsOutputs路徑行為)、Out of scopeAcceptance可執行指令)、對應 spec §9 ID
## 8. 批准
- [x] Plan 已批准2026-07-31使用者請繼續拆解」)→ tasks [`tasks/INDEX.md`](./tasks/INDEX.md)58 號段 T500T604