DeepLearning101 commited on
Commit
1b7678c
·
verified ·
1 Parent(s): 7fe92f9

Create main.py

Browse files
Files changed (1) hide show
  1. main.py +127 -0
main.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from pydantic import BaseModel
4
+ from typing import Optional, Dict
5
+ from supabase import create_client, Client
6
+ import os
7
+ import requests
8
+ import uuid
9
+
10
+ app = FastAPI(title="Cié Cié Backend API")
11
+
12
+ # 🌟 解決 CORS (跨域) 問題:允許您的 GitHub Pages 前端呼叫這台主機
13
+ app.add_middleware(
14
+ CORSMiddleware,
15
+ allow_origins=["*"], # 正式上線可改為 ["https://ciecietaipei.github.io"]
16
+ allow_credentials=True,
17
+ allow_methods=["*"],
18
+ allow_headers=["*"],
19
+ )
20
+
21
+ # 讀取環境變數 (請在 Hugging Face Settings 中設定)
22
+ SUPABASE_URL = os.getenv("SUPABASE_URL", "")
23
+ # ⚠️ 注意:這裡必須使用 service_role key,才能無視 RLS 直接查核黑名單與寫入!
24
+ SUPABASE_KEY = os.getenv("SUPABASE_KEY", "")
25
+
26
+ LINE_ACCESS_TOKEN = os.getenv("LINE_ACCESS_TOKEN", "")
27
+ BOSS_LINE_ID = os.getenv("BOSS_LINE_ID", "") # 老闆的 LINE User ID
28
+
29
+ # 初始化 Supabase
30
+ supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) if SUPABASE_URL else None
31
+
32
+ # 定義前端傳來的資料結構 (Payload)
33
+ class OrderPayload(BaseModel):
34
+ service_type: str
35
+ name: str
36
+ tel: str
37
+ date: str
38
+ time: str
39
+ line_id: Optional[str] = ""
40
+ pax: int = 2
41
+ cart: Dict[str, int] = {}
42
+ deposit_required: int = 0
43
+ total_amount: int = 0
44
+
45
+ @app.get("/")
46
+ def read_root():
47
+ return {"status": "online", "message": "Cié Cié FastAPI is running."}
48
+
49
+ @app.post("/api/submit_booking")
50
+ async def submit_booking(payload: OrderPayload):
51
+ if not supabase:
52
+ raise HTTPException(status_code=500, detail="資料庫未設定")
53
+
54
+ # ==========================================
55
+ # 🕵️‍♂️ 階段 1:查核 No-Show 黑名單
56
+ # ==========================================
57
+ is_noshow = False
58
+ try:
59
+ # 用電話號碼去資料庫找有沒有 No-Show 紀錄
60
+ res = supabase.table("bookings").select("id").eq("tel", payload.tel).eq("status", "No-Show").execute()
61
+ is_noshow = len(res.data) > 0
62
+ except Exception as e:
63
+ print("查詢黑名單失敗:", e)
64
+
65
+ # 決定最終訂金:如果是黑名單,原本不用付訂金的也強制收 $1000
66
+ final_deposit = payload.deposit_required
67
+ if is_noshow and final_deposit == 0:
68
+ final_deposit = 1000
69
+
70
+ # ==========================================
71
+ # 💳 階段 2:金流分流處理 (需要收訂金 vs 不用收訂金)
72
+ # ==========================================
73
+
74
+ # 狀況 A:需要付款 (產生 LINE Pay 連結)
75
+ if final_deposit > 0:
76
+ order_id = f"ORDER-{uuid.uuid4().hex[:8].upper()}"
77
+
78
+ # ⚠️ 這裡未來會串接真實的 LINE Pay API,目前先回傳模擬的結帳網址
79
+ mock_payment_url = f"https://sandbox-web-pay.line.me/web/payment/wait?transactionReserveId=mock&orderId={order_id}"
80
+
81
+ # 我們不先把資料寫入資料庫,而是等他「付款成功」的 Webhook 再寫入
82
+ return {
83
+ "status": "require_payment",
84
+ "message": "訂單需支付訂金",
85
+ "is_noshow_penalty": is_noshow, # 讓前端知道是不是因為被懲罰才要付錢
86
+ "deposit_amount": final_deposit,
87
+ "payment_url": mock_payment_url,
88
+ "order_id": order_id
89
+ }
90
+
91
+ # 狀況 B:不需付款 (直接寫入資料庫並完成訂位)
92
+ booking_data = {
93
+ "name": payload.name,
94
+ "tel": payload.tel,
95
+ "date": payload.date,
96
+ "time": payload.time,
97
+ "pax": payload.pax,
98
+ "email": "", # 可由前端擴充
99
+ "user_id": payload.line_id,
100
+ "status": "待處理",
101
+ "remarks": f"類型: {'外帶' if payload.service_type == 'takeout' else '內用'}\n餐點內容: {payload.cart}"
102
+ }
103
+
104
+ try:
105
+ supabase.table("bookings").insert(booking_data).execute()
106
+
107
+ # 🔔 通知老闆有新訂位
108
+ notify_boss(payload.name, payload.tel, payload.date, payload.time, payload.pax)
109
+
110
+ return {
111
+ "status": "success",
112
+ "message": "訂位已成功建立!"
113
+ }
114
+ except Exception as e:
115
+ raise HTTPException(status_code=500, detail=f"寫入資料庫失敗: {str(e)}")
116
+
117
+ def notify_boss(name, tel, date, time, pax):
118
+ """發送 LINE 通知給老闆 (需設定環境變數)"""
119
+ if not LINE_ACCESS_TOKEN or not BOSS_LINE_ID:
120
+ return
121
+ msg = f"🔔 【新訂位通知】\n姓名:{name}\n電話:{tel}\n時間:{date} {time}\n人數:{pax}位"
122
+ headers = {"Authorization": f"Bearer {LINE_ACCESS_TOKEN}"}
123
+ payload = {"to": BOSS_LINE_ID, "messages": [{"type": "text", "text": msg}]}
124
+ try:
125
+ requests.post("https://api.line.me/v2/bot/message/push", headers=headers, json=payload)
126
+ except:
127
+ pass