-- Activity reward snapshots are wallet-owned immutable grant facts. Every ALTER/INDEX is guarded so this -- migration can run after fresh initdb and can be retried on later deployments without duplicate errors. CREATE TABLE IF NOT EXISTS resource_group_snapshots ( app_code VARCHAR(32) NOT NULL COMMENT '应用编码', snapshot_id VARCHAR(96) NOT NULL COMMENT '不可变资源组快照 ID', pin_key VARCHAR(192) NOT NULL COMMENT '发布方业务版本幂等键', source_group_id BIGINT NOT NULL COMMENT '来源资源组 ID', version_no BIGINT NOT NULL COMMENT '来源资源组递增快照版本', snapshot_hash CHAR(64) NOT NULL COMMENT '完整快照 SHA-256', snapshot_json JSON NOT NULL COMMENT '含资源详情、礼物语义和钱包资产的完整不可变快照', source_group_updated_at_ms BIGINT NOT NULL COMMENT '锁定时来源资源组版本', source_content_hash CHAR(64) NOT NULL COMMENT '资源组、组项和嵌套资源的规范化内容哈希', created_by_user_id BIGINT NOT NULL, created_at_ms BIGINT NOT NULL, PRIMARY KEY (app_code, snapshot_id), UNIQUE KEY uk_resource_group_snapshot_pin (app_code, pin_key), UNIQUE KEY uk_resource_group_snapshot_version (app_code, source_group_id, version_no), KEY idx_resource_group_snapshot_source (app_code, source_group_id, created_at_ms) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='资源组发布态不可变奖励快照'; SET @ddl := IF( (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'user_resource_entitlements' AND COLUMN_NAME = 'source_snapshot_id') = 0, 'ALTER TABLE user_resource_entitlements ADD COLUMN source_snapshot_id VARCHAR(96) NOT NULL DEFAULT '''' AFTER source_grant_id', '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 = 'user_resource_entitlements' AND COLUMN_NAME = 'resource_snapshot_hash') = 0, 'ALTER TABLE user_resource_entitlements ADD COLUMN resource_snapshot_hash CHAR(64) NOT NULL DEFAULT '''' AFTER source_snapshot_id', '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 = 'user_resource_entitlements' AND COLUMN_NAME = 'resource_snapshot_json') = 0, 'ALTER TABLE user_resource_entitlements ADD COLUMN resource_snapshot_json JSON NULL AFTER resource_snapshot_hash', '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 = 'user_resource_entitlements' AND COLUMN_NAME = 'gift_snapshot_hash') = 0, 'ALTER TABLE user_resource_entitlements ADD COLUMN gift_snapshot_hash CHAR(64) NOT NULL DEFAULT '''' AFTER resource_snapshot_json', '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 = 'user_resource_entitlements' AND COLUMN_NAME = 'gift_snapshot_json') = 0, 'ALTER TABLE user_resource_entitlements ADD COLUMN gift_snapshot_json JSON NULL AFTER gift_snapshot_hash', '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 = 'user_resource_entitlements' AND INDEX_NAME = 'idx_user_resource_semantic') = 0, 'CREATE INDEX idx_user_resource_semantic ON user_resource_entitlements (app_code, user_id, resource_id, resource_snapshot_hash, status, expires_at_ms)', 'SELECT 1' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;