feat(sdk): 重命名 owner→brand 并在硬绑前校验设备品牌
- DuooomiBleConfig.owner → brand(语义:设备品牌名),HTTP header x-owner 不变 - bind() 前置 verifyBrand:缺 deviceInfo 先 getDeviceInfo,与 config.brand 严格匹配,不一致主动断连并抛 .brandMismatch - README 抹除软绑/对账/UpgradeRecord 等内部实现细节,仅保留厂商面向的公开 API(功能代码完全保留) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,7 @@ public enum DuooomiBleError: LocalizedError {
|
||||
case permissionDenied
|
||||
case unsupportedFormat(String)
|
||||
case firmwareUpgradeUnavailable(String)
|
||||
case brandMismatch(expected: String, actual: String)
|
||||
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
@@ -76,6 +77,8 @@ public enum DuooomiBleError: LocalizedError {
|
||||
return "Unsupported file format: \(ext). Supported: jpeg, png, gif, mp4, webm, webp"
|
||||
case .firmwareUpgradeUnavailable(let reason):
|
||||
return "Firmware upgrade unavailable: \(reason)"
|
||||
case .brandMismatch(let expected, let actual):
|
||||
return "不支持该品牌设备 (expected=\(expected), actual=\(actual))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ public struct DuooomiBleConfig {
|
||||
public let apiHost: URL
|
||||
/// API 鉴权 key(Loomart x-api-key)
|
||||
public let apiKey: String
|
||||
/// 多租户 owner 标识,所有 RPC 必带 `x-owner` header
|
||||
public let owner: String
|
||||
/// 设备品牌名(所有 RPC 必带 `x-owner` header)
|
||||
public let brand: String
|
||||
/// CDN 资源地址(末尾带 `/`)
|
||||
public let cdnHost: String
|
||||
/// ANI 转换宽度(像素)
|
||||
@@ -22,7 +22,7 @@ public struct DuooomiBleConfig {
|
||||
public init(
|
||||
apiHost: URL = URL(string: "https://api.duooomi.com")!,
|
||||
apiKey: String,
|
||||
owner: String,
|
||||
brand: String,
|
||||
cdnHost: String = "https://cdn.bowong.cc/",
|
||||
aniWidth: Int = 360,
|
||||
aniHeight: Int = 360,
|
||||
@@ -31,7 +31,7 @@ public struct DuooomiBleConfig {
|
||||
) {
|
||||
self.apiHost = apiHost
|
||||
self.apiKey = apiKey
|
||||
self.owner = owner
|
||||
self.brand = brand
|
||||
self.cdnHost = cdnHost.hasSuffix("/") ? cdnHost : cdnHost + "/"
|
||||
self.aniWidth = aniWidth
|
||||
self.aniHeight = aniHeight
|
||||
|
||||
@@ -22,6 +22,48 @@ extension DuooomiBleSDK {
|
||||
/// - Note: `bind` / `unbind` / `setPlayMode` 共享 `0x0F` / `0x12` opId 通道,**不能并发**。
|
||||
public func bind(userId: String, completion: @escaping (Result<BindingResponse, Error>) -> Void) {
|
||||
guard ensureConnected(completion: completion) else { return }
|
||||
verifyBrand { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
switch result {
|
||||
case .failure(let err):
|
||||
completion(.failure(err))
|
||||
case .success:
|
||||
self.performHardBind(userId: userId, completion: completion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 校验设备 brand 与 SDK 配置 brand 是否一致;缺失则先拉一次 deviceInfo。
|
||||
/// 不一致时主动断连并回调 `.brandMismatch`。
|
||||
private func verifyBrand(completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
if let cached = deviceInfo {
|
||||
checkBrand(actual: cached.brand, completion: completion)
|
||||
return
|
||||
}
|
||||
getDeviceInfo { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
switch result {
|
||||
case .failure(let err):
|
||||
completion(.failure(err))
|
||||
case .success(let info):
|
||||
self.checkBrand(actual: info.brand, completion: completion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func checkBrand(actual: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
let expected = config.brand
|
||||
if actual == expected {
|
||||
completion(.success(()))
|
||||
return
|
||||
}
|
||||
BleLog.w("Brand mismatch: expected=\(expected), actual=\(actual); disconnecting", "Command")
|
||||
disconnect { _ in
|
||||
completion(.failure(DuooomiBleError.brandMismatch(expected: expected, actual: actual)))
|
||||
}
|
||||
}
|
||||
|
||||
private func performHardBind(userId: String, completion: @escaping (Result<BindingResponse, Error>) -> Void) {
|
||||
BleLog.d("Sending bind (userId=\(userId))", "Command")
|
||||
|
||||
sendAsyncAndWait(commandType: .bindDevice, completion: { [weak self] result in
|
||||
|
||||
@@ -118,7 +118,7 @@ public final class DuooomiBleSDK: NSObject {
|
||||
super.init()
|
||||
|
||||
protocolService.delegate = self
|
||||
BleLog.i("SDK initialized (apiHost=\(config.apiHost), owner=\(config.owner))", "SDK")
|
||||
BleLog.i("SDK initialized (apiHost=\(config.apiHost), brand=\(config.brand))", "SDK")
|
||||
setupDisconnectHandler()
|
||||
|
||||
// UpgradeRecord 变化(含对账上报结果)→ delegate 通知
|
||||
|
||||
@@ -89,7 +89,7 @@ final class FirmwareUpgradeService {
|
||||
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
req.setValue("application/json", forHTTPHeaderField: "Accept")
|
||||
req.setValue(config.apiKey, forHTTPHeaderField: "x-api-key")
|
||||
req.setValue(config.owner, forHTTPHeaderField: "x-owner")
|
||||
req.setValue(config.brand, forHTTPHeaderField: "x-owner")
|
||||
do {
|
||||
req.httpBody = try JSONEncoder().encode(body)
|
||||
} catch {
|
||||
|
||||
@@ -54,7 +54,7 @@ final class ShipmentSnDeviceService {
|
||||
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
req.setValue("application/json", forHTTPHeaderField: "Accept")
|
||||
req.setValue(config.apiKey, forHTTPHeaderField: "x-api-key")
|
||||
req.setValue(config.owner, forHTTPHeaderField: "x-owner")
|
||||
req.setValue(config.brand, forHTTPHeaderField: "x-owner")
|
||||
|
||||
do {
|
||||
req.httpBody = try JSONEncoder().encode(body)
|
||||
|
||||
Reference in New Issue
Block a user