211 lines
7.0 KiB
C#
211 lines
7.0 KiB
C#
using AdsBillingService.API.Application.Queries;
|
|
using AdsBillingService.Infrastructure;
|
|
using MediatR;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace AdsBillingService.API.Controllers.Admin;
|
|
|
|
/// <summary>
|
|
/// EN: Admin controller for managing billing accounts.
|
|
/// VI: Admin controller quản lý tài khoản billing.
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("api/v1/admin/ads-billing/accounts")]
|
|
[Produces("application/json")]
|
|
public class AdminBillingAccountsController : ControllerBase
|
|
{
|
|
private readonly IMediator _mediator;
|
|
private readonly AdsBillingServiceContext _context;
|
|
private readonly ILogger<AdminBillingAccountsController> _logger;
|
|
|
|
public AdminBillingAccountsController(
|
|
IMediator mediator,
|
|
AdsBillingServiceContext context,
|
|
ILogger<AdminBillingAccountsController> logger)
|
|
{
|
|
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
|
_context = context ?? throw new ArgumentNullException(nameof(context));
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Search billing accounts with filters.
|
|
/// VI: Tìm kiếm tài khoản billing với bộ lọc.
|
|
/// </summary>
|
|
[HttpGet]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public async Task<IActionResult> SearchAccounts(
|
|
[FromQuery] string? status = null,
|
|
[FromQuery] string? paymentMethod = null,
|
|
[FromQuery] int pageNumber = 1,
|
|
[FromQuery] int pageSize = 20)
|
|
{
|
|
_logger.LogInformation("Admin searching accounts with Status={Status}, PaymentMethod={PaymentMethod}",
|
|
status, paymentMethod);
|
|
|
|
var query = _context.BillingAccounts.AsNoTracking();
|
|
|
|
if (!string.IsNullOrEmpty(status))
|
|
{
|
|
query = query.Where(a => a.Status.ToString() == status);
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(paymentMethod))
|
|
{
|
|
query = query.Where(a => a.PaymentMethod.ToString() == paymentMethod);
|
|
}
|
|
|
|
var accounts = await query
|
|
.OrderByDescending(a => a.CreatedAt)
|
|
.Skip((pageNumber - 1) * pageSize)
|
|
.Take(pageSize)
|
|
.Select(a => new
|
|
{
|
|
a.Id,
|
|
a.AdvertiserId,
|
|
PaymentMethod = a.PaymentMethod.ToString(),
|
|
Status = a.Status.ToString(),
|
|
a.Balance,
|
|
a.CreditLimit,
|
|
a.CreatedAt
|
|
})
|
|
.ToListAsync();
|
|
|
|
return Ok(new { pageNumber, pageSize, total = accounts.Count, data = accounts });
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Get billing accounts statistics.
|
|
/// VI: Lấy thống kê tài khoản billing.
|
|
/// </summary>
|
|
[HttpGet("stats")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public async Task<IActionResult> GetStatistics()
|
|
{
|
|
_logger.LogInformation("Getting billing accounts statistics");
|
|
|
|
var totalAccounts = await _context.BillingAccounts.CountAsync();
|
|
var activeAccounts = await _context.BillingAccounts.CountAsync(a => a.Status == Domain.AggregatesModel.BillingAccountAggregate.AccountStatus.Active);
|
|
var totalBalance = await _context.BillingAccounts.SumAsync(a => a.Balance);
|
|
var totalCreditLimit = await _context.BillingAccounts.SumAsync(a => a.CreditLimit);
|
|
|
|
return Ok(new
|
|
{
|
|
totalAccounts,
|
|
activeAccounts,
|
|
suspendedAccounts = totalAccounts - activeAccounts,
|
|
totalBalance,
|
|
totalCreditLimit
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Suspend a billing account.
|
|
/// VI: Tạm ngưng tài khoản billing.
|
|
/// </summary>
|
|
[HttpPost("{id}/suspend")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public async Task<IActionResult> SuspendAccount(Guid id, [FromBody] SuspendAccountRequest request)
|
|
{
|
|
_logger.LogInformation("Suspending account {AccountId}, Reason: {Reason}", id, request.Reason);
|
|
|
|
var account = await _context.BillingAccounts.FirstOrDefaultAsync(a => a.Id == id);
|
|
|
|
if (account == null)
|
|
{
|
|
return NotFound(new { message = $"Account {id} not found" });
|
|
}
|
|
|
|
account.Suspend();
|
|
await _context.SaveEntitiesAsync();
|
|
|
|
return Ok(new
|
|
{
|
|
accountId = id,
|
|
status = "Suspended",
|
|
reason = request.Reason
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Reactivate a suspended billing account.
|
|
/// VI: Kích hoạt lại tài khoản billing đã tạm ngưng.
|
|
/// </summary>
|
|
[HttpPost("{id}/reactivate")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public async Task<IActionResult> ReactivateAccount(Guid id)
|
|
{
|
|
_logger.LogInformation("Reactivating account {AccountId}", id);
|
|
|
|
var account = await _context.BillingAccounts.FirstOrDefaultAsync(a => a.Id == id);
|
|
|
|
if (account == null)
|
|
{
|
|
return NotFound(new { message = $"Account {id} not found" });
|
|
}
|
|
|
|
account.Reactivate();
|
|
await _context.SaveEntitiesAsync();
|
|
|
|
return Ok(new
|
|
{
|
|
accountId = id,
|
|
status = account.Status.ToString(),
|
|
message = "Account reactivated successfully"
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Update credit limit for a billing account.
|
|
/// VI: Cập nhật hạn mức tín dụng cho tài khoản billing.
|
|
/// </summary>
|
|
[HttpPut("{id}/credit-limit")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
public async Task<IActionResult> UpdateCreditLimit(Guid id, [FromBody] UpdateCreditLimitRequest request)
|
|
{
|
|
_logger.LogInformation("Updating credit limit for account {AccountId} to {NewLimit}",
|
|
id, request.NewCreditLimit);
|
|
|
|
if (request.NewCreditLimit < 0)
|
|
{
|
|
return BadRequest(new { message = "Credit limit cannot be negative" });
|
|
}
|
|
|
|
var account = await _context.BillingAccounts.FirstOrDefaultAsync(a => a.Id == id);
|
|
|
|
if (account == null)
|
|
{
|
|
return NotFound(new { message = $"Account {id} not found" });
|
|
}
|
|
|
|
var oldCreditLimit = account.CreditLimit;
|
|
account.SetCreditLimit(request.NewCreditLimit);
|
|
await _context.SaveEntitiesAsync();
|
|
|
|
return Ok(new
|
|
{
|
|
accountId = id,
|
|
oldCreditLimit,
|
|
newCreditLimit = account.CreditLimit,
|
|
message = "Credit limit updated successfully"
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Request to suspend account.
|
|
/// VI: Request tạm ngưng tài khoản.
|
|
/// </summary>
|
|
public record SuspendAccountRequest(string Reason);
|
|
|
|
/// <summary>
|
|
/// EN: Request to update credit limit.
|
|
/// VI: Request cập nhật hạn mức tín dụng.
|
|
/// </summary>
|
|
public record UpdateCreditLimitRequest(decimal NewCreditLimit);
|