feat(service): P4.1 support tables (queue/team_member/ticket/ticket_thread_link/state_history/callback) + events
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -95,6 +95,32 @@ enum IiosInboxState {
|
||||
STALE
|
||||
}
|
||||
|
||||
enum IiosTicketState {
|
||||
NEW
|
||||
OPEN
|
||||
PENDING_CUSTOMER
|
||||
PENDING_INTERNAL
|
||||
RESOLVED
|
||||
CLOSED
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
enum IiosTicketPriority {
|
||||
P0
|
||||
P1
|
||||
P2
|
||||
P3
|
||||
P4
|
||||
}
|
||||
|
||||
enum IiosCallbackStatus {
|
||||
PENDING
|
||||
SCHEDULED
|
||||
COMPLETED
|
||||
MISSED
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
// ─── Kernel tables ────────────────────────────────────────────────
|
||||
|
||||
/// Frozen six-vector scope. orgId + appId are REQUIRED (no global-by-null).
|
||||
@@ -114,6 +140,9 @@ model IiosScope {
|
||||
threads IiosThread[]
|
||||
interactions IiosInteraction[]
|
||||
inboxItems IiosInboxItem[]
|
||||
supportQueues IiosSupportQueue[]
|
||||
tickets IiosTicket[]
|
||||
callbacks IiosCallbackRequest[]
|
||||
|
||||
@@index([orgId, appId, tenantId])
|
||||
}
|
||||
@@ -157,6 +186,10 @@ model IiosActorRef {
|
||||
receipts IiosMessageReceipt[]
|
||||
unreadCounters IiosUnreadCounter[]
|
||||
inboxItemsOwned IiosInboxItem[]
|
||||
teamMemberships IiosSupportTeamMember[]
|
||||
ticketsRequested IiosTicket[] @relation("TicketRequester")
|
||||
ticketsAssigned IiosTicket[] @relation("TicketAssignee")
|
||||
callbacksRequested IiosCallbackRequest[]
|
||||
}
|
||||
|
||||
/// A configured channel surface (PORTAL in P1).
|
||||
@@ -200,6 +233,7 @@ model IiosThread {
|
||||
interactions IiosInteraction[]
|
||||
unreadCounters IiosUnreadCounter[]
|
||||
inboxItems IiosInboxItem[]
|
||||
ticketLinks IiosTicketThreadLink[]
|
||||
|
||||
@@index([scopeId, status])
|
||||
}
|
||||
@@ -242,9 +276,10 @@ model IiosInteraction {
|
||||
traceId String?
|
||||
metadata Json?
|
||||
|
||||
parts IiosMessagePart[]
|
||||
receipts IiosMessageReceipt[]
|
||||
inboxItems IiosInboxItem[]
|
||||
parts IiosMessagePart[]
|
||||
receipts IiosMessageReceipt[]
|
||||
inboxItems IiosInboxItem[]
|
||||
ticketsCreatedFrom IiosTicket[]
|
||||
|
||||
@@unique([scopeId, idempotencyKey])
|
||||
@@index([threadId, occurredAt])
|
||||
@@ -369,3 +404,113 @@ model IiosInboxItemStateHistory {
|
||||
|
||||
@@index([inboxItemId])
|
||||
}
|
||||
|
||||
// ─── P4: Support (specialization over message + inbox) ────────────
|
||||
|
||||
/// A support queue; agents (team members) are assigned tickets from it.
|
||||
model IiosSupportQueue {
|
||||
id String @id @default(cuid())
|
||||
scopeId String
|
||||
scope IiosScope @relation(fields: [scopeId], references: [id], onDelete: Cascade)
|
||||
name String
|
||||
supportTier String @default("REGULAR")
|
||||
assignmentPolicy Json?
|
||||
enabled Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
members IiosSupportTeamMember[]
|
||||
tickets IiosTicket[]
|
||||
|
||||
@@index([scopeId])
|
||||
}
|
||||
|
||||
/// An agent's membership + capacity in a queue. Agent = an ActorRef that is a member.
|
||||
model IiosSupportTeamMember {
|
||||
queueId String
|
||||
queue IiosSupportQueue @relation(fields: [queueId], references: [id], onDelete: Cascade)
|
||||
actorId String
|
||||
actor IiosActorRef @relation(fields: [actorId], references: [id])
|
||||
role String @default("AGENT")
|
||||
maxActive Int @default(3)
|
||||
activeCount Int @default(0)
|
||||
availabilityState String @default("OFFLINE")
|
||||
|
||||
@@id([queueId, actorId])
|
||||
@@index([actorId])
|
||||
}
|
||||
|
||||
/// Support work object. Linked many-to-many to threads; the thread stays generic.
|
||||
model IiosTicket {
|
||||
id String @id @default(cuid())
|
||||
scopeId String
|
||||
scope IiosScope @relation(fields: [scopeId], references: [id], onDelete: Cascade)
|
||||
queueId String?
|
||||
queue IiosSupportQueue? @relation(fields: [queueId], references: [id])
|
||||
requesterActorId String?
|
||||
requesterActor IiosActorRef? @relation("TicketRequester", fields: [requesterActorId], references: [id])
|
||||
assignedActorId String?
|
||||
assignedActor IiosActorRef? @relation("TicketAssignee", fields: [assignedActorId], references: [id])
|
||||
subject String
|
||||
state IiosTicketState @default(NEW)
|
||||
priority IiosTicketPriority @default(P3)
|
||||
issueClass String?
|
||||
createdFromInteractionId String?
|
||||
createdFromInteraction IiosInteraction? @relation(fields: [createdFromInteractionId], references: [id])
|
||||
traceId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
metadata Json?
|
||||
|
||||
threadLinks IiosTicketThreadLink[]
|
||||
stateHistory IiosTicketStateHistory[]
|
||||
callbacks IiosCallbackRequest[]
|
||||
|
||||
@@index([scopeId, state])
|
||||
@@index([assignedActorId, state])
|
||||
}
|
||||
|
||||
/// M:N link between tickets and kernel threads (one thread ↔ many tickets, and vice-versa).
|
||||
model IiosTicketThreadLink {
|
||||
ticketId String
|
||||
ticket IiosTicket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
|
||||
threadId String
|
||||
thread IiosThread @relation(fields: [threadId], references: [id], onDelete: Cascade)
|
||||
relationKind String @default("PRIMARY")
|
||||
linkedAt DateTime @default(now())
|
||||
|
||||
@@id([ticketId, threadId])
|
||||
@@index([threadId])
|
||||
}
|
||||
|
||||
/// Ticket state-transition audit.
|
||||
model IiosTicketStateHistory {
|
||||
id String @id @default(cuid())
|
||||
ticketId String
|
||||
ticket IiosTicket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
|
||||
fromState IiosTicketState?
|
||||
toState IiosTicketState
|
||||
actorId String?
|
||||
reasonCode String?
|
||||
at DateTime @default(now())
|
||||
|
||||
@@index([ticketId])
|
||||
}
|
||||
|
||||
/// Callback request placeholder (the Meeting module is P8; meetingRef is a stub).
|
||||
model IiosCallbackRequest {
|
||||
id String @id @default(cuid())
|
||||
scopeId String
|
||||
scope IiosScope @relation(fields: [scopeId], references: [id], onDelete: Cascade)
|
||||
ticketId String?
|
||||
ticket IiosTicket? @relation(fields: [ticketId], references: [id])
|
||||
requesterActorId String
|
||||
requesterActor IiosActorRef @relation(fields: [requesterActorId], references: [id])
|
||||
preferChannel String
|
||||
preferredTime DateTime?
|
||||
notes String?
|
||||
status IiosCallbackStatus @default(PENDING)
|
||||
meetingRef String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([scopeId, status])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user