fix: auto repair bugs #83

This commit is contained in:
repair-agent 2026-03-26 16:54:55 +08:00
parent ce249058f2
commit 67ec658bad
5 changed files with 111 additions and 43 deletions

View File

@ -163,5 +163,34 @@ GoRouter goRouter(Ref ref) {
}, },
), ),
], ],
observers: [_BusinessRouteObserver(ref)],
); );
} }
/// SharedPreferences
class _BusinessRouteObserver extends NavigatorObserver {
final Ref _ref;
_BusinessRouteObserver(this._ref);
void _saveIfBusiness(Route<dynamic>? route) {
final name = route?.settings.name;
if (name != null && _validBusinessRoutes.contains(name)) {
final productType = _ref.read(currentProductTypeProvider);
debugPrint('[Router] 保存业务页: $name, productType=${productType.name}');
SharedPreferences.getInstance().then((prefs) {
prefs.setString(_lastRouteKey, name);
prefs.setString(_lastProductTypeKey, productType.name);
});
}
}
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
_saveIfBusiness(route);
}
@override
void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {
_saveIfBusiness(newRoute);
}
}

View File

@ -1,14 +1,11 @@
import 'dart:convert';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import '../../../../core/network/api_config.dart'; import '../../../../core/network/api_client.dart';
import '../../../../pages/profile/profile_page.dart'; import '../../../../pages/profile/profile_page.dart';
import '../../../../theme/product_theme.dart'; import '../../../../theme/product_theme.dart';
import '../../../../widgets/animated_gradient_background.dart'; import '../../../../widgets/animated_gradient_background.dart';
@ -53,25 +50,16 @@ class _BadgeBasicControlPageState extends ConsumerState<BadgeBasicControlPage>
Future<void> _loadLastImage() async { Future<void> _loadLastImage() async {
try { try {
final prefs = await SharedPreferences.getInstance(); final apiClient = ref.read(apiClientProvider);
final token = prefs.getString('access_token'); final data = await apiClient.get('/badge/history/');
final resp = await http.get( final images = ((data as Map<String, dynamic>)['images'] as List<dynamic>? ?? [])
Uri.parse('${ApiConfig.fullBaseUrl}/badge/history/'), .cast<Map<String, dynamic>>()
headers: {if (token != null) 'Authorization': 'Bearer $token'}, .where((img) =>
).timeout(const Duration(seconds: 10)); img['generation_status'] == 'completed' &&
(img['image_url'] as String?)?.isNotEmpty == true)
if (resp.statusCode == 200) { .toList();
final body = jsonDecode(resp.body) as Map<String, dynamic>; if (images.isNotEmpty && mounted) {
final data = body['data'] as Map<String, dynamic>? ?? {}; setState(() => _lastImageUrl = images.first['image_url'] as String);
final images = (data['images'] as List<dynamic>? ?? [])
.cast<Map<String, dynamic>>()
.where((img) =>
img['generation_status'] == 'completed' &&
(img['image_url'] as String?)?.isNotEmpty == true)
.toList();
if (images.isNotEmpty && mounted) {
setState(() => _lastImageUrl = images.first['image_url'] as String);
}
} }
} catch (_) {} } catch (_) {}
if (mounted) setState(() => _loading = false); if (mounted) setState(() => _loading = false);

View File

@ -23,12 +23,11 @@ class DeviceController extends _$DeviceController {
Future<String?> bindDevice(String sn, {int? spiritId}) async { Future<String?> bindDevice(String sn, {int? spiritId}) async {
final repository = ref.read(deviceRepositoryProvider); final repository = ref.read(deviceRepositoryProvider);
final result = await repository.bindDevice(sn, spiritId: spiritId); final result = await repository.bindDevice(sn, spiritId: spiritId);
if (!ref.mounted) return '组件已卸载'; if (!ref.mounted) return null; //
return result.fold( return result.fold(
(failure) => failure.message, (failure) => failure.message,
(bindingId) { (bindingId) {
if (!ref.mounted) return '组件已卸载'; if (ref.mounted) ref.invalidateSelf();
ref.invalidateSelf();
return null; return null;
}, },
); );

View File

@ -289,23 +289,18 @@ class _BluetoothPageState extends ConsumerState<BluetoothPage>
debugPrint('[Bluetooth] 设备已就绪: $mac$displayName'); debugPrint('[Bluetooth] 设备已就绪: $mac$displayName');
} catch (e) { } catch (e) {
debugPrint('[Bluetooth] queryByMac 失败($mac): $e'); debugPrint('[Bluetooth] queryByMac 失败($mac): $e');
// API BLE fallback
if (!mounted) return; if (!mounted) return;
final bleDevice = _pendingBleDevices[mac]; //
setState(() { setState(() => _isSearching = false);
if (!_devices.any((d) => d.macAddress == mac)) {
_devices.add(MockDevice(
sn: '',
name: '${_airhubPrefix}设备',
macAddress: mac,
type: DeviceType.plush,
hasAI: true,
bleDevice: bleDevice,
));
}
_isSearching = false;
});
try { await FlutterBluePlus.stopScan(); } catch (_) {} try { await FlutterBluePlus.stopScan(); } catch (_) {}
_macInfoCache.remove(mac); //
showGlassDialog(
context: context,
title: '设备查询失败',
description: '无法验证设备信息,请检查网络后重试。',
confirmText: '确定',
onConfirm: () => Navigator.of(context).pop(),
);
} }
} }
@ -444,6 +439,16 @@ class _BluetoothPageState extends ConsumerState<BluetoothPage>
} }
} catch (e) { } catch (e) {
debugPrint('[Bluetooth] bindDevice 异常: $e'); debugPrint('[Bluetooth] bindDevice 异常: $e');
if (!mounted) return;
setState(() => _isConnecting = false);
showGlassDialog(
context: context,
title: '绑定失败',
description: '$e',
confirmText: '确定',
onConfirm: () => Navigator.of(context).pop(),
);
return;
} }
if (!mounted) return; if (!mounted) return;
setState(() => _isConnecting = false); setState(() => _isConnecting = false);
@ -564,7 +569,13 @@ class _BluetoothPageState extends ConsumerState<BluetoothPage>
children: [ children: [
// - CSS: border-radius: 12px, bg: rgba(255,255,255,0.6), no border // - CSS: border-radius: 12px, bg: rgba(255,255,255,0.6), no border
GestureDetector( GestureDetector(
onTap: () => context.pop(), onTap: () {
if (context.canPop()) {
context.pop();
} else {
context.go('/');
}
},
child: Container( child: Container(
width: 40, width: 40,
height: 40, height: 40,
@ -902,7 +913,13 @@ class _BluetoothPageState extends ConsumerState<BluetoothPage>
children: [ children: [
// - HTML: frosted glass with border // - HTML: frosted glass with border
GestureDetector( GestureDetector(
onTap: () => context.pop(), onTap: () {
if (context.canPop()) {
context.pop();
} else {
context.go('/');
}
},
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(25), borderRadius: BorderRadius.circular(25),
child: Container( child: Container(

View File

@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import '../core/services/ble_provisioning_service.dart'; import '../core/services/ble_provisioning_service.dart';
import '../features/device/data/datasources/device_remote_data_source.dart';
import '../features/device/presentation/controllers/device_controller.dart'; import '../features/device/presentation/controllers/device_controller.dart';
import '../widgets/animated_gradient_background.dart'; import '../widgets/animated_gradient_background.dart';
import '../widgets/gradient_button.dart'; import '../widgets/gradient_button.dart';
@ -133,6 +134,30 @@ class _WifiConfigPageState extends ConsumerState<WifiConfigPage>
if (_isBinding) return; if (_isBinding) return;
setState(() => _isBinding = true); setState(() => _isBinding = true);
// BLE
final mac = _deviceInfo['mac'] as String? ?? '';
if (mac.isNotEmpty) {
try {
final dataSource = ref.read(deviceRemoteDataSourceProvider);
final macData = await dataSource.queryByMac(mac);
final bindStatus = macData['bind_status'] as String? ?? 'unbound';
if (bindStatus == 'bound_by_other') {
if (!mounted) return;
setState(() => _isBinding = false);
showGlassDialog(
context: context,
title: '无法绑定',
description: '该设备已被其他用户绑定,无法使用。如需解绑请联系设备所有者。',
confirmText: '确定',
onConfirm: () => Navigator.of(context).pop(),
);
return;
}
} catch (e) {
debugPrint('[WiFi Config] 检查设备归属失败: $e');
}
}
final sn = _deviceInfo['sn'] as String? ?? ''; final sn = _deviceInfo['sn'] as String? ?? '';
if (sn.isNotEmpty) { if (sn.isNotEmpty) {
try { try {
@ -152,6 +177,16 @@ class _WifiConfigPageState extends ConsumerState<WifiConfigPage>
} }
} catch (e) { } catch (e) {
debugPrint('[WiFi Config] bindDevice 异常: $e'); debugPrint('[WiFi Config] bindDevice 异常: $e');
if (!mounted) return;
setState(() => _isBinding = false);
showGlassDialog(
context: context,
title: '绑定失败',
description: '$e',
confirmText: '确定',
onConfirm: () => Navigator.of(context).pop(),
);
return;
} }
} }
if (!mounted) return; if (!mounted) return;