using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Common;
using gehGassi.Domain.Dogs;
using gehGassi.Domain.Ratings;
using gehGassi.Domain.Users;
using gehGassi.Dto.DogWalkers;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using NetTopologySuite.Geometries;
using NetTopologySuite;
using gehGassi.Common.Extensions;
using System.Collections;
using gehGassi.Domain.Messages;
using gehGassi.Dto.Common;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace gehGassi.Core.Services
{
///
/// Service der die Verwaltung von App-Usern realisiert
///
public class AppUserService : ServiceBase, IAppUserService
{
private readonly IRepository _profileRepository;
private readonly IRepository _certificateRepository;
private readonly IRepository _walkerRepository;
private readonly IRepository _blockRepository;
private readonly IRepository _blockWithNamesRepository;
///
/// Erstellt eine Instanz
///
/// Instanz eines IUnitOfWork
public AppUserService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_profileRepository = unitOfWork.GetRepository();
_certificateRepository = unitOfWork.GetRepository();
_walkerRepository = unitOfWork.GetRepository();
_blockRepository = unitOfWork.GetRepository();
_blockWithNamesRepository = unitOfWork.GetRepository();
}
///
/// Gibt eine gefilterte Liste von App-Usern zurück
///
/// Filterbegriff der in bestimmten Feldern gesucht wird
/// Sollen gelöschte inkludiert werden?
/// List betroffener App-User
public IQueryable Filter(string filter, bool includeDeleted)
{
if(includeDeleted)
return Repository.Query(c => c.FirstName.Contains(filter) || c.LastName.Contains(filter) || c.Contact.Email.Contains(filter));
else
return Repository.Query(c => c.Deleted == false && (c.FirstName.Contains(filter) || c.LastName.Contains(filter) || c.Contact.Email.Contains(filter)));
}
///
/// Erstellen eines App-Users
///
/// App-User
public AppUser Create()
{
var item = new AppUser()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow,
Contact = new ContactMin(),
Address = new Address(),
SocialMedia = new SocialMedia(),
RatingStatistics = new RatingStatistics(),
RatingStatisticsWalker = new RatingStatistics(),
Type = AppUserType.DogOwner
};
return item;
}
///
/// Gibt eine gefilterte Liste von App-Usern zurück. Sucht nur im Namen
///
/// Filterbegriff der in bestimmten Feldern gesucht wird
/// Sollen gelöschte inkludiert werden?
/// List betroffener App-User
public async Task> SearchAsync(string filter, bool includeDeleted)
{
if(includeDeleted)
return (await Repository.FindAsync(c => c.FirstName.Contains(filter) || c.LastName.Contains(filter) || c.Contact.Email.Contains(filter))).ToList();
else
return (await Repository.FindAsync(c => c.Deleted == false && (c.FirstName.Contains(filter) || c.LastName.Contains(filter) || c.Contact.Email.Contains(filter)))).ToList();
}
///
/// Gibt die nächste freie Nummer für einen App-User zurück
///
/// App-User-Nummer
public string GetNextNumber()
{
long number = 1;
var total = Repository.Count();
number = total + 1;
return $"AU-{DateTime.UtcNow.Year:0000}{DateTime.UtcNow.Month:00}-{number:0000000000}";
}
///
/// Gibt die Anzahl von App-UserN im System zurück
///
/// Sollen gelöschte inkludiert werden?
/// Anzahl App-User
public async Task CountAsync(bool includeDeleted)
{
if (includeDeleted)
return await Repository.CountAsync().ConfigureAwait(false);
else
return await Repository.CountAsync(c => c.Deleted == false).ConfigureAwait(false);
}
///
/// Gibt die Anzahl der AppUser im System zurück welche einem Typ entsprechen
///
/// Sollen gelöschte inkludiert werden?
/// Typ der gesucht wird
/// Optional: wenn angegeben, dann muss das Datum des letzten Updates größer oder gleich dem lastUpdate Datum sein
/// Anzahl AppUser
public async Task CountAsync(bool includeDeleted, AppUserType type, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => true);
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
if(type == AppUserType.DogWalker)
query = query.Where(c => c.Type == AppUserType.DogWalker);
else if (type == AppUserType.DogOwner)
query = query.Where(c => c.Type == AppUserType.DogOwner);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt >= lastUpdate.Value);
var count = await query.CountAsync().ConfigureAwait(false);
return count;
}
///
/// Entsperren von gesperrten App-Usern deren LockedUntil-Datum erreicht wurde.
///
/// Anzahl entsperrte App-User
public async Task AutoUnlockAsync()
{
var lockedAppUsers = await Repository.FindAsync(c => c.Locked && c.LockedUntil != null && c.LockedUntil <= DateTimeOffset.UtcNow);
var appUsers = lockedAppUsers.ToList();
foreach (var appUser in appUsers)
{
appUser.Locked = false;
appUser.LockedUntil = null;
appUser.LockedReason = string.Empty;
}
return appUsers.Count();
}
///
/// Gibt eine Liste von App-Usern zurück, die für die automatische Auszahlung in Frage kommen
///
/// List App-User
public async Task> GetForAutoPayoutAsync()
{
var users = await Repository.FindAsync(c => c.AutoPayout && !string.IsNullOrWhiteSpace(c.PaymentId) && !string.IsNullOrWhiteSpace(c.BankId) && c.TermsAccepted && c.PrivacyAccepted && c.PaymentTermsAccepted && c.KycPassed && !c.Deleted).ConfigureAwait(false);
return users.ToList();
}
///
/// Gibt zurück ob ein App-User gesperrt ist
///
/// Id des App-Users
/// true wenn gesperrt, false sonst
public async Task IsLockedAsync(string appUserId)
{
var appUser = await Repository.FirstOrDefaultAsync(c => c.Id == appUserId);
if(appUser != null && appUser.Deleted == false)
return appUser.Locked;
return false;
}
///
/// Gibt eine List von AppUsern zurück
///
/// skip
/// take
/// Typ der gesucht wird
/// Optional: wenn angegeben, dann muss das Datum des letzten Updates größer oder gleich dem lastUpdate Datum sein
/// Sollen gelöschte inkludiert werden?
/// Liste AppUser
public async Task> GetAllAsync(int skip, int take, AppUserType type, DateTimeOffset? lastUpdate, bool includeDeleted)
{
var query = Repository.Query(c => true);
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
if (type == AppUserType.DogWalker)
query = query.Where(c => c.Type == AppUserType.DogWalker);
else if (type == AppUserType.DogOwner)
query = query.Where(c => c.Type == AppUserType.DogOwner);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt >= lastUpdate.Value);
query = query.Skip(skip).Take(take);
var list = await query.ToListAsync().ConfigureAwait(false);
return list;
}
#region Profile
///
/// Erstellt ein DogWalker Profil wenn es noch nicht existiert
///
/// Id des App-Users
/// DogWalker Propfil
public async Task CreateWalkerProfileIfNotExistsAsync(string id)
{
var profile = await _profileRepository.GetAsync(id);
if (profile == null)
{
profile = new DogWalkerProfile()
{
Id = id,
UpdatedAt = DateTimeOffset.UtcNow,
Deleted = false,
Radius = 10,
About = string.Empty,
AllowedRequests = DogWalkRequestType.Walking,
PuppyAllowed = false,
DifficultAllowed = false,
AggressionLevel = DogAggressionLevel.Low,
AcceptedSize = DogSize.Small,
PriceWalk = 10,
PriceDay = 7,
PriceSitting = 40,
NotAvailable = false,
NotAvailableInfo = string.Empty
};
_profileRepository.Add(profile);
}
return profile;
}
///
/// Gibt ein Walker-Profil für einen App-User zurück
///
/// Id des App-Users
/// DogWalker Propfil
public async Task GetWalkerProfileAsync(string id)
{
var profile = await _profileRepository.GetAsync(id);
return profile;
}
///
/// Löschen eines Walker-Profils für einen App-User
///
/// Id des App-Users
/// DogWalker Propfil
public async Task RemoveWalkerProfileAsync(string id)
{
var profile = await _profileRepository.GetAsync(id);
if(profile != null)
_profileRepository.Remove(profile);
}
#endregion
#region DogWalkers
///
/// Gibt eine Liste Dogwalkern zurück - sortiert nach Distanz
///
/// Id des App-Users der ignoriert werden soll, falls ein DogOwner auch Walker ist
/// Optional: Breitengrad
/// Optional: Längengrad
/// Wie viele sollen max gewählt werden?
/// Liste passender Dogwalker
public async Task> GetWalkersForAppAsync(string ignoreId, double lat, double lng, int take)
{
//Lokalisierungsmodus --> Andere Abfrage weil wir die Distanz zu den Geokoordinaten brauchen
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var walkersQuery = _walkerRepository.QueryView(c => c.Deleted == false && c.Id != ignoreId);
walkersQuery = walkersQuery.OrderBy(c => c.Location.Distance(location));
var walkers = await walkersQuery.Take(take).ToListAsync().ConfigureAwait(false);
return walkers;
}
///
/// Gibt einen Dogwalker zurück
///
/// Id des DogWalkers
/// DogWalker oder null wenn nicht gefunden
public async Task GetWalkerAsync(string dogWalkerId)
{
var baseQuery = _walkerRepository.QueryView(c => c.Id == dogWalkerId);
return await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
}
///
/// Gibt eine Liste von Dogwalkern zurück
///
/// Abfrage-Objekt für Dogwalker - Beinhaltet alle Filter und auch Paging
/// Liste gefudene Walks
public async Task<(int total, List list)> GetWalkersAsync(DogWalkerQueryDto query)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(query.Location.Lng.Value, query.Location.Lat.Value));
//var baseQuery = _walkerRepository.QueryView(c => c.Deleted == false);
var baseQuery = _walkerRepository.QueryStoredProcedure("SELECT * FROM tv_DogWalkers({0})", c => c.Deleted == false && c.Locked == false, query.IgnoreId);
if (!string.IsNullOrWhiteSpace(query.IgnoreId))
baseQuery = baseQuery.Where(c => c.Id != query.IgnoreId);
if (!string.IsNullOrWhiteSpace(query.Name))
baseQuery = baseQuery.Where(c => c.FirstName.Contains(query.Name) || c.LastName.Contains(query.Name));
if (!string.IsNullOrWhiteSpace(query.City))
baseQuery = baseQuery.Where(c => c.Address_City.Contains(query.City) || c.Address_Zip.Contains(query.City));
if (!string.IsNullOrWhiteSpace(query.Country))
baseQuery = baseQuery.Where(c => c.Address_CountryCode == query.Country.ToUpperInvariant());
baseQuery = baseQuery.Where(c => c.RatingStatisticsWalker_AverageRating >= query.MinRating);
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (query.DynamicSortOrder.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(query.DynamicSortOrder.First().Property, (SortDirection)query.DynamicSortOrder.First().SortDirection); ;
foreach (var sortOrder in query.DynamicSortOrder)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, (SortDirection)sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.ThenBy(c => c.Location.Distance(location)).Skip(query.Skip).Take(query.Take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.Location.Distance(location)).Skip(query.Skip).Take(query.Take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
///
/// Gibt eine Liste von Dogwalkern zurück welche über eine Adresse verfügen und deren Standort vom angegebenen Standort nicht weiter als der definierte Radius entfernt sind
///
/// Id des Walkers der ignoriert werden soll, falls der hundebesitzer auch Walker ist
/// Location für die Abholung des Hundes - darf nicht weiter als definierter Radius entfernt sein
/// Liste gefundene Walker
public async Task> GetWalkersForNotificationAsync(string ignoreId, Point location)
{
var baseQuery = _walkerRepository.QueryView(c => c.Deleted == false && c.Id != ignoreId
&& !string.IsNullOrWhiteSpace(c.Address_AddressLine1)
&& !string.IsNullOrWhiteSpace(c.Address_Zip)
&& !string.IsNullOrWhiteSpace(c.Address_City)
&& c.Location != null);
baseQuery = baseQuery.Where(c => c.Location.IsWithinDistance(location, c.Radius * 1000));
return await baseQuery.ToListAsync().ConfigureAwait(false);
}
#endregion
#region Systemnachrichten
///
/// Abfrage des App-Users für eine Systemnachricht
///
/// Abfrageparameter
/// Liste der App-User
public async Task> GetForSystemMessageAsync(SystemMessageAppUserQuery query)
{
var baseQuery = Repository.QueryView(c => c.Deleted == false);
if (query.AppUserType != AppUserType.Both)
{
baseQuery = baseQuery.Where(c => c.Type == query.AppUserType || c.Type == AppUserType.Both);
}
if (!string.IsNullOrWhiteSpace(query.City))
baseQuery = baseQuery.Where(c => c.Address.City.Contains(query.City));
if (!string.IsNullOrWhiteSpace(query.Zip))
baseQuery = baseQuery.Where(c => c.Address.Zip.Contains(query.Zip));
if (!string.IsNullOrWhiteSpace(query.Country))
baseQuery = baseQuery.Where(c => c.Address.CountryCode == query.Country.ToUpperInvariant());
if (!string.IsNullOrWhiteSpace(query.State))
baseQuery = baseQuery.Where(c => c.Address.State == query.State.ToUpperInvariant());
if(query.VerifiedOnly)
baseQuery = baseQuery.Where(c => c.Verified);
if (query.Sex.HasValue && query.Sex.Value != Sex.Undefined)
baseQuery = baseQuery.Where(c => c.Sex == query.Sex.Value);
return await baseQuery.ToListAsync();
}
///
/// Gibt zurück wie viele App-User für eine Systemnachricht gefunden werden
///
/// Abfrageparameter
/// Anzahl der App-User
public async Task CountForSystemMessageAsync(SystemMessageAppUserQuery query)
{
var baseQuery = Repository.QueryView(c => c.Deleted == false);
if (query.AppUserType != AppUserType.Both)
{
baseQuery = baseQuery.Where(c => c.Type == query.AppUserType || c.Type == AppUserType.Both);
}
if (!string.IsNullOrWhiteSpace(query.City))
baseQuery = baseQuery.Where(c => c.Address.City.Contains(query.City));
if (!string.IsNullOrWhiteSpace(query.Zip))
baseQuery = baseQuery.Where(c => c.Address.Zip.Contains(query.Zip));
if (!string.IsNullOrWhiteSpace(query.Country))
baseQuery = baseQuery.Where(c => c.Address.CountryCode == query.Country.ToUpperInvariant());
if (!string.IsNullOrWhiteSpace(query.State))
baseQuery = baseQuery.Where(c => c.Address.State == query.State.ToUpperInvariant());
if (query.VerifiedOnly)
baseQuery = baseQuery.Where(c => c.Verified);
if (query.Sex.HasValue && query.Sex.Value != Sex.Undefined)
baseQuery = baseQuery.Where(c => c.Sex == query.Sex.Value);
return await baseQuery.CountAsync();
}
#endregion
#region Blocks
///
/// Erstellen einer Blockierung eines App-Users
///
/// AppUserBlock
public AppUserBlock CreateBlock()
{
var item = new AppUserBlock
{
Created = DateTimeOffset.UtcNow
};
return item;
}
///
/// Hinzufügen eines Blocks eine App-Users
///
/// AppUserBlock
public void AddBlock(AppUserBlock block)
{
_blockRepository.Add(block);
}
///
/// Blockiert einen App-User für einen anderen, wenn noch kein Block existiert
///
/// Id des App-Users der die Blockierung vornimmt
/// Id des App-Users der blockiert wird
/// Von wem erstellt?
/// Optional: Bis wann soll der App-User blockiert werden?
/// Optional: Verweis auf eine Meldung
/// Erstellte oder existierende Blockierung
public async Task BlockAppUserAsync(string blockingAppUserId, string blockedAppUserId, string createdBy, DateTimeOffset? blockUntil = null, long? reportId = null)
{
var block = await _blockRepository.FirstOrDefaultAsync(c => c.BlockingAppUserId == blockingAppUserId && c.BlockedAppUserId == blockedAppUserId).ConfigureAwait(false);
if (block == null)
{
block = CreateBlock();
block.BlockingAppUserId = blockingAppUserId;
block.BlockedAppUserId = blockedAppUserId;
block.CreatedBy = createdBy;
block.AppUserReportId = reportId;
block.BlockedUntil = null;
AddBlock(block);
}
return block;
}
///
/// Aufheben der Blockierung eines App-Users durch einen anderen, wenn vorhanden
///
/// Id des App-Users der die Blockierung vornimmt
/// Id des App-Users der blockiert wird
/// true wenn erfolgreich, false sonst
public async Task UnblockAppUserAsync(string blockingAppUserId, string blockedAppUserId)
{
var block = await _blockRepository.FirstOrDefaultAsync(c => c.BlockingAppUserId == blockingAppUserId && c.BlockedAppUserId == blockedAppUserId).ConfigureAwait(false);
if (block != null)
{
RemoveBlock(block);
return true;
}
return false;
}
///
/// Löschen einer Blockierung
///
/// AppUserBlock
public void RemoveBlock(AppUserBlock block)
{
_blockRepository.Remove(block);
}
///
/// Löschen einer Blockierung
///
/// Id der Blockierung
public async Task RemoveBlockByIdAsync(long id)
{
var block = await _blockRepository.FirstOrDefaultAsync(c => c.Id == id).ConfigureAwait(false);
if (block != null)
_blockRepository.Remove(block);
}
///
/// Entfernen von Blocks eines App-Users der blockiert hat
///
/// Id des AppUsers der blockiert hat
/// Task
public async Task RemoveBlocksByBlockingUserAsync(string id)
{
var blocks = await _blockRepository.FindAsync(c => c.BlockingAppUserId == id).ConfigureAwait(false);
foreach (var appUserBlock in blocks)
{
_blockRepository.Remove(appUserBlock);
}
}
///
/// Entfernen von Blocks eines App-Users der blockiert wurde
///
/// Id des AppUsers der blockiert wurde
/// Task
public async Task RemoveBlocksByBlockedUserAsync(string id)
{
var blocks = await _blockRepository.FindAsync(c => c.BlockedAppUserId == id).ConfigureAwait(false);
foreach (var appUserBlock in blocks)
{
_blockRepository.Remove(appUserBlock);
}
}
///
/// Löschen von abgelaufenen Blockierungen, welche ein Ablaufdatum haben
///
/// Datum das erreicht werden muss
/// Anzahl gelösche Blockierungen
public async Task RemoveExpiredBlocksAsync(DateTimeOffset date)
{
var blocks = (await _blockRepository.FindAsync(c => c.BlockedUntil.HasValue && c.BlockedUntil.Value <= date).ConfigureAwait(false)).ToList();
foreach (var appUserBlock in blocks)
{
_blockRepository.Remove(appUserBlock);
}
return blocks.Count;
}
///
/// Gibt zurück ob ein App-User von einem anderen blockiert wurde
///
/// Id des App-Users der blockiert hat
/// Id des App-Users der blockiert wurde
/// true wenn blockiert, false sonst
public async Task IsBlockedAsync(string blockingAppUserId, string blockedAppUserId)
{
return await _blockRepository.FirstOrDefaultAsync(c => c.BlockingAppUserId == blockingAppUserId && c.BlockedAppUserId == blockedAppUserId).ConfigureAwait(false) != null;
}
///
/// Gibt eine Liste von blockierten App-Usern für einen App-User zurück
///
/// Abfrageparameter
/// Liste von gesperrten App-Usern
public async Task<(int total, List list)> GetBlockedAppUsersAsync(BlockedQueryDto query)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(query.Location.Lng.Value, query.Location.Lat.Value));
//var baseQuery = _walkerRepository.QueryView(c => c.Deleted == false);
var baseQuery = _blockWithNamesRepository.QueryStoredProcedure("SELECT * FROM tv_AppUserBlocksWithNames({0})", c => c.Deleted == false && c.Locked == false, query.BlockingAppUserId);
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (query.DynamicSortOrder.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(query.DynamicSortOrder.First().Property, (SortDirection)query.DynamicSortOrder.First().SortDirection); ;
foreach (var sortOrder in query.DynamicSortOrder)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, (SortDirection)sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.ThenBy(c => c.Location.Distance(location)).Skip(query.Skip).Take(query.Take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.Location.Distance(location)).Skip(query.Skip).Take(query.Take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
///
/// Gibt eine Liste von AppUsern zurück welche als geöscht markiert sind
///
/// Liste AppUser
public async Task> GetDeletedAsync()
{
return (await Repository.FindAsync(c => c.Deleted).ConfigureAwait(false)).ToList();
}
///
/// Gibt eine Liste von AppUsern zurück deren Location nicht valide ist
///
/// Liste AppUser
public async Task> GetWithInvalidLocationAsync()
{
var appUsers = await Repository.FindAsync(c => (c.Location == null || c.Location.X == 0 && c.Location.Y == 0) && c.Deleted == false).ConfigureAwait(false);
return appUsers.ToList();
}
#endregion
}
}