feat(referrals): 添加邀请返利和注册确认功能

This commit is contained in:
Entropy.Xu
2026-05-16 17:37:58 +08:00
parent 328ac721ce
commit 973eb1a614
56 changed files with 6246 additions and 52 deletions

View File

@@ -0,0 +1,59 @@
ALTER TABLE users
ADD COLUMN privacy_policy_accepted_version VARCHAR(64),
ADD COLUMN privacy_policy_accepted_at BIGINT;
ALTER TABLE announcements
ADD COLUMN requires_ack BOOLEAN NOT NULL DEFAULT FALSE;
CREATE TABLE IF NOT EXISTS user_invite_codes (
user_id VARCHAR(64) PRIMARY KEY,
invite_code VARCHAR(64) NOT NULL UNIQUE,
active BOOLEAN NOT NULL DEFAULT TRUE,
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
CONSTRAINT user_invite_codes_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS user_referrals (
id VARCHAR(64) PRIMARY KEY,
inviter_user_id VARCHAR(64) NOT NULL,
invitee_user_id VARCHAR(64) NOT NULL UNIQUE,
invite_code_snapshot VARCHAR(64) NOT NULL,
source_json TEXT,
first_paid_order_id VARCHAR(64),
first_paid_at BIGINT,
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
KEY idx_user_referrals_inviter (inviter_user_id, created_at),
KEY idx_user_referrals_invite_code (invite_code_snapshot),
CONSTRAINT user_referrals_inviter_user_id_fkey FOREIGN KEY (inviter_user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT user_referrals_invitee_user_id_fkey FOREIGN KEY (invitee_user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT user_referrals_first_paid_order_fkey FOREIGN KEY (first_paid_order_id) REFERENCES payment_orders(id) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS referral_rewards (
id VARCHAR(64) PRIMARY KEY,
referral_id VARCHAR(64) NOT NULL,
inviter_user_id VARCHAR(64) NOT NULL,
invitee_user_id VARCHAR(64) NOT NULL,
reward_type VARCHAR(32) NOT NULL,
trigger_point VARCHAR(64) NOT NULL,
source_order_id VARCHAR(64),
idempotency_key VARCHAR(128) NOT NULL UNIQUE,
amount_usd DOUBLE NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'pending',
wallet_transaction_id VARCHAR(64),
reversed_amount_usd DOUBLE NOT NULL DEFAULT 0,
pending_reversal_amount_usd DOUBLE NOT NULL DEFAULT 0,
failure_reason TEXT,
admin_operator_id VARCHAR(64),
admin_note TEXT,
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
KEY idx_referral_rewards_inviter_status (inviter_user_id, status, created_at),
KEY idx_referral_rewards_source_order (source_order_id),
CONSTRAINT referral_rewards_referral_id_fkey FOREIGN KEY (referral_id) REFERENCES user_referrals(id) ON DELETE CASCADE,
CONSTRAINT referral_rewards_inviter_user_id_fkey FOREIGN KEY (inviter_user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT referral_rewards_invitee_user_id_fkey FOREIGN KEY (invitee_user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT referral_rewards_source_order_fkey FOREIGN KEY (source_order_id) REFERENCES payment_orders(id) ON DELETE SET NULL
);

View File

@@ -0,0 +1,57 @@
ALTER TABLE public.users
ADD COLUMN privacy_policy_accepted_version character varying(64),
ADD COLUMN privacy_policy_accepted_at timestamp with time zone;
ALTER TABLE public.announcements
ADD COLUMN requires_ack boolean NOT NULL DEFAULT false;
CREATE TABLE IF NOT EXISTS public.user_invite_codes (
user_id character varying(64) PRIMARY KEY REFERENCES public.users(id) ON DELETE CASCADE,
invite_code character varying(64) NOT NULL UNIQUE,
active boolean NOT NULL DEFAULT true,
created_at timestamp with time zone NOT NULL DEFAULT NOW(),
updated_at timestamp with time zone NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS public.user_referrals (
id character varying(64) PRIMARY KEY,
inviter_user_id character varying(64) NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
invitee_user_id character varying(64) NOT NULL UNIQUE REFERENCES public.users(id) ON DELETE CASCADE,
invite_code_snapshot character varying(64) NOT NULL,
source_json jsonb,
first_paid_order_id character varying(64) REFERENCES public.payment_orders(id) ON DELETE SET NULL,
first_paid_at timestamp with time zone,
created_at timestamp with time zone NOT NULL DEFAULT NOW(),
updated_at timestamp with time zone NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_user_referrals_inviter
ON public.user_referrals USING btree (inviter_user_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_user_referrals_invite_code
ON public.user_referrals USING btree (invite_code_snapshot);
CREATE TABLE IF NOT EXISTS public.referral_rewards (
id character varying(64) PRIMARY KEY,
referral_id character varying(64) NOT NULL REFERENCES public.user_referrals(id) ON DELETE CASCADE,
inviter_user_id character varying(64) NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
invitee_user_id character varying(64) NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
reward_type character varying(32) NOT NULL,
trigger_point character varying(64) NOT NULL,
source_order_id character varying(64) REFERENCES public.payment_orders(id) ON DELETE SET NULL,
idempotency_key character varying(128) NOT NULL UNIQUE,
amount_usd numeric(20,8) NOT NULL,
status character varying(32) NOT NULL DEFAULT 'pending',
wallet_transaction_id character varying(64),
reversed_amount_usd numeric(20,8) NOT NULL DEFAULT 0,
pending_reversal_amount_usd numeric(20,8) NOT NULL DEFAULT 0,
failure_reason text,
admin_operator_id character varying(64),
admin_note text,
created_at timestamp with time zone NOT NULL DEFAULT NOW(),
updated_at timestamp with time zone NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_referral_rewards_inviter_status
ON public.referral_rewards USING btree (inviter_user_id, status, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_referral_rewards_source_order
ON public.referral_rewards USING btree (source_order_id);

View File

@@ -0,0 +1,63 @@
ALTER TABLE users ADD COLUMN privacy_policy_accepted_version TEXT;
ALTER TABLE users ADD COLUMN privacy_policy_accepted_at INTEGER;
ALTER TABLE announcements ADD COLUMN requires_ack INTEGER NOT NULL DEFAULT 0;
CREATE TABLE IF NOT EXISTS user_invite_codes (
user_id TEXT PRIMARY KEY,
invite_code TEXT NOT NULL UNIQUE,
active INTEGER NOT NULL DEFAULT 1,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS user_referrals (
id TEXT PRIMARY KEY,
inviter_user_id TEXT NOT NULL,
invitee_user_id TEXT NOT NULL UNIQUE,
invite_code_snapshot TEXT NOT NULL,
source_json TEXT,
first_paid_order_id TEXT,
first_paid_at INTEGER,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY(inviter_user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY(invitee_user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY(first_paid_order_id) REFERENCES payment_orders(id) ON DELETE SET NULL
);
CREATE INDEX IF NOT EXISTS idx_user_referrals_inviter
ON user_referrals (inviter_user_id, created_at);
CREATE INDEX IF NOT EXISTS idx_user_referrals_invite_code
ON user_referrals (invite_code_snapshot);
CREATE TABLE IF NOT EXISTS referral_rewards (
id TEXT PRIMARY KEY,
referral_id TEXT NOT NULL,
inviter_user_id TEXT NOT NULL,
invitee_user_id TEXT NOT NULL,
reward_type TEXT NOT NULL,
trigger_point TEXT NOT NULL,
source_order_id TEXT,
idempotency_key TEXT NOT NULL UNIQUE,
amount_usd REAL NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
wallet_transaction_id TEXT,
reversed_amount_usd REAL NOT NULL DEFAULT 0,
pending_reversal_amount_usd REAL NOT NULL DEFAULT 0,
failure_reason TEXT,
admin_operator_id TEXT,
admin_note TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY(referral_id) REFERENCES user_referrals(id) ON DELETE CASCADE,
FOREIGN KEY(inviter_user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY(invitee_user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY(source_order_id) REFERENCES payment_orders(id) ON DELETE SET NULL
);
CREATE INDEX IF NOT EXISTS idx_referral_rewards_inviter_status
ON referral_rewards (inviter_user_id, status, created_at);
CREATE INDEX IF NOT EXISTS idx_referral_rewards_source_order
ON referral_rewards (source_order_id);