Browse Source

上传移动设备优化

newdev_shunjiawei
liwei1dao 5 months ago
parent
commit
50e0c393f5
  1. 6
      lib/data/models/appconfig_model.dart
  2. 4
      lib/data/models/appconfig_model.g.dart
  3. 17
      lib/data/services/ble_manager.dart
  4. 7
      lib/modules/home/controllers/home_controller.dart
  5. 3
      lib/modules/mobile_elf/controllers/mobile_elf_controller.dart
  6. 120
      lib/modules/pairing/controllers/pairing_controller.dart
  7. 12
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/StsAgent.kt

6
lib/data/models/appconfig_model.dart

@ -87,6 +87,10 @@ class DBProduct {
final bool? broadcastpeertranslate; //通话翻译是否本地播报对方翻译内容 final bool? broadcastpeertranslate; //通话翻译是否本地播报对方翻译内容
final String? updatedescription; final String? updatedescription;
final String? updatepackageaddress; final String? updatepackageaddress;
// 产品渠道方:official / mobile 等,用于判定是否为“移动精灵”设备
final String? channel;
// 渠道附加数据(JSON 字符串),例如:{"platform_id":"...","platform_key":"...","tui":"..."}
final String? channelextra;
DBProduct({ DBProduct({
required this.id, required this.id,
@ -102,6 +106,8 @@ class DBProduct {
required this.broadcastpeertranslate, required this.broadcastpeertranslate,
required this.updatedescription, required this.updatedescription,
required this.updatepackageaddress, required this.updatepackageaddress,
this.channel,
this.channelextra,
}); });
factory DBProduct.fromJson(Map<String, dynamic> json) => factory DBProduct.fromJson(Map<String, dynamic> json) =>

4
lib/data/models/appconfig_model.g.dart

@ -87,6 +87,8 @@ DBProduct _$DBProductFromJson(Map<String, dynamic> json) => DBProduct(
broadcastpeertranslate: json['broadcastpeertranslate'] as bool?, broadcastpeertranslate: json['broadcastpeertranslate'] as bool?,
updatedescription: json['updatedescription'] as String?, updatedescription: json['updatedescription'] as String?,
updatepackageaddress: json['updatepackageaddress'] as String?, updatepackageaddress: json['updatepackageaddress'] as String?,
channel: json['channel'] as String?,
channelextra: json['channelextra'] as String?,
); );
Map<String, dynamic> _$DBProductToJson(DBProduct instance) => <String, dynamic>{ Map<String, dynamic> _$DBProductToJson(DBProduct instance) => <String, dynamic>{
@ -103,4 +105,6 @@ Map<String, dynamic> _$DBProductToJson(DBProduct instance) => <String, dynamic>{
'broadcastpeertranslate': instance.broadcastpeertranslate, 'broadcastpeertranslate': instance.broadcastpeertranslate,
'updatedescription': instance.updatedescription, 'updatedescription': instance.updatedescription,
'updatepackageaddress': instance.updatepackageaddress, 'updatepackageaddress': instance.updatepackageaddress,
'channel': instance.channel,
'channelextra': instance.channelextra,
}; };

17
lib/data/services/ble_manager.dart

@ -955,16 +955,15 @@ class BleManager extends GetxService {
} }
// STS 模式参数(移动精灵) // STS 模式参数(移动精灵)
// 优先使用设备检测结果(YIDONG_PID 匹配时写入),否则用服务端 AGENT_TYPE // 仅以配对时根据 DBProduct.channel=='mobile' 写入的 storage 为准,
final agentType = _storage.read<String>('device_agent_type') // 不做任何 env 兜底——若 storage 里无对应参数,进入移动精灵时直接报错暴露问题
?? AppConfig.env('AGENT_TYPE') final agentType = _storage.read<String>('device_agent_type') ?? '';
?? 'stt_llm_tts';
final agentMode = (agentType == 'yidong') ? 'sts' : 'stt_llm_tts'; final agentMode = (agentType == 'yidong') ? 'sts' : 'stt_llm_tts';
final agentId = (agentType == 'yidong') final agentId = (agentType == 'yidong') ? 'mobile_elf' : 'default_agent';
? (AppConfig.env('MOBILE_ELF_AGENT_ID') ?? 'mobile_elf') // STS 平台认证参数从 DBProduct.channelextra 解析后存入 storage,
: 'default_agent'; // 不再读取 .env 中的 MOBILE_ELF_PRODUCT_ID/KEY
final stsProductId = AppConfig.env('MOBILE_ELF_PRODUCT_ID') ?? ''; final stsProductId = _storage.read<String>('sts_product_id') ?? '';
final stsProductKey = AppConfig.env('MOBILE_ELF_PRODUCT_KEY') ?? ''; final stsProductKey = _storage.read<String>('sts_product_key') ?? '';
// 使用当前连接设备的 SN(配对时写入),认证失败由原生层上报 credential_failed 事件 // 使用当前连接设备的 SN(配对时写入),认证失败由原生层上报 credential_failed 事件
final stsDeviceNo = _storage.read<String>('device_sts_code') ?? ''; final stsDeviceNo = _storage.read<String>('device_sts_code') ?? '';
final stsDeviceId = _storage.read<String>('sts_device_id') ?? ''; final stsDeviceId = _storage.read<String>('sts_device_id') ?? '';

7
lib/modules/home/controllers/home_controller.dart

@ -222,10 +222,9 @@ class HomeController extends GetxController {
} }
void exploreAIFeatures() { void exploreAIFeatures() {
// 优先读设备检测写入的 storage(YIDONG_PID 匹配时),否则降级到服务端 AGENT_TYPE // 按设备渠道路由:配对时 DBProduct.channel=='mobile' 才会写入 device_agent_type='yidong',
final agentType = GetStorage().read<String>('device_agent_type') // 其他渠道(官方等)默认走单段式 stt_llm_tts 逻辑。不再读取 .env 中的 AGENT_TYPE。
?? AppConfig.env('AGENT_TYPE') final agentType = GetStorage().read<String>('device_agent_type') ?? '';
?? 'stt_llm_tts';
if (agentType == 'yidong') { if (agentType == 'yidong') {
Get.toNamed(Routes.mobileElf); Get.toNamed(Routes.mobileElf);
} else { } else {

3
lib/modules/mobile_elf/controllers/mobile_elf_controller.dart

@ -6,7 +6,6 @@ import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart'; import 'package:get_storage/get_storage.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import '../../../data/models/appconfig.dart';
import '../../../core/utils/logger.dart'; import '../../../core/utils/logger.dart';
import '../../../core/utils/permission_util.dart'; import '../../../core/utils/permission_util.dart';
@ -68,7 +67,7 @@ class MobileElfController extends GetxController {
// 生命周期 // 生命周期
// ═══════════════════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════════════════
String get _agentId => AppConfig.env('MOBILE_ELF_AGENT_ID') ?? 'mobile_elf'; String get _agentId => 'mobile_elf';
@override @override
void onInit() { void onInit() {

120
lib/modules/pairing/controllers/pairing_controller.dart

@ -1,5 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'package:app_settings/app_settings.dart'; import 'package:app_settings/app_settings.dart';
import '/data/models/appconfig_model.dart'; import '/data/models/appconfig_model.dart';
@ -1194,15 +1195,12 @@ class PairingController extends GetxController with WidgetsBindingObserver {
} }
} }
/// 检测是否为移动精灵(YIDONG)设备,并写入 GetStorage /// 检测是否为移动精灵设备,并写入 GetStorage
/// 判定依据:服务端下发的 DBProduct.channel == 'mobile'
/// 平台认证参数从 DBProduct.channelextra (JSON) 中解析:
/// { "platform_id": "...", "platform_key": "...", "tui": "..." }
void _detectAndSaveYidongDevice() { void _detectAndSaveYidongDevice() {
final yidongPid = AppConfig.env('YIDONG_PID') ?? ''; // 1) 获取当前设备 pid(BLE 扫描 > 已绑定设备)
if (yidongPid.isEmpty) {
Logger.i(_tag, '[YIDONG] 未配置 YIDONG_PID,跳过检测');
return;
}
// 优先使用本次扫描解析的 pid,否则从已绑定设备列表中查找
int? pid = _verifiedDevicePid; int? pid = _verifiedDevicePid;
String pidSource = 'BLE扫描'; String pidSource = 'BLE扫描';
if (pid == null) { if (pid == null) {
@ -1229,43 +1227,97 @@ class PairingController extends GetxController with WidgetsBindingObserver {
return; return;
} }
// 将 int pid 转为十六进制字符串(不补零,大写)与 YIDONG_PID 比对 // 2) 从服务端下发的产品列表中查找对应产品
final pidHex = pid.toRadixString(16).toUpperCase(); DBProduct? product;
final isYidong = pidHex == yidongPid.toUpperCase(); try {
product = AppConfig.config.products.firstWhere((p) => p.id == pid);
Logger.i(_tag, '[YIDONG] 设备检测: pidHex=$pidHex yidongPid=${yidongPid.toUpperCase()} source=$pidSource matched=$isYidong'); } catch (_) {
product = null;
if (isYidong) { }
_storage.write('device_agent_type', 'yidong'); if (product == null) {
// deviceCode 来源: Logger.i(_tag, '[YIDONG] 未在 AppConfig.products 中找到 pid=$pid 对应产品,清理旧凭证');
// 新设备 → BLE 扫描从广播包提取 _clearYidongStorage();
// 已绑定设备 → authbindingDevice 从 existingDevice.license 赋值 return;
if (deviceCode.value.isNotEmpty) { }
final prevCode = _storage.read<String>('device_sts_code') ?? '';
// SN 变了说明换了设备,清除旧凭证,强制重新向移动平台认证 final channel = (product.channel ?? '').trim().toLowerCase();
if (prevCode != deviceCode.value) { final isYidong = channel == 'mobile';
_storage.remove('sts_device_id'); Logger.i(_tag,
_storage.remove('sts_device_secret'); '[YIDONG] 设备检测: pid=$pid channel=${product.channel} source=$pidSource matched=$isYidong');
Logger.i(_tag, '[YIDONG] SN 变更($prevCode → ${deviceCode.value}),已清除旧凭证,强制重新认证');
if (!isYidong) {
_clearYidongStorage();
return;
}
// 3) 解析 channelextra,取 platform_id / platform_key / tui
String platformId = '';
String platformKey = '';
String tui = '';
final extra = product.channelextra;
if (extra != null && extra.isNotEmpty) {
try {
final dynamic decoded = jsonDecode(extra);
if (decoded is Map) {
platformId = (decoded['platform_id'] ?? '').toString();
platformKey = (decoded['platform_key'] ?? '').toString();
tui = (decoded['tui'] ?? '').toString();
} }
_storage.write('device_sts_code', deviceCode.value); } catch (e) {
Logger.i(_tag, '[YIDONG] 识别成功,写入 device_sts_code=${deviceCode.value}'); Logger.e(_tag, '[YIDONG] 解析 channelextra 失败: $e raw=$extra');
} else { }
// 既无扫描结果也无 license,清除所有凭证,认证失败会提示用户 }
_storage.remove('device_sts_code'); if (platformId.isEmpty || platformKey.isEmpty) {
Logger.w(_tag,
'[YIDONG] channelextra 缺失 platform_id/platform_key,无法配置 STS,清理旧凭证');
_clearYidongStorage();
return;
}
// 4) 写入存储:设备标识 + STS 平台认证参数
_storage.write('device_agent_type', 'yidong');
_storage.write('sts_product_id', platformId);
_storage.write('sts_product_key', platformKey);
if (tui.isNotEmpty) {
_storage.write('sts_tui', tui);
} else {
_storage.remove('sts_tui');
}
Logger.i(_tag,
'[YIDONG] 识别成功,已写入 sts_product_id=$platformId sts_product_key=*** tui=$tui');
// deviceCode 来源:新设备=BLE扫描提取;已绑定设备=existingDevice.license
if (deviceCode.value.isNotEmpty) {
final prevCode = _storage.read<String>('device_sts_code') ?? '';
// SN 变了说明换了设备,清除旧凭证,强制重新向移动平台认证
if (prevCode != deviceCode.value) {
_storage.remove('sts_device_id'); _storage.remove('sts_device_id');
_storage.remove('sts_device_secret'); _storage.remove('sts_device_secret');
Logger.i(_tag, '[YIDONG] deviceCode 为空,已清除所有凭证'); Logger.i(_tag,
'[YIDONG] SN 变更($prevCode → ${deviceCode.value}),已清除旧凭证,强制重新认证');
} }
_storage.write('device_sts_code', deviceCode.value);
Logger.i(_tag, '[YIDONG] 写入 device_sts_code=${deviceCode.value}');
} else { } else {
_storage.remove('device_agent_type');
_storage.remove('device_sts_code'); _storage.remove('device_sts_code');
_storage.remove('sts_device_id'); _storage.remove('sts_device_id');
_storage.remove('sts_device_secret'); _storage.remove('sts_device_secret');
Logger.i(_tag, '[YIDONG] 非移动精灵设备,已清除相关存储'); Logger.i(_tag, '[YIDONG] deviceCode 为空,已清除设备凭证');
} }
} }
/// 清理移动精灵相关本地存储
void _clearYidongStorage() {
_storage.remove('device_agent_type');
_storage.remove('device_sts_code');
_storage.remove('sts_device_id');
_storage.remove('sts_device_secret');
_storage.remove('sts_product_id');
_storage.remove('sts_product_key');
_storage.remove('sts_tui');
Logger.i(_tag, '[YIDONG] 非移动精灵设备,已清除相关存储');
}
/// 导航到主页 /// 导航到主页
void _navigateToHome() { void _navigateToHome() {
// 检测是否为移动精灵(YIDONG)设备 // 检测是否为移动精灵(YIDONG)设备

12
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/StsAgent.kt

@ -416,9 +416,19 @@ class StsAgent(
if (inCall) resetVad() if (inCall) resetVad()
} }
/** 清空消息历史 */ /** 清空消息历史(内存 + SQLite) */
fun clearHistory() { fun clearHistory() {
messages = JSONArray() messages = JSONArray()
// 同步删除本地数据库中该 agentId 的所有消息,避免页面 onInit 再次加载到旧消息
val agentId = config.agentId
launch {
try {
val ok = chatStorageHelper.deleteMessages(agentId = agentId)
Log.i(TAG, "[YIDONG] clearHistory: 数据库清理 agentId=$agentId success=$ok")
} catch (e: Exception) {
Log.e(TAG, "[YIDONG] clearHistory: 数据库清理异常 ${e.message}", e)
}
}
} }
/** 释放资源 */ /** 释放资源 */

Loading…
Cancel
Save