PM阅片跟踪列表

Test.EIImageViewer
hang 2022-06-14 13:58:08 +08:00
parent 09fc253d67
commit 2d8f739f12
10 changed files with 359 additions and 118 deletions

View File

@ -72,6 +72,13 @@
<param name="queryVisitTask"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Service.VisitTaskService.GetReadingTaskList(IRaCIS.Core.Application.ViewModel.VisitTaskQuery)">
<summary>
获取影像阅片列表 相比而言多了几个字段 和配置信息
</summary>
<param name="queryVisitTask"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Service.VisitTaskService.GetSubjectAssignList(IRaCIS.Core.Application.ViewModel.SubjectAssignQuery)">
<summary>
获取手动分配 未分配的Subject列表(IsHaveAssigned 传递false)
@ -3257,6 +3264,13 @@
<param name="trialConfig"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.TrialConfigService.ConfigTrialReadingTaskViewRule(IRaCIS.Core.Application.Contracts.TrialReadingTaskViewConfig)">
<summary>
配置项目赌片规则信息
</summary>
<param name="trialConfig"></param>
<returns></returns>
</member>
<member name="T:IRaCIS.Core.Application.Triggers.AddVisitPlanTrigger">
<summary>
添加访视计划 要给改项目下的所有Subject 添加该访视

View File

@ -64,6 +64,21 @@ namespace IRaCIS.Core.Application.ViewModel
}
public class ReadingTaskView: VisitTaskView
{
//建议完成时间
public int SuggesteDays { get; set; }
public ReadingTaskState ReadingTaskState { get; set; }
public DateTime? SignTime { get; set; }
//是否回退过
public bool IsReturned { get; set; }
}
public class HistoryReadingDoctorUser
{

View File

@ -89,13 +89,13 @@ namespace IRaCIS.Core.Application.Service
public async Task<PageOutput<JudgeVisitTaskView>/*, object)*/> GetJudgeVisitTaskList(VisitTaskQuery queryVisitTask)
{
var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == queryVisitTask.TrialId )
var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == queryVisitTask.TrialId)
.WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId)
.WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId)
.WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent)
.WhereIf(queryVisitTask.DoctorUserId != null, t => t.DoctorUserId == queryVisitTask.DoctorUserId)
.WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory)
.WhereIf(queryVisitTask.ReadingCategory == null, t => t.ReadingCategory!= ReadingCategory.Judge)
.WhereIf(queryVisitTask.ReadingCategory == null, t => t.ReadingCategory != ReadingCategory.Judge)
.WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState)
.WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName))
.WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => t.Subject.Code.Contains(queryVisitTask.SubjectCode))
@ -114,6 +114,41 @@ namespace IRaCIS.Core.Application.Service
}
/// <summary>
/// 获取影像阅片列表 相比而言多了几个字段 和配置信息
/// </summary>
/// <param name="queryVisitTask"></param>
/// <returns></returns>
[HttpPost]
public async Task<(PageOutput<ReadingTaskView>, object)> GetReadingTaskList(VisitTaskQuery queryVisitTask)
{
var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == queryVisitTask.TrialId)
.WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId)
.WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId)
.WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent)
.WhereIf(queryVisitTask.DoctorUserId != null, t => t.DoctorUserId == queryVisitTask.DoctorUserId)
.WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory)
.WhereIf(queryVisitTask.ReadingCategory == null, t => t.ReadingCategory != ReadingCategory.Judge)
.WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState)
.WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName))
.WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => t.Subject.Code.Contains(queryVisitTask.SubjectCode))
.WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime > queryVisitTask.BeginAllocateDate)
.WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime < queryVisitTask.EndAllocateDate.Value.AddDays(1))
.ProjectTo<ReadingTaskView>(_mapper.ConfigurationProvider);
var defalutSortArray = new string[] { nameof(VisitTask.IsUrgent) + " desc", nameof(VisitTask.SubjectId) };
var pageList = await visitTaskQueryable.ToPagedListAsync(queryVisitTask.PageIndex, queryVisitTask.PageSize, queryVisitTask.SortField, queryVisitTask.Asc, string.IsNullOrWhiteSpace(queryVisitTask.SortField), defalutSortArray);
var trialTaskConfig = _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).ProjectTo<TrialTaskConfig>(_mapper.ConfigurationProvider).FirstOrDefault();
return (pageList, trialTaskConfig);
}
/// <summary>
/// 获取手动分配 未分配的Subject列表(IsHaveAssigned 传递false)
/// </summary>
@ -121,7 +156,7 @@ namespace IRaCIS.Core.Application.Service
[HttpPost]
public async Task<PageOutput<SubjectAssignView>> GetSubjectAssignList(SubjectAssignQuery querySubjectAssign)
{
var subjectQuery = _subjectRepository.Where(t => t.TrialId == querySubjectAssign.TrialId )
var subjectQuery = _subjectRepository.Where(t => t.TrialId == querySubjectAssign.TrialId)
.WhereIf(querySubjectAssign.SiteId != null, t => t.SiteId == querySubjectAssign.SiteId)
.WhereIf(querySubjectAssign.SubjectId != null, t => t.Id == querySubjectAssign.SubjectId)
.WhereIf(querySubjectAssign.IsHaveAssigned != null && querySubjectAssign.IsHaveAssigned == true, t => t.SubjectDoctorList.Count() > 0)
@ -160,7 +195,7 @@ namespace IRaCIS.Core.Application.Service
var armEnum = assginSubjectDoctorCommand.DoctorUserIdArmList.Where(t => t.DoctorUserId == doctorUserId).First().ArmEnum;
await _subjectUserRepository.AddAsync(new SubjectUser() { TrialId = assginSubjectDoctorCommand.TrialId, SubjectId = subjectId, DoctorUserId = doctorUserId, ArmEnum= armEnum, AssignTime = DateTime.Now });
await _subjectUserRepository.AddAsync(new SubjectUser() { TrialId = assginSubjectDoctorCommand.TrialId, SubjectId = subjectId, DoctorUserId = doctorUserId, ArmEnum = armEnum, AssignTime = DateTime.Now });
}
//await _subjectRepository.BatchUpdateNoTrackingAsync(t => t.Id == subjectId, u => new Subject() { IsAssignDoctorUser = true });
@ -200,7 +235,7 @@ namespace IRaCIS.Core.Application.Service
[UnitOfWork]
public async Task<IResponseOutput> ManualAssignDoctorApplyTask(AssignConfirmCommand assignConfirmCommand)
{
var trialId= assignConfirmCommand.TrialId;
var trialId = assignConfirmCommand.TrialId;
//获取项目配置 判断应该分配几个医生
//var trialConfig = (await _trialRepository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.ReadingType, t.IsFollowVisitAutoAssign, t.IsFollowGlobalVisitAutoAssign, t.FollowGlobalVisitAutoAssignDefaultState, t.TaskAllocateObjEnum }).FirstOrDefaultAsync()).IfNullThrowException();
@ -284,41 +319,41 @@ namespace IRaCIS.Core.Application.Service
{
//自动分配的话,需要把手动分配的给删掉
throw new BusinessValidationFailedException("自动分配算法正在开发,还未完成");
var trialId = autoSubjectAssignCommand.TrialId;
//获取所有的Subject 目前的分配情况
var subjectList = _subjectRepository.Where(t => t.TrialId == trialId).Select(t => new { SubjectId = t.Id, DoctorUserIdList = t.SubjectDoctorList.Select(t => t.DoctorUserId) }).ToList();
var subjectList = _subjectRepository.Where(t => t.TrialId == trialId).Select(t => new { SubjectId = t.Id, DoctorUserList = t.SubjectDoctorList.Select(t => new { t.DoctorUserId, t.ArmEnum }) }).ToList();
//受试者总数
var subjectCount = subjectList.Count();
//获取待分配的医生列表 指导分配医生的数量
var waitAllocationDoctorList = _taskAllocationRuleRepository.Where(t => t.TrialId == trialId && t.IsEnable)
.Select(t => new { t.DoctorUserId, t.PlanReadingRatio, Weight = (double)(subjectCount * t.PlanReadingRatio) % 100 })
.Select(t => new { t.DoctorUserId, t.PlanReadingRatio, Weight = (subjectCount * t.PlanReadingRatio) % 100 })
.OrderByDescending(t => t.PlanReadingRatio).ThenByDescending(t => t.Weight).ToList();
//获取项目配置 判断应该分配几个医生
var trialConfig = (await _trialRepository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.ReadingType, t.IsFollowVisitAutoAssign, t.IsFollowGlobalVisitAutoAssign, t.FollowGlobalVisitAutoAssignDefaultState, t.TaskAllocateObjEnum }).FirstOrDefaultAsync()).IfNullThrowException();
//存放分配后Subject 医生的情况
var assignedSubjectDoctorList = subjectList.Clone().SelectMany(t => t.DoctorUserIdList.Select(c => new { t.SubjectId, DoctorUserId = c })).ToList();
var assignedSubjectDoctorList = subjectList.Clone().SelectMany(t => t.DoctorUserList.Select(c => new { t.SubjectId, DoctorUserId = c.DoctorUserId, c.ArmEnum })).ToList();
//给医生分配Subject 前验证 已经分配的数据是否符合分配的规范
foreach (var doctor in waitAllocationDoctorList)
{
//该医生的目前已分配到的受试者
var hasAssignedSubjectIdList = assignedSubjectDoctorList.Where(t => t.DoctorUserId == doctor.DoctorUserId).Select(t => t.SubjectId).ToList();
var hasAssignedSubjectIdList = assignedSubjectDoctorList.Where(t => t.DoctorUserId == doctor.DoctorUserId).Select(t => t.SubjectId).Distinct().ToList();
//验证已经分配的Subject 是否 符合项目配置规范
foreach (var subjectId in hasAssignedSubjectIdList)
{
var hasAssignDoctorCount = subjectList.Where(t => t.SubjectId == subjectId).FirstOrDefault().DoctorUserIdList.Count();
var hasAssignDoctorCount = subjectList.Where(t => t.SubjectId == subjectId).FirstOrDefault().DoctorUserList.Count();
//分配两个医生
if (trialConfig.ReadingType == ReadingMethod.Double)
@ -343,17 +378,21 @@ namespace IRaCIS.Core.Application.Service
//验证通过 给医生开始分配
if (trialConfig.ReadingType == ReadingMethod.Single)
{
//eg 10个Sujbect 都是1/4 先分配整数 再分配 10%4=2个
//不够分的subject数量 适用于 2个Subject 4个医生 这种
//特殊: 不够分的subject数量 适用于 2个Subject 4个医生 这种
var specialSubjectCount = subjectCount % waitAllocationDoctorList.Count();
//按照医生维度 分配Subject
foreach (var doctor in waitAllocationDoctorList)
{
//该医生的目前已分配到的受试者
var hasAssignedSubjectIdList = assignedSubjectDoctorList.Where(t => t.DoctorUserId == doctor.DoctorUserId).Select(t => t.SubjectId).ToList();
var hasAssignedSubjectIdList = assignedSubjectDoctorList.Where(t => t.DoctorUserId == doctor.DoctorUserId).Select(t => t.SubjectId).Distinct().ToList();
//已分配的Subject 数量
var hasAssignedSubjectCount = hasAssignedSubjectIdList.Count();
@ -379,7 +418,7 @@ namespace IRaCIS.Core.Application.Service
//从未分配的Subjct找到可以分配的分配给该医生
var allAssignedSubjectIdList = assignedSubjectDoctorList.Select(t => t.SubjectId).ToList();
var allAssignedSubjectIdList = assignedSubjectDoctorList.Select(t => t.SubjectId).Distinct().ToList();
//取需要分配的数量 并且没有分配给其他医生的包括自己
var assignSubjectIdList = subjectList.Where(t => !allAssignedSubjectIdList.Contains(t.SubjectId)).Select(t => t.SubjectId).Take(planSubjectCount - hasAssignedSubjectCount).ToList();
@ -387,25 +426,115 @@ namespace IRaCIS.Core.Application.Service
foreach (var assignSubjectId in assignSubjectIdList)
{
//将分配结果记录
assignedSubjectDoctorList.Add(new { SubjectId = assignSubjectId, DoctorUserId = doctor.DoctorUserId });
assignedSubjectDoctorList.Add(new { SubjectId = assignSubjectId, DoctorUserId = doctor.DoctorUserId, ArmEnum = 0 });
await _subjectUserRepository.AddAsync(new SubjectUser() { TrialId = trialId, SubjectId = assignSubjectId, DoctorUserId = doctor.DoctorUserId, AssignTime = DateTime.Now });
}
}
}
//验证是否所有Subject 是否分配好
if (assignedSubjectDoctorList.Select(t => t.SubjectId).Count() != subjectCount)
else
{
throw new BusinessValidationFailedException("分配算法有问题有Subject 未分配");
throw new BusinessValidationFailedException("当前有医生已分配的Subject 数量超过了计划分配Subject数量不支持自动分配");
}
}
}
if (trialConfig.ReadingType == ReadingMethod.Double)
{
var specialSubjectCount = subjectCount % waitAllocationDoctorList.Count();
//按照医生维度 分配Subject
foreach (var doctor in waitAllocationDoctorList)
{
//该医生的目前已分配到的受试者
var hasAssignedSubjectIdList = assignedSubjectDoctorList.Where(t => t.DoctorUserId == doctor.DoctorUserId).Select(t => t.SubjectId).ToList();
//已分配的Subject 数量
var hasAssignedSubjectCount = hasAssignedSubjectIdList.Count();
//该医生计划分配到的Subject 最接近的整数数量
var planSubjectCount = (int)Math.Floor((double)(subjectCount * doctor.PlanReadingRatio *2) / 100);
//权重大的,将特殊的分配
if (doctor.Weight >= 50)
{
if (specialSubjectCount > 0)
{
specialSubjectCount--;
planSubjectCount++;
}
else
{
planSubjectCount++;
}
}
//如果计划的数量 大于已经分配的数量 那么该医生 可以分配新的Subject
if (planSubjectCount > hasAssignedSubjectCount)
{
//分配给其他医生 已经满了的
var otherExceptDoctorIdList = assignedSubjectDoctorList.Where(t => t.DoctorUserId != doctor.DoctorUserId).GroupBy(t => t.SubjectId)
.Select(g => new { SubjectId = g.Key, DoctorCount = g.Count() }).Where(c => c.DoctorCount == 2).Select(t => t.SubjectId).ToList();
//取需要分配的数量 并且没有分配给其他医生的包括自己
var assignSubjectIdList = subjectList.Where(t => !hasAssignedSubjectIdList.Contains(t.SubjectId) && !otherExceptDoctorIdList.Contains(t.SubjectId))
.Select(t => t.SubjectId).Take((planSubjectCount - hasAssignedSubjectCount) * 2).ToList();
foreach (var assignSubjectId in assignSubjectIdList)
{
var otherHaveAssignedSubject= assignedSubjectDoctorList.Where(t => t.SubjectId == assignSubjectId).FirstOrDefault();
if (otherHaveAssignedSubject != null)
{
if (otherHaveAssignedSubject.ArmEnum == 1)
{
assignedSubjectDoctorList.Add(new { SubjectId = assignSubjectId, DoctorUserId = doctor.DoctorUserId, ArmEnum = 2 });
await _subjectUserRepository.AddAsync(new SubjectUser() { TrialId = trialId, SubjectId = assignSubjectId, DoctorUserId = doctor.DoctorUserId, ArmEnum = 2, AssignTime = DateTime.Now });
}
else if(otherHaveAssignedSubject.ArmEnum == 2)
{
assignedSubjectDoctorList.Add(new { SubjectId = assignSubjectId, DoctorUserId = doctor.DoctorUserId, ArmEnum = 1 });
await _subjectUserRepository.AddAsync(new SubjectUser() { TrialId = trialId, SubjectId = assignSubjectId, DoctorUserId = doctor.DoctorUserId, ArmEnum = 1, AssignTime = DateTime.Now });
}
}
else
{
assignedSubjectDoctorList.Add(new { SubjectId = assignSubjectId, DoctorUserId = doctor.DoctorUserId, ArmEnum = 1 });
await _subjectUserRepository.AddAsync(new SubjectUser() { TrialId = trialId, SubjectId = assignSubjectId, DoctorUserId = doctor.DoctorUserId, ArmEnum = 1, AssignTime = DateTime.Now });
}
}
}
else
{
throw new BusinessValidationFailedException("当前有医生已分配的Subject 数量超过了计划分配Subject数量不支持自动分配");
}
}
await _subjectUserRepository.SaveChangesAsync();
//如果是2个Subject 3个医生 这种 都是百分之33的比率
// 10 3
@ -414,7 +543,7 @@ namespace IRaCIS.Core.Application.Service
#region 按照Subject 遍历去分
#region 完全按照Subject 遍历去分
//foreach (var subject in subjectList)
//{
@ -469,10 +598,21 @@ namespace IRaCIS.Core.Application.Service
#endregion
return ResponseOutput.Ok();
}
//验证是否所有Subject 是否分配好
if (assignedSubjectDoctorList.Select(t => t.SubjectId).Distinct().Count() != subjectCount)
{
throw new BusinessValidationFailedException("分配算法有问题有Subject 未分配");
}
await _subjectUserRepository.SaveChangesAsync();
return ResponseOutput.Ok();
}
/// <summary>
/// 任务 手动分配 重新分配 确认 取消分配
/// </summary>分配

View File

@ -40,6 +40,9 @@ namespace IRaCIS.Core.Application.Service
.ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName))
.ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName));
CreateMap<VisitTask, JudgeVisitTaskView>()
.ForMember(o => o.SiteId, t => t.MapFrom(u => u.Subject.SiteId))
.ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.Subject.TrialSite.TrialSiteCode))
@ -51,6 +54,18 @@ namespace IRaCIS.Core.Application.Service
.ForMember(o => o.HistoryReadingDoctorUserList, t => t.MapFrom(u => u.JudgeVisitList));
CreateMap<VisitTask, ReadingTaskView>()
.ForMember(o => o.SiteId, t => t.MapFrom(u => u.Subject.SiteId))
.ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.Subject.TrialSite.TrialSiteCode))
.ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.Subject.Code))
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode))
.ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName))
.ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName))
.ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName));
CreateMap<VisitTask, HistoryReadingDoctorUser>()
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode))
.ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName))

View File

@ -142,6 +142,15 @@ namespace IRaCIS.Core.Application.Contracts
}
public class TrialReadingTaskViewConfig
{
public Guid TrialId { get; set; }
public ReadingTaskViewMethod ReadingTaskViewEnum { get; set; }
public bool IsReadingTaskViewInOrder { get; set; } = true;
}
public class TrialUrgentConfig
{
public Guid TrialId { get; set; }

View File

@ -393,5 +393,23 @@ namespace IRaCIS.Core.Application
return ResponseOutput.Ok(await _trialRepository.SaveChangesAsync());
}
/// <summary>
/// 配置项目赌片规则信息
/// </summary>
/// <param name="trialConfig"></param>
/// <returns></returns>
[HttpPut]
public async Task<IResponseOutput> ConfigTrialReadingTaskViewRule(TrialReadingTaskViewConfig trialConfig)
{
var trialInfo = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialConfig.TrialId)).IfNullThrowException();
_mapper.Map(trialConfig, trialInfo);
return ResponseOutput.Ok(await _trialRepository.SaveChangesAsync());
}
}
}

View File

@ -21,6 +21,9 @@ namespace IRaCIS.Core.Application.Service
CreateMap<TrialTaskConfig, Trial>().ForMember(d => d.Id, u => u.MapFrom(s => s.TrialId)).ReverseMap();
CreateMap<TrialReadingTaskViewConfig, Trial>().ForMember(d => d.Id, u => u.MapFrom(s => s.TrialId)).ReverseMap();
CreateMap<UserTrialCommand, TrialUser>();
CreateMap<TrialSiteCommand, TrialSite>()

View File

@ -61,4 +61,26 @@ namespace IRaCIS.Core.Domain.Share
Special=3
}
public enum ReadingTaskViewMethod
{
//受试者
Subject = 0,
//访视/阅片期
ReadingPeriodOrVisit = 2,
}
public enum ReadingTaskState
{
WaitReading=0,
Reading=1,
HaveSigned=2
}
}

View File

@ -132,7 +132,7 @@ namespace IRaCIS.Core.Domain.Models
//建议完成时间
public int SuggesteDays { get; set; }
public int ReadingTaskState { get; set; }
public ReadingTaskState ReadingTaskState { get; set; }
public DateTime? SignTime { get; set; }

View File

@ -327,6 +327,11 @@ namespace IRaCIS.Core.Domain.Models
public TaskAllocateDefaultState FollowGlobalVisitAutoAssignDefaultState { get; set; }
public ReadingTaskViewMethod ReadingTaskViewEnum { get; set; }
public bool IsReadingTaskViewInOrder { get; set; } = true;
//public Guid? ReviewTypeId { get; set; } = Guid.Empty;
//[ForeignKey("ReviewTypeId")]