通知偏好設定 Mockup PRD
此範本專門給 30%-feature-gen 產出的 mockup PRD 使用。 backend-api-spec-generator 會讀取這份文件與 Storybook mockups 來產生後端 API 規格。
1. 文件資訊
- 文件類型:Mockup 需求規格書(前後端共用視圖)
- 適用對象:前端開發、後端開發、產品、QA
- 最後更新:2026/03/25
- 版本:1.2.16
- 對應 Storybook:
storybook/src/stories/mockups/account/NotificationPreferences.stories.ts - Storybook 連結:https://storybook.wport.me/?path=/docs/mockups-account-notification-preferences—docs
- 建立日期:2026/03/13
2. 開發進度與設計來源
開發進度
- 前端 PR #:TBD
- 後端 PR #:TBD
設計來源
- Figma 連結:無
- 既有畫面參照:
- CakeResume 通知偏好設定頁面
- LinkedIn Settings > Notifications(分類 + toggle 模式)
- 104 人力銀行 招募管理 > 訊息通知個人化設定
3. 模擬頁面摘要(Mockup Summary)
3.1 功能概述
- 功能名稱:通知偏好設定(Notification Preferences)
- 主要使用者角色:所有使用者(求職者、企業成員)
- 核心目標:
- 讓使用者自主控制要收到哪些 Email 通知,減少不必要的信件
- 透過事件通知(新訊息、面試邀約、新應徵、履歷瀏覽等)提升求職者與企業的互動即時性
- 通知偏好頁僅管理通知開關;Email 語系改由個人偏好語系統一管理
3.2 範圍界定
- 包含範圍:
- 通知偏好設定頁面(位於使用者下拉選單 > 偏好設定)
- 人才端通知設定區塊(所有使用者可見)
- 企業端通知設定區塊(偵測到有公司成員身份時才顯示)
- 5 種 Email 通知事件的寄送邏輯
- 人才回覆訊息通知支援「上架者 + 曾主動聯繫該人才的企業成員」收件規則
- 訊息類通知的頻率限制(1 小時內最多 1 封)
- 每日摘要「誰看過我的履歷」(每日 19:00)
- 互動:採「切換 Switch 即寫入」無獨立「儲存」按鈕(與常見求職平台通知設定一致);前端可對寫入 API 使用 debounce(例如 300–500ms)合併短時間內多次切換,避免多餘請求;寫入成功可不顯示 toast(靜默寫入即可)
- 排除範圍:
- 不做站內通知(鈴鐺、通知列表)— 未來獨立卡片
- 不做 Push Notification
- 不做匿名瀏覽企業功能(未來付費功能)
- 不做 Email 信件模板設計(另案處理)
- 不做面試邀約功能本身(僅處理通知,不含邀約流程 UI/API)
3.3 成功條件
- 企業端收到新應徵通知後 24 小時內查看應徵的比例提升
- 人才端收到「有人看過履歷」摘要後點擊信件回站的比例
- 企業端收到通知後及時回應求職者的比例
- 使用者不再因為沒收到通知而錯過新訊息、面試邀約或應徵
4. 商業規則對齊(Business Rules Alignment)
4.1 本功能使用到的業務規則
| 規則 ID | 規則摘要 | 是否關鍵 | 備註 |
|---|---|---|---|
| BR-018 | 電子郵件發送規則與類型 | ✅ 是 | 本功能擴充 BR-018,新增 4 種事件通知類型 |
| BR-007 | 公司成員邀請與管理 | ⚠️ 參考 | 判斷使用者是否有公司成員身份(決定是否顯示企業端區塊) |
| BR-NEW-1 | 通知偏好預設全開 | ✅ 是 | 新規則:使用者首次進入偏好設定時,所有通知預設開啟 |
| BR-NEW-2 | 訊息通知頻率限制 | ✅ 是 | 新規則:一般訊息/回覆同一收件者同一事件類型,1 小時內最多寄送 1 封,且需在事件觸發當下存在新未讀訊息才寄;面試邀約不套用此限制 |
4.2 補充說明
- BR-018 目前僅列出驗證信、邀請信等類型,本功能新增 4 種事件通知類型,建議回寫至 business-rules.md
5. 資料實體與欄位(Data Entities & Fields)
5.1 實體列表
| 實體名稱 | 說明 | 來源頁面/區塊 |
|---|---|---|
| UserNotificationPreferences | 使用者通知偏好設定 | 偏好設定頁面 |
| NotificationRateLimit | 通知頻率限制紀錄 | Notification Service 內部 |
5.2 實體欄位定義(TypeScript 介面)
UserNotificationPreferences
interface UserNotificationPreferences {
user_id: string
// 人才端
talent_new_message: boolean // 新訊息(即時)
talent_interview_invite: boolean // 面試邀約(即時,高優先)
talent_resume_viewed_digest: boolean // 誰看過我的履歷(每日摘要)
// 企業端
employer_new_application: boolean // 收到新應徵(即時)
employer_talent_reply: boolean // 人才回覆訊息(即時)
updated_at: string // 最後更新時間
}
NotificationRateLimit(Notification Service 內部使用)
interface NotificationRateLimit {
recipient_email: string
event_type: 'talent_new_message' | 'talent_interview_invite' | 'employer_talent_reply'
last_notified_at: string // 上次寄送時間
}
6. 畫面區塊與資料需求(UI Sections & Data Needs)
6.1 區塊列表
| 區塊 ID | 區塊名稱 | 說明 |
|---|---|---|
| SEC-1 | 人才端通知設定 | 新訊息、面試邀約、誰看過履歷 的 toggle |
| SEC-2 | 企業端通知設定 | 收到新應徵、人才回覆訊息 的 toggle(有公司身份才顯示) |
6.2 各區塊資料需求
SEC-1 人才端通知設定
- 組件:3 個 toggle 開關
- 欄位:
| 設定項 | 說明 | 頻率 | 預設 |
|---|---|---|---|
| 新訊息 | 有企業發送新訊息時通知 | 即時(1hr 限制) | ON |
| 面試邀約 | 有企業發送面試邀約時通知 | 即時(高優先,不限 1hr) | ON |
| 誰看過我的履歷 | 每日彙整哪些公司查看了你的履歷 | 每日 19:00 摘要 | ON |
- 顯示條件:所有使用者皆可見
SEC-2 企業端通知設定
- 組件:2 個 toggle 開關
- 欄位:
| 設定項 | 說明 | 頻率 | 預設 |
|---|---|---|---|
| 收到新應徵 | 有求職者應徵您的職缺時通知 | 即時 | ON |
| 人才回覆訊息 | 人才回覆您的訊息時通知 | 即時(1hr 限制) | ON |
- 顯示條件:使用者至少屬於一家公司(任何角色皆可)
- 設定範圍:統一套用所有公司,不分開設定
7. 使用者動作與後端需求(User Actions & Backend Needs)
| 動作 ID | 動作名稱 | 類型 | 需要後端 | 涉及實體 | 說明 |
|---|---|---|---|---|---|
| ACT-1 | 載入通知偏好 | 查詢 | ✅ 是 | UserNotificationPreferences | 進入頁面時載入;無紀錄則回傳預設值 |
| ACT-2 | 儲存通知偏好 | 寫入 | ✅ 是 | UserNotificationPreferences | 每次切換 Switch 即觸發寫入(可 debounce);無獨立「儲存」按鈕 |
| ACT-3 | 判斷是否有公司身份 | 查詢 | ✅ 是 | — | 決定是否顯示企業端區塊 |
| ACT-5 | 寄送即時通知 | 寫入 | ✅ 是 | NotificationRateLimit | Notification Service 收到事件後執行 |
| ACT-6 | 寄送每日摘要 | 寫入 | ✅ 是 | — | 每日 19:00 排程觸發 |
| ACT-7 | 寄送面試邀約通知 | 寫入 | ✅ 是 | — | 面試邀約事件觸發時即時寄送(高優先) |
場景化動作描述
場景 A1:純人才使用者首次進入
| 項目 | 描述 |
|---|---|
| 進入條件 | 已登入、不屬於任何公司、偏好紀錄不存在 |
| 畫面呈現 | 顯示人才端區塊(3 個 toggle,預設 ON);不顯示企業端區塊 |
| 操作流程 | 使用者切換 Switch →(可 debounce 後)呼叫 API 寫入(成功可不顯示提示) |
| 錯誤處理 | 寫入失敗 → 顯示錯誤提示,還原該項修改前的值 |
場景 A2:同時是人才 + 企業成員
| 項目 | 描述 |
|---|---|
| 進入條件 | 已登入、至少屬於一家公司 |
| 畫面呈現 | 顯示人才端區塊 + 企業端區塊(共 5 個 toggle) |
| 操作流程 | 同 A1(皆為切換即寫入,無儲存按鈕) |
場景 A3:已設定過的使用者再次進入
| 項目 | 描述 |
|---|---|
| 進入條件 | 已登入、偏好紀錄存在 |
| 畫面呈現 | 顯示上次儲存的設定值 |
| 操作流程 | 同 A1(皆為切換即寫入,無儲存按鈕) |
場景 B:人才端新訊息通知寄送
| 項目 | 描述 |
|---|---|
| 觸發 | 企業對人才發送新訊息 |
| 判斷流程 | Notification Service 收到事件 → 查人才偏好(talent_new_message)→ ON 才繼續 → 查頻率限制(< 1hr 且無新未讀 → 不寄;>= 1hr 且有新未讀 → 寄信)→ 讀取個人偏好語系(非本功能欄位)→ 寄信 |
場景 B2:人才端面試邀約通知寄送(高優先)
| 項目 | 描述 |
|---|---|
| 觸發 | 企業對人才送出面試邀約 |
| 判斷流程 | Notification Service 收到 interview_invite_created 事件 → 查人才偏好(talent_interview_invite)→ ON 才繼續 → 不套用 1 小時限制 → 讀取個人偏好語系(非本功能欄位)→ 即時寄信 |
場景 B6:每日摘要寄送
| 項目 | 描述 |
|---|---|
| 觸發 | 每日 19:00 排程 |
| 判斷流程 | 撈今日有履歷被瀏覽的使用者 → 查偏好(talent_resume_viewed_digest)→ ON 才寄 → 組摘要信(含公司卡片)→ 依個人偏好語系選模板(非本功能欄位)→ 寄出;當天無瀏覽紀錄則不寄 |
場景 C:企業端新應徵通知
| 項目 | 描述 |
|---|---|
| 觸發 | 有求職者應徵某職缺 |
| 收件者判斷 | 固定寄送給該職缺上架者(job_poster);並檢查上架者偏好(employer_new_application = ON)才寄送 |
| fallback | 無(收件者固定為上架者) |
8. API Hints(提示用,非最終 API 設計)
8.1 資料讀取需求(Read)
-
HINT-GET-1:取得使用者通知偏好設定
- 對應區塊:SEC-1, SEC-2
- 輸出:
UserNotificationPreferences(無紀錄時回傳預設值:全部 ON)
-
HINT-GET-2:判斷使用者是否有公司成員身份
- 對應區塊:SEC-2 顯示條件
- 輸出:
{ hasCompanyMembership: boolean }
8.2 資料寫入需求(Write)
- HINT-MUTATION-1:儲存使用者通知偏好
- 輸入:
UserNotificationPreferences(5 個通知 boolean,全部或部分欄位) - 行為:upsert(無紀錄則建立,有紀錄則更新);由前端在 每次 Switch 變更 觸發(可 debounce 合併),無獨立儲存按鈕
- 輸入:
8.3 事件通知需求(Event-Driven)
-
HINT-EVENT-1:新訊息事件(人才端)
- 觸發:企業對人才發送新訊息
- payload:
{ talent_user_id, sender_user_id, conversation_id, job_id }
-
HINT-EVENT-2:新訊息事件
- 觸發:企業對人才發送訊息
- payload:
{ talent_user_id, sender_user_id, conversation_id }
-
HINT-EVENT-2B:面試邀約事件(人才端,高優先)
- 觸發:企業對人才發送面試邀約
- payload:
{ talent_user_id, invitation_id, company_id, job_id, interview_time }
-
HINT-EVENT-3:新應徵事件(收件者:固定上架者
job_poster)- 觸發:求職者應徵某職缺
- payload:
{ job_id, applicant_user_id, job_poster_user_id }
-
HINT-EVENT-4:人才回覆訊息事件(收件者:上架者 + 曾主動聯繫該人才的企業成員)
- 觸發:人才回覆企業的訊息
- payload:
{ job_id, talent_user_id, conversation_id, job_poster_user_id, recruiter_user_ids } - 收件者:
- 一定包含
job_poster_user_id - 若該職缺有其他企業成員曾主動聯繫此人才,這些成員也納入
recruiter_user_ids - 去重後逐一檢查
employer_talent_reply偏好並寄送
- 一定包含
-
HINT-EVENT-5:履歷被瀏覽事件
- 觸發:企業成員在人才搜尋中查看某人履歷
- payload:
{ talent_user_id, company_id, viewed_at }
8.4 排程需求(Scheduled)
- HINT-CRON-1:每日履歷瀏覽摘要
- 排程:每日 19:00
- 行為:撈取當天有瀏覽紀錄的使用者 → 依偏好過濾 → 組摘要信(含公司卡片)→ 依個人偏好語系選模板(非本功能欄位)→ 批次寄出
8.5 寄信觸發點對齊表(Trigger Points Alignment)
此表用於與 RD 對齊「在哪個系統事件才算正式寄信」,避免重複寄送或漏寄。
| 事件類型 | 模組 | 建議觸發點(預設) | 去重鍵(建議) | 頻率限制切點 | 失敗補償建議 | 待確認 |
|---|---|---|---|---|---|---|
| 面試邀約 | 求職者 | interview_invite_created 事件進入 Notification Service 後 | recipient_user_id + event_type + invitation_id | 不套用 1 小時限制(即時寄送) | 寄送失敗進 retry queue,最多 3 次(exponential backoff) | 事件來源模組與 payload 是否一致(是否已有 invitation_id) |
| 新訊息 | 求職者 | message_created 事件進入 Notification Service 後 | recipient_user_id + event_type + conversation_id | 寄送前檢查 NotificationRateLimit + 事件觸發當下是否存在新未讀訊息 | 寄送失敗進 retry queue,最多 3 次(exponential backoff) | 事件來源模組與 payload 是否一致 |
| 誰看過我的履歷(每日摘要) | 求職者 | 每日 19:00 cron 啟動摘要 job | recipient_user_id + summary_date | 每日一封(當日無資料不寄) | job 失敗可補跑;單封重試最多 3 次(exponential backoff),補跑需避免重複寄同日摘要 | 時區固定 Asia/Taipei;補跑窗口待確認 |
| 收到新應徵 | 公司 | 應徵交易「正式成功」後發出 job_application_created 事件 | job_application_id + recipient_user_id | 不限頻率(每次應徵都可寄) | 失敗可重送(最多 3 次,exponential backoff),但需保證同一 job_application_id 不重複寄 | 「正式成功」定義(DB commit 後 / domain event) |
| 人才回覆訊息 | 公司 | 人才訊息落庫成功後發出 talent_reply_created 事件 | recipient_user_id + event_type + message_id | 以「每位收件者」為單位套用 1 小時限制 + 事件觸發當下是否存在新未讀訊息 | 逐收件者重試(最多 3 次,exponential backoff),避免部分收件者失敗造成整批中斷 | recruiter_user_ids 明確定義為:同公司、同職缺、且在該 conversation_id 曾由企業端送出過訊息的成員 |
Trigger 對齊檢查清單
- 每個事件是否都有唯一 id(
message_id/job_application_id)可做冪等保護? - 事件是否在「資料已成功寫入」後才發送(避免寄信先於交易成功)?
- 收件者名單是否在觸發當下決定,且可重算(上架者 + 曾聯繫成員)?
- 發信失敗後重試是否會重複寄送(需要 idempotency key)?
訊息類通知案例規則(對齊)
- 人才端:同一人才若在 1 小時內收到多家企業新訊息(例如 20 家),Email 通知最多 1 封(可彙整);且需在事件觸發當下存在新未讀訊息才寄。
- 企業端:人才回覆通知以「每位收件者」為單位做 1 小時 1 封限制;若同時有多位人才回覆,可能產生多封(符合人力銀行低頻場景);且需在事件觸發當下存在新未讀訊息才寄。
- 每日摘要(誰看過我的履歷)固定每日 1 封,內文列出當日有瀏覽的企業清單。
已讀 / 未讀判斷建議(訊息類通知)
- Notification Service 在判斷是否允許寄送時,需同時檢查是否存在新未讀訊息(事件觸發當下狀態)。
- 建議資料來源(二選一,依後端現況擇一):
- Conversation 維度:
unread_count_by_user > 0 - Message 維度:存在
created_at > last_read_at的訊息
- Conversation 維度:
- 若無法取得可用未讀狀態,則保守策略為:僅套用 1 小時頻率限制,不做「新未讀」放行條件(待後端補齊後再啟用)。
8.6 Email 類型與模板輸入(Email Types & Template Inputs)
本章節只定義「有哪些信」與「每封信最低限度需要哪些資料」。
模板文案、版型、最終 subject 文字可由設計/內容流程另行產出。
| template_key | 對應事件 | 收件者 | subject 方向(暫定) | 最低模板變數(必填) |
|---|---|---|---|---|
email.talent.interview_invite | 面試邀約 | 人才帳號 email | 你收到新的面試邀約 | talent_name, company_name, job_title, interview_time, interview_type, interview_link, invited_at, conversation_link |
email.talent.new_message | 新訊息 | 人才帳號 email | 有企業對你發送新訊息 | talent_name, company_name, job_title, conversation_link, sent_at |
email.talent.resume_viewed_digest.daily | 誰看過我的履歷(每日摘要) | 人才帳號 email | 今天有公司看過你的履歷 | talent_name, summary_date, viewer_companies[], resume_center_link |
email.employer.new_application | 收到新應徵 | 職缺上架者(job_poster) | 你上架的職缺收到新應徵 | poster_name, job_title, applicant_name, application_link, applied_at, conversation_link |
email.employer.talent_reply | 人才回覆訊息 | 上架者 + 曾主動聯繫該人才的企業成員 | 人才回覆了你的對話訊息 | recipient_name, talent_name, job_title, conversation_link, message_preview, replied_at |
8.6.1 CTA 按鈕與 Deep Link 規格(含 token)
除每日摘要(
email.talent.resume_viewed_digest.daily)的resume_center_link不使用 token 外,其餘*_link(例如conversation_link、interview_link、application_link)皆視為「可直接打開的帶 token 連結」。token 由後端產生,用來驗證該 email 收件者是否被允許進入對話/頁面。
通用規則:
- 每封信件至少包含 1 個 Primary CTA Button。
- Button 點下去要導到對應頁面(對話/申請/履歷中心/面試邀約/職缺上下文)。
- 連結 token 僅允許該收件者存取;若收件者未登入:
- 先導向登入頁
- 登入後自動帶回 token 並重新驗證
- 驗證通過後再進入目標頁面
五封信件 CTA mapping(按鈕標籤為暫定,可由內容/設計調整):
| template_key | Primary CTA Button | 導向連結(變數) |
|---|---|---|
email.talent.new_message | 查看對話並回覆 | conversation_link |
email.talent.interview_invite | 查看對話並處理邀約 | conversation_link |
email.talent.resume_viewed_digest.daily | 回到履歷中心 | resume_center_link |
email.employer.new_application | 查看對話並處理申請 | conversation_link |
email.employer.talent_reply | 查看對話並回覆 | conversation_link |
補充情境(對你是 HR 的需求對齊):
- 只要 token 有效,點
email.employer.talent_reply的查看對話並回覆就會直接跳轉到「你(公司 HR)與該人才」對話筐。 - 若同一人才對話由多位公司成員參與,則
conversation_link必須是針對該 email 收件者生成的 token,確保 HR 打開的是自己的對話入口。
每日摘要例外說明:
email.talent.resume_viewed_digest.daily的resume_center_link不使用 token;若未登入則導向登入並回跳到履歷中心,呈現當日摘要(最多 5 間公司)。
模板邊界說明
- 本 PRD 僅要求
template_key與必填變數,不要求最終 HTML/文案版型。 - 新訊息/面試邀約相關通知共用同一套 Notification pipeline(事件處理、重試、頻率限制),但
template_key需依事件與收件者角色分流。 - 各語系模板由 Email 系統依「個人偏好語系」選擇(語系設定不在本功能頁管理)。
- 若變數缺漏,Notification Service 應拒絕寄送並記錄可追蹤錯誤(避免寄出破版信件)。
9. 導航 / Storybook Map
- Storybook 標題:
Mockups/Account/NotificationPreferences - Stories 路徑:
storybook/src/stories/mockups/account/NotificationPreferences.stories.ts - Storybook 連結:https://storybook.wport.me/?path=/docs/mockups-account-notification-preferences—docs
- 情境控制:
hasCompanyMembership:是否有公司成員身份(控制企業端區塊顯示)initialPreferences:預設偏好設定值
- 對應 production 路由:使用者下拉選單 > 偏好設定(既有路徑內新增區塊)
10. Flowcharts(User / System / Navigation)
10.1 User Flow(通知偏好設定頁面)
flowchart TD
Start[使用者點擊大頭照 → 偏好設定<br>(已登入狀態)] --> LoadPref[載入通知偏好設定]
LoadPref --> HasRecord{偏好紀錄存在?}
HasRecord -->|否| DefaultAll[顯示預設值:全部 ON]
HasRecord -->|是| ShowSaved[顯示上次儲存的設定值]
DefaultAll --> RenderPage
ShowSaved --> RenderPage
RenderPage[渲染偏好設定頁面] --> CompanyCheck{使用者有公司成員身份?}
CompanyCheck -->|否| ShowTalentOnly[只顯示人才端區塊]
CompanyCheck -->|是| ShowBoth[顯示人才端 + 企業端區塊]
ShowTalentOnly --> UserEdit
ShowBoth --> UserEdit
UserEdit[使用者切換 Switch<br>無「儲存」按鈕] --> Persist[(可 debounce)呼叫 API 寫入偏好]
Persist --> SaveResult{寫入成功?}
SaveResult -->|是| Done[完成(可不顯示 toast)]
SaveResult -->|否| ErrToast[顯示錯誤提示,還原該項修改前數值]
10.2 System Flow(即時通知寄送)
補充:面試邀約(
talent_interview_invite)為高優先事件,收到事件且偏好為 ON 時即時寄送,不套用 1 小時限制。
sequenceDiagram
participant Src as 觸發來源模組
participant NS as Notification Service
participant DB as 資料庫
participant Mail as Email 寄送服務
Src->>NS: 發出事件(event_type, payload)
NS->>DB: 查詢收件者(依 event_type 決定邏輯)
Note over NS,DB: 人才端 → 人才帳號 Email<br>企業端新應徵 → 職缺上架者<br>企業端人才回覆 → 上架者 + 曾聯繫該人才的企業成員
NS->>DB: 查收件者 user_notification_preferences
alt 收件者為會員 + 該通知 toggle = OFF
NS--xNS: 丟棄,不寄
else 收件者為會員 + toggle = ON,或非會員
alt 訊息類通知(新訊息/人才回覆)
NS->>DB: 查 last_notified_at(收件者 + event_type)
alt < 1hr
NS--xNS: 丟棄,不寄
else ≥ 1hr
NS->>DB: 查是否存在新未讀訊息(事件觸發當下)
alt 無新未讀
NS--xNS: 丟棄,不寄
else 有新未讀
NS->>DB: 查收件者個人偏好語系(非本功能欄位)
NS->>Mail: 寄送 Email(依語系選模板)
NS->>DB: 更新 last_notified_at
end
end
else 新應徵通知
NS->>DB: 查收件者個人偏好語系(非本功能欄位)
NS->>Mail: 寄送 Email(依語系選模板)
end
end
10.3 System Flow(每日摘要 — 誰看過我的履歷)
sequenceDiagram
participant Cron as 排程(每日 19:00)
participant NS as Notification Service
participant DB as 資料庫
participant Mail as Email 寄送服務
Cron->>NS: 觸發每日摘要任務
NS->>DB: 撈取今日有履歷被瀏覽的使用者清單<br>(grouped by user_id)
loop 對每個有瀏覽紀錄的使用者
NS->>DB: 查 user_notification_preferences<br>(talent_resume_viewed_digest = ON?)
alt toggle = OFF
NS--xNS: 跳過此使用者
else toggle = ON
NS->>DB: 查該使用者今日的瀏覽紀錄<br>(哪些公司看過、時間)
NS->>DB: 查收件者個人偏好語系(非本功能欄位)
NS->>Mail: 寄送摘要 Email<br>(含公司卡片、公司數量)
end
end
10.4 System Flow(四種即時信件:倒到對話匡)
flowchart TD
T[多來源觸發:message_created / interview_invite_created / job_application_created / talent_reply_created] --> NS[Notification Service(worker 並行處理)]
NS --> NewMsg[email.talent.new_message(新訊息)]
NS --> InterviewInv[email.talent.interview_invite(面試邀約)]
NS --> NewApp[email.employer.new_application(新應徵)]
NS --> TalentReply[email.employer.talent_reply(人才回覆)]
NewMsg --> NewMsgCheck[寄前:對話匡(新訊息);toggle=ON?;1hr 節流;事件觸發當下存在新未讀?]
InterviewInv --> InterviewCheck[寄前:對話匡(面試邀約);toggle=ON?(不套用 1hr)]
NewApp --> NewAppCheck[寄前:對話匡(新應徵);job_poster 偏好=ON?(無頻率限制)]
TalentReply --> TalentReplyCheck[寄前:對話匡(人才回覆);逐收件者 toggle=ON?;1hr 節流;事件觸發當下存在新未讀?]
10.5 System Flow(Email CTA 點擊:登入/驗 token)
flowchart TD
A[使用者在 Email 點擊 Primary CTA 按鈕] --> B[瀏覽器打開帶 token 的 *_link]
B --> C{token 是否有效且屬於該 email 收件者?}
C -->|否| X[顯示「連結已失效或無權限」頁面]
C -->|是| D{使用者已登入?}
D -->|否| E[導向登入頁(redirect_to=目標頁 + token)]
E --> F[登入成功後回跳]
F --> G{再次驗證 token + 使用者權限}
D -->|是| G
G -->|否| X
G -->|是| H{目標頁類型:使用者頁 or 公司頁?}
H -->|使用者頁| I[進入目標使用者頁:對話筐/履歷中心/面試邀約詳情]
H -->|公司頁| J{目標公司識別:token 指定 company_id 是否等於目前 active company?}
J -->|是| K[進入目標公司頁:該公司對話筐/申請列表/職缺上下文]
J -->|否| L{token 類型:使用者 scope 還是 公司 scope?}
L -->|使用者| M[直接進入目標公司頁:該公司對話筐/申請列表/職缺上下文<br>(不切換 active company;同時驗證你是否為該公司成員;無則失敗)]
L -->|公司| O[前後端切換公司情境到 token 指定 company_id<br>(同時驗證你是否為該公司成員;無則失敗)]
M --> N[進入目標公司頁:該公司對話筐/申請列表/職缺上下文]
O --> N[進入目標公司頁:該公司對話筐/申請列表/職缺上下文]
10.6 System Flow(每日摘要 Email:不使用 token + 最多 5 間公司)
flowchart TD
Cron[每日 19:00 cron] --> NS[Notification Service 執行每日摘要]
NS --> DB1[撈取今日(Asia/Taipei)履歷被瀏覽事件<br>group by talent_user_id]
DB1 --> NS3[針對每個人才:取今日被瀏覽公司<br>依 viewed_at 由新到舊排序;取前 5 間]
NS3 --> NS4{取到的公司數是否為 0?}
NS4 -->|是| Skip[不寄送]
NS4 -->|否| Send[寄送 `email.talent.resume_viewed_digest.daily`<br>(resume_center_link:不使用 token;公司卡片連結可能帶 token)]
Send --> Click[收件者點信件中的公司卡片或「查看公司」(可能帶 token)]
Click --> T0{此時 token/mode:company or user?}
T0 -->|user| OpenCompany[進入公司頁面(user leaf)]
T0 -->|company| SwitchToUser[切換到 user leaf] --> OpenCompany
11. Mock Data Schema(Mock 資料結構)
- 資料來源:僅 mock,不呼叫真實 API
- CRUD 行為:mock 僅本地狀態切換、不呼叫 API;重新整理頁面後還原為預設
- 情境切換:
hasCompanyMembership:控制企業端區塊是否顯示initialPreferences:控制各 toggle 初始值(4 個通知開關)
12. 實作備註(Implementation Notes)
- 禁止事項:
- ❌ 本文件不直接定義最終 API path/method,僅提供 Hints
- ❌ 不直接修改
prd/business-rules.md,新規則先在「商業規則對齊」描述,再由人決定是否回寫
- 注意事項:
- 欄位命名與 Storybook/TS interface 一致
- Email 語系由個人偏好語系統一管理,不在本功能頁設定
- 企業端通知設定統一套用所有公司,不分公司獨立設定
- 人才端通知分為「新訊息」與「面試邀約」兩級;面試邀約為高優先事件
- 儲存互動:不使用頁級「儲存」按鈕;改為切換即寫入,前端可 debounce 降低 API 次數;成功不必強制 toast
架構決策記錄
| 決策項目 | 選擇 | 理由 |
|---|---|---|
| 偏好寫入 UX | 切換即寫入(無儲存按鈕);成功可不 toast | 對齊主要競品設定頁行為;寫入量低,debounce 可抑制連點;避免每切一次就打擾使用者 |
| 通知服務架構 | 事件驅動(Event-Driven) | 解耦,未來擴充站內通知只需加 consumer |
| 頻率限制實作位置 | Notification Service 層處理 | 頻率邏輯集中管理,事件發送保持簡單 |
| 偏好設定儲存方式 | 每個通知類型一個 boolean 欄位 | 通知類型固定(4 種),KISS 原則 |
| 每日摘要排程 | 留給後端依實際資料結構決定 | Cron 或事件累積皆可,視瀏覽紀錄查詢成本 |
| Email 語系管理位置 | 由個人偏好語系統一管理(不在本功能欄位) | 避免語系設定分散於多個頁面 |
通知收件者邏輯彙整
| 事件 | 收件者決定邏輯 | 頻率限制 |
|---|---|---|
| 新訊息 | 人才帳號 Email | 1hr 內最多 1 封 + 須有新未讀訊息(事件觸發當下) |
| 面試邀約 | 人才帳號 Email | 即時寄送(高優先,不套用 1hr 限制) |
| 誰看過我的履歷 | 人才帳號 Email | 每日 19:00 一封(無瀏覽不寄) |
| 收到新應徵 | 固定寄送給職缺上架者(job_poster),並尊重其 employer_new_application 偏好 | 無限制(每次應徵都寄) |
| 人才回覆訊息 | 寄送給職缺上架者(job_poster)與曾主動聯繫該人才的企業成員;逐一尊重 employer_talent_reply 偏好 | 1hr 內最多 1 封 + 須有新未讀訊息(以收件者為單位、事件觸發當下) |
訊息通知是否一次性(定義)
- 訊息類通知(
新訊息、人才回覆訊息)不是一次性只寄一封。 - 系統採用「節流 + 新未讀」機制:
- 每位收件者、每種事件類型,1 小時內最多 1 封(不含面試邀約)。
- 只有在存在「新未讀訊息」(事件觸發當下)時才觸發寄送。
- 使用者已讀後,若後續又有新訊息且符合節流條件,仍可再次寄送提醒。
入口與狀態約束表
| 入口 | 狀態 | 約束 | 預期行為 |
|---|---|---|---|
| 偏好設定頁 | 已登入、無偏好紀錄 | — | 顯示預設值(全 ON) |
| 偏好設定頁 | 已登入、有偏好紀錄 | — | 顯示上次儲存值 |
| 偏好設定頁 | 已登入、無公司身份 | — | 只顯示人才端區塊 |
| 偏好設定頁 | 已登入、有公司身份 | — | 顯示人才端 + 企業端區塊 |
新業務規則建議
以下規則建議回寫至 business-rules.md:
- BR-NEW-1:通知偏好預設全開 — 新使用者的所有通知偏好預設為開啟
- BR-NEW-2:訊息通知頻率限制 — 一般訊息/人才回覆同一收件者同一事件類型,1 小時內最多寄 1 封 Email 通知,且須於事件觸發當下存在新未讀訊息才觸發(面試邀約不套用)
- BR-NEW-3:新應徵通知收件者固定為上架者 — 新應徵通知固定寄給職缺上架者(job_poster),不採用外部聯絡 email
- BR-NEW-4:人才回覆通知收件者擴充 — 人才回覆訊息時,收件者包含上架者與曾主動聯繫該人才的企業成員,並尊重各自偏好設定
- BR-NEW-5:面試邀約通知高優先 — 面試邀約採獨立事件與模板,偏好為 ON 時即時寄送,不套用 1 小時節流
13. 下一步(Next Steps)
- 使用
storybook-mockup-genskill 產出偏好設定頁面的 Storybook mockup - 產出後將 Storybook 連結回填至本 PRD §1 及 Trello 卡片
- 與後端確認 Email 寄送服務(SendGrid / SES / 其他)及發送量限制
- 與後端確認「誰看過我的履歷」瀏覽紀錄是否已記錄
- 與後端確認每日摘要排程實作方式
- 將 BR-NEW-1 ~ BR-NEW-5 回寫至
business-rules.md - 面試邀約通知事件已納入本期,作為高優先獨立觸發點
更新記錄
-
2026-03-25:v1.2.16 更新
- 調整 10.6:收件者已登入後不需再判斷連結是否帶 token;改判斷此時 token/mode 是 company 或 user
-
2026-03-25:v1.2.15 更新
- 修正
10.6:每日摘要的 token 不用來切換到公司 leaf;token 僅用於授權驗證,最終一律進入履歷中心當日摘要(user leaf)
- 修正
-
2026-03-25:v1.2.14 更新
- 調整
10.6:token scope=公司時,僅用於切換/驗證公司情境;最終仍進入履歷中心當日摘要(user leaf)
- 調整
-
2026-03-25:v1.2.13 更新
- 修正
10.6每日摘要 flowchart:移除 viewer_companies 步驟;點公司頁依 token scope(公司需切換/跳轉、使用者可直接開公司頁)
- 修正
-
2026-03-25:v1.2.10 更新
- 調整 10.5:公司頁分支中,目標公司識別不相符時直接切換到 token 指定 company(並在切換過程驗證權限;無則失敗)
-
2026-03-25:v1.2.12 更新
- 修正 10.6 每日摘要 flowchart 中 Mermaid 語法(移除
viewer_companies[]的[]以避免 parse error)
- 修正 10.6 每日摘要 flowchart 中 Mermaid 語法(移除
-
2026-03-25:v1.2.11 更新
- 調整四種即時信件 CTA 為同一路徑:對話匡;新增每日摘要(不使用 token、最多 5 間公司)flowchart
- 調整 10.5:token=使用者時直接跳公司頁;token=公司時才需要切換公司情境
-
2026-03-25:v1.2.9 更新
- 調整 10.5「公司頁」分支:登入 A 公司但 token 指向 B 公司時,若為 B 成員則切換公司情境;否則無權限
-
2026-03-25:v1.2.8 更新
- 調整 10.5 flowchart:已登入要走第二分支,並新增「使用者頁/公司頁」與「company_id」判斷
-
2026-03-25:v1.2.7 更新
- 補上
10.5:Email CTA 點擊後的登入/驗 token flowchart
- 補上
-
2026-03-25:v1.2.6 更新
- 補齊五封信件的 CTA 按鈕與 Deep Link 規格(帶 token;未登入先導向登入並回跳、再驗證)
-
2026-03-25:v1.2.5 更新
- 訊息通知不再等待「未讀持續超過 5 分鐘」;改為事件觸發當下存在新未讀訊息即可允許寄送
- 新增 10.4「五類 Email 同時寄送/分流」flowchart
-
2026-03-25:v1.2.4 更新
- 寫入成功 不強制 成功提示(toast);mock 移除成功訊息與「已開啟 n/5」匯總 Tag
-
2026-03-25:v1.2.3 更新
- 明確 無「儲存」按鈕:偏好以 Switch 切換即寫入;前端可 debounce
- 同步 User Flow、場景描述、ACT-2、Mock 說明與 Storybook mockup
-
2026-03-24:v1.2.2 更新
- 調整 8.5「寄信觸發點對齊表」排序,將「面試邀約」移至最上方
- 8.5 新增「模組」欄位(求職者 / 公司),並將事件類型名稱簡化,改善換行可讀性
-
2026-03-24:v1.2.1 更新
- 訊息類通知(人才新訊息、企業端人才回覆)新增「新未讀需持續超過 5 分鐘才寄」規則,避免使用者在線上互動時被 Email 打擾
-
2026-03-24:v1.2.0 更新
- 人才端通知分級為「新訊息」與「面試邀約」,面試邀約改為高優先獨立觸發點
- 新增
talent_interview_invite偏好欄位與對應event_type - 新增面試邀約事件與模板(
email.talent.interview_invite) - 明確定義面試邀約不套用 1 小時節流,偏好 ON 時即時寄送
-
2026-03-23:v1.1.0 更新
- 通知偏好頁移除 Email 語系欄位,改由個人偏好語系統一管理
- 取消非會員退訂機制,移除相關場景/資料結構/API hints
- 企業端收件者規則定稿:新應徵寄給上架者;人才回覆寄給上架者 + 曾主動聯繫該人才的企業成員
- 補充寄信觸發點對齊表、Email 類型與模板輸入、重試策略(最多 3 次)
- 明確訊息通知非一次性,採 1 小時節流 + 新未讀條件
-
2026-03-13:初版 PRD
- 通知偏好設定頁面:人才端 2 種 + 企業端 2 種通知 toggle(不含 Email 語系欄位)
- 事件驅動架構,Notification Service 統一處理偏好與頻率限制(語系由個人偏好管理)
- 新應徵通知收件者固定為職缺上架者(job_poster)
- 人才回覆通知收件者為上架者 + 曾主動聯繫該人才的企業成員
- 取消非會員退訂機制,移除退訂頁與相關資料結構
- 訊息類通知 1hr 頻率限制 + 須有新未讀
- 每日摘要「誰看過我的履歷」19:00 寄出,含公司卡片
- 4 條新業務規則建議
文件結束