generator client { provider = "prisma-client-js" } datasource db { provider = "mysql" url = env("DATABASE_URL") } enum ProjectStatus { ACTIVE ARCHIVED } enum EnvironmentKind { TEST PRODUCTION } enum DeployRunStatus { PENDING QUEUED RUNNING SUCCESS FAILED CANCELED } enum DeployRunTrigger { MANUAL GITEA_WEBHOOK RETRY JENKINS_EXTERNAL } enum RunStepStatus { PENDING RUNNING SUCCESS FAILED SKIPPED CANCELED } enum SecretProvider { JENKINS GITEA WECOM LLM INTERNAL } enum AgentInvocationPurpose { RISK_SUMMARY FAILURE_DIAGNOSIS RUNBOOK_QA RELEASE_NOTES INCIDENT_REVIEW } enum OutboxStatus { PENDING SENT FAILED DEAD } enum UserRole { SUPER_ADMIN MEMBER } enum UserStatus { ACTIVE DISABLED } enum ProjectPermissionLevel { READ BUILD } enum PlatformMessageType { PASSWORD_RESET_REQUEST } enum PlatformMessageStatus { PENDING HANDLED } model Project { id String @id @default(cuid()) key String @unique name String repositoryUrl String? @db.VarChar(512) defaultBranch String @default("develop") status ProjectStatus @default(ACTIVE) metadata Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt environments Environment[] deployJobs DeployJob[] deployRuns DeployRun[] permissions ProjectMemberPermission[] @@map("projects") } model Environment { id String @id @default(cuid()) projectId String name EnvironmentKind displayName String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) deployJobs DeployJob[] deployRuns DeployRun[] @@unique([projectId, name]) @@index([projectId]) @@map("environments") } model DeployJob { id String @id @default(cuid()) projectId String environmentId String jenkinsJobPath String jenkinsParameters Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade) deployRuns DeployRun[] @@unique([projectId, environmentId]) @@index([environmentId]) @@map("deploy_jobs") } model DeployRun { id String @id @default(cuid()) projectId String environmentId String jobId String? status DeployRunStatus @default(PENDING) trigger DeployRunTrigger @default(MANUAL) ref String operator String idempotencyKey String @unique jenkinsQueueId String? jenkinsBuildNumber Int? failureSummary String? @db.Text metadata Json? version Int @default(1) startedAt DateTime? finishedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt project Project @relation(fields: [projectId], references: [id], onDelete: Restrict) environment Environment @relation(fields: [environmentId], references: [id], onDelete: Restrict) job DeployJob? @relation(fields: [jobId], references: [id], onDelete: SetNull) steps RunStep[] auditLogs AuditLog[] agentInvocations AgentInvocation[] outboxMessages OutboxMessage[] @@index([projectId, environmentId, createdAt]) @@index([status, createdAt]) @@map("deploy_runs") } model RunStep { id String @id @default(cuid()) deployRunId String bpmnNodeId String name String status RunStepStatus @default(PENDING) order Int @default(0) errorSummary String? @db.Text metadata Json? startedAt DateTime? finishedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deployRun DeployRun @relation(fields: [deployRunId], references: [id], onDelete: Cascade) @@unique([deployRunId, bpmnNodeId]) @@index([deployRunId, order]) @@map("run_steps") } model AuditLog { id String @id @default(cuid()) deployRunId String? actorId String? actorName String? action String resourceType String resourceId String? requestId String? sourceIp String? userAgent String? before Json? after Json? parameterDigest Json? createdAt DateTime @default(now()) deployRun DeployRun? @relation(fields: [deployRunId], references: [id], onDelete: SetNull) @@index([resourceType, resourceId, createdAt]) @@index([actorId, createdAt]) @@map("audit_logs") } model User { id String @id @default(cuid()) account String @unique displayName String role UserRole @default(MEMBER) status UserStatus @default(ACTIVE) passwordHash String @db.Text mustChangePassword Boolean @default(false) passwordVersion Int @default(1) lastLoginAt DateTime? createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt projectPermissions ProjectMemberPermission[] requestedMessages PlatformMessage[] @relation("message_requester") handledMessages PlatformMessage[] @relation("message_handler") @@index([role, status]) @@map("users") } model ProjectMemberPermission { id String @id @default(cuid()) userId String projectId String level ProjectPermissionLevel createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) @@unique([userId, projectId]) @@index([projectId]) @@map("project_member_permissions") } model PlatformMessage { id String @id @default(cuid()) type PlatformMessageType status PlatformMessageStatus @default(PENDING) title String content String @db.Text requesterId String? requesterAccount String targetResourceType String? targetResourceId String? metadata Json? handledById String? handledAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt requester User? @relation("message_requester", fields: [requesterId], references: [id], onDelete: SetNull) handler User? @relation("message_handler", fields: [handledById], references: [id], onDelete: SetNull) @@index([type, status, createdAt]) @@index([requesterId, status]) @@map("platform_messages") } model Secret { id String @id @default(cuid()) provider SecretProvider scopeType String scopeId String name String encryptedValue String @db.Text version Int @default(1) rotatedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([provider, scopeType, scopeId, name]) @@index([scopeType, scopeId]) @@map("secrets") } model AgentInvocation { id String @id @default(cuid()) deployRunId String? purpose AgentInvocationPurpose actorId String? actorName String? model String? promptVersion String inputDigest Json outputDigest Json? tokenEstimate Int? status String errorSummary String? @db.Text createdAt DateTime @default(now()) completedAt DateTime? deployRun DeployRun? @relation(fields: [deployRunId], references: [id], onDelete: SetNull) @@index([purpose, createdAt]) @@index([deployRunId, createdAt]) @@map("agent_invocations") } model OutboxMessage { id String @id @default(cuid()) deployRunId String? channel String template String payload Json status OutboxStatus @default(PENDING) attemptCount Int @default(0) nextAttemptAt DateTime? lastError String? @db.Text idempotencyKey String @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deployRun DeployRun? @relation(fields: [deployRunId], references: [id], onDelete: SetNull) @@index([status, nextAttemptAt]) @@map("outbox_messages") }