SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;

USE hyapp_lucky_gift;

-- 用户画像是幸运礼物事实的专用读模型。列表、窗口指标和大奖解释都只能读取这些表，
-- 不能在运营查询时临时 GROUP BY 全量内部/外部抽奖事实。
CREATE TABLE IF NOT EXISTS lucky_gift_user_profiles (
  app_code VARCHAR(32) NOT NULL COMMENT '应用编码',
  pool_id VARCHAR(96) NOT NULL COMMENT '幸运礼物奖池 ID',
  strategy_version VARCHAR(32) NOT NULL COMMENT 'fixed_v2/dynamic_v3；两套奖池画像严格隔离',
  identity_type VARCHAR(16) NOT NULL COMMENT 'internal/external',
  identity_key VARCHAR(128) COLLATE utf8mb4_bin NOT NULL COMMENT '内部 user_id 文本或外部 external_user_id；永不存展示稳定哈希',
  strategy_user_id BIGINT NOT NULL COMMENT '策略状态用户键；外部值仅供本服务关联状态，绝不下发',
  internal_user_id BIGINT NOT NULL DEFAULT 0 COMMENT '内部真实 user_id；外部固定为 0',
  external_user_id VARCHAR(128) COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '外部原始用户 ID；内部固定为空',
  rule_version BIGINT NOT NULL DEFAULT 0 COMMENT '最近一次不可变规则版本',
  current_stage VARCHAR(32) NOT NULL DEFAULT '' COMMENT '最近一次抽奖阶段',
  total_draws BIGINT NOT NULL DEFAULT 0 COMMENT '累计子抽次数',
  total_wager_coins BIGINT NOT NULL DEFAULT 0 COMMENT '累计消耗金币',
  total_payout_coins BIGINT NOT NULL DEFAULT 0 COMMENT '累计用户可见返奖金币',
  lifetime_rtp_ppm BIGINT NOT NULL DEFAULT 0 COMMENT '累计 RTP，物化后用于无 filesort 游标排序',
  total_base_reward_coins BIGINT NOT NULL DEFAULT 0 COMMENT '累计基础返奖金币',
  ordinary_win_count BIGINT NOT NULL DEFAULT 0 COMMENT '普通非零奖命中次数',
  high_multiplier_ordinary_win_count BIGINT NOT NULL DEFAULT 0 COMMENT '高倍率普通奖命中次数；与大奖独立',
  rtp_compensation_jackpot_count BIGINT NOT NULL DEFAULT 0 COMMENT 'RTP 补偿大奖命中次数',
  cumulative_spend_jackpot_count BIGINT NOT NULL DEFAULT 0 COMMENT '累计消费大奖命中次数',
  jackpot_count BIGINT NOT NULL DEFAULT 0 COMMENT '两类独立大奖累计次数，物化后用于排序和筛选',
  guarantee_hit_count BIGINT NOT NULL DEFAULT 0 COMMENT '连续未中奖保护命中次数',
  zero_draw_count BIGINT NOT NULL DEFAULT 0 COMMENT '0 倍未中奖次数',
  granted_draw_count BIGINT NOT NULL DEFAULT 0 COMMENT '已发放次数',
  pending_draw_count BIGINT NOT NULL DEFAULT 0 COMMENT '待发放次数',
  failed_draw_count BIGINT NOT NULL DEFAULT 0 COMMENT '发放失败次数',
  max_multiplier_ppm BIGINT NOT NULL DEFAULT 0 COMMENT '历史最高倍率',
  max_reward_coins BIGINT NOT NULL DEFAULT 0 COMMENT '历史单抽最高返奖',
  last_downweight_active TINYINT(1) NOT NULL DEFAULT 0 COMMENT '最近一次抽奖是否触发 24h RTP 降权',
  current_equivalent_draws BIGINT NOT NULL DEFAULT 0 COMMENT '当前运行状态的等价抽数快照',
  current_loss_streak BIGINT NOT NULL DEFAULT 0 COMMENT '当前连续未中奖次数快照',
  current_pending_spend_jackpot_tokens BIGINT NOT NULL DEFAULT 0 COMMENT '当前累计消费大奖资格快照',
  first_draw_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '首次抽奖事实时间',
  last_draw_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '最近抽奖事实时间',
  aggregated_at_ms BIGINT NOT NULL COMMENT '最近聚合时间，UTC epoch ms',
  created_at_ms BIGINT NOT NULL COMMENT '创建时间，UTC epoch ms',
  updated_at_ms BIGINT NOT NULL COMMENT '更新时间，UTC epoch ms',
  PRIMARY KEY (app_code, pool_id, strategy_version, identity_type, identity_key),
  KEY idx_lucky_user_profile_sort_last (app_code, pool_id, strategy_version, last_draw_at_ms DESC, identity_type DESC, identity_key DESC),
  KEY idx_lucky_user_profile_sort_rtp (app_code, pool_id, strategy_version, lifetime_rtp_ppm DESC, identity_type DESC, identity_key DESC),
  KEY idx_lucky_user_profile_sort_wager (app_code, pool_id, strategy_version, total_wager_coins DESC, identity_type DESC, identity_key DESC),
  KEY idx_lucky_user_profile_sort_payout (app_code, pool_id, strategy_version, total_payout_coins DESC, identity_type DESC, identity_key DESC),
  KEY idx_lucky_user_profile_sort_jackpot (app_code, pool_id, strategy_version, jackpot_count DESC, identity_type DESC, identity_key DESC),
  KEY idx_lucky_user_profile_sort_loss (app_code, pool_id, strategy_version, current_loss_streak DESC, identity_type DESC, identity_key DESC),
  KEY idx_lucky_user_profile_stage (app_code, pool_id, strategy_version, current_stage, identity_type, identity_key),
  KEY idx_lucky_user_profile_pending (app_code, pool_id, strategy_version, current_pending_spend_jackpot_tokens DESC, identity_type DESC, identity_key DESC),
  KEY idx_lucky_user_profile_internal (app_code, pool_id, strategy_version, internal_user_id),
  KEY idx_lucky_user_profile_external (app_code, pool_id, strategy_version, external_user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='幸运礼物用户画像累计快照';

-- 画像事件只保留统计所需的窄字段。永久唯一键既支持严格滚动窗口，也让历史回灌与实时写入幂等共存。
CREATE TABLE IF NOT EXISTS lucky_gift_user_profile_events (
  app_code VARCHAR(32) NOT NULL COMMENT '应用编码',
  source_type VARCHAR(24) NOT NULL COMMENT 'internal/external_item/external_legacy',
  source_draw_id VARCHAR(128) NOT NULL COMMENT '来源抽奖事实 ID',
  pool_id VARCHAR(96) NOT NULL COMMENT '幸运礼物奖池 ID',
  strategy_version VARCHAR(32) NOT NULL COMMENT 'fixed_v2/dynamic_v3',
  identity_type VARCHAR(16) NOT NULL COMMENT 'internal/external',
  identity_key VARCHAR(128) COLLATE utf8mb4_bin NOT NULL COMMENT '可搜索用户身份键',
  draw_count BIGINT NOT NULL DEFAULT 1 COMMENT '该事件代表的子抽次数；fixed_v2 外部整单固定为 1，旧 dynamic 聚合事实可大于 1',
  wager_coins BIGINT NOT NULL DEFAULT 0 COMMENT '消耗金币',
  payout_coins BIGINT NOT NULL DEFAULT 0 COMMENT '用户可见返奖金币',
  base_reward_coins BIGINT NOT NULL DEFAULT 0 COMMENT '基础返奖金币',
  multiplier_ppm BIGINT NOT NULL DEFAULT 0 COMMENT '命中倍率',
  hit_type VARCHAR(32) NOT NULL COMMENT 'zero/ordinary/rtp_compensation/spend_milestone',
  reward_status VARCHAR(32) NOT NULL COMMENT 'pending/granted/failed',
  occurred_at_ms BIGINT NOT NULL COMMENT '支付事实时间，UTC epoch ms',
  aggregation_token VARCHAR(96) NOT NULL DEFAULT '' COMMENT '首次插入者的聚合批次令牌；用于在线写入与回灌并发去重',
  created_at_ms BIGINT NOT NULL COMMENT '读模型写入时间，UTC epoch ms',
  PRIMARY KEY (app_code, source_type, source_draw_id),
  KEY idx_lucky_user_profile_event_window (app_code, pool_id, strategy_version, identity_type, identity_key, occurred_at_ms),
  KEY idx_lucky_user_profile_event_token (app_code, source_type, aggregation_token)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='幸运礼物用户画像窗口事件';

CREATE TABLE IF NOT EXISTS lucky_gift_user_multiplier_stats (
  app_code VARCHAR(32) NOT NULL COMMENT '应用编码',
  pool_id VARCHAR(96) NOT NULL COMMENT '幸运礼物奖池 ID',
  strategy_version VARCHAR(32) NOT NULL COMMENT 'fixed_v2/dynamic_v3',
  identity_type VARCHAR(16) NOT NULL COMMENT 'internal/external',
  identity_key VARCHAR(128) COLLATE utf8mb4_bin NOT NULL COMMENT '可搜索用户身份键',
  hit_type VARCHAR(32) NOT NULL COMMENT '普通奖和两类大奖分别聚合，允许同倍率共存',
  multiplier_ppm BIGINT NOT NULL COMMENT '倍率，1x=1000000',
  draw_count BIGINT NOT NULL DEFAULT 0 COMMENT '命中次数',
  wager_coins BIGINT NOT NULL DEFAULT 0 COMMENT '对应抽奖消耗',
  payout_coins BIGINT NOT NULL DEFAULT 0 COMMENT '对应返奖',
  updated_at_ms BIGINT NOT NULL COMMENT '更新时间，UTC epoch ms',
  PRIMARY KEY (app_code, pool_id, strategy_version, identity_type, identity_key, hit_type, multiplier_ppm)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='幸运礼物用户画像倍率分布';

CREATE TABLE IF NOT EXISTS lucky_gift_user_jackpot_hits (
  app_code VARCHAR(32) NOT NULL COMMENT '应用编码',
  source_type VARCHAR(24) NOT NULL COMMENT 'internal/external_item',
  source_draw_id VARCHAR(128) NOT NULL COMMENT '来源抽奖事实 ID',
  pool_id VARCHAR(96) NOT NULL COMMENT '幸运礼物奖池 ID',
  strategy_version VARCHAR(32) NOT NULL COMMENT 'fixed_v2/dynamic_v3',
  identity_type VARCHAR(16) NOT NULL COMMENT 'internal/external',
  identity_key VARCHAR(128) COLLATE utf8mb4_bin NOT NULL COMMENT '可搜索用户身份键',
  request_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '内部 command_id 或外部 request_id',
  rule_version BIGINT NOT NULL DEFAULT 0 COMMENT '不可变规则版本',
  stage VARCHAR(32) NOT NULL DEFAULT '' COMMENT '命中时阶段',
  mechanism VARCHAR(32) NOT NULL COMMENT 'rtp_compensation/spend_milestone',
  reason_code VARCHAR(96) NOT NULL DEFAULT '' COMMENT '内核最终原因码',
  tier_id VARCHAR(96) NOT NULL DEFAULT '' COMMENT '独立大奖 tier ID',
  multiplier_ppm BIGINT NOT NULL DEFAULT 0 COMMENT '大奖倍率',
  wager_coins BIGINT NOT NULL DEFAULT 0 COMMENT '本抽消耗',
  payout_coins BIGINT NOT NULL DEFAULT 0 COMMENT '本抽返奖',
  conditions_json JSON NOT NULL COMMENT '命中当刻条件快照',
  occurred_at_ms BIGINT NOT NULL COMMENT '支付事实时间，UTC epoch ms',
  created_at_ms BIGINT NOT NULL COMMENT '读模型写入时间，UTC epoch ms',
  PRIMARY KEY (app_code, source_type, source_draw_id),
  KEY idx_lucky_user_jackpot_profile (app_code, pool_id, strategy_version, identity_type, identity_key, occurred_at_ms DESC, source_type DESC, source_draw_id DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='幸运礼物用户画像大奖解释';

-- migration 可在建表完成、进程尚未回灌时安全重跑。列使用 INSTANT；画像表在首次上线时为空，
-- 排序索引使用 INPLACE+LOCK=NONE，明确禁止意外 COPY 大表。
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND COLUMN_NAME='lifetime_rtp_ppm')=0,
  'ALTER TABLE lucky_gift_user_profiles ADD COLUMN lifetime_rtp_ppm BIGINT NOT NULL DEFAULT 0 COMMENT ''累计 RTP，物化后用于无 filesort 游标排序'' AFTER total_payout_coins, ALGORITHM=INSTANT', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND COLUMN_NAME='jackpot_count')=0,
  'ALTER TABLE lucky_gift_user_profiles ADD COLUMN jackpot_count BIGINT NOT NULL DEFAULT 0 COMMENT ''两类独立大奖累计次数'' AFTER cumulative_spend_jackpot_count, ALGORITHM=INSTANT', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND COLUMN_NAME='current_equivalent_draws')=0,
  'ALTER TABLE lucky_gift_user_profiles ADD COLUMN current_equivalent_draws BIGINT NOT NULL DEFAULT 0 COMMENT ''当前运行状态的等价抽数快照'' AFTER last_downweight_active, ALGORITHM=INSTANT', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND COLUMN_NAME='current_loss_streak')=0,
  'ALTER TABLE lucky_gift_user_profiles ADD COLUMN current_loss_streak BIGINT NOT NULL DEFAULT 0 COMMENT ''当前连续未中奖次数快照'' AFTER current_equivalent_draws, ALGORITHM=INSTANT', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND COLUMN_NAME='current_pending_spend_jackpot_tokens')=0,
  'ALTER TABLE lucky_gift_user_profiles ADD COLUMN current_pending_spend_jackpot_tokens BIGINT NOT NULL DEFAULT 0 COMMENT ''当前累计消费大奖资格快照'' AFTER current_loss_streak, ALGORITHM=INSTANT', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;

SET @ddl := IF((SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND INDEX_NAME='idx_lucky_user_profile_sort_last')=0,
  'ALTER TABLE lucky_gift_user_profiles ADD KEY idx_lucky_user_profile_sort_last (app_code, pool_id, strategy_version, last_draw_at_ms DESC, identity_type DESC, identity_key DESC), ALGORITHM=INPLACE, LOCK=NONE', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND INDEX_NAME='idx_lucky_user_profile_sort_rtp')=0,
  'ALTER TABLE lucky_gift_user_profiles ADD KEY idx_lucky_user_profile_sort_rtp (app_code, pool_id, strategy_version, lifetime_rtp_ppm DESC, identity_type DESC, identity_key DESC), ALGORITHM=INPLACE, LOCK=NONE', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND INDEX_NAME='idx_lucky_user_profile_sort_wager')=0,
  'ALTER TABLE lucky_gift_user_profiles ADD KEY idx_lucky_user_profile_sort_wager (app_code, pool_id, strategy_version, total_wager_coins DESC, identity_type DESC, identity_key DESC), ALGORITHM=INPLACE, LOCK=NONE', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND INDEX_NAME='idx_lucky_user_profile_sort_payout')=0,
  'ALTER TABLE lucky_gift_user_profiles ADD KEY idx_lucky_user_profile_sort_payout (app_code, pool_id, strategy_version, total_payout_coins DESC, identity_type DESC, identity_key DESC), ALGORITHM=INPLACE, LOCK=NONE', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND INDEX_NAME='idx_lucky_user_profile_sort_jackpot')=0,
  'ALTER TABLE lucky_gift_user_profiles ADD KEY idx_lucky_user_profile_sort_jackpot (app_code, pool_id, strategy_version, jackpot_count DESC, identity_type DESC, identity_key DESC), ALGORITHM=INPLACE, LOCK=NONE', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND INDEX_NAME='idx_lucky_user_profile_sort_loss')=0,
  'ALTER TABLE lucky_gift_user_profiles ADD KEY idx_lucky_user_profile_sort_loss (app_code, pool_id, strategy_version, current_loss_streak DESC, identity_type DESC, identity_key DESC), ALGORITHM=INPLACE, LOCK=NONE', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND INDEX_NAME='idx_lucky_user_profile_stage')=0,
  'ALTER TABLE lucky_gift_user_profiles ADD KEY idx_lucky_user_profile_stage (app_code, pool_id, strategy_version, current_stage, identity_type, identity_key), ALGORITHM=INPLACE, LOCK=NONE', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND INDEX_NAME='idx_lucky_user_profile_pending')=0,
  'ALTER TABLE lucky_gift_user_profiles ADD KEY idx_lucky_user_profile_pending (app_code, pool_id, strategy_version, current_pending_spend_jackpot_tokens DESC, identity_type DESC, identity_key DESC), ALGORITHM=INPLACE, LOCK=NONE', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;

-- 早期本地 009 使用全 ASC 的 list 索引；完整 sort_last 已覆盖其正向和反向扫描，删除重复索引避免每抽双写。
SET @ddl := IF((SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_profiles' AND INDEX_NAME='idx_lucky_user_profile_list')>0,
  'ALTER TABLE lucky_gift_user_profiles DROP INDEX idx_lucky_user_profile_list, ALGORITHM=INPLACE, LOCK=NONE', 'SELECT 1');
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;

-- 正常首次上线时画像表尚为空；该更新只用于半完成 migration 后已有画像的幂等修复。
-- 只扫描专用小型读模型并按主键关联状态，不触碰 8 万级开奖事实表。
UPDATE lucky_gift_user_profiles p
LEFT JOIN lucky_user_states s
  ON s.app_code = p.app_code AND s.user_id = p.strategy_user_id AND s.gift_id = p.pool_id
SET p.lifetime_rtp_ppm = CASE
      WHEN p.total_wager_coins <= 0 THEN 0
      ELSE CAST(LEAST(CAST(9223372036854775807 AS DECIMAL(65,0)),
           FLOOR(CAST(p.total_payout_coins AS DECIMAL(65,0)) * 1000000 / p.total_wager_coins)) AS SIGNED)
    END,
    p.jackpot_count = p.rtp_compensation_jackpot_count + p.cumulative_spend_jackpot_count,
    p.current_equivalent_draws = COALESCE(s.equivalent_draws, 0),
    p.current_loss_streak = COALESCE(s.loss_streak, 0),
    p.current_pending_spend_jackpot_tokens = COALESCE(s.pending_spend_jackpot_tokens, 0);

-- 早期半完成的 009 只有 occurred_at_ms；完整大奖游标还必须把 source_type/source_draw_id 纳入索引顺序。
SET @jackpot_cursor_columns := (SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX SEPARATOR ',') FROM information_schema.STATISTICS
  WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='lucky_gift_user_jackpot_hits' AND INDEX_NAME='idx_lucky_user_jackpot_profile');
SET @ddl := CASE
  WHEN COALESCE(@jackpot_cursor_columns, '') = '' THEN
    'ALTER TABLE lucky_gift_user_jackpot_hits ADD KEY idx_lucky_user_jackpot_profile (app_code, pool_id, strategy_version, identity_type, identity_key, occurred_at_ms DESC, source_type DESC, source_draw_id DESC), ALGORITHM=INPLACE, LOCK=NONE'
  WHEN @jackpot_cursor_columns <> 'app_code,pool_id,strategy_version,identity_type,identity_key,occurred_at_ms,source_type,source_draw_id' THEN
    'ALTER TABLE lucky_gift_user_jackpot_hits DROP INDEX idx_lucky_user_jackpot_profile, ADD KEY idx_lucky_user_jackpot_profile (app_code, pool_id, strategy_version, identity_type, identity_key, occurred_at_ms DESC, source_type DESC, source_draw_id DESC), ALGORITHM=INPLACE, LOCK=NONE'
  ELSE 'SELECT 1'
END;
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;

-- 两个来源索引按 information_schema 守卫，允许 migration 在网络中断或半完成后安全重跑。
-- INPLACE+LOCK=NONE 禁止退化成 COPY；上线前仍必须检查表大小和长事务，避免 MDL 等待。
SET @ddl := IF(
  (SELECT COUNT(*) FROM information_schema.STATISTICS
   WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'external_lucky_gift_draws'
     AND INDEX_NAME = 'idx_external_lucky_profile_cursor') = 0,
  'ALTER TABLE external_lucky_gift_draws ADD KEY idx_external_lucky_profile_cursor (app_code, created_at_ms, draw_id), ALGORITHM=INPLACE, LOCK=NONE',
  'SELECT 1'
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;

SET @ddl := IF(
  (SELECT COUNT(*) FROM information_schema.STATISTICS
   WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'external_lucky_gift_draw_items'
     AND INDEX_NAME = 'idx_external_lucky_item_profile_cursor') = 0,
  'ALTER TABLE external_lucky_gift_draw_items ADD KEY idx_external_lucky_item_profile_cursor (app_code, created_at_ms, draw_id), ALGORITHM=INPLACE, LOCK=NONE',
  'SELECT 1'
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
