Agent 更新(一):版本发布与查询(Admin 设备页内入口,不新增菜单) #143

Open
opened 2026-08-29 09:11:52 +08:00 by ila · 3 comments
Owner

所属与来源

  • 关联工单:#137 / #138 权限菜单(本工单刻意不新增菜单项,避免与其耦合)、#127 采购规则落库(单例指针模式的同类做法)。
  • 来源:用户于 2026-08-29 提出「增加 Agent App 更新功能」,并要求方案最简单易用;经讨论确认:Admin 侧不新增菜单项,入口做在设备列表页内。
  • 类型:Server + Admin / Agent 版本发布与查询。
  • 设计证据:设备列表页新增一个按钮与一个抽屉,属现有页面的小范围扩展,可复用现有列表与表单规范,提供标注截图即可,不需要完整原型。设计证据经用户确认后方可编写生产代码。
  • 工具回退说明:本工单通过 Gitea API 创建;当前会话未提供 Gitea MCP 工具,按 AGENTS.md「Gitea 交互与工单最小读取」记录回退原因。

当前事实(提交 30d8238 复核)

  • models.AgentDevice 已记录 AgentVersion(models/schema.go:35),可用于展示各设备当前版本。
  • 设备心跳每 15 秒一次,是任务调度的核心链路。
  • Admin 前端 GoAuto 模块共 11 个,均位于 web/src/router/index.js 的 constantRoutes;#137 / #138 尚未实施,sys_menu 中没有 GoAuto 记录。
  • 服务端已有 uploadfile 静态目录可承载文件。
  • API 权限矩阵位于 server/app/goauto/access/purchaser.go,由迁移写入 casbin_rule 并全量重建。

目标

  1. Admin 可上传 Agent APK 并将其设为当前版本,可查看历史版本并回滚。
  2. Agent 可查询当前发布版本,用于提示更新(下载与安装在另一工单)。
  3. 不新增 Admin 菜单项,不引入与 #137 / #138 的耦合。
  4. 版本号由服务端从 APK 解析,不依赖人工填写。

非目标

  • 不实现 Agent 端的检查、下载与安装(另一工单)。
  • 不做自动/静默安装、强制更新、后台自动下载。
  • 不做灰度发布、按设备分组、差分包更新。
  • 不修改设备心跳契约——心跳是任务调度核心链路,更新检查属低频操作,走独立接口。
  • 不新增顶级菜单与路由。

关键设计决定

一、不新增菜单,入口做在设备页内

发版是极低频操作(一次发布点一次);更新的对象正是设备,与设备管理同域;且不新增菜单即可完全避开 sys_menu 记录、角色绑定与前端路由改造,不与尚未实施的 #137 / #138 产生耦合。

若日后抽屉内容过于拥挤,再拆为设备模块下的子页面,届时 #137 / #138 已完成,新增菜单成本更低。

二、单例指针表达「当前版本」

沿用与 #127 相同的做法:用单例设置指向当前发布版本,而非在版本表上加 is_current 标记。单例天然保证「有且只有一个当前版本」,且回滚即把指针指回旧版本,无需删除记录。

三、版本号由服务端解析

version_code / version_name 一律从上传的 APK 中解析获得,不接受人工填写——人工填写必然出现与实际不符的情况,而版本比较依赖 version_code 的准确性。

实施方案

数据模型

  1. 新增 agent_app_release:
    • version_code(整数,比较用)、version_name(显示用)
    • file_path(复用 uploadfile 目录)、sha256、size_bytes
    • notes(更新说明,可空)
    • created_by、created_at
    • version_code 唯一,避免重复上传同一版本产生歧义
  2. 新增 agent_app_release_setting 单例表,指向当前发布版本;外键 OnDelete:RESTRICT。
  3. 迁移只新增表,不改动既有表。

服务端

  1. 上传接口:接收 APK,解析 versionCode / versionName,计算 SHA-256,落盘并入库。
    • 校验扩展名与文件头,拒绝非 APK;
    • 大小上限明确(建议 200MB),超限返回可读错误;
    • 不得复用 web/src/utils/request.js 中被 #139 修复前的写法,前端上传须以 FormData 提交且不手写 Content-Type(见 #139)。
  2. 设为当前版本接口:更新单例指针,幂等。
  3. 版本列表与详情查询接口(Admin)。
  4. Agent 查询接口 GET /api/agent/v1/app/latest:走设备 token 鉴权,返回 versionCode、versionName、下载地址、sha256、sizeBytes、notes。无发布版本时返回明确的空结果,不报错。
  5. APK 下载必须走设备 token 或管理员鉴权,不得暴露为完全公开的静态文件——APK 内含服务端地址等配置信息。

权限

  1. 在 access/purchaser.go 登记:
    • 上传、设为当前版本:Purchaser: false(管理员专属);
    • 版本列表查询:Purchaser: false(采购员无需查看发版记录)。
  2. Agent 查询接口属 Agent API,不纳入管理角色矩阵(与既有 Agent 接口一致)。

Admin 界面

  1. 设备列表页顶部新增「Agent 版本」按钮,打开抽屉:
    • 顶部显示当前版本号与发布时间;
    • 「上传新版本」:选择 APK + 填写更新说明;上传后服务端解析版本号并回显,人工不填版本号;
    • 历史版本列表:版本号、上传时间、当前版本标识、「设为当前」操作。
  2. 该按钮按角色隐藏,采购员不可见;前端隐藏不构成安全边界,最终由第 9 项的 API 权限兜底。

安全边界

  • APK 下载需鉴权,不得公开暴露。
  • 上传接口限定扩展名、文件头与大小上限。
  • 发布与回滚为管理员专属操作。
  • 不在日志、工单与 Wiki 中记录服务端地址、令牌等 APK 内配置信息。
  • 数据库迁移属高风险改动,实施前需再次人工确认。

验收标准

  • 可上传 APK,服务端正确解析 versionCode / versionName 并计算 SHA-256。
  • 人工无法填写版本号;上传同一 version_code 的重复包被拒绝。
  • 非 APK 文件、超过大小上限的文件被拒绝,提示可读。
  • 「设为当前版本」可正确切换,幂等;将指针指回旧版本即完成回滚。
  • Agent 查询接口以设备 token 鉴权返回当前版本信息;无发布版本时返回空结果而非错误。
  • APK 下载地址未鉴权时不可访问。
  • 上传、设为当前、版本列表三个接口对采购员关闭;Agent 查询接口不受管理角色矩阵影响。
  • Admin 未新增任何菜单项与路由;入口位于设备列表页内。
  • 「Agent 版本」按钮对采购员不可见。
  • 心跳接口契约未发生任何变化。
  • 设计证据经用户确认后才进入生产代码。

验证方式

  • go test ./app/goauto/...
  • 迁移在空库与既有库两种前提下各执行一次。
  • 手工验证:上传、解析、设为当前、回滚、重复上传被拒、非 APK 被拒、未鉴权下载被拒六条路径。
  • 用真实 Agent APK(当前 0.9.12 或更新)验证版本解析结果与包内实际值一致。
  • 未覆盖的部署环境如实回写。

依赖、并行与风险

  • 无前置依赖;是 Agent 端更新工单的前置。
  • 与 #137 / #138 无耦合(本工单不新增菜单)。
  • 建议在 #139 修复后再实施前端上传,避免踩同一个 Content-Type 问题。
  • 风险:APK 解析依赖第三方库或自行解析 AndroidManifest.xml。缓解:实施时选定方案并在工单说明;解析失败必须明确报错,不得回退为人工填写版本号。
  • 回退:还原提交并保留已上传文件与表即可;Agent 端尚未实现更新功能,不影响现网设备。

文档影响

  • Wiki Architecture-and-Code-Map:版本发布模型与文件存放位置。
  • Wiki Android-Agent-API-Contract:新增 Agent 版本查询接口。
  • Wiki Deployment-and-Operations(若已存在):发版操作步骤与回滚方式。
  • 按 Wiki-first 门禁:先改线上页面并回读 revision,再执行一轮 sync 与一轮 sync --check,把页面与 revision 写回本工单。

状态

待实施(数据库迁移需实施前再次人工确认;界面需先取得设计证据)。

## 所属与来源 - 关联工单:#137 / #138 权限菜单(本工单刻意不新增菜单项,避免与其耦合)、#127 采购规则落库(单例指针模式的同类做法)。 - 来源:用户于 2026-08-29 提出「增加 Agent App 更新功能」,并要求方案最简单易用;经讨论确认:Admin 侧**不新增菜单项**,入口做在设备列表页内。 - 类型:Server + Admin / Agent 版本发布与查询。 - 设计证据:设备列表页新增一个按钮与一个抽屉,属现有页面的小范围扩展,可复用现有列表与表单规范,提供标注截图即可,不需要完整原型。**设计证据经用户确认后方可编写生产代码。** - 工具回退说明:本工单通过 Gitea API 创建;当前会话未提供 Gitea MCP 工具,按 `AGENTS.md`「Gitea 交互与工单最小读取」记录回退原因。 ## 当前事实(提交 30d8238 复核) - `models.AgentDevice` 已记录 `AgentVersion`(`models/schema.go:35`),可用于展示各设备当前版本。 - 设备心跳每 15 秒一次,是任务调度的核心链路。 - Admin 前端 GoAuto 模块共 11 个,均位于 `web/src/router/index.js` 的 `constantRoutes`;#137 / #138 尚未实施,`sys_menu` 中没有 GoAuto 记录。 - 服务端已有 `uploadfile` 静态目录可承载文件。 - API 权限矩阵位于 `server/app/goauto/access/purchaser.go`,由迁移写入 `casbin_rule` 并全量重建。 ## 目标 1. Admin 可上传 Agent APK 并将其设为当前版本,可查看历史版本并回滚。 2. Agent 可查询当前发布版本,用于提示更新(下载与安装在另一工单)。 3. **不新增 Admin 菜单项**,不引入与 #137 / #138 的耦合。 4. 版本号由服务端从 APK 解析,不依赖人工填写。 ## 非目标 - 不实现 Agent 端的检查、下载与安装(另一工单)。 - 不做自动/静默安装、强制更新、后台自动下载。 - 不做灰度发布、按设备分组、差分包更新。 - 不修改设备心跳契约——心跳是任务调度核心链路,更新检查属低频操作,走独立接口。 - 不新增顶级菜单与路由。 ## 关键设计决定 ### 一、不新增菜单,入口做在设备页内 发版是极低频操作(一次发布点一次);更新的对象正是设备,与设备管理同域;且不新增菜单即可完全避开 `sys_menu` 记录、角色绑定与前端路由改造,不与尚未实施的 #137 / #138 产生耦合。 若日后抽屉内容过于拥挤,再拆为设备模块下的子页面,届时 #137 / #138 已完成,新增菜单成本更低。 ### 二、单例指针表达「当前版本」 沿用与 #127 相同的做法:用单例设置指向当前发布版本,而非在版本表上加 `is_current` 标记。单例天然保证「有且只有一个当前版本」,且**回滚即把指针指回旧版本**,无需删除记录。 ### 三、版本号由服务端解析 `version_code` / `version_name` 一律从上传的 APK 中解析获得,不接受人工填写——人工填写必然出现与实际不符的情况,而版本比较依赖 `version_code` 的准确性。 ## 实施方案 ### 数据模型 1. 新增 `agent_app_release`: - `version_code`(整数,比较用)、`version_name`(显示用) - `file_path`(复用 `uploadfile` 目录)、`sha256`、`size_bytes` - `notes`(更新说明,可空) - `created_by`、`created_at` - `version_code` 唯一,避免重复上传同一版本产生歧义 2. 新增 `agent_app_release_setting` 单例表,指向当前发布版本;外键 `OnDelete:RESTRICT`。 3. 迁移只新增表,不改动既有表。 ### 服务端 4. 上传接口:接收 APK,解析 `versionCode` / `versionName`,计算 SHA-256,落盘并入库。 - 校验扩展名与文件头,拒绝非 APK; - 大小上限明确(建议 200MB),超限返回可读错误; - **不得复用 `web/src/utils/request.js` 中被 #139 修复前的写法**,前端上传须以 FormData 提交且不手写 Content-Type(见 #139)。 5. 设为当前版本接口:更新单例指针,幂等。 6. 版本列表与详情查询接口(Admin)。 7. **Agent 查询接口** `GET /api/agent/v1/app/latest`:走设备 token 鉴权,返回 `versionCode`、`versionName`、下载地址、`sha256`、`sizeBytes`、`notes`。无发布版本时返回明确的空结果,不报错。 8. **APK 下载必须走设备 token 或管理员鉴权**,不得暴露为完全公开的静态文件——APK 内含服务端地址等配置信息。 ### 权限 9. 在 `access/purchaser.go` 登记: - 上传、设为当前版本:`Purchaser: false`(管理员专属); - 版本列表查询:`Purchaser: false`(采购员无需查看发版记录)。 10. Agent 查询接口属 Agent API,不纳入管理角色矩阵(与既有 Agent 接口一致)。 ### Admin 界面 11. 设备列表页顶部新增「Agent 版本」按钮,打开抽屉: - 顶部显示当前版本号与发布时间; - 「上传新版本」:选择 APK + 填写更新说明;上传后服务端解析版本号并回显,人工不填版本号; - 历史版本列表:版本号、上传时间、当前版本标识、「设为当前」操作。 12. 该按钮**按角色隐藏**,采购员不可见;前端隐藏不构成安全边界,最终由第 9 项的 API 权限兜底。 ## 安全边界 - APK 下载需鉴权,不得公开暴露。 - 上传接口限定扩展名、文件头与大小上限。 - 发布与回滚为管理员专属操作。 - 不在日志、工单与 Wiki 中记录服务端地址、令牌等 APK 内配置信息。 - 数据库迁移属高风险改动,实施前需再次人工确认。 ## 验收标准 - [ ] 可上传 APK,服务端正确解析 `versionCode` / `versionName` 并计算 SHA-256。 - [ ] 人工无法填写版本号;上传同一 `version_code` 的重复包被拒绝。 - [ ] 非 APK 文件、超过大小上限的文件被拒绝,提示可读。 - [ ] 「设为当前版本」可正确切换,幂等;将指针指回旧版本即完成回滚。 - [ ] Agent 查询接口以设备 token 鉴权返回当前版本信息;无发布版本时返回空结果而非错误。 - [ ] APK 下载地址未鉴权时不可访问。 - [ ] 上传、设为当前、版本列表三个接口对采购员关闭;Agent 查询接口不受管理角色矩阵影响。 - [ ] Admin 未新增任何菜单项与路由;入口位于设备列表页内。 - [ ] 「Agent 版本」按钮对采购员不可见。 - [ ] 心跳接口契约未发生任何变化。 - [ ] 设计证据经用户确认后才进入生产代码。 ## 验证方式 - `go test ./app/goauto/...` - 迁移在空库与既有库两种前提下各执行一次。 - 手工验证:上传、解析、设为当前、回滚、重复上传被拒、非 APK 被拒、未鉴权下载被拒六条路径。 - 用真实 Agent APK(当前 0.9.12 或更新)验证版本解析结果与包内实际值一致。 - 未覆盖的部署环境如实回写。 ## 依赖、并行与风险 - 无前置依赖;是 Agent 端更新工单的前置。 - 与 #137 / #138 无耦合(本工单不新增菜单)。 - 建议在 #139 修复后再实施前端上传,避免踩同一个 Content-Type 问题。 - 风险:APK 解析依赖第三方库或自行解析 `AndroidManifest.xml`。缓解:实施时选定方案并在工单说明;解析失败必须明确报错,不得回退为人工填写版本号。 - 回退:还原提交并保留已上传文件与表即可;Agent 端尚未实现更新功能,不影响现网设备。 ## 文档影响 - Wiki `Architecture-and-Code-Map`:版本发布模型与文件存放位置。 - Wiki `Android-Agent-API-Contract`:新增 Agent 版本查询接口。 - Wiki `Deployment-and-Operations`(若已存在):发版操作步骤与回滚方式。 - 按 Wiki-first 门禁:先改线上页面并回读 revision,再执行一轮 `sync` 与一轮 `sync --check`,把页面与 revision 写回本工单。 ## 状态 待实施(数据库迁移需实施前再次人工确认;界面需先取得设计证据)。
Author
Owner

#143 + #144 v1 标注稿待确认(2026-08-29)

  • 设计证据:查看 #143 + #144 v1 标注稿
  • 版本识别:GoAuto #143 + #144 v1
  • 形式:Gitea 在线 SVG 标注稿(HTTP 200,image/svg+xml)
  • 状态:待用户确认;尚未编写生产代码、执行迁移或上传 APK

覆盖范围

  1. 设备列表页管理员专属“Agent 版本”按钮,不新增菜单或路由。
  2. 抽屉中的当前版本、APK 选择、更新说明、上传加载/失败、历史版本与“设为当前”。
  3. 版本号仅由服务端解析;上传后设为当前与回滚均有明确确认反馈。
  4. 采购员隐藏入口,服务端权限仍为最终边界。
  5. #144 设置页手动检查、新版本说明、下载进度/取消、忙碌禁用、SHA-256 失败及未知来源权限引导。

设计确认后仍须用户单独明确授权 #143 的追加数据库迁移,才能进入生产实施。

## #143 + #144 v1 标注稿待确认(2026-08-29) - 设计证据:[查看 #143 + #144 v1 标注稿](https://git.ilapage.cn/attachments/d0856734-0d18-4485-b819-2a33f69213b7) - 版本识别:`GoAuto #143 + #144 v1` - 形式:Gitea 在线 SVG 标注稿(HTTP 200,`image/svg+xml`) - 状态:待用户确认;尚未编写生产代码、执行迁移或上传 APK ### 覆盖范围 1. 设备列表页管理员专属“Agent 版本”按钮,不新增菜单或路由。 2. 抽屉中的当前版本、APK 选择、更新说明、上传加载/失败、历史版本与“设为当前”。 3. 版本号仅由服务端解析;上传后设为当前与回滚均有明确确认反馈。 4. 采购员隐藏入口,服务端权限仍为最终边界。 5. #144 设置页手动检查、新版本说明、下载进度/取消、忙碌禁用、SHA-256 失败及未知来源权限引导。 设计确认后仍须用户单独明确授权 #143 的追加数据库迁移,才能进入生产实施。
Author
Owner

用户于 2026-08-29 明确确认 #143 + #144 v1 标注稿,并授权 #143 按工单方案实施追加数据库迁移。现按设备页管理员抽屉方案实施;APK 下载保持鉴权,不新增菜单或心跳字段。

用户于 2026-08-29 明确确认 `#143 + #144 v1` 标注稿,并授权 #143 按工单方案实施追加数据库迁移。现按设备页管理员抽屉方案实施;APK 下载保持鉴权,不新增菜单或心跳字段。
Author
Owner

已按已确认的 #143+#144 v1 实施并推送,现转待验收。

实现:

  • 新增 Agent APK 发布记录、当前发布设置及追加迁移。
  • Admin 支持私有上传、列表、设为当前版本和鉴权下载;Agent 使用 Device Token 查询最新版本并鉴权下载。
  • APK 不放入静态目录;限制 200 MB,保存 SHA-256/大小/说明/创建人;服务端从二进制 AndroidManifest 读取 versionCode/versionName,versionCode 唯一。
  • 上传与“设为当前”分离,避免上传即发布;文件名加入 UUID,规避并发重名清理风险。

提交:f0f5f5b;共享文档:1ec895b。
验证:相关 Go 单测及全量 Server 测试/build 通过;使用本仓库实际 debug APK 验证解析得到 versionCode 41、versionName 0.9.28;设备管理页定向 lint 和 Web 生产构建通过。
Wiki:API Contract 811176b77b923111b5ee972b421716ea224a3a96;Architecture d94641e4574e064395edb9da4b52b1a268925923;Local Dev 88cbef39a8744753021b82f2c97879db29e320d4。

边界:未执行数据库迁移、未上传 APK、未设置当前发布;这些属于迁移/发布动作,等待单独授权。

已按已确认的 #143+#144 v1 实施并推送,现转待验收。 实现: - 新增 Agent APK 发布记录、当前发布设置及追加迁移。 - Admin 支持私有上传、列表、设为当前版本和鉴权下载;Agent 使用 Device Token 查询最新版本并鉴权下载。 - APK 不放入静态目录;限制 200 MB,保存 SHA-256/大小/说明/创建人;服务端从二进制 AndroidManifest 读取 versionCode/versionName,versionCode 唯一。 - 上传与“设为当前”分离,避免上传即发布;文件名加入 UUID,规避并发重名清理风险。 提交:f0f5f5b;共享文档:1ec895b。 验证:相关 Go 单测及全量 Server 测试/build 通过;使用本仓库实际 debug APK 验证解析得到 versionCode 41、versionName 0.9.28;设备管理页定向 lint 和 Web 生产构建通过。 Wiki:API Contract 811176b77b923111b5ee972b421716ea224a3a96;Architecture d94641e4574e064395edb9da4b52b1a268925923;Local Dev 88cbef39a8744753021b82f2c97879db29e320d4。 边界:未执行数据库迁移、未上传 APK、未设置当前发布;这些属于迁移/发布动作,等待单独授权。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#143