132 lines
6.0 KiB
SQL
132 lines
6.0 KiB
SQL
-- Default-off, organization-wide task-summary subscriptions for team leads.
|
|
-- This is a read-only projection: it never grants task mutation or ERP
|
|
-- execution authority and it does not reuse employee AgentBus reply rows.
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS user_channels_organization_id_id_notification_idx
|
|
ON user_channels (organization_id, id);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS tasks_organization_id_id_notification_idx
|
|
ON tasks (organization_id, id);
|
|
|
|
CREATE INDEX IF NOT EXISTS outbox_events_leader_summary_projection_idx
|
|
ON outbox_events (organization_id, aggregate_id, created_at DESC)
|
|
WHERE topic = 'task.updated' AND aggregate_type = 'task';
|
|
|
|
CREATE INDEX IF NOT EXISTS tasks_leader_summary_projection_idx
|
|
ON tasks (organization_id, source, status, assigned_user_id, id)
|
|
WHERE assigned_user_id IS NOT NULL;
|
|
|
|
CREATE TABLE IF NOT EXISTS leader_task_summary_subscriptions (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
leader_user_id uuid NOT NULL,
|
|
channel_id uuid NOT NULL,
|
|
scope text NOT NULL DEFAULT 'organization',
|
|
include_manual boolean NOT NULL DEFAULT true,
|
|
include_agentbus boolean NOT NULL DEFAULT true,
|
|
enabled boolean NOT NULL DEFAULT false,
|
|
starts_at timestamptz NOT NULL DEFAULT now(),
|
|
revision integer NOT NULL DEFAULT 0,
|
|
recipient_address_ciphertext text NOT NULL,
|
|
recipient_address_fingerprint text NOT NULL,
|
|
conversation_id_ciphertext text NOT NULL,
|
|
conversation_id_fingerprint text NOT NULL,
|
|
target_verified_at timestamptz,
|
|
target_verified_by uuid REFERENCES users(id) ON DELETE SET NULL,
|
|
created_by uuid REFERENCES users(id) ON DELETE SET NULL,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT leader_task_summary_subscriptions_scope_check
|
|
CHECK (scope = 'organization'),
|
|
CONSTRAINT leader_task_summary_subscriptions_sources_check
|
|
CHECK (include_manual OR include_agentbus),
|
|
CONSTRAINT leader_task_summary_subscriptions_revision_check
|
|
CHECK (revision >= 0),
|
|
CONSTRAINT leader_task_summary_subscriptions_recipient_fingerprint_check
|
|
CHECK (recipient_address_fingerprint ~ '^[a-f0-9]{64}$'),
|
|
CONSTRAINT leader_task_summary_subscriptions_conversation_fingerprint_check
|
|
CHECK (conversation_id_fingerprint ~ '^[a-f0-9]{64}$'),
|
|
CONSTRAINT leader_task_summary_subscriptions_leader_scope_fkey
|
|
FOREIGN KEY (organization_id, leader_user_id)
|
|
REFERENCES users (organization_id, id)
|
|
ON DELETE CASCADE,
|
|
CONSTRAINT leader_task_summary_subscriptions_channel_scope_fkey
|
|
FOREIGN KEY (organization_id, channel_id)
|
|
REFERENCES user_channels (organization_id, id)
|
|
ON DELETE CASCADE,
|
|
UNIQUE (organization_id, leader_user_id)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS leader_task_summary_subscriptions_active_idx
|
|
ON leader_task_summary_subscriptions
|
|
(organization_id, enabled, starts_at, channel_id)
|
|
WHERE enabled = true;
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS leader_task_summary_subscriptions_organization_id_id_idx
|
|
ON leader_task_summary_subscriptions (organization_id, id);
|
|
|
|
CREATE TABLE IF NOT EXISTS leader_task_summary_deliveries (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
subscription_id uuid NOT NULL,
|
|
subscription_revision integer NOT NULL,
|
|
task_id uuid NOT NULL,
|
|
leader_user_id uuid NOT NULL,
|
|
channel_id uuid NOT NULL,
|
|
source_outbox_event_id bigint NOT NULL,
|
|
milestone text NOT NULL,
|
|
recipient_address_ciphertext text NOT NULL,
|
|
recipient_address_fingerprint text NOT NULL,
|
|
conversation_id_ciphertext text NOT NULL,
|
|
conversation_id_fingerprint text NOT NULL,
|
|
payload_ciphertext text NOT NULL,
|
|
payload_fingerprint text NOT NULL,
|
|
delivery_status text NOT NULL DEFAULT 'pending',
|
|
attempt_count integer NOT NULL DEFAULT 0,
|
|
next_attempt_at timestamptz NOT NULL DEFAULT now(),
|
|
last_error text,
|
|
delivered_at timestamptz,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT leader_task_summary_deliveries_milestone_check
|
|
CHECK (milestone IN ('final', 'needs_review', 'resolved')),
|
|
CONSTRAINT leader_task_summary_deliveries_status_check
|
|
CHECK (delivery_status IN ('pending', 'sending', 'delivered', 'failed', 'cancelled')),
|
|
CONSTRAINT leader_task_summary_deliveries_attempt_check
|
|
CHECK (attempt_count >= 0),
|
|
CONSTRAINT leader_task_summary_deliveries_revision_check
|
|
CHECK (subscription_revision >= 0),
|
|
CONSTRAINT leader_task_summary_deliveries_payload_fingerprint_check
|
|
CHECK (payload_fingerprint ~ '^[a-f0-9]{64}$'),
|
|
CONSTRAINT leader_task_summary_deliveries_recipient_fingerprint_check
|
|
CHECK (recipient_address_fingerprint ~ '^[a-f0-9]{64}$'),
|
|
CONSTRAINT leader_task_summary_deliveries_conversation_fingerprint_check
|
|
CHECK (conversation_id_fingerprint ~ '^[a-f0-9]{64}$'),
|
|
CONSTRAINT leader_task_summary_deliveries_subscription_scope_fkey
|
|
FOREIGN KEY (organization_id, subscription_id)
|
|
REFERENCES leader_task_summary_subscriptions (organization_id, id)
|
|
ON DELETE CASCADE,
|
|
CONSTRAINT leader_task_summary_deliveries_task_scope_fkey
|
|
FOREIGN KEY (organization_id, task_id)
|
|
REFERENCES tasks (organization_id, id)
|
|
ON DELETE CASCADE,
|
|
CONSTRAINT leader_task_summary_deliveries_leader_scope_fkey
|
|
FOREIGN KEY (organization_id, leader_user_id)
|
|
REFERENCES users (organization_id, id)
|
|
ON DELETE CASCADE,
|
|
CONSTRAINT leader_task_summary_deliveries_channel_scope_fkey
|
|
FOREIGN KEY (organization_id, channel_id)
|
|
REFERENCES user_channels (organization_id, id)
|
|
ON DELETE CASCADE,
|
|
UNIQUE (subscription_id, subscription_revision, task_id, milestone)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS leader_task_summary_deliveries_pending_idx
|
|
ON leader_task_summary_deliveries
|
|
(channel_id, delivery_status, next_attempt_at, created_at, id)
|
|
WHERE delivery_status IN ('pending', 'sending', 'failed');
|
|
|
|
CREATE INDEX IF NOT EXISTS leader_task_summary_deliveries_task_idx
|
|
ON leader_task_summary_deliveries
|
|
(organization_id, task_id, leader_user_id, created_at DESC);
|