diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml index b629ca218..5bc700ff6 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -17665,7 +17665,7 @@ TrialDocumentService - + TrialDocumentService diff --git a/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs index 614c90328..ece094738 100644 --- a/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs +++ b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs @@ -2311,8 +2311,263 @@ namespace IRaCIS.Core.Application.Service.Common #region 通用阅片结果导出 + /// + /// 一致性分析结果导出 7 8 分别是自身 和组件一致性 + /// + /// + /// + /// + /// + /// + [HttpPost] + public async Task GetAnalysisTaskList_Export(VisitTaskQuery inQuery, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + //每次查询必须是单标准的 + var criterion = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == inQuery.TrialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionName, t.ArbitrationRule }).FirstNotNullAsync(); + var query = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && t.TaskAllocationState == TaskAllocationState.Allocated && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze)) + + //一致性分析 + .WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults, t => t.IsSelfAnalysis == true || t.IsSelfAnalysis == null) + .WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults, t => t.IsSelfAnalysis == null ? t.Subject.SubjectVisitTaskList.Any(u => u.IsSelfAnalysis == true && u.VisitTaskNum == t.VisitTaskNum && u.DoctorUserId == t.DoctorUserId && u.TrialReadingCriterionId == t.TrialReadingCriterionId) : true) + .WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfInterReaderAnalysisResults, t => t.IsSelfAnalysis == false || t.IsSelfAnalysis == null) + + //访视和全局查询已签名完成的,裁判可以是未签名,未完成的 + .Where(t => (t.ReadingTaskState == ReadingTaskState.HaveSigned && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global)) || t.ReadingCategory == ReadingCategory.Judge) + .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) + .WhereIf(inQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == inQuery.TrialSiteId) + + .WhereIf(inQuery.IsUrgent != null, t => t.IsUrgent == inQuery.IsUrgent) + .WhereIf(inQuery.DoctorUserId != null, t => t.DoctorUserId == inQuery.DoctorUserId) + .WhereIf(inQuery.ReadingCategory != null, t => t.ReadingCategory == inQuery.ReadingCategory) + //.WhereIf(inQuery.ReadingTaskState != null, t => t.ReadingTaskState == inQuery.ReadingTaskState) + .WhereIf(inQuery.TaskAllocationState != null, t => t.TaskAllocationState == inQuery.TaskAllocationState) + .WhereIf(inQuery.ArmEnum != null, t => t.ArmEnum == inQuery.ArmEnum) + .WhereIf(!string.IsNullOrEmpty(inQuery.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.TaskName.Contains(inQuery.TaskName) || t.TaskBlindName.Contains(inQuery.TaskName)) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.Subject.Code.Contains(inQuery.SubjectCode)) + .WhereIf(inQuery.BeginAllocateDate != null, t => t.AllocateTime > inQuery.BeginAllocateDate) + .WhereIf(inQuery.EndAllocateDate != null, t => t.AllocateTime < inQuery.EndAllocateDate); + + var list = await query.ProjectTo(_mapper.ConfigurationProvider, + new + { + readingExportType = inQuery.ReadingExportType, + criterionType = criterion.CriterionType, + trialReadingCriterionId = inQuery.TrialReadingCriterionId, + isEn_Us = _userInfo.IsEn_Us + }).ToListAsync(); + + list = list.OrderBy(t => t.SubjectCode).ThenBy(t => t.ArmEnum).ThenBy(t => t.VisitTaskNum).ToList(); + + + var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + exportInfo.CriterionName = criterion.CriterionName; + + #region 处理系统标准存在疾病和整体肿瘤合并 + + //如果是以合并后的找翻译字典,会少,所以必须放在前面 + var translateDicNameList = list.SelectMany(t => t.QuestionAnswerList).Where(t => t.TranslateDicName.IsNotNullOrEmpty()).Select(t => t.TranslateDicName).Distinct().ToList(); + + //针对1.1 整体肿瘤评估 有的两列要合并一列 + if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.RECIST1Pointt1_MB || criterion.CriterionType == CriterionType.IRECIST1Point1) + { + foreach (var item in list) + { + //处理合并表头 + + var questionType = item.IsBaseline == true ? QuestionType.ExistDisease : QuestionType.Tumor; + + var findItem = item.QuestionAnswerList.Where(t => t.QuestionType == questionType).FirstOrDefault(); + + if (findItem != null) + { + findItem.QuestionName = _userInfo.IsEn_Us ? "Overall Response" : "整体肿瘤评估"; + } + + + if (item.IsBaseline == true) + { + item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.Tumor).ToList(); + } + else + { + item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.ExistDisease).ToList(); + } + } + } + else if (criterion.CriterionType == CriterionType.Lugano2014 || criterion.CriterionType == CriterionType.Lugano2014WithoutPET) + { + + foreach (var item in list) + { + //处理合并表头 + + var questionType = item.IsBaseline == true ? QuestionType.ExistDisease : QuestionType.ImgOncology; + + var findItem = item.QuestionAnswerList.Where(t => t.QuestionType == questionType).FirstOrDefault(); + + if (findItem != null) + { + findItem.QuestionName = _userInfo.IsEn_Us ? "Overall Response" : "整体肿瘤评估"; + } + + if (item.IsBaseline == true) + { + item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.ImgOncology).ToList(); + } + else + { + item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.ExistDisease).ToList(); + } + } + } + else if (criterion.CriterionType == CriterionType.PCWG3) + { + + } + else if (criterion.CriterionType == CriterionType.SelfDefine) + { + //自定义的又问题名称重复 所以统一加上组名 + + //有重复的就加,没有重复的就不加 + if (list.Any(t => t.QuestionAnswerList.Select(t => t.QuestionName).Count() != t.QuestionAnswerList.Select(t => t.QuestionName).Distinct().Count())) + { + foreach (var item in list) + { + foreach (var qs in item.QuestionAnswerList) + { + qs.QuestionName = qs.Group + "_" + qs.QuestionName; + } + } + } + + + } + + #endregion + + var export_Template = StaticData.Export.TrialSelfAnalysisList_Export; + + #region 自身一致性分析和组间一致性分析 + + if (inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults) + { + //找到非一致性分析的任务 + var selfExportList = list.Where(t => t.IsSelfAnalysis == null).ToList(); + + //处理一致性分析结果是否和原始阅片是否一致 + foreach (var item in selfExportList) + { + //找到一致性分析的结果 + var selfAnalysisTask = list.Where(t => t.IsSelfAnalysis == true && t.SubjectCode == item.SubjectCode && t.VisitTaskNum == item.VisitTaskNum && t.TaskName == t.TaskName && t.UserName == item.UserName).FirstOrDefault(); + + //将自身一致性分析的字段 赋值到访视任务这个字段 + item.IsAnalysisDiffToOriginalData = selfAnalysisTask?.IsAnalysisDiffToOriginalData; + + //处理再次阅片人的结果 + if (selfAnalysisTask != null) + { + var cloneQuestionAnswerList = selfAnalysisTask.QuestionAnswerList.Clone(); + + foreach (var qItem in cloneQuestionAnswerList) + { + qItem.QuestionName = qItem.QuestionName + $"{(_userInfo.IsEn_Us ? "(Again)" : "(再次)")}"; + } + + item.QuestionAnswerList = item.QuestionAnswerList.Union(cloneQuestionAnswerList).ToList(); + } + } + + list = selfExportList; + } + else + { + export_Template = StaticData.Export.TrialGroupAnalysisList_Export; + + var newList = new List(); + + foreach (var group in list.GroupBy(t => new { t.SubjectCode, t.VisitTaskNum, t.TaskName }).OrderBy(g => g.Key.SubjectCode).ThenBy(g => g.Key.VisitTaskNum)) + { + var subjectVisitGroupList = group.ToList(); + + + //找到当前访视组间一致性分析的任务结果 + + var groupOtherTaskList = subjectVisitGroupList.Where(t => t.IsSelfAnalysis == false).ToList(); + + foreach (var subjectVisitTaskArm in subjectVisitGroupList.Where(t => t.IsSelfAnalysis == null).OrderBy(t => t.ArmEnum)) + { + foreach (var otherTask in groupOtherTaskList) + { + //非一致性分析任务 + var cloneObj = subjectVisitTaskArm.Clone(); + + var otherTaskQuestionAnserList = otherTask.QuestionAnswerList.Clone(); + + foreach (var qItem in otherTaskQuestionAnserList) + { + qItem.QuestionName = qItem.QuestionName + $"{(_userInfo.IsEn_Us ? "(Again)" : "(再次)")}"; + } + + //处理 再次阅片人,再次阅片人角色 两列 + var addQuestionList = new List(); + + addQuestionList.Add(new CommonQuesionInfo() { QuestionName = _userInfo.IsEn_Us ? "Reviwer(Again)" : "阅片人(再次)", QuestionValue = otherTask.UserName }); + addQuestionList.Add(new CommonQuesionInfo() { QuestionName = _userInfo.IsEn_Us ? "Reviwer Role(Again)" : "阅片人角色(再次)", QuestionValue = ((int)otherTask.ArmEnum).ToString(), TranslateDicName = "ArmEnum" }); + + + cloneObj.QuestionAnswerList = cloneObj.QuestionAnswerList.Union(addQuestionList).Union(otherTaskQuestionAnserList).ToList(); + + + cloneObj.IsGroupAnalysisDiffToOriginalData = cloneObj.ArmEnum == Arm.DoubleReadingArm1 ? otherTask.IsGroupDiffArm1 : otherTask.IsGroupDiffArm2; + + newList.Add(cloneObj); + } + } + } + translateDicNameList.Add("ArmEnum"); + + list = newList; + + } + + #endregion + + var columNameList = list.SelectMany(t => t.QuestionAnswerList).Where(t => t.QuestionName.IsNotNullOrEmpty()).Select(t => t.QuestionName).Distinct().ToList(); + + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list.Where(t => t.ReadingCategory != ReadingCategory.Global).ToList(), _userInfo.TimeZoneId); + exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId); + + var dynamicColumnConfig = new DynamicColumnConfig() + { + //可读的列表名行索引,不是{{}} 模板行索引 + AutoColumnTitleRowIndex = 2, + AutoColumnStartIndex = 5, + TempalteLastColumnIndex = 4, + DynamicItemDicName = "TranslateDicName", + DynamicItemValueName = "QuestionValue", + DynamicItemTitleName = "QuestionName", + DynamicListName = "QuestionAnswerList", + RemoveColunmIndexList = new List() { }, + ColumnIdNameList = columNameList.Select(t => new DynamicColumnConfig.ColumItem() { Id = Guid.Empty, Name = t }).ToList(), + TranslateDicNameList = translateDicNameList ?? new List() + }; + + + var (memoryStream, fileName) = await ExcelExportHelper.DataExport_NpoiTestAsync(export_Template, exportInfo, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(AnalysisDynamicCommonExport), criterion.CriterionType, dynamicColumnConfig); + + return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + { + FileDownloadName = $"{exportInfo.ResearchProgramNo}_{exportInfo.CriterionName}_{fileName}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx" + }; + + } + /// /// 获取阅片标准可以导出的列表 diff --git a/IRaCIS.Core.Application/Service/Common/MailService.cs b/IRaCIS.Core.Application/Service/Common/MailService.cs index d91524a08..42d3d2466 100644 --- a/IRaCIS.Core.Application/Service/Common/MailService.cs +++ b/IRaCIS.Core.Application/Service/Common/MailService.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; using MimeKit; +using System.Net.Mail; using System.Runtime.CompilerServices; namespace IRaCIS.Core.Application.Service @@ -70,6 +71,8 @@ namespace IRaCIS.Core.Application.Service Task<(Guid identityUserId, Guid userRoleId)> DoctorJoinTrialEmail(Guid trialId, Guid doctorId, string baseUrl, string rootUrl); Task UserFeedBackMail(Guid feedBackId); + + Task AfterUserModifyPasswordSendEmailAsync(Guid userId); } public class MailVerificationService(IRepository _verificationCodeRepository, @@ -874,5 +877,41 @@ namespace IRaCIS.Core.Application.Service await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); } + + + //用户修改密码发送邮件 + public async Task AfterUserModifyPasswordSendEmailAsync(Guid userId) + { + + var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).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 + ); + + return (topicStr, htmlBodyStr); + }; + + + await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.IdentityUser_ModifyPassword, messageToSend, emailConfigFunc); + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); + } + } } diff --git a/IRaCIS.Core.Application/Service/Management/UserService.cs b/IRaCIS.Core.Application/Service/Management/UserService.cs index 971eefe6b..a05c095da 100644 --- a/IRaCIS.Core.Application/Service/Management/UserService.cs +++ b/IRaCIS.Core.Application/Service/Management/UserService.cs @@ -435,6 +435,7 @@ namespace IRaCIS.Core.Application.Service await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = identityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = identityUserId, OptType = UserOptType.UnloginModifyPasswoed }, true); + await _mailVerificationService.AfterUserModifyPasswordSendEmailAsync(identityUserId); return ResponseOutput.Ok(); @@ -481,6 +482,8 @@ namespace IRaCIS.Core.Application.Service await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = _userInfo.IdentityUserId, OptType = UserOptType.LoginModifyPassword }, true); + await _mailVerificationService.AfterUserModifyPasswordSendEmailAsync(_userInfo.IdentityUserId); + return ResponseOutput.Result(success); diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs index 0e8cc66f4..526f366ab 100644 --- a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs +++ b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs @@ -1148,7 +1148,9 @@ namespace IRaCIS.Core.Application.Contracts public class SelftAnalysisExport : AnalysisExortCommon { - //自身一致性分析任务特有 + //自身 + // + //任务特有 [DictionaryTranslateAttribute("YesOrNo")] public bool? IsAnalysisDiffToOriginalData { get; set; } diff --git a/IRaCIS.Core.Domain.Share/Common/EmailScenarioEnum.cs b/IRaCIS.Core.Domain.Share/Common/EmailScenarioEnum.cs index b11006898..40acc6c5c 100644 --- a/IRaCIS.Core.Domain.Share/Common/EmailScenarioEnum.cs +++ b/IRaCIS.Core.Domain.Share/Common/EmailScenarioEnum.cs @@ -322,6 +322,8 @@ namespace IRaCIS.Core.Domain.Share /// TrialTraining_ExpirationNotification = 72, + + IdentityUser_ModifyPassword=73, }