Files
zarinpal-analytics-backend/migrations/000005_growth_coach.sql

193 lines
8.3 KiB
SQL

ALTER TABLE ai_insight_snapshots ADD COLUMN IF NOT EXISTS deterministic_copy jsonb;
-- statement-break
ALTER TABLE ai_insight_snapshots ADD COLUMN IF NOT EXISTS provider_request_id text;
-- statement-break
UPDATE ai_insight_snapshots SET deterministic_copy=ai_response WHERE deterministic_copy IS NULL;
-- statement-break
CREATE TABLE IF NOT EXISTS growth_coach_profiles (
id text PRIMARY KEY,
merchant_id bigint NOT NULL REFERENCES merchants(id),
terminal_id bigint NOT NULL REFERENCES terminals(id),
terminal_source_key text NOT NULL,
version integer NOT NULL CHECK (version > 0),
is_current boolean NOT NULL DEFAULT true,
business_description text NOT NULL CHECK (char_length(business_description) BETWEEN 20 AND 2000),
niches jsonb NOT NULL,
sales_channels jsonb NOT NULL,
primary_goal text NOT NULL CHECK (char_length(primary_goal) BETWEEN 3 AND 500),
constraints_text text NOT NULL DEFAULT '' CHECK (char_length(constraints_text) <= 1000),
difficulty smallint NOT NULL DEFAULT 2 CHECK (difficulty BETWEEN 1 AND 5),
actor_user_id bigint NOT NULL REFERENCES merchant_users(id),
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (merchant_id,terminal_id,version)
);
-- statement-break
CREATE UNIQUE INDEX IF NOT EXISTS growth_coach_profile_current_idx ON growth_coach_profiles(merchant_id,terminal_id) WHERE is_current;
-- statement-break
CREATE TABLE IF NOT EXISTS growth_coach_plans (
id text PRIMARY KEY,
merchant_id bigint NOT NULL REFERENCES merchants(id),
terminal_id bigint NOT NULL REFERENCES terminals(id),
terminal_source_key text NOT NULL,
profile_id text NOT NULL REFERENCES growth_coach_profiles(id),
parent_id text REFERENCES growth_coach_plans(id),
cadence text NOT NULL CHECK (cadence IN ('DAILY','WEEKLY','MONTHLY')),
status text NOT NULL CHECK (status IN ('GENERATING','READY','PARTIAL','FAILED','CLOSED')),
period_start date NOT NULL,
period_end date NOT NULL,
daily_brief text,
difficulty smallint NOT NULL CHECK (difficulty BETWEEN 1 AND 5),
prompt_version text NOT NULL,
schema_version text NOT NULL,
model text NOT NULL,
provider_request_id text,
input_tokens bigint NOT NULL DEFAULT 0,
output_tokens bigint NOT NULL DEFAULT 0,
latency_ms bigint NOT NULL DEFAULT 0,
provider_requests bigint NOT NULL DEFAULT 0,
estimated_cost text,
failure_code text,
created_at timestamptz NOT NULL DEFAULT now(),
completed_at timestamptz,
CHECK (period_end >= period_start)
);
-- statement-break
CREATE INDEX IF NOT EXISTS growth_coach_plans_scope_idx ON growth_coach_plans(merchant_id,terminal_id,cadence,period_start DESC);
-- statement-break
CREATE TABLE IF NOT EXISTS growth_coach_recommendations (
id text PRIMARY KEY,
plan_id text NOT NULL REFERENCES growth_coach_plans(id) ON DELETE CASCADE,
merchant_id bigint NOT NULL REFERENCES merchants(id),
terminal_id bigint NOT NULL REFERENCES terminals(id),
rank smallint NOT NULL CHECK (rank BETWEEN 1 AND 5),
status text NOT NULL CHECK (status IN ('OFFERED','ACCEPTED','REJECTED','EXPIRED')),
action_type text NOT NULL CHECK (action_type IN ('MANUAL','METRIC')),
title text NOT NULL,
rationale text NOT NULL,
expected_impact text NOT NULL,
effort text NOT NULL CHECK (effort IN ('LOW','MEDIUM','HIGH')),
evidence_ids jsonb NOT NULL,
metric_id text,
metric_direction text CHECK (metric_direction IN ('INCREASE','DECREASE')),
action_level text CHECK (action_level IN ('SMALL','MEDIUM','STRETCH')),
baseline numeric(30,6),
target numeric(30,6),
sample_size bigint,
deadline date NOT NULL,
decision_reason text,
decision_note text,
decided_by bigint REFERENCES merchant_users(id),
decided_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE(plan_id,rank)
);
-- statement-break
CREATE TABLE IF NOT EXISTS golden_steps (
id text PRIMARY KEY,
recommendation_id text NOT NULL UNIQUE REFERENCES growth_coach_recommendations(id),
merchant_id bigint NOT NULL REFERENCES merchants(id),
terminal_id bigint NOT NULL REFERENCES terminals(id),
terminal_source_key text NOT NULL,
action_type text NOT NULL CHECK (action_type IN ('MANUAL','METRIC')),
title text NOT NULL,
status text NOT NULL CHECK (status IN ('NOT_STARTED','IN_PROGRESS','COMPLETED','ABANDONED','MISSED')),
metric_id text,
metric_direction text,
baseline numeric(30,6),
target numeric(30,6),
current_value numeric(30,6),
sample_size bigint,
deadline date NOT NULL,
started_at timestamptz,
completed_at timestamptz,
actor_user_id bigint NOT NULL REFERENCES merchant_users(id),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
-- statement-break
CREATE INDEX IF NOT EXISTS golden_steps_scope_idx ON golden_steps(merchant_id,terminal_id,status,deadline);
-- statement-break
CREATE TABLE IF NOT EXISTS growth_coach_jobs (
id text PRIMARY KEY,
merchant_id bigint NOT NULL REFERENCES merchants(id),
terminal_id bigint NOT NULL REFERENCES terminals(id),
terminal_source_key text NOT NULL,
profile_id text NOT NULL REFERENCES growth_coach_profiles(id),
cadence text NOT NULL CHECK (cadence IN ('DAILY','WEEKLY','MONTHLY')),
trigger_reason text NOT NULL,
period_start date NOT NULL,
period_end date NOT NULL,
fingerprint text NOT NULL UNIQUE,
idempotency_key text,
request_hash text,
status text NOT NULL CHECK (status IN ('QUEUED','RUNNING','COMPLETED','FAILED')),
attempts integer NOT NULL DEFAULT 0,
scheduled_at timestamptz NOT NULL DEFAULT now(),
lease_token text,
lease_expires_at timestamptz,
failure_code text,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
completed_at timestamptz,
UNIQUE(merchant_id,idempotency_key)
);
-- statement-break
CREATE INDEX IF NOT EXISTS growth_coach_jobs_pending_idx ON growth_coach_jobs(scheduled_at,created_at) WHERE status IN ('QUEUED','RUNNING');
-- statement-break
CREATE TABLE IF NOT EXISTS growth_coach_events (
id bigserial PRIMARY KEY,
merchant_id bigint NOT NULL REFERENCES merchants(id),
terminal_id bigint NOT NULL REFERENCES terminals(id),
actor_user_id bigint REFERENCES merchant_users(id),
entity_type text NOT NULL,
entity_id text NOT NULL,
event_type text NOT NULL,
payload jsonb NOT NULL DEFAULT '{}'::jsonb,
occurred_at timestamptz NOT NULL DEFAULT now()
);
-- statement-break
CREATE TABLE IF NOT EXISTS growth_coach_progress_snapshots (
id bigserial PRIMARY KEY,
action_id text NOT NULL REFERENCES golden_steps(id) ON DELETE CASCADE,
watermark text NOT NULL,
value numeric(30,6) NOT NULL,
sample_size bigint NOT NULL,
evaluated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE(action_id,watermark)
);
-- statement-break
CREATE TABLE IF NOT EXISTS growth_coach_difficulty_proposals (
id text PRIMARY KEY,
merchant_id bigint NOT NULL REFERENCES merchants(id),
terminal_id bigint NOT NULL REFERENCES terminals(id),
from_level smallint NOT NULL CHECK (from_level BETWEEN 1 AND 5),
to_level smallint NOT NULL CHECK (to_level BETWEEN 1 AND 5),
reason text NOT NULL,
status text NOT NULL CHECK (status IN ('OFFERED','ACCEPTED','REJECTED','EXPIRED')),
decided_by bigint REFERENCES merchant_users(id),
decided_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now()
);
-- statement-break
CREATE TABLE IF NOT EXISTS growth_coach_outcome_reviews (
id text PRIMARY KEY,
plan_id text NOT NULL UNIQUE REFERENCES growth_coach_plans(id),
merchant_id bigint NOT NULL REFERENCES merchants(id),
terminal_id bigint NOT NULL REFERENCES terminals(id),
summary text NOT NULL,
completed_on_time integer NOT NULL DEFAULT 0,
missed integer NOT NULL DEFAULT 0,
created_at timestamptz NOT NULL DEFAULT now()
);
-- statement-break
CREATE TABLE IF NOT EXISTS growth_coach_idempotency (
merchant_id bigint NOT NULL REFERENCES merchants(id),
idempotency_key text NOT NULL,
request_hash text NOT NULL,
response jsonb NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY(merchant_id,idempotency_key)
);
-- statement-break
INSERT INTO schema_migrations(version) VALUES (5) ON CONFLICT DO NOTHING;