Initial commit du projet Flutter
This commit is contained in:
561
lib/services/wortis_api_service.dart
Normal file
561
lib/services/wortis_api_service.dart
Normal file
@@ -0,0 +1,561 @@
|
||||
// Enhanced lib/services/wortis_api_service.dart
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../models/service_model.dart';
|
||||
|
||||
class WortisApiService {
|
||||
static const String baseUrl = 'https://api.live.wortis.cg/tpe';
|
||||
|
||||
// Singleton pattern
|
||||
static final WortisApiService _instance = WortisApiService._internal();
|
||||
factory WortisApiService() => _instance;
|
||||
WortisApiService._internal();
|
||||
|
||||
// Retry configuration
|
||||
static const int maxRetries = 3;
|
||||
static const Duration retryDelay = Duration(seconds: 2);
|
||||
|
||||
Future<List<WortisService>> getServices() async {
|
||||
return _retryRequest<List<WortisService>>(() async {
|
||||
final response = await http
|
||||
.get(
|
||||
Uri.parse('$baseUrl/get_services_back'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
)
|
||||
.timeout(Duration(seconds: 30));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final Map<String, dynamic> jsonData = json.decode(response.body);
|
||||
final List<dynamic> servicesJson = jsonData['all_services'] ?? [];
|
||||
|
||||
return servicesJson
|
||||
.map((serviceJson) => WortisService.fromJson(serviceJson))
|
||||
.toList();
|
||||
} else {
|
||||
throw Exception('Erreur API: ${response.statusCode}');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Méthode pour grouper les services par secteur
|
||||
Map<String, List<WortisService>> groupServicesBySector(
|
||||
List<WortisService> services,
|
||||
) {
|
||||
Map<String, List<WortisService>> grouped = {};
|
||||
|
||||
for (var service in services) {
|
||||
String sector = service.secteurActivite;
|
||||
if (grouped[sector] == null) {
|
||||
grouped[sector] = [];
|
||||
}
|
||||
grouped[sector]!.add(service);
|
||||
}
|
||||
|
||||
return grouped;
|
||||
}
|
||||
|
||||
// Generic retry logic
|
||||
Future<T> _retryRequest<T>(Future<T> Function() request) async {
|
||||
Exception? lastException;
|
||||
|
||||
for (int attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
return await request();
|
||||
} on SocketException catch (e) {
|
||||
lastException = e;
|
||||
print('Tentative $attempt échouée (SocketException): $e');
|
||||
if (attempt < maxRetries) {
|
||||
await Future.delayed(retryDelay * attempt);
|
||||
}
|
||||
} on HttpException catch (e) {
|
||||
lastException = e;
|
||||
print('Tentative $attempt échouée (HttpException): $e');
|
||||
if (attempt < maxRetries) {
|
||||
await Future.delayed(retryDelay * attempt);
|
||||
}
|
||||
} on http.ClientException catch (e) {
|
||||
lastException = e;
|
||||
print('Tentative $attempt échouée (ClientException): $e');
|
||||
if (attempt < maxRetries) {
|
||||
await Future.delayed(retryDelay * attempt);
|
||||
}
|
||||
} catch (e) {
|
||||
// For other exceptions, don't retry
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
throw Exception(
|
||||
'Impossible de se connecter après $maxRetries tentatives: $lastException',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AuthApiService {
|
||||
static const String baseUrl = 'https://api.live.wortis.cg/tpe';
|
||||
static const int maxRetries = 3;
|
||||
static const Duration retryDelay = Duration(seconds: 2);
|
||||
|
||||
/// Test de connexion à l'API avec retry
|
||||
static Future<bool> testConnection() async {
|
||||
try {
|
||||
return await _retryRequest<bool>(() async {
|
||||
final response = await http
|
||||
.get(
|
||||
Uri.parse('http://google.com/'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
)
|
||||
.timeout(Duration(seconds: 10));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
return data['success'] == true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
} catch (e) {
|
||||
print('Erreur test connexion après plusieurs tentatives: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Connexion utilisateur avec retry
|
||||
static Future<Map<String, dynamic>> login(String agentId, String pin) async {
|
||||
try {
|
||||
return await _retryRequest<Map<String, dynamic>>(() async {
|
||||
final response = await http
|
||||
.post(
|
||||
Uri.parse('$baseUrl/auth/login'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
body: jsonEncode({'agent_id': agentId, 'pin': pin}),
|
||||
)
|
||||
.timeout(Duration(seconds: 15));
|
||||
|
||||
return jsonDecode(response.body);
|
||||
});
|
||||
} catch (e) {
|
||||
print('Erreur login API après retry: $e');
|
||||
return {'success': false, 'message': 'Erreur de connexion au serveur'};
|
||||
}
|
||||
}
|
||||
|
||||
/// Créer un utilisateur avec retry
|
||||
static Future<Map<String, dynamic>> createUser(
|
||||
String nom,
|
||||
String pin, {
|
||||
String role = 'agent',
|
||||
}) async {
|
||||
try {
|
||||
return await _retryRequest<Map<String, dynamic>>(() async {
|
||||
final response = await http
|
||||
.post(
|
||||
Uri.parse('$baseUrl/users/register'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
body: jsonEncode({'nom': nom, 'pin': pin, 'role': role}),
|
||||
)
|
||||
.timeout(Duration(seconds: 15));
|
||||
|
||||
return jsonDecode(response.body);
|
||||
});
|
||||
} catch (e) {
|
||||
print('Erreur création utilisateur après retry: $e');
|
||||
return {'success': false, 'message': 'Erreur de création'};
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupérer le solde d'un agent avec retry
|
||||
static Future<Map<String, dynamic>> getAgentBalance(String agentId) async {
|
||||
try {
|
||||
return await _retryRequest<Map<String, dynamic>>(() async {
|
||||
final response = await http
|
||||
.get(
|
||||
Uri.parse('$baseUrl/agent/$agentId/balance'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
)
|
||||
.timeout(Duration(seconds: 15));
|
||||
|
||||
return jsonDecode(response.body);
|
||||
});
|
||||
} catch (e) {
|
||||
print('Erreur récupération solde après retry: $e');
|
||||
return {
|
||||
'success': false,
|
||||
'message': 'Erreur lors de la récupération du solde',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Recharger le solde d'un agent avec retry
|
||||
static Future<Map<String, dynamic>> rechargeAgent(
|
||||
String agentId,
|
||||
double montant,
|
||||
) async {
|
||||
try {
|
||||
return await _retryRequest<Map<String, dynamic>>(() async {
|
||||
final response = await http
|
||||
.post(
|
||||
Uri.parse('$baseUrl/agent/$agentId/recharge'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
body: jsonEncode({'montant': montant}),
|
||||
)
|
||||
.timeout(Duration(seconds: 15));
|
||||
|
||||
return jsonDecode(response.body);
|
||||
});
|
||||
} catch (e) {
|
||||
print('Erreur recharge après retry: $e');
|
||||
return {'success': false, 'message': 'Erreur lors de la recharge'};
|
||||
}
|
||||
}
|
||||
|
||||
/// Mettre à jour le solde d'un agent avec retry
|
||||
static Future<Map<String, dynamic>> updateAgentBalance(
|
||||
String agentId,
|
||||
double nouveauSolde,
|
||||
) async {
|
||||
try {
|
||||
return await _retryRequest<Map<String, dynamic>>(() async {
|
||||
final response = await http
|
||||
.put(
|
||||
Uri.parse('$baseUrl/agent/$agentId/balance'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
body: jsonEncode({'solde': nouveauSolde}),
|
||||
)
|
||||
.timeout(Duration(seconds: 15));
|
||||
|
||||
return jsonDecode(response.body);
|
||||
});
|
||||
} catch (e) {
|
||||
print('Erreur mise à jour solde après retry: $e');
|
||||
return {
|
||||
'success': false,
|
||||
'message': 'Erreur lors de la mise à jour du solde',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Enregistrer une transaction avec retry
|
||||
static Future<Map<String, dynamic>> recordTransaction({
|
||||
required String agentId,
|
||||
required String serviceId,
|
||||
required String serviceName,
|
||||
required double montant,
|
||||
required double commission,
|
||||
required String typeTransaction,
|
||||
required Map<String, dynamic> detailsTransaction,
|
||||
}) async {
|
||||
try {
|
||||
return await _retryRequest<Map<String, dynamic>>(() async {
|
||||
final response = await http
|
||||
.post(
|
||||
Uri.parse('$baseUrl/transactions'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
body: jsonEncode({
|
||||
'agent_id': agentId,
|
||||
'service_id': serviceId,
|
||||
'service_name': serviceName,
|
||||
'montant': montant,
|
||||
'commission': commission,
|
||||
'type_transaction': typeTransaction,
|
||||
'details': detailsTransaction,
|
||||
'date_transaction': DateTime.now().toIso8601String(),
|
||||
}),
|
||||
)
|
||||
.timeout(Duration(seconds: 15));
|
||||
|
||||
return jsonDecode(response.body);
|
||||
});
|
||||
} catch (e) {
|
||||
print('Erreur enregistrement transaction après retry: $e');
|
||||
return {
|
||||
'success': false,
|
||||
'message': 'Erreur lors de l\'enregistrement de la transaction',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Generic retry logic for AuthApiService
|
||||
static Future<T> _retryRequest<T>(Future<T> Function() request) async {
|
||||
Exception? lastException;
|
||||
|
||||
for (int attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
return await request();
|
||||
} on SocketException catch (e) {
|
||||
lastException = e;
|
||||
print('AuthAPI - Tentative $attempt échouée (SocketException): $e');
|
||||
if (attempt < maxRetries) {
|
||||
await Future.delayed(retryDelay * attempt);
|
||||
}
|
||||
} on HttpException catch (e) {
|
||||
lastException = e;
|
||||
print('AuthAPI - Tentative $attempt échouée (HttpException): $e');
|
||||
if (attempt < maxRetries) {
|
||||
await Future.delayed(retryDelay * attempt);
|
||||
}
|
||||
} on http.ClientException catch (e) {
|
||||
lastException = e;
|
||||
print('AuthAPI - Tentative $attempt échouée (ClientException): $e');
|
||||
if (attempt < maxRetries) {
|
||||
await Future.delayed(retryDelay * attempt);
|
||||
}
|
||||
} catch (e) {
|
||||
// For other exceptions, don't retry
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
throw Exception(
|
||||
'Impossible de se connecter après $maxRetries tentatives: $lastException',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ApiService {
|
||||
static const String baseUrl = 'https://api.live.wortis.cg/tpe';
|
||||
static const int maxRetries = 3;
|
||||
static const Duration retryDelay = Duration(seconds: 2);
|
||||
|
||||
/// Récupérer les champs d'un service avec retry amélioré
|
||||
Future<Map<String, dynamic>> fetchServiceFields(String serviceName) async {
|
||||
return _retryRequest<Map<String, dynamic>>(() async {
|
||||
// Fix: Remove double slash in URL
|
||||
final url = '$baseUrl/service/${Uri.encodeComponent(serviceName)}';
|
||||
print('Fetching service fields from: $url');
|
||||
|
||||
final response = await http
|
||||
.get(
|
||||
Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Connection': 'keep-alive',
|
||||
'User-Agent': 'WortisApp/1.0',
|
||||
},
|
||||
)
|
||||
.timeout(Duration(seconds: 30));
|
||||
|
||||
print('Response status: ${response.statusCode}');
|
||||
print('Response headers: ${response.headers}');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return jsonDecode(response.body);
|
||||
} else {
|
||||
throw Exception(
|
||||
'Erreur API: ${response.statusCode} - ${response.body}',
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Vérifier les données avec GET et retry
|
||||
Future<Map<String, dynamic>> verifyDataGet(
|
||||
String url,
|
||||
Map<String, dynamic> params,
|
||||
) async {
|
||||
return _retryRequest<Map<String, dynamic>>(() async {
|
||||
final uri = Uri.parse(url).replace(
|
||||
queryParameters: params.map(
|
||||
(key, value) => MapEntry(key, value.toString()),
|
||||
),
|
||||
);
|
||||
|
||||
final response = await http
|
||||
.get(
|
||||
uri,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
)
|
||||
.timeout(Duration(seconds: 30));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return jsonDecode(response.body);
|
||||
} else {
|
||||
throw Exception(
|
||||
'Données non trouvées - Status: ${response.statusCode}',
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Vérifier les données avec POST et retry
|
||||
Future<Map<String, dynamic>> verifyDataPost(
|
||||
String url,
|
||||
Map<String, dynamic> data,
|
||||
String operationId,
|
||||
) async {
|
||||
return _retryRequest<Map<String, dynamic>>(() async {
|
||||
final response = await http
|
||||
.post(
|
||||
Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
body: jsonEncode(data),
|
||||
)
|
||||
.timeout(Duration(seconds: 30));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return jsonDecode(response.body);
|
||||
} else {
|
||||
throw Exception(
|
||||
'Données non trouvées - Status: ${response.statusCode}',
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Soumettre les données du formulaire avec retry
|
||||
Future<void> submitFormData(
|
||||
BuildContext context,
|
||||
String url,
|
||||
Map<String, dynamic> data,
|
||||
Map<String, dynamic>? serviceData,
|
||||
dynamic additionalData,
|
||||
bool isCardPayment,
|
||||
) async {
|
||||
try {
|
||||
await _retryRequest<void>(() async {
|
||||
print(url);
|
||||
final response = await http
|
||||
.post(
|
||||
Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
body: jsonEncode(data),
|
||||
)
|
||||
.timeout(Duration(seconds: 30));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final responseData = jsonDecode(response.body);
|
||||
|
||||
// Afficher un message de succès
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Row(
|
||||
children: [
|
||||
Icon(Icons.check_circle, color: Colors.white),
|
||||
SizedBox(width: 8),
|
||||
Text('Transaction effectuée avec succès'),
|
||||
],
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Retourner à la page précédente
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
} else {
|
||||
throw Exception(
|
||||
'Erreur lors de la soumission - Status: ${response.statusCode}',
|
||||
);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
print('Erreur submitFormData après retry: $e');
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Row(
|
||||
children: [
|
||||
Icon(Icons.error, color: Colors.white),
|
||||
SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Erreur lors de la transaction: ${e.toString()}',
|
||||
maxLines: 2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
backgroundColor: Colors.red,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
duration: Duration(seconds: 5),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Annuler l'opération en cours
|
||||
void cancelOperation() {
|
||||
print('Opération annulée');
|
||||
}
|
||||
|
||||
// Generic retry logic for ApiService
|
||||
Future<T> _retryRequest<T>(Future<T> Function() request) async {
|
||||
Exception? lastException;
|
||||
|
||||
for (int attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
return await request();
|
||||
} on SocketException catch (e) {
|
||||
lastException = e;
|
||||
print('API - Tentative $attempt échouée (SocketException): $e');
|
||||
if (attempt < maxRetries) {
|
||||
await Future.delayed(retryDelay * attempt);
|
||||
}
|
||||
} on HttpException catch (e) {
|
||||
lastException = e;
|
||||
print('API - Tentative $attempt échouée (HttpException): $e');
|
||||
if (attempt < maxRetries) {
|
||||
await Future.delayed(retryDelay * attempt);
|
||||
}
|
||||
} on http.ClientException catch (e) {
|
||||
lastException = e;
|
||||
print('API - Tentative $attempt échouée (ClientException): $e');
|
||||
if (attempt < maxRetries) {
|
||||
await Future.delayed(retryDelay * attempt);
|
||||
}
|
||||
} catch (e) {
|
||||
// For other exceptions, don't retry
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
throw Exception(
|
||||
'Impossible de se connecter après $maxRetries tentatives: $lastException',
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user