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()
|
||||
//.MinimumLevel.Information()
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
||||
.MinimumLevel.Override("ZiggyCreatures.Caching.Fusion", LogEventLevel.Warning)
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day)
|
||||
.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.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
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;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service
|
||||
{
|
||||
|
|
@ -120,7 +121,7 @@ namespace IRaCIS.Core.SCP.Service
|
|||
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)
|
||||
{
|
||||
|
|
@ -129,7 +130,7 @@ namespace IRaCIS.Core.SCP.Service
|
|||
isCanReceiveIamge = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (association.CallingAE == "test-callingAE")
|
||||
|
|
@ -328,6 +329,11 @@ namespace IRaCIS.Core.SCP.Service
|
|||
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||
|
||||
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 ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}/{studyInstanceUid}";
|
||||
|
|
@ -336,337 +342,383 @@ namespace IRaCIS.Core.SCP.Service
|
|||
long fileSize = 0;
|
||||
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
|
||||
ms.Position = 0;
|
||||
var dicomFile = DicomFile.Open(ms);
|
||||
dataset.AddOrUpdate(dicomTag, item.ReplaceValue);
|
||||
}
|
||||
|
||||
var numberOfFrames = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1);
|
||||
foreach (var item in ircFiledList)
|
||||
{
|
||||
|
||||
//多帧处理逻辑
|
||||
if (numberOfFrames > 1)
|
||||
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
|
||||
|
||||
if (dicomTag == DicomTag.ClinicalTrialProtocolID)
|
||||
{
|
||||
//一定要有像素数据才处理
|
||||
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
|
||||
dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolID, trialSiteInfo.TrialCode);
|
||||
|
||||
if (pixelData != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
}
|
||||
if (dicomTag == DicomTag.ClinicalTrialSiteID)
|
||||
{
|
||||
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
|
||||
|
||||
|
||||
#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())
|
||||
if (pixelData != null)
|
||||
{
|
||||
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);
|
||||
|
||||
//没有缩略图
|
||||
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
||||
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)
|
||||
{
|
||||
|
||||
// 生成缩略图
|
||||
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();
|
||||
sharpimage.Save(memoryStream, new JpegEncoder());
|
||||
bot.Add(botOffset);
|
||||
|
||||
// 上传缩略图到 OSS
|
||||
|
||||
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, $"{seriesId.ToString()}_{instanceId.ToString()}.preview.jpg", false);
|
||||
botOffset += (uint)data.Length;
|
||||
|
||||
|
||||
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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
await _seriesRepository.SaveChangesAsync();
|
||||
|
||||
if (_ImageUploadList.Any(t => t.StudyInstanceUid == studyInstanceUid))
|
||||
//传递过来的就是拆分的,但是是没有偏移表的,我需要自己创建偏移表,不然生成缩略图失败
|
||||
else if (syntax.IsEncapsulated && fragmentCount > pixelData.NumberOfFrames && originOffsetTable.Count == 0)
|
||||
{
|
||||
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)
|
||||
if (find.SCPStudyId != scpStudyId)
|
||||
{
|
||||
find.SCPStudyId = scpStudyId;
|
||||
bot.RemoveAt(bot.Count - 1);
|
||||
// 设置到新的 PixelData
|
||||
frag.OffsetTable.AddRange(bot.ToArray());
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//监控信息设置
|
||||
_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()}");
|
||||
|
||||
if (_ImageUploadList.Any(t => t.StudyInstanceUid == studyInstanceUid))
|
||||
// 生成缩略图
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
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} 上传完成 ");
|
||||
|
|
|
|||
|
|
@ -22,11 +22,7 @@ namespace IRaCIS.Core.SCP.Service
|
|||
public class DicomArchiveService(IRepository<SCPPatient> _patientRepository,
|
||||
IRepository<SCPStudy> _studyRepository,
|
||||
IRepository<SCPSeries> _seriesRepository,
|
||||
IRepository<SCPInstance> _instanceRepository,
|
||||
IDistributedLockProvider _distributedLockProvider,
|
||||
IRepository<SystemAnonymization> _systemAnonymizationRepository,
|
||||
IRepository<TrialSite> _trialSiteRepository,
|
||||
IFusionCache _fusionCache
|
||||
IRepository<SCPInstance> _instanceRepository
|
||||
) : BaseService, IDicomArchiveService
|
||||
{
|
||||
|
||||
|
|
@ -45,66 +41,6 @@ namespace IRaCIS.Core.SCP.Service
|
|||
|
||||
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 seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
|
||||
|
|
@ -293,6 +229,7 @@ namespace IRaCIS.Core.SCP.Service
|
|||
findStudy.DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty);
|
||||
findStudy.CalledAE = calledAE;
|
||||
findStudy.CallingAE = callingAE;
|
||||
findStudy.PatientIdStr = patientIdStr;
|
||||
findStudy.PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
|
||||
findStudy.PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty);
|
||||
findStudy.PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty);
|
||||
|
|
|
|||
|
|
@ -96,6 +96,12 @@ builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resourc
|
|||
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
||||
builder.Services.AddControllers(options =>
|
||||
{
|
||||
// 关键配置:禁用不可空引用类型的自动 Required 验证
|
||||
options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true;
|
||||
|
||||
// 插到最前,抢在默认绑定器之前
|
||||
//options.ModelBinderProviders.Insert(0, new SpecificNullableBinderProvider());
|
||||
|
||||
options.Filters.Add<ModelActionFilter>();
|
||||
options.Filters.Add<ProjectExceptionFilter>();
|
||||
options.Filters.Add<UnitOfWorkFilter>();
|
||||
|
|
|
|||
|
|
@ -71,29 +71,27 @@ namespace IRaCIS.Core.API
|
|||
|
||||
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?;
|
||||
|
||||
|
||||
if (nullableDateTime != null && nullableDateTime.HasValue)
|
||||
{
|
||||
dateTime = nullableDateTime.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
throw new JsonSerializationException($"Could not convert string to DateTime: {reader.Value} Path {reader.Path}");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DateTime.TryParse((string)reader.Value, out dateTime) == false)
|
||||
if (canConvert == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////如果前端传递带时区的,那么转换会报错,需要DateTimeKind.Unspecified
|
||||
//dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
|
||||
|
||||
// 将客户端时间转换为服务器时区的时间
|
||||
var serverZoneTime = TimeZoneInfo.ConvertTime(dateTime, _clientTimeZone, TimeZoneInfo.Local);
|
||||
|
|
@ -113,7 +111,10 @@ namespace IRaCIS.Core.API
|
|||
//第一个参数默认使用系统本地时区 也就是应用服务器的时区
|
||||
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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ using Newtonsoft.Json;
|
|||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
|
|
@ -51,34 +53,90 @@ namespace IRaCIS.Core.API
|
|||
}
|
||||
public class NullToEmptyStringValueProvider : IValueProvider
|
||||
{
|
||||
PropertyInfo _MemberInfo;
|
||||
PropertyInfo _memberInfo;
|
||||
private readonly bool _isNullable;
|
||||
public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
|
||||
{
|
||||
_MemberInfo = memberInfo;
|
||||
_memberInfo = memberInfo;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// DTO → JSON 返回前端的时候 处理null 变为"" 方便前端判断
|
||||
public object GetValue(object target)
|
||||
{
|
||||
object result = _MemberInfo.GetValue(target);
|
||||
if (_MemberInfo.PropertyType == typeof(string) && result == null) result = "";
|
||||
else if (_MemberInfo.PropertyType == typeof(String[]) && result == null) result = new string[] { };
|
||||
//else if (_MemberInfo.PropertyType == typeof(Nullable<Int32>) && result == null) result = 0;
|
||||
//else if (_MemberInfo.PropertyType == typeof(Nullable<Decimal>) && result == null) result = 0.00M;
|
||||
var result = _memberInfo.GetValue(target);
|
||||
// 检查类型是否为string或string?
|
||||
if (_memberInfo.PropertyType == typeof(string) && result == null)
|
||||
{
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
//影响 模型绑定时接收前端的值,同时影响 正常 JSON 序列化
|
||||
public void SetValue(object target, object value)
|
||||
{
|
||||
|
||||
if (_MemberInfo.PropertyType == typeof(string))
|
||||
{
|
||||
//去掉前后空格
|
||||
_MemberInfo.SetValue(target, value == null ? string.Empty : value.ToString() == string.Empty ? value : value/*.ToString().Trim()*/);
|
||||
_memberInfo.SetValue(target, value);
|
||||
|
||||
#region 前端针对 string 类型的变量,如果传递null 会报错必传
|
||||
|
||||
//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
|
||||
"TemplateType": 2,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyDays": 1
|
||||
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyDays": 1
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@
|
|||
"TemplateType": 1,
|
||||
|
||||
"OpenTrialRelationDelete": false,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyDays": 1
|
||||
//MFA免验证
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,8 @@
|
|||
"TemplateType": 1,
|
||||
"OpenLoginMFA": true,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyDays": 1
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@
|
|||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 1,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyDays": 1
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyDays": 1
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
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 Newtonsoft.Json;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
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 UserMFATag(Guid userId) => $"UserMFAVerifyPass:{userId}";
|
||||
|
||||
}
|
||||
|
||||
public static class CacheHelper
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using FellowOakDicom;
|
||||
using DocumentFormat.OpenXml.Office.CustomUI;
|
||||
using FellowOakDicom;
|
||||
using FellowOakDicom.Media;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
|
@ -57,6 +58,7 @@ namespace IRaCIS.Core.Application.Helper
|
|||
var mappings = new List<string>();
|
||||
int index = 1;
|
||||
|
||||
var studyUid=list.FirstOrDefault()?.StudyInstanceUid;
|
||||
|
||||
var dicomDir = new DicomDirectory();
|
||||
|
||||
|
|
@ -128,7 +130,9 @@ namespace IRaCIS.Core.Application.Helper
|
|||
// 重置流位置
|
||||
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.UserModel;
|
||||
using NPOI.XSSF.UserModel;
|
||||
using SharpCompress.Common;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using Xceed.Document.NET;
|
||||
|
|
@ -870,17 +871,16 @@ public static class ExcelExportHelper
|
|||
//模板路径
|
||||
var tplPath = physicalPath;
|
||||
|
||||
#region 根据中英文 删除模板sheet
|
||||
|
||||
// 打开模板文件
|
||||
var templateFile = new FileStream(tplPath, FileMode.Open, FileAccess.Read);
|
||||
|
||||
// 获取文件流
|
||||
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;
|
||||
|
||||
|
|
@ -985,14 +985,8 @@ public static class ExcelExportHelper
|
|||
}
|
||||
}
|
||||
|
||||
using (var memoryStream2 = new MemoryStream())
|
||||
{
|
||||
workbook.Write(memoryStream2, true);
|
||||
|
||||
memoryStream2.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
templateStream = memoryStream2;
|
||||
}
|
||||
workbook.Write(templateStream, leaveOpen: true);
|
||||
templateStream.Position = 0;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1099,6 +1093,63 @@ public static class ExcelExportHelper
|
|||
//模板路径
|
||||
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();
|
||||
|
||||
|
|
@ -1107,7 +1158,7 @@ public static class ExcelExportHelper
|
|||
IgnoreTemplateParameterMissing = true,
|
||||
};
|
||||
|
||||
await MiniExcel.SaveAsByTemplateAsync(memoryStream, tplPath, data, config);
|
||||
await MiniExcel.SaveAsByTemplateAsync(memoryStream, templateStream.ToArray(), data, config);
|
||||
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ public interface IOSSService
|
|||
|
||||
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();
|
||||
|
||||
|
|
@ -249,7 +249,7 @@ public class OSSService : IOSSService
|
|||
|
||||
LifeCycleExpiration =
|
||||
{
|
||||
Days = 30
|
||||
Days = 30 //最后一次修改时间
|
||||
},
|
||||
StorageClass = StorageClass.Archive
|
||||
}
|
||||
|
|
@ -302,8 +302,8 @@ public class OSSService : IOSSService
|
|||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
|
@ -333,15 +333,15 @@ public class OSSService : IOSSService
|
|||
Transitions = new List<LifecycleTransition>
|
||||
{
|
||||
// 1天后转为低频访问 (Standard-IA)
|
||||
new LifecycleTransition
|
||||
{
|
||||
Days = 1,
|
||||
StorageClass = S3StorageClass.StandardInfrequentAccess // 对应S3 Standard-IA
|
||||
},
|
||||
//new LifecycleTransition
|
||||
//{
|
||||
// Days = 1, //Days' in Transition action must be greater than or equal to 30 for storageClass 'STANDARD_IA'"
|
||||
// StorageClass = S3StorageClass.StandardInfrequentAccess // 对应S3 Standard-IA
|
||||
//},
|
||||
// 30天后转为归档 (Glacier Instant Retrieval)
|
||||
new LifecycleTransition
|
||||
{
|
||||
Days = 30,
|
||||
Days = 30, //创建时间
|
||||
StorageClass = S3StorageClass.GlacierInstantRetrieval // 对应归档(即时检索)
|
||||
}
|
||||
// 如果需要更深的归档,可以继续添加:
|
||||
|
|
@ -765,8 +765,8 @@ public class OSSService : IOSSService
|
|||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
|
@ -891,8 +891,8 @@ public class OSSService : IOSSService
|
|||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
|
@ -966,8 +966,8 @@ public class OSSService : IOSSService
|
|||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
|
@ -1172,8 +1172,8 @@ public class OSSService : IOSSService
|
|||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
|
@ -1546,8 +1546,8 @@ public class OSSService : IOSSService
|
|||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
|
@ -1561,7 +1561,7 @@ public class OSSService : IOSSService
|
|||
|
||||
var listObjectsResponse = await amazonS3Client.ListObjectsV2Async(listObjectsRequest);
|
||||
|
||||
if (listObjectsResponse.S3Objects.Count > 0)
|
||||
if (listObjectsResponse.S3Objects?.Count > 0)
|
||||
{
|
||||
// 准备删除请求
|
||||
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();
|
||||
|
||||
|
|
@ -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 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)
|
||||
{
|
||||
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(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
|
@ -1714,8 +1728,8 @@ public class OSSService : IOSSService
|
|||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var request = new Amazon.S3.Model.GetObjectMetadataRequest
|
||||
|
|
@ -1799,9 +1813,16 @@ public class OSSService : IOSSService
|
|||
{
|
||||
var awsOptions = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
|
||||
// 创建 STS 客户端(考虑使用 RegionEndpoint)
|
||||
var stsConfig = new AmazonSecurityTokenServiceConfig
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsOptions.Region)
|
||||
};
|
||||
|
||||
//aws 临时凭证
|
||||
// 创建 STS 客户端
|
||||
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
|
||||
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey, stsConfig);
|
||||
|
||||
// 使用 AssumeRole 请求临时凭证
|
||||
var assumeRoleRequest = new AssumeRoleRequest
|
||||
|
|
|
|||
|
|
@ -19195,6 +19195,13 @@
|
|||
<param name="tpCode"></param>
|
||||
<param name="key"></param>
|
||||
</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})">
|
||||
<summary> 指定资源Id,获取Dicom检查所属序列信息列表 </summary>
|
||||
<param name="studyId"> Dicom检查的Id </param>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ using MassTransit;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
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)
|
||||
.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(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);
|
||||
|
||||
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)
|
||||
{
|
||||
if (item.SplitRowId!=null&&lesionRelationship.ContainsKey(item.SplitRowId.Value))
|
||||
if (item.SplitRowId != null && lesionRelationship.ContainsKey(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];
|
||||
}
|
||||
|
|
@ -2402,6 +2403,10 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
|||
//另一个阅片人的任务根据任务进度自动进入PM退回或PM申请重阅
|
||||
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();
|
||||
|
||||
|
|
@ -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);
|
||||
|
||||
otherVisitIdList = otherVisitIdList.Where(t => t != task.SourceSubjectVisitId.Value).ToList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//BM后续访视 ,筛选状态不变,任务生成状态重置(实际该访视任务状态 可能是重阅重置了或者失效了,需要后续生成,或者取消分配了,需要后续重新分配)
|
||||
await _subjectCriteriaEvaluationVisitFilterRepository.UpdatePartialFromQueryAsync(t => t.TrialReadingCriterion.CriterionType == CriterionType.RECIST1Pointt1_MB && t.SubjectVisit.SubjectId == task.SubjectId && otherVisitIdList.Contains(t.SubjectVisitId),
|
||||
t => new SubjectCriteriaEvaluationVisitFilter()
|
||||
|
|
@ -2723,6 +2731,13 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
|||
//默认影响的都是该标准的任务
|
||||
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);
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -92,6 +92,10 @@ namespace IRaCIS.Core.Application.Contracts
|
|||
|
||||
public class UnionDocumentWithConfirmInfoView : UnionDocumentView
|
||||
{
|
||||
public DocUserSignType SysDocUserSignType { get; set; }
|
||||
|
||||
public bool IsConfirmIdentityUserInner { get; set; }
|
||||
|
||||
public Guid TrialId { 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 List<CriterionType>? SysCriterionTypeList { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -127,7 +128,7 @@ namespace IRaCIS.Core.Application.ViewModel
|
|||
{
|
||||
public Guid SubjectId { get; set; }
|
||||
|
||||
public Guid TrialReadingCriterionId { get; set; }
|
||||
public CriterionType CriterionType { get; set; }
|
||||
|
||||
public EmailBusinessScenario BusinessScenarioEnum { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ namespace IRaCIS.Core.Application.Contracts
|
|||
Task<IResponseOutput> UserConfirm(UserConfirmCommand userConfirmCommand);
|
||||
Task<List<TrialUserDto>> GetTrialUserSelect(Guid trialId);
|
||||
|
||||
Task<IResponseOutput<PageOutput<UnionDocumentWithConfirmInfoView>>> GetSysDocumentConfirmList(SystemDocQuery inQuery);
|
||||
|
||||
|
||||
//Task<PageOutput<DocumentUnionWithUserStatView>> GetTrialSystemDocumentList(DocumentTrialUnionQuery querySystemDocument);
|
||||
//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()
|
||||
{
|
||||
IsPublish = true,
|
||||
PublishDate= DateTime.Now,
|
||||
IsDeleted = false,
|
||||
});
|
||||
|
||||
|
|
@ -301,6 +302,7 @@ namespace IRaCIS.Core.Application.Services
|
|||
{
|
||||
AttachmentCount=sysDoc.SystemDocumentAttachmentList.Where(z=>!z.OffLine).Count(),
|
||||
IsSystemDoc = true,
|
||||
IsPublish=sysDoc.IsPublish,
|
||||
CurrentStaffTrainDays=sysDoc.CurrentStaffTrainDays,
|
||||
NewStaffTrainDays = sysDoc.NewStaffTrainDays,
|
||||
Id = sysDoc.Id,
|
||||
|
|
@ -326,6 +328,7 @@ namespace IRaCIS.Core.Application.Services
|
|||
//UserTypeShortName = user.UserTypeRole.UserTypeShortName
|
||||
};
|
||||
|
||||
|
||||
var list = await query
|
||||
//过滤掉删除的,并且没有签名的
|
||||
.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()
|
||||
{
|
||||
IsPublish = true,
|
||||
PublishDate = DateTime.Now,
|
||||
IsDeleted = false,
|
||||
},false,true);
|
||||
}, false, true);
|
||||
await _trialDocumentRepository.SaveChangesAsync();
|
||||
Console.WriteLine("开始 发布项目文档");
|
||||
|
||||
|
|
@ -150,7 +151,7 @@ namespace IRaCIS.Core.Application.Services
|
|||
{
|
||||
// 从新作用域解析服务
|
||||
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);
|
||||
|
|
@ -171,7 +172,7 @@ namespace IRaCIS.Core.Application.Services
|
|||
.WhereIf(inQuery.UserTypeId != null, t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == inQuery.UserTypeId))
|
||||
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted)
|
||||
.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 });
|
||||
|
||||
return await trialDocumentQueryable.ToPagedListAsync(inQuery);
|
||||
|
|
@ -180,7 +181,7 @@ namespace IRaCIS.Core.Application.Services
|
|||
[HttpPost]
|
||||
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)
|
||||
.Where(t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId))
|
||||
|
||||
|
|
@ -353,7 +354,7 @@ namespace IRaCIS.Core.Application.Services
|
|||
#region 统一用户修改
|
||||
|
||||
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)
|
||||
from trialUser in _trialIdentityUserRepository.AsQueryable(false)
|
||||
|
|
@ -392,7 +393,7 @@ namespace IRaCIS.Core.Application.Services
|
|||
|
||||
//项目文档查询
|
||||
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
|
||||
&& t.TrialUserRoleList.Any(t => trialDoc.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == t.UserRole.UserTypeId)))
|
||||
|
||||
|
|
@ -582,7 +583,7 @@ namespace IRaCIS.Core.Application.Services
|
|||
#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.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))
|
||||
|
|
@ -713,7 +714,8 @@ namespace IRaCIS.Core.Application.Services
|
|||
.WhereIf(inQuery.BeginCreateTime != null, t => t.CreateTime >= inQuery.BeginCreateTime)
|
||||
.WhereIf(inQuery.EndCreateTime != null, t => t.CreateTime <= inQuery.EndCreateTime)
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.UserName), t => t.UserName.Contains(inQuery.UserName))
|
||||
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted);
|
||||
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted)
|
||||
.WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.EA, t => t.ConfirmTime != null);
|
||||
|
||||
var result = await unionQuery.ToPagedListAsync(inQuery);
|
||||
|
||||
|
|
@ -945,15 +947,18 @@ namespace IRaCIS.Core.Application.Services
|
|||
var isEA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.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>());
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
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.UserTypeId != null ? t.UserRoleList.Any(t => t.UserTypeId == inQuery.UserTypeId && t.IsUserRoleDisabled == false) : true)
|
||||
.Where(t => isEA ? t.IsZhiZhun == true : true) //EA 只能查看内部人员文档
|
||||
|
|
@ -962,6 +967,9 @@ namespace IRaCIS.Core.Application.Services
|
|||
select new UnionDocumentWithConfirmInfoView()
|
||||
{
|
||||
IsSystemDoc = true,
|
||||
SysDocUserSignType = sysDoc.DocUserSignType,
|
||||
IsConfirmIdentityUserInner = identityUser.IsZhiZhun,
|
||||
IsPublish=sysDoc.IsPublish,
|
||||
Id = sysDoc.Id,
|
||||
CreateTime = sysDoc.CreateTime,
|
||||
IsDeleted = sysDoc.IsDeleted,
|
||||
|
|
@ -993,14 +1001,16 @@ namespace IRaCIS.Core.Application.Services
|
|||
};
|
||||
|
||||
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(inQuery.FileTypeId != null, t => t.FileTypeId == inQuery.FileTypeId)
|
||||
.WhereIf(inQuery.IsConfirmed == true, t => t.ConfirmTime != null)
|
||||
.WhereIf(inQuery.IsConfirmed == false, t => t.ConfirmTime == null)
|
||||
.WhereIf(inQuery.StartConfirmTime != null, t => t.ConfirmTime >= inQuery.StartConfirmTime.Value)
|
||||
.WhereIf(inQuery.EndConfirmTime != null, t => t.ConfirmTime <= inQuery.EndConfirmTime.Value)
|
||||
.WhereIf(inQuery.BeginCreateTime != null, t => t.CreateTime >= inQuery.BeginCreateTime)
|
||||
.WhereIf(inQuery.EndCreateTime != null, t => t.CreateTime <= inQuery.EndCreateTime)
|
||||
.WhereIf(inQuery.BeginCreateTime != null, t => t.CreateTime >= inQuery.BeginCreateTime)
|
||||
.WhereIf(inQuery.EndCreateTime != null, t => t.CreateTime <= inQuery.EndCreateTime)
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.UserName), t => t.UserName.Contains(inQuery.UserName))
|
||||
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted)
|
||||
.WhereIf(isInternal == false, t => t.ConfirmTime != null); //不是内部的人,看有签名时间的
|
||||
|
|
@ -1113,7 +1123,7 @@ namespace IRaCIS.Core.Application.Services
|
|||
|
||||
_mapper.Map(addOrEditTrialDocument, document);
|
||||
document.UpdateTime = DateTime.Now;
|
||||
|
||||
|
||||
#region 不区分路径了
|
||||
|
||||
//if (document.FileTypeId != addOrEditTrialDocument.FileTypeId)
|
||||
|
|
|
|||
|
|
@ -773,10 +773,12 @@ namespace IRaCIS.Core.Application.Service
|
|||
{
|
||||
var subjectId = generateEmailCommand.SubjectId;
|
||||
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 进展 已生成任务的 访视
|
||||
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);
|
||||
|
||||
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)
|
||||
.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 == true, t => t.IsDistinguishCriteria == true)
|
||||
.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 list = await orderQuery.ToListAsync();
|
||||
|
||||
|
||||
return ResponseOutput.Ok(list, trialConfig);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,4 +117,10 @@ namespace IRaCIS.Core.Application.Contracts.Dicom.DTO
|
|||
public bool HasLabel { 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 BodyPartForEditOther { get; set; } = string.Empty;
|
||||
|
||||
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 }))
|
||||
{
|
||||
|
||||
var studyUid = 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));
|
||||
|
|
@ -1192,7 +1195,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
|||
|
||||
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 studyUid = item.Key.StudyInstanceUid;
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
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 studyUid = 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)
|
||||
{
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -382,7 +382,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
|||
|
||||
Id = t.Id,
|
||||
|
||||
Bodypart = t.BodyPartExamined,
|
||||
Bodypart = t.BodyPartForEdit,
|
||||
BodyPartForEditOther = t.BodyPartForEditOther,
|
||||
|
||||
Modalities = t.Modalities,
|
||||
|
||||
|
|
@ -433,6 +434,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
|||
Id = t.Id,
|
||||
|
||||
Bodypart = t.BodyPart,
|
||||
BodyPartForEditOther=t.BodyPartForEditOther,
|
||||
|
||||
Modalities = t.Modality,
|
||||
|
||||
|
|
|
|||
|
|
@ -308,6 +308,8 @@ namespace IRaCIS.Core.Application.Service
|
|||
|
||||
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);
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
|
|
@ -316,7 +318,7 @@ namespace IRaCIS.Core.Application.Service
|
|||
|
||||
|
||||
/// <summary>
|
||||
/// 重置密码发邮件 (未登陆修改)
|
||||
/// 重置密码发邮件 (未登陆修改-忘记密码)
|
||||
/// </summary>
|
||||
/// <param name="email"></param>
|
||||
/// <returns></returns>
|
||||
|
|
@ -357,6 +359,8 @@ namespace IRaCIS.Core.Application.Service
|
|||
|
||||
await _mailVerificationService.AnolymousSendEmailForResetAccount(email, verificationCode);
|
||||
|
||||
await _fusionCache.RemoveByTagAsync(CacheKeys.UserMFATag(existUser.Id));
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
|
||||
}
|
||||
|
|
@ -438,6 +442,10 @@ namespace IRaCIS.Core.Application.Service
|
|||
|
||||
await _mailVerificationService.AfterUserModifyPasswordSendEmailAsync(identityUserId);
|
||||
|
||||
|
||||
var find = await _identityUserRepository.FindAsync(identityUserId);
|
||||
await _fusionCache.RemoveAsync(CacheKeys.UserLoginError(find.UserName));
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
|
||||
}
|
||||
|
|
@ -485,6 +493,8 @@ namespace IRaCIS.Core.Application.Service
|
|||
|
||||
await _mailVerificationService.AfterUserModifyPasswordSendEmailAsync(_userInfo.IdentityUserId);
|
||||
|
||||
await _fusionCache.RemoveByTagAsync(CacheKeys.UserMFATag(_userInfo.IdentityUserId));
|
||||
|
||||
return ResponseOutput.Result(success);
|
||||
|
||||
|
||||
|
|
@ -861,8 +871,9 @@ namespace IRaCIS.Core.Application.Service
|
|||
|
||||
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;
|
||||
|
||||
|
||||
await _fusionCache.RemoveByTagAsync(CacheKeys.UserMFATag(loginUser.IdentityUserId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1176,15 +1187,20 @@ namespace IRaCIS.Core.Application.Service
|
|||
if (_verifyConfig.CurrentValue.OpenLoginMFA)
|
||||
{
|
||||
|
||||
|
||||
if ((await _fusionCache.GetOrDefaultAsync(CacheKeys.UserMFAVerifyPass(identityUserId, _userInfo.BrowserFingerprint), "")) == _userInfo.BrowserFingerprint)
|
||||
{
|
||||
userLoginReturnModel.IsMFA = false;
|
||||
|
||||
Log.Logger.Warning($"MFA登录:{userName} 浏览器标识: {_userInfo.BrowserFingerprint},判断缓存里存在 ");
|
||||
}
|
||||
else
|
||||
{
|
||||
//MFA 发送邮件
|
||||
|
||||
userLoginReturnModel.IsMFA = true;
|
||||
|
||||
Log.Logger.Warning($"MFA登录:{userName} 浏览器标识: {_userInfo.BrowserFingerprint} 判断缓存已经不存在");
|
||||
}
|
||||
|
||||
var email = userLoginReturnModel.BasicInfo.EMail;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ namespace IRaCIS.Core.Application.Contracts
|
|||
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
public bool IsBeMark { get; set; }
|
||||
|
||||
public DateTime? MarkTime { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -983,6 +983,8 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
|||
/// </summary>
|
||||
public ImageMark? ImageMarkEnum { get; set; }
|
||||
|
||||
public string QuestionGroupName { get; set; }
|
||||
|
||||
/// <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();
|
||||
|
||||
|
||||
// 获取非DIOCM标记
|
||||
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)
|
||||
{
|
||||
var studyId = nonoDicomStudyFileIds.Select(x => x.StudyId).FirstOrDefault();
|
||||
|
||||
var noneDicomids = nonoDicomStudyFileIds.Select(x => x.NoneDicomFileId).ToList();
|
||||
|
||||
var noneDicomStudyViewMark = new NoneDicomStudyView() { Id = Guid.NewGuid() };
|
||||
noneDicomStudyViewMark.IsCriticalSequence = true;
|
||||
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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -374,7 +374,7 @@ namespace IRaCIS.Core.Application.Service
|
|||
CreateMap<AddOrUpdateReadingQuestionTrialInDto, ReadingQuestionTrial>()
|
||||
.ForMember(dest => dest.CreateUserRole, opt => opt.Ignore());
|
||||
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.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))
|
||||
|
|
|
|||
|
|
@ -338,6 +338,7 @@ namespace IRaCIS.Core.Application.Service
|
|||
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.EmailFromName = $"{_systemEmailConfig.FromName}-{trial.TrialCode}";
|
||||
trial.UpdateTime = DateTime.Now;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using IRaCIS.Core.Domain;
|
|||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infra.EFCore.Context;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Encryption;
|
||||
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
|
||||
|
|
@ -38,6 +39,7 @@ using SixLabors.ImageSharp;
|
|||
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
|
@ -75,14 +77,85 @@ namespace IRaCIS.Core.Application.Service
|
|||
{
|
||||
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]
|
||||
public async Task<IResponseOutput> TestOSS(StorageClass storageClass)
|
||||
{
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 发布时间
|
||||
/// </summary>
|
||||
public DateTime? PublishDate { get; set; }
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,11 @@ public class TrialDocument : BaseFullDeleteAuditEntity
|
|||
/// </summary>
|
||||
|
||||
public bool IsPublish { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 发布时间
|
||||
/// </summary>
|
||||
public DateTime? PublishDate { get; set; }
|
||||
}
|
||||
|
||||
[Comment("项目签署文档附件")]
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ public partial class Trial : BaseFullDeleteAuditEntity
|
|||
/// 图像格式
|
||||
/// </summary>
|
||||
[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
|
||||
|
||||
#region 邮件配置
|
||||
|
|
|
|||
|
|
@ -52,4 +52,7 @@
|
|||
|
||||
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.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Microsoft.VisualBasic;
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
|
@ -60,8 +61,13 @@ public class IRaCISDBContext : DbContext
|
|||
//针对字符串使用默认的长度配置为200,如果标注了StringLength 其他长度,就是标注的长度,如果标注了MaxLength 那么就是nvarcharMax
|
||||
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
|
||||
//configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
|
||||
configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
|
|
@ -116,6 +122,7 @@ public class IRaCISDBContext : DbContext
|
|||
|
||||
|
||||
#region decimal 自定义精度,适配多种数据库
|
||||
|
||||
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
||||
{
|
||||
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
|
||||
|
||||
//遍历实体模型手动配置
|
||||
|
|
@ -160,6 +173,26 @@ public class IRaCISDBContext : DbContext
|
|||
|
||||
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))
|
||||
{
|
||||
|
|
@ -694,7 +727,7 @@ public class IRaCISDBContext : DbContext
|
|||
|
||||
public virtual DbSet<AuditDocumentClosure> AuditDocumentClosure { get; set; }
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class TestLength : Entity
|
||||
|
|
|
|||
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>
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
_dbContext.Entry(waitModifyEntity).Property(prop.Name).IsModified = true;
|
||||
entityEntry.Property(prop.Name).IsModified = true;
|
||||
|
||||
object value = prop.GetValue(applyObj);
|
||||
prop.SetValue(waitModifyEntity, value);
|
||||
|
|
|
|||
|
|
@ -48,29 +48,6 @@ steps:
|
|||
commands:
|
||||
- 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
|
||||
commands:
|
||||
- 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:
|
||||
branch:
|
||||
- Test_IRC_Net8—_SCP_Disable
|
||||
- Test_IRC_Net8
|
||||
|
||||
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
|
|
@ -111,37 +90,7 @@ trigger:
|
|||
---
|
||||
kind: pipeline
|
||||
type: ssh
|
||||
name: ssh-linux-test-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-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
|
||||
name: ssh-linux-test-hir
|
||||
|
||||
platform:
|
||||
os: Linux
|
||||
|
|
@ -161,8 +110,16 @@ steps:
|
|||
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 ; 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:
|
||||
branch:
|
||||
- Test_HIR
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue