feie9456 747dcd359a 家庭守护圈:多人加入、角色权限、24 小时邀请链接、旧设备绑定自动兼容。
家庭动态:老人/家属发言、三种可见范围,家庭内部内容不会播给老人。
共同照护:家庭备忘录、任务创建、认领、完成与交接记录。
用药闭环:计划、未来 7 天提醒实例、确认服用、延后、异常反馈。
风险闭环:红橙黄蓝分级、30 分钟去重、推送、认领、处理与误报记录。
报平安:家属发起、老人语音回应、状态同步。
Web 导航升级为守护、家庭、照护、陪伴、我的。
设备接口已升级为设备令牌认证,UUID 不再单独承担认证。
2026-07-18 19:59:20 +08:00

516 lines
16 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
}
enum MessageDirection {
ELDER_TO_FAMILY
FAMILY_TO_ELDER
}
enum MessageImportance {
LOW
NORMAL
HIGH
URGENT
}
enum MessageStatus {
PENDING
DELIVERED
READ
}
enum ConversationRole {
USER
ASSISTANT
TOOL
}
enum UsageEventType {
APP_OPEN
SETTINGS_OPENED
AI_SESSION_STARTED
AI_SESSION_ENDED
CAMERA_ENABLED
CAMERA_DISABLED
TOOL_CALLED
}
enum ToolCallStatus {
SUCCESS
FAILURE
}
enum FamilyRole {
ADMIN
CAREGIVER
CARING
}
enum MembershipStatus {
ACTIVE
REMOVED
}
enum InvitationStatus {
PENDING
ACCEPTED
EXPIRED
REVOKED
}
enum ContentVisibility {
ELDER_ONLY
FAMILY_ONLY
FAMILY_AND_ELDER
}
enum MemoPriority {
NORMAL
IMPORTANT
PINNED
}
enum CareTaskStatus {
OPEN
CLAIMED
COMPLETED
CANCELLED
}
enum MedicationSource {
PRESCRIPTION
PACKAGE_LABEL
FAMILY_ENTRY
}
enum MedicationDoseStatus {
SCHEDULED
REMINDING
TAKEN
SNOOZED
SKIPPED
MISSED
NEEDS_FAMILY_CONFIRMATION
}
enum CheckInStatus {
PENDING
SAFE
NEEDS_CONTACT
DECLINED
EXPIRED
}
enum AlertLevel {
RED
ORANGE
YELLOW
BLUE
}
enum AlertStatus {
OPEN
ACKNOWLEDGED
RESOLVED
DISMISSED
}
model CaregiverAccount {
id String @id @default(cuid())
username String? @unique
passwordHash String?
nickname String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
sessions CaregiverSession[]
bindings DeviceBinding[]
messages FamilyMessage[]
pushSubscriptions PushSubscription[]
createdCircles FamilyCircle[] @relation("CircleCreator")
memberships FamilyMembership[]
posts FamilyPost[]
memos FamilyMemo[]
createdTasks CareTask[] @relation("TaskCreator")
assignedTasks CareTask[] @relation("TaskAssignee")
medicationPlans MedicationPlan[]
requestedCheckIns CheckInRequest[]
claimedAlerts SafetyAlert[] @relation("AlertClaimer")
auditLogs AuditLog[]
createdInvitations FamilyInvitation[] @relation("InvitationCreator")
}
model CaregiverSession {
id String @id @default(cuid())
caregiverId String
sessionToken String @unique
userAgent String?
lastSeenAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
caregiver CaregiverAccount @relation(fields: [caregiverId], references: [id], onDelete: Cascade)
@@index([caregiverId, updatedAt(sort: Desc)])
}
model ElderDevice {
id String @id @default(cuid())
deviceUuid String @unique
displayName String?
appVersion String?
lastSeenAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
bindings DeviceBinding[]
messages FamilyMessage[]
conversationTurns ConversationTurn[]
usageEvents UsageEvent[]
toolCalls ToolCallLog[]
deviceTokenHash String?
elderProfile ElderProfile?
}
model ElderProfile {
id String @id @default(cuid())
elderDeviceId String @unique
preferredName String
timezone String @default("Asia/Shanghai")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
elderDevice ElderDevice @relation(fields: [elderDeviceId], references: [id], onDelete: Cascade)
familyCircle FamilyCircle?
consentSetting ConsentSetting?
}
model FamilyCircle {
id String @id @default(cuid())
elderProfileId String @unique
createdById String
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
elderProfile ElderProfile @relation(fields: [elderProfileId], references: [id], onDelete: Cascade)
createdBy CaregiverAccount @relation("CircleCreator", fields: [createdById], references: [id], onDelete: Restrict)
memberships FamilyMembership[]
posts FamilyPost[]
memos FamilyMemo[]
tasks CareTask[]
medicationPlans MedicationPlan[]
checkIns CheckInRequest[]
safetySignals SafetySignal[]
alerts SafetyAlert[]
memories FamilyMemory[]
auditLogs AuditLog[]
invitations FamilyInvitation[]
}
model FamilyInvitation {
id String @id @default(cuid())
familyCircleId String
createdById String
tokenHash String @unique
role FamilyRole @default(CARING)
relationLabel String?
status InvitationStatus @default(PENDING)
expiresAt DateTime
acceptedAt DateTime?
createdAt DateTime @default(now())
familyCircle FamilyCircle @relation(fields: [familyCircleId], references: [id], onDelete: Cascade)
createdBy CaregiverAccount @relation("InvitationCreator", fields: [createdById], references: [id], onDelete: Cascade)
@@index([familyCircleId, status, expiresAt])
}
model FamilyMembership {
id String @id @default(cuid())
familyCircleId String
caregiverId String
role FamilyRole @default(CARING)
relationLabel String?
status MembershipStatus @default(ACTIVE)
emergencyPush Boolean @default(true)
routinePush Boolean @default(true)
dutyOrder Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
familyCircle FamilyCircle @relation(fields: [familyCircleId], references: [id], onDelete: Cascade)
caregiver CaregiverAccount @relation(fields: [caregiverId], references: [id], onDelete: Cascade)
@@unique([familyCircleId, caregiverId])
@@index([caregiverId, status])
}
model FamilyPost {
id String @id @default(cuid())
familyCircleId String
authorId String?
content String
visibility ContentVisibility @default(FAMILY_AND_ELDER)
elderAuthored Boolean @default(false)
pinned Boolean @default(false)
deliveredAt DateTime?
readAt DateTime?
createdAt DateTime @default(now())
familyCircle FamilyCircle @relation(fields: [familyCircleId], references: [id], onDelete: Cascade)
author CaregiverAccount? @relation(fields: [authorId], references: [id], onDelete: SetNull)
@@index([familyCircleId, createdAt(sort: Desc)])
}
model FamilyMemo {
id String @id @default(cuid())
familyCircleId String
authorId String
title String
content String
priority MemoPriority @default(NORMAL)
visibility ContentVisibility @default(FAMILY_AND_ELDER)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
familyCircle FamilyCircle @relation(fields: [familyCircleId], references: [id], onDelete: Cascade)
author CaregiverAccount @relation(fields: [authorId], references: [id], onDelete: Restrict)
@@index([familyCircleId, priority, updatedAt(sort: Desc)])
}
model CareTask {
id String @id @default(cuid())
familyCircleId String
createdById String
assignedToId String?
title String
description String?
dueAt DateTime?
status CareTaskStatus @default(OPEN)
completedAt DateTime?
completionNote String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
familyCircle FamilyCircle @relation(fields: [familyCircleId], references: [id], onDelete: Cascade)
createdBy CaregiverAccount @relation("TaskCreator", fields: [createdById], references: [id], onDelete: Restrict)
assignedTo CaregiverAccount? @relation("TaskAssignee", fields: [assignedToId], references: [id], onDelete: SetNull)
@@index([familyCircleId, status, dueAt])
}
model MedicationPlan {
id String @id @default(cuid())
familyCircleId String
createdById String
name String
dosage String
instructions String?
timesJson String
source MedicationSource
startDate DateTime
endDate DateTime?
stockCount Int?
active Boolean @default(true)
pausedReason String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
familyCircle FamilyCircle @relation(fields: [familyCircleId], references: [id], onDelete: Cascade)
createdBy CaregiverAccount @relation(fields: [createdById], references: [id], onDelete: Restrict)
doses MedicationDose[]
@@index([familyCircleId, active])
}
model MedicationDose {
id String @id @default(cuid())
medicationPlanId String
scheduledFor DateTime
status MedicationDoseStatus @default(SCHEDULED)
respondedAt DateTime?
snoozedUntil DateTime?
note String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
medicationPlan MedicationPlan @relation(fields: [medicationPlanId], references: [id], onDelete: Cascade)
@@unique([medicationPlanId, scheduledFor])
@@index([scheduledFor, status])
}
model CheckInRequest {
id String @id @default(cuid())
familyCircleId String
requestedById String
prompt String?
dueAt DateTime?
status CheckInStatus @default(PENDING)
responseText String?
respondedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
familyCircle FamilyCircle @relation(fields: [familyCircleId], references: [id], onDelete: Cascade)
requestedBy CaregiverAccount @relation(fields: [requestedById], references: [id], onDelete: Restrict)
@@index([familyCircleId, status, createdAt(sort: Desc)])
}
model SafetySignal {
id String @id @default(cuid())
familyCircleId String
category String
summary String
confidence Float?
contextText String?
createdAt DateTime @default(now())
familyCircle FamilyCircle @relation(fields: [familyCircleId], references: [id], onDelete: Cascade)
alert SafetyAlert?
@@index([familyCircleId, category, createdAt(sort: Desc)])
}
model SafetyAlert {
id String @id @default(cuid())
familyCircleId String
safetySignalId String @unique
claimedById String?
level AlertLevel
status AlertStatus @default(OPEN)
title String
summary String
resolutionNote String?
claimedAt DateTime?
resolvedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
familyCircle FamilyCircle @relation(fields: [familyCircleId], references: [id], onDelete: Cascade)
safetySignal SafetySignal @relation(fields: [safetySignalId], references: [id], onDelete: Cascade)
claimedBy CaregiverAccount? @relation("AlertClaimer", fields: [claimedById], references: [id], onDelete: SetNull)
@@index([familyCircleId, status, createdAt(sort: Desc)])
}
model FamilyMemory {
id String @id @default(cuid())
familyCircleId String
title String
story String
mediaUrl String?
occurredAt DateTime?
createdAt DateTime @default(now())
familyCircle FamilyCircle @relation(fields: [familyCircleId], references: [id], onDelete: Cascade)
}
model ConsentSetting {
id String @id @default(cuid())
elderProfileId String @unique
shareTranscripts Boolean @default(true)
shareRawAudio Boolean @default(false)
updatedAt DateTime @updatedAt
elderProfile ElderProfile @relation(fields: [elderProfileId], references: [id], onDelete: Cascade)
}
model AuditLog {
id String @id @default(cuid())
familyCircleId String
actorId String?
action String
entityType String
entityId String?
detailJson String?
createdAt DateTime @default(now())
familyCircle FamilyCircle @relation(fields: [familyCircleId], references: [id], onDelete: Cascade)
actor CaregiverAccount? @relation(fields: [actorId], references: [id], onDelete: SetNull)
@@index([familyCircleId, createdAt(sort: Desc)])
}
model DeviceBinding {
id String @id @default(cuid())
caregiverId String
elderDeviceId String
alias String?
createdAt DateTime @default(now())
caregiver CaregiverAccount @relation(fields: [caregiverId], references: [id], onDelete: Cascade)
elderDevice ElderDevice @relation(fields: [elderDeviceId], references: [id], onDelete: Cascade)
@@unique([caregiverId, elderDeviceId])
@@index([elderDeviceId, createdAt])
}
model FamilyMessage {
id String @id @default(cuid())
publicId Int @unique @default(autoincrement())
elderDeviceId String
caregiverId String?
direction MessageDirection
content String
recipientRelation String?
importance MessageImportance @default(NORMAL)
status MessageStatus @default(PENDING)
source String?
createdAt DateTime @default(now())
deliveredAt DateTime?
readAt DateTime?
elderDevice ElderDevice @relation(fields: [elderDeviceId], references: [id], onDelete: Cascade)
caregiver CaregiverAccount? @relation(fields: [caregiverId], references: [id], onDelete: SetNull)
@@index([elderDeviceId, createdAt(sort: Desc)])
@@index([direction, status, createdAt(sort: Desc)])
}
model PushSubscription {
id String @id @default(cuid())
caregiverId String
endpoint String @unique
p256dh String
auth String
expirationTime DateTime?
userAgent String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
caregiver CaregiverAccount @relation(fields: [caregiverId], references: [id], onDelete: Cascade)
@@index([caregiverId, updatedAt(sort: Desc)])
}
model ConversationTurn {
id String @id @default(cuid())
elderDeviceId String
sessionId String?
role ConversationRole
content String
metaJson String?
createdAt DateTime @default(now())
elderDevice ElderDevice @relation(fields: [elderDeviceId], references: [id], onDelete: Cascade)
@@index([elderDeviceId, createdAt(sort: Desc)])
@@index([sessionId, createdAt])
}
model UsageEvent {
id String @id @default(cuid())
elderDeviceId String
eventType UsageEventType
detailJson String?
createdAt DateTime @default(now())
elderDevice ElderDevice @relation(fields: [elderDeviceId], references: [id], onDelete: Cascade)
@@index([elderDeviceId, createdAt(sort: Desc)])
@@index([eventType, createdAt(sort: Desc)])
}
model ToolCallLog {
id String @id @default(cuid())
elderDeviceId String
sessionId String?
callId String?
toolName String
argumentsJson String
outputText String?
status ToolCallStatus
createdAt DateTime @default(now())
elderDevice ElderDevice @relation(fields: [elderDeviceId], references: [id], onDelete: Cascade)
@@index([elderDeviceId, createdAt(sort: Desc)])
@@index([toolName, createdAt(sort: Desc)])
}