2026-05-28 23:27:15 +08:00

165 lines
5.6 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 { 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<Array<{
id: string
action: string
materialId: string
userId: string
timestamp: Date
changes: any
materialSnapshot: any
username: string | null
email: string | null
productModel: string | null
}>>`
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<string, string> = {
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<string, unknown>
: 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<string, { oldValue: any; newValue: any }>
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 })
}
}