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

38 KiB
Raw Blame History

Spec: 巡樓後端Phase C · live

Status: approved
Status note: 使用者 2026-07-10「spec 通過」
Source: docs/product/haixun-backend/requirements.mdapproved
Last updated: 2026-07-10
修訂:平台 keyBYOK 計費;會員 API 齊全、頁面可不開;§9 嚴格驗收場景全量

1. 摘要

本規格定義 Harbor Deskapps/web)接 live 時後端必須具備的對外行為與跨切契約。
骨架 搬入並改造 stand-alone-service,撰寫 一律 go-zero.api → generate → handlerlogicmodule
P0數字 uid、權限、Threads OAuth、Job通知、AI·Exa·dev 設定(平台 keyBYOK)。
P1創作Outbox 真發送、人設、靈感研究、用量、海巡雙路徑邀請關係
會員相關 API 必須完整(含註冊/三方登入等);前端頁面可不提供(現階段 UI 以 admin 代建email 登入為主)。
Worker 為 獨立 binaryMongo index initseeder 分包裝。

2. 名詞與實體

名詞 定義
Member 租戶內登入主體;主鍵 uid 為數字;以 email或三方綁定登入角色含至少 memberadmin
Permission 能力點或角色集合;中介層依 token 內身份擋 API
Platform keys 寫在 系統設定檔etc 的 AIExa 等金鑰;全站預設用這組代打外部服務
BYOK Bring Your Own Key會員在設定填了自己的 AI 或 Exa key 時,該 meter 改打會員 key
Key mode 單次呼叫解析結果:platformbyok(寫入 UsageEvent
ThreadsAccount OAuth 綁定的 Threads 帳號
ThreadPlay 互回方案;可指定 schedule_start_at
OutboxBundle / OutboxStep 發送佇列;worker 真打 Threads
Job 背景任務guarded 更新
Notification 站內通知
Persona / Brand / Scout* 人設、品牌、海巡
Invite 邀請碼、parent_uid(數字 uid
AiSettings / PlacementSettings 會員級覆寫providermodel、是否已配 key、dev_mode
Usage / Plan freestarterpro事件含是否 BYOK
Gateway / Worker go-zero HTTP獨立 worker

3. 狀態機

3.1 Member

created (admin 代建 | 自助註冊 API | 三方首次登入) --> email_unverified | active
email_unverified --> active           : 驗證成功(規則依註冊管道)
active --> suspended                  : admin 停權
suspended --> active                  : admin 復權
* --> password_reset_pending          : 申請重設token 時限)
  • 頁面:現階段前端不提供公開註冊/三方登入 UI。
  • API:會員域 完整提供(含註冊、三方 OAuth 綁定/登入等),供日後開頁或外部客戶端;未開 UI ≠ 不做 API。
  • 停權會員:登入拒絕;既有 token 在 me中介層視為無效。

3.2 ThreadsAccountOAuth

(none) --> authorizing                : 發起 OAuth
authorizing --> connected             : callback 成功換 token
authorizing --> failed                : 使用者拒絕/換票失敗
connected --> connected               : refresh session 成功
connected --> expired_or_error        : token 失效
expired_or_error --> connected        : 重新授權或 refresh 成功
* --> disconnected                    : 解除綁定(刪除或標記不可用)
  • is_usable:僅 connected 且未過期可供 leadcast發送。

3.3 Job

pending --> queued --> running --> succeeded
                       running --> failed
                       running --> cancel_requested --> cancelled
queued|pending --> cancelled
  • 更新必須 guarded樂觀條件或等價Redis lock value = workerIDAGENTS.md)。

3.4 OutboxBundle / OutboxStep

Bundle: scheduling --> active --> completed
                      active --> partial_failed
                      * --> cancelled

Step:   draft|scheduled --> publishing --> published
                            publishing --> failed
        failed --> publishing   : retry規則允許時
        * --> cancelled|blocked
  • 真發送: published 僅在 Threads API或等價回成功後寫入。
  • root主貼失敗 後續 reply 不得標為 published可標 blocked維持 scheduled。
  • 步驟 scheduled_at 來自 play 的 schedule_start_at 與間隔;未到時間 worker 不送
  • 單篇 compose 亦必帶可選 schedule_start_at(預設 now

3.5 Invite 綁定

parent_uid = null  -->  parent_uid = inviter_uid   : 會員 apply 邀請碼(一次)
parent_uid != null -->  (會員不可改)
admin: parent_uid 任意合法調整(防環:不可掛到自己的子孫下)

3.6 海巡掃描路徑

dev_mode_enabled == false  -->  scan_via_api
dev_mode_enabled == true   -->  scan_via_crawler  (Chrome extension session)

兩條路徑皆須實作;結果統一寫入 ScoutPost 語意。

4. 使用者流程(行為)

4.1 會員進站P0

主路徑(現前端):

  1. Admin 代建島民 → 數字 uid、臨時密碼、角色、邀請碼、可選 parent密碼僅回傳一次。
  2. 島民 email密碼 login → JWT。
  3. 未驗證信箱:對齊前端閘(僅驗證/登出/有限 profile

完整 API頁面可暫無

  1. 自助註冊、寄碼、驗證、三方 OAuth 註冊/登入/綁定解綁等 後端齊全
  2. 行為與權限與 admin 代建一致(數字 uid、角色預設 member、產邀請碼
  3. 前端何時開頁不阻擋 API 驗收。

4.2 Threads OAuthP0

  1. 已登入會員請求「開始綁定」→ 系統回授權 URL含 state
  2. 使用者在 ThreadsMeta 完成授權 → callback 至後端。
  3. 後端換 token、寫入 ThreadsAccount密文 token 不進 JWT、不進前端永久存儲
  4. 前端列表顯示 username、connection、session 過期語意。
  5. 刷新 session成功延長更新 session_* 欄位;失敗標記 error。

4.3 互回/單篇 → Outbox 真發送P1

  1. 會員存 ThreadPlay含 steps、leadcast、schedule_start_at、間隔)或 compose 單篇(含 schedule_start_at)。
  2. submitpublishSingle → 建立 OutboxBundleStepsscheduled_at 算好)。
  3. Worker 到點取步驟 → publishing → 呼叫 Threads → published 或 failed。
  4. 會員看 Outbox 詳情;失敗可 retry合規則

4.4 海巡P1

  1. 會員維護 BrandProductprepareBrief → 可審 scan_terms。
  2. runScan讀會員 dev_mode_enabled 選 API 或爬蟲。
  3. 命中列表可 draft outreachskipmarksend發送仍受 Outbox 或直接回覆契約約束spec 實作時與前端 mock 對齊)。

4.5 邀請P1

  1. 每位會員有唯一 invite_code
  2. 無邀請人:applyInviteCode 綁定;已綁定拒絕。
  3. 會員讀自己網絡(邀請人、直邀、延伸計數)。
  4. Admin搜尋、焦點調 parent_uid、防環。

4.6 設定 AIExa 與金鑰模式P0

  1. 系統預設 key平台模式

    • 寫在 唯一 etc 設定檔(或 env 覆寫同鍵):至少
      • AI各 provider 的平台 API key或目前啟用 provider 的 key
      • 搜尋Exa 平台 API key
    • 會員未填自己的 key 時,所有 AI搜尋呼叫走 platform keys
  2. 會員 BYOK

    • 會員在設定填寫自己的 AI key 和/或 Exa key → 該類呼叫改走會員 key。
    • 讀取 API 只回 *_configured(是否已填),永不回明文
    • 清除會員 key → 回到平台 key。
  3. 解析順序(每次外部呼叫)

if 會員該 meter 已配置自己的 key:
    use BYOK key  →  key_mode = byok
else if etc 有平台 key:
    use platform key → key_mode = platform
else:
    fail 明確錯誤(未設定可用金鑰)
  1. Providermodel單一 providerxaiopencode-go)+單一 model換 provider 回模型清單並保留同名或預設第一個。
  2. dev_mode_enabled 只影響海巡掃描路徑,與 BYOK 正交。

4.7 用量怎麼算(平台 vs BYOK— 契約(分開計、分開顯示)

目標:

  1. 平台代付才扣方案 credits,並計入「平台用量」。
  2. BYOK 不扣方案 credits必須另計次數,且 報表API前端展示與平台用量完全分開,禁止混成一條「總次數」讓人分不清誰付的。

4.7.1 記帳規則

情況 扣方案 credits 記什麼 event 標記
AI · platform 平台點數 + 平台次數 key_mode=platformcredits>0
AI · byok 僅 BYOK 次數credits 固定 0 key_mode=byok
搜尋 · platform 同上 platform
搜尋 · byok 僅 BYOK 次數 byok
混合(同流程 AI+搜尋) 各 meter 獨立 兩筆 event 可不同 key_mode AI byok、搜尋 platform → 平台只扣搜尋點BYOK 只加 AI 次數
雙端皆無 key 失敗 不寫成功用量 錯誤可讀
外部呼叫失敗 不扣成功點 可選 failed 事件 不計入成功 BYOK平台次數

額度檢查(僅 platform 呼叫前查剩餘 credits → 不足拒絕 → 成功後原子扣點並寫 platform event。

BYOK 不查方案 credits可另設 BYOK 專用 rate limit每分鐘etc 可配);計入 byok_count 系列欄位。

4.7.2 彙總欄位(必須分開,禁止混加)

UsageMonthSummary(或等價)至少拆成兩組,不得只回一個模糊的 total_calls

欄位組 含義 計入條件
platform credits_usedcredits_remaining、可選 platform_call_count by meter key_mode=platform 成功事件
byok byok_call_count 總計 + by meter(如 ai_copy / web_search / ai_research / ai_image key_mode=byok 成功事件
  • 平台「已用點數」加總 platform。
  • BYOK「使用次數」加總 byok不得加進 platform 的 used 或讓 UI 誤顯示為同一進度條。
  • 明細 listEvents 每筆必有 key_mode;篩選參數建議支援 key_mode=platform|byok|all(預設 all 但列表仍標示清楚)。

4.7.3 顯示契約前端Admin

畫面 必須
會員用量摘要 兩塊:① 方案點數(平台)② BYOK 本月次數(可按 meter 拆)
進度條/剩餘 只綁平台點數BYOK 用數字計數,不用同一條 bar 混算
事件明細 每列標示「平台/自己的 Key」
Admin 全體用量 平台消耗與 BYOK 次數 分欄或分圖,禁止單一「總呼叫」混加

4.7.4 為何

  • 經濟清楚:誰付錢、扣誰的額度。
  • 產品清楚:會員知道自帶 key 用了幾次,不被算進方案進度。
  • 營運可稽核:日後若對 BYOK 加服務費,已有獨立計數。

4.8 失敗通則

  • 未實作功能:非 2xx 成功空 data;回明確錯誤碼與 message。
  • Auth 失敗:不強制清前端所有 local 狀態的「假成功」;前端依錯誤處理(對齊既有精神)。

5. 介面契約

5.1 跨切

項目 契約
成功 envelope { "code": 102000, "message": "...", "data": ..., "error": null }(與 AGENTS.md 一致;若 stand-alone 既有成功碼不同,搬改時統一為 102000 並在 migrate 文件註明
失敗 code ≠ 成功;message 可讀;error 可帶細節;禁止 data 裝成功空列表假裝有實作
時間 全部 UTC unix nanoseconds int64
列表 請求 page(從 1pageSize;回應 pagination + list
Auth Authorization: Bearer <access_token>refresh 另端點
權限 Auth middleware 解析 uidAdmin middlewarepermission 檢查 admin
身分 對外 JSON 會員主鍵欄位名 uid 型別數字JSON number禁止字串 slug 主鍵

5.2 能力分組(對齊 apps/web repos · 路徑前綴建議 /api/v1

下列為 行為邊界;精確 path 可在 go-zero .api 微調,但 能力不得缺。未列 P2 可不做。

主要行為 權限
AuthAPI 齊全) login, logout, refresh, me, updateProfile, requestPasswordReset, resetPassword, sendEmailVerification, verifyEmailregister三方 OAuth 登入/綁定/解綁provider 清單可配置) 公開UI 可暫不暴露 register三方
Admin Members listPage, get, create, setSuspended, setEmailVerified, setRoles, resetPassword admin
Threads Accounts oauthStart, oauthCallback, list, refreshSession, disconnect member
Jobs list, get, createDemo依 template 建立 member
Notifications list, unreadCount, markRead, markAllRead member
Settings get/save Ai, get/save Placement, listModels(provider);回傳是否 platform_key_availablemember_key_configured無明文 member
Personas CRUD, setActive, analyzeText, analyzeAccount member
Plays list/get/save/remove, resolveExternalLink, submit, listByPost, listByExternalUrl member
Outbox list/get, retryStep, remove發送由 worker無「假成功」模擬當 live 完成條件) member
OwnPosts / Mentions / Compose ownPosts: list/sync/lastSyncedAt/generateReply/sendReply/analyzePostmentions: list/generateReply/markReplied/skipcompose: mimicanalyzeViral、publishSingle(+schedule)。不做 generateFromFormula member
Inspiration / Research / Media 僅現 UI trends(list/refresh)、elements CRUD、session(get/save/clear)、chat(chat|generate)research.searchgenerateImageattach 真上傳(持久 URL不做舊靈感 list/viral/bookmark/spark/searchTrends member
Usage summary, events含 key_mode, plan, purchase(mock pay), admin tenant summary/analytics, setMemberPrefs memberadmin
Scout brands/products CRUD, importProductFromUrl, prepareBrief, runScanFromBrief, posts, outreach*, homework*。不做 ScoutTopic CRUD現 UI 未用) member
Invite getMyNetwork, applyInviteCode, admin getTree/move/listParentCandidates memberadmin

5.3 前端對照live 必須可驅動)

前端區域 後端域
Login / Verify / Reset / Profile Auth
AdminUsers Admin Members + Usage prefs
Crew 帳號 Threads Accounts OAuth
Crew 人設 Personas
Studio 方案Wizard單篇排程 Plays, Compose, Outbox
Outbox 頁 Outbox + Worker
Jobs / 鈴鐺 Jobs, Notifications
Settings Settings + 擴充 ZIP靜態或 downloads API
Scout / Brands Scout
Invite Invite
Usage / Plans Usage

5.4 Job templateP0P1 最低)

Template 觸發 成功
demo 或健康檢查類 手動/設定 進度跑完 succeeded + 通知
persona_analyze_* 分析人設 persona 更新
threads_sync_posts 同步已發 own posts 更新
outbox_publish_step worker 輪詢 步驟 publishedfailed
scout_scan 海巡掃描 ScoutPost 寫入
inspireresearch 可選 長任務 結果落庫或回寫 session

6. 與現況對照

能力 Retain Replace Remove 備註
stand-alone-service go-zero 骨架 結構、middleware、module 模式 業務域改巡樓 電商 cart/order/product… 直接搬
stand-alone 多份 etc 收斂為一份 etc/gateway.yaml+ env gateway-dev/prod/nas 碎檔常態 決策 #9
stand-alone 數字/可產 uid 方向 數字 uid 對齊巡樓 Member 前端 mock 字串 uid 不當契約 決策 #10
stand-alone permissionadmin middleware 概念 收斂 memberadmin 電商權限雜訊 決策 #11
apps/web mock 行為 產品語意 live HTTP 實作 localStorage 當唯一真相 行為來源
apps/web mock user_* 字串 僅 mock live 用數字 uid 當 API 契約
old/backend 海巡OAuth發文 演算法參考 新 module 重寫 整包搬 old
Outbox 前端 simulate 按鈕 mock 可留 live 以 worker 真發為準 以 simulate 當 live 完成 決策 #3
舊靈感 list/viral/spark… Remove;只做現 UI chat 流 決策 2026-07-10
generateFromFormula / ScoutTopic Remove(現 UI 未用) 決策 2026-07-10
附圖 attachLocal mock data URL 真上傳 + 可取 URL 假上傳當完成 決策 #19
Insights 真管線 頁面 local 可留 Phase C 後端真管線 決策 #20 P2
Member uid JSON number FE toString 顯示 字串 slug 主鍵 決策 #10+#21
公開註冊 頁面 前端暫不開 決策 #1 UI
公開註冊/三方 API 完整實作 不做半套 API 齊、頁可後開
AI搜尋 key etc 平台 key 會員 BYOK 覆寫 僅會員 key、無平台 fallback 當唯一模式 §4.64.7
Mongo index 混在啟動 獨立 init 包 啟動時隱式 bulk 決策 #12
Seeder 混業務 獨立 seeder 包 與 init 合併 決策 #13

7. 非功能

  • 安全/權限: JWT 鑑權admin 路由雙重檢查provider API key 加密或至少與 JWT 分離存儲OAuth token 僅服務端。
  • 錯誤碼: 業務錯誤用穩定數字scope可沿 stand-alone FullCode 精神);成功 102000
  • 可觀測: Job progress_percentprogress_summaryOutbox step error 字串worker 日誌不含 key。
  • 效能邊界(初值): 列表預設 pageSize≤50Outbox worker 併發可配置海巡單次命中上限可配置spec 實作預設 50100
  • 設定: 單一 etc 檔secret 優先環境變數覆寫。

8. 資料保存(邏輯層)

實體 關鍵欄位 生命週期
Member uid(number), email, password_hash, roles[], status, email_verified, invite_code, parent_uid?, profile… admin 建→用→可停權
ThreadsAccount id, owner_uid, platform ids, tokens(server), connection, session_expires_at OAuth 建→刷新→解除
ThreadPlay id, owner_uid, steps[], schedule_start_at, status 草稿→提交
OutboxBundle/Step id, play_id, steps status, scheduled_at, error 至 completed取消
Job id, template_type, status, progress_*, error 終態保留可查
Notification id, uid, kind, ref_*, read_at 可累積
Persona / Brand / Product / Scout* owner_uid + 領域欄位 CRUD
Invite 邊 parent_uid on Member 補填或 admin 調
Platform secrets etcenvAI keys、Exa key 部署設定
AiSettings / Placement per uidprovider, model, member keys configured, dev_mode 覆寫更新
UsageEvent uid, meter, credits, key_mode, created_at platform 與 byok 分列
UsageMonthSummary platform 點數欄 + byok 次數欄(可 by meter 禁止混成唯一總次數
Plan prefs uid, plan_id, unlimited? 只約束 platform 扣點
Indexes 各集合查詢鍵 init 指令維護
Seed admin 帳、demo 選用 seeder 指令

9. 驗收場景Given / When / Then— 嚴格 PM 全量

通過標準: 下列每一條皆可自動化或手測重現;任一失敗=該域未過關。
共通斷言(凡成功回應皆適用,下稱「成功 envelope」
code=102000message 非空;時間欄位為 int64 unix ns UTC;列表含 paginationpage,pageSize,total,totalPages+ list絕不在未實作時回 200/102000 且 data 空成功。

共通斷言(失敗): code≠102000message 人類可讀;不得洩漏其他使用者密碼/明文 API keyOAuth token。


9.0 基礎設施/工程約束

ID Given When Then
A-01 倉庫交付 檢視後端專案 源自 stand-alone-service 改造go-zero .api+handler/logic/module 結構存在
A-02 交付物 etc/ 僅一份主設定檔(如 gateway.yaml);無多份 dev/prod/nas 常態碎檔
A-03 交付物 建置 存在 gateway獨立 worker 兩個可執行目標
A-04 乾淨 Mongo 只跑 init 必要 index 存在;強制插入業務種子會員
A-05 已 init 空業務 只跑 seeder 種子 admin或文件約定帳出現可重複執行不產生重複關鍵唯一鍵idempotent
A-06 gateway 啟動 不跑 seeder 服務可起、健康檢查 OK不依賴 seeder 才會 bind port
A-07 任意 API 成功 成功 envelope失敗非空成功
A-08 文件/契約 查會員主鍵 uid 為數字JSON numberuser_xxx 字串主鍵

9.1 Auth · 登入/會話/權限

ID Given When Then
AU-01 已 seeder 的有效帳密 login 正確 102000回 access+refreshbody 含數字 uid、roles
AU-02 有效帳 login 錯誤密碼 失敗;不洩漏「帳號是否存在」以外必要資訊(至少不回 token
AU-03 不存在 email login 失敗(與 AU-02 同形可接受)
AU-04 有效 access me 回當前會員uid 數字;與 login 一致
AU-05 無 Authorization me 401未授權類失敗
AU-06 過期 access me 失敗;回成功空 member
AU-07 有效 refresh refresh 新 access及可選新 refresh舊 access 策略文件化(作廢或並存短時)
AU-08 無效 refresh refresh 失敗
AU-09 已登入 logout 成功;之後用原 access me 失敗(或等同登出語意)
AU-10 suspended 會員 login 失敗message 表明停權
AU-11 曾登入後被停權 me 或受保護 API 失敗/拒絕
AU-12 非 admin 任一 admin-only API 權限失敗
AU-13 admin 同一 admin API 可成功(其餘條件滿足時)
AU-14 access 屬於 uid=A 讀改 uid=B 的私有資源 失敗(除非 admin 且 API 允許)

9.2 Auth · 註冊 API頁面可不存在

ID Given When Then
AR-01 註冊 API 已部署 文件OpenAPI.api 端點存在,可被整合測試呼叫
AR-02 合法 email+密碼 POST 註冊 102000得數字 uid預設角色含 member有 invite_code
AR-03 email 已存在 再註冊 失敗
AR-04 密碼過短/非法 註冊 失敗,可讀原因
AR-05 註冊成功且需驗證 未驗證 login 進主功能 對齊閘:主功能拒或僅允許驗證流
AR-06 註冊後 用回傳或流程寄碼→verify email_verified=true
AR-07 錯誤驗證碼 verify 失敗verified 仍 false
AR-08 過期驗證碼 verify 失敗
AR-09 前端無註冊頁 只打 API 仍可完整走完 AR-02AR-06API 驗收不依賴 UI

9.3 Auth · 重設密碼/改密/改資料

ID Given When Then
AP-01 已知 email requestPasswordReset 一律成功形(或不洩漏是否存在);有 token 機制
AP-02 有效 reset token resetPassword 新密碼 成功;舊密碼 login 失敗;新密碼 login 成功
AP-03 過期/用過 token resetPassword 失敗
AP-04 已登入 updateProfile 改 display_name 成功me 反映
AP-05 已登入 改 email 成功後 email_verified=false或文件約定需重驗證
AP-06 改密 錯 current_password 失敗
AP-07 改密 正確 current + 合法 new 成功;新密碼可登
AP-08 頭像 合法圖上傳data 成功;非法格式/過大失敗

9.4 Auth · 三方登入 API頁面可不開

ID Given When Then
AO-01 配置至少一 provider 開始三方登入 回授權 URL 或等價
AO-02 首次三方成功 callback 完成 新建 Member 數字 uid可發 JWT
AO-03 同三方帳再次登入 callback 同一 uid,不重複建檔
AO-04 已 email 登入會員 綁定三方 綁定成功;之後可用三方登
AO-05 解綁 呼叫解綁 成功後不可再用該三方登(除非再綁)
AO-06 三方失敗/拒權 callback 失敗可讀;不建半套會員

9.5 Admin 島民管理

ID Given When Then
AD-01 admin createMember 合法 數字 uid臨時密碼僅此次回應DB 有密碼雜湊
AD-02 admin create 重複 email 失敗
AD-03 admin listPage page=1 pagination 正確list 元素含數字 uid
AD-04 admin query 名稱emailuid 只回匹配列
AD-05 admin setSuspended true 該帳 login 失敗
AD-06 admin setSuspended false 可再 login
AD-07 admin 停權自己 失敗
AD-08 系統僅一 active admin 停權或降級該 admin 失敗(至少保留一位)
AD-09 admin setRoles 加 admin 成功
AD-10 admin setEmailVerified 狀態與 verified_at 正確
AD-11 admin resetPassword 無自訂 回新臨時密碼;舊密失效
AD-12 admin resetPassword 指定密 該密可登
AD-13 member createMember 權限失敗
AD-14 admin getUser 存在/不存在 有則完整;無則 404 形失敗

9.6 Threads OAuth 帳號

ID Given When Then
TH-01 已登入 oauthStart 回授權 URLstate 可驗證
TH-02 合法 callback 換票成功 帳號列表多一筆connection=connectedis_usable=truetoken 不在 JSON 明文
TH-03 使用者拒絕授權 callback 失敗/無 usable 帳;不寫假 connected
TH-04 換票失敗 callback failed 語意;可讀錯誤
TH-05 已綁帳號 list 屬當前 uid含 session 過期欄位語意
TH-06 connected 帳 refreshSession 成功 更新 session_expiresrefreshed仍 usable
TH-07 refresh 失敗 refreshSession 標記 error不可用不假裝成功
TH-08 已綁 disconnect 列表不再 usable不可再當 lead 發送
TH-09 未 OAuth 只「登記」 (若有旁路) P0 不得靠旁路算驗收通過;驗收以 TH-02 為準
TH-10 他會員帳號 id 我方 refresh/disconnect 失敗

9.7 Job 與通知

ID Given When Then
JB-01 已登入 建立 demo Job status 從 pending/queued 可查
JB-02 Job running worker 推進 progress_percent 單調不亂跳summary 更新
JB-03 Job 成功 終態 status=succeededcompleted_at 有值
JB-04 Job 失敗 終態 status=failederror 非空
JB-05 Job 成功/失敗 通知 對應 uid 有 Notificationunread+1
JB-06 有未讀 unreadCount 數字正確
JB-07 未讀通知 markRead read_at 有值unread-1
JB-08 多則未讀 markAllRead unread=0
JB-09 他 uid 的 job 我 get 失敗
JB-10 非法狀態跳轉 強改(若有內部 API guarded 拒絕

9.8 設定 · AIExadev · 平台 keyBYOK

ID Given When Then
ST-01 已登入 getAi 有 provider/modelapi_key_configured 布林;明文 key
ST-02 已登入 saveAi 換 provider 成功research_* 與 provider/model 一致
ST-03 provider=xai listModels 回該 provider 清單非空
ST-04 換 provider listModels預設 model 同名保留否則第一個
ST-05 etc 有平台 AI key、會員未填 任 AI 呼叫成功 key_mode=platform扣 credits;平台次數+1BYOK 次數不變
ST-06 會員 save AI key getAi configured=true仍無明文
ST-07 會員已填 AI key AI 呼叫成功 key_mode=byokcredits=0;方案剩餘不變;BYOK 次數+1;平台 used 不變
ST-08 會員清除 AI key 再 AI 回 platform若 etc 有)
ST-09 etc 無 AI 且會員無 AI 呼叫 失敗可讀;不假成功
ST-10 etc 有 Exa、會員未填 搜尋成功 platform 扣點;平台搜尋次數+1
ST-11 會員填 Exa key 搜尋成功 byok不扣點BYOK 搜尋次數+1
ST-12 AI byok + 搜尋 platform 同流程 各記 event 平台只扣搜尋BYOK 只加 AI 次;摘要兩組分開
ST-13 platform 額度不足 AI 呼叫 拒絕、不打外部(預設)
ST-14 getPlacement Exa 語意dev_mode 布林;無明文 key
ST-15 savePlacement dev_mode true/false get 值持久
ST-16 擴充 ZIP GET 下載 可下載非空 zip靜態或 API

9.9 用量與方案

ID Given When Then
USG-01 新會員 getSummary plan 預設合理;platform 剩餘byok 次數=0 分欄
USG-02 僅 platform 消耗後 getSummary platform used↑ remaining↓byok 次數不變
USG-03 僅 BYOK 消耗後 getSummary platform remaining 不變byok_call_count↑(可按 meter
USG-04 混合AI byok + 搜尋 platform getSummary 平台只扣搜尋點BYOK 只加 AI 次數;兩組數字各自正確
USG-05 listEvents 每筆含 meter、credits、key_mode、時間 ns
USG-06 listEvents 篩 key_mode=byok byok 列;無 platform 列
USG-07 listEvents 篩 platform 僅 platform 列
USG-08 回傳 summary JSON 檢查 禁止單一 total_calls 把 platform+byok 加總當唯一欄位(若有 grand total 必須另標且文件化;預設不要)
USG-09 前端用量頁契約 對照 API 點數條只吃 platformBYOK 另區顯示次數
USG-10 admin tenant analytics 平台消耗與 BYOK 次數 分欄/分序列,不可混成一條
USG-11 member purchasePlan假付款 plan 變更purchase 紀錄
USG-12 admin setMemberPrefs plan 該 uid 平台方案變
USG-13 admin unlimited platform 不因額度拒byok 計數仍獨立累加
USG-14 member set 他人 prefs 失敗

9.10 人設 Persona

ID Given When Then
PE-01 已登入 create/save 得 idlist 可見
PE-02 多則 setActiveId getActive 為該 id
PE-03 刪除 active remove active 重設規則明確(空或另一則)
PE-04 analyzeFromText 成功 status/ready 欄位更新;可落庫
PE-05 analyzeFromAccount 成功或 Job 結果可查;失敗 Job failed+通知
PE-06 他 uid 人設 我改刪 失敗

9.11 互回方案 Play 與單篇 Compose

ID Given When Then
PL-01 合法 lead+steps save 持久get 一致
PL-02 reply 帳號不在 speakers submit 失敗
PL-03 合法 play + schedule_start_at 未來 submit Outbox 建立step.scheduled_at 對齊
PL-04 resolveExternalLink 合法 URL 回 target 預覽欄位
PL-05 非法 URL resolve 失敗
PL-06 compose 單篇 + schedule 未來 publishSingle Outbox 一 stepscheduled_at=指定時間
PL-07 compose 不傳 schedule publishSingle scheduled_at≈now允許小誤差
PL-08 無可用帳號 publish/submit 失敗
PL-09 刪 play remove list 不再見;已提交 outbox 策略明確(保留或級聯文件化)
PL-10 已有綁 ownPost 的 play listByPost 只回該 post 的方案
PL-11 已 resolve 外部 URL listByExternalUrl 只回該 URL 正規化後的方案
CP-01 已登入+人設 compose.mimic 回仿寫正文platform/byok 計費適用
CP-02 已登入 compose.analyzeViral 回結構分析欄位;計費適用

9.12 Outbox 真發送Worker

ID Given When Then
OB-01 step 未到 scheduled_at worker tick 呼叫 Threads狀態仍 scheduled
OB-02 step 已到點且 token 有效 worker publishing→publishedThreads 側可見(或 sandbox 可證)
OB-03 Threads API 失敗 worker step=failederror 非空; published
OB-04 root failed 查 bundle 後續 reply 皆非 publishedblocked/scheduled 等)
OB-05 root 成功 reply 到點 worker reply published
OB-06 failed step 可重試 retryStep 再進 publishing成功則 published
OB-07 不可重試狀態 retryStep 失敗
OB-08 全部成功 bundle status=completed
OB-09 部分失敗 bundle partial_failed
OB-10 remove bundle list 不可見worker 不再送
OB-11 他 uid outbox 我 get/retry 失敗
OB-12 live 環境 僅靠前端 simulate 不算 OB-02 驗收通過

9.13 已發貼文/提及

ID Given When Then
OP-01 usable 帳 sync 列表更新lastSyncedAt 更新
OP-02 sync 失敗 錯誤可讀;不清空當成功
OP-03 有貼文 generateReply 回文字platform 或 byok 計費規則適用
OP-04 sendReply 成功 Threads 可見;本地狀態已回
OP-05 sendReply 失敗 本地不標已送成功
OP-06 mention markReplied / skip 狀態正確
OP-07 他帳 accountId sync 失敗
OP-08 有貼文 analyzePost 分析欄位更新platform/byok 計費適用
OP-09 有 mention list可篩 account 契約完整;時間 ns
OP-10 有 mention generateReply 回文字草稿;計費適用
OP-11 generateFromFormula 不提供Remove呼叫應 404未實作失敗不得假成功

9.14 靈感/研究/媒體

範圍: 僅現 UIInspirePanel + Today 趨勢)。舊 listviralbookmarksparksearchTrends 不做、不驗收

ID Given When Then
IN-01 listTrends 有 list可空但契約完整
IN-02 refreshTrends 更新或明確錯誤
IN-03 save/remove element CRUD 正確
IN-04 chat mode=chat session 寫入 messages
IN-05 chat mode=generate draft.body 乾淨正文可取
IN-06 research.search platform/byok 計費規則同 §9.8
IN-07 generateImage 回 url計費規則適用
IN-08 attach/upload真上傳 合法檔 multipart或等價 回持久 url+id非法過大失敗禁止僅回本機 data URL 當 live 完成
IN-09 未實作上傳 呼叫 102000+空成功

9.15 海巡 Scout

ID Given When Then
SC-01 已登入 createBrand/saveProduct 持久 list 可見
SC-02 刪 brand 所屬 product 策略明確(級聯或拒刪)
SC-03 prepareBrief 有 intent 回 brief含 scan_terms 可改
SC-04 intent 空 prepareBrief 失敗
SC-05 dev_mode=false runScanFromBrief 走 API 路徑log/欄位 scan_path=api 可證)
SC-06 dev_mode=true 且 session 可用 runScan 走 crawlerscan_path=crawler
SC-07 dev_mode=true 但無 session runScan 失敗可讀或明確降級策略(文件+實作一致)
SC-08 掃描成功 listPosts 有命中;時間 ns
SC-09 draftOutreach 草稿文字寫入
SC-10 skip / markPublished 狀態對
SC-11 sendOutreach 真送 對齊發送契約;失敗不假 published
SC-12 removePost / removeTheme 列表消失
SC-13 homework save/get/list/remove CRUD 正確
SC-14 importProductFromUrl 合法 回填草稿欄位;非法 URL 失敗

9.16 邀請 Invite

ID Given When Then
IV-01 新會員 getMyNetwork 有 invite_codeparent 空或 seeder 值
IV-02 無 parent apply 合法碼 parent=邀請人 uidupline 可見
IV-03 已 parent apply 任意碼 失敗 alreadyBound
IV-04 apply 自己的碼 失敗
IV-05 apply 不存在碼 失敗
IV-06 apply 空碼 失敗
IV-07 有直邀 getMyNetwork downlines 正確;計數一致
IV-08 admin getTree 森林結構完整
IV-09 admin move 到合法父 parent 更新;子孫跟著
IV-10 admin move 到自己子孫下 失敗防環
IV-11 admin move 到自己 失敗
IV-12 member moveMember 權限失敗
IV-13 admin listParentCandidates(排除子樹) 不含該子樹節點

9.17 契約與安全紅線

ID Given When Then
X-01 任一未實作路徑 呼叫 102000+空 data
X-02 成功回應 檢查 無 password、無 raw api key、無 oauth access_token 欄位
X-03 worker/gateway log 打 key 的請求 log 明文 key
X-04 列表 page=0 或負 正規化或 400行為一致
X-05 pageSize 過大 cap 到上限(如 50
X-06 時間欄位 抽樣 皆 ns 量級 int64非秒、非 ms 混用)

9.18 驗收執行約定(嚴格 PM

  1. P0 過關: §9.0 + §9.1 + §9.3 + §9.5 + §9.6 + §9.7 + §9.8ST-0116 中與 P0 相關)+ §9.17 全過§9.2§9.4 API 存在性可測。
  2. P1 過關: P0 全過 + §9.9§9.16 全過(含 OB 真發送、SC 雙路徑、IV
  3. 每條 ID 必須在測試清單有對應 case 名稱(自動化或手測腳本表);禁止「大概可以」。
  4. 外部依賴ThreadsAI可用 契約測試 + 受控沙箱;但 OB-02TH-02 不得用「只改 DB 狀態」冒充。

10. 開放規格問題

  1. 成功碼若 stand-alone 現況≠102000搬遷時統一策略建議統一 102000)。
  2. OAuth callback 網域與 state 儲存RedisDB細節。
  3. 爬蟲 session 與 gateway 的傳遞安全(僅 dev_mode
  4. 目錄落點:apps/backendgateway + cmd/worker 等同 monorepo 根;已定)。
  5. BYOK 是否要加「平台服務費」(每呼叫固定點)— 預設否;僅記 event日後可加。
  6. 三方登入支援哪些 provider 第一版GoogleAppleplan 列最低集合API 介面先通用。

11. 批准

  • Spec 已批准2026-07-10 / 使用者「spec 通過」):