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