Полная реализация синхронизации

This commit is contained in:
RoyceDa
2026-02-15 18:14:42 +02:00
parent fe5bf2bd04
commit 7dc94678ba
15 changed files with 333 additions and 32 deletions

View File

@@ -1,5 +1,6 @@
package im.rosetta.service.dispatch;
import java.util.HashSet;
import java.util.List;
import im.rosetta.Failures;
@@ -96,6 +97,11 @@ public class MessageDispatcher {
* Сохраняем сообщение в буфер на случай если получатель офлайн, или нам нужна будет синхронизация сообщений для получателя
*/
this.bufferService.pushPacketToBuffer(fromPublicKey, toPublicKey, packet);
/**
* Ретранслируем сообщение всем авторизованным сессиям отправителя, чтобы синхронизировать отправленные сообщения
*/
this.retranslate(packet, client);
}
/**
@@ -111,5 +117,34 @@ public class MessageDispatcher {
this.sendPeer(packet, client, true);
}
/**
* Сообщает всем авторизованным сессиям отправителя о том, что он отправил сообщения,
* для того чтобы синхронизировать отправленные сообщения на всех устройствах отправителя
* @param packet пакет сообщения
* @param client клиент отправляющий пакет
* @throws ProtocolException
*/
public void retranslate(PacketBaseDialog packet, Client client) throws ProtocolException {
ECIAuthentificate eciAuthentificate = client.getTag(ECIAuthentificate.class);
HashSet<Client> clients = this.clientManager.getClientIndexer()
.getClients(ECIAuthentificate.class, "publicKey", eciAuthentificate.getPublicKey());
if(clients == null){
/**
* Нет авторизованных сессий с таким публичным ключом
*/
return;
}
for(Client c : clients){
/**
* Проходим по всем устройствам с таким публичным ключом и ретранслируем им пакет, кроме того устройства что
* отправило пакет
*/
if(c.equals(client)){
continue;
}
c.send(packet);
}
}
}

View File

@@ -10,7 +10,7 @@ import im.rosetta.database.entity.Buffer;
import im.rosetta.database.repository.BufferRepository;
import im.rosetta.exception.UnauthorizedExeception;
import im.rosetta.service.Service;
import im.rosetta.service.services.runtime.PacketBuffer;
import io.orprotocol.ProtocolException;
import io.orprotocol.client.Client;
import io.orprotocol.packet.Packet;
@@ -32,7 +32,7 @@ public class BufferService extends Service<BufferRepository> {
* @return
* @throws ProtocolException
*/
public List<Packet> getPacketsFromTime(Client client, long fromTimestampMs) throws ProtocolException, UnauthorizedExeception {
public PacketBuffer getPacketsFromTime(Client client, long fromTimestampMs, int take) throws ProtocolException, UnauthorizedExeception {
ECIAuthentificate eciAuthentificate = client.getTag(ECIAuthentificate.class);
if(eciAuthentificate == null || !eciAuthentificate.hasAuthorized()){
/**
@@ -41,20 +41,25 @@ public class BufferService extends Service<BufferRepository> {
throw new UnauthorizedExeception("Unauthorized client cannot get packets from buffer");
}
String toPublicKey = eciAuthentificate.getPublicKey();
String hql = "FROM Buffer WHERE to = :to AND timestamp > :timestamp ORDER BY timestamp ASC";
String hql = "FROM Buffer WHERE (to = :to OR from = :from) AND timestamp > :timestamp ORDER BY timestamp ASC";
HashMap<String, Object> parameters = new HashMap<>();
parameters.put("to", toPublicKey);
parameters.put("from", toPublicKey);
parameters.put("timestamp", fromTimestampMs);
List<Packet> packets = new ArrayList<>();
long lastTimestamp = fromTimestampMs;
try(QuerySession<Buffer> querySession = this.getRepository().buildQuery(hql, parameters)){
List<Buffer> buffers = querySession.getQuery().list();
List<Buffer> buffers = querySession.getQuery().setMaxResults(take).list();
for(Buffer buffer : buffers) {
byte[] packetBytes = buffer.getPacket();
Packet packet = this.packetManager.createPacket(packetBytes);
packets.add(packet);
}
if(!buffers.isEmpty()){
lastTimestamp = buffers.get(buffers.size() - 1).getTimestamp();
}
}
return packets;
return new PacketBuffer(packets, lastTimestamp);
}
/**

View File

@@ -2,10 +2,13 @@ package im.rosetta.service.services;
import java.util.List;
import im.rosetta.client.tags.ECIAuthentificate;
import im.rosetta.client.tags.ECIDevice;
import im.rosetta.database.entity.Device;
import im.rosetta.database.entity.User;
import im.rosetta.database.repository.DeviceRepository;
import im.rosetta.service.Service;
import io.orprotocol.client.Client;
public class DeviceService extends Service<DeviceRepository> {
@@ -45,4 +48,38 @@ public class DeviceService extends Service<DeviceRepository> {
return this.getRepository().findAllByField("publicKey", publicKey);
}
/**
* Получает время последней синхронизации устройства, для корректной работы синхронизации сообщений
* @param client клиент для которого нужно получить время последней синхронизации устройства
* @return время последней синхронизации устройства, или 0 если устройство не найдено,
* или клиент не авторизован, таким образом вызывающий код синхронизирует все сообщения
*/
public long getLastSyncTime(Client client){
ECIAuthentificate eciAuthentificate = client.getTag(ECIAuthentificate.class);
if(eciAuthentificate == null || !eciAuthentificate.hasAuthorized()){
/**
* Если клиент не авторизован, возвращаем 0, такого быть не должно
*/
return 0;
}
ECIDevice eciDevice = client.getTag(ECIDevice.class);
if(eciDevice == null){
/**
* Если у клиента нет тега устройства, возвращаем 0, такого быть не должно, но на всякий случай
*/
return 0;
}
Device device = this.getRepository().findByField(new java.util.HashMap<String, Object>(){{
put("deviceId", eciDevice.getDeviceId());
put("publicKey", eciAuthentificate.getPublicKey());
}});
if(device == null){
/**
* Если устройство не найдено, возвращаем 0, значит это устройство новое
*/
return 0;
}
return device.getSyncTime();
}
}

View File

@@ -0,0 +1,31 @@
package im.rosetta.service.services.runtime;
import java.util.List;
import io.orprotocol.packet.Packet;
/**
* Класс для хранения пакетов для синхронизации и времени последнего пакета для корректной работы синхронизации сообщений
* Когда клиент запрашивает синхронизацию сообщений, мы возвращаем ему список пакетов для
* синхронизации и время последнего пакета, чтобы клиент мог корректно обновить время последней
* синхронизации и не запрашивать одни и те же пакеты при следующей синхронизации
*/
public class PacketBuffer {
private List<Packet> packets;
private long lastPacketTimestamp;
public PacketBuffer(List<Packet> packets, long lastPacketTimestamp) {
this.packets = packets;
this.lastPacketTimestamp = lastPacketTimestamp;
}
public List<Packet> getPackets() {
return packets;
}
public long getLastPacketTimestamp() {
return lastPacketTimestamp;
}
}