專案

一般

配置概況

動作

Task #1470

進行中

Feature #1464: [M1] 垂直切片:HPICIOEB 散客線上報名端到端

[API] T01a 核心 schema ERD 設計

是由 鍾正剛 於 27 天 前加入. 於 27 天 前更新.

狀態:
Resolved
優先權:
Normal
被分派者:
開始日期:
2026-09-04
完成日期:
完成百分比:

0%

預估工時:

概述

範圍
產出 docs/features/001-booking-platform/erd.md:Mermaid erDiagram、各表欄位與型別/nullable、索引清單、關鍵設計點說明(Session 補充屬性怎麼存、表單答案 JSON 欄位、金額明細快照)。
本票不寫任何 code。

依 D18/spec §4.5:關聯以邏輯關係標示,並註明「應用層維護,DB 無約束」;關聯欄位仍須列出索引。

驗收
ERD 文件完成並經使用者 review 核可

相依
無(M1 起點)

spec: docs/features/001-booking-platform/spec.md
tickets: docs/features/001-booking-platform/tickets.md


相關的議題清單 1 (1 進行中 — 0 已結束)

阻擋 hpigeopark - Task #1485: [API] T01b 核心 schema 與 entity 實作Resolved鍾正剛2026-09-04

動作

是由 鍾正剛 於 27 天 前更新

  • 阻擋 Feature #1471: [API] T02 開放日期表匯入(後端) 已新增

是由 鍾正剛 於 27 天 前更新

是由 鍾正剛 於 27 天 前更新

  • 阻擋 Feature #1474: [API] T05 計價純計算模組 已新增

是由 鍾正剛 於 27 天 前更新

  • 主旨 從 [API] T01 核心 schema 與 entity 變更為 [API] T01a 核心 schema ERD 設計
  • 概述 已更新 (差異)

是由 鍾正剛 於 27 天 前更新

  • 阻擋 Task #1485: [API] T01b 核心 schema 與 entity 實作 已新增

是由 鍾正剛 於 27 天 前更新

  • 阻擋 已刪除 (Feature #1471: [API] T02 開放日期表匯入(後端))

是由 鍾正剛 於 27 天 前更新

是由 鍾正剛 於 27 天 前更新

  • 阻擋 已刪除 (Feature #1474: [API] T05 計價純計算模組)

是由 鍾正剛 於 27 天 前更新

【T01a 完成,交付 ERD 待 review】

文件位置:docs/features/001-booking-platform/erd.md(836 行)

本票依定義只出設計文件、不寫 code、不建表。核可後由 #1485 T01b 依此文件建 schema 與 JPA entity。


一、關鍵設計判斷

  1. PK 一律 bigint AUTO_INCREMENT,業務識別碼另立 code varchar(32) 欄位。放棄舊系統 varchar PK + 自製 ID Generator,減輕 JPA 與跨表 join 負擔。
  2. 無 FK 但關聯欄位一律建索引(D18)。舊系統 13 張業務表除 PRIMARY 外零索引,這是本次重建的效能重點修補。共列 24 條非 PRIMARY 索引,依「關聯欄位/前台查詢/後台查詢/排程掃描」四類分組說明服務的查詢。
  3. 狀態欄位 varchar 存 enum name,狀態與取消原因分離兩欄(§5.3)。舊系統 int + Converter 導致 DB 側完全無法辨認,是明確的 anti-pattern。
  4. 三種預約型態單表實作(D10):booking_type 欄位 + session_id(前兩型必填)與 apply_date_from/to(申請審核型必填)互斥為 NULL。無 DB CHECK,由 service 層保證。
  5. Session 補充屬性採 JSON:activity.session_extra_fields 宣告欄位形狀、session.extra_attributes 存值。避免舊 h_tide.gather_time varchar(100) 變成 1,519 筆自由格式的重演。取捨與 EAV 拆表方案列入待確認 Q4。
  6. 金額明細快照:order.price_breakdown 為結構化 JSON,含 steps[](含 label/kind/amount)、priceTier 快照、agreement、coupon、subtotal、total。文件中示範 M1(居民票 170×3)與 M2(含協議+coupon)兩種形狀,供客服逐行還原。
  7. 樂觀鎖 + 單一 UPDATE 原子扣減(§5.1):session.version 為 JPA @Version;名額扣減走 UPDATE ... WHERE version=? AND (capacity IS NULL OR occupied_count+? <= capacity) 一句 SQL。capacity IS NULL 對應無上限場次(D17),仍記錄 occupied_count 但不做上限檢查。
  8. 表單版本快照為 int,不是 FK:order.form_definition_version 存整數,配合 form_definition (code, version) 唯一鍵解讀舊訂單答案。發布新版時舊版改 ARCHIVED、不刪。

二、對 spec 的補充(spec 未寫、本文件補)

  • 主鍵型別、稽核欄位 4 欄、金額 int、狀態欄位型別、price_breakdown JSON 結構、form_definition_version 為快照非 FK、reserved_until 單欄承載線上/線下兩種語意、activity_i18n 表結構。

沒有偏離 spec 之處。假設 target env MySQL ≥ 8.0。

三、M1 vs M2 界線

  • M1 建 11 張表:activity, activity_i18n, price_tier, session, order, booking, order_payment, order_invoice, form_definition, form_field, form_option_source。
  • M1 建欄位但暫不使用:在 order 上預留 customer_id、agreement_id、coupon_code、discount_amount 4 欄(M2 用)。理由:M6 遷移後 order 將有數萬列,ALTER TABLE 代價高;四欄 nullable,M1 不用不會有副作用。
  • M2+ 才建的表:customer, agreement, agreement_scope, coupon, coupon_redemption。M2 開工時再依 O-4(退款)等再定案,現在建大概率要改。
  • 明確不建:舊 h_order_info_channel、h_order_detail、h_event 系列。理由詳見文件 §6.4。

四、待使用者確認(8 項,見文件 §7)

  • Q1 主鍵:bigint + code 欄位(建議) vs 保留 varchar 業務碼
  • Q2 order 是否改名為 orders 避開 MySQL 保留字(建議改名)
  • Q3 activity_i18n 是否 M1 就建(建議建,避免日後 ALTER)
  • Q4 session.extra_attributes 用 JSON(本設計) vs EAV 拆表
  • Q5 booking.booking_type 冗餘存於 booking(建議保留)
  • Q6 M1 是否建 form_option_source(建議建)
  • Q7 order.reserved_until 是否用 partial index(須確認正式環境 MySQL 版本)
  • Q8 補充屬性 JSON key 用英文 identifier(本設計) vs 中文

五、下一步

核可後 T01b(#1485)接手,依此 ERD 建 schema 與 entity。
若上述 Q1~Q8 有異議或補充,請直接在此票留 comment。

是由 鍾正剛 於 27 天 前更新

【T01a ERD Rev 2 — 依 review 修訂完成】

文件:docs/features/001-booking-platform/erd.md(898 行,頂部含修訂紀錄表)

一、實際改動的項目

Q1~Q8 裁示落實

  • Q1 依 spec D19/§4.6 重寫 §1.1 主鍵政策;orders PK 改為 order_no varchar(14),取消 order_id 代理鍵與 order_no UK;booking/order_payment/order_invoice 的 FK 欄位改為 order_no varchar(14);索引更名。移除初稿誤設計 legacy_mid,並在文件中說明 M6 遷移舊 mid 原樣沿用即可。加入「對外只露業務碼」與「code→id 入口解析一次」兩條實作要求(避免逐號窮舉與 N+1)。
  • Q2 全文一致改為 orders(Mermaid、表定義、索引命名、prose 皆同步;`order` 只剩兩處是刻意提到「MySQL 保留字/裁示紀錄」的敘述)。
  • Q3 activity_i18n M1 就建(原本即為此設計,無 code 變動)。
  • Q4 session.extra_attributes 用 JSON(同上)。
  • Q5 §4.2 明確寫入 service 層約束:已存在 booking 的 activity 禁止變更 booking_type,並說明實作點(bookingRepository.existsByActivityId)。
  • Q6 form_option_source M1 就建(同上)。
  • Q7 §5.4 完全重寫:MySQL 無 partial index 是事實,改採複合索引 (payment_status, reserved_until);索引命名 idx_orders_payment_status_reserved_until。§5.5 重新盤點索引總數。
  • Q8 §4.1 加入「M3 前置需求」註記:活動設定介面與匯入對應介面須顯示中文 label,不露 identifier;系統內 key 為英文、UI 顯示為中文,這是分工的重點。

三項修正

  • 修正一 §4.5 重寫。移除 AND version = :version;同時移除 session.version 欄位(於 §3.4 亦一併移除)。文件說明兩個原因(冗餘、與 @Version 衝突)與選擇「移除欄位」而非「保留但不標 @Version」的理由(Session 其他變更皆為低頻後台操作,無其他樂觀鎖需求)。T06 測試要求補入第二項「高競爭下的成功率」驗收條件(容量 100 / 20 併發全部成功)。
  • 修正二 §6.1 標題「10 張」改為「11 張」,敘述同步。
  • 修正三 §8 拆為 8.1~8.5,將「M4 兩型可能需要新的補充屬性型態」自「假設」升格為 §8.5 R-1「明確風險」,含衝擊時機(M4 開工時 booking 表已有正式訂單)、三種因應方向(加欄位/加子表/開新表)、責任歸屬(coordinator M4 spec 起草階段 review)。

環境事實

§8.3 將原「假設 MySQL ≥ 8.0」升格為已確認事實:local/staging 8.0.23、production 8.0.26(IP 亦收錄,供 T01b DDL 部署參考)。註明兩者皆無 partial index。

其他一致性修訂

  • Mermaid ORDER→ORDERS、order_id 欄位屬性移除、child 表 FK 改為 order_no varchar(14)、SESSION 移除 bigint version 欄位
  • §5.3 移除 idx_order_no 條目並加註「PK 直接服務,不需另建索引」
  • §3.12 heading 由「M1 只在 Order 預留」改為「M1 只在 orders 預留」
  • prose 中所有 order.<field> 全數改為 orders.<field>
  • §1.3 敘述由「所有 <parent>_id 欄位」擴充為「<parent>_id bigint 或 <parent>_no varchar」以反映新現實

二、對裁示的異議

無。八項裁示與三項修正我全部同意,理由如下:

  • Q1 對不可變 key 加代理鍵確實是為不存在的問題付成本,且 D18 無 FK 的前提下 varchar PK 的效能顧慮已被舊系統的實測(h_order_master.mid PK 是 varchar(11))證偽。
  • Q7 MySQL 無 partial index 是事實,複合 (payment_status, reserved_until) 是最左前綴等值 + 範圍,正好命中;我在 Rev 1 誤稱「partial」而非「functional」是名詞混淆。
  • 修正一 版號檢查在此情境確實冗餘(capacity 條件本身即為原子);@Version 與原生 UPDATE 混用會導致 stale entity 拋假 exception 是常見坑,我在 Rev 1 沒發現這點。
  • 修正三 M4 風險確實比「假設」嚴重 —— 尤其 APPLICATION_REVIEW 型(租借申請)舊系統從未上線,屬全新設計,M4 才會發現真實需求,而屆時 booking 表已有正式訂單。

三、修訂後的待確認事項

無。Rev 1 的 Q1~Q8 全部關閉;Rev 2 沒有新增待確認項目。

§8.4 保留一項假設(M2 agreement.type 至少含 PERCENT 與 FIXED_UNIT_PRICE 兩種),但註明「不擋 M1」,M2 開工時再確認即可。

§8.5 R-1 是明確風險(不是待確認):責任歸屬 M4 spec 起草階段,M1 現在無法完全消除,也不需要在 M1 消除。

四、下一步

Rev 2 可交由 T01b(#1485)進場實作。若 coordinator 認為 Rev 2 仍有需修訂處,請在此票留 comment;否則我等 T01b 派工。

是由 鍾正剛 於 27 天 前更新

【T01a ERD Rev 3 — 依二輪 review 修訂完成】

文件:docs/features/001-booking-platform/erd.md(1041 行,頂部含三版修訂紀錄)

一、改動項目

修正一:form_definition_version 從 orders 搬到 booking

  • Mermaid:ORDERS 移除、BOOKING 新增此欄位
  • §3.5 移除;§3.6 新增(int NOT NULL)
  • §4.6 解讀流程改寫:(code, version) 一組資料現在完全落在 booking 這一層,M1 1:1 與未來 1:N(購物車)皆自然運作

修正二:D20 多語一律走側表

新增/改造 4 張表:

  • form_definition_i18n:(form_definition_id, lang_code, title),UK (form_definition_id, lang_code)
  • form_field_i18n:(form_field_id, lang_code, label, help_text),UK 同上
  • form_option(由 form_option_source 拆分主資料):(option_id PK, payload_key, value, sort_order, enabled),UK (payload_key, value)
  • form_option_i18n:(option_id, lang_code, label),UK 同上

主表 form_definition.title、form_field.label、form_field.help_text 保留為 canonical zh_TW(比照既有 activity + activity_i18n 慣例);側表存其他語系。讀取策略:查當前語系側表,找不到回退主表 zh_TW。M1 只填 zh_TW,多語編輯介面 M3。

修正三:D21 可查詢的動態欄位

  • form_field.searchable bit(1) NOT NULL default b'0' 新增旗標
  • booking_search_attribute 側表:(bsa_id PK, booking_id, field_key, value_text varchar(200))
  • 索引 idx_bsa_field_value(field_key, value_text)(後台篩選)+ idx_bsa_booking(booking_id)(維護)
  • M1 寫入路徑就位;篩選介面 M3
  • 文件明寫否決 generated column 的理由(每加可查欄位就 ALTER TABLE,與 M3 園方自主直接衝突)與 M1 就寫入的理由(避免 backfill;貴的是補資料不是寫程式)

連帶更新

  • §5.5 新增「側表索引」小節;§5.6 重新盤點 —— 各表索引數含 5 張新/改側表;總計約 32 條非 PRIMARY 索引
  • §5.1 移除 idx_activity_i18n_activity(UK 左前綴已服務)並補上一行說明
  • §6.1 M1 建表 11→15;分為核心 8 + 多語側表 4 + 表單選項 1 + 搜尋側表 1,並註明各群組的裁示依據
  • §7 拆為 §7.1 Rev 2 裁示、§7.2 Rev 3 裁示三項;Q6 說明加註「Rev 3 已改為 form_option + form_option_i18n」
  • §8.1 更新 booking.form_definition_version 補充敘述(原寫 orders);新增「i18n 側表結構統一」與 booking_search_attribute 的 value_text 型別選擇說明

二、對三項修正的異議

無。三項修正我全部同意,理由:

  • 修正一 明顯是設計 bug —— 我沒把「M1 1:1、日後拆購物車 1:N」的意圖貫徹到欄位放置。感謝 coordinator 直接點名 D9 的原始意圖,這是我原本該想到的。
  • 修正二 D20 消除了我在 Rev 1/2 混用兩慣例(activity_i18n 側表 vs form_option_source JSON 內嵌)的不一致。依 D14 表單引擎日後可能上收 ss3a-framework,慣例統一極重要。而且拆表後 M3 表單編輯器逐語系編輯的路徑會直觀許多。
  • 修正三 D21 側表 + searchable 旗標是對舊系統病灶(h_order_master 47 欄膨脹)最正確的回應。generated column 方案我原本 Rev 1/2 也沒考慮,coordinator 主動否決並註明理由(避免日後被再提議)是好判斷。M1 就寫入避免 backfill 的判斷也很紮實。

三、新增表之後的建表與索引數

M1 建表總數:15 張

  • 核心 9:activity, price_tier, session, orders, booking, order_payment, order_invoice, form_definition, form_field
  • 多語側表 4:activity_i18n, form_definition_i18n, form_field_i18n, form_option_i18n
  • 表單選項 1:form_option(原 form_option_source 拆分後主表)
  • 搜尋側表 1:booking_search_attribute

(Rev 2 為 11 張。Rev 3 淨增 4:+form_definition_i18n、+form_field_i18n、+form_option_i18n、+booking_search_attribute;form_option 為 form_option_source 拆分後的主表,計為原地重命名)

索引總數:約 32 條非 PRIMARY

  • 核心表(9 張):約 22 條(含 activity 的 UK code、booking 的 6 條、orders 的 4 條等)
  • 多語側表(4 張):4 條 UK
  • 表單選項(1 張):1 條 UK
  • 搜尋側表(1 張):2 條(複合搜尋 + booking_id 反向)
  • 部分索引因複合 UK 已服務左前綴而合併,實作階段可再收斂

(Rev 2 為約 24 條。Rev 3 淨增約 8 條)

四、待確認事項

無。Rev 3 沒有新的待確認項目。

§8.4 保留一項假設(M2 agreement.type 至少含兩種);§8.5 R-1 是 M4 明確風險(責任歸屬 M4 spec 起草階段)。兩者狀態不變。

五、下一步

Rev 3 可交由 T01b(#1485)進場實作。M1 建表擴為 15 張,比原估多 4 張與若干側表寫入路徑;T01b 工作量小幅增加但仍在可控範圍。

是由 鍾正剛 於 27 天 前更新

  • 狀態 從 New 變更為 Resolved

【T01a 已核可】

ERD 經三輪 review 後核可,commit b14e7ce。

產出:docs/features/001-booking-platform/erd.md(Rev 3,1041 行)

  • M1 建表 15 張、非 PRIMARY 索引約 32 條、FK 約束 0 個(D18)

三輪的主要修正

  • Rev 2:8 項裁示(主鍵政策改為 D19「不可變自然鍵當主鍵」、表名 orders、排程索引改複合 (payment_status, reserved_until))+ 名額扣減移除樂觀鎖版號(冗餘且與 JPA @Version 混用會拋假的 OptimisticLockException)
  • Rev 3:表單版本從 orders 移到 booking(D9 拆兩層為未來 1:N,orders 單一 int 無法表達多活動)、多語改側表(D20)、動態欄位可查詢(D21)

coordinator 補記:修訂紀錄索引數與 §5.6 對齊為 32;§5.1 補上 uk_activity_code。

後續由 #1485 T01b 依此 ERD 建表與 entity。

動作

匯出至 Atom PDF