feat: AI Runtime Prisma Schema (API-AI-004~010 + 070/071/075)
Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 11s

新增 18 个模型:
- P0: UserLearningProfile, UserAiSettings, UserModelCredential
- P0: AiRuntimeJob, AiRuntimeResult, LearningAnalysisSnapshot
- P0: ModelInvocationLog, UserAiUsageDaily, PlatformAiBudgetDaily
- P0: RuntimeInstance
- P1 预留: AiLearningAnalysis, WeakPointCandidate,
  NextActionRecommendation, QuestionGenerationPlan,
  FlashcardGenerationPlan, Flashcard, AiArtifactFeedback

User 模型补充新表 relation 字段。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-11 20:38:00 +08:00
parent e6cbe16a11
commit 6888fe1d12

View File

@ -56,6 +56,12 @@ model User {
readingEvents ReadingEvent[] readingEvents ReadingEvent[]
materialReadingProgresses MaterialReadingProgress[] materialReadingProgresses MaterialReadingProgress[]
temporaryReadingMaterials TemporaryReadingMaterial[] temporaryReadingMaterials TemporaryReadingMaterial[]
learningProfile UserLearningProfile?
aiSettings UserAiSettings?
modelCredentials UserModelCredential[]
aiRuntimeJobs AiRuntimeJob[]
aiRuntimeResults AiRuntimeResult[]
aiAnalysisResultsNew AiLearningAnalysis[]
@@index([email]) @@index([email])
@@index([status]) @@index([status])
@ -1763,3 +1769,414 @@ model QuizAnswer {
@@index([attemptId]) @@index([attemptId])
@@index([questionId]) @@index([questionId])
} }
// ═══════════════════════════════════════════════════════
// M-API-AI-RUNTIME: AI Runtime 调度与落库
// ═══════════════════════════════════════════════════════
// ── API-AI-004: UserLearningProfile ──
model UserLearningProfile {
id String @id @default(cuid())
userId String @unique
learningGoal String? @db.VarChar(255)
currentLevel String? @db.VarChar(32)
dailyAvailableMinutes Int?
qualityPreference String? @db.VarChar(32)
ageRange String? @db.VarChar(32)
occupation String? @db.VarChar(100)
examTarget String? @db.VarChar(255)
learningDeadline DateTime?
learningStyle String? @db.VarChar(100)
aiAcceptanceLevel String? @db.VarChar(32)
digitalSkillLevel String? @db.VarChar(32)
preferredQuestionTypes Json?
preferredLanguage String? @db.VarChar(32)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
}
// ── API-AI-005: UserAiSettings ──
model UserAiSettings {
id String @id @default(cuid())
userId String @unique
allowAiAnalysis Boolean @default(true)
allowUseLearningBehavior Boolean @default(true)
allowUseUserProfile Boolean @default(true)
allowUseDocumentContent Boolean @default(false)
allowStoreAiAnalysisHistory Boolean @default(true)
apiKeyMode String @default("platform_key") @db.VarChar(32)
defaultCredentialId String?
fallbackToPlatformKey Boolean @default(true)
maxDailyAiJobs Int @default(20)
maxDailyTokenBudget Int @default(100000)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
}
// ── API-AI-006: UserModelCredential ──
model UserModelCredential {
id String @id @default(cuid())
userId String
provider String @default("deepseek") @db.VarChar(32)
keyAlias String? @db.VarChar(100)
encryptedApiKey String @db.Text
keyHash String @db.VarChar(255)
maskedKey String @db.VarChar(20)
status String @default("active") @db.VarChar(32)
lastTestedAt DateTime?
lastUsedAt DateTime?
lastErrorCode String? @db.VarChar(100)
lastErrorMessage String? @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
user User @relation(fields: [userId], references: [id])
@@index([userId])
@@index([keyHash])
}
// ── API-AI-007: AiRuntimeJob ──
model AiRuntimeJob {
id String @id @default(cuid())
userId String
jobType String @db.VarChar(64)
targetType String @db.VarChar(32)
targetId String @db.VarChar(255)
snapshotId String?
status String @default("pending") @db.VarChar(32)
priority Int @default(0)
idempotencyKey String? @unique @db.VarChar(255)
apiKeyMode String @default("platform_key") @db.VarChar(32)
credentialId String?
modelProvider String @default("deepseek") @db.VarChar(32)
modelName String @default("deepseek-chat") @db.VarChar(64)
promptVersion String? @db.VarChar(100)
outputSchemaVersion String? @db.VarChar(100)
attemptNo Int @default(0)
retriedFromJobId String? @db.VarChar(255)
lockedBy String? @db.VarChar(100)
lockedAt DateTime?
lockUntil DateTime?
startedAt DateTime?
finishedAt DateTime?
retryCount Int @default(0)
maxRetryCount Int @default(3)
timeoutSeconds Int @default(120)
errorCode String? @db.VarChar(100)
errorMessage String? @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
result AiRuntimeResult?
@@index([status])
@@index([jobType])
@@index([userId])
@@index([targetType, targetId])
@@index([lockUntil])
}
// ── API-AI-007: AiRuntimeResult ──
model AiRuntimeResult {
id String @id @default(cuid())
jobId String @unique
userId String
runtimeInstanceId String @db.VarChar(100)
status String @db.VarChar(32)
attemptNo Int @default(0)
resultIdempotencyKey String? @db.VarChar(255)
outputHash String? @db.VarChar(255)
rawOutput Json?
validatedOutput Json?
schemaVersion String @db.VarChar(100)
validationErrors Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
job AiRuntimeJob @relation(fields: [jobId], references: [id])
@@index([userId])
@@index([jobId])
@@index([resultIdempotencyKey])
}
// ── API-AI-008: LearningAnalysisSnapshot ──
model LearningAnalysisSnapshot {
id String @id @default(cuid())
userId String
scopeType String @db.VarChar(32)
scopeId String @db.VarChar(255)
snapshotVersion String @db.VarChar(100)
sourceDataVersion String? @db.VarChar(100)
privacyScope Json?
userProfile Json?
aiSettings Json?
deviceContext Json?
learningBehaviorSummary Json?
materialProgressSummary Json?
contentStructureSummary Json?
behaviorSignals Json?
scoreSignals Json?
constraints Json?
allowedModelFields Json?
expiresAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([scopeType, scopeId])
}
// ── API-AI-009: ModelInvocationLog ──
model ModelInvocationLog {
id String @id @default(cuid())
userId String
jobId String
provider String @db.VarChar(32)
model String @db.VarChar(64)
apiKeyMode String @db.VarChar(32)
credentialId String? @db.VarChar(255)
promptName String @db.VarChar(100)
promptVersion String @db.VarChar(100)
outputSchemaVersion String @db.VarChar(100)
inputTokens Int @default(0)
outputTokens Int @default(0)
totalTokens Int @default(0)
latencyMs Int @default(0)
costEstimate Int?
success Boolean @default(false)
errorCode String? @db.VarChar(100)
errorMessage String? @db.Text
retryCount Int @default(0)
runtimeInstanceId String @db.VarChar(100)
traceId String? @db.VarChar(255)
correlationId String? @db.VarChar(255)
createdAt DateTime @default(now())
@@index([userId])
@@index([jobId])
@@index([provider, model])
@@index([createdAt])
}
// ── P0 增补: UserAiUsageDaily (API-AI-070) ──
model UserAiUsageDaily {
id String @id @default(cuid())
userId String
localDate DateTime
apiKeyMode String @db.VarChar(32)
jobCount Int @default(0)
inputTokens Int @default(0)
outputTokens Int @default(0)
totalTokens Int @default(0)
costEstimate Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([userId, localDate, apiKeyMode])
@@index([userId])
}
// ── P0 增补: PlatformAiBudgetDaily (API-AI-071) ──
model PlatformAiBudgetDaily {
id String @id @default(cuid())
localDate DateTime
provider String @db.VarChar(32)
model String @db.VarChar(64)
inputTokens Int @default(0)
outputTokens Int @default(0)
totalTokens Int @default(0)
costEstimate Int @default(0)
jobCount Int @default(0)
failedCount Int @default(0)
circuitBreakerStatus String @default("closed") @db.VarChar(32)
circuitBreakerReason String? @db.VarChar(255)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([localDate, provider, model])
}
// ── P0 增补: RuntimeInstance (API-AI-075) ──
model RuntimeInstance {
id String @id @default(cuid())
runtimeInstanceId String @unique @db.VarChar(100)
runtimeVersion String? @db.VarChar(50)
status String @default("active") @db.VarChar(32)
lastHeartbeatAt DateTime?
capabilities Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ── P1 预留: AiLearningAnalysis ──
model AiLearningAnalysis {
id String @id @default(cuid())
userId String
jobId String
snapshotId String?
targetType String @db.VarChar(32)
targetId String @db.VarChar(255)
learningState String? @db.VarChar(32)
summary String? @db.Text
riskLevel String? @db.VarChar(32)
confidence Float?
evidence Json?
nextActionIds Json?
promptVersion String? @db.VarChar(100)
schemaVersion String? @db.VarChar(100)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([jobId])
}
// ── P1 预留: WeakPointCandidate ──
model WeakPointCandidate {
id String @id @default(cuid())
userId String
jobId String
snapshotId String?
targetType String @db.VarChar(32)
targetId String @db.VarChar(255)
knowledgePointId String? @db.VarChar(255)
title String @db.VarChar(255)
reason String? @db.Text
confidence Float?
evidence Json?
status String @default("active") @db.VarChar(32)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([jobId])
}
// ── P1 预留: NextActionRecommendation ──
model NextActionRecommendation {
id String @id @default(cuid())
userId String
jobId String
snapshotId String?
actionType String @db.VarChar(32)
targetType String? @db.VarChar(32)
targetId String? @db.VarChar(255)
title String @db.VarChar(255)
reason String? @db.Text
priority Int @default(0)
estimatedMinutes Int?
deviceSuitability String? @db.VarChar(32)
status String @default("active") @db.VarChar(32)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([jobId])
}
// ── P1 预留: QuestionGenerationPlan ──
model QuestionGenerationPlan {
id String @id @default(cuid())
userId String
jobId String
snapshotId String?
targetType String? @db.VarChar(32)
targetId String? @db.VarChar(255)
knowledgePointIds Json?
questionTypes Json?
difficultyLevel String? @db.VarChar(32)
count Int @default(5)
reason String? @db.Text
status String @default("pending") @db.VarChar(32)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
}
// ── P1 预留: FlashcardGenerationPlan ──
model FlashcardGenerationPlan {
id String @id @default(cuid())
userId String
jobId String
snapshotId String?
targetType String? @db.VarChar(32)
targetId String? @db.VarChar(255)
knowledgePointIds Json?
count Int @default(5)
difficultyLevel String? @db.VarChar(32)
reason String? @db.Text
status String @default("pending") @db.VarChar(32)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
}
// ── P1 预留: Flashcard ──
model Flashcard {
id String @id @default(cuid())
userId String
sourceType String? @db.VarChar(32)
sourceId String? @db.VarChar(255)
knowledgePointId String? @db.VarChar(255)
front String @db.Text
back String @db.Text
hint String? @db.Text
difficultyLevel String? @db.VarChar(32)
sourceBlockIds Json?
generatedByJobId String? @db.VarChar(255)
promptVersion String? @db.VarChar(100)
schemaVersion String? @db.VarChar(100)
status String @default("draft") @db.VarChar(32)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
@@index([userId])
}
// ── P1 预留: AiArtifactFeedback ──
model AiArtifactFeedback {
id String @id @default(cuid())
userId String
artifactType String @db.VarChar(32)
artifactId String @db.VarChar(255)
feedbackType String @db.VarChar(32)
reason String? @db.Text
createdAt DateTime @default(now())
@@index([userId])
@@index([artifactType])
}