線稿索引所有 spec最後更新 2026-06-11
specs/travelers.md

旅客

一句話:travelers訂單明細層的旅客 PII(綁 order_id非綁 user)——一個「人」可橫跨多筆訂單、出現為多筆 traveler,所以它不是會員/owner 的錨點。本檔是旅客實體的單一入口;欄位以 code 為準(packages/core/src/schema/business.tstravelers),畫面以 wireframe 為準(§6)。

定位:domain/core spec(實體與邏輯)。功能切片(分房 / 驗證 / 護照 / 同意書)在各自 spec,§5 索引串接。狀態見 roadmap

1. 實體模型

travelers(schema:packages/core/src/schema/business.ts):

  • 歸屬order_id(FK → orders.idonDelete: cascade)。綁訂單,不綁 user
  • 核心 PIIfull_namebirth_dateid_number(身分證,存 id_number_enc jsonb envelope 加密欄)、passport_no(護照,存 passport_no_enc jsonb envelope 加密欄)、phoneemail
  • 緊急聯絡emergency_contact_name / emergency_contact_phone
  • 旗標 / 關聯is_primary(是否同訂購人)、room_id(FK → departure_rooms.id,分房,onDelete: set null)、created_at

orders / 訂購人 user 的關係:訂購人是 orders.user_id(帳號);travelers 是該訂單的實際出行旅客(可含或不含訂購人本人,is_primary 標記)。詳見 orders §1、member-ownership §「會員/旅客/customer 三者釐清」。

2. 不變式

  • order-scoped、非 owner 錨點:travelers 沒有帳號語意、會重複,不可拿來掛 CRM owner(owner 掛 member_profiles / user,見 member-ownership)。
  • PII 加密id_number / passport_no 無明文欄,分別存 id_number_enc / passport_no_enc(jsonb EncryptedField)—— AES-256-GCM envelope(per-row 隨機 DEK,GCP Cloud KMS 管 KEK),AAD 綁 dbName:table:column:rowId:mask 防跨租戶/跨列/跨欄移植。讀取走持久化遮罩(零 KMS);全碼僅經 order-scoped reveal 解密 + traveler.decrypt audit(防 IDOR)。低敏感度欄(如 gender)解耦、可存明文。設計見 2026-06-11-kms-pii-encryption-design
  • 可改窗口:出發前 7 天可改(用於入山證 + 投保名冊)。
  • cascade:訂單刪除連帶旅客(FK cascade);分房刪除時 room_id set null。

3. 存取層契約

travelers 沒有獨立 repo,一律隨訂單帶出(first arg db: ScopedDb):

  • getAdminOrderDetailAdminOrderDetail.travelers: AdminTravelerRow[]packages/core/src/orders/admin-repo.ts)。
  • getOrderById / getOrderByIdForUser… & { travelers: TravelerInput[] }packages/core/src/orders/repo.ts)。
  • 建單時隨 createOrderWithReservationtravelers[] 一併寫入(同 tx)。
  • 入山名冊:manifest.ts。分房讀寫:rooms/repo.tsroom_id 回寫)。

4. 權限

無獨立 traveler resource——讀寫經訂單權限:order.read(看)、order.update(新增/編輯旅客)。可見範圍同訂單(owner-id query 層)。

5. 擴充旅客的功能 spec(索引)

Spec擴充什麼
room-assignmentgender(硬約束分房,明文)、room_preference / roommate_prefroom_id 配房
traveler-validationaddress / validation_exempt、身分證檢查碼 / 緊急聯絡 / 必填 / 金額一致即時驗證
document-expirypassport_expiry_date + 效期警戒(海外)
passport-ocr護照 OCR、20+ 護照欄位、purge cron(海外,KMS 前置)
orders母實體(訂單外殼 + 建單雙插)
同意書(consent_signatures每旅客/每訂單同意書簽署(append-only)

6. 畫面(UI = wireframe)

  • 旅客名單卡(在訂單詳情內)../apps/wireframes/public/legacy/admin/order-detail.html
  • 前台護照上傳 ../apps/wireframes/public/legacy/site/passport-upload.html
  • 後台護照審閱 ../apps/wireframes/public/legacy/admin/passport-review.html
  • 同意書簽署 ../apps/wireframes/public/legacy/site/consent-sign.html

Changelog

版本日期變更Commit
22026-06-11id_number / passport_no 改 envelope 加密欄(*_enc jsonb,KMS);補 AAD / 遮罩讀取 / reveal+audit 不變式,連結 KMS 設計 spec。
12026-06-01初版:旅客實體核心 spec(order-scoped PII / 不變式 / 契約 / 擴充索引 / wireframe)