import { PrismaClient } from '@prisma/client'; const databaseUrl = process.env.DATABASE_URL; if (!databaseUrl) { throw new Error('必须设置 DATABASE_URL'); } const prisma = new PrismaClient({ datasources: { db: { url: databaseUrl, }, }, }); async function main() { console.log('=== 正式系统数据统计 ===\n'); // 用户统计 const totalUsers = await prisma.user.count({ where: { archivedAt: null, }, }); const activeUsers = await prisma.user.count({ where: { archivedAt: null, updatedAt: { gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), }, }, }); // 素材统计 const totalMaterials = await prisma.material.count({ where: { deletedAt: null, }, }); // 素材类型分布 const materialTypeStats = await prisma.material.groupBy({ by: ['materialType'], where: { deletedAt: null, }, _count: { id: true, }, orderBy: { _count: { id: 'desc', }, }, }); // 视频/音频素材数量 const videoAudioCount = await prisma.material.count({ where: { deletedAt: null, materialType: { in: ['video', 'audio', '主图视频', '种草视频', '安装视频', '音乐'], }, }, }); // 素材资产数量 const assetCount = await prisma.materialAsset.count(); // 最近30天上传统计 const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); const recentUploads = await prisma.material.count({ where: { deletedAt: null, createdAt: { gte: thirtyDaysAgo, }, }, }); // 下载统计 const downloadStats = await prisma.material.aggregate({ where: { deletedAt: null, }, _sum: { downloadCount: true, }, _avg: { downloadCount: true, }, }); // 最近7天活跃用户 const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); const recentActiveUsers = await prisma.material.groupBy({ by: ['userId'], where: { deletedAt: null, createdAt: { gte: sevenDaysAgo, }, }, _count: { id: true, }, }); console.log('📊 用户统计:'); console.log(` 总用户数: ${totalUsers}`); console.log(` 最近7天活跃用户: ${activeUsers}`); console.log(` 最近7天上传素材的用户: ${recentActiveUsers.length}`); console.log(''); console.log('📦 素材统计:'); console.log(` 总素材数: ${totalMaterials}`); console.log(` 视频/音频素材: ${videoAudioCount}`); console.log(` 素材资产数: ${assetCount}`); console.log(` 最近30天上传: ${recentUploads}`); console.log(` 总下载次数: ${downloadStats._sum.downloadCount || 0}`); console.log(` 平均下载次数: ${Math.round(downloadStats._avg.downloadCount || 0)}`); console.log(''); console.log('📋 素材类型分布:'); materialTypeStats.forEach((stat) => { const percentage = ((stat._count.id / totalMaterials) * 100).toFixed(1); console.log(` ${stat.materialType}: ${stat._count.id} (${percentage}%)`); }); console.log(''); // 计算并发需求 const estimatedConcurrentUsers = Math.max(10, Math.ceil(totalUsers * 0.1)); // 假设10%用户同时在线 const estimatedDailyRequests = Math.max(1000, recentUploads * 10 + (downloadStats._sum.downloadCount || 0) * 0.1); const peakConcurrentRequests = Math.ceil(estimatedDailyRequests / (24 * 60 * 60) * 10); // 峰值是平均的10倍 console.log('🔧 并发需求估算:'); console.log(` 估算并发用户: ${estimatedConcurrentUsers}`); console.log(` 估算日请求量: ${estimatedDailyRequests}`); console.log(` 峰值并发请求: ${peakConcurrentRequests}`); console.log(''); // 推荐配置 console.log('💡 FRP配置建议:'); const recommendedPoolCount = Math.max(30, Math.min(100, peakConcurrentRequests * 2)); console.log(` 推荐连接池大小 (poolCount): ${recommendedPoolCount}`); console.log(` 推荐心跳间隔 (heartbeatInterval): 20秒`); console.log(` 推荐心跳超时 (heartbeatTimeout): 60秒`); console.log(` 推荐文件描述符限制 (LimitNOFILE): ${Math.max(8192, recommendedPoolCount * 100)}`); console.log(` 推荐内存限制 (MemoryMax): 1G`); console.log(` 推荐CPU限制 (CPUQuota): 100% (不限制)`); } main() .catch((e) => { console.error('Error:', e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });