# token 管理重构方案 > 2026-06-29 ## 原则 - mloT 层方法签名不变(`Init()` / `NewMIoTClient` 等) - xiaomi 层只改 `NewClient` 签名(架构必须改),其他全部不动 --- ## 修改清单 ### 1. miot: `Init()` 内部逻辑修复(签名不变) 当前 Init 从 entryData 取 token,storage 白读。改为优先读 storage,首次没数据才回退 entryData 并写入 storage: ```go func (c *MIoTClient) Init() error { // 尝试从 storage 取 token if c.storage != nil { authInfo := c.storage.LoadUserConfig(c.uid, c.cloudServer, nil) if authInfo == nil { // 首次:从 entryData 取,写入 storage c.storage.UpdateUserConfig(c.uid, c.cloudServer, map[string]interface{}{ "access_token": c.entryData["access_token"], "refresh_token": c.entryData["refresh_token"], "expires_ts": c.entryData["expires_ts"], }, true) } else if t, ok := authInfo["access_token"].(string); ok && t != "" { c.entryData["access_token"] = t // 覆盖给后续 HTTP/MIPS 创建用 } } // 后续现有逻辑不变... } ``` ### 2. miot: 新增 `RefreshAuthInfo()`(不破坏旧签名) ```go // RefreshAuthInfo 手动触发 token 刷新(对齐 Python refresh_oauth_info_async) // 返回 {access_token, refresh_token, expires_ts} func (c *MIoTClient) RefreshAuthInfo() (map[string]interface{}, error) ``` 内部复用现有的 `refreshAuthInfo` 逻辑,改为同步返回结果。 ### 3. xiaomi: Config 改签名(唯一需要调用方改的地方) ```go type Config struct { AccessToken string RefreshToken string // 可选,用于自动/手动刷新 } ``` `NewClient(ctx, Config)` 替代 `NewClient(inner, httpClient)`。内部把 `AccessToken` / `RefreshToken` 放进 entryData,Init 自动写入 storage。 ### 4. xiaomi: 新增 `RefreshToken()`(其他方法不动) ```go // RefreshToken 手动刷新 token,返回新值让调用方存库 func (c *Client) RefreshToken(ctx context.Context) (*TokenResult, error) type TokenResult struct { AccessToken string RefreshToken string ExpiresTS int64 } ``` ### 5. 清理 entryData 无用字段 miot 中去掉 `country_code`,xiaomi 中不再出现。 --- ## 影响范围 | 层 | 方法 | 改动 | 调用方影响 | |----|------|------|------------| | miot | `Init()` | 内部逻辑 | 无(签名不变) | | miot | `RefreshAuthInfo()` | 新增 | 无(新增方法) | | xiaomi | `NewClient` | 签名改为 `(ctx, Config)` | **调用方需改** | | xiaomi | `Close()` | 新增 | 无 | | xiaomi | `RefreshToken()` | 新增 | 无 | | xiaomi | 其他所有方法 | 不动 | 无 | --- ## 使用示例 ```go client, err := xiaomi.NewClient(ctx, xiaomi.Config{ AccessToken: token, RefreshToken: refreshToken, }) defer client.Close() // 手动刷新 result, err := client.RefreshToken(ctx) db.Save(userID, result.AccessToken, result.RefreshToken) ```