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

120 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 业务 BaseUrl 切换 — 设计文档
- 日期:2026-09-01
- 状态:已确认
- 关联模块:网络层 / 设备初始化 / 全局配置
## 1. 背景与目标
SmartTakePlate(发盘机)当前业务服务地址由 `GlobalData.appBaseUrl` 决定,但该字段:
- 默认值为空串,全局无任何活跃代码赋值(仅剩注释)。
- 已具备一套"半成品"基础设施(`SpTool.getBaseUrl/setBaseUrl``GlobalKey.KEY_BASE_URL`
`LOCAL/TEST/PROD_BASE_URL` 常量),但从未接线。
目标:让现场运维人员能在**设备端**切换业务环境(本地 / 测试 / 生产),切换结果持久化,
重启后生效。
## 2. 需求
- **入口**:设备端隐藏入口,置于**登录前的初始化界面**(`DeviceInitActivity`)。
原因:若入口放在业务主界面,一旦配置的服务地址访问不到,业务界面因依赖网络请求成功才能
进入,将无法切回正确地址,形成死锁。
- **生效方式**:持久化到 SharedPreferences,重启 app 后生效(最简单可靠)。
- **选项来源**:仅预设环境列表(本地 / 测试 / 生产),不支持手输。
## 3. 现状分析
| 已存在 | 状态 |
|---|---|
| `GlobalData.appBaseUrl``var String = ""` | 默认空串,无活跃赋值 |
| `LOCAL_BASE_URL` / `TEST_BASE_URL` / `PROD_BASE_URL` | 已定义,不带尾斜杠 |
| `GlobalKey.KEY_BASE_URL = "baseUrlKey"` | 已定义 |
| `SpTool.getBaseUrl() / setBaseUrl()` | 已实现,无调用方 |
| `ApiClient.retrofit``by lazy``baseUrl(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. 数据流
```mermaid
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 次 → 弹窗 → 切「本地」→ 重启 → 抓包/日志确认请求打到新地址;
再切回「测试」验证可恢复。