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
}
}