Browse Source

上传设备保定机制

newdev_shunjiawei
liwei1dao 1 year ago
parent
commit
0d8bae2d4d
  1. 28
      lib/data/models/user_Info.dart
  2. 9
      lib/data/services/network/api.dart
  3. 129
      lib/modules/pairing/controllers/pairing_controller.dart
  4. 51
      lib/modules/pairing/views/pairing_view.dart
  5. 73
      lib/modules/pairing/views/pairing_view_new.dart
  6. 3
      lib/modules/settings/views/settings_view.dart

28
lib/data/models/user_Info.dart

@ -14,14 +14,15 @@ class User {
String name; String name;
String uid; String uid;
String avatar; String avatar;
String deviceid;
String language; String language;
String token; String token;
String mail; String mail;
String phone; String phone;
String phonemac; String phonemac;
String password; String password;
int membershipLevel = 0; //添加会员等级字段,0表示普通用户 int viplv = 0; //添加会员等级字段,0表示普通用户
String earphonemac;
String earphoneauthcode;
// final Int64 lastbettime; // final Int64 lastbettime;
// final Int64 createtime; // final Int64 createtime;
// final String wxopenid; // final String wxopenid;
@ -33,14 +34,15 @@ class User {
required this.name, required this.name,
required this.uid, required this.uid,
required this.avatar, required this.avatar,
required this.deviceid,
required this.language, required this.language,
required this.token, required this.token,
required this.mail, required this.mail,
required this.phone, required this.phone,
required this.phonemac, required this.phonemac,
required this.password, required this.password,
this.membershipLevel = 0, // 添加默认值 required this.viplv, // 添加默认值
required this.earphonemac,
required this.earphoneauthcode,
// required this.lastbettime, // required this.lastbettime,
// required this.createtime, // required this.createtime,
// required this.wxopenid, // required this.wxopenid,
@ -60,14 +62,16 @@ class User {
name: (userData?['name'] as String?) ?? '默认名称', // 提供默认值 name: (userData?['name'] as String?) ?? '默认名称', // 提供默认值
uid: (userData?['uid'] as String?) ?? '', uid: (userData?['uid'] as String?) ?? '',
avatar: (userData?['avatar'] as String?) ?? '', avatar: (userData?['avatar'] as String?) ?? '',
deviceid: (userData?['deviceid'] as String?) ?? '',
language: (userData?['language'] as String?) ?? 'en', language: (userData?['language'] as String?) ?? 'en',
mail: (userData?['mail'] as String?) ?? '', mail: (userData?['mail'] as String?) ?? '',
phone: (userData?['phone'] as String?) ?? '', phone: (userData?['phone'] as String?) ?? '',
phonemac: (userData?['phonemac'] as String?) ?? '', phonemac: (userData?['phonemac'] as String?) ?? '',
password: (userData?['password'] as String?) ?? '', password: (userData?['password'] as String?) ?? '',
membershipLevel: (userData?['membershipLevel'] as int?) ?? 0, // 提供默认值 viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值
earphonemac: (userData?['earphonemac'] as String?) ?? '',
earphoneauthcode: (userData?['earphoneauthcode'] as String?) ?? '',
// lastbettime: _parseTimestamp(userData['lastbettime']), // lastbettime: _parseTimestamp(userData['lastbettime']),
// createtime: _parseTimestamp(userData['createtime']), // createtime: _parseTimestamp(userData['createtime']),
// wxopenid: (userData?['wxopenid'] as String?) ?? '', // wxopenid: (userData?['wxopenid'] as String?) ?? '',
@ -85,13 +89,16 @@ class User {
name: (userData?['name'] as String?) ?? '默认名称', // 提供默认值 name: (userData?['name'] as String?) ?? '默认名称', // 提供默认值
uid: (userData?['uid'] as String?) ?? '', uid: (userData?['uid'] as String?) ?? '',
avatar: (userData?['avatar'] as String?) ?? '', avatar: (userData?['avatar'] as String?) ?? '',
deviceid: (userData?['deviceid'] as String?) ?? '',
language: (userData?['language'] as String?) ?? 'en', language: (userData?['language'] as String?) ?? 'en',
mail: (userData?['mail'] as String?) ?? '', mail: (userData?['mail'] as String?) ?? '',
phone: (userData?['phone'] as String?) ?? '', phone: (userData?['phone'] as String?) ?? '',
phonemac: (userData?['phonemac'] as String?) ?? '', phonemac: (userData?['phonemac'] as String?) ?? '',
password: (userData?['password'] as String?) ?? '', password: (userData?['password'] as String?) ?? '',
viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值
earphonemac: (userData?['earphonemac'] as String?) ?? '',
earphoneauthcode: (userData?['earphoneauthcode'] as String?) ?? '',
// lastbettime: _parseTimestamp(userData['lastbettime']), // lastbettime: _parseTimestamp(userData['lastbettime']),
// createtime: _parseTimestamp(userData['createtime']), // createtime: _parseTimestamp(userData['createtime']),
// wxopenid: (userData?['wxopenid'] as String?) ?? '', // wxopenid: (userData?['wxopenid'] as String?) ?? '',
@ -106,14 +113,17 @@ class User {
'name': name, 'name': name,
'uid': uid, 'uid': uid,
'avatar': avatar, 'avatar': avatar,
'deviceid': deviceid,
'language': language, 'language': language,
'token': token, 'token': token,
'mail': mail, 'mail': mail,
'phone': phone, 'phone': phone,
'phonemac': phonemac, 'phonemac': phonemac,
'password': password, 'password': password,
'membershipLevel': membershipLevel, 'viplv': viplv,
'earphonemac': earphonemac,
'earphoneauthcode': earphoneauthcode,
// 'lastbettime': lastbettime, // 'lastbettime': lastbettime,
// 'createtime': createtime, // 'createtime': createtime,
// 'wxopenid': wxopenid, // 'wxopenid': wxopenid,

9
lib/data/services/network/api.dart

@ -126,4 +126,13 @@ class Api {
params: params, params: params,
); );
} }
//绑定设备
static bindearphone([params]) {
return DioManager().request(
NWMethod.post,
'/api/home/user_bindearphone',
params: params,
);
}
} }

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

@ -1,4 +1,7 @@
import 'package:app_settings/app_settings.dart'; import 'package:app_settings/app_settings.dart';
import 'package:deep_voice/data/models/user_Info.dart';
import 'package:deep_voice/data/services/network/api.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'dart:async'; import 'dart:async';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
@ -15,9 +18,10 @@ enum PairingState {
checking, // 检查中(蓝牙、权限等) checking, // 检查中(蓝牙、权限等)
waitingClassic, // 等待经典蓝牙连接 waitingClassic, // 等待经典蓝牙连接
verifying, // 验证设备 verifying, // 验证设备
manualbinding, //手动绑定设备
associating, // 正在进行BLE设备关联 associating, // 正在进行BLE设备关联
waitingBle, // 等待BLE连接 waitingBle, //等待BLE连接
completed, // 配对完成 completed, //配对完成
error, // 错误状态 error, // 错误状态
} }
@ -31,6 +35,7 @@ enum ErrorType {
deviceBound, // 设备已被绑定 deviceBound, // 设备已被绑定
associationFailed, // 关联失败 associationFailed, // 关联失败
bleTimeout, // BLE连接超时 bleTimeout, // BLE连接超时
authnFailed, // 授权失败
networkError, // 网络错误 networkError, // 网络错误
} }
@ -53,6 +58,7 @@ class PairingController extends GetxController {
final isLoading = false.obs; final isLoading = false.obs;
final showConnectButton = false.obs; final showConnectButton = false.obs;
final showRetryButton = false.obs; final showRetryButton = false.obs;
final showAutoCodeInput = false.obs;
final showSkipButton = false.obs; final showSkipButton = false.obs;
final connectButtonText = '连接设备'.obs; final connectButtonText = '连接设备'.obs;
final retryButtonText = '重试'.obs; final retryButtonText = '重试'.obs;
@ -68,6 +74,7 @@ class PairingController extends GetxController {
// ========== 当前设备信息 ========== // ========== 当前设备信息 ==========
BluetoothDevice? _currentClassicDevice; BluetoothDevice? _currentClassicDevice;
String? _verifiedDeviceAddress; String? _verifiedDeviceAddress;
TextEditingController codeInputController = TextEditingController();
// Public getter for view access // Public getter for view access
BluetoothDevice? get currentDevice => _currentClassicDevice; BluetoothDevice? get currentDevice => _currentClassicDevice;
@ -236,6 +243,7 @@ class PairingController extends GetxController {
showConnectButton.value = false; showConnectButton.value = false;
showRetryButton.value = false; showRetryButton.value = false;
showSkipButton.value = false; showSkipButton.value = false;
showAutoCodeInput.value = false;
switch (state) { switch (state) {
case PairingState.idle: case PairingState.idle:
@ -276,7 +284,12 @@ class PairingController extends GetxController {
_startBleConnection(); _startBleConnection();
}); });
break; break;
case PairingState.manualbinding:
statusText.value = '需要手动版定授权码...';
isLoading.value = false;
showAutoCodeInput.value = true;
showSkipButton.value = true;
break;
case PairingState.completed: case PairingState.completed:
statusText.value = '配对成功!'; statusText.value = '配对成功!';
isLoading.value = false; isLoading.value = false;
@ -306,6 +319,10 @@ class PairingController extends GetxController {
showRetryButton.value = true; showRetryButton.value = true;
retryButtonText.value = '重新关联'; retryButtonText.value = '重新关联';
break; break;
case ErrorType.authnFailed:
showRetryButton.value = true;
retryButtonText.value = '重新授权';
break;
default: default:
showRetryButton.value = true; showRetryButton.value = true;
retryButtonText.value = '重试'; retryButtonText.value = '重试';
@ -393,44 +410,48 @@ class PairingController extends GetxController {
Future<void> _verifyDevice(BluetoothDevice device) async { Future<void> _verifyDevice(BluetoothDevice device) async {
try { try {
Logger.i(_tag, '开始验证设备: ${device.name} (${device.address})'); Logger.i(_tag, '开始验证设备: ${device.name} (${device.address})');
// TODO: 实现API验证逻辑 // TODO: 实现API验证逻辑
// 暂时模拟验证通过 // 暂时模拟验证通过
await Future.delayed(const Duration(seconds: 2)); // await Future.delayed(const Duration(seconds: 2));;
if (await authbindingDevice()) {
// 检查设备名称是否包含 DeepSound 或 DeapSound // 检查设备名称是否包含 DeepSound 或 DeapSound
final deviceName = device.name ?? ''; final deviceName = device.name ?? '';
if (deviceName.contains('DeepSound') || if (deviceName.contains('DeepSound') ||
deviceName.contains('DeapSound')) { deviceName.contains('DeapSound')) {
Logger.i(_tag, '设备验证通过'); Logger.i(_tag, '设备验证通过');
_verifiedDeviceAddress = device.address; _verifiedDeviceAddress = device.address;
// 检查是否已有BLE关联 // 检查是否已有BLE关联
final pairedMacAddress = await _bleManager.getPairedMacAddress(); final pairedMacAddress = await _bleManager.getPairedMacAddress();
Logger.i(_tag, '已关联的MAC地址: $pairedMacAddress'); Logger.i(_tag, '已关联的MAC地址: $pairedMacAddress');
if (pairedMacAddress != null && if (pairedMacAddress != null &&
pairedMacAddress.toUpperCase() == device.address.toUpperCase()) { pairedMacAddress.toUpperCase() == device.address.toUpperCase()) {
// 已有关联,检查BLE连接状态 // 已有关联,检查BLE连接状态
Logger.i(_tag, '设备已关联,检查BLE连接状态'); Logger.i(_tag, '设备已关联,检查BLE连接状态');
final currentBleState = await _bleManager.getConnectionState(); final currentBleState = await _bleManager.getConnectionState();
Logger.i(_tag, '验证时BLE状态: $currentBleState'); Logger.i(_tag, '验证时BLE状态: $currentBleState');
if (currentBleState == 2) { if (currentBleState == 2) {
// BleConnectionState.connected // BleConnectionState.connected
Logger.i(_tag, 'BLE已连接,直接完成配对'); Logger.i(_tag, 'BLE已连接,直接完成配对');
_transition(PairingState.completed); _transition(PairingState.completed);
} else {
Logger.i(_tag, 'BLE未连接,等待BLE连接');
_transition(PairingState.waitingBle);
}
} else { } else {
Logger.i(_tag, 'BLE未连接,等待BLE连接'); // 没有关联或关联的是其他设备,需要进行关联
_transition(PairingState.waitingBle); Logger.i(_tag, '需要进行BLE设备关联');
_transition(PairingState.associating);
} }
} else { } else {
// 没有关联或关联的是其他设备,需要进行关联 // _setError(ErrorType.deviceNotMatch, '请连接 DeepSound 设备');
Logger.i(_tag, '需要进行BLE设备关联'); _transition(PairingState.completed);
_transition(PairingState.associating);
} }
} else { } else {
_setError(ErrorType.deviceNotMatch, '请连接 DeepSound 设备'); //验证失败 进入手动授权
_transition(PairingState.manualbinding);
} }
} catch (e) { } catch (e) {
Logger.e(_tag, '设备验证异常: $e'); Logger.e(_tag, '设备验证异常: $e');
@ -504,6 +525,46 @@ class PairingController extends GetxController {
} }
} }
//自动绑定设备
Future<bool> authbindingDevice() async {
if (User.instance.earphonemac == currentDevice?.address) {
// 已经绑定
return true;
}
// 调用BLE管理器的自动绑定方法
final response = await Api.bindearphone({
"earphonemac": currentDevice?.address,
"earphoneauthcode": "",
});
Logger.i(_tag, '手动授权结果 ${response}');
if (response != null) {
Logger.i(_tag, '设备自动绑定成功');
User.instance.earphonemac = currentDevice?.address ?? '';
return true;
} else {
Logger.e(_tag, '手动绑定失败!');
return false;
}
}
/// 用户点击授权码输入框
void onAutoCodeInputTap() async {
// 调用BLE管理器的自动绑定方法
final response = await Api.bindearphone({
"earphonemac": currentDevice?.address,
"earphoneauthcode": codeInputController.text.trim(),
});
Logger.i(_tag, '手动授权结果 ${response}');
if (response != null) {
_transition(PairingState.verifying);
_verifyDevice(_currentClassicDevice!);
} else {
Logger.e(_tag, '手动绑定失败!');
_setError(ErrorType.authnFailed, '设备授权失败,请检查授权码');
}
}
/// 导航到主页 /// 导航到主页
void _navigateToHome() async { void _navigateToHome() async {
await BleManager.to.startAgentService(); await BleManager.to.startAgentService();

51
lib/modules/pairing/views/pairing_view.dart

@ -336,6 +336,8 @@ class PairingView extends GetView<PairingController> {
return '扫描超时,请确保设备在附近'; return '扫描超时,请确保设备在附近';
case ErrorType.networkError: case ErrorType.networkError:
return '网络错误,请检查网络连接'; return '网络错误,请检查网络连接';
case ErrorType.authnFailed:
return '授权失败,请检查授权码是否正确';
default: default:
return '配对失败,请重试'; return '配对失败,请重试';
} }
@ -433,7 +435,7 @@ class PairingView extends GetView<PairingController> {
final showConnectButton = controller.showConnectButton.value; final showConnectButton = controller.showConnectButton.value;
final showRetryButton = controller.showRetryButton.value; final showRetryButton = controller.showRetryButton.value;
final showSkipButton = controller.showSkipButton.value; final showSkipButton = controller.showSkipButton.value;
final showAutoCodeInput = controller.showAutoCodeInput.value;
return Container( return Container(
width: double.infinity, width: double.infinity,
child: Column( child: Column(
@ -490,6 +492,51 @@ class PairingView extends GetView<PairingController> {
), ),
), ),
), ),
if (showAutoCodeInput)
Column(
children: [
// 输入框
SizedBox(
width: double.infinity,
height: 48.h,
child: TextField(
controller: controller
.codeInputController, // 你可以在 Controller 中声明这个
decoration: InputDecoration(
hintText: '请输入代码',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.r),
),
contentPadding: EdgeInsets.symmetric(horizontal: 16.w),
),
),
),
SizedBox(height: 12.h),
SizedBox(
width: double.infinity,
height: 48.h,
child: ElevatedButton(
onPressed: controller.onAutoCodeInputTap,
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(Get.context!).primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
elevation: 0,
),
child: Text(
'授权绑定',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
),
],
),
// 自动输入代码按钮
// 跳过按钮 // 跳过按钮
if (showSkipButton) ...[ if (showSkipButton) ...[
@ -510,11 +557,9 @@ class PairingView extends GetView<PairingController> {
), ),
), ),
], ],
], ],
), ),
); );
}); });
} }
} }

73
lib/modules/pairing/views/pairing_view_new.dart

@ -21,17 +21,17 @@ class PairingView extends GetView<PairingController> {
children: [ children: [
// 顶部标题 // 顶部标题
_buildHeader(isDarkMode), _buildHeader(isDarkMode),
// 中间内容 // 中间内容
Expanded( Expanded(
child: Center( child: Center(
child: Obx(() => _buildContent(isDarkMode)), child: Obx(() => _buildContent(isDarkMode)),
), ),
), ),
// 底部按钮 // 底部按钮
Obx(() => _buildButtons(isDarkMode)), Obx(() => _buildButtons(isDarkMode)),
SizedBox(height: 20.h), SizedBox(height: 20.h),
], ],
), ),
@ -69,15 +69,15 @@ class PairingView extends GetView<PairingController> {
Widget _buildContent(bool isDarkMode) { Widget _buildContent(bool isDarkMode) {
final state = controller.currentState; final state = controller.currentState;
final isLoading = controller.isLoading.value; final isLoading = controller.isLoading.value;
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
// 状态图标 // 状态图标
_buildStateIcon(state, isLoading, isDarkMode), _buildStateIcon(state, isLoading, isDarkMode),
SizedBox(height: 40.h), SizedBox(height: 40.h),
// 状态文本 // 状态文本
Text( Text(
controller.statusText.value, controller.statusText.value,
@ -87,7 +87,7 @@ class PairingView extends GetView<PairingController> {
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
// 错误详情 // 错误详情
if (state == PairingState.error) ...[ if (state == PairingState.error) ...[
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -132,48 +132,51 @@ class PairingView extends GetView<PairingController> {
), ),
); );
} }
IconData iconData; IconData iconData;
Color iconColor; Color iconColor;
switch (state) { switch (state) {
case PairingState.idle: case PairingState.idle:
case PairingState.checking: case PairingState.checking:
iconData = Icons.bluetooth_searching; iconData = Icons.bluetooth_searching;
iconColor = Colors.blue; iconColor = Colors.blue;
break; break;
case PairingState.waitingClassic: case PairingState.waitingClassic:
iconData = Icons.bluetooth; iconData = Icons.bluetooth;
iconColor = Colors.grey; iconColor = Colors.grey;
break; break;
case PairingState.verifying: case PairingState.verifying:
iconData = Icons.verified_user; iconData = Icons.verified_user;
iconColor = Colors.orange; iconColor = Colors.orange;
break; break;
case PairingState.associating: case PairingState.associating:
iconData = Icons.link; iconData = Icons.link;
iconColor = Colors.blue; iconColor = Colors.blue;
break; break;
case PairingState.waitingBle: case PairingState.waitingBle:
iconData = Icons.sync; iconData = Icons.sync;
iconColor = Colors.blue; iconColor = Colors.blue;
break; break;
case PairingState.manualbinding:
iconData = Icons.blind;
iconColor = Colors.blue;
break;
case PairingState.completed: case PairingState.completed:
iconData = Icons.check_circle; iconData = Icons.check_circle;
iconColor = Colors.green; iconColor = Colors.green;
break; break;
case PairingState.error: case PairingState.error:
iconData = Icons.error; iconData = Icons.error;
iconColor = Colors.red; iconColor = Colors.red;
break; break;
} }
return Container( return Container(
width: 100.w, width: 100.w,
height: 100.w, height: 100.w,
@ -193,8 +196,9 @@ class PairingView extends GetView<PairingController> {
Widget _buildButtons(bool isDarkMode) { Widget _buildButtons(bool isDarkMode) {
final showConnectButton = controller.showConnectButton.value; final showConnectButton = controller.showConnectButton.value;
final showRetryButton = controller.showRetryButton.value; final showRetryButton = controller.showRetryButton.value;
final showAutoCodeInput = controller.showAutoCodeInput.value;
final showSkipButton = controller.showSkipButton.value; final showSkipButton = controller.showSkipButton.value;
return Column( return Column(
children: [ children: [
// 主要操作按钮 // 主要操作按钮
@ -204,16 +208,40 @@ class PairingView extends GetView<PairingController> {
controller.onConnectTap, controller.onConnectTap,
isDarkMode, isDarkMode,
), ),
if (showRetryButton) if (showRetryButton)
_buildPrimaryButton( _buildPrimaryButton(
'重试', '重试',
controller.onRetryTap, controller.onRetryTap,
isDarkMode, isDarkMode,
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
if (showAutoCodeInput)
// 自动输入代码按钮
SizedBox(
width: double.infinity,
height: 48.h,
child: ElevatedButton(
onPressed: controller.onAutoCodeInputTap,
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(Get.context!).primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
elevation: 0,
),
child: Text(
'自动输入代码',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
),
// 跳过按钮 // 跳过按钮
if (showSkipButton) if (showSkipButton)
TextButton( TextButton(
@ -231,7 +259,8 @@ class PairingView extends GetView<PairingController> {
} }
/// 构建主要按钮 /// 构建主要按钮
Widget _buildPrimaryButton(String text, VoidCallback onPressed, bool isDarkMode) { Widget _buildPrimaryButton(
String text, VoidCallback onPressed, bool isDarkMode) {
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
height: 48.h, height: 48.h,
@ -255,4 +284,4 @@ class PairingView extends GetView<PairingController> {
), ),
); );
} }
} }

3
lib/modules/settings/views/settings_view.dart

@ -200,8 +200,7 @@ class SettingsView extends GetView<SettingsController> {
2: 'proUser', //'专业用户' 2: 'proUser', //'专业用户'
}; };
final membershipString = membershipMap[ final membershipString = membershipMap[
controller controller.userInfo.value.viplv] ??
.userInfo.value.membershipLevel] ??
'Unknown'; 'Unknown';
return Text( return Text(
membershipString, membershipString,

Loading…
Cancel
Save