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 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.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 '../../../../theme/product_theme.dart';
import '../../../../widgets/animated_gradient_background.dart';
@ -53,25 +50,16 @@ class _BadgeBasicControlPageState extends ConsumerState<BadgeBasicControlPage>
Future<void> _loadLastImage() async {
try {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('access_token');
final resp = await http.get(
Uri.parse('${ApiConfig.fullBaseUrl}/badge/history/'),
headers: {if (token != null) 'Authorization': 'Bearer $token'},
).timeout(const Duration(seconds: 10));
if (resp.statusCode == 200) {
final body = jsonDecode(resp.body) as Map<String, dynamic>;
final data = body['data'] as Map<String, dynamic>? ?? {};
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);
}
final apiClient = ref.read(apiClientProvider);
final data = await apiClient.get('/badge/history/');
final images = ((data as Map<String, dynamic>)['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 (_) {}
if (mounted) setState(() => _loading = false);

View File

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

View File

@ -289,23 +289,18 @@ class _BluetoothPageState extends ConsumerState<BluetoothPage>
debugPrint('[Bluetooth] 设备已就绪: $mac$displayName');
} catch (e) {
debugPrint('[Bluetooth] queryByMac 失败($mac): $e');
// API BLE fallback
if (!mounted) return;
final bleDevice = _pendingBleDevices[mac];
setState(() {
if (!_devices.any((d) => d.macAddress == mac)) {
_devices.add(MockDevice(
sn: '',
name: '${_airhubPrefix}设备',
macAddress: mac,
type: DeviceType.plush,
hasAI: true,
bleDevice: bleDevice,
));
}
_isSearching = false;
});
//
setState(() => _isSearching = false);
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) {
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;
setState(() => _isConnecting = false);
@ -564,7 +569,13 @@ class _BluetoothPageState extends ConsumerState<BluetoothPage>
children: [
// - CSS: border-radius: 12px, bg: rgba(255,255,255,0.6), no border
GestureDetector(
onTap: () => context.pop(),
onTap: () {
if (context.canPop()) {
context.pop();
} else {
context.go('/');
}
},
child: Container(
width: 40,
height: 40,
@ -902,7 +913,13 @@ class _BluetoothPageState extends ConsumerState<BluetoothPage>
children: [
// - HTML: frosted glass with border
GestureDetector(
onTap: () => context.pop(),
onTap: () {
if (context.canPop()) {
context.pop();
} else {
context.go('/');
}
},
child: ClipRRect(
borderRadius: BorderRadius.circular(25),
child: Container(

View File

@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_svg/flutter_svg.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 '../widgets/animated_gradient_background.dart';
import '../widgets/gradient_button.dart';
@ -133,6 +134,30 @@ class _WifiConfigPageState extends ConsumerState<WifiConfigPage>
if (_isBinding) return;
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? ?? '';
if (sn.isNotEmpty) {
try {
@ -152,6 +177,16 @@ class _WifiConfigPageState extends ConsumerState<WifiConfigPage>
}
} catch (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;