// ═══════════════════════════════════════════════════════════════ // driver_fingerprint_migration.dart // ─────────────────────────────────────────────────────────────── // المنطق ببساطة: // 1. خذ البصمة كما هي من DB // 2. split('_') → احذف آخر جزء (OS version) // 3. join('_') → encrypt → رفع // // مثال: // "abc123_SamsungA51_13" → "abc123_SamsungA51" → encrypt // "TECNO_LH7n-GL_14" → "TECNO_LH7n-GL" → encrypt // "unknown_2412DPC0AG_15" → "unknown_2412DPC0AG" → encrypt // ═══════════════════════════════════════════════════════════════ import 'package:flutter/material.dart'; import '../../../constant/links.dart'; import '../../../controller/functions/crud.dart'; import '../../../controller/functions/encrypt_decrypt.dart'; import '../../../print.dart'; class DriverFingerprintMigrationTool extends StatefulWidget { const DriverFingerprintMigrationTool({super.key}); @override State createState() => _DriverFingerprintMigrationToolState(); } class _DriverFingerprintMigrationToolState extends State { bool _isRunning = false; bool _isDone = false; int _total = 0; int _processed = 0; int _updated = 0; int _failed = 0; String _currentLog = ''; static const int _batchSize = 50; // ───────────────────────────────────────────────────────────── // المنطق الأساسي — حذف آخر جزء بعد "_" // ───────────────────────────────────────────────────────────── String _removeLastSegment(String raw) { final parts = raw.split('_'); if (parts.length <= 1) return raw; // جزء واحد — ما في شيء نحذفه parts.removeLast(); return parts.join('_'); } Future _startMigration() async { setState(() { _isRunning = true; _isDone = false; _processed = 0; _updated = 0; _failed = 0; _currentLog = 'جارٍ جلب بصمات السائقين...'; }); try { final records = await _fetchAll(); if (records == null) { _log('❌ فشل في جلب البيانات'); setState(() => _isRunning = false); return; } _total = records.length; _log('✅ تم جلب $_total بصمة — بدء المعالجة...'); for (int i = 0; i < records.length; i += _batchSize) { final batch = records.skip(i).take(_batchSize).toList(); _log('⚙️ معالجة ${i + 1} → ${i + batch.length} من $_total'); await Future.wait(batch.map(_processSingle)); if (i + _batchSize < records.length) { await Future.delayed(const Duration(milliseconds: 300)); } } _log('🎉 اكتمل!\nمحدَّث: $_updated | فاشل: $_failed'); setState(() { _isDone = true; _isRunning = false; }); } catch (e) { _log('❌ خطأ: $e'); setState(() => _isRunning = false); } } Future>?> _fetchAll() async { try { final response = await CRUD().post( link: AppLink.getAllDriverFingerprints, payload: {'admin_key': 'iuyweiruinakjbfkajkjlkmalkcxnlahd'}, ); 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('fetchAll error: $e'); return null; } } Future _processSingle(Map record) async { final captainId = record['captain_id']?.toString() ?? ''; final rawFp = record['fingerPrint']?.toString() ?? ''; if (captainId.isEmpty || rawFp.isEmpty) { setState(() { _failed++; _processed++; }); return; } try { // ── حذف آخر جزء (OS version) ───────────────────────────── final String newRaw = _removeLastSegment(rawFp); final String encrypted = EncryptionHelper.instance.encryptData(newRaw); Log.print('🔄 [$captainId] "$rawFp" → "$newRaw" → encrypted'); // ── رفع للسيرفر ────────────────────────────────────────── final res = await CRUD().post( link: AppLink.updateDriverFingerprintAdmin, payload: { 'captain_id': captainId, 'fingerprint': encrypted, 'admin_key': 'iuyweiruinakjbfkajkjlkmalkcxnlahd', }, ); if (res != 'failure' && res?['status'] == 'success') { setState(() { _updated++; _processed++; }); } else { setState(() { _failed++; _processed++; }); } } catch (e) { Log.print('❌ [$captainId]: $e'); setState(() { _failed++; _processed++; }); } } void _log(String msg) { Log.print(msg); setState(() => _currentLog = msg); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Driver FP Migration')), 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' '"abc123_Samsung_13" → "abc123_Samsung" → encrypt\n' '"TECNO_LH7n_14" → "TECNO_LH7n" → encrypt', style: TextStyle(fontSize: 13, height: 1.7, fontFamily: 'monospace'), ), ), const SizedBox(height: 24), if (_total > 0) ...[ Text('التقدم: $_processed / $_total', style: const TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 8), LinearProgressIndicator( value: _total > 0 ? _processed / _total : 0, backgroundColor: Colors.grey.shade200, color: _isDone ? Colors.green : Colors.blue, minHeight: 8, ), const SizedBox(height: 16), ], if (_processed > 0) Row(children: [ _chip('محدَّث', _updated, Colors.green), const SizedBox(width: 8), _chip('فاشل', _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', fontSize: 12)), ), const Spacer(), SizedBox( width: double.infinity, height: 52, 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, fontSize: 16)), ], ) : Text( _isDone ? '✅ اكتمل الترحيل' : 'بدء ترحيل بصمات السائقين', style: const TextStyle(color: Colors.white, fontSize: 16), ), ), ), ]), ), ); } Widget _chip(String label, int value, Color color) => 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)), ); }