schema.prisma 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  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. // 音频时长配额(2026-04-12 新增)
  21. usedAudioMinutes Int @default(0) // 本月已使用音频分钟数
  22. subscriptionResetDate DateTime? // 配额重置日期
  23. orders Order[]
  24. playRecords PlayRecord[]
  25. preferences UserPreference?
  26. favorites Favorite[]
  27. comments Comment[]
  28. signRecords SignRecord[]
  29. subscriptions Subscription[]
  30. tokenUsages TokenUsage[]
  31. tokenBalance TokenBalance?
  32. drafts Draft[]
  33. playlists Playlist[]
  34. @@index([phone])
  35. @@index([openid])
  36. }
  37. // 订单
  38. model Order {
  39. id Int @id @default(autoincrement())
  40. userId Int
  41. orderNo String @unique
  42. planId Int? // 关联的套餐ID
  43. productType String // monthly 或 yearly
  44. amount Decimal @db.Decimal(10, 2)
  45. status String @default("pending") // pending, paid, failed, refunded
  46. paymentMethod String? // alipay, wechat, mock
  47. paymentId String? // 第三方支付单号
  48. paidAt DateTime?
  49. createdAt DateTime @default(now())
  50. updatedAt DateTime @updatedAt
  51. user User @relation(fields: [userId], references: [id])
  52. plan SubscriptionPlan? @relation(fields: [planId], references: [id])
  53. tokenUsages TokenUsage[]
  54. @@index([userId, createdAt])
  55. @@index([orderNo])
  56. @@index([status])
  57. }
  58. // 播放记录(播放的是章节的音频)
  59. model PlayRecord {
  60. id Int @id @default(autoincrement())
  61. userId Int
  62. chapterId Int // BookChapter.id(章节ID)
  63. progress Float @default(0) // 播放进度(秒)
  64. duration Float @default(0) // 总时长
  65. updatedAt DateTime @updatedAt
  66. createdAt DateTime @default(now())
  67. user User @relation(fields: [userId], references: [id])
  68. chapter BookChapter @relation(fields: [chapterId], references: [id])
  69. @@unique([userId, chapterId])
  70. @@index([userId])
  71. @@index([chapterId])
  72. }
  73. // 用户偏好
  74. model UserPreference {
  75. id Int @id @default(autoincrement())
  76. userId Int @unique
  77. playSpeed Float @default(1.0)
  78. quality String @default("standard") // standard, high
  79. theme String @default("light")
  80. // 新增字段 - 用户偏好设置增强
  81. defaultVoiceId String? @default("cherry") // 默认音色ID
  82. defaultVolume Int @default(50) // 默认音量 0-100
  83. autoPlayNext Boolean @default(true) // 自动播放下一首
  84. wifiOnlyDownload Boolean @default(false) // 仅WiFi下载
  85. updatedAt DateTime @updatedAt
  86. createdAt DateTime @default(now())
  87. user User @relation(fields: [userId], references: [id])
  88. }
  89. // 收藏(收藏的是书籍/专辑)
  90. model Favorite {
  91. id Int @id @default(autoincrement())
  92. userId Int
  93. bookId Int // Book.id(收藏的是整本书籍)
  94. createdAt DateTime @default(now())
  95. user User @relation(fields: [userId], references: [id])
  96. book Book @relation(fields: [bookId], references: [id], onDelete: Cascade)
  97. @@unique([userId, bookId])
  98. @@index([userId])
  99. @@index([bookId])
  100. }
  101. // 评论(评论的是章节)
  102. model Comment {
  103. id Int @id @default(autoincrement())
  104. userId Int
  105. chapterId Int // BookChapter.id
  106. content String @db.Text
  107. rating Int // 1-5星
  108. createdAt DateTime @default(now())
  109. user User @relation(fields: [userId], references: [id])
  110. chapter BookChapter @relation(fields: [chapterId], references: [id])
  111. @@index([chapterId])
  112. @@index([userId])
  113. }
  114. // 系统通知
  115. model Notification {
  116. id String @id @default(uuid())
  117. userId String
  118. title String
  119. content String @db.Text
  120. isRead Boolean @default(false)
  121. createdAt DateTime @default(now())
  122. }
  123. // ============ 书籍/章节(核心内容) ============
  124. // 书籍(专辑)
  125. model Book {
  126. id Int @id @default(autoincrement())
  127. userId Int?
  128. title String // 书名
  129. subtitle String? // 副标题
  130. description String @db.Text // 书籍描述/用户输入
  131. coverUrl String? // 封面图
  132. targetAudience String @default("通用") // 目标受众
  133. style String @default("专业严谨") // 写作风格
  134. bookScale String @default("标准教程") // 书籍规模:800, 2000, 5000, 小册子, 标准教程, 系统教材
  135. totalChapters Int @default(10) // 总章节数
  136. estimatedWords Int @default(0) // 预估总字数
  137. status String @default("draft") // draft, planning, generating, completed, failed, interrupted
  138. progress Int @default(0) // 生成进度 0-100
  139. isPublished Boolean @default(false) // 是否已发布
  140. // 大纲(JSON 存储)
  141. outlineJson String? @db.LongText // BookOutline JSON
  142. // 前言/后记
  143. foreword String? @db.Text
  144. afterword String? @db.Text
  145. // 错误信息
  146. errorMsg String? @db.Text
  147. createdAt DateTime @default(now())
  148. updatedAt DateTime @updatedAt
  149. chapters BookChapter[]
  150. videoProjects VideoProject[] // 关联的视频项目
  151. favorites Favorite[] // 收藏此书籍的用户
  152. @@index([userId, status])
  153. @@index([createdAt])
  154. }
  155. // 书籍章节(内容单元)
  156. // 一个章节 = 一份内容,有3种形式:content(文本)、audioUrl(音频)、videoUrl(视频)
  157. // level=1 表示章(顶级),level=2 表示节,level=3 表示小节
  158. // parentId = null 表示章(顶级),parentId = 章ID 表示节,parentId = 节ID 表示小节
  159. model BookChapter {
  160. id Int @id @default(autoincrement())
  161. bookId Int
  162. parentId Int? // 父节点ID(null = 章这一级)
  163. level Int @default(1) // 层级:1=章, 2=节, 3=小节
  164. number Int // 同级排序序号
  165. title String // 章节标题
  166. summary String? @db.Text // 章节概述
  167. keyPoints String? @db.Text // 核心知识点(JSON数组)
  168. estimatedWords Int @default(1000) // 预估字数
  169. content String? @db.LongText // 正文内容(原始文本)
  170. wordCount Int @default(0) // 实际字数
  171. // 大纲状态(title, summary, keyPoints)
  172. status String @default("pending") // pending, completed, failed
  173. // 内容生成状态(content, wordCount)
  174. contentStatus String? @db.VarChar(20) // null=未开始, pending=待生成, generating=生成中, completed=已完成, failed=失败
  175. contentError String? @db.Text // 内容生成错误信息
  176. generatedAt DateTime?
  177. // 3种表现形式
  178. audioUrl String? @db.Text // 音频URL(由 content 生成)
  179. audioDuration Int @default(0) // 音频时长(秒)
  180. videoUrl String? @db.Text // 视频URL(由 content/audio 生成)
  181. videoDuration Int? // 视频时长(秒)
  182. // 公开状态(2026-04-14 新增)
  183. isPublic Boolean @default(false) // 是否公开到首页
  184. book Book @relation(fields: [bookId], references: [id], onDelete: Cascade)
  185. // parent BookChapter? @relation("ChapterChildren", fields: [parentId], references: [id], onDelete: Cascade)
  186. // children BookChapter[] @relation("ChapterChildren")
  187. videoProjects VideoProject[]
  188. playRecords PlayRecord[]
  189. comments Comment[]
  190. playlistItems PlaylistItem[]
  191. @@unique([bookId, parentId, level, number])
  192. @@index([bookId])
  193. @@index([bookId, parentId])
  194. @@index([bookId, level])
  195. }
  196. // ============ 视频生成模块 ============
  197. // 视频项目(关联到书籍的某个章节)
  198. model VideoProject {
  199. id Int @id @default(autoincrement())
  200. userId Int?
  201. title String // 项目标题
  202. description String? // 描述
  203. coverUrl String? // 封面图
  204. // 素材配置(JSON存储)
  205. configJson String? @db.LongText // VideoConfig JSON
  206. // 输出视频
  207. outputUrl String? // 生成后的视频URL
  208. duration Int? // 视频时长(秒)
  209. fileSize Int? // 文件大小(字节)
  210. // 关联:直接关联到章节
  211. bookId Int? // 关联的书籍ID(可选)
  212. chapterId Int? // 关联的章节ID(直接关联章节获取 content/audioUrl)
  213. status String @default("draft") // draft, processing, completed, failed
  214. progress Int @default(0) // 0-100
  215. errorMsg String? @db.Text
  216. createdAt DateTime @default(now())
  217. updatedAt DateTime @updatedAt
  218. book Book? @relation(fields: [bookId], references: [id])
  219. chapter BookChapter? @relation(fields: [chapterId], references: [id])
  220. @@index([userId, status])
  221. @@index([createdAt])
  222. }
  223. // ============ 搜索历史 ============
  224. model SearchHistory {
  225. id Int @id @default(autoincrement())
  226. userId Int // 用户ID,0表示未登录用户
  227. keyword String // 搜索关键词
  228. createdAt DateTime @default(now())
  229. @@index([userId, createdAt])
  230. @@index([userId])
  231. }
  232. // 热门搜索词
  233. model HotSearch {
  234. id Int @id @default(autoincrement())
  235. keyword String // 搜索关键词
  236. count Int @default(0) // 搜索次数
  237. sort Int @default(0) // 排序,数字越大越靠前
  238. createdAt DateTime @default(now())
  239. updatedAt DateTime @updatedAt
  240. @@index([sort])
  241. @@index([count])
  242. }
  243. // 签到记录
  244. model SignRecord {
  245. id Int @id @default(autoincrement())
  246. userId Int
  247. createdAt DateTime @default(now())
  248. user User @relation(fields: [userId], references: [id])
  249. @@unique([userId, createdAt])
  250. @@index([userId, createdAt])
  251. }
  252. // ============ 订阅套餐系统 ============
  253. // 套餐计划
  254. model SubscriptionPlan {
  255. id Int @id @default(autoincrement())
  256. name String // 套餐名称
  257. level Int @default(0) // 套餐等级:0免费 1基础 2专业 3旗舰
  258. priceMonthly Decimal @db.Decimal(10, 2) @default(0) // 月付价格
  259. priceYearly Decimal @db.Decimal(10, 2) @default(0) // 年付价格
  260. description String? @db.Text // 套餐描述
  261. features String? @db.Text // 功能列表(JSON数组)
  262. isRecommended Boolean @default(false) // 是否推荐
  263. isActive Boolean @default(true) // 是否上架
  264. sortOrder Int @default(0) // 排序
  265. // 限制配置
  266. dailyGenerations Int @default(3) // 每日生成次数,-1表示无限制
  267. perGenerationLimit Int @default(2000) // 单次生成限制字数
  268. monthlyTokens Int @default(10000) // 每月token配额,-1表示无限制
  269. monthlyMinutes Int @default(30) // 每月音频时长配额(分钟)
  270. yearlyTokens Int? // 每年token配额(旗舰版用)
  271. voiceOptions Int @default(5) // 可用音色数,-1表示全部
  272. audioQuality String @default("standard") // standard, high, lossless
  273. // 高级功能
  274. apiAccess Boolean @default(false) // API访问权限
  275. batchProcessing Boolean @default(false) // 批量处理权限
  276. teamManagement Boolean @default(false) // 团队管理权限
  277. overageEnabled Boolean @default(false) // 是否允许超出配额
  278. overagePrice Decimal @db.Decimal(10, 2) @default(0) // 超出配额价格(元/分钟)
  279. createdAt DateTime @default(now())
  280. updatedAt DateTime @updatedAt
  281. subscriptions Subscription[]
  282. orders Order[]
  283. @@index([level])
  284. @@index([isActive, sortOrder])
  285. }
  286. // 用户订阅记录
  287. model Subscription {
  288. id Int @id @default(autoincrement())
  289. userId Int
  290. planId Int
  291. startDate DateTime
  292. endDate DateTime
  293. status String @default("active") // active, expired, cancelled
  294. autoRenew Boolean @default(false) // 自动续费
  295. createdAt DateTime @default(now())
  296. updatedAt DateTime @updatedAt
  297. user User @relation(fields: [userId], references: [id])
  298. plan SubscriptionPlan @relation(fields: [planId], references: [id])
  299. @@index([userId, status])
  300. @@index([userId, endDate])
  301. }
  302. // Token使用记录
  303. model TokenUsage {
  304. id Int @id @default(autoincrement())
  305. userId Int
  306. type String // text_to_speech, api_call, batch_process
  307. amount Int @default(0) // 消耗token数量
  308. contentLength Int @default(0) // 内容长度(字数)
  309. orderId Int? // 关联的订单ID
  310. description String? @db.Text // 消耗描述
  311. createdAt DateTime @default(now())
  312. user User @relation(fields: [userId], references: [id])
  313. order Order? @relation(fields: [orderId], references: [id])
  314. @@index([userId, createdAt])
  315. @@index([userId, type])
  316. }
  317. // Token余额
  318. model TokenBalance {
  319. id Int @id @default(autoincrement())
  320. userId Int @unique
  321. totalTokens Int @default(0) // 总token配额
  322. usedTokens Int @default(0) // 已使用token
  323. resetDate DateTime? // 重置日期(月/年)
  324. createdAt DateTime @default(now())
  325. updatedAt DateTime @updatedAt
  326. user User @relation(fields: [userId], references: [id])
  327. @@index([userId])
  328. }
  329. // 视频素材库(保留作为独立素材)
  330. model VideoMaterial {
  331. id Int @id @default(autoincrement())
  332. userId Int? // null 表示公共素材
  333. type String // image, audio, template
  334. name String // 素材名称
  335. url String @db.Text // 素材URL
  336. thumbnail String? // 缩略图URL
  337. tags String? @db.Text // 标签(JSON数组)
  338. category String? // 分类:nature, abstract, business, music等
  339. duration Int? // 音频时长(秒)
  340. size Int? // 文件大小(字节)
  341. width Int? // 图片宽度
  342. height Int? // 图片高度
  343. createdAt DateTime @default(now())
  344. updatedAt DateTime @updatedAt
  345. @@index([userId, type])
  346. @@index([type, category])
  347. }
  348. // 音频生成记录
  349. model AudioRecord {
  350. id Int @id @default(autoincrement())
  351. userId Int?
  352. audioId String @unique // uuid,对应磁盘上的文件夹名
  353. title String @default("未命名音频")
  354. text String? @db.LongText
  355. wordCount Int @default(0)
  356. voiceId String @default("cherry")
  357. voiceParams String? // JSON: {speed, pitch, volume}
  358. audioUrl String? @db.Text
  359. audioDuration Int @default(0) // 秒
  360. audioSize Int @default(0) // 字节
  361. status String @default("processing") // processing, completed, failed
  362. errorMsg String? @db.Text
  363. createdAt DateTime @default(now())
  364. updatedAt DateTime @updatedAt
  365. @@index([userId])
  366. @@index([audioId])
  367. }
  368. // ============ 社交平台发布模块 ============
  369. // 平台账号(存储各平台的登录凭证)
  370. model PlatformAccount {
  371. id Int @id @default(autoincrement())
  372. userId Int
  373. platform String // douyin, kuaishou, bilibili
  374. nickname String @default("")
  375. avatar String @default("")
  376. cookies String @db.Text // 登录 Cookie
  377. headers String? @db.Text // 关键请求头(JSON)
  378. isValid Boolean @default(true)
  379. expireTime DateTime? // 凭证过期时间
  380. createdAt DateTime @default(now())
  381. updatedAt DateTime @updatedAt
  382. @@unique([userId, platform])
  383. @@index([userId])
  384. @@index([platform])
  385. }
  386. // 发布任务记录
  387. model PublishTask {
  388. id Int @id @default(autoincrement())
  389. userId Int
  390. videoProjectId Int
  391. platform String // douyin, kuaishou, bilibili
  392. title String @db.Text
  393. description String? @db.Text
  394. tags String? @db.Text // JSON 数组
  395. coverUrl String? @db.Text
  396. videoUrl String? @db.Text // 本地视频路径
  397. status String @default("pending") // pending, uploading, success, failed
  398. errorMsg String? @db.Text
  399. publishedUrl String? @db.Text // 发布后的作品链接
  400. createdAt DateTime @default(now())
  401. updatedAt DateTime @updatedAt
  402. @@index([userId, platform])
  403. @@index([status])
  404. }
  405. // ============ 草稿模块 ============
  406. model Draft {
  407. id Int @id @default(autoincrement())
  408. userId Int
  409. type String // content, outline, audio
  410. title String? @db.Text
  411. content String? @db.LongText
  412. metadata String? @db.Text // JSON 存储额外信息
  413. autoSavedAt DateTime?
  414. createdAt DateTime @default(now())
  415. updatedAt DateTime @updatedAt
  416. user User @relation(fields: [userId], references: [id])
  417. @@index([userId, type])
  418. @@index([userId, updatedAt])
  419. }
  420. // ============ 播放列表模块 ============
  421. model Playlist {
  422. id Int @id @default(autoincrement())
  423. userId Int
  424. name String // 播放列表名称
  425. description String? @db.Text
  426. createdAt DateTime @default(now())
  427. updatedAt DateTime @updatedAt
  428. user User @relation(fields: [userId], references: [id])
  429. items PlaylistItem[]
  430. @@index([userId])
  431. }
  432. model PlaylistItem {
  433. id Int @id @default(autoincrement())
  434. playlistId Int
  435. chapterId Int? // 关联的章节ID
  436. audioId String? // 关联的音频ID(独立音频)
  437. order Int @default(0) // 排序
  438. playlist Playlist @relation(fields: [playlistId], references: [id], onDelete: Cascade)
  439. chapter BookChapter? @relation(fields: [chapterId], references: [id])
  440. @@index([playlistId, order])
  441. }