feat(service): P2.1 messaging tables — IiosMessageReceipt + IiosUnreadCounter
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
|||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "IiosReceiptKind" AS ENUM ('DELIVERED', 'READ', 'ACKED', 'FAILED', 'SUPPRESSED', 'RATE_LIMITED');
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "IiosMessageReceipt" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"interactionId" TEXT NOT NULL,
|
||||||
|
"actorId" TEXT,
|
||||||
|
"receiptKind" "IiosReceiptKind" NOT NULL,
|
||||||
|
"occurredAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"providerReceiptRef" TEXT,
|
||||||
|
"metadata" JSONB,
|
||||||
|
|
||||||
|
CONSTRAINT "IiosMessageReceipt_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "IiosUnreadCounter" (
|
||||||
|
"threadId" TEXT NOT NULL,
|
||||||
|
"actorId" TEXT NOT NULL,
|
||||||
|
"unreadCount" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"lastReadInteractionId" TEXT,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "IiosUnreadCounter_pkey" PRIMARY KEY ("threadId","actorId")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "IiosMessageReceipt_interactionId_idx" ON "IiosMessageReceipt"("interactionId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "IiosMessageReceipt_interactionId_actorId_receiptKind_key" ON "IiosMessageReceipt"("interactionId", "actorId", "receiptKind");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "IiosMessageReceipt" ADD CONSTRAINT "IiosMessageReceipt_interactionId_fkey" FOREIGN KEY ("interactionId") REFERENCES "IiosInteraction"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "IiosMessageReceipt" ADD CONSTRAINT "IiosMessageReceipt_actorId_fkey" FOREIGN KEY ("actorId") REFERENCES "IiosActorRef"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "IiosUnreadCounter" ADD CONSTRAINT "IiosUnreadCounter_threadId_fkey" FOREIGN KEY ("threadId") REFERENCES "IiosThread"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "IiosUnreadCounter" ADD CONSTRAINT "IiosUnreadCounter_actorId_fkey" FOREIGN KEY ("actorId") REFERENCES "IiosActorRef"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
@@ -66,6 +66,15 @@ enum IiosDeliveryState {
|
|||||||
REDACTED
|
REDACTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum IiosReceiptKind {
|
||||||
|
DELIVERED
|
||||||
|
READ
|
||||||
|
ACKED
|
||||||
|
FAILED
|
||||||
|
SUPPRESSED
|
||||||
|
RATE_LIMITED
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Kernel tables ────────────────────────────────────────────────
|
// ─── Kernel tables ────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Frozen six-vector scope. orgId + appId are REQUIRED (no global-by-null).
|
/// Frozen six-vector scope. orgId + appId are REQUIRED (no global-by-null).
|
||||||
@@ -124,6 +133,8 @@ model IiosActorRef {
|
|||||||
threadsCreated IiosThread[] @relation("ThreadCreatedBy")
|
threadsCreated IiosThread[] @relation("ThreadCreatedBy")
|
||||||
participations IiosThreadParticipant[]
|
participations IiosThreadParticipant[]
|
||||||
interactions IiosInteraction[]
|
interactions IiosInteraction[]
|
||||||
|
receipts IiosMessageReceipt[]
|
||||||
|
unreadCounters IiosUnreadCounter[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A configured channel surface (PORTAL in P1).
|
/// A configured channel surface (PORTAL in P1).
|
||||||
@@ -165,6 +176,7 @@ model IiosThread {
|
|||||||
|
|
||||||
participants IiosThreadParticipant[]
|
participants IiosThreadParticipant[]
|
||||||
interactions IiosInteraction[]
|
interactions IiosInteraction[]
|
||||||
|
unreadCounters IiosUnreadCounter[]
|
||||||
|
|
||||||
@@index([scopeId, status])
|
@@index([scopeId, status])
|
||||||
}
|
}
|
||||||
@@ -208,6 +220,7 @@ model IiosInteraction {
|
|||||||
metadata Json?
|
metadata Json?
|
||||||
|
|
||||||
parts IiosMessagePart[]
|
parts IiosMessagePart[]
|
||||||
|
receipts IiosMessageReceipt[]
|
||||||
|
|
||||||
@@unique([scopeId, idempotencyKey])
|
@@unique([scopeId, idempotencyKey])
|
||||||
@@index([threadId, occurredAt])
|
@@index([threadId, occurredAt])
|
||||||
@@ -255,3 +268,34 @@ model IiosProcessedEvent {
|
|||||||
|
|
||||||
@@id([consumerName, eventId])
|
@@id([consumerName, eventId])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── P2: messaging — receipts & unread ────────────────────────────
|
||||||
|
|
||||||
|
/// Delivery/read receipt for a message interaction, per actor.
|
||||||
|
model IiosMessageReceipt {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
interactionId String
|
||||||
|
interaction IiosInteraction @relation(fields: [interactionId], references: [id], onDelete: Cascade)
|
||||||
|
actorId String?
|
||||||
|
actor IiosActorRef? @relation(fields: [actorId], references: [id])
|
||||||
|
receiptKind IiosReceiptKind
|
||||||
|
occurredAt DateTime @default(now())
|
||||||
|
providerReceiptRef String?
|
||||||
|
metadata Json?
|
||||||
|
|
||||||
|
@@unique([interactionId, actorId, receiptKind])
|
||||||
|
@@index([interactionId])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Per-(thread, actor) unread count + last-read marker.
|
||||||
|
model IiosUnreadCounter {
|
||||||
|
threadId String
|
||||||
|
thread IiosThread @relation(fields: [threadId], references: [id], onDelete: Cascade)
|
||||||
|
actorId String
|
||||||
|
actor IiosActorRef @relation(fields: [actorId], references: [id])
|
||||||
|
unreadCount Int @default(0)
|
||||||
|
lastReadInteractionId String?
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@id([threadId, actorId])
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user