Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details

IRC_NewDev
he 2024-06-12 13:45:18 +08:00
commit f6612286d9
18 changed files with 679 additions and 339 deletions

View File

@ -30,6 +30,8 @@ using IRaCIS.Core.Application.Helper;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Contracts;
using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO; using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO;
using DocumentFormat.OpenXml.Spreadsheet;
using AutoMapper.QueryableExtensions;
namespace IRaCIS.Api.Controllers namespace IRaCIS.Api.Controllers
{ {
@ -96,14 +98,55 @@ namespace IRaCIS.Api.Controllers
/// <summary> 系统用户登录接口[New] </summary> /// <summary> 系统用户登录接口[New] </summary>
[HttpPost, Route("user/login")] [HttpPost, Route("user/login")]
[AllowAnonymous] [AllowAnonymous]
public async Task<IResponseOutput<LoginReturnDTO>> Login(UserLoginDTO loginUser, [FromServices] IEasyCachingProvider provider, [FromServices] IUserService _userService, public async Task<IResponseOutput> Login(UserLoginDTO loginUser,
[FromServices] IEasyCachingProvider provider,
[FromServices] IUserService _userService,
[FromServices] ITokenService _tokenService, [FromServices] ITokenService _tokenService,
[FromServices] IReadingImageTaskService readingImageTaskService, [FromServices] IReadingImageTaskService readingImageTaskService,
[FromServices] IConfiguration configuration) IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig,
IMailVerificationService _mailVerificationService)
{ {
//MFA 邮箱验证 前端传递用户Id 和MFACode
if (loginUser.UserId != null && _verifyConfig.CurrentValue.OpenLoginMFA)
{
Guid userId = (Guid)loginUser.UserId;
//验证MFA 编码是否有问题 ,前端要拆开,自己调用验证的逻辑
//await _userService.VerifyMFACodeAsync(userId, loginUser.MFACode);
//var loginUser = await _userRepository.Where(u => u.UserName.Equals(userName) && u.Password == password).ProjectTo<UserBasicInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
var basicInfo = await _userService.GetUserBasicInfo(userId, loginUser.Password);
var loginReturn = new LoginReturnDTO() { BasicInfo = basicInfo };
loginReturn.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(loginReturn.BasicInfo));
// 创建一个 CookieOptions 对象,用于设置 Cookie 的属性
var option = new CookieOptions
{
Expires = DateTime.Now.AddMonths(1), // 设置过期时间为 30 分钟之后
HttpOnly = false, // 确保 cookie 只能通过 HTTP 访问
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None, // 设置 SameSite 属性
Secure = false // 确保 cookie 只能通过 HTTPS 访问
};
HttpContext.Response.Cookies.Append("access_token", loginReturn.JWTStr, option);
// 验证阅片休息时间
await readingImageTaskService.ResetReadingRestTime(userId);
await provider.SetAsync(userId.ToString(), loginReturn.JWTStr, TimeSpan.FromDays(7));
await provider.SetAsync($"{userId.ToString()}_Online", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes));
return ResponseOutput.Ok(loginReturn);
}
else
{
var returnModel = await _userService.Login(loginUser.UserName, loginUser.Password); var returnModel = await _userService.Login(loginUser.UserName, loginUser.Password);
if (returnModel.IsSuccess) if (returnModel.IsSuccess)
@ -169,6 +212,45 @@ namespace IRaCIS.Api.Controllers
#endregion #endregion
var userId = returnModel.Data.BasicInfo.Id;
if (_verifyConfig.CurrentValue.OpenLoginMFA)
{
//发版屏蔽
returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
//MFA 发送邮件
returnModel.Data.IsMFA = true;
var email = returnModel.Data.BasicInfo.EMail;
#region 隐藏Email
// 找到 "@" 符号的位置
int atIndex = email.IndexOf('@');
// 替换 "@" 符号前的中间两位为星号
string visiblePart = email.Substring(0, atIndex);
int startIndex = (visiblePart.Length - 2) / 2;
// 替换中间两位字符为星号
string hiddenPartBeforeAt = visiblePart.Substring(0, startIndex) + "**" + visiblePart.Substring(startIndex + 2);
string afterAt = email.Substring(atIndex + 1);
// 组合隐藏和可见部分
string hiddenEmail = hiddenPartBeforeAt + "@" + afterAt;
#endregion
returnModel.Data.BasicInfo.EMail = hiddenEmail;
await _userService.SendMFAEmail(userId);
}
else
{
returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo)); returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
// 创建一个 CookieOptions 对象,用于设置 Cookie 的属性 // 创建一个 CookieOptions 对象,用于设置 Cookie 的属性
@ -182,16 +264,28 @@ namespace IRaCIS.Api.Controllers
HttpContext.Response.Cookies.Append("access_token", returnModel.Data.JWTStr, option); HttpContext.Response.Cookies.Append("access_token", returnModel.Data.JWTStr, option);
}
var userId = returnModel.Data.BasicInfo.Id.ToString();
//provider.Set(userId, userId, TimeSpan.FromMinutes(AppSettings.LoginExpiredTimeSpan));
// 验证阅片休息时间 // 验证阅片休息时间
await readingImageTaskService.ResetReadingRestTime(returnModel.Data.BasicInfo.Id); await readingImageTaskService.ResetReadingRestTime(returnModel.Data.BasicInfo.Id);
await provider.SetAsync(userId.ToString(), returnModel.Data.JWTStr, TimeSpan.FromDays(7)); await provider.SetAsync(userId.ToString(), returnModel.Data.JWTStr, TimeSpan.FromDays(7));
return returnModel;
await provider.SetAsync($"{userId.ToString()}_Online", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes));
} }
}
return returnModel;
}
}
[HttpGet, Route("imageShare/ShareImage")] [HttpGet, Route("imageShare/ShareImage")]

View File

@ -29,7 +29,7 @@
<param name="doctorId"></param> <param name="doctorId"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,EasyCaching.Core.IEasyCachingProvider,IRaCIS.Application.Services.IUserService,IRaCIS.Core.Application.Auth.ITokenService,IRaCIS.Core.Application.Contracts.IReadingImageTaskService,Microsoft.Extensions.Configuration.IConfiguration)"> <member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,EasyCaching.Core.IEasyCachingProvider,IRaCIS.Application.Services.IUserService,IRaCIS.Core.Application.Auth.ITokenService,IRaCIS.Core.Application.Contracts.IReadingImageTaskService,Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.ServiceVerifyConfigOption},IRaCIS.Application.Services.IMailVerificationService)">
<summary> 系统用户登录接口[New] </summary> <summary> 系统用户登录接口[New] </summary>
</member> </member>
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialInspection(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Application.Contracts.TrialCommand})"> <member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialInspection(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Application.Contracts.TrialCommand})">

View File

@ -60,7 +60,10 @@
"LoginMaxFailCount": 5, "LoginMaxFailCount": 5,
"LoginFailLockMinutes": 1, "LoginFailLockMinutes": 1,
"AutoLoginOutMinutes": 1
"AutoLoginOutMinutes": 1,
"OpenLoginMFA": false
}, },
"SystemEmailSendConfig": { "SystemEmailSendConfig": {

View File

@ -853,11 +853,22 @@
后台托管服务的方式运行 后台托管服务的方式运行
</summary> </summary>
</member> </member>
<member name="M:IRaCIS.Core.Application.Service.ImageAndDoc.DownloadAndUploadService.GetSubjectImageUploadList(System.Guid)"> <member name="M:IRaCIS.Core.Application.Service.ImageAndDoc.DownloadAndUploadService.SubejctRandomReadingTaskNameDeal(System.Guid,System.Guid)">
<summary>
受试者随机阅片,任务进行随机编号
进入阅片任务前随机挑选出该受试者的一个任务然后给该任务一个编号编号给的逻辑是TimePoint Ran+ 已阅任务数量+1
根据当前受试者该标准已阅任务数量(生效失效的任务都算,考虑重阅,最后编号不重复) 第一个就是TimePoint Ran1后面依次随机挑选出来的阅片序号依次递增
</summary>
<param name="subjectId"></param>
<param name="trialReadingCriterionId"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Service.ImageAndDoc.DownloadAndUploadService.GetSubjectImageUploadList(System.Guid,System.Guid)">
<summary> <summary>
获取该受试者任务上传列表(展示已上传情况) 获取该受试者任务上传列表(展示已上传情况)
</summary> </summary>
<param name="subjectId"></param> <param name="subjectId"></param>
<param name="trialReadingCriterionId"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:IRaCIS.Core.Application.Service.ImageAndDoc.DownloadAndUploadService.RequestPackageAndAnonymizImage(System.Guid,System.Guid,System.Boolean)"> <member name="M:IRaCIS.Core.Application.Service.ImageAndDoc.DownloadAndUploadService.RequestPackageAndAnonymizImage(System.Guid,System.Guid,System.Boolean)">
@ -13254,6 +13265,22 @@
<param name="state"></param> <param name="state"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:IRaCIS.Application.Services.UserService.SendMFAEmail(System.Guid)">
<summary>
发送MFA 验证邮件
</summary>
<param name="userId"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Application.Services.UserService.VerifyMFACodeAsync(System.Guid,System.String)">
<summary>
验证MFA 邮件
</summary>
<param name="userId"></param>
<param name="Code"></param>
<returns></returns>
<exception cref="T:IRaCIS.Core.Infrastructure.BusinessValidationFailedException"></exception>
</member>
<member name="M:IRaCIS.Application.Services.UserService.Login(System.String,System.String)"> <member name="M:IRaCIS.Application.Services.UserService.Login(System.String,System.String)">
<summary> <summary>
用户登陆 用户登陆

View File

@ -155,6 +155,8 @@ namespace IRaCIS.Core.Application.Service
await _visitTaskRepository.SaveChangesAsync(); await _visitTaskRepository.SaveChangesAsync();
} }
//基于标准签名 产生任务 (或者手动选择某些访视生成该标准的任务) //基于标准签名 产生任务 (或者手动选择某些访视生成该标准的任务)
public async Task BaseCritrionGenerateVisitTask(Guid trialId, Guid confirmedTrialReadingCriterionId, bool? isManualSelectVisit = null, List<Guid>? subjectVisitIdList = null) public async Task BaseCritrionGenerateVisitTask(Guid trialId, Guid confirmedTrialReadingCriterionId, bool? isManualSelectVisit = null, List<Guid>? subjectVisitIdList = null)
{ {
@ -197,27 +199,30 @@ namespace IRaCIS.Core.Application.Service
foreach (var subjectVisit in subjectGroup.SubjectVisitList) foreach (var subjectVisit in subjectGroup.SubjectVisitList)
{ {
var blindTaskName = string.Empty;
var isNeedClinicalDataSign = IsNeedClinicalDataSign(ReadingCategory.Visit, subjectVisit.IsBaseLine, trialReadingCriterionConfig.TrialReadingCriterionId, clinicalDataConfirmList); var isNeedClinicalDataSign = IsNeedClinicalDataSign(ReadingCategory.Visit, subjectVisit.IsBaseLine, trialReadingCriterionConfig.TrialReadingCriterionId, clinicalDataConfirmList);
var isClinicalDataSign = IsClinicalDataSign(ReadingCategory.Visit, subjectVisit.IsBaseLine, trialReadingCriterionConfig.TrialReadingCriterionId, clinicalDataConfirmList, subjectVisit.Id, trialId); var isClinicalDataSign = IsClinicalDataSign(ReadingCategory.Visit, subjectVisit.IsBaseLine, trialReadingCriterionConfig.TrialReadingCriterionId, clinicalDataConfirmList, subjectVisit.Id, trialId);
if (visitNumList.IndexOf(subjectVisit.VisitNum) == 0) var blindTaskName = string.Empty;
if (trialReadingCriterionConfig.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
{
if (visitNumList.IndexOf(subjectVisit.VisitNum) == 0 && subjectVisit.VisitNum==0)
{ {
blindTaskName = visitBlindConfig.BlindBaseLineName; blindTaskName = visitBlindConfig.BlindBaseLineName;
} }
else else
{
if (trialReadingCriterionConfig.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
{ {
blindTaskName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(subjectVisit.VisitNum); blindTaskName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(subjectVisit.VisitNum);
}
} }
else else
{ {
blindTaskName = visitBlindConfig.BlindFollowUpPrefix; blindTaskName ="Timepoint";
}
} }
//每个访视 根据项目配置生成任务 双审生成两个 //每个访视 根据项目配置生成任务 双审生成两个
TaskUrgentType? taskUrgentType = null; TaskUrgentType? taskUrgentType = null;
@ -661,17 +666,16 @@ namespace IRaCIS.Core.Application.Service
var assignConfigList = await _subjectUserRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.OrignalSubjectUserId == null && t.IsConfirmed).Select(u => new { u.DoctorUserId, u.ArmEnum }).ToListAsync(); var assignConfigList = await _subjectUserRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.OrignalSubjectUserId == null && t.IsConfirmed).Select(u => new { u.DoctorUserId, u.ArmEnum }).ToListAsync();
var blindTaskName = string.Empty;
var isNeedClinicalDataSign = IsNeedClinicalDataSign(ReadingCategory.Visit, subjectVisit.IsBaseLine, trialReadingCriterionConfig.TrialReadingCriterionId, clinicalDataConfirmList); var isNeedClinicalDataSign = IsNeedClinicalDataSign(ReadingCategory.Visit, subjectVisit.IsBaseLine, trialReadingCriterionConfig.TrialReadingCriterionId, clinicalDataConfirmList);
var isClinicalDataSign = IsClinicalDataSign(ReadingCategory.Visit, subjectVisit.IsBaseLine, trialReadingCriterionConfig.TrialReadingCriterionId, clinicalDataConfirmList, subjectVisit.Id, trialId); var isClinicalDataSign = IsClinicalDataSign(ReadingCategory.Visit, subjectVisit.IsBaseLine, trialReadingCriterionConfig.TrialReadingCriterionId, clinicalDataConfirmList, subjectVisit.Id, trialId);
var isFrontTaskNeedSignButNotSign = await _visitTaskRepository.AnyAsync(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.TaskState == TaskState.Effect && t.VisitTaskNum < subjectVisit.VisitNum && t.IsNeedClinicalDataSign == true && t.IsClinicalDataSign == false); var isFrontTaskNeedSignButNotSign = await _visitTaskRepository.AnyAsync(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.TaskState == TaskState.Effect && t.VisitTaskNum < subjectVisit.VisitNum && t.IsNeedClinicalDataSign == true && t.IsClinicalDataSign == false);
var blindTaskName = string.Empty;
if (trialReadingCriterionConfig.IsReadingTaskViewInOrder == ReadingOrder.InOrder) if (trialReadingCriterionConfig.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
{ {
if (visitNumList.IndexOf(subjectVisit.VisitNum) == 0) if (visitNumList.IndexOf(subjectVisit.VisitNum) == 0 && subjectVisit.VisitNum == 0)
{ {
blindTaskName = visitBlindConfig.BlindBaseLineName; blindTaskName = visitBlindConfig.BlindBaseLineName;
} }
@ -683,7 +687,7 @@ namespace IRaCIS.Core.Application.Service
} }
else else
{ {
blindTaskName = visitBlindConfig.BlindFollowUpPrefix; blindTaskName = "Timepoint";
} }

View File

@ -105,7 +105,8 @@ namespace IRaCIS.Core.Application.Service.Allocation
var list = await _repository.Where<ReadingQuestionCriterionTrial>(t => t.TrialId == trialId && t.IsConfirm) var list = await _repository.Where<ReadingQuestionCriterionTrial>(t => t.TrialId == trialId && t.IsConfirm)
.OrderBy(t => t.ShowOrder) .OrderBy(t => t.ShowOrder)
.Select(t => new TrialReadingCriterionDto() { .Select(t => new TrialReadingCriterionDto()
{
TrialReadingCriterionId = t.Id, TrialReadingCriterionId = t.Id,
IsAutoCreate = t.IsAutoCreate, IsAutoCreate = t.IsAutoCreate,
IsAdditionalAssessment = t.IsAdditionalAssessment, IsAdditionalAssessment = t.IsAdditionalAssessment,
@ -943,14 +944,15 @@ namespace IRaCIS.Core.Application.Service.Allocation
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpPost] [HttpPost]
public async Task<(PageOutput<IRUnReadSubjectView>, object)> GetIRUnReadSubjectTaskList(IRUnReadSubjectQuery iRUnReadSubjectQuery) public async Task<IResponseOutput<PageOutput<IRUnReadSubjectView>>> GetIRUnReadSubjectTaskList(IRUnReadSubjectQuery inQuery)
{ {
var trialId = iRUnReadSubjectQuery.TrialId; var trialId = inQuery.TrialId;
var subjectCode = inQuery.SubjectCode;
var trialReadingCriterionId = iRUnReadSubjectQuery.TrialReadingCriterionId; var trialReadingCriterionId = inQuery.TrialReadingCriterionId;
var criterionConfig = await _trialReadingCriterionRepository.Where(x => x.Id == iRUnReadSubjectQuery.TrialReadingCriterionId).FirstNotNullAsync(); var criterionConfig = await _trialReadingCriterionRepository.Where(x => x.Id == inQuery.TrialReadingCriterionId).FirstNotNullAsync();
var readingTool = criterionConfig.ReadingTool; var readingTool = criterionConfig.ReadingTool;
var isReadingTaskViewInOrder = criterionConfig.IsReadingTaskViewInOrder; var isReadingTaskViewInOrder = criterionConfig.IsReadingTaskViewInOrder;
@ -964,13 +966,13 @@ namespace IRaCIS.Core.Application.Service.Allocation
{ {
TrialId = trialId, TrialId = trialId,
TrialReadingCriterionId = trialReadingCriterionId, TrialReadingCriterionId = trialReadingCriterionId,
SubjectCode = iRUnReadSubjectQuery.SubjectCode, SubjectCode = inQuery.SubjectCode,
Page = new PageInput() Page = new PageInput()
{ {
PageIndex = iRUnReadSubjectQuery.PageIndex, PageIndex = inQuery.PageIndex,
PageSize = iRUnReadSubjectQuery.PageSize, PageSize = inQuery.PageSize,
Asc = iRUnReadSubjectQuery.Asc, Asc = inQuery.Asc,
SortField = iRUnReadSubjectQuery.SortField, SortField = inQuery.SortField,
} }
@ -984,15 +986,15 @@ namespace IRaCIS.Core.Application.Service.Allocation
var result = new PageOutput<IRUnReadSubjectView>() var result = new PageOutput<IRUnReadSubjectView>()
{ {
PageSize = iRUnReadSubjectQuery.PageSize, PageSize = inQuery.PageSize,
PageIndex = iRUnReadSubjectQuery.PageIndex, PageIndex = inQuery.PageIndex,
TotalCount = totalCount, TotalCount = totalCount,
CurrentPageData = currentPageData, CurrentPageData = currentPageData,
}; };
// 封装的方法有问题 // 封装的方法有问题
//var result = await visitQuery.ToPagedListAsync(iRUnReadSubjectQuery.PageIndex, iRUnReadSubjectQuery.PageSize, String.IsNullOrEmpty(iRUnReadSubjectQuery.SortField) ? nameof(IRUnReadSubjectView.SubjectId) : iRUnReadSubjectQuery.SortField, iRUnReadSubjectQuery.Asc); //var result = await visitQuery.ToPagedListAsync(iRUnReadSubjectQuery.PageIndex, iRUnReadSubjectQuery.PageSize, String.IsNullOrEmpty(iRUnReadSubjectQuery.SortField) ? nameof(IRUnReadSubjectView.SubjectId) : iRUnReadSubjectQuery.SortField, iRUnReadSubjectQuery.Asc);
return (result, new return ResponseOutput.Ok(result, new
{ {
RandomReadInfo = new IRUnReadOutDto(), RandomReadInfo = new IRUnReadOutDto(),
IsReadingTaskViewInOrder = isReadingTaskViewInOrder, IsReadingTaskViewInOrder = isReadingTaskViewInOrder,
@ -1004,26 +1006,15 @@ namespace IRaCIS.Core.Application.Service.Allocation
CriterionType = criterionConfig.CriterionType, CriterionType = criterionConfig.CriterionType,
}); });
} }
else else if(isReadingTaskViewInOrder == ReadingOrder.SubjectRandom)
{ {
var taskQuery = _visitTaskRepository.Where(x => x.TrialId == iRUnReadSubjectQuery.TrialId && x.DoctorUserId == _userInfo.Id && x.TaskState == TaskState.Effect && x.TrialReadingCriterionId == trialReadingCriterionId) var critrion = await _trialReadingCriterionRepository.FindAsync(trialReadingCriterionId);
// .Where(x=>x.Subject.ClinicalDataList.Any(c => c.IsSign && (c.ReadingId == x.SouceReadModuleId || c.ReadingId == x.SourceSubjectVisitId)))
.Where(x => !x.Subject.IsDeleted).Where(x => (x.IsNeedClinicalDataSign && x.IsClinicalDataSign) || !x.IsNeedClinicalDataSign);
IRUnReadOutDto iRUnReadOut = new IRUnReadOutDto() var visitQuery = _visitTaskRepository.Where(x => x.TrialId == trialId && x.DoctorUserId == _userInfo.Id && x.TaskState == TaskState.Effect)
{
FinishJudgeTaskCount = await taskQuery.Where(x => x.ReadingCategory == ReadingCategory.Judge && x.ReadingTaskState == ReadingTaskState.HaveSigned).CountAsync(),
FinishTaskCount = await taskQuery.Where(x => x.ReadingCategory != ReadingCategory.Judge && x.ReadingTaskState == ReadingTaskState.HaveSigned).CountAsync(),
SuggesteFinishedTime = await taskQuery.Where(x => x.ReadingTaskState != ReadingTaskState.HaveSigned).MaxAsync(x => x.SuggesteFinishedTime),
UnReadJudgeTaskCount = await taskQuery.Where(x => x.ReadingCategory == ReadingCategory.Judge && x.ReadingTaskState != ReadingTaskState.HaveSigned).CountAsync(),
UnReadTaskCount = await taskQuery.Where(x => x.ReadingCategory != ReadingCategory.Judge && x.ReadingTaskState != ReadingTaskState.HaveSigned).CountAsync(),
};
var visitGroupQuery = taskQuery.GroupBy(x => new { x.SubjectId, x.Subject.Code, x.BlindSubjectCode });
.WhereIf(!string.IsNullOrEmpty(subjectCode), t => (t.Subject.Code.Contains(subjectCode!) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(subjectCode!) && t.IsAnalysisCreate));
var visitGroupQuery = visitQuery.GroupBy(x => new { x.SubjectId, x.Subject.Code, x.BlindSubjectCode });
var visitTaskQuery = visitGroupQuery.Select(x => new IRUnReadSubjectView() var visitTaskQuery = visitGroupQuery.Select(x => new IRUnReadSubjectView()
{ {
@ -1033,22 +1024,17 @@ namespace IRaCIS.Core.Application.Service.Allocation
SuggesteFinishedTime = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned).Min(x => x.SuggesteFinishedTime), SuggesteFinishedTime = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned).Min(x => x.SuggesteFinishedTime),
//未读任务量 //未读任务量
UnReadTaskCount = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned).Count(), UnReadCanReadTaskCount = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned).Count(),
//未读 里可读任务量
UnReadCanReadTaskCount = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned && y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true)
//不能对包含聚合或子查询的表达式执行聚合函数
//&& !x.Any(t => t.ReadingTaskState != ReadingTaskState.HaveSigned && t.IsNeedClinicalDataSign == true && t.IsClinicalDataSign == false && t.VisitTaskNum<y.VisitTaskNum )
).Count(),
UrgentCount = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned).Where(x => x.IsUrgent).Count(),
//已读任务量 //已读任务量
HaveReadTaskCount = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState == ReadingTaskState.HaveSigned).Count(), HaveReadTaskCount = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState == ReadingTaskState.HaveSigned).Count(),
ExistReadingApply = x.Any(y => (y.ReReadingApplyState == ReReadingApplyState.DocotorHaveApplyed && y.TrialReadingCriterionId == trialReadingCriterionId) || y.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed), ExistReadingApply = x.Any(y => (y.ReReadingApplyState == ReReadingApplyState.DocotorHaveApplyed && y.TrialReadingCriterionId == trialReadingCriterionId) || y.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed),
//查出所有未读的 未读的可读的 在这个列表基础上 过滤下 y.IsFrontTaskNeedSignButNotSign==false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true) 这样容易排错 确认这三个字段是否维护有误 UnReadCanReadTaskList = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned)
UnReadTaskList = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned).OrderBy(x => x.VisitTaskNum) .OrderBy(x => x.VisitTaskNum)
.Select(u => new IRUnreadTaskView() .Select(u => new IRUnreadTaskView()
{ {
Id = u.Id, Id = u.Id,
@ -1065,22 +1051,48 @@ namespace IRaCIS.Core.Application.Service.Allocation
IsClinicalDataSign = u.IsClinicalDataSign, IsClinicalDataSign = u.IsClinicalDataSign,
IsFrontTaskNeedSignButNotSign = u.IsFrontTaskNeedSignButNotSign IsFrontTaskNeedSignButNotSign = u.IsFrontTaskNeedSignButNotSign
}) })
.ToList(), .ToList()
}).Where(x => x.UnReadCanReadTaskCount > 0); }).Where(x => x.UnReadCanReadTaskCount > 0);
var pageList = await visitTaskQuery.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField)? nameof(IRUnReadSubjectView.UnReadCanReadTaskCount) : inQuery.SortField, inQuery.Asc );
var totalCount = await visitGroupQuery.CountAsync();
var currentPageData = await visitTaskQuery.ToListAsync(); return ResponseOutput.Ok(pageList, new
{
RandomReadInfo = new IRUnReadOutDto(),
IsReadingTaskViewInOrder = isReadingTaskViewInOrder,
ReadingTool = readingTool,
IseCRFShowInDicomReading = criterionConfig.IseCRFShowInDicomReading,
IsReadingShowSubjectInfo = criterionConfig.IsReadingShowSubjectInfo,
IsReadingShowPreviousResults = criterionConfig.IsReadingShowPreviousResults,
DigitPlaces = criterionConfig.DigitPlaces,
CriterionType = criterionConfig.CriterionType,
});
}
else
{
var taskQuery = _visitTaskRepository.Where(x => x.TrialId == inQuery.TrialId && x.DoctorUserId == _userInfo.Id && x.TaskState == TaskState.Effect && x.TrialReadingCriterionId == trialReadingCriterionId)
// .Where(x=>x.Subject.ClinicalDataList.Any(c => c.IsSign && (c.ReadingId == x.SouceReadModuleId || c.ReadingId == x.SourceSubjectVisitId)))
.Where(x => !x.Subject.IsDeleted).Where(x => (x.IsNeedClinicalDataSign && x.IsClinicalDataSign) || !x.IsNeedClinicalDataSign);
IRUnReadOutDto iRUnReadOut = new IRUnReadOutDto()
{
FinishJudgeTaskCount = await taskQuery.Where(x => x.ReadingCategory == ReadingCategory.Judge && x.ReadingTaskState == ReadingTaskState.HaveSigned).CountAsync(),
FinishTaskCount = await taskQuery.Where(x => x.ReadingCategory != ReadingCategory.Judge && x.ReadingTaskState == ReadingTaskState.HaveSigned).CountAsync(),
SuggesteFinishedTime = await taskQuery.Where(x => x.ReadingTaskState != ReadingTaskState.HaveSigned).MaxAsync(x => x.SuggesteFinishedTime),
UnReadJudgeTaskCount = await taskQuery.Where(x => x.ReadingCategory == ReadingCategory.Judge && x.ReadingTaskState != ReadingTaskState.HaveSigned).CountAsync(),
UnReadTaskCount = await taskQuery.Where(x => x.ReadingCategory != ReadingCategory.Judge && x.ReadingTaskState != ReadingTaskState.HaveSigned).CountAsync(),
};
var result = new PageOutput<IRUnReadSubjectView>() var result = new PageOutput<IRUnReadSubjectView>()
{ {
PageSize = iRUnReadSubjectQuery.PageSize, PageSize = inQuery.PageSize,
PageIndex = iRUnReadSubjectQuery.PageIndex, PageIndex = inQuery.PageIndex,
TotalCount = totalCount, TotalCount = 0,
CurrentPageData = currentPageData, CurrentPageData = null,
}; };
return (result, new return ResponseOutput.Ok(result, new
{ {
IsReadingTaskViewInOrder = isReadingTaskViewInOrder, IsReadingTaskViewInOrder = isReadingTaskViewInOrder,
RandomReadInfo = iRUnReadOut, RandomReadInfo = iRUnReadOut,
@ -1114,8 +1126,7 @@ namespace IRaCIS.Core.Application.Service.Allocation
var critrion = await _trialReadingCriterionRepository.FindAsync(trialReadingCriterionId); var critrion = await _trialReadingCriterionRepository.FindAsync(trialReadingCriterionId);
var visitQuery = _visitTaskRepository.Where(x => x.TrialId == inDto.TrialId && x.DoctorUserId == _userInfo.Id var visitQuery = _visitTaskRepository.Where(x => x.TrialId == inDto.TrialId && x.DoctorUserId == _userInfo.Id && x.TaskState == TaskState.Effect)
&& x.TaskState == TaskState.Effect /*&& x.TrialReadingCriterionId== inDto.TrialReadingCriterionId*/)
.WhereIf(inDto.SubjectId!=null,x=>x.SubjectId==inDto.SubjectId) .WhereIf(inDto.SubjectId!=null,x=>x.SubjectId==inDto.SubjectId)
//前序 不存在 未生成任务的访视 //前序 不存在 未生成任务的访视
@ -1211,7 +1222,7 @@ namespace IRaCIS.Core.Application.Service.Allocation
var result = new List<IRUnReadSubjectView>(); var result = new List<IRUnReadSubjectView>();
var propName = string.IsNullOrWhiteSpace(inDto.Page!.SortField) ? "UnReadCanReadTaskCount" : inDto.Page.SortField; var propName = string.IsNullOrWhiteSpace(inDto.Page!.SortField) ? nameof(IRUnReadSubjectView.UnReadCanReadTaskCount) : inDto.Page.SortField;
var visitTaskOrderQuery = inDto.Page.Asc ? visitTaskQuery.OrderBy(propName) : visitTaskQuery.OrderBy(propName + " desc"); var visitTaskOrderQuery = inDto.Page.Asc ? visitTaskQuery.OrderBy(propName) : visitTaskQuery.OrderBy(propName + " desc");
if (inDto.Page != null) if (inDto.Page != null)

View File

@ -21,6 +21,8 @@ namespace IRaCIS.Application.Services
Task SiteSurveyRejectEmail(MimeMessage messageToSend); Task SiteSurveyRejectEmail(MimeMessage messageToSend);
Task SenMFAVerifyEmail(Guid userId, string userName, string emailAddress, int verificationCode);
Task SendMailEditEmail(Guid userId, string userName, string emailAddress, int verificationCode); Task SendMailEditEmail(Guid userId, string userName, string emailAddress, int verificationCode);
Task AnolymousSendEmailForResetAccount(string emailAddress, int verificationCode); Task AnolymousSendEmailForResetAccount(string emailAddress, int verificationCode);
@ -91,6 +93,66 @@ namespace IRaCIS.Application.Services
return str; return str;
} }
//MFA
public async Task SenMFAVerifyEmail(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));
//主题
//---[来自{0}] 关于MFA邮箱验证的提醒
messageToSend.Subject = _localizer["Mail_EmailMFATopic", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN];
var builder = new BodyBuilder();
var pathToFile = _hostEnvironment.WebRootPath
+ Path.DirectorySeparatorChar.ToString()
+ "EmailTemplate"
+ Path.DirectorySeparatorChar.ToString()
//+ "UserOptCommon.html";
+ (_userInfo.IsEn_Us ? "UserOptCommon_US.html" : "UserOptCommon.html");
using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile))
{
var templateInfo = SourceReader.ReadToEnd();
builder.HtmlBody = string.Format(ReplaceCompanyName(templateInfo),
userName,
_localizer["Mail_MFAEmail"],
verificationCode
);
}
messageToSend.Body = builder.ToMessageBody();
EventHandler<MessageSentEventArgs> sucessHandle = (sender, args) =>
{
// args.Response
var code = verificationCode.ToString();
_ = _verificationCodeRepository.AddAsync(new VerificationCode()
{
CodeType = 0,
HasSend = true,
Code = code,
UserId = userId,
ExpirationTime = DateTime.Now.AddMinutes(3)
}).Result;
_ = _verificationCodeRepository.SaveChangesAsync().Result;
};
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle);
}
//重置邮箱 //重置邮箱
public async Task SendMailEditEmail(Guid userId, string userName, string emailAddress, int verificationCode) public async Task SendMailEditEmail(Guid userId, string userName, string emailAddress, int verificationCode)
{ {

View File

@ -21,6 +21,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc.DTO
public string TaskName { get; set; } public string TaskName { get; set; }
public bool IsImageFilter { get; set; }
public string CriterionModalitys { get; set; } public string CriterionModalitys { get; set; }
public Guid? SourceSubjectVisitId { get; set; } public Guid? SourceSubjectVisitId { get; set; }

View File

@ -12,6 +12,7 @@ using Medallion.Threading;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -28,12 +29,13 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
{ {
private readonly IEasyCachingProvider _provider; private readonly IEasyCachingProvider _provider;
private readonly IRepository<SystemAnonymization> _systemAnonymizationRepository; private readonly IRepository<SystemAnonymization> _systemAnonymizationRepository;
private readonly IRepository<VisitTask> _visitTaskRepository;
private readonly IRepository<SubjectVisit> _subjectVisitRepository; private readonly IRepository<SubjectVisit> _subjectVisitRepository;
private readonly IOSSService _oSSService; private readonly IOSSService _oSSService;
private readonly IRepository<StudyMonitor> _studyMonitorRepository; private readonly IRepository<StudyMonitor> _studyMonitorRepository;
private readonly IDistributedLockProvider _distributedLockProvider; private readonly IDistributedLockProvider _distributedLockProvider;
public DownloadAndUploadService(IEasyCachingProvider provider, IRepository<SystemAnonymization> systemAnonymizationRepository, IRepository<SubjectVisit> subjectVisitRepository, IOSSService oSSService, public DownloadAndUploadService(IEasyCachingProvider provider, IRepository<SystemAnonymization> systemAnonymizationRepository, IRepository<SubjectVisit> subjectVisitRepository, IOSSService oSSService,
IRepository<StudyMonitor> studyMonitorRepository, IDistributedLockProvider distributedLockProvider) IRepository<StudyMonitor> studyMonitorRepository, IDistributedLockProvider distributedLockProvider, IRepository<VisitTask> visitTaskRepository)
{ {
_systemAnonymizationRepository = systemAnonymizationRepository; _systemAnonymizationRepository = systemAnonymizationRepository;
@ -43,16 +45,62 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
_studyMonitorRepository = studyMonitorRepository; _studyMonitorRepository = studyMonitorRepository;
_distributedLockProvider = distributedLockProvider; _distributedLockProvider = distributedLockProvider;
_provider = provider; _provider = provider;
_visitTaskRepository = visitTaskRepository;
}
/// <summary>
/// 受试者随机阅片,任务进行随机编号
/// 进入阅片任务前随机挑选出该受试者的一个任务然后给该任务一个编号编号给的逻辑是TimePoint Ran+ 已阅任务数量+1
/// 根据当前受试者该标准已阅任务数量(生效失效的任务都算,考虑重阅,最后编号不重复) 第一个就是TimePoint Ran1后面依次随机挑选出来的阅片序号依次递增
/// </summary>
/// <param name="subjectId"></param>
/// <param name="trialReadingCriterionId"></param>
/// <returns></returns>
public async Task<IResponseOutput> SubejctRandomReadingTaskNameDeal(Guid subjectId, Guid trialReadingCriterionId)
{
//subject 随机阅片 才处理任务编号
if (_visitTaskRepository.Any(t => t.SubjectId == subjectId && t.TrialReadingCriterionId == trialReadingCriterionId && t.TrialReadingCriterion.IsReadingTaskViewInOrder == ReadingOrder.SubjectRandom))
{
//找到 非一致性分析,未签名,状态正常的 并且任务名称是TimePoint的 任务
var needDealTaskList = await _visitTaskRepository.Where(t => t.SubjectId == subjectId && t.TrialReadingCriterionId == trialReadingCriterionId && t.IsAnalysisCreate == false && t.DoctorUserId==_userInfo.Id
&& t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskBlindName == "Timepoint" && t.ReadingCategory == ReadingCategory.Visit
&& (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze), true).ToListAsync();
if (needDealTaskList.Count>0)
{
//已完成的访视任务数量(包含重阅的)
var haveFinishedTaskCount = await _visitTaskRepository.CountAsync(t => t.SubjectId == subjectId && t.TrialReadingCriterionId == trialReadingCriterionId && t.IsAnalysisCreate == false
&& t.ReadingTaskState == ReadingTaskState.HaveSigned && t.ReadingCategory == ReadingCategory.Visit);
//随机赋值编号 比如要处理5个任务实例化一个包含1-5的数组每次随机取出一个
List<int> availableNumbers = Enumerable.Range(haveFinishedTaskCount + 1, needDealTaskList.Count).ToList();
Random rng = new Random();
foreach (var visitTask in needDealTaskList)
{
int randomIndex = rng.Next(availableNumbers.Count);
visitTask.TaskBlindName = $"Timepoint Ran {availableNumbers[randomIndex]}";
availableNumbers.RemoveAt(randomIndex);
}
await _visitTaskRepository.SaveChangesAsync();
}
}
return ResponseOutput.Ok();
} }
/// <summary> /// <summary>
/// 获取该受试者任务上传列表(展示已上传情况) /// 获取该受试者任务上传列表(展示已上传情况)
/// </summary> /// </summary>
/// <param name="subjectId"></param> /// <param name="subjectId"></param>
/// <param name="trialReadingCriterionId"></param>
/// <returns></returns> /// <returns></returns>
public async Task<IResponseOutput<List<SubjectImageUploadDTO>>> GetSubjectImageUploadList(Guid subjectId) public async Task<IResponseOutput<List<SubjectImageUploadDTO>>> GetSubjectImageUploadList(Guid subjectId,Guid trialReadingCriterionId)
{ {
var query = _repository.Where<VisitTask>(t => t.SubjectId == subjectId && t.SourceSubjectVisitId != null && t.DoctorUserId == _userInfo.Id) await SubejctRandomReadingTaskNameDeal(subjectId, trialReadingCriterionId);
var query = _repository.Where<VisitTask>(t => t.SubjectId == subjectId &&t.TrialReadingCriterionId== trialReadingCriterionId && t.SourceSubjectVisitId != null && t.DoctorUserId == _userInfo.Id)
.Select(u => new SubjectImageUploadDTO() .Select(u => new SubjectImageUploadDTO()
{ {
VisitTaskId = u.Id, VisitTaskId = u.Id,
@ -63,6 +111,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
TrialSiteId = u.Subject.TrialSiteId, TrialSiteId = u.Subject.TrialSiteId,
IsImageFilter = u.TrialReadingCriterion.IsImageFilter,
CriterionModalitys = u.TrialReadingCriterion.CriterionModalitys, CriterionModalitys = u.TrialReadingCriterion.CriterionModalitys,
SubjectCode = u.IsSelfAnalysis == true ? u.BlindSubjectCode : u.Subject.Code, SubjectCode = u.IsSelfAnalysis == true ? u.BlindSubjectCode : u.Subject.Code,
@ -535,50 +585,59 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
if (isAnonymize) if (isAnonymize)
{ {
//受试者随机阅片,需要匿名化检查时间
DicomFile dicomFile = await DicomFile.OpenAsync(destinationPath, Encoding.Default); DicomFile dicomFile = await DicomFile.OpenAsync(destinationPath, Encoding.Default);
DicomDataset dataset = dicomFile.Dataset; DicomDataset dataset = dicomFile.Dataset;
dataset.AddOrUpdate(DicomTag.StudyDate, string.Empty);
dataset.AddOrUpdate(DicomTag.StudyTime, string.Empty);
foreach (var item in addOrUpdateFixedFieldList) #region 前端已经匿名化不需要做相关tag匿名化
{ //DicomFile dicomFile = await DicomFile.OpenAsync(destinationPath, Encoding.Default);
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16)); //DicomDataset dataset = dicomFile.Dataset;
dataset.AddOrUpdate(dicomTag, item.ReplaceValue); //foreach (var item in addOrUpdateFixedFieldList)
} //{
foreach (var item in ircFieldList) // var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
{
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16)); // dataset.AddOrUpdate(dicomTag, item.ReplaceValue);
//}
if (dicomTag == DicomTag.ClinicalTrialProtocolID) //foreach (var item in ircFieldList)
{ //{
dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.TrialCode);
} // var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
if (dicomTag == DicomTag.ClinicalTrialSiteID)
{
//dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.TrialSiteCode);
} // if (dicomTag == DicomTag.ClinicalTrialProtocolID)
if (dicomTag == DicomTag.ClinicalTrialSubjectID) // {
{ // dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.TrialCode);
dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.SubjectCode);
} // }
if (dicomTag == DicomTag.ClinicalTrialTimePointID) // if (dicomTag == DicomTag.ClinicalTrialSiteID)
{ // {
dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.VisitNum.ToString()); // //dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.TrialSiteCode);
} // }
if (dicomTag == DicomTag.PatientID) // if (dicomTag == DicomTag.ClinicalTrialSubjectID)
{ // {
dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.TrialCode + "_" + subjectAndVisitInfo.SubjectCode); // dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.SubjectCode);
} // }
// if (dicomTag == DicomTag.ClinicalTrialTimePointID)
// {
// dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.VisitNum.ToString());
// }
// if (dicomTag == DicomTag.PatientID)
// {
// dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.TrialCode + "_" + subjectAndVisitInfo.SubjectCode);
// }
//}
#endregion
}
} }
#endregion #endregion
} }

View File

@ -21,6 +21,8 @@ namespace IRaCIS.Application.Contracts
{ {
public string UserName { get; set; } = string.Empty; public string UserName { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public Guid? UserId { get; set; }
} }
public class LoginReturnDTO public class LoginReturnDTO
@ -28,6 +30,8 @@ namespace IRaCIS.Application.Contracts
public UserBasicInfo BasicInfo { get; set; } = new UserBasicInfo(); public UserBasicInfo BasicInfo { get; set; } = new UserBasicInfo();
public string JWTStr { get; set; }=string.Empty; public string JWTStr { get; set; }=string.Empty;
public bool IsMFA { get; set; } = false;
} }
public class UserBasicInfo public class UserBasicInfo
@ -59,7 +63,7 @@ namespace IRaCIS.Application.Contracts
public string PermissionStr { get; set; } = String.Empty; public string PermissionStr { get; set; } = String.Empty;
public string EMail { get; set; } = string.Empty;
public bool IsFirstAdd { get; set; } public bool IsFirstAdd { get; set; }
public bool IsReviewer { get; set; } = false; public bool IsReviewer { get; set; } = false;

View File

@ -10,6 +10,10 @@ namespace IRaCIS.Application.Services
Task<UserDetailDTO> GetUser(Guid id); Task<UserDetailDTO> GetUser(Guid id);
Task<PageOutput<UserListDTO>> GetUserList(UserListQueryDTO param); Task<PageOutput<UserListDTO>> GetUserList(UserListQueryDTO param);
Task<IResponseOutput<LoginReturnDTO>> Login(string userName, string password); Task<IResponseOutput<LoginReturnDTO>> Login(string userName, string password);
Task<IResponseOutput> VerifyMFACodeAsync(Guid userId, string Code);
Task<IResponseOutput> SendMFAEmail(Guid userId);
Task<UserBasicInfo> GetUserBasicInfo(Guid userId,string pwd);
Task<IResponseOutput> ModifyPassword(EditPasswordCommand editPwModel); Task<IResponseOutput> ModifyPassword(EditPasswordCommand editPwModel);
Task<IResponseOutput> ResetPassword(Guid userId); Task<IResponseOutput> ResetPassword(Guid userId);

View File

@ -15,6 +15,9 @@ using Medallion.Threading;
using EasyCaching.Core; using EasyCaching.Core;
using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Contracts;
using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO; using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO;
using IRaCIS.Core.Application.Auth;
using BeetleX.Redis.Commands;
using IRaCIS.Core.Domain.Models;
namespace IRaCIS.Application.Services namespace IRaCIS.Application.Services
{ {
@ -639,7 +642,70 @@ namespace IRaCIS.Application.Services
} }
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>
/// <returns></returns>
[AllowAnonymous]
public async Task<IResponseOutput> SendMFAEmail(Guid userId)
{
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);
return ResponseOutput.Ok();
}
/// <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 _repository.GetQueryable<VerificationCode>().OrderByDescending(x => x.ExpirationTime).Where(t => t.UserId == userId && t.Code == Code && t.CodeType == VerifyType.Email).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 _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = userId, OptUserId = userId, OptType = UserOptType.MFALogin }, true);
}
}
return ResponseOutput.Ok();
}
/// <summary> /// <summary>
/// 用户登陆 /// 用户登陆
@ -696,8 +762,6 @@ namespace IRaCIS.Application.Services
return ResponseOutput.NotOk(_localizer["User_CheckNameOrPw"], new LoginReturnDTO()); return ResponseOutput.NotOk(_localizer["User_CheckNameOrPw"], new LoginReturnDTO());
} }
if (loginUser.Status == 0) if (loginUser.Status == 0)
@ -751,14 +815,6 @@ namespace IRaCIS.Application.Services
}); });
// 登录 清除缓存
//_cache.Remove(userLoginReturnModel.BasicInfo.Id.ToString());
var userId = loginUser.Id;
await _cache.SetAsync($"{userId.ToString()}_Online", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes));
return ResponseOutput.Ok(userLoginReturnModel); return ResponseOutput.Ok(userLoginReturnModel);
} }

View File

@ -344,6 +344,8 @@ namespace IRaCIS.Core.Application.Contracts
{ {
public string TrialModalitys { get; set;} public string TrialModalitys { get; set;}
public bool IsImageFilter { get; set; }
/// <summary> /// <summary>
/// 项目ID /// 项目ID
@ -506,6 +508,7 @@ namespace IRaCIS.Core.Application.Contracts
public ReadingImageUpload? ImageUploadEnum { get; set; } public ReadingImageUpload? ImageUploadEnum { get; set; }
public Guid TrialReadingCriterionId { get; set; }
} }
@ -864,6 +867,7 @@ namespace IRaCIS.Core.Application.Contracts
public class SetCriterionReadingInfoInDto public class SetCriterionReadingInfoInDto
{ {
public bool IsImageFilter { get; set; }
public string CriterionModalitys { get; set; } public string CriterionModalitys { get; set; }
public ReadingImageDownload? ImageDownloadEnum { get; set; } public ReadingImageDownload? ImageDownloadEnum { get; set; }

View File

@ -317,12 +317,12 @@ namespace IRaCIS.Core.Application
#endregion #endregion
var trialModalitys = _readingQuestionCriterionTrialRepository.Where(t => t.Id == inDto.TrialReadingCriterionId).Select(t => t.Trial.Modalitys).FirstOrDefault(); //var trialModalitys = _readingQuestionCriterionTrialRepository.Where(t => t.Id == inDto.TrialReadingCriterionId).Select(t => t.Trial.Modalitys).FirstOrDefault();
var systemCriterion = await _readingQuestionCriterionSystemRepository.Where(x => x.Id == trialCriterion.ReadingQuestionCriterionSystemId).FirstNotNullAsync(); var systemCriterion = await _readingQuestionCriterionSystemRepository.Where(x => x.Id == trialCriterion.ReadingQuestionCriterionSystemId).FirstNotNullAsync();
await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(x => x.Id == inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial() await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(x => x.Id == inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial()
{ {
CriterionModalitys= trialModalitys, //CriterionModalitys= trialModalitys,
IsOncologyReading = systemCriterion.IsOncologyReading, IsOncologyReading = systemCriterion.IsOncologyReading,
IsSystemSetOncology = systemCriterion.IsOncologyReading, IsSystemSetOncology = systemCriterion.IsOncologyReading,
IseCRFShowInDicomReading = systemCriterion.IseCRFShowInDicomReading, IseCRFShowInDicomReading = systemCriterion.IseCRFShowInDicomReading,
@ -604,6 +604,7 @@ namespace IRaCIS.Core.Application
await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial() await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial()
{ {
IsImageFilter=inDto.IsImageFilter,
ImageDownloadEnum = inDto.ImageDownloadEnum, ImageDownloadEnum = inDto.ImageDownloadEnum,
ImageUploadEnum = inDto.ImageUploadEnum, ImageUploadEnum = inDto.ImageUploadEnum,
CriterionModalitys = inDto.CriterionModalitys, CriterionModalitys = inDto.CriterionModalitys,
@ -953,7 +954,7 @@ namespace IRaCIS.Core.Application
trialInfo.UpdateTime = DateTime.Now; trialInfo.UpdateTime = DateTime.Now;
await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == trialConfig.TrialId && t.IsSigned == false, u => new ReadingQuestionCriterionTrial() { CriterionModalitys = trialConfig.Modalitys }); //await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == trialConfig.TrialId && t.IsSigned == false, u => new ReadingQuestionCriterionTrial() { CriterionModalitys = trialConfig.Modalitys });
return ResponseOutput.Ok(await _repository.SaveChangesAsync()); return ResponseOutput.Ok(await _repository.SaveChangesAsync());
} }
@ -1039,7 +1040,8 @@ namespace IRaCIS.Core.Application
if (trialConfig.TrialCriterionIds.Contains(item.Id)) if (trialConfig.TrialCriterionIds.Contains(item.Id))
{ {
item.IsConfirm = true; item.IsConfirm = true;
item.CriterionModalitys = trialInfo.Modalitys;
//item.CriterionModalitys = trialInfo.Modalitys;
} }
else else
{ {

View File

@ -249,7 +249,8 @@ namespace IRaCIS.Core.Application.Service
CreateMap<ReadingQuestionCriterionTrial, GetTrialReadingInfoOutDto>() CreateMap<ReadingQuestionCriterionTrial, GetTrialReadingInfoOutDto>()
.ForMember(t => t.TrialModalitys, u => u.MapFrom(c => c.Trial.Modalitys)); .ForMember(t => t.TrialModalitys, u => u.MapFrom(c => c.Trial.Modalitys))
.ForMember(t => t.TrialReadingCriterionId, u => u.MapFrom(c => c.Id));
CreateMap<ReadingQuestionTrial, TrialAdditionaQuestion>() CreateMap<ReadingQuestionTrial, TrialAdditionaQuestion>()

View File

@ -93,7 +93,11 @@ namespace IRaCIS.Core.Domain.Models
DeleteUser=10, DeleteUser=10,
UpdateUser=11 UpdateUser=11,
MFALogin=12,
MFALoginFail=13,
} }
} }

View File

@ -279,6 +279,8 @@ namespace IRaCIS.Core.Domain.Models
public ReadingImageDownload? ImageDownloadEnum { get; set; } public ReadingImageDownload? ImageDownloadEnum { get; set; }
public ReadingImageUpload? ImageUploadEnum { get; set; } public ReadingImageUpload? ImageUploadEnum { get; set; }
public bool IsImageFilter { get; set; }
} }
public enum ReadingImageDownload public enum ReadingImageDownload

View File

@ -22,6 +22,8 @@ namespace IRaCIS.Core.Domain.Share
public int LoginFailLockMinutes { get; set; } public int LoginFailLockMinutes { get; set; }
public int AutoLoginOutMinutes { get; set; } public int AutoLoginOutMinutes { get; set; }
public bool OpenLoginMFA { get; set; }
} }
public class SystemEmailSendConfig public class SystemEmailSendConfig