commit
This commit is contained in:
@@ -6,6 +6,10 @@ using FnbEngine.API.Application.Behaviors;
|
||||
using FnbEngine.Infrastructure;
|
||||
using Serilog;
|
||||
|
||||
// EN: Enable legacy timestamp behavior for Npgsql (DateTime.Kind compatibility)
|
||||
// VI: Bật chế độ timestamp cũ cho Npgsql (tương thích DateTime.Kind)
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
|
||||
// EN: Configure Serilog early / VI: Cấu hình Serilog sớm
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.WriteTo.Console()
|
||||
|
||||
@@ -43,6 +43,16 @@ public static class Config
|
||||
public static IEnumerable<ApiResource> ApiResources =>
|
||||
[
|
||||
new ApiResource("iam-api", "IAM Service API")
|
||||
{
|
||||
Scopes = { "api" },
|
||||
UserClaims = { "role", "email", "name" }
|
||||
},
|
||||
new ApiResource("goodgo-api", "GoodGo Platform API")
|
||||
{
|
||||
Scopes = { "api" },
|
||||
UserClaims = { "role", "email", "name" }
|
||||
},
|
||||
new ApiResource("goodgo-services", "GoodGo Internal Services")
|
||||
{
|
||||
Scopes = { "api" },
|
||||
UserClaims = { "role", "email", "name" }
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace OrderService.API.Application.Queries;
|
||||
/// EN: Query for POS dashboard data.
|
||||
/// VI: Query cho dữ liệu dashboard POS.
|
||||
/// </summary>
|
||||
public record GetPosDashboardQuery(Guid ShopId) : IRequest<PosDashboardDto>;
|
||||
public record GetPosDashboardQuery(Guid ShopId, string? Period = "today") : IRequest<PosDashboardDto>;
|
||||
|
||||
/// <summary>
|
||||
/// EN: POS dashboard DTO with today's stats.
|
||||
@@ -90,12 +90,18 @@ public class GetPosDashboardQueryHandler : IRequestHandler<GetPosDashboardQuery,
|
||||
|
||||
public async Task<PosDashboardDto> Handle(GetPosDashboardQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var today = DateTime.UtcNow.Date;
|
||||
var tomorrow = today.AddDays(1);
|
||||
var now = DateTime.UtcNow;
|
||||
var fromDate = request.Period?.ToLower() switch
|
||||
{
|
||||
"7d" => now.Date.AddDays(-7),
|
||||
"30d" => now.Date.AddDays(-30),
|
||||
_ => now.Date // "today" or default
|
||||
};
|
||||
var toDate = now.Date.AddDays(1);
|
||||
var parameters = new DynamicParameters();
|
||||
parameters.Add("ShopId", request.ShopId);
|
||||
parameters.Add("Today", today);
|
||||
parameters.Add("Tomorrow", tomorrow);
|
||||
parameters.Add("Today", fromDate);
|
||||
parameters.Add("Tomorrow", toDate);
|
||||
|
||||
// EN: Aggregate stats for today / VI: Thống kê tổng hợp hôm nay
|
||||
var aggregateSql = @"
|
||||
|
||||
@@ -181,13 +181,14 @@ public class OrdersController : ControllerBase
|
||||
[ProducesResponseType(typeof(PosDashboardDto), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PosDashboardDto>> GetPosDashboard(
|
||||
[FromQuery] Guid shopId,
|
||||
[FromQuery] string? period = "today",
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"EN: Getting POS dashboard for shop {ShopId} / VI: Lấy dashboard POS cho shop {ShopId}",
|
||||
shopId);
|
||||
"EN: Getting POS dashboard for shop {ShopId} period {Period} / VI: Lấy dashboard POS cho shop {ShopId} kỳ {Period}",
|
||||
shopId, period);
|
||||
|
||||
var query = new GetPosDashboardQuery(shopId);
|
||||
var query = new GetPosDashboardQuery(shopId, period);
|
||||
var result = await _mediator.Send(query, cancellationToken);
|
||||
|
||||
return Ok(result);
|
||||
|
||||
@@ -61,22 +61,35 @@ public class CreateCampaignCommandHandler : IRequestHandler<CreateCampaignComman
|
||||
request.VoucherValidityDays,
|
||||
request.MaxPerUser);
|
||||
|
||||
// Create escrow hold in Wallet Service
|
||||
// Create escrow hold in Wallet Service (optional — skip if wallet unavailable)
|
||||
var escrowAmount = request.FaceValue * request.TotalVouchers;
|
||||
_logger.LogInformation("Creating escrow hold for {Amount} {Currency}",
|
||||
escrowAmount, request.BackingAssetCode);
|
||||
try
|
||||
{
|
||||
if (request.MerchantWalletId != Guid.Empty)
|
||||
{
|
||||
_logger.LogInformation("Creating escrow hold for {Amount} {Currency}",
|
||||
escrowAmount, request.BackingAssetCode);
|
||||
|
||||
var holdResult = await _walletService.CreateHoldAsync(
|
||||
request.MerchantWalletId,
|
||||
escrowAmount,
|
||||
request.BackingAssetCode,
|
||||
"CAMPAIGN",
|
||||
campaign.Id,
|
||||
$"Campaign escrow: {request.Name}",
|
||||
cancellationToken);
|
||||
var holdResult = await _walletService.CreateHoldAsync(
|
||||
request.MerchantWalletId,
|
||||
escrowAmount,
|
||||
request.BackingAssetCode,
|
||||
"CAMPAIGN",
|
||||
campaign.Id,
|
||||
$"Campaign escrow: {request.Name}",
|
||||
cancellationToken);
|
||||
|
||||
// Set escrow reference on campaign
|
||||
campaign.SetEscrowHold(holdResult.WalletId, holdResult.HoldId);
|
||||
campaign.SetEscrowHold(holdResult.WalletId, holdResult.HoldId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Skipping escrow hold — no merchant wallet provided");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to create escrow hold for campaign {Name}. Continuing without escrow.", request.Name);
|
||||
}
|
||||
|
||||
// Generate voucher codes
|
||||
campaign.GenerateVouchers(request.TotalVouchers);
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace PromotionService.API.Controllers.Admin;
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/v1/admin/campaigns")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
[Authorize]
|
||||
[Produces("application/json")]
|
||||
public class AdminCampaignsController : ControllerBase
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace PromotionService.API.Controllers.Admin;
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/v1/admin/redemptions")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
[Authorize]
|
||||
[Produces("application/json")]
|
||||
public class AdminRedemptionsController : ControllerBase
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace PromotionService.API.Controllers.Admin;
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/v1/admin/vouchers")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
[Authorize]
|
||||
[Produces("application/json")]
|
||||
public class AdminVouchersController : ControllerBase
|
||||
{
|
||||
|
||||
@@ -30,7 +30,7 @@ public class CampaignsController : ControllerBase
|
||||
/// VI: Tạo chiến dịch mới.
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Authorize(Roles = "Merchant,Admin")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(CampaignDto), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
public async Task<ActionResult<CampaignDto>> CreateCampaign([FromBody] CreateCampaignCommand command)
|
||||
@@ -72,7 +72,7 @@ public class CampaignsController : ControllerBase
|
||||
/// VI: Kích hoạt chiến dịch.
|
||||
/// </summary>
|
||||
[HttpPost("{id:guid}/activate")]
|
||||
[Authorize(Roles = "Merchant,Admin")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ActivateCampaign(Guid id)
|
||||
@@ -86,7 +86,7 @@ public class CampaignsController : ControllerBase
|
||||
/// VI: Tạm dừng chiến dịch.
|
||||
/// </summary>
|
||||
[HttpPost("{id:guid}/pause")]
|
||||
[Authorize(Roles = "Merchant,Admin")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> PauseCampaign(Guid id)
|
||||
@@ -100,7 +100,7 @@ public class CampaignsController : ControllerBase
|
||||
/// VI: Hủy chiến dịch.
|
||||
/// </summary>
|
||||
[HttpPost("{id:guid}/cancel")]
|
||||
[Authorize(Roles = "Merchant,Admin")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> CancelCampaign(Guid id)
|
||||
|
||||
@@ -152,8 +152,8 @@ try
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateIssuer = false,
|
||||
ValidateAudience = false,
|
||||
ValidateLifetime = true
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user