From 75bf9b394b841069a0451ecceac48efce189f4cf Mon Sep 17 00:00:00 2001
From: hang <872297557@qq.com>
Date: Tue, 12 Nov 2024 16:50:28 +0800
Subject: [PATCH] =?UTF-8?q?=E8=BF=81=E7=A7=BBHIR=20=20=E5=88=9D=E6=AD=A5?=
=?UTF-8?q?=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
IRC.Core.SCP/Service/CStoreSCPService.cs | 144 +-
IRC.Core.SCP/Service/DicomArchiveService.cs | 41 +-
.../Service/Interface/IDicomArchiveService.cs | 2 +-
IRC.Core.SCP/Service/PatientStudyService.cs | 249 +
IRC.Core.SCP/appsettings.Test_HIR_SCP.json | 79 +
.../Properties/launchSettings.json | 43 +-
.../ServiceCollectionSetup.cs | 2 +
IRaCIS.Core.API/appsettings.Test_HIR.json | 77 +
.../TrialGlobalLimitActionFilter.cs | 89 +-
.../LegacyController/TrialResourceFilter.cs | 196 -
.../TrialGlobalLimitEndpointFilter.cs | 89 +-
IRaCIS.Core.Application/Helper/CacheHelper.cs | 9 +-
.../IRaCIS.Core.Application.xml | 28 +-
.../Allocation/DTO/VisitTaskViewModel.cs | 133 +
.../Service/Allocation/VisitTaskService.cs | 232 +
.../Service/Allocation/_MapConfig.cs | 16 +
.../Service/Document/EmailSendService.cs | 75 +-
.../Service/Management/UserService.cs | 65 +-
.../ReadingMedicineQuestionService.cs | 2 +-
.../TrialSiteUser/DTO/TrialConfigDTO.cs | 5 +
.../TrialSiteUser/TrialConfigService.cs | 86 +-
.../Service/Visit/PatientService.cs | 20 +-
.../VisitTaskIAfterSignTrigger.cs | 62 +
.../Common/EmailScenarioEnum.cs | 4 +
.../HIR/SubjectVisitClinicalDialog.cs | 32 +
.../Common/AuditingData.cs | 57 +
.../Context/IRaCISDBContext.cs | 2 +-
.../20241112075526_AddSecond.Designer.cs | 18645 ++++++++++++++++
.../Migrations/20241112075526_AddSecond.cs | 48 +
.../IRaCISDBContextModelSnapshot.cs | 37 +
30 files changed, 20148 insertions(+), 421 deletions(-)
create mode 100644 IRC.Core.SCP/Service/PatientStudyService.cs
create mode 100644 IRC.Core.SCP/appsettings.Test_HIR_SCP.json
create mode 100644 IRaCIS.Core.API/appsettings.Test_HIR.json
delete mode 100644 IRaCIS.Core.Application/BusinessFilter/LegacyController/TrialResourceFilter.cs
create mode 100644 IRaCIS.Core.Application/Triggers/AfterSaveTrigger/VisitTaskIAfterSignTrigger.cs
create mode 100644 IRaCIS.Core.Domain/HIR/SubjectVisitClinicalDialog.cs
create mode 100644 IRaCIS.Core.Infra.EFCore/Migrations/20241112075526_AddSecond.Designer.cs
create mode 100644 IRaCIS.Core.Infra.EFCore/Migrations/20241112075526_AddSecond.cs
diff --git a/IRC.Core.SCP/Service/CStoreSCPService.cs b/IRC.Core.SCP/Service/CStoreSCPService.cs
index f13f63b78..aa426a049 100644
--- a/IRC.Core.SCP/Service/CStoreSCPService.cs
+++ b/IRC.Core.SCP/Service/CStoreSCPService.cs
@@ -24,7 +24,39 @@ using IRaCIS.Core.Infrastructure;
namespace IRaCIS.Core.SCP.Service
{
+ ///
+ /// 后台托管服务的方式运行
+ ///
+ //public class CStoreSCPHostedService : IHostedService
+ //{
+ // private readonly ILogger _logger;
+ // private readonly IDicomServerFactory _dicomServerFactory;
+ // private IDicomServer? _server;
+ // public CStoreSCPHostedService(ILogger 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(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 List CalledAEList { get; set; }
@@ -43,11 +75,6 @@ namespace IRaCIS.Core.SCP.Service
private SCPImageUpload _upload { get; set; }
- private Guid _trialId { get; set; }
-
- private Guid _trialSiteId { get; set; }
-
-
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
{
@@ -58,21 +85,21 @@ namespace IRaCIS.Core.SCP.Service
private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[]
{
- // Lossless
- DicomTransferSyntax.JPEGLSLossless, //1.2.840.10008.1.2.4.80
- DicomTransferSyntax.JPEG2000Lossless, //1.2.840.10008.1.2.4.90
- DicomTransferSyntax.JPEGProcess14SV1, //1.2.840.10008.1.2.4.70
- DicomTransferSyntax.JPEGProcess14, //1.2.840.10008.1.2.4.57 JPEG Lossless, Non-Hierarchical (Process 14)
- DicomTransferSyntax.RLELossless, //1.2.840.10008.1.2.5
+ // Lossless
+ DicomTransferSyntax.JPEGLSLossless,
+ DicomTransferSyntax.JPEG2000Lossless,
+ DicomTransferSyntax.JPEGProcess14SV1,
+ DicomTransferSyntax.JPEGProcess14,
+ DicomTransferSyntax.RLELossless,
// Lossy
- DicomTransferSyntax.JPEGLSNearLossless,//1.2.840.10008.1.2.4.81"
- DicomTransferSyntax.JPEG2000Lossy, //1.2.840.10008.1.2.4.91
- DicomTransferSyntax.JPEGProcess1, //1.2.840.10008.1.2.4.50
- DicomTransferSyntax.JPEGProcess2_4, //1.2.840.10008.1.2.4.51
+ DicomTransferSyntax.JPEGLSNearLossless,
+ DicomTransferSyntax.JPEG2000Lossy,
+ DicomTransferSyntax.JPEGProcess1,
+ DicomTransferSyntax.JPEGProcess2_4,
// Uncompressed
- DicomTransferSyntax.ExplicitVRLittleEndian, //1.2.840.10008.1.2.1
- DicomTransferSyntax.ExplicitVRBigEndian, //1.2.840.10008.1.2.2
- DicomTransferSyntax.ImplicitVRLittleEndian //1.2.840.10008.1.2
+ DicomTransferSyntax.ExplicitVRLittleEndian,
+ DicomTransferSyntax.ExplicitVRBigEndian,
+ DicomTransferSyntax.ImplicitVRLittleEndian
};
@@ -94,48 +121,18 @@ namespace IRaCIS.Core.SCP.Service
_serviceProvider = (IServiceProvider)this.UserState;
+ var option = _serviceProvider.GetService>().CurrentValue;
- var _trialDicomAERepository = _serviceProvider.GetService>();
+ var calledAEList = option.CalledAEList;
- var trialDicomAEList = _trialDicomAERepository.Select(t => new { t.CalledAE, t.TrialId }).ToList();
- var trialCalledAEList = trialDicomAEList.Select(t => t.CalledAE).ToList();
+ if (!calledAEList.Contains(association.CalledAE))
- Log.Logger.Information("当前系统配置:", string.Join('|', trialDicomAEList));
-
- var findCalledAE = trialDicomAEList.Where(t => t.CalledAE == association.CalledAE).FirstOrDefault();
-
- var isCanReceiveIamge = false;
-
- if (findCalledAE != null)
- {
- _trialId = findCalledAE.TrialId;
-
- var _trialSiteDicomAERepository = _serviceProvider.GetService>();
-
-
- 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)
+ //if (association.CalledAE != "STORESCP")
{
- Log.Logger.Warning($"拒绝CallingAE:{association.CallingAE} CalledAE:{association.CalledAE}的连接");
+ Log.Logger.Warning($"拒绝CalledAE:{association.CalledAE}的连接");
return SendAssociationRejectAsync(
DicomRejectResult.Permanent,
@@ -155,8 +152,6 @@ namespace IRaCIS.Core.SCP.Service
}
}
-
-
return SendAssociationAcceptAsync(association);
}
@@ -172,15 +167,7 @@ namespace IRaCIS.Core.SCP.Service
_upload.EndTime = DateTime.Now;
_upload.StudyCount = _SCPStudyIdList.Count;
-
- await _SCPImageUploadRepository.AddAsync(_upload, true);
-
-
- var _studyRepository = _serviceProvider.GetService>();
- //将检查设置为传输结束
- await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
-
- await _studyRepository.SaveChangesAndClearAllTrackingAsync();
+ await _SCPImageUploadRepository.AddAsync(_upload,true);
await SendAssociationReleaseResponseAsync();
}
@@ -188,9 +175,11 @@ namespace IRaCIS.Core.SCP.Service
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();
+ await patientStudyService.AutoBindingPatientStudyVisitAsync(_SCPStudyIdList);
//处理检查Modality
var _dictionaryRepository = _serviceProvider.GetService>();
@@ -242,11 +231,11 @@ namespace IRaCIS.Core.SCP.Service
//奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致
if (exception == null)
{
- //var _studyRepository = _serviceProvider.GetService>();
- ////将检查设置为传输结束
- //await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
+ var _studyRepository = _serviceProvider.GetService>();
+ //将检查设置为传输结束
+ 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}");
@@ -262,9 +251,8 @@ namespace IRaCIS.Core.SCP.Service
string seriesInstanceUid = request.Dataset.GetString(DicomTag.SeriesInstanceUID);
string sopInstanceUid = request.Dataset.GetString(DicomTag.SOPInstanceUID);
- //Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
- Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, _trialId.ToString());
- Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, _trialId.ToString());
+ Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid);
+ Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid);
var ossService = _serviceProvider.GetService();
@@ -274,7 +262,7 @@ namespace IRaCIS.Core.SCP.Service
var _distributedLockProvider = _serviceProvider.GetService();
var storeRelativePath = string.Empty;
- var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}{studyInstanceUid}";
+ var ossFolderPath = $"Dicom/{studyInstanceUid}";
long fileSize = 0;
@@ -307,7 +295,7 @@ namespace IRaCIS.Core.SCP.Service
{
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))
{
@@ -336,10 +324,14 @@ namespace IRaCIS.Core.SCP.Service
series.ImageResizePath = seriesPath;
+
+
+ //await _seriesRepository.BatchUpdateNoTrackingAsync(t => t.Id == seriesId, u => new SCPSeries() { ImageResizePath = seriesPath });
+
+
}
}
-
await _seriesRepository.SaveChangesAsync();
}
catch (Exception ex)
@@ -355,7 +347,7 @@ namespace IRaCIS.Core.SCP.Service
//监控信息设置
_upload.FileCount++;
- _upload.FileSize = _upload.FileSize + fileSize;
+ _upload.FileSize= _upload.FileSize+ fileSize;
return new DicomCStoreResponse(request, DicomStatus.Success);
}
diff --git a/IRC.Core.SCP/Service/DicomArchiveService.cs b/IRC.Core.SCP/Service/DicomArchiveService.cs
index a79319afd..3a6de7459 100644
--- a/IRC.Core.SCP/Service/DicomArchiveService.cs
+++ b/IRC.Core.SCP/Service/DicomArchiveService.cs
@@ -11,7 +11,6 @@ using FellowOakDicom.Network;
using IRaCIS.Core.SCP.Service;
using IRaCIS.Core.Infra.EFCore;
using MassTransit;
-using System.Runtime.Intrinsics.X86;
using Serilog.Sinks.File;
namespace IRaCIS.Core.SCP.Service
@@ -52,18 +51,18 @@ namespace IRaCIS.Core.SCP.Service
///
///
///
- public async Task ArchiveDicomFileAsync(DicomDataset dataset, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
+ public async Task ArchiveDicomFileAsync(DicomDataset dataset, string fileRelativePath, string callingAE, string calledAE, long fileSize)
{
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
- string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID,string.Empty);
+ string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
- Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString());
- Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString());
- Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString());
+ Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid);
+ Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid);
+ Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid);
var isStudyNeedAdd = false;
var isSeriesNeedAdd = false;
@@ -74,11 +73,12 @@ namespace IRaCIS.Core.SCP.Service
//using (@lock.Acquire())
{
- var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr /*&& t.TrialSiteId==trialSiteId*/ );
- var findStudy = await _studyRepository.FirstOrDefaultAsync(t=>t.Id== studyId);
- var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
+ var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr);
+ var findStudy = await _studyRepository.FirstOrDefaultAsync(t => t.Id == studyId);
+ var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == instanceId);
+
DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue(DicomTag.StudyTime).TimeOfDay);
//先传输了修改了患者编号的,又传输了没有修改患者编号的,导致后传输的没有修改患者编号的下面的检查为0
@@ -90,8 +90,6 @@ namespace IRaCIS.Core.SCP.Service
findPatient = new SCPPatient()
{
Id = NewId.NextSequentialGuid(),
- //TrialId=trialId,
- //TrialSiteId=trialSiteId,
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
@@ -117,20 +115,6 @@ namespace IRaCIS.Core.SCP.Service
{
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
{
@@ -163,8 +147,6 @@ namespace IRaCIS.Core.SCP.Service
PatientId = findPatient.Id,
Id = studyId,
- //TrialId = trialId,
- //TrialSiteId = trialSiteId,
StudyInstanceUid = studyInstanceUid,
StudyTime = studyTime,
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
@@ -273,8 +255,7 @@ namespace IRaCIS.Core.SCP.Service
Path = fileRelativePath,
- FileSize= fileSize,
-
+ FileSize = fileSize,
};
++findStudy.InstanceCount;
@@ -304,7 +285,7 @@ namespace IRaCIS.Core.SCP.Service
}
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();
diff --git a/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs b/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs
index 99312ef4f..031dcbd89 100644
--- a/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs
+++ b/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs
@@ -5,7 +5,7 @@ namespace IRaCIS.Core.SCP.Service
{
public interface IDicomArchiveService
{
- Task ArchiveDicomFileAsync(DicomDataset dicomDataset,Guid trialId,Guid trialSiteId, string fileRelativePath,string callingAE,string calledAE,long fileSize);
+ Task ArchiveDicomFileAsync(DicomDataset dicomDataset,string fileRelativePath,string callingAE,string calledAE,long fileSize);
}
}
diff --git a/IRC.Core.SCP/Service/PatientStudyService.cs b/IRC.Core.SCP/Service/PatientStudyService.cs
new file mode 100644
index 000000000..f41366145
--- /dev/null
+++ b/IRC.Core.SCP/Service/PatientStudyService.cs
@@ -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 AutoBindingPatientStudyVisitAsync(List scpStudyIdList);
+ }
+
+ [ApiExplorerSettings(GroupName = "Trial")]
+ public class PatientStudyService : BaseService, IPatientStudyService
+ {
+ private readonly IRepository _studySubjectVisitRepository;
+ private readonly IRepository _subjectPatientRepository;
+ private readonly IRepository _trialRepository;
+ private readonly IRepository _patientRepository;
+ private readonly IRepository _studyRepository;
+ private readonly IRepository _subjectRepository;
+ private readonly IRepository _subjectVisitRepository;
+ private readonly IDistributedLockProvider _distributedLockProvider;
+
+ public PatientStudyService(IRepository studySubjectVisitRepository, IRepository studyRepository, IRepository subjectPatientRepository, IRepository trialRepository, IRepository patientRepository, IRepository subjectRepository, IRepository 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 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();
+ }
+
+
+ ///
+ /// 传输完成后,自动给检查绑定访视
+ ///
+ ///
+ ///
+ [HttpPost]
+ public async Task AutoBindingPatientStudyVisitAsync(List 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();
+
+ }
+
+ }
+}
diff --git a/IRC.Core.SCP/appsettings.Test_HIR_SCP.json b/IRC.Core.SCP/appsettings.Test_HIR_SCP.json
new file mode 100644
index 000000000..f85eeb533
--- /dev/null
+++ b/IRC.Core.SCP/appsettings.Test_HIR_SCP.json
@@ -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": "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",
+ "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
+ }
+}
diff --git a/IRaCIS.Core.API/Properties/launchSettings.json b/IRaCIS.Core.API/Properties/launchSettings.json
index 89bfb6def..39b9a3a33 100644
--- a/IRaCIS.Core.API/Properties/launchSettings.json
+++ b/IRaCIS.Core.API/Properties/launchSettings.json
@@ -24,52 +24,15 @@
},
"applicationUrl": "http://localhost:6100"
},
- "IRaCIS.Test_IRC_PGSQL": {
+ "IRaCIS.HIR_IRC": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Test_IRC_PGSQL"
- },
- "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"
+ "ASPNETCORE_ENVIRONMENT": "Test_HIR"
},
"applicationUrl": "http://localhost:6100"
}
+
}
}
\ No newline at end of file
diff --git a/IRaCIS.Core.API/_ServiceExtensions/ServiceCollectionSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/ServiceCollectionSetup.cs
index 7541ecf25..9a1d84cba 100644
--- a/IRaCIS.Core.API/_ServiceExtensions/ServiceCollectionSetup.cs
+++ b/IRaCIS.Core.API/_ServiceExtensions/ServiceCollectionSetup.cs
@@ -1,5 +1,6 @@
using IP2Region.Net.Abstractions;
using IP2Region.Net.XDB;
+using IRaCIS.Application.Contracts;
using IRaCIS.Core.Application.BackGroundJob;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.Service;
@@ -30,6 +31,7 @@ public static class ServiceCollectionSetup
services.AddOptions().Configure(_configuration.GetSection("EncrypteResponseConfig"));
services.AddOptions().Configure(_configuration.GetSection("SystemPacsConfig"));
services.Configure(_configuration.GetSection("IRaCISBasicConfig"));
+ services.AddOptions().Configure(_configuration.GetSection("SystemHospitalConfig"));
services.Configure(_configuration.GetSection("BasicSystemConfig"));
diff --git a/IRaCIS.Core.API/appsettings.Test_HIR.json b/IRaCIS.Core.API/appsettings.Test_HIR.json
new file mode 100644
index 000000000..632677775
--- /dev/null
+++ b/IRaCIS.Core.API/appsettings.Test_HIR.json
@@ -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": "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",
+ "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"
+ }
+}
\ No newline at end of file
diff --git a/IRaCIS.Core.Application/BusinessFilter/LegacyController/TrialGlobalLimitActionFilter.cs b/IRaCIS.Core.Application/BusinessFilter/LegacyController/TrialGlobalLimitActionFilter.cs
index 8d24d2591..7df51b758 100644
--- a/IRaCIS.Core.Application/BusinessFilter/LegacyController/TrialGlobalLimitActionFilter.cs
+++ b/IRaCIS.Core.Application/BusinessFilter/LegacyController/TrialGlobalLimitActionFilter.cs
@@ -1,8 +1,11 @@
-using IRaCIS.Core.Application.BusinessFilter;
+using IRaCIS.Application.Contracts;
+using IRaCIS.Core.Application.BusinessFilter;
using IRaCIS.Core.Application.Helper;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -13,7 +16,9 @@ using static IRaCIS.Core.Domain.Share.StaticData;
namespace IRaCIS.Core.Application.Filter;
-public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository _trialRepository) : IAsyncActionFilter
+public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository _trialRepository,
+ IOptionsMonitor _basicSystemConfigConfig,
+ IOptionsMonitor _hospitalOption, IStringLocalizer _localizer) : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
@@ -113,9 +118,9 @@ public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _
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不对
@@ -145,22 +150,78 @@ public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _
//通过path 或者body 找到trialId 了
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;
- // 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
- if (trialStatusStr == StaticData.TrialState.TrialOngoing || optActions.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
+ if (trialInfo != null)
{
+ trialStatusStr = trialInfo.TrialStatusStr;
- await next();
+ 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(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)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
+ if (trialStatusStr == StaticData.TrialState.TrialOngoing || optActions.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
+ {
+
+ await next();
+
+ }
+ // 项目停止、或者完成 不允许操作
+ else
+ {
+ //---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
+ context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_InterceptedProjectStatusRule")));
+
+ }
}
- // 项目停止、或者完成 不允许操作
- else
- {
- //---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
- context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_InterceptedProjectStatusRule")));
- }
}
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
diff --git a/IRaCIS.Core.Application/BusinessFilter/LegacyController/TrialResourceFilter.cs b/IRaCIS.Core.Application/BusinessFilter/LegacyController/TrialResourceFilter.cs
deleted file mode 100644
index d05790c3c..000000000
--- a/IRaCIS.Core.Application/BusinessFilter/LegacyController/TrialResourceFilter.cs
+++ /dev/null
@@ -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;
-
-///
-/// 主要为了 处理项目结束 锁库,不允许操作
-///
-public class TrialResourceFilter : Attribute, IAsyncResourceFilter
-{
- private readonly IUserInfo _userInfo;
- private readonly IFusionCache _fusionCache;
- public IStringLocalizer _localizer;
- private readonly IRepository _trialRepository;
- private readonly List _trialOptList = new List();
-
-
- public TrialResourceFilter(IFusionCache fusionCache, IRepository 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"]));
- }
-
-
- }
-}
diff --git a/IRaCIS.Core.Application/BusinessFilter/MinimalAPI/TrialGlobalLimitEndpointFilter.cs b/IRaCIS.Core.Application/BusinessFilter/MinimalAPI/TrialGlobalLimitEndpointFilter.cs
index 6648db25e..7f31cbf42 100644
--- a/IRaCIS.Core.Application/BusinessFilter/MinimalAPI/TrialGlobalLimitEndpointFilter.cs
+++ b/IRaCIS.Core.Application/BusinessFilter/MinimalAPI/TrialGlobalLimitEndpointFilter.cs
@@ -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.Mvc;
+using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -24,7 +27,9 @@ public class TrialGlobalLimitAttribute : Attribute
-public class TrialGlobalLimitEndpointFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository _trialRepository) : IEndpointFilter
+public class TrialGlobalLimitEndpointFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository _trialRepository,
+ IOptionsMonitor _basicSystemConfigConfig,
+ IOptionsMonitor _hospitalOption, IStringLocalizer _localizer) : IEndpointFilter
{
public async ValueTask