161 lines
4.0 KiB
TypeScript
161 lines
4.0 KiB
TypeScript
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 }
|
||
);
|
||
}
|
||
}
|