# 数据库模块 ## 技术栈 - Prisma 7.8 + @prisma/adapter-mariadb - MySQL (阿里云RDS) - 连接字符串: `mysql://mohe001:***@rm-0jlbgr2rv6dj3t6jngo.mysql.rds.aliyuncs.com:3306/zhuiguang_ai` - Prisma单例模式防止热重载多实例 ## 数据模型 ### Category (工具分类) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | name | String @db.VarChar(50) | 分类名称 | | slug | String @unique @db.VarChar(50) | URL标识 | | icon | String? @db.VarChar(100) | 图标 | | parentId | Int? @map("parent_id") | 父分类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/children (自引用层级) ### SkillCategory (技能分类) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | name | String @db.VarChar(50) | 分类名称 | | slug | String @unique @db.VarChar(50) | URL标识 | | icon | String? @db.VarChar(100) | 图标 | | parentId | Int? @map("parent_id") | 父分类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/children (自引用层级) ### Tool (已发布工具) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | name | String @db.VarChar(100) | 工具名称 | | slug | String @unique @db.VarChar(100) | URL标识 | | categoryId | Int @map("category_id") | 分类ID | | description | String @db.Text | 简介 | | logoUrl | String? @map("logo_url") @db.VarChar(255) | Logo地址 | | websiteUrl | String @map("website_url") @db.VarChar(255) | 官网地址 | | pricingModel | PricingModel @default(freemium) | 定价模型 | | pricingDetail | String? @map("pricing_detail") @db.VarChar(200) | 定价详情 | | features | Json? | 功能特性(JSON) | | tags | Json? | 标签数组(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) | 状态(draft/published) | | lastCheckedAt | DateTime? @map("last_checked_at") | 最后巡检时间 | | websiteOk | Boolean @default(true) @map("website_ok") | 网站可访问 | | logoOk | Boolean @default(true) @map("logo_ok") | Logo有效 | | createdAt | DateTime @default(now()) @map("created_at") | 创建时间 | | updatedAt | DateTime @updatedAt @map("updated_at") | 更新时间 | **关系**: category, favorites, browsingHistory, reviews, collectionItems, comments ### PendingTool (待审核工具) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | name | String @db.VarChar(100) | 工具名称 | | slug | String? @db.VarChar(100) | URL标识 | | categoryId | Int? @map("category_id") | 分类ID | | description | String? @db.Text | 简介 | | logoUrl | String? @map("logo_url") @db.VarChar(255) | Logo地址 | | websiteUrl | String? @map("website_url") @db.VarChar(255) | 官网地址 | | pricingModel | PricingModel? @default(freemium) | 定价模型 | | pricingDetail | String? @map("pricing_detail") @db.VarChar(200) | 定价详情 | | features | Json? | 功能特性 | | tags | Json? | 标签数组 | | sourceUrl | String? @map("source_url") @db.VarChar(255) | 来源地址 | | status | PendingStatus @default(pending) | 状态(pending/approved/rejected) | | checkWebsiteOk | Boolean? @map("check_website_ok") | 网站可访问检查结果 | | checkLogoOk | Boolean? @map("check_logo_ok") | Logo检查结果 | | checkDescOk | Boolean? @map("check_desc_ok") | 描述检查结果 | | checkCategoryOk | Boolean? @map("check_category_ok") | 分类检查结果 | | checkDetail | Json? @map("check_detail") | 检查详情(JSON) | | createdAt | DateTime @default(now()) @map("created_at") | 创建时间 | | updatedAt | DateTime @updatedAt @map("updated_at") | 更新时间 | **关系**: category ### Skill (已发布技能) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | name | String @db.VarChar(100) | 技能名称 | | slug | String @unique @db.VarChar(100) | URL标识 | | categoryId | Int @map("category_id") | 分类ID | | description | String @db.Text | 简介 | | logoUrl | String? @map("logo_url") @db.VarChar(255) | Logo地址 | | sourceUrl | String @map("source_url") @db.VarChar(255) | 源码地址(GitHub) | | sourceType | String @default("gitee") @db.VarChar(20) | 来源类型 | | rating | Float @default(0) @db.Float | 综合评分 | | features | Json? | 功能特性(forks/language/license/topics) | | 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") | 更新时间 | | lastReviewedAt | DateTime? @map("last_reviewed_at") | 最近评测时间 | | githubStarsHistory | Json? @map("github_stars_history") | Star历史数据(90天) | | githubStars | Int @default(0) @map("github_stars") | 当前GitHub Stars | | starsChangeDay | Int @default(0) @map("stars_change_day") | 日Star变化 | | starsChangeWeek | Int @default(0) @map("stars_change_week") | 周Star变化 | | starsChangeMonth | Int @default(0) @map("stars_change_month") | 月Star变化 | | commentCount | Int @default(0) @map("comment_count") | 评论数 | | avgRating | Float @default(0) @map("avg_rating") | 平均评分 | **关系**: category, favorites, browsingHistory, reviews, collectionItems, skillReview, comments ### PendingSkill (待审核技能) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | name | String @db.VarChar(100) | 技能名称 | | slug | String? @db.VarChar(100) | URL标识 | | categoryId | Int? @map("category_id") | 分类ID | | description | String? @db.Text | 简介 | | logoUrl | String? @map("logo_url") @db.VarChar(255) | Logo地址 | | 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 ### User (用户) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | email | String @unique @db.VarChar(255) | 邮箱 | | passwordHash | String? @db.VarChar(255) | 密码哈希(bcrypt) | | name | String? @db.VarChar(100) | 昵称 | | avatarUrl | String? | 头像地址 | | oauthProvider | String? @db.VarChar(50) | OAuth提供商(github) | | oauthId | String? @unique @db.VarChar(255) | OAuth外部ID | | role | String @default("user") @db.VarChar(20) | 角色字符串(向后兼容) | | roleId | Int? @map("role_id") | 角色ID(关联Role表) | | points | Int @default(0) | 积分 | | level | UserLevel @default(BRONZE) | 用户等级 | | createdAt | DateTime @default(now()) @map("created_at") | 创建时间 | | updatedAt | DateTime @updatedAt @map("updated_at") | 更新时间 | **关系**: assignedRole, favorites, browsingHistory, reviews, collections, comments, commentLikes, notifications, forumTopics, forumPosts ### 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[]) ### 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 (角色-权限关联) | 字段 | 类型 | 说明 | |------|------|------| | roleId | Int @map("role_id") | 角色ID(联合主键) | | permissionId | Int @map("permission_id") | 权限ID(联合主键) | **关系**: role, permission (onDelete: Cascade) ### SystemConfig (系统配置) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | key | String @unique @db.VarChar(50) | 配置键 | | value | Json | 配置值(JSON) | ### SkillReview (技能评测) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | skillId | Int @unique @map("skill_id") | 关联Skill(一对一) | | capability | Float | 场景与能力评分(1-5) | | devExp | Float @map("dev_exp") | 开发体验评分(1-5) | | costLicense | Float @map("cost_license") | 成本与许可评分(1-5) | | community | Float | 社区健康评分(1-5) | | performance | Float | 性能稳定评分(1-5) | | overall | Float | 综合评分(五维均值) | | reviews | Json | 完整评测数据(含各维度summary/detail) | | 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") | 创建时间 | **关系**: skill (一对一) ### ReviewGenerationLog (评测生成日志) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | skillId | Int @map("skill_id") | 关联Skill | | status | String @db.VarChar(20) | SUCCESS/FAILED | | prompt | String @db.Text | 生成提示词 | | response | String? @db.Text | API响应 | | error | String? @db.Text | 错误信息 | | createdAt | DateTime @default(now()) @map("created_at") | 创建时间 | ### 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) | Cron表达式 | | config | Json? | 任务配置(JSON) | | updatedAt | DateTime @updatedAt @map("updated_at") | 更新时间 | ### 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) | 执行状态(completed/failed) | | startedAt | DateTime @map("started_at") | 开始时间 | | finishedAt | DateTime? @map("finished_at") | 结束时间 | | duration | Int? | 执行时长(毫秒) | | result | Json? | 执行结果(JSON) | | error | String? @db.Text | 错误信息 | | triggerBy | String @default("cron") @map("trigger_by") @db.VarChar(20) | 触发方式 | | createdAt | DateTime @default(now()) @map("created_at") | 创建时间 | ### Favorite (收藏) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | userId | Int @map("user_id") | 用户ID | | toolId | Int? @map("tool_id") | 工具ID | | skillId | Int? @map("skill_id") | 技能ID | | createdAt | DateTime @default(now()) @map("created_at") | 收藏时间 | **关系**: user, tool, skill **唯一约束**: [userId, toolId], [userId, skillId] ### BrowsingHistory (浏览历史) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | userId | Int @map("user_id") | 用户ID | | toolId | Int? @map("tool_id") | 工具ID | | skillId | Int? @map("skill_id") | 技能ID | | viewedAt | DateTime @default(now()) @updatedAt @map("viewed_at") | 浏览时间 | **关系**: user, tool, skill ### Review (评论) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | userId | Int @map("user_id") | 用户ID | | toolId | Int? @map("tool_id") | 工具ID | | skillId | Int? @map("skill_id") | 技能ID | | rating | Int? | 评分(1-5) | | comment | String @db.Text | 评论内容 | | createdAt | DateTime @default(now()) @map("created_at") | 创建时间 | **关系**: user, tool, skill ### Collection (收藏夹) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | userId | Int @map("user_id") | 用户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, items ### CollectionItem (收藏夹项目) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | collectionId | Int @map("collection_id") | 收藏夹ID | | toolId | Int? @map("tool_id") | 工具ID | | skillId | Int? @map("skill_id") | 技能ID | | addedAt | DateTime @default(now()) @map("added_at") | 添加时间 | **关系**: collection, tool, skill ### DailyReport (AI日报) | 字段 | 类型 | 说明 | |------|------|------| | 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 (日报条目) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | reportId | Int @map("report_id") | 日报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 (onDelete: Cascade) ### Comment (评论/回复) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | userId | Int @map("user_id") | 用户ID | | toolId | Int? @map("tool_id") | 工具ID | | skillId | Int? @map("skill_id") | 技能ID | | parentId | Int? @map("parent_id") | 父评论ID(回复) | | content | String @db.Text | 评论内容 | | rating | Int? | 评分(1-5) | | 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, tool, skill, parent/replies (自引用), likes ### CommentLike (评论点赞) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | commentId | Int @map("comment_id") | 评论ID | | userId | Int @map("user_id") | 用户ID | | createdAt | DateTime @default(now()) @map("created_at") | 创建时间 | **关系**: comment, user (onDelete: Cascade) **唯一约束**: [commentId, userId] ### Notification (通知) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | userId | Int @map("user_id") | 用户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 ### ForumCategory (论坛板块) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | name | String @db.VarChar(30) | 板块名称 | | slug | String @unique @db.VarChar(30) | URL标识 | | description | String? @db.VarChar(100) | 描述 | | icon | String? @db.VarChar(30) | 图标 | | sortOrder | Int @default(0) @map("sort_order") | 排序 | | topicCount | Int @default(0) @map("topic_count") | 话题数 | | createdAt | DateTime @default(now()) @map("created_at") | 创建时间 | **关系**: topics ### ForumTopic (论坛话题) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | categoryId | Int @map("category_id") | 板块ID | | userId | Int @map("user_id") | 作者ID | | title | String @db.VarChar(100) | 标题 | | slug | String @unique @db.VarChar(120) | URL标识 | | 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, user, posts ### ForumPost (论坛回复) | 字段 | 类型 | 说明 | |------|------|------| | id | Int @id @default(autoincrement()) | 主键 | | topicId | Int @map("topic_id") | 话题ID | | userId | Int @map("user_id") | 作者ID | | content | String @db.Text | 回复内容 | | createdAt | DateTime @default(now()) @map("created_at") | 创建时间 | | updatedAt | DateTime @updatedAt @map("updated_at") | 更新时间 | **关系**: topic, user ## 枚举 ### PricingModel (定价模型) - `free` — 免费 - `freemium` — 免费增值 - `paid` — 付费 - `subscription` — 订阅制 ### Status (状态) - `draft` — 草稿 - `published` — 已发布 ### PendingStatus (待审核状态) - `pending` — 待审核 - `approved` — 已通过 - `rejected` — 已拒绝 ### ReviewStatus (评测状态) - `DRAFT` — 草稿 - `PUBLISHED` — 已发布 - `REJECTED` — 已拒绝 - `OUTDATED` — 已过期 ### UserLevel (用户等级) - `BRONZE` — 青铜 - `SILVER` — 白银 - `GOLD` — 黄金 - `PLATINUM` — 铂金 ### CommentStatus (评论状态) - `ACTIVE` — 活跃 - `HIDDEN` — 隐藏 - `DELETED` — 已删除 ### NotifType (通知类型) - `COMMENT_REPLY` — 评论回复 - `COMMENT_LIKE` — 评论点赞 - `TOPIC_REPLY` — 话题回复 - `SYSTEM` — 系统通知 ## 索引 | 模型 | 索引 | 类型 | |------|------|------| | Tool | [categoryId, status] | 联合索引 | | Tool | [name, description] | 全文索引 | | Skill | [categoryId, status] | 联合索引 | | SkillReview | [skillId] | 唯一索引 | | SkillReview | [overall] | 普通索引 | | ReviewGenerationLog | [skillId] | 普通索引 | | TaskLog | [taskKey, startedAt] | 联合索引 | | TaskConfig | [taskKey] | 唯一索引 | | Favorite | [userId] | 普通索引 | | BrowsingHistory | [userId, viewedAt] | 联合索引 | | Collection | [userId] | 普通索引 | | DailyReport | [date] | 唯一索引 | | DailyReport | [status] | 普通索引 | | DailyReportItem | [reportId, sortOrder] | 联合索引 | | Comment | [toolId, createdAt] | 联合索引 | | Comment | [skillId, createdAt] | 联合索引 | | Comment | [userId] | 普通索引 | | Notification | [userId, isRead, createdAt] | 联合索引 | | ForumTopic | [categoryId, lastReplyAt] | 联合索引 | | ForumTopic | [userId] | 普通索引 | | ForumTopic | [title] | 全文索引 | | ForumPost | [topicId, createdAt] | 联合索引 | | Permission | [groupName, sortOrder] | 联合索引 | | User | [oauthProvider, oauthId] | 联合唯一 | ## 迁移历史 | # | 迁移名 | 日期 | 说明 | |---|--------|------|------| | 1 | init | 2026-04-28 | 初始化核心表(Category/Tool/PendingTool/SystemConfig/User) | | 2 | remove_scraped_at | 2026-05-01 | 移除scrapedAt字段 | | 3 | add_discovery_configs | 2026-05-01 | 添加发现配置 | | 4 | fix_tag_pool_column | 2026-05-01 | 修复标签池列 | | 5 | add_discovery_configs_v2 | 2026-05-02 | 发现配置V2 | | 6 | add_api_providers_and_skills | 2026-05-02 | 添加API提供者和技能(Skill/PendingSkill) | | 7 | add_skill_categories | 2026-05-02 | 添加技能分类(SkillCategory) | | 8 | add_pendingtool_category_relation | 2026-05-02 | 添加PendingTool分类关联 | | 9 | add_hot_score | 2026-05-08 | 添加热度评分(DailyReportItem.hotScore) | | 10 | add_skill_reviews | 2026-05-20 | 添加技能评测表+Skill扩展字段 | | 11 | add_task_system | 2026-05-24 | 添加任务调度系统+权限体系+社区功能 |