thread-master/docs/product/haixun-console/spec.md

17 KiB
Raw Blame History

Spec: 巡樓 Console 重構Harbor Desk

Status: approved
Source: docs/product/haixun-console/requirements.mdapproved 2026-07-09
Last updated: 2026-07-09
批准:使用者要求開始拆 plan/tasks2026-07-09
變更reply 步驟發言池含 lead主帳可回別人的回覆
範圍Phase A 地基契約 + Phase B 前端/Mock 行為Phase C 接真映射Phase D 僅邊界預留


1. 摘要

重構後的巡樓 Console 分四階段交付。本規格鎖定:

  1. 平台地基登入會話、角色權限、Threads 帳號基礎、Job、站內通知。
  2. 新前端 Harbor Desk5 入口 IA、Studio 多帳互回 wizard、Outbox、通知預設 mock 資料
  3. 同一契約mock | live 切換B 未過關不開大型業務後端。

落點2026-07-09 已封存舊碼):

項目 決策
舊 monorepo old/ 整包封存,只讀
新前端 repo 根 apps/web(從零)
新後端 services/api從零A 可先 mock-only 前端)
視覺 Harbor Desk 為唯一預設主題
海巡 Scout B 階段占位空頁
Email 驗證閘 A/B 關閉
Mock 持久化 localStorage

2. 名詞與實體

名詞 定義
Member 登入會員email/password + JWT
Role 簡化:member | admin(可多值)
ThreadsAccount 已綁定的 Threads 經營帳號OAuth token 由後端保管)
Lead 本串主帳號:固定發 root 樓主貼;並屬於發言池,可在後續 reply 再出場
Cast 本串額外可出場帳號(通常不含 lead與 lead 組成發言池
Speakers發言池 { lead_account_id } cast_account_ids每一則 reply 步驟的 account_id 必須 ∈ Speakers允許 = lead
ThreadPlay 發文計畫 / 串場劇本(一則主貼 + 有序回覆步驟)
PlayStep 劇本一步:kind=root|reply、綁定 account_id、文案、可選排程偏移;reply 語意為接上一步(單線)
OutboxItem 出站佇列項目:對應某 PlayStep 的一次發送意圖與狀態
Job 背景任務 run進度、狀態、結果摘要
Notification 站內通知(多由 Job 終態產生)
DataSource mock | live:前端資料來源模式

3. 狀態機

3.1 Job

pending --> queued --> running --> succeeded
                       running --> failed
                       running --> cancel_requested --> cancelled
queued|pending --> cancelled   (若支援提前取消)
* --> expired                  (lock 逾時後端語意mock 可簡化省略)

3.2 ThreadPlay

draft --> ready          (劇本與帳號齊全、通過預覽)
ready --> scheduling     (寫入 Outbox)
scheduling --> active    (至少一步 in flight 或 scheduled)
active --> completed     (全部步驟 succeeded 或 skipped)
active --> partial_failed (有 failed可重試)
draft|ready --> archived

3.3 PlayStep / OutboxItem

draft --> scheduled --> publishing --> published
                         publishing --> failed
scheduled --> cancelled
failed --> scheduled     (retry)

規則:

  • root 步驟未 published 前,後續 reply 不得進入 publishing
  • rootfailed,後續步驟保持 scheduled 或改 blocked(顯示「等待主貼」)。
  • root.account_id 必須 = lead_account_id
  • 任一 reply.account_id 必須 ∈ Speakers含 lead);校驗失敗不可 submit。
  • P0 單線:第 n 步 reply 視為回覆第 n1 步已發出的那則(不另選 parentD 再談樹狀)。

3.4 ThreadsAccount.connection

unknown --> connected
unknown --> error
connected --> error
error --> connected      (重新授權/診斷成功)

4. 使用者流程

4.1 登入A/B

  1. 使用者開 /login,輸入 email、password可選 tenant 若多租戶 UI 需要mock 固定 default)。
  2. 成功 → 存 access/refreshlive或 session flagmock→ 進 /app/today
  3. 失敗 → 表單錯誤,不進主殼。
  4. 重新整理:有有效會話則進主殼,否則回登入。
  5. 登出 → 清會話 → 登入頁。

4.2 帳號 CrewB mock / C live

  1. 列表顯示已綁帳號顯示名、頭像色、connection、是否預設 lead 候選。
  2. 「連接 Threads」live 走 OAuthmock 新增一個假帳號。
  3. 標記不健康帳號不可被選為 Lead/Castdisabled + 原因)。

4.3 創作 Studio — 互回 wizardB 核心)

步驟 畫面行為
1 主題 標題/一句主題;可「示範填入」
2 船隊 必選 1 Lead多選 Cast≥0UI 列表可預設排除 lead避免重複勾不可選 error 帳號
3 劇本 第 0 步固定 root + account_id=lead(不可改帳);其後每步 reply下拉發言池 = lead + 全部 cast含 lead,以便主帳回別人的回覆)+ textarea可增刪排序root 固定頂)
4 預覽 對話泡泡:帳號名 + 文案root 標記;可見 lead 出現在非第 0 步
5 船期 主貼時間datetime回覆間隔分鐘預設 5可預覽各步預計時間
6 提交 建/更新 ThreadPlay=scheduling,生成 OutboxItem導向 Outbox 或 Today

任一步「上一步」保留表單狀態。草稿自動存mocklocalStorage key 見 §8

4.4 Outbox

  1. 列表Play 標題、狀態、步驟進度2/5
  2. 點進詳情:逐步狀態時間線。
  3. Mock「模擬發送」依序 root→replies 推進狀態(可加速)。
  4. 失敗步驟顯示錯誤文案 +「重試」mock 直接 succeeded

4.5 Today

  1. 卡片:待處理失敗、今日排程、進行中 Job、快捷「開新串場」。
  2. 點通知/失敗 → 對應 Outbox 或 Job 詳情。

4.6 通知

  1. 頂欄鈴鐺:未讀數。
  2. 下拉最近 N 筆;點擊標記已讀並 navigate。
  3. Job succeeded|failed 產生通知mock 在模擬結束時寫入)。

4.7 ScoutB

占位頁:標題「探查」、短說明「海巡獲客將在下一階段開放」、無假成功操作。

4.8 設定

  • 顯示目前 DataSource 切換dev 可見prod 預設 live 且可藏)。
  • AI keyB 僅 UI 存 local不送出C 再定義。

5. 介面契約

5.0 共用規則

規則 規格
Envelope { code, message, data?, error? };成功 code=102000
列表 query page/pageSizedata: { pagination, list }
時間 API/DBunix nanoseconds int64 UTCUI 顯示本地時區
Auth Authorization: Bearer <access>401 → refresh 一次 → 仍失敗登出
禁止 未實作端點回 200+空成功;應 501 或明確業務錯誤

前端 repository 介面(概念):

AuthRepo, MemberRepo, PermissionRepo,
ThreadsAccountRepo, ThreadPlayRepo, OutboxRepo,
JobRepo, NotificationRepo

createRepos(dataSource) → mock 或 live 實作同一 TypeScript interface。

5.1 HTTP / APIPhase A 最小 + C 對齊)

路徑為目標契約;可與舊 /api/v1 對齊以利搬碼。Mock 不經 HTTP 亦可,但 shape 相同。

Auth / Member / Permission

Method Path 用途 要點
POST /api/v1/auth/login 登入 body: email, password, tenant_id? → tokens
POST /api/v1/auth/refresh 換票 refresh_token → tokens
POST /api/v1/auth/logout 登出 auth
GET /api/v1/members/me tenant_id, uid, email, roles[]
GET /api/v1/permissions/me 我的權限 keys[] 或 *

Admin 建號可延後mock 內建一組使用者即可。

Threads accounts

Method Path 用途
GET /api/v1/threads-accounts 列表pagination
POST /api/v1/threads-accounts/oauth/start 取得授權 URLlive
GET /api/v1/threads-accounts/:id 詳情 / 健康
POST /api/v1/threads-accounts/:id/diagnostics 連線診斷(可選 A

Jobs

Method Path 用途
GET /api/v1/jobs 列表
GET /api/v1/jobs/:id 詳情
POST /api/v1/jobs/:id/cancel 取消
POST /api/v1/jobs 建立A 可僅 demo 或內部)

Job 資源欄位(最低):id, template_type, status, progress_summary, progress_percent, error?, created_at, updated_at, completed_at?

Notifications

Method Path 用途
GET /api/v1/notifications 列表
POST /api/v1/notifications/:id/read 已讀
POST /api/v1/notifications/read-all 全已讀

欄位:id, title, body, kind, ref_type, ref_id, read_at?, created_at

ThreadPlay / OutboxB mock 完整C 再 live

Method Path 用途
GET/POST /api/v1/thread-plays 列表 / 建立
GET/PATCH /api/v1/thread-plays/:id 讀 / 更新劇本
POST /api/v1/thread-plays/:id/submit 提交進 Outbox
GET /api/v1/outbox 佇列列表
GET /api/v1/outbox/:id 詳情(含 steps
POST /api/v1/outbox/:id/steps/:stepId/retry 重試一步

ThreadPlay 主體(邏輯 JSON

{
  "id": "…",
  "title": "週末咖啡話題",
  "topic": "…",
  "status": "draft",
  "lead_account_id": "…",
  "cast_account_ids": ["…"],
  "steps": [
    {
      "id": "s0",
      "sort_order": 0,
      "kind": "root",
      "account_id": "…",
      "text": "主貼文案",
      "delay_from_previous_sec": 0
    },
    {
      "id": "s1",
      "sort_order": 1,
      "kind": "reply",
      "account_id": "…",
      "text": "回覆文案",
      "delay_from_previous_sec": 300
    }
  ],
  "schedule_start_at": 0,
  "created_at": 0,
  "updated_at": 0
}

5.2 前端路由(apps/web

使用 path router(非 hashbasename 可 /

Path 名稱 需登入 Phase
/login 登入 B
/app 佈局殼 B
/app/today 今日 B
/app/crew 帳號 B
/app/studio 創作列表 B
/app/studio/new 新 wizard B
/app/studio/:playId 編輯 wizard B
/app/outbox 發送佇列 B
/app/outbox/:id 佇列詳情 B
/app/jobs 任務(可二級) B
/app/jobs/:id 任務詳情 B
/app/scout 探查占位 B
/app/settings 設定 B

導覽一級≤5 Today · Crew · Studio · Scout · Outbox
Jobs 從 Today/通知進入Settings 在使用者選單。

5.3 Job typesA 最小)

template_type 用途 Phase
demo_long_task 驗證 job+通知 A/B mock 可模擬
(業務 job 發佈串場、8D… C/D

B mock 可不跑真 workerTimer 推進 job 狀態並寫 notification。


6. 與現況對照

能力 決策 備註
整個舊 monorepo Archive 實體路徑 old/;主線不引用為 package
舊 Auth/JWT/envelope 參考後重寫 契約精神可相容
舊 Job 核心 參考後重寫 可抄模式,不搬半成品
copy_mission 管線 Discard 不復活
舊 web Console Archive apps/web
海巡 Patrol 全套 Defer D Scout 占位
田園掌機 UI Discard 預設 Harbor Desk
時間/envelope/job 規則 Keep as policy 見根目錄 AGENTS.md

7. 非功能

安全 / 權限

  • 未登入不可進 /app/**
  • admin 專屬操作(若有)前後端雙檢。
  • Livetoken 不進 URLrefresh 單飛。
  • Mock不模擬權限繞過成功寫入 live。

錯誤

  • UI 可讀中文 message。
  • 區分 401登出vs 5xx保留會話顯示重試

可觀測

  • Job 進度摘要必顯示。
  • Outbox 步驟級狀態必顯示。

效能B

  • 首屏可互動 < 3s本機 dev 網路)。
  • 列表 mock ≤ 50 筆即可。

無障礙(基線)

  • 表單 label 關聯;主按鈕對比足夠;焦點可見。

8. 資料保存(邏輯)

8.1 Mock localStorage keys

Key 內容
harbor.datasource mock | live
harbor.session mock 登入態
harbor.accounts ThreadsAccount[]
harbor.plays ThreadPlay[]
harbor.outbox Outbox 聚合
harbor.jobs Job[]
harbor.notifications Notification[]
harbor.play_draft wizard 未提交草稿

8.2 實體欄位(最低)

實體 關鍵欄位
Member uid, tenant_id, email, roles
ThreadsAccount id, username, display_name, connection, is_usable
ThreadPlay 見 §5.1 JSON
OutboxBundle id, play_id, status, steps[]
OutboxStep step_id, account_id, status, error?, published_at?, scheduled_at?
Job 見 §5.1
Notification 見 §5.1

9. UI 規格Harbor Desk

9.1 Layout

┌─────────────────────────────────────────────┐
│ Logo   [今日]…          🔔  主題  使用者 ▾   │  topbar
├──────────┬──────────────────────────────────┤
│ Today    │  Page title                      │
│ Crew     │  Content                         │
│ Studio   │                                  │
│ Scout    │                                  │
│ Outbox   │                                  │
└──────────┴──────────────────────────────────┘
  • 桌面:左導覽 ~220px。
  • 手機:底欄 5 格Today/Crew/Studio/Scout/Outbox設定進使用者選單。
  • 內容最大寬 ~1080px表單閱讀寬 ~640720pxwizard

9.2 視覺 token建議名實作可微調色值

Token 用途 建議
--hb-bg 頁背景 冷灰白
--hb-surface 卡片
--hb-ink 主文 近黑 slate
--hb-muted 次文 可讀灰(勿過淡)
--hb-line 邊框 淺灰
--hb-brand 主色/CTA 霧海青
--hb-danger 錯誤
--hb-success 成功
--radius 卡片 1216px
--font-sans 混排 Inter, Taipei Sans TC, system

禁止滿版動態雲、木質頂欄、emoji 主 icon、彩虹漸層按鈕。

9.3 元件基線

Buttonprimary/ghost/danger、Input、Textarea、Select、Card、Badge狀態、EmptyState、Stepperwizard、Toast、BellMenu。


10. 驗收場景Given / When / Then

  1. Given 未登入 When/app/today Then 導向 /login
  2. Given mock 帳密正確 When 登入 Then 進 Today 且看到至少一張待辦/空狀態。
  3. Given 兩個 usable 帳號 A、B When Studiolead=A cast=B劇本為 root(A) → reply(B) → reply(A) Then 可預覽三句submit 後 Outbox 三步順序正確且第二則回覆發言者為 lead。
    3b. Given 劇本某 reply 選了不在 Speakers 的帳號 When 提交 Then 阻擋並提示。
  4. Given 已提交串 When 模擬發送 Then root→reply 依序 published通知出現成功。
  5. Given root 模擬失敗 When 查看 Then reply 不標 published可重試 root。
  6. Given Job 模擬完成 When 開鈴鐺 Then 未讀 +1點進 job 詳情。
  7. Given Scout When 進入 Then 僅占位,無「成功建立海巡」類假操作。
  8. Given live 模式且後端 5xx When 拉 me Then 不強制清 token與需求 US-09 對齊C 必須)。

11. Phase 邊界(實作不得越級)

Phase 不做
A 契約+可選最小 live auth/job或純 mock 平台殼 海巡、完整互回後端
B apps/web 全 mock 體驗 大寫業務 API
C 逐 repo 接 live 改 IA 大翻(除非修 bug
D 海巡等

12. 開放規格問題

  1. Tenant UI login 是否顯示 tenant 欄?預設隱藏mock/live 固定 default tenant。
  2. Lead 能否出現在 reply已決策:必須支援主帳回別人的回覆。Cast 多選 UI 仍可不重複勾 lead發言下拉一定含 lead。
  3. A 是否必須先有可跑的 Go gateway還是 B 可完全純前端? 預設B 可純前端 mockA 的 live 可與 B 平行,但不阻塞 B 過關。
  4. Outbox 與舊 publish_queue 是否同 collection 預設C 再定映射;規格層當新模型。

13. 批准

  • Spec 已批准2026-07-09使用者好請幫我開始拆
  • 下一步:依 plan.mdtasks/ 實作