generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model User {
  id                      Int                  @id @default(autoincrement())
  phone                   String?              @unique
  email                   String?              @unique
  passwordHash            String?
  nickname                String?
  avatar                  String?
  bio                     String?
  status                  UserStatus           @default(ACTIVE)
  memberPlan              MemberPlan           @default(FREE)
  memberExpire            DateTime?
  sandboxDaily            Int                  @default(5)
  followerCount           Int                  @default(0)
  followingCount          Int                  @default(0)
  postCount               Int                  @default(0)
  lastLoginAt             DateTime?
  createdAt               DateTime             @default(now())
  updatedAt               DateTime             @updatedAt
  deletedAt               DateTime?
  username                String?              @unique
  sandboxExtra            Int                  @default(0)
  circleMemberships       CircleMember[]
  createdCircles          Circle[]             @relation("CircleCreator")
  comments                Comment[]
  following               Follow[]             @relation("Follower")
  followers               Follow[]             @relation("Following")
  learnRecords            LearnRecord[]
  notifications           Notification[]
  orders                  Order[]
  organizationMemberships OrganizationMember[]
  postLikes               PostLike[]
  posts                   Post[]
  practiceSubmissions     PracticeSubmission[]
  promptFavorites         PromptFavorite[]
  prompts                 Prompt[]
  sandboxSessions         SandboxSession[]
  subscriptions           Subscription[]
  userSkills              UserSkill[]

  @@map("users")
}

model Category {
  id          Int        @id @default(autoincrement())
  name        String
  slug        String     @unique
  description String?
  sortOrder   Int        @default(0)
  parentId    Int?
  createdAt   DateTime   @default(now())
  updatedAt   DateTime   @updatedAt
  parent      Category?  @relation("CategoryTree", fields: [parentId], references: [id])
  children    Category[] @relation("CategoryTree")
  contents    Content[]
  courses     Course[]
  prompts     Prompt[]
  tools       Tool[]

  @@index([parentId], map: "categories_parentId_fkey")
  @@map("categories")
}

model Course {
  id          Int                @id @default(autoincrement())
  title       String
  description String?
  cover       String?
  categoryId  Int?
  price       Float              @default(0)
  isFree      Boolean            @default(true)
  status      ContentStatus      @default(DRAFT)
  sortOrder   Int                @default(0)
  createdAt   DateTime           @default(now())
  updatedAt   DateTime           @updatedAt
  deletedAt   DateTime?
  chapters    Chapter[]
  assignments CourseAssignment[]
  category    Category?          @relation(fields: [categoryId], references: [id])
  progress    LearnRecord[]

  @@index([categoryId], map: "courses_categoryId_fkey")
  @@map("courses")
}

model Chapter {
  id        Int      @id @default(autoincrement())
  courseId  Int
  title     String
  sortOrder Int      @default(0)
  createdAt DateTime @default(now())
  course    Course   @relation(fields: [courseId], references: [id], onDelete: Cascade)
  lessons   Lesson[]

  @@index([courseId], map: "chapters_courseId_fkey")
  @@map("chapters")
}

model Lesson {
  id        Int           @id @default(autoincrement())
  chapterId Int
  title     String
  content   String?       @db.Text
  videoUrl  String?
  duration  Int?
  sortOrder Int           @default(0)
  status    ContentStatus @default(DRAFT)
  createdAt DateTime      @default(now())
  updatedAt DateTime      @updatedAt
  progress  LearnRecord[]
  chapter   Chapter       @relation(fields: [chapterId], references: [id], onDelete: Cascade)

  @@index([chapterId], map: "lessons_chapterId_fkey")
  @@map("lessons")
}

model LearnRecord {
  id        Int      @id @default(autoincrement())
  userId    Int
  courseId  Int
  lessonId  Int
  completed Boolean  @default(false)
  progress  Float    @default(0)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  course    Course   @relation(fields: [courseId], references: [id], onDelete: Cascade)
  lesson    Lesson   @relation(fields: [lessonId], references: [id], onDelete: Cascade)
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([userId, lessonId])
  @@index([courseId], map: "learn_records_courseId_fkey")
  @@index([lessonId], map: "learn_records_lessonId_fkey")
  @@map("learn_records")
}

model Prompt {
  id          Int              @id @default(autoincrement())
  title       String
  content     String           @db.Text
  description String?
  categoryId  Int?
  tags        String?
  authorId    Int?
  model       String?
  isPublic    Boolean          @default(true)
  status      ContentStatus    @default(PUBLISHED)
  viewCount   Int              @default(0)
  likeCount   Int              @default(0)
  createdAt   DateTime         @default(now())
  updatedAt   DateTime         @updatedAt
  deletedAt   DateTime?
  favorites   PromptFavorite[]
  author      User?            @relation(fields: [authorId], references: [id])
  category    Category?        @relation(fields: [categoryId], references: [id])

  @@index([authorId], map: "prompts_authorId_fkey")
  @@index([categoryId], map: "prompts_categoryId_fkey")
  @@map("prompts")
}

model PromptFavorite {
  id        Int      @id @default(autoincrement())
  userId    Int
  promptId  Int
  createdAt DateTime @default(now())
  prompt    Prompt   @relation(fields: [promptId], references: [id], onDelete: Cascade)
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([userId, promptId])
  @@index([promptId], map: "prompt_favorites_promptId_fkey")
  @@map("prompt_favorites")
}

model Tool {
  id            Int           @id @default(autoincrement())
  slug          String?       @unique(map: "slug") @db.VarChar(200)
  name          String
  metaTitle     String?       @db.VarChar(200)
  metaDesc      String?       @db.VarChar(300)
  description   String?
  content       String?       @db.LongText
  url           String
  icon          String?
  categoryId    Int?
  tags          String?
  isFeatured    Boolean       @default(false)
  status        ContentStatus @default(PUBLISHED)
  viewCount     Int           @default(0)
  createdAt     DateTime      @default(now())
  updatedAt     DateTime      @updatedAt
  deletedAt     DateTime?
  affiliateLink String?
  category      Category?     @relation(fields: [categoryId], references: [id])

  @@index([categoryId], map: "tools_categoryId_fkey")
  @@map("tools")
}

model Content {
  id            Int           @id @default(autoincrement())
  title         String
  summary       String?
  content       String?       @db.Text
  cover         String?
  categoryId    Int?
  tags          String?
  authorName    String?
  contentType   String        @default("article")
  status        ContentStatus @default(DRAFT)
  viewCount     Int           @default(0)
  isAiGenerated Boolean       @default(false)
  publishedAt   DateTime?
  createdAt     DateTime      @default(now())
  updatedAt     DateTime      @updatedAt
  deletedAt     DateTime?
  category      Category?     @relation(fields: [categoryId], references: [id])

  @@index([categoryId], map: "contents_categoryId_fkey")
  @@map("contents")
}

model AiModel {
  id            Int      @id @default(autoincrement())
  name          String
  provider      String
  description   String?
  capabilities  String?
  contextWindow Int?
  maxTokens     Int?
  pricing       String?
  isFree        Boolean  @default(false)
  isFeatured    Boolean  @default(false)
  icon          String?
  sortOrder     Int      @default(0)
  status        String   @default("ACTIVE")
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt

  @@map("ai_models")
}

model Skill {
  id              String      @id
  name            String
  description     String
  icon            String
  category        String
  difficulty      String
  systemPrompt    String      @db.Text
  starters        String      @db.Text
  tasks           String      @db.Text
  tags            String
  prerequisiteIds String?
  sortOrder       Int         @default(0)
  status          String      @default("ACTIVE")
  createdAt       DateTime    @default(now())
  updatedAt       DateTime    @updatedAt
  price           Float?      @default(0)
  purchasedBy     UserSkill[]

  @@map("skills")
}

model SandboxSession {
  id             Int      @id @default(autoincrement())
  userId         Int
  conversationId String   @default("")
  model          String
  title          String   @default("AI 对话")
  messages       String   @db.Text
  feedback       String?
  tokens         Int      @default(0)
  duration       Int      @default(0)
  createdAt      DateTime @default(now())
  user           User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([userId, conversationId])
  @@index([userId, createdAt])
  @@map("sandbox_sessions")
}

model Order {
  id             Int         @id @default(autoincrement())
  orderNo        String      @unique
  userId         Int
  amount         Float
  planType       String
  status         OrderStatus @default(PENDING)
  payChannel     String?
  paidAt         DateTime?
  createdAt      DateTime    @default(now())
  updatedAt      DateTime    @updatedAt
  transactionId  String?
  gatewayOrderId String?
  metadata       String?
  user           User        @relation(fields: [userId], references: [id])
  userSkills     UserSkill[]

  @@index([userId, status])
  @@index([status, createdAt])
  @@map("orders")
}

model UserSkill {
  id          Int      @id @default(autoincrement())
  userId      Int
  skillId     String
  orderId     Int?
  purchasedAt DateTime @default(now())
  order       Order?   @relation(fields: [orderId], references: [id])
  skill       Skill    @relation(fields: [skillId], references: [id])
  user        User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([userId, skillId])
  @@index([orderId], map: "user_skills_orderId_fkey")
  @@index([skillId], map: "user_skills_skillId_fkey")
  @@map("user_skills")
}

model Subscription {
  id        Int        @id @default(autoincrement())
  userId    Int
  plan      MemberPlan
  startDate DateTime
  endDate   DateTime
  status    String     @default("ACTIVE")
  createdAt DateTime   @default(now())
  updatedAt DateTime   @updatedAt
  user      User       @relation(fields: [userId], references: [id])

  @@index([userId, status])
  @@map("subscriptions")
}

model AdminRole {
  id          String      @id @default(cuid())
  name        String      @unique
  description String?
  permissions Json
  status      String      @default("ACTIVE")
  createdAt   DateTime    @default(now())
  updatedAt   DateTime    @updatedAt
  admins      AdminUser[]

  @@map("admin_roles")
}

model AdminUser {
  id           Int        @id @default(autoincrement())
  username     String     @unique
  passwordHash String
  nickname     String?
  roleId       String?
  status       String     @default("ACTIVE")
  lastLoginAt  DateTime?
  createdAt    DateTime   @default(now())
  updatedAt    DateTime   @updatedAt
  role         AdminRole? @relation(fields: [roleId], references: [id])

  @@index([roleId], map: "admin_users_roleId_fkey")
  @@map("admin_users")
}

model AdminLog {
  id        Int      @id @default(autoincrement())
  adminId   Int
  action    String
  target    String?
  detail    String?
  ip        String?
  createdAt DateTime @default(now())

  @@map("admin_logs")
}

model Post {
  id           Int        @id @default(autoincrement())
  userId       Int
  title        String
  content      String     @db.Text
  tags         String?
  circleId     Int?
  status       String     @default("PUBLISHED")
  viewCount    Int        @default(0)
  likeCount    Int        @default(0)
  commentCount Int        @default(0)
  createdAt    DateTime   @default(now())
  updatedAt    DateTime   @updatedAt
  comments     Comment[]
  likes        PostLike[]
  circle       Circle?    @relation(fields: [circleId], references: [id])
  user         User       @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId])
  @@index([status, createdAt])
  @@index([circleId])
  @@map("posts")
}

model Comment {
  id         Int       @id @default(autoincrement())
  postId     Int
  userId     Int
  content    String
  parentId   Int?
  status     String    @default("PUBLISHED")
  reviewNote String?
  reviewedAt DateTime?
  createdAt  DateTime  @default(now())
  parent     Comment?  @relation("CommentReply", fields: [parentId], references: [id])
  replies    Comment[] @relation("CommentReply")
  post       Post      @relation(fields: [postId], references: [id], onDelete: Cascade)
  user       User      @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([postId])
  @@index([status])
  @@index([parentId], map: "comments_parentId_fkey")
  @@index([userId], map: "comments_userId_fkey")
  @@map("comments")
}

model PostLike {
  id        Int      @id @default(autoincrement())
  postId    Int
  userId    Int
  createdAt DateTime @default(now())
  post      Post     @relation(fields: [postId], references: [id], onDelete: Cascade)
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([postId, userId])
  @@index([userId], map: "post_likes_userId_fkey")
  @@map("post_likes")
}

model Circle {
  id          Int            @id @default(autoincrement())
  name        String
  description String?
  tags        String?
  creatorId   Int
  isPublic    Boolean        @default(true)
  createdAt   DateTime       @default(now())
  updatedAt   DateTime       @updatedAt
  members     CircleMember[]
  creator     User           @relation("CircleCreator", fields: [creatorId], references: [id])
  posts       Post[]

  @@index([creatorId], map: "circles_creatorId_fkey")
  @@map("circles")
}

model CircleMember {
  id       Int      @id @default(autoincrement())
  circleId Int
  userId   Int
  role     String   @default("member")
  joinedAt DateTime @default(now())
  circle   Circle   @relation(fields: [circleId], references: [id], onDelete: Cascade)
  user     User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([circleId, userId])
  @@index([userId], map: "circle_members_userId_fkey")
  @@map("circle_members")
}

model Follow {
  id          Int      @id @default(autoincrement())
  followerId  Int
  followingId Int
  createdAt   DateTime @default(now())
  follower    User     @relation("Follower", fields: [followerId], references: [id], onDelete: Cascade)
  following   User     @relation("Following", fields: [followingId], references: [id], onDelete: Cascade)

  @@unique([followerId, followingId])
  @@index([followingId], map: "follows_followingId_fkey")
  @@map("follows")
}

model Organization {
  id           Int                  @id @default(autoincrement())
  name         String
  description  String?
  contactName  String?
  contactPhone String?
  logo         String?
  memberCount  Int                  @default(0)
  status       String               @default("ACTIVE")
  createdAt    DateTime             @default(now())
  updatedAt    DateTime             @updatedAt
  assignments  CourseAssignment[]
  members      OrganizationMember[]

  @@map("organizations")
}

model OrganizationMember {
  id             Int          @id @default(autoincrement())
  organizationId Int
  userId         Int
  role           String       @default("MEMBER")
  status         String       @default("ACTIVE")
  joinedAt       DateTime     @default(now())
  organization   Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  user           User         @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([organizationId, userId])
  @@index([userId], map: "organization_members_userId_fkey")
  @@map("organization_members")
}

model CourseAssignment {
  id             Int          @id @default(autoincrement())
  organizationId Int
  courseId       Int
  assignedBy     Int
  deadline       DateTime?
  createdAt      DateTime     @default(now())
  course         Course       @relation(fields: [courseId], references: [id], onDelete: Cascade)
  organization   Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)

  @@index([courseId], map: "course_assignments_courseId_fkey")
  @@index([organizationId], map: "course_assignments_organizationId_fkey")
  @@map("course_assignments")
}

model Notification {
  id        Int      @id @default(autoincrement())
  userId    Int
  type      String
  title     String
  content   String?
  link      String?
  relatedId Int?
  isRead    Boolean  @default(false)
  createdAt DateTime @default(now())
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId, isRead])
  @@map("notifications")
}

model PracticeQuestion {
  id               Int                  @id @default(autoincrement())
  skillId          String?
  scenario         String               @db.Text
  title            String
  description      String               @db.Text
  instructions     String               @db.Text
  difficulty       String               @default("BEGINNER")
  category         String               @default("general")
  expectedCriteria String               @db.Text
  starterCode      String?              @db.Text
  hint             String?              @db.Text
  sortOrder        Int                  @default(0)
  status           String               @default("PUBLISHED")
  attemptCount     Int                  @default(0)
  createdAt        DateTime             @default(now())
  updatedAt        DateTime             @updatedAt
  submissions      PracticeSubmission[]

  @@map("practice_questions")
}

model PracticeSubmission {
  id             Int              @id @default(autoincrement())
  userId         Int
  questionId     Int
  answer         String           @db.Text
  score          Int?
  maxScore       Int              @default(100)
  feedback       String?          @db.Text
  criteriaScores String?          @db.Text
  duration       Int              @default(0)
  status         String           @default("SUBMITTED")
  submittedAt    DateTime         @default(now())
  scoredAt       DateTime?
  question       PracticeQuestion @relation(fields: [questionId], references: [id], onDelete: Cascade)
  user           User             @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId])
  @@index([questionId])
  @@index([userId, status])
  @@map("practice_submissions")
}

model Banner {
  id        Int       @id @default(autoincrement())
  title     String
  image     String
  link      String?
  position  String    @default("home")
  sortOrder Int       @default(0)
  status    String    @default("DRAFT")
  startAt   DateTime?
  endAt     DateTime?
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt

  @@map("banners")
}

model SystemNotification {
  id          Int       @id @default(autoincrement())
  title       String
  content     String
  type        String    @default("system")
  target      String    @default("all")
  targetIds   String?
  status      String    @default("DRAFT")
  scheduledAt DateTime?
  sentAt      DateTime?
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt

  @@map("system_notifications")
}

model SystemConfig {
  id          String   @id @default(cuid())
  category    String
  key         String   @unique
  value       String
  description String?
  status      String   @default("ACTIVE")
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  @@map("system_configs")
}

enum UserStatus {
  ACTIVE
  INACTIVE
  BANNED
}

enum ContentStatus {
  DRAFT
  PUBLISHED
  ARCHIVED
}

enum OrderStatus {
  PENDING
  PAID
  CANCELLED
  REFUNDED
}

enum MemberPlan {
  FREE
  MONTHLY
  YEARLY
}

model AffiliateProgram {
  id                Int             @id @default(autoincrement())
  name              String
  slug              String          @unique
  provider          String?
  description       String?
  commissionRate    String?
  cookieDays        Int?
  homepageUrl       String?
  logo              String?
  active            Boolean         @default(true)
  sortOrder         Int             @default(0)
  createdAt         DateTime        @default(now())
  updatedAt         DateTime        @updatedAt
  links             AffiliateLink[]

  @@map("affiliate_programs")
}

model AffiliateLink {
  id                   Int              @id @default(autoincrement())
  programId            Int
  title                String
  description          String?
  url                  String
  thumbnail            String?
  toolId               Int?
  skillId              String?
  tags                 String?
  estimatedCommission  Float            @default(0)
  sortOrder            Int              @default(0)
  isFeatured           Boolean          @default(false)
  active               Boolean          @default(true)
  createdAt            DateTime         @default(now())
  updatedAt            DateTime         @updatedAt
  clicks               AffiliateClick[]
  program              AffiliateProgram @relation(fields: [programId], references: [id], onDelete: Cascade)

  @@index([programId])
  @@index([toolId])
  @@index([skillId])
  @@map("affiliate_links")
}

model AffiliateClick {
  id         Int       @id @default(autoincrement())
  linkId     Int
  userId     Int?
  ip         String?
  userAgent  String?
  ref        String?
  createdAt  DateTime  @default(now())
  link       AffiliateLink @relation(fields: [linkId], references: [id], onDelete: Cascade)

  @@index([linkId])
  @@map("affiliate_clicks")
}

model Donation {
  id        Int       @id @default(autoincrement())
  name      String?
  message   String?   @db.VarChar(280)
  channel   String    @default("ALIPAY")
  amount    Float?
  createdAt DateTime  @default(now())

  @@index([createdAt])
  @@map("donations")
}
