老系統還在跑 SQL Server 2000,我用 LINE 幫運動場館做了一套預約系統
2026-10-17
阿茲
7
運動場館的場地預約還停在電話與櫃台人工登記?本文分享如何在不更動舊系統(SQL Server 2000)的前提下,用 LINE 官方帳號、Cloudflare Workers 與 D1 資料庫打造會員自助預約系統:從時段計算引擎、後台審核、開打前自動提醒,到把訂單匯出回舊系統的完整流程,以及上線後實際踩到的問題與修正。
第一次跟這家運動場館開會,對方打開他們的管理系統給我看。
那是一套單機版的舊系統,後面接的是 SQL Server 2000。會員資料、場地、預約全部在裡面,已經用了很多年。系統沒壞,員工也用得很熟,唯一的問題是:它只存在櫃台那台電腦上。
想訂場地的人只有兩條路,打電話,或是直接到現場。櫃台接起電話,一邊翻系統看哪個時段有空,一邊跟客人來回確認:「七點那場被訂走了,七點半可以嗎?打一個小時?」確認完再手動輸入。遇到週末晚上的熱門時段,電話會一通接一通。
老闆的需求其實很單純:想先試試看,讓會員用 LINE 自己預約,行不行得通。
我第一個決定:不要動那套舊系統
很多人聽到「數位轉型」,第一個念頭是把舊系統整個換掉。
我評估之後反而先把這個選項排除。那套系統裡有多年的會員資料和作業習慣,員工也都熟悉,硬換一套新的,風險和成本都太高,也不是客戶現在真正要解決的問題。
所以我把流程定成這樣:
LINE 預約 → 後台審核 → 匯出資料 → 匯入舊系統

新系統只負責「收預約」這一段。會員在 LINE 上預約、管理者在後台審核,審核通過的訂單再透過匯出 API 整批拉出來,交給原本的系統處理。匯出時只拿「已審核通過、而且還沒匯出過」的訂單,同一筆資料不會被重複匯入。
舊系統繼續做它擅長的事,新系統只補上原本缺的那個入口。
從 Google Sheets 改成 Cloudflare
一開始的提案,資料庫用的是 Google Sheets。便宜、快,管理者也看得懂,拿來做概念驗證很適合。
但把需求攤開來細談,我發現預約系統有一個 Sheets 很難處理的問題:兩個人同時訂同一個時段。
週五晚上八點的場地,可能同時有好幾個人在搶。試算表沒有交易機制,兩筆寫入同時進來,很難保證只有一筆成功。
最後正式版改用 Cloudflare Workers 加上 D1 資料庫,框架用 Hono。好處是:
- 不用架主機,也不需要固定 IP
- 以這個場館的預約量,Cloudflare 免費方案就夠用
- D1 是真的關聯式資料庫,可以在寫入時做衝突檢查
整套架構長這樣:
會員第一次加入官方帳號時,會先透過 LIFF 綁定姓名、手機和會員卡號。之後從圖文選單點「預約場地」,依序選場地、日期、時長、時段,最後按確認,全程在 LINE 對話裡完成,不需要另外下載 App。
花最多時間的,是「時段」

要我選這個專案最難的部分,答案不是 LINE 串接,也不是後台,而是時段計算。
客戶的規則聽起來很簡單:每次預約 60 分鐘、時段每 15 分鐘跳一格、前後各留 15 分鐘緩衝。實際寫下去,要考慮的東西一層疊一層:
- 營業時間從哪裡來:平常照每週模板,但遇到國定假日要整天公休,遇到比賽要改營業時間。所以優先序是「公休」最大,其次「特殊營業時間」,最後才是每週模板。
- 已經被訂走的時段:待審核和已確認的預約都要算佔用,而且要把前後緩衝一起扣掉。
- 當天預約:只能訂兩小時以後的時段,不然場館來不及準備。
- 最後確認:使用者從看到時段到按下確認,中間可能已經有人先訂走了。所以送出時要在寫入資料庫的那一刻再檢查一次衝突,不能只相信畫面上顯示的結果。
上線後還遇到一個很小、但使用者一眼就看得出來的問題。
某個場地前一筆預約結束加上緩衝後,空檔從 08:10 開始。系統照「每 15 分鐘一格」往下算,列出來的時段是 08:10、08:25、08:40。
程式邏輯沒錯,但沒有人會約「八點二十五分打球」。

後來我把規則改成:保留空檔的起點,之後對齊整點刻度。同樣的情況現在會列出 08:10、08:15、08:30、08:45。為了這個修正,我補了十幾組測試案例,包括跨小時、跨休息時段、23:30 以後不能跨日,還有 20、45 分鐘這類除不盡的間隔。
時段計算這種功能很少人會特別注意到,但只要算錯一次,客人就會打電話到櫃台問。

審核、提醒,還有「看不到的排程」
場館希望預約先經過人工審核,所以會員送出後是「待審核」。管理者在後台按下通過或拒絕,系統會立刻推播一張 LINE 卡片給會員;拒絕時可以寫原因,會員也能直接點按鈕重新預約。

後來客戶又加了一個需求:開打前兩小時提醒。
個用 Cloudflare 的 Cron Trigger 做,每 30 分鐘跑一次,找出接下來兩小時內要開打、還沒提醒過的預約,逐一推播。有兩個細節我特別處理:
- 已經過去的預約絕對不發。排程如果延遲或補跑,不能在人家打完球之後才收到「提醒您兩小時後開打」。
- 排程每次執行都要留紀錄。Cron 在背景跑,沒有畫面。一開始客戶問「提醒到底有沒有發」,我自己也只能去翻 log。後來我讓排程每次跑完都把執行時間、查詢時段、推播筆數寫回資料庫,後台儀表板直接顯示,管理者也能手動觸發一次檢查。
使用者看不到的程式,更需要有地方讓人確認它有沒有正常執行。
後來加的「延長時間」
上線一段時間後,場館提出一個很實際的需求:客人打到一半想多打半小時,櫃台要能直接幫他延長。
這個功能看起來只是改一個結束時間,實際上要處理的比新增預約還多:延長後會不會撞到下一組?會不會超過營業時間?兩個管理者同時在不同電腦按延長怎麼辦?
我的做法是讓延長和新增預約共用同一套衝突檢查,寫入時再用版本號確認資料沒有被別人改過,每一次延長都留下異動紀錄。後台只給「+30 分鐘」和「+60 分鐘」兩個按鈕,按下去先試算,確認沒問題才送出。

做完這套系統,我的幾個體會
回頭看,這個專案從報價到上線,真正的需求其實一直都很具體:
- 會員不用打電話,自己在 LINE 上就能看到空檔
- 櫃台不用一邊接電話一邊翻系統
- 同一個時段不會被訂兩次
- 審核結果和開打提醒會自己送到會員手上
- 訂單可以整批匯回原本的系統,舊系統照常使用
沒有 AI,也沒有什麼大型平台。就是 LINE、Cloudflare、一個資料庫,加上一套把時段算對的邏輯。

不過我也越來越確定一件事:傳統產業的數位化,很多時候不是把舊的換掉,而是在旁邊補上一個新的入口。舊系統有它的價值,員工的習慣也是。先找到流程裡最卡的那一段,把它接上線,其他的可以慢慢來。
至於那個 08:25 的問題,它提醒我:系統「沒有錯」和使用者「用得順」,中間還差很多。這段差距通常要到現場看人實際使用,才找得到。