---
title: "我如何建立系統：QR Code 行動點餐共用核心"
description: "一套服務多品牌、多場域的雲端點餐系統，整合內用、外帶、院內配送、套餐與多品牌菜單等複雜情境，已應用於桃園機場、臺北榮總、南門市場與 POPEYES。我負責操作流程、UI/UX 與前端開發。"
url: https://dasto.app/portfolio/systems/
language: zh-Hant
alternate_en: https://dasto.app/portfolio/en/systems/
author: "Daco Hsieh 謝明達"
published: 2026-07-05
updated: 2026-08-06
---

# 我如何建立系統：QR Code 行動點餐共用核心

> 一套服務多品牌、多場域的雲端點餐系統，整合內用、外帶、院內配送、套餐與多品牌菜單等複雜情境，已應用於桃園機場、臺北榮總、南門市場與 POPEYES。我負責操作流程、UI/UX 與前端開發。

![酢漿草標本主視覺](https://dasto.app/portfolio/assets/case5-oxalis-hero-B8jIFuYo.webp)

- **Specimen**: QR Code 行動點餐共用核心
- **Category**: Frontend Systems / Ordering Flow / Shared Architecture
- **Location**: Taiwan
- **Field Span**: 10 months to first launch, then 2.5 years of maintenance
- **Collector**: Daco
- **Role**: Sole Frontend Engineer / System Design / Integration

30 秒看懂這個專案

這是一套服務不同品牌與場域的雲端點餐系統。我參與操作流程、UI／UX 與前端開發，整合內用、外帶、院內配送、套餐選擇、多品牌菜單及 QR Code 參數等複雜情境。系統已實際應用於桃園機場、臺北榮總、南門市場、馬修嚴選與 POPEYES 等場域。

- **專案類型**: 企業系統／餐飲科技
- **我的角色**: UI／UX、前端開發、流程規劃
- **核心能力**: 複雜流程、跨場域部署、工程落地

在智維任職期間，我負責建立並持續維護一套行動點餐前端系統。使用者從 QR Code 進入後，網址會帶入品牌、店家與場域分區等參數，再依設定取得對應的店家、菜單、付款方式與服務流程。

這套系統後來正式導入桃園機場、榮總美食街、南門市場、馬修嚴選與 POPEYES，另有四至五個場域處於測試階段。它不是只支援一家店，而是要支援多品牌、多場域、多付款、多語系與不同取餐邏輯。

![桃園機場場域中的店家選擇畫面](https://dasto.app/portfolio/assets/airport-store-grid-DcDQCmDQ.webp)

*圖 1 — 系統必須能在同一套核心下，承接一個場域中數十家不同餐飲品牌的入口與營運狀態。*

從第一版開發到首個場域正式上線，歷時約十個月。之後兩年半，我持續負責新場域導入、功能擴充與共用核心維護。這個 case 對我來說，不只是「做出點餐頁面」，而是把真實商業世界裡大量例外與限制，整理成一套可以重複部署的前端系統。

我是專案中唯一的前端工程師，負責前端架構、API 串接、套餐與加價購規則、購物車與即時計價、多語系、多種電子支付、訂單送出與查詢，以及多專案共用核心的維護。UI 視覺雖由設計端提供，但多半只有靜態圖，互動規格並不完整，因此我實際上需要把靜態畫面轉化成能被人正確操作的系統。

這個專案最核心的挑戰，不是把菜單顯示出來，而是把複雜餐飲規則轉成使用者不會出錯的流程。

一份套餐可能同時包含必選項目、數量上下限、加價購、多層子選項、互斥條件、特定品牌規則，以及不同數量與價格組合。API 會一次回傳完整的 JSON 規則資料，但不會在使用者每一步操作時重新幫你驗證，所以所有流程判斷都必須由前端完成。

![套餐選項步驟畫面](https://dasto.app/portfolio/assets/airport-option-flow-Co1UmwYt.webp)

*圖 2 — 套餐流程的重點不是把選項列出來，而是把規則拆進每一步，讓使用者自然完成一筆合法訂單。*

我會逐層解析 API 回傳的規則，並把驗證放進每一個操作階段，而不是等到加入購物車或結帳時才告訴使用者訂單不合法。像是飲料選滿兩杯後就不能再選第三杯；想修改時，也可以只清除飲料區段重新選擇，而不需要把整份套餐整個刪掉重來。

我的設計原則是：使用者不必理解所有規則，也能自然完成一筆合法訂單。這其實是很典型的系統設計問題。你不是消滅複雜度，而是把複雜度藏到正確的位置，讓人不需要直接面對它。

為了避免未完成的套餐直接污染正式訂單，我把系統中的狀態拆成兩層：第一層是尚未加入購物車、仍在編輯中的商品物件；第二層才是正式的購物車物件。只有當商品完成所有必要選項後，才會被加入購物車，加入完成後編輯中的物件就會清空。

相同商品加入購物車時，系統也不能只比對商品 ID。我會逐層比較商品與所有客製選項：商品與客製完全相同才合併數量；商品相同但客製不同，就保留為兩筆獨立商品。這種拆法讓使用者可以在各步驟間來回修改，而不會破壞已加入購物車的內容。

金額計算也是同樣的思路。總金額從點餐開始就一路顯示在畫面下方，因此每一個操作都必須立即反映正確價格。可能影響金額的，不只商品數量，還包含套餐內容、加價購、固定折抵、百分比折扣、折價券、返回前一步重新選擇，以及特定品牌優惠。

我沒有直接在既有金額上做加減，而是每次狀態改變後，都依完整商品與購物車內容重新計算。這樣可以避免使用者多次修改、返回與取消後，因為增量運算而累積誤差。像 POPEYES 的「自備飲料杯折 5 元」這類無法完全由共用資料描述的需求，則會保留在品牌客製邏輯中。

另一個關鍵工作，是把前端互動狀態轉成零容錯的 API 訂單格式。訂單 API 的 JSON 結構非常複雜，套餐與客製內容可能形成二十層以上的巢狀資料，而且 API 對 request 幾乎沒有容錯。即使某個商品沒有客製內容，也不能省略欄位，必須保留 key 並明確傳入 `null`。

因此，我另外建立了訂單標準化與檢查函式，專門負責判斷商品是單點或套餐、逐層整理客製資料、補齊必要欄位、檢查型別、轉換空值格式、移除前端用但 API 不接受的欄位，最後再產生標準訂單 JSON。整個資料流程可以整理成：使用者操作 → 編輯中商品 → 購物車訂單 → 訂單標準化 → API request。

前端負責維持順暢互動與即時金額；訂單送出後，後端會重新驗證商品規則與價格，並回傳訂單編號。前端再以訂單編號重新查詢一次正式訂單，取得後端確認的價格後，才進入金流流程。也就是說：前端負責即時體驗，後端保有最終訂單與金額的決定權。

這套系統雖然共用相同核心，但會依不同場域需求載入不同功能。像桃園機場會依 QR Code 中的分區參數顯示不同店家與菜單；榮總除了內用與外帶，也支援院內配送與指定送達時間；其他測試中的場域則包含外送員媒合機制。桃園機場與南門市場也支援多語系，語系會保存在 localStorage，下次進入時沿用上次選擇。

![多語系菜單畫面](https://dasto.app/portfolio/assets/airport-menu-multilang-CMHuFPkw.webp)

*圖 3 — 同一套核心還必須承接多語系與不同場域配置，畫面、菜單與流程都會跟著參數切換。*

系統支援 Apple Pay、Google Pay、LINE Pay、街口支付與臨櫃付款。這些流程並不是打一支 API 就結束，而是需要前端、後端與金流服務多次交換資料。也因此，前端不只是「接畫面」，而是整條交易流程中的狀態協調者。

在場域導入層面，我把共用邏輯集中維護在 Git submodule 中，包含 API 串接、商品與套餐處理、購物車、即時計價、訂單格式化、訂單查詢、頁面管理、多語系、金流流程，以及共用事件與介面元件。各場域有自己的專案，但核心邏輯不需要重寫。

![南門市場店家選擇畫面](https://dasto.app/portfolio/assets/nanmen-store-grid-Rld6mK8D.webp)

*圖 4 — 南門市場是另一種場域型態，店家結構與營運內容不同，但仍能沿用同一套參數驅動的前端核心。*

當共用核心更新時，各場域只需要更新 submodule，就能同步取得最新版功能，而不需要逐案複製與修改。若新場域後台資料與基礎參數都已設定完成，只需要帶入品牌、店家與場域參數，系統在十分鐘內就能呈現基本版本；只有遇到特殊流程或品牌需求時，才需要再進行前端調整。

![另一個多店家場域的選擇畫面](https://dasto.app/portfolio/assets/imei-store-grid-BKi6zyqH.webp)

*圖 5 — 不同美食街或複合場域可以共用核心邏輯，再依品牌與店家資料快速生成各自的入口。*

這是這個專案最重要的成果之一：從逐案開發，逐步建立成一套以參數驅動、可快速複製的前端系統。它讓新需求不再從零開始，也讓維護成本不會隨場域數量線性成長。

有趣的是，雖然這個專案沒有使用 React，而是基於 JavaScript 與 jQuery 開發，我仍然用元件化的方式拆分功能，透過 `.load()` 動態載入頁面模組。每當使用者進入新的操作階段，就建立一個新的內容層，再用 `layer` 管理目前的 `z-index`，讓人可以前進、返回，又不必整頁重載。

由於瀏覽器原生上一頁可能造成資料遺失，我也加入攔截與警告機制，讓使用者可以選擇停留在目前頁面。這其實是一種在舊技術限制下，盡量建立單頁操作體驗的做法。

在開發與測試上，我也沒有每次都從掃描 QR Code 開始，而是會直接在 Chrome DevTools 中注入特定購物車物件，或加入暫時的自動執行流程，讓系統直接進入套餐特定階段、購物車、結帳、金流或訂單查詢。這讓我能快速重現問題，專注驗證金額、JSON 結構與付款流程。

如果今天重新開發，我會改用 React，並加入 TypeScript、明確的狀態管理與自動化測試，讓複雜套餐、金額與訂單資料更容易維護。但我仍會保留當時幾個重要原則：讓 API 規則驅動畫面、把編輯中狀態與正式購物車分開、每次異動後重新計算價格、在每一步阻止錯誤，以及把 API request 轉換集中在單一標準化出口。

## 相關案例

- [IndieSurvive：把邏輯系統包進遊戲形式](https://dasto.app/portfolio/systems/indiesurvive/) — IndieSurvive 把獨立開發者的生存壓力、資源分配與風險管理，做成一套有規則、有平衡機制的終端機風格模擬遊戲。

---

作者: Daco Hsieh 謝明達 · 聯絡: enodaco@gmail.com · 作品集首頁: https://dasto.app/portfolio/ · English version: https://dasto.app/portfolio/en/systems/
