feat: WebSocket temps réel + Finance Workflow + corrections
- Task #6: WebSocket /ws/dashboard + Kafka events (5 topics) * Backend: KafkaEventProducer, KafkaEventConsumer * Mobile: WebSocketService (reconnection, heartbeat, typed events) * DashboardBloc: Auto-refresh depuis WebSocket events - Finance Workflow: approbations + budgets (backend + mobile) * Backend: entities, services, resources, migrations Flyway V6 * Mobile: features finance_workflow complète avec BLoC - Corrections DI: interfaces IRepository partout * IProfileRepository, IOrganizationRepository, IMembreRepository * GetIt configuré avec @injectable - Spec-Kit: constitution + templates mis à jour * .specify/memory/constitution.md enrichie * Templates agent, plan, spec, tasks, checklist - Nettoyage: fichiers temporaires supprimés Signed-off-by: lions dev Team
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
/// Modèle des statistiques de cache système
|
||||
/// Correspond à CacheStatsResponse du backend
|
||||
library cache_stats_model;
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'cache_stats_model.g.dart';
|
||||
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
class CacheStatsModel extends Equatable {
|
||||
final int? totalSizeBytes;
|
||||
final String? totalSizeFormatted;
|
||||
final int? totalEntries;
|
||||
final double? hitRate;
|
||||
final int? hits;
|
||||
final int? misses;
|
||||
final DateTime? lastCleared;
|
||||
|
||||
const CacheStatsModel({
|
||||
this.totalSizeBytes,
|
||||
this.totalSizeFormatted,
|
||||
this.totalEntries,
|
||||
this.hitRate,
|
||||
this.hits,
|
||||
this.misses,
|
||||
this.lastCleared,
|
||||
});
|
||||
|
||||
factory CacheStatsModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$CacheStatsModelFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$CacheStatsModelToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
totalSizeBytes,
|
||||
totalSizeFormatted,
|
||||
totalEntries,
|
||||
hitRate,
|
||||
hits,
|
||||
misses,
|
||||
lastCleared,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/// Modèle de configuration système
|
||||
/// Correspond à SystemConfigResponse du backend
|
||||
library system_config_model;
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'system_config_model.g.dart';
|
||||
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
class SystemConfigModel extends Equatable {
|
||||
// Configuration générale
|
||||
final String? applicationName;
|
||||
final String? timezone;
|
||||
final String? defaultLanguage;
|
||||
final bool? maintenanceMode;
|
||||
final String? version;
|
||||
final DateTime? lastUpdated;
|
||||
|
||||
// Configuration réseau
|
||||
final int? networkTimeout;
|
||||
final int? maxRetries;
|
||||
final int? connectionPoolSize;
|
||||
|
||||
// Configuration sécurité
|
||||
final bool? twoFactorAuthEnabled;
|
||||
final int? sessionTimeoutMinutes;
|
||||
final bool? auditLoggingEnabled;
|
||||
|
||||
// Configuration performance
|
||||
final bool? metricsCollectionEnabled;
|
||||
final int? metricsIntervalSeconds;
|
||||
final bool? performanceOptimizationEnabled;
|
||||
|
||||
// Configuration backup
|
||||
final bool? autoBackupEnabled;
|
||||
final String? backupFrequency;
|
||||
final int? backupRetentionDays;
|
||||
final DateTime? lastBackup;
|
||||
|
||||
// Configuration logs
|
||||
final String? logLevel;
|
||||
final int? logRetentionDays;
|
||||
final bool? detailedLoggingEnabled;
|
||||
final bool? logCompressionEnabled;
|
||||
|
||||
// Configuration monitoring
|
||||
final bool? realTimeMonitoringEnabled;
|
||||
final int? monitoringIntervalSeconds;
|
||||
final bool? emailAlertsEnabled;
|
||||
final bool? pushAlertsEnabled;
|
||||
|
||||
// Configuration alertes
|
||||
final bool? cpuHighAlertEnabled;
|
||||
final int? cpuThresholdPercent;
|
||||
final bool? memoryLowAlertEnabled;
|
||||
final int? memoryThresholdPercent;
|
||||
final bool? criticalErrorAlertEnabled;
|
||||
final bool? connectionFailureAlertEnabled;
|
||||
final int? connectionFailureThreshold;
|
||||
|
||||
// Statut système
|
||||
final String? systemStatus;
|
||||
final int? uptime;
|
||||
|
||||
const SystemConfigModel({
|
||||
this.applicationName,
|
||||
this.timezone,
|
||||
this.defaultLanguage,
|
||||
this.maintenanceMode,
|
||||
this.version,
|
||||
this.lastUpdated,
|
||||
this.networkTimeout,
|
||||
this.maxRetries,
|
||||
this.connectionPoolSize,
|
||||
this.twoFactorAuthEnabled,
|
||||
this.sessionTimeoutMinutes,
|
||||
this.auditLoggingEnabled,
|
||||
this.metricsCollectionEnabled,
|
||||
this.metricsIntervalSeconds,
|
||||
this.performanceOptimizationEnabled,
|
||||
this.autoBackupEnabled,
|
||||
this.backupFrequency,
|
||||
this.backupRetentionDays,
|
||||
this.lastBackup,
|
||||
this.logLevel,
|
||||
this.logRetentionDays,
|
||||
this.detailedLoggingEnabled,
|
||||
this.logCompressionEnabled,
|
||||
this.realTimeMonitoringEnabled,
|
||||
this.monitoringIntervalSeconds,
|
||||
this.emailAlertsEnabled,
|
||||
this.pushAlertsEnabled,
|
||||
this.cpuHighAlertEnabled,
|
||||
this.cpuThresholdPercent,
|
||||
this.memoryLowAlertEnabled,
|
||||
this.memoryThresholdPercent,
|
||||
this.criticalErrorAlertEnabled,
|
||||
this.connectionFailureAlertEnabled,
|
||||
this.connectionFailureThreshold,
|
||||
this.systemStatus,
|
||||
this.uptime,
|
||||
});
|
||||
|
||||
factory SystemConfigModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$SystemConfigModelFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$SystemConfigModelToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
applicationName,
|
||||
timezone,
|
||||
defaultLanguage,
|
||||
maintenanceMode,
|
||||
version,
|
||||
lastUpdated,
|
||||
networkTimeout,
|
||||
maxRetries,
|
||||
connectionPoolSize,
|
||||
twoFactorAuthEnabled,
|
||||
sessionTimeoutMinutes,
|
||||
auditLoggingEnabled,
|
||||
metricsCollectionEnabled,
|
||||
metricsIntervalSeconds,
|
||||
performanceOptimizationEnabled,
|
||||
autoBackupEnabled,
|
||||
backupFrequency,
|
||||
backupRetentionDays,
|
||||
lastBackup,
|
||||
logLevel,
|
||||
logRetentionDays,
|
||||
detailedLoggingEnabled,
|
||||
logCompressionEnabled,
|
||||
realTimeMonitoringEnabled,
|
||||
monitoringIntervalSeconds,
|
||||
emailAlertsEnabled,
|
||||
pushAlertsEnabled,
|
||||
cpuHighAlertEnabled,
|
||||
cpuThresholdPercent,
|
||||
memoryLowAlertEnabled,
|
||||
memoryThresholdPercent,
|
||||
criticalErrorAlertEnabled,
|
||||
connectionFailureAlertEnabled,
|
||||
connectionFailureThreshold,
|
||||
systemStatus,
|
||||
uptime,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'system_metrics_model.g.dart';
|
||||
|
||||
/// Modèle pour les métriques système en temps réel
|
||||
@JsonSerializable(fieldRename: FieldRename.none)
|
||||
class SystemMetricsModel {
|
||||
// Métriques CPU
|
||||
final double? cpuUsagePercent;
|
||||
final int? availableProcessors;
|
||||
final double? systemLoadAverage;
|
||||
|
||||
// Métriques mémoire
|
||||
final int? totalMemoryBytes;
|
||||
final int? usedMemoryBytes;
|
||||
final int? freeMemoryBytes;
|
||||
final int? maxMemoryBytes;
|
||||
final double? memoryUsagePercent;
|
||||
final String? totalMemoryFormatted;
|
||||
final String? usedMemoryFormatted;
|
||||
final String? freeMemoryFormatted;
|
||||
|
||||
// Métriques disque
|
||||
final int? totalDiskBytes;
|
||||
final int? usedDiskBytes;
|
||||
final int? freeDiskBytes;
|
||||
final double? diskUsagePercent;
|
||||
final String? totalDiskFormatted;
|
||||
final String? usedDiskFormatted;
|
||||
final String? freeDiskFormatted;
|
||||
|
||||
// Métriques utilisateurs
|
||||
final int? activeUsersCount;
|
||||
final int? totalUsersCount;
|
||||
final int? activeSessionsCount;
|
||||
final int? failedLoginAttempts24h;
|
||||
|
||||
// Métriques API
|
||||
final int? apiRequestsLastHour;
|
||||
final int? apiRequestsToday;
|
||||
final double? averageResponseTimeMs;
|
||||
final int? totalRequestsCount;
|
||||
|
||||
// Métriques base de données
|
||||
final int? dbConnectionPoolSize;
|
||||
final int? dbActiveConnections;
|
||||
final int? dbIdleConnections;
|
||||
final bool? dbHealthy;
|
||||
|
||||
// Métriques erreurs et logs
|
||||
final int? criticalErrorsCount;
|
||||
final int? warningsCount;
|
||||
final int? infoLogsCount;
|
||||
final int? debugLogsCount;
|
||||
final int? totalLogsCount;
|
||||
|
||||
// Métriques réseau
|
||||
final double? networkBytesReceivedPerSec;
|
||||
final double? networkBytesSentPerSec;
|
||||
final String? networkInFormatted;
|
||||
final String? networkOutFormatted;
|
||||
|
||||
// Métriques système
|
||||
final String? systemStatus;
|
||||
final int? uptimeMillis;
|
||||
final String? uptimeFormatted;
|
||||
final String? startTime;
|
||||
final String? currentTime;
|
||||
final String? javaVersion;
|
||||
final String? quarkusVersion;
|
||||
final String? applicationVersion;
|
||||
|
||||
// Métriques maintenance
|
||||
final String? lastBackup;
|
||||
final String? nextScheduledMaintenance;
|
||||
final String? lastMaintenance;
|
||||
|
||||
// URLs
|
||||
final String? apiBaseUrl;
|
||||
final String? authServerUrl;
|
||||
final String? cdnUrl;
|
||||
|
||||
// Cache
|
||||
final int? totalCacheSizeBytes;
|
||||
final String? totalCacheSizeFormatted;
|
||||
final int? totalCacheEntries;
|
||||
|
||||
const SystemMetricsModel({
|
||||
this.cpuUsagePercent,
|
||||
this.availableProcessors,
|
||||
this.systemLoadAverage,
|
||||
this.totalMemoryBytes,
|
||||
this.usedMemoryBytes,
|
||||
this.freeMemoryBytes,
|
||||
this.maxMemoryBytes,
|
||||
this.memoryUsagePercent,
|
||||
this.totalMemoryFormatted,
|
||||
this.usedMemoryFormatted,
|
||||
this.freeMemoryFormatted,
|
||||
this.totalDiskBytes,
|
||||
this.usedDiskBytes,
|
||||
this.freeDiskBytes,
|
||||
this.diskUsagePercent,
|
||||
this.totalDiskFormatted,
|
||||
this.usedDiskFormatted,
|
||||
this.freeDiskFormatted,
|
||||
this.activeUsersCount,
|
||||
this.totalUsersCount,
|
||||
this.activeSessionsCount,
|
||||
this.failedLoginAttempts24h,
|
||||
this.apiRequestsLastHour,
|
||||
this.apiRequestsToday,
|
||||
this.averageResponseTimeMs,
|
||||
this.totalRequestsCount,
|
||||
this.dbConnectionPoolSize,
|
||||
this.dbActiveConnections,
|
||||
this.dbIdleConnections,
|
||||
this.dbHealthy,
|
||||
this.criticalErrorsCount,
|
||||
this.warningsCount,
|
||||
this.infoLogsCount,
|
||||
this.debugLogsCount,
|
||||
this.totalLogsCount,
|
||||
this.networkBytesReceivedPerSec,
|
||||
this.networkBytesSentPerSec,
|
||||
this.networkInFormatted,
|
||||
this.networkOutFormatted,
|
||||
this.systemStatus,
|
||||
this.uptimeMillis,
|
||||
this.uptimeFormatted,
|
||||
this.startTime,
|
||||
this.currentTime,
|
||||
this.javaVersion,
|
||||
this.quarkusVersion,
|
||||
this.applicationVersion,
|
||||
this.lastBackup,
|
||||
this.nextScheduledMaintenance,
|
||||
this.lastMaintenance,
|
||||
this.apiBaseUrl,
|
||||
this.authServerUrl,
|
||||
this.cdnUrl,
|
||||
this.totalCacheSizeBytes,
|
||||
this.totalCacheSizeFormatted,
|
||||
this.totalCacheEntries,
|
||||
});
|
||||
|
||||
factory SystemMetricsModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$SystemMetricsModelFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$SystemMetricsModelToJson(this);
|
||||
|
||||
/// Helper pour formater un pourcentage
|
||||
String formatPercent(double? percent) {
|
||||
if (percent == null) return '0%';
|
||||
return '${percent.toStringAsFixed(1)}%';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/// Repository pour la gestion de la configuration système
|
||||
/// Implémentation avec l'API backend SystemResource
|
||||
library system_config_repository_impl;
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:unionflow_mobile_apps/core/network/api_client.dart';
|
||||
import 'package:unionflow_mobile_apps/core/utils/logger.dart';
|
||||
import '../../domain/repositories/system_config_repository.dart';
|
||||
import '../models/system_config_model.dart';
|
||||
import '../models/cache_stats_model.dart';
|
||||
import '../models/system_metrics_model.dart';
|
||||
|
||||
/// Implémentation du repository de configuration système
|
||||
@LazySingleton(as: ISystemConfigRepository)
|
||||
class SystemConfigRepositoryImpl implements ISystemConfigRepository {
|
||||
final ApiClient _apiClient;
|
||||
static const String _base = '/api/system';
|
||||
|
||||
SystemConfigRepositoryImpl(this._apiClient);
|
||||
|
||||
@override
|
||||
Future<SystemConfigModel> getConfig() async {
|
||||
final response = await _apiClient.get('$_base/config');
|
||||
if (response.statusCode == 200) {
|
||||
return SystemConfigModel.fromJson(response.data as Map<String, dynamic>);
|
||||
}
|
||||
throw Exception('Erreur ${response.statusCode}');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SystemConfigModel> updateConfig(Map<String, dynamic> config) async {
|
||||
final response = await _apiClient.put('$_base/config', data: config);
|
||||
if (response.statusCode == 200) {
|
||||
return SystemConfigModel.fromJson(response.data as Map<String, dynamic>);
|
||||
}
|
||||
throw Exception('Erreur ${response.statusCode}');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CacheStatsModel> getCacheStats() async {
|
||||
final response = await _apiClient.get('$_base/cache/stats');
|
||||
if (response.statusCode == 200) {
|
||||
return CacheStatsModel.fromJson(response.data as Map<String, dynamic>);
|
||||
}
|
||||
throw Exception('Erreur ${response.statusCode}');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SystemMetricsModel> getMetrics() async {
|
||||
final response = await _apiClient.get('$_base/metrics');
|
||||
if (response.statusCode == 200) {
|
||||
return SystemMetricsModel.fromJson(response.data as Map<String, dynamic>);
|
||||
}
|
||||
throw Exception('Erreur ${response.statusCode}');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clearCache() async {
|
||||
final response = await _apiClient.post('$_base/cache/clear');
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('Erreur ${response.statusCode}');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> testDatabase() async {
|
||||
final response = await _apiClient.post('$_base/test/database');
|
||||
if (response.statusCode == 200) {
|
||||
return response.data as Map<String, dynamic>;
|
||||
}
|
||||
throw Exception('Erreur ${response.statusCode}');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> testEmail() async {
|
||||
final response = await _apiClient.post('$_base/test/email');
|
||||
if (response.statusCode == 200) {
|
||||
return response.data as Map<String, dynamic>;
|
||||
}
|
||||
throw Exception('Erreur ${response.statusCode}');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SystemConfigModel> resetConfig() async {
|
||||
try {
|
||||
// Tente d'abord l'endpoint dédié pour le reset
|
||||
final response = await _apiClient.post('$_base/config/reset');
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return SystemConfigModel.fromJson(response.data as Map<String, dynamic>);
|
||||
}
|
||||
throw Exception('Erreur ${response.statusCode}');
|
||||
} on DioException catch (e, st) {
|
||||
// Si l'endpoint n'existe pas (404), fallback : récupérer config par défaut via GET
|
||||
if (e.response?.statusCode == 404) {
|
||||
AppLogger.warning(
|
||||
'SystemConfigRepository: Endpoint /reset non disponible, fallback sur config par défaut',
|
||||
);
|
||||
// Alternative: Appeler un endpoint qui retourne la config par défaut
|
||||
// ou construire une config minimale côté client
|
||||
try {
|
||||
final defaultResponse = await _apiClient.get('$_base/config/default');
|
||||
if (defaultResponse.statusCode == 200) {
|
||||
return SystemConfigModel.fromJson(defaultResponse.data as Map<String, dynamic>);
|
||||
}
|
||||
} catch (_) {
|
||||
// Si même le default échoue, retourner une config minimale
|
||||
AppLogger.error(
|
||||
'SystemConfigRepository: resetConfig fallback échoué, config minimale retournée',
|
||||
error: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
// Config minimale codée en dur pour éviter un crash total
|
||||
return SystemConfigModel.fromJson({
|
||||
'id': 'default',
|
||||
'appName': 'UnionFlow',
|
||||
'version': '1.0.0',
|
||||
'maintenance': false,
|
||||
'enableCache': true,
|
||||
'cacheExpirationMinutes': 30,
|
||||
});
|
||||
}
|
||||
}
|
||||
AppLogger.error('SystemConfigRepository: resetConfig échoué', error: e, stackTrace: st);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user