using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.Json; using System.Threading.Tasks; using gehGassi.Dto; using gehGassi.Dto.Common; using gehGassi.Dto.Dogs; using gehGassi.Dto.Ratings; using gehGassi.Dto.Walks; using gehGassiApp.Core.Data; using gehGassiApp.Core.Interfaces; using gehGassiApp.Core.Interfaces.Synchronization; using gehGassiApp.Core.Mapper; using gehGassiApp.Core.Resources; using gehGassiApp.Domain.Common; using gehGassiApp.Domain.Users; namespace gehGassiApp.Core.Services { public class RatingService : ServiceBase, IRatingService { private readonly ICommunicationService _communicationService; private readonly ISyncInfoPullService _pullService; private readonly ISyncInfoPushService _pushService; private readonly ILocationService _locationService; private readonly IRepository _appUserRepository; /// /// Erstellt eine Instanz /// /// Instanz eines IUnitOfWork /// Instanz eines ICommunicationService /// Instanz eines ISyncInfoPullService /// Instanz eines ISyncInfoPushService /// Instanz eines ILocationService public RatingService(IUnitOfWork unitOfWork, ICommunicationService communicationService, ISyncInfoPullService syncInfoPullService, ISyncInfoPushService syncInfoPushService, ILocationService locationService) : base(unitOfWork) { _communicationService = communicationService; _pullService = syncInfoPullService; _pushService = syncInfoPushService; _locationService = locationService; _appUserRepository = unitOfWork.GetRepository(); } public override Rating Get(object id, bool noTracking = true) { throw new NotImplementedException(); } public override async Task GetAsync(object id, bool noTracking = true) { await Task.Delay(1); throw new NotImplementedException(); } /// /// Erstellen eines Ratings /// /// public Rating Create() { var rating = new Rating() { Id = Guid.NewGuid().ToString("N"), Created = DateTimeOffset.UtcNow }; return rating; } /// /// Gibt ein Rating basierend auf Abfragekriterien zurück /// /// Id des Bewerters /// Typ des Bewertes /// Id des Bewertungsziels /// Typ des Bewertungsziels /// Aktuelles Accesstoken /// CancellationToken /// true wenn bereits bewertet, false sonst public async Task GetRatingEx(string fromId, AppUserType fromType, string targetId, RatingTarget targetType, string accessToken, CancellationToken token) { //zuerst lokal prüfen, sonst auch online nachsehen var rating = await Repository.FirstOrDefaultAsync(c => c.FromId == fromId && c.FromType == fromType && c.TargetId == targetId && c.TargetType == targetType).ConfigureAwait(false); if (rating != null) return rating; //Nun online prüfen var isConnected = await _communicationService.IsConnected(); if (isConnected) { var query = new RatingCheckQueryDto() { FromId = fromId, FromType = (AppUserTypeDto)fromType, TargetId = targetId, TargetType = (RatingTargetDto)targetType }; var queryResult = await _communicationService.GetRatingExAsync(query, accessToken, token); if (queryResult.Success) return queryResult.Value; } return null; } /// /// Gibt ein Rating basierend auf der ID zurück /// /// Id des Ratings /// Aktuelles Accesstoken /// CancellationToken /// CommunicationResult public async Task GetRatingAsync(string ratingId, string accessToken, CancellationToken token) { //Nun online prüfen var isConnected = await _communicationService.IsConnected(); if (isConnected) { var queryResult = await _communicationService.GetRatingAsync(ratingId, accessToken, token); if (queryResult.Success) return queryResult.Value; } return null; } /// /// Hinzufügen eines Ratings /// /// Rating /// Aktuelles Accesstoken /// CancellationToken /// Angelegtes Rating public async Task AddAsync(Rating rating, string accessToken, CancellationToken token) { var existingRating = await Repository.GetAsync(rating.Id); if (existingRating == null) { Repository.Add(rating); await CommitAsync(); //Jetzt das Rating an der Server übertragen oder ein Delta erstellen var ratingDto = rating.ToDto(); var isConnected = await _communicationService.IsConnected(); var createDelta = true; if (isConnected) { var createResult = await _communicationService.CreateRatingAsync(ratingDto, accessToken, token); if (createResult.Success && createResult.Value.Status != CreateStatus.Error) { createDelta = false; } } if (createDelta) { await _pushService.AddAsync(nameof(Rating), rating.Id, SyncOperation.Create, rating).ConfigureAwait(false); } return rating; } return null; } /// /// Aktualisieren eines Ratings /// /// Rating /// Aktuelles Accesstoken /// CancellationToken /// true wenn erfolgreich, false sonst public async Task UpdateAsync(Rating rating, string accessToken, CancellationToken token) { var localRating = await Repository.FirstOrDefaultAsync(c => c.Id == rating.Id, false).ConfigureAwait(false); if (localRating != null && !localRating.Deleted) { localRating.FromId = rating.FromId; localRating.FromType = rating.FromType; localRating.TargetId = rating.TargetId; localRating.TargetType = rating.TargetType; localRating.Points = rating.Points; localRating.Info = rating.Info; localRating.UpdatedAt = DateTimeOffset.UtcNow; Repository.Update(localRating); await CommitAsync().ConfigureAwait(false); var ratingDto = localRating.ToDto(); var isConnected = await _communicationService.IsConnected(); var createDelta = true; if (isConnected) { var updateResult = await _communicationService.UpdateRatingAsync(ratingDto, accessToken, token); if (updateResult.Success && updateResult.Value) createDelta = false; } if (createDelta) { await _pushService.AddAsync(nameof(Rating), localRating.Id, SyncOperation.Edit, localRating).ConfigureAwait(false); } return true; } return false; } /// /// Echtes Löschen eines Ratings - wird derzeit nicht verwendet! /// Bitte Rating auf deleted setzen und UPDATE /// /// Id des Ratings /// Id des AppBenutzers /// Aktuelles Accesstoken /// CancellationToken /// true wenn erfolgreich, false sonst public async Task DeleteAsync(string ratingId, string appUserId, string accessToken, CancellationToken token) { var localRating = await Repository.FirstOrDefaultAsync(c => c.Id == ratingId, false).ConfigureAwait(false); if (localRating != null && localRating.FromId == appUserId) { Repository.Remove(localRating); await CommitAsync().ConfigureAwait(false); var ratingDto = localRating.ToDto(); var isConnected = await _communicationService.IsConnected(); var createDelta = true; if (isConnected) { var createResult = await _communicationService.DeleteRatingAsync(ratingDto, appUserId, accessToken, token); if (createResult.Success && createResult.Value) createDelta = false; } if (createDelta) { await _pushService.AddAsync(nameof(Rating), localRating.Id, SyncOperation.Delete, localRating).ConfigureAwait(false); } return true; } return false; } /// /// Wurde bereits eine Bewertung vorgenommen? /// /// Id des Bewerters /// Typ des Bewertes /// Id des Bewertungsziels /// Typ des Bewertungsziels /// Aktuelles Accesstoken /// CancellationToken /// true wenn bereits bewertet, false sonst public async Task HasRatedAsync(string fromId, AppUserType fromType, string targetId, RatingTarget targetType, string accessToken, CancellationToken token) { //zuerst lokal prüfen, sonst auch online nachsehen var rating = await Repository.FirstOrDefaultAsync(c => c.FromId == fromId && c.FromType == fromType && c.TargetId == targetId && c.TargetType == targetType).ConfigureAwait(false); if (rating != null) return true; //Nun online prüfen var isConnected = await _communicationService.IsConnected(); if (isConnected) { var query = new RatingCheckQueryDto() { FromId = fromId, FromType = (AppUserTypeDto)fromType, TargetId = targetId, TargetType = (RatingTargetDto)targetType }; var queryResult = await _communicationService.HasRatedAsync(query, accessToken, token); if (queryResult.Success) return queryResult.Value; } return false; } /// /// Kann eine Bewertung vorgenommen werden? /// /// Id des Bewerters /// Typ des Bewertes /// Id des Bewertungsziels /// Typ des Bewertungsziels /// Aktuelles Accesstoken /// CancellationToken /// true wennmöglich, false sonst public async Task CanRateAsync(string fromId, AppUserType fromType, string targetId, RatingTarget targetType, string accessToken, CancellationToken token) { //Nur online prüfen var isConnected = await _communicationService.IsConnected(); if (isConnected) { var query = new RatingCheckQueryDto() { FromId = fromId, FromType = (AppUserTypeDto)fromType, TargetId = targetId, TargetType = (RatingTargetDto)targetType }; var queryResult = await _communicationService.CanRateAsync(query, accessToken, token); if (queryResult.Success) return queryResult.Value; } return false; } /// /// Abrufen einer Liste von Ratings /// /// Quelle Id /// Quelle Typ /// Ziel Id /// Ziel Typ /// Liste von Sortierangaben /// Aktueller appMode /// Gewünschte Sprache /// Wie viele Walks sollen abgerufen werden? -1 Wenn nicht anwenden. /// Wie viele Walks sollen ausgelassen werden? -1 Wenn nicht anwenden. /// Aktuelles Accesstoken /// CancellationToken /// ListCommunicationResult public async Task>> GetRatingsAsync(string fromId, AppUserType? fromType, string targetId, RatingTarget? targetType, List sortOrders, string language, AppMode appMode, LocationDto locaton, int take, int skip, string accessToken, CancellationToken token) { var result = new ListCommunicationResult>() { Value = new List() }; var isConnected = await _communicationService.IsConnected(); if (isConnected) { var query = new RatingsQueryDto() { Location = locaton, Language = language, AppMode = (AppModeDto)appMode, Take = take, Skip = skip, LastUpdate = null, FromId = fromId, FromType = (AppUserTypeDto?)fromType, TargetId = targetId, TargetType = (RatingTargetDto?)targetType }; query.DynamicSortOrder = sortOrders.ToDto(); var queryResult = await _communicationService.GetRatingsWithNamesAsync(query, accessToken, token); if (queryResult.Success && queryResult.Value.Count >= 1) { return queryResult; } } else { result.Success = false; result.ErrorCode = CommunicationErrors.ServerNoConnection; result.ErrorMessage = Errors.Server_NoConnection; } return result; } #region Pull-Push Implementation /// /// 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) { var user = await _appUserRepository.FirstOrDefaultAsync(c => c.Id != "", false).ConfigureAwait(false); //Zuerst lezte Aktivität holen... DateTimeOffset? lastUpdate = null; var lastSyncInfo = await _pullService.GetAsync(nameof(Rating)); if (lastSyncInfo != null) lastUpdate = lastSyncInfo.LastUpdate; var requestResult = await _communicationService.GetRatingsForSyncAsync(user.Id, lastUpdate, accessToken, token); if (requestResult.Success) { result = await HandleChangesAsync(requestResult.Value); if (result.LastUpdate != null) await _pullService.AddOrUpddateAsync(nameof(Rating), result.LastUpdate.Value).ConfigureAwait(false); } result.LastUpdate ??= lastUpdate; } return result; } /// /// 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) { var result = new SyncResult(); if (await _pushService.HasOpenAsync(nameof(Rating)) > 0) { var deltas = await _pushService.GetAllAsync(nameof(Rating)); if (deltas.Any()) { var user = await _appUserRepository.FirstOrDefaultAsync(c => c.Id != "", false).ConfigureAwait(false); deltas = deltas.OrderBy(c => c.DateTime).ToList(); var isConnected = await _communicationService.IsConnected(); if (!isConnected) return result; foreach (var syncInfoPush in deltas) { try { if (!string.IsNullOrWhiteSpace(syncInfoPush.Value)) { var request = JsonSerializer.Deserialize(syncInfoPush.Value, new JsonSerializerOptions(JsonSerializerDefaults.Web)); if (syncInfoPush.Operation == SyncOperation.Create) { var createDto = request.ToDto(); var createResult = await _communicationService.CreateRatingAsync(createDto, accessToken, token); if (createResult.Success && createResult.Value.Status != CreateStatus.Error) { await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false); } //TODO: Was tun wenn Fehler? } else if (syncInfoPush.Operation == SyncOperation.Edit) { var updateDto = request.ToDto(); var updateResult = await _communicationService.UpdateRatingAsync(updateDto, accessToken, token); if (updateResult.Success && updateResult.Value) await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false); //TODO: Was tun wenn Fehler? } else { var deleteDto = request.ToDto(); var deleteResult = await _communicationService.DeleteRatingAsync(deleteDto, user.Id, accessToken, token); if (deleteResult.Success && deleteResult.Value) await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false); //TODO: Was tun wenn Fehler? } } else { await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false); } } //Else ist nichts tun, konnte nicht übertragen werden. } } return result; } #endregion #region Private /// /// Behandeln der Liste von Ratings wenn welche vom Online-Store geholt werden. /// /// Liste der Ratings /// Task private async Task> HandleChangesAsync(List ratings) { var result = new SyncResult(); //Je Rasse durchgehen ob was gemacht werden soll foreach (var rating in ratings) { var localRating = await Repository.FirstOrDefaultAsync(c => c.Id == rating.Id, false); if (localRating != null && localRating.UpdatedAt < rating.UpdatedAt) { if (localRating.UpdatedAt >= rating.UpdatedAt) { if (result.LastUpdate == null || result.LastUpdate < localRating.UpdatedAt) result.LastUpdate = localRating.UpdatedAt; continue; } var deleted = localRating.Deleted == false && rating.Deleted; localRating.Version = rating.Version; localRating.UpdatedAt = rating.UpdatedAt; localRating.Deleted = rating.Deleted; localRating.FromId = rating.FromId; localRating.FromType = rating.FromType; localRating.TargetId = rating.TargetId; localRating.TargetType = rating.TargetType; localRating.Points = rating.Points; localRating.Info = rating.Info; localRating.Created = rating.Created; if (result.LastUpdate == null || result.LastUpdate < localRating.UpdatedAt) result.LastUpdate = localRating.UpdatedAt; if (!deleted) result.Updated.Add(localRating); else result.Deleted.Add(localRating); Repository.Update(localRating); } if (localRating == null) { localRating = new Rating() { Id = rating.Id, Version = rating.Version, UpdatedAt = rating.UpdatedAt, Deleted = rating.Deleted, FromId = rating.FromId, FromType = rating.FromType, TargetId = rating.TargetId, TargetType = rating.TargetType, Points = rating.Points, Info = rating.Info, Created = rating.Created }; Repository.Add(localRating); if (result.LastUpdate == null || result.LastUpdate < localRating.UpdatedAt) result.LastUpdate = localRating.UpdatedAt; result.Added.Add(localRating); } } if (result.HasChanges) { try { await CommitAsync().ConfigureAwait(false); } catch (Exception ex) { var err = ex.Message; } } return result; } #endregion } }