// ═══════════════════════════════════════════════════════════════ // fingerprint_migration.dart // ─────────────────────────────────────────────────────────────── // أداة ترحيل البصمات القديمة للنظام الجديد // ─────────────────────────────────────────────────────────────── // المشكلة: // البصمة القديمة = encrypt(androidId_model_osVersion) // البصمة الجديدة = encrypt(androidId_model) // // الحل: // 1. نجيب كل البصمات من السيرفر (batch 50 في المرة) // 2. نفك تشفير كل بصمة بـ EncryptionHelper // 3. نحذف آخر جزء (osVersion) مع الـ _ قبله // 4. نعيد التشفير // 5. نرفع البصمة المحدّثة للسيرفر // // يُستخدم مرة واحدة فقط ثم يُحذف من التطبيق // ═══════════════════════════════════════════════════════════════ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import '../../../constant/links.dart'; import '../../../controller/functions/crud.dart'; import '../../../controller/functions/encrypt_decrypt.dart'; import '../../../print.dart'; class FingerprintMigrationTool extends StatefulWidget { const FingerprintMigrationTool({super.key}); @override State createState() => _FingerprintMigrationToolState(); } class _FingerprintMigrationToolState extends State { // ── حالة الترحيل ────────────────────────────────────────── bool _isRunning = false; bool _isDone = false; int _total = 0; int _processed = 0; int _updated = 0; // بصمات تم تحديثها int _skipped = 0; // بصمات كانت بالفعل بالنظام الجديد int _failed = 0; // فشل في المعالجة String _currentLog = ''; static const int _batchSize = 50; // ───────────────────────────────────────────────────────────── // الدالة الرئيسية للترحيل // ───────────────────────────────────────────────────────────── Future _startMigration() async { setState(() { _isRunning = true; _isDone = false; _processed = 0; _updated = 0; _skipped = 0; _failed = 0; _currentLog = 'جارٍ جلب البصمات من السيرفر...'; }); try { // ── 1. جلب كل البصمات من السيرفر ────────────────────── final allFingerprints = await _fetchAllFingerprints(); if (allFingerprints == null) { _log('❌ فشل في جلب البيانات من السيرفر'); setState(() => _isRunning = false); return; } _total = allFingerprints.length; _log('✅ تم جلب $_total بصمة — بدء المعالجة...'); // ── 2. معالجة على batches ────────────────────────────── for (int i = 0; i < allFingerprints.length; i += _batchSize) { final batch = allFingerprints.skip(i).take(_batchSize).toList(); _log('⚙️ معالجة ${i + 1} → ${i + batch.length} من $_total'); // معالجة الـ batch بالتوازي await Future.wait( batch.map((record) => _processSingleRecord(record)), ); // استراحة قصيرة بين الـ batches لحماية السيرفر if (i + _batchSize < allFingerprints.length) { await Future.delayed(const Duration(milliseconds: 300)); } } _log('🎉 اكتمل الترحيل!\n' 'محدَّث: $_updated | متجاوز: $_skipped | فاشل: $_failed'); setState(() { _isDone = true; _isRunning = false; }); } catch (e) { _log('❌ خطأ عام: $e'); setState(() => _isRunning = false); } } // ───────────────────────────────────────────────────────────── // جلب كل البصمات من السيرفر // ───────────────────────────────────────────────────────────── Future>?> _fetchAllFingerprints() async { try { final response = await CRUD().post( link: AppLink.getAllFingerprints, // أضفه في AppLink payload: { 'admin_key': 'iuyweiruinakjbfkajkjlkmalkcxnlahd' }, // مفتاح أمان للـ endpoint ); if (response == 'failure' || response == null) return null; final data = response['data']; if (data is! List) return null; return List>.from(data); } catch (e) { Log.print('fetchAllFingerprints error: $e'); return null; } } // ───────────────────────────────────────────────────────────── // معالجة بصمة واحدة // ───────────────────────────────────────────────────────────── Future _processSingleRecord(Map record) async { final String passengerID = record['passengerID']?.toString() ?? ''; final String encryptedFp = record['fingerPrint']?.toString() ?? ''; final String userType = record['userType']?.toString() ?? 'passenger'; if (passengerID.isEmpty || encryptedFp.isEmpty) { setState(() { _failed++; _processed++; }); return; } try { // ── فك التشفير ──────────────────────────────────────── final String rawFp = EncryptionHelper.instance.decryptData(encryptedFp); // ── تحليل البصمة ────────────────────────────────────── // الشكل القديم: "androidId_model_osVersion" (3 أجزاء أو أكثر) // الشكل الجديد: "androidId_model" (جزءان فقط) final List parts = rawFp.split('_'); if (parts.length <= 2) { // البصمة بالفعل بالنظام الجديد — تجاوزها setState(() { _skipped++; _processed++; }); return; } // ── حذف آخر جزء (osVersion) ────────────────────────── // مثال: "abc123_SamsungA51_13" → "abc123_SamsungA51" // نأخذ أول جزأين فقط بغض النظر عن عدد الأجزاء final String newRawFp = '${parts[0]}_${parts[1]}'; // ── إعادة التشفير ───────────────────────────────────── final String newEncryptedFp = EncryptionHelper.instance.encryptData(newRawFp); // ── رفع البصمة الجديدة للسيرفر ─────────────────────── final response = await CRUD().post( link: AppLink.updateFingerprintAdmin, // أضفه في AppLink payload: { 'passengerID': passengerID, 'fingerprint': newEncryptedFp, 'userType': userType, 'admin_key': 'iuyweiruinakjbfkajkjlkmalkcxnlahd', }, ); if (response != 'failure' && response?['status'] == 'success') { setState(() { _updated++; _processed++; }); Log.print('✅ Updated: $passengerID | $rawFp → $newRawFp'); } else { setState(() { _failed++; _processed++; }); Log.print('❌ Failed update: $passengerID'); } } catch (e) { // فشل فك التشفير أو إعادة التشفير setState(() { _failed++; _processed++; }); Log.print('❌ Process error for $passengerID: $e'); } } void _log(String message) { Log.print(message); setState(() => _currentLog = message); } // ───────────────────────────────────────────────────────────── // UI // ───────────────────────────────────────────────────────────── @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Fingerprint Migration Tool')), body: Padding( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // ── شرح الأداة ────────────────────────────────── Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.orange.shade50, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.orange.shade200), ), child: const Text( '⚠️ هذه الأداة تُستخدم مرة واحدة فقط\n' 'تقوم بتحديث بصمات الأجهزة القديمة\n' 'لتكون متوافقة مع النظام الجديد (بدون OS version)', style: TextStyle(fontSize: 14, height: 1.6), ), ), Container( child: TextButton( onPressed: () { print(EncryptionHelper.instance.decryptData( 'dab40749cdecbfddf4696566448b384f0d272705b08b4ff779e085fbf3257026')); }, child: Text( "Decrypt Test", ), ), ), Container( child: TextButton( onPressed: () { print(EncryptionHelper.instance.encryptData( '1B501143-C579-461C-B556-4E8B390EEFE1_iPhone')); }, child: Text( "Encrypt Test", ), ), ), const SizedBox(height: 24), // ── شريط التقدم ───────────────────────────────── if (_total > 0) ...[ Text('التقدم: $_processed / $_total'), const SizedBox(height: 8), LinearProgressIndicator( value: _total > 0 ? _processed / _total : 0, backgroundColor: Colors.grey.shade200, color: _isDone ? Colors.green : Colors.blue, ), const SizedBox(height: 16), ], // ── إحصائيات ──────────────────────────────────── if (_processed > 0) Row(children: [ _statChip('محدَّث', _updated, Colors.green), const SizedBox(width: 8), _statChip('متجاوز', _skipped, Colors.blue), const SizedBox(width: 8), _statChip('فاشل', _failed, Colors.red), ]), const SizedBox(height: 16), // ── السجل الحالي ───────────────────────────────── if (_currentLog.isNotEmpty) Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(8), ), child: Text(_currentLog, style: const TextStyle(fontFamily: 'monospace')), ), const Spacer(), // ── زر التشغيل ────────────────────────────────── SizedBox( width: double.infinity, height: 50, child: ElevatedButton( onPressed: (_isRunning || _isDone) ? null : _startMigration, style: ElevatedButton.styleFrom( backgroundColor: _isDone ? Colors.green : Colors.blue, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12)), ), child: _isRunning ? const Row( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: 20, height: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2), ), SizedBox(width: 12), Text('جارٍ الترحيل...', style: TextStyle(color: Colors.white)), ], ) : Text( _isDone ? '✅ اكتمل الترحيل' : 'بدء الترحيل', style: const TextStyle(color: Colors.white, fontSize: 16), ), ), ), ], ), ), ); } Widget _statChip(String label, int value, Color color) { return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(20), border: Border.all(color: color.withOpacity(0.3)), ), child: Text('$label: $value', style: TextStyle(color: color, fontWeight: FontWeight.bold)), ); } }