# 架构设计

## 整体架构

```text
┌────────────────────────────────────────────────────────────┐
│ apps/aixue      apps/test      apps/yunxiao      apps/... │  品牌应用层
│ main.dart 只负责传入 ShellEnvironment                      │
├────────────────────────────────────────────────────────────┤
│                      web_shell_core                        │  核心库
│  ┌──────────┐ ┌──────────┐ ┌──────────┐                   │
│  │  config  │ │  engine  │ │  bridge  │                   │
│  │ 启动配置  │ │ 兼容引擎  │ │ JS 桥接  │                   │
│  └──────────┘ └──────────┘ └──────────┘                   │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐                   │
│  │ services │ │    ui    │ │ testing  │                   │
│  │ 宿主服务  │ │ 壳层界面  │ │ 测试钩子  │                   │
│  └──────────┘ └──────────┘ └──────────┘                   │
├────────────────────────────────────────────────────────────┤
│ CoreShellActivity (Java)                                   │  原生层
│ 进程隔离 · WebView 信息查询 · 深度重置                        │
└────────────────────────────────────────────────────────────┘
```

## 配置分层

### 启动配置

启动配置负责影响首屏加载行为，当前拆成两层：

1. 本地默认启动配置：`assets/config/bootstrap.json`
2. 远程启动配置：`bootstrap_config_url`

字段示例：

```json
{
  "initialUrl": "https://example.com/login",
  "preferredOrientations": ["portraitUp", "portraitDown"]
}
```

### 升级配置

升级配置仅用于版本检查，不参与首页地址解析，单独由 `upgrade_config_url` 拉取。

字段示例：

```json
{
  "versionName": "1.0.1",
  "version": 101,
  "isForce": 0,
  "remark": "1. 修复已知问题",
  "filePath": "https://example.com/app-release.apk",
  "fileSize": 25000
}
```

## 核心流程

### 1. 启动流程

```text
main()
  → runShellApp(env)
    → WidgetsFlutterBinding.ensureInitialized()
    → 读取本地默认启动配置 bootstrapConfigAsset
    → 拉取远程启动配置 bootstrapConfigUrl（可选，失败走缓存）
    → 合并 initialUrl / preferredOrientations
    → setupConfigUrl(upgradeConfigUrl)
    → _initializeUrls()
    → SystemChrome.setPreferredOrientations(...)
    → 进入沉浸式模式
    → runApp(ShellApp)
      → Android   → WebShellPage
      → 非 Android → UnsupportedPlatformPage
```

### 2. WebView 启动与恢复

```text
WebShellPage.initState()
  → 查询 Android WebView 信息（SDK / 包名 / 版本号）
  → 生成兼容性策略（renderModes / useWideViewPort / aggressiveRecovery）
  → 创建 WebView
  → 首帧后加载 _initialUrl
  → 同时异步执行 checkVersion(context)
  → 主帧成功 → 注入 Bridge
  → 主帧失败 → 展示 ErrorOverlay，不再白屏
  → 启动超时 → 切换渲染模式 / 深度清理 / 自动重试
```

### 3. JS Bridge 协议

```text
H5 页面                         Flutter 壳
  │                                │
  │ AppShellChannel.postMessage    │
  │──────────────────────────────→ │  解析 action + payload
  │                                │  执行 handler
  │ __appShellReceiveResponse      │
  │←────────────────────────────── │  返回 { requestId, success, data/error }
```

当前支持 12 个 Action：

| Action | 说明 |
|---|---|
| `pickImage` | 从图库选图 |
| `captureImage` | 相机拍照 |
| `pickFile` | 文件选择 |
| `openExternal` | 打开外部应用 |
| `requestPermissions` | 请求系统权限 |
| `reloadPage` | 重新加载当前页面 |
| `goBack` | 返回上一页 |
| `closeApp` | 关闭应用 |
| `getDeviceInfo` | 获取设备信息 |
| `getNetworkStatus` | 获取网络状态 |
| `showToast` | 展示 Toast |
| `setStatusBar` | 设置状态栏样式 |

## 兼容性策略

| 条件 | 渲染模式 | 恢复策略 |
|---|---|---|
| SDK ≥ 29 + 新版 WebView | texture 优先 | 标准恢复 |
| SDK ≤ 28 或旧版 WebView | hybrid 优先 | 激进恢复 |
| F136A 设备 | hybrid 优先 | 激进恢复 + 建议更新 WebView |

## 新增品牌流程

1. 新建 `flavors/品牌名.yaml`
2. 在 `flavors/品牌名/` 放置品牌资源
3. 执行 `dart run tool/generate_app.dart 品牌名`
4. 生成器自动产出：
   - `apps/品牌名/lib/main.dart`
   - `apps/品牌名/assets/config/bootstrap.json`
   - `apps/品牌名/assets/branding/*`
   - Android 图标、启动页、签名配置
5. 构建 APK：`cd apps/品牌名 && flutter build apk --release`
