Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 11s
Schema 新增: - coverType/coverIcon/coverColor(封面类型/系统图标/颜色) - visibility(private/public) - isPinned(置顶) - ownerType(user/official) - isVerified(认证标识) - KnowledgeBaseSubscription 表 API 新增: - POST /knowledge-bases/:id/pin(切换置顶) - PATCH /knowledge-bases/:id/visibility(切换公开/私有) - POST /knowledge-bases/:id/subscribe(订阅) - DELETE /knowledge-bases/:id/subscribe(取消订阅) - GET /knowledge-bases/subscribed(已订阅列表) - GET /knowledge-bases/discover(发现公开库) 增强: - findAll 支持 visibility/ownerType 筛选 + 置顶优先排序 - findOne 公开库允许任何人查看 - update 支持所有新字段 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1571 lines
46 KiB
Plaintext
1571 lines
46 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
|
|
}
|
|
|
|
datasource db {
|
|
provider = "mysql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
email String? @db.VarChar(255)
|
|
nickname String? @db.VarChar(100)
|
|
avatarUrl String? @db.VarChar(500)
|
|
role String @default("USER") @db.VarChar(32)
|
|
status String @default("active") @db.VarChar(32)
|
|
onboardingCompleted Boolean @default(false)
|
|
lastLoginAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
deletedAt DateTime?
|
|
|
|
authAccounts AuthAccount[]
|
|
refreshTokens RefreshToken[]
|
|
memberships UserMembership[]
|
|
profile UserProfile?
|
|
preferences UserPreference?
|
|
consents UserConsent[]
|
|
knowledgeBases KnowledgeBase[]
|
|
knowledgeItems KnowledgeItem[]
|
|
knowledgeItemRelations KnowledgeItemRelation[]
|
|
tags Tag[]
|
|
uploadedFiles UploadedFile[]
|
|
documentImports DocumentImport[]
|
|
learningSessions LearningSession[]
|
|
learningRecords LearningRecord[]
|
|
activeRecallQuestions ActiveRecallQuestion[]
|
|
activeRecallAnswers ActiveRecallAnswer[]
|
|
aiAnalysisJobs AiAnalysisJob[]
|
|
aiAnalysisResults AiAnalysisResult[]
|
|
focusItems FocusItem[]
|
|
reviewCards ReviewCard[]
|
|
reviewLogs ReviewLog[]
|
|
reviewPlans ReviewPlan[]
|
|
dailyLearningActivities DailyLearningActivity[]
|
|
notifications Notification[]
|
|
feedbacks Feedback[]
|
|
aiUsageLogs AiUsageLog[]
|
|
knowledgeSources KnowledgeSource[]
|
|
knowledgeChunks KnowledgeChunk[]
|
|
importCandidates ImportCandidate[]
|
|
|
|
@@index([email])
|
|
@@index([status])
|
|
}
|
|
|
|
model AuthAccount {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
provider String @db.VarChar(32)
|
|
providerUserId String @db.VarChar(255)
|
|
email String? @db.VarChar(255)
|
|
rawProfileJson Json?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
|
|
@@unique([provider, providerUserId])
|
|
@@index([userId])
|
|
}
|
|
|
|
model RefreshToken {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
tokenHash String @db.VarChar(255)
|
|
deviceId String? @db.VarChar(255)
|
|
deviceName String? @db.VarChar(255)
|
|
expiresAt DateTime
|
|
revokedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([tokenHash])
|
|
}
|
|
|
|
model UserProfile {
|
|
id String @id @default(cuid())
|
|
userId String @unique
|
|
learningIdentity String? @db.VarChar(100)
|
|
learningDirection String? @db.VarChar(255)
|
|
bio String? @db.Text
|
|
currentGoal String? @db.VarChar(255)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
}
|
|
|
|
model UserPreference {
|
|
id String @id @default(cuid())
|
|
userId String @unique
|
|
preferredMethods Json?
|
|
defaultFocusMinutes Int @default(25)
|
|
aiSuggestionLevel String @default("normal") @db.VarChar(32)
|
|
language String @default("zh-CN") @db.VarChar(32)
|
|
appearance String @default("system") @db.VarChar(32)
|
|
notificationEnabled Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
}
|
|
|
|
model UserConsent {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
consentType String @db.VarChar(32)
|
|
version String @db.VarChar(50)
|
|
acceptedAt DateTime
|
|
ipAddress String? @db.VarChar(100)
|
|
userAgent String? @db.VarChar(500)
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([consentType])
|
|
}
|
|
|
|
model Workspace {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
name String @db.VarChar(255)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
model KnowledgeFolder {
|
|
id String @id @default(cuid())
|
|
knowledgeBaseId String
|
|
parentId String?
|
|
name String @db.VarChar(255)
|
|
sortOrder Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
deletedAt DateTime?
|
|
|
|
knowledgeBase KnowledgeBase @relation(fields: [knowledgeBaseId], references: [id])
|
|
parent KnowledgeFolder? @relation("FolderTree", fields: [parentId], references: [id])
|
|
children KnowledgeFolder[] @relation("FolderTree")
|
|
|
|
@@index([knowledgeBaseId])
|
|
@@index([parentId])
|
|
}
|
|
|
|
model KnowledgeBase {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
title String @db.VarChar(255)
|
|
description String? @db.Text
|
|
coverKey String? @db.VarChar(100)
|
|
coverType String @default("custom") @db.VarChar(32)
|
|
coverIcon String? @db.VarChar(50)
|
|
coverColor String? @db.VarChar(20)
|
|
visibility String @default("private") @db.VarChar(16)
|
|
isPinned Boolean @default(false)
|
|
ownerType String @default("user") @db.VarChar(16)
|
|
isVerified Boolean @default(false)
|
|
status String @default("active") @db.VarChar(32)
|
|
itemCount Int @default(0)
|
|
lastStudiedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
deletedAt DateTime?
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
items KnowledgeItem[]
|
|
sources KnowledgeSource[]
|
|
candidates ImportCandidate[]
|
|
chunks KnowledgeChunk[]
|
|
focusItems FocusItem[]
|
|
folders KnowledgeFolder[]
|
|
subscriptions KnowledgeBaseSubscription[]
|
|
|
|
@@index([userId])
|
|
@@index([status])
|
|
@@index([visibility])
|
|
@@index([ownerType])
|
|
}
|
|
|
|
model KnowledgeBaseSubscription {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
knowledgeBaseId String
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
knowledgeBase KnowledgeBase @relation(fields: [knowledgeBaseId], references: [id])
|
|
|
|
@@unique([userId, knowledgeBaseId])
|
|
@@index([userId])
|
|
@@index([knowledgeBaseId])
|
|
}
|
|
|
|
model Artifact {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
kbId String
|
|
type String @db.VarChar(32)
|
|
title String @db.VarChar(255)
|
|
configJson Json?
|
|
status String @default("draft") @db.VarChar(16)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([userId])
|
|
@@index([kbId])
|
|
}
|
|
|
|
model KnowledgeItem {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
knowledgeBaseId String
|
|
parentId String?
|
|
itemType String @db.VarChar(32)
|
|
title String @db.VarChar(255)
|
|
content String? @db.LongText
|
|
summary String? @db.Text
|
|
learnable Boolean @default(true)
|
|
sourceType String? @db.VarChar(32)
|
|
sourceRef String? @db.VarChar(500)
|
|
sourceDeleted Boolean @default(false)
|
|
sourceTitleSnapshot String? @db.VarChar(255)
|
|
sourceSnippetSnapshot String? @db.Text
|
|
orderIndex Int @default(0)
|
|
status String @default("active") @db.VarChar(32)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
deletedAt DateTime?
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
knowledgeBase KnowledgeBase @relation(fields: [knowledgeBaseId], references: [id])
|
|
parent KnowledgeItem? @relation("KnowledgeItemRelations", fields: [parentId], references: [id])
|
|
children KnowledgeItem[] @relation("KnowledgeItemRelations")
|
|
tags KnowledgeItemTag[]
|
|
|
|
@@index([userId])
|
|
@@index([knowledgeBaseId])
|
|
@@index([parentId])
|
|
@@index([itemType])
|
|
}
|
|
|
|
model KnowledgeItemRelation {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
sourceItemId String
|
|
targetItemId String
|
|
relationType String @db.VarChar(32)
|
|
confidence Decimal? @db.Decimal(5, 2)
|
|
reason String? @db.Text
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
|
|
@@index([sourceItemId])
|
|
@@index([targetItemId])
|
|
}
|
|
|
|
model Tag {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
name String @db.VarChar(100)
|
|
color String? @db.VarChar(32)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
items KnowledgeItemTag[]
|
|
|
|
@@unique([userId, name])
|
|
}
|
|
|
|
model KnowledgeItemTag {
|
|
id String @id @default(cuid())
|
|
knowledgeItemId String
|
|
tagId String
|
|
createdAt DateTime @default(now())
|
|
|
|
knowledgeItem KnowledgeItem @relation(fields: [knowledgeItemId], references: [id])
|
|
tag Tag @relation(fields: [tagId], references: [id])
|
|
|
|
@@unique([knowledgeItemId, tagId])
|
|
}
|
|
|
|
model UploadedFile {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
filename String @db.VarChar(255)
|
|
mimeType String? @db.VarChar(100)
|
|
storagePath String @db.VarChar(500)
|
|
objectKey String? @db.VarChar(500)
|
|
bucket String? @db.VarChar(100)
|
|
sizeBytes BigInt @default(0)
|
|
checksum String? @db.VarChar(255)
|
|
sha256 String? @db.VarChar(64)
|
|
purpose String? @db.VarChar(32)
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
sources KnowledgeSource[]
|
|
|
|
@@index([userId])
|
|
@@index([objectKey])
|
|
@@index([sha256])
|
|
}
|
|
|
|
model DocumentImport {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
knowledgeBaseId String?
|
|
sourceId String?
|
|
fileId String?
|
|
sourceType String @db.VarChar(32)
|
|
sourceName String? @db.VarChar(255)
|
|
sourceUrl String? @db.VarChar(500)
|
|
rawText String? @db.LongText
|
|
status String @default("QUEUED") @db.VarChar(32)
|
|
step String? @db.VarChar(32)
|
|
progress Int @default(0)
|
|
workerId String? @db.VarChar(255)
|
|
retryCount Int @default(0)
|
|
maxRetries Int @default(3)
|
|
heartbeatAt DateTime?
|
|
errorCode String? @db.VarChar(32)
|
|
errorMessage String? @db.Text
|
|
resultJson Json?
|
|
startedAt DateTime?
|
|
completedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
source KnowledgeSource? @relation(fields: [sourceId], references: [id])
|
|
candidates ImportCandidate[]
|
|
|
|
@@index([userId])
|
|
@@index([status])
|
|
@@index([sourceId])
|
|
@@index([workerId])
|
|
}
|
|
|
|
model ImportStepLog {
|
|
id String @id @default(cuid())
|
|
importId String
|
|
step String @db.VarChar(32)
|
|
status String @db.VarChar(16)
|
|
detail String? @db.VarChar(500)
|
|
startedAt DateTime?
|
|
completedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([importId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model LearningSession {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
knowledgeBaseId String?
|
|
knowledgeItemId String?
|
|
mode String @db.VarChar(32)
|
|
status String @default("active") @db.VarChar(32)
|
|
startedAt DateTime
|
|
endedAt DateTime?
|
|
durationSeconds Int @default(0)
|
|
focusMinutes Int?
|
|
metadata Json?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([knowledgeItemId])
|
|
@@index([startedAt])
|
|
}
|
|
|
|
model LearningRecord {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
sessionId String?
|
|
recordType String @db.VarChar(32)
|
|
title String @db.VarChar(255)
|
|
description String? @db.Text
|
|
durationSeconds Int @default(0)
|
|
occurredAt DateTime
|
|
metadata Json?
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([occurredAt])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model ActiveRecallQuestion {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
knowledgeItemId String?
|
|
questionText String @db.Text
|
|
difficulty String? @db.VarChar(32)
|
|
createdBy String @default("ai") @db.VarChar(32)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
answers ActiveRecallAnswer[]
|
|
|
|
@@index([userId])
|
|
@@index([knowledgeItemId])
|
|
}
|
|
|
|
model ActiveRecallAnswer {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
questionId String?
|
|
sessionId String?
|
|
answerType String @default("text") @db.VarChar(32)
|
|
answerText String? @db.LongText
|
|
audioFileId String?
|
|
submittedAt DateTime
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
question ActiveRecallQuestion? @relation(fields: [questionId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([questionId])
|
|
@@index([sessionId])
|
|
}
|
|
|
|
model AiAnalysisJob {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
sessionId String?
|
|
answerId String?
|
|
jobType String @db.VarChar(32)
|
|
status String @default("pending") @db.VarChar(32)
|
|
progress Int @default(0)
|
|
errorMessage String? @db.Text
|
|
queuedAt DateTime?
|
|
startedAt DateTime?
|
|
completedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
results AiAnalysisResult[]
|
|
|
|
@@index([userId])
|
|
@@index([status])
|
|
@@index([sessionId])
|
|
}
|
|
|
|
model AiAnalysisResult {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
jobId String
|
|
sessionId String?
|
|
answerId String?
|
|
summary String? @db.Text
|
|
masteryScore Int?
|
|
strengths Json?
|
|
weaknesses Json?
|
|
suggestions Json?
|
|
nextActions Json?
|
|
rawResult Json?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
job AiAnalysisJob @relation(fields: [jobId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([jobId])
|
|
@@index([sessionId])
|
|
}
|
|
|
|
model FocusItem {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
knowledgeBaseId String?
|
|
knowledgeItemId String?
|
|
analysisResultId String?
|
|
title String @db.VarChar(255)
|
|
reason String? @db.Text
|
|
suggestion String? @db.Text
|
|
priority String @default("normal") @db.VarChar(32)
|
|
status String @default("open") @db.VarChar(32)
|
|
source String? @db.VarChar(32)
|
|
masteryScore Int?
|
|
dueAt DateTime?
|
|
completedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
deletedAt DateTime?
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
knowledgeBase KnowledgeBase? @relation(fields: [knowledgeBaseId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([status])
|
|
@@index([dueAt])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model ReviewCard {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
knowledgeItemId String?
|
|
focusItemId String?
|
|
frontText String @db.Text
|
|
backText String? @db.Text
|
|
difficulty String? @db.VarChar(32)
|
|
status String @default("active") @db.VarChar(32)
|
|
nextReviewAt DateTime?
|
|
intervalDays Int @default(1)
|
|
easeFactor Decimal @default(2.50) @db.Decimal(4, 2)
|
|
repetitionCount Int @default(0)
|
|
lapseCount Int @default(0)
|
|
scheduleState String? @db.VarChar(16)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
deletedAt DateTime?
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
logs ReviewLog[]
|
|
|
|
@@index([userId])
|
|
@@index([nextReviewAt])
|
|
@@index([focusItemId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model ReviewLog {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
reviewCardId String
|
|
sessionId String?
|
|
rating String @db.VarChar(32)
|
|
responseText String? @db.Text
|
|
reviewedAt DateTime
|
|
nextReviewAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
reviewCard ReviewCard @relation(fields: [reviewCardId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([reviewCardId])
|
|
@@index([reviewedAt])
|
|
}
|
|
|
|
model ReviewPlan {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
title String @db.VarChar(255)
|
|
status String @default("active") @db.VarChar(32)
|
|
scheduledAt DateTime?
|
|
completedAt DateTime?
|
|
cardCount Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([scheduledAt])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model DailyLearningActivity {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
activityDate DateTime @db.Date
|
|
durationSeconds Int @default(0)
|
|
sessionsCount Int @default(0)
|
|
activeRecallCount Int @default(0)
|
|
reviewCount Int @default(0)
|
|
aiAnalysisCount Int @default(0)
|
|
completedLoopCount Int @default(0)
|
|
activityLevel Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
|
|
@@unique([userId, activityDate])
|
|
@@index([userId])
|
|
}
|
|
|
|
model Notification {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
type String @db.VarChar(32)
|
|
title String @db.VarChar(255)
|
|
content String? @db.Text
|
|
data Json?
|
|
scope String @default("user") @db.VarChar(16)
|
|
readAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([readAt])
|
|
@@index([type])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model Feedback {
|
|
id String @id @default(cuid())
|
|
userId String?
|
|
email String? @db.VarChar(255)
|
|
category String @db.VarChar(64)
|
|
content String @db.Text
|
|
deviceInfo Json?
|
|
status String @default("open") @db.VarChar(32)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User? @relation(fields: [userId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([status])
|
|
}
|
|
|
|
model AiUsageLog {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
feature String @db.VarChar(64)
|
|
provider String @db.VarChar(32)
|
|
model String @db.VarChar(100)
|
|
tier String @db.VarChar(32)
|
|
promptKey String @db.VarChar(128)
|
|
promptVersion String @db.VarChar(32)
|
|
inputTokens Int @default(0)
|
|
outputTokens Int @default(0)
|
|
estimatedCost Float @default(0)
|
|
latencyMs Int @default(0)
|
|
success Boolean @default(true)
|
|
errorMessage String? @db.VarChar(500)
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([feature])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model ModelRoute {
|
|
id String @id @default(cuid())
|
|
tier String @db.VarChar(32)
|
|
taskType String @default("*") @db.VarChar(32)
|
|
preferredProvider String @db.VarChar(32)
|
|
preferredModel String @db.VarChar(100)
|
|
fallbackProvider String @db.VarChar(32)
|
|
fallbackModel String @db.VarChar(100)
|
|
maxRetries Int @default(2)
|
|
isActive Boolean @default(true)
|
|
createdBy String? @db.VarChar(100)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([tier, taskType])
|
|
}
|
|
|
|
model ProviderConfig {
|
|
id String @id @default(cuid())
|
|
name String @unique @db.VarChar(32)
|
|
enabled Boolean @default(true)
|
|
baseUrl String? @db.VarChar(255)
|
|
updatedBy String? @db.VarChar(100)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model FallbackEvent {
|
|
id String @id @default(cuid())
|
|
tier String @db.VarChar(32)
|
|
taskType String @db.VarChar(32)
|
|
fromProvider String @db.VarChar(32)
|
|
fromModel String @db.VarChar(100)
|
|
toProvider String @db.VarChar(32)
|
|
toModel String @db.VarChar(100)
|
|
errorMessage String? @db.VarChar(500)
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model WaitlistEntry {
|
|
id String @id @default(cuid())
|
|
nickname String @db.VarChar(100)
|
|
email String @db.VarChar(255)
|
|
devices Json?
|
|
interests Json?
|
|
painpoint String? @db.Text
|
|
willingBeta Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([email])
|
|
}
|
|
|
|
model AppChangelog {
|
|
id String @id @default(cuid())
|
|
version String @db.VarChar(50)
|
|
title String @db.VarChar(255)
|
|
content String @db.Text
|
|
platform String @default("ios") @db.VarChar(32)
|
|
publishedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
// ── 知识库新增模型 ──
|
|
|
|
model KnowledgeSource {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
knowledgeBaseId String
|
|
fileId String?
|
|
type String @default("file") @db.VarChar(32)
|
|
title String? @db.VarChar(255)
|
|
originalFilename String? @db.VarChar(255)
|
|
mimeType String? @db.VarChar(100)
|
|
sizeBytes BigInt @default(0)
|
|
textLength Int @default(0)
|
|
parseStatus String @default("pending") @db.VarChar(32)
|
|
indexStatus String @default("pending") @db.VarChar(32)
|
|
learningStatus String @default("pending") @db.VarChar(32)
|
|
parsedObjectKey String? @db.VarChar(500)
|
|
metadataObjectKey String? @db.VarChar(500)
|
|
originalObjectKey String? @db.VarChar(500)
|
|
version Int @default(1)
|
|
parentSourceId String?
|
|
replacedBySourceId String?
|
|
errorCode String? @db.VarChar(32)
|
|
errorMessage String? @db.Text
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
deletedAt DateTime?
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
knowledgeBase KnowledgeBase @relation(fields: [knowledgeBaseId], references: [id])
|
|
file UploadedFile? @relation(fields: [fileId], references: [id])
|
|
chunks KnowledgeChunk[]
|
|
imports DocumentImport[]
|
|
references SourceReference[]
|
|
candidates ImportCandidate[]
|
|
|
|
@@index([userId])
|
|
@@index([knowledgeBaseId])
|
|
@@index([fileId])
|
|
@@index([parseStatus])
|
|
@@index([indexStatus])
|
|
}
|
|
|
|
model KnowledgeChunk {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
knowledgeBaseId String
|
|
sourceId String
|
|
content String @db.LongText
|
|
chunkIndex Int
|
|
pageNumber Int?
|
|
sectionTitle String? @db.VarChar(500)
|
|
tokenCount Int @default(0)
|
|
externalVectorId String? @db.VarChar(255)
|
|
embeddingModel String? @db.VarChar(100)
|
|
embeddingStatus String @default("pending") @db.VarChar(32)
|
|
metadataJson Json?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
deletedAt DateTime?
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
knowledgeBase KnowledgeBase @relation(fields: [knowledgeBaseId], references: [id])
|
|
source KnowledgeSource @relation(fields: [sourceId], references: [id])
|
|
references SourceReference[]
|
|
|
|
@@index([userId])
|
|
@@index([sourceId])
|
|
@@index([knowledgeBaseId])
|
|
@@index([externalVectorId])
|
|
}
|
|
|
|
model SourceReference {
|
|
id String @id @default(cuid())
|
|
sourceId String
|
|
chunkId String?
|
|
artifactType String @db.VarChar(32)
|
|
artifactId String @db.VarChar(100)
|
|
pageNumber Int?
|
|
sectionTitle String? @db.VarChar(500)
|
|
excerptText String? @db.VarChar(2000)
|
|
createdAt DateTime @default(now())
|
|
|
|
source KnowledgeSource @relation(fields: [sourceId], references: [id])
|
|
chunk KnowledgeChunk? @relation(fields: [chunkId], references: [id])
|
|
|
|
@@index([artifactType, artifactId])
|
|
@@index([sourceId])
|
|
}
|
|
|
|
model ImportCandidate {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
knowledgeBaseId String
|
|
sourceId String
|
|
importId String
|
|
title String @db.VarChar(255)
|
|
summary String? @db.Text
|
|
content String? @db.LongText
|
|
tagsJson Json?
|
|
recallQuestionsJson Json?
|
|
sourceTextSnippet String? @db.Text
|
|
sourceChunkIds Json?
|
|
confidence Decimal @default(0) @db.Decimal(4, 3)
|
|
difficulty String? @db.VarChar(16)
|
|
orderIndex Int @default(0)
|
|
status String @default("PENDING") @db.VarChar(16)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
knowledgeBase KnowledgeBase @relation(fields: [knowledgeBaseId], references: [id])
|
|
source KnowledgeSource @relation(fields: [sourceId], references: [id])
|
|
import DocumentImport @relation(fields: [importId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([sourceId])
|
|
@@index([importId])
|
|
@@index([status])
|
|
}
|
|
|
|
model BackupJob {
|
|
id String @id @default(cuid())
|
|
type String @db.VarChar(16)
|
|
status String @default("RUNNING") @db.VarChar(16)
|
|
localPath String? @db.VarChar(500)
|
|
cosObjectKey String? @db.VarChar(500)
|
|
fileSizeBytes BigInt @default(0)
|
|
startedAt DateTime @default(now())
|
|
completedAt DateTime?
|
|
errorMessage String? @db.Text
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model CleanupJob {
|
|
id String @id @default(cuid())
|
|
type String @db.VarChar(32)
|
|
status String @default("RUNNING") @db.VarChar(16)
|
|
target String? @db.VarChar(255)
|
|
rowsAffected Int @default(0)
|
|
startedAt DateTime @default(now())
|
|
completedAt DateTime?
|
|
errorMessage String? @db.Text
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model AdminUser {
|
|
id String @id @default(cuid())
|
|
email String @unique @db.VarChar(255)
|
|
passwordHash String @db.VarChar(255)
|
|
displayName String @db.VarChar(100)
|
|
role String @default("ADMIN") @db.VarChar(32)
|
|
status String @default("ACTIVE") @db.VarChar(32)
|
|
twoFactorEnabled Boolean @default(false)
|
|
twoFactorSecret String? @db.VarChar(100)
|
|
lastLoginAt DateTime?
|
|
lastLoginIp String? @db.VarChar(45)
|
|
failedLoginCount Int @default(0)
|
|
lockedUntil DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
deletedAt DateTime?
|
|
|
|
sessions AdminSession[]
|
|
conversations AdminConversation[]
|
|
auditLogs AdminAuditLog[]
|
|
|
|
@@index([email])
|
|
@@index([status])
|
|
}
|
|
|
|
model AdminSession {
|
|
id String @id @default(cuid())
|
|
adminUserId String
|
|
refreshTokenHash String @db.VarChar(255)
|
|
ip String? @db.VarChar(45)
|
|
userAgent String? @db.VarChar(500)
|
|
expiresAt DateTime
|
|
revokedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
adminUser AdminUser @relation(fields: [adminUserId], references: [id])
|
|
|
|
@@index([adminUserId])
|
|
@@index([refreshTokenHash])
|
|
}
|
|
|
|
model AdminAuditLog {
|
|
id String @id @default(cuid())
|
|
adminUserId String
|
|
action String @db.VarChar(64)
|
|
resourceType String? @db.VarChar(64)
|
|
resourceId String? @db.VarChar(255)
|
|
beforeJson Json?
|
|
afterJson Json?
|
|
ip String? @db.VarChar(45)
|
|
userAgent String? @db.VarChar(500)
|
|
riskLevel String? @db.VarChar(16)
|
|
reason String? @db.VarChar(500)
|
|
createdAt DateTime @default(now())
|
|
|
|
adminUser AdminUser @relation(fields: [adminUserId], references: [id])
|
|
|
|
@@index([adminUserId])
|
|
@@index([action])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model MembershipPlan {
|
|
id String @id @default(cuid())
|
|
code String @unique @db.VarChar(32)
|
|
name String @db.VarChar(100)
|
|
priceMonthly Int @default(0)
|
|
priceYearly Int @default(0)
|
|
maxKnowledgeBases Int @default(1)
|
|
maxStorageBytes BigInt @default(0)
|
|
maxFileSizeBytes BigInt @default(0)
|
|
monthlyOcrPages Int @default(0)
|
|
monthlyVisionPages Int @default(0)
|
|
monthlyChatCount Int @default(0)
|
|
monthlyAiAnalysisCount Int @default(0)
|
|
monthlyRecallCount Int @default(0)
|
|
monthlyCardGenCount Int @default(0)
|
|
isActive Boolean @default(true)
|
|
memberships UserMembership[]
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model AdminConversation {
|
|
id String @id @default(cuid())
|
|
adminUserId String
|
|
title String @default("新对话") @db.VarChar(200)
|
|
hermesSessionId String @unique @db.VarChar(64)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
deletedAt DateTime?
|
|
messages AdminMessage[]
|
|
|
|
adminUser AdminUser @relation(fields: [adminUserId], references: [id])
|
|
|
|
@@index([adminUserId])
|
|
@@index([hermesSessionId])
|
|
}
|
|
|
|
model AdminMessage {
|
|
id String @id @default(cuid())
|
|
conversationId String
|
|
role String @db.VarChar(16)
|
|
content String @db.LongText
|
|
createdAt DateTime @default(now())
|
|
|
|
conversation AdminConversation @relation(fields: [conversationId], references: [id])
|
|
|
|
@@index([conversationId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model ChatSession {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
knowledgeBaseId String
|
|
title String @default("新对话") @db.VarChar(200)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
messages ChatMessage[]
|
|
|
|
@@index([userId])
|
|
@@index([knowledgeBaseId])
|
|
}
|
|
|
|
model ChatMessage {
|
|
id String @id @default(cuid())
|
|
sessionId String
|
|
role String @db.VarChar(16)
|
|
content String @db.LongText
|
|
tokens Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
|
|
session ChatSession @relation(fields: [sessionId], references: [id])
|
|
citations ChatCitation[]
|
|
|
|
@@index([sessionId])
|
|
}
|
|
|
|
model ChatCitation {
|
|
id String @id @default(cuid())
|
|
messageId String
|
|
chunkId String?
|
|
sourceId String?
|
|
sourceTitle String? @db.VarChar(255)
|
|
excerptText String? @db.VarChar(2000)
|
|
pageNumber Int?
|
|
createdAt DateTime @default(now())
|
|
|
|
message ChatMessage @relation(fields: [messageId], references: [id])
|
|
|
|
@@index([messageId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model AdminCostItem {
|
|
id String @id @default(cuid())
|
|
name String @db.VarChar(100)
|
|
category String @default("other") @db.VarChar(32)
|
|
amount Float
|
|
currency String @default("CNY") @db.VarChar(8)
|
|
purchaseDate DateTime
|
|
expiryDate DateTime?
|
|
billingCycle String @default("once") @db.VarChar(16)
|
|
note String? @db.VarChar(255)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([category])
|
|
@@index([expiryDate])
|
|
}
|
|
|
|
model AppConfig {
|
|
id String @id @default(cuid())
|
|
key String @unique @db.VarChar(100)
|
|
value String @db.Text
|
|
description String? @db.VarChar(500)
|
|
environment String @default("production") @db.VarChar(32)
|
|
updatedBy String? @db.VarChar(100)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([key])
|
|
}
|
|
|
|
model FeatureFlag {
|
|
id String @id @default(cuid())
|
|
name String @unique @db.VarChar(100)
|
|
enabled Boolean @default(false)
|
|
description String? @db.VarChar(500)
|
|
rolloutPct Int @default(100)
|
|
whitelist String? @db.Text
|
|
updatedBy String? @db.VarChar(100)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([name])
|
|
}
|
|
|
|
model ConfigChangeLog {
|
|
id String @id @default(cuid())
|
|
entityType String @db.VarChar(32)
|
|
entityId String @db.VarChar(100)
|
|
field String @db.VarChar(100)
|
|
oldValue String? @db.Text
|
|
newValue String? @db.Text
|
|
changedBy String? @db.VarChar(100)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([entityType, entityId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model SecurityEvent {
|
|
id String @id @default(cuid())
|
|
userId String?
|
|
adminUserId String?
|
|
eventType String @db.VarChar(64)
|
|
severity String @default("low") @db.VarChar(16)
|
|
ip String? @db.VarChar(45)
|
|
userAgent String? @db.VarChar(500)
|
|
detail Json?
|
|
handled Boolean @default(false)
|
|
handledBy String? @db.VarChar(100)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
@@index([eventType])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model SensitiveWord {
|
|
id String @id @default(cuid())
|
|
word String @unique @db.VarChar(100)
|
|
category String @default("general") @db.VarChar(32)
|
|
riskLevel String @default("medium") @db.VarChar(16)
|
|
enabled Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([word])
|
|
@@index([category])
|
|
}
|
|
|
|
model ContentSafetyCheck {
|
|
id String @id @default(cuid())
|
|
userId String? @db.VarChar(100)
|
|
contentType String @db.VarChar(32)
|
|
content String @db.Text
|
|
riskLevel String @db.VarChar(16)
|
|
matchedWords String? @db.Text
|
|
result String @default("pending") @db.VarChar(16)
|
|
reviewerId String? @db.VarChar(100)
|
|
reviewNote String? @db.VarChar(500)
|
|
createdAt DateTime @default(now())
|
|
reviewedAt DateTime?
|
|
|
|
@@index([userId])
|
|
@@index([result])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model ContentReport {
|
|
id String @id @default(cuid())
|
|
reporterId String
|
|
targetType String @db.VarChar(32)
|
|
targetId String @db.VarChar(100)
|
|
reason String @db.VarChar(500)
|
|
status String @default("pending") @db.VarChar(16)
|
|
handledBy String? @db.VarChar(100)
|
|
handleNote String? @db.VarChar(500)
|
|
createdAt DateTime @default(now())
|
|
handledAt DateTime?
|
|
|
|
@@index([status])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model ViolationRecord {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
contentType String @db.VarChar(32)
|
|
content String @db.VarChar(1000)
|
|
riskLevel String @db.VarChar(16)
|
|
penalty String @default("none") @db.VarChar(32)
|
|
appliedBy String? @db.VarChar(100)
|
|
appliedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model ApiMetric {
|
|
id String @id @default(cuid())
|
|
path String @db.VarChar(255)
|
|
method String @db.VarChar(10)
|
|
statusCode Int
|
|
duration Int
|
|
userId String? @db.VarChar(100)
|
|
ip String? @db.VarChar(45)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([path])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model TaskLog {
|
|
id String @id @default(cuid())
|
|
queueName String @db.VarChar(64)
|
|
jobId String @db.VarChar(100)
|
|
status String @default("enqueued") @db.VarChar(16)
|
|
payload Json?
|
|
error String? @db.Text
|
|
attempts Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([queueName])
|
|
@@index([status])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model UserMembership {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
planId String
|
|
startedAt DateTime @default(now())
|
|
expiresAt DateTime?
|
|
active Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id])
|
|
plan MembershipPlan @relation(fields: [planId], references: [id])
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
model UserDevice {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
deviceId String @db.VarChar(255)
|
|
deviceName String? @db.VarChar(100)
|
|
osVersion String? @db.VarChar(50)
|
|
pushToken String? @db.VarChar(500)
|
|
lastSeenAt DateTime @default(now())
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([userId, deviceId])
|
|
@@index([userId])
|
|
}
|
|
|
|
model AccountDeletionRequest {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
status String @default("pending") @db.VarChar(16)
|
|
requestedAt DateTime @default(now())
|
|
coolingEndsAt DateTime
|
|
reviewedBy String? @db.VarChar(100)
|
|
reviewedAt DateTime?
|
|
completedAt DateTime?
|
|
cancelledAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
@@index([status])
|
|
}
|
|
|
|
model QuotaUsage {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
quotaType String @db.VarChar(32)
|
|
amount Int
|
|
resource String? @db.VarChar(255)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId, quotaType])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model CostDailySummary {
|
|
id String @id @default(cuid())
|
|
date DateTime
|
|
provider String @db.VarChar(32)
|
|
model String? @db.VarChar(100)
|
|
calls Int @default(0)
|
|
tokens Int @default(0)
|
|
cost Float @default(0)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([date, provider, model])
|
|
}
|
|
|
|
model SecretRecord {
|
|
id String @id @default(cuid())
|
|
name String @unique @db.VarChar(100)
|
|
provider String @db.VarChar(32)
|
|
encrypted String @db.Text
|
|
maskLast4 String @db.VarChar(4)
|
|
status String @default("active") @db.VarChar(16)
|
|
expiresAt DateTime?
|
|
rotatedFrom String? @db.VarChar(100)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([provider])
|
|
}
|
|
|
|
model SecretAccessLog {
|
|
id String @id @default(cuid())
|
|
secretId String
|
|
secretName String @db.VarChar(100)
|
|
accessedBy String @db.VarChar(100)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([secretId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model RecentItem {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
targetType String @db.VarChar(32)
|
|
targetId String @db.VarChar(255)
|
|
title String @db.VarChar(255)
|
|
metadata Json?
|
|
accessedAt DateTime @updatedAt
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId, accessedAt])
|
|
@@index([userId, targetType])
|
|
}
|
|
|
|
model Favorite {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
targetType String @db.VarChar(32)
|
|
targetId String @db.VarChar(255)
|
|
title String? @db.VarChar(255)
|
|
metadata Json?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([userId, targetType, targetId])
|
|
@@index([userId])
|
|
}
|
|
|
|
model SearchHistory {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
query String @db.VarChar(500)
|
|
resultsCount Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId, createdAt])
|
|
}
|
|
|
|
model NotificationPreference {
|
|
id String @id @default(cuid())
|
|
userId String @unique
|
|
reviewReminder Boolean @default(true)
|
|
learningReminder Boolean @default(true)
|
|
streakAlert Boolean @default(true)
|
|
pushEnabled Boolean @default(true)
|
|
quietStartHour Int?
|
|
quietEndHour Int?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model PushToken {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
token String @db.VarChar(500)
|
|
platform String @db.VarChar(32)
|
|
deviceId String? @db.VarChar(255)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, token])
|
|
@@index([userId])
|
|
}
|
|
|
|
model NotificationTemplate {
|
|
id String @id @default(cuid())
|
|
name String @db.VarChar(100)
|
|
type String @db.VarChar(32)
|
|
title String @db.VarChar(255)
|
|
content String @db.Text
|
|
channel String @default("in_app") @db.VarChar(32)
|
|
enabled Boolean @default(true)
|
|
createdBy String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([type])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model LearningGoal {
|
|
id String @id @default(cuid())
|
|
userId String @unique
|
|
dailyCardTarget Int @default(10)
|
|
dailyMinuteTarget Int @default(30)
|
|
weeklyCardTarget Int @default(50)
|
|
favoriteSubject String? @db.VarChar(100)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model StreakRecord {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
streakType String @db.VarChar(32)
|
|
length Int @default(0)
|
|
startDate DateTime
|
|
endDate DateTime
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
@@index([streakType])
|
|
}
|
|
|
|
model LearningStats {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
date DateTime
|
|
totalCards Int @default(0)
|
|
reviewedCards Int @default(0)
|
|
newCards Int @default(0)
|
|
totalMinutes Int @default(0)
|
|
avgMasteryScore Decimal? @db.Decimal(5, 2)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([userId, date])
|
|
@@index([userId])
|
|
}
|
|
|
|
model ServiceHealth {
|
|
id String @id @default(cuid())
|
|
serverName String @db.VarChar(100)
|
|
serviceName String @db.VarChar(64)
|
|
status String @db.VarChar(16)
|
|
message String? @db.VarChar(500)
|
|
checkedAt DateTime @default(now())
|
|
|
|
@@index([serverName])
|
|
@@index([serviceName])
|
|
@@index([checkedAt])
|
|
}
|
|
|
|
model ExportJob {
|
|
id String @id @default(cuid())
|
|
type String @db.VarChar(32)
|
|
status String @default("pending") @db.VarChar(16)
|
|
format String @default("csv") @db.VarChar(8)
|
|
filePath String? @db.VarChar(500)
|
|
fileSize Int @default(0)
|
|
startedAt DateTime?
|
|
completedAt DateTime?
|
|
errorMessage String? @db.Text
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model AgentTask {
|
|
id String @id @default(cuid())
|
|
userId String?
|
|
type String @db.VarChar(32)
|
|
status String @default("pending") @db.VarChar(16)
|
|
input String? @db.Text
|
|
output String? @db.Text
|
|
sessionId String? @db.VarChar(255)
|
|
approvedBy String?
|
|
approvedAt DateTime?
|
|
giteaUrl String? @db.VarChar(500)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([status])
|
|
@@index([userId])
|
|
}
|
|
|
|
model AgentArtifact {
|
|
id String @id @default(cuid())
|
|
taskId String?
|
|
type String @db.VarChar(32)
|
|
title String @db.VarChar(255)
|
|
content String? @db.Text
|
|
status String @default("draft") @db.VarChar(16)
|
|
createdBy String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([taskId])
|
|
@@index([type])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model DecisionRecord {
|
|
id String @id @default(cuid())
|
|
title String @db.VarChar(255)
|
|
context String? @db.Text
|
|
decision String? @db.Text
|
|
rationale String? @db.Text
|
|
status String @default("proposed") @db.VarChar(32)
|
|
createdBy String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([status])
|
|
}
|
|
|
|
model ReleaseChecklist {
|
|
id String @id @default(cuid())
|
|
version String @db.VarChar(50)
|
|
item String @db.VarChar(255)
|
|
checked Boolean @default(false)
|
|
sortOrder Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([version])
|
|
}
|
|
|
|
model PrivacyPolicy {
|
|
id String @id @default(cuid())
|
|
version String @db.VarChar(50)
|
|
title String @db.VarChar(255)
|
|
content String @db.Text
|
|
effectiveAt DateTime
|
|
published Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([version])
|
|
}
|
|
|
|
model UserAgreement {
|
|
id String @id @default(cuid())
|
|
version String @db.VarChar(50)
|
|
title String @db.VarChar(255)
|
|
content String @db.Text
|
|
effectiveAt DateTime
|
|
published Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([version])
|
|
}
|
|
|
|
model FilingRecord {
|
|
id String @id @default(cuid())
|
|
type String @db.VarChar(64)
|
|
title String @db.VarChar(255)
|
|
filePath String? @db.VarChar(500)
|
|
status String @default("pending") @db.VarChar(16)
|
|
notes String? @db.Text
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model DataExportRequest {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
status String @default("pending") @db.VarChar(16)
|
|
filePath String? @db.VarChar(500)
|
|
completedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([userId])
|
|
@@index([status])
|
|
}
|
|
|
|
model VendorBill {
|
|
id String @id @default(cuid())
|
|
provider String @db.VarChar(32)
|
|
billMonth String @db.VarChar(7)
|
|
amount Decimal @db.Decimal(10, 2)
|
|
currency String @default("CNY") @db.VarChar(8)
|
|
usageSummary Json?
|
|
paidAt DateTime?
|
|
notes String? @db.Text
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([provider, billMonth])
|
|
@@index([provider])
|
|
}
|