You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

274 lines
9.4 KiB
C#

using System.Text.RegularExpressions;
using Mapster;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using NPin.Framework.SqlSugarCore.Abstractions;
using NPin.Framework.Upms.Domain.Entities;
using NPin.Framework.Upms.Domain.Entities.ValueObjects;
using NPin.Framework.Upms.Domain.Repositories;
using NPin.Framework.Upms.Domain.Shared.Caches;
using NPin.Framework.Upms.Domain.Shared.Consts;
using NPin.Framework.Upms.Domain.Shared.Dtos;
using NPin.Framework.Upms.Domain.Shared.Etos;
using NPin.Framework.Upms.Domain.Shared.Options;
using Volo.Abp.Authorization;
using Volo.Abp.Caching;
using Volo.Abp.Domain.Services;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Uow;
namespace NPin.Framework.Upms.Domain.Managers;
/// <summary>
/// 用户管理器 领域服务
/// </summary>
public partial class UserManager : DomainService
{
[GeneratedRegex("^[a-zA-Z0-9]+$")]
private static partial Regex UsernameRegex();
private readonly IUserRepository _repository;
private readonly ISqlSugarRepository<RoleAggregateRoot> _repositoryRole;
private readonly ISqlSugarRepository<UserRoleEntity> _repositoryUserRole;
private readonly ISqlSugarRepository<UserPostEntity> _repositoryUserPost;
private readonly ISqlSugarRepository<UserOrganizationEntity> _repositoryUserOrg;
private IDistributedCache<UserInfoCacheItem, UserInfoCacheKey> _userCache;
private ILocalEventBus _localEventBus;
public UserManager(
IUserRepository repository,
ISqlSugarRepository<UserRoleEntity> repositoryUserRole,
ISqlSugarRepository<UserPostEntity> repositoryUserPost,
ISqlSugarRepository<UserOrganizationEntity> repositoryUserOrg,
IDistributedCache<UserInfoCacheItem, UserInfoCacheKey> userCache,
ILocalEventBus localEventBus, ISqlSugarRepository<RoleAggregateRoot> repositoryRole)
{
_repository = repository;
_repositoryUserRole = repositoryUserRole;
_repositoryUserPost = repositoryUserPost;
_repositoryUserOrg = repositoryUserOrg;
_userCache = userCache;
_localEventBus = localEventBus;
_repositoryRole = repositoryRole;
}
/// <summary>
/// 创建用户
/// </summary>
/// <param name="aggregateRoot"></param>
public async Task CreateAsync(UserAggregateRoot aggregateRoot)
{
await ValidateUser(aggregateRoot);
var returnEntity = await _repository.InsertReturnEntityAsync(aggregateRoot);
aggregateRoot = returnEntity;
// 触发事件
await _localEventBus.PublishAsync(new UserCreatedEventArgs(aggregateRoot.Id));
}
private async Task ValidateUser(UserAggregateRoot aggregateRoot)
{
// TODO 不一定非要用户名,这里需要更自由的逻辑
if (aggregateRoot.Username is UserConst.Admin or UserConst.TenantAdmin)
{
throw new UserFriendlyException("无效的用户名");
}
if (aggregateRoot.Username.Length < 2)
{
throw new UserFriendlyException("用户名长度错误需大于2个字符");
}
// 正则表达式,匹配只包含数字和字母的字符串
var isMatch = UsernameRegex().IsMatch(aggregateRoot.Username);
if (!isMatch)
{
throw new UserFriendlyException("用户名不能包含除【字母】与【数字】的其他字符");
}
// 密码长度判断
// TODO需要读取配置
if (aggregateRoot.EncryptPassword.Password.Length < 6)
{
throw new UserFriendlyException($"密码格式错误,长度需大于等于{6}位");
}
if (!string.IsNullOrEmpty(aggregateRoot.PhoneNumber))
{
if (await _repository.IsAnyAsync(x => x.PhoneNumber == aggregateRoot.PhoneNumber))
{
throw new UserFriendlyException("手机号重复");
}
}
}
/// <summary>
/// 设置默认角色
/// </summary>
/// <param name="userId"></param>
public async Task SetDefaultRoleAsync(Guid userId)
{
// 检查默认角色是否存在,不存在不处理
var role = await _repositoryRole.GetFirstAsync(x => x.Code == UserConst.DefaultRoleCode);
if (role is not null)
{
await SetRoleAsync([userId], [role.Id]);
}
}
/// <summary>
/// 给用户设置角色
/// </summary>
/// <param name="userIds"></param>
/// <param name="roleIds"></param>
[UnitOfWork]
public async Task SetRoleAsync(List<Guid> userIds, List<Guid> roleIds)
{
// 删除用户之前的所有关系(物理)
await _repositoryUserRole.DeleteAsync(u => userIds.Contains(u.UserId));
var entities = (from userId in userIds
from roleId in roleIds
select new UserRoleEntity { UserId = userId, RoleId = roleId })
.ToList();
await _repositoryUserRole.InsertRangeAsync(entities);
}
/// <summary>
/// 设置用户岗位
/// </summary>
/// <param name="userIds"></param>
/// <param name="postIds"></param>
[UnitOfWork]
public async Task SetPostAsync(List<Guid> userIds, List<Guid> postIds)
{
// 删除用户之前的所有关系(物理)
await _repositoryUserPost.DeleteAsync(u => userIds.Contains(u.UserId));
var entities = (from userId in userIds
from postId in postIds
select new UserPostEntity { UserId = userId, PostId = postId })
.ToList();
await _repositoryUserPost.InsertRangeAsync(entities);
}
/// <summary>
/// 设置用户组织机构
/// </summary>
/// <param name="userIds"></param>
/// <param name="orgIds"></param>
[UnitOfWork]
public async Task SetOrganizationAsync(List<Guid> userIds, List<Guid> orgIds)
{
// 删除用户之前的所有关系(物理)
await _repositoryUserOrg.DeleteAsync(u => userIds.Contains(u.UserId));
var entities = (from userId in userIds
from orgId in orgIds
select new UserOrganizationEntity { UserId = userId, OrganizationId = orgId })
.ToList();
await _repositoryUserOrg.InsertRangeAsync(entities);
}
/// <summary>
/// 从缓存中获取用户信息,若缓存不存在则查库
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
/// <exception cref="AbpAuthorizationException">库中无此用户403</exception>
public async Task<UserFullDto> GetInfoByCacheAsync(Guid userId)
{
// 1. 缓存获取
UserFullDto ret = null;
var tokenExpires = LazyServiceProvider.GetRequiredService<IOptions<JwtOptions>>().Value.ExpiresMinuteTime;
var cached = await _userCache.GetOrAddAsync(new UserInfoCacheKey(userId),
async () =>
{
// 2. 库查询
var user = await _repository.GetAllInfoAsync(userId);
var dto = EntityMapToDto(user);
ret = dto ?? throw new AbpAuthorizationException();
return new UserInfoCacheItem(dto);
},
() => new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(tokenExpires)
});
if (cached is not null)
{
ret = cached.Info;
}
return ret!;
}
private UserFullDto? EntityMapToDto(UserAggregateRoot? entity)
{
if (entity is null)
{
return null;
}
var ret = new UserFullDto();
// 密码过滤
entity.EncryptPassword = new EncryptPasswordValueObject();
// 超级管理员特殊处理
if (UserConst.Admin.Equals(entity.Username))
{
ret.User = _userCache.Adapt<UserDto>();
ret.RoleCodes.Add(UserConst.AdminRoleCode);
ret.PermissionCodes.Add(UserConst.AdminPermissionCode);
return ret;
}
// 角色
var roleDtoList = new List<RoleDto>();
var roleCodes = new HashSet<string>();
foreach (var role in entity.Roles)
{
roleDtoList.Add(role.Adapt<RoleDto>());
roleCodes.Add(role.Code);
}
ret.Roles = roleDtoList;
ret.RoleCodes = roleCodes;
// 岗位
var postDtoList = new List<PostDto>();
var postCodes = new HashSet<string>();
foreach (var post in entity.Posts)
{
postDtoList.Add(post.Adapt<PostDto>());
postCodes.Add(post.Code);
}
ret.Posts = postDtoList;
ret.PostCodes = postCodes;
// 组织结构
var orgDtoList = new List<OrganizationDto>();
var orgCodes = new HashSet<string>();
foreach (var org in entity.Organizations)
{
orgDtoList.Add(org.Adapt<OrganizationDto>());
orgCodes.Add(org.Code);
}
ret.Organizations = orgDtoList;
ret.OrganizationCodes = orgCodes;
ret.User = entity.Adapt<UserDto>();
// TODO permissionCode
return ret;
}
}