From d16601918c65496208e1004ad3622aba7bb9c65a Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Fri, 25 Jul 2025 20:16:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0ble=20=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner.xcodeproj/project.pbxproj | 2 +- .../controllers/pairing_controller.dart | 28 +++- .../ios/agent_service/Package.swift | 7 +- .../agent_service/AgentServiceImpl.swift | 3 +- .../Sources/user_portrait/UserPortrait.swift | 156 ++++++++++++++++++ 5 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 local_plugins/user_portrait/ios/user_portrait/Sources/user_portrait/UserPortrait.swift diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index c91c43c66..8bf262c98 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 60; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ diff --git a/lib/modules/pairing/controllers/pairing_controller.dart b/lib/modules/pairing/controllers/pairing_controller.dart index eb488a73a..bfa21798c 100644 --- a/lib/modules/pairing/controllers/pairing_controller.dart +++ b/lib/modules/pairing/controllers/pairing_controller.dart @@ -613,8 +613,11 @@ class PairingController extends GetxController { final names = AppConfig.allproductNames(); // 检查是否已有绑定的设备 print("device.address${device.address}"); - final existingDevice = User.instance.devices.firstWhere( - (d) => d.devicetype == 1 && d.devicemac == device.address, + var existingDevice = User.instance.devices.firstWhere( + (d) => + d.devicetype == 1 && + d.devicename == device.name && + d.devicemac == device.address, orElse: () => UserDevice( id: -1, uid: '', @@ -627,7 +630,26 @@ class PairingController extends GetxController { // 已经绑定 return 9; } - + if (Platform.isAndroid) { + // 检查是否已有BLE绑定的设备 + existingDevice = User.instance.devices.firstWhere( + (d) => + d.devicetype == 2 && + d.devicename == device.name && + d.devicemac == device.address, + orElse: () => UserDevice( + id: -1, + uid: '', + devicetype: -1, + devicename: '', + devicemac: '', + license: ''), + ); + if (existingDevice.id != -1) { + // 已经绑定 + return 10; + } + } print( '获取到 deviceName: $deviceName address: ${device.address} names:$names'); if (names.contains(deviceName)) { diff --git a/local_plugins/agent_service/ios/agent_service/Package.swift b/local_plugins/agent_service/ios/agent_service/Package.swift index 278e827c5..cc30a9ad6 100644 --- a/local_plugins/agent_service/ios/agent_service/Package.swift +++ b/local_plugins/agent_service/ios/agent_service/Package.swift @@ -14,7 +14,9 @@ let package = Package( .package(path: "../../../azure_speech/ios/azure_speech"), .package(path: "../../../chat_api/ios/chat_api"), .package(path: "../../../chat_storage/ios/chat_storage"), - .package(path: "../../../music_service/ios/music_service") + .package(path: "../../../music_service/ios/music_service"), + .package(path: "../../../music_service/ios/user_portrait") + ], targets: [ .target( @@ -26,7 +28,8 @@ let package = Package( .product(name: "azure-speech", package: "azure_speech"), .product(name: "chat-api", package: "chat_api"), .product(name: "chat-storage", package: "chat_storage"), - .product(name: "music-service", package: "music_service") + .product(name: "music-service", package: "music_service"), + .product(name: "user-portrait", package: "user_portrait"), ], path: "Sources/agent_service", resources: [ diff --git a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift index 29dd1d360..d5f07c938 100644 --- a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift +++ b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift @@ -8,6 +8,7 @@ import os.log import chat_api import chat_storage import music_service +import user_portrait protocol AgentServiceListener: AnyObject { func onEvent(eventName: String, data: [String: Any]) @@ -78,7 +79,7 @@ class AgentServiceImpl: NSObject { chatApiService = ChatApiService() // chatStorageHelper 现在是单例,不需要初始化 - + UserPortrait.shared.initialize() BleService.shared.setDelegate(self) } diff --git a/local_plugins/user_portrait/ios/user_portrait/Sources/user_portrait/UserPortrait.swift b/local_plugins/user_portrait/ios/user_portrait/Sources/user_portrait/UserPortrait.swift new file mode 100644 index 000000000..e39ae8e5d --- /dev/null +++ b/local_plugins/user_portrait/ios/user_portrait/Sources/user_portrait/UserPortrait.swift @@ -0,0 +1,156 @@ +import Foundation +import UIKit + +public class UserPortrait: NSObject { + private static let TAG = "UserPortrait" + private static let PREF_NAME = "deep_voice_user" + + // 单例实例 + public static let shared = UserPortrait() + + private var appContext: UIApplication? + private var userDefaults: UserDefaults? + private var portrait: [String: Any] = [:] + + // 私有初始化方法,防止外部创建实例 + private override init() { + super.init() + } + + /** + * 初始化用户画像 + */ + public func initialize() { + do { + // 注册通知监听器 + NotificationCenter.default.addObserver( + self, + selector: #selector(handleUpdatePortrait(_:)), + name: NSNotification.Name("com.yunqiinnovation.user_portrait.UPDATE_PORTRAIT"), + object: nil + ) + + // 获取 UserDefaults + appContext = UIApplication.shared + userDefaults = UserDefaults(suiteName: UserPortrait.PREF_NAME) ?? UserDefaults.standard + + // 加载已保存的画像数据 + loadPortraitFromDefaults() + + print("\(UserPortrait.TAG): 用户画像初始化成功") + } catch { + print("\(UserPortrait.TAG): 初始化用户画像时发生异常: \(error.localizedDescription)") + } + } + + /** + * 处理更新画像通知 + */ + @objc private func handleUpdatePortrait(_ notification: Notification) { + print("\(UserPortrait.TAG): 接收到通知事件 \(notification.name.rawValue)") + + guard let userInfo = notification.userInfo, + let key = userInfo["key"] as? String, + let value = userInfo["value"] as? String else { + print("\(UserPortrait.TAG): 通知数据格式错误") + return + } + + print("\(UserPortrait.TAG): 收到 UPDATE_PORTRAIT 通知 \(key):\(value)") + + if !key.isEmpty { + print("\(UserPortrait.TAG): 接收到画像更新:\(key) = \(value)") + set(key: key, value: value) + } + } + + /** + * 从 UserDefaults 加载画像数据 + */ + private func loadPortraitFromDefaults() { + guard let defaults = userDefaults else { return } + + // 获取所有保存的键值对 + let allKeys = defaults.dictionaryRepresentation().keys + for key in allKeys { + if let value = defaults.object(forKey: key) { + portrait[key] = value + } + } + } + + /** + * 获取所有画像数据 + */ + public func getAll() -> [String: String] { + return portrait.compactMapValues { value in + return String(describing: value) + } + } + + /** + * 设置画像字段 + */ + public func set(key: String, value: String) { + userDefaults?.set(value, forKey: key) + userDefaults?.synchronize() // 立即同步到磁盘 + + // 更新内存中的画像数据 + portrait[key] = value + + print("\(UserPortrait.TAG): 设置画像字段 \(key) = \(value)") + } + + /** + * 获取指定键的画像值 + */ + public func get(key: String) -> String? { + return portrait[key] as? String + } + + /** + * 清空所有画像数据 + */ + public func clean() { + portrait.removeAll() + + // 清空 UserDefaults 中的数据 + if let defaults = userDefaults { + let allKeys = defaults.dictionaryRepresentation().keys + for key in allKeys { + defaults.removeObject(forKey: key) + } + defaults.synchronize() + } + + print("\(UserPortrait.TAG): 已清空所有画像数据") + } + + /** + * 发送更新画像通知(用于其他模块触发更新) + */ + public func sendUpdateNotification(key: String, value: String) { + let userInfo: [String: Any] = [ + "key": key, + "value": value + ] + + NotificationCenter.default.post( + name: NSNotification.Name("com.yunqiinnovation.user_portrait.UPDATE_PORTRAIT"), + object: nil, + userInfo: userInfo + ) + } + + /** + * 移除通知监听器(在应用退出时调用) + */ + public func deinitialize() { + NotificationCenter.default.removeObserver( + self, + name: NSNotification.Name("com.yunqiinnovation.user_portrait.UPDATE_PORTRAIT"), + object: nil + ) + print("\(UserPortrait.TAG): 用户画像已清理") + } +}