feat(agent): 图搜所需相册权限与授权引导 #276

Open
opened 2026-09-14 10:17:46 +08:00 by ila · 2 comments
Owner

所属 MVP:#275 | 来源:用户 2026-09-14 需求讨论(原话摘要见 #275)

目标

让 Agent 具备把图片写入设备相册并读取相册最新一张的能力,并为采购员提供可走通的授权引导。

当前事实

android/app/src/main/AndroidManifest.xml 现在没有任何存储或媒体权限,只有 INTERNET / ACCESS_NETWORK_STATE / FOREGROUND_SERVICE / FOREGROUND_SERVICE_DATA_SYNC / WAKE_LOCK / POST_NOTIFICATIONS / REQUEST_INSTALL_PACKAGES。图搜要往相册写图并判断"自己是不是最新一张",这是从零新增一类权限。

前置依赖

无。可与 #278 并行,建议最先做。

方案

  1. manifest 声明:Android 13+ READ_MEDIA_IMAGES;Android 10–12 READ_EXTERNAL_STORAGE(maxSdkVersion 限定)。写入自己的 MediaStore 记录不需要权限,读取"全相册最新 jpeg"需要。
  2. 运行时授权请求,接入 ui/AccessibilityReadiness.kt 现有的权限引导流程,增加一步。
  3. 设置页/状态页显示相册权限状态;未授权时给出明确的开启路径。
  4. 提供一个可供后续图搜执行器调用的权限查询入口,未授权时返回可识别的失败原因(供 #277 映射为人话)。

注意:参考项目 D:\chengma\cmroubao_old 只声明了 READ_EXTERNAL_STORAGE,未声明 READ_MEDIA_IMAGES。在 Android 13+ 上其"相册最新一张"判断只能看见自己写入的文件,若其它 App 刚存入更新的照片会误判。本工单不得照抄该声明。

设计证据

改动为现有权限引导流程新增一步 + 状态显示,属于现有界面的小范围调整。需提供标注截图或明确复用现有权限引导规范后再实施。

验收

  • 全新安装:走完引导可取得相册权限。
  • 老设备升级:已在用的设备装新版后,引导能提示并完成新权限授权。
  • 拒绝授权时,状态页有明确提示与重新授权入口,App 不崩溃。
  • 未授权时权限查询入口返回可识别的失败原因。

验证

Android 单元测试 + .\scripts erify.ps1 -Component android;真机验证需覆盖至少一台已在用的老设备的升级路径。

风险

影响所有现有设备。未授权即图搜不可用,但不得影响现有采集与采购功能。

文档影响

设备权限要求变化,需更新对应 Wiki 的运行说明章节。

> 所属 MVP:#275 | 来源:用户 2026-09-14 需求讨论(原话摘要见 #275) ## 目标 让 Agent 具备把图片写入设备相册并读取相册最新一张的能力,并为采购员提供可走通的授权引导。 ## 当前事实 `android/app/src/main/AndroidManifest.xml` 现在**没有任何存储或媒体权限**,只有 INTERNET / ACCESS_NETWORK_STATE / FOREGROUND_SERVICE / FOREGROUND_SERVICE_DATA_SYNC / WAKE_LOCK / POST_NOTIFICATIONS / REQUEST_INSTALL_PACKAGES。图搜要往相册写图并判断"自己是不是最新一张",这是从零新增一类权限。 ## 前置依赖 无。可与 #278 并行,建议最先做。 ## 方案 1. manifest 声明:Android 13+ `READ_MEDIA_IMAGES`;Android 10–12 `READ_EXTERNAL_STORAGE`(`maxSdkVersion` 限定)。写入自己的 MediaStore 记录不需要权限,读取"全相册最新 jpeg"需要。 2. 运行时授权请求,接入 `ui/AccessibilityReadiness.kt` 现有的权限引导流程,增加一步。 3. 设置页/状态页显示相册权限状态;未授权时给出明确的开启路径。 4. 提供一个可供后续图搜执行器调用的权限查询入口,未授权时返回可识别的失败原因(供 #277 映射为人话)。 > 注意:参考项目 `D:\chengma\cmroubao_old` 只声明了 `READ_EXTERNAL_STORAGE`,未声明 `READ_MEDIA_IMAGES`。在 Android 13+ 上其"相册最新一张"判断只能看见自己写入的文件,若其它 App 刚存入更新的照片会误判。**本工单不得照抄该声明。** ## 设计证据 改动为现有权限引导流程新增一步 + 状态显示,属于现有界面的小范围调整。需提供标注截图或明确复用现有权限引导规范后再实施。 ## 验收 - [ ] 全新安装:走完引导可取得相册权限。 - [ ] **老设备升级**:已在用的设备装新版后,引导能提示并完成新权限授权。 - [ ] 拒绝授权时,状态页有明确提示与重新授权入口,App 不崩溃。 - [ ] 未授权时权限查询入口返回可识别的失败原因。 ## 验证 Android 单元测试 + `.\scripts erify.ps1 -Component android`;真机验证需覆盖至少一台**已在用的老设备**的升级路径。 ## 风险 影响所有现有设备。未授权即图搜不可用,但不得影响现有采集与采购功能。 ## 文档影响 设备权限要求变化,需更新对应 Wiki 的运行说明章节。
Author
Owner

核对范围与方法(2026-09-15)

  • 被核对分支:release/283 @ c80d0c9;基线 main @ c14f5f5。
  • 该分支是打包分支,同时含 #272 #273 #274 #276 #278 #279 #280 #282 #283,本次只就 #275~#280 的验收项作结论。
  • 执行内容:go build ./...、go test ./...(server)、npm run build:prod(web)、逐条对照工单验收项读取 diff。
  • 未执行:真机验证、Android 构建与单测(见 #277 结论,Android 侧无可验证的实现)。

结论:部分完成,不达标

已完成

AndroidManifest.xml 两条声明正确:

<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

maxSdkVersion="32" 处理正确,避免了本工单提醒的参考项目缺陷。

未完成的验收项

  • 接入 ui/AccessibilityReadiness.kt 现有权限引导流程(方案第 2 条)
  • 设置页/状态页显示相册权限状态(方案第 3 条)
  • 拒绝授权时的明确提示与重新授权入口(验收第 3 条)
  • 供图搜执行器调用的权限查询入口(方案第 4 条)
  • 老设备升级路径的真机验证(验收第 2 条)

实现缺陷

override fun onCreate(...) {
    ...
    requestNotificationPermission()
    requestMediaPermission()

requestMediaPermission() 在 onCreate 中无条件调用,且 App 内没有任何其它入口可再次发起授权。用户一旦选择拒绝(尤其是「不再询问」),没有任何补救路径,直接违反验收第 3 条。

设计证据

本工单要求"需提供标注截图或明确复用现有权限引导规范后再实施",未见相关记录。

状态维持待实施。

## 核对范围与方法(2026-09-15) - 被核对分支:`release/283` @ `c80d0c9`;基线 `main` @ `c14f5f5`。 - 该分支是打包分支,同时含 #272 #273 #274 #276 #278 #279 #280 #282 #283,本次只就 #275~#280 的验收项作结论。 - 执行内容:`go build ./...`、`go test ./...`(server)、`npm run build:prod`(web)、逐条对照工单验收项读取 diff。 - **未执行**:真机验证、Android 构建与单测(见 #277 结论,Android 侧无可验证的实现)。 ## 结论:部分完成,不达标 ### 已完成 `AndroidManifest.xml` 两条声明正确: ```xml <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> ``` `maxSdkVersion="32"` 处理正确,避免了本工单提醒的参考项目缺陷。 ### 未完成的验收项 - [ ] 接入 `ui/AccessibilityReadiness.kt` 现有权限引导流程(方案第 2 条) - [ ] 设置页/状态页显示相册权限状态(方案第 3 条) - [ ] 拒绝授权时的明确提示与**重新授权入口**(验收第 3 条) - [ ] 供图搜执行器调用的权限查询入口(方案第 4 条) - [ ] 老设备升级路径的真机验证(验收第 2 条) ### 实现缺陷 ```kotlin override fun onCreate(...) { ... requestNotificationPermission() requestMediaPermission() ``` `requestMediaPermission()` 在 `onCreate` 中无条件调用,且 App 内没有任何其它入口可再次发起授权。用户一旦选择拒绝(尤其是「不再询问」),**没有任何补救路径**,直接违反验收第 3 条。 ### 设计证据 本工单要求"需提供标注截图或明确复用现有权限引导规范后再实施",未见相关记录。 状态维持待实施。
Author
Owner

上线信息(共用)

  • 合并提交:1d01186,已推送 be03972..1d01186 到 main。
  • 发布目录:/home/goauto/releases/20260915-1d01186,二进制 SHA-256 前缀 65e404c4721d592a(本地与服务器一致)。
  • systemctl is-active goauto.service = active,监听 127.0.0.1:8010。
  • 数据库迁移:新执行 2 个,跳过 45 个已应用。迁移前已做全库逻辑备份(66 张表,gzip 校验通过),路径在运维备份目录,不在此列出。
  • 迁移前后 collection_task 222 行、shopee_product 26901 行,逐一相符。

外部入口验证:

检查 结果
GET / HTTP 200
GET /api/admin/v1/collection-tasks {"code":401,"msg":"cookie token is empty"}
POST /api/admin/v1/collection-tasks/image-search/batch {"code":401,...}
不存在路由对照 HTTP 404

最后两行是关键对照:图搜接口返回 401 而非 404,证明新路由已注册。

回退方式:把 current 软链切回 20260914-be03972-r2 后重启。两条迁移均为加列与放宽约束,不阻碍回退到旧二进制。

实施

提交:e01fcd9、c1eb23e。

  • AndroidManifest.xml 声明 READ_MEDIA_IMAGES 与 READ_EXTERNAL_STORAGE(maxSdkVersion="32")。
  • ui/AccessibilityReadiness.kt 新增 MediaPermissionPolicy / MediaPermissionReadiness,沿用既有 AccessibilityReadinessDetector 模式:current(context) 供图搜执行器只读查询,requiredPermission(),appDetailsSettingsIntent() 处理「不再询问」。
  • ui/AgentStatusFragment.kt 新增权限状态卡片与「重新授权」按钮。
  • MainActivity.requestMediaPermission() 改为委托 MediaPermissionPolicy.requiredPermission(),不再是唯一入口。

验证

Android testDebugUnitTest 与 assembleDebug 通过。

未验证

  • 真机授权弹窗流程、「不再询问」后跳转系统设置页。
  • 老设备升级后重新授权的路径(验收第 2 条)——需在真机安装新 APK 后确认。
  • 未改动 AgentSettingsFragment.kt,权限状态只做在状态页(工单允许二选一)。

状态

代码已上线,真机验证未做,保持待验收。

## 上线信息(共用) - 合并提交:`1d01186`,已推送 `be03972..1d01186` 到 `main`。 - 发布目录:`/home/goauto/releases/20260915-1d01186`,二进制 SHA-256 前缀 `65e404c4721d592a`(本地与服务器一致)。 - `systemctl is-active goauto.service` = `active`,监听 `127.0.0.1:8010`。 - 数据库迁移:新执行 2 个,跳过 45 个已应用。迁移前已做全库逻辑备份(66 张表,gzip 校验通过),路径在运维备份目录,不在此列出。 - 迁移前后 `collection_task` 222 行、`shopee_product` 26901 行,逐一相符。 外部入口验证: | 检查 | 结果 | |---|---| | `GET /` | HTTP 200 | | `GET /api/admin/v1/collection-tasks` | `{"code":401,"msg":"cookie token is empty"}` | | `POST /api/admin/v1/collection-tasks/image-search/batch` | `{"code":401,...}` | | 不存在路由对照 | HTTP 404 | 最后两行是关键对照:图搜接口返回 401 而非 404,证明新路由已注册。 回退方式:把 `current` 软链切回 `20260914-be03972-r2` 后重启。两条迁移均为加列与放宽约束,不阻碍回退到旧二进制。 ## 实施 提交:`e01fcd9`、`c1eb23e`。 - `AndroidManifest.xml` 声明 `READ_MEDIA_IMAGES` 与 `READ_EXTERNAL_STORAGE`(`maxSdkVersion="32"`)。 - `ui/AccessibilityReadiness.kt` 新增 `MediaPermissionPolicy` / `MediaPermissionReadiness`,沿用既有 `AccessibilityReadinessDetector` 模式:`current(context)` 供图搜执行器只读查询,`requiredPermission()`,`appDetailsSettingsIntent()` 处理「不再询问」。 - `ui/AgentStatusFragment.kt` 新增权限状态卡片与「重新授权」按钮。 - `MainActivity.requestMediaPermission()` 改为委托 `MediaPermissionPolicy.requiredPermission()`,不再是唯一入口。 ## 验证 Android `testDebugUnitTest` 与 `assembleDebug` 通过。 ## 未验证 - 真机授权弹窗流程、「不再询问」后跳转系统设置页。 - **老设备升级后重新授权的路径**(验收第 2 条)——需在真机安装新 APK 后确认。 - 未改动 `AgentSettingsFragment.kt`,权限状态只做在状态页(工单允许二选一)。 ## 状态 代码已上线,**真机验证未做**,保持待验收。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#276