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:
dahoud
2026-03-15 02:12:17 +00:00
parent bbc409de9d
commit e8ad874015
635 changed files with 58160 additions and 20674 deletions

View File

@@ -0,0 +1,44 @@
import 'package:injectable/injectable.dart';
import 'package:unionflow_mobile_apps/core/network/api_client.dart';
import '../../domain/entities/feed_item.dart';
@lazySingleton
class FeedRepository {
final ApiClient _apiClient;
FeedRepository(this._apiClient);
/// Récupère le flux d'actualité depuis le backend Quarkus.
/// Vérifier la route backend (ex. /api/feed ou /api/posts) et adapter _feedPath si besoin.
static const String _feedPath = '/api/feed';
Future<List<FeedItem>> getFeed({int page = 0, int size = 10}) async {
try {
final response = await _apiClient.get(
_feedPath,
queryParameters: {'page': page, 'size': size},
);
final List<dynamic> data = response.data['content'] ?? response.data; // Gère la pagination Spring/Quarkus
return data.map((json) {
// Mapping manuel basique depuis le JSON API vers l'entité locale
// À ajuster selon la structure JSON exacte renvoyée par l'API
return FeedItem(
id: json['id']?.toString() ?? '',
type: FeedItemType.post, // Par défaut, ou selon json['type']
authorName: json['authorName'] ?? json['author']?['name'] ?? 'Auteur inconnu',
authorAvatarUrl: json['authorAvatarUrl'] ?? json['author']?['avatarUrl'],
createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt']) : DateTime.now(),
content: json['content'] ?? '',
likesCount: json['likesCount'] ?? 0,
commentsCount: json['commentsCount'] ?? 0,
isLikedByMe: json['isLikedByMe'] ?? false,
);
}).toList();
} catch (e) {
// Propagation de l'erreur pour la gestion globale
throw Exception('Erreur lors de la récupération du flux externe: $e');
}
}
}