Files
pos-system/services/ads-billing-service-net/src/AdsBillingService.API/Controllers/Admin/AdminBillingAccountsController.cs
2026-02-23 12:38:59 +00:00

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