schema.prisma 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  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. audioSource String? @db.VarChar(20) // 'full' | 'on_demand' | null — 区分完整异步生成 vs 按需同步合成
  180. videoUrl String? @db.Text
  181. videoDuration Int?
  182. isPublic Boolean @default(false)
  183. genStage String @default("idle")
  184. lrcLyrics String? @db.Text
  185. contentCheck String? @db.Text
  186. book Book @relation(fields: [bookId], references: [id], onDelete: Cascade)
  187. comments Comment[]
  188. playRecords PlayRecord[]
  189. playlistItems PlaylistItem[]
  190. ttsTasks TtsTask[]
  191. videoProjects VideoProject[]
  192. @@unique([bookId, parentId, level, number])
  193. @@index([bookId])
  194. @@index([bookId, parentId])
  195. @@index([bookId, level])
  196. }
  197. model VideoProject {
  198. id Int @id @default(autoincrement())
  199. userId Int?
  200. title String
  201. description String?
  202. coverUrl String?
  203. configJson String? @db.LongText
  204. outputUrl String?
  205. duration Int?
  206. fileSize Int?
  207. bookId Int?
  208. chapterId Int?
  209. status String @default("draft")
  210. progress Int @default(0)
  211. errorMsg String? @db.Text
  212. createdAt DateTime @default(now())
  213. updatedAt DateTime @updatedAt
  214. book Book? @relation(fields: [bookId], references: [id])
  215. chapter BookChapter? @relation(fields: [chapterId], references: [id])
  216. @@index([userId, status])
  217. @@index([createdAt])
  218. @@index([bookId], map: "VideoProject_bookId_fkey")
  219. @@index([chapterId], map: "VideoProject_chapterId_fkey")
  220. }
  221. model SearchHistory {
  222. id Int @id @default(autoincrement())
  223. userId Int
  224. keyword String
  225. createdAt DateTime @default(now())
  226. @@index([userId, createdAt])
  227. @@index([userId])
  228. }
  229. model HotSearch {
  230. id Int @id @default(autoincrement())
  231. keyword String
  232. count Int @default(0)
  233. sort Int @default(0)
  234. createdAt DateTime @default(now())
  235. updatedAt DateTime @updatedAt
  236. @@index([sort])
  237. @@index([count])
  238. }
  239. model SignRecord {
  240. id Int @id @default(autoincrement())
  241. userId Int
  242. createdAt DateTime @default(now())
  243. user User @relation(fields: [userId], references: [id])
  244. @@unique([userId, createdAt])
  245. @@index([userId, createdAt])
  246. }
  247. model SubscriptionPlan {
  248. id Int @id @default(autoincrement())
  249. name String
  250. level Int @default(0)
  251. priceMonthly Decimal @default(0.00) @db.Decimal(10, 2)
  252. priceYearly Decimal @default(0.00) @db.Decimal(10, 2)
  253. description String? @db.Text
  254. features String? @db.Text
  255. isRecommended Boolean @default(false)
  256. isActive Boolean @default(true)
  257. sortOrder Int @default(0)
  258. dailyGenerations Int @default(3)
  259. perGenerationLimit Int @default(2000)
  260. monthlyTokens Int @default(10000)
  261. monthlyMinutes Int @default(30)
  262. yearlyTokens Int?
  263. voiceOptions Int @default(5)
  264. audioQuality String @default("standard")
  265. apiAccess Boolean @default(false)
  266. batchProcessing Boolean @default(false)
  267. teamManagement Boolean @default(false)
  268. overageEnabled Boolean @default(false)
  269. overagePrice Decimal @default(0.000) @db.Decimal(10, 3)
  270. createdAt DateTime @default(now())
  271. updatedAt DateTime @updatedAt
  272. orders Order[]
  273. subscriptions Subscription[]
  274. @@index([level])
  275. @@index([isActive, sortOrder])
  276. }
  277. model Subscription {
  278. id Int @id @default(autoincrement())
  279. userId Int
  280. planId Int
  281. startDate DateTime
  282. endDate DateTime
  283. status String @default("active")
  284. autoRenew Boolean @default(false)
  285. createdAt DateTime @default(now())
  286. updatedAt DateTime @updatedAt
  287. plan SubscriptionPlan @relation(fields: [planId], references: [id])
  288. user User @relation(fields: [userId], references: [id])
  289. @@index([userId, status])
  290. @@index([userId, endDate])
  291. @@index([planId], map: "Subscription_planId_fkey")
  292. }
  293. model TokenUsage {
  294. id Int @id @default(autoincrement())
  295. userId Int
  296. type String
  297. amount Int @default(0)
  298. contentLength Int @default(0)
  299. orderId Int?
  300. description String? @db.Text
  301. createdAt DateTime @default(now())
  302. order Order? @relation(fields: [orderId], references: [id])
  303. user User @relation(fields: [userId], references: [id])
  304. @@index([userId, createdAt])
  305. @@index([userId, type])
  306. @@index([orderId], map: "TokenUsage_orderId_fkey")
  307. }
  308. model TokenBalance {
  309. id Int @id @default(autoincrement())
  310. userId Int @unique
  311. totalTokens Int @default(0)
  312. usedTokens Int @default(0) // 总消耗
  313. packTokens Int @default(0) // 积分包购买的Token总数
  314. usedPackTokens Int @default(0) // 积分包已消耗的Token数
  315. resetDate DateTime?
  316. createdAt DateTime @default(now())
  317. updatedAt DateTime @updatedAt
  318. user User @relation(fields: [userId], references: [id])
  319. @@index([userId])
  320. }
  321. model VideoMaterial {
  322. id Int @id @default(autoincrement())
  323. userId Int?
  324. type String
  325. name String
  326. url String @db.Text
  327. thumbnail String?
  328. tags String? @db.Text
  329. category String?
  330. duration Int?
  331. size Int?
  332. width Int?
  333. height Int?
  334. createdAt DateTime @default(now())
  335. updatedAt DateTime @updatedAt
  336. @@index([userId, type])
  337. @@index([type, category])
  338. }
  339. model AudioRecord {
  340. id Int @id @default(autoincrement())
  341. userId Int?
  342. audioId String @unique
  343. title String @default("未命名音频")
  344. text String? @db.LongText
  345. wordCount Int @default(0)
  346. voiceId String @default("cherry")
  347. voiceParams String?
  348. audioUrl String? @db.Text
  349. audioDuration Int @default(0)
  350. audioSize Int @default(0)
  351. status String @default("processing")
  352. errorMsg String? @db.Text
  353. createdAt DateTime @default(now())
  354. updatedAt DateTime @updatedAt
  355. bookId Int?
  356. provider String? // TTS 供应商: bailian / edge / mock
  357. // 用户选的语言(B 系列"语言选择器"),诊断时一目了然
  358. targetLanguage String? @db.VarChar(10)
  359. // 入队时锁定的 vendor+model,避免 models.json 变更导致音色漂移
  360. preferredVendor String? @db.VarChar(20)
  361. preferredModel String? @db.VarChar(50)
  362. @@index([userId])
  363. @@index([audioId])
  364. @@index([bookId])
  365. }
  366. model TtsTask {
  367. id Int @id @default(autoincrement())
  368. taskType String @default("tts") @db.VarChar(20) // "tts" | "content"
  369. chapterId Int
  370. bookId Int?
  371. userId Int?
  372. contentHash String @db.VarChar(64)
  373. content String? @db.LongText
  374. status String @default("pending")
  375. audioUrl String? @db.Text
  376. audioDuration Int @default(0)
  377. errorMsg String? @db.Text
  378. retryCount Int @default(0)
  379. maxRetries Int @default(3)
  380. voiceId String @default("default")
  381. voiceSpeed Float @default(1.0)
  382. // 入队时锁定的语言/模型(由 pickTtsVendor() 路由结果固化)
  383. targetLanguage String? @db.VarChar(10)
  384. preferredVendor String? @db.VarChar(20)
  385. preferredModel String? @db.VarChar(50)
  386. startedAt DateTime?
  387. completedAt DateTime?
  388. createdAt DateTime @default(now())
  389. updatedAt DateTime @updatedAt
  390. chapter BookChapter @relation(fields: [chapterId], references: [id], onDelete: Cascade)
  391. @@index([taskType, status, createdAt])
  392. @@index([chapterId, contentHash])
  393. @@index([contentHash, chapterId])
  394. }
  395. model AiCallLog {
  396. id Int @id @default(autoincrement())
  397. callType String @db.VarChar(20) // llm_chat | llm_tools | tts_synthesize | tts_create | tts_poll
  398. provider String @db.VarChar(30) // minimax | bailian | volcengine
  399. model String @db.VarChar(50) // MiniMax-M2.7 | cosyvoice-v3-flash | qwen3-tts-instruct-flash
  400. userId Int?
  401. bookId Int?
  402. chapterId Int?
  403. textLen Int @default(0) // 输入文本长度(字符数)
  404. prompt String? @db.LongText // 提示词内容(LLM only)
  405. inputTokens Int @default(0) // 输入 Token 数
  406. outputTokens Int @default(0) // 输出 Token 数
  407. estimatedCost Float @default(0) // 估算成本(元)
  408. duration Int @default(0) // 耗时(ms)
  409. success Boolean @default(true)
  410. errorMsg String? @db.Text
  411. createdAt DateTime @default(now())
  412. @@index([callType, createdAt])
  413. @@index([provider, createdAt])
  414. @@index([userId, createdAt])
  415. @@index([bookId])
  416. @@index([createdAt])
  417. }
  418. model PlatformAccount {
  419. id Int @id @default(autoincrement())
  420. userId Int
  421. platform String
  422. nickname String @default("")
  423. avatar String @default("")
  424. cookies String @db.Text
  425. headers String? @db.Text
  426. isValid Boolean @default(true)
  427. expireTime DateTime?
  428. createdAt DateTime @default(now())
  429. updatedAt DateTime @updatedAt
  430. @@unique([userId, platform])
  431. @@index([userId])
  432. @@index([platform])
  433. }
  434. model PublishTask {
  435. id Int @id @default(autoincrement())
  436. userId Int
  437. videoProjectId Int
  438. platform String
  439. title String @db.Text
  440. description String? @db.Text
  441. tags String? @db.Text
  442. coverUrl String? @db.Text
  443. videoUrl String? @db.Text
  444. status String @default("pending")
  445. errorMsg String? @db.Text
  446. publishedUrl String? @db.Text
  447. createdAt DateTime @default(now())
  448. updatedAt DateTime @updatedAt
  449. @@index([userId, platform])
  450. @@index([status])
  451. }
  452. model Draft {
  453. id Int @id @default(autoincrement())
  454. userId Int
  455. type String
  456. title String? @db.Text
  457. content String? @db.LongText
  458. metadata String? @db.Text
  459. autoSavedAt DateTime?
  460. createdAt DateTime @default(now())
  461. updatedAt DateTime @updatedAt
  462. user User @relation(fields: [userId], references: [id])
  463. @@index([userId, type])
  464. @@index([userId, updatedAt])
  465. }
  466. model Playlist {
  467. id Int @id @default(autoincrement())
  468. userId Int
  469. name String
  470. description String? @db.Text
  471. createdAt DateTime @default(now())
  472. updatedAt DateTime @updatedAt
  473. user User @relation(fields: [userId], references: [id])
  474. items PlaylistItem[]
  475. @@index([userId])
  476. }
  477. model PlaylistItem {
  478. id Int @id @default(autoincrement())
  479. playlistId Int
  480. chapterId Int?
  481. audioId String?
  482. order Int @default(0)
  483. chapter BookChapter? @relation(fields: [chapterId], references: [id])
  484. playlist Playlist @relation(fields: [playlistId], references: [id], onDelete: Cascade)
  485. @@index([playlistId, order])
  486. @@index([chapterId], map: "PlaylistItem_chapterId_fkey")
  487. }
  488. model Feedback {
  489. id String @id @default(uuid())
  490. type String
  491. title String @db.Text
  492. content String @db.Text
  493. contact String @db.Text
  494. screenshotUrls String @db.Text
  495. status String @default("submitted")
  496. createdAt DateTime @default(now())
  497. updatedAt DateTime @updatedAt
  498. @@index([type])
  499. @@index([status])
  500. @@index([createdAt])
  501. }
  502. model ShareRecord {
  503. id Int @id @default(autoincrement())
  504. userId Int
  505. audioId Int
  506. platform String
  507. shareCode String
  508. rewarded Boolean @default(false)
  509. createdAt DateTime @default(now())
  510. user User @relation(fields: [userId], references: [id])
  511. @@index([userId])
  512. @@index([shareCode])
  513. @@index([createdAt])
  514. }
  515. model InviteRecord {
  516. id Int @id @default(autoincrement())
  517. inviterId Int
  518. inviteeId Int @unique
  519. shareCode String?
  520. rewarded Boolean @default(false)
  521. rewardAmount Int @default(10000)
  522. createdAt DateTime @default(now())
  523. inviter User @relation("InviterRelation", fields: [inviterId], references: [id])
  524. invitee User @relation("InviteeRelation", fields: [inviteeId], references: [id])
  525. @@index([inviterId])
  526. @@index([shareCode])
  527. }
  528. model TokenPackPurchase {
  529. id Int @id @default(autoincrement())
  530. userId Int
  531. packMinutes Int
  532. quantity Int
  533. unitPrice Float
  534. totalAmount Float
  535. addedQuota Float
  536. createdAt DateTime @default(now())
  537. }