diff --git a/src/NetCorePal.D3Shop.Admin.Shared/Dtos/Identity/CreateDepartmentUserInfoDto.cs b/src/NetCorePal.D3Shop.Admin.Shared/Dtos/Identity/CreateDepartmentUserInfoDto.cs
new file mode 100644
index 0000000..a7481c5
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Admin.Shared/Dtos/Identity/CreateDepartmentUserInfoDto.cs
@@ -0,0 +1,17 @@
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NetCorePal.D3Shop.Admin.Shared.Dtos.Identity
+{
+
+ ///
+ /// 创建部门用户信息
+ ///
+ ///
+ ///
+ public record CreateDepartmentUserInfoDto(AdminUserId UserId, string UserName);
+}
diff --git a/src/NetCorePal.D3Shop.Admin.Shared/Dtos/Identity/UpdateDepartmentUserInfoDto.cs b/src/NetCorePal.D3Shop.Admin.Shared/Dtos/Identity/UpdateDepartmentUserInfoDto.cs
new file mode 100644
index 0000000..7d9f839
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Admin.Shared/Dtos/Identity/UpdateDepartmentUserInfoDto.cs
@@ -0,0 +1,17 @@
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NetCorePal.D3Shop.Admin.Shared.Dtos.Identity
+{
+
+ ///
+ /// 更新部门用户信息
+ ///
+ ///
+ ///
+ public record UpdateDepartmentUserInfoDto(AdminUserId UserId, string UserName);
+}
diff --git a/src/NetCorePal.D3Shop.Admin.Shared/NetCorePal.D3Shop.Admin.Shared.csproj b/src/NetCorePal.D3Shop.Admin.Shared/NetCorePal.D3Shop.Admin.Shared.csproj
index 335f1a5..d9b64e0 100644
--- a/src/NetCorePal.D3Shop.Admin.Shared/NetCorePal.D3Shop.Admin.Shared.csproj
+++ b/src/NetCorePal.D3Shop.Admin.Shared/NetCorePal.D3Shop.Admin.Shared.csproj
@@ -7,13 +7,13 @@
-
-
-
+
+
+
-
+
diff --git a/src/NetCorePal.D3Shop.Admin.Shared/Permission/PermissionCodes.cs b/src/NetCorePal.D3Shop.Admin.Shared/Permission/PermissionCodes.cs
index a48ff55..65e96bd 100644
--- a/src/NetCorePal.D3Shop.Admin.Shared/Permission/PermissionCodes.cs
+++ b/src/NetCorePal.D3Shop.Admin.Shared/Permission/PermissionCodes.cs
@@ -25,4 +25,13 @@ public static class PermissionCodes
public const string RoleView = nameof(RoleView);
#endregion
+
+ #region DepartmentManagement
+ public const string DepartmentManagement = nameof(DepartmentManagement);
+ public const string DepartmentCreate = nameof(DepartmentCreate);
+ public const string DepartmentEdit = nameof(DepartmentEdit);
+ public const string DepartmentView = nameof(DepartmentView);
+ public const string DepartmentDelete = nameof(DepartmentDelete);
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Admin.Shared/Requests/CreateDepartmentRequest.cs b/src/NetCorePal.D3Shop.Admin.Shared/Requests/CreateDepartmentRequest.cs
new file mode 100644
index 0000000..ecfd7b7
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Admin.Shared/Requests/CreateDepartmentRequest.cs
@@ -0,0 +1,17 @@
+using NetCorePal.D3Shop.Admin.Shared.Dtos.Identity;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
+using System.ComponentModel.DataAnnotations;
+
+namespace NetCorePal.D3Shop.Admin.Shared.Requests;
+
+public class CreateDepartmentRequest
+{
+ [Required] public string Name { get; set; } = string.Empty;
+ public string Description { get; set; } = string.Empty;
+ public DeptId ParentId { get; set; } = new DeptId(0);
+
+ public IEnumerable Users { get; set; } = [];
+}
+
diff --git a/src/NetCorePal.D3Shop.Admin.Shared/Requests/DepartmentQueryRequest.cs b/src/NetCorePal.D3Shop.Admin.Shared/Requests/DepartmentQueryRequest.cs
new file mode 100644
index 0000000..8c1d0c0
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Admin.Shared/Requests/DepartmentQueryRequest.cs
@@ -0,0 +1,8 @@
+using NetCorePal.Extensions.Dto;
+
+namespace NetCorePal.D3Shop.Admin.Shared.Requests;
+
+public class DepartmentQueryRequest : PageRequest
+{
+ public string? Name { get; set; }
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Admin.Shared/Requests/UpdateDepartmentInfoRequest.cs b/src/NetCorePal.D3Shop.Admin.Shared/Requests/UpdateDepartmentInfoRequest.cs
new file mode 100644
index 0000000..2d9ca1e
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Admin.Shared/Requests/UpdateDepartmentInfoRequest.cs
@@ -0,0 +1,12 @@
+using NetCorePal.D3Shop.Admin.Shared.Dtos.Identity;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using System.ComponentModel.DataAnnotations;
+
+namespace NetCorePal.D3Shop.Admin.Shared.Requests;
+
+public class UpdateDepartmentInfoRequest
+{
+ [Required] public string Name { get; set; } = string.Empty;
+ public string Description { get; set; } = string.Empty;
+ public IEnumerable Users { get; set; } = [];
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Admin.Shared/Responses/DepartmentResponse.cs b/src/NetCorePal.D3Shop.Admin.Shared/Responses/DepartmentResponse.cs
new file mode 100644
index 0000000..65599dc
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Admin.Shared/Responses/DepartmentResponse.cs
@@ -0,0 +1,11 @@
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+
+namespace NetCorePal.D3Shop.Admin.Shared.Responses;
+
+public class DepartmentResponse(DeptId id, string name, string description)
+{
+ public DeptId Id { get; } = id;
+ public string Name { get; set; } = name;
+ public string Description { get; set; } = description;
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Domain/AggregatesModel/Identity/AdminUserAggregate/AdminUser.cs b/src/NetCorePal.D3Shop.Domain/AggregatesModel/Identity/AdminUserAggregate/AdminUser.cs
index 7887420..e3aaeb3 100644
--- a/src/NetCorePal.D3Shop.Domain/AggregatesModel/Identity/AdminUserAggregate/AdminUser.cs
+++ b/src/NetCorePal.D3Shop.Domain/AggregatesModel/Identity/AdminUserAggregate/AdminUser.cs
@@ -1,4 +1,5 @@
-using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
using NetCorePal.Extensions.Domain;
using NetCorePal.Extensions.Primitives;
@@ -19,6 +20,11 @@ protected AdminUser()
public string Password { get; private set; } = string.Empty;
public DateTime CreatedAt { get; init; }
public virtual ICollection Roles { get; } = [];
+
+ public virtual ICollection UserDepts { get; } = [];
+
+
+
public virtual ICollection Permissions { get; } = [];
public bool IsDeleted { get; private set; }
public DateTime? DeletedAt { get; private set; }
@@ -47,6 +53,12 @@ public void UpdateRoleInfo(RoleId roleId, string roleName)
savedRole?.UpdateRoleInfo(roleName);
}
+ public void SetUserDepts(DeptId deptId, string deptName)
+ {
+ var savedDept = UserDepts.FirstOrDefault(r => r.DeptId == deptId);
+ savedDept?.UpdateDeptInfo(deptName);
+ }
+
public void UpdateRoles(IEnumerable rolesToBeAssigned,
IEnumerable permissions)
{
diff --git a/src/NetCorePal.D3Shop.Domain/AggregatesModel/Identity/AdminUserAggregate/UserDept.cs b/src/NetCorePal.D3Shop.Domain/AggregatesModel/Identity/AdminUserAggregate/UserDept.cs
new file mode 100644
index 0000000..0dc315d
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Domain/AggregatesModel/Identity/AdminUserAggregate/UserDept.cs
@@ -0,0 +1,30 @@
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate
+{
+ public class UserDept
+ {
+ protected UserDept() { }
+
+ public AdminUserId AdminUserId { get; private set; } = default!;
+ public DeptId DeptId { get; private set; } = default!;
+ public string DeptName { get; private set; } = string.Empty;
+
+ public UserDept(DeptId deptId, string deptName)
+ {
+ DeptId = deptId;
+ DeptName = deptName;
+ }
+
+ public void UpdateDeptInfo(string deptName)
+ {
+ DeptName = deptName;
+ }
+ }
+}
diff --git a/src/NetCorePal.D3Shop.Domain/AggregatesModel/Identity/DepartmentAggregate/Department.cs b/src/NetCorePal.D3Shop.Domain/AggregatesModel/Identity/DepartmentAggregate/Department.cs
new file mode 100644
index 0000000..71d8410
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Domain/AggregatesModel/Identity/DepartmentAggregate/Department.cs
@@ -0,0 +1,108 @@
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using NetCorePal.D3Shop.Domain.DomainEvents.Identity;
+using NetCorePal.Extensions.Domain;
+using NetCorePal.Extensions.Primitives;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate
+{
+ public partial record DeptId : IInt64StronglyTypedId;
+
+ ///
+ /// 部门
+ ///
+ public class Department : Entity, IAggregateRoot
+ {
+
+ ///
+ /// 部门名称
+ ///
+
+ public string Name { get; private set; } = string.Empty;
+
+ ///
+ /// 描述
+ ///
+ public string Description { get; private set; } = string.Empty;
+
+ ///
+ /// 父部门id
+ ///
+
+ public DeptId ParentId { get; private set; } = new DeptId(0);
+
+ public DateTime CreatedAt { get; init; }
+
+ public bool IsDeleted { get; private set; }
+ public DateTime? DeletedAt { get; private set; }
+
+ public virtual ICollection Users { get; } = [];
+
+
+ protected Department()
+ {
+
+ }
+
+ public Department(string name, string description, DeptId parentId, IEnumerable deptUsers)
+ {
+ Name = name;
+ Description = description;
+ ParentId = parentId;
+ CreatedAt = DateTime.Now;
+ foreach (var user in deptUsers)
+ {
+ Users.Add(user);
+ }
+ }
+
+ ///
+ /// 修改部门信息
+ ///
+ ///
+ ///
+ ///
+ public void UpdateDepartInfo(string name, string description, IEnumerable deptUsers)
+ {
+ Name = name;
+ Description = description;
+
+ var currentUserMap = Users.ToDictionary(r => r.UserId);
+ var targetUserMap = deptUsers.ToDictionary(r => r.UserId);
+
+ var userIdsToRemove = currentUserMap.Keys.Except(targetUserMap.Keys);
+ foreach (var userId in userIdsToRemove)
+ {
+ Users.Remove(currentUserMap[userId]);
+ }
+
+ var userIdsToAdd = targetUserMap.Keys.Except(currentUserMap.Keys);
+ foreach (var userId in userIdsToAdd)
+ {
+ var targetUser = targetUserMap[userId];
+ Users.Add(targetUser);
+ }
+
+ AddDomainEvent(new DepartmentInfoChangedDomainEvent(this));
+ }
+
+
+ public void UpdateDepartmentUserName(AdminUserId userId, string userName)
+ {
+ var savedUser = Users.FirstOrDefault(r => r.UserId == userId);
+ savedUser?.UpdateUserInfo(userName);
+ }
+
+
+ public void Delete()
+ {
+ if (IsDeleted) throw new KnownException("部门已经被删除!");
+ IsDeleted = true;
+ DeletedAt = DateTime.Now;
+ }
+ }
+}
diff --git a/src/NetCorePal.D3Shop.Domain/AggregatesModel/Identity/DepartmentAggregate/DepartmentUser.cs b/src/NetCorePal.D3Shop.Domain/AggregatesModel/Identity/DepartmentAggregate/DepartmentUser.cs
new file mode 100644
index 0000000..3019c27
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Domain/AggregatesModel/Identity/DepartmentAggregate/DepartmentUser.cs
@@ -0,0 +1,47 @@
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate
+{
+
+ ///
+ /// 部门用户
+ ///
+ public class DepartmentUser
+ {
+ protected DepartmentUser()
+ {
+
+ }
+
+ ///
+ /// 部门id
+ ///
+ public DeptId DeptId { get; set; } = default!;
+
+ ///
+ /// 用户名称
+ ///
+ public string UserName { get; private set; } = string.Empty;
+
+ ///
+ /// 部门id
+ ///
+ public AdminUserId UserId { get; set; } = default!;
+
+ public DepartmentUser(string userName, AdminUserId userId)
+ {
+ UserName = userName;
+ UserId = userId;
+ }
+
+ public void UpdateUserInfo(string userName)
+ {
+ UserName = userName;
+ }
+ }
+}
diff --git a/src/NetCorePal.D3Shop.Domain/DomainEvents/Identity/DepartmentInfoChangedDomainEvent.cs b/src/NetCorePal.D3Shop.Domain/DomainEvents/Identity/DepartmentInfoChangedDomainEvent.cs
new file mode 100644
index 0000000..cde1d6d
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Domain/DomainEvents/Identity/DepartmentInfoChangedDomainEvent.cs
@@ -0,0 +1,7 @@
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
+using NetCorePal.Extensions.Domain;
+
+namespace NetCorePal.D3Shop.Domain.DomainEvents.Identity;
+
+public record DepartmentInfoChangedDomainEvent(Department Department) : IDomainEvent;
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Infrastructure/ApplicationDbContext.cs b/src/NetCorePal.D3Shop.Infrastructure/ApplicationDbContext.cs
index 3d6ffb8..c221a6e 100644
--- a/src/NetCorePal.D3Shop.Infrastructure/ApplicationDbContext.cs
+++ b/src/NetCorePal.D3Shop.Infrastructure/ApplicationDbContext.cs
@@ -6,6 +6,7 @@
using NetCorePal.D3Shop.Domain.AggregatesModel.DeliverAggregate;
using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
namespace NetCorePal.D3Shop.Infrastructure
{
@@ -37,6 +38,8 @@ protected override void ConfigureConventions(ModelConfigurationBuilder configura
#region Identity
public DbSet AdminUsers => Set();
public DbSet Roles => Set();
+
+ public DbSet Departments => Set();
#endregion
}
}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Infrastructure/EntityConfigurations/Identity/AdminUserConfiguration.cs b/src/NetCorePal.D3Shop.Infrastructure/EntityConfigurations/Identity/AdminUserConfiguration.cs
index 2bf434e..85b167c 100644
--- a/src/NetCorePal.D3Shop.Infrastructure/EntityConfigurations/Identity/AdminUserConfiguration.cs
+++ b/src/NetCorePal.D3Shop.Infrastructure/EntityConfigurations/Identity/AdminUserConfiguration.cs
@@ -28,6 +28,13 @@ public void Configure(EntityTypeBuilder builder)
.OnDelete(DeleteBehavior.ClientCascade);
builder.Navigation(au => au.Permissions).AutoInclude();
+ //配置 AdminUser 与 UserDept 的一对多关系
+ builder.HasMany(au => au.UserDepts)
+ .WithOne()
+ .HasForeignKey(aup => aup.AdminUserId)
+ .OnDelete(DeleteBehavior.ClientCascade);
+ builder.Navigation(au => au.UserDepts).AutoInclude();
+
builder.HasQueryFilter(au => !au.IsDeleted);
}
}
@@ -41,6 +48,17 @@ public void Configure(EntityTypeBuilder builder)
}
}
+ internal class UserDeptConfiguration : IEntityTypeConfiguration
+ {
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.ToTable("userDepts");
+ builder.HasKey(aur => new { aur.AdminUserId, aur.DeptId });
+
+
+ }
+ }
+
internal class AdminUserPermissionConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
diff --git a/src/NetCorePal.D3Shop.Infrastructure/EntityConfigurations/Identity/DepartmentConfiguration.cs b/src/NetCorePal.D3Shop.Infrastructure/EntityConfigurations/Identity/DepartmentConfiguration.cs
new file mode 100644
index 0000000..d27aec2
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Infrastructure/EntityConfigurations/Identity/DepartmentConfiguration.cs
@@ -0,0 +1,40 @@
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Microsoft.EntityFrameworkCore;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+
+namespace NetCorePal.D3Shop.Infrastructure.EntityConfigurations.Identity
+{
+
+ internal class DepartmentConfiguration : IEntityTypeConfiguration
+ {
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.ToTable("departments");
+ builder.HasKey(r => r.Id);
+ builder.Property(r => r.Id).ValueGeneratedOnAdd().UseSnowFlakeValueGenerator();
+
+ //配置 Department 与 DepartmentUser 的一对多关系
+ builder.HasMany(au => au.Users)
+ .WithOne()
+ .HasForeignKey(aup => aup.DeptId)
+ .OnDelete(DeleteBehavior.ClientCascade);
+ builder.Navigation(au => au.Users).AutoInclude();
+ }
+
+ internal class DepartmentUserConfiguration : IEntityTypeConfiguration
+ {
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.ToTable("departmentUser");
+ builder.HasKey(aur => new { aur.UserId, aur.DeptId });
+ }
+ }
+ }
+}
diff --git a/src/NetCorePal.D3Shop.Infrastructure/Repositories/Identity/IDepartmentRepository.cs b/src/NetCorePal.D3Shop.Infrastructure/Repositories/Identity/IDepartmentRepository.cs
new file mode 100644
index 0000000..d4f3dcf
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Infrastructure/Repositories/Identity/IDepartmentRepository.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.Extensions.Repository;
+using NetCorePal.Extensions.Repository.EntityFrameworkCore;
+
+namespace NetCorePal.D3Shop.Infrastructure.Repositories.Identity
+{
+ public interface IDepartmentRepository : IRepository;
+
+ public class DepartmentRepository(ApplicationDbContext context) : RepositoryBase(context), IDepartmentRepository
+ {
+ }
+}
diff --git a/src/NetCorePal.D3Shop.Web.Admin.Client/Components/Identity/Dept/AddDept.razor b/src/NetCorePal.D3Shop.Web.Admin.Client/Components/Identity/Dept/AddDept.razor
new file mode 100644
index 0000000..836b211
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web.Admin.Client/Components/Identity/Dept/AddDept.razor
@@ -0,0 +1,30 @@
+@using NetCorePal.D3Shop.Web.Admin.Client.Components.Identity
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/NetCorePal.D3Shop.Web.Admin.Client/Components/Identity/Dept/AddDept.razor.cs b/src/NetCorePal.D3Shop.Web.Admin.Client/Components/Identity/Dept/AddDept.razor.cs
new file mode 100644
index 0000000..1ae578d
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web.Admin.Client/Components/Identity/Dept/AddDept.razor.cs
@@ -0,0 +1,61 @@
+using Microsoft.AspNetCore.Components.Forms;
+
+namespace NetCorePal.D3Shop.Web.Admin.Client.Components.Identity.Dept;
+
+public partial class AddDept
+{
+ [Inject] private IDepartmentService DepartmentService { get; set; } = default!;
+ [Inject] private MessageService Message { get; set; } = default!;
+
+ [Parameter] public EventCallback OnItemAdded { get; set; }
+
+ private Form _form = default!;
+
+ private Tabs _tabs = default!;
+
+ // private List _assignedPermissionCodes = [];
+
+ private CreateDepartmentRequest _newRoleModel = new();
+
+ private bool _modalVisible;
+ private bool _modalConfirmLoading;
+
+ private void ShowModal()
+ {
+ _modalVisible = true;
+ }
+
+
+ private void CloseModal()
+ {
+ _modalVisible = false;
+ _newRoleModel = new CreateDepartmentRequest();
+ // _assignedPermissionCodes.Clear();
+ _tabs.GoTo(0);
+ }
+
+ private async Task Form_OnFinish(EditContext editContext)
+ {
+ _modalConfirmLoading = true;
+ StateHasChanged();
+ // _newRoleModel.PermissionCodes = _assignedPermissionCodes;
+ var response = await DepartmentService.CreateDepartment(_newRoleModel);
+ if (response.Success)
+ {
+ _ = Message.Success("创建成功!");
+ CloseModal();
+ await OnItemAdded.InvokeAsync();
+ }
+ else
+ {
+ _ = Message.Error(response.Message);
+ }
+
+ _modalConfirmLoading = false;
+ }
+
+ private void Form_OnFinishFailed(EditContext editContext)
+ {
+ _tabs.GoTo(0);
+ }
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Web.Admin.Client/Components/Identity/Dept/EditDeptInfo.razor b/src/NetCorePal.D3Shop.Web.Admin.Client/Components/Identity/Dept/EditDeptInfo.razor
new file mode 100644
index 0000000..ed682ab
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web.Admin.Client/Components/Identity/Dept/EditDeptInfo.razor
@@ -0,0 +1,60 @@
+编辑
+
+
+
+
+
+@code {
+ [CascadingParameter] public DepartmentResponse Row { get; set; } = default!;
+ [Inject] private IDepartmentService DepartmentService { get; set; } = default!;
+ [Inject] private MessageService Message { get; set; } = default!;
+ [Parameter] public EventCallback OnRowUpdated { get; set; }
+
+ private bool _modalVisible;
+ private bool _modalConfirmLoading;
+ private Form _form = default!;
+ private UpdateDepartmentInfoRequest _deptInfoModel = new();
+
+ private void ShowModal()
+ {
+ _modalVisible = true;
+ _deptInfoModel.Name = Row.Name;
+ _deptInfoModel.Description = Row.Description;
+ }
+
+ private async Task Form_OnFinish(EditContext editContext)
+ {
+ _modalConfirmLoading = true;
+ StateHasChanged();
+ var response = await DepartmentService.UpdateDepartmentInfo(Row.Id, _deptInfoModel);
+ _modalConfirmLoading = false;
+ if (response.Success)
+ {
+ _modalVisible = false;
+ _ = Message.Success("更新成功!");
+ Row.Name = _deptInfoModel.Name;
+ Row.Description = _deptInfoModel.Description;
+ await OnRowUpdated.InvokeAsync();
+ }
+ else
+ {
+ _ = Message.Error(response.Message);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Web.Admin.Client/Layouts/BasicLayout.razor.cs b/src/NetCorePal.D3Shop.Web.Admin.Client/Layouts/BasicLayout.razor.cs
index a553373..8540ed4 100644
--- a/src/NetCorePal.D3Shop.Web.Admin.Client/Layouts/BasicLayout.razor.cs
+++ b/src/NetCorePal.D3Shop.Web.Admin.Client/Layouts/BasicLayout.razor.cs
@@ -43,6 +43,14 @@ protected override async Task OnInitializedAsync()
Key = "roles",
Icon = "crown",
BoundPermissionCode = PermissionCodes.RoleManagement
+ },
+ new PermissionMenuDataItem
+ {
+ Path = "/admin/dept",
+ Name = "部门管理",
+ Key = "depts",
+ Icon = "crown",
+ BoundPermissionCode = PermissionCodes.DepartmentManagement
}
];
diff --git a/src/NetCorePal.D3Shop.Web.Admin.Client/Pages/Dept.razor b/src/NetCorePal.D3Shop.Web.Admin.Client/Pages/Dept.razor
new file mode 100644
index 0000000..1d20ed7
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web.Admin.Client/Pages/Dept.razor
@@ -0,0 +1,64 @@
+@page "/admin/dept"
+@using NetCorePal.D3Shop.Web.Admin.Client.Extensions
+@using NetCorePal.D3Shop.Web.Admin.Client.Components.Identity.Dept
+
+
+@attribute [ClientPermission(PermissionCodes.AdminUserManagement)]
+
+
+
+
+
+
+
+
+ 部门列表
+
+
+
+
+
+
+ @if (context.CheckPermission(PermissionCodes.DepartmentCreate))
+ {
+
+ }
+
+
+
+
+
+
+
+
+
+ @if (context.CheckPermission(PermissionCodes.DepartmentEdit))
+ {
+
+
+
+ }
+
+
+
+ @if (context.CheckPermission(PermissionCodes.DepartmentDelete))
+ {
+ Delete(row)">删除
+ }
+
+
+
+
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Web.Admin.Client/Pages/Dept.razor.cs b/src/NetCorePal.D3Shop.Web.Admin.Client/Pages/Dept.razor.cs
new file mode 100644
index 0000000..ff2cf4e
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web.Admin.Client/Pages/Dept.razor.cs
@@ -0,0 +1,79 @@
+using AntDesign.TableModels;
+
+namespace NetCorePal.D3Shop.Web.Admin.Client.Pages;
+
+public sealed partial class Dept
+{
+ [Inject] private IDepartmentService DepartmentService { get; set; } = default!;
+ [Inject] private MessageService Message { get; set; } = default!;
+ [Inject] private ConfirmService ConfirmService { get; set; } = default!;
+
+ private PagedData _pagedDepartments = new(default!, default, default, default);
+
+ private Table _table = default!;
+
+ private readonly DepartmentQueryRequest _departmentQueryRequest = new() { CountTotal = true };
+
+ private bool _loading;
+
+ protected override void OnAfterRender(bool firstRender)
+ {
+ if (!firstRender) return;
+ _table.ReloadData(1, 10);
+ }
+
+ private async Task GetPagedDepartments()
+ {
+ var response = await DepartmentService.GetAllDepartments(_departmentQueryRequest);
+ if (response.Success)
+ {
+ _pagedDepartments = response.Data;
+ _departmentQueryRequest.PageIndex = response.Data.PageIndex;
+ _departmentQueryRequest.PageSize = response.Data.PageSize;
+ }
+ else _ = Message.Error(response.Message);
+ }
+
+ private async Task HandleItemAdded()
+ {
+ await GetPagedDepartments();
+ }
+
+ private void HandleItemUpdated()
+ {
+ _table.ReloadData();
+ }
+ private async Task Delete(DepartmentResponse row)
+ {
+ if (!await Confirm($"确认删除部门:{row.Name}?"))
+ return;
+ var response = await DepartmentService.DeleteDepartment(row.Id);
+ if (response.Success)
+ {
+ _ = Message.Success("删除成功!");
+ await GetPagedDepartments();
+ }
+ else
+ {
+ _ = Message.Error(response.Message);
+ }
+ }
+
+ private async Task Confirm(string message)
+ {
+ return await ConfirmService.Show(message, "警告", ConfirmButtons.YesNo, ConfirmIcon.Warning) == ConfirmResult.Yes;
+ }
+
+ private async Task OnSearch()
+ {
+ _departmentQueryRequest.PageIndex = 1;
+ await GetPagedDepartments();
+ }
+
+ private async Task Table_OnChange(QueryModel obj)
+ {
+ _loading = true;
+ await GetPagedDepartments();
+ _loading = false;
+ }
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Web.Admin.Client/Services/IDepartmentService.cs b/src/NetCorePal.D3Shop.Web.Admin.Client/Services/IDepartmentService.cs
new file mode 100644
index 0000000..d1861ed
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web.Admin.Client/Services/IDepartmentService.cs
@@ -0,0 +1,23 @@
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
+using NetCorePal.D3Shop.Web.Admin.Client.Attributes;
+
+namespace NetCorePal.D3Shop.Web.Admin.Client.Services;
+
+[RefitService]
+public interface IDepartmentService
+{
+ [Post("/api/Department/CreateDepartment")]
+ Task> CreateDepartment([Body] CreateDepartmentRequest request);
+
+ [Get("/api/Department/GetAllDepartments")]
+ Task>> GetAllDepartments([Query] DepartmentQueryRequest request);
+
+ [Put("/api/Department/UpdateDepartmentInfo/{id}")]
+ Task UpdateDepartmentInfo(DeptId id, [Body] UpdateDepartmentInfoRequest request);
+
+ [Delete("/api/Department/DeleteDepartment/{id}")]
+ Task DeleteDepartment(DeptId id);
+
+
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Web/Application/Commands/Identity/CreateDepartmentCommand.cs b/src/NetCorePal.D3Shop.Web/Application/Commands/Identity/CreateDepartmentCommand.cs
new file mode 100644
index 0000000..cd316dd
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web/Application/Commands/Identity/CreateDepartmentCommand.cs
@@ -0,0 +1,45 @@
+using FluentValidation;
+using NetCorePal.D3Shop.Admin.Shared.Dtos.Identity;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Infrastructure.Repositories.Identity;
+using NetCorePal.D3Shop.Web.Admin.Client.Pages;
+using NetCorePal.D3Shop.Web.Application.Commands.Identity.Dto;
+using NetCorePal.D3Shop.Web.Application.Queries.Identity;
+using NetCorePal.Extensions.Primitives;
+
+namespace NetCorePal.D3Shop.Web.Application.Commands.Identity;
+
+public record CreateDepartmentCommand(
+ string Name,
+ string Description,
+ IEnumerable Users,
+ DeptId ParentId)
+ : ICommand;
+
+public class CreateDepartmentCommandValidator : AbstractValidator
+{
+ public CreateDepartmentCommandValidator(DepartmentQuery departmentQuery)
+ {
+ RuleFor(u => u.Name).NotEmpty().WithMessage("部门名称不能为空");
+ //RuleFor(u => u.Description).NotEmpty().WithMessage("不能为空");
+ RuleFor(u => u.Name).MustAsync(async (n, ct) => !await departmentQuery.DoesDepartmentExist(n, ct))
+ .WithMessage(u => $"该部门已存在,Name={u.Name}");
+ }
+}
+
+public class CreateDepartmentCommandHandler(IDepartmentRepository departmentRepository)
+ : ICommandHandler
+{
+ public async Task Handle(CreateDepartmentCommand request, CancellationToken cancellationToken)
+ {
+ List departmentUsers = [];
+ foreach (var user in request.Users)
+ {
+ departmentUsers.Add(new DepartmentUser(user.UserName, user.UserId));
+ }
+ var department = new Department(request.Name, request.Description, request.ParentId, departmentUsers);
+ await departmentRepository.AddAsync(department, cancellationToken);
+ return department.Id;
+ }
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Web/Application/Commands/Identity/DeleteDepartmentCommand.cs b/src/NetCorePal.D3Shop.Web/Application/Commands/Identity/DeleteDepartmentCommand.cs
new file mode 100644
index 0000000..c2a92c3
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web/Application/Commands/Identity/DeleteDepartmentCommand.cs
@@ -0,0 +1,21 @@
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Infrastructure.Repositories.Identity;
+using NetCorePal.Extensions.Primitives;
+
+namespace NetCorePal.D3Shop.Web.Application.Commands.Identity;
+
+public record DeleteDepartmentCommand(DeptId DeptId) : ICommand;
+
+public class DeleteDepartmentCommandHandler(IDepartmentRepository departmentRepository)
+ : ICommandHandler
+{
+ public async Task Handle(DeleteDepartmentCommand request, CancellationToken cancellationToken)
+ {
+ var depart = await departmentRepository.GetAsync(request.DeptId, cancellationToken) ??
+ throw new KnownException($"部门不存在,DeptId={request.DeptId}");
+
+
+ depart.Delete();
+ }
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Web/Application/Commands/Identity/UpdateDepartmrntInfoCommand.cs b/src/NetCorePal.D3Shop.Web/Application/Commands/Identity/UpdateDepartmrntInfoCommand.cs
new file mode 100644
index 0000000..bfb386b
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web/Application/Commands/Identity/UpdateDepartmrntInfoCommand.cs
@@ -0,0 +1,41 @@
+using FluentValidation;
+using NetCorePal.D3Shop.Admin.Shared.Dtos.Identity;
+using NetCorePal.D3Shop.Admin.Shared.Requests;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
+using NetCorePal.D3Shop.Infrastructure.Repositories.Identity;
+using NetCorePal.D3Shop.Web.Application.Queries.Identity;
+using NetCorePal.Extensions.Primitives;
+
+namespace NetCorePal.D3Shop.Web.Application.Commands.Identity;
+
+public record UpdateDepartmrntInfoCommand(DeptId DepartmentId, string Name, string Description, IEnumerable Users) : ICommand;
+
+
+
+public class UpdateDepartmentCommandValidator : AbstractValidator
+{
+ public UpdateDepartmentCommandValidator(DepartmentQuery departmentQuery)
+ {
+ RuleFor(u => u.Name).NotEmpty().WithMessage("部门名称不能为空");
+ }
+}
+
+public class UpdateDepartmentInfoCommandHandler(DepartmentRepository departmentRepository)
+ : ICommandHandler
+{
+ public async Task Handle(UpdateDepartmrntInfoCommand request, CancellationToken cancellationToken)
+ {
+ var department = await departmentRepository.GetAsync(request.DepartmentId, cancellationToken) ??
+ throw new KnownException($"未找到部门,DepartId = {request.DepartmentId}");
+
+ List departmentUsers = [];
+ foreach (var user in request.Users)
+ {
+ departmentUsers.Add(new DepartmentUser(user.UserName, user.UserId));
+ }
+
+ department.UpdateDepartInfo(request.Name, request.Description, departmentUsers);
+ }
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Web/Application/Commands/Identity/UpdateUserDeptInfoCommand.cs b/src/NetCorePal.D3Shop.Web/Application/Commands/Identity/UpdateUserDeptInfoCommand.cs
new file mode 100644
index 0000000..efb443f
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web/Application/Commands/Identity/UpdateUserDeptInfoCommand.cs
@@ -0,0 +1,21 @@
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
+using NetCorePal.D3Shop.Infrastructure.Repositories.Identity;
+using NetCorePal.Extensions.Primitives;
+
+namespace NetCorePal.D3Shop.Web.Application.Commands.Identity;
+
+public record UpdateUserDeptInfoCommand(AdminUserId AdminUserId, DeptId DeptId, string DeptName) : ICommand;
+
+public class UpdateUserDeptInfoCommandHandler(AdminUserRepository adminUserRepository)
+ : ICommandHandler
+{
+ public async Task Handle(UpdateUserDeptInfoCommand request, CancellationToken cancellationToken)
+ {
+ var user = await adminUserRepository.GetAsync(request.AdminUserId, cancellationToken) ??
+ throw new KnownException($"未找到用户,AdminUserId = {request.AdminUserId}");
+
+ user.SetUserDepts(request.DeptId, request.DeptName);
+ }
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Web/Application/DomainEventHandlers/Identity/DepartmentInfoChangedDomainEventHandler.cs b/src/NetCorePal.D3Shop.Web/Application/DomainEventHandlers/Identity/DepartmentInfoChangedDomainEventHandler.cs
new file mode 100644
index 0000000..a183b86
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web/Application/DomainEventHandlers/Identity/DepartmentInfoChangedDomainEventHandler.cs
@@ -0,0 +1,22 @@
+using MediatR;
+using NetCorePal.D3Shop.Domain.DomainEvents.Identity;
+using NetCorePal.D3Shop.Web.Application.Commands.Identity;
+using NetCorePal.D3Shop.Web.Application.Queries.Identity;
+using NetCorePal.Extensions.Domain;
+
+namespace NetCorePal.D3Shop.Web.Application.DomainEventHandlers.Identity;
+
+public class DepartmentInfoChangedDomainEventHandler(IMediator mediator, AdminUserQuery adminUserQuery)
+ : IDomainEventHandler
+{
+ public async Task Handle(DepartmentInfoChangedDomainEvent notification, CancellationToken cancellationToken)
+ {
+ var department = notification.Department;
+ var adminUserIds = await adminUserQuery.GetUserIdsByDeptIdAsync(department.Id, cancellationToken);
+ foreach (var adminUserId in adminUserIds)
+ {
+ await mediator.Send(new UpdateUserDeptInfoCommand(adminUserId, department.Id, department.Name),
+ cancellationToken);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Web/Application/Queries/Identity/AdminUserQuery.cs b/src/NetCorePal.D3Shop.Web/Application/Queries/Identity/AdminUserQuery.cs
index b02d7c3..feb26c8 100644
--- a/src/NetCorePal.D3Shop.Web/Application/Queries/Identity/AdminUserQuery.cs
+++ b/src/NetCorePal.D3Shop.Web/Application/Queries/Identity/AdminUserQuery.cs
@@ -3,6 +3,7 @@
using NetCorePal.D3Shop.Admin.Shared.Requests;
using NetCorePal.D3Shop.Admin.Shared.Responses;
using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
using NetCorePal.D3Shop.Web.Const;
using NetCorePal.D3Shop.Web.Controllers.Identity.Dto;
@@ -79,6 +80,13 @@ public async Task DoesAdminUserExist(string userName, CancellationToken ca
.AnyAsync(au => au.Name == userName, cancellationToken);
}
+ public async Task> GetUserIdsByDeptIdAsync(DeptId deptId, CancellationToken cancellationToken)
+ {
+ return await AdminUserSet.AsNoTracking()
+ .Where(x => x.UserDepts.Any(r => r.DeptId == deptId))
+ .Select(x => x.Id)
+ .ToListAsync(cancellationToken);
+ }
public async Task DoesAdminUserExist(RoleId roleId, CancellationToken cancellationToken)
{
return await AdminUserSet.AsNoTracking()
diff --git a/src/NetCorePal.D3Shop.Web/Application/Queries/Identity/DepartmentQuery.cs b/src/NetCorePal.D3Shop.Web/Application/Queries/Identity/DepartmentQuery.cs
new file mode 100644
index 0000000..e8cad67
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web/Application/Queries/Identity/DepartmentQuery.cs
@@ -0,0 +1,39 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Caching.Memory;
+using NetCorePal.D3Shop.Admin.Shared.Requests;
+using NetCorePal.D3Shop.Admin.Shared.Responses;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
+using NetCorePal.D3Shop.Web.Const;
+using NetCorePal.D3Shop.Web.Controllers.Identity.Dto;
+using NetCorePal.D3Shop.Web.Extensions;
+using NetCorePal.Extensions.Dto;
+using NetCorePal.Extensions.Primitives;
+
+namespace NetCorePal.D3Shop.Web.Application.Queries.Identity;
+
+public class DepartmentQuery(ApplicationDbContext applicationDbContext) : IQuery
+{
+ private DbSet DepartmentSet { get; } = applicationDbContext.Departments;
+
+ public async Task DoesDepartmentExist(string name, CancellationToken cancellationToken)
+ {
+ return await DepartmentSet.AsNoTracking()
+ .AnyAsync(r => r.Name == name, cancellationToken: cancellationToken);
+ }
+
+
+ public async Task> GetAllDepartmentsAsync(DepartmentQueryRequest queryRequest,
+ CancellationToken cancellationToken)
+ {
+ var departments = await DepartmentSet.AsNoTracking()
+ .WhereIf(!queryRequest.Name.IsNullOrWhiteSpace(), dt => dt.Name.Contains(queryRequest.Name!))
+ .OrderBy(dt => dt.Id)
+ .Select(dt => new DepartmentResponse(dt.Id, dt.Name, dt.Description))
+ .ToPagedDataAsync(queryRequest, cancellationToken);
+ return departments;
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Web/Blazor/BlazorServiceExtensions.cs b/src/NetCorePal.D3Shop.Web/Blazor/BlazorServiceExtensions.cs
index 7c5d718..0139868 100644
--- a/src/NetCorePal.D3Shop.Web/Blazor/BlazorServiceExtensions.cs
+++ b/src/NetCorePal.D3Shop.Web/Blazor/BlazorServiceExtensions.cs
@@ -9,5 +9,7 @@ public static void AddClientServices(this IServiceCollection services)
{
services.AddScoped();
services.AddScoped();
+ services.AddScoped();
+
}
}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Web/Controllers/Identity/DepartmentController.cs b/src/NetCorePal.D3Shop.Web/Controllers/Identity/DepartmentController.cs
new file mode 100644
index 0000000..70d6163
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web/Controllers/Identity/DepartmentController.cs
@@ -0,0 +1,73 @@
+using MediatR;
+using Microsoft.AspNetCore.Mvc;
+using NetCorePal.D3Shop.Admin.Shared.Permission;
+using NetCorePal.D3Shop.Admin.Shared.Requests;
+using NetCorePal.D3Shop.Admin.Shared.Responses;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
+using NetCorePal.D3Shop.Web.Admin.Client.Services;
+using NetCorePal.D3Shop.Web.Application.Commands.Identity;
+using NetCorePal.D3Shop.Web.Application.Commands.Identity.Dto;
+using NetCorePal.D3Shop.Web.Application.Queries.Identity;
+using NetCorePal.D3Shop.Web.Auth;
+using NetCorePal.D3Shop.Web.Blazor;
+using NetCorePal.D3Shop.Web.Helper;
+using NetCorePal.Extensions.Dto;
+using NetCorePal.Extensions.Primitives;
+
+namespace NetCorePal.D3Shop.Web.Controllers.Identity;
+
+[Route("api/[controller]/[action]")]
+[ApiController]
+[KnownExceptionHandler]
+[AdminPermission(PermissionCodes.DepartmentManagement)]
+public class DepartmentController(
+ IMediator mediator,
+ DepartmentQuery departmentQuery)
+ : ControllerBase, IDepartmentService
+{
+ private CancellationToken CancellationToken => HttpContext?.RequestAborted ?? default;
+
+ [HttpPost]
+ [AdminPermission(PermissionCodes.DepartmentCreate)]
+ public async Task> CreateDepartment([FromBody] CreateDepartmentRequest request)
+ {
+
+ var departmentId = await mediator.Send(
+ new CreateDepartmentCommand(request.Name, request.Description,request.Users, request.ParentId),
+ CancellationToken);
+
+ return departmentId.AsResponseData();
+ }
+
+ [HttpGet]
+ public async Task>> GetAllDepartments(
+ [FromQuery] DepartmentQueryRequest request)
+ {
+ var department = await departmentQuery.GetAllDepartmentsAsync(request, CancellationToken);
+ return department.AsResponseData();
+ }
+
+
+
+ [HttpPut("{id}")]
+ [AdminPermission(PermissionCodes.DepartmentEdit)]
+ public async Task UpdateDepartmentInfo([FromRoute] DeptId id,
+ [FromBody] UpdateDepartmentInfoRequest request)
+ {
+
+ await mediator.Send(new UpdateDepartmrntInfoCommand(id, request.Name, request.Description,request.Users), CancellationToken);
+ return new ResponseData();
+ }
+
+
+ [HttpDelete("{id}")]
+ [AdminPermission(PermissionCodes.DepartmentDelete)]
+ public async Task DeleteDepartment([FromRoute] DeptId id)
+ {
+ await mediator.Send(new DeleteDepartmentCommand(id), CancellationToken);
+ return new ResponseData();
+ }
+
+}
\ No newline at end of file
diff --git a/src/NetCorePal.D3Shop.Web/Migrations/20241223113656_AddTableDepartmentUser.Designer.cs b/src/NetCorePal.D3Shop.Web/Migrations/20241223113656_AddTableDepartmentUser.Designer.cs
new file mode 100644
index 0000000..aeea4ab
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web/Migrations/20241223113656_AddTableDepartmentUser.Designer.cs
@@ -0,0 +1,308 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetCorePal.D3Shop.Infrastructure;
+
+#nullable disable
+
+namespace NetCorePal.D3Shop.Web.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ [Migration("20241223113656_AddTableDepartmentUser")]
+ partial class AddTableDepartmentUser
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "8.0.2")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.DeliverAggregate.DeliverRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("OrderId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.ToTable("deliverrecord", (string)null);
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.AdminUser", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("IsDeleted")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Phone")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("adminUsers", (string)null);
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.AdminUserPermission", b =>
+ {
+ b.Property("AdminUserId")
+ .HasColumnType("bigint");
+
+ b.Property("PermissionCode")
+ .HasColumnType("varchar(255)");
+
+ b.Property("SourceRoleIds")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("AdminUserId", "PermissionCode");
+
+ b.ToTable("adminUserPermissions", (string)null);
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.AdminUserRole", b =>
+ {
+ b.Property("AdminUserId")
+ .HasColumnType("bigint");
+
+ b.Property("RoleId")
+ .HasColumnType("bigint");
+
+ b.Property("RoleName")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("AdminUserId", "RoleId");
+
+ b.ToTable("adminUserRoles", (string)null);
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.UserDept", b =>
+ {
+ b.Property("AdminUserId")
+ .HasColumnType("bigint");
+
+ b.Property("DeptId")
+ .HasColumnType("bigint");
+
+ b.Property("DeptName")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("AdminUserId", "DeptId");
+
+ b.ToTable("userDepts", (string)null);
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate.Department", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("IsDeleted")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("ParentId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.ToTable("departments", (string)null);
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate.DepartmentUser", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("bigint");
+
+ b.Property("DeptId")
+ .HasColumnType("bigint");
+
+ b.Property("UserName")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("UserId", "DeptId");
+
+ b.HasIndex("DeptId");
+
+ b.ToTable("departmentUser", (string)null);
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate.Role", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("roles", (string)null);
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate.RolePermission", b =>
+ {
+ b.Property("RoleId")
+ .HasColumnType("bigint");
+
+ b.Property("PermissionCode")
+ .HasColumnType("varchar(255)");
+
+ b.HasKey("RoleId", "PermissionCode");
+
+ b.ToTable("rolePermissions", (string)null);
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.OrderAggregate.Order", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("Count")
+ .HasColumnType("int");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("Paid")
+ .HasColumnType("tinyint(1)");
+
+ b.HasKey("Id");
+
+ b.ToTable("order", (string)null);
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.AdminUserPermission", b =>
+ {
+ b.HasOne("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.AdminUser", null)
+ .WithMany("Permissions")
+ .HasForeignKey("AdminUserId")
+ .OnDelete(DeleteBehavior.ClientCascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.AdminUserRole", b =>
+ {
+ b.HasOne("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.AdminUser", null)
+ .WithMany("Roles")
+ .HasForeignKey("AdminUserId")
+ .OnDelete(DeleteBehavior.ClientCascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.UserDept", b =>
+ {
+ b.HasOne("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.AdminUser", null)
+ .WithMany("UserDepts")
+ .HasForeignKey("AdminUserId")
+ .OnDelete(DeleteBehavior.ClientCascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate.DepartmentUser", b =>
+ {
+ b.HasOne("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate.Department", null)
+ .WithMany("Users")
+ .HasForeignKey("DeptId")
+ .OnDelete(DeleteBehavior.ClientCascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate.RolePermission", b =>
+ {
+ b.HasOne("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate.Role", null)
+ .WithMany("Permissions")
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.AdminUser", b =>
+ {
+ b.Navigation("Permissions");
+
+ b.Navigation("Roles");
+
+ b.Navigation("UserDepts");
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate.Department", b =>
+ {
+ b.Navigation("Users");
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate.Role", b =>
+ {
+ b.Navigation("Permissions");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/NetCorePal.D3Shop.Web/Migrations/20241223113656_AddTableDepartmentUser.cs b/src/NetCorePal.D3Shop.Web/Migrations/20241223113656_AddTableDepartmentUser.cs
new file mode 100644
index 0000000..08ec41f
--- /dev/null
+++ b/src/NetCorePal.D3Shop.Web/Migrations/20241223113656_AddTableDepartmentUser.cs
@@ -0,0 +1,184 @@
+using System;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace NetCorePal.D3Shop.Web.Migrations
+{
+ ///
+ public partial class AddTableDepartmentUser : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_adminUserPermissions_adminUsers_AdminUserId",
+ table: "adminUserPermissions");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_adminUserRoles_adminUsers_AdminUserId",
+ table: "adminUserRoles");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_adminUserRoles_roles_RoleId",
+ table: "adminUserRoles");
+
+ migrationBuilder.DropIndex(
+ name: "IX_adminUserRoles_RoleId",
+ table: "adminUserRoles");
+
+ migrationBuilder.DropColumn(
+ name: "PermissionRemark",
+ table: "rolePermissions");
+
+ migrationBuilder.DropColumn(
+ name: "PermissionRemark",
+ table: "adminUserPermissions");
+
+ migrationBuilder.CreateTable(
+ name: "departments",
+ columns: table => new
+ {
+ Id = table.Column(type: "bigint", nullable: false)
+ .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
+ Name = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Description = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ ParentId = table.Column(type: "bigint", nullable: false),
+ CreatedAt = table.Column(type: "datetime(6)", nullable: false),
+ IsDeleted = table.Column(type: "tinyint(1)", nullable: false),
+ DeletedAt = table.Column(type: "datetime(6)", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_departments", x => x.Id);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "userDepts",
+ columns: table => new
+ {
+ AdminUserId = table.Column(type: "bigint", nullable: false),
+ DeptId = table.Column(type: "bigint", nullable: false),
+ DeptName = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_userDepts", x => new { x.AdminUserId, x.DeptId });
+ table.ForeignKey(
+ name: "FK_userDepts_adminUsers_AdminUserId",
+ column: x => x.AdminUserId,
+ principalTable: "adminUsers",
+ principalColumn: "Id");
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "departmentUser",
+ columns: table => new
+ {
+ DeptId = table.Column(type: "bigint", nullable: false),
+ UserId = table.Column(type: "bigint", nullable: false),
+ UserName = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_departmentUser", x => new { x.UserId, x.DeptId });
+ table.ForeignKey(
+ name: "FK_departmentUser_departments_DeptId",
+ column: x => x.DeptId,
+ principalTable: "departments",
+ principalColumn: "Id");
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_departmentUser_DeptId",
+ table: "departmentUser",
+ column: "DeptId");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_adminUserPermissions_adminUsers_AdminUserId",
+ table: "adminUserPermissions",
+ column: "AdminUserId",
+ principalTable: "adminUsers",
+ principalColumn: "Id");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_adminUserRoles_adminUsers_AdminUserId",
+ table: "adminUserRoles",
+ column: "AdminUserId",
+ principalTable: "adminUsers",
+ principalColumn: "Id");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_adminUserPermissions_adminUsers_AdminUserId",
+ table: "adminUserPermissions");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_adminUserRoles_adminUsers_AdminUserId",
+ table: "adminUserRoles");
+
+ migrationBuilder.DropTable(
+ name: "departmentUser");
+
+ migrationBuilder.DropTable(
+ name: "userDepts");
+
+ migrationBuilder.DropTable(
+ name: "departments");
+
+ migrationBuilder.AddColumn(
+ name: "PermissionRemark",
+ table: "rolePermissions",
+ type: "longtext",
+ nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.AddColumn(
+ name: "PermissionRemark",
+ table: "adminUserPermissions",
+ type: "longtext",
+ nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_adminUserRoles_RoleId",
+ table: "adminUserRoles",
+ column: "RoleId");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_adminUserPermissions_adminUsers_AdminUserId",
+ table: "adminUserPermissions",
+ column: "AdminUserId",
+ principalTable: "adminUsers",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_adminUserRoles_adminUsers_AdminUserId",
+ table: "adminUserRoles",
+ column: "AdminUserId",
+ principalTable: "adminUsers",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_adminUserRoles_roles_RoleId",
+ table: "adminUserRoles",
+ column: "RoleId",
+ principalTable: "roles",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ }
+ }
+}
diff --git a/src/NetCorePal.D3Shop.Web/Migrations/ApplicationDbContextModelSnapshot.cs b/src/NetCorePal.D3Shop.Web/Migrations/ApplicationDbContextModelSnapshot.cs
index 9d73f8a..fc0f9b2 100644
--- a/src/NetCorePal.D3Shop.Web/Migrations/ApplicationDbContextModelSnapshot.cs
+++ b/src/NetCorePal.D3Shop.Web/Migrations/ApplicationDbContextModelSnapshot.cs
@@ -80,10 +80,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("PermissionCode")
.HasColumnType("varchar(255)");
- b.Property("PermissionRemark")
- .IsRequired()
- .HasColumnType("longtext");
-
b.Property("SourceRoleIds")
.IsRequired()
.HasColumnType("longtext");
@@ -107,11 +103,78 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.HasKey("AdminUserId", "RoleId");
- b.HasIndex("RoleId");
-
b.ToTable("adminUserRoles", (string)null);
});
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.UserDept", b =>
+ {
+ b.Property("AdminUserId")
+ .HasColumnType("bigint");
+
+ b.Property("DeptId")
+ .HasColumnType("bigint");
+
+ b.Property("DeptName")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("AdminUserId", "DeptId");
+
+ b.ToTable("userDepts", (string)null);
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate.Department", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("IsDeleted")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("ParentId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.ToTable("departments", (string)null);
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate.DepartmentUser", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("bigint");
+
+ b.Property("DeptId")
+ .HasColumnType("bigint");
+
+ b.Property("UserName")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("UserId", "DeptId");
+
+ b.HasIndex("DeptId");
+
+ b.ToTable("departmentUser", (string)null);
+ });
+
modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate.Role", b =>
{
b.Property("Id")
@@ -144,10 +207,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("PermissionCode")
.HasColumnType("varchar(255)");
- b.Property("PermissionRemark")
- .IsRequired()
- .HasColumnType("longtext");
-
b.HasKey("RoleId", "PermissionCode");
b.ToTable("rolePermissions", (string)null);
@@ -182,7 +241,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.HasOne("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.AdminUser", null)
.WithMany("Permissions")
.HasForeignKey("AdminUserId")
- .OnDelete(DeleteBehavior.Cascade)
+ .OnDelete(DeleteBehavior.ClientCascade)
.IsRequired();
});
@@ -191,13 +250,25 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.HasOne("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.AdminUser", null)
.WithMany("Roles")
.HasForeignKey("AdminUserId")
- .OnDelete(DeleteBehavior.Cascade)
+ .OnDelete(DeleteBehavior.ClientCascade)
.IsRequired();
+ });
- b.HasOne("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate.Role", null)
- .WithMany()
- .HasForeignKey("RoleId")
- .OnDelete(DeleteBehavior.Cascade)
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.UserDept", b =>
+ {
+ b.HasOne("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate.AdminUser", null)
+ .WithMany("UserDepts")
+ .HasForeignKey("AdminUserId")
+ .OnDelete(DeleteBehavior.ClientCascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate.DepartmentUser", b =>
+ {
+ b.HasOne("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate.Department", null)
+ .WithMany("Users")
+ .HasForeignKey("DeptId")
+ .OnDelete(DeleteBehavior.ClientCascade)
.IsRequired();
});
@@ -215,6 +286,13 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Navigation("Permissions");
b.Navigation("Roles");
+
+ b.Navigation("UserDepts");
+ });
+
+ modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate.Department", b =>
+ {
+ b.Navigation("Users");
});
modelBuilder.Entity("NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate.Role", b =>
diff --git a/src/NetCorePal.D3Shop.Web/NetCorePal.D3Shop.Web.csproj b/src/NetCorePal.D3Shop.Web/NetCorePal.D3Shop.Web.csproj
index 60b30c3..328a393 100644
--- a/src/NetCorePal.D3Shop.Web/NetCorePal.D3Shop.Web.csproj
+++ b/src/NetCorePal.D3Shop.Web/NetCorePal.D3Shop.Web.csproj
@@ -25,6 +25,10 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/test/NetCorePal.D3Shop.Domain.Tests/Identity/DepartmentTests.cs b/test/NetCorePal.D3Shop.Domain.Tests/Identity/DepartmentTests.cs
new file mode 100644
index 0000000..9e93ab4
--- /dev/null
+++ b/test/NetCorePal.D3Shop.Domain.Tests/Identity/DepartmentTests.cs
@@ -0,0 +1,109 @@
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.RoleAggregate;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NetCorePal.D3Shop.Domain.Tests.Identity
+{
+
+
+ public class DepartmentTests
+ {
+ private readonly Department _department;
+
+ // 构造函数:初始化 Department 实例,使用构造函数传入初始数据
+ public DepartmentTests()
+ {
+ // 创建初始的用户列表
+ var initialUsers = new List
+ {
+ new DepartmentUser("User1", new AdminUserId(1)), // 创建 User1
+ new DepartmentUser("User2", new AdminUserId(2)) // 创建 User2
+ };
+
+ // 使用构造函数创建 Department 实例,传入部门名称、描述、父部门 ID 和用户列表
+ _department = new Department("InitialName", "InitialDescription", new DeptId(1), initialUsers);
+ }
+
+ // 测试:更新部门名称和描述
+ [Fact]
+ public void UpdateDepartInfo_ShouldUpdateNameAndDescription()
+ {
+ // Arrange:准备更新后的名称和描述
+ var newName = "UpdatedName";
+ var newDescription = "UpdatedDescription";
+
+ // Act:调用 UpdateDepartInfo 方法更新部门名称和描述
+ _department.UpdateDepartInfo(newName, newDescription, _department.Users);
+
+ // Assert:验证名称和描述是否正确更新
+ Assert.Equal(newName, _department.Name);
+ Assert.Equal(newDescription, _department.Description);
+ }
+
+ // 测试:添加新用户
+ [Fact]
+ public void UpdateDepartInfo_ShouldAddNewUsers()
+ {
+ // Arrange:准备新的用户列表,其中包括一个已有的用户和一个新用户
+ var newUsers = new List
+ {
+ new DepartmentUser("User1", new AdminUserId(1)), // 已有用户 User1
+ new DepartmentUser("User3", new AdminUserId(3)) // 新用户 User3
+ };
+
+ // Act:调用 UpdateDepartInfo 方法更新用户列表
+ _department.UpdateDepartInfo(_department.Name, _department.Description, newUsers);
+
+ // Assert:验证新用户是否已成功添加
+ Assert.Contains(_department.Users, u => u.UserId.Equals(new AdminUserId(3)) && u.UserName == "User3");
+ }
+
+ // 测试:移除已不存在的用户
+ [Fact]
+ public void UpdateDepartInfo_ShouldRemoveAbsentUsers()
+ {
+ // Arrange:准备新的用户列表,其中仅包含 User2,User1 将被移除
+ var newUsers = new List
+ {
+ new DepartmentUser("User2", new AdminUserId(2)) // 仅包含 User2
+ // User1 不在新列表中,应该被移除
+ };
+
+ // Act:调用 UpdateDepartInfo 方法更新用户列表
+ _department.UpdateDepartInfo(_department.Name, _department.Description, newUsers);
+
+ // Assert:验证 User1 是否被移除
+ Assert.DoesNotContain(_department.Users, u => u.UserId.Equals(new AdminUserId(1)) && u.UserName == "User1");
+ }
+
+ // 测试:保留没有改变的用户
+ [Fact]
+ public void UpdateDepartInfo_ShouldRetainUnchangedUsers()
+ {
+ // Arrange:准备新的用户列表,User1 和 User2 都没有变化
+ var newUsers = new List
+ {
+ new DepartmentUser("User1", new AdminUserId(1)), // 保持不变的用户 User1
+ new DepartmentUser("User2", new AdminUserId(2)) // 保持不变的用户 User2
+ };
+
+ // Act:调用 UpdateDepartInfo 方法更新用户列表
+ _department.UpdateDepartInfo(_department.Name, _department.Description, newUsers);
+
+ // Assert:验证用户列表中依然只有 2 个用户,没有丢失或重复
+ Assert.Equal(2, _department.Users.Count); // 确保用户数量没有变化
+ }
+ }
+
+
+
+
+}
+
+
+
diff --git a/test/NetCorePal.D3Shop.Web.Tests/Identity/DepartmentTests.cs b/test/NetCorePal.D3Shop.Web.Tests/Identity/DepartmentTests.cs
new file mode 100644
index 0000000..4cac8f8
--- /dev/null
+++ b/test/NetCorePal.D3Shop.Web.Tests/Identity/DepartmentTests.cs
@@ -0,0 +1,153 @@
+using AntDesign;
+using Microsoft.AspNetCore.Mvc.Formatters;
+using NetCorePal.D3Shop.Admin.Shared.Requests;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.AdminUserAggregate;
+using Newtonsoft.Json;
+using System.Net.Http.Headers;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NetCorePal.D3Shop.Domain.AggregatesModel.Identity.DepartmentAggregate;
+using NetCorePal.D3Shop.Admin.Shared.Dtos.Identity;
+using NetCorePal.Extensions.Dto;
+using NetCorePal.D3Shop.Admin.Shared.Responses;
+using System.Xml.Linq;
+
+namespace NetCorePal.D3Shop.Web.Tests.Identity
+{
+ [Collection("web")]
+ public class DepartmentTests
+ {
+ private readonly HttpClient _client;
+
+ public DepartmentTests(MyWebApplicationFactory factory)
+ {
+ _client = factory.WithWebHostBuilder(builder => { builder.ConfigureServices(_ => { }); })
+ .CreateClient();
+ const string json = $$"""
+ {
+ "name": "{{AppDefaultCredentials.Name}}",
+ "password": "{{AppDefaultCredentials.Password}}"
+ }
+ """;
+ var content = new StringContent(json);
+ content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
+ _client.PostAsync("api/AdminUserAccount/login", content).GetAwaiter().GetResult();
+ }
+
+ #region CreateDepartment Tests
+
+ [Fact]
+ public async Task CreateDepartment_ShouldReturnDeptId()
+ {
+ // Arrange
+ var request = new CreateDepartmentRequest
+ {
+ Name = "TestDepartment",
+ Description = "test decription",
+ ParentId = new DeptId(0),
+ Users = new List
+ {
+ new CreateDepartmentUserInfoDto(new AdminUserId(1),"User1"), // 创建 User1
+ new CreateDepartmentUserInfoDto(new AdminUserId(2), "User2" ) // 创建 User2
+ },
+
+ };
+
+ // Act
+ var response = await _client.PostAsNewtonsoftJsonAsync("/api/Department/CreateDepartment", request);
+
+ // Assert - 创建成功
+ Assert.True(response.IsSuccessStatusCode);
+ var responseData = await response.Content.ReadFromNewtonsoftJsonAsync>();
+ Assert.NotNull(responseData);
+ Assert.NotEqual(0, responseData.Data.Id); // 验证返回的用户ID不是默认值
+ }
+
+ #endregion
+
+ #region GetAllDepartments Tests
+
+ [Fact]
+ public async Task GetAllDepartments_ShouldReturnPagedData()
+ {
+ const string testDeptName = "TestDepartment";
+ // Arrange
+ var request = new DepartmentQueryRequest
+ {
+ PageIndex = 1,
+ PageSize = 10,
+ Name = testDeptName,
+ };
+
+ var queryString = $"?PageIndex={request.PageIndex}&PageSize={request.PageSize}";
+ var url = "/api/Department/GetAllDepartments" + queryString;
+
+ // Act
+ var response = await _client.GetAsync(url);
+
+ // Assert
+ response.EnsureSuccessStatusCode(); // 确保返回 200 OK
+ var conditionData = await response.Content
+ .ReadFromNewtonsoftJsonAsync>>();
+ Assert.NotNull(conditionData);
+ Assert.All(conditionData.Data.Items, dept => Assert.Contains(testDeptName, dept.Name)); // 验证返回的用户符合条件
+ }
+
+ #endregion
+
+ #region UpdateDepartmentInfo Tests
+
+ [Fact]
+ public async Task UpdateDepartmentInfo_ShouldReturnSuccess()
+ {
+ // Arrange
+ var deptId = 1; // 假设部门 ID 为 1
+ var request = new UpdateDepartmentInfoRequest
+ {
+ Name = "Updated Department",
+ Description = "Updated Description",
+ Users = new List
+ {
+ new CreateDepartmentUserInfoDto( new AdminUserId(1),"User3"), // 创建 User1
+ new CreateDepartmentUserInfoDto(new AdminUserId(2), "User4" ) // 创建 User2
+ }
+ };
+
+ // Act
+ var response = await _client.PutAsNewtonsoftJsonAsync($"/api/Department/UpdateDepartmentInfo/{deptId}", request);
+
+ // Assert
+ response.EnsureSuccessStatusCode();
+ var responseData = await response.Content.ReadAsStringAsync();
+ Assert.Contains("success", responseData, StringComparison.OrdinalIgnoreCase);
+ }
+
+ #endregion
+
+ #region DeleteDepartment Tests
+
+ [Fact]
+ public async Task DeleteDepartment_ShouldReturnSuccess()
+ {
+ // Arrange
+ var deptId = 1; // 假设部门 ID 为 1
+
+ // Act
+ var response = await _client.DeleteAsync($"/api/Department/DeleteDepartment/{deptId}");
+
+ // Assert
+ response.EnsureSuccessStatusCode();
+ var responseData = await response.Content.ReadAsStringAsync();
+ Assert.Contains("success", responseData, StringComparison.OrdinalIgnoreCase);
+ }
+
+ #endregion
+
+ }
+
+
+
+}