440 lines
19 KiB
C#
440 lines
19 KiB
C#
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.DogWalkers;
|
|
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.Messages;
|
|
using gehGassiApp.Domain.News;
|
|
using gehGassiApp.Domain.Users;
|
|
using gehGassiApp.Domain.Walkers;
|
|
|
|
namespace gehGassiApp.Core.Services
|
|
{
|
|
/// <summary>
|
|
/// Service der die Verfügbarkeit eines Walkers für Walks je wochentag ermöglicht
|
|
/// </summary>
|
|
public class WalkingTimeService : ServiceBase<WalkingTime>, IWalkingTimeService
|
|
{
|
|
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="syncInfoPullService">Instanz eines ISyncInfoPullService</param>
|
|
/// <param name="syncInfoPushService">Instanz eines ISyncInfoPushService</param>
|
|
public WalkingTimeService(IUnitOfWork unitOfWork, ICommunicationService communicationService, ISyncInfoPullService syncInfoPullService, ISyncInfoPushService syncInfoPushService) : base(unitOfWork)
|
|
{
|
|
_communicationService = communicationService;
|
|
_pullService = syncInfoPullService;
|
|
_pushService = syncInfoPushService;
|
|
|
|
_appUserRepository = unitOfWork.GetRepository<AppUser>();
|
|
}
|
|
|
|
public override WalkingTime Get(object id, bool noTracking = true)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override async Task<WalkingTime> GetAsync(object id, bool noTracking = true)
|
|
{
|
|
await Task.Delay(1);
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Liste aller WalkingTimes eines DogWalkers zurück
|
|
/// </summary>
|
|
/// <param name="dogWalkerId">Id des DogWalkers</param>
|
|
/// <param name="forceOnline">Unbedingt online versuchen</param>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>CommunicationResult</returns>
|
|
public async Task<CommunicationResult<List<WalkingTime>>> GetAllAsync(string dogWalkerId, bool forceOnline, string accessToken, CancellationToken token)
|
|
{
|
|
var result = new CommunicationResult<List<WalkingTime>>() { Value = new List<WalkingTime>() };
|
|
|
|
if (!forceOnline)
|
|
{
|
|
var walkingTimes = await Repository.FindAsync(c => c.DogWalkerId == dogWalkerId && c.Deleted == false).ConfigureAwait(false);
|
|
if (walkingTimes != null && walkingTimes.Any())
|
|
{
|
|
result.Value = walkingTimes.OrderBy(c => c.Day).ThenBy(c => c.Start).ToList();
|
|
result.Success = true;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
var isConnected = await _communicationService.IsConnected();
|
|
|
|
if (isConnected)
|
|
{
|
|
var walkingTimesResult = await _communicationService.GetAllWalkingTimesAsync(dogWalkerId, accessToken, token);
|
|
if (walkingTimesResult.Success)
|
|
{
|
|
var changeResult = await HandleChangesAsync(walkingTimesResult.Value);
|
|
if (changeResult.LastUpdate != null)
|
|
await _pullService.AddOrUpddateAsync(nameof(WalkingTime), changeResult.LastUpdate.Value).ConfigureAwait(false);
|
|
return walkingTimesResult;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.Success = false;
|
|
result.ErrorCode = CommunicationErrors.ServerNoConnection;
|
|
result.ErrorMessage = Errors.Server_NoConnection;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hinzufügen einer WalkingTime
|
|
/// </summary>
|
|
/// <param name="dogWalkerId">Id des Dogwalkers</param>
|
|
/// <param name="day">Wochentag</param>
|
|
/// <param name="start">Startzeit</param>
|
|
/// <param name="end">Endzeit</param>
|
|
/// <param name="enabled">Aktiv</param>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>WalkingTime</returns>
|
|
public async Task<WalkingTime> AddAsync(string dogWalkerId, DayOfWeek day, TimeSpan start, TimeSpan end, bool enabled, string accessToken, CancellationToken token)
|
|
{
|
|
var walkingTimeId = Guid.NewGuid().ToString("N");
|
|
var walkingTime = new WalkingTime()
|
|
{
|
|
Id = walkingTimeId,
|
|
UpdatedAt = DateTimeOffset.UtcNow,
|
|
DogWalkerId = dogWalkerId,
|
|
Day = day,
|
|
Start = start,
|
|
End = end,
|
|
Enabled = enabled,
|
|
Deleted = false
|
|
};
|
|
|
|
Repository.Add(walkingTime);
|
|
await CommitAsync();
|
|
|
|
//Jetzt die WalkingTime an der Server übertragen oder ein Delta erstellen
|
|
var isConnected = await _communicationService.IsConnected();
|
|
var createDelta = true;
|
|
if (isConnected)
|
|
{
|
|
var walkingTimeDto = walkingTime.ToDto();
|
|
var createResult = await _communicationService.CreateWalkingTimeAsync(walkingTimeDto, accessToken, token);
|
|
if (createResult.Success)
|
|
createDelta = false;
|
|
}
|
|
if (createDelta)
|
|
{
|
|
await _pushService.AddAsync(nameof(WalkingTime), walkingTime.Id, SyncOperation.Create, walkingTime).ConfigureAwait(false);
|
|
}
|
|
|
|
return walkingTime;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aktualisieren einer WalkingTime
|
|
/// </summary>
|
|
/// <param name="walkingTime">WalkingTime</param>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>True wenn erfolgreich, false sonst</returns>
|
|
public async Task<bool> UpdateAsync(WalkingTime walkingTime, string accessToken, CancellationToken token)
|
|
{
|
|
var localWalkingTime = await Repository.FirstOrDefaultAsync(c => c.Id == walkingTime.Id, false).ConfigureAwait(false);
|
|
if (localWalkingTime != null && !localWalkingTime.Deleted)
|
|
{
|
|
bool hasChanges = localWalkingTime.Day != walkingTime.Day || localWalkingTime.Start != walkingTime.Start || localWalkingTime.End != walkingTime.End || localWalkingTime.Enabled != walkingTime.Enabled;
|
|
|
|
if (hasChanges)
|
|
{
|
|
localWalkingTime.Day = walkingTime.Day;
|
|
localWalkingTime.Start = walkingTime.Start;
|
|
localWalkingTime.End = walkingTime.End;
|
|
localWalkingTime.Enabled = walkingTime.Enabled;
|
|
localWalkingTime.UpdatedAt = DateTimeOffset.UtcNow;
|
|
|
|
Repository.Update(localWalkingTime);
|
|
await CommitAsync().ConfigureAwait(false);
|
|
|
|
var isConnected = await _communicationService.IsConnected();
|
|
var createDelta = true;
|
|
if (isConnected)
|
|
{
|
|
var walkingTimeDto = localWalkingTime.ToDto();
|
|
var updateResult = await _communicationService.UpdateWalkingTimeAsync(walkingTimeDto, accessToken, token);
|
|
if (updateResult.Success && updateResult.Value)
|
|
createDelta = false;
|
|
}
|
|
|
|
if (createDelta)
|
|
{
|
|
await _pushService.AddAsync(nameof(WalkingTime), localWalkingTime.Id, SyncOperation.Edit, localWalkingTime).ConfigureAwait(false);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Echtes Löschen einer WalkingTime
|
|
/// </summary>
|
|
/// <param name="walkingTimeId">Id der WalkingTime</param>
|
|
/// <param name="appUserId">Id des AppBenutzers</param>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>True wenn erfolgreich, false sonst</returns>
|
|
public async Task<bool> DeleteAsync(string walkingTimeId, string appUserId, string accessToken, CancellationToken token)
|
|
{
|
|
var localWalkingTime = await Repository.FirstOrDefaultAsync(c => c.Id == walkingTimeId, false).ConfigureAwait(false);
|
|
if (localWalkingTime != null && localWalkingTime.DogWalkerId == appUserId)
|
|
{
|
|
Repository.Remove(localWalkingTime);
|
|
await CommitAsync().ConfigureAwait(false);
|
|
|
|
var walkingTimeDto = localWalkingTime.ToDto();
|
|
var isConnected = await _communicationService.IsConnected();
|
|
var createDelta = true;
|
|
if (isConnected)
|
|
{
|
|
var deleteResult = await _communicationService.DeleteWalkingTimeAsync(walkingTimeDto, appUserId, accessToken, token);
|
|
if (deleteResult.Success && deleteResult.Value)
|
|
createDelta = false;
|
|
}
|
|
|
|
if (createDelta)
|
|
{
|
|
await _pushService.AddAsync(nameof(WalkingTime), localWalkingTime.Id, SyncOperation.Delete, localWalkingTime).ConfigureAwait(false);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#region Pull-Push Implementation
|
|
|
|
/// <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<WalkingTime>> PullAsync(string language, string accessToken, CancellationToken token)
|
|
{
|
|
var result = new SyncResult<WalkingTime>();
|
|
|
|
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(WalkingTime));
|
|
if (lastSyncInfo != null)
|
|
lastUpdate = lastSyncInfo.LastUpdate;
|
|
|
|
var requestResult = await _communicationService.GetWalkingTimesForSyncAsync(user.Id, lastUpdate, accessToken, token);
|
|
if (requestResult.Success)
|
|
{
|
|
result = await HandleChangesAsync(requestResult.Value);
|
|
if (result.LastUpdate != null)
|
|
await _pullService.AddOrUpddateAsync(nameof(WalkingTime), result.LastUpdate.Value).ConfigureAwait(false);
|
|
}
|
|
|
|
result.LastUpdate ??= lastUpdate;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <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<WalkingTime>> PushAsync(string accessToken, CancellationToken token)
|
|
{
|
|
var result = new SyncResult<WalkingTime>();
|
|
|
|
if (await _pushService.HasOpenAsync(nameof(WalkingTime)) > 0)
|
|
{
|
|
var deltas = await _pushService.GetAllAsync(nameof(WalkingTime));
|
|
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<WalkingTime>(syncInfoPush.Value, new JsonSerializerOptions(JsonSerializerDefaults.Web));
|
|
|
|
if (syncInfoPush.Operation == SyncOperation.Create)
|
|
{
|
|
var createDto = request.ToDto();
|
|
var createResult = await _communicationService.CreateWalkingTimeAsync(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.UpdateWalkingTimeAsync(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.DeleteWalkingTimeAsync(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
|
|
|
|
/// <summary>
|
|
/// Behandeln der Liste von öffentlichern Anfragen wenn welche vom Online-Store geholt werden.
|
|
/// </summary>
|
|
/// <param name="walkingTimes">Liste der anfragen</param>
|
|
/// <returns>Task</returns>
|
|
private async Task<SyncResult<WalkingTime>> HandleChangesAsync(List<WalkingTime> walkingTimes)
|
|
{
|
|
var result = new SyncResult<WalkingTime>();
|
|
|
|
//Je Rasse durchgehen ob was gemacht werden soll
|
|
foreach (var walkingTime in walkingTimes)
|
|
{
|
|
var localWalkingTime = await Repository.FirstOrDefaultAsync(c => c.Id == walkingTime.Id, false);
|
|
if (localWalkingTime != null && localWalkingTime.UpdatedAt < walkingTime.UpdatedAt)
|
|
{
|
|
if (localWalkingTime.UpdatedAt >= walkingTime.UpdatedAt)
|
|
{
|
|
if (result.LastUpdate == null || result.LastUpdate < localWalkingTime.UpdatedAt)
|
|
result.LastUpdate = localWalkingTime.UpdatedAt;
|
|
continue;
|
|
}
|
|
|
|
var deleted = localWalkingTime.Deleted == false && walkingTime.Deleted;
|
|
|
|
localWalkingTime.Version = walkingTime.Version;
|
|
localWalkingTime.UpdatedAt = walkingTime.UpdatedAt;
|
|
localWalkingTime.Deleted = walkingTime.Deleted;
|
|
|
|
localWalkingTime.DogWalkerId = walkingTime.DogWalkerId;
|
|
localWalkingTime.Day = walkingTime.Day;
|
|
localWalkingTime.Start = walkingTime.Start;
|
|
localWalkingTime.End = walkingTime.End;
|
|
localWalkingTime.Enabled = walkingTime.Enabled;
|
|
|
|
if (result.LastUpdate == null || result.LastUpdate < localWalkingTime.UpdatedAt)
|
|
result.LastUpdate = localWalkingTime.UpdatedAt;
|
|
if (!deleted)
|
|
result.Updated.Add(localWalkingTime);
|
|
else
|
|
result.Deleted.Add(localWalkingTime);
|
|
Repository.Update(localWalkingTime);
|
|
}
|
|
|
|
if (localWalkingTime == null)
|
|
{
|
|
var localWalkingTimeToAdd = new WalkingTime()
|
|
{
|
|
Id = walkingTime.Id,
|
|
Version = walkingTime.Version,
|
|
UpdatedAt = walkingTime.UpdatedAt,
|
|
Deleted = walkingTime.Deleted,
|
|
|
|
DogWalkerId = walkingTime.DogWalkerId,
|
|
Day = walkingTime.Day,
|
|
Start = walkingTime.Start,
|
|
End = walkingTime.End,
|
|
Enabled = walkingTime.Enabled
|
|
};
|
|
|
|
Repository.Add(localWalkingTimeToAdd);
|
|
|
|
if (result.LastUpdate == null || result.LastUpdate < localWalkingTimeToAdd.UpdatedAt)
|
|
result.LastUpdate = localWalkingTimeToAdd.UpdatedAt;
|
|
result.Added.Add(localWalkingTimeToAdd);
|
|
}
|
|
}
|
|
|
|
if (result.HasChanges)
|
|
{
|
|
try
|
|
{
|
|
await CommitAsync().ConfigureAwait(false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
var err = ex.Message;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|