586 lines
25 KiB
C#
586 lines
25 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.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<Rating>, IRatingService
|
|
{
|
|
private readonly ICommunicationService _communicationService;
|
|
private readonly ISyncInfoPullService _pullService;
|
|
private readonly ISyncInfoPushService _pushService;
|
|
private readonly ILocationService _locationService;
|
|
|
|
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>
|
|
/// <param name="locationService">Instanz eines ILocationService</param>
|
|
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<AppUser>();
|
|
}
|
|
|
|
public override Rating Get(object id, bool noTracking = true)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override async Task<Rating> GetAsync(object id, bool noTracking = true)
|
|
{
|
|
await Task.Delay(1);
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Erstellen eines Ratings
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public Rating Create()
|
|
{
|
|
var rating = new Rating()
|
|
{
|
|
Id = Guid.NewGuid().ToString("N"),
|
|
Created = DateTimeOffset.UtcNow
|
|
};
|
|
return rating;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt ein Rating basierend auf Abfragekriterien zurück
|
|
/// </summary>
|
|
/// <param name="fromId">Id des Bewerters</param>
|
|
/// <param name="fromType">Typ des Bewertes</param>
|
|
/// <param name="targetId">Id des Bewertungsziels</param>
|
|
/// <param name="targetType">Typ des Bewertungsziels</param>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>true wenn bereits bewertet, false sonst</returns>
|
|
public async Task<Rating> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt ein Rating basierend auf der ID zurück
|
|
/// </summary>
|
|
/// <param name="ratingId">Id des Ratings</param>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>CommunicationResult</returns>
|
|
public async Task<RatingWithNames> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hinzufügen eines Ratings
|
|
/// </summary>
|
|
/// <param name="rating">Rating</param>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>Angelegtes Rating</returns>
|
|
public async Task<Rating> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aktualisieren eines Ratings
|
|
/// </summary>
|
|
/// <param name="rating">Rating</param>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>true wenn erfolgreich, false sonst</returns>
|
|
public async Task<bool> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Echtes Löschen eines Ratings - wird derzeit nicht verwendet!
|
|
/// Bitte Rating auf deleted setzen und UPDATE
|
|
/// </summary>
|
|
/// <param name="ratingId">Id des Ratings</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 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wurde bereits eine Bewertung vorgenommen?
|
|
/// </summary>
|
|
/// <param name="fromId">Id des Bewerters</param>
|
|
/// <param name="fromType">Typ des Bewertes</param>
|
|
/// <param name="targetId">Id des Bewertungsziels</param>
|
|
/// <param name="targetType">Typ des Bewertungsziels</param>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>true wenn bereits bewertet, false sonst</returns>
|
|
public async Task<bool> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Kann eine Bewertung vorgenommen werden?
|
|
/// </summary>
|
|
/// <param name="fromId">Id des Bewerters</param>
|
|
/// <param name="fromType">Typ des Bewertes</param>
|
|
/// <param name="targetId">Id des Bewertungsziels</param>
|
|
/// <param name="targetType">Typ des Bewertungsziels</param>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>true wennmöglich, false sonst</returns>
|
|
public async Task<bool> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Abrufen einer Liste von Ratings
|
|
/// </summary>
|
|
/// <param name="fromId">Quelle Id</param>
|
|
/// <param name="fromType">Quelle Typ</param>
|
|
/// <param name="targetId">Ziel Id</param>
|
|
/// <param name="targetType">Ziel Typ</param>
|
|
/// <param name="sortOrders">Liste von Sortierangaben</param>
|
|
/// <param name="appMode">Aktueller appMode</param>
|
|
/// <param name="language">Gewünschte Sprache</param>
|
|
/// <param name="take">Wie viele Walks sollen abgerufen werden? -1 Wenn nicht anwenden.</param>
|
|
/// <param name="skip">Wie viele Walks sollen ausgelassen werden? -1 Wenn nicht anwenden.</param>
|
|
/// <param name="accessToken">Aktuelles Accesstoken</param>
|
|
/// <param name="token">CancellationToken</param>
|
|
/// <returns>ListCommunicationResult</returns>
|
|
public async Task<ListCommunicationResult<List<RatingWithNames>>> GetRatingsAsync(string fromId, AppUserType? fromType, string targetId, RatingTarget? targetType, List<DynamicSortOrder> sortOrders, string language, AppMode appMode, LocationDto locaton, int take, int skip, string accessToken, CancellationToken token)
|
|
{
|
|
var result = new ListCommunicationResult<List<RatingWithNames>>() { Value = new List<RatingWithNames>() };
|
|
|
|
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
|
|
|
|
/// <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<Rating>> PullAsync(string language, string accessToken, CancellationToken token)
|
|
{
|
|
var result = new SyncResult<Rating>();
|
|
|
|
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;
|
|
}
|
|
|
|
/// <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<Rating>> PushAsync(string accessToken, CancellationToken token)
|
|
{
|
|
var result = new SyncResult<Rating>();
|
|
|
|
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<Rating>(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
|
|
|
|
/// <summary>
|
|
/// Behandeln der Liste von Ratings wenn welche vom Online-Store geholt werden.
|
|
/// </summary>
|
|
/// <param name="ratings">Liste der Ratings</param>
|
|
/// <returns>Task</returns>
|
|
private async Task<SyncResult<Rating>> HandleChangesAsync(List<Rating> ratings)
|
|
{
|
|
var result = new SyncResult<Rating>();
|
|
|
|
//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
|
|
}
|
|
}
|