Files
Aether/crates/aether-data/schema/generated/sqlite/baseline/005_wallet_billing.sql
2026-05-19 10:18:25 +08:00

319 lines
13 KiB
SQL

-- Generated by aether-data-schema from schema/logical/*.toml.
-- Do not edit generated files directly; edit logical schema or explicit overrides instead.
CREATE TABLE IF NOT EXISTS wallets (
id TEXT PRIMARY KEY NOT NULL,
user_id TEXT,
api_key_id TEXT,
balance REAL NOT NULL DEFAULT 0,
gift_balance REAL NOT NULL DEFAULT 0,
limit_mode TEXT NOT NULL DEFAULT 'finite',
currency TEXT NOT NULL DEFAULT 'USD',
status TEXT NOT NULL DEFAULT 'active',
total_recharged REAL NOT NULL DEFAULT 0,
total_consumed REAL NOT NULL DEFAULT 0,
total_refunded REAL NOT NULL DEFAULT 0,
total_adjusted REAL NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
UNIQUE (user_id),
UNIQUE (api_key_id)
);
CREATE INDEX IF NOT EXISTS wallets_api_key_id_idx ON wallets (api_key_id);
CREATE INDEX IF NOT EXISTS wallets_user_id_idx ON wallets (user_id);
CREATE TABLE IF NOT EXISTS wallet_transactions (
id TEXT PRIMARY KEY NOT NULL,
wallet_id TEXT NOT NULL,
category TEXT NOT NULL,
reason_code TEXT NOT NULL,
amount REAL NOT NULL,
balance_before REAL NOT NULL,
balance_after REAL NOT NULL,
recharge_balance_before REAL NOT NULL,
recharge_balance_after REAL NOT NULL,
gift_balance_before REAL NOT NULL,
gift_balance_after REAL NOT NULL,
link_type TEXT,
link_id TEXT,
operator_id TEXT,
description TEXT,
created_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_wallet_tx_wallet_created ON wallet_transactions (wallet_id, created_at);
CREATE INDEX IF NOT EXISTS idx_wallet_tx_category_created ON wallet_transactions (category, created_at);
CREATE INDEX IF NOT EXISTS idx_wallet_tx_reason_created ON wallet_transactions (reason_code, created_at);
CREATE INDEX IF NOT EXISTS idx_wallet_tx_link ON wallet_transactions (link_type, link_id);
CREATE INDEX IF NOT EXISTS ix_wallet_transactions_operator_id ON wallet_transactions (operator_id);
CREATE TABLE IF NOT EXISTS wallet_daily_usage_ledgers (
id TEXT PRIMARY KEY NOT NULL,
wallet_id TEXT NOT NULL,
billing_date TEXT NOT NULL,
billing_timezone TEXT NOT NULL,
total_cost_usd REAL NOT NULL DEFAULT 0,
total_requests INTEGER NOT NULL DEFAULT 0,
input_tokens INTEGER NOT NULL DEFAULT 0,
output_tokens INTEGER NOT NULL DEFAULT 0,
cache_creation_tokens INTEGER NOT NULL DEFAULT 0,
cache_read_tokens INTEGER NOT NULL DEFAULT 0,
first_finalized_at INTEGER,
last_finalized_at INTEGER,
aggregated_at INTEGER NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_wallet_daily_usage_wallet_date ON wallet_daily_usage_ledgers (wallet_id, billing_timezone, billing_date);
CREATE TABLE IF NOT EXISTS payment_orders (
id TEXT PRIMARY KEY NOT NULL,
order_no TEXT NOT NULL,
wallet_id TEXT NOT NULL,
user_id TEXT,
amount_usd REAL NOT NULL,
pay_amount REAL,
pay_currency TEXT,
exchange_rate REAL,
refunded_amount_usd REAL NOT NULL DEFAULT 0,
refundable_amount_usd REAL NOT NULL DEFAULT 0,
payment_method TEXT NOT NULL,
payment_provider TEXT,
payment_channel TEXT,
order_kind TEXT NOT NULL DEFAULT 'wallet_recharge',
product_id TEXT,
product_snapshot TEXT,
fulfillment_status TEXT NOT NULL DEFAULT 'pending',
fulfillment_error TEXT,
gateway_order_id TEXT,
gateway_response TEXT,
status TEXT NOT NULL DEFAULT 'pending',
created_at INTEGER NOT NULL,
paid_at INTEGER,
credited_at INTEGER,
expires_at INTEGER,
UNIQUE (order_no)
);
CREATE INDEX IF NOT EXISTS idx_payment_orders_wallet_created ON payment_orders (wallet_id, created_at);
CREATE INDEX IF NOT EXISTS idx_payment_orders_user_created ON payment_orders (user_id, created_at);
CREATE INDEX IF NOT EXISTS idx_payment_orders_status ON payment_orders (status);
CREATE INDEX IF NOT EXISTS idx_payment_orders_gateway_order_id ON payment_orders (gateway_order_id);
CREATE INDEX IF NOT EXISTS idx_payment_orders_kind_status ON payment_orders (order_kind, status);
CREATE INDEX IF NOT EXISTS idx_payment_orders_product ON payment_orders (product_id);
CREATE TABLE IF NOT EXISTS user_invite_codes (
user_id TEXT PRIMARY KEY NOT NULL,
invite_code TEXT NOT NULL,
active INTEGER NOT NULL DEFAULT 1,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
UNIQUE (invite_code),
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 TEXT PRIMARY KEY NOT NULL,
inviter_user_id TEXT NOT NULL,
invitee_user_id TEXT NOT NULL,
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,
UNIQUE (invitee_user_id),
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 INDEX IF NOT EXISTS idx_user_referrals_inviter ON user_referrals (inviter_user_id, created_at);
CREATE INDEX IF NOT EXISTS idx_user_referrals_created ON user_referrals (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 NOT NULL,
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,
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,
UNIQUE (idempotency_key),
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
);
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_inviter_created ON referral_rewards (inviter_user_id, created_at);
CREATE INDEX IF NOT EXISTS idx_referral_rewards_created ON referral_rewards (created_at);
CREATE INDEX IF NOT EXISTS idx_referral_rewards_source_order ON referral_rewards (source_order_id);
CREATE TABLE IF NOT EXISTS payment_gateway_configs (
provider TEXT PRIMARY KEY NOT NULL,
enabled INTEGER NOT NULL DEFAULT 0,
endpoint_url TEXT NOT NULL,
callback_base_url TEXT,
merchant_id TEXT NOT NULL,
merchant_key_encrypted TEXT,
pay_currency TEXT NOT NULL DEFAULT 'CNY',
usd_exchange_rate REAL NOT NULL DEFAULT 7.2,
min_recharge_usd REAL NOT NULL DEFAULT 1,
channels_json TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS payment_callbacks (
id TEXT PRIMARY KEY NOT NULL,
payment_order_id TEXT,
payment_method TEXT NOT NULL,
callback_key TEXT NOT NULL,
order_no TEXT,
gateway_order_id TEXT,
payload_hash TEXT,
signature_valid INTEGER NOT NULL DEFAULT 0,
status TEXT NOT NULL DEFAULT 'received',
payload TEXT,
error_message TEXT,
created_at INTEGER NOT NULL,
processed_at INTEGER,
UNIQUE (callback_key)
);
CREATE INDEX IF NOT EXISTS idx_payment_callbacks_order ON payment_callbacks (order_no);
CREATE INDEX IF NOT EXISTS idx_payment_callbacks_gateway_order ON payment_callbacks (gateway_order_id);
CREATE INDEX IF NOT EXISTS idx_payment_callbacks_created ON payment_callbacks (created_at);
CREATE INDEX IF NOT EXISTS ix_payment_callbacks_payment_order_id ON payment_callbacks (payment_order_id);
CREATE TABLE IF NOT EXISTS billing_plans (
id TEXT PRIMARY KEY NOT NULL,
title TEXT NOT NULL,
description TEXT,
price_amount REAL NOT NULL,
price_currency TEXT NOT NULL DEFAULT 'CNY',
duration_unit TEXT NOT NULL,
duration_value INTEGER NOT NULL,
enabled INTEGER NOT NULL DEFAULT 1,
sort_order INTEGER NOT NULL DEFAULT 0,
max_active_per_user INTEGER NOT NULL DEFAULT 1,
purchase_limit_scope TEXT NOT NULL DEFAULT 'active_period',
entitlements_json TEXT NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_billing_plans_enabled_sort ON billing_plans (enabled, sort_order);
CREATE TABLE IF NOT EXISTS user_plan_entitlements (
id TEXT PRIMARY KEY NOT NULL,
user_id TEXT NOT NULL,
plan_id TEXT NOT NULL,
payment_order_id TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
starts_at INTEGER NOT NULL,
expires_at INTEGER NOT NULL,
entitlements_snapshot TEXT NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_user_plan_entitlements_user_active ON user_plan_entitlements (user_id, status, expires_at);
CREATE INDEX IF NOT EXISTS idx_user_plan_entitlements_order ON user_plan_entitlements (payment_order_id);
CREATE TABLE IF NOT EXISTS entitlement_usage_ledgers (
id TEXT PRIMARY KEY NOT NULL,
user_entitlement_id TEXT NOT NULL,
user_id TEXT NOT NULL,
request_id TEXT NOT NULL,
amount_usd REAL NOT NULL,
balance_before REAL NOT NULL,
balance_after REAL NOT NULL,
usage_date TEXT NOT NULL,
created_at INTEGER NOT NULL,
UNIQUE (user_entitlement_id, request_id)
);
CREATE INDEX IF NOT EXISTS idx_entitlement_usage_user_date ON entitlement_usage_ledgers (user_id, usage_date);
CREATE INDEX IF NOT EXISTS idx_entitlement_usage_entitlement_date ON entitlement_usage_ledgers (user_entitlement_id, usage_date);
CREATE TABLE IF NOT EXISTS refund_requests (
id TEXT PRIMARY KEY NOT NULL,
refund_no TEXT NOT NULL,
wallet_id TEXT NOT NULL,
user_id TEXT,
payment_order_id TEXT,
source_type TEXT NOT NULL,
source_id TEXT,
refund_mode TEXT NOT NULL,
amount_usd REAL NOT NULL,
status TEXT NOT NULL DEFAULT 'pending_approval',
reason TEXT,
requested_by TEXT,
approved_by TEXT,
processed_by TEXT,
gateway_refund_id TEXT,
payout_method TEXT,
payout_reference TEXT,
payout_proof TEXT,
failure_reason TEXT,
idempotency_key TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
processed_at INTEGER,
completed_at INTEGER,
UNIQUE (refund_no),
UNIQUE (idempotency_key)
);
CREATE INDEX IF NOT EXISTS idx_refund_wallet_created ON refund_requests (wallet_id, created_at);
CREATE INDEX IF NOT EXISTS idx_refund_user_created ON refund_requests (user_id, created_at);
CREATE INDEX IF NOT EXISTS idx_refund_status ON refund_requests (status);
CREATE INDEX IF NOT EXISTS ix_refund_requests_payment_order_id ON refund_requests (payment_order_id);
CREATE INDEX IF NOT EXISTS ix_refund_requests_requested_by ON refund_requests (requested_by);
CREATE INDEX IF NOT EXISTS ix_refund_requests_approved_by ON refund_requests (approved_by);
CREATE INDEX IF NOT EXISTS ix_refund_requests_processed_by ON refund_requests (processed_by);
CREATE TABLE IF NOT EXISTS redeem_code_batches (
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
amount_usd REAL NOT NULL,
currency TEXT NOT NULL DEFAULT 'USD',
balance_bucket TEXT NOT NULL DEFAULT 'gift',
total_count INTEGER NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
description TEXT,
created_by TEXT,
expires_at INTEGER,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_redeem_code_batches_status ON redeem_code_batches (status, created_at);
CREATE TABLE IF NOT EXISTS redeem_codes (
id TEXT PRIMARY KEY NOT NULL,
batch_id TEXT NOT NULL,
code_hash TEXT NOT NULL,
code_prefix TEXT NOT NULL,
code_suffix TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
redeemed_by_user_id TEXT,
redeemed_wallet_id TEXT,
redeemed_payment_order_id TEXT,
redeemed_at INTEGER,
disabled_by TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
UNIQUE (code_hash)
);
CREATE INDEX IF NOT EXISTS idx_redeem_codes_batch_created ON redeem_codes (batch_id, created_at);
CREATE INDEX IF NOT EXISTS idx_redeem_codes_status ON redeem_codes (status, updated_at);
CREATE INDEX IF NOT EXISTS idx_redeem_codes_redeemed_user ON redeem_codes (redeemed_by_user_id, redeemed_at);
CREATE INDEX IF NOT EXISTS idx_redeem_codes_redeemed_order ON redeem_codes (redeemed_payment_order_id);