医生发送邮件

Uat_Study
hang 2022-06-07 17:31:19 +08:00
parent e92979388c
commit 7de33670c9
10 changed files with 261 additions and 17 deletions

View File

@ -115,12 +115,18 @@
<Content Update="wwwroot\EmailTemplate\TrialSiteSurveyReject.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\TrialDoctorExistJoin.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\TrialUserExistJoin.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\AdminAddUser.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\TrialDoctorFirstJoin.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\UserOptCommon.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
<div style='padding-left: 40px;background: #f6f6f6'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 18px'>
尊敬的 {0} ,您好:
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
展影医疗作为 [{1} (实验方案号:{2 })] 项目的IRC供应商诚邀您参加该项目IRC阅片相关工作欢迎您提供指导和建议非常感谢
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
该项目采用电子化工作流,系统及您的账号信息如下:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
<div>
项目编号: {3}
</div>
<div>
试验方案号: {2}
</div>
<div>
试验名称: {1}
</div>
<div>
用户名: {4}
</div>
<div>
角色: {5}
</div>
<div>
系统登录地址:{6}
</div>
</div>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div style="font-size: 14px;">上海展影医疗科技有限公司</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,52 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
<div style='padding-left: 40px;background: #f6f6f6'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 18px'>
尊敬的 {0} ,您好:
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
展影医疗作为 [{1} (实验方案号:{2 })] 项目的IRC供应商诚邀您参加该项目IRC阅片相关工作欢迎您提供指导和建议非常感谢
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
该项目采用电子化工作流,系统及您的账号信息如下:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
<div>
项目编号: {3}
</div>
<div>
试验方案号: {2}
</div>
<div>
试验名称: {1}
</div>
<div>
用户名: {4}
</div>
<div>
角色: {5}
</div>
<div>
首次登陆前,请通过该链接修改您的账户信息:
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
初始化账号信息
</a>
</div>
</div>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div style="font-size: 14px;">上海展影医疗科技有限公司</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -3485,7 +3485,7 @@
入组流程-CRO确定医生名单 [ Approve]
</summary>
</member>
<member name="M:IRaCIS.Application.Services.EnrollService.ConfirmReviewer(System.Guid,System.Guid[],System.Int32)">
<member name="M:IRaCIS.Application.Services.EnrollService.ConfirmReviewer(IRaCIS.Core.Application.Service.WorkLoad.DTO.ConfirmReviewerCommand)">
<summary>
入组流程-后台确认医生入组[Confirm]
</summary>

View File

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting;
using IRaCIS.Core.Application.Auth;
using AutoMapper;
using IRaCIS.Application.Contracts;
using Nito.AsyncEx;
namespace IRaCIS.Application.Services
{
@ -26,6 +27,8 @@ namespace IRaCIS.Application.Services
Task ExternalUserJoinEmail(Guid trialId, Guid userId, string baseUrl, string rootUrl);
Task DoctorJoinTrialEmail(Guid trialId, Guid doctorId, string baseUrl, string rootUrl);
}
@ -43,6 +46,12 @@ namespace IRaCIS.Application.Services
private readonly IMapper _mapper;
private readonly IRepository<Trial> _trialRepository;
private readonly IRepository<UserType> _userTypeRepository;
private readonly IRepository<Doctor> _doctorTypeRepository;
private readonly AsyncLock _mutex = new AsyncLock();
public MailVerificationService(IRepository<VerificationCode> verificationCodeRepository,
@ -50,7 +59,9 @@ namespace IRaCIS.Application.Services
IWebHostEnvironment hostEnvironment,
IRepository<User> userRepository,
ITokenService tokenService,
IRepository<Trial> trialRepository,
IRepository<Trial> trialRepository,
IRepository<UserType> userTypeRepository,
IRepository<Doctor> doctorTypeRepository,
IMapper mapper)
{
_verificationCodeRepository = verificationCodeRepository;
@ -64,6 +75,9 @@ namespace IRaCIS.Application.Services
_userRepository = userRepository;
_trialRepository = trialRepository;
_userTypeRepository = userTypeRepository;
_doctorTypeRepository = doctorTypeRepository;
}
//重置邮箱
@ -128,7 +142,6 @@ namespace IRaCIS.Application.Services
//不登录 通过邮箱重置密码
public async Task AnolymousSendEmailForResetAccount(string emailAddress, int verificationCode)
{
@ -282,8 +295,8 @@ namespace IRaCIS.Application.Services
await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new User() { EmailToken = token });
routeUrl = routeUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + sysUserInfo.UserTypeRole.UserTypeShortName + "&access_token=" + token;
var domain=baseUrl.Substring(0,baseUrl.IndexOf("/login"));
var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login"));
var redirectUrl = $"{domain}/api/User/UserRedirect?url={ System.Web.HttpUtility.UrlEncode(routeUrl) }";
@ -354,7 +367,7 @@ namespace IRaCIS.Application.Services
}
//Site调研 用户加入项目
public async Task SiteSurveyUserJoinEmail(Guid trialId, Guid userId, string baseUrl, string rootUrl)
{
var sysUserInfo = (await _userRepository.Where(t => t.Id == userId).Include(t => t.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException();
@ -416,6 +429,7 @@ namespace IRaCIS.Application.Services
}
//外部用户 加入项目
public async Task ExternalUserJoinEmail(Guid trialId, Guid userId, string baseUrl, string rootUrl)
{
var trialInfo = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId)).IfNullThrowException();
@ -480,6 +494,100 @@ namespace IRaCIS.Application.Services
public async Task DoctorJoinTrialEmail(Guid trialId, Guid doctorId, string baseUrl, string rootUrl)
{
var doctor = await _doctorTypeRepository.FindAsync(doctorId);
User sysUserInfo = null;
using (await _mutex.LockAsync())
{
var isDoctorHaveAccount = await _userRepository.AnyAsync(t => t.DoctorId == doctorId);
if (!isDoctorHaveAccount)
{
var saveItem = new User() { FirstName = doctor.FirstName, LastName = doctor.LastName, EMail = doctor.EMail };
saveItem.Code = _userRepository.Select(t => t.Code).DefaultIfEmpty().Max() + 1;
saveItem.UserCode = AppSettings.GetCodeStr(saveItem.Code, nameof(User));
saveItem.UserName = saveItem.UserCode;
saveItem.UserTypeEnum = UserTypeEnum.IndependentReviewer;
saveItem.DoctorId = doctorId;
sysUserInfo = await _userRepository.AddAsync(saveItem);
await _userRepository.SaveChangesAsync();
}
else
{
sysUserInfo = (await _userRepository.Where(t => t.DoctorId == doctorId).Include(t => t.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException();
}
}
var userType= await _userTypeRepository.FirstAsync(t => t.UserTypeEnum== UserTypeEnum.IndependentReviewer);
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress("GRR", "iracis_grr@163.com"));
//收件地址
messageToSend.To.Add(new MailboxAddress(doctor.FullName, doctor.EMail));
//主题
messageToSend.Subject = $"[来自展影IRC] [{ trialInfo.ResearchProgramNo}]邀请信";
var builder = new BodyBuilder();
var token = _tokenService.GetToken(IRaCISClaims.Create(_mapper.Map<UserBasicInfo>(sysUserInfo)));
if (sysUserInfo.IsFirstAdd)
{
await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new User() { EmailToken = token });
}
var pathToFile = _hostEnvironment.WebRootPath
+ Path.DirectorySeparatorChar.ToString()
+ "EmailTemplate"
+ Path.DirectorySeparatorChar.ToString()
+ (sysUserInfo.IsFirstAdd ? "TrialDoctorFirstJoin.html" : "TrialDoctorExistJoin.html");
using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile))
{
var templateInfo = SourceReader.ReadToEnd();
var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + userType.UserTypeShortName + "&access_token=" + token;
var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login"));
var redirectUrl = $"{domain}/api/User/UserRedirect?url={ System.Web.HttpUtility.UrlEncode(routeUrl) }";
builder.HtmlBody = string.Format(templateInfo,
sysUserInfo.FullName,
trialInfo.ExperimentName,
trialInfo.ResearchProgramNo,
trialInfo.TrialCode,
sysUserInfo.UserName,
sysUserInfo.UserTypeRole.UserTypeShortName,
sysUserInfo.IsFirstAdd ? redirectUrl : baseUrl
);
}
messageToSend.Body = builder.ToMessageBody();
await SendEmailHelper.SendEmailAsync(messageToSend, null);
}
}

View File

@ -67,6 +67,21 @@ namespace IRaCIS.Core.Application.Service.WorkLoad.DTO
}
public class ConfirmReviewerCommand
{
[NotDefault]
public Guid TrialId { get; set; }
public Guid[] DoctorIdArray { get; set; } = new Guid [0];
public int ConfirmState { get; set; }
public string BaseUrl { get; set; } = string.Empty;
public string RouteUrl { get; set; } = string.Empty;
}
public class EnrollCommand
{
public Guid Id { get; set; }

View File

@ -20,6 +20,7 @@ namespace IRaCIS.Application.Services
private readonly IRepository<Doctor> _doctorRepository;
private readonly IRepository<EnrollDetail> _enrollDetailRepository;
private readonly IRepository<Workload> _workloadRepository;
private readonly IMailVerificationService _mailVerificationService;
public EnrollService(IRepository<Trial> clinicalTrialProjectRepository,
@ -27,7 +28,9 @@ namespace IRaCIS.Application.Services
IRepository<Enroll> intoGroupRepository,
IRepository<Doctor> doctorRepository,
IRepository<TrialPaymentPrice> TrialPaymentPriceRepository,
IRepository<EnrollDetail> intoGroupDetailRepository, IRepository<Workload> workloadRepository)
IRepository<EnrollDetail> intoGroupDetailRepository,
IRepository<Workload> workloadRepository,
IMailVerificationService mailVerificationService)
{
_trialRepository = clinicalTrialProjectRepository;
_TrialPaymentPriceRepository = TrialPaymentPriceRepository;
@ -38,6 +41,8 @@ namespace IRaCIS.Application.Services
_enrollDetailRepository = intoGroupDetailRepository;
_workloadRepository = workloadRepository;
_mailVerificationService = mailVerificationService;
}
@ -344,10 +349,11 @@ namespace IRaCIS.Application.Services
/// 入组流程-后台确认医生入组[Confirm]
/// </summary>
[HttpPost("{trialId:guid}/{confirmState:int}")]
[HttpPost]
[TypeFilter(typeof(TrialResourceFilter))]
[Authorize(Policy = IRaCISPolicy.PM_APM_SPM_CPM)]
public async Task<IResponseOutput> ConfirmReviewer(Guid trialId, Guid[] doctorIdArray, int confirmState)
[UnitOfWork]
public async Task<IResponseOutput> ConfirmReviewer(ConfirmReviewerCommand confirmReviewerCommand)
{
//var trial = _trialRepository.FirstOrDefault(t => t.Id == trialId);
//var existItem = _trialRepository.FindSingleOrDefault(u => u.Id == trialId && u.TrialStatus >= (int)TrialEnrollStatus.HasConfirmedDoctorNames);
@ -356,6 +362,8 @@ namespace IRaCIS.Application.Services
////trial.TrialStatus = (int)TrialStatus.HasConfirmedDoctorNames;
//_trialRepository.Update(trial);
var trialId = confirmReviewerCommand.TrialId;
var trial = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
if (trial == null) return Null404NotFound(trial);
@ -364,16 +372,16 @@ namespace IRaCIS.Application.Services
//更新入组表
var intoGroupList = await _enrollRepository.Where(t => t.TrialId == trialId,true).ToListAsync();
if (confirmState == 1) //确认入组
if (confirmReviewerCommand.ConfirmState == 1) //确认入组
{
foreach (var intoGroupItem in intoGroupList)
{
if (doctorIdArray.Contains(intoGroupItem.DoctorId))
if (confirmReviewerCommand.DoctorIdArray.Contains(intoGroupItem.DoctorId))
{
intoGroupItem.EnrollStatus = (int)EnrollStatus.ConfirmIntoGroup;
intoGroupItem.EnrollTime = DateTime.Now;
await _mailVerificationService.DoctorJoinTrialEmail(trialId, intoGroupItem.DoctorId, confirmReviewerCommand.BaseUrl, confirmReviewerCommand.RouteUrl);
await _enrollDetailRepository.AddAsync(new EnrollDetail()
{
DoctorId = intoGroupItem.DoctorId,
@ -385,11 +393,11 @@ namespace IRaCIS.Application.Services
}
}
else if (confirmState == 0)//回退上一步
else if (confirmReviewerCommand.ConfirmState == 0)//回退上一步
{
foreach (var intoGroupItem in intoGroupList)
{
if (doctorIdArray.Contains(intoGroupItem.DoctorId))
if (confirmReviewerCommand.DoctorIdArray.Contains(intoGroupItem.DoctorId))
{
intoGroupItem.EnrollStatus = (int)EnrollStatus.InviteIntoGroup;
intoGroupItem.EnrollTime = null;

View File

@ -6,7 +6,7 @@ namespace IRaCIS.Application.Services
{
Task<IResponseOutput> AddOrUpdateEnroll(EnrollCommand addOrUpdateModel);
Task<IResponseOutput> ApproveReviewer(Guid trialId, Guid[] doctorIdArray, int auditState);
Task<IResponseOutput> ConfirmReviewer(Guid trialId, Guid[] doctorIdArray, int confirmState);
//Task<IResponseOutput> ConfirmReviewer(Guid trialId, Guid[] doctorIdArray, int confirmState);
Task<IResponseOutput> EnrollBackOrOut(Guid trialId, Guid doctorId, int optType, DateTime? outEnrollTime);
Task<PageOutput<EnrollViewModel>> GetTrialDoctorList(EnrollGetQuery challengeQuery);
Task<IResponseOutput> SelectReviewers(Guid trialId, Guid[] doctorIdArray);

View File

@ -4,19 +4,22 @@ namespace IRaCIS.Core.Domain.Share
{
public enum ReadingCategory
{
//访视
Visit=1,
//阅片周期
ReadingPeriod=2,
//全局
Global=3
}
public enum TaskState
{
//未分配
NotAllocate = 0,
//已分配
Allocated = 2,
}

View File

@ -71,6 +71,9 @@ namespace IRaCIS.Core.Domain.Models
public string EmailToken { get; set; } = string.Empty;
//医生生成账号后,会有值
public Guid? DoctorId { get; set; }
[Projectable] public string FullName => LastName + " / " + FirstName;
//[Projectable] public string FullName => $"{LastName} / {FirstName}";