#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ FastAPI接口测试应用 (MySQL版本) 提供所有管理功能的API接口测试页面,直接使用MySQL数据库 """ import os import sys import pymysql from datetime import datetime, timedelta from typing import List, Optional from pydantic import BaseModel from fastapi import FastAPI, HTTPException, Depends, status from fastapi.middleware.cors import CORSMiddleware # 添加项目根目录到Python路径 sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) # 导入配置 from config import Config # 解析数据库URL def parse_database_url(url): """解析数据库URL""" # 格式: mysql+pymysql://user:password@host:port/database try: # 移除mysql+pymysql://前缀 url = url.replace('mysql+pymysql://', '') # 找到@符号前的用户名密码 at_index = url.index('@') user_pass = url[:at_index] url = url[at_index + 1:] # 分割用户名和密码 user, password = user_pass.split(':', 1) # 找到数据库名 if '/' in url: host_port, database = url.split('/', 1) # 提取端口 if ':' in host_port: host, port = host_port.split(':', 1) else: host = host_port port = '3306' else: raise ValueError("URL格式不正确") return { 'user': user, 'password': password, 'host': host, 'port': port, 'database': database } except Exception as e: print(f"无法解析数据库URL: {e}") return None # 获取数据库连接 def get_db_connection(): """获取MySQL数据库连接""" db_config = parse_database_url(Config.SQLALCHEMY_DATABASE_URI) if not db_config: raise Exception("无法解析数据库配置") connection = pymysql.connect( host=db_config['host'], port=int(db_config['port']), user=db_config['user'], password=db_config['password'], database=db_config['database'], charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor ) return connection # 创建FastAPI应用 app = FastAPI( title="KaMiXiTong API测试平台 (MySQL版)", description="软件授权管理系统的完整API接口测试平台,使用MySQL数据库", version="1.0.0", docs_url="/docs", redoc_url="/redoc" ) # 添加CORS中间件 app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # ==================== 用户管理模型 ==================== class AdminBase(BaseModel): username: str email: Optional[str] = None role: Optional[int] = 0 # 0=普通管理员, 1=超级管理员 status: Optional[int] = 1 # 0=禁用, 1=正常 class Config: from_attributes = True class AdminCreate(AdminBase): password: str class AdminUpdate(AdminBase): password: Optional[str] = None class AdminInDB(AdminBase): admin_id: int create_time: datetime update_time: datetime # ==================== 工单管理模型 ==================== class TicketBase(BaseModel): title: str product_id: str description: str priority: Optional[int] = 1 # 1=低, 2=中, 3=高 status: Optional[int] = 0 # 0=待处理, 1=处理中, 2=已解决, 3=已关闭 class Config: from_attributes = True class TicketCreate(TicketBase): software_version: Optional[str] = None machine_code: Optional[str] = None license_key: Optional[str] = None class TicketUpdate(TicketBase): pass class TicketInDB(TicketBase): ticket_id: int create_time: datetime update_time: datetime # ==================== 卡密管理模型 ==================== class LicenseBase(BaseModel): product_id: str type: int = 1 # 0=试用, 1=正式 status: Optional[int] = 0 # 0=未使用, 1=已使用, 2=已过期, 3=已禁用 valid_days: Optional[int] = 365 class Config: from_attributes = True class LicenseCreate(LicenseBase): count: int = 1 prefix: Optional[str] = "" length: Optional[int] = 32 class LicenseUpdate(LicenseBase): pass class LicenseInDB(LicenseBase): license_id: int license_key: str create_time: datetime update_time: datetime expire_time: Optional[datetime] = None # ==================== 版本管理模型 ==================== class VersionBase(BaseModel): product_id: str version_num: str platform: Optional[str] = "" description: Optional[str] = "" update_log: Optional[str] = "" download_url: Optional[str] = "" file_hash: Optional[str] = "" force_update: Optional[int] = 0 download_status: Optional[int] = 1 # 0=下架, 1=上架 min_license_version: Optional[str] = "" publish_status: Optional[int] = 0 # 0=未发布, 1=已发布 class Config: from_attributes = True class VersionCreate(VersionBase): publish_now: Optional[bool] = False class VersionUpdate(VersionBase): pass class VersionInDB(VersionBase): version_id: int create_time: datetime update_time: datetime # ==================== 设备管理模型 ==================== class DeviceBase(BaseModel): product_id: str machine_code: str software_version: Optional[str] = "" status: Optional[int] = 1 # 0=禁用, 1=正常, 2=黑名单 class Config: from_attributes = True class DeviceCreate(DeviceBase): license_key: Optional[str] = None class DeviceUpdate(DeviceBase): pass class DeviceInDB(DeviceBase): device_id: int create_time: datetime last_verify_time: Optional[datetime] = None # ==================== 产品管理模型 ==================== class ProductBase(BaseModel): product_name: str description: Optional[str] = "" status: Optional[int] = 1 # 0=禁用, 1=正常 class Config: from_attributes = True class ProductCreate(ProductBase): product_id: Optional[str] = None class ProductUpdate(ProductBase): pass class ProductInDB(ProductBase): product_id: str create_time: datetime update_time: datetime # ==================== 用户管理接口 ==================== @app.get("/") async def root(): return {"message": "欢迎使用KaMiXiTong API测试平台 (MySQL版)", "version": "1.0.0"} @app.get("/admins", response_model=List[AdminInDB]) async def get_admins( skip: int = 0, limit: int = 100, keyword: Optional[str] = None, role: Optional[int] = None, status: Optional[int] = None ): """获取管理员列表""" try: connection = get_db_connection() with connection.cursor() as cursor: # 构建查询 sql = "SELECT * FROM admin WHERE is_deleted = 0" params = [] if keyword: sql += " AND username LIKE %s" params.append(f"%{keyword}%") if role is not None: sql += " AND role = %s" params.append(role) if status is not None: sql += " AND status = %s" params.append(status) sql += " ORDER BY create_time DESC LIMIT %s OFFSET %s" params.extend([limit, skip]) cursor.execute(sql, params) admins = cursor.fetchall() connection.close() return admins except Exception as e: raise HTTPException(status_code=500, detail=f"数据库查询失败: {str(e)}") @app.post("/admins", response_model=AdminInDB) async def create_admin(admin: AdminCreate): """创建管理员""" try: connection = get_db_connection() with connection.cursor() as cursor: # 检查用户名是否已存在 cursor.execute( "SELECT admin_id FROM admin WHERE username = %s AND is_deleted = 0", (admin.username,) ) existing = cursor.fetchone() if existing: raise HTTPException(status_code=400, detail="用户名已存在") # 创建管理员(简化密码处理) sql = """ INSERT INTO admin (username, email, password_hash, role, status, create_time, update_time) VALUES (%s, %s, %s, %s, %s, %s, %s) """ params = ( admin.username, admin.email, f"hashed_{admin.password}", # 简化处理 admin.role, admin.status, datetime.utcnow(), datetime.utcnow() ) cursor.execute(sql, params) admin_id = cursor.lastrowid connection.commit() # 查询创建的管理员 cursor.execute("SELECT * FROM admin WHERE admin_id = %s", (admin_id,)) created_admin = cursor.fetchone() connection.close() return created_admin except Exception as e: raise HTTPException(status_code=500, detail=f"创建管理员失败: {str(e)}") @app.get("/admins/{admin_id}", response_model=AdminInDB) async def get_admin(admin_id: int): """获取管理员详情""" try: connection = get_db_connection() with connection.cursor() as cursor: cursor.execute( "SELECT * FROM admin WHERE admin_id = %s AND is_deleted = 0", (admin_id,) ) admin = cursor.fetchone() connection.close() if not admin: raise HTTPException(status_code=404, detail="管理员不存在") return admin except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"查询管理员失败: {str(e)}") @app.put("/admins/{admin_id}", response_model=AdminInDB) async def update_admin(admin_id: int, admin: AdminUpdate): """更新管理员""" try: connection = get_db_connection() with connection.cursor() as cursor: # 检查管理员是否存在 cursor.execute( "SELECT * FROM admin WHERE admin_id = %s AND is_deleted = 0", (admin_id,) ) existing_admin = cursor.fetchone() if not existing_admin: raise HTTPException(status_code=404, detail="管理员不存在") # 检查新用户名是否已存在 if admin.username and admin.username != existing_admin['username']: cursor.execute( "SELECT admin_id FROM admin WHERE username = %s AND admin_id != %s AND is_deleted = 0", (admin.username, admin_id) ) duplicate = cursor.fetchone() if duplicate: raise HTTPException(status_code=400, detail="用户名已存在") # 更新字段 updates = [] params = [] if admin.username is not None: updates.append("username = %s") params.append(admin.username) if admin.email is not None: updates.append("email = %s") params.append(admin.email) if admin.role is not None: updates.append("role = %s") params.append(admin.role) if admin.status is not None: updates.append("status = %s") params.append(admin.status) if admin.password: updates.append("password_hash = %s") params.append(f"hashed_{admin.password}") # 简化处理 if updates: updates.append("update_time = %s") params.append(datetime.utcnow()) params.append(admin_id) sql = f"UPDATE admin SET {', '.join(updates)} WHERE admin_id = %s" cursor.execute(sql, params) connection.commit() # 查询更新后的管理员 cursor.execute("SELECT * FROM admin WHERE admin_id = %s", (admin_id,)) updated_admin = cursor.fetchone() connection.close() return updated_admin except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"更新管理员失败: {str(e)}") @app.delete("/admins/{admin_id}") async def delete_admin(admin_id: int): """删除管理员(软删除)""" try: connection = get_db_connection() with connection.cursor() as cursor: # 检查管理员是否存在 cursor.execute( "SELECT admin_id FROM admin WHERE admin_id = %s AND is_deleted = 0", (admin_id,) ) admin = cursor.fetchone() if not admin: raise HTTPException(status_code=404, detail="管理员不存在") # 软删除 cursor.execute( "UPDATE admin SET is_deleted = 1, delete_time = %s WHERE admin_id = %s", (datetime.utcnow(), admin_id) ) connection.commit() connection.close() return {"message": "管理员删除成功"} except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"删除管理员失败: {str(e)}") @app.post("/admins/{admin_id}/toggle-status") async def toggle_admin_status(admin_id: int): """切换管理员状态""" try: connection = get_db_connection() with connection.cursor() as cursor: # 检查管理员是否存在 cursor.execute( "SELECT * FROM admin WHERE admin_id = %s AND is_deleted = 0", (admin_id,) ) admin = cursor.fetchone() if not admin: raise HTTPException(status_code=404, detail="管理员不存在") # 切换状态 new_status = 0 if admin['status'] == 1 else 1 cursor.execute( "UPDATE admin SET status = %s, update_time = %s WHERE admin_id = %s", (new_status, datetime.utcnow(), admin_id) ) connection.commit() # 查询更新后的管理员 cursor.execute("SELECT * FROM admin WHERE admin_id = %s", (admin_id,)) updated_admin = cursor.fetchone() connection.close() status_name = "正常" if updated_admin['status'] == 1 else "禁用" action = "启用" if updated_admin['status'] == 1 else "禁用" return {"message": f"管理员已{action}", "status": updated_admin['status'], "status_name": status_name} except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"切换管理员状态失败: {str(e)}") # ==================== 工单管理接口 ==================== @app.get("/tickets", response_model=List[TicketInDB]) async def get_tickets( skip: int = 0, limit: int = 100, status: Optional[int] = None, priority: Optional[int] = None, product_id: Optional[str] = None ): """获取工单列表""" try: connection = get_db_connection() with connection.cursor() as cursor: # 构建查询 sql = "SELECT * FROM ticket" params = [] conditions = [] if status is not None: conditions.append("status = %s") params.append(status) if priority is not None: conditions.append("priority = %s") params.append(priority) if product_id: conditions.append("product_id = %s") params.append(product_id) if conditions: sql += " WHERE " + " AND ".join(conditions) sql += " ORDER BY create_time DESC LIMIT %s OFFSET %s" params.extend([limit, skip]) cursor.execute(sql, params) tickets = cursor.fetchall() connection.close() return tickets except Exception as e: raise HTTPException(status_code=500, detail=f"查询工单失败: {str(e)}") @app.post("/tickets", response_model=TicketInDB) async def create_ticket(ticket: TicketCreate): """创建工单""" try: connection = get_db_connection() with connection.cursor() as cursor: # 创建工单 sql = """ INSERT INTO ticket (title, product_id, software_version, machine_code, license_key, description, priority, status, create_time, update_time) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """ params = ( ticket.title, ticket.product_id, ticket.software_version, ticket.machine_code, ticket.license_key, ticket.description, ticket.priority, ticket.status, datetime.utcnow(), datetime.utcnow() ) cursor.execute(sql, params) ticket_id = cursor.lastrowid connection.commit() # 查询创建的工单 cursor.execute("SELECT * FROM ticket WHERE ticket_id = %s", (ticket_id,)) created_ticket = cursor.fetchone() connection.close() return created_ticket except Exception as e: raise HTTPException(status_code=500, detail=f"创建工单失败: {str(e)}") # ==================== 卡密管理接口 ==================== @app.get("/licenses", response_model=List[LicenseInDB]) async def get_licenses( skip: int = 0, limit: int = 100, product_id: Optional[str] = None, status: Optional[int] = None, license_type: Optional[int] = None, keyword: Optional[str] = None ): """获取卡密列表""" try: connection = get_db_connection() with connection.cursor() as cursor: # 构建查询 sql = "SELECT * FROM license" params = [] conditions = [] if product_id: conditions.append("product_id = %s") params.append(product_id) if status is not None: conditions.append("status = %s") params.append(status) if license_type is not None: conditions.append("type = %s") params.append(license_type) if keyword: conditions.append("license_key LIKE %s") params.append(f"%{keyword}%") if conditions: sql += " WHERE " + " AND ".join(conditions) sql += " ORDER BY create_time DESC LIMIT %s OFFSET %s" params.extend([limit, skip]) cursor.execute(sql, params) licenses = cursor.fetchall() connection.close() return licenses except Exception as e: raise HTTPException(status_code=500, detail=f"查询卡密失败: {str(e)}") @app.post("/licenses", response_model=dict) async def generate_licenses(license: LicenseCreate): """批量生成卡密""" try: # 验证参数 if license.count < 1 or license.count > 10000: raise HTTPException(status_code=400, detail="生成数量必须在1-10000之间") if license.length < 16 or license.length > 35: raise HTTPException(status_code=400, detail="卡密长度必须在16-35之间") # 试用卡密最大有效期限制 if license.type == 0 and license.valid_days and license.valid_days > 90: raise HTTPException(status_code=400, detail="试用卡密有效期不能超过90天") # 生成卡密 import secrets import string connection = get_db_connection() with connection.cursor() as cursor: licenses = [] characters = string.ascii_uppercase + string.digits for i in range(license.count): # 生成卡密 key = license.prefix + ''.join(secrets.choice(characters) for _ in range(license.length - len(license.prefix))) # 确保卡密唯一 max_attempts = 10 for attempt in range(max_attempts): cursor.execute("SELECT license_id FROM license WHERE license_key = %s", (key,)) existing = cursor.fetchone() if not existing: break key = license.prefix + ''.join(secrets.choice(characters) for _ in range(license.length - len(license.prefix))) else: raise HTTPException(status_code=500, detail="无法生成唯一的卡密,请稍后重试") # 计算过期时间 expire_time = None if license.valid_days: expire_time = datetime.utcnow() + timedelta(days=license.valid_days) # 创建卡密 sql = """ INSERT INTO license (product_id, license_key, type, status, create_time, update_time, expire_time) VALUES (%s, %s, %s, %s, %s, %s, %s) """ params = ( license.product_id, key, license.type, 0, # 未使用 datetime.utcnow(), datetime.utcnow(), expire_time ) cursor.execute(sql, params) license_id = cursor.lastrowid # 查询创建的卡密 cursor.execute("SELECT * FROM license WHERE license_id = %s", (license_id,)) created_license = cursor.fetchone() licenses.append(created_license) connection.commit() connection.close() return { "message": f"成功生成 {license.count} 个卡密", "licenses": licenses, "count": len(licenses) } except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"生成卡密失败: {str(e)}") # ==================== 版本管理接口 ==================== @app.get("/versions", response_model=List[VersionInDB]) async def get_versions( skip: int = 0, limit: int = 100, product_id: Optional[str] = None, publish_status: Optional[int] = None ): """获取版本列表""" try: connection = get_db_connection() with connection.cursor() as cursor: # 构建查询 sql = "SELECT * FROM version" params = [] conditions = [] if product_id: conditions.append("product_id = %s") params.append(product_id) if publish_status is not None: conditions.append("publish_status = %s") params.append(publish_status) if conditions: sql += " WHERE " + " AND ".join(conditions) sql += " ORDER BY create_time DESC LIMIT %s OFFSET %s" params.extend([limit, skip]) cursor.execute(sql, params) versions = cursor.fetchall() connection.close() return versions except Exception as e: raise HTTPException(status_code=500, detail=f"查询版本失败: {str(e)}") @app.post("/versions", response_model=VersionInDB) async def create_version(version: VersionCreate): """创建版本""" try: # 验证参数 if not version.product_id or not version.version_num: raise HTTPException(status_code=400, detail="缺少必要参数") connection = get_db_connection() with connection.cursor() as cursor: # 检查版本号是否重复 cursor.execute( "SELECT version_id FROM version WHERE product_id = %s AND version_num = %s", (version.product_id, version.version_num) ) existing = cursor.fetchone() if existing: raise HTTPException(status_code=400, detail="版本号已存在") # 创建版本 sql = """ INSERT INTO version (product_id, version_num, platform, description, update_log, download_url, file_hash, force_update, download_status, min_license_version, publish_status, create_time, update_time) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """ params = ( version.product_id, version.version_num, version.platform, version.description, version.update_log, version.download_url, version.file_hash, version.force_update, version.download_status, version.min_license_version, version.publish_status, datetime.utcnow(), datetime.utcnow() ) cursor.execute(sql, params) version_id = cursor.lastrowid connection.commit() # 如果选择了立即发布,则发布版本 if version.publish_now: cursor.execute( "UPDATE version SET publish_status = 1, update_time = %s WHERE version_id = %s", (datetime.utcnow(), version_id) ) connection.commit() # 查询创建的版本 cursor.execute("SELECT * FROM version WHERE version_id = %s", (version_id,)) created_version = cursor.fetchone() connection.close() return created_version except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"创建版本失败: {str(e)}") @app.post("/versions/{version_id}/publish") async def publish_version(version_id: int): """发布版本""" try: connection = get_db_connection() with connection.cursor() as cursor: # 检查版本是否存在 cursor.execute("SELECT * FROM version WHERE version_id = %s", (version_id,)) version = cursor.fetchone() if not version: raise HTTPException(status_code=404, detail="版本不存在") # 发布版本 cursor.execute( "UPDATE version SET publish_status = 1, update_time = %s WHERE version_id = %s", (datetime.utcnow(), version_id) ) connection.commit() # 查询更新后的版本 cursor.execute("SELECT * FROM version WHERE version_id = %s", (version_id,)) updated_version = cursor.fetchone() connection.close() return {"message": "版本发布成功", "version": updated_version} except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"发布版本失败: {str(e)}") # ==================== 设备管理接口 ==================== @app.get("/devices", response_model=List[DeviceInDB]) async def get_devices( skip: int = 0, limit: int = 100, product_id: Optional[str] = None, software_version: Optional[str] = None, status: Optional[int] = None, keyword: Optional[str] = None ): """获取设备列表""" try: connection = get_db_connection() with connection.cursor() as cursor: # 构建查询 sql = "SELECT * FROM device" params = [] conditions = [] if product_id: conditions.append("product_id = %s") params.append(product_id) if software_version: conditions.append("software_version = %s") params.append(software_version) if status is not None: conditions.append("status = %s") params.append(status) if keyword: conditions.append("machine_code LIKE %s") params.append(f"%{keyword}%") if conditions: sql += " WHERE " + " AND ".join(conditions) sql += " ORDER BY last_verify_time DESC LIMIT %s OFFSET %s" params.extend([limit, skip]) cursor.execute(sql, params) devices = cursor.fetchall() connection.close() return devices except Exception as e: raise HTTPException(status_code=500, detail=f"查询设备失败: {str(e)}") @app.put("/devices/{device_id}/status") async def update_device_status(device_id: int, status: int): """更新设备状态""" if status not in [0, 1, 2]: raise HTTPException(status_code=400, detail="无效的状态值") try: connection = get_db_connection() with connection.cursor() as cursor: # 检查设备是否存在 cursor.execute("SELECT * FROM device WHERE device_id = %s", (device_id,)) device = cursor.fetchone() if not device: raise HTTPException(status_code=404, detail="设备不存在") # 更新设备状态 cursor.execute( "UPDATE device SET status = %s, last_verify_time = %s WHERE device_id = %s", (status, datetime.utcnow(), device_id) ) connection.commit() # 查询更新后的设备 cursor.execute("SELECT * FROM device WHERE device_id = %s", (device_id,)) updated_device = cursor.fetchone() connection.close() return {"message": "设备状态更新成功", "device": updated_device} except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"更新设备状态失败: {str(e)}") @app.delete("/devices/{device_id}") async def delete_device(device_id: int): """删除设备""" try: connection = get_db_connection() with connection.cursor() as cursor: # 检查设备是否存在 cursor.execute("SELECT device_id FROM device WHERE device_id = %s", (device_id,)) device = cursor.fetchone() if not device: raise HTTPException(status_code=404, detail="设备不存在") # 删除设备 cursor.execute("DELETE FROM device WHERE device_id = %s", (device_id,)) connection.commit() connection.close() return {"message": "设备删除成功"} except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"删除设备失败: {str(e)}") # ==================== 产品管理接口 ==================== @app.get("/products", response_model=List[ProductInDB]) async def get_products( skip: int = 0, limit: int = 100, keyword: Optional[str] = None ): """获取产品列表""" try: connection = get_db_connection() with connection.cursor() as cursor: # 构建查询 sql = "SELECT * FROM product" params = [] conditions = [] if keyword: conditions.append("(product_name LIKE %s OR description LIKE %s)") params.extend([f"%{keyword}%", f"%{keyword}%"]) if conditions: sql += " WHERE " + " AND ".join(conditions) sql += " ORDER BY create_time DESC LIMIT %s OFFSET %s" params.extend([limit, skip]) cursor.execute(sql, params) products = cursor.fetchall() connection.close() return products except Exception as e: raise HTTPException(status_code=500, detail=f"查询产品失败: {str(e)}") @app.post("/products", response_model=ProductInDB) async def create_product(product: ProductCreate): """创建产品""" if not product.product_name.strip(): raise HTTPException(status_code=400, detail="产品名称不能为空") try: connection = get_db_connection() with connection.cursor() as cursor: # 检查自定义ID是否重复 if product.product_id: cursor.execute( "SELECT product_id FROM product WHERE product_id = %s", (product.product_id,) ) existing = cursor.fetchone() if existing: raise HTTPException(status_code=400, detail="产品ID已存在") # 创建产品 import uuid product_id = product.product_id if product.product_id else f"PROD_{uuid.uuid4().hex[:8]}".upper() sql = """ INSERT INTO product (product_id, product_name, description, status, create_time, update_time) VALUES (%s, %s, %s, %s, %s, %s) """ params = ( product_id, product.product_name, product.description, product.status, datetime.utcnow(), datetime.utcnow() ) cursor.execute(sql, params) connection.commit() # 查询创建的产品 cursor.execute("SELECT * FROM product WHERE product_id = %s", (product_id,)) created_product = cursor.fetchone() connection.close() return created_product except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"创建产品失败: {str(e)}") @app.get("/products/{product_id}", response_model=ProductInDB) async def get_product(product_id: str): """获取产品详情""" try: connection = get_db_connection() with connection.cursor() as cursor: cursor.execute("SELECT * FROM product WHERE product_id = %s", (product_id,)) product = cursor.fetchone() connection.close() if not product: raise HTTPException(status_code=404, detail="产品不存在") return product except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"查询产品失败: {str(e)}") @app.put("/products/{product_id}", response_model=ProductInDB) async def update_product(product_id: str, product: ProductUpdate): """更新产品""" try: connection = get_db_connection() with connection.cursor() as cursor: # 检查产品是否存在 cursor.execute("SELECT * FROM product WHERE product_id = %s", (product_id,)) existing_product = cursor.fetchone() if not existing_product: raise HTTPException(status_code=404, detail="产品不存在") # 更新字段 updates = [] params = [] if product.product_name is not None: updates.append("product_name = %s") params.append(product.product_name) if product.description is not None: updates.append("description = %s") params.append(product.description) if product.status is not None: updates.append("status = %s") params.append(product.status) if updates: updates.append("update_time = %s") params.append(datetime.utcnow()) params.append(product_id) sql = f"UPDATE product SET {', '.join(updates)} WHERE product_id = %s" cursor.execute(sql, params) connection.commit() # 查询更新后的产品 cursor.execute("SELECT * FROM product WHERE product_id = %s", (product_id,)) updated_product = cursor.fetchone() connection.close() return updated_product except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"更新产品失败: {str(e)}") @app.delete("/products/{product_id}") async def delete_product(product_id: str): """删除产品""" try: connection = get_db_connection() with connection.cursor() as cursor: # 检查产品是否存在 cursor.execute("SELECT product_id FROM product WHERE product_id = %s", (product_id,)) product = cursor.fetchone() if not product: raise HTTPException(status_code=404, detail="产品不存在") # 删除产品 cursor.execute("DELETE FROM product WHERE product_id = %s", (product_id,)) connection.commit() connection.close() return {"message": "产品删除成功"} except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"删除产品失败: {str(e)}") if __name__ == "__main__": import uvicorn # 使用127.0.0.1而不是0.0.0.0来避免权限问题 uvicorn.run(app, host="127.0.0.1", port=9004, log_level="info")