feat: add configurable verification and redemption codes
This commit is contained in:
82
migrations/006_redemption_codes.sql
Normal file
82
migrations/006_redemption_codes.sql
Normal file
@@ -0,0 +1,82 @@
|
||||
BEGIN;
|
||||
|
||||
INSERT INTO system_config (key, value, description)
|
||||
VALUES ('auth', '{"email_verification_required": true}', '认证功能开关')
|
||||
ON CONFLICT (key) DO NOTHING;
|
||||
|
||||
ALTER TABLE usage_periods
|
||||
ADD COLUMN IF NOT EXISTS grant_used_units INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE usage_periods
|
||||
ADD CONSTRAINT usage_periods_grant_used_units_nonnegative
|
||||
CHECK (grant_used_units >= 0 AND grant_used_units <= used_units);
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS redemption_codes (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
code_hash VARCHAR(64) NOT NULL UNIQUE,
|
||||
code_hint VARCHAR(32) NOT NULL,
|
||||
benefit_kind VARCHAR(16) NOT NULL CHECK (benefit_kind IN ('plan', 'units')),
|
||||
plan_id UUID REFERENCES plans(id),
|
||||
units INTEGER,
|
||||
duration_days INTEGER NOT NULL,
|
||||
redeem_before TIMESTAMPTZ,
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
note TEXT,
|
||||
created_by UUID NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
CHECK (duration_days BETWEEN 1 AND 3650),
|
||||
CHECK (
|
||||
(benefit_kind = 'plan' AND plan_id IS NOT NULL AND units IS NULL)
|
||||
OR
|
||||
(benefit_kind = 'units' AND plan_id IS NULL AND units BETWEEN 1 AND 10000000)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_redemption_codes_created_at
|
||||
ON redemption_codes(created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_redemption_codes_active_deadline
|
||||
ON redemption_codes(is_active, redeem_before);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS redemption_records (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
code_id UUID NOT NULL UNIQUE REFERENCES redemption_codes(id),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
benefit_kind VARCHAR(16) NOT NULL CHECK (benefit_kind IN ('plan', 'units')),
|
||||
plan_id UUID REFERENCES plans(id),
|
||||
units INTEGER,
|
||||
benefit_starts_at TIMESTAMPTZ NOT NULL,
|
||||
benefit_expires_at TIMESTAMPTZ NOT NULL,
|
||||
redeemed_ip INET,
|
||||
redeemed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
CHECK (benefit_expires_at > benefit_starts_at)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_redemption_records_user_time
|
||||
ON redemption_records(user_id, redeemed_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unit_grants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
redemption_record_id UUID NOT NULL UNIQUE REFERENCES redemption_records(id) ON DELETE CASCADE,
|
||||
total_units INTEGER NOT NULL CHECK (total_units > 0),
|
||||
remaining_units INTEGER NOT NULL CHECK (remaining_units >= 0 AND remaining_units <= total_units),
|
||||
starts_at TIMESTAMPTZ NOT NULL,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
CHECK (expires_at > starts_at)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_unit_grants_available
|
||||
ON unit_grants(user_id, expires_at, created_at)
|
||||
WHERE remaining_units > 0;
|
||||
|
||||
COMMIT;
|
||||
Reference in New Issue
Block a user