feat: استيراد كود سيرو إلى تريبز (سيرو @ecfe7568) — بلا تعديل

قرار المالك 2026-07-27: باك إند سيرو PHP هو المعتمد، وتطبيقاته المجرّبة
ميدانياً تحل محل إعادة البناء المؤرشفة. سيرو نفسه لم يُمسّ.

الخريطة:
  backend · payment_server · loction_server · ride_server ·
  passenger_server · docker · dashboard · stress_test  → الجذر
  siro_rider  → apps/rider          siro_driver  → apps/driver
  siro_admin  → dashboards/admin    siro_service → dashboards/service
  android_bot → apps/android_bot    socialBot    → apps/socialBot

نُسخ المتعقَّب في git سيرو فقط عبر `git archive` (3,198 ملفاً / ~169 م.ب)
لا `cp -r` — فاستُثنيت مخلفات البناء تلقائياً. بلا أي تعديل محتوى عمداً:
كل ما يلي يصير فرقاً مقروءاً مقابل المصدر.

لم يُستورد وسببه: siromove.com (الموقع التسويقي يبقى marketing/ في تريبز،
سيرو فيه 8 ملفات) · docs و planning (تريبز له docs/ الخاص) · deploy.sh
(ليس نشراً على سيرفر بل `git add . && git push origin --all` — فخّ في
مستودع آخر) · transit_dashboard (بانتظار قرار مصير backend-transit و
dashboards/transit-web).

⚠️ لا يبني بعد — ثلاثة نواقص متوقعة ومقصودة:
1. `.env` و `lib/env/env.g.dart` غير متعقَّبين في سيرو (أسرار لكل مستأجر):
   كل تطبيق فلاتر يحتاج .env خاصاً ثم توليد env.g.dart بـ build_runner.
2. إعدادات Firebase (9 ملفات google-services.json و GoogleService-Info.plist)
   يستبعدها .gitignore تريبز — ولكل مستأجر مشروع Firebase خاص أصلاً.
3. apps/driver في سيرو يشير إلى `../../Intaleq/packages/get` خارج المستودع →
   يجب ضمّ الحزم داخله أسوة بـ apps/rider.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hamza-Ayed
2026-07-27 05:14:13 +03:00
co-authored by Claude Opus 5
parent 9909d9b4c1
commit 4d8414c96b
3198 changed files with 766859 additions and 0 deletions
@@ -0,0 +1,293 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_driver/constant/box_name.dart';
import 'package:siro_driver/constant/links.dart';
import 'package:siro_driver/controller/functions/crud.dart';
import 'package:siro_driver/models/model/driver/rides_summary_model.dart';
import '../../../main.dart';
class StatisticsController extends GetxController {
bool isLoading = false;
// ═══ Weekly Data ═══
List<DayStat> weeklyStats = [];
double weeklyEarnings = 0;
int weeklyTrips = 0;
double weeklyHours = 0;
// ═══ Monthly Data ═══
List<MonthlyPriceDriverModel> monthlyEarnings = [];
List<MonthlyRideModel> monthlyRides = [];
List<MonthlyDataModel> monthlyDuration = [];
double monthlyTotalEarnings = 0;
int monthlyTotalTrips = 0;
double monthlyTotalHours = 0;
String bestDay = '--';
double bestDayEarnings = 0;
// ═══ Tab State ═══
int selectedTab = 0; // 0=weekly, 1=monthly
@override
void onInit() {
super.onInit();
reloadData();
}
void changeTab(int tab) {
selectedTab = tab;
update();
}
// ═══════ جلب البيانات الأسبوعية ═══════
Future<void> fetchWeeklyData() async {
isLoading = true;
update();
try {
var res = await CRUD().get(
link: AppLink.getWeeklyAggregate,
payload: {
'driver_id': box.read(BoxName.driverID).toString(),
},
);
if (res != null && res != 'failure') {
box.write('stats_weekly_cache', res);
var data = jsonDecode(res);
if (data['message'] is List) {
weeklyStats = (data['message'] as List)
.map((e) => DayStat.fromJson(e))
.toList();
weeklyEarnings = weeklyStats.fold(0, (s, d) => s + d.earnings);
weeklyTrips = weeklyStats.fold(0, (s, d) => s + d.trips);
weeklyHours = weeklyStats.fold(0, (s, d) => s + d.hours);
}
}
} catch (e) {
debugPrint('❌ [Stats] Weekly fetch error: $e');
// Fallback: generate from local data
_generateLocalWeeklyData();
}
isLoading = false;
update();
}
// ═══════ جلب البيانات الشهرية ═══════
Future<void> fetchMonthlyData() async {
try {
// 1. أرباح شهرية
var earningsRes = await CRUD().getWallet(
link: AppLink.getAllPaymentFromRide,
payload: {'driverID': box.read(BoxName.driverID).toString()},
);
if (earningsRes != null && earningsRes != 'failure') {
box.write('stats_monthly_earnings_cache', earningsRes);
var data = jsonDecode(earningsRes);
if (data['message'] is List) {
monthlyEarnings = (data['message'] as List)
.map((e) => MonthlyPriceDriverModel.fromJson(e))
.toList();
monthlyTotalEarnings =
monthlyEarnings.fold(0, (s, d) => s + d.pricePerDay);
// أفضل يوم
if (monthlyEarnings.isNotEmpty) {
var best = monthlyEarnings
.reduce((a, b) => a.pricePerDay > b.pricePerDay ? a : b);
bestDay = best.day.toString();
bestDayEarnings = best.pricePerDay;
}
}
}
// 2. رحلات شهرية
var ridesRes = await CRUD().get(
link: AppLink.getRidesDriverByDay,
payload: {'driver_id': box.read(BoxName.driverID).toString()},
);
if (ridesRes != null && ridesRes != 'failure') {
box.write('stats_monthly_rides_cache', ridesRes);
var data = jsonDecode(ridesRes);
if (data['message'] is List) {
monthlyRides = (data['message'] as List)
.map((e) => MonthlyRideModel.fromJson(e))
.toList();
monthlyTotalTrips = monthlyRides.fold(0, (s, d) => s + d.countRide);
}
}
// 3. ساعات شهرية
var durationRes = await CRUD().get(
link: AppLink.getTotalDriverDuration,
payload: {'driver_id': box.read(BoxName.driverID).toString()},
);
if (durationRes != null && durationRes != 'failure') {
box.write('stats_monthly_duration_cache', durationRes);
var data = jsonDecode(durationRes);
if (data['message'] is List) {
monthlyDuration = (data['message'] as List)
.map((e) => MonthlyDataModel.fromJson(e))
.toList();
monthlyTotalHours =
monthlyDuration.fold(0, (s, d) => s + d.totalDuration.toDouble());
}
}
} catch (e) {
debugPrint('❌ [Stats] Monthly fetch error: $e');
}
update();
}
void _generateLocalWeeklyData() {
// Fallback بيانات محلية عند عدم توفر الـ API
final now = DateTime.now();
weeklyStats = List.generate(7, (i) {
final day = now.subtract(Duration(days: 6 - i));
return DayStat(
date: day,
dayName: _getDayName(day.weekday),
earnings: 0,
trips: 0,
hours: 0,
);
});
}
String _getDayName(int weekday) {
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
return days[weekday - 1];
}
bool _isFetching = false;
Future<void> reloadData({bool force = false}) async {
if (_isFetching) return;
_isFetching = true;
// التحقق من التخزين المؤقت (3 ساعات)
String? lastFetchStr = box.read('stats_last_fetch');
if (!force && lastFetchStr != null) {
try {
DateTime lastFetch = DateTime.parse(lastFetchStr);
if (DateTime.now().difference(lastFetch).inHours < 3) {
debugPrint('ℹ️ [Statistics] Loading cached statistics (last fetch: $lastFetchStr)');
// تحميل الكاش الأسبوعي
String? weeklyCache = box.read('stats_weekly_cache');
if (weeklyCache != null) {
var data = jsonDecode(weeklyCache);
if (data['message'] is List) {
weeklyStats = (data['message'] as List)
.map((e) => DayStat.fromJson(e))
.toList();
weeklyEarnings = weeklyStats.fold(0, (s, d) => s + d.earnings);
weeklyTrips = weeklyStats.fold(0, (s, d) => s + d.trips);
weeklyHours = weeklyStats.fold(0, (s, d) => s + d.hours);
}
}
// تحميل الكاش الشهري (الأرباح)
String? earningsCache = box.read('stats_monthly_earnings_cache');
if (earningsCache != null) {
var data = jsonDecode(earningsCache);
if (data['message'] is List) {
monthlyEarnings = (data['message'] as List)
.map((e) => MonthlyPriceDriverModel.fromJson(e))
.toList();
monthlyTotalEarnings =
monthlyEarnings.fold(0, (s, d) => s + d.pricePerDay);
if (monthlyEarnings.isNotEmpty) {
var best = monthlyEarnings
.reduce((a, b) => a.pricePerDay > b.pricePerDay ? a : b);
bestDay = best.day.toString();
bestDayEarnings = best.pricePerDay;
}
}
}
// تحميل الكاش الشهري (الرحلات)
String? ridesCache = box.read('stats_monthly_rides_cache');
if (ridesCache != null) {
var data = jsonDecode(ridesCache);
if (data['message'] is List) {
monthlyRides = (data['message'] as List)
.map((e) => MonthlyRideModel.fromJson(e))
.toList();
monthlyTotalTrips = monthlyRides.fold(0, (s, d) => s + d.countRide);
}
}
// تحميل الكاش الشهري (المدد)
String? durationCache = box.read('stats_monthly_duration_cache');
if (durationCache != null) {
var data = jsonDecode(durationCache);
if (data['message'] is List) {
monthlyDuration = (data['message'] as List)
.map((e) => MonthlyDataModel.fromJson(e))
.toList();
monthlyTotalHours =
monthlyDuration.fold(0, (s, d) => s + d.totalDuration.toDouble());
}
}
_isFetching = false;
update();
return;
}
} catch (e) {
debugPrint('⚠️ [Statistics] Failed to load cached statistics: $e');
}
}
isLoading = true;
update();
try {
debugPrint('📊 [Statistics] Reloading data from server...');
await fetchWeeklyData();
await fetchMonthlyData();
box.write('stats_last_fetch', DateTime.now().toIso8601String());
debugPrint('📊 [Statistics] Data reload complete.');
} catch (e) {
debugPrint('❌ [Statistics] Error reloading data: $e');
} finally {
_isFetching = false;
isLoading = false;
update();
}
}
}
// ═══════ نموذج إحصائية اليوم ═══════
class DayStat {
final DateTime date;
final String dayName;
final double earnings;
final int trips;
final double hours;
DayStat({
required this.date,
required this.dayName,
required this.earnings,
required this.trips,
required this.hours,
});
factory DayStat.fromJson(Map<String, dynamic> json) {
final date =
DateTime.tryParse(json['day']?.toString() ?? '') ?? DateTime.now();
const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
return DayStat(
date: date,
dayName: dayNames[date.weekday - 1],
earnings: double.tryParse(json['earnings']?.toString() ?? '0') ?? 0,
trips: int.tryParse(json['trips']?.toString() ?? '0') ?? 0,
hours: double.tryParse(json['hours']?.toString() ?? '0') ?? 0,
);
}
}