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

1150 lines
46 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.Spreadsheet;
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.Service.OAuth;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Models;
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<UserRole> _userRoleRepository,
IMailVerificationService _mailVerificationService,
IRepository<VerificationCode> _verificationCodeRepository,
IRepository<TrialUserRole> _userTrialRepository,
IRepository<UserLog> _userLogRepository,
IRepository<UserPassWordLog> _userPassWordLogRepository,
IDistributedLockProvider _distributedLockProvider,
IRepository<Trial> _trialRepository,
IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig,
IRepository<IdentityUser> _identityUserRepository,
IRepository<Doctor> _doctorRepository,
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 _identityUserRepository.WhereIf(identityUserId != null, t => t.Id != identityUserId).AnyAsync(t => t.UserName == userName))
{
//---用户名已经存在。
throw new BusinessValidationFailedException(_localizer["User_UsernameExist"]);
}
}
private async Task VerifyUserPhoneAsync(Guid? identityUserId, string phone)
{
if (await _identityUserRepository.WhereIf(identityUserId != null, t => t.Id != identityUserId).AnyAsync(t => (t.Phone == phone)))
{
//---该用户类型中已存在具有相同的电话的用户。
throw new BusinessValidationFailedException(_localizer["User_PhoneDup"]);
}
}
private async Task VerifyUserEmailAsync(Guid? identityUserId, string email)
{
if (await _identityUserRepository.WhereIf(identityUserId != null, t => t.Id != 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 _userRoleRepository.FirstOrDefaultAsync(t => t.Id == userId)).IfNullThrowException();
if (_verifyConfig.CurrentValue.OpenUserComplexPassword)
{
if (oldPwd != null && oldPwd == newPwd)
{
//---新密码与旧密码相同。
throw new BusinessValidationFailedException(_localizer["User_NewOldPwdSame"]);
}
var dbUser = (await _identityUserRepository.Where(t => t.Id == 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(3).ToListAsync();
if (passWordList.Any(x => x.PassWord == newPwd))
{
throw new BusinessValidationFailedException(_localizer["User_PassWordRepeat"]);
}
}
await _identityUserRepository.BatchUpdateNoTrackingAsync(x => x.Id == identityUserId, x => new IdentityUser()
{
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.IdentityUserId, _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.IdentityUserId && 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);
// 修改用户邮箱的时候 需要修改医生表的邮箱
var oldEmail = await _identityUserRepository.Where(t => t.Id == _userInfo.IdentityUserId).Select(t => t.EMail).FirstOrDefaultAsync();
await _doctorRepository.UpdatePartialFromQueryAsync(x => x.EMail == oldEmail, u => new Doctor()
{
EMail = newEmail
});
await _identityUserRepository.UpdatePartialFromQueryAsync(_userInfo.IdentityUserId, u => new IdentityUser()
{
EMail = newEmail
});
//删除验证码历史记录
await _verificationCodeRepository.BatchDeleteNoTrackingAsync(t => t.UserId == _userInfo.UserRoleId && t.CodeType == 0);
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = _userInfo.IdentityUserId, OptType = UserOptType.UpdateUser }, true);
return ResponseOutput.Ok();
}
}
}
[HttpPut("{newPhone}")]
public async Task<IResponseOutput> SetNewPhone(string newPhone)
{
await VerifyUserPhoneAsync(_userInfo.IdentityUserId, newPhone);
await _identityUserRepository.UpdatePartialFromQueryAsync(_userInfo.IdentityUserId, u => new IdentityUser()
{
Phone = newPhone
});
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = _userInfo.IdentityUserId, OptType = UserOptType.UpdateUser }, true);
return ResponseOutput.Ok();
}
[HttpPut("{newUserName}")]
public async Task<IResponseOutput> SetNewUserName(string newUserName)
{
await VerifyUserNameAsync(_userInfo.IdentityUserId, newUserName);
await _identityUserRepository.UpdatePartialFromQueryAsync(_userInfo.IdentityUserId, u => new IdentityUser()
{
UserName = newUserName
});
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = _userInfo.IdentityUserId, 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 _identityUserRepository.UpdatePartialFromQueryAsync(_userInfo.IdentityUserId, u => new IdentityUser()
{
UserName = newUserName,
Password = newPWd,
IsFirstAdd = false,
EmailToken = String.Empty
}, true);
await _userPassWordLogRepository.AddAsync(new UserPassWordLog()
{
CreateTime = DateTime.Now,
PassWord = newPWd,
IdentityUserId = _userInfo.IdentityUserId,
});
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = _userInfo.IdentityUserId, 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 _identityUserRepository.UpdatePartialFromQueryAsync(t => t.Id == identityUserId, u => new IdentityUser()
{
Password = MD5Helper.Md5(pwd),
IsFirstAdd = true
});
var userName = _identityUserRepository.Where(t => t.Id == identityUserId).Select(t => t.UserName).FirstOrDefault();
await _fusionCache.RemoveAsync(CacheKeys.UserLoginError(userName));
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = identityUserId, 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 existUser = await _identityUserRepository.Where(t => t.EMail == email && t.Status == UserStateEnum.Enable).FirstOrDefaultAsync();
if (existUser == null)
{
//---邮箱错误。
return ResponseOutput.NotOk(_localizer["User_EmailError"]);
}
else
{
if (existUser.IsFirstAdd && existUser.UserName.IsNullOrEmpty())
{
return ResponseOutput.NotOk(_localizer["User_Notinitialized"]);
}
}
//验证码 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<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 userInfo = await _identityUserRepository.Where(t => t.EMail == email && t.Status == UserStateEnum.Enable).Select(t => new UserAccountDto() { Id = t.Id, UserName = t.UserName, UserRealName = t.FullName }).FirstOrDefaultAsync();
return userInfo;
}
/// <summary>
/// (未登陆) 设置新密码
/// </summary>
/// <param name="identityUserId"></param>
/// <param name="newPwd"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("{identityUserId:guid}/{newPwd}")]
public async Task<IResponseOutput> AnonymousSetPassword(Guid identityUserId, string newPwd)
{
await VerifyUserPwdAsync(identityUserId, newPwd);
await _identityUserRepository.UpdatePartialFromQueryAsync(t => t.Id == identityUserId, u => new IdentityUser()
{
Password = newPwd,
LastChangePassWordTime = DateTime.Now,
IsFirstAdd = false
});
await _userPassWordLogRepository.AddAsync(new UserPassWordLog()
{
CreateTime = DateTime.Now,
PassWord = newPwd,
IdentityUserId = identityUserId,
});
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = identityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = identityUserId, OptType = UserOptType.UnloginModifyPasswoed }, true);
return ResponseOutput.Ok();
}
/// <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.IdentityUserId, editPwModel.NewUserName);
await _identityUserRepository.UpdatePartialFromQueryAsync(t => t.Id == _userInfo.IdentityUserId, u => new IdentityUser()
{
UserName = editPwModel.NewUserName,
});
}
var success = await _identityUserRepository.BatchUpdateNoTrackingAsync(t => t.Id == _userInfo.IdentityUserId, u => new IdentityUser()
{
Password = editPwModel.NewPassWord,
LastChangePassWordTime = DateTime.Now,
IsFirstAdd = false
});
await _userPassWordLogRepository.AddAsync(new UserPassWordLog()
{
CreateTime = DateTime.Now,
PassWord = editPwModel.NewPassWord,
IdentityUserId = _userInfo.IdentityUserId,
});
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = _userInfo.IdentityUserId, 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 = _identityUserRepository.Where(x => x.UserRoleList.Any(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.BeginLastChangePassWordTime != null, t => t.LastChangePassWordTime >= inQuery.BeginLastChangePassWordTime)
.WhereIf(inQuery.EndLastChangePassWordTime != null, t => t.LastChangePassWordTime <= inQuery.EndLastChangePassWordTime)
.WhereIf(inQuery.UserType != null, t => t.UserRoleList.Any(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)
.WhereIf(inQuery.UserCeateSource != null, t => t.UserCeateSource == inQuery.UserCeateSource)
.ProjectTo<UserListDTO>(_mapper.ConfigurationProvider);
return await userQueryable.ToPagedListAsync(inQuery);
}
/// <summary>
/// 根据用户Id获取用户详细信息[New]
/// </summary>
/// <returns></returns>xiuga
public async Task<UserDetailDTO> GetUser(Guid identityUserId)
{
identityUserId = identityUserId != Guid.Empty ? identityUserId : _userInfo.IdentityUserId;
var user = await _identityUserRepository.Where(t => t.Id == identityUserId).ProjectTo<UserDetailDTO>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
if (user != null)
{
user.AccountList = await _userRoleRepository.Where(t => t.IdentityUserId == identityUserId).ProjectTo<UserAccountInfo>(_mapper.ConfigurationProvider).OrderBy(t => t.UserTypeShortName).ToListAsync();
}
return user;
}
/// <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<IdentityUser>(userAddModel);
var @lock = _distributedLockProvider.CreateLock($"UserAccount");
using (await @lock.AcquireAsync())
{
saveItem.Code = await _identityUserRepository.Select(t => t.Code).DefaultIfEmpty().MaxAsync() + 1;
saveItem.UserCode = AppSettings.GetCodeStr(saveItem.Code, nameof(IdentityUser));
if (saveItem.IsZhiZhun)
{
var organizationName = _userInfo.IsEn_Us ? _systemEmailConfig.OrganizationName : _systemEmailConfig.OrganizationNameCN;
saveItem.OrganizationName = organizationName;
}
saveItem.UserCeateSource = UserCeateSource.AdminCreate;
saveItem.Password = MD5Helper.Md5(IRCEmailPasswordHelper.GenerateRandomPassword(10));
var addRoleList = new List<UserRole>();
foreach (var role in userAddModel.UserRoleList)
{
var addRole = _mapper.Map<UserRole>(userAddModel);
addRole.UserTypeEnum = role.UserTypeEnum;
addRole.UserTypeId = role.UserTypeId;
addRole.IsUserRoleDisabled = role.IsUserRoleDisabled;
addRoleList.Add(addRole);
}
saveItem.UserRoleList = addRoleList;
await _identityUserRepository.AddAsync(saveItem);
var success = await _identityUserRepository.SaveChangesAsync();
}
await _mailVerificationService.AddUserSendEmailAsync(saveItem.Id, userAddModel.BaseUrl, userAddModel.RouteUrl);
return ResponseOutput.Ok(new UserAddedReturnDTO { Id = saveItem.Id, UserCode = saveItem.UserCode });
}
[HttpPost]
public async Task<IResponseOutput> AddNewUserSendEmail(NewUserSendEmailCommand incommand)
{
await _mailVerificationService.AddUserSendEmailAsync(incommand.IdentityUserId, incommand.BaseUrl, incommand.RouteUrl);
return ResponseOutput.Ok();
}
/// <summary>
/// 更新用户
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public async Task<IResponseOutput> UpdateUser(UserCommand model)
{
await VerifyUserNameAsync(model.Id, model.UserName);
await VerifyUserEmailAsync(model.Id, model.EMail);
//await VerifyUserPhoneAsync(model.Id, model.UserTypeId, model.Phone);
var user = await _identityUserRepository.Where(t => t.Id == model.Id, true).Include(t => t.UserRoleList).FirstOrDefaultAsync();
if (user == null) return Null404NotFound(user);
if (user.Status != model.Status)
{
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = model.Id, OptType = model.Status == UserStateEnum.Enable ? UserOptType.AccountEnable : UserOptType.AccountLocked }, true);
await _fusionCache.SetAsync(CacheKeys.UserDisable(user.Id), model.Status == UserStateEnum.Disable, TimeSpan.FromHours(1));
}
_mapper.Map(model, user);
if (user.IsZhiZhun)
{
user.OrganizationName = _userInfo.IsEn_Us ? _systemEmailConfig.OrganizationName : _systemEmailConfig.OrganizationNameCN;
}
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = _userInfo.UserRoleId, OptUserId = model.Id, OptType = UserOptType.UpdateUser });
//处理角色的添加、禁用
foreach (var role in model.UserRoleList)
{
var find = user.UserRoleList.FirstOrDefault(t => t.UserTypeEnum == role.UserTypeEnum && t.UserTypeId == role.UserTypeId);
if (find != null)
{
find.IsUserRoleDisabled = role.IsUserRoleDisabled;
await _fusionCache.SetAsync(CacheKeys.UserRoleDisable(find.Id), find.IsUserRoleDisabled, TimeSpan.FromHours(1));
}
else
{
var addRole = _mapper.Map<UserRole>(model);
addRole.Id = NewId.NextSequentialGuid();
addRole.IdentityUserId = user.Id;
addRole.UserTypeEnum = role.UserTypeEnum;
addRole.UserTypeId = role.UserTypeId;
user.UserRoleList.Add(addRole);
}
}
var success = await _userRoleRepository.SaveChangesAsync();
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = model.Id, OptType = UserOptType.UpdateUser }, true);
return ResponseOutput.Ok(success);
}
[HttpPut]
public async Task<IResponseOutput> UpdateUserBasicInfo(UserBasicInfoCommand command)
{
var user = await _identityUserRepository.Where(t => t.Id == command.Id, true).FirstOrDefaultAsync();
if (user == null) return Null404NotFound(user);
_mapper.Map(command, user);
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = command.Id, OptType = UserOptType.UpdateUser }, true);
await _identityUserRepository.SaveChangesAsync();
return ResponseOutput.Ok();
}
[HttpPut]
public async Task<IResponseOutput> UpdateUserRoleInfo(UpdateUserRolesDto command)
{
var user = await _identityUserRepository.Where(t => t.Id == command.Id, true).Include(t => t.UserRoleList).FirstOrDefaultAsync();
if (user == null) return Null404NotFound(user);
foreach (var role in command.UserRoleList)
{
var find = user.UserRoleList.FirstOrDefault(t => t.UserTypeEnum == role.UserTypeEnum && t.UserTypeId == role.UserTypeId);
if (find != null)
{
find.IsUserRoleDisabled = role.IsUserRoleDisabled;
await _fusionCache.SetAsync(CacheKeys.UserRoleDisable(find.Id), find.IsUserRoleDisabled, TimeSpan.FromHours(1));
}
else
{
var addRole = _mapper.Map<UserRole>(user);
addRole.Id = NewId.NextSequentialGuid();
addRole.IdentityUserId = user.Id;
addRole.UserTypeEnum = role.UserTypeEnum;
addRole.UserTypeId = role.UserTypeId;
addRole.IsUserRoleDisabled = role.IsUserRoleDisabled;
user.UserRoleList.Add(addRole);
}
}
var success = await _userRoleRepository.SaveChangesAsync();
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = command.Id, OptType = UserOptType.UpdateUserRole }, true);
return ResponseOutput.Ok();
}
///// <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 _identityUserRepository.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 _identityUserRepository.Where(u => u.Id == userId && u.Password == pwd).ProjectTo<UserBasicInfo>(_mapper.ConfigurationProvider).FirstNotNullAsync();
return info;
}
/// <summary>
/// 发送MFA 验证邮件
/// </summary>
/// <returns></returns>
public async Task<IResponseOutput> SendMFAEmail(SendMfaCommand sendMfa)
{
var identityUserId = sendMfa.IdentityUserId == Guid.Empty ? _userInfo.IdentityUserId : sendMfa.IdentityUserId;
var userInfo = await _identityUserRepository.Where(u => u.Id == identityUserId).Select(t => new { t.FullName, t.EMail }).FirstOrDefaultAsync();
int verificationCode = new Random().Next(100000, 1000000);
await _mailVerificationService.SenMFAVerifyEmail(identityUserId, userInfo.FullName, userInfo.EMail, verificationCode, sendMfa.MFAType);
var hiddenEmail = IRCEmailPasswordHelper.MaskEmail(userInfo.EMail);
return ResponseOutput.Ok(hiddenEmail);
}
/// <summary>
/// 验证MFA 邮件
/// </summary>
/// <param name="Code"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
public async Task<IResponseOutput> VerifyMFACodeAsync(string Code)
{
var identityUserId = _userInfo.IdentityUserId;
var verificationRecord = await _verificationCodeRepository.Where(t => t.UserId == identityUserId && t.Code == Code && t.CodeType == VerifyType.Email).OrderByDescending(x => x.ExpirationTime).FirstOrDefaultAsync();
VerifyEmialGetDoctorInfoOutDto result = new VerifyEmialGetDoctorInfoOutDto();
//检查数据库是否存在该验证码
if (verificationRecord == null)
{
//---验证码错误。
throw new BusinessValidationFailedException(_localizer["TrialSiteSurvey_WrongVerificationCode"]);
}
else
{
//检查验证码是否失效
if (verificationRecord.ExpirationTime < DateTime.Now)
{
//---验证码已经过期。
throw new BusinessValidationFailedException(_localizer["TrialSiteSurvey_ExpiredVerificationCode"]);
}
else //验证码正确 并且 没有超时
{
//删除验证码历史记录
await _verificationCodeRepository.BatchDeleteNoTrackingAsync(t => t.Id == verificationRecord.Id);
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionUserName = _userInfo.UserName, ActionIdentityUserId = identityUserId, OptType = UserOptType.MFALogin }, true);
}
}
return ResponseOutput.Ok();
}
[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.IdentityUserId != null, t => t.ActionIdentityUserId == inQuery.IdentityUserId)
.WhereIf(inQuery.TargetIdentityUserId != null, t => t.TargetIdentityUserId == inQuery.TargetIdentityUserId)
.WhereIf(inQuery.TrialId != null, t => t.ActionIdentityUser.UserTrialList.Any(c => c.TrialId == inQuery.TrialId) || t.TargetIdentityUser.UserTrialList.Any(c => c.TrialId == inQuery.TrialId))
.WhereIf(trialCreateTime != null, t => t.CreateTime >= trialCreateTime)
.WhereIf(inQuery.OptType != null, t => t.OptType == inQuery.OptType)
.WhereIf(inQuery.BeginDate != null, t => t.CreateTime >= inQuery.BeginDate)
.WhereIf(inQuery.EndDate != null, t => t.CreateTime <= inQuery.EndDate)
.WhereIf(inQuery.IsLoginUncommonly != null, t => t.IsLoginUncommonly == inQuery.IsLoginUncommonly)
.WhereIf(!string.IsNullOrEmpty(inQuery.LoginUserName), t => t.ActionUserName.Contains(inQuery.LoginUserName!))
.WhereIf(!string.IsNullOrEmpty(inQuery.LoginFaildName), t => t.ActionUserName.Contains(inQuery.LoginFaildName!))
.WhereIf(!string.IsNullOrEmpty(inQuery.IP), t => t.IP.Contains(inQuery.IP!))
.WhereIf(!string.IsNullOrEmpty(inQuery.LoginUserType), t => t.ActionUserType == inQuery.LoginUserType)
.WhereIf(inQuery.UserTypeId != null, t => t.CreateUserRole.UserTypeId == inQuery.UserTypeId)
.ProjectTo<UserLogView>(_mapper.ConfigurationProvider);
var pageList = await userLogQueryable.ToPagedListAsync(inQuery);
//项目创建账户的时候没有用户名但是目标用户Id 有值,导致查询出来有名字,所以在此单独处理下
foreach (var item in pageList.CurrentPageData)
{
if (item.OptType == UserOptType.AddUser)
{
item.TargetIdentityUserName = item.UserObj.UserName;
}
}
return pageList;
}
[AllowAnonymous]
[HttpGet]
public async Task<IResponseOutput> LoginOut(Guid identityUserId, Guid userRoleId)
{
await _fusionCache.RemoveAsync(CacheKeys.UserToken(identityUserId));
var userName = await _userRoleRepository.Where(t => t.Id == userRoleId).Select(t => t.IdentityUser.UserName).FirstOrDefaultAsync();
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = identityUserId, ActionUserName = userName, 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
)
{
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));
var userLog = new UserLog();
if (failCount >= maxFailures)
{
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionUserName = userName, LoginPassword = password, OptType = UserOptType.TempLockLogin }, true);
//$"密码连续错误{maxFailures}次,当前账号已被限制登录,请等待 {lockoutMinutes} 分钟后再试。"
throw new BusinessValidationFailedException(_localizer["User_ErrorLimit", maxFailures, lockoutMinutes]);
}
var userLoginReturnModel = new IRCLoginReturnDTO();
var loginUser = await _identityUserRepository.Where(u => (u.UserName.Equals(userName) || u.EMail.Equals(userName)) && u.Password == password).ProjectTo<UserBasicInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
var existUserLoginInfo = await _identityUserRepository.Where(u => u.UserName == userName || u.EMail == userName).Select(t => new { t.LastLoginIP, t.LastChangePassWordTime, t.Id }).FirstOrDefaultAsync();
var isExistAccount = existUserLoginInfo != null;
var isLoginUncommonly = false;
#region //登录用户是系统用户的时候,就要要记录异地登录
//账号在系统存在
if (isExistAccount || loginUser != null)
{
var ipinfo = _searcher.Search(_userInfo.IP);
var iPRegion = string.Join('|', ipinfo.Split('|').TakeLast(3));
string SplitAndConcatenate(string input)
{
string[] parts = input.Split('|');
return parts.Length >= 3 ? parts[0] + parts[1] : string.Join("", parts);
}
if (existUserLoginInfo.LastLoginIP != string.Empty)
{
//设置上次登录的IP
await _identityUserRepository.BatchUpdateNoTrackingAsync(x => x.Id == existUserLoginInfo.Id, x => new IdentityUser()
{
LastLoginIP = iPRegion,
LastLoginTime = DateTime.Now
});
// 与上一次区域不一致
if (SplitAndConcatenate(existUserLoginInfo.LastLoginIP) != SplitAndConcatenate(iPRegion))
{
isLoginUncommonly = true;
if (loginUser != null)
{
//异地登录
loginUser.LoginState = 2;
//超过90天没修改密码
if (_verifyConfig.CurrentValue.IsNeedChangePassWord && loginUser.LastChangePassWordTime != null && DateTime.Now.AddDays(-_verifyConfig.CurrentValue.ChangePassWordDays) > loginUser.LastChangePassWordTime.Value)
{
loginUser.NeedChangePassWord = true;
}
}
}
}
}
#endregion
if (loginUser == null)
{
//错误次数累加
failCount++;
await _fusionCache.SetAsync(cacheKey, failCount, TimeSpan.FromMinutes(lockoutMinutes));
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionUserName = userName, LoginPassword = password, OptType = UserOptType.AccountOrPasswordError, IsLoginUncommonly = isLoginUncommonly }, true);
return ResponseOutput.NotOk(_localizer["User_CheckNameOrPw"], new IRCLoginReturnDTO());
}
if (loginUser.Status == 0)
{
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = loginUser.IdentityUserId, ActionUserName = loginUser.UserName, OptType = UserOptType.LoginLockedAccount, IsLoginUncommonly = isLoginUncommonly }, true);
//---该用户已经被禁用。
return ResponseOutput.NotOk(_localizer["User_Disabled"], new IRCLoginReturnDTO());
}
//登录成功 清除缓存
await _fusionCache.SetAsync(cacheKey, 0, TimeSpan.FromMinutes(lockoutMinutes));
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = loginUser.IdentityUserId, ActionUserName = loginUser.UserName, OptType = UserOptType.Login, IsLoginUncommonly = isLoginUncommonly }, true);
userLoginReturnModel.BasicInfo = loginUser;
if (loginUser.LastChangePassWordTime == null)
{
await _identityUserRepository.BatchUpdateNoTrackingAsync(x => x.Id == loginUser.IdentityUserId, x => new IdentityUser()
{
LastChangePassWordTime = DateTime.Now
});
}
//返回临时token
userLoginReturnModel.JWTStr = _tokenService.GetToken(new UserTokenInfo() { IdentityUserId = loginUser.IdentityUserId, UserName = userName });
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;
//修改密码 || 90天修改密码再mfa 之前
if (userLoginReturnModel.BasicInfo.IsFirstAdd || userLoginReturnModel.BasicInfo.NeedChangePassWord)
{
//userLoginReturnModel.JWTStr = _tokenService.GetToken(userLoginReturnModel.BasicInfo);
}
else
{
//正常登录才发送邮件
await SendMFAEmail(new SendMfaCommand() { IdentityUserId = identityUserId, MFAType = UserMFAType.Login });
}
}
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 _userRoleRepository.Where(t => t.IdentityUserId == identityUserId).ProjectTo<UserAccountInfo>(_mapper.ConfigurationProvider).OrderBy(t => t.UserTypeShortName).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, [FromServices] IReadingImageTaskService readingImageTaskService)
{
var identityUserId = _userInfo.IdentityUserId;
var userTokenInfo = await _userRoleRepository.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.IdentityUser.IsTestUser,
IsZhiZhun = t.IdentityUser.IsZhiZhun,
FullName = t.IdentityUser.FullName,
PermissionStr = t.UserTypeRole.PermissionStr,
UserName = t.IdentityUser.UserName,
UserTypeShortName = t.UserTypeRole.UserTypeShortName,
}).FirstOrDefaultAsync();
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = userTokenInfo.IdentityUserId, ActionUserName = $"{userTokenInfo.UserName}", ActionUserType = userTokenInfo.UserTypeShortName, OptType = UserOptType.LoginSelectRole }, true);
if (userTokenInfo != null)
{
var jwt = _tokenService.GetToken(userTokenInfo);
// 验证阅片休息时间
await readingImageTaskService.ResetReadingRestTime(userTokenInfo.UserRoleId);
await _fusionCache.SetAsync(CacheKeys.UserToken(userTokenInfo.IdentityUserId), jwt, TimeSpan.FromDays(7));
return jwt;
}
else
{
throw new BusinessValidationFailedException("传递参数查询数据库不存在!");
}
}
#endregion
}
}