| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  | import 'dart:async'; | 
					
						
							|  |  |  |  | import 'package:flutter/foundation.dart'; | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  | import 'package:flutter_new_badger/flutter_new_badger.dart'; | 
					
						
							|  |  |  |  | import 'package:qhd_prevention/http/ApiService.dart'; | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  | import 'package:flutter/material.dart'; | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  | /// 优化版 BadgeManager:每个接口超时保护、增量更新、notify 合并、原生角标 debounce
 | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  | class BadgeManager extends ChangeNotifier { | 
					
						
							|  |  |  |  |   BadgeManager._internal(); | 
					
						
							|  |  |  |  |   static final BadgeManager _instance = BadgeManager._internal(); | 
					
						
							|  |  |  |  |   factory BadgeManager() => _instance; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 各模块未读
 | 
					
						
							|  |  |  |  |   int _appCount = 0; | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |   int _appDysCount = 0; | 
					
						
							|  |  |  |  |   int _appDzgCount = 0; | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |   int _notifCount = 0; | 
					
						
							|  |  |  |  |   int _envInspectCount = 0; | 
					
						
							|  |  |  |  |   int _eightWorkCount = 0; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |   // 读取接口值的公开 getter
 | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |   int get count => _appCount + _notifCount + _envInspectCount + _eightWorkCount; | 
					
						
							|  |  |  |  |   int get appCount => _appCount; | 
					
						
							|  |  |  |  |   int get appDysCount => _appDysCount; | 
					
						
							|  |  |  |  |   int get appDzgCount => _appDzgCount; | 
					
						
							|  |  |  |  |   int get notifCount => _notifCount; | 
					
						
							|  |  |  |  |   int get envInspectCount => _envInspectCount; | 
					
						
							|  |  |  |  |   int get eightWorkCount => _eightWorkCount; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |   // 通知合并控制(短时间内多次更新只触发一次 notify)
 | 
					
						
							|  |  |  |  |   Timer? _notifyTimer; | 
					
						
							|  |  |  |  |   Duration _notifyDelay = const Duration(milliseconds: 180); | 
					
						
							|  |  |  |  |   void _scheduleNotify() { | 
					
						
							|  |  |  |  |     // 如果已有计划,不需要再立即计划(合并)
 | 
					
						
							|  |  |  |  |     _notifyTimer?.cancel(); | 
					
						
							|  |  |  |  |     _notifyTimer = Timer(_notifyDelay, () { | 
					
						
							|  |  |  |  |       try { | 
					
						
							|  |  |  |  |         notifyListeners(); | 
					
						
							|  |  |  |  |       } catch (e) { | 
					
						
							|  |  |  |  |         debugPrint('BadgeManager.notifyListeners error: $e'); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       _notifyTimer = null; | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 原生角标同步 debounce(防止短时间内频繁调用)
 | 
					
						
							|  |  |  |  |   Timer? _syncTimer; | 
					
						
							|  |  |  |  |   Duration _syncDelay = const Duration(milliseconds: 250); | 
					
						
							|  |  |  |  |   void _syncNativeDebounced() { | 
					
						
							|  |  |  |  |     _syncTimer?.cancel(); | 
					
						
							|  |  |  |  |     _syncTimer = Timer(_syncDelay, () { | 
					
						
							|  |  |  |  |       try { | 
					
						
							|  |  |  |  |         final total = count; | 
					
						
							|  |  |  |  |         if (total > 0) { | 
					
						
							|  |  |  |  |           FlutterNewBadger.setBadge(total); | 
					
						
							|  |  |  |  |         } else { | 
					
						
							|  |  |  |  |           FlutterNewBadger.removeBadge(); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       } catch (e) { | 
					
						
							|  |  |  |  |         debugPrint('BadgeManager._syncNativeDebounced error: $e'); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       _syncTimer = null; | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // safe wrapper: 给单个 future 加超时与兜底返回
 | 
					
						
							|  |  |  |  |   Future<T> _safe<T>(Future<T> future, T fallback, {Duration timeout = const Duration(seconds: 5)}) async { | 
					
						
							|  |  |  |  |     try { | 
					
						
							|  |  |  |  |       return await future.timeout(timeout); | 
					
						
							|  |  |  |  |     } catch (e, st) { | 
					
						
							|  |  |  |  |       // 报错不抛出到外层,记录并返回 fallback
 | 
					
						
							|  |  |  |  |       debugPrint('BadgeManager._safe error: $e\n$st'); | 
					
						
							|  |  |  |  |       return fallback; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /// 初始化所有模块(并行发起,但每个接口独立 timeout & 提交结果)
 | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |   Future<void> initAllModules() async { | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |     // 不 await 整个 Future.wait,使调用方不会因为单个慢接口阻塞
 | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |     try { | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |       // 每个请求都通过 _safe 包裹,设置超时与兜底
 | 
					
						
							|  |  |  |  |       final fWork = _safe<Map<String, dynamic>>( | 
					
						
							|  |  |  |  |         ApiService.getWork().then((r) => r as Map<String, dynamic>), | 
					
						
							|  |  |  |  |         <String, dynamic>{}, | 
					
						
							|  |  |  |  |         timeout: const Duration(seconds: 6), | 
					
						
							|  |  |  |  |       ); | 
					
						
							|  |  |  |  |       final fNotif = _safe<Map<String, dynamic>>( | 
					
						
							|  |  |  |  |         ApiService.getNotifRedPoint().then((r) => r as Map<String, dynamic>), | 
					
						
							|  |  |  |  |         <String, dynamic>{}, | 
					
						
							|  |  |  |  |         timeout: const Duration(seconds: 4), | 
					
						
							|  |  |  |  |       ); | 
					
						
							|  |  |  |  |       final fCheck = _safe<Map<String, dynamic>>( | 
					
						
							|  |  |  |  |         ApiService.getSafetyEnvironmentalInspectionCount().then((r) => r as Map<String, dynamic>), | 
					
						
							|  |  |  |  |         <String, dynamic>{}, | 
					
						
							|  |  |  |  |         timeout: const Duration(seconds: 5), | 
					
						
							|  |  |  |  |       ); | 
					
						
							|  |  |  |  |       final fRed = _safe<Map<String, dynamic>>( | 
					
						
							|  |  |  |  |         ApiService.getRedPoint().then((r) => r as Map<String, dynamic>), | 
					
						
							|  |  |  |  |         <String, dynamic>{}, | 
					
						
							|  |  |  |  |         timeout: const Duration(seconds: 5), | 
					
						
							|  |  |  |  |       ); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // 按接口独立处理:完成即更新并合并通知(progressive update)
 | 
					
						
							|  |  |  |  |       fWork.then((workJson) { | 
					
						
							|  |  |  |  |         try { | 
					
						
							|  |  |  |  |           final hid = (workJson['hidCount'] as Map<String, dynamic>?) ?? {}; | 
					
						
							|  |  |  |  |           _appDysCount = (hid['dys'] ?? 0) as int? ?? 0; | 
					
						
							|  |  |  |  |           _appDzgCount = (hid['dzg'] ?? 0) as int? ?? 0; | 
					
						
							|  |  |  |  |           _appCount = _appDysCount + _appDzgCount; | 
					
						
							|  |  |  |  |         } catch (e, st) { | 
					
						
							|  |  |  |  |           debugPrint('BadgeManager.parse workJson error: $e\n$st'); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |         _scheduleNotify(); | 
					
						
							|  |  |  |  |         _syncNativeDebounced(); | 
					
						
							|  |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |       fNotif.then((notifJson) { | 
					
						
							|  |  |  |  |         try { | 
					
						
							|  |  |  |  |           _notifCount = (notifJson['count'] as int?) ?? 0; | 
					
						
							|  |  |  |  |         } catch (e, st) { | 
					
						
							|  |  |  |  |           debugPrint('BadgeManager.parse notifJson error: $e\n$st'); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |         _scheduleNotify(); | 
					
						
							|  |  |  |  |         _syncNativeDebounced(); | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |       fCheck.then((checkJson) { | 
					
						
							|  |  |  |  |         try { | 
					
						
							|  |  |  |  |           // 兼容不同返回结构,防止空指针
 | 
					
						
							|  |  |  |  |           int checkedCount = 0, repulseAndCheckCount = 0, confirmCount = 0, repulseCount = 0; | 
					
						
							|  |  |  |  |           if (checkJson['checkedCount'] is Map) checkedCount = (checkJson['checkedCount']['checkedCount'] ?? 0) as int? ?? 0; | 
					
						
							|  |  |  |  |           if (checkJson['repulseAndCheckCount'] is Map) repulseAndCheckCount = (checkJson['repulseAndCheckCount']['repulseAndCheckCount'] ?? 0) as int? ?? 0; | 
					
						
							|  |  |  |  |           if (checkJson['confirmCount'] is Map) confirmCount = (checkJson['confirmCount']['confirmCount'] ?? 0) as int? ?? 0; | 
					
						
							|  |  |  |  |           if (checkJson['repulseCount'] is Map) repulseCount = (checkJson['repulseCount']['repulseCount'] ?? 0) as int? ?? 0; | 
					
						
							|  |  |  |  |           _envInspectCount = checkedCount + repulseAndCheckCount + confirmCount + repulseCount; | 
					
						
							|  |  |  |  |         } catch (e, st) { | 
					
						
							|  |  |  |  |           debugPrint('BadgeManager.parse checkJson error: $e\n$st'); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |         _scheduleNotify(); | 
					
						
							|  |  |  |  |         _syncNativeDebounced(); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       fRed.then((redPointJson) { | 
					
						
							|  |  |  |  |         try { | 
					
						
							|  |  |  |  |           _eightWorkCount = 0; | 
					
						
							|  |  |  |  |           final m = (redPointJson['count'] as Map<String, dynamic>?) ?? {}; | 
					
						
							|  |  |  |  |           for (final v in m.values) { | 
					
						
							|  |  |  |  |             _eightWorkCount += (v ?? 0) as int? ?? 0; | 
					
						
							|  |  |  |  |           } | 
					
						
							|  |  |  |  |         } catch (e, st) { | 
					
						
							|  |  |  |  |           debugPrint('BadgeManager.parse redPointJson error: $e\n$st'); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |         _scheduleNotify(); | 
					
						
							|  |  |  |  |         _syncNativeDebounced(); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // 可选:等待所有结束(不阻塞调用方),用于 debug 或需要最终一致性的场景
 | 
					
						
							|  |  |  |  |       unawaited(Future.wait([fWork, fNotif, fCheck, fRed]).then((_) { | 
					
						
							|  |  |  |  |         debugPrint('BadgeManager.initAllModules: all done at ${DateTime.now()}'); | 
					
						
							|  |  |  |  |       })); | 
					
						
							|  |  |  |  |     } catch (e, st) { | 
					
						
							|  |  |  |  |       debugPrint('BadgeManager.initAllModules unexpected error: $e\n$st'); | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |   // 下面的 updateX 方法也做了超时保护并使用 _onModuleChanged 合并通知
 | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |   void updateAppCount() async { | 
					
						
							|  |  |  |  |     try { | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |       final workJson = await _safe<Map<String, dynamic>>( | 
					
						
							|  |  |  |  |         ApiService.getWork().then((r) => r as Map<String, dynamic>), | 
					
						
							|  |  |  |  |         <String, dynamic>{}, | 
					
						
							|  |  |  |  |         timeout: const Duration(seconds: 5), | 
					
						
							|  |  |  |  |       ); | 
					
						
							|  |  |  |  |       final hid = (workJson['hidCount'] as Map<String, dynamic>?) ?? {}; | 
					
						
							|  |  |  |  |       _appDysCount = (hid['dys'] ?? 0) as int? ?? 0; | 
					
						
							|  |  |  |  |       _appDzgCount = (hid['dzg'] ?? 0) as int? ?? 0; | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |       _appCount = _appDysCount + _appDzgCount; | 
					
						
							|  |  |  |  |       _onModuleChanged(); | 
					
						
							|  |  |  |  |     } catch (e) { | 
					
						
							|  |  |  |  |       debugPrint('updateAppCount error: $e'); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void updateNotifCount() async { | 
					
						
							|  |  |  |  |     try { | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |       final notifJson = await _safe<Map<String, dynamic>>( | 
					
						
							|  |  |  |  |         ApiService.getNotifRedPoint().then((r) => r as Map<String, dynamic>), | 
					
						
							|  |  |  |  |         <String, dynamic>{}, | 
					
						
							|  |  |  |  |         timeout: const Duration(seconds: 4), | 
					
						
							|  |  |  |  |       ); | 
					
						
							|  |  |  |  |       _notifCount = (notifJson['count'] as int?) ?? 0; | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |       _onModuleChanged(); | 
					
						
							|  |  |  |  |     } catch (e) { | 
					
						
							|  |  |  |  |       debugPrint('updateNotifCount error: $e'); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void updateEnvInspectCount() async { | 
					
						
							|  |  |  |  |     try { | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |       final checkJson = await _safe<Map<String, dynamic>>( | 
					
						
							|  |  |  |  |         ApiService.getSafetyEnvironmentalInspectionCount().then((r) => r as Map<String, dynamic>), | 
					
						
							|  |  |  |  |         <String, dynamic>{}, | 
					
						
							|  |  |  |  |         timeout: const Duration(seconds: 5), | 
					
						
							|  |  |  |  |       ); | 
					
						
							|  |  |  |  |       int checkedCount = 0, repulseAndCheckCount = 0, confirmCount = 0, repulseCount = 0; | 
					
						
							|  |  |  |  |       if (checkJson['checkedCount'] is Map) checkedCount = (checkJson['checkedCount']['checkedCount'] ?? 0) as int? ?? 0; | 
					
						
							|  |  |  |  |       if (checkJson['repulseAndCheckCount'] is Map) repulseAndCheckCount = (checkJson['repulseAndCheckCount']['repulseAndCheckCount'] ?? 0) as int? ?? 0; | 
					
						
							|  |  |  |  |       if (checkJson['confirmCount'] is Map) confirmCount = (checkJson['confirmCount']['confirmCount'] ?? 0) as int? ?? 0; | 
					
						
							|  |  |  |  |       if (checkJson['repulseCount'] is Map) repulseCount = (checkJson['repulseCount']['repulseCount'] ?? 0) as int? ?? 0; | 
					
						
							|  |  |  |  |       _envInspectCount = checkedCount + repulseAndCheckCount + confirmCount + repulseCount; | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |       _onModuleChanged(); | 
					
						
							|  |  |  |  |     } catch (e) { | 
					
						
							|  |  |  |  |       debugPrint('updateEnvInspectCount error: $e'); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void updateEightWorkCount() async { | 
					
						
							|  |  |  |  |     try { | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |       final redPointJson = await _safe<Map<String, dynamic>>( | 
					
						
							|  |  |  |  |         ApiService.getRedPoint().then((r) => r as Map<String, dynamic>), | 
					
						
							|  |  |  |  |         <String, dynamic>{}, | 
					
						
							|  |  |  |  |         timeout: const Duration(seconds: 5), | 
					
						
							|  |  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |       int sum = 0; | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |       final m = (redPointJson['count'] as Map<String, dynamic>?) ?? {}; | 
					
						
							|  |  |  |  |       for (final v in m.values) sum += (v ?? 0) as int? ?? 0; | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |       _eightWorkCount = sum; | 
					
						
							|  |  |  |  |       _onModuleChanged(); | 
					
						
							|  |  |  |  |     } catch (e) { | 
					
						
							|  |  |  |  |       debugPrint('updateEightWorkCount error: $e'); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void clearAll() { | 
					
						
							|  |  |  |  |     _appCount = _notifCount = _envInspectCount = _eightWorkCount = 0; | 
					
						
							|  |  |  |  |     _appDysCount = _appDzgCount = 0; | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |     _syncNativeDebounced(); | 
					
						
							|  |  |  |  |     _scheduleNotify(); | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void _onModuleChanged() { | 
					
						
							| 
									
										
										
										
											2025-09-10 13:48:03 +08:00
										 |  |  |  |     _syncNativeDebounced(); | 
					
						
							|  |  |  |  |     _scheduleNotify(); | 
					
						
							| 
									
										
										
										
											2025-08-07 17:33:16 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | } |