using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Domain.Advertisements;
using gehGassi.Domain.Common;
using Microsoft.EntityFrameworkCore;
using NetTopologySuite.Geometries;
using NetTopologySuite;
namespace gehGassi.Core.Services
{
///
/// Service der die Verwaltung von Bannern realisiert
///
public class BannerService : ServiceBase, IBannerService
{
private readonly IRepository _namesRepository;
private readonly IRepository _statisticsRepository;
///
/// Erstellt eine Instanz
///
/// Instanz eines IUnitOfWork
public BannerService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository();
_statisticsRepository = unitOfWork.GetRepository();
}
///
/// Gibt eine gefilterte Liste von Bannern zurück
///
/// Filterbegriff der in bestimmten Feldern gesucht wird
/// List betroffener Banner
public IQueryable Filter(string filter)
{
return Repository.Query(c => c.NameLocalized.Contains(filter));
}
///
/// Erstellen eines Banners
///
/// Banner
public Banner Create()
{
var item = new Banner()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return item;
}
///
/// Gibt eine gefilterte Liste von Bannern zurück.
///
/// Filterbegriff der in bestimmten Feldern gesucht wird
/// List betroffener Banner
public async Task> SearchAsync(string filter)
{
return (await Repository.FindAsync(c => c.NameLocalized.Contains(filter))).ToList();
}
///
/// Gibt eine gefilterte Liste von Bannern mit Namen zurück in einer gewählten Sprache
///
/// Filterbegriff der in bestimmten Feldern gesucht wird
/// Gewünschte Sprache
/// Fallback Sprache
/// Sollen gelöschte inkludiert werden?
/// Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?
/// Liste betroffener Banner
public IQueryable FilterWithNames(string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_BannersWithNames({0},{1})", c => c.Name.Contains(filter) || c.CustomerName.Contains(filter), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
if (excludeDraft)
baseQuery = baseQuery.Where(c => c.Status != BannerStatus.Draft);
return baseQuery;
}
///
/// Gibt eine gefilterte Liste von Bannern mit Namen für Kunden zurück in einer gewählten Sprache
///
/// Id des Kunden
/// Filterbegriff der in bestimmten Feldern gesucht wird
/// Gewünschte Sprache
/// Fallback Sprache
/// Sollen gelöschte inkludiert werden?
/// Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?
/// Liste betroffener Banner
public IQueryable FilterWithNamesCustomer(long customerId, string filter, string language, string fallback, bool includeDeleted, bool excludeDraft)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_BannersWithNames({0},{1})", c => c.CustomerId == customerId && (c.Name.Contains(filter) || c.CustomerName.Contains(filter)), language, fallback);
if (!includeDeleted)
baseQuery = baseQuery.Where(c => c.Deleted == false);
if (excludeDraft)
baseQuery = baseQuery.Where(c => c.Status != BannerStatus.Draft);
return baseQuery;
}
///
/// Gibt die Anzahl von Bannern zurück
///
/// Sollen gelöschte inkludiert werden?
/// Sollen Entitäten im Draft-Status (Cart) ausgeblendet werden?
/// Anzahl Banner
public async Task CountAsync(bool includeDeleted, bool excludeDraft)
{
var query = Repository.Query(c => c.Id != "");
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
if (excludeDraft)
query = query.Where(c => c.Status != BannerStatus.Draft);
return await query.CountAsync().ConfigureAwait(false);
}
///
/// Gibt die Anzahl von Bannern in einem Status zurück
///
/// Gesuchter Status
/// Sollen gelöschte inkludiert werden?
/// Anzahl Banner
public async Task CountAsync(BannerStatus status, bool includeDeleted)
{
var query = Repository.Query(c => c.Status == status);
if (!includeDeleted)
query = query.Where(c => c.Deleted == false);
return await query.CountAsync().ConfigureAwait(false);
}
///
/// Gibt einen Banner für einen Artikel im Warenkorb zurück
///
/// Id des Artikels im Warenkorb
/// Banner oder null, wenn nicht gefunden
public async Task GetByCartItemAsync(long cartItemId)
{
return await Repository.FirstOrDefaultAsync(c => c.CartItemId == cartItemId && c.Status == BannerStatus.Draft);
}
///
/// Gibt einen Banner für einen Artikel in einer Bestellung zurück
///
/// Id des Artikels in der Bestellung
/// Banner oder null, wenn nicht gefunden
public async Task GetByOrderItemAsync(long orderItemId)
{
return await Repository.FirstOrDefaultAsync(c => c.OrderItemId == orderItemId);
}
///
/// Setzen des Status von Bannern nach Bestellung
///
/// Id der Bestellung
/// Zu setzender Status
/// Anzahl betroffener Banner
public async Task SetStatusByOrderAsync(long orderId, BannerStatus status)
{
var items = await Repository.FindAsync(c => c.OrderId == orderId);
foreach (var item in items)
{
item.Status = status;
}
return items.Count();
}
///
/// Setzen des Bezahlstatus von Bannern nach Bestellung
///
/// Id der Bestellung
/// Zu setzender Status
/// Anzahl betroffener Banner
public async Task SetPaymentStatusByOrderAsync(long orderId, PaymentStatus status)
{
var items = await Repository.FindAsync(c => c.OrderId == orderId);
foreach (var item in items)
{
item.PaymentStatus = status;
}
return items.Count();
}
///
/// Gibt einen Banner für eine Platzierung zurück
///
/// Platzierung des Banners
/// Optional: Id des Banners der gerade angezeigt wird
/// Land
/// Bundesland
/// Optional: Breitengrad
/// Optional: Längengrad
/// Gewünschte Sprache
/// Fallback Sprache
/// Zufälliger Banner wenn gefunden, null sonst
public async Task GetBannerForLocationAsync(BannerLocation bannerLocation, string currentBannerId, string country, string state, double lat, double lng, string language, string fallback)
{
language = language.ToUpperInvariant();
fallback = fallback.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(country))
country = country.ToUpperInvariant();
if (!string.IsNullOrWhiteSpace(state))
state = state.ToUpperInvariant();
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var location = geometryFactory.CreatePoint(new Coordinate(lng, lat));
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM tv_BannersWithNames({0},{1})", c => c.Deleted == false && c.Status == BannerStatus.Running && c.BannerLocation == bannerLocation &&
(c.GeoMode == GeoMode.None ||
(c.GeoMode == GeoMode.Country && c.Address_CountryCode == country) ||
(c.GeoMode == GeoMode.CountryAndState && c.Address_CountryCode == country && c.Address_State == state) ||
(c.GeoMode == GeoMode.Location && c.Location.IsWithinDistance(location, c.Radius * 1000))), language, fallback);
var list = await baseQuery.ToListAsync().ConfigureAwait(false);
if (list.Any())
{
if (list.Count > 1 && !string.IsNullOrWhiteSpace(currentBannerId))
{
//Wenn aktuell ein Banner angezeigt wird, dann diesen aus der Liste entfernen.
//Das geht nur wenn mindestens 2 Banner in der Liste sind.
list = list.Where(c => c.Id != currentBannerId).ToList();
}
var random = new Random();
int index = random.Next(list.Count);
return list[index];
}
return null;
}
///
/// Fügt eine Anzahl von Views zu einem Banner hinzu und beendet die Laufzeit wenn die Budgetgrenze erreicht wurde
///
/// Id des Banners
/// Anzahl Views
/// AppMode
/// Sprache
/// Land
/// Bundesland
/// Anzahl Views gesamt
public async Task AddViewsAsync(string id, int viewCount, AppMode appMode, string language, string country, string state)
{
var banner = await GetAsync(id);
if (banner != null)
{
AddStatistic(id, appMode, BannerStatisticType.View, viewCount, language, country, state);
banner.CurrentViews += viewCount;
var budgetUsed = (banner.CurrentViews * banner.PriceView) + (banner.CurrentClicks * banner.PriceClick);
banner.BudgetUsed = budgetUsed;
if (banner.BudgetUsed >= banner.Budget)
{
banner.Status = BannerStatus.Ended;
banner.EndDate = DateTimeOffset.UtcNow;
}
return banner.CurrentViews;
}
return 0;
}
///
/// Fügt eine Anzahl von Klicks zu einem Banner hinzu und beendet die Laufzeit wenn die Budgetgrenze erreicht wurde
///
/// Id des Banners
/// Anzahl Klicks
/// AppMode
/// Sprache
/// Land
/// Bundesland
/// Anzahl Klicks gesamt
public async Task AddClicksAsync(string id, int clickCount, AppMode appMode, string language, string country, string state)
{
var banner = await GetAsync(id);
if (banner != null)
{
AddStatistic(id, appMode, BannerStatisticType.Click, clickCount, language, country, state);
banner.CurrentClicks += clickCount;
var budgetUsed = (banner.CurrentViews * banner.PriceView) + (banner.CurrentClicks * banner.PriceClick);
banner.BudgetUsed = budgetUsed;
if (banner.BudgetUsed >= banner.Budget)
{
banner.Status = BannerStatus.Ended;
banner.EndDate = DateTimeOffset.UtcNow;
}
return banner.CurrentClicks;
}
return 0;
}
///
/// Hinzufügen einer Statstik für einen Banner
///
/// Id des Banners
/// AppMode
/// Klick oder View
/// Anzahl Klicks oder Views
/// Sprache
/// Land
/// Bundesland
/// Task
public void AddStatistic(string id, AppMode appMode, BannerStatisticType type, int value, string language, string country, string state)
{
var statistic = new BannerStatistic
{
BannerId = id,
AppMode = appMode,
StatisticType = type,
Value = value,
Language = language,
CountryCode = country,
StateCode = state,
EventDate = DateTimeOffset.UtcNow
};
statistic.Year = statistic.EventDate.Year;
statistic.Month = statistic.EventDate.Month;
statistic.Day = statistic.EventDate.Day;
statistic.Hour = statistic.EventDate.Hour;
_statisticsRepository.Add(statistic);
}
///
/// Starten von gebuchten Bannern wenn diese das Start-Datum erreicht haben
///
/// Anzahl gestartete Banner
public async Task StartBookedAsnyc()
{
var items = await Repository.FindAsync(c => c.Status == BannerStatus.Booked && c.Deleted == false && c.StartDate <= DateTimeOffset.UtcNow);
foreach (var item in items)
{
item.Status = BannerStatus.Running;
}
return items.Count();
}
///
/// Stoppen von abgelaufenen Bannern wenn diese das Enddatum erreicht haben
///
/// Anzahl gestoppte Banner
public async Task StopRunningAsync()
{
var items = await Repository.FindAsync(c => c.Status == BannerStatus.Running && c.Deleted == false && c.BudgetUsed >= c.Budget);
foreach (var item in items)
{
item.EndDate = DateTimeOffset.UtcNow;
item.Status = BannerStatus.Ended;
}
return items.Count();
}
}
}