From dcda5fa528b26b8fe948f3bb4db0ab58cb49d51d Mon Sep 17 00:00:00 2001 From: lvfengfree Date: Tue, 20 Jan 2026 16:26:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E4=BF=9D=E6=8A=A4=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E9=98=B2=E6=AD=A2=E5=88=A0=E9=99=A4=E5=86=85=E7=BD=AE=E8=8F=9C?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/MenuController.cs | 197 +++++- .../AmtScanner.Api/Data/DbSeeder.cs | 18 +- ...20082406_AddMenuIsSystemColumn.Designer.cs | 662 ++++++++++++++++++ .../20260120082406_AddMenuIsSystemColumn.cs | 29 + .../Migrations/AppDbContextModelSnapshot.cs | 3 + backend-csharp/AmtScanner.Api/Models/Menu.cs | 5 + 6 files changed, 904 insertions(+), 10 deletions(-) create mode 100644 backend-csharp/AmtScanner.Api/Migrations/20260120082406_AddMenuIsSystemColumn.Designer.cs create mode 100644 backend-csharp/AmtScanner.Api/Migrations/20260120082406_AddMenuIsSystemColumn.cs diff --git a/backend-csharp/AmtScanner.Api/Controllers/MenuController.cs b/backend-csharp/AmtScanner.Api/Controllers/MenuController.cs index 31cde47..9cbaf51 100644 --- a/backend-csharp/AmtScanner.Api/Controllers/MenuController.cs +++ b/backend-csharp/AmtScanner.Api/Controllers/MenuController.cs @@ -1,7 +1,9 @@ +using AmtScanner.Api.Data; using AmtScanner.Api.Models; using AmtScanner.Api.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; namespace AmtScanner.Api.Controllers; @@ -12,10 +14,12 @@ namespace AmtScanner.Api.Controllers; public class MenuController : ControllerBase { private readonly IMenuService _menuService; + private readonly AppDbContext _context; - public MenuController(IMenuService menuService) + public MenuController(IMenuService menuService, AppDbContext context) { _menuService = menuService; + _context = context; } /// @@ -45,4 +49,195 @@ public class MenuController : ControllerBase var menus = await _menuService.GetAllMenusAsync(); return Ok(ApiResponse>.Success(menus)); } + + /// + /// 创建菜单 + /// + [Authorize] + [HttpPost("api/menu")] + public async Task>> CreateMenu([FromBody] CreateMenuRequest request) + { + var menu = new Menu + { + ParentId = request.ParentId, + Name = request.Name, + Path = request.Path, + Component = request.Component, + Title = request.Title, + Icon = request.Icon, + Sort = request.Sort, + IsHide = request.IsHide, + KeepAlive = request.KeepAlive, + Link = request.Link, + IsIframe = request.IsIframe, + Roles = request.Roles != null ? string.Join(",", request.Roles) : null + }; + + _context.Menus.Add(menu); + await _context.SaveChangesAsync(); + + return Ok(ApiResponse.Success(menu, "菜单创建成功")); + } + + /// + /// 更新菜单 + /// + [Authorize] + [HttpPut("api/menu/{id}")] + public async Task>> UpdateMenu(int id, [FromBody] UpdateMenuRequest request) + { + var menu = await _context.Menus.FindAsync(id); + if (menu == null) + { + return Ok(ApiResponse.Fail(404, "菜单不存在")); + } + + if (request.ParentId.HasValue) menu.ParentId = request.ParentId; + if (!string.IsNullOrEmpty(request.Name)) menu.Name = request.Name; + if (!string.IsNullOrEmpty(request.Path)) menu.Path = request.Path; + if (request.Component != null) menu.Component = request.Component; + if (!string.IsNullOrEmpty(request.Title)) menu.Title = request.Title; + if (request.Icon != null) menu.Icon = request.Icon; + if (request.Sort.HasValue) menu.Sort = request.Sort.Value; + if (request.IsHide.HasValue) menu.IsHide = request.IsHide.Value; + if (request.KeepAlive.HasValue) menu.KeepAlive = request.KeepAlive.Value; + if (request.Link != null) menu.Link = request.Link; + if (request.IsIframe.HasValue) menu.IsIframe = request.IsIframe.Value; + if (request.Roles != null) menu.Roles = string.Join(",", request.Roles); + + await _context.SaveChangesAsync(); + + return Ok(ApiResponse.Success(menu, "菜单更新成功")); + } + + /// + /// 删除菜单 + /// + [Authorize] + [HttpDelete("api/menu/{id}")] + public async Task>> DeleteMenu(int id) + { + var menu = await _context.Menus.FindAsync(id); + if (menu == null) + { + return Ok(ApiResponse.Fail(404, "菜单不存在")); + } + + // 检查是否为系统内置菜单 + if (menu.IsSystem) + { + return Ok(ApiResponse.Fail(400, "系统内置菜单不能删除")); + } + + // 检查是否有子菜单 + var hasChildren = await _context.Menus.AnyAsync(m => m.ParentId == id); + if (hasChildren) + { + return Ok(ApiResponse.Fail(400, "请先删除子菜单")); + } + + // 删除角色菜单关联 + var roleMenus = await _context.RoleMenus.Where(rm => rm.MenuId == id).ToListAsync(); + _context.RoleMenus.RemoveRange(roleMenus); + + _context.Menus.Remove(menu); + await _context.SaveChangesAsync(); + + return Ok(ApiResponse.Success(null, "菜单删除成功")); + } + + /// + /// 重置菜单为默认配置 + /// + [Authorize] + [HttpPost("api/menu/reset")] + public async Task>> ResetMenus() + { + // 清空现有菜单和角色菜单关联 + _context.RoleMenus.RemoveRange(_context.RoleMenus); + _context.Menus.RemoveRange(_context.Menus); + await _context.SaveChangesAsync(); + + // 重新创建默认菜单 + var menus = new List + { + // 仪表盘菜单(系统内置) + new() { Id = 1, Name = "Dashboard", Path = "/dashboard", Component = "/index/index", Title = "menus.dashboard.title", Icon = "ri:pie-chart-line", Sort = 1, Roles = "R_SUPER,R_ADMIN,R_USER", IsSystem = true }, + new() { Id = 2, ParentId = 1, Name = "Console", Path = "console", Component = "/dashboard/console", Title = "menus.dashboard.console", KeepAlive = false, Sort = 1, Roles = "R_SUPER,R_ADMIN,R_USER", IsSystem = true }, + + // 系统管理菜单(系统内置) + new() { Id = 10, Name = "System", Path = "/system", Component = "/index/index", Title = "menus.system.title", Icon = "ri:user-3-line", Sort = 99, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { Id = 11, ParentId = 10, Name = "User", Path = "user", Component = "/system/user", Title = "menus.system.user", KeepAlive = true, Sort = 1, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { Id = 12, ParentId = 10, Name = "Role", Path = "role", Component = "/system/role", Title = "menus.system.role", KeepAlive = true, Sort = 2, Roles = "R_SUPER", IsSystem = true }, + new() { Id = 13, ParentId = 10, Name = "UserCenter", Path = "user-center", Component = "/system/user-center", Title = "menus.system.userCenter", IsHide = true, KeepAlive = true, Sort = 3, Roles = "R_SUPER,R_ADMIN,R_USER", IsSystem = true }, + new() { Id = 14, ParentId = 10, Name = "Menus", Path = "menu", Component = "/system/menu", Title = "menus.system.menu", KeepAlive = true, Sort = 4, Roles = "R_SUPER", IsSystem = true } + }; + + _context.Menus.AddRange(menus); + await _context.SaveChangesAsync(); + + // 重新分配角色菜单 + var superRole = await _context.Roles.FirstAsync(r => r.RoleCode == "R_SUPER"); + var adminRole = await _context.Roles.FirstAsync(r => r.RoleCode == "R_ADMIN"); + var userRole = await _context.Roles.FirstAsync(r => r.RoleCode == "R_USER"); + + var allMenuIds = await _context.Menus.Select(m => m.Id).ToListAsync(); + var adminMenuIds = await _context.Menus + .Where(m => m.Roles != null && (m.Roles.Contains("R_ADMIN") || m.Roles.Contains("R_USER"))) + .Select(m => m.Id).ToListAsync(); + var userMenuIds = await _context.Menus + .Where(m => m.Roles != null && m.Roles.Contains("R_USER")) + .Select(m => m.Id).ToListAsync(); + + var roleMenus = new List(); + foreach (var menuId in allMenuIds) + roleMenus.Add(new RoleMenu { RoleId = superRole.Id, MenuId = menuId }); + foreach (var menuId in adminMenuIds) + roleMenus.Add(new RoleMenu { RoleId = adminRole.Id, MenuId = menuId }); + foreach (var menuId in userMenuIds) + roleMenus.Add(new RoleMenu { RoleId = userRole.Id, MenuId = menuId }); + + _context.RoleMenus.AddRange(roleMenus); + await _context.SaveChangesAsync(); + + return Ok(ApiResponse.Success(null, "菜单已重置为默认配置")); + } +} + +/// +/// 创建菜单请求 +/// +public class CreateMenuRequest +{ + public int? ParentId { get; set; } + public string Name { get; set; } = string.Empty; + public string Path { get; set; } = string.Empty; + public string? Component { get; set; } + public string Title { get; set; } = string.Empty; + public string? Icon { get; set; } + public int Sort { get; set; } + public bool IsHide { get; set; } + public bool KeepAlive { get; set; } + public string? Link { get; set; } + public bool IsIframe { get; set; } + public List? Roles { get; set; } +} + +/// +/// 更新菜单请求 +/// +public class UpdateMenuRequest +{ + public int? ParentId { get; set; } + public string? Name { get; set; } + public string? Path { get; set; } + public string? Component { get; set; } + public string? Title { get; set; } + public string? Icon { get; set; } + public int? Sort { get; set; } + public bool? IsHide { get; set; } + public bool? KeepAlive { get; set; } + public string? Link { get; set; } + public bool? IsIframe { get; set; } + public List? Roles { get; set; } } diff --git a/backend-csharp/AmtScanner.Api/Data/DbSeeder.cs b/backend-csharp/AmtScanner.Api/Data/DbSeeder.cs index f95fce4..271cd67 100644 --- a/backend-csharp/AmtScanner.Api/Data/DbSeeder.cs +++ b/backend-csharp/AmtScanner.Api/Data/DbSeeder.cs @@ -101,16 +101,16 @@ public static class DbSeeder var menus = new List { - // 仪表盘菜单 - 与前端 dashboard.ts 匹配 - new() { Id = 1, Name = "Dashboard", Path = "/dashboard", Component = "/index/index", Title = "menus.dashboard.title", Icon = "ri:pie-chart-line", Sort = 1, Roles = "R_SUPER,R_ADMIN,R_USER" }, - new() { Id = 2, ParentId = 1, Name = "Console", Path = "console", Component = "/dashboard/console", Title = "menus.dashboard.console", KeepAlive = false, Sort = 1, Roles = "R_SUPER,R_ADMIN,R_USER" }, + // 仪表盘菜单 - 与前端 dashboard.ts 匹配(系统内置) + new() { Id = 1, Name = "Dashboard", Path = "/dashboard", Component = "/index/index", Title = "menus.dashboard.title", Icon = "ri:pie-chart-line", Sort = 1, Roles = "R_SUPER,R_ADMIN,R_USER", IsSystem = true }, + new() { Id = 2, ParentId = 1, Name = "Console", Path = "console", Component = "/dashboard/console", Title = "menus.dashboard.console", KeepAlive = false, Sort = 1, Roles = "R_SUPER,R_ADMIN,R_USER", IsSystem = true }, - // 系统管理菜单 - 与前端 system.ts 匹配 - new() { Id = 10, Name = "System", Path = "/system", Component = "/index/index", Title = "menus.system.title", Icon = "ri:user-3-line", Sort = 99, Roles = "R_SUPER,R_ADMIN" }, - new() { Id = 11, ParentId = 10, Name = "User", Path = "user", Component = "/system/user", Title = "menus.system.user", KeepAlive = true, Sort = 1, Roles = "R_SUPER,R_ADMIN" }, - new() { Id = 12, ParentId = 10, Name = "Role", Path = "role", Component = "/system/role", Title = "menus.system.role", KeepAlive = true, Sort = 2, Roles = "R_SUPER" }, - new() { Id = 13, ParentId = 10, Name = "UserCenter", Path = "user-center", Component = "/system/user-center", Title = "menus.system.userCenter", IsHide = true, KeepAlive = true, Sort = 3, Roles = "R_SUPER,R_ADMIN,R_USER" }, - new() { Id = 14, ParentId = 10, Name = "Menus", Path = "menu", Component = "/system/menu", Title = "menus.system.menu", KeepAlive = true, Sort = 4, Roles = "R_SUPER" } + // 系统管理菜单 - 与前端 system.ts 匹配(系统内置) + new() { Id = 10, Name = "System", Path = "/system", Component = "/index/index", Title = "menus.system.title", Icon = "ri:user-3-line", Sort = 99, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { Id = 11, ParentId = 10, Name = "User", Path = "user", Component = "/system/user", Title = "menus.system.user", KeepAlive = true, Sort = 1, Roles = "R_SUPER,R_ADMIN", IsSystem = true }, + new() { Id = 12, ParentId = 10, Name = "Role", Path = "role", Component = "/system/role", Title = "menus.system.role", KeepAlive = true, Sort = 2, Roles = "R_SUPER", IsSystem = true }, + new() { Id = 13, ParentId = 10, Name = "UserCenter", Path = "user-center", Component = "/system/user-center", Title = "menus.system.userCenter", IsHide = true, KeepAlive = true, Sort = 3, Roles = "R_SUPER,R_ADMIN,R_USER", IsSystem = true }, + new() { Id = 14, ParentId = 10, Name = "Menus", Path = "menu", Component = "/system/menu", Title = "menus.system.menu", KeepAlive = true, Sort = 4, Roles = "R_SUPER", IsSystem = true } }; // 使用 IDENTITY_INSERT 插入带 ID 的数据 diff --git a/backend-csharp/AmtScanner.Api/Migrations/20260120082406_AddMenuIsSystemColumn.Designer.cs b/backend-csharp/AmtScanner.Api/Migrations/20260120082406_AddMenuIsSystemColumn.Designer.cs new file mode 100644 index 0000000..9258dfc --- /dev/null +++ b/backend-csharp/AmtScanner.Api/Migrations/20260120082406_AddMenuIsSystemColumn.Designer.cs @@ -0,0 +1,662 @@ +// +using System; +using AmtScanner.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AmtScanner.Api.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20260120082406_AddMenuIsSystemColumn")] + partial class AddMenuIsSystemColumn + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("AmtScanner.Api.Models.AmtCredential", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("IsDefault") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Username") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("AmtCredentials"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.AmtDevice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("AmtOnline") + .HasColumnType("tinyint(1)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("DiscoveredAt") + .HasColumnType("datetime(6)"); + + b.Property("Hostname") + .HasColumnType("longtext"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("LastSeenAt") + .HasColumnType("datetime(6)"); + + b.Property("MajorVersion") + .HasColumnType("int"); + + b.Property("MinorVersion") + .HasColumnType("int"); + + b.Property("OsOnline") + .HasColumnType("tinyint(1)"); + + b.Property("ProvisioningState") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("IpAddress") + .IsUnique(); + + b.ToTable("AmtDevices"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.HardwareInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DeviceId") + .HasColumnType("bigint"); + + b.Property("LastUpdated") + .HasColumnType("datetime(6)"); + + b.Property("ProcessorCores") + .HasColumnType("int"); + + b.Property("ProcessorCurrentClockSpeed") + .HasColumnType("int"); + + b.Property("ProcessorMaxClockSpeed") + .HasColumnType("int"); + + b.Property("ProcessorModel") + .HasColumnType("longtext"); + + b.Property("ProcessorThreads") + .HasColumnType("int"); + + b.Property("SystemManufacturer") + .HasColumnType("longtext"); + + b.Property("SystemModel") + .HasColumnType("longtext"); + + b.Property("SystemSerialNumber") + .HasColumnType("longtext"); + + b.Property("TotalMemoryBytes") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("DeviceId"); + + b.HasIndex("LastUpdated"); + + b.ToTable("HardwareInfos"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.MemoryModule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CapacityBytes") + .HasColumnType("bigint"); + + b.Property("HardwareInfoId") + .HasColumnType("bigint"); + + b.Property("Manufacturer") + .HasColumnType("longtext"); + + b.Property("MemoryType") + .HasColumnType("longtext"); + + b.Property("PartNumber") + .HasColumnType("longtext"); + + b.Property("SerialNumber") + .HasColumnType("longtext"); + + b.Property("SlotLocation") + .HasColumnType("longtext"); + + b.Property("SpeedMHz") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("HardwareInfoId"); + + b.ToTable("MemoryModules"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.Menu", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AuthList") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("Component") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Icon") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("IsHide") + .HasColumnType("tinyint(1)"); + + b.Property("IsHideTab") + .HasColumnType("tinyint(1)"); + + b.Property("IsIframe") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("KeepAlive") + .HasColumnType("tinyint(1)"); + + b.Property("Link") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ParentId") + .HasColumnType("int"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Roles") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Sort") + .HasColumnType("int"); + + b.Property("Title") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.HasIndex("ParentId"); + + b.ToTable("Menus"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.RemoteAccessToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DeviceId") + .HasColumnType("bigint"); + + b.Property("ExpiresAt") + .HasColumnType("datetime(6)"); + + b.Property("IsUsed") + .HasColumnType("tinyint(1)"); + + b.Property("MaxUseCount") + .HasColumnType("int"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Token") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("UseCount") + .HasColumnType("int"); + + b.Property("UsedAt") + .HasColumnType("datetime(6)"); + + b.Property("WindowsCredentialId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("DeviceId"); + + b.HasIndex("Token") + .IsUnique(); + + b.HasIndex("WindowsCredentialId"); + + b.ToTable("RemoteAccessTokens"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("RoleCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RoleName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("RoleCode") + .IsUnique(); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.RoleMenu", b => + { + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("MenuId") + .HasColumnType("int"); + + b.HasKey("RoleId", "MenuId"); + + b.HasIndex("MenuId"); + + b.ToTable("RoleMenus"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.StorageDevice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CapacityBytes") + .HasColumnType("bigint"); + + b.Property("DeviceId") + .HasColumnType("longtext"); + + b.Property("HardwareInfoId") + .HasColumnType("bigint"); + + b.Property("InterfaceType") + .HasColumnType("longtext"); + + b.Property("Model") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("HardwareInfoId"); + + b.ToTable("StorageDevices"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Avatar") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Gender") + .IsRequired() + .HasMaxLength(1) + .HasColumnType("varchar(1)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("NickName") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("PasswordHash") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Phone") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("RefreshToken") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(1) + .HasColumnType("varchar(1)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.UserRole", b => + { + b.Property("UserId") + .HasColumnType("int"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("UserRoles"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.WindowsCredential", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Domain") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IsDefault") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Password") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("WindowsCredentials"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.HardwareInfo", b => + { + b.HasOne("AmtScanner.Api.Models.AmtDevice", "Device") + .WithMany() + .HasForeignKey("DeviceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Device"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.MemoryModule", b => + { + b.HasOne("AmtScanner.Api.Models.HardwareInfo", "HardwareInfo") + .WithMany("MemoryModules") + .HasForeignKey("HardwareInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HardwareInfo"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.Menu", b => + { + b.HasOne("AmtScanner.Api.Models.Menu", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.RemoteAccessToken", b => + { + b.HasOne("AmtScanner.Api.Models.AmtDevice", "Device") + .WithMany() + .HasForeignKey("DeviceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AmtScanner.Api.Models.WindowsCredential", "WindowsCredential") + .WithMany() + .HasForeignKey("WindowsCredentialId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Device"); + + b.Navigation("WindowsCredential"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.RoleMenu", b => + { + b.HasOne("AmtScanner.Api.Models.Menu", "Menu") + .WithMany("RoleMenus") + .HasForeignKey("MenuId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AmtScanner.Api.Models.Role", "Role") + .WithMany("RoleMenus") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Menu"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.StorageDevice", b => + { + b.HasOne("AmtScanner.Api.Models.HardwareInfo", "HardwareInfo") + .WithMany("StorageDevices") + .HasForeignKey("HardwareInfoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HardwareInfo"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.UserRole", b => + { + b.HasOne("AmtScanner.Api.Models.Role", "Role") + .WithMany("UserRoles") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AmtScanner.Api.Models.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.HardwareInfo", b => + { + b.Navigation("MemoryModules"); + + b.Navigation("StorageDevices"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.Menu", b => + { + b.Navigation("Children"); + + b.Navigation("RoleMenus"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.Role", b => + { + b.Navigation("RoleMenus"); + + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("AmtScanner.Api.Models.User", b => + { + b.Navigation("UserRoles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend-csharp/AmtScanner.Api/Migrations/20260120082406_AddMenuIsSystemColumn.cs b/backend-csharp/AmtScanner.Api/Migrations/20260120082406_AddMenuIsSystemColumn.cs new file mode 100644 index 0000000..d245757 --- /dev/null +++ b/backend-csharp/AmtScanner.Api/Migrations/20260120082406_AddMenuIsSystemColumn.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AmtScanner.Api.Migrations +{ + /// + public partial class AddMenuIsSystemColumn : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsSystem", + table: "Menus", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsSystem", + table: "Menus"); + } + } +} diff --git a/backend-csharp/AmtScanner.Api/Migrations/AppDbContextModelSnapshot.cs b/backend-csharp/AmtScanner.Api/Migrations/AppDbContextModelSnapshot.cs index c2e35f6..c787c9c 100644 --- a/backend-csharp/AmtScanner.Api/Migrations/AppDbContextModelSnapshot.cs +++ b/backend-csharp/AmtScanner.Api/Migrations/AppDbContextModelSnapshot.cs @@ -224,6 +224,9 @@ namespace AmtScanner.Api.Migrations b.Property("IsIframe") .HasColumnType("tinyint(1)"); + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + b.Property("KeepAlive") .HasColumnType("tinyint(1)"); diff --git a/backend-csharp/AmtScanner.Api/Models/Menu.cs b/backend-csharp/AmtScanner.Api/Models/Menu.cs index 546a7c1..c465b3f 100644 --- a/backend-csharp/AmtScanner.Api/Models/Menu.cs +++ b/backend-csharp/AmtScanner.Api/Models/Menu.cs @@ -73,6 +73,11 @@ public class Menu /// public bool IsIframe { get; set; } = false; + /// + /// 是否系统内置菜单(不可删除) + /// + public bool IsSystem { get; set; } = false; + /// /// 是否隐藏标签页 ///