first commit
This commit is contained in:
@@ -0,0 +1,509 @@
|
||||
using System.Data;
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Application.Interfaces.SnowflakeID;
|
||||
using F9WebAiTest.Domain.Common;
|
||||
using F9WebAiTest.Domain.Entities;
|
||||
using F9WebAiTest.Domain.Entities.Base;
|
||||
using F9WebAiTest.Domain.Entities.Notice;
|
||||
using F9WebAiTest.Domain.Enums.SYS.RBAC;
|
||||
using SysMenu = F9WebAiTest.Domain.Entities.SYS.RBAC.Menu;
|
||||
using SysPermission = F9WebAiTest.Domain.Entities.SYS.RBAC.Permission;
|
||||
using SysRolePermission = F9WebAiTest.Domain.Entities.SYS.RBAC.RolePermission;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.Data;
|
||||
|
||||
public static class DbSeeder
|
||||
{
|
||||
public static void Seed(IFreeSql fsql, ISnowflakeIdGenerator snowflake)
|
||||
{
|
||||
// 1. 建表(CodeFirst SyncStructure,增量同步,兼容旧库)
|
||||
SyncStructure(fsql);
|
||||
|
||||
// 2. 默认角色
|
||||
if (!fsql.Select<Role>().Any())
|
||||
{
|
||||
fsql.Insert(new Role { Name = "超级管理员", Code = "admin", Description = "拥有所有权限", Sort = 1 }).ExecuteAffrows();
|
||||
fsql.Insert(new Role { Name = "普通用户", Code = "user", Description = "普通员工", Sort = 2 }).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 3. 默认菜单(系统管理)
|
||||
if (!fsql.Select<Menu>().Any())
|
||||
{
|
||||
var systemId = InsertAndGetId(fsql, new Menu { Name = "系统管理", Type = "directory", Path = "/system", Icon = "Setting", Sort = 1 });
|
||||
var deptId = InsertAndGetId(fsql, new Menu { Name = "部门管理", Type = "menu", Path = "/system/department", Component = "system/department/index", Icon = "OfficeBuilding", Permission = "system:department:list", Sort = 1, ParentId = systemId });
|
||||
var empId = InsertAndGetId(fsql, new Menu { Name = "职工管理", Type = "menu", Path = "/system/employee", Component = "system/employee/index", Icon = "User", Permission = "system:employee:list", Sort = 2, ParentId = systemId });
|
||||
var roleId = InsertAndGetId(fsql, new Menu { Name = "角色管理", Type = "menu", Path = "/system/role", Component = "system/role/index", Icon = "UserFilled", Permission = "system:role:list", Sort = 3, ParentId = systemId });
|
||||
var menuId = InsertAndGetId(fsql, new Menu { Name = "菜单管理", Type = "menu", Path = "/system/menu", Component = "system/menu/index", Icon = "Menu", Permission = "system:menu:list", Sort = 4, ParentId = systemId });
|
||||
var logId = InsertAndGetId(fsql, new Menu { Name = "操作日志", Type = "menu", Path = "/system/log", Component = "system/log/index", Icon = "Document", Permission = "system:log:list", Sort = 5, ParentId = systemId });
|
||||
fsql.Insert(new Menu[]
|
||||
{
|
||||
new Menu { Name = "部门操作", Type = "button", Permission = "system:department:btn", Sort = 1, ParentId = deptId },
|
||||
new Menu { Name = "职工操作", Type = "button", Permission = "system:employee:btn", Sort = 1, ParentId = empId },
|
||||
new Menu { Name = "角色操作", Type = "button", Permission = "system:role:btn", Sort = 1, ParentId = roleId },
|
||||
new Menu { Name = "菜单操作", Type = "button", Permission = "system:menu:btn", Sort = 1, ParentId = menuId },
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 4. 考勤菜单(增量)
|
||||
SeedAttendanceMenus(fsql);
|
||||
|
||||
// 5. 业务基础数据(部门树 + 机组车台 + 庄口 + 规格 + 定级指标 + 菜单)
|
||||
BusinessSeedData.Seed(fsql);
|
||||
|
||||
// 6. 通知:消息类型种子 + 菜单
|
||||
SeedNotificationData(fsql, snowflake);
|
||||
|
||||
// 6.5 数据迁移:旧 RBAC 表 → 新 DDD RBAC 表(先迁移,确保 admin 角色/用户已在新表)
|
||||
MigrateRbacData(fsql);
|
||||
|
||||
// 6.6 F9 风格 RBAC 菜单(用户/角色/菜单/部门管理,写入新 sys_rbac_menus 表)
|
||||
SeedSysRbacMenus(fsql, snowflake);
|
||||
|
||||
// 6.7 为菜单创建权限单元(Permission),并修正旧菜单权限关联(统一权限模型)
|
||||
FixMenuPermissions(fsql, snowflake);
|
||||
|
||||
// 7. 管理员账号(幂等)
|
||||
var admin = fsql.Select<Employee>().Where(e => e.Username == "admin").First();
|
||||
if (admin == null)
|
||||
{
|
||||
var dept = fsql.Select<Department>().Where(d => d.Name == "主干运维").First()
|
||||
?? fsql.Select<Department>().First();
|
||||
var adminId = InsertAndGetId(fsql, new Employee
|
||||
{
|
||||
Username = "admin",
|
||||
Password = BCrypt.Net.BCrypt.HashPassword("123456"),
|
||||
RealName = "系统管理员",
|
||||
Nickname = "管理员",
|
||||
DepartmentId = dept.Id,
|
||||
Email = "admin@company.com",
|
||||
Phone = "13800000000",
|
||||
Status = 1
|
||||
});
|
||||
admin = fsql.Select<Employee>().Where(e => e.Id == adminId).First();
|
||||
}
|
||||
|
||||
// 8. admin 关联 admin 角色
|
||||
var adminRole = fsql.Select<Role>().Where(r => r.Code == "admin").First();
|
||||
if (!fsql.Select<EmployeeRole>().Any(er => er.EmployeeId == admin.Id && er.RoleId == adminRole.Id))
|
||||
{
|
||||
fsql.Insert(new EmployeeRole { EmployeeId = admin.Id, RoleId = adminRole.Id }).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 9. admin 角色分配所有菜单(幂等补充)
|
||||
var allMenus = fsql.Select<Menu>().ToList();
|
||||
foreach (var m in allMenus)
|
||||
{
|
||||
if (!fsql.Select<RoleMenu>().Any(rm => rm.RoleId == adminRole.Id && rm.MenuId == m.Id))
|
||||
{
|
||||
fsql.Insert(new RoleMenu { RoleId = adminRole.Id, MenuId = m.Id }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>CodeFirst 建表(新表建表,旧表同步失败则跳过)+ 旧库加列兜底</summary>
|
||||
private static void SyncStructure(IFreeSql fsql)
|
||||
{
|
||||
// 扫描 BaseEntity(旧)与 Entity(新 DDD 基类)两类实体的具体子类建表
|
||||
var entityTypes = typeof(BaseEntity).Assembly.GetTypes()
|
||||
.Where(t => t.IsClass && !t.IsAbstract &&
|
||||
(t.IsSubclassOf(typeof(BaseEntity)) || t.IsSubclassOf(typeof(Entity))));
|
||||
foreach (var t in entityTypes)
|
||||
{
|
||||
try
|
||||
{
|
||||
fsql.CodeFirst.SyncStructure(t);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 旧表同步失败(如唯一索引与历史数据冲突),跳过,保持旧库兼容
|
||||
Console.WriteLine($"[DbSeeder] 表 {t.Name} 结构同步跳过:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 旧库加列兜底(SyncStructure 不会为已有表加列)
|
||||
EnsureColumn(fsql, "LeaveRequests", "CurrentOrderNo", "INTEGER NOT NULL DEFAULT 1");
|
||||
EnsureColumn(fsql, "NotificationTypes", "Category", "INTEGER NOT NULL DEFAULT 0");
|
||||
EnsureColumn(fsql, "NotificationTypes", "Icon", "TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(fsql, "NotificationTypes", "RouteTemplate", "TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(fsql, "NotificationMessages", "Category", "INTEGER NOT NULL DEFAULT 0");
|
||||
EnsureColumn(fsql, "NotificationMessages", "RoutePath", "TEXT NOT NULL DEFAULT ''");
|
||||
}
|
||||
|
||||
/// <summary>为已有表补加列(列已存在则忽略)</summary>
|
||||
private static void EnsureColumn(IFreeSql fsql, string table, string column, string definition)
|
||||
{
|
||||
try
|
||||
{
|
||||
fsql.Ado.ExecuteNonQuery($"ALTER TABLE \"{table}\" ADD COLUMN \"{column}\" {definition};");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 列已存在,忽略
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>插入并返回自增主键 ID(SQLite 用 last_insert_rowid 可靠获取)</summary>
|
||||
public static long InsertAndGetId<T>(IFreeSql fsql, T entity) where T : BaseEntity
|
||||
{
|
||||
fsql.Insert(entity).ExecuteAffrows();
|
||||
return Convert.ToInt64(fsql.Ado.ExecuteScalar("SELECT last_insert_rowid();"));
|
||||
}
|
||||
|
||||
/// <summary>通知种子:消息类型 + 消息中心菜单</summary>
|
||||
private static void SeedNotificationData(IFreeSql fsql, ISnowflakeIdGenerator snowflake)
|
||||
{
|
||||
// 消息类型:发现特野
|
||||
if (!fsql.Select<NotificationType>().Any(t => t.Code == "TeyeFound"))
|
||||
{
|
||||
fsql.Insert(new NotificationType
|
||||
{
|
||||
Code = "TeyeFound", Name = "发现特野", Description = "纤度检验发现特野时自动推送提醒",
|
||||
RequiredPermission = "", IsActive = true, CreatedBy = 0, Sort = 1,
|
||||
Category = NotificationCategory.Notification, Icon = "Warning", RouteTemplate = "/prod/xdjy"
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 消息类型:发现特大糙
|
||||
if (!fsql.Select<NotificationType>().Any(t => t.Code == "TdcFound"))
|
||||
{
|
||||
fsql.Insert(new NotificationType
|
||||
{
|
||||
Code = "TdcFound", Name = "发现特大糙", Description = "黑板检验发现特大糙时自动推送提醒",
|
||||
RequiredPermission = "", IsActive = true, CreatedBy = 0, Sort = 2,
|
||||
Category = NotificationCategory.Notification, Icon = "Warning", RouteTemplate = "/prod/hbjy"
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 消息类型:请假审批(订阅权限用独立码,避免与业务按钮权限冲突)
|
||||
if (!fsql.Select<NotificationType>().Any(t => t.Code == "LeaveApproval"))
|
||||
{
|
||||
fsql.Insert(new NotificationType
|
||||
{
|
||||
Code = "LeaveApproval", Name = "请假审批", Description = "员工提交请假申请后,推送待办给审批人",
|
||||
RequiredPermission = "notification:subscribe:leaveapproval", IsActive = true, CreatedBy = 0, Sort = 3,
|
||||
Category = NotificationCategory.Todo, Icon = "Checked", RouteTemplate = "/attendance/approve?leaveId={businessId}"
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 消息类型:请假结果通知
|
||||
if (!fsql.Select<NotificationType>().Any(t => t.Code == "LeaveResult"))
|
||||
{
|
||||
fsql.Insert(new NotificationType
|
||||
{
|
||||
Code = "LeaveResult", Name = "请假结果通知", Description = "请假审批完成后通知申请人结果",
|
||||
RequiredPermission = "", IsActive = true, CreatedBy = 0, Sort = 4,
|
||||
Category = NotificationCategory.Notification, Icon = "Message", RouteTemplate = "/attendance/my"
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 消息类型:广播通知(有权限的用户主动发送的广播,推送给所有订阅者)
|
||||
if (!fsql.Select<NotificationType>().Any(t => t.Code == "Broadcast"))
|
||||
{
|
||||
fsql.Insert(new NotificationType
|
||||
{
|
||||
Code = "Broadcast", Name = "广播通知", Description = "用户发送的广播通知,推送给所有订阅该类型的用户",
|
||||
RequiredPermission = "", IsActive = true, CreatedBy = 0, Sort = 5,
|
||||
Category = NotificationCategory.Announcement, Icon = "Bell", RouteTemplate = ""
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 广播发送权限单元(ResourceType=操作,Code=notification:broadcast,admin 自动拥有,普通用户可在角色管理分配)
|
||||
var broadcastPerm = fsql.Select<SysPermission>()
|
||||
.Where(p => p.Code == "notification:broadcast").First();
|
||||
if (broadcastPerm == null)
|
||||
{
|
||||
var perm = new SysPermission(snowflake.CreateId(), Permission_Type_Enum.Enable, Resource_Type_Enum.操作, "broadcast", 0);
|
||||
perm.SetCode("notification:broadcast");
|
||||
fsql.Insert(perm).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 菜单:消息中心
|
||||
var notiDirId = fsql.Select<Menu>().Where(m => m.Path == "/notification").First()?.Id ?? 0;
|
||||
if (notiDirId == 0)
|
||||
{
|
||||
notiDirId = InsertAndGetId(fsql, new Menu { Name = "消息中心", Type = "directory", Path = "/notification", Icon = "Bell", Sort = 5 });
|
||||
}
|
||||
|
||||
AddNotificationMenu(fsql, notiDirId, "我的消息", "/notification/messages", "notification/messages/index", "Message", "notification:messages:list", 1);
|
||||
AddNotificationMenu(fsql, notiDirId, "消息订阅", "/notification/subscribe", "notification/subscribe/index", "BellFilled", "notification:subscribe:list", 2);
|
||||
AddNotificationMenu(fsql, notiDirId, "消息类型管理", "/notification/type", "notification/type/index", "SetUp", "notification:type:list", 3);
|
||||
|
||||
// 清理历史遗留:发现特野/发现特大糙 改为「所有人可订阅」
|
||||
foreach (var (code, oldPerm) in new[] { ("TeyeFound", "notification:subscribe:foundteye"), ("TdcFound", "notification:subscribe:foundtedacao") })
|
||||
{
|
||||
var t = fsql.Select<NotificationType>().Where(x => x.Code == code).First();
|
||||
if (t == null) continue;
|
||||
|
||||
// 1. RequiredPermission 改为空(所有人可订阅)
|
||||
if (!string.IsNullOrEmpty(t.RequiredPermission))
|
||||
{
|
||||
t.RequiredPermission = "";
|
||||
fsql.Update<NotificationType>().SetSource(t).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 2. 删除订阅权限单元及其角色关联(新 Permission 表)
|
||||
var perm = fsql.Select<SysPermission>()
|
||||
.Where(p => p.ResourceType == Resource_Type_Enum.消息订阅 && p.ResourceId == t.Id).First();
|
||||
if (perm != null)
|
||||
{
|
||||
fsql.Delete<SysRolePermission>().Where(rp => rp.PermissionId == perm.Id).ExecuteAffrows();
|
||||
fsql.Delete<SysPermission>().Where(p => p.Id == perm.Id).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 3. 删除历史遗留的旧"订阅按钮"菜单及其角色关联(新表 sys_rbac_menus + 旧表 Menus)
|
||||
fsql.Ado.ExecuteNonQuery($@"
|
||||
DELETE FROM sys_rbac_role_permissions WHERE permission_id IN
|
||||
(SELECT id FROM sys_rbac_menus WHERE permission = '{oldPerm}');
|
||||
DELETE FROM sys_rbac_menus WHERE permission = '{oldPerm}';
|
||||
DELETE FROM RoleMenus WHERE MenuId IN (SELECT Id FROM Menus WHERE Permission = '{oldPerm}');
|
||||
DELETE FROM Menus WHERE Permission = '{oldPerm}';");
|
||||
}
|
||||
|
||||
// 清理所有旧的"订阅按钮"菜单(已被 Permission 表的消息订阅权限替代,避免与消息订阅权限重复冗余)
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
DELETE FROM sys_rbac_role_permissions WHERE permission_id IN
|
||||
(SELECT id FROM sys_rbac_menus WHERE name LIKE '订阅:%' AND menu_type = 0);
|
||||
DELETE FROM sys_rbac_menus WHERE name LIKE '订阅:%' AND menu_type = 0;
|
||||
DELETE FROM RoleMenus WHERE MenuId IN (SELECT Id FROM Menus WHERE Name LIKE '订阅:%' AND Type = 'button');
|
||||
DELETE FROM Menus WHERE Name LIKE '订阅:%' AND Type = 'button';");
|
||||
|
||||
// 清理 RequiredPermission 为空的消息类型的订阅权限(历史遗留,所有人可订阅无需权限单元)
|
||||
foreach (var t in fsql.Select<NotificationType>().Where(x => x.RequiredPermission == "").ToList())
|
||||
{
|
||||
var perm = fsql.Select<SysPermission>()
|
||||
.Where(p => p.ResourceType == Resource_Type_Enum.消息订阅 && p.ResourceId == t.Id).First();
|
||||
if (perm != null)
|
||||
{
|
||||
fsql.Delete<SysRolePermission>().Where(rp => rp.PermissionId == perm.Id).ExecuteAffrows();
|
||||
fsql.Delete<SysPermission>().Where(p => p.Id == perm.Id).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
|
||||
// 同步订阅权限到 RBAC 权限单元(ResourceType=消息订阅,Code=RequiredPermission)
|
||||
foreach (var t in fsql.Select<NotificationType>().ToList())
|
||||
{
|
||||
if (string.IsNullOrEmpty(t.RequiredPermission)) continue;
|
||||
var existPerm = fsql.Select<SysPermission>()
|
||||
.Where(p => p.ResourceType == Resource_Type_Enum.消息订阅 && p.ResourceId == t.Id).First();
|
||||
if (existPerm == null)
|
||||
{
|
||||
var perm = new SysPermission(snowflake.CreateId(), Permission_Type_Enum.Visble, Resource_Type_Enum.消息订阅, t.Code, t.Id);
|
||||
perm.SetCode(t.RequiredPermission);
|
||||
fsql.Insert(perm).ExecuteAffrows();
|
||||
}
|
||||
else if (existPerm.Code != t.RequiredPermission)
|
||||
{
|
||||
// 修正 Code 为业务权限码(覆盖旧的自动生成码)
|
||||
existPerm.SetCode(t.RequiredPermission);
|
||||
fsql.Update<SysPermission>().SetSource(existPerm).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
|
||||
// admin 角色分配消息中心菜单权限
|
||||
var adminRole = fsql.Select<Role>().Where(r => r.Code == "admin").First();
|
||||
if (adminRole != null)
|
||||
{
|
||||
var notiMenus = fsql.Select<Menu>().Where(m => m.Path.StartsWith("/notification")).ToList();
|
||||
foreach (var m in notiMenus)
|
||||
{
|
||||
if (!fsql.Select<RoleMenu>().Any(rm => rm.RoleId == adminRole.Id && rm.MenuId == m.Id))
|
||||
fsql.Insert(new RoleMenu { RoleId = adminRole.Id, MenuId = m.Id }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddNotificationMenu(IFreeSql fsql, long parentId, string name, string path, string component, string icon, string permission, int sort)
|
||||
{
|
||||
if (!fsql.Select<Menu>().Any(m => m.Path == path))
|
||||
{
|
||||
fsql.Insert(new Menu { ParentId = parentId, Name = name, Type = "menu", Path = path, Component = component, Icon = icon, Permission = permission, Sort = sort }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
|
||||
private static void SeedAttendanceMenus(IFreeSql fsql)
|
||||
{
|
||||
var attDirId = fsql.Select<Menu>().Where(m => m.Path == "/attendance").First()?.Id ?? 0;
|
||||
if (attDirId == 0)
|
||||
{
|
||||
attDirId = InsertAndGetId(fsql, new Menu { Name = "考勤管理", Type = "directory", Path = "/attendance", Icon = "Clock", Sort = 2 });
|
||||
}
|
||||
|
||||
void AddMenu(string name, string path, string component, string icon, string permission, int sort)
|
||||
{
|
||||
if (!fsql.Select<Menu>().Any(m => m.Path == path))
|
||||
{
|
||||
fsql.Insert(new Menu { Name = name, Type = "menu", Path = path, Component = component, Icon = icon, Permission = permission, Sort = sort, ParentId = attDirId }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
AddMenu("考勤打卡", "/attendance/punch", "attendance/punch/index", "AlarmClock", "attendance:punch:list", 1);
|
||||
AddMenu("我的考勤", "/attendance/my", "attendance/my/index", "Calendar", "attendance:my:list", 2);
|
||||
AddMenu("请假申请", "/attendance/leave", "attendance/leave/index", "Suitcase", "attendance:leave:list", 3);
|
||||
AddMenu("考勤记录", "/attendance/records", "attendance/records/index", "List", "attendance:records:list", 4);
|
||||
AddMenu("请假审批", "/attendance/approve", "attendance/approve/index", "Checked", "attendance:approve:list", 5);
|
||||
|
||||
void AddButton(string menuPath, string name, string permission)
|
||||
{
|
||||
var menu = fsql.Select<Menu>().Where(m => m.Path == menuPath).First();
|
||||
if (menu != null && !fsql.Select<Menu>().Any(m => m.Permission == permission))
|
||||
{
|
||||
fsql.Insert(new Menu { Name = name, Type = "button", Permission = permission, Sort = 1, ParentId = menu.Id }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
AddButton("/attendance/punch", "打卡操作", "attendance:punch:btn");
|
||||
AddButton("/attendance/leave", "请假操作", "attendance:leave:btn");
|
||||
AddButton("/attendance/approve", "审批操作", "attendance:approve:btn");
|
||||
|
||||
var adminRole = fsql.Select<Role>().Where(r => r.Code == "admin").First();
|
||||
if (adminRole != null)
|
||||
{
|
||||
var attMenus = fsql.Select<Menu>().Where(m => m.Path.StartsWith("/attendance") || m.Permission.StartsWith("attendance:")).ToList();
|
||||
foreach (var m in attMenus)
|
||||
{
|
||||
if (!fsql.Select<RoleMenu>().Any(rm => rm.RoleId == adminRole.Id && rm.MenuId == m.Id))
|
||||
fsql.Insert(new RoleMenu { RoleId = adminRole.Id, MenuId = m.Id }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>数据迁移:旧 RBAC 表 → 新 DDD RBAC 表(幂等,仅迁移尚不存在的记录)</summary>
|
||||
private static void MigrateRbacData(IFreeSql fsql)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 部门:Departments → sys_departments
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
INSERT INTO sys_departments (id, code, name, parent_id, manage_type, duty_type, description, is_active, sort_order, created_by, created_at, update_by, updated_at, is_deleted)
|
||||
SELECT Id, COALESCE(Code, ''), Name, COALESCE(ParentId, 0), 10, 5, '', Status, Sort, '', COALESCE(CreatedAt, datetime('now')), '', UpdatedAt, IsDeleted
|
||||
FROM Departments WHERE Id NOT IN (SELECT id FROM sys_departments);");
|
||||
|
||||
// 2. 角色:Roles → sys_rbac_roles
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
INSERT INTO sys_rbac_roles (id, name, code, sort_order, description, is_active, created_by, created_at, update_by, updated_at, is_deleted)
|
||||
SELECT Id, Name, Code, Sort, COALESCE(Description, ''), Status, '', COALESCE(CreatedAt, datetime('now')), '', UpdatedAt, IsDeleted
|
||||
FROM Roles WHERE Id NOT IN (SELECT id FROM sys_rbac_roles);");
|
||||
|
||||
// 3. 菜单:Menus → sys_rbac_menus
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
INSERT INTO sys_rbac_menus (id, icon_svg, code, name, menu_type, assembly_name, type_full_name, parent_id, level, is_active, sort_order, path, component, permission, created_by, created_at, update_by, updated_at, is_deleted)
|
||||
SELECT Id, COALESCE(Icon, ''), '', Name, CASE WHEN Type = 'directory' THEN 1 WHEN Type = 'menu' THEN 2 ELSE 0 END, '', '', COALESCE(ParentId, 0), 1, Status, Sort, COALESCE(Path, ''), COALESCE(Component, ''), COALESCE(Permission, ''), '', COALESCE(CreatedAt, datetime('now')), '', UpdatedAt, IsDeleted
|
||||
FROM Menus WHERE Id NOT IN (SELECT id FROM sys_rbac_menus);");
|
||||
|
||||
// 4. 用户:Employees → sys_rbac_users
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
INSERT INTO sys_rbac_users (id, department_id, user_name, phone_number, login_name, password_hash, is_active, sort_order, lastlogin_at, nickname, avatar, email, gender, created_by, created_at, update_by, updated_at, is_deleted)
|
||||
SELECT Id, DepartmentId, RealName, COALESCE(Phone, ''), Username, Password, Status, 0, NULL, COALESCE(Nickname, ''), COALESCE(Avatar, ''), COALESCE(Email, ''), COALESCE(Gender, '男'), '', COALESCE(CreatedAt, datetime('now')), '', UpdatedAt, IsDeleted
|
||||
FROM Employees WHERE Id NOT IN (SELECT id FROM sys_rbac_users);");
|
||||
|
||||
// 5. 用户角色:EmployeeRoles → sys_rbac_user_roles
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
INSERT INTO sys_rbac_user_roles (id, user_id, role_id, created_by, created_at, update_by, updated_at, is_deleted)
|
||||
SELECT Id, EmployeeId, RoleId, '', COALESCE(CreatedAt, datetime('now')), '', UpdatedAt, IsDeleted
|
||||
FROM EmployeeRoles WHERE Id NOT IN (SELECT id FROM sys_rbac_user_roles);");
|
||||
|
||||
// 6. 角色权限:RoleMenus → sys_rbac_role_permissions
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
INSERT INTO sys_rbac_role_permissions (id, role_id, permission_id, created_by, created_at, update_by, updated_at, is_deleted)
|
||||
SELECT Id, RoleId, MenuId, '', COALESCE(CreatedAt, datetime('now')), '', UpdatedAt, IsDeleted
|
||||
FROM RoleMenus WHERE Id NOT IN (SELECT id FROM sys_rbac_role_permissions);");
|
||||
|
||||
Console.WriteLine("[DbSeeder] RBAC 数据迁移完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[DbSeeder] RBAC 数据迁移跳过:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>F9 风格 RBAC 前端菜单种子(写入新 sys_rbac_menus 表,驱动前端导航)</summary>
|
||||
private static void SeedSysRbacMenus(IFreeSql fsql, ISnowflakeIdGenerator snowflake)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 删除旧"系统管理"菜单(/system/*,对接已下线的旧接口),仅保留新的 /sys 管理菜单
|
||||
fsql.Ado.ExecuteNonQuery("DELETE FROM sys_rbac_role_permissions WHERE permission_id IN (SELECT id FROM sys_rbac_menus WHERE path LIKE '/system%')");
|
||||
fsql.Ado.ExecuteNonQuery("DELETE FROM sys_rbac_menus WHERE path LIKE '/system%'");
|
||||
|
||||
// sys 目录
|
||||
long sysDirId;
|
||||
var sysDir = fsql.Ado.ExecuteDataTable("SELECT id FROM sys_rbac_menus WHERE path = '/sys'");
|
||||
if (sysDir.Rows.Count == 0)
|
||||
{
|
||||
sysDirId = snowflake.CreateId();
|
||||
fsql.Ado.ExecuteNonQuery($"INSERT INTO sys_rbac_menus (id, icon_svg, name, menu_type, parent_id, level, is_active, sort_order, path, component, permission) VALUES ({sysDirId}, 'UserFilled', '系统管理(RBAC)', 1, 0, 1, 0, 6, '/sys', '', '')");
|
||||
}
|
||||
else
|
||||
{
|
||||
sysDirId = Convert.ToInt64(sysDir.Rows[0]["id"]);
|
||||
}
|
||||
|
||||
void AddMenu(string name, string path, string component, string icon, string permission, int sort)
|
||||
{
|
||||
var exist = fsql.Ado.ExecuteDataTable($"SELECT id FROM sys_rbac_menus WHERE path = '{path}'");
|
||||
if (exist.Rows.Count > 0) return;
|
||||
var id = snowflake.CreateId();
|
||||
fsql.Ado.ExecuteNonQuery($"INSERT INTO sys_rbac_menus (id, icon_svg, name, menu_type, parent_id, level, is_active, sort_order, path, component, permission) VALUES ({id}, '{icon}', '{name}', 2, {sysDirId}, 2, 0, {sort}, '{path}', '{component}', '{permission}')");
|
||||
}
|
||||
|
||||
AddMenu("用户管理", "/sys/user", "sys/user/index", "User", "sys:user:list", 1);
|
||||
AddMenu("角色管理", "/sys/role", "sys/role/index", "UserFilled", "sys:role:list", 2);
|
||||
AddMenu("菜单管理", "/sys/menu", "sys/menu/index", "Menu", "sys:menu:list", 3);
|
||||
AddMenu("部门管理", "/sys/department", "sys/department/index", "OfficeBuilding", "sys:department:list", 4);
|
||||
|
||||
// admin 角色分配 sys 菜单权限
|
||||
var adminRole = fsql.Ado.ExecuteDataTable("SELECT id FROM sys_rbac_roles WHERE code = 'admin'");
|
||||
if (adminRole.Rows.Count > 0)
|
||||
{
|
||||
var roleId = Convert.ToInt64(adminRole.Rows[0]["id"]);
|
||||
var menus = fsql.Ado.ExecuteDataTable("SELECT id FROM sys_rbac_menus WHERE path LIKE '/sys%'");
|
||||
foreach (DataRow row in menus.Rows)
|
||||
{
|
||||
var menuId = Convert.ToInt64(row["id"]);
|
||||
var exist = fsql.Ado.ExecuteDataTable($"SELECT id FROM sys_rbac_role_permissions WHERE role_id = {roleId} AND permission_id = {menuId}");
|
||||
if (exist.Rows.Count == 0)
|
||||
{
|
||||
fsql.Ado.ExecuteNonQuery($"INSERT INTO sys_rbac_role_permissions (id, role_id, permission_id) VALUES ({snowflake.CreateId()}, {roleId}, {menuId})");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[DbSeeder] RBAC 菜单种子跳过:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>为菜单创建权限单元(Permission),并修正旧菜单权限关联(RolePermission.PermissionId 从 Menu.Id 改为 Permission.Id)</summary>
|
||||
private static void FixMenuPermissions(IFreeSql fsql, ISnowflakeIdGenerator snowflake)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var menu in fsql.Select<SysMenu>().ToList())
|
||||
{
|
||||
// 1. 查或创建菜单权限单元(ResourceType=菜单,ResourceId=Menu.Id)
|
||||
var perm = fsql.Select<SysPermission>()
|
||||
.Where(p => p.ResourceType == Resource_Type_Enum.菜单 && p.ResourceId == menu.Id).First();
|
||||
if (perm == null)
|
||||
{
|
||||
var resourceIdentity = string.IsNullOrEmpty(menu.Code) ? menu.Id.ToString() : menu.Code;
|
||||
perm = new SysPermission(snowflake.CreateId(), Permission_Type_Enum.Visble, Resource_Type_Enum.菜单, resourceIdentity, menu.Id);
|
||||
if (!string.IsNullOrEmpty(menu.Permission))
|
||||
perm.SetCode(menu.Permission);
|
||||
fsql.Insert(perm).ExecuteAffrows();
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(menu.Permission) && perm.Code != menu.Permission)
|
||||
{
|
||||
perm.SetCode(menu.Permission);
|
||||
fsql.Update<SysPermission>().SetSource(perm).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 2. 修正旧菜单权限关联(permission_id 旧值 = Menu.Id,改为 Permission.Id)
|
||||
fsql.Ado.ExecuteNonQuery($"UPDATE sys_rbac_role_permissions SET permission_id = {perm.Id} WHERE permission_id = {menu.Id};");
|
||||
}
|
||||
Console.WriteLine("[DbSeeder] 菜单权限单元同步完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[DbSeeder] 菜单权限单元同步跳过:{ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user