Files
nj/app/lib/features/mood/bloc/mood_bloc.dart
iven 7e3597dc77 feat(diary): B4+B5+B6 后端服务 + F5/F6/F7 前端模块
后端 (erp-diary):
- B4: CommentService 班级成员验证 + 删除评语 + SSE 通知推送
- B4: NotificationService 评语/主题/成就三类通知事件
- B5: StickerService 贴纸包列表 + 贴纸查询 + 模板管理
- B5: AchievementService 成就列表 + 解锁 + SSE 通知
- B6: MoodStatsService 心情统计 + 连续天数
- B6: ContentSafetyService 敏感词过滤框架
- SSE handler 增加 diary.notification.* 事件处理
- 新增 14 个 API 端点 + diary.comment.delete 权限

前端 (Flutter):
- F5: CalendarBloc + 月视图日历 + 日记列表
- F6: MoodBloc + fl_chart 心情饼图 + 统计卡片 + 连续天数
- F7: 贴纸库分类浏览 + 模板画廊
- 首页改为日记流 + 心情快速选择
- 成就页改为徽章收集展示

验证: cargo check ✓ cargo test 17/17 ✓ flutter analyze 0 error
2026-06-01 09:32:09 +08:00

129 lines
3.0 KiB
Dart

// 心情 BLoC — 管理心情统计和趋势数据
import 'package:flutter/material.dart';
import 'package:nuanji_app/data/models/journal_entry.dart';
// ===== 心情统计模型 =====
/// 心情统计数据
class MoodStats {
final List<MoodCount> 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,
});
}
/// 心情趋势数据点
class MoodTrendPoint {
final DateTime date;
final Mood mood;
final int journalCount;
const MoodTrendPoint({
required this.date,
required this.mood,
required this.journalCount,
});
}
/// 统计周期
enum StatsPeriod { week, month, quarter }
// ===== 心情页面状态 =====
/// 心情页面状态
class MoodState {
final MoodStats stats;
final List<MoodTrendPoint> trendData;
final StatsPeriod selectedPeriod;
final bool isLoading;
final String? errorMessage;
const MoodState({
this.stats = const MoodStats(),
this.trendData = const [],
this.selectedPeriod = StatsPeriod.week,
this.isLoading = false,
this.errorMessage,
});
MoodState copyWith({
MoodStats? stats,
List<MoodTrendPoint>? trendData,
StatsPeriod? selectedPeriod,
bool? isLoading,
String? errorMessage,
}) =>
MoodState(
stats: stats ?? this.stats,
trendData: trendData ?? this.trendData,
selectedPeriod: selectedPeriod ?? this.selectedPeriod,
isLoading: isLoading ?? this.isLoading,
errorMessage: errorMessage,
);
}
// ===== 心情 BLoC =====
class MoodBloc extends ChangeNotifier {
MoodState _state = const MoodState();
MoodState get state => _state;
/// 切换统计周期
void changePeriod(StatsPeriod period) {
_state = _state.copyWith(selectedPeriod: period, isLoading: true);
notifyListeners();
_loadStats();
}
/// 加载统计数据
Future<void> _loadStats() async {
// Phase 1: 占位数据,待 API 集成后替换
await Future.delayed(const Duration(milliseconds: 300));
_state = _state.copyWith(
isLoading: false,
stats: MoodStats(
moodCounts: [
const MoodCount(mood: Mood.happy, count: 12, percentage: 40.0),
const MoodCount(mood: Mood.calm, count: 8, percentage: 26.7),
const MoodCount(mood: Mood.thinking, count: 5, percentage: 16.7),
const MoodCount(mood: Mood.sad, count: 3, percentage: 10.0),
const MoodCount(mood: Mood.angry, count: 2, percentage: 6.6),
],
streakDays: 7,
totalJournals: 30,
dominantMood: Mood.happy,
),
);
notifyListeners();
}
/// 初始加载
void load() {
_state = _state.copyWith(isLoading: true);
notifyListeners();
_loadStats();
}
}