refactoring
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import '../../../../core/config/environment.dart';
|
||||
import '../../../../core/utils/logger.dart';
|
||||
import '../../../../features/authentication/data/datasources/keycloak_auth_service.dart';
|
||||
import '../models/formule_model.dart';
|
||||
import '../models/souscription_status_model.dart';
|
||||
|
||||
@lazySingleton
|
||||
class SouscriptionDatasource {
|
||||
final KeycloakAuthService _authService;
|
||||
final Dio _dio = Dio();
|
||||
|
||||
SouscriptionDatasource(this._authService);
|
||||
|
||||
Future<Options> _authOptions() async {
|
||||
final token = await _authService.getValidToken();
|
||||
return Options(
|
||||
headers: {'Authorization': 'Bearer $token'},
|
||||
validateStatus: (s) => s != null && s < 500,
|
||||
);
|
||||
}
|
||||
|
||||
String get _base => AppConfig.apiBaseUrl;
|
||||
|
||||
/// Liste toutes les formules disponibles (public)
|
||||
Future<List<FormuleModel>> getFormules() async {
|
||||
try {
|
||||
final opts = await _authOptions();
|
||||
final response = await _dio.get('$_base/api/souscriptions/formules', options: opts);
|
||||
if (response.statusCode == 200 && response.data is List) {
|
||||
return (response.data as List)
|
||||
.map((e) => FormuleModel.fromJson(e as Map))
|
||||
.toList();
|
||||
}
|
||||
} catch (e) {
|
||||
AppLogger.error('SouscriptionDatasource.getFormules: $e');
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/// Récupère la souscription courante de l'OrgAdmin connecté
|
||||
Future<SouscriptionStatusModel?> getMaSouscription() async {
|
||||
try {
|
||||
final opts = await _authOptions();
|
||||
final response = await _dio.get('$_base/api/souscriptions/ma-souscription', options: opts);
|
||||
if (response.statusCode == 200 && response.data is Map) {
|
||||
return SouscriptionStatusModel.fromJson(response.data as Map);
|
||||
}
|
||||
} catch (e) {
|
||||
AppLogger.error('SouscriptionDatasource.getMaSouscription: $e');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Crée une demande de souscription
|
||||
Future<SouscriptionStatusModel?> creerDemande({
|
||||
required String typeFormule,
|
||||
required String plageMembres,
|
||||
required String typePeriode,
|
||||
required String typeOrganisation,
|
||||
required String organisationId,
|
||||
}) async {
|
||||
try {
|
||||
final opts = await _authOptions();
|
||||
final response = await _dio.post(
|
||||
'$_base/api/souscriptions/demande',
|
||||
data: {
|
||||
'typeFormule': typeFormule,
|
||||
'plageMembres': plageMembres,
|
||||
'typePeriode': typePeriode,
|
||||
'typeOrganisation': typeOrganisation,
|
||||
'organisationId': organisationId,
|
||||
},
|
||||
options: opts,
|
||||
);
|
||||
if ((response.statusCode == 200 || response.statusCode == 201) && response.data is Map) {
|
||||
return SouscriptionStatusModel.fromJson(response.data as Map);
|
||||
}
|
||||
AppLogger.warning('SouscriptionDatasource.creerDemande: HTTP ${response.statusCode}');
|
||||
} catch (e) {
|
||||
AppLogger.error('SouscriptionDatasource.creerDemande: $e');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Initie le paiement Wave pour une souscription existante
|
||||
Future<SouscriptionStatusModel?> initierPaiement(String souscriptionId) async {
|
||||
try {
|
||||
final opts = await _authOptions();
|
||||
final response = await _dio.post(
|
||||
'$_base/api/souscriptions/$souscriptionId/initier-paiement',
|
||||
options: opts,
|
||||
);
|
||||
if (response.statusCode == 200 && response.data is Map) {
|
||||
return SouscriptionStatusModel.fromJson(response.data as Map);
|
||||
}
|
||||
} catch (e) {
|
||||
AppLogger.error('SouscriptionDatasource.initierPaiement: $e');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Confirme le paiement Wave après retour du deep link
|
||||
Future<bool> confirmerPaiement(String souscriptionId) async {
|
||||
try {
|
||||
final opts = await _authOptions();
|
||||
final response = await _dio.post(
|
||||
'$_base/api/souscriptions/$souscriptionId/confirmer-paiement',
|
||||
options: opts,
|
||||
);
|
||||
return response.statusCode == 200;
|
||||
} catch (e) {
|
||||
AppLogger.error('SouscriptionDatasource.confirmerPaiement: $e');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
37
lib/features/onboarding/data/models/auth_status_model.dart
Normal file
37
lib/features/onboarding/data/models/auth_status_model.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
/// Réponse enrichie de /api/membres/mon-statut
|
||||
class AuthStatusModel {
|
||||
final String statutCompte;
|
||||
|
||||
/// État du workflow d'onboarding — non null si statutCompte == EN_ATTENTE_VALIDATION
|
||||
final String onboardingState;
|
||||
final String? souscriptionId;
|
||||
final String? waveSessionId;
|
||||
|
||||
const AuthStatusModel({
|
||||
required this.statutCompte,
|
||||
this.onboardingState = 'NO_SUBSCRIPTION',
|
||||
this.souscriptionId,
|
||||
this.waveSessionId,
|
||||
});
|
||||
|
||||
bool get isActive => statutCompte == 'ACTIF';
|
||||
bool get isPendingOnboarding => statutCompte == 'EN_ATTENTE_VALIDATION';
|
||||
bool get isBlocked =>
|
||||
statutCompte == 'SUSPENDU' || statutCompte == 'DESACTIVE';
|
||||
|
||||
factory AuthStatusModel.fromJson(Map<dynamic, dynamic> json) {
|
||||
return AuthStatusModel(
|
||||
statutCompte: (json['statutCompte'] as String?) ?? 'ACTIF',
|
||||
onboardingState: (json['onboardingState'] as String?) ?? 'NO_SUBSCRIPTION',
|
||||
souscriptionId: json['souscriptionId'] as String?,
|
||||
waveSessionId: json['waveSessionId'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
factory AuthStatusModel.active() =>
|
||||
const AuthStatusModel(statutCompte: 'ACTIF', onboardingState: 'VALIDATED');
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'AuthStatusModel($statutCompte, onboarding=$onboardingState, sous=$souscriptionId)';
|
||||
}
|
||||
43
lib/features/onboarding/data/models/formule_model.dart
Normal file
43
lib/features/onboarding/data/models/formule_model.dart
Normal file
@@ -0,0 +1,43 @@
|
||||
/// Formule d'abonnement retournée par /api/souscriptions/formules
|
||||
class FormuleModel {
|
||||
final String code; // BASIC | STANDARD | PREMIUM
|
||||
final String libelle;
|
||||
final String? description;
|
||||
final String plage; // PETITE | MOYENNE | GRANDE | TRES_GRANDE
|
||||
final String plageLibelle;
|
||||
final int minMembres;
|
||||
final int maxMembres; // -1 = illimité
|
||||
final double prixMensuel;
|
||||
final double prixAnnuel;
|
||||
final int ordreAffichage;
|
||||
|
||||
const FormuleModel({
|
||||
required this.code,
|
||||
required this.libelle,
|
||||
this.description,
|
||||
required this.plage,
|
||||
required this.plageLibelle,
|
||||
required this.minMembres,
|
||||
required this.maxMembres,
|
||||
required this.prixMensuel,
|
||||
required this.prixAnnuel,
|
||||
required this.ordreAffichage,
|
||||
});
|
||||
|
||||
factory FormuleModel.fromJson(Map<dynamic, dynamic> json) {
|
||||
return FormuleModel(
|
||||
code: json['code'] as String,
|
||||
libelle: json['libelle'] as String,
|
||||
description: json['description'] as String?,
|
||||
plage: json['plage'] as String,
|
||||
plageLibelle: (json['plageLibelle'] as String?) ?? '',
|
||||
minMembres: (json['minMembres'] as num?)?.toInt() ?? 0,
|
||||
maxMembres: (json['maxMembres'] as num?)?.toInt() ?? -1,
|
||||
prixMensuel: (json['prixMensuel'] as num?)?.toDouble() ?? 0,
|
||||
prixAnnuel: (json['prixAnnuel'] as num?)?.toDouble() ?? 0,
|
||||
ordreAffichage: (json['ordreAffichage'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
String get maxMembresLabel => maxMembres == -1 ? '∞' : '$maxMembres';
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/// Statut courant d'une souscription retourné par le backend
|
||||
class SouscriptionStatusModel {
|
||||
final String souscriptionId;
|
||||
final String statutValidation;
|
||||
final String typeFormule;
|
||||
final String plageMembres;
|
||||
final String plageLibelle;
|
||||
final String typePeriode;
|
||||
final String typeOrganisation;
|
||||
final double? montantTotal;
|
||||
final double? montantMensuelBase;
|
||||
final double? coefficientApplique;
|
||||
final String? waveSessionId;
|
||||
final String? waveLaunchUrl;
|
||||
final String organisationId;
|
||||
final String? organisationNom;
|
||||
|
||||
const SouscriptionStatusModel({
|
||||
required this.souscriptionId,
|
||||
required this.statutValidation,
|
||||
required this.typeFormule,
|
||||
required this.plageMembres,
|
||||
required this.plageLibelle,
|
||||
required this.typePeriode,
|
||||
required this.typeOrganisation,
|
||||
this.montantTotal,
|
||||
this.montantMensuelBase,
|
||||
this.coefficientApplique,
|
||||
this.waveSessionId,
|
||||
this.waveLaunchUrl,
|
||||
required this.organisationId,
|
||||
this.organisationNom,
|
||||
});
|
||||
|
||||
factory SouscriptionStatusModel.fromJson(Map<dynamic, dynamic> json) {
|
||||
return SouscriptionStatusModel(
|
||||
souscriptionId: json['souscriptionId'] as String,
|
||||
statutValidation: json['statutValidation'] as String,
|
||||
typeFormule: json['typeFormule'] as String,
|
||||
plageMembres: json['plageMembres'] as String,
|
||||
plageLibelle: (json['plageLibelle'] as String?) ?? '',
|
||||
typePeriode: json['typePeriode'] as String,
|
||||
typeOrganisation: (json['typeOrganisation'] as String?) ?? 'ASSOCIATION',
|
||||
montantTotal: (json['montantTotal'] as num?)?.toDouble(),
|
||||
montantMensuelBase: (json['montantMensuelBase'] as num?)?.toDouble(),
|
||||
coefficientApplique: (json['coefficientApplique'] as num?)?.toDouble(),
|
||||
waveSessionId: json['waveSessionId'] as String?,
|
||||
waveLaunchUrl: json['waveLaunchUrl'] as String?,
|
||||
organisationId: json['organisationId'] as String,
|
||||
organisationNom: json['organisationNom'] as String?,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user