Files
张翔 be5d5ede90 feat: 添加异常日志功能并优化UI样式
refactor: 重构后端查询逻辑和API响应处理

fix: 修复用户角色更新和文件上传问题

test: 添加前端性能测试脚本和E2E测试用例

chore: 更新依赖版本和配置文件

docs: 添加环境检查脚本和测试文档

style: 统一表格标签样式和路由命名

perf: 优化前端页面加载速度和响应时间
2026-03-24 13:32:20 +08:00

338 lines
12 KiB
JavaScript

#!/usr/bin/env node
const http = require('http');
const https = require('https');
const fs = require('fs');
const path = require('path');
const API_BASE_URL = process.env.API_BASE_URL || 'http://localhost:8080';
const RESULTS_FILE = path.join(__dirname, '../performance-test-results.json');
class PerformanceTester {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.results = [];
}
async testEndpoint(endpoint, method = 'GET', body = null) {
const url = `${this.baseUrl}${endpoint}`;
const startTime = Date.now();
return new Promise((resolve, reject) => {
const options = {
method: method,
headers: {
'Content-Type': 'application/json',
}
};
if (body) {
options.headers['Content-Length'] = Buffer.byteLength(JSON.stringify(body));
}
const protocol = url.startsWith('https') ? https : http;
const req = protocol.request(url, options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
const endTime = Date.now();
const duration = endTime - startTime;
resolve({
endpoint,
method,
statusCode: res.statusCode,
duration,
success: res.statusCode >= 200 && res.statusCode < 300,
dataSize: data.length
});
});
});
req.on('error', (error) => {
const endTime = Date.now();
const duration = endTime - startTime;
resolve({
endpoint,
method,
statusCode: 0,
duration,
success: false,
error: error.message
});
});
if (body) {
req.write(JSON.stringify(body));
}
req.end();
});
}
async runLoadTest(endpoint, concurrentRequests = 10, totalRequests = 100) {
console.log(`\n📊 开始负载测试: ${endpoint}`);
console.log(` 并发数: ${concurrentRequests}`);
console.log(` 总请求数: ${totalRequests}\n`);
const results = [];
const startTime = Date.now();
for (let i = 0; i < totalRequests; i += concurrentRequests) {
const batch = Math.min(concurrentRequests, totalRequests - i);
const promises = [];
for (let j = 0; j < batch; j++) {
promises.push(this.testEndpoint(endpoint));
}
const batchResults = await Promise.all(promises);
results.push(...batchResults);
console.log(` 进度: ${Math.min(i + batch, totalRequests)}/${totalRequests} 请求已完成`);
}
const endTime = Date.now();
const totalDuration = endTime - startTime;
const successfulRequests = results.filter(r => r.success);
const failedRequests = results.filter(r => !r.success);
const durations = successfulRequests.map(r => r.duration);
const avgDuration = durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0;
const minDuration = durations.length > 0 ? Math.min(...durations) : 0;
const maxDuration = durations.length > 0 ? Math.max(...durations) : 0;
const p95Duration = this.calculatePercentile(durations, 95);
const p99Duration = this.calculatePercentile(durations, 99);
const throughput = (successfulRequests.length / totalDuration) * 1000;
return {
endpoint,
concurrentRequests,
totalRequests,
successfulRequests: successfulRequests.length,
failedRequests: failedRequests.length,
successRate: (successfulRequests.length / totalRequests * 100).toFixed(2),
totalDuration,
avgDuration,
minDuration,
maxDuration,
p95Duration,
p99Duration,
throughput: throughput.toFixed(2),
results
};
}
calculatePercentile(values, percentile) {
if (values.length === 0) return 0;
const sorted = [...values].sort((a, b) => a - b);
const index = Math.ceil((percentile / 100) * sorted.length) - 1;
return sorted[Math.max(0, index)];
}
async runPerformanceTests() {
console.log('🚀 开始性能测试...\n');
const endpoints = [
{ path: '/api/auth/login', method: 'POST', body: { username: 'admin', password: 'admin123' } },
{ path: '/api/users', method: 'GET' },
{ path: '/api/roles', method: 'GET' },
{ path: '/api/menus', method: 'GET' },
{ path: '/api/dicts', method: 'GET' },
];
for (const endpoint of endpoints) {
console.log(`\n📡 测试端点: ${endpoint.method} ${endpoint.path}`);
const results = [];
const iterations = 10;
for (let i = 0; i < iterations; i++) {
const result = await this.testEndpoint(endpoint.path, endpoint.method, endpoint.body);
results.push(result);
console.log(` ${i + 1}/${iterations}: ${result.duration}ms - ${result.success ? '✅' : '❌'}`);
}
const durations = results.filter(r => r.success).map(r => r.duration);
const avgDuration = durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0;
const minDuration = durations.length > 0 ? Math.min(...durations) : 0;
const maxDuration = durations.length > 0 ? Math.max(...durations) : 0;
const successRate = (results.filter(r => r.success).length / results.length * 100).toFixed(2);
this.results.push({
endpoint: endpoint.path,
method: endpoint.method,
avgDuration,
minDuration,
maxDuration,
successRate,
status: this.evaluatePerformance(avgDuration)
});
}
this.saveResults();
this.printSummary();
}
evaluatePerformance(avgDuration) {
if (avgDuration < 100) {
return '🟢 优秀';
} else if (avgDuration < 300) {
return '🟡 良好';
} else if (avgDuration < 500) {
return '🟠 一般';
} else {
return '🔴 需要优化';
}
}
saveResults() {
const timestamp = new Date().toISOString();
const data = {
timestamp,
performanceTests: this.results,
loadTests: this.loadTestResults
};
const history = [];
if (fs.existsSync(RESULTS_FILE)) {
try {
history.push(...JSON.parse(fs.readFileSync(RESULTS_FILE, 'utf8')));
} catch (e) {
console.warn('⚠️ 无法解析历史结果文件');
}
}
history.push(data);
if (history.length > 20) {
history.shift();
}
fs.writeFileSync(RESULTS_FILE, JSON.stringify(history, null, 2));
}
printSummary() {
console.log('\n📊 性能测试摘要:');
console.log('═══════════════════════════════════════');
const table = this.results.map(r => ({
端点: r.endpoint,
方法: r.method,
平均: `${r.avgDuration.toFixed(0)}ms`,
最小: `${r.minDuration}ms`,
最大: `${r.maxDuration}ms`,
成功率: `${r.successRate}%`,
状态: r.status
}));
console.table(table);
if (this.loadTestResults) {
console.log('\n📈 负载测试摘要:');
console.log('═══════════════════════════════════════');
const loadTable = this.loadTestResults.map(r => ({
端点: r.endpoint,
总请求: r.totalRequests,
成功: r.successfulRequests,
失败: r.failedRequests,
成功率: `${r.successRate}%`,
平均响应: `${r.avgDuration.toFixed(0)}ms`,
P95: `${r.p95Duration.toFixed(0)}ms`,
P99: `${r.p99Duration.toFixed(0)}ms`,
吞吐量: `${r.throughput} req/s`
}));
console.table(loadTable);
}
console.log('\n💡 性能优化建议:');
this.printRecommendations();
}
printRecommendations() {
const slowEndpoints = this.results.filter(r => r.avgDuration > 300);
if (slowEndpoints.length > 0) {
console.log(' ⚠️ 以下端点响应时间较长,建议优化:');
slowEndpoints.forEach(r => {
console.log(` - ${r.endpoint}: ${r.avgDuration.toFixed(0)}ms`);
});
}
const lowSuccessRate = this.results.filter(r => parseFloat(r.successRate) < 95);
if (lowSuccessRate.length > 0) {
console.log(' ⚠️ 以下端点成功率较低,建议检查:');
lowSuccessRate.forEach(r => {
console.log(` - ${r.endpoint}: ${r.successRate}%`);
});
}
if (slowEndpoints.length === 0 && lowSuccessRate.length === 0) {
console.log(' ✅ 所有端点性能良好,无需优化');
}
}
async runLoadTests() {
console.log('\n📊 开始负载测试...\n');
const endpoints = ['/api/users', '/api/roles', '/api/menus'];
this.loadTestResults = [];
for (const endpoint of endpoints) {
const result = await this.runLoadTest(endpoint, 10, 100);
this.loadTestResults.push(result);
console.log(`\n📈 ${endpoint} 负载测试结果:`);
console.log(` 成功率: ${result.successRate}%`);
console.log(` 平均响应时间: ${result.avgDuration.toFixed(0)}ms`);
console.log(` P95响应时间: ${result.p95Duration.toFixed(0)}ms`);
console.log(` P99响应时间: ${result.p99Duration.toFixed(0)}ms`);
console.log(` 吞吐量: ${result.throughput} req/s`);
}
this.saveResults();
}
}
async function main() {
const tester = new PerformanceTester(API_BASE_URL);
const command = process.argv[2];
switch (command) {
case 'performance':
await tester.runPerformanceTests();
break;
case 'load':
await tester.runLoadTests();
break;
case 'all':
await tester.runPerformanceTests();
await tester.runLoadTests();
break;
default:
console.log('使用方法:');
console.log(' node scripts/performance-test.js performance - 运行性能测试');
console.log(' node scripts/performance-test.js load - 运行负载测试');
console.log(' node scripts/performance-test.js all - 运行所有测试');
console.log('\n环境变量:');
console.log(' API_BASE_URL - API基础URL (默认: http://localhost:8080)');
}
}
if (require.main === module) {
main();
}
module.exports = PerformanceTester;