新增訂餐功能
This commit is contained in:
110
app.py
110
app.py
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import mysql.connector
|
import mysql.connector
|
||||||
from mysql.connector import pooling
|
from mysql.connector import pooling
|
||||||
@@ -6,8 +7,6 @@ from flask_cors import CORS
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
# --- 從環境變數讀取資料庫連線資訊 ---
|
# --- 從環境變數讀取資料庫連線資訊 ---
|
||||||
# os.environ.get() 會嘗試讀取環境變數,如果找不到,則使用後面的預設值
|
|
||||||
# 這讓程式碼在本地 (使用預設值) 和 Zeabur (使用環境變數) 都能運作
|
|
||||||
DB_CONFIG = {
|
DB_CONFIG = {
|
||||||
'user': os.environ.get('DB_USER', 'A023'),
|
'user': os.environ.get('DB_USER', 'A023'),
|
||||||
'password': os.environ.get('DB_PASSWORD', 'UkrHhx76kCCM'),
|
'password': os.environ.get('DB_PASSWORD', 'UkrHhx76kCCM'),
|
||||||
@@ -33,7 +32,8 @@ except mysql.connector.Error as err:
|
|||||||
print(f"資料庫連線池建立失敗: {err}")
|
print(f"資料庫連線池建立失敗: {err}")
|
||||||
|
|
||||||
# --- 統一回應格式的輔助函式 ---
|
# --- 統一回應格式的輔助函式 ---
|
||||||
def create_response(status, data, message, code):
|
def create_standard_response(status, data, message, code):
|
||||||
|
"""建立標準的 JSON 回應,包含 data 欄位"""
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"status": status,
|
"status": status,
|
||||||
"code": code,
|
"code": code,
|
||||||
@@ -41,10 +41,23 @@ def create_response(status, data, message, code):
|
|||||||
"data": data
|
"data": data
|
||||||
}), code
|
}), code
|
||||||
|
|
||||||
def create_error_response(message, code):
|
def create_order_success_response(message, order_id, code):
|
||||||
return create_response("error", None, message, code)
|
"""建立符合訂單成功需求的特殊 JSON 回應"""
|
||||||
|
return jsonify({
|
||||||
|
"status": "success",
|
||||||
|
"message": message,
|
||||||
|
"order_id": order_id
|
||||||
|
}), code
|
||||||
|
|
||||||
# --- API Endpoint: /menu ---
|
def create_error_response(message, code):
|
||||||
|
"""建立標準的錯誤 JSON 回應"""
|
||||||
|
return jsonify({
|
||||||
|
"status": "error",
|
||||||
|
"code": code,
|
||||||
|
"message": message
|
||||||
|
}), code
|
||||||
|
|
||||||
|
# --- API Endpoint: /menu (GET) ---
|
||||||
@app.route('/menu', methods=['GET'])
|
@app.route('/menu', methods=['GET'])
|
||||||
def get_menu_by_date():
|
def get_menu_by_date():
|
||||||
menu_date_str = request.args.get('date')
|
menu_date_str = request.args.get('date')
|
||||||
@@ -73,9 +86,9 @@ def get_menu_by_date():
|
|||||||
menu_items = cursor.fetchall()
|
menu_items = cursor.fetchall()
|
||||||
|
|
||||||
if not menu_items:
|
if not menu_items:
|
||||||
return create_response("success", [], "no menu found", 200)
|
return create_standard_response("success", [], "no menu found", 200)
|
||||||
|
|
||||||
return create_response("success", menu_items, "Menu retrieved successfully.", 200)
|
return create_standard_response("success", menu_items, "Menu retrieved successfully.", 200)
|
||||||
|
|
||||||
except mysql.connector.Error as err:
|
except mysql.connector.Error as err:
|
||||||
return create_error_response(f"Database error: {err}", 500)
|
return create_error_response(f"Database error: {err}", 500)
|
||||||
@@ -87,6 +100,83 @@ def get_menu_by_date():
|
|||||||
if conn and conn.is_connected():
|
if conn and conn.is_connected():
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
# --- API Endpoint: /order (POST) ---
|
||||||
|
@app.route('/order', methods=['POST'])
|
||||||
|
def create_order():
|
||||||
|
"""建立一筆新的訂單"""
|
||||||
|
if not request.is_json:
|
||||||
|
return create_error_response("Invalid content type, must be application/json", 415)
|
||||||
|
|
||||||
|
data = request.get_json()
|
||||||
|
|
||||||
|
required_fields = ['employee_id', 'employee_name', 'order_date', 'quantity']
|
||||||
|
missing_fields = [field for field in required_fields if field not in data]
|
||||||
|
if missing_fields:
|
||||||
|
return create_error_response(f"Missing required fields: {', '.join(missing_fields)}", 400)
|
||||||
|
|
||||||
|
try:
|
||||||
|
order_date = data['order_date']
|
||||||
|
quantity = int(data['quantity'])
|
||||||
|
datetime.strptime(order_date, '%Y-%m-%d')
|
||||||
|
if quantity <= 0:
|
||||||
|
raise ValueError("Quantity must be a positive integer.")
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return create_error_response("Invalid data format for date or quantity.", 400)
|
||||||
|
|
||||||
|
if not connection_pool:
|
||||||
|
return create_error_response("Database connection not available.", 503)
|
||||||
|
|
||||||
|
conn = None
|
||||||
|
cursor = None
|
||||||
|
try:
|
||||||
|
conn = connection_pool.get_connection()
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# 設定當前連線的時區為 UTC+8
|
||||||
|
cursor.execute("SET time_zone = '+8:00'")
|
||||||
|
|
||||||
|
# 1. 根據 order_date 查詢 menu_item_id
|
||||||
|
query_menu = "SELECT id FROM menu_items WHERE menu_date = %s LIMIT 1"
|
||||||
|
cursor.execute(query_menu, (order_date,))
|
||||||
|
menu_item = cursor.fetchone()
|
||||||
|
|
||||||
|
if not menu_item:
|
||||||
|
return create_error_response(f"No menu found for date {order_date}", 404)
|
||||||
|
|
||||||
|
menu_item_id = menu_item[0]
|
||||||
|
|
||||||
|
# 2. 將訂單資料寫入資料庫
|
||||||
|
# 使用 NOW() 函數,因為連線時區已設定
|
||||||
|
add_order_query = ("INSERT INTO employee_order "
|
||||||
|
"(emp_id, name, order_date, menu_item_id, order_qty, update_by, update_at) "
|
||||||
|
"VALUES (%s, %s, %s, %s, %s, %s, NOW())")
|
||||||
|
order_data = (
|
||||||
|
data['employee_id'],
|
||||||
|
data['employee_name'],
|
||||||
|
order_date,
|
||||||
|
menu_item_id,
|
||||||
|
quantity,
|
||||||
|
data['employee_id']
|
||||||
|
)
|
||||||
|
cursor.execute(add_order_query, order_data)
|
||||||
|
new_order_id = cursor.lastrowid
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
return create_order_success_response("新增成功", new_order_id, 201)
|
||||||
|
|
||||||
|
except mysql.connector.Error as err:
|
||||||
|
if conn: conn.rollback()
|
||||||
|
return create_error_response(f"Database error: {err}", 500)
|
||||||
|
except Exception as e:
|
||||||
|
if conn: conn.rollback()
|
||||||
|
return create_error_response(f"An unexpected error occurred: {e}", 500)
|
||||||
|
finally:
|
||||||
|
if cursor:
|
||||||
|
cursor.close()
|
||||||
|
if conn and conn.is_connected():
|
||||||
|
conn.close()
|
||||||
|
|
||||||
# --- 全域錯誤處理 ---
|
# --- 全域錯誤處理 ---
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def not_found(error):
|
def not_found(error):
|
||||||
@@ -98,7 +188,5 @@ def method_not_allowed(error):
|
|||||||
|
|
||||||
# --- 啟動應用程式 ---
|
# --- 啟動應用程式 ---
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# Zeabur 會透過 Gunicorn 啟動,這段主要用於本地測試
|
|
||||||
# Zeabur 會自動注入 PORT 環境變數
|
|
||||||
port = int(os.environ.get('PORT', 5000))
|
port = int(os.environ.get('PORT', 5000))
|
||||||
app.run(host='0.0.0.0', port=port, debug=False) # 正式環境 debug 應為 False
|
app.run(host='0.0.0.0', port=port, debug=False)
|
||||||
|
Reference in New Issue
Block a user