furniture-3d-system-Wu/scripts/performance-test.ts

373 lines
9.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

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

/**
* 数据库性能测试脚本
* 用于验证索引优化效果
*/
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
interface TestResult {
name: string;
duration: number;
recordCount: number;
}
async function measureQuery(name: string, queryFn: () => Promise<unknown>): Promise<TestResult> {
const start = Date.now();
const result = await queryFn();
const duration = Date.now() - start;
const recordCount = Array.isArray(result) ? result.length : 1;
console.log(` ✓ ${name}: ${duration}ms (${recordCount} 条记录)`);
return { name, duration, recordCount };
}
async function main() {
console.log('🚀 开始性能测试...\n');
const results: TestResult[] = [];
// ============================================
// 产品查询测试
// ============================================
console.log('📦 产品查询测试:');
results.push(
await measureQuery('产品列表(无过滤)', async () => {
return await prisma.product.findMany({
take: 20,
});
})
);
results.push(
await measureQuery('产品列表(软删除过滤)', async () => {
return await prisma.product.findMany({
where: { deletedAt: null },
take: 20,
});
})
);
results.push(
await measureQuery('产品列表(部门+可见性)', async () => {
const dept = await prisma.department.findFirst();
return await prisma.product.findMany({
where: {
departmentId: dept?.id,
visibility: 'TEAM',
deletedAt: null,
},
take: 20,
});
})
);
results.push(
await measureQuery('产品列表(按创建时间排序)', async () => {
return await prisma.product.findMany({
where: { deletedAt: null },
orderBy: { createdAt: 'desc' },
take: 20,
});
})
);
results.push(
await measureQuery('产品统计(活跃产品数)', async () => {
return await prisma.product.count({
where: { deletedAt: null },
});
})
);
// ============================================
// SKU 查询测试
// ============================================
console.log('\n📦 SKU 查询测试:');
results.push(
await measureQuery('SKU 列表(按产品)', async () => {
const product = await prisma.product.findFirst();
return await prisma.productSKU.findMany({
where: { productId: product?.id },
});
})
);
results.push(
await measureQuery('SKU 列表(产品+状态)', async () => {
const product = await prisma.product.findFirst();
return await prisma.productSKU.findMany({
where: {
productId: product?.id,
isActive: true,
deletedAt: null,
},
});
})
);
results.push(
await measureQuery('SKU 统计(活跃 SKU 数)', async () => {
return await prisma.productSKU.count({
where: {
isActive: true,
deletedAt: null,
},
});
})
);
// ============================================
// 用户查询测试
// ============================================
console.log('\n👤 用户查询测试:');
results.push(
await measureQuery('用户列表(按部门)', async () => {
const dept = await prisma.department.findFirst();
return await prisma.user.findMany({
where: { departmentId: dept?.id },
});
})
);
results.push(
await measureQuery('用户列表(部门+状态)', async () => {
const dept = await prisma.department.findFirst();
return await prisma.user.findMany({
where: {
departmentId: dept?.id,
status: 'ACTIVE',
},
});
})
);
results.push(
await measureQuery('用户列表(按角色)', async () => {
return await prisma.user.findMany({
where: { role: 'DESIGNER' },
});
})
);
results.push(
await measureQuery('用户统计(活跃用户数)', async () => {
return await prisma.user.count({
where: { status: 'ACTIVE' },
});
})
);
// ============================================
// 反馈查询测试
// ============================================
console.log('\n💬 反馈查询测试:');
results.push(
await measureQuery('反馈列表(按产品)', async () => {
const product = await prisma.product.findFirst();
return await prisma.feedback.findMany({
where: { productId: product?.id },
});
})
);
results.push(
await measureQuery('反馈列表(产品+状态)', async () => {
const product = await prisma.product.findFirst();
return await prisma.feedback.findMany({
where: {
productId: product?.id,
status: 'PENDING',
},
});
})
);
results.push(
await measureQuery('反馈列表(优先级+状态)', async () => {
return await prisma.feedback.findMany({
where: {
priority: 'HIGH',
status: 'PENDING',
},
});
})
);
results.push(
await measureQuery('反馈统计(待处理数)', async () => {
return await prisma.feedback.count({
where: { status: 'PENDING' },
});
})
);
results.push(
await measureQuery('反馈统计(按状态分组)', async () => {
return await prisma.feedback.groupBy({
by: ['status'],
_count: true,
});
})
);
// ============================================
// 操作日志查询测试
// ============================================
console.log('\n📝 操作日志查询测试:');
results.push(
await measureQuery('日志列表(按用户)', async () => {
const user = await prisma.user.findFirst();
return await prisma.activityLog.findMany({
where: { userId: user?.id },
take: 20,
});
})
);
results.push(
await measureQuery('日志列表(用户+时间排序)', async () => {
const user = await prisma.user.findFirst();
return await prisma.activityLog.findMany({
where: { userId: user?.id },
orderBy: { createdAt: 'desc' },
take: 20,
});
})
);
results.push(
await measureQuery('日志列表(按类型)', async () => {
return await prisma.activityLog.findMany({
where: { type: 'PRODUCT_CREATE' },
take: 20,
});
})
);
results.push(
await measureQuery('日志列表(类型+时间排序)', async () => {
return await prisma.activityLog.findMany({
where: { type: 'PRODUCT_CREATE' },
orderBy: { createdAt: 'desc' },
take: 20,
});
})
);
results.push(
await measureQuery('日志统计(按类型分组)', async () => {
return await prisma.activityLog.groupBy({
by: ['type'],
_count: true,
});
})
);
// ============================================
// 复杂查询测试
// ============================================
console.log('\n🔍 复杂查询测试:');
results.push(
await measureQuery('产品详情(含 SKU)', async () => {
const product = await prisma.product.findFirst();
return await prisma.product.findUnique({
where: { id: product?.id },
include: {
skus: true,
assemblySteps: true,
panels: true,
},
});
})
);
results.push(
await measureQuery('用户详情(含部门和小组)', async () => {
const user = await prisma.user.findFirst();
return await prisma.user.findUnique({
where: { id: user?.id },
include: {
department: true,
team: true,
},
});
})
);
results.push(
await measureQuery('部门树(含子部门)', async () => {
return await prisma.department.findMany({
where: { parentId: null },
include: {
children: {
include: {
children: true,
},
},
},
});
})
);
// ============================================
// 性能总结
// ============================================
console.log('\n📊 性能总结:');
console.log('─'.repeat(60));
const avgDuration = results.reduce((sum, r) => sum + r.duration, 0) / results.length;
const maxDuration = Math.max(...results.map((r) => r.duration));
const minDuration = Math.min(...results.map((r) => r.duration));
console.log(` 平均响应时间: ${avgDuration.toFixed(2)}ms`);
console.log(` 最快查询: ${minDuration}ms`);
console.log(` 最慢查询: ${maxDuration}ms`);
const fastQueries = results.filter((r) => r.duration < 50).length;
const mediumQueries = results.filter((r) => r.duration >= 50 && r.duration < 100).length;
const slowQueries = results.filter((r) => r.duration >= 100).length;
console.log(`\n 性能分布:`);
console.log(` < 50ms: ${fastQueries} 个查询 (${((fastQueries / results.length) * 100).toFixed(1)}%)`);
console.log(` 50-100ms: ${mediumQueries} 个查询 (${((mediumQueries / results.length) * 100).toFixed(1)}%)`);
console.log(` > 100ms: ${slowQueries} 个查询 (${((slowQueries / results.length) * 100).toFixed(1)}%)`);
console.log('\n─'.repeat(60));
if (avgDuration < 100) {
console.log('✅ 性能优秀!平均响应时间 < 100ms');
} else if (avgDuration < 200) {
console.log('⚠️ 性能良好,但有优化空间');
} else {
console.log('❌ 性能需要优化');
}
// 找出最慢的查询
const slowestQueries = results.sort((a, b) => b.duration - a.duration).slice(0, 5);
console.log('\n🐌 最慢的 5 个查询:');
slowestQueries.forEach((r, i) => {
console.log(` ${i + 1}. ${r.name}: ${r.duration}ms`);
});
console.log('\n✅ 性能测试完成!');
}
main()
.catch((e) => {
console.error('❌ 性能测试失败:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});