373 lines
17 KiB
C#
373 lines
17 KiB
C#
using gehGassi.Core.Interfaces;
|
|
using gehGassi.Domain.Walks;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using gehGassi.Domain.Common;
|
|
using gehGassi.Domain.Dogs;
|
|
using gehGassi.Domain.Ratings;
|
|
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using gehGassi.Common.Extensions;
|
|
|
|
namespace gehGassi.Core.Services
|
|
{
|
|
/// <summary>
|
|
/// Service der die Verwaltung von Ratings ermöglicht
|
|
/// </summary>
|
|
public class RatingService : ServiceBase<Rating>, IRatingService
|
|
{
|
|
private readonly IRepository<RatingWithNames> _namesRepository;
|
|
private readonly IRepository<RatingWithNamesEx> _namesExRepository;
|
|
private readonly IRepository<Dog> _dogsRepository;
|
|
private readonly IRepository<AppUser> _appUserRepository;
|
|
private readonly IRepository<Walk> _walksRepository;
|
|
|
|
/// <summary>
|
|
/// Erstellt eine Instanz
|
|
/// </summary>
|
|
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
|
|
public RatingService(IUnitOfWork unitOfWork) : base(unitOfWork)
|
|
{
|
|
_namesRepository = unitOfWork.GetRepository<RatingWithNames>();
|
|
_namesExRepository = unitOfWork.GetRepository<RatingWithNamesEx>();
|
|
_dogsRepository = unitOfWork.GetRepository<Dog>();
|
|
_appUserRepository = unitOfWork.GetRepository<AppUser>();
|
|
_walksRepository = unitOfWork.GetRepository<Walk>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Erstellen eines Ratings
|
|
/// </summary>
|
|
/// <returns>Rating</returns>
|
|
public Rating Create()
|
|
{
|
|
var rating = new Rating()
|
|
{
|
|
Id = Guid.NewGuid().ToString("N"),
|
|
Created = DateTimeOffset.UtcNow
|
|
};
|
|
return rating;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Erstellen eines Ratings
|
|
/// </summary>
|
|
/// <param name="fromId">Id der Rating Quelle</param>
|
|
/// <param name="fromType">Typ der Rating Quelle</param>
|
|
/// <param name="targetId">Id des Ziels des Ratings</param>
|
|
/// <param name="targetType">Typ des Rating Ziels</param>
|
|
/// <param name="points">Vergebene Punkte</param>
|
|
/// <param name="info">Zusatzinfo zum Rating</param>
|
|
/// <returns>Rating</returns>
|
|
public Rating Create(string fromId, AppUserType fromType, string targetId, RatingTarget targetType, decimal points, string info)
|
|
{
|
|
var rating = new Rating()
|
|
{
|
|
Id = Guid.NewGuid().ToString("N"),
|
|
FromId = fromId,
|
|
FromType = fromType,
|
|
TargetId = targetId,
|
|
TargetType = targetType,
|
|
Points = points,
|
|
Info = info,
|
|
Created = DateTimeOffset.UtcNow
|
|
};
|
|
return rating;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Liste der Ratings eines Benutzers zurück. Kann Owner und Walker sein
|
|
/// </summary>
|
|
/// <param name="appUserId">Id des AppUsers</param>
|
|
/// <param name="lastUpdate">Letztes Update</param>
|
|
/// <returns>Liste der öffentlichen Anfragen</returns>
|
|
public async Task<List<Rating>> GetForSyncAppAsync(string appUserId, DateTimeOffset? lastUpdate)
|
|
{
|
|
var query = Repository.Query(c => c.FromId == appUserId);
|
|
|
|
if (lastUpdate.HasValue)
|
|
query = query.Where(c => c.UpdatedAt > lastUpdate);
|
|
|
|
return await query.ToListAsync().ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt ein Rating basierend auf Abfragekriterien zurück
|
|
/// </summary>
|
|
/// <param name="fromId">Id der Rating Quelle</param>
|
|
/// <param name="fromType">Typ der Rating Quelle</param>
|
|
/// <param name="targetId">Id des Ziels des Ratings</param>
|
|
/// <param name="targetType">Typ des Rating Ziels</param>
|
|
/// <returns>Rating wenn gefunden, null sonst</returns>
|
|
public async Task<Rating> GetRatingExAsync(string fromId, AppUserType fromType, string targetId, RatingTarget targetType)
|
|
{
|
|
var rating = await Repository.FirstOrDefaultAsync(c => c.FromId == fromId && c.FromType == fromType && c.TargetId == targetId && c.TargetType == targetType).ConfigureAwait(false);
|
|
return rating;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Neu berechnen der Ratings-Statistics für ein Ziel
|
|
/// </summary>
|
|
/// <param name="targetId">Id des Ziels des Ratings</param>
|
|
/// <param name="targetType">Typ des Rating Ziels</param>
|
|
/// <returns>true wenn erfolgreich, false sonst</returns>
|
|
public async Task<bool> UpdateRatingStatisticsAsync(string targetId, RatingTarget targetType)
|
|
{
|
|
if (targetType == RatingTarget.Dog || targetType == RatingTarget.Owner || targetType == RatingTarget.Walker)
|
|
{
|
|
var totalRatings = await Repository.CountAsync(c => c.TargetId == targetId && c.Deleted == false).ConfigureAwait(false);
|
|
var totalPoints = (await Repository.FindAsync(c => c.TargetId == targetId && c.Deleted == false).ConfigureAwait(false)).Sum(c => c.Points);
|
|
var averageRating = 0m;
|
|
if (totalRatings > 0)
|
|
averageRating = totalPoints / totalRatings;
|
|
|
|
if (targetType == RatingTarget.Dog)
|
|
{
|
|
var dog = await _dogsRepository.FirstOrDefaultAsync(c => c.Id == targetId);
|
|
if (dog != null)
|
|
{
|
|
dog.RatingStatistics.TotalRatings = totalRatings;
|
|
dog.RatingStatistics.TotalPoints = totalPoints;
|
|
dog.RatingStatistics.AverageRating = averageRating;
|
|
dog.UpdatedAt = DateTimeOffset.UtcNow;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (targetType == RatingTarget.Owner)
|
|
{
|
|
var appUser = await _appUserRepository.FirstOrDefaultAsync(c => c.Id == targetId);
|
|
if (appUser != null)
|
|
{
|
|
appUser.RatingStatistics.TotalRatings = totalRatings;
|
|
appUser.RatingStatistics.TotalPoints = totalPoints;
|
|
appUser.RatingStatistics.AverageRating = averageRating;
|
|
appUser.UpdatedAt = DateTimeOffset.UtcNow;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (targetType == RatingTarget.Walker)
|
|
{
|
|
var appUser = await _appUserRepository.FirstOrDefaultAsync(c => c.Id == targetId);
|
|
if (appUser != null)
|
|
{
|
|
appUser.RatingStatisticsWalker.TotalRatings = totalRatings;
|
|
appUser.RatingStatisticsWalker.TotalPoints = totalPoints;
|
|
appUser.RatingStatisticsWalker.AverageRating = averageRating;
|
|
appUser.UpdatedAt = DateTimeOffset.UtcNow;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt an ob ein Bewerter ein Ziel bereits bewertet hat
|
|
/// </summary>
|
|
/// <param name="fromId">Id der Rating Quelle</param>
|
|
/// <param name="fromType">Typ der Rating Quelle</param>
|
|
/// <param name="targetId">Id des Ziels des Ratings</param>
|
|
/// <param name="targetType">Typ des Rating Ziels</param>
|
|
/// <returns>true wenn bereits bewertet, false sonst</returns>
|
|
public async Task<bool> HasRatedAsync(string fromId, AppUserType fromType, string targetId, RatingTarget targetType)
|
|
{
|
|
var rating = await Repository.FirstOrDefaultAsync(c => c.FromId == fromId && c.FromType == fromType && c.TargetId == targetId && c.TargetType == targetType).ConfigureAwait(false);
|
|
return rating != null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt zurück ob ein Bewerter ein Ziel bewerten darf.
|
|
/// z.B. ein Walker darf einen hund nur bewerten, wenn mindestens ein Walk mit dem hund stattgefunden hat.
|
|
/// Ein Owner darf einen Walker nur bewerten, wenn dieser mindestens einen Walk für den Owen gemacht hat usw.
|
|
/// </summary>
|
|
/// <param name="fromId">Id der Rating Quelle</param>
|
|
/// <param name="fromType">Typ der Rating Quelle</param>
|
|
/// <param name="targetId">Id des Ziels des Ratings</param>
|
|
/// <param name="targetType">Typ des Rating Ziels</param>
|
|
/// <returns>true wenn eine Bewertung stattfinden darf, false sonst</returns>
|
|
public async Task<bool> CanRateAsync(string fromId, AppUserType fromType, string targetId, RatingTarget targetType)
|
|
{
|
|
if (targetType == RatingTarget.Dog || targetType == RatingTarget.Owner || targetType == RatingTarget.Walker)
|
|
{
|
|
//1. Darf ein Walker einen Hund bewerten?
|
|
if ((fromType == AppUserType.DogWalker || fromType == AppUserType.Both) && targetType == RatingTarget.Dog)
|
|
{
|
|
var existingWalk = await _walksRepository.FirstOrDefaultAsync(c => c.DogsJson.Contains(targetId) && c.DogWalkerId == fromId && c.Completed.HasValue).ConfigureAwait(false);
|
|
return existingWalk != null;
|
|
}
|
|
|
|
//2. Darf ein Owner einen Walker bewerten?
|
|
if ((fromType == AppUserType.DogOwner || fromType == AppUserType.Both) && targetType == RatingTarget.Walker)
|
|
{
|
|
var existingWalk = await _walksRepository.FirstOrDefaultAsync(c => c.DogOwnerId == fromId && c.DogWalkerId == targetId && c.Completed.HasValue).ConfigureAwait(false);
|
|
return existingWalk != null;
|
|
}
|
|
|
|
//2. Darf ein Walker einen Owner bewerten?
|
|
if ((fromType == AppUserType.DogWalker || fromType == AppUserType.Both) && targetType == RatingTarget.Owner)
|
|
{
|
|
var existingWalk = await _walksRepository.FirstOrDefaultAsync(c => c.DogOwnerId == targetId && c.DogWalkerId == fromId && c.Completed.HasValue).ConfigureAwait(false);
|
|
return existingWalk != null;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Alle Ratings eines AppUsers und für einen AppUser löschen
|
|
/// </summary>
|
|
/// <param name="appUserId">Id des AppUsers</param>
|
|
/// <returns>Anzahl gelöscht</returns>
|
|
public async Task<List<Tuple<string, RatingTarget>>> DeleteAllForAppUserAsync(string appUserId)
|
|
{
|
|
var targetsToUpdate = new List<Tuple<string, RatingTarget>>();
|
|
|
|
//Zuerst werden alle Ratings gelöscht, die an den AppUser gestellt wurden
|
|
var ratings = await Repository.FindAsync(c => c.TargetId == appUserId).ConfigureAwait(false);
|
|
foreach (var rating in ratings)
|
|
{
|
|
Repository.Remove(rating);
|
|
}
|
|
//Nun alle suchen die von dem Benutzer kommen und diese löschen.
|
|
//Achutung: bei den Zielbenutzern muss dann die Statistik aktualisiert werden!
|
|
var ratingsFromUser = await Repository.FindAsync(c => c.FromId == appUserId).ConfigureAwait(false);
|
|
foreach (var rating in ratingsFromUser)
|
|
{
|
|
if (rating.TargetType == RatingTarget.Dog || rating.TargetType == RatingTarget.Owner || rating.TargetType == RatingTarget.Walker)
|
|
{
|
|
targetsToUpdate.Add(new Tuple<string, RatingTarget>(rating.TargetId, rating.TargetType));
|
|
}
|
|
|
|
Repository.Remove(rating);
|
|
}
|
|
|
|
return targetsToUpdate;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Liste von Ratings mit Namen aufgelöst zurück
|
|
/// </summary>
|
|
/// <param name="fromId">Id der Rating Quelle</param>
|
|
/// <param name="fromType">Typ der Rating Quelle</param>
|
|
/// <param name="targetId">Id des Ziels des Ratings</param>
|
|
/// <param name="targetType">Typ des Rating Ziels</param>
|
|
/// <param name="sortList">Optional: Liste der sortierung</param>
|
|
/// <param name="skip">Datensätze auslassen</param>
|
|
/// <param name="take">Datensätze nehmen</param>
|
|
/// <returns>Liste gefudene Walks</returns>
|
|
public async Task<(int total, List<RatingWithNames> list)> GetWithNamesAsync(string fromId, AppUserType? fromType, string targetId, RatingTarget? targetType, List<DynamicSortOrder> sortList, int skip, int take)
|
|
{
|
|
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM vw_RatingsWithNames", c => c.Deleted == false);
|
|
|
|
if (!string.IsNullOrWhiteSpace(fromId))
|
|
baseQuery = baseQuery.Where(c => c.FromId == fromId);
|
|
if (fromType.HasValue)
|
|
baseQuery = baseQuery.Where(c => c.FromType == fromType);
|
|
if (!string.IsNullOrWhiteSpace(targetId))
|
|
baseQuery = baseQuery.Where(c => c.TargetId == targetId);
|
|
if (targetType.HasValue)
|
|
baseQuery = baseQuery.Where(c => c.TargetType == targetType);
|
|
|
|
var total = await baseQuery.CountAsync().ConfigureAwait(false);
|
|
|
|
if (sortList.Any())
|
|
{
|
|
var isFirst = true;
|
|
var orderedBaseQuery = baseQuery.SpecialOrderBy(sortList.First().Property, sortList.First().SortDirection); ;
|
|
foreach (var sortOrder in sortList)
|
|
{
|
|
if (isFirst)
|
|
{
|
|
isFirst = false;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, sortOrder.SortDirection);
|
|
}
|
|
}
|
|
var list = await orderedBaseQuery.Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
|
|
return (total, list);
|
|
}
|
|
else
|
|
{
|
|
var list = await baseQuery.OrderByDescending(c => c.UpdatedAt).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
|
|
return (total, list);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Liste von Ratings mit Namen aufgelöst und mit lock und block Info zurück
|
|
/// </summary>
|
|
/// <param name="blockingAppUserId">Id des Benutzers der abfrägt und für den Blocking geprüft wird</param>
|
|
/// <param name="fromId">Id der Rating Quelle</param>
|
|
/// <param name="fromType">Typ der Rating Quelle</param>
|
|
/// <param name="targetId">Id des Ziels des Ratings</param>
|
|
/// <param name="targetType">Typ des Rating Ziels</param>
|
|
/// <param name="sortList">Optional: Liste der sortierung</param>
|
|
/// <param name="skip">Datensätze auslassen</param>
|
|
/// <param name="take">Datensätze nehmen</param>
|
|
/// <returns>Liste gefudene Walks</returns>
|
|
public async Task<(int total, List<RatingWithNamesEx> list)> GetWithNamesExAsync(string blockingAppUserId, string fromId, AppUserType? fromType, string targetId, RatingTarget? targetType, List<DynamicSortOrder> sortList, int skip, int take)
|
|
{
|
|
var baseQuery = _namesExRepository.QueryStoredProcedure("SELECT * FROM tv_RatingsWithNamesEx({0})", c => c.Deleted == false, blockingAppUserId);
|
|
|
|
if (!string.IsNullOrWhiteSpace(fromId))
|
|
baseQuery = baseQuery.Where(c => c.FromId == fromId);
|
|
if (fromType.HasValue)
|
|
baseQuery = baseQuery.Where(c => c.FromType == fromType);
|
|
if (!string.IsNullOrWhiteSpace(targetId))
|
|
baseQuery = baseQuery.Where(c => c.TargetId == targetId);
|
|
if (targetType.HasValue)
|
|
baseQuery = baseQuery.Where(c => c.TargetType == targetType);
|
|
|
|
var total = await baseQuery.CountAsync().ConfigureAwait(false);
|
|
|
|
if (sortList.Any())
|
|
{
|
|
var isFirst = true;
|
|
var orderedBaseQuery = baseQuery.SpecialOrderBy(sortList.First().Property, sortList.First().SortDirection); ;
|
|
foreach (var sortOrder in sortList)
|
|
{
|
|
if (isFirst)
|
|
{
|
|
isFirst = false;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, sortOrder.SortDirection);
|
|
}
|
|
}
|
|
var list = await orderedBaseQuery.Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
|
|
return (total, list);
|
|
}
|
|
else
|
|
{
|
|
var list = await baseQuery.OrderByDescending(c => c.UpdatedAt).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
|
|
return (total, list);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt ein Rating mit Namen aufgelöst zurück
|
|
/// </summary>
|
|
/// <param name="ratingId">Id des Ratings</param>
|
|
/// <returns>Rating oder null, wenn nicht gefunden</returns>
|
|
public async Task<RatingWithNames> GetWithNamesAsync(string ratingId)
|
|
{
|
|
return await _namesRepository.FirstOrDefaultAsync(c => c.Id == ratingId).ConfigureAwait(false);
|
|
}
|
|
}
|
|
}
|