-- ============================================================================ -- 012_rock_fulfillment.sql: Rock 发货平台 (0666.pro) 代理接入与库存风控数据表与商品目录 -- ============================================================================ -- 1) Rock 平台系统配置表 CREATE TABLE IF NOT EXISTS rock_configs ( id BIGINT PRIMARY KEY DEFAULT 1, auto_delivery_enabled BOOLEAN NOT NULL DEFAULT true, base_url VARCHAR(255) NOT NULL DEFAULT 'https://0666.pro', bearer_token TEXT NOT NULL DEFAULT '', username VARCHAR(128) NOT NULL DEFAULT '', points NUMERIC(12, 2) NOT NULL DEFAULT 0.00, min_points_warning NUMERIC(12, 2) NOT NULL DEFAULT 1050.00, token_warning_stock INT NOT NULL DEFAULT 10, auto_purchase_enabled BOOLEAN NOT NULL DEFAULT false, auto_purchase_count INT NOT NULL DEFAULT 50, purchase_cooldown_minutes INT NOT NULL DEFAULT 10, last_purchased_at TIMESTAMPTZ, last_heartbeat_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- 初始化默认配置行(固定 ID=1) INSERT INTO rock_configs (id, auto_delivery_enabled, base_url, bearer_token, min_points_warning, token_warning_stock, auto_purchase_enabled, auto_purchase_count) VALUES (1, true, 'https://0666.pro', '', 1050.00, 10, false, 50) ON CONFLICT (id) DO NOTHING; -- 2) Rock 通用 Token 本地库存池表 CREATE TABLE IF NOT EXISTS rock_tokens ( id BIGSERIAL PRIMARY KEY, upstream_token_id VARCHAR(64) NOT NULL DEFAULT '', token TEXT NOT NULL, redeem_code VARCHAR(64) NOT NULL, color VARCHAR(32) NOT NULL DEFAULT 'black', region VARCHAR(64) NOT NULL DEFAULT 'Q/V可用', status VARCHAR(32) NOT NULL DEFAULT 'available', assigned_order_no VARCHAR(128), expected_uid VARCHAR(64), actual_role_name VARCHAR(128), actual_role_id VARCHAR(64), actual_channel VARCHAR(64), initial_bound_snapshot TEXT, upstream_fulfill_id BIGINT, target_skin VARCHAR(128), target_currency VARCHAR(64) NOT NULL DEFAULT '幸运币', target_currency_quantity INT NOT NULL DEFAULT 0, mismatch_at TIMESTAMPTZ, mismatch_reason VARCHAR(255), claimed_at TIMESTAMPTZ, bound_at TIMESTAMPTZ, redeemed_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_rock_tokens_status_color ON rock_tokens (status, color); CREATE INDEX IF NOT EXISTS idx_rock_tokens_assigned_order ON rock_tokens (assigned_order_no); CREATE INDEX IF NOT EXISTS idx_rock_tokens_redeem_code ON rock_tokens (redeem_code); CREATE INDEX IF NOT EXISTS idx_rock_tokens_mismatch ON rock_tokens (status, mismatch_at); -- 3) 插入 Rock 皮肤到商品目录 (products 表与自营 merchant_products 表) WITH rock_products(sku, name, category, default_price, fulfillment_config) AS ( VALUES ('rock_black_tshirt', '黑色ROCK长袖T恤', '服饰', 2500, '{"routing_mode":"rock","priority":["rock"],"delivery_type":"LINK_BINDING"}'), ('rock_blue_sportswear', '蓝色运动服上衣', '服饰', 2500, '{"routing_mode":"rock","priority":["rock"],"delivery_type":"LINK_BINDING"}') ) INSERT INTO products (code, name, category, status) SELECT p.sku, p.name, p.category, 'active' FROM rock_products p ON CONFLICT (code) DO UPDATE SET name = EXCLUDED.name, category = EXCLUDED.category, status = 'active'; WITH rock_products(sku, name, category, default_price, fulfillment_config) AS ( VALUES ('rock_black_tshirt', '黑色ROCK长袖T恤', '服饰', 2500, '{"routing_mode":"rock","priority":["rock"],"delivery_type":"LINK_BINDING"}'), ('rock_blue_sportswear', '蓝色运动服上衣', '服饰', 2500, '{"routing_mode":"rock","priority":["rock"],"delivery_type":"LINK_BINDING"}') ), self_merchant AS ( SELECT id FROM merchants WHERE code = 'self-operated' LIMIT 1 ) INSERT INTO merchant_products ( merchant_id, product_id, sku, display_name, price_amount, cost_amount, currency, stock, status, fulfillment_config ) SELECT sm.id, p.id, rp.sku, rp.name, rp.default_price, rp.default_price, 'POINT', -1, 'active', rp.fulfillment_config FROM rock_products rp JOIN products p ON p.code = rp.sku CROSS JOIN self_merchant sm ON CONFLICT (merchant_id, sku) DO UPDATE SET display_name = EXCLUDED.display_name, price_amount = EXCLUDED.price_amount, cost_amount = EXCLUDED.cost_amount, fulfillment_config = EXCLUDED.fulfillment_config, status = 'active', deleted_at = NULL;