hyapp-server/server/admin/migrations/102_user_withdrawal_operations_review.sql
2026-07-16 18:51:58 +08:00

89 lines
6.2 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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');