1917 lines
96 KiB
Plaintext
1917 lines
96 KiB
Plaintext
generator client {
|
||
provider = "prisma-client-js"
|
||
previewFeatures = []
|
||
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
|
||
engineType = "library"
|
||
}
|
||
|
||
datasource db {
|
||
provider = "postgresql"
|
||
url = env("DATABASE_URL")
|
||
}
|
||
|
||
model User {
|
||
id String @id @default(uuid())
|
||
email String @unique
|
||
username String @unique
|
||
password String
|
||
role Role @default(USER)
|
||
isActive Boolean @default(false)
|
||
canDownload Boolean @default(true)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
department String @default("")
|
||
dingtalkUserId String? @unique
|
||
loginType String @default("normal")
|
||
realName String?
|
||
phone String?
|
||
avatar String?
|
||
managedDepartment String?
|
||
isSuperAdmin Boolean @default(false)
|
||
departmentId String?
|
||
employmentStatus String? @default("active")
|
||
resignationDate DateTime?
|
||
transferDate DateTime?
|
||
transferToDeptId String?
|
||
employmentNote String?
|
||
statusChangedBy String?
|
||
statusChangedAt DateTime?
|
||
status String @default("pending")
|
||
lastLoginAt DateTime?
|
||
lastLoginIp String?
|
||
loginCount Int @default(0)
|
||
archivedAt DateTime?
|
||
managedDepartments Department[] @relation("DepartmentManager")
|
||
downloadLogs MaterialDownloadLog[]
|
||
favorites MaterialFavorite[]
|
||
likes MaterialLike[]
|
||
materialLogs MaterialLog[]
|
||
materialTransfersFrom MaterialTransfer[] @relation("MaterialTransferFromUser")
|
||
materialTransfersTo MaterialTransfer[] @relation("MaterialTransferToUser")
|
||
materialTransfersAsOperator MaterialTransfer[] @relation("MaterialTransferOperator")
|
||
materials Material[] @relation("MaterialOwner")
|
||
taskDetailRequestedMaterials Material[] @relation("TaskDetailRequester")
|
||
productPackageUploadJobs ProductPackageUploadJob[] @relation("ProductPackageUploadJobOperator")
|
||
departmentRef Department? @relation("UserDepartment", fields: [departmentId], references: [id])
|
||
transferToDept Department? @relation("UserTransferDepartment", fields: [transferToDeptId], references: [id])
|
||
employeeBindings UserEmployeeBinding[]
|
||
permissionInherits UserPermissionInherit[]
|
||
submittedTaskDetailChangeRequests TaskDetailChangeRequest[] @relation("TaskDetailChangeRequesterUser")
|
||
managedTaskDetailChangeRequests TaskDetailChangeRequest[] @relation("TaskDetailChangeManagerUser")
|
||
brandKnowledgeUploads BrandKnowledge[] @relation("BrandKnowledgeUploader")
|
||
brandKnowledgeLikes BrandKnowledgeLike[]
|
||
brandKnowledgeFavorites BrandKnowledgeFavorite[]
|
||
detailGeneratorProjects DetailGeneratorProject[]
|
||
detailGeneratorAssets DetailGeneratorAsset[]
|
||
detailGeneratorJobs DetailGeneratorJob[]
|
||
desktopClientGrants DesktopClientGrant[]
|
||
desktopClientSessions DesktopClientSession[]
|
||
desktopWorkspaceProjects DesktopWorkspaceProject[]
|
||
desktopWorkspaceAssets DesktopWorkspaceAsset[]
|
||
|
||
@@index([status])
|
||
@@index([archivedAt])
|
||
@@index([departmentId])
|
||
@@index([employmentStatus])
|
||
@@index([transferToDeptId])
|
||
@@map("users")
|
||
}
|
||
|
||
model DesktopClientGrant {
|
||
id String @id
|
||
codeHash String @unique @map("code_hash")
|
||
ownerId String @map("owner_id")
|
||
challenge String
|
||
redirectUri String @map("redirect_uri")
|
||
expiresAt DateTime @map("expires_at") @db.Timestamptz(6)
|
||
usedAt DateTime? @map("used_at") @db.Timestamptz(6)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||
@@map("desktop_client_grants")
|
||
}
|
||
|
||
model DesktopClientSession {
|
||
id String @id
|
||
tokenHash String @unique @map("token_hash")
|
||
ownerId String @map("owner_id")
|
||
deviceName String @map("device_name")
|
||
expiresAt DateTime @map("expires_at") @db.Timestamptz(6)
|
||
revokedAt DateTime? @map("revoked_at") @db.Timestamptz(6)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||
@@index([ownerId], map: "desktop_client_sessions_owner_idx")
|
||
@@map("desktop_client_sessions")
|
||
}
|
||
|
||
model DesktopWorkspaceProject {
|
||
id String @id
|
||
ownerId String @map("owner_id")
|
||
sourceWebId String? @map("source_web_id")
|
||
document Json
|
||
conversation Json @default("[]")
|
||
composer String @default("")
|
||
status String @default("draft")
|
||
revision Int @default(1)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||
updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamptz(6)
|
||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||
assets DesktopWorkspaceAsset[]
|
||
@@index([ownerId,updatedAt], map: "desktop_workspace_projects_owner_idx")
|
||
@@unique([ownerId,sourceWebId], map: "desktop_workspace_projects_source_idx")
|
||
@@map("desktop_workspace_projects")
|
||
}
|
||
|
||
model DesktopWorkspaceAsset {
|
||
id String @id
|
||
projectId String @map("project_id")
|
||
ownerId String @map("owner_id")
|
||
source String
|
||
sha256 String
|
||
storageKey String @map("storage_key")
|
||
mimeType String @map("mime_type")
|
||
byteSize Int @map("byte_size")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||
project DesktopWorkspaceProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||
@@unique([projectId,source])
|
||
@@index([ownerId,projectId], map: "desktop_workspace_assets_owner_idx")
|
||
@@map("desktop_workspace_assets")
|
||
}
|
||
|
||
model AiModelProvider {
|
||
id String @id
|
||
name String
|
||
baseUrl String @map("base_url")
|
||
enabled Boolean @default(false)
|
||
credentialSource String @map("credential_source")
|
||
secretCiphertext String? @map("secret_ciphertext")
|
||
secretNonce String? @map("secret_nonce")
|
||
secretAuthTag String? @map("secret_auth_tag")
|
||
legacyBinding Boolean @default(false) @map("legacy_binding")
|
||
revision Int @default(1)
|
||
syncedAt DateTime? @db.Timestamptz(6) @map("synced_at")
|
||
createdAt DateTime @default(now()) @db.Timestamptz(6) @map("created_at")
|
||
updatedAt DateTime @default(now()) @db.Timestamptz(6) @map("updated_at")
|
||
models AiModelCatalog[]
|
||
@@map("ai_model_providers")
|
||
}
|
||
|
||
model AiModelCatalog {
|
||
id String @id
|
||
providerId String @map("provider_id")
|
||
modelId String @map("model_id")
|
||
enabled Boolean @default(false)
|
||
stages String[] @default([])
|
||
present Boolean @default(true)
|
||
compatibility String @default("unconfirmed")
|
||
capabilityMetadata Json? @map("capability_metadata")
|
||
createdAt DateTime @default(now()) @db.Timestamptz(6) @map("created_at")
|
||
updatedAt DateTime @default(now()) @db.Timestamptz(6) @map("updated_at")
|
||
provider AiModelProvider @relation(fields: [providerId], references: [id], onDelete: Restrict)
|
||
analysisDefaults AiModelConfiguration[] @relation("AiModelDefaultAnalysis")
|
||
copyDefaults AiModelConfiguration[] @relation("AiModelDefaultCopy")
|
||
imageDefaults AiModelConfiguration[] @relation("AiModelDefaultImage")
|
||
verifications AiModelVerification[]
|
||
@@unique([providerId, modelId], map: "ai_model_catalog_provider_model_key")
|
||
@@map("ai_model_catalog")
|
||
}
|
||
|
||
model AiModelVerification {
|
||
id String @id
|
||
modelKey String @map("model_key")
|
||
stage String
|
||
contextHash String @map("context_hash")
|
||
idempotencyKey String @map("idempotency_key")
|
||
actorId String @map("actor_id")
|
||
status String
|
||
requestId String? @map("request_id")
|
||
taskId String? @map("task_id")
|
||
message String?
|
||
evidence Json @default("{}")
|
||
startedAt DateTime @default(now()) @db.Timestamptz(6) @map("started_at")
|
||
deadlineAt DateTime @default(dbgenerated("(now() + '00:10:00'::interval)")) @db.Timestamptz(6) @map("deadline_at")
|
||
finishedAt DateTime? @db.Timestamptz(6) @map("finished_at")
|
||
model AiModelCatalog @relation(fields: [modelKey], references: [id], onDelete: Restrict)
|
||
@@unique([modelKey, stage, idempotencyKey], map: "ai_model_verifications_idempotency_key")
|
||
@@index([modelKey, stage, startedAt(sort: Desc)], map: "ai_model_verifications_model_stage_idx")
|
||
@@map("ai_model_verifications")
|
||
}
|
||
|
||
model AiModelConfiguration {
|
||
id String @id
|
||
defaultAnalysis String? @map("default_analysis")
|
||
defaultCopy String? @map("default_copy")
|
||
defaultImage String? @map("default_image")
|
||
revision Int @default(1)
|
||
updatedAt DateTime @default(now()) @db.Timestamptz(6) @map("updated_at")
|
||
analysisModel AiModelCatalog? @relation("AiModelDefaultAnalysis", fields: [defaultAnalysis], references: [id], onDelete: Restrict)
|
||
copyModel AiModelCatalog? @relation("AiModelDefaultCopy", fields: [defaultCopy], references: [id], onDelete: Restrict)
|
||
imageModel AiModelCatalog? @relation("AiModelDefaultImage", fields: [defaultImage], references: [id], onDelete: Restrict)
|
||
@@map("ai_model_configuration")
|
||
}
|
||
|
||
model AiModelAudit {
|
||
id String @id
|
||
actorId String @map("actor_id")
|
||
action String
|
||
providerId String? @map("provider_id")
|
||
details Json @default("{}")
|
||
createdAt DateTime @default(now()) @db.Timestamptz(6) @map("created_at")
|
||
@@index([createdAt], map: "ai_model_audit_created_idx")
|
||
@@map("ai_model_audit")
|
||
}
|
||
|
||
model DetailGeneratorProject {
|
||
id String @id
|
||
ownerId String @map("owner_id")
|
||
document Json
|
||
revision Int @default(1)
|
||
createdAt DateTime @default(now()) @db.Timestamptz(6) @map("created_at")
|
||
updatedAt DateTime @default(now()) @db.Timestamptz(6) @map("updated_at")
|
||
deletedAt DateTime? @db.Timestamptz(6) @map("deleted_at")
|
||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||
assets DetailGeneratorAsset[]
|
||
jobs DetailGeneratorJob[]
|
||
@@map("detail_generator_projects")
|
||
}
|
||
|
||
model DetailGeneratorBrandProfile {
|
||
id String @id
|
||
name String @unique
|
||
profile Json
|
||
revision Int @default(1)
|
||
updatedAt DateTime @default(now()) @db.Timestamptz(6) @map("updated_at")
|
||
@@map("detail_generator_brand_profiles")
|
||
}
|
||
|
||
model DetailGeneratorAsset {
|
||
id String @id
|
||
projectId String @map("project_id")
|
||
ownerId String @map("owner_id")
|
||
kind String
|
||
name String
|
||
mimeType String @map("mime_type")
|
||
storageKey String @unique @map("storage_key")
|
||
byteSize Int @map("byte_size")
|
||
width Int?
|
||
height Int?
|
||
extractedText String? @map("extracted_text")
|
||
createdAt DateTime @default(now()) @db.Timestamptz(6) @map("created_at")
|
||
project DetailGeneratorProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||
@@index([projectId, ownerId], map: "detail_generator_assets_project_idx")
|
||
@@map("detail_generator_assets")
|
||
}
|
||
|
||
model DetailGeneratorJob {
|
||
id String @id
|
||
projectId String @map("project_id")
|
||
ownerId String @map("owner_id")
|
||
kind String
|
||
status String @default("queued")
|
||
idempotencyKey String @map("idempotency_key")
|
||
fingerprint String
|
||
projectRevision Int @map("project_revision")
|
||
payload Json @default("{}")
|
||
checkpoint Json @default("{}")
|
||
progress Int @default(0)
|
||
error String?
|
||
requestId String? @map("request_id")
|
||
leaseToken String? @map("lease_token")
|
||
leaseExpiresAt DateTime? @db.Timestamptz(6) @map("lease_expires_at")
|
||
createdAt DateTime @default(now()) @db.Timestamptz(6) @map("created_at")
|
||
startedAt DateTime? @db.Timestamptz(6) @map("started_at")
|
||
completedAt DateTime? @db.Timestamptz(6) @map("completed_at")
|
||
updatedAt DateTime @default(now()) @db.Timestamptz(6) @map("updated_at")
|
||
project DetailGeneratorProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||
@@unique([ownerId, idempotencyKey])
|
||
@@index([status, createdAt], map: "detail_generator_jobs_claim_idx")
|
||
@@index([ownerId, status], map: "detail_generator_jobs_owner_status_idx")
|
||
@@map("detail_generator_jobs")
|
||
}
|
||
|
||
model Material {
|
||
id String @id @default(uuid())
|
||
previewUrl String
|
||
productModel String
|
||
reportTitle String? @map("report_title")
|
||
reportKind String? @map("report_kind")
|
||
certificateExpiresOn DateTime? @db.Date @map("certificate_expires_on")
|
||
certificateTestedOn DateTime? @db.Date @map("certificate_tested_on")
|
||
certificateNumber String? @map("certificate_number")
|
||
certificateUsageInstructions String? @map("certificate_usage_instructions")
|
||
reportUsageRules String? @map("report_usage_rules")
|
||
categoryName String
|
||
sceneName String
|
||
materialType String @default("3DMAX")
|
||
renderer String @default("CR")
|
||
authorName String
|
||
zipFileName String @default("")
|
||
zipFilePath String @default("")
|
||
zipFileSize BigInt @default(0)
|
||
downloadCount Int @default(0)
|
||
likeCount Int @default(0)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
userId String
|
||
displayImages String[] @default([])
|
||
platformName String @default("")
|
||
uploadId String?
|
||
videoSourceUrl String?
|
||
videoPreview540Url String?
|
||
videoPreviewClipUrl String?
|
||
videoCoverUrl String?
|
||
videoDuration Int?
|
||
videoProcessingStatus String?
|
||
transcodeLastError String? @map("transcodelasterror")
|
||
transcodeAttempts Int? @map("transcodeattempts")
|
||
transcodeLeaseToken String? @map("transcode_lease_token")
|
||
transcodeLeaseExpiresAt DateTime? @map("transcode_lease_expires_at")
|
||
deletedAt DateTime?
|
||
productCategoryName String @default("")
|
||
/// 直播切片专用:直播日期 / 主播 / 可选关联用户 / 直播平台
|
||
liveDate DateTime? @map("live_date")
|
||
liveHost String? @map("live_host")
|
||
liveHostUserId String? @map("live_host_user_id")
|
||
livePlatform String? @map("live_platform")
|
||
originalOwnerId String?
|
||
transferredAt DateTime?
|
||
transferredBy String?
|
||
transferNote String?
|
||
/// 原归属人离职后,素材托管给的部门。托管只改变"谁负责维护",
|
||
/// 不改变 userId 与 authorName——素材是谁做的,永远记在谁名下。
|
||
custodyDepartmentId String? @map("custody_department_id")
|
||
custodyAt DateTime? @map("custody_at")
|
||
custodyReason String? @map("custody_reason")
|
||
taskNo String? @map("task_no")
|
||
dingtalkApprovalNo String? @map("dingtalk_approval_no")
|
||
taskStatus String? @map("task_status")
|
||
taskFetchedAt DateTime? @map("task_fetched_at")
|
||
isFullSet Boolean @default(false) @map("is_full_set")
|
||
isAiGenerated Boolean @default(false) @map("is_ai_generated")
|
||
taskDetailSourceType String? @map("task_detail_source_type")
|
||
taskDetailRequesterUserId String? @map("task_detail_requester_user_id")
|
||
taskDetailRequesterName String? @map("task_detail_requester_name")
|
||
taskDetailDemandType String? @map("task_detail_demand_type")
|
||
taskDetailPrivate Boolean @default(false) @map("task_detail_private")
|
||
/// 专用 init 建档时写 true,完成落库后写 false。列表/筛选只读这个标记,
|
||
/// 不再每次去资产表现场判断是不是上传空壳。
|
||
isIncompleteShell Boolean @default(false) @map("is_incomplete_shell")
|
||
assets MaterialAsset[]
|
||
favorites MaterialFavorite[]
|
||
likes MaterialLike[]
|
||
skus MaterialSku[]
|
||
transfers MaterialTransfer[]
|
||
transcodeProgresses VideoTranscodeProgress[] @relation("MaterialTranscodeProgress")
|
||
productPackageUploadJobs ProductPackageUploadJob[]
|
||
taskDetailChangeRequests TaskDetailChangeRequest[]
|
||
user User @relation("MaterialOwner", fields: [userId], references: [id], onDelete: Cascade)
|
||
taskDetailRequester User? @relation("TaskDetailRequester", fields: [taskDetailRequesterUserId], references: [id], onDelete: SetNull)
|
||
custodyDepartment Department? @relation("MaterialCustodyDepartment", fields: [custodyDepartmentId], references: [id], onDelete: SetNull)
|
||
|
||
@@index([userId])
|
||
@@index([custodyDepartmentId])
|
||
@@index([taskDetailRequesterUserId])
|
||
@@index([createdAt])
|
||
@@index([categoryName])
|
||
@@index([sceneName])
|
||
@@index([platformName])
|
||
@@index([productCategoryName])
|
||
@@index([materialType])
|
||
@@index([liveDate])
|
||
@@index([downloadCount])
|
||
@@index([likeCount])
|
||
@@index([deletedAt])
|
||
@@index([originalOwnerId])
|
||
@@index([transferredAt])
|
||
@@index([taskNo])
|
||
@@index([dingtalkApprovalNo])
|
||
@@index([taskStatus])
|
||
@@index([isIncompleteShell])
|
||
@@index([isFullSet])
|
||
@@index([productModel])
|
||
@@index([reportTitle])
|
||
@@index([authorName])
|
||
@@index([transcodeLeaseExpiresAt, transcodeLeaseToken], map: "materials_transcode_lease_idx")
|
||
@@map("materials")
|
||
}
|
||
|
||
model BrandKnowledge {
|
||
id String @id @default(uuid())
|
||
title String
|
||
knowledgeType String @map("knowledge_type")
|
||
brand String @default("")
|
||
productCategoryName String @default("") @map("product_category_name")
|
||
fileName String @map("file_name")
|
||
filePath String @map("file_path")
|
||
mimeType String @map("mime_type")
|
||
fileSize BigInt @map("file_size")
|
||
previewImageName String? @map("preview_image_name")
|
||
previewImagePath String? @map("preview_image_path")
|
||
previewImageSlices String[] @default([]) @map("preview_image_slices")
|
||
previewImageMimeType String? @map("preview_image_mime_type")
|
||
previewImageSize BigInt? @map("preview_image_size")
|
||
downloadCount Int @default(0) @map("download_count")
|
||
uploadedBy String @map("uploaded_by")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
deletedAt DateTime? @map("deleted_at")
|
||
uploader User @relation("BrandKnowledgeUploader", fields: [uploadedBy], references: [id], onDelete: Cascade)
|
||
likes BrandKnowledgeLike[]
|
||
favorites BrandKnowledgeFavorite[]
|
||
|
||
@@index([knowledgeType])
|
||
@@index([brand])
|
||
@@index([productCategoryName])
|
||
@@index([createdAt])
|
||
@@index([uploadedBy])
|
||
@@index([deletedAt])
|
||
@@map("brand_knowledge")
|
||
}
|
||
|
||
model BrandKnowledgeLike {
|
||
id String @id @default(uuid())
|
||
knowledgeId String @map("knowledge_id")
|
||
userId String @map("user_id")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
knowledge BrandKnowledge @relation(fields: [knowledgeId], references: [id], onDelete: Cascade)
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([knowledgeId, userId])
|
||
@@index([knowledgeId])
|
||
@@index([userId])
|
||
@@map("brand_knowledge_likes")
|
||
}
|
||
|
||
model BrandKnowledgeFavorite {
|
||
id String @id @default(uuid())
|
||
knowledgeId String @map("knowledge_id")
|
||
userId String @map("user_id")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
knowledge BrandKnowledge @relation(fields: [knowledgeId], references: [id], onDelete: Cascade)
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([knowledgeId, userId])
|
||
@@index([knowledgeId])
|
||
@@index([userId])
|
||
@@map("brand_knowledge_favorites")
|
||
}
|
||
|
||
model ProductPackageUploadJob {
|
||
id String @id @default(uuid())
|
||
materialId String @map("material_id")
|
||
operatorUserId String @map("operator_user_id")
|
||
taskNo String? @map("task_no")
|
||
dingtalkApprovalNo String? @map("dingtalk_approval_no")
|
||
assets Json
|
||
options Json?
|
||
status String
|
||
stage String
|
||
progress Int @default(0)
|
||
errorMessage String? @map("error_message")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
startedAt DateTime? @map("started_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
workerId String? @map("worker_id")
|
||
leaseToken String? @map("lease_token")
|
||
leaseExpiresAt DateTime? @map("lease_expires_at")
|
||
|
||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||
operator User @relation("ProductPackageUploadJobOperator", fields: [operatorUserId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([materialId])
|
||
@@index([operatorUserId, createdAt])
|
||
@@index([status, createdAt])
|
||
@@index([status, leaseExpiresAt], map: "product_package_upload_jobs_lease_idx")
|
||
@@map("product_package_upload_jobs")
|
||
}
|
||
|
||
model MaterialLike {
|
||
id String @id @default(uuid())
|
||
materialId String
|
||
userId String
|
||
createdAt DateTime @default(now())
|
||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([materialId, userId])
|
||
@@index([materialId])
|
||
@@index([userId])
|
||
@@map("material_likes")
|
||
}
|
||
|
||
model MaterialFavorite {
|
||
id String @id @default(uuid())
|
||
materialId String
|
||
userId String
|
||
createdAt DateTime @default(now())
|
||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([materialId, userId])
|
||
@@index([materialId])
|
||
@@index([userId])
|
||
@@map("material_favorites")
|
||
}
|
||
|
||
model MaterialDownloadLog {
|
||
id String @id @default(uuid())
|
||
materialId String
|
||
userId String
|
||
downloadedAt DateTime @default(now())
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([materialId])
|
||
@@index([userId])
|
||
@@index([downloadedAt])
|
||
@@index([materialId, userId, downloadedAt])
|
||
@@map("material_download_logs")
|
||
}
|
||
|
||
model MaterialLog {
|
||
id String @id @default(uuid())
|
||
materialId String
|
||
userId String
|
||
action String
|
||
changes String?
|
||
timestamp DateTime @default(now())
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([materialId])
|
||
@@index([userId])
|
||
@@index([timestamp])
|
||
@@map("material_logs")
|
||
}
|
||
|
||
// 永久删除的持久化凭证;不关联 Material,素材删除后仍保留清理清单与操作人。
|
||
model MaterialPurgeJob {
|
||
id String @id @default(uuid())
|
||
materialId String @unique @map("material_id")
|
||
operatorUserId String @map("operator_user_id")
|
||
materialSnapshot Json @map("material_snapshot")
|
||
files Json
|
||
status String @default("queued")
|
||
attempts Int @default(0)
|
||
errorMessage String? @map("error_message")
|
||
leaseToken String? @map("lease_token")
|
||
leaseExpiresAt DateTime? @map("lease_expires_at")
|
||
nextAttemptAt DateTime @default(now()) @map("next_attempt_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
audits MaterialPurgeAudit[]
|
||
|
||
@@index([status, nextAttemptAt])
|
||
@@map("material_purge_jobs")
|
||
}
|
||
|
||
model MaterialPurgeAudit {
|
||
id String @id @default(uuid())
|
||
jobId String @map("job_id")
|
||
event String
|
||
operatorUserId String? @map("operator_user_id")
|
||
message String?
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
job MaterialPurgeJob @relation(fields: [jobId], references: [id], onDelete: Restrict)
|
||
|
||
@@index([jobId, createdAt])
|
||
@@map("material_purge_audits")
|
||
}
|
||
|
||
model MaterialSku {
|
||
id String @id @default(uuid())
|
||
materialId String @map("material_id")
|
||
skuName String @map("sku_name")
|
||
filePath String @map("file_path")
|
||
fileSize BigInt @default(0) @map("file_size")
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
fileName String? @map("file_name")
|
||
previewUrl String? @map("preview_url")
|
||
deletedAt DateTime? @map("deleted_at")
|
||
whiteBackgroundSourceType String? @map("white_background_source_type")
|
||
whiteBackgroundUploadedBy String? @map("white_background_uploaded_by")
|
||
whiteBackgroundUploadedAt DateTime? @map("white_background_uploaded_at")
|
||
barcodes MaterialSkuBarcode[]
|
||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([materialId])
|
||
@@index([skuName, deletedAt])
|
||
@@index([sortOrder])
|
||
@@index([deletedAt])
|
||
@@index([whiteBackgroundSourceType])
|
||
@@index([whiteBackgroundUploadedBy])
|
||
@@index([whiteBackgroundUploadedAt])
|
||
@@map("material_skus")
|
||
}
|
||
|
||
model ProductCategory {
|
||
id String @id @default(uuid())
|
||
name String @unique
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("product_categories")
|
||
}
|
||
|
||
model Category {
|
||
id String @id @default(uuid())
|
||
name String @unique
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("categories")
|
||
}
|
||
|
||
model Scene {
|
||
id String @id @default(uuid())
|
||
name String @unique
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("scenes")
|
||
}
|
||
|
||
model Platform {
|
||
id String @id @default(uuid())
|
||
name String @unique
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("platforms")
|
||
}
|
||
|
||
model SystemSettings {
|
||
id String @id @default(uuid())
|
||
headerBgColor String @default("#1f2937")
|
||
siteName String @default("由壹点3D素材")
|
||
updatedAt DateTime @updatedAt
|
||
createdAt DateTime @default(now())
|
||
headerLogoUrl String @default("/brand/header-logo.png")
|
||
loginLogoUrl String @default("/brand/login-logo.png")
|
||
s3AccessKey String @default("")
|
||
s3Bucket String @default("")
|
||
s3Endpoint String @default("")
|
||
s3PublicUrl String @default("")
|
||
s3Region String @default("us-east-1")
|
||
s3SecretKey String @default("")
|
||
maintenanceEnabled Boolean @default(false) @map("maintenance_enabled")
|
||
maintenanceMessage String? @map("maintenance_message")
|
||
maintenanceStartTime DateTime? @map("maintenance_start_time")
|
||
maintenanceEndTime DateTime? @map("maintenance_end_time")
|
||
|
||
@@map("system_settings")
|
||
}
|
||
|
||
model Department {
|
||
id String @id @default(uuid())
|
||
name String @unique
|
||
createdAt DateTime @default(now())
|
||
parentId String?
|
||
level Int @default(1)
|
||
status String @default("active")
|
||
managerId String?
|
||
code String? @unique
|
||
description String?
|
||
sortOrder Int @default(0)
|
||
updatedAt DateTime @updatedAt
|
||
manager User? @relation("DepartmentManager", fields: [managerId], references: [id])
|
||
parent Department? @relation("DepartmentHierarchy", fields: [parentId], references: [id])
|
||
children Department[] @relation("DepartmentHierarchy")
|
||
users User[] @relation("UserDepartment")
|
||
transferredUsers User[] @relation("UserTransferDepartment")
|
||
custodyMaterials Material[] @relation("MaterialCustodyDepartment")
|
||
|
||
@@index([parentId])
|
||
@@index([status])
|
||
@@index([level])
|
||
@@index([managerId])
|
||
@@map("departments")
|
||
}
|
||
|
||
model Danmaku {
|
||
id String @id @default(uuid())
|
||
content String
|
||
username String
|
||
userId String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([createdAt])
|
||
@@map("danmakus")
|
||
}
|
||
|
||
model DepartmentMaterialPermission {
|
||
id String @id @default(uuid())
|
||
department String
|
||
materialType String
|
||
canView Boolean @default(false)
|
||
canDownload Boolean @default(false)
|
||
canShare Boolean @default(true)
|
||
linkedToDepartment String?
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
canDownloadSource Boolean @default(false) @map("can_download_source")
|
||
|
||
@@unique([department, materialType])
|
||
@@index([department])
|
||
@@index([materialType])
|
||
@@index([linkedToDepartment])
|
||
@@map("department_material_permissions")
|
||
}
|
||
|
||
/// 权限组:一份权限,同时挂部门和人。部门进组是底子,人进组是加餐。
|
||
model MaterialPermissionPack {
|
||
id String @id @default(uuid())
|
||
slug String @unique
|
||
name String
|
||
videoEarlyAccess Boolean @default(false) @map("video_early_access")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
items MaterialPermissionPackItem[]
|
||
departments MaterialPermissionPackDepartment[]
|
||
inheritors UserPermissionInherit[]
|
||
|
||
@@map("material_permission_packs")
|
||
}
|
||
|
||
model MaterialPermissionPackItem {
|
||
id String @id @default(uuid())
|
||
packId String @map("pack_id")
|
||
materialType String @map("material_type")
|
||
canView Boolean @default(false) @map("can_view")
|
||
canDownload Boolean @default(false) @map("can_download")
|
||
canDownloadSource Boolean @default(false) @map("can_download_source")
|
||
canShare Boolean @default(true) @map("can_share")
|
||
canEditAssets Boolean @default(false) @map("can_edit_assets")
|
||
canAccessPrivate Boolean @default(false) @map("can_access_private")
|
||
pack MaterialPermissionPack @relation(fields: [packId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([packId, materialType], map: "material_permission_pack_item_pack_type_key")
|
||
@@index([packId], map: "material_permission_pack_item_pack_idx")
|
||
@@map("material_permission_pack_items")
|
||
}
|
||
|
||
/// 权限组里的部门。子部门没单独进组时,沿用最近的上级组。
|
||
model MaterialPermissionPackDepartment {
|
||
id String @id @default(uuid())
|
||
packId String @map("pack_id")
|
||
organizationUnitId String? @unique(map: "material_permission_pack_dept_unit_key") @map("organization_unit_id")
|
||
/// 迁移前的路径快照,仅供失效关联的展示与清理;鉴权使用关联部门的当前路径。
|
||
departmentPath String @map("department_path")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
pack MaterialPermissionPack @relation(fields: [packId], references: [id], onDelete: Cascade)
|
||
organizationUnit OrganizationUnit? @relation(fields: [organizationUnitId], references: [id], onDelete: Restrict)
|
||
|
||
@@index([packId])
|
||
@@map("material_permission_pack_departments")
|
||
}
|
||
|
||
/// 人可以进多组。只按 userId 绑定,不按姓名。
|
||
model UserPermissionInherit {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
packId String @map("pack_id")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
pack MaterialPermissionPack @relation(fields: [packId], references: [id], onDelete: Restrict)
|
||
|
||
@@unique([userId, packId], map: "user_permission_inherit_user_pack_key")
|
||
@@index([packId], map: "user_permission_inherit_pack_idx")
|
||
@@index([userId], map: "user_permission_inherit_user_idx")
|
||
@@map("user_permission_inherits")
|
||
}
|
||
|
||
model MaterialTransfer {
|
||
id String @id @default(uuid())
|
||
materialId String
|
||
fromUserId String
|
||
toUserId String
|
||
transferredBy String
|
||
transferNote String?
|
||
transferredAt DateTime @default(now())
|
||
fromUser User @relation("MaterialTransferFromUser", fields: [fromUserId], references: [id])
|
||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||
toUser User @relation("MaterialTransferToUser", fields: [toUserId], references: [id])
|
||
operator User @relation("MaterialTransferOperator", fields: [transferredBy], references: [id])
|
||
|
||
@@index([materialId])
|
||
@@index([fromUserId])
|
||
@@index([toUserId])
|
||
@@index([transferredAt])
|
||
@@map("material_transfers")
|
||
}
|
||
|
||
model MaterialAsset {
|
||
id String @id @default(uuid())
|
||
materialId String @map("material_id")
|
||
assetType String @map("asset_type")
|
||
fileUrl String @map("file_url")
|
||
fileName String? @map("file_name")
|
||
fileSize BigInt @default(0) @map("file_size")
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
skuCode String? @map("sku_code")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||
barcodes MaterialAssetBarcode[]
|
||
|
||
@@index([materialId])
|
||
@@index([assetType])
|
||
@@index([sortOrder])
|
||
@@map("material_assets")
|
||
}
|
||
|
||
model MaterialAssetBarcode {
|
||
id String @id @default(uuid())
|
||
assetId String @map("asset_id")
|
||
barcode String
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
asset MaterialAsset @relation(fields: [assetId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([assetId, barcode])
|
||
@@index([barcode])
|
||
@@index([assetId])
|
||
@@map("material_asset_barcodes")
|
||
}
|
||
|
||
model MaterialSkuBarcode {
|
||
id String @id @default(uuid())
|
||
skuId String @map("sku_id")
|
||
barcode String
|
||
deletedAt DateTime? @map("deleted_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
sku MaterialSku @relation(fields: [skuId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([barcode])
|
||
@@index([skuId])
|
||
@@index([deletedAt])
|
||
@@map("material_sku_barcodes")
|
||
}
|
||
|
||
model ApiKey {
|
||
id String @id @default(uuid())
|
||
name String
|
||
key String @unique
|
||
secretCiphertext String? @map("secret_ciphertext")
|
||
secretNonce String? @map("secret_nonce")
|
||
secretAuthTag String? @map("secret_auth_tag")
|
||
isActive Boolean @default(true) @map("is_active")
|
||
createdBy String @map("created_by")
|
||
lastUsedAt DateTime? @map("last_used_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
callLogs ApiCallLog[]
|
||
externalMaterialImportJobs ExternalMaterialImportJob[]
|
||
idempotencyRecords ExternalApiIdempotencyRecord[]
|
||
|
||
@@index([key])
|
||
@@index([isActive])
|
||
@@map("api_keys")
|
||
}
|
||
|
||
model ApiKeyRevealAudit {
|
||
id String @id @default(uuid())
|
||
apiKeyId String @map("api_key_id")
|
||
viewerUserId String @map("viewer_user_id")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
@@index([apiKeyId, createdAt])
|
||
@@index([viewerUserId, createdAt])
|
||
@@map("api_key_reveal_audits")
|
||
}
|
||
|
||
model ExternalApiIdempotencyRecord {
|
||
id String @id @default(uuid())
|
||
apiKeyId String @map("api_key_id")
|
||
operationType String @map("operation_type")
|
||
clientRequestId String @map("client_request_id")
|
||
requestHash String @map("request_hash")
|
||
status String
|
||
responseStatus Int? @map("response_status")
|
||
responseBody Json? @map("response_body")
|
||
completedAt DateTime? @map("completed_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
apiKey ApiKey @relation(fields: [apiKeyId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([apiKeyId, operationType, clientRequestId])
|
||
@@index([apiKeyId])
|
||
@@index([operationType])
|
||
@@index([clientRequestId])
|
||
@@index([status])
|
||
@@index([createdAt])
|
||
@@map("external_api_idempotency_records")
|
||
}
|
||
|
||
model ApiCallLog {
|
||
id String @id @default(uuid())
|
||
apiKeyId String @map("api_key_id")
|
||
endpoint String
|
||
method String
|
||
taskNo String? @map("task_no")
|
||
queryType String? @map("query_type")
|
||
queryValue String? @map("query_value")
|
||
statusCode Int @map("status_code")
|
||
duration Int
|
||
ipAddress String? @map("ip_address")
|
||
userAgent String? @map("user_agent")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
apiKey ApiKey @relation(fields: [apiKeyId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([apiKeyId])
|
||
@@index([taskNo])
|
||
@@index([queryType])
|
||
@@index([queryValue])
|
||
@@index([createdAt])
|
||
@@map("api_call_logs")
|
||
}
|
||
|
||
model ExternalMaterialImportJob {
|
||
id String @id @default(uuid())
|
||
apiKeyId String @map("api_key_id")
|
||
clientRequestId String @map("client_request_id")
|
||
materialType String @map("material_type")
|
||
taskNo String @map("task_no")
|
||
status String
|
||
resolvedUserId String? @map("resolved_user_id")
|
||
requestPayload Json @map("request_payload")
|
||
submissionSnapshot Json @map("submission_snapshot")
|
||
resultMaterialId String? @map("result_material_id")
|
||
errorCode String? @map("error_code")
|
||
errorMessage String? @map("error_message")
|
||
completedAt DateTime? @map("completed_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
apiKey ApiKey @relation(fields: [apiKeyId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([apiKeyId, clientRequestId])
|
||
@@index([taskNo, status])
|
||
@@index([status])
|
||
@@index([createdAt])
|
||
@@map("external_material_import_jobs")
|
||
}
|
||
|
||
model PerformanceSurveyEmployeeProfile {
|
||
id String @id @default(uuid())
|
||
dingtalkUserId String? @unique @map("dingtalk_user_id")
|
||
dingEmail String? @map("ding_email")
|
||
jobNumber String? @unique @map("job_number")
|
||
flowerName String? @map("flower_name")
|
||
realName String? @map("real_name")
|
||
displayName String @map("display_name")
|
||
departmentName String? @map("department_name")
|
||
departmentPath String? @map("department_path")
|
||
positionTitle String? @map("position_title")
|
||
positionLevel String? @map("position_level")
|
||
levelType String? @map("level_type")
|
||
isLeadership Boolean @default(false) @map("is_leadership")
|
||
isDingtalkAdmin Boolean @default(false) @map("is_dingtalk_admin")
|
||
isDingtalkBoss Boolean @default(false) @map("is_dingtalk_boss")
|
||
isDingtalkSenior Boolean @default(false) @map("is_dingtalk_senior")
|
||
managerDingtalkUserId String? @map("manager_dingtalk_user_id")
|
||
managerJobNumber String? @map("manager_job_number")
|
||
managerName String? @map("manager_name")
|
||
employmentStatus String? @map("employment_status")
|
||
/// 规范化的在职状态:ACTIVE / RESIGNED / UNKNOWN。
|
||
/// employmentStatus 是上游原样值,口径混杂(有数字码 2/3/5,也有「正式」「离职」「试用」等中文,
|
||
/// 还有大量空值),靠关键词匹配判断在职会大面积误判,因此单独规范化一列供运行时判定使用。
|
||
employmentState String? @default("UNKNOWN") @map("employment_state")
|
||
/// 最后工作日,来自上游离职表
|
||
resignedAt DateTime? @map("resigned_at")
|
||
/// 离职交接人的钉钉唯一值,上游填写率较低,仅作参考
|
||
handoverDingtalkUserId String? @map("handover_dingtalk_user_id")
|
||
workPlace String? @map("work_place")
|
||
sourcePayload Json? @map("source_payload")
|
||
syncedAt DateTime? @map("synced_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
evaluatorAssignments PerformanceSurveyAssignment[] @relation("PerformanceSurveyEvaluator")
|
||
targetRecords PerformanceSurveyTarget[] @relation("PerformanceSurveyTargetEmployee")
|
||
responses PerformanceSurveyResponse[] @relation("PerformanceSurveyRespondentEmployee")
|
||
submittedWorkItems PerformanceSurveyWorkItem[] @relation("PerformanceSurveyWorkItemSubmitter")
|
||
workItemScores PerformanceSurveyWorkItemScore[] @relation("PerformanceSurveyWorkItemScoreEvaluator")
|
||
notifications PerformanceSurveyNotification[]
|
||
manualPositionAssignments PerformanceSurveyEmployeePositionAssignment[]
|
||
departmentMemberships PerformanceSurveyEmployeeDepartmentMembership[]
|
||
userBindings UserEmployeeBinding[]
|
||
submittedWorkReviewProjects WorkReviewProject[] @relation("WorkReviewSubmitter")
|
||
managedWorkReviewProjects WorkReviewProject[] @relation("WorkReviewManager")
|
||
finalWorkReviewProjects WorkReviewProject[] @relation("WorkReviewFinalReviewer")
|
||
workReviews WorkReviewReview[] @relation("WorkReviewReviewer")
|
||
submittedTaskDetailChangeRequests TaskDetailChangeRequest[] @relation("TaskDetailChangeRequesterEmployee")
|
||
managedTaskDetailChangeRequests TaskDetailChangeRequest[] @relation("TaskDetailChangeManagerEmployee")
|
||
ledTeamFunctionConfigs TeamFunctionConfig[]
|
||
|
||
@@index([displayName])
|
||
@@index([dingEmail])
|
||
@@index([departmentName])
|
||
@@index([positionLevel])
|
||
@@index([levelType])
|
||
@@index([isLeadership])
|
||
@@index([isDingtalkAdmin])
|
||
@@index([managerDingtalkUserId])
|
||
@@index([employmentStatus])
|
||
@@index([employmentState])
|
||
@@map("performance_survey_employee_profiles")
|
||
}
|
||
|
||
/// 共享组织架构,直接镜像上游 HR 系统的部门树(rpt.hr_groups)。
|
||
///
|
||
/// 此前系统没有真正的组织表:部门树是遍历已同步员工的 departmentPath 字符串
|
||
/// 现场拼出来的,因而只能看到"有人的部门",且同名部门会被合并、父子归属取决于
|
||
/// 谁先被遍历到。上游其实是一棵完整的单根树(单一根节点、零孤儿节点),
|
||
/// 直接同步即可,不必倒推。
|
||
model OrganizationUnit {
|
||
id String @id @default(uuid())
|
||
/// 上游 hr_groups.Id
|
||
sourceDeptId Int @unique @map("source_dept_id")
|
||
name String
|
||
parentId String? @map("parent_id")
|
||
/// 上游 hr_groups.parent_id,同步期间用于重建父子关系
|
||
sourceParentId Int? @map("source_parent_id")
|
||
/// 以 / 分隔的全路径,展示与前缀匹配用
|
||
path String
|
||
level Int @default(1)
|
||
/// 本次同步是否仍存在于上游;上游删除的部门置为 false 而非物理删除
|
||
isActive Boolean @default(true) @map("is_active")
|
||
syncedAt DateTime? @map("synced_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
parent OrganizationUnit? @relation("OrganizationHierarchy", fields: [parentId], references: [id])
|
||
children OrganizationUnit[] @relation("OrganizationHierarchy")
|
||
permissionGroupDepartments MaterialPermissionPackDepartment[]
|
||
teamFunctionConfig TeamFunctionConfig?
|
||
|
||
@@index([parentId])
|
||
@@index([sourceParentId])
|
||
@@index([path])
|
||
@@index([isActive])
|
||
@@map("organization_units")
|
||
}
|
||
|
||
/// 职责配置独立于 HR 镜像;部门和负责人只保存稳定身份,名称在读取时取花名册。
|
||
model TeamFunctionConfig {
|
||
id String @id @default(uuid())
|
||
organizationUnitId String @unique @map("organization_unit_id")
|
||
leadEmployeeProfileId String? @map("lead_employee_profile_id")
|
||
description String @default("")
|
||
branches Json @default("[]")
|
||
revision Int @default(1)
|
||
updatedByUserId String @map("updated_by_user_id")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
organizationUnit OrganizationUnit @relation(fields: [organizationUnitId], references: [id], onDelete: Restrict)
|
||
leadEmployeeProfile PerformanceSurveyEmployeeProfile? @relation(fields: [leadEmployeeProfileId], references: [id], onDelete: Restrict)
|
||
audits TeamFunctionConfigAudit[]
|
||
@@map("team_function_configs")
|
||
}
|
||
|
||
model TeamFunctionConfigAudit {
|
||
id String @id @default(uuid())
|
||
configId String @map("config_id")
|
||
actorUserId String @map("actor_user_id")
|
||
before Json?
|
||
after Json
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
config TeamFunctionConfig @relation(fields: [configId], references: [id], onDelete: Restrict)
|
||
@@index([configId, createdAt])
|
||
@@map("team_function_config_audits")
|
||
}
|
||
|
||
/// 素材库账号与权威人员档案的正式绑定关系。
|
||
/// 取代此前"运行时按钉钉ID/邮箱/sourcePayload 逐层软匹配"的隐式关联做法。
|
||
/// 活跃绑定的唯一性由部分唯一索引保证(仅约束 status='ACTIVE',见对应 migration),
|
||
/// 保证一个账号只对应一名权威人员、一名权威人员只对应一个活跃账号。
|
||
/// 冲突一律进人工处理,禁止按姓名自动合并。
|
||
model UserEmployeeBinding {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
employeeProfileId String @map("employee_profile_id")
|
||
bindingSource String @map("binding_source")
|
||
status String @default("ACTIVE")
|
||
verifiedAt DateTime? @map("verified_at")
|
||
note String?
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
employeeProfile PerformanceSurveyEmployeeProfile @relation(fields: [employeeProfileId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([userId], map: "user_employee_binding_user_idx")
|
||
@@index([employeeProfileId], map: "user_employee_binding_profile_idx")
|
||
@@index([status], map: "user_employee_binding_status_idx")
|
||
@@map("user_employee_bindings")
|
||
}
|
||
|
||
model PerformanceSurveyEmployeeDepartmentMembership {
|
||
id String @id @default(uuid())
|
||
employeeProfileId String @map("employee_profile_id")
|
||
sourceDeptId String @map("source_dept_id")
|
||
departmentName String? @map("department_name")
|
||
departmentPath String? @map("department_path")
|
||
isPrimary Boolean @default(false) @map("is_primary")
|
||
syncedAt DateTime? @map("synced_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
employeeProfile PerformanceSurveyEmployeeProfile @relation(fields: [employeeProfileId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([employeeProfileId, sourceDeptId], map: "ps_employee_dept_profile_dept_key")
|
||
@@index([sourceDeptId], map: "ps_employee_dept_source_dept_idx")
|
||
@@index([departmentName], map: "ps_employee_dept_name_idx")
|
||
@@index([departmentPath], map: "ps_employee_dept_path_idx")
|
||
@@index([employeeProfileId, isPrimary], map: "ps_employee_dept_profile_primary_idx")
|
||
@@map("performance_survey_employee_department_memberships")
|
||
}
|
||
|
||
/// 员工组织与在职状态的时间快照。
|
||
///
|
||
/// 人员主档会被每日同步更新,不能据此还原历史周的编制。该表按小时保存
|
||
/// 完整人员状态,团队产能按周末前最近一份快照计算历史人数;快照不依赖
|
||
/// 当前主档是否仍存在,保留离职与调岗前的事实记录。
|
||
model PerformanceSurveyEmployeeRosterSnapshot {
|
||
id String @id @default(uuid())
|
||
capturedAt DateTime @map("captured_at")
|
||
profileCount Int @map("profile_count")
|
||
employeeProfileId String @map("employee_profile_id")
|
||
dingtalkUserId String? @map("dingtalk_user_id")
|
||
name String
|
||
departmentName String? @map("department_name")
|
||
departmentPath String? @map("department_path")
|
||
employmentState String? @map("employment_state")
|
||
isBrandCenter Boolean @default(false) @map("is_brand_center")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
@@unique([capturedAt, employeeProfileId], map: "ps_employee_roster_snapshot_capture_profile_key")
|
||
@@index([capturedAt], map: "ps_employee_roster_snapshot_captured_at_idx")
|
||
@@index([departmentName, capturedAt], map: "ps_employee_roster_snapshot_department_time_idx")
|
||
@@index([dingtalkUserId, capturedAt], map: "ps_employee_roster_snapshot_dingtalk_time_idx")
|
||
@@map("performance_survey_employee_roster_snapshots")
|
||
}
|
||
|
||
model PerformanceSurveyDepartmentProfile {
|
||
id String @id @default(uuid())
|
||
name String @unique
|
||
parentName String? @map("parent_name")
|
||
sourceDeptId String? @map("source_dept_id")
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
employeeCount Int @default(0) @map("employee_count")
|
||
syncedAt DateTime? @map("synced_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([parentName])
|
||
@@index([sourceDeptId])
|
||
@@map("performance_survey_department_profiles")
|
||
}
|
||
|
||
model PerformanceSurveyTemplate {
|
||
id String @id @default(uuid())
|
||
code String @unique
|
||
name String
|
||
description String?
|
||
surveyType String @default("person_period") @map("survey_type")
|
||
weight Float @default(1)
|
||
questions Json
|
||
scoreBands Json @map("score_bands")
|
||
isActive Boolean @default(true) @map("is_active")
|
||
archivedAt DateTime? @map("archived_at")
|
||
archivedByUserId String? @map("archived_by_user_id")
|
||
archivedByName String? @map("archived_by_name")
|
||
archiveReason String? @map("archive_reason") @db.VarChar(500)
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
versions PerformanceSurveyTemplateVersion[]
|
||
|
||
@@index([surveyType])
|
||
@@index([isActive])
|
||
@@index([archivedAt])
|
||
@@map("performance_survey_templates")
|
||
}
|
||
|
||
model PerformanceSurveyTemplateVersion {
|
||
id String @id @default(uuid())
|
||
templateId String @map("template_id")
|
||
version Int
|
||
status String @default("draft")
|
||
effectiveMonth String? @map("effective_month")
|
||
definition Json
|
||
publishedAt DateTime? @map("published_at")
|
||
createdByUserId String? @map("created_by_user_id")
|
||
createdByName String? @map("created_by_name")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
template PerformanceSurveyTemplate @relation(fields: [templateId], references: [id], onDelete: Cascade)
|
||
positionPlanItems PerformanceSurveyPositionPlanItem[]
|
||
surveys PerformanceSurvey[]
|
||
|
||
@@unique([templateId, version], map: "performance_survey_template_version_unique")
|
||
@@index([status, effectiveMonth], map: "performance_survey_template_version_status_month_idx")
|
||
@@map("performance_survey_template_versions")
|
||
}
|
||
|
||
model PerformanceSurveyPositionGroup {
|
||
id String @id @default(uuid())
|
||
code String @unique
|
||
name String
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
isActive Boolean @default(true) @map("is_active")
|
||
isSystem Boolean @default(false) @map("is_system")
|
||
archivedAt DateTime? @map("archived_at")
|
||
archivedByUserId String? @map("archived_by_user_id")
|
||
archivedByName String? @map("archived_by_name")
|
||
archiveReason String? @map("archive_reason") @db.VarChar(500)
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
rules PerformanceSurveyPositionRule[]
|
||
plans PerformanceSurveyPositionPlan[]
|
||
surveys PerformanceSurvey[]
|
||
manualAssignments PerformanceSurveyEmployeePositionAssignment[]
|
||
|
||
@@index([isActive, sortOrder], map: "performance_survey_position_group_active_sort_idx")
|
||
@@index([archivedAt, sortOrder], map: "performance_survey_position_group_archived_sort_idx")
|
||
@@map("performance_survey_position_groups")
|
||
}
|
||
|
||
model PerformanceSurveyPositionRule {
|
||
id String @id @default(uuid())
|
||
positionGroupId String @map("position_group_id")
|
||
departmentPathPrefix String? @map("department_path_prefix")
|
||
positionTitle String? @map("position_title")
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
isActive Boolean @default(true) @map("is_active")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
positionGroup PerformanceSurveyPositionGroup @relation(fields: [positionGroupId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([positionGroupId, isActive, sortOrder], map: "performance_survey_position_rule_group_idx")
|
||
@@index([positionTitle], map: "performance_survey_position_rule_title_idx")
|
||
@@map("performance_survey_position_rules")
|
||
}
|
||
|
||
model PerformanceSurveyPositionPlan {
|
||
id String @id @default(uuid())
|
||
positionGroupId String @map("position_group_id")
|
||
version Int
|
||
status String @default("draft")
|
||
effectiveMonth String? @map("effective_month")
|
||
matchRules Json @default("[]") @map("match_rules")
|
||
groupConfig Json? @map("group_config")
|
||
publishedAt DateTime? @map("published_at")
|
||
createdByUserId String? @map("created_by_user_id")
|
||
createdByName String? @map("created_by_name")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
positionGroup PerformanceSurveyPositionGroup @relation(fields: [positionGroupId], references: [id], onDelete: Cascade)
|
||
items PerformanceSurveyPositionPlanItem[]
|
||
|
||
@@unique([positionGroupId, version], map: "performance_survey_position_plan_version_unique")
|
||
@@index([positionGroupId, status, effectiveMonth], map: "performance_survey_position_plan_group_status_month_idx")
|
||
@@map("performance_survey_position_plans")
|
||
}
|
||
|
||
model PerformanceSurveyEmployeePositionAssignment {
|
||
id String @id @default(uuid())
|
||
employeeProfileId String @map("employee_profile_id")
|
||
assignmentType String @map("assignment_type")
|
||
positionGroupId String? @map("position_group_id")
|
||
startMonth String @map("start_month")
|
||
endMonth String? @map("end_month")
|
||
reason String @db.VarChar(500)
|
||
status String @default("active")
|
||
createdByUserId String @map("created_by_user_id")
|
||
createdByName String @map("created_by_name")
|
||
archivedAt DateTime? @map("archived_at")
|
||
archivedByUserId String? @map("archived_by_user_id")
|
||
archivedByName String? @map("archived_by_name")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
employeeProfile PerformanceSurveyEmployeeProfile @relation(fields: [employeeProfileId], references: [id], onDelete: Restrict)
|
||
positionGroup PerformanceSurveyPositionGroup? @relation(fields: [positionGroupId], references: [id], onDelete: Restrict)
|
||
|
||
@@index([employeeProfileId, status, startMonth, endMonth], map: "performance_survey_manual_assignment_employee_range_idx")
|
||
@@index([positionGroupId], map: "performance_survey_manual_assignment_group_idx")
|
||
@@index([status, startMonth, endMonth], map: "performance_survey_manual_assignment_status_range_idx")
|
||
@@map("performance_survey_employee_position_assignments")
|
||
}
|
||
|
||
model PerformanceSurveyPositionPlanItem {
|
||
id String @id @default(uuid())
|
||
positionPlanId String @map("position_plan_id")
|
||
templateVersionId String @map("template_version_id")
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
isEnabled Boolean @default(true) @map("is_enabled")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
positionPlan PerformanceSurveyPositionPlan @relation(fields: [positionPlanId], references: [id], onDelete: Cascade)
|
||
templateVersion PerformanceSurveyTemplateVersion @relation(fields: [templateVersionId], references: [id], onDelete: Restrict)
|
||
|
||
@@unique([positionPlanId, templateVersionId], map: "performance_survey_position_plan_item_unique")
|
||
@@index([positionPlanId, isEnabled, sortOrder], map: "performance_survey_position_plan_item_sort_idx")
|
||
@@map("performance_survey_position_plan_items")
|
||
}
|
||
|
||
model PerformanceSurvey {
|
||
id String @id @default(uuid())
|
||
title String
|
||
description String?
|
||
surveyKind String @default("person_period") @map("survey_kind")
|
||
templateCode String @map("template_code")
|
||
templateSnapshot Json @map("template_snapshot")
|
||
templateVersionId String? @map("template_version_id")
|
||
positionGroupId String? @map("position_group_id")
|
||
positionGroupCode String? @map("position_group_code")
|
||
positionGroupSnapshot Json? @map("position_group_snapshot")
|
||
status String @default("draft")
|
||
creatorUserId String @map("creator_user_id")
|
||
creatorName String @map("creator_name")
|
||
creatorEmployeeId String? @map("creator_employee_id")
|
||
departmentName String? @map("department_name")
|
||
assessmentMonth String? @map("assessment_month")
|
||
dueAt DateTime? @map("due_at")
|
||
openedAt DateTime? @map("opened_at")
|
||
closedAt DateTime? @map("closed_at")
|
||
cancelledAt DateTime? @map("cancelled_at")
|
||
cancelledByUserId String? @map("cancelled_by_user_id")
|
||
cancelledByName String? @map("cancelled_by_name")
|
||
cancelReason String? @db.VarChar(500) @map("cancel_reason")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
targets PerformanceSurveyTarget[]
|
||
assignments PerformanceSurveyAssignment[]
|
||
responses PerformanceSurveyResponse[]
|
||
workItems PerformanceSurveyWorkItem[]
|
||
workItemScores PerformanceSurveyWorkItemScore[]
|
||
notifications PerformanceSurveyNotification[]
|
||
templateVersion PerformanceSurveyTemplateVersion? @relation(fields: [templateVersionId], references: [id], onDelete: SetNull)
|
||
positionGroup PerformanceSurveyPositionGroup? @relation(fields: [positionGroupId], references: [id], onDelete: SetNull)
|
||
|
||
@@index([status])
|
||
@@index([surveyKind])
|
||
@@index([templateCode])
|
||
@@index([templateVersionId])
|
||
@@index([positionGroupId, assessmentMonth], map: "performance_surveys_position_group_month_idx")
|
||
@@index([positionGroupCode, assessmentMonth], map: "performance_surveys_position_group_code_month_idx")
|
||
@@index([templateCode, assessmentMonth], map: "performance_surveys_template_month_idx")
|
||
@@index([creatorUserId])
|
||
@@index([departmentName])
|
||
@@index([assessmentMonth])
|
||
@@index([dueAt])
|
||
@@map("performance_surveys")
|
||
}
|
||
|
||
model PerformanceSurveyTarget {
|
||
id String @id @default(uuid())
|
||
surveyId String @map("survey_id")
|
||
targetType String @default("employee") @map("target_type")
|
||
employeeProfileId String? @map("employee_profile_id")
|
||
targetName String @map("target_name")
|
||
targetSnapshot Json @map("target_snapshot")
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
survey PerformanceSurvey @relation(fields: [surveyId], references: [id], onDelete: Cascade)
|
||
employeeProfile PerformanceSurveyEmployeeProfile? @relation("PerformanceSurveyTargetEmployee", fields: [employeeProfileId], references: [id])
|
||
assignments PerformanceSurveyAssignment[]
|
||
responses PerformanceSurveyResponse[]
|
||
workItems PerformanceSurveyWorkItem[]
|
||
workItemScores PerformanceSurveyWorkItemScore[]
|
||
notifications PerformanceSurveyNotification[]
|
||
|
||
@@index([surveyId])
|
||
@@index([targetType])
|
||
@@index([employeeProfileId])
|
||
@@index([employeeProfileId, surveyId], map: "performance_survey_targets_employee_survey_idx")
|
||
@@map("performance_survey_targets")
|
||
}
|
||
|
||
model PerformanceSurveyAssignment {
|
||
id String @id @default(uuid())
|
||
surveyId String @map("survey_id")
|
||
targetId String @map("target_id")
|
||
evaluatorEmployeeId String? @map("evaluator_employee_id")
|
||
evaluatorUserId String? @map("evaluator_user_id")
|
||
evaluatorDingtalkUserId String? @map("evaluator_dingtalk_user_id")
|
||
evaluatorKey String @map("evaluator_key")
|
||
evaluatorName String @map("evaluator_name")
|
||
evaluatorSnapshot Json @map("evaluator_snapshot")
|
||
workflowStageKey String @default("legacy") @map("workflow_stage_key")
|
||
workflowStageOrder Int @default(0) @map("workflow_stage_order")
|
||
reviewerSource String @default("manual_selection") @map("reviewer_source")
|
||
releasedAt DateTime? @map("released_at")
|
||
status String @default("pending")
|
||
submittedAt DateTime? @map("submitted_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
survey PerformanceSurvey @relation(fields: [surveyId], references: [id], onDelete: Cascade)
|
||
target PerformanceSurveyTarget @relation(fields: [targetId], references: [id], onDelete: Cascade)
|
||
evaluatorEmployee PerformanceSurveyEmployeeProfile? @relation("PerformanceSurveyEvaluator", fields: [evaluatorEmployeeId], references: [id])
|
||
response PerformanceSurveyResponse?
|
||
workItemScores PerformanceSurveyWorkItemScore[]
|
||
notifications PerformanceSurveyNotification[]
|
||
|
||
@@unique([targetId, evaluatorKey, workflowStageKey], map: "performance_survey_assignment_target_evaluator_stage_key")
|
||
@@index([surveyId])
|
||
@@index([targetId])
|
||
@@index([evaluatorEmployeeId])
|
||
@@index([evaluatorUserId])
|
||
@@index([evaluatorDingtalkUserId])
|
||
@@index([evaluatorKey])
|
||
@@index([surveyId, targetId, workflowStageOrder], map: "performance_survey_assignment_stage_idx")
|
||
@@index([status])
|
||
@@map("performance_survey_assignments")
|
||
}
|
||
|
||
model PerformanceSurveyResponse {
|
||
id String @id @default(uuid())
|
||
surveyId String @map("survey_id")
|
||
targetId String @map("target_id")
|
||
assignmentId String @unique @map("assignment_id")
|
||
respondentUserId String @map("respondent_user_id")
|
||
respondentEmployeeId String? @map("respondent_employee_id")
|
||
respondentName String @map("respondent_name")
|
||
respondentSnapshot Json @map("respondent_snapshot")
|
||
scores Json
|
||
comment String?
|
||
totalScore Float @map("total_score")
|
||
achievementScore Float @map("achievement_score")
|
||
kpiScore Float @map("kpi_score")
|
||
weightedScore Float @map("weighted_score")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
survey PerformanceSurvey @relation(fields: [surveyId], references: [id], onDelete: Cascade)
|
||
target PerformanceSurveyTarget @relation(fields: [targetId], references: [id], onDelete: Cascade)
|
||
assignment PerformanceSurveyAssignment @relation(fields: [assignmentId], references: [id], onDelete: Cascade)
|
||
respondentEmployee PerformanceSurveyEmployeeProfile? @relation("PerformanceSurveyRespondentEmployee", fields: [respondentEmployeeId], references: [id])
|
||
|
||
@@index([surveyId])
|
||
@@index([targetId])
|
||
@@index([respondentUserId])
|
||
@@index([respondentEmployeeId])
|
||
@@index([createdAt])
|
||
@@map("performance_survey_responses")
|
||
}
|
||
|
||
model PerformanceSurveyWorkItem {
|
||
id String @id @default(uuid())
|
||
surveyId String @map("survey_id")
|
||
targetId String @map("target_id")
|
||
submitterUserId String? @map("submitter_user_id")
|
||
submitterEmployeeId String? @map("submitter_employee_id")
|
||
title String
|
||
description String?
|
||
imageUrls Json @map("image_urls")
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
status String @default("submitted")
|
||
submittedAt DateTime? @map("submitted_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
survey PerformanceSurvey @relation(fields: [surveyId], references: [id], onDelete: Cascade)
|
||
target PerformanceSurveyTarget @relation(fields: [targetId], references: [id], onDelete: Cascade)
|
||
submitterEmployee PerformanceSurveyEmployeeProfile? @relation("PerformanceSurveyWorkItemSubmitter", fields: [submitterEmployeeId], references: [id])
|
||
scores PerformanceSurveyWorkItemScore[]
|
||
|
||
@@index([surveyId])
|
||
@@index([targetId])
|
||
@@index([submitterUserId])
|
||
@@index([submitterEmployeeId])
|
||
@@index([status])
|
||
@@map("performance_survey_work_items")
|
||
}
|
||
|
||
model PerformanceSurveyWorkItemScore {
|
||
id String @id @default(uuid())
|
||
workItemId String @map("work_item_id")
|
||
surveyId String @map("survey_id")
|
||
targetId String @map("target_id")
|
||
assignmentId String @map("assignment_id")
|
||
evaluatorUserId String @map("evaluator_user_id")
|
||
evaluatorEmployeeId String? @map("evaluator_employee_id")
|
||
evaluatorName String @map("evaluator_name")
|
||
scores Json
|
||
comment String?
|
||
totalScore Float @map("total_score")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
workItem PerformanceSurveyWorkItem @relation(fields: [workItemId], references: [id], onDelete: Cascade)
|
||
survey PerformanceSurvey @relation(fields: [surveyId], references: [id], onDelete: Cascade)
|
||
target PerformanceSurveyTarget @relation(fields: [targetId], references: [id], onDelete: Cascade)
|
||
assignment PerformanceSurveyAssignment @relation(fields: [assignmentId], references: [id], onDelete: Cascade)
|
||
evaluatorEmployee PerformanceSurveyEmployeeProfile? @relation("PerformanceSurveyWorkItemScoreEvaluator", fields: [evaluatorEmployeeId], references: [id])
|
||
|
||
@@unique([workItemId, assignmentId], map: "performance_survey_work_item_score_unique")
|
||
@@index([surveyId])
|
||
@@index([targetId])
|
||
@@index([assignmentId])
|
||
@@index([evaluatorUserId])
|
||
@@index([evaluatorEmployeeId])
|
||
@@map("performance_survey_work_item_scores")
|
||
}
|
||
|
||
model PerformanceSurveyNotification {
|
||
id String @id @default(uuid())
|
||
surveyId String? @map("survey_id")
|
||
targetId String? @map("target_id")
|
||
assignmentId String? @map("assignment_id")
|
||
recipientEmployeeId String? @map("recipient_employee_id")
|
||
recipientUserId String? @map("recipient_user_id")
|
||
dingtalkUserId String? @map("dingtalk_user_id")
|
||
recipientName String @map("recipient_name")
|
||
eventType String @map("event_type")
|
||
businessKey String @map("business_key")
|
||
title String
|
||
content String
|
||
actionUrl String? @map("action_url")
|
||
status String @default("pending")
|
||
attemptCount Int @default(0) @map("attempt_count")
|
||
maxAttempts Int @default(5) @map("max_attempts")
|
||
nextRetryAt DateTime? @map("next_retry_at")
|
||
lastStatusCode Int? @map("last_status_code")
|
||
lastResponseBody String? @map("last_response_body")
|
||
lastError String? @map("last_error")
|
||
providerType String @default("work_notice") @map("provider_type")
|
||
providerMessageId String? @map("provider_message_id")
|
||
readStatus String @default("unknown") @map("read_status")
|
||
readAt DateTime? @map("read_at")
|
||
readStatusCheckedAt DateTime? @map("read_status_checked_at")
|
||
sentAt DateTime? @map("sent_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
survey PerformanceSurvey? @relation(fields: [surveyId], references: [id], onDelete: Cascade)
|
||
target PerformanceSurveyTarget? @relation(fields: [targetId], references: [id], onDelete: Cascade)
|
||
assignment PerformanceSurveyAssignment? @relation(fields: [assignmentId], references: [id], onDelete: Cascade)
|
||
recipientEmployee PerformanceSurveyEmployeeProfile? @relation(fields: [recipientEmployeeId], references: [id])
|
||
|
||
@@unique([eventType, businessKey], map: "performance_survey_notification_event_key")
|
||
@@index([surveyId])
|
||
@@index([targetId])
|
||
@@index([assignmentId])
|
||
@@index([recipientEmployeeId])
|
||
@@index([recipientUserId])
|
||
@@index([dingtalkUserId])
|
||
@@index([status, nextRetryAt])
|
||
@@index([readStatus, readStatusCheckedAt], map: "performance_survey_notifications_read_status_checked_at_idx")
|
||
@@index([createdAt])
|
||
@@map("performance_survey_notifications")
|
||
}
|
||
|
||
model PerformanceSurveyConfigAuditLog {
|
||
id String @id @default(uuid())
|
||
action String
|
||
targetType String @map("target_type")
|
||
targetId String? @map("target_id")
|
||
actorUserId String @map("actor_user_id")
|
||
actorName String @map("actor_name")
|
||
reason String? @db.VarChar(500)
|
||
beforeSnapshot Json? @map("before_snapshot")
|
||
afterSnapshot Json? @map("after_snapshot")
|
||
metadata Json?
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
@@index([targetType, targetId, createdAt], map: "performance_survey_config_audit_target_idx")
|
||
@@index([actorUserId, createdAt], map: "performance_survey_config_audit_actor_idx")
|
||
@@index([createdAt], map: "performance_survey_config_audit_created_idx")
|
||
@@map("performance_survey_config_audit_logs")
|
||
}
|
||
|
||
model WorkReviewProject {
|
||
id String @id @default(uuid())
|
||
title String
|
||
brand String?
|
||
description String?
|
||
workDate DateTime @db.Date @map("work_date")
|
||
submitterUserId String @map("submitter_user_id")
|
||
submitterEmployeeId String @map("submitter_employee_id")
|
||
submitterName String @map("submitter_name")
|
||
submitterSnapshot Json @map("submitter_snapshot")
|
||
managerEmployeeId String @map("manager_employee_id")
|
||
managerUserId String? @map("manager_user_id")
|
||
managerDingtalkUserId String? @map("manager_dingtalk_user_id")
|
||
managerName String @map("manager_name")
|
||
finalReviewerEmployeeId String @map("final_reviewer_employee_id")
|
||
finalReviewerUserId String? @map("final_reviewer_user_id")
|
||
finalReviewerDingtalkUserId String? @map("final_reviewer_dingtalk_user_id")
|
||
finalReviewerName String @map("final_reviewer_name")
|
||
status String @default("pending_review")
|
||
latestVersion Int @default(1) @map("latest_version")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
submitterEmployee PerformanceSurveyEmployeeProfile @relation("WorkReviewSubmitter", fields: [submitterEmployeeId], references: [id], onDelete: Restrict)
|
||
managerEmployee PerformanceSurveyEmployeeProfile @relation("WorkReviewManager", fields: [managerEmployeeId], references: [id], onDelete: Restrict)
|
||
finalReviewerEmployee PerformanceSurveyEmployeeProfile @relation("WorkReviewFinalReviewer", fields: [finalReviewerEmployeeId], references: [id], onDelete: Restrict)
|
||
versions WorkReviewVersion[]
|
||
notifications WorkReviewNotification[]
|
||
|
||
@@index([submitterUserId, workDate], map: "work_review_projects_submitter_date_idx")
|
||
@@index([submitterEmployeeId, workDate], map: "work_review_projects_submitter_employee_date_idx")
|
||
@@index([managerEmployeeId, status], map: "work_review_projects_manager_status_idx")
|
||
@@index([finalReviewerEmployeeId, status], map: "work_review_projects_final_reviewer_status_idx")
|
||
@@index([status, workDate], map: "work_review_projects_status_date_idx")
|
||
@@index([updatedAt], map: "work_review_projects_updated_at_idx")
|
||
@@map("work_review_projects")
|
||
}
|
||
|
||
model WorkReviewVersion {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
version Int
|
||
submissionNote String? @map("submission_note")
|
||
imageUrls Json @map("image_urls")
|
||
status String @default("pending_review")
|
||
submittedAt DateTime @default(now()) @map("submitted_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
project WorkReviewProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||
reviews WorkReviewReview[]
|
||
notifications WorkReviewNotification[]
|
||
|
||
@@unique([projectId, version], map: "work_review_versions_project_version_key")
|
||
@@index([projectId, submittedAt], map: "work_review_versions_project_submitted_idx")
|
||
@@index([status, submittedAt], map: "work_review_versions_status_submitted_idx")
|
||
@@map("work_review_versions")
|
||
}
|
||
|
||
model WorkReviewReview {
|
||
id String @id @default(uuid())
|
||
versionId String @map("version_id")
|
||
reviewerRole String @map("reviewer_role")
|
||
reviewerEmployeeId String @map("reviewer_employee_id")
|
||
reviewerUserId String? @map("reviewer_user_id")
|
||
reviewerName String @map("reviewer_name")
|
||
reviewerSnapshot Json @map("reviewer_snapshot")
|
||
decision String
|
||
imageComments Json @map("image_comments")
|
||
generalComment String? @map("general_comment")
|
||
reviewedAt DateTime @default(now()) @map("reviewed_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
version WorkReviewVersion @relation(fields: [versionId], references: [id], onDelete: Cascade)
|
||
reviewerEmployee PerformanceSurveyEmployeeProfile @relation("WorkReviewReviewer", fields: [reviewerEmployeeId], references: [id], onDelete: Restrict)
|
||
|
||
@@unique([versionId, reviewerRole], map: "work_review_reviews_version_role_key")
|
||
@@index([reviewerEmployeeId, reviewedAt], map: "work_review_reviews_reviewer_reviewed_idx")
|
||
@@index([decision], map: "work_review_reviews_decision_idx")
|
||
@@map("work_review_reviews")
|
||
}
|
||
|
||
model WorkReviewNotification {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
versionId String @map("version_id")
|
||
recipientEmployeeId String? @map("recipient_employee_id")
|
||
recipientUserId String? @map("recipient_user_id")
|
||
dingtalkUserId String? @map("dingtalk_user_id")
|
||
recipientName String @map("recipient_name")
|
||
eventType String @map("event_type")
|
||
businessKey String @map("business_key")
|
||
title String
|
||
content String
|
||
actionUrl String? @map("action_url")
|
||
status String @default("pending")
|
||
attemptCount Int @default(0) @map("attempt_count")
|
||
maxAttempts Int @default(5) @map("max_attempts")
|
||
nextRetryAt DateTime? @map("next_retry_at")
|
||
providerType String @default("work_notice") @map("provider_type")
|
||
providerMessageId String? @map("provider_message_id")
|
||
lastError String? @map("last_error")
|
||
sentAt DateTime? @map("sent_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
project WorkReviewProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||
version WorkReviewVersion @relation(fields: [versionId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([eventType, businessKey], map: "work_review_notifications_event_key")
|
||
@@index([projectId, createdAt], map: "work_review_notifications_project_created_idx")
|
||
@@index([versionId], map: "work_review_notifications_version_idx")
|
||
@@index([recipientEmployeeId], map: "work_review_notifications_recipient_employee_idx")
|
||
@@index([recipientUserId], map: "work_review_notifications_recipient_user_idx")
|
||
@@index([dingtalkUserId], map: "work_review_notifications_dingtalk_user_idx")
|
||
@@index([status, nextRetryAt], map: "work_review_notifications_status_retry_idx")
|
||
@@map("work_review_notifications")
|
||
}
|
||
|
||
/// 任务详情图片变更申请。正式素材只在直属主管批准后才应用。
|
||
model TaskDetailChangeRequest {
|
||
id String @id @default(uuid())
|
||
materialId String @map("material_id")
|
||
requesterUserId String @map("requester_user_id")
|
||
requesterEmployeeId String @map("requester_employee_id")
|
||
requesterName String @map("requester_name")
|
||
requesterSnapshot Json @map("requester_snapshot")
|
||
requesterPositionLevel String? @map("requester_position_level")
|
||
requesterLevelType String? @map("requester_level_type")
|
||
requesterIsLeadership Boolean @default(false) @map("requester_is_leadership")
|
||
managerEmployeeId String @map("manager_employee_id")
|
||
managerUserId String? @map("manager_user_id")
|
||
managerDingtalkUserId String @map("manager_dingtalk_user_id")
|
||
managerName String @map("manager_name")
|
||
managerSnapshot Json @map("manager_snapshot")
|
||
status String @default("pending_manager_approval")
|
||
operations Json
|
||
baseMaterialUpdatedAt DateTime @map("base_material_updated_at")
|
||
submittedAt DateTime @default(now()) @map("submitted_at")
|
||
expiresAt DateTime @map("expires_at")
|
||
decidedAt DateTime? @map("decided_at")
|
||
decidedByUserId String? @map("decided_by_user_id")
|
||
decisionComment String? @map("decision_comment")
|
||
appliedAt DateTime? @map("applied_at")
|
||
applyError String? @map("apply_error")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
material Material @relation(fields: [materialId], references: [id], onDelete: Cascade)
|
||
requesterUser User @relation("TaskDetailChangeRequesterUser", fields: [requesterUserId], references: [id], onDelete: Restrict)
|
||
managerUser User? @relation("TaskDetailChangeManagerUser", fields: [managerUserId], references: [id], onDelete: SetNull)
|
||
requesterEmployee PerformanceSurveyEmployeeProfile @relation("TaskDetailChangeRequesterEmployee", fields: [requesterEmployeeId], references: [id], onDelete: Restrict)
|
||
managerEmployee PerformanceSurveyEmployeeProfile @relation("TaskDetailChangeManagerEmployee", fields: [managerEmployeeId], references: [id], onDelete: Restrict)
|
||
notifications TaskDetailChangeNotification[]
|
||
|
||
@@index([materialId, createdAt], map: "task_detail_change_requests_material_created_idx")
|
||
@@index([requesterUserId, createdAt], map: "task_detail_change_requests_requester_created_idx")
|
||
@@index([managerDingtalkUserId, status], map: "task_detail_change_requests_manager_status_idx")
|
||
@@index([status, expiresAt], map: "task_detail_change_requests_status_expires_idx")
|
||
@@index([status, updatedAt], map: "task_detail_change_requests_status_updated_idx")
|
||
@@map("task_detail_change_requests")
|
||
}
|
||
|
||
/// 任务详情变更审批通知 outbox;投递失败可恢复,不影响审批主状态。
|
||
model TaskDetailChangeNotification {
|
||
id String @id @default(uuid())
|
||
requestId String @map("request_id")
|
||
recipientUserId String? @map("recipient_user_id")
|
||
dingtalkUserId String @map("dingtalk_user_id")
|
||
recipientName String @map("recipient_name")
|
||
eventType String @map("event_type")
|
||
businessKey String @map("business_key")
|
||
title String
|
||
content String
|
||
actionUrl String @map("action_url")
|
||
status String @default("pending")
|
||
attemptCount Int @default(0) @map("attempt_count")
|
||
maxAttempts Int @default(5) @map("max_attempts")
|
||
nextRetryAt DateTime? @map("next_retry_at")
|
||
providerMessageId String? @map("provider_message_id")
|
||
lastError String? @map("last_error")
|
||
sentAt DateTime? @map("sent_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
request TaskDetailChangeRequest @relation(fields: [requestId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([eventType, businessKey], map: "task_detail_change_notifications_event_key")
|
||
@@index([requestId, createdAt], map: "task_detail_change_notifications_request_created_idx")
|
||
@@index([status, nextRetryAt], map: "task_detail_change_notifications_status_retry_idx")
|
||
@@map("task_detail_change_notifications")
|
||
}
|
||
|
||
|
||
/// 钉钉审批流程的本地只读快照。
|
||
///
|
||
/// 上游 rpt.ding_Workflow / ding_WorkflowTask / ding_WorkflowScheme 均为无索引大表,
|
||
/// 部门看板不得在用户请求链路中直连扫描。后台任务首次建立全量基线,之后每小时
|
||
/// 增量同步主流程,并每 24 小时全量校准流程与商品 ID 表单字段。
|
||
model DingWorkflowSnapshot {
|
||
processInstanceId String @id @map("process_instance_id")
|
||
businessId String? @map("business_id")
|
||
title String?
|
||
result String?
|
||
originatorUserId String? @map("originator_user_id")
|
||
originatorDeptId String? @map("originator_dept_id")
|
||
originatorDeptName String? @map("originator_dept_name")
|
||
status String
|
||
bizAction String? @map("biz_action")
|
||
processCode String? @map("process_code")
|
||
flowType String? @map("flow_type")
|
||
joinedAtRaw String? @map("joined_at_raw")
|
||
sourceCreatedAtRaw String? @map("source_created_at_raw")
|
||
sourceUpdatedAtRaw String? @map("source_updated_at_raw")
|
||
sourceFinishedAtRaw String? @map("source_finished_at_raw")
|
||
isOngoing Boolean @default(false) @map("is_ongoing")
|
||
schemeFieldCount Int @default(0) @map("scheme_field_count")
|
||
taskCount Int @default(0) @map("task_count")
|
||
activeTaskCount Int @default(0) @map("active_task_count")
|
||
latestTaskStatus String? @map("latest_task_status")
|
||
latestTaskAtRaw String? @map("latest_task_at_raw")
|
||
brand String?
|
||
demandType String? @map("demand_type")
|
||
requesterName String? @map("requester_name")
|
||
requesterDepartment String? @map("requester_department")
|
||
projectType String? @map("project_type")
|
||
formFields Json? @map("form_fields")
|
||
activeTasks Json? @map("active_tasks")
|
||
syncedAt DateTime @map("synced_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([isOngoing, sourceUpdatedAtRaw], map: "ding_workflow_snapshots_ongoing_updated_idx")
|
||
@@index([status], map: "ding_workflow_snapshots_status_idx")
|
||
@@index([originatorDeptName], map: "ding_workflow_snapshots_department_idx")
|
||
@@index([brand], map: "ding_workflow_snapshots_brand_idx")
|
||
@@index([demandType], map: "ding_workflow_snapshots_demand_type_idx")
|
||
@@index([requesterDepartment], map: "ding_workflow_snapshots_requester_department_idx")
|
||
@@map("ding_workflow_snapshots")
|
||
}
|
||
|
||
/// 钉钉审批流程同步游标、租约与可观测状态。
|
||
model DingWorkflowSyncState {
|
||
id String @id
|
||
status String @default("idle")
|
||
sourceCursorRaw String? @map("source_cursor_raw")
|
||
lastStartedAt DateTime? @map("last_started_at")
|
||
lastCompletedAt DateTime? @map("last_completed_at")
|
||
lastError String? @map("last_error")
|
||
leaseOwner String? @map("lease_owner")
|
||
leaseExpiresAt DateTime? @map("lease_expires_at")
|
||
rowsUpserted Int @default(0) @map("rows_upserted")
|
||
currentOngoingCount Int @default(0) @map("current_ongoing_count")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([leaseExpiresAt], map: "ding_workflow_sync_states_lease_idx")
|
||
@@map("ding_workflow_sync_states")
|
||
}
|
||
|
||
/// 业绩转化商品经营数据的本地只读快照。
|
||
///
|
||
/// 上游经营报表按商品和统计日期保存多行来源记录。快照保留来源行,
|
||
/// 详情页再按日期聚合,避免把来源表的修订值固化成不可追溯的汇总值。
|
||
model PerformanceConversionMetricSnapshot {
|
||
id String @id @default(uuid())
|
||
sourceKey String @unique @map("source_key")
|
||
sourceRowId String? @map("source_row_id")
|
||
productId String @map("product_id")
|
||
metricDate DateTime @db.Date @map("metric_date")
|
||
productName String? @map("product_name")
|
||
sourceBrand String? @map("source_brand")
|
||
category1 String?
|
||
category2 String?
|
||
category3 String?
|
||
paymentAmount String? @map("payment_amount")
|
||
paymentUsers String? @map("payment_users")
|
||
cartUsers String? @map("cart_users")
|
||
visitors String?
|
||
conversionRate String? @map("conversion_rate")
|
||
favorites String?
|
||
impressions String?
|
||
clicks String?
|
||
clickRate String? @map("click_rate")
|
||
promotionSpend String? @map("promotion_spend")
|
||
promotionRevenue String? @map("promotion_revenue")
|
||
roi String?
|
||
syncedAt DateTime @map("synced_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([productId, metricDate], map: "performance_conversion_metric_product_date_idx")
|
||
@@index([metricDate], map: "performance_conversion_metric_date_idx")
|
||
@@map("performance_conversion_metric_snapshots")
|
||
}
|
||
|
||
/// 业绩转化经营数据每日增量同步游标、租约与可观测状态。
|
||
model PerformanceConversionSyncState {
|
||
id String @id
|
||
status String @default("idle")
|
||
sourceCursorRaw String? @map("source_cursor_raw")
|
||
lastStartedAt DateTime? @map("last_started_at")
|
||
lastCompletedAt DateTime? @map("last_completed_at")
|
||
lastError String? @map("last_error")
|
||
leaseOwner String? @map("lease_owner")
|
||
leaseExpiresAt DateTime? @map("lease_expires_at")
|
||
rowsUpserted Int @default(0) @map("rows_upserted")
|
||
productsSynced Int @default(0) @map("products_synced")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([leaseExpiresAt], map: "performance_conversion_sync_states_lease_idx")
|
||
@@map("performance_conversion_sync_states")
|
||
}
|
||
|
||
model VideoTranscodeProgress {
|
||
id String @id @default(uuid())
|
||
materialId String @map("material_id")
|
||
stage String
|
||
progress Int @default(0)
|
||
status String @default("waiting") @map("status")
|
||
priority Int @default(0)
|
||
startedAt DateTime @default(now()) @map("started_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
errorMessage String? @map("error_message")
|
||
retryCount Int @default(0) @map("retry_count")
|
||
lastHeartbeat DateTime? @map("last_heartbeat")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
material Material @relation("MaterialTranscodeProgress", fields: [materialId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([materialId, stage])
|
||
@@index([status])
|
||
@@index([priority, status])
|
||
@@index([materialId])
|
||
@@index([lastHeartbeat])
|
||
@@map("video_transcode_progresses")
|
||
}
|
||
|
||
enum Role {
|
||
USER
|
||
ADMIN
|
||
DEPT_ADMIN
|
||
SYSTEM_ADMIN
|
||
}
|