import { NextRequest, NextResponse } from 'next/server' import { prisma } from '@/lib/prisma' import { getServerSession } from 'next-auth' import { authOptions } from '@/lib/auth' export const dynamic = 'force-dynamic' export async function GET(req: NextRequest) { try { // 获取会话 let session try { session = await getServerSession(authOptions) } catch (sessionError) { console.error('[SystemStatus] Session error:', sessionError) return NextResponse.json( { error: '会话获取失败', message: sessionError instanceof Error ? sessionError.message : '未知错误' }, { status: 500 } ) } if (!session) { return NextResponse.json( { error: '未登录,无法访问系统状态' }, { status: 401 } ) } // 并行获取各项统计数据(优化:减少查询次数) const thirtyDaysAgoForStats = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) let userStats: { _count: { id: number } } let materialStats: { _count: { id: number } } let systemInfo: { enableRegistration: boolean; enableUserActivation: boolean; siteName: string } | null let recentActivity: Array<{ id: string; productModel: string | null; createdAt: Date; user: { username: string; email: string } | null }> let totalUsers: number let totalMaterials: number let activeUsers: number let departmentStats: Array<{ department: string | null; _count: { id: number } }> let roleDistribution: Array<{ role: string; isSuperAdmin: boolean | null; _count: { id: number } }> try { [ userStats, materialStats, systemInfo, recentActivity, totalUsers, totalMaterials, activeUsers, departmentStats, roleDistribution ] = await Promise.all([ // 用户统计(最近30天) prisma.user.aggregate({ _count: { id: true }, where: { createdAt: { gte: thirtyDaysAgoForStats } } }), // 素材统计(最近30天) prisma.material.aggregate({ _count: { id: true }, where: { createdAt: { gte: thirtyDaysAgoForStats } } }), // 系统设置(使用缓存,减少数据库查询) prisma.systemSettings.findFirst({ select: { enableRegistration: true, enableUserActivation: true, siteName: true, } }), // 最近活动(限制为10条,减少数据传输) prisma.material.findMany({ take: 10, // 减少到10条 orderBy: { createdAt: 'desc' }, select: { id: true, productModel: true, createdAt: true, user: { select: { username: true, email: true } } } }), // 总用户数 prisma.user.count(), // 总素材数 prisma.material.count(), // 活跃用户(最近7天) prisma.user.count({ where: { updatedAt: { gte: sevenDaysAgo } } }), // 部门统计(包括所有部门,包括空字符串和null) prisma.user.groupBy({ by: ['department'], _count: { id: true } }), // 权限分布统计 prisma.user.groupBy({ by: ['role', 'isSuperAdmin'], _count: { id: true } }) ]) } catch (dbError) { console.error('[SystemStatus] Database query error:', dbError) // 如果数据库查询失败,返回默认值 userStats = { _count: { id: 0 } } materialStats = { _count: { id: 0 } } systemInfo = null recentActivity = [] totalUsers = 0 totalMaterials = 0 activeUsers = 0 departmentStats = [] roleDistribution = [] } // 计算趋势数据 const thirtyDaysAgo = new Date() thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30) // 获取本月第一天 const startOfCurrentMonth = new Date() startOfCurrentMonth.setDate(1) startOfCurrentMonth.setHours(0, 0, 0, 0) let materialTrendDaily: Array<{ date: Date, count: bigint }> = [] let materialTrendMonthly: Array<{ date: Date, count: bigint }> = [] let userGrowthTrend: Array<{ date: Date, count: bigint }> = [] try { // 用户增长趋势(最近30天) userGrowthTrend = await prisma.$queryRaw>` SELECT DATE_TRUNC('day', "createdAt")::date as date, COUNT(*)::bigint as count FROM "users" WHERE "createdAt" >= ${thirtyDaysAgo} GROUP BY DATE_TRUNC('day', "createdAt") ORDER BY date ASC ` // 先检查是否有素材数据 const materialCount = await prisma.material.count() console.log('[SystemStatus] Total materials in database:', materialCount) if (materialCount > 0) { // 获取一些样本数据,看看createdAt的实际值 const sampleMaterials = await prisma.material.findMany({ take: 3, select: { id: true, createdAt: true }, orderBy: { createdAt: 'desc' } }) console.log('[SystemStatus] Sample material createdAt values:', sampleMaterials.map(m => ({ id: m.id, createdAt: m.createdAt, createdAtISO: m.createdAt.toISOString(), createdAtLocal: m.createdAt.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }) }))) } // 素材每天的数据(直接使用createdAt,不转换时区,让PostgreSQL使用数据库时区) const dailyResult = await prisma.$queryRaw>` SELECT "createdAt"::date as date, COUNT(*)::bigint as count FROM "materials" GROUP BY "createdAt"::date ORDER BY date DESC LIMIT 100 ` materialTrendDaily = dailyResult console.log('[SystemStatus] Material daily trend count:', dailyResult.length) if (dailyResult.length > 0) { console.log('[SystemStatus] Sample daily data:', dailyResult.slice(0, 5).map(item => ({ date: item.date, dateStr: item.date.toISOString().split('T')[0], count: Number(item.count) }))) } else { console.warn('[SystemStatus] No daily trend data found, but material count is:', materialCount) } // 素材每月的数据(直接使用createdAt,不转换时区) const monthlyResult = await prisma.$queryRaw>` SELECT DATE_TRUNC('month', "createdAt")::date as date, COUNT(*)::bigint as count FROM "materials" GROUP BY DATE_TRUNC('month', "createdAt") ORDER BY date DESC LIMIT 24 ` materialTrendMonthly = monthlyResult console.log('[SystemStatus] Material monthly trend count:', monthlyResult.length) if (monthlyResult.length > 0) { console.log('[SystemStatus] Sample monthly data:', monthlyResult.slice(0, 5).map(item => ({ date: item.date, dateStr: item.date.toISOString().split('T')[0].substring(0, 7), count: Number(item.count) }))) } else { console.warn('[SystemStatus] No monthly trend data found, but material count is:', materialCount) } } catch (queryError) { console.error('[SystemStatus] Trend query error:', queryError) materialTrendDaily = [] materialTrendMonthly = [] userGrowthTrend = [] } const statusData = { overview: { totalUsers, activeUsers, totalMaterials, newUsersThisMonth: userStats._count.id, newMaterialsThisMonth: materialStats._count.id }, trends: { userGrowth: userGrowthTrend.map(item => ({ date: item.date.toISOString().split('T')[0], // 转换为 YYYY-MM-DD 格式 count: Number(item.count) })), // 兼容旧字段 materialUploads,默认返回月度数据 materialUploads: materialTrendMonthly.map(item => ({ date: item.date.toISOString().split('T')[0].substring(0, 7), count: Number(item.count) })), materialUploadsDaily: materialTrendDaily.map(item => ({ date: item.date.toISOString().split('T')[0], // 转换为 YYYY-MM-DD 格式 count: Number(item.count) })), materialUploadsMonthly: materialTrendMonthly.map(item => { const dateStr = item.date.toISOString().split('T')[0] return { date: dateStr.substring(0, 7), // 转换为 YYYY-MM 格式 count: Number(item.count) } }) }, distribution: { departments: departmentStats.map(item => ({ department: item.department || '未设置', count: item._count.id })), roles: roleDistribution.map(item => ({ role: item.role || 'USER', isSuperAdmin: item.isSuperAdmin || false, count: item._count.id })) }, systemInfo: { registrationEnabled: systemInfo?.enableRegistration || false, userActivationRequired: systemInfo?.enableUserActivation || false, siteName: systemInfo?.siteName || '默认站点' }, recentActivity: recentActivity .filter(activity => activity.user) // 过滤掉没有用户的记录 .map(activity => ({ id: activity.id, title: activity.productModel || '未命名素材', createdAt: activity.createdAt.toISOString(), uploadedBy: { username: activity.user?.username || '未知用户', email: activity.user?.email || '' } })), // 系统状态 status: { database: 'healthy', // 可以扩展为实际数据库连接检查 timestamp: new Date().toISOString(), uptime: process.uptime() } } return NextResponse.json(statusData) } catch (error) { console.error('[SystemStatus] GET error:', error) // 输出更详细的错误信息 if (error instanceof Error) { console.error('[SystemStatus] Error message:', error.message) console.error('[SystemStatus] Error stack:', error.stack) } return NextResponse.json( { error: '获取系统状态失败', message: error instanceof Error ? error.message : '未知错误' }, { status: 500 } ) } }