| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- generator client {
- provider = "prisma-client-js"
- }
- datasource db {
- provider = "mysql"
- url = env("DATABASE_URL")
- }
- model User {
- id Int @id @default(autoincrement())
- phone String? @unique
- openid String? @unique
- nickname String @default("用户")
- avatar String @default("")
- memberLevel Int @default(0)
- memberExpireAt DateTime?
- dailyUsage Int @default(0)
- lastUsageDate String @default("")
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- // 音频时长配额(2026-04-12 新增)
- usedAudioMinutes Int @default(0) // 本月已使用音频分钟数
- subscriptionResetDate DateTime? // 配额重置日期
- orders Order[]
- playRecords PlayRecord[]
- preferences UserPreference?
- favorites Favorite[]
- comments Comment[]
- signRecords SignRecord[]
- subscriptions Subscription[]
- tokenUsages TokenUsage[]
- tokenBalance TokenBalance?
- drafts Draft[]
- playlists Playlist[]
- @@index([phone])
- @@index([openid])
- }
- // 订单
- model Order {
- id Int @id @default(autoincrement())
- userId Int
- orderNo String @unique
- planId Int? // 关联的套餐ID
- productType String // monthly 或 yearly
- amount Decimal @db.Decimal(10, 2)
- status String @default("pending") // pending, paid, failed, refunded
- paymentMethod String? // alipay, wechat, mock
- paymentId String? // 第三方支付单号
- paidAt DateTime?
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- user User @relation(fields: [userId], references: [id])
- plan SubscriptionPlan? @relation(fields: [planId], references: [id])
- tokenUsages TokenUsage[]
- @@index([userId, createdAt])
- @@index([orderNo])
- @@index([status])
- }
- // 播放记录(播放的是章节的音频)
- model PlayRecord {
- id Int @id @default(autoincrement())
- userId Int
- chapterId Int // BookChapter.id(章节ID)
- progress Float @default(0) // 播放进度(秒)
- duration Float @default(0) // 总时长
- updatedAt DateTime @updatedAt
- createdAt DateTime @default(now())
- user User @relation(fields: [userId], references: [id])
- chapter BookChapter @relation(fields: [chapterId], references: [id])
- @@unique([userId, chapterId])
- @@index([userId])
- @@index([chapterId])
- }
- // 用户偏好
- model UserPreference {
- id Int @id @default(autoincrement())
- userId Int @unique
- playSpeed Float @default(1.0)
- quality String @default("standard") // standard, high
- theme String @default("light")
- // 新增字段 - 用户偏好设置增强
- defaultVoiceId String? @default("cherry") // 默认音色ID
- defaultVolume Int @default(50) // 默认音量 0-100
- autoPlayNext Boolean @default(true) // 自动播放下一首
- wifiOnlyDownload Boolean @default(false) // 仅WiFi下载
- updatedAt DateTime @updatedAt
- createdAt DateTime @default(now())
- user User @relation(fields: [userId], references: [id])
- }
- // 收藏(收藏的是书籍/专辑)
- model Favorite {
- id Int @id @default(autoincrement())
- userId Int
- bookId Int // Book.id(收藏的是整本书籍)
- createdAt DateTime @default(now())
- user User @relation(fields: [userId], references: [id])
- book Book @relation(fields: [bookId], references: [id], onDelete: Cascade)
- @@unique([userId, bookId])
- @@index([userId])
- @@index([bookId])
- }
- // 评论(评论的是章节)
- model Comment {
- id Int @id @default(autoincrement())
- userId Int
- chapterId Int // BookChapter.id
- content String @db.Text
- rating Int // 1-5星
- createdAt DateTime @default(now())
- user User @relation(fields: [userId], references: [id])
- chapter BookChapter @relation(fields: [chapterId], references: [id])
- @@index([chapterId])
- @@index([userId])
- }
- // 系统通知
- model Notification {
- id String @id @default(uuid())
- userId String
- title String
- content String @db.Text
- isRead Boolean @default(false)
- createdAt DateTime @default(now())
- }
- // ============ 书籍/章节(核心内容) ============
- // 书籍(专辑)
- model Book {
- id Int @id @default(autoincrement())
- userId Int?
- title String // 书名
- subtitle String? // 副标题
- description String @db.Text // 书籍描述/用户输入
- coverUrl String? // 封面图
- targetAudience String @default("通用") // 目标受众
- style String @default("专业严谨") // 写作风格
- bookScale String @default("标准教程") // 书籍规模:800, 2000, 5000, 小册子, 标准教程, 系统教材
- totalChapters Int @default(10) // 总章节数
- estimatedWords Int @default(0) // 预估总字数
- status String @default("draft") // draft, planning, generating, completed, failed, interrupted
- progress Int @default(0) // 生成进度 0-100
- isPublished Boolean @default(false) // 是否已发布
- // 大纲(JSON 存储)
- outlineJson String? @db.LongText // BookOutline JSON
- // 前言/后记
- foreword String? @db.Text
- afterword String? @db.Text
- // 错误信息
- errorMsg String? @db.Text
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- chapters BookChapter[]
- videoProjects VideoProject[] // 关联的视频项目
- favorites Favorite[] // 收藏此书籍的用户
- @@index([userId, status])
- @@index([createdAt])
- }
- // 书籍章节(内容单元)
- // 一个章节 = 一份内容,有3种形式:content(文本)、audioUrl(音频)、videoUrl(视频)
- // level=1 表示章(顶级),level=2 表示节,level=3 表示小节
- // parentId = null 表示章(顶级),parentId = 章ID 表示节,parentId = 节ID 表示小节
- model BookChapter {
- id Int @id @default(autoincrement())
- bookId Int
- parentId Int? // 父节点ID(null = 章这一级)
- level Int @default(1) // 层级:1=章, 2=节, 3=小节
- number Int // 同级排序序号
- title String // 章节标题
- summary String? @db.Text // 章节概述
- keyPoints String? @db.Text // 核心知识点(JSON数组)
- estimatedWords Int @default(1000) // 预估字数
- content String? @db.LongText // 正文内容(原始文本)
- wordCount Int @default(0) // 实际字数
-
- // 大纲状态(title, summary, keyPoints)
- status String @default("pending") // pending, completed, failed
-
- // 内容生成状态(content, wordCount)
- contentStatus String? @db.VarChar(20) // null=未开始, pending=待生成, generating=生成中, completed=已完成, failed=失败
- contentError String? @db.Text // 内容生成错误信息
-
- generatedAt DateTime?
- // 3种表现形式
- audioUrl String? @db.Text // 音频URL(由 content 生成)
- audioDuration Int @default(0) // 音频时长(秒)
- videoUrl String? @db.Text // 视频URL(由 content/audio 生成)
- videoDuration Int? // 视频时长(秒)
- // 公开状态(2026-04-14 新增)
- isPublic Boolean @default(false) // 是否公开到首页
- book Book @relation(fields: [bookId], references: [id], onDelete: Cascade)
- // parent BookChapter? @relation("ChapterChildren", fields: [parentId], references: [id], onDelete: Cascade)
- // children BookChapter[] @relation("ChapterChildren")
- videoProjects VideoProject[]
- playRecords PlayRecord[]
- comments Comment[]
- playlistItems PlaylistItem[]
- @@unique([bookId, parentId, level, number])
- @@index([bookId])
- @@index([bookId, parentId])
- @@index([bookId, level])
- }
- // ============ 视频生成模块 ============
- // 视频项目(关联到书籍的某个章节)
- model VideoProject {
- id Int @id @default(autoincrement())
- userId Int?
- title String // 项目标题
- description String? // 描述
- coverUrl String? // 封面图
- // 素材配置(JSON存储)
- configJson String? @db.LongText // VideoConfig JSON
- // 输出视频
- outputUrl String? // 生成后的视频URL
- duration Int? // 视频时长(秒)
- fileSize Int? // 文件大小(字节)
- // 关联:直接关联到章节
- bookId Int? // 关联的书籍ID(可选)
- chapterId Int? // 关联的章节ID(直接关联章节获取 content/audioUrl)
- status String @default("draft") // draft, processing, completed, failed
- progress Int @default(0) // 0-100
- errorMsg String? @db.Text
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- book Book? @relation(fields: [bookId], references: [id])
- chapter BookChapter? @relation(fields: [chapterId], references: [id])
- @@index([userId, status])
- @@index([createdAt])
- }
- // ============ 搜索历史 ============
- model SearchHistory {
- id Int @id @default(autoincrement())
- userId Int // 用户ID,0表示未登录用户
- keyword String // 搜索关键词
- createdAt DateTime @default(now())
- @@index([userId, createdAt])
- @@index([userId])
- }
- // 热门搜索词
- model HotSearch {
- id Int @id @default(autoincrement())
- keyword String // 搜索关键词
- count Int @default(0) // 搜索次数
- sort Int @default(0) // 排序,数字越大越靠前
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- @@index([sort])
- @@index([count])
- }
- // 签到记录
- model SignRecord {
- id Int @id @default(autoincrement())
- userId Int
- createdAt DateTime @default(now())
- user User @relation(fields: [userId], references: [id])
- @@unique([userId, createdAt])
- @@index([userId, createdAt])
- }
- // ============ 订阅套餐系统 ============
- // 套餐计划
- model SubscriptionPlan {
- id Int @id @default(autoincrement())
- name String // 套餐名称
- level Int @default(0) // 套餐等级:0免费 1基础 2专业 3旗舰
- priceMonthly Decimal @db.Decimal(10, 2) @default(0) // 月付价格
- priceYearly Decimal @db.Decimal(10, 2) @default(0) // 年付价格
- description String? @db.Text // 套餐描述
- features String? @db.Text // 功能列表(JSON数组)
- isRecommended Boolean @default(false) // 是否推荐
- isActive Boolean @default(true) // 是否上架
- sortOrder Int @default(0) // 排序
-
- // 限制配置
- dailyGenerations Int @default(3) // 每日生成次数,-1表示无限制
- perGenerationLimit Int @default(2000) // 单次生成限制字数
- monthlyTokens Int @default(10000) // 每月token配额,-1表示无限制
- monthlyMinutes Int @default(30) // 每月音频时长配额(分钟)
- yearlyTokens Int? // 每年token配额(旗舰版用)
- voiceOptions Int @default(5) // 可用音色数,-1表示全部
- audioQuality String @default("standard") // standard, high, lossless
-
- // 高级功能
- apiAccess Boolean @default(false) // API访问权限
- batchProcessing Boolean @default(false) // 批量处理权限
- teamManagement Boolean @default(false) // 团队管理权限
- overageEnabled Boolean @default(false) // 是否允许超出配额
- overagePrice Decimal @db.Decimal(10, 2) @default(0) // 超出配额价格(元/分钟)
-
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- subscriptions Subscription[]
- orders Order[]
- @@index([level])
- @@index([isActive, sortOrder])
- }
- // 用户订阅记录
- model Subscription {
- id Int @id @default(autoincrement())
- userId Int
- planId Int
- startDate DateTime
- endDate DateTime
- status String @default("active") // active, expired, cancelled
- autoRenew Boolean @default(false) // 自动续费
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- user User @relation(fields: [userId], references: [id])
- plan SubscriptionPlan @relation(fields: [planId], references: [id])
- @@index([userId, status])
- @@index([userId, endDate])
- }
- // Token使用记录
- model TokenUsage {
- id Int @id @default(autoincrement())
- userId Int
- type String // text_to_speech, api_call, batch_process
- amount Int @default(0) // 消耗token数量
- contentLength Int @default(0) // 内容长度(字数)
- orderId Int? // 关联的订单ID
- description String? @db.Text // 消耗描述
- createdAt DateTime @default(now())
- user User @relation(fields: [userId], references: [id])
- order Order? @relation(fields: [orderId], references: [id])
- @@index([userId, createdAt])
- @@index([userId, type])
- }
- // Token余额
- model TokenBalance {
- id Int @id @default(autoincrement())
- userId Int @unique
- totalTokens Int @default(0) // 总token配额
- usedTokens Int @default(0) // 已使用token
- resetDate DateTime? // 重置日期(月/年)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- user User @relation(fields: [userId], references: [id])
- @@index([userId])
- }
- // 视频素材库(保留作为独立素材)
- model VideoMaterial {
- id Int @id @default(autoincrement())
- userId Int? // null 表示公共素材
- type String // image, audio, template
- name String // 素材名称
- url String @db.Text // 素材URL
- thumbnail String? // 缩略图URL
- tags String? @db.Text // 标签(JSON数组)
- category String? // 分类:nature, abstract, business, music等
- duration Int? // 音频时长(秒)
- size Int? // 文件大小(字节)
- width Int? // 图片宽度
- height Int? // 图片高度
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- @@index([userId, type])
- @@index([type, category])
- }
- // 音频生成记录
- model AudioRecord {
- id Int @id @default(autoincrement())
- userId Int?
- audioId String @unique // uuid,对应磁盘上的文件夹名
- title String @default("未命名音频")
- text String? @db.LongText
- wordCount Int @default(0)
- voiceId String @default("cherry")
- voiceParams String? // JSON: {speed, pitch, volume}
- audioUrl String? @db.Text
- audioDuration Int @default(0) // 秒
- audioSize Int @default(0) // 字节
- status String @default("processing") // processing, completed, failed
- errorMsg String? @db.Text
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- @@index([userId])
- @@index([audioId])
- }
- // ============ 社交平台发布模块 ============
- // 平台账号(存储各平台的登录凭证)
- model PlatformAccount {
- id Int @id @default(autoincrement())
- userId Int
- platform String // douyin, kuaishou, bilibili
- nickname String @default("")
- avatar String @default("")
- cookies String @db.Text // 登录 Cookie
- headers String? @db.Text // 关键请求头(JSON)
- isValid Boolean @default(true)
- expireTime DateTime? // 凭证过期时间
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- @@unique([userId, platform])
- @@index([userId])
- @@index([platform])
- }
- // 发布任务记录
- model PublishTask {
- id Int @id @default(autoincrement())
- userId Int
- videoProjectId Int
- platform String // douyin, kuaishou, bilibili
- title String @db.Text
- description String? @db.Text
- tags String? @db.Text // JSON 数组
- coverUrl String? @db.Text
- videoUrl String? @db.Text // 本地视频路径
- status String @default("pending") // pending, uploading, success, failed
- errorMsg String? @db.Text
- publishedUrl String? @db.Text // 发布后的作品链接
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- @@index([userId, platform])
- @@index([status])
- }
- // ============ 草稿模块 ============
- model Draft {
- id Int @id @default(autoincrement())
- userId Int
- type String // content, outline, audio
- title String? @db.Text
- content String? @db.LongText
- metadata String? @db.Text // JSON 存储额外信息
- autoSavedAt DateTime?
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- user User @relation(fields: [userId], references: [id])
- @@index([userId, type])
- @@index([userId, updatedAt])
- }
- // ============ 播放列表模块 ============
- model Playlist {
- id Int @id @default(autoincrement())
- userId Int
- name String // 播放列表名称
- description String? @db.Text
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- user User @relation(fields: [userId], references: [id])
- items PlaylistItem[]
- @@index([userId])
- }
- model PlaylistItem {
- id Int @id @default(autoincrement())
- playlistId Int
- chapterId Int? // 关联的章节ID
- audioId String? // 关联的音频ID(独立音频)
- order Int @default(0) // 排序
- playlist Playlist @relation(fields: [playlistId], references: [id], onDelete: Cascade)
- chapter BookChapter? @relation(fields: [chapterId], references: [id])
- @@index([playlistId, order])
- }
|