using gehGassiApp.Core.Interfaces; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using gehGassi.Dto.Dogs; using gehGassiApp.Domain.Dogs; using gehGassiApp.Core.Data; using gehGassiApp.Core.Interfaces.Synchronization; using gehGassiApp.Domain.Common; using gehGassiApp.Domain.Users; using gehGassi.Dto; using gehGassiApp.Core.Helper; using gehGassiApp.Domain.Messages; using System.Text.Json; using gehGassiApp.Core.Mapper; namespace gehGassiApp.Core.Services { /// /// Service der die Verwaltung von Hunden ermöglicht /// public class DogService : ServiceBase, IDogService { private readonly ICommunicationService _communicationService; private readonly ISyncInfoPullService _pullService; private readonly ISyncInfoPushService _pushService; private readonly IRepository _appUserRepository; /// /// Erstellt eine Instanz /// /// Instanz eines IUnitOfWork /// Instanz eines ICommunicationService /// Instanz eines ISyncInfoPullService /// Instanz eines ISyncInfoPushService public DogService(IUnitOfWork unitOfWork, ICommunicationService communicationService, ISyncInfoPullService pullService, ISyncInfoPushService pushService) : base(unitOfWork) { _communicationService = communicationService; _pullService = pullService; _pushService = pushService; _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 Dog 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); } /// /// Erstellen eines Hundes /// /// public Dog Create(string appUserId) { var dog = new Dog() { Id = Guid.NewGuid().ToString("N"), AppUserId = appUserId, Created = DateTimeOffset.UtcNow }; return dog; } /// /// Gibt eine Liste der Hunden zurück. /// Entweder von der DB oder vom Server /// /// Id des AppBenutzers /// Aktuelles Accesstoken /// CancellationToken /// Liste der Hunde public async Task> GetDogsAsync(string appUserId, string accessToken, CancellationToken token) { //Zuerst DB var localDogs = await Repository.FindAsync(c => c.AppUserId == appUserId && c.Deleted == false, noTracking: true).ConfigureAwait(false); if (localDogs != null && localDogs.Any()) return localDogs.Where(c => c.Deleted == false).OrderBy(c => c.Name).ToList(); else { //Dann wenn lokal nicht verfügbar immer online... var isConnected = await _communicationService.IsConnected(); //isConnected = false; if (isConnected) { var dogsResult = await _communicationService.GetDogsAsync(appUserId, null, accessToken, token); if (dogsResult.Success) { var result = await HandleChangesAsync(dogsResult.Value); if (result.LastUpdate != null) await _pullService.AddOrUpddateAsync(nameof(Dog), result.LastUpdate.Value).ConfigureAwait(false); return dogsResult.Value.Where(c => c.Deleted == false).OrderBy(c => c.Name).ToList(); } } } //Keine Daten online, keine offline return new List(); } /// /// Gibt eine Liste der Hunden zurück. /// Entweder von der DB oder vom Server /// /// Id des AppBenutzers /// Gewünschte Sprache /// Aktuelles Accesstoken /// CancellationToken /// Liste der Hunde public async Task> GetDogsMinAsync(string appUserId, string language, string accessToken, CancellationToken token) { var isConnected = await _communicationService.IsConnected(); //isConnected = false; if (isConnected) { var dogsResult = await _communicationService.GetDogsMinAsync(appUserId, language, accessToken, token); if (dogsResult.Success) { return dogsResult.Value.Where(c => c.Deleted == false).OrderBy(c => c.Name).ToList(); } } return new List(); } /// /// Gibt einen hund vom Server zurück /// /// Id des Hundes /// Aktuelles Accesstoken /// CancellationToken /// Hund oder null, wenn nicht gfunden public async Task GetDogAsync(string dogId, string accessToken, CancellationToken token) { var isConnected = await _communicationService.IsConnected(); if (isConnected) { var dogResult = await _communicationService.GetDogAsync(dogId, accessToken, token); if (dogResult.Success) { return dogResult.Value; } } return null; } /// /// Hinzufügen eines Hundes /// /// Hund /// Aktuelles Accesstoken /// CancellationToken /// Angelegter Hund oder null, wenn nicht anlegbar public async Task AddAsync(Dog dog, string accessToken, CancellationToken token) { var existingDog = await Repository.GetAsync(dog.Id); if (existingDog == null) { Repository.Add(dog); await CommitAsync(); //Jetzt den Hund an der Server übertragen oder ein Delta erstellen var dogDto = dog.ToDto(); var photoFileName = string.Empty; byte[] photo = null; if (!string.IsNullOrWhiteSpace(dog.Photo)) { if (!dog.Photo.StartsWithHttp()) { //Datei laden und in byte array umwandeln if (File.Exists(dog.Photo)) { await using var stream = File.OpenRead(dog.Photo); using var memoryStream = new MemoryStream(); await stream.CopyToAsync(memoryStream, token); photo = memoryStream.ToArray(); photoFileName = Path.GetFileName(dogDto.Photo); } } } var isConnected = await _communicationService.IsConnected(); var createDelta = true; if (isConnected) { var createResult = await _communicationService.AddDogAsync(dogDto, photoFileName, photo, accessToken, token); if (createResult.Success && createResult.Value.Status != CreateStatus.Error) { if (createResult.Value.Status == CreateStatus.Success) //Damit wird exists umgangen, darf gar nicht sein { dog.Number = createResult.Value.Value.Number; Repository.Update(dog); await CommitAsync(); } createDelta = false; } } if (createDelta) { await _pushService.AddAsync(nameof(Dog), dog.Id, SyncOperation.Create, dog).ConfigureAwait(false); } return dog; } return null; } /// /// Aktualisieren eines Hundes /// /// Hund /// Aktuelles Accesstoken /// CancellationToken /// Angelegter Hund public async Task UpdateAsync(Dog dog, string accessToken, CancellationToken token) { var localDog = await Repository.FirstOrDefaultAsync(c => c.Id == dog.Id, false).ConfigureAwait(false); if (localDog != null && !localDog.Deleted) { localDog.DogRaceId = dog.DogRaceId; localDog.AppUserId = dog.AppUserId; localDog.Number = dog.Number; localDog.Name = dog.Name; localDog.Description = dog.Description; localDog.Photo = dog.Photo; localDog.BirthDate = dog.BirthDate; localDog.Sex = dog.Sex; localDog.Size = dog.Size; localDog.PullsTheLeash = dog.PullsTheLeash; localDog.WalksTheLeash = dog.WalksTheLeash; localDog.BasicCommands = dog.BasicCommands; localDog.Hunter = dog.Hunter; localDog.Eating = dog.Eating; localDog.Jumper = dog.Jumper; localDog.LikesAdults = dog.LikesAdults; localDog.LikesChildren = dog.LikesChildren; localDog.LikesConspecifics = dog.LikesConspecifics; localDog.IsTrustful = dog.IsTrustful; localDog.IsAggressiv = dog.IsAggressiv; localDog.IsLoneGunner = dog.IsLoneGunner; localDog.AggressionLevel = dog.AggressionLevel; localDog.RushesOutside = dog.RushesOutside; localDog.IsCalm = dog.IsCalm; localDog.IsPeeing = dog.IsPeeing; localDog.IsShy = dog.IsShy; localDog.IsGuard = dog.IsGuard; localDog.Intolerances = dog.Intolerances; localDog.Chiped = dog.Chiped; localDog.Sterilized = dog.Sterilized; localDog.MedicalInfo = dog.MedicalInfo; localDog.HouseTrained = dog.HouseTrained; localDog.FeedingTimes = dog.FeedingTimes; localDog.FeedingIndividual = dog.FeedingIndividual; localDog.RatingStatistics = dog.RatingStatistics; localDog.UpdatedAt = DateTimeOffset.UtcNow; Repository.Update(localDog); await CommitAsync().ConfigureAwait(false); var dogDto = localDog.ToDto(); var photoFileName = string.Empty; byte[] photo = null; if (!string.IsNullOrWhiteSpace(localDog.Photo)) { if (!localDog.Photo.StartsWithHttp()) { //Datei laden und in byte array umwandeln if (File.Exists(localDog.Photo)) { await using var stream = File.OpenRead(localDog.Photo); using var memoryStream = new MemoryStream(); await stream.CopyToAsync(memoryStream, token); photo = memoryStream.ToArray(); photoFileName = Path.GetFileName(dogDto.Photo); } } } var isConnected = await _communicationService.IsConnected(); var createDelta = true; if (isConnected) { var updateResult = await _communicationService.UpdateDogAsync(dogDto, photoFileName, photo, accessToken, token); if (updateResult.Success && updateResult.Value) createDelta = false; } if (createDelta) { await _pushService.AddAsync(nameof(Dog), localDog.Id, SyncOperation.Edit, localDog).ConfigureAwait(false); } return true; } return false; } /// /// Echtes Löschen eines Hundes - wird derzeit nicht verwendet! /// Bitte Hund auf deleted setzen und UPDATE /// /// Id des Hundes /// Id des AppBenutzers /// Aktuelles Accesstoken /// CancellationToken /// Angelegter Hund public async Task DeleteAsync(string dogId, string appUserId, string accessToken, CancellationToken token) { var localDog = await Repository.FirstOrDefaultAsync(c => c.Id == dogId, false).ConfigureAwait(false); if (localDog != null && localDog.AppUserId == appUserId) { //Bild löschen wenn nicht URL. if (!string.IsNullOrWhiteSpace(localDog.Photo)) { if (!localDog.Photo.StartsWithHttp()) { if (File.Exists(localDog.Photo)) { File.Delete(localDog.Photo); } } } Repository.Remove(localDog); await CommitAsync().ConfigureAwait(false); var dogDto = localDog.ToDto(); var isConnected = await _communicationService.IsConnected(); var createDelta = true; if (isConnected) { var createResult = await _communicationService.DeleteDogAsync(dogDto, appUserId, accessToken, token); if (createResult.Success && createResult.Value) createDelta = false; } if (createDelta) { await _pushService.AddAsync(nameof(Dog), localDog.Id, SyncOperation.Delete, localDog).ConfigureAwait(false); } return true; } return false; } #region Pull-Push Implementation /// /// 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(Dog)) > 0) { var deltas = await _pushService.GetAllAsync(nameof(Dog)); 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 dog = JsonSerializer.Deserialize(syncInfoPush.Value, new JsonSerializerOptions(JsonSerializerDefaults.Web)); var dogDto = dog.ToDto(); var photoFileName = string.Empty; byte[] photo = null; if (!string.IsNullOrWhiteSpace(dog.Photo)) { if (!dog.Photo.StartsWithHttp()) { //Datei laden und in byte array umwandeln if (File.Exists(dog.Photo)) { await using var stream = File.OpenRead(dog.Photo); using var memoryStream = new MemoryStream(); await stream.CopyToAsync(memoryStream, token); photo = memoryStream.ToArray(); photoFileName = Path.GetFileName(dogDto.Photo); } } } if (syncInfoPush.Operation == SyncOperation.Create) { var createResult = await _communicationService.AddDogAsync(dogDto, photoFileName, photo, accessToken, token); if (createResult.Success && createResult.Value.Status != CreateStatus.Error) { await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false); var dogToUpdate = await Repository.GetAsync(createResult.Value.Value.Id); if (dogToUpdate != null) { dogToUpdate.Number = createResult.Value.Value.Number; Repository.Update(dogToUpdate); await CommitAsync(); } } //TODO: Was tun wenn Fehler? } else if (syncInfoPush.Operation == SyncOperation.Edit) { var updateResult = await _communicationService.UpdateDogAsync(dogDto, photoFileName, photo, accessToken, token); if(updateResult.Success && updateResult.Value) await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false); //TODO: Was tun wenn Fehler? } else { var deleteResult = await _communicationService.DeleteDogAsync(dogDto, 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; } /// /// 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(Dog)); if (lastSyncInfo != null) lastUpdate = lastSyncInfo.LastUpdate; var dogsResult = await _communicationService.GetDogsAsync(user.Id, lastUpdate, accessToken, token); if (dogsResult.Success) { result = await HandleChangesAsync(dogsResult.Value); if (result.LastUpdate != null) await _pullService.AddOrUpddateAsync(nameof(Dog), result.LastUpdate.Value).ConfigureAwait(false); } result.LastUpdate ??= lastUpdate; } return result; } #endregion #region Private /// /// Behandeln der Liste von Hunden wenn welche vom Online-Store geholt werden. /// /// Liste der Hunde /// Task private async Task> HandleChangesAsync(List dogs) { var result = new SyncResult(); //Je Rasse durchgehen ob was gemacht werden soll foreach (var dog in dogs) { var localDog = await Repository.FirstOrDefaultAsync(c => c.Id == dog.Id, false); if (localDog != null && localDog.UpdatedAt < dog.UpdatedAt) { if (localDog.UpdatedAt >= dog.UpdatedAt) { if (result.LastUpdate == null || result.LastUpdate < localDog.UpdatedAt) result.LastUpdate = localDog.UpdatedAt; continue; } var deleted = localDog.Deleted == false && dog.Deleted; localDog.Version = dog.Version; localDog.UpdatedAt = dog.UpdatedAt; localDog.Deleted = dog.Deleted; localDog.DogRaceId = dog.DogRaceId; localDog.AppUserId = dog.AppUserId; localDog.Number = dog.Number; localDog.Name = dog.Name; localDog.Description = dog.Description; localDog.Photo = dog.Photo; localDog.BirthDate = dog.BirthDate; localDog.Sex = dog.Sex; localDog.Size = dog.Size; localDog.PullsTheLeash = dog.PullsTheLeash; localDog.WalksTheLeash = dog.WalksTheLeash; localDog.BasicCommands = dog.BasicCommands; localDog.Hunter = dog.Hunter; localDog.Eating = dog.Eating; localDog.Jumper = dog.Jumper; localDog.LikesAdults = dog.LikesAdults; localDog.LikesChildren = dog.LikesChildren; localDog.LikesConspecifics = dog.LikesConspecifics; localDog.IsTrustful = dog.IsTrustful; localDog.IsAggressiv = dog.IsAggressiv; localDog.IsLoneGunner = dog.IsLoneGunner; localDog.AggressionLevel = dog.AggressionLevel; localDog.RushesOutside = dog.RushesOutside; localDog.IsCalm = dog.IsCalm; localDog.IsPeeing = dog.IsPeeing; localDog.IsShy = dog.IsShy; localDog.IsGuard = dog.IsGuard; localDog.Intolerances = dog.Intolerances; localDog.Chiped = dog.Chiped; localDog.Sterilized = dog.Sterilized; localDog.MedicalInfo = dog.MedicalInfo; localDog.HouseTrained = dog.HouseTrained; localDog.FeedingTimes = dog.FeedingTimes; localDog.FeedingIndividual = dog.FeedingIndividual; localDog.RatingStatistics = dog.RatingStatistics; localDog.Created = dog.Created; if (result.LastUpdate == null || result.LastUpdate < localDog.UpdatedAt) result.LastUpdate = localDog.UpdatedAt; if (!deleted) result.Updated.Add(localDog); else result.Deleted.Add(localDog); Repository.Update(localDog); } if (localDog == null) { localDog = new Dog() { Id = dog.Id, Version = dog.Version, UpdatedAt = dog.UpdatedAt, Deleted = dog.Deleted, DogRaceId = dog.DogRaceId, AppUserId = dog.AppUserId, Number = dog.Number, Name = dog.Name, Description = dog.Description, Photo = dog.Photo, BirthDate = dog.BirthDate, Sex = dog.Sex, Size = dog.Size, PullsTheLeash = dog.PullsTheLeash, WalksTheLeash = dog.WalksTheLeash, BasicCommands = dog.BasicCommands, Hunter = dog.Hunter, Eating = dog.Eating, Jumper = dog.Jumper, LikesAdults = dog.LikesAdults, LikesChildren = dog.LikesChildren, LikesConspecifics = dog.LikesConspecifics, IsTrustful = dog.IsTrustful, IsAggressiv = dog.IsAggressiv, IsLoneGunner = dog.IsLoneGunner, AggressionLevel = dog.AggressionLevel, RushesOutside = dog.RushesOutside, IsCalm = dog.IsCalm, IsPeeing = dog.IsPeeing, IsShy = dog.IsShy, IsGuard = dog.IsGuard, Intolerances = dog.Intolerances, Chiped = dog.Chiped, Sterilized = dog.Sterilized, MedicalInfo = dog.MedicalInfo, HouseTrained = dog.HouseTrained, FeedingTimes = dog.FeedingTimes, FeedingIndividual = dog.FeedingIndividual, RatingStatistics = dog.RatingStatistics, Created = dog.Created, }; Repository.Add(localDog); if (result.LastUpdate == null || result.LastUpdate < localDog.UpdatedAt) result.LastUpdate = localDog.UpdatedAt; result.Added.Add(localDog); } } if (result.HasChanges) { try { await CommitAsync().ConfigureAwait(false); } catch (Exception ex) { var err = ex.Message; } } return result; } #endregion } }