generator client { provider = "prisma-client-js" } datasource db { provider = "mysql" url = env("DATABASE_URL") } model User { id Int @id @default(autoincrement()) phone String? @unique openid String? @unique nickname String @default("用户") avatar String @default("") memberLevel Int @default(0) memberExpireAt DateTime? dailyUsage Int @default(0) lastUsageDate String @default("") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt usedAudioMinutes Int @default(0) subscriptionResetDate DateTime? comments Comment[] drafts Draft[] favorites Favorite[] orders Order[] playRecords PlayRecord[] playlists Playlist[] signRecords SignRecord[] subscriptions Subscription[] tokenBalance TokenBalance? tokenUsages TokenUsage[] preferences UserPreference? @@index([phone]) @@index([openid]) } model Order { id Int @id @default(autoincrement()) userId Int orderNo String @unique planId Int? productType String amount Decimal @db.Decimal(10, 2) status String @default("pending") paymentMethod String? paymentId String? paidAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt plan SubscriptionPlan? @relation(fields: [planId], references: [id]) user User @relation(fields: [userId], references: [id]) tokenUsages TokenUsage[] @@index([userId, createdAt]) @@index([orderNo]) @@index([status]) @@index([planId], map: "Order_planId_fkey") } model PlayRecord { id Int @id @default(autoincrement()) userId Int chapterId Int progress Float @default(0) duration Float @default(0) updatedAt DateTime @updatedAt createdAt DateTime @default(now()) chapter BookChapter @relation(fields: [chapterId], references: [id]) user User @relation(fields: [userId], references: [id]) @@unique([userId, chapterId]) @@index([userId]) @@index([chapterId]) } model UserPreference { id Int @id @default(autoincrement()) userId Int @unique playSpeed Float @default(1) quality String @default("standard") theme String @default("light") defaultVoiceId String? @default("cherry") defaultVolume Int @default(50) autoPlayNext Boolean @default(true) wifiOnlyDownload Boolean @default(false) updatedAt DateTime @updatedAt createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id]) } model Favorite { id Int @id @default(autoincrement()) userId Int bookId Int createdAt DateTime @default(now()) book Book @relation(fields: [bookId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id]) @@unique([userId, bookId]) @@index([userId]) @@index([bookId]) } model Comment { id Int @id @default(autoincrement()) userId Int chapterId Int content String @db.Text rating Int createdAt DateTime @default(now()) chapter BookChapter @relation(fields: [chapterId], references: [id]) user User @relation(fields: [userId], references: [id]) @@index([chapterId]) @@index([userId]) } model Notification { id String @id @default(uuid()) userId String title String content String @db.Text isRead Boolean @default(false) createdAt DateTime @default(now()) } model Book { id Int @id @default(autoincrement()) userId Int? title String subtitle String? description String @db.Text coverUrl String? targetAudience String @default("通用") style String @default("专业严谨") bookScale String @default("标准教程") totalChapters Int @default(10) estimatedWords Int @default(0) progress Int @default(0) isPublished Boolean @default(false) outlineJson String? @db.LongText foreword String? @db.Text afterword String? @db.Text errorMsg String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt failedStage String? @db.VarChar(30) genStage String @default("draft") bookAnalysis String? @db.Text chapters BookChapter[] favorites Favorite[] videoProjects VideoProject[] @@index([userId]) @@index([createdAt]) } model BookChapter { id Int @id @default(autoincrement()) bookId Int parentId Int @default(0) level Int @default(1) number Int title String summary String? @db.Text keyPoints String? @db.Text estimatedWords Int @default(1000) content String? @db.LongText wordCount Int @default(0) contentError String? @db.Text generatedAt DateTime? audioUrl String? @db.Text audioDuration Int @default(0) videoUrl String? @db.Text videoDuration Int? isPublic Boolean @default(false) genStage String @default("idle") lrcLyrics String? @db.Text book Book @relation(fields: [bookId], references: [id], onDelete: Cascade) comments Comment[] playRecords PlayRecord[] playlistItems PlaylistItem[] videoProjects VideoProject[] @@unique([bookId, parentId, level, number]) @@index([bookId]) @@index([bookId, parentId]) @@index([bookId, level]) } model VideoProject { id Int @id @default(autoincrement()) userId Int? title String description String? coverUrl String? configJson String? @db.LongText outputUrl String? duration Int? fileSize Int? bookId Int? chapterId Int? status String @default("draft") progress Int @default(0) errorMsg String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt book Book? @relation(fields: [bookId], references: [id]) chapter BookChapter? @relation(fields: [chapterId], references: [id]) @@index([userId, status]) @@index([createdAt]) @@index([bookId], map: "VideoProject_bookId_fkey") @@index([chapterId], map: "VideoProject_chapterId_fkey") } model SearchHistory { id Int @id @default(autoincrement()) userId Int keyword String createdAt DateTime @default(now()) @@index([userId, createdAt]) @@index([userId]) } model HotSearch { id Int @id @default(autoincrement()) keyword String count Int @default(0) sort Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([sort]) @@index([count]) } model SignRecord { id Int @id @default(autoincrement()) userId Int createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id]) @@unique([userId, createdAt]) @@index([userId, createdAt]) } model SubscriptionPlan { id Int @id @default(autoincrement()) name String level Int @default(0) priceMonthly Decimal @default(0.00) @db.Decimal(10, 2) priceYearly Decimal @default(0.00) @db.Decimal(10, 2) description String? @db.Text features String? @db.Text isRecommended Boolean @default(false) isActive Boolean @default(true) sortOrder Int @default(0) dailyGenerations Int @default(3) perGenerationLimit Int @default(2000) monthlyTokens Int @default(10000) monthlyMinutes Int @default(30) yearlyTokens Int? voiceOptions Int @default(5) audioQuality String @default("standard") apiAccess Boolean @default(false) batchProcessing Boolean @default(false) teamManagement Boolean @default(false) overageEnabled Boolean @default(false) overagePrice Decimal @default(0.00) @db.Decimal(10, 2) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt orders Order[] subscriptions Subscription[] @@index([level]) @@index([isActive, sortOrder]) } model Subscription { id Int @id @default(autoincrement()) userId Int planId Int startDate DateTime endDate DateTime status String @default("active") autoRenew Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt plan SubscriptionPlan @relation(fields: [planId], references: [id]) user User @relation(fields: [userId], references: [id]) @@index([userId, status]) @@index([userId, endDate]) @@index([planId], map: "Subscription_planId_fkey") } model TokenUsage { id Int @id @default(autoincrement()) userId Int type String amount Int @default(0) contentLength Int @default(0) orderId Int? description String? @db.Text createdAt DateTime @default(now()) order Order? @relation(fields: [orderId], references: [id]) user User @relation(fields: [userId], references: [id]) @@index([userId, createdAt]) @@index([userId, type]) @@index([orderId], map: "TokenUsage_orderId_fkey") } model TokenBalance { id Int @id @default(autoincrement()) userId Int @unique totalTokens Int @default(0) usedTokens Int @default(0) resetDate DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id]) @@index([userId]) } model VideoMaterial { id Int @id @default(autoincrement()) userId Int? type String name String url String @db.Text thumbnail String? tags String? @db.Text category String? duration Int? size Int? width Int? height Int? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([userId, type]) @@index([type, category]) } model AudioRecord { id Int @id @default(autoincrement()) userId Int? audioId String @unique title String @default("未命名音频") text String? @db.LongText wordCount Int @default(0) voiceId String @default("cherry") voiceParams String? audioUrl String? @db.Text audioDuration Int @default(0) audioSize Int @default(0) status String @default("processing") errorMsg String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt bookId Int? @@index([userId]) @@index([audioId]) @@index([bookId]) } model PlatformAccount { id Int @id @default(autoincrement()) userId Int platform String nickname String @default("") avatar String @default("") cookies String @db.Text headers String? @db.Text isValid Boolean @default(true) expireTime DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([userId, platform]) @@index([userId]) @@index([platform]) } model PublishTask { id Int @id @default(autoincrement()) userId Int videoProjectId Int platform String title String @db.Text description String? @db.Text tags String? @db.Text coverUrl String? @db.Text videoUrl String? @db.Text status String @default("pending") errorMsg String? @db.Text publishedUrl String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([userId, platform]) @@index([status]) } model Draft { id Int @id @default(autoincrement()) userId Int type String title String? @db.Text content String? @db.LongText metadata String? @db.Text autoSavedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id]) @@index([userId, type]) @@index([userId, updatedAt]) } model Playlist { id Int @id @default(autoincrement()) userId Int name String description String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id]) items PlaylistItem[] @@index([userId]) } model PlaylistItem { id Int @id @default(autoincrement()) playlistId Int chapterId Int? audioId String? order Int @default(0) chapter BookChapter? @relation(fields: [chapterId], references: [id]) playlist Playlist @relation(fields: [playlistId], references: [id], onDelete: Cascade) @@index([playlistId, order]) @@index([chapterId], map: "PlaylistItem_chapterId_fkey") } model Feedback { id String @id @default(uuid()) type String title String @db.Text content String @db.Text contact String @db.Text screenshotUrls String @db.Text status String @default("submitted") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([type]) @@index([status]) @@index([createdAt]) }