diff --git a/deployments/local/docker-compose.yml b/deployments/local/docker-compose.yml index 316eb663..5495097f 100644 --- a/deployments/local/docker-compose.yml +++ b/deployments/local/docker-compose.yml @@ -1012,6 +1012,64 @@ services: - "traefik.http.routers.ads-serving-admin.entrypoints=web" - "traefik.http.routers.ads-serving-admin.service=ads-serving-service" + # Ads Billing Service .NET - Billing, Invoicing & Payment Management + ads-billing-service-net: + build: + context: ../../services/ads-billing-service-net + dockerfile: Dockerfile + image: goodgo/ads-billing-service-net:latest + container_name: ads-billing-service-net-local + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://+:8080 + # EN: Database - Neon PostgreSQL + # VI: Cơ sở dữ liệu - Neon PostgreSQL + - ConnectionStrings__DefaultConnection=Host=ep-holy-glitter-a4hongg7-pooler.us-east-1.aws.neon.tech;Port=5432;Database=ads_billing_service;Username=neondb_owner;Password=npg_Ssfy6HKO0cXI;SSL Mode=Require + # EN: IAM Service Communication + # VI: Giao tiếp IAM Service + - IamService__BaseUrl=http://iam-service-net:8080 + - IamService__ServiceName=ads-billing-service + # EN: Wallet Service Communication + # VI: Giao tiếp Wallet Service + - WalletService__BaseUrl=http://wallet-service-net:8080 + # EN: JWT Configuration + # VI: Cấu hình JWT + - Jwt__Authority=http://iam-service-net:8080 + - Jwt__Audience=goodgo-api + - Jwt__RequireHttpsMetadata=false + ports: + - "5023:8080" + depends_on: + iam-service-net: + condition: service_healthy + wallet-service-net: + condition: service_healthy + traefik: + condition: service_started + networks: + - microservices-network + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/health/live"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + labels: + - "traefik.enable=true" + # EN: Public API routes for billing, invoices, credit lines + # VI: Routes API công khai cho billing, hóa đơn, tín dụng + - "traefik.http.routers.ads-billing-service.rule=PathPrefix(`/api/v1/ads-billing/accounts`) || PathPrefix(`/api/v1/ads-billing/invoices`) || PathPrefix(`/api/v1/ads-billing/credit-lines`)" + - "traefik.http.routers.ads-billing-service.entrypoints=web" + - "traefik.http.services.ads-billing-service.loadbalancer.server.port=8080" + - "traefik.http.services.ads-billing-service.loadbalancer.healthcheck.path=/health/live" + - "traefik.http.services.ads-billing-service.loadbalancer.healthcheck.interval=10s" + # EN: Admin API routes for billing management + # VI: Routes API Admin để quản lý billing + - "traefik.http.routers.ads-billing-admin.rule=PathPrefix(`/api/v1/admin/ads-billing`)" + - "traefik.http.routers.ads-billing-admin.entrypoints=web" + - "traefik.http.routers.ads-billing-admin.service=ads-billing-service" + # Jaeger - Distributed Tracing # jaeger: # image: jaegertracing/all-in-one:1.47 diff --git a/services/ads-manager-service-net/src/AdsManagerService.API/Controllers/AdminCampaignsController.cs b/services/ads-manager-service-net/src/AdsManagerService.API/Controllers/AdminCampaignsController.cs new file mode 100644 index 00000000..737c1818 --- /dev/null +++ b/services/ads-manager-service-net/src/AdsManagerService.API/Controllers/AdminCampaignsController.cs @@ -0,0 +1,79 @@ +using AdsManagerService.API.Application.Queries; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace AdsManagerService.API.Controllers; + +/// +/// EN: Admin API Controller for campaign management and oversight. +/// VI: API Controller Admin cho quản lý và giám sát chiến dịch. +/// +[ApiController] +[Route("api/v1/admin/ads-manager/campaigns")] +[Produces("application/json")] +public class AdminCampaignsController : ControllerBase +{ + private readonly IMediator _mediator; + private readonly ILogger _logger; + + public AdminCampaignsController(IMediator mediator, ILogger logger) + { + _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + /// + /// EN: List all campaigns across all advertisers (Admin only). + /// VI: Liệt kê tất cả chiến dịch của tất cả advertisers (Admin only). + /// + [HttpGet] + [ProducesResponseType(typeof(ListCampaignsResult), StatusCodes.Status200OK)] + public async Task> ListAllCampaigns( + [FromQuery] string? status, + [FromQuery] int page = 1, + [FromQuery] int pageSize = 50) + { + var query = new ListCampaignsQuery + { + Status = status, + Page = page, + PageSize = pageSize + }; + + var result = await _mediator.Send(query); + return Ok(result); + } + + /// + /// EN: Get campaign statistics. + /// VI: Lấy thống kê chiến dịch. + /// + [HttpGet("stats")] + [ProducesResponseType(typeof(CampaignStatsDto), StatusCodes.Status200OK)] + public async Task> GetCampaignStats() + { + var stats = await _mediator.Send(new GetCampaignStatsQuery()); + return Ok(stats); + } +} + +/// +/// EN: Campaign statistics DTO. +/// VI: DTO thống kê chiến dịch. +/// +public record CampaignStatsDto +{ + public int TotalCampaigns { get; init; } + public int ActiveCampaigns { get; init; } + public int PausedCampaigns { get; init; } + public int DraftCampaigns { get; init; } + public int CompletedCampaigns { get; init; } + public decimal TotalSpend { get; init; } + public decimal TotalBudget { get; init; } +} + +/// +/// EN: Query to get campaign statistics. +/// VI: Query lấy thống kê chiến dịch. +/// +public record GetCampaignStatsQuery : IRequest;