using gehGassi.Core.Interfaces;
using gehGassi.Domain.Advertisements;
using gehGassi.Domain.Common;
using gehGassi.Domain.Walks;
using Microsoft.EntityFrameworkCore;
using NetTopologySuite.Geometries;
using NetTopologySuite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Common.Extensions;
namespace gehGassi.Core.Services
{
///
/// Service der die Verwaltung von öffentlichen Anfragen ermöglicht
///
public class PublicWalkRequestService : ServiceBase, IPublicWalkRequestService
{
private readonly IRepository _namesRepository;
private readonly IRepository _namesWithStatusRepository;
private readonly IRepository _responsesRepository;
private readonly IRepository _notificationsRepository;
///
/// Erstellt eine Instanz
///
/// Instanz eines IUnitOfWork
public PublicWalkRequestService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository();
_namesWithStatusRepository = unitOfWork.GetRepository();
_responsesRepository = unitOfWork.GetRepository();
_notificationsRepository = unitOfWork.GetRepository();
}
///
/// Erstellen einer öffentlichen Anfrage
///
///
public PublicWalkRequest Create()
{
var request = new PublicWalkRequest
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return request;
}
///
/// Gibt eine Liste der öffentlichen Anfragen eines Benutzers zurück
///
/// Id des AppUsers
/// Letztes Update
/// Liste der öffentlichen Anfragen
public async Task> GetForSyncAppAsync(string appUserId, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => c.DogOwnerId == appUserId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
///
/// Gibt die anzahl öffentlicher Anfragen eines AppUsers zurück
///
/// AppUserId
/// Anzahl öffentliche Anfragen
public async Task CountAsync(string appUserId)
{
var items = await Repository.CountAsync(c => c.DogOwnerId == appUserId);
return items;
}
///
/// Abfrage der öffentlichen Anfragen eines AppUsers "Latest"
///
/// Id des AppUsers
/// Wie viele Datensätze
/// Liste der öffentlichen Anfragen
public async Task> GetLatestAsync(string appUserId, int take)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNames()", c => c.DogOwnerId == appUserId && c.Deleted == false);
var requests = await baseQuery.OrderByDescending(c => c.UpdatedAt).Take(take).ToListAsync();
return requests;
}
///
/// Gibt eine Liste von eigenen öffentlichen Anfragen für die App zurück.
///
/// App-User Id die ignoriert werden soll (dogOwnerId)
/// Optional: Id des AppUsers der die Anfragen erstellt hat
/// Optional: Id des DogWalkers
/// Gesuchert Status. Alle wenn NULL
/// Optional: Liste der sortierung
/// Optional: Breitengrad
/// Optional: Längengrad
/// Datensätze auslassen
/// Datensätze nehmen
/// Liste passender Anfragen
public async Task<(int total, List list)> GetAsync(string ignoreId, string dogOwnerId, string dogWalkerId, PublicWalkRequestStatus? status, List sortList, double lat, double lng, int skip, int take)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNames()", c => c.Deleted == false);
if (status.HasValue)
baseQuery = baseQuery.Where(c => c.Status == status);
if (!string.IsNullOrEmpty(ignoreId))
baseQuery = baseQuery.Where(c => c.DogOwnerId != ignoreId);
if (!string.IsNullOrEmpty(dogOwnerId))
baseQuery = baseQuery.Where(c => c.DogOwnerId == dogOwnerId);
if (!string.IsNullOrEmpty(dogWalkerId))
baseQuery = baseQuery.Where(c => c.DogWalkerId == dogWalkerId);
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
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.ThenBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
///
/// Gibt eine Liste von eigenen öffentlichen Anfragen für die App zurück.
///
/// App-User Id die ignoriert werden soll (dogOwnerId)
/// Optional: Id des AppUsers der die Anfragen erstellt hat
/// Optional: Id des DogWalkers
/// Gesuchert Status. Alle wenn NULL
/// Mindest-Preis
/// Optional: Liste der sortierung
/// Optional: Breitengrad
/// Optional: Längengrad
/// Datensätze auslassen
/// Datensätze nehmen
/// Maximaler Radius
/// Typ der öffentlichen Anfrage, Alle wenn NULL
/// Liste passender Anfragen
public async Task<(int total, List list)> GetAsync(string ignoreId, string dogOwnerId, string dogWalkerId, PublicWalkRequestStatus? status, double maxRadius, PublicWalkRequestType? type, decimal minPrice, List sortList, double lat, double lng, int skip, int take)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNames()", c => c.Deleted == false);
if (status.HasValue)
baseQuery = baseQuery.Where(c => c.Status == status);
if (!string.IsNullOrEmpty(ignoreId))
baseQuery = baseQuery.Where(c => c.DogOwnerId != ignoreId);
if (!string.IsNullOrEmpty(dogOwnerId))
baseQuery = baseQuery.Where(c => c.DogOwnerId == dogOwnerId);
if (!string.IsNullOrEmpty(dogWalkerId))
baseQuery = baseQuery.Where(c => c.DogWalkerId == dogWalkerId);
if(type.HasValue)
baseQuery = baseQuery.Where(c => c.RequestType == type);
if(minPrice > 0)
baseQuery = baseQuery.Where(c => c.Price >= minPrice);
baseQuery = baseQuery.Where(c => c.Location.IsWithinDistance(location, maxRadius * 1000));
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.ThenBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
///
/// Gibt eine Liste von eigenen öffentlichen Anfragen für die App zurück. Mit dem Antwort-Status für einen DogWalker
///
/// App-User Id die ignoriert werden soll (dogOwnerId)
/// Optional: Id des AppUsers der die Anfragen erstellt hat
/// Optional: Id des DogWalkers
/// Gesuchert Status. Alle wenn NULL
/// es muss eine Antwort vorhanden sein
/// Optional: Liste der sortierung
/// Sollten blockierte unnd/oder gesperrte Benutzer in die Liste eingefügt werden?
/// Optional: Breitengrad
/// Optional: Längengrad
/// Datensätze auslassen
/// Datensätze nehmen
/// Id des Dogwalkers für welchen die Liste aufbereitet werden soll
/// Gesuchter Status oder aller wenn null
/// Liste passender Anfragen
public async Task<(int total, List list)> GetWithResponseStatusAsync(string ignoreId, string dogOwnerId, string dogWalkerId, PublicWalkRequestStatus? status, string targetDogWalkerId, PublicWalkResponseStatus? responseStatus, bool mustHaveResponse, List sortList, double lat, double lng, bool includeBlockedAndLocked, int skip, int take)
{
var baseQuery = _namesWithStatusRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNamesAndResponseStatus({0})", c => c.Deleted == false, targetDogWalkerId);
if (!includeBlockedAndLocked)
baseQuery = baseQuery.Where(c => c.DogOwnerBlocked == false && c.DogOwnerLocked == false);
if (status.HasValue)
baseQuery = baseQuery.Where(c => c.Status == status);
if (!string.IsNullOrEmpty(ignoreId))
baseQuery = baseQuery.Where(c => c.DogOwnerId != ignoreId);
if (!string.IsNullOrEmpty(dogOwnerId))
baseQuery = baseQuery.Where(c => c.DogOwnerId == dogOwnerId);
if (!string.IsNullOrEmpty(dogWalkerId))
baseQuery = baseQuery.Where(c => c.DogWalkerId == dogWalkerId);
if (responseStatus.HasValue)
baseQuery = baseQuery.Where(c => c.ResponseStatus == responseStatus || c.ResponseStatus == null);
if (mustHaveResponse)
baseQuery = baseQuery.Where(c => c.ResponseStatus != null);
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
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.ThenBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
///
/// Gibt eine Liste von eigenen öffentlichen Anfragen für die App zurück. Mit dem Antwort-Status für einen DogWalker
///
/// App-User Id die ignoriert werden soll (dogOwnerId)
/// Optional: Id des AppUsers der die Anfragen erstellt hat
/// Optional: Id des DogWalkers
/// Gesuchert Status. Alle wenn null
/// Suchtext für Suche in Hundename, OwnerName oder Stadt
/// es muss eine Antwort vorhanden sein
/// Optional: Liste der sortierung
/// Optional: Breitengrad
/// Optional: Längengrad
/// Sollen blockierte unnd/oder gesperrte Benutzer in die Liste eingefügt werden?
/// Datensätze auslassen
/// Datensätze nehmen
/// Id des Dogwalkers für welchen die Liste aufbereitet werden soll
/// Gesuchter Status oder alle wenn null
/// Maximaler Radius
/// Typ der öffentlichen Anfrage, oder alle wenn NULL
/// Mindest-Preis
/// Liste passender Anfragen
public async Task<(int total, List list)> GetWithResponseStatusAsync(string ignoreId, string dogOwnerId, string dogWalkerId, PublicWalkRequestStatus? status, string targetDogWalkerId, PublicWalkResponseStatus? responseStatus, double maxRadius, PublicWalkRequestType? type, decimal minPrice, bool mustHaveResponse, string searchText, List sortList, double lat, double lng, bool includeBlockedAndLocked, int skip, int take)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var baseQuery = _namesWithStatusRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNamesAndResponseStatus({0})", c => c.Deleted == false, targetDogWalkerId);
if (!includeBlockedAndLocked)
baseQuery = baseQuery.Where(c => c.DogOwnerBlocked == false && c.DogOwnerLocked == false);
if (status.HasValue)
baseQuery = baseQuery.Where(c => c.Status == status);
if (!string.IsNullOrEmpty(ignoreId))
baseQuery = baseQuery.Where(c => c.DogOwnerId != ignoreId);
if (!string.IsNullOrEmpty(dogOwnerId))
baseQuery = baseQuery.Where(c => c.DogOwnerId == dogOwnerId);
if (!string.IsNullOrEmpty(dogWalkerId))
baseQuery = baseQuery.Where(c => c.DogWalkerId == dogWalkerId);
if (type.HasValue)
baseQuery = baseQuery.Where(c => c.RequestType == type);
if (minPrice > 0)
baseQuery = baseQuery.Where(c => c.Price >= minPrice);
if (responseStatus.HasValue)
baseQuery = baseQuery.Where(c => c.ResponseStatus == responseStatus || c.ResponseStatus == null);
if (mustHaveResponse)
baseQuery = baseQuery.Where(c => c.ResponseStatus != null);
if (!string.IsNullOrWhiteSpace(searchText))
baseQuery = baseQuery.Where(c => c.DogName.Contains(searchText) || c.DogsJson.Contains(searchText) || c.DogOwnerName.Contains(searchText) || c.PickupAddress_City.Contains(searchText));
baseQuery = baseQuery.Where(c => c.Location.IsWithinDistance(location, maxRadius * 1000));
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.ThenBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderBy(c => c.StartDate).ThenBy(c => c.Location.Distance(location)).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
///
/// Gibt eine öffentliche Ausschreibung mit Namen aufgelöst zurück
///
/// ID der Ausschreibung
/// öffentliche Ausschreibung oder null, wenn nicht gefunden
public async Task GetWithNamesAsync(string id)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNames()", c => c.Id == id);
return await baseQuery.FirstOrDefaultAsync().ConfigureAwait(false);
}
///
/// Setzt öffntliche Anfragen auf abgelaufen, wenn diese nicht angenommen oder storniert wurden
///
/// Datum für Prüfung
/// Anzahl timed out
public async Task SetTimedOutAsync(DateTimeOffset date)
{
var requests = await Repository.FindAsync(c => c.ExpirationDate <= date && c.Deleted == false && c.Status == PublicWalkRequestStatus.Open).ConfigureAwait(false);
foreach (var request in requests)
{
request.Status = PublicWalkRequestStatus.TimedOut;
request.UpdatedAt = DateTimeOffset.UtcNow;
var responses = await _responsesRepository.FindAsync(c => c.PublicWalkRequestId == request.Id).ConfigureAwait(false);
foreach (var response in responses)
{
response.Status = PublicWalkResponseStatus.Closed;
response.UpdatedAt = DateTimeOffset.UtcNow;
}
}
return requests.Count();
}
///
/// Zurücksetzen einer öffentlichen Anfrage
///
/// Id der öffentlichen Anfrage
/// true wenn erfolgreich, false sonst
public async Task ResetAsync(string id)
{
var request = await Repository.FirstOrDefaultAsync(c => c.Id == id);
if (request != null && request.Deleted == false && request.Status == PublicWalkRequestStatus.Placed)
{
request.Status = PublicWalkRequestStatus.Open;
request.PlacedDate = null;
request.PublicWalkResponseId = string.Empty;
request.DogWalkerId = string.Empty;
request.UpdatedAt = DateTimeOffset.UtcNow;
return true;
}
return false;
}
///
/// Gibt eine Liste der offenen Anfragen für einen Hundebesitzer in einem gewählten Status zurück
///
/// Id des Hundebesitzers
/// Gesuchter Status
///
public async Task> GetByStatusAsync(string dogOwnerId, PublicWalkRequestStatus status)
{
var requests = await Repository.FindAsync(c => c.DogOwnerId == dogOwnerId && c.Status == status && c.Deleted == false).ConfigureAwait(false);
return requests.ToList();
}
///
/// Gibt eine gefilterte Liste von öffentlichen Anfragen zurück
///
/// Filterbegriff der in bestimmten Feldern gesucht wird
/// Sollen gelöschte inkludiert werden?
/// Liste betroffener öffentlicher Anfragen
public IQueryable FilterPublicWalkRequestsWithNames(string filter, bool includeDeleted)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_PublicWalkRequestWithNames()", c => c.DogOwnerName.Contains(filter) || c.PickupAddress_City.Contains(filter));
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
return baseQuery;
}
#region Notifications
///
/// Erstellt einen Eintrag für die Benachrichtigung einer neuen öffentlichen Anfrage
///
/// Id der öffentlichen Anfrage
/// Id des Hundebesitzers - AppUserId
/// Abholadresse
/// GEO-Koordinaten
/// PublicWalkRequestNotification
public PublicWalkRequestNotification CreateNotification(string publicWalkRequestId, string dogOwnerId, Address pickupAddress, Point location)
{
var item = new PublicWalkRequestNotification
{
PublicWalkRequestId = publicWalkRequestId,
DogOwnerId = dogOwnerId,
SentDate = null,
SentCount = 0,
HasBeenSent = false,
Created = DateTimeOffset.UtcNow,
PickupAddress =
{
AddressLine1 = pickupAddress.AddressLine1,
AddressLine2 = pickupAddress.AddressLine2,
Zip = pickupAddress.Zip,
City = pickupAddress.City,
CountryCode = pickupAddress.CountryCode,
State = pickupAddress.State
},
Location = location
};
return item;
}
///
/// Hinzufügen einer Benachrichtigung
///
/// Benachrichtigungs-Objekt
public void AddNotification(PublicWalkRequestNotification publicWalkRequestNotification)
{
_notificationsRepository.Add(publicWalkRequestNotification);
}
///
/// Gibt eine Liste von zu sendenden Benachrichtigungen zurück
///
/// Liste zu sendende Benachrichtigungen
public async Task> GetNotificationsToSendAsync()
{
var notifications = await _notificationsRepository.FindAsync(c => c.HasBeenSent == false && c.SentDate == null).ConfigureAwait(false);
return notifications.ToList();
}
///
/// Zählt wie viele öffenltiche Anfragen in einem bestimmten Zeitraum durch einen Benutzer erstellt wurden
///
/// Start-Datum
/// End-Datum
/// Id des Hundebesitzers
/// Max Status der Ausschreibung
/// Anzahl öffentliche Anfragen
public async Task CountByCreationDateAsync(DateTimeOffset startDate, DateTimeOffset endDate, string dogOwnerId, PublicWalkRequestStatus maxStatus)
{
var count = await Repository.CountAsync(c => c.DogOwnerId == dogOwnerId && c.Created >= startDate && c.Created <= endDate && c.Status <= maxStatus).ConfigureAwait(false);
return count;
}
#endregion
}
}