1108 lines
38 KiB
Python
1108 lines
38 KiB
Python
|
|
#!/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")
|