// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" previewFeatures = ["postgresqlExtensions"] } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ================================ // Enums // ================================ enum ProjectStatus { ACTIVE ARCHIVED } enum LinkType { WEBSITE GITHUB HUGGINGFACE PAPER } enum TaskStatus { PENDING IN_PROGRESS COMPLETED FAILED } // ================================ // Models // ================================ model Project { id String @id @default(cuid()) name String nameEn String? slug String @unique description String descriptionEn String? content String? @db.Text contentEn String? @db.Text status ProjectStatus @default(ACTIVE) source String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Relations tags ProjectTag[] links ExternalLink[] discoveryTasks ProjectDiscoveryTask[] // Indexes @@index([status, createdAt], map: "idx_project_status_createdAt") @@index([slug], map: "idx_project_slug") @@map("projects") } model Tag { id String @id @default(cuid()) name String @unique nameEn String? slug String @unique createdAt DateTime @default(now()) // Relations projects ProjectTag[] // Indexes @@index([slug], map: "idx_tag_slug") @@map("tags") } model ExternalLink { id String @id @default(cuid()) type LinkType url String title String? projectId String // Relations project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) // Indexes @@index([projectId], map: "idx_link_projectId") @@index([type], map: "idx_link_type") @@index([url], map: "idx_link_url") @@index([type, url], map: "idx_link_type_url") @@unique([projectId, url]) @@map("external_links") } // Project-Tag many-to-many relationship table model ProjectTag { projectId String tagId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade) @@id([projectId, tagId]) @@index([tagId]) @@map("project_tags") } // Project Discovery Task model model ProjectDiscoveryTask { id String @id @default(cuid()) status TaskStatus @default(PENDING) // 原始数据(仅URL) sourceUrl String sourceType String @default("manual") // 探索结果 explorationData Json? explorationSummary String? @db.Text // 错误处理 errorMessage String? @db.Text retryCount Int @default(0) lastRetryAt DateTime? // 关联 projectId String? project Project? @relation(fields: [projectId], references: [id]) // 时间戳 createdAt DateTime @default(now()) startedAt DateTime? completedAt DateTime? updatedAt DateTime @updatedAt // 索引 @@index([status, createdAt], map: "idx_task_status_created") @@index([sourceUrl], map: "idx_task_source_url") @@index([projectId], map: "idx_task_project_id") @@map("project_discovery_tasks") } // ================================ // Keyword Cloud System Models // ================================ // 季度元数据表 model Quarter { id Int @id @default(autoincrement()) quarter String @unique // "2023-Q1", "2023-Q2" title String @db.Text // "2023年第一季度" titleEn String? @db.Text // "Q1 2023" subtitle String? @db.Text // "聊天界面的黎明" subtitleEn String? @db.Text // "The dawn of chat interface" displayOrder Int @default(0) // 前端排序 isActive Boolean @default(true) // 是否显示 keywords Keyword[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([quarter], map: "idx_quarter_quarter") @@index([displayOrder], map: "idx_quarter_displayOrder") @@map("quarters") } // 关键词核心数据表 model Keyword { id Int @id @default(autoincrement()) word String // "ChatGPT" trendScore Int // 0-100, 从 Google Trends 获取 // 外键关联 quarterId Int quarter Quarter @relation(fields: [quarterId], references: [id], onDelete: Cascade) // 内容字段(支持中英双语) description String @db.Text // AI 生成的一句话描述 descriptionEn String? @db.Text // 英文描述 detailPoints Json // JSON 数组: ["要点1", "要点2", "要点3"] detailPointsEn Json? // 英文版要点 // 视觉样式配置 visualConfig Json // {color, size, rotation, border} // 元数据 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([quarterId], map: "idx_keyword_quarterId") @@index([trendScore], map: "idx_keyword_trendScore") @@index([word], map: "idx_keyword_word") @@map("keywords") } // 视觉样式规则配置表 model VisualStyleRule { id Int @id @default(autoincrement()) name String @unique // "热门大词-金色" // 分数区间 minScore Int // 90 maxScore Int // 100 // 视觉属性 color String // "primary", "secondary", "accent" size String // "text-5xl", "text-3xl", "text-xl" border String // "border-4", "border-2" rotation String? // "rotate-1", "rotate-2", null // 控制 priority Int @default(0) // 优先级,分数重叠时按优先级 enabled Boolean @default(true) // 是否启用 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([enabled], map: "idx_visualStyleRule_enabled") @@index([minScore, maxScore], map: "idx_visualStyleRule_scoreRange") @@map("visual_style_rules") } // 错误日志表 model KeywordCloudErrorLog { id Int @id @default(autoincrement()) quarter String // "2023-Q1" keyword String? // "ChatGPT" errorType String // "INVALID_DATA", "API_ERROR", "DB_ERROR" errorMessage String @db.Text // 详细错误信息 rawData Json? // 原始数据便于调试 createdAt DateTime @default(now()) @@index([quarter], map: "idx_keywordCloudErrorLog_quarter") @@index([errorType], map: "idx_keywordCloudErrorLog_errorType") @@map("keyword_cloud_error_logs") }