Files
Academic-Affairs-System/src/Jiaowu.Api/Infrastructure/Persistence/DatabaseInitializer.cs
T
2026-07-25 20:21:42 +08:00

195 lines
6.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
namespace Jiaowu.Api.Infrastructure.Persistence;
public sealed class DatabaseInitializer(
AppDbContext db,
RoleManager<ApplicationRole> roleManager,
UserManager<ApplicationUser> userManager,
DevelopmentSqliteMigrator sqliteMigrator,
DatabaseOptions databaseOptions,
IConfiguration configuration,
IHostEnvironment environment,
ILogger<DatabaseInitializer> logger)
{
public async Task InitializeAsync(bool migrateOnly = false)
{
if (environment.IsDevelopment())
{
if (migrateOnly)
{
throw new InvalidOperationException(
"--migrate-only 仅用于非 Development 环境的 MySQL 数据库。");
}
await db.Database.EnsureCreatedAsync();
await sqliteMigrator.MigrateAsync();
}
else if (migrateOnly || databaseOptions.ApplyMigrationsOnStartup)
{
await db.Database.MigrateAsync();
}
else
{
var pendingMigrations = (await db.Database.GetPendingMigrationsAsync()).ToArray();
if (pendingMigrations.Length > 0)
{
throw new InvalidOperationException(
$"数据库还有 {pendingMigrations.Length} 个待执行迁移。请在发布前运行 " +
"'Jiaowu.Api --migrate-only',或显式配置 " +
"Database:ApplyMigrationsOnStartup=true。");
}
}
if (migrateOnly)
{
logger.LogInformation("数据库迁移已完成。");
return;
}
await SeedRolesAsync();
await SeedAdministratorAsync();
await SeedCourseCategoriesAsync();
}
private async Task SeedRolesAsync()
{
var roleDefinitions = new Dictionary<string, (string Description, DataScope Scope)>
{
[SystemRoles.SuperAdmin] = ("系统配置与全部数据管理", DataScope.All),
[SystemRoles.AcademicAdmin] = ("校级教务管理", DataScope.All),
[SystemRoles.CollegeAdmin] = ("院系教务管理", DataScope.College),
[SystemRoles.Teacher] = ("教师教学工作台", DataScope.Self),
[SystemRoles.Counselor] = ("辅导员与班级管理", DataScope.Class),
[SystemRoles.Student] = ("学生自助服务", DataScope.Self),
[SystemRoles.Leader] = ("校级统计查看", DataScope.All)
};
foreach (var (name, definition) in roleDefinitions)
{
if (await roleManager.RoleExistsAsync(name))
{
continue;
}
var result = await roleManager.CreateAsync(new ApplicationRole
{
Name = name,
Description = definition.Description,
DataScope = definition.Scope
});
EnsureSucceeded(result, $"创建角色 {name}");
}
}
private async Task SeedAdministratorAsync()
{
var userName = configuration["SeedAdmin:UserName"];
var password = configuration["SeedAdmin:Password"];
if (string.IsNullOrWhiteSpace(userName) || string.IsNullOrWhiteSpace(password))
{
if (!environment.IsDevelopment())
{
logger.LogWarning("未配置 SeedAdmin,生产环境不会创建默认管理员。");
}
return;
}
var user = await userManager.FindByNameAsync(userName);
if (user is null)
{
user = new ApplicationUser
{
UserName = userName,
DisplayName = configuration["SeedAdmin:DisplayName"] ?? "系统管理员",
LockoutEnabled = true,
IsEnabled = true
};
EnsureSucceeded(await userManager.CreateAsync(user, password), "创建初始管理员");
}
if (!user.LockoutEnabled)
{
user.LockoutEnabled = true;
EnsureSucceeded(await userManager.UpdateAsync(user), "启用管理员登录保护");
}
if (!await userManager.IsInRoleAsync(user, SystemRoles.SuperAdmin))
{
EnsureSucceeded(
await userManager.AddToRoleAsync(user, SystemRoles.SuperAdmin),
"授予超级管理员角色");
}
}
private async Task SeedCourseCategoriesAsync()
{
var defaults = new[]
{
("BASIC", "基础课程", 10),
("MORAL", "德育课程", 20),
("AESTHETIC", "美育课程", 30),
("LABOR", "劳动教育", 40),
("INNOVATION", "创新创业", 50),
("ENGLISH", "大学英语", 60),
("SPORTS", "大学体育", 70),
("MILITARY", "国防教育", 80),
("MAJOR", "专业教育", 90),
("PRACTICE", "实践教学", 100)
};
var existingCodes = (await db.CourseCategories
.Select(x => x.Code)
.ToListAsync())
.ToHashSet(StringComparer.OrdinalIgnoreCase);
foreach (var (code, name, sortOrder) in defaults)
{
if (existingCodes.Contains(code))
{
continue;
}
db.CourseCategories.Add(new CourseCategory
{
Code = code,
Name = name,
SortOrder = sortOrder
});
}
await db.SaveChangesAsync();
var categories = await db.CourseCategories
.ToDictionaryAsync(x => x.Code, StringComparer.OrdinalIgnoreCase);
var uncategorizedCourses = await db.Courses
.Where(x => x.CourseCategoryId == null)
.ToListAsync();
foreach (var course in uncategorizedCourses)
{
var categoryCode = course.Nature switch
{
CourseNature.Practice => "PRACTICE",
CourseNature.MajorRequired or CourseNature.MajorElective => "MAJOR",
_ => "BASIC"
};
course.CourseCategoryId = categories[categoryCode].Id;
}
await db.SaveChangesAsync();
}
private static void EnsureSucceeded(IdentityResult result, string action)
{
if (result.Succeeded)
{
return;
}
throw new InvalidOperationException(
$"{action}失败:{string.Join("", result.Errors.Select(x => x.Description))}");
}
}