From 39cc9a8538fd059c5bc220324160ec80a15d713e Mon Sep 17 00:00:00 2001 From: movingsam Date: Wed, 18 Feb 2026 23:00:09 +0800 Subject: [PATCH] feat(auth): extract Tenant to Platform domain - Add Fengling.Platform domain and infrastructure projects - Move Tenant aggregate from AuthService/Console to Platform.Domain - Add TenantRepository and SeedData to Platform - Remove duplicate Tenant/TenantInfo models from AuthService and Console - Update controllers and services to use Platform.Domain.Tenant - Add new migrations for PlatformDbContext BREAKING CHANGE: Tenant entity now uses strongly-typed ID (TenantId) --- Controllers/AccountController.cs | 47 +++-- Controllers/StatsController.cs | 29 ++-- Controllers/TenantsController.cs | 159 ++++++++--------- Controllers/UsersController.cs | 88 +++++----- Data/ApplicationDbContext.cs | 20 +-- Data/SeedData.cs | 51 ++---- Fengling.AuthService.csproj | 5 + ....cs => 20260218145654_Initial.Designer.cs} | 161 ++---------------- ...20_inital.cs => 20260218145654_Initial.cs} | 69 +------- .../ApplicationDbContextModelSnapshot.cs | 89 ++-------- Models/Tenant.cs | 63 ------- Models/TenantInfo.cs | 3 - 12 files changed, 192 insertions(+), 592 deletions(-) rename Migrations/{20260206142720_inital.Designer.cs => 20260218145654_Initial.Designer.cs} (83%) rename Migrations/{20260206142720_inital.cs => 20260218145654_Initial.cs} (85%) delete mode 100644 Models/Tenant.cs delete mode 100644 Models/TenantInfo.cs diff --git a/Controllers/AccountController.cs b/Controllers/AccountController.cs index f9e82c4..94f61a0 100644 --- a/Controllers/AccountController.cs +++ b/Controllers/AccountController.cs @@ -1,6 +1,8 @@ using Fengling.AuthService.Data; using Fengling.AuthService.Models; using Fengling.AuthService.ViewModels; +using Fengling.Platform.Domain.AggregatesModel.TenantAggregate; +using Fengling.Platform.Infrastructure; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Identity; @@ -10,25 +12,14 @@ using Microsoft.EntityFrameworkCore; namespace Fengling.AuthService.Controllers; [Route("account")] -public class AccountController : Controller +public class AccountController( + UserManager userManager, + SignInManager signInManager, + ApplicationDbContext dbContext, + ILogger logger, + PlatformDbContext platformDbContext) + : Controller { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly ApplicationDbContext _dbContext; - private readonly ILogger _logger; - - public AccountController( - UserManager userManager, - SignInManager signInManager, - ApplicationDbContext dbContext, - ILogger logger) - { - _userManager = userManager; - _signInManager = signInManager; - _dbContext = dbContext; - _logger = logger; - } - [HttpGet("login")] public IActionResult Login(string returnUrl = "/") { @@ -44,14 +35,14 @@ public class AccountController : Controller return View(model); } - var user = await _userManager.FindByNameAsync(model.Username); + var user = await userManager.FindByNameAsync(model.Username); if (user == null || user.IsDeleted) { ModelState.AddModelError(string.Empty, "用户名或密码错误"); return View(model); } - var result = await _signInManager.PasswordSignInAsync(user, model.Password, model.RememberMe, true); + var result = await signInManager.PasswordSignInAsync(user, model.Password, model.RememberMe, true); if (!result.Succeeded) { if (result.IsLockedOut) @@ -83,12 +74,12 @@ public class AccountController : Controller return View(model); } - var defaultTenant = await _dbContext.Tenants - .FirstOrDefaultAsync(t => t.TenantId == "default"); + var tenant = await platformDbContext.Tenants + .FirstOrDefaultAsync(t => t.TenantCode == model.TenantCode); - if (defaultTenant == null) + if (tenant == null) { - ModelState.AddModelError(string.Empty, "系统配置错误:未找到默认租户"); + ModelState.AddModelError(string.Empty, $"系统配置错误:未找到租户{model.TenantCode}"); return View(model); } @@ -98,10 +89,10 @@ public class AccountController : Controller Email = model.Email, NormalizedUserName = model.Username.ToUpper(), NormalizedEmail = model.Email.ToUpper(), - TenantInfo = new TenantInfo(defaultTenant.Id, defaultTenant.TenantId, defaultTenant.Name) + TenantInfo = new TenantInfo(tenant) }; - var result = await _userManager.CreateAsync(user, model.Password); + var result = await userManager.CreateAsync(user, model.Password); if (!result.Succeeded) { foreach (var error in result.Errors) @@ -111,7 +102,7 @@ public class AccountController : Controller return View(model); } - await _signInManager.SignInAsync(user, isPersistent: false); + await signInManager.SignInAsync(user, isPersistent: false); return LocalRedirect(model.ReturnUrl); } @@ -127,7 +118,7 @@ public class AccountController : Controller [ValidateAntiForgeryToken] public async Task LogoutPost() { - await _signInManager.SignOutAsync(); + await signInManager.SignOutAsync(); return Redirect("/"); } } diff --git a/Controllers/StatsController.cs b/Controllers/StatsController.cs index 90ce1aa..0dba0ee 100644 --- a/Controllers/StatsController.cs +++ b/Controllers/StatsController.cs @@ -1,5 +1,6 @@ using Fengling.AuthService.Data; using Fengling.AuthService.Models; +using Fengling.Platform.Infrastructure; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -10,21 +11,13 @@ namespace Fengling.AuthService.Controllers; [ApiController] [Route("api/[controller]")] [Authorize] -public class StatsController : ControllerBase +public class StatsController( + ApplicationDbContext context, + IOpenIddictApplicationManager applicationManager, + ILogger logger, + PlatformDbContext platformDbContext) + : ControllerBase { - private readonly ApplicationDbContext _context; - private readonly IOpenIddictApplicationManager _applicationManager; - private readonly ILogger _logger; - - public StatsController( - ApplicationDbContext context, - IOpenIddictApplicationManager applicationManager, - ILogger logger) - { - _context = context; - _applicationManager = applicationManager; - _logger = logger; - } [HttpGet("dashboard")] public async Task> GetDashboardStats() @@ -32,10 +25,10 @@ public class StatsController : ControllerBase var today = DateTime.UtcNow.Date; var tomorrow = today.AddDays(1); - var userCount = await _context.Users.CountAsync(u => !u.IsDeleted); - var tenantCount = await _context.Tenants.CountAsync(t => !t.IsDeleted); + var userCount = await context.Users.CountAsync(u => !u.IsDeleted); + var tenantCount = await platformDbContext.Tenants.CountAsync(t => !t.Deleted); var oauthClientCount = await CountOAuthClientsAsync(); - var todayAccessCount = await _context.AccessLogs + var todayAccessCount = await context.AccessLogs .CountAsync(l => l.CreatedAt >= today && l.CreatedAt < tomorrow); return Ok(new @@ -50,7 +43,7 @@ public class StatsController : ControllerBase private async Task CountOAuthClientsAsync() { var count = 0; - var applications = _applicationManager.ListAsync(); + var applications = applicationManager.ListAsync(); await foreach (var _ in applications) { count++; diff --git a/Controllers/TenantsController.cs b/Controllers/TenantsController.cs index 686443c..27285b3 100644 --- a/Controllers/TenantsController.cs +++ b/Controllers/TenantsController.cs @@ -6,49 +6,42 @@ 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 : ControllerBase +public class TenantsController( + ApplicationDbContext context, + UserManager userManager, + ILogger logger, + PlatformDbContext platformDbContext) + : ControllerBase { - private readonly ApplicationDbContext _context; - private readonly UserManager _userManager; - private readonly ILogger _logger; - - public TenantsController( - ApplicationDbContext context, - UserManager userManager, - ILogger logger) - { - _context = context; - _userManager = userManager; - _logger = logger; - } - [HttpGet] public async Task> GetTenants( [FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] string? name = null, - [FromQuery] string? tenantId = null, - [FromQuery] string? status = null) + [FromQuery] string? tenantCode = null, + [FromQuery] TenantStatus? status = null) { - var query = _context.Tenants.AsQueryable(); + var query = platformDbContext.Tenants.AsQueryable(); if (!string.IsNullOrEmpty(name)) { query = query.Where(t => t.Name.Contains(name)); } - if (!string.IsNullOrEmpty(tenantId)) + if (!string.IsNullOrEmpty(tenantCode)) { - query = query.Where(t => t.TenantId.Contains(tenantId)); + query = query.Where(t => t.TenantCode.Contains(tenantCode)); } - if (!string.IsNullOrEmpty(status)) + if (status.HasValue) { query = query.Where(t => t.Status == status); } @@ -64,11 +57,11 @@ public class TenantsController : ControllerBase foreach (var tenant in tenants) { - var userCount = await _context.Users.CountAsync(u => u.TenantInfo.Id == tenant.Id && !u.IsDeleted); + var userCount = await context.Users.CountAsync(u => u.TenantInfo.TenantId == tenant.Id && !u.IsDeleted); result.Add(new { id = tenant.Id, - tenantId = tenant.TenantId, + tenantId = tenant.Id, name = tenant.Name, contactName = tenant.ContactName, contactEmail = tenant.ContactEmail, @@ -92,9 +85,9 @@ public class TenantsController : ControllerBase } [HttpGet("{id}")] - public async Task> GetTenant(long id) + public async Task> GetTenant(TenantId id) { - var tenant = await _context.Tenants.FindAsync(id); + var tenant = await platformDbContext.Tenants.FindAsync(id); if (tenant == null) { return NotFound(); @@ -103,7 +96,7 @@ public class TenantsController : ControllerBase return Ok(new { id = tenant.Id, - tenantId = tenant.TenantId, + tenantId = tenant.Id, name = tenant.Name, contactName = tenant.ContactName, contactEmail = tenant.ContactEmail, @@ -118,16 +111,19 @@ public class TenantsController : ControllerBase } [HttpGet("{tenantId}/users")] - public async Task>> GetTenantUsers(string tenantId) + public async Task>> GetTenantUsers(TenantId tenantId) { - var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.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.Id == tenant.Id && !u.IsDeleted) + var users = await context.Users + .Where(u => + u.TenantInfo.TenantId == tenant.Id + && !u.IsDeleted) .ToListAsync(); var result = users.Select(async u => new @@ -136,8 +132,8 @@ public class TenantsController : ControllerBase userName = u.UserName, email = u.Email, realName = u.RealName, - tenantId = u.TenantInfo.Id, - roles = await _userManager.GetRolesAsync(u), + tenantId = u.TenantInfo.TenantId, + roles = await userManager.GetRolesAsync(u), isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow, createdAt = u.CreatedTime, }); @@ -146,15 +142,16 @@ public class TenantsController : ControllerBase } [HttpGet("{tenantId}/roles")] - public async Task>> GetTenantRoles(string tenantId) + public async Task>> GetTenantRoles(TenantId tenantId) { - var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.TenantId == tenantId); + var tenant = await platformDbContext.Tenants + .FirstOrDefaultAsync(t => t.Id == tenantId); if (tenant == null) { return NotFound(); } - var roles = await _context.Roles + var roles = await context.Roles .Where(r => r.TenantId == tenant.Id) .ToListAsync(); @@ -169,9 +166,10 @@ public class TenantsController : ControllerBase } [HttpGet("{tenantId}/settings")] - public async Task> GetTenantSettings(string tenantId) + public async Task> GetTenantSettings(TenantId tenantId) { - var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.TenantId == tenantId); + var tenant = await platformDbContext.Tenants + .FirstOrDefaultAsync(t => t.Id == tenantId); if (tenant == null) { return NotFound(); @@ -191,15 +189,16 @@ public class TenantsController : ControllerBase } [HttpPut("{tenantId}/settings")] - public async Task UpdateTenantSettings(string tenantId, TenantSettings settings) + public async Task UpdateTenantSettings(TenantId tenantId, TenantSettings settings) { - var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.TenantId == tenantId); + var tenant = await platformDbContext.Tenants.FirstOrDefaultAsync(t => t.Id == tenantId); if (tenant == null) { return NotFound(); } - await CreateAuditLog("tenant", "update", "TenantSettings", tenant.Id, tenant.TenantId, null, JsonSerializer.Serialize(settings)); + await CreateAuditLog("tenant", "update", "TenantSettings", tenant.Id, tenant.Name, null, + JsonSerializer.Serialize(settings)); return NoContent(); } @@ -207,24 +206,14 @@ public class TenantsController : ControllerBase [HttpPost] public async Task> CreateTenant(CreateTenantDto dto) { - var tenant = new Tenant - { - TenantId = dto.TenantId, - Name = dto.Name, - ContactName = dto.ContactName, - ContactEmail = dto.ContactEmail, - ContactPhone = dto.ContactPhone, - MaxUsers = dto.MaxUsers, - Description = dto.Description, - Status = dto.Status, - ExpiresAt = dto.ExpiresAt, - CreatedAt = DateTime.UtcNow, - }; + var tenant = new Tenant(dto.TenantCode, dto.TenantName, dto.ContactName, dto.ContactEmail, dto.ContactPhone, + dto.MaxUsers, dto.Description, dto.ExpiresAt); - _context.Tenants.Add(tenant); - await _context.SaveChangesAsync(); + platformDbContext.Tenants.Add(tenant); + await context.SaveChangesAsync(); - await CreateAuditLog("tenant", "create", "Tenant", tenant.Id, tenant.TenantId, null, JsonSerializer.Serialize(dto)); + await CreateAuditLog("tenant", "create", "Tenant", tenant.Id, tenant.TenantCode, + null, JsonSerializer.Serialize(dto)); return CreatedAtAction(nameof(GetTenant), new { id = tenant.Id }, tenant); } @@ -232,27 +221,20 @@ public class TenantsController : ControllerBase [HttpPut("{id}")] public async Task UpdateTenant(long id, UpdateTenantDto dto) { - var tenant = await _context.Tenants.FindAsync(id); + 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); - tenant.Name = dto.Name; - tenant.ContactName = dto.ContactName; - tenant.ContactEmail = dto.ContactEmail; - tenant.ContactPhone = dto.ContactPhone; - tenant.MaxUsers = dto.MaxUsers; - tenant.Description = dto.Description; - tenant.Status = dto.Status; - tenant.ExpiresAt = dto.ExpiresAt; - tenant.UpdatedAt = DateTime.UtcNow; - await _context.SaveChangesAsync(); + await context.SaveChangesAsync(); - await CreateAuditLog("tenant", "update", "Tenant", tenant.Id, tenant.TenantId, oldValue, JsonSerializer.Serialize(tenant)); + await CreateAuditLog("tenant", "update", "Tenant", tenant.Id, tenant.Name, oldValue, + JsonSerializer.Serialize(tenant)); return NoContent(); } @@ -260,7 +242,7 @@ public class TenantsController : ControllerBase [HttpDelete("{id}")] public async Task DeleteTenant(long id) { - var tenant = await _context.Tenants.FindAsync(id); + var tenant = await platformDbContext.Tenants.FindAsync(id); if (tenant == null) { return NotFound(); @@ -268,22 +250,23 @@ public class TenantsController : ControllerBase var oldValue = JsonSerializer.Serialize(tenant); - var users = await _context.Users.Where(u => u.TenantInfo.Id == tenant.Id).ToListAsync(); + 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.IsDeleted = true; - await _context.SaveChangesAsync(); + tenant.Delete(); + await context.SaveChangesAsync(); - await CreateAuditLog("tenant", "delete", "Tenant", tenant.Id, tenant.TenantId, oldValue); + 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) + 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"); @@ -303,23 +286,21 @@ public class TenantsController : ControllerBase NewValue = newValue, }; - _context.AuditLogs.Add(log); - await _context.SaveChangesAsync(); + context.AuditLogs.Add(log); + await context.SaveChangesAsync(); } } -public class CreateTenantDto -{ - public string TenantId { get; set; } = string.Empty; - 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 record CreateTenantDto( + string TenantCode, + string TenantName, + string ContactName, + string ContactEmail, + string ContactPhone, + int? MaxUsers, + string? Description, + string Status, + DateTime? ExpiresAt); public class UpdateTenantDto { @@ -341,4 +322,4 @@ public class TenantSettings public List PasswordPolicy { get; set; } = new(); public int MinPasswordLength { get; set; } = 8; public int SessionTimeout { get; set; } = 120; -} +} \ No newline at end of file diff --git a/Controllers/UsersController.cs b/Controllers/UsersController.cs index 9781eed..ba5c035 100644 --- a/Controllers/UsersController.cs +++ b/Controllers/UsersController.cs @@ -5,30 +5,22 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Security.Claims; +using Fengling.Platform.Domain.AggregatesModel.TenantAggregate; +using Fengling.Platform.Infrastructure; namespace Fengling.AuthService.Controllers; [ApiController] [Route("api/[controller]")] [Authorize] -public class UsersController : ControllerBase +public class UsersController( + ApplicationDbContext context, + UserManager userManager, + RoleManager roleManager, + ILogger logger, + PlatformDbContext platformDbContext) + : ControllerBase { - private readonly ApplicationDbContext _context; - private readonly UserManager _userManager; - private readonly RoleManager _roleManager; - private readonly ILogger _logger; - - public UsersController( - ApplicationDbContext context, - UserManager userManager, - RoleManager roleManager, - ILogger logger) - { - _context = context; - _userManager = userManager; - _roleManager = roleManager; - _logger = logger; - } [HttpGet] public async Task> GetUsers( @@ -36,9 +28,9 @@ public class UsersController : ControllerBase [FromQuery] int pageSize = 10, [FromQuery] string? userName = null, [FromQuery] string? email = null, - [FromQuery] string? tenantId = null) + [FromQuery] string? tenantCode = null) { - var query = _context.Users.AsQueryable(); + var query = context.Users.AsQueryable(); if (!string.IsNullOrEmpty(userName)) { @@ -50,9 +42,9 @@ public class UsersController : ControllerBase query = query.Where(u => u.Email != null && u.Email.Contains(email)); } - if (!string.IsNullOrEmpty(tenantId)) + if (!string.IsNullOrEmpty(tenantCode)) { - query = query.Where(u => u.TenantInfo.Id.ToString() == tenantId); + query = query.Where(u => u.TenantInfo.TenantCode == tenantCode); } var totalCount = await query.CountAsync(); @@ -69,8 +61,8 @@ public class UsersController : ControllerBase email = u.Email, realName = u.RealName, phone = u.Phone, - tenantId = u.TenantInfo.Id, - roles = (await _userManager.GetRolesAsync(u)).ToList(), + tenantId = u.TenantInfo.TenantId, + roles = (await userManager.GetRolesAsync(u)).ToList(), emailConfirmed = u.EmailConfirmed, isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow, createdAt = u.CreatedTime, @@ -88,13 +80,13 @@ public class UsersController : ControllerBase [HttpGet("{id}")] public async Task> GetUser(long id) { - var user = await _context.Users.FindAsync(id); + var user = await context.Users.FindAsync(id); if (user == null) { return NotFound(); } - var roles = await _userManager.GetRolesAsync(user); + var roles = await userManager.GetRolesAsync(user); return Ok(new { @@ -103,7 +95,7 @@ public class UsersController : ControllerBase email = user.Email, realName = user.RealName, phone = user.Phone, - tenantId = user.TenantInfo.Id, + tenantId = user.TenantInfo.TenantId, roles, emailConfirmed = user.EmailConfirmed, isActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, @@ -115,11 +107,11 @@ public class UsersController : ControllerBase public async Task> CreateUser(CreateUserDto dto) { var tenantId = dto.TenantId ?? 0; - Tenant tenant = null; + Tenant? tenant = null; if (tenantId != 0) { - tenant = await _context.Tenants.FindAsync(tenantId); + tenant = await platformDbContext.Tenants.FindAsync(tenantId); if (tenant == null) { return BadRequest("Invalid tenant ID"); @@ -132,33 +124,33 @@ public class UsersController : ControllerBase Email = dto.Email, RealName = dto.RealName, Phone = dto.Phone, - TenantInfo = new TenantInfo(tenantId, tenant?.TenantId ?? "default", tenant?.Name ?? "默认租户"), + TenantInfo = new TenantInfo(tenant!), EmailConfirmed = dto.EmailConfirmed, CreatedTime = DateTime.UtcNow, }; - var result = await _userManager.CreateAsync(user, dto.Password); + var result = await userManager.CreateAsync(user, dto.Password); if (!result.Succeeded) { return BadRequest(result.Errors); } - if (dto.RoleIds != null && dto.RoleIds.Any()) + if (dto.RoleIds.Any()) { foreach (var roleId in dto.RoleIds) { - var role = await _roleManager.FindByIdAsync(roleId.ToString()); + var role = await roleManager.FindByIdAsync(roleId.ToString()); if (role != null) { - await _userManager.AddToRoleAsync(user, role.Name!); + await userManager.AddToRoleAsync(user, role.Name!); } } } if (!dto.IsActive) { - await _userManager.SetLockoutEnabledAsync(user, true); - await _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue); + await userManager.SetLockoutEnabledAsync(user, true); + await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue); } await CreateAuditLog("user", "create", "User", user.Id, user.UserName, null, SerializeToJson(dto)); @@ -169,7 +161,7 @@ public class UsersController : ControllerBase [HttpPut("{id}")] public async Task UpdateUser(long id, UpdateUserDto dto) { - var user = await _context.Users.FindAsync(id); + var user = await context.Users.FindAsync(id); if (user == null) { return NotFound(); @@ -185,16 +177,16 @@ public class UsersController : ControllerBase if (dto.IsActive) { - await _userManager.SetLockoutEnabledAsync(user, false); - await _userManager.SetLockoutEndDateAsync(user, null); + await userManager.SetLockoutEnabledAsync(user, false); + await userManager.SetLockoutEndDateAsync(user, null); } else { - await _userManager.SetLockoutEnabledAsync(user, true); - await _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue); + await userManager.SetLockoutEnabledAsync(user, true); + await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue); } - await _context.SaveChangesAsync(); + await context.SaveChangesAsync(); await CreateAuditLog("user", "update", "User", user.Id, user.UserName, oldValue, System.Text.Json.JsonSerializer.Serialize(user)); @@ -204,14 +196,14 @@ public class UsersController : ControllerBase [HttpPut("{id}/password")] public async Task ResetPassword(long id, ResetPasswordDto dto) { - var user = await _userManager.FindByIdAsync(id.ToString()); + var user = await userManager.FindByIdAsync(id.ToString()); if (user == null) { return NotFound(); } - var token = await _userManager.GeneratePasswordResetTokenAsync(user); - var result = await _userManager.ResetPasswordAsync(user, token, dto.NewPassword); + var token = await userManager.GeneratePasswordResetTokenAsync(user); + var result = await userManager.ResetPasswordAsync(user, token, dto.NewPassword); if (!result.Succeeded) { @@ -226,7 +218,7 @@ public class UsersController : ControllerBase [HttpDelete("{id}")] public async Task DeleteUser(long id) { - var user = await _context.Users.FindAsync(id); + var user = await context.Users.FindAsync(id); if (user == null) { return NotFound(); @@ -235,7 +227,7 @@ public class UsersController : ControllerBase var oldValue = System.Text.Json.JsonSerializer.Serialize(user); user.IsDeleted = true; user.UpdatedTime = DateTime.UtcNow; - await _context.SaveChangesAsync(); + await context.SaveChangesAsync(); await CreateAuditLog("user", "delete", "User", user.Id, user.UserName, oldValue); @@ -262,8 +254,8 @@ public class UsersController : ControllerBase NewValue = newValue, }; - _context.AuditLogs.Add(log); - await _context.SaveChangesAsync(); + context.AuditLogs.Add(log); + await context.SaveChangesAsync(); } private string SerializeToJson(object obj) diff --git a/Data/ApplicationDbContext.cs b/Data/ApplicationDbContext.cs index b5d9b46..93ae24e 100644 --- a/Data/ApplicationDbContext.cs +++ b/Data/ApplicationDbContext.cs @@ -7,7 +7,6 @@ namespace Fengling.AuthService.Data; public class ApplicationDbContext(DbContextOptions options) : IdentityDbContext(options) { - public DbSet Tenants { get; set; } public DbSet AccessLogs { get; set; } public DbSet AuditLogs { get; set; } @@ -23,27 +22,16 @@ public class ApplicationDbContext(DbContextOptions options entity.OwnsOne(e => e.TenantInfo, navigationBuilder => { - navigationBuilder.Property(e => e.Id).HasColumnName("TenantId"); - navigationBuilder.Property(e => e.TenantId).HasColumnName("TenantCode"); - navigationBuilder.Property(e => e.Name).HasColumnName("TenantName"); + navigationBuilder.Property(e => e.TenantCode).HasColumnName("TenantCode"); + navigationBuilder.Property(e => e.TenantId).HasColumnName("TenantId"); + navigationBuilder.Property(e => e.TenantName).HasColumnName("TenantName"); navigationBuilder.WithOwner(); }); }); builder.Entity(entity => { entity.Property(e => e.Description).HasMaxLength(200); }); - builder.Entity(entity => - { - entity.HasKey(e => e.Id); - entity.HasIndex(e => e.TenantId).IsUnique(); - entity.Property(e => e.TenantId).HasMaxLength(50); - entity.Property(e => e.Name).HasMaxLength(100); - entity.Property(e => e.ContactName).HasMaxLength(50); - entity.Property(e => e.ContactEmail).HasMaxLength(100); - entity.Property(e => e.ContactPhone).HasMaxLength(20); - entity.Property(e => e.Status).HasMaxLength(20); - entity.Property(e => e.Description).HasMaxLength(500); - }); + builder.Entity(entity => { diff --git a/Data/SeedData.cs b/Data/SeedData.cs index 43a49ed..c8b66ce 100644 --- a/Data/SeedData.cs +++ b/Data/SeedData.cs @@ -1,4 +1,6 @@ using Fengling.AuthService.Models; +using Fengling.Platform.Domain.AggregatesModel.TenantAggregate; +using Fengling.Platform.Infrastructure; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using OpenIddict.Abstractions; @@ -16,28 +18,11 @@ public static class SeedData var applicationManager = scope.ServiceProvider.GetRequiredService(); var scopeManager = scope.ServiceProvider.GetRequiredService(); + var platformDbContext = scope.ServiceProvider.GetRequiredService(); + + var adminTenant = await platformDbContext.InitializeAsync(); await context.Database.EnsureCreatedAsync(); - var defaultTenant = await context.Tenants - .AsNoTracking() - .FirstOrDefaultAsync(t => t.TenantId == "default"); - if (defaultTenant == null) - { - defaultTenant = new Tenant - { - TenantId = "default", - Name = "默认租户", - ContactName = "系统管理员", - ContactEmail = "admin@fengling.local", - ContactPhone = "13800138000", - MaxUsers = 1000, - Description = "系统默认租户", - Status = "active", - CreatedAt = DateTime.UtcNow - }; - context.Tenants.Add(defaultTenant); - await context.SaveChangesAsync(); - } var adminRole = await roleManager.FindByNameAsync("Admin"); if (adminRole == null) @@ -47,7 +32,7 @@ public static class SeedData Name = "Admin", DisplayName = "管理员", Description = "System administrator", - TenantId = defaultTenant.Id, + TenantId = adminTenant.Id, IsSystem = true, Permissions = new List { @@ -70,7 +55,7 @@ public static class SeedData Name = "User", DisplayName = "普通用户", Description = "Regular user", - TenantId = defaultTenant.Id, + TenantId = adminTenant.Id, IsSystem = true, Permissions = new List { "user.view" }, CreatedTime = DateTime.UtcNow @@ -87,7 +72,7 @@ public static class SeedData Email = "admin@fengling.local", RealName = "系统管理员", Phone = "13800138000", - TenantInfo = new TenantInfo(defaultTenant.Id, defaultTenant.TenantId, defaultTenant.Name), + TenantInfo = new TenantInfo(adminTenant), EmailConfirmed = true, IsDeleted = false, CreatedTime = DateTime.UtcNow @@ -109,7 +94,7 @@ public static class SeedData Email = "test@fengling.local", RealName = "测试用户", Phone = "13900139000", - TenantInfo = new TenantInfo(defaultTenant.Id, defaultTenant.TenantId, defaultTenant.Name), + TenantInfo = new TenantInfo(adminTenant.Id, adminTenant.TenantCode, adminTenant.Name), EmailConfirmed = true, IsDeleted = false, CreatedTime = DateTime.UtcNow @@ -140,19 +125,19 @@ public static class SeedData }; foreach (var uri in new[] - { - "http://localhost:5777/auth/callback", - "https://console.fengling.local/auth/callback" - }) + { + "http://localhost:5777/auth/callback", + "https://console.fengling.local/auth/callback" + }) { descriptor.RedirectUris.Add(new Uri(uri)); } foreach (var uri in new[] - { - "http://localhost:5777/", - "https://console.fengling.local/" - }) + { + "http://localhost:5777/", + "https://console.fengling.local/" + }) { descriptor.PostLogoutRedirectUris.Add(new Uri(uri)); } @@ -206,4 +191,4 @@ public static class SeedData await applicationManager.CreateAsync(resourceDescriptor); } } -} +} \ No newline at end of file diff --git a/Fengling.AuthService.csproj b/Fengling.AuthService.csproj index c3d5d61..0fa79c9 100644 --- a/Fengling.AuthService.csproj +++ b/Fengling.AuthService.csproj @@ -33,4 +33,9 @@ PreserveNewest + + + + + diff --git a/Migrations/20260206142720_inital.Designer.cs b/Migrations/20260218145654_Initial.Designer.cs similarity index 83% rename from Migrations/20260206142720_inital.Designer.cs rename to Migrations/20260218145654_Initial.Designer.cs index b62e558..8806b30 100644 --- a/Migrations/20260206142720_inital.Designer.cs +++ b/Migrations/20260218145654_Initial.Designer.cs @@ -13,8 +13,8 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Fengling.AuthService.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20260206142720_inital")] - partial class inital + [Migration("20260218145654_Initial")] + partial class Initial { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -312,141 +312,6 @@ namespace Fengling.AuthService.Migrations b.ToTable("AuditLogs"); }); - modelBuilder.Entity("Fengling.AuthService.Models.OAuthApplication", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClientId") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("ClientSecret") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ClientType") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("ConsentType") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasMaxLength(500) - .HasColumnType("character varying(500)"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.PrimitiveCollection("GrantTypes") - .IsRequired() - .HasColumnType("text[]"); - - b.PrimitiveCollection("PostLogoutRedirectUris") - .IsRequired() - .HasColumnType("text[]"); - - b.PrimitiveCollection("RedirectUris") - .IsRequired() - .HasColumnType("text[]"); - - b.PrimitiveCollection("Scopes") - .IsRequired() - .HasColumnType("text[]"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ClientId") - .IsUnique(); - - b.ToTable("OAuthApplications"); - }); - - modelBuilder.Entity("Fengling.AuthService.Models.Tenant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ContactEmail") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("ContactName") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("ContactPhone") - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasMaxLength(500) - .HasColumnType("character varying(500)"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .HasColumnType("boolean"); - - b.Property("MaxUsers") - .HasColumnType("integer"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("TenantId") - .IsUnique(); - - b.ToTable("Tenants"); - }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.Property("Id") @@ -760,25 +625,25 @@ namespace Fengling.AuthService.Migrations modelBuilder.Entity("Fengling.AuthService.Models.ApplicationUser", b => { - b.OwnsOne("Fengling.AuthService.Models.TenantInfo", "TenantInfo", b1 => + b.OwnsOne("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.TenantInfo", "TenantInfo", b1 => { b1.Property("ApplicationUserId") .HasColumnType("bigint"); - b1.Property("Id") - .HasColumnType("bigint") - .HasColumnName("TenantId"); - - b1.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("TenantName"); - - b1.Property("TenantId") + b1.Property("TenantCode") .IsRequired() .HasColumnType("text") .HasColumnName("TenantCode"); + b1.Property("TenantId") + .HasColumnType("bigint") + .HasColumnName("TenantId"); + + b1.Property("TenantName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("TenantName"); + b1.HasKey("ApplicationUserId"); b1.ToTable("AspNetUsers"); diff --git a/Migrations/20260206142720_inital.cs b/Migrations/20260218145654_Initial.cs similarity index 85% rename from Migrations/20260206142720_inital.cs rename to Migrations/20260218145654_Initial.cs index 7948300..d1bc9f7 100644 --- a/Migrations/20260206142720_inital.cs +++ b/Migrations/20260218145654_Initial.cs @@ -8,7 +8,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Fengling.AuthService.Migrations { /// - public partial class inital : Migration + public partial class Initial : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) @@ -119,31 +119,6 @@ namespace Fengling.AuthService.Migrations table.PrimaryKey("PK_AuditLogs", x => x.Id); }); - migrationBuilder.CreateTable( - name: "OAuthApplications", - columns: table => new - { - Id = table.Column(type: "bigint", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - ClientId = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), - ClientSecret = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), - DisplayName = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), - RedirectUris = table.Column(type: "text[]", nullable: false), - PostLogoutRedirectUris = table.Column(type: "text[]", nullable: false), - Scopes = table.Column(type: "text[]", nullable: false), - GrantTypes = table.Column(type: "text[]", nullable: false), - ClientType = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), - ConsentType = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), - Status = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), - Description = table.Column(type: "character varying(500)", maxLength: 500, nullable: true), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_OAuthApplications", x => x.Id); - }); - migrationBuilder.CreateTable( name: "OpenIddictApplications", columns: table => new @@ -189,30 +164,6 @@ namespace Fengling.AuthService.Migrations table.PrimaryKey("PK_OpenIddictScopes", x => x.Id); }); - migrationBuilder.CreateTable( - name: "Tenants", - columns: table => new - { - Id = table.Column(type: "bigint", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - TenantId = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), - Name = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), - ContactName = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), - ContactEmail = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), - ContactPhone = table.Column(type: "character varying(20)", maxLength: 20, nullable: true), - MaxUsers = table.Column(type: "integer", nullable: true), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - Description = table.Column(type: "character varying(500)", maxLength: 500, nullable: true), - Status = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), - ExpiresAt = table.Column(type: "timestamp with time zone", nullable: true), - UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), - IsDeleted = table.Column(type: "boolean", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Tenants", x => x.Id); - }); - migrationBuilder.CreateTable( name: "AspNetRoleClaims", columns: table => new @@ -469,12 +420,6 @@ namespace Fengling.AuthService.Migrations table: "AuditLogs", column: "TenantId"); - migrationBuilder.CreateIndex( - name: "IX_OAuthApplications_ClientId", - table: "OAuthApplications", - column: "ClientId", - unique: true); - migrationBuilder.CreateIndex( name: "IX_OpenIddictApplications_ClientId", table: "OpenIddictApplications", @@ -507,12 +452,6 @@ namespace Fengling.AuthService.Migrations table: "OpenIddictTokens", column: "ReferenceId", unique: true); - - migrationBuilder.CreateIndex( - name: "IX_Tenants_TenantId", - table: "Tenants", - column: "TenantId", - unique: true); } /// @@ -539,18 +478,12 @@ namespace Fengling.AuthService.Migrations migrationBuilder.DropTable( name: "AuditLogs"); - migrationBuilder.DropTable( - name: "OAuthApplications"); - migrationBuilder.DropTable( name: "OpenIddictScopes"); migrationBuilder.DropTable( name: "OpenIddictTokens"); - migrationBuilder.DropTable( - name: "Tenants"); - migrationBuilder.DropTable( name: "AspNetRoles"); diff --git a/Migrations/ApplicationDbContextModelSnapshot.cs b/Migrations/ApplicationDbContextModelSnapshot.cs index b7fb39f..935f985 100644 --- a/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Migrations/ApplicationDbContextModelSnapshot.cs @@ -309,73 +309,6 @@ namespace Fengling.AuthService.Migrations b.ToTable("AuditLogs"); }); - modelBuilder.Entity("Fengling.AuthService.Models.Tenant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ContactEmail") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("ContactName") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("ContactPhone") - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasMaxLength(500) - .HasColumnType("character varying(500)"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .HasColumnType("boolean"); - - b.Property("MaxUsers") - .HasColumnType("integer"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("Settings") - .HasColumnType("text"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("TenantId") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("TenantId") - .IsUnique(); - - b.ToTable("Tenants"); - }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.Property("Id") @@ -689,25 +622,25 @@ namespace Fengling.AuthService.Migrations modelBuilder.Entity("Fengling.AuthService.Models.ApplicationUser", b => { - b.OwnsOne("Fengling.AuthService.Models.TenantInfo", "TenantInfo", b1 => + b.OwnsOne("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.TenantInfo", "TenantInfo", b1 => { b1.Property("ApplicationUserId") .HasColumnType("bigint"); - b1.Property("Id") - .HasColumnType("bigint") - .HasColumnName("TenantId"); - - b1.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("TenantName"); - - b1.Property("TenantId") + b1.Property("TenantCode") .IsRequired() .HasColumnType("text") .HasColumnName("TenantCode"); + b1.Property("TenantId") + .HasColumnType("bigint") + .HasColumnName("TenantId"); + + b1.Property("TenantName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("TenantName"); + b1.HasKey("ApplicationUserId"); b1.ToTable("AspNetUsers"); diff --git a/Models/Tenant.cs b/Models/Tenant.cs deleted file mode 100644 index e836cf6..0000000 --- a/Models/Tenant.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Fengling.AuthService.Models; - -public class Tenant -{ - private long _id; - private string _tenantId; - private string _name; - - [Key] - public long Id - { - get => _id; - set => _id = value; - } - - [MaxLength(50)] - [Required] - public string TenantId - { - get => _tenantId; - set => _tenantId = value; - } - - [MaxLength(100)] - [Required] - public string Name - { - get => _name; - set => _name = value; - } - - [MaxLength(50)] - [Required] - public string ContactName { get; set; } = string.Empty; - - [MaxLength(100)] - [Required] - [EmailAddress] - public string ContactEmail { get; set; } = string.Empty; - - [MaxLength(20)] - public string? ContactPhone { get; set; } - - public int? MaxUsers { get; set; } - - public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - - [MaxLength(500)] - public string? Description { get; set; } - - [MaxLength(20)] - public string Status { get; set; } = "active"; - - public DateTime? ExpiresAt { get; set; } - - public DateTime? UpdatedAt { get; set; } - - public bool IsDeleted { get; set; } - - public TenantInfo Info => new(Id, TenantId, Name); -} diff --git a/Models/TenantInfo.cs b/Models/TenantInfo.cs deleted file mode 100644 index 0f4fa48..0000000 --- a/Models/TenantInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Fengling.AuthService.Models; - -public record TenantInfo(long Id, string TenantId, string Name);