1198 lines
42 KiB
Plaintext
1198 lines
42 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?
|
||
screenshots Json? // Array of { url: string, alt?: string } - max 5 screenshots
|
||
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([status, viewCount]) // for published tools sorted by views
|
||
@@index([status, createdAt]) // for recent published tools
|
||
@@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([status, viewCount]) // for published skills sorted by views
|
||
@@index([status, createdAt]) // for recent published skills
|
||
@@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[]
|
||
|
||
apiKeys ApiKey[]
|
||
|
||
|
||
@@unique([oauthProvider, oauthId])
|
||
@@index([isBot]) // for filtering bot users
|
||
@@index([role]) // for role-based queries
|
||
@@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")
|
||
isPublic Boolean @default(false) @map("is_public")
|
||
publicSlug String? @unique @map("public_slug") @db.VarChar(100)
|
||
viewCount Int @default(0) @map("view_count")
|
||
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])
|
||
@@index([isPublic, viewCount])
|
||
@@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], onDelete: Cascade)
|
||
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
|
||
DIGEST
|
||
}
|
||
|
||
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")
|
||
// 行业智库元数据:{ kind:"thinktank", industry:<板块slug>, version: n, lastTopic, lastRoundAt }
|
||
// 主题帖 topic.content = 最新"打法"(1 楼),楼层 post = 每周研讨对话原文/真人问答
|
||
thinktank Json? @map("thinktank")
|
||
|
||
category ForumCategory @relation(fields: [categoryId], references: [id])
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
posts ForumPost[]
|
||
likes ForumTopicLike[]
|
||
tags ForumTopicTag[]
|
||
bookmarks ForumTopicBookmark[]
|
||
reports ForumReport[]
|
||
subscribers ForumTopicSubscription[]
|
||
|
||
@@index([categoryId, lastReplyAt])
|
||
@@index([categoryId, isPinned, createdAt])
|
||
@@index([categoryId, updatedAt]) // for recent topics by category
|
||
@@index([createdAt]) // for chronological queries
|
||
@@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], onDelete: Cascade)
|
||
likes ForumPostLike[]
|
||
replyTo ForumPost? @relation("PostReplies", fields: [replyToId], references: [id], onDelete: SetNull)
|
||
replies ForumPost[] @relation("PostReplies")
|
||
|
||
@@index([topicId, createdAt])
|
||
@@index([topicId, isHidden, createdAt]) // for filtered post queries
|
||
@@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")
|
||
|
||
crossForumAffinities BotCrossForumAffinity[]
|
||
|
||
@@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([botId, date]) // for bot daily stats queries
|
||
@@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")
|
||
|
||
bot BotConfig @relation(fields: [botId], references: [id], onDelete: Cascade)
|
||
|
||
@@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")
|
||
}
|
||
|
||
model Feedback {
|
||
id Int @id @default(autoincrement())
|
||
type String @default("suggestion") @db.VarChar(20)
|
||
content String @db.Text
|
||
contact String? @db.VarChar(255)
|
||
pageUrl String? @map("page_url") @db.VarChar(500)
|
||
userId String? @map("user_id") @db.VarChar(50)
|
||
status String @default("pending") @db.VarChar(20)
|
||
adminNote String? @map("admin_note") @db.Text
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([status, createdAt])
|
||
@@map("feedbacks")
|
||
}
|
||
|
||
model ApiKey {
|
||
id Int @id @default(autoincrement())
|
||
userId Int @map("user_id")
|
||
name String @db.VarChar(100)
|
||
keyHash String @unique @map("key_hash") @db.VarChar(64)
|
||
keyPrefix String @map("key_prefix") @db.VarChar(8)
|
||
tier String @default("free") @db.VarChar(20)
|
||
rateLimit Int @default(100) @map("rate_limit")
|
||
lastUsedAt DateTime? @map("last_used_at")
|
||
requestCount Int @default(0) @map("request_count")
|
||
isActive Boolean @default(true) @map("is_active")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
expiresAt DateTime? @map("expires_at")
|
||
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([userId])
|
||
@@index([keyPrefix])
|
||
@@map("api_keys")
|
||
}
|