import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' import { getServerSession } from 'next-auth' import { authOptions } from '@/lib/auth' import { isDepartmentAdmin, isSystemAdmin } from '@/lib/department-permission' import { getMaterialUploadStats } from '@/lib/admin-material-stats' import { prisma } from '@/lib/prisma' interface StatsSessionUser { role?: string | null managedDepartment?: string | null department?: string | null } function toDateStart(dateString: string): Date { return new Date(`${dateString}T00:00:00`) } function toDateEnd(dateString: string): Date { return new Date(`${dateString}T23:59:59.999`) } function escapeCsvValue(value: unknown): string { if (value === null || value === undefined) { return '' } const stringValue = String(value) if (/[",\n]/.test(stringValue)) { return `"${stringValue.replace(/"/g, '""')}"` } return stringValue } function formatDateTime(value: Date): string { return value.toLocaleString('sv-SE', { timeZone: 'Asia/Shanghai', hour12: false, }) } export async function GET(request: NextRequest) { try { const session = await getServerSession(authOptions) const sessionUser = session?.user as StatsSessionUser | undefined const role = sessionUser?.role const canViewAllDepartments = isSystemAdmin(role) const canViewDepartmentScope = isDepartmentAdmin(role) const canViewStats = canViewAllDepartments || canViewDepartmentScope const scopeDepartmentName = canViewDepartmentScope ? String(sessionUser?.managedDepartment || sessionUser?.department || '').trim() : null if (!session?.user || !canViewStats) { return NextResponse.json({ error: '无权限' }, { status: 403 }) } if (canViewDepartmentScope && !scopeDepartmentName) { return NextResponse.json({ error: '当前账号未绑定部门,无法导出统计数据' }, { status: 400 }) } const { searchParams } = new URL(request.url) const startDate = searchParams.get('startDate') const endDate = searchParams.get('endDate') const departmentKey = searchParams.get('departmentKey') const stats = await getMaterialUploadStats({ startDate, endDate, departmentKey, scopeDepartmentName, }) const selectedUserIds = Array.from(new Set(stats.selectedUsers.map((user) => user.userId))) if (selectedUserIds.length === 0) { const lines = ['素材ID,上传时间,产品型号,素材类型,店铺,场景,平台,品类,作者,上传账号,上传姓名,上传部门,下载次数,点赞数'] const csvContent = `\uFEFF${lines.join('\n')}` const rangeStart = startDate || 'all' const rangeEnd = endDate || 'all' const fileName = `material-details-${rangeStart}-${rangeEnd}.csv` return new NextResponse(csvContent, { status: 200, headers: { 'Content-Type': 'text/csv; charset=utf-8', 'Content-Disposition': `attachment; filename="${encodeURIComponent(fileName)}"`, }, }) } const createdAtFilter: { gte?: Date; lte?: Date } = {} if (startDate) { createdAtFilter.gte = toDateStart(startDate) } if (endDate) { createdAtFilter.lte = toDateEnd(endDate) } const materials = await prisma.material.findMany({ where: { deletedAt: null, userId: { in: selectedUserIds }, ...(createdAtFilter.gte || createdAtFilter.lte ? { createdAt: createdAtFilter } : {}), }, orderBy: { createdAt: 'desc' }, select: { id: true, productModel: true, materialType: true, categoryName: true, sceneName: true, platformName: true, productCategoryName: true, authorName: true, createdAt: true, downloadCount: true, likeCount: true, user: { select: { username: true, realName: true, department: true, departmentRef: { select: { name: true, }, }, }, }, }, }) const lines: string[] = [] lines.push('素材ID,上传时间,产品型号,素材类型,店铺,场景,平台,品类,作者,上传账号,上传姓名,上传部门,下载次数,点赞数') for (const material of materials) { const departmentName = material.user.departmentRef?.name || material.user.department || '未分配部门' lines.push( [ material.id, formatDateTime(material.createdAt), material.productModel, material.materialType, material.categoryName, material.sceneName, material.platformName, material.productCategoryName, material.authorName, material.user.username, material.user.realName || '', departmentName, material.downloadCount, material.likeCount, ] .map(escapeCsvValue) .join(',') ) } const csvContent = `\uFEFF${lines.join('\n')}` const rangeStart = startDate || 'all' const rangeEnd = endDate || 'all' const fileName = `material-details-${rangeStart}-${rangeEnd}.csv` return new NextResponse(csvContent, { status: 200, headers: { 'Content-Type': 'text/csv; charset=utf-8', 'Content-Disposition': `attachment; filename="${encodeURIComponent(fileName)}"`, }, }) } catch (error) { console.error('[Admin Data Stats Export] GET error:', error) return NextResponse.json({ error: '导出 CSV 失败' }, { status: 500 }) } }