# ARCH_PLAN.md 审查报告 > 审查时间:2026-06-28 | 目标版本:v1.0 → v1.1 --- ## 审查结果总览 共发现 **11 个问题**,分级如下: | 等级 | 数量 | 说明 | |------|------|------| | P0 阻塞 | 4 | 必须修复才能进入实施 | | P1 重要 | 3 | 影响完整性和一致性 | | P2 优化 | 4 | 锦上添花 | --- ## P0 — 阻塞项 ### #1 缺少 `context.Context` 支持 **问题**:所有涉及 IO 的方法(GetUserInfo、GetHomeList、GetDevices、SetProp、Action 等)都没有 `context.Context` 参数。Go 惯例要求 IO 操作可超时和取消。 **影响范围**: - `client.go`:GetUserInfo、GetHomeList、GetHome、GetRooms、GetRoomDevices、GetDevices、GetDevice、RefreshDevices - `properties.go`:GetProp、SetProp、GetProps、SetProps - `actions.go`:Action - `devices/base.go`:GetProp、SetProp、Action - `devices/air_conditioner.go`:TurnOn、TurnOff、IsOn、SetMode、GetMode、SetTargetTemp、GetTargetTemp、GetCurrentTemp 等全部 IO 方法 - `devices/light.go`、`switch.go`、`fan.go`、`cover.go` 等:同上 **不修改的方法**(纯本地操作): - SubProp / UnsubProp / SubEvent / UnsubEvent / SubDeviceState / UnsubDeviceState - OnPropsChanged - NewClient / NewDevice 等工厂方法 --- ### #2 `PropertyValue.Type` 字段在回调场景下不可用 **问题**:`PropertyValue` 定义了 `Type string` 字段标注"SPEC format"。但 SubProp 回调中 miot 传入的 params 只有 `{did, siid, piid, value}`,没有 type 信息。该字段只在主动 GetProp 时才有可能从 SPEC 查到。 **当前代码**(types.go): ```go type PropertyValue struct { DID string `json:"did"` SIID int `json:"siid"` PIID int `json:"piid"` Code int `json:"code"` Value interface{} `json:"value"` Type string `json:"type"` // SPEC format: "bool"/"uint8"/"float"/"string" } ``` **修复方案**:改为注释说明只在 GetProp 场景可用。 --- ### #3 缺少通用的设备类型判断/分类机制 **问题**:用户拿到 `[]*DeviceInfo` 后无法区分哪台是空调、哪台是灯。只能盲目试 `devices.NewAirConditioner(...)` 看返回 error。 **修复方案**:在 `xiaomi/devices/` 新增分类体系: **新文件 `kind.go`**: ```go package devices // Kind 设备大类 type Kind string const ( KindAirConditioner Kind = "air_conditioner" KindLight Kind = "light" KindSwitch Kind = "switch" KindFan Kind = "fan" KindCover Kind = "cover" KindHumidifier Kind = "humidifier" KindVacuum Kind = "vacuum" KindWaterHeater Kind = "water_heater" KindThermostat Kind = "thermostat" KindUnknown Kind = "unknown" ) // Classify 从 DeviceInfo 快速判断设备种类(基于 URN/Model 前缀匹配,不依赖 SPEC 加载) func Classify(info *xiaomi.DeviceInfo) Kind ``` **types.go 中加方法**: ```go func (d *DeviceInfo) DeviceKind() devices.Kind ``` **client.go 中加过滤器**: ```go func FilterByKind(kind devices.Kind) DeviceFilter ``` --- ### #4 缺少 `Device` 基础接口 **问题**:BaseDevice 目前是 struct 而非 interface。设备控制抽象需要一个通用 `Device` 接口让所有设备类型内嵌,工厂函数才能返回统一类型。 **当前代码**(devices/base.go): ```go type BaseDevice struct { client *xiaomi.Client info *xiaomi.DeviceInfo } ``` **修复方案**:在 base.go 中拆分接口和实现: ```go // Device 所有设备控制接口的公共基础 type Device interface { DID() string Name() string Model() string Online() bool Info() *xiaomi.DeviceInfo } // BaseDevice 公共实现(嵌入到具体类型中) type BaseDevice struct { client *xiaomi.Client info *xiaomi.DeviceInfo } // 实现 Device 接口的所有方法... ``` 各设备接口改为内嵌 Device: ```go type AirConditioner interface { Device // ← 嵌入 TurnOn() error // ... } type Light interface { Device TurnOn() error // ... } ``` **新文件 `factory.go`**(通用工厂): ```go // NewDevice 根据 DeviceInfo 自动创建对应的设备控制接口 // 返回 Device,调用方用 type switch 分流 func NewDevice(client *xiaomi.Client, info *xiaomi.DeviceInfo) (Device, error) ``` 使用模式: ```go dev, err := devices.NewDevice(client, info) switch d := dev.(type) { case devices.AirConditioner: d.TurnOn(ctx) case devices.Light: d.SetBrightness(ctx, 80) } ``` --- ## P1 — 重要项 ### #5 HomeInfo 缺少地理位置字段 **问题**:Python 版 `get_homeinfos_async` 返回的 home 包含 `city_id`、`longitude`、`latitude`、`address`,对位置相关自动化有用。 **修复方案**:HomeInfo 结构体加字段: ```go type HomeInfo struct { // ... 现有字段 ... CityID string `json:"city_id"` Longitude float64 `json:"longitude"` Latitude float64 `json:"latitude"` Address string `json:"address"` } ``` --- ### #6 Client 的 homes 缓存语义不清 **问题**:Client struct 定义了 `homes map[string]*HomeInfo` 暗示缓存,但 GetHomeList 每次都调 HTTP API。缓存何时加载、何时失效均未说明。 **修复方案**:去掉 `homes` 缓存字段,Client 保持无状态: ```go type Client struct { inner *miot.MIoTClient mu sync.RWMutex } ``` --- ### #7 枚举值与 SPEC 原始值的映射关系未定义 **问题**:`ACMode` 值是 `"cool"`、`"heat"` 等字符串,但 SPEC 中 mode 是数字 0-4。SpecResolver 需要做双向值映射,文档未提。 **修复方案**:新增 `specs/mapper.go`: ```go package specs // PropertyMapper SPEC 原始值 ↔ 枚举字符串 双向映射 type PropertyMapper struct { rawToEnum map[interface{}]string enumToRaw map[string]interface{} } // NewPropertyMapper 从 SPEC value-list + 外部映射表 构造 func NewPropertyMapper(vlist []miot.MIoTSpecValueItem, valueMap map[interface{}]string) *PropertyMapper func (m *PropertyMapper) ToEnum(raw interface{}) (string, bool) func (m *PropertyMapper) ToRaw(enum string) (interface{}, bool) ``` --- ## P2 — 优化项 ### #8 DeviceInfo 无直接可读的设备类型 **解决**:已通过 P0 #3 的 `DeviceKind()` 方法和 `Classify()` 函数解决。 --- ### #9 各设备接口 OnPropsChanged 返回值不统一 **问题**:OnPropsChanged 返回 `(string, error)`,但 SubProp/SubEvent 返回 `(string, error)` — 这里它们是统一的。但缺少对 subID 语义的说明。 **修复方案**:注释统一说明 subID 是订阅标识符,可用于取消。 --- ### #10 例中 ACMode 枚举值和 SPEC 原始值需对应 **解决**:已通过 P1 #7 的 PropertyMapper 解决。 --- ### #11 使用示例未体现上下文控制和工厂模式 **修复方案**:使用示例改为展示 `context.WithTimeout`、`devices.NewDevice` 通用工厂、`type switch` 分流模式。 --- ## 修复后需新增的 ADR ### ADR-006:所有 IO 方法携带 context.Context - 决定:IO 方法首参加 ctx;订阅/工厂等本地方法不加 - 代价:方法签名变长 ### ADR-007:Device 基础接口 + Kind 分类体系 - 决定:Device 接口为所有设备接口的公共基;Classify 基于 URN 字符串匹配快速分类;NewDevice 为通用工厂 - 代价:Classify 依赖字符串匹配,非常见型号可能误判,匹配表需可扩展 --- ## 修复实施清单 | 序号 | 等级 | 修改文件 | 内容 | |------|------|----------|------| | 1 | P0 | client.go, properties.go, actions.go, devices/*.go | 所有 IO 方法加 ctx context.Context 首参 | | 2 | P0 | types.go | PropertyValue.Type 注释改为"仅 GetProp 场景可用" | | 3 | P0 | devices/kind.go(新) | Kind 枚举 + Classify 函数 | | 3 | P0 | types.go | DeviceInfo.DeviceKind() 方法 | | 3 | P0 | client.go | 新增 FilterByKind | | 4 | P0 | devices/base.go | 拆分 Device 接口 + BaseDevice 实现 | | 4 | P0 | devices/factory.go(新) | NewDevice 通用工厂 | | 4 | P0 | devices/*.go | 各接口内嵌 Device | | 5 | P1 | types.go | HomeInfo 加 CityID/Longitude/Latitude/Address | | 6 | P1 | client.go | Client 去掉 homes 缓存字段 | | 7 | P1 | specs/mapper.go(新) | PropertyMapper 双向值映射 | | 9 | P2 | devices/*.go | OnPropsChanged 统一命名返回值 subID | | 11 | P2 | ARCH_PLAN.md 使用示例 | 更新为 ctx + factory + type switch 模式 | --- ## 修复后的包结构变化 ``` xiaomi/ ├── devices/ │ ├── base.go # 修改:拆分 Device 接口 + BaseDevice 实现 │ ├── kind.go # 新增:Kind 枚举 + Classify 分类 │ ├── factory.go # 新增:NewDevice 通用工厂 │ └── ... # 修改:各接口内嵌 Device,IO 方法加 ctx ├── specs/ │ ├── resolver.go # 不变 │ └── mapper.go # 新增:PropertyMapper 值映射 ├── client.go # 修改:加 ctx、去 homes 缓存、加 FilterByKind ├── types.go # 修改:HomeInfo 加字段、PropertyValue.Type 注释、DeviceKind 方法 ├── properties.go # 修改:加 ctx ├── actions.go # 修改:加 ctx └── ... # 其他 IO 方法加 ctx ``` --- *此报告为独立审查文档,详细修复已在 ARCH_PLAN.md 中执行。*