API-CIECIETAIPEI / main.py
DeepLearning101's picture
Create main.py
1b7678c verified
raw
history blame
4.81 kB
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Optional, Dict
from supabase import create_client, Client
import os
import requests
import uuid
app = FastAPI(title="Cié Cié Backend API")
# 🌟 解決 CORS (跨域) 問題:允許您的 GitHub Pages 前端呼叫這台主機
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 正式上線可改為 ["https://ciecietaipei.github.io"]
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 讀取環境變數 (請在 Hugging Face Settings 中設定)
SUPABASE_URL = os.getenv("SUPABASE_URL", "")
# ⚠️ 注意:這裡必須使用 service_role key,才能無視 RLS 直接查核黑名單與寫入!
SUPABASE_KEY = os.getenv("SUPABASE_KEY", "")
LINE_ACCESS_TOKEN = os.getenv("LINE_ACCESS_TOKEN", "")
BOSS_LINE_ID = os.getenv("BOSS_LINE_ID", "") # 老闆的 LINE User ID
# 初始化 Supabase
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) if SUPABASE_URL else None
# 定義前端傳來的資料結構 (Payload)
class OrderPayload(BaseModel):
service_type: str
name: str
tel: str
date: str
time: str
line_id: Optional[str] = ""
pax: int = 2
cart: Dict[str, int] = {}
deposit_required: int = 0
total_amount: int = 0
@app.get("/")
def read_root():
return {"status": "online", "message": "Cié Cié FastAPI is running."}
@app.post("/api/submit_booking")
async def submit_booking(payload: OrderPayload):
if not supabase:
raise HTTPException(status_code=500, detail="資料庫未設定")
# ==========================================
# 🕵️‍♂️ 階段 1:查核 No-Show 黑名單
# ==========================================
is_noshow = False
try:
# 用電話號碼去資料庫找有沒有 No-Show 紀錄
res = supabase.table("bookings").select("id").eq("tel", payload.tel).eq("status", "No-Show").execute()
is_noshow = len(res.data) > 0
except Exception as e:
print("查詢黑名單失敗:", e)
# 決定最終訂金:如果是黑名單,原本不用付訂金的也強制收 $1000
final_deposit = payload.deposit_required
if is_noshow and final_deposit == 0:
final_deposit = 1000
# ==========================================
# 💳 階段 2:金流分流處理 (需要收訂金 vs 不用收訂金)
# ==========================================
# 狀況 A:需要付款 (產生 LINE Pay 連結)
if final_deposit > 0:
order_id = f"ORDER-{uuid.uuid4().hex[:8].upper()}"
# ⚠️ 這裡未來會串接真實的 LINE Pay API,目前先回傳模擬的結帳網址
mock_payment_url = f"https://sandbox-web-pay.line.me/web/payment/wait?transactionReserveId=mock&orderId={order_id}"
# 我們不先把資料寫入資料庫,而是等他「付款成功」的 Webhook 再寫入
return {
"status": "require_payment",
"message": "訂單需支付訂金",
"is_noshow_penalty": is_noshow, # 讓前端知道是不是因為被懲罰才要付錢
"deposit_amount": final_deposit,
"payment_url": mock_payment_url,
"order_id": order_id
}
# 狀況 B:不需付款 (直接寫入資料庫並完成訂位)
booking_data = {
"name": payload.name,
"tel": payload.tel,
"date": payload.date,
"time": payload.time,
"pax": payload.pax,
"email": "", # 可由前端擴充
"user_id": payload.line_id,
"status": "待處理",
"remarks": f"類型: {'外帶' if payload.service_type == 'takeout' else '內用'}\n餐點內容: {payload.cart}"
}
try:
supabase.table("bookings").insert(booking_data).execute()
# 🔔 通知老闆有新訂位
notify_boss(payload.name, payload.tel, payload.date, payload.time, payload.pax)
return {
"status": "success",
"message": "訂位已成功建立!"
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"寫入資料庫失敗: {str(e)}")
def notify_boss(name, tel, date, time, pax):
"""發送 LINE 通知給老闆 (需設定環境變數)"""
if not LINE_ACCESS_TOKEN or not BOSS_LINE_ID:
return
msg = f"🔔 【新訂位通知】\n姓名:{name}\n電話:{tel}\n時間:{date} {time}\n人數:{pax}位"
headers = {"Authorization": f"Bearer {LINE_ACCESS_TOKEN}"}
payload = {"to": BOSS_LINE_ID, "messages": [{"type": "text", "text": msg}]}
try:
requests.post("https://api.line.me/v2/bot/message/push", headers=headers, json=payload)
except:
pass