From 6888fe1d12484b5906ce032cee9a6c2d821cc199 Mon Sep 17 00:00:00 2001 From: wangdl Date: Thu, 11 Jun 2026 20:38:00 +0800 Subject: [PATCH] feat: AI Runtime Prisma Schema (API-AI-004~010 + 070/071/075) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 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 --- prisma/schema.prisma | 417 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 417 insertions(+) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8f7a3aa..ea5843f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -56,6 +56,12 @@ model User { readingEvents ReadingEvent[] materialReadingProgresses MaterialReadingProgress[] temporaryReadingMaterials TemporaryReadingMaterial[] + learningProfile UserLearningProfile? + aiSettings UserAiSettings? + modelCredentials UserModelCredential[] + aiRuntimeJobs AiRuntimeJob[] + aiRuntimeResults AiRuntimeResult[] + aiAnalysisResultsNew AiLearningAnalysis[] @@index([email]) @@index([status]) @@ -1763,3 +1769,414 @@ model QuizAnswer { @@index([attemptId]) @@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]) +}