250 lines
11 KiB
C#
250 lines
11 KiB
C#
using IRaCIS.Core.Domain.Share;
|
||
using System.Text;
|
||
using Microsoft.AspNetCore.Hosting;
|
||
using IRaCIS.Core.Infrastructure;
|
||
using Medallion.Threading;
|
||
using FellowOakDicom;
|
||
using FellowOakDicom.Imaging.Codec;
|
||
using System.Data;
|
||
using IRaCIS.Core.Domain.Models;
|
||
using FellowOakDicom.Network;
|
||
using IRaCIS.Core.SCP.Service;
|
||
using IRaCIS.Core.Infra.EFCore;
|
||
using MassTransit;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using IRaCIS.Core.Infrastructure.Extention;
|
||
using Microsoft.EntityFrameworkCore;
|
||
|
||
namespace IRaCIS.Core.SCP.Service
|
||
{
|
||
public interface IPatientStudyService
|
||
{
|
||
Task<IResponseOutput> AutoBindingPatientStudyVisitAsync(List<Guid> scpStudyIdList);
|
||
}
|
||
|
||
[ApiExplorerSettings(GroupName = "Trial")]
|
||
public class PatientStudyService : BaseService, IPatientStudyService
|
||
{
|
||
private readonly IRepository<SCPStudySubjectVisit> _studySubjectVisitRepository;
|
||
private readonly IRepository<SubjectPatient> _subjectPatientRepository;
|
||
private readonly IRepository<Trial> _trialRepository;
|
||
private readonly IRepository<SCPPatient> _patientRepository;
|
||
private readonly IRepository<SCPStudy> _studyRepository;
|
||
private readonly IRepository<Subject> _subjectRepository;
|
||
private readonly IRepository<SubjectVisit> _subjectVisitRepository;
|
||
private readonly IDistributedLockProvider _distributedLockProvider;
|
||
|
||
public PatientStudyService(IRepository<SCPStudySubjectVisit> studySubjectVisitRepository, IRepository<SCPStudy> studyRepository, IRepository<SubjectPatient> subjectPatientRepository, IRepository<Trial> trialRepository, IRepository<SCPPatient> patientRepository, IRepository<Subject> subjectRepository, IRepository<SubjectVisit> subjectVisitRepository, IDistributedLockProvider distributedLockProvider)
|
||
{
|
||
_studySubjectVisitRepository = studySubjectVisitRepository;
|
||
_studyRepository = studyRepository;
|
||
_subjectPatientRepository = subjectPatientRepository;
|
||
_trialRepository = trialRepository;
|
||
_patientRepository = patientRepository;
|
||
_subjectRepository = subjectRepository;
|
||
_subjectVisitRepository = subjectVisitRepository;
|
||
_distributedLockProvider = distributedLockProvider;
|
||
}
|
||
|
||
|
||
|
||
public class AuToBindingStudyInfo
|
||
{
|
||
public Guid SCPStudyId { get; set; }
|
||
public DateTime? StudyTime { get; set; }
|
||
}
|
||
|
||
private async Task DealAutoBindingStudyAsync(Guid trialId, Guid subjectId, List<AuToBindingStudyInfo> studyList, decimal? startBindVisitNum = null)
|
||
{
|
||
|
||
//自动创建访视 和检查绑定
|
||
|
||
//1. 查询已存在的访视
|
||
var subjectAllVisitList = await _subjectVisitRepository.Where(t => t.SubjectId == subjectId)
|
||
.Select(t => new
|
||
{
|
||
t.SubjectId,
|
||
SubjectVisitId = t.Id,
|
||
t.SubmitState,
|
||
VisitNum = t.VisitNum,
|
||
MaxStudyTime = t.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime),
|
||
MinStudyTime = t.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime)
|
||
})
|
||
.ToListAsync();
|
||
|
||
|
||
//2、获取项目配置
|
||
var trialconfig = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.BlindBaseLineName, t.BlindFollowUpPrefix }).FirstOrDefault();
|
||
|
||
//3、 未提交的最小的访视号 从这个访视开始绑定
|
||
|
||
var subjectMaxVisitNum = startBindVisitNum == null ? subjectAllVisitList.Where(t => t.SubmitState != SubmitStateEnum.Submitted).MinOrDefault(t => t.VisitNum) : startBindVisitNum.Value;
|
||
|
||
|
||
List<(int VisitCount, Guid SCPStudyId)> visits = new List<(int, Guid)>();
|
||
|
||
int visitCount = 0;
|
||
|
||
DateTime? lastVisitTime = null;
|
||
|
||
foreach (var study in studyList)
|
||
{
|
||
if (lastVisitTime == null || (study.StudyTime - lastVisitTime.Value).Value.TotalDays >= 15)
|
||
{
|
||
// 当前时间点与上一个访视时间点间隔大于等于 15 天,需要建立一个新的访视
|
||
visitCount++;
|
||
|
||
visits.Add((visitCount, study.SCPStudyId));
|
||
}
|
||
else
|
||
{
|
||
visits.Add((visitCount, study.SCPStudyId));
|
||
}
|
||
|
||
lastVisitTime = study.StudyTime;
|
||
}
|
||
|
||
//4、生成访视 并且绑定
|
||
|
||
for (int i = 0; i < visitCount; i++)
|
||
{
|
||
|
||
var bindSubjectVisitId = Guid.Empty;
|
||
|
||
var bindVisitNum = i + subjectMaxVisitNum;
|
||
|
||
var existSubjectVisit = subjectAllVisitList.FirstOrDefault(t => t.SubjectId == subjectId && t.VisitNum == bindVisitNum);
|
||
|
||
if (existSubjectVisit == null)
|
||
{
|
||
bindSubjectVisitId = NewId.NextGuid();
|
||
|
||
//基线
|
||
if (bindVisitNum == 0)
|
||
{
|
||
|
||
await _subjectVisitRepository.AddAsync(new SubjectVisit() { TrialId = trialId, SubjectId = subjectId, VisitName = trialconfig.BlindBaseLineName, VisitNum = bindVisitNum, Id = bindSubjectVisitId, SubmitState = SubmitStateEnum.ToSubmit, IsBaseLine = true });
|
||
|
||
}
|
||
else
|
||
{
|
||
await _subjectVisitRepository.AddAsync(new SubjectVisit() { TrialId = trialId, SubjectId = subjectId, VisitName = trialconfig.BlindFollowUpPrefix + $" {(int)bindVisitNum}", VisitNum = bindVisitNum, Id = bindSubjectVisitId, SubmitState = SubmitStateEnum.ToSubmit });
|
||
}
|
||
}
|
||
|
||
else
|
||
{
|
||
bindSubjectVisitId = existSubjectVisit.SubjectVisitId;
|
||
}
|
||
|
||
|
||
|
||
var currentVisitStudyList = visits.Where(t => t.VisitCount == (i + 1)).ToList();
|
||
|
||
foreach (var item in currentVisitStudyList)
|
||
{
|
||
//访视状态为未提交才绑定
|
||
if (!subjectAllVisitList.Any(t => t.SubjectId == subjectId && t.SubjectVisitId == bindSubjectVisitId && t.SubmitState == SubmitStateEnum.Submitted))
|
||
{
|
||
var find = await _subjectVisitRepository.FindAsync(bindSubjectVisitId);
|
||
find.SubmitState = SubmitStateEnum.ToSubmit;
|
||
await _studySubjectVisitRepository.AddAsync(new SCPStudySubjectVisit() { TrialId = trialId, SubjectVisitId = bindSubjectVisitId, SCPStudyId = item.SCPStudyId, SubjectId = subjectId });
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
await _subjectPatientRepository.SaveChangesAsync();
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 传输完成后,自动给检查绑定访视
|
||
/// </summary>
|
||
/// <param name="inCommand"></param>
|
||
/// <returns></returns>
|
||
[HttpPost]
|
||
public async Task<IResponseOutput> AutoBindingPatientStudyVisitAsync(List<Guid> scpStudyIdList)
|
||
{
|
||
//一个检查 可能绑定到不同的项目的不同subject 有的该检查已绑定访视,有的该检查绑定了访视
|
||
|
||
var query = from scpStudy in _studyRepository.Where(t => scpStudyIdList.Contains(t.Id))
|
||
join subjectPatient in _subjectPatientRepository.AsQueryable()
|
||
on scpStudy.PatientId equals subjectPatient.PatientId
|
||
select new
|
||
{
|
||
subjectPatient.Subject.Status,
|
||
subjectPatient.Subject.TrialId,
|
||
subjectPatient.SubjectId,
|
||
subjectPatient.PatientId,
|
||
SCPStudyId = scpStudy.Id,
|
||
scpStudy.StudyTime
|
||
};
|
||
|
||
var list = query.ToList();
|
||
|
||
if (list.Count > 0)
|
||
{
|
||
|
||
|
||
var subjectIdList = list.Select(t => t.SubjectId).ToList();
|
||
|
||
var allSubjectVisitList = await _subjectVisitRepository.Where(t => subjectIdList.Contains(t.SubjectId))
|
||
.Select(t => new { t.SubjectId, SubjectVisitId = t.Id, t.SubmitState, VisitNum = t.VisitNum, MaxStudyTime = t.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime), MinStudyTime = t.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime) })
|
||
.ToListAsync();
|
||
|
||
foreach (var g in list.GroupBy(t => new { t.SubjectId, t.TrialId, t.Status }))
|
||
{
|
||
var subjectId = g.Key.SubjectId;
|
||
var trialId = g.Key.TrialId;
|
||
|
||
//访视结束,那么就不处理
|
||
if (g.Key.Status == SubjectStatus.EndOfVisit)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
|
||
// 预先处理1: 数据库可能有已存在的subject 患者绑定,在这里要一起考虑绑定
|
||
|
||
var dbPatientIdList = _subjectPatientRepository.Where(t => t.SubjectId == subjectId).Select(t => t.PatientId).ToList();
|
||
|
||
// 预先处理2: 删除未提交的所有绑定的检查记录,所有检查一起考虑绑定
|
||
|
||
await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted);
|
||
|
||
//预处理3 找到该subjecct 已提交的访视的最大检查时间,绑定的检查时间要比这个时间要大
|
||
|
||
var maxStudyTime = allSubjectVisitList.Where(t => t.SubjectId == subjectId && t.SubmitState == SubmitStateEnum.Submitted).MaxOrDefault(t => t.MaxStudyTime);
|
||
|
||
|
||
// 预处理4: 处理需要绑定的检查
|
||
//获取 该受试者绑定患者已存在的检查,考虑要生成多少个访视,去除已提交的检查
|
||
|
||
var studyList = await _studyRepository.Where(t => dbPatientIdList.Contains(t.PatientId)
|
||
&& !t.SCPStudySubjectVisitList.Any(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted))
|
||
.WhereIf(maxStudyTime != null, t => t.StudyTime > maxStudyTime)
|
||
.Select(t => new AuToBindingStudyInfo { SCPStudyId = t.Id, StudyTime = t.StudyTime }).OrderBy(t => t.StudyTime).ToListAsync();
|
||
|
||
await DealAutoBindingStudyAsync(trialId, subjectId, studyList);
|
||
|
||
|
||
}
|
||
|
||
await _subjectVisitRepository.SaveChangesAsync();
|
||
}
|
||
|
||
//将检查设置为传输结束
|
||
await _studyRepository.BatchUpdateNoTrackingAsync(t => scpStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||
|
||
return ResponseOutput.Ok();
|
||
|
||
}
|
||
|
||
}
|
||
}
|