WuYongJie f646b71813
Some checks failed
CI Check / check (push) Has been cancelled
chore: 初始化本地仓库并同步最近改动
2025-11-18 17:50:16 +08:00

222 lines
6.7 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 { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth'
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 }
)
}
const isSuperAdmin = (session?.user as any)?.isSuperAdmin
if (!session || ((session.user as any)?.role !== 'ADMIN' && !isSuperAdmin)) {
return NextResponse.json(
{ error: '无权限访问' },
{ status: 403 }
)
}
// 并行获取各项统计数据(优化:减少查询次数)
const thirtyDaysAgo = 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: thirtyDaysAgo } }
}),
// 素材统计(最近30天)
prisma.material.aggregate({
_count: { id: true },
where: { createdAt: { gte: thirtyDaysAgo } }
}),
// 系统设置(使用缓存,减少数据库查询)
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 }
}
}),
// 部门统计
prisma.user.groupBy({
by: ['department'],
_count: { id: true },
where: {
department: { not: '' }
}
}),
// 权限分布统计
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 = []
}
// 计算素材上传趋势(按月统计,最近12个月)
const twelveMonthsAgo = new Date()
twelveMonthsAgo.setMonth(twelveMonthsAgo.getMonth() - 12)
let materialTrend: Array<{ date: Date, count: bigint }> = []
try {
materialTrend = await prisma.$queryRaw<Array<{ date: Date, count: bigint }>>`
SELECT
DATE_TRUNC('month', created_at)::date as date,
COUNT(*)::bigint as count
FROM materials
WHERE created_at >= ${twelveMonthsAgo}
GROUP BY DATE_TRUNC('month', created_at)
ORDER BY date ASC
`
} catch (queryError) {
console.error('[SystemStatus] Material trend query error:', queryError)
materialTrend = []
}
const statusData = {
overview: {
totalUsers,
activeUsers,
totalMaterials,
newUsersThisMonth: userStats._count.id,
newMaterialsThisMonth: materialStats._count.id
},
trends: {
materialUploads: materialTrend.map(item => ({
date: item.date.toISOString().split('T')[0].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 }
)
}
}