200 lines
5.6 KiB
Dart
200 lines
5.6 KiB
Dart
import 'package:flutter/foundation.dart' show kIsWeb;
|
||
import '../models/daily_stats.dart';
|
||
import '../models/app_usage.dart';
|
||
import '../database/app_usage_dao.dart';
|
||
import '../database/daily_stats_dao.dart';
|
||
import 'mock_data_service.dart';
|
||
|
||
class StatisticsService {
|
||
final AppUsageDao _appUsageDao = AppUsageDao();
|
||
final DailyStatsDao _dailyStatsDao = DailyStatsDao();
|
||
|
||
// 获取今日统计(如果不存在则计算)
|
||
Future<DailyStats> getTodayStats() async {
|
||
// Web 平台返回测试数据
|
||
if (kIsWeb) {
|
||
return MockDataService.generateTodayStats();
|
||
}
|
||
|
||
final today = DateTime.now();
|
||
|
||
// 先查数据库
|
||
var stats = await _dailyStatsDao.getTodayStats();
|
||
if (stats != null) {
|
||
return stats;
|
||
}
|
||
|
||
// 如果不存在,计算并保存
|
||
stats = await _calculateDailyStats(today);
|
||
if (stats != null) {
|
||
await _dailyStatsDao.upsertDailyStats(stats);
|
||
} else {
|
||
// 如果没有数据,返回空统计
|
||
stats = DailyStats(
|
||
date: today,
|
||
totalTime: 0,
|
||
createdAt: DateTime.now(),
|
||
updatedAt: DateTime.now(),
|
||
);
|
||
}
|
||
|
||
return stats;
|
||
}
|
||
|
||
// 计算指定日期的统计
|
||
Future<DailyStats?> _calculateDailyStats(DateTime date) async {
|
||
final startOfDay = DateTime(date.year, date.month, date.day);
|
||
final endOfDay = startOfDay.add(const Duration(days: 1));
|
||
|
||
// 获取该日期的所有应用使用记录
|
||
final appUsages = await _appUsageDao.getAppUsages(
|
||
startTime: startOfDay,
|
||
endTime: endOfDay,
|
||
);
|
||
|
||
if (appUsages.isEmpty) return null;
|
||
|
||
// 按分类聚合
|
||
final categoryTime = <String, int>{};
|
||
int totalTime = 0;
|
||
int appSwitchCount = 0;
|
||
|
||
for (final usage in appUsages) {
|
||
categoryTime[usage.category] = (categoryTime[usage.category] ?? 0) + usage.duration;
|
||
totalTime += usage.duration;
|
||
appSwitchCount += usage.deviceUnlockCount;
|
||
}
|
||
|
||
// 计算效率评分
|
||
final efficiencyScore = _calculateEfficiencyScore(categoryTime, totalTime);
|
||
|
||
// 计算专注度评分
|
||
final focusScore = _calculateFocusScore(appSwitchCount, totalTime);
|
||
|
||
return DailyStats(
|
||
date: startOfDay,
|
||
totalTime: totalTime,
|
||
workTime: categoryTime['work'] ?? 0,
|
||
studyTime: categoryTime['study'] ?? 0,
|
||
entertainmentTime: categoryTime['entertainment'] ?? 0,
|
||
socialTime: categoryTime['social'] ?? 0,
|
||
toolTime: categoryTime['tool'] ?? 0,
|
||
efficiencyScore: efficiencyScore,
|
||
focusScore: focusScore,
|
||
appSwitchCount: appSwitchCount,
|
||
createdAt: DateTime.now(),
|
||
updatedAt: DateTime.now(),
|
||
);
|
||
}
|
||
|
||
// 计算效率评分
|
||
int _calculateEfficiencyScore(Map<String, int> categoryTime, int totalTime) {
|
||
if (totalTime == 0) return 0;
|
||
|
||
// 工作时间占比(40%)
|
||
final workRatio = (categoryTime['work'] ?? 0) / totalTime;
|
||
final workScore = workRatio * 40;
|
||
|
||
// 学习时间占比(30%)
|
||
final studyRatio = (categoryTime['study'] ?? 0) / totalTime;
|
||
final studyScore = studyRatio * 30;
|
||
|
||
// 娱乐时间占比(越低越好,30%)
|
||
final entertainmentRatio = (categoryTime['entertainment'] ?? 0) / totalTime;
|
||
final entertainmentScore = (1 - entertainmentRatio) * 30;
|
||
|
||
return (workScore + studyScore + entertainmentScore).round();
|
||
}
|
||
|
||
// 计算专注度评分
|
||
int _calculateFocusScore(int appSwitchCount, int totalTime) {
|
||
if (totalTime == 0) return 0;
|
||
|
||
// 平均每小时切换次数
|
||
final switchesPerHour = (appSwitchCount / (totalTime / 3600));
|
||
|
||
// 理想情况:每小时切换 < 10 次 = 100分
|
||
// 每小时切换 > 50 次 = 0分
|
||
final score = 100 - (switchesPerHour * 2).clamp(0, 100);
|
||
|
||
return score.round();
|
||
}
|
||
|
||
// 获取本周统计
|
||
Future<List<DailyStats>> getWeekStats() async {
|
||
// Web 平台返回测试数据
|
||
if (kIsWeb) {
|
||
return MockDataService.generateWeekStats();
|
||
}
|
||
|
||
final stats = await _dailyStatsDao.getWeekStats();
|
||
|
||
// 如果数据不完整,计算缺失的日期
|
||
final now = DateTime.now();
|
||
final startOfWeek = now.subtract(Duration(days: now.weekday - 1));
|
||
|
||
final result = <DailyStats>[];
|
||
for (int i = 0; i < 7; i++) {
|
||
final date = startOfWeek.add(Duration(days: i));
|
||
final existing = stats.firstWhere(
|
||
(s) => s.date.year == date.year &&
|
||
s.date.month == date.month &&
|
||
s.date.day == date.day,
|
||
orElse: () => DailyStats(
|
||
date: date,
|
||
totalTime: 0,
|
||
createdAt: DateTime.now(),
|
||
updatedAt: DateTime.now(),
|
||
),
|
||
);
|
||
result.add(existing);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
// 获取本月统计
|
||
Future<List<DailyStats>> getMonthStats() async {
|
||
return await _dailyStatsDao.getMonthStats();
|
||
}
|
||
|
||
// 刷新今日统计(重新计算)
|
||
Future<DailyStats> refreshTodayStats() async {
|
||
final today = DateTime.now();
|
||
final stats = await _calculateDailyStats(today);
|
||
|
||
if (stats != null) {
|
||
await _dailyStatsDao.upsertDailyStats(stats);
|
||
return stats;
|
||
} else {
|
||
final emptyStats = DailyStats(
|
||
date: today,
|
||
totalTime: 0,
|
||
createdAt: DateTime.now(),
|
||
updatedAt: DateTime.now(),
|
||
);
|
||
await _dailyStatsDao.upsertDailyStats(emptyStats);
|
||
return emptyStats;
|
||
}
|
||
}
|
||
|
||
// 获取 Top 应用
|
||
Future<List<AppUsage>> getTopApps({
|
||
required DateTime startTime,
|
||
required DateTime endTime,
|
||
int limit = 10,
|
||
}) async {
|
||
// Web 平台返回测试数据
|
||
if (kIsWeb) {
|
||
return MockDataService.generateTopApps().take(limit).toList();
|
||
}
|
||
|
||
return await _appUsageDao.getTopApps(
|
||
startTime: startTime,
|
||
endTime: endTime,
|
||
limit: limit,
|
||
);
|
||
}
|
||
}
|
||
|