generator client { provider = "prisma-client-js" } datasource db { provider = "mysql" } model Category { id Int @id @default(autoincrement()) name String @db.VarChar(50) slug String @unique @db.VarChar(50) icon String? @db.VarChar(100) parentId Int? @map("parent_id") sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tools Tool[] pendingTools PendingTool[] parent Category? @relation("CategoryToCategory", fields: [parentId], references: [id]) children Category[] @relation("CategoryToCategory") @@map("categories") } model SkillCategory { id Int @id @default(autoincrement()) name String @db.VarChar(50) slug String @unique @db.VarChar(50) icon String? @db.VarChar(100) parentId Int? @map("parent_id") sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") skills Skill[] pendingSkills PendingSkill[] parent SkillCategory? @relation("SkillCategoryToSkillCategory", fields: [parentId], references: [id]) children SkillCategory[] @relation("SkillCategoryToSkillCategory") @@map("skill_categories") } model Tool { id Int @id @default(autoincrement()) name String @db.VarChar(100) slug String @unique @db.VarChar(100) categoryId Int @map("category_id") description String @db.Text logoUrl String? @map("logo_url") @db.VarChar(255) websiteUrl String @map("website_url") @db.VarChar(255) pricingModel PricingModel @default(freemium) @map("pricing_model") pricingDetail String? @map("pricing_detail") @db.VarChar(200) features Json? tags Json? viewCount Int @default(0) @map("view_count") commentCount Int @default(0) @map("comment_count") avgRating Float @default(0) @map("avg_rating") status Status @default(draft) lastCheckedAt DateTime? @map("last_checked_at") websiteOk Boolean @default(true) @map("website_ok") logoOk Boolean @default(true) @map("logo_ok") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") category Category @relation(fields: [categoryId], references: [id]) favorites Favorite[] browsingHistory BrowsingHistory[] reviews Review[] collectionItems CollectionItem[] comments Comment[] @@index([categoryId, status]) @@fulltext([name, description]) @@map("tools") } model PendingTool { id Int @id @default(autoincrement()) name String @db.VarChar(100) slug String? @db.VarChar(100) categoryId Int? @map("category_id") description String? @db.Text logoUrl String? @map("logo_url") @db.VarChar(255) websiteUrl String? @map("website_url") @db.VarChar(255) pricingModel PricingModel? @default(freemium) @map("pricing_model") pricingDetail String? @map("pricing_detail") @db.VarChar(200) features Json? tags Json? sourceUrl String? @map("source_url") @db.VarChar(255) status PendingStatus @default(pending) checkWebsiteOk Boolean? @map("check_website_ok") checkLogoOk Boolean? @map("check_logo_ok") checkDescOk Boolean? @map("check_desc_ok") checkCategoryOk Boolean? @map("check_category_ok") checkDetail Json? @map("check_detail") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") category Category? @relation(fields: [categoryId], references: [id]) @@map("pending_tools") } model Skill { id Int @id @default(autoincrement()) name String @db.VarChar(100) slug String @unique @db.VarChar(100) categoryId Int @map("category_id") description String @db.Text logoUrl String? @map("logo_url") @db.VarChar(255) sourceUrl String @map("source_url") @db.VarChar(255) sourceType String @default("gitee") @map("source_type") @db.VarChar(20) rating Float @default(0) @db.Float features Json? tags Json? viewCount Int @default(0) @map("view_count") status Status @default(draft) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") category SkillCategory @relation(fields: [categoryId], references: [id]) favorites Favorite[] browsingHistory BrowsingHistory[] reviews Review[] collectionItems CollectionItem[] skillReview SkillReview? comments Comment[] lastReviewedAt DateTime? @map("last_reviewed_at") githubStarsHistory Json? @map("github_stars_history") githubStars Int @default(0) @map("github_stars") starsChangeDay Int @default(0) @map("stars_change_day") starsChangeWeek Int @default(0) @map("stars_change_week") starsChangeMonth Int @default(0) @map("stars_change_month") commentCount Int @default(0) @map("comment_count") avgRating Float @default(0) @map("avg_rating") @@index([categoryId, status]) @@map("skills") } model PendingSkill { id Int @id @default(autoincrement()) name String @db.VarChar(100) slug String? @db.VarChar(100) categoryId Int? @map("category_id") description String? @db.Text logoUrl String? @map("logo_url") @db.VarChar(255) sourceUrl String? @map("source_url") @db.VarChar(255) sourceType String? @map("source_type") @db.VarChar(20) rating Float @default(0) @db.Float features Json? tags Json? status PendingStatus @default(pending) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") category SkillCategory? @relation(fields: [categoryId], references: [id]) @@map("pending_skills") } enum PricingModel { free freemium paid subscription } enum Status { draft published } enum PendingStatus { pending approved rejected } model SystemConfig { id Int @id @default(autoincrement()) key String @unique @db.VarChar(50) value Json @@map("system_configs") } model User { id Int @id @default(autoincrement()) email String @unique @db.VarChar(255) passwordHash String? @db.VarChar(255) name String? @db.VarChar(100) avatarUrl String? bio String? @db.Text oauthProvider String? @db.VarChar(50) oauthId String? @unique @db.VarChar(255) role String @default("user") @db.VarChar(20) roleId Int? @map("role_id") points Int @default(0) level UserLevel @default(LV01) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") assignedRole Role? @relation(fields: [roleId], references: [id]) favorites Favorite[] browsingHistory BrowsingHistory[] reviews Review[] collections Collection[] comments Comment[] commentLikes CommentLike[] notifications Notification[] forumTopics ForumTopic[] forumPosts ForumPost[] pointsHistory PointsHistory[] @@unique([oauthProvider, oauthId]) @@map("users") } model PointsHistory { id Int @id @default(autoincrement()) userId Int @map("user_id") points Int reason String @db.VarChar(50) detail String? @db.VarChar(200) refType String? @map("ref_type") @db.VarChar(50) refId Int? @map("ref_id") createdAt DateTime @default(now()) @map("created_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId, createdAt]) @@map("points_history") } enum UserLevel { LV01 LV02 LV03 LV04 LV05 LV06 LV07 LV08 LV09 LV10 LV11 LV12 LV13 LV14 LV15 LV16 LV17 LV18 LV19 LV20 } model Role { id Int @id @default(autoincrement()) name String @db.VarChar(50) slug String @unique @db.VarChar(50) isSystem Boolean @default(false) @map("is_system") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") permissions RolePermission[] users User[] @@map("roles") } model Permission { id Int @id @default(autoincrement()) groupName String @map("group_name") @db.VarChar(50) name String @db.VarChar(50) key String @unique @db.VarChar(80) sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") rolePermissions RolePermission[] @@index([groupName, sortOrder]) @@map("permissions") } model RolePermission { roleId Int @map("role_id") permissionId Int @map("permission_id") role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) @@id([roleId, permissionId]) @@map("role_permissions") } model Favorite { id Int @id @default(autoincrement()) userId Int @map("user_id") toolId Int? @map("tool_id") skillId Int? @map("skill_id") createdAt DateTime @default(now()) @map("created_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) tool Tool? @relation(fields: [toolId], references: [id], onDelete: Cascade) skill Skill? @relation(fields: [skillId], references: [id], onDelete: Cascade) @@unique([userId, toolId]) @@unique([userId, skillId]) @@index([userId]) @@map("favorites") } model BrowsingHistory { id Int @id @default(autoincrement()) userId Int @map("user_id") toolId Int? @map("tool_id") skillId Int? @map("skill_id") viewedAt DateTime @default(now()) @updatedAt @map("viewed_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) tool Tool? @relation(fields: [toolId], references: [id], onDelete: Cascade) skill Skill? @relation(fields: [skillId], references: [id], onDelete: Cascade) @@unique([userId, toolId]) @@unique([userId, skillId]) @@index([userId, viewedAt]) @@map("browsing_histories") } model Review { id Int @id @default(autoincrement()) userId Int @map("user_id") toolId Int? @map("tool_id") skillId Int? @map("skill_id") rating Int? comment String @db.Text createdAt DateTime @default(now()) @map("created_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) tool Tool? @relation(fields: [toolId], references: [id], onDelete: Cascade) skill Skill? @relation(fields: [skillId], references: [id], onDelete: Cascade) @@unique([userId, toolId]) @@unique([userId, skillId]) @@index([toolId, createdAt]) @@index([skillId, createdAt]) @@map("reviews") } model Collection { id Int @id @default(autoincrement()) userId Int @map("user_id") name String @db.VarChar(50) description String? @db.Text sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) items CollectionItem[] @@index([userId]) @@map("collections") } model CollectionItem { id Int @id @default(autoincrement()) collectionId Int @map("collection_id") toolId Int? @map("tool_id") skillId Int? @map("skill_id") addedAt DateTime @default(now()) @map("added_at") collection Collection @relation(fields: [collectionId], references: [id], onDelete: Cascade) tool Tool? @relation(fields: [toolId], references: [id], onDelete: Cascade) skill Skill? @relation(fields: [skillId], references: [id], onDelete: Cascade) @@unique([collectionId, toolId]) @@unique([collectionId, skillId]) @@index([collectionId]) @@map("collection_items") } model DailyReport { id Int @id @default(autoincrement()) date DateTime @unique @db.Date title String @db.VarChar(200) summary String? @db.Text hotCount Int @default(0) @map("hot_count") coverUrl String? @map("cover_url") @db.VarChar(255) status Status @default(draft) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") items DailyReportItem[] @@index([date]) @@index([status]) @@map("daily_reports") } model DailyReportItem { id Int @id @default(autoincrement()) reportId Int @map("report_id") sortOrder Int @default(0) @map("sort_order") category String @default("tech") @db.VarChar(20) title String @db.VarChar(200) summary String? @db.Text detail String? @db.Text sourceUrl String? @map("source_url") @db.VarChar(500) imageUrl String? @map("image_url") @db.VarChar(255) hotScore Int @default(0) @map("hot_score") createdAt DateTime @default(now()) @map("created_at") report DailyReport @relation(fields: [reportId], references: [id], onDelete: Cascade) @@index([reportId, sortOrder]) @@map("daily_report_items") } model SkillReview { id Int @id @default(autoincrement()) skillId Int @unique @map("skill_id") skill Skill @relation(fields: [skillId], references: [id]) capability Float devExp Float @map("dev_exp") costLicense Float @map("cost_license") community Float performance Float overall Float reviews Json longArticle String? @map("long_article") @db.Text videoScript String? @map("video_script") @db.Text socialCards Json? @map("social_cards") status ReviewStatus @default(DRAFT) reviewedAt DateTime @updatedAt @map("reviewed_at") createdAt DateTime @default(now()) @map("created_at") @@index([skillId]) @@index([overall]) @@map("skill_reviews") } model ReviewGenerationLog { id Int @id @default(autoincrement()) skillId Int @map("skill_id") status String @db.VarChar(20) prompt String @db.Text response String? @db.Text error String? @db.Text createdAt DateTime @default(now()) @map("created_at") @@index([skillId]) @@map("review_generation_logs") } enum ReviewStatus { DRAFT PUBLISHED REJECTED OUTDATED } model TaskLog { id Int @id @default(autoincrement()) taskKey String @map("task_key") @db.VarChar(50) taskName String @map("task_name") @db.VarChar(100) status String @db.VarChar(20) startedAt DateTime @map("started_at") finishedAt DateTime? @map("finished_at") duration Int? result Json? error String? @db.Text triggerBy String @default("cron") @map("trigger_by") @db.VarChar(20) createdAt DateTime @default(now()) @map("created_at") @@index([taskKey, startedAt]) @@map("task_logs") } model TaskConfig { id Int @id @default(autoincrement()) taskKey String @unique @map("task_key") @db.VarChar(50) taskName String @map("task_name") @db.VarChar(100) description String? @db.VarChar(255) enabled Boolean @default(true) cronExpr String? @map("cron_expr") @db.VarChar(50) config Json? updatedAt DateTime @updatedAt @map("updated_at") @@map("task_configs") } model Comment { id Int @id @default(autoincrement()) userId Int @map("user_id") toolId Int? @map("tool_id") skillId Int? @map("skill_id") parentId Int? @map("parent_id") content String @db.Text rating Int? likeCount Int @default(0) @map("like_count") isPinned Boolean @default(false) @map("is_pinned") status CommentStatus @default(ACTIVE) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id]) tool Tool? @relation(fields: [toolId], references: [id], onDelete: Cascade) skill Skill? @relation(fields: [skillId], references: [id], onDelete: Cascade) parent Comment? @relation("CommentReplies", fields: [parentId], references: [id]) replies Comment[] @relation("CommentReplies") likes CommentLike[] @@index([toolId, createdAt]) @@index([skillId, createdAt]) @@index([userId]) @@map("comments") } enum CommentStatus { ACTIVE HIDDEN DELETED } model CommentLike { id Int @id @default(autoincrement()) commentId Int @map("comment_id") userId Int @map("user_id") createdAt DateTime @default(now()) @map("created_at") comment Comment @relation(fields: [commentId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([commentId, userId]) @@map("comment_likes") } model Notification { id Int @id @default(autoincrement()) userId Int @map("user_id") type NotifType title String @db.VarChar(100) content String? @db.VarChar(255) link String? @db.VarChar(255) isRead Boolean @default(false) @map("is_read") createdAt DateTime @default(now()) @map("created_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId, isRead, createdAt]) @@map("notifications") } enum NotifType { COMMENT_REPLY COMMENT_LIKE TOPIC_REPLY SYSTEM } model ForumCategory { id Int @id @default(autoincrement()) name String @db.VarChar(30) slug String @unique @db.VarChar(30) description String? @db.VarChar(100) icon String? @db.VarChar(30) parentId Int? @map("parent_id") sortOrder Int @default(0) @map("sort_order") topicCount Int @default(0) @map("topic_count") createdAt DateTime @default(now()) @map("created_at") parent ForumCategory? @relation("CategoryHierarchy", fields: [parentId], references: [id]) children ForumCategory[] @relation("CategoryHierarchy") topics ForumTopic[] @@map("forum_categories") } model ForumTopic { id Int @id @default(autoincrement()) categoryId Int @map("category_id") userId Int @map("user_id") title String @db.VarChar(100) slug String @unique @db.VarChar(120) content String @db.Text isPinned Boolean @default(false) @map("is_pinned") isLocked Boolean @default(false) @map("is_locked") viewCount Int @default(0) @map("view_count") replyCount Int @default(0) @map("reply_count") lastReplyAt DateTime? @map("last_reply_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") category ForumCategory @relation(fields: [categoryId], references: [id]) user User @relation(fields: [userId], references: [id]) posts ForumPost[] @@index([categoryId, lastReplyAt]) @@index([userId]) @@fulltext([title]) @@map("forum_topics") } model ForumPost { id Int @id @default(autoincrement()) topicId Int @map("topic_id") userId Int @map("user_id") content String @db.Text isAnswer Boolean @default(false) @map("is_answer") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") topic ForumTopic @relation(fields: [topicId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id]) @@index([topicId, createdAt]) @@map("forum_posts") }