Browse Source

feat(user): 添加用户管理功能,包括创建、更新、删除、查询用户等功能

pull/1149/head
feijie 11 months ago
parent
commit
bc2e5e3b7b
  1. 15
      aspnet-core/templates/aio/content/migrations/PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore/DataSeeder/IProjectNameDataSeeder.cs
  2. 503
      aspnet-core/templates/aio/content/migrations/PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore/DataSeeder/ProjectNameDataSeederDataSeeder.cs
  3. 6
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Permissions/ProjectNamePermissionDefinitionProvider.cs
  4. 8
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Permissions/ProjectNamePermissions.cs
  5. 37
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/CreateUpdateUserDto.cs
  6. 39
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserDto.cs
  7. 39
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserItemDto.cs
  8. 14
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/Dtos/UserPagedAndSortedResultRequestDto.cs
  9. 55
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application.Contracts/PackageName/CompanyName/ProjectName/Users/IUserAppService.cs
  10. 1
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName.CompanyName.ProjectName.Application.csproj
  11. 10
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/ProjectNameApplicationMapperProfile.cs
  12. 2
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/ProjectNameApplicationModule.cs
  13. 198
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName/CompanyName/ProjectName/Users/UserAppService.cs
  14. 4
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName.CompanyName.ProjectName.Domain.csproj
  15. 4
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/ProjectNameDomainModule.cs
  16. 21
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/IHaveTreeCode.cs
  17. 33
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/ITreeCodeGenerator.cs
  18. 114
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/TreeCodes/TreeCodeGenerator.cs
  19. 69
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/IUserManager.cs
  20. 9
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/IUserRepository.cs
  21. 59
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/User.cs
  22. 406
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName/CompanyName/ProjectName/Users/UserManager.cs
  23. 1
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName.CompanyName.ProjectName.EntityFrameworkCore.csproj
  24. 5
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/IProjectNameDbContext.cs
  25. 9
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameDbContext.cs
  26. 17
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameDbContextModelCreatingExtensions.cs
  27. 6
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameEntityFrameworkCoreModule.cs
  28. 22
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/Users/UserRepository.cs
  29. 134
      aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.HttpApi/PackageName/CompanyName/ProjectName/Users/UserController.cs

15
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
{
/// <summary>
/// 初始化数据
/// </summary>
/// <param name="context">数据种子上下文</param>
/// <returns>任务</returns>
Task SeedAsync(DataSeedContext context);
}
}

503
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<ProjectNameDataSeeder> _logger;
private readonly IRepository<Org, Guid> _orgRepository;
private readonly IRepository<WorkUnit, Guid> _workUnitRepository;
private readonly IRepository<User, Guid> _userRepository;
private readonly IdentityUserManager _identityUserManager;
private readonly IdentityRoleManager _identityRoleManager;
private readonly IUnitOfWorkManager _unitOfWorkManager;
/// <summary>
/// 构造函数
/// </summary>
public ProjectNameDataSeeder(
ICurrentTenant currentTenant,
IGuidGenerator guidGenerator,
ILogger<ProjectNameDataSeeder> logger,
IRepository<Org, Guid> orgRepository,
IRepository<WorkUnit, Guid> workUnitRepository,
IRepository<User, Guid> 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;
}
/// <summary>
/// 初始化数据
/// </summary>
/// <param name="context">数据种子上下文</param>
/// <returns>任务</returns>
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("巡检数据初始化完成");
}
}
/// <summary>
/// 初始化角色数据
/// </summary>
private async Task SeedRolesAsync()
{
// 超级管理员
await CreateRoleIfNotExistsAsync(
"超级管理员",
"系统超级管理员,拥有所有权限");
// 区委办公室-督查室管理员
await CreateRoleIfNotExistsAsync(
"区委办公室-督查室管理员",
"区委办公室督查室管理员");
// 责任领导秘书
await CreateRoleIfNotExistsAsync(
"责任领导秘书",
"负责协助责任领导进行工作");
// 责任单位管理员
await CreateRoleIfNotExistsAsync(
"责任单位管理员",
"负责管理责任单位的信息");
// 责任单位落实人员
await CreateRoleIfNotExistsAsync(
"责任单位落实人员",
"负责执行责任单位的任务");
// 责任单位分管领导
await CreateRoleIfNotExistsAsync(
"责任单位分管领导",
"负责管理责任单位的部分工作");
// 责任单位党组书记
await CreateRoleIfNotExistsAsync(
"责任单位党组书记",
"负责责任单位的党组工作");
}
/// <summary>
/// 创建角色(如果不存在)
/// </summary>
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}");
}
}
/// <summary>
/// 初始化单位数据
/// </summary>
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);
}
/// <summary>
/// 创建单位(如果不存在)
/// </summary>
private async Task<WorkUnit> 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;
}
/// <summary>
/// 初始化组织数据
/// </summary>
private async Task SeedOrgsAsync()
{
// 创建区委办公室-督查室
var supervisionOffice = await CreateOrgIfNotExistsAsync(
"区委办公室-督查室",
"001",
"001",
"区委办公室督查室");
}
/// <summary>
/// 创建组织(如果不存在)
/// </summary>
private async Task<Org> 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;
}
/// <summary>
/// 初始化用户数据
/// </summary>
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, "超级管理员");
}
/// <summary>
/// 创建用户(如果不存在)
/// </summary>
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}");
}
}
}
}
}

6
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)

8
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";
}
}

37
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
{
/// <summary>
/// 用户名称
/// </summary>
[Required(ErrorMessage = "用户名称不能为空")]
[StringLength(50, ErrorMessage = "用户名称长度不能超过50个字符")]
public string NickName { get; set; }
/// <summary>
/// 密码
/// </summary>
[StringLength(20, MinimumLength = 6, ErrorMessage = "密码长度必须在6-20个字符之间")]
public string Password { get; set; }
/// <summary>
/// 联系方式
/// </summary>
public string ContactInfo { get; set; }
/// <summary>
/// 职位
/// </summary>
public string Position { get; set; }
/// <summary>
/// 是否启用
/// </summary>
public bool IsActive { get; set; } = true;
}
}

39
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<Guid>
{
/// <summary>
/// 用户名称
/// </summary>
public string NickName { get; set; }
/// <summary>
/// Identity用户Id
/// </summary>
public Guid IdentityUserId { get; set; }
/// <summary>
/// 用户状态
/// </summary>
public bool IsActive { get; set; }
/// <summary>
/// 联系方式
/// </summary>
public string ContactInfo { get; set; }
/// <summary>
/// 职位
/// </summary>
public string Position { get; set; }
/// <summary>
/// 角色名称
/// </summary>
public string RoleNames { get; set; }
}
}

39
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<Guid>
{
/// <summary>
/// 用户名称
/// </summary>
public string NickName { get; set; }
/// <summary>
/// Identity用户Id
/// </summary>
public Guid IdentityUserId { get; set; }
/// <summary>
/// 用户状态
/// </summary>
public bool IsActive { get; set; }
/// <summary>
/// 联系方式
/// </summary>
public string ContactInfo { get; set; }
/// <summary>
/// 职位
/// </summary>
public string Position { get; set; }
/// <summary>
/// 角色名称
/// </summary>
public string RoleNames { get; set; }
}
}

14
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
{
/// <summary>
/// 用户名称
/// </summary>
public string NickName { get; set; }
}
}

55
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
{
/// <summary>
/// 用户应用服务接口
/// </summary>
public interface IUserAppService :
IApplicationService
{
/// <summary>
/// 创建用户
/// </summary>
Task<UserDto> CreateAsync(CreateUpdateUserDto input);
/// <summary>
/// 更新用户
/// </summary>
Task<UserDto> UpdateAsync(Guid id, CreateUpdateUserDto input);
/// <summary>
/// 删除用户
/// </summary>
Task DeleteAsync(Guid id);
/// <summary>
/// 获取用户
/// </summary>
Task<UserDto> GetAsync(Guid id);
/// <summary>
/// 获取用户列表
/// </summary>
Task<PagedResultDto<UserItemDto>> GetListAsync(UserPagedAndSortedResultRequestDto input);
/// <summary>
/// 修改用户密码
/// </summary>
Task ChangePasswordAsync(Guid id, string currentPassword, string newPassword);
/// <summary>
/// 重置用户密码(管理员操作)
/// </summary>
Task ResetPasswordAsync(Guid id, string newPassword);
/// <summary>
/// 启用或禁用用户
/// </summary>
Task SetUserActiveStatusAsync(Guid id, bool isActive);
}
}

1
aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Application/PackageName.CompanyName.ProjectName.Application.csproj

@ -14,6 +14,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.BackgroundJobs" />
<PackageReference Include="Volo.Abp.Ddd.Application" />
<PackageReference Include="LINGYUN.Abp.Dynamic.Queryable.Application" />
</ItemGroup>

10
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<User, UserDto>()
.ForMember(d => d.IsActive, o => o.Ignore())
.ForMember(d => d.RoleNames, o => o.Ignore());
CreateMap<User, UserItemDto>()
.ForMember(d => d.IsActive, o => o.Ignore())
.ForMember(d => d.RoleNames, o => o.Ignore());
CreateMap<CreateUpdateUserDto, User>(MemberList.None);
}
}

2
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
{

198
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
{
/// <summary>
/// 用户
/// </summary>
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;
}
/// <summary>
/// 创建用户
/// </summary>
[Authorize(ProjectNamePermissions.User.Create)]
public async Task<UserDto> 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);
}
/// <summary>
/// 更新用户
/// </summary>
[Authorize(ProjectNamePermissions.User.Update)]
public async Task<UserDto> 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);
}
/// <summary>
/// 删除用户
/// </summary>
[Authorize(ProjectNamePermissions.User.Delete)]
public Task DeleteAsync(Guid id)
{
return _userManager.DeleteAsync(id);
}
/// <summary>
/// 获取用户
/// </summary>
[Authorize(ProjectNamePermissions.User.Default)]
public async Task<UserDto> GetAsync(Guid id)
{
var user = await _userManager.GetAsync(id);
return await MapToUserDtoAsync(user);
}
/// <summary>
/// 获取用户列表
/// </summary>
[Authorize(ProjectNamePermissions.User.Default)]
public async Task<PagedResultDto<UserItemDto>> 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<UserItemDto>();
foreach (var user in users)
{
var userDto = ObjectMapper.Map<User, UserItemDto>(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<UserItemDto>(totalCount, userDtos);
}
/// <summary>
/// 修改用户密码
/// </summary>
[Authorize]
public async Task ChangePasswordAsync(Guid id, string currentPassword, string newPassword)
{
await _userManager.ChangePasswordAsync(id, currentPassword, newPassword);
}
/// <summary>
/// 重置用户密码(管理员操作)
/// </summary>
[Authorize(ProjectNamePermissions.User.Update)]
public async Task ResetPasswordAsync(Guid id, string newPassword)
{
await _userManager.ResetPasswordAsync(id, newPassword);
}
/// <summary>
/// 启用或禁用用户
/// </summary>
[Authorize(ProjectNamePermissions.User.Update)]
public async Task SetUserActiveStatusAsync(Guid id, bool isActive)
{
await _userManager.SetUserActiveStatusAsync(id, isActive);
}
/// <summary>
/// 创建基础查询,应用过滤条件
/// </summary>
protected async virtual Task<IQueryable<User>> CreateFilteredQueryAsync(
UserPagedAndSortedResultRequestDto input)
{
// 获取基础查询,并加载相关实体
var query = await _userRepository.WithDetailsAsync(
x => x.IdentityUser);
// 应用过滤条件
return query
.WhereIf(!string.IsNullOrWhiteSpace(input.NickName),
x => x.NickName.Contains(input.NickName));
}
/// <summary>
/// 将User实体映射为UserDto,并填充权限信息
/// </summary>
private Task<UserDto> MapToUserDtoAsync(User user)
{
var userDto = ObjectMapper.Map<User, UserDto>(user);
// 设置用户状态
if (user.IdentityUser != null)
{
userDto.IsActive = user.IdentityUser.LockoutEnd == null || user.IdentityUser.LockoutEnd < DateTimeOffset.Now;
}
return Task.FromResult(userDto);
}
}
}

4
aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.Domain/PackageName.CompanyName.ProjectName.Domain.csproj

@ -14,10 +14,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LINGYUN.Abp.Identity.Domain" />
<PackageReference Include="Volo.Abp.AutoMapper" />
<PackageReference Include="Volo.Abp.BackgroundJobs.Abstractions" />
<PackageReference Include="Volo.Abp.BackgroundWorkers.Hangfire" />
<PackageReference Include="Volo.Abp.Caching" />
<PackageReference Include="Volo.Abp.Ddd.Domain" />
<PackageReference Include="LINGYUN.Abp.DataProtection" />
<PackageReference Include="Volo.Abp.Settings" />
</ItemGroup>
<ItemGroup>

4
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
{

21
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
{
/// <summary>
/// 定义具有树形编码的实体接口
/// </summary>
public interface IHaveTreeCode : IHasCreationTime
{
/// <summary>
/// 树形编码
/// </summary>
string TreeCode { get; set; }
/// <summary>
/// 父级Id
/// </summary>
Guid? ParentId { get; set; }
}
}

33
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
{
/// <summary>
/// 树形编码生成器接口
/// </summary>
public interface ITreeCodeGenerator
{
/// <summary>
/// 生成树形编码
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <param name="repository">仓储</param>
/// <param name="parentId">父级Id</param>
/// <returns>生成的树形编码</returns>
Task<string> GenerateAsync<TEntity>(
IRepository<TEntity, Guid> repository,
Guid? parentId)
where TEntity : class, IEntity<Guid>, IHaveTreeCode;
/// <summary>
/// 更新节点及其所有子节点的TreeCode
/// </summary>
Task UpdateTreeCodesAsync<TEntity>(
IRepository<TEntity, Guid> repository,
Guid entityId)
where TEntity : class, IEntity<Guid>, IHaveTreeCode;
}
}

114
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
{
/// <summary>
/// 树形编码生成器
/// </summary>
public class TreeCodeGenerator : ITreeCodeGenerator, ISingletonDependency
{
/// <summary>
/// 生成树形编码
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <param name="repository">仓储</param>
/// <param name="parentId">父级Id</param>
/// <returns>生成的树形编码</returns>
public async virtual Task<string> GenerateAsync<TEntity>(
IRepository<TEntity, Guid> repository,
Guid? parentId)
where TEntity : class, IEntity<Guid>, IHaveTreeCode
{
if (!parentId.HasValue)
{
// 生成根节点编码
return await GenerateRootCodeAsync(repository);
}
else
{
// 生成子节点编码
return await GenerateChildCodeAsync(repository, parentId.Value);
}
}
private async Task<string> GenerateRootCodeAsync<TEntity>(
IRepository<TEntity, Guid> repository)
where TEntity : class, IEntity<Guid>, 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<string> GenerateChildCodeAsync<TEntity>(
IRepository<TEntity, Guid> repository,
Guid parentId)
where TEntity : class, IEntity<Guid>, 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}";
}
/// <summary>
/// 更新节点及其所有子节点的TreeCode
/// </summary>
public async virtual Task UpdateTreeCodesAsync<TEntity>(
IRepository<TEntity, Guid> repository,
Guid entityId)
where TEntity : class, IEntity<Guid>, 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);
}
}
}
}

69
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
{
/// <summary>
/// 用户管理服务接口
/// </summary>
public interface IUserManager : IDomainService
{
/// <summary>
/// 创建新用户
/// </summary>
Task<User> CreateAsync(
string nickName,
string password,
string contactInfo = null,
string position = null,
bool isActive = true
);
/// <summary>
/// 更新用户信息
/// </summary>
Task<User> UpdateAsync(
Guid id,
string nickName,
string password,
string contactInfo = null,
string position = null,
bool isActive = true);
/// <summary>
/// 删除用户
/// </summary>
Task DeleteAsync(Guid id);
/// <summary>
/// 修改用户密码
/// </summary>
Task ChangePasswordAsync(Guid id, string currentPassword, string newPassword);
/// <summary>
/// 重置用户密码
/// </summary>
Task ResetPasswordAsync(Guid id, string newPassword);
/// <summary>
/// 获取用户信息
/// </summary>
Task<User> GetAsync(Guid id);
/// <summary>
/// 根据Identity用户ID获取用户
/// </summary>
Task<User> FindByIdentityUserIdAsync(Guid identityUserId);
/// <summary>
/// 根据用户昵称查找用户
/// </summary>
Task<User> FindByNickNameAsync(string nickName);
/// <summary>
/// 禁用或启用用户
/// </summary>
Task SetUserActiveStatusAsync(Guid id, bool isActive);
}
}

9
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<User, Guid>
{
}
}

59
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
{
/// <summary>
/// 用户实体
/// </summary>
public class User : FullAuditedEntity<Guid>
{
/// <summary>
/// 用户名称
/// </summary>
[MaxLength(50)]
public string NickName { get; set; }
/// <summary>
/// Identity用户Id
/// </summary>
public Guid IdentityUserId { get; set; }
/// <summary>
/// Identity用户
/// </summary>
public virtual IdentityUser IdentityUser { get; set; }
/// <summary>
/// 联系方式
/// </summary>
[MaxLength(50)]
public string ContactInfo { get; set; }
/// <summary>
/// 职位
/// </summary>
[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;
}
}
}

406
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
{
/// <summary>
/// 用户管理服务,用于处理用户的CRUD操作
/// </summary>
public class UserManager : DomainService, IUserManager
{
private readonly IUserRepository _userRepository;
private readonly IdentityUserManager _identityUserManager;
private readonly ILogger<UserManager> _logger;
public UserManager(
IUserRepository userRepository,
IdentityUserManager identityUserManager,
ILogger<UserManager> logger)
{
_userRepository = userRepository;
_identityUserManager = identityUserManager;
_logger = logger;
}
/// <summary>
/// 创建新用户
/// </summary>
/// <param name="nickName">用户昵称</param>
/// <param name="password">用户密码</param>
/// <param name="contactInfo">联系方式</param>
/// <param name="position">职位</param>
/// <param name="isActive">是否启用</param>
/// <returns>创建的用户实体</returns>
public async Task<User> 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;
}
/// <summary>
/// 更新用户信息
/// </summary>
/// <param name="id">用户ID</param>
/// <param name="nickName">用户昵称</param>
/// <param name="password">用户密码(可选,如不修改则传入null)</param>
/// <param name="contactInfo">联系方式</param>
/// <param name="position">职位</param>
/// <param name="isActive">是否启用</param>
/// <returns>更新后的用户实体</returns>
public async Task<User> 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}次重试...");
}
}
}
/// <summary>
/// 删除用户
/// </summary>
/// <param name="id">用户ID</param>
/// <returns>操作任务</returns>
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}");
}
/// <summary>
/// 修改用户密码
/// </summary>
/// <param name="id">用户ID</param>
/// <param name="currentPassword">当前密码</param>
/// <param name="newPassword">新密码</param>
/// <returns>操作结果</returns>
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}]成功修改了密码");
}
/// <summary>
/// 重置用户密码(管理员操作)
/// </summary>
/// <param name="id">用户ID</param>
/// <param name="newPassword">新密码</param>
/// <returns>操作结果</returns>
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}]的密码");
}
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="id">用户ID</param>
/// <returns>用户实体</returns>
public async Task<User> GetAsync(Guid id)
{
return await _userRepository.GetAsync(id);
}
/// <summary>
/// 根据Identity用户ID获取用户
/// </summary>
/// <param name="identityUserId">Identity用户ID</param>
/// <returns>用户实体,如果不存在则返回null</returns>
public async Task<User> FindByIdentityUserIdAsync(Guid identityUserId)
{
return await _userRepository.FindAsync(u => u.IdentityUserId == identityUserId);
}
/// <summary>
/// 根据用户昵称查找用户
/// </summary>
/// <param name="nickName">用户昵称</param>
/// <returns>用户实体,如果不存在则返回null</returns>
public async Task<User> FindByNickNameAsync(string nickName)
{
return await _userRepository.FindAsync(u => u.NickName == nickName);
}
/// <summary>
/// 禁用或启用用户
/// </summary>
/// <param name="id">用户ID</param>
/// <param name="isActive">是否启用</param>
/// <returns>操作任务</returns>
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}]");
}
}
}

1
aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName.CompanyName.ProjectName.EntityFrameworkCore.csproj

@ -28,6 +28,7 @@
<PackageReference Include="LINGYUN.Abp.Data.DbMigrator" />
<PackageReference Include="LINGYUN.Abp.DataProtection.EntityFrameworkCore" />
<PackageReference Include="LINGYUN.Abp.Saas.EntityFrameworkCore" />
<PackageReference Include="Volo.Abp.Identity.EntityFrameworkCore" />
</ItemGroup>
<ItemGroup>

5
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<User> Users { get; }
}

9
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<ProjectNameDbContext>, IProjectNameDbContext
{
public virtual DbSet<User> Users { get; set; }
public ProjectNameDbContext(
DbContextOptions<ProjectNameDbContext> options) : base(options)
{
@ -14,8 +18,9 @@ public class ProjectNameDbContext : AbpDataProtectionDbContext<ProjectNameDbCont
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ConfigureProjectName();
modelBuilder.ConfigureIdentity();
base.OnModelCreating(modelBuilder);
}
}

17
aspnet-core/templates/aio/content/src/PackageName.CompanyName.ProjectName.EntityFrameworkCore/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameDbContextModelCreatingExtensions.cs

@ -1,6 +1,8 @@
using Microsoft.EntityFrameworkCore;
using PackageName.CompanyName.ProjectName.Users;
using System;
using Volo.Abp;
using Volo.Abp.EntityFrameworkCore.Modeling;
namespace PackageName.CompanyName.ProjectName.EntityFrameworkCore;
@ -17,5 +19,20 @@ public static class ProjectNameDbContextModelCreatingExtensions
ProjectNameDbProperties.DbSchema
);
optionsAction?.Invoke(options);
builder.Entity<User>(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<User>(u => u.IdentityUserId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
});
}
}

6
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<ProjectNameDbContext>(options =>
{
options.AddDefaultRepositories<IProjectNameDbContext>();
options.AddDefaultRepositories<IProjectNameDbContext>(true);
options.AddRepository<User, UserRepository>();
});
}
}

22
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<ProjectNameDbContext, User, Guid>, IUserRepository
{
public UserRepository(IDbContextProvider<ProjectNameDbContext> dbContextProvider) : base(dbContextProvider)
{
}
public override async Task<IQueryable<User>> WithDetailsAsync()
{
return (await GetDbSetAsync()).Include(x => x.IdentityUser);
}
}
}

134
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
{
/// <summary>
/// 用户管理控制器
/// </summary>
[RemoteService]
[Route("api/app/user")]
public class UserController : ProjectNameControllerBase
{
private readonly IUserAppService _userAppService;
public UserController(IUserAppService userAppService)
{
_userAppService = userAppService;
}
/// <summary>
/// 创建用户
/// </summary>
[HttpPost]
public async Task<UserDto> CreateAsync(CreateUpdateUserDto input)
{
return await _userAppService.CreateAsync(input);
}
/// <summary>
/// 更新用户
/// </summary>
[HttpPut("{id}")]
public async Task<UserDto> UpdateAsync(Guid id, CreateUpdateUserDto input)
{
return await _userAppService.UpdateAsync(id, input);
}
/// <summary>
/// 删除用户
/// </summary>
[HttpDelete("{id}")]
public async Task DeleteAsync(Guid id)
{
await _userAppService.DeleteAsync(id);
}
/// <summary>
/// 获取用户
/// </summary>
[HttpGet("{id}")]
public async Task<UserDto> GetAsync(Guid id)
{
return await _userAppService.GetAsync(id);
}
/// <summary>
/// 获取用户列表
/// </summary>
[HttpGet]
public async Task<PagedResultDto<UserItemDto>> GetListAsync(UserPagedAndSortedResultRequestDto input)
{
return await _userAppService.GetListAsync(input);
}
/// <summary>
/// 修改用户密码
/// </summary>
[HttpPost("{id}/change-password")]
public async Task ChangePasswordAsync(Guid id, [FromBody] ChangePasswordRequest request)
{
await _userAppService.ChangePasswordAsync(id, request.CurrentPassword, request.NewPassword);
}
/// <summary>
/// 重置用户密码(管理员操作)
/// </summary>
[HttpPost("{id}/reset-password")]
public async Task ResetPasswordAsync(Guid id, [FromBody] ResetPasswordRequest request)
{
await _userAppService.ResetPasswordAsync(id, request.NewPassword);
}
/// <summary>
/// 启用或禁用用户
/// </summary>
[HttpPost("{id}/set-active")]
public async Task SetUserActiveStatusAsync(Guid id, [FromBody] SetUserActiveRequest request)
{
await _userAppService.SetUserActiveStatusAsync(id, request.IsActive);
}
}
/// <summary>
/// 修改密码请求
/// </summary>
public class ChangePasswordRequest
{
/// <summary>
/// 当前密码
/// </summary>
public string CurrentPassword { get; set; }
/// <summary>
/// 新密码
/// </summary>
public string NewPassword { get; set; }
}
/// <summary>
/// 重置密码请求
/// </summary>
public class ResetPasswordRequest
{
/// <summary>
/// 新密码
/// </summary>
public string NewPassword { get; set; }
}
/// <summary>
/// 设置用户状态请求
/// </summary>
public class SetUserActiveRequest
{
/// <summary>
/// 是否启用
/// </summary>
public bool IsActive { get; set; }
}
}
Loading…
Cancel
Save