| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- CREATE TABLE IF NOT EXISTS voc.workspace_member (
- id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
- workspace_id bigint NOT NULL REFERENCES voc.workspace(id) ON DELETE CASCADE,
- user_external_id text NOT NULL,
- email text NOT NULL DEFAULT '',
- display_name text NOT NULL DEFAULT '',
- role text NOT NULL DEFAULT 'viewer' CHECK (role IN ('owner', 'admin', 'analyst', 'viewer')),
- status text NOT NULL DEFAULT 'active' CHECK (status IN ('invited', 'active', 'disabled')),
- invited_at timestamptz,
- last_seen_at timestamptz,
- created_at timestamptz NOT NULL DEFAULT now(),
- updated_at timestamptz NOT NULL DEFAULT now(),
- UNIQUE (workspace_id, user_external_id)
- );
- CREATE INDEX IF NOT EXISTS workspace_member_user_active_idx
- ON voc.workspace_member (user_external_id, workspace_id)
- WHERE status = 'active';
- CREATE INDEX IF NOT EXISTS workspace_member_workspace_role_idx
- ON voc.workspace_member (workspace_id, role, id);
- CREATE TABLE IF NOT EXISTS voc.analysis_run (
- id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
- public_id text NOT NULL UNIQUE,
- workspace_id bigint NOT NULL REFERENCES voc.workspace(id) ON DELETE CASCADE,
- analysis_type text NOT NULL CHECK (analysis_type IN ('voice', 'pain_point', 'feature', 'scenario', 'risk', 'report')),
- target_kind text NOT NULL DEFAULT 'workspace' CHECK (target_kind IN ('workspace', 'category', 'product')),
- target_key text NOT NULL DEFAULT '',
- status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'completed', 'partial', 'failed', 'cancelled')),
- input jsonb NOT NULL DEFAULT '{}'::jsonb,
- result jsonb,
- evidence_count integer NOT NULL DEFAULT 0 CHECK (evidence_count >= 0),
- requested_by_external_id text NOT NULL,
- error_summary text,
- requested_at timestamptz NOT NULL DEFAULT now(),
- started_at timestamptz,
- completed_at timestamptz,
- updated_at timestamptz NOT NULL DEFAULT now()
- );
- CREATE INDEX IF NOT EXISTS analysis_run_workspace_status_requested_idx
- ON voc.analysis_run (workspace_id, status, requested_at DESC, id DESC);
- CREATE INDEX IF NOT EXISTS analysis_run_workspace_target_idx
- ON voc.analysis_run (workspace_id, target_kind, target_key, requested_at DESC, id DESC);
- CREATE TABLE IF NOT EXISTS voc.action_item (
- id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
- public_id text NOT NULL UNIQUE,
- workspace_id bigint NOT NULL REFERENCES voc.workspace(id) ON DELETE CASCADE,
- source_analysis_id bigint REFERENCES voc.analysis_run(id) ON DELETE SET NULL,
- action_type text NOT NULL DEFAULT 'general' CHECK (action_type IN ('general', 'experience', 'product', 'strategy', 'data_quality')),
- title text NOT NULL,
- description text NOT NULL DEFAULT '',
- priority text NOT NULL DEFAULT 'medium' CHECK (priority IN ('critical', 'high', 'medium', 'low')),
- status text NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'planned', 'in_progress', 'blocked', 'completed', 'cancelled')),
- product_key text,
- assignee_external_id text,
- due_at timestamptz,
- created_by_external_id text NOT NULL,
- completed_at timestamptz,
- created_at timestamptz NOT NULL DEFAULT now(),
- updated_at timestamptz NOT NULL DEFAULT now()
- );
- CREATE INDEX IF NOT EXISTS action_item_workspace_status_priority_idx
- ON voc.action_item (workspace_id, status, priority, id DESC);
- CREATE INDEX IF NOT EXISTS action_item_workspace_assignee_open_idx
- ON voc.action_item (workspace_id, assignee_external_id, id DESC)
- WHERE status IN ('open', 'planned', 'in_progress', 'blocked');
- CREATE INDEX IF NOT EXISTS action_item_source_analysis_idx
- ON voc.action_item (source_analysis_id)
- WHERE source_analysis_id IS NOT NULL;
- CREATE TABLE IF NOT EXISTS voc.alert (
- id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
- public_id text NOT NULL UNIQUE,
- workspace_id bigint NOT NULL REFERENCES voc.workspace(id) ON DELETE CASCADE,
- alert_type text NOT NULL DEFAULT 'voc_risk' CHECK (alert_type IN ('voc_risk', 'sentiment_spike', 'data_quality', 'sync_failure', 'quota')),
- severity text NOT NULL DEFAULT 'medium' CHECK (severity IN ('critical', 'high', 'medium', 'low')),
- status text NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'acknowledged', 'resolved', 'dismissed')),
- product_key text,
- title text NOT NULL,
- summary text NOT NULL DEFAULT '',
- evidence jsonb NOT NULL DEFAULT '[]'::jsonb,
- detected_at timestamptz NOT NULL DEFAULT now(),
- acknowledged_by_external_id text,
- acknowledged_at timestamptz,
- resolved_at timestamptz,
- created_at timestamptz NOT NULL DEFAULT now(),
- updated_at timestamptz NOT NULL DEFAULT now()
- );
- CREATE INDEX IF NOT EXISTS alert_workspace_status_detected_idx
- ON voc.alert (workspace_id, status, detected_at DESC, id DESC);
- CREATE INDEX IF NOT EXISTS alert_workspace_product_open_idx
- ON voc.alert (workspace_id, product_key, detected_at DESC, id DESC)
- WHERE status IN ('open', 'acknowledged');
- CREATE TABLE IF NOT EXISTS voc.audit_log (
- id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
- workspace_id bigint NOT NULL REFERENCES voc.workspace(id) ON DELETE CASCADE,
- actor_external_id text NOT NULL,
- action text NOT NULL,
- entity_type text NOT NULL,
- entity_public_id text,
- metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
- created_at timestamptz NOT NULL DEFAULT now()
- );
- CREATE INDEX IF NOT EXISTS audit_log_workspace_created_idx
- ON voc.audit_log (workspace_id, created_at DESC, id DESC);
- CREATE INDEX IF NOT EXISTS audit_log_workspace_entity_idx
- ON voc.audit_log (workspace_id, entity_type, entity_public_id, id DESC)
- WHERE entity_public_id IS NOT NULL;
|