You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
9.5 KiB
288 lines
9.5 KiB
1 week ago
|
from flask import Flask, request, jsonify
|
||
|
from flask_sqlalchemy import SQLAlchemy
|
||
|
from flask_cors import CORS
|
||
|
import requests
|
||
|
from datetime import datetime
|
||
|
import base64
|
||
|
|
||
|
app = Flask(__name__)
|
||
|
CORS(app)
|
||
|
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root@localhost/flower'
|
||
|
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||
|
|
||
|
UPLOAD_SERVICE_URL = 'https://c.2nicep.com:3000/uploads'
|
||
|
|
||
|
db = SQLAlchemy(app)
|
||
|
|
||
|
|
||
|
class FlowerShop(db.Model):
|
||
|
__tablename__ = 'flower_shop'
|
||
|
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||
|
name = db.Column(db.String(100), nullable=False)
|
||
|
description = db.Column(db.Text, nullable=True)
|
||
|
price = db.Column(db.DECIMAL(10, 2), nullable=False)
|
||
|
quantity = db.Column(db.Integer, nullable=False, default=0)
|
||
|
image_url = db.Column(db.String(255), nullable=True)
|
||
|
is_active = db.Column(db.Boolean, default=True)
|
||
|
|
||
|
|
||
|
class Order(db.Model):
|
||
|
__tablename__ = 'orders'
|
||
|
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||
|
flower_id = db.Column(db.Integer, db.ForeignKey('flower_shop.id'), nullable=False)
|
||
|
quantity = db.Column(db.Integer, nullable=False)
|
||
|
total_price = db.Column(db.DECIMAL(10, 2), nullable=False)
|
||
|
status = db.Column(db.String(20), nullable=False, default='pending')
|
||
|
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
|
||
|
|
||
|
flower = db.relationship('FlowerShop', backref=db.backref('orders', lazy=True))
|
||
|
|
||
|
|
||
|
API_KEY = "kbt40nMOF8tLGg5IdZStJc4G"
|
||
|
SECRET_KEY = "jUoj82HAALIzDWRlrTauoHwLcnB3eAn7"
|
||
|
|
||
|
|
||
|
def get_access_token():
|
||
|
url = "https://aip.baidubce.com/oauth/2.0/token"
|
||
|
params = {"grant_type": "client_credentials", "client_id": API_KEY, "client_secret": SECRET_KEY}
|
||
|
return str(requests.post(url, params=params).json().get("access_token"))
|
||
|
|
||
|
|
||
|
@app.route('/api/identify', methods=['POST'])
|
||
|
def identify_flower():
|
||
|
if 'image' not in request.files:
|
||
|
return jsonify({"error": "没有提供图片文件"}), 400
|
||
|
|
||
|
image_file = request.files['image']
|
||
|
image = base64.b64encode(image_file.read()).decode('utf-8')
|
||
|
|
||
|
url = "https://aip.baidubce.com/rest/2.0/image-classify/v1/plant?access_token=" + get_access_token()
|
||
|
|
||
|
payload = {
|
||
|
"image": image,
|
||
|
"baike_num": 0
|
||
|
}
|
||
|
headers = {
|
||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||
|
'Accept': 'application/json'
|
||
|
}
|
||
|
|
||
|
response = requests.post(url, headers=headers, data=payload)
|
||
|
result = response.json()
|
||
|
|
||
|
if 'result' in result and len(result['result']) > 0:
|
||
|
top_result = result['result'][0]
|
||
|
# 检查数据库中是否存在这种花
|
||
|
flower = FlowerShop.query.filter(FlowerShop.name.ilike(f"%{top_result['name']}%")).first()
|
||
|
|
||
|
if flower:
|
||
|
return jsonify({
|
||
|
"id": flower.id,
|
||
|
"name": flower.name,
|
||
|
"description": flower.description,
|
||
|
"price": float(flower.price),
|
||
|
"quantity": flower.quantity,
|
||
|
"image_url": flower.image_url
|
||
|
}), 200
|
||
|
else:
|
||
|
# 如果数据库中没有这种花,返回识别的名称和概率
|
||
|
return jsonify({
|
||
|
"name": top_result['name'],
|
||
|
"probability": top_result['score']
|
||
|
}), 200
|
||
|
else:
|
||
|
return jsonify({"error": "未能识别花卉"}), 404
|
||
|
|
||
|
|
||
|
def upload_file_to_service(file):
|
||
|
if not file:
|
||
|
return None
|
||
|
files = {'images': (file.filename, file.read(), file.content_type)}
|
||
|
try:
|
||
|
response = requests.post(UPLOAD_SERVICE_URL, files=files)
|
||
|
if response.status_code == 200:
|
||
|
return response.json()['files'][0]
|
||
|
else:
|
||
|
print(f"Upload failed: {response.text}")
|
||
|
return None
|
||
|
except Exception as e:
|
||
|
print(f"Error during upload: {str(e)}")
|
||
|
return None
|
||
|
|
||
|
|
||
|
@app.route('/api/flowers', methods=['GET'])
|
||
|
def get_flowers():
|
||
|
query = request.args.get('query', '')
|
||
|
flowers = FlowerShop.query.filter(FlowerShop.name.ilike(f'%{query}%'), FlowerShop.is_active == True).all()
|
||
|
return jsonify([{
|
||
|
"id": flower.id,
|
||
|
"name": flower.name,
|
||
|
"description": flower.description,
|
||
|
"price": float(flower.price),
|
||
|
"quantity": flower.quantity,
|
||
|
"image_url": flower.image_url
|
||
|
} for flower in flowers])
|
||
|
|
||
|
|
||
|
@app.route('/api/flowers', methods=['POST'])
|
||
|
def add_flower():
|
||
|
name = request.form.get('name')
|
||
|
description = request.form.get('description')
|
||
|
price = request.form.get('price')
|
||
|
quantity = request.form.get('quantity')
|
||
|
image = request.files.get('image')
|
||
|
|
||
|
if not name or not price or not quantity:
|
||
|
return jsonify({"error": "Name, price, and quantity are required"}), 400
|
||
|
|
||
|
try:
|
||
|
price = float(price)
|
||
|
quantity = int(quantity)
|
||
|
except ValueError:
|
||
|
return jsonify({"error": "Invalid price or quantity format"}), 400
|
||
|
|
||
|
if quantity < 0:
|
||
|
return jsonify({"error": "Quantity must be non-negative"}), 400
|
||
|
|
||
|
image_url = upload_file_to_service(image)
|
||
|
|
||
|
try:
|
||
|
new_flower = FlowerShop(name=name, description=description, price=price, quantity=quantity, image_url=image_url)
|
||
|
db.session.add(new_flower)
|
||
|
db.session.commit()
|
||
|
except Exception as e:
|
||
|
db.session.rollback()
|
||
|
print(f"Database error: {str(e)}")
|
||
|
return jsonify({"error": "Failed to add flower to database"}), 500
|
||
|
|
||
|
return jsonify({
|
||
|
"message": "Flower added successfully",
|
||
|
"id": new_flower.id,
|
||
|
"name": new_flower.name,
|
||
|
"description": new_flower.description,
|
||
|
"price": float(new_flower.price),
|
||
|
"quantity": new_flower.quantity,
|
||
|
"image_url": new_flower.image_url
|
||
|
}), 201
|
||
|
|
||
|
|
||
|
@app.route('/api/my-products', methods=['GET'])
|
||
|
def get_my_products():
|
||
|
products = FlowerShop.query.all()
|
||
|
return jsonify([{
|
||
|
"id": product.id,
|
||
|
"name": product.name,
|
||
|
"description": product.description,
|
||
|
"price": float(product.price),
|
||
|
"quantity": product.quantity,
|
||
|
"image_url": product.image_url,
|
||
|
"is_active": product.is_active
|
||
|
} for product in products])
|
||
|
|
||
|
|
||
|
@app.route('/api/products/<int:product_id>', methods=['PUT'])
|
||
|
def update_product(product_id):
|
||
|
product = FlowerShop.query.get_or_404(product_id)
|
||
|
data = request.json
|
||
|
product.name = data.get('name', product.name)
|
||
|
product.description = data.get('description', product.description)
|
||
|
product.price = data.get('price', product.price)
|
||
|
product.quantity = data.get('quantity', product.quantity)
|
||
|
product.is_active = data.get('is_active', product.is_active)
|
||
|
|
||
|
try:
|
||
|
db.session.commit()
|
||
|
except Exception as e:
|
||
|
db.session.rollback()
|
||
|
return jsonify({"error": f"Failed to update product: {str(e)}"}), 500
|
||
|
|
||
|
return jsonify({"message": "Product updated successfully"})
|
||
|
|
||
|
|
||
|
@app.route('/api/products/<int:product_id>/toggle-status', methods=['POST'])
|
||
|
def toggle_product_status(product_id):
|
||
|
product = FlowerShop.query.get_or_404(product_id)
|
||
|
product.is_active = not product.is_active
|
||
|
|
||
|
try:
|
||
|
db.session.commit()
|
||
|
except Exception as e:
|
||
|
db.session.rollback()
|
||
|
return jsonify({"error": f"Failed to toggle product status: {str(e)}"}), 500
|
||
|
|
||
|
return jsonify({"message": f"Product {'activated' if product.is_active else 'deactivated'} successfully"})
|
||
|
|
||
|
|
||
|
@app.route('/api/orders', methods=['GET'])
|
||
|
def get_orders():
|
||
|
try:
|
||
|
orders = Order.query.all()
|
||
|
return jsonify([{
|
||
|
"id": order.id,
|
||
|
"flowerName": order.flower.name if order.flower else "Unknown",
|
||
|
"quantity": order.quantity,
|
||
|
"totalPrice": float(order.total_price),
|
||
|
"status": order.status,
|
||
|
"createdAt": order.created_at.isoformat()
|
||
|
} for order in orders])
|
||
|
except Exception as e:
|
||
|
print(f"Error fetching orders: {str(e)}")
|
||
|
return jsonify({"error": "Failed to fetch orders"}), 500
|
||
|
|
||
|
|
||
|
@app.route('/api/orders', methods=['POST'])
|
||
|
def create_order():
|
||
|
data = request.json
|
||
|
flower_id = data.get('flowerId')
|
||
|
quantity = data.get('quantity', 1)
|
||
|
|
||
|
if not flower_id or not quantity:
|
||
|
return jsonify({"error": "Flower ID and quantity are required"}), 400
|
||
|
|
||
|
flower = FlowerShop.query.get(flower_id)
|
||
|
if not flower:
|
||
|
return jsonify({"error": "Flower not found"}), 404
|
||
|
|
||
|
if flower.quantity < quantity:
|
||
|
return jsonify({"error": "Not enough stock"}), 400
|
||
|
|
||
|
total_price = flower.price * quantity
|
||
|
|
||
|
try:
|
||
|
new_order = Order(flower_id=flower_id, quantity=quantity, total_price=total_price)
|
||
|
flower.quantity -= quantity # 减少库存
|
||
|
db.session.add(new_order)
|
||
|
db.session.commit()
|
||
|
except Exception as e:
|
||
|
db.session.rollback()
|
||
|
print(f"Database error: {str(e)}")
|
||
|
return jsonify({"error": "Failed to create order"}), 500
|
||
|
|
||
|
return jsonify({
|
||
|
"message": "Order created successfully",
|
||
|
"orderId": new_order.id,
|
||
|
"totalPrice": float(new_order.total_price)
|
||
|
}), 201
|
||
|
|
||
|
|
||
|
@app.route('/api/orders/<int:order_id>/cancel', methods=['POST'])
|
||
|
def cancel_order(order_id):
|
||
|
order = Order.query.get_or_404(order_id)
|
||
|
if order.status != 'pending':
|
||
|
return jsonify({"error": "Only pending orders can be cancelled"}), 400
|
||
|
|
||
|
try:
|
||
|
order.status = 'cancelled'
|
||
|
order.flower.quantity += order.quantity # 恢复库存
|
||
|
db.session.commit()
|
||
|
except Exception as e:
|
||
|
db.session.rollback()
|
||
|
return jsonify({"error": f"Failed to cancel order: {str(e)}"}), 500
|
||
|
|
||
|
return jsonify({"message": "Order cancelled successfully"})
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
with app.app_context():
|
||
|
db.create_all()
|
||
|
app.run(debug=True)
|