diff --git a/src/Core/DBMigratorPart2.php b/src/Core/DBMigratorPart2.php index ada980a8..23884638 100644 --- a/src/Core/DBMigratorPart2.php +++ b/src/Core/DBMigratorPart2.php @@ -1,563 +1,564 @@ -get_charset_collate(); - $prefix = $wpdb->prefix; - - // ---------- 1. 统一资产账户与不可变账本 ---------- - $sql_asset_account = "CREATE TABLE {$prefix}aksrt_asset_account ( - user_id BIGINT UNSIGNED NOT NULL, - asset VARCHAR(16) NOT NULL, - balance_minor BIGINT UNSIGNED NOT NULL DEFAULT 0, - version BIGINT UNSIGNED NOT NULL DEFAULT 0, - updated_at DATETIME(6) NOT NULL, - PRIMARY KEY (user_id, asset), - KEY idx_asset_balance (asset, balance_minor) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_asset_account); - - $sql_asset_transaction = "CREATE TABLE {$prefix}aksrt_asset_transaction ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - operation_key VARCHAR(191) NOT NULL, - payload_hash CHAR(64) NOT NULL, - reason VARCHAR(64) NOT NULL, - ref_type VARCHAR(32) DEFAULT NULL, - ref_key VARCHAR(191) DEFAULT NULL, - actor_user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, - metadata LONGTEXT DEFAULT NULL, - ip VARCHAR(64) DEFAULT NULL, - ua VARCHAR(255) DEFAULT NULL, - created_at DATETIME(6) NOT NULL, - PRIMARY KEY (id), - UNIQUE KEY uniq_operation_key (operation_key), - KEY idx_reason_time (reason, created_at), - KEY idx_reference (ref_type, ref_key) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_asset_transaction); - - $sql_asset_entry = "CREATE TABLE {$prefix}aksrt_asset_entry ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - transaction_id BIGINT UNSIGNED NOT NULL, - line_no SMALLINT UNSIGNED NOT NULL, - user_id BIGINT UNSIGNED NOT NULL, - asset VARCHAR(16) NOT NULL, - entry_type VARCHAR(32) NOT NULL, - delta_minor BIGINT NOT NULL, - balance_after_minor BIGINT UNSIGNED NOT NULL, - counterparty_user_id BIGINT UNSIGNED DEFAULT NULL, - remark VARCHAR(255) DEFAULT NULL, - created_at DATETIME(6) NOT NULL, - PRIMARY KEY (id), - UNIQUE KEY uniq_transaction_line (transaction_id, line_no), - KEY idx_user_asset_time (user_id, asset, created_at, id), - KEY idx_transaction (transaction_id) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_asset_entry); - - // 后台「资金流水」页的默认查询是 WHERE 1=1 ORDER BY created_at DESC, id DESC LIMIT n - // 外加同条件的 COUNT(*),且界面上的日期区间筛选也只有 created_at 可用。 - // 索引必须能以 created_at 为前导列,否则每次翻页都是全表扫描 + filesort, - // 而 idx_flow_event_time 的前导列是 event_type(筛选时可空),救不了这两条查询。 - $sql_financial_flow = "CREATE TABLE {$prefix}aksrt_financial_flow ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - flow_number VARCHAR(32) NOT NULL, - operation_key VARCHAR(191) NOT NULL, - event_type VARCHAR(64) NOT NULL, - ref_type VARCHAR(32) DEFAULT NULL, - ref_key VARCHAR(191) DEFAULT NULL, - order_number VARCHAR(32) DEFAULT NULL, - withdrawal_number VARCHAR(32) DEFAULT NULL, - ledger_transaction_id BIGINT UNSIGNED DEFAULT NULL, - provider_transaction_id VARCHAR(128) DEFAULT NULL, - metadata LONGTEXT DEFAULT NULL, - created_at DATETIME(6) NOT NULL, - PRIMARY KEY (id), - UNIQUE KEY uniq_flow_number (flow_number), - UNIQUE KEY uniq_flow_operation (operation_key), - UNIQUE KEY uniq_flow_ledger_transaction (ledger_transaction_id), - KEY idx_flow_created (created_at), - KEY idx_flow_event_time (event_type, created_at), - KEY idx_flow_order (order_number), - KEY idx_flow_withdrawal (withdrawal_number), - KEY idx_flow_reference (ref_type, ref_key), - KEY idx_flow_provider_transaction (provider_transaction_id) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_financial_flow); - - $sql_financial_flow_entry = "CREATE TABLE {$prefix}aksrt_financial_flow_entry ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - flow_id BIGINT UNSIGNED NOT NULL, - line_no SMALLINT UNSIGNED NOT NULL, - user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, - asset VARCHAR(16) NOT NULL, - direction VARCHAR(8) NOT NULL, - entry_type VARCHAR(32) NOT NULL, - delta_minor BIGINT NOT NULL, - balance_after_minor BIGINT UNSIGNED DEFAULT NULL, - counterparty_user_id BIGINT UNSIGNED DEFAULT NULL, - remark VARCHAR(255) DEFAULT NULL, - PRIMARY KEY (id), - UNIQUE KEY uniq_flow_line (flow_id, line_no), - KEY idx_flow_entry_user (user_id, flow_id), - KEY idx_flow_entry_asset (asset, flow_id), - KEY idx_flow_entry_direction (direction, flow_id), - KEY idx_flow_entry_counterparty (counterparty_user_id, flow_id) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_financial_flow_entry); - - // ---------- 2. 签到 ---------- - $sql_signin = "CREATE TABLE {$prefix}aksrt_signin_log ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - user_id BIGINT UNSIGNED NOT NULL, - signin_date DATE NOT NULL, - streak INT NOT NULL DEFAULT 1, - reward INT NOT NULL DEFAULT 0, - PRIMARY KEY (id), - UNIQUE KEY uniq_user_date (user_id, signin_date), - KEY idx_user (user_id) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_signin); - - // ---------- 3. 下载 token ---------- - $sql_dl_token = "CREATE TABLE {$prefix}aksrt_dl_token ( - token CHAR(48) NOT NULL, - user_id BIGINT UNSIGNED NOT NULL, - post_id BIGINT UNSIGNED NOT NULL, - source_index INT NOT NULL DEFAULT 0, - cost INT NOT NULL DEFAULT 0, - ip_bind VARCHAR(64) DEFAULT NULL, - ua_hash CHAR(40) DEFAULT NULL, - used_count INT NOT NULL DEFAULT 0, - max_uses INT NOT NULL DEFAULT 1, - expires_at DATETIME NOT NULL, - created_at DATETIME NOT NULL, - PRIMARY KEY (token), - KEY idx_expires (expires_at), - KEY idx_user (user_id), - KEY idx_post (post_id) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_dl_token); - - // ---------- 5. 用户通知收件箱 ---------- - $sql_notification = "CREATE TABLE {$prefix}aksrt_notification ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - user_id BIGINT UNSIGNED NOT NULL, - type VARCHAR(32) NOT NULL, - title VARCHAR(191) NOT NULL, - description TEXT DEFAULT NULL, - target_url TEXT DEFAULT NULL, - is_read TINYINT(1) NOT NULL DEFAULT 0, - created_at DATETIME NOT NULL, - read_at DATETIME DEFAULT NULL, - PRIMARY KEY (id), - KEY idx_user_created (user_id, created_at), - KEY idx_user_read (user_id, is_read) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_notification); - - // ---------- 8. 订单表 ---------- - $sql_order = "CREATE TABLE {$prefix}aksrt_order ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - order_number VARCHAR(32) NOT NULL, - request_key VARCHAR(191) DEFAULT NULL, - user_id BIGINT UNSIGNED NOT NULL, - product_type VARCHAR(32) NOT NULL DEFAULT '', - product_id BIGINT UNSIGNED NOT NULL DEFAULT 0, - amount DECIMAL(10,2) NOT NULL DEFAULT 0, - currency VARCHAR(8) NOT NULL DEFAULT 'CNY', - status VARCHAR(20) NOT NULL DEFAULT 'pending', - shipping_status TINYINT NOT NULL DEFAULT 0, - payment_method VARCHAR(32) NOT NULL DEFAULT '', - payment_transaction_id VARCHAR(128) NOT NULL DEFAULT '', - created_at DATETIME NOT NULL, - paid_at DATETIME DEFAULT NULL, - delivered_at DATETIME DEFAULT NULL, - completed_at DATETIME DEFAULT NULL, - cancelled_at DATETIME DEFAULT NULL, - refunded_at DATETIME DEFAULT NULL, - expires_at DATETIME DEFAULT NULL, - note TEXT DEFAULT NULL, - PRIMARY KEY (id), - UNIQUE KEY uniq_order_number (order_number), - UNIQUE KEY uniq_request_key (request_key), - KEY idx_user (user_id), - KEY idx_user_status_created (user_id, status, created_at), - KEY idx_status (status), - KEY idx_created (created_at), - KEY idx_product (product_type, product_id) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_order); - - // ---------- order after-sales ---------- - $sql_order_aftersale = "CREATE TABLE {$prefix}aksrt_order_aftersale ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - order_number VARCHAR(32) NOT NULL, - user_id BIGINT UNSIGNED NOT NULL, - aftersale_type VARCHAR(24) NOT NULL DEFAULT 'refund', - reason TEXT NOT NULL, - images LONGTEXT DEFAULT NULL, - refund_amount VARCHAR(32) NOT NULL DEFAULT '0', - status VARCHAR(20) NOT NULL DEFAULT 'pending', - admin_note TEXT DEFAULT NULL, - gateway_request_id VARCHAR(128) DEFAULT NULL, - gateway_refund_id VARCHAR(128) DEFAULT NULL, - gateway_result LONGTEXT DEFAULT NULL, - created_at DATETIME NOT NULL, - reviewed_at DATETIME DEFAULT NULL, - completed_at DATETIME DEFAULT NULL, - PRIMARY KEY (id), - KEY idx_order_status (order_number, status), - KEY idx_user_created (user_id, created_at), - KEY idx_status_created (status, created_at) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_order_aftersale); - - // ---------- 9. 作者收益台账 ---------- - $sql_revenue = "CREATE TABLE {$prefix}aksrt_revenue ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - author_id BIGINT UNSIGNED NOT NULL DEFAULT 0, - order_number VARCHAR(32) NOT NULL DEFAULT '', - post_id BIGINT UNSIGNED NOT NULL DEFAULT 0, - buyer_id BIGINT UNSIGNED NOT NULL DEFAULT 0, - currency VARCHAR(8) NOT NULL DEFAULT 'CNY', - original_amount DECIMAL(18,2) NOT NULL DEFAULT 0, - share_ratio DECIMAL(5,2) NOT NULL DEFAULT 0, - author_level TINYINT UNSIGNED NOT NULL DEFAULT 0, - amount DECIMAL(18,2) NOT NULL DEFAULT 0, - type VARCHAR(32) NOT NULL DEFAULT 'order', - status VARCHAR(20) NOT NULL DEFAULT 'pending', - reversal_amount DECIMAL(18,2) NOT NULL DEFAULT 0, - error_message TEXT DEFAULT NULL, - audit_json LONGTEXT DEFAULT NULL, - created_at DATETIME NOT NULL, - settled_at DATETIME DEFAULT NULL, - reversed_at DATETIME DEFAULT NULL, - PRIMARY KEY (id), - UNIQUE KEY uniq_creator_order (author_id, order_number), - KEY idx_author (author_id), - KEY idx_order (order_number), - KEY idx_post (post_id), - KEY idx_buyer (buyer_id), - KEY idx_currency (currency), - KEY idx_status (status), - KEY idx_created (created_at) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_revenue); - - // ---------- 10. 提现台账 ---------- - $sql_withdrawal = "CREATE TABLE {$prefix}aksrt_withdrawal ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - withdrawal_number VARCHAR(32) NOT NULL, - user_id BIGINT UNSIGNED NOT NULL, - amount DECIMAL(10,2) NOT NULL DEFAULT 0, - gross_amount DECIMAL(10,2) NOT NULL DEFAULT 0, - method VARCHAR(32) NOT NULL DEFAULT '', - account_info TEXT DEFAULT NULL, - status VARCHAR(20) NOT NULL DEFAULT 'pending', - operation_key VARCHAR(191) DEFAULT NULL, - ledger_transaction_id BIGINT UNSIGNED DEFAULT NULL, - created_at DATETIME NOT NULL, - processed_at DATETIME DEFAULT NULL, - note TEXT DEFAULT NULL, - PRIMARY KEY (id), - UNIQUE KEY uniq_withdrawal_number (withdrawal_number), - UNIQUE KEY uniq_withdrawal_operation (operation_key), - UNIQUE KEY uniq_ledger_transaction (ledger_transaction_id), - KEY idx_user (user_id), - KEY idx_user_created (user_id, created_at), - KEY idx_status (status), - KEY idx_created (created_at) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_withdrawal); - - // ---------- 11. 举报工单 ---------- - $sql_report = "CREATE TABLE {$prefix}aksrt_report ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - reporter_id BIGINT UNSIGNED NOT NULL, - target_type VARCHAR(20) NOT NULL, - target_id BIGINT UNSIGNED NOT NULL, - reason TEXT NOT NULL, - category VARCHAR(40) NOT NULL DEFAULT '', - status VARCHAR(20) NOT NULL DEFAULT 'pending', - created_at DATETIME NOT NULL, - updated_at DATETIME DEFAULT NULL, - notes_json TEXT DEFAULT NULL, - PRIMARY KEY (id), - KEY idx_status (status), - KEY idx_target (target_type, target_id), - KEY idx_reporter (reporter_id), - KEY idx_created (created_at) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_report); - - // ---------- 12. 用户限制/封禁 ---------- - $sql_restriction = "CREATE TABLE {$prefix}aksrt_user_restriction ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - user_id BIGINT UNSIGNED NOT NULL, - type VARCHAR(20) NOT NULL DEFAULT 'warn', - reason TEXT NOT NULL, - scope VARCHAR(20) NOT NULL DEFAULT 'global', - expires_at DATETIME DEFAULT NULL, - created_by BIGINT UNSIGNED NOT NULL DEFAULT 0, - created_at DATETIME NOT NULL, - PRIMARY KEY (id), - KEY idx_user (user_id), - KEY idx_type (type), - KEY idx_scope (scope), - KEY idx_expires (expires_at), - KEY idx_created (created_at) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_restriction); - - // ── follow ── - $sql_follow = "CREATE TABLE {$prefix}aksrt_follow ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - follower_id BIGINT UNSIGNED NOT NULL, - following_id BIGINT UNSIGNED NOT NULL, - created_at DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', - PRIMARY KEY (id), - UNIQUE KEY idx_pair (follower_id, following_id), - KEY idx_following (following_id), - KEY idx_created (created_at) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_follow); - - // ── coupon ── - $sql_coupon = "CREATE TABLE {$prefix}aksrt_coupon ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - code VARCHAR(60) NOT NULL DEFAULT '', - type VARCHAR(20) NOT NULL DEFAULT 'fixed', - value DECIMAL(10,2) NOT NULL DEFAULT 0, - min_amount DECIMAL(10,2) NOT NULL DEFAULT 0, - usage_limit INT UNSIGNED NOT NULL DEFAULT 0, - used_count INT UNSIGNED NOT NULL DEFAULT 0, - expires_at DATETIME DEFAULT NULL, - per_user_limit INT UNSIGNED NOT NULL DEFAULT 0, - product_scope TEXT DEFAULT NULL, - max_discount DECIMAL(10,2) NOT NULL DEFAULT 0, - status VARCHAR(20) NOT NULL DEFAULT 'active', - created_at DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', - PRIMARY KEY (id), - UNIQUE KEY idx_code (code), - KEY idx_status (status), - KEY idx_expires (expires_at) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_coupon); - - // ── coupon_usage (R68) ── - $sql_coupon_usage = "CREATE TABLE {$prefix}aksrt_coupon_usage ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - coupon_id BIGINT UNSIGNED NOT NULL DEFAULT 0, - user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, - order_number VARCHAR(32) NOT NULL DEFAULT '', - discount DECIMAL(10,2) NOT NULL DEFAULT 0, - created_at DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', - PRIMARY KEY (id), - KEY idx_coupon (coupon_id), - KEY idx_user (user_id), - UNIQUE KEY uniq_coupon_usage_order (order_number) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_coupon_usage); - - // ── typed charge cards ── - $sql_charge_card = "CREATE TABLE {$prefix}aksrt_charge_card ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - card VARCHAR(60) NOT NULL DEFAULT '', - password VARCHAR(80) NOT NULL DEFAULT '', - type VARCHAR(20) NOT NULL DEFAULT 'balance', - amount DECIMAL(10,2) NOT NULL DEFAULT 0, - balance DECIMAL(10,2) NOT NULL DEFAULT 0, - status VARCHAR(20) NOT NULL DEFAULT 'active', - meta LONGTEXT DEFAULT NULL, - order_number VARCHAR(32) NOT NULL DEFAULT '', - used_by BIGINT UNSIGNED DEFAULT NULL, - used_at DATETIME DEFAULT NULL, - expires_at DATETIME DEFAULT NULL, - created_at DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', - note TEXT DEFAULT NULL, - PRIMARY KEY (id), - UNIQUE KEY idx_password (password), - KEY idx_card (card), - KEY idx_type (type), - KEY idx_status (status), - KEY idx_used_by (used_by), - KEY idx_order_number (order_number), - KEY idx_expires (expires_at) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_charge_card); - - // ── one-time invite codes (registration gate + per-batch rewards) ── - $sql_invite_code = "CREATE TABLE {$prefix}aksrt_invite_code ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - code VARCHAR(32) NOT NULL, - batch_id VARCHAR(32) NOT NULL DEFAULT '', - remark VARCHAR(191) NOT NULL DEFAULT '', - rewards TEXT DEFAULT NULL, - max_uses INT UNSIGNED NOT NULL DEFAULT 1, - used_count INT UNSIGNED NOT NULL DEFAULT 0, - status VARCHAR(20) NOT NULL DEFAULT 'active', - expires_at DATETIME DEFAULT NULL, - created_by BIGINT UNSIGNED NOT NULL DEFAULT 0, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00', - PRIMARY KEY (id), - UNIQUE KEY uniq_invite_code (code), - KEY idx_batch (batch_id), - KEY idx_status (status), - KEY idx_expires (expires_at) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_invite_code); - - // ── invite-code usage log (who consumed which code) ── - $sql_invite_code_usage = "CREATE TABLE {$prefix}aksrt_invite_code_usage ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - code_id BIGINT UNSIGNED NOT NULL, - user_id BIGINT UNSIGNED NOT NULL, - used_at DATETIME NOT NULL, - PRIMARY KEY (id), - UNIQUE KEY uniq_invite_usage (code_id, user_id), - KEY idx_usage_user (user_id), - KEY idx_usage_code (code_id) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_invite_code_usage); - - // ── canonical Ref attribution and permanent user binding ── - $sql_affiliate_relation = "CREATE TABLE {$prefix}aksrt_affiliate_relation ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - referral_code VARCHAR(32) NOT NULL, - promoter_id BIGINT UNSIGNED NOT NULL, - invitee_id BIGINT UNSIGNED DEFAULT NULL, - first_touch_at DATETIME NOT NULL, - bound_at DATETIME DEFAULT NULL, - status VARCHAR(20) NOT NULL DEFAULT 'captured', - reward_status VARCHAR(20) NOT NULL DEFAULT 'skipped', - rewarded_at DATETIME DEFAULT NULL, - registration_ip VARCHAR(64) DEFAULT NULL, - created_at DATETIME NOT NULL, - PRIMARY KEY (id), - KEY idx_referral_code (referral_code), - UNIQUE KEY uniq_invitee (invitee_id), - KEY idx_promoter (promoter_id), - KEY idx_status_touch (status, first_touch_at), - KEY idx_reward_status (reward_status), - KEY idx_reward_ip (registration_ip, rewarded_at) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_affiliate_relation); - - // ── immutable commission ledger ── - // 佣金即时入账(无冻结/结算期):下单支付即 credit 到 COMMISSION 资金池, - // 本表仅作佣金明细账 + 退款冲正的溯源记录。status: credited | cancelled。 - $sql_commission_ledger = "CREATE TABLE {$prefix}aksrt_commission_ledger ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - promoter_id BIGINT UNSIGNED NOT NULL, - source_user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, - order_number VARCHAR(64) NOT NULL, - order_type VARCHAR(32) NOT NULL, - eligible_base DECIMAL(18,2) NOT NULL DEFAULT 0, - ratio DECIMAL(8,4) NOT NULL DEFAULT 0, - original_amount DECIMAL(18,2) NOT NULL DEFAULT 0, - adjustment_total DECIMAL(18,2) NOT NULL DEFAULT 0, - status VARCHAR(20) NOT NULL DEFAULT 'credited', - idempotency_key VARCHAR(128) NOT NULL, - cancel_operation_key VARCHAR(191) DEFAULT NULL, - cancel_payload_hash CHAR(64) DEFAULT NULL, - created_at DATETIME NOT NULL, - note TEXT DEFAULT NULL, - PRIMARY KEY (id), - UNIQUE KEY uniq_commission_idempotency (idempotency_key), - UNIQUE KEY uniq_commission_cancel_operation (cancel_operation_key), - KEY idx_promoter_status (promoter_id, status), - KEY idx_order_type (order_number, order_type), - KEY idx_created (created_at) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_commission_ledger); - - // 佣金欠款挂账:退款全额收回后,若佣金池不足产生的差额记为欠款, - // 后续新增佣金先抵扣欠款再入池。每用户一行。 - $sql_commission_debt = "CREATE TABLE {$prefix}aksrt_commission_debt ( - user_id BIGINT UNSIGNED NOT NULL, - debt_minor BIGINT UNSIGNED NOT NULL DEFAULT 0, - updated_at DATETIME NOT NULL, - PRIMARY KEY (user_id) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_commission_debt); - - // ── immutable commission adjustments (refund/debt/manual) ── - $sql_commission_adjustment = "CREATE TABLE {$prefix}aksrt_commission_adjustment ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - ledger_id BIGINT UNSIGNED NOT NULL, - promoter_id BIGINT UNSIGNED NOT NULL, - amount DECIMAL(18,2) NOT NULL DEFAULT 0, - reason VARCHAR(32) NOT NULL, - source_key VARCHAR(128) NOT NULL, - operator_id BIGINT UNSIGNED NOT NULL DEFAULT 0, - created_at DATETIME NOT NULL, - note TEXT DEFAULT NULL, - PRIMARY KEY (id), - UNIQUE KEY uniq_adjustment_source (source_key), - KEY idx_ledger (ledger_id), - KEY idx_promoter (promoter_id) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_commission_adjustment); - - // ── payout account snapshots ── - $sql_payout_account = "CREATE TABLE {$prefix}aksrt_payout_account ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - user_id BIGINT UNSIGNED NOT NULL, - channel VARCHAR(20) NOT NULL, - attachment_id BIGINT UNSIGNED NOT NULL DEFAULT 0, - account_hash CHAR(64) NOT NULL DEFAULT '', - status VARCHAR(20) NOT NULL DEFAULT 'active', - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - PRIMARY KEY (id), - UNIQUE KEY uniq_user_channel (user_id, channel), - KEY idx_user_status (user_id, status) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_payout_account); - - // ── order_items (§6.5 多商品多商家) ── - $sql_order_items = "CREATE TABLE {$prefix}aksrt_order_item ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - order_number VARCHAR(32) NOT NULL DEFAULT '', - product_id BIGINT UNSIGNED NOT NULL DEFAULT 0, - product_title VARCHAR(255) NOT NULL DEFAULT '', - options_key VARCHAR(255) NOT NULL DEFAULT '', - options_label VARCHAR(255) NOT NULL DEFAULT '', - quantity INT UNSIGNED NOT NULL DEFAULT 1, - unit_price DECIMAL(10,2) NOT NULL DEFAULT 0, - total_price DECIMAL(10,2) NOT NULL DEFAULT 0, - discount_amount DECIMAL(10,2) NOT NULL DEFAULT 0, - shipping_fee DECIMAL(10,2) NOT NULL DEFAULT 0, - pay_price DECIMAL(10,2) NOT NULL DEFAULT 0, - shipping_type VARCHAR(20) NOT NULL DEFAULT 'express', - auto_delivery_content TEXT DEFAULT NULL, - discount_hit TEXT DEFAULT NULL, - gift_data TEXT DEFAULT NULL, - image VARCHAR(512) NOT NULL DEFAULT '', - PRIMARY KEY (id), - KEY idx_order (order_number), - KEY idx_product (product_id) - ) ENGINE=InnoDB {$charset_collate};"; - dbDelta($sql_order_items); - - self::migrate_identity_tables($prefix, $charset_collate); - self::migrate_vip_tables($prefix, $charset_collate); - } -} +get_charset_collate(); + $prefix = $wpdb->prefix; + + // ---------- 1. 统一资产账户与不可变账本 ---------- + $sql_asset_account = "CREATE TABLE {$prefix}aksrt_asset_account ( + user_id BIGINT UNSIGNED NOT NULL, + asset VARCHAR(16) NOT NULL, + balance_minor BIGINT UNSIGNED NOT NULL DEFAULT 0, + version BIGINT UNSIGNED NOT NULL DEFAULT 0, + updated_at DATETIME(6) NOT NULL, + PRIMARY KEY (user_id, asset), + KEY idx_asset_balance (asset, balance_minor) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_asset_account); + + $sql_asset_transaction = "CREATE TABLE {$prefix}aksrt_asset_transaction ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + operation_key VARCHAR(191) NOT NULL, + payload_hash CHAR(64) NOT NULL, + reason VARCHAR(64) NOT NULL, + ref_type VARCHAR(32) DEFAULT NULL, + ref_key VARCHAR(191) DEFAULT NULL, + actor_user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, + metadata LONGTEXT DEFAULT NULL, + ip VARCHAR(64) DEFAULT NULL, + ua VARCHAR(255) DEFAULT NULL, + created_at DATETIME(6) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY uniq_operation_key (operation_key), + KEY idx_reason_time (reason, created_at), + KEY idx_reference (ref_type, ref_key) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_asset_transaction); + + $sql_asset_entry = "CREATE TABLE {$prefix}aksrt_asset_entry ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + transaction_id BIGINT UNSIGNED NOT NULL, + line_no SMALLINT UNSIGNED NOT NULL, + user_id BIGINT UNSIGNED NOT NULL, + asset VARCHAR(16) NOT NULL, + entry_type VARCHAR(32) NOT NULL, + delta_minor BIGINT NOT NULL, + balance_after_minor BIGINT UNSIGNED NOT NULL, + counterparty_user_id BIGINT UNSIGNED DEFAULT NULL, + remark VARCHAR(255) DEFAULT NULL, + created_at DATETIME(6) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY uniq_transaction_line (transaction_id, line_no), + KEY idx_user_asset_time (user_id, asset, created_at, id), + KEY idx_transaction (transaction_id) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_asset_entry); + + // 后台「资金流水」页的默认查询是 WHERE 1=1 ORDER BY created_at DESC, id DESC LIMIT n + // 外加同条件的 COUNT(*),且界面上的日期区间筛选也只有 created_at 可用。 + // 索引必须能以 created_at 为前导列,否则每次翻页都是全表扫描 + filesort, + // 而 idx_flow_event_time 的前导列是 event_type(筛选时可空),救不了这两条查询。 + $sql_financial_flow = "CREATE TABLE {$prefix}aksrt_financial_flow ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + flow_number VARCHAR(32) NOT NULL, + operation_key VARCHAR(191) NOT NULL, + event_type VARCHAR(64) NOT NULL, + ref_type VARCHAR(32) DEFAULT NULL, + ref_key VARCHAR(191) DEFAULT NULL, + order_number VARCHAR(32) DEFAULT NULL, + withdrawal_number VARCHAR(32) DEFAULT NULL, + ledger_transaction_id BIGINT UNSIGNED DEFAULT NULL, + provider_transaction_id VARCHAR(128) DEFAULT NULL, + metadata LONGTEXT DEFAULT NULL, + created_at DATETIME(6) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY uniq_flow_number (flow_number), + UNIQUE KEY uniq_flow_operation (operation_key), + UNIQUE KEY uniq_flow_ledger_transaction (ledger_transaction_id), + KEY idx_flow_created (created_at), + KEY idx_flow_event_time (event_type, created_at), + KEY idx_flow_order (order_number), + KEY idx_flow_withdrawal (withdrawal_number), + KEY idx_flow_reference (ref_type, ref_key), + KEY idx_flow_provider_transaction (provider_transaction_id) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_financial_flow); + + $sql_financial_flow_entry = "CREATE TABLE {$prefix}aksrt_financial_flow_entry ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + flow_id BIGINT UNSIGNED NOT NULL, + line_no SMALLINT UNSIGNED NOT NULL, + user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, + asset VARCHAR(16) NOT NULL, + direction VARCHAR(8) NOT NULL, + entry_type VARCHAR(32) NOT NULL, + delta_minor BIGINT NOT NULL, + balance_after_minor BIGINT UNSIGNED DEFAULT NULL, + counterparty_user_id BIGINT UNSIGNED DEFAULT NULL, + remark VARCHAR(255) DEFAULT NULL, + PRIMARY KEY (id), + UNIQUE KEY uniq_flow_line (flow_id, line_no), + KEY idx_flow_entry_user (user_id, flow_id), + KEY idx_flow_entry_asset (asset, flow_id), + KEY idx_flow_entry_direction (direction, flow_id), + KEY idx_flow_entry_counterparty (counterparty_user_id, flow_id) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_financial_flow_entry); + + // ---------- 2. 签到 ---------- + $sql_signin = "CREATE TABLE {$prefix}aksrt_signin_log ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + user_id BIGINT UNSIGNED NOT NULL, + signin_date DATE NOT NULL, + streak INT NOT NULL DEFAULT 1, + reward INT NOT NULL DEFAULT 0, + PRIMARY KEY (id), + UNIQUE KEY uniq_user_date (user_id, signin_date), + KEY idx_user (user_id) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_signin); + + // ---------- 3. 下载 token ---------- + $sql_dl_token = "CREATE TABLE {$prefix}aksrt_dl_token ( + token CHAR(48) NOT NULL, + user_id BIGINT UNSIGNED NOT NULL, + post_id BIGINT UNSIGNED NOT NULL, + source_index INT NOT NULL DEFAULT 0, + cost INT NOT NULL DEFAULT 0, + ip_bind VARCHAR(64) DEFAULT NULL, + ua_hash CHAR(40) DEFAULT NULL, + used_count INT NOT NULL DEFAULT 0, + max_uses INT NOT NULL DEFAULT 1, + expires_at DATETIME NOT NULL, + created_at DATETIME NOT NULL, + PRIMARY KEY (token), + KEY idx_expires (expires_at), + KEY idx_user (user_id), + KEY idx_post (post_id) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_dl_token); + + // ---------- 5. 用户通知收件箱 ---------- + $sql_notification = "CREATE TABLE {$prefix}aksrt_notification ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + user_id BIGINT UNSIGNED NOT NULL, + type VARCHAR(32) NOT NULL, + title VARCHAR(191) NOT NULL, + description TEXT DEFAULT NULL, + target_url TEXT DEFAULT NULL, + is_read TINYINT(1) NOT NULL DEFAULT 0, + created_at DATETIME NOT NULL, + read_at DATETIME DEFAULT NULL, + PRIMARY KEY (id), + KEY idx_user_created (user_id, created_at), + KEY idx_user_read (user_id, is_read) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_notification); + + // ---------- 8. 订单表 ---------- + $sql_order = "CREATE TABLE {$prefix}aksrt_order ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + order_number VARCHAR(32) NOT NULL, + request_key VARCHAR(191) DEFAULT NULL, + user_id BIGINT UNSIGNED NOT NULL, + product_type VARCHAR(32) NOT NULL DEFAULT '', + product_id BIGINT UNSIGNED NOT NULL DEFAULT 0, + amount DECIMAL(10,2) NOT NULL DEFAULT 0, + currency VARCHAR(8) NOT NULL DEFAULT 'CNY', + status VARCHAR(20) NOT NULL DEFAULT 'pending', + shipping_status TINYINT NOT NULL DEFAULT 0, + payment_method VARCHAR(32) NOT NULL DEFAULT '', + payment_transaction_id VARCHAR(128) NOT NULL DEFAULT '', + created_at DATETIME NOT NULL, + paid_at DATETIME DEFAULT NULL, + delivered_at DATETIME DEFAULT NULL, + completed_at DATETIME DEFAULT NULL, + cancelled_at DATETIME DEFAULT NULL, + refunded_at DATETIME DEFAULT NULL, + expires_at DATETIME DEFAULT NULL, + note TEXT DEFAULT NULL, + PRIMARY KEY (id), + UNIQUE KEY uniq_order_number (order_number), + UNIQUE KEY uniq_request_key (request_key), + KEY idx_user (user_id), + KEY idx_user_status_created (user_id, status, created_at), + KEY idx_status (status), + KEY idx_created (created_at), + KEY idx_product (product_type, product_id) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_order); + + // ---------- order after-sales ---------- + $sql_order_aftersale = "CREATE TABLE {$prefix}aksrt_order_aftersale ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + order_number VARCHAR(32) NOT NULL, + user_id BIGINT UNSIGNED NOT NULL, + aftersale_type VARCHAR(24) NOT NULL DEFAULT 'refund', + reason TEXT NOT NULL, + apply_form LONGTEXT DEFAULT NULL, + images LONGTEXT DEFAULT NULL, + refund_amount VARCHAR(32) NOT NULL DEFAULT '0', + status VARCHAR(20) NOT NULL DEFAULT 'pending', + admin_note TEXT DEFAULT NULL, + gateway_request_id VARCHAR(128) DEFAULT NULL, + gateway_refund_id VARCHAR(128) DEFAULT NULL, + gateway_result LONGTEXT DEFAULT NULL, + created_at DATETIME NOT NULL, + reviewed_at DATETIME DEFAULT NULL, + completed_at DATETIME DEFAULT NULL, + PRIMARY KEY (id), + KEY idx_order_status (order_number, status), + KEY idx_user_created (user_id, created_at), + KEY idx_status_created (status, created_at) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_order_aftersale); + + // ---------- 9. 作者收益台账 ---------- + $sql_revenue = "CREATE TABLE {$prefix}aksrt_revenue ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + author_id BIGINT UNSIGNED NOT NULL DEFAULT 0, + order_number VARCHAR(32) NOT NULL DEFAULT '', + post_id BIGINT UNSIGNED NOT NULL DEFAULT 0, + buyer_id BIGINT UNSIGNED NOT NULL DEFAULT 0, + currency VARCHAR(8) NOT NULL DEFAULT 'CNY', + original_amount DECIMAL(18,2) NOT NULL DEFAULT 0, + share_ratio DECIMAL(5,2) NOT NULL DEFAULT 0, + author_level TINYINT UNSIGNED NOT NULL DEFAULT 0, + amount DECIMAL(18,2) NOT NULL DEFAULT 0, + type VARCHAR(32) NOT NULL DEFAULT 'order', + status VARCHAR(20) NOT NULL DEFAULT 'pending', + reversal_amount DECIMAL(18,2) NOT NULL DEFAULT 0, + error_message TEXT DEFAULT NULL, + audit_json LONGTEXT DEFAULT NULL, + created_at DATETIME NOT NULL, + settled_at DATETIME DEFAULT NULL, + reversed_at DATETIME DEFAULT NULL, + PRIMARY KEY (id), + UNIQUE KEY uniq_creator_order (author_id, order_number), + KEY idx_author (author_id), + KEY idx_order (order_number), + KEY idx_post (post_id), + KEY idx_buyer (buyer_id), + KEY idx_currency (currency), + KEY idx_status (status), + KEY idx_created (created_at) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_revenue); + + // ---------- 10. 提现台账 ---------- + $sql_withdrawal = "CREATE TABLE {$prefix}aksrt_withdrawal ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + withdrawal_number VARCHAR(32) NOT NULL, + user_id BIGINT UNSIGNED NOT NULL, + amount DECIMAL(10,2) NOT NULL DEFAULT 0, + gross_amount DECIMAL(10,2) NOT NULL DEFAULT 0, + method VARCHAR(32) NOT NULL DEFAULT '', + account_info TEXT DEFAULT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'pending', + operation_key VARCHAR(191) DEFAULT NULL, + ledger_transaction_id BIGINT UNSIGNED DEFAULT NULL, + created_at DATETIME NOT NULL, + processed_at DATETIME DEFAULT NULL, + note TEXT DEFAULT NULL, + PRIMARY KEY (id), + UNIQUE KEY uniq_withdrawal_number (withdrawal_number), + UNIQUE KEY uniq_withdrawal_operation (operation_key), + UNIQUE KEY uniq_ledger_transaction (ledger_transaction_id), + KEY idx_user (user_id), + KEY idx_user_created (user_id, created_at), + KEY idx_status (status), + KEY idx_created (created_at) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_withdrawal); + + // ---------- 11. 举报工单 ---------- + $sql_report = "CREATE TABLE {$prefix}aksrt_report ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + reporter_id BIGINT UNSIGNED NOT NULL, + target_type VARCHAR(20) NOT NULL, + target_id BIGINT UNSIGNED NOT NULL, + reason TEXT NOT NULL, + category VARCHAR(40) NOT NULL DEFAULT '', + status VARCHAR(20) NOT NULL DEFAULT 'pending', + created_at DATETIME NOT NULL, + updated_at DATETIME DEFAULT NULL, + notes_json TEXT DEFAULT NULL, + PRIMARY KEY (id), + KEY idx_status (status), + KEY idx_target (target_type, target_id), + KEY idx_reporter (reporter_id), + KEY idx_created (created_at) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_report); + + // ---------- 12. 用户限制/封禁 ---------- + $sql_restriction = "CREATE TABLE {$prefix}aksrt_user_restriction ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + user_id BIGINT UNSIGNED NOT NULL, + type VARCHAR(20) NOT NULL DEFAULT 'warn', + reason TEXT NOT NULL, + scope VARCHAR(20) NOT NULL DEFAULT 'global', + expires_at DATETIME DEFAULT NULL, + created_by BIGINT UNSIGNED NOT NULL DEFAULT 0, + created_at DATETIME NOT NULL, + PRIMARY KEY (id), + KEY idx_user (user_id), + KEY idx_type (type), + KEY idx_scope (scope), + KEY idx_expires (expires_at), + KEY idx_created (created_at) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_restriction); + + // ── follow ── + $sql_follow = "CREATE TABLE {$prefix}aksrt_follow ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + follower_id BIGINT UNSIGNED NOT NULL, + following_id BIGINT UNSIGNED NOT NULL, + created_at DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + PRIMARY KEY (id), + UNIQUE KEY idx_pair (follower_id, following_id), + KEY idx_following (following_id), + KEY idx_created (created_at) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_follow); + + // ── coupon ── + $sql_coupon = "CREATE TABLE {$prefix}aksrt_coupon ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + code VARCHAR(60) NOT NULL DEFAULT '', + type VARCHAR(20) NOT NULL DEFAULT 'fixed', + value DECIMAL(10,2) NOT NULL DEFAULT 0, + min_amount DECIMAL(10,2) NOT NULL DEFAULT 0, + usage_limit INT UNSIGNED NOT NULL DEFAULT 0, + used_count INT UNSIGNED NOT NULL DEFAULT 0, + expires_at DATETIME DEFAULT NULL, + per_user_limit INT UNSIGNED NOT NULL DEFAULT 0, + product_scope TEXT DEFAULT NULL, + max_discount DECIMAL(10,2) NOT NULL DEFAULT 0, + status VARCHAR(20) NOT NULL DEFAULT 'active', + created_at DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + PRIMARY KEY (id), + UNIQUE KEY idx_code (code), + KEY idx_status (status), + KEY idx_expires (expires_at) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_coupon); + + // ── coupon_usage (R68) ── + $sql_coupon_usage = "CREATE TABLE {$prefix}aksrt_coupon_usage ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + coupon_id BIGINT UNSIGNED NOT NULL DEFAULT 0, + user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, + order_number VARCHAR(32) NOT NULL DEFAULT '', + discount DECIMAL(10,2) NOT NULL DEFAULT 0, + created_at DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + PRIMARY KEY (id), + KEY idx_coupon (coupon_id), + KEY idx_user (user_id), + UNIQUE KEY uniq_coupon_usage_order (order_number) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_coupon_usage); + + // ── typed charge cards ── + $sql_charge_card = "CREATE TABLE {$prefix}aksrt_charge_card ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + card VARCHAR(60) NOT NULL DEFAULT '', + password VARCHAR(80) NOT NULL DEFAULT '', + type VARCHAR(20) NOT NULL DEFAULT 'balance', + amount DECIMAL(10,2) NOT NULL DEFAULT 0, + balance DECIMAL(10,2) NOT NULL DEFAULT 0, + status VARCHAR(20) NOT NULL DEFAULT 'active', + meta LONGTEXT DEFAULT NULL, + order_number VARCHAR(32) NOT NULL DEFAULT '', + used_by BIGINT UNSIGNED DEFAULT NULL, + used_at DATETIME DEFAULT NULL, + expires_at DATETIME DEFAULT NULL, + created_at DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + note TEXT DEFAULT NULL, + PRIMARY KEY (id), + UNIQUE KEY idx_password (password), + KEY idx_card (card), + KEY idx_type (type), + KEY idx_status (status), + KEY idx_used_by (used_by), + KEY idx_order_number (order_number), + KEY idx_expires (expires_at) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_charge_card); + + // ── one-time invite codes (registration gate + per-batch rewards) ── + $sql_invite_code = "CREATE TABLE {$prefix}aksrt_invite_code ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + code VARCHAR(32) NOT NULL, + batch_id VARCHAR(32) NOT NULL DEFAULT '', + remark VARCHAR(191) NOT NULL DEFAULT '', + rewards TEXT DEFAULT NULL, + max_uses INT UNSIGNED NOT NULL DEFAULT 1, + used_count INT UNSIGNED NOT NULL DEFAULT 0, + status VARCHAR(20) NOT NULL DEFAULT 'active', + expires_at DATETIME DEFAULT NULL, + created_by BIGINT UNSIGNED NOT NULL DEFAULT 0, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00', + PRIMARY KEY (id), + UNIQUE KEY uniq_invite_code (code), + KEY idx_batch (batch_id), + KEY idx_status (status), + KEY idx_expires (expires_at) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_invite_code); + + // ── invite-code usage log (who consumed which code) ── + $sql_invite_code_usage = "CREATE TABLE {$prefix}aksrt_invite_code_usage ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + code_id BIGINT UNSIGNED NOT NULL, + user_id BIGINT UNSIGNED NOT NULL, + used_at DATETIME NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY uniq_invite_usage (code_id, user_id), + KEY idx_usage_user (user_id), + KEY idx_usage_code (code_id) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_invite_code_usage); + + // ── canonical Ref attribution and permanent user binding ── + $sql_affiliate_relation = "CREATE TABLE {$prefix}aksrt_affiliate_relation ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + referral_code VARCHAR(32) NOT NULL, + promoter_id BIGINT UNSIGNED NOT NULL, + invitee_id BIGINT UNSIGNED DEFAULT NULL, + first_touch_at DATETIME NOT NULL, + bound_at DATETIME DEFAULT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'captured', + reward_status VARCHAR(20) NOT NULL DEFAULT 'skipped', + rewarded_at DATETIME DEFAULT NULL, + registration_ip VARCHAR(64) DEFAULT NULL, + created_at DATETIME NOT NULL, + PRIMARY KEY (id), + KEY idx_referral_code (referral_code), + UNIQUE KEY uniq_invitee (invitee_id), + KEY idx_promoter (promoter_id), + KEY idx_status_touch (status, first_touch_at), + KEY idx_reward_status (reward_status), + KEY idx_reward_ip (registration_ip, rewarded_at) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_affiliate_relation); + + // ── immutable commission ledger ── + // 佣金即时入账(无冻结/结算期):下单支付即 credit 到 COMMISSION 资金池, + // 本表仅作佣金明细账 + 退款冲正的溯源记录。status: credited | cancelled。 + $sql_commission_ledger = "CREATE TABLE {$prefix}aksrt_commission_ledger ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + promoter_id BIGINT UNSIGNED NOT NULL, + source_user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, + order_number VARCHAR(64) NOT NULL, + order_type VARCHAR(32) NOT NULL, + eligible_base DECIMAL(18,2) NOT NULL DEFAULT 0, + ratio DECIMAL(8,4) NOT NULL DEFAULT 0, + original_amount DECIMAL(18,2) NOT NULL DEFAULT 0, + adjustment_total DECIMAL(18,2) NOT NULL DEFAULT 0, + status VARCHAR(20) NOT NULL DEFAULT 'credited', + idempotency_key VARCHAR(128) NOT NULL, + cancel_operation_key VARCHAR(191) DEFAULT NULL, + cancel_payload_hash CHAR(64) DEFAULT NULL, + created_at DATETIME NOT NULL, + note TEXT DEFAULT NULL, + PRIMARY KEY (id), + UNIQUE KEY uniq_commission_idempotency (idempotency_key), + UNIQUE KEY uniq_commission_cancel_operation (cancel_operation_key), + KEY idx_promoter_status (promoter_id, status), + KEY idx_order_type (order_number, order_type), + KEY idx_created (created_at) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_commission_ledger); + + // 佣金欠款挂账:退款全额收回后,若佣金池不足产生的差额记为欠款, + // 后续新增佣金先抵扣欠款再入池。每用户一行。 + $sql_commission_debt = "CREATE TABLE {$prefix}aksrt_commission_debt ( + user_id BIGINT UNSIGNED NOT NULL, + debt_minor BIGINT UNSIGNED NOT NULL DEFAULT 0, + updated_at DATETIME NOT NULL, + PRIMARY KEY (user_id) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_commission_debt); + + // ── immutable commission adjustments (refund/debt/manual) ── + $sql_commission_adjustment = "CREATE TABLE {$prefix}aksrt_commission_adjustment ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + ledger_id BIGINT UNSIGNED NOT NULL, + promoter_id BIGINT UNSIGNED NOT NULL, + amount DECIMAL(18,2) NOT NULL DEFAULT 0, + reason VARCHAR(32) NOT NULL, + source_key VARCHAR(128) NOT NULL, + operator_id BIGINT UNSIGNED NOT NULL DEFAULT 0, + created_at DATETIME NOT NULL, + note TEXT DEFAULT NULL, + PRIMARY KEY (id), + UNIQUE KEY uniq_adjustment_source (source_key), + KEY idx_ledger (ledger_id), + KEY idx_promoter (promoter_id) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_commission_adjustment); + + // ── payout account snapshots ── + $sql_payout_account = "CREATE TABLE {$prefix}aksrt_payout_account ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + user_id BIGINT UNSIGNED NOT NULL, + channel VARCHAR(20) NOT NULL, + attachment_id BIGINT UNSIGNED NOT NULL DEFAULT 0, + account_hash CHAR(64) NOT NULL DEFAULT '', + status VARCHAR(20) NOT NULL DEFAULT 'active', + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY uniq_user_channel (user_id, channel), + KEY idx_user_status (user_id, status) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_payout_account); + + // ── order_items (§6.5 多商品多商家) ── + $sql_order_items = "CREATE TABLE {$prefix}aksrt_order_item ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + order_number VARCHAR(32) NOT NULL DEFAULT '', + product_id BIGINT UNSIGNED NOT NULL DEFAULT 0, + product_title VARCHAR(255) NOT NULL DEFAULT '', + options_key VARCHAR(255) NOT NULL DEFAULT '', + options_label VARCHAR(255) NOT NULL DEFAULT '', + quantity INT UNSIGNED NOT NULL DEFAULT 1, + unit_price DECIMAL(10,2) NOT NULL DEFAULT 0, + total_price DECIMAL(10,2) NOT NULL DEFAULT 0, + discount_amount DECIMAL(10,2) NOT NULL DEFAULT 0, + shipping_fee DECIMAL(10,2) NOT NULL DEFAULT 0, + pay_price DECIMAL(10,2) NOT NULL DEFAULT 0, + shipping_type VARCHAR(20) NOT NULL DEFAULT 'express', + auto_delivery_content TEXT DEFAULT NULL, + discount_hit TEXT DEFAULT NULL, + gift_data TEXT DEFAULT NULL, + image VARCHAR(512) NOT NULL DEFAULT '', + PRIMARY KEY (id), + KEY idx_order (order_number), + KEY idx_product (product_id) + ) ENGINE=InnoDB {$charset_collate};"; + dbDelta($sql_order_items); + + self::migrate_identity_tables($prefix, $charset_collate); + self::migrate_vip_tables($prefix, $charset_collate); + } +}