319 lines
11 KiB
C#
319 lines
11 KiB
C#
using Fengling.Console.Models.Dtos;
|
|
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
|
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
|
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
|
|
using Fengling.Platform.Infrastructure;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Security.Claims;
|
|
using TenantStatus = Fengling.Platform.Domain.AggregatesModel.TenantAggregate.TenantStatus;
|
|
|
|
namespace Fengling.Console.Services;
|
|
|
|
public interface ITenantService
|
|
{
|
|
Task<(IEnumerable<TenantDto> Items, int TotalCount)> GetTenantsAsync(int page, int pageSize, string? name = null,
|
|
string? tenantCode = null, TenantStatus? status = null);
|
|
Task<TenantDto?> GetTenantAsync(long id);
|
|
Task<IEnumerable<UserDto>> GetTenantUsersAsync(long tenantId);
|
|
Task<IEnumerable<object>> GetTenantRolesAsync(long tenantId);
|
|
Task<TenantSettingsDto> GetTenantSettingsAsync(long id);
|
|
Task UpdateTenantSettingsAsync(long id, TenantSettingsDto settings);
|
|
Task<TenantDto> CreateTenantAsync(CreateTenantDto dto);
|
|
Task<TenantDto> UpdateTenantAsync(long id, UpdateTenantDto dto);
|
|
Task DeleteTenantAsync(long id);
|
|
}
|
|
|
|
public class TenantService(
|
|
ITenantManager tenantManager,
|
|
UserManager<ApplicationUser> userManager,
|
|
RoleManager<ApplicationRole> roleManager,
|
|
PlatformDbContext context,
|
|
IHttpContextAccessor httpContextAccessor)
|
|
: ITenantService
|
|
{
|
|
public async Task<(IEnumerable<TenantDto> Items, int TotalCount)> GetTenantsAsync
|
|
(int page, int pageSize, string? name = null, string? tenantCode = null, TenantStatus? status = null)
|
|
{
|
|
var tenants = await tenantManager.GetPagedAsync(page, pageSize, name, tenantCode, status);
|
|
var totalCount = await tenantManager.GetCountAsync(name, tenantCode, status);
|
|
|
|
var tenantDtos = new List<TenantDto>();
|
|
foreach (var tenant in tenants)
|
|
{
|
|
var userCount = await tenantManager.GetUserCountAsync(tenant.Id);
|
|
tenantDtos.Add(new TenantDto
|
|
{
|
|
Id = tenant.Id,
|
|
TenantCode = tenant.TenantCode,
|
|
Name = tenant.Name,
|
|
ContactName = tenant.ContactName,
|
|
ContactEmail = tenant.ContactEmail,
|
|
ContactPhone = tenant.ContactPhone,
|
|
MaxUsers = tenant.MaxUsers,
|
|
UserCount = userCount,
|
|
Status = tenant.Status,
|
|
ExpiresAt = tenant.ExpiresAt,
|
|
Description = tenant.Description,
|
|
CreatedAt = tenant.CreatedAt
|
|
});
|
|
}
|
|
|
|
return (tenantDtos, totalCount);
|
|
}
|
|
|
|
public async Task<TenantDto?> GetTenantAsync(long id)
|
|
{
|
|
var tenant = await tenantManager.FindByIdAsync(id);
|
|
if (tenant == null) return null;
|
|
|
|
return new TenantDto
|
|
{
|
|
Id = tenant.Id,
|
|
TenantCode = tenant.TenantCode,
|
|
Name = tenant.Name,
|
|
ContactName = tenant.ContactName,
|
|
ContactEmail = tenant.ContactEmail,
|
|
ContactPhone = tenant.ContactPhone,
|
|
MaxUsers = tenant.MaxUsers,
|
|
UserCount = await tenantManager.GetUserCountAsync(tenant.Id),
|
|
Status = tenant.Status,
|
|
ExpiresAt = tenant.ExpiresAt,
|
|
Description = tenant.Description,
|
|
CreatedAt = tenant.CreatedAt
|
|
};
|
|
}
|
|
|
|
public async Task<IEnumerable<UserDto>> GetTenantUsersAsync(long tenantId)
|
|
{
|
|
var tenant = await tenantManager.FindByIdAsync(tenantId);
|
|
if (tenant == null)
|
|
{
|
|
throw new KeyNotFoundException($"Tenant with ID {tenantId} not found");
|
|
}
|
|
|
|
var users = await context.Users
|
|
.Where(u => u.TenantInfo!.TenantId == tenantId)
|
|
.ToListAsync();
|
|
|
|
var userDtos = new List<UserDto>();
|
|
|
|
foreach (var user in users)
|
|
{
|
|
var roles = await userManager.GetRolesAsync(user);
|
|
userDtos.Add(new UserDto
|
|
{
|
|
Id = user.Id,
|
|
UserName = user.UserName,
|
|
Email = user.Email,
|
|
RealName = user.RealName,
|
|
TenantCode = user.TenantInfo.TenantId.ToString(),
|
|
TenantName = tenant?.Name ?? "",
|
|
Roles = roles.ToList(),
|
|
EmailConfirmed = user.EmailConfirmed,
|
|
IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
|
|
CreatedAt = user.CreatedTime
|
|
});
|
|
}
|
|
|
|
return userDtos;
|
|
}
|
|
|
|
public async Task<IEnumerable<object>> GetTenantRolesAsync(long tenantId)
|
|
{
|
|
var tenant = await tenantManager.FindByIdAsync(tenantId);
|
|
if (tenant == null)
|
|
{
|
|
throw new KeyNotFoundException($"Tenant with ID {tenantId} not found");
|
|
}
|
|
|
|
var roles = await context.Roles
|
|
.Where(r => r.TenantId == tenantId)
|
|
.ToListAsync();
|
|
|
|
return roles.Select(r => new
|
|
{
|
|
id = r.Id,
|
|
name = r.Name,
|
|
displayName = r.DisplayName
|
|
});
|
|
}
|
|
|
|
public async Task<TenantSettingsDto> GetTenantSettingsAsync(long id)
|
|
{
|
|
var tenant = await tenantManager.FindByIdAsync(id);
|
|
if (tenant == null)
|
|
{
|
|
throw new KeyNotFoundException($"Tenant with ID {id} not found");
|
|
}
|
|
|
|
return new TenantSettingsDto
|
|
{
|
|
AllowRegistration = false,
|
|
AllowedEmailDomains = "",
|
|
DefaultRoleId = null,
|
|
PasswordPolicy = new List<string> { "requireNumber", "requireLowercase" },
|
|
MinPasswordLength = 8,
|
|
SessionTimeout = 120
|
|
};
|
|
}
|
|
|
|
public async Task UpdateTenantSettingsAsync(long id, TenantSettingsDto settings)
|
|
{
|
|
var tenant = await tenantManager.FindByIdAsync(id);
|
|
if (tenant == null)
|
|
{
|
|
throw new KeyNotFoundException($"Tenant with ID {id} not found");
|
|
}
|
|
|
|
await CreateAuditLog("tenant", "update", "TenantSettings", tenant.Id, tenant.Name, null, System.Text.Json.JsonSerializer.Serialize(settings));
|
|
}
|
|
|
|
public async Task<TenantDto> CreateTenantAsync(CreateTenantDto dto)
|
|
{
|
|
var tenant = new Tenant
|
|
{
|
|
TenantCode = dto.TenantCode,
|
|
Name = dto.Name,
|
|
ContactName = dto.ContactName,
|
|
ContactEmail = dto.ContactEmail,
|
|
ContactPhone = dto.ContactPhone,
|
|
MaxUsers = dto.MaxUsers,
|
|
Description = dto.Description,
|
|
ExpiresAt = dto.ExpiresAt,
|
|
Status = TenantStatus.Active,
|
|
CreatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
var result = await tenantManager.CreateAsync(tenant);
|
|
|
|
if (!result.Succeeded)
|
|
{
|
|
var errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
|
throw new InvalidOperationException($"Failed to create tenant: {errors}");
|
|
}
|
|
|
|
await CreateAuditLog("tenant", "create", "Tenant", tenant.Id, tenant.Name, null,
|
|
System.Text.Json.JsonSerializer.Serialize(dto));
|
|
|
|
return new TenantDto
|
|
{
|
|
Id = tenant.Id,
|
|
TenantCode = tenant.TenantCode,
|
|
Name = tenant.Name,
|
|
ContactName = tenant.ContactName,
|
|
ContactEmail = tenant.ContactEmail,
|
|
ContactPhone = tenant.ContactPhone,
|
|
MaxUsers = tenant.MaxUsers,
|
|
UserCount = 0,
|
|
Status = tenant.Status,
|
|
ExpiresAt = tenant.ExpiresAt,
|
|
Description = tenant.Description,
|
|
CreatedAt = tenant.CreatedAt
|
|
};
|
|
}
|
|
|
|
public async Task<TenantDto> UpdateTenantAsync(long id, UpdateTenantDto dto)
|
|
{
|
|
var tenant = await tenantManager.FindByIdAsync(id);
|
|
if (tenant == null)
|
|
{
|
|
throw new KeyNotFoundException($"Tenant with ID {id} not found");
|
|
}
|
|
|
|
var oldValue = System.Text.Json.JsonSerializer.Serialize(tenant);
|
|
|
|
tenant.Name = dto.Name;
|
|
tenant.ContactName = dto.ContactName;
|
|
tenant.ContactEmail = dto.ContactEmail;
|
|
tenant.ContactPhone = dto.ContactPhone;
|
|
tenant.UpdatedAt = DateTime.UtcNow;
|
|
|
|
var result = await tenantManager.UpdateAsync(tenant);
|
|
|
|
if (!result.Succeeded)
|
|
{
|
|
var errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
|
throw new InvalidOperationException($"Failed to update tenant: {errors}");
|
|
}
|
|
|
|
await CreateAuditLog("tenant", "update", "Tenant", tenant.Id, tenant.Name, oldValue,
|
|
System.Text.Json.JsonSerializer.Serialize(tenant));
|
|
|
|
return new TenantDto
|
|
{
|
|
Id = tenant.Id,
|
|
TenantCode = tenant.TenantCode,
|
|
Name = tenant.Name,
|
|
ContactName = tenant.ContactName,
|
|
ContactEmail = tenant.ContactEmail,
|
|
ContactPhone = tenant.ContactPhone,
|
|
MaxUsers = tenant.MaxUsers,
|
|
UserCount = await tenantManager.GetUserCountAsync(tenant.Id),
|
|
Status = tenant.Status,
|
|
ExpiresAt = tenant.ExpiresAt,
|
|
Description = tenant.Description,
|
|
CreatedAt = tenant.CreatedAt
|
|
};
|
|
}
|
|
|
|
public async Task DeleteTenantAsync(long id)
|
|
{
|
|
var tenant = await tenantManager.FindByIdAsync(id);
|
|
if (tenant == null)
|
|
{
|
|
throw new KeyNotFoundException($"Tenant with ID {id} not found");
|
|
}
|
|
|
|
var oldValue = System.Text.Json.JsonSerializer.Serialize(tenant);
|
|
|
|
var users = await context.Users
|
|
.Where(u => u.TenantInfo.TenantId == id)
|
|
.ToListAsync();
|
|
|
|
foreach (var user in users)
|
|
{
|
|
user.IsDeleted = true;
|
|
user.UpdatedTime = DateTime.UtcNow;
|
|
}
|
|
await context.SaveChangesAsync();
|
|
|
|
tenant.IsDeleted = true;
|
|
var result = await tenantManager.UpdateAsync(tenant);
|
|
|
|
if (!result.Succeeded)
|
|
{
|
|
var errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
|
throw new InvalidOperationException($"Failed to delete tenant: {errors}");
|
|
}
|
|
|
|
await CreateAuditLog("tenant", "delete", "Tenant", tenant.Id, tenant.Name, oldValue);
|
|
}
|
|
|
|
private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId, string? targetName, string? oldValue = null, string? newValue = null)
|
|
{
|
|
var httpContext = httpContextAccessor.HttpContext;
|
|
var userName = httpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier) ?? httpContext?.User?.Identity?.Name ?? "system";
|
|
var tenantIdClaim = httpContext?.User?.FindFirstValue("TenantId");
|
|
|
|
var log = new AuditLog
|
|
{
|
|
Operator = userName,
|
|
TenantId = tenantIdClaim,
|
|
Operation = operation,
|
|
Action = action,
|
|
TargetType = targetType,
|
|
TargetId = targetId,
|
|
TargetName = targetName,
|
|
IpAddress = httpContext?.Connection?.RemoteIpAddress?.ToString() ?? "unknown",
|
|
Status = "success",
|
|
OldValue = oldValue,
|
|
NewValue = newValue,
|
|
CreatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
context.AuditLogs.Add(log);
|
|
await context.SaveChangesAsync();
|
|
}
|
|
}
|