◆ wport | pm_32

通知偏好設定 Mockup PRD

通知偏好設定

來源 doc/feature/pm_32/notification-preferences-mockup.md

通知偏好設定 Mockup PRD

此範本專門給 30%-feature-gen 產出的 mockup PRD 使用。 backend-api-spec-generator 會讀取這份文件與 Storybook mockups 來產生後端 API 規格。


1. 文件資訊


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寄送即時通知寫入✅ 是NotificationRateLimitNotification 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 啟動摘要 jobrecipient_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 的訊息
  • 若無法取得可用未讀狀態,則保守策略為:僅套用 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

除每日摘要(email.talent.resume_viewed_digest.daily)的 resume_center_link 不使用 token 外,其餘 *_link(例如 conversation_linkinterview_linkapplication_link)皆視為「可直接打開的帶 token 連結」。token 由後端產生,用來驗證該 email 收件者是否被允許進入對話/頁面。

通用規則:

  • 每封信件至少包含 1 個 Primary CTA Button。
  • Button 點下去要導到對應頁面(對話/申請/履歷中心/面試邀約/職缺上下文)。
  • 連結 token 僅允許該收件者存取;若收件者未登入:
    • 先導向登入頁
    • 登入後自動帶回 token 並重新驗證
    • 驗證通過後再進入目標頁面

五封信件 CTA mapping(按鈕標籤為暫定,可由內容/設計調整):

template_keyPrimary 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.dailyresume_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 語系管理位置由個人偏好語系統一管理(不在本功能欄位)避免語系設定分散於多個頁面

通知收件者邏輯彙整

事件收件者決定邏輯頻率限制
新訊息人才帳號 Email1hr 內最多 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-gen skill 產出偏好設定頁面的 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)
  • 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 條新業務規則建議

文件結束