| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- 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)
- costLimit Float @default(0)
- quotaReserved Float @default(0)
- subscriptionResetDate DateTime?
- comments Comment[]
- drafts Draft[]
- favorites Favorite[]
- notifications Notification[]
- orders Order[]
- playRecords PlayRecord[]
- playlists Playlist[]
- signRecords SignRecord[]
- subscriptions Subscription[]
- tokenBalance TokenBalance?
- tokenUsages TokenUsage[]
- preferences UserPreference?
- shareRecords ShareRecord[]
- invitesSent InviteRecord[] @relation("InviterRelation")
- invitesReceived InviteRecord[] @relation("InviteeRelation")
- @@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 Int
- title String
- content String @db.Text
- isRead Boolean @default(false)
- createdAt DateTime @default(now())
- user User @relation(fields: [userId], references: [id])
- @@index([userId])
- }
- 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("1000")
- 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
- voiceSpeed Float @default(1.0)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- failedStage String? @db.VarChar(30)
- genStage String @default("draft")
- bookAnalysis String? @db.Text
- autoGenerateContent Boolean @default(true)
- autoGenerateAudio Boolean @default(true)
- // 非错误的报告字段(与 errorMsg 分离,避免前端误判为失败)
- qualityReport String? @db.Text
- continuityReport String? @db.Text
- rewriteReport 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
- contentCheck String? @db.Text
- book Book @relation(fields: [bookId], references: [id], onDelete: Cascade)
- comments Comment[]
- playRecords PlayRecord[]
- playlistItems PlaylistItem[]
- ttsTasks TtsTask[]
- 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.000) @db.Decimal(10, 3)
- 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) // 总消耗
- packTokens Int @default(0) // 积分包购买的Token总数
- usedPackTokens Int @default(0) // 积分包已消耗的Token数
- 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?
- provider String? // TTS 供应商: bailian / edge / mock
- @@index([userId])
- @@index([audioId])
- @@index([bookId])
- }
- model TtsTask {
- id Int @id @default(autoincrement())
- taskType String @default("tts") @db.VarChar(20) // "tts" | "content"
- chapterId Int
- bookId Int?
- userId Int?
- contentHash String @db.VarChar(64)
- content String? @db.LongText
- status String @default("pending")
- audioUrl String? @db.Text
- audioDuration Int @default(0)
- errorMsg String? @db.Text
- retryCount Int @default(0)
- maxRetries Int @default(3)
- voiceId String @default("default")
- voiceSpeed Float @default(1.0)
- startedAt DateTime?
- completedAt DateTime?
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- chapter BookChapter @relation(fields: [chapterId], references: [id], onDelete: Cascade)
- @@index([taskType, status, createdAt])
- @@index([chapterId, contentHash])
- @@index([contentHash, chapterId])
- }
- model AiCallLog {
- id Int @id @default(autoincrement())
- callType String @db.VarChar(20) // llm_chat | llm_tools | tts_synthesize | tts_create | tts_poll
- provider String @db.VarChar(30) // minimax | bailian | volcengine
- model String @db.VarChar(50) // MiniMax-M2.7 | cosyvoice-v3-flash | qwen3-tts-instruct-flash
- userId Int?
- bookId Int?
- chapterId Int?
- textLen Int @default(0) // 输入文本长度(字符数)
- prompt String? @db.LongText // 提示词内容(LLM only)
- inputTokens Int @default(0) // 输入 Token 数
- outputTokens Int @default(0) // 输出 Token 数
- estimatedCost Float @default(0) // 估算成本(元)
- duration Int @default(0) // 耗时(ms)
- success Boolean @default(true)
- errorMsg String? @db.Text
- createdAt DateTime @default(now())
- @@index([callType, createdAt])
- @@index([provider, createdAt])
- @@index([userId, createdAt])
- @@index([bookId])
- @@index([createdAt])
- }
- 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])
- }
- model ShareRecord {
- id Int @id @default(autoincrement())
- userId Int
- audioId Int
- platform String
- shareCode String
- rewarded Boolean @default(false)
- createdAt DateTime @default(now())
- user User @relation(fields: [userId], references: [id])
- @@index([userId])
- @@index([shareCode])
- @@index([createdAt])
- }
- model InviteRecord {
- id Int @id @default(autoincrement())
- inviterId Int
- inviteeId Int @unique
- shareCode String?
- rewarded Boolean @default(false)
- rewardAmount Int @default(10000)
- createdAt DateTime @default(now())
- inviter User @relation("InviterRelation", fields: [inviterId], references: [id])
- invitee User @relation("InviteeRelation", fields: [inviteeId], references: [id])
- @@index([inviterId])
- @@index([shareCode])
- }
- model TokenPackPurchase {
- id Int @id @default(autoincrement())
- userId Int
- packMinutes Int
- quantity Int
- unitPrice Float
- totalAmount Float
- addedQuota Float
- createdAt DateTime @default(now())
- }
|