379 lines
17 KiB
C#
379 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using gehGassi.Dto;
|
|
using gehGassiApp.Core.Data;
|
|
using gehGassiApp.Core.Helper;
|
|
using gehGassiApp.Core.Interfaces;
|
|
using gehGassiApp.Core.Interfaces.Synchronization;
|
|
using gehGassiApp.Domain.Common;
|
|
using gehGassiApp.Domain.Messages;
|
|
using gehGassiApp.Domain.Users;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace gehGassiApp.Core.Services
|
|
{
|
|
/// <summary>
|
|
/// Service der die Verwaltung von Systemnachrichten ermöglicht
|
|
/// </summary>
|
|
public class SystemMessageService : ServiceBase<SystemMessage>, ISystemMessageService
|
|
{
|
|
private readonly ICommunicationService _communicationService;
|
|
private readonly ISyncInfoPullService _pullService;
|
|
private readonly IRepository<AppUser> _appUserRepository;
|
|
|
|
/// <summary>
|
|
/// Erstellt eine Instanz
|
|
/// </summary>
|
|
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
|
|
/// <param name="communicationService">Instanz eines ICommunicationService</param>
|
|
/// <param name="pullService">Instanz eines ISyncInfoPullService</param>
|
|
public SystemMessageService(IUnitOfWork unitOfWork, ICommunicationService communicationService, ISyncInfoPullService pullService) : base(unitOfWork)
|
|
{
|
|
_communicationService = communicationService;
|
|
_pullService = pullService;
|
|
_appUserRepository = unitOfWork.GetRepository<AppUser>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Entität anhand der eindeutigen Id zurück
|
|
/// </summary>
|
|
/// <param name="id">Id der Entität</param>
|
|
/// <param name="noTracking">Gibt an ob NoTRacking verwendet werden soll. Es werden keine Entitäten im EF-Speicher gehalten</param>
|
|
/// <returns>Entität oder null, wenn nicht gefunden</returns>
|
|
public override SystemMessage Get(object id, bool noTracking = true)
|
|
{
|
|
return Repository.SingleOrDefault(c => c.Id == id.ToString(), noTracking);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Entität anhand der eindeutigen Id zurück
|
|
/// </summary>
|
|
/// <param name="id">Id der Entität</param>
|
|
/// <param name="noTracking">Gibt an ob NoTRacking verwendet werden soll. Es werden keine Entitäten im EF-Speicher gehalten</param>
|
|
/// <returns>Entität oder null, wenn nicht gefunden</returns>
|
|
public override async Task<SystemMessage> GetAsync(object id, bool noTracking = true)
|
|
{
|
|
return await Repository.SingleOrDefaultAsync(c => c.Id == id.ToString(), noTracking).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Holen der Daten vom lokalen Speicher die noch nicht synchronisiert wurden und senden an den Server
|
|
/// </summary>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>Task</returns>
|
|
public async Task<SyncResult<SystemMessage>> PushAsync(string accessToken, CancellationToken token)
|
|
{
|
|
//Bewusst nicht implementiert
|
|
await Task.Delay(1);
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Liste der SystemNachrichten
|
|
/// </summary>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="appUserType">Typ des App-Users. Wird aber hier für den AppMode verwendet</param>
|
|
/// <param name="take">Wie viele News sollen abgerufen werden? -1 Wenn nicht anwenden.</param>
|
|
/// <param name="skip">Wie viele News sollen ausgelassen werden? -1 Wenn nicht anwenden.</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <param name="senderId">Id des Senders</param>
|
|
/// <returns>Liste SystemNachrichten</returns>
|
|
public async Task<ListCommunicationResult<List<SystemMessage>>> GetMessagesAsync(string senderId, AppUserType appUserType, int take, int skip, string accessToken, CancellationToken token)
|
|
{
|
|
var result = new ListCommunicationResult<List<SystemMessage>>() { Value = new List<SystemMessage>() };
|
|
|
|
var isConnected = await _communicationService.IsConnected();
|
|
//isConnected = false;
|
|
if (isConnected)
|
|
{
|
|
var messagesResult = await _communicationService.GetSystemMessagesForSyncAsync(senderId, null, accessToken, token).ConfigureAwait(false);
|
|
if (messagesResult.Success)
|
|
{
|
|
var changesResult = await HandleChangesAsync(messagesResult.Value);
|
|
if (changesResult.LastUpdate != null)
|
|
await _communicationService.ConfirmSystemMessagesAsync(senderId, changesResult.LastUpdate, accessToken, token);
|
|
}
|
|
}
|
|
|
|
//Jetzt die DB abfragen
|
|
result.Total = await Repository.CountAsync(c => c.Read == false && c.Deleted == false && (c.AppUserType == appUserType || c.AppUserType == AppUserType.Both));
|
|
//var query = Repository.Query(c => c.Read == false && c.Deleted == false && (c.AppUserType == appUserType || c.AppUserType == AppUserType.Both)).OrderByDescending(c => c.UpdatedAt);
|
|
//var messages = await (query.Skip(skip).Take(take).ToListAsync().ConfigureAwait(false));
|
|
var messages = (await Repository.FindAsync(c => c.Read == false && c.Deleted == false && (c.AppUserType == appUserType || c.AppUserType == AppUserType.Both), c => c.UpdatedAt, skip, take, false)).ToList();
|
|
result.Success = true;
|
|
result.Value = messages;
|
|
result.Take = take;
|
|
result.Skip = skip;
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt die Anzahl der ungelesenen Systemnachrichten zurück
|
|
/// </summary>
|
|
/// <param name="appUserType">Typ des App-Users. Wird aber hier für den AppMode verwendet</param>
|
|
/// <returns>Anzahl ungelesene Nachrichten</returns>
|
|
public async Task<int> CountUnreadAsync(AppUserType appUserType)
|
|
{
|
|
return await Repository.CountAsync(c => c.Deleted == false && c.Read == false && (c.AppUserType == appUserType || c.AppUserType == AppUserType.Both)).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt die Anzahl der ungelesenen Systemnachrichten für alle Rollen zurück
|
|
/// </summary>
|
|
/// <returns>Anzahl ungelesene Nachrichten</returns>
|
|
public async Task<int> CountUnreadAsync()
|
|
{
|
|
return await Repository.CountAsync(c => c.Deleted == false && c.Read == false).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Eine Nachricht wurde gelesen
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
/// <returns></returns>
|
|
public async Task MessageReadAsync(SystemMessage message)
|
|
{
|
|
var localMessage = await Repository.FirstOrDefaultAsync(c => c.Id == message.Id, false);
|
|
if (localMessage != null)
|
|
{
|
|
localMessage.Read = true;
|
|
localMessage.ReadDate = DateTimeOffset.UtcNow;
|
|
Repository.Update(localMessage);
|
|
await CommitAsync();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bestätigt alle Nachrichten die den Kriterien entsprechen als gelesen.
|
|
/// Kann verwendet werden wenn z.B. ein Walk geöffnet wird ohne dass eine anliegende Systemnachricht gelesen wurde
|
|
/// </summary>
|
|
/// <param name="appUserType">Typ des App-Users. Wird aber hier für den AppMode verwendet</param>
|
|
/// <param name="key">Id des Objekts</param>
|
|
/// <param name="table">Typ des Objekts</param>
|
|
/// <returns>Task</returns>
|
|
public async Task<int> MessagesReadAsync(AppUserType appUserType, string key, string table)
|
|
{
|
|
var messages = await Repository.FindAsync(c => c.AppUserType == appUserType && c.Key == key && c.Table == table && c.Read == false, false).ConfigureAwait(false);
|
|
foreach (var message in messages)
|
|
{
|
|
message.Read = true;
|
|
message.ReadDate = DateTimeOffset.UtcNow;
|
|
Repository.Update(message);
|
|
}
|
|
await CommitAsync();
|
|
return messages.Count();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Entfernen von Systemnachrichten vom Server für einen AppUser und eine bestimmte Kombination aus Key und Table
|
|
/// </summary>
|
|
/// <param name="appUserId">Id des AppUsers</param>
|
|
/// <param name="appUserType">Typ des AppUsers</param>
|
|
/// <param name="key">Key</param>
|
|
/// <param name="table">Table</param>
|
|
/// <param name="created">Alter als dieses Datum</param>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>bool</returns>
|
|
public async Task<bool> RemoveMessagesAsync(string appUserId, AppUserType appUserType, string key, string table, DateTimeOffset? created, string accessToken, CancellationToken token)
|
|
{
|
|
var isConnected = await _communicationService.IsConnected();
|
|
if (isConnected)
|
|
{
|
|
var createResult = await _communicationService.RemoveSystemMessagesAsync(appUserId, appUserType, key, table, created, accessToken, token);
|
|
if (createResult.Success)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Löschen abgelaufener Systemnachrichten
|
|
/// </summary>
|
|
/// <returns>Task</returns>
|
|
public async Task<int> RemoveExpiredOrReadAsync()
|
|
{
|
|
var now = DateTimeOffset.UtcNow;
|
|
var yesterday = DateTimeOffset.UtcNow.AddDays(-1);
|
|
var count = 0;
|
|
var messages = await Repository.FindAsync(c => (c.Read == true && c.ReadDate <= yesterday) || c.Expires < now, false).ConfigureAwait(false);
|
|
if (messages != null && messages.Any())
|
|
{
|
|
foreach (var message in messages)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"Lösche {message.Id} | {message.Key} | {message.Table} | {message.AppUserType} | {message.Type} | {message.Read} | {message.ReadDate} | {message.Created} | {message.Expires}");
|
|
Repository.Remove(message);
|
|
}
|
|
|
|
await CommitAsync();
|
|
count = messages.Count();
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Prüft ob eine Systemnachricht für eine bestimmte Kombination bereits existiert
|
|
/// </summary>
|
|
/// <param name="appUserType">Typ des App-Users. Wird aber hier für den AppMode verwendet</param>
|
|
/// <param name="key">Id des Objekts</param>
|
|
/// <param name="table">Typ des Objekts</param>
|
|
/// <param name="type">Typ der Systemnachricht</param>
|
|
/// <returns>true wenn es eine ungelesene gibt, false sonst</returns>
|
|
public async Task<bool> HasMessageAsync(AppUserType appUserType, string key, string table, SystemMessageType type)
|
|
{
|
|
var messageFound = await Repository.FirstOrDefaultAsync(c => c.AppUserType == appUserType && c.Key == key && c.Table == table && c.Type == type);
|
|
return messageFound != null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hinzufügen einer Systemnachricht lokal
|
|
/// </summary>
|
|
/// <param name="appUserId">Id des AppUsers</param>
|
|
/// <param name="appUserType">Typ des App-Users. Wird aber hier für den AppMode verwendet</param>
|
|
/// <param name="key">Id des Objekts</param>
|
|
/// <param name="table">Typ des Objekts</param>
|
|
/// <param name="type">Typ der Systemnachricht</param>
|
|
/// <param name="expires">Ablaufdatum</param>
|
|
/// <returns>Task</returns>
|
|
public async Task AddMessageAsync(string appUserId, AppUserType appUserType, string key, string table, SystemMessageType type, DateTimeOffset? expires)
|
|
{
|
|
var message = new SystemMessage()
|
|
{
|
|
Id = Guid.NewGuid().ToString("N"),
|
|
Created = DateTimeOffset.UtcNow,
|
|
Read = false,
|
|
ReadDate = null,
|
|
AppUserId = appUserId,
|
|
AppUserType = appUserType,
|
|
Key = key,
|
|
Table = table,
|
|
Type = type,
|
|
Expires = expires,
|
|
UpdatedAt = DateTimeOffset.UtcNow
|
|
};
|
|
|
|
Repository.Add(message);
|
|
await CommitAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Löschen aller Systemnachrichten
|
|
/// </summary>
|
|
/// <returns>Task</returns>
|
|
public async Task DeleteAllAsync()
|
|
{
|
|
var messages = await Repository.GetAllAsync(false);
|
|
foreach (var systemMessage in messages)
|
|
{
|
|
Repository.Remove(systemMessage);
|
|
}
|
|
|
|
await CommitAsync();
|
|
}
|
|
|
|
#region Push/Pull
|
|
|
|
/// <summary>
|
|
/// Holen der letzten Daten vom Server und Synchronisieren mit den lokalen Daten
|
|
/// </summary>
|
|
/// <param name="language">Sprache</param>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>Task</returns>
|
|
public async Task<SyncResult<SystemMessage>> PullAsync(string language, string accessToken, CancellationToken token)
|
|
{
|
|
var result = new SyncResult<SystemMessage>();
|
|
|
|
var isConnected = await _communicationService.IsConnected();
|
|
|
|
if (isConnected)
|
|
{
|
|
//Zuerst lezte Aktivität holen... HIER NICHT da wir immer alle offenen Nachrichten holen
|
|
//DateTimeOffset? lastUpdate = null;
|
|
//var lastSyncInfo = await _pullService.GetAsync(nameof(Message));
|
|
//if (lastSyncInfo != null)
|
|
// lastUpdate = lastSyncInfo.LastUpdate;
|
|
|
|
var user = await _appUserRepository.FirstOrDefaultAsync(c => c.Id != "", false).ConfigureAwait(false);
|
|
|
|
var messagesResult = await _communicationService.GetSystemMessagesForSyncAsync(user.Id, null, accessToken, token);
|
|
if (messagesResult.Success)
|
|
{
|
|
result = await HandleChangesAsync(messagesResult.Value);
|
|
if (result.LastUpdate != null)
|
|
{
|
|
await _communicationService.ConfirmSystemMessagesAsync(user.Id, result.LastUpdate, accessToken, token);
|
|
await _pullService.AddOrUpddateAsync(nameof(SystemMessage), result.LastUpdate.Value).ConfigureAwait(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region private
|
|
|
|
/// <summary>
|
|
/// Behandeln der Liste von Nachrichten wenn welche vom Online-Store geholt werden.
|
|
/// </summary>
|
|
/// <param name="messages">Liste der Konversationen</param>
|
|
/// <returns>Task</returns>
|
|
private async Task<SyncResult<SystemMessage>> HandleChangesAsync(List<SystemMessage> messages)
|
|
{
|
|
var result = new SyncResult<SystemMessage>();
|
|
|
|
foreach (var message in messages)
|
|
{
|
|
var localMessage = await Repository.FirstOrDefaultAsync(c => c.Key == message.Key && c.Table == message.Table && c.Type == message.Type && c.AppUserType == message.AppUserType, false).ConfigureAwait(false);
|
|
if (localMessage != null && !localMessage.Deleted)
|
|
{
|
|
localMessage.Read = false;
|
|
localMessage.ReadDate = null;
|
|
localMessage.Created = message.Created;
|
|
localMessage.Expires = message.Expires;
|
|
localMessage.Message = message.Message;
|
|
localMessage.UpdatedAt = message.UpdatedAt;
|
|
|
|
if (result.LastUpdate == null || result.LastUpdate < localMessage.UpdatedAt)
|
|
result.LastUpdate = localMessage.UpdatedAt;
|
|
result.Updated.Add(localMessage);
|
|
}
|
|
|
|
if (localMessage == null)
|
|
{
|
|
Repository.Add(message);
|
|
|
|
if (result.LastUpdate == null || result.LastUpdate < message.UpdatedAt)
|
|
result.LastUpdate = message.UpdatedAt;
|
|
result.Added.Add(message);
|
|
}
|
|
}
|
|
|
|
if (result.HasChanges)
|
|
{
|
|
try
|
|
{
|
|
await CommitAsync().ConfigureAwait(false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
var err = ex.Message;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|