query( "CREATE TABLE IF NOT EXISTS module_usage_counters ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, module_key VARCHAR(64) NOT NULL, metric_key VARCHAR(128) NOT NULL, scope_type VARCHAR(32) NOT NULL DEFAULT 'global', scope_id VARCHAR(191) NOT NULL DEFAULT '', period_key VARCHAR(32) NOT NULL DEFAULT 'all_time', period_start DATETIME NOT NULL, period_end DATETIME NULL, unit VARCHAR(32) NOT NULL DEFAULT 'count', used_quantity DECIMAL(20,4) NOT NULL DEFAULT 0, limit_quantity DECIMAL(20,4) NULL, status VARCHAR(32) NOT NULL DEFAULT 'ok', metadata_json LONGTEXT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uniq_module_usage_counter (module_key, metric_key, scope_type, scope_id, period_key, period_start), KEY idx_module_usage_counters_module_period (module_key, period_key, period_start), KEY idx_module_usage_counters_status (status, updated_at), KEY idx_module_usage_counters_scope (scope_type, scope_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" ); $db->query( "CREATE TABLE IF NOT EXISTS module_usage_snapshots ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, module_key VARCHAR(64) NOT NULL, metric_key VARCHAR(128) NOT NULL, source VARCHAR(32) NOT NULL DEFAULT 'provider', period_key VARCHAR(32) NOT NULL DEFAULT 'provider', period_start DATETIME NULL, period_end DATETIME NULL, unit VARCHAR(32) NOT NULL DEFAULT 'count', used_quantity DECIMAL(20,4) NULL, limit_quantity DECIMAL(20,4) NULL, remaining_quantity DECIMAL(20,4) NULL, usage_percent DECIMAL(8,4) NULL, status VARCHAR(32) NOT NULL DEFAULT 'unknown', raw_payload_json LONGTEXT NULL, checked_at DATETIME NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_module_usage_snapshots_module_checked (module_key, metric_key, checked_at), KEY idx_module_usage_snapshots_status (status, checked_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" ); $db->query( "CREATE TABLE IF NOT EXISTS module_quota_settings ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, module_key VARCHAR(64) NOT NULL, metric_key VARCHAR(128) NOT NULL, enabled TINYINT(1) NOT NULL DEFAULT 1, enforce_mode VARCHAR(16) NOT NULL DEFAULT 'observe', soft_limit_percent DECIMAL(6,2) NOT NULL DEFAULT 90.00, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uniq_module_quota_setting (module_key, metric_key), KEY idx_module_quota_settings_mode (enforce_mode, enabled) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" ); $db->query( "CREATE TABLE IF NOT EXISTS module_usage_logs ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, module VARCHAR(64) NOT NULL, action VARCHAR(64) NOT NULL, status_code INT NOT NULL DEFAULT 0, data LONGTEXT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_module_usage_logs_module_created (module, created_at), KEY idx_module_usage_logs_action_created (action, created_at), KEY idx_module_usage_logs_status_created (status_code, created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" ); self::ensureColumn('module_quota_settings', 'enabled', "TINYINT(1) NOT NULL DEFAULT 1 AFTER metric_key"); self::ensureColumn('module_quota_settings', 'soft_limit_percent', "DECIMAL(6,2) NOT NULL DEFAULT 90.00 AFTER enforce_mode"); self::$initialized = true; } private static function ensureColumn(string $table, string $column, string $definition): void { global $db; $table = preg_replace('/[^a-zA-Z0-9_]/', '', $table); $column = preg_replace('/[^a-zA-Z0-9_]/', '', $column); if ($table === '' || $column === '') { return; } $result = $db->query("SHOW COLUMNS FROM `{$table}` LIKE '{$column}'"); if ($result && $result->num_rows > 0) { return; } $db->query("ALTER TABLE `{$table}` ADD COLUMN `{$column}` {$definition}"); } }