Files
SmartPlateCabinet/docs/superpowers/specs/2026-09-01-baseurl-switch-design.md
T

5.2 KiB
Raw Blame History

业务 BaseUrl 切换 — 设计文档

  • 日期:2026-09-01
  • 状态:已确认
  • 关联模块:网络层 / 设备初始化 / 全局配置

1. 背景与目标

SmartTakePlate(发盘机)当前业务服务地址由 GlobalData.appBaseUrl 决定,但该字段:

  • 默认值为空串,全局无任何活跃代码赋值(仅剩注释)。
  • 已具备一套"半成品"基础设施(SpTool.getBaseUrl/setBaseUrlGlobalKey.KEY_BASE_URLLOCAL/TEST/PROD_BASE_URL 常量),但从未接线。

目标:让现场运维人员能在设备端切换业务环境(本地 / 测试 / 生产),切换结果持久化, 重启后生效。

2. 需求

  • 入口:设备端隐藏入口,置于登录前的初始化界面DeviceInitActivity)。 原因:若入口放在业务主界面,一旦配置的服务地址访问不到,业务界面因依赖网络请求成功才能 进入,将无法切回正确地址,形成死锁。
  • 生效方式:持久化到 SharedPreferences,重启 app 后生效(最简单可靠)。
  • 选项来源:仅预设环境列表(本地 / 测试 / 生产),不支持手输。

3. 现状分析

已存在 状态
GlobalData.appBaseUrlvar String = "" 默认空串,无活跃赋值
LOCAL_BASE_URL / TEST_BASE_URL / PROD_BASE_URL 已定义,不带尾斜杠
GlobalKey.KEY_BASE_URL = "baseUrlKey" 已定义
SpTool.getBaseUrl() / setBaseUrl() 已实现,无调用方
ApiClient.retrofitby lazybaseUrl(GlobalData.appBaseUrl) 用空串构建,切环境不重建
ApiService / ApiServiceV2 全部方法 @Url url = "${GlobalData.appBaseUrl}/..." 默认参数,调用时求值

关键结论:

  1. 所有请求地址由 GlobalData.appBaseUrl每次调用时经默认参数拼接决定,故切环境 只需改这一个变量(配合重启重建 Retrofit)。
  2. GlobalData.appBaseUrl="" 会让 Retrofit.Builder().baseUrl("") 抛异常 —— 现有隐患。

4. 设计

4.1 组件划分与改动清单

文件 改动
GlobalData.kt 补充环境列表模型(名称 + URL),复用已有 LOCAL/TEST/PROD_BASE_URL
MyApp.kt initGlobalData() 接线:读 SpTool.getBaseUrl(),兜底 TEST_BASE_URL
DeviceInitActivity.kt 加隐藏连点手势 + 弹环境选择弹窗
ApiClient.kt 修复 Retrofit baseUrl 校验(补尾斜杠 / 占位),消除空串崩溃
新增 EnvironmentSelectDialog(或内联 Dialog 环境单选弹窗,高亮当前项
BaseActivity.kt(复用) 复用已有 clearAllFace() 清人脸库

4.2 关键设计决策

决策 A — URL 规范化(消除现有隐患)

  • 约定 GlobalData.appBaseUrl 不带尾斜杠,供 @Url 拼接得到形如 https://dev.yixiong-tech.com:8081/terminal/... 的完整地址。
  • Retrofit 的 baseUrl 单独规范化:空则占位 http://localhost/,非空且无尾斜杠则补 /。 因所有请求走 @Url 全路径,Retrofit baseUrl 不参与实际拼接,只需合法即可。

决策 B — 切换时清理环境残留

选中环境后执行:

  1. SpTool.setBaseUrl(url) 持久化;
  2. 清空本地人脸库(FaceDatabase / clearAllFace());
  3. 重置人脸时间戳 lastFaceTimestamp = 0
  4. 重置 firstGetFace = true

原因:不同环境的人脸数据与时间戳不同,不清理会导致重启后增量同步对不上。

另:ArcSoft 激活参数(appId / sdkKey / activeKey)无需单独处理——DeviceInitActivity 每次启动都会调用 getDeviceConfig() 从新环境重新获取并覆盖,切环境后自动适配。

决策 C — 隐藏手势

DeviceInitActivity 连续点击屏幕(如 5 次)触发,不新增可见 UI 元素。该界面登录前 可达、不依赖网络,满足"配错地址也能切回"的要求。

5. 数据流

sequenceDiagram
    participant User as 运维人员
    participant App as MyApp/DeviceInit
    participant Sp as SpTool(SharedPrefs)
    participant Net as Retrofit/网络

    App->>Sp: 启动时读 KEY_BASE_URL
    alt 有值
        Sp-->>App: 返回已存 URL
    else 无值(首次)
        Sp-->>App: 空 → 用 TEST_BASE_URL
    end
    App->>App: GlobalData.appBaseUrl = 结果

    User->>App: 初始化界面连点5次
    App->>App: 弹环境选择Dialog(高亮当前)
    User->>App: 选择「生产」并确认
    App->>Sp: setBaseUrl(PROD)
    App->>App: 清人脸库 + 重置时间戳 + firstGetFace=true
    App->>User: Toast「重启后生效」
    User->>App: 重启 app
    App->>Net: appBaseUrl 已指向生产 → 全量拉新环境人脸

6. 错误处理

  • baseUrl 空 / 非法:兜底 TEST_BASE_URL,并保证 Retrofit baseUrl 合法(占位/补斜杠)。
  • 清人脸库失败:非致命,仅记日志,不阻塞切换(重启后首次拉取仍会兜底全量)。
  • 写 SharedPreferences 失败:几乎不抛;Toast 提示用户重试。

7. 验证方式

  • 构建:./gradlew assembleDebug 通过。
  • 手动:初始化界面连点 5 次 → 弹窗 → 切「本地」→ 重启 → 抓包/日志确认请求打到新地址; 再切回「测试」验证可恢复。