# TSGame 应用启动流程详解

## 目录

1. [概述](#概述)
2. [启动流程时序](#启动流程时序)
3. [远程配置文件管理](#远程配置文件管理)
   - [3.1 配置文件结构](#31-配置文件结构)
   - [3.2 配置参数获取逻辑](#32-配置参数获取逻辑)
   - [3.3 配置属性详解](#33-配置属性详解)
   - [3.4 应用升级验证详解](#34-应用升级验证详解)
   - [3.5 下载地址获取详解](#35-下载地址获取详解)
   - [3.6 解压路径和启动路径详解](#36-解压路径和启动路径详解)
   - [3.7 大厅跳转子游戏的具体逻辑详解](#37-大厅跳转子游戏的具体逻辑详解)
4. [资源目录和解压路径](#资源目录和解压路径)
   - [4.1 文件系统结构](#41-文件系统结构)
   - [4.2 ZIP包解压路径规范](#42-zip包解压路径规范)
5. [大厅启动逻辑](#大厅启动逻辑)
   - [5.1 大厅资源初始化](#51-大厅资源初始化)
   - [5.2 大厅WebView启动](#52-大厅webview启动)
6. [子游戏跳转启动逻辑](#子游戏跳转启动逻辑)
   - [6.1 子游戏资源管理](#61-子游戏资源管理)
   - [6.2 子游戏WebView切换](#62-子游戏webview切换)
   - [6.3 子游戏启动流程的配置属性读取详解](#63-子游戏启动流程的配置属性读取详解)
   - [6.4 子游戏启动路径的完整解析](#64-子游戏启动路径的完整解析)
7. [版本检查和更新流程](#版本检查和更新流程)
8. [启动异常处理](#启动异常处理)

---

## 概述

TSGame HarmonyOS应用采用分阶段启动流程，通过远程配置文件驱动，支持大厅和子游戏的动态资源管理。应用启动过程包括配置文件获取、资源下载解压、WebView初始化和页面加载等关键步骤。

### 核心特性

- **远程配置驱动**：通过七牛云CDN获取分层配置
- **双WebView架构**：大厅和子游戏独立管理
- **动态资源更新**：支持ZIP包热更新
- **版本检查机制**：自动检测和更新资源版本
- **降级兼容**：配置获取失败时使用本地缓存

---

## 启动流程时序

### 完整启动时序图

```
用户启动App
     ↓
EntryAbility.onCreate()
     ↓
初始化基础管理器
     ↓
ConfigManager.loadStartupConfig() ←→ 加载本地启动配置
     ↓
检测远程配置文件 ←→ 七牛云CDN
     ↓                    ↓
配置文件存在？        下载配置文件
     ↓ (是)               ↓
解析远程配置          校验文件格式
     ↓                    ↓
获取大厅ZIP包URL      保存到本地缓存
     ↓
下载大厅ZIP包 ←→ CDN服务器
     ↓                    ↓
解压大厅资源          验证ZIP包完整性
     ↓
初始化大厅WebView
     ↓
加载大厅HTML页面
     ↓
大厅显示完成
     ↓
用户点击子游戏
     ↓
获取子游戏ZIP包URL
     ↓
验证子游戏ZIP包有效性
     ↓
下载子游戏ZIP包 ←→ CDN服务器
     ↓                    ↓
解压子游戏资源        验证ZIP包完整性
     ↓
初始化子游戏WebView
     ↓
加载子游戏HTML页面
     ↓
子游戏启动完成
```

### 启动阶段详解

#### 阶段1：应用初始化

```typescript
class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    console.log('TSGame应用启动');
    
    // 1. 初始化基础服务
    this.initBaseServices();
    
    // 2. 加载启动配置
    this.loadStartupConfig();
    
    // 3. 启动配置检查流程
    this.startConfigCheckFlow();
  }
  
  private async initBaseServices(): Promise<void> {
    // 初始化文件管理器
    await FileManager.initialize();
    
    // 初始化网络管理器
    await NetworkManager.initialize();
    
    // 初始化配置管理器
    await ConfigManager.initialize();
    
    // 初始化资源管理器
    await ResourceManager.initialize();
  }
}
```

#### 阶段2：配置文件检查

```typescript
class ConfigManager {
  
  /**
   * 启动配置检查流程
   */
  public async startConfigCheckFlow(): Promise<void> {
    try {
      // 1. 加载本地启动配置
      const localConfig = await this.loadLocalStartupConfig();
      
      // 2. 获取远程配置文件
      const remoteConfig = await this.fetchRemoteConfig();
      
      // 3. 比较并更新配置
      const needUpdate = this.compareConfigs(localConfig, remoteConfig);
      
      if (needUpdate) {
        // 4. 更新本地配置缓存
        await this.updateLocalConfig(remoteConfig);
      }
      
      // 5. 启动大厅初始化流程
      await this.startHallInitialization(remoteConfig);
      
    } catch (error) {
      console.error('配置检查失败:', error);
      // 降级到本地配置
      await this.fallbackToLocalConfig();
    }
  }
  
  /**
   * 获取远程配置文件
   */
  private async fetchRemoteConfig(): Promise<RemoteConfig> {
    const gameData = await this.getCurrentGameData();
    const configUrl = this.buildConfigUrl(gameData);
    
    console.log('请求配置文件URL:', configUrl);
    
    const response = await fetch(configUrl + '?t=' + Date.now(), {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        'Cache-Control': 'no-cache'
      },
      timeout: 10000
    });
    
    if (!response.ok) {
      throw new Error(`配置文件获取失败: ${response.status}`);
    }
    
    const configData = await response.json();
    console.log('远程配置获取成功:', configData);
    
    return configData;
  }
}
```

---

## 远程配置文件管理

### 3.1 配置文件结构

#### 配置文件完整JSON结构

```json
{
  "configVersion": "1.0.0",
  "timestamp": 1688544000000,
  "data": {
    "app_download": "https://cdn.jxjygame.com/app/tsgame_v3.6.3.apk",
    "app_version": "3.6.3", 
    "app_size": "25.6MB",
    "game_download": "https://cdn.jxjygame.com/games/default_game.zip",
    "game_version": "1.0.0",
    "game_size": "12.3MB",
    "url": "https://api.jxjygame.com",
    "showmessage": "欢迎使用进贤聚友棋牌",
    "agentlist": [
      {
        "agentid": "agent001",
        "agentname": "代理商A",
        "url": "https://api-agent001.jxjygame.com",
        "app_download": "https://cdn.jxjygame.com/app/agent001_v3.6.3.apk",
        "app_version": "3.6.3",
        "app_size": "25.8MB",
        "game_download": "https://cdn.jxjygame.com/games/agent001_games.zip",
        "game_version": "2.1.0",
        "game_size": "15.2MB",
        "showmessage": "代理商A定制版本",
        "channellist": [
          {
            "channelid": "channel001", 
            "channelname": "华为渠道",
            "app_download": "https://cdn.jxjygame.com/app/huawei_v3.6.3.apk",
            "app_version": "3.6.3",
            "app_size": "25.9MB",
            "game_download": "https://cdn.jxjygame.com/games/huawei_games.zip",
            "game_version": "2.1.1",
            "game_size": "15.5MB",
            "showmessage": "华为渠道专版",
            "marketlist": [
              {
                "marketid": "huawei_appgallery",
                "marketname": "华为应用市场",
                "app_download": "https://cdn.jxjygame.com/app/huawei_market_v3.6.4.apk",
                "app_version": "3.6.4",
                "app_size": "26.1MB",
                "game_download": "https://cdn.jxjygame.com/games/huawei_market_games.zip",
                "game_version": "2.1.2",
                "game_size": "15.8MB",
                "showmessage": "华为应用市场版本"
              }
            ]
          }
        ]
      }
    ]
  }
}
```

#### 配置层级说明

配置文件采用四级层次结构，支持参数覆盖：

1. **全局配置** (data根级别) - 最低优先级
2. **代理商配置** (agentlist) - 覆盖全局配置
3. **渠道配置** (channellist) - 覆盖代理商配置  
4. **市场配置** (marketlist) - 最高优先级，覆盖所有上级配置

### 3.2 配置参数获取逻辑

#### 核心参数获取类

```typescript
/**
 * 配置参数获取工具类
 * 严格按照Android版本的逻辑实现，支持分层参数覆盖
 */
export class ConfigParameterHelper {
  
  /**
   * 根据层级结构获取配置参数值
   * @param config 配置对象
   * @param paramName 参数名称
   * @param gameData 游戏数据上下文
   * @returns 参数值
   */
  public static getParameterValue(config: any, paramName: string, gameData: GameData): any {
    const agentId = gameData.agentId;
    const channelId = gameData.channelId;
    const marketId = gameData.marketId;
    
    let paramValue = null;

    // 内部函数：遍历数组匹配key获取参数值
    const getParameterFromList = (arrayList: any[], keyName: string, keyValue: string): any => {
      if (arrayList && keyName && keyValue) {
        for (let i = 0; i < arrayList.length; i++) {
          if (arrayList[i][keyName] === keyValue) {
            // 如果找到匹配项且包含目标参数，更新参数值
            if (arrayList[i][paramName] !== undefined) {
              paramValue = arrayList[i][paramName];
            }
            return arrayList[i];
          }
        }
      }
      return null;
    };

    // 检查配置数据是否存在
    if (!config || !config.data) {
      return paramValue;
    }

    // 1. 首先检查全局配置（最低优先级）
    if (config.data[paramName] !== undefined) {
      paramValue = config.data[paramName];
    }

    // 2. 按层级查找，后面的层级覆盖前面的：代理商 -> 渠道 -> 市场
    
    // 代理商级别配置
    const agentConfig = getParameterFromList(config.data.agentlist, "agentid", agentId);
    if (!agentConfig) {
      return paramValue; // 如果找不到代理商配置，返回全局配置值
    }

    // 渠道级别配置
    const channelConfig = getParameterFromList(agentConfig.channellist, "channelid", channelId);
    if (!channelConfig) {
      return paramValue; // 如果找不到渠道配置，返回当前参数值
    }

    // 市场级别配置（最高优先级）
    const marketConfig = getParameterFromList(channelConfig.marketlist, "marketid", marketId);
    // 不管是否找到市场配置，都返回当前参数值（可能已被前面层级更新）

    return paramValue;
  }

  /**
   * 获取应用下载地址
   * 对应配置文件中的 app_download 属性
   */
  public static getAppDownloadUrl(config: any, gameData: GameData): string {
    return this.getParameterValue(config, "app_download", gameData) || "";
  }

  /**
   * 获取应用版本号
   * 对应配置文件中的 app_version 属性
   */
  public static getAppVersion(config: any, gameData: GameData): string {
    return this.getParameterValue(config, "app_version", gameData) || "";
  }

  /**
   * 获取游戏ZIP包下载地址
   * 对应配置文件中的 game_download 属性
   */
  public static getGameDownloadUrl(config: any, gameData: GameData): string {
    return this.getParameterValue(config, "game_download", gameData) || "";
  }

  /**
   * 获取游戏版本号
   * 对应配置文件中的 game_version 属性
   */
  public static getGameVersion(config: any, gameData: GameData): string {
    return this.getParameterValue(config, "game_version", gameData) || "";
  }

  /**
   * 获取服务器地址
   * 对应配置文件中的 url 属性
   */
  public static getServerUrl(config: any, gameData: GameData): string {
    return this.getParameterValue(config, "url", gameData) || "";
  }

  /**
   * 获取显示消息
   * 对应配置文件中的 showmessage 属性
   */
  public static getShowMessage(config: any, gameData: GameData): string {
    return this.getParameterValue(config, "showmessage", gameData) || "";
  }
}

/**
 * 游戏数据上下文
 */
export interface GameData {
  agentId: string;    // 代理商ID，从本地配置或启动参数获取
  channelId: string;  // 渠道ID，从本地配置或启动参数获取
  marketId: string;   // 市场ID，从本地配置或启动参数获取
}
```

#### 配置参数获取示例

```typescript
// 配置参数获取的实际使用示例
export class ConfigUsageExample {
  
  public static async demonstrateConfigUsage(): Promise<void> {
    // 1. 获取当前游戏数据上下文
    const gameData: GameData = {
      agentId: "agent001",     // 从本地存储或启动参数获取
      channelId: "channel001", // 从本地存储或启动参数获取
      marketId: "huawei_appgallery" // 从本地存储或启动参数获取
    };

    // 2. 加载远程配置
    const config = await ConfigManager.fetchRemoteConfig();

    // 3. 获取各种配置参数
    const appDownloadUrl = ConfigParameterHelper.getAppDownloadUrl(config, gameData);
    // 实际获取到: "https://cdn.jxjygame.com/app/huawei_market_v3.6.4.apk"
    
    const appVersion = ConfigParameterHelper.getAppVersion(config, gameData);
    // 实际获取到: "3.6.4" (市场级配置覆盖)
    
    const gameDownloadUrl = ConfigParameterHelper.getGameDownloadUrl(config, gameData);
    // 实际获取到: "https://cdn.jxjygame.com/games/huawei_market_games.zip"
    
    const gameVersion = ConfigParameterHelper.getGameVersion(config, gameData);
    // 实际获取到: "2.1.2" (市场级配置覆盖)
    
    const serverUrl = ConfigParameterHelper.getServerUrl(config, gameData);
    // 实际获取到: "https://api-agent001.jxjygame.com" (代理商级配置)
    
    const showMessage = ConfigParameterHelper.getShowMessage(config, gameData);
    // 实际获取到: "华为应用市场版本" (市场级配置覆盖)
    
    console.log('配置参数获取结果:');
    console.log(`App Download URL: ${appDownloadUrl}`);
    console.log(`App Version: ${appVersion}`);
    console.log(`Game Download URL: ${gameDownloadUrl}`);
    console.log(`Game Version: ${gameVersion}`);
    console.log(`Server URL: ${serverUrl}`);
    console.log(`Show Message: ${showMessage}`);
  }
}
```

#### 配置获取的优先级规则

1. **全局配置** (`data`根级别)：作为默认值，优先级最低
2. **代理商配置** (`agentlist[].`)：覆盖全局配置中的同名属性
3. **渠道配置** (`channellist[].`)：覆盖代理商和全局配置中的同名属性
4. **市场配置** (`marketlist[].`)：最高优先级，覆盖所有上级配置中的同名属性

### 3.4 应用升级验证详解

#### 升级验证的具体配置属性

TSGame应用的升级验证基于以下配置属性的组合判断：

```typescript
interface AppUpgradeValidation {
  // 主要验证属性
  app_version: string;        // 远程应用版本号 - 核心验证属性
  app_download: string;       // 应用下载URL - 升级时获取新版本
  app_size: string;          // 应用包大小 - 用于下载进度显示
  showmessage: string;       // 版本更新说明 - 用户升级提示
  
  // 辅助验证属性
  force_update?: boolean;     // 强制更新标志 - 是否必须升级
  min_version?: string;       // 最低支持版本 - 低于此版本强制升级
  update_priority?: number;   // 更新优先级 - 控制更新推送频率
}
```

#### 具体的升级验证逻辑

```typescript
export class AppUpgradeValidator {
  
  /**
   * 验证应用是否需要升级
   * 主要读取配置属性：app_version、force_update、min_version
   */
  public static async validateAppUpgrade(config: any, gameData: any): Promise<UpgradeInfo> {
    // 1. 获取远程版本号 - 读取 app_version 属性
    const remoteVersion = ConfigParameterHelper.getAppVersion(config, gameData);
    console.log(`远程应用版本: ${remoteVersion}`);
    
    // 2. 获取本地应用版本
    const localVersion = await DeviceInfo.getAppVersion();
    console.log(`本地应用版本: ${localVersion}`);
    
    // 3. 版本比较 - 核心验证逻辑
    const needUpdate = VersionComparator.isVersionNewer(remoteVersion, localVersion);
    
    // 4. 检查是否强制更新 - 读取 force_update 属性
    const forceUpdate = ConfigParameterHelper.getBooleanValue(config, gameData, 'force_update', false);
    
    // 5. 检查最低版本要求 - 读取 min_version 属性
    const minVersion = ConfigParameterHelper.getString(config, gameData, 'min_version', '1.0.0');
    const belowMinVersion = VersionComparator.isVersionOlder(localVersion, minVersion);
    
    return {
      needUpdate: needUpdate || belowMinVersion,
      forceUpdate: forceUpdate || belowMinVersion,
      remoteVersion,
      localVersion,
      upgradeType: belowMinVersion ? 'critical' : (forceUpdate ? 'force' : 'optional')
    };
  }
  
  /**
   * 获取应用下载信息
   * 主要读取配置属性：app_download、app_size、showmessage
   */
  public static getAppDownloadInfo(config: any, gameData: any): AppDownloadInfo {
    return {
      // 读取 app_download 属性 - 应用下载地址
      downloadUrl: ConfigParameterHelper.getAppDownloadUrl(config, gameData),
      
      // 读取 app_size 属性 - 应用包大小
      packageSize: ConfigParameterHelper.getAppSize(config, gameData),
      
      // 读取 showmessage 属性 - 更新说明
      upgradeMessage: ConfigParameterHelper.getShowMessage(config, gameData),
      
      // 其他下载相关属性
      md5Hash: ConfigParameterHelper.getString(config, gameData, 'app_md5', ''),
      timeout: ConfigParameterHelper.getNumber(config, gameData, 'download_timeout', 300000)
    };
  }
}
```

### 3.5 下载地址获取详解

#### 应用下载地址的具体获取

```typescript
export class DownloadUrlResolver {
  
  /**
   * 获取应用下载地址
   * 配置属性：app_download
   */
  public static getAppDownloadUrl(config: any, gameData: any): string {
    // 按优先级读取 app_download 属性
    const downloadUrl = ConfigParameterHelper.getAppDownloadUrl(config, gameData);
    
    console.log(`应用下载地址配置路径解析:`);
    console.log(`1. 尝试读取市场级别: marketlist[${gameData.marketid}].app_download`);
    console.log(`2. 尝试读取渠道级别: channellist[${gameData.channelid}].app_download`);
    console.log(`3. 尝试读取代理商级别: agentlist[${gameData.agentid}].app_download`);
    console.log(`4. 尝试读取全局级别: data.app_download`);
    console.log(`最终获取到的下载地址: ${downloadUrl}`);
    
    return downloadUrl;
  }
  
  /**
   * 获取大厅ZIP包下载地址
   * 配置属性：game_download（大厅使用相同的配置）
   */
  public static getHallZipDownloadUrl(config: any, gameData: any): string {
    // 大厅ZIP包使用 game_download 属性
    const hallZipUrl = ConfigParameterHelper.getGameDownloadUrl(config, gameData);
    
    console.log(`大厅ZIP包下载地址: ${hallZipUrl}`);
    return hallZipUrl;
  }
  
  /**
   * 获取子游戏ZIP包下载地址
   * 配置属性：game_download + 游戏ID拼接
   */
  public static getGameZipDownloadUrl(config: any, gameData: any, gameId: string): string {
    // 基础下载地址
    const baseDownloadUrl = ConfigParameterHelper.getGameDownloadUrl(config, gameData);
    
    // 拼接游戏ID构成完整下载地址
    const gameZipUrl = `${baseDownloadUrl}/${gameId}.zip`;
    
    console.log(`子游戏${gameId}的ZIP包下载地址: ${gameZipUrl}`);
    return gameZipUrl;
  }
}
```

### 3.6 解压路径和启动路径详解

#### 大厅ZIP解压路径详解

```typescript
export class HallPathResolver {
  
  /**
   * 大厅ZIP包解压的完整路径规范
   */
  public static getHallPaths(): HallPathInfo {
    const appRootPath = getContext().filesDir;
    
    return {
      // 1. 大厅ZIP包下载临时路径
      downloadTempPath: `${appRootPath}/downloads/hall_temp.zip`,
      
      // 2. 大厅资源解压目标路径
      extractTargetPath: `${appRootPath}/game_resources/hall`,
      
      // 3. 大厅启动入口文件路径
      entryFilePath: `${appRootPath}/game_resources/hall/index.html`,
      
      // 4. 大厅资源验证路径
      versionFilePath: `${appRootPath}/game_resources/hall/version.txt`,
      configFilePath: `${appRootPath}/game_resources/hall/config.json`,
      
      // 5. 大厅WebView加载URL
      webViewLoadUrl: `file://${appRootPath}/game_resources/hall/index.html`
    };
  }
  
  /**
   * 大厅解压流程详解
   */
  public static async extractHallZip(zipFilePath: string): Promise<void> {
    const paths = this.getHallPaths();
    
    console.log(`=== 大厅ZIP包解压流程 ===`);
    console.log(`源文件: ${zipFilePath}`);
    console.log(`目标路径: ${paths.extractTargetPath}`);
    
    // 1. 清理旧的大厅资源
    if (await fileIo.access(paths.extractTargetPath)) {
      await fileIo.rmdir(paths.extractTargetPath, true);
      console.log(`已清理旧大厅资源: ${paths.extractTargetPath}`);
    }
    
    // 2. 创建解压目标目录
    await fileIo.mkdir(paths.extractTargetPath, true);
    
    // 3. 执行ZIP解压
    await ZipExtractor.extractToDirectory(zipFilePath, paths.extractTargetPath);
    
    // 4. 验证关键文件
    await this.validateHallFiles(paths);
    
    console.log(`大厅ZIP包解压完成，启动路径: ${paths.webViewLoadUrl}`);
  }
  
  /**
   * 验证大厅文件完整性
   */
  private static async validateHallFiles(paths: HallPathInfo): Promise<void> {
    const requiredFiles = [
      paths.entryFilePath,     // index.html - 必须存在
      `${paths.extractTargetPath}/js/main.js`,     // 主逻辑文件
      `${paths.extractTargetPath}/css/main.css`    // 主样式文件
    ];
    
    for (const filePath of requiredFiles) {
      if (!await fileIo.access(filePath)) {
        throw new Error(`大厅关键文件缺失: ${filePath}`);
      }
    }
    
    console.log(`大厅文件验证通过`);
  }
}
```

#### 子游戏ZIP解压路径详解

```typescript
export class GamePathResolver {
  
  /**
   * 子游戏ZIP包解压的完整路径规范
   */
  public static getGamePaths(gameId: string): GamePathInfo {
    const appRootPath = getContext().filesDir;
    
    return {
      // 1. 子游戏ZIP包下载临时路径
      downloadTempPath: `${appRootPath}/downloads/${gameId}_temp.zip`,
      
      // 2. 子游戏资源解压目标路径
      extractTargetPath: `${appRootPath}/game_resources/games/${gameId}`,
      
      // 3. 子游戏启动入口文件路径
      entryFilePath: `${appRootPath}/game_resources/games/${gameId}/index.html`,
      
      // 4. 子游戏资源验证路径
      versionFilePath: `${appRootPath}/game_resources/games/${gameId}/version.txt`,
      configFilePath: `${appRootPath}/game_resources/games/${gameId}/game.json`,
      
      // 5. 子游戏WebView加载URL
      webViewLoadUrl: `file://${appRootPath}/game_resources/games/${gameId}/index.html`,
      
      // 6. 子游戏资源子目录
      jsDir: `${appRootPath}/game_resources/games/${gameId}/js`,
      cssDir: `${appRootPath}/game_resources/games/${gameId}/css`,
      imagesDir: `${appRootPath}/game_resources/games/${gameId}/images`,
      audioDir: `${appRootPath}/game_resources/games/${gameId}/audio`
    };
  }
  
  /**
   * 子游戏解压流程详解
   */
  public static async extractGameZip(zipFilePath: string, gameId: string): Promise<void> {
    const paths = this.getGamePaths(gameId);
    
    console.log(`=== 子游戏${gameId}的ZIP包解压流程 ===`);
    console.log(`源文件: ${zipFilePath}`);
    console.log(`目标路径: ${paths.extractTargetPath}`);
    
    // 1. 清理旧的游戏资源
    if (await fileIo.access(paths.extractTargetPath)) {
      await fileIo.rmdir(paths.extractTargetPath, true);
      console.log(`已清理旧游戏${gameId}资源: ${paths.extractTargetPath}`);
    }
    
    // 2. 创建游戏目录结构
    await this.createGameDirectories(paths);
    
    // 3. 执行ZIP解压
    await ZipExtractor.extractToDirectory(zipFilePath, paths.extractTargetPath);
    
    // 4. 验证游戏文件
    await this.validateGameFiles(gameId, paths);
    
    console.log(`子游戏${gameId}解压完成，启动路径: ${paths.webViewLoadUrl}`);
  }
  
  /**
   * 创建游戏目录结构
   */
  private static async createGameDirectories(paths: GamePathInfo): Promise<void> {
    const directories = [
      paths.extractTargetPath,
      paths.jsDir,
      paths.cssDir,
      paths.imagesDir,
      paths.audioDir
    ];
    
    for (const dir of directories) {
      await fileIo.mkdir(dir, true);
    }
    
    console.log(`游戏目录结构创建完成`);
  }
  
  /**
   * 验证游戏文件完整性
   */
  private static async validateGameFiles(gameId: string, paths: GamePathInfo): Promise<void> {
    const requiredFiles = [
      paths.entryFilePath,     // index.html - 游戏入口
      `${paths.jsDir}/game.js`,           // 游戏主逻辑
      `${paths.cssDir}/game.css`          // 游戏样式
    ];
    
    for (const filePath of requiredFiles) {
      if (!await fileIo.access(filePath)) {
        throw new Error(`游戏${gameId}关键文件缺失: ${filePath}`);
      }
    }
    
    console.log(`游戏${gameId}文件验证通过`);
  }
}
```

### 3.7 大厅跳转子游戏的具体逻辑详解

#### 跳转触发和参数传递

```typescript
export class HallGameSwitchController {
  
  /**
   * 大厅跳转子游戏的完整流程
   * 这是从大厅点击游戏到子游戏启动的详细逻辑
   */
  public static async switchFromHallToGame(gameId: string, gameData: any): Promise<void> {
    try {
      console.log(`=== 大厅跳转子游戏流程开始 ===`);
      console.log(`目标游戏ID: ${gameId}`);
      console.log(`游戏数据:`, gameData);
      
      // 第一步：在大厅WebView中接收跳转请求
      await this.handleHallGameClick(gameId, gameData);
      
      // 第二步：检查子游戏资源状态
      const resourceStatus = await this.checkGameResourceStatus(gameId);
      
      // 第三步：根据资源状态决定后续流程
      if (resourceStatus.needDownload) {
        await this.downloadAndPrepareGame(gameId, resourceStatus);
      }
      
      // 第四步：切换WebView并启动子游戏
      await this.switchWebViewToGame(gameId, gameData);
      
      console.log(`=== 大厅跳转子游戏流程完成 ===`);
      
    } catch (error) {
      console.error(`大厅跳转子游戏失败:`, error);
      await this.handleSwitchError(gameId, error);
    }
  }
  
  /**
   * 第一步：处理大厅中的游戏点击事件
   */
  private static async handleHallGameClick(gameId: string, gameData: any): Promise<void> {
    console.log(`=== 第一步：处理大厅游戏点击 ===`);
    
    // 1. 在大厅WebView中触发JSBridge调用
    // 大厅JS代码会调用: window.WebViewJavascriptBridge.callHandler('SwitchOverGameData', {...})
    
    // 2. 验证游戏参数
    this.validateGameSwitchParams(gameId, gameData);
    
    // 3. 记录跳转日志用于统计
    await Logger.logGameSwitch(gameId, 'hall_click', gameData);
    
    // 4. 显示切换加载界面
    await UIController.showGameSwitchLoading(gameId);
    
    console.log(`大厅游戏点击处理完成`);
  }
  
  /**
   * 第二步：检查子游戏资源状态
   */
  private static async checkGameResourceStatus(gameId: string): Promise<GameResourceStatus> {
    console.log(`=== 第二步：检查游戏${gameId}资源状态 ===`);
    
    const paths = GamePathResolver.getGamePaths(gameId);
    const config = await ConfigManager.getRemoteConfig();
    const gameData = await ConfigManager.getCurrentGameData();
    
    // 1. 检查游戏目录是否存在
    const gameExists = await fileIo.access(paths.extractTargetPath);
    console.log(`游戏目录存在: ${gameExists}`);
    
    if (!gameExists) {
      return {
        needDownload: true,
        reason: 'game_not_exists',
        downloadUrl: DownloadUrlResolver.getGameZipDownloadUrl(config, gameData, gameId)
      };
    }
    
    // 2. 检查游戏版本是否需要更新
    const remoteVersion = ConfigParameterHelper.getGameVersion(config, gameData);
    const localVersion = await this.getLocalGameVersion(gameId, paths);
    
    console.log(`远程游戏版本: ${remoteVersion}`);
    console.log(`本地游戏版本: ${localVersion}`);
    
    const needUpdate = VersionComparator.isVersionNewer(remoteVersion, localVersion);
    
    if (needUpdate) {
      return {
        needDownload: true,
        reason: 'version_update',
        downloadUrl: DownloadUrlResolver.getGameZipDownloadUrl(config, gameData, gameId),
        oldVersion: localVersion,
        newVersion: remoteVersion
      };
    }
    
    // 3. 验证游戏文件完整性
    const isValid = await this.validateGameIntegrity(gameId, paths);
    
    if (!isValid) {
      return {
        needDownload: true,
        reason: 'file_corruption',
        downloadUrl: DownloadUrlResolver.getGameZipDownloadUrl(config, gameData, gameId)
      };
    }
    
    console.log(`游戏${gameId}资源检查通过，无需下载`);
    return {
      needDownload: false,
      reason: 'ready',
      launchUrl: paths.webViewLoadUrl
    };
  }
  
  /**
   * 第三步：下载并准备游戏资源（如果需要）
   */
  private static async downloadAndPrepareGame(gameId: string, resourceStatus: GameResourceStatus): Promise<void> {
    console.log(`=== 第三步：下载并准备游戏${gameId}资源 ===`);
    console.log(`下载原因: ${resourceStatus.reason}`);
    console.log(`下载地址: ${resourceStatus.downloadUrl}`);
    
    const paths = GamePathResolver.getGamePaths(gameId);
    
    // 1. 显示下载进度界面
    await UIController.showGameDownloadProgress(gameId, resourceStatus);
    
    // 2. 下载游戏ZIP包
    await FileDownloader.downloadWithProgress(
      resourceStatus.downloadUrl,
      paths.downloadTempPath,
      {
        onProgress: (progress: number, total: number) => {
          const percent = Math.floor((progress / total) * 100);
          UIController.updateGameDownloadProgress(gameId, percent);
        },
        timeout: 300000 // 5分钟超时
      }
    );
    
    console.log(`游戏${gameId}ZIP包下载完成: ${paths.downloadTempPath}`);
    
    // 3. 解压游戏资源
    await GamePathResolver.extractGameZip(paths.downloadTempPath, gameId);
    
    // 4. 清理临时文件
    await fileIo.unlink(paths.downloadTempPath);
    
    // 5. 隐藏下载进度界面
    await UIController.hideGameDownloadProgress(gameId);
    
    console.log(`游戏${gameId}资源准备完成`);
  }
  
  /**
   * 第四步：切换WebView并启动子游戏
   */
  private static async switchWebViewToGame(gameId: string, gameData: any): Promise<void> {
    console.log(`=== 第四步：切换WebView启动游戏${gameId} ===`);
    
    const paths = GamePathResolver.getGamePaths(gameId);
    
    // 1. 隐藏大厅WebView
    await WebViewManager.hideHallWebView();
    console.log(`大厅WebView已隐藏`);
    
    // 2. 初始化子游戏WebView
    await WebViewManager.initGameWebView(gameId);
    console.log(`子游戏WebView初始化完成`);
    
    // 3. 配置子游戏WebView参数
    await this.configureGameWebView(gameId, gameData);
    
    // 4. 加载子游戏页面
    console.log(`开始加载游戏页面: ${paths.webViewLoadUrl}`);
    await WebViewManager.loadGamePage(gameId, paths.webViewLoadUrl);
    
    // 5. 显示子游戏WebView
    await WebViewManager.showGameWebView(gameId);
    console.log(`子游戏WebView已显示`);
    
    // 6. 向子游戏传递启动参数
    await this.passGameStartupParams(gameId, gameData);
    
    // 7. 隐藏加载界面
    await UIController.hideGameSwitchLoading();
    
    console.log(`游戏${gameId}启动完成`);
  }
  
  /**
   * 配置子游戏WebView的特定参数
   */
  private static async configureGameWebView(gameId: string, gameData: any): Promise<void> {
    const webView = WebViewManager.getGameWebView(gameId);
    
    // 1. 设置用户代理
    webView.setUserAgent(`TSGame-HarmonyOS/${await DeviceInfo.getAppVersion()} Game/${gameId}`);
    
    // 2. 设置JavaScript Bridge
    await JSBridgeManager.setupGameBridge(gameId, webView);
    
    // 3. 注册游戏专用接口
    await this.registerGameSpecificHandlers(gameId, webView);
    
    console.log(`游戏${gameId}的WebView配置完成`);
  }
  
  /**
   * 向子游戏传递启动参数
   */
  private static async passGameStartupParams(gameId: string, gameData: any): Promise<void> {
    const webView = WebViewManager.getGameWebView(gameId);
    
    // 构造游戏启动参数
    const startupParams = {
      gameId: gameId,
      userId: gameData.userId,
      userToken: gameData.userToken,
      serverUrl: ConfigParameterHelper.getServerUrl(await ConfigManager.getRemoteConfig(), gameData),
      fromPage: 'hall',
      timestamp: Date.now(),
      deviceInfo: await DeviceInfo.getBasicInfo()
    };
    
    // 通过JSBridge传递参数给游戏
    webView.callHandler('onGameInit', startupParams, (response) => {
      console.log(`游戏${gameId}初始化回调:`, response);
    });
    
    console.log(`游戏${gameId}启动参数传递完成:`, startupParams);
  }
  
  /**
   * 获取本地游戏版本
   */
  private static async getLocalGameVersion(gameId: string, paths: GamePathInfo): Promise<string> {
    try {
      if (await fileIo.access(paths.versionFilePath)) {
        const versionContent = await fileIo.readText(paths.versionFilePath);
        return versionContent.trim();
      }
    } catch (error) {
      console.warn(`读取游戏${gameId}版本失败:`, error);
    }
    return '';
  }
  
  /**
   * 验证游戏文件完整性
   */
  private static async validateGameIntegrity(gameId: string, paths: GamePathInfo): Promise<boolean> {
    try {
      // 检查关键文件是否存在
      const requiredFiles = [
        paths.entryFilePath,
        `${paths.jsDir}/game.js`,
        `${paths.cssDir}/game.css`
      ];
      
      for (const filePath of requiredFiles) {
        if (!await fileIo.access(filePath)) {
          console.warn(`游戏${gameId}关键文件缺失: ${filePath}`);
          return false;
        }
      }
      
      return true;
    } catch (error) {
      console.error(`验证游戏${gameId}完整性失败:`, error);
      return false;
    }
  }
  
  /**
   * 处理切换错误
   */
  private static async handleSwitchError(gameId: string, error: Error): Promise<void> {
    console.error(`游戏${gameId}切换失败:`, error);
    
    // 隐藏加载界面
    await UIController.hideGameSwitchLoading();
    
    // 显示错误提示
    await UIController.showErrorDialog(`游戏启动失败`, error.message);
    
    // 记录错误日志
    await Logger.logError('GAME_SWITCH_ERROR', {
      gameId,
      error: error.message,
      stack: error.stack
    });
  }
}

// 相关数据结构定义
interface GameResourceStatus {
  needDownload: boolean;
  reason: 'game_not_exists' | 'version_update' | 'file_corruption' | 'ready';
  downloadUrl?: string;
  oldVersion?: string;
  newVersion?: string;
  launchUrl?: string;
}

interface HallPathInfo {
  downloadTempPath: string;
  extractTargetPath: string;
  entryFilePath: string;
  versionFilePath: string;
  configFilePath: string;
  webViewLoadUrl: string;
}

interface GamePathInfo {
  downloadTempPath: string;
  extractTargetPath: string;
  entryFilePath: string;
  versionFilePath: string;
  configFilePath: string;
  webViewLoadUrl: string;
  jsDir: string;
  cssDir: string;
  imagesDir: string;
  audioDir: string;
}
```

---

## 资源目录和解压路径

### 4.1 文件系统结构

#### HarmonyOS应用文件系统布局

```
/data/app/el2/100/base/com.jx.jyhd.harmonyos/haps/entry/files/
├── game_resources/                    # 游戏资源根目录
│   ├── hall/                         # 大厅资源目录
│   │   ├── index.html                # 大厅入口文件
│   │   ├── js/                       # 大厅JS文件
│   │   │   ├── main.js              # 大厅主要逻辑
│   │   │   ├── bridge.js            # JSBridge桥接文件
│   │   │   └── utils.js             # 工具函数
│   │   ├── css/                      # 大厅样式文件
│   │   │   ├── main.css             # 主样式
│   │   │   └── responsive.css       # 响应式样式
│   │   ├── images/                   # 大厅图片资源
│   │   │   ├── logo.png             # 应用Logo
│   │   │   ├── bg.jpg              # 背景图片
│   │   │   └── icons/              # 图标文件夹
│   │   └── version.js               # 大厅版本信息
│   ├── games/                        # 子游戏资源目录
│   │   ├── game001/                  # 斗地主游戏
│   │   │   ├── index.html            # 游戏入口文件
│   │   │   ├── js/                   # 游戏JS文件
│   │   │   ├── css/                  # 游戏样式文件
│   │   │   ├── images/               # 游戏图片资源
│   │   │   ├── audio/                # 游戏音频资源
│   │   │   └── version.js           # 游戏版本信息
│   │   ├── game002/                  # 麻将游戏
│   │   │   ├── index.html
│   │   │   ├── js/
│   │   │   ├── css/
│   │   │   ├── images/
│   │   │   ├── audio/
│   │   │   └── version.js
│   │   └── ...                       # 其他子游戏
│   ├── downloads/                    # 临时下载目录
│   │   ├── hall_temp.zip             # 临时大厅ZIP包
│   │   ├── game001_temp.zip          # 临时游戏ZIP包
│   │   └── ...
│   └── cache/                        # 缓存目录
│       ├── images/                   # 图片缓存
│       ├── config/                   # 配置缓存
│       └── temp/                     # 临时文件
├── config_cache/                     # 配置缓存目录
│   ├── remote_config.json            # 远程配置缓存
│   └── startup_config.json           # 启动配置缓存
└── logs/                             # 日志目录
    ├── app.log                       # 应用日志
    ├── download.log                  # 下载日志
    └── error.log                     # 错误日志
```

### 4.2 ZIP包解压路径规范

#### 路径管理类

```typescript
export class PathManager {
  
  /**
   * 获取应用根目录路径
   */
  public static getAppRootPath(): string {
    return getContext().filesDir;
  }
  
  /**
   * 获取游戏资源根目录路径
   */
  public static getGameResourcesPath(): string {
    return `${this.getAppRootPath()}/game_resources`;
  }
  
  /**
   * 获取大厅资源目录路径
   */
  public static getHallResourcePath(): string {
    return `${this.getGameResourcesPath()}/hall`;
  }
  
  /**
   * 获取子游戏资源目录路径
   * @param gameId 游戏ID
   */
  public static getGameResourcePath(gameId: string): string {
    return `${this.getGameResourcesPath()}/games/${gameId}`;
  }
  
  /**
   * 获取下载临时目录路径
   */
  public static getDownloadsPath(): string {
    return `${this.getGameResourcesPath()}/downloads`;
  }
  
  /**
   * 获取大厅ZIP包临时下载路径
   */
  public static getHallZipTempPath(): string {
    return `${this.getDownloadsPath()}/hall_temp.zip`;
  }
  
  /**
   * 获取子游戏ZIP包临时下载路径
   * @param gameId 游戏ID
   */
  public static getGameZipTempPath(gameId: string): string {
    return `${this.getDownloadsPath()}/${gameId}_temp.zip`;
  }
  
  /**
   * 获取配置缓存目录路径
   */
  public static getConfigCachePath(): string {
    return `${this.getAppRootPath()}/config_cache`;
  }
  
  /**
   * 获取远程配置缓存文件路径
   */
  public static getRemoteConfigCachePath(): string {
    return `${this.getConfigCachePath()}/remote_config.json`;
  }
}
```

#### ZIP包解压流程

```typescript
export class ZipExtractor {
  
  /**
   * 解压大厅ZIP包到指定目录
   * @param zipFilePath ZIP包文件路径
   */
  public static async extractHallZip(zipFilePath: string): Promise<void> {
    const targetPath = PathManager.getHallResourcePath();
    
    console.log(`开始解压大厅ZIP包:`);
    console.log(`源文件: ${zipFilePath}`);
    console.log(`目标路径: ${targetPath}`);
    
    // 1. 清理目标目录
    await this.cleanDirectory(targetPath);
    
    // 2. 创建目标目录
    await fileIo.mkdir(targetPath, true);
    
    // 3. 解压ZIP包
    await this.extractZipToDirectory(zipFilePath, targetPath);
    
    // 4. 验证解压结果
    await this.verifyExtractedFiles(targetPath);
    
    console.log('大厅ZIP包解压完成');
  }
  
  /**
   * 解压子游戏ZIP包到指定目录
   * @param zipFilePath ZIP包文件路径
   * @param gameId 游戏ID
   */
  public static async extractGameZip(zipFilePath: string, gameId: string): Promise<void> {
    const targetPath = PathManager.getGameResourcePath(gameId);
    
    console.log(`开始解压游戏ZIP包:`);
    console.log(`游戏ID: ${gameId}`);
    console.log(`源文件: ${zipFilePath}`);
    console.log(`目标路径: ${targetPath}`);
    
    // 1. 清理目标目录
    await this.cleanDirectory(targetPath);
    
    // 2. 创建目标目录
    await fileIo.mkdir(targetPath, true);
    
    // 3. 解压ZIP包
    await this.extractZipToDirectory(zipFilePath, targetPath);
    
    // 4. 验证解压结果
    await this.verifyExtractedFiles(targetPath);
    
    console.log(`游戏${gameId}的ZIP包解压完成`);
  }
  
  /**
   * 清理目录
   */
  private static async cleanDirectory(dirPath: string): Promise<void> {
    if (await fileIo.access(dirPath)) {
      await fileIo.rmdir(dirPath, true);
      console.log(`已清理目录: ${dirPath}`);
    }
  }
  
  /**
   * 执行ZIP包解压
   */
  private static async extractZipToDirectory(zipPath: string, targetPath: string): Promise<void> {
    // 使用HarmonyOS的解压API
    const zipFile = new zlib.ZipFile(zipPath);
    await zipFile.extractAll(targetPath);
    
    console.log(`ZIP包解压完成: ${zipPath} -> ${targetPath}`);
  }
  
  /**
   * 验证解压后的文件
   */
  private static async verifyExtractedFiles(dirPath: string): Promise<void> {
    // 检查必要的文件是否存在
    const indexHtmlPath = `${dirPath}/index.html`;
    if (!await fileIo.access(indexHtmlPath)) {
      throw new Error(`解压验证失败: 缺少index.html文件`);
    }
    
    console.log(`解压文件验证通过: ${dirPath}`);
  }
}
```

---

## 大厅启动逻辑

### 5.1 大厅资源初始化

#### 大厅管理器

```typescript
export class HallManager {
  
  /**
   * 初始化大厅资源
   */
  public static async initializeHall(): Promise<void> {
    try {
      console.log('开始初始化大厅资源');
      
      // 1. 检查大厅资源版本
      const needUpdate = await this.checkHallVersion();
      
      if (needUpdate) {
        // 2. 下载大厅资源
        await this.downloadHallResources();
        
        // 3. 解压大厅资源
        await this.extractHallResources();
      }
      
      // 4. 验证大厅资源完整性
      await this.verifyHallResources();
      
      // 5. 初始化大厅WebView
      await this.initializeHallWebView();
      
      console.log('大厅资源初始化完成');
      
    } catch (error) {
      console.error('大厅资源初始化失败:', error);
      throw error;
    }
  }
  
  /**
   * 检查大厅版本
   */
  private static async checkHallVersion(): Promise<boolean> {
    const config = await ConfigManager.getRemoteConfig();
    const gameData = await ConfigManager.getCurrentGameData();
    
    // 获取远程大厅版本
    const remoteVersion = ConfigParameterHelper.getGameVersion(config, gameData);
    console.log('远程大厅版本:', remoteVersion);
    
    // 获取本地大厅版本
    const localVersion = await this.getLocalHallVersion();
    console.log('本地大厅版本:', localVersion);
    
    // 比较版本
    const needUpdate = VersionComparator.isVersionNewer(remoteVersion, localVersion);
    console.log('大厅需要更新:', needUpdate);
    
    return needUpdate;
  }
  
  /**
   * 下载大厅资源
   */
  private static async downloadHallResources(): Promise<void> {
    const config = await ConfigManager.getRemoteConfig();
    const gameData = await ConfigManager.getCurrentGameData();
    
    // 获取大厅下载URL
    const downloadUrl = ConfigParameterHelper.getGameDownloadUrl(config, gameData);
    console.log('大厅下载URL:', downloadUrl);
    
    if (!downloadUrl) {
      throw new Error('未找到大厅下载URL');
    }
    
    // 下载大厅ZIP包
    const zipPath = PathManager.getHallZipTempPath();
    await FileDownloader.download(downloadUrl, zipPath, {
      onProgress: (progress: number, total: number) => {
        const percent = Math.floor((progress / total) * 100);
        console.log(`大厅下载进度: ${percent}%`);
      }
    });
    
    console.log('大厅ZIP包下载完成:', zipPath);
  }
  
  /**
   * 解压大厅资源
   */
  private static async extractHallResources(): Promise<void> {
    const zipPath = PathManager.getHallZipTempPath();
    
    // 解压到大厅目录
    await ZipExtractor.extractHallZip(zipPath);
    
    // 清理临时ZIP文件
    await fileIo.unlink(zipPath);
    
    console.log('大厅资源解压完成');
  }
  
  /**
   * 获取本地大厅版本
   */
  private static async getLocalHallVersion(): Promise<string> {
    const versionPath = `${PathManager.getHallResourcePath()}/version.txt`;
    
    try {
      if (await fileIo.access(versionPath)) {
        const versionContent = await fileIo.readText(versionPath);
        return versionContent.trim();
      }
    } catch (error) {
      console.warn('读取本地大厅版本失败:', error);
    }
    
    return '';
  }
}
```

### 5.2 大厅WebView启动

#### WebView管理器

```typescript
export class HallWebViewController {
  private webView: WebviewController | null = null;
  
  /**
   * 初始化大厅WebView
   */
  public async initializeHallWebView(): Promise<void> {
    try {
      console.log('开始初始化大厅WebView');
      
      // 1. 创建WebView控制器
      this.webView = new webview.WebviewController();
      
      // 2. 配置WebView设置
      await this.configureWebViewSettings();
      
      // 3. 注册JS Bridge接口
      await this.registerJSBridgeHandlers();
      
      // 4. 加载大厅HTML页面
      await this.loadHallPage();
      
      console.log('大厅WebView初始化完成');
      
    } catch (error) {
      console.error('大厅WebView初始化失败:', error);
      throw error;
    }
  }
  
  /**
   * 配置WebView设置
   */
  private async configureWebViewSettings(): Promise<void> {
    if (!this.webView) return;
    
    // 启用JavaScript
    this.webView.enableJavaScript = true;
    
    // 启用DOM存储
    this.webView.domStorageAccess = true;
    
    // 启用文件访问
    this.webView.fileAccess = true;
    
    // 设置用户代理
    this.webView.userAgent = 'TSGame_HarmonyOS/3.6.0 (HarmonyOS)';
    
    console.log('WebView设置配置完成');
  }
  
  /**
   * 注册JS Bridge接口
   */
  private async registerJSBridgeHandlers(): Promise<void> {
    if (!this.webView) return;
    
    // 注册所有JS Bridge接口
    await JSBridgeManager.registerAllHandlers(this.webView);
    
    console.log('JS Bridge接口注册完成');
  }
  
  /**
   * 加载大厅HTML页面
   */
  private async loadHallPage(): Promise<void> {
    if (!this.webView) return;
    
    const hallIndexPath = `${PathManager.getHallResourcePath()}/index.html`;
    
    // 检查大厅HTML文件是否存在
    if (!await fileIo.access(hallIndexPath)) {
      throw new Error('大厅HTML文件不存在');
    }
    
    // 构建文件URL
    const fileUrl = `file://${hallIndexPath}`;
    console.log('加载大厅页面:', fileUrl);
    
    // 加载页面
    await this.webView.loadUrl(fileUrl);
    
    // 设置页面加载监听
    this.webView.onPageBegin = (event) => {
      console.log('大厅页面开始加载:', event.url);
    };
    
    this.webView.onPageEnd = (event) => {
      console.log('大厅页面加载完成:', event.url);
      this.onHallPageLoadCompleted();
    };
    
    this.webView.onErrorReceive = (event) => {
      console.error('大厅页面加载错误:', event.error);
    };
  }
  
  /**
   * 大厅页面加载完成回调
   */
  private onHallPageLoadCompleted(): void {
    console.log('大厅启动完成，可以接受用户操作');
    
    // 通知应用大厅已就绪
    EventManager.emit('hall_ready');
  }
}
```

---

## 子游戏跳转启动逻辑

### 6.1 子游戏资源管理

#### 子游戏启动的完整时序

```
大厅点击游戏
     ↓
HallWebView.callHandler('SwitchOverGameData') 
     ↓
GameSwitchController.handleGameSwitch()
     ↓
检查子游戏资源状态 ← 读取 game_version 配置属性
     ↓
[如果需要] 下载子游戏ZIP ← 使用 game_download 配置属性 
     ↓
[如果需要] 解压到游戏目录 ← 解压到 /game_resources/games/{gameId}/
     ↓
隐藏大厅WebView，初始化游戏WebView
     ↓
加载游戏页面 ← 加载 file:///game_resources/games/{gameId}/index.html
     ↓
显示游戏WebView，传递启动参数
     ↓
子游戏启动完成
```

#### 子游戏跳转的JSBridge接口调用

```typescript
// 在大厅WebView中的JavaScript代码
function switchToGame(gameInfo) {
    // 调用SwitchOverGameData接口跳转到子游戏
    window.WebViewJavascriptBridge.callHandler('SwitchOverGameData', {
        gameId: gameInfo.gameId,           // 子游戏唯一标识
        gameType: gameInfo.gameType,       // 游戏类型
        gameUrl: gameInfo.gameUrl,         // 游戏访问地址（用于fallback）
        gameName: gameInfo.gameName,       // 游戏显示名称
        gameIcon: gameInfo.gameIcon,       // 游戏图标URL
        gameDesc: gameInfo.gameDesc,       // 游戏描述
        extraData: {                       // 传递给子游戏的额外数据
            userId: currentUser.userId,
            userToken: currentUser.token,
            fromHall: true,
            timestamp: Date.now()
        }
    });
}
```

### 6.2 子游戏WebView切换的详细实现

#### WebView切换管理器的完整实现

```typescript
export class GameWebViewController {
  private static hallWebView: WebviewController | null = null;
  private static gameWebViews: Map<string, WebviewController> = new Map();
  private static currentGameId: string = '';
  
  /**
   * 处理大厅到子游戏的切换
   * 这是SwitchOverGameData接口的具体实现
   */
  public static async handleSwitchOverGameData(params: SwitchGameParams): Promise<void> {
    try {
      console.log(`=== 开始处理游戏切换请求 ===`);
      console.log(`游戏ID: ${params.gameId}`);
      console.log(`游戏参数:`, params);
      
      // 第一阶段：资源检查和准备
      await this.prepareGameResources(params.gameId, params);
      
      // 第二阶段：WebView切换
      await this.performWebViewSwitch(params.gameId, params);
      
      // 第三阶段：游戏启动
      await this.launchGameInWebView(params.gameId, params);
      
      console.log(`=== 游戏切换完成 ===`);
      
    } catch (error) {
      console.error(`游戏切换失败:`, error);
      await this.handleSwitchError(params.gameId, error);
    }
  }
  
  /**
   * 第一阶段：准备游戏资源
   */
  private static async prepareGameResources(gameId: string, params: SwitchGameParams): Promise<void> {
    console.log(`=== 第一阶段：准备游戏${gameId}资源 ===`);
    
    // 1. 获取远程配置，检查版本信息
    const config = await ConfigManager.getRemoteConfig();
    const gameData = await ConfigManager.getCurrentGameData();
    
    // 2. 读取游戏版本配置属性进行版本检查
    const remoteGameVersion = ConfigParameterHelper.getGameVersion(config, gameData);
    console.log(`远程游戏版本: ${remoteGameVersion}`);
    
    // 3. 获取游戏路径信息
    const gamePaths = GamePathResolver.getGamePaths(gameId);
    console.log(`游戏资源路径: ${gamePaths.extractTargetPath}`);
    console.log(`游戏启动路径: ${gamePaths.webViewLoadUrl}`);
    
    // 4. 检查本地游戏资源状态
    const resourceStatus = await this.checkGameResourceStatus(gameId, remoteGameVersion, gamePaths);
    
    // 5. 如果需要下载，执行下载和解压流程
    if (resourceStatus.needDownload) {
      await this.downloadAndExtractGame(gameId, config, gameData, gamePaths);
    }
    
    console.log(`游戏${gameId}资源准备完成`);
  }
  
  /**
   * 检查游戏资源状态的详细逻辑
   */
  private static async checkGameResourceStatus(
    gameId: string, 
    remoteVersion: string, 
    gamePaths: GamePathInfo
  ): Promise<GameResourceStatus> {
    
    // 1. 检查游戏目录是否存在
    const gameExists = await fileIo.access(gamePaths.extractTargetPath);
    if (!gameExists) {
      console.log(`游戏${gameId}目录不存在: ${gamePaths.extractTargetPath}`);
      return { needDownload: true, reason: 'not_exists' };
    }
    
    // 2. 检查游戏入口文件
    const entryExists = await fileIo.access(gamePaths.entryFilePath);
    if (!entryExists) {
      console.log(`游戏${gameId}入口文件不存在: ${gamePaths.entryFilePath}`);
      return { needDownload: true, reason: 'entry_missing' };
    }
    
    // 3. 读取本地版本信息
    const localVersion = await this.getLocalGameVersion(gamePaths.versionFilePath);
    console.log(`本地游戏版本: ${localVersion}`);
    
    // 4. 版本比较
    if (VersionComparator.isVersionNewer(remoteVersion, localVersion)) {
      console.log(`游戏${gameId}需要版本更新: ${localVersion} -> ${remoteVersion}`);
      return { 
        needDownload: true, 
        reason: 'version_update',
        oldVersion: localVersion,
        newVersion: remoteVersion
      };
    }
    
    // 5. 验证关键文件完整性
    const isValid = await this.validateGameFiles(gameId, gamePaths);
    if (!isValid) {
      console.log(`游戏${gameId}文件验证失败`);
      return { needDownload: true, reason: 'file_corruption' };
    }
    
    console.log(`游戏${gameId}资源检查通过`);
    return { needDownload: false, reason: 'ready' };
  }
  
  /**
   * 下载和解压游戏的详细流程
   */
  private static async downloadAndExtractGame(
    gameId: string,
    config: any,
    gameData: any,
    gamePaths: GamePathInfo
  ): Promise<void> {
    
    console.log(`=== 开始下载和解压游戏${gameId} ===`);
    
    // 1. 读取游戏下载地址配置属性
    const gameDownloadUrl = DownloadUrlResolver.getGameZipDownloadUrl(config, gameData, gameId);
    console.log(`游戏下载地址: ${gameDownloadUrl}`);
    
    if (!gameDownloadUrl) {
      throw new Error(`未找到游戏${gameId}的下载地址配置`);
    }
    
    // 2. 显示下载进度界面
    await UIController.showGameDownloadDialog(gameId);
    
    try {
      // 3. 下载游戏ZIP包到临时路径
      console.log(`开始下载到: ${gamePaths.downloadTempPath}`);
      await FileDownloader.downloadWithProgress(
        gameDownloadUrl,
        gamePaths.downloadTempPath,
        {
          onProgress: (loaded: number, total: number) => {
            const percent = Math.floor((loaded / total) * 100);
            UIController.updateGameDownloadProgress(gameId, percent);
            console.log(`游戏${gameId}下载进度: ${percent}%`);
          },
          timeout: 300000 // 5分钟超时
        }
      );
      
      console.log(`游戏${gameId}下载完成`);
      
      // 4. 解压游戏ZIP包到目标路径
      console.log(`开始解压到: ${gamePaths.extractTargetPath}`);
      await GamePathResolver.extractGameZip(gamePaths.downloadTempPath, gameId);
      
      console.log(`游戏${gameId}解压完成`);
      
      // 5. 清理临时下载文件
      await fileIo.unlink(gamePaths.downloadTempPath);
      console.log(`临时文件已清理: ${gamePaths.downloadTempPath}`);
      
    } finally {
      // 6. 隐藏下载进度界面
      await UIController.hideGameDownloadDialog(gameId);
    }
  }
  
  /**
   * 第二阶段：执行WebView切换
   */
  private static async performWebViewSwitch(gameId: string, params: SwitchGameParams): Promise<void> {
    console.log(`=== 第二阶段：执行WebView切换到游戏${gameId} ===`);
    
    // 1. 保存当前大厅状态
    await this.saveHallState();
    
    // 2. 隐藏大厅WebView
    await this.hideHallWebView();
    console.log(`大厅WebView已隐藏`);
    
    // 3. 初始化或获取游戏WebView
    const gameWebView = await this.getOrCreateGameWebView(gameId);
    console.log(`游戏${gameId}的WebView已准备`);
    
    // 4. 配置游戏WebView
    await this.configureGameWebView(gameId, gameWebView, params);
    console.log(`游戏${gameId}的WebView配置完成`);
    
    // 5. 显示游戏WebView
    await this.showGameWebView(gameId);
    console.log(`游戏${gameId}的WebView已显示`);
    
    // 6. 更新当前游戏状态
    this.currentGameId = gameId;
  }
  
  /**
   * 第三阶段：在WebView中启动游戏
   */
  private static async launchGameInWebView(gameId: string, params: SwitchGameParams): Promise<void> {
    console.log(`=== 第三阶段：在WebView中启动游戏${gameId} ===`);
    
    const gamePaths = GamePathResolver.getGamePaths(gameId);
    const gameWebView = this.gameWebViews.get(gameId);
    
    if (!gameWebView) {
      throw new Error(`游戏${gameId}的WebView未找到`);
    }
    
    // 1. 加载游戏页面
    console.log(`加载游戏页面: ${gamePaths.webViewLoadUrl}`);
    await gameWebView.loadUrl(gamePaths.webViewLoadUrl);
    
    // 2. 等待页面加载完成
    await this.waitForGamePageReady(gameId, gameWebView);
    
    // 3. 设置游戏启动参数
    const startupParams = await this.buildGameStartupParams(gameId, params);
    console.log(`游戏启动参数:`, startupParams);
    
    // 4. 通过JSBridge传递启动参数
    await this.passStartupParamsToGame(gameId, gameWebView, startupParams);
    
    // 5. 通知游戏初始化完成
    await this.notifyGameInitComplete(gameId, gameWebView);
    
    console.log(`游戏${gameId}在WebView中启动完成`);
  }
  
  /**
   * 获取或创建游戏WebView
   */
  private static async getOrCreateGameWebView(gameId: string): Promise<WebviewController> {
    let gameWebView = this.gameWebViews.get(gameId);
    
    if (!gameWebView) {
      console.log(`创建新的游戏${gameId}WebView`);
      
      // 创建新的WebView实例
      gameWebView = new webview.WebviewController();
      
      // 基础配置
      gameWebView.setJavaScriptEnabled(true);
      gameWebView.setDomStorageEnabled(true);
      gameWebView.setFileAccessEnabled(true);
      gameWebView.setAllowFileAccessFromFileURLs(true);
      
      // 设置用户代理
      const userAgent = `TSGame-HarmonyOS/${await DeviceInfo.getAppVersion()} Game/${gameId}`;
      gameWebView.setUserAgent(userAgent);
      
      // 缓存WebView实例
      this.gameWebViews.set(gameId, gameWebView);
      
      console.log(`游戏${gameId}WebView创建完成`);
    }
    
    return gameWebView;
  }
  
  /**
   * 配置游戏WebView的专用设置
   */
  private static async configureGameWebView(
    gameId: string, 
    gameWebView: WebviewController, 
    params: SwitchGameParams
  ): Promise<void> {
    
    // 1. 设置JSBridge
    await JSBridgeManager.setupGameJSBridge(gameId, gameWebView);
    console.log(`游戏${gameId}的JSBridge设置完成`);
    
    // 2. 注册游戏专用的Handler
    await this.registerGameSpecificHandlers(gameId, gameWebView);
    
    // 3. 设置WebView事件监听
    this.setupGameWebViewListeners(gameId, gameWebView);
    
    // 4. 配置游戏特定的WebView选项
    await this.applyGameSpecificWebViewConfig(gameId, gameWebView, params);
  }
  
  /**
   * 构建游戏启动参数
   */
  private static async buildGameStartupParams(gameId: string, params: SwitchGameParams): Promise<any> {
    const config = await ConfigManager.getRemoteConfig();
    const gameData = await ConfigManager.getCurrentGameData();
    const userInfo = await UserManager.getCurrentUserInfo();
    
    return {
      // 游戏基础信息
      gameId: gameId,
      gameName: params.gameName,
      gameType: params.gameType,
      
      // 用户信息
      userId: userInfo.userId,
      userToken: userInfo.token,
      nickname: userInfo.nickname,
      avatar: userInfo.avatar,
      coins: userInfo.coins,
      level: userInfo.level,
      
      // 服务器配置
      serverUrl: ConfigParameterHelper.getServerUrl(config, gameData),
      apiVersion: 'v1.0',
      
      // 启动上下文
      fromPage: 'hall',
      timestamp: Date.now(),
      sessionId: generateSessionId(),
      
      // 设备信息
      deviceInfo: await DeviceInfo.getBasicInfo(),
      
      // 传递的额外数据
      extraData: params.extraData || {}
    };
  }
  
  /**
   * 通过JSBridge传递启动参数给游戏
   */
  private static async passStartupParamsToGame(
    gameId: string,
    gameWebView: WebviewController,
    startupParams: any
  ): Promise<void> {
    
    return new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error(`游戏${gameId}启动参数传递超时`));
      }, 10000);
      
      // 调用游戏的初始化Handler
      gameWebView.callHandler('onGameInit', startupParams, (response) => {
        clearTimeout(timeout);
        
        console.log(`游戏${gameId}初始化响应:`, response);
        
        if (response && response.success) {
          resolve();
        } else {
          reject(new Error(`游戏${gameId}初始化失败: ${response?.message || '未知错误'}`));
        }
      });
    });
  }
  
  /**
   * 等待游戏页面加载完成
   */
  private static async waitForGamePageReady(gameId: string, gameWebView: WebviewController): Promise<void> {
    return new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error(`游戏${gameId}页面加载超时`));
      }, 30000);
      
      // 监听页面加载完成事件
      gameWebView.setOnPageFinished((url) => {
        clearTimeout(timeout);
        console.log(`游戏${gameId}页面加载完成: ${url}`);
        resolve();
      });
      
      // 监听页面加载错误
      gameWebView.setOnPageError((error) => {
        clearTimeout(timeout);
        reject(new Error(`游戏${gameId}页面加载失败: ${error.description}`));
      });
    });
  }
  
  /**
   * 隐藏大厅WebView
   */
  private static async hideHallWebView(): Promise<void> {
    if (this.hallWebView) {
      // 通知大厅页面即将隐藏
      this.hallWebView.callHandler('onPagePause', { reason: 'game_switch' });
      
      // 隐藏大厅WebView UI
      await UIController.hideHallWebView();
    }
  }
  
  /**
   * 显示游戏WebView
   */
  private static async showGameWebView(gameId: string): Promise<void> {
    // 显示游戏WebView UI
    await UIController.showGameWebView(gameId);
    
    // 通知游戏页面已显示
    const gameWebView = this.gameWebViews.get(gameId);
    if (gameWebView) {
      gameWebView.callHandler('onPageResume', { 
        fromBackground: false,
        reason: 'game_start' 
      });
    }
  }
  
  /**
   * 游戏返回大厅的逻辑
   */
  public static async switchBackToHall(gameId: string): Promise<void> {
    console.log(`=== 游戏${gameId}返回大厅 ===`);
    
    // 1. 隐藏游戏WebView
    await UIController.hideGameWebView(gameId);
    
    // 2. 通知游戏页面暂停
    const gameWebView = this.gameWebViews.get(gameId);
    if (gameWebView) {
      gameWebView.callHandler('onPagePause', { reason: 'back_to_hall' });
    }
    
    // 3. 显示大厅WebView
    await UIController.showHallWebView();
    
    // 4. 通知大厅页面恢复
    if (this.hallWebView) {
      this.hallWebView.callHandler('onPageResume', { 
        fromBackground: false,
        reason: 'back_from_game',
        gameId: gameId
      });
    }
    
    // 5. 更新当前状态
    this.currentGameId = '';
    
    console.log(`已返回大厅`);
  }
  
  /**
   * 处理切换错误
   */
  private static async handleSwitchError(gameId: string, error: Error): Promise<void> {
    console.error(`游戏${gameId}切换失败:`, error);
    
    // 记录错误日志
    await Logger.logError('GAME_SWITCH_ERROR', {
      gameId,
      error: error.message,
      stack: error.stack,
      timestamp: Date.now()
    });
    
    // 隐藏可能的加载界面
    await UIController.hideGameDownloadDialog(gameId);
    
    // 显示错误提示
    await UIController.showErrorDialog(`游戏启动失败`, `${error.message}\n\n请稍后重试或联系客服。`);
    
    // 确保回到大厅状态
    await this.switchBackToHall(gameId);
  }
}

// 辅助数据结构
interface SwitchGameParams {
  gameId: string;
  gameType: string;
  gameUrl: string;
  gameName: string;
  gameIcon: string;
  gameDesc?: string;
  extraData?: any;
}

interface GameResourceStatus {
  needDownload: boolean;
  reason: 'not_exists' | 'entry_missing' | 'version_update' | 'file_corruption' | 'ready';
  oldVersion?: string;
  newVersion?: string;
}
```

### 6.3 子游戏启动流程的配置属性读取详解

#### 启动过程中读取的具体配置属性

```typescript
export class GameStartupConfigReader {
  
  /**
   * 子游戏启动过程中需要读取的所有配置属性
   */
  public static async readGameStartupConfig(gameId: string): Promise<GameStartupConfig> {
    const config = await ConfigManager.getRemoteConfig();
    const gameData = await ConfigManager.getCurrentGameData();
    
    console.log(`=== 读取游戏${gameId}启动配置 ===`);
    
    return {
      // 版本相关配置（用于资源检查）
      gameVersion: ConfigParameterHelper.getGameVersion(config, gameData),
      
      // 下载相关配置（用于资源下载）  
      gameDownloadUrl: ConfigParameterHelper.getGameDownloadUrl(config, gameData),
      gameSize: ConfigParameterHelper.getGameSize(config, gameData),
      
      // 服务器相关配置（传递给游戏用于网络请求）
      serverUrl: ConfigParameterHelper.getServerUrl(config, gameData),
      apiEndpoint: ConfigParameterHelper.getString(config, gameData, 'api_endpoint', '/api'),
      
      // 功能开关配置
      enableAudio: ConfigParameterHelper.getBoolean(config, gameData, 'enable_audio', true),
      enableVibration: ConfigParameterHelper.getBoolean(config, gameData, 'enable_vibration', true),
      
      // 游戏专用配置
      maxPlayers: ConfigParameterHelper.getNumber(config, gameData, 'max_players', 4),
      gameTimeout: ConfigParameterHelper.getNumber(config, gameData, 'game_timeout', 300),
      
      // 调试配置
      debugMode: ConfigParameterHelper.getBoolean(config, gameData, 'debug_mode', false),
      logLevel: ConfigParameterHelper.getString(config, gameData, 'log_level', 'info')
    };
  }
  
  /**
   * 打印配置读取详细信息
   */
  public static logConfigDetails(gameId: string, config: any, gameData: any): void {
    console.log(`=== 游戏${gameId}配置读取详情 ===`);
    
    // 显示配置文件层级结构
    console.log(`配置文件层级信息:`);
    console.log(`- 代理商ID: ${gameData.agentid}`);
    console.log(`- 渠道ID: ${gameData.channelid}`);
    console.log(`- 市场ID: ${gameData.marketid}`);
    
    // 显示版本配置读取路径
    console.log(`版本配置读取路径:`);
    this.logConfigPath('game_version', config, gameData);
    
    // 显示下载配置读取路径
    console.log(`下载配置读取路径:`);
    this.logConfigPath('game_download', config, gameData);
    
    // 显示服务器配置读取路径
    console.log(`服务器配置读取路径:`);
    this.logConfigPath('url', config, gameData);
  }
  
  /**
   * 记录具体配置属性的读取路径
   */
  private static logConfigPath(propertyName: string, config: any, gameData: any): void {
    const paths = [
      `marketlist[${gameData.marketid}].${propertyName}`,
      `channellist[${gameData.channelid}].${propertyName}`,
      `agentlist[${gameData.agentid}].${propertyName}`,
      `data.${propertyName}`
    ];
    
    for (const path of paths) {
      const value = this.getValueByPath(config, path);
      if (value !== undefined) {
        console.log(`  ✓ ${path} = ${value}`);
        break;
      } else {
        console.log(`  ✗ ${path} = undefined`);
      }
    }
  }
  
  /**
   * 根据路径获取配置值
   */
  private static getValueByPath(config: any, path: string): any {
    const parts = path.split('.');
    let current = config;
    
    for (const part of parts) {
      if (part.includes('[') && part.includes(']')) {
        // 处理数组索引，如 marketlist[market001]
        const arrayName = part.substring(0, part.indexOf('['));
        const index = part.substring(part.indexOf('[') + 1, part.indexOf(']'));
        
        if (current[arrayName] && Array.isArray(current[arrayName])) {
          current = current[arrayName].find((item: any) => 
            item.marketid === index || item.channelid === index || item.agentid === index
          );
        } else {
          return undefined;
        }
      } else {
        current = current[part];
      }
      
      if (current === undefined) {
        return undefined;
      }
    }
    
    return current;
  }
}

interface GameStartupConfig {
  gameVersion: string;
  gameDownloadUrl: string;
  gameSize: string;
  serverUrl: string;
  apiEndpoint: string;
  enableAudio: boolean;
  enableVibration: boolean;
  maxPlayers: number;
  gameTimeout: number;
  debugMode: boolean;
  logLevel: string;
}
```

### 6.4 子游戏启动路径的完整解析

#### 游戏启动路径的构建和验证

```typescript
export class GameLaunchPathResolver {
  
  /**
   * 解析游戏的完整启动路径
   */
  public static resolveGameLaunchPaths(gameId: string): GameLaunchPaths {
    const appRoot = getContext().filesDir;
    
    const paths = {
      // 基础路径
      appRootPath: appRoot,
      gameRootPath: `${appRoot}/game_resources/games/${gameId}`,
      
      // 启动相关路径
      entryHtmlPath: `${appRoot}/game_resources/games/${gameId}/index.html`,
      webViewLoadUrl: `file://${appRoot}/game_resources/games/${gameId}/index.html`,
      
      // 资源路径
      jsPath: `${appRoot}/game_resources/games/${gameId}/js`,
      cssPath: `${appRoot}/game_resources/games/${gameId}/css`,
      imagesPath: `${appRoot}/game_resources/games/${gameId}/images`,
      audioPath: `${appRoot}/game_resources/games/${gameId}/audio`,
      
      // 配置和数据路径
      gameConfigPath: `${appRoot}/game_resources/games/${gameId}/game.json`,
      versionFilePath: `${appRoot}/game_resources/games/${gameId}/version.txt`,
      saveDataPath: `${appRoot}/game_resources/games/${gameId}/savedata`,
      
      // 临时和缓存路径
      tempPath: `${appRoot}/game_resources/games/${gameId}/temp`,
      cachePath: `${appRoot}/game_resources/games/${gameId}/cache`
    };
    
    console.log(`=== 游戏${gameId}启动路径解析 ===`);
    console.log(`游戏根目录: ${paths.gameRootPath}`);
    console.log(`启动入口: ${paths.entryHtmlPath}`);
    console.log(`WebView加载URL: ${paths.webViewLoadUrl}`);
    
    return paths;
  }
  
  /**
   * 验证游戏启动路径的有效性
   */
  public static async validateGameLaunchPaths(gameId: string, paths: GameLaunchPaths): Promise<ValidationResult> {
    console.log(`=== 验证游戏${gameId}启动路径 ===`);
    
    const validationResult: ValidationResult = {
      isValid: true,
      errors: [],
      warnings: []
    };
    
    // 1. 验证游戏根目录
    if (!await fileIo.access(paths.gameRootPath)) {
      validationResult.isValid = false;
      validationResult.errors.push(`游戏根目录不存在: ${paths.gameRootPath}`);
    }
    
    // 2. 验证启动入口文件
    if (!await fileIo.access(paths.entryHtmlPath)) {
      validationResult.isValid = false;
      validationResult.errors.push(`游戏入口文件不存在: ${paths.entryHtmlPath}`);
    }
    
    // 3. 验证关键资源目录
    const requiredDirs = [paths.jsPath, paths.cssPath];
    for (const dir of requiredDirs) {
      if (!await fileIo.access(dir)) {
        validationResult.warnings.push(`资源目录不存在: ${dir}`);
      }
    }
    
    // 4. 验证版本文件
    if (!await fileIo.access(paths.versionFilePath)) {
      validationResult.warnings.push(`版本文件不存在: ${paths.versionFilePath}`);
    }
    
    console.log(`游戏${gameId}路径验证结果:`, validationResult);
    return validationResult;
  }
  
  /**
   * 创建游戏启动所需的目录结构
   */
  public static async createGameDirectoryStructure(gameId: string, paths: GameLaunchPaths): Promise<void> {
    console.log(`=== 创建游戏${gameId}目录结构 ===`);
    
    const directories = [
      paths.gameRootPath,
      paths.jsPath,
      paths.cssPath,
      paths.imagesPath,
      paths.audioPath,
      paths.saveDataPath,
      paths.tempPath,
      paths.cachePath
    ];
    
    for (const dir of directories) {
      try {
        await fileIo.mkdir(dir, true);
        console.log(`目录创建成功: ${dir}`);
      } catch (error) {
        console.warn(`目录创建失败: ${dir}`, error);
      }
    }
  }
}

interface GameLaunchPaths {
  appRootPath: string;
  gameRootPath: string;
  entryHtmlPath: string;
  webViewLoadUrl: string;
  jsPath: string;
  cssPath: string;
  imagesPath: string;
  audioPath: string;
  gameConfigPath: string;
  versionFilePath: string;
  saveDataPath: string;
  tempPath: string;
  cachePath: string;
}

interface ValidationResult {
  isValid: boolean;
  errors: string[];
  warnings: string[];
}
```

---

## 总结

TSGame HarmonyOS应用的启动流程采用分阶段、可恢复的设计理念，通过精确的配置属性读取和路径管理实现应用和游戏的动态更新。

### 关键配置属性汇总

#### 应用升级验证的配置属性

| 配置属性 | 读取方法 | 作用说明 | 使用时机 |
|---------|----------|----------|----------|
| `app_version` | `ConfigParameterHelper.getAppVersion()` | **主要验证属性** - 远程应用版本号，与本地版本比较判断是否需要升级 | 应用启动时版本检查 |
| `app_download` | `ConfigParameterHelper.getAppDownloadUrl()` | **下载地址属性** - 应用APK/APP安装包下载URL | 确认需要升级时获取下载地址 |
| `app_size` | `ConfigParameterHelper.getAppSize()` | 应用安装包大小，用于显示下载进度和预估时间 | 下载过程中显示进度 |
| `force_update` | `ConfigParameterHelper.getBooleanValue()` | 强制更新标志，控制是否必须升级 | 版本检查时确定升级策略 |
| `min_version` | `ConfigParameterHelper.getString()` | 最低支持版本，低于此版本强制升级 | 版本检查时确定升级策略 |
| `showmessage` | `ConfigParameterHelper.getShowMessage()` | 版本更新说明，向用户展示升级内容 | 升级提示对话框显示 |

#### 游戏资源管理的配置属性

| 配置属性 | 读取方法 | 作用说明 | 使用时机 |
|---------|----------|----------|----------|
| `game_version` | `ConfigParameterHelper.getGameVersion()` | **主要验证属性** - 远程游戏资源版本号，与本地比较判断是否需要更新 | 游戏启动前资源检查 |
| `game_download` | `ConfigParameterHelper.getGameDownloadUrl()` | **下载地址属性** - 游戏ZIP包基础下载URL，拼接gameId构成完整下载地址 | 需要下载游戏资源时 |
| `game_size` | `ConfigParameterHelper.getGameSize()` | 游戏ZIP包大小，用于下载进度显示 | 游戏资源下载过程中 |

#### 服务器和功能配置属性

| 配置属性 | 读取方法 | 作用说明 | 使用时机 |
|---------|----------|----------|----------|
| `url` | `ConfigParameterHelper.getServerUrl()` | **服务器地址属性** - API服务器基础URL，传递给游戏用于网络请求 | 游戏启动时传递给WebView |
| `api_endpoint` | `ConfigParameterHelper.getString()` | API端点路径，拼接到服务器地址后 | 构建完整API地址 |
| `enable_audio` | `ConfigParameterHelper.getBoolean()` | 音频功能开关，控制游戏是否启用音频 | 游戏初始化时配置 |
| `enable_vibration` | `ConfigParameterHelper.getBoolean()` | 震动功能开关，控制游戏是否启用震动反馈 | 游戏初始化时配置 |

### 关键路径汇总

#### 大厅相关路径

| 路径类型 | 具体路径 | 用途说明 |
|---------|----------|----------|
| **大厅ZIP下载临时路径** | `{filesDir}/downloads/hall_temp.zip` | 大厅ZIP包下载的临时存储位置 |
| **大厅资源解压目标路径** | `{filesDir}/game_resources/hall/` | 大厅ZIP包解压后的资源存放目录 |
| **大厅启动入口路径** | `{filesDir}/game_resources/hall/index.html` | 大厅WebView加载的HTML入口文件 |
| **大厅WebView加载URL** | `file://{filesDir}/game_resources/hall/index.html` | 大厅WebView实际加载的file://协议URL |

#### 子游戏相关路径

| 路径类型 | 具体路径模板 | 用途说明 |
|---------|-------------|----------|
| **子游戏ZIP下载临时路径** | `{filesDir}/downloads/{gameId}_temp.zip` | 子游戏ZIP包下载的临时存储位置 |
| **子游戏资源解压目标路径** | `{filesDir}/game_resources/games/{gameId}/` | 子游戏ZIP包解压后的资源存放目录 |
| **子游戏启动入口路径** | `{filesDir}/game_resources/games/{gameId}/index.html` | 子游戏WebView加载的HTML入口文件 |
| **子游戏WebView加载URL** | `file://{filesDir}/game_resources/games/{gameId}/index.html` | 子游戏WebView实际加载的file://协议URL |
| **子游戏JS资源路径** | `{filesDir}/game_resources/games/{gameId}/js/` | 子游戏JavaScript文件存放目录 |
| **子游戏CSS资源路径** | `{filesDir}/game_resources/games/{gameId}/css/` | 子游戏CSS样式文件存放目录 |
| **子游戏图片资源路径** | `{filesDir}/game_resources/games/{gameId}/images/` | 子游戏图片资源存放目录 |
| **子游戏音频资源路径** | `{filesDir}/game_resources/games/{gameId}/audio/` | 子游戏音频文件存放目录 |

#### 配置和缓存路径

| 路径类型 | 具体路径 | 用途说明 |
|---------|----------|----------|
| **远程配置缓存路径** | `{filesDir}/config_cache/remote_config.json` | 远程配置文件的本地缓存 |
| **启动配置缓存路径** | `{filesDir}/config_cache/startup_config.json` | 应用启动配置的本地缓存 |

### 大厅跳转子游戏的完整流程

#### 第一阶段：接收跳转请求
1. **触发方式**：大厅WebView中JavaScript调用 `window.WebViewJavascriptBridge.callHandler('SwitchOverGameData', {...})`
2. **参数验证**：验证gameId、gameName等必要参数
3. **显示加载**：显示游戏切换加载界面

#### 第二阶段：资源检查和准备
1. **读取配置**：
   - 读取 `game_version` 属性进行版本比较
   - 读取 `game_download` 属性获取下载地址
   - 读取 `game_size` 属性用于进度显示

2. **资源检查**：
   - 检查游戏目录：`{filesDir}/game_resources/games/{gameId}/`
   - 检查入口文件：`{filesDir}/game_resources/games/{gameId}/index.html`
   - 读取本地版本：`{filesDir}/game_resources/games/{gameId}/version.txt`

3. **下载解压**（如需要）：
   - 下载到临时路径：`{filesDir}/downloads/{gameId}_temp.zip`
   - 解压到目标路径：`{filesDir}/game_resources/games/{gameId}/`
   - 清理临时文件

#### 第三阶段：WebView切换
1. **隐藏大厅**：调用大厅WebView的 `onPagePause` 事件
2. **初始化游戏WebView**：创建或获取gameId对应的WebView实例
3. **配置JSBridge**：设置游戏专用的JavaScript Bridge接口
4. **显示游戏WebView**：将游戏WebView设置为可见状态

#### 第四阶段：游戏启动
1. **加载游戏页面**：WebView加载 `file://{filesDir}/game_resources/games/{gameId}/index.html`
2. **传递启动参数**：
   - 用户信息（userId、token、coins等）
   - 服务器配置（从 `url` 属性读取）
   - 游戏配置（从其他配置属性读取）
3. **调用游戏初始化**：通过JSBridge调用游戏的 `onGameInit` 接口
4. **隐藏加载界面**：游戏初始化完成后隐藏加载界面

### 配置属性读取的层级优先级

TSGame应用的配置读取遵循严格的层级优先级规则：

```
市场级别配置 (最高优先级)
   ↓ 覆盖
渠道级别配置
   ↓ 覆盖  
代理商级别配置
   ↓ 覆盖
全局级别配置 (最低优先级，默认值)
```

#### 具体的配置路径读取顺序

以 `app_version` 配置属性为例：

1. **第一优先级**：`marketlist[{marketid}].app_version`
2. **第二优先级**：`channellist[{channelid}].app_version`  
3. **第三优先级**：`agentlist[{agentid}].app_version`
4. **第四优先级**：`data.app_version`（全局默认值）

### 核心特性总结

1. **配置驱动启动**：所有启动决策基于远程配置文件的具体属性值
2. **分层配置管理**：支持全局、代理商、渠道、市场四级配置覆盖机制
3. **精确路径管理**：每个资源都有明确的下载路径、解压路径和启动路径
4. **双WebView架构**：大厅和子游戏完全独立的WebView实例和资源管理
5. **版本检查机制**：基于 `app_version` 和 `game_version` 的自动版本检测
6. **异常降级处理**：配置获取失败、资源下载失败等多重降级策略

### 开发者要点

1. **配置属性命名**：所有配置属性名称必须与Android版本保持一致
2. **路径兼容性**：文件路径需要适配HarmonyOS的沙盒文件系统
3. **权限申请**：文件读写、网络访问等权限需要动态申请
4. **性能优化**：WebView初始化、资源解压等耗时操作需要异步处理
5. **错误处理**：每个环节都需要完善的错误处理和用户提示机制

这个启动流程设计确保了TSGame应用在HarmonyOS平台上的高可用性、可维护性和用户体验的一致性。通过精确的配置管理和路径规范，实现了与Android版本完全一致的功能特性。
