// 心情 BLoC — 通过 API 加载心情统计数据 import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:nuanji_app/data/models/journal_entry.dart'; import 'package:nuanji_app/data/remote/api_client.dart'; // ===== 模型 ===== /// 心情统计数据 class MoodStats { final List moodCounts; final int streakDays; final int totalJournals; final Mood? dominantMood; const MoodStats({ this.moodCounts = const [], this.streakDays = 0, this.totalJournals = 0, this.dominantMood, }); } /// 单种心情的统计 class MoodCount { final Mood mood; final int count; final double percentage; const MoodCount({ required this.mood, required this.count, required this.percentage, }); } /// 统计周期 enum StatsPeriod { week, month, quarter } // ===== State ===== /// 心情页面状态 class MoodState { final MoodStats stats; final StatsPeriod selectedPeriod; final bool isLoading; final String? errorMessage; const MoodState({ this.stats = const MoodStats(), this.selectedPeriod = StatsPeriod.week, this.isLoading = false, this.errorMessage, }); MoodState copyWith({ MoodStats? stats, StatsPeriod? selectedPeriod, bool? isLoading, String? errorMessage, }) => MoodState( stats: stats ?? this.stats, selectedPeriod: selectedPeriod ?? this.selectedPeriod, isLoading: isLoading ?? this.isLoading, errorMessage: errorMessage, ); } // ===== BLoC ===== /// 心情统计 BLoC — ChangeNotifier 模式 class MoodBloc extends ChangeNotifier { final ApiClient _api; MoodState _state = const MoodState(); MoodState get state => _state; MoodBloc({required ApiClient api}) : _api = api; /// 初始加载 void load() { _state = _state.copyWith(isLoading: true); notifyListeners(); _loadStats(); } /// 切换统计周期 void changePeriod(StatsPeriod period) { if (period == _state.selectedPeriod) return; _state = _state.copyWith(selectedPeriod: period, isLoading: true); notifyListeners(); _loadStats(); } Future _loadStats() async { try { final periodStr = switch (_state.selectedPeriod) { StatsPeriod.week => 'week', StatsPeriod.month => 'month', StatsPeriod.quarter => 'quarter', }; final response = await _api.get( '/diary/stats/mood', queryParams: {'period': periodStr}, ); final body = response.data as Map; final data = body['data'] as Map; final counts = (data['mood_counts'] as List? ?? []) .map((item) { final m = item as Map; return MoodCount( mood: Mood.values.firstWhere( (v) => v.value == m['mood'], orElse: () => Mood.happy, ), count: (m['count'] as num).toInt(), percentage: (m['percentage'] as num).toDouble(), ); }) .toList(); final dominant = data['dominant_mood'] != null ? Mood.values.firstWhere( (v) => v.value == data['dominant_mood'], orElse: () => Mood.happy, ) : null; _state = _state.copyWith( isLoading: false, stats: MoodStats( moodCounts: counts, streakDays: (data['streak_days'] as num?)?.toInt() ?? 0, totalJournals: (data['total_journals'] as num?)?.toInt() ?? 0, dominantMood: dominant, ), ); } catch (e) { debugPrint('MoodBloc._loadStats 失败: $e'); _state = _state.copyWith( isLoading: false, errorMessage: '加载统计数据失败', ); } notifyListeners(); } }