SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 这不是可直接混跑旧 admin 的普通滚动迁移：旧 admin 不读 operations_status，会把总体 status=pending 的新单直接财务终审。
-- 生产必须先摘流所有旧 admin，再执行 102 并部署新 admin，恢复 admin 流量后才能滚动 gateway。

-- checkpoint 在首次执行时固定迁移前的最大申请 ID。迁移中断后重放仍使用原边界，
-- 不会把部署窗口内由旧 gateway 新建的单误标为 skipped。该表只保存小型迁移元数据，不承载业务状态。
CREATE TABLE IF NOT EXISTS schema_migration_checkpoints (
  version VARCHAR(128) NOT NULL,
  checkpoint_value BIGINT UNSIGNED NOT NULL DEFAULT 0,
  created_at_ms BIGINT NOT NULL DEFAULT 0,
  PRIMARY KEY (version)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='可重放迁移的持久边界';

INSERT IGNORE INTO schema_migration_checkpoints (version, checkpoint_value, created_at_ms)
SELECT '102_user_withdrawal_operations_review.sql', COALESCE(MAX(id), 0), CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP(3)) * 1000 AS UNSIGNED)
FROM admin_user_withdrawal_applications;

-- 新列的数据库默认必须是 pending：旧 gateway 不传新列，在滚动窗口内也会自动进入运营审核。
-- 单个 ALTER 的列变更具有原子性；信息查询和持久 checkpoint 使整个文件在中断后可安全重放。
SET @ddl := IF(
  (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'admin_user_withdrawal_applications' AND COLUMN_NAME = 'operations_status') = 0,
  'ALTER TABLE admin_user_withdrawal_applications ADD COLUMN operations_status VARCHAR(32) NOT NULL DEFAULT ''pending'' COMMENT ''运营审核状态：skipped/pending/rejecting/approved/rejected'' AFTER status, ADD COLUMN operations_reviewer_user_id BIGINT UNSIGNED NULL AFTER operations_status, ADD COLUMN operations_reviewer_name VARCHAR(64) NOT NULL DEFAULT '''' AFTER operations_reviewer_user_id, ADD COLUMN operations_audit_remark TEXT NULL AFTER operations_reviewer_name, ADD COLUMN operations_audit_command_id VARCHAR(128) NOT NULL DEFAULT '''' AFTER operations_audit_remark, ADD COLUMN operations_audit_transaction_id VARCHAR(128) NOT NULL DEFAULT '''' AFTER operations_audit_command_id, ADD COLUMN operations_reviewed_at_ms BIGINT NULL AFTER operations_audit_transaction_id, ALGORITHM=INPLACE, LOCK=NONE',
  'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- 兼容早期局部执行过 default=skipped 的开发库；修正默认值是元数据操作，不回写全表。
SET @ddl := IF(
  (SELECT COLUMN_DEFAULT FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'admin_user_withdrawal_applications' AND COLUMN_NAME = 'operations_status') <> 'pending',
  'ALTER TABLE admin_user_withdrawal_applications ALTER COLUMN operations_status SET DEFAULT ''pending'', ALGORITHM=INPLACE, LOCK=NONE',
  'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- 只把迁移前已经终审的历史行标为 skipped，保留 status=pending 的真实待审申请进入运营初审；
-- 按主键范围回填低频 admin 申请，不扫描 wallet/user 流水。大量存量时在低峰执行，并监控行锁和复制延迟。
UPDATE admin_user_withdrawal_applications application
JOIN schema_migration_checkpoints checkpoint
  ON checkpoint.version = '102_user_withdrawal_operations_review.sql'
SET application.operations_status = 'skipped'
WHERE application.id <= checkpoint.checkpoint_value
  AND application.status IN ('approved', 'rejected')
  AND application.operations_status = 'pending';

-- 索引在回填后构建，避免回填期间逐行维护新索引。建索引会扫描该 admin 表并产生额外 IO/磁盘占用，
-- 必须在低峰确认磁盘余量，并监控 metadata lock 和复制延迟。
SET @ddl := IF(
  (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'admin_user_withdrawal_applications' AND INDEX_NAME = 'idx_admin_withdrawal_app_operations_status_time') = 0,
  'ALTER TABLE admin_user_withdrawal_applications ADD KEY idx_admin_withdrawal_app_operations_status_time (app_code, operations_status, created_at_ms, id), ALGORITHM=INPLACE, LOCK=NONE',
  'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

SET @now_ms = CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP(3)) * 1000 AS UNSIGNED);

-- 财务权限保留原 code，避免存量自建角色和 JWT 契约失效；仅收敛后台展示名称并新增独立运营权限。
INSERT INTO admin_permissions (name, code, kind, description, created_at_ms, updated_at_ms) VALUES
  ('用户提现财务审核', 'finance-withdrawal:view', 'menu', '查看已通过运营审核或历史兼容的用户提现申请', @now_ms, @now_ms),
  ('用户提现财务审核操作', 'finance-withdrawal:audit', 'button', '财务终审用户提现并扣减或释放冻结余额', @now_ms, @now_ms),
  ('用户提现运营审核', 'operations-withdrawal:view', 'menu', '查看需要运营初审的用户提现申请', @now_ms, @now_ms),
  ('用户提现运营审核操作', 'operations-withdrawal:audit', 'button', '运营初审用户提现；通过后转交财务，拒绝时释放冻结余额', @now_ms, @now_ms)
ON DUPLICATE KEY UPDATE
  name = VALUES(name),
  kind = VALUES(kind),
  description = VALUES(description),
  updated_at_ms = @now_ms;

-- 固定运营岗位不能继续持有财务终审权限，否则可绕过运营初审直接结算 frozen；只清理两个精确 role code 和两个精确 permission code。
DELETE role_permission
FROM admin_role_permissions role_permission
JOIN admin_roles role ON role.id = role_permission.role_id
JOIN admin_permissions permission ON permission.id = role_permission.permission_id
WHERE role.code IN ('ops-admin', 'operations-specialist')
  AND permission.code IN ('finance-withdrawal:view', 'finance-withdrawal:audit');

INSERT IGNORE INTO admin_role_permissions (role_id, permission_id)
SELECT role.id, permission.id
FROM admin_roles role
JOIN admin_permissions permission
WHERE role.code IN ('platform-admin', 'ops-admin', 'operations-specialist')
  AND permission.code IN ('operations-withdrawal:view', 'operations-withdrawal:audit');
