using Mapster; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using SqlSugar; using System.Text; using Wood.Cache; using Wood.Data.Repository; using Wood.Entity; using Wood.Entity.SystemManage; using Wood.Service.SystemManage.Dto; using Wood.Service.SystemManage.Manager; using Wood.Service.SystemManage.Param; using Wood.Util; using Wood.Util.JwtAuthorization; namespace Wood.Service.SystemManage { /// /// 用户管理 /// public class UserService : ApiService { private readonly UserManager _userManager; private readonly SqlSugarRepository _tenantRepository; private readonly SqlSugarRepository _roleRepository; private readonly SqlSugarRepository _positionRepository; private readonly SqlSugarRepository _menuAuthorizeRepository; private readonly SqlSugarRepository _menuRepository; private readonly SqlSugarRepository _refreshTokenRepository; private readonly SqlSugarRepository _logLoginRepository; private readonly FileManager _fileManager; private readonly OrgManager _orgManager; private readonly ICache _cache; private SqlSugarRepository _userRepository => _userManager.AsRepository(); public UserService(UserManager userManager, SqlSugarRepository roleRepository, SqlSugarRepository tenantRepository, ICache cache, SqlSugarRepository refreshTokenRepository, SqlSugarRepository menuAuthorizeRepository, SqlSugarRepository menuRepository, FileManager fileManager, SqlSugarRepository logLoginRepository, OrgManager orgManager, SqlSugarRepository positionRepository) { _userManager = userManager; _roleRepository = roleRepository; _tenantRepository = tenantRepository; _cache = cache; _refreshTokenRepository = refreshTokenRepository; _menuAuthorizeRepository = menuAuthorizeRepository; _menuRepository = menuRepository; _fileManager = fileManager; _logLoginRepository = logLoginRepository; _orgManager = orgManager; _positionRepository = positionRepository; } /// /// 分页获取用户数据 /// /// /// public async Task> Paged(UserPagedParam param) { return await BuildQuery(param) .Select(it => new UserPagedDto { Id = it.Id.SelectAll(), OrgName = it.Org!.OrgName, Roles = SqlFunc.Subqueryable() .LeftJoin((ur, ro) => ur.RoleId == ro.Id) .Where((ur, ro) => ur.UserId == it.Id) .SelectStringJoin((ur, ro) => ro.RoleName, ","), }) .ToPagedListAsync(param); } /// /// 获取用户信息列表用于选择 /// /// public async Task> SelectList(UserSelectListParam param) { return await _userRepository.AsQueryable() .WhereIF(!string.IsNullOrEmpty(param.Name), it => it.RealName!.Contains(param.Name!) || it.UserName.Contains(param.Name!)) .WhereIF(param.Ids.Any(), it => param.Ids.Contains(it.Id)) .Where(it => it.AccountType != AccountTypeEnum.SuperAdmin) .Select(it => new ElSelectDto() { Label = it.RealName + "(" + it.UserName + ")", Disabled = it.Status == 0, Value = it.Id.ToString() }).ToListAsync(); } /// /// 是否存在用户 /// /// /// public async Task GetExist(string userName) { return await _userRepository.IsAnyAsync(it => it.UserName == userName); } /// /// 获取验证码 /// /// [AllowAnonymous] public UserCaptchaDto GetCaptcha() { var tuple = CaptchaHelper.GetCaptchaCode(); var image = CaptchaHelper.CreateCaptchaImage(tuple.Item1); UserCaptchaDto dto = new UserCaptchaDto() { Guid = IdGeneratorHelper.Instance.GetGuid(), Img = Convert.ToBase64String(image) }; _cache.SetCache(dto.Guid, tuple.Item2, DateTime.Now.AddMinutes(2)); return dto; } /// /// 用户登录 /// /// 可用租户信息 [AllowAnonymous] public async Task Login(UserLoginParam param) { if (!(int.TryParse(param.CaptchaCode, out int val) && _cache.TryGetCache(param.Captcha, out int cacheVal) && val == cacheVal)) { _cache.RemoveCache(param.Captcha); throw Oops.Oh("验证码错误!"); } _cache.SetCache(param.Captcha, param.UserName, DateTime.Now.AddMinutes(5)); var users = await _userRepository.AsQueryable() .Where(it => it.UserName == param.UserName) .Where(it => it.Status == 1) .ToListAsync(); if (users == null || users.Count < 1) throw Oops.Oh($"不存在用户【{param.UserName}】!"); List passOk = new List(); //密码验证通过的账户信息 foreach (var item in users) { if (param!.Password == CryptogramHelper.GMSM4Decrypt(item.Password)) passOk.Add(item); } if (passOk.Any()) { var tenantIds = passOk.Select(it => it.TenantId).ToList(); var tenants = await _tenantRepository.AsQueryable() .Where(it => tenantIds.Contains(it.Id)) .Select(it => new { it.Id, it.TenantName, it.Status }) .ToListAsync(); if (tenants.All(it => it.Status != 1)) throw Oops.Oh("登录失败,账号已经冻结!"); return tenants; } throw Oops.Oh("登录失败,没有相关用户信息!"); } /// /// 用户租户登录 /// [AllowAnonymous] public async Task TenantLogin(UserTenantLoginParam param) { if (_cache.TryGetCache(param.Captcha, out string? cacheVal) && cacheVal == param.UserName) { LogLoginEntity logLoginEntity = new LogLoginEntity() { Account = param.UserName, Browser = NetHelper.Browser, IpAddress = NetHelper.Ip, LogStatus = LoginStatusEnum.Failed, Os = NetHelper.GetOSVersion() }; _cache.RemoveCache(param.Captcha); var user = await _userRepository.GetFirstAsync(it => it.UserName == param.UserName); if (user == null) { logLoginEntity.LogStatus = LoginStatusEnum.NoAccount; logLoginEntity.Message = $"不存在用户【{param.UserName}】!"; await _logLoginRepository.InsertAsync(logLoginEntity); throw Oops.Oh($"不存在用户【{param.UserName}】!"); } if (user!.Status != 1) { logLoginEntity.LogStatus = LoginStatusEnum.Frozen; logLoginEntity.Message = $"登录失败,账号已经冻结!"; await _logLoginRepository.InsertAsync(logLoginEntity); throw Oops.Oh("登录失败,账号已经冻结!"); } if (param!.Password != CryptogramHelper.GMSM4Decrypt(user.Password)) { logLoginEntity.LogStatus = LoginStatusEnum.PasswordError; logLoginEntity.Message = $"密码不正确!"; await _logLoginRepository.InsertAsync(logLoginEntity); throw Oops.Oh("密码不正确!"); } var tenant = await _tenantRepository.GetByIdAsync(param.TenantId); if (tenant.Status == 1) { var token = await DoLogin(user.Id); logLoginEntity.LogStatus = LoginStatusEnum.Success; await _logLoginRepository.InsertAsync(logLoginEntity); return token; } throw Oops.Oh("无效租户!"); } else { _cache.RemoveCache(param.Captcha); throw Oops.Oh("登录失败!验证码无效。"); } } /// /// 用户登录 /// /// 可用租户信息 [AllowAnonymous] public async Task RefreshLogin(UserRefreshLoginParam param) { var refreshToken = await _refreshTokenRepository.GetFirstAsync(it => it.RefreshToken == param.RefreshToken); if (refreshToken == null) throw Oops.Oh("登录过期,请重新登录!"); //refreshToken已经撤销 if (refreshToken.RevokedAt != null && refreshToken.RevokedAt.Value < DateTime.Now) throw Oops.Oh("登录过期,请重新登录!"); //refreshToken已经过期 if (refreshToken.ExpiresAt < DateTime.Now) throw Oops.Oh("登录过期,请重新登录!"); var token = await DoLogin(refreshToken.UserId); var userInfo = await _userRepository.GetByIdAsync(refreshToken.UserId); LogLoginEntity logLoginEntity = new LogLoginEntity() { Account = userInfo!.UserName, Browser = NetHelper.Browser, IpAddress = NetHelper.Ip, LogStatus = LoginStatusEnum.RefreshSuccess, Os = NetHelper.GetOSVersion(), Message = param.RefreshToken }; await _logLoginRepository.InsertAsync(logLoginEntity); //重置refreshToken有效期 token.RefreshToken = param.RefreshToken; refreshToken.ExpiresAt = token.RefreshTokenExpiresTime; await _refreshTokenRepository.UpdateAsync(refreshToken); return token; } /// /// 修改密码 /// /// /// public async Task ResetPassword(UserChangePasswordParam param) { var user = await _userRepository.GetByIdAsync(param.Id); if (user != null) { if (param!.Password == CryptogramHelper.GMSM4Decrypt(user.Password)) { if (param.NewPassword == param.ConfirmPassword) { if (param.Password != param.NewPassword) { user.Password = CryptogramHelper.GMSM4Encrypt(param.Password); await _userRepository.UpdateAsync(user); } else throw Oops.Oh("旧密码不能和新密码一样!"); } else throw Oops.Oh("两次输入密码不一致!"); } else throw Oops.Oh("密码错误!"); } else throw Oops.Oh("用户不存在!"); } /// /// 获取当前用户的基本信息 /// /// public async Task GetCurrentUserInfo() { var user = this.UserInfo(); var userInfo = await _userRepository.AsQueryable() .Includes(it => it.Roles) .Includes(it => it.Org) .Includes(it => it.Position) .Where(it => it.Id == user!.UserId) .FirstAsync(); string roleName = userInfo!.AccountType.GetDescription(); if (userInfo!.Roles != null && userInfo!.Roles.Any()) roleName = string.Join(',', userInfo.Roles.Select(it => it.RoleName)); var avatorInfo = await _fileManager.GetFilePathsByCode(userInfo.Avatar!); return new { userInfo.Id, userInfo.UserName, userInfo.TenantId, userInfo.OrgId, userInfo.RealName, userInfo.NickName, userInfo.AccountType, Avatar = avatorInfo.FirstOrDefault(), roleName, userInfo.Position?.PositionName, userInfo.PositionId, userInfo.Remark, userInfo.LastVisit, userInfo.Org?.OrgName, }; } /// /// 获取当前用户的菜单权限信息 /// /// public async Task> GetCurrentRoleAuthorizeInfo() { var user = this.UserInfo(); //超级管理员 if (user!.IsSuperAdmin) { return await _menuRepository.AsQueryable().Where(it => it.Status == 1).OrderBy(it => it.Sort).ToListAsync(); } else { var usercache = _cache.GetCache(user!.CacheKey); var menuIds = await _menuAuthorizeRepository.AsQueryable() .Where(it => usercache!.Roles.Contains(it.RoleId)) .Select(it => it.MenuId) .Distinct().ToListAsync(); return await _menuRepository.AsQueryable() .Where(it => it.Status == 1) .Where(it => menuIds.Contains(it.Id)) .OrderBy(it => it.Sort) .ToListAsync(); } } /// /// 删除用户 /// /// /// [UnitOfWork] public async Task Delete(BaseIdListParam param) { if (param.Ids.Any()) { //用户标记为删除 await _userRepository.FakeDeleteAsync(it => param.Ids.Contains(it.Id)); //清空 refrshtoken await _refreshTokenRepository.DeleteAsync(it => param.Ids.Contains(it.UserId)); } } /// /// 新增 /// /// [UnitOfWork] public async Task Add(UserAddParam param) { var entity = param.Adapt(); entity.AccountType = AccountTypeEnum.User; entity.LoginCount = 0; entity.Roles = await _roleRepository.GetListAsync(it => param.Roles.Contains(it.Id)); entity.Salt = CryptogramHelper.GenerateRandomString(16); entity.Password = CryptogramHelper.GMSM4Encrypt("123456"); entity.Avatar = await _fileManager.AddFile(param.AvatarImgId); await _userRepository.AsSugarClient().InsertNav(entity).Include(it => it.Roles).ExecuteCommandAsync(); } /// /// 更新 /// /// [UnitOfWork] public async Task Update([FromBody] UserUpdateParam param) { var entity = await _userRepository.GetByIdAsync(param.Id); param.Adapt(entity); entity.Roles = await _roleRepository.GetListAsync(it => param.Roles.Contains(it.Id)); entity.Avatar = await _fileManager.UpdateFile(entity.Avatar, param.AvatarImgId); //更新A表和 关系表 await _userRepository.AsSugarClient().UpdateNav(entity) .Include(it => it.Roles, new UpdateNavOptions() { ManyToManyIsUpdateA = true }).ExecuteCommandAsync(); } /// /// 获取明细 /// /// /// public async Task GetDetail(BaseIdParam param) { var entity = await _userRepository.AsQueryable() .Includes(it => it.Roles) .Where(it => it.Id == param.Id).FirstAsync(); var dto = entity.Adapt(); dto.Roles = entity.Roles!.Select(it => it.Id).ToList(); return dto; } /// /// 导出 /// /// public async Task Export(UserPagedParam param) { var dtos = await BuildQuery(param) .Select(it => new UserExportDto { Id = it.Id.SelectAll(), Roles = SqlFunc.Subqueryable() .LeftJoin((ur, ro) => ur.RoleId == ro.Id) .Where((ur, ro) => ur.UserId == it.Id) .SelectStringJoin((ur, ro) => ro.RoleName, ","), Position = it.Position!.PositionName, Org = it.Org!.OrgName }) .ToListAsync(); return await ExportFile(dtos, "用户信息.xlsx"); } /// /// 导入 /// /// [UnitOfWork] public async Task> ImportAsync(IFormCollection fileList) { if (fileList.Files.Count == 0) throw Oops.Oh("没有可导入的文件!"); List errors = new List(); var imports = await ImportFile(fileList.Files[0], errors); if (errors.Count > 0) return errors; var positionDict = (await _positionRepository.GetListAsync()).ToDictionary(it => it.PositionName, it => it); var roleDict = (await _roleRepository.GetListAsync()).ToDictionary(it => it.RoleName, it => it); Dictionary orgDict = new Dictionary(); List users = new List(); int headerIndex = 2 + 1; StringBuilder msgBuilder = new StringBuilder(); for (int i = 0; i < imports.Data.Count; i++) { msgBuilder.Clear(); var item = imports.Data.ElementAt(i); UserEntity userEntity = new UserEntity { UserName = item.UserName, RealName = item.RealName, NickName = item.RealName, Mobile = item.Mobile, AccountType = AccountTypeEnum.User, Email = item.Email, Gender = item.Gender == "男" ? 1 : 0, }; if (!string.IsNullOrEmpty(item.Position)) { if (positionDict.ContainsKey(item.Position!)) userEntity.Position = positionDict[item.Position]; else msgBuilder.Append($";找不到职位:【{item.Position}】!"); } if (!string.IsNullOrEmpty(item.Birthday)) { if (DateTime.TryParse(item.Birthday, out DateTime r)) userEntity.Birthday = r; else if (double.TryParse(item.Birthday, out double d)) userEntity.Birthday = DateTime.FromOADate(d); else msgBuilder.Append(";出生日期格式错误!"); } string[] roles = item.Roles!.Split(','); userEntity.Roles = new List(); foreach (var r in roles) { if (roleDict.ContainsKey(r)) userEntity.Roles.Add(roleDict[r]); else msgBuilder.Append($";没有找到角色【{r}】!"); } if (orgDict.ContainsKey(item.Org!)) userEntity.Org = orgDict[item.Org!]; else { var org = await _orgManager.GetOrgsByPath(item.Org!); if (org.Count == 1) { userEntity.Org = org.First(); orgDict.Add(item.Org!, org.First()); } else if (org.Count == 0) msgBuilder.Append($";没有找到机构【{item.Org}】!"); else msgBuilder.Append($";找到多个机构【{item.Org}】!"); } if (msgBuilder.Length > 0) errors.Add(new ImportErrorDto { Index = headerIndex + i, Errors = msgBuilder.ToString().Substring(1) }); else users.Add(userEntity); } //没有错误信息再进行入库 if (!errors.Any()) await _userRepository.AsSugarClient().InsertNav(users).Include(it => it.Org).Include(it => it.Position).Include(it => it.Roles).ExecuteCommandAsync(); return errors; } #region 私有方法 private ISugarQueryable BuildQuery(UserPagedParam param) { return _userRepository.AsQueryable() .WhereIF(!string.IsNullOrEmpty(param.Mobile), it => it.Mobile!.Contains(param.Mobile!)) .WhereIF(!string.IsNullOrEmpty(param.UserName), it => it.UserName.Contains(param.UserName!)) .WhereIF(param.Status > 0, it => it.Status == param.Status) .WhereIF(param.OrgId > 0, it => it.OrgId == param.OrgId) .Where(it => it.AccountType != AccountTypeEnum.SuperAdmin); } /// /// 生成Token信息 /// /// 用户id /// JwtToken private async Task DoLogin(long userId) { UserEntity? userInfo = await _userRepository.AsQueryable() .Includes(it => it.Roles) .Includes(it => it.Org) .Includes(it => it.Position) .Where(it => it.Id == userId) .FirstAsync(); userInfo.LoginCount += 1; if (userInfo.FirstVisit == null) userInfo.FirstVisit = DateTime.Now; userInfo.PreviousVisit = userInfo.LastVisit; userInfo.LastVisit = DateTime.Now; await _userRepository.UpdateAsync(userInfo); //生成 jwtToken JwtHelper jwtHelper = new JwtHelper(); JwtToken? token = jwtHelper.CreateToken(new JwtUserInfo() { UserName = userInfo!.UserName, AccountType = (int)userInfo.AccountType, NickName = userInfo.NickName, RealName = userInfo.RealName, TenantId = userInfo.TenantId, OrgId = userInfo.OrgId, UserId = userInfo.Id, }); //增加对应用户的refreshtoken await _refreshTokenRepository.InsertAsync(new RefreshTokenEntity() { ExpiresAt = token.RefreshTokenExpiresTime, IssuedAt = token.IssuedAt, RevokedAt = null, RefreshToken = token.RefreshToken!, UserId = userInfo.Id }); //缓存用户信息 await _userManager.InitCache(userInfo, token.TokenExpiresTime); return token; } #endregion } }