feat: add Ads Billing Service to docker-compose and implement Admin Campaigns Controller in Ads Manager Service.

This commit is contained in:
Ho Ngoc Hai
2026-01-18 01:42:43 +07:00
parent 175c7b45b0
commit 73086b2186
2 changed files with 137 additions and 0 deletions

View File

@@ -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

View File

@@ -0,0 +1,79 @@
using AdsManagerService.API.Application.Queries;
using MediatR;
using Microsoft.AspNetCore.Mvc;
namespace AdsManagerService.API.Controllers;
/// <summary>
/// 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.
/// </summary>
[ApiController]
[Route("api/v1/admin/ads-manager/campaigns")]
[Produces("application/json")]
public class AdminCampaignsController : ControllerBase
{
private readonly IMediator _mediator;
private readonly ILogger<AdminCampaignsController> _logger;
public AdminCampaignsController(IMediator mediator, ILogger<AdminCampaignsController> logger)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// 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).
/// </summary>
[HttpGet]
[ProducesResponseType(typeof(ListCampaignsResult), StatusCodes.Status200OK)]
public async Task<ActionResult<ListCampaignsResult>> 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);
}
/// <summary>
/// EN: Get campaign statistics.
/// VI: Lấy thống kê chiến dịch.
/// </summary>
[HttpGet("stats")]
[ProducesResponseType(typeof(CampaignStatsDto), StatusCodes.Status200OK)]
public async Task<ActionResult<CampaignStatsDto>> GetCampaignStats()
{
var stats = await _mediator.Send(new GetCampaignStatsQuery());
return Ok(stats);
}
}
/// <summary>
/// EN: Campaign statistics DTO.
/// VI: DTO thống kê chiến dịch.
/// </summary>
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; }
}
/// <summary>
/// EN: Query to get campaign statistics.
/// VI: Query lấy thống kê chiến dịch.
/// </summary>
public record GetCampaignStatsQuery : IRequest<CampaignStatsDto>;