2026-05-25 14:31:58 +08:00

161 lines
4.0 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 { NextResponse } from 'next/server';
import { mainPrisma, getProjectDb } from '@/lib/db';
import { getAdminSession } from '@/lib/auth';
export async function GET(
request: Request,
{ params }: { params: Promise<{ projectCode: string }> }
) {
try {
const session = await getAdminSession();
const { projectCode } = await params;
if (!session) {
return NextResponse.json(
{ error: '未登录' },
{ status: 401 }
);
}
// 获取项目信息并验证权限
const project = await mainPrisma.project.findUnique({
where: { code: projectCode },
});
if (!project) {
return NextResponse.json(
{ error: '项目不存在' },
{ status: 404 }
);
}
// 检查权限
const admin = await mainPrisma.admin.findUnique({
where: { id: session.id },
include: { adminProjects: true },
});
if (!admin) {
return NextResponse.json(
{ error: '管理员不存在' },
{ status: 404 }
);
}
if (!admin.isSuperAdmin) {
const hasAccess = admin.adminProjects.some(
(ap: { projectId: string }) => ap.projectId === project.id
);
if (!hasAccess) {
return NextResponse.json(
{ error: '无权访问此项目' },
{ status: 403 }
);
}
}
// 获取项目数据库连接
const prisma = await getProjectDb(projectCode);
// 获取统计数据
const [
totalLicenses,
licensesByType,
licensesByStatus,
totalActivations,
activeActivations,
pendingAlerts,
recentActivations,
] = await Promise.all([
// 许可证总数
prisma.license.count(),
// 按类型统计
prisma.license.groupBy({
by: ['licenseType'],
_count: true,
}),
// 按状态统计
prisma.license.groupBy({
by: ['status'],
_count: true,
}),
// 激活总数
prisma.activation.count(),
// 活跃激活(7天内有心跳)
prisma.activation.count({
where: {
isActive: true,
lastHeartbeat: {
gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
},
},
}),
// 待处理告警
prisma.alert.count({
where: { status: 'pending' },
}),
// 最近激活
prisma.activation.findMany({
take: 10,
orderBy: { activatedAt: 'desc' },
include: {
license: {
select: {
licenseKey: true,
licenseType: true,
customerName: true,
},
},
},
}),
]);
// 格式化统计数据
const byType: Record<string, number> = {};
licensesByType.forEach((item: { licenseType: string; _count: number }) => {
byType[item.licenseType] = item._count;
});
const byStatus: Record<string, number> = {};
licensesByStatus.forEach((item: { status: string; _count: number }) => {
byStatus[item.status] = item._count;
});
return NextResponse.json({
licenses: {
total: totalLicenses,
byType,
byStatus,
},
activations: {
total: totalActivations,
active: activeActivations,
},
alerts: {
pending: pendingAlerts,
},
recentActivations: recentActivations.map((activation: {
id: string;
activatedAt: Date;
license: {
licenseKey: string;
licenseType: string;
customerName: string | null;
};
}) => ({
id: activation.id,
licenseKey: activation.license.licenseKey,
licenseType: activation.license.licenseType,
customerName: activation.license.customerName,
activatedAt: activation.activatedAt.toISOString(),
})),
});
} catch (error) {
console.error('获取仪表盘数据失败:', error);
return NextResponse.json(
{ error: '服务器错误' },
{ status: 500 }
);
}
}