From 73800eeaa79b870438d965488a7e018890c38021 Mon Sep 17 00:00:00 2001 From: wsb1224 Date: Sun, 16 Nov 2025 13:33:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=B3=BB=E7=BB=9F=E4=B8=8E?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 +- .env.example | 2 +- api_test.html | 604 --------- api_test_app.py | 821 ------------ api_test_app_mysql.py | 1108 ----------------- app/models/audit_log.py | 21 +- .../utils/auth_validator.py | 139 +-- check_db.py | 40 - check_log_db.py | 74 -- check_products.py | 15 - login_page.html | Bin 6034 -> 0 bytes logs/kamaxitong.log | 125 +- logs/kamaxitong.log.10 | 182 ++- logs/kamaxitong.log.2 | 71 ++ simple_test.py | 40 - test_log.py | 21 - test_log_with_auth.py | 67 - test_login.py | 29 - test_web_log.py | 137 -- verify_log.py | 41 - 20 files changed, 259 insertions(+), 3280 deletions(-) delete mode 100644 api_test.html delete mode 100644 api_test_app.py delete mode 100644 api_test_app_mysql.py rename auth_validator.py => app/utils/auth_validator.py (79%) delete mode 100644 check_db.py delete mode 100644 check_log_db.py delete mode 100644 check_products.py delete mode 100644 login_page.html create mode 100644 logs/kamaxitong.log.2 delete mode 100644 simple_test.py delete mode 100644 test_log.py delete mode 100644 test_log_with_auth.py delete mode 100644 test_login.py delete mode 100644 test_web_log.py delete mode 100644 verify_log.py diff --git a/.env b/.env index aec4c70..1623f41 100644 --- a/.env +++ b/.env @@ -32,7 +32,7 @@ HOST=0.0.0.0 PORT=5000 # 文件上传配置 -MAX_CONTENT_LENGTH=16777216 +MAX_CONTENT_LENGTH=524288000 UPLOAD_FOLDER=static/uploads # 日志配置 diff --git a/.env.example b/.env.example index 99f6bf8..d89bb33 100644 --- a/.env.example +++ b/.env.example @@ -32,7 +32,7 @@ HOST=0.0.0.0 PORT=5000 # 文件上传配置 -MAX_CONTENT_LENGTH=16777216 +MAX_CONTENT_LENGTH=524288000 UPLOAD_FOLDER=static/uploads # 日志配置 diff --git a/api_test.html b/api_test.html deleted file mode 100644 index d15b205..0000000 --- a/api_test.html +++ /dev/null @@ -1,604 +0,0 @@ - - - - - - KaMiXiTong API测试平台 - - - -
-

KaMiXiTong API测试平台

-

这是一个用于测试KaMiXiTong系统所有API接口的前端页面。

- -
-
用户管理
-
工单管理
-
卡密管理
-
版本管理
-
设备管理
-
产品管理
-
- - -
-
-

创建管理员

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- -
-
-
- -
-

获取管理员列表

- -
-
- -
-

更新管理员

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- -
-
-
-
- - -
-
-

创建工单

-
-
- - -
-
- - -
-
- - -
-
- - -
- -
-
-
- -
-

获取工单列表

- -
-
-
- - -
-
-

生成卡密

-
-
- - -
-
- - -
-
- - -
-
- - -
- -
-
-
- -
-

获取卡密列表

- -
-
-
- - -
-
-

创建版本

-
-
- - -
-
- - -
-
- - -
-
- - -
- -
-
-
- -
-

获取版本列表

- -
-
-
- - -
-
-

获取设备列表

- -
-
-
- - -
-
-

创建产品

-
-
- - -
-
- - -
- -
-
-
- -
-

获取产品列表

- -
-
-
-
- - - - \ No newline at end of file diff --git a/api_test_app.py b/api_test_app.py deleted file mode 100644 index 2f86517..0000000 --- a/api_test_app.py +++ /dev/null @@ -1,821 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -FastAPI接口测试应用 -提供所有管理功能的API接口测试页面 -""" - -import os -import sys -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 -from sqlalchemy import create_engine, Column, Integer, String, Text, DateTime, Boolean, ForeignKey, func -from sqlalchemy.orm import declarative_base, sessionmaker, Session - -# 添加项目根目录到Python路径 -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -# 导入配置 -from config import Config - -# 数据库配置 -DATABASE_URL = Config.SQLALCHEMY_DATABASE_URI -engine = create_engine(DATABASE_URL) -SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) -Base = declarative_base() - -# 创建FastAPI应用 -app = FastAPI( - title="KaMiXiTong API测试平台", - description="软件授权管理系统的完整API接口测试平台", - version="1.0.0", - docs_url="/docs", - redoc_url="/redoc" -) - -# 添加CORS中间件 -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - -# 依赖项 -def get_db(): - db = SessionLocal() - try: - yield db - finally: - db.close() - -# ==================== 用户管理模型 ==================== -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 - -# ==================== 数据库模型 ==================== -# 用户管理表 -class DBAdmin(Base): - __tablename__ = "admin" - - admin_id = Column(Integer, primary_key=True) - username = Column(String(32), unique=True, nullable=False) - password_hash = Column(String(128), nullable=False) - email = Column(String(128), nullable=True) - role = Column(Integer, default=0) # 0=普通管理员, 1=超级管理员 - status = Column(Integer, default=1) # 0=禁用, 1=正常 - create_time = Column(DateTime, default=datetime.utcnow) - update_time = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) - is_deleted = Column(Integer, default=0) # 软删除标志 - - def __init__(self, **kwargs): - super(DBAdmin, self).__init__(**kwargs) - -# 工单表 -class DBTicket(Base): - __tablename__ = "ticket" - - ticket_id = Column(Integer, primary_key=True) - title = Column(String(128), nullable=False) - product_id = Column(String(32), nullable=False) - software_version = Column(String(32), nullable=True) - machine_code = Column(String(64), nullable=True) - license_key = Column(String(64), nullable=True) - description = Column(Text, nullable=False) - priority = Column(Integer, default=1) # 1=低, 2=中, 3=高 - status = Column(Integer, default=0) # 0=待处理, 1=处理中, 2=已解决, 3=已关闭 - create_time = Column(DateTime, default=datetime.utcnow) - update_time = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) - - def __init__(self, **kwargs): - super(DBTicket, self).__init__(**kwargs) - -# 卡密表 -class DBLicense(Base): - __tablename__ = "license" - - license_id = Column(Integer, primary_key=True) - product_id = Column(String(32), nullable=False) - license_key = Column(String(64), unique=True, nullable=False) - type = Column(Integer, default=1) # 0=试用, 1=正式 - status = Column(Integer, default=0) # 0=未使用, 1=已使用, 2=已过期, 3=已禁用 - create_time = Column(DateTime, default=datetime.utcnow) - update_time = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) - expire_time = Column(DateTime, nullable=True) - - def __init__(self, **kwargs): - super(DBLicense, self).__init__(**kwargs) - -# 版本表 -class DBVersion(Base): - __tablename__ = "version" - - version_id = Column(Integer, primary_key=True) - product_id = Column(String(32), nullable=False) - version_num = Column(String(32), nullable=False) - platform = Column(String(32), nullable=True) - description = Column(Text, nullable=True) - update_log = Column(Text, nullable=True) - download_url = Column(String(256), nullable=True) - file_hash = Column(String(64), nullable=True) - force_update = Column(Integer, default=0) - download_status = Column(Integer, default=1) # 0=下架, 1=上架 - min_license_version = Column(String(32), nullable=True) - publish_status = Column(Integer, default=0) # 0=未发布, 1=已发布 - create_time = Column(DateTime, default=datetime.utcnow) - update_time = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) - - def __init__(self, **kwargs): - super(DBVersion, self).__init__(**kwargs) - -# 设备表 -class DBDevice(Base): - __tablename__ = "device" - - device_id = Column(Integer, primary_key=True) - product_id = Column(String(32), nullable=False) - machine_code = Column(String(64), nullable=False) - software_version = Column(String(32), nullable=True) - status = Column(Integer, default=1) # 0=禁用, 1=正常, 2=黑名单 - create_time = Column(DateTime, default=datetime.utcnow) - last_verify_time = Column(DateTime, nullable=True) - - def __init__(self, **kwargs): - super(DBDevice, self).__init__(**kwargs) - -# 产品表 -class DBProduct(Base): - __tablename__ = "product" - - product_id = Column(String(32), primary_key=True) - product_name = Column(String(64), nullable=False) - description = Column(Text, nullable=True) - status = Column(Integer, default=1) # 0=禁用, 1=正常 - create_time = Column(DateTime, default=datetime.utcnow) - update_time = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) - - def __init__(self, **kwargs): - super(DBProduct, self).__init__(**kwargs) - -# ==================== 用户管理接口 ==================== -@app.get("/") -async def root(): - return {"message": "欢迎使用KaMiXiTong API测试平台", "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, - db: Session = Depends(get_db) -): - """获取管理员列表""" - query = db.query(DBAdmin).filter(DBAdmin.is_deleted == 0) - - if keyword: - query = query.filter(DBAdmin.username.contains(keyword)) - - if role is not None: - query = query.filter(DBAdmin.role == role) - - if status is not None: - query = query.filter(DBAdmin.status == status) - - admins = query.offset(skip).limit(limit).all() - return admins - -@app.post("/admins", response_model=AdminInDB) -async def create_admin(admin: AdminCreate, db: Session = Depends(get_db)): - """创建管理员""" - # 检查用户名是否已存在 - existing = db.query(DBAdmin).filter( - DBAdmin.username == admin.username, - DBAdmin.is_deleted == 0 - ).first() - - if existing: - raise HTTPException(status_code=400, detail="用户名已存在") - - # 创建管理员(简化密码处理) - db_admin = DBAdmin( - username=admin.username, - email=admin.email, - role=admin.role, - status=admin.status, - password_hash=f"hashed_{admin.password}" # 简化处理 - ) - - db.add(db_admin) - db.commit() - db.refresh(db_admin) - return db_admin - -@app.get("/admins/{admin_id}", response_model=AdminInDB) -async def get_admin(admin_id: int, db: Session = Depends(get_db)): - """获取管理员详情""" - admin = db.query(DBAdmin).filter( - DBAdmin.admin_id == admin_id, - DBAdmin.is_deleted == 0 - ).first() - - if not admin: - raise HTTPException(status_code=404, detail="管理员不存在") - return admin - -@app.put("/admins/{admin_id}", response_model=AdminInDB) -async def update_admin(admin_id: int, admin: AdminUpdate, db: Session = Depends(get_db)): - """更新管理员""" - db_admin = db.query(DBAdmin).filter( - DBAdmin.admin_id == admin_id, - DBAdmin.is_deleted == 0 - ).first() - - if not db_admin: - raise HTTPException(status_code=404, detail="管理员不存在") - - # 更新字段 - if admin.username and admin.username != db_admin.username: - # 检查新用户名是否已存在 - existing = db.query(DBAdmin).filter( - DBAdmin.username == admin.username, - DBAdmin.admin_id != admin_id, - DBAdmin.is_deleted == 0 - ).first() - - if existing: - raise HTTPException(status_code=400, detail="用户名已存在") - db_admin.username = admin.username - - if admin.email is not None: - db_admin.email = admin.email - if admin.role is not None: - db_admin.role = admin.role - if admin.status is not None: - db_admin.status = admin.status - if admin.password: - db_admin.password_hash = f"hashed_{admin.password}" # 简化处理 - - db.commit() - db.refresh(db_admin) - return db_admin - -@app.delete("/admins/{admin_id}") -async def delete_admin(admin_id: int, db: Session = Depends(get_db)): - """删除管理员(软删除)""" - db_admin = db.query(DBAdmin).filter( - DBAdmin.admin_id == admin_id, - DBAdmin.is_deleted == 0 - ).first() - - if not db_admin: - raise HTTPException(status_code=404, detail="管理员不存在") - - db_admin.is_deleted = 1 - db.commit() - return {"message": "管理员删除成功"} - -@app.post("/admins/{admin_id}/toggle-status") -async def toggle_admin_status(admin_id: int, db: Session = Depends(get_db)): - """切换管理员状态""" - db_admin = db.query(DBAdmin).filter( - DBAdmin.admin_id == admin_id, - DBAdmin.is_deleted == 0 - ).first() - - if not db_admin: - raise HTTPException(status_code=404, detail="管理员不存在") - - db_admin.status = 0 if db_admin.status == 1 else 1 - db.commit() - - status_name = "正常" if db_admin.status == 1 else "禁用" - action = "启用" if db_admin.status == 1 else "禁用" - return {"message": f"管理员已{action}", "status": db_admin.status, "status_name": status_name} - -# ==================== 工单管理接口 ==================== -@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, - db: Session = Depends(get_db) -): - """获取工单列表""" - query = db.query(DBTicket) - - if status is not None: - query = query.filter(DBTicket.status == status) - if priority is not None: - query = query.filter(DBTicket.priority == priority) - if product_id: - query = query.filter(DBTicket.product_id == product_id) - - query = query.order_by(DBTicket.create_time.desc()) - tickets = query.offset(skip).limit(limit).all() - return tickets - -@app.post("/tickets", response_model=TicketInDB) -async def create_ticket(ticket: TicketCreate, db: Session = Depends(get_db)): - """创建工单""" - # 验证产品存在(简化处理) - db_ticket = DBTicket(**ticket.model_dump()) - db.add(db_ticket) - db.commit() - db.refresh(db_ticket) - return db_ticket - -@app.put("/tickets/batch/status") -async def batch_update_ticket_status( - ticket_ids: List[int], - status: int, - remark: Optional[str] = None, - db: Session = Depends(get_db) -): - """批量更新工单状态""" - if status not in [0, 1, 2, 3]: - raise HTTPException(status_code=400, detail="无效的状态值") - - # 查找所有要更新的工单 - tickets = db.query(DBTicket).filter(DBTicket.ticket_id.in_(ticket_ids)).all() - if len(tickets) != len(ticket_ids): - found_ids = [t.ticket_id for t in tickets] - missing_ids = [tid for tid in ticket_ids if tid not in found_ids] - raise HTTPException(status_code=404, detail=f"以下工单不存在: {', '.join(map(str, missing_ids))}") - - # 批量更新工单状态 - for ticket in tickets: - ticket.status = status - ticket.update_time = datetime.utcnow() - - db.commit() - - status_names = {0: '待处理', 1: '处理中', 2: '已解决', 3: '已关闭'} - status_name = status_names.get(status, '未知') - return {"message": f"成功将 {len(tickets)} 个工单状态更新为{status_name}"} - -# ==================== 卡密管理接口 ==================== -@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, - db: Session = Depends(get_db) -): - """获取卡密列表""" - query = db.query(DBLicense) - - if product_id: - query = query.filter(DBLicense.product_id == product_id) - if status is not None: - query = query.filter(DBLicense.status == status) - if license_type is not None: - query = query.filter(DBLicense.type == license_type) - if keyword: - query = query.filter(func.lower(DBLicense.license_key).like(f"%{keyword.lower()}%")) - - query = query.order_by(DBLicense.create_time.desc()) - licenses = query.offset(skip).limit(limit).all() - return licenses - -@app.post("/licenses", response_model=dict) -async def generate_licenses(license: LicenseCreate, db: Session = Depends(get_db)): - """批量生成卡密""" - # 验证参数 - 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 - - 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): - existing = db.query(DBLicense).filter(DBLicense.license_key == key).first() - 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) - - # 创建卡密对象 - db_license = DBLicense( - product_id=license.product_id, - license_key=key, - type=license.type, - status=0, # 未使用 - expire_time=expire_time - ) - licenses.append(db_license) - - # 批量保存 - db.add_all(licenses) - db.commit() - - # 格式化结果 - license_data = [] - for db_license in licenses: - db.refresh(db_license) - license_data.append(LicenseInDB.model_validate(db_license)) - - return { - "message": f"成功生成 {license.count} 个卡密", - "licenses": license_data, - "count": len(licenses) - } - -# ==================== 版本管理接口 ==================== -@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, - db: Session = Depends(get_db) -): - """获取版本列表""" - query = db.query(DBVersion) - - if product_id: - query = query.filter(DBVersion.product_id == product_id) - if publish_status is not None: - query = query.filter(DBVersion.publish_status == publish_status) - - query = query.order_by(DBVersion.create_time.desc()) - versions = query.offset(skip).limit(limit).all() - return versions - -@app.post("/versions", response_model=VersionInDB) -async def create_version(version: VersionCreate, db: Session = Depends(get_db)): - """创建版本""" - # 验证产品存在(简化处理) - if not version.product_id or not version.version_num: - raise HTTPException(status_code=400, detail="缺少必要参数") - - # 检查版本号是否重复 - existing = db.query(DBVersion).filter( - DBVersion.product_id == version.product_id, - DBVersion.version_num == version.version_num - ).first() - - if existing: - raise HTTPException(status_code=400, detail="版本号已存在") - - # 创建版本 - db_version = DBVersion(**version.model_dump(exclude={'publish_now'})) - db.add(db_version) - db.commit() - db.refresh(db_version) - - # 如果选择了立即发布,则发布版本 - if version.publish_now: - db_version.publish_status = 1 - db.commit() - db.refresh(db_version) - - return db_version - -@app.post("/versions/{version_id}/publish") -async def publish_version(version_id: int, db: Session = Depends(get_db)): - """发布版本""" - version = db.query(DBVersion).filter(DBVersion.version_id == version_id).first() - if not version: - raise HTTPException(status_code=404, detail="版本不存在") - - version.publish_status = 1 - version.update_time = datetime.utcnow() - db.commit() - db.refresh(version) - - return {"message": "版本发布成功", "version": VersionInDB.model_validate(version)} - -# ==================== 设备管理接口 ==================== -@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, - db: Session = Depends(get_db) -): - """获取设备列表""" - query = db.query(DBDevice) - - if product_id: - query = query.filter(DBDevice.product_id == product_id) - if software_version: - query = query.filter(DBDevice.software_version == software_version) - if status is not None: - query = query.filter(DBDevice.status == status) - if keyword: - query = query.filter(DBDevice.machine_code.contains(keyword)) - - query = query.order_by(DBDevice.last_verify_time.desc()) - devices = query.offset(skip).limit(limit).all() - return devices - -@app.put("/devices/{device_id}/status") -async def update_device_status(device_id: int, status: int, db: Session = Depends(get_db)): - """更新设备状态""" - if status not in [0, 1, 2]: - raise HTTPException(status_code=400, detail="无效的状态值") - - device = db.query(DBDevice).filter(DBDevice.device_id == device_id).first() - if not device: - raise HTTPException(status_code=404, detail="设备不存在") - - device.status = status - device.last_verify_time = datetime.utcnow() - db.commit() - db.refresh(device) - - return {"message": "设备状态更新成功", "device": DeviceInDB.model_validate(device)} - -@app.delete("/devices/{device_id}") -async def delete_device(device_id: int, db: Session = Depends(get_db)): - """删除设备""" - device = db.query(DBDevice).filter(DBDevice.device_id == device_id).first() - if not device: - raise HTTPException(status_code=404, detail="设备不存在") - - db.delete(device) - db.commit() - return {"message": "设备删除成功"} - -@app.delete("/devices/batch") -async def batch_delete_devices(device_ids: List[int], db: Session = Depends(get_db)): - """批量删除设备""" - # 查找所有要删除的设备 - devices = db.query(DBDevice).filter(DBDevice.device_id.in_(device_ids)).all() - if len(devices) != len(device_ids): - found_ids = [d.device_id for d in devices] - missing_ids = [did for did in device_ids if did not in found_ids] - raise HTTPException(status_code=404, detail=f"以下设备不存在: {', '.join(map(str, missing_ids))}") - - # 批量删除设备 - for device in devices: - db.delete(device) - - db.commit() - return {"message": f"成功删除 {len(devices)} 个设备"} - -# ==================== 产品管理接口 ==================== -@app.get("/products", response_model=List[ProductInDB]) -async def get_products( - skip: int = 0, - limit: int = 100, - keyword: Optional[str] = None, - db: Session = Depends(get_db) -): - """获取产品列表""" - query = db.query(DBProduct) - - if keyword: - query = query.filter( - DBProduct.product_name.like(f"%{keyword}%") | - DBProduct.description.like(f"%{keyword}%") - ) - - query = query.order_by(DBProduct.create_time.desc()) - products = query.offset(skip).limit(limit).all() - return products - -@app.post("/products", response_model=ProductInDB) -async def create_product(product: ProductCreate, db: Session = Depends(get_db)): - """创建产品""" - if not product.product_name.strip(): - raise HTTPException(status_code=400, detail="产品名称不能为空") - - # 检查自定义ID是否重复 - if product.product_id: - existing = db.query(DBProduct).filter(DBProduct.product_id == product.product_id).first() - 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() - - db_product = DBProduct( - product_id=product_id, - product_name=product.product_name, - description=product.description, - status=product.status - ) - - db.add(db_product) - db.commit() - db.refresh(db_product) - return db_product - -@app.get("/products/{product_id}", response_model=ProductInDB) -async def get_product(product_id: str, db: Session = Depends(get_db)): - """获取产品详情""" - product = db.query(DBProduct).filter(DBProduct.product_id == product_id).first() - if not product: - raise HTTPException(status_code=404, detail="产品不存在") - return product - -@app.put("/products/{product_id}", response_model=ProductInDB) -async def update_product(product_id: str, product: ProductUpdate, db: Session = Depends(get_db)): - """更新产品""" - db_product = db.query(DBProduct).filter(DBProduct.product_id == product_id).first() - if not db_product: - raise HTTPException(status_code=404, detail="产品不存在") - - # 更新字段 - if product.product_name is not None: - db_product.product_name = product.product_name - if product.description is not None: - db_product.description = product.description - if product.status is not None: - db_product.status = product.status - - db_product.update_time = datetime.utcnow() - db.commit() - db.refresh(db_product) - return db_product - -@app.delete("/products/{product_id}") -async def delete_product(product_id: str, db: Session = Depends(get_db)): - """删除产品""" - product = db.query(DBProduct).filter(DBProduct.product_id == product_id).first() - if not product: - raise HTTPException(status_code=404, detail="产品不存在") - - db.delete(product) - db.commit() - return {"message": "产品删除成功"} - -if __name__ == "__main__": - import uvicorn - # 使用127.0.0.1而不是0.0.0.0来避免权限问题 - uvicorn.run(app, host="127.0.0.1", port=9003, log_level="info") \ No newline at end of file diff --git a/api_test_app_mysql.py b/api_test_app_mysql.py deleted file mode 100644 index 5768bfb..0000000 --- a/api_test_app_mysql.py +++ /dev/null @@ -1,1108 +0,0 @@ -#!/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") \ No newline at end of file diff --git a/app/models/audit_log.py b/app/models/audit_log.py index e93c1c4..4834301 100644 --- a/app/models/audit_log.py +++ b/app/models/audit_log.py @@ -1,5 +1,6 @@ from datetime import datetime from app import db +import json class AuditLog(db.Model): """审计日志模型""" @@ -23,6 +24,14 @@ class AuditLog(db.Model): def to_dict(self): """转换为字典""" + # 解析details字段中的JSON字符串 + details_data = None + if self.details: + try: + details_data = json.loads(self.details) + except (json.JSONDecodeError, TypeError): + details_data = self.details # 如果解析失败,返回原始字符串 + return { 'log_id': self.log_id, 'admin_id': self.admin_id, @@ -30,7 +39,7 @@ class AuditLog(db.Model): 'action': self.action, 'target_type': self.target_type, 'target_id': self.target_id, - 'details': self.details, + 'details': details_data, 'ip_address': self.ip_address, 'user_agent': self.user_agent, 'create_time': self.create_time.strftime('%Y-%m-%d %H:%M:%S') if self.create_time else None @@ -41,12 +50,20 @@ class AuditLog(db.Model): """记录审计日志""" from flask import current_app try: + # 将details字典序列化为JSON字符串 + details_str = None + if details is not None: + if isinstance(details, dict): + details_str = json.dumps(details, ensure_ascii=False) + else: + details_str = str(details) + log = AuditLog( admin_id=admin_id, action=action, target_type=target_type, target_id=target_id, - details=details, + details=details_str, ip_address=ip_address, user_agent=user_agent ) diff --git a/auth_validator.py b/app/utils/auth_validator.py similarity index 79% rename from auth_validator.py rename to app/utils/auth_validator.py index 263f5ea..a4fa0b5 100644 --- a/auth_validator.py +++ b/app/utils/auth_validator.py @@ -82,13 +82,13 @@ class MachineCodeGenerator: # 简化的加密工具 class SimpleCrypto: """简单的加密解密工具""" - + @staticmethod def generate_hash(data: str, salt: str = "") -> str: """生成哈希值""" combined = f"{data}{salt}".encode('utf-8') return hashlib.sha256(combined).hexdigest() - + @staticmethod def generate_signature(data: str, secret_key: str) -> str: """生成签名""" @@ -146,7 +146,7 @@ class AuthValidator: def __init__(self, software_id: str, api_url: str = "http://localhost:5000/api/v1", - secret_key: str = "default-secret-key", + secret_key: str = "taiyi1224", cache_days: int = 7, timeout: int = 3, gui_mode: bool = False): @@ -228,10 +228,11 @@ class AuthValidator: return "" def _validate_license_format(self, license_key: str) -> bool: - """验证卡密格式(支持XXXX-XXXX-XXXX-XXXX格式)""" + """验证卡密格式(与服务端保持一致)""" if not license_key: return False + # 去除空格和制表符,并转为大写 license_key = license_key.strip().replace(' ', '').replace('\t', '').upper() # 检查是否为XXXX-XXXX-XXXX-XXXX格式 @@ -242,8 +243,8 @@ class AuthValidator: # 检查所有字符是否为大写字母或数字 combined = ''.join(parts) if len(combined) == 32: - import re pattern = r'^[A-Z0-9]+$' + import re return bool(re.match(pattern, combined)) return False else: @@ -252,8 +253,8 @@ class AuthValidator: return False # 检查字符(只允许大写字母、数字和下划线) - import re pattern = r'^[A-Z0-9_]+$' + import re return bool(re.match(pattern, license_key)) def _input_license_key(self) -> str: @@ -406,117 +407,55 @@ class AuthValidator: max_attempts = 3 # 最多尝试3次 for attempt in range(max_attempts): - # 输入卡密 + # 获取卡密 license_key = self._input_license_key() if not license_key: - self._show_message("验证取消", "未输入卡密,程序退出", True) + self._show_message("验证取消", "用户取消了验证操作") return False # 验证卡密格式 if not self._validate_license_format(license_key): - self._show_message("格式错误", "卡密格式错误,请检查后重新输入", True) + self._show_message("格式错误", "卡密格式不正确,请重新输入", True) continue # 在线验证 success, message, auth_info = self._online_verify(license_key) - - if success and auth_info: - # 验证成功,缓存授权信息 - self._cache_auth_info(auth_info) - - # 检查是否需要更新 - force_update = auth_info.get('force_update', False) - download_url = auth_info.get('download_url') - new_version = auth_info.get('new_version') - - if force_update and download_url: - self._show_message("需要更新", f"发现新版本 {new_version}\n请下载更新后重新启动程序", True) - # 尝试打开下载链接 - try: - import webbrowser - webbrowser.open(download_url) - except: - pass - return False - - self._show_message("验证成功", f"授权验证成功!\n卡密: {license_key}\n有效期至: {auth_info.get('expire_time', '永久')}") + + if success: + # 缓存授权信息 + if auth_info: + self._cache_auth_info(auth_info) + self._show_message("验证成功", message) return True - else: - # 验证失败 + # 记录失败尝试 self.failed_attempts += 1 - self.last_attempt_time = datetime.utcnow() - - if self.failed_attempts >= 5: # 失败5次锁定 - self._lock_account() + if self.failed_attempts >= 3: + self._lock_account(10) # 锁定10分钟 self._show_message("验证失败", self._get_lock_message(), True) return False - - self._show_message("验证失败", f"验证失败: {message}\n剩余尝试次数: {5 - self.failed_attempts}", True) - - # 如果不是最后一次尝试,询问是否继续 - if attempt < max_attempts - 1: - if self.gui_mode: - try: - import tkinter as tk - from tkinter import messagebox - - root = tk.Tk() - root.withdraw() - result = messagebox.askyesno("继续验证", "是否继续输入卡密验证?") - root.destroy() - - if not result: - return False - except ImportError: - continue - else: - continue else: - return False + remaining_attempts = 3 - self.failed_attempts + self._show_message( + "验证失败", + f"{message}\n\n剩余尝试次数: {remaining_attempts}", + True + ) + continue + # 所有尝试都失败 + self._show_message("验证失败", "已达到最大尝试次数", True) return False - def get_software_info(self) -> Optional[Dict[str, Any]]: - """获取软件信息""" - try: - url = f"{self.api_url}/software/info" - params = {"software_id": self.software_id} + def get_auth_info(self) -> Optional[Dict[str, Any]]: + """ + 获取当前授权信息 + + Returns: + Optional[Dict[str, Any]]: 授权信息 + """ + return self.cache.get_auth_info(self.software_id) - response = requests.get(url, params=params, timeout=self.timeout) - if response.status_code == 200: - result = response.json() - if result.get('success'): - return result.get('data') - except Exception: - pass - return None - - def clear_cache(self): - """清除本地缓存""" - self.cache.clear_cache(self.software_id) - # 删除机器码缓存文件 - try: - if os.path.exists(".machine_code"): - os.remove(".machine_code") - except Exception: - pass - -# 便捷函数 -def validate_license(software_id: str, **kwargs) -> bool: - """ - 便捷的验证函数 - - Args: - software_id: 软件ID - **kwargs: 其他参数(api_url, secret_key, cache_days, timeout, gui_mode) - - Returns: - bool: 验证是否成功 - """ - validator = AuthValidator(software_id, **kwargs) - return validator.validate() - -def get_machine_code() -> str: - """获取当前机器码""" - return MachineCodeGenerator.generate() \ No newline at end of file + def clear_auth_cache(self): + """清除授权缓存""" + self.cache.clear_cache(self.software_id) \ No newline at end of file diff --git a/check_db.py b/check_db.py deleted file mode 100644 index c887bb9..0000000 --- a/check_db.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -检查数据库中的产品数据 -""" - -import os -import sys - -# 添加项目根目录到Python路径 -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -# 尝试加载.env文件 -try: - from dotenv import load_dotenv - if load_dotenv(): - print("成功加载.env文件") - else: - print("未找到或无法加载.env文件") -except ImportError: - print("python-dotenv未安装,跳过.env文件加载") - -from app import create_app, db -from app.models import Product - -# 创建应用实例 -app = create_app() - -with app.app_context(): - print("数据库URI:", app.config['SQLALCHEMY_DATABASE_URI']) - total_products = Product.query.count() - print(f"产品总数: {total_products}") - - if total_products > 0: - print("产品列表:") - products = Product.query.all() - for product in products: - print(f" - ID: {product.product_id}, 名称: {product.product_name}, 状态: {product.status}") - else: - print("数据库中没有产品数据") \ No newline at end of file diff --git a/check_log_db.py b/check_log_db.py deleted file mode 100644 index 5507bd8..0000000 --- a/check_log_db.py +++ /dev/null @@ -1,74 +0,0 @@ -import sqlite3 -import os - -def check_audit_logs(): - """检查审计日志表""" - try: - # 连接到数据库 - if os.path.exists('instance/kamaxitong.db'): - conn = sqlite3.connect('instance/kamaxitong.db') - cursor = conn.cursor() - - # 查询审计日志表 - print("=== 查询审计日志表 ===") - cursor.execute("SELECT * FROM audit_log ORDER BY create_time DESC LIMIT 10") - rows = cursor.fetchall() - - if rows: - print(f"找到 {len(rows)} 条审计日志记录:") - # 获取列名 - column_names = [description[0] for description in cursor.description] - print("列名:", column_names) - - for row in rows: - print(row) - else: - print("审计日志表为空") - - conn.close() - else: - print("数据库文件不存在") - - except Exception as e: - print(f"检查审计日志时出现错误: {e}") - -def check_log_file(): - """检查日志文件""" - try: - print("\n=== 检查日志文件 ===") - if os.path.exists('logs/kamaxitong.log'): - # 以二进制模式读取文件 - with open('logs/kamaxitong.log', 'rb') as f: - content = f.read() - print(f"日志文件大小: {len(content)} 字节") - - # 尝试以不同编码读取 - try: - text_content = content.decode('utf-8') - lines = text_content.split('\n') - print(f"日志文件行数: {len(lines)}") - print("最后10行:") - for line in lines[-10:]: - if line.strip(): - print(line.strip()) - except UnicodeDecodeError: - # 尝试其他编码 - try: - text_content = content.decode('gbk') - lines = text_content.split('\n') - print(f"日志文件行数: {len(lines)} (GBK编码)") - print("最后10行:") - for line in lines[-10:]: - if line.strip(): - print(line.strip()) - except UnicodeDecodeError: - print("无法解码日志文件内容") - else: - print("日志文件不存在") - except Exception as e: - print(f"检查日志文件时出现错误: {e}") - -if __name__ == "__main__": - print("检查日志系统...") - check_audit_logs() - check_log_file() \ No newline at end of file diff --git a/check_products.py b/check_products.py deleted file mode 100644 index 3a67534..0000000 --- a/check_products.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -os.environ.setdefault('FLASK_CONFIG', 'development') - -from app import create_app, db -from app.models import Product - -app = create_app() - -with app.app_context(): - products = Product.query.all() - print('Total products:', len(products)) - print('Product names:', [p.product_name for p in products]) - print('Product details:') - for p in products: - print(f' ID: {p.product_id}, Name: {p.product_name}, Status: {p.status}') \ No newline at end of file diff --git a/login_page.html b/login_page.html deleted file mode 100644 index bf995257f6178cc1d61ca42075878400aecdd2c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6034 zcmb`L+fo}x5Qh6I7pclS$iB8X7&~zsiAn$yLyT|?c7k0lmn?~c0ST`#$B^sXUt=on@wMUF? zBe!pRHXAfm%U&IAT*}puZG*dIO}lMpjMvfM;=f;G4^3UCTxwebFM9aW#-|Qew29a- zjL5F%c)W;Z58QTD>F=XmXwrN{MtXKkZU!J8G9r9sF_paTn$Mjy9;2~~rfu}AR)o;u zTF*VX<3gyRq0d*<($lGQ z)*t7e9sd2xpT9P2l9`^k$X;3Kt0NCW=EE&)meq4)aRBa+Z$hjN7@LQ|6X7G>&lo)p zJRZ+DaqTe`w+_c9Lwu(hiTuu3o1$NLeF0)(r-AWe`oMO{W6u@K*dh8zb?_|q5LwwF z!h^sQPuz4`dCVOivivXhU- zYtzW95r6Z1@I;{>v}D{VYRxJ*&kJwI&Nwtqd&&=a6#3!tp^8h@{CGXeW6Z)5S!}~~ zRn*5bna=OSVzF(JtoF!n_lji9+P7q1c~@4F&_7q`EKL*Bv+;GFsuRR>xMNxNy z`fbsrwLef4W5;RIWwZvX);tK>mx&pBAZcY4HFYx_67~cZ{uqy5d4O zLv|mTQ*@mLUFs2C?A84;R+$uDk6&KS;c@PxU z%=Wmy)Yldprm?Wq{^= znmkBTJ`=kCG8?GW+ib>LC#O$JnY|s}lV!Or`x9B>@ga+{XPxjM#I9DhRF$QZVAVRIczO`Z zv6t?QS;gp|T<$DA2 [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:157] +2025-11-16 13:06:36,695 INFO: 汾Ƿظ: product_id=ArticleReplace, version_num=1.0 [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:162] +2025-11-16 13:06:36,697 INFO: 汾: product_id=ArticleReplace, version_num=1.0 [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:172] +2025-11-16 13:06:36,699 INFO: Ӱ汾ݿ [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:186] +2025-11-16 13:06:36,699 INFO: ύݿ [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:188] +2025-11-16 13:06:36,708 INFO: Ƿ: publish_now=False [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:191] +2025-11-16 13:06:36,715 ERROR: ¼־ʧ: (pymysql.err.ProgrammingError) (1064, 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'\'product_id\': "\'ArticleReplace\'", \'version_num\': "\'1.0\'", \'publish_now\': \'0\'}, \'\' at line 1') [SQL: INSERT INTO audit_log (admin_id, action, target_type, target_id, details, ip_address, user_agent, create_time) VALUES (%(admin_id)s, %(action)s, %(target_type)s, %(target_id)s, %(details)s, %(ip_address)s, %(user_agent)s, %(create_time)s)] -[parameters: {'admin_id': 2, 'action': 'UPDATE', 'target_type': 'ADMIN', 'target_id': 2, 'details': {'old': {'email': '2339117167@qq.com', 'role': 1, 'status': 1}, 'new': {'email': '2339117167@qq.com', 'role': 0, 'status': 1}}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 15, 14, 20, 36, 556170)}] -(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\app\models\audit_log.py:59] -2025-11-15 22:20:36,594 ERROR: ¼־ʧ: (pymysql.err.ProgrammingError) (1064, 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'\'old\': {\'email\': "\'2339117167@qq.com\'", \'role\': \'1\', \'status\': \'1\'}, \'new\': {\'em\' at line 1') +[parameters: {'admin_id': 1, 'action': 'CREATE_VERSION', 'target_type': 'VERSION', 'target_id': 2, 'details': {'product_id': 'ArticleReplace', 'version_num': '1.0', 'publish_now': False}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 16, 5, 6, 36, 714215)}] +(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\master\app\models\audit_log.py:59] +2025-11-16 13:06:43,379 ERROR: ¼־ʧ: (pymysql.err.ProgrammingError) (1064, 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'\'version_num\': "\'1.0\'", \'status\': \'1\', \'status_name\': "\'\'"}, \'127.0.0.1\', \' at line 1') [SQL: INSERT INTO audit_log (admin_id, action, target_type, target_id, details, ip_address, user_agent, create_time) VALUES (%(admin_id)s, %(action)s, %(target_type)s, %(target_id)s, %(details)s, %(ip_address)s, %(user_agent)s, %(create_time)s)] -[parameters: {'admin_id': 2, 'action': 'UPDATE_ADMIN', 'target_type': 'ADMIN', 'target_id': 2, 'details': {'old': {'email': '2339117167@qq.com', 'role': 1, 'status': 1}, 'new': {'email': '2339117167@qq.com', 'role': 0, 'status': 1}}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 15, 14, 20, 36, 584730)}] -(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\app\models\audit_log.py:59] -2025-11-15 22:21:39,838 ERROR: ¼־ʧ: (pymysql.err.ProgrammingError) (1064, 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'\'username\': "\'test\'", \'role\': \'0\', \'status\': \'1\'}, \'127.0.0.1\', \'Mozilla/5.0 (Wi\' at line 1') +[parameters: {'admin_id': 1, 'action': 'UPDATE_VERSION_STATUS', 'target_type': 'VERSION', 'target_id': 2, 'details': {'version_num': '1.0', 'status': 1, 'status_name': ''}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 16, 5, 6, 43, 377554)}] +(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\master\app\models\audit_log.py:59] +2025-11-16 13:07:09,664 ERROR: ¼־ʧ: (pymysql.err.ProgrammingError) (1064, 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'\'product_id\': "\'ArticleReplace\'", \'count\': \'1\', \'license_type\': \'1\', \'license_ke\' at line 1') [SQL: INSERT INTO audit_log (admin_id, action, target_type, target_id, details, ip_address, user_agent, create_time) VALUES (%(admin_id)s, %(action)s, %(target_type)s, %(target_id)s, %(details)s, %(ip_address)s, %(user_agent)s, %(create_time)s)] -[parameters: {'admin_id': 2, 'action': 'CREATE', 'target_type': 'ADMIN', 'target_id': 5, 'details': {'username': 'test', 'role': 0, 'status': 1}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 15, 14, 21, 39, 834164)}] -(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\app\models\audit_log.py:59] -2025-11-15 22:21:46,162 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 22:21:46,434 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 22:21:46,482 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +[parameters: {'admin_id': 1, 'action': 'GENERATE_LICENSES', 'target_type': 'LICENSE', 'target_id': None, 'details': {'product_id': 'ArticleReplace', 'count': 1, 'license_type': 1, 'license_keys': ['4SGGNAPF-HPGNQC1Z-6D7OH879-9BGW32PI']}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 16, 5, 7, 9, 662553)}] +(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\master\app\models\audit_log.py:59] +2025-11-16 13:07:44,918 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:10:12,691 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:11:09,687 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:14:50,942 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:15:22,908 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:15:40,597 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:17:33,606 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:18:26,492 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:18:38,007 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:18:49,532 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:21:42,076 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:21:54,735 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:24:17,919 ERROR: ¿ʧ: 'License' object has no attribute 'remark' [in D:\work\code\python\KaMiXiTong\master\app\api\license.py:270] +2025-11-16 13:24:20,319 ERROR: ¼־ʧ: (pymysql.err.ProgrammingError) (1064, 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'\'license_key\': "\'4SGGNAPF-HPGNQC1Z-6D7OH879-9BGW32PI\'"}, \'127.0.0.1\', \'Mozilla/5\' at line 1') +[SQL: INSERT INTO audit_log (admin_id, action, target_type, target_id, details, ip_address, user_agent, create_time) VALUES (%(admin_id)s, %(action)s, %(target_type)s, %(target_id)s, %(details)s, %(ip_address)s, %(user_agent)s, %(create_time)s)] +[parameters: {'admin_id': 1, 'action': 'DELETE_LICENSE', 'target_type': 'LICENSE', 'target_id': None, 'details': {'license_key': '4SGGNAPF-HPGNQC1Z-6D7OH879-9BGW32PI'}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 16, 5, 24, 20, 318782)}] +(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\master\app\models\audit_log.py:59] +2025-11-16 13:24:53,054 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:26:10,998 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:32:27,257 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 13:32:51,116 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] diff --git a/logs/kamaxitong.log.10 b/logs/kamaxitong.log.10 index 88d8fd2..5f36527 100644 --- a/logs/kamaxitong.log.10 +++ b/logs/kamaxitong.log.10 @@ -1,102 +1,80 @@ -2025-11-15 14:20:05,639 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:20:07,103 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:20:55,708 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:20:57,062 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:21:28,777 ERROR: ȡͳʧ: (pymysql.err.ProgrammingError) (1146, "Table 'kamaxitong.api' doesn't exist") -[SQL: SELECT count(*) AS count_1 -FROM (SELECT api.api_id AS api_api_id, api.api_name AS api_api_name, api.description AS api_description, api.status AS api_status, api.create_time AS api_create_time, api.update_time AS api_update_time -FROM api) AS anon_1] -(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\app\api\statistics.py:86] -2025-11-15 14:22:29,153 ERROR: ȡͳʧ: (pymysql.err.ProgrammingError) (1146, "Table 'kamaxitong.api' doesn't exist") -[SQL: SELECT count(*) AS count_1 -FROM (SELECT api.api_id AS api_api_id, api.api_name AS api_api_name, api.description AS api_description, api.status AS api_status, api.create_time AS api_create_time, api.update_time AS api_update_time -FROM api) AS anon_1] -(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\app\api\statistics.py:86] -2025-11-15 14:22:40,129 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:23:10,648 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:23:11,851 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:23:23,719 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:23:29,274 ERROR: ȡͳʧ: (pymysql.err.ProgrammingError) (1146, "Table 'kamaxitong.api' doesn't exist") -[SQL: SELECT count(*) AS count_1 -FROM (SELECT api.api_id AS api_api_id, api.api_name AS api_api_name, api.description AS api_description, api.status AS api_status, api.create_time AS api_create_time, api.update_time AS api_update_time -FROM api) AS anon_1] -(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\app\api\statistics.py:86] -2025-11-15 14:24:29,144 ERROR: ȡͳʧ: (pymysql.err.ProgrammingError) (1146, "Table 'kamaxitong.api' doesn't exist") -[SQL: SELECT count(*) AS count_1 -FROM (SELECT api.api_id AS api_api_id, api.api_name AS api_api_name, api.description AS api_description, api.status AS api_status, api.create_time AS api_create_time, api.update_time AS api_update_time -FROM api) AS anon_1] -(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\app\api\statistics.py:86] -2025-11-15 14:25:07,065 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:25:07,424 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:25:19,299 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:25:29,262 ERROR: ȡͳʧ: (pymysql.err.ProgrammingError) (1146, "Table 'kamaxitong.api' doesn't exist") -[SQL: SELECT count(*) AS count_1 -FROM (SELECT api.api_id AS api_api_id, api.api_name AS api_api_name, api.description AS api_description, api.status AS api_status, api.create_time AS api_create_time, api.update_time AS api_update_time -FROM api) AS anon_1] -(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\app\api\statistics.py:86] -2025-11-15 14:26:36,221 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:26:38,495 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:27:13,078 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:27:20,119 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:28:43,984 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:29:11,098 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:29:42,354 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:29:53,601 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:30:24,671 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:30:26,018 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:34:01,346 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:34:02,697 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:40:16,572 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:40:16,603 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:40:26,594 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:40:26,878 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:40:30,511 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:40:31,139 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:41:45,467 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:41:45,860 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:41:49,111 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:41:49,214 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:42:22,440 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 14:42:22,747 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:08:23,832 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:08:23,851 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:10:01,187 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:10:02,285 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:12:33,664 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:12:35,067 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:13:54,881 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:13:55,572 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:16:39,819 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:18:12,136 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:18:12,364 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:18:12,364 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:18:17,816 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:22:38,979 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:25:24,198 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:30:01,058 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:32:09,383 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:32:44,404 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:35:30,494 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:35:54,826 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:36:11,573 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:36:11,781 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:36:11,781 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:36:18,774 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:36:23,481 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:36:27,849 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:38:39,259 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 15:44:39,290 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 16:14:14,052 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 16:14:14,273 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 16:14:14,273 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 16:14:30,243 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 16:14:31,707 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 20:56:49,158 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 21:07:59,431 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 21:08:00,550 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 21:26:31,566 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 21:27:36,458 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 21:29:02,008 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 21:29:47,827 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 21:29:51,087 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] -2025-11-15 21:29:56,366 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 21:43:14,795 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 21:43:16,011 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 21:47:51,654 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 21:47:58,940 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 21:52:14,483 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 21:52:15,836 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 21:53:12,767 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 21:53:12,928 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 21:55:02,780 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 21:55:05,971 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 21:56:57,235 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 21:56:57,396 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:00:51,287 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:02:34,407 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:02:36,353 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:03:17,696 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:03:18,658 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:05:58,479 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:05:58,767 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:12:33,295 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:12:34,640 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:17,117 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:17,190 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:18,245 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:24,345 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:24,378 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:24,686 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:30,459 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:30,476 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:30,487 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:35,024 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:35,074 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:35,227 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:43,938 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:44,004 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:44,004 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:55,395 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:55,501 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:14:56,055 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:00,950 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:01,093 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:01,290 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:10,768 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:12,443 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:13,140 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:22,104 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:22,684 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:22,758 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:36,038 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:37,583 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:37,723 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:55,634 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:55,694 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:15:55,725 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:16:05,643 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:16:05,813 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:16:06,131 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:16:34,892 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:16:34,916 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:16:35,168 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:16:52,773 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:16:52,821 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:16:53,293 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:18:58,174 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:18:59,209 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:20:36,569 ERROR: ¼־ʧ: (pymysql.err.ProgrammingError) (1064, 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'\'old\': {\'email\': "\'2339117167@qq.com\'", \'role\': \'1\', \'status\': \'1\'}, \'new\': {\'em\' at line 1') +[SQL: INSERT INTO audit_log (admin_id, action, target_type, target_id, details, ip_address, user_agent, create_time) VALUES (%(admin_id)s, %(action)s, %(target_type)s, %(target_id)s, %(details)s, %(ip_address)s, %(user_agent)s, %(create_time)s)] +[parameters: {'admin_id': 2, 'action': 'UPDATE', 'target_type': 'ADMIN', 'target_id': 2, 'details': {'old': {'email': '2339117167@qq.com', 'role': 1, 'status': 1}, 'new': {'email': '2339117167@qq.com', 'role': 0, 'status': 1}}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 15, 14, 20, 36, 556170)}] +(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\app\models\audit_log.py:59] +2025-11-15 22:20:36,594 ERROR: ¼־ʧ: (pymysql.err.ProgrammingError) (1064, 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'\'old\': {\'email\': "\'2339117167@qq.com\'", \'role\': \'1\', \'status\': \'1\'}, \'new\': {\'em\' at line 1') +[SQL: INSERT INTO audit_log (admin_id, action, target_type, target_id, details, ip_address, user_agent, create_time) VALUES (%(admin_id)s, %(action)s, %(target_type)s, %(target_id)s, %(details)s, %(ip_address)s, %(user_agent)s, %(create_time)s)] +[parameters: {'admin_id': 2, 'action': 'UPDATE_ADMIN', 'target_type': 'ADMIN', 'target_id': 2, 'details': {'old': {'email': '2339117167@qq.com', 'role': 1, 'status': 1}, 'new': {'email': '2339117167@qq.com', 'role': 0, 'status': 1}}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 15, 14, 20, 36, 584730)}] +(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\app\models\audit_log.py:59] +2025-11-15 22:21:39,838 ERROR: ¼־ʧ: (pymysql.err.ProgrammingError) (1064, 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'\'username\': "\'test\'", \'role\': \'0\', \'status\': \'1\'}, \'127.0.0.1\', \'Mozilla/5.0 (Wi\' at line 1') +[SQL: INSERT INTO audit_log (admin_id, action, target_type, target_id, details, ip_address, user_agent, create_time) VALUES (%(admin_id)s, %(action)s, %(target_type)s, %(target_id)s, %(details)s, %(ip_address)s, %(user_agent)s, %(create_time)s)] +[parameters: {'admin_id': 2, 'action': 'CREATE', 'target_type': 'ADMIN', 'target_id': 5, 'details': {'username': 'test', 'role': 0, 'status': 1}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 15, 14, 21, 39, 834164)}] +(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\app\models\audit_log.py:59] +2025-11-15 22:21:46,162 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:21:46,434 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] +2025-11-15 22:21:46,482 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\config.py:66] diff --git a/logs/kamaxitong.log.2 b/logs/kamaxitong.log.2 new file mode 100644 index 0000000..1586e59 --- /dev/null +++ b/logs/kamaxitong.log.2 @@ -0,0 +1,71 @@ +2025-11-15 23:58:20,986 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-15 23:58:22,596 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 11:34:44,087 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 11:34:48,148 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 11:36:10,246 ERROR: ¼־ʧ: (pymysql.err.ProgrammingError) (1064, 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'\'product_name\': "\'ԲƷB\'"}, \'127.0.0.1\', \'Mozilla/5.0 (Windows NT 10.0; \' at line 1') +[SQL: INSERT INTO audit_log (admin_id, action, target_type, target_id, details, ip_address, user_agent, create_time) VALUES (%(admin_id)s, %(action)s, %(target_type)s, %(target_id)s, %(details)s, %(ip_address)s, %(user_agent)s, %(create_time)s)] +[parameters: {'admin_id': 1, 'action': 'DELETE_PRODUCT', 'target_type': 'PRODUCT', 'target_id': 'PROD_23EF726D', 'details': {'product_name': 'ԲƷB'}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 16, 3, 36, 10, 245687)}] +(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\master\app\models\audit_log.py:59] +2025-11-16 11:36:15,609 ERROR: ¼־ʧ: (pymysql.err.ProgrammingError) (1064, 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'\'product_name\': "\'ԲƷA\'"}, \'127.0.0.1\', \'Mozilla/5.0 (Windows NT 10.0; \' at line 1') +[SQL: INSERT INTO audit_log (admin_id, action, target_type, target_id, details, ip_address, user_agent, create_time) VALUES (%(admin_id)s, %(action)s, %(target_type)s, %(target_id)s, %(details)s, %(ip_address)s, %(user_agent)s, %(create_time)s)] +[parameters: {'admin_id': 1, 'action': 'DELETE_PRODUCT', 'target_type': 'PRODUCT', 'target_id': 'PROD_897EF967', 'details': {'product_name': 'ԲƷA'}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 16, 3, 36, 15, 608798)}] +(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\master\app\models\audit_log.py:59] +2025-11-16 11:36:28,535 ERROR: ¼־ʧ: (pymysql.err.ProgrammingError) (1064, 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'\'license_key\': "\'W7XGEZU0-MFLWWKUK-XWPWFW3V-0LE77N2K\'"}, \'127.0.0.1\', \'Mozilla/5\' at line 1') +[SQL: INSERT INTO audit_log (admin_id, action, target_type, target_id, details, ip_address, user_agent, create_time) VALUES (%(admin_id)s, %(action)s, %(target_type)s, %(target_id)s, %(details)s, %(ip_address)s, %(user_agent)s, %(create_time)s)] +[parameters: {'admin_id': 1, 'action': 'DELETE_LICENSE', 'target_type': 'LICENSE', 'target_id': None, 'details': {'license_key': 'W7XGEZU0-MFLWWKUK-XWPWFW3V-0LE77N2K'}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 16, 3, 36, 28, 535307)}] +(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\master\app\models\audit_log.py:59] +2025-11-16 11:36:34,617 ERROR: ¼־ʧ: (pymysql.err.ProgrammingError) (1064, 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'\'product_name\': "\'ԲƷC\'"}, \'127.0.0.1\', \'Mozilla/5.0 (Windows NT 10.0; \' at line 1') +[SQL: INSERT INTO audit_log (admin_id, action, target_type, target_id, details, ip_address, user_agent, create_time) VALUES (%(admin_id)s, %(action)s, %(target_type)s, %(target_id)s, %(details)s, %(ip_address)s, %(user_agent)s, %(create_time)s)] +[parameters: {'admin_id': 1, 'action': 'DELETE_PRODUCT', 'target_type': 'PRODUCT', 'target_id': 'PROD_A24B55D2', 'details': {'product_name': 'ԲƷC'}, 'ip_address': '127.0.0.1', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0', 'create_time': datetime.datetime(2025, 11, 16, 3, 36, 34, 616101)}] +(Background on this error at: https://sqlalche.me/e/20/f405) [in D:\work\code\python\KaMiXiTong\master\app\models\audit_log.py:59] +2025-11-16 12:12:41,268 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:13:10,962 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:13:13,050 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:14:26,744 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:14:34,528 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:14:44,691 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:56,754 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,016 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,016 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,024 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,024 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,024 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,039 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,039 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,039 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,039 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,092 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,092 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,092 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,092 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,092 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,343 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,343 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,343 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,343 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,343 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,343 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,372 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,372 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,372 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,372 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,372 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,372 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:57,372 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:16:59,517 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:17:24,098 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:21:22,890 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:21:27,735 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:21:31,439 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:21:40,107 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:21:43,784 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:21:53,199 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:22:01,231 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:22:03,158 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:22:05,098 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:22:07,063 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:22:11,957 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:22:59,514 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:23:01,359 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:32:48,638 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] +2025-11-16 12:33:31,339 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71] diff --git a/simple_test.py b/simple_test.py deleted file mode 100644 index f582a3f..0000000 --- a/simple_test.py +++ /dev/null @@ -1,40 +0,0 @@ -import requests -import json - -# 创建会话以保持登录状态 -session = requests.Session() - -def test_apis(): - """直接测试API接口""" - try: - # 1. 测试创建产品(这会生成操作日志) - print("=== 测试创建产品 ===") - product_data = { - 'product_name': '测试产品', - 'description': '这是一个测试产品' - } - - response = session.post( - "http://localhost:5000/api/v1/products", - json=product_data, - headers={'Content-Type': 'application/json'} - ) - print(f"创建产品状态码: {response.status_code}") - print(f"创建产品响应: {response.text}") - - # 2. 测试获取操作日志 - print("\n=== 测试获取操作日志 ===") - response = session.get("http://localhost:5000/api/v1/logs") - print(f"获取日志状态码: {response.status_code}") - if response.status_code == 200: - log_data = response.json() - print(f"日志数据: {json.dumps(log_data, indent=2, ensure_ascii=False)}") - else: - print(f"获取日志失败: {response.text}") - - except Exception as e: - print(f"测试过程中出现错误: {e}") - -if __name__ == "__main__": - print("开始简单测试...") - test_apis() \ No newline at end of file diff --git a/test_log.py b/test_log.py deleted file mode 100644 index 13accd7..0000000 --- a/test_log.py +++ /dev/null @@ -1,21 +0,0 @@ -import requests -import json - -# 测试创建产品API -def test_create_product(): - url = "http://localhost:5000/api/v1/products" - headers = {"Content-Type": "application/json"} - data = { - "product_name": "测试产品", - "description": "这是一个测试产品" - } - - try: - response = requests.post(url, headers=headers, data=json.dumps(data)) - print(f"Status Code: {response.status_code}") - print(f"Response: {response.text}") - except Exception as e: - print(f"Error: {e}") - -if __name__ == "__main__": - test_create_product() \ No newline at end of file diff --git a/test_log_with_auth.py b/test_log_with_auth.py deleted file mode 100644 index f8ba1d1..0000000 --- a/test_log_with_auth.py +++ /dev/null @@ -1,67 +0,0 @@ -import requests -import json - -# 创建会话以保持登录状态 -session = requests.Session() - -def login(): - """登录系统""" - url = "http://localhost:5000/api/v1/auth/login" - headers = {"Content-Type": "application/json"} - data = { - "username": "admin", - "password": "admin123" - } - - try: - response = session.post(url, headers=headers, data=json.dumps(data)) - print(f"Login Status Code: {response.status_code}") - print(f"Login Response: {response.text}") - return response.status_code == 200 - except Exception as e: - print(f"Login Error: {e}") - return False - -def test_create_product(): - """测试创建产品API""" - url = "http://localhost:5000/api/v1/products" - headers = {"Content-Type": "application/json"} - data = { - "product_name": "测试产品", - "description": "这是一个测试产品" - } - - try: - response = session.post(url, headers=headers, data=json.dumps(data)) - print(f"Create Product Status Code: {response.status_code}") - print(f"Create Product Response: {response.text}") - return response.status_code == 200 - except Exception as e: - print(f"Create Product Error: {e}") - return False - -def test_get_logs(): - """测试获取日志API""" - url = "http://localhost:5000/api/v1/logs" - try: - response = session.get(url) - print(f"Get Logs Status Code: {response.status_code}") - print(f"Get Logs Response: {response.text}") - return response.status_code == 200 - except Exception as e: - print(f"Get Logs Error: {e}") - return False - -if __name__ == "__main__": - # 登录 - if login(): - print("登录成功") - # 测试创建产品 - if test_create_product(): - print("创建产品成功") - # 测试获取日志 - test_get_logs() - else: - print("创建产品失败") - else: - print("登录失败") \ No newline at end of file diff --git a/test_login.py b/test_login.py deleted file mode 100644 index 689b411..0000000 --- a/test_login.py +++ /dev/null @@ -1,29 +0,0 @@ -import requests - -# 测试登录页面访问 -response = requests.get('http://127.0.0.1:5000/login') -print(f"Status Code: {response.status_code}") -print(f"Content Length: {len(response.content)}") -print(f"Content Type: {response.headers.get('content-type')}") - -# 检查页面内容 -content = response.text -if '登录' in content: - print("页面包含登录相关文本") -else: - print("页面不包含登录相关文本") - -# 检查是否有错误信息 -if '错误' in content or 'Error' in content: - print("页面包含错误信息") -else: - print("页面不包含明显错误信息") - -# 尝试登录 -login_data = { - 'username': 'admin', - 'password': 'admin123', - 'csrf_token': '' # 我们需要从页面中提取CSRF令牌 -} - -print("尝试登录测试...") \ No newline at end of file diff --git a/test_web_log.py b/test_web_log.py deleted file mode 100644 index 89429b9..0000000 --- a/test_web_log.py +++ /dev/null @@ -1,137 +0,0 @@ -import requests -from bs4 import BeautifulSoup - -# 创建会话以保持登录状态 -session = requests.Session() - -def get_csrf_token(): - """从登录页面获取CSRF令牌""" - try: - response = session.get("http://localhost:5000/login") - soup = BeautifulSoup(response.text, 'html.parser') - csrf_input = soup.find('input', {'name': 'csrf_token'}) - if csrf_input: - # 直接获取value属性 - try: - # 忽略类型检查错误 - csrf_token = csrf_input.get('value') # type: ignore - if not csrf_token: - csrf_token = csrf_input['value'] # type: ignore - if csrf_token: - return csrf_token - except: - pass - print("未找到CSRF令牌输入字段") - return None - except Exception as e: - print(f"获取CSRF令牌失败: {e}") - return None - -def login(): - """登录系统""" - try: - # 获取CSRF令牌 - csrf_token = get_csrf_token() - if not csrf_token: - return False - - # 准备登录数据 - login_data = { - 'username': 'admin', - 'password': 'admin123', - 'csrf_token': csrf_token - } - - # 发送登录请求 - response = session.post("http://localhost:5000/login", data=login_data) - - # 检查是否登录成功(通过重定向到dashboard来判断) - if response.url and 'dashboard' in response.url: - print("登录成功") - return True - else: - print(f"登录失败,状态码: {response.status_code}") - print(f"响应URL: {response.url}") - return False - - except Exception as e: - print(f"登录过程中出现错误: {e}") - return False - -def test_create_product(): - """测试创建产品""" - try: - # 准备产品数据 - product_data = { - 'product_name': '测试产品', - 'description': '这是一个测试产品' - } - - # 发送创建产品请求 - response = session.post( - "http://localhost:5000/api/v1/products", - json=product_data, - headers={'Content-Type': 'application/json'} - ) - - print(f"创建产品状态码: {response.status_code}") - print(f"创建产品响应: {response.text}") - return response.status_code == 200 - - except Exception as e: - print(f"创建产品时出现错误: {e}") - return False - -def test_get_logs(): - """测试获取操作日志""" - try: - # 发送获取日志请求 - response = session.get("http://localhost:5000/api/v1/logs") - - print(f"获取日志状态码: {response.status_code}") - print(f"获取日志响应: {response.text}") - return response.status_code == 200 - - except Exception as e: - print(f"获取日志时出现错误: {e}") - return False - -def test_view_logs_page(): - """测试访问日志页面""" - try: - # 访问日志管理页面 - response = session.get("http://localhost:5000/logs") - - print(f"访问日志页面状态码: {response.status_code}") - if response.status_code == 200: - print("成功访问日志管理页面") - return True - else: - print(f"访问日志页面失败: {response.text}") - return False - - except Exception as e: - print(f"访问日志页面时出现错误: {e}") - return False - -if __name__ == "__main__": - print("开始测试日志功能...") - - # 登录 - if login(): - print("=== 登录成功 ===") - - # 测试创建产品(这会生成操作日志) - print("\n=== 测试创建产品 ===") - test_create_product() - - # 测试获取操作日志 - print("\n=== 测试获取操作日志 ===") - test_get_logs() - - # 测试访问日志页面 - print("\n=== 测试访问日志页面 ===") - test_view_logs_page() - - else: - print("登录失败,无法继续测试") \ No newline at end of file diff --git a/verify_log.py b/verify_log.py deleted file mode 100644 index 498d2c4..0000000 --- a/verify_log.py +++ /dev/null @@ -1,41 +0,0 @@ -import requests -import json - -# 直接测试日志API,绕过认证检查(在实际环境中应该有认证) -def test_log_functionality(): - """测试日志功能""" - try: - # 1. 先手动创建一个产品(绕过认证检查) - print("=== 手动创建产品以生成日志 ===") - - # 我们直接查看数据库中是否已有产品 - print("检查现有产品...") - - # 2. 测试获取操作日志(绕过认证检查) - print("\n=== 测试获取操作日志 ===") - - # 由于我们无法绕过Flask-Login的认证检查,我们直接查看日志文件 - print("查看日志文件内容...") - - try: - with open('logs/kamaxitong.log', 'r', encoding='utf-8') as f: - lines = f.readlines() - print(f"日志文件共有 {len(lines)} 行") - # 显示最后几行 - for line in lines[-10:]: - print(line.strip()) - except FileNotFoundError: - print("日志文件不存在") - except Exception as e: - print(f"读取日志文件失败: {e}") - - # 3. 测试审计日志表 - print("\n=== 测试审计日志表 ===") - # 我们需要直接连接数据库来查看审计日志 - - except Exception as e: - print(f"测试过程中出现错误: {e}") - -if __name__ == "__main__": - print("验证日志功能...") - test_log_functionality() \ No newline at end of file