feat: Datenmodell Sprachnotizen (T7 L2 Task 4)
VoiceCapture und VoiceCaptureAudio nach Spec §3 mit CHECKs (audioBytes 1..2 MiB, durationSec 1..240, CONFIRMED genau mit inspectionId, audioMime nur bei AUDIO), Recht voice-captures:use für ADMIN, BEEKEEPER und STAFF, Sperrreihenfolge mit VoiceCapture am Ende, Ausschluss aus dem Backup mit Begründung, Wächter gegen Suche und Export. Refs #27 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
257286fbef
commit
af9df0afe4
10 files changed
+412
No files matched your search
@@ -104,6 +104,7 @@ model User {
|
||||
refreshTokens RefreshToken[]
|
||||
|
||||
inspections Inspection[]
|
||||
voiceCaptures VoiceCapture[]
|
||||
hiveChanges HiveChange[]
|
||||
varroaMeasurements VarroaMeasurement[]
|
||||
swarmControlChecks SwarmControlCheck[]
|
||||
@@ -333,6 +334,7 @@ model Colony {
|
||||
queenEvaluations QueenEvaluation[]
|
||||
suggestions Suggestion[]
|
||||
hiveSetup HiveSetup?
|
||||
voiceCaptures VoiceCapture[]
|
||||
|
||||
@@index([apiaryId])
|
||||
@@index([status])
|
||||
@@ -2285,3 +2287,73 @@ enum HiveChangeSource {
|
||||
INSPECTION
|
||||
AI_SUGGESTION
|
||||
}
|
||||
|
||||
enum VoiceCaptureSource {
|
||||
AUDIO
|
||||
TEXT
|
||||
}
|
||||
|
||||
enum VoiceCaptureStatus {
|
||||
QUEUED // audio uploaded, waits for Whisper
|
||||
TRANSCRIBING // claimed by the CronJob
|
||||
TRANSCRIBED // text present, waits for Claude (also source TEXT)
|
||||
EXTRACTING // claimed by the worker
|
||||
READY // suggestions present, waits for the user
|
||||
CONFIRMED // inspection saved, content deleted
|
||||
DISCARDED // discarded by the user, content deleted
|
||||
FAILED // finally failed, code in lastErrorCode
|
||||
EXPIRED // deadline passed, content deleted
|
||||
}
|
||||
|
||||
// A voice note. Audio and text are personal data: content lives here only
|
||||
// encrypted and only until it is taken over, at most 7 days. The row itself
|
||||
// holds no content and stays 90 days for the error and cost overview.
|
||||
model VoiceCapture {
|
||||
id String @id @default(cuid())
|
||||
ownerUserId String
|
||||
owner User @relation(fields: [ownerUserId], references: [id])
|
||||
colonyId String
|
||||
colony Colony @relation(fields: [colonyId], references: [id])
|
||||
inspectionDate DateTime @db.Date // date of the inspection per the form
|
||||
source VoiceCaptureSource
|
||||
status VoiceCaptureStatus @default(QUEUED)
|
||||
|
||||
audioMime String? // audio/webm, audio/ogg, audio/mp4
|
||||
audioBytes Int?
|
||||
durationSec Int? // reported by the device; Whisper measures again
|
||||
|
||||
transcriptEnc String? // key-ring ciphertext
|
||||
suggestionEnc String? // key-ring ciphertext of the checked suggestion
|
||||
|
||||
attempts Int @default(0)
|
||||
availableAt DateTime @default(now())
|
||||
claimedAt DateTime?
|
||||
claimToken String? // guards against completion by a process that lost the claim
|
||||
lastErrorCode String? // fixed code, never content
|
||||
|
||||
model String? // e.g. "claude-sonnet-5-5"
|
||||
inputTokens Int?
|
||||
outputTokens Int?
|
||||
|
||||
inspectionId String? // set when CONFIRMED
|
||||
createdAt DateTime @default(now())
|
||||
readyAt DateTime?
|
||||
contentExpiresAt DateTime // createdAt + 7 days
|
||||
contentDeletedAt DateTime? // when audio/text/suggestion were deleted
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
audio VoiceCaptureAudio?
|
||||
|
||||
@@index([status, availableAt])
|
||||
@@index([ownerUserId, status])
|
||||
@@index([contentExpiresAt])
|
||||
}
|
||||
|
||||
// The audio lives in its own table: deleting is a DELETE of the row (not
|
||||
// nulling a big column), and the row is never loaded into lists.
|
||||
model VoiceCaptureAudio {
|
||||
captureId String @id
|
||||
capture VoiceCapture @relation(fields: [captureId], references: [id], onDelete: Cascade)
|
||||
dataEnc Bytes // key-ring ciphertext of the audio, at most 2 MB plaintext
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
Reference in new issue
Block a user