本專案是一個以 Flask + MySQL + Nginx + Docker Compose 打造的寵物店網站範例,適合學習網站全端開發與容器化部署。
本網站圖片都是從網路上隨機取得,教學使用 根據本範例修改爲其他商業網站,請記得更換圖片!
[使用者瀏覽器]
│
▼
[Nginx] ← 靜態檔案(圖片、CSS、JS)
│
▼
[Flask (app.py)]
│
▼
[MySQL 資料庫]
- Nginx:反向代理,負責接收外部請求,將 API 請求轉發給 Flask,並直接提供靜態檔案(/static)。
- Flask:Python Web 框架,負責處理動態頁面、路由、表單、資料庫互動。
- MySQL:關聯式資料庫,儲存網站資料(最新消息、服務、聯絡表單等)。
- Docker Compose:一鍵啟動所有服務,並用 volume 掛載本機程式碼,方便開發與同步。
-
下載專案
git clone git@github.com:amostsai/petshop.git cd petshop -
準備 Docker Secrets
- 編輯
secrets/目錄內的三個檔案:mysql_root_password.txtmysql_user_password.txtflask_secret_key.txt
- 將內容改成自己的安全密碼/金鑰。範例值僅供教學使用。
- 編輯
-
啟動服務
docker compose up
- 第一次啟動會自動建立資料庫與 seed 假資料。
- 預設網站入口:http://localhost
-
關閉服務
docker compose down
-
若修改靜態檔案(html, css, js, jpg, png, gif等檔案)
docker compose down docker compose up
petshop/
├── app/ # Flask 主程式
│ ├── __init__.py # 匯出 create_app 供 flask run 使用
│ ├── app.py # Flask application factory + feature 註冊
│ ├── config.py # 分環境設定與秘密讀取邏輯
│ ├── features/ # 依網站功能分組的模組
│ │ ├── main/ # 首頁
│ │ ├── news/ # 最新消息
│ │ ├── contact/ # 聯絡我們
│ │ ├── services/ # 服務介紹
│ │ └── about/ # 關於我們
│ ├── lib/ # 共用工具(資料庫、快取、錯誤類別等)
│ ├── static/ # 靜態檔案(CSS, JS, 圖片)
│ └── templates/ # 共用模板(如 base.html)
├── env/
│ ├── flask/ # Dockerfile
│ ├── mysql/ # 資料庫初始化 SQL
│ └── nginx/ # Nginx 設定
├── docker-compose.yml # 一鍵啟動所有服務
└── README.md # 專案說明
- 每個 feature 資料夾集中該功能所需的 Blueprint (
__init__.py)、路由 (routes.py)、服務與資料存取層(視需求提供service.py、repository.py)、以及對應模板 (templates/)。
- Blueprint 分層:如何將不同功能模組化,方便維護與擴充。
- 資料庫連線:如何用 Python 連接 MySQL,並安全寫入資料。
- 表單處理:前端驗證 + 後端驗證 + 資料寫入 DB。
- 靜態檔案與模板:Nginx 如何直接服務 /static,Flask 如何渲染 Jinja2 模板。
- Docker volume:程式碼即時同步到 container,開發方便。
- 一鍵啟動:用 docker compose up 即可啟動所有服務。
/products顯示商品分類、列表與詳細頁,可將有庫存的商品加入購物車。- 購物車支援調整數量、移除商品與查看訂單摘要,並在導覽列顯示件數徽章。
- 結帳流程會寫入訂單與明細至 MySQL,並導向綠界測試付款頁。付款完成後可回到感謝頁查看訂單編號與付款狀態。
本專案預設使用綠界 ECPay STAGE 測試環境:
- 付款網址:
https://payment-stage.ecpay.com.tw/Cashier/AioCheckOut/V5 - 測試 MerchantID:
3002607 - 測試 HashKey / HashIV 已在開發設定中提供,可用環境變數覆寫。
可用環境變數:
ECPAY_CHECKOUT_URL=https://payment-stage.ecpay.com.tw/Cashier/AioCheckOut/V5
ECPAY_MERCHANT_ID=3002607
ECPAY_HASH_KEY=你的 HashKey
ECPAY_HASH_IV=你的 HashIV
ECPAY_RETURN_URL=https://你的公開網址/cart/ecpay/return
ECPAY_CLIENT_BACK_URL=https://你的公開網址/cart/thank-you/訂單編號注意:綠界付款完成後會由綠界伺服器呼叫 ECPAY_RETURN_URL。本機 Docker 的 localhost 無法被綠界連到;若要測試訂單自動更新為 paid,請使用 ngrok 或其他公開測試網址,例如:
ECPAY_RETURN_URL=https://example.ngrok-free.app/cart/ecpay/return docker compose up若服務已經在執行,設定公開 callback 後需要重建 Flask container 讓環境變數進入容器:
ECPAY_RETURN_URL=https://example.ngrok-free.app/cart/ecpay/return docker compose up -d --force-recreate flask
docker compose exec flask env | grep ECPAY_RETURN_URL付款後若平台仍顯示待付款,先確認 Flask log 是否有收到綠界 callback:
docker compose logs --tail=200 flask | grep /cart/ecpay/return如果沒有任何 /cart/ecpay/return 紀錄,代表綠界沒有成功打到平台,通常是 ECPAY_RETURN_URL 沒有設定成公開網址、沒有傳進 container,或 ngrok/防火牆沒有轉到本機服務。
完成公開 callback smoke test 後,再封存 OpenSpec 任務:
-
用 ngrok 或公開測試網址設定
ECPAY_RETURN_URL=https://.../cart/ecpay/return。 -
完成一次綠界測試付款。
-
確認該訂單的
payment_status變成paid。 -
勾選
openspec/changes/add-ecpay-test-payment/tasks.md的2.3 Smoke test checkout in Docker with an externally reachable callback URL。 -
執行封存並驗證:
openspec archive add-ecpay-test-payment --yes openspec validate --strict
目前此 OpenSpec change 先保留在 active changes,避免在公開 callback 驗證完成前封存。
若你是在既有 MySQL volume 上加入此功能,env/mysql/init.sql 不會自動重新套用。可選擇重建資料庫:
docker compose down -v
docker compose up或手動補上訂單付款欄位:
ALTER TABLE orders
ADD COLUMN payment_status ENUM('pending','paid','failed') NOT NULL DEFAULT 'pending' AFTER total_amount,
ADD COLUMN ecpay_trade_no VARCHAR(20) NULL AFTER payment_status,
ADD COLUMN ecpay_payment_type VARCHAR(20) NULL AFTER ecpay_trade_no,
ADD COLUMN ecpay_payment_date DATETIME NULL AFTER ecpay_payment_type,
ADD COLUMN ecpay_return_code VARCHAR(10) NULL AFTER ecpay_payment_date,
ADD COLUMN ecpay_return_message VARCHAR(255) NULL AFTER ecpay_return_code;- 安裝相依套件後可在專案根目錄執行
pytest,預設會載入APP_ENV=testing的 Flask 設定。 - 測試使用 fixtures 模擬資料庫回傳值與購物車狀態,不需要啟動 MySQL 或額外 seeding。
- 若要觀察重新導向流程,可加上
-s或--maxfail=1輕鬆除錯。 - 亦可透過 Docker 執行:
docker compose --profile test run --rm pytest(或COMPOSE_PROFILES=test docker compose run --rm pytest),會使用 testing 組態並在容器內跑完整測試。
後台入口:/admin/orders
預設開發帳密:
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin可透過環境變數或 Docker secret 覆寫。正式環境請務必更換預設密碼。
後台目前提供:
- 訂單列表與付款狀態篩選:
pending、paid、failed、cancelled - 訂單詳情:顧客資訊、取貨方式、備註、商品明細、總金額、綠界交易資訊
- 本地取消:可將
pending或failed訂單標記為cancelled
注意:目前取消只是站內狀態,不會呼叫綠界退款或取消 API。若訂單已標記為 cancelled,後續晚到的綠界 callback 不會自動把狀態改成 paid。
若你是在既有 MySQL volume 上加入後台取消狀態,需讓 orders.payment_status 支援 cancelled:
ALTER TABLE orders
MODIFY COLUMN payment_status ENUM('pending','paid','failed','cancelled') NOT NULL DEFAULT 'pending';- 嘗試新增一個「留言板」功能,練習 blueprint、資料庫設計、模板渲染。
- 修改 Nginx 設定,體驗靜態檔案快取。