feat(admin): enhance admin dashboard, user management, and content editor UX
- Dashboard: add stats API with content status distribution, recent notifications, and recent content updates; display active users, pending reviews, unread counts - User management: full CRUD with role assignment, search, pagination, delete dialog - Notification center: list with unread filter, mark as read, mark all as read, pagination, and auto-refresh unread count - Content editor: form validation (required fields, slug format, blur-triggered errors), auto-save with 3s debounce and status indicator, publish confirmation dialog, unsaved changes warning on leave - Admin layout: add navigation links for user management, roles, and notifications - admin-api: make request() method public for custom API calls - gitignore: add reports/mutation/ to exclude mutation test output
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { NextRequest } from 'next/server';
|
||||
import { prisma } from '@/lib/db';
|
||||
import { authenticateRequest } from '@/lib/auth';
|
||||
import { success, unauthorized, internalError } from '@/lib/api-response';
|
||||
|
||||
// GET /api/admin/stats - 获取仪表盘统计数据
|
||||
export async function GET(request: NextRequest) {
|
||||
const user = authenticateRequest(request);
|
||||
if (!user) return unauthorized();
|
||||
|
||||
try {
|
||||
const [modelCount, itemCount, zoneCount, userCount, pendingReviewCount, unreadCount] = await Promise.all([
|
||||
prisma.contentModel.count(),
|
||||
prisma.contentItem.count(),
|
||||
prisma.contentZone.count(),
|
||||
prisma.user.count({ where: { status: 1 } }),
|
||||
prisma.contentItem.count({ where: { status: 'review' } }),
|
||||
prisma.notification.count({ where: { userId: user.userId, read: false } }),
|
||||
]);
|
||||
|
||||
// 获取每个内容模型的条目数
|
||||
const modelItemCounts = await prisma.contentItem.groupBy({
|
||||
by: ['modelCode'],
|
||||
_count: { modelCode: true },
|
||||
});
|
||||
|
||||
// 获取不同状态的条目数
|
||||
const statusCounts = await prisma.contentItem.groupBy({
|
||||
by: ['status'],
|
||||
_count: { status: true },
|
||||
});
|
||||
|
||||
// 获取最近 5 条通知
|
||||
const recentNotifications = await prisma.notification.findMany({
|
||||
where: { userId: user.userId },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 5,
|
||||
select: {
|
||||
id: true,
|
||||
type: true,
|
||||
title: true,
|
||||
read: true,
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
// 获取最近 5 条更新的内容
|
||||
const recentItems = await prisma.contentItem.findMany({
|
||||
orderBy: { updatedAt: 'desc' },
|
||||
take: 5,
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
modelCode: true,
|
||||
status: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
return success({
|
||||
models: modelCount,
|
||||
items: itemCount,
|
||||
zones: zoneCount,
|
||||
activeUsers: userCount,
|
||||
pendingReviews: pendingReviewCount,
|
||||
unreadNotifications: unreadCount,
|
||||
modelItemCounts: modelItemCounts.map((m) => ({
|
||||
modelCode: m.modelCode,
|
||||
count: m._count.modelCode,
|
||||
})),
|
||||
statusCounts: statusCounts.map((s) => ({
|
||||
status: s.status,
|
||||
count: s._count.status,
|
||||
})),
|
||||
recentNotifications,
|
||||
recentItems,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Get stats error:', error);
|
||||
return internalError();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user