commit 914205573255e876ded42f249e17b255e4375450 Author: PJ-PanSC Date: Mon Sep 8 16:24:41 2025 +0800 Initial commit diff --git a/.env b/.env new file mode 100644 index 0000000..2226edf --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +DB_HOST=mysql.theaken.com +DB_PORT=33306 +DB_NAME=db_A021 +DB_USER=A021 +DB_PASSWORD=wJk1O5qtP7pf \ No newline at end of file diff --git a/20250908_AI Prompt.txt b/20250908_AI Prompt.txt new file mode 100644 index 0000000..34adb14 --- /dev/null +++ b/20250908_AI Prompt.txt @@ -0,0 +1,35 @@ +請執行hw3_Products_lowMargin.md prompt 內容 + + + +您一是資深程式設計師,請根據以下需求幫我產生一段 Python 程式碼,能夠連線到資料庫一個 pizzas table及新增10筆資料。 +資料庫資訊:請參考Z:\E\AI\AI_Coding\DB>.env。 +需求: +1.create pizzas TABLE,pizzas table 的欄位有:name(名稱)、size(尺寸)、price(價格) +2.請您在產生10筆模擬資料新增到 pizzas table。 +3.執行成功後,印出「pizzas 的模擬資料!」。 +4.請將產生出的 Python 儲存於 Z:\E\AI\AI_Coding\20250908> 路徑下。 + + +-------------------------------------------------------------------------------------------------------------------------- + +請產生一支可直接執行的 Flask API 伺服器,需求: +- 使用 mysql.connector 連線 + +資料庫資訊:請參考Z:\E\AI\AI_Coding\DB>.env。 +TABLE = users + +- 路由: + - GET /v1/users:支援 ?min_age、?max_age、?page、?limit,要多回傳 {meta} + - GET /v1/users/:找不到回 404 + - POST /v1/users:接收 JSON {name,email,age},欄位驗證,成功回 201 並回新資料 + - PATCH /v1/users/:可更新任一欄位(name/email/age),成功回 200 回更新後資料 + - DELETE /v1/users/:成功回 204 + +- 所有寫入請用參數化查詢避免 SQL 注入 +- 統一格式 { status,code,message,data } +- 統一錯誤格式 {status:"error", code, message} +- 啟用 CORS(允許本機前端) + + + diff --git a/create_and_insert_pizzas.py b/create_and_insert_pizzas.py new file mode 100644 index 0000000..ce85ad6 --- /dev/null +++ b/create_and_insert_pizzas.py @@ -0,0 +1,60 @@ +import mysql.connector +from mysql.connector import Error +import random + +# 資料庫連線資訊 +db_config = { + 'host': 'mysql.theaken.com', + 'port': 33306, + 'database': 'db_A021', + 'user': 'A021', + 'password': 'wJk1O5qtP7pf' +} + +# 披薩名稱列表 +pizza_names = [ + '瑪格麗特', '夏威夷', '海鮮總匯', '燻雞蘑菇', '蔬菜總匯', + '德式香腸', '四種起司', '韓式泡菜', '泰式酸辣', 'BBQ烤肉' +] + +# 披薩尺寸列表 +pizza_sizes = ['S', 'M', 'L', 'XL'] + +def create_and_insert_pizzas(): + try: + # 建立資料庫連線 + connection = mysql.connector.connect(**db_config) + cursor = connection.cursor() + + # 創建pizzas表 + cursor.execute(""" + CREATE TABLE IF NOT EXISTS pizzas ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(50) NOT NULL, + size VARCHAR(10) NOT NULL, + price INT NOT NULL + ) + """) + + # 新增10筆披薩資料 + for i in range(10): + name = random.choice(pizza_names) + size = random.choice(pizza_sizes) + price = random.randint(100, 500) + + query = "INSERT INTO pizzas (name, size, price) VALUES (%s, %s, %s)" + cursor.execute(query, (name, size, price)) + + # 提交交易 + connection.commit() + print("pizzas 的模擬資料!") + + except Error as e: + print(f"資料庫錯誤: {e}") + finally: + if connection.is_connected(): + cursor.close() + connection.close() + +if __name__ == "__main__": + create_and_insert_pizzas() \ No newline at end of file diff --git a/pizza_api.py b/pizza_api.py new file mode 100644 index 0000000..2b61e94 --- /dev/null +++ b/pizza_api.py @@ -0,0 +1,231 @@ +from flask import Flask, request, jsonify +from flask_cors import CORS +import mysql.connector +from mysql.connector import Error + +app = Flask(__name__) +CORS(app) + +# 資料庫連線資訊 +db_config = { + 'host': 'mysql.theaken.com', + 'port': 33306, + 'database': 'db_A021', + 'user': 'A021', + 'password': 'wJk1O5qtP7pf' +} + +def get_db_connection(): + try: + connection = mysql.connector.connect(**db_config) + return connection + except Error as e: + print(f"資料庫連線錯誤: {e}") + return None + +@app.route('/v1/pizzas', methods=['GET']) +def get_pizzas(): + min_id = request.args.get('min_id', type=int) + max_id = request.args.get('max_id', type=int) + page = request.args.get('page', default=1, type=int) + limit = request.args.get('limit', default=10, type=int) + offset = (page - 1) * limit + + connection = get_db_connection() + if not connection: + return jsonify({"status": "error", "code": 500, "message": "資料庫連線失敗"}), 500 + + try: + cursor = connection.cursor(dictionary=True) + + # 基礎查詢 + query = "SELECT * FROM pizzas WHERE 1=1" + params = [] + + # 添加篩選條件 + if min_id is not None: + query += " AND id >= %s" + params.append(min_id) + if max_id is not None: + query += " AND id <= %s" + params.append(max_id) + + # 計算總數 + count_query = "SELECT COUNT(*) as total FROM (" + query + ") as t" + cursor.execute(count_query, params) + total = cursor.fetchone()['total'] + + # 添加分頁 + query += " LIMIT %s OFFSET %s" + params.extend([limit, offset]) + + cursor.execute(query, params) + pizzas = cursor.fetchall() + + meta = { + "total": total, + "page": page, + "limit": limit, + "has_next": (page * limit) < total + } + + return jsonify({ + "status": "success", + "code": 200, + "message": "", + "data": pizzas, + "meta": meta + }) + except Error as e: + return jsonify({"status": "error", "code": 500, "message": str(e)}), 500 + finally: + if connection.is_connected(): + cursor.close() + connection.close() + +@app.route('/v1/pizzas/', methods=['GET']) +def get_pizza(pizza_id): + connection = get_db_connection() + if not connection: + return jsonify({"status": "error", "code": 500, "message": "資料庫連線失敗"}), 500 + + try: + cursor = connection.cursor(dictionary=True) + cursor.execute("SELECT * FROM pizzas WHERE id = %s", (pizza_id,)) + pizza = cursor.fetchone() + + if not pizza: + return jsonify({"status": "error", "code": 404, "message": "披薩不存在"}), 404 + + return jsonify({"status": "success", "code": 200, "message": "", "data": pizza}) + except Error as e: + return jsonify({"status": "error", "code": 500, "message": str(e)}), 500 + finally: + if connection.is_connected(): + cursor.close() + connection.close() + +@app.route('/v1/pizzas', methods=['POST']) +def create_pizza(): + data = request.get_json() + + # 驗證必要欄位 + if not all(key in data for key in ['name', 'size', 'price']): + return jsonify({"status": "error", "code": 400, "message": "缺少必要欄位: name, size, price"}), 400 + + connection = get_db_connection() + if not connection: + return jsonify({"status": "error", "code": 500, "message": "資料庫連線失敗"}), 500 + + try: + cursor = connection.cursor(dictionary=True) + query = "INSERT INTO pizzas (name, size, price) VALUES (%s, %s, %s)" + cursor.execute(query, (data['name'], data['size'], data['price'])) + connection.commit() + + # 獲取新建立的披薩 + pizza_id = cursor.lastrowid + cursor.execute("SELECT * FROM pizzas WHERE id = %s", (pizza_id,)) + new_pizza = cursor.fetchone() + + return jsonify({ + "status": "success", + "code": 201, + "message": "披薩建立成功", + "data": new_pizza + }), 201 + except Error as e: + return jsonify({"status": "error", "code": 500, "message": str(e)}), 500 + finally: + if connection.is_connected(): + cursor.close() + connection.close() + +@app.route('/v1/pizzas/', methods=['PATCH']) +def update_pizza(pizza_id): + # 從URL參數獲取要更新的欄位 + update_name = request.args.get('name') + update_size = request.args.get('size') + update_price = request.args.get('price', type=float) + + connection = get_db_connection() + if not connection: + return jsonify({"status": "error", "code": 500, "message": "資料庫連線失敗"}), 500 + + try: + cursor = connection.cursor(dictionary=True) + + # 檢查披薩是否存在 + cursor.execute("SELECT * FROM pizzas WHERE id = %s", (pizza_id,)) + pizza = cursor.fetchone() + if not pizza: + return jsonify({"status": "error", "code": 404, "message": "披薩不存在"}), 404 + + # 構建更新語句 + set_clauses = [] + params = [] + + if update_name is not None: + set_clauses.append("name = %s") + params.append(update_name) + if update_size is not None: + set_clauses.append("size = %s") + params.append(update_size) + if update_price is not None: + set_clauses.append("price = %s") + params.append(update_price) + + # 如果沒有提供任何可更新參數 + if not set_clauses: + return jsonify({"status": "error", "code": 400, "message": "至少需要提供一個更新參數: name, size, price"}), 400 + + query = "UPDATE pizzas SET " + ", ".join(set_clauses) + " WHERE id = %s" + params.append(pizza_id) + + cursor.execute(query, params) + connection.commit() + + # 獲取更新後的披薩 + cursor.execute("SELECT * FROM pizzas WHERE id = %s", (pizza_id,)) + updated_pizza = cursor.fetchone() + + return jsonify({ + "status": "success", + "code": 200, + "message": "披薩更新成功", + "data": updated_pizza + }) + except Error as e: + return jsonify({"status": "error", "code": 500, "message": str(e)}), 500 + finally: + if connection.is_connected(): + cursor.close() + connection.close() + +@app.route('/v1/pizzas/', methods=['DELETE']) +def delete_pizza(pizza_id): + connection = get_db_connection() + if not connection: + return jsonify({"status": "error", "code": 500, "message": "資料庫連線失敗"}), 500 + + try: + cursor = connection.cursor() + + # 檢查披薩是否存在 + cursor.execute("SELECT id FROM pizzas WHERE id = %s", (pizza_id,)) + if not cursor.fetchone(): + return jsonify({"status": "error", "code": 404, "message": "披薩不存在"}), 404 + + cursor.execute("DELETE FROM pizzas WHERE id = %s", (pizza_id,)) + connection.commit() + + return '', 204 + except Error as e: + return jsonify({"status": "error", "code": 500, "message": str(e)}), 500 + finally: + if connection.is_connected(): + cursor.close() + connection.close() + +if __name__ == '__main__': + app.run(debug=True) \ No newline at end of file