◆ wport | pm_43

求職者收藏與追蹤中心(Saved & Alerts)Mockup PRD(給後端 API 規格產生器使用)

求職者收藏與追蹤中心

來源 doc/feature/pm_43/job-seeker-saved-center-prd.md

求職者收藏與追蹤中心(Saved & Alerts)Mockup PRD(給後端 API 規格產生器使用)

整合三項原始需求:收藏公司、收藏職缺、Saved Filter(儲存搜尋條件)。 產出 persona:CEO with PM Background(SaaS / HR tech lens)。


1. 文件資訊


2. 開發進度與設計來源

開發進度

  • 前端 PR #:[TBD] 後端 PR #:[TBD]

設計來源

  • Figma:[待補上]
  • 既有畫面:求職者會員中心框架;職缺/公司搜尋頁(Saved Filter 由搜尋條件衍生)

3. 模擬頁面摘要

3.1 功能概述

  • 功能名稱:求職者收藏與追蹤中心
  • 主要使用者角色:Job Seeker
  • 核心目標
    • 讓求職者建立「想追蹤的公司 / 職缺」watchlist,並把常用搜尋條件存成 Saved Filter(v1:儲存 / 套用 / 改名 / 刪除)。
    • v2:透過 Saved Filter 新職缺通知(email / 推播)創造回訪與再行銷,呼應「平台通知與 Email」「推播中心」。
    • 為企業端鋪「被收藏數」雇主品牌訊號(未來付費洞察的資料基礎)。

3.2 範圍界定

  • 包含範圍(v1)
    • 收藏 / 取消收藏:職缺、公司(愛心 toggle;含失效項仍顯示愛心,再點即取消)。
    • 收藏清單(職缺 tab、公司 tab),含空狀態與失效項目 boundary。
    • Saved Filter:把職缺搜尋條件儲存、命名、改名、套用、刪除(上限 10 組,免費)。
  • 排除範圍
    • v1 不做:Saved Filter 新職缺通知、email digest、推播、new_match_count badge(整包延後 v2,見 §4.2 v2 預留)。
    • 收藏分類 / 資料夾(v2)。
    • 通知中心本體(另案:推播中心 / 平台通知與 Email)。
    • 企業端「被收藏數洞察」(另案,B 端付費)。

3.3 成功條件(v1)

  • 收藏 toggle 在搜尋頁 / 詳情頁 / 收藏中心三處狀態一致(樂觀更新 + 失敗回滾)。
  • Saved Filter 可儲存、改名、一鍵套回搜尋、刪除;達 10 組上限時禁用新增。
  • 失效項目(職缺下架 / 公司轉為不完整)在清單以 boundary 呈現,不死連結;仍用愛心 toggle 取消收藏。
  • 封鎖公司時自動移除該公司收藏及其旗下職缺收藏;已封鎖公司依 BR-027 不可見,無法觸發收藏。

4. 商業規則對齊

4.1 本功能使用到的業務規則

規則 ID規則摘要是否關鍵備註
BR-014公司搜尋只顯示資料完整公司;不完整公司詳情頁 404✅ 是收藏公司清單只列可見公司;若收藏後變不完整 → boundary 標示「暫不可瀏覽」
BR-015職缺搜尋只顯示上架職缺✅ 是收藏職缺若下架/關閉(BR-009)→ boundary;Saved Filter 結果僅含上架職缺
BR-009職缺狀態✅ 是收藏職缺終態處理
BR-001登入後功能✅ 是收藏中心為登入後頁面
BR-027封鎖雙向隱藏✅ 是已封鎖公司不出現於搜尋/推薦;無收藏入口(見 §4.2)
BR-018Email 發送規則與類型v2(Saved Filter 通知;v1 不做寄信)

4.2 補充說明(已裁決,回寫 business-rules.md v1.6.2)

  • BR-024(收藏 ⇄ 封鎖互斥):求職者收藏某公司與封鎖某公司互斥。執行封鎖(pm_44)時自動移除對該公司的公司收藏,以及對該公司旗下所有職缺之收藏。封鎖確認 Modal 若偵測已收藏(公司或旗下職缺),提示「將同時移除相關收藏」。
  • BR-027 × 收藏入口:已封鎖公司及其職缺依 BR-027 不出現於職缺/公司搜尋、推薦與可瀏覽入口,使用者無法觸發對已封鎖公司的收藏 toggle(非 API 拒絕路徑;預防性隱藏)。
  • BR-025(Saved Filter 上限):每帳號 Saved Filter 上限 10 組(v1 免費,不分級);v2 再評估付費方案擴充上限。僅含上架職缺條件語意(對齊 BR-015)。
  • BR-026(收藏可見性):收藏清單顯示目前仍可見與失效項;失效項保留收藏關係但以 boundary 呈現,使用者透過愛心 toggle 取消收藏(無獨立移除鈕)。

4.2.1 v2 預留(本期不做寄信/通知)

以下已裁決於 business-rules.md,實作延後 v2;PRD 保留語意供 RD 規劃,不列入 v1 驗收

  • BR-025 通知頻率(v2):所有「開啟通知」的 Saved Filter,新符合職缺合併為單封 digest每帳號每日最多 1 封(每條件一區塊)。per-filter 可設 每日 / 每週 / 關閉(預設每日);weekly 僅在帳號每週 digest 日納入彙整。
  • Email 模板(v2):主旨 你追蹤的 {m} 個條件共有 {n} 個新職缺(單條件則帶 {filter_name});內文每條件一區塊、最多 5 筆新職缺 + CTA;含「管理通知頻率」連結。

5. 資料實體與欄位

5.1 實體列表

實體名稱說明來源頁面/區塊
SavedJob收藏的職缺(user↔job 綁定 + 收藏時間 + 失效狀態)SEC-1 職缺 tab
SavedCompany收藏的公司(user↔company 綁定)SEC-2 公司 tab
SavedFilter儲存的搜尋條件(命名 + 條件快照)SEC-3 Saved Filter
SavedFilterMatchSaved Filter 比對到的新職缺(v2 通知用)v2 / Email

5.2 TypeScript 介面(語意用途)

唯一性(v1):同一使用者對同一職缺(或同一公司)僅能有一筆收藏綁定;重複收藏為 idempotent,不重複建檔。

interface SavedJob {
  enc_id: string;  // 收藏綁定自身識別;v1 API 可不暴露(取消收藏以 job.enc_id 為準)
  job: {
    enc_id: string;
    title: string;
    company_name: string;
    company_logo_url?: string;
    area_name?: string;
    lifecycle_code: 'published' | 'unpublished' | 'closed' | 'deleted'; // BR-009
  };
  saved_at: string;       // ISO8601
  is_available: boolean;  // 由 lifecycle 推導;false 顯示 boundary
}

interface SavedCompany {
  enc_id: string;  // 同 SavedJob,v1 API 可不暴露(取消收藏以 company.enc_id 為準)
  company: {
    enc_id: string;
    name: string;
    logo_url?: string;
    is_visible: boolean;  // 對齊 BR-014(資料完整才可見)
    open_job_count?: number;
  };
  saved_at: string;
}

interface SavedFilter {
  enc_id: string;
  name: string;                 // 使用者命名(必填,≤ 30 字);可改名(ACT-8);名稱允許重複(enc_id 為唯一識別)
  criteria: JobSearchCriteria;  // 搜尋條件快照(語意,不規定序列化)
  created_at: string;
}

// 條件快照:欄位以「後端職缺搜尋 API 現行 16 個篩選欄位」為準(BR-015),
// 命名、型別一字不差,FE 搜尋頁 query state 可原樣存入 / 套回,不做兩套詞彙。
interface JobSearchCriteria {
  keyword?: string;
  area_codes?: string[];
  job_classification_codes?: string[];
  work_experience_code?: string;        // 單值 enum(非陣列)
  remote_mode_codes?: string[];
  work_hours_type_codes?: string[];
  week_rest_policy_codes?: string[];
  compensation_schemes_code?: string;   // 薪資方案(扁平欄位,非巢狀 salary 物件)
  salary_unit_code?: string;
  min_salary?: number;
  max_salary?: number;
  salary_currency_code?: string;        // ISO 4217
  job_feature_codes?: string[];
  identity_type_codes?: string[];
  work_identity_type_codes?: string[];
  language_requirements?: string[];     // "language_code,proficiency_level_code"
}

5.3 v2 預留欄位(本期不實作)

// v2:通知與站內 badge(見 §4.2.1)
interface SavedFilterV2 extends SavedFilter {
  notify_enabled: boolean;      // = notify_frequency !== 'off'
  notify_frequency: 'daily' | 'weekly' | 'off';
  new_match_count: number;      // 套用該條件至搜尋頁時歸零
}

6. 畫面區塊與資料需求

6.1 區塊列表

區塊 ID區塊名稱說明
SEC-1收藏職缺 tabSavedJob 清單,含失效 boundary
SEC-2收藏公司 tabSavedCompany 清單
SEC-3Saved Filter tab已存條件清單 + 套用 / 改名 / 刪除
SEC-4收藏 toggle(嵌入元件)出現在搜尋卡片 / 職缺詳情 / 公司頁的愛心/書籤
SEC-5空狀態各 tab 無資料時 CTA(去找工作 / 去找公司 / 去搜尋)

6.2 各區塊資料需求

SEC-1 收藏職缺 tab

  • 顯示實體SavedJob[];分頁、排序(saved_at desc)
  • 失效處理is_available=false 時灰態顯示「此職缺已下架/關閉」boundary;愛心維持實心已收藏態(品牌色),點擊即取消收藏(ACT-1)

SEC-2 收藏公司 tab

  • 顯示實體SavedCompany[]is_visible=false 顯示「暫不可瀏覽」boundary(BR-014);取消收藏同樣走愛心 toggle(ACT-2)

SEC-3 Saved Filter tab

  • 顯示實體SavedFilter[]
  • 操作(v1):套用(帶條件回搜尋頁)、改名、刪除(新增僅能從找工作搜尋頁「儲存搜尋條件」啟動
  • 上限:達 10 組時「新增」禁用並提示(BR-025)

SEC-4 收藏 toggle(共用元件)

  • 行為:樂觀更新;未登入點擊 → 導登入並保留意圖(登入後自動完成收藏)
  • 一致性:同一資源在多處的收藏狀態需同步(前端 store 單一真相)
  • 封鎖:已封鎖公司及其職缺不出現於可收藏入口(BR-027),不提供收藏 toggle

7. 使用者動作與後端需求

動作 ID動作名稱類型需要後端涉及實體說明
ACT-1收藏/取消職缺寫入SavedJobsave / unsave 成對端點(UI 為愛心 toggle),各自 idempotent;(user, job) 唯一
ACT-2收藏/取消公司寫入SavedCompanysave / unsave 成對端點,各自 idempotent;(user, company) 唯一;與封鎖互斥(BR-024);已封鎖公司依 BR-027 不可見、無收藏入口
ACT-3載入收藏清單查詢SavedJob/Company分頁
ACT-4儲存 Saved Filter寫入SavedFilter檢核 ≤ 10 組
ACT-5套用 Saved Filter查詢JobSearchCriteria不新增搜尋端點:Saved Filter 清單 API 回完整 criteria,由前端帶條件打既有職缺搜尋 API
ACT-7刪除 Saved Filter寫入SavedFilter
ACT-8重新命名 Saved Filter寫入SavedFilter名稱必填、≤ 30 字;前後端驗證

8. API Hints

8.1 Read

  • HINT-GET-1:收藏職缺清單(分頁、含 is_available)→ SEC-1
  • HINT-GET-2:收藏公司清單(含 is_visible)→ SEC-2
  • HINT-GET-3:Saved Filter 清單 → SEC-3

8.2 Write

  • HINT-MUTATION-1:收藏 / 取消收藏職缺與公司採 save / unsave 成對端點(各自 idempotent;(user, resource) 唯一;「toggle」僅為 UI 愛心行為);封鎖連動移除由 pm_44 封鎖 API 處理(BR-024)
  • HINT-MUTATION-2:建立 Saved Filter(後端檢核上限 10)
  • HINT-MUTATION-3:Saved Filter 改名 / 刪除

8.3 v2 預留(本期不做)

  • HINT-JOB-1(背景):Saved Filter 比對排程 → digest email/推播(BR-025,見 §4.2.1)

9. 導航 / Storybook Map

  • Storybook 標題Mockups/求職者/收藏與追蹤中心
  • Stories 路徑storybook/src/stories/mockups/user/SavedCenter.stories.ts
  • 情境控制(建議 args)tab(jobs/companies/filters)、scenario(empty / has-items / has-invalid / filter-limit-reached / not-logged-in)
  • 共用元件 storySavedToggle(愛心/書籤)isolation

10. Flowcharts

10.1 User Flow

flowchart TD
    A[搜尋頁/職缺頁/公司頁] --> B{點收藏?}
    B -- 未登入 --> C[導登入, 保留收藏意圖]
    C --> D[登入後自動完成收藏]
    B -- 已登入 --> E[樂觀更新 toggle]
    E --> F{後端成功?}
    F -- 否 --> G[回滾 + 錯誤提示]
    H[收藏中心] --> I[切 tab: 職缺/公司/Saved Filter]
    I --> J{Saved Filter?}
    J -- 是 --> K[套用條件回搜尋 / 改名 / 刪除]
    J -- 否 --> L[清單; 失效項 boundary + 愛心 toggle 取消收藏]
    K --> M{新增且已達10組?}
    M -- 是 --> N[提示上限, 禁用新增]

10.2 System Flow(Saved Filter 通知 — v2,本期不做

sequenceDiagram
    participant Cron as 背景排程
    participant BE as Backend
    participant Mail as Email/推播
    participant User
    Cron->>BE: 比對各 SavedFilter 與新上架職缺
    BE->>BE: 計算 new_match_count (僅上架職缺, BR-015)
    alt notify_enabled 且有新符合
        BE->>Mail: 發送彙整通知(每日最多1封)
        Mail-->>User: Email/推播(CTA 帶 saved_filter token)
        User->>BE: 點 CTA -> /jobs?saved_filter=enc_id
        BE-->>User: 套用條件之搜尋結果
    end

10.4 Edge Case Coverage

類別情境系統行為UI 回饋可重試資料一致性策略備註
Network & Performance愛心連點save / unsave 各自 idempotent;前端鎖定即時切換No同一 (user,resource) 唯一
Data & Input重複收藏同一職缺/公司idempotent;不重複建檔愛心狀態不變No(user, resource) 唯一PRD §5.2
Network & Performancetoggle 失敗回滾錯誤 toastYes以後端為準樂觀更新
Data & InputSaved Filter 名稱空白/超長前後端驗證顯示限制(≤30字)N/AN/A
Data & Input達 10 組上限後端拒絕提示上限(不分級收費)N/AN/ABR-025
User Interruption未登入收藏後跳登入再返回保留意圖並完成「已為你收藏」Yes意圖去重避免重複
Auth & LifecycleSession 過期導登入回跳提示Yes
Logical Inconsistency收藏職缺已下架is_available=false灰態 boundary + 愛心 toggle 取消N/A保留關係至取消BR-009
Logical Inconsistency收藏公司變不完整is_visible=false「暫不可瀏覽」+ 愛心 toggle 取消N/A保留關係至取消BR-014
Logical Inconsistency收藏後封鎖同公司自動移除公司收藏及該公司旗下職缺收藏封鎖 Modal 提示「將同時移除相關收藏」N/A連動清除BR-024
Logical Inconsistency已封鎖公司嘗試收藏不適用(BR-027 已隱藏入口)無收藏 toggle 可點N/A預防性隱藏BR-027

11. Mock Data Schema

  • 資料來源:僅 mock。
  • CRUD 行為:toggle / 新增 / 改名 Saved Filter 僅元件狀態,重整還原。
  • 情境切換scenario 控 empty / has-items / has-invalid / filter-limit-reached / not-logged-in。

12. 實作備註

  • 禁止事項:❌ 不定義最終 API;BR-024/025/026 已回寫 business-rules.md v1.6.2。
  • 注意事項:C 端 → user CSS profile;收藏狀態用單一 store 同步多處;Saved Filter criteria 命名對齊職缺搜尋(BR-015 十大類),避免兩套詞彙。

12.1 四致性表

欄位對照表

欄位語意SoT版本
SavedJob.is_available職缺是否仍可瀏覽BR-009 + PRDv1
SavedCompany.is_visible公司是否可見BR-014v1
SavedFilter.name條件名稱(可改名)PRDv1
SavedFilter.notify_enabled通知開關BR-025v2
SavedFilter.notify_frequencyper-filter 頻率BR-025v2
SavedFilter.new_match_count新符合數 badgePRDv2

常數表

常數SoT版本
Saved Filter 上限10 組(v1 免費)BR-025v1
名稱長度≤ 30 字PRDv1
收藏唯一性每 (user, job/company) 一筆PRDv1
通知頻率上限帳號每日 1 封彙整BR-025v2
per-filter 頻率每日 / 每週 / 關閉BR-025v2
Email 列出新職缺數最多 5 筆PRDv2
Saved Filter 付費擴充上限TBDPRDv2

事件字典

事件觸發PayloadSoT版本
saved_toggle收藏/取消{resource_type, resource_enc_id, on}PRDv1
saved_filter_created建立條件{filter_enc_id}PRDv1
saved_filter_renamed改名{filter_enc_id, name}PRDv1
saved_filter_applied套用條件{filter_enc_id}PRDv1
saved_filter_notify_clicked點 email CTA{filter_enc_id, match_count}PRDv2

術語字典

術語定義SoT版本
收藏user↔resource 正向綁定PRDv1
Saved Filter命名化的搜尋條件快照PRDv1
新符合Saved Filter 自上次套用後新增的上架職缺(v2BR-015 + PRDv2

12.2 版本凍結表

項目v1/v2Blocking?原因決策者日期
收藏職缺/公司 togglev1MVPEric2026/06/23
Saved Filter(儲存/套用/改名/刪除)v1MVPEric2026/07/03
Saved Filter 通知(email/推播/badge)v2v1 明確不做寄信Eric2026/07/03
收藏分類資料夾v2非核心Eric2026/06/23
即時通知(vs 每日彙整)v2先做每日彙整Eric2026/06/23
Saved Filter 上限 10 組(免費)v1BR-025Eric2026/06/23
Saved Filter 付費擴充上限v2付費用戶可儲存更多組Eric2026/07/03

12.3 PRD 問題清單

#章節/規則問題建議修正類別影響SoT
1BR-025Saved Filter v1 免費 10 組 — 已裁決無(已定案)商業BR-025
2§4.2.1 通知v1 不做寄信;v2 與推播中心對齊已收斂至 §4.2.1 / §12.2相依PRD
3BR-024封鎖連動移除範圍與提示已裁決:移除公司收藏 + 旗下職缺收藏;Modal 提示「將同時移除相關收藏」商業BR-024

13. 下一步

  • v2:與「推播中心 / 平台通知與 Email」對齊 Saved Filter 通知通道與 digest 頻率(§4.2.1)。
  • v2:評估 Saved Filter 付費擴充上限(v1 維持免費 10 組)。
  • Coda:新增列 求職者收藏與追蹤中心,PRD 欄=pm_43(review 後執行)。

文件結束