222 lines
11 KiB
SQL
222 lines
11 KiB
SQL
-- Yumi Game King: canonical GAME_CONSUME_GOLD -> draw chances, deterministic ranking and settlement.
|
|
--
|
|
-- Performance review:
|
|
-- 1. The event path first resolves the very small set of enabled activities by
|
|
-- (sys_origin, enabled, start_time, end_time), then inserts a unique activity/event ledger row
|
|
-- and updates one aggregate primary key. It never scans wallet shards or arbitrary expenses.
|
|
-- 2. Ranking reads the materialized aggregate through
|
|
-- (activity_id, total_consumed DESC, consume_reached_time ASC, user_id ASC); settlement freezes
|
|
-- only Top 30 and does not GROUP BY the append-only ledger.
|
|
-- 3. Draw idempotency, settlement idempotency, finite-stock reservation and reward delivery claims
|
|
-- all use primary/unique keys. Unlimited stock (-1) is not updated and therefore avoids a hot row.
|
|
-- 4. Backfill binds sys_origin + GAME_CONSUME_GOLD and keysets by (occurred_at,id) through the added
|
|
-- index. The public lastId is resolved to its scoped occurred_at before each page, preserving the
|
|
-- lastId-only contract without sorting/rescanning the whole activity window.
|
|
-- 5. The activity tables are empty, but the backfill index targets the existing production
|
|
-- task_center_event_archive_outbox (about 35 million rows / 50 GiB at review time). Treat that ALTER
|
|
-- as an independent online DDL: run it before the application rollout, monitor metadata-lock waits,
|
|
-- temporary disk usage and replica lag until completion, and do not hide it inside a service restart.
|
|
-- 6. The ALTER explicitly requires ALGORITHM=INPLACE and LOCK=NONE. If the target MySQL version or table
|
|
-- state cannot honor those guarantees, the migration must fail instead of silently falling back to a
|
|
-- table-copy or write-blocking algorithm. It performs no UPDATE/DELETE/backfill.
|
|
-- 7. The one-row-per-sysOrigin scope lock is touched only by admin enable/save transactions; it keeps
|
|
-- concurrent overlap checks correct without serializing the high-volume game event path.
|
|
|
|
SET NAMES utf8mb4;
|
|
|
|
CREATE TABLE IF NOT EXISTS `yumi_game_king_activity` (
|
|
`id` BIGINT NOT NULL,
|
|
`activity_code` VARCHAR(64) NOT NULL,
|
|
`activity_name` VARCHAR(128) NOT NULL,
|
|
`activity_desc` VARCHAR(1000) NOT NULL DEFAULT '',
|
|
`sys_origin` VARCHAR(32) NOT NULL,
|
|
`time_zone` VARCHAR(64) NOT NULL DEFAULT 'Asia/Riyadh',
|
|
`start_time` DATETIME(3) NOT NULL,
|
|
`end_time` DATETIME(3) NOT NULL,
|
|
`settlement_delay_minutes` INT NOT NULL DEFAULT 30,
|
|
`settlement_time` DATETIME(3) NOT NULL,
|
|
`coin_per_draw` BIGINT NOT NULL,
|
|
`ranking_type` VARCHAR(32) NOT NULL DEFAULT 'TYCOON',
|
|
`ranking_period` VARCHAR(32) NOT NULL DEFAULT 'OVERALL',
|
|
`enabled` TINYINT(1) NOT NULL DEFAULT 0,
|
|
`settlement_status` VARCHAR(32) NOT NULL DEFAULT 'NOT_STARTED',
|
|
`create_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
`update_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
|
PRIMARY KEY (`id`),
|
|
UNIQUE KEY `uk_yumi_gk_activity_code` (`activity_code`),
|
|
KEY `idx_yumi_gk_activity_window` (`sys_origin`, `enabled`, `start_time`, `end_time`),
|
|
KEY `idx_yumi_gk_activity_settle` (`settlement_status`, `settlement_time`, `end_time`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='Yumi Game King activity';
|
|
|
|
CREATE TABLE IF NOT EXISTS `yumi_game_king_scope_lock` (
|
|
`sys_origin` VARCHAR(32) NOT NULL,
|
|
`update_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
|
PRIMARY KEY (`sys_origin`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='Serializes enabled-window checks per Yumi tenant';
|
|
|
|
CREATE TABLE IF NOT EXISTS `yumi_game_king_prize` (
|
|
`id` BIGINT NOT NULL,
|
|
`activity_id` BIGINT NOT NULL,
|
|
`prize_name` VARCHAR(128) NOT NULL,
|
|
`prize_image` VARCHAR(500) NOT NULL DEFAULT '',
|
|
`weight` BIGINT NOT NULL,
|
|
`stock` BIGINT NOT NULL DEFAULT -1 COMMENT '-1 unlimited, 0 depleted, positive remaining',
|
|
`resource_group_id` BIGINT NOT NULL,
|
|
`reward_group_name` VARCHAR(128) NOT NULL DEFAULT '',
|
|
`reward_items_json` TEXT NOT NULL,
|
|
`enabled` TINYINT(1) NOT NULL DEFAULT 1,
|
|
`sort_order` TINYINT NOT NULL,
|
|
`create_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
`update_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
|
PRIMARY KEY (`id`),
|
|
UNIQUE KEY `uk_yumi_gk_prize_position` (`activity_id`, `sort_order`),
|
|
KEY `idx_yumi_gk_prize_enabled` (`activity_id`, `enabled`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='Seven fixed Yumi Game King prizes';
|
|
|
|
CREATE TABLE IF NOT EXISTS `yumi_game_king_rank_reward` (
|
|
`id` BIGINT NOT NULL,
|
|
`activity_id` BIGINT NOT NULL,
|
|
`start_rank` SMALLINT NOT NULL,
|
|
`end_rank` SMALLINT NOT NULL,
|
|
`resource_group_id` BIGINT NOT NULL,
|
|
`reward_name` VARCHAR(128) NOT NULL DEFAULT '',
|
|
`reward_items_json` TEXT NOT NULL,
|
|
`create_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
`update_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
|
PRIMARY KEY (`id`),
|
|
UNIQUE KEY `uk_yumi_gk_rank_range` (`activity_id`, `start_rank`, `end_rank`),
|
|
KEY `idx_yumi_gk_rank_start` (`activity_id`, `start_rank`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='Six fixed end-settlement rank reward ranges';
|
|
|
|
CREATE TABLE IF NOT EXISTS `yumi_game_king_consume_ledger` (
|
|
`id` BIGINT NOT NULL,
|
|
`activity_id` BIGINT NOT NULL,
|
|
`event_id` VARCHAR(128) NOT NULL,
|
|
`user_id` BIGINT NOT NULL,
|
|
`sys_origin` VARCHAR(32) NOT NULL,
|
|
`amount` BIGINT NOT NULL,
|
|
`event_time` DATETIME(3) NOT NULL,
|
|
`payload_json` TEXT NOT NULL,
|
|
`create_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
PRIMARY KEY (`id`),
|
|
UNIQUE KEY `uk_yumi_gk_ledger_event` (`activity_id`, `event_id`),
|
|
KEY `idx_yumi_gk_ledger_user` (`activity_id`, `user_id`, `event_time`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='Idempotent canonical game-consumption ledger';
|
|
|
|
CREATE TABLE IF NOT EXISTS `yumi_game_king_user` (
|
|
`activity_id` BIGINT NOT NULL,
|
|
`user_id` BIGINT NOT NULL,
|
|
`total_consumed` BIGINT NOT NULL DEFAULT 0,
|
|
`used_chances` BIGINT NOT NULL DEFAULT 0,
|
|
`consume_reached_time` DATETIME(3) NOT NULL,
|
|
`create_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
`update_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
|
PRIMARY KEY (`activity_id`, `user_id`),
|
|
KEY `idx_yumi_gk_user_rank` (
|
|
`activity_id`, `total_consumed` DESC, `consume_reached_time` ASC, `user_id` ASC
|
|
)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='Overall game-consumption aggregate and draw chance usage';
|
|
|
|
CREATE TABLE IF NOT EXISTS `yumi_game_king_user_daily` (
|
|
`activity_id` BIGINT NOT NULL,
|
|
`stat_date` DATE NOT NULL,
|
|
`user_id` BIGINT NOT NULL,
|
|
`total_consumed` BIGINT NOT NULL DEFAULT 0,
|
|
`consume_reached_time` DATETIME(3) NOT NULL,
|
|
`create_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
`update_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
|
PRIMARY KEY (`activity_id`, `stat_date`, `user_id`),
|
|
KEY `idx_yumi_gk_daily_rank` (
|
|
`activity_id`, `stat_date`, `total_consumed` DESC, `consume_reached_time` ASC, `user_id` ASC
|
|
)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='Activity-timezone daily game-consumption aggregate';
|
|
|
|
CREATE TABLE IF NOT EXISTS `yumi_game_king_draw_record` (
|
|
`id` BIGINT NOT NULL,
|
|
`activity_id` BIGINT NOT NULL,
|
|
`user_id` BIGINT NOT NULL,
|
|
`request_id` VARCHAR(64) NOT NULL,
|
|
`prize_id` BIGINT NOT NULL,
|
|
`prize_name` VARCHAR(128) NOT NULL,
|
|
`prize_image` VARCHAR(500) NOT NULL DEFAULT '',
|
|
`resource_group_id` BIGINT NOT NULL,
|
|
`delivery_status` VARCHAR(32) NOT NULL,
|
|
`retry_count` INT NOT NULL DEFAULT 0,
|
|
`failure_reason` VARCHAR(500) NOT NULL DEFAULT '',
|
|
`draw_time` DATETIME(3) NOT NULL,
|
|
`deliver_time` DATETIME(3) NULL,
|
|
`create_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
`update_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
|
PRIMARY KEY (`id`),
|
|
UNIQUE KEY `uk_yumi_gk_draw_request` (`activity_id`, `user_id`, `request_id`),
|
|
KEY `idx_yumi_gk_draw_user` (`activity_id`, `user_id`, `create_time`),
|
|
KEY `idx_yumi_gk_draw_manage` (`activity_id`, `delivery_status`, `id`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='Idempotent Yumi Game King draw records';
|
|
|
|
CREATE TABLE IF NOT EXISTS `yumi_game_king_settlement_record` (
|
|
`id` BIGINT NOT NULL,
|
|
`activity_id` BIGINT NOT NULL,
|
|
`user_id` BIGINT NOT NULL,
|
|
`rank_no` SMALLINT NOT NULL,
|
|
`total_consumed` BIGINT NOT NULL,
|
|
`rank_reward_id` BIGINT NOT NULL,
|
|
`resource_group_id` BIGINT NOT NULL,
|
|
`business_no` VARCHAR(128) NOT NULL,
|
|
`delivery_status` VARCHAR(32) NOT NULL,
|
|
`retry_count` INT NOT NULL DEFAULT 0,
|
|
`failure_reason` VARCHAR(500) NOT NULL DEFAULT '',
|
|
`deliver_time` DATETIME(3) NULL,
|
|
`create_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
`update_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
|
PRIMARY KEY (`id`),
|
|
UNIQUE KEY `uk_yumi_gk_settle_user` (`activity_id`, `user_id`),
|
|
UNIQUE KEY `uk_yumi_gk_settle_rank` (`activity_id`, `rank_no`),
|
|
UNIQUE KEY `uk_yumi_gk_settle_business` (`business_no`),
|
|
KEY `idx_yumi_gk_settle_status` (`activity_id`, `delivery_status`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='Frozen deterministic Top30 settlement records';
|
|
|
|
CREATE TABLE IF NOT EXISTS `yumi_game_king_delivery_item` (
|
|
`id` BIGINT NOT NULL,
|
|
`owner_type` VARCHAR(20) NOT NULL COMMENT 'DRAW or SETTLEMENT',
|
|
`owner_id` BIGINT NOT NULL,
|
|
`activity_id` BIGINT NOT NULL,
|
|
`user_id` BIGINT NOT NULL,
|
|
`resource_group_id` BIGINT NOT NULL,
|
|
`delivery_status` VARCHAR(32) NOT NULL,
|
|
`retry_count` INT NOT NULL DEFAULT 0,
|
|
`failure_reason` VARCHAR(500) NOT NULL DEFAULT '',
|
|
`deliver_time` DATETIME(3) NULL,
|
|
`create_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
`update_time` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
|
PRIMARY KEY (`id`),
|
|
UNIQUE KEY `uk_yumi_gk_delivery_owner` (`owner_type`, `owner_id`),
|
|
KEY `idx_yumi_gk_delivery_status` (`delivery_status`, `update_time`, `id`),
|
|
KEY `idx_yumi_gk_delivery_activity` (`activity_id`, `delivery_status`, `id`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='Group-level reward delivery and UNKNOWN reconciliation';
|
|
|
|
SET @current_schema := DATABASE();
|
|
SET @add_task_center_game_king_backfill_sql := IF(
|
|
NOT EXISTS (
|
|
SELECT 1
|
|
FROM INFORMATION_SCHEMA.STATISTICS
|
|
WHERE table_schema = @current_schema
|
|
AND table_name = 'task_center_event_archive_outbox'
|
|
AND index_name = 'idx_task_center_game_king_backfill'
|
|
),
|
|
'ALTER TABLE `task_center_event_archive_outbox` ADD INDEX `idx_task_center_game_king_backfill` (`sys_origin`, `event_type`, `occurred_at`, `id`), ALGORITHM=INPLACE, LOCK=NONE',
|
|
'SELECT 1'
|
|
);
|
|
PREPARE add_task_center_game_king_backfill_stmt FROM @add_task_center_game_king_backfill_sql;
|
|
EXECUTE add_task_center_game_king_backfill_stmt;
|
|
DEALLOCATE PREPARE add_task_center_game_king_backfill_stmt;
|