# Умная синхронизация сообщений при восстановлении сети ## Проблема При восстановлении соединения после офлайна, приложение запрашивало **все сообщения заново** (последние 50 для каждого чата), вместо того чтобы получить только **новые сообщения**, которые пришли пока клиент был офлайн. ## Решение Добавлен новый параметр API `afterSequenceId` который позволяет запрашивать только сообщения с sequenceId > указанного. ## Изменения ### Бэкенд #### 1. IMessageRepository.cs Добавлен метод для получения сообщений ПОСЛЕ указанного sequenceId: ```csharp Task> GetChatMessagesAfterAsync(Guid chatId, long sequenceId, int limit, CancellationToken cancellationToken); ``` #### 2. MessageRepository.cs Реализация метода: ```csharp public async Task> GetChatMessagesAfterAsync(Guid chatId, long sequenceId, int limit, CancellationToken cancellationToken) { var builder = Builders.Filter; var filter = builder.And( builder.Eq(m => m.ChatId, chatId), builder.Gt(m => m.SequenceId, sequenceId) ); return await _messages.Find(filter) .SortBy(m => m.SequenceId) .Limit(limit) .ToListAsync(cancellationToken); } ``` #### 3. GetMessagesQuery.cs Добавлен параметр `AfterSequenceId`: ```csharp public record GetMessagesQuery( Guid UserId, Guid ChatId, string? Cursor, long? Pivot = null, long? AfterSequenceId = null, // НОВЫЙ ПАРАМЕТР int? Limit = null ) : IQuery>; ``` Обработчик использует новый метод: ```csharp if (request.AfterSequenceId.HasValue) { messages = await _messageRepository.GetChatMessagesAfterAsync( request.ChatId, request.AfterSequenceId.Value, queryLimit, cancellationToken ); } ``` #### 4. MessagesEndpoints.cs Добавлен query параметр в endpoint: ```csharp group.MapGet("chat/{chatId:guid}", async ( [FromRoute] Guid chatId, [FromQuery] string? cursor, [FromQuery] long? afterSequenceId, // НОВЫЙ ПАРАМЕТР [FromQuery] long? pivot, [FromQuery] int? limit, ISender sender, IUserContext userContext, CancellationToken ct) => { ... } ); ``` ### Мобильное приложение #### 1. ChatApi.kt Добавлен параметр в API: ```kotlin @GET("messages/chat/{chatId}") suspend fun getMessages( @Path("chatId") chatId: String, @Query("cursor") cursor: String? = null, @Query("pivot") pivot: Long? = null, @Query("afterSequenceId") afterSequenceId: Long? = null, // НОВЫЙ ПАРАМЕТР @Query("limit") limit: Int? = 50 ): List ``` #### 2. ChatRepository.kt Добавлен метод: ```kotlin suspend fun getMessages( chatId: String, cursor: String? = null, pivot: Long? = null, afterSequenceId: Long? = null, // НОВЫЙ ПАРАМЕТР limit: Int? = null ): List ``` Добавлен метод для получения последнего sequenceId: ```kotlin suspend fun getLastKnownSequenceId(chatId: String): Int? ``` #### 3. ChatRepositoryImpl.kt Реализация: ```kotlin override suspend fun getLastKnownSequenceId(chatId: String): Int? { return try { messageDao.getMaxSequenceId(chatId) } catch (e: Exception) { Log.e(TAG, "Failed to get last sequenceId", e) null } } ``` #### 4. SignalRNotificationObserver.kt Умная синхронизация: ```kotlin private fun syncMissedMessages() { scope.launch { val chats = chatRepository.getChats() chats.forEach { chat -> val lastSequenceId = chatRepository.getLastKnownSequenceId(chat.id) if (lastSequenceId != null) { // Запрашиваем только сообщения ПОСЛЕ последнего известного chatRepository.getMessages( chatId = chat.id, afterSequenceId = lastSequenceId.toLong(), limit = 100 ) } else { // Нет локальных сообщений - загружаем последние 50 chatRepository.getMessages(chatId = chat.id, limit = 50) } } } } ``` ## Преимущества ### До изменений - Запрашивались **все последние 50 сообщений** для каждого чата - При 20 чатах: 20 × 50 = **1000 сообщений** из сети - Дублирование данных, медленная синхронизация ### После изменений - Запрашиваются **только новые сообщения** с последнего sequenceId - При 20 чатах и 5 новых сообщениях в каждом: 20 × 5 = **100 сообщений** - **В 10 раз меньше трафика** - **Быстрая синхронизация** ## Пример использования API ```http GET /api/messages/chat/{chatId}?afterSequenceId=12345&limit=100 ``` Ответит только с сообщениями где `sequenceId > 12345`. ## Тестирование 1. Откройте приложение, загрузите чаты 2. Отключите сеть 3. Отправьте несколько сообщений с другого устройства 4. Включите сеть 5. Проверьте логи: должно быть `fetching newer...` и загрузка только новых сообщений ## Логи ``` SignalRNtfObserver: Starting missed messages sync... SignalRNtfObserver: Syncing 5 chats for missed messages SignalRNtfObserver: Chat abc-123: last known seqId=100, fetching newer... SignalRNtfObserver: Chat abc-123: fetched 3 new messages SignalRNtfObserver: Synced chat abc-123 ```