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(); } } }