Files
digital-psychology/apps/api/migrations/000001_init.up.sql
T
jackyu66gitandCursor 0f320e040b feat: P1 profile/portrait API and freeze local-dev environment
Add host-first environment contracts (Local vs CI vs Prod), deps-only
compose, and the Profile → Portrait → deep-access mock payment slice
with device identity and auto-migrate on API startup.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 16:24:57 +08:00

106 lines
3.8 KiB
SQL

-- P1 core schema (see .ai/domain/erd.md)
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TABLE IF NOT EXISTS schema_migrations (
version text PRIMARY KEY,
applied_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS users (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
status varchar(32) NOT NULL DEFAULT 'active',
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
deleted_at timestamptz NULL
);
CREATE TABLE IF NOT EXISTS device_identities (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
device_key varchar(128) NOT NULL UNIQUE,
user_id uuid NULL REFERENCES users(id),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
deleted_at timestamptz NULL
);
CREATE INDEX IF NOT EXISTS idx_device_identities_user_id ON device_identities(user_id);
CREATE TABLE IF NOT EXISTS profiles (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES users(id),
relation varchar(16) NOT NULL CHECK (relation IN ('self', 'other')),
display_name varchar(64) NOT NULL DEFAULT '',
birth_date date NOT NULL,
birth_time time NULL,
birth_place varchar(128) NULL,
gender varchar(32) NULL,
relation_type varchar(32) NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
deleted_at timestamptz NULL
);
CREATE INDEX IF NOT EXISTS idx_profiles_user_id ON profiles(user_id);
CREATE TABLE IF NOT EXISTS growth_reports (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES users(id),
profile_id uuid NOT NULL REFERENCES profiles(id),
type varchar(32) NOT NULL,
summary jsonb NOT NULL DEFAULT '{}',
detail jsonb NOT NULL DEFAULT '{}',
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
deleted_at timestamptz NULL
);
CREATE INDEX IF NOT EXISTS idx_growth_reports_user_id ON growth_reports(user_id);
CREATE INDEX IF NOT EXISTS idx_growth_reports_profile_id ON growth_reports(profile_id);
CREATE TABLE IF NOT EXISTS memberships (
user_id uuid PRIMARY KEY REFERENCES users(id),
plan varchar(32) NOT NULL DEFAULT 'month',
status varchar(32) NOT NULL DEFAULT 'expired',
expires_at timestamptz NULL,
ask_quota_left int NOT NULL DEFAULT 0,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
deleted_at timestamptz NULL
);
CREATE TABLE IF NOT EXISTS orders (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES users(id),
kind varchar(32) NOT NULL,
plan varchar(32) NULL,
report_id uuid NULL REFERENCES growth_reports(id),
amount_cents int NOT NULL DEFAULT 0,
status varchar(32) NOT NULL DEFAULT 'created',
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
deleted_at timestamptz NULL
);
CREATE INDEX IF NOT EXISTS idx_orders_user_id ON orders(user_id);
CREATE TABLE IF NOT EXISTS payments (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
order_id uuid NOT NULL REFERENCES orders(id),
channel varchar(32) NOT NULL DEFAULT 'mock',
status varchar(32) NOT NULL DEFAULT 'created',
raw jsonb NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
deleted_at timestamptz NULL
);
CREATE INDEX IF NOT EXISTS idx_payments_order_id ON payments(order_id);
CREATE TABLE IF NOT EXISTS deep_accesses (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES users(id),
report_id uuid NOT NULL REFERENCES growth_reports(id),
order_id uuid NOT NULL REFERENCES orders(id),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
deleted_at timestamptz NULL,
UNIQUE (user_id, report_id)
);
CREATE INDEX IF NOT EXISTS idx_deep_accesses_report_id ON deep_accesses(report_id);