using Fengling.AuthService.Data; using Fengling.AuthService.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Security.Claims; using System.Text.Json; using Fengling.Platform.Domain.AggregatesModel.TenantAggregate; using Fengling.Platform.Infrastructure; namespace Fengling.AuthService.Controllers; [ApiController] [Route("api/[controller]")] [Authorize] public class TenantsController( ApplicationDbContext context, UserManager userManager, ILogger logger, PlatformDbContext platformDbContext) : ControllerBase { [HttpGet] public async Task> GetTenants( [FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] string? name = null, [FromQuery] string? tenantCode = null, [FromQuery] TenantStatus? status = null) { var query = platformDbContext.Tenants.AsQueryable(); if (!string.IsNullOrEmpty(name)) { query = query.Where(t => t.Name.Contains(name)); } if (!string.IsNullOrEmpty(tenantCode)) { query = query.Where(t => t.TenantCode.Contains(tenantCode)); } if (status.HasValue) { query = query.Where(t => t.Status == status); } var totalCount = await query.CountAsync(); var tenants = await query .OrderByDescending(t => t.CreatedAt) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); var result = new List(); foreach (var tenant in tenants) { var userCount = await context.Users.CountAsync(u => u.TenantInfo.TenantId == tenant.Id && !u.IsDeleted); result.Add(new { id = tenant.Id, tenantId = tenant.Id, name = tenant.Name, contactName = tenant.ContactName, contactEmail = tenant.ContactEmail, contactPhone = tenant.ContactPhone, maxUsers = tenant.MaxUsers, userCount, status = tenant.Status, expiresAt = tenant.ExpiresAt, description = tenant.Description, createdAt = tenant.CreatedAt, }); } return Ok(new { items = result, totalCount, page, pageSize }); } [HttpGet("{id}")] public async Task> GetTenant(TenantId id) { var tenant = await platformDbContext.Tenants.FindAsync(id); if (tenant == null) { return NotFound(); } return Ok(new { id = tenant.Id, tenantId = tenant.Id, name = tenant.Name, contactName = tenant.ContactName, contactEmail = tenant.ContactEmail, contactPhone = tenant.ContactPhone, maxUsers = tenant.MaxUsers, status = tenant.Status, expiresAt = tenant.ExpiresAt, description = tenant.Description, createdAt = tenant.CreatedAt, updatedAt = tenant.UpdatedAt, }); } [HttpGet("{tenantId}/users")] public async Task>> GetTenantUsers(TenantId tenantId) { var tenant = await platformDbContext.Tenants .FirstOrDefaultAsync(t => t.Id == tenantId); if (tenant == null) { return NotFound(); } var users = await context.Users .Where(u => u.TenantInfo.TenantId == tenant.Id && !u.IsDeleted) .ToListAsync(); var result = users.Select(async u => new { id = u.Id, userName = u.UserName, email = u.Email, realName = u.RealName, tenantId = u.TenantInfo.TenantId, roles = await userManager.GetRolesAsync(u), isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow, createdAt = u.CreatedTime, }); return Ok(await Task.WhenAll(result)); } [HttpGet("{tenantId}/roles")] public async Task>> GetTenantRoles(TenantId tenantId) { var tenant = await platformDbContext.Tenants .FirstOrDefaultAsync(t => t.Id == tenantId); if (tenant == null) { return NotFound(); } var roles = await context.Roles .Where(r => r.TenantId == tenant.Id) .ToListAsync(); var result = roles.Select(r => new { id = r.Id, name = r.Name, displayName = r.DisplayName, }); return Ok(result); } [HttpGet("{tenantId}/settings")] public async Task> GetTenantSettings(TenantId tenantId) { var tenant = await platformDbContext.Tenants .FirstOrDefaultAsync(t => t.Id == tenantId); if (tenant == null) { return NotFound(); } var settings = new TenantSettings { AllowRegistration = false, AllowedEmailDomains = "", DefaultRoleId = null, PasswordPolicy = new List { "requireNumber", "requireLowercase" }, MinPasswordLength = 8, SessionTimeout = 120, }; return Ok(settings); } [HttpPut("{tenantId}/settings")] public async Task UpdateTenantSettings(TenantId tenantId, TenantSettings settings) { var tenant = await platformDbContext.Tenants.FirstOrDefaultAsync(t => t.Id == tenantId); if (tenant == null) { return NotFound(); } await CreateAuditLog("tenant", "update", "TenantSettings", tenant.Id, tenant.Name, null, JsonSerializer.Serialize(settings)); return NoContent(); } [HttpPost] public async Task> CreateTenant(CreateTenantDto dto) { var tenant = new Tenant(dto.TenantCode, dto.TenantName, dto.ContactName, dto.ContactEmail, dto.ContactPhone, dto.MaxUsers, dto.Description, dto.ExpiresAt); platformDbContext.Tenants.Add(tenant); await context.SaveChangesAsync(); await CreateAuditLog("tenant", "create", "Tenant", tenant.Id, tenant.TenantCode, null, JsonSerializer.Serialize(dto)); return CreatedAtAction(nameof(GetTenant), new { id = tenant.Id }, tenant); } [HttpPut("{id}")] public async Task UpdateTenant(long id, UpdateTenantDto dto) { var tenant = await platformDbContext.Tenants.FindAsync(id); if (tenant == null) { return NotFound(); } var oldValue = JsonSerializer.Serialize(tenant); tenant.UpdateInfo(dto.Name, dto.ContactName, dto.ContactEmail, dto.ContactPhone); await context.SaveChangesAsync(); await CreateAuditLog("tenant", "update", "Tenant", tenant.Id, tenant.Name, oldValue, JsonSerializer.Serialize(tenant)); return NoContent(); } [HttpDelete("{id}")] public async Task DeleteTenant(long id) { var tenant = await platformDbContext.Tenants.FindAsync(id); if (tenant == null) { return NotFound(); } var oldValue = JsonSerializer.Serialize(tenant); var users = await context.Users.Where(u => u.TenantInfo.TenantId == tenant.Id).ToListAsync(); foreach (var user in users) { user.IsDeleted = true; user.UpdatedTime = DateTime.UtcNow; } tenant.Delete(); await context.SaveChangesAsync(); await CreateAuditLog("tenant", "delete", "Tenant", tenant.Id, tenant.Name, oldValue); return NoContent(); } private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId, string? targetName, string? oldValue = null, string? newValue = null) { var userName = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.Identity?.Name ?? "system"; var tenantId = User.FindFirstValue("TenantId"); var log = new AuditLog { Operator = userName, TenantId = tenantId, Operation = operation, Action = action, TargetType = targetType, TargetId = targetId, TargetName = targetName, IpAddress = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown", Status = "success", OldValue = oldValue, NewValue = newValue, }; context.AuditLogs.Add(log); await context.SaveChangesAsync(); } } public record CreateTenantDto( string TenantCode, string TenantName, string ContactName, string ContactEmail, string ContactPhone, int? MaxUsers, string? Description, string Status, DateTime? ExpiresAt); public class UpdateTenantDto { public string Name { get; set; } = string.Empty; public string ContactName { get; set; } = string.Empty; public string ContactEmail { get; set; } = string.Empty; public string? ContactPhone { get; set; } public int? MaxUsers { get; set; } public string? Description { get; set; } public string Status { get; set; } = "active"; public DateTime? ExpiresAt { get; set; } } public class TenantSettings { public bool AllowRegistration { get; set; } public string AllowedEmailDomains { get; set; } = string.Empty; public long? DefaultRoleId { get; set; } public List PasswordPolicy { get; set; } = new(); public int MinPasswordLength { get; set; } = 8; public int SessionTimeout { get; set; } = 120; }