# ZQcom **Repository Path**: ichliebedich-DaCapo/zqcom ## Basic Information - **Project Name**: ZQcom - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-04-17 - **Last Updated**: 2026-08-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ZQcom 一个基于 `Wails + Go + Vue 3 + TypeScript` 的桌面串口调试助手,面向日常协议联调、产线测试、自动化脚本调试和曲线观察场景。
**需要注意,此为临时工具,用于快速调试串口,以图表形式展示数据趋势。个人用的工具,难免会有一些疏漏,请合理使用!** ![主界面截图](resource/pictures/main_ui.png) **发布页在gitee上:[发布页链接](https://gitee.com/ichliebedich-DaCapo/zqcom/releases/?latest)** ## 功能概览 - 串口参数配置:端口、波特率、数据位、校验位、停止位、读超时 - 收发统一窗口:按时间线查看 `RX / TX`,支持多选、全选、批量删除 - 接收显示支持 `文本 / HEX` 切换,并支持 ANSI 颜色与转义字符开关 - 发送模式支持 `文本 / HEX` 切换,并可设置 `CR / LF / CRLF` - HEX 发送输入限制为 `0-9 / A-F`,并自动按每 `2` 个字符分组显示 - 发送区提供独立的红色“清空发送”按钮 - 循环发送:支持周期发送与启动即发 - 运行日志抽屉:支持导出 `TXT / CSV` - 脚本工作台:加载、编辑、运行 JavaScript 自动化脚本 - 独立脚本 API 帮助窗口、独立使用说明窗口、程序内版本说明窗口 - 实时曲线:支持自动解析和脚本主动推送 - 工具菜单支持加载已保存的串口记录并在独立窗口中按图表方式查看 - 三栏布局可拖动,宽度与折叠状态会持久化保存 - 主题支持:浅色、深色、跟随系统 - 内置虚拟测试串口,无硬件也能自测 - 内置在线更新:启动后静默检查,可在版本窗口中下载并安装更新 ## 适用场景 - 日常串口调试 - 协议收发验证 - 产测或重复指令发送 - 用脚本做自动应答、批注、过滤和数据提取 - 观察数值型串口输出的实时趋势 - 回看已保存的收发记录并做离线曲线分析 ## 技术栈 - 桌面容器:`Wails v2` - 后端:`Go` - 前端:`Vue 3 + Vite + TypeScript` - 图表:`uPlot` - 状态组织:`Composition API` + 单例 composable - 字体:`Space Grotesk` + `IBM Plex Mono` ## 项目结构 ```text ZQcom/ ├─ app.json ├─ main.go ├─ wails.json ├─ internal/ │ └─ backend/ │ ├─ app.go │ ├─ app_meta.go │ ├─ update.go │ ├─ serial_manager.go │ ├─ serial_virtual.go │ ├─ serial_platform_windows.go │ └─ serial_platform_stub.go ├─ frontend/ │ ├─ package.json │ ├─ src/ │ │ ├─ App.vue │ │ ├─ main.ts │ │ ├─ style.css │ │ ├─ components/ │ │ │ ├─ AppHeader.vue │ │ │ ├─ ReceiveConsolePanel.vue │ │ │ ├─ ScriptWorkbench.vue │ │ │ ├─ TelemetryPanelCard.vue │ │ │ ├─ UsageGuidePage.vue │ │ │ └─ TelemetryWorkspacePage.vue │ │ ├─ composables/ │ │ │ ├─ useSerialStudio.ts │ │ │ └─ useTheme.ts │ │ └─ lib/ │ │ ├─ appInfo.ts │ │ ├─ backend.ts │ │ ├─ runtime.ts │ │ ├─ transcript.ts │ │ ├─ usageGuide.ts │ │ └─ telemetryWorkspace.ts │ └─ wailsjs/ ├─ docs/ │ └─ project.md └─ build/ ``` ## 界面与窗口 ### 主窗口 #### 左侧 - 串口配置 - 运行概览 #### 中间 - 收发窗口 - 发送输入 - 循环发送控制 #### 右侧 - 脚本工作台 - 实时曲线 #### 底部 - 连接状态 - 接收编码 / 脚本 / 主题状态 - 运行日志开关 - 临时通知提示 ### 独立窗口 - 脚本 API 帮助窗口 - 使用说明窗口 - 历史数据图表窗口 ## 菜单说明 ### 连接 - 刷新串口 - 连接当前串口 - 断开连接 ### 视图 - 切换主题 - 展开 / 折叠实时曲线 ### 工具 - 加载保存数据图表 ### 帮助 - 使用说明 - 下载发布页 - 版本 - 重置配置 ## 内置测试串口 项目内置了两个虚拟端口,方便没有硬件时直接验证流程。 ### `TEST-LOOPBACK` - 发送什么就回什么 - 适合验证文本、HEX、循环发送和脚本自动回复 ### `TEST-SIM-DATA` - 持续输出示例数值 - 适合验证接收窗口、自动解析、脚本 `onFrame()` 和实时曲线 ## 收发与发送说明 ### 收发窗口 - 统一按时间线展示 `RX / TX` - 每条记录显示时间戳、字节数和脚本批注 - 接收与发送可分别切换 `文本 / HEX` 视图 - 支持保存当前收发记录 - 支持重新加载已保存的收发记录 - 支持多选、全选、批量删除、全部清空 ### 发送区 - 文本模式支持编码选择与尾部追加 - HEX 模式下只允许输入 `0-9 / A-F` - HEX 输入会自动整理为每 `2` 位一组,例如 `AA 01 0D 0A` - 提供独立的红色“清空发送”按钮 - 支持循环发送与启动即发 ## 脚本系统 脚本工作台使用 JavaScript 编写逻辑。点击“运行”时会先自动编译,成功后再启用;如果编译失败,会直接弹出错误提示。 ### 典型钩子 ```js function onStart(api) {} function onStop(api) {} function onConnect(state, api) {} function onDisconnect(state, api) {} function onFrame(frame, api) {} ``` ### 常用 API ```js api.sendText(payload, append?) api.sendHex(payload, append?) api.log(level, message, source?) api.plot(label, value, timestamp?) api.plotMany({ ...values }, timestamp?) api.emitMetric(label, value, timestamp?) api.emitMetrics(metrics, timestamp?) api.clearMetrics() api.getState() api.getPorts() api.latestFrame() api.encodeHex(text) api.decodeHex(hex) ``` ### `onFrame()` 可返回的控制对象 ```js { drop: true, annotation: "filtered by script", metrics: { 温度: 36.5 }, response: { payload: "ACK", mode: "text", append: "crlf" }, responses: [ { payload: "01 03 00 00", mode: "hex" } ] } ``` ### 一个简单示例 ```js function onFrame(frame, api) { const match = frame.text.match(/temp\\s*=\\s*(-?\\d+(?:\\.\\d+)?)/i); if (!match) { return; } const value = Number(match[1]); api.plot('温度', value, frame.unixMilli); return { annotation: '温度 ' + value.toFixed(2) + ' °C', }; } ``` ## 实时曲线与历史数据图表 ### 实时曲线 - 默认最多显示 `50` 个点 - 可以在界面中修改上限 - 可以关闭图表采样,关闭后新的收发数据不会再自动解析为曲线 - 除自动解析外,也支持脚本主动调用 `api.plot()` / `api.plotMany()` 推送 - 支持折叠 / 展开 - 支持独立帮助窗口 ### 历史数据图表窗口 - 入口位于菜单栏 `工具 -> 加载保存数据图表` - 可加载通过 ZQcom 保存的收发记录文件 - 会自动从收发记录中提取可识别数值并重建曲线 - 使用与主界面实时曲线一致的图表交互能力 ## 在线更新 ### 运行时行为 - 程序启动后会静默检查一次更新 - 如果发现新版本,会在底部状态栏给出提示,并在"帮助"菜单上显示红点 - 版本窗口中可以手动"检查更新" - 点击"下载并安装"后会显示下载进度和安装启动状态 - 当前实现面向 Windows,下载完成后会用 BAT 脚本替换 exe 并重启 ### 元信息职责 - [app.json](app.json):程序自身元信息,包含名称、当前版本、发布页地址、当前版本说明 > 更新检查通过 Gitee Releases API 获取最新版本信息,无需维护外部清单文件。 ## 日志规则 当前日志策略偏向“操作摘要”,避免和收发窗口重复。 - 手动发送:记录发送动作,保留必要内容摘要 - 循环发送:只记录启动、停止等动作 - 脚本:只记录加载、启用、停用、编译失败等脚本操作 - 具体收发内容:统一看中间的收发窗口 ## 状态持久化 以下状态会自动保存,并在重启后恢复: - 左侧栏宽度 - 右侧栏宽度 - 左侧栏折叠状态 - 日志抽屉开关 - 曲线面板折叠状态 - 接收显示模式 - 发送尾部追加方式 - 发送模式 - 发送文本编码 - 上次选择的串口 - 上次加载的脚本路径 - 图表最大点数 - 主题模式 - 图表采集开关 - 自动解析开关 ## 开发环境要求 - Go 1.22+ 或兼容版本 - Node.js 18+ 或更高版本 - npm - Wails CLI ## 安装依赖 ```bash cd frontend npm install ``` ## 前端开发与调试 ```bash cd frontend npm run dev ``` 默认开发服务地址由 `wails.json` 指向: ```json "frontend:dev:serverUrl": "http://127.0.0.1:34116" ``` ## 构建与测试 ```bash cd frontend npm run build ``` ```bash go test ./... ``` ```bash wails build -clean ``` 构建产物通常位于: - `build/bin/ZQcom.exe` ## 在线更新系统 本项目实现了完整的在线更新功能,用户可以不离开应用即可检查并安装最新版本。 ### 架构概览 更新系统由三个部分组成: | 层级 | 文件 | 职责 | |------|------|------| | 元数据 | `app.json` | 维护当前版本号、版本说明、发布页 URL | | 后端 | `internal/backend/update.go` | 获取远程发行版、下载、自替换 | | 前端 | `frontend/src/App.vue` | UI 展示、进度条、下载/安装调用 | | 类型定义 | `frontend/src/lib/types.ts` | TypeScript 接口 | ### Gitee 发行版方案 项目使用 **Gitee Releases API** 作为更新来源,原因: - 国内可访问,无需翻墙 - API 公开可用,无需 token - 支持上传附件(.exe 文件) **API 端点**:`https://gitee.com/api/v5/repos/{owner}/{repo}/releases?per_page={N}&page=1` ### `app.json` 配置 ```json { "name": "ZQcom", "version": "v1.0.3", "subtitle": "串口调试助手", "releaseUrl": "https://gitee.com/ichliebedich-DaCapo/zqcom/releases/", "releaseNotes": [ "更新内容行 1。", "更新内容行 2。" ] } ``` 各字段说明: | 字段 | 必填 | 说明 | |------|------|------| | `name` | 是 | 应用名称,用于构建默认下载文件名 | | `version` | 是 | 当前版本号,格式如 `v1.0.3` | | `subtitle` | 否 | 应用副标题 | | `releaseUrl` | 是 | 发布页完整 URL,用于提取 owner/repo 以及拼接 fallback 下载 URL | | `releaseNotes` | 是 | 当前版本的更新说明列表,展示在版本窗口中 | > `releaseUrl` 必须是完整 URL(包含协议),用于解析出 `owner` 和 `repo`。 > 解析规则:去掉 `https://` 后按 `/` 分割,第 2 段是 owner,第 3 段是 repo。 ### Gitee 发布流程 每次发布新版本时,需要在 Gitee 上创建一个发行版: 1. **访问** `https://gitee.com/{owner}/{repo}/releases/new` 2. **填写 Tag** — 格式如 `v1.0.4`,必须与 `app.json` 中的一致 3. **填写标题** — 可选,通常填版本号 4. **填写说明** — 用 `- ` 开头的列表格式,会被解析为更新内容 5. **上传附件** — 必须上传编译后的 `.exe` 文件作为附件 6. **点击发布** 附件上传后,Gitee API 会返回 `browser_download_url`,程序会自动使用这个地址下载更新包。 ### 后端更新流程(`update.go`) #### 1. 检查更新(`CheckForUpdates`) ``` 读取 app.json 中的 releaseUrl → 解析出 owner/repo → 请求 Gitee API: /api/v5/repos/{owner}/{repo}/releases → 获取最多 10+2 个发行版 → 提取版本号、发布时间、说明、附件下载 URL → 与当前版本号比较(支持 pre-release 标记如 -beta) → 缓存结果到 %TEMP%/ZQcom/release-cache.json → 返回 UpdateCheckResult ``` 如果 API 请求失败,会尝试读取缓存文件,保证离线也能显示版本信息。 #### 2. 下载安装(`InstallUpdate`) ``` 从前端接收 UpdateInstallRequest(version, url, filename) → 如果 URL 为空,用 releaseUrl + version + filename 拼接 → 下载 exe 到 %TEMP%/ZQcom/updates/ → 生成 update.bat 脚本 → 隐藏启动 bat → 退出当前程序 ``` #### 3. BAT 替换脚本 ```batch @echo off timeout /t 2 /nobreak >nul copy /y "新exe路径" "当前exe路径" >nul 2>&1 if errorlevel 1 goto fail start "" "当前exe路径" del "%~f0" exit :fail echo Update failed pause del "%~f0" ``` 使用 `\r\n` 换行符,避免中文编码问题。`start /min` 隐藏窗口运行。 #### 4. 版本号比较逻辑 `parseVersionParts` 函数解析版本号: - 去掉 `v` 前缀 - 分割预发布标记(如 `-beta`),存在预发布标记则末尾补 `0` - 按 `.` 分割为数字数组,逐位比较 例如:`v1.0.3-beta` → `[1, 0, 3, 0]`,`v1.0.3` → `[1, 0, 3]` ### 前端更新 UI #### 版本窗口结构 ``` ┌─ 版本 ─────────────────────────────┐ │ [关闭] │ │ │ │ ZQcom v1.0.3 更新内容 │ │ - 更新项 1 │ │ - 更新项 2 │ │ │ │ ▼ v1.0.4-beta 2026/04/16 │ ← 可展开的历史版本 │ - 历史版本说明 1 │ │ - 历史版本说明 2 │ │ │ │ ────────────── ──────────────── │ │ 当前版本:v1.0.3 操作区 │ │ 状态:发现新版本 v1.0.4 │ │ 最新版本:v1.0.4 │ │ 发布时间:2026/04/16 10:30 │ │ [检查更新] [下载安装] │ └────────────────────────────────────┘ ``` #### 关键状态管理 ```typescript updateInfo: UpdateCheckResult | null // 更新检查结果 updateProgress: UpdateProgress // 下载进度 updateInstalling: boolean // 是否正在安装 ``` #### 事件流 ``` 用户点击"检查更新" → backend.checkForUpdates() → 返回 UpdateCheckResult → 更新 updateInfo → 如果有更新,hasUpdateBadge = true → 显示红点 用户点击"下载安装" → backend.installUpdate({ version, url, filename }) → 后端触发 app:update-progress 事件 → 前端监听进度更新 UI → 下载完成后程序退出,bat 脚本替换文件 ``` ### 关键数据类型 ```typescript interface UpdateCheckResult { currentVersion: string; latestVersion?: string; url?: string; filename?: string; publishedAt?: string; notes?: string[]; hasUpdate: boolean; message: string; releases?: ReleaseInfo[]; // 历史版本列表 } interface ReleaseInfo { version: string; name: string; publishedAt: string; notes: string[]; isCurrent: boolean; } interface UpdateInstallRequest { version: string; url: string; filename?: string; } ``` ### 常见问题 **Q: 下载更新包返回 HTTP 404** - 确认 Gitee 发行版中已上传 `.exe` 附件 - 确认 `app.json` 中 `releaseUrl` 格式正确(含 `https://`) - 下载请求已添加 `User-Agent` 头,Gitee CDN 会拒绝无 UA 的请求 **Q: 更新后版本没变化** - BAT 脚本中的 `copy` 命令需要管理员权限或目标文件未被其他进程占用 - 如果复制失败,BAT 会跳转到 `:fail` 分支,需要手动替换 **Q: GitCode 能否使用同样的方案** - GitCode 的 API 需要 token 认证,raw 文件地址也需要登录态 - 建议使用 Gitee 作为主要发布平台 ## 关键后端接口 前端通过 `internal/backend/App` 暴露的方法与后端通信,核心接口包括: - `GetSnapshot()` - `GetAppMetadata()` - `RefreshPorts()` - `AutoDetectBaud(config)` - `ConnectPort(config)` - `GetTextEncodingInfo()` - `DisconnectPort()` - `Send(request)` - `StartLoopSend(config)` - `StopLoopSend()` - `ClearLogs()` - `AppendLog(level, source, message)` - `ExportLogs(format)` - `SaveTextFile(defaultFilename, content)` - `OpenScriptFile(defaultPath)` - `CheckForUpdates()` - `InstallUpdate(request)` ## 关键事件通道 前后端实时同步主要依赖这些事件: - `serial:data` - `serial:tx` - `serial:state` - `serial:log` - `serial:ports` - `app:update-progress` ## 相关文档 - 项目设计补充说明见 [docs/project.md](docs/project.md) - 程序元信息位于 `app.json` - 收发记录导入解析逻辑位于 `frontend/src/lib/transcript.ts` - 在线更新逻辑位于 `internal/backend/update.go` ## 当前已验证命令 ```bash cd frontend npm run build go test ./... ``` ## Release Notes 维护 当前版本说明不再直接写在 `app.json` 的 `releaseNotes` 数组里。 现在的维护方式是: - 在 `app.json` 中维护 `version` 和 `releaseNotesFile` - 在 `docs/releases/` 下为对应版本创建 Markdown 文件,例如 `docs/releases/v1.0.4.md` - 程序启动时会读取这份 Markdown,并在版本窗口中显示 示例: ```json { "name": "ZQcom", "version": "v1.0.4", "subtitle": "串口调试助手", "releaseUrl": "https://gitee.com/ichliebedich-DaCapo/zqcom/releases/", "releaseNotesFile": "docs/releases/v1.0.4.md" } ``` ## 在线更新实现备忘(2026-04) 这一节专门记录当前项目里“在线更新”和“版本历史展示”的关键经验。下次重开窗口时,如果要继续维护或重做在线更新功能,优先先看这一节,再去看对应代码。 ### 当前实现涉及的核心文件 - `app.json` - 应用名、当前版本、Gitee 发布页地址、当前版本说明文件路径都在这里。 - `main.go` - Wails 启动入口。 - 这里必须固定 `Windows.WebviewUserDataPath`,否则 WebView2 会默认按 exe 文件名建立用户数据目录,用户一旦手动重命名程序,原来的 `localStorage` 配置就会“像丢了一样”。 - `internal/backend/update.go` - 在线检查更新、读取 Gitee 发行版、版本排序、下载地址解析、安装更新都在这里。 - `internal/backend/update_version_test.go` - 版本比较和排序规则的测试应集中补在这里。 - `frontend/src/App.vue` - 版本窗口 UI、历史版本展开逻辑、检查更新按钮、安装最新版本按钮、任意版本下载按钮都在这里。 - `frontend/src/lib/types.ts` - 前后端更新数据结构的前端类型定义。 - `docs/releases/*.md` - 当前版本的本地版本说明来源。 ### 一定不要再踩的坑 #### 1. 不能相信 Gitee API 返回顺序 Gitee 发行版接口返回的数据顺序不能直接当成“最新在前”。之前已经遇到过这样的真实情况: - 当前程序版本是 `v1.0.4` - Gitee 上新建了 `v1.0.5` - 历史列表里却出现了: - 当前版本 - `v1.0.5` - `v1.0.3` - `v1.0.3-beta` - 如果直接拿第一条当最新版本,就会把 `v1.0.4` 误判成最新 所以后端必须自己做版本排序,不能依赖接口原始顺序。 #### 2. 版本排序必须按“语义版本”处理 目前项目已经按下面的规则比较版本: - 先去掉前缀 `v` - 再比较主版本、次版本、修订版本数字 - 如果主版本相同,再比较预发布标识 - 同号时正式版必须高于预发布版 例如: - `v1.0.5` > `v1.0.4` - `v1.0.3` > `v1.0.3-beta` - `v1.0.3-beta.2` > `v1.0.3-beta.1` 如果后面要改排序逻辑,先跑测试,避免再次出现“检查更新把当前版本误识别为最新版本”的回归。 #### 3. WebView2 配置目录不能跟着 exe 文件名走 这是另一个已经确认过的隐藏 bug。 Wails 的 Windows WebView2 默认会把用户数据目录放在类似: - `%APPDATA%\\[BinaryName.exe]` 如果用户把 `ZQcom.exe` 重命名成别的名字,WebView2 就会新建一套新的用户数据目录,前端里依赖 `localStorage` 保存的配置、主题、会话参数看起来就像“全部丢失”了一样。 当前修复原则是: - 在 `main.go` 中显式设置 `windows.Options.WebviewUserDataPath` - 路径必须固定到应用名目录,而不是当前 exe 名称 - 当前建议路径: - `filepath.Join(os.UserConfigDir(), "ZQcom", "webview2")` 以后如果再遇到“重命名程序后配置没了”,先检查这里,不要先怀疑前端存储逻辑。 ### 当前版本窗口的正确交互逻辑 版本窗口不是“所有版本混在一起展示”,而是要分成 3 个逻辑区域: #### 1. 当前版本卡片 - 永远显示在最前面 - 展示当前版本自己的本地版本说明 - 当前版本旁边不放“下载此版本”按钮 - 如果存在比当前更高的版本,则显示“显示新版本 / 收起新版本”按钮 - 如果不存在更新版本,则这个按钮隐藏 #### 2. 比当前更新的版本 - 默认隐藏 - 只有点击当前版本卡片上的按钮后才展开显示 - 展开后,这些版本显示在当前版本卡片的上方 - 每个版本都可以单独展开版本说明 - 每个版本都应有“下载此版本”按钮 #### 3. 过往版本 - 只显示比当前版本旧的版本 - 显示在当前版本卡片下方 - 每个版本都可以折叠/展开 - 每个版本都应有“下载此版本”按钮 ### 为什么版本窗口要这样分组 这是为了区分两个完全不同的概念: - “当前程序是什么版本” - “仓库里有哪些历史版本和更新版本” 如果把所有版本直接按排序塞进一个列表,会出现两个问题: - 用户会把“当前版本”误读成“最新版本” - 当存在新版本时,当前版本会被挤到中间,语义非常混乱 所以现在的设计是: - 当前版本单独做一张卡片 - 更新版本按需展开 - 更旧版本单独放在“过往版本”区域 ### 版本窗口的滚动规则 版本说明内容可能很长,不能让弹窗高度无限膨胀。 现在的规则是: - 整个版本弹窗设置最大高度 - 弹窗内容区域使用内部滚动 - 当版本说明太长时,只在弹窗内部滚动,不允许把弹窗继续向上下撑开 以后如果用户再次反馈“版本窗口被主界面遮住”或“弹窗超出窗口边界”,优先检查: - `.overlay-card` - `.overlay-card__body--scroll` - 版本说明内容容器的 `overflow` 和 `max-height` ### 当前在线更新的职责分工 #### 后端负责 - 从 `app.json` 读取当前版本和 `releaseUrl` - 根据 `releaseUrl` 解析出 Gitee 仓库 owner/repo - 请求 Gitee 发行版接口 - 把返回的发行版转换成内部 `ReleaseInfo` - 对发行版做语义版本排序 - 判断 `latestVersion` 是否高于 `currentVersion` - 为最新版本生成 `InstallUpdate` 所需下载地址 - 为历史任意版本生成单独下载地址 - 执行最新版本的下载与安装替换流程 #### 前端负责 - 展示当前版本说明 - 展示更新版本和历史版本 - 控制每一项的折叠/展开 - 调用“检查更新” - 调用“下载并安装最新版本” - 调用“下载某个指定版本” - 监听 `app:update-progress` 更新进度条 ### Gitee 发布页与 API 的使用原则 #### 1. `app.json.releaseUrl` 是基础入口 当前示例: ```json "releaseUrl": "https://gitee.com/ichliebedich-DaCapo/zqcom/releases/" ``` 后端需要从这个地址中解析: - owner: `ichliebedich-DaCapo` - repo: `zqcom` 再拼接 Gitee API 请求地址。 #### 2. 请求下载地址时要带 `User-Agent` 之前已经确认过: - 某些 Gitee / CDN 下载请求如果没有 `User-Agent`,可能返回异常或 404 所以无论是拉取发行版信息,还是实际下载更新包,都要显式设置 `User-Agent`。 #### 3. 历史版本下载地址不能只靠拍脑袋拼接 理想顺序应该是: 1. 优先从发行版资源列表里找到真实附件地址 2. 优先选 `.exe` 附件 3. 同时带回附件文件名 4. 如果资源列表为空,再回退到约定式地址 当前回退策略可以保留为: - `releaseUrl/download/{tag}/{appName}.exe` 但要把它视为兜底方案,不要当成唯一方案。 ### 最新版本安装与任意版本下载是两套逻辑 这是实现上很重要的区分。 #### 1. “下载并安装最新版本” 这个按钮的职责是: - 只针对最新版本 - 调用后端 `InstallUpdate` - 后端下载文件到临时目录 - 通过 bat 脚本在程序退出后替换 exe - 替换成功后重新启动程序 也就是说,这个入口是“自动更新安装”。 #### 2. “下载此版本” 这个按钮的职责是: - 面向任意版本条目 - 不负责自动替换当前程序 - 直接打开对应发行版附件下载地址 也就是说,这个入口更像“手动下载指定版本安装包”。 这两个入口不能混成一个逻辑,否则容易把历史版本误当成自动降级/升级安装。 ### 当前数据结构约定 如果下次继续扩展更新系统,优先沿用这些字段语义: #### `UpdateCheckResult` - `currentVersion` - 当前程序版本 - `latestVersion` - 排序后识别出的最新正式/预发布版本 - `url` - 最新版本的下载地址,供 `InstallUpdate` 使用 - `filename` - 最新版本下载文件名 - `publishedAt` - 最新版本发布时间 - `notes` - 最新版本说明 - `hasUpdate` - 是否发现高于当前版本的新版本 - `message` - 当前检查结果说明 - `releases` - 所有发行版历史记录,前端会再分成“更新版本 / 当前版本 / 过往版本” #### `ReleaseInfo` - `version` - `name` - `publishedAt` - `notes` - `isCurrent` - `url` - 该版本附件下载地址 - `filename` - 该版本附件文件名 ### 推荐的发布规范 每次发新版时,最好同时满足下面几条: - `app.json.version` 更新到新版本,例如 `v1.0.5` - `app.json.releaseNotesFile` 指向对应 Markdown,例如 `docs/releases/v1.0.5.md` - Gitee 的 tag 名称与程序版本保持一致,例如 `v1.0.5` - Gitee 发行版里上传 Windows 可执行附件,优先保持文件名稳定,例如 `ZQcom.exe` - 发行版说明和本地 `docs/releases/*.md` 内容尽量一致 ### 下次如果要继续实现或重构在线更新,建议按这个顺序 1. 先检查 `app.json` - `version` - `releaseUrl` - `releaseNotesFile` 2. 再检查 `internal/backend/update.go` - 版本解析 - Gitee API 拉取 - 排序规则 - 下载地址解析 - 安装流程 3. 再检查 `frontend/src/App.vue` - 当前版本卡片 - 新版本折叠区 - 过往版本列表 - 检查更新与安装按钮 4. 最后跑测试和手工验证 ### 必做验证清单 每次改完在线更新逻辑,至少验证下面这些场景: #### 版本排序与展示 - 当前版本为 `v1.0.4` - Gitee 存在 `v1.0.5` - 还存在 `v1.0.3`、`v1.0.3-beta` - 检查更新必须识别 `v1.0.5` 为最新版本 - 默认版本窗口中只显示: - 当前版本 - 更旧版本 - 点击“显示新版本”后,`v1.0.5` 才出现在当前版本上方 - 当前版本不显示下载按钮 - 历史条目显示下载按钮 #### 任意版本下载 - 点击 `v1.0.5` 的“下载此版本”能打开对应附件 - 点击 `v1.0.3` 的“下载此版本”也能打开对应附件 - 没有附件时,回退地址仍然可用 #### 自动安装最新版本 - 点击“检查更新”后能拿到最新版本信息 - 点击“下载并安装最新版本”后有进度条 - 下载完成后 bat 替换成功 - 重启后程序版本变化正确 #### 重命名 exe 后配置不丢失 - 先打开程序,修改几个前端设置 - 关闭程序 - 把 exe 改成别的名字 - 再打开程序 - 之前的配置仍然存在 ### 每次改完建议执行的命令 ```bash cd frontend npm run build cd .. go test ./... ``` 如果是改了版本排序或更新逻辑,建议额外补对应单元测试,不要只做手工验证。