Files
AutoTime-Tracker/lib/services/statistics_service.dart
2025-11-13 15:45:28 +08:00

200 lines
5.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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,
);
}
}