docs: reorganize project documentation (#15)
## Summary - add a root README with project overview, setup, usage, architecture, security, and contribution guidance - replace stale planning and requirement notes with current 1.0 project overview - update architecture and hardware validation docs to match the implemented app - remove obsolete UI branch notes and the old Java/XML AsyncTask example - remove the superpowers documentation and move vendor ABSDK references into docs/vendor - preserve vendor Markdown and XLS content byte-for-byte ## Verification - Markdown relative links checked - vendor files verified as 100% Git renames - git diff --check passed - no application tests run; documentation-only change Reviewed-on: #15 Co-authored-by: KremeCN <3265494849@qq.com>
This commit was merged in pull request #15.
This commit is contained in:
+88
-123
@@ -1,166 +1,131 @@
|
||||
# ABox Manager 架构基线
|
||||
# ABox Manager 架构
|
||||
|
||||
## 1. 文档范围
|
||||
## 总体结构
|
||||
|
||||
本文记录已建立的模块边界、依赖方向、提供方路由和并行开发接口,覆盖 ABox(局域网 ABSDK)与本地体验两条连接路径。ABSDK 0630 的初始化、签名细节和会话清理等未确认内容不在本文中固化,协议观测证据见 `docs/protocol/absdk-0630.md`。
|
||||
|
||||
产品范围和固定分工以 `docs/project_plan.md` 为准,原始简要需求继续保留在 `docs/requirement_spec.md`。
|
||||
|
||||
## 2. 当前模块
|
||||
ABox Manager 是单 Activity 的 Kotlin/Compose Android 应用。业务能力通过纯 Kotlin 领域端口表达,Android 模块负责持久化、系统集成、依赖注入和 UI,厂商 SDK 被限制在独立防腐层中。
|
||||
|
||||
```text
|
||||
abox-mgr/
|
||||
├── android/
|
||||
│ ├── app/ # Android 应用、提供方路由与依赖注入组合根
|
||||
│ └── core/
|
||||
│ ├── designsystem/ # Compose Material 3 设计系统基础
|
||||
│ └── session/ # 提供方 profile、凭据 Keystore 持久化
|
||||
├── domain/ # 纯 Kotlin 领域模型和公共端口
|
||||
├── absdk-bridge/ # ABSDK 0630 防腐层(生产)
|
||||
├── runtime/simulation/ # 本地体验模拟运行时(生产)
|
||||
├── testing/fakes/ # 测试专用 Fake(仅测试依赖)
|
||||
├── docs/
|
||||
└── samples/ # 历史示例,仅供参考
|
||||
├── domain/ # 领域模型、端口、用例与执行器
|
||||
├── absdk-bridge/ # ABSDK 0630 防腐层
|
||||
├── runtime/simulation/ # 本地体验生产模拟运行时
|
||||
├── testing/fakes/ # 测试专用 Fake
|
||||
└── android/
|
||||
├── core/
|
||||
│ ├── designsystem/ # Compose 主题、组件和设计令牌
|
||||
│ ├── preferences/ # 界面偏好 DataStore
|
||||
│ ├── devicecatalog/ # ABox 本地设备目录与 Provider 路由
|
||||
│ └── session/ # Profile、会话偏好和加密凭据
|
||||
├── feature/
|
||||
│ ├── infrared/ # 遥控器、按键、码库与学习流程
|
||||
│ └── automation/ # 情景、自动化、通知和活动记录
|
||||
└── app/ # 应用组合根、导航和设备页面
|
||||
```
|
||||
|
||||
依赖方向:
|
||||
依赖遵循以下方向:
|
||||
|
||||
```text
|
||||
android/app ────────────────┬──> android/core/designsystem
|
||||
├──> android/core/session ──> domain
|
||||
├──> domain
|
||||
├──> absdk-bridge ──> domain
|
||||
└──> runtime/simulation ──> domain
|
||||
|
||||
android/core/designsystem ─────> Compose Material 3
|
||||
testing/fakes(仅测试)────────> domain
|
||||
domain ────────────────────────> Kotlin 与 Coroutines
|
||||
android/app ──> android/core/*, android/feature/*, domain
|
||||
android/app ──> absdk-bridge
|
||||
android/app ──> runtime/simulation
|
||||
android/feature/* ──> domain
|
||||
android/core/* ──> domain
|
||||
absdk-bridge ──> domain
|
||||
runtime/simulation ──> domain
|
||||
testing/fakes ──> domain
|
||||
domain ──> Kotlin 与 Coroutines
|
||||
```
|
||||
|
||||
`domain` 不依赖 Android、Compose、Room、ABSDK 或具体网络实现。业务 UI 只依赖领域端口和类型化结果。
|
||||
`domain` 不依赖 Android、Compose、Room、DataStore 或 ABSDK。UI 不直接调用厂商 SDK。
|
||||
|
||||
## 3. 提供方与数据隔离
|
||||
## Provider 与会话
|
||||
|
||||
认证前应用只提供两种入口,两者在所有构建变体中始终可用:
|
||||
应用支持 `ABOX` 和本地体验两种 Provider。`ProviderProfileId` 是所有会话、目录、遥控器、情景、自动化状态和活动记录的数据隔离键。
|
||||
|
||||
1. **ABox**:通过进程内唯一的真实 `AbsdkBridge` 连接当前局域网中的智能家居网关,登录成功后进入应用;
|
||||
2. **本地体验**:由 `:runtime:simulation` 直接建立模拟会话并进入应用,不请求凭据、不访问网络、不调用 ABSDK。
|
||||
- 任一时刻只有一个活动 Provider;
|
||||
- 切换 Provider 前结束当前会话;
|
||||
- ABox 会话由进程内唯一 `AbsdkBridge` 维护;
|
||||
- 本地体验由 `runtime/simulation` 建立无凭据会话;
|
||||
- 自动登录凭据使用 Android Keystore 的 AES-256-GCM 加密,profile id 作为 AAD;
|
||||
- ABSDK 没有 logout 和 Token setter,退出只清理 App/Bridge 本地会话,重新登录覆盖 SDK Token。
|
||||
|
||||
每个提供方 profile 由 `ProviderProfileId` 标识,所有会话、设备目录和设备操作都显式携带该标识。
|
||||
## 领域端口
|
||||
|
||||
约束:
|
||||
主要公共端口包括:
|
||||
|
||||
- 任一时刻只启用一个提供方;
|
||||
- 切换提供方前必须退出当前会话;
|
||||
- 不同提供方的数据、凭据和设备状态不得互相混用;
|
||||
- 本地体验不调用 ABSDK,也不启动本地 HTTP 服务;
|
||||
- Release 不是特殊情况:debug 与 release 暴露相同的提供方和路由行为。
|
||||
- `SessionGateway`、`LocalSessionGateway`;
|
||||
- `ProviderProfileStore`、`CredentialStore`;
|
||||
- `DeviceCatalog`;
|
||||
- `SocketGateway`、`TemperatureHumidityGateway`、`InfraredGateway`;
|
||||
- `VirtualRemoteRepository`、`InfraredKeyRepository`;
|
||||
- `SceneRepository`、`SceneExecutor`、`NotificationGateway`;
|
||||
- `ActivityRepository`。
|
||||
|
||||
## 4. 公共领域端口
|
||||
所有设备与会话操作返回 `DomainResult<T>`。错误通过 `DomainError` 分类,必要时保留脱敏的厂商错误码;`ABRet`、原始 `Map` 和厂商对象不得越过 Bridge。
|
||||
|
||||
`domain` 当前提供以下最小端口:
|
||||
## 设备目录与默认内容
|
||||
|
||||
- `SessionGateway`:登录、观察会话、验证会话和退出;
|
||||
- `LocalSessionGateway`:无凭据本地会话的标记端口,继承 `SessionGateway`;
|
||||
- `ProviderProfileStore`:提供方 profile、活动 profile 和登录偏好持久化;
|
||||
- `CredentialStore`:自动登录凭据的加密存取;
|
||||
- `DeviceCatalog`:按提供方观察设备目录;
|
||||
- `SocketGateway`:查询和设置插座状态;
|
||||
- `TemperatureHumidityGateway`:读取温度和湿度;
|
||||
- `InfraredGateway`:发送、学习和下载红外码。
|
||||
ABSDK 0630 没有设备发现或列表接口。ABox 设备目录由 `android/core/devicecatalog` 使用 Room 按 profile 持久化,设备操作最终使用 `DeviceName` 调用网关。
|
||||
|
||||
端口只覆盖需求和 ABSDK 0630 文档已经确认的行为。`DeviceId` 是 App 本地目录中的稳定记录标识;设备操作必须使用 ABSDK 0630 实际接受的 `DeviceName`。同一提供方配置和设备类型内,`DeviceName` 必须唯一,设备目录在添加、编辑和导入时负责强制该约束。设备目录的增删改与二维码导入、遥控器布局、情景动作和持久化模型将在对应模块提出后增量加入,不在公共层提前假定。
|
||||
ABox profile 首次初始化时,如果某一设备类型尚不存在,App 补充以下默认记录:
|
||||
|
||||
## 5. 结果与错误边界
|
||||
- `智能插座`;
|
||||
- `温湿度传感器`;
|
||||
- `红外控制器`。
|
||||
|
||||
所有端口返回 `DomainResult<T>`:
|
||||
初始化按设备类型判断,不迁移或重命名已有记录。用户修改设备名后,名称必须与网关实际设备名一致。
|
||||
|
||||
- `Success<T>` 携带类型化领域值;
|
||||
- `Failure` 携带 `DomainError`;
|
||||
- `DomainError.externalCode` 可以保留外部 SDK 错误码用于诊断;
|
||||
- UI 不接触 `ABRet`、原始 `Map`、HTTP 表单或任何服务端数据库模型。
|
||||
同一初始化流程会为 `红外控制器` 添加两个未学习的自定义遥控器:自定义电视遥控器和自定义面板遥控器。
|
||||
|
||||
ABSDK 防腐层已在 `:absdk-bridge` 中完成同步阻塞调用的后台串行化、返回解析和错误映射。
|
||||
## 设备操作
|
||||
|
||||
## 6. Fake 边界
|
||||
`android/app` 中的路由网关根据活动 Provider 分发操作:
|
||||
|
||||
`testing/fakes` 提供会话、设备目录、插座、温湿度和红外 Fake。它们:
|
||||
- ABox 调用共享 `AbsdkBridge`;
|
||||
- 本地体验调用确定性的模拟实现。
|
||||
|
||||
- 不使用网络、Android API、真实时间或随机数;
|
||||
- 支持成功、失败、无数据、离线和 Token 无效场景;
|
||||
- 对插座状态和设备目录按 `ProviderProfileId` 隔离;
|
||||
- 记录红外学习、发送和下载调用;
|
||||
- 可直接用于其他模块的 ViewModel 与 UI 测试。
|
||||
插座控制不会把期望值直接当成真实状态。命令成功后必须回读;回读失败时 UI 保持“等待确认”或错误状态。温湿度允许单字段缺失,只有两个字段都不存在时才进入无数据状态。设备离线通过领域错误显式传递到 UI。
|
||||
|
||||
Fake 只用于开发和测试,不能作为真实 ABox 或真实硬件验收证据。`:testing:fakes` 是测试专用模块,不得加入应用生产依赖。
|
||||
## 红外
|
||||
|
||||
ABSDK 不提供设备目录发现能力。生产 ABox 设备目录由 `:android:core:devicecatalog` 按当前 profile 持久化,来源是用户明确配置或导入的真实 `DeviceName`;它不从本地体验目录复制数据,也不把测试 Fake 绑定到生产组合根。Local profile 仍由 `SimulationDeviceCatalog` 提供独立的确定性目录。
|
||||
`android/feature/infrared` 使用独立 Room 数据库保存虚拟遥控器、按钮布局、学习状态和硬件槽位信息。
|
||||
|
||||
## 7. 本地体验模拟运行时
|
||||
- 预置遥控器从内置码库下载,不提供学习入口;
|
||||
- 自定义遥控器通过硬件学习建立按键;
|
||||
- 按键状态区分未学习、学习中、失败和可发送;
|
||||
- 学习任务由应用级协调器执行,页面离开后可继续;
|
||||
- 进程中断遗留的学习状态在恢复时保守标记为失败;
|
||||
- 开源码库来源和转换约束见 `docs/third-party/flipper-irdb.md`。
|
||||
|
||||
`:runtime:simulation` 是生产 Kotlin/JVM 模块,为本地体验提供确定性实现:
|
||||
## 情景、自动化与活动记录
|
||||
|
||||
- `SimulationSessionGateway`:直接建立无凭据的本地会话;
|
||||
- `SimulationDeviceCatalog`:内置的本地体验设备目录;
|
||||
- `SimulationSocketGateway`、`SimulationTemperatureHumidityGateway`、`SimulationInfraredGateway`:固定行为的模拟设备操作。
|
||||
`android/feature/automation` 使用 Room 保存情景文档、触发器匹配状态、定时 occurrence claim 和活动记录。
|
||||
|
||||
模拟实现不暴露脚本化失败注入、调用记录或断言辅助;这些能力只存在于 `:testing:fakes`。
|
||||
手动指令直接执行。自动指令由 WorkManager 调度和 `AutomationEngine` 评估:
|
||||
|
||||
## 8. Android 组合根与提供方路由
|
||||
1. 收集相关温湿度与插座状态快照;
|
||||
2. 对每个启用的自动指令评估多个触发器;
|
||||
3. 状态型触发器只在不匹配到匹配的转换上产生新触发;
|
||||
4. 时间触发器通过 occurrence key 去重;
|
||||
5. 任一新触发出现后,再要求全部条件满足;
|
||||
6. 按情景配置执行动作并记录结果。
|
||||
|
||||
`android/app` 是单 Activity Compose 应用,负责:
|
||||
触发器支持时间、温度、湿度和插座状态;条件支持温度、湿度、插座状态和时间范围;动作支持插座、虚拟遥控器、红外按键、延迟和通知。
|
||||
|
||||
- `ABoxManagerApplication` 与 Hilt 组合根;
|
||||
- `MainActivity`;
|
||||
- 根 `NavHost` 与提供方选择、登录、认证后路由;
|
||||
- 默认英文和简体中文资源;
|
||||
- 使用 `android/core/designsystem` 的主题和基础组件。
|
||||
活动记录按 profile 保存设备命令、状态确认、传感器刷新、红外发送/学习、情景及动作结果。记录只保存领域层允许的信息,不保存凭据、原始响应或红外码值。
|
||||
|
||||
组合根按端口绑定生产实现:
|
||||
## 界面偏好
|
||||
|
||||
```text
|
||||
SessionGateway -> AbsdkBridge(进程内单例)
|
||||
LocalSessionGateway -> SimulationSessionGateway
|
||||
SocketGateway -> RoutingSocketGateway
|
||||
TemperatureHumidityGateway -> RoutingTemperatureHumidityGateway
|
||||
InfraredGateway -> RoutingInfraredGateway
|
||||
```
|
||||
`android/core/preferences` 使用 DataStore 保存主题模式、动态颜色、强调色、色彩风格、色彩规范、界面缩放和触觉反馈偏好。当前生产界面保持 Material 渲染;未采用的渲染器值保留兼容数据,但不作为正式功能承诺。预测性返回默认关闭,且不向用户显示设置入口。
|
||||
|
||||
`AbsdkBridge` 以 Hilt 单例提供并在所有构建变体中相同:会话与插座/温湿度/红外操作共享同一个 Bridge 实例,使互斥锁和 Bridge 本地会话与厂商 SDK 单例状态保持一致。三个 Routing 网关读取活动 profile 并按提供方分发——ABox 转发到共享的 AbsdkBridge 实现,本地体验转发到模拟实现。任何功能 UI 都不直接选择真实或 Fake 实现。
|
||||
## ABSDK 边界
|
||||
|
||||
设备目录导航、插座与温湿度页面、红外页面和情景页面由固定模块负责人实现,不放入工程基础层。
|
||||
`absdk-bridge` 是厂商 JAR 的唯一生产接入点:
|
||||
|
||||
## 9. ABSDK Bridge
|
||||
- 全部同步 SDK 调用转移到后台并串行执行;
|
||||
- 进程内共享同一个 SDK/Bridge 实例;
|
||||
- 不调用语义未确认的 `setHostInfo`;
|
||||
- 使用 `@CleartextAbsdkTransport` 显式标记明文传输风险;
|
||||
- App 仅因厂商 JAR 的历史行为允许明文流量,不把该策略扩展到其他网络功能。
|
||||
|
||||
`:absdk-bridge` 是纯 Kotlin/JVM 防腐层,依赖 `domain` 并实现会话、插座、温湿度和红外 Gateway。该模块:
|
||||
|
||||
- 只在 `VendorAbsdkClient` 中接触 `ABSDK`、`ABRet` 和 `IrInfo`;
|
||||
- 将所有同步调用移到后台并串行进入全局 SDK;
|
||||
- 将返回值和错误码转换为 `DomainResult` 和领域模型;
|
||||
- 不调用未确认的 `setHostInfo`;
|
||||
- logout 只清除 Bridge 本地会话,因为 JAR 没有公开 logout 或 Token setter;
|
||||
- 生产工厂始终返回进程内同一个 Bridge,使互斥锁和本地会话与厂商单例保持一致;
|
||||
- 真实工厂标记为 `@CleartextAbsdkTransport`,调用方必须显式 opt-in 承认厂商 JAR 的硬编码 HTTP 风险;
|
||||
- 当前直接引用归档 Sample 目录中的唯一一份 JAR,不复制厂商二进制;
|
||||
- ABSDK 0630 没有设备目录能力,Bridge 因此也不提供设备目录或设备发现。
|
||||
|
||||
App 组合根已通过 Hilt 单例绑定该 Bridge,绑定处以 `@CleartextAbsdkTransport` 显式 opt-in 承认厂商 JAR 的硬编码明文 HTTP 风险;App 同时声明 `INTERNET` 权限并允许明文传输(`usesCleartextTraffic`),这是项目所有者对当前局域网集成约束的明确风险接受,不代表传输具备机密性或完整性。协议证据与限制记录在 `docs/protocol/absdk-0630.md`。
|
||||
|
||||
## 10. Provider Session
|
||||
|
||||
`:android:core:session` 负责提供方登录的持久化与 Android 安全边界:
|
||||
|
||||
- `ProviderProfileStore` 使用 DataStore 保存 profile、当前活动 profile、记住用户名和自动登录开关;已移除的提供方类型(如历史兼容后端值)被忽略,不会成为活动 profile;
|
||||
- `CredentialStore` 使用 Android Keystore 中的 AES-256-GCM 密钥加密自动登录凭据,DataStore 只保存带随机 IV 的密文;
|
||||
- `ProviderProfileId` 作为 GCM AAD,防止不同 profile 的密文互换;
|
||||
- 本地体验的会话由 `:runtime:simulation` 的 `SimulationSessionGateway` 提供,不调用 HTTP 或 ABSDK。
|
||||
|
||||
纯 Kotlin `SessionCoordinator` 统一处理启动恢复、提供方选择、登录偏好、自动登录、Token 失效重登、退出和切换提供方。ABox 登录成功必须来自注入的真实 Bridge 返回 `DomainResult.Success<Session>`;无效凭据和网络错误停留在登录页并显示本地化错误。ABox 远端退出清除密码和 Session,但保留 profile 与记住的用户名以便再次显示登录页;本地体验退出清除本地会话和活动 profile,回到提供方选择。
|
||||
|
||||
两个提供方在所有构建变体中始终可用,不存在按 debug/release 区分的可用性开关。
|
||||
|
||||
登录完成后进入 `authenticated` 协作路由。设备中心、详情、红外、情景、自动化和活动记录由对应 Android 模块提供,并通过当前 profile 路由到真实或本地体验端口。
|
||||
|
||||
## 11. 后续边界
|
||||
|
||||
Room 与设备业务 DataStore 的具体 schema/key 只在对应数据所有权明确后创建。生产 ABox 设备目录由设备目录模块维护,不能回退到 `FakeDeviceCatalog`。历史示例中的 AsyncTask、明文密码存储和散落 SDK 调用不得进入正式架构;ABSDK 厂商 JAR 的明文局域网传输风险已明确记录并由所有构建变体共同承担。
|
||||
协议事实、端点、签名边界和待确认事项见 `docs/protocol/absdk-0630.md`。
|
||||
|
||||
Reference in New Issue
Block a user