Kamixitong/api_test_app_mysql.py

1108 lines
38 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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")