Wave 2 — 3 parallel agents fixing P1 issues: Validators (57 new FluentValidation validators): - ads-manager: 10 validators for all commands - ads-billing: 3 validators for all commands - ads-tracking: 2 validators for missing commands - ads-analytics: 1 validator for CreateReport - social: 8 validators for all commands - mining: 16 validators for all commands - mission: 4 validators for all commands - promotion: 13 validators for all commands Missing handlers (10 implemented): - promotion: ExchangeVoucher, PurchaseVoucher, SearchVouchers, GetCampaignStatistics, GetCampaignVouchers - mission: GetUserMissionProgress - mkt-facebook: GetConversations, GetCustomers - ads-manager: ListAudiences, GetAudienceById All validators use bilingual messages (EN/VI) and are auto-registered via MediatR ValidatorBehavior pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
116 lines
4.1 KiB
C#
116 lines
4.1 KiB
C#
using AdsManagerService.API.Controllers;
|
|
using AdsManagerService.Domain.AggregatesModel.AudienceAggregate;
|
|
using AdsManagerService.Infrastructure;
|
|
using MediatR;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace AdsManagerService.API.Application.Queries;
|
|
|
|
/// <summary>
|
|
/// EN: Handler for ListAudiencesQuery - returns audiences for an advertiser.
|
|
/// VI: Handler cho ListAudiencesQuery - trả về audiences cho nhà quảng cáo.
|
|
/// </summary>
|
|
public class ListAudiencesQueryHandler : IRequestHandler<ListAudiencesQuery, List<AudienceDto>>
|
|
{
|
|
private readonly AdsManagerServiceContext _context;
|
|
|
|
public ListAudiencesQueryHandler(AdsManagerServiceContext context)
|
|
{
|
|
_context = context ?? throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
public async Task<List<AudienceDto>> Handle(ListAudiencesQuery request, CancellationToken cancellationToken)
|
|
{
|
|
// EN: Get custom audiences for the advertiser / VI: Lấy custom audiences cho nhà quảng cáo
|
|
var customAudiences = await _context.CustomAudiences
|
|
.AsNoTracking()
|
|
.Where(a => a.AdvertiserId == request.AdvertiserId)
|
|
.OrderByDescending(a => a.CreatedAt)
|
|
.Select(a => new AudienceDto
|
|
{
|
|
Id = a.Id,
|
|
Name = a.Name,
|
|
Type = "custom",
|
|
Size = a.Size,
|
|
CreatedAt = a.CreatedAt
|
|
})
|
|
.ToListAsync(cancellationToken);
|
|
|
|
// EN: Get lookalike audiences for the advertiser / VI: Lấy lookalike audiences cho nhà quảng cáo
|
|
var lookalikeAudiences = await _context.LookalikeAudiences
|
|
.AsNoTracking()
|
|
.Where(a => a.AdvertiserId == request.AdvertiserId)
|
|
.OrderByDescending(a => a.CreatedAt)
|
|
.Select(a => new AudienceDto
|
|
{
|
|
Id = a.Id,
|
|
Name = a.Name,
|
|
Type = "lookalike",
|
|
Size = a.Size,
|
|
CreatedAt = a.CreatedAt
|
|
})
|
|
.ToListAsync(cancellationToken);
|
|
|
|
// EN: Merge and sort by creation date / VI: Gộp và sắp xếp theo ngày tạo
|
|
var allAudiences = customAudiences
|
|
.Concat(lookalikeAudiences)
|
|
.OrderByDescending(a => a.CreatedAt)
|
|
.ToList();
|
|
|
|
return allAudiences;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Handler for GetAudienceByIdQuery - returns a single audience by ID.
|
|
/// VI: Handler cho GetAudienceByIdQuery - trả về một audience theo ID.
|
|
/// </summary>
|
|
public class GetAudienceByIdQueryHandler : IRequestHandler<GetAudienceByIdQuery, AudienceDto?>
|
|
{
|
|
private readonly AdsManagerServiceContext _context;
|
|
|
|
public GetAudienceByIdQueryHandler(AdsManagerServiceContext context)
|
|
{
|
|
_context = context ?? throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
public async Task<AudienceDto?> Handle(GetAudienceByIdQuery request, CancellationToken cancellationToken)
|
|
{
|
|
// EN: Try custom audience first / VI: Thử custom audience trước
|
|
var customAudience = await _context.CustomAudiences
|
|
.AsNoTracking()
|
|
.FirstOrDefaultAsync(a => a.Id == request.AudienceId, cancellationToken);
|
|
|
|
if (customAudience != null)
|
|
{
|
|
return new AudienceDto
|
|
{
|
|
Id = customAudience.Id,
|
|
Name = customAudience.Name,
|
|
Type = "custom",
|
|
Size = customAudience.Size,
|
|
CreatedAt = customAudience.CreatedAt
|
|
};
|
|
}
|
|
|
|
// EN: Try lookalike audience / VI: Thử lookalike audience
|
|
var lookalikeAudience = await _context.LookalikeAudiences
|
|
.AsNoTracking()
|
|
.FirstOrDefaultAsync(a => a.Id == request.AudienceId, cancellationToken);
|
|
|
|
if (lookalikeAudience != null)
|
|
{
|
|
return new AudienceDto
|
|
{
|
|
Id = lookalikeAudience.Id,
|
|
Name = lookalikeAudience.Name,
|
|
Type = "lookalike",
|
|
Size = lookalikeAudience.Size,
|
|
CreatedAt = lookalikeAudience.CreatedAt
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|