-- Aslan Gift Challenge: non-lucky paid-gift ranking, daily tasks and reward settlement.
--
-- Performance review (DDL only; run through the normal deployment migration process):
-- 1. The gift hot path inserts one row through uq_aslan_gc_ledger_source_event, then updates one
--    overall PK row, one daily PK row and at most three daily-task rows. Every lookup is bounded
--    by activity/user/date keys and is O(log N); ranking never GROUP BYs the append-only ledger.
-- 2. Overall and daily Top-N queries bind activity_id (and stat_date for DAILY) before reading
--    score order from covering indexes. The same score/reached-time/user-id order supports a
--    deterministic rank query without sorting the entire activity in memory.
-- 3. Campaign lookup uses idx_aslan_gc_activity_window. start_time is the range column and
--    end_time is an index-covered residual filter; enabled campaigns per sys_origin are expected
--    to remain few and overlapping enabled windows are rejected by the application transaction.
-- 4. Period headers make an empty DAILY ranking settle exactly once and gate late writes after a
--    snapshot starts. Due headers and task-delivery recovery use status/time indexes; reward
--    reconciliation reads only one frozen owner and its configured group through unique keys.
-- 5. No UPDATE or DELETE below uses an unindexed predicate. This migration creates empty campaign
--    tables and idempotent RBAC rows only; it does not scan or backfill gift_give_running_water.
-- 6. Before production rollout, run EXPLAIN for display-activity, Top-N, my-rank, due-settlement
--    and recovery queries against production-like cardinality. Any gift-history backfill must use
--    a checked Mongo index and an id cursor; OFFSET/full collection scans are explicitly excluded.
-- 7. Enabling a campaign inserts at most 366 DAILY headers in one batched primary/unique-key write.
--    claim_request_id adds one nullable audit index updated only on the first successful task claim.
-- 8. Enabling also freezes at most 5000 reward rows in batches of 500. Reads always bind
--    activity_id + resource_group_id and follow idx_aslan_gc_reward_snapshot_group; pre-start
--    rebuild/delete binds activity_id and uses the same leftmost index prefix, so no table scan is
--    introduced when a shared reward group is edited later.

SET NAMES utf8mb4;

CREATE TABLE IF NOT EXISTS aslan_gift_challenge_activity (
  id BIGINT NOT NULL,
  activity_code VARCHAR(64) NOT NULL,
  activity_name VARCHAR(128) NOT NULL,
  activity_desc VARCHAR(1000) NULL,
  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,
  daily_settlement_delay_minutes INT NOT NULL DEFAULT 30,
  overall_settlement_delay_minutes INT NOT NULL DEFAULT 30,
  overall_settlement_time DATETIME(3) NOT NULL,
  display_top_n SMALLINT NOT NULL DEFAULT 100,
  enabled TINYINT(1) NOT NULL DEFAULT 0,
  overall_settlement_status VARCHAR(32) NOT NULL DEFAULT 'NOT_STARTED',
  version INT NOT NULL DEFAULT 0,
  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 uq_aslan_gc_activity_code (activity_code),
  KEY idx_aslan_gc_activity_window (sys_origin, enabled, start_time, end_time),
  KEY idx_aslan_gc_activity_settle (
    overall_settlement_status, overall_settlement_time, end_time, id
  )
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Aslan paid non-lucky gift challenge campaign';

CREATE TABLE IF NOT EXISTS aslan_gift_challenge_task_config (
  id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  task_code VARCHAR(64) NOT NULL,
  task_type VARCHAR(32) NOT NULL COMMENT 'ENTER_PAGE or SEND_GIFT_GOLD',
  task_title VARCHAR(128) NOT NULL,
  task_desc VARCHAR(500) NULL,
  target_value DECIMAL(24,2) NOT NULL,
  resource_group_id BIGINT NULL,
  enabled TINYINT(1) NOT NULL DEFAULT 1,
  sort_order SMALLINT 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 uq_aslan_gc_task_code (activity_id, task_code),
  UNIQUE KEY uq_aslan_gc_task_sort (activity_id, sort_order),
  KEY idx_aslan_gc_task_enabled (activity_id, enabled, sort_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Exactly three activity-scoped daily task definitions';

CREATE TABLE IF NOT EXISTS aslan_gift_challenge_rank_reward (
  id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  period_type VARCHAR(16) NOT NULL COMMENT 'DAILY or OVERALL',
  start_rank SMALLINT NOT NULL,
  end_rank SMALLINT NOT NULL,
  resource_group_id BIGINT NOT NULL,
  reward_name VARCHAR(128) 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 uq_aslan_gc_rank_range (activity_id, period_type, start_rank, end_rank),
  KEY idx_aslan_gc_rank_start (activity_id, period_type, start_rank, end_rank)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Configurable DAILY and OVERALL rank reward ranges';

CREATE TABLE IF NOT EXISTS aslan_gift_challenge_reward_snapshot (
  id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  resource_group_id BIGINT NOT NULL,
  reward_config_id BIGINT NOT NULL,
  reward_type VARCHAR(32) NOT NULL,
  detail_type VARCHAR(64) NULL,
  content VARCHAR(500) NULL,
  quantity INT NULL,
  sort_order INT NULL,
  remark VARCHAR(500) NULL,
  cover TEXT NULL,
  source_url TEXT NULL,
  display_name VARCHAR(500) NULL,
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  PRIMARY KEY (id),
  UNIQUE KEY uq_aslan_gc_reward_snapshot (
    activity_id, resource_group_id, reward_config_id
  ),
  KEY idx_aslan_gc_reward_snapshot_group (
    activity_id, resource_group_id, sort_order, reward_config_id
  )
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Immutable per-activity reward item templates captured before start';

CREATE TABLE IF NOT EXISTS aslan_gift_challenge_gift_ledger (
  id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  source_event_track_id BIGINT NOT NULL,
  user_id BIGINT NOT NULL,
  gift_id BIGINT NOT NULL,
  gift_tab VARCHAR(32) NULL,
  amount DECIMAL(24,2) NOT NULL,
  event_time DATETIME(3) NOT NULL,
  stat_date DATE NOT NULL,
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  PRIMARY KEY (id),
  UNIQUE KEY uq_aslan_gc_ledger_source_event (activity_id, source_event_track_id),
  KEY idx_aslan_gc_ledger_user (activity_id, user_id, event_time),
  KEY idx_aslan_gc_ledger_date (activity_id, stat_date, id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Idempotent eligible paid-gift fact ledger';

CREATE TABLE IF NOT EXISTS aslan_gift_challenge_user_score (
  activity_id BIGINT NOT NULL,
  user_id BIGINT NOT NULL,
  score DECIMAL(24,2) NOT NULL DEFAULT 0,
  score_reached_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 (activity_id, user_id),
  KEY idx_aslan_gc_user_rank (
    activity_id, score DESC, score_reached_time ASC, user_id ASC
  )
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Activity-wide user score aggregate';

CREATE TABLE IF NOT EXISTS aslan_gift_challenge_user_daily_score (
  activity_id BIGINT NOT NULL,
  stat_date DATE NOT NULL,
  user_id BIGINT NOT NULL,
  score DECIMAL(24,2) NOT NULL DEFAULT 0,
  score_reached_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 (activity_id, stat_date, user_id),
  KEY idx_aslan_gc_daily_rank (
    activity_id, stat_date, score DESC, score_reached_time ASC, user_id ASC
  )
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Activity-timezone daily user score aggregate';

CREATE TABLE IF NOT EXISTS aslan_gift_challenge_user_task_daily (
  id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  stat_date DATE NOT NULL,
  user_id BIGINT NOT NULL,
  task_config_id BIGINT NOT NULL,
  task_code VARCHAR(64) NOT NULL,
  task_type VARCHAR(32) NOT NULL,
  task_title VARCHAR(128) NOT NULL,
  sort_order SMALLINT NOT NULL,
  target_value DECIMAL(24,2) NOT NULL,
  progress_value DECIMAL(24,2) NOT NULL DEFAULT 0,
  completed_time DATETIME(3) NULL,
  resource_group_id BIGINT NULL,
  business_no VARCHAR(160) NOT NULL,
  claim_request_id VARCHAR(128) NULL COMMENT 'First successful H5 claim request audit id',
  delivery_status VARCHAR(32) NOT NULL DEFAULT 'NOT_CLAIMED'
    COMMENT 'NOT_CLAIMED, PENDING, PROCESSING, SUCCESS or UNKNOWN',
  retry_count INT NOT NULL DEFAULT 0,
  failure_reason VARCHAR(500) NULL,
  claimed_time DATETIME(3) 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 uq_aslan_gc_task_daily (activity_id, stat_date, user_id, task_code),
  UNIQUE KEY uq_aslan_gc_task_business (business_no),
  KEY idx_aslan_gc_task_user (activity_id, user_id, stat_date, sort_order),
  KEY idx_aslan_gc_task_claim_request (claim_request_id),
  KEY idx_aslan_gc_task_recover (delivery_status, update_time, id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Daily task target/reward snapshot, progress and claim state';

CREATE TABLE IF NOT EXISTS aslan_gift_challenge_period_settlement (
  id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  period_type VARCHAR(16) NOT NULL COMMENT 'DAILY or OVERALL',
  period_key VARCHAR(16) NOT NULL COMMENT 'yyyy-MM-dd for DAILY, OVERALL otherwise',
  stat_date DATE NULL,
  snapshot_due_time DATETIME(3) NOT NULL,
  status VARCHAR(32) NOT NULL DEFAULT 'NOT_STARTED'
    COMMENT 'NOT_STARTED, PROCESSING, COMPLETED or RECONCILIATION_REQUIRED',
  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 uq_aslan_gc_period (activity_id, period_type, period_key),
  KEY idx_aslan_gc_period_due (status, snapshot_due_time, id),
  KEY idx_aslan_gc_period_activity (activity_id, period_type, status, period_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Per-period snapshot gate including empty DAILY rankings';

CREATE TABLE IF NOT EXISTS aslan_gift_challenge_settlement (
  id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  period_type VARCHAR(16) NOT NULL COMMENT 'DAILY or OVERALL',
  period_key VARCHAR(16) NOT NULL COMMENT 'yyyy-MM-dd for DAILY, OVERALL otherwise',
  stat_date DATE NULL,
  user_id BIGINT NOT NULL,
  rank_no SMALLINT NOT NULL,
  score DECIMAL(24,2) NOT NULL,
  rank_reward_id BIGINT NOT NULL,
  resource_group_id BIGINT NOT NULL,
  business_no VARCHAR(160) NOT NULL,
  delivery_status VARCHAR(32) NOT NULL COMMENT 'PENDING, PROCESSING, SUCCESS or UNKNOWN',
  retry_count INT NOT NULL DEFAULT 0,
  failure_reason VARCHAR(500) 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 uq_aslan_gc_settlement_user (
    activity_id, period_type, period_key, user_id
  ),
  UNIQUE KEY uq_aslan_gc_settlement_rank (
    activity_id, period_type, period_key, rank_no
  ),
  UNIQUE KEY uq_aslan_gc_settlement_business (business_no),
  KEY idx_aslan_gc_settlement_status (
    activity_id, period_type, period_key, delivery_status, rank_no
  ),
  KEY idx_aslan_gc_settlement_recover (delivery_status, update_time, id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Frozen DAILY/OVERALL rank snapshots and delivery state';

CREATE TABLE IF NOT EXISTS aslan_gift_challenge_delivery_item (
  id BIGINT NOT NULL,
  owner_type VARCHAR(20) NOT NULL COMMENT 'TASK or SETTLEMENT',
  owner_id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  user_id BIGINT NOT NULL,
  reward_config_id BIGINT NOT NULL,
  resource_group_id BIGINT NOT NULL,
  reward_type VARCHAR(32) NOT NULL,
  detail_type VARCHAR(64) NULL,
  content VARCHAR(500) NULL,
  quantity INT NULL,
  sort_order INT NULL,
  remark VARCHAR(500) NULL,
  delivery_status VARCHAR(32) NOT NULL COMMENT 'PENDING, PROCESSING, SUCCESS or UNKNOWN',
  retry_count INT NOT NULL DEFAULT 0,
  failure_reason VARCHAR(500) 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 uq_aslan_gc_delivery_config (owner_type, owner_id, reward_config_id),
  KEY idx_aslan_gc_delivery_owner (owner_type, owner_id, delivery_status),
  KEY idx_aslan_gc_delivery_activity (activity_id, delivery_status, id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Per reward-config transactional delivery outbox';

-- RBAC is idempotent. The page is inherited only by roles that already own Operations.
INSERT INTO sys_resource (id, resource_name, mapping, method, auth_type, perm, update_time)
VALUES
  ('activity:gift-challenge:list', '礼物挑战活动列表', '/activity/gift-challenge/list', 'GET', 3, 'activity:gift-challenge:list', NOW()),
  ('activity:gift-challenge:detail', '礼物挑战活动详情', '/activity/gift-challenge/detail/**', 'GET', 3, 'activity:gift-challenge:detail', NOW()),
  ('activity:gift-challenge:save', '保存礼物挑战活动', '/activity/gift-challenge/save', 'POST', 3, 'activity:gift-challenge:save', NOW()),
  ('activity:gift-challenge:enable', '启停礼物挑战活动', '/activity/gift-challenge/enable/**', 'PUT', 3, 'activity:gift-challenge:enable', NOW()),
  ('activity:gift-challenge:tasks:get', '礼物挑战任务列表', '/activity/gift-challenge/tasks/**', 'GET', 3, 'activity:gift-challenge:tasks:get', NOW()),
  ('activity:gift-challenge:tasks:save', '保存礼物挑战任务', '/activity/gift-challenge/tasks/**', 'PUT', 3, 'activity:gift-challenge:tasks:save', NOW()),
  ('activity:gift-challenge:rank:get', '礼物挑战排名奖励', '/activity/gift-challenge/rank-rewards/**', 'GET', 3, 'activity:gift-challenge:rank:get', NOW()),
  ('activity:gift-challenge:rank:save', '保存礼物挑战排名奖励', '/activity/gift-challenge/rank-rewards/**', 'PUT', 3, 'activity:gift-challenge:rank:save', NOW()),
  ('activity:gift-challenge:ranking', '查看礼物挑战榜单', '/activity/gift-challenge/ranking/**', 'GET', 3, 'activity:gift-challenge:ranking', NOW()),
  ('activity:gift-challenge:settlement:get', '礼物挑战结算记录', '/activity/gift-challenge/settlement-records/**', 'GET', 3, 'activity:gift-challenge:settlement:get', NOW()),
  ('activity:gift-challenge:settlement:run', '执行礼物挑战结算', '/activity/gift-challenge/settlement/**', 'POST', 3, 'activity:gift-challenge:settlement:run', NOW()),
  ('activity:gift-challenge:delivery:resolve', '核账处理礼物挑战未知奖励', '/activity/gift-challenge/delivery-item/resolve/**', 'POST', 3, 'activity:gift-challenge:delivery:resolve', NOW())
ON DUPLICATE KEY UPDATE
  resource_name = VALUES(resource_name),
  mapping = VALUES(mapping),
  method = VALUES(method),
  auth_type = VALUES(auth_type),
  perm = VALUES(perm),
  update_time = NOW();

INSERT INTO sys_menu (
  parent_id, menu_name, path, router, menu_type, icon, alias, sort, status,
  create_user, update_user, create_time, update_time
)
SELECT
  parent.id,
  '礼物挑战活动',
  'activity/gift-challenge/index',
  'activity/gift-challenge',
  2,
  'gift',
  'ActivityGiftChallenge',
  89,
  0,
  '0',
  '0',
  NOW(),
  NOW()
FROM sys_menu parent
LEFT JOIN sys_menu existing ON existing.alias = 'ActivityGiftChallenge'
WHERE (parent.alias = 'OperationManager' OR parent.menu_name = '运营管理')
  AND parent.menu_type = 1
  AND existing.id IS NULL
LIMIT 1;

UPDATE sys_menu campaign_menu
JOIN (
  SELECT id
  FROM sys_menu
  WHERE (alias = 'OperationManager' OR menu_name = '运营管理')
    AND menu_type = 1
  LIMIT 1
) parent ON 1 = 1
SET
  campaign_menu.parent_id = parent.id,
  campaign_menu.menu_name = '礼物挑战活动',
  campaign_menu.path = 'activity/gift-challenge/index',
  campaign_menu.router = 'activity/gift-challenge',
  campaign_menu.menu_type = 2,
  campaign_menu.icon = 'gift',
  campaign_menu.sort = 89,
  campaign_menu.status = 0,
  campaign_menu.update_user = '0',
  campaign_menu.update_time = NOW()
WHERE campaign_menu.alias = 'ActivityGiftChallenge';

INSERT INTO sys_menu_resource (menu_id, resource_id)
SELECT menu.id, resource.id
FROM sys_menu menu
JOIN sys_resource resource ON resource.id LIKE 'activity:gift-challenge:%'
LEFT JOIN sys_menu_resource existing
  ON existing.menu_id = menu.id AND existing.resource_id = resource.id
WHERE menu.alias = 'ActivityGiftChallenge'
  AND existing.id IS NULL;

INSERT INTO sys_role_menu (role_id, menu_id)
SELECT DISTINCT source_role.role_id, campaign_menu.id
FROM sys_role_menu source_role
JOIN sys_menu source_menu ON source_menu.id = source_role.menu_id
JOIN sys_menu campaign_menu ON campaign_menu.alias = 'ActivityGiftChallenge'
LEFT JOIN sys_role_menu existing
  ON existing.role_id = source_role.role_id AND existing.menu_id = campaign_menu.id
WHERE (source_menu.alias = 'OperationManager' OR source_menu.menu_name = '运营管理')
  AND source_menu.menu_type = 1
  AND existing.id IS NULL;

-- Buttons are real menu_type=3 rows because controller-side hasButtonsAlias checks sys_menu.alias,
-- not sys_resource.perm. Existing page owners inherit all four buttons idempotently.
INSERT INTO sys_menu (
  parent_id, menu_name, path, router, menu_type, icon, alias, sort, status,
  create_user, update_user, create_time, update_time
)
SELECT
  page.id,
  button.menu_name,
  '',
  '',
  3,
  '',
  button.alias,
  button.sort_no,
  0,
  '0',
  '0',
  NOW(),
  NOW()
FROM sys_menu page
JOIN (
  SELECT '编辑礼物挑战配置' AS menu_name, 'activity:gift-challenge:edit' AS alias, 1 AS sort_no
  UNION ALL
  SELECT '启停礼物挑战活动', 'activity:gift-challenge:enable', 2
  UNION ALL
  SELECT '执行礼物挑战结算', 'activity:gift-challenge:settle', 3
  UNION ALL
  SELECT '核账礼物挑战未知奖励', 'activity:gift-challenge:reconcile', 4
) button ON 1 = 1
LEFT JOIN sys_menu existing ON existing.alias = button.alias
WHERE page.alias = 'ActivityGiftChallenge'
  AND existing.id IS NULL;

UPDATE sys_menu button
JOIN sys_menu page ON page.alias = 'ActivityGiftChallenge'
SET
  button.parent_id = page.id,
  button.menu_type = 3,
  button.status = 0,
  button.update_user = '0',
  button.update_time = NOW()
WHERE button.alias IN (
  'activity:gift-challenge:edit',
  'activity:gift-challenge:enable',
  'activity:gift-challenge:settle',
  'activity:gift-challenge:reconcile'
);

INSERT INTO sys_role_menu (role_id, menu_id)
SELECT DISTINCT page_role.role_id, button.id
FROM sys_role_menu page_role
JOIN sys_menu page ON page.id = page_role.menu_id AND page.alias = 'ActivityGiftChallenge'
JOIN sys_menu button ON button.parent_id = page.id AND button.menu_type = 3
LEFT JOIN sys_role_menu existing
  ON existing.role_id = page_role.role_id AND existing.menu_id = button.id
WHERE button.alias IN (
  'activity:gift-challenge:edit',
  'activity:gift-challenge:enable',
  'activity:gift-challenge:settle',
  'activity:gift-challenge:reconcile'
)
  AND existing.id IS NULL;
