From 7d3cae2d8d09746ad1ae8b8bda2e9f1cf3f4bb00 Mon Sep 17 00:00:00 2001 From: luolan Date: Tue, 15 Jul 2025 22:21:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AD=97=E5=85=B8=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E4=BF=AE=E5=A4=8D=E4=B8=80=E5=A0=86bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Identity/Pages/Account/Login.cshtml.cs | 2 +- .../Identity/Pages/Account/Register.cshtml.cs | 2 +- .../Admin/AdminDictionaryController.cs | 203 --------- .../Controllers/Admin/AdminRoleControllers.cs | 17 +- AGSS/Controllers/Dict/DictController.cs | 228 ++++++++++ AGSS/DbSet/UserSet.cs | 47 +- .../20250714035418_AddMenuTableV2.Designer.cs | 425 ------------------ .../20250714035418_AddMenuTableV2.cs | 22 - .../ApplicationDbContextModelSnapshot.cs | 122 +++-- AGSS/Models/DTOs/DictionaryDto.cs | 1 - AGSS/Models/Entities/DictionaryModel.cs | 53 ++- AGSS/Program.cs | 7 + AGSS/Services/CurrentUserService.cs | 25 ++ AGSS/Services/DictService.cs | 175 ++++++++ script.sql | 117 +---- 15 files changed, 620 insertions(+), 826 deletions(-) delete mode 100644 AGSS/Controllers/Admin/AdminDictionaryController.cs create mode 100644 AGSS/Controllers/Dict/DictController.cs delete mode 100644 AGSS/Migrations/20250714035418_AddMenuTableV2.Designer.cs delete mode 100644 AGSS/Migrations/20250714035418_AddMenuTableV2.cs create mode 100644 AGSS/Services/CurrentUserService.cs create mode 100644 AGSS/Services/DictService.cs diff --git a/AGSS/Areas/Identity/Pages/Account/Login.cshtml.cs b/AGSS/Areas/Identity/Pages/Account/Login.cshtml.cs index 99034d9..b08dc0b 100644 --- a/AGSS/Areas/Identity/Pages/Account/Login.cshtml.cs +++ b/AGSS/Areas/Identity/Pages/Account/Login.cshtml.cs @@ -136,7 +136,7 @@ namespace AGSS.Areas.Identity.Pages.Account var roles = await _userManager.GetRolesAsync(user); foreach (string role in roles) { - claims.Add(new Claim(ClaimTypes.Role, role)); + claims.Add(new Claim("role", role)); } string jwtToken = _jwt.BuildToken(claims, jwtOptions.Value); diff --git a/AGSS/Areas/Identity/Pages/Account/Register.cshtml.cs b/AGSS/Areas/Identity/Pages/Account/Register.cshtml.cs index a5310f8..033fcb5 100644 --- a/AGSS/Areas/Identity/Pages/Account/Register.cshtml.cs +++ b/AGSS/Areas/Identity/Pages/Account/Register.cshtml.cs @@ -148,7 +148,7 @@ namespace AGSS.Areas.Identity.Pages.Account var roles = await _userManager.GetRolesAsync(user); foreach (string role in roles) { - claims.Add(new Claim(ClaimTypes.Role, role)); + claims.Add(new Claim("role", role)); } string jwtToken = _jwt.BuildToken(claims, jwtOptions.Value); var frontendCallback = $"{Request.Query["frontendCallback"]}?token={jwtToken}"; diff --git a/AGSS/Controllers/Admin/AdminDictionaryController.cs b/AGSS/Controllers/Admin/AdminDictionaryController.cs deleted file mode 100644 index 45bc88b..0000000 --- a/AGSS/Controllers/Admin/AdminDictionaryController.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System; -using AGSS.Models.Entities; -using AGSS.Models.Template; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using System.Linq; -using System.Threading.Tasks; -using AGSS.DbSet; - -namespace AGSS.Controllers.Admin -{ - - [Route("api/v1/[controller]/[action]")] - public class AdminDictionaryController : ControllerBase - { - private readonly ApplicationDbContext _dbContext; - private readonly UserManager _userManager; - - public AdminDictionaryController(ApplicationDbContext dbContext, UserManager userManager) - { - _dbContext = dbContext; - _userManager = userManager; - } - - [HttpPost] - [Authorize(Roles = "Admin")] - public async Task GetParentDictionaries([FromBody] string label) - { - - - // 确保 label 不是 null - label ??= string.Empty; - - var parentDictionaries = _dbContext.Dictionaries.Where(d => d.ParentId == null && (string.IsNullOrEmpty(label) || d.Label.Contains(label))).ToList(); - return Ok(new ReturnTemplate(200, "查询成功", parentDictionaries)); - } - - [HttpPost] - [Authorize(Roles = "Admin")] - public async Task AddParentDictionary([FromBody] DictionaryModel dictionary) - { - - - if (dictionary == null || string.IsNullOrWhiteSpace(dictionary.Label) || string.IsNullOrWhiteSpace(dictionary.Value)) - { - return Ok(new ReturnTemplate(400, "请求参数无效,请提供Label和Value", null)); - } - - dictionary.Uuid = Guid.NewGuid().ToString(); - dictionary.CreateTime = DateTime.UtcNow; - dictionary.CreateUserId = _userManager.GetUserId(User); - - _dbContext.Dictionaries.Add(dictionary); - await _dbContext.SaveChangesAsync(); - - return Ok(new ReturnTemplate(200, "添加父级字典成功", dictionary)); - } - - [HttpPut] - [Authorize(Roles = "Admin")] - public async Task UpdateParentDictionary([FromBody] DictionaryModel dictionary) - { - - - if (dictionary == null || string.IsNullOrWhiteSpace(dictionary.Uuid) || string.IsNullOrWhiteSpace(dictionary.Label)) - { - return Ok(new ReturnTemplate(400, "请求参数无效,请提供Uuid和Label", null)); - } - - var existingDictionary = _dbContext.Dictionaries.FirstOrDefault(d => d.Uuid == dictionary.Uuid && d.ParentId == null); - if (existingDictionary == null) - { - return Ok(new ReturnTemplate(404, "未找到指定的父级字典", null)); - } - - existingDictionary.Label = dictionary.Label; - await _dbContext.SaveChangesAsync(); - - return Ok(new ReturnTemplate(200, "更新父级字典成功", existingDictionary)); - } - - [HttpDelete] - [Authorize(Roles = "Admin")] - public async Task DeleteParentDictionary([FromBody] string uuid) - { - - - if (string.IsNullOrWhiteSpace(uuid)) - { - return Ok(new ReturnTemplate(400, "请求参数无效,请提供Uuid", null)); - } - - var parentDictionary = _dbContext.Dictionaries.FirstOrDefault(d => d.Uuid == uuid && d.ParentId == null); - if (parentDictionary == null) - { - return Ok(new ReturnTemplate(404, "未找到指定的父级字典", null)); - } - - _dbContext.Dictionaries.RemoveRange(_dbContext.Dictionaries.Where(d => d.ParentId == uuid)); - _dbContext.Dictionaries.Remove(parentDictionary); - await _dbContext.SaveChangesAsync(); - - return Ok(new ReturnTemplate(200, "删除父级字典成功", null)); - } - - [HttpPost] - [Authorize(Roles = "Admin")] - public async Task CreateChildDictionary([FromBody] DictionaryModel dictionary) - { - - - if (dictionary == null || string.IsNullOrWhiteSpace(dictionary.ParentId) || string.IsNullOrWhiteSpace(dictionary.ParentValue) || string.IsNullOrWhiteSpace(dictionary.Label) || string.IsNullOrWhiteSpace(dictionary.Value)) - { - return Ok(new ReturnTemplate(400, "请求参数无效,请提供ParentId、ParentValue、Label和Value", null)); - } - - dictionary.Uuid = Guid.NewGuid().ToString(); - dictionary.CreateTime = DateTime.UtcNow; - dictionary.CreateUserId = _userManager.GetUserId(User); - - _dbContext.Dictionaries.Add(dictionary); - await _dbContext.SaveChangesAsync(); - - return Ok(new ReturnTemplate(200, "创建子级字典成功", dictionary)); - } - - [HttpPut] - [Authorize(Roles = "Admin")] - public async Task UpdateChildDictionary([FromBody] DictionaryModel dictionary) - { - - - if (dictionary == null || string.IsNullOrWhiteSpace(dictionary.Uuid) || string.IsNullOrWhiteSpace(dictionary.Label) || string.IsNullOrWhiteSpace(dictionary.Value)) - { - return Ok(new ReturnTemplate(400, "请求参数无效,请提供Uuid、Label和Value", null)); - } - - var existingDictionary = _dbContext.Dictionaries.FirstOrDefault(d => d.Uuid == dictionary.Uuid && d.ParentId != null); - if (existingDictionary == null) - { - return Ok(new ReturnTemplate(404, "未找到指定的子级字典", null)); - } - - existingDictionary.Label = dictionary.Label; - existingDictionary.LabelEn = dictionary.LabelEn; - existingDictionary.Remark = dictionary.Remark; - existingDictionary.Value = dictionary.Value; - existingDictionary.Tag = dictionary.Tag; - await _dbContext.SaveChangesAsync(); - - return Ok(new ReturnTemplate(200, "更新子级字典成功", existingDictionary)); - } - - [HttpDelete] - [Authorize(Roles = "Admin")] - public async Task DeleteChildDictionary([FromBody] string uuid) - { - - if (string.IsNullOrWhiteSpace(uuid)) - { - return Ok(new ReturnTemplate(400, "请求参数无效,请提供Uuid", null)); - } - - var childDictionary = _dbContext.Dictionaries.FirstOrDefault(d => d.Uuid == uuid && d.ParentId != null); - if (childDictionary == null) - { - return Ok(new ReturnTemplate(404, "未找到指定的子级字典", null)); - } - - _dbContext.Dictionaries.Remove(childDictionary); - await _dbContext.SaveChangesAsync(); - - return Ok(new ReturnTemplate(200, "删除子级字典成功", null)); - } - - [HttpPost] - public IActionResult GetChildDictionaries([FromBody] ChildDictionaryRequest request) - { - if (string.IsNullOrWhiteSpace(request.Value)) - { - return Ok(new ReturnTemplate(400, "请求参数无效,请提供Value", null)); - } - - var childDictionaries = _dbContext.Dictionaries - .Where(d => d.ParentValue == request.Value && - (request.Tag == null || (!string.IsNullOrEmpty(d.Tag) && d.Tag.Contains("," + request.Tag + ",")))) - .ToList(); - - if (request.Tag != null) - { - childDictionaries = childDictionaries.Where(d => !string.IsNullOrEmpty(d.Tag)).ToList(); - } - - return Ok(new ReturnTemplate(200, "查询成功", childDictionaries)); - } - public class ChildDictionaryRequest - { - public string Value { get; set; } - public string Tag { get; set; } - } - } -} \ No newline at end of file diff --git a/AGSS/Controllers/Admin/AdminRoleControllers.cs b/AGSS/Controllers/Admin/AdminRoleControllers.cs index 9b898ab..606369c 100644 --- a/AGSS/Controllers/Admin/AdminRoleControllers.cs +++ b/AGSS/Controllers/Admin/AdminRoleControllers.cs @@ -18,8 +18,8 @@ namespace AGSS.Controllers.Admin; /// 控制器类,用于管理角色相关的操作,包括添加角色、分配角色给用户以及通过角色查询用户。 /// 该控制器仅限具有"Admin"角色的用户访问。 /// -[Authorize] -[Route("api/v1/[controller]/[action]")] +[Authorize(Roles = "Admin")] +[Route("api/v1/[controller]")] public class AdminRoleControllers:ControllerBase { /// @@ -60,7 +60,7 @@ public class AdminRoleControllers:ControllerBase /// /// 要添加的角色信息 /// 返回操作结果,包含状态码、消息和数据 - [HttpPost] + [HttpPost("role")] public async Task AddRole(string rolename,string normalizedname) { @@ -84,7 +84,8 @@ public class AdminRoleControllers:ControllerBase /// 用户的唯一标识符 /// 要分配的角色名称 /// 返回一个包含操作结果的ReturnTemplate对象,其中Code表示状态码,Msg表示消息,Data表示附加数据(如果有的话) - [HttpPost] + + [HttpPost("role/endow")] public async Task EndowRole(string userId, string roleName) { var user = await _userManager.FindByIdAsync(userId); @@ -116,7 +117,7 @@ public class AdminRoleControllers:ControllerBase /// /// 要删除的用户的唯一标识符。 /// 返回操作结果,包含状态码、消息和数据。如果删除成功,则返回200状态码;如果用户ID为空或未找到指定用户,则分别返回400或404状态码;若删除过程中出现错误,则返回500状态码并附带错误信息。 - [HttpPost] + [HttpDelete("user")] public async Task DelUser(string userId) { if (string.IsNullOrWhiteSpace(userId)) @@ -143,7 +144,7 @@ public class AdminRoleControllers:ControllerBase } - [HttpPost] + [HttpPost("menu")] public async Task SetMenu([FromBody]UserMenuRequest request) { if (string.IsNullOrWhiteSpace(request.Id) || string.IsNullOrWhiteSpace(request.MenuName)) @@ -169,7 +170,7 @@ public class AdminRoleControllers:ControllerBase } - [HttpGet] + [HttpGet("role/all")] public async Task AllRole() { @@ -184,7 +185,7 @@ public class AdminRoleControllers:ControllerBase /// /// 包含角色名称、页码和每页大小的请求对象 /// 返回包含总用户数和当前页用户的响应对象 - [HttpPost] + [HttpPost("user")] public async Task SearchUserFromRole([FromBody] SearchUserFromRoleRequest request) { diff --git a/AGSS/Controllers/Dict/DictController.cs b/AGSS/Controllers/Dict/DictController.cs new file mode 100644 index 0000000..3c48151 --- /dev/null +++ b/AGSS/Controllers/Dict/DictController.cs @@ -0,0 +1,228 @@ +using System.ComponentModel.DataAnnotations; +using AGSS.Models.Template; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace AGSS.Controllers.Dict +{ + [ApiController] + [Route("api/[controller]")] + // [Produces("application/json")] + public class DictController : ControllerBase + { + private readonly DictService _dictService; + private readonly ILogger _logger; + + public DictController(DictService dictService, ILogger logger) + { + _dictService = dictService; + _logger = logger; + } + + + // 1. 获取父级列表 + [HttpGet("parents")] + public async Task GetParents([FromQuery] string? label) + { + try + { + var result = await _dictService.GetParentsAsync(label); + return Ok(new ReturnTemplate(200, "获取成功啦!", result)); + } + catch (Exception ex) + { + return Ok(new ReturnTemplate(500, "失败,服务器类错误!", ex)); + } + } + +// 2. 创建父级项 + + // [Authorize(Roles = "Admin")] + [HttpPost("parents")] + public async Task CreateParent([FromBody] CreateParentRequest request) + { + try + { + var result = await _dictService.CreateParentAsync(request.Label, request.Value); + return Ok(new ReturnTemplate(200, "创建成功啦!", result)); + } + catch (Exception ex) + { + return Ok(new ReturnTemplate(500, "错误", ex.Message)); + } + } + +// 请求模型 + public class CreateParentRequest + { + [Required(ErrorMessage = "字典标签不能为空")] + [StringLength(100, ErrorMessage = "标签长度不能超过100字符")] + public string Label { get; set; } = null!; + + [Required(ErrorMessage = "字典值不能为空")] + [StringLength(50, ErrorMessage = "值长度不能超过50字符")] + public string Value { get; set; } = null!; + } + +// 2.1 更新父级项 + + [Authorize(Roles = "Admin")] + [HttpPut("parents/{uuid}")] + public async Task UpdateParent( + [FromRoute] Guid uuid, + [FromBody] UpdateParentRequest request) + { + try + { + await _dictService.UpdateParentAsync(uuid, request.Label); + return Ok(new ReturnTemplate(200, "创建成功啦!", "")); + } + catch (Exception ex) + { + return Ok(new ReturnTemplate(200, "失败", ex)); + } + } + + public class UpdateParentRequest + { + [Required(ErrorMessage = "字典标签不能为空")] + [StringLength(100, ErrorMessage = "标签长度不能超过100字符")] + public string Label { get; set; } = null!; + } + +// 3. 删除父级项 + + [Authorize(Roles = "Admin")] + [HttpDelete("parents/{uuid}")] + public async Task DeleteParent([FromRoute] Guid uuid) + { + try + { + await _dictService.DeleteParentAsync(uuid); + return Ok(new ReturnTemplate(200, "删除成功啦!", "")); + } + catch (Exception ex) + { + return Ok(new ReturnTemplate(200, "失败", ex)); + } + } + + + // 4. 创建子项 + [HttpPost("children")] + public async Task CreateChild([FromBody] CreateChildRequest request) + { + try + { + var result = await _dictService.CreateChildAsync( + request.ParentId, + request.ParentValue, + request.Label, + request.Value, + request.LabelEn, + request.Remark, + request.Tag); + + return Ok(new ReturnTemplate(200, "创建成功啦!", result)); + } + catch (Exception ex) + { + return Ok(new ReturnTemplate(200, "我们遇到了一些问题,无法删除!", "")); + } + } + + public class CreateChildRequest + { + [Required(ErrorMessage = "父级ID不能为空")] public Guid ParentId { get; set; } + + [Required(ErrorMessage = "父级值不能为空")] public string ParentValue { get; set; } = null!; + + [Required(ErrorMessage = "字典标签不能为空")] public string Label { get; set; } = null!; + + [Required(ErrorMessage = "字典值不能为空")] public string Value { get; set; } = null!; + + public string? LabelEn { get; set; } + public string? Remark { get; set; } + public string? Tag { get; set; } + } + +// 4.1 更新子项 + [HttpPut("children/{uuid}")] + public async Task UpdateChild( + [FromRoute] Guid uuid, + [FromBody] UpdateChildRequest request) + { + try + { + await _dictService.UpdateChildAsync( + uuid, + request.Label, + request.Value, + request.LabelEn, + request.Remark, + request.Tag); + + return Ok(new ReturnTemplate(200, "更新成功啦!", "")); + } + catch (Exception ex) + { + return Ok(new ReturnTemplate(200, "失败!", "")); + } + } + + public class UpdateChildRequest + { + [Required(ErrorMessage = "字典标签不能为空")] public string Label { get; set; } = null!; + + [Required(ErrorMessage = "字典值不能为空")] public string Value { get; set; } = null!; + + public string? LabelEn { get; set; } + public string? Remark { get; set; } + public string? Tag { get; set; } + } + +// 5. 删除子项 + [HttpDelete("children/{uuid}")] + public async Task DeleteChild([FromRoute] Guid uuid) + { + try + { + await _dictService.DeleteChildAsync(uuid); + return Ok(new ReturnTemplate(200, "删除成功啦!", "")); + } + catch (Exception ex) + { + return Ok(new ReturnTemplate(200, "删除失败!", "")); + } + } + +// 6. 获取子项 + [HttpGet("children")] + public async Task GetChildren( + [FromQuery, Required] string value, + [FromQuery] string? tag = null) + { + try + { + var result = await _dictService.GetChildrenAsync(value, tag); + return Ok(new ReturnTemplate(200, "查询成功啦!", result)); + } + catch (Exception ex) + { + return Ok(new ReturnTemplate(200, "查询失败!", "")); + } + } + } + +// 统一响应模型 + public class ApiResponse + { + public int Code { get; set; } + public string Message { get; set; } = null!; + } + + public class ApiResponse : ApiResponse + { + public T? Data { get; set; } + } +} \ No newline at end of file diff --git a/AGSS/DbSet/UserSet.cs b/AGSS/DbSet/UserSet.cs index a37519d..52ea038 100644 --- a/AGSS/DbSet/UserSet.cs +++ b/AGSS/DbSet/UserSet.cs @@ -4,27 +4,58 @@ using Microsoft.EntityFrameworkCore; namespace AGSS.DbSet { - public class ApplicationDbContext : IdentityDbContext + public class ApplicationDbContext : IdentityDbContext { - public override DbSet Users { get; set; } public override DbSet Roles { get; set; } - public DbSet Dictionaries { get; set; } + public DbSet DictItems { get; set; } public DbSet Menus { get; set; } public ApplicationDbContext(DbContextOptions options) : base(options) { } - - + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Uuid); + entity.Property(e => e.ParentId).HasColumnName("parentid"); + entity.Property(e => e.Tag).HasColumnName("tag"); + entity.Property(e => e.CreateTime).HasColumnName("createtime"); + entity.Property(e => e.ParentValue).HasColumnName("parentvalue"); + entity.Property(e => e.Value).HasColumnName("value"); + entity.Property(e => e.Label).HasColumnName("label"); + entity.Property(e => e.Tag).HasColumnName("tag"); + }); + +// 配置自引用关系 + modelBuilder.Entity() + .HasOne(d => d.Parent) + .WithMany(d => d.Children) + .HasForeignKey(d => d.ParentId) + .OnDelete(DeleteBehavior.Cascade); // 级联删除 + +// 添加索引 + modelBuilder.Entity() + .HasIndex(d => d.ParentId); + + modelBuilder.Entity() + .HasIndex(d => d.ParentValue); + + modelBuilder.Entity() + .HasIndex(d => d.Value); + + modelBuilder.Entity() + .HasIndex(d => d.Tag); + +// 确保顶级项的约束 + modelBuilder.Entity() + .HasCheckConstraint("CK_TopLevelItems", + "ParentId IS NULL OR (Tag IS NOT NULL AND ParentValue IS NOT NULL)"); - - modelBuilder.Entity() - .HasKey(d => d.Uuid); // 假设 Id 是 DictionaryModel 的主键字段 modelBuilder.Entity() .HasKey(m => m.Uuid); diff --git a/AGSS/Migrations/20250714035418_AddMenuTableV2.Designer.cs b/AGSS/Migrations/20250714035418_AddMenuTableV2.Designer.cs deleted file mode 100644 index 9db0985..0000000 --- a/AGSS/Migrations/20250714035418_AddMenuTableV2.Designer.cs +++ /dev/null @@ -1,425 +0,0 @@ -// -using System; -using AGSS.DbSet; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace AGSS.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20250714035418_AddMenuTableV2")] - partial class AddMenuTableV2 - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.6") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("AGSS.Models.Entities.DictionaryModel", b => - { - b.Property("Uuid") - .HasColumnType("text"); - - b.Property("CreateTime") - .HasColumnType("timestamp with time zone"); - - b.Property("CreateUserId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Label") - .IsRequired() - .HasColumnType("text"); - - b.Property("LabelEn") - .IsRequired() - .HasColumnType("text"); - - b.Property("ParentId") - .IsRequired() - .HasColumnType("text"); - - b.Property("ParentValue") - .IsRequired() - .HasColumnType("text"); - - b.Property("Remark") - .IsRequired() - .HasColumnType("text"); - - b.Property("Tag") - .IsRequired() - .HasColumnType("text"); - - b.Property("Value") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Uuid"); - - b.ToTable("Dictionaries"); - }); - - modelBuilder.Entity("AGSS.Models.Entities.MenuModel", b => - { - b.Property("Uuid") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("Adaptability") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Component") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("CreateTime") - .HasColumnType("timestamp with time zone"); - - b.Property("Icon") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("Label") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("MenuCode") - .HasMaxLength(500) - .HasColumnType("character varying(500)"); - - b.Property("ParentId") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("Path") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("Query") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Sort") - .HasColumnType("integer"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(1) - .HasColumnType("character varying(1)"); - - b.Property("UpdateTime") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Uuid"); - - b.HasIndex("ParentId"); - - b.ToTable("Menus"); - }); - - modelBuilder.Entity("AGSS.Models.Entities.RoleModel", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("AspNetRoles", (string)null); - }); - - modelBuilder.Entity("AGSS.Models.Entities.UserModel", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("AccessFailedCount") - .HasColumnType("integer"); - - b.Property("Birthday") - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Config") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("Description") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("JobCode") - .HasMaxLength(10) - .HasColumnType("character varying(10)"); - - b.Property("JobName") - .HasMaxLength(10) - .HasColumnType("character varying(10)"); - - b.Property("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("MenuCode") - .HasMaxLength(500) - .HasColumnType("character varying(500)"); - - b.Property("MenuName") - .HasMaxLength(500) - .HasColumnType("character varying(500)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("PhoneNumber") - .HasColumnType("text"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("Sex") - .HasColumnType("text"); - - b.Property("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.ToTable("AspNetUsers", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("text"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); - - modelBuilder.Entity("AGSS.Models.Entities.MenuModel", b => - { - b.HasOne("AGSS.Models.Entities.MenuModel", null) - .WithMany() - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Restrict); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("AGSS.Models.Entities.RoleModel", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("AGSS.Models.Entities.UserModel", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("AGSS.Models.Entities.UserModel", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("AGSS.Models.Entities.RoleModel", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("AGSS.Models.Entities.UserModel", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("AGSS.Models.Entities.UserModel", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/AGSS/Migrations/20250714035418_AddMenuTableV2.cs b/AGSS/Migrations/20250714035418_AddMenuTableV2.cs deleted file mode 100644 index 3b0d80c..0000000 --- a/AGSS/Migrations/20250714035418_AddMenuTableV2.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace AGSS.Migrations -{ - /// - public partial class AddMenuTableV2 : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - - } - } -} diff --git a/AGSS/Migrations/ApplicationDbContextModelSnapshot.cs b/AGSS/Migrations/ApplicationDbContextModelSnapshot.cs index 9cdf659..950a7ef 100644 --- a/AGSS/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/AGSS/Migrations/ApplicationDbContextModelSnapshot.cs @@ -22,51 +22,6 @@ namespace AGSS.Migrations NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("AGSS.Models.Entities.DictionaryModel", b => - { - b.Property("Uuid") - .HasColumnType("text"); - - b.Property("CreateTime") - .HasColumnType("timestamp with time zone"); - - b.Property("CreateUserId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Label") - .IsRequired() - .HasColumnType("text"); - - b.Property("LabelEn") - .IsRequired() - .HasColumnType("text"); - - b.Property("ParentId") - .IsRequired() - .HasColumnType("text"); - - b.Property("ParentValue") - .IsRequired() - .HasColumnType("text"); - - b.Property("Remark") - .IsRequired() - .HasColumnType("text"); - - b.Property("Tag") - .IsRequired() - .HasColumnType("text"); - - b.Property("Value") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Uuid"); - - b.ToTable("Dictionaries"); - }); - modelBuilder.Entity("AGSS.Models.Entities.MenuModel", b => { b.Property("Uuid") @@ -252,6 +207,68 @@ namespace AGSS.Migrations b.ToTable("AspNetUsers", (string)null); }); + modelBuilder.Entity("DictItem", b => + { + b.Property("Uuid") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("createtime"); + + b.Property("CreateUserId") + .HasColumnType("uuid"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("label"); + + b.Property("LabelEn") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ParentId") + .HasColumnType("uuid") + .HasColumnName("parentid"); + + b.Property("ParentValue") + .HasColumnType("text") + .HasColumnName("parentvalue"); + + b.Property("Remark") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Tag") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("tag"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("value"); + + b.HasKey("Uuid"); + + b.HasIndex("ParentId"); + + b.HasIndex("ParentValue"); + + b.HasIndex("Tag"); + + b.HasIndex("Value"); + + b.ToTable("DictItems", t => + { + t.HasCheckConstraint("CK_TopLevelItems", "ParentId IS NULL OR (Tag IS NOT NULL AND ParentValue IS NOT NULL)"); + }); + }); + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.Property("Id") @@ -366,6 +383,16 @@ namespace AGSS.Migrations .OnDelete(DeleteBehavior.Restrict); }); + modelBuilder.Entity("DictItem", b => + { + b.HasOne("DictItem", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Parent"); + }); + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.HasOne("AGSS.Models.Entities.RoleModel", null) @@ -416,6 +443,11 @@ namespace AGSS.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); + + modelBuilder.Entity("DictItem", b => + { + b.Navigation("Children"); + }); #pragma warning restore 612, 618 } } diff --git a/AGSS/Models/DTOs/DictionaryDto.cs b/AGSS/Models/DTOs/DictionaryDto.cs index 9558932..08e583f 100644 --- a/AGSS/Models/DTOs/DictionaryDto.cs +++ b/AGSS/Models/DTOs/DictionaryDto.cs @@ -9,7 +9,6 @@ namespace AGSS.Models.DTOs public string ParentId { get; set; } public string ParentValue { get; set; } public string Label { get; set; } - public string LabelEn { get; set; } public string Remark { get; set; } public string Value { get; set; } public string Tag { get; set; } diff --git a/AGSS/Models/Entities/DictionaryModel.cs b/AGSS/Models/Entities/DictionaryModel.cs index 77269e1..8871965 100644 --- a/AGSS/Models/Entities/DictionaryModel.cs +++ b/AGSS/Models/Entities/DictionaryModel.cs @@ -1,18 +1,37 @@ -using System; -using System.Collections.Generic; -namespace AGSS.Models.Entities +using System.ComponentModel.DataAnnotations; + +public class DictItem { - public class DictionaryModel - { - public string Uuid { get; set; } - public string ParentId { get; set; } - public string ParentValue { get; set; } - public string Label { get; set; } - public string LabelEn { get; set; } - public string Remark { get; set; } - public string Value { get; set; } - public string Tag { get; set; } - public DateTime CreateTime { get; set; } - public string CreateUserId { get; set; } - } -} + // 主键 + public Guid Uuid { get; set; } = Guid.NewGuid(); + + // 层级关系 + public Guid? ParentId { get; set; } + public string? ParentValue { get; set; } + + // 基础信息 + [Required] + [StringLength(100)] + public string Label { get; set; } = null!; // 中文标签 + + [StringLength(100)] + public string? LabelEn { get; set; } // 英文标签 + + [StringLength(500)] + public string? Remark { get; set; } // 备注 + + [Required] + [StringLength(50)] + public string Value { get; set; } = null!; // 字典值 + + [StringLength(500)] + public string? Tag { get; set; } // 逗号分隔的标签 + + // 审计字段 + public DateTime CreateTime { get; set; } = DateTime.UtcNow; + public Guid CreateUserId { get; set; } + + // 导航属性 + public DictItem? Parent { get; set; } + public ICollection Children { get; set; } = new List(); +} \ No newline at end of file diff --git a/AGSS/Program.cs b/AGSS/Program.cs index f177555..f5945ea 100644 --- a/AGSS/Program.cs +++ b/AGSS/Program.cs @@ -1,4 +1,5 @@ using System.Reflection; +using System.Security.Claims; using System.Text; using AGSS.DbSet; using AGSS.Models; @@ -56,6 +57,8 @@ builder.Services.AddIdentityCore(options => builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); @@ -82,6 +85,10 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) builder.Services.AddAuthorization(); +builder.Services.Configure(options => +{ + options.TokenValidationParameters.RoleClaimType = "role"; +}); builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle diff --git a/AGSS/Services/CurrentUserService.cs b/AGSS/Services/CurrentUserService.cs new file mode 100644 index 0000000..c5a0cba --- /dev/null +++ b/AGSS/Services/CurrentUserService.cs @@ -0,0 +1,25 @@ +// 用于获取当前用户信息 + +using System.Security.Claims; + +public interface ICurrentUserService +{ + Guid UserId { get; } + bool IsAdmin { get; } +} + +public class CurrentUserService : ICurrentUserService +{ + private readonly IHttpContextAccessor _httpContextAccessor; + + public CurrentUserService(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public Guid UserId => + Guid.Parse(_httpContextAccessor.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value); + + public bool IsAdmin => + _httpContextAccessor.HttpContext?.User?.IsInRole("luolan") ?? false; +} \ No newline at end of file diff --git a/AGSS/Services/DictService.cs b/AGSS/Services/DictService.cs new file mode 100644 index 0000000..147e112 --- /dev/null +++ b/AGSS/Services/DictService.cs @@ -0,0 +1,175 @@ +using AGSS.DbSet; +using Microsoft.EntityFrameworkCore; + +public class DictService +{ + private readonly ApplicationDbContext _context; + + public DictService(ApplicationDbContext context, ICurrentUserService currentUserService) + { + _context = context; + _currentUserService = currentUserService; + } + + private readonly ICurrentUserService _currentUserService; + + + // 1. 获取父级列表 + public async Task> GetParentsAsync(string? label) + { + var query = _context.DictItems + .Where(d => d.ParentId == null); + + if (!string.IsNullOrWhiteSpace(label)) + { + query = query.Where(d => d.Label.Contains(label)); + } + + return await query.ToListAsync(); + } + + // 2. 创建父级项 + public async Task CreateParentAsync(string label, string value) + { + if (!_currentUserService.IsAdmin) + throw new UnauthorizedAccessException("您不是管理员,无权限"); + + var newItem = new DictItem + { + Label = label, + Value = value, + CreateUserId = _currentUserService.UserId + }; + + _context.DictItems.Add(newItem); + await _context.SaveChangesAsync(); + return newItem; + } + + // 2.1 更新父级项 + public async Task UpdateParentAsync(Guid uuid, string label) + { + if (!_currentUserService.IsAdmin) + throw new UnauthorizedAccessException("您不是管理员,无权限"); + + var item = await _context.DictItems + .FirstOrDefaultAsync(d => d.Uuid == uuid && d.ParentId == null); + + if (item == null) throw new KeyNotFoundException("字典项不存在"); + + item.Label = label; + await _context.SaveChangesAsync(); + } + + // 3. 删除父级项(级联删除) + public async Task DeleteParentAsync(Guid uuid) + { + if (!_currentUserService.IsAdmin) + throw new UnauthorizedAccessException("您不是管理员,无权限"); + + var item = await _context.DictItems + .Include(d => d.Children) + .FirstOrDefaultAsync(d => d.Uuid == uuid && d.ParentId == null); + + if (item == null) throw new KeyNotFoundException("字典项不存在"); + + _context.DictItems.Remove(item); + await _context.SaveChangesAsync(); + } + + // 4. 创建子项 + public async Task CreateChildAsync( + Guid parentId, + string parentValue, + string label, + string value, + string? labelEn = null, + string? remark = null, + string? tag = null) + { + if (!_currentUserService.IsAdmin) + throw new UnauthorizedAccessException("您不是管理员,无权限"); + + var parent = await _context.DictItems + .FirstOrDefaultAsync(d => d.Uuid == parentId && d.ParentId == null); + + if (parent == null) throw new KeyNotFoundException("父级字典项不存在"); + + var newItem = new DictItem + { + ParentId = parentId, + ParentValue = parentValue, + Label = label, + Value = value, + LabelEn = labelEn, + Remark = remark, + Tag = tag, + CreateUserId = _currentUserService.UserId + }; + + _context.DictItems.Add(newItem); + await _context.SaveChangesAsync(); + return newItem; + } + + // 4.1 更新子项 + public async Task UpdateChildAsync( + Guid uuid, + string label, + string value, + string? labelEn = null, + string? remark = null, + string? tag = null) + { + if (!_currentUserService.IsAdmin) + throw new UnauthorizedAccessException("您不是管理员,无权限"); + + var item = await _context.DictItems + .FirstOrDefaultAsync(d => d.Uuid == uuid && d.ParentId != null); + + if (item == null) throw new KeyNotFoundException("字典项不存在"); + + item.Label = label; + item.Value = value; + item.LabelEn = labelEn; + item.Remark = remark; + item.Tag = tag; + + await _context.SaveChangesAsync(); + } + + // 5. 删除子项 + public async Task DeleteChildAsync(Guid uuid) + { + if (!_currentUserService.IsAdmin) + throw new UnauthorizedAccessException("您不是管理员,无权限"); + + var item = await _context.DictItems + .FirstOrDefaultAsync(d => d.Uuid == uuid && d.ParentId != null); + + if (item == null) throw new KeyNotFoundException("字典项不存在"); + + _context.DictItems.Remove(item); + await _context.SaveChangesAsync(); + } + + // 6. 获取子项(带标签过滤) + public async Task> GetChildrenAsync(string value, string? tag = null) + { + var query = _context.DictItems + .Where(d => d.ParentValue == value); + + if (!string.IsNullOrWhiteSpace(tag)) + { + // 精确标签匹配逻辑 + var tags = tag.Split(',', StringSplitOptions.RemoveEmptyEntries) + .Select(t => t.Trim()) + .ToList(); + + query = query.Where(d => d.Tag != null && + tags.All(t => d.Tag.Contains(t))); + } + + return await query.ToListAsync(); + } +} \ No newline at end of file diff --git a/script.sql b/script.sql index 73a8d5b..ef682b8 100644 --- a/script.sql +++ b/script.sql @@ -1,105 +1,32 @@ -CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" ( - "MigrationId" character varying(150) NOT NULL, - "ProductVersion" character varying(32) NOT NULL, - CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId") +START TRANSACTION; +DROP TABLE "Dictionaries"; + +CREATE TABLE "DictItems" ( + "Uuid" uuid NOT NULL, + parentid uuid, + parentvalue text, + label character varying(100) NOT NULL, + "LabelEn" character varying(100), + "Remark" character varying(500), + value character varying(50) NOT NULL, + tag character varying(500), + createtime timestamp with time zone NOT NULL, + "CreateUserId" uuid NOT NULL, + CONSTRAINT "PK_DictItems" PRIMARY KEY ("Uuid"), + CONSTRAINT "CK_TopLevelItems" CHECK (ParentId IS NULL OR (Tag IS NOT NULL AND ParentValue IS NOT NULL)), + CONSTRAINT "FK_DictItems_DictItems_parentid" FOREIGN KEY (parentid) REFERENCES "DictItems" ("Uuid") ON DELETE CASCADE ); -START TRANSACTION; -CREATE TABLE "AspNetRoles" ( - "Id" text NOT NULL, - "Name" character varying(256), - "NormalizedName" character varying(256), - "ConcurrencyStamp" text, - CONSTRAINT "PK_AspNetRoles" PRIMARY KEY ("Id") -); +CREATE INDEX "IX_DictItems_parentid" ON "DictItems" (parentid); -CREATE TABLE "AspNetUsers" ( - "Id" text NOT NULL, - "Sex" text, - "Description" character varying(100), - "Config" character varying(200), - "JobCode" character varying(10), - "JobName" character varying(10), - "Birthday" character varying(20), - "MenuCode" character varying(500), - "MenuName" character varying(500), - "UserName" character varying(256), - "NormalizedUserName" character varying(256), - "Email" character varying(256), - "NormalizedEmail" character varying(256), - "EmailConfirmed" boolean NOT NULL, - "PasswordHash" text, - "SecurityStamp" text, - "ConcurrencyStamp" text, - "PhoneNumber" text, - "PhoneNumberConfirmed" boolean NOT NULL, - "TwoFactorEnabled" boolean NOT NULL, - "LockoutEnd" timestamp with time zone, - "LockoutEnabled" boolean NOT NULL, - "AccessFailedCount" integer NOT NULL, - CONSTRAINT "PK_AspNetUsers" PRIMARY KEY ("Id") -); +CREATE INDEX "IX_DictItems_parentvalue" ON "DictItems" (parentvalue); -CREATE TABLE "AspNetRoleClaims" ( - "Id" integer GENERATED BY DEFAULT AS IDENTITY, - "RoleId" text NOT NULL, - "ClaimType" text, - "ClaimValue" text, - CONSTRAINT "PK_AspNetRoleClaims" PRIMARY KEY ("Id"), - CONSTRAINT "FK_AspNetRoleClaims_AspNetRoles_RoleId" FOREIGN KEY ("RoleId") REFERENCES "AspNetRoles" ("Id") ON DELETE CASCADE -); +CREATE INDEX "IX_DictItems_tag" ON "DictItems" (tag); -CREATE TABLE "AspNetUserClaims" ( - "Id" integer GENERATED BY DEFAULT AS IDENTITY, - "UserId" text NOT NULL, - "ClaimType" text, - "ClaimValue" text, - CONSTRAINT "PK_AspNetUserClaims" PRIMARY KEY ("Id"), - CONSTRAINT "FK_AspNetUserClaims_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE -); - -CREATE TABLE "AspNetUserLogins" ( - "LoginProvider" text NOT NULL, - "ProviderKey" text NOT NULL, - "ProviderDisplayName" text, - "UserId" text NOT NULL, - CONSTRAINT "PK_AspNetUserLogins" PRIMARY KEY ("LoginProvider", "ProviderKey"), - CONSTRAINT "FK_AspNetUserLogins_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE -); - -CREATE TABLE "AspNetUserRoles" ( - "UserId" text NOT NULL, - "RoleId" text NOT NULL, - CONSTRAINT "PK_AspNetUserRoles" PRIMARY KEY ("UserId", "RoleId"), - CONSTRAINT "FK_AspNetUserRoles_AspNetRoles_RoleId" FOREIGN KEY ("RoleId") REFERENCES "AspNetRoles" ("Id") ON DELETE CASCADE, - CONSTRAINT "FK_AspNetUserRoles_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE -); - -CREATE TABLE "AspNetUserTokens" ( - "UserId" text NOT NULL, - "LoginProvider" text NOT NULL, - "Name" text NOT NULL, - "Value" text, - CONSTRAINT "PK_AspNetUserTokens" PRIMARY KEY ("UserId", "LoginProvider", "Name"), - CONSTRAINT "FK_AspNetUserTokens_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE -); - -CREATE INDEX "IX_AspNetRoleClaims_RoleId" ON "AspNetRoleClaims" ("RoleId"); - -CREATE UNIQUE INDEX "RoleNameIndex" ON "AspNetRoles" ("NormalizedName"); - -CREATE INDEX "IX_AspNetUserClaims_UserId" ON "AspNetUserClaims" ("UserId"); - -CREATE INDEX "IX_AspNetUserLogins_UserId" ON "AspNetUserLogins" ("UserId"); - -CREATE INDEX "IX_AspNetUserRoles_RoleId" ON "AspNetUserRoles" ("RoleId"); - -CREATE INDEX "EmailIndex" ON "AspNetUsers" ("NormalizedEmail"); - -CREATE UNIQUE INDEX "UserNameIndex" ON "AspNetUsers" ("NormalizedUserName"); +CREATE INDEX "IX_DictItems_value" ON "DictItems" (value); INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") -VALUES ('20250709144855_Initial', '9.0.6'); +VALUES ('20250715135627_dic1', '9.0.6'); COMMIT;