Projektkezeléssel kapcsolatos hibajavítások, projekt törlése a szerveren, bejelentkezés
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 6s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 6s
This commit is contained in:
@@ -15,6 +15,7 @@ import 'package:terepi_seged/models/source_point.dart';
|
||||
import 'package:terepi_seged/models/stakeout_point.dart';
|
||||
import 'package:terepi_seged/models/track.dart';
|
||||
import 'package:terepi_seged/models/vechicle_position_log.dart';
|
||||
import 'package:terepi_seged/services/app_logger.dart';
|
||||
import 'package:terepi_seged/services/device_identity_service.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import '../models/project.dart';
|
||||
@@ -46,7 +47,7 @@ class AppDatabase {
|
||||
final path = p.join(dbDir.path, 'terepi_seged.db');
|
||||
|
||||
return openDatabase(path,
|
||||
version: 7,
|
||||
version: 8,
|
||||
onConfigure: (db) => db.execute('PRAGMA foreign_keys = ON'),
|
||||
onCreate: _onCreate,
|
||||
onUpgrade: _onUpgrade);
|
||||
@@ -239,7 +240,7 @@ class AppDatabase {
|
||||
vertical_error REAL,
|
||||
description TEXT,
|
||||
is_deleted INTEGER NOT NULL DEFAULT 0,
|
||||
project_id INTEGER NOT NULL DEFAULT 2,
|
||||
project_id INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL,
|
||||
sync_status TEXT NOT NULL DEFAULT 'pending'
|
||||
)
|
||||
@@ -274,6 +275,7 @@ class AppDatabase {
|
||||
|
||||
await _addAppInstanceIdColumns(db);
|
||||
await _addContactLocationColumns(db);
|
||||
await _addProjectMissingStreakColumn(db);
|
||||
|
||||
await _createVibratorNavTables(db);
|
||||
|
||||
@@ -315,6 +317,7 @@ class AppDatabase {
|
||||
await _addAppInstanceIdColumns(db);
|
||||
await _createVibratorNavTables(db);
|
||||
await _addContactLocationColumns(db);
|
||||
await _addProjectMissingStreakColumn(db);
|
||||
}
|
||||
|
||||
Future<void> _migrateToV4(Database db) async {
|
||||
@@ -1406,6 +1409,11 @@ class AppDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _addProjectMissingStreakColumn(Database db) async {
|
||||
await _tryExec(db,
|
||||
'ALTER TABLE projects ADD COLUMN missing_streak INTEGER NOT NULL DEFAULT 0');
|
||||
}
|
||||
|
||||
// Future<void> testOnly() async {
|
||||
// final db = await database;
|
||||
// await db.execute(
|
||||
@@ -1493,4 +1501,84 @@ class AppDatabase {
|
||||
where: 'project_id = ?', whereArgs: [projectId]);
|
||||
return rows.map(VehiclePositionLog.fromMap).toList();
|
||||
}
|
||||
|
||||
/// Összeveti a szerver "aktív tagság" listáját a helyi, SZINKRONIZÁLT
|
||||
/// (nem csak-lokális) projektekkel. Ami tartósan (több ciklusban)
|
||||
/// hiányzik onnan, azt — a helyi gyerek-adatokkal EGYÜTT — törli.
|
||||
/// Szándékosan NEM azonnal töröl egyetlen hiányzás után, hogy egy
|
||||
/// átmeneti hálózati/RLS-hiba ne okozhasson véletlen adatvesztést.
|
||||
Future<void> reconcileMissingProjects(Set<String> remoteUuids) async {
|
||||
const missingThreshold = 3; // ennyi egymást követő ciklus után törlünk
|
||||
|
||||
final db = await database;
|
||||
final localSynced = await db.query('projects',
|
||||
where: 'is_local_only = 0',
|
||||
columns: ['id', 'uuid', 'name', 'missing_streak']);
|
||||
|
||||
for (final row in localSynced) {
|
||||
final localId = row['id'] as int;
|
||||
final uuid = row['uuid'] as String;
|
||||
final streak = (row['missing_streak'] as int?) ?? 0;
|
||||
|
||||
if (remoteUuids.contains(uuid)) {
|
||||
if (streak != 0) {
|
||||
await db.update('projects', {'missing_streak': 0},
|
||||
where: 'id = ?', whereArgs: [localId]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
final newStreak = streak + 1;
|
||||
if (newStreak >= missingThreshold) {
|
||||
await _cascadeDeleteLocalProject(localId, uuid, row['name'] as String?);
|
||||
} else {
|
||||
await db.update('projects', {'missing_streak': newStreak},
|
||||
where: 'id = ?', whereArgs: [localId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A projekt ÉS minden helyi, hozzá kötött adat törlése — a szerveren
|
||||
/// már nem létező (törölt vagy tagságból kikerült) projekt helyi
|
||||
/// árváinak eltávolítása.
|
||||
///
|
||||
/// A track_points (tracks-hoz) és a note_item_photos/note_item_audios
|
||||
/// (note_items-hez) már ON DELETE CASCADE-del hivatkoznak a szülőre, és
|
||||
/// a PRAGMA foreign_keys = ON aktív (onConfigure) — ezeket a SQLite
|
||||
/// automatikusan törli, nem kell kézzel foglalkozni velük.
|
||||
///
|
||||
/// A contacts_outbox és a pending_points külön figyelmet igényel:
|
||||
/// a contacts_outbox a projekt UUID-jével (nem a helyi int id-vel)
|
||||
/// van kulcsolva, a pending_points viszont a szokásos int id-vel.
|
||||
Future<void> _cascadeDeleteLocalProject(
|
||||
int localProjectId, String projectUuid, String? name) async {
|
||||
final db = await database;
|
||||
await db.transaction((txn) async {
|
||||
for (final table in [
|
||||
'tracks', // → track_points automatikusan
|
||||
'measured_points',
|
||||
'note_items', // → note_item_photos/audios automatikusan
|
||||
'stakeout_points',
|
||||
'imported_layers',
|
||||
'source_points',
|
||||
'vehicle_position_logs',
|
||||
'pending_points',
|
||||
]) {
|
||||
await txn.delete(table,
|
||||
where: 'project_id = ?', whereArgs: [localProjectId]);
|
||||
}
|
||||
|
||||
// A contacts_outbox a projekt UUID-jét használja kulcsként.
|
||||
await txn.delete('contacts_outbox',
|
||||
where: 'project_id = ?', whereArgs: [projectUuid]);
|
||||
|
||||
await txn
|
||||
.delete('projects', where: 'id = ?', whereArgs: [localProjectId]);
|
||||
});
|
||||
|
||||
AppLogger.e(
|
||||
'AppDatabase',
|
||||
'Projekt helyi törlése: "$name" (id=$localProjectId) — a szerveren '
|
||||
'már nem szerepel a tagsági listában (törölve vagy kikerültünk).');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user