1135 lines
39 KiB
Plaintext
1135 lines
39 KiB
Plaintext
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)
|
|
isBot Boolean @default(false) @map("is_bot")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
botConfig BotConfig?
|
|
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[]
|
|
topicLikes ForumTopicLike[]
|
|
postLikes ForumPostLike[]
|
|
categoryFollows ForumCategoryFollow[]
|
|
topicBookmarks ForumTopicBookmark[]
|
|
topicDrafts ForumDraft[]
|
|
reports ForumReport[]
|
|
resolvedReports ForumReport[] @relation("ForumReportResolver")
|
|
checkIns ForumCheckIn[]
|
|
moderationLogs ForumModerationLog[] @relation("ForumModerationActor")
|
|
achievements UserAchievement[]
|
|
topicSubscriptions ForumTopicSubscription[]
|
|
|
|
@@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
|
|
MENTION
|
|
BEST_ANSWER
|
|
POST_LIKE
|
|
TOPIC_LIKE
|
|
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")
|
|
followerCount Int @default(0) @map("follower_count")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
parent ForumCategory? @relation("CategoryHierarchy", fields: [parentId], references: [id])
|
|
children ForumCategory[] @relation("CategoryHierarchy")
|
|
topics ForumTopic[]
|
|
followers ForumCategoryFollow[]
|
|
|
|
@@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")
|
|
isFeatured Boolean @default(false) @map("is_featured")
|
|
isHidden Boolean @default(false) @map("is_hidden")
|
|
viewCount Int @default(0) @map("view_count")
|
|
replyCount Int @default(0) @map("reply_count")
|
|
likeCount Int @default(0) @map("like_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[]
|
|
likes ForumTopicLike[]
|
|
tags ForumTopicTag[]
|
|
bookmarks ForumTopicBookmark[]
|
|
reports ForumReport[]
|
|
subscribers ForumTopicSubscription[]
|
|
|
|
@@index([categoryId, lastReplyAt])
|
|
@@index([categoryId, isPinned, createdAt])
|
|
@@index([userId])
|
|
@@index([isFeatured, lastReplyAt])
|
|
@@index([isHidden])
|
|
@@fulltext([title])
|
|
@@fulltext([title, content])
|
|
@@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")
|
|
isHidden Boolean @default(false) @map("is_hidden")
|
|
likeCount Int @default(0) @map("like_count")
|
|
replyToId Int? @map("reply_to_id")
|
|
replyToSnapshot String? @map("reply_to_snapshot") @db.VarChar(300)
|
|
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])
|
|
likes ForumPostLike[]
|
|
replyTo ForumPost? @relation("PostReplies", fields: [replyToId], references: [id], onDelete: SetNull)
|
|
replies ForumPost[] @relation("PostReplies")
|
|
|
|
@@index([topicId, createdAt])
|
|
@@index([userId])
|
|
@@index([replyToId])
|
|
@@index([isHidden])
|
|
@@map("forum_posts")
|
|
}
|
|
|
|
model ForumTopicLike {
|
|
id Int @id @default(autoincrement())
|
|
topicId Int @map("topic_id")
|
|
userId Int @map("user_id")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
topic ForumTopic @relation(fields: [topicId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([topicId, userId])
|
|
@@index([userId])
|
|
@@map("forum_topic_likes")
|
|
}
|
|
|
|
model ForumPostLike {
|
|
id Int @id @default(autoincrement())
|
|
postId Int @map("post_id")
|
|
userId Int @map("user_id")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
post ForumPost @relation(fields: [postId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([postId, userId])
|
|
@@index([userId])
|
|
@@map("forum_post_likes")
|
|
}
|
|
|
|
model ForumCategoryFollow {
|
|
id Int @id @default(autoincrement())
|
|
categoryId Int @map("category_id")
|
|
userId Int @map("user_id")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
category ForumCategory @relation(fields: [categoryId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([categoryId, userId])
|
|
@@index([userId])
|
|
@@map("forum_category_follows")
|
|
}
|
|
|
|
model ForumTag {
|
|
id Int @id @default(autoincrement())
|
|
name String @unique @db.VarChar(20)
|
|
slug String @unique @db.VarChar(30)
|
|
topicCount Int @default(0) @map("topic_count")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
topics ForumTopicTag[]
|
|
|
|
@@map("forum_tags")
|
|
}
|
|
|
|
model ForumTopicTag {
|
|
id Int @id @default(autoincrement())
|
|
topicId Int @map("topic_id")
|
|
tagId Int @map("tag_id")
|
|
|
|
topic ForumTopic @relation(fields: [topicId], references: [id], onDelete: Cascade)
|
|
tag ForumTag @relation(fields: [tagId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([topicId, tagId])
|
|
@@index([tagId])
|
|
@@map("forum_topic_tags")
|
|
}
|
|
|
|
model ForumDraft {
|
|
id Int @id @default(autoincrement())
|
|
userId Int @map("user_id")
|
|
categoryId Int? @map("category_id")
|
|
title String? @db.VarChar(100)
|
|
content String @db.Text
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId, updatedAt])
|
|
@@map("forum_drafts")
|
|
}
|
|
|
|
model ForumTopicBookmark {
|
|
id Int @id @default(autoincrement())
|
|
topicId Int @map("topic_id")
|
|
userId Int @map("user_id")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
topic ForumTopic @relation(fields: [topicId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([topicId, userId])
|
|
@@index([userId, createdAt])
|
|
@@map("forum_topic_bookmarks")
|
|
}
|
|
|
|
model ForumReport {
|
|
id Int @id @default(autoincrement())
|
|
reporterId Int @map("reporter_id")
|
|
topicId Int? @map("topic_id")
|
|
postId Int? @map("post_id")
|
|
reason String @db.VarChar(50)
|
|
detail String? @db.VarChar(500)
|
|
status String @default("pending") @db.VarChar(20)
|
|
resolution String? @db.VarChar(20)
|
|
resolvedBy Int? @map("resolved_by")
|
|
resolvedAt DateTime? @map("resolved_at")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
reporter User @relation(fields: [reporterId], references: [id], onDelete: Cascade)
|
|
topic ForumTopic? @relation(fields: [topicId], references: [id], onDelete: Cascade)
|
|
resolver User? @relation("ForumReportResolver", fields: [resolvedBy], references: [id], onDelete: SetNull)
|
|
|
|
@@index([status, createdAt])
|
|
@@index([reporterId])
|
|
@@index([topicId])
|
|
@@index([postId])
|
|
@@index([resolvedBy])
|
|
@@map("forum_reports")
|
|
}
|
|
|
|
model ForumCheckIn {
|
|
id Int @id @default(autoincrement())
|
|
userId Int @map("user_id")
|
|
checkInDate DateTime @map("check_in_date") @db.Date
|
|
streak Int @default(1)
|
|
pointsEarned Int @default(0) @map("points_earned")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, checkInDate])
|
|
@@index([userId, checkInDate])
|
|
@@map("forum_check_ins")
|
|
}
|
|
|
|
model ForumModerationLog {
|
|
id Int @id @default(autoincrement())
|
|
actorId Int @map("actor_id")
|
|
targetType String @map("target_type") @db.VarChar(20)
|
|
targetId Int @map("target_id")
|
|
action String @db.VarChar(30)
|
|
reason String? @db.VarChar(200)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
actor User @relation("ForumModerationActor", fields: [actorId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([actorId, createdAt])
|
|
@@index([targetType, targetId])
|
|
@@index([createdAt])
|
|
@@map("forum_moderation_logs")
|
|
}
|
|
|
|
model BotConfig {
|
|
id Int @id @default(autoincrement())
|
|
userId Int @unique @map("user_id")
|
|
displayName String @map("display_name") @db.VarChar(50)
|
|
personality Json
|
|
primaryForums Json @map("primary_forums")
|
|
activeHours Json @map("active_hours")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
memories BotMemory[]
|
|
botPersonaVariants BotPersonaVariant[] @relation("BotVariants")
|
|
adversarialLearnings BotAdversarialLearning[] @relation("BotAdversarialLearnings")
|
|
|
|
@@map("bot_configs")
|
|
}
|
|
|
|
model BotMemory {
|
|
id Int @id @default(autoincrement())
|
|
botId Int @map("bot_id")
|
|
memoryType String @map("memory_type") @db.VarChar(20)
|
|
layer String @default("LONGTERM") @map("layer") @db.VarChar(20)
|
|
content Json
|
|
importance Float @default(0.5)
|
|
contextTags Json? @map("context_tags")
|
|
ttl DateTime? @map("ttl")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
bot BotConfig @relation(fields: [botId], references: [id])
|
|
|
|
@@index([botId, layer, importance])
|
|
@@index([botId, memoryType, createdAt])
|
|
@@index([botId, createdAt])
|
|
@@map("bot_memories")
|
|
}
|
|
|
|
model BotDailyStat {
|
|
id Int @id @default(autoincrement())
|
|
botId Int @map("bot_id")
|
|
date DateTime @db.Date
|
|
topicCreated Int @default(0) @map("topic_created")
|
|
replySent Int @default(0) @map("reply_sent")
|
|
followUpSent Int @default(0) @map("follow_up_sent")
|
|
repliesReceived Int @default(0) @map("replies_received")
|
|
likesReceived Int @default(0) @map("likes_received")
|
|
humanInteractions Int @default(0) @map("human_interactions")
|
|
feedbackProcessed Int @default(0) @map("feedback_processed")
|
|
topTopicId Int? @map("top_topic_id")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
@@unique([botId, date])
|
|
@@index([date])
|
|
@@map("bot_daily_stats")
|
|
}
|
|
|
|
model BotSkill {
|
|
id Int @id @default(autoincrement())
|
|
botId Int @map("bot_id")
|
|
name String @db.VarChar(50)
|
|
category String @db.VarChar(30)
|
|
hookPattern String @map("hook_pattern") @db.Text
|
|
bodyTemplate String @map("body_template") @db.Text
|
|
applicability String? @db.Text
|
|
sourceTopicIds Json @map("source_topic_ids")
|
|
successRate Float @default(0) @map("success_rate")
|
|
usageCount Int @default(0) @map("usage_count")
|
|
avgReplies Float @default(0) @map("avg_replies")
|
|
avgLikes Float @default(0) @map("avg_likes")
|
|
version Int @default(1)
|
|
isActive Boolean @default(true) @map("is_active")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
@@index([botId, category, isActive])
|
|
@@index([botId, successRate])
|
|
@@map("bot_skills")
|
|
}
|
|
|
|
model BotWeeklyReview {
|
|
id Int @id @default(autoincrement())
|
|
botId Int @map("bot_id")
|
|
weekStart DateTime @map("week_start") @db.Date
|
|
weekEnd DateTime @map("week_end") @db.Date
|
|
topicsCreated Int @default(0) @map("topics_created")
|
|
repliesSent Int @default(0) @map("replies_sent")
|
|
repliesReceived Int @default(0) @map("replies_received")
|
|
humanRatio Float @default(0) @map("human_ratio")
|
|
likesReceived Int @default(0) @map("likes_received")
|
|
engagementScore Float @default(0) @map("engagement_score")
|
|
nextWeekQuota String @map("next_week_quota") @db.VarChar(20)
|
|
reflection String? @db.Text
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
@@unique([botId, weekStart])
|
|
@@index([botId, weekStart])
|
|
@@map("bot_weekly_reviews")
|
|
}
|
|
|
|
model BotPersona {
|
|
botId Int @id @map("bot_id")
|
|
avgReplyLength Int @default(0) @map("avg_reply_length")
|
|
questionRatio Float @default(0) @map("question_ratio")
|
|
exclamationCount Int @default(0) @map("exclamation_count")
|
|
avgTopicLength Int @default(0) @map("avg_topic_length")
|
|
stanceKeywords Json? @map("stance_keywords")
|
|
topHumanUsers Json? @map("top_human_users")
|
|
topTopicTypes Json? @map("top_topic_types")
|
|
lastTopicTitles Json? @map("last_topic_titles")
|
|
postSampleCount Int @default(0) @map("post_sample_count")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
@@map("bot_personas")
|
|
}
|
|
|
|
model BotPersonaVariant {
|
|
id Int @id @default(autoincrement())
|
|
botId Int @map("bot_id")
|
|
experimentId Int? @map("experiment_id")
|
|
variantKey String @map("variant_key") @db.VarChar(20)
|
|
label String @db.VarChar(50)
|
|
description String? @db.VarChar(200)
|
|
stanceKeywords Json? @map("stance_keywords")
|
|
topTopicTypes Json? @map("top_topic_types")
|
|
topHumanUsers Json? @map("top_human_users")
|
|
styleHints Json? @map("style_hints")
|
|
weight Float @default(0.5)
|
|
isControl Boolean @default(false) @map("is_control")
|
|
isActive Boolean @default(true) @map("is_active")
|
|
sampleCount Int @default(0) @map("sample_count")
|
|
replyCount Int @default(0) @map("reply_count")
|
|
likeCount Int @default(0) @map("like_count")
|
|
humanReplyCount Int @default(0) @map("human_reply_count")
|
|
engagementScore Float @default(0) @map("engagement_score")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
botConfig BotConfig @relation("BotVariants", fields: [botId], references: [id], onDelete: Cascade)
|
|
experiment BotPersonaExperiment? @relation("ExperimentVariants", fields: [experimentId], references: [id], onDelete: SetNull)
|
|
assignments BotPersonaAssignment[]
|
|
metrics BotPersonaMetric[]
|
|
|
|
@@unique([botId, variantKey])
|
|
@@index([botId, isActive])
|
|
@@index([experimentId])
|
|
@@map("bot_persona_variants")
|
|
}
|
|
|
|
model BotPersonaExperiment {
|
|
id Int @id @default(autoincrement())
|
|
botId Int @map("bot_id")
|
|
name String @db.VarChar(80)
|
|
description String? @db.VarChar(200)
|
|
status String @default("active") @db.VarChar(20)
|
|
winnerVariantKey String? @map("winner_variant_key") @db.VarChar(20)
|
|
minSamplesPerArm Int @default(20) @map("min_samples_per_arm")
|
|
significanceLevel Float @default(0.1) @map("significance_level")
|
|
primaryMetric String @default("engagement_score") @map("primary_metric") @db.VarChar(30)
|
|
config Json?
|
|
startedAt DateTime @default(now()) @map("started_at")
|
|
endedAt DateTime? @map("ended_at")
|
|
lastAnalyzedAt DateTime? @map("last_analyzed_at")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
variants BotPersonaVariant[] @relation("ExperimentVariants")
|
|
|
|
@@index([botId, status])
|
|
@@index([status])
|
|
@@map("bot_persona_experiments")
|
|
}
|
|
|
|
model BotPersonaAssignment {
|
|
id Int @id @default(autoincrement())
|
|
botId Int @map("bot_id")
|
|
variantId Int @map("variant_id")
|
|
refType String @map("ref_type") @db.VarChar(20)
|
|
refId Int @map("ref_id")
|
|
forumSlug String? @map("forum_slug") @db.VarChar(30)
|
|
replies Int @default(0)
|
|
likes Int @default(0)
|
|
humanReplies Int @default(0) @map("human_replies")
|
|
impressions Int @default(0)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
variant BotPersonaVariant @relation(fields: [variantId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([refType, refId])
|
|
@@index([botId, variantId])
|
|
@@index([variantId, createdAt])
|
|
@@map("bot_persona_assignments")
|
|
}
|
|
|
|
model BotPersonaMetric {
|
|
id Int @id @default(autoincrement())
|
|
variantId Int @map("variant_id")
|
|
date DateTime @db.Date
|
|
impressions Int @default(0)
|
|
replies Int @default(0)
|
|
likes Int @default(0)
|
|
humanReplies Int @default(0) @map("human_replies")
|
|
contentCount Int @default(0) @map("content_count")
|
|
avgReplies Float @default(0) @map("avg_replies")
|
|
avgLikes Float @default(0) @map("avg_likes")
|
|
computedScore Float @default(0) @map("computed_score")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
variant BotPersonaVariant @relation(fields: [variantId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([variantId, date])
|
|
@@index([date])
|
|
@@map("bot_persona_metrics")
|
|
}
|
|
|
|
model BotAdversarialLearning {
|
|
id Int @id @default(autoincrement())
|
|
botId Int @map("bot_id")
|
|
weekKey String @map("week_key") @db.VarChar(20)
|
|
sourceRefType String @map("source_ref_type") @db.VarChar(20)
|
|
sourceRefId Int @map("source_ref_id")
|
|
sourceUserId Int @map("source_user_id")
|
|
sourceUserName String? @map("source_user_name") @db.VarChar(50)
|
|
forumSlug String? @map("forum_slug") @db.VarChar(30)
|
|
sourceTitle String? @map("source_title") @db.VarChar(200)
|
|
sourceContent String @map("source_content") @db.Text
|
|
sourceMetrics Json @map("source_metrics")
|
|
relevanceScore Float @default(0) @map("relevance_score")
|
|
learnedInsight String @map("learned_insight") @db.Text
|
|
learnCategories Json? @map("learn_categories")
|
|
status String @default("active") @db.VarChar(20)
|
|
usedCount Int @default(0) @map("used_count")
|
|
learnedAt DateTime @default(now()) @map("learned_at")
|
|
expiresAt DateTime? @map("expires_at")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
botConfig BotConfig @relation("BotAdversarialLearnings", fields: [botId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([botId, weekKey])
|
|
@@index([botId, status])
|
|
@@index([weekKey])
|
|
@@index([sourceRefType, sourceRefId])
|
|
@@map("bot_adversarial_learnings")
|
|
}
|
|
|
|
model BotCrossForumAffinity {
|
|
id Int @id @default(autoincrement())
|
|
botId Int @map("bot_id")
|
|
forumSlug String @map("forum_slug") @db.VarChar(30)
|
|
affinity Float @default(0)
|
|
sampleCount Int @default(0) @map("sample_count")
|
|
reason String? @db.VarChar(100)
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
@@unique([botId, forumSlug])
|
|
@@index([botId, affinity])
|
|
@@map("bot_cross_forum_affinities")
|
|
}
|
|
|
|
model Achievement {
|
|
key String @id @db.VarChar(50)
|
|
name String @db.VarChar(50)
|
|
description String @db.VarChar(200)
|
|
icon String @db.VarChar(20)
|
|
tier String @default("bronze") @db.VarChar(20)
|
|
points Int @default(0)
|
|
category String @default("general") @db.VarChar(30)
|
|
sortOrder Int @default(0) @map("sort_order")
|
|
isActive Boolean @default(true) @map("is_active")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
users UserAchievement[]
|
|
|
|
@@index([category, isActive, sortOrder])
|
|
@@map("achievements")
|
|
}
|
|
|
|
model UserAchievement {
|
|
id Int @id @default(autoincrement())
|
|
userId Int @map("user_id")
|
|
achievementKey String @map("achievement_key") @db.VarChar(50)
|
|
progress Int @default(0)
|
|
unlockedAt DateTime? @map("unlocked_at")
|
|
notifiedAt DateTime? @map("notified_at")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
achievement Achievement @relation(fields: [achievementKey], references: [key])
|
|
|
|
@@unique([userId, achievementKey])
|
|
@@index([userId, unlockedAt])
|
|
@@index([achievementKey])
|
|
@@map("user_achievements")
|
|
}
|
|
|
|
model ForumTopicSubscription {
|
|
id Int @id @default(autoincrement())
|
|
topicId Int @map("topic_id")
|
|
userId Int @map("user_id")
|
|
notifyReply Boolean @default(true) @map("notify_reply")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
topic ForumTopic @relation(fields: [topicId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([topicId, userId])
|
|
@@index([userId, createdAt])
|
|
@@map("forum_topic_subscriptions")
|
|
}
|