using IRaCIS.Application.Contracts; using IRaCIS.Core.Application.Auth; using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Infrastructure; using MailKit; using Medallion.Threading; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Options; using MimeKit; namespace IRaCIS.Core.Application.Service { public interface IMailVerificationService { Task AnolymousSendEmail(string researchProgramNo, string emailAddress, int verificationCode); Task SendEmailVerification(string emailAddress, int verificationCode); Task SenMFAVerifyEmail(Guid userId, string userName, string emailAddress, int verificationCode, UserMFAType mfaType = UserMFAType.Login); Task SendMailEditEmail(Guid userId, string userName, string emailAddress, int verificationCode); Task AnolymousSendEmailForResetAccount(string emailAddress, int verificationCode); Task AddUserSendEmailAsync(Guid userId, string baseUrl, string routeUrl); Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456"); } public class MailVerificationService(IRepository _verificationCodeRepository, IRepository _systemBasicDatarepository, IRepository _visitTaskRepository, IRepository _trialSiteRepository, IRepository _userRoleRepository, IRepository _userFeedBackRepository, ITokenService _tokenService, IRepository _identityUserRepository, IRepository _subjectVisitRepository, IRepository _trialRepository, IRepository _userTypeRepository, IRepository _doctorTypeRepository, IRepository _dictionaryRepository, IRepository _emailNoticeConfigrepository, IOptionsMonitor systemEmailConfig, IDistributedLockProvider _distributedLockProvider, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IMailVerificationService { private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue; private async Task GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario scenario, MimeMessage messageToSend, Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc) { var configInfo = await _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario).FirstOrDefaultAsync(); if (configInfo == null) { throw new BusinessValidationFailedException("系统未找到当前场景邮件配置信息,请联系运维人员核查"); } var (topicStr, htmlBodyStr) = _userInfo.IsEn_Us ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN); try { //每个场景修改主题 和body的逻辑不一样 (topicStr, htmlBodyStr) = emailFunc((topicStr, htmlBodyStr)); } catch (Exception ex) { throw new BusinessValidationFailedException("邮件模板内容有误,填充内容出现问题,请联系运维人员核查"); } messageToSend.Subject = topicStr; var builder = new BodyBuilder(); builder.HtmlBody = htmlBodyStr; messageToSend.Body = builder.ToMessageBody(); return configInfo; } public async Task GetEmailConfigInfoAsync(EmailBusinessScenario scenario) { var configInfo = await _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario).Include(t => t.EmailNoticeUserTypeList).FirstOrDefaultAsync(); if (configInfo == null) { throw new BusinessValidationFailedException("系统未找到当前场景邮件配置信息,请联系运维人员核查"); } return configInfo; } private EventHandler GetEmailSuccessHandle(Guid userId, int verificationCode, string email = "") { EventHandler sucessHandle = (sender, args) => { // args.Response var code = verificationCode.ToString(); _ = _verificationCodeRepository.AddAsync(new VerificationCode() { CodeType = VerifyType.Email, HasSend = true, Code = code, UserId = userId, EmailOrPhone = email, ExpirationTime = DateTime.Now.AddMinutes(3) }).Result; _ = _verificationCodeRepository.SaveChangesAsync().Result; }; return sucessHandle; } private string ReplaceCompanyName(string needDealtxt) { var str = needDealtxt.Replace("{company}", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyName : _systemEmailConfig.CompanyNameCN) .Replace("{company abbreviation}", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN); return str; } //MFA public async Task SenMFAVerifyEmail(Guid userId, string userName, string emailAddress, int verificationCode, UserMFAType mfaType = UserMFAType.Login) { var messageToSend = new MimeMessage(); //发件地址 messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); //收件地址 messageToSend.To.Add(new MailboxAddress(userName, emailAddress)); //主题---[来自{0}] 关于MFA邮箱验证的提醒 var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { var topicStr = string.Format(input.topicStr, companyName); var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), userName, verificationCode ); return (topicStr, htmlBodyStr); }; await GetEmailSubejctAndHtmlInfoAndBuildAsync(mfaType == UserMFAType.Login ? EmailBusinessScenario.MFALogin : EmailBusinessScenario.MFAUnlock, messageToSend, emailConfigFunc); var sucessHandle = GetEmailSuccessHandle(userId, verificationCode, emailAddress); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle); } //用户重置邮箱 public async Task SendMailEditEmail(Guid userId, string userName, string emailAddress, int verificationCode) { var messageToSend = new MimeMessage(); //发件地址 messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); //收件地址 messageToSend.To.Add(new MailboxAddress(userName, emailAddress)); //主题 //---[来自展影IRC] 关于重置邮箱的提醒 var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { var topicStr = string.Format(input.topicStr, companyName); var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), userName, //---您正在进行邮箱重置操作 verificationCode ); return (topicStr, htmlBodyStr); }; await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.UserResetEmail, messageToSend, emailConfigFunc); var sucessHandle = GetEmailSuccessHandle(userId, verificationCode, emailAddress); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle); } //不登录 通过邮箱重置密码 public async Task AnolymousSendEmailForResetAccount(string emailAddress, int verificationCode) { var messageToSend = new MimeMessage(); //发件地址 messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); //收件地址 messageToSend.To.Add(new MailboxAddress(String.Empty, emailAddress)); var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { var topicStr = string.Format(input.topicStr, companyName); var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), "Sir/Madam", //---您正在进行邮箱重置密码操作 verificationCode ); return (topicStr, htmlBodyStr); }; await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.UnloginUseEmailResetPassword, messageToSend, emailConfigFunc); ////此时不知道用户 var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle); } /// /// Reviewer简历录入 发送验证码 /// /// /// /// public async Task SendEmailVerification(string emailAddress, int verificationCode) { var messageToSend = new MimeMessage(); //发件地址 messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); //收件地址 messageToSend.To.Add(new MailboxAddress(String.Empty, emailAddress)); //主题 //---[来自展影IRC]的提醒 var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { var topicStr = string.Format(input.topicStr, companyName); var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), "Sir/Madam", //---您正在参与展影医疗IRC项目 companyName, verificationCode ); return (topicStr, htmlBodyStr); }; await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.ReviewerLogin, messageToSend, emailConfigFunc); //此时不知道用户 var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle); } //中心调研 登陆 public async Task AnolymousSendEmail(string researchProgramNo, string emailAddress, int verificationCode) { var messageToSend = new MimeMessage(); //发件地址 messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); //收件地址 messageToSend.To.Add(new MailboxAddress(String.Empty, emailAddress)); //主题 //$"[来自展影IRC] [{researchProgramNo}] 关于中心调研的提醒"; var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { var topicStr = string.Format(input.topicStr, companyName, researchProgramNo); var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), "Sir/Madam", //---您正在参与展影医疗IRC项目中心调研工作 companyName, verificationCode ); return (topicStr, htmlBodyStr); }; await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SiteSurveyLogin, messageToSend, emailConfigFunc); //此时不知道用户 var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle); } //添加用户发送邮件 public async Task AddUserSendEmailAsync(Guid userId, string baseUrl, string routeUrl) { var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).Include(t => t.UserRoleList).ThenInclude(c => c.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException(); var messageToSend = new MimeMessage(); //发件地址 messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); //收件地址 messageToSend.To.Add(new MailboxAddress(sysUserInfo.FullName, sysUserInfo.EMail)); //主题 //---[来自展影IRC] 关于创建账户的提醒 var token = _tokenService.GetToken(new UserTokenInfo() { IdentityUserId = sysUserInfo.Id }); await _identityUserRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new IdentityUser() { EmailToken = token }); routeUrl = routeUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&lang=" + (_userInfo.IsEn_Us ? "en" : "zh") + "&access_token=" + token; var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { var topicStr = string.Format(input.topicStr, companyName); var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), sysUserInfo.FullName, sysUserInfo.EMail, //string.Join(',', sysUserInfo.UserRoleList.Select(t => t.UserTypeRole.UserTypeShortName)), redirectUrl ); return (topicStr, htmlBodyStr); }; await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SysCreateUser, messageToSend, emailConfigFunc); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); } //管理员重置密码发送邮件 public async Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456") { var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).Include(t => t.UserRoleList).ThenInclude(c => c.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException(); var messageToSend = new MimeMessage(); //发件地址 messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); //收件地址 messageToSend.To.Add(new MailboxAddress(sysUserInfo.FullName, sysUserInfo.EMail)); //主题 //---[来自展影IRC] 关于重置账户密码的提醒 var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { var topicStr = string.Format(input.topicStr, companyName); var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), sysUserInfo.FullName, sysUserInfo.UserName, //string.Join(',', sysUserInfo.UserRoleList.Select(t => t.UserTypeRole.UserTypeShortName)), pwdNotMd5 ); return (topicStr, htmlBodyStr); }; await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SysResetPassword, messageToSend, emailConfigFunc); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); } } }