diff --git a/IRC.Core.SCP/IRC.Core.SCP.csproj b/IRC.Core.SCP/IRC.Core.SCP.csproj
index 084654c25..999a17361 100644
--- a/IRC.Core.SCP/IRC.Core.SCP.csproj
+++ b/IRC.Core.SCP/IRC.Core.SCP.csproj
@@ -19,7 +19,7 @@
-
+
true
diff --git a/IRC.Core.SCP/Service/CStoreSCPService.cs b/IRC.Core.SCP/Service/CStoreSCPService.cs
index bb9d2571f..6ecaeae28 100644
--- a/IRC.Core.SCP/Service/CStoreSCPService.cs
+++ b/IRC.Core.SCP/Service/CStoreSCPService.cs
@@ -22,6 +22,7 @@ using SharpCompress.Common;
using SixLabors.ImageSharp.Formats.Jpeg;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Extention;
+using FellowOakDicom.IO.Buffer;
namespace IRaCIS.Core.SCP.Service
{
@@ -40,7 +41,9 @@ namespace IRaCIS.Core.SCP.Service
{
private IServiceProvider _serviceProvider { get; set; }
- private List _SCPStudyIdList { get; set; } = new List();
+ private List _SCPStudyIdList => _ImageUploadList.Where(t => t.SCPStudyId != Guid.Empty).Select(t => t.SCPStudyId).ToList();
+
+ private List _ImageUploadList { get; set; } = new List();
private SCPImageUpload _upload { get; set; }
@@ -163,27 +166,48 @@ namespace IRaCIS.Core.SCP.Service
public async Task OnReceiveAssociationReleaseRequestAsync()
{
- await DataMaintenanceAsaync();
+ var _distributedLockProvider = _serviceProvider.GetService();
+ var @lock = _distributedLockProvider.CreateLock($"{_upload.CallingAE}");
+
+ using (await @lock.AcquireAsync())
+ {
+
+ await DataMaintenanceAsaync();
+
+ //记录监控
+
+ await AddUploadLogAsync();
+
+
+
+ var _studyRepository = _serviceProvider.GetService>();
+ //将检查设置为传输结束
+ await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
+
+ await _studyRepository.SaveChangesAndClearAllTrackingAsync();
+
+ }
+
+ await SendAssociationReleaseResponseAsync();
+
+ }
+
+ private async Task AddUploadLogAsync()
+ {
//记录监控
var _SCPImageUploadRepository = _serviceProvider.GetService>();
_upload.EndTime = DateTime.Now;
- _upload.StudyCount = _SCPStudyIdList.Count;
+ _upload.StudyCount = _ImageUploadList.Count;
_upload.TrialId = _trialId;
_upload.TrialSiteId = _trialSiteId;
- await _SCPImageUploadRepository.AddAsync(_upload, true);
+ _upload.UploadJsonStr = (new SCPImageLog() { UploadList = _ImageUploadList }).ToJsonStr();
-
- var _studyRepository = _serviceProvider.GetService>();
- //将检查设置为传输结束
- await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
-
- await _studyRepository.SaveChangesAndClearAllTrackingAsync();
-
- await SendAssociationReleaseResponseAsync();
+ //可能是测试echo 导致记录了
+ await _SCPImageUploadRepository.AddAsync(_upload, _upload.FileCount > 0 ? true : false);
}
@@ -249,6 +273,11 @@ namespace IRaCIS.Core.SCP.Service
//await _studyRepository.SaveChangesAndClearAllTrackingAsync();
}
+ else
+ {
+ //记录日志
+ await AddUploadLogAsync();
+ }
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
}
@@ -259,17 +288,24 @@ namespace IRaCIS.Core.SCP.Service
public async Task OnCStoreRequestAsync(DicomCStoreRequest request)
{
- string studyInstanceUid = request.Dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID,string.Empty);
+ string studyInstanceUid = request.Dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty);
string seriesInstanceUid = request.Dataset.GetSingleValueOrDefault(DicomTag.SeriesInstanceUID, string.Empty);
string sopInstanceUid = request.Dataset.GetSingleValueOrDefault(DicomTag.SOPInstanceUID, string.Empty);
+ string patientIdStr = request.Dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
- if(studyInstanceUid.IsNullOrEmpty() || seriesInstanceUid.IsNullOrEmpty() || sopInstanceUid.IsNullOrEmpty())
+ if (studyInstanceUid.IsNullOrEmpty() || seriesInstanceUid.IsNullOrEmpty() || sopInstanceUid.IsNullOrEmpty())
{
Log.Logger.Error($"接收数据读取StudyInstanceUID:{studyInstanceUid}、SeriesInstanceUID:{seriesInstanceUid}、SOPInstanceUID:{sopInstanceUid}有空 ");
return new DicomCStoreResponse(request, DicomStatus.Success);
}
+ //确保来了影像集合存在
+ if (!_ImageUploadList.Any(t => t.StudyInstanceUid == studyInstanceUid))
+ {
+ _ImageUploadList.Add(new ImageUploadInfo() { StudyInstanceUid = studyInstanceUid });
+ }
+
//Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, _trialId.ToString());
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, _trialId.ToString());
@@ -288,11 +324,70 @@ namespace IRaCIS.Core.SCP.Service
long fileSize = 0;
try
{
-
using (MemoryStream ms = new MemoryStream())
{
await request.File.SaveAsync(ms);
+ #region 1帧拆成多个固定大小的,方便移动端浏览
+
+ // 回到开头,读取 dicom
+ ms.Position = 0;
+ var dicomFile = DicomFile.Open(ms);
+
+ var pixelData = DicomPixelData.Create(dicomFile.Dataset);
+ var syntax = pixelData.Syntax;
+
+ // 每个 fragment 固定大小 (64KB 示例,可以自己调整)
+ int fragmentSize = 20 * 1024;
+
+ if (syntax.IsEncapsulated)
+ {
+ var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
+
+ for (int n = 0; n < pixelData.NumberOfFrames; n++)
+ {
+ var frameData = pixelData.GetFrame(n); // 获取完整一帧
+ var data = frameData.Data;
+ int offset = 0;
+
+ while (offset < data.Length)
+ {
+ int size = Math.Min(fragmentSize, data.Length - offset);
+ var buffer = new byte[size];
+ Buffer.BlockCopy(data, offset, buffer, 0, size);
+
+ newFragments.Fragments.Add(new MemoryByteBuffer(buffer));
+ offset += size;
+ }
+ }
+
+ // 替换原 PixelData
+ dicomFile.Dataset.AddOrUpdate(newFragments);
+
+
+ // 重新保存 dicom 到流
+ ms.SetLength(0);
+ dicomFile.Save(ms);
+ }
+
+
+ ms.Position = 0;
+
+ #endregion
+
+
+ #region 本地测试
+ //// --- 保存到本地文件测试 ---
+ //var localPath = @"D:\TestDicom.dcm";
+ //using (var fs = new FileStream(localPath, FileMode.Create, FileAccess.Write))
+ //{
+ // ms.CopyTo(fs);
+ //}
+ //return new DicomCStoreResponse(request, DicomStatus.Success);
+
+ #endregion
+
+
//irc 从路径最后一截取Guid
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
@@ -315,12 +410,8 @@ 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.File, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
- if (!_SCPStudyIdList.Contains(scpStudyId))
- {
- _SCPStudyIdList.Add(scpStudyId);
- }
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
@@ -349,12 +440,40 @@ namespace IRaCIS.Core.SCP.Service
await _seriesRepository.SaveChangesAsync();
+
+ if (_ImageUploadList.Any(t => t.StudyInstanceUid == studyInstanceUid))
+ {
+ var find = _ImageUploadList.FirstOrDefault(t => t.StudyInstanceUid.Equals(studyInstanceUid));
+
+
+ find.SuccessImageCount++;
+
+ if (!find.PatientNameList.Any(t => t == patientIdStr) && patientIdStr.IsNotNullOrEmpty())
+ {
+ find.PatientNameList.Add(patientIdStr);
+ }
+
+ //首次 (默认是Guid 空,数据库归档出了Id)
+ if (find.SCPStudyId != scpStudyId)
+ {
+ find.SCPStudyId = scpStudyId;
+
+ }
+ }
}
catch (Exception ex)
{
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}");
+ if (_ImageUploadList.Any(t => t.StudyInstanceUid == studyInstanceUid))
+ {
+ var find = _ImageUploadList.FirstOrDefault(t => t.StudyInstanceUid.Equals(studyInstanceUid));
+
+ find.FailedImageCount++;
+
+ }
+
}
}
diff --git a/IRC.Core.SCP/Service/DicomArchiveService.cs b/IRC.Core.SCP/Service/DicomArchiveService.cs
index e522f6690..fcda120b0 100644
--- a/IRC.Core.SCP/Service/DicomArchiveService.cs
+++ b/IRC.Core.SCP/Service/DicomArchiveService.cs
@@ -13,6 +13,7 @@ using IRaCIS.Core.Infra.EFCore;
using MassTransit;
using System.Runtime.Intrinsics.X86;
using Serilog.Sinks.File;
+using IRaCIS.Core.Infrastructure.Extention;
namespace IRaCIS.Core.SCP.Service
{
@@ -52,8 +53,10 @@ 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(DicomFile dicomFile, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
{
+ var dataset = dicomFile.Dataset;
+
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
@@ -82,7 +85,7 @@ namespace IRaCIS.Core.SCP.Service
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
- if (findPatient == null && findStudy==null)
+ if (findPatient == null /*&& findStudy==null*/)
{
isPatientNeedAdd = true;
@@ -151,6 +154,32 @@ namespace IRaCIS.Core.SCP.Service
}
findPatient.LatestPushTime = DateTime.Now;
+ findPatient.PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
+ findPatient.PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty);
+ findPatient.PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty);
+ findPatient.PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty);
+
+ if (findPatient.PatientBirthDate.Length == 8)
+ {
+ var birthDateStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}-{findPatient.PatientBirthDate[4]}{findPatient.PatientBirthDate[5]}-{findPatient.PatientBirthDate[6]}{findPatient.PatientBirthDate[7]}";
+
+ var yearStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}";
+
+ int year = 0;
+
+ var canParse = int.TryParse(yearStr, out year);
+
+ if (canParse && year > 1900)
+ {
+ findPatient.PatientBirthDate = birthDateStr;
+
+ }
+ else
+ {
+ findPatient.PatientBirthDate = string.Empty;
+ }
+
+ }
}
if (findStudy == null)
@@ -167,6 +196,9 @@ namespace IRaCIS.Core.SCP.Service
TrialSiteId = trialSiteId,
StudyInstanceUid = studyInstanceUid,
StudyTime = studyTime,
+ DicomStudyDate = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty),
+ DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty),
+
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
//ModalityForEdit = modalityForEdit,
Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
@@ -201,6 +233,19 @@ namespace IRaCIS.Core.SCP.Service
findStudy.PatientBirthDate = $"{findStudy.PatientBirthDate[0]}{findStudy.PatientBirthDate[1]}{findStudy.PatientBirthDate[2]}{findStudy.PatientBirthDate[3]}-{findStudy.PatientBirthDate[4]}{findStudy.PatientBirthDate[5]}-{findStudy.PatientBirthDate[6]}{findStudy.PatientBirthDate[7]}";
}
}
+ else
+ {
+ findStudy.DicomStudyDate = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty);
+ findStudy.DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty);
+ findStudy.CalledAE = calledAE;
+ findStudy.CallingAE = callingAE;
+ findStudy.PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
+ findStudy.PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty);
+ findStudy.PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty);
+ findStudy.UpdateTime = DateTime.Now;
+
+ await _patientRepository.BatchUpdateNoTrackingAsync(t => t.Id == findStudy.PatientId, u => new SCPPatient() { LatestPushTime = DateTime.Now });
+ }
if (findSerice == null)
@@ -218,6 +263,9 @@ namespace IRaCIS.Core.SCP.Service
//SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay),
//SeriesTime = DateTime.TryParse(dataset.GetSingleValue(DicomTag.SeriesDate) + dataset.GetSingleValue(DicomTag.SeriesTime), out DateTime dt) ? dt : null,
SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue(DicomTag.SeriesDate).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue(DicomTag.SeriesTime).TimeOfDay),
+ DicomSeriesDate = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty),
+ DicomSeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty),
+
Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
@@ -239,7 +287,20 @@ namespace IRaCIS.Core.SCP.Service
++findStudy.SeriesCount;
}
+ else
+ {
+ findSerice.DicomSeriesDate = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty);
+ findSerice.DicomSeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty);
+ findSerice.UpdateTime = DateTime.Now;
+ }
+ var transferSyntaxUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
+
+ var isEncapsulated = false;
+ if (transferSyntaxUID.IsNotNullOrEmpty())
+ {
+ isEncapsulated = DicomTransferSyntax.Lookup(DicomUID.Parse(transferSyntaxUID)).IsEncapsulated;
+ }
if (findInstance == null)
{
@@ -280,6 +341,15 @@ namespace IRaCIS.Core.SCP.Service
++findStudy.InstanceCount;
++findSerice.InstanceCount;
}
+ else
+ {
+ findInstance.SOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
+ findInstance.MediaStorageSOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
+ findInstance.TransferSytaxUID = transferSyntaxUID;
+ findInstance.MediaStorageSOPInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
+ findInstance.IsEncapsulated = isEncapsulated;
+ findInstance.UpdateTime = DateTime.Now;
+ }
if (isPatientNeedAdd)
{
diff --git a/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs b/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs
index 99312ef4f..5adef58ca 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(DicomFile dicomFile, Guid trialId,Guid trialSiteId, string fileRelativePath,string callingAE,string calledAE,long fileSize);
}
}
diff --git a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/JSONTimeZoneConverter.cs b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/JSONTimeZoneConverter.cs
index 1ae4adfd2..9b289e8a4 100644
--- a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/JSONTimeZoneConverter.cs
+++ b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/JSONTimeZoneConverter.cs
@@ -126,6 +126,51 @@ namespace IRaCIS.Core.API
}
+ public class DateOnlyUniversalJsonConverter : JsonConverter
+ {
+ private readonly string _format;
+
+ public DateOnlyUniversalJsonConverter(string format = "yyyy-MM-dd")
+ {
+ _format = format;
+ }
+
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(DateOnly) || objectType == typeof(DateOnly?);
+ }
+
+ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
+ {
+ if (value == null)
+ {
+ writer.WriteValue(""); // null -> 空字符串
+ return;
+ }
+
+ var date = (DateOnly)value;
+ if (date == default)
+ {
+ writer.WriteValue(""); // default(DateOnly) -> 空字符串
+ }
+ else
+ {
+ writer.WriteValue(date.ToString(_format));
+ }
+ }
+
+ public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
+ {
+ var str = reader.TokenType == JsonToken.Null ? null : reader.Value?.ToString();
+
+ if (string.IsNullOrWhiteSpace(str) || !DateOnly.TryParse(str, out var date))
+ {
+ return objectType == typeof(DateOnly?) ? null : default(DateOnly);
+ }
+
+ return date;
+ }
+ }
#region 废弃
diff --git a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NewtonsoftJsonSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NewtonsoftJsonSetup.cs
index 147472270..79d96c953 100644
--- a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NewtonsoftJsonSetup.cs
+++ b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NewtonsoftJsonSetup.cs
@@ -49,6 +49,8 @@ namespace IRaCIS.Core.API
//必须放在后面
options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService());
+ //options.SerializerSettings.Converters.Add(new DateOnlyUniversalJsonConverter("yyyy-MM-dd"));
+
})
.AddControllersAsServices()//动态webApi属性注入需要
diff --git a/IRaCIS.Core.Application/Helper/OSSService.cs b/IRaCIS.Core.Application/Helper/OSSService.cs
index 4322c6573..cad0084a6 100644
--- a/IRaCIS.Core.Application/Helper/OSSService.cs
+++ b/IRaCIS.Core.Application/Helper/OSSService.cs
@@ -294,7 +294,7 @@ public class OSSService : IOSSService
GetObjectStoreTempToken();
}
//token 过期了
- if (AliyunOSSTempToken.Expiration.AddSeconds(10) <= DateTime.Now)
+ if (AliyunOSSTempToken?.Expiration.AddSeconds(10) <= DateTime.Now)
{
GetObjectStoreTempToken();
}
diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.csproj b/IRaCIS.Core.Application/IRaCIS.Core.Application.csproj
index 2bdee0a79..790c4f8ed 100644
--- a/IRaCIS.Core.Application/IRaCIS.Core.Application.csproj
+++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.csproj
@@ -41,11 +41,11 @@
-
+
-
+
@@ -53,7 +53,7 @@
-
+
@@ -62,9 +62,9 @@
-
+
-
+
diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml
index 9caaa29f0..cd592f3a8 100644
--- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml
+++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml
@@ -957,16 +957,6 @@
-
-
- 一致性分析结果导出 7 8 分别是自身 和组件一致性
-
-
-
-
-
-
-
获取阅片标准可以导出的列表
@@ -1126,6 +1116,14 @@
+
+
+
+ 下载已经删除的影像
+
+
+
+
读取该项目的数据,进行维护
@@ -1142,7 +1140,7 @@
-
+
单个文件接收 归档
@@ -1493,19 +1491,92 @@
-
+
稽查文档
-
+
- 获取稽查文档
+ 查看授权时间内的稽查 (admin qa 看所有 EA只看到自己参与的)
+
+
+ 稽查记录 列表
+
+
+
+
+
+
+ 删除稽查记录
+
+
+
+
+
+
+ 设置稽查记录某个文件 或者某个文件夹授权
+
+
+
+
+
+
+ 插入闭包表关系
+
+
+
+
+ 获取文件树形结构 (传Id 根节点就是自己)
+
+
+
+
+
+
+ 设置是否授权
+
+
+
+
+
+
+ 删除稽查文档
+
+
+
+
+
+
+ 把历史版本设置为当前版本--修改 维护闭包表,之间的闭包关系指向新版本文件
+
+
+
+
+
+ 获取面包屑导航 (查询自己的祖先,不包括自己)
+
+
+
+
+
+
+ 移动文件或者文件夹 到其他文件夹
+
+
+
+
+
+ 复制文件或者文件夹
+
+
+
+
修改稽查文档
@@ -1523,7 +1594,7 @@
- 新增稽查文档
+ 新增稽查文档 (批量上传文件时才是数组,文件夹时单个对象)
@@ -1534,39 +1605,13 @@
-
+
- 获取面包屑导航
+ 获取稽查文档
-
-
-
-
-
- 获取文件树形结构 (传Id 根节点就是自己)
-
-
-
-
-
-
- 删除稽查文档
-
-
-
-
-
-
- 移动文件或者文件夹 到其他文件夹
-
-
-
-
-
- 复制文件或者文件夹
-
-
+
+
@@ -1575,19 +1620,6 @@
-
-
- 把历史版本设置为当前版本
-
-
-
-
-
- 设置是否授权
-
-
-
-
系统文件类型
@@ -2801,7 +2833,7 @@
-
+
获取用户列表
@@ -8722,11 +8754,6 @@
截图地址
-
-
- 第一次添加的任务ID
-
-
CreateUserId
@@ -9137,6 +9164,11 @@
修约小数点
+
+
+ 是否查看检查部位
+
+
标准类型
@@ -12178,6 +12210,11 @@
是否是系统数据
+
+
+ 是否查看检查部位
+
+
任务类型
@@ -13101,7 +13138,7 @@
-
+
IR影像阅片
@@ -13113,6 +13150,13 @@
+
+
+ 查看检查部位
+
+
+
+
删除单个表格问题标记
@@ -14275,6 +14319,14 @@
+
+
+ 重建闭包表
+
+
+
+
+
清理一致性分析任务
diff --git a/IRaCIS.Core.Application/MassTransit/Recurring/TrialDocumentConsumer.cs b/IRaCIS.Core.Application/MassTransit/Recurring/TrialDocumentConsumer.cs
index 955ffb18a..1002094bd 100644
--- a/IRaCIS.Core.Application/MassTransit/Recurring/TrialDocumentConsumer.cs
+++ b/IRaCIS.Core.Application/MassTransit/Recurring/TrialDocumentConsumer.cs
@@ -9,6 +9,7 @@ using MassTransit;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MimeKit;
+using NPOI.SS.Formula.Functions;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -57,17 +58,15 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
join trialIdentityUser in _trialIdentityUserRepository.Where(x => x.IsDeleted == false) on trialDoc.TrialId equals trialIdentityUser.TrialId
join trialUserRole in _trialUserRoleRepository.Where(x => x.IsDeleted == false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
- from identityUser in _identityUserRepository.AsQueryable(false)
-
- .Where(t => t.Status == UserStateEnum.Enable &&
- t.Id == trialIdentityUser.IdentityUserId &&
- t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
- .Any(t => trialDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
+ join identityUser in _identityUserRepository.AsQueryable(false).Where(u => u.Status == UserStateEnum.Enable)
+ on trialIdentityUser.IdentityUserId equals identityUser.Id
+
join confirm in _trialDocConfirmedUserRepository.Where() on
new { trialIdentityUser.IdentityUserId, TrialDocumentId = trialDoc.Id } equals new { IdentityUserId = confirm.ConfirmUserId, confirm.TrialDocumentId } into cc
from confirm in cc.DefaultIfEmpty()
+ where trialIdentityUser.TrialUserRoleList.Any(ur => !ur.IsDeleted && trialDoc.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == ur.UserRole.UserTypeId))
select new TrialSignDocView()
{
TrialCode = trialDoc.Trial.TrialCode,
@@ -191,12 +190,10 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
from trialDoc in _trialDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
join trialIdentityUser in _trialIdentityUserRepository.Where(x=>x.IsDeleted==false) on trialDoc.TrialId equals trialIdentityUser.TrialId
join trialUserRole in _trialUserRoleRepository.Where(x => x.IsDeleted == false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
- from identityUser in _identityUserRepository.AsQueryable(false)
- .Where(t => t.Status == UserStateEnum.Enable &&
- t.Id == trialIdentityUser.IdentityUserId &&
- t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
- .Any(t => context.Message.NewUserTypeIds.Contains(t.UserTypeId) &&
- trialDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
+ join identityUser in _identityUserRepository.AsQueryable(false).Where(u => u.Status == UserStateEnum.Enable)
+ on trialIdentityUser.IdentityUserId equals identityUser.Id
+ where trialIdentityUser.TrialUserRoleList.Any(ur => !ur.IsDeleted && context.Message.NewUserTypeIds.Contains(ur.UserRole.UserTypeId) && trialDoc.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == ur.UserRole.UserTypeId))
+
select new UnionDocumentWithConfirmInfoView()
{
IsSystemDoc = true,
@@ -222,11 +219,11 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
from trialDoc in _trialDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
join trialIdentityUser in _trialIdentityUserRepository.Where(x => x.IsDeleted == false) on trialDoc.TrialId equals trialIdentityUser.TrialId
join trialUserRole in _trialUserRoleRepository.Where(x=>x.IsDeleted==false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
- from identityUser in _identityUserRepository.AsQueryable(false)
- .Where(t => t.Status == UserStateEnum.Enable &&
- t.Id== trialIdentityUser.IdentityUserId&&
- t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
- .Any(t => trialDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
+ join identityUser in _identityUserRepository.AsQueryable(false).Where(u => u.Status == UserStateEnum.Enable)
+ on trialIdentityUser.IdentityUserId equals identityUser.Id
+ where trialIdentityUser.TrialUserRoleList.Any(ur => !ur.IsDeleted &&trialDoc.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == ur.UserRole.UserTypeId))
+
+
select new UnionDocumentWithConfirmInfoView()
{
IsSystemDoc = false,
diff --git a/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs
index 1d5e9aec2..f2e60d207 100644
--- a/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs
+++ b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs
@@ -135,7 +135,7 @@ namespace IRaCIS.Core.Application.Service.Common
QuestionAnswerList = g.Select(t => new QCQuestionAnswerExport() { Answer = t.Answer, QuestionName = t.QuesitonName, ShowOrder = t.ShowOrder, QuestionId = t.QuestionId }).OrderBy(t => t.ShowOrder).ToList()
- }).OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitNum).ThenBy(t=>t.CurrentQCEnum).ThenBy(t=>t.AuditTime).ToList();
+ }).OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitNum).ThenBy(t => t.CurrentQCEnum).ThenBy(t => t.AuditTime).ToList();
var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException();
@@ -2310,263 +2310,8 @@ namespace IRaCIS.Core.Application.Service.Common
#region 通用阅片结果导出
- ///
- /// 一致性分析结果导出 7 8 分别是自身 和组件一致性
- ///
- ///
- ///
- ///
- ///
- ///
- [HttpPost]
- public async Task GetAnalysisTaskList_Export(VisitTaskQuery inQuery,
- [FromServices] IRepository _commonDocumentRepository,
- [FromServices] IDictionaryService _dictionaryService,
- [FromServices] IRepository _trialRepository)
- {
- //每次查询必须是单标准的
- var criterion = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == inQuery.TrialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionName, t.ArbitrationRule }).FirstNotNullAsync();
- var query = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && t.TaskAllocationState == TaskAllocationState.Allocated && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze))
-
- //一致性分析
- .WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults, t => t.IsSelfAnalysis == true || t.IsSelfAnalysis == null)
- .WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults, t => t.IsSelfAnalysis == null ? t.Subject.SubjectVisitTaskList.Any(u => u.IsSelfAnalysis == true && u.VisitTaskNum == t.VisitTaskNum && u.DoctorUserId == t.DoctorUserId && u.TrialReadingCriterionId == t.TrialReadingCriterionId) : true)
- .WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfInterReaderAnalysisResults, t => t.IsSelfAnalysis == false || t.IsSelfAnalysis == null)
-
- //访视和全局查询已签名完成的,裁判可以是未签名,未完成的
- .Where(t => (t.ReadingTaskState == ReadingTaskState.HaveSigned && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global)) || t.ReadingCategory == ReadingCategory.Judge)
- .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId)
- .WhereIf(inQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == inQuery.TrialSiteId)
-
- .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.ArmEnum != null, t => t.ArmEnum == inQuery.ArmEnum)
- .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))
- .WhereIf(inQuery.BeginAllocateDate != null, t => t.AllocateTime > inQuery.BeginAllocateDate)
- .WhereIf(inQuery.EndAllocateDate != null, t => t.AllocateTime < inQuery.EndAllocateDate);
-
- var list = await query.ProjectTo(_mapper.ConfigurationProvider,
- new
- {
- readingExportType = inQuery.ReadingExportType,
- criterionType = criterion.CriterionType,
- trialReadingCriterionId = inQuery.TrialReadingCriterionId,
- isEn_Us = _userInfo.IsEn_Us
- }).ToListAsync();
-
- list = list.OrderBy(t => t.SubjectCode).ThenBy(t => t.ArmEnum).ThenBy(t => t.VisitTaskNum).ToList();
-
-
- var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException();
- exportInfo.CriterionName = criterion.CriterionName;
-
- #region 处理系统标准存在疾病和整体肿瘤合并
-
- //如果是以合并后的找翻译字典,会少,所以必须放在前面
- var translateDicNameList = list.SelectMany(t => t.QuestionAnswerList).Where(t => t.TranslateDicName.IsNotNullOrEmpty()).Select(t => t.TranslateDicName).Distinct().ToList();
-
- //针对1.1 整体肿瘤评估 有的两列要合并一列
- if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.RECIST1Pointt1_MB || criterion.CriterionType == CriterionType.IRECIST1Point1)
- {
- foreach (var item in list)
- {
- //处理合并表头
-
- var questionType = item.IsBaseline == true ? QuestionType.ExistDisease : QuestionType.Tumor;
-
- var findItem = item.QuestionAnswerList.Where(t => t.QuestionType == questionType).FirstOrDefault();
-
- if (findItem != null)
- {
- findItem.QuestionName = _userInfo.IsEn_Us ? "Overall Response" : "整体肿瘤评估";
- }
-
-
- if (item.IsBaseline == true)
- {
- item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.Tumor).ToList();
- }
- else
- {
- item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.ExistDisease).ToList();
- }
- }
- }
- else if (criterion.CriterionType == CriterionType.Lugano2014 || criterion.CriterionType == CriterionType.Lugano2014WithoutPET)
- {
-
- foreach (var item in list)
- {
- //处理合并表头
-
- var questionType = item.IsBaseline == true ? QuestionType.ExistDisease : QuestionType.ImgOncology;
-
- var findItem = item.QuestionAnswerList.Where(t => t.QuestionType == questionType).FirstOrDefault();
-
- if (findItem != null)
- {
- findItem.QuestionName = _userInfo.IsEn_Us ? "Overall Response" : "整体肿瘤评估";
- }
-
- if (item.IsBaseline == true)
- {
- item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.ImgOncology).ToList();
- }
- else
- {
- item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.ExistDisease).ToList();
- }
- }
- }
- else if (criterion.CriterionType == CriterionType.PCWG3)
- {
-
- }
- else if (criterion.CriterionType == CriterionType.SelfDefine)
- {
- //自定义的又问题名称重复 所以统一加上组名
-
- //有重复的就加,没有重复的就不加
- if (list.Any(t => t.QuestionAnswerList.Select(t => t.QuestionName).Count() != t.QuestionAnswerList.Select(t => t.QuestionName).Distinct().Count()))
- {
- foreach (var item in list)
- {
- foreach (var qs in item.QuestionAnswerList)
- {
- qs.QuestionName = qs.Group + "_" + qs.QuestionName;
- }
- }
- }
-
-
- }
-
- #endregion
-
- var export_Template = StaticData.Export.TrialSelfAnalysisList_Export;
-
- #region 自身一致性分析和组间一致性分析
-
- if (inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults)
- {
- //找到非一致性分析的任务
- var selfExportList = list.Where(t => t.IsSelfAnalysis == null).ToList();
-
- //处理一致性分析结果是否和原始阅片是否一致
- foreach (var item in selfExportList)
- {
- //找到一致性分析的结果
- var selfAnalysisTask = list.Where(t => t.IsSelfAnalysis == true && t.SubjectCode == item.SubjectCode && t.VisitTaskNum == item.VisitTaskNum && t.TaskName == t.TaskName && t.UserName == item.UserName).FirstOrDefault();
-
- //将自身一致性分析的字段 赋值到访视任务这个字段
- item.IsAnalysisDiffToOriginalData = selfAnalysisTask?.IsAnalysisDiffToOriginalData;
-
- //处理再次阅片人的结果
- if (selfAnalysisTask != null)
- {
- var cloneQuestionAnswerList = selfAnalysisTask.QuestionAnswerList.Clone();
-
- foreach (var qItem in cloneQuestionAnswerList)
- {
- qItem.QuestionName = qItem.QuestionName + $"{(_userInfo.IsEn_Us ? "(Again)" : "(再次)")}";
- }
-
- item.QuestionAnswerList = item.QuestionAnswerList.Union(cloneQuestionAnswerList).ToList();
- }
- }
-
- list = selfExportList;
- }
- else
- {
- export_Template = StaticData.Export.TrialGroupAnalysisList_Export;
-
- var newList = new List();
-
- foreach (var group in list.GroupBy(t => new { t.SubjectCode, t.VisitTaskNum, t.TaskName }).OrderBy(g => g.Key.SubjectCode).ThenBy(g => g.Key.VisitTaskNum))
- {
- var subjectVisitGroupList = group.ToList();
-
-
- //找到当前访视组间一致性分析的任务结果
-
- var groupOtherTaskList = subjectVisitGroupList.Where(t => t.IsSelfAnalysis == false).ToList();
-
- foreach (var subjectVisitTaskArm in subjectVisitGroupList.Where(t => t.IsSelfAnalysis == null).OrderBy(t => t.ArmEnum))
- {
- foreach (var otherTask in groupOtherTaskList)
- {
- //非一致性分析任务
- var cloneObj = subjectVisitTaskArm.Clone();
-
- var otherTaskQuestionAnserList = otherTask.QuestionAnswerList.Clone();
-
- foreach (var qItem in otherTaskQuestionAnserList)
- {
- qItem.QuestionName = qItem.QuestionName + $"{(_userInfo.IsEn_Us ? "(Again)" : "(再次)")}";
- }
-
- //处理 再次阅片人,再次阅片人角色 两列
- var addQuestionList = new List();
-
- addQuestionList.Add(new CommonQuesionInfo() { QuestionName = _userInfo.IsEn_Us ? "Reviwer(Again)" : "阅片人(再次)", QuestionValue = otherTask.UserName });
- addQuestionList.Add(new CommonQuesionInfo() { QuestionName = _userInfo.IsEn_Us ? "Reviwer Role(Again)" : "阅片人角色(再次)", QuestionValue = ((int)otherTask.ArmEnum).ToString(), TranslateDicName = "ArmEnum" });
-
-
- cloneObj.QuestionAnswerList = cloneObj.QuestionAnswerList.Union(addQuestionList).Union(otherTaskQuestionAnserList).ToList();
-
-
- cloneObj.IsGroupAnalysisDiffToOriginalData = cloneObj.ArmEnum == Arm.DoubleReadingArm1 ? otherTask.IsGroupDiffArm1 : otherTask.IsGroupDiffArm2;
-
- newList.Add(cloneObj);
- }
- }
- }
- translateDicNameList.Add("ArmEnum");
-
- list = newList;
-
- }
-
- #endregion
-
- var columNameList = list.SelectMany(t => t.QuestionAnswerList).Where(t => t.QuestionName.IsNotNullOrEmpty()).Select(t => t.QuestionName).Distinct().ToList();
-
- exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list.Where(t => t.ReadingCategory != ReadingCategory.Global).ToList(), _userInfo.TimeZoneId);
- exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId);
-
- var dynamicColumnConfig = new DynamicColumnConfig()
- {
- //可读的列表名行索引,不是{{}} 模板行索引
- AutoColumnTitleRowIndex = 2,
- AutoColumnStartIndex = 5,
- TempalteLastColumnIndex = 4,
- DynamicItemDicName = "TranslateDicName",
- DynamicItemValueName = "QuestionValue",
- DynamicItemTitleName = "QuestionName",
- DynamicListName = "QuestionAnswerList",
- RemoveColunmIndexList = new List() { },
- ColumnIdNameList = columNameList.Select(t => new DynamicColumnConfig.ColumItem() { Id = Guid.Empty, Name = t }).ToList(),
- TranslateDicNameList = translateDicNameList ?? new List()
- };
-
-
- var (memoryStream, fileName) = await ExcelExportHelper.DataExport_NpoiTestAsync(export_Template, exportInfo, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(AnalysisDynamicCommonExport), criterion.CriterionType, dynamicColumnConfig);
-
- return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
- {
- FileDownloadName = $"{exportInfo.ResearchProgramNo}_{exportInfo.CriterionName}_{fileName}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx"
- };
-
- }
-
///
/// 获取阅片标准可以导出的列表
@@ -2897,7 +2642,8 @@ namespace IRaCIS.Core.Application.Service.Common
{
#region 外层问题处理
- if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.RECIST1Pointt1_MB || criterion.CriterionType == CriterionType.IRECIST1Point1)
+ if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.RECIST1Pointt1_MB
+ || criterion.CriterionType == CriterionType.IRECIST1Point1 || criterion.CriterionType == CriterionType.mRECISTHCC)
{
fistLeveLNameList = trialConfigQuestionList.Select(t => new DynamicColumnConfig.ColumItem()
@@ -2940,7 +2686,8 @@ namespace IRaCIS.Core.Application.Service.Common
var extralNameList = new List();
if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.RECIST1Pointt1_MB
- || criterion.CriterionType == CriterionType.IRECIST1Point1 || criterion.CriterionType == CriterionType.Lugano2014 || criterion.CriterionType == CriterionType.Lugano2014WithoutPET)
+ || criterion.CriterionType == CriterionType.IRECIST1Point1 || criterion.CriterionType == CriterionType.mRECISTHCC
+ || criterion.CriterionType == CriterionType.Lugano2014 || criterion.CriterionType == CriterionType.Lugano2014WithoutPET)
{
//if(inQuery.ReadingExportType == ExportResult.DetailedTableOfLesions)
@@ -2981,7 +2728,8 @@ namespace IRaCIS.Core.Application.Service.Common
var addLessionInfoList = new List();
if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.RECIST1Pointt1_MB
- || criterion.CriterionType == CriterionType.IRECIST1Point1 || criterion.CriterionType == CriterionType.Lugano2014 || criterion.CriterionType == CriterionType.Lugano2014WithoutPET)
+ || criterion.CriterionType == CriterionType.IRECIST1Point1 || criterion.CriterionType == CriterionType.mRECISTHCC
+ || criterion.CriterionType == CriterionType.Lugano2014 || criterion.CriterionType == CriterionType.Lugano2014WithoutPET)
{
//病灶编号 和病灶类型没有配置,但是需要有的
addLessionInfoList.Add(new CommonQuesionInfo() { QuestionName = _userInfo.IsEn_Us ? "Lesion ID" : "病灶编号", QuestionValue = lession.LessionCode });
@@ -3021,7 +2769,8 @@ namespace IRaCIS.Core.Application.Service.Common
#endregion
#region 不管是list 还是taskList 最终处理的数据都是list 处理好数据后合并
- if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.RECIST1Pointt1_MB || criterion.CriterionType == CriterionType.IRECIST1Point1)
+ if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.RECIST1Pointt1_MB
+ || criterion.CriterionType == CriterionType.IRECIST1Point1 || criterion.CriterionType == CriterionType.mRECISTHCC)
{
//针对1.1 整体肿瘤评估 有的两列要合并一列
foreach (var item in list)
diff --git a/IRaCIS.Core.Application/Service/Common/MailService.cs b/IRaCIS.Core.Application/Service/Common/MailService.cs
index d57f263ac..d91524a08 100644
--- a/IRaCIS.Core.Application/Service/Common/MailService.cs
+++ b/IRaCIS.Core.Application/Service/Common/MailService.cs
@@ -760,7 +760,7 @@ namespace IRaCIS.Core.Application.Service
if (feedBack.VisitTaskId != null)
{
- var emailType = await _dictionaryRepository.Where(t => t.Parent.Code == "Email_BusinessScenario" && t.ParentId != null && t.Code == ((int)EmailBusinessScenario.IRImageError).ToString()).Select(t => _userInfo.IsEn_Us ? t.Value : t.ValueCN).FirstOrDefaultAsync();
+ var emailType = await _dictionaryRepository.Where(t => t.Parent.Code == "FeedBackTypeToIR" && t.ParentId != null && t.Code == ((int)feedBack.QuestionType).ToString()).Select(t => _userInfo.IsEn_Us ? t.Value : t.ValueCN).FirstOrDefaultAsync();
var info = await _visitTaskRepository.Where(t => t.Id == feedBack.VisitTaskId).Select(t => new { t.Trial.ResearchProgramNo, t.Trial.TrialCode, SubejctCode = t.Subject.Code, t.SourceSubjectVisit.VisitName }).FirstNotNullAsync();
diff --git a/IRaCIS.Core.Application/Service/Common/TrialImageDownloadService.cs b/IRaCIS.Core.Application/Service/Common/TrialImageDownloadService.cs
index 17b95ecbd..462954b67 100644
--- a/IRaCIS.Core.Application/Service/Common/TrialImageDownloadService.cs
+++ b/IRaCIS.Core.Application/Service/Common/TrialImageDownloadService.cs
@@ -1513,6 +1513,1175 @@ namespace IRaCIS.Core.Application.Service
+ try
+ {
+ var copyResult = _ossClient.CopyObject(copyReq);
+ Console.WriteLine($"✅ 恢复成功: {prevVersion.Key}, 新版本ID={copyResult.VersionId}");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"❌ 恢复失败: {prevVersion.Key}, 错误: {ex.Message}");
+ }
+
+
+ }
+
+ return ResponseOutput.Ok();
+ }
+
+
+ ///
+ /// 维护dir 需求新增的字段
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task TrialImageAddExtralField(Guid trialId,
+ [FromServices] IRepository _instanceRepository,
+ [FromServices] IRepository _studyRepository,
+ [FromServices] IRepository _seriesRepository)
+ {
+ // UPDATE DicomStudy
+ //SET DicomStudyDate = CONVERT(char(8), StudyTime, 112), --yyyyMMdd
+ // DicomStudyTime = REPLACE(CONVERT(char(8), StudyTime, 108), ':', ''); --HHmmss
+ // where DicomStudyDate = ''
+
+
+ //instance 找到传输语法为空的,然后分组
+ var seriesList = _instanceRepository.Where(t => t.TrialId == trialId && t.TransferSytaxUID == "")
+ //按照序列 和 NumberOfFrames 分组
+ .GroupBy(t => new { t.NumberOfFrames, t.SeriesId })
+ // 每个分组 取数据最小的一条
+ .Select(g => new { g.Key.SeriesId, g.Key.NumberOfFrames, g.OrderBy(t => t.FileSize).First().Path }).ToList();
+
+ foreach (var item in seriesList)
+ {
+ var stream = await _oSSService.GetStreamFromOSSAsync(item.Path);
+
+ var dicomFile = DicomFile.Open(stream);
+
+ var pixelData = DicomPixelData.Create(dicomFile.Dataset);
+
+ //获取像素是否为封装形式
+ var syntax = dicomFile.Dataset.InternalTransferSyntax;
+
+ //读取需要维护的值
+ var transferSyntaxUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
+ var mediaStorageSOPClassUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
+ var mediaStorageSOPInstanceUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
+ var sOPClassUID = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
+
+ //维护序列层级四个字段 后再用sql 维护study series 时间拆分 和 MediaStorageSOPInstanceUID
+ await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.SeriesId == item.SeriesId, t => new DicomInstance()
+ {
+ IsEncapsulated = syntax.IsEncapsulated,
+ TransferSytaxUID = transferSyntaxUID,
+ MediaStorageSOPClassUID = mediaStorageSOPClassUID,
+ SOPClassUID = sOPClassUID,
+ });
+
+ }
+
+ return ResponseOutput.Ok();
+
+ }
+
+
+ ///
+ /// 下载已经删除的影像
+ ///
+ ///
+ ///
+ [HttpPost]
+ [AllowAnonymous]
+ public async Task DownloadDeleteTrialImage(Guid trialId)
+ {
+ trialId = Guid.Parse("01000000-ac13-0242-6397-08dcd2d2a091");
+
+ var downloadInfo = _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).Select(t => new
+ {
+ t.ResearchProgramNo,
+
+ VisitList = t.SubjectVisitList
+ .Select(sv => new
+ {
+ TrialSiteCode = sv.TrialSite.TrialSiteCode,
+ SubjectCode = sv.Subject.Code,
+ VisitName = sv.VisitName,
+ StudyList = sv.StudyList.Select(u => new
+ {
+ u.PatientId,
+ u.StudyTime,
+ u.StudyCode,
+
+ SeriesList = u.SeriesList.Select(z => new
+ {
+ z.Modality,
+ z.SeriesNumber,
+
+ InstancePathList = z.DicomInstanceList.Where(t => t.IsDeleted == true || t.DicomSerie.IsDeleted == true || t.IsReading == false || t.DicomSerie.IsReading == false).Select(k => new
+ {
+ k.Path,
+ k.FileSize
+ }).ToList()
+ })
+
+ }).ToList()
+ }).ToList()
+
+ }).FirstOrDefault();
+
+
+ var filesizes = downloadInfo.VisitList.SelectMany(t => t.StudyList).SelectMany(t => t.SeriesList).SelectMany(t => t.InstancePathList).Sum(t => t.FileSize);
+ var count2 = downloadInfo.VisitList.SelectMany(t => t.StudyList).SelectMany(t => t.SeriesList).SelectMany(t => t.InstancePathList).Count();
+
+ Console.WriteLine($"下载总数量:{count2},总大小{filesizes}");
+
+ if (downloadInfo != null)
+ {
+ var downloadJobs = new List<(string Path, Func Job)>();
+
+ var rootFolder = @"E:\DownloadImage";
+
+ //var rootFolder = FileStoreHelper.GetDonwnloadImageFolder(_hostEnvironment);
+
+ // 获取无效字符(系统定义的)
+ string invalidChars = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
+
+ // 用正则表达式替换所有非法字符为下划线或空字符
+ string pattern = $"[{Regex.Escape(invalidChars)}]";
+
+ var regexNo = Regex.Replace(downloadInfo.ResearchProgramNo, pattern, "_");
+
+ // 创建一个临时文件夹来存放文件
+ string trialFolderPath = Path.Combine(rootFolder, $"{regexNo}_{NewId.NextGuid()}");
+ Directory.CreateDirectory(trialFolderPath);
+
+ foreach (var visitItem in downloadInfo.VisitList)
+ {
+ if (visitItem.StudyList.Count() == 0)
+ {
+ continue;
+ }
+
+ #region 处理 中心,受试者dicom non-dicom 文件夹层级
+
+ //var siteFolderPath = Path.Combine(trialFolderPath, visitItem.TrialSiteCode);
+ //if (!Directory.Exists(siteFolderPath))
+ //{
+ // Directory.CreateDirectory(siteFolderPath);
+ //}
+
+ #endregion
+
+
+ foreach (var studyInfo in visitItem.StudyList)
+ {
+ // 遍历 Series
+ foreach (var seriesInfo in studyInfo.SeriesList)
+ {
+ string studyDicomFolderPath = Path.Combine(trialFolderPath, $"{visitItem.SubjectCode}_{visitItem.VisitName}", $"{studyInfo.StudyCode}_{studyInfo.StudyTime?.ToString("yyyy-MM-dd")}", $"{seriesInfo.SeriesNumber}");
+
+ // 创建 影像 文件夹
+ Directory.CreateDirectory(studyDicomFolderPath);
+
+ // 遍历 InstancePathList
+ foreach (var instanceInfo in seriesInfo.InstancePathList)
+ {
+ // 复制文件到相应的文件夹
+ string destinationPath = Path.Combine(studyDicomFolderPath, Path.GetFileName(instanceInfo.Path));
+
+
+ //加入到下载任务里
+ downloadJobs.Add((instanceInfo.Path, () => _oSSService.DownLoadFromOSSAsync(instanceInfo.Path, destinationPath)));
+
+ //下载到当前目录
+ //await _oSSService.DownLoadFromOSSAsync(instanceInfo.Path, destinationPath);
+ }
+ }
+
+
+ }
+ }
+
+ #region 异步方式处理
+
+ int totalCount = downloadJobs.Count;
+ int downloadedCount = 0;
+
+ // 在 trialFolderPath 下面放一个失败记录文件
+ string failedLogPath = Path.Combine(trialFolderPath, "failed_downloads.txt");
+
+ // 确保文件存在(如果之前有就清空)
+ File.WriteAllText(failedLogPath, "");
+
+ foreach (var job in downloadJobs)
+ {
+ try
+ {
+ await job.Job();
+ }
+ catch (Exception ex)
+ {
+
+
+ string errorMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 下载失败: {job.Path}, 错误: {ex.Message}\r\n";
+
+ Console.WriteLine(errorMessage);
+
+ await File.AppendAllTextAsync(failedLogPath, errorMessage);
+ }
+
+ downloadedCount++;
+
+ // 每处理50个,输出一次进度(或最后一个时也输出)
+ if (downloadedCount % 50 == 0 || downloadedCount == totalCount)
+ {
+ Console.WriteLine($"已下载 {downloadedCount} / {totalCount} 个文件,完成 {(downloadedCount * 100.0 / totalCount):F2}%");
+ }
+ }
+ #endregion
+
+ #region 多线程测试
+
+ //const int batchSize = 15;
+ //int totalCount = downloadJobs.Count;
+ //int downloadedCount = 0;
+
+ //for (int i = 0; i < downloadJobs.Count; i += batchSize)
+ //{
+ // var batch = downloadJobs.Skip(i).Take(batchSize).Select(job => job());
+
+ // await Task.WhenAll(batch);
+
+ // downloadedCount += batch.Count();
+
+ // Console.WriteLine($"已下载 {downloadedCount} / {totalCount} 个文件,完成 {(downloadedCount * 100.0 / totalCount):F2}%");
+ //}
+ #endregion
+
+ }
+
+ return ResponseOutput.Ok();
+
+
+ }
+
+
+ [AllowAnonymous]
+ public async Task ReadDicomDataWriteDB([FromServices] IRepository _instanceRepository)
+ {
+ var testPath = @"E:\WXT001";
+ var path = @"E:\WXT001";
+
+ var files = Directory.GetFiles(testPath, "*", SearchOption.AllDirectories)
+ // 只要没有后缀(Windows 显示类型是 .file)
+ .Where(f => string.IsNullOrEmpty(Path.GetExtension(f)))
+ .Where(f => Guid.TryParse(Path.GetFileNameWithoutExtension(f), out _))
+ .ToList();
+
+ Console.WriteLine($"找到 {files.Count} 个 DICOM 文件");
+
+ int total = files.Count;
+ int processed = 0;
+ double lastPercent = 0;
+
+ var options = new ParallelOptions { MaxDegreeOfParallelism = 12 };
+
+ // 输出文件路径
+ var outputFile = Path.Combine(@"D:\dicomWrite", $"{Guid.NewGuid()}_dicom_info.txt");
+
+ var outputErrorFile = Path.Combine(@"D:\dicomWrite", $"{Guid.NewGuid()}_dicom_info_error.txt");
+
+ // 用并发安全的写法(锁保护)
+ var fileLock = new object();
+
+ foreach (var file in files)
+ {
+ try
+ {
+ var id = Guid.Parse(Path.GetFileNameWithoutExtension(file));
+ var dicomFile = DicomFile.Open(file);
+
+ var dataset = dicomFile.Dataset;
+ var fileMeta = dicomFile.FileMetaInfo;
+ var syntax = dataset.InternalTransferSyntax;
+
+
+
+ //单位 设备 PatientId Visit 检查UId 帧数
+
+ var stationName = dataset.GetSingleValueOrDefault(DicomTag.StationName, string.Empty);
+
+ var institutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty);
+
+ var manufacturer = dataset.GetSingleValueOrDefault(DicomTag.Manufacturer, string.Empty);
+
+ //PatientID TrialCode_SubjectCode
+ //var patientID = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
+
+ //SubjectCode
+ var clinicalTrialSubjectID = dataset.GetSingleValueOrDefault(DicomTag.ClinicalTrialSubjectID, string.Empty);
+ //访视visitNum
+ var clinicalTrialTimePointID = dataset.GetSingleValueOrDefault(DicomTag.ClinicalTrialTimePointID, string.Empty);
+ var studyInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty);
+ var seriesInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.SeriesInstanceUID, string.Empty);
+ var sOPInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.SOPInstanceUID, string.Empty);
+
+ var numberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1);
+
+
+ // 传输语法
+ var transferSyntaxUID = fileMeta.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
+ var sOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
+ var mediaStorageSOPClassUID = fileMeta.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
+
+ var mediaStorageSOPInstanceUID = fileMeta.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
+
+
+ // 拼接一行 CSV 格式
+ var line = string.Join(",",
+ id,
+ stationName,
+ institutionName,
+ manufacturer,
+ clinicalTrialSubjectID,
+ clinicalTrialTimePointID,
+ studyInstanceUID,
+ seriesInstanceUID,
+ sOPInstanceUID,
+ numberOfFrames,
+ transferSyntaxUID,
+ sOPClassUID,
+ mediaStorageSOPClassUID,
+ mediaStorageSOPInstanceUID
+
+ );
+
+
+ await File.AppendAllTextAsync(outputFile, line + Environment.NewLine);
+
+ await _instanceRepository.BatchUpdateNoTrackingAsync(
+ t => t.Id == id,
+ t => new DicomInstance
+ {
+ IsEncapsulated = syntax.IsEncapsulated,
+ TransferSytaxUID = transferSyntaxUID,
+ MediaStorageSOPClassUID = mediaStorageSOPClassUID,
+ SOPClassUID = sOPClassUID,
+ MediaStorageSOPInstanceUID = mediaStorageSOPInstanceUID
+ }, false);
+ }
+ catch (Exception ex)
+ {
+ var errorMsg = $"{DateTime.Now}❌ {file} 解析失败: {ex.Message}";
+ Console.WriteLine(errorMsg);
+
+ await File.AppendAllTextAsync(outputErrorFile, errorMsg + Environment.NewLine);
+ }
+ finally
+ {
+ var done = Interlocked.Increment(ref processed);
+ double percent = done * 100.0 / total;
+
+ // 只在进度提升 >= 1% 时打印
+ if (percent - lastPercent >= 5.0 || done == total)
+ {
+ lastPercent = percent;
+ Console.WriteLine($"{DateTime.Now} 进度: {done}/{total} ({percent:F2}%)");
+ }
+ }
+
+ }
+
+
+
+
+
+
+ return ResponseOutput.Ok();
+ }
+
+ #region 维护已经下载本地的数据
+
+ [AllowAnonymous]
+ public async Task ReadExcelData([FromServices] IRepository _instanceRepository)
+ {
+ var rows = await MiniExcel.QueryAsync(@"C:\Users\hang\Desktop\维护数据读取.xlsx");
+
+ rows = rows.Where(t => !string.IsNullOrEmpty(t.InstanceId.ToString())).ToList();
+
+ int total = rows.Count();
+ int processed = 0;
+ double lastPercent = 0;
+
+ var outputErrorFile = Path.Combine(@"D:\dicomWrite", $"{Guid.NewGuid()}_dicom_info_error.txt");
+
+ foreach (var item in rows)
+ {
+
+
+ try
+ {
+ await _instanceRepository.BatchUpdateNoTrackingAsync(
+ t => t.Id == item.InstanceId,
+ t => new DicomInstance
+ {
+
+ IsEncapsulated = item.IsEncapsulated,
+ TransferSytaxUID = item.TransferSyntaxUID,
+ MediaStorageSOPClassUID = item.MediaStorageSOPClassUID,
+ SOPClassUID = item.SOPClassUID,
+ MediaStorageSOPInstanceUID = item.MediaStorageSOPInstanceUID
+ }, false);
+ }
+ catch (Exception ex)
+ {
+
+ var errorMsg = $"{item.InstanceId} {DateTime.Now} 更新失败: {ex.Message}";
+ Console.WriteLine(errorMsg);
+
+ await File.AppendAllTextAsync(outputErrorFile, errorMsg + Environment.NewLine);
+ }
+ finally
+ {
+ processed++;
+ double percent = processed * 100.0 / total;
+
+ // 每提升 5% 或完成时输出
+ if (percent - lastPercent >= 2.0 || processed == total)
+ {
+ lastPercent = percent;
+ Console.WriteLine($"{DateTime.Now} 进度: {processed}/{total} ({percent:F2}%)");
+ }
+ }
+
+ }
+
+
+
+
+ return ResponseOutput.Ok();
+ }
+
+ public class DicomSOPInfo
+ {
+ public Guid InstanceId { get; set; }
+
+
+ public string TransferSyntaxUID { get; set; }
+
+ public string SOPClassUID { get; set; }
+
+ public string MediaStorageSOPClassUID { get; set; }
+
+ public string MediaStorageSOPInstanceUID { get; set; }
+
+ public bool IsEncapsulated => DicomTransferSyntax.Lookup(DicomUID.Parse(TransferSyntaxUID)).IsEncapsulated;
+
+
+
+ }
+
+
+ [AllowAnonymous]
+ public async Task ReadExcelImageDataInstanceIsReading([FromServices] IRepository _instanceRepository,
+ [FromServices] IRepository _seriesRepository,
+ [FromServices] IRepository _studyRepository)
+ {
+
+ var trialId = Guid.Parse("01000000-ac13-0242-6397-08dcd2d2a091");
+
+ var rows = await MiniExcel.QueryAsync(@"C:\Users\hang\Desktop\instanceReading.xlsx");
+
+ rows = rows.Where(t => !string.IsNullOrEmpty(t.SopInstanceUid) && t.SopInstanceUid.Length > 15).ToList();
+
+ var outputErrorFile = Path.Combine(@"D:\dicomWrite", $"{Guid.NewGuid()}_dicom_info_error.txt");
+
+
+ //foreach (var batch in rows.Chunk(20))
+ //{
+ // var sopUids = batch.Select(x => x.SopInstanceUid).ToList();
+
+ // try
+ // {
+ // await _instanceRepository.BatchUpdateNoTrackingAsync(
+ // t => sopUids.Contains(t.SopInstanceUid) && t.TrialId == trialId,
+ // t => new DicomInstance
+ // {
+ // IsReading = true,
+ // IsDeleted = false
+ // }, false);
+
+
+ // await _seriesRepository.BatchUpdateNoTrackingAsync(
+ // t => t.DicomInstanceList.Any(t => sopUids.Contains(t.SopInstanceUid)) && t.TrialId == trialId,
+ // t => new DicomSeries
+ // {
+ // IsReading = true,
+ // IsDeleted = false
+ // }, false);
+ // }
+ // catch (Exception ex)
+ // {
+ // var errorMsg = $"{string.Join(",", sopUids)} {DateTime.Now} 批量更新失败: {ex.Message}";
+ // Console.WriteLine(errorMsg);
+ // await File.AppendAllTextAsync(outputErrorFile, errorMsg + Environment.NewLine);
+ // }
+ //}
+
+ //找到该项目的检查,实时统计数量,并且回更数据库
+
+ var studyList = _studyRepository.Where(t => t.TrialId == trialId && (t.SeriesCount != t.SeriesList.Count() || t.InstanceCount != t.InstanceList.Count()))
+ .Select(t => new
+ {
+ t.Id,
+ t.StudyCode,
+ DBSeriesCount = t.SeriesCount,
+ DBInstanceCount = t.InstanceCount,
+
+ ActrualSeriesCount = t.SeriesList.Count(),
+
+ ActrualInstanceCount = t.InstanceList.Count(),
+
+ }).ToList();
+
+
+ var seriesList = _seriesRepository.Where(t => t.TrialId == trialId && t.InstanceCount != t.DicomInstanceList.Count())
+ .Select(t => new
+ {
+ SeriesId = t.Id,
+ t.DicomStudy.StudyCode,
+ DBInstanceCount = t.InstanceCount,
+ ActrualInstanceCount = t.DicomInstanceList.Count(),
+
+ }).ToList();
+
+
+ await File.AppendAllTextAsync(outputErrorFile, studyList.ToJsonStr() + Environment.NewLine);
+
+ await File.AppendAllTextAsync(outputErrorFile, seriesList.ToJsonStr() + Environment.NewLine);
+
+ return ResponseOutput.Ok();
+ }
+
+ public class DicomSOPInstanceInfo
+ {
+ public string SopInstanceUid { get; set; }
+
+
+ }
+
+ #endregion
+
+
+ #region 通过Excel 读取未下载的,边下载边维护数据
+
+ [AllowAnonymous]
+ public async Task WriteNeedDealData([FromServices] IRepository _instanceRepository)
+ {
+
+ #region 获取差集数据
+ //var rows = await MiniExcel.QueryAsync(@"C:\Users\hang\Desktop\维护数据读取.xlsx");
+
+ //rows = rows.Where(t => !string.IsNullOrEmpty(t.InstanceId.ToString())).ToList();
+
+ //var allRows = await MiniExcel.QueryAsync(@"C:\Users\hang\Desktop\AllData.xlsx");
+
+ //allRows = allRows.Where(t => !string.IsNullOrEmpty(t.InstanceId.ToString())).ToList();
+
+ //var needDealRows = allRows.Where(t => !rows.Select(c => c.InstanceId).Contains(t.InstanceId)).ToList();
+
+ //var outputFile = Path.Combine(@"D:\dicomWrite", $"{Guid.NewGuid()}_dicom_info.txt");
+
+ //foreach (var item in needDealRows)
+ //{
+ // var line = string.Join(",", item.InstanceId, item.Path);
+
+ // await File.AppendAllTextAsync(outputFile, line + Environment.NewLine);
+ //}
+
+ #endregion
+
+ var folder = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
+
+ var needDealRows = await MiniExcel.QueryAsync(Path.Combine(folder, "needDownload.xlsx"));
+
+ needDealRows = needDealRows.Where(t => !string.IsNullOrEmpty(t.InstanceId.ToString())).ToList();
+
+
+
+ var outputFile = Path.Combine(folder, $"{Guid.NewGuid()}_dicom_info.txt");
+
+ var outputErrorFile = Path.Combine(folder, $"{Guid.NewGuid()}_dicom_info_error.txt");
+
+ int total = needDealRows.Count();
+
+ Console.WriteLine($"需要处理数量{total}");
+
+ int processed = 0;
+ double lastPercent = 0;
+
+ foreach (var item in needDealRows)
+ {
+
+ try
+ {
+ await using var stream = await _oSSService.GetStreamFromOSSAsync(item.Path);
+
+ var dicomFile = DicomFile.Open(stream);
+
+ var dataset = dicomFile.Dataset;
+ var fileMeta = dicomFile.FileMetaInfo;
+
+ var pixelData = DicomPixelData.Create(dicomFile.Dataset);
+
+ //获取像素是否为封装形式
+ var syntax = dicomFile.Dataset.InternalTransferSyntax;
+
+ var stationName = dataset.GetSingleValueOrDefault(DicomTag.StationName, string.Empty);
+
+ var institutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty);
+
+ var manufacturer = dataset.GetSingleValueOrDefault(DicomTag.Manufacturer, string.Empty);
+
+ //PatientID TrialCode_SubjectCode
+ //var patientID = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
+
+ //SubjectCode
+ var clinicalTrialSubjectID = dataset.GetSingleValueOrDefault(DicomTag.ClinicalTrialSubjectID, string.Empty);
+ //访视visitNum
+ var clinicalTrialTimePointID = dataset.GetSingleValueOrDefault(DicomTag.ClinicalTrialTimePointID, string.Empty);
+ var studyInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty);
+ var seriesInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.SeriesInstanceUID, string.Empty);
+ var sOPInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.SOPInstanceUID, string.Empty);
+
+ var numberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1);
+
+
+ // 传输语法
+ var transferSyntaxUID = fileMeta.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
+ var sOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
+ var mediaStorageSOPClassUID = fileMeta.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
+
+ var mediaStorageSOPInstanceUID = fileMeta.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
+
+
+ var line = string.Join(",",
+ item.InstanceId,
+ stationName,
+ institutionName,
+ manufacturer,
+ clinicalTrialSubjectID,
+ clinicalTrialTimePointID,
+ studyInstanceUID,
+ seriesInstanceUID,
+ sOPInstanceUID,
+ numberOfFrames,
+ transferSyntaxUID,
+ sOPClassUID,
+ mediaStorageSOPClassUID,
+ mediaStorageSOPInstanceUID
+
+ );
+
+
+ await File.AppendAllTextAsync(outputFile, line + Environment.NewLine);
+
+ //维护序列层级四个字段 后再用sql 维护study series 时间拆分 和 MediaStorageSOPInstanceUID
+ await _instanceRepository.BatchUpdateNoTrackingAsync(
+ t => t.Id == item.InstanceId,
+ t => new DicomInstance
+ {
+ IsEncapsulated = syntax.IsEncapsulated,
+ TransferSytaxUID = transferSyntaxUID,
+ MediaStorageSOPClassUID = mediaStorageSOPClassUID,
+ SOPClassUID = sOPClassUID,
+ MediaStorageSOPInstanceUID = mediaStorageSOPInstanceUID
+ }, false);
+ }
+ catch (Exception ex)
+ {
+
+ var errorMsg = $"{DateTime.Now} ❌ 失败: {ex.Message} | InstanceId={item.InstanceId}, Path={item.Path}";
+
+ Console.WriteLine(errorMsg);
+
+ await File.AppendAllTextAsync(outputErrorFile, errorMsg + Environment.NewLine);
+ }
+ finally
+ {
+ processed++;
+ double percent = processed * 100.0 / total;
+
+ // 每提升 5% 或完成时输出
+ if (percent - lastPercent >= 2.0 || processed == total)
+ {
+ lastPercent = percent;
+ Console.WriteLine($"{DateTime.Now} 进度: {processed}/{total} ({percent:F2}%)");
+ }
+ }
+
+
+ }
+
+ return ResponseOutput.Ok();
+ }
+
+ public class NeedDealInstanceInfo
+ {
+ public Guid InstanceId { get; set; }
+
+ public string Path { get; set; }
+ }
+
+ ///
+ /// 读取该项目的数据,进行维护
+ ///
+ ///
+ ///
+ ///
+ [AllowAnonymous]
+ public async Task WriteTrialNeedDealData([FromServices] IRepository _instanceRepository, Guid trialId)
+ {
+
+ #region 获取差集数据
+ //var rows = await MiniExcel.QueryAsync(@"C:\Users\hang\Desktop\维护数据读取.xlsx");
+
+ //rows = rows.Where(t => !string.IsNullOrEmpty(t.InstanceId.ToString())).ToList();
+
+ //var allRows = await MiniExcel.QueryAsync(@"C:\Users\hang\Desktop\AllData.xlsx");
+
+ //allRows = allRows.Where(t => !string.IsNullOrEmpty(t.InstanceId.ToString())).ToList();
+
+ //var needDealRows = allRows.Where(t => !rows.Select(c => c.InstanceId).Contains(t.InstanceId)).ToList();
+
+ //var outputFile = Path.Combine(@"D:\dicomWrite", $"{Guid.NewGuid()}_dicom_info.txt");
+
+ //foreach (var item in needDealRows)
+ //{
+ // var line = string.Join(",", item.InstanceId, item.Path);
+
+ // await File.AppendAllTextAsync(outputFile, line + Environment.NewLine);
+ //}
+
+ #endregion
+
+ var folder = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
+
+ var needDealRows = _instanceRepository.Where(t => t.TrialId == trialId && t.TransferSytaxUID == "").Select(t => new NeedDealInstanceInfo() { InstanceId = t.Id, Path = t.Path });
+
+
+
+ var outputFile = Path.Combine(folder, $"{Guid.NewGuid()}_dicom_info.txt");
+
+ var outputErrorFile = Path.Combine(folder, $"{Guid.NewGuid()}_dicom_info_error.txt");
+
+ int total = needDealRows.Count();
+
+ Console.WriteLine($"需要处理数量{total}");
+
+ int processed = 0;
+ double lastPercent = 0;
+
+ foreach (var item in needDealRows)
+ {
+
+ try
+ {
+ await using var stream = await _oSSService.GetStreamFromOSSAsync(item.Path);
+
+ var dicomFile = DicomFile.Open(stream);
+
+ var dataset = dicomFile.Dataset;
+ var fileMeta = dicomFile.FileMetaInfo;
+
+ var pixelData = DicomPixelData.Create(dicomFile.Dataset);
+
+ //获取像素是否为封装形式
+ var syntax = dicomFile.Dataset.InternalTransferSyntax;
+
+ var stationName = dataset.GetSingleValueOrDefault(DicomTag.StationName, string.Empty);
+
+ var institutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty);
+
+ var manufacturer = dataset.GetSingleValueOrDefault(DicomTag.Manufacturer, string.Empty);
+
+ //PatientID TrialCode_SubjectCode
+ //var patientID = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
+
+ //SubjectCode
+ var clinicalTrialSubjectID = dataset.GetSingleValueOrDefault(DicomTag.ClinicalTrialSubjectID, string.Empty);
+ //访视visitNum
+ var clinicalTrialTimePointID = dataset.GetSingleValueOrDefault(DicomTag.ClinicalTrialTimePointID, string.Empty);
+ var studyInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty);
+ var seriesInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.SeriesInstanceUID, string.Empty);
+ var sOPInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.SOPInstanceUID, string.Empty);
+
+ var numberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1);
+
+
+ // 传输语法
+ var transferSyntaxUID = fileMeta.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
+ var sOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
+ var mediaStorageSOPClassUID = fileMeta.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
+
+ var mediaStorageSOPInstanceUID = fileMeta.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
+
+
+ var line = string.Join(",",
+ item.InstanceId,
+ stationName,
+ institutionName,
+ manufacturer,
+ clinicalTrialSubjectID,
+ clinicalTrialTimePointID,
+ studyInstanceUID,
+ seriesInstanceUID,
+ sOPInstanceUID,
+ numberOfFrames,
+ transferSyntaxUID,
+ sOPClassUID,
+ mediaStorageSOPClassUID,
+ mediaStorageSOPInstanceUID
+
+ );
+
+
+ await File.AppendAllTextAsync(outputFile, line + Environment.NewLine);
+
+ //维护序列层级四个字段 后再用sql 维护study series 时间拆分 和 MediaStorageSOPInstanceUID
+ await _instanceRepository.BatchUpdateNoTrackingAsync(
+ t => t.Id == item.InstanceId,
+ t => new DicomInstance
+ {
+ IsEncapsulated = syntax.IsEncapsulated,
+ TransferSytaxUID = transferSyntaxUID,
+ MediaStorageSOPClassUID = mediaStorageSOPClassUID,
+ SOPClassUID = sOPClassUID,
+ MediaStorageSOPInstanceUID = mediaStorageSOPInstanceUID
+ }, false);
+ }
+ catch (Exception ex)
+ {
+
+ var errorMsg = $"{DateTime.Now} ❌ 失败: {ex.Message} | InstanceId={item.InstanceId}, Path={item.Path}";
+
+ Console.WriteLine(errorMsg);
+
+ await File.AppendAllTextAsync(outputErrorFile, errorMsg + Environment.NewLine);
+ }
+ finally
+ {
+ processed++;
+ double percent = processed * 100.0 / total;
+
+ // 每提升 5% 或完成时输出
+ if (percent - lastPercent >= 2.0 || processed == total)
+ {
+ lastPercent = percent;
+ Console.WriteLine($"{DateTime.Now} 进度: {processed}/{total} ({percent:F2}%)");
+ }
+ }
+
+
+ }
+
+ return ResponseOutput.Ok();
+ }
+ #endregion
+
+
+ #region oss 下载删除影像 ,并且恢复数据
+ [AllowAnonymous]
+ public async Task RestoreDBOSSDate(
+ [FromServices] IOSSService _oSSService, [FromServices] IWebHostEnvironment _hostEnvironment, [FromServices] IRepository _studyRepository)
+ {
+ var folder = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
+
+ var outputFile = Path.Combine(folder, $"{Guid.NewGuid()}_deleteKey_info.txt");
+ var outputFile2 = Path.Combine(folder, $"{Guid.NewGuid()}_deleteKeyExport_info.txt");
+
+ var outputErrorFile = Path.Combine(folder, $"{Guid.NewGuid()}_deleteKeyerror.txt");
+ var outputErrorFile2 = Path.Combine(folder, $"{Guid.NewGuid()}_deleteKeyerrorStudy.txt");
+
+
+ var aliConfig = _oSSService.ObjectStoreServiceOptions.AliyunOSS;
+
+ var tempToken = _oSSService.GetObjectStoreTempToken();
+
+ var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint,
+ tempToken.AliyunOSS.AccessKeyId,
+ tempToken.AliyunOSS.AccessKeySecret,
+ tempToken.AliyunOSS.SecurityToken);
+
+
+ var allVersions = new List();
+ var allDeleteMarkers = new List();
+
+ var request = new ListObjectVersionsRequest(tempToken.AliyunOSS.BucketName)
+ {
+ Prefix = "01000000-ac13-0242-6397-08dcd2d2a091/Image",
+ //Prefix = "01000000-ac13-0242-6397-08dcd2d2a091/Image/08dd9c04-c1b2-c2da-0242-ac1301000000/01000000-ac13-0242-235b-08dd9c04c1b3",
+ MaxKeys = 1000,
+ };
+
+ ObjectVersionList result;
+ do
+ {
+
+ result = _ossClient.ListObjectVersions(request);
+
+ if (result.ObjectVersionSummaries != null)
+ allVersions.AddRange(result.ObjectVersionSummaries);
+
+ if (result.DeleteMarkerSummaries != null)
+ allDeleteMarkers.AddRange(result.DeleteMarkerSummaries);
+
+ request.KeyMarker = result.NextKeyMarker;
+ request.VersionIdMarker = result.NextVersionIdMarker;
+
+ } while (result.IsTruncated);
+
+ Console.WriteLine($"共找到 {allDeleteMarkers.Count} 个删除标记");
+
+
+
+ int total = allDeleteMarkers.Count;
+
+ int processed = 0;
+ double lastPercent = 0;
+
+
+ // 按 Key 分组,找每个删除标记前的最近版本
+ var versionsByKey = allVersions
+ .GroupBy(v => v.Key)
+ .ToDictionary(g => g.Key, g => g.OrderByDescending(x => x.LastModified).ToList());
+
+ foreach (var del in allDeleteMarkers)
+ {
+ #region 防止阿里云过期
+ if (tempToken.AliyunOSS.Expiration.AddSeconds(10) <= DateTime.Now)
+ {
+ tempToken = _oSSService.GetObjectStoreTempToken();
+
+ _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint,
+ tempToken.AliyunOSS.AccessKeyId,
+ tempToken.AliyunOSS.AccessKeySecret,
+ tempToken.AliyunOSS.SecurityToken);
+ }
+
+ #endregion
+
+ if (!versionsByKey.TryGetValue(del.Key, out var versions))
+ continue; // 没有历史版本无法恢复
+
+ var prevVersion = versions.FirstOrDefault(v => v.LastModified < del.LastModified);
+ if (prevVersion == null)
+ continue; // 没找到可恢复版本
+
+ if (Path.GetExtension(prevVersion.Key).IsNotNullOrEmpty())
+ {
+ continue;//不是dicom 文件
+ }
+
+
+ try
+ {
+ //await File.AppendAllTextAsync(outputFile, $"{prevVersion.Key},{prevVersion.VersionId}" + Environment.NewLine);
+
+ var getReq = new GetObjectRequest(tempToken.AliyunOSS.BucketName, prevVersion.Key)
+ {
+ VersionId = prevVersion.VersionId
+ };
+
+ using (var getResult = _ossClient.GetObject(getReq))
+ using (var memStream = new MemoryStream())
+ {
+ // 先把 OSS 流复制到内存流
+ getResult.Content.CopyTo(memStream);
+ memStream.Position = 0;
+
+ // 读取 DICOM 信息
+ var dicomFile = DicomFile.Open(memStream);
+ var studyInstanceUID = dicomFile.Dataset.GetString(DicomTag.StudyInstanceUID);
+
+ var findInfo = await _studyRepository.Where(t => t.StudyInstanceUid == studyInstanceUID && t.TrialId == Guid.Parse("01000000-ac13-0242-6397-08dcd2d2a091"))
+ .Select(t => new { t.StudyInstanceUid, t.Subject.Code, t.SubjectVisit.VisitName, t.SubjectId, t.SubjectVisitId }).FirstOrDefaultAsync();
+
+ if (findInfo != null)
+ {
+
+ // 再保存到另一个路径(可以使用 fo-dicom 保存)
+
+ var fileName = Path.GetFileNameWithoutExtension(prevVersion.Key);
+ var anotherPath = Path.Combine(folder, findInfo.Code, findInfo.VisitName, studyInstanceUID, fileName);
+ // 去掉 folder 部分,得到相对路径
+ var relativePath = Path.GetRelativePath(folder, anotherPath);
+ Directory.CreateDirectory(Path.GetDirectoryName(anotherPath));
+ dicomFile.Save(anotherPath);
+
+ await File.AppendAllTextAsync(outputFile2, $"{findInfo.SubjectId},{findInfo.SubjectVisitId},{prevVersion.Key},{prevVersion.VersionId},{relativePath},{findInfo.Code},{findInfo.VisitName},{findInfo.StudyInstanceUid},{fileName}" + Environment.NewLine);
+ }
+ else
+ {
+ await File.AppendAllTextAsync(outputErrorFile2, $"{studyInstanceUID},{prevVersion.Key},{prevVersion.VersionId}" + Environment.NewLine);
+ }
+
+ //Console.WriteLine($"读取到 studyInstanceUID: {studyInstanceUID}");
+
+ //var localPath = Path.Combine(folder, prevVersion.Key.Trim('/').Replace('/', Path.DirectorySeparatorChar));
+ //Directory.CreateDirectory(Path.GetDirectoryName(localPath));
+ //// 保存到原本路径
+ //memStream.Position = 0;
+ //using (var fs = File.Create(localPath))
+ //{
+ // memStream.CopyTo(fs);
+ //}
+
+
+ }
+
+
+
+ //Console.WriteLine($"✅ 下载成功: {prevVersion.Key} (version={prevVersion.VersionId})");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"❌ 下载失败: {prevVersion.Key}, 错误: {ex.Message}");
+
+ await File.AppendAllTextAsync(outputErrorFile, $"{prevVersion.Key},{prevVersion.VersionId}" + Environment.NewLine);
+ }
+ finally
+ {
+ processed++;
+ double percent = processed * 100.0 / total;
+
+ // 每提升 5% 或完成时输出
+ if (percent - lastPercent >= 2.0 || processed == total)
+ {
+ lastPercent = percent;
+ Console.WriteLine($"{DateTime.Now} 进度: {processed}/{total} ({percent:F2}%)");
+ }
+ }
+
+
+ // 使用 CopyObject 把历史版本拷贝为最新版本(恢复)
+ //var copyReq = new CopyObjectRequest
+ //{
+ // Bucket = bucketName,
+ // Key = prevVersion.Key,
+ // SourceBucket = bucketName,
+ // SourceKey = prevVersion.Key,
+ // SourceVersionId = prevVersion.VersionId
+ //};
+
+ //try
+ //{
+ // var copyResult = client.CopyObject(copyReq);
+ // Console.WriteLine($"✅ 恢复成功: {prevVersion.Key} -> newVersionId={copyResult.VersionId}");
+ //}
+ //catch (Exception ex)
+ //{
+ // Console.WriteLine($"❌ 恢复失败: {prevVersion.Key}, 错误: {ex.Message}");
+ //}
+ }
+
+
+
+ return ResponseOutput.Ok();
+ }
+
+
+
+ public async Task OSSDeleteReStorre([FromServices] IOSSService _oSSService, [FromServices] IWebHostEnvironment _hostEnvironment)
+ {
+ var aliConfig = _oSSService.ObjectStoreServiceOptions.AliyunOSS;
+
+ var tempToken = _oSSService.GetObjectStoreTempToken();
+
+ var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint,
+ tempToken.AliyunOSS.AccessKeyId,
+ tempToken.AliyunOSS.AccessKeySecret,
+ tempToken.AliyunOSS.SecurityToken);
+
+
+ var allVersions = new List();
+ var allDeleteMarkers = new List();
+
+ var request = new ListObjectVersionsRequest(tempToken.AliyunOSS.BucketName)
+ {
+ Prefix = "test-delete-restore",
+ //Prefix = "01000000-ac13-0242-6397-08dcd2d2a091/Image/08dd9c04-c1b2-c2da-0242-ac1301000000/01000000-ac13-0242-235b-08dd9c04c1b3",
+ MaxKeys = 1000,
+ };
+
+ ObjectVersionList result;
+ do
+ {
+
+ result = _ossClient.ListObjectVersions(request);
+
+ if (result.ObjectVersionSummaries != null)
+ allVersions.AddRange(result.ObjectVersionSummaries);
+
+ if (result.DeleteMarkerSummaries != null)
+ allDeleteMarkers.AddRange(result.DeleteMarkerSummaries);
+
+ request.KeyMarker = result.NextKeyMarker;
+ request.VersionIdMarker = result.NextVersionIdMarker;
+
+ } while (result.IsTruncated);
+
+ Console.WriteLine($"共找到 {allDeleteMarkers.Count} 个删除标记");
+
+ var versionsByKey = allVersions
+ .GroupBy(v => v.Key)
+ .ToDictionary(g => g.Key, g => g.OrderByDescending(x => x.LastModified).ToList());
+
+
+ foreach (var del in allDeleteMarkers)
+ {
+ #region 防止阿里云过期
+ if (tempToken.AliyunOSS.Expiration.AddSeconds(10) <= DateTime.Now)
+ {
+ tempToken = _oSSService.GetObjectStoreTempToken();
+
+ _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint,
+ tempToken.AliyunOSS.AccessKeyId,
+ tempToken.AliyunOSS.AccessKeySecret,
+ tempToken.AliyunOSS.SecurityToken);
+ }
+
+ #endregion
+
+ if (!versionsByKey.TryGetValue(del.Key, out var versions))
+ continue; // 没有历史版本无法恢复
+
+ var prevVersion = versions.FirstOrDefault(v => v.LastModified < del.LastModified);
+ if (prevVersion == null)
+ continue; // 没找到可恢复版本
+
+
+
+
+ // 创建 CopyObject 请求
+ // 先用构造函数指定源和目标
+ var copyReq = new CopyObjectRequest(
+ sourceBucketName: tempToken.AliyunOSS.BucketName,
+ sourceKey: prevVersion.Key,
+ destinationBucketName: tempToken.AliyunOSS.BucketName,
+ destinationKey: prevVersion.Key // 覆盖到同名 Key,达到“恢复”的效果
+ );
+
+ // 再设置版本号
+ copyReq.SourceVersionId = prevVersion.VersionId;
+
+
+
try
{
var copyResult = _ossClient.CopyObject(copyReq);
@@ -1538,8 +2707,7 @@ namespace IRaCIS.Core.Application.Service
///
///
[AllowAnonymous]
- [HttpGet]
- public async Task ReadExcelReStorreOSSDeleteDataAndDBData([FromServices] IOSSService _oSSService, [FromServices] IWebHostEnvironment _hostEnvironment, string subjectCode = "")
+ public async Task ReadExcelReStorreOSSDeleteDataAndDBData([FromServices] IOSSService _oSSService, [FromServices] IWebHostEnvironment _hostEnvironment, string subjectCode)
{
var folder = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
@@ -1570,10 +2738,6 @@ namespace IRaCIS.Core.Application.Service
tempToken.AliyunOSS.AccessKeySecret,
tempToken.AliyunOSS.SecurityToken);
- var allDeleteDistinceKeys = new List();
-
- var haveRestoreInstanceIdList = new List();
- var haveRestoreSeriesIdList = new List();
var restoreCount = 0;
foreach (var item in rows)
@@ -1584,14 +2748,6 @@ namespace IRaCIS.Core.Application.Service
continue;
}
- if (allDeleteDistinceKeys.Contains(item.Key))
- {
- continue;
- }
-
- allDeleteDistinceKeys.Add(item.Key);
-
-
try
{
@@ -1615,30 +2771,11 @@ namespace IRaCIS.Core.Application.Service
continue;
}
-
-
- var fileSize = new FileInfo(dicomFilePath).Length;
-
- var archieveInfo = await ArchiveDicomFileAsync(dicomFile, trialId, item.SubjectId, item.SubjectVisitId, item.Key, fileSize, haveRestoreSeriesIdList, haveRestoreInstanceIdList);
-
- if (archieveInfo.IsInstanceAdd)
- {
- haveRestoreInstanceIdList.Add(archieveInfo.InstanceId);
- }
-
- if (archieveInfo.IsSeriesAdd)
- {
- haveRestoreSeriesIdList.Add(archieveInfo.SeriesId);
- }
-
- if (archieveInfo.IsInstanceAdd == false)
- {
- continue;
- }
-
restoreCount++;
- Console.WriteLine($"恢复SOPInstanceUID{sopInstanceUid},实际恢复当前数量{restoreCount}");
+ Console.WriteLine($"恢复SOPInstanceUID{sopInstanceUid},当前数量{restoreCount}");
+
+ await ArchiveDicomFileAsync(dicomFile.Dataset, trialId, item.SubjectId, item.SubjectVisitId);
#endregion
@@ -1669,15 +2806,14 @@ namespace IRaCIS.Core.Application.Service
// 再设置版本号
copyReq.SourceVersionId = item.VersionId;
- //var copyResult = _ossClient.CopyObject(copyReq);
+ var copyResult = _ossClient.CopyObject(copyReq);
- Console.WriteLine($"✅ 恢复成功: {item.SubjectCode} {item.Key} 版本{item.VersionId}");
}
catch (Exception ex)
{
- var errorMsg = $"❌ 恢复失败:{item.SubjectCode} {item.Key}, 错误: {ex.Message}";
+ var errorMsg = $"❌ 恢复失败: {item.Key}, 错误: {ex.Message}";
Console.WriteLine(errorMsg);
await File.AppendAllTextAsync(outputErrorFile, errorMsg + Environment.NewLine);
@@ -1696,8 +2832,6 @@ namespace IRaCIS.Core.Application.Service
}
- Console.WriteLine($"{haveRestoreInstanceIdList.Count} ",haveRestoreInstanceIdList.ToJsonStr());
- Console.WriteLine($"{haveRestoreSeriesIdList.Count} ",haveRestoreSeriesIdList.ToJsonStr());
await _studyRepository.SaveChangesAsync();
@@ -1731,23 +2865,6 @@ namespace IRaCIS.Core.Application.Service
public string SOPInstanceUID { get; set; }
}
- public class CurrentImageArchiveInfo
- {
- public bool IsStudyAdd { get; set; }
-
- public bool IsSeriesAdd { get; set; }
-
- public bool IsInstanceAdd { get; set; }
-
- public Guid StudyId { get; set; }
-
- public Guid SeriesId { get; set; }
-
-
- public Guid InstanceId { get; set; }
- }
-
-
///
/// 单个文件接收 归档
///
@@ -1757,11 +2874,8 @@ namespace IRaCIS.Core.Application.Service
///
///
///
- public async Task ArchiveDicomFileAsync(DicomFile dicomFile, Guid trialId, Guid subjectId, Guid subjectVisitId, string path, long fileSize, List addSeriesIdList, List addInstanceIdList)
+ public async Task ArchiveDicomFileAsync(DicomDataset dataset, Guid trialId, Guid subjectId, Guid subjectVisitId)
{
-
- var dataset = dicomFile.Dataset;
-
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
@@ -1773,9 +2887,6 @@ namespace IRaCIS.Core.Application.Service
var isSeriesNeedAdd = false;
var isInstanceNeedAdd = false;
- var archieveInfo = new CurrentImageArchiveInfo() { StudyId = studyId, SeriesId = seriesId, InstanceId = instanceId };
-
-
var findStudy = await _studyRepository.FirstOrDefaultAsync(t => t.Id == studyId);
var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
@@ -1787,8 +2898,6 @@ namespace IRaCIS.Core.Application.Service
{
isSeriesNeedAdd = true;
-
-
findSerice = new DicomSeries
{
@@ -1829,11 +2938,11 @@ namespace IRaCIS.Core.Application.Service
InstanceCount = 0
};
- //++findStudy.SeriesCount;
+ ++findStudy.SeriesCount;
}
- var transferSyntaxUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
+ var transferSyntaxUID = dataset.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
var isEncapsulated = false;
if (transferSyntaxUID.IsNotNullOrEmpty())
@@ -1860,9 +2969,9 @@ namespace IRaCIS.Core.Application.Service
SopInstanceUid = sopInstanceUid,
SOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty),
- MediaStorageSOPClassUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty),
+ MediaStorageSOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty),
TransferSytaxUID = transferSyntaxUID,
- MediaStorageSOPInstanceUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty),
+ MediaStorageSOPInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty),
IsEncapsulated = isEncapsulated,
InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
@@ -1882,41 +2991,20 @@ namespace IRaCIS.Core.Application.Service
WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty),
};
- //++findStudy.InstanceCount;
- //++findSerice.InstanceCount;
-
- //賦值路徑
- findInstance.Path = "/" + path;
- findInstance.FileSize = fileSize;
- }
-
-
- if (isSeriesNeedAdd && !addSeriesIdList.Any(t => t == seriesId))
- {
- archieveInfo.IsSeriesAdd = true;
-
- ++findStudy.SeriesCount;
-
- Console.WriteLine($"增加SeriesId:{seriesId}");
-
- await _seriesRepository.AddAsync(findSerice);
- }
- if (isInstanceNeedAdd && !addInstanceIdList.Any(t => t == instanceId))
- {
- archieveInfo.IsInstanceAdd = true;
-
++findStudy.InstanceCount;
++findSerice.InstanceCount;
-
- await _instanceRepository.AddAsync(findInstance);
-
- Console.WriteLine($"增加Instance:{instanceId}");
}
- return archieveInfo;
- ////有新建序列的,否则就要自己记住是否有新建序列,不然会重复跟踪
- //await _studyRepository.SaveChangesAsync();
+ if (isSeriesNeedAdd)
+ {
+ await _seriesRepository.AddAsync(findSerice);
+ }
+ if (isInstanceNeedAdd)
+ {
+ await _instanceRepository.AddAsync(findInstance);
+ }
+
}
}
diff --git a/IRaCIS.Core.Application/Service/Document/AuditDocumentService.cs b/IRaCIS.Core.Application/Service/Document/AuditDocumentService.cs
index d5bea6976..cf27ce4e0 100644
--- a/IRaCIS.Core.Application/Service/Document/AuditDocumentService.cs
+++ b/IRaCIS.Core.Application/Service/Document/AuditDocumentService.cs
@@ -19,6 +19,11 @@ using MassTransit;
using NPOI.POIFS.Properties;
using Org.BouncyCastle.Crypto;
using Microsoft.AspNetCore.Http;
+using IRaCIS.Core.Application.Contracts;
+using Microsoft.EntityFrameworkCore;
+using static Microsoft.Extensions.Logging.EventSource.LoggingEventSource;
+using NPOI.SS.Formula.Functions;
+using EFCore.BulkExtensions;
namespace IRaCIS.Core.Application.Service;
@@ -27,37 +32,728 @@ namespace IRaCIS.Core.Application.Service;
///
///
[ApiExplorerSettings(GroupName = "FileRecord")]
-public class AuditDocumentService(IRepository _auditDocumentRepository,
- IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer): BaseService
+public class AuditDocumentService(IRepository _auditDocumentRepository, IRepository _auditRecordRepository,
+ IRepository _auditRecordPermissionRepository, IRepository _auditRecordIdentityUserRepository, IRepository _auditDocumentClosureRepository,
+ IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService
{
- ///
- /// 获取稽查文档
- ///
- ///
- ///
- [HttpPost]
- public async Task> GetAuditDocumentList(AuditDocumentQuery inQuery)
- {
- var auditDocumentQueryable = _auditDocumentRepository
- .ProjectTo(_mapper.ConfigurationProvider);
- var pageList = await auditDocumentQueryable.ToPagedListAsync(inQuery);
- return pageList;
- }
+ #region 稽查新增需求
- ///
- /// 修改稽查文档
- ///
- ///
- ///
- [HttpPost]
+
+ ///
+ /// 查看授权时间内的稽查 (admin qa 看所有 EA只看到自己参与的)
+ ///
+ ///
+ ///
+ [HttpPost]
+ public async Task> GetAuditRecordSelectList(AuditRecordQuery inQuery)
+ {
+
+ var dateTimeNow = DateTime.Now;
+
+ var isAdminOrQA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.Admin || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.QA;
+
+ var auditRecordQueryable = _auditRecordRepository.Where(t => t.BeginTime < dateTimeNow && t.EndTime > dateTimeNow && t.AuditState == AuditState.Ongoing)
+ //过滤查看用户
+ .WhereIf(isAdminOrQA == false, t => t.AuditRecordIdentityUserList.Any(t => t.IdentityUserId == _userInfo.IdentityUserId))
+ .WhereIf(inQuery.BeginAuditTime != null, t => t.AuditTime >= inQuery.BeginAuditTime)
+ .WhereIf(inQuery.EndAuditTime != null, t => t.AuditTime <= inQuery.EndAuditTime)
+ .WhereIf(inQuery.AuditState != null, t => t.AuditState == inQuery.AuditState)
+ .WhereIf(inQuery.AuditType != null, t => t.AuditType == inQuery.AuditType)
+ .WhereIf(inQuery.BeginTime != null, t => t.BeginTime >= inQuery.BeginTime)
+ .WhereIf(inQuery.EndTime != null, t => t.EndTime <= inQuery.EndTime)
+ .WhereIf(inQuery.BeginCreateTime != null, t => t.CreateTime >= inQuery.BeginCreateTime)
+ .WhereIf(inQuery.EndCreateTime != null, t => t.CreateTime <= inQuery.EndCreateTime)
+ .WhereIf(inQuery.IdentityUserName.IsNotNullOrEmpty(), t => t.AuditRecordIdentityUserList.Any(c => c.IdentityUser.UserName.Contains(inQuery.IdentityUserName) || c.IdentityUser.FullName.Contains(inQuery.IdentityUserName)))
+ .WhereIf(inQuery.CompanyName.IsNotNullOrEmpty(), t => t.CompanyName.Contains(inQuery.CompanyName))
+ .WhereIf(inQuery.AuditContent.IsNotNullOrEmpty(), t => t.AuditContent.Contains(inQuery.AuditContent))
+ .ProjectTo(_mapper.ConfigurationProvider);
+
+ var list = await auditRecordQueryable.OrderBy(t => t.AuditTime).ToListAsync();
+
+ return list;
+ }
+
+
+ ///
+ /// 稽查记录 列表
+ ///
+ ///
+ ///
+ [HttpPost]
+ public async Task> GetAuditRecordList(AuditRecordQuery inQuery)
+ {
+
+ var auditRecordQueryable = _auditRecordRepository
+ .WhereIf(inQuery.BeginAuditTime != null, t => t.AuditTime >= inQuery.BeginAuditTime)
+ .WhereIf(inQuery.EndAuditTime != null, t => t.AuditTime <= inQuery.EndAuditTime)
+ .WhereIf(inQuery.AuditState != null, t => t.AuditState == inQuery.AuditState)
+ .WhereIf(inQuery.AuditType != null, t => t.AuditType == inQuery.AuditType)
+ .WhereIf(inQuery.BeginTime != null, t => t.BeginTime >= inQuery.BeginTime)
+ .WhereIf(inQuery.EndTime != null, t => t.EndTime <= inQuery.EndTime)
+ .WhereIf(inQuery.BeginCreateTime != null, t => t.CreateTime >= inQuery.BeginCreateTime)
+ .WhereIf(inQuery.EndCreateTime != null, t => t.CreateTime <= inQuery.EndCreateTime)
+ .WhereIf(inQuery.IdentityUserName.IsNotNullOrEmpty(), t => t.AuditRecordIdentityUserList.Any(c => c.IdentityUser.UserName.Contains(inQuery.IdentityUserName) || c.IdentityUser.FullName.Contains(inQuery.IdentityUserName)))
+ .WhereIf(inQuery.CompanyName.IsNotNullOrEmpty(), t => t.CompanyName.Contains(inQuery.CompanyName))
+ .WhereIf(inQuery.AuditContent.IsNotNullOrEmpty(), t => t.AuditContent.Contains(inQuery.AuditContent))
+ .ProjectTo(_mapper.ConfigurationProvider);
+
+ var pageList = await auditRecordQueryable.ToPagedListAsync(inQuery);
+
+ return pageList;
+ }
+
+
+ [HttpPost]
+ public async Task AddOrUpdateAuditRecord(AuditRecordAddOrEdit addOrEditAuditRecord)
+ {
+
+
+ if (addOrEditAuditRecord.Id == null)
+ {
+ var entity = _mapper.Map(addOrEditAuditRecord);
+
+
+ if (await _auditRecordRepository.AnyAsync(t => t.CompanyName == addOrEditAuditRecord.CompanyName && t.AuditContent == addOrEditAuditRecord.AuditContent && t.AuditTime == addOrEditAuditRecord.AuditTime, true))
+ {
+ //---重复的稽查记录。
+ return ResponseOutput.NotOk(_localizer["AuditDocument_RepeatAuditRecord"]);
+ }
+
+ await _auditRecordRepository.AddAsync(entity, true);
+
+ return ResponseOutput.Ok(entity.Id.ToString());
+ }
+ else
+ {
+ if (await _auditRecordRepository.AnyAsync(t => t.CompanyName == addOrEditAuditRecord.CompanyName && t.AuditContent == addOrEditAuditRecord.AuditContent && t.AuditTime == addOrEditAuditRecord.AuditTime
+ && t.Id != addOrEditAuditRecord.Id, true))
+ {
+ //---重复的稽查记录。
+ return ResponseOutput.NotOk(_localizer["AuditDocument_RepeatAuditRecord"]);
+ }
+
+ var find = _auditRecordRepository.Where(t => t.Id == addOrEditAuditRecord.Id, true).Include(t => t.AuditRecordIdentityUserList).FirstOrDefault();
+
+ _mapper.Map(addOrEditAuditRecord, find);
+ find.UpdateTime = DateTime.Now;
+
+ var success = await _auditRecordRepository.SaveChangesAsync();
+
+ return ResponseOutput.Ok(find.Id.ToString());
+ }
+
+
+
+
+ }
+
+ [HttpPut]
+ public async Task AddOrDeleteAuditRecordUser(AddOrDeleteAuditUserCommand inCommand)
+ {
+ if (inCommand.IsAdd)
+ {
+ foreach (var identityUserId in inCommand.IdentityUserIdList)
+ {
+ await _auditRecordIdentityUserRepository.AddAsync(new AuditRecordIdentityUser() { AuditRecordId = inCommand.AuditRecordId, IdentityUserId = identityUserId });
+ }
+ }
+ else
+ {
+ await _auditRecordIdentityUserRepository.BatchDeleteNoTrackingAsync(t => t.AuditRecordId == inCommand.AuditRecordId && inCommand.IdentityUserIdList.Contains(t.IdentityUserId));
+
+ }
+
+ await _auditRecordIdentityUserRepository.SaveChangesAsync();
+
+ return ResponseOutput.Ok();
+ }
+
+ ///
+ /// 删除稽查记录
+ ///
+ ///
+ ///
+ [HttpDelete("{auditRecordId:guid}")]
+ public async Task DeleteAuditRecord(Guid auditRecordId)
+ {
+ if (await _auditRecordRepository.Where(t => t.Id == auditRecordId).AnyAsync(u => u.AuditState != AuditState.NotStart))
+ {
+ //未开始的才允许删除
+ return ResponseOutput.NotOk(_localizer["AuditDocument_CannotDeleteStartRecod"]);
+ }
+
+ var success = await _auditRecordRepository.DeleteFromQueryAsync(t => t.Id == auditRecordId, true);
+
+ return ResponseOutput.Ok();
+ }
+
+ ///
+ /// 设置稽查记录某个文件 或者某个文件夹授权
+ ///
+ ///
+ ///
+ public async Task SetAuditRecordPermission(SetAuditRecordPermissionCommand inCommand)
+ {
+
+ var list = await _auditDocumentRepository.Where(t => inCommand.AuditDocumentIdList.Contains(t.Id)).ToListAsync();
+
+ foreach (var item in list)
+ {
+
+ //闭包表中找到 设置Id为祖先的所有 后代 包括自己
+ var matchIdList = await _auditDocumentClosureRepository.Where(t => item.Id == t.AncestorId).Select(t => t.DescendantId).ToListAsync();
+
+ if (inCommand.IsAuthorization)
+ {
+ foreach (var matchItem in matchIdList)
+ {
+ await _auditRecordPermissionRepository.AddAsync(new AuditRecordPermission() { AuditRecordId = inCommand.AuditRecordId, AuditDocumentId = matchItem });
+ }
+
+ }
+ else
+ {
+ await _auditRecordPermissionRepository.BatchDeleteNoTrackingAsync(t => t.AuditRecordId == inCommand.AuditRecordId && matchIdList.Contains(t.AuditDocumentId));
+
+ }
+
+ }
+
+ await _auditRecordPermissionRepository.SaveChangesAsync();
+
+
+ return ResponseOutput.Ok();
+ }
+
+
+
+
+
+ #endregion
+
+ #region 闭包修改
+
+ ///
+ /// GPT 移动子树代码--适合一次提交事务
+ ///
+ ///
+ ///
+ ///
+ private async Task MoveSubtreeAsync(Guid subtreeRootId, Guid? newParentId)
+ {
+
+ // 1. 子树所有闭包关系(后代包含自身)
+ var subtreeClosures = await _auditDocumentClosureRepository
+ .Where(c => c.AncestorId == subtreeRootId)
+ .ToListAsync();
+
+ var descendantIdsQuery = _auditDocumentClosureRepository
+ .Where(c => c.AncestorId == subtreeRootId).Select(c => c.DescendantId);
+
+ // 2. 删除原有祖先关系,保留自反关系和子树内部关系
+ await _auditDocumentClosureRepository.BatchDeleteNoTrackingAsync(c =>
+ descendantIdsQuery.Contains(c.DescendantId) &&
+ c.AncestorId != c.DescendantId && // 保留自反关系
+ !descendantIdsQuery.Contains(c.AncestorId) // 保留子树内部关系
+ );
+
+ // 3. 查新父节点祖先
+ var newParentClosures = newParentId.HasValue
+ ? await _auditDocumentClosureRepository
+ .Where(c => c.DescendantId == newParentId.Value)
+ .ToListAsync()
+ : new List();
+
+ // 4. 生成新关系
+ var newClosures = new List();
+
+ foreach (var pc in newParentClosures)
+ {
+ foreach (var sc in subtreeClosures)
+ {
+ newClosures.Add(new AuditDocumentClosure
+ {
+ AncestorId = pc.AncestorId,
+ DescendantId = sc.DescendantId,
+ Depth = pc.Depth + sc.Depth + 1 //深度公式:新深度 = 父祖先深度 + 子树原深度 + 1
+ });
+ }
+ }
+
+ // 5. 插入新关系
+ await _auditDocumentClosureRepository.AddRangeAsync(newClosures);
+ await _auditDocumentClosureRepository.SaveChangesAsync();
+
+ }
+
+
+ ///
+ /// 插入闭包表关系
+ ///
+ private async Task AddClosureRelationsAsync(Guid nodeId, Guid? parentId)
+ {
+ var closures = new List();
+
+ // 自身关系
+ closures.Add(new AuditDocumentClosure
+ {
+ AncestorId = nodeId,
+ DescendantId = nodeId,
+ Depth = 0
+ });
+
+ if (parentId.HasValue)
+ {
+ // 查出所有父级的祖先
+ var parentClosures = await _auditDocumentClosureRepository
+ .Where(c => c.DescendantId == parentId.Value).Select(t => new { t.AncestorId, t.Depth })
+ .ToListAsync();
+
+ // 生成父级的所有祖先到新节点的关系
+ foreach (var pc in parentClosures)
+ {
+ closures.Add(new AuditDocumentClosure
+ {
+ AncestorId = pc.AncestorId,
+ DescendantId = nodeId,
+ Depth = pc.Depth + 1
+ });
+ }
+ }
+
+ await _auditDocumentClosureRepository.AddRangeAsync(closures);
+ await _auditDocumentClosureRepository.SaveChangesAsync();
+ }
+
+ ///
+ /// 获取文件树形结构 (传Id 根节点就是自己)
+ ///
+ ///
+ ///
+ [HttpPost]
+ public async Task> GetAuditDocumentData(GetAuditDocumentDataInDto inDto)
+ {
+
+ var defalutSortArray = new string[] { nameof(AuditDocumentData.AuditDocumentTypeEnum), nameof(AuditDocumentData.Name) };
+ if (inDto.SortField.IsNotNullOrEmpty())
+ {
+ defalutSortArray = new string[] { nameof(AuditDocumentData.AuditDocumentTypeEnum), inDto.SortField + (inDto.Asc ? " asc" : " desc") };
+ inDto.SortField = string.Empty;
+ }
+
+ //匹配节点子查询
+ var matchedList = await _auditDocumentRepository.Where(x => x.AuditDocumentTypeEnum != AuditDocumentType.HistoricalVersion)
+ .WhereIf(inDto.IsAuthorization != null, x => x.IsAuthorization == inDto.IsAuthorization)
+ .WhereIf(inDto.AuditRecordId != null, x => x.IsAuthorization == inDto.IsAuthorization)
+ .WhereIf(inDto.Name.IsNotNullOrEmpty(), n => n.Name.Contains(inDto.Name))
+
+ .SelectMany(t => t.AncestorList).Select(t => t.Ancestor)//祖先包含自己
+ .Distinct() //可能包含相同的祖先
+ .ProjectTo(_mapper.ConfigurationProvider)
+ .SortToListAsync(defalutSortArray);
+
+ #region 针对某次稽查 授权展示
+ //某个稽查记录
+ if (inDto.AuditRecordId != null)
+ {
+ //找到该稽查记录 授权文件的所有父节点Id
+
+ var matchDocIdQuery = _auditRecordPermissionRepository.Where(t => t.AuditRecordId == inDto.AuditRecordId).Select(t => t.AuditDocumentId);
+
+ var parentIdList = _auditDocumentClosureRepository.Where(t => matchDocIdQuery.Contains(t.DescendantId)).Select(t => t.AncestorId).Distinct().ToList();
+
+ //刚开始未授权,就是空的,默认展示所有
+ if (inDto.IsCurrentAuditRecordAuthorization == true)
+ {
+ //只查看授权的
+ matchedList = matchedList.Where(t => parentIdList.Contains((Guid)t.Id)).ToList();
+ }
+
+
+ foreach (var ancestorId in parentIdList)
+ {
+ var find = matchedList.FirstOrDefault(t => t.Id == ancestorId);
+
+ if (find != null)
+ {
+ //如果前端不想用新字段,那么就覆盖之前的授权字段
+ find.IsCurrentAuditRecordAuthorization = true;
+ }
+ }
+
+ }
+ #endregion
+
+
+
+ // 构建字典并拼装树
+ var dict = matchedList.ToDictionary(n => n.Id, t => t);
+
+ foreach (var node in dict.Values)
+ {
+ if (node.ParentId.HasValue && dict.TryGetValue(node.ParentId.Value, out var parent))
+ parent.Children.Add(node);
+ }
+
+ var query = dict.Values
+ .WhereIf(inDto.SelfId != null, x => inDto.SelfId == x.Id)
+ .WhereIf(inDto.Id != null, x => inDto.Id == x.ParentId)
+ .WhereIf(inDto.Id == null && inDto.SelfId == null, x => x.ParentId == null);
+
+
+ PageOutput result = new PageOutput()
+ {
+ PageIndex = inDto.PageIndex,
+ PageSize = inDto.PageSize,
+ TotalCount = query.Count(),
+ };
+
+ var root = query.Skip(inDto.PageSize * (inDto.PageIndex - 1)).Take(inDto.PageSize).ToList();
+
+ result.CurrentPageData = root;
+
+ return result;
+
+ }
+
+
+
+ ///
+ /// 设置是否授权
+ ///
+ ///
+ ///
+ [HttpPost]
+ public async Task SetIsAuthorization(SetIsAuthorizationInDto inDto)
+ {
+ if (inDto.IsAuthorization)
+ {
+
+ //闭包表中找到 设置Id为祖先的所有 后代 包括自己
+ var matchIdQuery = _auditDocumentClosureRepository.Where(t => inDto.Ids.Contains(t.AncestorId)).Select(t => t.DescendantId);
+
+ //闭包表中找到 设置Id为后代的所有 祖先 包括自己
+ var matchIdQuery2 = _auditDocumentClosureRepository.Where(t => inDto.Ids.Contains(t.DescendantId)).Select(t => t.AncestorId);
+
+
+ var unionQuery = matchIdQuery.Union(matchIdQuery2);
+
+ await _auditDocumentRepository.BatchUpdateNoTrackingAsync(t => unionQuery.Contains(t.Id), u => new AuditDocument() { IsAuthorization = inDto.IsAuthorization });
+
+ }
+ else
+ {
+ //闭包表中找到 设置Id为祖先的所有 后代 包括自己
+ var matchIdQuery = _auditDocumentClosureRepository.Where(t => inDto.Ids.Contains(t.AncestorId)).Select(t => t.DescendantId);
+
+ await _auditDocumentRepository.BatchUpdateNoTrackingAsync(t => matchIdQuery.Contains(t.Id), u => new AuditDocument() { IsAuthorization = inDto.IsAuthorization });
+
+
+ }
+
+ return ResponseOutput.Ok();
+
+ }
+
+
+
+
+
+ ///
+ /// 删除稽查文档
+ ///
+ ///
+ ///
+ [HttpPost]
+ [UnitOfWork]
+ public async Task DeleteAuditDocument(DeleteAuditDocumentInDto inDto)
+ {
+
+ var isNotOptHistoryFile = _auditDocumentRepository.Where(t => inDto.Ids.Contains(t.Id)).All(t => t.MainFileId == null);
+
+ // 删除文件 或者文件夹里所有内容 (能在闭包表中找到)
+ if (isNotOptHistoryFile)
+ {
+ //闭包表中找到 设置Id为祖先的所有 后代 包括自己
+ var matchList = _auditDocumentClosureRepository.Where(t => inDto.Ids.Contains(t.AncestorId)).Select(t => new { t.DescendantId, t.Descendant.MainFileId, IsExistOldVersion = t.Descendant.AuditDocumentOldVersionList.Any() });
+
+ var matchIdList = matchList.Select(t => t.DescendantId).Distinct();
+
+ //找到有版本的
+ var matchIdVersionList = matchList.Where(t => t.IsExistOldVersion).Select(t => t.DescendantId).Distinct();
+
+
+ //删除自己以及后代 (能在闭包表中找到的,肯定不是删除 当前历史版本的操作 和当前历史所有版本的操作,能在闭包包中找到,那么肯定是删除当前文件,和当前文件所有 版本文件)
+ var success = await _auditDocumentRepository.DeleteFromQueryAsync(t => matchIdList.Contains(t.Id) || matchIdVersionList.Any(c => c == t.MainFileId));
+
+
+
+ // 删除原有闭包关系
+ await _auditDocumentClosureRepository.BatchDeleteNoTrackingAsync(t => matchIdList.Contains(t.DescendantId) || matchIdList.Contains(t.AncestorId));
+
+ }
+ else
+ {
+ //删除历史版本操作 删除当前历史所有版本操作
+
+ var mainFileIdList = await _auditDocumentRepository.Where(t => inDto.Ids.Contains(t.Id)).Select(t => t.MainFileId).Distinct().ToListAsync();
+
+ //删除当前选择的历史版本
+ await _auditDocumentRepository.BatchDeleteNoTrackingAsync(t => inDto.Ids.Contains(t.Id));
+
+
+ foreach (var item in mainFileIdList)
+ {
+ var historicalVersionList = await _auditDocumentRepository.Where(x => x.MainFileId == item).OrderBy(x => x.Version).ToListAsync();
+
+ var num = 1;
+ foreach (var historical in historicalVersionList)
+ {
+ await _auditDocumentRepository.UpdatePartialFromQueryAsync(historical.Id, x => new AuditDocument()
+ {
+ Version = num,
+ });
+ num++;
+ }
+ }
+
+
+ }
+
+ await _auditDocumentRepository.SaveChangesAsync();
+
+ return ResponseOutput.Ok();
+
+ }
+
+
+ ///
+ /// 把历史版本设置为当前版本--修改 维护闭包表,之间的闭包关系指向新版本文件
+ ///
+ ///
+ [HttpPost]
+ public async Task SetCurrentVersion(SetCurrentVersionInDto inDto)
+ {
+ var file = await _auditDocumentRepository.Where(x => x.Id == inDto.Id).FirstNotNullAsync();
+ if (file.AuditDocumentTypeEnum != AuditDocumentType.HistoricalVersion)
+ {
+ throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotSetCurrentVersion"]);
+ }
+
+ var mainFile = await _auditDocumentRepository.Where(x => x.Id == file.MainFileId).FirstNotNullAsync();
+
+ var historicalVersionIds = await _auditDocumentRepository.Where(x => x.MainFileId == mainFile.Id && x.Id != inDto.Id).OrderBy(x => x.Version).Select(x => x.Id).ToListAsync();
+
+ historicalVersionIds.Add(mainFile.Id);
+ await _auditDocumentRepository.UpdatePartialFromQueryAsync(inDto.Id, x => new AuditDocument()
+ {
+ MainFileId = null,
+ ParentId = mainFile.ParentId,
+ AuditDocumentTypeEnum = AuditDocumentType.File,
+ });
+
+ int num = 1;
+ foreach (var item in historicalVersionIds)
+ {
+ await _auditDocumentRepository.UpdatePartialFromQueryAsync(item, x => new AuditDocument()
+ {
+ MainFileId = inDto.Id,
+ ParentId = null,
+ Version = num,
+ AuditDocumentTypeEnum = AuditDocumentType.HistoricalVersion
+ });
+ num++;
+ }
+
+ await _auditDocumentRepository.SaveChangesAsync();
+
+ #region 闭包修改
+
+
+ //版本设计这里,当前版本才有parentId 不是当前版本就没有parentId->移动的时候还要维护历史版本的parentid
+ //增加闭包,所以这里要维护闭包表
+
+ await _auditDocumentClosureRepository.BatchUpdateNoTrackingAsync(t => t.AncestorId == mainFile.Id, t => new AuditDocumentClosure() { AncestorId = inDto.Id });
+
+ await _auditDocumentClosureRepository.BatchUpdateNoTrackingAsync(t => t.DescendantId == mainFile.Id, t => new AuditDocumentClosure() { DescendantId = inDto.Id });
+
+ #endregion
+
+
+ return ResponseOutput.Ok();
+
+ }
+
+
+
+ ///
+ /// 获取面包屑导航 (查询自己的祖先,不包括自己)
+ ///
+ ///
+ ///
+ [HttpPost]
+ public async Task> GetBreadcrumbData(GetBreadcrumbDataInDto inDto)
+ {
+
+ //闭包表中找到 设置Id为后代的所有 祖先 Depth > 0 不包括自己
+ var list = await _auditDocumentClosureRepository.Where(t => inDto.Id == t.DescendantId /*&& t.Depth > 0*/)
+ .OrderByDescending(t => t.Depth).Select(t => t.Ancestor).ProjectTo(_mapper.ConfigurationProvider).ToListAsync();
+
+ return list;
+
+
+
+ #region 无闭包表废弃
+ //List result = new List();
+
+ //await findParent(result, inDto.Id);
+ //async Task findParent(List datas, Guid id)
+ //{
+ // var data = await _auditDocumentRepository.Where(x => x.Id == id).ProjectTo(_mapper.ConfigurationProvider).FirstNotNullAsync();
+ // datas.Add(data);
+ // if (data.ParentId != null)
+ // {
+ // await findParent(datas, data.ParentId.Value);
+ // }
+
+ //}
+
+ //result.Reverse();
+
+ //return result;
+
+ #endregion
+
+ }
+
+
+
+ #endregion
+
+
+ #region 闭包维护关系,只处理最底层调用 AddOrUpdateAuditDocument
+
+
+
+ ///
+ /// 移动文件或者文件夹 到其他文件夹
+ ///
+ ///
+ [HttpPost]
+ public async Task MovieFileOrFolder(MovieFileOrFolderInDto inDto)
+ {
+
+ //闭包表中找到 设置Id为祖先的所有 后代 包括自己
+ var matchIdQuery = _auditDocumentClosureRepository.Where(t => inDto.Ids.Contains(t.AncestorId)).Select(t => t.DescendantId);
+
+ //无法将当前文件夹移动到当前目录以及子文件夹
+
+ if (matchIdQuery.Any(t => t == inDto.ParentId))
+ {
+ throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotMove"]);
+ }
+
+ #region 不考虑文件名重复 移动子树快速方法
+ //foreach (var item in inDto.Ids)
+ //{
+ // var subtreeRoot = await _auditDocumentRepository.FirstOrDefaultAsync(t => t.Id == item);
+
+ // subtreeRoot.ParentId = inDto.ParentId;
+
+ // await MoveSubtreeAsync(item, inDto.ParentId);
+ //}
+ #endregion
+
+
+ // 不能自动到自己父类这个文件夹
+ if (await _auditDocumentRepository.AnyAsync(x => x.ParentId == inDto.ParentId && inDto.Ids.Contains(x.Id)))
+ {
+ throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotMoveToParent"]);
+ }
+
+
+ await CopyFileOrFolder(inDto);
+
+ await DeleteAuditDocument(new DeleteAuditDocumentInDto()
+ {
+ Ids = inDto.Ids
+ });
+
+ return ResponseOutput.Ok();
+ }
+
+ ///
+ /// 复制文件或者文件夹
+ ///
+ ///
+ ///
+ [HttpPost]
+ public async Task CopyFileOrFolder(MovieFileOrFolderInDto inDto)
+ {
+
+
+
+ foreach (var item in inDto.Ids)
+ {
+ var data = (await GetAuditDocumentData(new GetAuditDocumentDataInDto()
+ {
+ SelfId = item,
+
+ PageIndex = 1,
+ PageSize = 1000
+ })).CurrentPageData;
+
+ List auditDocumentAddOrEdits = _mapper.Map>(data);
+ auditDocumentAddOrEdits.ForEach(x =>
+ {
+ x.IsUpdate = false;
+ x.IsAuthorization = false;
+ x.Id = null;
+ x.ParentId = inDto.ParentId;
+ });
+
+ await addData(auditDocumentAddOrEdits);
+ }
+
+ async Task addData(List data)
+ {
+ foreach (var item in data)
+ {
+ item.Id = null;
+ var result = await AddOrUpdateAuditDocument(item);
+
+ item.Children.ForEach(x =>
+ {
+ x.ParentId = result.Id;
+ x.IsUpdate = false;
+ x.Id = null;
+ });
+
+ if (item.Children.Count() > 0)
+ {
+ await addData(item.Children);
+ }
+ }
+ }
+
+
+ return ResponseOutput.Ok();
+ }
+
+ ///
+ /// 修改稽查文档
+ ///
+ ///
+ ///
+ [HttpPost]
public async Task UpdateAuditDocument(AuditDocumentUpdateDto inDto)
{
- AuditDocumentAddOrEdit addOrEdit = _mapper.Map(inDto);
- addOrEdit.IsUpdate = true;
+ AuditDocumentAddOrEdit addOrEdit = _mapper.Map(inDto);
+ addOrEdit.IsUpdate = true;
- var result= await AddOrUpdateAuditDocument(addOrEdit);
+ var result = await AddOrUpdateAuditDocument(addOrEdit);
return ResponseOutput.Ok(result.Id);
}
@@ -80,32 +776,36 @@ public class AuditDocumentService(IRepository _auditDocumentRepos
List auditDocumentAddOrEdits = new List();
auditDocumentAddOrEdits.Add(inDto);
- var result= await AddAuditDocument(auditDocumentAddOrEdits);
+ var result = await AddAuditDocument(auditDocumentAddOrEdits);
return ResponseOutput.Ok(result);
}
+
///
- /// 新增稽查文档
+ /// 新增稽查文档 (批量上传文件时才是数组,文件夹时单个对象)
///
///
///
[HttpPost]
public async Task AddAuditDocument(List inDto)
- {
- List resultData = new List();
+ {
+ List resultData = new List();
inDto.ForEach(x => x.IsUpdate = false);
await addData(inDto);
- async Task addData(List data)
- {
- foreach(var item in data)
- {
- var result= await AddOrUpdateAuditDocument(item);
- resultData.Add(result.Id);
- item.Children.ForEach(x => {
- x.ParentId = result.Id;
- x.IsUpdate = false;
+ //构建父子关系,然后返回Id
+ async Task addData(List data)
+ {
+ foreach (var item in data)
+ {
+ var result = await AddOrUpdateAuditDocument(item);
+
+ resultData.Add(result.Id);
+ item.Children.ForEach(x =>
+ {
+ x.ParentId = result.Id;
+ x.IsUpdate = false;
});
@@ -114,346 +814,111 @@ public class AuditDocumentService(IRepository _auditDocumentRepos
await addData(item.Children);
}
}
- }
+ }
- return ResponseOutput.Ok(resultData);
+ return ResponseOutput.Ok(resultData);
}
-
-
///
/// 通用方法
///
///
- private async Task AddOrUpdateAuditDocument(AuditDocumentAddOrEdit inDto)
- {
-
- var alikeData = await _auditDocumentRepository.Where(x =>x.Id!=inDto.Id&& x.ParentId == inDto.ParentId&&x.Name==inDto.Name&&x.AuditDocumentTypeEnum==inDto.AuditDocumentTypeEnum).FirstOrDefaultAsync();
- if (alikeData != null)
- {
- if (inDto.AuditDocumentTypeEnum == AuditDocumentType.Folder)
- {
- if (inDto.IsUpdate)
- {
- throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotAddFolder"]);
- }
- else
- {
- return alikeData;
- }
- }
- else
- {
- var entityData = await _auditDocumentRepository.InsertOrUpdateAsync(inDto, true);
- var historicalVersionIds = await _auditDocumentRepository.Where(x => x.MainFileId == alikeData.Id).OrderBy(x => x.Version).Select(x => x.Id).ToListAsync();
- historicalVersionIds.Add(alikeData.Id);
- int num = 1;
-
- foreach (var item in historicalVersionIds)
- {
- await _auditDocumentRepository.UpdatePartialFromQueryAsync(item, x => new AuditDocument()
- {
- MainFileId = entityData.Id,
- ParentId = null,
- Version = num,
- AuditDocumentTypeEnum = AuditDocumentType.HistoricalVersion
- },true);
- num++;
- }
-
- return entityData;
- }
- }
-
-
- var entity = await _auditDocumentRepository.InsertOrUpdateAsync(inDto, true);
- return entity;
- }
-
- ///
- /// 获取面包屑导航
- ///
- ///
- ///
- [HttpPost]
- public async Task> GetBreadcrumbData(GetBreadcrumbDataInDto inDto)
- {
- List result=new List();
-
- await findParent(result, inDto.Id);
- async Task findParent(List datas, Guid id)
- {
- var data= await _auditDocumentRepository.Where(x => x.Id == id).ProjectTo(_mapper.ConfigurationProvider).FirstNotNullAsync();
- datas.Add(data);
- if (data.ParentId != null)
- {
- await findParent(datas, data.ParentId.Value);
- }
-
- }
-
- result.Reverse();
-
- return result;
-
- }
-
- ///
- /// 获取文件树形结构 (传Id 根节点就是自己)
- ///
- ///
- ///
- [HttpPost]
- public async Task> GetAuditDocumentData(GetAuditDocumentDataInDto inDto)
- {
-
- var defalutSortArray = new string[] { nameof(AuditDocumentData.AuditDocumentTypeEnum), nameof(AuditDocumentData.Name) };
- if (inDto.SortField.IsNotNullOrEmpty())
- {
- defalutSortArray = new string[] { nameof(AuditDocumentData.AuditDocumentTypeEnum), inDto.SortField + (inDto.Asc ? " asc" : " desc") };
- inDto.SortField = string.Empty;
- }
- // 新取出来排序 然后再找子项
- var data= (await _auditDocumentRepository
- .Where(x=>x.AuditDocumentTypeEnum!=AuditDocumentType.HistoricalVersion)
- .WhereIf(inDto.IsAuthorization!=null,x=>x.IsAuthorization==inDto.IsAuthorization)
- .ProjectTo(_mapper.ConfigurationProvider).ToPagedListAsync(new PageInput() {
- PageIndex=1,
- PageSize=999999,
- }, defalutSortArray) ).CurrentPageData.ToList();
-
-
-
- if (inDto.Name.IsNotNullOrEmpty())
- {
- List findIds = new List();
- var findData = data.Where(x => x.Name.Contains(inDto.Name)).Select(x => x.Id.Value).ToList();
- GetParentId(findIds, findData, data);
- findIds.AddRange(findData);
-
- data = data.Where(x => findIds.Distinct().Contains(x.Id.Value)).ToList();
-
- }
-
-
- var query = data
- .WhereIf(inDto.SelfId != null, x => inDto.SelfId == x.Id)
- .WhereIf(inDto.Id != null, x => inDto.Id == x.ParentId)
- .WhereIf(inDto.Id == null&& inDto.SelfId == null, x => x.ParentId == null);
-
-
-
- PageOutput result = new PageOutput()
- {
- PageIndex = inDto.PageIndex,
- PageSize = inDto.PageSize,
- TotalCount = query.Count(),
- };
- var root = query
- .Skip(inDto.PageSize * (inDto.PageIndex - 1)).Take(inDto.PageSize).ToList();
-
- var historicalVersionList = await _auditDocumentRepository
- .Where(x => x.AuditDocumentTypeEnum == AuditDocumentType.HistoricalVersion).ProjectTo(_mapper.ConfigurationProvider).ToListAsync();
-
- foreach (var item in root)
- {
- GetChildren(item, data, historicalVersionList);
-
- }
- result.CurrentPageData = root;
- return result;
-
- }
-
- private void GetParentId(List parentIds, List ids, List dataList)
- {
- var parentid = dataList.Where(x => ids.Contains(x.Id.Value) && x.ParentId != null).Select(x => x.ParentId.Value).ToList();
-
- if (parentid.Count() > 0)
- {
- parentIds.AddRange(parentid);
-
- GetParentId(parentIds, parentid, dataList);
- }
-
- }
-
- private void GetChildren(AuditDocumentData item, List dataList, List historyList)
+ private async Task AddOrUpdateAuditDocument(AuditDocumentAddOrEdit inDto)
{
- item.Children = dataList.Where(x => x.ParentId == item.Id).ToList();
- item.HistoricalVersionsCount= historyList.Where(x => x.MainFileId == item.Id).Count();
- foreach (var x in item.Children)
+ //同一层级不能加同名的文件
+ var alikeData = await _auditDocumentRepository.Where(x => x.Id != inDto.Id && x.ParentId == inDto.ParentId && x.Name == inDto.Name && x.AuditDocumentTypeEnum == inDto.AuditDocumentTypeEnum).FirstOrDefaultAsync();
+
+ //同层级找到了同名的
+ if (alikeData != null)
{
- GetChildren(x, dataList, historyList);
- }
- }
- ///
- /// 删除稽查文档
- ///
- ///
- ///
- [HttpPost]
- public async Task DeleteAuditDocument(DeleteAuditDocumentInDto inDto)
- {
- var data = await _auditDocumentRepository.Select(x => new DeleteAudit (){
- Id=x.Id,
- ParentId= x.ParentId,
- MainFileId= x.MainFileId
- }).ToListAsync();
-
- List DeleteId= inDto.Ids;
-
- finId(inDto.Ids, data);
-
- void finId(List deletids, List deletes)
- {
- DeleteId.AddRange(deletids);
-
- var temp = deletes.Where(x =>(x.ParentId!=null&& deletids.Contains(x.ParentId.Value))||(x.MainFileId!=null&& deletids.Contains(x.MainFileId.Value))).Select(x => x.Id).ToList();
- if (temp.Count() > 0)
- {
- finId(temp, deletes);
- }
- }
-
- DeleteId = DeleteId.Distinct().ToList();
- var mainFileId=await _auditDocumentRepository.Where(x => DeleteId.Contains(x.Id)&&x.MainFileId!=null).Select(x => x.MainFileId).Distinct().ToListAsync();
- var success = await _auditDocumentRepository.DeleteFromQueryAsync(t => DeleteId.Distinct().Contains(t.Id), true);
-
- foreach (var item in mainFileId)
- {
- var historicalVersionList = await _auditDocumentRepository.Where(x => x.MainFileId == item).OrderBy(x=>x.Version).ToListAsync();
-
- var num = 1;
- foreach (var historical in historicalVersionList)
- {
- await _auditDocumentRepository.UpdatePartialFromQueryAsync(historical.Id, x => new AuditDocument()
- {
- Version = num,
- }, true);
- num++;
- }
-
-
-
- }
-
-
- return ResponseOutput.Ok();
-
-
- }
-
- ///
- /// 移动文件或者文件夹 到其他文件夹
- ///
- ///
- [HttpPost]
- public async Task MovieFileOrFolder(MovieFileOrFolderInDto inDto)
- {
-
- var data = await _auditDocumentRepository.Select(x => new DeleteAudit()
- {
- Id = x.Id,
- ParentId = x.ParentId,
- AuditDocumentTypeEnum = x.AuditDocumentTypeEnum,
- MainFileId = x.MainFileId
- }).ToListAsync();
-
- foreach (var id in inDto.Ids)
- {
- var file = data.Where(x => x.Id == id).FirstOrDefault();
- if (file.AuditDocumentTypeEnum == AuditDocumentType.Folder)
+ if (inDto.AuditDocumentTypeEnum == AuditDocumentType.Folder)
{
- if (finChild(new List { id }, inDto.ParentId, data))
+ //更新
+ if (inDto.IsUpdate)
{
- throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotMove"]);
+ throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotAddFolder"]);
+ }
+ else
+ {
+ //添加相同目录,已存在,那么就不处理
+ return alikeData;
}
- }
- }
-
- bool finChild(List ids, Guid ChildId, List data)
- {
- var child = data.Where(x => x.ParentId != null && ids.Contains(x.ParentId.Value)).ToList();
- if (child.Count() == 0)
- {
- return false;
- }
- else if (child.Any(x => x.Id == ChildId))
- {
- return true;
}
else
{
- var newids = child.Select(x => x.Id).ToList();
+ //同名文件,那么作为版本处理(首先插入)
- return finChild(newids, ChildId, data);
+ var entityData = await _auditDocumentRepository.InsertOrUpdateAsync(inDto, true);
+ //找到该同名文件的历史版本
+ var historicalVersionIds = await _auditDocumentRepository.Where(x => x.MainFileId == alikeData.Id).OrderBy(x => x.Version).Select(x => x.Id).ToListAsync();
+ //加上当前的同名文件 一起重新设置版本号
+ historicalVersionIds.Add(alikeData.Id);
+
+ // 删除原有闭包关系 (重命的文件变成历史版本了)
+ await _auditDocumentClosureRepository.BatchDeleteNoTrackingAsync(t => t.AncestorId == alikeData.Id || t.DescendantId == alikeData.Id);
+
+ int num = 1;
+
+ foreach (var item in historicalVersionIds)
+ {
+ await _auditDocumentRepository.UpdatePartialFromQueryAsync(item, x => new AuditDocument()
+ {
+ MainFileId = entityData.Id,
+ ParentId = null,
+ Version = num,
+ AuditDocumentTypeEnum = AuditDocumentType.HistoricalVersion
+ });
+ num++;
+
+
+
+ await _auditDocumentRepository.SaveChangesAsync();
+ }
+
+ // 针对版本文件是不需要维护闭包表的
+ if (inDto.Id == null && entityData.MainFileId == null)
+ {
+ //维护新增闭包关系
+ await AddClosureRelationsAsync(entityData.Id, entityData.ParentId);
+ }
+
+ return entityData;
}
}
- foreach (var id in inDto.Ids)
- {
- await _auditDocumentRepository.UpdatePartialFromQueryAsync(id, x => new AuditDocument()
- {
- ParentId = inDto.ParentId
- });
+ //同层级没找到同名的文件,那么走正常的新增 和更新
+ var entity = await _auditDocumentRepository.InsertOrUpdateAsync(inDto, true);
- await _auditDocumentRepository.SaveChangesAsync();
+ if (inDto.Id == null && entity.MainFileId == null)
+ {
+ //维护新增闭包关系
+ await AddClosureRelationsAsync(entity.Id, entity.ParentId);
}
-
- return ResponseOutput.Ok();
+
+ return entity;
}
+ #endregion
+
+
+ #region 增加闭包设计 不用修改地方
+
///
- /// 复制文件或者文件夹
+ /// 获取稽查文档
///
- ///
+ ///
///
+ ///
[HttpPost]
- public async Task CopyFileOrFolder(MovieFileOrFolderInDto inDto)
+ public async Task> GetAuditDocumentList(AuditDocumentQuery inQuery)
{
+ var auditDocumentQueryable = _auditDocumentRepository
+ .ProjectTo(_mapper.ConfigurationProvider);
+ var pageList = await auditDocumentQueryable.ToPagedListAsync(inQuery);
- foreach (var item in inDto.Ids)
- {
- var data = (await GetAuditDocumentData(new GetAuditDocumentDataInDto()
- {
- SelfId =item,
-
- PageIndex=1,
- PageSize= 1000
- })).CurrentPageData;
-
- List auditDocumentAddOrEdits = _mapper.Map>(data);
- auditDocumentAddOrEdits.ForEach(x => {
- x.IsUpdate = false;
- x.Id = null;
- x.ParentId = inDto.ParentId;
- });
- await addData(auditDocumentAddOrEdits);
- }
-
- async Task addData(List data)
- {
- foreach (var item in data)
- {
- item.Id = null;
- var result = await AddOrUpdateAuditDocument(item);
-
- item.Children.ForEach(x => {
- x.ParentId = result.Id;
- x.IsUpdate = false;
- x.Id = null;
- });
-
- if (item.Children.Count() > 0)
- {
- await addData(item.Children);
- }
- }
- }
- return ResponseOutput.Ok();
+ return pageList;
}
///
@@ -462,122 +927,360 @@ public class AuditDocumentService(IRepository _auditDocumentRepos
///
///
[HttpPost]
- public async Task> GetHistoricalVersion(GetHistoricalVersionInDto inDto)
- {
+ public async Task> GetHistoricalVersion(GetHistoricalVersionInDto inDto)
+ {
- List< HistoricalVersionDto > result=new List();
+ List result = new List();
- result = await _auditDocumentRepository.Where(x => x.MainFileId == inDto.Id).ProjectTo(_mapper.ConfigurationProvider).OrderByDescending(x => x.Version).ToListAsync();
- var currentData = await _auditDocumentRepository.Where(x => x.Id == inDto.Id).ProjectTo(_mapper.ConfigurationProvider).FirstNotNullAsync();
- currentData.IsCurrentVersion = true;
+ result = await _auditDocumentRepository.Where(x => x.MainFileId == inDto.Id).ProjectTo(_mapper.ConfigurationProvider).OrderByDescending(x => x.Version).ToListAsync();
+ var currentData = await _auditDocumentRepository.Where(x => x.Id == inDto.Id).ProjectTo(_mapper.ConfigurationProvider).FirstNotNullAsync();
+ currentData.IsCurrentVersion = true;
result.Insert(0, currentData);
- return result;
- }
-
- ///
- /// 把历史版本设置为当前版本
- ///
- ///
- [HttpPost]
- public async Task SetCurrentVersion(SetCurrentVersionInDto inDto)
- {
- var file = await _auditDocumentRepository.Where(x => x.Id == inDto.Id).FirstNotNullAsync();
- if (file.AuditDocumentTypeEnum != AuditDocumentType.HistoricalVersion)
- {
- throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotSetCurrentVersion"]);
- }
-
- var mainFile = await _auditDocumentRepository.Where(x => x.Id == file.MainFileId).FirstNotNullAsync();
-
- var historicalVersionIds= await _auditDocumentRepository.Where(x => x.MainFileId == mainFile.Id&&x.Id!=inDto.Id).OrderBy(x=>x.Version).Select(x=>x.Id).ToListAsync();
-
- historicalVersionIds.Add(mainFile.Id);
- await _auditDocumentRepository.UpdatePartialFromQueryAsync(inDto.Id, x => new AuditDocument() {
- MainFileId=null,
- ParentId= mainFile.ParentId,
- AuditDocumentTypeEnum=AuditDocumentType.File,
- });
-
- int num = 1;
- foreach (var item in historicalVersionIds)
- {
- await _auditDocumentRepository.UpdatePartialFromQueryAsync(item, x => new AuditDocument()
- {
- MainFileId = inDto.Id,
- ParentId = null,
- Version=num,
- AuditDocumentTypeEnum= AuditDocumentType.HistoricalVersion
- });
- num++;
- }
-
- await _auditDocumentRepository.SaveChangesAsync();
-
-
- return ResponseOutput.Ok();
-
+ return result;
}
- ///
- /// 设置是否授权
- ///
- ///
- ///
- [HttpPost]
- public async Task SetIsAuthorization(SetIsAuthorizationInDto inDto)
- {
- var data = await _auditDocumentRepository.Select(x => new DeleteAudit()
- {
- Id = x.Id,
- ParentId = x.ParentId,
- MainFileId = x.MainFileId
- }).ToListAsync();
-
- List allid = new List();
- findChild(allid, inDto.Ids, data);
- if (inDto.IsAuthorization)
- {
- findParent(allid, inDto.Ids, data);
- }
- allid= allid.Distinct().ToList();
- await _auditDocumentRepository.UpdatePartialFromQueryAsync(t => allid.Contains(t.Id), x => new AuditDocument() {
-
- IsAuthorization = inDto.IsAuthorization
- });
- await _auditDocumentRepository.SaveChangesAsync();
- return ResponseOutput.Ok();
-
- void findParent(List allId, List current, List data)
- {
- allId.AddRange(current);
- var parent = data.Where(x => current.Contains(x.Id)).Select(x => x.ParentId).Where(x=>x!=null).Select(x=>(Guid)x).ToList();
-
- if (parent.Count() > 0)
- {
- findParent(allId, parent, data);
+ #endregion
- }
- }
- void findChild(List allId,List current, List data)
- {
- allId.AddRange(current);
- var child = data.Where(x =>(x.ParentId!=null&& current.Contains(x.ParentId.Value))||(x.MainFileId!=null&¤t.Contains(x.MainFileId.Value))).Select(x => x.Id).ToList();
- if (child.Count() > 0)
- {
-
- findChild(allId, child, data);
+ #region 闭包后续废弃方法
- }
- }
- }
+
+ /////
+ ///// 删除稽查文档
+ /////
+ /////
+ /////
+ //[HttpPost]
+ //public async Task DeleteAuditDocument(DeleteAuditDocumentInDto inDto)
+ //{
+ // var data = await _auditDocumentRepository.Select(x => new DeleteAudit()
+ // {
+ // Id = x.Id,
+ // ParentId = x.ParentId,
+ // MainFileId = x.MainFileId
+ // }).ToListAsync();
+
+ // List DeleteId = inDto.Ids;
+
+ // finId(inDto.Ids, data);
+
+ // void finId(List deletids, List deletes)
+ // {
+ // DeleteId.AddRange(deletids);
+
+ // var temp = deletes.Where(x => (x.ParentId != null && deletids.Contains(x.ParentId.Value)) || (x.MainFileId != null && deletids.Contains(x.MainFileId.Value))).Select(x => x.Id).ToList();
+ // if (temp.Count() > 0)
+ // {
+ // finId(temp, deletes);
+ // }
+ // }
+
+ // DeleteId = DeleteId.Distinct().ToList();
+ // var mainFileId = await _auditDocumentRepository.Where(x => DeleteId.Contains(x.Id) && x.MainFileId != null).Select(x => x.MainFileId).Distinct().ToListAsync();
+ // var success = await _auditDocumentRepository.DeleteFromQueryAsync(t => DeleteId.Distinct().Contains(t.Id), true);
+
+ // foreach (var item in mainFileId)
+ // {
+ // var historicalVersionList = await _auditDocumentRepository.Where(x => x.MainFileId == item).OrderBy(x => x.Version).ToListAsync();
+
+ // var num = 1;
+ // foreach (var historical in historicalVersionList)
+ // {
+ // await _auditDocumentRepository.UpdatePartialFromQueryAsync(historical.Id, x => new AuditDocument()
+ // {
+ // Version = num,
+ // }, true);
+ // num++;
+ // }
+
+
+
+ // }
+
+
+ // return ResponseOutput.Ok();
+
+
+ //}
+
+
+
+ /////
+ ///// 获取文件树形结构 (传Id 根节点就是自己)
+ /////
+ /////
+ /////
+ //[HttpPost]
+ //public async Task> GetAuditDocumentData(GetAuditDocumentDataInDto inDto)
+ //{
+
+ // var defalutSortArray = new string[] { nameof(AuditDocumentData.AuditDocumentTypeEnum), nameof(AuditDocumentData.Name) };
+ // if (inDto.SortField.IsNotNullOrEmpty())
+ // {
+ // defalutSortArray = new string[] { nameof(AuditDocumentData.AuditDocumentTypeEnum), inDto.SortField + (inDto.Asc ? " asc" : " desc") };
+ // inDto.SortField = string.Empty;
+ // }
+ // // 新取出来排序 然后再找子项
+ // var data = (await _auditDocumentRepository
+ // .Where(x => x.AuditDocumentTypeEnum != AuditDocumentType.HistoricalVersion)
+ // .WhereIf(inDto.IsAuthorization != null, x => x.IsAuthorization == inDto.IsAuthorization)
+ // .ProjectTo(_mapper.ConfigurationProvider).ToPagedListAsync(new PageInput()
+ // {
+ // PageIndex = 1,
+ // PageSize = 999999,
+ // }, defalutSortArray)).CurrentPageData.ToList();
+
+
+
+ // if (inDto.Name.IsNotNullOrEmpty())
+ // {
+ // List findIds = new List();
+ // var findData = data.Where(x => x.Name.Contains(inDto.Name)).Select(x => x.Id.Value).ToList();
+ // GetParentId(findIds, findData, data);
+ // findIds.AddRange(findData);
+
+ // data = data.Where(x => findIds.Distinct().Contains(x.Id.Value)).ToList();
+
+ // }
+
+
+ // var query = data
+ // .WhereIf(inDto.SelfId != null, x => inDto.SelfId == x.Id)
+ // .WhereIf(inDto.Id != null, x => inDto.Id == x.ParentId)
+ // .WhereIf(inDto.Id == null && inDto.SelfId == null, x => x.ParentId == null);
+
+
+
+ // PageOutput result = new PageOutput()
+ // {
+ // PageIndex = inDto.PageIndex,
+ // PageSize = inDto.PageSize,
+ // TotalCount = query.Count(),
+ // };
+ // var root = query
+ // .Skip(inDto.PageSize * (inDto.PageIndex - 1)).Take(inDto.PageSize).ToList();
+
+ // var historicalVersionList = await _auditDocumentRepository
+ // .Where(x => x.AuditDocumentTypeEnum == AuditDocumentType.HistoricalVersion).ProjectTo(_mapper.ConfigurationProvider).ToListAsync();
+
+ // foreach (var item in root)
+ // {
+ // GetChildren(item, data, historicalVersionList);
+
+ // }
+ // result.CurrentPageData = root;
+ // return result;
+
+ //}
+
+
+
+
+ //private void GetParentId(List parentIds, List ids, List dataList)
+ //{
+ // var parentid = dataList.Where(x => ids.Contains(x.Id.Value) && x.ParentId != null).Select(x => x.ParentId.Value).ToList();
+
+ // if (parentid.Count() > 0)
+ // {
+ // parentIds.AddRange(parentid);
+
+ // GetParentId(parentIds, parentid, dataList);
+ // }
+
+ //}
+
+ //private void GetChildren(AuditDocumentData item, List dataList, List historyList)
+ //{
+ // item.Children = dataList.Where(x => x.ParentId == item.Id).ToList();
+ // item.HistoricalVersionsCount = historyList.Where(x => x.MainFileId == item.Id).Count();
+ // foreach (var x in item.Children)
+ // {
+ // GetChildren(x, dataList, historyList);
+ // }
+ //}
+ /////
+ ///// 移动文件或者文件夹 到其他文件夹
+ /////
+ /////
+ //[HttpPost]
+ //public async Task MovieFileOrFolder(MovieFileOrFolderInDto inDto)
+ //{
+
+ // var data = await _auditDocumentRepository.Select(x => new DeleteAudit()
+ // {
+ // Id = x.Id,
+ // ParentId = x.ParentId,
+ // AuditDocumentTypeEnum = x.AuditDocumentTypeEnum,
+ // MainFileId = x.MainFileId
+ // }).ToListAsync();
+
+ // foreach (var id in inDto.Ids)
+ // {
+ // var file = data.Where(x => x.Id == id).FirstOrDefault();
+ // if (file.AuditDocumentTypeEnum == AuditDocumentType.Folder)
+ // {
+ // if (finChild(new List { id }, inDto.ParentId, data))
+ // {
+ // throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotMove"]);
+ // }
+ // }
+ // }
+
+ // bool finChild(List ids, Guid ChildId, List data)
+ // {
+ // var child = data.Where(x => x.ParentId != null && ids.Contains(x.ParentId.Value)).ToList();
+ // if (child.Count() == 0)
+ // {
+ // return false;
+ // }
+ // else if (child.Any(x => x.Id == ChildId))
+ // {
+ // return true;
+ // }
+ // else
+ // {
+ // var newids = child.Select(x => x.Id).ToList();
+
+ // return finChild(newids, ChildId, data);
+
+ // }
+ // }
+
+ // foreach (var id in inDto.Ids)
+ // {
+ // await _auditDocumentRepository.UpdatePartialFromQueryAsync(id, x => new AuditDocument()
+ // {
+ // ParentId = inDto.ParentId
+ // });
+
+ // await _auditDocumentRepository.SaveChangesAsync();
+ // }
+
+ // return ResponseOutput.Ok();
+ //}
+
+ /////
+ ///// 复制文件或者文件夹
+ /////
+ /////
+ /////
+ //[HttpPost]
+ //public async Task CopyFileOrFolder(MovieFileOrFolderInDto inDto)
+ //{
+
+ // foreach (var item in inDto.Ids)
+ // {
+ // var data = (await GetAuditDocumentData(new GetAuditDocumentDataInDto()
+ // {
+ // SelfId = item,
+
+ // PageIndex = 1,
+ // PageSize = 1000
+ // })).CurrentPageData;
+
+ // List auditDocumentAddOrEdits = _mapper.Map>(data);
+ // auditDocumentAddOrEdits.ForEach(x =>
+ // {
+ // x.IsUpdate = false;
+ // x.Id = null;
+ // x.ParentId = inDto.ParentId;
+ // });
+ // await addData(auditDocumentAddOrEdits);
+ // }
+
+ // async Task addData(List data)
+ // {
+ // foreach (var item in data)
+ // {
+ // item.Id = null;
+ // var result = await AddOrUpdateAuditDocument(item);
+
+ // item.Children.ForEach(x =>
+ // {
+ // x.ParentId = result.Id;
+ // x.IsUpdate = false;
+ // x.Id = null;
+ // });
+
+ // if (item.Children.Count() > 0)
+ // {
+ // await addData(item.Children);
+ // }
+ // }
+ // }
+ // return ResponseOutput.Ok();
+ //}
+
+
+ /////
+ ///// 设置是否授权
+ /////
+ /////
+ /////
+ //[HttpPost]
+ //public async Task SetIsAuthorization(SetIsAuthorizationInDto inDto)
+ //{
+ // var data = await _auditDocumentRepository.Select(x => new DeleteAudit()
+ // {
+ // Id = x.Id,
+ // ParentId = x.ParentId,
+ // MainFileId = x.MainFileId
+ // }).ToListAsync();
+
+ // List allid = new List();
+ // findChild(allid, inDto.Ids, data);
+ // if (inDto.IsAuthorization)
+ // {
+ // findParent(allid, inDto.Ids, data);
+ // }
+ // allid = allid.Distinct().ToList();
+ // await _auditDocumentRepository.UpdatePartialFromQueryAsync(t => allid.Contains(t.Id), x => new AuditDocument()
+ // {
+
+ // IsAuthorization = inDto.IsAuthorization
+ // });
+ // await _auditDocumentRepository.SaveChangesAsync();
+ // return ResponseOutput.Ok();
+
+ // void findParent(List allId, List current, List data)
+ // {
+ // allId.AddRange(current);
+ // var parent = data.Where(x => current.Contains(x.Id)).Select(x => x.ParentId).Where(x => x != null).Select(x => (Guid)x).ToList();
+
+ // if (parent.Count() > 0)
+ // {
+
+
+ // findParent(allId, parent, data);
+
+ // }
+ // }
+
+ // void findChild(List allId, List current, List data)
+ // {
+ // allId.AddRange(current);
+
+ // var child = data.Where(x => (x.ParentId != null && current.Contains(x.ParentId.Value)) || (x.MainFileId != null && current.Contains(x.MainFileId.Value))).Select(x => x.Id).ToList();
+ // if (child.Count() > 0)
+ // {
+
+
+ // findChild(allId, child, data);
+
+ // }
+
+ // }
+ //}
+
+ #endregion
+
}
diff --git a/IRaCIS.Core.Application/Service/Document/DTO/AuditDocumentViewModel.cs b/IRaCIS.Core.Application/Service/Document/DTO/AuditDocumentViewModel.cs
index 73a71c913..4ade36b57 100644
--- a/IRaCIS.Core.Application/Service/Document/DTO/AuditDocumentViewModel.cs
+++ b/IRaCIS.Core.Application/Service/Document/DTO/AuditDocumentViewModel.cs
@@ -9,6 +9,95 @@ using IRaCIS.Core.Domain.Share;
using System.Collections.Generic;
namespace IRaCIS.Core.Application.ViewModel;
+
+public class AuditRecordView : AuditRecordAddOrEdit
+{
+ public Guid CreateUserId { get; set; }
+ public DateTime CreateTime { get; set; }
+ public Guid UpdateUserId { get; set; }
+ public DateTime UpdateTime { get; set; }
+
+
+ public new List IdnetityUserIdList => IdentityUserList.Select(t => t.Id).ToList();
+ public List IdentityUserList { get; set; }
+}
+
+public class AuditIdentiUserInfo
+{
+ public Guid Id { get; set; }
+
+ public string UserName { get; set; }
+
+ public string FullName { get; set; }
+}
+
+public class AuditRecordAddOrEdit
+{
+ public Guid? Id { get; set; }
+ public string CompanyName { get; set; }
+
+ public string AuditContent { get; set; }
+
+ public DateOnly AuditTime { get; set; }
+
+
+ public DateTime? BeginTime { get; set; }
+
+ public DateTime? EndTime { get; set; }
+
+ public AuditState AuditState { get; set; }
+
+ public AuditType AuditType { get; set; }
+
+ //public List IdnetityUserIdList { get; set; }
+}
+
+public class AddOrDeleteAuditUserCommand
+{
+ public Guid AuditRecordId { get; set; }
+
+ public List IdentityUserIdList { get; set; }
+
+ public bool IsAdd { get; set; }
+}
+
+public class AuditRecordQuery : PageInput
+{
+ public string? CompanyName { get; set; }
+
+ public string? AuditContent { get; set; }
+
+ public DateOnly? BeginAuditTime { get; set; }
+ public DateOnly? EndAuditTime { get; set; }
+
+
+ public DateTime? BeginTime { get; set; }
+
+ public DateTime? EndTime { get; set; }
+
+ public AuditState? AuditState { get; set; }
+
+ public AuditType? AuditType { get; set; }
+
+
+ public DateTime? BeginCreateTime { get; set; }
+
+ public DateTime? EndCreateTime { get; set; }
+
+ public string? IdentityUserName { get; set; }
+}
+
+public class SetAuditRecordPermissionCommand
+{
+
+ public List AuditDocumentIdList { get; set; }
+
+ public Guid AuditRecordId { get; set; }
+
+ public bool IsAuthorization { get; set; }
+}
+
+
public class DeleteAudit
{
public Guid Id { get; set; }
@@ -24,16 +113,21 @@ public class DeleteAuditDocumentInDto
public List Ids { get; set; }
}
-public class GetAuditDocumentDataInDto:PageInput
+public class GetAuditDocumentDataInDto : PageInput
{
- public Guid? Id { get; set; }
+ public Guid? Id { get; set; }
public Guid? SelfId { get; set; }
public bool? IsAuthorization { get; set; }
+ public bool? IsCurrentAuditRecordAuthorization { get; set; }
+
public string Name { get; set; } = string.Empty;
+
+ //当前稽查记录Id
+ public Guid? AuditRecordId { get; set; }
}
public class GetAuditDocumentDataOutDto
@@ -53,17 +147,20 @@ public class AuditDocumentData : AuditDocumentUpdateDto
public int? Version { get; set; }
public int HistoricalVersionsCount { get; set; }
- public List Children { get; set; }=new List (){ };
+ public List Children { get; set; } = new List() { };
+
+
+ public bool? IsCurrentAuditRecordAuthorization { get; set; }
}
public class AuditDocumentView : AuditDocumentAddOrEdit
{
-
+
public DateTime CreateTime { get; set; }
-
+
public DateTime UpdateTime { get; set; }
-
+
}
public class SetIsAuthorizationInDto
@@ -80,7 +177,7 @@ public class SetCurrentVersionInDto
public class GetHistoricalVersionInDto
{
- public Guid Id { get; set; }
+ public Guid Id { get; set; }
}
public class GetHistoricalVersionOutDto
@@ -93,10 +190,10 @@ public class MovieFileOrFolderInDto
{
public List Ids { get; set; }
- public Guid ParentId { get; set; }
+ public Guid? ParentId { get; set; }
}
-public class HistoricalVersionDto: AuditDocumentUpdateDto
+public class HistoricalVersionDto : AuditDocumentUpdateDto
{
public bool IsCurrentVersion { get; set; } = false;
@@ -113,7 +210,7 @@ public class AuditDocumentUpdateDto
public AuditDocumentType AuditDocumentTypeEnum { get; set; }
- public string? FileFormat { get; set; }
+ public string? FileFormat { get; set; }
public string? FilePath { get; set; }
@@ -133,34 +230,34 @@ public class GetBreadcrumbDataInDto
public Guid Id { get; set; }
}
-public class AuditDocumentAddOrEdit: AuditDocumentUpdateDto
+public class AuditDocumentAddOrEdit : AuditDocumentUpdateDto
{
-
- public List Children { get; set; }=new List() { };
+
+ public List Children { get; set; } = new List() { };
public bool IsUpdate { get; set; } = true;
}
-public class AuditDocumentQuery:PageInput
+public class AuditDocumentQuery : PageInput
{
public AuditDocumentType? AuditDocumentTypeEnum { get; set; }
-
+
public string? FileFormat { get; set; }
-
+
public string? FilePath { get; set; }
-
- public decimal? FileSize { get; set; }
-
+
+ public decimal? FileSize { get; set; }
+
public bool? IsAuthorization { get; set; }
-
- public Guid? MainFileId { get; set; }
-
+
+ public Guid? MainFileId { get; set; }
+
public string? Name { get; set; }
-
- public Guid? ParentId { get; set; }
-
- }
+
+ public Guid? ParentId { get; set; }
+
+}
diff --git a/IRaCIS.Core.Application/Service/Document/TrialDocumentService.cs b/IRaCIS.Core.Application/Service/Document/TrialDocumentService.cs
index 06fdd2181..7189d817a 100644
--- a/IRaCIS.Core.Application/Service/Document/TrialDocumentService.cs
+++ b/IRaCIS.Core.Application/Service/Document/TrialDocumentService.cs
@@ -900,7 +900,12 @@ namespace IRaCIS.Core.Application.Services
///
public async Task GetSysDocSignUserList()
{
- var list = _systemDocConfirmedUserRepository.Where(t => t.ConfirmTime != null).Select(t => new { t.ConfirmUserId, t.ConfirmUser.UserName, t.ConfirmUser.FullName }).Distinct().ToList();
+ //EA 只能查看内部人员文档
+ var isEA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.EA;
+
+ var list = _systemDocConfirmedUserRepository.Where(t => t.ConfirmTime != null)
+ .WhereIf(isEA, t => t.ConfirmUser.IsZhiZhun == true)
+ .Select(t => new { t.ConfirmUserId, t.ConfirmUser.UserName, t.ConfirmUser.FullName }).Distinct().ToList();
return ResponseOutput.Ok(list);
}
@@ -911,12 +916,15 @@ namespace IRaCIS.Core.Application.Services
var isInternal = _userInfo.IsZhiZhun;
+ var isEA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.EA;
+
var systemDocQuery =
from sysDoc in _systemDocumentRepository.AsQueryable(false)
.Where(t => inQuery.UserTypeId != null ? t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == inQuery.UserTypeId) : true)
from identityUser in _identityUserRepository.AsQueryable(false).Where(t => t.Status == UserStateEnum.Enable && t.UserRoleList.Where(t => t.IsUserRoleDisabled == false).Any(t => sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
.Where(t => inQuery.UserId != null ? t.Id == inQuery.UserId : true)
.Where(t => inQuery.UserTypeId != null ? t.UserRoleList.Any(t => t.UserTypeId == inQuery.UserTypeId && t.IsUserRoleDisabled == false) : true)
+ .Where(t => isEA ? t.IsZhiZhun == true : true) //EA 只能查看内部人员文档
join confirm in _systemDocConfirmedUserRepository.Where() on new { ConfirmUserId = identityUser.Id, SystemDocumentId = sysDoc.Id } equals new { confirm.ConfirmUserId, confirm.SystemDocumentId } into cc
from confirm in cc.DefaultIfEmpty()
select new UnionDocumentWithConfirmInfoView()
@@ -942,7 +950,14 @@ namespace IRaCIS.Core.Application.Services
//UserTypeId = trialUser.UserRole.UserTypeId,
//UserTypeShortName = trialUser.UserRole.UserTypeRole.UserTypeShortName,
- FullFilePath = sysDoc.Path
+ FullFilePath = sysDoc.Path,
+
+ AttachmentCount = sysDoc.SystemDocumentAttachmentList.Where(t => t.OffLine == false).Count(),
+
+ DocNeedSignUserTypeList = sysDoc.NeedConfirmedUserTypeList.Select(t => t.UserTypeRole.UserTypeShortName).ToList(),
+
+ IdentityUserTypeList = identityUser.UserRoleList.Where(t => t.IsUserRoleDisabled == false).Select(c => c.UserTypeRole.UserTypeShortName).ToList()
+
};
var unionQuery = systemDocQuery.IgnoreQueryFilters().Where(t => !(t.IsDeleted == true && t.ConfirmTime == null))
@@ -956,44 +971,44 @@ namespace IRaCIS.Core.Application.Services
.WhereIf(inQuery.EndCreateTime != null, t => t.CreateTime <= inQuery.EndCreateTime)
.WhereIf(!string.IsNullOrEmpty(inQuery.UserName), t => t.UserName.Contains(inQuery.UserName))
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted)
- .WhereIf(isInternal == false, t => t.ConfirmTime != null);
+ .WhereIf(isInternal == false, t => t.ConfirmTime != null); //不是内部的人,看有签名时间的
var result = await unionQuery.ToPagedListAsync(inQuery);
#region 处理文档 需要签署的角色类型 和每个人的角色信息
- var trialDocIdList = result.CurrentPageData.Where(t => t.IsSystemDoc == false).Select(t => t.Id).ToList();
+ //var trialDocIdList = result.CurrentPageData.Where(t => t.IsSystemDoc == false).Select(t => t.Id).ToList();
- var sysDocIdList = result.CurrentPageData.Where(t => t.IsSystemDoc == true).Select(t => t.Id).ToList();
+ //var sysDocIdList = result.CurrentPageData.Where(t => t.IsSystemDoc == true).Select(t => t.Id).ToList();
- var identityUserIdList = result.CurrentPageData.Select(t => t.ConfirmUserId).Distinct().ToList();
+ //var identityUserIdList = result.CurrentPageData.Select(t => t.ConfirmUserId).Distinct().ToList();
- var sysDocUserTypeList = _systemDocNeedConfirmedUserTypeRepository.Where(t => sysDocIdList.Contains(t.SystemDocumentId)).Select(t => new { t.SystemDocumentId, t.UserTypeRole.UserTypeShortName }).ToList();
+ //var sysDocUserTypeList = _systemDocNeedConfirmedUserTypeRepository.Where(t => sysDocIdList.Contains(t.SystemDocumentId)).Select(t => new { t.SystemDocumentId, t.UserTypeRole.UserTypeShortName }).ToList();
- var identityUserUserTypeList = _identityUserRepository.Where(t => identityUserIdList.Contains(t.Id)).IgnoreQueryFilters().Select(t => new { IdentityUserId = t.Id, UserTypeList = t.UserRoleList.Where(t => t.IsUserRoleDisabled == false).Select(c => c.UserTypeRole.UserTypeShortName).ToList() });
+ //var identityUserUserTypeList = _identityUserRepository.Where(t => identityUserIdList.Contains(t.Id)).IgnoreQueryFilters().Select(t => new { IdentityUserId = t.Id, UserTypeList = t.UserRoleList.Where(t => t.IsUserRoleDisabled == false).Select(c => c.UserTypeRole.UserTypeShortName).ToList() });
- //Concat 不能用导航属性
- var sysids = result.CurrentPageData.Where(t => t.IsSystemDoc == true).Select(t => t.Id).ToList();
+ ////Concat 不能用导航属性
+ //var sysids = result.CurrentPageData.Where(t => t.IsSystemDoc == true).Select(t => t.Id).ToList();
- var sysDataList = await _systemDocumentRepository.Where(x => sysids.Contains(x.Id)).Include(x => x.SystemDocumentAttachmentList).ToListAsync();
+ //var sysDataList = await _systemDocumentRepository.Where(x => sysids.Contains(x.Id)).Include(x => x.SystemDocumentAttachmentList).ToListAsync();
- foreach (var item in result.CurrentPageData)
- {
+ //foreach (var item in result.CurrentPageData)
+ //{
- if (sysDataList.Any(y => y.Id == item.Id))
- {
- item.AttachmentCount = sysDataList.Where(y => y.Id == item.Id).Select(x => x.SystemDocumentAttachmentList.Where(z => !z.OffLine).Count()).FirstOrDefault();
- }
+ // if (sysDataList.Any(y => y.Id == item.Id))
+ // {
+ // item.AttachmentCount = sysDataList.Where(y => y.Id == item.Id).Select(x => x.SystemDocumentAttachmentList.Where(z => !z.OffLine).Count()).FirstOrDefault();
+ // }
- if (item.IsSystemDoc)
- {
- item.DocNeedSignUserTypeList = sysDocUserTypeList.Where(t => t.SystemDocumentId == item.Id).Select(t => t.UserTypeShortName).ToList();
- }
+ // if (item.IsSystemDoc)
+ // {
+ // item.DocNeedSignUserTypeList = sysDocUserTypeList.Where(t => t.SystemDocumentId == item.Id).Select(t => t.UserTypeShortName).ToList();
+ // }
- item.IdentityUserTypeList = identityUserUserTypeList.Where(t => t.IdentityUserId == item.ConfirmUserId).SelectMany(c => c.UserTypeList).ToList();
- }
+ // item.IdentityUserTypeList = identityUserUserTypeList.Where(t => t.IdentityUserId == item.ConfirmUserId).SelectMany(c => c.UserTypeList).ToList();
+ //}
#endregion
diff --git a/IRaCIS.Core.Application/Service/Document/_MapConfig.cs b/IRaCIS.Core.Application/Service/Document/_MapConfig.cs
index 4cff1cd34..da8d51638 100644
--- a/IRaCIS.Core.Application/Service/Document/_MapConfig.cs
+++ b/IRaCIS.Core.Application/Service/Document/_MapConfig.cs
@@ -29,7 +29,8 @@ namespace IRaCIS.Core.Application.Service
CreateMap();
CreateMap();
CreateMap();
- CreateMap();
+ CreateMap()
+ .ForMember(d => d.HistoricalVersionsCount, u => u.MapFrom(s => s.AuditDocumentOldVersionList.Count()));
CreateMap().ReverseMap();
CreateMap()
.ForMember(d => d.AttachmentCount, u => u.MapFrom(s => s.SystemDocumentAttachmentList.Count()))
@@ -43,7 +44,7 @@ namespace IRaCIS.Core.Application.Service
.ForMember(dest => dest.CreateUserRole, opt => opt.Ignore());
CreateMap()
- .ForMember(d => d.AttachmentCount, u => u.MapFrom(s =>s.TrialDocumentAttachmentList.Count()))
+ .ForMember(d => d.AttachmentCount, u => u.MapFrom(s => s.TrialDocumentAttachmentList.Count()))
.ForMember(d => d.FileType, u => u.MapFrom(s => isEn_Us ? s.FileType.Value : s.FileType.ValueCN))
.ForMember(d => d.IsSomeUserSigned, u => u.MapFrom(s => s.TrialDocConfirmedUserList.Any(t => t.ConfirmTime != null)))
.ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path));
@@ -81,7 +82,7 @@ namespace IRaCIS.Core.Application.Service
CreateMap();
- CreateMap ();
+ CreateMap();
CreateMap()
@@ -94,13 +95,14 @@ namespace IRaCIS.Core.Application.Service
CreateMap().ForMember(d => d.NeedConfirmedUserTypeList, c => c.MapFrom(t => t.NeedConfirmedUserTypeIdList));
- CreateMap().EqualityComparison((odto, o) => odto == o.NeedConfirmUserTypeId)
+ CreateMap()
+ .EqualityComparison((odto, o) => odto == o.NeedConfirmUserTypeId)
.ForMember(d => d.NeedConfirmUserTypeId, c => c.MapFrom(t => t))
.ForMember(d => d.SystemDocumentId, c => c.Ignore());
CreateMap()
- .ForMember(d => d.SysCriterionTypeList, c => c.MapFrom(t => t.SysEmailNoticeConfig.CriterionTypeList))
+ .ForMember(d => d.SysCriterionTypeList, c => c.MapFrom(t => t.SysEmailNoticeConfig.CriterionTypeList))
.ForMember(d => d.TrialCriterionName, c => c.MapFrom(t => t.TrialReadingCriterion.CriterionName))
.ForMember(d => d.TrialEmailNoticeUserList, c => c.MapFrom(t => t.TrialEmailNoticeUserList));
@@ -146,6 +148,26 @@ namespace IRaCIS.Core.Application.Service
CreateMap();
CreateMap().ReverseMap();
+
+
+
+ CreateMap()
+ .ForMember(d => d.IdentityUserList, c => c.MapFrom(t => t.AuditRecordIdentityUserList));
+
+ CreateMap()
+ /* .ForMember(d => d.AuditRecordIdentityUserList, c => c.MapFrom(t => t.IdnetityUserIdList))*/;
+
+ CreateMap()
+ .EqualityComparison((odto, o) => odto == o.IdentityUserId)
+ .ForMember(d => d.AuditRecordId, c => c.Ignore())
+ .ForMember(d => d.IdentityUserId, c => c.MapFrom(t => t));
+
+
+ CreateMap()
+ .ForMember(d => d.Id, c => c.MapFrom(t => t.IdentityUser.Id))
+ .ForMember(d => d.UserName, c => c.MapFrom(t => t.IdentityUser.UserName))
+ .ForMember(d => d.FullName, c => c.MapFrom(t => t.IdentityUser.FullName));
+
}
}
diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DownloadAndUploadDTO.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DownloadAndUploadDTO.cs
index 3124f5d84..6e5748018 100644
--- a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DownloadAndUploadDTO.cs
+++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DownloadAndUploadDTO.cs
@@ -102,6 +102,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc.DTO
public string BodyPartForEdit { get; set; }
+ public string BodyPartForEditOther { get; set; }
+
public DateTime? StudyTime { get; set; }
public string Modalities { get; set; }
@@ -126,6 +128,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc.DTO
public string BodyPart { get; set; }
+ public string BodyPartForEditOther { get; set; }
+
public string Modality { get; set; }
public DateTime ImageDate { get; set; }
diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs
index 21c9dd018..ff33f0b44 100644
--- a/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs
+++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs
@@ -190,6 +190,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
ModalityForEdit = t.ModalityForEdit,
BodyPartExamined = t.BodyPartExamined,
BodyPartForEdit = t.BodyPartForEdit,
+ BodyPartForEditOther = t.BodyPartForEditOther,
StudyCode = t.StudyCode,
StudyTime = t.StudyTime,
@@ -209,6 +210,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
ModalityForEdit = t.ModalityForEdit,
BodyPartExamined = t.BodyPartExamined,
BodyPartForEdit = t.BodyPartForEdit,
+ BodyPartForEditOther = t.BodyPartForEditOther,
StudyCode = t.StudyCode,
StudyTime = t.StudyTime,
@@ -435,7 +437,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
studyMonitor.RecordPath = incommand.RecordPath;
studyMonitor.FileSize = incommand.Study.SeriesList.SelectMany(t => t.InstanceList).Sum(t => t.FileSize);
- var studyId = IdentifierHelper.CreateGuid(incommand.Study.StudyInstanceUid, incommand.TrialId.ToString());
+ var studyId = IdentifierHelper.CreateGuid(incommand.Study.StudyInstanceUid, incommand.TrialId.ToString(), visiTaskId.ToString());
var findStudy = await _taskStudyRepository.FirstOrDefaultAsync(t => t.Id == studyId);
@@ -471,7 +473,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
}
- study.Id = IdentifierHelper.CreateGuid(incommand.Study.StudyInstanceUid, incommand.TrialId.ToString(), visiTaskId.ToString());
+ study.Id = studyId;
study.TrialId = incommand.TrialId;
study.SubjectId = incommand.SubjectId;
study.VisitTaskId = visiTaskId;
@@ -524,6 +526,9 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
await _taskInstanceRepository.AddAsync(isntance);
}
}
+
+
+ findStudy = study;
}
else
{
@@ -568,35 +573,71 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
//新的序列 那么 检查的序列数量+1
findStudy.SeriesCount += 1;
}
- else
- {
- //该序列掉了instance
- dicomSeries.InstanceCount += seriesItem.InstanceList.Count;
- }
+ //else
+ //{
+ // //该序列掉了instance
+ // dicomSeries.InstanceCount += seriesItem.InstanceList.Count;
+ //}
+
+ //找到该序列已经存在的instanceId
+ var existInstanceIdList = _taskInstanceRepository.Where(t => t.SeriesId == dicomSeries.Id).Select(t => t.Id).ToList();
foreach (var instanceItem in seriesItem.InstanceList)
{
var insntance = _mapper.Map(instanceItem);
- insntance.Id = IdentifierHelper.CreateGuid(insntance.StudyInstanceUid, insntance.SeriesInstanceUid, insntance.SopInstanceUid, trialId.ToString(), visiTaskId.ToString());
- insntance.StudyId = findStudy.Id;
- insntance.SeriesId = dicomSeries.Id;
- insntance.TrialId = incommand.TrialId;
- insntance.SubjectId = incommand.SubjectId;
- insntance.VisitTaskId = visiTaskId;
+ var instanceId = IdentifierHelper.CreateGuid(insntance.StudyInstanceUid, insntance.SeriesInstanceUid, insntance.SopInstanceUid, trialId.ToString(), visiTaskId.ToString());
+
+
+ if (!existInstanceIdList.Any(t => t == instanceId))
+ {
+ insntance.Id = instanceId;
+ insntance.StudyId = findStudy.Id;
+ insntance.SeriesId = dicomSeries.Id;
+
+ insntance.TrialId = incommand.TrialId;
+ insntance.SubjectId = incommand.SubjectId;
+ insntance.VisitTaskId = visiTaskId;
+
+ await _taskInstanceRepository.AddAsync(insntance);
+
+ dicomSeries.InstanceCount++;
+ findStudy.InstanceCount++;
+ }
- await _taskInstanceRepository.AddAsync(insntance);
}
-
-
// 不管是新的序列 还是 该序列 掉了Instance 重传的时候 检查的instance 数量都会增加
- findStudy.InstanceCount += seriesItem.InstanceList.Count;
+ //findStudy.InstanceCount += seriesItem.InstanceList.Count;
}
}
+ #region 只配置单个部位自动赋值
+
+ var originStudy = _dicomStudyRepository.Where(t => t.TrialId == incommand.TrialId && t.StudyInstanceUid == findStudy.StudyInstanceUid).FirstOrDefault();
+
+ if (originStudy != null)
+ {
+ findStudy.BodyPartForEdit = originStudy.BodyPartForEdit;
+
+ findStudy.BodyPartForEditOther = originStudy.BodyPartForEditOther;
+ }
+
+
+ ////项目配置的影像部位
+ //var trialBodyParts = _trialRepository.Where(t => t.Id == trialId).Select(t => t.BodyPartTypes).FirstOrDefault();
+
+ //var trialBodyPartList = trialBodyParts.Split('|', StringSplitOptions.RemoveEmptyEntries);
+
+ //if (trialBodyPartList.Count() == 1)
+ //{
+ // var first = trialBodyPartList.First();
+ // findStudy.BodyPartForEdit = first;
+ //}
+ #endregion
+
var @lock2 = _distributedLockProvider.CreateLock($"StudyCommit");
using (await @lock2.AcquireAsync())
@@ -722,6 +763,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
Description = ns.Description,
ImageDate = ns.ImageDate,
BodyPart = ns.BodyPart,
+ BodyPartForEditOther=ns.BodyPartForEditOther,
FileCount = ns.FileCount,
Modality = ns.Modality,
StudyCode = ns.StudyCode,
@@ -974,6 +1016,13 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
subjectCode = inQuery.SubjectCode;
}
+ //下载这里查看也需要维护编号,维护的时候不会处理一致性分析任务
+ if (inQuery.SubjectId != null && inQuery.TrialReadingCriterionId != null)
+ {
+ await SubejctRandomReadingTaskNameDeal((Guid)subjectId, inQuery.TrialReadingCriterionId);
+ }
+
+
TaskState? taskState = null;
if (inQuery.VisitTaskId != null)
{
diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs
index 8d9ff79d4..e46c85f9f 100644
--- a/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs
+++ b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs
@@ -323,18 +323,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
}
- #region 只配置单个部位自动赋值
- //项目配置的影像部位
- var trialBodyParts = _trialRepository.Where(t => t.Id == trialId).Select(t => t.BodyPartTypes).FirstOrDefault();
-
- var trialBodyPartList = trialBodyParts.Split('|', StringSplitOptions.RemoveEmptyEntries);
-
- if (trialBodyPartList.Count() == 1)
- {
- var first = trialBodyPartList.First();
- findStudy.BodyPartForEdit = first;
- }
- #endregion
+
var @lock2 = _distributedLockProvider.CreateLock($"StudyCommit");
@@ -579,7 +568,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
///
[HttpGet("{subjectVisitId:guid}")]
[AllowAnonymous]
- public IResponseOutput> GetAllRelationStudyList(Guid subjectVisitId,bool? isReading)
+ public IResponseOutput> GetAllRelationStudyList(Guid subjectVisitId, bool? isReading)
{
#region 废弃
//var studylist = _studyRepository.Where(u => u.SubjectVisitId == subjectVisitId && u.IsDeleted == false).Select(t => new { StudyId = t.Id, t.SubjectId, t.TrialId }).ToList();
@@ -608,17 +597,17 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
var studyInfo = _dicomStudyRepository.Where(u => u.SubjectVisitId == subjectVisitId).Select(t => new { t.SubjectId, t.TrialId }).FirstOrDefault().IfNullThrowException();
var list = _dicomStudyRepository.Where(t => t.SubjectVisitId != subjectVisitId && t.TrialId == studyInfo.TrialId && t.SubjectId == studyInfo.SubjectId)
- .Select(t=> new RelationStudyDTO()
+ .Select(t => new RelationStudyDTO()
{
StudyId = t.Id,
StudyCode = t.StudyCode,
VisitName = t.SubjectVisit.VisitName,
Modalities = t.Modalities,
Description = t.Description,
- SeriesCount = t.SeriesList.Where(t=> isReading==true? t.IsReading==true:true).Count()
- }) .ToList();
+ SeriesCount = t.SeriesList.Where(t => isReading == true ? t.IsReading == true : true).Count()
+ }).ToList();
- list = list.Where(t=>t.SeriesCount>0).OrderBy(u => u.VisitName).ThenBy(s => s.StudyCode).ToList();
+ list = list.Where(t => t.SeriesCount > 0).OrderBy(u => u.VisitName).ThenBy(s => s.StudyCode).ToList();
return ResponseOutput.Ok(list);
}
@@ -628,7 +617,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
/// Dicom检查的Id
[HttpGet, Route("{studyId:guid}")]
[AllowAnonymous]
- public IResponseOutput Item(Guid studyId,bool? isPacs)
+ public IResponseOutput Item(Guid studyId, bool? isPacs)
{
if (isPacs == true)
{
@@ -861,7 +850,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
- var verifyStudyInfo = _dicomStudyRepository.Where(t => t.TrialId == trialId && t.Id == expectStudyId,false,true).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault();
+ var verifyStudyInfo = _dicomStudyRepository.Where(t => t.TrialId == trialId && t.Id == expectStudyId, false, true).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault();
result.StudyInfo = verifyStudyInfo;
diff --git a/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs b/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs
index d711165cd..90c41fc23 100644
--- a/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs
+++ b/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs
@@ -292,6 +292,8 @@ namespace IRaCIS.Application.Contracts
public string OrganizationName { get; set; } = string.Empty;
public Guid? UserType { get; set; }
+ public UserTypeEnum? UserTypeEnum { get; set; }
+
public bool? IsTestUser { get; set; }
public bool? IsZhiZhun { get; set; }
@@ -313,6 +315,10 @@ namespace IRaCIS.Application.Contracts
public DateTime? EndLastChangePassWordTime { get; set; }
public UserCeateSource? UserCeateSource { get; set; }
+
+ public Guid? AuditRecordId { get; set; }
+
+ public bool? IsAuditRecordUserSelect { get; set; }
}
public class UserRoleInfoDTO
@@ -352,6 +358,9 @@ namespace IRaCIS.Application.Contracts
#endregion
public int JoinedTrialCount { get; set; }
+
+
+ public bool IsAuditRecordUserSelect { get; set; }
}
diff --git a/IRaCIS.Core.Application/Service/Management/Interface/IUserService.cs b/IRaCIS.Core.Application/Service/Management/Interface/IUserService.cs
index f3a745821..09ef1af63 100644
--- a/IRaCIS.Core.Application/Service/Management/Interface/IUserService.cs
+++ b/IRaCIS.Core.Application/Service/Management/Interface/IUserService.cs
@@ -9,7 +9,7 @@ namespace IRaCIS.Core.Application.Service
Task> AddUser(UserCommand userAddModel);
//Task DeleteUser(Guid userId);
//Task GetUser(Guid id);
- Task> GetUserList(UserListQueryDTO param);
+ //Task> GetUserList(UserListQueryDTO param);
//Task> Login(string userName, string password);
Task VerifyMFACodeAsync(string Code);
diff --git a/IRaCIS.Core.Application/Service/Management/UserService.cs b/IRaCIS.Core.Application/Service/Management/UserService.cs
index 67e4f2e06..f602ec53e 100644
--- a/IRaCIS.Core.Application/Service/Management/UserService.cs
+++ b/IRaCIS.Core.Application/Service/Management/UserService.cs
@@ -495,7 +495,7 @@ namespace IRaCIS.Core.Application.Service
///
///
[HttpPost]
- public async Task> GetUserList(UserListQueryDTO inQuery)
+ public async Task> GetUserList(UserListQueryDTO inQuery, [FromServices] IRepository _auditRecordIdentityUserRepository)
{
var userQueryable = _identityUserRepository.Where(x => x.UserRoleList.Any(x => x.UserTypeEnum != UserTypeEnum.SuperAdmin))
@@ -510,14 +510,36 @@ namespace IRaCIS.Core.Application.Service
.WhereIf(inQuery.EndLastLoginTime != null, t => t.LastLoginTime <= inQuery.EndLastLoginTime)
.WhereIf(inQuery.BeginLastChangePassWordTime != null, t => t.LastChangePassWordTime >= inQuery.BeginLastChangePassWordTime)
.WhereIf(inQuery.EndLastChangePassWordTime != null, t => t.LastChangePassWordTime <= inQuery.EndLastChangePassWordTime)
- .WhereIf(inQuery.UserType != null, t => t.UserRoleList.Any(t => t.UserTypeId == inQuery.UserType && t.IsUserRoleDisabled==false))
+ .WhereIf(inQuery.UserType != null, t => t.UserRoleList.Any(t => t.UserTypeId == inQuery.UserType && t.IsUserRoleDisabled == false))
+ .WhereIf(inQuery.UserTypeEnum != null, t => t.UserRoleList.Any(t => t.UserTypeRole.UserTypeEnum == inQuery.UserTypeEnum && t.IsUserRoleDisabled == false))
.WhereIf(inQuery.UserState != null, t => t.Status == inQuery.UserState)
.WhereIf(inQuery.IsTestUser != null, t => t.IsTestUser == inQuery.IsTestUser)
.WhereIf(inQuery.IsZhiZhun != null, t => t.IsZhiZhun == inQuery.IsZhiZhun)
.WhereIf(inQuery.UserCeateSource != null, t => t.UserCeateSource == inQuery.UserCeateSource)
+ .WhereIf(inQuery.AuditRecordId != null && inQuery.IsAuditRecordUserSelect == true, t => t.AuditRecordList.Any(t => t.AuditRecordId == inQuery.AuditRecordId))
+ .WhereIf(inQuery.AuditRecordId != null && inQuery.IsAuditRecordUserSelect == false, t => !t.AuditRecordList.Any(t => t.AuditRecordId == inQuery.AuditRecordId))
.ProjectTo(_mapper.ConfigurationProvider);
- return await userQueryable.ToPagedListAsync(inQuery);
+ var pageList = await userQueryable.ToPagedListAsync(inQuery);
+
+
+ if (inQuery.AuditRecordId != null)
+ {
+ var selectIdList = _auditRecordIdentityUserRepository.Where(t => t.AuditRecordId == inQuery.AuditRecordId).Select(t => t.IdentityUserId).ToList();
+
+ foreach (var item in selectIdList)
+ {
+ var find = pageList.CurrentPageData.Where(t => t.Id == item).FirstOrDefault();
+
+ if (find != null)
+ {
+ find.IsAuditRecordUserSelect = true;
+
+ }
+ }
+ }
+
+ return pageList;
}
@@ -863,8 +885,8 @@ namespace IRaCIS.Core.Application.Service
.WhereIf(inQuery.OptTypeList != null && inQuery.OptTypeList.Count > 0, t => inQuery.OptTypeList.Contains(t.OptType))
.WhereIf(inQuery.BeginDate != null, t => t.CreateTime >= inQuery.BeginDate)
.WhereIf(inQuery.EndDate != null, t => t.CreateTime <= inQuery.EndDate)
- .WhereIf(inQuery.IsLoginUncommonly != null , t => t.IsLoginUncommonly== inQuery.IsLoginUncommonly)
-
+ .WhereIf(inQuery.IsLoginUncommonly != null, t => t.IsLoginUncommonly == inQuery.IsLoginUncommonly)
+
.WhereIf(!string.IsNullOrEmpty(inQuery.LoginUserName), t => t.ActionUserName.Contains(inQuery.LoginUserName!))
.WhereIf(!string.IsNullOrEmpty(inQuery.LoginFaildName), t => t.ActionUserName.Contains(inQuery.LoginFaildName!))
.WhereIf(!string.IsNullOrEmpty(inQuery.IP), t => t.IP.Contains(inQuery.IP!))
@@ -922,7 +944,7 @@ namespace IRaCIS.Core.Application.Service
var password = loginDto.Password;
var emailConfig = _emailConfig.CurrentValue;
- var companyInfo = new SystemEmailSendConfigView() { CompanyName = emailConfig.CompanyName, CompanyNameCN = emailConfig.CompanyNameCN, CompanyShortName = emailConfig.CompanyShortName, CompanyShortNameCN = emailConfig.CompanyShortNameCN,SystemShortName=emailConfig.SystemShortName ,EmailRegexStr=emailConfig.EmailRegexStr};
+ var companyInfo = new SystemEmailSendConfigView() { CompanyName = emailConfig.CompanyName, CompanyNameCN = emailConfig.CompanyNameCN, CompanyShortName = emailConfig.CompanyShortName, CompanyShortNameCN = emailConfig.CompanyShortNameCN, SystemShortName = emailConfig.SystemShortName, EmailRegexStr = emailConfig.EmailRegexStr };
int maxFailures = _verifyConfig.CurrentValue.LoginMaxFailCount;
@@ -991,7 +1013,7 @@ namespace IRaCIS.Core.Application.Service
//超过90天没修改密码
- if (loginUser!= null&&_verifyConfig.CurrentValue.IsNeedChangePassWord && loginUser.LastChangePassWordTime != null && DateTime.Now.AddDays(-_verifyConfig.CurrentValue.ChangePassWordDays) > loginUser.LastChangePassWordTime.Value)
+ if (loginUser != null && _verifyConfig.CurrentValue.IsNeedChangePassWord && loginUser.LastChangePassWordTime != null && DateTime.Now.AddDays(-_verifyConfig.CurrentValue.ChangePassWordDays) > loginUser.LastChangePassWordTime.Value)
{
loginUser.NeedChangePassWord = true;
}
@@ -1002,7 +1024,7 @@ namespace IRaCIS.Core.Application.Service
UserOptType.LoginLockedAccount
};
- var actionUserName= loginUser!= null ? loginUser.UserName : userName;
+ var actionUserName = loginUser != null ? loginUser.UserName : userName;
var lastLoginIPRegion = await _userLogRepository.Where(t => t.ActionUserName == actionUserName && userOptTypes.Contains(t.OptType))
.OrderByDescending(t => t.CreateTime).Select(t => t.IPRegion).FirstOrDefaultAsync();
@@ -1022,7 +1044,7 @@ namespace IRaCIS.Core.Application.Service
//异地登录
loginUser.LoginState = 2;
-
+
}
}
}
diff --git a/IRaCIS.Core.Application/Service/QC/DTO/NoneDicomStudyViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/NoneDicomStudyViewModel.cs
index 915b65ce2..bce0b65c3 100644
--- a/IRaCIS.Core.Application/Service/QC/DTO/NoneDicomStudyViewModel.cs
+++ b/IRaCIS.Core.Application/Service/QC/DTO/NoneDicomStudyViewModel.cs
@@ -81,6 +81,8 @@ namespace IRaCIS.Core.Application.Contracts
public string VideoUrl { get; set; } = string.Empty;
+ public string BodyPartForEditOther { get; set; }
+
}
diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs
index 8dee5be56..396b6213d 100644
--- a/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs
+++ b/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs
@@ -39,6 +39,8 @@ namespace IRaCIS.Core.Application.Contracts.DTO
public string BodyPart { get; set; } = String.Empty;
+ public string BodyPartForEditOther { get; set; }
+
}
public class QCQuestionAnswerCommand
@@ -427,6 +429,8 @@ namespace IRaCIS.Core.Application.Contracts.DTO
public bool IsCompleteClinicalData { get; set; }
+ public string BodyPartForEditOther { get; set; }
+
}
public class QASeriesInfoDto
diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs
index 128b15cfe..0e8cc66f4 100644
--- a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs
+++ b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs
@@ -518,7 +518,11 @@ namespace IRaCIS.Core.Application.Contracts
public int? CheckWaitReplyCount { get; set; }
+ public string SuspendReason { get; set; }
+ public string Reason { get; set; }
+
+ public string ExportReason => Status == SubjectStatus.OutOfVisit ? Reason : (Status == SubjectStatus.EndOfVisit ? SuspendReason : "");
}
@@ -1795,7 +1799,7 @@ namespace IRaCIS.Core.Application.Contracts
public decimal VisitNum { get; set; }
[DictionaryTranslateAttribute("Subject_Visit_Status")]
- public SubjectStatus SubjectStatus { get; set; }
+ public SubjectStatus SubjectStatus { get; set; }
public string SubjectCode { get; set; } = String.Empty;
public String TrialSiteCode { get; set; } = String.Empty;
diff --git a/IRaCIS.Core.Application/Service/QC/QCOperationService.cs b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs
index 08b304e74..300cbbeee 100644
--- a/IRaCIS.Core.Application/Service/QC/QCOperationService.cs
+++ b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs
@@ -541,6 +541,7 @@ namespace IRaCIS.Core.Application.Image.QA
sv.CheckUserId = _userInfo.UserRoleId;
sv.CheckState = CheckStateEnum.CVPassed;
+ sv.CheckTime = DateTime.Now;
sv.ReadingStatus = ReadingStatusEnum.TaskAllocate;
@@ -1066,6 +1067,7 @@ namespace IRaCIS.Core.Application.Image.QA
study.BodyPartForEdit = updateModalityCommand.BodyPart;
+ study.BodyPartForEditOther = updateModalityCommand.BodyPartForEditOther;
study.ModalityForEdit = updateModalityCommand.Modality;
study.StudyName = updateModalityCommand.StudyName;
diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs
index ba9f91aff..1aab4758a 100644
--- a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs
+++ b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs
@@ -446,6 +446,11 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
}
+ public class ViewStudyPartInDto
+ {
+ public Guid VisitTaskId { get; set; }
+ }
+
public class DeleteSingleTableQuestionMarkInDto
{
[NotDefault]
@@ -855,12 +860,6 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
///
public string? PicturePath { get; set; }
- ///
- /// 第一次添加的任务ID
- ///
- public decimal FristAddTaskNum { get; set; } = 0;
-
-
public SplitOrMergeType? SplitOrMergeType { get; set; }
///
@@ -1033,6 +1032,8 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
public string MergeName { get; set; }
+ public decimal FristAddTaskNum { get; set; }
+
///
/// 病灶类型
///
@@ -1867,6 +1868,11 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
///
public int? DigitPlaces { get; set; } = 2;
+ ///
+ /// 是否查看检查部位
+ ///
+ public bool IsViewStudyPart { get; set; } = false;
+
///
/// 标准类型
@@ -2350,8 +2356,6 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
public bool? IsCanEditPosition { get; set; }
- public decimal FristAddTaskNum { get; set; } = 0;
-
public decimal? WW { get; set; }
public decimal? WL { get; set; }
diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/VisitTaskDto.cs b/IRaCIS.Core.Application/Service/Reading/Dto/VisitTaskDto.cs
index f322de639..19601b00a 100644
--- a/IRaCIS.Core.Application/Service/Reading/Dto/VisitTaskDto.cs
+++ b/IRaCIS.Core.Application/Service/Reading/Dto/VisitTaskDto.cs
@@ -12,6 +12,11 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
public string TaskBlindName { get; set; } = string.Empty;
+ ///
+ /// 是否查看检查部位
+ ///
+ public bool IsViewStudyPart { get; set; } = false;
+
//任务来源访视Id 方便回更访视读片状态
public Guid? SourceSubjectVisitId { get; set; }
public Guid? SouceReadModuleId { get; set; }
diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs
index 1e67a961b..d5b7ad04b 100644
--- a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs
+++ b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs
@@ -35,6 +35,7 @@ namespace IRaCIS.Core.Application.Service
IRepository _noneDicomStudyRepository,
IRepository _visitTaskRepository,
IRepository _trialRepository,
+ IRepository _taskInstanceRepository,
IRepository _noneDicomStudyFileRepository,
IRepository _readingNoneDicomMarkRepository,
IRepository _userLogRepository,
@@ -93,6 +94,22 @@ namespace IRaCIS.Core.Application.Service
}
#endregion
+ ///
+ /// 查看检查部位
+ ///
+ ///
+ ///
+ [HttpPost]
+ public async Task ViewStudyPart(ViewStudyPartInDto inDto)
+ {
+ await _visitTaskRepository.UpdatePartialFromQueryAsync(x => x.Id == inDto.VisitTaskId, x => new VisitTask()
+ {
+ IsViewStudyPart = true,
+ });
+ return await _visitTaskRepository.SaveChangesAsync();
+ }
+
+
///
/// 删除单个表格问题标记
@@ -2602,7 +2619,7 @@ namespace IRaCIS.Core.Application.Service
await VerifyTaskIsSign(inDto.VisitTaskId);
if (inDto.InstanceId != null && inDto.IsDicomReading)
{
- if (!(await _dicomInstanceRepository.AnyAsync(x => x.Id == inDto.InstanceId && x.SeriesId == inDto.SeriesId)))
+ if ((!(await _dicomInstanceRepository.AnyAsync(x => x.Id == inDto.InstanceId && x.SeriesId == inDto.SeriesId))) && (!(await _taskInstanceRepository.AnyAsync(x => x.Id == inDto.InstanceId && x.SeriesId == inDto.SeriesId))))
{
throw new BusinessValidationFailedException(_localizer["ReadingImage_Idnotcorrespond"]);
}
@@ -3178,7 +3195,9 @@ namespace IRaCIS.Core.Application.Service
var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync();
var isBaseline = await _subjectVisitRepository.Where(x => x.Id == taskInfo.SourceSubjectVisitId).Select(x => x.IsBaseLine).FirstOrDefaultAsync();
- var readingQuestionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == taskInfo.TrialReadingCriterionId && x.Type != "group"
+
+
+ var readingQuestionList = await _readingQuestionTrialRepository.Where(x => !x.ExcludeShowVisitList.Contains(taskInfo.VisitTaskNum) &&x.ReadingQuestionCriterionTrialId == taskInfo.TrialReadingCriterionId && x.Type != "group"
&& (x.IsJudgeQuestion || (x.IsRequired == IsRequired.Required && x.ShowQuestion == ShowQuestion.Show))
).ToListAsync();
if (isBaseline)
@@ -3622,6 +3641,7 @@ namespace IRaCIS.Core.Application.Service
task.ReadingVersionEnum = criterionInfo.ReadingVersionEnum;
task.ReadingToolList = criterionInfo.ReadingToolList;
task.IsExistUnprocessedFeedback = await _userFeedBackRepository.AnyAsync(x => x.VisitTaskId == task.VisitTaskId && x.State == 0);
+ task.IsViewStudyPart= visitTaskInfo.IsViewStudyPart;
// 添加默认答案
if (inDto.VisitTaskId == null && visitTaskInfo.ReadingTaskState != ReadingTaskState.HaveSigned)
{
diff --git a/IRaCIS.Core.Application/Service/Reading/_MapConfig.cs b/IRaCIS.Core.Application/Service/Reading/_MapConfig.cs
index e7f8fc11d..a56843f78 100644
--- a/IRaCIS.Core.Application/Service/Reading/_MapConfig.cs
+++ b/IRaCIS.Core.Application/Service/Reading/_MapConfig.cs
@@ -280,6 +280,7 @@ namespace IRaCIS.Core.Application.Service
.ForMember(d => d.ReadingQuestionSystemId, u => u.MapFrom(s => s.Id));
CreateMap()
+ .ForMember(d => d.FristAddTaskNum, u => u.MapFrom(s => s.FristAddTask.VisitTaskNum))
.ForMember(dest => dest.CreateUserRole, opt => opt.Ignore())
.ForMember(d => d.MergeName, u => u.MapFrom(s => s.MergeRow == null ? string.Empty : s.MergeRow.ReadingQuestionTrial.OrderMark + s.MergeRow.RowIndex.GetLesionMark()))
.ForMember(d => d.SplitName, u => u.MapFrom(s => s.SplitRow == null ? string.Empty : s.SplitRow.ReadingQuestionTrial.OrderMark + s.SplitRow.RowIndex.GetLesionMark()))
diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/General/GeneralCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/General/GeneralCalculateService.cs
index 0176f0db8..465fe64e2 100644
--- a/IRaCIS.Core.Application/Service/ReadingCalculate/General/GeneralCalculateService.cs
+++ b/IRaCIS.Core.Application/Service/ReadingCalculate/General/GeneralCalculateService.cs
@@ -179,7 +179,7 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
var baseLineVisitId = await _subjectVisitRepository.Where(x => x.SubjectId == visitTask.SubjectId && x.IsBaseLine).Select(x => x.Id).FirstOrDefaultAsync();
- var rowInfoList = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == visitTaskId).ToListAsync();
+ var rowInfoList = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == visitTaskId).Include(x=>x.FristAddTask).ToListAsync();
var baseLinetaskId = await _visitTaskRepository.Where(x => x.SourceSubjectVisitId == baseLineVisitId && x.TaskState == TaskState.Effect
&& x.TrialReadingCriterionId == visitTask.TrialReadingCriterionId
@@ -228,7 +228,7 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
RowIndex = x.RowIndex,
MeasureData = x.MeasureData,
OtherMeasureData = x.OtherMeasureData,
- FristAddTaskNum = x.FristAddTaskNum,
+ FristAddTaskNum = x.FristAddTask.VisitTaskNum,
TableQuestionList = tableQuestion.Where(y => y.QuestionId == item.QuestionId && y.RowId == x.Id).ToList(),
}).ToList();
diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/IVUSCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/IVUSCalculateService.cs
index 5ba788bb6..430bfdcf8 100644
--- a/IRaCIS.Core.Application/Service/ReadingCalculate/IVUSCalculateService.cs
+++ b/IRaCIS.Core.Application/Service/ReadingCalculate/IVUSCalculateService.cs
@@ -702,7 +702,6 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
IsCurrentTaskAdd = true,
BlindName = taskinfo.TaskBlindName,
OrderMark = questionInfo.OrderMark,
- FristAddTaskNum = taskinfo.VisitTaskNum,
FristAddTaskId = taskinfo.Id,
RowMark = questionInfo.OrderMark + decimal.Parse(maxnum.ToString()).GetLesionMark()
});
@@ -842,7 +841,6 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
IsCurrentTaskAdd = true,
BlindName = inDto.BlindName,
OrderMark = questionInfo.OrderMark,
- FristAddTaskNum = inDto.VisitTaskNum,
FristAddTaskId = inDto.VisitTaskId,
RowMark = questionInfo.OrderMark + decimal.Parse(item.ToString()).GetLesionMark()
});
@@ -903,7 +901,6 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
IsCurrentTaskAdd = true,
BlindName = inDto.BlindName,
OrderMark = pAVquestionInfo.OrderMark,
- FristAddTaskNum = inDto.VisitTaskNum,
FristAddTaskId = inDto.VisitTaskId,
RowMark = pAVquestionInfo.OrderMark + decimal.Parse(item.ToString()).GetLesionMark()
});
diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/MRECISTHCCCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/MRECISTHCCCalculateService.cs
index 4a1c64ea1..2f2ab5243 100644
--- a/IRaCIS.Core.Application/Service/ReadingCalculate/MRECISTHCCCalculateService.cs
+++ b/IRaCIS.Core.Application/Service/ReadingCalculate/MRECISTHCCCalculateService.cs
@@ -867,30 +867,59 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
TrialId = x.TrialId,
}).ToListAsync();
- foreach (var item in recistTableAnswers)
- {
+
+ // 肛门淋巴结
+ var portalOrgan = await _organInfoRepository.Where(x => x.SystemCriterionId == trialReadingCriterion.ReadingQuestionCriterionSystemId &&
+
+ x.Part == "肝门淋巴结" &&
- item.QuestionId = tableRowAnswers.Where(y => y.OriginalId == item.RowId).Select(x => x.QuestionId).FirstOrDefault();
- item.TableQuestionId = tableQuestionList.Where(x => x.ReadingQuestionId == item.QuestionId && x.QuestionMark == item.QuestionMark).Select(x => x.Id).FirstOrDefault();
- }
+ x.IsLymphNodes == IsLymph.Yes
+ ).FirstOrDefaultAsync();
+
+ foreach (var item in recistTableAnswers)
+ {
+ var rowinfo = tableRowAnswers.Where(y => y.OriginalId == item.RowId).FirstOrDefault();
+ if (rowinfo != null)
+ {
+ item.QuestionId = rowinfo.QuestionId;
+
+
+ if (portalOrgan != null && rowinfo.OrganInfoId == portalOrgan.Id)
+ {
+ var IslymphNode = recistTableAnswers.Where(x => x.RowId == item.RowId && x.QuestionMark == QuestionMark.IsLymph).Select(x => x.Answer).FirstIsNullReturnEmpty().EqEnum(YesOrNoOrNa.Yes);
+ var minorAxis = recistTableAnswers.Where(x => x.RowId == item.RowId && x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Answer).FirstIsNullReturnEmpty().IsNullOrEmptyReturn0();
+
+ var iSbetween15and20 = minorAxis >= 15 && minorAxis < 20;
+ if (item.QuestionMark == QuestionMark.State && IslymphNode && iSbetween15and20 && rowinfo.LesionType == LesionType.TargetLesion)
+ {
+ item.Answer = string.Empty;
+ }
+ }
+
+ }
+
+
+ item.TableQuestionId = tableQuestionList.Where(x => x.ReadingQuestionId == item.QuestionId && x.QuestionMark == item.QuestionMark).Select(x => x.Id).FirstOrDefault();
+ }
recistTableAnswers = recistTableAnswers.Where(x => x.TableQuestionId != default(Guid)).ToList();
- List notNeedCopyMarks = new List()
- {
-
- };
- var tableAnswers = recistTableAnswers.Select(x => new ReadingTableQuestionAnswer
- {
- Id = NewId.NextGuid(),
- Answer = notNeedCopyMarks.Contains(x.QuestionMark) ? string.Empty : x.Answer,
- QuestionId = x.QuestionId,
- RowIndex = x.RowIndex,
- RowId = tableRowAnswers.Where(y => y.OriginalId == x.RowId).Select(x => x.Id).FirstOrDefault(),
- TableQuestionId = x.TableQuestionId,
- TrialId = x.TrialId,
- VisitTaskId = visitTaskId,
- }).ToList();
+ List notNeedCopyMarks = new List()
+ {
+
+ };
+
+ var tableAnswers = recistTableAnswers.Select(x => new ReadingTableQuestionAnswer
+ {
+ Id = NewId.NextGuid(),
+ Answer = notNeedCopyMarks.Contains(x.QuestionMark) ? string.Empty : x.Answer,
+ QuestionId = x.QuestionId,
+ RowIndex = x.RowIndex,
+ RowId = tableRowAnswers.Where(y => y.OriginalId == x.RowId).Select(x => x.Id).FirstOrDefault(),
+ TableQuestionId = x.TableQuestionId,
+ TrialId = x.TrialId,
+ VisitTaskId = visitTaskId,
+ }).ToList();
// 添加 典型肝内病灶 默认值
tableRowAnswers.ForEach(x =>
diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/MRIPDFFCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/MRIPDFFCalculateService.cs
index d577c2a79..c368029d9 100644
--- a/IRaCIS.Core.Application/Service/ReadingCalculate/MRIPDFFCalculateService.cs
+++ b/IRaCIS.Core.Application/Service/ReadingCalculate/MRIPDFFCalculateService.cs
@@ -414,7 +414,6 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
rowlist.Add(new ReadingTableAnswerRowInfo()
{
FristAddTaskId = visitTaskId,
- FristAddTaskNum = taskinfo.VisitTaskNum,
IsCurrentTaskAdd = true,
BlindName = taskinfo.TaskBlindName,
OrderMark = tableQuestion.OrderMark,
diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/OCTCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/OCTCalculateService.cs
index 44bc97be2..e58015c3c 100644
--- a/IRaCIS.Core.Application/Service/ReadingCalculate/OCTCalculateService.cs
+++ b/IRaCIS.Core.Application/Service/ReadingCalculate/OCTCalculateService.cs
@@ -558,7 +558,6 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
IsCurrentTaskAdd = true,
BlindName = taskinfo.TaskBlindName,
OrderMark = questionInfo.OrderMark,
- FristAddTaskNum = taskinfo.VisitTaskNum,
FristAddTaskId = taskinfo.Id,
RowMark = questionInfo.OrderMark + decimal.Parse(maxnum.ToString()).GetLesionMark()
});
@@ -769,7 +768,6 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
IsCurrentTaskAdd = true,
BlindName = taskinfo.TaskBlindName,
OrderMark = questionInfo.OrderMark,
- FristAddTaskNum = taskinfo.VisitTaskNum,
FristAddTaskId = taskinfo.Id,
RowMark = questionInfo.OrderMark + decimal.Parse(maxnum.ToString()).GetLesionMark()
});
@@ -1043,7 +1041,6 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
IsCurrentTaskAdd = true,
BlindName = inDto.BlindName,
OrderMark = patchDataStatisticsInfo.OrderMark,
- FristAddTaskNum = inDto.VisitTaskNum,
FristAddTaskId = inDto.VisitTaskId,
RowMark = patchDataStatisticsInfo.OrderMark + decimal.Parse(item.ToString()).GetLesionMark()
});
diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialMaintenanceService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialMaintenanceService.cs
index ed7b3aca8..25b4ed629 100644
--- a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialMaintenanceService.cs
+++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialMaintenanceService.cs
@@ -211,6 +211,7 @@ namespace IRaCIS.Core.Application.Service
//测试项目 可以加入 测试用户 或者内部正式用户
.WhereIf(trialType == TrialType.NoneOfficial, t => t.IdentityUser.IsTestUser == true || (t.IdentityUser.IsTestUser == false && t.IdentityUser.IsZhiZhun))
+ .Where(t => t.IdentityUser.Status == UserStateEnum.Enable && t.IsUserRoleDisabled == false)
.Where(t => userTypeEnums.Contains(t.UserTypeEnum))
.WhereIf(inQuery.UserTypeEnum != null, t => t.UserTypeEnum == inQuery.UserTypeEnum)
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.UserRealName), t => t.IdentityUser.FullName.Contains(inQuery.UserRealName))
diff --git a/IRaCIS.Core.Application/Service/Visit/DTO/ClinicalStudySubjects.cs b/IRaCIS.Core.Application/Service/Visit/DTO/ClinicalStudySubjects.cs
index 45de32885..c1c3a6153 100644
--- a/IRaCIS.Core.Application/Service/Visit/DTO/ClinicalStudySubjects.cs
+++ b/IRaCIS.Core.Application/Service/Visit/DTO/ClinicalStudySubjects.cs
@@ -62,6 +62,8 @@ namespace IRaCIS.Application.Contracts
public Guid? FinalSubjectVisitId { get; set; }
public bool IsSubjectQuit { get; set; }
+
+ public string SuspendReason { get; set; } = string.Empty;
}
@@ -136,7 +138,7 @@ namespace IRaCIS.Application.Contracts
public Guid LatestSubmitSubjectVisitId { get; set; }
-
+ public string SuspendReason { get; set; }
}
diff --git a/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs b/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs
index 8b8fa455f..754e79e91 100644
--- a/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs
+++ b/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs
@@ -1029,6 +1029,8 @@ namespace IRaCIS.Application.Contracts
public int StudyCount { get; set; }
+ public string UploadJsonStr { get; set; }
+
public Guid TrialId { get; set; }
public Guid TrialSiteId { get; set; }
diff --git a/IRaCIS.Core.Application/Service/Visit/DTO/VisitPointViewModel.cs b/IRaCIS.Core.Application/Service/Visit/DTO/VisitPointViewModel.cs
index 5f0b329e8..abd09105d 100644
--- a/IRaCIS.Core.Application/Service/Visit/DTO/VisitPointViewModel.cs
+++ b/IRaCIS.Core.Application/Service/Visit/DTO/VisitPointViewModel.cs
@@ -272,6 +272,10 @@ namespace IRaCIS.Core.Application.Contracts
public string StudyName { get; set; } = string.Empty;
+ public string BodyPartForEdit { get; set; }
+
+ public string BodyPartForEditOther { get; set; }
+
public List SeriesList { get; set; } = new List();
}
diff --git a/IRaCIS.Core.Application/Service/Visit/SubjectVisitService.cs b/IRaCIS.Core.Application/Service/Visit/SubjectVisitService.cs
index 3826dc7f6..51acf5bb5 100644
--- a/IRaCIS.Core.Application/Service/Visit/SubjectVisitService.cs
+++ b/IRaCIS.Core.Application/Service/Visit/SubjectVisitService.cs
@@ -515,6 +515,10 @@ namespace IRaCIS.Core.Application.Services
StudyCode = k.StudyCode,
StudyId = k.Id,
+ BodyPartForEdit=k.BodyPartForEdit,
+ BodyPartForEditOther=k.BodyPartForEditOther
+
+
}).ToListAsync();
var studyIds = dicomStudyList.Select(t => t.StudyId).ToList();
@@ -718,6 +722,9 @@ namespace IRaCIS.Core.Application.Services
StudyCode = x.StudyCode,
IsDicom = false,
+ BodyPartForEdit=x.BodyPart,
+ BodyPartForEditOther=x.BodyPartForEditOther
+
}).ToList();
var isExistTaskNoneDicomFile = _noneDicomStudyFileRepository.Any(t => t.VisitTaskId == indto.VisitTaskId);
diff --git a/IRaCIS.Core.Application/TestService.cs b/IRaCIS.Core.Application/TestService.cs
index 08f48d8af..43439a5f3 100644
--- a/IRaCIS.Core.Application/TestService.cs
+++ b/IRaCIS.Core.Application/TestService.cs
@@ -73,6 +73,72 @@ namespace IRaCIS.Core.Application.Service
ILogger _logger, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService
{
public static int IntValue = 100;
+
+ ///
+ /// 重建闭包表
+ ///
+ ///
+ ///
+ ///
+ [AllowAnonymous]
+ public async Task RebuildAuditDocumentClosureAsync([FromServices] IRepository _auditDocumentClosureRepository, [FromServices] IRepository _auditDocumentRepository)
+ {
+
+
+ await _auditDocumentClosureRepository.BatchDeleteNoTrackingAsync(t => t.Id != Guid.Empty);
+
+ //过滤版本文件,防止污染闭包表
+ var documents = await _auditDocumentRepository.Where(t => t.MainFileId == null).Select(t => new { t.Id, t.ParentId }).ToListAsync();
+
+ var closures = new List();
+
+ // 建立一个字典,加快查找
+ var lookup = documents.ToDictionary(d => d.Id, d => d.ParentId);
+
+ foreach (var doc in documents)
+ {
+ // 1️ 自己 -> 自己 (depth = 0)
+ closures.Add(new AuditDocumentClosure
+ {
+ AncestorId = doc.Id,
+ DescendantId = doc.Id,
+ Depth = 0
+ });
+
+ // 2️ 递归向上找所有祖先
+ int depth = 1;
+ var currentParentId = doc.ParentId;
+
+
+ //脏数据 parentId==Guid.Empty
+ while (currentParentId.HasValue && currentParentId != Guid.Empty)
+ {
+ closures.Add(new AuditDocumentClosure
+ {
+ AncestorId = currentParentId.Value,
+ DescendantId = doc.Id,
+ Depth = depth
+ });
+
+ depth++;
+
+ // 继续向上查找
+ if (!lookup.TryGetValue(currentParentId.Value, out var nextParent))
+ break;
+
+ currentParentId = nextParent;
+ }
+ }
+
+
+
+ await _auditDocumentClosureRepository.AddRangeAsync(closures);
+ await _auditDocumentClosureRepository.SaveChangesAsync();
+
+ return ResponseOutput.Ok();
+ }
+
+
///
/// 清理一致性分析任务
///
@@ -372,6 +438,9 @@ namespace IRaCIS.Core.Application.Service
}
+
+
+
[AllowAnonymous]
public async Task RestoreDBOSSDate(
[FromServices] IOSSService _oSSService, [FromServices] IWebHostEnvironment _hostEnvironment, [FromServices] IRepository _studyRepository)
diff --git a/IRaCIS.Core.Domain/Allocation/VisitTask.cs b/IRaCIS.Core.Domain/Allocation/VisitTask.cs
index 82cec78de..bfe6a50de 100644
--- a/IRaCIS.Core.Domain/Allocation/VisitTask.cs
+++ b/IRaCIS.Core.Domain/Allocation/VisitTask.cs
@@ -297,6 +297,11 @@ public class VisitTask : BaseFullAuditEntity
[Comment("退回原因")]
public string PMBackReason { get; set; }
+ ///
+ /// 是否查看检查部位
+ ///
+ public bool IsViewStudyPart { get; set; } = false;
+
#region 完全随机增加字段
[Comment("完全随机阅片号")]
diff --git a/IRaCIS.Core.Domain/Document/AuditDocument.cs b/IRaCIS.Core.Domain/Document/AuditDocument.cs
index cbd69071a..73ea586da 100644
--- a/IRaCIS.Core.Domain/Document/AuditDocument.cs
+++ b/IRaCIS.Core.Domain/Document/AuditDocument.cs
@@ -11,7 +11,19 @@ namespace IRaCIS.Core.Domain.Models
[Table("AuditDocument")]
public class AuditDocument : BaseFullAuditEntity
{
-
+ // 0=自己, 1=直接子节点, 2=孙节点...
+ [JsonIgnore]
+ public List AncestorList { get; set; } = new List();
+
+ [JsonIgnore]
+ public List DescendantList { get; set; } = new List();
+
+ [JsonIgnore]
+ public List AuditDocumentOldVersionList { get; set; }
+
+ [JsonIgnore]
+ [ForeignKey(nameof(MainFileId))]
+ public AuditDocument MainAuditDocument { get; set; }
///
/// 文件夹名或者文件名
///
@@ -80,4 +92,110 @@ namespace IRaCIS.Core.Domain.Models
HistoricalVersion = 2,
}
+
+ ///
+ /// 稽查文档闭包表
+ ///
+ public class AuditDocumentClosure : Entity
+ {
+ [JsonIgnore]
+
+ public AuditDocument Ancestor { get; set; }
+
+ [JsonIgnore]
+
+ public AuditDocument Descendant { get; set; }
+
+ ///
+ /// 父Id
+ ///
+ [Comment("祖先")]
+ public Guid AncestorId { get; set; }
+
+ ///
+ /// 子Id
+ ///
+ [Comment("后代")]
+ public Guid DescendantId { get; set; }
+ public int Depth { get; set; } // 0=自己, 1=直接子节点, 2=孙节点...
+ }
+
+
+ public class AuditRecord : BaseFullAuditEntity
+ {
+ [JsonIgnore]
+ public List AuditRecordIdentityUserList { get; set; }
+
+ public string CompanyName { get; set; }
+
+ public string AuditContent { get; set; }
+
+ [Comment("稽查日期")]
+ public DateOnly AuditTime { get; set; }
+
+
+ public DateTime? BeginTime { get; set; }
+
+ public DateTime? EndTime { get; set; }
+
+ [Comment("稽查状态")]
+ public AuditState AuditState { get; set; }
+
+ [Comment("稽查形式")]
+ public AuditType AuditType { get; set; }
+
+ }
+
+
+ public class AuditRecordIdentityUser : BaseAddAuditEntity
+ {
+ [JsonIgnore]
+ public IdentityUser IdentityUser { get; set; }
+
+ [JsonIgnore]
+ public AuditRecord AuditRecord { get; set; }
+
+
+
+ public Guid IdentityUserId { get; set; }
+
+ public Guid AuditRecordId { get; set; }
+ }
+
+
+ public class AuditRecordPermission : BaseAddAuditEntity
+ {
+ [JsonIgnore]
+ public AuditRecord AuditRecord { get; set; }
+
+ [JsonIgnore]
+ public AuditDocument AuditDocument { get; set; }
+
+
+ public Guid AuditRecordId { get; set; }
+
+
+ public Guid AuditDocumentId { get; set; }
+ }
+
+
+ public enum AuditState
+ {
+ NotStart = 0,
+
+ Ongoing = 1,
+
+ End = 2
+
+ }
+
+ public enum AuditType
+ {
+ None = 0,
+
+ Online = 1,
+
+ OnSite = 2
+
+ }
}
diff --git a/IRaCIS.Core.Domain/Image/DicomStudy.cs b/IRaCIS.Core.Domain/Image/DicomStudy.cs
index 7fbc3c62e..07dd5c7cc 100644
--- a/IRaCIS.Core.Domain/Image/DicomStudy.cs
+++ b/IRaCIS.Core.Domain/Image/DicomStudy.cs
@@ -41,6 +41,8 @@ public class DicomStudy : BaseFullDeleteAuditEntity, IEntitySeqId
public string BodyPartForEdit { get; set; } = null!;
+ public string BodyPartForEditOther { get; set; }
+
public int Code { get; set; }
///
diff --git a/IRaCIS.Core.Domain/Image/NoneDicomStudy.cs b/IRaCIS.Core.Domain/Image/NoneDicomStudy.cs
index 177bfa59e..ed3411ac7 100644
--- a/IRaCIS.Core.Domain/Image/NoneDicomStudy.cs
+++ b/IRaCIS.Core.Domain/Image/NoneDicomStudy.cs
@@ -53,5 +53,7 @@ public class NoneDicomStudy : BaseFullDeleteAuditEntity
#endregion
public string ModifyReason { get; set; }
+
+ public string BodyPartForEditOther { get; set; }
}
diff --git a/IRaCIS.Core.Domain/Image/SCPImageUpload.cs b/IRaCIS.Core.Domain/Image/SCPImageUpload.cs
index d1650b3e2..2b1eb6cd4 100644
--- a/IRaCIS.Core.Domain/Image/SCPImageUpload.cs
+++ b/IRaCIS.Core.Domain/Image/SCPImageUpload.cs
@@ -1,3 +1,5 @@
+using System.Linq;
+
namespace IRaCIS.Core.Domain.Models;
[Comment("项目中心 - 影像推送记录")]
@@ -21,4 +23,36 @@ public class SCPImageUpload : BaseAddAuditEntity
public int StudyCount { get; set; }
public Guid TrialId { get; set; }
public Guid TrialSiteId { get; set; }
+
+ [MaxLength]
+ public string UploadJsonStr { get; set; }
}
+
+public class SCPImageLog
+{
+ ////当前传输 检查患者名字,一个检查可能患者名字不同 记录去重的,然后,拼接
+ //public string PatientNames => string.Join(",", UploadList.SelectMany(t => t.PatientNameList).ToList());
+
+ ///
+ /// 归档到数据库的检查Id
+ ///
+ public List SCPStudyIdList => UploadList.Select(t => t.SCPStudyId).ToList();
+
+
+ public List UploadList { get; set; } = new List();
+
+}
+
+public class ImageUploadInfo
+{
+ public List PatientNameList { get; set; } = new List();
+
+ public Guid SCPStudyId { get; set; }
+
+ public string StudyInstanceUid { get; set; }
+
+
+ public int FailedImageCount { get; set; }
+
+ public int SuccessImageCount { get; set; }
+}
\ No newline at end of file
diff --git a/IRaCIS.Core.Domain/Image/SCPInstance.cs b/IRaCIS.Core.Domain/Image/SCPInstance.cs
index 8c3fb2ff0..33b4e8888 100644
--- a/IRaCIS.Core.Domain/Image/SCPInstance.cs
+++ b/IRaCIS.Core.Domain/Image/SCPInstance.cs
@@ -40,4 +40,20 @@ public class SCPInstance : BaseFullAuditEntity, IEntitySeqId
public string Path { get; set; } = string.Empty;
public long? FileSize { get; set; }
+
+
+
+ #region DIR 增加
+
+ public string SOPClassUID { get; set; }
+
+ public string MediaStorageSOPClassUID { get; set; }
+
+ public string TransferSytaxUID { get; set; }
+
+ public string MediaStorageSOPInstanceUID { get; set; }
+
+ public bool IsEncapsulated { get; set; }
+
+ #endregion
}
diff --git a/IRaCIS.Core.Domain/Image/SCPSeries.cs b/IRaCIS.Core.Domain/Image/SCPSeries.cs
index a6cb28cec..5c217c4c4 100644
--- a/IRaCIS.Core.Domain/Image/SCPSeries.cs
+++ b/IRaCIS.Core.Domain/Image/SCPSeries.cs
@@ -39,4 +39,11 @@ public class SCPSeries : BaseFullDeleteAuditEntity, IEntitySeqId
[StringLength(1000)]
public string ImageResizePath { get; set; } = string.Empty;
+ #region DIR 增加
+
+ public string DicomSeriesDate { get; set; }
+
+ public string DicomSeriesTime { get; set; }
+ #endregion
+
}
diff --git a/IRaCIS.Core.Domain/Image/SCPStudy.cs b/IRaCIS.Core.Domain/Image/SCPStudy.cs
index a14710bb4..b458d7182 100644
--- a/IRaCIS.Core.Domain/Image/SCPStudy.cs
+++ b/IRaCIS.Core.Domain/Image/SCPStudy.cs
@@ -64,4 +64,18 @@ public class SCPStudy : BaseFullDeleteAuditEntity, IEntitySeqId
public Guid TrialSiteId { get; set; }
public Guid? SubjectVisitId { get; set; }
+
+
+
+ public string BodyPartForEditOther { get; set; }
+
+ #region DIR 增加字段
+
+ public string DicomStudyDate { get; set; }
+
+ public string DicomStudyTime { get; set; }
+
+ public string StudyDIRPath { get; set; }
+
+ #endregion
}
diff --git a/IRaCIS.Core.Domain/Image/TaskInstance.cs b/IRaCIS.Core.Domain/Image/TaskInstance.cs
index 0d87b70e4..30ea8c974 100644
--- a/IRaCIS.Core.Domain/Image/TaskInstance.cs
+++ b/IRaCIS.Core.Domain/Image/TaskInstance.cs
@@ -46,4 +46,19 @@ public class TaskInstance : BaseFullAuditEntity, IEntitySeqId
public long? FileSize { get; set; }
+
+ #region DIR 增加
+
+ public string SOPClassUID { get; set; }
+
+ public string MediaStorageSOPClassUID { get; set; }
+
+ public string TransferSytaxUID { get; set; }
+
+ public string MediaStorageSOPInstanceUID { get; set; }
+
+ public bool IsEncapsulated { get; set; }
+
+ #endregion
+
}
diff --git a/IRaCIS.Core.Domain/Image/TaskSeries.cs b/IRaCIS.Core.Domain/Image/TaskSeries.cs
index 824e17fd6..e9bba7bb5 100644
--- a/IRaCIS.Core.Domain/Image/TaskSeries.cs
+++ b/IRaCIS.Core.Domain/Image/TaskSeries.cs
@@ -41,4 +41,12 @@ public class TaskSeries : BaseFullDeleteAuditEntity, IEntitySeqId
[StringLength(1000)]
public string ImageResizePath { get; set; } = string.Empty;
+
+ #region DIR 增加
+
+ public string DicomSeriesDate { get; set; }
+
+ public string DicomSeriesTime { get; set; }
+ #endregion
+
}
diff --git a/IRaCIS.Core.Domain/Image/TaskStudy.cs b/IRaCIS.Core.Domain/Image/TaskStudy.cs
index c08f94a49..2ec72d69a 100644
--- a/IRaCIS.Core.Domain/Image/TaskStudy.cs
+++ b/IRaCIS.Core.Domain/Image/TaskStudy.cs
@@ -54,4 +54,17 @@ public class TaskStudy : BaseFullDeleteAuditEntity, IEntitySeqId
public string BodyPartForEdit { get; set; } = string.Empty;
public string ModalityForEdit { get; set; } = string.Empty;
+
+
+
+ public string BodyPartForEditOther { get; set; }
+ #region DIR 增加字段
+
+ public string DicomStudyDate { get; set; }
+
+ public string DicomStudyTime { get; set; }
+
+ public string StudyDIRPath { get; set; }
+
+ #endregion
}
diff --git a/IRaCIS.Core.Domain/Management/User.cs b/IRaCIS.Core.Domain/Management/User.cs
index 9ac550c24..577181053 100644
--- a/IRaCIS.Core.Domain/Management/User.cs
+++ b/IRaCIS.Core.Domain/Management/User.cs
@@ -69,6 +69,10 @@ public class IdentityUser : BaseFullAuditEntity
[JsonIgnore]
public List UserTrialList { get; set; }
+ [JsonIgnore]
+
+ public List AuditRecordList { get; set; }
+
[JsonIgnore]
public List UserRoleList { get; set; } = new List();
diff --git a/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTableAnswerRowInfo.cs b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTableAnswerRowInfo.cs
index 091686f6e..2bdaaa87d 100644
--- a/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTableAnswerRowInfo.cs
+++ b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTableAnswerRowInfo.cs
@@ -10,12 +10,19 @@ public class ReadingTableAnswerRowInfo : BaseFullDeleteAuditEntity
[JsonIgnore]
[ForeignKey("InstanceId")]
public DicomInstance Instance { get; set; }
- [JsonIgnore]
- [ForeignKey("VisitTaskId")]
- public VisitTask VisitTask { get; set; }
+
[JsonIgnore]
[ForeignKey("OrganInfoId")]
public OrganInfo OrganInfo { get; set; }
+
+ [JsonIgnore]
+ [ForeignKey("VisitTaskId")]
+ public VisitTask VisitTask { get; set; }
+
+ [JsonIgnore]
+ [ForeignKey("FristAddTaskId")]
+ public VisitTask FristAddTask { get; set; }
+
[JsonIgnore]
[ForeignKey("SplitRowId")]
public ReadingTableAnswerRowInfo SplitRow { get; set; }
@@ -76,9 +83,6 @@ public class ReadingTableAnswerRowInfo : BaseFullDeleteAuditEntity
[StringLength(1000)]
public string PicturePath { get; set; } = string.Empty;
- [Comment("第一次添加的任务ID")]
- public decimal FristAddTaskNum { get; set; } = 0;
-
[Comment("首次添加任务ID")]
public Guid FristAddTaskId { get; set; }
diff --git a/IRaCIS.Core.Domain/Visit/Subject.cs b/IRaCIS.Core.Domain/Visit/Subject.cs
index 152265e3d..4ccbdf68a 100644
--- a/IRaCIS.Core.Domain/Visit/Subject.cs
+++ b/IRaCIS.Core.Domain/Visit/Subject.cs
@@ -95,4 +95,7 @@ public class Subject : BaseFullDeleteAuditEntity
[Comment("受试者退出")]
public bool IsSubjectQuit { get; set; }
+
+ [Comment("访视中止原因")]
+ public string SuspendReason { get; set; }
}
diff --git a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs
index ba0759fdd..f8b4aee21 100644
--- a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs
+++ b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs
@@ -3,6 +3,7 @@ using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infra.EFCore.Common;
using IRaCIS.Core.Infrastructure.Encryption;
using IRaCIS.Core.Infrastructure.Extention;
+using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Newtonsoft.Json;
@@ -58,6 +59,9 @@ public class IRaCISDBContext : DbContext
//configurationBuilder.Conventions.Add(_ => new DecimalPrecisionConvention(18,2));
//针对字符串使用默认的长度配置为200,如果标注了StringLength 其他长度,就是标注的长度,如果标注了MaxLength 那么就是nvarcharMax
configurationBuilder.Conventions.Add(_ => new DefaultStringLengthConvention(400));
+
+ //https://learn.microsoft.com/zh-cn/ef/core/modeling/relationships/conventions?utm_source=chatgpt.com
+ //configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
@@ -669,6 +673,14 @@ public class IRaCISDBContext : DbContext
public virtual DbSet SubjectVisitImageBackRecord { get; set; }
+
+
+ public virtual DbSet AuditRecord { get; set; }
+ public virtual DbSet AuditRecordIdentityUser { get; set; }
+ public virtual DbSet AuditRecordPermission { get; set; }
+
+ public virtual DbSet AuditDocumentClosure { get; set; }
+
}
diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/AuditDocumentConfigration.cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/AuditDocumentConfigration.cs
new file mode 100644
index 000000000..8e904cc95
--- /dev/null
+++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/AuditDocumentConfigration.cs
@@ -0,0 +1,40 @@
+using IRaCIS.Core.Domain.Models;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+
+namespace IRaCIS.Core.Infra.EFCore.EntityConfigration
+{
+ public class AuditDocumentConfigration : IEntityTypeConfiguration
+ {
+
+
+ public void Configure(EntityTypeBuilder builder)
+ {
+
+ builder.HasMany(r => r.AncestorList).WithOne(t => t.Descendant).HasForeignKey(f => f.DescendantId);
+
+ builder.HasMany(r => r.DescendantList).WithOne(t => t.Ancestor).HasForeignKey(f => f.AncestorId);
+
+
+ builder.HasMany(r => r.AuditDocumentOldVersionList).WithOne(t => t.MainAuditDocument).HasForeignKey(f => f.MainFileId);
+
+
+ // builder.HasOne(r => r.WordFileRecord)
+ //.WithOne(t => t.WordFileRecord)
+ //.HasForeignKey(t => t.WordFileRecordId)
+ //.OnDelete(DeleteBehavior.Cascade);
+ // builder.HasOne(r => r.PDFFileRecord).WithOne().HasForeignKey(f => f.TrialFileTypeId);
+
+ // builder.HasOne(r => r.WordFileRecord).WithOne().HasForeignKey(f => f.TrialFileTypeId);
+
+ // builder.HasOne(r => r.SignFileRecord).WithOne().HasForeignKey(f => f.TrialFileTypeId);
+
+ // builder.HasOne(r => r.HistoryFileRecord).WithOne().HasForeignKey(f => f.TrialFileTypeId);
+
+ }
+ }
+
+
+
+}
diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/VisitTaskConfigration.cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/VisitTaskConfigration.cs
index c592a9fbb..194c7d1b8 100644
--- a/IRaCIS.Core.Infra.EFCore/EntityConfigration/VisitTaskConfigration.cs
+++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/VisitTaskConfigration.cs
@@ -23,6 +23,10 @@ namespace IRaCIS.Core.Infra.EFCore.EntityConfigration
//subject 删除了,但是任务没删除,导致的查询问题
builder.HasQueryFilter(b => b.Subject.IsDeleted == false);
+
+ builder.HasMany(t => t.LesionList).WithOne(s => s.VisitTask).HasForeignKey(t => t.VisitTaskId);
+
+
}
}
public class ReadingQuestionTrialConfigration : IEntityTypeConfiguration
@@ -35,4 +39,6 @@ namespace IRaCIS.Core.Infra.EFCore.EntityConfigration
}
}
+
+
}
diff --git a/IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj b/IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj
index 12aa90fdc..d0b656afe 100644
--- a/IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj
+++ b/IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj
@@ -31,12 +31,12 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/IRaCIS.Core.Infra.EFCore/Migrations/20250912024946_FristAddTaskNum.Designer.cs b/IRaCIS.Core.Infra.EFCore/Migrations/20250912024946_FristAddTaskNum.Designer.cs
new file mode 100644
index 000000000..810c0f7b1
--- /dev/null
+++ b/IRaCIS.Core.Infra.EFCore/Migrations/20250912024946_FristAddTaskNum.Designer.cs
@@ -0,0 +1,19186 @@
+//
+using System;
+using IRaCIS.Core.Infra.EFCore;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace IRaCIS.Core.Infra.EFCore.Migrations
+{
+ [DbContext(typeof(IRaCISDBContext))]
+ [Migration("20250912024946_FristAddTaskNum")]
+ partial class FristAddTaskNum
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "8.0.19")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+ modelBuilder.Entity("IRaCIS.Core.Domain.Models.Attachment", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("Code")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)")
+ .HasComment("编码");
+
+ b.Property("CreateTime")
+ .HasColumnType("datetime2");
+
+ b.Property("CreateUserId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("DoctorId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("ExpiryDate")
+ .HasColumnType("datetime2")
+ .HasComment("过期时间");
+
+ b.Property("FileName")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)");
+
+ b.Property("IsOfficial")
+ .HasColumnType("bit")
+ .HasComment("是否正式简历");
+
+ b.Property("Language")
+ .HasColumnType("int")
+ .HasComment("1 中文 2为英文");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)")
+ .HasComment("文件类型名");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreateUserId");
+
+ b.HasIndex("DoctorId");
+
+ b.ToTable("Attachment", t =>
+ {
+ t.HasComment("医生 - 简历|证书 文档表");
+ });
+ });
+
+ modelBuilder.Entity("IRaCIS.Core.Domain.Models.CRO", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("CROCode")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)");
+
+ b.Property("CROName")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)");
+
+ b.Property("CRONameCN")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)");
+
+ b.Property("CreateTime")
+ .HasColumnType("datetime2");
+
+ b.Property("CreateUserId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("IsTrialLevel")
+ .HasColumnType("bit")
+ .HasComment("是否是项目级别");
+
+ b.Property("TrialId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("UpdateTime")
+ .HasColumnType("datetime2");
+
+ b.Property("UpdateUserId")
+ .HasColumnType("uniqueidentifier");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreateUserId");
+
+ b.ToTable("CROCompany", t =>
+ {
+ t.HasComment("机构 - CRO");
+ });
+ });
+
+ modelBuilder.Entity("IRaCIS.Core.Domain.Models.CheckChallengeDialog", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("CreateTime")
+ .HasColumnType("datetime2");
+
+ b.Property("CreateUserId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("IsCRCNeedReply")
+ .HasColumnType("bit")
+ .HasComment("CRC是否需要回复 前端使用");
+
+ b.Property("ParamInfo")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)")
+ .HasComment("核查的检查信息Json");
+
+ b.Property("SubjectVisitId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("TalkContent")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserTypeEnum")
+ .HasColumnType("int")
+ .HasComment("核查过程中的操作用户类型");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreateUserId");
+
+ b.HasIndex("SubjectVisitId");
+
+ b.ToTable("CheckChallengeDialog", t =>
+ {
+ t.HasComment("一致性核查 - 对话记录表");
+ });
+ });
+
+ modelBuilder.Entity("IRaCIS.Core.Domain.Models.ClinicalAnswerRowInfo", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("ClinicalFormId")
+ .HasColumnType("uniqueidentifier")
+ .HasComment("表单Id");
+
+ b.Property("CreateTime")
+ .HasColumnType("datetime2");
+
+ b.Property("CreateUserId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("QuestionId")
+ .HasColumnType("uniqueidentifier")
+ .HasComment("问题Id");
+
+ b.Property("RowIndex")
+ .HasColumnType("int");
+
+ b.Property("SubjectId")
+ .HasColumnType("uniqueidentifier")
+ .HasComment("受试者Id");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreateUserId");
+
+ b.ToTable("ClinicalAnswerRowInfo", t =>
+ {
+ t.HasComment("受试者 - 临床表单表格问题行记录");
+ });
+ });
+
+ modelBuilder.Entity("IRaCIS.Core.Domain.Models.ClinicalDataSystemSet", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("ClinicalDataLevel")
+ .HasColumnType("int");
+
+ b.Property("ClinicalDataSetEnName")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)");
+
+ b.Property("ClinicalDataSetEnum")
+ .HasColumnType("int")
+ .HasComment("枚举(字典里面取的)");
+
+ b.Property("ClinicalDataSetName")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)");
+
+ b.Property("ClinicalUploadType")
+ .HasColumnType("int")
+ .HasComment("上传方式");
+
+ b.Property("CreateTime")
+ .HasColumnType("datetime2");
+
+ b.Property("CreateUserId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("CriterionEnumListStr")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)");
+
+ b.Property("FileName")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)");
+
+ b.Property("IsApply")
+ .HasColumnType("bit")
+ .HasComment("是否应用");
+
+ b.Property("IsEnable")
+ .HasColumnType("bit");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)");
+
+ b.Property("UploadRole")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreateUserId");
+
+ b.ToTable("ClinicalDataSystemSet", t =>
+ {
+ t.HasComment("系统 - 临床数据配置");
+ });
+ });
+
+ modelBuilder.Entity("IRaCIS.Core.Domain.Models.ClinicalDataTrialSet", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("ClinicalDataLevel")
+ .HasColumnType("int")
+ .HasComment("临床级别");
+
+ b.Property("ClinicalDataSetEnName")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)");
+
+ b.Property("ClinicalDataSetName")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)");
+
+ b.Property("ClinicalUploadType")
+ .HasColumnType("int")
+ .HasComment("上传方式");
+
+ b.Property("CreateTime")
+ .HasColumnType("datetime2");
+
+ b.Property("CreateUserId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("CriterionEnumListStr")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)");
+
+ b.Property("FileName")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)");
+
+ b.Property("IsApply")
+ .HasColumnType("bit")
+ .HasComment("是否应用");
+
+ b.Property("IsConfirm")
+ .HasColumnType("bit");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)");
+
+ b.Property("SystemClinicalDataSetId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("TrialId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("UploadRole")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreateUserId");
+
+ b.HasIndex("SystemClinicalDataSetId");
+
+ b.HasIndex("TrialId");
+
+ b.ToTable("ClinicalDataTrialSet", t =>
+ {
+ t.HasComment("项目 - 临床数据适应标准配置");
+ });
+ });
+
+ modelBuilder.Entity("IRaCIS.Core.Domain.Models.ClinicalForm", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("CheckDate")
+ .HasColumnType("datetime2")
+ .HasComment("检查日期");
+
+ b.Property("ClinicalDataTrialSetId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("CreateTime")
+ .HasColumnType("datetime2");
+
+ b.Property("CreateUserId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("PicturePath")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)")
+ .HasComment("截图地址");
+
+ b.Property("ReadingId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property