import { NextRequest, NextResponse } from 'next/server' import { getServerSession } from 'next-auth' import { authOptions } from '@/lib/auth' import { prisma } from '@/lib/prisma' import { isCurrentSuperAdmin } from '@/lib/admin-permissions' export const dynamic = 'force-dynamic' /** * 获取素材操作日志 * 包括上传、删除、修改等操作 */ export async function GET(req: NextRequest) { try { const session = await getServerSession(authOptions) if (!session || !(await isCurrentSuperAdmin(session))) { return NextResponse.json({ error: '仅最高权限账号可查看素材日志' }, { status: 403 }) } const { searchParams } = new URL(req.url) const limit = parseInt(searchParams.get('limit') || '100') const offset = parseInt(searchParams.get('offset') || '0') // 获取素材操作日志 let materialLogs: any[] = [] try { const rawLogs = await prisma.$queryRaw>` SELECT ml.id, ml."action", ml."materialId", ml."userId", ml."timestamp", ml."changes", ml."materialSnapshot", u.username, u.email, m."productModel" FROM "material_logs" ml LEFT JOIN "users" u ON ml."userId" = u.id LEFT JOIN "materials" m ON ml."materialId" = m.id ORDER BY ml."timestamp" DESC LIMIT ${limit} OFFSET ${offset} ` materialLogs = rawLogs.map((log) => ({ id: log.id, action: log.action, materialId: log.materialId, userId: log.userId, timestamp: log.timestamp, changes: log.changes, materialSnapshot: log.materialSnapshot, user: log.username ? { username: log.username, email: log.email } : null, material: log.productModel ? { productModel: log.productModel } : null, })) } catch (sqlError: any) { console.error('[MaterialLogs] SQL query error:', sqlError) materialLogs = [] } // 字段名映射 const fieldNames: Record = { productModel: '产品型号', categoryName: '一级分类', sceneName: '二级分类', materialType: '素材类型', renderer: '渲染器', authorName: '作者花名', brandName: '品牌名称', seriesName: '系列/产品线', skuCode: 'SKU编码', materialTexture: '材质/工艺', renderTime: '渲染耗时', imageResolution: '输出分辨率', colorMode: '颜色模式', } const ACTION_META: Record< string, { label: string type: 'upload' | 'update' | 'delete' | 'toggle' | 'other' } > = { UPLOAD: { label: '上传素材', type: 'upload' }, UPDATE: { label: '修改素材', type: 'update' }, DELETE: { label: '删除素材', type: 'delete' }, TOGGLE_PREMIUM: { label: '切换精品状态', type: 'toggle' }, } // 构建日志列表 const logs = materialLogs.map((log: any) => { const normalizedAction = typeof log.action === 'string' ? log.action.toUpperCase() : '' const actionMeta = ACTION_META[normalizedAction] || { label: log.action || '其它操作', type: 'other' as const, } const actionText = actionMeta.label // 构建详细修改内容 const changeDetails: string[] = [] const snapshot = log.materialSnapshot && typeof log.materialSnapshot === 'object' ? log.materialSnapshot as Record : null const snapshotName = typeof snapshot?.productModel === 'string' ? snapshot.productModel : '' const materialName = log.material?.productModel || snapshotName || '已删除素材' const username = log.user?.username || '未知用户' if (log.changes && typeof log.changes === 'object') { const changes = log.changes as Record Object.entries(changes).forEach(([key, change]) => { const fieldName = fieldNames[key] || key const oldVal = change.oldValue ?? '(空)' const newVal = change.newValue ?? '(空)' changeDetails.push(`(${fieldName}:${oldVal} --> ${newVal})`) }) } if (changeDetails.length === 0) { if (normalizedAction === 'UPLOAD') { changeDetails.push(`(${username}上传了素材:${materialName})`) } else if (normalizedAction === 'DELETE') { changeDetails.push(`(${username}删除了素材:${materialName})`) } else { changeDetails.push(`(${username}${actionText}:${materialName})`) } } return { id: log.id, type: actionMeta.type, action: actionText, actionDetail: changeDetails, materialId: log.materialId, materialName: materialName, userId: log.userId, username: username, userEmail: log.user?.email || '未知邮箱', timestamp: log.timestamp instanceof Date ? log.timestamp.toISOString() : (typeof log.timestamp === 'string' ? log.timestamp : new Date(log.timestamp).toISOString()), } }) return NextResponse.json({ logs, total: logs.length, }) } catch (error) { console.error('[MaterialLogs] GET error:', error) return NextResponse.json({ error: '获取日志失败' }, { status: 500 }) } }