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. 96
      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 String? updatedescription;
final String? updatepackageaddress;
// 产品渠道方:official / mobile 等,用于判定是否为“移动精灵”设备
final String? channel;
// 渠道附加数据(JSON 字符串),例如:{"platform_id":"...","platform_key":"...","tui":"..."}
final String? channelextra;
DBProduct({
required this.id,
@ -102,6 +106,8 @@ class DBProduct {
required this.broadcastpeertranslate,
required this.updatedescription,
required this.updatepackageaddress,
this.channel,
this.channelextra,
});
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?,
updatedescription: json['updatedescription'] 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>{
@ -103,4 +105,6 @@ Map<String, dynamic> _$DBProductToJson(DBProduct instance) => <String, dynamic>{
'broadcastpeertranslate': instance.broadcastpeertranslate,
'updatedescription': instance.updatedescription,
'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 模式参数(移动精灵)
// 优先使用设备检测结果(YIDONG_PID 匹配时写入),否则用服务端 AGENT_TYPE
final agentType = _storage.read<String>('device_agent_type')
?? AppConfig.env('AGENT_TYPE')
?? 'stt_llm_tts';
// 仅以配对时根据 DBProduct.channel=='mobile' 写入的 storage 为准,
// 不做任何 env 兜底——若 storage 里无对应参数,进入移动精灵时直接报错暴露问题
final agentType = _storage.read<String>('device_agent_type') ?? '';
final agentMode = (agentType == 'yidong') ? 'sts' : 'stt_llm_tts';
final agentId = (agentType == 'yidong')
? (AppConfig.env('MOBILE_ELF_AGENT_ID') ?? 'mobile_elf')
: 'default_agent';
final stsProductId = AppConfig.env('MOBILE_ELF_PRODUCT_ID') ?? '';
final stsProductKey = AppConfig.env('MOBILE_ELF_PRODUCT_KEY') ?? '';
final agentId = (agentType == 'yidong') ? 'mobile_elf' : 'default_agent';
// STS 平台认证参数从 DBProduct.channelextra 解析后存入 storage,
// 不再读取 .env 中的 MOBILE_ELF_PRODUCT_ID/KEY
final stsProductId = _storage.read<String>('sts_product_id') ?? '';
final stsProductKey = _storage.read<String>('sts_product_key') ?? '';
// 使用当前连接设备的 SN(配对时写入),认证失败由原生层上报 credential_failed 事件
final stsDeviceNo = _storage.read<String>('device_sts_code') ?? '';
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() {
// 优先读设备检测写入的 storage(YIDONG_PID 匹配时),否则降级到服务端 AGENT_TYPE
final agentType = GetStorage().read<String>('device_agent_type')
?? AppConfig.env('AGENT_TYPE')
?? 'stt_llm_tts';
// 按设备渠道路由:配对时 DBProduct.channel=='mobile' 才会写入 device_agent_type='yidong',
// 其他渠道(官方等)默认走单段式 stt_llm_tts 逻辑。不再读取 .env 中的 AGENT_TYPE。
final agentType = GetStorage().read<String>('device_agent_type') ?? '';
if (agentType == 'yidong') {
Get.toNamed(Routes.mobileElf);
} 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:permission_handler/permission_handler.dart';
import '../../../data/models/appconfig.dart';
import '../../../core/utils/logger.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
void onInit() {

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

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

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

@ -416,9 +416,19 @@ class StsAgent(
if (inCall) resetVad()
}
/** 清空消息历史 */
/** 清空消息历史(内存 + SQLite) */
fun clearHistory() {
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