CREATE TABLE IF NOT EXISTS users ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, deleted_at TIMESTAMPTZ, username VARCHAR(64) NOT NULL, password_hash VARCHAR(255) NOT NULL, nickname VARCHAR(64), role VARCHAR(32) NOT NULL DEFAULT 'distributor', status BIGINT DEFAULT 1, invite_code VARCHAR(32), parent_id BIGINT ); CREATE UNIQUE INDEX IF NOT EXISTS idx_users_username ON users (username); CREATE UNIQUE INDEX IF NOT EXISTS idx_users_invite_code ON users (invite_code); CREATE INDEX IF NOT EXISTS idx_users_deleted_at ON users (deleted_at); CREATE INDEX IF NOT EXISTS idx_users_parent_id ON users (parent_id); CREATE TABLE IF NOT EXISTS merchants ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, deleted_at TIMESTAMPTZ, code VARCHAR(64) NOT NULL, name VARCHAR(128) NOT NULL, status VARCHAR(16) NOT NULL DEFAULT 'active', contact_name VARCHAR(64), contact_info VARCHAR(128) ); CREATE UNIQUE INDEX IF NOT EXISTS idx_merchants_code ON merchants (code); CREATE INDEX IF NOT EXISTS idx_merchants_deleted_at ON merchants (deleted_at); CREATE INDEX IF NOT EXISTS idx_merchants_status ON merchants (status); CREATE TABLE IF NOT EXISTS merchant_members ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, merchant_id BIGINT NOT NULL, user_id BIGINT NOT NULL, role VARCHAR(32) NOT NULL DEFAULT 'operator', status BIGINT NOT NULL DEFAULT 1, is_default BOOLEAN NOT NULL DEFAULT FALSE ); CREATE UNIQUE INDEX IF NOT EXISTS idx_merchant_member ON merchant_members (merchant_id, user_id); CREATE INDEX IF NOT EXISTS idx_merchant_members_merchant_id ON merchant_members (merchant_id); CREATE INDEX IF NOT EXISTS idx_merchant_members_user_id ON merchant_members (user_id); CREATE TABLE IF NOT EXISTS skins ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, deleted_at TIMESTAMPTZ, merchant_id BIGINT NOT NULL DEFAULT 0, name VARCHAR(128) NOT NULL, sku VARCHAR(64) NOT NULL, game VARCHAR(64), category VARCHAR(64), cover_url VARCHAR(512), price DOUBLE PRECISION NOT NULL DEFAULT 0, cost_price DOUBLE PRECISION DEFAULT 0, commission DOUBLE PRECISION DEFAULT 0, stock BIGINT DEFAULT 0, status BIGINT DEFAULT 1, description TEXT ); DROP INDEX IF EXISTS idx_skins_sku; CREATE UNIQUE INDEX IF NOT EXISTS idx_skins_merchant_sku ON skins (merchant_id, sku); CREATE INDEX IF NOT EXISTS idx_skins_deleted_at ON skins (deleted_at); CREATE INDEX IF NOT EXISTS idx_skins_merchant_id ON skins (merchant_id); CREATE INDEX IF NOT EXISTS idx_skins_game ON skins (game); CREATE INDEX IF NOT EXISTS idx_skins_category ON skins (category); CREATE TABLE IF NOT EXISTS orders ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, deleted_at TIMESTAMPTZ, merchant_id BIGINT NOT NULL DEFAULT 0, order_no VARCHAR(64) NOT NULL, skin_id BIGINT NOT NULL, distributor_id BIGINT NOT NULL, buyer_name VARCHAR(64), amount DOUBLE PRECISION NOT NULL, commission_amt DOUBLE PRECISION DEFAULT 0, status VARCHAR(32) DEFAULT 'pending', remark VARCHAR(255), provider_order_no VARCHAR(64), shipped_at TIMESTAMPTZ, ship_fail_reason VARCHAR(512), game_channel VARCHAR(64), game_uid VARCHAR(128), role_name VARCHAR(64), pay_score BIGINT DEFAULT 0 ); CREATE UNIQUE INDEX IF NOT EXISTS idx_orders_order_no ON orders (order_no); CREATE INDEX IF NOT EXISTS idx_orders_deleted_at ON orders (deleted_at); CREATE INDEX IF NOT EXISTS idx_orders_merchant_id ON orders (merchant_id); CREATE INDEX IF NOT EXISTS idx_orders_skin_id ON orders (skin_id); CREATE INDEX IF NOT EXISTS idx_orders_distributor_id ON orders (distributor_id); CREATE INDEX IF NOT EXISTS idx_orders_status ON orders (status); CREATE INDEX IF NOT EXISTS idx_orders_provider_order_no ON orders (provider_order_no); CREATE TABLE IF NOT EXISTS ship_logs ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, merchant_id BIGINT NOT NULL DEFAULT 0, order_no VARCHAR(64) NOT NULL, order_id BIGINT, ship_status VARCHAR(32) NOT NULL, provider_order_no VARCHAR(64), fail_reason VARCHAR(512), payload TEXT, result_status VARCHAR(32), message VARCHAR(255) ); CREATE INDEX IF NOT EXISTS idx_ship_logs_merchant_id ON ship_logs (merchant_id); CREATE INDEX IF NOT EXISTS idx_ship_logs_order_no ON ship_logs (order_no); CREATE INDEX IF NOT EXISTS idx_ship_logs_order_id ON ship_logs (order_id); CREATE TABLE IF NOT EXISTS api_clients ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, deleted_at TIMESTAMPTZ, merchant_id BIGINT NOT NULL, name VARCHAR(128) NOT NULL, app_key VARCHAR(96) NOT NULL, secret_ciphertext TEXT NOT NULL, signature_version VARCHAR(16) NOT NULL DEFAULT 'v1', scopes TEXT NOT NULL, status VARCHAR(16) NOT NULL DEFAULT 'active', expires_at TIMESTAMPTZ, last_used_at TIMESTAMPTZ ); CREATE UNIQUE INDEX IF NOT EXISTS idx_api_clients_app_key ON api_clients (app_key); CREATE INDEX IF NOT EXISTS idx_api_clients_deleted_at ON api_clients (deleted_at); CREATE INDEX IF NOT EXISTS idx_api_clients_merchant_id ON api_clients (merchant_id); CREATE INDEX IF NOT EXISTS idx_api_clients_status ON api_clients (status); CREATE TABLE IF NOT EXISTS products ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, deleted_at TIMESTAMPTZ, code VARCHAR(96) NOT NULL, name VARCHAR(160) NOT NULL, category VARCHAR(64), description TEXT, attributes TEXT, status VARCHAR(16) NOT NULL DEFAULT 'active' ); CREATE UNIQUE INDEX IF NOT EXISTS idx_products_code ON products (code); CREATE INDEX IF NOT EXISTS idx_products_deleted_at ON products (deleted_at); CREATE INDEX IF NOT EXISTS idx_products_category ON products (category); CREATE INDEX IF NOT EXISTS idx_products_status ON products (status); CREATE TABLE IF NOT EXISTS merchant_products ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, deleted_at TIMESTAMPTZ, merchant_id BIGINT NOT NULL, product_id BIGINT NOT NULL, sku VARCHAR(96) NOT NULL, display_name VARCHAR(160), price_amount BIGINT NOT NULL DEFAULT 0, cost_amount BIGINT NOT NULL DEFAULT 0, currency VARCHAR(12) NOT NULL DEFAULT 'CNY', stock BIGINT NOT NULL DEFAULT -1, status VARCHAR(16) NOT NULL DEFAULT 'active', fulfillment_config TEXT ); CREATE UNIQUE INDEX IF NOT EXISTS idx_merchant_product_sku ON merchant_products (merchant_id, sku); CREATE INDEX IF NOT EXISTS idx_merchant_products_deleted_at ON merchant_products (deleted_at); CREATE INDEX IF NOT EXISTS idx_merchant_products_merchant_id ON merchant_products (merchant_id); CREATE INDEX IF NOT EXISTS idx_merchant_products_product_id ON merchant_products (product_id); CREATE INDEX IF NOT EXISTS idx_merchant_products_status ON merchant_products (status); CREATE TABLE IF NOT EXISTS wallet_accounts ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, merchant_id BIGINT NOT NULL, currency VARCHAR(12) NOT NULL DEFAULT 'CNY', available_balance BIGINT NOT NULL DEFAULT 0, frozen_balance BIGINT NOT NULL DEFAULT 0 ); CREATE UNIQUE INDEX IF NOT EXISTS idx_wallet_accounts_merchant_id ON wallet_accounts (merchant_id); CREATE TABLE IF NOT EXISTS wallet_ledger_entries ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, merchant_id BIGINT NOT NULL, wallet_account_id BIGINT NOT NULL, entry_no VARCHAR(64) NOT NULL, type VARCHAR(24) NOT NULL, amount BIGINT NOT NULL, balance_after BIGINT NOT NULL, reference_type VARCHAR(32) NOT NULL, reference_no VARCHAR(96) NOT NULL, idempotency_key VARCHAR(128), note VARCHAR(255) ); CREATE UNIQUE INDEX IF NOT EXISTS idx_wallet_ledger_entries_entry_no ON wallet_ledger_entries (entry_no); CREATE UNIQUE INDEX IF NOT EXISTS idx_wallet_ledger_idempotency ON wallet_ledger_entries (merchant_id, idempotency_key); CREATE INDEX IF NOT EXISTS idx_wallet_ledger_entries_merchant_id ON wallet_ledger_entries (merchant_id); CREATE INDEX IF NOT EXISTS idx_wallet_ledger_entries_wallet_account_id ON wallet_ledger_entries (wallet_account_id); CREATE INDEX IF NOT EXISTS idx_wallet_ledger_entries_type ON wallet_ledger_entries (type); CREATE INDEX IF NOT EXISTS idx_wallet_ledger_entries_reference_no ON wallet_ledger_entries (reference_no); CREATE TABLE IF NOT EXISTS fulfillment_orders ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, deleted_at TIMESTAMPTZ, merchant_id BIGINT NOT NULL, order_no VARCHAR(64) NOT NULL, client_order_no VARCHAR(96) NOT NULL, merchant_product_id BIGINT NOT NULL, product_sku VARCHAR(96) NOT NULL, product_name VARCHAR(160) NOT NULL, quantity BIGINT NOT NULL DEFAULT 1, amount BIGINT NOT NULL, currency VARCHAR(12) NOT NULL DEFAULT 'CNY', payment_status VARCHAR(16) NOT NULL DEFAULT 'pending', fulfillment_status VARCHAR(16) NOT NULL DEFAULT 'pending', buyer_reference VARCHAR(128), request_data TEXT, result_data TEXT, provider_order_no VARCHAR(96), failure_reason VARCHAR(512), cancelled_at TIMESTAMPTZ, delivered_at TIMESTAMPTZ ); CREATE UNIQUE INDEX IF NOT EXISTS idx_fulfillment_orders_order_no ON fulfillment_orders (order_no); CREATE UNIQUE INDEX IF NOT EXISTS idx_order_client_no ON fulfillment_orders (merchant_id, client_order_no); CREATE INDEX IF NOT EXISTS idx_fulfillment_orders_deleted_at ON fulfillment_orders (deleted_at); CREATE INDEX IF NOT EXISTS idx_fulfillment_orders_merchant_id ON fulfillment_orders (merchant_id); CREATE INDEX IF NOT EXISTS idx_fulfillment_orders_merchant_product_id ON fulfillment_orders (merchant_product_id); CREATE INDEX IF NOT EXISTS idx_fulfillment_orders_payment_status ON fulfillment_orders (payment_status); CREATE INDEX IF NOT EXISTS idx_fulfillment_orders_fulfillment_status ON fulfillment_orders (fulfillment_status); CREATE INDEX IF NOT EXISTS idx_fulfillment_orders_provider_order_no ON fulfillment_orders (provider_order_no); CREATE TABLE IF NOT EXISTS fulfillment_jobs ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, merchant_id BIGINT NOT NULL, order_id BIGINT NOT NULL, status VARCHAR(16) NOT NULL DEFAULT 'pending', attempts BIGINT NOT NULL DEFAULT 0, next_run_at TIMESTAMPTZ NOT NULL, provider_order_no VARCHAR(96), request_payload TEXT, result_payload TEXT, last_error VARCHAR(512) ); CREATE UNIQUE INDEX IF NOT EXISTS idx_fulfillment_jobs_order_id ON fulfillment_jobs (order_id); CREATE INDEX IF NOT EXISTS idx_fulfillment_jobs_merchant_id ON fulfillment_jobs (merchant_id); CREATE INDEX IF NOT EXISTS idx_fulfillment_jobs_status ON fulfillment_jobs (status); CREATE INDEX IF NOT EXISTS idx_fulfillment_jobs_next_run_at ON fulfillment_jobs (next_run_at); CREATE INDEX IF NOT EXISTS idx_fulfillment_jobs_provider_order_no ON fulfillment_jobs (provider_order_no); CREATE TABLE IF NOT EXISTS callback_subscriptions ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, deleted_at TIMESTAMPTZ, merchant_id BIGINT NOT NULL, name VARCHAR(128) NOT NULL, url VARCHAR(1024) NOT NULL, events TEXT NOT NULL, secret_ciphertext TEXT NOT NULL, status VARCHAR(16) NOT NULL DEFAULT 'active' ); CREATE INDEX IF NOT EXISTS idx_callback_subscriptions_deleted_at ON callback_subscriptions (deleted_at); CREATE INDEX IF NOT EXISTS idx_callback_subscriptions_merchant_id ON callback_subscriptions (merchant_id); CREATE INDEX IF NOT EXISTS idx_callback_subscriptions_status ON callback_subscriptions (status); CREATE TABLE IF NOT EXISTS callback_deliveries ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, merchant_id BIGINT NOT NULL, callback_subscription_id BIGINT NOT NULL, event_id VARCHAR(64) NOT NULL, event VARCHAR(64) NOT NULL, payload TEXT NOT NULL, status VARCHAR(16) NOT NULL DEFAULT 'pending', attempts BIGINT NOT NULL DEFAULT 0, next_attempt_at TIMESTAMPTZ NOT NULL, last_status_code BIGINT NOT NULL DEFAULT 0, last_response TEXT, delivered_at TIMESTAMPTZ ); CREATE UNIQUE INDEX IF NOT EXISTS idx_callback_deliveries_event_id ON callback_deliveries (event_id); CREATE INDEX IF NOT EXISTS idx_callback_deliveries_merchant_id ON callback_deliveries (merchant_id); CREATE INDEX IF NOT EXISTS idx_callback_deliveries_callback_subscription_id ON callback_deliveries (callback_subscription_id); CREATE INDEX IF NOT EXISTS idx_callback_deliveries_event ON callback_deliveries (event); CREATE INDEX IF NOT EXISTS idx_callback_deliveries_status ON callback_deliveries (status); CREATE INDEX IF NOT EXISTS idx_callback_deliveries_next_attempt_at ON callback_deliveries (next_attempt_at); CREATE TABLE IF NOT EXISTS api_request_nonces ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, api_client_id BIGINT NOT NULL, nonce VARCHAR(96) NOT NULL, expires_at TIMESTAMPTZ NOT NULL ); CREATE UNIQUE INDEX IF NOT EXISTS idx_api_nonce ON api_request_nonces (api_client_id, nonce); CREATE INDEX IF NOT EXISTS idx_api_request_nonces_api_client_id ON api_request_nonces (api_client_id); CREATE INDEX IF NOT EXISTS idx_api_request_nonces_expires_at ON api_request_nonces (expires_at); CREATE TABLE IF NOT EXISTS audit_logs ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ, merchant_id BIGINT, actor_user_id BIGINT, api_client_id BIGINT, request_id VARCHAR(64), action VARCHAR(96) NOT NULL, entity_type VARCHAR(64) NOT NULL, entity_id VARCHAR(96) NOT NULL, metadata TEXT ); CREATE INDEX IF NOT EXISTS idx_audit_logs_merchant_id ON audit_logs (merchant_id); CREATE INDEX IF NOT EXISTS idx_audit_logs_actor_user_id ON audit_logs (actor_user_id); CREATE INDEX IF NOT EXISTS idx_audit_logs_api_client_id ON audit_logs (api_client_id); CREATE INDEX IF NOT EXISTS idx_audit_logs_request_id ON audit_logs (request_id); CREATE INDEX IF NOT EXISTS idx_audit_logs_action ON audit_logs (action); CREATE INDEX IF NOT EXISTS idx_audit_logs_entity_type ON audit_logs (entity_type); CREATE INDEX IF NOT EXISTS idx_audit_logs_entity_id ON audit_logs (entity_id);