求職者收藏與追蹤中心(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-018 | Email 發送規則與類型 | — | 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 |
| SavedFilterMatch | Saved 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 | 收藏職缺 tab | SavedJob 清單,含失效 boundary |
| SEC-2 | 收藏公司 tab | SavedCompany 清單 |
| SEC-3 | Saved 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 | 收藏/取消職缺 | 寫入 | ✅ | SavedJob | save / unsave 成對端點(UI 為愛心 toggle),各自 idempotent;(user, job) 唯一 |
| ACT-2 | 收藏/取消公司 | 寫入 | ✅ | SavedCompany | save / 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)
- 共用元件 story:
SavedToggle(愛心/書籤)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 & Performance | toggle 失敗 | 回滾 | 錯誤 toast | Yes | 以後端為準 | 樂觀更新 |
| Data & Input | Saved Filter 名稱空白/超長 | 前後端驗證 | 顯示限制(≤30字) | N/A | N/A | |
| Data & Input | 達 10 組上限 | 後端拒絕 | 提示上限(不分級收費) | N/A | N/A | BR-025 |
| User Interruption | 未登入收藏後跳登入再返回 | 保留意圖並完成 | 「已為你收藏」 | Yes | 意圖去重避免重複 | |
| Auth & Lifecycle | Session 過期 | 導登入回跳 | 提示 | 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 + PRD | v1 |
| SavedCompany.is_visible | 公司是否可見 | BR-014 | v1 |
| SavedFilter.name | 條件名稱(可改名) | PRD | v1 |
| SavedFilter.notify_enabled | 通知開關 | BR-025 | v2 |
| SavedFilter.notify_frequency | per-filter 頻率 | BR-025 | v2 |
| SavedFilter.new_match_count | 新符合數 badge | PRD | v2 |
常數表
| 常數 | 值 | SoT | 版本 |
|---|
| Saved Filter 上限 | 10 組(v1 免費) | BR-025 | v1 |
| 名稱長度 | ≤ 30 字 | PRD | v1 |
| 收藏唯一性 | 每 (user, job/company) 一筆 | PRD | v1 |
| 通知頻率上限 | 帳號每日 1 封彙整 | BR-025 | v2 |
| per-filter 頻率 | 每日 / 每週 / 關閉 | BR-025 | v2 |
| Email 列出新職缺數 | 最多 5 筆 | PRD | v2 |
| Saved Filter 付費擴充上限 | TBD | PRD | v2 |
事件字典
| 事件 | 觸發 | Payload | SoT | 版本 |
|---|
| saved_toggle | 收藏/取消 | {resource_type, resource_enc_id, on} | PRD | v1 |
| saved_filter_created | 建立條件 | {filter_enc_id} | PRD | v1 |
| saved_filter_renamed | 改名 | {filter_enc_id, name} | PRD | v1 |
| saved_filter_applied | 套用條件 | {filter_enc_id} | PRD | v1 |
| saved_filter_notify_clicked | 點 email CTA | {filter_enc_id, match_count} | PRD | v2 |
術語字典
| 術語 | 定義 | SoT | 版本 |
|---|
| 收藏 | user↔resource 正向綁定 | PRD | v1 |
| Saved Filter | 命名化的搜尋條件快照 | PRD | v1 |
| 新符合 | Saved Filter 自上次套用後新增的上架職缺(v2) | BR-015 + PRD | v2 |
12.2 版本凍結表
| 項目 | v1/v2 | Blocking? | 原因 | 決策者 | 日期 |
|---|
| 收藏職缺/公司 toggle | v1 | 否 | MVP | Eric | 2026/06/23 |
| Saved Filter(儲存/套用/改名/刪除) | v1 | 否 | MVP | Eric | 2026/07/03 |
| Saved Filter 通知(email/推播/badge) | v2 | 否 | v1 明確不做寄信 | Eric | 2026/07/03 |
| 收藏分類資料夾 | v2 | 否 | 非核心 | Eric | 2026/06/23 |
| 即時通知(vs 每日彙整) | v2 | 否 | 先做每日彙整 | Eric | 2026/06/23 |
| Saved Filter 上限 10 組(免費) | v1 | 否 | BR-025 | Eric | 2026/06/23 |
| Saved Filter 付費擴充上限 | v2 | 否 | 付費用戶可儲存更多組 | Eric | 2026/07/03 |
12.3 PRD 問題清單
| # | 章節/規則 | 問題 | 建議修正 | 類別 | 影響 | SoT |
|---|
| 1 | BR-025 | Saved Filter v1 免費 10 組 — 已裁決 | 無(已定案) | 商業 | — | BR-025 |
| 2 | §4.2.1 通知 | v1 不做寄信;v2 與推播中心對齊 | 已收斂至 §4.2.1 / §12.2 | 相依 | — | PRD |
| 3 | BR-024 | 封鎖連動移除範圍與提示 | 已裁決:移除公司收藏 + 旗下職缺收藏;Modal 提示「將同時移除相關收藏」 | 商業 | — | BR-024 |
13. 下一步
- v2:與「推播中心 / 平台通知與 Email」對齊 Saved Filter 通知通道與 digest 頻率(§4.2.1)。
- v2:評估 Saved Filter 付費擴充上限(v1 維持免費 10 組)。
- Coda:新增列
求職者收藏與追蹤中心,PRD 欄=pm_43(review 後執行)。
文件結束