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
  usedAudioMinutes      Int             @default(0)
  costLimit             Float           @default(0)
  quotaReserved         Float           @default(0)
  subscriptionResetDate DateTime?
  comments              Comment[]
  drafts                Draft[]
  favorites             Favorite[]
  notifications         Notification[]
  orders                Order[]
  playRecords           PlayRecord[]
  playlists             Playlist[]
  signRecords           SignRecord[]
  subscriptions         Subscription[]
  tokenBalance          TokenBalance?
  tokenUsages           TokenUsage[]
  preferences           UserPreference?
  shareRecords          ShareRecord[]
  invitesSent           InviteRecord[]   @relation("InviterRelation")
  invitesReceived       InviteRecord[]   @relation("InviteeRelation")

  @@index([phone])
  @@index([openid])
}

model Order {
  id            Int               @id @default(autoincrement())
  userId        Int
  orderNo       String            @unique
  planId        Int?
  productType   String
  amount        Decimal           @db.Decimal(10, 2)
  status        String            @default("pending")
  paymentMethod String?
  paymentId     String?
  paidAt        DateTime?
  createdAt     DateTime          @default(now())
  updatedAt     DateTime          @updatedAt
  plan          SubscriptionPlan? @relation(fields: [planId], references: [id])
  user          User              @relation(fields: [userId], references: [id])
  tokenUsages   TokenUsage[]

  @@index([userId, createdAt])
  @@index([orderNo])
  @@index([status])
  @@index([planId], map: "Order_planId_fkey")
}

model PlayRecord {
  id        Int         @id @default(autoincrement())
  userId    Int
  chapterId Int
  progress  Float       @default(0)
  duration  Float       @default(0)
  updatedAt DateTime    @updatedAt
  createdAt DateTime    @default(now())
  chapter   BookChapter @relation(fields: [chapterId], references: [id])
  user      User        @relation(fields: [userId], references: [id])

  @@unique([userId, chapterId])
  @@index([userId])
  @@index([chapterId])
}

model UserPreference {
  id               Int      @id @default(autoincrement())
  userId           Int      @unique
  playSpeed        Float    @default(1)
  quality          String   @default("standard")
  theme            String   @default("light")
  defaultVoiceId   String?  @default("cherry")
  defaultVolume    Int      @default(50)
  autoPlayNext     Boolean  @default(true)
  wifiOnlyDownload Boolean  @default(false)
  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
  createdAt DateTime @default(now())
  book      Book     @relation(fields: [bookId], references: [id], onDelete: Cascade)
  user      User     @relation(fields: [userId], references: [id])

  @@unique([userId, bookId])
  @@index([userId])
  @@index([bookId])
}

model Comment {
  id        Int         @id @default(autoincrement())
  userId    Int
  chapterId Int
  content   String      @db.Text
  rating    Int
  createdAt DateTime    @default(now())
  chapter   BookChapter @relation(fields: [chapterId], references: [id])
  user      User        @relation(fields: [userId], references: [id])

  @@index([chapterId])
  @@index([userId])
}

model Notification {
  id        String   @id @default(uuid())
  userId    Int
  title     String
  content   String   @db.Text
  isRead    Boolean  @default(false)
  createdAt DateTime @default(now())
  user      User     @relation(fields: [userId], references: [id])

  @@index([userId])
}

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("1000")
  totalChapters  Int            @default(10)
  estimatedWords Int            @default(0)
  progress       Int            @default(0)
  isPublished    Boolean        @default(false)
  outlineJson    String?        @db.LongText
  foreword       String?        @db.Text
  afterword      String?        @db.Text
  errorMsg       String?        @db.Text
  createdAt      DateTime       @default(now())
  updatedAt      DateTime       @updatedAt
  failedStage    String?        @db.VarChar(30)
  genStage       String         @default("draft")
  bookAnalysis   String?        @db.Text
  chapters       BookChapter[]
  favorites      Favorite[]
  videoProjects  VideoProject[]

  @@index([userId])
  @@index([createdAt])
}

model BookChapter {
  id             Int            @id @default(autoincrement())
  bookId         Int
  parentId       Int            @default(0)
  level          Int            @default(1)
  number         Int
  title          String
  summary        String?        @db.Text
  keyPoints      String?        @db.Text
  estimatedWords Int            @default(1000)
  content        String?        @db.LongText
  wordCount      Int            @default(0)
  contentError   String?        @db.Text
  generatedAt    DateTime?
  audioUrl       String?        @db.Text
  audioDuration  Int            @default(0)
  videoUrl       String?        @db.Text
  videoDuration  Int?
  isPublic       Boolean        @default(false)
  genStage       String         @default("idle")
  lrcLyrics      String?        @db.Text
  contentCheck   String?        @db.Text
  book           Book           @relation(fields: [bookId], references: [id], onDelete: Cascade)
  comments       Comment[]
  playRecords    PlayRecord[]
  playlistItems  PlaylistItem[]
  ttsTasks       TtsTask[]
  videoProjects  VideoProject[]

  @@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?
  configJson  String?      @db.LongText
  outputUrl   String?
  duration    Int?
  fileSize    Int?
  bookId      Int?
  chapterId   Int?
  status      String       @default("draft")
  progress    Int          @default(0)
  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])
  @@index([bookId], map: "VideoProject_bookId_fkey")
  @@index([chapterId], map: "VideoProject_chapterId_fkey")
}

model SearchHistory {
  id        Int      @id @default(autoincrement())
  userId    Int
  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)
  priceMonthly       Decimal        @default(0.00) @db.Decimal(10, 2)
  priceYearly        Decimal        @default(0.00) @db.Decimal(10, 2)
  description        String?        @db.Text
  features           String?        @db.Text
  isRecommended      Boolean        @default(false)
  isActive           Boolean        @default(true)
  sortOrder          Int            @default(0)
  dailyGenerations   Int            @default(3)
  perGenerationLimit Int            @default(2000)
  monthlyTokens      Int            @default(10000)
  monthlyMinutes     Int            @default(30)
  yearlyTokens       Int?
  voiceOptions       Int            @default(5)
  audioQuality       String         @default("standard")
  apiAccess          Boolean        @default(false)
  batchProcessing    Boolean        @default(false)
  teamManagement     Boolean        @default(false)
  overageEnabled     Boolean        @default(false)
  overagePrice       Decimal        @default(0.00) @db.Decimal(10, 2)
  createdAt          DateTime       @default(now())
  updatedAt          DateTime       @updatedAt
  orders             Order[]
  subscriptions      Subscription[]

  @@index([level])
  @@index([isActive, sortOrder])
}

model Subscription {
  id        Int              @id @default(autoincrement())
  userId    Int
  planId    Int
  startDate DateTime
  endDate   DateTime
  status    String           @default("active")
  autoRenew Boolean          @default(false)
  createdAt DateTime         @default(now())
  updatedAt DateTime         @updatedAt
  plan      SubscriptionPlan @relation(fields: [planId], references: [id])
  user      User             @relation(fields: [userId], references: [id])

  @@index([userId, status])
  @@index([userId, endDate])
  @@index([planId], map: "Subscription_planId_fkey")
}

model TokenUsage {
  id            Int      @id @default(autoincrement())
  userId        Int
  type          String
  amount        Int      @default(0)
  contentLength Int      @default(0)
  orderId       Int?
  description   String?  @db.Text
  createdAt     DateTime @default(now())
  order         Order?   @relation(fields: [orderId], references: [id])
  user          User     @relation(fields: [userId], references: [id])

  @@index([userId, createdAt])
  @@index([userId, type])
  @@index([orderId], map: "TokenUsage_orderId_fkey")
}

model TokenBalance {
  id          Int       @id @default(autoincrement())
  userId      Int       @unique
  totalTokens Int       @default(0)
  usedTokens  Int       @default(0)
  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?
  type      String
  name      String
  url       String   @db.Text
  thumbnail String?
  tags      String?  @db.Text
  category  String?
  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
  title         String   @default("未命名音频")
  text          String?  @db.LongText
  wordCount     Int      @default(0)
  voiceId       String   @default("cherry")
  voiceParams   String?
  audioUrl      String?  @db.Text
  audioDuration Int      @default(0)
  audioSize     Int      @default(0)
  status        String   @default("processing")
  errorMsg      String?  @db.Text
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt
  bookId        Int?

  @@index([userId])
  @@index([audioId])
  @@index([bookId])
}

model TtsTask {
  id            Int          @id @default(autoincrement())
  taskType      String       @default("tts")    @db.VarChar(20)  // "tts" | "content"
  chapterId     Int
  bookId        Int?
  userId        Int?
  contentHash   String       @db.VarChar(64)
  content       String?      @db.LongText
  status        String       @default("pending")
  audioUrl      String?      @db.Text
  audioDuration Int          @default(0)
  errorMsg      String?      @db.Text
  retryCount    Int          @default(0)
  maxRetries    Int          @default(3)
  voiceId       String       @default("default")
  startedAt     DateTime?
  completedAt   DateTime?
  createdAt     DateTime     @default(now())
  updatedAt     DateTime     @updatedAt
  chapter       BookChapter  @relation(fields: [chapterId], references: [id], onDelete: Cascade)

  @@index([taskType, status, createdAt])
  @@index([chapterId, contentHash])
  @@index([contentHash, chapterId])
}

model AiCallLog {
  id            Int      @id @default(autoincrement())
  callType      String   @db.VarChar(20)   // llm_chat | llm_tools | tts_synthesize | tts_create | tts_poll
  provider      String   @db.VarChar(30)   // minimax | bailian | volcengine
  model         String   @db.VarChar(50)   // MiniMax-M2.7 | cosyvoice-v3-flash | qwen3-tts-instruct-flash
  userId        Int?
  bookId        Int?
  chapterId     Int?
  textLen       Int      @default(0)       // 输入文本长度（字符数）
  prompt        String?  @db.LongText       // 提示词内容（LLM only）
  inputTokens   Int      @default(0)       // 输入 Token 数
  outputTokens  Int      @default(0)       // 输出 Token 数
  estimatedCost Float    @default(0)       // 估算成本（元）
  duration      Int      @default(0)       // 耗时(ms)
  success       Boolean  @default(true)
  errorMsg      String?  @db.Text
  createdAt     DateTime @default(now())

  @@index([callType, createdAt])
  @@index([provider, createdAt])
  @@index([userId, createdAt])
  @@index([bookId])
  @@index([createdAt])
}

model PlatformAccount {
  id         Int       @id @default(autoincrement())
  userId     Int
  platform   String
  nickname   String    @default("")
  avatar     String    @default("")
  cookies    String    @db.Text
  headers    String?   @db.Text
  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
  title          String   @db.Text
  description    String?  @db.Text
  tags           String?  @db.Text
  coverUrl       String?  @db.Text
  videoUrl       String?  @db.Text
  status         String   @default("pending")
  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
  title       String?   @db.Text
  content     String?   @db.LongText
  metadata    String?   @db.Text
  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?
  audioId    String?
  order      Int          @default(0)
  chapter    BookChapter? @relation(fields: [chapterId], references: [id])
  playlist   Playlist     @relation(fields: [playlistId], references: [id], onDelete: Cascade)

  @@index([playlistId, order])
  @@index([chapterId], map: "PlaylistItem_chapterId_fkey")
}

model Feedback {
  id             String   @id @default(uuid())
  type           String
  title          String   @db.Text
  content        String   @db.Text
  contact        String   @db.Text
  screenshotUrls String   @db.Text
  status         String   @default("submitted")
  createdAt      DateTime @default(now())
  updatedAt      DateTime @updatedAt

  @@index([type])
  @@index([status])
  @@index([createdAt])
}

model ShareRecord {
  id        Int      @id @default(autoincrement())
  userId    Int
  audioId   Int
  platform  String
  shareCode String   @unique
  createdAt DateTime @default(now())
  user      User     @relation(fields: [userId], references: [id])

  @@index([userId])
  @@index([shareCode])
}

model InviteRecord {
  id        Int      @id @default(autoincrement())
  inviterId Int
  inviteeId Int      @unique
  shareCode String?
  rewarded  Boolean  @default(false)
  createdAt DateTime @default(now())
  inviter   User     @relation("InviterRelation", fields: [inviterId], references: [id])
  invitee   User     @relation("InviteeRelation", fields: [inviteeId], references: [id])

  @@index([inviterId])
  @@index([shareCode])
}

model TokenPackPurchase {
  id          Int      @id @default(autoincrement())
  userId      Int
  packMinutes Int
  quantity    Int
  unitPrice   Float
  totalAmount Float
  addedQuota  Float
  createdAt   DateTime @default(now())
}
