Self-Service Booking Kiosk
Kiosk 是一個預約網站,但其信任模型是反轉的:客戶端在設計上未經身分驗證且為實體共用,因此所有信任邏輯都位於伺服器端(價格、容量、冪等性),所有狀態都是可拋棄的(記憶體內、自動重置、永不持久化)。此技能中的一切皆源自這兩個事實。
使用時機
- 建立為時段型容量(工作站 × 時間窗口)的 walk-up 預約終端機,並可選配設備與消耗品附加項目。
- 為已具有線上預約功能的應用程式加入 kiosk 模式。
- 根據下方的硬性規則審核現有 kiosk;provenance.md 記錄了每條規則背後的原因。
不應使用時機
- 場內離線備援伺服器 — 這是其兄弟技能
island-mode-server的範疇;此技能僅定義 kiosk 客戶端的容錯切換合約。 - 數位看板 / 顯示螢幕 — 不同的能力(播放、無輸入),不同的技能。
- 員工面向的 POS 或管理預約工具 — 員工有身分驗證且受信任;此技能的信任模型不適用。
- 一般的預約網站 — 如果有用可以取用其後端介面,但 kiosk 的機制(鍵盤、計時器、觸控強化)在此是多餘的。
架構
KioskProvider (純 reducer: steps, selections, cart, sessionId)
│ props: dictionary slices hooks: pricing · equipment · availability
screens: service→date→time→participants→equipment→summary→confirmation
│ └─ side flow: booking-lookup → booking-edit
▼ one fetch wrapper (device key + offline base-URL failover)
/api/kiosk/booking/{create,lookup,add-items,edit} ← all trust decisions
▼ KioskBackend seam: server pricing · promo · stock locks (TTL 15 min)
transactional capacity re-check · counter/online payment · webhook
realtime: location.lastBookingChangeAt → silent refetch → REFRESH_SLOT
關鍵事實
- 使用者點擊才導航;背景重新整理永遠不會。
SET_SLOT與REFRESH_SLOT的區別在於即時可用性更新與在摘要頁面中途中將客戶傳送到其他地方。 - 伺服器為所有項目定價。 沒有任何與金錢相關的內容傳入客戶端;客戶端總計僅供預覽。
- 櫃台付款是彈性路徑 — 預約確認時狀態為
PENDING_COUNTER_PAYMENT,付款流程中不整合任何付款系統;kiosk 上的線上付款意味著客戶掃描 QR 碼,絕不將 kiosk 導航離開。 - 每次文字輸入都會開啟螢幕小鍵盤並設定
inputMode="none"。缺少任何一個都會導致畫面無法使用。 - 庫存鎖定是 TTL 保留(15 分鐘),並在每個失敗路徑上釋放 — 清除任務是安全網,而非主要機制。
- 客戶端 sessionId 是冪等鍵。 雙擊、重試、遺失回應:最多只會產生一筆預約。
硬性規則
絕不持久化 kiosk 工作階段狀態。 下一個客戶不得看到上一個客戶的姓名。使用記憶體內儲存 + 120 秒閒置重置 + 30 秒確認頁面重置,並在硬體上驗證。
絕不信任客戶端提供的價格、容量或促銷有效性。 伺服器計算每個金額,並對無效促銷回應
PROMO_INVALID;在 kiosk 上的客戶無法確認金額。
每個 kiosk 請求使用同一個 fetch 包裝器。 包裝器攜帶裝置標頭、錯誤封裝與容錯切換的 base URL,因此身分驗證、錯誤處理與離線備援可同時套用至所有請求。
如果已設定 device key,則缺少標頭時回傳 401。 僅在 key 錯誤時拒絕請求等同於沒有身分驗證。
在取得庫存鎖定後,於每個失敗路徑釋放鎖定。 一個保持保留狀態的 409 錯誤會使庫存在整個 TTL 期間凍結。
快速開始
- 閱讀 state-machine.md;實作 reducer 和 provider,並執行其測試套件。
- 根據 screens.md 的合約,使用主機的設計系統建立畫面 — 鍵盤、計時器、觸控強化。
- 根據 api-contract.md 實作路由,並透過 booking-backend.md 的介面與後端整合。
- 根據 realtime-offline.md 串接即時更新與離線功能。
- 按照 operations.md 部署並進行冒煙測試。
適配合約
| 介面 | 此技能提供 | 主機需提供 |
|---|---|---|
| 領域實體 | service/station/equipment/consumable + rename table | 其詞彙(court, kart, bay…) |
| 租戶範圍 | kiosk URL 中的 locationId,伺服器驗證 | 其位置/站點模型 |
| 裝置驗證 | x-kiosk-api-key 合約 | key 管理;可選的每裝置 token |
| 資料存取 | KioskBackend 介面;Firestore 參考 + SQL 草稿 | 其 ORM/SDK |
| 付款 | 櫃台狀態 + checkout-session/webhook 合約 | Stripe 或等價方案 |
| 即時更新 | SubscribeFreshness 介面 | Firestore/Supabase/SSE/polling |
| 離線 | NetworkStatus 合約 | LAN 伺服器(island-mode-server)或無 |
| UI 基礎元件 | 結構、狀態、互動規則 | 按鈕、對話框、旋轉圖示、外觀 |
| 樣式 | 佈局意圖、觸控目標、一個 --primary token | 其設計系統 |
| 字串 | dictionary key tree,所有 key 必填 | 其 i18n 檔案,所有語系 |
| 驗證 | 無相依性的 guard | 其 schema 函式庫(若有的話) |
參考目錄
| 情境 | 觸發關鍵字 | 參考文件 |
|---|---|---|
| 狀態、步驟、reducer、工作階段、測試 | step, reducer, GO_BACK, REFRESH_SLOT, sessionId, reset | state-machine.md |
| 畫面、鍵盤、計時器、觸控、i18n 鍵 | on-screen keyboard, inactivity, countdown, QR, touch target, dictionary | screens.md |
| 路由、驗證、冪等性、錯誤 | x-kiosk-api-key, 401, 409, idempotency, PROMO_INVALID, lookup, PII | api-contract.md |
| 容量、庫存、付款、webhook、crons | transaction, stock lock, TTL, PENDING_COUNTER_PAYMENT, webhook, refund | booking-backend.md |
| 即時更新、快取、離線備援 | lastBookingChangeAt, onSnapshot, s-maxage, offline banner, LAN, health poll | realtime-offline.md |
| 部署、閘控、操作者介面 | Edge --kiosk, assigned access, heartbeat, feature gate, smoke test | operations.md |
| 範本與早期實作差異的原因 | audit, ledger, kept deliberately, added | provenance.md |