using gehGassi.Core.Interfaces; using gehGassi.Domain.Pages; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using gehGassi.Domain.Coins; using gehGassi.Domain.Common; using gehGassi.Domain.Walks; using Microsoft.EntityFrameworkCore; namespace gehGassi.Core.Services { /// /// Service der die Verwaltung von Coin-Konten, Transaktionen und Monats- und Jahresstatistiken realisiert /// public class CoinsService : ServiceBase, ICoinsService { private readonly IRepository _transactionRepository; private readonly IRepository _perMonthRepository; private readonly IRepository _perMonthNamesRepository; private readonly IRepository _perYearRepository; private readonly IRepository _perYearNamesRepository; /// /// Erstellt eine Instanz /// /// Instanz eines IUnitOfWork public CoinsService(IUnitOfWork unitOfWork) : base(unitOfWork) { _transactionRepository = unitOfWork.GetRepository(); _perMonthRepository = unitOfWork.GetRepository(); _perMonthNamesRepository = unitOfWork.GetRepository(); _perYearRepository = unitOfWork.GetRepository(); _perYearNamesRepository = unitOfWork.GetRepository(); } /// /// Gibt einen Coin-Account zurück oder erstellt einen neuen wenn noch nicht vorhanden /// /// Id des AppUsers /// Typ des AppUsers /// CoinAccount public async Task GetOrCreateAsync(string appUserId, AppUserType appUserType) { var account = await Repository.FirstOrDefaultAsync(c => c.AppUserId == appUserId && c.AppUserType == appUserType); if (account == null) { account = new CoinsAccount() { Id = Guid.NewGuid().ToString("N"), AppUserId = appUserId, AppUserType = appUserType, Balance = 0, Created = DateTimeOffset.UtcNow, UpdatedAt = DateTimeOffset.UtcNow }; Repository.Add(account); await CommitAsync("System"); } return account; } /// /// Einzahlen einer Coins-Summe auf das passende Konto eines AppUsers /// /// Id des AppUsers /// Typ des AppUsers /// Coins-Betrag der eingezahlt werden soll /// Optional: Id des Walks auf den sich die Transaktion bezieht /// Transaktion public async Task DepositAsync(string appUserId, AppUserType appUserType, decimal ammount, string walkId) { var account = await GetOrCreateAsync(appUserId, appUserType); //Transaktion anlegen var transaction = new CoinsTransaction() { TransactionType = CoinsTransactionType.Deposit, AccountId = account.Id, Ammount = ammount, WalkId = walkId, Created = DateTimeOffset.UtcNow }; _transactionRepository.Add(transaction); account.Balance += ammount; //Punkte für das Monatskonto var coinsPerMonth = await GetOrCreateCoinsPerMonthAsync(appUserId, appUserType, DateTimeOffset.UtcNow); coinsPerMonth.Balance += ammount; coinsPerMonth.LastUpdate = DateTimeOffset.UtcNow; //Punkte für das Jahreskonto var coinsPerYear = await GetOrCreateCoinsPerYearAsync(appUserId, appUserType, DateTimeOffset.UtcNow); coinsPerYear.Balance += ammount; coinsPerYear.LastUpdate = DateTimeOffset.UtcNow; await CommitAsync("System"); return transaction; } /// /// Abbuchen einer Coins-Summe von dem passenden Konto eines AppUsers /// /// Id des AppUsers /// Typ des AppUsers /// Coins-Betrag der eingezahlt werden soll /// CoinsTransaction public async Task WithdrawAsync(string appUserId, AppUserType appUserType, decimal ammount) { var account = await GetOrCreateAsync(appUserId, appUserType); if (account.Balance < ammount) return null; //Transaktion anlegen var transaction = new CoinsTransaction() { TransactionType = CoinsTransactionType.Withdrawal, AccountId = account.Id, Ammount = ammount, WalkId = string.Empty, Created = DateTimeOffset.UtcNow }; _transactionRepository.Add(transaction); account.Balance -= ammount; await CommitAsync("System"); return transaction; } /// /// Gibt den Coins-Stand eines AppUsers für einen Monat zurück /// /// Id des AppUsers /// Typ des AppUsers /// Datum /// CoinsPerMonth public async Task GetOrCreateCoinsPerMonthAsync(string appUserId, AppUserType appUserType, DateTimeOffset date) { var coinsPerMonth = await _perMonthRepository.FirstOrDefaultAsync(c => c.AppUserId == appUserId && c.AppUserType == appUserType && c.Year == date.Year && c.Month == date.Month); if (coinsPerMonth == null) { coinsPerMonth = new CoinsPerMonth() { AppUserId = appUserId, AppUserType = appUserType, Year = date.Year, Month = date.Month, Balance = 0, LastUpdate = DateTimeOffset.UtcNow }; //Keine Wertung rückwirkend erstellen if (date.Year == DateTimeOffset.UtcNow.Year && date.Month == DateTimeOffset.UtcNow.Month) { _perMonthRepository.Add(coinsPerMonth); await CommitAsync("System"); } } return coinsPerMonth; } /// /// Gibt den Coins-Stand eines AppUsers für ein Jahr zurück /// /// Id des AppUsers /// Typ des AppUsers /// Datum /// CoinsPerYear public async Task GetOrCreateCoinsPerYearAsync(string appUserId, AppUserType appUserType, DateTimeOffset date) { var coinsPerYear = await _perYearRepository.FirstOrDefaultAsync(c => c.AppUserId == appUserId && c.AppUserType == appUserType && c.Year == date.Year); if (coinsPerYear == null) { coinsPerYear = new CoinsPerYear() { AppUserId = appUserId, AppUserType = appUserType, Year = date.Year, Balance = 0, LastUpdate = DateTimeOffset.UtcNow }; //Keine Wertung rückwirkend erstellen if (date.Year == DateTimeOffset.UtcNow.Year) { _perYearRepository.Add(coinsPerYear); await CommitAsync("System"); } } return coinsPerYear; } /// /// Gibt die Top X der Coins je Monat für ein Jahr und Monat zurück. /// Sortiert nach Coins absteigend und bei Gleichstand nach LastUpdate aufsteigend /// /// Anzalh Datensätze /// Typ des AppUsers /// Jahr /// Monat /// Mindest-Wert der erreicht werden musste /// Liste CoinsPerMonthWithNames public async Task> GetMonthlyTopAsync(int count, AppUserType appUserType, int year, int month, decimal minBalance) { var coins = await _perMonthNamesRepository.QueryView(c => c.AppUserType == appUserType && c.Year == year && c.Month == month && c.Balance >= minBalance).OrderByDescending(c => c.Balance).ThenBy(c => c.LastUpdate).Take(count).ToListAsync(); return coins; } /// /// Gibt die Top X der Coins je Jahr zurück. /// Sortiert nach Coins absteigend und bei Gleichstand nach LastUpdate aufsteigend /// /// Anzalh Datensätze /// Typ des AppUsers /// Jahr /// Mindest-Wert der erreicht werden musste /// Liste CoinsPerMonthWithNames public async Task> GetYearlyTopAsync(int count, AppUserType appUserType, int year, decimal minBalance) { var coins = await _perYearNamesRepository.QueryView(c => c.AppUserType == appUserType && c.Year == year && c.Balance >= minBalance).OrderByDescending(c => c.Balance).ThenBy(c => c.LastUpdate).Take(count).ToListAsync(); return coins; } } }