schema.prisma 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  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. @@index([phone])
  33. @@index([openid])
  34. }
  35. // 订单
  36. model Order {
  37. id Int @id @default(autoincrement())
  38. userId Int
  39. orderNo String @unique
  40. planId Int? // 关联的套餐ID
  41. productType String // monthly 或 yearly
  42. amount Decimal @db.Decimal(10, 2)
  43. status String @default("pending") // pending, paid, failed, refunded
  44. paymentMethod String? // alipay, wechat, mock
  45. paymentId String? // 第三方支付单号
  46. paidAt DateTime?
  47. createdAt DateTime @default(now())
  48. updatedAt DateTime @updatedAt
  49. user User @relation(fields: [userId], references: [id])
  50. plan SubscriptionPlan? @relation(fields: [planId], references: [id])
  51. tokenUsages TokenUsage[]
  52. @@index([userId, createdAt])
  53. @@index([orderNo])
  54. @@index([status])
  55. }
  56. // 播放记录(播放的是章节的音频)
  57. model PlayRecord {
  58. id Int @id @default(autoincrement())
  59. userId Int
  60. chapterId Int // BookChapter.id(章节ID)
  61. progress Float @default(0) // 播放进度(秒)
  62. duration Float @default(0) // 总时长
  63. updatedAt DateTime @updatedAt
  64. createdAt DateTime @default(now())
  65. user User @relation(fields: [userId], references: [id])
  66. chapter BookChapter @relation(fields: [chapterId], references: [id])
  67. @@unique([userId, chapterId])
  68. @@index([userId])
  69. @@index([chapterId])
  70. }
  71. // 用户偏好
  72. model UserPreference {
  73. id Int @id @default(autoincrement())
  74. userId Int @unique
  75. playSpeed Float @default(1.0)
  76. quality String @default("standard") // standard, high
  77. theme String @default("light")
  78. updatedAt DateTime @updatedAt
  79. createdAt DateTime @default(now())
  80. user User @relation(fields: [userId], references: [id])
  81. }
  82. // 收藏(收藏的是书籍/专辑)
  83. model Favorite {
  84. id Int @id @default(autoincrement())
  85. userId Int
  86. bookId Int // Book.id(收藏的是整本书籍)
  87. createdAt DateTime @default(now())
  88. user User @relation(fields: [userId], references: [id])
  89. book Book @relation(fields: [bookId], references: [id], onDelete: Cascade)
  90. @@unique([userId, bookId])
  91. @@index([userId])
  92. @@index([bookId])
  93. }
  94. // 分类
  95. model Category {
  96. id Int @id @default(autoincrement())
  97. name String
  98. icon String @default("")
  99. sort Int @default(0)
  100. createdAt DateTime @default(now())
  101. }
  102. // 评论(评论的是章节)
  103. model Comment {
  104. id Int @id @default(autoincrement())
  105. userId Int
  106. chapterId Int // BookChapter.id
  107. content String @db.Text
  108. rating Int // 1-5星
  109. createdAt DateTime @default(now())
  110. user User @relation(fields: [userId], references: [id])
  111. chapter BookChapter @relation(fields: [chapterId], references: [id])
  112. @@index([chapterId])
  113. @@index([userId])
  114. }
  115. // 系统通知
  116. model Notification {
  117. id String @id @default(uuid())
  118. userId String
  119. title String
  120. content String @db.Text
  121. isRead Boolean @default(false)
  122. createdAt DateTime @default(now())
  123. }
  124. // ============ 模板管理 ============
  125. model Template {
  126. id Int @id @default(autoincrement())
  127. name String
  128. category String // 广告营销/知识付费/短视频/企业宣传/日常生活/新闻资讯
  129. content String @db.Text // 模板内容
  130. description String? @db.Text // 模板描述
  131. createdAt DateTime @default(now())
  132. updatedAt DateTime @updatedAt
  133. @@index([category])
  134. }
  135. // ============ 书籍/章节(核心内容) ============
  136. // 书籍(专辑)
  137. model Book {
  138. id Int @id @default(autoincrement())
  139. userId Int?
  140. title String // 书名
  141. subtitle String? // 副标题
  142. description String @db.Text // 书籍描述/用户输入
  143. coverUrl String? // 封面图
  144. targetAudience String @default("通用") // 目标受众
  145. style String @default("专业严谨") // 写作风格
  146. totalChapters Int @default(10) // 总章节数
  147. estimatedWords Int @default(0) // 预估总字数
  148. status String @default("draft") // draft, planning, generating, completed, failed
  149. progress Int @default(0) // 生成进度 0-100
  150. isPublished Boolean @default(false) // 是否已发布
  151. // 大纲(JSON 存储)
  152. outlineJson String? @db.LongText // BookOutline JSON
  153. // 前言/后记
  154. foreword String? @db.Text
  155. afterword String? @db.Text
  156. // 错误信息
  157. errorMsg String? @db.Text
  158. createdAt DateTime @default(now())
  159. updatedAt DateTime @updatedAt
  160. chapters BookChapter[]
  161. videoProjects VideoProject[] // 关联的视频项目
  162. favorites Favorite[] // 收藏此书籍的用户
  163. @@index([userId, status])
  164. @@index([createdAt])
  165. }
  166. // 书籍章节(内容单元)
  167. // 一个章节 = 一份内容,有3种形式:content(文本)、audioUrl(音频)、videoUrl(视频)
  168. model BookChapter {
  169. id Int @id @default(autoincrement())
  170. bookId Int
  171. number Int // 章节序号
  172. title String // 章节标题
  173. summary String? @db.Text // 章节概述
  174. keyPoints String? @db.Text // 核心知识点(JSON数组)
  175. estimatedWords Int @default(1000) // 预估字数
  176. content String? @db.LongText // 正文内容(原始文本)
  177. wordCount Int @default(0) // 实际字数
  178. status String @default("pending") // pending, generating, completed, failed
  179. errorMsg String? @db.Text
  180. generatedAt DateTime?
  181. // 3种表现形式
  182. audioUrl String? @db.Text // 音频URL(由 content 生成)
  183. audioDuration Int @default(0) // 音频时长(秒)
  184. videoUrl String? @db.Text // 视频URL(由 content/audio 生成)
  185. videoDuration Int? // 视频时长(秒)
  186. book Book @relation(fields: [bookId], references: [id], onDelete: Cascade)
  187. videoProjects VideoProject[]
  188. playRecords PlayRecord[]
  189. comments Comment[]
  190. @@unique([bookId, number])
  191. @@index([bookId])
  192. }
  193. // ============ 学习路径模块 ============
  194. // 学习路径任务
  195. model LearningPath {
  196. id Int @id @default(autoincrement())
  197. userId Int?
  198. topic String @db.Text // 用户输入的主题
  199. status String @default("pending") // pending, generating, completed, failed
  200. progress Int @default(0) // 0-100 进度百分比
  201. totalCount Int @default(0) // 总内容块数
  202. completedCount Int @default(0) // 已完成内容块数
  203. errorMsg String? @db.Text // 错误信息
  204. // 生成模式: progressive(渐进确认) | multi-agent(多Agent并行)
  205. generationMode String @default("progressive")
  206. // 当前步骤(渐进模式): pending, subjects, chapters, sections, content, completed
  207. currentStep String @default("pending")
  208. // 多Agent模式状态: 存储各Agent的进度JSON
  209. agentStatus String? @db.Text
  210. createdAt DateTime @default(now())
  211. updatedAt DateTime @updatedAt
  212. subjects Subject[]
  213. @@index([userId, status])
  214. }
  215. // 学科(大类)
  216. model Subject {
  217. id Int @id @default(autoincrement())
  218. learningPathId Int
  219. name String // 学科名称,如"计算机原理"
  220. orderIndex Int @default(0) // 排序
  221. status String @default("pending") // pending, generating, completed
  222. aiResponse String? @db.Text // AI返回的原始内容
  223. // 确认状态(渐进模式): pending, confirmed, regenerating
  224. confirmationStatus String @default("pending")
  225. createdAt DateTime @default(now())
  226. updatedAt DateTime @updatedAt
  227. learningPath LearningPath @relation(fields: [learningPathId], references: [id], onDelete: Cascade)
  228. chapters Chapter[]
  229. @@index([learningPathId, orderIndex])
  230. }
  231. // 章节
  232. model Chapter {
  233. id Int @id @default(autoincrement())
  234. subjectId Int
  235. name String // 章节名称
  236. orderIndex Int @default(0)
  237. status String @default("pending")
  238. aiResponse String? @db.Text
  239. // 确认状态(渐进模式): pending, confirmed, regenerating
  240. confirmationStatus String @default("pending")
  241. createdAt DateTime @default(now())
  242. updatedAt DateTime @updatedAt
  243. subject Subject @relation(fields: [subjectId], references: [id], onDelete: Cascade)
  244. sections Section[]
  245. @@index([subjectId, orderIndex])
  246. }
  247. // 小节
  248. model Section {
  249. id Int @id @default(autoincrement())
  250. chapterId Int
  251. name String // 小节名称
  252. orderIndex Int @default(0)
  253. status String @default("pending")
  254. aiResponse String? @db.Text
  255. // 确认状态(渐进模式): pending, confirmed, regenerating
  256. confirmationStatus String @default("pending")
  257. createdAt DateTime @default(now())
  258. updatedAt DateTime @updatedAt
  259. chapter Chapter @relation(fields: [chapterId], references: [id], onDelete: Cascade)
  260. contentBlocks ContentBlock[]
  261. @@index([chapterId, orderIndex])
  262. }
  263. // 内容块(详细内容)
  264. model ContentBlock {
  265. id Int @id @default(autoincrement())
  266. sectionId Int
  267. content String @db.Text // 详细内容
  268. orderIndex Int @default(0)
  269. wordCount Int @default(0)
  270. createdAt DateTime @default(now())
  271. updatedAt DateTime @updatedAt
  272. section Section @relation(fields: [sectionId], references: [id], onDelete: Cascade)
  273. @@index([sectionId, orderIndex])
  274. }
  275. // ============ 视频生成模块 ============
  276. // 视频项目(关联到书籍的某个章节)
  277. model VideoProject {
  278. id Int @id @default(autoincrement())
  279. userId Int?
  280. title String // 项目标题
  281. description String? // 描述
  282. coverUrl String? // 封面图
  283. // 素材配置(JSON存储)
  284. configJson String? @db.LongText // VideoConfig JSON
  285. // 输出视频
  286. outputUrl String? // 生成后的视频URL
  287. duration Int? // 视频时长(秒)
  288. fileSize Int? // 文件大小(字节)
  289. // 关联:直接关联到章节
  290. bookId Int? // 关联的书籍ID(可选)
  291. chapterId Int? // 关联的章节ID(直接关联章节获取 content/audioUrl)
  292. status String @default("draft") // draft, processing, completed, failed
  293. progress Int @default(0) // 0-100
  294. errorMsg String? @db.Text
  295. createdAt DateTime @default(now())
  296. updatedAt DateTime @updatedAt
  297. book Book? @relation(fields: [bookId], references: [id])
  298. chapter BookChapter? @relation(fields: [chapterId], references: [id])
  299. @@index([userId, status])
  300. @@index([createdAt])
  301. }
  302. // ============ 搜索历史 ============
  303. model SearchHistory {
  304. id Int @id @default(autoincrement())
  305. userId Int // 用户ID,0表示未登录用户
  306. keyword String // 搜索关键词
  307. createdAt DateTime @default(now())
  308. @@index([userId, createdAt])
  309. @@index([userId])
  310. }
  311. // 热门搜索词
  312. model HotSearch {
  313. id Int @id @default(autoincrement())
  314. keyword String // 搜索关键词
  315. count Int @default(0) // 搜索次数
  316. sort Int @default(0) // 排序,数字越大越靠前
  317. createdAt DateTime @default(now())
  318. updatedAt DateTime @updatedAt
  319. @@index([sort])
  320. @@index([count])
  321. }
  322. // 签到记录
  323. model SignRecord {
  324. id Int @id @default(autoincrement())
  325. userId Int
  326. createdAt DateTime @default(now())
  327. user User @relation(fields: [userId], references: [id])
  328. @@unique([userId, createdAt])
  329. @@index([userId, createdAt])
  330. }
  331. // ============ 订阅套餐系统 ============
  332. // 套餐计划
  333. model SubscriptionPlan {
  334. id Int @id @default(autoincrement())
  335. name String // 套餐名称
  336. level Int @default(0) // 套餐等级:0免费 1基础 2专业 3旗舰
  337. priceMonthly Decimal @db.Decimal(10, 2) @default(0) // 月付价格
  338. priceYearly Decimal @db.Decimal(10, 2) @default(0) // 年付价格
  339. description String? @db.Text // 套餐描述
  340. features String? @db.Text // 功能列表(JSON数组)
  341. isRecommended Boolean @default(false) // 是否推荐
  342. isActive Boolean @default(true) // 是否上架
  343. sortOrder Int @default(0) // 排序
  344. // 限制配置
  345. dailyGenerations Int @default(3) // 每日生成次数,-1表示无限制
  346. perGenerationLimit Int @default(2000) // 单次生成限制字数
  347. monthlyTokens Int @default(10000) // 每月token配额,-1表示无限制
  348. monthlyMinutes Int @default(30) // 每月音频时长配额(分钟)
  349. yearlyTokens Int? // 每年token配额(旗舰版用)
  350. voiceOptions Int @default(5) // 可用音色数,-1表示全部
  351. audioQuality String @default("standard") // standard, high, lossless
  352. // 高级功能
  353. apiAccess Boolean @default(false) // API访问权限
  354. batchProcessing Boolean @default(false) // 批量处理权限
  355. teamManagement Boolean @default(false) // 团队管理权限
  356. createdAt DateTime @default(now())
  357. updatedAt DateTime @updatedAt
  358. subscriptions Subscription[]
  359. orders Order[]
  360. @@index([level])
  361. @@index([isActive, sortOrder])
  362. }
  363. // 用户订阅记录
  364. model Subscription {
  365. id Int @id @default(autoincrement())
  366. userId Int
  367. planId Int
  368. startDate DateTime
  369. endDate DateTime
  370. status String @default("active") // active, expired, cancelled
  371. autoRenew Boolean @default(false) // 自动续费
  372. createdAt DateTime @default(now())
  373. updatedAt DateTime @updatedAt
  374. user User @relation(fields: [userId], references: [id])
  375. plan SubscriptionPlan @relation(fields: [planId], references: [id])
  376. @@index([userId, status])
  377. @@index([userId, endDate])
  378. }
  379. // Token使用记录
  380. model TokenUsage {
  381. id Int @id @default(autoincrement())
  382. userId Int
  383. type String // text_to_speech, api_call, batch_process
  384. amount Int @default(0) // 消耗token数量
  385. contentLength Int @default(0) // 内容长度(字数)
  386. orderId Int? // 关联的订单ID
  387. description String? @db.Text // 消耗描述
  388. createdAt DateTime @default(now())
  389. user User @relation(fields: [userId], references: [id])
  390. order Order? @relation(fields: [orderId], references: [id])
  391. @@index([userId, createdAt])
  392. @@index([userId, type])
  393. }
  394. // Token余额
  395. model TokenBalance {
  396. id Int @id @default(autoincrement())
  397. userId Int @unique
  398. totalTokens Int @default(0) // 总token配额
  399. usedTokens Int @default(0) // 已使用token
  400. resetDate DateTime? // 重置日期(月/年)
  401. createdAt DateTime @default(now())
  402. updatedAt DateTime @updatedAt
  403. user User @relation(fields: [userId], references: [id])
  404. @@index([userId])
  405. }
  406. // 视频素材库(保留作为独立素材)
  407. model VideoMaterial {
  408. id Int @id @default(autoincrement())
  409. userId Int? // null 表示公共素材
  410. type String // image, audio, template
  411. name String // 素材名称
  412. url String @db.Text // 素材URL
  413. thumbnail String? // 缩略图URL
  414. tags String? @db.Text // 标签(JSON数组)
  415. category String? // 分类:nature, abstract, business, music等
  416. duration Int? // 音频时长(秒)
  417. size Int? // 文件大小(字节)
  418. width Int? // 图片宽度
  419. height Int? // 图片高度
  420. createdAt DateTime @default(now())
  421. updatedAt DateTime @updatedAt
  422. @@index([userId, type])
  423. @@index([type, category])
  424. }