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 } }