Восстановление сети

This commit is contained in:
Халимов Рустам
2026-04-20 11:14:45 +03:00
parent c839a9bc03
commit b65c8f3633
9 changed files with 0 additions and 187 deletions

View File

@@ -1,186 +0,0 @@
# Умная синхронизация сообщений при восстановлении сети
## Проблема
При восстановлении соединения после офлайна, приложение запрашивало **все сообщения заново** (последние 50 для каждого чата), вместо того чтобы получить только **новые сообщения**, которые пришли пока клиент был офлайн.
## Решение
Добавлен новый параметр API `afterSequenceId` который позволяет запрашивать только сообщения с sequenceId > указанного.
## Изменения
### Бэкенд
#### 1. IMessageRepository.cs
Добавлен метод для получения сообщений ПОСЛЕ указанного sequenceId:
```csharp
Task<List<Message>> GetChatMessagesAfterAsync(Guid chatId, long sequenceId, int limit, CancellationToken cancellationToken);
```
#### 2. MessageRepository.cs
Реализация метода:
```csharp
public async Task<List<Message>> GetChatMessagesAfterAsync(Guid chatId, long sequenceId, int limit, CancellationToken cancellationToken)
{
var builder = Builders<Message>.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<List<MessageDetailDto>>;
```
Обработчик использует новый метод:
```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<MessageDto>
```
#### 2. ChatRepository.kt
Добавлен метод:
```kotlin
suspend fun getMessages(
chatId: String,
cursor: String? = null,
pivot: Long? = null,
afterSequenceId: Long? = null, // НОВЫЙ ПАРАМЕТР
limit: Int? = null
): List<Message>
```
Добавлен метод для получения последнего 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
```

View File

@@ -89,7 +89,6 @@ class SignalRNotificationObserver @Inject constructor(
// При восстановлении соединения SignalR обновляем список чатов и вступаем в них
signalrClient.status
.filter { it == ConnectionStatus.CONNECTED }
.distinctUntilChanged()
.onEach {
android.util.Log.d("SignalRNtfObserver", "SignalR connected, refreshing chats and joining rooms")
onSignalRConnected()