schema.prisma 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. generator client {
  2. provider = "prisma-client-js"
  3. }
  4. datasource db {
  5. provider = "mysql"
  6. url = env("DATABASE_URL")
  7. }
  8. model User {
  9. id Int @id @default(autoincrement())
  10. phone String? @unique
  11. openid String? @unique
  12. nickname String @default("用户")
  13. avatar String @default("")
  14. memberLevel Int @default(0)
  15. memberExpireAt DateTime?
  16. dailyUsage Int @default(0)
  17. lastUsageDate String @default("")
  18. createdAt DateTime @default(now())
  19. updatedAt DateTime @updatedAt
  20. usedAudioMinutes Int @default(0)
  21. costLimit Float @default(0)
  22. quotaReserved Float @default(0)
  23. subscriptionResetDate DateTime?
  24. comments Comment[]
  25. drafts Draft[]
  26. favorites Favorite[]
  27. notifications Notification[]
  28. orders Order[]
  29. playRecords PlayRecord[]
  30. playlists Playlist[]
  31. signRecords SignRecord[]
  32. subscriptions Subscription[]
  33. tokenBalance TokenBalance?
  34. tokenUsages TokenUsage[]
  35. preferences UserPreference?
  36. shareRecords ShareRecord[]
  37. invitesSent InviteRecord[] @relation("InviterRelation")
  38. invitesReceived InviteRecord[] @relation("InviteeRelation")
  39. @@index([phone])
  40. @@index([openid])
  41. }
  42. model Order {
  43. id Int @id @default(autoincrement())
  44. userId Int
  45. orderNo String @unique
  46. planId Int?
  47. productType String
  48. amount Decimal @db.Decimal(10, 2)
  49. status String @default("pending")
  50. paymentMethod String?
  51. paymentId String?
  52. paidAt DateTime?
  53. createdAt DateTime @default(now())
  54. updatedAt DateTime @updatedAt
  55. plan SubscriptionPlan? @relation(fields: [planId], references: [id])
  56. user User @relation(fields: [userId], references: [id])
  57. tokenUsages TokenUsage[]
  58. @@index([userId, createdAt])
  59. @@index([orderNo])
  60. @@index([status])
  61. @@index([planId], map: "Order_planId_fkey")
  62. }
  63. model PlayRecord {
  64. id Int @id @default(autoincrement())
  65. userId Int
  66. chapterId Int
  67. progress Float @default(0)
  68. duration Float @default(0)
  69. updatedAt DateTime @updatedAt
  70. createdAt DateTime @default(now())
  71. chapter BookChapter @relation(fields: [chapterId], references: [id])
  72. user User @relation(fields: [userId], references: [id])
  73. @@unique([userId, chapterId])
  74. @@index([userId])
  75. @@index([chapterId])
  76. }
  77. model UserPreference {
  78. id Int @id @default(autoincrement())
  79. userId Int @unique
  80. playSpeed Float @default(1)
  81. quality String @default("standard")
  82. theme String @default("light")
  83. defaultVoiceId String? @default("cherry")
  84. defaultVolume Int @default(50)
  85. autoPlayNext Boolean @default(true)
  86. wifiOnlyDownload Boolean @default(false)
  87. updatedAt DateTime @updatedAt
  88. createdAt DateTime @default(now())
  89. user User @relation(fields: [userId], references: [id])
  90. }
  91. model Favorite {
  92. id Int @id @default(autoincrement())
  93. userId Int
  94. bookId Int
  95. createdAt DateTime @default(now())
  96. book Book @relation(fields: [bookId], references: [id], onDelete: Cascade)
  97. user User @relation(fields: [userId], references: [id])
  98. @@unique([userId, bookId])
  99. @@index([userId])
  100. @@index([bookId])
  101. }
  102. model Comment {
  103. id Int @id @default(autoincrement())
  104. userId Int
  105. chapterId Int
  106. content String @db.Text
  107. rating Int
  108. createdAt DateTime @default(now())
  109. chapter BookChapter @relation(fields: [chapterId], references: [id])
  110. user User @relation(fields: [userId], references: [id])
  111. @@index([chapterId])
  112. @@index([userId])
  113. }
  114. model Notification {
  115. id String @id @default(uuid())
  116. userId Int
  117. title String
  118. content String @db.Text
  119. isRead Boolean @default(false)
  120. createdAt DateTime @default(now())
  121. user User @relation(fields: [userId], references: [id])
  122. @@index([userId])
  123. }
  124. model Book {
  125. id Int @id @default(autoincrement())
  126. userId Int?
  127. title String
  128. subtitle String?
  129. description String @db.Text
  130. coverUrl String?
  131. targetAudience String @default("通用")
  132. style String @default("专业严谨")
  133. bookScale String @default("1000")
  134. totalChapters Int @default(10)
  135. estimatedWords Int @default(0)
  136. progress Int @default(0)
  137. isPublished Boolean @default(false)
  138. outlineJson String? @db.LongText
  139. foreword String? @db.Text
  140. afterword String? @db.Text
  141. errorMsg String? @db.Text
  142. voiceSpeed Float @default(1.0)
  143. // 目标语言: BCP-47 风格 key,如 zh-CMN / zh-YUE / en-US / ja-JP
  144. // 老书(无此字段)按 zh-CMN 处理
  145. targetLanguage String @default("zh-CMN") @db.VarChar(10)
  146. createdAt DateTime @default(now())
  147. updatedAt DateTime @updatedAt
  148. failedStage String? @db.VarChar(30)
  149. genStage String @default("draft")
  150. bookAnalysis String? @db.Text
  151. autoGenerateContent Boolean @default(true)
  152. autoGenerateAudio Boolean @default(true)
  153. // 非错误的报告字段(与 errorMsg 分离,避免前端误判为失败)
  154. qualityReport String? @db.Text
  155. continuityReport String? @db.Text
  156. rewriteReport String? @db.Text
  157. chapters BookChapter[]
  158. favorites Favorite[]
  159. videoProjects VideoProject[]
  160. @@index([userId])
  161. @@index([createdAt])
  162. }
  163. model BookChapter {
  164. id Int @id @default(autoincrement())
  165. bookId Int
  166. parentId Int @default(0)
  167. level Int @default(1)
  168. number Int
  169. title String
  170. summary String? @db.Text
  171. keyPoints String? @db.Text
  172. estimatedWords Int @default(1000)
  173. content String? @db.LongText
  174. wordCount Int @default(0)
  175. contentError String? @db.Text
  176. generatedAt DateTime?
  177. audioUrl String? @db.Text
  178. audioDuration Int @default(0)
  179. videoUrl String? @db.Text
  180. videoDuration Int?
  181. isPublic Boolean @default(false)
  182. genStage String @default("idle")
  183. lrcLyrics String? @db.Text
  184. contentCheck String? @db.Text
  185. book Book @relation(fields: [bookId], references: [id], onDelete: Cascade)
  186. comments Comment[]
  187. playRecords PlayRecord[]
  188. playlistItems PlaylistItem[]
  189. ttsTasks TtsTask[]
  190. videoProjects VideoProject[]
  191. @@unique([bookId, parentId, level, number])
  192. @@index([bookId])
  193. @@index([bookId, parentId])
  194. @@index([bookId, level])
  195. }
  196. model VideoProject {
  197. id Int @id @default(autoincrement())
  198. userId Int?
  199. title String
  200. description String?
  201. coverUrl String?
  202. configJson String? @db.LongText
  203. outputUrl String?
  204. duration Int?
  205. fileSize Int?
  206. bookId Int?
  207. chapterId Int?
  208. status String @default("draft")
  209. progress Int @default(0)
  210. errorMsg String? @db.Text
  211. createdAt DateTime @default(now())
  212. updatedAt DateTime @updatedAt
  213. book Book? @relation(fields: [bookId], references: [id])
  214. chapter BookChapter? @relation(fields: [chapterId], references: [id])
  215. @@index([userId, status])
  216. @@index([createdAt])
  217. @@index([bookId], map: "VideoProject_bookId_fkey")
  218. @@index([chapterId], map: "VideoProject_chapterId_fkey")
  219. }
  220. model SearchHistory {
  221. id Int @id @default(autoincrement())
  222. userId Int
  223. keyword String
  224. createdAt DateTime @default(now())
  225. @@index([userId, createdAt])
  226. @@index([userId])
  227. }
  228. model HotSearch {
  229. id Int @id @default(autoincrement())
  230. keyword String
  231. count Int @default(0)
  232. sort Int @default(0)
  233. createdAt DateTime @default(now())
  234. updatedAt DateTime @updatedAt
  235. @@index([sort])
  236. @@index([count])
  237. }
  238. model SignRecord {
  239. id Int @id @default(autoincrement())
  240. userId Int
  241. createdAt DateTime @default(now())
  242. user User @relation(fields: [userId], references: [id])
  243. @@unique([userId, createdAt])
  244. @@index([userId, createdAt])
  245. }
  246. model SubscriptionPlan {
  247. id Int @id @default(autoincrement())
  248. name String
  249. level Int @default(0)
  250. priceMonthly Decimal @default(0.00) @db.Decimal(10, 2)
  251. priceYearly Decimal @default(0.00) @db.Decimal(10, 2)
  252. description String? @db.Text
  253. features String? @db.Text
  254. isRecommended Boolean @default(false)
  255. isActive Boolean @default(true)
  256. sortOrder Int @default(0)
  257. dailyGenerations Int @default(3)
  258. perGenerationLimit Int @default(2000)
  259. monthlyTokens Int @default(10000)
  260. monthlyMinutes Int @default(30)
  261. yearlyTokens Int?
  262. voiceOptions Int @default(5)
  263. audioQuality String @default("standard")
  264. apiAccess Boolean @default(false)
  265. batchProcessing Boolean @default(false)
  266. teamManagement Boolean @default(false)
  267. overageEnabled Boolean @default(false)
  268. overagePrice Decimal @default(0.000) @db.Decimal(10, 3)
  269. createdAt DateTime @default(now())
  270. updatedAt DateTime @updatedAt
  271. orders Order[]
  272. subscriptions Subscription[]
  273. @@index([level])
  274. @@index([isActive, sortOrder])
  275. }
  276. model Subscription {
  277. id Int @id @default(autoincrement())
  278. userId Int
  279. planId Int
  280. startDate DateTime
  281. endDate DateTime
  282. status String @default("active")
  283. autoRenew Boolean @default(false)
  284. createdAt DateTime @default(now())
  285. updatedAt DateTime @updatedAt
  286. plan SubscriptionPlan @relation(fields: [planId], references: [id])
  287. user User @relation(fields: [userId], references: [id])
  288. @@index([userId, status])
  289. @@index([userId, endDate])
  290. @@index([planId], map: "Subscription_planId_fkey")
  291. }
  292. model TokenUsage {
  293. id Int @id @default(autoincrement())
  294. userId Int
  295. type String
  296. amount Int @default(0)
  297. contentLength Int @default(0)
  298. orderId Int?
  299. description String? @db.Text
  300. createdAt DateTime @default(now())
  301. order Order? @relation(fields: [orderId], references: [id])
  302. user User @relation(fields: [userId], references: [id])
  303. @@index([userId, createdAt])
  304. @@index([userId, type])
  305. @@index([orderId], map: "TokenUsage_orderId_fkey")
  306. }
  307. model TokenBalance {
  308. id Int @id @default(autoincrement())
  309. userId Int @unique
  310. totalTokens Int @default(0)
  311. usedTokens Int @default(0) // 总消耗
  312. packTokens Int @default(0) // 积分包购买的Token总数
  313. usedPackTokens Int @default(0) // 积分包已消耗的Token数
  314. resetDate DateTime?
  315. createdAt DateTime @default(now())
  316. updatedAt DateTime @updatedAt
  317. user User @relation(fields: [userId], references: [id])
  318. @@index([userId])
  319. }
  320. model VideoMaterial {
  321. id Int @id @default(autoincrement())
  322. userId Int?
  323. type String
  324. name String
  325. url String @db.Text
  326. thumbnail String?
  327. tags String? @db.Text
  328. category String?
  329. duration Int?
  330. size Int?
  331. width Int?
  332. height Int?
  333. createdAt DateTime @default(now())
  334. updatedAt DateTime @updatedAt
  335. @@index([userId, type])
  336. @@index([type, category])
  337. }
  338. model AudioRecord {
  339. id Int @id @default(autoincrement())
  340. userId Int?
  341. audioId String @unique
  342. title String @default("未命名音频")
  343. text String? @db.LongText
  344. wordCount Int @default(0)
  345. voiceId String @default("cherry")
  346. voiceParams String?
  347. audioUrl String? @db.Text
  348. audioDuration Int @default(0)
  349. audioSize Int @default(0)
  350. status String @default("processing")
  351. errorMsg String? @db.Text
  352. createdAt DateTime @default(now())
  353. updatedAt DateTime @updatedAt
  354. bookId Int?
  355. provider String? // TTS 供应商: bailian / edge / mock
  356. // 用户选的语言(B 系列"语言选择器"),诊断时一目了然
  357. targetLanguage String? @db.VarChar(10)
  358. // 入队时锁定的 vendor+model,避免 models.json 变更导致音色漂移
  359. preferredVendor String? @db.VarChar(20)
  360. preferredModel String? @db.VarChar(50)
  361. @@index([userId])
  362. @@index([audioId])
  363. @@index([bookId])
  364. }
  365. model TtsTask {
  366. id Int @id @default(autoincrement())
  367. taskType String @default("tts") @db.VarChar(20) // "tts" | "content"
  368. chapterId Int
  369. bookId Int?
  370. userId Int?
  371. contentHash String @db.VarChar(64)
  372. content String? @db.LongText
  373. status String @default("pending")
  374. audioUrl String? @db.Text
  375. audioDuration Int @default(0)
  376. errorMsg String? @db.Text
  377. retryCount Int @default(0)
  378. maxRetries Int @default(3)
  379. voiceId String @default("default")
  380. voiceSpeed Float @default(1.0)
  381. // 入队时锁定的语言/模型(由 pickTtsVendor() 路由结果固化)
  382. targetLanguage String? @db.VarChar(10)
  383. preferredVendor String? @db.VarChar(20)
  384. preferredModel String? @db.VarChar(50)
  385. startedAt DateTime?
  386. completedAt DateTime?
  387. createdAt DateTime @default(now())
  388. updatedAt DateTime @updatedAt
  389. chapter BookChapter @relation(fields: [chapterId], references: [id], onDelete: Cascade)
  390. @@index([taskType, status, createdAt])
  391. @@index([chapterId, contentHash])
  392. @@index([contentHash, chapterId])
  393. }
  394. model AiCallLog {
  395. id Int @id @default(autoincrement())
  396. callType String @db.VarChar(20) // llm_chat | llm_tools | tts_synthesize | tts_create | tts_poll
  397. provider String @db.VarChar(30) // minimax | bailian | volcengine
  398. model String @db.VarChar(50) // MiniMax-M2.7 | cosyvoice-v3-flash | qwen3-tts-instruct-flash
  399. userId Int?
  400. bookId Int?
  401. chapterId Int?
  402. textLen Int @default(0) // 输入文本长度(字符数)
  403. prompt String? @db.LongText // 提示词内容(LLM only)
  404. inputTokens Int @default(0) // 输入 Token 数
  405. outputTokens Int @default(0) // 输出 Token 数
  406. estimatedCost Float @default(0) // 估算成本(元)
  407. duration Int @default(0) // 耗时(ms)
  408. success Boolean @default(true)
  409. errorMsg String? @db.Text
  410. createdAt DateTime @default(now())
  411. @@index([callType, createdAt])
  412. @@index([provider, createdAt])
  413. @@index([userId, createdAt])
  414. @@index([bookId])
  415. @@index([createdAt])
  416. }
  417. model PlatformAccount {
  418. id Int @id @default(autoincrement())
  419. userId Int
  420. platform String
  421. nickname String @default("")
  422. avatar String @default("")
  423. cookies String @db.Text
  424. headers String? @db.Text
  425. isValid Boolean @default(true)
  426. expireTime DateTime?
  427. createdAt DateTime @default(now())
  428. updatedAt DateTime @updatedAt
  429. @@unique([userId, platform])
  430. @@index([userId])
  431. @@index([platform])
  432. }
  433. model PublishTask {
  434. id Int @id @default(autoincrement())
  435. userId Int
  436. videoProjectId Int
  437. platform String
  438. title String @db.Text
  439. description String? @db.Text
  440. tags String? @db.Text
  441. coverUrl String? @db.Text
  442. videoUrl String? @db.Text
  443. status String @default("pending")
  444. errorMsg String? @db.Text
  445. publishedUrl String? @db.Text
  446. createdAt DateTime @default(now())
  447. updatedAt DateTime @updatedAt
  448. @@index([userId, platform])
  449. @@index([status])
  450. }
  451. model Draft {
  452. id Int @id @default(autoincrement())
  453. userId Int
  454. type String
  455. title String? @db.Text
  456. content String? @db.LongText
  457. metadata String? @db.Text
  458. autoSavedAt DateTime?
  459. createdAt DateTime @default(now())
  460. updatedAt DateTime @updatedAt
  461. user User @relation(fields: [userId], references: [id])
  462. @@index([userId, type])
  463. @@index([userId, updatedAt])
  464. }
  465. model Playlist {
  466. id Int @id @default(autoincrement())
  467. userId Int
  468. name String
  469. description String? @db.Text
  470. createdAt DateTime @default(now())
  471. updatedAt DateTime @updatedAt
  472. user User @relation(fields: [userId], references: [id])
  473. items PlaylistItem[]
  474. @@index([userId])
  475. }
  476. model PlaylistItem {
  477. id Int @id @default(autoincrement())
  478. playlistId Int
  479. chapterId Int?
  480. audioId String?
  481. order Int @default(0)
  482. chapter BookChapter? @relation(fields: [chapterId], references: [id])
  483. playlist Playlist @relation(fields: [playlistId], references: [id], onDelete: Cascade)
  484. @@index([playlistId, order])
  485. @@index([chapterId], map: "PlaylistItem_chapterId_fkey")
  486. }
  487. model Feedback {
  488. id String @id @default(uuid())
  489. type String
  490. title String @db.Text
  491. content String @db.Text
  492. contact String @db.Text
  493. screenshotUrls String @db.Text
  494. status String @default("submitted")
  495. createdAt DateTime @default(now())
  496. updatedAt DateTime @updatedAt
  497. @@index([type])
  498. @@index([status])
  499. @@index([createdAt])
  500. }
  501. model ShareRecord {
  502. id Int @id @default(autoincrement())
  503. userId Int
  504. audioId Int
  505. platform String
  506. shareCode String
  507. rewarded Boolean @default(false)
  508. createdAt DateTime @default(now())
  509. user User @relation(fields: [userId], references: [id])
  510. @@index([userId])
  511. @@index([shareCode])
  512. @@index([createdAt])
  513. }
  514. model InviteRecord {
  515. id Int @id @default(autoincrement())
  516. inviterId Int
  517. inviteeId Int @unique
  518. shareCode String?
  519. rewarded Boolean @default(false)
  520. rewardAmount Int @default(10000)
  521. createdAt DateTime @default(now())
  522. inviter User @relation("InviterRelation", fields: [inviterId], references: [id])
  523. invitee User @relation("InviteeRelation", fields: [inviteeId], references: [id])
  524. @@index([inviterId])
  525. @@index([shareCode])
  526. }
  527. model TokenPackPurchase {
  528. id Int @id @default(autoincrement())
  529. userId Int
  530. packMinutes Int
  531. quantity Int
  532. unitPrice Float
  533. totalAmount Float
  534. addedQuota Float
  535. createdAt DateTime @default(now())
  536. }