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)
This commit is contained in:
movingsam 2026-02-18 23:00:09 +08:00
parent 8184f77c0f
commit 39cc9a8538
12 changed files with 192 additions and 592 deletions

View File

@ -1,6 +1,8 @@
using Fengling.AuthService.Data; using Fengling.AuthService.Data;
using Fengling.AuthService.Models; using Fengling.AuthService.Models;
using Fengling.AuthService.ViewModels; using Fengling.AuthService.ViewModels;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Fengling.Platform.Infrastructure;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
@ -10,25 +12,14 @@ using Microsoft.EntityFrameworkCore;
namespace Fengling.AuthService.Controllers; namespace Fengling.AuthService.Controllers;
[Route("account")] [Route("account")]
public class AccountController : Controller public class AccountController(
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ApplicationDbContext _dbContext;
private readonly ILogger<AccountController> _logger;
public AccountController(
UserManager<ApplicationUser> userManager, UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager, SignInManager<ApplicationUser> signInManager,
ApplicationDbContext dbContext, ApplicationDbContext dbContext,
ILogger<AccountController> logger) ILogger<AccountController> logger,
{ PlatformDbContext platformDbContext)
_userManager = userManager; : Controller
_signInManager = signInManager; {
_dbContext = dbContext;
_logger = logger;
}
[HttpGet("login")] [HttpGet("login")]
public IActionResult Login(string returnUrl = "/") public IActionResult Login(string returnUrl = "/")
{ {
@ -44,14 +35,14 @@ public class AccountController : Controller
return View(model); return View(model);
} }
var user = await _userManager.FindByNameAsync(model.Username); var user = await userManager.FindByNameAsync(model.Username);
if (user == null || user.IsDeleted) if (user == null || user.IsDeleted)
{ {
ModelState.AddModelError(string.Empty, "用户名或密码错误"); ModelState.AddModelError(string.Empty, "用户名或密码错误");
return View(model); 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.Succeeded)
{ {
if (result.IsLockedOut) if (result.IsLockedOut)
@ -83,12 +74,12 @@ public class AccountController : Controller
return View(model); return View(model);
} }
var defaultTenant = await _dbContext.Tenants var tenant = await platformDbContext.Tenants
.FirstOrDefaultAsync(t => t.TenantId == "default"); .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); return View(model);
} }
@ -98,10 +89,10 @@ public class AccountController : Controller
Email = model.Email, Email = model.Email,
NormalizedUserName = model.Username.ToUpper(), NormalizedUserName = model.Username.ToUpper(),
NormalizedEmail = model.Email.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) if (!result.Succeeded)
{ {
foreach (var error in result.Errors) foreach (var error in result.Errors)
@ -111,7 +102,7 @@ public class AccountController : Controller
return View(model); return View(model);
} }
await _signInManager.SignInAsync(user, isPersistent: false); await signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(model.ReturnUrl); return LocalRedirect(model.ReturnUrl);
} }
@ -127,7 +118,7 @@ public class AccountController : Controller
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]
public async Task<IActionResult> LogoutPost() public async Task<IActionResult> LogoutPost()
{ {
await _signInManager.SignOutAsync(); await signInManager.SignOutAsync();
return Redirect("/"); return Redirect("/");
} }
} }

View File

@ -1,5 +1,6 @@
using Fengling.AuthService.Data; using Fengling.AuthService.Data;
using Fengling.AuthService.Models; using Fengling.AuthService.Models;
using Fengling.Platform.Infrastructure;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -10,21 +11,13 @@ namespace Fengling.AuthService.Controllers;
[ApiController] [ApiController]
[Route("api/[controller]")] [Route("api/[controller]")]
[Authorize] [Authorize]
public class StatsController : ControllerBase public class StatsController(
{
private readonly ApplicationDbContext _context;
private readonly IOpenIddictApplicationManager _applicationManager;
private readonly ILogger<StatsController> _logger;
public StatsController(
ApplicationDbContext context, ApplicationDbContext context,
IOpenIddictApplicationManager applicationManager, IOpenIddictApplicationManager applicationManager,
ILogger<StatsController> logger) ILogger<StatsController> logger,
{ PlatformDbContext platformDbContext)
_context = context; : ControllerBase
_applicationManager = applicationManager; {
_logger = logger;
}
[HttpGet("dashboard")] [HttpGet("dashboard")]
public async Task<ActionResult<object>> GetDashboardStats() public async Task<ActionResult<object>> GetDashboardStats()
@ -32,10 +25,10 @@ public class StatsController : ControllerBase
var today = DateTime.UtcNow.Date; var today = DateTime.UtcNow.Date;
var tomorrow = today.AddDays(1); var tomorrow = today.AddDays(1);
var userCount = await _context.Users.CountAsync(u => !u.IsDeleted); var userCount = await context.Users.CountAsync(u => !u.IsDeleted);
var tenantCount = await _context.Tenants.CountAsync(t => !t.IsDeleted); var tenantCount = await platformDbContext.Tenants.CountAsync(t => !t.Deleted);
var oauthClientCount = await CountOAuthClientsAsync(); var oauthClientCount = await CountOAuthClientsAsync();
var todayAccessCount = await _context.AccessLogs var todayAccessCount = await context.AccessLogs
.CountAsync(l => l.CreatedAt >= today && l.CreatedAt < tomorrow); .CountAsync(l => l.CreatedAt >= today && l.CreatedAt < tomorrow);
return Ok(new return Ok(new
@ -50,7 +43,7 @@ public class StatsController : ControllerBase
private async Task<int> CountOAuthClientsAsync() private async Task<int> CountOAuthClientsAsync()
{ {
var count = 0; var count = 0;
var applications = _applicationManager.ListAsync(); var applications = applicationManager.ListAsync();
await foreach (var _ in applications) await foreach (var _ in applications)
{ {
count++; count++;

View File

@ -6,49 +6,42 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json; using System.Text.Json;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Fengling.Platform.Infrastructure;
namespace Fengling.AuthService.Controllers; namespace Fengling.AuthService.Controllers;
[ApiController] [ApiController]
[Route("api/[controller]")] [Route("api/[controller]")]
[Authorize] [Authorize]
public class TenantsController : ControllerBase public class TenantsController(
{
private readonly ApplicationDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
private readonly ILogger<TenantsController> _logger;
public TenantsController(
ApplicationDbContext context, ApplicationDbContext context,
UserManager<ApplicationUser> userManager, UserManager<ApplicationUser> userManager,
ILogger<TenantsController> logger) ILogger<TenantsController> logger,
{ PlatformDbContext platformDbContext)
_context = context; : ControllerBase
_userManager = userManager; {
_logger = logger;
}
[HttpGet] [HttpGet]
public async Task<ActionResult<object>> GetTenants( public async Task<ActionResult<object>> GetTenants(
[FromQuery] int page = 1, [FromQuery] int page = 1,
[FromQuery] int pageSize = 10, [FromQuery] int pageSize = 10,
[FromQuery] string? name = null, [FromQuery] string? name = null,
[FromQuery] string? tenantId = null, [FromQuery] string? tenantCode = null,
[FromQuery] string? status = null) [FromQuery] TenantStatus? status = null)
{ {
var query = _context.Tenants.AsQueryable(); var query = platformDbContext.Tenants.AsQueryable();
if (!string.IsNullOrEmpty(name)) if (!string.IsNullOrEmpty(name))
{ {
query = query.Where(t => t.Name.Contains(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); query = query.Where(t => t.Status == status);
} }
@ -64,11 +57,11 @@ public class TenantsController : ControllerBase
foreach (var tenant in tenants) 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 result.Add(new
{ {
id = tenant.Id, id = tenant.Id,
tenantId = tenant.TenantId, tenantId = tenant.Id,
name = tenant.Name, name = tenant.Name,
contactName = tenant.ContactName, contactName = tenant.ContactName,
contactEmail = tenant.ContactEmail, contactEmail = tenant.ContactEmail,
@ -92,9 +85,9 @@ public class TenantsController : ControllerBase
} }
[HttpGet("{id}")] [HttpGet("{id}")]
public async Task<ActionResult<Tenant>> GetTenant(long id) public async Task<ActionResult<Tenant>> GetTenant(TenantId id)
{ {
var tenant = await _context.Tenants.FindAsync(id); var tenant = await platformDbContext.Tenants.FindAsync(id);
if (tenant == null) if (tenant == null)
{ {
return NotFound(); return NotFound();
@ -103,7 +96,7 @@ public class TenantsController : ControllerBase
return Ok(new return Ok(new
{ {
id = tenant.Id, id = tenant.Id,
tenantId = tenant.TenantId, tenantId = tenant.Id,
name = tenant.Name, name = tenant.Name,
contactName = tenant.ContactName, contactName = tenant.ContactName,
contactEmail = tenant.ContactEmail, contactEmail = tenant.ContactEmail,
@ -118,16 +111,19 @@ public class TenantsController : ControllerBase
} }
[HttpGet("{tenantId}/users")] [HttpGet("{tenantId}/users")]
public async Task<ActionResult<List<object>>> GetTenantUsers(string tenantId) public async Task<ActionResult<List<object>>> 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) if (tenant == null)
{ {
return NotFound(); return NotFound();
} }
var users = await _context.Users var users = await context.Users
.Where(u => u.TenantInfo.Id == tenant.Id && !u.IsDeleted) .Where(u =>
u.TenantInfo.TenantId == tenant.Id
&& !u.IsDeleted)
.ToListAsync(); .ToListAsync();
var result = users.Select(async u => new var result = users.Select(async u => new
@ -136,8 +132,8 @@ public class TenantsController : ControllerBase
userName = u.UserName, userName = u.UserName,
email = u.Email, email = u.Email,
realName = u.RealName, realName = u.RealName,
tenantId = u.TenantInfo.Id, tenantId = u.TenantInfo.TenantId,
roles = await _userManager.GetRolesAsync(u), roles = await userManager.GetRolesAsync(u),
isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow, isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow,
createdAt = u.CreatedTime, createdAt = u.CreatedTime,
}); });
@ -146,15 +142,16 @@ public class TenantsController : ControllerBase
} }
[HttpGet("{tenantId}/roles")] [HttpGet("{tenantId}/roles")]
public async Task<ActionResult<List<object>>> GetTenantRoles(string tenantId) public async Task<ActionResult<List<object>>> 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) if (tenant == null)
{ {
return NotFound(); return NotFound();
} }
var roles = await _context.Roles var roles = await context.Roles
.Where(r => r.TenantId == tenant.Id) .Where(r => r.TenantId == tenant.Id)
.ToListAsync(); .ToListAsync();
@ -169,9 +166,10 @@ public class TenantsController : ControllerBase
} }
[HttpGet("{tenantId}/settings")] [HttpGet("{tenantId}/settings")]
public async Task<ActionResult<TenantSettings>> GetTenantSettings(string tenantId) public async Task<ActionResult<TenantSettings>> 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) if (tenant == null)
{ {
return NotFound(); return NotFound();
@ -191,15 +189,16 @@ public class TenantsController : ControllerBase
} }
[HttpPut("{tenantId}/settings")] [HttpPut("{tenantId}/settings")]
public async Task<IActionResult> UpdateTenantSettings(string tenantId, TenantSettings settings) public async Task<IActionResult> 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) if (tenant == null)
{ {
return NotFound(); 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(); return NoContent();
} }
@ -207,24 +206,14 @@ public class TenantsController : ControllerBase
[HttpPost] [HttpPost]
public async Task<ActionResult<Tenant>> CreateTenant(CreateTenantDto dto) public async Task<ActionResult<Tenant>> CreateTenant(CreateTenantDto dto)
{ {
var tenant = new Tenant var tenant = new Tenant(dto.TenantCode, dto.TenantName, dto.ContactName, dto.ContactEmail, dto.ContactPhone,
{ dto.MaxUsers, dto.Description, dto.ExpiresAt);
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,
};
_context.Tenants.Add(tenant); platformDbContext.Tenants.Add(tenant);
await _context.SaveChangesAsync(); 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); return CreatedAtAction(nameof(GetTenant), new { id = tenant.Id }, tenant);
} }
@ -232,27 +221,20 @@ public class TenantsController : ControllerBase
[HttpPut("{id}")] [HttpPut("{id}")]
public async Task<IActionResult> UpdateTenant(long id, UpdateTenantDto dto) public async Task<IActionResult> UpdateTenant(long id, UpdateTenantDto dto)
{ {
var tenant = await _context.Tenants.FindAsync(id); var tenant = await platformDbContext.Tenants.FindAsync(id);
if (tenant == null) if (tenant == null)
{ {
return NotFound(); return NotFound();
} }
var oldValue = JsonSerializer.Serialize(tenant); 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(); return NoContent();
} }
@ -260,7 +242,7 @@ public class TenantsController : ControllerBase
[HttpDelete("{id}")] [HttpDelete("{id}")]
public async Task<IActionResult> DeleteTenant(long id) public async Task<IActionResult> DeleteTenant(long id)
{ {
var tenant = await _context.Tenants.FindAsync(id); var tenant = await platformDbContext.Tenants.FindAsync(id);
if (tenant == null) if (tenant == null)
{ {
return NotFound(); return NotFound();
@ -268,22 +250,23 @@ public class TenantsController : ControllerBase
var oldValue = JsonSerializer.Serialize(tenant); 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) foreach (var user in users)
{ {
user.IsDeleted = true; user.IsDeleted = true;
user.UpdatedTime = DateTime.UtcNow; user.UpdatedTime = DateTime.UtcNow;
} }
tenant.IsDeleted = true; tenant.Delete();
await _context.SaveChangesAsync(); 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(); 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 userName = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.Identity?.Name ?? "system";
var tenantId = User.FindFirstValue("TenantId"); var tenantId = User.FindFirstValue("TenantId");
@ -303,23 +286,21 @@ public class TenantsController : ControllerBase
NewValue = newValue, NewValue = newValue,
}; };
_context.AuditLogs.Add(log); context.AuditLogs.Add(log);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
public class CreateTenantDto public record CreateTenantDto(
{ string TenantCode,
public string TenantId { get; set; } = string.Empty; string TenantName,
public string Name { get; set; } = string.Empty; string ContactName,
public string ContactName { get; set; } = string.Empty; string ContactEmail,
public string ContactEmail { get; set; } = string.Empty; string ContactPhone,
public string? ContactPhone { get; set; } int? MaxUsers,
public int? MaxUsers { get; set; } string? Description,
public string? Description { get; set; } string Status,
public string Status { get; set; } = "active"; DateTime? ExpiresAt);
public DateTime? ExpiresAt { get; set; }
}
public class UpdateTenantDto public class UpdateTenantDto
{ {

View File

@ -5,30 +5,22 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Security.Claims; using System.Security.Claims;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Fengling.Platform.Infrastructure;
namespace Fengling.AuthService.Controllers; namespace Fengling.AuthService.Controllers;
[ApiController] [ApiController]
[Route("api/[controller]")] [Route("api/[controller]")]
[Authorize] [Authorize]
public class UsersController : ControllerBase public class UsersController(
{
private readonly ApplicationDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _roleManager;
private readonly ILogger<UsersController> _logger;
public UsersController(
ApplicationDbContext context, ApplicationDbContext context,
UserManager<ApplicationUser> userManager, UserManager<ApplicationUser> userManager,
RoleManager<ApplicationRole> roleManager, RoleManager<ApplicationRole> roleManager,
ILogger<UsersController> logger) ILogger<UsersController> logger,
{ PlatformDbContext platformDbContext)
_context = context; : ControllerBase
_userManager = userManager; {
_roleManager = roleManager;
_logger = logger;
}
[HttpGet] [HttpGet]
public async Task<ActionResult<object>> GetUsers( public async Task<ActionResult<object>> GetUsers(
@ -36,9 +28,9 @@ public class UsersController : ControllerBase
[FromQuery] int pageSize = 10, [FromQuery] int pageSize = 10,
[FromQuery] string? userName = null, [FromQuery] string? userName = null,
[FromQuery] string? email = 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)) if (!string.IsNullOrEmpty(userName))
{ {
@ -50,9 +42,9 @@ public class UsersController : ControllerBase
query = query.Where(u => u.Email != null && u.Email.Contains(email)); 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(); var totalCount = await query.CountAsync();
@ -69,8 +61,8 @@ public class UsersController : ControllerBase
email = u.Email, email = u.Email,
realName = u.RealName, realName = u.RealName,
phone = u.Phone, phone = u.Phone,
tenantId = u.TenantInfo.Id, tenantId = u.TenantInfo.TenantId,
roles = (await _userManager.GetRolesAsync(u)).ToList(), roles = (await userManager.GetRolesAsync(u)).ToList(),
emailConfirmed = u.EmailConfirmed, emailConfirmed = u.EmailConfirmed,
isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow, isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow,
createdAt = u.CreatedTime, createdAt = u.CreatedTime,
@ -88,13 +80,13 @@ public class UsersController : ControllerBase
[HttpGet("{id}")] [HttpGet("{id}")]
public async Task<ActionResult<object>> GetUser(long id) public async Task<ActionResult<object>> GetUser(long id)
{ {
var user = await _context.Users.FindAsync(id); var user = await context.Users.FindAsync(id);
if (user == null) if (user == null)
{ {
return NotFound(); return NotFound();
} }
var roles = await _userManager.GetRolesAsync(user); var roles = await userManager.GetRolesAsync(user);
return Ok(new return Ok(new
{ {
@ -103,7 +95,7 @@ public class UsersController : ControllerBase
email = user.Email, email = user.Email,
realName = user.RealName, realName = user.RealName,
phone = user.Phone, phone = user.Phone,
tenantId = user.TenantInfo.Id, tenantId = user.TenantInfo.TenantId,
roles, roles,
emailConfirmed = user.EmailConfirmed, emailConfirmed = user.EmailConfirmed,
isActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, isActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
@ -115,11 +107,11 @@ public class UsersController : ControllerBase
public async Task<ActionResult<ApplicationUser>> CreateUser(CreateUserDto dto) public async Task<ActionResult<ApplicationUser>> CreateUser(CreateUserDto dto)
{ {
var tenantId = dto.TenantId ?? 0; var tenantId = dto.TenantId ?? 0;
Tenant tenant = null; Tenant? tenant = null;
if (tenantId != 0) if (tenantId != 0)
{ {
tenant = await _context.Tenants.FindAsync(tenantId); tenant = await platformDbContext.Tenants.FindAsync(tenantId);
if (tenant == null) if (tenant == null)
{ {
return BadRequest("Invalid tenant ID"); return BadRequest("Invalid tenant ID");
@ -132,33 +124,33 @@ public class UsersController : ControllerBase
Email = dto.Email, Email = dto.Email,
RealName = dto.RealName, RealName = dto.RealName,
Phone = dto.Phone, Phone = dto.Phone,
TenantInfo = new TenantInfo(tenantId, tenant?.TenantId ?? "default", tenant?.Name ?? "默认租户"), TenantInfo = new TenantInfo(tenant!),
EmailConfirmed = dto.EmailConfirmed, EmailConfirmed = dto.EmailConfirmed,
CreatedTime = DateTime.UtcNow, CreatedTime = DateTime.UtcNow,
}; };
var result = await _userManager.CreateAsync(user, dto.Password); var result = await userManager.CreateAsync(user, dto.Password);
if (!result.Succeeded) if (!result.Succeeded)
{ {
return BadRequest(result.Errors); return BadRequest(result.Errors);
} }
if (dto.RoleIds != null && dto.RoleIds.Any()) if (dto.RoleIds.Any())
{ {
foreach (var roleId in dto.RoleIds) foreach (var roleId in dto.RoleIds)
{ {
var role = await _roleManager.FindByIdAsync(roleId.ToString()); var role = await roleManager.FindByIdAsync(roleId.ToString());
if (role != null) if (role != null)
{ {
await _userManager.AddToRoleAsync(user, role.Name!); await userManager.AddToRoleAsync(user, role.Name!);
} }
} }
} }
if (!dto.IsActive) if (!dto.IsActive)
{ {
await _userManager.SetLockoutEnabledAsync(user, true); await userManager.SetLockoutEnabledAsync(user, true);
await _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue); await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue);
} }
await CreateAuditLog("user", "create", "User", user.Id, user.UserName, null, SerializeToJson(dto)); await CreateAuditLog("user", "create", "User", user.Id, user.UserName, null, SerializeToJson(dto));
@ -169,7 +161,7 @@ public class UsersController : ControllerBase
[HttpPut("{id}")] [HttpPut("{id}")]
public async Task<IActionResult> UpdateUser(long id, UpdateUserDto dto) public async Task<IActionResult> UpdateUser(long id, UpdateUserDto dto)
{ {
var user = await _context.Users.FindAsync(id); var user = await context.Users.FindAsync(id);
if (user == null) if (user == null)
{ {
return NotFound(); return NotFound();
@ -185,16 +177,16 @@ public class UsersController : ControllerBase
if (dto.IsActive) if (dto.IsActive)
{ {
await _userManager.SetLockoutEnabledAsync(user, false); await userManager.SetLockoutEnabledAsync(user, false);
await _userManager.SetLockoutEndDateAsync(user, null); await userManager.SetLockoutEndDateAsync(user, null);
} }
else else
{ {
await _userManager.SetLockoutEnabledAsync(user, true); await userManager.SetLockoutEnabledAsync(user, true);
await _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue); 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)); 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")] [HttpPut("{id}/password")]
public async Task<IActionResult> ResetPassword(long id, ResetPasswordDto dto) public async Task<IActionResult> ResetPassword(long id, ResetPasswordDto dto)
{ {
var user = await _userManager.FindByIdAsync(id.ToString()); var user = await userManager.FindByIdAsync(id.ToString());
if (user == null) if (user == null)
{ {
return NotFound(); return NotFound();
} }
var token = await _userManager.GeneratePasswordResetTokenAsync(user); var token = await userManager.GeneratePasswordResetTokenAsync(user);
var result = await _userManager.ResetPasswordAsync(user, token, dto.NewPassword); var result = await userManager.ResetPasswordAsync(user, token, dto.NewPassword);
if (!result.Succeeded) if (!result.Succeeded)
{ {
@ -226,7 +218,7 @@ public class UsersController : ControllerBase
[HttpDelete("{id}")] [HttpDelete("{id}")]
public async Task<IActionResult> DeleteUser(long id) public async Task<IActionResult> DeleteUser(long id)
{ {
var user = await _context.Users.FindAsync(id); var user = await context.Users.FindAsync(id);
if (user == null) if (user == null)
{ {
return NotFound(); return NotFound();
@ -235,7 +227,7 @@ public class UsersController : ControllerBase
var oldValue = System.Text.Json.JsonSerializer.Serialize(user); var oldValue = System.Text.Json.JsonSerializer.Serialize(user);
user.IsDeleted = true; user.IsDeleted = true;
user.UpdatedTime = DateTime.UtcNow; user.UpdatedTime = DateTime.UtcNow;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
await CreateAuditLog("user", "delete", "User", user.Id, user.UserName, oldValue); await CreateAuditLog("user", "delete", "User", user.Id, user.UserName, oldValue);
@ -262,8 +254,8 @@ public class UsersController : ControllerBase
NewValue = newValue, NewValue = newValue,
}; };
_context.AuditLogs.Add(log); context.AuditLogs.Add(log);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
private string SerializeToJson(object obj) private string SerializeToJson(object obj)

View File

@ -7,7 +7,6 @@ namespace Fengling.AuthService.Data;
public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: IdentityDbContext<ApplicationUser, ApplicationRole, long>(options) : IdentityDbContext<ApplicationUser, ApplicationRole, long>(options)
{ {
public DbSet<Tenant> Tenants { get; set; }
public DbSet<AccessLog> AccessLogs { get; set; } public DbSet<AccessLog> AccessLogs { get; set; }
public DbSet<AuditLog> AuditLogs { get; set; } public DbSet<AuditLog> AuditLogs { get; set; }
@ -23,27 +22,16 @@ public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options
entity.OwnsOne(e => e.TenantInfo, navigationBuilder => entity.OwnsOne(e => e.TenantInfo, navigationBuilder =>
{ {
navigationBuilder.Property(e => e.Id).HasColumnName("TenantId"); navigationBuilder.Property(e => e.TenantCode).HasColumnName("TenantCode");
navigationBuilder.Property(e => e.TenantId).HasColumnName("TenantCode"); navigationBuilder.Property(e => e.TenantId).HasColumnName("TenantId");
navigationBuilder.Property(e => e.Name).HasColumnName("TenantName"); navigationBuilder.Property(e => e.TenantName).HasColumnName("TenantName");
navigationBuilder.WithOwner(); navigationBuilder.WithOwner();
}); });
}); });
builder.Entity<ApplicationRole>(entity => { entity.Property(e => e.Description).HasMaxLength(200); }); builder.Entity<ApplicationRole>(entity => { entity.Property(e => e.Description).HasMaxLength(200); });
builder.Entity<Tenant>(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<AccessLog>(entity => builder.Entity<AccessLog>(entity =>
{ {

View File

@ -1,4 +1,6 @@
using Fengling.AuthService.Models; using Fengling.AuthService.Models;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Fengling.Platform.Infrastructure;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using OpenIddict.Abstractions; using OpenIddict.Abstractions;
@ -16,28 +18,11 @@ public static class SeedData
var applicationManager = scope.ServiceProvider.GetRequiredService<IOpenIddictApplicationManager>(); var applicationManager = scope.ServiceProvider.GetRequiredService<IOpenIddictApplicationManager>();
var scopeManager = scope.ServiceProvider.GetRequiredService<IOpenIddictScopeManager>(); var scopeManager = scope.ServiceProvider.GetRequiredService<IOpenIddictScopeManager>();
var platformDbContext = scope.ServiceProvider.GetRequiredService<PlatformDbContext>();
var adminTenant = await platformDbContext.InitializeAsync();
await context.Database.EnsureCreatedAsync(); 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"); var adminRole = await roleManager.FindByNameAsync("Admin");
if (adminRole == null) if (adminRole == null)
@ -47,7 +32,7 @@ public static class SeedData
Name = "Admin", Name = "Admin",
DisplayName = "管理员", DisplayName = "管理员",
Description = "System administrator", Description = "System administrator",
TenantId = defaultTenant.Id, TenantId = adminTenant.Id,
IsSystem = true, IsSystem = true,
Permissions = new List<string> Permissions = new List<string>
{ {
@ -70,7 +55,7 @@ public static class SeedData
Name = "User", Name = "User",
DisplayName = "普通用户", DisplayName = "普通用户",
Description = "Regular user", Description = "Regular user",
TenantId = defaultTenant.Id, TenantId = adminTenant.Id,
IsSystem = true, IsSystem = true,
Permissions = new List<string> { "user.view" }, Permissions = new List<string> { "user.view" },
CreatedTime = DateTime.UtcNow CreatedTime = DateTime.UtcNow
@ -87,7 +72,7 @@ public static class SeedData
Email = "admin@fengling.local", Email = "admin@fengling.local",
RealName = "系统管理员", RealName = "系统管理员",
Phone = "13800138000", Phone = "13800138000",
TenantInfo = new TenantInfo(defaultTenant.Id, defaultTenant.TenantId, defaultTenant.Name), TenantInfo = new TenantInfo(adminTenant),
EmailConfirmed = true, EmailConfirmed = true,
IsDeleted = false, IsDeleted = false,
CreatedTime = DateTime.UtcNow CreatedTime = DateTime.UtcNow
@ -109,7 +94,7 @@ public static class SeedData
Email = "test@fengling.local", Email = "test@fengling.local",
RealName = "测试用户", RealName = "测试用户",
Phone = "13900139000", Phone = "13900139000",
TenantInfo = new TenantInfo(defaultTenant.Id, defaultTenant.TenantId, defaultTenant.Name), TenantInfo = new TenantInfo(adminTenant.Id, adminTenant.TenantCode, adminTenant.Name),
EmailConfirmed = true, EmailConfirmed = true,
IsDeleted = false, IsDeleted = false,
CreatedTime = DateTime.UtcNow CreatedTime = DateTime.UtcNow

View File

@ -33,4 +33,9 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Fengling.Platform\Fengling.Platform.Domain\Fengling.Platform.Domain.csproj" />
<ProjectReference Include="..\Fengling.Platform\Fengling.Platform.Infrastructure\Fengling.Platform.Infrastructure.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -13,8 +13,8 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Fengling.AuthService.Migrations namespace Fengling.AuthService.Migrations
{ {
[DbContext(typeof(ApplicationDbContext))] [DbContext(typeof(ApplicationDbContext))]
[Migration("20260206142720_inital")] [Migration("20260218145654_Initial")]
partial class inital partial class Initial
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -312,141 +312,6 @@ namespace Fengling.AuthService.Migrations
b.ToTable("AuditLogs"); b.ToTable("AuditLogs");
}); });
modelBuilder.Entity("Fengling.AuthService.Models.OAuthApplication", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("ClientSecret")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("ClientType")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("ConsentType")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.PrimitiveCollection<string[]>("GrantTypes")
.IsRequired()
.HasColumnType("text[]");
b.PrimitiveCollection<string[]>("PostLogoutRedirectUris")
.IsRequired()
.HasColumnType("text[]");
b.PrimitiveCollection<string[]>("RedirectUris")
.IsRequired()
.HasColumnType("text[]");
b.PrimitiveCollection<string[]>("Scopes")
.IsRequired()
.HasColumnType("text[]");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime?>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("ContactEmail")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("ContactName")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("ContactPhone")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<DateTime?>("ExpiresAt")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<int?>("MaxUsers")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("TenantId")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("TenantId")
.IsUnique();
b.ToTable("Tenants");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@ -760,25 +625,25 @@ namespace Fengling.AuthService.Migrations
modelBuilder.Entity("Fengling.AuthService.Models.ApplicationUser", b => 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<long>("ApplicationUserId") b1.Property<long>("ApplicationUserId")
.HasColumnType("bigint"); .HasColumnType("bigint");
b1.Property<long>("Id") b1.Property<string>("TenantCode")
.HasColumnType("bigint")
.HasColumnName("TenantId");
b1.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("TenantName");
b1.Property<string>("TenantId")
.IsRequired() .IsRequired()
.HasColumnType("text") .HasColumnType("text")
.HasColumnName("TenantCode"); .HasColumnName("TenantCode");
b1.Property<long>("TenantId")
.HasColumnType("bigint")
.HasColumnName("TenantId");
b1.Property<string>("TenantName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("TenantName");
b1.HasKey("ApplicationUserId"); b1.HasKey("ApplicationUserId");
b1.ToTable("AspNetUsers"); b1.ToTable("AspNetUsers");

View File

@ -8,7 +8,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Fengling.AuthService.Migrations namespace Fengling.AuthService.Migrations
{ {
/// <inheritdoc /> /// <inheritdoc />
public partial class inital : Migration public partial class Initial : Migration
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
@ -119,31 +119,6 @@ namespace Fengling.AuthService.Migrations
table.PrimaryKey("PK_AuditLogs", x => x.Id); table.PrimaryKey("PK_AuditLogs", x => x.Id);
}); });
migrationBuilder.CreateTable(
name: "OAuthApplications",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
ClientId = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
ClientSecret = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
DisplayName = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
RedirectUris = table.Column<string[]>(type: "text[]", nullable: false),
PostLogoutRedirectUris = table.Column<string[]>(type: "text[]", nullable: false),
Scopes = table.Column<string[]>(type: "text[]", nullable: false),
GrantTypes = table.Column<string[]>(type: "text[]", nullable: false),
ClientType = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
ConsentType = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
Description = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_OAuthApplications", x => x.Id);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "OpenIddictApplications", name: "OpenIddictApplications",
columns: table => new columns: table => new
@ -189,30 +164,6 @@ namespace Fengling.AuthService.Migrations
table.PrimaryKey("PK_OpenIddictScopes", x => x.Id); table.PrimaryKey("PK_OpenIddictScopes", x => x.Id);
}); });
migrationBuilder.CreateTable(
name: "Tenants",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
TenantId = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
Name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
ContactName = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
ContactEmail = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
ContactPhone = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
MaxUsers = table.Column<int>(type: "integer", nullable: true),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
Description = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
ExpiresAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
IsDeleted = table.Column<bool>(type: "boolean", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Tenants", x => x.Id);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "AspNetRoleClaims", name: "AspNetRoleClaims",
columns: table => new columns: table => new
@ -469,12 +420,6 @@ namespace Fengling.AuthService.Migrations
table: "AuditLogs", table: "AuditLogs",
column: "TenantId"); column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_OAuthApplications_ClientId",
table: "OAuthApplications",
column: "ClientId",
unique: true);
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_OpenIddictApplications_ClientId", name: "IX_OpenIddictApplications_ClientId",
table: "OpenIddictApplications", table: "OpenIddictApplications",
@ -507,12 +452,6 @@ namespace Fengling.AuthService.Migrations
table: "OpenIddictTokens", table: "OpenIddictTokens",
column: "ReferenceId", column: "ReferenceId",
unique: true); unique: true);
migrationBuilder.CreateIndex(
name: "IX_Tenants_TenantId",
table: "Tenants",
column: "TenantId",
unique: true);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -539,18 +478,12 @@ namespace Fengling.AuthService.Migrations
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AuditLogs"); name: "AuditLogs");
migrationBuilder.DropTable(
name: "OAuthApplications");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "OpenIddictScopes"); name: "OpenIddictScopes");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "OpenIddictTokens"); name: "OpenIddictTokens");
migrationBuilder.DropTable(
name: "Tenants");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AspNetRoles"); name: "AspNetRoles");

View File

@ -309,73 +309,6 @@ namespace Fengling.AuthService.Migrations
b.ToTable("AuditLogs"); b.ToTable("AuditLogs");
}); });
modelBuilder.Entity("Fengling.AuthService.Models.Tenant", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("ContactEmail")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("ContactName")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("ContactPhone")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<DateTime?>("ExpiresAt")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<int?>("MaxUsers")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("Settings")
.HasColumnType("text");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("TenantId")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("TenantId")
.IsUnique();
b.ToTable("Tenants");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@ -689,25 +622,25 @@ namespace Fengling.AuthService.Migrations
modelBuilder.Entity("Fengling.AuthService.Models.ApplicationUser", b => 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<long>("ApplicationUserId") b1.Property<long>("ApplicationUserId")
.HasColumnType("bigint"); .HasColumnType("bigint");
b1.Property<long>("Id") b1.Property<string>("TenantCode")
.HasColumnType("bigint")
.HasColumnName("TenantId");
b1.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("TenantName");
b1.Property<string>("TenantId")
.IsRequired() .IsRequired()
.HasColumnType("text") .HasColumnType("text")
.HasColumnName("TenantCode"); .HasColumnName("TenantCode");
b1.Property<long>("TenantId")
.HasColumnType("bigint")
.HasColumnName("TenantId");
b1.Property<string>("TenantName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("TenantName");
b1.HasKey("ApplicationUserId"); b1.HasKey("ApplicationUserId");
b1.ToTable("AspNetUsers"); b1.ToTable("AspNetUsers");

View File

@ -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);
}

View File

@ -1,3 +0,0 @@
namespace Fengling.AuthService.Models;
public record TenantInfo(long Id, string TenantId, string Name);