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; /// /// 用户管理器 领域服务 /// public partial class UserManager : DomainService { [GeneratedRegex("^[a-zA-Z0-9]+$")] private static partial Regex UsernameRegex(); private readonly IUserRepository _repository; private readonly ISqlSugarRepository _repositoryRole; private readonly ISqlSugarRepository _repositoryUserRole; private readonly ISqlSugarRepository _repositoryUserPost; private readonly ISqlSugarRepository _repositoryUserOrg; private IDistributedCache _userCache; private ILocalEventBus _localEventBus; public UserManager( IUserRepository repository, ISqlSugarRepository repositoryUserRole, ISqlSugarRepository repositoryUserPost, ISqlSugarRepository repositoryUserOrg, IDistributedCache userCache, ILocalEventBus localEventBus, ISqlSugarRepository repositoryRole) { _repository = repository; _repositoryUserRole = repositoryUserRole; _repositoryUserPost = repositoryUserPost; _repositoryUserOrg = repositoryUserOrg; _userCache = userCache; _localEventBus = localEventBus; _repositoryRole = repositoryRole; } /// /// 创建用户 /// /// public async Task CreateAsync(UserEntity entity) { await ValidateUser(entity); var returnEntity = await _repository.InsertReturnEntityAsync(entity); entity = returnEntity; // 触发事件 await _localEventBus.PublishAsync(new UserCreatedEventArgs(entity.Id)); } private async Task ValidateUser(UserEntity entity) { // TODO 不一定非要用户名,这里需要更自由的逻辑 if (entity.Username is UserConst.Admin or UserConst.TenantAdmin) { throw new UserFriendlyException("无效的用户名"); } if (entity.Username.Length < 2) { throw new UserFriendlyException("用户名长度错误,需大于2个字符"); } // 正则表达式,匹配只包含数字和字母的字符串 var isMatch = UsernameRegex().IsMatch(entity.Username); if (!isMatch) { throw new UserFriendlyException("用户名不能包含除【字母】与【数字】的其他字符"); } // 密码长度判断 // TODO(需要读取配置) if (entity.EncryptPassword.Password.Length < 6) { throw new UserFriendlyException($"密码格式错误,长度需大于等于{6}位"); } if (!string.IsNullOrEmpty(entity.PhoneNumber)) { if (await _repository.IsAnyAsync(x => x.PhoneNumber == entity.PhoneNumber)) { throw new UserFriendlyException("手机号重复"); } } } /// /// 设置默认角色 /// /// 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]); } } /// /// 给用户设置角色 /// /// /// [UnitOfWork] public async Task SetRoleAsync(List userIds, List 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); } /// /// 设置用户岗位 /// /// /// [UnitOfWork] public async Task SetPostAsync(List userIds, List 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); } /// /// 设置用户组织机构 /// /// /// [UnitOfWork] public async Task SetOrganizationAsync(List userIds, List 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); } /// /// 从缓存中获取用户信息,若缓存不存在则查库 /// /// /// /// 库中无此用户,403 public async Task GetInfoByCacheAsync(Guid userId) { // 1. 缓存获取 UserFullDto ret = null; var tokenExpires = LazyServiceProvider.GetRequiredService>().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(UserEntity? 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(); ret.RoleCodes.Add(UserConst.AdminRoleCode); ret.PermissionCodes.Add(UserConst.AdminPermissionCode); return ret; } // 角色 var roleDtoList = new List(); var roleCodes = new HashSet(); foreach (var role in entity.Roles) { roleDtoList.Add(role.Adapt()); roleCodes.Add(role.Code); } ret.Roles = roleDtoList; ret.RoleCodes = roleCodes; // 岗位 var postDtoList = new List(); var postCodes = new HashSet(); foreach (var post in entity.Posts) { postDtoList.Add(post.Adapt()); postCodes.Add(post.Code); } ret.Posts = postDtoList; ret.PostCodes = postCodes; // 组织结构 var orgDtoList = new List(); var orgCodes = new HashSet(); foreach (var org in entity.Organizations) { orgDtoList.Add(org.Adapt()); orgCodes.Add(org.Code); } ret.Organizations = orgDtoList; ret.OrganizationCodes = orgCodes; ret.User = entity.Adapt(); // TODO permissionCode return ret; } }