generator client { provider = "prisma-client-js" } datasource db { provider = "mysql" url = env("DATABASE_URL") } enum UserStatus { ACTIVE INACTIVE BANNED } enum ContentStatus { DRAFT PUBLISHED ARCHIVED } enum OrderStatus { PENDING PAID CANCELLED REFUNDED } enum MemberPlan { FREE MONTHLY YEARLY } model User { id Int @id @default(autoincrement()) username String? @unique 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) sandboxExtra Int @default(0) followerCount Int @default(0) followingCount Int @default(0) postCount Int @default(0) lastLoginAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? learnRecords LearnRecord[] promptFavorites PromptFavorite[] prompts Prompt[] sandboxSessions SandboxSession[] orders Order[] subscriptions Subscription[] posts Post[] comments Comment[] postLikes PostLike[] followers Follow[] @relation("Following") following Follow[] @relation("Follower") createdCircles Circle[] @relation("CircleCreator") circleMemberships CircleMember[] organizationMemberships OrganizationMember[] notifications Notification[] practiceSubmissions PracticeSubmission[] 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") courses Course[] prompts Prompt[] contents Content[] tools Tool[] @@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? category Category? @relation(fields: [categoryId], references: [id]) chapters Chapter[] progress LearnRecord[] assignments CourseAssignment[] @@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[] @@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 chapter Chapter @relation(fields: [chapterId], references: [id], onDelete: Cascade) progress LearnRecord[] @@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 user User @relation(fields: [userId], references: [id], onDelete: Cascade) course Course @relation(fields: [courseId], references: [id], onDelete: Cascade) lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade) @@unique([userId, lessonId]) @@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? category Category? @relation(fields: [categoryId], references: [id]) author User? @relation(fields: [authorId], references: [id]) favorites PromptFavorite[] @@map("prompts") } model PromptFavorite { id Int @id @default(autoincrement()) userId Int promptId Int createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) prompt Prompt @relation(fields: [promptId], references: [id], onDelete: Cascade) @@unique([userId, promptId]) @@map("prompt_favorites") } model Tool { id Int @id @default(autoincrement()) name String description String? url String icon String? affiliateLink 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? category Category? @relation(fields: [categoryId], references: [id]) @@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]) @@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? price Float? @default(0) sortOrder Int @default(0) status String @default("ACTIVE") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt 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? transactionId String? gatewayOrderId String? metadata String? paidAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt 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()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) skill Skill @relation(fields: [skillId], references: [id]) order Order? @relation(fields: [orderId], references: [id]) @@unique([userId, skillId]) @@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 @default("[]") 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? role AdminRole? @relation(fields: [roleId], references: [id]) status String @default("ACTIVE") lastLoginAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@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 user User @relation(fields: [userId], references: [id], onDelete: Cascade) comments Comment[] likes PostLike[] circle Circle? @relation(fields: [circleId], references: [id]) @@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()) post Post @relation(fields: [postId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) parent Comment? @relation("CommentReply", fields: [parentId], references: [id]) replies Comment[] @relation("CommentReply") @@index([postId]) @@index([status]) @@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]) @@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 creator User @relation("CircleCreator", fields: [creatorId], references: [id]) members CircleMember[] posts Post[] @@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]) @@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]) @@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 members OrganizationMember[] assignments CourseAssignment[] @@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]) @@map("organization_members") } model CourseAssignment { id Int @id @default(autoincrement()) organizationId Int courseId Int assignedBy Int deadline DateTime? createdAt DateTime @default(now()) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) course Course @relation(fields: [courseId], references: [id], onDelete: Cascade) @@map("course_assignments") } model Notification { id Int @id @default(autoincrement()) userId Int type String // 'like', 'comment', 'follow', 'system' 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") }