// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // 用户表 model User { id String @id @default(cuid()) email String @unique name String? password String avatar String? isAdmin Boolean @default(false) // 是否为管理员 isActive Boolean @default(true) // 是否启用 hasSeenHelp Boolean @default(false) // 是否已查看使用帮助 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // 关系 ownedTrees FamilyTree[] @relation("TreeOwner") collaborations TreeCollaborator[] activityLogs ActivityLog[] loginLogs LoginLog[] } // 家族树表 model FamilyTree { id String @id @default(cuid()) name String description String? rootMemberId String? ownerId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // 关系 owner User @relation("TreeOwner", fields: [ownerId], references: [id], onDelete: Cascade) members FamilyMember[] collaborators TreeCollaborator[] activityLogs ActivityLog[] @@index([ownerId]) } // 协作者表 model TreeCollaborator { id String @id @default(cuid()) treeId String userId String role Role @default(VIEWER) invitedBy String invitedAt DateTime @default(now()) // 关系 tree FamilyTree @relation(fields: [treeId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([treeId, userId]) @@index([treeId]) @@index([userId]) } enum Role { OWNER EDITOR VIEWER } // 家族成员表 model FamilyMember { id String @id @default(cuid()) treeId String // 基本信息 surname String givenName String fullName String gender Gender generation Int generationName String? courtesyName String? artName String? posthumousName String? rank Int? isFounder Boolean @default(false) // 日期 birthDate String? deathDate String? isLunarDate Boolean @default(false) // 地点 ancestralHome String? birthPlace String? deathPlace String? burialPlace String? // 联系方式 phone String? telephone String? email String? address String? occupation String? // 关系 fatherId String? motherId String? spouseIds String[] @default([]) childrenIds String[] @default([]) // 配偶父母信息 spouseFatherName String? spouseMotherName String? // 内容 bio String? tags String[] @default([]) avatarUrl String? avatarImageId String? photoIds String[] @default([]) // 已弃用,用于兼容 photos Json? @default("[]") // 新格式:[{ url, caption, uploadedAt }] stories Json? @default("[]") // 家族故事 // 元数据 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // 关系 tree FamilyTree @relation(fields: [treeId], references: [id], onDelete: Cascade) // 索引优化 @@index([treeId]) @@index([fatherId]) @@index([motherId]) @@index([treeId, generation]) // 复合索引:按树和世代查询 @@index([treeId, createdAt]) // 复合索引:按树和创建时间查询 @@index([fullName]) // 姓名搜索索引 @@index([surname]) // 姓氏筛选索引 } enum Gender { MALE FEMALE } // 操作日志表 model ActivityLog { id String @id @default(cuid()) treeId String userId String action Action entityType EntityType entityId String? entityName String? changes Json? timestamp DateTime @default(now()) // 关系 tree FamilyTree @relation(fields: [treeId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([treeId]) @@index([userId]) @@index([timestamp]) } enum Action { CREATE UPDATE DELETE IMPORT EXPORT ERROR } enum EntityType { MEMBER PHOTO STORY SETTINGS TREE } // 成员历史版本表 model MemberHistory { id String @id @default(cuid()) memberId String treeId String version Int // 版本号 // 保存成员的完整快照 snapshot Json // 成员的所有字段 // 变更信息 changedBy String // 修改者ID changedAt DateTime @default(now()) changeType Action // CREATE, UPDATE, DELETE changes Json? // 具体变更的字段 { field: { old: ..., new: ... } } @@index([memberId]) @@index([treeId]) @@index([changedAt]) @@unique([memberId, version]) } // 登录日志表 model LoginLog { id String @id @default(cuid()) userId String email String userName String? ip String? userAgent String? success Boolean @default(true) message String? // 失败原因 createdAt DateTime @default(now()) // 关系 user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@index([createdAt]) }