Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
commit
e6b3c662e2
|
|
@ -194,6 +194,7 @@ app.MapControllers();
|
||||||
Log.Logger = new LoggerConfiguration()
|
Log.Logger = new LoggerConfiguration()
|
||||||
//.MinimumLevel.Information()
|
//.MinimumLevel.Information()
|
||||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
||||||
|
.MinimumLevel.Override("ZiggyCreatures.Caching.Fusion", LogEventLevel.Warning)
|
||||||
.WriteTo.Console()
|
.WriteTo.Console()
|
||||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day)
|
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day)
|
||||||
.CreateLogger();
|
.CreateLogger();
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,29 @@
|
||||||
using FellowOakDicom.Network;
|
using FellowOakDicom;
|
||||||
using FellowOakDicom;
|
using FellowOakDicom.Imaging;
|
||||||
|
using FellowOakDicom.IO.Buffer;
|
||||||
|
using FellowOakDicom.Network;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using IRaCIS.Core.SCP.Service;
|
||||||
|
using Medallion.Threading;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Serilog;
|
||||||
|
using SharpCompress.Common;
|
||||||
|
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using ZiggyCreatures.Caching.Fusion;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using IRaCIS.Core.SCP.Service;
|
|
||||||
using IRaCIS.Core.Domain.Models;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using Medallion.Threading;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using Serilog;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using System.Data;
|
|
||||||
using FellowOakDicom.Imaging;
|
|
||||||
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
|
namespace IRaCIS.Core.SCP.Service
|
||||||
{
|
{
|
||||||
|
|
@ -120,7 +121,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
var _trialSiteDicomAERepository = _serviceProvider.GetService<IRepository<TrialSiteDicomAE>>();
|
var _trialSiteDicomAERepository = _serviceProvider.GetService<IRepository<TrialSiteDicomAE>>();
|
||||||
|
|
||||||
|
|
||||||
var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId==_trialId).FirstOrDefault();
|
var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId == _trialId).FirstOrDefault();
|
||||||
|
|
||||||
if (findTrialSiteAE != null)
|
if (findTrialSiteAE != null)
|
||||||
{
|
{
|
||||||
|
|
@ -328,6 +329,11 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||||
|
|
||||||
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
||||||
|
var _fusionCache = _serviceProvider.GetService<IFusionCache>();
|
||||||
|
var _trialSiteRepository = _serviceProvider.GetService<IRepository<TrialSite>>();
|
||||||
|
var _systemAnonymizationRepository = _serviceProvider.GetService<IRepository<SystemAnonymization>>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var storeRelativePath = string.Empty;
|
var storeRelativePath = string.Empty;
|
||||||
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}/{studyInstanceUid}";
|
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}/{studyInstanceUid}";
|
||||||
|
|
@ -336,337 +342,383 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
long fileSize = 0;
|
long fileSize = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream())
|
// 直接拿 Dataset(已经完整)
|
||||||
|
var dataset = request.Dataset;
|
||||||
|
|
||||||
|
#region 匿名化
|
||||||
|
|
||||||
|
|
||||||
|
var anonymizeList = await _fusionCache.GetOrSetAsync(CacheKeys.SystemAnonymization, _ => CacheHelper.GetSystemAnonymizationListAsync(_systemAnonymizationRepository), TimeSpan.FromDays(7));
|
||||||
|
|
||||||
|
var trialSiteInfo = await _fusionCache.GetOrSetAsync(CacheKeys.TrialSiteInfo(_trialSiteId), _ => CacheHelper.GetTrialSiteInfo(_trialSiteId, _trialSiteRepository), TimeSpan.FromMinutes(2));
|
||||||
|
|
||||||
|
var fixedFiledList = anonymizeList.Where(t => t.IsFixed).ToList();
|
||||||
|
|
||||||
|
var ircFiledList = anonymizeList.Where(t => t.IsFixed == false).ToList();
|
||||||
|
|
||||||
|
foreach (var item in fixedFiledList)
|
||||||
{
|
{
|
||||||
await request.File.SaveAsync(ms);
|
|
||||||
|
|
||||||
#region 1帧拆成多个固定大小的,方便移动端浏览
|
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
|
||||||
|
|
||||||
// 回到开头,读取 dicom
|
dataset.AddOrUpdate(dicomTag, item.ReplaceValue);
|
||||||
ms.Position = 0;
|
}
|
||||||
var dicomFile = DicomFile.Open(ms);
|
|
||||||
|
|
||||||
var numberOfFrames = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1);
|
foreach (var item in ircFiledList)
|
||||||
|
{
|
||||||
|
|
||||||
//多帧处理逻辑
|
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
|
||||||
if (numberOfFrames > 1)
|
|
||||||
|
if (dicomTag == DicomTag.ClinicalTrialProtocolID)
|
||||||
{
|
{
|
||||||
//一定要有像素数据才处理
|
dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolID, trialSiteInfo.TrialCode);
|
||||||
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
|
|
||||||
|
|
||||||
if (pixelData != null)
|
}
|
||||||
{
|
if (dicomTag == DicomTag.ClinicalTrialSiteID)
|
||||||
try
|
{
|
||||||
{
|
dataset.AddOrUpdate(DicomTag.ClinicalTrialSiteID, trialSiteInfo.TrialSiteCode);
|
||||||
|
|
||||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 开始处理多帧instanceId:{instanceId}");
|
|
||||||
|
|
||||||
var syntax = pixelData.Syntax;
|
|
||||||
|
|
||||||
// 每个 fragment 固定大小 (64KB 示例,可以自己调整)
|
|
||||||
int fragmentSize = 20 * 1024;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var frag = dicomFile.Dataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData);
|
|
||||||
|
|
||||||
int fragmentCount = frag?.Fragments?.Count() ?? 0;
|
|
||||||
|
|
||||||
var originOffsetTable = frag?.OffsetTable; //有可能没有表,需要自己重建
|
|
||||||
|
|
||||||
var bot = new List<uint>();
|
|
||||||
|
|
||||||
uint botOffset = 0;
|
|
||||||
|
|
||||||
//需要拆成固定片段的
|
|
||||||
if (syntax.IsEncapsulated && fragmentCount == pixelData.NumberOfFrames && numberOfFrames > 1)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
|
|
||||||
|
|
||||||
#region test
|
|
||||||
//var newDicomFile = dicomFile.Clone();
|
|
||||||
|
|
||||||
//var newDataset = newDicomFile.Dataset;
|
|
||||||
|
|
||||||
//var dstPd = DicomPixelData.Create(newDataset, true);
|
|
||||||
|
|
||||||
//for (int i = 0; i < pixelData.NumberOfFrames; i++)
|
|
||||||
//{
|
|
||||||
// var frame = pixelData.GetFrame(i);
|
|
||||||
|
|
||||||
// dstPd.AddFrame(frame);
|
|
||||||
|
|
||||||
// var data = frame.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;
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
//}
|
|
||||||
//var newOffsetTable = newDataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData).OffsetTable;
|
|
||||||
|
|
||||||
//newFragments.OffsetTable.AddRange(newOffsetTable.ToArray());
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region test fo-dicom auto bot
|
|
||||||
//var newDicomFile = dicomFile.Clone();
|
|
||||||
|
|
||||||
//var newDataset = newDicomFile.Dataset;
|
|
||||||
|
|
||||||
//var dstPd = DicomPixelData.Create(newDataset, true);
|
|
||||||
|
|
||||||
//for (int i = 0; i < pixelData.NumberOfFrames; i++)
|
|
||||||
//{
|
|
||||||
// var frame = pixelData.GetFrame(i);
|
|
||||||
|
|
||||||
// dstPd.AddFrame(frame);
|
|
||||||
//}
|
|
||||||
//var newOffsetTable = newDataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData).OffsetTable;
|
|
||||||
|
|
||||||
//Console.WriteLine(newOffsetTable.ToJsonStr());
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 最终使用
|
|
||||||
|
|
||||||
for (int n = 0; n < pixelData.NumberOfFrames; n++)
|
|
||||||
{
|
|
||||||
var frameData = pixelData.GetFrame(n); // 获取完整一帧
|
|
||||||
var data = frameData.Data;
|
|
||||||
int offset = 0;
|
|
||||||
|
|
||||||
bot.Add(botOffset);
|
|
||||||
|
|
||||||
botOffset += (uint)data.Length;
|
|
||||||
|
|
||||||
|
|
||||||
while (offset < data.Length)
|
|
||||||
{
|
|
||||||
botOffset += 8;
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//保留原始偏移表
|
|
||||||
|
|
||||||
if (originOffsetTable.Count == pixelData.NumberOfFrames)
|
|
||||||
{
|
|
||||||
newFragments.OffsetTable.AddRange(originOffsetTable.ToArray());
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newFragments.OffsetTable.AddRange(bot.ToArray());
|
|
||||||
|
|
||||||
//Console.WriteLine(bot.ToJsonStr());
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
dicomFile.Dataset.AddOrUpdate(newFragments);
|
|
||||||
|
|
||||||
|
|
||||||
// 重新保存 dicom 到流
|
|
||||||
ms.SetLength(0);
|
|
||||||
|
|
||||||
dicomFile.Save(ms);
|
|
||||||
}
|
|
||||||
//传递过来的就是拆分的,但是是没有偏移表的,我需要自己创建偏移表,不然生成缩略图失败
|
|
||||||
else if (syntax.IsEncapsulated && fragmentCount > pixelData.NumberOfFrames && originOffsetTable.Count == 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint offset = 0;
|
|
||||||
|
|
||||||
bot.Add(offset);
|
|
||||||
|
|
||||||
var fistSize = frag.Fragments.FirstOrDefault()?.Size ?? 0;
|
|
||||||
|
|
||||||
//和上一个大小不一样
|
|
||||||
var isDiffrentBefore = false;
|
|
||||||
|
|
||||||
uint count = 0;
|
|
||||||
|
|
||||||
// 假设你知道每帧对应的 fragment 数量
|
|
||||||
foreach (var frameFragments in frag.Fragments)
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
|
|
||||||
if (frameFragments.Size == fistSize)
|
|
||||||
{
|
|
||||||
isDiffrentBefore = false;
|
|
||||||
// 累加这一帧所有 fragment 的大小
|
|
||||||
offset += (uint)frameFragments.Size;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
offset += (uint)frameFragments.Size;
|
|
||||||
isDiffrentBefore = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDiffrentBefore)
|
|
||||||
{
|
|
||||||
//每个Fragment 也占用字节
|
|
||||||
offset += 8 * count;
|
|
||||||
bot.Add(offset);
|
|
||||||
count = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bot.RemoveAt(bot.Count - 1);
|
|
||||||
// 设置到新的 PixelData
|
|
||||||
frag.OffsetTable.AddRange(bot.ToArray());
|
|
||||||
|
|
||||||
|
|
||||||
// 重新保存 DICOM 到流
|
|
||||||
ms.SetLength(0);
|
|
||||||
|
|
||||||
dicomFile.Save(ms);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception mutiEx)
|
|
||||||
{
|
|
||||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 处理多帧失败,上传原始文件:{mutiEx.ToString()}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dicomTag == DicomTag.ClinicalTrialSubjectID)
|
||||||
|
{
|
||||||
|
dataset.AddOrUpdate(DicomTag.ClinicalTrialSubjectID, "");
|
||||||
|
|
||||||
|
}
|
||||||
|
if (dicomTag == DicomTag.ClinicalTrialTimePointID)
|
||||||
|
{
|
||||||
|
dataset.AddOrUpdate(DicomTag.ClinicalTrialTimePointID, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dicomTag == DicomTag.PatientID)
|
||||||
|
{
|
||||||
|
var pid = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
|
||||||
|
|
||||||
|
dataset.AddOrUpdate(DicomTag.PatientID, trialSiteInfo.TrialCode + "-" + pid);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// 构造 DicomFile(不用 Open)
|
||||||
|
var dicomFile = new DicomFile(dataset);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region 1帧拆成多个固定大小的,方便移动端浏览
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var numberOfFrames = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1);
|
||||||
|
|
||||||
|
//多帧处理逻辑
|
||||||
|
if (numberOfFrames > 1)
|
||||||
|
{
|
||||||
|
//一定要有像素数据才处理
|
||||||
|
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
|
||||||
|
|
||||||
#endregion
|
if (pixelData != null)
|
||||||
|
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
||||||
ms.Position = 0;
|
|
||||||
|
|
||||||
//irc 从路径最后一截取Guid
|
|
||||||
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
|
|
||||||
|
|
||||||
fileSize = ms.Length;
|
|
||||||
|
|
||||||
|
|
||||||
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
|
||||||
|
|
||||||
using (await @lock.AcquireAsync())
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(dicomFile, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE, fileSize);
|
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 开始处理多帧instanceId:{instanceId}");
|
||||||
|
|
||||||
|
var syntax = pixelData.Syntax;
|
||||||
|
|
||||||
|
// 每个 fragment 固定大小 (64KB 示例,可以自己调整)
|
||||||
|
int fragmentSize = 20 * 1024;
|
||||||
|
|
||||||
|
|
||||||
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
|
||||||
|
|
||||||
//没有缩略图
|
var frag = dicomFile.Dataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData);
|
||||||
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
|
||||||
|
int fragmentCount = frag?.Fragments?.Count() ?? 0;
|
||||||
|
|
||||||
|
var originOffsetTable = frag?.OffsetTable; //有可能没有表,需要自己重建
|
||||||
|
|
||||||
|
var bot = new List<uint>();
|
||||||
|
|
||||||
|
uint botOffset = 0;
|
||||||
|
|
||||||
|
//需要拆成固定片段的
|
||||||
|
if (syntax.IsEncapsulated && fragmentCount == pixelData.NumberOfFrames && numberOfFrames > 1)
|
||||||
{
|
{
|
||||||
|
|
||||||
// 生成缩略图
|
|
||||||
using (var memoryStream = new MemoryStream())
|
|
||||||
|
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
|
||||||
|
|
||||||
|
#region test
|
||||||
|
//var newDicomFile = dicomFile.Clone();
|
||||||
|
|
||||||
|
//var newDataset = newDicomFile.Dataset;
|
||||||
|
|
||||||
|
//var dstPd = DicomPixelData.Create(newDataset, true);
|
||||||
|
|
||||||
|
//for (int i = 0; i < pixelData.NumberOfFrames; i++)
|
||||||
|
//{
|
||||||
|
// var frame = pixelData.GetFrame(i);
|
||||||
|
|
||||||
|
// dstPd.AddFrame(frame);
|
||||||
|
|
||||||
|
// var data = frame.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;
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
//}
|
||||||
|
//var newOffsetTable = newDataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData).OffsetTable;
|
||||||
|
|
||||||
|
//newFragments.OffsetTable.AddRange(newOffsetTable.ToArray());
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region test fo-dicom auto bot
|
||||||
|
//var newDicomFile = dicomFile.Clone();
|
||||||
|
|
||||||
|
//var newDataset = newDicomFile.Dataset;
|
||||||
|
|
||||||
|
//var dstPd = DicomPixelData.Create(newDataset, true);
|
||||||
|
|
||||||
|
//for (int i = 0; i < pixelData.NumberOfFrames; i++)
|
||||||
|
//{
|
||||||
|
// var frame = pixelData.GetFrame(i);
|
||||||
|
|
||||||
|
// dstPd.AddFrame(frame);
|
||||||
|
//}
|
||||||
|
//var newOffsetTable = newDataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData).OffsetTable;
|
||||||
|
|
||||||
|
//Console.WriteLine(newOffsetTable.ToJsonStr());
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 最终使用
|
||||||
|
|
||||||
|
for (int n = 0; n < pixelData.NumberOfFrames; n++)
|
||||||
{
|
{
|
||||||
DicomImage image = new DicomImage(dicomFile.Dataset);
|
var frameData = pixelData.GetFrame(n); // 获取完整一帧
|
||||||
|
var data = frameData.Data;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
var sharpimage = image.RenderImage().AsSharpImage();
|
bot.Add(botOffset);
|
||||||
sharpimage.Save(memoryStream, new JpegEncoder());
|
|
||||||
|
|
||||||
// 上传缩略图到 OSS
|
botOffset += (uint)data.Length;
|
||||||
|
|
||||||
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, $"{seriesId.ToString()}_{instanceId.ToString()}.preview.jpg", false);
|
|
||||||
|
|
||||||
|
|
||||||
series.ImageResizePath = seriesPath;
|
while (offset < data.Length)
|
||||||
|
{
|
||||||
|
botOffset += 8;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//保留原始偏移表
|
||||||
|
|
||||||
|
if (originOffsetTable.Count == pixelData.NumberOfFrames)
|
||||||
|
{
|
||||||
|
newFragments.OffsetTable.AddRange(originOffsetTable.ToArray());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newFragments.OffsetTable.AddRange(bot.ToArray());
|
||||||
|
|
||||||
|
//Console.WriteLine(bot.ToJsonStr());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
dicomFile.Dataset.AddOrUpdate(newFragments);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
//传递过来的就是拆分的,但是是没有偏移表的,我需要自己创建偏移表,不然生成缩略图失败
|
||||||
|
else if (syntax.IsEncapsulated && fragmentCount > pixelData.NumberOfFrames && originOffsetTable.Count == 0)
|
||||||
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())
|
uint offset = 0;
|
||||||
|
|
||||||
|
bot.Add(offset);
|
||||||
|
|
||||||
|
var fistSize = frag.Fragments.FirstOrDefault()?.Size ?? 0;
|
||||||
|
|
||||||
|
//和上一个大小不一样
|
||||||
|
var isDiffrentBefore = false;
|
||||||
|
|
||||||
|
uint count = 0;
|
||||||
|
|
||||||
|
// 假设你知道每帧对应的 fragment 数量
|
||||||
|
foreach (var frameFragments in frag.Fragments)
|
||||||
{
|
{
|
||||||
find.PatientNameList.Add(patientIdStr);
|
count++;
|
||||||
|
|
||||||
|
if (frameFragments.Size == fistSize)
|
||||||
|
{
|
||||||
|
isDiffrentBefore = false;
|
||||||
|
// 累加这一帧所有 fragment 的大小
|
||||||
|
offset += (uint)frameFragments.Size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offset += (uint)frameFragments.Size;
|
||||||
|
isDiffrentBefore = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDiffrentBefore)
|
||||||
|
{
|
||||||
|
//每个Fragment 也占用字节
|
||||||
|
offset += 8 * count;
|
||||||
|
bot.Add(offset);
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//首次 (默认是Guid 空,数据库归档出了Id)
|
bot.RemoveAt(bot.Count - 1);
|
||||||
if (find.SCPStudyId != scpStudyId)
|
// 设置到新的 PixelData
|
||||||
{
|
frag.OffsetTable.AddRange(bot.ToArray());
|
||||||
find.SCPStudyId = scpStudyId;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//监控信息设置
|
|
||||||
_upload.FileCount++;
|
|
||||||
_upload.FileSize = _upload.FileSize + fileSize;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception mutiEx)
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 处理多帧失败,上传原始文件:{mutiEx.ToString()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
// 直接写入内存
|
||||||
|
await using var ms = new MemoryStream();
|
||||||
|
await dicomFile.SaveAsync(ms);
|
||||||
|
ms.Position = 0;
|
||||||
|
|
||||||
|
//irc 从路径最后一截取Guid
|
||||||
|
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
|
||||||
|
|
||||||
|
fileSize = ms.Length;
|
||||||
|
|
||||||
|
|
||||||
|
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
||||||
|
|
||||||
|
using (await @lock.AcquireAsync())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(dicomFile, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE, fileSize);
|
||||||
|
|
||||||
|
|
||||||
|
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||||
|
|
||||||
|
//没有缩略图
|
||||||
|
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
||||||
{
|
{
|
||||||
|
|
||||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}");
|
// 生成缩略图
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
if (_ImageUploadList.Any(t => t.StudyInstanceUid == studyInstanceUid))
|
|
||||||
{
|
{
|
||||||
var find = _ImageUploadList.FirstOrDefault(t => t.StudyInstanceUid.Equals(studyInstanceUid));
|
DicomImage image = new DicomImage(dicomFile.Dataset);
|
||||||
|
|
||||||
find.FailedImageCount++;
|
var sharpimage = image.RenderImage().AsSharpImage();
|
||||||
|
sharpimage.Save(memoryStream, new JpegEncoder());
|
||||||
|
|
||||||
|
// 上传缩略图到 OSS
|
||||||
|
|
||||||
|
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, $"{seriesId.ToString()}_{instanceId.ToString()}.preview.jpg", false);
|
||||||
|
|
||||||
|
|
||||||
|
series.ImageResizePath = seriesPath;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//监控信息设置
|
||||||
|
_upload.FileCount++;
|
||||||
|
_upload.FileSize = _upload.FileSize + fileSize;
|
||||||
|
}
|
||||||
|
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++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
|
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
public class DicomArchiveService(IRepository<SCPPatient> _patientRepository,
|
public class DicomArchiveService(IRepository<SCPPatient> _patientRepository,
|
||||||
IRepository<SCPStudy> _studyRepository,
|
IRepository<SCPStudy> _studyRepository,
|
||||||
IRepository<SCPSeries> _seriesRepository,
|
IRepository<SCPSeries> _seriesRepository,
|
||||||
IRepository<SCPInstance> _instanceRepository,
|
IRepository<SCPInstance> _instanceRepository
|
||||||
IDistributedLockProvider _distributedLockProvider,
|
|
||||||
IRepository<SystemAnonymization> _systemAnonymizationRepository,
|
|
||||||
IRepository<TrialSite> _trialSiteRepository,
|
|
||||||
IFusionCache _fusionCache
|
|
||||||
) : BaseService, IDicomArchiveService
|
) : BaseService, IDicomArchiveService
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -45,66 +41,6 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
|
|
||||||
var dataset = dicomFile.Dataset;
|
var dataset = dicomFile.Dataset;
|
||||||
|
|
||||||
#region 匿名化
|
|
||||||
|
|
||||||
var anonymizeList = await _fusionCache.GetOrSetAsync(CacheKeys.SystemAnonymization, _ => CacheHelper.GetSystemAnonymizationListAsync(_systemAnonymizationRepository), TimeSpan.FromDays(7));
|
|
||||||
|
|
||||||
var trialSiteInfo = await _fusionCache.GetOrSetAsync(CacheKeys.TrialSiteInfo(trialSiteId), _ => CacheHelper.GetTrialSiteInfo(trialSiteId, _trialSiteRepository), TimeSpan.FromMinutes(2));
|
|
||||||
|
|
||||||
var fixedFiledList = anonymizeList.Where(t => t.IsFixed).ToList();
|
|
||||||
|
|
||||||
var ircFiledList = anonymizeList.Where(t => t.IsFixed == false).ToList();
|
|
||||||
|
|
||||||
foreach (var item in fixedFiledList)
|
|
||||||
{
|
|
||||||
|
|
||||||
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
|
|
||||||
|
|
||||||
dataset.AddOrUpdate(dicomTag, item.ReplaceValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in ircFiledList)
|
|
||||||
{
|
|
||||||
|
|
||||||
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
|
|
||||||
|
|
||||||
if (dicomTag == DicomTag.ClinicalTrialProtocolID)
|
|
||||||
{
|
|
||||||
dataset.AddOrUpdate(dicomTag, trialSiteInfo.TrialCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
if (dicomTag == DicomTag.ClinicalTrialSiteID)
|
|
||||||
{
|
|
||||||
dataset.AddOrUpdate(dicomTag, trialSiteInfo.TrialSiteCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dicomTag == DicomTag.ClinicalTrialSubjectID)
|
|
||||||
{
|
|
||||||
dataset.AddOrUpdate(dicomTag, "");
|
|
||||||
|
|
||||||
}
|
|
||||||
if (dicomTag == DicomTag.ClinicalTrialTimePointID)
|
|
||||||
{
|
|
||||||
dataset.AddOrUpdate(dicomTag, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dicomTag == DicomTag.PatientID)
|
|
||||||
{
|
|
||||||
var pid = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
|
|
||||||
|
|
||||||
dataset.AddOrUpdate(dicomTag, trialSiteInfo.TrialCode + "_" + pid);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
|
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
|
||||||
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||||
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
|
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
|
||||||
|
|
@ -293,6 +229,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
findStudy.DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty);
|
findStudy.DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty);
|
||||||
findStudy.CalledAE = calledAE;
|
findStudy.CalledAE = calledAE;
|
||||||
findStudy.CallingAE = callingAE;
|
findStudy.CallingAE = callingAE;
|
||||||
|
findStudy.PatientIdStr = patientIdStr;
|
||||||
findStudy.PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
|
findStudy.PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
|
||||||
findStudy.PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty);
|
findStudy.PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty);
|
||||||
findStudy.PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty);
|
findStudy.PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty);
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,12 @@ builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resourc
|
||||||
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
||||||
builder.Services.AddControllers(options =>
|
builder.Services.AddControllers(options =>
|
||||||
{
|
{
|
||||||
|
// 关键配置:禁用不可空引用类型的自动 Required 验证
|
||||||
|
options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true;
|
||||||
|
|
||||||
|
// 插到最前,抢在默认绑定器之前
|
||||||
|
//options.ModelBinderProviders.Insert(0, new SpecificNullableBinderProvider());
|
||||||
|
|
||||||
options.Filters.Add<ModelActionFilter>();
|
options.Filters.Add<ModelActionFilter>();
|
||||||
options.Filters.Add<ProjectExceptionFilter>();
|
options.Filters.Add<ProjectExceptionFilter>();
|
||||||
options.Filters.Add<UnitOfWorkFilter>();
|
options.Filters.Add<UnitOfWorkFilter>();
|
||||||
|
|
|
||||||
|
|
@ -71,29 +71,27 @@ namespace IRaCIS.Core.API
|
||||||
|
|
||||||
DateTime dateTime;
|
DateTime dateTime;
|
||||||
|
|
||||||
if (reader.ValueType == typeof(DateTime) || reader.ValueType == typeof(DateTime?))
|
// 2. 检查目标类型是否可空
|
||||||
|
bool isNullable = objectType == typeof(DateTime?);
|
||||||
|
|
||||||
|
var canConvert = DateTime.TryParse(reader.Value?.ToString(), out dateTime);
|
||||||
|
|
||||||
|
|
||||||
|
if (isNullable == false && canConvert == false)
|
||||||
{
|
{
|
||||||
DateTime? nullableDateTime = reader.Value as DateTime?;
|
|
||||||
|
|
||||||
|
throw new JsonSerializationException($"Could not convert string to DateTime: {reader.Value} Path {reader.Path}");
|
||||||
if (nullableDateTime != null && nullableDateTime.HasValue)
|
|
||||||
{
|
|
||||||
dateTime = nullableDateTime.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (DateTime.TryParse((string)reader.Value, out dateTime) == false)
|
if (canConvert == false)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////如果前端传递带时区的,那么转换会报错,需要DateTimeKind.Unspecified
|
||||||
|
//dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
|
||||||
|
|
||||||
// 将客户端时间转换为服务器时区的时间
|
// 将客户端时间转换为服务器时区的时间
|
||||||
var serverZoneTime = TimeZoneInfo.ConvertTime(dateTime, _clientTimeZone, TimeZoneInfo.Local);
|
var serverZoneTime = TimeZoneInfo.ConvertTime(dateTime, _clientTimeZone, TimeZoneInfo.Local);
|
||||||
|
|
@ -113,7 +111,10 @@ namespace IRaCIS.Core.API
|
||||||
//第一个参数默认使用系统本地时区 也就是应用服务器的时区
|
//第一个参数默认使用系统本地时区 也就是应用服务器的时区
|
||||||
DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone);
|
DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone);
|
||||||
|
|
||||||
//writer.WriteValue(clientZoneTime);
|
|
||||||
|
//// 最简单的方式:创建 DateTimeOffset
|
||||||
|
//DateTimeOffset dateTimeOffset = new DateTimeOffset(clientZoneTime, _clientTimeZone.GetUtcOffset(clientZoneTime));
|
||||||
|
//writer.WriteValue(dateTimeOffset.ToString("yyyy-MM-dd HH:mm:sszzz"));
|
||||||
|
|
||||||
writer.WriteValue(clientZoneTime.ToString(_dateFormat));
|
writer.WriteValue(clientZoneTime.ToString(_dateFormat));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@ using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
|
|
@ -51,34 +53,90 @@ namespace IRaCIS.Core.API
|
||||||
}
|
}
|
||||||
public class NullToEmptyStringValueProvider : IValueProvider
|
public class NullToEmptyStringValueProvider : IValueProvider
|
||||||
{
|
{
|
||||||
PropertyInfo _MemberInfo;
|
PropertyInfo _memberInfo;
|
||||||
|
private readonly bool _isNullable;
|
||||||
public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
|
public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
|
||||||
{
|
{
|
||||||
_MemberInfo = memberInfo;
|
_memberInfo = memberInfo;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// DTO → JSON 返回前端的时候 处理null 变为"" 方便前端判断
|
||||||
public object GetValue(object target)
|
public object GetValue(object target)
|
||||||
{
|
{
|
||||||
object result = _MemberInfo.GetValue(target);
|
var result = _memberInfo.GetValue(target);
|
||||||
if (_MemberInfo.PropertyType == typeof(string) && result == null) result = "";
|
// 检查类型是否为string或string?
|
||||||
else if (_MemberInfo.PropertyType == typeof(String[]) && result == null) result = new string[] { };
|
if (_memberInfo.PropertyType == typeof(string) && result == null)
|
||||||
//else if (_MemberInfo.PropertyType == typeof(Nullable<Int32>) && result == null) result = 0;
|
{
|
||||||
//else if (_MemberInfo.PropertyType == typeof(Nullable<Decimal>) && result == null) result = 0.00M;
|
|
||||||
|
|
||||||
|
#region 返回的时候处理 string string? null 为"" 不区分处理
|
||||||
|
|
||||||
|
|
||||||
|
//// 如果是string?类型,返回null 可以做到,但是得反射,因为 string string? 是编译层区分的
|
||||||
|
|
||||||
|
//var isNullable1 = _memberInfo.CustomAttributes
|
||||||
|
// .Any(a => a.AttributeType.Name == "NullableAttribute");
|
||||||
|
|
||||||
|
////var isNullable2 = _memberInfo.CustomAttributes
|
||||||
|
//// .Any(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute");
|
||||||
|
|
||||||
|
//if (isNullable1)
|
||||||
|
//{
|
||||||
|
// return result;
|
||||||
|
//}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
// 如果是string类型,返回空字符串
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//影响 模型绑定时接收前端的值,同时影响 正常 JSON 序列化
|
||||||
public void SetValue(object target, object value)
|
public void SetValue(object target, object value)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (_MemberInfo.PropertyType == typeof(string))
|
_memberInfo.SetValue(target, value);
|
||||||
{
|
|
||||||
//去掉前后空格
|
#region 前端针对 string 类型的变量,如果传递null 会报错必传
|
||||||
_MemberInfo.SetValue(target, value == null ? string.Empty : value.ToString() == string.Empty ? value : value/*.ToString().Trim()*/);
|
|
||||||
|
//if (_memberInfo.PropertyType == typeof(string))
|
||||||
|
//{
|
||||||
|
// _memberInfo.SetValue(target, value == null ? string.Empty : value);
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// _memberInfo.SetValue(target, value);
|
||||||
|
//}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 处理模型验证区分 string string?
|
||||||
|
|
||||||
|
//////接收模型的时候 定义的明明是string 但是上面也有该属性,判断不准的 比如阅片跟踪列表查询
|
||||||
|
//var isNullable1 = _memberInfo.CustomAttributes.Any(a => a.AttributeType.Name == "NullableAttribute");
|
||||||
|
|
||||||
|
////不影响 string? 传递null 变为""
|
||||||
|
//if (_memberInfo.PropertyType == typeof(string) && isNullable1 == false)
|
||||||
|
//{
|
||||||
|
// //如果不处理 前段传递null string不会接收,说前段没传递会验证报错
|
||||||
|
// _memberInfo.SetValue(target, value == null ? string.Empty : value);
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
|
||||||
|
// _memberInfo.SetValue(target, value);
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_MemberInfo.SetValue(target, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,8 @@
|
||||||
|
|
||||||
// 模板类型 1 Elevate 2 Extensive
|
// 模板类型 1 Elevate 2 Extensive
|
||||||
"TemplateType": 2,
|
"TemplateType": 2,
|
||||||
//MFA免验证发送天数
|
|
||||||
"UserMFAVerifyDays": 1
|
"UserMFAVerifyMinutes": 1440
|
||||||
},
|
},
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
"Port": 465,
|
"Port": 465,
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
// 模板类型 1 Elevate 2 Extensive
|
// 模板类型 1 Elevate 2 Extensive
|
||||||
"TemplateType": 2,
|
"TemplateType": 2,
|
||||||
//MFA免验证发送天数
|
//MFA免验证发送天数
|
||||||
"UserMFAVerifyDays": 1
|
"UserMFAVerifyMinutes": 1440
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@
|
||||||
"TemplateType": 1,
|
"TemplateType": 1,
|
||||||
|
|
||||||
"OpenTrialRelationDelete": false,
|
"OpenTrialRelationDelete": false,
|
||||||
//MFA免验证发送天数
|
//MFA免验证
|
||||||
"UserMFAVerifyDays": 1
|
"UserMFAVerifyMinutes": 1440
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,8 @@
|
||||||
"TemplateType": 1,
|
"TemplateType": 1,
|
||||||
"OpenLoginMFA": true,
|
"OpenLoginMFA": true,
|
||||||
//MFA免验证发送天数
|
//MFA免验证发送天数
|
||||||
"UserMFAVerifyDays": 1
|
"UserMFAVerifyMinutes": 1440
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
// 模板类型 1 Elevate 2 Extensive
|
// 模板类型 1 Elevate 2 Extensive
|
||||||
"TemplateType": 1,
|
"TemplateType": 1,
|
||||||
//MFA免验证发送天数
|
//MFA免验证发送天数
|
||||||
"UserMFAVerifyDays": 1
|
"UserMFAVerifyMinutes": 1440
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@
|
||||||
// 模板类型 1 Elevate 2 Extensive
|
// 模板类型 1 Elevate 2 Extensive
|
||||||
"TemplateType": 2,
|
"TemplateType": 2,
|
||||||
//MFA免验证发送天数
|
//MFA免验证发送天数
|
||||||
"UserMFAVerifyDays": 1
|
"UserMFAVerifyMinutes": 1440
|
||||||
|
|
||||||
},
|
},
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Filter;
|
namespace IRaCIS.Core.Application.Filter;
|
||||||
|
|
@ -28,3 +32,7 @@ public class ModelActionFilter(IStringLocalizer _localizer) : ActionFilterAttrib
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, ISt
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (exception.InnerException is null ? (exception.Message)
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (exception.InnerException is null ? (exception.Message)
|
||||||
: (exception.InnerException?.Message )), ApiResponseCodeEnum.ProgramException));
|
: (exception.Message + "Inner ExceptionMsg:" + exception.InnerException?.Message)), ApiResponseCodeEnum.ProgramException));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,8 @@ public static class CacheKeys
|
||||||
//每个用户 每个浏览器独立时间
|
//每个用户 每个浏览器独立时间
|
||||||
public static string UserMFAVerifyPass(Guid userId,string browserFingerprint) => $"UserMFAVerifyPass:{userId}:{browserFingerprint}";
|
public static string UserMFAVerifyPass(Guid userId,string browserFingerprint) => $"UserMFAVerifyPass:{userId}:{browserFingerprint}";
|
||||||
|
|
||||||
|
public static string UserMFATag(Guid userId) => $"UserMFAVerifyPass:{userId}";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class CacheHelper
|
public static class CacheHelper
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using FellowOakDicom;
|
using DocumentFormat.OpenXml.Office.CustomUI;
|
||||||
|
using FellowOakDicom;
|
||||||
using FellowOakDicom.Media;
|
using FellowOakDicom.Media;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
@ -57,6 +58,7 @@ namespace IRaCIS.Core.Application.Helper
|
||||||
var mappings = new List<string>();
|
var mappings = new List<string>();
|
||||||
int index = 1;
|
int index = 1;
|
||||||
|
|
||||||
|
var studyUid=list.FirstOrDefault()?.StudyInstanceUid;
|
||||||
|
|
||||||
var dicomDir = new DicomDirectory();
|
var dicomDir = new DicomDirectory();
|
||||||
|
|
||||||
|
|
@ -128,7 +130,9 @@ namespace IRaCIS.Core.Application.Helper
|
||||||
// 重置流位置
|
// 重置流位置
|
||||||
memoryStream.Position = 0;
|
memoryStream.Position = 0;
|
||||||
|
|
||||||
await _oSSService.UploadToOSSAsync(memoryStream, ossFolder, "DICOMDIR", false);
|
var relativePath= await _oSSService.UploadToOSSAsync(memoryStream, ossFolder, "DICOMDIR", true);
|
||||||
|
|
||||||
|
dic.Add($"{studyUid}_DICOMDIR" , relativePath.Split('/').Last());
|
||||||
}
|
}
|
||||||
|
|
||||||
//清理临时文件
|
//清理临时文件
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ using NPOI.HSSF.UserModel;
|
||||||
using NPOI.SS.Formula.Functions;
|
using NPOI.SS.Formula.Functions;
|
||||||
using NPOI.SS.UserModel;
|
using NPOI.SS.UserModel;
|
||||||
using NPOI.XSSF.UserModel;
|
using NPOI.XSSF.UserModel;
|
||||||
|
using SharpCompress.Common;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Xceed.Document.NET;
|
using Xceed.Document.NET;
|
||||||
|
|
@ -870,17 +871,16 @@ public static class ExcelExportHelper
|
||||||
//模板路径
|
//模板路径
|
||||||
var tplPath = physicalPath;
|
var tplPath = physicalPath;
|
||||||
|
|
||||||
#region 根据中英文 删除模板sheet
|
|
||||||
|
|
||||||
// 打开模板文件
|
|
||||||
var templateFile = new FileStream(tplPath, FileMode.Open, FileAccess.Read);
|
|
||||||
|
|
||||||
// 获取文件流
|
// 获取文件流
|
||||||
var templateStream = new MemoryStream();
|
var templateStream = new MemoryStream();
|
||||||
templateFile.CopyTo(templateStream);
|
|
||||||
templateStream.Seek(0, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
var workbook = new XSSFWorkbook(templateStream);
|
#region 根据中英文 删除模板sheet
|
||||||
|
|
||||||
|
// 打开模板文件
|
||||||
|
var templateFileStream = new FileStream(tplPath, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
|
var workbook = new XSSFWorkbook(templateFileStream);
|
||||||
|
|
||||||
int sheetCount = workbook.NumberOfSheets;
|
int sheetCount = workbook.NumberOfSheets;
|
||||||
|
|
||||||
|
|
@ -985,14 +985,8 @@ public static class ExcelExportHelper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var memoryStream2 = new MemoryStream())
|
workbook.Write(templateStream, leaveOpen: true);
|
||||||
{
|
templateStream.Position = 0;
|
||||||
workbook.Write(memoryStream2, true);
|
|
||||||
|
|
||||||
memoryStream2.Seek(0, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
templateStream = memoryStream2;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1099,6 +1093,63 @@ public static class ExcelExportHelper
|
||||||
//模板路径
|
//模板路径
|
||||||
var tplPath = physicalPath;
|
var tplPath = physicalPath;
|
||||||
|
|
||||||
|
var templateStream = new MemoryStream();
|
||||||
|
|
||||||
|
|
||||||
|
#region npoi 移除某一行
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
if (isEn_US)
|
||||||
|
{
|
||||||
|
// 打开模板文件
|
||||||
|
using var templateFileStream = new FileStream(tplPath, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
|
using var workbook = new XSSFWorkbook(templateFileStream);
|
||||||
|
|
||||||
|
int sheetCount = workbook.NumberOfSheets;
|
||||||
|
|
||||||
|
int removeRowIndex = 1; // 要删除的行(0-based)
|
||||||
|
|
||||||
|
for (int i = 0; i < sheetCount; i++)
|
||||||
|
{
|
||||||
|
var sheet = workbook.GetSheetAt(i);
|
||||||
|
|
||||||
|
// 2️ 删除行
|
||||||
|
var row = sheet.GetRow(removeRowIndex);
|
||||||
|
if (row != null)
|
||||||
|
{
|
||||||
|
sheet.RemoveRow(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3️ 上移后续行
|
||||||
|
if (removeRowIndex < sheet.LastRowNum)
|
||||||
|
{
|
||||||
|
sheet.ShiftRows(
|
||||||
|
removeRowIndex + 1,
|
||||||
|
sheet.LastRowNum,
|
||||||
|
-1,
|
||||||
|
true, // copyRowHeight
|
||||||
|
false // resetOriginalRowHeight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
workbook.Write(templateStream, leaveOpen: true);
|
||||||
|
templateStream.Position = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using (var fs = new FileStream(tplPath, FileMode.Open, FileAccess.Read))
|
||||||
|
{
|
||||||
|
fs.CopyTo(templateStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
templateStream.Position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
var memoryStream = new MemoryStream();
|
var memoryStream = new MemoryStream();
|
||||||
|
|
||||||
|
|
@ -1107,7 +1158,7 @@ public static class ExcelExportHelper
|
||||||
IgnoreTemplateParameterMissing = true,
|
IgnoreTemplateParameterMissing = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
await MiniExcel.SaveAsByTemplateAsync(memoryStream, tplPath, data, config);
|
await MiniExcel.SaveAsByTemplateAsync(memoryStream, templateStream.ToArray(), data, config);
|
||||||
|
|
||||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,7 @@ public interface IOSSService
|
||||||
|
|
||||||
public Task DeleteFromPrefix(string prefix, bool isCache = false);
|
public Task DeleteFromPrefix(string prefix, bool isCache = false);
|
||||||
|
|
||||||
public Task DeleteObjects(List<string> objectKeys);
|
public Task DeleteObjects(List<string> objectKeys, bool isCache = false);
|
||||||
|
|
||||||
List<string> GetRootFolderNames();
|
List<string> GetRootFolderNames();
|
||||||
|
|
||||||
|
|
@ -249,7 +249,7 @@ public class OSSService : IOSSService
|
||||||
|
|
||||||
LifeCycleExpiration =
|
LifeCycleExpiration =
|
||||||
{
|
{
|
||||||
Days = 30
|
Days = 30 //最后一次修改时间
|
||||||
},
|
},
|
||||||
StorageClass = StorageClass.Archive
|
StorageClass = StorageClass.Archive
|
||||||
}
|
}
|
||||||
|
|
@ -302,8 +302,8 @@ public class OSSService : IOSSService
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
var clientConfig = new AmazonS3Config
|
var clientConfig = new AmazonS3Config
|
||||||
{
|
{
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||||
UseHttp = true,
|
//,UseHttp = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
@ -333,15 +333,15 @@ public class OSSService : IOSSService
|
||||||
Transitions = new List<LifecycleTransition>
|
Transitions = new List<LifecycleTransition>
|
||||||
{
|
{
|
||||||
// 1天后转为低频访问 (Standard-IA)
|
// 1天后转为低频访问 (Standard-IA)
|
||||||
new LifecycleTransition
|
//new LifecycleTransition
|
||||||
{
|
//{
|
||||||
Days = 1,
|
// Days = 1, //Days' in Transition action must be greater than or equal to 30 for storageClass 'STANDARD_IA'"
|
||||||
StorageClass = S3StorageClass.StandardInfrequentAccess // 对应S3 Standard-IA
|
// StorageClass = S3StorageClass.StandardInfrequentAccess // 对应S3 Standard-IA
|
||||||
},
|
//},
|
||||||
// 30天后转为归档 (Glacier Instant Retrieval)
|
// 30天后转为归档 (Glacier Instant Retrieval)
|
||||||
new LifecycleTransition
|
new LifecycleTransition
|
||||||
{
|
{
|
||||||
Days = 30,
|
Days = 30, //创建时间
|
||||||
StorageClass = S3StorageClass.GlacierInstantRetrieval // 对应归档(即时检索)
|
StorageClass = S3StorageClass.GlacierInstantRetrieval // 对应归档(即时检索)
|
||||||
}
|
}
|
||||||
// 如果需要更深的归档,可以继续添加:
|
// 如果需要更深的归档,可以继续添加:
|
||||||
|
|
@ -765,8 +765,8 @@ public class OSSService : IOSSService
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
var clientConfig = new AmazonS3Config
|
var clientConfig = new AmazonS3Config
|
||||||
{
|
{
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||||
UseHttp = true,
|
//,UseHttp = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
@ -891,8 +891,8 @@ public class OSSService : IOSSService
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
var clientConfig = new AmazonS3Config
|
var clientConfig = new AmazonS3Config
|
||||||
{
|
{
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||||
UseHttp = true,
|
//,UseHttp = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
@ -966,8 +966,8 @@ public class OSSService : IOSSService
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
var clientConfig = new AmazonS3Config
|
var clientConfig = new AmazonS3Config
|
||||||
{
|
{
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||||
UseHttp = true,
|
//,UseHttp = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
@ -1172,8 +1172,8 @@ public class OSSService : IOSSService
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
var clientConfig = new AmazonS3Config
|
var clientConfig = new AmazonS3Config
|
||||||
{
|
{
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||||
UseHttp = true,
|
//,UseHttp = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
@ -1546,8 +1546,8 @@ public class OSSService : IOSSService
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
var clientConfig = new AmazonS3Config
|
var clientConfig = new AmazonS3Config
|
||||||
{
|
{
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||||
UseHttp = true,
|
//,UseHttp = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
@ -1561,7 +1561,7 @@ public class OSSService : IOSSService
|
||||||
|
|
||||||
var listObjectsResponse = await amazonS3Client.ListObjectsV2Async(listObjectsRequest);
|
var listObjectsResponse = await amazonS3Client.ListObjectsV2Async(listObjectsRequest);
|
||||||
|
|
||||||
if (listObjectsResponse.S3Objects.Count > 0)
|
if (listObjectsResponse.S3Objects?.Count > 0)
|
||||||
{
|
{
|
||||||
// 准备删除请求
|
// 准备删除请求
|
||||||
var deleteObjectsRequest = new Amazon.S3.Model.DeleteObjectsRequest
|
var deleteObjectsRequest = new Amazon.S3.Model.DeleteObjectsRequest
|
||||||
|
|
@ -1591,7 +1591,7 @@ public class OSSService : IOSSService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteObjects(List<string> objectKeys)
|
public async Task DeleteObjects(List<string> objectKeys, bool isCache = false)
|
||||||
{
|
{
|
||||||
GetObjectStoreTempToken();
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
|
@ -1601,9 +1601,23 @@ public class OSSService : IOSSService
|
||||||
|
|
||||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
var bucketName = string.Empty;
|
||||||
|
|
||||||
|
if (isCache)
|
||||||
|
{
|
||||||
|
Uri uri = new Uri(aliConfig.ViewEndpoint);
|
||||||
|
string host = uri.Host; // 获取 "zy-irc-test-dev-cache.oss-cn-shanghai.aliyuncs.com"
|
||||||
|
string[] parts = host.Split('.');
|
||||||
|
bucketName = parts[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bucketName = aliConfig.BucketName;
|
||||||
|
}
|
||||||
|
|
||||||
if (objectKeys.Count > 0)
|
if (objectKeys.Count > 0)
|
||||||
{
|
{
|
||||||
var result = _ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, objectKeys, false));
|
var result = _ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(bucketName, objectKeys, false));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1639,8 +1653,8 @@ public class OSSService : IOSSService
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
var clientConfig = new AmazonS3Config
|
var clientConfig = new AmazonS3Config
|
||||||
{
|
{
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||||
UseHttp = true,
|
//,UseHttp = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
@ -1714,8 +1728,8 @@ public class OSSService : IOSSService
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
var clientConfig = new AmazonS3Config
|
var clientConfig = new AmazonS3Config
|
||||||
{
|
{
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||||
UseHttp = true,
|
//,UseHttp = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var request = new Amazon.S3.Model.GetObjectMetadataRequest
|
var request = new Amazon.S3.Model.GetObjectMetadataRequest
|
||||||
|
|
@ -1799,9 +1813,16 @@ public class OSSService : IOSSService
|
||||||
{
|
{
|
||||||
var awsOptions = ObjectStoreServiceOptions.AWS;
|
var awsOptions = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
|
||||||
|
// 创建 STS 客户端(考虑使用 RegionEndpoint)
|
||||||
|
var stsConfig = new AmazonSecurityTokenServiceConfig
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.GetBySystemName(awsOptions.Region)
|
||||||
|
};
|
||||||
|
|
||||||
//aws 临时凭证
|
//aws 临时凭证
|
||||||
// 创建 STS 客户端
|
// 创建 STS 客户端
|
||||||
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
|
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey, stsConfig);
|
||||||
|
|
||||||
// 使用 AssumeRole 请求临时凭证
|
// 使用 AssumeRole 请求临时凭证
|
||||||
var assumeRoleRequest = new AssumeRoleRequest
|
var assumeRoleRequest = new AssumeRoleRequest
|
||||||
|
|
|
||||||
|
|
@ -19195,6 +19195,13 @@
|
||||||
<param name="tpCode"></param>
|
<param name="tpCode"></param>
|
||||||
<param name="key"></param>
|
<param name="key"></param>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.Application.Services.SeriesService.UpdateImageResizePath(IRaCIS.Core.Application.Contracts.Dicom.DTO.UpdateImageResizeDTO)">
|
||||||
|
<summary>
|
||||||
|
更新缩略图路径
|
||||||
|
</summary>
|
||||||
|
<param name="dto"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
<member name="M:IRaCIS.Core.Application.Services.SeriesService.List(System.Guid,System.Nullable{System.Boolean},System.Nullable{System.Boolean})">
|
<member name="M:IRaCIS.Core.Application.Services.SeriesService.List(System.Guid,System.Nullable{System.Boolean},System.Nullable{System.Boolean})">
|
||||||
<summary> 指定资源Id,获取Dicom检查所属序列信息列表 </summary>
|
<summary> 指定资源Id,获取Dicom检查所属序列信息列表 </summary>
|
||||||
<param name="studyId"> Dicom检查的Id </param>
|
<param name="studyId"> Dicom检查的Id </param>
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ using MassTransit;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System.Linq;
|
||||||
using System.Linq.Dynamic.Core;
|
using System.Linq.Dynamic.Core;
|
||||||
using Subject = IRaCIS.Core.Domain.Models.Subject;
|
using Subject = IRaCIS.Core.Domain.Models.Subject;
|
||||||
|
|
||||||
|
|
@ -1172,7 +1173,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||||
var visitQuery = _visitTaskRepository.Where(x => x.TrialId == trialId && x.DoctorUserId == _userInfo.UserRoleId && x.TaskState == TaskState.Effect)
|
var visitQuery = _visitTaskRepository.Where(x => x.TrialId == trialId && x.DoctorUserId == _userInfo.UserRoleId && x.TaskState == TaskState.Effect)
|
||||||
.WhereIf(inQuery.SubjectId != null, x => x.SubjectId == inQuery.SubjectId)
|
.WhereIf(inQuery.SubjectId != null, x => x.SubjectId == inQuery.SubjectId)
|
||||||
.WhereIf(!string.IsNullOrEmpty(subjectCode), t => (t.Subject.Code.Contains(subjectCode!) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(subjectCode!) && t.IsAnalysisCreate))
|
.WhereIf(!string.IsNullOrEmpty(subjectCode), t => (t.Subject.Code.Contains(subjectCode!) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(subjectCode!) && t.IsAnalysisCreate))
|
||||||
.WhereIf(critrion.CriterionType == CriterionType.OCT, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.NoneDicomStudyList.Where(t=>t.Modality=="OCT").SelectMany(t => t.ImageLabelNoneDicomFileList).Any() : true)
|
.WhereIf(critrion.CriterionType == CriterionType.OCT, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.NoneDicomStudyList.Where(t => t.Modality == "OCT").SelectMany(t => t.ImageLabelNoneDicomFileList).Any() : true)
|
||||||
.WhereIf(critrion.CriterionType == CriterionType.IVUS, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.NoneDicomStudyList.Where(t => t.Modality == "IVUS").SelectMany(t => t.ImageLabelNoneDicomFileList).Any() : true);
|
.WhereIf(critrion.CriterionType == CriterionType.IVUS, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.NoneDicomStudyList.Where(t => t.Modality == "IVUS").SelectMany(t => t.ImageLabelNoneDicomFileList).Any() : true);
|
||||||
|
|
||||||
var visitGroupQuery = visitQuery.GroupBy(x => new { x.SubjectId, x.Subject.Code, x.BlindSubjectCode });
|
var visitGroupQuery = visitQuery.GroupBy(x => new { x.SubjectId, x.Subject.Code, x.BlindSubjectCode });
|
||||||
|
|
@ -2292,12 +2293,12 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||||
|
|
||||||
foreach (var item in readingTableAnswerRowInfoList)
|
foreach (var item in readingTableAnswerRowInfoList)
|
||||||
{
|
{
|
||||||
if (item.SplitRowId!=null&&lesionRelationship.ContainsKey(item.SplitRowId.Value))
|
if (item.SplitRowId != null && lesionRelationship.ContainsKey(item.SplitRowId.Value))
|
||||||
{
|
{
|
||||||
item.SplitRowId = lesionRelationship[item.SplitRowId.Value];
|
item.SplitRowId = lesionRelationship[item.SplitRowId.Value];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.MergeRowId!=null&&lesionRelationship.ContainsKey(item.MergeRowId.Value))
|
if (item.MergeRowId != null && lesionRelationship.ContainsKey(item.MergeRowId.Value))
|
||||||
{
|
{
|
||||||
item.MergeRowId = lesionRelationship[item.MergeRowId.Value];
|
item.MergeRowId = lesionRelationship[item.MergeRowId.Value];
|
||||||
}
|
}
|
||||||
|
|
@ -2402,6 +2403,10 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||||
//另一个阅片人的任务根据任务进度自动进入PM退回或PM申请重阅
|
//另一个阅片人的任务根据任务进度自动进入PM退回或PM申请重阅
|
||||||
filterExpression = filterExpression.And(t => t.VisitTaskNum >= task.VisitTaskNum);
|
filterExpression = filterExpression.And(t => t.VisitTaskNum >= task.VisitTaskNum);
|
||||||
|
|
||||||
|
//退回只影响有序的后续所有的,无序的当前访视
|
||||||
|
filterExpression = filterExpression.And(t => (t.TrialReadingCriterion.IsReadingTaskViewInOrder == ReadingOrder.InOrder) ||
|
||||||
|
(t.TrialReadingCriterion.IsReadingTaskViewInOrder != ReadingOrder.InOrder && t.SourceSubjectVisitId == task.SourceSubjectVisitId));
|
||||||
|
|
||||||
|
|
||||||
var influenceTaskList = await _visitTaskRepository.Where(filterExpression, true).ToListAsync();
|
var influenceTaskList = await _visitTaskRepository.Where(filterExpression, true).ToListAsync();
|
||||||
|
|
||||||
|
|
@ -2559,9 +2564,12 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||||
|
|
||||||
//删除序列数据
|
//删除序列数据
|
||||||
await _subjectCriteriaEvaluationVisitStudyFilterRepository.BatchDeleteNoTrackingAsync(t => t.TrialReadingCriterion.CriterionType == CriterionType.RECIST1Pointt1_MB && t.SubjectVisit.SubjectId == task.SubjectId && t.SubjectVisitId == task.SourceSubjectVisitId);
|
await _subjectCriteriaEvaluationVisitStudyFilterRepository.BatchDeleteNoTrackingAsync(t => t.TrialReadingCriterion.CriterionType == CriterionType.RECIST1Pointt1_MB && t.SubjectVisit.SubjectId == task.SubjectId && t.SubjectVisitId == task.SourceSubjectVisitId);
|
||||||
|
|
||||||
|
otherVisitIdList = otherVisitIdList.Where(t => t != task.SourceSubjectVisitId.Value).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//BM后续访视 ,筛选状态不变,任务生成状态重置(实际该访视任务状态 可能是重阅重置了或者失效了,需要后续生成,或者取消分配了,需要后续重新分配)
|
//BM后续访视 ,筛选状态不变,任务生成状态重置(实际该访视任务状态 可能是重阅重置了或者失效了,需要后续生成,或者取消分配了,需要后续重新分配)
|
||||||
await _subjectCriteriaEvaluationVisitFilterRepository.UpdatePartialFromQueryAsync(t => t.TrialReadingCriterion.CriterionType == CriterionType.RECIST1Pointt1_MB && t.SubjectVisit.SubjectId == task.SubjectId && otherVisitIdList.Contains(t.SubjectVisitId),
|
await _subjectCriteriaEvaluationVisitFilterRepository.UpdatePartialFromQueryAsync(t => t.TrialReadingCriterion.CriterionType == CriterionType.RECIST1Pointt1_MB && t.SubjectVisit.SubjectId == task.SubjectId && otherVisitIdList.Contains(t.SubjectVisitId),
|
||||||
t => new SubjectCriteriaEvaluationVisitFilter()
|
t => new SubjectCriteriaEvaluationVisitFilter()
|
||||||
|
|
@ -2723,6 +2731,13 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||||
//默认影响的都是该标准的任务
|
//默认影响的都是该标准的任务
|
||||||
filterExpression = filterExpression.And(t => t.TrialReadingCriterionId == filterObj.TrialReadingCriterionId);
|
filterExpression = filterExpression.And(t => t.TrialReadingCriterionId == filterObj.TrialReadingCriterionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//退回只影响有序的后续所有的,无序的当前访视
|
||||||
|
if (isReReading == false)
|
||||||
|
{
|
||||||
|
filterExpression = filterExpression.And(t => (t.TrialReadingCriterion.IsReadingTaskViewInOrder == ReadingOrder.InOrder) ||
|
||||||
|
(t.TrialReadingCriterion.IsReadingTaskViewInOrder != ReadingOrder.InOrder && t.SourceSubjectVisitId == filterObj.SourceSubjectVisitId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -603,16 +603,21 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||||
|
|
||||||
var tu = CreatNewTUExport(task, lesion, visitIndexNoDic, translateDataList, isEn_Us);
|
var tu = CreatNewTUExport(task, lesion, visitIndexNoDic, translateDataList, isEn_Us);
|
||||||
|
|
||||||
if (lesion.OrganInfoId.HasValue)
|
|
||||||
|
if (!tuList.Any(t => t.SubjectCode == task.SubjectCode && t.ARM_TumorNo == $"{task.ArmEnumStr}_{lesion.LessionCode}"))
|
||||||
{
|
{
|
||||||
tu.BodyPart = _userInfo.IsEn_Us ? trialOrganDic[lesion.OrganInfoId.Value].PartEN : trialOrganDic[lesion.OrganInfoId.Value].Part;
|
|
||||||
|
if (lesion.OrganInfoId.HasValue)
|
||||||
|
{
|
||||||
|
tu.BodyPart = _userInfo.IsEn_Us ? trialOrganDic[lesion.OrganInfoId.Value].PartEN : trialOrganDic[lesion.OrganInfoId.Value].Part;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Fill_Resisit_Lugano_TUExport(tu, lesion);
|
||||||
|
|
||||||
|
tuList.Add(tu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Fill_Resisit_Lugano_TUExport(tu, lesion);
|
|
||||||
|
|
||||||
tuList.Add(tu);
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,10 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
|
|
||||||
public class UnionDocumentWithConfirmInfoView : UnionDocumentView
|
public class UnionDocumentWithConfirmInfoView : UnionDocumentView
|
||||||
{
|
{
|
||||||
|
public DocUserSignType SysDocUserSignType { get; set; }
|
||||||
|
|
||||||
|
public bool IsConfirmIdentityUserInner { get; set; }
|
||||||
|
|
||||||
public Guid TrialId { get; set; }
|
public Guid TrialId { get; set; }
|
||||||
|
|
||||||
public bool IsNeedSendEmial { get; set; }
|
public bool IsNeedSendEmial { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
public new List<UserTypeEnum> CopyUserTypeList => TrialEmailNoticeUserList.Where(t => t.EmailUserType == EmailUserType.Copy).Select(t => t.UserType).ToList();
|
public new List<UserTypeEnum> CopyUserTypeList => TrialEmailNoticeUserList.Where(t => t.EmailUserType == EmailUserType.Copy).Select(t => t.UserType).ToList();
|
||||||
|
|
||||||
public List<CriterionType>? SysCriterionTypeList { get; set; }
|
public List<CriterionType>? SysCriterionTypeList { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -127,7 +128,7 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
{
|
{
|
||||||
public Guid SubjectId { get; set; }
|
public Guid SubjectId { get; set; }
|
||||||
|
|
||||||
public Guid TrialReadingCriterionId { get; set; }
|
public CriterionType CriterionType { get; set; }
|
||||||
|
|
||||||
public EmailBusinessScenario BusinessScenarioEnum { get; set; }
|
public EmailBusinessScenario BusinessScenarioEnum { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
Task<IResponseOutput> UserConfirm(UserConfirmCommand userConfirmCommand);
|
Task<IResponseOutput> UserConfirm(UserConfirmCommand userConfirmCommand);
|
||||||
Task<List<TrialUserDto>> GetTrialUserSelect(Guid trialId);
|
Task<List<TrialUserDto>> GetTrialUserSelect(Guid trialId);
|
||||||
|
|
||||||
|
Task<IResponseOutput<PageOutput<UnionDocumentWithConfirmInfoView>>> GetSysDocumentConfirmList(SystemDocQuery inQuery);
|
||||||
|
|
||||||
|
|
||||||
//Task<PageOutput<DocumentUnionWithUserStatView>> GetTrialSystemDocumentList(DocumentTrialUnionQuery querySystemDocument);
|
//Task<PageOutput<DocumentUnionWithUserStatView>> GetTrialSystemDocumentList(DocumentTrialUnionQuery querySystemDocument);
|
||||||
//List<TrialUserUnionDocumentView> GetTrialUserDocumentList(Guid trialId);
|
//List<TrialUserUnionDocumentView> GetTrialUserDocumentList(Guid trialId);
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,7 @@ namespace IRaCIS.Core.Application.Services
|
||||||
await _systemDocumentRepository.BatchUpdateNoTrackingAsync(x => inDto.Ids.Contains(x.Id), x => new SystemDocument()
|
await _systemDocumentRepository.BatchUpdateNoTrackingAsync(x => inDto.Ids.Contains(x.Id), x => new SystemDocument()
|
||||||
{
|
{
|
||||||
IsPublish = true,
|
IsPublish = true,
|
||||||
|
PublishDate= DateTime.Now,
|
||||||
IsDeleted = false,
|
IsDeleted = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -301,6 +302,7 @@ namespace IRaCIS.Core.Application.Services
|
||||||
{
|
{
|
||||||
AttachmentCount=sysDoc.SystemDocumentAttachmentList.Where(z=>!z.OffLine).Count(),
|
AttachmentCount=sysDoc.SystemDocumentAttachmentList.Where(z=>!z.OffLine).Count(),
|
||||||
IsSystemDoc = true,
|
IsSystemDoc = true,
|
||||||
|
IsPublish=sysDoc.IsPublish,
|
||||||
CurrentStaffTrainDays=sysDoc.CurrentStaffTrainDays,
|
CurrentStaffTrainDays=sysDoc.CurrentStaffTrainDays,
|
||||||
NewStaffTrainDays = sysDoc.NewStaffTrainDays,
|
NewStaffTrainDays = sysDoc.NewStaffTrainDays,
|
||||||
Id = sysDoc.Id,
|
Id = sysDoc.Id,
|
||||||
|
|
@ -326,6 +328,7 @@ namespace IRaCIS.Core.Application.Services
|
||||||
//UserTypeShortName = user.UserTypeRole.UserTypeShortName
|
//UserTypeShortName = user.UserTypeRole.UserTypeShortName
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var list = await query
|
var list = await query
|
||||||
//过滤掉删除的,并且没有签名的
|
//过滤掉删除的,并且没有签名的
|
||||||
.Where(t => !(t.IsDeleted == true && t.ConfirmTime == null))
|
.Where(t => !(t.IsDeleted == true && t.ConfirmTime == null))
|
||||||
|
|
|
||||||
|
|
@ -101,8 +101,9 @@ namespace IRaCIS.Core.Application.Services
|
||||||
await _trialDocumentRepository.UpdatePartialFromQueryAsync(x => inDto.Ids.Contains(x.Id), x => new TrialDocument()
|
await _trialDocumentRepository.UpdatePartialFromQueryAsync(x => inDto.Ids.Contains(x.Id), x => new TrialDocument()
|
||||||
{
|
{
|
||||||
IsPublish = true,
|
IsPublish = true,
|
||||||
|
PublishDate = DateTime.Now,
|
||||||
IsDeleted = false,
|
IsDeleted = false,
|
||||||
},false,true);
|
}, false, true);
|
||||||
await _trialDocumentRepository.SaveChangesAsync();
|
await _trialDocumentRepository.SaveChangesAsync();
|
||||||
Console.WriteLine("开始 发布项目文档");
|
Console.WriteLine("开始 发布项目文档");
|
||||||
|
|
||||||
|
|
@ -150,7 +151,7 @@ namespace IRaCIS.Core.Application.Services
|
||||||
{
|
{
|
||||||
// 从新作用域解析服务
|
// 从新作用域解析服务
|
||||||
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
|
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
|
||||||
await mediator.Publish(new ImageQCRecurringEvent { TrialId= Guid.Parse("08de2254-5d7d-581a-0242-0a0001000000") });
|
await mediator.Publish(new ImageQCRecurringEvent { TrialId = Guid.Parse("08de2254-5d7d-581a-0242-0a0001000000") });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return ResponseOutput.Result(true);
|
return ResponseOutput.Result(true);
|
||||||
|
|
@ -171,7 +172,7 @@ namespace IRaCIS.Core.Application.Services
|
||||||
.WhereIf(inQuery.UserTypeId != null, t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == inQuery.UserTypeId))
|
.WhereIf(inQuery.UserTypeId != null, t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == inQuery.UserTypeId))
|
||||||
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted)
|
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted)
|
||||||
.WhereIf(inQuery.IsPublish != null, t => t.IsPublish == inQuery.IsPublish)
|
.WhereIf(inQuery.IsPublish != null, t => t.IsPublish == inQuery.IsPublish)
|
||||||
.WhereIf(!string.IsNullOrEmpty(inQuery.FileTypeCode), t => t.FileType.Code== inQuery.FileTypeCode)
|
.WhereIf(!string.IsNullOrEmpty(inQuery.FileTypeCode), t => t.FileType.Code == inQuery.FileTypeCode)
|
||||||
.ProjectTo<TrialDocumentView>(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken, isEn_Us = _userInfo.IsEn_Us });
|
.ProjectTo<TrialDocumentView>(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken, isEn_Us = _userInfo.IsEn_Us });
|
||||||
|
|
||||||
return await trialDocumentQueryable.ToPagedListAsync(inQuery);
|
return await trialDocumentQueryable.ToPagedListAsync(inQuery);
|
||||||
|
|
@ -180,7 +181,7 @@ namespace IRaCIS.Core.Application.Services
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<PageOutput<TrialSignDocView>> GetTrialSignDocumentList(TrialDocQuery inQuery)
|
public async Task<PageOutput<TrialSignDocView>> GetTrialSignDocumentList(TrialDocQuery inQuery)
|
||||||
{
|
{
|
||||||
var trialDocQueryable = from trialDoc in _trialDocumentRepository.Where(t=>t.IsPublish)
|
var trialDocQueryable = from trialDoc in _trialDocumentRepository.Where(t => t.IsPublish)
|
||||||
.WhereIf(inQuery.TrialId != null, t => t.TrialId == inQuery.TrialId)
|
.WhereIf(inQuery.TrialId != null, t => t.TrialId == inQuery.TrialId)
|
||||||
.Where(t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId))
|
.Where(t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId))
|
||||||
|
|
||||||
|
|
@ -353,7 +354,7 @@ namespace IRaCIS.Core.Application.Services
|
||||||
#region 统一用户修改
|
#region 统一用户修改
|
||||||
|
|
||||||
var systemDocQuery =
|
var systemDocQuery =
|
||||||
from sysDoc in _systemDocumentRepository.Where(t=>t.IsPublish).Where(t => t.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == _userInfo.UserTypeId))
|
from sysDoc in _systemDocumentRepository.Where(t => t.IsPublish).Where(t => t.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == _userInfo.UserTypeId))
|
||||||
//外部人员 只签署 外部需要签署的
|
//外部人员 只签署 外部需要签署的
|
||||||
.WhereIf(isInternal == false, t => t.DocUserSignType == DocUserSignType.InnerAndOuter)
|
.WhereIf(isInternal == false, t => t.DocUserSignType == DocUserSignType.InnerAndOuter)
|
||||||
from trialUser in _trialIdentityUserRepository.AsQueryable(false)
|
from trialUser in _trialIdentityUserRepository.AsQueryable(false)
|
||||||
|
|
@ -392,7 +393,7 @@ namespace IRaCIS.Core.Application.Services
|
||||||
|
|
||||||
//项目文档查询
|
//项目文档查询
|
||||||
var trialDocQuery =
|
var trialDocQuery =
|
||||||
from trialDoc in _trialDocumentRepository.Where(t=>t.IsPublish).Where(t => t.TrialId == inQuery.TrialId).Where(t => t.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == _userInfo.UserTypeId))
|
from trialDoc in _trialDocumentRepository.Where(t => t.IsPublish).Where(t => t.TrialId == inQuery.TrialId).Where(t => t.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == _userInfo.UserTypeId))
|
||||||
from trialUser in _trialIdentityUserRepository.AsQueryable(false).Where(t => t.TrialId == inQuery.TrialId && t.IdentityUserId == _userInfo.IdentityUserId
|
from trialUser in _trialIdentityUserRepository.AsQueryable(false).Where(t => t.TrialId == inQuery.TrialId && t.IdentityUserId == _userInfo.IdentityUserId
|
||||||
&& t.TrialUserRoleList.Any(t => trialDoc.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == t.UserRole.UserTypeId)))
|
&& t.TrialUserRoleList.Any(t => trialDoc.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == t.UserRole.UserTypeId)))
|
||||||
|
|
||||||
|
|
@ -582,7 +583,7 @@ namespace IRaCIS.Core.Application.Services
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
var needSignTrialDocCount = await _trialDocumentRepository.AsQueryable(true).Where(t=>t.IsPublish)
|
var needSignTrialDocCount = await _trialDocumentRepository.AsQueryable(true).Where(t => t.IsPublish)
|
||||||
.Where(t => t.TrialId == inQuery.TrialId && t.Trial.TrialStatusStr != StaticData.TrialState.TrialStopped)
|
.Where(t => t.TrialId == inQuery.TrialId && t.Trial.TrialStatusStr != StaticData.TrialState.TrialStopped)
|
||||||
.Where(t => t.Trial.TrialIdentityUserList.Any(t => t.IdentityUserId == _userInfo.IdentityUserId && t.TrialUserRoleList.Any(t => t.UserRole.UserTypeId == _userInfo.UserTypeId)))
|
.Where(t => t.Trial.TrialIdentityUserList.Any(t => t.IdentityUserId == _userInfo.IdentityUserId && t.TrialUserRoleList.Any(t => t.UserRole.UserTypeId == _userInfo.UserTypeId)))
|
||||||
.Where(t => t.IsDeleted == false && !t.TrialDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.IdentityUserId && t.ConfirmTime != null) && t.NeedConfirmedUserTypeList.Any(u => u.NeedConfirmUserTypeId == _userInfo.UserTypeId))
|
.Where(t => t.IsDeleted == false && !t.TrialDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.IdentityUserId && t.ConfirmTime != null) && t.NeedConfirmedUserTypeList.Any(u => u.NeedConfirmUserTypeId == _userInfo.UserTypeId))
|
||||||
|
|
@ -713,7 +714,8 @@ namespace IRaCIS.Core.Application.Services
|
||||||
.WhereIf(inQuery.BeginCreateTime != null, t => t.CreateTime >= inQuery.BeginCreateTime)
|
.WhereIf(inQuery.BeginCreateTime != null, t => t.CreateTime >= inQuery.BeginCreateTime)
|
||||||
.WhereIf(inQuery.EndCreateTime != null, t => t.CreateTime <= inQuery.EndCreateTime)
|
.WhereIf(inQuery.EndCreateTime != null, t => t.CreateTime <= inQuery.EndCreateTime)
|
||||||
.WhereIf(!string.IsNullOrEmpty(inQuery.UserName), t => t.UserName.Contains(inQuery.UserName))
|
.WhereIf(!string.IsNullOrEmpty(inQuery.UserName), t => t.UserName.Contains(inQuery.UserName))
|
||||||
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted);
|
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted)
|
||||||
|
.WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.EA, t => t.ConfirmTime != null);
|
||||||
|
|
||||||
var result = await unionQuery.ToPagedListAsync(inQuery);
|
var result = await unionQuery.ToPagedListAsync(inQuery);
|
||||||
|
|
||||||
|
|
@ -945,15 +947,18 @@ namespace IRaCIS.Core.Application.Services
|
||||||
var isEA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.EA;
|
var isEA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.EA;
|
||||||
|
|
||||||
//EA 但是没有在进行的培训记录查看权限,那么返回空数据
|
//EA 但是没有在进行的培训记录查看权限,那么返回空数据
|
||||||
if (isEA && !_auditRecordRepository.Any(t => t.IsViewTrainingRecord && t.AuditState == AuditState.Ongoing && t.AuditRecordIdentityUserList.Any(c=>c.IdentityUserId==_userInfo.IdentityUserId)))
|
if (isEA && !_auditRecordRepository.Any(t => t.IsViewTrainingRecord && t.AuditState == AuditState.Ongoing && t.AuditRecordIdentityUserList.Any(c => c.IdentityUserId == _userInfo.IdentityUserId)))
|
||||||
{
|
{
|
||||||
return ResponseOutput.Ok(new PageOutput<UnionDocumentWithConfirmInfoView>());
|
return ResponseOutput.Ok(new PageOutput<UnionDocumentWithConfirmInfoView>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var systemDocQuery =
|
var systemDocQuery =
|
||||||
from sysDoc in _systemDocumentRepository.AsQueryable(false)
|
from sysDoc in _systemDocumentRepository.Where(t => t.IsPublish)
|
||||||
.Where(t => inQuery.UserTypeId != null ? t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == inQuery.UserTypeId) : true)
|
.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)))
|
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.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 => inQuery.UserTypeId != null ? t.UserRoleList.Any(t => t.UserTypeId == inQuery.UserTypeId && t.IsUserRoleDisabled == false) : true)
|
||||||
.Where(t => isEA ? t.IsZhiZhun == true : true) //EA 只能查看内部人员文档
|
.Where(t => isEA ? t.IsZhiZhun == true : true) //EA 只能查看内部人员文档
|
||||||
|
|
@ -962,6 +967,9 @@ namespace IRaCIS.Core.Application.Services
|
||||||
select new UnionDocumentWithConfirmInfoView()
|
select new UnionDocumentWithConfirmInfoView()
|
||||||
{
|
{
|
||||||
IsSystemDoc = true,
|
IsSystemDoc = true,
|
||||||
|
SysDocUserSignType = sysDoc.DocUserSignType,
|
||||||
|
IsConfirmIdentityUserInner = identityUser.IsZhiZhun,
|
||||||
|
IsPublish=sysDoc.IsPublish,
|
||||||
Id = sysDoc.Id,
|
Id = sysDoc.Id,
|
||||||
CreateTime = sysDoc.CreateTime,
|
CreateTime = sysDoc.CreateTime,
|
||||||
IsDeleted = sysDoc.IsDeleted,
|
IsDeleted = sysDoc.IsDeleted,
|
||||||
|
|
@ -993,14 +1001,16 @@ namespace IRaCIS.Core.Application.Services
|
||||||
};
|
};
|
||||||
|
|
||||||
var unionQuery = systemDocQuery.IgnoreQueryFilters().Where(t => !(t.IsDeleted == true && t.ConfirmTime == null))
|
var unionQuery = systemDocQuery.IgnoreQueryFilters().Where(t => !(t.IsDeleted == true && t.ConfirmTime == null))
|
||||||
|
//外部人员 只签署 外部需要签署的
|
||||||
|
.Where(t => t.IsConfirmIdentityUserInner == false ? t.SysDocUserSignType == DocUserSignType.InnerAndOuter : true)
|
||||||
.WhereIf(!string.IsNullOrEmpty(inQuery.Name), t => t.Name.Contains(inQuery.Name))
|
.WhereIf(!string.IsNullOrEmpty(inQuery.Name), t => t.Name.Contains(inQuery.Name))
|
||||||
.WhereIf(inQuery.FileTypeId != null, t => t.FileTypeId == inQuery.FileTypeId)
|
.WhereIf(inQuery.FileTypeId != null, t => t.FileTypeId == inQuery.FileTypeId)
|
||||||
.WhereIf(inQuery.IsConfirmed == true, t => t.ConfirmTime != null)
|
.WhereIf(inQuery.IsConfirmed == true, t => t.ConfirmTime != null)
|
||||||
.WhereIf(inQuery.IsConfirmed == false, t => t.ConfirmTime == null)
|
.WhereIf(inQuery.IsConfirmed == false, t => t.ConfirmTime == null)
|
||||||
.WhereIf(inQuery.StartConfirmTime != null, t => t.ConfirmTime >= inQuery.StartConfirmTime.Value)
|
.WhereIf(inQuery.StartConfirmTime != null, t => t.ConfirmTime >= inQuery.StartConfirmTime.Value)
|
||||||
.WhereIf(inQuery.EndConfirmTime != null, t => t.ConfirmTime <= inQuery.EndConfirmTime.Value)
|
.WhereIf(inQuery.EndConfirmTime != null, t => t.ConfirmTime <= inQuery.EndConfirmTime.Value)
|
||||||
.WhereIf(inQuery.BeginCreateTime != null, t => t.CreateTime >= inQuery.BeginCreateTime)
|
.WhereIf(inQuery.BeginCreateTime != null, t => t.CreateTime >= inQuery.BeginCreateTime)
|
||||||
.WhereIf(inQuery.EndCreateTime != null, t => t.CreateTime <= inQuery.EndCreateTime)
|
.WhereIf(inQuery.EndCreateTime != null, t => t.CreateTime <= inQuery.EndCreateTime)
|
||||||
.WhereIf(!string.IsNullOrEmpty(inQuery.UserName), t => t.UserName.Contains(inQuery.UserName))
|
.WhereIf(!string.IsNullOrEmpty(inQuery.UserName), t => t.UserName.Contains(inQuery.UserName))
|
||||||
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted)
|
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted)
|
||||||
.WhereIf(isInternal == false, t => t.ConfirmTime != null); //不是内部的人,看有签名时间的
|
.WhereIf(isInternal == false, t => t.ConfirmTime != null); //不是内部的人,看有签名时间的
|
||||||
|
|
|
||||||
|
|
@ -773,10 +773,12 @@ namespace IRaCIS.Core.Application.Service
|
||||||
{
|
{
|
||||||
var subjectId = generateEmailCommand.SubjectId;
|
var subjectId = generateEmailCommand.SubjectId;
|
||||||
var businessScenarioEnum = generateEmailCommand.BusinessScenarioEnum;
|
var businessScenarioEnum = generateEmailCommand.BusinessScenarioEnum;
|
||||||
var trialReadingCriterionId = generateEmailCommand.TrialReadingCriterionId;
|
var criterionType = generateEmailCommand.CriterionType;
|
||||||
|
|
||||||
|
|
||||||
var trialConfig = await _subjectRepository.Where(t => t.Id == subjectId).Select(t => new { t.Trial.IsEnrollementQualificationConfirm, t.Trial.IsPDProgressView }).FirstNotNullAsync();
|
|
||||||
|
var trialConfig = await _subjectRepository.Where(t => t.Id == subjectId).Select(t => new { t.Trial.IsEnrollementQualificationConfirm, t.Trial.IsPDProgressView, t.TrialId }).FirstNotNullAsync();
|
||||||
|
var trialReadingCriterionId = _readingQuestionCriterionTrialRepository.Where(t => t.CriterionType == criterionType && t.TrialId == trialConfig.TrialId).Select(t => t.Id).FirstOrDefault();
|
||||||
|
|
||||||
//找到入组确认 或者Pd 进展 已生成任务的 访视
|
//找到入组确认 或者Pd 进展 已生成任务的 访视
|
||||||
var subjectVisitList = await _subjectVisitRepository.Where(t => t.SubjectId == subjectId & t.CheckState == CheckStateEnum.CVPassed && (t.IsEnrollmentConfirm == true || t.PDState == PDStateEnum.PDProgress)).ToListAsync();
|
var subjectVisitList = await _subjectVisitRepository.Where(t => t.SubjectId == subjectId & t.CheckState == CheckStateEnum.CVPassed && (t.IsEnrollmentConfirm == true || t.PDState == PDStateEnum.PDProgress)).ToListAsync();
|
||||||
|
|
@ -1671,10 +1673,10 @@ x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.LesionNumber && x.Readi
|
||||||
{
|
{
|
||||||
//await SyncSystemEmainCofigDocListAsync(inQuery.TrialId);
|
//await SyncSystemEmainCofigDocListAsync(inQuery.TrialId);
|
||||||
|
|
||||||
var trialConfig = _trialRepository.Where(t => t.Id == inQuery.TrialId).Select(t => new { t.IsEnrollementQualificationConfirm, t.IsPDProgressView }).First();
|
var trialConfig = _trialRepository.Where(t => t.Id == inQuery.TrialId).Select(t => new { t.IsEnrollementQualificationConfirm, t.IsPDProgressView, TrialCriterionTypeList = t.TrialReadingCriterionList.Where(t => t.IsSigned).Select(t => t.CriterionType).ToList() }).First();
|
||||||
|
|
||||||
var trialEmailNoticeConfigQueryable = _trialEmailNoticeConfigRepository.Where(t => t.TrialId == inQuery.TrialId)
|
var trialEmailNoticeConfigQueryable = _trialEmailNoticeConfigRepository.Where(t => t.TrialId == inQuery.TrialId)
|
||||||
.WhereIf(inQuery.EmailTopic.IsNotNullOrEmpty(), t => t.EmailTopic.Contains(inQuery.EmailTopic)||t.EmailTopicCN.Contains(inQuery.EmailTopic))
|
.WhereIf(inQuery.EmailTopic.IsNotNullOrEmpty(), t => t.EmailTopic.Contains(inQuery.EmailTopic) || t.EmailTopicCN.Contains(inQuery.EmailTopic))
|
||||||
.WhereIf(inQuery.IsDistinguishCriteria == false, t => t.IsDistinguishCriteria == false)
|
.WhereIf(inQuery.IsDistinguishCriteria == false, t => t.IsDistinguishCriteria == false)
|
||||||
.WhereIf(inQuery.IsDistinguishCriteria == true, t => t.IsDistinguishCriteria == true)
|
.WhereIf(inQuery.IsDistinguishCriteria == true, t => t.IsDistinguishCriteria == true)
|
||||||
.WhereIf(inQuery.CriterionTypeEnum != null, t => t.CriterionTypeList.Any(c => c == inQuery.CriterionTypeEnum))
|
.WhereIf(inQuery.CriterionTypeEnum != null, t => t.CriterionTypeList.Any(c => c == inQuery.CriterionTypeEnum))
|
||||||
|
|
@ -1692,6 +1694,7 @@ x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.LesionNumber && x.Readi
|
||||||
var orderQuery = inQuery.Asc ? trialEmailNoticeConfigQueryable.OrderBy(sortField) : trialEmailNoticeConfigQueryable.OrderBy(sortField + " desc");
|
var orderQuery = inQuery.Asc ? trialEmailNoticeConfigQueryable.OrderBy(sortField) : trialEmailNoticeConfigQueryable.OrderBy(sortField + " desc");
|
||||||
var list = await orderQuery.ToListAsync();
|
var list = await orderQuery.ToListAsync();
|
||||||
|
|
||||||
|
|
||||||
return ResponseOutput.Ok(list, trialConfig);
|
return ResponseOutput.Ok(list, trialConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -117,4 +117,10 @@ namespace IRaCIS.Core.Application.Contracts.Dicom.DTO
|
||||||
public bool HasLabel { get; set; } = false;
|
public bool HasLabel { get; set; } = false;
|
||||||
public bool KeySeries { get; set; } = false;
|
public bool KeySeries { get; set; } = false;
|
||||||
}
|
}
|
||||||
|
public class UpdateImageResizeDTO
|
||||||
|
{
|
||||||
|
public Guid SeriesId { get; set; }
|
||||||
|
|
||||||
|
public string ImageResizePath { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,8 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
|
|
||||||
public string Bodypart { get; set; } = string.Empty;
|
public string Bodypart { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string BodyPartForEditOther { get; set; } = string.Empty;
|
||||||
|
|
||||||
public DateTime? StudyTime { get; set; }
|
public DateTime? StudyTime { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1185,6 +1185,9 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
|
|
||||||
foreach (var item in list.GroupBy(t => new { t.StudyInstanceUid, t.DicomStudyId }))
|
foreach (var item in list.GroupBy(t => new { t.StudyInstanceUid, t.DicomStudyId }))
|
||||||
{
|
{
|
||||||
|
|
||||||
|
var studyUid = item.Key.StudyInstanceUid;
|
||||||
|
|
||||||
var ossFolder = $"{pathInfo.TrialId}/Image/{pathInfo.SubjectId}/{pathInfo.VisitId}/{item.Key.StudyInstanceUid}";
|
var ossFolder = $"{pathInfo.TrialId}/Image/{pathInfo.SubjectId}/{pathInfo.VisitId}/{item.Key.StudyInstanceUid}";
|
||||||
|
|
||||||
var isSucess = await SafeBussinessHelper.RunAsync(async () => await DicomDIRHelper.GenerateStudyDIRAndUploadAsync(item.ToList(), dirDic, ossFolder, _oSSService));
|
var isSucess = await SafeBussinessHelper.RunAsync(async () => await DicomDIRHelper.GenerateStudyDIRAndUploadAsync(item.ToList(), dirDic, ossFolder, _oSSService));
|
||||||
|
|
@ -1192,7 +1195,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
|
|
||||||
if (isSucess)
|
if (isSucess)
|
||||||
{
|
{
|
||||||
await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/DICOMDIR" });
|
await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/{dirDic[$"{studyUid}_DICOMDIR"]}" });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1625,6 +1628,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
|
|
||||||
var ossFolder = $"{pathInfo.TrialId}/Image/{pathInfo.SubjectId}/{visitId}/{item.Key.StudyInstanceUid}";
|
var ossFolder = $"{pathInfo.TrialId}/Image/{pathInfo.SubjectId}/{visitId}/{item.Key.StudyInstanceUid}";
|
||||||
|
|
||||||
|
var studyUid = item.Key.StudyInstanceUid;
|
||||||
|
|
||||||
var isSucess = await SafeBussinessHelper.RunAsync(async () => await DicomDIRHelper.GenerateStudyDIRAndUploadAsync(item.ToList(), dirDic, ossFolder, _oSSService));
|
var isSucess = await SafeBussinessHelper.RunAsync(async () => await DicomDIRHelper.GenerateStudyDIRAndUploadAsync(item.ToList(), dirDic, ossFolder, _oSSService));
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1632,11 +1637,11 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
{
|
{
|
||||||
if (isTaskStudy)
|
if (isTaskStudy)
|
||||||
{
|
{
|
||||||
await _taskStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new TaskStudy() { StudyDIRPath = $"/{ossFolder}/DICOMDIR" });
|
await _taskStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new TaskStudy() { StudyDIRPath = $"/{ossFolder}/{dirDic[$"{studyUid}_DICOMDIR"]}" });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/DICOMDIR" });
|
await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/{dirDic[$"{studyUid}_DICOMDIR"]}" });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -2303,6 +2308,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
|
|
||||||
var subjectId = item.First().SubjectId;
|
var subjectId = item.First().SubjectId;
|
||||||
|
|
||||||
|
var studyUid = item.Key.StudyInstanceUid;
|
||||||
|
|
||||||
var ossFolder = $"{inCommand.TrialId}/Image/{subjectId}/{visitId}/{item.Key.StudyInstanceUid}";
|
var ossFolder = $"{inCommand.TrialId}/Image/{subjectId}/{visitId}/{item.Key.StudyInstanceUid}";
|
||||||
|
|
||||||
|
|
@ -2310,7 +2316,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
|
|
||||||
if (isSucess)
|
if (isSucess)
|
||||||
{
|
{
|
||||||
await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/DICOMDIR" });
|
await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/{dirDic[$"{studyUid}_DICOMDIR"]}" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,18 @@ namespace IRaCIS.Core.Application.Services
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新缩略图路径
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IResponseOutput> UpdateImageResizePath(UpdateImageResizeDTO dto)
|
||||||
|
{
|
||||||
|
await _seriesRepository.UpdatePartialFromQueryAsync(t => t.Id == dto.SeriesId, u => new DicomSeries() { ImageResizePath = dto.ImageResizePath }, true);
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
//医生读片那一块有耦合,关键序列 这里暂时留存
|
//医生读片那一块有耦合,关键序列 这里暂时留存
|
||||||
/// <summary> 指定资源Id,获取Dicom检查所属序列信息列表 </summary>
|
/// <summary> 指定资源Id,获取Dicom检查所属序列信息列表 </summary>
|
||||||
|
|
|
||||||
|
|
@ -382,7 +382,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
|
|
||||||
Id = t.Id,
|
Id = t.Id,
|
||||||
|
|
||||||
Bodypart = t.BodyPartExamined,
|
Bodypart = t.BodyPartForEdit,
|
||||||
|
BodyPartForEditOther = t.BodyPartForEditOther,
|
||||||
|
|
||||||
Modalities = t.Modalities,
|
Modalities = t.Modalities,
|
||||||
|
|
||||||
|
|
@ -433,6 +434,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
Id = t.Id,
|
Id = t.Id,
|
||||||
|
|
||||||
Bodypart = t.BodyPart,
|
Bodypart = t.BodyPart,
|
||||||
|
BodyPartForEditOther=t.BodyPartForEditOther,
|
||||||
|
|
||||||
Modalities = t.Modality,
|
Modalities = t.Modality,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -308,6 +308,8 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
await _fusionCache.RemoveAsync(CacheKeys.UserLoginError(userName));
|
await _fusionCache.RemoveAsync(CacheKeys.UserLoginError(userName));
|
||||||
|
|
||||||
|
await _fusionCache.RemoveByTagAsync(CacheKeys.UserMFATag(identityUserId));
|
||||||
|
|
||||||
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = identityUserId, OptType = UserOptType.ResetPassword }, true);
|
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = identityUserId, OptType = UserOptType.ResetPassword }, true);
|
||||||
|
|
||||||
return ResponseOutput.Ok();
|
return ResponseOutput.Ok();
|
||||||
|
|
@ -316,7 +318,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 重置密码发邮件 (未登陆修改)
|
/// 重置密码发邮件 (未登陆修改-忘记密码)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="email"></param>
|
/// <param name="email"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
|
@ -357,6 +359,8 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
await _mailVerificationService.AnolymousSendEmailForResetAccount(email, verificationCode);
|
await _mailVerificationService.AnolymousSendEmailForResetAccount(email, verificationCode);
|
||||||
|
|
||||||
|
await _fusionCache.RemoveByTagAsync(CacheKeys.UserMFATag(existUser.Id));
|
||||||
|
|
||||||
return ResponseOutput.Ok();
|
return ResponseOutput.Ok();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -438,6 +442,10 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
await _mailVerificationService.AfterUserModifyPasswordSendEmailAsync(identityUserId);
|
await _mailVerificationService.AfterUserModifyPasswordSendEmailAsync(identityUserId);
|
||||||
|
|
||||||
|
|
||||||
|
var find = await _identityUserRepository.FindAsync(identityUserId);
|
||||||
|
await _fusionCache.RemoveAsync(CacheKeys.UserLoginError(find.UserName));
|
||||||
|
|
||||||
return ResponseOutput.Ok();
|
return ResponseOutput.Ok();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -485,6 +493,8 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
await _mailVerificationService.AfterUserModifyPasswordSendEmailAsync(_userInfo.IdentityUserId);
|
await _mailVerificationService.AfterUserModifyPasswordSendEmailAsync(_userInfo.IdentityUserId);
|
||||||
|
|
||||||
|
await _fusionCache.RemoveByTagAsync(CacheKeys.UserMFATag(_userInfo.IdentityUserId));
|
||||||
|
|
||||||
return ResponseOutput.Result(success);
|
return ResponseOutput.Result(success);
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -861,8 +871,9 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
if (isRemember)
|
if (isRemember)
|
||||||
{
|
{
|
||||||
await _fusionCache.SetAsync(CacheKeys.UserMFAVerifyPass(identityUserId, _userInfo.BrowserFingerprint), _userInfo.BrowserFingerprint, TimeSpan.FromMinutes(_serviceVerifyConfigConfig.UserMFAVerifyMinutes));
|
await _fusionCache.SetAsync(CacheKeys.UserMFAVerifyPass(identityUserId, _userInfo.BrowserFingerprint), _userInfo.BrowserFingerprint,
|
||||||
|
TimeSpan.FromMinutes(_serviceVerifyConfigConfig.UserMFAVerifyMinutes), new[] { CacheKeys.UserMFATag(identityUserId) });
|
||||||
|
Log.Logger.Warning($"MFA登录记录:{_userInfo.UserName} 浏览器标识: {_userInfo.BrowserFingerprint} 设置缓存分钟{_serviceVerifyConfigConfig.UserMFAVerifyMinutes}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1066,7 +1077,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
//异地登录
|
//异地登录
|
||||||
loginUser.LoginState = 2;
|
loginUser.LoginState = 2;
|
||||||
|
|
||||||
|
await _fusionCache.RemoveByTagAsync(CacheKeys.UserMFATag(loginUser.IdentityUserId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1176,15 +1187,20 @@ namespace IRaCIS.Core.Application.Service
|
||||||
if (_verifyConfig.CurrentValue.OpenLoginMFA)
|
if (_verifyConfig.CurrentValue.OpenLoginMFA)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
if ((await _fusionCache.GetOrDefaultAsync(CacheKeys.UserMFAVerifyPass(identityUserId, _userInfo.BrowserFingerprint), "")) == _userInfo.BrowserFingerprint)
|
if ((await _fusionCache.GetOrDefaultAsync(CacheKeys.UserMFAVerifyPass(identityUserId, _userInfo.BrowserFingerprint), "")) == _userInfo.BrowserFingerprint)
|
||||||
{
|
{
|
||||||
userLoginReturnModel.IsMFA = false;
|
userLoginReturnModel.IsMFA = false;
|
||||||
|
|
||||||
|
Log.Logger.Warning($"MFA登录:{userName} 浏览器标识: {_userInfo.BrowserFingerprint},判断缓存里存在 ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//MFA 发送邮件
|
//MFA 发送邮件
|
||||||
|
|
||||||
userLoginReturnModel.IsMFA = true;
|
userLoginReturnModel.IsMFA = true;
|
||||||
|
|
||||||
|
Log.Logger.Warning($"MFA登录:{userName} 浏览器标识: {_userInfo.BrowserFingerprint} 判断缓存已经不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
var email = userLoginReturnModel.BasicInfo.EMail;
|
var email = userLoginReturnModel.BasicInfo.EMail;
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
|
|
||||||
public bool IsDeleted { get; set; }
|
public bool IsDeleted { get; set; }
|
||||||
|
|
||||||
|
public bool IsBeMark { get; set; }
|
||||||
|
|
||||||
public DateTime? MarkTime { get; set; }
|
public DateTime? MarkTime { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -983,6 +983,8 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ImageMark? ImageMarkEnum { get; set; }
|
public ImageMark? ImageMarkEnum { get; set; }
|
||||||
|
|
||||||
|
public string QuestionGroupName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 影像工具
|
/// 影像工具
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -485,13 +485,16 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
List<NoneDicomStudyView> result = await noneDicomStudyQueryable.OrderBy(x => x.ImageDate).ThenBy(x => x.CreateTime).ToListAsync();
|
List<NoneDicomStudyView> result = await noneDicomStudyQueryable.OrderBy(x => x.ImageDate).ThenBy(x => x.CreateTime).ToListAsync();
|
||||||
|
|
||||||
|
// 获取非DIOCM标记
|
||||||
var nonoDicomStudyFileIds = await _readingNoneDicomMarkRepository.Where(x => x.VisitTaskId == inDto.VisistTaskId).ToListAsync();
|
var nonoDicomStudyFileIds = await _readingNoneDicomMarkRepository.Where(x => x.VisitTaskId == inDto.VisistTaskId).ToListAsync();
|
||||||
|
|
||||||
|
var markFileId = nonoDicomStudyFileIds.Select(x => x.NoneDicomFileId).ToList();
|
||||||
if (nonoDicomStudyFileIds.Count > 0 && taskinfo.ReadingTaskState == ReadingTaskState.HaveSigned)
|
if (nonoDicomStudyFileIds.Count > 0 && taskinfo.ReadingTaskState == ReadingTaskState.HaveSigned)
|
||||||
{
|
{
|
||||||
var studyId = nonoDicomStudyFileIds.Select(x => x.StudyId).FirstOrDefault();
|
var studyId = nonoDicomStudyFileIds.Select(x => x.StudyId).FirstOrDefault();
|
||||||
|
|
||||||
var noneDicomids = nonoDicomStudyFileIds.Select(x => x.NoneDicomFileId).ToList();
|
var noneDicomids = nonoDicomStudyFileIds.Select(x => x.NoneDicomFileId).ToList();
|
||||||
|
|
||||||
var noneDicomStudyViewMark = new NoneDicomStudyView() { Id = Guid.NewGuid() };
|
var noneDicomStudyViewMark = new NoneDicomStudyView() { Id = Guid.NewGuid() };
|
||||||
noneDicomStudyViewMark.IsCriticalSequence = true;
|
noneDicomStudyViewMark.IsCriticalSequence = true;
|
||||||
noneDicomStudyViewMark.NoneDicomStudyFileList = await _noneDicomStudyFileRepository.Where(x => noneDicomids.Contains(x.Id)).ProjectTo<NoneDicomStudyFileView>(_mapper.ConfigurationProvider).ToListAsync();
|
noneDicomStudyViewMark.NoneDicomStudyFileList = await _noneDicomStudyFileRepository.Where(x => noneDicomids.Contains(x.Id)).ProjectTo<NoneDicomStudyFileView>(_mapper.ConfigurationProvider).ToListAsync();
|
||||||
|
|
@ -506,6 +509,14 @@ namespace IRaCIS.Core.Application.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var item in result)
|
||||||
|
{
|
||||||
|
foreach (var item1 in item.NoneDicomStudyFileList)
|
||||||
|
{
|
||||||
|
item1.IsBeMark= markFileId.Contains(item1.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var trialInfo = await _trialRepository.Where(x => x.Id == inDto.TrialId).Select(x => new
|
var trialInfo = await _trialRepository.Where(x => x.Id == inDto.TrialId).Select(x => new
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -374,7 +374,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
CreateMap<AddOrUpdateReadingQuestionTrialInDto, ReadingQuestionTrial>()
|
CreateMap<AddOrUpdateReadingQuestionTrialInDto, ReadingQuestionTrial>()
|
||||||
.ForMember(dest => dest.CreateUserRole, opt => opt.Ignore());
|
.ForMember(dest => dest.CreateUserRole, opt => opt.Ignore());
|
||||||
CreateMap<ReadingQuestionTrial, ReadingQuestionTrialView>()
|
CreateMap<ReadingQuestionTrial, ReadingQuestionTrialView>()
|
||||||
|
.ForMember(d => d.QuestionGroupName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupName : s.GroupInfo.GroupName))
|
||||||
.ForMember(d => d.GroupName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupName : s.GroupInfo.GroupName))
|
.ForMember(d => d.GroupName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupName : s.GroupInfo.GroupName))
|
||||||
.ForMember(d => d.GroupEnName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupEnName : s.GroupInfo.GroupEnName))
|
.ForMember(d => d.GroupEnName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupEnName : s.GroupInfo.GroupEnName))
|
||||||
.ForMember(d => d.ParentQuestionName, u => u.MapFrom(s => s.ParentReadingQuestionTrial == null ? string.Empty : s.ParentReadingQuestionTrial.QuestionName))
|
.ForMember(d => d.ParentQuestionName, u => u.MapFrom(s => s.ParentReadingQuestionTrial == null ? string.Empty : s.ParentReadingQuestionTrial.QuestionName))
|
||||||
|
|
|
||||||
|
|
@ -338,6 +338,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
trial.DeclarationTypes = $"|{string.Join('|', updateModel.DeclarationTypeEnumList.Select(x => ((int)x).ToString()).ToList())}|";
|
trial.DeclarationTypes = $"|{string.Join('|', updateModel.DeclarationTypeEnumList.Select(x => ((int)x).ToString()).ToList())}|";
|
||||||
trial.AttendedReviewerTypes = $"|{string.Join('|', updateModel.AttendedReviewerTypeEnumList.Select(x => ((int)x).ToString()).ToList())}|";
|
trial.AttendedReviewerTypes = $"|{string.Join('|', updateModel.AttendedReviewerTypeEnumList.Select(x => ((int)x).ToString()).ToList())}|";
|
||||||
|
|
||||||
|
trial.EmailFromName = $"{_systemEmailConfig.FromName}-{trial.TrialCode}";
|
||||||
trial.UpdateTime = DateTime.Now;
|
trial.UpdateTime = DateTime.Now;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ using IRaCIS.Core.Domain;
|
||||||
using IRaCIS.Core.Domain.Models;
|
using IRaCIS.Core.Domain.Models;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using IRaCIS.Core.Infra.EFCore.Context;
|
||||||
using IRaCIS.Core.Infrastructure;
|
using IRaCIS.Core.Infrastructure;
|
||||||
using IRaCIS.Core.Infrastructure.Encryption;
|
using IRaCIS.Core.Infrastructure.Encryption;
|
||||||
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
|
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
|
||||||
|
|
@ -38,6 +39,7 @@ using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||||
using SixLabors.ImageSharp.Processing;
|
using SixLabors.ImageSharp.Processing;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
@ -75,14 +77,85 @@ namespace IRaCIS.Core.Application.Service
|
||||||
{
|
{
|
||||||
public static int IntValue = 100;
|
public static int IntValue = 100;
|
||||||
|
|
||||||
|
|
||||||
|
public class ModelVerifyCommand
|
||||||
|
{
|
||||||
|
public int? IntNUllValue { get; set; }
|
||||||
|
public int IntValue { get; set; }
|
||||||
|
|
||||||
|
public string StringValue { get; set; }
|
||||||
|
|
||||||
|
public string? StringNUllValue { get; set; }
|
||||||
|
|
||||||
|
public string StringBackDefaultValue { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public Guid GuidValue { get; set; } = NewId.NextSequentialGuid();
|
||||||
|
|
||||||
|
public Guid? GuidNUllValue { get; set; }
|
||||||
|
|
||||||
|
[NotDefault]
|
||||||
|
public Guid GuidValueNotDefault { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public bool BoolValue { get; set; }
|
||||||
|
|
||||||
|
public bool? BoolNUllValue { get; set; }
|
||||||
|
|
||||||
|
public DateTime DateTimeValue { get; set; }
|
||||||
|
|
||||||
|
public DateTime? DateTimeNUllValue { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//创建一个模型验证的方法
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IResponseOutput> PostModelVerify(ModelVerifyCommand modelVerify)
|
||||||
|
{
|
||||||
|
|
||||||
|
return ResponseOutput.Ok(modelVerify);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
|
||||||
|
public async Task<IResponseOutput> CreatNewDBStruct()
|
||||||
|
{
|
||||||
|
var factory = new IRaCISDBContextFactory();
|
||||||
|
|
||||||
|
using var db = factory.CreateDbContext(Array.Empty<string>());
|
||||||
|
|
||||||
|
// ⚠️ 临时用,确认是测试库
|
||||||
|
// db.Database.EnsureDeleted();
|
||||||
|
|
||||||
|
db.Database.EnsureCreated();
|
||||||
|
|
||||||
|
Console.WriteLine("数据库结构已创建完成");
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
|
||||||
|
public async Task<IResponseOutput> DeleteCacheDIR()
|
||||||
|
{
|
||||||
|
var list = _dicomStudyRepository.Where(t => t.StudyDIRPath != "").Select(t => t.StudyDIRPath).ToList();
|
||||||
|
|
||||||
|
|
||||||
|
await _IOSSService.DeleteObjects(list.Select(t => t.TrimStart('/')).ToList(), true);
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<IResponseOutput> TestOSS(StorageClass storageClass)
|
public async Task<IResponseOutput> TestOSS(StorageClass storageClass)
|
||||||
{
|
{
|
||||||
if (storageClass == StorageClass.IA || storageClass == StorageClass.Archive || storageClass == StorageClass.ColdArchive || storageClass == StorageClass.DeepColdArchive)
|
if (storageClass == StorageClass.IA || storageClass == StorageClass.Archive || storageClass == StorageClass.ColdArchive || storageClass == StorageClass.DeepColdArchive)
|
||||||
{
|
{
|
||||||
//await _IOSSService.SetImmediateArchiveRule($"Test-Archive/Archive{(int)storageClass}/");
|
await _IOSSService.SetImmediateArchiveRule($"Test-Archive/Archive{(int)storageClass}/");
|
||||||
|
|
||||||
await _IOSSService.RestoreFilesByPrefixAsync($"Test-Archive/Archive{(int)storageClass}/");
|
//await _IOSSService.RestoreFilesByPrefixAsync($"Test-Archive/Archive{(int)storageClass}/");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,11 @@ public class SystemDocument : BaseFullDeleteAuditEntity
|
||||||
|
|
||||||
public bool IsPublish { get; set; } = true;
|
public bool IsPublish { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发布时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? PublishDate { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,11 @@ public class TrialDocument : BaseFullDeleteAuditEntity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
public bool IsPublish { get; set; } = true;
|
public bool IsPublish { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发布时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? PublishDate { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Comment("项目签署文档附件")]
|
[Comment("项目签署文档附件")]
|
||||||
|
|
|
||||||
|
|
@ -259,7 +259,7 @@ public partial class Trial : BaseFullDeleteAuditEntity
|
||||||
/// 图像格式
|
/// 图像格式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[StringLength(2000)]
|
[StringLength(2000)]
|
||||||
public List<string> ImageFormatList { get; set; } = new List<string>() {"jpg","jpeg","png","bmp","pdf","zip" };
|
public List<string> ImageFormatList { get; set; } = new List<string>() {"jpg","jpeg","png","bmp","pdf","zip","mp4" };
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 邮件配置
|
#region 邮件配置
|
||||||
|
|
|
||||||
|
|
@ -53,3 +53,6 @@
|
||||||
5、以下命令将生成一个从指定 from 迁移到指定 to 迁移的 SQL 脚本。
|
5、以下命令将生成一个从指定 from 迁移到指定 to 迁移的 SQL 脚本。
|
||||||
|
|
||||||
dotnet ef migrations script from to -p IRaCIS.Core.Infra.EFCore
|
dotnet ef migrations script from to -p IRaCIS.Core.Infra.EFCore
|
||||||
|
|
||||||
|
6、查看迁移列表
|
||||||
|
dotnet ef migrations list -p IRaCIS.Core.Infra.EFCore
|
||||||
|
|
@ -6,6 +6,7 @@ using IRaCIS.Core.Infrastructure.Extention;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
|
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Microsoft.VisualBasic;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
@ -60,8 +61,13 @@ public class IRaCISDBContext : DbContext
|
||||||
//针对字符串使用默认的长度配置为200,如果标注了StringLength 其他长度,就是标注的长度,如果标注了MaxLength 那么就是nvarcharMax
|
//针对字符串使用默认的长度配置为200,如果标注了StringLength 其他长度,就是标注的长度,如果标注了MaxLength 那么就是nvarcharMax
|
||||||
configurationBuilder.Conventions.Add(_ => new DefaultStringLengthConvention(400));
|
configurationBuilder.Conventions.Add(_ => new DefaultStringLengthConvention(400));
|
||||||
|
|
||||||
|
//configurationBuilder.Conventions.Add(_ => new RemoveForeignKeyConvention());
|
||||||
|
|
||||||
|
//控制外键索引生成与否
|
||||||
//https://learn.microsoft.com/zh-cn/ef/core/modeling/relationships/conventions?utm_source=chatgpt.com
|
//https://learn.microsoft.com/zh-cn/ef/core/modeling/relationships/conventions?utm_source=chatgpt.com
|
||||||
//configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
|
configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
|
@ -116,6 +122,7 @@ public class IRaCISDBContext : DbContext
|
||||||
|
|
||||||
|
|
||||||
#region decimal 自定义精度,适配多种数据库
|
#region decimal 自定义精度,适配多种数据库
|
||||||
|
|
||||||
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
||||||
{
|
{
|
||||||
foreach (var property in entityType.GetProperties())
|
foreach (var property in entityType.GetProperties())
|
||||||
|
|
@ -142,6 +149,12 @@ public class IRaCISDBContext : DbContext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//用户名区分大小写
|
||||||
|
modelBuilder.Entity<IdentityUser>(entity =>
|
||||||
|
{
|
||||||
|
entity.Property(e => e.UserName)
|
||||||
|
.UseCollation("Chinese_PRC_CS_AS");
|
||||||
|
});
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
//遍历实体模型手动配置
|
//遍历实体模型手动配置
|
||||||
|
|
@ -160,6 +173,26 @@ public class IRaCISDBContext : DbContext
|
||||||
|
|
||||||
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
||||||
{
|
{
|
||||||
|
#region 修改Id 聚集索引-> Id非聚集,CreateTime聚集索引,方便分区
|
||||||
|
|
||||||
|
// 1️ 所有 Entity:Id 为主键(非聚集)
|
||||||
|
if (typeof(Entity).IsAssignableFrom(entityType.ClrType))
|
||||||
|
{
|
||||||
|
modelBuilder.Entity(entityType.ClrType)
|
||||||
|
.HasKey(nameof(Entity.Id))
|
||||||
|
.IsClustered(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2️ 所有 IAuditAdd:CreateTime 为聚集索引
|
||||||
|
if (typeof(IAuditAdd).IsAssignableFrom(entityType.ClrType))
|
||||||
|
{
|
||||||
|
modelBuilder.Entity(entityType.ClrType)
|
||||||
|
.HasIndex(nameof(IAuditAdd.CreateTime))
|
||||||
|
.IsClustered();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
// 软删除配置
|
// 软删除配置
|
||||||
if (typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType))
|
if (typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
21464
IRaCIS.Core.Infra.EFCore/Migrations/20260110112346_clusterModify.Designer.cs
generated
Normal file
21464
IRaCIS.Core.Infra.EFCore/Migrations/20260110112346_clusterModify.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
21470
IRaCIS.Core.Infra.EFCore/Migrations/20260116084058_PublishDate.Designer.cs
generated
Normal file
21470
IRaCIS.Core.Infra.EFCore/Migrations/20260116084058_PublishDate.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,39 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Infra.EFCore.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class PublishDate : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "PublishDate",
|
||||||
|
table: "TrialDocument",
|
||||||
|
type: "datetime2",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "PublishDate",
|
||||||
|
table: "SystemDocument",
|
||||||
|
type: "datetime2",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "PublishDate",
|
||||||
|
table: "TrialDocument");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "PublishDate",
|
||||||
|
table: "SystemDocument");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -119,7 +119,10 @@ namespace IRaCIS.Core.Infra.EFCore
|
||||||
/// <summary>EntityState.Detached的实体 修改 部分字段</summary>
|
/// <summary>EntityState.Detached的实体 修改 部分字段</summary>
|
||||||
public static void EntityModifyPartialFiled<T>(this IRaCISDBContext _dbContext, T waitModifyEntity, Expression<Func<T, T>> updateFactory) where T : Entity
|
public static void EntityModifyPartialFiled<T>(this IRaCISDBContext _dbContext, T waitModifyEntity, Expression<Func<T, T>> updateFactory) where T : Entity
|
||||||
{
|
{
|
||||||
var entityEntry = _dbContext.Entry(waitModifyEntity);
|
//解决重复跟踪问题
|
||||||
|
var tracked = _dbContext.ChangeTracker.Entries<T>().FirstOrDefault(e => e.Entity.Id.Equals(waitModifyEntity.Id));
|
||||||
|
var entityEntry = tracked?? _dbContext.Entry(waitModifyEntity);
|
||||||
|
|
||||||
//entityEntry.State = EntityState.Detached;
|
//entityEntry.State = EntityState.Detached;
|
||||||
|
|
||||||
var list = ((MemberInitExpression)updateFactory.Body).Bindings.Select(mb => mb.Member.Name)
|
var list = ((MemberInitExpression)updateFactory.Body).Bindings.Select(mb => mb.Member.Name)
|
||||||
|
|
@ -134,7 +137,7 @@ namespace IRaCIS.Core.Infra.EFCore
|
||||||
|
|
||||||
foreach (PropertyInfo prop in list)
|
foreach (PropertyInfo prop in list)
|
||||||
{
|
{
|
||||||
_dbContext.Entry(waitModifyEntity).Property(prop.Name).IsModified = true;
|
entityEntry.Property(prop.Name).IsModified = true;
|
||||||
|
|
||||||
object value = prop.GetValue(applyObj);
|
object value = prop.GetValue(applyObj);
|
||||||
prop.SetValue(waitModifyEntity, value);
|
prop.SetValue(waitModifyEntity, value);
|
||||||
|
|
|
||||||
|
|
@ -48,29 +48,6 @@ steps:
|
||||||
commands:
|
commands:
|
||||||
- cd /opt/1panel/xc-deploy/Test_IRC/devops-build-publish;sh test-irc-update-or-create-stack.sh v${DRONE_BUILD_NUMBER}
|
- cd /opt/1panel/xc-deploy/Test_IRC/devops-build-publish;sh test-irc-update-or-create-stack.sh v${DRONE_BUILD_NUMBER}
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
- Test_IRC_Net8
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: ssh
|
|
||||||
name: ssh-linux-test-irc-scp-publish
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: Linux
|
|
||||||
arch: 386
|
|
||||||
|
|
||||||
clone:
|
|
||||||
disable: true
|
|
||||||
|
|
||||||
server:
|
|
||||||
host: 106.14.89.110
|
|
||||||
user: root
|
|
||||||
password:
|
|
||||||
from_secret: test_ssh_pwd
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: publish-test-irc-scp
|
- name: publish-test-irc-scp
|
||||||
commands:
|
commands:
|
||||||
- cd /opt/1panel/xc-deploy/Test_IRC_SCP/devops-build-publish;sh pull-build-test-irc-scp-image.sh v${DRONE_BUILD_NUMBER}
|
- cd /opt/1panel/xc-deploy/Test_IRC_SCP/devops-build-publish;sh pull-build-test-irc-scp-image.sh v${DRONE_BUILD_NUMBER}
|
||||||
|
|
@ -78,7 +55,9 @@ steps:
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
branch:
|
branch:
|
||||||
- Test_IRC_Net8—_SCP_Disable
|
- Test_IRC_Net8
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
|
|
@ -111,37 +90,7 @@ trigger:
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: ssh
|
type: ssh
|
||||||
name: ssh-linux-test-scp-publish
|
name: ssh-linux-test-hir
|
||||||
|
|
||||||
platform:
|
|
||||||
os: Linux
|
|
||||||
arch: 386
|
|
||||||
|
|
||||||
clone:
|
|
||||||
disable: true #禁用默认克隆
|
|
||||||
|
|
||||||
server:
|
|
||||||
host: 106.14.89.110
|
|
||||||
user: root
|
|
||||||
password:
|
|
||||||
from_secret: test_ssh_pwd
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: publish-test-hir-scp
|
|
||||||
commands:
|
|
||||||
- cd /opt/1panel/xc-deploy/Test_HIR_SCP/devops-build-publish;sh pull-build-test-hir-scp-image.sh v${DRONE_BUILD_NUMBER}
|
|
||||||
- cd /opt/1panel/xc-deploy/Test_HIR_SCP ;sh update-image-if-need-then-pull-aliyun.sh v${DRONE_BUILD_NUMBER}
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
- Test_HIR
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
kind: pipeline
|
|
||||||
type: ssh
|
|
||||||
name: ssh-linux-test-hir-publish
|
|
||||||
|
|
||||||
platform:
|
platform:
|
||||||
os: Linux
|
os: Linux
|
||||||
|
|
@ -161,8 +110,16 @@ steps:
|
||||||
commands:
|
commands:
|
||||||
- cd /opt/1panel/xc-deploy/Test_HIR/devops-build-publish;sh pull-build-test-hir-image.sh v${DRONE_BUILD_NUMBER}
|
- cd /opt/1panel/xc-deploy/Test_HIR/devops-build-publish;sh pull-build-test-hir-image.sh v${DRONE_BUILD_NUMBER}
|
||||||
- cd /opt/1panel/xc-deploy/Test_HIR ; sh update-image-if-need-then-pull-aliyun.sh v${DRONE_BUILD_NUMBER}
|
- cd /opt/1panel/xc-deploy/Test_HIR ; sh update-image-if-need-then-pull-aliyun.sh v${DRONE_BUILD_NUMBER}
|
||||||
|
|
||||||
|
- name: publish-test-hir-scp
|
||||||
|
commands:
|
||||||
|
- cd /opt/1panel/xc-deploy/Test_HIR_SCP/devops-build-publish;sh pull-build-test-hir-scp-image.sh v${DRONE_BUILD_NUMBER}
|
||||||
|
- cd /opt/1panel/xc-deploy/Test_HIR_SCP ;sh update-image-if-need-then-pull-aliyun.sh v${DRONE_BUILD_NUMBER}
|
||||||
trigger:
|
trigger:
|
||||||
branch:
|
branch:
|
||||||
- Test_HIR
|
- Test_HIR
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue