Files
managing-apps/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlBotRepository.cs
2025-09-25 11:08:28 +07:00

270 lines
9.0 KiB
C#

using Managing.Application.Abstractions.Repositories;
using Managing.Domain.Bots;
using Microsoft.EntityFrameworkCore;
using static Managing.Common.Enums;
namespace Managing.Infrastructure.Databases.PostgreSql;
public class PostgreSqlBotRepository : IBotRepository
{
private readonly ManagingDbContext _context;
public PostgreSqlBotRepository(ManagingDbContext context)
{
_context = context;
}
public async Task InsertBotAsync(Bot bot)
{
bot.CreateDate = DateTime.UtcNow;
var entity = PostgreSqlMappers.Map(bot);
// Set the UserId if user is provided
if (bot.User != null)
{
var userEntity = await _context.Users
.AsNoTracking()
.FirstOrDefaultAsync(u => u.Id == bot.User.Id)
.ConfigureAwait(false);
if (userEntity == null)
{
throw new InvalidOperationException($"User with id '{bot.User.Id}' not found");
}
entity.UserId = userEntity.Id;
}
await _context.Bots.AddAsync(entity).ConfigureAwait(false);
await _context.SaveChangesAsync().ConfigureAwait(false);
}
public async Task<IEnumerable<Bot>> GetBotsAsync()
{
var entities = await _context.Bots
.AsNoTracking()
.Include(m => m.User)
.ToListAsync()
.ConfigureAwait(false);
return PostgreSqlMappers.Map(entities);
}
public async Task UpdateBot(Bot bot)
{
var existingEntity = await _context.Bots
.AsTracking()
.FirstOrDefaultAsync(b => b.Identifier == bot.Identifier)
.ConfigureAwait(false);
if (existingEntity == null)
{
throw new InvalidOperationException($"Bot backup with identifier '{bot.Identifier}' not found");
}
// Update the existing entity properties directly instead of creating a new one
existingEntity.Name = bot.Name;
existingEntity.Ticker = bot.Ticker;
existingEntity.Status = bot.Status;
existingEntity.StartupTime = bot.StartupTime;
existingEntity.TradeWins = bot.TradeWins;
existingEntity.TradeLosses = bot.TradeLosses;
existingEntity.Pnl = bot.Pnl;
existingEntity.Roi = bot.Roi;
existingEntity.Volume = bot.Volume;
existingEntity.Fees = bot.Fees;
existingEntity.UpdatedAt = DateTime.UtcNow;
existingEntity.LongPositionCount = bot.LongPositionCount;
existingEntity.ShortPositionCount = bot.ShortPositionCount;
await _context.SaveChangesAsync().ConfigureAwait(false);
}
public async Task DeleteBot(Guid identifier)
{
var entity = await _context.Bots
.AsTracking()
.FirstOrDefaultAsync(b => b.Identifier == identifier)
.ConfigureAwait(false);
if (entity == null)
{
throw new InvalidOperationException($"Bot backup with identifier '{identifier}' not found");
}
_context.Bots.Remove(entity);
await _context.SaveChangesAsync().ConfigureAwait(false);
}
public async Task<Bot> GetBotByIdentifierAsync(Guid identifier)
{
var entity = await _context.Bots
.AsNoTracking()
.Include(m => m.User)
.FirstOrDefaultAsync(b => b.Identifier == identifier)
.ConfigureAwait(false);
return PostgreSqlMappers.Map(entity);
}
public async Task<IEnumerable<Bot>> GetBotsByUserIdAsync(int id)
{
var entities = await _context.Bots
.AsNoTracking()
.Include(m => m.User)
.Where(b => b.UserId == id)
.ToListAsync()
.ConfigureAwait(false);
return PostgreSqlMappers.Map(entities);
}
public async Task<IEnumerable<Bot>> GetBotsByStatusAsync(BotStatus status)
{
var entities = await _context.Bots
.AsNoTracking()
.Include(m => m.User)
.Where(b => b.Status == status)
.ToListAsync()
.ConfigureAwait(false);
return PostgreSqlMappers.Map(entities);
}
public async Task<Bot> GetBotByNameAsync(string name)
{
var entity = await _context.Bots
.AsNoTracking()
.Include(m => m.User)
.FirstOrDefaultAsync(b => b.Name == name)
.ConfigureAwait(false);
return PostgreSqlMappers.Map(entity);
}
public async Task<IEnumerable<Bot>> GetBotsByIdsAsync(IEnumerable<Guid> identifiers)
{
try
{
await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);
var entities = await _context.Bots
.AsNoTracking()
.Include(m => m.User)
.Where(b => identifiers.Contains(b.Identifier))
.ToListAsync()
.ConfigureAwait(false);
return PostgreSqlMappers.Map(entities);
}
finally
{
// Always ensure the connection is closed after the operation
await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context);
}
}
public async Task<(IEnumerable<Bot> Bots, int TotalCount)> GetBotsPaginatedAsync(
int pageNumber,
int pageSize,
BotStatus? status = null,
string? name = null,
string? ticker = null,
string? agentName = null,
string sortBy = "CreateDate",
string sortDirection = "Desc")
{
// Build the query with filters
var query = _context.Bots
.AsNoTracking()
.Include(m => m.User)
.AsQueryable();
// Apply filters
if (status.HasValue)
{
query = query.Where(b => b.Status == status.Value);
}
if (!string.IsNullOrWhiteSpace(name))
{
query = query.Where(b => EF.Functions.ILike(b.Name, $"%{name}%"));
}
if (!string.IsNullOrWhiteSpace(ticker))
{
query = query.Where(b => EF.Functions.ILike(b.Ticker.ToString(), $"%{ticker}%"));
}
if (!string.IsNullOrWhiteSpace(agentName))
{
query = query.Where(b => b.User != null && EF.Functions.ILike(b.User.AgentName, $"%{agentName}%"));
}
// Get total count before applying pagination
var totalCount = await query.CountAsync().ConfigureAwait(false);
// Apply sorting
query = sortBy.ToLower() switch
{
"name" => sortDirection.ToLower() == "asc"
? query.OrderBy(b => b.Name)
: query.OrderByDescending(b => b.Name),
"ticker" => sortDirection.ToLower() == "asc"
? query.OrderBy(b => b.Ticker)
: query.OrderByDescending(b => b.Ticker),
"status" => sortDirection.ToLower() == "asc"
? query.OrderBy(b => b.Status)
: query.OrderByDescending(b => b.Status),
"startuptime" => sortDirection.ToLower() == "asc"
? query.OrderBy(b => b.StartupTime)
: query.OrderByDescending(b => b.StartupTime),
"pnl" => sortDirection.ToLower() == "asc"
? query.OrderBy(b => b.Pnl)
: query.OrderByDescending(b => b.Pnl),
"winrate" => sortDirection.ToLower() == "asc"
? query.OrderBy(b => b.TradeWins / (b.TradeWins + b.TradeLosses))
: query.OrderByDescending(b => b.TradeWins / (b.TradeWins + b.TradeLosses)),
"agentname" => sortDirection.ToLower() == "asc"
? query.OrderBy(b => b.User.AgentName)
: query.OrderByDescending(b => b.User.AgentName),
_ => sortDirection.ToLower() == "asc"
? query.OrderBy(b => b.CreateDate)
: query.OrderByDescending(b => b.CreateDate) // Default to CreateDate
};
// Apply pagination
var skip = (pageNumber - 1) * pageSize;
var entities = await query
.Skip(skip)
.Take(pageSize)
.ToListAsync()
.ConfigureAwait(false);
var bots = PostgreSqlMappers.Map(entities);
return (bots, totalCount);
}
public async Task<IEnumerable<Bot>> GetTopBotsByPnLAsync(IEnumerable<BotStatus> statuses, int count = 3)
{
var entities = await _context.Bots
.AsNoTracking()
.Include(m => m.User)
.Where(b => statuses.Contains(b.Status))
.OrderByDescending(b => b.Pnl)
.Take(count)
.ToListAsync()
.ConfigureAwait(false);
return PostgreSqlMappers.Map(entities);
}
public async Task<IEnumerable<Bot>> GetTopBotsByRoiAsync(IEnumerable<BotStatus> statuses, int count = 3)
{
var entities = await _context.Bots
.AsNoTracking()
.Include(m => m.User)
.Where(b => statuses.Contains(b.Status))
.OrderByDescending(b => b.Roi)
.Take(count)
.ToListAsync()
.ConfigureAwait(false);
return PostgreSqlMappers.Map(entities);
}
}