schema.prisma 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  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. status String @default("pending") // pending, generating, completed, failed
  172. errorMsg String? @db.Text
  173. generatedAt DateTime?
  174. // 3种表现形式
  175. audioUrl String? @db.Text // 音频URL(由 content 生成)
  176. audioDuration Int @default(0) // 音频时长(秒)
  177. videoUrl String? @db.Text // 视频URL(由 content/audio 生成)
  178. videoDuration Int? // 视频时长(秒)
  179. // 公开状态(2026-04-14 新增)
  180. isPublic Boolean @default(false) // 是否公开到首页
  181. book Book @relation(fields: [bookId], references: [id], onDelete: Cascade)
  182. // parent BookChapter? @relation("ChapterChildren", fields: [parentId], references: [id], onDelete: Cascade)
  183. // children BookChapter[] @relation("ChapterChildren")
  184. videoProjects VideoProject[]
  185. playRecords PlayRecord[]
  186. comments Comment[]
  187. playlistItems PlaylistItem[]
  188. @@unique([bookId, parentId, level, number])
  189. @@index([bookId])
  190. @@index([bookId, parentId])
  191. @@index([bookId, level])
  192. }
  193. // ============ 视频生成模块 ============
  194. // 视频项目(关联到书籍的某个章节)
  195. model VideoProject {
  196. id Int @id @default(autoincrement())
  197. userId Int?
  198. title String // 项目标题
  199. description String? // 描述
  200. coverUrl String? // 封面图
  201. // 素材配置(JSON存储)
  202. configJson String? @db.LongText // VideoConfig JSON
  203. // 输出视频
  204. outputUrl String? // 生成后的视频URL
  205. duration Int? // 视频时长(秒)
  206. fileSize Int? // 文件大小(字节)
  207. // 关联:直接关联到章节
  208. bookId Int? // 关联的书籍ID(可选)
  209. chapterId Int? // 关联的章节ID(直接关联章节获取 content/audioUrl)
  210. status String @default("draft") // draft, processing, completed, failed
  211. progress Int @default(0) // 0-100
  212. errorMsg String? @db.Text
  213. createdAt DateTime @default(now())
  214. updatedAt DateTime @updatedAt
  215. book Book? @relation(fields: [bookId], references: [id])
  216. chapter BookChapter? @relation(fields: [chapterId], references: [id])
  217. @@index([userId, status])
  218. @@index([createdAt])
  219. }
  220. // ============ 搜索历史 ============
  221. model SearchHistory {
  222. id Int @id @default(autoincrement())
  223. userId Int // 用户ID,0表示未登录用户
  224. keyword String // 搜索关键词
  225. createdAt DateTime @default(now())
  226. @@index([userId, createdAt])
  227. @@index([userId])
  228. }
  229. // 热门搜索词
  230. model HotSearch {
  231. id Int @id @default(autoincrement())
  232. keyword String // 搜索关键词
  233. count Int @default(0) // 搜索次数
  234. sort Int @default(0) // 排序,数字越大越靠前
  235. createdAt DateTime @default(now())
  236. updatedAt DateTime @updatedAt
  237. @@index([sort])
  238. @@index([count])
  239. }
  240. // 签到记录
  241. model SignRecord {
  242. id Int @id @default(autoincrement())
  243. userId Int
  244. createdAt DateTime @default(now())
  245. user User @relation(fields: [userId], references: [id])
  246. @@unique([userId, createdAt])
  247. @@index([userId, createdAt])
  248. }
  249. // ============ 订阅套餐系统 ============
  250. // 套餐计划
  251. model SubscriptionPlan {
  252. id Int @id @default(autoincrement())
  253. name String // 套餐名称
  254. level Int @default(0) // 套餐等级:0免费 1基础 2专业 3旗舰
  255. priceMonthly Decimal @db.Decimal(10, 2) @default(0) // 月付价格
  256. priceYearly Decimal @db.Decimal(10, 2) @default(0) // 年付价格
  257. description String? @db.Text // 套餐描述
  258. features String? @db.Text // 功能列表(JSON数组)
  259. isRecommended Boolean @default(false) // 是否推荐
  260. isActive Boolean @default(true) // 是否上架
  261. sortOrder Int @default(0) // 排序
  262. // 限制配置
  263. dailyGenerations Int @default(3) // 每日生成次数,-1表示无限制
  264. perGenerationLimit Int @default(2000) // 单次生成限制字数
  265. monthlyTokens Int @default(10000) // 每月token配额,-1表示无限制
  266. monthlyMinutes Int @default(30) // 每月音频时长配额(分钟)
  267. yearlyTokens Int? // 每年token配额(旗舰版用)
  268. voiceOptions Int @default(5) // 可用音色数,-1表示全部
  269. audioQuality String @default("standard") // standard, high, lossless
  270. // 高级功能
  271. apiAccess Boolean @default(false) // API访问权限
  272. batchProcessing Boolean @default(false) // 批量处理权限
  273. teamManagement Boolean @default(false) // 团队管理权限
  274. overageEnabled Boolean @default(false) // 是否允许超出配额
  275. overagePrice Decimal @db.Decimal(10, 2) @default(0) // 超出配额价格(元/分钟)
  276. createdAt DateTime @default(now())
  277. updatedAt DateTime @updatedAt
  278. subscriptions Subscription[]
  279. orders Order[]
  280. @@index([level])
  281. @@index([isActive, sortOrder])
  282. }
  283. // 用户订阅记录
  284. model Subscription {
  285. id Int @id @default(autoincrement())
  286. userId Int
  287. planId Int
  288. startDate DateTime
  289. endDate DateTime
  290. status String @default("active") // active, expired, cancelled
  291. autoRenew Boolean @default(false) // 自动续费
  292. createdAt DateTime @default(now())
  293. updatedAt DateTime @updatedAt
  294. user User @relation(fields: [userId], references: [id])
  295. plan SubscriptionPlan @relation(fields: [planId], references: [id])
  296. @@index([userId, status])
  297. @@index([userId, endDate])
  298. }
  299. // Token使用记录
  300. model TokenUsage {
  301. id Int @id @default(autoincrement())
  302. userId Int
  303. type String // text_to_speech, api_call, batch_process
  304. amount Int @default(0) // 消耗token数量
  305. contentLength Int @default(0) // 内容长度(字数)
  306. orderId Int? // 关联的订单ID
  307. description String? @db.Text // 消耗描述
  308. createdAt DateTime @default(now())
  309. user User @relation(fields: [userId], references: [id])
  310. order Order? @relation(fields: [orderId], references: [id])
  311. @@index([userId, createdAt])
  312. @@index([userId, type])
  313. }
  314. // Token余额
  315. model TokenBalance {
  316. id Int @id @default(autoincrement())
  317. userId Int @unique
  318. totalTokens Int @default(0) // 总token配额
  319. usedTokens Int @default(0) // 已使用token
  320. resetDate DateTime? // 重置日期(月/年)
  321. createdAt DateTime @default(now())
  322. updatedAt DateTime @updatedAt
  323. user User @relation(fields: [userId], references: [id])
  324. @@index([userId])
  325. }
  326. // 视频素材库(保留作为独立素材)
  327. model VideoMaterial {
  328. id Int @id @default(autoincrement())
  329. userId Int? // null 表示公共素材
  330. type String // image, audio, template
  331. name String // 素材名称
  332. url String @db.Text // 素材URL
  333. thumbnail String? // 缩略图URL
  334. tags String? @db.Text // 标签(JSON数组)
  335. category String? // 分类:nature, abstract, business, music等
  336. duration Int? // 音频时长(秒)
  337. size Int? // 文件大小(字节)
  338. width Int? // 图片宽度
  339. height Int? // 图片高度
  340. createdAt DateTime @default(now())
  341. updatedAt DateTime @updatedAt
  342. @@index([userId, type])
  343. @@index([type, category])
  344. }
  345. // 音频生成记录
  346. model AudioRecord {
  347. id Int @id @default(autoincrement())
  348. userId Int?
  349. audioId String @unique // uuid,对应磁盘上的文件夹名
  350. title String @default("未命名音频")
  351. text String? @db.LongText
  352. wordCount Int @default(0)
  353. voiceId String @default("cherry")
  354. voiceParams String? // JSON: {speed, pitch, volume}
  355. audioUrl String? @db.Text
  356. audioDuration Int @default(0) // 秒
  357. audioSize Int @default(0) // 字节
  358. status String @default("processing") // processing, completed, failed
  359. errorMsg String? @db.Text
  360. createdAt DateTime @default(now())
  361. updatedAt DateTime @updatedAt
  362. @@index([userId])
  363. @@index([audioId])
  364. }
  365. // ============ 社交平台发布模块 ============
  366. // 平台账号(存储各平台的登录凭证)
  367. model PlatformAccount {
  368. id Int @id @default(autoincrement())
  369. userId Int
  370. platform String // douyin, kuaishou, bilibili
  371. nickname String @default("")
  372. avatar String @default("")
  373. cookies String @db.Text // 登录 Cookie
  374. headers String? @db.Text // 关键请求头(JSON)
  375. isValid Boolean @default(true)
  376. expireTime DateTime? // 凭证过期时间
  377. createdAt DateTime @default(now())
  378. updatedAt DateTime @updatedAt
  379. @@unique([userId, platform])
  380. @@index([userId])
  381. @@index([platform])
  382. }
  383. // 发布任务记录
  384. model PublishTask {
  385. id Int @id @default(autoincrement())
  386. userId Int
  387. videoProjectId Int
  388. platform String // douyin, kuaishou, bilibili
  389. title String @db.Text
  390. description String? @db.Text
  391. tags String? @db.Text // JSON 数组
  392. coverUrl String? @db.Text
  393. videoUrl String? @db.Text // 本地视频路径
  394. status String @default("pending") // pending, uploading, success, failed
  395. errorMsg String? @db.Text
  396. publishedUrl String? @db.Text // 发布后的作品链接
  397. createdAt DateTime @default(now())
  398. updatedAt DateTime @updatedAt
  399. @@index([userId, platform])
  400. @@index([status])
  401. }
  402. // ============ 草稿模块 ============
  403. model Draft {
  404. id Int @id @default(autoincrement())
  405. userId Int
  406. type String // content, outline, audio
  407. title String? @db.Text
  408. content String? @db.LongText
  409. metadata String? @db.Text // JSON 存储额外信息
  410. autoSavedAt DateTime?
  411. createdAt DateTime @default(now())
  412. updatedAt DateTime @updatedAt
  413. user User @relation(fields: [userId], references: [id])
  414. @@index([userId, type])
  415. @@index([userId, updatedAt])
  416. }
  417. // ============ 播放列表模块 ============
  418. model Playlist {
  419. id Int @id @default(autoincrement())
  420. userId Int
  421. name String // 播放列表名称
  422. description String? @db.Text
  423. createdAt DateTime @default(now())
  424. updatedAt DateTime @updatedAt
  425. user User @relation(fields: [userId], references: [id])
  426. items PlaylistItem[]
  427. @@index([userId])
  428. }
  429. model PlaylistItem {
  430. id Int @id @default(autoincrement())
  431. playlistId Int
  432. chapterId Int? // 关联的章节ID
  433. audioId String? // 关联的音频ID(独立音频)
  434. order Int @default(0) // 排序
  435. playlist Playlist @relation(fields: [playlistId], references: [id], onDelete: Cascade)
  436. chapter BookChapter? @relation(fields: [chapterId], references: [id])
  437. @@index([playlistId, order])
  438. }