docs: add xml comments and update ignore rules

This commit is contained in:
2025-12-12 10:39:51 +08:00
parent d38127d6b2
commit 715cbb3d36
24 changed files with 865 additions and 95 deletions

View File

@@ -22,38 +22,52 @@ namespace TakeoutSaaS.Infrastructure.Identity.Persistence;
/// </summary>
public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger<IdentityDataSeeder> logger) : IHostedService
{
/// <summary>
/// 执行后台账号与权限种子。
/// </summary>
/// <param name="cancellationToken">取消标记。</param>
/// <returns>异步任务。</returns>
public async Task StartAsync(CancellationToken cancellationToken)
{
// 1. 创建作用域并解析依赖
using var scope = serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<IdentityDbContext>();
var options = scope.ServiceProvider.GetRequiredService<IOptions<AdminSeedOptions>>().Value;
var passwordHasher = scope.ServiceProvider.GetRequiredService<IPasswordHasher<DomainIdentityUser>>();
var tenantContextAccessor = scope.ServiceProvider.GetRequiredService<ITenantContextAccessor>();
// 2. 校验功能开关
if (!options.Enabled)
{
logger.LogInformation("AdminSeed 已禁用,跳过后台账号初始化");
return;
}
// 3. 确保数据库已迁移
await context.Database.MigrateAsync(cancellationToken);
// 4. 校验账号配置
if (options.Users is null or { Count: 0 })
{
logger.LogInformation("AdminSeed 未配置账号,跳过后台账号初始化");
return;
}
// 5. 写入角色模板
await SeedRoleTemplatesAsync(context, options.RoleTemplates, cancellationToken);
// 6. 逐个账号处理
foreach (var userOptions in options.Users)
{
// 6.1 进入租户作用域
using var tenantScope = EnterTenantScope(tenantContextAccessor, userOptions.TenantId);
// 6.2 查询账号并收集配置
var user = await context.IdentityUsers.FirstOrDefaultAsync(x => x.Account == userOptions.Account, cancellationToken);
var roles = NormalizeValues(userOptions.Roles);
var permissions = NormalizeValues(userOptions.Permissions);
if (user == null)
{
// 6.3 创建新账号
user = new DomainIdentityUser
{
Id = 0,
@@ -69,6 +83,7 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
}
else
{
// 6.4 更新既有账号
user.DisplayName = userOptions.DisplayName;
user.TenantId = userOptions.TenantId;
user.MerchantId = userOptions.MerchantId;
@@ -76,7 +91,7 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
logger.LogInformation("已更新后台账号 {Account}", user.Account);
}
// 确保角色存在
// 6.5 确保角色存在
var existingRoles = await context.Roles
.Where(r => r.TenantId == userOptions.TenantId && roles.Contains(r.Code))
.ToListAsync(cancellationToken);
@@ -97,7 +112,7 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
});
}
// 确保权限存在
// 6.6 确保权限存在
var existingPermissions = await context.Permissions
.Where(p => p.TenantId == userOptions.TenantId && permissions.Contains(p.Code))
.ToListAsync(cancellationToken);
@@ -118,9 +133,10 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
});
}
// 6.7 保存基础角色/权限
await context.SaveChangesAsync(cancellationToken);
// 重新加载角色/权限以获取 Id
// 6.8 重新加载角色/权限以获取 Id
var roleEntities = await context.Roles
.Where(r => r.TenantId == userOptions.TenantId && roles.Contains(r.Code))
.ToListAsync(cancellationToken);
@@ -128,7 +144,7 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
.Where(p => p.TenantId == userOptions.TenantId && permissions.Contains(p.Code))
.ToListAsync(cancellationToken);
// 重置用户角色
// 6.9 重置用户角色
var existingUserRoles = await context.UserRoles
.Where(ur => ur.TenantId == userOptions.TenantId && ur.UserId == user.Id)
.ToListAsync(cancellationToken);
@@ -191,6 +207,7 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
continue;
}
// 6.10 绑定角色与权限
await context.RolePermissions.AddAsync(new DomainRolePermission
{
TenantId = userOptions.TenantId,
@@ -209,9 +226,15 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
}
}
// 7. 最终保存
await context.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// 停止生命周期时的清理(此处无需处理)。
/// </summary>
/// <param name="cancellationToken">取消标记。</param>
/// <returns>已完成任务。</returns>
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
private static async Task SeedRoleTemplatesAsync(
@@ -219,23 +242,28 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
IList<RoleTemplateSeedOptions> templates,
CancellationToken cancellationToken)
{
// 1. 空集合直接返回
if (templates is null || templates.Count == 0)
{
return;
}
// 2. 逐个处理模板
foreach (var templateOptions in templates)
{
// 2.1 校验必填字段
if (string.IsNullOrWhiteSpace(templateOptions.TemplateCode) || string.IsNullOrWhiteSpace(templateOptions.Name))
{
continue;
}
// 2.2 查询现有模板
var code = templateOptions.TemplateCode.Trim();
var existing = await context.RoleTemplates.FirstOrDefaultAsync(x => x.TemplateCode == code, cancellationToken);
if (existing == null)
{
// 2.3 新增模板
existing = new DomainRoleTemplate
{
TemplateCode = code,
@@ -249,6 +277,7 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
}
else
{
// 2.4 更新模板
existing.Name = templateOptions.Name.Trim();
existing.Description = templateOptions.Description;
existing.IsActive = templateOptions.IsActive;
@@ -256,13 +285,15 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
await context.SaveChangesAsync(cancellationToken);
}
// 2.5 重置模板权限
var permissionCodes = NormalizeValues(templateOptions.Permissions);
var existingPermissions = await context.RoleTemplatePermissions
.Where(x => x.RoleTemplateId == existing.Id)
.ToListAsync(cancellationToken);
// 2.6 清空旧权限并保存
context.RoleTemplatePermissions.RemoveRange(existingPermissions);
await context.SaveChangesAsync(cancellationToken);
// 2.7 去重后的权限编码
var distinctPermissionCodes = permissionCodes.Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
foreach (var permissionCode in distinctPermissionCodes)
{