◆ wport | pm_42

應徵紀錄(My Applications)Mockup PRD(給後端 API 規格產生器使用)

應徵紀錄(My Applications)

來源 doc/feature/pm_42/application-records-prd.md

應徵紀錄(My Applications)Mockup PRD(給後端 API 規格產生器使用)

此範本專門給 gen-storybook-mockup-gen 產出的 mockup PRD 使用。 backend-api-spec-generator 會讀取這份文件與 Storybook mockups 來產生後端 API 規格。 產出 persona:CEO with PM Background(SaaS / HR tech lens)。


1. 文件資訊

  • 文件類型:Mockup 需求規格書(前後端共用視圖)
  • 適用對象:前端開發、後端開發、產品、QA
  • 最後更新:2026/07/06
  • 版本:1.2.1
  • 對應 Storybookstorybook/src/stories/mockups/user/ApplicationRecords.stories.ts(已產出)
  • Storybook 連結https://storybook.wport.me/?path=/docs/mockups-user-applicationrecords—docs
  • 建立日期:2026/06/23
  • Coda backlog應徵紀錄

2. 開發進度與設計來源

開發進度

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

設計來源

  • Figma 連結:[待補上]
  • Pixsole / 既有畫面:求職者既有「會員中心」C 端框架(沿用 user app 樣式 profile;見實作備註)

3. 模擬頁面摘要(Mockup Summary)

3.1 功能概述

  • 功能名稱:應徵紀錄(My Applications)
  • 主要使用者角色:Job Seeker(求職者,含僑外生 / 外籍)
  • 核心目標
    • 讓求職者在單一頁面掌握「我投過哪些職缺、能不能再投、下次可投時間」。
    • 降低投遞後焦慮與重複投遞無效操作,提升投遞後 7 日回訪與再次應徵品質。
    • BR-011 的 72 小時再投冷卻一致地呈現「下次可投時間」。

3.2 範圍界定

  • 包含範圍(v1)
    • 應徵清單(同職缺多次合併一列;顯示公司、職缺、最近應徵日已投 N 次)。
    • 「再次應徵」按鈕(冷卻中 disabled 並倒數;見 BR-011)。
    • 職缺已關閉 / 已刪除:卡片灰階、職缺連結停用、顯示「此職缺已關閉」boundary(見 BR-009)。
  • 排除範圍(v1 不做,Storybook 可展示完整願景)
    • 狀態 badge 與狀態篩選 tab(全部 / 審閱中 / 面試邀約 / 婉拒)→ v2(待 HR 流程)。
    • 面試邀約 / 婉拒狀態(HR 端流程尚未實作)→ v2
    • pm_44 封鎖公司互動(再投隱藏 / is_blocked_by_me)→ v2(pm_44 上線後)。
    • 完整多次應徵時間軸(逐次列出)→ v2;v1 僅顯示最近應徵日與 apply_count
    • 站內信 / 與 HR 對話整合(另案)。
    • 應徵附加問卷的填寫流程(Coda:投遞附加問卷,另案)。
    • 履歷編輯(另案 edit-resume)。

3.3 成功條件

  • 求職者能在 1 頁看到全部應徵紀錄,且冷卻中職缺明確顯示「下次可投時間」。
  • 已關閉 / 已刪除職缺以灰階呈現,不會破圖或死連結,顯示「此職缺已關閉」。
  • 應徵後 7 日回訪率、再次應徵轉換率可被埋點量測。

4. 商業規則對齊(Business Rules Alignment)

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

規則 ID規則摘要是否關鍵備註
BR-011同一職缺再次應徵須距上次滿 72 小時;冷卻中無法投遞,系統回傳冷卻中與下次可投時間✅ 是冷卻不再是獨立狀態/ tab;改由「再次應徵」按鈕承載:冷卻中按鈕 disabled 並倒數(HH:MM:SS / 天·時),到期自動可投
BR-009職缺狀態(上架/下架/已刪除/已關閉);已刪除、已關閉為終態✅ 是已投職缺若進入終態,詳情需 boundary 顯示,不可再投
BR-015職缺搜尋僅顯示上架職缺⬜ 否紀錄頁不受搜尋顯示限制(已投紀錄仍須可查看歷史),但「再次應徵」入口受 BR-009/BR-011 約束
BR-001只有 email 已驗證才能登入✅ 是應徵紀錄為登入後頁面

4.2 補充說明

  • 本功能不新增 business rule,僅在 UI 層忠實呈現 BR-011/BR-009。
  • 與 pm_44(封鎖公司)互動:v2;v1 不實作封鎖相關 boundary 與 is_blocked_by_me

5. 資料實體與欄位(Data Entities & Fields)

描述「需要追蹤什麼資料、為什麼」,不規定 API 欄位命名 / 雜湊 / DB schema(由 OpenAPI Spec 決定)。

5.1 實體列表

實體名稱說明來源頁面/區塊
ApplicationRecord一次應徵的快照與狀態,供清單與詳情顯示SEC-1 主列表、SEC-3 詳情
JobSnapshot應徵當下的職缺顯示資訊(避免職缺被改/下架後顯示空白)SEC-1、SEC-3
CompanySnapshot應徵當下的公司顯示資訊(名稱、logo)SEC-1、SEC-3
ReapplyEligibility是否可再次應徵(含冷卻到期時間)SEC-3、ACT

5.2 實體欄位定義(TypeScript 介面,語意用途;命名待與 Storybook 對齊)

// v1 清單所需欄位(語意用途;命名由 OpenAPI Spec 決定)
interface ApplicationRecord {
  enc_id: string;
  job: JobSnapshot;
  company: CompanySnapshot;
  applied_at: string;             // ISO8601 最近應徵時間
  resume_enc_id: string;
  apply_count: number;            // 同職缺累計有效應徵次數(≥1)
  reapply: ReapplyEligibility;
  // v2 再納入:
  // status_code: ApplicationStatus;
  // status_name: string;
}

// v2:HR 處理狀態 + tab 篩選(全部 / 審閱中 / 面試邀約 / 婉拒)
type ApplicationStatus =
  | 'submitted'
  | 'reviewing'
  | 'invited'
  | 'rejected';
// 註(Eric 2026/06/24):冷卻不是 status enum,由 reapply.cooldown_until 驅動按鈕倒數。
// 註(Eric 2026/07/03):v1 不顯示狀態 badge / tab;Storybook mock 可保留完整願景供設計參考。

interface JobSnapshot {
  enc_id: string;
  title: string;
  lifecycle_code: 'published' | 'unpublished' | 'closed' | 'deleted'; // 對齊 BR-009
  area_name?: string;
}

interface CompanySnapshot {
  enc_id: string;
  name: string;
  logo_url?: string;
  // is_blocked_by_me: boolean;  // v2 + pm_44
}

interface ReapplyEligibility {
  can_reapply: boolean;
  cooldown_until?: string;        // ISO8601;冷卻中時提供,供按鈕倒數
  reason_code?: 'cooldown' | 'job_closed' | 'job_deleted'; // v2 再加 company_blocked
}

6. 畫面區塊與資料需求(UI Sections & Data Needs)

6.1 區塊列表

區塊 ID區塊名稱說明
SEC-1應徵清單卡片/列表,一筆一職缺;公司/職缺/最近應徵日/再投;終態職缺灰階
SEC-2狀態篩選列v2(Storybook 可展示 tab:全部 / 審閱中 / 面試邀約 / 婉拒)
SEC-3單筆詳情點開抽屜或導職缺頁;v1 顯示最近應徵日 + 再投/冷卻倒數
SEC-4空狀態無應徵時導向「找工作」CTA

6.2 各區塊資料需求

SEC-1 應徵清單

  • 顯示實體ApplicationRecord[]
  • 資料需求:分頁(page, page_size)、排序(預設 applied_at desc)
  • 顯示規則:同職缺多次應徵合併為一列已投 N 次),時間顯示最近應徵日
  • 職缺終態(closed / deleted):整卡灰階opacity 降低或灰階濾鏡);職缺標題連結停用;顯示 boundary「此職缺已關閉」;隱藏「再次應徵」
  • 空狀態:顯示「你還沒有應徵任何職缺」+「去找工作」CTA

SEC-2 狀態篩選列(v2)

  • v2 實作 tab + 狀態 badge(全部 / 審閱中 / 面試邀約 / 婉拒)。
  • Storybook mock 可保留完整 tab 作為設計願景;production v1 不實作。

SEC-3 單筆詳情

  • 內容(v1):職缺標題(非終態可連結職缺頁)、公司、最近應徵日apply_count再次應徵 按鈕
  • 內容(v2):狀態 badge、完整多次應徵時間軸、pm_44 封鎖 boundary
  • 職缺終態:同 SEC-1 灰階 +「此職缺已關閉」、停用連結、隱藏再投
  • 「再次應徵」按鈕的三態
    • 可投:enabled,文案「再次應徵」→ 導投遞流程。
    • 冷卻中(BR-011):disabled,按鈕本身顯示倒數計時(剩 <1 天用 HH:MM:SS,≥1 天用「N 天 H 時後可再投」);hover/title 顯示「下次可投:{cooldown_until}」;到期自動恢復可投。
    • 終態(BR-009):隱藏按鈕,灰階 + boundary「此職缺已關閉」。
  • 倒數來源:前端依後端 reapply.cooldown_until 換算並每秒 tick,避免時鐘漂移(§12)。

7. 使用者動作與後端需求(User Actions & Backend Needs)

動作 ID動作名稱類型需要後端涉及實體說明
ACT-1載入應徵清單查詢(Read)✅ 是ApplicationRecord含分頁、排序;由後端依本 PRD 產 API spec
ACT-2切換狀態 tab查詢(Read)❌ v2ApplicationRecordv2
ACT-3開啟詳情查詢(UI)❌ v1 可選ApplicationRecordv1 清單欄位足夠;v2 再評估明細 API
ACT-4再次應徵寫入(Write)✅ 是ReapplyEligibility沿用既有 apply 流程;受 BR-011/BR-009 檢核
ACT-5撤回應徵(nice)寫入(Write)✅ 是ApplicationRecordMVP 可不做,列入 v2

8. API Hints(提示用,非最終 API 設計)

8.1 資料讀取需求(Read)

  • HINT-GET-1:取得我的應徵清單(分頁 + 排序)→ SEC-1;語意見 §5.2、§12.4 欄位映射(實作由 backend-api-spec-generator 產出

8.2 資料寫入需求(Write)

  • HINT-MUTATION-1:再次應徵 → 沿用既有 apply 端點語意(job_enc_id, resume_enc_id);須能讓 UI 呈現 BR-011 冷卻(清單/詳情之 reapply.cooldown_until)。

9. 導航 / Storybook Map

  • Storybook 標題Mockups/求職者/應徵紀錄
  • Stories 路徑storybook/src/stories/mockups/user/ApplicationRecords.stories.ts
  • Storybook 連結https://storybook.wport.me/?path=/docs/mockups-user-applicationrecords—docs
  • 情境控制(實際 args)scenarioempty / mixed / has-cooldown / job-closed)——封鎖情境併入 mixed,無獨立 company-blocked(對齊實際 Storybook)
  • Storybook 與 v1 關係:Storybook 為完整願景 mock(含 tab、四種狀態);production v1 範圍以 §3.2、§12.2 為準,兩者並存不視為 PRD 不一致。
  • 對應 production 路由:求職者會員中心下(僅參考,不在此新增路由)

10. Flowcharts

10.1 User Flow

flowchart TD
    U1[登入後進入會員中心] --> U2[開啟應徵紀錄]
    U2 --> U3{有應徵紀錄?}
    U3 -- 否 --> U4[空狀態 + 去找工作 CTA]
    U3 -- 是 --> U5[顯示清單 + 最近應徵日]
    U5 --> U7[開啟單筆詳情]
    U7 --> U8{職缺狀態 / 冷卻?}
    U8 -- 可再投 --> U9[再次應徵 -> 投遞流程]
    U8 -- 冷卻中 --> U10[再次應徵按鈕 disabled + 倒數計時]
    U8 -- 已關閉/已刪除 --> U11[灰階 + 此職缺已關閉, 停用連結]

10.2 System Flow

sequenceDiagram
    participant User
    participant FE as Frontend
    participant BE as Backend
    User->>FE: 開啟應徵紀錄
    FE->>BE: GET 應徵清單(page)
    BE-->>FE: ApplicationRecord[] (含 reapply/cooldown)
    FE-->>User: 渲染清單
    User->>FE: 點「再次應徵」
    FE->>BE: POST 再次應徵(job,resume)
    BE->>BE: 檢核 BR-011 冷卻 + BR-009 狀態
    alt 通過
        BE-->>FE: 成功
        FE-->>User: 導投遞流程/成功提示
    else 冷卻中或終態
        BE-->>FE: 業務錯誤或 reapply 欄位
        FE-->>User: 顯示原因與下次可投時間
    end

10.4 Edge Case Coverage

類別情境系統行為UI 回饋可重試資料一致性策略備註
Network & Performance清單載入逾時 > 15s取消請求顯示逾時 + 重試Yes不寫入
Network & Performance「再次應徵」連點後端去重;前端按鈕 loading 鎖定防連點Noidempotency by (user,job,window)
Data & Input狀態 enum 未知值前端 fallback 顯示「處理中」不破版N/A以後端 status 為準v2(v1 無 status enum)
User Interruption載入清單中離開頁面取消未完成請求Yes無寫入
Auth & LifecycleSession 過期導登入並保留回跳提示登入過期Yes
Logical Inconsistency職缺已關閉/已刪除但紀錄存在清單/詳情灰階,再投停用「此職缺已關閉」;連結停用No以 job lifecycle 為準BR-009
Logical Inconsistency冷卻中嘗試再投後端拒絕回 cooldown_until顯示下次可投時間到期後 Yes不建立重複應徵BR-011

11. Mock Data Schema

  • 資料來源:僅 mock,不呼叫真實 API。
  • 願景 vs v1:mock 可含 tab 與四種狀態(完整願景);v1 production 範圍見 §3.2。

12. 實作備註(Implementation Notes)

  • 禁止事項:❌ 不在此定義最終 API path/method;❌ 不直接改 business-rules.md。
  • 注意事項
    • 欄位命名與 Storybook/TS interface 一致。
    • C 端頁面 → 走 user CSS profile(見 gen-storybook-mockup-gen 的 Dual CSS Profile Contract)。
    • 冷卻倒數以後端 cooldown_until 為準,前端僅做顯示換算,避免時鐘漂移。

12.1 四致性表(Stage 6)

欄位對照表

欄位語意SoT版本
status_codeHR 處理狀態 enumPRD §5.2v2
apply_count同職缺有效應徵次數PRD §5.2v1
reapply.cooldown_until下次可投時間BR-011 + PRDv1
job.lifecycle_code職缺生命週期BR-009v1

常數表

常數SoT版本
再投冷卻72 小時BR-011v1
清單分頁 page_size20PRDv1
清單載入 timeout15sPRDv1

事件字典

事件觸發Payload 摘要SoT版本
application_list_viewed開啟頁面{}PRDv1
application_reapply_clicked點再次應徵{job_enc_id, result}PRDv1

術語字典

術語定義SoT版本
有效應徵通過 BR-011 冷卻後成立的一次投遞BR-011v1
冷卻中距上次應徵未滿 72h 的狀態BR-011v1

12.2 版本凍結表

項目v1/v2Blocking?原因決策者日期
撤回應徵v2MVP 不含Eric2026/06/23
狀態 badge + tab(全部/審閱中/面試邀約/婉拒)v2待 HR 流程Eric2026/07/03
pm_44 封鎖公司互動v2pm_44 未上線Eric2026/07/03
完整多次應徵時間軸v2v1 僅最近應徵日 + apply_countEric2026/07/03
站內信整合out另案Eric2026/06/23

12.3 PRD 問題清單

#章節/規則問題建議修正類別影響SoT
1§5.2 statuscooldown 是否獨立狀態? 已裁決(Eric 2026/06/24):冷卻不是狀態,移出 enum / tab,改由 reapply.cooldown_until 驅動「再次應徵」按鈕倒數已定案PRD
2§5.2 status面試邀約 / 婉拒 / tab / badge 是否納入 v1? 已裁決(Eric 2026/07/03):皆 v2;Storybook 可展示完整願景已定案PRD
3§6.1多次應徵合併? 已定案:BE job_applications 合併一列已定案BE
4§6 SEC-3完整時間軸? v1 僅最近應徵日;逐次時間軸 v2已定案PRD
5§6 SEC-1職缺終態 UI已定案(Eric 2026/07/03):灰階 +「此職缺已關閉」已定案PRD
6§14.1 Av1 狀態流轉? 已裁決(Eric 2026/07/06):v1 不實作狀態機、不暴露 status enum、不做 tab/badge;頁面 = 全部應徵紀錄 flat list;reviewing / invited / rejected → v2,需另開 HR 流程 PRD已定案PRD
7§14.1 C2SEC-3 時間軸? 已裁決(Eric 2026/07/06):v1 清單欄位已足(最近應徵日 + apply_count);完整多次應徵時間軸 v2;詳情抽屜 v1 可選、不 blocking(對齊 ACT-3)已定案PRD
8§14.1 B1狀態變更通知? 已裁決(Eric 2026/07/06):v1 無狀態變化 → 不做狀態變更通知;v2 HR 流程上線後若需通知,另案已定案PRD
9§14.1 D1「已投遞」篩選 tab? 已裁決(Eric 2026/07/06):不需要;頁面即應徵紀錄,有紀錄即代表已投遞;v2 若有 tab,「全部」已涵蓋已定案PRD
10§14.1 B3pm_44 上線先後? 已裁決(Eric 2026/07/06):pm_42 先上線;pm_44 封鎖互動 v2 接上已定案PRD

12.4 後端參考備忘(W101-TalentSearchHub)

性質:供 RD / backend-api-spec-generator 參考,非 PRD 缺陷清單。PRD 已標「需要後端」之處,由後端依本節產 API。
SoT repoW101-TalentSearchHub(C 端應徵);W101-AMS 非本功能實作主體。
稽核日期:2026/07/03。

可沿用

PRD 需求後端現況路徑
BR-011 72h 冷卻REAPPLICATION_COOLDOWN_HOURS = 72applications.service.ts
同職缺合併job_applications unique (user_id, job_id, status)JobApplications.ts
再次應徵POST /applications/applyapplications.controller.ts
BR-009 終態阻擋再投validateJob()PUBLISHEDapplications.service.ts

RD 實作 backlog(不計入 PRD P0)

項目說明
求職者清單 API依 HINT-GET-1 + 下方欄位映射新建
清單含 reapplycooldown_until 供 UI 倒數
錯誤契約 / enum 映射由 OpenAPI spec 決定,PRD 不規定
pm_44 is_blocked_by_mev2 + pm_44 上線後
Legacy applyRecord 遷移工程計畫,不寫入 PRD

欄位映射草案(清單 API)

PRD 欄位後端來源
enc_idjob_applications.id 加密
applied_atlast_applied_at
resume_enc_idresume_id 加密
apply_countreapplication_count + 1
reapply.can_reapplycheckCanReapply(...).allowed ∧ job 可投
reapply.cooldown_untilcheckCanReapply(...).nextAvailableAt
job.lifecycle_codeJobStatus → §5.2 語意

13. 下一步(Next Steps)

  • 送 RD:backend-api-spec-generator 依本 PRD + §12.4 + §14 裁決產清單 API spec(無需再等 PM)。
  • 前端 v1:清單 + 灰階終態 + 再投/冷卻(不實作 tab/badge)。
  • v2 backlog:狀態 tab、HR 狀態機 PRD、pm_44、完整時間軸、狀態變更通知。
  • 補 i18n key(冷卻提示、「此職缺已關閉」)。
  • Coda:回寫 應徵紀錄 列 PRD 欄=pm_42

14. 待補與決策事項(RD 規格前審查,2026/07/06)

RD 在產出後端 API 規格前,對本 PRD 做了一輪完整性審查。§14.1 PM 裁決已於 2026/07/06 回覆;§14.2 為 RD 自行決定僅知會。Storybook 仍可展示完整願景(§9),production v1 以 §3.2 為準。

14.1 PM 裁決(已定案,Eric 2026/07/06)

以下回覆 RD 審查時提出的五項;多數與 §3.2、§12.2、§12.3 既有裁決一致,此處正式收斂以免重複提問。

A. v1 應徵狀態 / tab(原:狀態流轉未定義)

  • 裁決:v1 不實作應徵狀態機、清單 API 不暴露 status_code / status_name、不做 tab / badge。
  • 頁面語意全部應徵紀錄(flat list);求職者看到的就是「我投過哪些職缺」。
  • v2reviewing / invited / rejected 連同狀態流轉定義 → 另開 HR 流程 PRD,不在本案 v1 scope。
  • 依據:§3.2 L49-51、§12.2 L320、§12.3 #2。
  • Storybook 提醒mixed 情境的狀態卡為靜態願景 mock,≠ production v1 行為。

C2. SEC-3 單筆詳情 / 多次應徵時間軸

  • 裁決:完整多次應徵時間軸 → v2;v1 清單列已含最近應徵日 + apply_count(§6.2 SEC-3、§3.2 L53)。
  • 詳情抽屜:v1 可選、不 blocking(ACT-3:清單欄位已足夠);不強制補 mockup。
  • RD 審查備註:§14.1 原引用「§6.1 SEC-3 描述時間軸」與現行正文不符——§6.2 已明確分列 v1 / v2 內容,時間軸從未列入 v1。

B1. 狀態變更後要不要通知求職者?

  • 裁決:v1 無狀態變化 → 不做狀態變更通知(信件 / 推播皆不含)。
  • v2:待 HR 流程與狀態上線後,若需通知 → 另案(不擴本案 scope)。
  • 依據:§3.2 L54(站內信已排除);狀態單純是紀錄,沒有狀態變化就不需要通知。

D1. 要不要有「已投遞」篩選 tab?

  • 裁決不需要
  • v1:無 tab,不適用。
  • v2:若未來做 tab,不需獨立「已投遞」tab——頁面即應徵紀錄,有紀錄即代表已投遞,「全部」已涵蓋。

B3. pm_44(封鎖公司)與本案的上線先後?

  • 裁決pm_42 先上線;pm_44 封鎖互動 v2 接上。
  • 技術:RD graceful degrade 維持 §14.2 既定(is_blocked_by_me 當 false、不顯示封鎖 boundary)。
  • 依據:§3.2 L52、§12.2 L321。

14.2 RD 自行決定(知會,不需 PM 回覆)

以下屬顯示規則 / 技術契約 / 文件對齊,RD 於 spec 階段自行定案並記錄於此,PM 無需處理:

  • B2 合併列顯示哪一次狀態(v2):v1 清單不含 status,本項不適用;待 v2 狀態上線時 RD 定:合併列顯示最新一次應徵的狀態,與時間一致。
  • D2「再次應徵」端點語意(資格檢查 vs 完整投遞):§8.2 與 §10 流程圖描述不一。RD 定:於 spec 階段對齊既有投遞流程實作決定,不另擴新語意。
  • C1 Storybook 情境與命名不一致:§9 / §11 列 5 情境(含獨立 company-blocked),實際只有 4 個(mixed / has-cooldown / job-closed / empty)且命名為 mixed(非 mixed-status)。RD 定:以實際 Storybook 為準,回頭對齊 §9 / §11 文字與命名。
  • D1-技術面 status 參數合法值RD 定:v1 清單 API 不含 status 參數;v2 若實作 tab 篩選,status query 收 §5.2 四個 enum 值,不需獨立「已投遞」tab(§14.1 D1 已定)。
  • B3-技術面 pm_44 fallbackRD 定:pm_44 未上線時 graceful degrade(is_blocked_by_me 當 false、不顯示封鎖 boundary)。

一句話總結v1 可開工——清單 API + 冷卻(BR-011)+ 終態(BR-009);狀態/tab/通知/封鎖/時間軸均不 blocking。§14.1 五項 PM 裁決已定案;§14.2 僅知會。

文件結束