From bc2e5e3b7b47b833191cd4810a9fade8e1b71f18 Mon Sep 17 00:00:00 2001 From: feijie Date: Fri, 21 Mar 2025 19:53:30 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(user):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E5=8C=85=E6=8B=AC=E5=88=9B=E5=BB=BA=E3=80=81=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E3=80=81=E5=88=A0=E9=99=A4=E3=80=81=E6=9F=A5=E8=AF=A2=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=AD=89=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataSeeder/IProjectNameDataSeeder.cs | 15 + .../ProjectNameDataSeederDataSeeder.cs | 503 ++++++++++++++++++ ...ProjectNamePermissionDefinitionProvider.cs | 6 + .../Permissions/ProjectNamePermissions.cs | 8 + .../Users/Dtos/CreateUpdateUserDto.cs | 37 ++ .../ProjectName/Users/Dtos/UserDto.cs | 39 ++ .../ProjectName/Users/Dtos/UserItemDto.cs | 39 ++ .../UserPagedAndSortedResultRequestDto.cs | 14 + .../ProjectName/Users/IUserAppService.cs | 55 ++ ...CompanyName.ProjectName.Application.csproj | 1 + .../ProjectNameApplicationMapperProfile.cs | 10 + .../ProjectNameApplicationModule.cs | 2 + .../ProjectName/Users/UserAppService.cs | 198 +++++++ ...Name.CompanyName.ProjectName.Domain.csproj | 4 + .../ProjectName/ProjectNameDomainModule.cs | 4 + .../ProjectName/TreeCodes/IHaveTreeCode.cs | 21 + .../TreeCodes/ITreeCodeGenerator.cs | 33 ++ .../TreeCodes/TreeCodeGenerator.cs | 114 ++++ .../ProjectName/Users/IUserManager.cs | 69 +++ .../ProjectName/Users/IUserRepository.cs | 9 + .../CompanyName/ProjectName/Users/User.cs | 59 ++ .../ProjectName/Users/UserManager.cs | 406 ++++++++++++++ ...ame.ProjectName.EntityFrameworkCore.csproj | 1 + .../IProjectNameDbContext.cs | 5 +- .../ProjectNameDbContext.cs | 9 +- ...ectNameDbContextModelCreatingExtensions.cs | 17 + .../ProjectNameEntityFrameworkCoreModule.cs | 6 +- .../ProjectName/Users/UserRepository.cs | 22 + .../ProjectName/Users/UserController.cs | 134 +++++ 29 files changed, 1836 insertions(+), 4 deletions(-) create mode 100644 aspnet-core/templates/aio/content/migrations/PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore/DataSeeder/IProjectNameDataSeeder.cs create mode 100644 aspnet-core/templates/aio/content/migrations/PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore/DataSeeder/ProjectNameDataSeederDataSeeder.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/CreateUpdateUserDto.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserDto.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserItemDto.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserPagedAndSortedResultRequestDto.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/IUserAppService.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/Users/UserAppService.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/IHaveTreeCode.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/ITreeCodeGenerator.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/TreeCodeGenerator.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/IUserManager.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/IUserRepository.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/User.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/UserManager.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/Users/UserRepository.cs create mode 100644 aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.HttpApi/PackageName/CompanyName/ProjectName/Users/UserController.cs diff --git a/aspnet-core/templates/aio/content/migrations/PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore/DataSeeder/IProjectNameDataSeeder.cs b/aspnet-core/templates/aio/content/migrations/PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore/DataSeeder/IProjectNameDataSeeder.cs new file mode 100644 index 000000000..a1e6c8708 --- /dev/null +++ b/aspnet-core/templates/aio/content/migrations/PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore/DataSeeder/IProjectNameDataSeeder.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using Volo.Abp.Data; + +namespace PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore.DataSeeder +{ + public interface IProjectNameDataSeeder + { + /// + /// 初始化数据 + /// + /// 数据种子上下文 + /// 任务 + Task SeedAsync(DataSeedContext context); + } +} diff --git a/aspnet-core/templates/aio/content/migrations/PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore/DataSeeder/ProjectNameDataSeederDataSeeder.cs b/aspnet-core/templates/aio/content/migrations/PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore/DataSeeder/ProjectNameDataSeederDataSeeder.cs new file mode 100644 index 000000000..57276f6be --- /dev/null +++ b/aspnet-core/templates/aio/content/migrations/PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore/DataSeeder/ProjectNameDataSeederDataSeeder.cs @@ -0,0 +1,503 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Guids; +using Volo.Abp.Identity; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Uow; +using IdentityRole = Volo.Abp.Identity.IdentityRole; +using IdentityUser = Volo.Abp.Identity.IdentityUser; + +namespace PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore.DataSeeder +{ + public class ProjectNameDataSeederDataSeeder : IProjectNameDataSeeder, ITransientDependency + { + private readonly ICurrentTenant _currentTenant; + private readonly IGuidGenerator _guidGenerator; + private readonly ILogger _logger; + private readonly IRepository _orgRepository; + private readonly IRepository _workUnitRepository; + private readonly IRepository _userRepository; + private readonly IdentityUserManager _identityUserManager; + private readonly IdentityRoleManager _identityRoleManager; + private readonly IUnitOfWorkManager _unitOfWorkManager; + + /// + /// 构造函数 + /// + public ProjectNameDataSeeder( + ICurrentTenant currentTenant, + IGuidGenerator guidGenerator, + ILogger logger, + IRepository orgRepository, + IRepository workUnitRepository, + IRepository userRepository, + IdentityUserManager identityUserManager, + IdentityRoleManager identityRoleManager, + IUnitOfWorkManager unitOfWorkManager) + { + _currentTenant = currentTenant; + _guidGenerator = guidGenerator; + _logger = logger; + _orgRepository = orgRepository; + _workUnitRepository = workUnitRepository; + _userRepository = userRepository; + _identityUserManager = identityUserManager; + _identityRoleManager = identityRoleManager; + _unitOfWorkManager = unitOfWorkManager; + } + + /// + /// 初始化数据 + /// + /// 数据种子上下文 + /// 任务 + public async Task SeedAsync(DataSeedContext context) + { + using (_currentTenant.Change(context.TenantId)) + { + _logger.LogInformation("开始初始化巡检数据..."); + + // 初始化角色 + using (var uow = _unitOfWorkManager.Begin(requiresNew: true)) + { + await SeedRolesAsync(); + await uow.CompleteAsync(); + } + + // 初始化单位数据 + using (var uow = _unitOfWorkManager.Begin(requiresNew: true)) + { + await SeedWorkUnitsAsync(); + await uow.CompleteAsync(); + } + + // 初始化组织数据 + using (var uow = _unitOfWorkManager.Begin(requiresNew: true)) + { + await SeedOrgsAsync(); + await uow.CompleteAsync(); + } + + // 初始化用户数据 + using (var uow = _unitOfWorkManager.Begin(requiresNew: true)) + { + await SeedUsersAsync(); + await uow.CompleteAsync(); + } + + _logger.LogInformation("巡检数据初始化完成"); + } + } + + /// + /// 初始化角色数据 + /// + private async Task SeedRolesAsync() + { + // 超级管理员 + await CreateRoleIfNotExistsAsync( + "超级管理员", + "系统超级管理员,拥有所有权限"); + + // 区委办公室-督查室管理员 + await CreateRoleIfNotExistsAsync( + "区委办公室-督查室管理员", + "区委办公室督查室管理员"); + + // 责任领导秘书 + await CreateRoleIfNotExistsAsync( + "责任领导秘书", + "负责协助责任领导进行工作"); + + // 责任单位管理员 + await CreateRoleIfNotExistsAsync( + "责任单位管理员", + "负责管理责任单位的信息"); + + // 责任单位落实人员 + await CreateRoleIfNotExistsAsync( + "责任单位落实人员", + "负责执行责任单位的任务"); + + // 责任单位分管领导 + await CreateRoleIfNotExistsAsync( + "责任单位分管领导", + "负责管理责任单位的部分工作"); + + // 责任单位党组书记 + await CreateRoleIfNotExistsAsync( + "责任单位党组书记", + "负责责任单位的党组工作"); + } + + /// + /// 创建角色(如果不存在) + /// + private async Task CreateRoleIfNotExistsAsync(string roleName, string description) + { + if (await _identityRoleManager.FindByNameAsync(roleName) == null) + { + var role = new IdentityRole( + _guidGenerator.Create(), + roleName, + _currentTenant.Id) + { + IsStatic = true, + IsPublic = true + }; + + await _identityRoleManager.CreateAsync(role); + + _logger.LogInformation($"创建角色:{roleName}"); + } + } + + /// + /// 初始化单位数据 + /// + private async Task SeedWorkUnitsAsync() + { + // 创建永川区 + var yongchuanDistrict = await CreateWorkUnitIfNotExistsAsync( + "永川区", + "001", + "001", + "永川区"); + + // 创建区政府 + var districtGovernment = await CreateWorkUnitIfNotExistsAsync( + "区政府", + "002", + "001.002", + "永川区政府", + yongchuanDistrict.Id); + + // 创建区委办公室 + var districtCommitteeOffice = await CreateWorkUnitIfNotExistsAsync( + "区委办公室", + "003", + "001.003", + "永川区委办公室", + yongchuanDistrict.Id); + + // 创建督查室 + await CreateWorkUnitIfNotExistsAsync( + "督查室", + "004", + "001.003.004", + "区委办公室督查室", + districtCommitteeOffice.Id); + + // 创建区委扫黑办 + await CreateWorkUnitIfNotExistsAsync( + "区委扫黑办", + "005", + "001.005", + "永川区委扫黑办", + yongchuanDistrict.Id); + + // 创建区体育局 + await CreateWorkUnitIfNotExistsAsync( + "区体育局", + "006", + "001.006", + "永川区体育局", + yongchuanDistrict.Id); + + // 创建区统计局 + await CreateWorkUnitIfNotExistsAsync( + "区统计局", + "007", + "001.007", + "永川区统计局", + yongchuanDistrict.Id); + + // 创建区信访办 + await CreateWorkUnitIfNotExistsAsync( + "区信访办", + "008", + "001.008", + "永川区信访办", + yongchuanDistrict.Id); + + // 创建区医保局 + await CreateWorkUnitIfNotExistsAsync( + "区医保局", + "009", + "001.009", + "永川区医保局", + yongchuanDistrict.Id); + + // 创建区大数据发展局 + await CreateWorkUnitIfNotExistsAsync( + "区大数据发展局", + "010", + "001.010", + "永川区大数据发展局", + yongchuanDistrict.Id); + + // 创建区机关事务局 + await CreateWorkUnitIfNotExistsAsync( + "区机关事务局", + "011", + "001.011", + "永川区机关事务局", + yongchuanDistrict.Id); + + // 创建区广播电视局 + await CreateWorkUnitIfNotExistsAsync( + "区广播电视局", + "012", + "001.012", + "永川区广播电视局", + yongchuanDistrict.Id); + + // 创建区中新项目管理局 + await CreateWorkUnitIfNotExistsAsync( + "区中新项目管理局", + "013", + "001.013", + "永川区中新项目管理局", + yongchuanDistrict.Id); + + // 创建镇街 + await CreateWorkUnitIfNotExistsAsync( + "镇街", + "014", + "001.014", + "永川区镇街", + yongchuanDistrict.Id); + } + + /// + /// 创建单位(如果不存在) + /// + private async Task CreateWorkUnitIfNotExistsAsync( + string name, + string code, + string treeCode, + string description, + Guid? parentId = null) + { + var workUnit = await _workUnitRepository.FindAsync(w => w.Name == name); + + if (workUnit == null) + { + workUnit = new WorkUnit( + _guidGenerator.Create(), + code, + treeCode, + name, + description) + { + ParentId = parentId + }; + + await _workUnitRepository.InsertAsync(workUnit); + + _logger.LogInformation($"创建单位:{name}"); + } + + return workUnit; + } + + /// + /// 初始化组织数据 + /// + private async Task SeedOrgsAsync() + { + // 创建区委办公室-督查室 + var supervisionOffice = await CreateOrgIfNotExistsAsync( + "区委办公室-督查室", + "001", + "001", + "区委办公室督查室"); + } + + /// + /// 创建组织(如果不存在) + /// + private async Task CreateOrgIfNotExistsAsync( + string name, + string code, + string treeCode, + string description, + Guid? parentId = null) + { + var org = await _orgRepository.FindAsync(o => o.Name == name); + + if (org == null) + { + org = new Org( + _guidGenerator.Create(), + code, + treeCode, + name, + description) + { + ParentId = parentId + }; + + await _orgRepository.InsertAsync(org); + + _logger.LogInformation($"创建组织:{name}"); + } + + return org; + } + + /// + /// 初始化用户数据 + /// + private async Task SeedUsersAsync() + { + // 获取所需单位 + var supervisionOffice = await _workUnitRepository.FindAsync(w => w.Name == "督查室"); + if (supervisionOffice == null) + { + _logger.LogError("未找到督查室单位,无法创建用户"); + return; + } + + // 获取所需组织 + var supervisionOrg = await _orgRepository.FindAsync(o => o.Name == "区委办公室-督查室"); + if (supervisionOrg == null) + { + _logger.LogError("未找到区委办公室-督查室组织,无法创建用户"); + return; + } + + // 查找超级管理员角色 + var superAdminRole = await _identityRoleManager.FindByNameAsync("超级管理员"); + if (superAdminRole == null) + { + _logger.LogError("未找到超级管理员角色,无法为用户分配角色"); + return; + } + + // 创建用户数据(使用固定用户名避免生成问题) + await CreateUserIfNotExistsAsync("李达康", "lidk001", GenderType.Male, supervisionOffice.Id, supervisionOrg.Id, "超级管理员"); + await CreateUserIfNotExistsAsync("高育良", "gyl002", GenderType.Male, supervisionOffice.Id, supervisionOrg.Id, "超级管理员"); + await CreateUserIfNotExistsAsync("祁同伟", "qtw003", GenderType.Male, supervisionOffice.Id, supervisionOrg.Id, "超级管理员"); + await CreateUserIfNotExistsAsync("侯亮平", "hlp004", GenderType.Male, supervisionOffice.Id, supervisionOrg.Id, "超级管理员"); + await CreateUserIfNotExistsAsync("赵瑞龙", "zrl005", GenderType.Female, supervisionOffice.Id, supervisionOrg.Id, "超级管理员"); + await CreateUserIfNotExistsAsync("宋梦平", "smp006", GenderType.Male, supervisionOffice.Id, supervisionOrg.Id, "超级管理员, 部门领导"); + await CreateUserIfNotExistsAsync("李太亮", "ltl007", GenderType.Male, supervisionOffice.Id, supervisionOrg.Id, "超级管理员, 部门领导"); + await CreateUserIfNotExistsAsync("高峰", "gf008", GenderType.Male, supervisionOffice.Id, supervisionOrg.Id, "超级管理员, 部门领导"); + await CreateUserIfNotExistsAsync("朱丽平", "zlp009", GenderType.Female, supervisionOffice.Id, supervisionOrg.Id, "超级管理员, 部门领导"); + await CreateUserIfNotExistsAsync("安欣", "ax010", GenderType.Male, supervisionOffice.Id, supervisionOrg.Id, "超级管理员"); + } + + /// + /// 创建用户(如果不存在) + /// + private async Task CreateUserIfNotExistsAsync( + string name, + string userName, + GenderType gender, + Guid workUnitId, + Guid orgId, + string roles) + { + // 检查用户是否已存在 + var existingUser = await _userRepository.FindAsync(u => u.NickName == name); + if (existingUser != null) + { + _logger.LogInformation($"用户[{name}]已存在,跳过创建"); + return; + } + + var identityUser = await _identityUserManager.FindByNameAsync(userName); + if (identityUser == null) + { + // 创建Identity用户 + identityUser = new IdentityUser( + _guidGenerator.Create(), + userName, + $"{userName}@example.com", + _currentTenant.Id) + { + Name = name, + Surname = "" + }; + + // 设置默认密码 123456 + var identityResult = await _identityUserManager.CreateAsync(identityUser, "123456"); + if (!identityResult.Succeeded) + { + _logger.LogError($"创建Identity用户[{name}]失败: {string.Join(", ", identityResult.Errors.Select(e => e.Description))}"); + return; + } + + // 分配角色 + if (!string.IsNullOrWhiteSpace(roles)) + { + var roleNames = roles.Split(',', StringSplitOptions.RemoveEmptyEntries); + foreach (var roleName in roleNames) + { + var trimmedRoleName = roleName.Trim(); + var role = await _identityRoleManager.FindByNameAsync(trimmedRoleName); + if (role != null) + { + var roleResult = await _identityUserManager.AddToRoleAsync(identityUser, trimmedRoleName); + if (!roleResult.Succeeded) + { + _logger.LogWarning($"为用户[{name}]分配角色[{trimmedRoleName}]失败: {string.Join(", ", roleResult.Errors.Select(e => e.Description))}"); + } + } + else + { + _logger.LogWarning($"角色[{trimmedRoleName}]不存在,无法为用户[{name}]分配"); + } + } + } + + // 创建系统用户 + var user = new User( + _guidGenerator.Create(), + name, + workUnitId, + identityUser.Id, + gender) + { + OrgId = orgId + }; + + // 保存用户 + await _userRepository.InsertAsync(user); + + _logger.LogInformation($"创建用户:{name},用户名:{userName}"); + } + else + { + _logger.LogInformation($"Identity用户[{userName}]已存在,检查是否需要创建业务用户"); + + // 检查是否需要创建业务用户 + var businessUser = await _userRepository.FindAsync(u => u.IdentityUserId == identityUser.Id); + if (businessUser == null) + { + // 创建系统用户 + var user = new User( + _guidGenerator.Create(), + name, + workUnitId, + identityUser.Id, + gender) + { + OrgId = orgId + }; + + // 保存用户 + await _userRepository.InsertAsync(user); + + _logger.LogInformation($"为已存在的Identity用户创建业务用户:{name}"); + } + } + } + } +} diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Permissions/ProjectNamePermissionDefinitionProvider.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Permissions/ProjectNamePermissionDefinitionProvider.cs index 4a170551b..2adf0a13f 100644 --- a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Permissions/ProjectNamePermissionDefinitionProvider.cs +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Permissions/ProjectNamePermissionDefinitionProvider.cs @@ -13,6 +13,12 @@ public class ProjectNamePermissionDefinitionProvider : PermissionDefinitionProvi group.AddPermission( ProjectNamePermissions.ManageSettings, L("Permission:ManageSettings")); + + var userPermission = group.AddPermission(ProjectNamePermissions.User.Default, L("Permission:User")); + userPermission.AddChild(ProjectNamePermissions.User.Create, L("Permission:Create")); + userPermission.AddChild(ProjectNamePermissions.User.Update, L("Permission:Update")); + userPermission.AddChild(ProjectNamePermissions.User.Delete, L("Permission:Delete")); + } private static LocalizableString L(string name) diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Permissions/ProjectNamePermissions.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Permissions/ProjectNamePermissions.cs index 98a957ff9..fb03f41bc 100644 --- a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Permissions/ProjectNamePermissions.cs +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Permissions/ProjectNamePermissions.cs @@ -5,4 +5,12 @@ public static class ProjectNamePermissions public const string GroupName = "ProjectName"; public const string ManageSettings = GroupName + ".ManageSettings"; + + public class User + { + public const string Default = GroupName + ".User"; + public const string Update = Default + ".Update"; + public const string Create = Default + ".Create"; + public const string Delete = Default + ".Delete"; + } } diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/CreateUpdateUserDto.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/CreateUpdateUserDto.cs new file mode 100644 index 000000000..ae1e5e270 --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/CreateUpdateUserDto.cs @@ -0,0 +1,37 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace PackageName.CompanyName.ProjectName.Users.Dtos +{ + [Serializable] + public class CreateUpdateUserDto + { + /// + /// 用户名称 + /// + [Required(ErrorMessage = "用户名称不能为空")] + [StringLength(50, ErrorMessage = "用户名称长度不能超过50个字符")] + public string NickName { get; set; } + + /// + /// 密码 + /// + [StringLength(20, MinimumLength = 6, ErrorMessage = "密码长度必须在6-20个字符之间")] + public string Password { get; set; } + + /// + /// 联系方式 + /// + public string ContactInfo { get; set; } + + /// + /// 职位 + /// + public string Position { get; set; } + + /// + /// 是否启用 + /// + public bool IsActive { get; set; } = true; + } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserDto.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserDto.cs new file mode 100644 index 000000000..2c5dd4f3d --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserDto.cs @@ -0,0 +1,39 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace PackageName.CompanyName.ProjectName.Users.Dtos +{ + [Serializable] + public class UserDto : FullAuditedEntityDto + { + /// + /// 用户名称 + /// + public string NickName { get; set; } + + /// + /// Identity用户Id + /// + public Guid IdentityUserId { get; set; } + + /// + /// 用户状态 + /// + public bool IsActive { get; set; } + + /// + /// 联系方式 + /// + public string ContactInfo { get; set; } + + /// + /// 职位 + /// + public string Position { get; set; } + + /// + /// 角色名称 + /// + public string RoleNames { get; set; } + } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserItemDto.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserItemDto.cs new file mode 100644 index 000000000..92014ffbe --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserItemDto.cs @@ -0,0 +1,39 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace PackageName.CompanyName.ProjectName.Users.Dtos +{ + [Serializable] + public class UserItemDto : FullAuditedEntityDto + { + /// + /// 用户名称 + /// + public string NickName { get; set; } + + /// + /// Identity用户Id + /// + public Guid IdentityUserId { get; set; } + + /// + /// 用户状态 + /// + public bool IsActive { get; set; } + + /// + /// 联系方式 + /// + public string ContactInfo { get; set; } + + /// + /// 职位 + /// + public string Position { get; set; } + + /// + /// 角色名称 + /// + public string RoleNames { get; set; } + } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserPagedAndSortedResultRequestDto.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserPagedAndSortedResultRequestDto.cs new file mode 100644 index 000000000..9c7c26ee9 --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserPagedAndSortedResultRequestDto.cs @@ -0,0 +1,14 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace PackageName.CompanyName.ProjectName.Users.Dtos +{ + [Serializable] + public class UserPagedAndSortedResultRequestDto : PagedAndSortedResultRequestDto + { + /// + /// 用户名称 + /// + public string NickName { get; set; } + } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/IUserAppService.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/IUserAppService.cs new file mode 100644 index 000000000..606d0ddc7 --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/IUserAppService.cs @@ -0,0 +1,55 @@ +using PackageName.CompanyName.ProjectName.Users.Dtos; +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace PackageName.CompanyName.ProjectName.Users +{ + /// + /// 用户应用服务接口 + /// + public interface IUserAppService : + IApplicationService + { + /// + /// 创建用户 + /// + Task CreateAsync(CreateUpdateUserDto input); + + /// + /// 更新用户 + /// + Task UpdateAsync(Guid id, CreateUpdateUserDto input); + + /// + /// 删除用户 + /// + Task DeleteAsync(Guid id); + + /// + /// 获取用户 + /// + Task GetAsync(Guid id); + + /// + /// 获取用户列表 + /// + Task> GetListAsync(UserPagedAndSortedResultRequestDto input); + + /// + /// 修改用户密码 + /// + Task ChangePasswordAsync(Guid id, string currentPassword, string newPassword); + + /// + /// 重置用户密码(管理员操作) + /// + Task ResetPasswordAsync(Guid id, string newPassword); + + /// + /// 启用或禁用用户 + /// + Task SetUserActiveStatusAsync(Guid id, bool isActive); + } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName.CompanyName.ProjectName.Application.csproj b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName.CompanyName.ProjectName.Application.csproj index b36251c2d..10f4f0ddc 100644 --- a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName.CompanyName.ProjectName.Application.csproj +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName.CompanyName.ProjectName.Application.csproj @@ -14,6 +14,7 @@ + diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/ProjectNameApplicationMapperProfile.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/ProjectNameApplicationMapperProfile.cs index 0ace9b456..2dd396079 100644 --- a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/ProjectNameApplicationMapperProfile.cs +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/ProjectNameApplicationMapperProfile.cs @@ -1,4 +1,6 @@ using AutoMapper; +using PackageName.CompanyName.ProjectName.Users; +using PackageName.CompanyName.ProjectName.Users.Dtos; namespace PackageName.CompanyName.ProjectName; @@ -6,5 +8,13 @@ public class ProjectNameApplicationMapperProfile : Profile { public ProjectNameApplicationMapperProfile() { + CreateMap() + .ForMember(d => d.IsActive, o => o.Ignore()) + .ForMember(d => d.RoleNames, o => o.Ignore()); + CreateMap() + .ForMember(d => d.IsActive, o => o.Ignore()) + .ForMember(d => d.RoleNames, o => o.Ignore()); + CreateMap(MemberList.None); + } } diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/ProjectNameApplicationModule.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/ProjectNameApplicationModule.cs index 6e1cf009f..d8704f2ac 100644 --- a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/ProjectNameApplicationModule.cs +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/ProjectNameApplicationModule.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Application; using Volo.Abp.Authorization; using Volo.Abp.AutoMapper; +using Volo.Abp.BackgroundJobs; using Volo.Abp.Modularity; namespace PackageName.CompanyName.ProjectName; @@ -12,6 +13,7 @@ namespace PackageName.CompanyName.ProjectName; typeof(AbpDddApplicationModule), typeof(ProjectNameDomainModule), typeof(ProjectNameApplicationContractsModule), + typeof(AbpBackgroundJobsModule), typeof(AbpDynamicQueryableApplicationModule))] public class ProjectNameApplicationModule : AbpModule { diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/Users/UserAppService.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/Users/UserAppService.cs new file mode 100644 index 000000000..25eef3a6e --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/Users/UserAppService.cs @@ -0,0 +1,198 @@ +using Microsoft.AspNetCore.Authorization; +using PackageName.CompanyName.ProjectName.Permissions; +using PackageName.CompanyName.ProjectName.Users.Dtos; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Identity; + +namespace PackageName.CompanyName.ProjectName.Users +{ + /// + /// 用户 + /// + public class UserAppService : ApplicationService, IUserAppService + { + private readonly IUserRepository _userRepository; + private readonly IUserManager _userManager; + private readonly IdentityUserManager _identityUserManager; + + public UserAppService( + IUserRepository userRepository, + IUserManager userManager, + IdentityUserManager identityUserManager) + { + _userRepository = userRepository; + _userManager = userManager; + _identityUserManager = identityUserManager; + } + + /// + /// 创建用户 + /// + [Authorize(ProjectNamePermissions.User.Create)] + public async Task CreateAsync(CreateUpdateUserDto input) + { + // 参数检查和验证逻辑可以在这里添加 + if (string.IsNullOrEmpty(input.NickName)) + { + throw new UserFriendlyException("昵称不能为空"); + } + if (string.IsNullOrEmpty(input.Password)) + { + throw new UserFriendlyException("密码不能为空"); + } + + // 使用UserManager创建用户 + var user = await _userManager.CreateAsync( + input.NickName, + input.Password, + input.ContactInfo, + input.Position, + input.IsActive); + + // 返回DTO对象 + return await MapToUserDtoAsync(user); + } + + /// + /// 更新用户 + /// + [Authorize(ProjectNamePermissions.User.Update)] + public async Task UpdateAsync(Guid id, CreateUpdateUserDto input) + { + // 使用UserManager更新用户基本信息 + var user = await _userManager.UpdateAsync( + id, + input.NickName, + input.Password, + input.ContactInfo, + input.Position, + input.IsActive); + + // 返回DTO对象 + return await MapToUserDtoAsync(user); + } + + /// + /// 删除用户 + /// + [Authorize(ProjectNamePermissions.User.Delete)] + public Task DeleteAsync(Guid id) + { + return _userManager.DeleteAsync(id); + } + + /// + /// 获取用户 + /// + [Authorize(ProjectNamePermissions.User.Default)] + public async Task GetAsync(Guid id) + { + var user = await _userManager.GetAsync(id); + return await MapToUserDtoAsync(user); + } + + /// + /// 获取用户列表 + /// + [Authorize(ProjectNamePermissions.User.Default)] + public async Task> GetListAsync(UserPagedAndSortedResultRequestDto input) + { + // 创建查询 + var query = await CreateFilteredQueryAsync(input); + + // 获取总记录数 + var totalCount = await AsyncExecuter.CountAsync(query); + + // 获取已排序和分页的查询结果 + var users = await AsyncExecuter.ToListAsync( + query.OrderBy(input.Sorting ?? nameof(User.NickName)) + .Skip(input.SkipCount) + .Take(input.MaxResultCount)); + + // 转换为DTO并返回 + var userDtos = new List(); + foreach (var user in users) + { + var userDto = ObjectMapper.Map(user); + + // 填充角色信息 + if (user.IdentityUser != null) + { + var roles = await _identityUserManager.GetRolesAsync(user.IdentityUser); + userDto.RoleNames = string.Join("、", roles); + userDto.IsActive = user.IdentityUser.LockoutEnd == null || user.IdentityUser.LockoutEnd < DateTimeOffset.Now; + } + + userDtos.Add(userDto); + } + + return new PagedResultDto(totalCount, userDtos); + } + + /// + /// 修改用户密码 + /// + [Authorize] + public async Task ChangePasswordAsync(Guid id, string currentPassword, string newPassword) + { + await _userManager.ChangePasswordAsync(id, currentPassword, newPassword); + } + + /// + /// 重置用户密码(管理员操作) + /// + [Authorize(ProjectNamePermissions.User.Update)] + public async Task ResetPasswordAsync(Guid id, string newPassword) + { + await _userManager.ResetPasswordAsync(id, newPassword); + } + + /// + /// 启用或禁用用户 + /// + [Authorize(ProjectNamePermissions.User.Update)] + public async Task SetUserActiveStatusAsync(Guid id, bool isActive) + { + await _userManager.SetUserActiveStatusAsync(id, isActive); + } + + /// + /// 创建基础查询,应用过滤条件 + /// + protected async virtual Task> CreateFilteredQueryAsync( + UserPagedAndSortedResultRequestDto input) + { + // 获取基础查询,并加载相关实体 + var query = await _userRepository.WithDetailsAsync( + x => x.IdentityUser); + + // 应用过滤条件 + return query + .WhereIf(!string.IsNullOrWhiteSpace(input.NickName), + x => x.NickName.Contains(input.NickName)); + } + + /// + /// 将User实体映射为UserDto,并填充权限信息 + /// + private Task MapToUserDtoAsync(User user) + { + var userDto = ObjectMapper.Map(user); + + // 设置用户状态 + if (user.IdentityUser != null) + { + userDto.IsActive = user.IdentityUser.LockoutEnd == null || user.IdentityUser.LockoutEnd < DateTimeOffset.Now; + } + + return Task.FromResult(userDto); + } + } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName.CompanyName.ProjectName.Domain.csproj b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName.CompanyName.ProjectName.Domain.csproj index 160b74c5c..4158f0a15 100644 --- a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName.CompanyName.ProjectName.Domain.csproj +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName.CompanyName.ProjectName.Domain.csproj @@ -14,10 +14,14 @@ + + + + diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/ProjectNameDomainModule.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/ProjectNameDomainModule.cs index 401aa849e..6ca9ae998 100644 --- a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/ProjectNameDomainModule.cs +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/ProjectNameDomainModule.cs @@ -1,7 +1,9 @@ using LINGYUN.Abp.DataProtection; +using LINGYUN.Abp.Identity; using Microsoft.Extensions.DependencyInjection; using PackageName.CompanyName.ProjectName.ObjectExtending; using Volo.Abp.AutoMapper; +using Volo.Abp.BackgroundWorkers.Hangfire; using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.Modularity; using Volo.Abp.ObjectExtending.Modularity; @@ -12,6 +14,8 @@ namespace PackageName.CompanyName.ProjectName; [DependsOn( typeof(AbpAutoMapperModule), typeof(AbpDataProtectionModule), + typeof(AbpIdentityDomainModule), + typeof(AbpBackgroundWorkersHangfireModule), typeof(ProjectNameDomainSharedModule))] public class ProjectNameDomainModule : AbpModule { diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/IHaveTreeCode.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/IHaveTreeCode.cs new file mode 100644 index 000000000..f1641be31 --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/IHaveTreeCode.cs @@ -0,0 +1,21 @@ +using System; +using Volo.Abp.Auditing; + +namespace PackageName.CompanyName.ProjectName.TreeCodes +{ + /// + /// 定义具有树形编码的实体接口 + /// + public interface IHaveTreeCode : IHasCreationTime + { + /// + /// 树形编码 + /// + string TreeCode { get; set; } + + /// + /// 父级Id + /// + Guid? ParentId { get; set; } + } +} diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/ITreeCodeGenerator.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/ITreeCodeGenerator.cs new file mode 100644 index 000000000..0c3ce0754 --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/ITreeCodeGenerator.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; + +namespace PackageName.CompanyName.ProjectName.TreeCodes +{ + /// + /// 树形编码生成器接口 + /// + public interface ITreeCodeGenerator + { + /// + /// 生成树形编码 + /// + /// 实体类型 + /// 仓储 + /// 父级Id + /// 生成的树形编码 + Task GenerateAsync( + IRepository repository, + Guid? parentId) + where TEntity : class, IEntity, IHaveTreeCode; + + /// + /// 更新节点及其所有子节点的TreeCode + /// + Task UpdateTreeCodesAsync( + IRepository repository, + Guid entityId) + where TEntity : class, IEntity, IHaveTreeCode; + } +} diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/TreeCodeGenerator.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/TreeCodeGenerator.cs new file mode 100644 index 000000000..2b08bc0f5 --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/TreeCodeGenerator.cs @@ -0,0 +1,114 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; + +namespace PackageName.CompanyName.ProjectName.TreeCodes +{ + /// + /// 树形编码生成器 + /// + public class TreeCodeGenerator : ITreeCodeGenerator, ISingletonDependency + { + /// + /// 生成树形编码 + /// + /// 实体类型 + /// 仓储 + /// 父级Id + /// 生成的树形编码 + public async virtual Task GenerateAsync( + IRepository repository, + Guid? parentId) + where TEntity : class, IEntity, IHaveTreeCode + { + if (!parentId.HasValue) + { + // 生成根节点编码 + return await GenerateRootCodeAsync(repository); + } + else + { + // 生成子节点编码 + return await GenerateChildCodeAsync(repository, parentId.Value); + } + } + + private async Task GenerateRootCodeAsync( + IRepository repository) + where TEntity : class, IEntity, IHaveTreeCode + { + var query = await repository.GetQueryableAsync(); + query = query.Where(e => e.ParentId == null).OrderByDescending(e => e.CreationTime); + var maxCodeEntity = await repository.AsyncExecuter.FirstOrDefaultAsync(query); + + if (maxCodeEntity == null) + { + return "0001"; + } + + int maxCode = int.Parse(maxCodeEntity.TreeCode.Split('.').LastOrDefault("0")); + return (maxCode + 1).ToString("D4"); + } + + private async Task GenerateChildCodeAsync( + IRepository repository, + Guid parentId) + where TEntity : class, IEntity, IHaveTreeCode + { + var parent = await repository.GetAsync(parentId); + if (parent == null) + { + throw new EntityNotFoundException(typeof(TEntity), parentId); + } + + var query = await repository.GetQueryableAsync(); + query = query.Where(e => e.ParentId == parentId).OrderByDescending(e => e.CreationTime); + var maxCodeEntity = await repository.AsyncExecuter.FirstOrDefaultAsync(query); + + string newCode; + if (maxCodeEntity == null) + { + newCode = "0001"; + } + else + { + int maxCode = int.Parse(maxCodeEntity.TreeCode.Split('.').Last()); + newCode = (maxCode + 1).ToString("D4"); + } + + // 构建完整的TreeCode: 父TreeCode.新编码 + return $"{parent.TreeCode}.{newCode}"; + } + + /// + /// 更新节点及其所有子节点的TreeCode + /// + public async virtual Task UpdateTreeCodesAsync( + IRepository repository, + Guid entityId) + where TEntity : class, IEntity, IHaveTreeCode + { + var entity = await repository.GetAsync(entityId); + var query = await repository.GetQueryableAsync(); + var children = await repository.AsyncExecuter.ToListAsync( + query.Where(e => e.ParentId == entityId)); + + foreach (var child in children) + { + // 获取子节点编码(TreeCode最后一部分)或生成新编码 + string childCode = child.TreeCode.Contains('.') + ? child.TreeCode.Split('.').Last() + : child.TreeCode; + + child.TreeCode = $"{entity.TreeCode}.{childCode}"; + await repository.UpdateAsync(child); + + // 递归更新子节点 + await UpdateTreeCodesAsync(repository, child.Id); + } + } + } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/IUserManager.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/IUserManager.cs new file mode 100644 index 000000000..7d15b7792 --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/IUserManager.cs @@ -0,0 +1,69 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Domain.Services; + +namespace PackageName.CompanyName.ProjectName.Users +{ + /// + /// 用户管理服务接口 + /// + public interface IUserManager : IDomainService + { + /// + /// 创建新用户 + /// + Task CreateAsync( + string nickName, + string password, + string contactInfo = null, + string position = null, + bool isActive = true + ); + + /// + /// 更新用户信息 + /// + Task UpdateAsync( + Guid id, + string nickName, + string password, + string contactInfo = null, + string position = null, + bool isActive = true); + + /// + /// 删除用户 + /// + Task DeleteAsync(Guid id); + + /// + /// 修改用户密码 + /// + Task ChangePasswordAsync(Guid id, string currentPassword, string newPassword); + + /// + /// 重置用户密码 + /// + Task ResetPasswordAsync(Guid id, string newPassword); + + /// + /// 获取用户信息 + /// + Task GetAsync(Guid id); + + /// + /// 根据Identity用户ID获取用户 + /// + Task FindByIdentityUserIdAsync(Guid identityUserId); + + /// + /// 根据用户昵称查找用户 + /// + Task FindByNickNameAsync(string nickName); + + /// + /// 禁用或启用用户 + /// + Task SetUserActiveStatusAsync(Guid id, bool isActive); + } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/IUserRepository.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/IUserRepository.cs new file mode 100644 index 000000000..75fac813b --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/IUserRepository.cs @@ -0,0 +1,9 @@ +using System; +using Volo.Abp.Domain.Repositories; + +namespace PackageName.CompanyName.ProjectName.Users +{ + public interface IUserRepository : IRepository + { + } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/User.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/User.cs new file mode 100644 index 000000000..855fb5116 --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/User.cs @@ -0,0 +1,59 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.Identity; + +namespace PackageName.CompanyName.ProjectName.Users +{ + /// + /// 用户实体 + /// + public class User : FullAuditedEntity + { + /// + /// 用户名称 + /// + [MaxLength(50)] + public string NickName { get; set; } + + /// + /// Identity用户Id + /// + public Guid IdentityUserId { get; set; } + + /// + /// Identity用户 + /// + public virtual IdentityUser IdentityUser { get; set; } + + /// + /// 联系方式 + /// + [MaxLength(50)] + public string ContactInfo { get; set; } + + /// + /// 职位 + /// + [MaxLength(50)] + public string Position { get; set; } + + protected User() + { + } + + public User( + Guid id, + string nickName, + Guid identityUserId, + string contactInfo = null, + string position = null + ) : base(id) + { + NickName = nickName; + IdentityUserId = identityUserId; + ContactInfo = contactInfo; + Position = position; + } + } +} diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/UserManager.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/UserManager.cs new file mode 100644 index 000000000..bd616ba34 --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/UserManager.cs @@ -0,0 +1,406 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Domain.Services; +using Volo.Abp.Identity; + +namespace PackageName.CompanyName.ProjectName.Users +{ + /// + /// 用户管理服务,用于处理用户的CRUD操作 + /// + public class UserManager : DomainService, IUserManager + { + private readonly IUserRepository _userRepository; + private readonly IdentityUserManager _identityUserManager; + private readonly ILogger _logger; + + public UserManager( + IUserRepository userRepository, + IdentityUserManager identityUserManager, + ILogger logger) + { + _userRepository = userRepository; + _identityUserManager = identityUserManager; + _logger = logger; + } + + /// + /// 创建新用户 + /// + /// 用户昵称 + /// 用户密码 + /// 联系方式 + /// 职位 + /// 是否启用 + /// 创建的用户实体 + public async Task CreateAsync( + string nickName, + string password, + string contactInfo = null, + string position = null, + bool isActive = true) + { + // 参数验证 + if (string.IsNullOrWhiteSpace(nickName)) + { + throw new UserFriendlyException("用户名不能为空"); + } + + // 密码校验 + if (string.IsNullOrWhiteSpace(password) || password.Length < 6) + { + throw new UserFriendlyException("密码不能为空且长度不能少于6位"); + } + + // 检查用户昵称是否已存在 + var existingUser = await _userRepository.FindAsync(u => u.NickName == nickName); + if (existingUser != null) + { + throw new UserFriendlyException($"昵称为 '{nickName}' 的用户已存在"); + } + + // 创建Identity用户 + var identityUser = new IdentityUser(GuidGenerator.Create(), nickName, $"{nickName}@inspection.com"); + var identityResult = await _identityUserManager.CreateAsync(identityUser, password); + if (!identityResult.Succeeded) + { + throw new UserFriendlyException("创建用户失败: " + + string.Join(", ", identityResult.Errors.Select(x => x.Description))); + } + + // 设置用户状态 + if (!isActive) + { + var lockoutResult = await _identityUserManager.SetLockoutEndDateAsync(identityUser, DateTimeOffset.MaxValue); + if (!lockoutResult.Succeeded) + { + throw new UserFriendlyException("设置用户状态失败: " + + string.Join(", ", lockoutResult.Errors.Select(e => e.Description))); + } + } + + // 创建业务用户 + var user = new User( + GuidGenerator.Create(), + nickName, + identityUser.Id, + contactInfo, + position + ); + + // 保存用户 + await _userRepository.InsertAsync(user, true); + + _logger.LogInformation($"创建了新用户:{nickName},ID:{user.Id}"); + + return user; + } + + /// + /// 更新用户信息 + /// + /// 用户ID + /// 用户昵称 + /// 用户密码(可选,如不修改则传入null) + /// 联系方式 + /// 职位 + /// 是否启用 + /// 更新后的用户实体 + public async Task UpdateAsync( + Guid id, + string nickName, + string password, + string contactInfo = null, + string position = null, + bool isActive = true) + { + // 最大重试次数 + const int maxRetries = 3; + int retryCount = 0; + + while (true) + { + try + { + // 每次尝试时重新获取最新的用户数据 + var user = await _userRepository.GetAsync(id, false); + if (user == null) + { + throw new UserFriendlyException("用户不存在"); + } + + // 检查用户昵称是否已被其他用户使用 + var existingUser = await _userRepository.FindAsync(u => u.NickName == nickName && u.Id != id); + if (existingUser != null) + { + throw new UserFriendlyException($"昵称为 '{nickName}' 的用户已存在"); + } + + // 更新Identity用户 + var identityUser = await _identityUserManager.FindByIdAsync(user.IdentityUserId.ToString()); + if (identityUser == null) + { + throw new UserFriendlyException("Identity用户不存在"); + } + + // 更新用户名 + var usernameResult = await _identityUserManager.SetUserNameAsync(identityUser, nickName); + if (!usernameResult.Succeeded) + { + throw new UserFriendlyException("更新用户名失败: " + + string.Join(", ", usernameResult.Errors.Select(e => e.Description))); + } + + // 更新电子邮件 + var emailResult = await _identityUserManager.SetEmailAsync(identityUser, $"{nickName}@inspection.com"); + if (!emailResult.Succeeded) + { + throw new UserFriendlyException("更新电子邮件失败: " + + string.Join(", ", emailResult.Errors.Select(e => e.Description))); + } + + // 如果提供了新密码,则更新密码 + if (!string.IsNullOrEmpty(password)) + { + // 移除当前密码 + await _identityUserManager.RemovePasswordAsync(identityUser); + // 设置新密码 + var passwordResult = await _identityUserManager.AddPasswordAsync(identityUser, password); + if (!passwordResult.Succeeded) + { + throw new UserFriendlyException("更新密码失败: " + + string.Join(", ", passwordResult.Errors.Select(e => e.Description))); + } + } + + // 设置用户状态 + await SetUserActiveStatusAsync(id, isActive); + + // 更新用户信息 + user.NickName = nickName; + user.ContactInfo = contactInfo; + user.Position = position; + + // 保存更新 + await _userRepository.UpdateAsync(user, true); + + _logger.LogInformation($"更新了用户信息:{nickName},ID:{user.Id}"); + + return user; + } + catch (Volo.Abp.Data.AbpDbConcurrencyException ex) + { + // 增加重试计数 + retryCount++; + + // 如果达到最大重试次数,则抛出用户友好的异常 + if (retryCount >= maxRetries) + { + throw new UserFriendlyException( + "更新用户信息失败:数据已被其他用户修改。请刷新页面后重试。", + "409", ex.Message, + ex); + } + + // 短暂延迟后重试 + await Task.Delay(100 * retryCount); // 逐步增加延迟时间 + + // 记录重试信息 + _logger.LogWarning($"检测到用户[{id}]更新时发生并发冲突,正在进行第{retryCount}次重试..."); + } + } + } + + /// + /// 删除用户 + /// + /// 用户ID + /// 操作任务 + public async Task DeleteAsync(Guid id) + { + // 获取用户 + var user = await _userRepository.GetAsync(id); + if (user == null) + { + throw new UserFriendlyException("用户不存在"); + } + + // 删除Identity用户 + var identityUser = await _identityUserManager.FindByIdAsync(user.IdentityUserId.ToString()); + if (identityUser != null) + { + var result = await _identityUserManager.DeleteAsync(identityUser); + if (!result.Succeeded) + { + throw new UserFriendlyException("删除Identity用户失败: " + + string.Join(", ", result.Errors.Select(e => e.Description))); + } + } + + // 删除用户 + await _userRepository.DeleteAsync(user); + + _logger.LogInformation($"删除了用户,ID:{id}"); + } + + /// + /// 修改用户密码 + /// + /// 用户ID + /// 当前密码 + /// 新密码 + /// 操作结果 + public async Task ChangePasswordAsync(Guid id, string currentPassword, string newPassword) + { + if (string.IsNullOrEmpty(newPassword) || newPassword.Length < 6) + { + throw new UserFriendlyException("新密码不能为空且长度不能少于6位"); + } + + // 获取用户 + var user = await _userRepository.GetAsync(id); + if (user == null) + { + throw new UserFriendlyException("用户不存在"); + } + + // 获取Identity用户 + var identityUser = await _identityUserManager.FindByIdAsync(user.IdentityUserId.ToString()); + if (identityUser == null) + { + throw new UserFriendlyException("Identity用户不存在"); + } + + // 修改密码 + var result = await _identityUserManager.ChangePasswordAsync(identityUser, currentPassword, newPassword); + if (!result.Succeeded) + { + throw new UserFriendlyException("修改密码失败: " + + string.Join(", ", result.Errors.Select(e => e.Description))); + } + + _logger.LogInformation($"用户[{user.NickName}]成功修改了密码"); + } + + /// + /// 重置用户密码(管理员操作) + /// + /// 用户ID + /// 新密码 + /// 操作结果 + public async Task ResetPasswordAsync(Guid id, string newPassword) + { + if (string.IsNullOrEmpty(newPassword) || newPassword.Length < 6) + { + throw new UserFriendlyException("新密码不能为空且长度不能少于6位"); + } + + // 获取用户 + var user = await _userRepository.GetAsync(id); + if (user == null) + { + throw new UserFriendlyException("用户不存在"); + } + + // 获取Identity用户 + var identityUser = await _identityUserManager.FindByIdAsync(user.IdentityUserId.ToString()); + if (identityUser == null) + { + throw new UserFriendlyException("Identity用户不存在"); + } + + // 生成重置令牌 + var token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityUser); + + // 重置密码 + var result = await _identityUserManager.ResetPasswordAsync(identityUser, token, newPassword); + if (!result.Succeeded) + { + throw new UserFriendlyException("重置密码失败: " + + string.Join(", ", result.Errors.Select(e => e.Description))); + } + + _logger.LogInformation($"管理员重置了用户[{user.NickName}]的密码"); + } + + /// + /// 获取用户信息 + /// + /// 用户ID + /// 用户实体 + public async Task GetAsync(Guid id) + { + return await _userRepository.GetAsync(id); + } + + /// + /// 根据Identity用户ID获取用户 + /// + /// Identity用户ID + /// 用户实体,如果不存在则返回null + public async Task FindByIdentityUserIdAsync(Guid identityUserId) + { + return await _userRepository.FindAsync(u => u.IdentityUserId == identityUserId); + } + + /// + /// 根据用户昵称查找用户 + /// + /// 用户昵称 + /// 用户实体,如果不存在则返回null + public async Task FindByNickNameAsync(string nickName) + { + return await _userRepository.FindAsync(u => u.NickName == nickName); + } + + /// + /// 禁用或启用用户 + /// + /// 用户ID + /// 是否启用 + /// 操作任务 + public async Task SetUserActiveStatusAsync(Guid id, bool isActive) + { + // 获取用户 + var user = await _userRepository.GetAsync(id); + if (user == null) + { + throw new UserFriendlyException("用户不存在"); + } + + // 获取Identity用户 + var identityUser = await _identityUserManager.FindByIdAsync(user.IdentityUserId.ToString()); + if (identityUser == null) + { + throw new UserFriendlyException("Identity用户不存在"); + } + + // 设置用户状态 + if (isActive) + { + // 启用用户 + var result = await _identityUserManager.SetLockoutEndDateAsync(identityUser, null); + if (!result.Succeeded) + { + throw new UserFriendlyException($"启用用户失败: " + + string.Join(", ", result.Errors.Select(e => e.Description))); + } + } + else + { + // 禁用用户(设置永久锁定) + var result = await _identityUserManager.SetLockoutEndDateAsync(identityUser, DateTimeOffset.MaxValue); + if (!result.Succeeded) + { + throw new UserFriendlyException($"禁用用户失败: " + + string.Join(", ", result.Errors.Select(e => e.Description))); + } + } + + _logger.LogInformation($"已{(isActive ? "启用" : "禁用")}用户[{user.NickName}]"); + } + } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName.CompanyName.ProjectName.EntityFrameworkCore.csproj b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName.CompanyName.ProjectName.EntityFrameworkCore.csproj index a7904db7d..ada1145a0 100644 --- a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName.CompanyName.ProjectName.EntityFrameworkCore.csproj +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName.CompanyName.ProjectName.EntityFrameworkCore.csproj @@ -28,6 +28,7 @@ + diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/IProjectNameDbContext.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/IProjectNameDbContext.cs index c39c2923e..f3749036f 100644 --- a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/IProjectNameDbContext.cs +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/IProjectNameDbContext.cs @@ -1,4 +1,6 @@ -using Volo.Abp.Data; +using Microsoft.EntityFrameworkCore; +using PackageName.CompanyName.ProjectName.Users; +using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; namespace PackageName.CompanyName.ProjectName.EntityFrameworkCore; @@ -6,4 +8,5 @@ namespace PackageName.CompanyName.ProjectName.EntityFrameworkCore; [ConnectionStringName(ProjectNameDbProperties.ConnectionStringName)] public interface IProjectNameDbContext : IEfCoreDbContext { + DbSet Users { get; } } diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameDbContext.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameDbContext.cs index 5cb2c015b..988271a16 100644 --- a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameDbContext.cs +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameDbContext.cs @@ -1,12 +1,16 @@ using LINGYUN.Abp.DataProtection.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; +using PackageName.CompanyName.ProjectName.Users; using Volo.Abp.Data; +using Volo.Abp.Identity.EntityFrameworkCore; namespace PackageName.CompanyName.ProjectName.EntityFrameworkCore; [ConnectionStringName(ProjectNameDbProperties.ConnectionStringName)] public class ProjectNameDbContext : AbpDataProtectionDbContext, IProjectNameDbContext { + public virtual DbSet Users { get; set; } + public ProjectNameDbContext( DbContextOptions options) : base(options) { @@ -14,8 +18,9 @@ public class ProjectNameDbContext : AbpDataProtectionDbContext(b => + { + b.ToTable(ProjectNameDbProperties.DbTablePrefix + "Users", ProjectNameDbProperties.DbSchema); + b.ConfigureByConvention(); + b.Property(x => x.NickName).HasComment("用户名称"); + b.Property(x => x.IdentityUserId).HasComment("Identity用户Id"); + + // 用户与IdentityUser的关系(一对一) + b.HasOne(x => x.IdentityUser) + .WithOne() + .HasForeignKey(u => u.IdentityUserId) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); + }); } } diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameEntityFrameworkCoreModule.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameEntityFrameworkCoreModule.cs index b18f43183..aac635d62 100644 --- a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameEntityFrameworkCoreModule.cs +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameEntityFrameworkCoreModule.cs @@ -2,7 +2,9 @@ using LINGYUN.Abp.DataProtection.EntityFrameworkCore; using LINGYUN.Abp.Saas.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using PackageName.CompanyName.ProjectName.Users; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Identity.EntityFrameworkCore; using Volo.Abp.Modularity; #if MySQL using Volo.Abp.EntityFrameworkCore.MySQL; @@ -38,6 +40,7 @@ namespace PackageName.CompanyName.ProjectName.EntityFrameworkCore; #elif PostgreSql typeof(AbpEntityFrameworkCorePostgreSqlModule), #endif + typeof(AbpIdentityEntityFrameworkCoreModule), typeof(AbpSaasEntityFrameworkCoreModule))] public class ProjectNameEntityFrameworkCoreModule : AbpModule { @@ -70,7 +73,8 @@ public class ProjectNameEntityFrameworkCoreModule : AbpModule context.Services.AddAbpDbContext(options => { - options.AddDefaultRepositories(); + options.AddDefaultRepositories(true); + options.AddRepository(); }); } } diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/Users/UserRepository.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/Users/UserRepository.cs new file mode 100644 index 000000000..72d1575bf --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/Users/UserRepository.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore; +using PackageName.CompanyName.ProjectName.EntityFrameworkCore; +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace PackageName.CompanyName.ProjectName.Users +{ + public class UserRepository : EfCoreRepository, IUserRepository + { + public UserRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public override async Task> WithDetailsAsync() + { + return (await GetDbSetAsync()).Include(x => x.IdentityUser); + } + } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.HttpApi/PackageName/CompanyName/ProjectName/Users/UserController.cs b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.HttpApi/PackageName/CompanyName/ProjectName/Users/UserController.cs new file mode 100644 index 000000000..85d3f28f9 --- /dev/null +++ b/aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.HttpApi/PackageName/CompanyName/ProjectName/Users/UserController.cs @@ -0,0 +1,134 @@ +using Microsoft.AspNetCore.Mvc; +using PackageName.CompanyName.ProjectName.Users.Dtos; +using System; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; + +namespace PackageName.CompanyName.ProjectName.Users +{ + /// + /// 用户管理控制器 + /// + [RemoteService] + [Route("api/app/user")] + public class UserController : ProjectNameControllerBase + { + private readonly IUserAppService _userAppService; + + public UserController(IUserAppService userAppService) + { + _userAppService = userAppService; + } + + /// + /// 创建用户 + /// + [HttpPost] + public async Task CreateAsync(CreateUpdateUserDto input) + { + return await _userAppService.CreateAsync(input); + } + + /// + /// 更新用户 + /// + [HttpPut("{id}")] + public async Task UpdateAsync(Guid id, CreateUpdateUserDto input) + { + return await _userAppService.UpdateAsync(id, input); + } + + /// + /// 删除用户 + /// + [HttpDelete("{id}")] + public async Task DeleteAsync(Guid id) + { + await _userAppService.DeleteAsync(id); + } + + /// + /// 获取用户 + /// + [HttpGet("{id}")] + public async Task GetAsync(Guid id) + { + return await _userAppService.GetAsync(id); + } + + /// + /// 获取用户列表 + /// + [HttpGet] + public async Task> GetListAsync(UserPagedAndSortedResultRequestDto input) + { + return await _userAppService.GetListAsync(input); + } + + /// + /// 修改用户密码 + /// + [HttpPost("{id}/change-password")] + public async Task ChangePasswordAsync(Guid id, [FromBody] ChangePasswordRequest request) + { + await _userAppService.ChangePasswordAsync(id, request.CurrentPassword, request.NewPassword); + } + + /// + /// 重置用户密码(管理员操作) + /// + [HttpPost("{id}/reset-password")] + public async Task ResetPasswordAsync(Guid id, [FromBody] ResetPasswordRequest request) + { + await _userAppService.ResetPasswordAsync(id, request.NewPassword); + } + + /// + /// 启用或禁用用户 + /// + [HttpPost("{id}/set-active")] + public async Task SetUserActiveStatusAsync(Guid id, [FromBody] SetUserActiveRequest request) + { + await _userAppService.SetUserActiveStatusAsync(id, request.IsActive); + } + } + + /// + /// 修改密码请求 + /// + public class ChangePasswordRequest + { + /// + /// 当前密码 + /// + public string CurrentPassword { get; set; } + + /// + /// 新密码 + /// + public string NewPassword { get; set; } + } + + /// + /// 重置密码请求 + /// + public class ResetPasswordRequest + { + /// + /// 新密码 + /// + public string NewPassword { get; set; } + } + + /// + /// 设置用户状态请求 + /// + public class SetUserActiveRequest + { + /// + /// 是否启用 + /// + public bool IsActive { get; set; } + } +}