658 lines
29 KiB
C#

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
{
/// <summary>
/// Service der die Verwaltung von Hunden ermöglicht
/// </summary>
public class DogService : ServiceBase<Dog>, IDogService
{
private readonly ICommunicationService _communicationService;
private readonly ISyncInfoPullService _pullService;
private readonly ISyncInfoPushService _pushService;
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>
/// <param name="pushService">Instanz eines ISyncInfoPushService</param>
public DogService(IUnitOfWork unitOfWork, ICommunicationService communicationService, ISyncInfoPullService pullService, ISyncInfoPushService pushService) : base(unitOfWork)
{
_communicationService = communicationService;
_pullService = pullService;
_pushService = pushService;
_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 Dog 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<Dog> GetAsync(object id, bool noTracking = true)
{
return await Repository.SingleOrDefaultAsync(c => c.Id == id.ToString(), noTracking).ConfigureAwait(false);
}
/// <summary>
/// Erstellen eines Hundes
/// </summary>
/// <returns></returns>
public Dog Create(string appUserId)
{
var dog = new Dog()
{
Id = Guid.NewGuid().ToString("N"),
AppUserId = appUserId,
Created = DateTimeOffset.UtcNow
};
return dog;
}
/// <summary>
/// Gibt eine Liste der Hunden zurück.
/// Entweder von der DB oder vom Server
/// </summary>
/// <param name="appUserId">Id des AppBenutzers</param>
/// <param name="accessToken">Aktuelles Accesstoken</param>
/// <param name="token">CancellationToken</param>
/// <returns>Liste der Hunde</returns>
public async Task<List<Dog>> 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<Dog>();
}
/// <summary>
/// Gibt eine Liste der Hunden zurück.
/// Entweder von der DB oder vom Server
/// </summary>
/// <param name="appUserId">Id des AppBenutzers</param>
/// <param name="language">Gewünschte Sprache</param>
/// <param name="accessToken">Aktuelles Accesstoken</param>
/// <param name="token">CancellationToken</param>
/// <returns>Liste der Hunde</returns>
public async Task<List<DogMinInfo>> 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<DogMinInfo>();
}
/// <summary>
/// Gibt einen hund vom Server zurück
/// </summary>
/// <param name="dogId">Id des Hundes</param>
/// <param name="accessToken">Aktuelles Accesstoken</param>
/// <param name="token">CancellationToken</param>
/// <returns>Hund oder null, wenn nicht gfunden</returns>
public async Task<Dog> 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;
}
/// <summary>
/// Hinzufügen eines Hundes
/// </summary>
/// <param name="dog">Hund</param>
/// <param name="accessToken">Aktuelles Accesstoken</param>
/// <param name="token">CancellationToken</param>
/// <returns>Angelegter Hund oder null, wenn nicht anlegbar</returns>
public async Task<Dog> 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;
}
/// <summary>
/// Aktualisieren eines Hundes
/// </summary>
/// <param name="dog">Hund</param>
/// <param name="accessToken">Aktuelles Accesstoken</param>
/// <param name="token">CancellationToken</param>
/// <returns>Angelegter Hund</returns>
public async Task<bool> 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;
}
/// <summary>
/// Echtes Löschen eines Hundes - wird derzeit nicht verwendet!
/// Bitte Hund auf deleted setzen und UPDATE
/// </summary>
/// <param name="dogId">Id des Hundes</param>
/// <param name="appUserId">Id des AppBenutzers</param>
/// <param name="accessToken">Aktuelles Accesstoken</param>
/// <param name="token">CancellationToken</param>
/// <returns>Angelegter Hund</returns>
public async Task<bool> 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
/// <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<Dog>> PushAsync(string accessToken, CancellationToken token)
{
var result = new SyncResult<Dog>();
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<Dog>(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;
}
/// <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<Dog>> PullAsync(string language, string accessToken, CancellationToken token)
{
var result = new SyncResult<Dog>();
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
/// <summary>
/// Behandeln der Liste von Hunden wenn welche vom Online-Store geholt werden.
/// </summary>
/// <param name="dogs">Liste der Hunde</param>
/// <returns>Task</returns>
private async Task<SyncResult<Dog>> HandleChangesAsync(List<Dog> dogs)
{
var result = new SyncResult<Dog>();
//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
}
}