irc-netcore-api/IRaCIS.Core.Application/Service/Management/UserService.cs

1172 lines
48 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using DocumentFormat.OpenXml.Wordprocessing;
using IP2Region.Net.Abstractions;
using IRaCIS.Application.Contracts;
using IRaCIS.Core.Application.Auth;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure;
using MassTransit;
using Medallion.Threading;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Org.BouncyCastle.Utilities.Encoders;
using Panda.DynamicWebApi.Attributes;
using System.Text.RegularExpressions;
using ZiggyCreatures.Caching.Fusion;
using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO;
namespace IRaCIS.Core.Application.Service
{
[ApiExplorerSettings(GroupName = "Management")]
public class UserService(IRepository<User> _userRepository,
IMailVerificationService _mailVerificationService,
IRepository<VerificationCode> _verificationCodeRepository,
IRepository<TrialUser> _userTrialRepository,
IRepository<UserLog> _userLogRepository,
IRepository<UserPassWordLog> _userPassWordLogRepository,
IDistributedLockProvider _distributedLockProvider,
IRepository<Trial> _trialRepository,
IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig,
ISearcher _searcher, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IFusionCache _fusionCache) : BaseService, IUserService
{
private SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
private async Task VerifyUserNameAsync(Guid? identityUserId, string userName)
{
if (await _userRepository.WhereIf(identityUserId != null, t => t.IdentityUserId != identityUserId).AnyAsync(t => t.UserName == userName))
{
//---用户名已经存在。
throw new BusinessValidationFailedException(_localizer["User_UsernameExist"]);
}
}
private async Task VerifyUserPhoneAsync(Guid? identityUserId, string phone)
{
if (await _userRepository.WhereIf(identityUserId != null, t => t.IdentityUserId != identityUserId).AnyAsync(t => (t.Phone == phone)))
{
//---该用户类型中已存在具有相同的电话的用户。
throw new BusinessValidationFailedException(_localizer["User_PhoneDup"]);
}
}
private async Task VerifyUserEmailAsync(Guid? identityUserId, string email)
{
if (await _userRepository.WhereIf(identityUserId != null, t => t.IdentityUserId != identityUserId).AnyAsync(t => t.EMail == email))
{
//---该用户类型中已存在具有相同邮箱的用户。
throw new BusinessValidationFailedException(_localizer["User_EmailDup"]);
}
}
private async Task VerifyUserPwdAsync(Guid identityUserId, string newPwd, string? oldPwd = null)
{
//var dbUser = (await _userRepository.FirstOrDefaultAsync(t => t.Id == userId)).IfNullThrowException();
if (_verifyConfig.CurrentValue.OpenUserComplexPassword)
{
if (oldPwd != null && oldPwd == newPwd)
{
//---新密码与旧密码相同。
throw new BusinessValidationFailedException(_localizer["User_NewOldPwdSame"]);
}
var dbUser = (await _userRepository.Where(t => t.IdentityUserId == identityUserId).FirstOrDefaultAsync()).IfNullThrowException();
if (oldPwd != null && dbUser.Password != oldPwd)
{
//---旧密码验证失败。
throw new BusinessValidationFailedException(_localizer["User_OldPwdInvalid"]);
}
if (dbUser.Password == newPwd)
{
//---新密码与旧密码相同。
throw new BusinessValidationFailedException(_localizer["User_NewOldPwdSame"]);
}
var passWordList = await _userPassWordLogRepository.Where(x => x.IdentityUserId == identityUserId).OrderByDescending(x => x.CreateTime).Take(2).ToListAsync();
if (passWordList.Any(x => x.PassWord == newPwd))
{
throw new BusinessValidationFailedException(_localizer["User_PassWordRepeat"]);
}
}
if (oldPwd == null)
{
oldPwd = await _userRepository.Where(x => x.IdentityUserId == identityUserId).Select(x => x.Password).FirstOrDefaultAsync();
}
if (oldPwd.IsNotNullOrEmpty())
{
await _userPassWordLogRepository.AddAsync(new UserPassWordLog()
{
CreateTime = DateTime.Now,
PassWord = oldPwd!,
IdentityUserId = identityUserId,
});
}
await _userRepository.BatchUpdateNoTrackingAsync(x => x.IdentityUserId == identityUserId, x => new User()
{
LastChangePassWordTime = DateTime.Now,
});
await _userPassWordLogRepository.SaveChangesAsync();
}
/// <summary>发送验证码 修改邮箱(已经登陆修改) New </summary>
[HttpGet("{email}")]
public async Task<IResponseOutput> SendVerificationCode(string email)
{
//检查手机或者邮箱是否有效
if (!Regex.IsMatch(email, @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"))
{
//---Please input a legal email
return ResponseOutput.NotOk(_localizer["User_LegalEmail"]);
}
//验证码 6位
int verificationCode = new Random().Next(100000, 1000000);
await _mailVerificationService.SendMailEditEmail(_userInfo.Id, _userInfo.FullName, email, verificationCode);
return ResponseOutput.Ok();
}
[HttpPut("{newEmail}/{verificationCode}")]
[UnitOfWork]
public async Task<IResponseOutput> SetNewEmail(string newEmail, string verificationCode)
{
var verificationRecord = await _verificationCodeRepository
.FirstOrDefaultAsync(t => t.UserId == _userInfo.Id && t.Code == verificationCode && t.CodeType == 0);
//检查数据库是否存在该验证码
if (verificationRecord == null)
{
//---验证码错误。
return ResponseOutput.NotOk(_localizer["User_VerificationCodeError"]);
}
else
{
//检查验证码是否失效
if (verificationRecord.ExpirationTime < DateTime.Now)
{
//---验证码已经过期。
return ResponseOutput.NotOk(_localizer["User_VerificationCodeExpired"]);
}
else if (verificationRecord.EmailOrPhone.Trim() != newEmail.Trim())
{
//发送验证嘛的和提交的邮箱不一致
return ResponseOutput.NotOk(_localizer["User_VerificationEmailNotSameWithBefore"]);
}
else //验证码正确 并且 没有超时
{
await VerifyUserEmailAsync(_userInfo.IdentityUserId, newEmail);
await _userRepository.UpdatePartialFromQueryAsync(_userInfo.IdentityUserId, u => new User()
{
EMail = newEmail
});
//删除验证码历史记录
await _verificationCodeRepository.BatchDeleteNoTrackingAsync(t => t.UserId == _userInfo.Id && t.CodeType == 0);
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = _userInfo.Id, OptUserId = _userInfo.Id, OptType = UserOptType.UpdateUser }, true);
return ResponseOutput.Ok();
}
}
}
[HttpPut("{newPhone}")]
public async Task<IResponseOutput> SetNewPhone(string newPhone)
{
await VerifyUserPhoneAsync(_userInfo.IdentityUserId, newPhone);
await _userRepository.UpdatePartialFromQueryAsync(_userInfo.IdentityUserId, u => new User()
{
Phone = newPhone
});
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = _userInfo.Id, OptUserId = _userInfo.Id, OptType = UserOptType.UpdateUser }, true);
return ResponseOutput.Ok();
}
[HttpPut("{newUserName}")]
public async Task<IResponseOutput> SetNewUserName(string newUserName)
{
await VerifyUserNameAsync(_userInfo.IdentityUserId, newUserName);
await _userRepository.UpdatePartialFromQueryAsync(_userInfo.IdentityUserId, u => new User()
{
UserName = newUserName
});
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = _userInfo.Id, OptUserId = _userInfo.Id, OptType = UserOptType.UpdateUser }, true);
return ResponseOutput.Ok();
}
[HttpGet]
public async Task<IResponseOutput> InitSetUserNameAndPwd(string newUserName, string newPWd)
{
await VerifyUserPwdAsync(_userInfo.IdentityUserId, newPWd);
await VerifyUserNameAsync(_userInfo.IdentityUserId, newUserName);
await _userRepository.UpdatePartialFromQueryAsync(_userInfo.IdentityUserId, u => new User()
{
UserName = newUserName,
Password = newPWd,
IsFirstAdd = false,
EmailToken = String.Empty
}, true);
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = _userInfo.Id, OptUserId = _userInfo.Id, OptType = UserOptType.UpdateUser }, true);
return ResponseOutput.Ok();
}
/// <summary>
/// 重置密码为 默认密码
/// </summary>
/// <param name="identityUserId"></param>
/// <returns></returns>
[HttpGet("{identityUserId:guid}")]
[UnitOfWork]
public async Task<IResponseOutput> ResetPassword(Guid identityUserId)
{
var pwd = IRCEmailPasswordHelper.GenerateRandomPassword(10);
await _mailVerificationService.AdminResetPwdSendEmailAsync(identityUserId, pwd);
await _userRepository.UpdatePartialFromQueryAsync(t => t.IdentityUserId == identityUserId, u => new User()
{
Password = MD5Helper.Md5(pwd),
IsFirstAdd = true
});
var userName = _userRepository.Where(t => t.IdentityUserId == identityUserId).Select(t => t.UserName).FirstOrDefault();
await _fusionCache.RemoveAsync(CacheKeys.UserLoginError(userName));
//await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = _userInfo.Id, OptUserId = userId, OptType = UserOptType.ResetPassword }, true);
return ResponseOutput.Ok();
}
/// <summary>
/// 重置密码发邮件 (未登陆修改)
/// </summary>
/// <param name="email"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("{email}")]
public async Task<IResponseOutput> AnonymousSendVerificationCode(string email)
{
//检查手机或者邮箱是否有效
if (!Regex.IsMatch(email, @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"))
{
//---请输入一个正确的邮箱。
return ResponseOutput.NotOk(_localizer["User_InvalidEmail"]);
}
////查找改邮箱或者手机的用户
var exist = await _userRepository.AnyAsync(t => t.EMail == email);
if (!exist)
{
//---邮箱错误。
return ResponseOutput.NotOk(_localizer["User_EmailError"]);
}
//验证码 6位
int verificationCode = new Random().Next(100000, 1000000);
await _mailVerificationService.AnolymousSendEmailForResetAccount(email, verificationCode);
return ResponseOutput.Ok();
}
/// <summary>
/// 验证验证码,没问题就返回用户所有的账户
/// </summary>
/// <param name="email"></param>
/// <param name="verifyCode"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
[AllowAnonymous]
[HttpGet("{email}/{verifyCode}")]
public async Task<List<UserAccountDto>> VerifyAnonymousVerifyCode(string email, string verifyCode)
{
var verificationRecord = await _verificationCodeRepository
.Where(t => t.UserId == Guid.Empty && t.Code == verifyCode && t.CodeType == VerifyType.Email && t.EmailOrPhone == email).OrderByDescending(t => t.CreateTime).FirstOrDefaultAsync();
//检查数据库是否存在该验证码
if (verificationRecord == null)
{
//---验证码错误。
throw new BusinessValidationFailedException(_localizer["User_VerificationCodeError"]);
}
else
{
//检查验证码是否失效
if (verificationRecord.ExpirationTime < DateTime.Now)
{
//---验证码已经过期。
throw new BusinessValidationFailedException(_localizer["User_VerificationCodeExpired"]);
}
else //验证码正确 并且 没有超时
{
//删除验证码历史记录
await _verificationCodeRepository.BatchDeleteNoTrackingAsync(t => t.Id == verificationRecord.Id);
}
}
var list = await _userRepository.Where(t => t.EMail == email && t.Status == UserStateEnum.Enable).Select(t => new UserAccountDto() { UserId = t.Id, UserName = t.UserName, UserRealName = t.FullName, UserType = t.UserTypeRole.UserTypeShortName }).ToListAsync();
return list;
}
/// <summary>
/// (未登陆) 设置新密码
/// </summary>
/// <param name="identityUserId"></param>
/// <param name="newPwd"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("{userId:guid}/{newPwd}")]
public async Task<IResponseOutput> AnonymousSetPassword(Guid identityUserId, string newPwd)
{
await VerifyUserPwdAsync(identityUserId, newPwd);
var success = await _userRepository.BatchUpdateNoTrackingAsync(t => t.IdentityUserId == identityUserId, u => new User()
{
Password = newPwd,
IsFirstAdd = false
});
//await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = userId, OptUserId = userId, LoginPassword = newPwd, OptType = UserOptType.UnloginModifyPasswoed }, true);
return ResponseOutput.Result(success);
}
/// <summary>
/// 修改密码,当前支持旧密码修改密码
/// </summary>
/// <returns></returns>
[HttpPost]
[UnitOfWork]
public async Task<IResponseOutput> ModifyPassword(EditPasswordCommand editPwModel)
{
await VerifyUserPwdAsync(_userInfo.IdentityUserId, editPwModel.NewPassWord, editPwModel.OldPassWord);
if (!string.IsNullOrEmpty(editPwModel.NewUserName))
{
await VerifyUserNameAsync(_userInfo.Id, editPwModel.NewUserName);
await _userRepository.BatchUpdateNoTrackingAsync(t => t.IdentityUserId == _userInfo.IdentityUserId, u => new User()
{
UserName = editPwModel.NewUserName,
});
}
var success = await _userRepository.BatchUpdateNoTrackingAsync(t => t.IdentityUserId == _userInfo.IdentityUserId, u => new User()
{
Password = editPwModel.NewPassWord,
IsFirstAdd = false
});
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = _userInfo.Id, OptUserId = _userInfo.Id, OptType = UserOptType.LoginModifyPassword }, true);
return ResponseOutput.Result(success);
}
/// <summary>
/// 获取用户列表
/// </summary>
/// <param name="inQuery"></param>
/// <returns></returns>
[HttpPost]
public async Task<PageOutput<UserListDTO>> GetUserList(UserListQueryDTO inQuery)
{
var userQueryable = _userRepository.Where(x => x.UserTypeEnum != UserTypeEnum.SuperAdmin)
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.UserName), t => t.UserName.Contains(inQuery.UserName))
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.RealName), t => t.FullName.Contains(inQuery.RealName))
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.Phone), t => t.Phone.Contains(inQuery.Phone))
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.OrganizationName), t => t.OrganizationName.Contains(inQuery.OrganizationName))
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.EMail), t => t.EMail.Contains(inQuery.EMail))
.WhereIf(inQuery.BeginCreateTime != null, t => t.CreateTime >= inQuery.BeginCreateTime)
.WhereIf(inQuery.EndCreateTime != null, t => t.CreateTime <= inQuery.EndCreateTime)
.WhereIf(inQuery.BeginLastLoginTime != null, t => t.LastLoginTime >= inQuery.BeginLastLoginTime)
.WhereIf(inQuery.EndLastLoginTime != null, t => t.LastLoginTime <= inQuery.EndLastLoginTime)
.WhereIf(inQuery.UserType != null, t => t.UserTypeId == inQuery.UserType)
.WhereIf(inQuery.UserState != null, t => t.Status == inQuery.UserState)
.WhereIf(inQuery.IsTestUser != null, t => t.IsTestUser == inQuery.IsTestUser)
.WhereIf(inQuery.IsZhiZhun != null, t => t.IsZhiZhun == inQuery.IsZhiZhun)
.GroupBy(t => t.IdentityUserId).Select(g => new UserListDTO()
{
IdentityUserId = g.Key,
UserRoleList = g.Select(c => new UserAddUserType()
{
UserTypeEnum = c.UserTypeEnum,
UserTypeId = c.UserTypeId,
}).ToList(),
UserCode = g.FirstOrDefault().UserCode,
DepartmentName = g.FirstOrDefault().DepartmentName,
CreateTime = g.FirstOrDefault().CreateTime,
EMail = g.FirstOrDefault().EMail,
FirstName = g.FirstOrDefault().FirstName,
IsTestUser = g.FirstOrDefault().IsTestUser,
IsZhiZhun = g.FirstOrDefault().IsZhiZhun,
LastName = g.FirstOrDefault().LastName,
OrganizationName = g.FirstOrDefault().OrganizationName,
FullName = g.FirstOrDefault().FullName,
Phone = g.FirstOrDefault().Phone,
PositionName = g.FirstOrDefault().PositionName,
Sex = g.FirstOrDefault().Sex,
Status = g.FirstOrDefault().Status,
UserName = g.FirstOrDefault().UserName,
});
return await userQueryable.ToPagedListAsync(inQuery);
}
/// <summary>
/// 根据用户Id获取用户详细信息[New]
/// </summary>
/// <returns></returns>xiuga
public async Task<UserDetailDTO> GetUser()
{
var userQuery = _userRepository.Where(t => t.IdentityUserId == _userInfo.IdentityUserId).ProjectTo<UserDetailDTO>(_mapper.ConfigurationProvider);
var userRole = await userQuery.FirstOrDefaultAsync();
userRole.AccountList = await _userRepository.Where(t => t.IdentityUserId == _userInfo.IdentityUserId).ProjectTo<UserAccountInfo>(_mapper.ConfigurationProvider).ToListAsync();
return userRole;
}
///// <summary>
///// 删除用户
///// </summary>
///// <param name="userId"></param>
///// <returns></returns>
//[HttpDelete("{userId:guid}")]
//public async Task<IResponseOutput> DeleteUser(Guid userId)
//{
// if (await _userTrialRepository.AnyAsync(t => t.Id == userId))
// {
// //---该用户已经参加项目,不能够删除。
// return ResponseOutput.NotOk(_localizer["User_InProject"]);
// }
// await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = _userInfo.Id, OptUserId = userId, OptType = UserOptType.DeleteUser }, true);
// var success = await _userRepository.BatchDeleteNoTrackingAsync(t => t.Id == userId);
// return ResponseOutput.Result(success);
//}
/// <summary>
/// 添加用户
/// </summary>
/// <param name="userAddModel"></param>
/// <returns></returns>
[UnitOfWork]
public async Task<IResponseOutput<UserAddedReturnDTO>> AddUser(UserCommand userAddModel)
{
await VerifyUserNameAsync(null, userAddModel.UserName);
await VerifyUserEmailAsync(null, userAddModel.EMail);
//await VerifyUserPhoneAsync(null, userAddModel.UserTypeId, userAddModel.Phone);
var saveItem = _mapper.Map<User>(userAddModel);
//设置用户标识
saveItem.IdentityUserId = NewId.NextSequentialGuid();
var @lock = _distributedLockProvider.CreateLock($"UserAccount");
using (await @lock.AcquireAsync())
{
saveItem.Code = await _userRepository.Select(t => t.Code).DefaultIfEmpty().MaxAsync() + 1;
saveItem.UserCode = AppSettings.GetCodeStr(saveItem.Code, nameof(User));
if (saveItem.IsZhiZhun)
{
var organizationName = _userInfo.IsEn_Us ? _systemEmailConfig.OrganizationName : _systemEmailConfig.OrganizationNameCN;
saveItem.OrganizationName = organizationName;
}
saveItem.Password = MD5Helper.Md5(IRCEmailPasswordHelper.GenerateRandomPassword(10));
}
//处理多个角色
foreach (var item in userAddModel.UserRoleList)
{
var cloneUser = saveItem.Clone();
cloneUser.UserTypeId = item.UserTypeId;
cloneUser.UserTypeEnum = item.UserTypeEnum;
cloneUser.IsUserRoleDisabled = item.IsUserRoleDisabled;
await _userRepository.AddAsync(cloneUser);
}
var success = await _userRepository.SaveChangesAsync();
await _mailVerificationService.AddUserSendEmailAsync(saveItem.IdentityUserId, userAddModel.BaseUrl, userAddModel.RouteUrl);
return ResponseOutput.Ok(new UserAddedReturnDTO { Id = saveItem.IdentityUserId, UserCode = saveItem.UserCode });
}
/// <summary>
/// 更新用户
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public async Task<IResponseOutput> UpdateUser(UserCommand model)
{
await VerifyUserNameAsync(model.IdentityUserId, model.UserName);
await VerifyUserEmailAsync(model.IdentityUserId, model.EMail);
//await VerifyUserPhoneAsync(model.Id, model.UserTypeId, model.Phone);
var userRoleList = await _userRepository.Where(t => t.IdentityUserId == model.IdentityUserId, true).ToListAsync();
if (userRoleList.Any(t => t.Status != model.Status))
{
//await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = _userInfo.Id, OptUserId = model.Id, OptType = model.Status == UserStateEnum.Enable ? UserOptType.AccountEnable : UserOptType.AccountLocked }, true);
}
if (model.IsZhiZhun)
{
model.OrganizationName = _userInfo.IsEn_Us ? _systemEmailConfig.OrganizationName : _systemEmailConfig.OrganizationNameCN;
}
//await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = _userInfo.Id, OptUserId = model.Id, OptType = UserOptType.UpdateUser });
foreach (var role in userRoleList)
{
role.UserName = model.UserName;
role.FirstName = model.FirstName;
role.LastName = model.LastName;
role.Phone = model.Phone;
role.EMail = model.EMail;
role.DepartmentName = model.DepartmentName;
role.OrganizationName = model.OrganizationName;
role.Sex = model.Sex;
role.Status = model.Status;
role.UserCode = model.UserCode;
role.PositionName = model.PositionName;
role.IsTestUser = model.IsTestUser;
role.IsZhiZhun = model.IsZhiZhun;
role.IsUserRoleDisabled = model.UserRoleList.FirstOrDefault(t => t.UserTypeId == role.UserTypeId)?.IsUserRoleDisabled ?? false;
}
var needAddRoleList = model.UserRoleList.Where(t => !userRoleList.Any(c => c.UserTypeId == t.UserTypeId)).ToList();
foreach (var addRole in needAddRoleList)
{
var cloneUser = userRoleList.FirstOrDefault().Clone();
cloneUser.UserTypeId = addRole.UserTypeId;
cloneUser.UserTypeEnum = addRole.UserTypeEnum;
cloneUser.IsUserRoleDisabled = addRole.IsUserRoleDisabled;
await _userRepository.AddAsync(cloneUser);
}
var success = await _userRepository.SaveChangesAsync();
return ResponseOutput.Ok(success);
}
/// <summary>
/// 禁用或者启用账户
/// </summary>
/// <param name="userId"></param>
/// <param name="state"></param>
/// <returns></returns>
[HttpPost("{userId:guid}/{state:int}")]
public async Task<IResponseOutput> UpdateUserState(Guid userId, UserStateEnum state)
{
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = _userInfo.Id, OptUserId = userId, OptType = state == UserStateEnum.Enable ? UserOptType.AccountEnable : UserOptType.AccountLocked }, true);
var success = await _userRepository.BatchUpdateNoTrackingAsync(u => u.IdentityUserId == userId, t => new User
{
Status = state
});
return ResponseOutput.Result(success);
}
public async Task<UserBasicInfo> GetUserBasicInfo(Guid userId, string pwd)
{
var info = await _userRepository.Where(u => u.Id == userId && u.Password == pwd).ProjectTo<UserBasicInfo>(_mapper.ConfigurationProvider).FirstNotNullAsync();
return info;
}
/// <summary>
/// 发送MFA 验证邮件
/// </summary>
/// <param name="userId"></param>
/// <param name="mfaType"></param>
/// <returns></returns>
[AllowAnonymous]
public async Task<IResponseOutput> SendMFAEmail(Guid userId, UserMFAType mfaType)
{
var userInfo = await _userRepository.Where(u => u.Id == userId).Select(t => new { t.FullName, t.EMail }).FirstOrDefaultAsync();
int verificationCode = new Random().Next(100000, 1000000);
await _mailVerificationService.SenMFAVerifyEmail(userId, userInfo.FullName, userInfo.EMail, verificationCode, (UserMFAType)mfaType);
var hiddenEmail = IRCEmailPasswordHelper.MaskEmail(userInfo.EMail);
return ResponseOutput.Ok(hiddenEmail);
}
/// <summary>
/// 验证MFA 邮件
/// </summary>
/// <param name="userId"></param>
/// <param name="Code"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
[AllowAnonymous]
public async Task<IResponseOutput> VerifyMFACodeAsync(Guid userId, string Code)
{
var verificationRecord = await _verificationCodeRepository.Where(t => t.UserId == userId && t.Code == Code && t.CodeType == VerifyType.Email).OrderByDescending(x => x.ExpirationTime).FirstOrDefaultAsync();
VerifyEmialGetDoctorInfoOutDto result = new VerifyEmialGetDoctorInfoOutDto();
//检查数据库是否存在该验证码
if (verificationRecord == null)
{
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = userId, OptUserId = userId, OptType = UserOptType.MFALoginFail }, true);
//---验证码错误。
throw new BusinessValidationFailedException(_localizer["TrialSiteSurvey_WrongVerificationCode"]);
}
else
{
//检查验证码是否失效
if (verificationRecord.ExpirationTime < DateTime.Now)
{
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = userId, OptUserId = userId, OptType = UserOptType.MFALoginFail }, true);
//---验证码已经过期。
throw new BusinessValidationFailedException(_localizer["TrialSiteSurvey_ExpiredVerificationCode"]);
}
else //验证码正确 并且 没有超时
{
//删除验证码历史记录
await _verificationCodeRepository.BatchDeleteNoTrackingAsync(t => t.Id == verificationRecord.Id);
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = userId, OptUserId = userId, OptType = UserOptType.MFALogin }, true);
}
}
return ResponseOutput.Ok();
}
/// <summary>
/// 用户登陆
/// </summary>
/// <param name="userName"></param>
/// <param name="password"></param>
/// <returns></returns>
[NonDynamicMethod]
public async Task<IResponseOutput<LoginReturnDTO>> Login(string userName, string password)
{
int maxFailures = _verifyConfig.CurrentValue.LoginMaxFailCount;
int lockoutMinutes = _verifyConfig.CurrentValue.LoginFailLockMinutes;
// 生成缓存键
string cacheKey = CacheKeys.UserLoginError(userName);
// 从缓存中获取登录失败次数
int? failCount = await _fusionCache.GetOrDefaultAsync<int?>(cacheKey);
if (failCount == null)
{
failCount = 0;
}
//每次登录 都重置缓存时间
await _fusionCache.SetAsync<int?>(cacheKey, failCount, TimeSpan.FromMinutes(lockoutMinutes));
if (failCount >= maxFailures)
{
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = Guid.Empty, OptUserId = Guid.Empty, LoginFaildName = userName, LoginPassword = password, OptType = UserOptType.AccountLocked }, true);
//$"密码连续错误{maxFailures}次,当前账号已被限制登录,请等待 {lockoutMinutes} 分钟后再试。"
throw new BusinessValidationFailedException(_localizer["User_ErrorLimit", maxFailures, lockoutMinutes]);
}
var userLoginReturnModel = new LoginReturnDTO();
var loginUser = await _userRepository.Where(u => u.UserName.Equals(userName) && u.Password == password).ProjectTo<UserBasicInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
if (loginUser == null)
{
//错误次数累加
failCount++;
await _fusionCache.SetAsync(cacheKey, failCount, TimeSpan.FromMinutes(lockoutMinutes));
var errorPwdUserId = await _userRepository.Where(u => u.UserName == userName).Select(t => t.Id).FirstOrDefaultAsync();
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = errorPwdUserId, OptUserId = errorPwdUserId, LoginFaildName = userName, LoginPassword = password, OptType = UserOptType.AccountOrPasswordError }, true);
return ResponseOutput.NotOk(_localizer["User_CheckNameOrPw"], new LoginReturnDTO());
}
if (loginUser.Status == 0)
{
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = loginUser.Id, OptUserId = loginUser.Id, LoginFaildName = userName, OptType = UserOptType.LoginLockedAccount }, true);
//---该用户已经被禁用。
return ResponseOutput.NotOk(_localizer["User_Disabled"], new LoginReturnDTO());
}
//登录成功 清除缓存
await _fusionCache.SetAsync(cacheKey, 0, TimeSpan.FromMinutes(lockoutMinutes));
var ipinfo = _searcher.Search(_userInfo.IP);
var iPRegion = string.Join('|', ipinfo.Split('|').TakeLast(3));
if (loginUser.LastLoginIP != string.Empty)
{
// 与上一次IP不一致
if (loginUser.LastLoginIP != iPRegion)
{
loginUser.LoginState = 2;
}
}
//超过90天没修改密码
if (_verifyConfig.CurrentValue.IsNeedChangePassWord && loginUser.LastChangePassWordTime != null && DateTime.Now.AddDays(-_verifyConfig.CurrentValue.ChangePassWordDays) > loginUser.LastChangePassWordTime.Value)
{
loginUser.LoginState = 1;
}
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = loginUser.Id, OptUserId = loginUser.Id, OptType = UserOptType.Login }, true);
userLoginReturnModel.BasicInfo = loginUser;
if (loginUser.LastChangePassWordTime == null)
{
await _userRepository.BatchUpdateNoTrackingAsync(x => x.Id == loginUser.Id, x => new User()
{
LastChangePassWordTime = DateTime.Now
});
}
await _userRepository.BatchUpdateNoTrackingAsync(x => x.Id == loginUser.Id, x => new User()
{
LastLoginIP = iPRegion,
LastLoginTime = DateTime.Now
});
return ResponseOutput.Ok(userLoginReturnModel);
}
[HttpPost]
public async Task<PageOutput<UserLogView>> GetUserLogList(UserLogQuery inQuery)
{
DateTime? trialCreateTime = inQuery.TrialId != null ? _trialRepository.Where(t => t.Id == inQuery.TrialId).Select(t => t.CreateTime).FirstOrDefault() : null;
var userLogQueryable =
_userLogRepository.AsQueryable().IgnoreQueryFilters()
.WhereIf(inQuery.TrialId != null, t => t.LoginUser.UserTrials.Any(c => c.TrialId == inQuery.TrialId && (c.UserId == t.LoginUserId || c.UserId == t.OptUserId)))
.WhereIf(trialCreateTime != null, t => t.CreateTime >= trialCreateTime)
.WhereIf(inQuery.OptType != null, t => t.OptType == inQuery.OptType)
.WhereIf(inQuery.UserId != null, t => t.LoginUserId == inQuery.UserId)
.WhereIf(inQuery.BeginDate != null, t => t.CreateTime >= inQuery.BeginDate)
.WhereIf(inQuery.EndDate != null, t => t.CreateTime <= inQuery.EndDate)
.WhereIf(!string.IsNullOrEmpty(inQuery.LoginUserName), t => t.LoginUser.UserName.Contains(inQuery.LoginUserName!))
.WhereIf(!string.IsNullOrEmpty(inQuery.LoginFaildName), t => t.LoginFaildName.Contains(inQuery.LoginFaildName!))
.WhereIf(!string.IsNullOrEmpty(inQuery.IP), t => t.IP.Contains(inQuery.IP!))
.WhereIf(inQuery.LoginUserTypeEnum != null, t => t.LoginUser.UserTypeEnum == inQuery.LoginUserTypeEnum)
.WhereIf(inQuery.UserTypeId != null, t => t.LoginUser.UserTypeId == inQuery.UserTypeId)
.ProjectTo<UserLogView>(_mapper.ConfigurationProvider);
var pageList = await userLogQueryable.ToPagedListAsync(inQuery);
return pageList;
}
[AllowAnonymous]
[HttpGet]
public async Task<IResponseOutput> LoginOut(Guid identityUserId)
{
await _fusionCache.RemoveAsync(CacheKeys.UserToken(identityUserId));
//await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = userId, OptUserId = _userInfo.Id, OptType = UserOptType.LoginOut }, true);
return ResponseOutput.Ok();
}
#region 多账号修改
/// <summary>
/// 账号验证,获取账号角色信息 获取临时token
/// </summary>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
[AllowAnonymous]
[HttpPost]
public async Task<IResponseOutput<IRCLoginReturnDTO>> GetUserLoginRoleList(IRCLoginDto loginDto,
[FromServices] ITokenService _tokenService,
[FromServices] IOptionsMonitor<SystemEmailSendConfig> _emailConfig,
[FromServices] IReadingImageTaskService readingImageTaskService)
{
var userName = loginDto.UserName;
var password = loginDto.Password;
var emailConfig = _emailConfig.CurrentValue;
var companyInfo = new SystemEmailSendConfigView() { CompanyName = emailConfig.CompanyName, CompanyNameCN = emailConfig.CompanyNameCN, CompanyShortName = emailConfig.CompanyShortName, CompanyShortNameCN = emailConfig.CompanyShortNameCN };
int maxFailures = _verifyConfig.CurrentValue.LoginMaxFailCount;
int lockoutMinutes = _verifyConfig.CurrentValue.LoginFailLockMinutes;
// 生成缓存键
string cacheKey = CacheKeys.UserLoginError(userName);
// 从缓存中获取登录失败次数
int? failCount = await _fusionCache.GetOrDefaultAsync<int?>(cacheKey);
if (failCount == null)
{
failCount = 0;
}
//每次登录 都重置缓存时间
await _fusionCache.SetAsync<int?>(cacheKey, failCount, TimeSpan.FromMinutes(lockoutMinutes));
if (failCount >= maxFailures)
{
//await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = Guid.Empty, OptUserId = Guid.Empty, LoginFaildName = userName, LoginPassword = password, OptType = UserOptType.AccountLocked }, true);
//$"密码连续错误{maxFailures}次,当前账号已被限制登录,请等待 {lockoutMinutes} 分钟后再试。"
throw new BusinessValidationFailedException(_localizer["User_ErrorLimit", maxFailures, lockoutMinutes]);
}
var userLoginReturnModel = new IRCLoginReturnDTO();
var loginUser = await _userRepository.Where(u => u.UserName.Equals(userName) && u.Password == password).ProjectTo<UserBasicInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
if (loginUser == null)
{
//错误次数累加
failCount++;
await _fusionCache.SetAsync(cacheKey, failCount, TimeSpan.FromMinutes(lockoutMinutes));
var errorPwdUserId = await _userRepository.Where(u => u.UserName == userName).Select(t => t.Id).FirstOrDefaultAsync();
//await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = errorPwdUserId, OptUserId = errorPwdUserId, LoginFaildName = userName, LoginPassword = password, OptType = UserOptType.AccountOrPasswordError }, true);
return ResponseOutput.NotOk(_localizer["User_CheckNameOrPw"], new IRCLoginReturnDTO());
}
if (loginUser.Status == 0)
{
//await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = loginUser.Id, OptUserId = loginUser.Id, LoginFaildName = userName, OptType = UserOptType.LoginLockedAccount }, true);
//---该用户已经被禁用。
return ResponseOutput.NotOk(_localizer["User_Disabled"], new IRCLoginReturnDTO());
}
//登录成功 清除缓存
await _fusionCache.SetAsync(cacheKey, 0, TimeSpan.FromMinutes(lockoutMinutes));
var ipinfo = _searcher.Search(_userInfo.IP);
var iPRegion = string.Join('|', ipinfo.Split('|').TakeLast(3));
if (loginUser.LastLoginIP != string.Empty)
{
// 与上一次IP不一致
if (loginUser.LastLoginIP != iPRegion)
{
loginUser.LoginState = 2;
}
}
//超过90天没修改密码
if (_verifyConfig.CurrentValue.IsNeedChangePassWord && loginUser.LastChangePassWordTime != null && DateTime.Now.AddDays(-_verifyConfig.CurrentValue.ChangePassWordDays) > loginUser.LastChangePassWordTime.Value)
{
loginUser.LoginState = 1;
}
//await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = loginUser.Id, OptUserId = loginUser.Id, OptType = UserOptType.Login }, true);
userLoginReturnModel.BasicInfo = loginUser;
if (loginUser.LastChangePassWordTime == null)
{
await _userRepository.BatchUpdateNoTrackingAsync(x => x.IdentityUserId == loginUser.IdentityUserId, x => new User()
{
LastChangePassWordTime = DateTime.Now
});
}
await _userRepository.BatchUpdateNoTrackingAsync(x => x.IdentityUserId == loginUser.IdentityUserId, x => new User()
{
LastLoginIP = iPRegion,
LastLoginTime = DateTime.Now
});
//返回临时token
userLoginReturnModel.JWTStr = _tokenService.GetToken(new UserTokenInfo() { IdentityUserId = loginUser.IdentityUserId, UserName = userName });
var userId = loginUser.Id;
var identityUserId = loginUser.IdentityUserId;
if (_verifyConfig.CurrentValue.OpenLoginMFA)
{
//MFA 发送邮件
userLoginReturnModel.IsMFA = true;
var email = userLoginReturnModel.BasicInfo.EMail;
var hiddenEmail = IRCEmailPasswordHelper.MaskEmail(email);
userLoginReturnModel.BasicInfo.EMail = hiddenEmail;
//修改密码
if (userLoginReturnModel.BasicInfo.IsFirstAdd || userLoginReturnModel.BasicInfo.LoginState == 1)
{
//userLoginReturnModel.JWTStr = _tokenService.GetToken(userLoginReturnModel.BasicInfo);
}
else
{
//正常登录才发送邮件
await SendMFAEmail(userId, UserMFAType.Login);
}
}
else
{
// 验证阅片休息时间
await readingImageTaskService.ResetReadingRestTime(userLoginReturnModel.BasicInfo.Id);
await _fusionCache.SetAsync(CacheKeys.UserToken(identityUserId), userLoginReturnModel.JWTStr, TimeSpan.FromDays(7));
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(identityUserId), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes));
}
userLoginReturnModel.BasicInfo.AccountList = await _userRepository.Where(t => t.IdentityUserId == identityUserId).ProjectTo<UserAccountInfo>(_mapper.ConfigurationProvider).ToListAsync();
userLoginReturnModel.CompanyInfo = companyInfo;
return ResponseOutput.Ok(userLoginReturnModel);
}
/// <summary>
/// 验证密码成功后选定角色然后获取当前角色的Token
/// </summary>
/// <param name="userRoleId"></param>
/// <param name="_tokenService"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
[HttpGet]
public async Task<string> LoginSelectUserRole(Guid userRoleId, [FromServices] ITokenService _tokenService)
{
var identityUserId = _userInfo.IdentityUserId;
var userTokenInfo = await _userRepository.Where(t => t.IdentityUserId == identityUserId && t.Id == userRoleId).Select(t => new UserTokenInfo()
{
UserRoleId = t.Id,
IdentityUserId = t.IdentityUserId,
UserTypeEnum = t.UserTypeEnum,
UserTypeId = t.UserTypeId,
IsTestUser = t.IsTestUser,
IsZhiZhun = t.IsZhiZhun,
FullName = t.FullName,
PermissionStr = t.UserTypeRole.PermissionStr,
UserName = t.UserName,
UserTypeShortName = t.UserTypeRole.UserTypeShortName,
}).FirstOrDefaultAsync();
if (userTokenInfo != null)
{
var jwt = _tokenService.GetToken(userTokenInfo);
var selectUserInfo = _userRepository.Where(t => t.IdentityUserId == identityUserId && t.Id == userRoleId).FirstOrDefault();
//多账号没维护
if (await _userRepository.Where(t => t.IdentityUserId == identityUserId).AnyAsync(t => t.IsMutiAccountInfoConfirm == false))
{
//将信息维护为一致
await _userRepository.BatchUpdateNoTrackingAsync(t => t.IdentityUserId == identityUserId, u => new User()
{
Code = selectUserInfo.Code,
UserName = selectUserInfo.UserName,
FirstName = selectUserInfo.FirstName,
LastName = selectUserInfo.LastName,
Phone = selectUserInfo.Phone,
EMail = selectUserInfo.EMail,
DepartmentName = selectUserInfo.DepartmentName,
OrganizationName = selectUserInfo.OrganizationName,
IsFirstAdd = selectUserInfo.IsFirstAdd,
Sex = selectUserInfo.Sex,
Status = selectUserInfo.Status,
UserCode = selectUserInfo.UserCode,
Password = selectUserInfo.Password,
PasswordChanged = selectUserInfo.PasswordChanged,
LastChangePassWordTime = selectUserInfo.LastChangePassWordTime,
PositionName = selectUserInfo.PositionName,
IsTestUser = selectUserInfo.IsTestUser,
IsZhiZhun = selectUserInfo.IsZhiZhun,
AutoCutNextTask = selectUserInfo.AutoCutNextTask,
//DoctorId=selectUserInfo.DoctorId,
LastLoginIP = selectUserInfo.LastLoginIP,
LastLoginTime = selectUserInfo.LastLoginTime,
});
}
return jwt;
//return new LoginSelectRoleReturn() { AccountList = accountList, JwtStr = jwt };
}
else
{
throw new BusinessValidationFailedException("传递参数查询数据库不存在!");
}
}
#endregion
}
}