236 lines
10 KiB
C#

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
{
/// <summary>
/// Service der die Verwaltung von Coin-Konten, Transaktionen und Monats- und Jahresstatistiken realisiert
/// </summary>
public class CoinsService : ServiceBase<CoinsAccount>, ICoinsService
{
private readonly IRepository<CoinsTransaction> _transactionRepository;
private readonly IRepository<CoinsPerMonth> _perMonthRepository;
private readonly IRepository<CoinsPerMonthWithNames> _perMonthNamesRepository;
private readonly IRepository<CoinsPerYear> _perYearRepository;
private readonly IRepository<CoinsPerYearWithNames> _perYearNamesRepository;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
public CoinsService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_transactionRepository = unitOfWork.GetRepository<CoinsTransaction>();
_perMonthRepository = unitOfWork.GetRepository<CoinsPerMonth>();
_perMonthNamesRepository = unitOfWork.GetRepository<CoinsPerMonthWithNames>();
_perYearRepository = unitOfWork.GetRepository<CoinsPerYear>();
_perYearNamesRepository = unitOfWork.GetRepository<CoinsPerYearWithNames>();
}
/// <summary>
/// Gibt einen Coin-Account zurück oder erstellt einen neuen wenn noch nicht vorhanden
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <returns>CoinAccount</returns>
public async Task<CoinsAccount> 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;
}
/// <summary>
/// Einzahlen einer Coins-Summe auf das passende Konto eines AppUsers
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <param name="ammount">Coins-Betrag der eingezahlt werden soll</param>
/// <param name="walkId">Optional: Id des Walks auf den sich die Transaktion bezieht</param>
/// <returns>Transaktion</returns>
public async Task<CoinsTransaction> 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;
}
/// <summary>
/// Abbuchen einer Coins-Summe von dem passenden Konto eines AppUsers
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <param name="ammount">Coins-Betrag der eingezahlt werden soll</param>
/// <returns>CoinsTransaction</returns>
public async Task<CoinsTransaction> 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;
}
/// <summary>
/// Gibt den Coins-Stand eines AppUsers für einen Monat zurück
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <param name="date">Datum</param>
/// <returns>CoinsPerMonth</returns>
public async Task<CoinsPerMonth> 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;
}
/// <summary>
/// Gibt den Coins-Stand eines AppUsers für ein Jahr zurück
/// </summary>
/// <param name="appUserId">Id des AppUsers</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <param name="date">Datum</param>
/// <returns>CoinsPerYear</returns>
public async Task<CoinsPerYear> 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;
}
/// <summary>
/// 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
/// </summary>
/// <param name="count">Anzalh Datensätze</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <param name="year">Jahr</param>
/// <param name="month">Monat</param>
/// <param name="minBalance">Mindest-Wert der erreicht werden musste</param>
/// <returns>Liste CoinsPerMonthWithNames</returns>
public async Task<List<CoinsPerMonthWithNames>> 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;
}
/// <summary>
/// Gibt die Top X der Coins je Jahr zurück.
/// Sortiert nach Coins absteigend und bei Gleichstand nach LastUpdate aufsteigend
/// </summary>
/// <param name="count">Anzalh Datensätze</param>
/// <param name="appUserType">Typ des AppUsers</param>
/// <param name="year">Jahr</param>
/// <param name="minBalance">Mindest-Wert der erreicht werden musste</param>
/// <returns>Liste CoinsPerMonthWithNames</returns>
public async Task<List<CoinsPerYearWithNames>> 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;
}
}
}