修复系统与验证器
This commit is contained in:
parent
66bf8190a6
commit
73800eeaa7
2
.env
2
.env
@ -32,7 +32,7 @@ HOST=0.0.0.0
|
||||
PORT=5000
|
||||
|
||||
# 文件上传配置
|
||||
MAX_CONTENT_LENGTH=16777216
|
||||
MAX_CONTENT_LENGTH=524288000
|
||||
UPLOAD_FOLDER=static/uploads
|
||||
|
||||
# 日志配置
|
||||
|
||||
@ -32,7 +32,7 @@ HOST=0.0.0.0
|
||||
PORT=5000
|
||||
|
||||
# 文件上传配置
|
||||
MAX_CONTENT_LENGTH=16777216
|
||||
MAX_CONTENT_LENGTH=524288000
|
||||
UPLOAD_FOLDER=static/uploads
|
||||
|
||||
# 日志配置
|
||||
|
||||
604
api_test.html
604
api_test.html
@ -1,604 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>KaMiXiTong API测试平台</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
h1, h2, h3 {
|
||||
color: #333;
|
||||
}
|
||||
.api-section {
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
input, select, textarea {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
.response {
|
||||
margin-top: 15px;
|
||||
padding: 15px;
|
||||
background-color: #e9ecef;
|
||||
border-radius: 4px;
|
||||
white-space: pre-wrap;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
.success {
|
||||
background-color: #d4edda;
|
||||
border-color: #c3e6cb;
|
||||
color: #155724;
|
||||
}
|
||||
.error {
|
||||
background-color: #f8d7da;
|
||||
border-color: #f5c6cb;
|
||||
color: #721c24;
|
||||
}
|
||||
.tabs {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.tab {
|
||||
padding: 10px 20px;
|
||||
background-color: #e9ecef;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ddd;
|
||||
border-bottom: none;
|
||||
border-radius: 5px 5px 0 0;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.tab.active {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
.tab-content {
|
||||
display: none;
|
||||
}
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>KaMiXiTong API测试平台</h1>
|
||||
<p>这是一个用于测试KaMiXiTong系统所有API接口的前端页面。</p>
|
||||
|
||||
<div class="tabs">
|
||||
<div class="tab active" onclick="openTab('admin')">用户管理</div>
|
||||
<div class="tab" onclick="openTab('ticket')">工单管理</div>
|
||||
<div class="tab" onclick="openTab('license')">卡密管理</div>
|
||||
<div class="tab" onclick="openTab('version')">版本管理</div>
|
||||
<div class="tab" onclick="openTab('device')">设备管理</div>
|
||||
<div class="tab" onclick="openTab('product')">产品管理</div>
|
||||
</div>
|
||||
|
||||
<!-- 用户管理 -->
|
||||
<div id="admin" class="tab-content active">
|
||||
<div class="api-section">
|
||||
<h2>创建管理员</h2>
|
||||
<form id="createAdminForm">
|
||||
<div class="form-group">
|
||||
<label for="username">用户名:</label>
|
||||
<input type="text" id="username" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="adminEmail">邮箱:</label>
|
||||
<input type="email" id="adminEmail">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">密码:</label>
|
||||
<input type="password" id="password" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="role">角色:</label>
|
||||
<select id="role">
|
||||
<option value="0">普通管理员</option>
|
||||
<option value="1">超级管理员</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="adminStatus">状态:</label>
|
||||
<select id="adminStatus">
|
||||
<option value="1">正常</option>
|
||||
<option value="0">禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit">创建管理员</button>
|
||||
</form>
|
||||
<div id="createAdminResponse" class="response"></div>
|
||||
</div>
|
||||
|
||||
<div class="api-section">
|
||||
<h2>获取管理员列表</h2>
|
||||
<button onclick="getAdmins()">获取管理员列表</button>
|
||||
<div id="getAdminsResponse" class="response"></div>
|
||||
</div>
|
||||
|
||||
<div class="api-section">
|
||||
<h2>更新管理员</h2>
|
||||
<form id="updateAdminForm">
|
||||
<div class="form-group">
|
||||
<label for="updateAdminId">管理员ID:</label>
|
||||
<input type="number" id="updateAdminId" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="updateUsername">用户名:</label>
|
||||
<input type="text" id="updateUsername">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="updateAdminEmail">邮箱:</label>
|
||||
<input type="email" id="updateAdminEmail">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="updatePassword">密码 (留空则不更新):</label>
|
||||
<input type="password" id="updatePassword">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="updateRole">角色:</label>
|
||||
<select id="updateRole">
|
||||
<option value="">不更新</option>
|
||||
<option value="0">普通管理员</option>
|
||||
<option value="1">超级管理员</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="updateAdminStatus">状态:</label>
|
||||
<select id="updateAdminStatus">
|
||||
<option value="">不更新</option>
|
||||
<option value="1">正常</option>
|
||||
<option value="0">禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit">更新管理员</button>
|
||||
</form>
|
||||
<div id="updateAdminResponse" class="response"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 工单管理 -->
|
||||
<div id="ticket" class="tab-content">
|
||||
<div class="api-section">
|
||||
<h2>创建工单</h2>
|
||||
<form id="createTicketForm">
|
||||
<div class="form-group">
|
||||
<label for="ticketTitle">标题:</label>
|
||||
<input type="text" id="ticketTitle" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="productId">产品ID:</label>
|
||||
<input type="text" id="productId" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="ticketDescription">描述:</label>
|
||||
<textarea id="ticketDescription" required></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="priority">优先级:</label>
|
||||
<select id="priority">
|
||||
<option value="1">低</option>
|
||||
<option value="2">中</option>
|
||||
<option value="3">高</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit">创建工单</button>
|
||||
</form>
|
||||
<div id="createTicketResponse" class="response"></div>
|
||||
</div>
|
||||
|
||||
<div class="api-section">
|
||||
<h2>获取工单列表</h2>
|
||||
<button onclick="getTickets()">获取工单列表</button>
|
||||
<div id="getTicketsResponse" class="response"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡密管理 -->
|
||||
<div id="license" class="tab-content">
|
||||
<div class="api-section">
|
||||
<h2>生成卡密</h2>
|
||||
<form id="generateLicenseForm">
|
||||
<div class="form-group">
|
||||
<label for="licenseProductId">产品ID:</label>
|
||||
<input type="text" id="licenseProductId" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="count">生成数量:</label>
|
||||
<input type="number" id="count" value="1" min="1" max="10000" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="licenseType">卡密类型:</label>
|
||||
<select id="licenseType">
|
||||
<option value="0">试用</option>
|
||||
<option value="1" selected>正式</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="validDays">有效期(天):</label>
|
||||
<input type="number" id="validDays" value="365" min="1" required>
|
||||
</div>
|
||||
<button type="submit">生成卡密</button>
|
||||
</form>
|
||||
<div id="generateLicenseResponse" class="response"></div>
|
||||
</div>
|
||||
|
||||
<div class="api-section">
|
||||
<h2>获取卡密列表</h2>
|
||||
<button onclick="getLicenses()">获取卡密列表</button>
|
||||
<div id="getLicensesResponse" class="response"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 版本管理 -->
|
||||
<div id="version" class="tab-content">
|
||||
<div class="api-section">
|
||||
<h2>创建版本</h2>
|
||||
<form id="createVersionForm">
|
||||
<div class="form-group">
|
||||
<label for="versionProductId">产品ID:</label>
|
||||
<input type="text" id="versionProductId" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="versionNum">版本号:</label>
|
||||
<input type="text" id="versionNum" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="platform">平台:</label>
|
||||
<input type="text" id="platform">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description">描述:</label>
|
||||
<textarea id="description"></textarea>
|
||||
</div>
|
||||
<button type="submit">创建版本</button>
|
||||
</form>
|
||||
<div id="createVersionResponse" class="response"></div>
|
||||
</div>
|
||||
|
||||
<div class="api-section">
|
||||
<h2>获取版本列表</h2>
|
||||
<button onclick="getVersions()">获取版本列表</button>
|
||||
<div id="getVersionsResponse" class="response"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 设备管理 -->
|
||||
<div id="device" class="tab-content">
|
||||
<div class="api-section">
|
||||
<h2>获取设备列表</h2>
|
||||
<button onclick="getDevices()">获取设备列表</button>
|
||||
<div id="getDevicesResponse" class="response"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 产品管理 -->
|
||||
<div id="product" class="tab-content">
|
||||
<div class="api-section">
|
||||
<h2>创建产品</h2>
|
||||
<form id="createProductForm">
|
||||
<div class="form-group">
|
||||
<label for="productName">产品名称:</label>
|
||||
<input type="text" id="productName" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="productDescription">描述:</label>
|
||||
<textarea id="productDescription"></textarea>
|
||||
</div>
|
||||
<button type="submit">创建产品</button>
|
||||
</form>
|
||||
<div id="createProductResponse" class="response"></div>
|
||||
</div>
|
||||
|
||||
<div class="api-section">
|
||||
<h2>获取产品列表</h2>
|
||||
<button onclick="getProducts()">获取产品列表</button>
|
||||
<div id="getProductsResponse" class="response"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 基础URL (MySQL版本)
|
||||
const BASE_URL = 'http://127.0.0.1:9004';
|
||||
|
||||
// 切换标签页
|
||||
function openTab(tabName) {
|
||||
// 隐藏所有标签页内容
|
||||
const tabContents = document.getElementsByClassName('tab-content');
|
||||
for (let i = 0; i < tabContents.length; i++) {
|
||||
tabContents[i].classList.remove('active');
|
||||
}
|
||||
|
||||
// 移除所有标签的活动状态
|
||||
const tabs = document.getElementsByClassName('tab');
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
tabs[i].classList.remove('active');
|
||||
}
|
||||
|
||||
// 显示当前标签页并设置活动状态
|
||||
document.getElementById(tabName).classList.add('active');
|
||||
event.currentTarget.classList.add('active');
|
||||
}
|
||||
|
||||
// 显示响应结果
|
||||
function showResponse(elementId, data, isSuccess = true) {
|
||||
const element = document.getElementById(elementId);
|
||||
element.textContent = JSON.stringify(data, null, 2);
|
||||
element.className = 'response ' + (isSuccess ? 'success' : 'error');
|
||||
}
|
||||
|
||||
// 用户管理API
|
||||
document.getElementById('createAdminForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const adminData = {
|
||||
username: document.getElementById('username').value,
|
||||
email: document.getElementById('adminEmail').value,
|
||||
password: document.getElementById('password').value,
|
||||
role: parseInt(document.getElementById('role').value),
|
||||
status: parseInt(document.getElementById('adminStatus').value)
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/admins`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(adminData)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
showResponse('createAdminResponse', result, response.ok);
|
||||
} catch (error) {
|
||||
showResponse('createAdminResponse', {error: error.message}, false);
|
||||
}
|
||||
});
|
||||
|
||||
async function getAdmins() {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/admins`);
|
||||
const result = await response.json();
|
||||
showResponse('getAdminsResponse', result, response.ok);
|
||||
} catch (error) {
|
||||
showResponse('getAdminsResponse', {error: error.message}, false);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('updateAdminForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const adminId = document.getElementById('updateAdminId').value;
|
||||
const updateData = {};
|
||||
|
||||
const username = document.getElementById('updateUsername').value;
|
||||
if (username) updateData.username = username;
|
||||
|
||||
const email = document.getElementById('updateAdminEmail').value;
|
||||
if (email) updateData.email = email;
|
||||
|
||||
const password = document.getElementById('updatePassword').value;
|
||||
if (password) updateData.password = password;
|
||||
|
||||
const role = document.getElementById('updateRole').value;
|
||||
if (role !== '') updateData.role = parseInt(role);
|
||||
|
||||
const status = document.getElementById('updateAdminStatus').value;
|
||||
if (status !== '') updateData.status = parseInt(status);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/admins/${adminId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(updateData)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
showResponse('updateAdminResponse', result, response.ok);
|
||||
} catch (error) {
|
||||
showResponse('updateAdminResponse', {error: error.message}, false);
|
||||
}
|
||||
});
|
||||
|
||||
// 工单管理API
|
||||
document.getElementById('createTicketForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const ticketData = {
|
||||
title: document.getElementById('ticketTitle').value,
|
||||
product_id: document.getElementById('productId').value,
|
||||
description: document.getElementById('ticketDescription').value,
|
||||
priority: parseInt(document.getElementById('priority').value)
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/tickets`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(ticketData)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
showResponse('createTicketResponse', result, response.ok);
|
||||
} catch (error) {
|
||||
showResponse('createTicketResponse', {error: error.message}, false);
|
||||
}
|
||||
});
|
||||
|
||||
async function getTickets() {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/tickets`);
|
||||
const result = await response.json();
|
||||
showResponse('getTicketsResponse', result, response.ok);
|
||||
} catch (error) {
|
||||
showResponse('getTicketsResponse', {error: error.message}, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 卡密管理API
|
||||
document.getElementById('generateLicenseForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const licenseData = {
|
||||
product_id: document.getElementById('licenseProductId').value,
|
||||
count: parseInt(document.getElementById('count').value),
|
||||
type: parseInt(document.getElementById('licenseType').value),
|
||||
valid_days: parseInt(document.getElementById('validDays').value)
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/licenses`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(licenseData)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
showResponse('generateLicenseResponse', result, response.ok);
|
||||
} catch (error) {
|
||||
showResponse('generateLicenseResponse', {error: error.message}, false);
|
||||
}
|
||||
});
|
||||
|
||||
async function getLicenses() {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/licenses`);
|
||||
const result = await response.json();
|
||||
showResponse('getLicensesResponse', result, response.ok);
|
||||
} catch (error) {
|
||||
showResponse('getLicensesResponse', {error: error.message}, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 版本管理API
|
||||
document.getElementById('createVersionForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const versionData = {
|
||||
product_id: document.getElementById('versionProductId').value,
|
||||
version_num: document.getElementById('versionNum').value,
|
||||
platform: document.getElementById('platform').value,
|
||||
description: document.getElementById('description').value
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/versions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(versionData)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
showResponse('createVersionResponse', result, response.ok);
|
||||
} catch (error) {
|
||||
showResponse('createVersionResponse', {error: error.message}, false);
|
||||
}
|
||||
});
|
||||
|
||||
async function getVersions() {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/versions`);
|
||||
const result = await response.json();
|
||||
showResponse('getVersionsResponse', result, response.ok);
|
||||
} catch (error) {
|
||||
showResponse('getVersionsResponse', {error: error.message}, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 设备管理API
|
||||
async function getDevices() {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/devices`);
|
||||
const result = await response.json();
|
||||
showResponse('getDevicesResponse', result, response.ok);
|
||||
} catch (error) {
|
||||
showResponse('getDevicesResponse', {error: error.message}, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 产品管理API
|
||||
document.getElementById('createProductForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const productData = {
|
||||
product_name: document.getElementById('productName').value,
|
||||
description: document.getElementById('productDescription').value
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/products`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(productData)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
showResponse('createProductResponse', result, response.ok);
|
||||
} catch (error) {
|
||||
showResponse('createProductResponse', {error: error.message}, false);
|
||||
}
|
||||
});
|
||||
|
||||
async function getProducts() {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/products`);
|
||||
const result = await response.json();
|
||||
showResponse('getProductsResponse', result, response.ok);
|
||||
} catch (error) {
|
||||
showResponse('getProductsResponse', {error: error.message}, false);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
821
api_test_app.py
821
api_test_app.py
@ -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")
|
||||
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
)
|
||||
|
||||
@ -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]]:
|
||||
"""
|
||||
获取当前授权信息
|
||||
|
||||
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
|
||||
Returns:
|
||||
Optional[Dict[str, Any]]: 授权信息
|
||||
"""
|
||||
return self.cache.get_auth_info(self.software_id)
|
||||
|
||||
def clear_cache(self):
|
||||
"""清除本地缓存"""
|
||||
def clear_auth_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()
|
||||
40
check_db.py
40
check_db.py
@ -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("数据库中没有产品数据")
|
||||
@ -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()
|
||||
@ -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}')
|
||||
BIN
login_page.html
BIN
login_page.html
Binary file not shown.
@ -1,80 +1,51 @@
|
||||
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')
|
||||
2025-11-16 13:06:04,834 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71]
|
||||
2025-11-16 13:06:06,705 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:71]
|
||||
2025-11-16 13:06:36,685 INFO: 开始创建版本 [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:98]
|
||||
2025-11-16 13:06:36,685 INFO: 请求的Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryBaWqjoVX0KdrVN1Z [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:99]
|
||||
2025-11-16 13:06:36,685 INFO: 请求方法: POST [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:100]
|
||||
2025-11-16 13:06:36,686 INFO: 请求URL: http://127.0.0.1:5000/api/v1/versions [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:101]
|
||||
2025-11-16 13:06:36,690 INFO: 请求头: {'Host': '127.0.0.1:5000', 'Connection': 'keep-alive', 'Content-Length': '769', 'Sec-Ch-Ua-Platform': '"Windows"', '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', 'Sec-Ch-Ua': '"Chromium";v="142", "Microsoft Edge";v="142", "Not_A Brand";v="99"', 'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryBaWqjoVX0KdrVN1Z', 'Sec-Ch-Ua-Mobile': '?0', 'Accept': '*/*', 'Origin': 'http://127.0.0.1:5000', 'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Dest': 'empty', 'Referer': 'http://127.0.0.1:5000/versions/create', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6', 'Cookie': 'remember_token=1|ae366cbb6a7c117a69a287f322b91e6d75e14efd338ed4249fe139a40620856246a057db8c7c1df9883347210e17f379727e0d28d269d0002fc5126ec50d83d8; session=eyJfZnJlc2giOmZhbHNlLCJfdXNlcl9pZCI6IjEifQ.aRlGfA.q1DLjLrFgNZLvNT_ChqsVKj_Uyc'} [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:102]
|
||||
2025-11-16 13:06:36,690 INFO: 处理表单数据请求 [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:106]
|
||||
2025-11-16 13:06:36,691 INFO: 收到的参数: product_id=ArticleReplace, version_num=1.0 [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:141]
|
||||
2025-11-16 13:06:36,691 INFO: 验证产品是否存在: product_id=ArticleReplace [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:147]
|
||||
2025-11-16 13:06:36,691 INFO: 执行产品查询... [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:149]
|
||||
2025-11-16 13:06:36,693 INFO: 数据库中所有产品: [('ArticleReplace', '改写软件')] [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:153]
|
||||
2025-11-16 13:06:36,695 INFO: 产品查询结果: <Product 改写软件> [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]
|
||||
|
||||
@ -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]
|
||||
|
||||
71
logs/kamaxitong.log.2
Normal file
71
logs/kamaxitong.log.2
Normal file
@ -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]
|
||||
@ -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()
|
||||
21
test_log.py
21
test_log.py
@ -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()
|
||||
@ -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("登录失败")
|
||||
@ -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("尝试登录测试...")
|
||||
137
test_web_log.py
137
test_web_log.py
@ -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("登录失败,无法继续测试")
|
||||
@ -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()
|
||||
Loading…
Reference in New Issue
Block a user