/
MPaidos
/
MobilePO
Обзор
Документация
Войти
/
MPaidos
/
MobilePO
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
lib/data/stats_database.dart
154 строки
4 KB
MPaidos
Обновить UI по макету Figma, локальную БД статистики и окружение для Android.
26 май 2026, 08:24
26 май 2026, 08:24
9539e85
Код
Авторство
О чём код?
import 'package:flutter/foundation.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'models/match_record.dart'; /// Локальная БД на устройстве (Hive). Данные лежат в папке приложения и не пропадают после перезапуска. class StatsDatabase { StatsDatabase._(); static const _matchesBox = 'ellias_matches_v1'; static const _metaBox = 'ellias_stats_meta_v1'; static const _nextIdKey = 'next_match_id'; static Box<dynamic>? _matches; static Box<dynamic>? _meta; static bool _ready = false; static String? _lastError; static bool get isReady => _ready; static String? get lastError => _lastError; static Future<void> open() async { if (_ready) return; _lastError = null; try { _matches = await Hive.openBox<dynamic>(_matchesBox); _meta = await Hive.openBox<dynamic>(_metaBox); _ready = true; await _migrateLegacyBox(); debugPrint('StatsDatabase: ready, matches=${_matches!.length}'); } catch (e, st) { _ready = false; _matches = null; _meta = null; _lastError = '$e'; debugPrint('StatsDatabase.open failed: $e\n$st'); } } static Future<MatchRecord> saveMatch({ required String team1, required String team2, required int score1, required int score2, required String difficulty, required int roundsPerTeam, required int secondsPerRound, }) async { _ensureReady(); final id = _nextId(); final words = score1 + score2; final record = MatchRecord( id: id, playedAt: DateTime.now(), team1: team1, team2: team2, score1: score1, score2: score2, wordsGuessed: words, difficulty: difficulty, roundsPerTeam: roundsPerTeam, secondsPerRound: secondsPerRound, ); await _matches!.put(id, record.toMap()); await _matches!.flush(); return record; } static List<MatchRecord> allMatchesNewestFirst() { if (!_ready || _matches == null) return const []; final list = <MatchRecord>[]; for (final key in _matches!.keys) { final raw = _matches!.get(key); if (raw is Map) { list.add(MatchRecord.fromMap(Map<dynamic, dynamic>.from(raw))); } } list.sort((a, b) => b.playedAt.compareTo(a.playedAt)); return list; } static StatsSummary computeSummary() { final matches = allMatchesNewestFirst(); if (matches.isEmpty) return StatsSummary.empty; var words = 0; var withWinner = 0; for (final m in matches) { words += m.wordsGuessed; if (m.score1 != m.score2) withWinner++; } return StatsSummary( totalGames: matches.length, wordsGuessed: words, gamesWithWinner: withWinner, ); } static Future<void> clearAll() async { _ensureReady(); await _matches!.clear(); await _meta!.put(_nextIdKey, 1); await _matches!.flush(); await _meta!.flush(); } static int _nextId() { final current = _meta!.get(_nextIdKey, defaultValue: 1); final id = current is int ? current : 1; _meta!.put(_nextIdKey, id + 1); return id; } /// Перенос записей из старого бокса `alias_stats`. static Future<void> _migrateLegacyBox() async { const legacyName = 'alias_stats'; if (!Hive.isBoxOpen(legacyName)) { try { final legacy = await Hive.openBox<dynamic>(legacyName); if (legacy.isEmpty) { await legacy.close(); return; } var migrated = 0; for (final key in legacy.keys) { final raw = legacy.get(key); if (raw is! Map) continue; final map = Map<dynamic, dynamic>.from(raw); final record = MatchRecord.fromMap(map); await saveMatch( team1: record.team1, team2: record.team2, score1: record.score1, score2: record.score2, difficulty: record.difficulty, roundsPerTeam: record.roundsPerTeam, secondsPerRound: record.secondsPerRound, ); migrated++; } await legacy.clear(); await legacy.close(); if (migrated > 0) debugPrint('StatsDatabase: migrated $migrated legacy rows'); } catch (_) { // Старого бокса нет — нормально. } } } static void _ensureReady() { if (!_ready || _matches == null || _meta == null) { throw StateError(_lastError ?? 'База данных не инициализирована'); } } }