Initial commit: digital reception monorepo (M1-M11 + demo extensions)
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
previewFeatures = ["postgresqlExtensions"]
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
extensions = [vector, uuidOssp(map: "uuid-ossp")]
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// AUTH / RBAC
|
||||
// =========================================================================
|
||||
|
||||
enum Role {
|
||||
MANAGER
|
||||
SENIOR_ADMIN
|
||||
SECURITY
|
||||
SYSADMIN
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
email String @unique
|
||||
fullName String @map("full_name")
|
||||
passwordHash String @map("password_hash")
|
||||
role Role
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
refreshTokens RefreshToken[]
|
||||
consentActions PatientConsent[]
|
||||
biometryAccessLog BiometryAccessLog[]
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
model RefreshToken {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
userId String @map("user_id") @db.Uuid
|
||||
tokenHash String @unique @map("token_hash")
|
||||
expiresAt DateTime @map("expires_at")
|
||||
revokedAt DateTime? @map("revoked_at")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId])
|
||||
@@map("refresh_tokens")
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// DOMAIN: PATIENT / CONSENT
|
||||
// =========================================================================
|
||||
|
||||
model Patient {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
polimedPatientId String? @unique @map("polimed_patient_id")
|
||||
fullName String? @map("full_name")
|
||||
consentReceivedAt DateTime? @map("consent_received_at")
|
||||
consentRevokedAt DateTime? @map("consent_revoked_at")
|
||||
pendingDeletionAt DateTime? @map("pending_deletion_at")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
consents PatientConsent[]
|
||||
tracks Track[]
|
||||
visits Visit[]
|
||||
consentRevocationJobs ConsentRevocationJob[]
|
||||
|
||||
@@map("patients")
|
||||
}
|
||||
|
||||
enum ConsentAction {
|
||||
GRANTED
|
||||
REVOKED
|
||||
}
|
||||
|
||||
model PatientConsent {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
patientId String @map("patient_id") @db.Uuid
|
||||
action ConsentAction
|
||||
paperRef String? @map("paper_ref")
|
||||
actorUserId String @map("actor_user_id") @db.Uuid
|
||||
occurredAt DateTime @default(now()) @map("occurred_at")
|
||||
|
||||
patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade)
|
||||
actor User @relation(fields: [actorUserId], references: [id])
|
||||
|
||||
@@index([patientId])
|
||||
@@map("patient_consents")
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// CAMERAS / ZONES
|
||||
// =========================================================================
|
||||
|
||||
enum ZoneCode {
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
E
|
||||
}
|
||||
|
||||
model Zone {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
code ZoneCode @unique
|
||||
name String
|
||||
|
||||
cameras Camera[]
|
||||
trackEvents TrackEvent[]
|
||||
|
||||
@@map("zones")
|
||||
}
|
||||
|
||||
model Camera {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
name String @unique
|
||||
rtspUrl String? @map("rtsp_url")
|
||||
zoneId String @map("zone_id") @db.Uuid
|
||||
|
||||
zone Zone @relation(fields: [zoneId], references: [id])
|
||||
trackEvents TrackEvent[]
|
||||
faceEmbeddings FaceEmbedding[]
|
||||
|
||||
@@map("cameras")
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// TRACKS / EVENTS / EMBEDDINGS
|
||||
// =========================================================================
|
||||
|
||||
enum TrackStatus {
|
||||
UNMATCHED
|
||||
MATCHED
|
||||
ANONYMIZED
|
||||
}
|
||||
|
||||
model Track {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
patientId String? @map("patient_id") @db.Uuid
|
||||
status TrackStatus @default(UNMATCHED)
|
||||
firstSeenAt DateTime @map("first_seen_at")
|
||||
lastSeenAt DateTime @map("last_seen_at")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
patient Patient? @relation(fields: [patientId], references: [id])
|
||||
events TrackEvent[]
|
||||
faceEmbeddings FaceEmbedding[]
|
||||
|
||||
@@index([status, firstSeenAt])
|
||||
@@index([patientId])
|
||||
@@map("tracks")
|
||||
}
|
||||
|
||||
enum TrackEventType {
|
||||
arrived
|
||||
waiting
|
||||
service_started
|
||||
service_ended
|
||||
left_without_service
|
||||
}
|
||||
|
||||
model TrackEvent {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
trackId String @map("track_id") @db.Uuid
|
||||
type TrackEventType
|
||||
cameraId String @map("camera_id") @db.Uuid
|
||||
zoneId String @map("zone_id") @db.Uuid
|
||||
occurredAt DateTime @map("occurred_at")
|
||||
evidenceKey String? @map("evidence_key")
|
||||
// [x1, y1, x2, y2] нормализовано 0..1 — bbox распознанного лица на кадре.
|
||||
faceBbox Json? @map("face_bbox") @db.JsonB
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
track Track @relation(fields: [trackId], references: [id], onDelete: Cascade)
|
||||
camera Camera @relation(fields: [cameraId], references: [id])
|
||||
zone Zone @relation(fields: [zoneId], references: [id])
|
||||
|
||||
@@index([trackId, occurredAt])
|
||||
@@map("track_events")
|
||||
}
|
||||
|
||||
// 512-d face embedding (InsightFace buffalo_l).
|
||||
// face-service пишет и читает напрямую через psycopg2 + pgvector.
|
||||
model FaceEmbedding {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
embedding Unsupported("vector(512)")
|
||||
patientId String? @map("patient_id") @db.Uuid
|
||||
trackId String? @map("track_id") @db.Uuid
|
||||
cameraId String @map("camera_id") @db.Uuid
|
||||
quality Float @default(0)
|
||||
capturedAt DateTime @map("captured_at")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
track Track? @relation(fields: [trackId], references: [id], onDelete: SetNull)
|
||||
camera Camera @relation(fields: [cameraId], references: [id])
|
||||
|
||||
@@index([trackId])
|
||||
@@index([patientId])
|
||||
@@index([capturedAt])
|
||||
@@map("face_embeddings")
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// VISITS
|
||||
// =========================================================================
|
||||
|
||||
model Visit {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
patientId String @map("patient_id") @db.Uuid
|
||||
polimedAppointmentId String? @map("polimed_appointment_id")
|
||||
arrivedAt DateTime @map("arrived_at")
|
||||
serviceStartedAt DateTime? @map("service_started_at")
|
||||
serviceEndedAt DateTime? @map("service_ended_at")
|
||||
leftWithoutService Boolean @default(false) @map("left_without_service")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([patientId, arrivedAt])
|
||||
@@map("visits")
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// AUDIT
|
||||
// =========================================================================
|
||||
|
||||
model BiometryAccessLog {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
actorUserId String? @map("actor_user_id") @db.Uuid
|
||||
subjectPatientId String? @map("subject_patient_id") @db.Uuid
|
||||
action String
|
||||
requestPath String? @map("request_path")
|
||||
occurredAt DateTime @default(now()) @map("occurred_at")
|
||||
|
||||
actor User? @relation(fields: [actorUserId], references: [id])
|
||||
|
||||
@@index([occurredAt])
|
||||
@@index([subjectPatientId])
|
||||
@@map("biometry_access_log")
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// CONSENT REVOCATION JOBS (24h delete)
|
||||
// =========================================================================
|
||||
|
||||
enum ConsentRevocationStatus {
|
||||
PENDING
|
||||
DONE
|
||||
}
|
||||
|
||||
model ConsentRevocationJob {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
patientId String @map("patient_id") @db.Uuid
|
||||
revokedAt DateTime @map("revoked_at")
|
||||
scheduledFor DateTime @map("scheduled_for")
|
||||
status ConsentRevocationStatus @default(PENDING)
|
||||
completedAt DateTime? @map("completed_at")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([status, scheduledFor])
|
||||
@@map("consent_revocation_jobs")
|
||||
}
|
||||
Reference in New Issue
Block a user