# 自动升级系统 — 实现规格

> 来源：需求探索会话 2026-05-28
> 状态：待开发

---

## 1. 架构总览

```
┌──────────────┐
│  远程版本 JSON │  ← 静态文件，放 CDN / OSS
│  version.json │
└──────┬───────┘
       │ HTTP GET
       ▼
┌──────────────────┐
│  u-desktop.exe    │  ← 主程序
│  · 启动时检查版本  │
│  · 弹窗提示升级    │
│  · 释放 updater   │
└──────┬───────────┘
       │ 启动进程
       ▼
┌──────────────────┐
│  updater.exe      │  ← 通用升级器，参数化驱动
│  · 下载安装包      │
│  · SHA256 校验    │
│  · 备份旧版本      │
│  · 替换 + 启动    │
│  · 失败自动回滚    │
└──────────────────┘
```

---

## 2. 主程序改动

### 2.1 版本号注入

`main.go` 增加版本变量，编译时注入：

```go
var version = "dev" // -ldflags "-X main.version=0.6.0"
```

`build` 脚本 / publish 技能同步更新 ldflags。

### 2.2 版本检查模块

新增 `updater.go`：

```
func checkUpdate() (*UpdateInfo, error)
```

- 启动时异步调用（不阻塞主流程）
- 请求远程 `version.json`
- 对比当前 version，返回更新信息或 nil

远程 version.json 格式：

```json
{
  "version": "0.7.0",
  "force": false,
  "release_notes": "· 新增视频壁纸\n· 修复全屏检测偶发失效",
  "package": {
    "url": "https://c.1216.top/u-desktop/u-desktop-0.7.0.zip",
    "size": 8388608,
    "sha256": "abc123def456..."
  }
}
```

version.json URL 存入 config 或硬编码默认值。

### 2.3 升级弹窗

用 WebView2 新建一个无边框小窗口，HTML 渲染弹窗内容。

#### 弹窗策略

| force 字段 | 位置 | 按钮 |
|-----------|------|------|
| `false` | 右下角，托盘图标上方 | [稍后] [立即升级] |
| `true` | 屏幕居中 | [立即升级]（仅一个按钮） |

#### 弹窗 HTML 结构

右下角气泡（非强制）：

```
┌─────────────────────────────┐
│  发现新版本 v0.7.0           │
│                             │
│  · 新增视频壁纸              │
│  · 修复全屏检测偶发失效       │
│                             │
│         [稍后]  [立即升级]   │
└─────────────────────────────┘
```

居中弹窗（强制）：

```
┌─────────────────────────────────────┐
│                                     │
│         发现新版本 v0.7.0            │
│                                     │
│  · 新增视频壁纸                     │
│  · 修复全屏检测偶发失效             │
│                                     │
│  大小: 8MB  |  预计用时: ~10秒      │
│                                     │
│              [立即升级]              │
└─────────────────────────────────────┘
```

#### 弹窗窗口参数

| 参数 | 右下角 | 居中 |
|------|--------|------|
| 宽度 | 340px | 420px |
| 高度 | auto（内容自适应） | auto |
| 位置 | 屏幕右下角 - 边距 20px | 屏幕居中 |
| 样式 | 无边框，圆角，毛玻璃 | 无边框，圆角，毛玻璃 |
| 置顶 | TOPMOST | TOPMOST |

### 2.4 用户点击"立即升级"

1. 从自身嵌入资源释放 `updater.exe` 到临时目录（或主程序同级目录）
2. 构造命令行参数启动 updater.exe
3. 主程序退出

```go
exec.Command(tmpUpdaterPath,
    "--package-url", info.Package.URL,
    "--sha256", info.Package.SHA256,
    "--target", selfExePath,
    "--version", info.Version,
    "--force", strconv.FormatBool(info.Force),
).Start()
// 主程序退出
```

### 2.5 托盘菜单

新增菜单项："检查更新"，手动触发版本检查。

---

## 3. updater.exe

### 3.1 职责

通用升级器，不含业务逻辑，纯参数驱动。可复用于其他产品（u-tabs 等）。

### 3.2 命令行参数

```bash
updater.exe \
  --package-url <下载地址> \
  --sha256 <SHA256校验值> \
  --target <目标exe路径> \
  --version <目标版本号> \
  --force \
  --backup-dir <备份目录，默认 target 同级 backup/>
```

### 3.3 流程

```
1. 显示进度窗口（WebView2 或控制台）
2. HTTP GET 下载 package-url → 临时文件
   - 显示下载进度（已下载/总大小）
   - 支持断点续传（可选，初期不做）
3. 计算下载文件 SHA256，与参数比对
   - 不匹配 → 报错退出
4. 备份当前 target → backup-dir/old-{timestamp}.exe
5. 如果是 zip 包：解压后替换
   如果是 exe：直接覆盖
6. 启动新版本 target
7. updater 退出

异常处理：
- 下载失败 → 重试 3 次，间隔 5s
- 校验失败 → 报错，不替换
- 启动新版本后监听 5s，新进程退出则回滚
- 回滚：将 backup 中的旧 exe 还原
```

### 3.4 进度显示

初期：无 UI，日志写 `updater.log`（`-H windowsgui` 隐藏控制台）。
后续：可加 WebView2 进度窗口。

### 3.5 日志

写入 `updater.log`（与 target 同目录），包含时间戳、步骤、错误信息。

---

## 4. 构建 & 发布流程

### 4.1 主程序构建

```bash
go build -ldflags "-H windowsgui -X main.version=0.7.0" -o u-desktop.exe
```

### 4.2 updater 构建

独立 main 包，单独目录（如 `updater/`）：

```bash
cd updater && go build -ldflags "-H windowsgui" -o updater.exe
```

### 4.3 发布

1. 构建 `u-desktop.exe` + `updater.exe`
2. UPX 压缩（可选）
3. 计算 SHA256
4. 上传到 `c.1216.top`
5. 更新远程 `version.json`
6. 更新 publish 技能（自动生成 version.json + 上传）

---

## 5. 配置

`config.go` 新增字段：

```go
type Config struct {
    // ... 现有字段
    UpdateURL      string `json:"update_url"`       // 版本检查 URL，默认 https://c.1216.top/u-desktop/version.json
    AutoCheckUpdate bool  `json:"auto_check_update"` // 启动时自动检查，默认 true
    SkipVersion    string `json:"skip_version"`      // 用户跳过的版本号
}
```

---

## 6. 目录结构

```
u-desktop/
├── main.go          # 增加版本变量
├── updater.go       # 新增：版本检查 + 弹窗逻辑
├── config.go        # 增加 update 相关字段
├── systray.go       # 托盘菜单增加"检查更新"
├── web/
│   ├── update.html  # 新增：升级弹窗 HTML
│   └── ...
├── updater/         # 新增：升级器独立目录
│   └── main.go
├── docs/
│   ├── feature-roadmap.md
│   └── auto-update-spec.md  # 本文档
└── ...
```

---

## 7. 实施顺序

| 步骤 | 内容 | 依赖 |
|------|------|------|
| 1 | `main.go` 加版本变量，构建脚本加 ldflags | 无 |
| 2 | `updater.go` 版本检查函数 + config 字段 | 步骤 1 |
| 3 | 远程 `version.json` 手动创建 + 上传 | 步骤 2 |
| 4 | 升级弹窗 `update.html` + Go 端窗口创建 | 步骤 2 |
| 5 | 主程序释放 + 启动 updater.exe | 步骤 4 |
| 6 | `updater/` 目录，下载 + 校验 + 替换 + 回滚 | 步骤 5 |
| 7 | 托盘菜单"检查更新" | 步骤 2 |
| 8 | publish 技能集成 version.json 生成 | 步骤 3 |
