using Microsoft.AspNetCore.Mvc; using Npgsql; using Dapper; namespace WebClientTpos.Server.Controllers; [ApiController] [Route("api/bff")] public class BffDataController : ControllerBase { // EN: DB host configurable via env var (Docker: "postgres", dev: "localhost") // VI: DB host cấu hình qua env var (Docker: "postgres", dev: "localhost") private static readonly string _dbHost = Environment.GetEnvironmentVariable("BFF_DB_HOST") ?? "localhost"; private static readonly string _dbPort = Environment.GetEnvironmentVariable("BFF_DB_PORT") ?? "5432"; private static readonly string _dbUser = Environment.GetEnvironmentVariable("BFF_DB_USER") ?? "goodgo"; private static readonly string _dbPass = Environment.GetEnvironmentVariable("BFF_DB_PASS") ?? "goodgo_dev_2024"; private static string ConnStr(string db) => $"Host={_dbHost};Port={_dbPort};Database={db};Username={_dbUser};Password={_dbPass}"; [HttpGet("shops")] public async Task GetShops() { await using var conn = new NpgsqlConnection(ConnStr("merchant_service")); var shops = await conn.QueryAsync( @"SELECT s.id, s.name, s.slug, s.description, s.phone, s.email, s.open_time, s.close_time, s.features_config, bc.name as category, st.name as status FROM shops s JOIN business_categories bc ON s.category_id = bc.id JOIN shop_statuses st ON s.status_id = st.id WHERE s.is_deleted = false ORDER BY s.name"); return Ok(shops); } [HttpGet("shops/{shopId:guid}")] public async Task GetShopById(Guid shopId) { await using var conn = new NpgsqlConnection(ConnStr("merchant_service")); var shop = await conn.QueryFirstOrDefaultAsync( @"SELECT s.id, s.name, s.slug, s.description, s.phone, s.email, s.open_time, s.close_time, bc.name as category, st.name as status FROM shops s JOIN business_categories bc ON s.category_id = bc.id JOIN shop_statuses st ON s.status_id = st.id WHERE s.id = @ShopId AND s.is_deleted = false", new { ShopId = shopId }); if (shop == null) return NotFound(new { message = "Shop not found" }); return Ok(shop); } [HttpGet("staff")] public async Task GetStaff() { await using var conn = new NpgsqlConnection(ConnStr("merchant_service")); var staff = await conn.QueryAsync( @"SELECT ms.id, ms.user_id, ms.employee_code, ms.phone, ms.email, ms.joined_at, ms.terminated_at, sr.name as role, ss.name as status, s.name as shop_name FROM merchant_staff ms JOIN staff_roles sr ON ms.role_id = sr.id JOIN staff_statuses ss ON ms.status_id = ss.id LEFT JOIN shop_members sm ON sm.staff_id = ms.id LEFT JOIN shops s ON sm.shop_id = s.id ORDER BY ms.joined_at DESC"); return Ok(staff); } [HttpGet("shops/{shopId}/products")] public async Task GetProducts(Guid shopId) { await using var conn = new NpgsqlConnection(ConnStr("catalog_service")); var products = await conn.QueryAsync( @"SELECT id, name, price, sku, description, image_url, is_active, attributes->>'category' as category, (attributes->>'duration')::int as duration_minutes FROM products WHERE shop_id = @ShopId AND is_active = true ORDER BY name", new { ShopId = shopId }); return Ok(products); } [HttpGet("shops/{shopId}/categories")] public async Task GetCategories(Guid shopId) { await using var conn = new NpgsqlConnection(ConnStr("catalog_service")); var categories = await conn.QueryAsync( @"SELECT id, name, description, display_order FROM categories WHERE shop_id = @ShopId AND is_active = true ORDER BY display_order", new { ShopId = shopId }); return Ok(categories); } [HttpGet("shops/{shopId}/tables")] public async Task GetTables(Guid shopId) { await using var conn = new NpgsqlConnection(ConnStr("fnb_engine")); var tables = await conn.QueryAsync( @"SELECT t.id, t.table_number, t.capacity, t.zone, CASE t.status_id WHEN 1 THEN 'available' WHEN 2 THEN 'occupied' WHEN 3 THEN 'reserved' WHEN 4 THEN 'cleaning' END as status, s.id as session_id, s.guest_count, s.started_at FROM tables t LEFT JOIN sessions s ON s.table_id = t.id AND s.status = 'Active' WHERE t.shop_id = @ShopId ORDER BY t.table_number", new { ShopId = shopId }); return Ok(tables); } [HttpGet("shops/{shopId}/appointments")] public async Task GetAppointments(Guid shopId) { await using var conn = new NpgsqlConnection(ConnStr("booking_service")); var appointments = await conn.QueryAsync( @"SELECT a.id, a.customer_id, a.staff_id, a.resource_id, a.service_id, a.start_time, a.end_time, a.status, r.name as resource_name FROM appointments a LEFT JOIN resources r ON a.resource_id = r.id WHERE a.shop_id = @ShopId ORDER BY a.start_time", new { ShopId = shopId }); return Ok(appointments); } [HttpGet("shops/{shopId}/resources")] public async Task GetResources(Guid shopId) { await using var conn = new NpgsqlConnection(ConnStr("booking_service")); var resources = await conn.QueryAsync( @"SELECT id, name, resource_type, capacity, is_active FROM resources WHERE shop_id = @ShopId AND is_active = true ORDER BY name", new { ShopId = shopId }); return Ok(resources); } }