本章目標與前置條件
本章回答「工作台上的數字從哪裡來」「個人資料哪些可編輯」「審計頁是否真的接 API」。 前置條件是能閱讀 Next.js App Router、SWR、資料範圍與 read-only projection;不需要先建立員工資料。
核心概念:一個快照,多個入口
backend GET /v1/workspace/overview 只取一個 as_of,以 Asia/Taipei 產生 period、workforce、
attendance、daily leave 與 todo count。attendance 或 leave source 不完整時回傳 availability 的
unavailable 狀態,不把未知資料冒充成 0:reason 分成 source_incomplete 與 before_cutoff(尚未到統計時點);
cohort 超過 2000 人時 attendance 不計算,直接回 source_incomplete。
權限以 internal/route/testdata/registry.golden 與 handler 為準。overview 的路由點是
hr.workspace_overview.read,handler 另查 platform.workspace.read,其 scope 必須是 tenant 或 all;
overview scope 只接受 tenant、all、department、department_subtree、direct_reports,self 回 403(20101)。
GET /v1/workspace/overview/todos 必須帶 category,目前支援:
onboarding、regularization、offboarding、leave_suspended、contract_expiry。
它需要四個權限點:路由點 hr.workspace_todo.read,handler 再查 platform.workspace.read(tenant/all)、
hr.workspace_overview.read 與 hr.employee.read。員工集合由 overview scope 決定,hr.workspace_todo 與
hr.employee 的 scope 必須完整覆蓋這個集合,否則整個請求回 403(20101),不會回部分名單。
欄位政策取自 hr.employee,集合結果也不能跳過欄位遮罩;planned_offboard_date、leave_start_date、
leave_end_date 目前未建模,固定回 null。
功能清單與目前狀態
| 入口/功能 | 目前狀態 |
|---|---|
| /workspace overview metrics、attendance、daily leave | 已實作(原始碼層級,API 串接) |
| 五類待辦 modal 與分頁 | 已實作(原始碼層級,API 串接);待離職、留職停薪的日期欄因後端未建模固定顯示「—」 |
| /workspace/approval-roles | 已有 ApprovalRolesPage 與 API 串接,詳見工作流;未做部署驗收 |
| /workspace/attendance、/workspace/clock | 有 work-hours/clock 元件,屬實際頁面入口 |
| /workspace/employees、/workspace/forms、/workspace/leave-policy | 有對應 HR、form、leave 元件;各自資料狀態分開驗證 |
| /workspace/turnover | 有 TurnoverPageContainer 與 feature 入口,部署資料待核實 |
| /workspace/audit-log | fixture/示意資料;page.tsx 明示目前不是 API 完成,後端沒有稽核查詢路由 |
| /dashboard | RoutePlaceholder,不可寫成洞察報表已完成 |
| 個人設定/個人資訊 | profile 讀取有 API(重用 HR employee 端點);偏好、密碼、裝置操作目前多為 local/模擬 |
| Header 使用者與天氣 | 使用者名稱與 email 取自 /v1/me;天氣只在首頁經前端 BFF 取得,不是後端 /v1 API |
| 首頁 AI assistant | 輸入、上傳、送出 disabled;目前是空狀態/佔位,不是 Agent 功能完成 |
| 首頁常用表單、每日打卡 | 表單清單走 GET /v1/forms/templates(僅 development 且 NEXT_PUBLIC_MSW=true 時改用 fixture);每日打卡依 attendance.clock.read 顯示 |
實際請求鏈
Workspace page
→ useGetMe() 得到 tenant/account
→ useWorkspaceOverview() identity-scoped SWR(每 60 秒 refresh)
→ GET /v1/workspace/overview
→ hr.workspace_overview.read + platform.workspace(tenant/all)+ overview scope
→ hr.Governor.WorkspaceOverview(one sampled as_of)
→ Repository SQL snapshot + tenant transaction
→ attendance facts 另以 SQL 讀取,規則在 Go 端判定(cohort 不超過 2000 人)
→ availability / metrics / todo counts點開待辦後:
selected category
→ useWorkspaceTodos(category, page, enabled)
→ GET /v1/workspace/overview/todos
→ hr.workspace_todo.read + platform.workspace + hr.workspace_overview + hr.employee
→ overview、todo、employee 三個 scope 覆蓋同一 cohort,否則 403(20101)
→ paged rows with field_access(值已依 field policy 遮罩或隱藏)
→ modal table(只顯示遮罩後的值,未讀 field_access)前端以 hr.workspace_todo.read 與 hr.employee.read 決定待辦按鈕與 enabled。overview 的 SWR 固定每 60 秒
refresh;若 attendance_available_at 晚於 as_of,另在該時點排一次 refresh。沒有統計資料時保留明確提示
(資料暫不可用或尚未到統計時間),不顯示假零值。
個人資訊與設定的實際邊界
useSettingsProfile.ts 以 /v1/me、employee detail、employee profile 與 HR lookups 組合目前登入者資料。
後端沒有 /v1/me/profile:employee detail 與 profile 重用 GET /v1/hr/employees/:employeeId 與
GET /v1/hr/employees/:employeeId/profile(resource hr.employee、page hr.employees、target_employee);
部門與職稱名稱另需 hr.org_unit.read、hr.position.read,沒有權限時顯示「無法取得部門名稱」「無法取得職稱名稱」。
SettingsPanels.tsx 將姓名、部門、職稱等 profile 欄位呈現為 disabled/read-only。語言、主題、timezone、
notification toggle 與 DND 是本地偏好(當次 React state,重新整理即回到 fixture 預設),不等於後端設定 API。
密碼變更目前是 local modal 的模擬成功提示;裝置登出也沒有看到服務端 mutation。開發新 API 前, 必須先確認這些 UI 是否已被真實 endpoint 取代,不能以 toast 或 fixture 當成帳戶資料已變更。
Header 使用者與天氣
PlatformShell 以 /v1/me 的 account.displayName(空白時改用 email)與 email 顯示使用者選單。
天氣只在首頁 / 且已登入時啟用:hooks/api/useHeaderWeather.ts 取瀏覽器低精度定位,經
hooks/api/useCurrentGeoContext.ts 呼叫同源 BFF /api/geo/current-context
(app/api/geo/current-context/route.ts)。這支 route 先以 httpOnly token cookie 呼叫
NEXUS_API_BASE_URL 的 /v1/me 驗證身份,依 tenant/account 限流(每分鐘 12 次),再收斂座標後查
Open-Meteo 與 Nominatim(NOMINATIM_BASE_URL 可替換);任一上游失敗回 503,不猜地點。
它不是後端 /v1 API,也不在 registry.golden。
審計紀錄的後端現況
後端已有寫入側:internal/service/audit/recorder.go 的 Record/RecordTx 把稽核證據追加到 audit_logs
(000002_audit_logs.sql)。服務層雖有 ReadByTrace,但沒有 HTTP route 使用它,registry.golden 也沒有稽核查詢路由。
internal/route/pages.go 已宣告 audit.log 頁(path /workspace/audit-log、ReadPoint audit.log.read),
而 audit.log.read 列在 internal/route/testdata/unrouted-points.golden,目前只作為頁面權限,沒有對應 API。
前端規則禁止為未在 registry.golden 註冊的路由建立 hooks/api/ hook,所以頁面只能維持 fixture;
/dashboard 的 platform.dashboard.read 同樣是未綁定路由的 point。
開發步驟
- 先讀 registry.golden、routes.go、workspace_overview.go 與 response renderer,確認路由點、page gate、scope、category 和欄位政策。
- 新增 metric 時沿用一次 as_of,明確區分合法零值、source incomplete 與 before cutoff。
- 新增待辦類型時同步 backend allow-list、repository query、response schema、frontend Zod enum 與 modal mapping。
- 欄位新增要同步 field_access;不要因為是 dashboard 集合就跳過 employee field policy。
- frontend hook 的 SWR key 必須包含 tenant/account;loading、error、unavailable、empty 各自呈現。
- 若要把 audit/dashboard/settings 由 fixture 或 local 行為接成真功能,先凍結 API、權限、migration 與驗收範圍;audit 要先有註冊在 registry.golden 的查詢路由,前端才可新增 hook。
預期結果與驗證
預期結果是:授權使用者看到與 as_of 一致的 workforce summary;統計來源不可用時看見 unavailable reason; 待辦列只來自 overview scope 內的員工,todo/employee scope 覆蓋不足時整個請求回 403(20101); 欄位依 field policy 遮罩或隱藏;前端身份切換不保留上一租戶的資料。
驗證清單:
- 對 overview 核對 period.timezone、as_of、workforce count、turnover rate 與 availability。
- 逐一測試五個 category、缺少 category(422/10430)、未知 query(422/10430)、重複 query(400/10001)、分頁及 unauthorized scope;另測只缺
hr.workspace_todo.read的帳號,以及 employee scope 窄於 overview scope 的帳號,兩者都應 403。 - 在 attendance cutoff 前驗證不會把 attendance_today 顯示成假零值。
- 以 field policy mask/hide 驗證 todo row 的值與 field_access 一致;前端目前只呈現遮罩後的值,不讀 field_access。
- 開啟 /workspace/audit-log,確認看到 fixture 明示,不把它當成 server audit readback。
- 開啟 /dashboard,確認仍是 placeholder;不要為了截圖替換成假資料。
- 個人設定只驗證已讀取的 profile;密碼/裝置/偏好 mutation 需另有真實 API 證據。
- 開啟待離職、留職停薪 modal,日期欄顯示「—」是後端 null 的預期結果,不是前端 mapping 錯誤。
本輪未執行 authenticated browser、workspace API、資料庫或部署驗證;以上為原始碼核實的驗收方案。
常見錯誤
- 將 unavailable、null 與合法 0 混成一種畫面狀態。
- 在前端自行計算 turnover 或補 attendance 數字,繞過 backend snapshot。
- 只檢查 workspace page point,漏掉 todo 的
hr.workspace_todo、hr.workspace_overview與hr.employee。 - 以 overview permission 讀出全租戶員工,忽略 department/direct reports scope。
- 以為 todo/employee scope 不足時會拿到部分名單;實際是整個請求 403。
- 把待離職、留職停薪日期欄的「—」當成前端 bug;目前是後端未建模。
- 把 /workspace/audit-log fixture 內容、/dashboard RoutePlaceholder 寫成已完成功能。
- 把個人設定的 disabled 欄位、local toast 或 demo logout 當成可持久化 API。
- 把 header 天氣當成後端 /v1 API 或到 registry.golden 找它;它是前端 BFF route。
- 因首頁有 AI assistant card 就宣稱 Agent 已可用;目前 input、upload、send 仍是 disabled。
相關文件
- 功能全景與實作索引:後端 registry 與 frontend page 的總覽。
- 專案目錄導覽:依實際 checkout 定位 route 與 feature owner。
- 組織與員工:workspace workforce、員工欄位與資料範圍來源。
- 通知中心:header 通知入口與待辦審核頁的差異。
- 身分、IAM 與權限目錄:page visibility、data scope 與 field policy。