459 lines
15 KiB
Plaintext
459 lines
15 KiB
Plaintext
generator client {
|
||
provider = "prisma-client-js"
|
||
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
|
||
}
|
||
|
||
datasource db {
|
||
provider = "postgresql"
|
||
url = env("DATABASE_URL")
|
||
}
|
||
|
||
enum Role {
|
||
USER
|
||
ADMIN
|
||
}
|
||
|
||
model User {
|
||
id String @id @default(uuid())
|
||
email String @unique
|
||
username String @unique
|
||
password String
|
||
role Role @default(USER)
|
||
isActive Boolean @default(false)
|
||
canDownload Boolean @default(true)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
department String @default("")
|
||
dingtalkUserId String? @unique
|
||
loginType String @default("normal")
|
||
realName String?
|
||
phone String? @unique
|
||
avatar String?
|
||
managedDepartment String?
|
||
isSuperAdmin Boolean @default(false)
|
||
parentId String?
|
||
likes MaterialLike[]
|
||
materials Material[]
|
||
downloadLogs MaterialDownloadLog[]
|
||
materialLogs MaterialLog[]
|
||
parent User? @relation("UserHierarchy", fields: [parentId], references: [id])
|
||
children User[] @relation("UserHierarchy")
|
||
favorites MaterialFavorite[]
|
||
browseHistory BrowseHistory[]
|
||
dingtalkLoginTokens DingtalkLoginToken[]
|
||
materialShareTokens MaterialShareToken[]
|
||
|
||
@@map("users")
|
||
}
|
||
|
||
model DingtalkLoginToken {
|
||
id String @id @default(uuid())
|
||
tokenHash String @unique
|
||
userId String
|
||
purpose String @default("login")
|
||
expiresAt DateTime
|
||
usedAt DateTime?
|
||
createdAt DateTime @default(now())
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([userId])
|
||
@@index([purpose])
|
||
@@index([expiresAt])
|
||
@@map("dingtalk_login_tokens")
|
||
}
|
||
|
||
model MaterialShareToken {
|
||
id String @id @default(uuid())
|
||
tokenHash String @unique
|
||
materialId String
|
||
createdByUserId String
|
||
expiresAt DateTime
|
||
revokedAt DateTime?
|
||
createdAt DateTime @default(now())
|
||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||
createdBy User @relation(fields: [createdByUserId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([materialId])
|
||
@@index([createdByUserId])
|
||
@@index([expiresAt])
|
||
@@index([revokedAt])
|
||
@@map("material_share_tokens")
|
||
}
|
||
|
||
model UploadTemplate {
|
||
id String @id @default(uuid())
|
||
name String
|
||
description String?
|
||
status String @default("draft")
|
||
materialType String @default("其他")
|
||
/// 模板素材类型大类:IMAGE / VIDEO / OTHER(与具体 materialType 文案解耦)
|
||
materialKind String @default("IMAGE")
|
||
version Int @default(1)
|
||
fieldGroups String?
|
||
displayConfig String?
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
fields UploadField[]
|
||
materials Material[]
|
||
|
||
@@index([status])
|
||
@@index([materialType])
|
||
@@index([materialKind])
|
||
@@index([createdAt])
|
||
@@index([updatedAt])
|
||
}
|
||
|
||
model UploadField {
|
||
id String @id @default(uuid())
|
||
templateId String
|
||
key String
|
||
label String
|
||
type String
|
||
required Boolean @default(false)
|
||
orderIndex Int @default(0)
|
||
layoutWidth String?
|
||
validations String?
|
||
optionsSource String?
|
||
optionsStatic String?
|
||
allowedExtensions String?
|
||
visibleOnHomeFilter Boolean @default(false)
|
||
isSearchable Boolean @default(false)
|
||
isReadOnly Boolean @default(false)
|
||
addToCategory Boolean @default(false)
|
||
conditions String?
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
template UploadTemplate @relation(fields: [templateId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([templateId])
|
||
@@index([key])
|
||
@@index([type])
|
||
@@index([visibleOnHomeFilter])
|
||
@@index([isSearchable])
|
||
@@index([addToCategory])
|
||
@@index([orderIndex])
|
||
@@index([templateId, orderIndex])
|
||
}
|
||
|
||
model FieldDefinition {
|
||
id String @id @default(uuid())
|
||
key String @unique
|
||
label String
|
||
type String @default("select")
|
||
description String?
|
||
source String @default("custom") // standard | custom
|
||
sourceTemplateIds String? // JSON array of template ids referencing this field
|
||
isStandard Boolean @default(false)
|
||
isFilterEnabled Boolean @default(false)
|
||
filterDisplayName String?
|
||
filterOrder Int @default(0)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
options FieldOption[]
|
||
|
||
@@index([source])
|
||
@@index([isStandard])
|
||
@@index([isFilterEnabled])
|
||
@@index([filterOrder])
|
||
@@index([isFilterEnabled, filterOrder])
|
||
}
|
||
|
||
model FieldOption {
|
||
id String @id @default(uuid())
|
||
fieldId String
|
||
value String
|
||
label String
|
||
orderIndex Int @default(0)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
field FieldDefinition @relation(fields: [fieldId], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
||
|
||
@@unique([fieldId, value])
|
||
@@index([fieldId])
|
||
@@index([fieldId, orderIndex])
|
||
}
|
||
|
||
model FilterItem {
|
||
id String @id @default(uuid())
|
||
fieldKey String
|
||
label String
|
||
group String?
|
||
orderIndex Int @default(0)
|
||
multiSelect Boolean @default(false)
|
||
showOnHome Boolean @default(true)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
}
|
||
|
||
model Material {
|
||
id String @id @default(uuid())
|
||
previewUrl String
|
||
productModel String
|
||
brandName String?
|
||
seriesName String?
|
||
skuCode String?
|
||
materialTexture String?
|
||
categoryName String
|
||
sceneName String
|
||
materialType String @default("3DMAX")
|
||
materialKind String @default("IMAGE") // 素材大类:IMAGE / VIDEO / OTHER
|
||
renderer String @default("CR")
|
||
renderTime String?
|
||
imageResolution String?
|
||
colorMode String?
|
||
audioDuration String? // 音频时长,如:00:30、1:25
|
||
audioCategory String? // 音频分类,如:背景音乐、音效、配音
|
||
audioScene String? // 音频使用场景,如:广告、产品视频、直播
|
||
audioUrl String? // 音频文件URL,用于在线播放
|
||
authorName String
|
||
/// 素材归属类型:USER(个人负责)/ TEAM(团队空间)
|
||
ownerType String @default("TEAM")
|
||
/// 素材归属主体ID:当 ownerType=USER 时为用户ID;ownerType=TEAM 时为团队/空间ID
|
||
ownerId String @default("default-team")
|
||
/// 最初上传者用户ID(离职后仍然保留,便于追溯)
|
||
createdByUserId String?
|
||
/// 最近一次所有权移交时间
|
||
lastTransferredAt DateTime?
|
||
zipFileName String @default("")
|
||
zipFilePath String @default("")
|
||
zipFileSize BigInt @default(0)
|
||
// 视频类素材相关字段(为后续视频预览 / 转码预留)
|
||
videoSourceUrl String? // 源视频文件 URL
|
||
videoPreview540Url String? // 540P 预览视频 URL
|
||
videoPreviewClipUrl String? // 首页卡片用 10 秒超小预览视频 URL
|
||
videoCoverUrl String? // 从视频帧中截取的封面图 URL
|
||
videoDuration Int? // 视频时长(秒)
|
||
videoProcessingStatus String? // 视频处理状态:pending / processing / done / failed
|
||
downloadCount Int @default(0)
|
||
likeCount Int @default(0)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
userId String
|
||
displayImages String @default("")
|
||
platformName String @default("")
|
||
previewUrls String? // 多预览图URLs(JSON数组格式)
|
||
metadata String?
|
||
templateId String?
|
||
isPremium Boolean @default(false)
|
||
deletedAt DateTime? // 软删除时间戳
|
||
likes MaterialLike[]
|
||
favorites MaterialFavorite[]
|
||
downloadLogs MaterialDownloadLog[]
|
||
browseHistory BrowseHistory[]
|
||
shareTokens MaterialShareToken[]
|
||
videoTranscodeJob VideoTranscodeJob?
|
||
template UploadTemplate? @relation(fields: [templateId], references: [id])
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([userId])
|
||
@@index([ownerType, ownerId])
|
||
@@index([createdByUserId])
|
||
@@index([createdAt])
|
||
@@index([categoryName])
|
||
@@index([sceneName])
|
||
@@index([platformName])
|
||
@@index([materialType])
|
||
@@index([downloadCount])
|
||
@@index([likeCount])
|
||
@@index([deletedAt])
|
||
@@map("materials")
|
||
}
|
||
|
||
model VideoTranscodeJob {
|
||
id String @id @default(uuid())
|
||
materialId String @unique
|
||
videoSourceUrl String
|
||
status String @default("queued") // queued / processing / done / failed
|
||
attempts Int @default(0)
|
||
maxAttempts Int @default(3)
|
||
needsCoverImage Boolean @default(true)
|
||
errorMessage String?
|
||
lockedAt DateTime?
|
||
startedAt DateTime?
|
||
finishedAt DateTime?
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([status, updatedAt])
|
||
@@index([lockedAt])
|
||
@@map("video_transcode_jobs")
|
||
}
|
||
|
||
model MaterialLike {
|
||
id String @id @default(uuid())
|
||
materialId String
|
||
userId String
|
||
createdAt DateTime @default(now())
|
||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([materialId, userId])
|
||
@@index([materialId])
|
||
@@index([userId])
|
||
@@map("material_likes")
|
||
}
|
||
|
||
model MaterialFavorite {
|
||
id String @id @default(uuid())
|
||
materialId String
|
||
userId String
|
||
createdAt DateTime @default(now())
|
||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([materialId, userId])
|
||
@@index([materialId])
|
||
@@index([userId])
|
||
@@map("material_favorites")
|
||
}
|
||
|
||
model MaterialDownloadLog {
|
||
id String @id @default(uuid())
|
||
materialId String
|
||
userId String
|
||
downloadedAt DateTime @default(now())
|
||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([materialId])
|
||
@@index([userId])
|
||
@@index([downloadedAt])
|
||
@@index([materialId, userId, downloadedAt])
|
||
@@map("material_download_logs")
|
||
}
|
||
|
||
model MaterialLog {
|
||
id String @id @default(uuid())
|
||
materialId String
|
||
userId String
|
||
action String // 'UPLOAD', 'UPDATE', 'DELETE', 'TOGGLE_PREMIUM'
|
||
changes Json? // 详细变更记录,格式:{ "fieldName": { "oldValue": "...", "newValue": "..." } }
|
||
materialSnapshot Json? // 素材硬删除后仍用于审计展示的最小快照
|
||
timestamp DateTime @default(now())
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([materialId])
|
||
@@index([userId])
|
||
@@index([timestamp])
|
||
@@map("material_logs")
|
||
}
|
||
|
||
model Category {
|
||
id String @id @default(uuid())
|
||
name String @unique
|
||
options String? // JSON array of options for dropdown
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("categories")
|
||
}
|
||
|
||
model Scene {
|
||
id String @id @default(uuid())
|
||
name String @unique
|
||
options String? // JSON array of options for dropdown
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("scenes")
|
||
}
|
||
|
||
model Platform {
|
||
id String @id @default(uuid())
|
||
name String @unique
|
||
options String? // JSON array of options for dropdown
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("platforms")
|
||
}
|
||
|
||
model SystemSettings {
|
||
id String @id @default(uuid())
|
||
headerBgColor String @default("#1f2937")
|
||
siteName String @default("FLNXI素材库")
|
||
premiumRatio Float @default(10.0)
|
||
updatedAt DateTime @updatedAt
|
||
createdAt DateTime @default(now())
|
||
headerLogoUrl String @default("")
|
||
loginLogoUrl String @default("")
|
||
s3AccessKey String @default("")
|
||
s3Bucket String @default("")
|
||
s3Endpoint String @default("")
|
||
s3PublicUrl String @default("")
|
||
s3Region String @default("us-east-1")
|
||
s3SecretKey String @default("")
|
||
enableRegistration Boolean @default(false)
|
||
enableUserActivation Boolean @default(false)
|
||
enableAiImageEdit Boolean @default(false)
|
||
enableTunnelService Boolean @default(false)
|
||
enablePublicShare Boolean @default(true)
|
||
homeDeleteButtonMode String? @default("ADMIN_ONLY")
|
||
enabledBuiltinTemplates String? // JSON array of enabled builtin template keys (empty array or null = all enabled)
|
||
categoryManagedFields String? // JSON array,记录分类管理已启用的字段 key
|
||
|
||
@@map("system_settings")
|
||
}
|
||
|
||
model Department {
|
||
id String @id @default(uuid())
|
||
name String @unique
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("departments")
|
||
}
|
||
|
||
model Danmaku {
|
||
id String @id @default(uuid())
|
||
content String
|
||
username String
|
||
userId String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([createdAt])
|
||
@@map("danmakus")
|
||
}
|
||
|
||
model Invitation {
|
||
id String @id @default(uuid())
|
||
code String @unique
|
||
createdBy String
|
||
usedBy String?
|
||
usedAt DateTime?
|
||
expiresAt DateTime?
|
||
maxUses Int @default(1)
|
||
currentUses Int @default(0)
|
||
isActive Boolean @default(true)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([code])
|
||
@@index([createdBy])
|
||
@@map("invitations")
|
||
}
|
||
|
||
|
||
model BrowseHistory {
|
||
id String @id @default(uuid())
|
||
materialId String
|
||
userId String
|
||
viewedAt DateTime @default(now())
|
||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([materialId, userId])
|
||
@@index([userId])
|
||
@@index([viewedAt])
|
||
@@index([userId, viewedAt])
|
||
@@map("browse_history")
|
||
}
|
||
|
||
// 系统配置表(用于存储许可证等配置)
|
||
model SystemConfig {
|
||
id String @id @default(uuid())
|
||
key String @unique
|
||
value String @db.Text
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@map("system_configs")
|
||
}
|