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
{
///
/// Service der die Verwaltung von Systemnachrichten ermöglicht
///
public class SystemMessageService : ServiceBase, ISystemMessageService
{
private readonly ICommunicationService _communicationService;
private readonly ISyncInfoPullService _pullService;
private readonly IRepository _appUserRepository;
///
/// Erstellt eine Instanz
///
/// Instanz eines IUnitOfWork
/// Instanz eines ICommunicationService
/// Instanz eines ISyncInfoPullService
public SystemMessageService(IUnitOfWork unitOfWork, ICommunicationService communicationService, ISyncInfoPullService pullService) : base(unitOfWork)
{
_communicationService = communicationService;
_pullService = pullService;
_appUserRepository = unitOfWork.GetRepository();
}
///
/// Gibt eine Entität anhand der eindeutigen Id zurück
///
/// Id der Entität
/// Gibt an ob NoTRacking verwendet werden soll. Es werden keine Entitäten im EF-Speicher gehalten
/// Entität oder null, wenn nicht gefunden
public override SystemMessage Get(object id, bool noTracking = true)
{
return Repository.SingleOrDefault(c => c.Id == id.ToString(), noTracking);
}
///
/// Gibt eine Entität anhand der eindeutigen Id zurück
///
/// Id der Entität
/// Gibt an ob NoTRacking verwendet werden soll. Es werden keine Entitäten im EF-Speicher gehalten
/// Entität oder null, wenn nicht gefunden
public override async Task GetAsync(object id, bool noTracking = true)
{
return await Repository.SingleOrDefaultAsync(c => c.Id == id.ToString(), noTracking).ConfigureAwait(false);
}
///
/// Holen der Daten vom lokalen Speicher die noch nicht synchronisiert wurden und senden an den Server
///
/// Aktuelles Accesstoken
/// CancellationToken
/// Task
public async Task> PushAsync(string accessToken, CancellationToken token)
{
//Bewusst nicht implementiert
await Task.Delay(1);
throw new NotImplementedException();
}
///
/// Gibt eine Liste der SystemNachrichten
///
/// Aktuelles Accesstoken
/// Typ des App-Users. Wird aber hier für den AppMode verwendet
/// Wie viele News sollen abgerufen werden? -1 Wenn nicht anwenden.
/// Wie viele News sollen ausgelassen werden? -1 Wenn nicht anwenden.
/// CancellationToken
/// Id des Senders
/// Liste SystemNachrichten
public async Task>> GetMessagesAsync(string senderId, AppUserType appUserType, int take, int skip, string accessToken, CancellationToken token)
{
var result = new ListCommunicationResult>() { Value = new List() };
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;
}
///
/// Gibt die Anzahl der ungelesenen Systemnachrichten zurück
///
/// Typ des App-Users. Wird aber hier für den AppMode verwendet
/// Anzahl ungelesene Nachrichten
public async Task CountUnreadAsync(AppUserType appUserType)
{
return await Repository.CountAsync(c => c.Deleted == false && c.Read == false && (c.AppUserType == appUserType || c.AppUserType == AppUserType.Both)).ConfigureAwait(false);
}
///
/// Gibt die Anzahl der ungelesenen Systemnachrichten für alle Rollen zurück
///
/// Anzahl ungelesene Nachrichten
public async Task CountUnreadAsync()
{
return await Repository.CountAsync(c => c.Deleted == false && c.Read == false).ConfigureAwait(false);
}
///
/// Eine Nachricht wurde gelesen
///
///
///
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();
}
}
///
/// 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
///
/// Typ des App-Users. Wird aber hier für den AppMode verwendet
/// Id des Objekts
/// Typ des Objekts
/// Task
public async Task 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();
}
///
/// Entfernen von Systemnachrichten vom Server für einen AppUser und eine bestimmte Kombination aus Key und Table
///
/// Id des AppUsers
/// Typ des AppUsers
/// Key
/// Table
/// Alter als dieses Datum
/// Aktuelles Accesstoken
/// CancellationToken
/// bool
public async Task 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;
}
///
/// Löschen abgelaufener Systemnachrichten
///
/// Task
public async Task 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;
}
///
/// Prüft ob eine Systemnachricht für eine bestimmte Kombination bereits existiert
///
/// Typ des App-Users. Wird aber hier für den AppMode verwendet
/// Id des Objekts
/// Typ des Objekts
/// Typ der Systemnachricht
/// true wenn es eine ungelesene gibt, false sonst
public async Task 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;
}
///
/// Hinzufügen einer Systemnachricht lokal
///
/// Id des AppUsers
/// Typ des App-Users. Wird aber hier für den AppMode verwendet
/// Id des Objekts
/// Typ des Objekts
/// Typ der Systemnachricht
/// Ablaufdatum
/// Task
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();
}
///
/// Löschen aller Systemnachrichten
///
/// Task
public async Task DeleteAllAsync()
{
var messages = await Repository.GetAllAsync(false);
foreach (var systemMessage in messages)
{
Repository.Remove(systemMessage);
}
await CommitAsync();
}
#region Push/Pull
///
/// Holen der letzten Daten vom Server und Synchronisieren mit den lokalen Daten
///
/// Sprache
/// Aktuelles Accesstoken
/// CancellationToken
/// Task
public async Task> PullAsync(string language, string accessToken, CancellationToken token)
{
var result = new SyncResult();
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
///
/// Behandeln der Liste von Nachrichten wenn welche vom Online-Store geholt werden.
///
/// Liste der Konversationen
/// Task
private async Task> HandleChangesAsync(List messages)
{
var result = new SyncResult();
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
}
}