迁移HIR 初步提交
parent
c158d53a39
commit
75bf9b394b
|
@ -24,7 +24,39 @@ using IRaCIS.Core.Infrastructure;
|
||||||
|
|
||||||
namespace IRaCIS.Core.SCP.Service
|
namespace IRaCIS.Core.SCP.Service
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 后台托管服务的方式运行
|
||||||
|
/// </summary>
|
||||||
|
//public class CStoreSCPHostedService : IHostedService
|
||||||
|
//{
|
||||||
|
// private readonly ILogger<CStoreSCPHostedService> _logger;
|
||||||
|
// private readonly IDicomServerFactory _dicomServerFactory;
|
||||||
|
// private IDicomServer? _server;
|
||||||
|
|
||||||
|
// public CStoreSCPHostedService(ILogger<CStoreSCPHostedService> logger, IDicomServerFactory dicomServerFactory)
|
||||||
|
// {
|
||||||
|
// _logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
// _dicomServerFactory = dicomServerFactory ?? throw new ArgumentNullException(nameof(dicomServerFactory));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
// {
|
||||||
|
// _logger.LogInformation("Starting DICOM server");
|
||||||
|
// _server = _dicomServerFactory.Create<CStoreSCPService>(104);
|
||||||
|
// _logger.LogInformation("DICOM server is running");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
// {
|
||||||
|
// if (_server != null)
|
||||||
|
// {
|
||||||
|
// _server.Stop();
|
||||||
|
// _server.Dispose();
|
||||||
|
// _server = null;
|
||||||
|
// }
|
||||||
|
// return Task.CompletedTask;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
public class DicomSCPServiceOption
|
public class DicomSCPServiceOption
|
||||||
{
|
{
|
||||||
public List<string> CalledAEList { get; set; }
|
public List<string> CalledAEList { get; set; }
|
||||||
|
@ -43,11 +75,6 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
|
|
||||||
private SCPImageUpload _upload { get; set; }
|
private SCPImageUpload _upload { get; set; }
|
||||||
|
|
||||||
private Guid _trialId { get; set; }
|
|
||||||
|
|
||||||
private Guid _trialSiteId { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
|
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
|
||||||
{
|
{
|
||||||
|
@ -59,20 +86,20 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[]
|
private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[]
|
||||||
{
|
{
|
||||||
// Lossless
|
// Lossless
|
||||||
DicomTransferSyntax.JPEGLSLossless, //1.2.840.10008.1.2.4.80
|
DicomTransferSyntax.JPEGLSLossless,
|
||||||
DicomTransferSyntax.JPEG2000Lossless, //1.2.840.10008.1.2.4.90
|
DicomTransferSyntax.JPEG2000Lossless,
|
||||||
DicomTransferSyntax.JPEGProcess14SV1, //1.2.840.10008.1.2.4.70
|
DicomTransferSyntax.JPEGProcess14SV1,
|
||||||
DicomTransferSyntax.JPEGProcess14, //1.2.840.10008.1.2.4.57 JPEG Lossless, Non-Hierarchical (Process 14)
|
DicomTransferSyntax.JPEGProcess14,
|
||||||
DicomTransferSyntax.RLELossless, //1.2.840.10008.1.2.5
|
DicomTransferSyntax.RLELossless,
|
||||||
// Lossy
|
// Lossy
|
||||||
DicomTransferSyntax.JPEGLSNearLossless,//1.2.840.10008.1.2.4.81"
|
DicomTransferSyntax.JPEGLSNearLossless,
|
||||||
DicomTransferSyntax.JPEG2000Lossy, //1.2.840.10008.1.2.4.91
|
DicomTransferSyntax.JPEG2000Lossy,
|
||||||
DicomTransferSyntax.JPEGProcess1, //1.2.840.10008.1.2.4.50
|
DicomTransferSyntax.JPEGProcess1,
|
||||||
DicomTransferSyntax.JPEGProcess2_4, //1.2.840.10008.1.2.4.51
|
DicomTransferSyntax.JPEGProcess2_4,
|
||||||
// Uncompressed
|
// Uncompressed
|
||||||
DicomTransferSyntax.ExplicitVRLittleEndian, //1.2.840.10008.1.2.1
|
DicomTransferSyntax.ExplicitVRLittleEndian,
|
||||||
DicomTransferSyntax.ExplicitVRBigEndian, //1.2.840.10008.1.2.2
|
DicomTransferSyntax.ExplicitVRBigEndian,
|
||||||
DicomTransferSyntax.ImplicitVRLittleEndian //1.2.840.10008.1.2
|
DicomTransferSyntax.ImplicitVRLittleEndian
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,48 +121,18 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
|
|
||||||
_serviceProvider = (IServiceProvider)this.UserState;
|
_serviceProvider = (IServiceProvider)this.UserState;
|
||||||
|
|
||||||
|
var option = _serviceProvider.GetService<IOptionsMonitor<DicomSCPServiceOption>>().CurrentValue;
|
||||||
|
|
||||||
|
|
||||||
var _trialDicomAERepository = _serviceProvider.GetService<IRepository<TrialDicomAE>>();
|
|
||||||
|
|
||||||
|
var calledAEList = option.CalledAEList;
|
||||||
|
|
||||||
var trialDicomAEList = _trialDicomAERepository.Select(t => new { t.CalledAE, t.TrialId }).ToList();
|
if (!calledAEList.Contains(association.CalledAE))
|
||||||
var trialCalledAEList = trialDicomAEList.Select(t => t.CalledAE).ToList();
|
|
||||||
|
|
||||||
Log.Logger.Information("当前系统配置:", string.Join('|', trialDicomAEList));
|
//if (association.CalledAE != "STORESCP")
|
||||||
|
|
||||||
var findCalledAE = trialDicomAEList.Where(t => t.CalledAE == association.CalledAE).FirstOrDefault();
|
|
||||||
|
|
||||||
var isCanReceiveIamge = false;
|
|
||||||
|
|
||||||
if (findCalledAE != null)
|
|
||||||
{
|
|
||||||
_trialId = findCalledAE.TrialId;
|
|
||||||
|
|
||||||
var _trialSiteDicomAERepository = _serviceProvider.GetService<IRepository<TrialSiteDicomAE>>();
|
|
||||||
|
|
||||||
|
|
||||||
var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId==_trialId).FirstOrDefault();
|
|
||||||
|
|
||||||
if (findTrialSiteAE != null)
|
|
||||||
{
|
|
||||||
_trialSiteId = findTrialSiteAE.TrialSiteId;
|
|
||||||
|
|
||||||
isCanReceiveIamge = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (association.CallingAE == "test-callingAE")
|
|
||||||
{
|
|
||||||
isCanReceiveIamge = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!trialCalledAEList.Contains(association.CalledAE) || isCanReceiveIamge == false)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
Log.Logger.Warning($"拒绝CallingAE:{association.CallingAE} CalledAE:{association.CalledAE}的连接");
|
Log.Logger.Warning($"拒绝CalledAE:{association.CalledAE}的连接");
|
||||||
|
|
||||||
return SendAssociationRejectAsync(
|
return SendAssociationRejectAsync(
|
||||||
DicomRejectResult.Permanent,
|
DicomRejectResult.Permanent,
|
||||||
|
@ -155,8 +152,6 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return SendAssociationAcceptAsync(association);
|
return SendAssociationAcceptAsync(association);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,25 +167,19 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
_upload.EndTime = DateTime.Now;
|
_upload.EndTime = DateTime.Now;
|
||||||
_upload.StudyCount = _SCPStudyIdList.Count;
|
_upload.StudyCount = _SCPStudyIdList.Count;
|
||||||
|
|
||||||
|
|
||||||
await _SCPImageUploadRepository.AddAsync(_upload,true);
|
await _SCPImageUploadRepository.AddAsync(_upload,true);
|
||||||
|
|
||||||
|
|
||||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
|
||||||
//将检查设置为传输结束
|
|
||||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
|
||||||
|
|
||||||
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
|
||||||
|
|
||||||
await SendAssociationReleaseResponseAsync();
|
await SendAssociationReleaseResponseAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task DataMaintenanceAsaync()
|
private async Task DataMaintenanceAsaync()
|
||||||
{
|
{
|
||||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality");
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality 以及自动创建访视,绑定检查");
|
||||||
|
|
||||||
|
var patientStudyService = _serviceProvider.GetService<IPatientStudyService>();
|
||||||
|
|
||||||
|
await patientStudyService.AutoBindingPatientStudyVisitAsync(_SCPStudyIdList);
|
||||||
|
|
||||||
//处理检查Modality
|
//处理检查Modality
|
||||||
var _dictionaryRepository = _serviceProvider.GetService<IRepository<Dictionary>>();
|
var _dictionaryRepository = _serviceProvider.GetService<IRepository<Dictionary>>();
|
||||||
|
@ -242,11 +231,11 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
//奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致
|
//奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致
|
||||||
if (exception == null)
|
if (exception == null)
|
||||||
{
|
{
|
||||||
//var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||||
////将检查设置为传输结束
|
//将检查设置为传输结束
|
||||||
//await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||||
|
|
||||||
//await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
|
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
|
||||||
|
@ -262,9 +251,8 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
string seriesInstanceUid = request.Dataset.GetString(DicomTag.SeriesInstanceUID);
|
string seriesInstanceUid = request.Dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||||
string sopInstanceUid = request.Dataset.GetString(DicomTag.SOPInstanceUID);
|
string sopInstanceUid = request.Dataset.GetString(DicomTag.SOPInstanceUID);
|
||||||
|
|
||||||
//Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
|
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid);
|
||||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, _trialId.ToString());
|
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid);
|
||||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, _trialId.ToString());
|
|
||||||
|
|
||||||
|
|
||||||
var ossService = _serviceProvider.GetService<IOSSService>();
|
var ossService = _serviceProvider.GetService<IOSSService>();
|
||||||
|
@ -274,7 +262,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
||||||
|
|
||||||
var storeRelativePath = string.Empty;
|
var storeRelativePath = string.Empty;
|
||||||
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}{studyInstanceUid}";
|
var ossFolderPath = $"Dicom/{studyInstanceUid}";
|
||||||
|
|
||||||
|
|
||||||
long fileSize = 0;
|
long fileSize = 0;
|
||||||
|
@ -307,7 +295,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
|
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
|
||||||
|
|
||||||
if (!_SCPStudyIdList.Contains(scpStudyId))
|
if (!_SCPStudyIdList.Contains(scpStudyId))
|
||||||
{
|
{
|
||||||
|
@ -336,10 +324,14 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
|
|
||||||
series.ImageResizePath = seriesPath;
|
series.ImageResizePath = seriesPath;
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
//await _seriesRepository.BatchUpdateNoTrackingAsync(t => t.Id == seriesId, u => new SCPSeries() { ImageResizePath = seriesPath });
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await _seriesRepository.SaveChangesAsync();
|
await _seriesRepository.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
@ -11,7 +11,6 @@ using FellowOakDicom.Network;
|
||||||
using IRaCIS.Core.SCP.Service;
|
using IRaCIS.Core.SCP.Service;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
using MassTransit;
|
using MassTransit;
|
||||||
using System.Runtime.Intrinsics.X86;
|
|
||||||
using Serilog.Sinks.File;
|
using Serilog.Sinks.File;
|
||||||
|
|
||||||
namespace IRaCIS.Core.SCP.Service
|
namespace IRaCIS.Core.SCP.Service
|
||||||
|
@ -52,7 +51,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
/// <param name="dataset"></param>
|
/// <param name="dataset"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="NotImplementedException"></exception>
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
public async Task<Guid> ArchiveDicomFileAsync(DicomDataset dataset, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
|
public async Task<Guid> ArchiveDicomFileAsync(DicomDataset dataset, string fileRelativePath, string callingAE, string calledAE, long fileSize)
|
||||||
{
|
{
|
||||||
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
|
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
|
||||||
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||||
|
@ -61,9 +60,9 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
|
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
|
||||||
|
|
||||||
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
|
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
|
||||||
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString());
|
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid);
|
||||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString());
|
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid);
|
||||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString());
|
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid);
|
||||||
|
|
||||||
var isStudyNeedAdd = false;
|
var isStudyNeedAdd = false;
|
||||||
var isSeriesNeedAdd = false;
|
var isSeriesNeedAdd = false;
|
||||||
|
@ -74,11 +73,12 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
|
|
||||||
//using (@lock.Acquire())
|
//using (@lock.Acquire())
|
||||||
{
|
{
|
||||||
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr /*&& t.TrialSiteId==trialSiteId*/ );
|
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr);
|
||||||
var findStudy = await _studyRepository.FirstOrDefaultAsync(t => t.Id == studyId);
|
var findStudy = await _studyRepository.FirstOrDefaultAsync(t => t.Id == studyId);
|
||||||
var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||||
var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == instanceId);
|
var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == instanceId);
|
||||||
|
|
||||||
|
|
||||||
DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay);
|
DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay);
|
||||||
|
|
||||||
//先传输了修改了患者编号的,又传输了没有修改患者编号的,导致后传输的没有修改患者编号的下面的检查为0
|
//先传输了修改了患者编号的,又传输了没有修改患者编号的,导致后传输的没有修改患者编号的下面的检查为0
|
||||||
|
@ -90,8 +90,6 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
findPatient = new SCPPatient()
|
findPatient = new SCPPatient()
|
||||||
{
|
{
|
||||||
Id = NewId.NextSequentialGuid(),
|
Id = NewId.NextSequentialGuid(),
|
||||||
//TrialId=trialId,
|
|
||||||
//TrialSiteId=trialSiteId,
|
|
||||||
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
||||||
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
||||||
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
||||||
|
@ -117,20 +115,6 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
{
|
{
|
||||||
findPatient.PatientBirthDate = birthDateStr;
|
findPatient.PatientBirthDate = birthDateStr;
|
||||||
|
|
||||||
DateTime birthDate;
|
|
||||||
|
|
||||||
if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate,out birthDate))
|
|
||||||
{
|
|
||||||
var patientAge = studyTime.Value.Year - birthDate.Year;
|
|
||||||
// 如果生日还未到,年龄减去一岁
|
|
||||||
if (studyTime.Value < birthDate.AddYears(patientAge))
|
|
||||||
{
|
|
||||||
patientAge--;
|
|
||||||
}
|
|
||||||
|
|
||||||
findPatient.PatientAge = patientAge.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -163,8 +147,6 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
|
|
||||||
PatientId = findPatient.Id,
|
PatientId = findPatient.Id,
|
||||||
Id = studyId,
|
Id = studyId,
|
||||||
//TrialId = trialId,
|
|
||||||
//TrialSiteId = trialSiteId,
|
|
||||||
StudyInstanceUid = studyInstanceUid,
|
StudyInstanceUid = studyInstanceUid,
|
||||||
StudyTime = studyTime,
|
StudyTime = studyTime,
|
||||||
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
||||||
|
@ -274,7 +256,6 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
Path = fileRelativePath,
|
Path = fileRelativePath,
|
||||||
|
|
||||||
FileSize = fileSize,
|
FileSize = fileSize,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
++findStudy.InstanceCount;
|
++findStudy.InstanceCount;
|
||||||
|
@ -304,7 +285,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath,FileSize=fileSize });
|
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath });
|
||||||
}
|
}
|
||||||
|
|
||||||
await _studyRepository.SaveChangesAsync();
|
await _studyRepository.SaveChangesAsync();
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
{
|
{
|
||||||
public interface IDicomArchiveService
|
public interface IDicomArchiveService
|
||||||
{
|
{
|
||||||
Task<Guid> ArchiveDicomFileAsync(DicomDataset dicomDataset,Guid trialId,Guid trialSiteId, string fileRelativePath,string callingAE,string calledAE,long fileSize);
|
Task<Guid> ArchiveDicomFileAsync(DicomDataset dicomDataset,string fileRelativePath,string callingAE,string calledAE,long fileSize);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,249 @@
|
||||||
|
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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ObjectStoreService": {
|
||||||
|
"ObjectStoreUse": "MinIO",
|
||||||
|
"AliyunOSS": {
|
||||||
|
"regionId": "cn-shanghai",
|
||||||
|
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||||
|
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||||
|
"bucketName": "zy-sir-test-store",
|
||||||
|
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||||
|
"viewEndpoint": "https://zy-sir-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"region": "oss-cn-shanghai"
|
||||||
|
},
|
||||||
|
|
||||||
|
"MinIO": {
|
||||||
|
"endPoint": "106.14.89.110",
|
||||||
|
"port": "9001",
|
||||||
|
"useSSL": false,
|
||||||
|
"accessKey": "fbStsVYCIPKHQneeqMwD",
|
||||||
|
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||||
|
"bucketName": "hir-test",
|
||||||
|
"viewEndpoint": "http://106.14.89.110:9001/hir-test/"
|
||||||
|
},
|
||||||
|
|
||||||
|
"AWS": {
|
||||||
|
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||||
|
"useSSL": false,
|
||||||
|
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
|
||||||
|
"secretKey": "l+yjtvV7Z4jiwm/7xCYv30UeUj/SvuqqYzAwjJHf",
|
||||||
|
"bucketName": "ei-irc-test-store",
|
||||||
|
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_HIR_New;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=106.14.89.110,1435;Database=Test_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
},
|
||||||
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
|
"OpenUserComplexPassword": false,
|
||||||
|
|
||||||
|
"OpenSignDocumentBeforeWork": false,
|
||||||
|
|
||||||
|
"OpenTrialRelationDelete": true,
|
||||||
|
|
||||||
|
"OpenLoginLimit": false,
|
||||||
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
|
"LoginFailLockMinutes": 30
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemEmailSendConfig": {
|
||||||
|
"Port": 465,
|
||||||
|
"Host": "smtp.qiye.aliyun.com",
|
||||||
|
"FromEmail": "test-study@extimaging.com",
|
||||||
|
"FromName": "Test_Study",
|
||||||
|
"AuthorizationCode": "zhanying123",
|
||||||
|
|
||||||
|
"SiteUrl": "http://study.test.extimaging.com/login"
|
||||||
|
},
|
||||||
|
"DicomSCPServiceConfig": {
|
||||||
|
"CalledAEList": [
|
||||||
|
"STORESCP",
|
||||||
|
"Value1",
|
||||||
|
"Value2",
|
||||||
|
"Value3"
|
||||||
|
],
|
||||||
|
"ServerPort": 11112
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,52 +24,15 @@
|
||||||
},
|
},
|
||||||
"applicationUrl": "http://localhost:6100"
|
"applicationUrl": "http://localhost:6100"
|
||||||
},
|
},
|
||||||
"IRaCIS.Test_IRC_PGSQL": {
|
"IRaCIS.HIR_IRC": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC_PGSQL"
|
"ASPNETCORE_ENVIRONMENT": "Test_HIR"
|
||||||
},
|
|
||||||
"applicationUrl": "http://localhost:6100"
|
|
||||||
},
|
|
||||||
"IRaCIS.Event_IRC": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Event_IRC"
|
|
||||||
},
|
|
||||||
"applicationUrl": "http://localhost:6100"
|
|
||||||
},
|
|
||||||
"Docker": {
|
|
||||||
"commandName": "Docker",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
|
|
||||||
"publishAllPorts": true
|
|
||||||
},
|
|
||||||
"IRaCIS.Uat_IRC": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Uat_IRC"
|
|
||||||
},
|
|
||||||
"applicationUrl": "http://localhost:6100"
|
|
||||||
},
|
|
||||||
"IRaCIS.Prod_IRC": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Prod_IRC"
|
|
||||||
},
|
|
||||||
"applicationUrl": "http://localhost:6100"
|
|
||||||
},
|
|
||||||
"IRaCIS.US_Uat_IRC": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "US_Uat_IRC"
|
|
||||||
},
|
},
|
||||||
"applicationUrl": "http://localhost:6100"
|
"applicationUrl": "http://localhost:6100"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using IP2Region.Net.Abstractions;
|
using IP2Region.Net.Abstractions;
|
||||||
using IP2Region.Net.XDB;
|
using IP2Region.Net.XDB;
|
||||||
|
using IRaCIS.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.BackGroundJob;
|
using IRaCIS.Core.Application.BackGroundJob;
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Application.Service;
|
using IRaCIS.Core.Application.Service;
|
||||||
|
@ -30,6 +31,7 @@ public static class ServiceCollectionSetup
|
||||||
services.AddOptions().Configure<IRCEncreptOption>(_configuration.GetSection("EncrypteResponseConfig"));
|
services.AddOptions().Configure<IRCEncreptOption>(_configuration.GetSection("EncrypteResponseConfig"));
|
||||||
services.AddOptions().Configure<SystemPacsConfig>(_configuration.GetSection("SystemPacsConfig"));
|
services.AddOptions().Configure<SystemPacsConfig>(_configuration.GetSection("SystemPacsConfig"));
|
||||||
services.Configure<IRaCISBasicConfigOption>(_configuration.GetSection("IRaCISBasicConfig"));
|
services.Configure<IRaCISBasicConfigOption>(_configuration.GetSection("IRaCISBasicConfig"));
|
||||||
|
services.AddOptions().Configure<SystemHospitalOption>(_configuration.GetSection("SystemHospitalConfig"));
|
||||||
|
|
||||||
services.Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
|
services.Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ObjectStoreService": {
|
||||||
|
"ObjectStoreUse": "MinIO",
|
||||||
|
"AliyunOSS": {
|
||||||
|
"regionId": "cn-shanghai",
|
||||||
|
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||||
|
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||||
|
"bucketName": "zy-sir-test-store",
|
||||||
|
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||||
|
"viewEndpoint": "https://zy-sir-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"region": "oss-cn-shanghai"
|
||||||
|
},
|
||||||
|
"MinIO": {
|
||||||
|
"endPoint": "hir-oss.test.extimaging.com",
|
||||||
|
"port": "443",
|
||||||
|
"useSSL": true,
|
||||||
|
//"endPoint": "106.14.89.110",
|
||||||
|
//"port": "9001",
|
||||||
|
//"useSSL": false,
|
||||||
|
"accessKey": "fbStsVYCIPKHQneeqMwD",
|
||||||
|
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||||
|
"bucketName": "hir-test",
|
||||||
|
//"viewEndpoint": "https://hir.test.extimaging.com/oss/hir-test"
|
||||||
|
"viewEndpoint": "https://hir-oss.test.extimaging.com/hir-test"
|
||||||
|
},
|
||||||
|
"AWS": {
|
||||||
|
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||||
|
"useSSL": false,
|
||||||
|
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
|
||||||
|
"secretKey": "l+yjtvV7Z4jiwm/7xCYv30UeUj/SvuqqYzAwjJHf",
|
||||||
|
"bucketName": "ei-irc-test-store",
|
||||||
|
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_HIR_New;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=106.14.89.110,1435;Database=Test_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
},
|
||||||
|
"BasicSystemConfig": {
|
||||||
|
"OpenUserComplexPassword": false,
|
||||||
|
"OpenSignDocumentBeforeWork": false,
|
||||||
|
"OpenTrialRelationDelete": true,
|
||||||
|
"OpenLoginLimit": false,
|
||||||
|
"LoginMaxFailCount": 5,
|
||||||
|
"LoginFailLockMinutes": 30,
|
||||||
|
"AESKey": "HIR_System_AES_Key_Info"
|
||||||
|
},
|
||||||
|
"SystemHospitalConfig": {
|
||||||
|
"HospitalCode": "EI",
|
||||||
|
"HospitalLogoPath": "/System/GeneralDocuments/1716453306898_图片2.png",
|
||||||
|
"TrialKeepCount": 60,
|
||||||
|
"HospitalName": "上海展影医疗科技有限公司",
|
||||||
|
"HospitalAliasName": "展影医疗",
|
||||||
|
"Country": "中国",
|
||||||
|
"City": "上海",
|
||||||
|
"Province": "上海",
|
||||||
|
"Address": "上海市杨浦区国泰路复旦科技园",
|
||||||
|
"Phone": "021-60702575",
|
||||||
|
"IsCanConnectInternet": false
|
||||||
|
},
|
||||||
|
"SystemEmailSendConfig": {
|
||||||
|
"Port": 465,
|
||||||
|
"Host": "smtp.qiye.aliyun.com",
|
||||||
|
"FromEmail": "test-study@extimaging.com",
|
||||||
|
"FromName": "Test_HIR",
|
||||||
|
"AuthorizationCode": "zhanying123",
|
||||||
|
"SiteUrl": "http://hir.test.extimaging.com/login"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
using IRaCIS.Core.Application.BusinessFilter;
|
using IRaCIS.Application.Contracts;
|
||||||
|
using IRaCIS.Core.Application.BusinessFilter;
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -13,7 +16,9 @@ using static IRaCIS.Core.Domain.Share.StaticData;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Filter;
|
namespace IRaCIS.Core.Application.Filter;
|
||||||
|
|
||||||
public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository<Trial> _trialRepository) : IAsyncActionFilter
|
public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository<Trial> _trialRepository,
|
||||||
|
IOptionsMonitor<ServiceVerifyConfigOption> _basicSystemConfigConfig,
|
||||||
|
IOptionsMonitor<SystemHospitalOption> _hospitalOption, IStringLocalizer _localizer) : IAsyncActionFilter
|
||||||
{
|
{
|
||||||
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||||
{
|
{
|
||||||
|
@ -113,9 +118,9 @@ public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _
|
||||||
|
|
||||||
trialIdStr = matchResult.Value;
|
trialIdStr = matchResult.Value;
|
||||||
|
|
||||||
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
var trialInfo = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(trialStatusStr))
|
if (string.IsNullOrWhiteSpace(trialInfo?.TrialStatusStr))
|
||||||
{
|
{
|
||||||
|
|
||||||
//数据库 检查该项目Id不对
|
//数据库 检查该项目Id不对
|
||||||
|
@ -145,7 +150,61 @@ public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _
|
||||||
//通过path 或者body 找到trialId 了
|
//通过path 或者body 找到trialId 了
|
||||||
if (!string.IsNullOrWhiteSpace(trialIdStr))
|
if (!string.IsNullOrWhiteSpace(trialIdStr))
|
||||||
{
|
{
|
||||||
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
var trialInfo = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||||
|
var trialStatusStr = string.Empty;
|
||||||
|
|
||||||
|
if (trialInfo != null)
|
||||||
|
{
|
||||||
|
trialStatusStr = trialInfo.TrialStatusStr;
|
||||||
|
|
||||||
|
var activationCode = trialInfo.AuthorizationEncrypt;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(activationCode))
|
||||||
|
{
|
||||||
|
//context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["项目未进行授权之前,不能进行操作"]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
var decodedText = string.Empty;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//解析加密信息
|
||||||
|
decodedText = Cryptography.DecryptString(activationCode, _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt");
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["该授权码与该项目不匹配"]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var hospitalCode = _hospitalOption.CurrentValue.HospitalCode;
|
||||||
|
var authInfo = JsonConvert.DeserializeObject<TrialAuthorizationInfo>(decodedText);
|
||||||
|
|
||||||
|
if (authInfo != null)
|
||||||
|
{
|
||||||
|
if (authInfo.TrialCode != trialInfo.TrialCode || authInfo.CreateUserId != trialInfo.CreateUserId || authInfo.TrialId != trialInfo.TrialId
|
||||||
|
|| authInfo.HospitalCode != hospitalCode || trialInfo.CriterionTypeList.Except(authInfo.CriterionTypeList).Count() != 0)
|
||||||
|
{
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["您的操作被禁止,系统检测到该项目授权码与该项目授权配置信息不一致,请还原项目授权配置信息!"]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DateTime.Now > authInfo.AuthorizationDeadLineDate.Value.AddDays(15))
|
||||||
|
{
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["当前时间已经超过项目授权截止时间半个月,请重新获取项目授权后再进行操作!"]));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["该授权码与该项目不匹配"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
||||||
if (trialStatusStr == StaticData.TrialState.TrialOngoing || optActions.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
if (trialStatusStr == StaticData.TrialState.TrialOngoing || optActions.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
||||||
|
@ -161,6 +220,8 @@ public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_InterceptedProjectStatusRule")));
|
context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_InterceptedProjectStatusRule")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
|
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
|
||||||
|
|
|
@ -1,196 +0,0 @@
|
||||||
using IRaCIS.Core.Application.Helper;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using ZiggyCreatures.Caching.Fusion;
|
|
||||||
using static IRaCIS.Core.Domain.Share.StaticData;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Filter;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 主要为了 处理项目结束 锁库,不允许操作
|
|
||||||
/// </summary>
|
|
||||||
public class TrialResourceFilter : Attribute, IAsyncResourceFilter
|
|
||||||
{
|
|
||||||
private readonly IUserInfo _userInfo;
|
|
||||||
private readonly IFusionCache _fusionCache;
|
|
||||||
public IStringLocalizer _localizer;
|
|
||||||
private readonly IRepository<Trial> _trialRepository;
|
|
||||||
private readonly List<string> _trialOptList = new List<string>();
|
|
||||||
|
|
||||||
|
|
||||||
public TrialResourceFilter(IFusionCache fusionCache, IRepository<Trial> trialRepository, IStringLocalizer localizer, IUserInfo userInfo, string trialOpt = null, string trialOpt2 = null, string trialOpt3 = null)
|
|
||||||
{
|
|
||||||
_fusionCache = fusionCache;
|
|
||||||
_userInfo = userInfo;
|
|
||||||
_localizer = localizer;
|
|
||||||
_trialRepository = trialRepository;
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(trialOpt)) _trialOptList.Add(trialOpt.Trim());
|
|
||||||
if (!string.IsNullOrWhiteSpace(trialOpt2)) _trialOptList.Add(trialOpt2.Trim());
|
|
||||||
if (!string.IsNullOrWhiteSpace(trialOpt3)) _trialOptList.Add(trialOpt3.Trim());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//优先选择异步的方法
|
|
||||||
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
|
|
||||||
{
|
|
||||||
// var typeFilter = context.ActionDescriptor.EndpointMetadata.Where(t => t.GetType() == typeof(TypeFilterAttribute)).Select(t => (TypeFilterAttribute)t).ToList().FirstOrDefault();
|
|
||||||
//var _trialOptList= typeFilter.Arguments.Select(t => t.ToString()).ToList();
|
|
||||||
|
|
||||||
// 获取当前请求的 Host 信息
|
|
||||||
var requestHost = context.HttpContext.Request.Host;
|
|
||||||
|
|
||||||
// 检查请求是否来自 localhost:6100
|
|
||||||
if (requestHost.Host == "localhost" && (requestHost.Port == 6100|| requestHost.Port==3305))
|
|
||||||
{
|
|
||||||
await next.Invoke();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region 处理新的用户类型,不能操作项目相关接口
|
|
||||||
|
|
||||||
// 后期列举出具体的类型,其他任何用户类型,都不允许操作
|
|
||||||
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower() != "TrialDocument/userConfirm".ToLower())
|
|
||||||
{
|
|
||||||
//---对不起,您的账户没有操作权限。
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_NoAccessPermission"]));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//TrialId 传递的途径多种,可能在path 可能在body 可能在数组中,也可能在对象中,可能就在url
|
|
||||||
var trialIdStr = string.Empty;
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(context.HttpContext.Request.Query["trialId"]))
|
|
||||||
{
|
|
||||||
trialIdStr = context.HttpContext.Request.Query["trialId"];
|
|
||||||
}
|
|
||||||
|
|
||||||
//先尝试从path中取TrialId
|
|
||||||
else if (context.RouteData.Values.Keys.Any(t => t.Contains("trialId")))
|
|
||||||
{
|
|
||||||
var index = context.RouteData.Values.Keys.ToList().IndexOf("trialId");
|
|
||||||
trialIdStr = context.RouteData.Values.Values.ToList()[index] as string;
|
|
||||||
}
|
|
||||||
else if (context.HttpContext.Request.Headers["Referer"].ToString().Contains("trialId"))
|
|
||||||
{
|
|
||||||
var headerStr = context.HttpContext.Request.Headers["Referer"].ToString();
|
|
||||||
|
|
||||||
var trialIdIndex = headerStr.IndexOf("trialId");
|
|
||||||
|
|
||||||
|
|
||||||
var matchResult = Regex.Match(headerStr.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
|
||||||
|
|
||||||
if (matchResult.Success)
|
|
||||||
{
|
|
||||||
trialIdStr = matchResult.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#region body 中取数据
|
|
||||||
|
|
||||||
//设置可以多次读
|
|
||||||
context.HttpContext.Request.EnableBuffering();
|
|
||||||
var reader = new StreamReader(context.HttpContext.Request.Body);
|
|
||||||
var contentFromBody = await reader.ReadToEndAsync();
|
|
||||||
//读取后,流的位置还原
|
|
||||||
context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
|
|
||||||
//context.HttpContext.Request.Body.Position = 0;
|
|
||||||
|
|
||||||
//找到参数位置在字符串中的索引
|
|
||||||
var trialIdIndex = contentFromBody.IndexOf("\"TrialId\"", StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
if (trialIdIndex > -1)
|
|
||||||
{
|
|
||||||
// (?<="trialId" *: *").*?(?=",)
|
|
||||||
|
|
||||||
//使用正则 [0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}
|
|
||||||
|
|
||||||
var matchResult = Regex.Match(contentFromBody.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
|
||||||
|
|
||||||
if (matchResult.Success)
|
|
||||||
{
|
|
||||||
//有可能匹配错误 "trialId":"","documentId":"b8180000-3e2c-0016-9fe0-08da33f96236" 从缓存里面验证下
|
|
||||||
|
|
||||||
trialIdStr = matchResult.Value;
|
|
||||||
|
|
||||||
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(trialStatusStr))
|
|
||||||
{
|
|
||||||
|
|
||||||
//数据库 检查该项目Id不对
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//使用字符串取 如果是swagger 可能有时取的不对 因为空格的原因
|
|
||||||
//trialIdStr = contentFromBody.Substring(trialIdIndex + "TrialId".Length + 4, 3
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
//通过path 或者body 找到trialId 了
|
|
||||||
if (!string.IsNullOrWhiteSpace(trialIdStr))
|
|
||||||
{
|
|
||||||
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
|
||||||
|
|
||||||
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
|
||||||
if (trialStatusStr == StaticData.TrialState.TrialOngoing || _trialOptList.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
|
||||||
{
|
|
||||||
|
|
||||||
await next.Invoke();
|
|
||||||
|
|
||||||
}
|
|
||||||
// 项目停止、或者完成 不允许操作
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_InterceptedProjectStatusRule"]));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
|
|
||||||
else if (_trialOptList.Any(t => t == TrialOpt.AddOrUpdateTrial || t == TrialOpt.SignSystemDocNoTrialId))
|
|
||||||
{
|
|
||||||
await next.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//如果项目相关接口没有传递trialId 会来到这里,提醒,以便修改
|
|
||||||
|
|
||||||
//---该接口参数中,没有传递项目编号,请核对。
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_MissingProjectNumber"]));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,9 @@
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Application.Contracts;
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -24,7 +27,9 @@ public class TrialGlobalLimitAttribute : Attribute
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class TrialGlobalLimitEndpointFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository<Trial> _trialRepository) : IEndpointFilter
|
public class TrialGlobalLimitEndpointFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository<Trial> _trialRepository,
|
||||||
|
IOptionsMonitor<ServiceVerifyConfigOption> _basicSystemConfigConfig,
|
||||||
|
IOptionsMonitor<SystemHospitalOption> _hospitalOption, IStringLocalizer _localizer) : IEndpointFilter
|
||||||
|
|
||||||
{
|
{
|
||||||
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
|
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
|
||||||
|
@ -121,9 +126,9 @@ public class TrialGlobalLimitEndpointFilter(IFusionCache _fusionCache, IUserInfo
|
||||||
|
|
||||||
trialIdStr = matchResult.Value;
|
trialIdStr = matchResult.Value;
|
||||||
|
|
||||||
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
var trialInfo = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(trialStatusStr))
|
if (string.IsNullOrWhiteSpace(trialInfo?.TrialStatusStr))
|
||||||
{
|
{
|
||||||
|
|
||||||
//数据库 检查该项目Id不对
|
//数据库 检查该项目Id不对
|
||||||
|
@ -151,7 +156,59 @@ public class TrialGlobalLimitEndpointFilter(IFusionCache _fusionCache, IUserInfo
|
||||||
//通过path 或者body 找到trialId 了
|
//通过path 或者body 找到trialId 了
|
||||||
if (!string.IsNullOrWhiteSpace(trialIdStr))
|
if (!string.IsNullOrWhiteSpace(trialIdStr))
|
||||||
{
|
{
|
||||||
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
var trialInfo = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||||
|
var trialStatusStr = string.Empty;
|
||||||
|
|
||||||
|
|
||||||
|
if (trialInfo != null)
|
||||||
|
{
|
||||||
|
trialStatusStr = trialInfo.TrialStatusStr;
|
||||||
|
|
||||||
|
var activationCode = trialInfo.AuthorizationEncrypt;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(activationCode))
|
||||||
|
{
|
||||||
|
//context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["项目未进行授权之前,不能进行操作"]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
var decodedText = string.Empty;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//解析加密信息
|
||||||
|
decodedText = Cryptography.DecryptString(activationCode, _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt");
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
return Results.Json(ResponseOutput.NotOk(_localizer["该授权码与该项目不匹配"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var hospitalCode = _hospitalOption.CurrentValue.HospitalCode;
|
||||||
|
var authInfo = JsonConvert.DeserializeObject<TrialAuthorizationInfo>(decodedText);
|
||||||
|
|
||||||
|
if (authInfo != null)
|
||||||
|
{
|
||||||
|
if (authInfo.TrialCode != trialInfo.TrialCode || authInfo.CreateUserId != trialInfo.CreateUserId || authInfo.TrialId != trialInfo.TrialId
|
||||||
|
|| authInfo.HospitalCode != hospitalCode || trialInfo.CriterionTypeList.Except(authInfo.CriterionTypeList).Count() != 0)
|
||||||
|
{
|
||||||
|
return Results.Json(ResponseOutput.NotOk(_localizer["您的操作被禁止,系统检测到该项目授权码与该项目授权配置信息不一致,请还原项目授权配置信息!"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DateTime.Now > authInfo.AuthorizationDeadLineDate.Value.AddDays(15))
|
||||||
|
{
|
||||||
|
return Results.Json(ResponseOutput.NotOk(_localizer["当前时间已经超过项目授权截止时间半个月,请重新获取项目授权后再进行操作!"]));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["该授权码与该项目不匹配"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
||||||
if (trialStatusStr == StaticData.TrialState.TrialOngoing || optActions.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
if (trialStatusStr == StaticData.TrialState.TrialOngoing || optActions.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
||||||
|
@ -167,6 +224,10 @@ public class TrialGlobalLimitEndpointFilter(IFusionCache _fusionCache, IUserInfo
|
||||||
return Results.Json(ResponseOutput.NotOk(I18n.T("TrialResource_InterceptedProjectStatusRule")));
|
return Results.Json(ResponseOutput.NotOk(I18n.T("TrialResource_InterceptedProjectStatusRule")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
|
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
namespace IRaCIS.Core.Application.Helper;
|
using IRaCIS.Application.Contracts;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Helper;
|
||||||
|
|
||||||
|
|
||||||
public static class CacheKeys
|
public static class CacheKeys
|
||||||
|
@ -60,9 +62,10 @@ public static class CacheKeys
|
||||||
|
|
||||||
public static class CacheHelper
|
public static class CacheHelper
|
||||||
{
|
{
|
||||||
public static async Task<string?> GetTrialStatusAsync(Guid trialId, IRepository<Trial> _trialRepository)
|
public static async Task<TrialCacheInfo> GetTrialStatusAsync(Guid trialId, IRepository<Trial> _trialRepository)
|
||||||
{
|
{
|
||||||
var statusStr = await _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).Select(t => t.TrialStatusStr).FirstOrDefaultAsync();
|
var statusStr = await _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).Select(t => new TrialCacheInfo { TrialId = t.Id, TrialStatusStr = t.TrialStatusStr, CriterionTypes = t.CriterionTypes, AuthorizationEncrypt = t.AuthorizationEncrypt, AuthorizationDate = t.AuthorizationDate, CreateUserId = t.CreateUserId, TrialCode = t.TrialCode })
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
return statusStr;
|
return statusStr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -344,11 +344,32 @@
|
||||||
访视读片任务
|
访视读片任务
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.Application.Service.Allocation.VisitTaskService.#ctor(IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.VisitTask},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Trial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectVisit},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TaskAllocationRule},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Subject},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectUser},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadModule},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.VisitTaskReReading},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TaskMedicalReview},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingClinicalData},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectCriteriaEvaluation},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectCriteriaEvaluationVisitFilter},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingQuestionCriterionTrial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingQuestionCriterionTrial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingTaskQuestionAnswer},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomInstance},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomSeries},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectCanceDoctor},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingTaskQuestionMark},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingTableAnswerRowInfo},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingCustomTag},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TaskInfluence},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TrialQCQuestionAnswer},Microsoft.Extensions.Logging.ILogger{IRaCIS.Core.Application.Service.Allocation.VisitTaskService},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectCriteriaEvaluationVisitStudyFilter},AutoMapper.IMapper,IRaCIS.Core.Domain.Share.IUserInfo,Microsoft.Extensions.Localization.IStringLocalizer)">
|
<member name="M:IRaCIS.Core.Application.Service.Allocation.VisitTaskService.#ctor(IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.VisitTask},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Trial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectVisit},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TaskAllocationRule},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Subject},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectUser},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadModule},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.VisitTaskReReading},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TaskMedicalReview},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingClinicalData},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectCriteriaEvaluation},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectCriteriaEvaluationVisitFilter},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingQuestionCriterionTrial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingQuestionCriterionTrial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingTaskQuestionAnswer},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomInstance},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomSeries},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectCanceDoctor},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingTaskQuestionMark},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingTableAnswerRowInfo},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingCustomTag},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TaskInfluence},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TrialQCQuestionAnswer},Microsoft.Extensions.Logging.ILogger{IRaCIS.Core.Application.Service.Allocation.VisitTaskService},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.PIAudit},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingQuestionTrial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectCriteriaEvaluationVisitStudyFilter},AutoMapper.IMapper,IRaCIS.Core.Domain.Share.IUserInfo,Microsoft.Extensions.Localization.IStringLocalizer)">
|
||||||
<summary>
|
<summary>
|
||||||
访视读片任务
|
访视读片任务
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.Application.Service.Allocation.VisitTaskService.PIAuditTask(IRaCIS.Core.Application.ViewModel.PIAuditTaskCommand)">
|
||||||
|
<summary>
|
||||||
|
new- 首次审核 后续编辑审核
|
||||||
|
</summary>
|
||||||
|
<param name="incommand"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.Application.Service.Allocation.VisitTaskService.PIAuditTaskReply(IRaCIS.Core.Application.ViewModel.PIAuditTaskReplyCommand)">
|
||||||
|
<summary>
|
||||||
|
new- 回复审核内容
|
||||||
|
</summary>
|
||||||
|
<param name="incommand"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.Application.Service.Allocation.VisitTaskService.GetPIAuditDialogList(IRaCIS.Core.Application.ViewModel.PIAuditDialogQuery)">
|
||||||
|
<summary>
|
||||||
|
new- 获取审核对话列表
|
||||||
|
</summary>
|
||||||
|
<param name="inQuery"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
<member name="M:IRaCIS.Core.Application.Service.Allocation.VisitTaskService.SetTaskUrgent(IRaCIS.Application.Contracts.SetTaskUrgentInDto)">
|
<member name="M:IRaCIS.Core.Application.Service.Allocation.VisitTaskService.SetTaskUrgent(IRaCIS.Application.Contracts.SetTaskUrgentInDto)">
|
||||||
<summary>
|
<summary>
|
||||||
设置任务加急
|
设置任务加急
|
||||||
|
@ -12980,11 +13001,6 @@
|
||||||
参考处理链接: https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/error-handling?view=aspnetcore-8.0
|
参考处理链接: https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/error-handling?view=aspnetcore-8.0
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="T:IRaCIS.Core.Application.Filter.TrialResourceFilter">
|
|
||||||
<summary>
|
|
||||||
主要为了 处理项目结束 锁库,不允许操作
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:IRaCIS.Core.Application.Helper.CacheKeys.UserLoginError(System.String)">
|
<member name="M:IRaCIS.Core.Application.Helper.CacheKeys.UserLoginError(System.String)">
|
||||||
<summary>
|
<summary>
|
||||||
用户登录错误 限制登录
|
用户登录错误 限制登录
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// 生成时间 2022-06-07 14:10:54
|
// 生成时间 2022-06-07 14:10:54
|
||||||
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
|
using IRaCIS.Application.Contracts;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
@ -10,6 +11,101 @@ using System.ComponentModel.DataAnnotations;
|
||||||
namespace IRaCIS.Core.Application.ViewModel
|
namespace IRaCIS.Core.Application.ViewModel
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public class PIReaingTaskView : ReadingTaskView
|
||||||
|
{
|
||||||
|
public Guid? FirstAuditUserId { get; set; }
|
||||||
|
|
||||||
|
public string FirstAuditUserName { get; set; }
|
||||||
|
public DateTime? FirstAuditTime { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public Guid? LatestReplyUserId { get; set; }
|
||||||
|
|
||||||
|
public string LatestReplyUserName { get; set; }
|
||||||
|
public DateTime? LatestReplyTime { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
public class PIAuditTaskEnrollOrPdCommand
|
||||||
|
{
|
||||||
|
[NotDefault]
|
||||||
|
public Guid VisitTaskId { get; set; }
|
||||||
|
public bool? IsEnrollment { get; set; }
|
||||||
|
|
||||||
|
public bool? IsPDConfirm { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PIAuditTaskCommand
|
||||||
|
{
|
||||||
|
[NotDefault]
|
||||||
|
public Guid VisitTaskId { get; set; }
|
||||||
|
public string NotAgreeReason { get; set; }
|
||||||
|
public string PIAuditNote { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public List<string> PIAuditImagePathList { get; set; }
|
||||||
|
|
||||||
|
public PIAuditState PIAuditState { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PIAuditTaskReplyCommand
|
||||||
|
{
|
||||||
|
[NotDefault]
|
||||||
|
public Guid VisitTaskId { get; set; }
|
||||||
|
|
||||||
|
public string ReplyContent { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
public class ClinicalDataDialog
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public DateTime CreateTime { get; set; }
|
||||||
|
public string Content { get; set; }
|
||||||
|
}
|
||||||
|
public class PIAuditDialogQuery
|
||||||
|
{
|
||||||
|
[NotDefault]
|
||||||
|
public Guid VisitTaskId { get; set; }
|
||||||
|
}
|
||||||
|
public class PIAuditDialogListView
|
||||||
|
{
|
||||||
|
|
||||||
|
public Guid VisitTaskId { get; set; }
|
||||||
|
public string NotAgreeReason { get; set; }
|
||||||
|
public string PIAuditNote { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public List<string> PIAuditImagePathList { get; set; }
|
||||||
|
|
||||||
|
public PIAuditState? PIAuditState { get; set; }
|
||||||
|
|
||||||
|
public string ReplyContent { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public bool? IsEnrollment { get; set; }
|
||||||
|
|
||||||
|
public bool? IsPDConfirm { get; set; }
|
||||||
|
|
||||||
|
public bool IsCurrentUser { get; set; }
|
||||||
|
|
||||||
|
public Guid CreateUserId { get; set; }
|
||||||
|
|
||||||
|
public string CreateUserName { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public DateTime CreateTime { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public UserTypeEnum UserTypeEnum { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
public class PIReadingResult
|
||||||
|
{
|
||||||
|
public Guid QuestionId { get; set; }
|
||||||
|
|
||||||
|
public string Answer { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class VisitTaskViewBasic
|
public class VisitTaskViewBasic
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
@ -214,6 +310,12 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsManualGeneration { get; set; }
|
public bool IsManualGeneration { get; set; }
|
||||||
//public bool IsAfterConvertedTask { get; set; }
|
//public bool IsAfterConvertedTask { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public List<PIReadingResult> PIReadingResultList { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public List<PatientBasicInfo> PatientList { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -399,6 +501,10 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
|
|
||||||
public string? SubjectCode { get; set; } = null;
|
public string? SubjectCode { get; set; } = null;
|
||||||
|
|
||||||
|
|
||||||
|
public string? PatientName { get; set; }
|
||||||
|
public string? PatientIdStr { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VisitTaskQuery : PageInput
|
public class VisitTaskQuery : PageInput
|
||||||
|
@ -465,6 +571,33 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
public string? RequestReReadingReason { get; set; }
|
public string? RequestReReadingReason { get; set; }
|
||||||
|
|
||||||
public ExportResult? ReadingExportType { get; set; }
|
public ExportResult? ReadingExportType { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region HIR
|
||||||
|
public PIAuditState? PIAuditState { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public string? FirstAuditUserName { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public bool? IsWaitPIAudit { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public string? LatestReplyUserName { get; set; }
|
||||||
|
|
||||||
|
public DateTime? FirstAuditTimeBegin { get; set; }
|
||||||
|
public DateTime? FirstAuditTimeEnd { get; set; }
|
||||||
|
public DateTime? LatestReplyTimeBegin { get; set; }
|
||||||
|
public DateTime? LatestReplyTimeEnd { get; set; }
|
||||||
|
|
||||||
|
public string? PatientName { get; set; }
|
||||||
|
public string? PatientIdStr { get; set; }
|
||||||
|
|
||||||
|
public string? PatientSex { get; set; }
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,12 @@ using IRaCIS.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Contracts;
|
using IRaCIS.Core.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Filter;
|
using IRaCIS.Core.Application.Filter;
|
||||||
using IRaCIS.Core.Application.ViewModel;
|
using IRaCIS.Core.Application.ViewModel;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Domain.Share.Reading;
|
using IRaCIS.Core.Domain.Share.Reading;
|
||||||
using IRaCIS.Core.Infra.EFCore.Common;
|
using IRaCIS.Core.Infra.EFCore.Common;
|
||||||
using IRaCIS.Core.Infrastructure;
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
using MassTransit;
|
using MassTransit;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -48,10 +50,196 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||||
IRepository<TaskInfluence> _taskInfluenceRepository,
|
IRepository<TaskInfluence> _taskInfluenceRepository,
|
||||||
IRepository<TrialQCQuestionAnswer> _trialQCQuestionAnswerRepository,
|
IRepository<TrialQCQuestionAnswer> _trialQCQuestionAnswerRepository,
|
||||||
ILogger<VisitTaskService> _logger,
|
ILogger<VisitTaskService> _logger,
|
||||||
|
IRepository<PIAudit> _PIAuditRepository,
|
||||||
|
IRepository<ReadingQuestionTrial> _readingQuestionTrialRepository,
|
||||||
IRepository<SubjectCriteriaEvaluationVisitStudyFilter> _subjectCriteriaEvaluationVisitStudyFilterRepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IVisitTaskService
|
IRepository<SubjectCriteriaEvaluationVisitStudyFilter> _subjectCriteriaEvaluationVisitStudyFilterRepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IVisitTaskService
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region HIR PI 相关接口
|
||||||
|
|
||||||
|
private IQueryable<VisitTask> GetReadingTaskQueryable(VisitTaskQuery inQuery)
|
||||||
|
{
|
||||||
|
var trialConfig = _trialRepository.Where(t => t.Id == inQuery.TrialId).Select(t => new { t.EnrollConfirmDefaultUserType, t.PDProgressDefaultUserType }).FirstOrDefault();
|
||||||
|
|
||||||
|
var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsAnalysisCreate == false)
|
||||||
|
|
||||||
|
.WhereIf(inQuery.TaskState != null, t => t.TaskState == inQuery.TaskState)
|
||||||
|
.WhereIf(inQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == inQuery.TrialSiteId)
|
||||||
|
.WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId)
|
||||||
|
.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.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId)
|
||||||
|
.WhereIf(inQuery.ReReadingApplyState != null, t => t.ReReadingApplyState == inQuery.ReReadingApplyState)
|
||||||
|
|
||||||
|
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)))
|
||||||
|
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientName.Contains(inQuery.PatientName)))
|
||||||
|
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientSex.Contains(inQuery.PatientSex)))
|
||||||
|
|
||||||
|
.WhereIf(inQuery.CompleteClinicalDataEnum == CompleteClinicalDataEnum.Complete, t => t.IsClinicalDataSign && t.IsNeedClinicalDataSign == true)
|
||||||
|
.WhereIf(inQuery.CompleteClinicalDataEnum == CompleteClinicalDataEnum.NotComplete, t => t.IsClinicalDataSign == false && t.IsNeedClinicalDataSign == true)
|
||||||
|
.WhereIf(inQuery.CompleteClinicalDataEnum == CompleteClinicalDataEnum.NA, t => t.IsNeedClinicalDataSign == false)
|
||||||
|
.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) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(inQuery.SubjectCode) && t.IsAnalysisCreate))
|
||||||
|
.WhereIf(inQuery.BeginAllocateDate != null, t => t.AllocateTime > inQuery.BeginAllocateDate)
|
||||||
|
.WhereIf(inQuery.EndAllocateDate != null, t => t.AllocateTime < inQuery.EndAllocateDate!.Value.AddDays(1))
|
||||||
|
|
||||||
|
.WhereIf(trialConfig?.EnrollConfirmDefaultUserType == UserTypeEnum.MIM && trialConfig?.PDProgressDefaultUserType != UserTypeEnum.MIM && _userInfo.UserTypeEnumInt == (int)UserTypeEnum.MIM, t => t.SourceSubjectVisit.IsBaseLine == true)
|
||||||
|
.WhereIf(trialConfig?.EnrollConfirmDefaultUserType != UserTypeEnum.MIM && trialConfig?.PDProgressDefaultUserType == UserTypeEnum.MIM && _userInfo.UserTypeEnumInt == (int)UserTypeEnum.MIM, t => t.SourceSubjectVisit.IsBaseLine == false)
|
||||||
|
.WhereIf(inQuery.IsWaitPIAudit == true, t => t.FirstAuditUserId == null)
|
||||||
|
.WhereIf(inQuery.IsWaitPIAudit == false, t => t.FirstAuditUserId != null)
|
||||||
|
.WhereIf(inQuery.PIAuditState != null, t => t.PIAuditState == inQuery.PIAuditState)
|
||||||
|
.WhereIf(inQuery.FirstAuditTimeBegin != null, t => t.AllocateTime > inQuery.FirstAuditTimeBegin)
|
||||||
|
.WhereIf(inQuery.FirstAuditTimeEnd != null, t => t.AllocateTime < inQuery.FirstAuditTimeEnd)
|
||||||
|
.WhereIf(inQuery.LatestReplyTimeBegin != null, t => t.AllocateTime > inQuery.LatestReplyTimeBegin)
|
||||||
|
.WhereIf(inQuery.LatestReplyTimeEnd != null, t => t.AllocateTime < inQuery.LatestReplyTimeEnd);
|
||||||
|
|
||||||
|
return visitTaskQueryable;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IResponseOutput<PageOutput<PIReaingTaskView>>> GetPIReadingAuditList(VisitTaskQuery queryVisitTask)
|
||||||
|
{
|
||||||
|
|
||||||
|
var visitTaskQueryable = GetReadingTaskQueryable(queryVisitTask)
|
||||||
|
.ProjectTo<PIReaingTaskView>(_mapper.ConfigurationProvider);
|
||||||
|
|
||||||
|
var defalutSortArray = new string[] { nameof(VisitTask.IsUrgent) + " desc", nameof(VisitTask.SubjectId), nameof(VisitTask.VisitTaskNum) };
|
||||||
|
|
||||||
|
var pageList = await visitTaskQueryable.ToPagedListAsync(queryVisitTask, defalutSortArray);
|
||||||
|
|
||||||
|
var trialTaskConfig = _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).ProjectTo<TrialUrgentConfig>(_mapper.ConfigurationProvider).FirstOrDefault();
|
||||||
|
|
||||||
|
var questionList = _readingQuestionTrialRepository.Where(t => t.TrialId == queryVisitTask.TrialId && t.ReadingQuestionCriterionTrialId == queryVisitTask.TrialReadingCriterionId)
|
||||||
|
.Where(t => t.IsJudgeQuestion == true)
|
||||||
|
.Select(t => new { QuestionId = t.Id, QuestionName = _userInfo.IsEn_Us ? t.QuestionEnName : t.QuestionName, t.DictionaryCode }).ToList();
|
||||||
|
|
||||||
|
trialTaskConfig!.OtherObj = questionList;
|
||||||
|
return ResponseOutput.Ok(pageList, trialTaskConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///new- 首次审核 后续编辑审核
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="incommand"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost]
|
||||||
|
|
||||||
|
public async Task<IResponseOutput> PIAuditTask(PIAuditTaskCommand incommand)
|
||||||
|
{
|
||||||
|
//需要设置首次审核时间 审核人
|
||||||
|
var visitTask = await _visitTaskRepository.FirstOrDefaultAsync(t => t.Id == incommand.VisitTaskId);
|
||||||
|
var isFirstAudit = visitTask.FirstAuditUserId == null;
|
||||||
|
|
||||||
|
visitTask.NotAgreeReason = incommand.NotAgreeReason;
|
||||||
|
visitTask.PIAuditNote = incommand.PIAuditNote;
|
||||||
|
visitTask.PIAuditImagePath = string.Join('|', incommand.PIAuditImagePathList);
|
||||||
|
visitTask.PIAuditState = incommand.PIAuditState;
|
||||||
|
|
||||||
|
visitTask.LatestReplyUserId = _userInfo.Id;
|
||||||
|
visitTask.LatestReplyTime = DateTime.Now;
|
||||||
|
visitTask.IsEnrollment = incommand.PIAuditState == PIAuditState.PINotAgree ? null : visitTask.IsEnrollment;
|
||||||
|
visitTask.IsPDConfirm = incommand.PIAuditState == PIAuditState.PINotAgree ? null : visitTask.IsPDConfirm;
|
||||||
|
|
||||||
|
if (isFirstAudit)
|
||||||
|
{
|
||||||
|
visitTask.FirstAuditUserId = _userInfo.Id;
|
||||||
|
visitTask.FirstAuditTime = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
//发送对话
|
||||||
|
var addDialig = _mapper.Map<PIAudit>(incommand);
|
||||||
|
|
||||||
|
addDialig.PIAuditState = incommand.PIAuditState;
|
||||||
|
|
||||||
|
addDialig.PIAuditImagePath = visitTask.PIAuditImagePath;
|
||||||
|
|
||||||
|
await _PIAuditRepository.AddAsync(addDialig);
|
||||||
|
|
||||||
|
await _visitTaskRepository.SaveChangesAsync();
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[UnitOfWork]
|
||||||
|
public async Task<IResponseOutput> PIAuditTaskEnrollOrPD(PIAuditTaskEnrollOrPdCommand incommand, [FromServices] IEmailSendService emailSendService)
|
||||||
|
{
|
||||||
|
var visitTask = await _visitTaskRepository.FirstOrDefaultAsync(t => t.Id == incommand.VisitTaskId);
|
||||||
|
visitTask.IsEnrollment = incommand.IsEnrollment != null ? incommand.IsEnrollment : visitTask.IsEnrollment;
|
||||||
|
visitTask.IsPDConfirm = incommand.IsPDConfirm != null ? incommand.IsPDConfirm : visitTask.IsPDConfirm;
|
||||||
|
visitTask.LatestReplyUserId = _userInfo.Id;
|
||||||
|
visitTask.LatestReplyTime = DateTime.Now;
|
||||||
|
|
||||||
|
await _visitTaskRepository.SaveChangesAsync();
|
||||||
|
|
||||||
|
//await emailSendService.SendEnrollOrPdEmail(visitTask.Id, incommand.IsEnrollment, incommand.IsPDConfirm);
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///new- 回复审核内容
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="incommand"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<IResponseOutput> PIAuditTaskReply(PIAuditTaskReplyCommand incommand)
|
||||||
|
{
|
||||||
|
await _PIAuditRepository.AddAsync(new PIAudit() { ReplyContent = incommand.ReplyContent, VisitTaskId = incommand.VisitTaskId });
|
||||||
|
|
||||||
|
await _PIAuditRepository.SaveChangesAsync();
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// new- 获取审核对话列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="inQuery"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<List<PIAuditDialogListView>> GetPIAuditDialogList(PIAuditDialogQuery inQuery)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
var list = await _PIAuditRepository.Where(t => t.VisitTaskId == inQuery.VisitTaskId).OrderBy(t => t.CreateTime).ProjectTo<PIAuditDialogListView>(_mapper.ConfigurationProvider).ToListAsync();
|
||||||
|
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
item.IsCurrentUser = item.CreateUserId == _userInfo.Id;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IResponseOutput> AddClinicalDataDialog(Guid visiTaskId, string content,
|
||||||
|
[FromServices] IRepository<SubjectVisitClinicalDialog> subjectVisitClinicalDialogRepository,
|
||||||
|
[FromServices] IEmailSendService emailSendService)
|
||||||
|
{
|
||||||
|
var taskInfo = await _visitTaskRepository.Where(t => t.Id == visiTaskId).Select(t => new { t.SourceSubjectVisitId, t.Subject.Code, t.SourceSubjectVisit.VisitName, t.Trial.ResearchProgramNo, t.Trial.TrialCode }).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
await subjectVisitClinicalDialogRepository.AddAsync(new SubjectVisitClinicalDialog() { SubjectVisitId = (Guid)taskInfo.SourceSubjectVisitId, Content = content }, true);
|
||||||
|
|
||||||
|
await emailSendService.SendClinicalDataQuestionAsync(visiTaskId, content);
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<ClinicalDataDialog>> GetClinicalDataDialog(Guid visiTaskId,
|
||||||
|
[FromServices] IRepository<SubjectVisitClinicalDialog> _subjectVisitClinicalDialogRepository)
|
||||||
|
{
|
||||||
|
var subjectVisitId = await _visitTaskRepository.Where(t => t.Id == visiTaskId).Select(t => t.SourceSubjectVisitId).FirstOrDefaultAsync();
|
||||||
|
var list = _subjectVisitClinicalDialogRepository.Where(t => t.SubjectVisitId == subjectVisitId).ProjectTo<ClinicalDataDialog>(_mapper.ConfigurationProvider).OrderByDescending(t => t.CreateTime).ToList();
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置任务加急
|
/// 设置任务加急
|
||||||
|
@ -1001,9 +1189,53 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||||
|
|
||||||
var critrion = await _trialReadingCriterionRepository.FindAsync(trialReadingCriterionId);
|
var critrion = await _trialReadingCriterionRepository.FindAsync(trialReadingCriterionId);
|
||||||
|
|
||||||
|
|
||||||
|
var readingDivisionEnum = critrion.ReadingDivisionEnum;
|
||||||
|
|
||||||
|
var piReadingScopenEnum = critrion.PIReadingScopenEnum;
|
||||||
|
|
||||||
if (critrion.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
|
if (critrion.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
|
||||||
{
|
{
|
||||||
var visitQuery = _visitTaskRepository.Where(x => x.TrialId == inQuery.TrialId && x.DoctorUserId == _userInfo.Id && x.TaskState == TaskState.Effect)
|
var visitQuery = _visitTaskRepository.Where(x => x.TrialId == inQuery.TrialId && x.DoctorUserId == _userInfo.Id && x.TaskState == TaskState.Effect)
|
||||||
|
.WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId)
|
||||||
|
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)))
|
||||||
|
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientName.Contains(inQuery.PatientName)))
|
||||||
|
|
||||||
|
//PI 读基线的时候,subject 如果PI基线没阅片完,SR就不能看
|
||||||
|
.WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SR && piReadingScopenEnum == PIReadingScopenEnum.AllBaseline && readingDivisionEnum == ReadingDivisionEnum.PIandSR,
|
||||||
|
t => t.Subject.SubjectVisitTaskList.Any(c => c.SourceSubjectVisit.IsBaseLine == true && c.ReadingTaskState == ReadingTaskState.HaveSigned && c.TaskState == TaskState.Effect && c.TrialReadingCriterionId == trialReadingCriterionId))
|
||||||
|
|
||||||
|
//PI 读随访的时候, subject 如果SR基线没阅片完,PI就不能看
|
||||||
|
.WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.PI && piReadingScopenEnum == PIReadingScopenEnum.AllVisit && readingDivisionEnum == ReadingDivisionEnum.PIandSR,
|
||||||
|
t => t.Subject.SubjectVisitTaskList.Any(c => c.SourceSubjectVisit.IsBaseLine == true && c.ReadingTaskState == ReadingTaskState.HaveSigned && c.TaskState == TaskState.Effect && c.TrialReadingCriterionId == trialReadingCriterionId))
|
||||||
|
|
||||||
|
//没有site概念
|
||||||
|
//.WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SR, t => t.Subject.TrialSite.CRCUserList.Any(u => u.UserId == _userInfo.Id))
|
||||||
|
// 仅仅SR阅片 PI 没有任务列表
|
||||||
|
.WhereIf(readingDivisionEnum == ReadingDivisionEnum.OnlySR && _userInfo.UserTypeEnumInt == (int)UserTypeEnum.PI, t => t.TrialId == Guid.Empty)
|
||||||
|
|
||||||
|
//PI查看 PI阅片所有基线
|
||||||
|
.WhereIf(readingDivisionEnum == ReadingDivisionEnum.PIandSR && _userInfo.UserTypeEnumInt == (int)UserTypeEnum.PI && piReadingScopenEnum == PIReadingScopenEnum.AllBaseline,
|
||||||
|
t => t.SourceSubjectVisit.IsBaseLine == true)
|
||||||
|
|
||||||
|
// PI查看 PI 阅片所有
|
||||||
|
.WhereIf(readingDivisionEnum == ReadingDivisionEnum.PIandSR && _userInfo.UserTypeEnumInt == (int)UserTypeEnum.PI && piReadingScopenEnum == PIReadingScopenEnum.AllBaselineandVisit,
|
||||||
|
t => true)
|
||||||
|
|
||||||
|
// PI查看 PI 阅片访视
|
||||||
|
.WhereIf(readingDivisionEnum == ReadingDivisionEnum.PIandSR && _userInfo.UserTypeEnumInt == (int)UserTypeEnum.PI && piReadingScopenEnum == PIReadingScopenEnum.AllVisit,
|
||||||
|
t => t.SourceSubjectVisit.IsBaseLine == false)
|
||||||
|
|
||||||
|
//SR查看 PI阅片所有基线
|
||||||
|
.WhereIf(readingDivisionEnum == ReadingDivisionEnum.PIandSR && _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SR && piReadingScopenEnum == PIReadingScopenEnum.AllBaseline,
|
||||||
|
t => t.SourceSubjectVisit.IsBaseLine == false)
|
||||||
|
|
||||||
|
//SR查看 PI 阅片所有
|
||||||
|
.WhereIf(readingDivisionEnum == ReadingDivisionEnum.PIandSR && _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SR && piReadingScopenEnum == PIReadingScopenEnum.AllBaselineandVisit,
|
||||||
|
t => t.TrialId == Guid.Empty)
|
||||||
|
//SR查看 PI 阅片访视
|
||||||
|
.WhereIf(readingDivisionEnum == ReadingDivisionEnum.PIandSR && _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SR && piReadingScopenEnum == PIReadingScopenEnum.AllVisit,
|
||||||
|
t => t.SourceSubjectVisit.IsBaseLine == true)
|
||||||
|
|
||||||
.WhereIf(inQuery.SubjectId != null, x => x.SubjectId == inQuery.SubjectId)
|
.WhereIf(inQuery.SubjectId != null, x => x.SubjectId == inQuery.SubjectId)
|
||||||
//前序 不存在 未生成任务的访视
|
//前序 不存在 未生成任务的访视
|
||||||
|
|
|
@ -10,6 +10,21 @@ namespace IRaCIS.Core.Application.Service
|
||||||
public AllocationConfig()
|
public AllocationConfig()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region HIR
|
||||||
|
CreateMap<VisitTask, PIReaingTaskView>().IncludeBase<VisitTask, ReadingTaskView>()
|
||||||
|
.ForMember(o => o.FirstAuditUserName, t => t.MapFrom(u => u.FirstAuditUser.UserName))
|
||||||
|
.ForMember(o => o.LatestReplyUserName, t => t.MapFrom(u => u.LatestReplyUser.UserName))
|
||||||
|
.ForMember(d => d.PatientList, u => u.MapFrom(s => s.Subject.SubjectPatientList));
|
||||||
|
|
||||||
|
CreateMap<PIAuditTaskCommand, PIAudit>();
|
||||||
|
|
||||||
|
CreateMap<PIAudit, PIAuditDialogListView>()
|
||||||
|
.ForMember(o => o.CreateUserName, t => t.MapFrom(u => u.CreateUser.UserName))
|
||||||
|
.ForMember(o => o.UserTypeEnum, t => t.MapFrom(u => u.CreateUser.UserTypeEnum))
|
||||||
|
//.ForMember(o => o.PIAuditState, t => t.MapFrom(u => u.VisitTask.PIAuditState))
|
||||||
|
;
|
||||||
|
#endregion
|
||||||
|
|
||||||
CreateMap<ReadingTableAnswerRowInfo, ConvertedRowInfo>()
|
CreateMap<ReadingTableAnswerRowInfo, ConvertedRowInfo>()
|
||||||
.ForMember(d => d.OriginalId, u => u.MapFrom(s => s.Id));
|
.ForMember(d => d.OriginalId, u => u.MapFrom(s => s.Id));
|
||||||
|
|
||||||
|
@ -321,6 +336,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
CreateMap<ReadingClinicalDataPDF, ReadingConsistentClinicalDataPDF>().ReverseMap();
|
CreateMap<ReadingClinicalDataPDF, ReadingConsistentClinicalDataPDF>().ReverseMap();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Application.Contracts;
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Infrastructure;
|
using IRaCIS.Core.Infrastructure;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
@ -10,6 +11,10 @@ namespace IRaCIS.Core.Application.Service
|
||||||
{
|
{
|
||||||
Task SendEnrollOrPdEmail(Guid visitTaskId, bool? isEnrollment, bool? isPDConfirm);
|
Task SendEnrollOrPdEmail(Guid visitTaskId, bool? isEnrollment, bool? isPDConfirm);
|
||||||
|
|
||||||
|
Task SendClinicalDataQuestionAsync(Guid visitTaskId, string content);
|
||||||
|
|
||||||
|
Task SendPIAuditResultAsync(Guid visitTaskId);
|
||||||
|
|
||||||
Task<(TrialEmailNoticeConfig?, SMTPEmailConfig?)> BuildEmailConfig(Guid trialId, EmailBusinessScenario businessScenario, Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc, Guid? siteId = null, Guid? trialReadingCriterionId = null);
|
Task<(TrialEmailNoticeConfig?, SMTPEmailConfig?)> BuildEmailConfig(Guid trialId, EmailBusinessScenario businessScenario, Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc, Guid? siteId = null, Guid? trialReadingCriterionId = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +23,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
IRepository<TrialUser> _trialUserRepository,
|
IRepository<TrialUser> _trialUserRepository,
|
||||||
IRepository<VisitTask> _visitTaskRepository,
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
IRepository<TrialSiteUser> _trialSiteUserRepository,
|
IRepository<TrialSiteUser> _trialSiteUserRepository,
|
||||||
|
IRepository<Dictionary> _dictionaryRepository,
|
||||||
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IEmailSendService
|
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IEmailSendService
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -98,6 +104,71 @@ namespace IRaCIS.Core.Application.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//临床数据质询
|
||||||
|
public async Task SendClinicalDataQuestionAsync(Guid visitTaskId, string content)
|
||||||
|
{
|
||||||
|
|
||||||
|
var isEn_us = _userInfo.IsEn_Us;
|
||||||
|
|
||||||
|
var info = await _visitTaskRepository.Where(t => t.Id == visitTaskId, ignoreQueryFilters: true).Select(t => new { t.TrialId, t.Trial.ResearchProgramNo, t.Trial.TrialCode, t.SourceSubjectVisit.VisitName, t.Subject.TrialSiteId, t.Subject.Code }).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
|
||||||
|
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc = trialEmailConfig =>
|
||||||
|
{
|
||||||
|
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, info.ResearchProgramNo, info.Code, info.VisitName);
|
||||||
|
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
|
||||||
|
EmailNamePlaceholder, info.ResearchProgramNo, info.Code, info.VisitName, _userInfo.UserName, content, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||||
|
return (topicStr, htmlBodyStr, isEn_us, null);
|
||||||
|
};
|
||||||
|
|
||||||
|
await SendTrialEmailAsync(info.TrialId, EmailBusinessScenario.ClinicalDataQuestion, topicAndHtmlFunc, info.TrialSiteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendPIAuditResultAsync(Guid visitTaskId)
|
||||||
|
{
|
||||||
|
var isEn_us = _userInfo.IsEn_Us;
|
||||||
|
|
||||||
|
var info = await _visitTaskRepository.Where(t => t.Id == visitTaskId, ignoreQueryFilters: true).Select(t => new { t.TrialId, t.Trial.ResearchProgramNo, t.Trial.TrialCode, t.SourceSubjectVisit.VisitName, t.Subject.TrialSiteId, t.Subject.Code }).FirstOrDefaultAsync();
|
||||||
|
var answerList = await _visitTaskRepository.Where(t => t.Id == visitTaskId, ignoreQueryFilters: true).SelectMany(t => t.ReadingTaskQuestionAnswerList).Where(t => t.ReadingQuestionTrial.IsJudgeQuestion == true).Select(t => new { QuestionName = isEn_us ? t.ReadingQuestionTrial.QuestionEnName : t.ReadingQuestionTrial.QuestionName, t.ReadingQuestionTrial.DictionaryCode, t.Answer }).ToListAsync();
|
||||||
|
|
||||||
|
var template = " <div style=\"margin-left: 2ch;\"> {0}: {1} </div>";
|
||||||
|
|
||||||
|
var needTranslateDicNameList = answerList.Where(t => !string.IsNullOrEmpty(t.DictionaryCode)).Select(t => t.DictionaryCode).ToList();
|
||||||
|
|
||||||
|
|
||||||
|
var searchList = await _dictionaryRepository.Where(t => needTranslateDicNameList.ToArray().Contains(t.Parent.Code) && t.ParentId != null && t.IsEnable).ProjectTo<BasicDicSelectCopy>(_mapper.ConfigurationProvider, new { isEn_Us = _userInfo.IsEn_Us }).ToListAsync();
|
||||||
|
|
||||||
|
var translateDataList = searchList.GroupBy(t => t.ParentCode).ToDictionary(g => g.Key, g => g.OrderBy(t => t.ShowOrder).ToList());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Func<bool, string, string, string> transFunc = (bool isNeedTranslate, string dicCode, string answer) =>
|
||||||
|
{
|
||||||
|
if (isNeedTranslate && translateDataList.ContainsKey(dicCode))
|
||||||
|
{
|
||||||
|
var result = translateDataList[dicCode].Where(t => t.Code.ToLower() == answer.ToLower()).Select(t => isEn_us ? t.Value : t.ValueCN).FirstOrDefault() ?? answer;
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
var piResult = string.Join(' ', answerList.Select(t => string.Format(template, t.QuestionName, transFunc(!string.IsNullOrEmpty(t.DictionaryCode), t.DictionaryCode, t.Answer))));
|
||||||
|
|
||||||
|
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc = trialEmailConfig =>
|
||||||
|
{
|
||||||
|
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, info.ResearchProgramNo, info.Code, info.VisitName);
|
||||||
|
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
|
||||||
|
EmailNamePlaceholder, info.ResearchProgramNo, info.Code, info.VisitName, _userInfo.UserName, piResult, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||||
|
return (topicStr, htmlBodyStr, isEn_us, null);
|
||||||
|
};
|
||||||
|
|
||||||
|
await SendTrialEmailAsync(info.TrialId, EmailBusinessScenario.PIAuditResutl, topicAndHtmlFunc, info.TrialSiteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
IRepository<Trial> _trialRepository,
|
IRepository<Trial> _trialRepository,
|
||||||
IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig,
|
IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig,
|
||||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig,
|
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig,
|
||||||
|
IOptionsMonitor<SystemHospitalOption> _systemHospitalConfig,
|
||||||
ISearcher _searcher, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IFusionCache _fusionCache) : BaseService, IUserService
|
ISearcher _searcher, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IFusionCache _fusionCache) : BaseService, IUserService
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -276,10 +276,14 @@ namespace IRaCIS.Core.Application.Service
|
||||||
public async Task<IResponseOutput> ResetPassword(Guid userId)
|
public async Task<IResponseOutput> ResetPassword(Guid userId)
|
||||||
{
|
{
|
||||||
|
|
||||||
var pwd = IRCEmailPasswordHelper.GenerateRandomPassword(10);
|
//var pwd = IRCEmailPasswordHelper.GenerateRandomPassword(10);
|
||||||
|
|
||||||
|
var pwd = "123456";
|
||||||
|
|
||||||
|
if (_systemHospitalConfig.CurrentValue.IsCanConnectInternet)
|
||||||
|
{
|
||||||
await _mailVerificationService.AdminResetPwdSendEmailAsync(userId, pwd);
|
await _mailVerificationService.AdminResetPwdSendEmailAsync(userId, pwd);
|
||||||
|
}
|
||||||
|
|
||||||
await _userRepository.UpdatePartialFromQueryAsync(userId, u => new User()
|
await _userRepository.UpdatePartialFromQueryAsync(userId, u => new User()
|
||||||
{
|
{
|
||||||
|
@ -347,6 +351,8 @@ namespace IRaCIS.Core.Application.Service
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpGet("{email}/{verifyCode}")]
|
[HttpGet("{email}/{verifyCode}")]
|
||||||
public async Task<List<UserAccountDto>> VerifyAnonymousVerifyCode(string email, string verifyCode)
|
public async Task<List<UserAccountDto>> VerifyAnonymousVerifyCode(string email, string verifyCode)
|
||||||
|
{
|
||||||
|
if (_systemHospitalConfig.CurrentValue.IsCanConnectInternet)
|
||||||
{
|
{
|
||||||
var verificationRecord = await _verificationCodeRepository
|
var verificationRecord = await _verificationCodeRepository
|
||||||
.Where(t => t.UserId == Guid.Empty && t.Code == verifyCode && t.CodeType == VerifyType.Email && t.EmailOrPhone == email).OrderByDescending(t => t.CreateTime).FirstOrDefaultAsync();
|
.Where(t => t.UserId == Guid.Empty && t.Code == verifyCode && t.CodeType == VerifyType.Email && t.EmailOrPhone == email).OrderByDescending(t => t.CreateTime).FirstOrDefaultAsync();
|
||||||
|
@ -374,8 +380,20 @@ namespace IRaCIS.Core.Application.Service
|
||||||
await _verificationCodeRepository.BatchDeleteNoTrackingAsync(t => t.Id == verificationRecord.Id);
|
await _verificationCodeRepository.BatchDeleteNoTrackingAsync(t => t.Id == verificationRecord.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var isPass = _userRepository.Where(t => t.EMail == email).Any(t => t.CheckCode == verifyCode);
|
||||||
|
|
||||||
var list = await _userRepository.Where(t => t.EMail == email && t.Status == UserStateEnum.Enable).Select(t => new UserAccountDto() { UserId = t.Id, UserName = t.UserName, UserRealName = t.FullName, UserType = t.UserTypeRole.UserTypeShortName }).ToListAsync();
|
if (!isPass)
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException(_localizer["User_VerificationCodeError"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var list = await _userRepository.Where(t => t.EMail == email).Select(t => new UserAccountDto() { UserId = t.Id, UserName = t.UserName, UserRealName = t.FullName, UserType = t.UserTypeRole.UserTypeShortName }).ToListAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -531,9 +549,12 @@ namespace IRaCIS.Core.Application.Service
|
||||||
var success = await _userRepository.SaveChangesAsync();
|
var success = await _userRepository.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_systemHospitalConfig.CurrentValue.IsCanConnectInternet)
|
||||||
|
{
|
||||||
await _mailVerificationService.AddUserSendEmailAsync(saveItem.Id, userAddModel.BaseUrl, userAddModel.RouteUrl);
|
await _mailVerificationService.AddUserSendEmailAsync(saveItem.Id, userAddModel.BaseUrl, userAddModel.RouteUrl);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return ResponseOutput.Ok(new UserAddedReturnDTO { Id = saveItem.Id, UserCode = saveItem.UserCode });
|
return ResponseOutput.Ok(new UserAddedReturnDTO { Id = saveItem.Id, UserCode = saveItem.UserCode });
|
||||||
|
|
||||||
|
|
|
@ -244,7 +244,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
/// <param name="inDto"></param>
|
/// <param name="inDto"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[TypeFilter(typeof(TrialResourceFilter))]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
public async Task<IResponseOutput> AddOrUpdateReadingMedicineTrialQuestion(ReadingMedicineTrialQuestionAddOrEdit inDto)
|
public async Task<IResponseOutput> AddOrUpdateReadingMedicineTrialQuestion(ReadingMedicineTrialQuestionAddOrEdit inDto)
|
||||||
{
|
{
|
||||||
var existsQuery = _readingMedicineTrialQuestionRepository
|
var existsQuery = _readingMedicineTrialQuestionRepository
|
||||||
|
|
|
@ -315,7 +315,12 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
|
|
||||||
public bool IsPDProgressView { get; set; }
|
public bool IsPDProgressView { get; set; }
|
||||||
|
|
||||||
|
public UserTypeEnum? EnrollConfirmDefaultUserType { get; set; }
|
||||||
|
|
||||||
|
public UserTypeEnum? PDProgressDefaultUserType { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public object? OtherObj { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,8 @@ namespace IRaCIS.Core.Application
|
||||||
IRepository<Enroll> _enrollRepository,
|
IRepository<Enroll> _enrollRepository,
|
||||||
IRepository<TrialStateChange> _trialStateChangeRepository,
|
IRepository<TrialStateChange> _trialStateChangeRepository,
|
||||||
IRepository<ReadingTableQuestionTrial> _readingTableQuestionTrialRepository,
|
IRepository<ReadingTableQuestionTrial> _readingTableQuestionTrialRepository,
|
||||||
|
IOptionsMonitor<ServiceVerifyConfigOption> _basicSystemConfigConfig,
|
||||||
|
IOptionsMonitor<SystemHospitalOption> _hospitalOption,
|
||||||
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IFusionCache _fusionCache) : BaseService, ITrialConfigService
|
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IFusionCache _fusionCache) : BaseService, ITrialConfigService
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -1174,15 +1176,57 @@ namespace IRaCIS.Core.Application
|
||||||
return ResponseOutput.NotOk(_localizer["TrialConfig_ProjectNotStarted"]);
|
return ResponseOutput.NotOk(_localizer["TrialConfig_ProjectNotStarted"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (trialStatusStr != StaticData.TrialState.TrialOngoing)
|
|
||||||
//{
|
|
||||||
// trial.TrialFinishTime = DateTime.Now;
|
|
||||||
//}
|
|
||||||
|
|
||||||
trial.TrialStateChangeList.Add(new TrialStateChange() { OriginState = trial.TrialStatusStr, NowState = trialStatusStr, Reason = reason ?? String.Empty, TrialId = trialId });
|
trial.TrialStateChangeList.Add(new TrialStateChange() { OriginState = trial.TrialStatusStr, NowState = trialStatusStr, Reason = reason ?? String.Empty, TrialId = trialId });
|
||||||
|
|
||||||
trial.TrialStatusStr = trialStatusStr;
|
trial.TrialStatusStr = trialStatusStr;
|
||||||
|
|
||||||
|
//项目启动,需要添加项目标准,设置默认数据
|
||||||
|
|
||||||
|
if (trialStatusStr.Contains(StaticData.TrialState.TrialOngoing))
|
||||||
|
{
|
||||||
|
//配置的要同步的系统标准
|
||||||
|
|
||||||
|
var criterionTypeList = trial.CriterionTypeList;
|
||||||
|
|
||||||
|
//删除项目项目已有的,之前查询接口,每次会自动添加,属实恶心
|
||||||
|
await _readingQuestionCriterionTrialRepository.BatchDeleteNoTrackingAsync(x => x.TrialId == trialId && x.IsConfirm == false);
|
||||||
|
|
||||||
|
var trialCriterionTypes = _readingQuestionCriterionTrialRepository.Where(x => x.TrialId == trialId)
|
||||||
|
.Select(x => x.CriterionType);
|
||||||
|
|
||||||
|
var needAddCriterionList = await _readingQuestionCriterionSystemRepository.Where(x => x.IsEnable && criterionTypeList.Contains(x.CriterionType) && !trialCriterionTypes.Contains(x.CriterionType)).ProjectTo<ReadingQuestionCriterionTrial>(_mapper.ConfigurationProvider).ToListAsync();
|
||||||
|
|
||||||
|
needAddCriterionList.ForEach(x =>
|
||||||
|
{
|
||||||
|
x.TrialId = trialId;
|
||||||
|
x.ReadingQuestionCriterionSystemId = x.Id;
|
||||||
|
x.Id = NewId.NextGuid();
|
||||||
|
|
||||||
|
x.IsAutoCreate =/* x.CriterionType == CriterionType.RECIST1Pointt1_MB ? false :*/ true;
|
||||||
|
|
||||||
|
//对应勾选确认
|
||||||
|
x.IsConfirm = true;
|
||||||
|
|
||||||
|
//对应签名
|
||||||
|
x.IsSigned = true;
|
||||||
|
x.ReadingInfoSignTime = DateTime.Now;
|
||||||
|
});
|
||||||
|
|
||||||
|
await _readingQuestionCriterionTrialRepository.AddRangeAsync(needAddCriterionList);
|
||||||
|
|
||||||
|
await _readingQuestionCriterionTrialRepository.SaveChangesAsync();
|
||||||
|
|
||||||
|
|
||||||
|
//同步标准数据
|
||||||
|
foreach (var item in needAddCriterionList)
|
||||||
|
{
|
||||||
|
await AsyncTrialCriterionDictionary(new AsyncTrialCriterionDictionaryInDto() { TrialReadingCriterionId = item.Id });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//Paused、 添加工总量 算医生读片中
|
//Paused、 添加工总量 算医生读片中
|
||||||
if (trialStatusStr.Contains(StaticData.TrialState.TrialCompleted))
|
if (trialStatusStr.Contains(StaticData.TrialState.TrialCompleted))
|
||||||
|
@ -1196,7 +1240,39 @@ namespace IRaCIS.Core.Application
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await _fusionCache.SetAsync(CacheKeys.Trial(trial.Id.ToString()), trialStatusStr, TimeSpan.FromDays(7));
|
#region 自动激活一个月
|
||||||
|
|
||||||
|
var hospitalCode = _hospitalOption.CurrentValue.HospitalCode;
|
||||||
|
var hospitalName = _hospitalOption.CurrentValue.HospitalName;
|
||||||
|
var deadLineDate = DateTime.Now.Date.AddMonths(1).AddDays(1).AddSeconds(-1);
|
||||||
|
|
||||||
|
var authorizationInfo = new TrialAuthorizationInfo()
|
||||||
|
{
|
||||||
|
ActiveTime = DateTime.Now,
|
||||||
|
PurchaseDuration = 1,
|
||||||
|
CriterionTypeList = trial.CriterionTypeList,
|
||||||
|
HospitalCode = hospitalCode,
|
||||||
|
HospitalName = hospitalName,
|
||||||
|
CreateUserId = trial.CreateUserId,
|
||||||
|
TrialId = trial.Id,
|
||||||
|
TrialCode = trial.TrialCode,
|
||||||
|
AuthorizationDeadLineDate = deadLineDate,
|
||||||
|
ActiveDeadLineDate = DateTime.Now.Date.AddDays(8).AddSeconds(-1)
|
||||||
|
};
|
||||||
|
|
||||||
|
var newActivationCode = Cryptography.EncryptString($"{JsonConvert.SerializeObject(authorizationInfo)}", _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt");
|
||||||
|
|
||||||
|
trial.AuthorizationEncrypt = newActivationCode;
|
||||||
|
trial.AuthorizationDate = deadLineDate;
|
||||||
|
|
||||||
|
//await _trialRepository.BatchUpdateNoTrackingAsync(t => t.Id == trialId, u => new Trial() { AuthorizationEncrypt = newActivationCode, AuthorizationDate = deadLineDate });
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
var caheInfo = new TrialCacheInfo() { TrialId = trial.Id, TrialStatusStr = trial.TrialStatusStr, CriterionTypes = trial.CriterionTypes, AuthorizationEncrypt = trial.AuthorizationEncrypt, AuthorizationDate = trial.AuthorizationDate, CreateUserId = trial.CreateUserId, TrialCode = trial.TrialCode };
|
||||||
|
|
||||||
|
|
||||||
|
await _fusionCache.SetAsync(CacheKeys.Trial(trial.Id.ToString()), caheInfo, TimeSpan.FromDays(7));
|
||||||
|
|
||||||
await _trialRepository.SaveChangesAsync();
|
await _trialRepository.SaveChangesAsync();
|
||||||
|
|
||||||
|
|
|
@ -196,8 +196,8 @@ namespace IRaCIS.Application.Services
|
||||||
[FromServices] IOptionsMonitor<SystemEmailSendConfig> _systemEmailSendConfig,
|
[FromServices] IOptionsMonitor<SystemEmailSendConfig> _systemEmailSendConfig,
|
||||||
[FromServices] IOptionsMonitor<ServiceVerifyConfigOption> _basicSystemConfigConfig,
|
[FromServices] IOptionsMonitor<ServiceVerifyConfigOption> _basicSystemConfigConfig,
|
||||||
[FromServices] IOptionsMonitor<SystemHospitalOption> _systemHospitalOption,
|
[FromServices] IOptionsMonitor<SystemHospitalOption> _systemHospitalOption,
|
||||||
IRepository<TrialDictionary> _trialDictionaryRepository,
|
[FromServices] IRepository<TrialDictionary> _trialDictionaryRepository,
|
||||||
IRepository<TrialUser> _trialUserRepository,
|
[FromServices] IRepository<TrialUser> _trialUserRepository,
|
||||||
[FromServices] IFusionCache _provider)
|
[FromServices] IFusionCache _provider)
|
||||||
{
|
{
|
||||||
var code = _systemHospitalOption.CurrentValue.HospitalCode;
|
var code = _systemHospitalOption.CurrentValue.HospitalCode;
|
||||||
|
@ -840,7 +840,7 @@ namespace IRaCIS.Application.Services
|
||||||
#region 受试者管理
|
#region 受试者管理
|
||||||
|
|
||||||
|
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IResponseOutput<string>> AddOrUpdateSubject([FromBody] AddOrUpdateSubjectCommand subjectCommand)
|
public async Task<IResponseOutput<string>> AddOrUpdateSubject([FromBody] AddOrUpdateSubjectCommand subjectCommand)
|
||||||
{
|
{
|
||||||
|
@ -1136,7 +1136,7 @@ namespace IRaCIS.Application.Services
|
||||||
/// <param name="inCommand"></param>
|
/// <param name="inCommand"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
public async Task<IResponseOutput> AutoBindingPatientStudyVisit(AutoBindingPatientStudyVisitCommand inCommand)
|
public async Task<IResponseOutput> AutoBindingPatientStudyVisit(AutoBindingPatientStudyVisitCommand inCommand)
|
||||||
{
|
{
|
||||||
//找到已绑定患者,但是没绑定检查的 新来的检查->现在换成未提交的
|
//找到已绑定患者,但是没绑定检查的 新来的检查->现在换成未提交的
|
||||||
|
@ -1216,7 +1216,7 @@ namespace IRaCIS.Application.Services
|
||||||
/// <param name="inCommand"></param>
|
/// <param name="inCommand"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> AddSubjectPatientBinding(AddSubjectPatientCommand inCommand, [FromServices] IRepository<SubjectVisit> _subjectVisitReposiotry)
|
public async Task<IResponseOutput> AddSubjectPatientBinding(AddSubjectPatientCommand inCommand, [FromServices] IRepository<SubjectVisit> _subjectVisitReposiotry)
|
||||||
{
|
{
|
||||||
|
@ -1317,7 +1317,7 @@ namespace IRaCIS.Application.Services
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpDelete]
|
[HttpDelete]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
public async Task<IResponseOutput> DeleteSubjectPatientBinding(DeleteSubejctPatientCommand inCommand,
|
public async Task<IResponseOutput> DeleteSubjectPatientBinding(DeleteSubejctPatientCommand inCommand,
|
||||||
|
|
||||||
|
|
||||||
|
@ -1432,7 +1432,7 @@ namespace IRaCIS.Application.Services
|
||||||
/// <param name="inCommandList"></param>
|
/// <param name="inCommandList"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
[Obsolete]
|
[Obsolete]
|
||||||
public async Task<IResponseOutput> AddSubjectPatientStudyBinding(List<AddSubjectPatientStudyVisitCommand> inCommandList)
|
public async Task<IResponseOutput> AddSubjectPatientStudyBinding(List<AddSubjectPatientStudyVisitCommand> inCommandList)
|
||||||
|
@ -1506,7 +1506,7 @@ namespace IRaCIS.Application.Services
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="inCommand"></param>
|
/// <param name="inCommand"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> UpdateSubjectVisitStudyBinding(UpdateSubjectVisitStudyBindingCommand inCommand)
|
public async Task<IResponseOutput> UpdateSubjectVisitStudyBinding(UpdateSubjectVisitStudyBindingCommand inCommand)
|
||||||
{
|
{
|
||||||
|
@ -1674,7 +1674,7 @@ namespace IRaCIS.Application.Services
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
public async Task<IResponseOutput> SubmitVisitStudyBinding(SubmitVisitStudyBindingCommand inCommand,
|
public async Task<IResponseOutput> SubmitVisitStudyBinding(SubmitVisitStudyBindingCommand inCommand,
|
||||||
[FromServices] IOptionsMonitor<ServiceVerifyConfigOption> _basicSystemConfigConfig,
|
[FromServices] IOptionsMonitor<ServiceVerifyConfigOption> _basicSystemConfigConfig,
|
||||||
[FromServices] IRepository _repository
|
[FromServices] IRepository _repository
|
||||||
|
@ -1967,7 +1967,7 @@ namespace IRaCIS.Application.Services
|
||||||
/// <param name="_subjectVisitReposiotry"></param>
|
/// <param name="_subjectVisitReposiotry"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
public async Task<IResponseOutput> AddOrUpdateSubjectVisit(AddOrUpdateSubjectVisitCommand incommand, [FromServices] IRepository<SubjectVisit> _subjectVisitReposiotry)
|
public async Task<IResponseOutput> AddOrUpdateSubjectVisit(AddOrUpdateSubjectVisitCommand incommand, [FromServices] IRepository<SubjectVisit> _subjectVisitReposiotry)
|
||||||
{
|
{
|
||||||
//var verifyExp1 = new EntityVerifyExp<SubjectVisit>()
|
//var verifyExp1 = new EntityVerifyExp<SubjectVisit>()
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
using AutoMapper;
|
||||||
|
using EntityFrameworkCore.Triggered;
|
||||||
|
using IRaCIS.Core.Application.Service;
|
||||||
|
using IRaCIS.Core.Application.ViewModel;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Triggers
|
||||||
|
{
|
||||||
|
|
||||||
|
public class VisitTaskIAfterSignTrigger : IAfterSaveTrigger<VisitTask>,IBeforeSaveTrigger<VisitTask>
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly IRepository<VisitTask> _visitTaskRepository;
|
||||||
|
|
||||||
|
private readonly IRepository<Subject> _subjectRepository;
|
||||||
|
private readonly IUserInfo _userInfo;
|
||||||
|
private readonly IEmailSendService _emailSendService;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public VisitTaskIAfterSignTrigger(IRepository<VisitTask> visitTaskRepository, IRepository<Subject> subjectRepository, IUserInfo userInfo, IEmailSendService emailSendService)
|
||||||
|
{
|
||||||
|
_visitTaskRepository = visitTaskRepository;
|
||||||
|
_subjectRepository = subjectRepository;
|
||||||
|
_userInfo = userInfo;
|
||||||
|
|
||||||
|
_emailSendService = emailSendService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task AfterSave(ITriggerContext<VisitTask> context, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var visitTask = context.Entity;
|
||||||
|
|
||||||
|
|
||||||
|
if (visitTask.SignTime != null && visitTask.ReadingTaskState == ReadingTaskState.HaveSigned )
|
||||||
|
{
|
||||||
|
//任务阅片完成 自动释放
|
||||||
|
await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.SubjectId == visitTask.SubjectId && t.TrialReadingCriterionId==visitTask.TrialReadingCriterionId && t.ReadingTaskState != ReadingTaskState.HaveSigned, t=>new VisitTask() { SubjectCriterionClaimUserId=null});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task BeforeSave(ITriggerContext<VisitTask> context, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var visitTask = context.Entity;
|
||||||
|
|
||||||
|
if (context.ChangeType == ChangeType.Modified && visitTask.ReadingTaskState==ReadingTaskState.HaveSigned && visitTask.ReadingTaskState != context.UnmodifiedEntity.ReadingTaskState && _userInfo.UserTypeEnumInt == (int)UserTypeEnum.PI)
|
||||||
|
{
|
||||||
|
visitTask.PIAuditState = PIAuditState.PIAgree;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.ChangeType == ChangeType.Modified && visitTask.PIAuditState!=context.UnmodifiedEntity.PIAuditState && visitTask.PIAuditState==PIAuditState.PIAgree)
|
||||||
|
{
|
||||||
|
await _emailSendService.SendPIAuditResultAsync(visitTask.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,6 +54,10 @@ namespace IRaCIS.Core.Domain.Share
|
||||||
|
|
||||||
QCToCRCImageQuestion = 7,
|
QCToCRCImageQuestion = 7,
|
||||||
|
|
||||||
|
ClinicalDataQuestion = 8,
|
||||||
|
|
||||||
|
PIAuditResutl = 9,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MFALogin = 10,
|
MFALogin = 10,
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
// 此代码由T4模板自动生成 byzhouhang 20210918
|
||||||
|
// 生成时间 2023-08-22 16:56:15
|
||||||
|
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
||||||
|
using System;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
namespace IRaCIS.Core.Domain.Models
|
||||||
|
{
|
||||||
|
|
||||||
|
[Table("SubjectVisitClinicalDialog")]
|
||||||
|
public class SubjectVisitClinicalDialog : BaseAddAuditEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SubjectVisitId
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public Guid SubjectVisitId { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Content
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public string Content { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -123,6 +123,63 @@ namespace IRaCIS.Core.Infra.EFCore.Common
|
||||||
public async Task InsertAddEntitys(List<EntityEntry> entitys)
|
public async Task InsertAddEntitys(List<EntityEntry> entitys)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region HIR
|
||||||
|
#region PI 审核
|
||||||
|
|
||||||
|
foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(PIAudit)))
|
||||||
|
{
|
||||||
|
var type = GetEntityAuditOpt(item);
|
||||||
|
|
||||||
|
var entity = item.Entity as PIAudit;
|
||||||
|
|
||||||
|
await InsertInspection<PIAudit>(item.Entity as PIAudit, type, x => new InspectionConvertDTO()
|
||||||
|
{
|
||||||
|
ObjectRelationParentId = entity.VisitTaskId,
|
||||||
|
IsDistinctionInterface = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(SubjectVisitClinicalDialog)))
|
||||||
|
{
|
||||||
|
var type = GetEntityAuditOpt(item);
|
||||||
|
|
||||||
|
var entity = item.Entity as SubjectVisitClinicalDialog;
|
||||||
|
|
||||||
|
await InsertInspection<SubjectVisitClinicalDialog>(item.Entity as SubjectVisitClinicalDialog, type, x => new InspectionConvertDTO()
|
||||||
|
{
|
||||||
|
ObjectRelationParentId = entity.SubjectVisitId,
|
||||||
|
IsDistinctionInterface = false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 受试者关联
|
||||||
|
foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(SubjectPatient)))
|
||||||
|
{
|
||||||
|
var type = GetEntityAuditOpt(item);
|
||||||
|
|
||||||
|
var entity = item.Entity as SubjectPatient;
|
||||||
|
|
||||||
|
var sCPPatient = await _dbContext.SCPPatient.AsNoTracking().Where(x => x.Id == entity.PatientId).FirstOrDefaultAsync();
|
||||||
|
var subjectCode = await _dbContext.Subject.AsNoTracking().Where(x => x.Id == entity.SubjectId).Select(x => x.Code).FirstOrDefaultAsync();
|
||||||
|
await InsertInspection<SubjectPatient>(entity, type, x => new InspectionConvertDTO()
|
||||||
|
{
|
||||||
|
SubjectId = x.SubjectId,
|
||||||
|
|
||||||
|
//项目的信息 找离的最近的项目稽查信息
|
||||||
|
ObjectRelationParentId = entity.SubjectId,
|
||||||
|
ObjectRelationParentId2 = entity.PatientId,
|
||||||
|
}, new
|
||||||
|
{
|
||||||
|
SubjectCode = subjectCode,
|
||||||
|
PatientName = sCPPatient.PatientName,
|
||||||
|
PatientIdStr = sCPPatient.PatientIdStr,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
#endregion
|
||||||
|
|
||||||
// 项目
|
// 项目
|
||||||
foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(Trial)))
|
foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(Trial)))
|
||||||
{
|
{
|
||||||
|
|
|
@ -589,7 +589,7 @@ public class IRaCISDBContext : DbContext
|
||||||
|
|
||||||
public virtual DbSet<SCPStudySubjectVisit> SCPStudySubjectVisit { get; set; }
|
public virtual DbSet<SCPStudySubjectVisit> SCPStudySubjectVisit { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<SubjectVisitClinicalDialog> SubjectVisitClinicalDialog { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
18645
IRaCIS.Core.Infra.EFCore/Migrations/20241112075526_AddSecond.Designer.cs
generated
Normal file
18645
IRaCIS.Core.Infra.EFCore/Migrations/20241112075526_AddSecond.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,48 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Infra.EFCore.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddSecond : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SubjectVisitClinicalDialog",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
SubjectVisitId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
Content = table.Column<string>(type: "nvarchar(400)", maxLength: 400, nullable: false),
|
||||||
|
CreateUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
CreateTime = table.Column<DateTime>(type: "datetime2", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SubjectVisitClinicalDialog", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_SubjectVisitClinicalDialog_User_CreateUserId",
|
||||||
|
column: x => x.CreateUserId,
|
||||||
|
principalTable: "User",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_SubjectVisitClinicalDialog_CreateUserId",
|
||||||
|
table: "SubjectVisitClinicalDialog",
|
||||||
|
column: "CreateUserId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SubjectVisitClinicalDialog");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8925,6 +8925,32 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IRaCIS.Core.Domain.Models.SubjectVisitClinicalDialog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Content")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(400)
|
||||||
|
.HasColumnType("nvarchar(400)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreateTime")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid>("CreateUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("SubjectVisitId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreateUserId");
|
||||||
|
|
||||||
|
b.ToTable("SubjectVisitClinicalDialog");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("IRaCIS.Core.Domain.Models.SystemAnonymization", b =>
|
modelBuilder.Entity("IRaCIS.Core.Domain.Models.SystemAnonymization", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
|
@ -16594,6 +16620,17 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
|
||||||
b.Navigation("TrialSite");
|
b.Navigation("TrialSite");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IRaCIS.Core.Domain.Models.SubjectVisitClinicalDialog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IRaCIS.Core.Domain.Models.User", "CreateUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CreateUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("CreateUser");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("IRaCIS.Core.Domain.Models.SystemAnonymization", b =>
|
modelBuilder.Entity("IRaCIS.Core.Domain.Models.SystemAnonymization", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("IRaCIS.Core.Domain.Models.User", "CreateUser")
|
b.HasOne("IRaCIS.Core.Domain.Models.User", "CreateUser")
|
||||||
|
|
Loading…
Reference in New Issue