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 orders Order[] playRecords PlayRecord[] preferences UserPreference? favorites Favorite[] comments Comment[] signRecords SignRecord[] subscriptions Subscription[] tokenUsages TokenUsage[] tokenBalance TokenBalance? @@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") 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 Category { id Int @id @default(autoincrement()) name String icon String @default("") sort Int @default(0) createdAt DateTime @default(now()) } // 评论(评论的是章节) 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 Template { id Int @id @default(autoincrement()) name String category String // 广告营销/知识付费/短视频/企业宣传/日常生活/新闻资讯 content String @db.Text // 模板内容 description String? @db.Text // 模板描述 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([category]) } // ============ 书籍/章节(核心内容) ============ // 书籍(专辑) 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("专业严谨") // 写作风格 totalChapters Int @default(10) // 总章节数 estimatedWords Int @default(0) // 预估总字数 status String @default("draft") // draft, planning, generating, completed, failed 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(视频) model BookChapter { id Int @id @default(autoincrement()) bookId Int 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) // 实际字数 status String @default("pending") // pending, generating, completed, failed errorMsg 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? // 视频时长(秒) book Book @relation(fields: [bookId], references: [id], onDelete: Cascade) videoProjects VideoProject[] playRecords PlayRecord[] comments Comment[] @@unique([bookId, number]) @@index([bookId]) } // ============ 学习路径模块 ============ // 学习路径任务 model LearningPath { id Int @id @default(autoincrement()) userId Int? topic String @db.Text // 用户输入的主题 status String @default("pending") // pending, generating, completed, failed progress Int @default(0) // 0-100 进度百分比 totalCount Int @default(0) // 总内容块数 completedCount Int @default(0) // 已完成内容块数 errorMsg String? @db.Text // 错误信息 // 生成模式: progressive(渐进确认) | multi-agent(多Agent并行) generationMode String @default("progressive") // 当前步骤(渐进模式): pending, subjects, chapters, sections, content, completed currentStep String @default("pending") // 多Agent模式状态: 存储各Agent的进度JSON agentStatus String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt subjects Subject[] @@index([userId, status]) } // 学科(大类) model Subject { id Int @id @default(autoincrement()) learningPathId Int name String // 学科名称,如"计算机原理" orderIndex Int @default(0) // 排序 status String @default("pending") // pending, generating, completed aiResponse String? @db.Text // AI返回的原始内容 // 确认状态(渐进模式): pending, confirmed, regenerating confirmationStatus String @default("pending") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt learningPath LearningPath @relation(fields: [learningPathId], references: [id], onDelete: Cascade) chapters Chapter[] @@index([learningPathId, orderIndex]) } // 章节 model Chapter { id Int @id @default(autoincrement()) subjectId Int name String // 章节名称 orderIndex Int @default(0) status String @default("pending") aiResponse String? @db.Text // 确认状态(渐进模式): pending, confirmed, regenerating confirmationStatus String @default("pending") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt subject Subject @relation(fields: [subjectId], references: [id], onDelete: Cascade) sections Section[] @@index([subjectId, orderIndex]) } // 小节 model Section { id Int @id @default(autoincrement()) chapterId Int name String // 小节名称 orderIndex Int @default(0) status String @default("pending") aiResponse String? @db.Text // 确认状态(渐进模式): pending, confirmed, regenerating confirmationStatus String @default("pending") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt chapter Chapter @relation(fields: [chapterId], references: [id], onDelete: Cascade) contentBlocks ContentBlock[] @@index([chapterId, orderIndex]) } // 内容块(详细内容) model ContentBlock { id Int @id @default(autoincrement()) sectionId Int content String @db.Text // 详细内容 orderIndex Int @default(0) wordCount Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt section Section @relation(fields: [sectionId], references: [id], onDelete: Cascade) @@index([sectionId, orderIndex]) } // ============ 视频生成模块 ============ // 视频项目(关联到书籍的某个章节) 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表示无限制 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) // 团队管理权限 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]) }