Compare commits
382 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bbcab237d4 | |||
| 010367d018 | |||
| ea43698ee9 | |||
| 7155f6c985 | |||
| 3bdba35501 | |||
| ebcf3c95e2 | |||
| 75c3c9ee06 | |||
| 048193e4f3 | |||
| 7879504b71 | |||
| 93558164de | |||
| 83d123a31e | |||
| 733de4c942 | |||
| 0e7bd5f5dd | |||
| 0ef7ef675c | |||
| ee76366a1c | |||
| 369a0803f9 | |||
| 1f7b96d9f0 | |||
| 72ff2bbf15 | |||
| 0862182f8e | |||
| 746a8de799 | |||
| 37e8545eba | |||
| 570318cf3b | |||
| 7e13edc440 | |||
| 5579181f54 | |||
| 39b840f8fc | |||
| 02fb4c9753 | |||
| cf15883d52 | |||
| 84380a0d21 | |||
| 76993b4c8b | |||
| 0a4f00e63d | |||
| 9a52e30172 | |||
| 120d583a5a | |||
| dd8e244e64 | |||
| 5d7a083f4a | |||
| b7f22245e1 | |||
| 876deb102c | |||
| 49741e5c44 | |||
| 29ee2ba800 | |||
| 65e2bee680 | |||
| d45871a0db | |||
| ab3b9bddc8 | |||
| 7a5ad83cbb | |||
| 2d2e0f6f2f | |||
| 3b5a735abf | |||
| e00f8b3549 | |||
| dee8c90319 | |||
| 47fd242200 | |||
| 1181540302 | |||
| e6b3c662e2 | |||
| 3a06ecd66d | |||
| 7121799900 | |||
| c814736427 | |||
| 4ac9e83903 | |||
| 782270d462 | |||
| ad77b66bdd | |||
| 77538ab7c2 | |||
| 2e1e042658 | |||
| 6736fa96fd | |||
| a965ab9336 | |||
| 2ce112720a | |||
| f8b7e7d764 | |||
| 1aa1189ae8 | |||
| 9b90ded79f | |||
| 61ab33b3f3 | |||
| e6458e66df | |||
| 0ef7fd85d4 | |||
| 2bf47515e7 | |||
| 296ecfa9d2 | |||
| 473d10533a | |||
| 59ba1a6193 | |||
| cf2b3f5954 | |||
| 7fed9cdceb | |||
| 0585966215 | |||
| 7f9d6afc62 | |||
| 6dce301ff7 | |||
| 7a2d95a77c | |||
| 74a8ac7575 | |||
| e9c6d123d3 | |||
| 310c4a0f70 | |||
| 571f81f271 | |||
| c3a9ca3ca2 | |||
| 6874459046 | |||
| f4af6d3218 | |||
| ec87ed1caf | |||
| 0bb5d09a23 | |||
| f43ac9e649 | |||
| 9e3180b326 | |||
| 1011e704b9 | |||
| 0afcaa7d70 | |||
| 5fb889bac9 | |||
| febf8868f9 | |||
| 665d9a27fb | |||
| ccf16423d2 | |||
| a5155457b1 | |||
| e358601232 | |||
| 1514694e37 | |||
| fbd128138d | |||
| eca7871684 | |||
| 8eb31d0c81 | |||
| 051094a742 | |||
| 9012b9c35c | |||
| c49cd2e75e | |||
| f8b36df86d | |||
| 8e5b7fb57c | |||
| b5a76804ad | |||
| d4e79e0905 | |||
| 11e4c39dba | |||
| 02dd26188d | |||
| 4f439c6ee7 | |||
| bb36240c3c | |||
| e17b26b41d | |||
| 3fce7b772e | |||
| 8ca740bae3 | |||
| ead07cb1ca | |||
| 951081edb7 | |||
| 7ceca5d802 | |||
| 86a3f1d2a5 | |||
| b0a349b5ff | |||
| 7523f646fe | |||
| fcd0dd9b08 | |||
| 208cb40525 | |||
| aa2861c399 | |||
| 4ee064dd14 | |||
| 8803a5f5b2 | |||
| 834f6be548 | |||
| df8292b2a5 | |||
| 312c96ef8f | |||
| de29bd7d15 | |||
| 3a2c78fe37 | |||
| 4465e19987 | |||
| afe84fdb5b | |||
| 728727af54 | |||
| 987667f59f | |||
| af1ca2ffc1 | |||
| e71682f8fa | |||
| 339414694d | |||
| 3ad6fad018 | |||
| adf82e862d | |||
| c9e4f63e03 | |||
| ade2ffd9c0 | |||
| 3919d6a8a3 | |||
| 0fab295995 | |||
| aa7eac50a5 | |||
| 23e0fae6ac | |||
| 27233c1e01 | |||
| c16905101f | |||
| 6ef9708bb7 | |||
| cd9769b39c | |||
| 109bdd263e | |||
| c8e5a5173c | |||
| e94e875a8a | |||
| 9d7bd3c35b | |||
| 5725e684c0 | |||
| 7927ad4161 | |||
| e070a78872 | |||
| 1a6ec6b315 | |||
| 64ea537dae | |||
| 990cf0ec41 | |||
| 9cdd70f491 | |||
| a8623ade44 | |||
| c0e19eaab4 | |||
| 43241709af | |||
| 292263a60c | |||
| 760f75061e | |||
| e11ca4a244 | |||
| 0266072c6a | |||
| 99b76704d3 | |||
| b39edee3eb | |||
| 9e426308d0 | |||
| 416d76e03a | |||
| ae867ae230 | |||
| e83dc93dca | |||
| da220a3f5a | |||
| 8141c64f87 | |||
| 5aaedffe20 | |||
| 9ec53e39ec | |||
| c93807d67f | |||
| 6430f11188 | |||
| bc4c523d88 | |||
| b7aaeee13e | |||
| 504304c341 | |||
| 941bddb771 | |||
| 1990cebd58 | |||
| 041d6724e2 | |||
| 0086ed4723 | |||
| b83dd29b20 | |||
| aa01c68887 | |||
| 00f5fa993a | |||
| 6b98e23e50 | |||
| 3810b33c9e | |||
| e58b0e5315 | |||
| a2548c4f95 | |||
| 0151305ca3 | |||
| fbdac497fa | |||
| 052ac1ff8d | |||
| e113bc9fe1 | |||
| 03b7f00bcd | |||
| 3bedea2aba | |||
| 90a7e20be4 | |||
| 4a02ae2e98 | |||
| 9852cacb01 | |||
| eda79fd455 | |||
| 45af1d7ba6 | |||
| 7a4520086f | |||
| a252142cef | |||
| 12a0f7ad82 | |||
| 3ef3586bf7 | |||
| 4aecfb3bed | |||
| 9a31e04fad | |||
| af333e72f5 | |||
| 26f1a2b8ee | |||
| 497d91f4a4 | |||
| bd2d698d9f | |||
| b4c884a367 | |||
| 337dec37b8 | |||
| c12ecfdcf0 | |||
| 919e2bcdd7 | |||
| 943dbb28a6 | |||
| 0926da24f7 | |||
| 6ad80f540d | |||
| 77b9b14cb6 | |||
| b9c55871b8 | |||
| 848581452a | |||
| 2e53cdf29b | |||
| d9bc12c8d4 | |||
| 234f8b074b | |||
| 079c11a7e3 | |||
| 9d9d65e381 | |||
| 2517c57778 | |||
| 5d15ae6fe3 | |||
| f9fbb59c53 | |||
| c55f7a427c | |||
| 70b8c23e41 | |||
| 2591a794bf | |||
| 1c4be1012a | |||
| 030eeb663b | |||
| 4b3180bdfe | |||
| 28b7cdb187 | |||
| f57f6bb389 | |||
| 673abdfec2 | |||
| 1d4d068e08 | |||
| 72d83e9cfd | |||
| 883783250f | |||
| cc1ae55480 | |||
| 1bc3e77c1a | |||
| e24d865ce3 | |||
| 2762c83d84 | |||
| c58ad6142f | |||
| c45f5ca083 | |||
| fc5c792328 | |||
| 1b00209c55 | |||
| f199d18c4b | |||
| 3f1d07727b | |||
| 64d70269f9 | |||
| f0a71f8e70 | |||
| 8901479dd5 | |||
| 389456c46b | |||
| 40483dd4ee | |||
| fe1d7724bc | |||
| 9204183add | |||
| db9895077c | |||
| 36c086136f | |||
| f3c78c1892 | |||
| 6d538bf250 | |||
| 7a84eebf98 | |||
| 064101b155 | |||
| 91e7d04e4d | |||
| 711fa5a2b8 | |||
| 9f7bc4be75 | |||
| df7022b2e3 | |||
| 6ee51a7f43 | |||
| aed8a77e13 | |||
| bcfa22a818 | |||
| 0840d57689 | |||
| deb7e77263 | |||
| aba477a49c | |||
| a9e5a7754e | |||
| a2d4d100d3 | |||
| 7f4f5e9d08 | |||
| 55a2301bb5 | |||
| 14ac852eee | |||
| d2e53c7b8c | |||
| 4a335d2a28 | |||
| 999fde1f5c | |||
| 024e62103a | |||
| 73889c51ae | |||
| 7a0b29bda6 | |||
| a592a39fae | |||
| aa96ef66c6 | |||
| 2b24ce723e | |||
| 867ee32f91 | |||
| 662001accd | |||
| 334b2fe440 | |||
| 2f728629f3 | |||
| eb8628da5b | |||
| 1427940224 | |||
| 06f10e38f5 | |||
| 5092f5204f | |||
| 13f4420e42 | |||
| e0e6e60ae0 | |||
| 9cac49d539 | |||
| 97d9aefbeb | |||
| 340119f697 | |||
| a075eb34cd | |||
| 53913d3777 | |||
| c1d79a7e9f | |||
| 3ee6f0ceaa | |||
| 1821d82ded | |||
| 476ca95aa3 | |||
| d8b799f057 | |||
| 4c4807c22e | |||
| 8a9afc2d9a | |||
| 6fcb670a89 | |||
| d224b519f7 | |||
| b382595767 | |||
| 64ff6f73d4 | |||
| e1632b58ac | |||
| 7e39692cc4 | |||
| 8de44fb7b3 | |||
| 647aee010a | |||
| 296459d38a | |||
| 7941328a29 | |||
| 38d9a21a5d | |||
| 7d88f49e68 | |||
| 3c46215a3c | |||
| 137ed07b2b | |||
| 6f05850fbc | |||
| 80447f5c7a | |||
| 971d94495a | |||
| 77790a1cb2 | |||
| ecfadcb5a3 | |||
| d9e3b642a5 | |||
| e56e269f2e | |||
| 98f7ab8c02 | |||
| 5d33bd03a7 | |||
| 55882aa0ba | |||
| 43d1a94f22 | |||
| 53b2b9156f | |||
| fafc075735 | |||
| 09ee238e50 | |||
| 7be3813527 | |||
| e6491a6203 | |||
| 4955a24d6f | |||
| 425f53b74c | |||
| 2ed9595186 | |||
| 4d1e3beda6 | |||
| 8b0e1b8988 | |||
| c77682a9ad | |||
| 8e0ff1f2b2 | |||
| b5e027a553 | |||
| ef712949ad | |||
| b429b2cf8c | |||
| 9fb95c3030 | |||
| 6883d47508 | |||
| dae44745ca | |||
| 24b7d2e1b9 | |||
| 21ac6850d2 | |||
| 8b69a07b93 | |||
| f92bf347e3 | |||
| 78669051de | |||
| ea90b2b38b | |||
| bd61a0f2e9 | |||
| 888ad8897e | |||
| eb18021988 | |||
| 7b243a9615 | |||
| 5716c4169a | |||
| abb9f47646 | |||
| 9a5221ef7d | |||
| d9eaa06de3 | |||
| 4f91874b82 | |||
| 139438133d | |||
| 6705cf0fc0 | |||
| 00255e902c | |||
| 71e867156f | |||
| 658f437f73 | |||
| 95bf613263 | |||
| 03d61347ca | |||
| 050a0875fe | |||
| d9c46acb03 | |||
| f19fc0e92f | |||
| 3bc4b86749 | |||
| 9158f28998 |
@@ -113,6 +113,9 @@ builder.Services.AddAutoMapper(automapper =>
|
||||
//EF ORM QueryWithNoLock
|
||||
builder.Services.AddEFSetup(_configuration);
|
||||
|
||||
// FusionCache
|
||||
builder.Services.AddFusionCache();
|
||||
|
||||
builder.Services.AddMediator(cfg =>
|
||||
{
|
||||
|
||||
@@ -191,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
|
||||
{
|
||||
@@ -51,6 +52,8 @@ namespace IRaCIS.Core.SCP.Service
|
||||
|
||||
private Guid _trialSiteId { get; set; }
|
||||
|
||||
private bool _releasedNormally = false;
|
||||
|
||||
|
||||
|
||||
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
|
||||
@@ -118,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)
|
||||
{
|
||||
@@ -127,7 +130,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||
isCanReceiveIamge = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (association.CallingAE == "test-callingAE")
|
||||
@@ -180,12 +183,15 @@ namespace IRaCIS.Core.SCP.Service
|
||||
await AddUploadLogAsync();
|
||||
|
||||
|
||||
_releasedNormally = true;
|
||||
|
||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
//将检查设置为传输结束
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||
Log.Logger.Information($"进入释放连接请求 {_releasedNormally}");
|
||||
|
||||
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||
//var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
////将检查设置为传输结束
|
||||
//await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||
|
||||
//await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||
|
||||
}
|
||||
|
||||
@@ -199,15 +205,25 @@ namespace IRaCIS.Core.SCP.Service
|
||||
|
||||
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
|
||||
|
||||
_upload.EndTime = DateTime.Now;
|
||||
_upload.StudyCount = _ImageUploadList.Count;
|
||||
_upload.TrialId = _trialId;
|
||||
_upload.TrialSiteId = _trialSiteId;
|
||||
//有时候没有建立连接会走关闭通过日志发现
|
||||
if (_upload != null)
|
||||
{
|
||||
_upload.EndTime = DateTime.Now;
|
||||
_upload.StudyCount = _ImageUploadList.Count;
|
||||
_upload.TrialId = _trialId;
|
||||
_upload.TrialSiteId = _trialSiteId;
|
||||
|
||||
_upload.UploadJsonStr = (new SCPImageLog() { UploadList = _ImageUploadList }).ToJsonStr();
|
||||
|
||||
//可能是测试echo 导致记录了
|
||||
if (_upload.FileCount > 0)
|
||||
{
|
||||
//可能是测试echo 导致记录了
|
||||
await _SCPImageUploadRepository.AddAsync(_upload, true);
|
||||
}
|
||||
}
|
||||
|
||||
_upload.UploadJsonStr = (new SCPImageLog() { UploadList = _ImageUploadList }).ToJsonStr();
|
||||
|
||||
//可能是测试echo 导致记录了
|
||||
await _SCPImageUploadRepository.AddAsync(_upload, _upload.FileCount > 0 ? true : false);
|
||||
}
|
||||
|
||||
|
||||
@@ -262,22 +278,25 @@ namespace IRaCIS.Core.SCP.Service
|
||||
|
||||
public async void OnConnectionClosed(Exception exception)
|
||||
{
|
||||
/* nothing to do here */
|
||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
|
||||
//奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致
|
||||
if (exception == null)
|
||||
{
|
||||
//var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
////将检查设置为传输结束
|
||||
//await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||
|
||||
//await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||
}
|
||||
else
|
||||
if (exception != null || _releasedNormally == false)
|
||||
{
|
||||
//客户端断网,恢复后,也是没有异常的,估计是超时走了关闭
|
||||
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = true });
|
||||
//记录日志
|
||||
await AddUploadLogAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
//将检查设置为传输结束
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = false });
|
||||
}
|
||||
|
||||
|
||||
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||
|
||||
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
|
||||
}
|
||||
@@ -316,6 +335,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}";
|
||||
@@ -324,74 +348,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 pixelData = DicomPixelData.Create(dicomFile.Dataset);
|
||||
var syntax = pixelData.Syntax;
|
||||
foreach (var item in ircFiledList)
|
||||
{
|
||||
|
||||
// 每个 fragment 固定大小 (64KB 示例,可以自己调整)
|
||||
int fragmentSize = 20 * 1024;
|
||||
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
|
||||
|
||||
if (syntax.IsEncapsulated)
|
||||
if (dicomTag == DicomTag.ClinicalTrialProtocolID)
|
||||
{
|
||||
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
|
||||
dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolID, trialSiteInfo.TrialCode);
|
||||
|
||||
for (int n = 0; n < pixelData.NumberOfFrames; n++)
|
||||
}
|
||||
if (dicomTag == DicomTag.ClinicalTrialSiteID)
|
||||
{
|
||||
dataset.AddOrUpdate(DicomTag.ClinicalTrialSiteID, trialSiteInfo.TrialSiteCode);
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (pixelData != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var frameData = pixelData.GetFrame(n); // 获取完整一帧
|
||||
var data = frameData.Data;
|
||||
int offset = 0;
|
||||
|
||||
while (offset < data.Length)
|
||||
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)
|
||||
{
|
||||
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 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);
|
||||
|
||||
|
||||
}
|
||||
//传递过来的就是拆分的,但是是没有偏移表的,我需要自己创建偏移表,不然生成缩略图失败
|
||||
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());
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception mutiEx)
|
||||
{
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 处理多帧失败,上传原始文件:{mutiEx.ToString()}");
|
||||
}
|
||||
|
||||
// 替换原 PixelData
|
||||
dicomFile.Dataset.AddOrUpdate(newFragments);
|
||||
|
||||
|
||||
// 重新保存 dicom 到流
|
||||
ms.SetLength(0);
|
||||
dicomFile.Save(ms);
|
||||
}
|
||||
|
||||
|
||||
ms.Position = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#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;
|
||||
|
||||
|
||||
#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);
|
||||
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
||||
|
||||
#endregion
|
||||
using (await @lock.AcquireAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(dicomFile, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE, fileSize);
|
||||
|
||||
|
||||
//irc 从路径最后一截取Guid
|
||||
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
|
||||
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||
|
||||
//没有缩略图
|
||||
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
||||
{
|
||||
|
||||
// 生成缩略图
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
DicomImage image = new DicomImage(dicomFile.Dataset);
|
||||
|
||||
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++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileSize = ms.Length;
|
||||
}
|
||||
|
||||
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
|
||||
@@ -402,87 +735,6 @@ namespace IRaCIS.Core.SCP.Service
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 上传异常 {ec.Message}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
||||
|
||||
using (await @lock.AcquireAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.File, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
|
||||
|
||||
|
||||
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||
|
||||
//没有缩略图
|
||||
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
||||
{
|
||||
|
||||
// 生成缩略图
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
DicomImage image = new DicomImage(request.Dataset);
|
||||
|
||||
var sharpimage = image.RenderImage().AsSharpImage();
|
||||
sharpimage.Save(memoryStream, new JpegEncoder());
|
||||
|
||||
// 上传缩略图到 OSS
|
||||
|
||||
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, seriesId.ToString() + ".preview.jpg", false);
|
||||
|
||||
Console.WriteLine(seriesPath + " Id: " + seriesId);
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
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++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//监控信息设置
|
||||
_upload.FileCount++;
|
||||
_upload.FileSize = _upload.FileSize + fileSize;
|
||||
return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service;
|
||||
|
||||
|
||||
public static class CacheKeys
|
||||
{
|
||||
//项目缓存
|
||||
public static string Trial(string trialIdStr) => $"TrialId:{trialIdStr}";
|
||||
|
||||
//检查编号递增锁
|
||||
public static string TrialStudyMaxCode(Guid trialId) => $"TrialStudyMaxCode:{trialId}";
|
||||
|
||||
public static string TrialStudyUidUploading(Guid trialId, string studyUid) => $"TrialStudyUid:{trialId}_{studyUid}";
|
||||
//CRC上传影像提交锁key
|
||||
public static string TrialStudyUidDBLock(Guid trialId, string studyUid) => $"TrialStudyUidDBLock:{trialId}_{studyUid}";
|
||||
|
||||
public static string TrialTaskStudyUidUploading(Guid trialId, Guid visiTaskId, string studyUid) => $"TrialStudyUid:{trialId}_{visiTaskId}_{studyUid}";
|
||||
//影像后处理上传提交锁key
|
||||
public static string TrialTaskStudyUidDBLock(Guid trialId, Guid visiTaskId, string studyUid) => $"TrialTaskStudyUidDBLock:{trialId}_{visiTaskId}_{studyUid}";
|
||||
//系统匿名化
|
||||
public static string SystemAnonymization => $"SystemAnonymization";
|
||||
//前端国际化
|
||||
public static string FrontInternational => $"FrontInternationalList";
|
||||
|
||||
//登录挤账号
|
||||
public static string UserToken(Guid userId) => $"UserToken:{userId}";
|
||||
|
||||
//超时没请求接口自动退出
|
||||
public static string UserAutoLoginOut(Guid userId) => $"UserAutoLoginOut:{userId}";
|
||||
|
||||
|
||||
public static string UserDisable(Guid userId) => $"UserDisable:{userId}";
|
||||
|
||||
public static string UserRoleDisable(Guid userRoleId) => $"UserRoleDisable:{userRoleId}";
|
||||
|
||||
/// <summary>
|
||||
/// 用户登录错误 限制登录
|
||||
/// </summary>
|
||||
/// <param name="userName"></param>
|
||||
/// <returns></returns>
|
||||
public static string UserLoginError(string userName) => $"login-failures:{userName}";
|
||||
|
||||
/// <summary>
|
||||
/// 跳过阅片
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
public static string SkipReadingCacheKey(Guid userId) => $"{userId}SkipReadingCache";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 开始阅片时间
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
public static string StartReadingTimeKey(Guid userId) => $"{userId}StartReadingTime";
|
||||
|
||||
/// <summary>
|
||||
/// 开始休息时间
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
public static string StartRestTime(Guid userId) => $"{userId}StartRestTime";
|
||||
|
||||
//每个用户 每个浏览器独立时间
|
||||
public static string UserMFAVerifyPass(Guid userId, string browserFingerprint) => $"UserMFAVerifyPass:{userId}:{browserFingerprint}";
|
||||
|
||||
public static string TrialSiteInfo(Guid trialSiteId) => $"{trialSiteId}TrialSiteInfo";
|
||||
}
|
||||
|
||||
public static class CacheHelper
|
||||
{
|
||||
public static async Task<string?> GetTrialStatusAsync(Guid trialId, IRepository<Trial> _trialRepository)
|
||||
{
|
||||
var statusStr = await _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).Select(t => t.TrialStatusStr).FirstOrDefaultAsync();
|
||||
|
||||
return statusStr;
|
||||
}
|
||||
|
||||
public class TrialSiteInfoDTO
|
||||
{
|
||||
public string TrialSiteCode { get; set; }
|
||||
public string TrialCode { get; set; }
|
||||
}
|
||||
public static async Task<TrialSiteInfoDTO> GetTrialSiteInfo(Guid trialSiteId, IRepository<TrialSite> _trialSiteRepository)
|
||||
{
|
||||
var obj = await _trialSiteRepository.Where(t => t.Id == trialSiteId, ignoreQueryFilters: true).Select(t => new TrialSiteInfoDTO { TrialCode= t.Trial.TrialCode, TrialSiteCode= t.TrialSiteCode }).FirstOrDefaultAsync();
|
||||
|
||||
return obj??new TrialSiteInfoDTO();
|
||||
}
|
||||
|
||||
public static async Task<List<SystemAnonymization>> GetSystemAnonymizationListAsync(IRepository<SystemAnonymization> _systemAnonymizationRepository)
|
||||
{
|
||||
var list = await _systemAnonymizationRepository.Where(t => t.IsEnable).ToListAsync();
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,32 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using Medallion.Threading;
|
||||
using FellowOakDicom;
|
||||
using FellowOakDicom;
|
||||
using FellowOakDicom.Imaging.Codec;
|
||||
using System.Data;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using FellowOakDicom.Network;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using MassTransit;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using Serilog.Sinks.File;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
using MassTransit;
|
||||
using Medallion.Threading;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Serilog.Sinks.File;
|
||||
using System.Data;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using System.Text;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
using static IRaCIS.Core.SCP.Service.CacheHelper;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service
|
||||
{
|
||||
public class DicomArchiveService : BaseService, IDicomArchiveService
|
||||
public class DicomArchiveService(IRepository<SCPPatient> _patientRepository,
|
||||
IRepository<SCPStudy> _studyRepository,
|
||||
IRepository<SCPSeries> _seriesRepository,
|
||||
IRepository<SCPInstance> _instanceRepository
|
||||
) : BaseService, IDicomArchiveService
|
||||
{
|
||||
private readonly IRepository<SCPPatient> _patientRepository;
|
||||
private readonly IRepository<SCPStudy> _studyRepository;
|
||||
private readonly IRepository<SCPSeries> _seriesRepository;
|
||||
private readonly IRepository<SCPInstance> _instanceRepository;
|
||||
private readonly IRepository<Dictionary> _dictionaryRepository;
|
||||
private readonly IDistributedLockProvider _distributedLockProvider;
|
||||
|
||||
|
||||
private List<Guid> _instanceIdList = new List<Guid>();
|
||||
|
||||
public DicomArchiveService(IRepository<SCPPatient> patientRepository, IRepository<SCPStudy> studyRepository,
|
||||
IRepository<SCPSeries> seriesRepository,
|
||||
IRepository<SCPInstance> instanceRepository,
|
||||
IRepository<Dictionary> dictionaryRepository,
|
||||
IDistributedLockProvider distributedLockProvider)
|
||||
{
|
||||
_distributedLockProvider = distributedLockProvider;
|
||||
_studyRepository = studyRepository;
|
||||
_patientRepository = patientRepository;
|
||||
_seriesRepository = seriesRepository;
|
||||
_instanceRepository = instanceRepository;
|
||||
_dictionaryRepository = dictionaryRepository;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -53,18 +36,19 @@ namespace IRaCIS.Core.SCP.Service
|
||||
/// <param name="dataset"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public async Task<Guid> ArchiveDicomFileAsync(DicomFile dicomFile, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
|
||||
public async Task<Guid> ArchiveDicomFileAsync(DicomFile dicomFile, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE, long fileSize)
|
||||
{
|
||||
|
||||
var dataset = dicomFile.Dataset;
|
||||
|
||||
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
|
||||
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
|
||||
|
||||
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID,string.Empty);
|
||||
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
|
||||
|
||||
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
|
||||
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString());
|
||||
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
|
||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString());
|
||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString());
|
||||
|
||||
@@ -77,9 +61,9 @@ namespace IRaCIS.Core.SCP.Service
|
||||
|
||||
//using (@lock.Acquire())
|
||||
{
|
||||
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId );
|
||||
var findStudy = await _studyRepository.FirstOrDefaultAsync(t=>t.Id== studyId);
|
||||
var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId == trialSiteId);
|
||||
var findStudy = await _studyRepository.FirstOrDefaultAsync(t => t.Id == studyId);
|
||||
var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||
var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == instanceId);
|
||||
|
||||
DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay);
|
||||
@@ -93,8 +77,8 @@ namespace IRaCIS.Core.SCP.Service
|
||||
findPatient = new SCPPatient()
|
||||
{
|
||||
Id = NewId.NextSequentialGuid(),
|
||||
TrialId=trialId,
|
||||
TrialSiteId=trialSiteId,
|
||||
TrialId = trialId,
|
||||
TrialSiteId = trialSiteId,
|
||||
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
||||
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
||||
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
||||
@@ -122,7 +106,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||
|
||||
DateTime birthDate;
|
||||
|
||||
if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate,out birthDate))
|
||||
if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate, out birthDate))
|
||||
{
|
||||
var patientAge = studyTime.Value.Year - birthDate.Year;
|
||||
// 如果生日还未到,年龄减去一岁
|
||||
@@ -245,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);
|
||||
@@ -289,7 +274,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||
|
||||
RadiopharmaceuticalInformationSequence = dataset.GetSingleValueOrDefault(DicomTag.RadiopharmaceuticalInformationSequence, string.Empty),
|
||||
AcquisitionDate = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionDate, string.Empty),
|
||||
|
||||
|
||||
|
||||
InstanceCount = 0
|
||||
};
|
||||
@@ -334,7 +319,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||
SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0),
|
||||
|
||||
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
|
||||
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0),
|
||||
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1),
|
||||
PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty),
|
||||
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
|
||||
FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty),
|
||||
@@ -358,7 +343,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||
|
||||
Path = fileRelativePath,
|
||||
|
||||
FileSize= fileSize,
|
||||
FileSize = fileSize,
|
||||
|
||||
};
|
||||
|
||||
@@ -398,7 +383,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||
}
|
||||
else
|
||||
{
|
||||
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath,FileSize=fileSize });
|
||||
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath, FileSize = fileSize });
|
||||
}
|
||||
|
||||
await _studyRepository.SaveChangesAsync();
|
||||
|
||||
@@ -279,7 +279,7 @@ namespace IRaCIS.Api.Controllers
|
||||
|
||||
var isExpire = _tokenService.IsTokenExpired(token);
|
||||
|
||||
if (!await _useRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd) || isExpire)
|
||||
if ( Guid.TryParse(userId,out _) == false || isExpire || !await _useRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd) )
|
||||
{
|
||||
decodeUrl = errorUrl + $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(I18n.T("UserRedirect_InitializationLinkExpire"))} ";
|
||||
}
|
||||
|
||||
@@ -15,12 +15,13 @@ using Hangfire.Storage;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MassTransit.Mediator;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace IRaCIS.Core.API.HostService;
|
||||
|
||||
public class HangfireHostService(IRecurringMessageScheduler _recurringMessageScheduler,
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||
|
||||
IConfiguration _config,
|
||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||
IMediator _mediator,
|
||||
ILogger<HangfireHostService> _logger) : IHostedService
|
||||
@@ -56,6 +57,8 @@ public class HangfireHostService(IRecurringMessageScheduler _recurringMessageSch
|
||||
}
|
||||
}
|
||||
|
||||
string defaultCulture = _config.GetValue<string>("SystemEmailSendConfig:CronEmailDefaultCulture");
|
||||
|
||||
// 项目手动选择,周期性邮件
|
||||
var taskInfoList = await _trialEmailNoticeConfigRepository.Where(t => t.Trial.TrialStatusStr == StaticData.TrialState.TrialOngoing && t.EmailCron != string.Empty && t.IsAutoSend)
|
||||
.Select(t => new { t.Id, t.Code, TrialCode = t.Trial.TrialCode, t.EmailCron, t.BusinessScenarioEnum, t.TrialId })
|
||||
@@ -68,11 +71,11 @@ public class HangfireHostService(IRecurringMessageScheduler _recurringMessageSch
|
||||
|
||||
var trialId = task.TrialId;
|
||||
|
||||
HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, trialId, task.BusinessScenarioEnum, task.EmailCron);
|
||||
HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, trialId, task.BusinessScenarioEnum, task.EmailCron, defaultCulture);
|
||||
}
|
||||
|
||||
|
||||
// 系统邮件定时任务
|
||||
// 系统邮件定时任务 要设置默认语言
|
||||
var systemTaskInfoList = await _emailNoticeConfigrepository.Where(t => t.EmailCron != string.Empty && t.IsAutoSend)
|
||||
.Select(t => new { t.Id, t.Code, t.EmailCron, t.BusinessScenarioEnum, })
|
||||
.ToListAsync();
|
||||
@@ -82,7 +85,7 @@ public class HangfireHostService(IRecurringMessageScheduler _recurringMessageSch
|
||||
//利用主键作为任务Id
|
||||
var jobId = $"{task.Id}_({task.BusinessScenarioEnum})";
|
||||
|
||||
HangfireJobHelper.AddOrUpdateTimingCronJob(jobId, task.BusinessScenarioEnum, task.EmailCron);
|
||||
HangfireJobHelper.AddOrUpdateTimingCronJob(jobId, task.BusinessScenarioEnum, task.EmailCron, defaultCulture);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -29,6 +30,10 @@ using System.Runtime.InteropServices;
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
|
||||
|
||||
|
||||
Console.WriteLine("Startup Culture: " + CultureInfo.CurrentCulture.Name);
|
||||
Console.WriteLine("Startup UI Culture: " + CultureInfo.CurrentUICulture.Name);
|
||||
|
||||
#region 获取环境变量
|
||||
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
||||
var config = new ConfigurationBuilder()
|
||||
@@ -96,6 +101,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>();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:3305",
|
||||
"applicationUrl": "http://0.0.0.0:3305",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
@@ -23,7 +23,7 @@
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC",
|
||||
"ASPNETCORE_OpenSwagger": "true"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"IRaCIS.Test_IRC_PGSQL": {
|
||||
"commandName": "Project",
|
||||
@@ -31,7 +31,7 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC_PGSQL"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"IRaCIS.Event_IRC": {
|
||||
"commandName": "Project",
|
||||
@@ -39,7 +39,7 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Event_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"Docker": {
|
||||
"commandName": "Docker",
|
||||
@@ -53,7 +53,7 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Uat_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"IRaCIS.Prod_IRC": {
|
||||
"commandName": "Project",
|
||||
@@ -61,7 +61,7 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Prod_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"IRaCIS.US_Uat_IRC": {
|
||||
"commandName": "Project",
|
||||
@@ -69,7 +69,7 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "US_Uat_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"IRaCIS.US_Prod_IRC": {
|
||||
"commandName": "Project",
|
||||
@@ -77,7 +77,7 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "US_Prod_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ namespace IRaCIS.Core.API._PipelineExtensions.Serilog
|
||||
|
||||
var requestBodyPayload = await ReadRequestBody(context.Request);
|
||||
|
||||
context.Items["RequestBody"] = requestBodyPayload;
|
||||
|
||||
using (LogContext.PushProperty("RequestBody", requestBodyPayload))
|
||||
{
|
||||
//await _next.Invoke()
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace IRaCIS.Core.API
|
||||
}
|
||||
else
|
||||
{
|
||||
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure()/*.CommandTimeout(60)*/);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace IRaCIS.Core.API
|
||||
.Enrich.FromLogContext()
|
||||
.Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health"))
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,retainedFileCountLimit:60);
|
||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,retainedFileCountLimit:365);
|
||||
|
||||
#region 根据环境配置是否打开错误发送邮件通知
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
@@ -56,13 +57,14 @@
|
||||
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyDays": 1
|
||||
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "uat@extimaging.com",
|
||||
"FromName": "UAT_IRC",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
//"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
//"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||
},
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621697b96f74e6f3d" ]
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
@@ -36,7 +42,8 @@
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
@@ -55,20 +62,24 @@
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyDays": 1
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "irc@extimaging.com",
|
||||
"FromName": "IRC Imaging System",
|
||||
"AuthorizationCode": "ExtImg@2022",
|
||||
"SiteUrl": "http://irc.extimaging.com/login",
|
||||
|
||||
"PlatformName": "EICS",
|
||||
"PlatformNameCN": "展影云平台",
|
||||
"SystemShortName": "IRC",
|
||||
"OrganizationName": "Extlmaging",
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
"OrganizationName": "ExtImaging",
|
||||
"OrganizationNameCN": "ExtImaging",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
@@ -76,6 +87,7 @@
|
||||
"IsEnv_US": false,
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"CronEmailDefaultCulture": "zh-CN",
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
},
|
||||
"SystemPacsConfig": {
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
// Hangfire 定时任务数据库链接字符串
|
||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621697b96f74e6f3d" ]
|
||||
},
|
||||
// 对象存储服务配置
|
||||
"ObjectStoreService": {
|
||||
// 使用的对象存储服务类型
|
||||
@@ -88,6 +94,8 @@
|
||||
},
|
||||
// 系统配置
|
||||
"BasicSystemConfig": {
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
// 打开用户复杂密码
|
||||
"OpenUserComplexPassword": false,
|
||||
// 是否在开始工作前强制签署电子知情同意书
|
||||
@@ -110,7 +118,7 @@
|
||||
"IsNeedChangePassWord": true,
|
||||
// 密码有效期(天),到期后必须修改
|
||||
"ChangePassWordDays": 90,
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
// 模板类型 1 Elevate(LiLi) 2 Extensive(展影)
|
||||
"TemplateType": 2,
|
||||
// 是否打开项目关联删除
|
||||
"OpenTrialRelationDelete": true,
|
||||
@@ -127,6 +135,8 @@
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
|
||||
"ImapPort": 993,
|
||||
// 发件人邮箱地址
|
||||
"FromEmail": "test@extimaging.com",
|
||||
// 发件人显示名称
|
||||
@@ -135,12 +145,15 @@
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
// 系统对外访问地址
|
||||
"SiteUrl": "http://irc.test.extimaging.com/login",
|
||||
// 系统简称
|
||||
|
||||
"PlatformName": "EICS",
|
||||
"PlatformNameCN": "展影云平台",
|
||||
// 系统简称一致性核查使用
|
||||
"SystemShortName": "IRC",
|
||||
// 组织英文名称
|
||||
"OrganizationName": "Extlmaging",
|
||||
"OrganizationName": "ExtImaging",
|
||||
// 组织中文名称
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
"OrganizationNameCN": "ExtImaging",
|
||||
// 公司英文全称
|
||||
"CompanyName": "Extensive Imaging",
|
||||
// 公司中文全称
|
||||
@@ -155,6 +168,7 @@
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
// 邮箱格式校验正则表达式
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"CronEmailDefaultCulture": "zh-CN",
|
||||
// 接收系统异常报警的邮箱列表
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
},
|
||||
|
||||
@@ -47,7 +47,8 @@
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": false,
|
||||
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
@@ -76,13 +77,14 @@
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "test@extimaging.com",
|
||||
"FromName": "Test_IRC",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
"SiteUrl": "http://irc.test.extimaging.com/login",
|
||||
|
||||
"OrganizationName": "Extlmaging",
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
"OrganizationName": "ExtImaging",
|
||||
"OrganizationNameCN": "ExtImaging",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
|
||||
@@ -12,7 +12,12 @@
|
||||
//"RemoteNew": "Server=44.210.231.169,1435;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
//"Hangfire": "Server=44.210.231.169,1435;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
},
|
||||
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621697b96f74e6f3d" ]
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AWS",
|
||||
"MinIO": {
|
||||
@@ -38,7 +43,8 @@
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
@@ -58,8 +64,8 @@
|
||||
"TemplateType": 1,
|
||||
|
||||
"OpenTrialRelationDelete": false,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyDays": 1
|
||||
//MFA免验证
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
|
||||
},
|
||||
|
||||
@@ -67,10 +73,14 @@
|
||||
"Port": 587,
|
||||
"Host": "smtp-mail.outlook.com",
|
||||
"Imap": "imap-mail.outlook.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "donotreply@elevateimaging.ai",
|
||||
"FromName": "LiLi System",
|
||||
"AuthorizationCode": "Q#669869497420ul",
|
||||
|
||||
|
||||
"PlatformName": "LiLi",
|
||||
"PlatformNameCN": "LiLi",
|
||||
"SystemShortName": "LiLi",
|
||||
"OrganizationName": "Elevate Imaging",
|
||||
"OrganizationNameCN": "Elevate Imaging",
|
||||
@@ -82,6 +92,7 @@
|
||||
"IsEnv_US": true,
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"CronEmailDefaultCulture": "en-US",
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
},
|
||||
|
||||
|
||||
@@ -44,7 +44,8 @@
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
@@ -68,17 +69,22 @@
|
||||
"TemplateType": 1,
|
||||
"OpenLoginMFA": true,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyDays": 1
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 587,
|
||||
"Host": "smtp-mail.outlook.com",
|
||||
"Imap": "imap-mail.outlook.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "donotreply@elevateimaging.ai",
|
||||
"FromName": "LiLi System",
|
||||
"AuthorizationCode": "Q#669869497420ul",
|
||||
|
||||
|
||||
"PlatformName": "LiLi",
|
||||
"PlatformNameCN": "LiLi",
|
||||
"SystemShortName": "LiLi",
|
||||
"OrganizationName": "Elevate Imaging",
|
||||
"OrganizationNameCN": "Elevate Imaging",
|
||||
|
||||
@@ -13,6 +13,12 @@
|
||||
"RemoteNew": "Server=3.226.182.187,1435;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=3.226.182.187,1435;Database=US_Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621697b96f74e6f3d" ]
|
||||
},
|
||||
|
||||
"ObjectStoreService": {
|
||||
|
||||
@@ -48,7 +54,8 @@
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
@@ -67,17 +74,20 @@
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 1,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyDays": 1
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 587,
|
||||
"Host": "smtp-mail.outlook.com",
|
||||
"Imap": "imap-mail.outlook.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "donotreply@elevateimaging.ai",
|
||||
"FromName": "LiLi System",
|
||||
"AuthorizationCode": "Q#669869497420ul",
|
||||
|
||||
"PlatformName": "LiLi",
|
||||
"PlatformNameCN": "LiLi",
|
||||
"SystemShortName": "LiLi",
|
||||
"OrganizationName": "Elevate Imaging",
|
||||
"OrganizationNameCN": "Elevate Imaging",
|
||||
@@ -89,6 +99,7 @@
|
||||
"IsEnv_US": true,
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"CronEmailDefaultCulture": "en-US",
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
},
|
||||
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
"RemoteNew": "Server=101.132.253.119,1435;Database=Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=101.132.253.119,1435;Database=Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621697b96f74e6f3d" ]
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
@@ -54,7 +60,8 @@
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
@@ -74,21 +81,24 @@
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyDays": 1
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "uat@extimaging.com",
|
||||
"FromName": "Uat IRC Imaging System",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
"SiteUrl": "http://irc.uat.extimaging.com/login",
|
||||
|
||||
"PlatformName": "EICS",
|
||||
"PlatformNameCN": "展影云平台",
|
||||
"SystemShortName": "IRC",
|
||||
"OrganizationName": "Extlmaging",
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
"OrganizationName": "ExtImaging",
|
||||
"OrganizationNameCN": "ExtImaging",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
@@ -96,6 +106,7 @@
|
||||
"IsEnv_US": false,
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"CronEmailDefaultCulture": "zh-CN",
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
},
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"SecurityKey": "ShangHaiZhanYing_SecurityKey_SHzyyl@2021",
|
||||
"Issuer": "Extimaging",
|
||||
"Audience": "EICS",
|
||||
"TokenExpireMinute": "10080"//7天
|
||||
"TokenExpireMinute": "10080" //7天
|
||||
},
|
||||
"IpRateLimiting": {
|
||||
"EnableEndpointRateLimiting": true,
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>%(DocumentTitle)</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css">
|
||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||
|
||||
@@ -505,8 +505,8 @@ var abp = abp || {};
|
||||
|
||||
//Inputs
|
||||
createInput(modalUxContent, 'tenancyName', 'Tenancy Name (Leave empty for Host)');
|
||||
createInput(modalUxContent, 'userName', 'Username or email address', 'text', 'cyldev');
|
||||
createInput(modalUxContent, 'password', 'Password', 'password', '123456');
|
||||
createInput(modalUxContent, 'userName', 'Username or email address', 'text', 'user1wj');
|
||||
createInput(modalUxContent, 'password', 'Password', 'password', '1');
|
||||
createInput(modalUxContent, 'pwdMd5', 'PwdMd5', 'text', '');
|
||||
createSelect(modalUxContent, 'roleSelect', 'role', [])
|
||||
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using IRaCIS.Core.Application.Helper.OtherTool;
|
||||
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.Configuration;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
|
||||
namespace IRaCIS.Core.Application.Filter;
|
||||
|
||||
|
||||
|
||||
public class ModelActionFilter(IStringLocalizer _localizer) : ActionFilterAttribute, IActionFilter
|
||||
public class ModelActionFilter(IStringLocalizer _localizer, IConfiguration _config, IUserInfo _userInfo) : ActionFilterAttribute, IActionFilter
|
||||
{
|
||||
|
||||
|
||||
@@ -23,8 +31,55 @@ public class ModelActionFilter(IStringLocalizer _localizer) : ActionFilterAttrib
|
||||
.Select(e => e.ErrorMessage)
|
||||
.ToArray();
|
||||
|
||||
var request = context.HttpContext.Request;
|
||||
|
||||
try
|
||||
{
|
||||
bool isOpenWeComNotice = _config.GetValue<bool>("WeComNoticeConfig:IsOpenWeComNotice");
|
||||
|
||||
if (isOpenWeComNotice)
|
||||
{
|
||||
string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty;
|
||||
|
||||
var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]);
|
||||
var baseUrl = uri.GetLeftPart(UriPartial.Authority);
|
||||
|
||||
var userList = _config.GetSection("WeComNoticeConfig:VueNoticeUserList").Get<string[]>();
|
||||
|
||||
var requestBody = context.HttpContext.Items["RequestBody"] as string;
|
||||
|
||||
|
||||
// 🔔 异步告警(不要阻塞请求)
|
||||
_ = WeComNotifier.SendAlertAsync(
|
||||
webhook: webhook,
|
||||
alert: new WeComAlert
|
||||
{
|
||||
Env = baseUrl,
|
||||
UserName = _userInfo.UserName.IsNotNullOrEmpty()? $"{_userInfo.UserName}({_userInfo.UserTypeShortName})": "匿名",
|
||||
Api = $"{request.Method} {request.Path}",
|
||||
Message = $"前端传递参数,后端模型验证失败\n 具体信息:{JsonConvert.SerializeObject(validationErrors)}",
|
||||
JsonData = requestBody,
|
||||
AtUsers = userList ?? []
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error($"模型验证过滤器里发送企业微信出现错误:{ex.Message}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---提供给接口的参数无效。
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject(validationErrors)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Application.Helper.OtherTool;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace IRaCIS.Core.Application.Filter;
|
||||
|
||||
public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, IStringLocalizer _localizer) : Attribute, IExceptionFilter
|
||||
public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, IStringLocalizer _localizer, IConfiguration _config, IUserInfo _userInfo) : Attribute, IExceptionFilter
|
||||
{
|
||||
|
||||
public void OnException(ExceptionContext context)
|
||||
@@ -46,7 +49,44 @@ 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));
|
||||
|
||||
try
|
||||
{
|
||||
bool isOpenWeComNotice = _config.GetValue<bool>("WeComNoticeConfig:IsOpenWeComNotice");
|
||||
|
||||
if (isOpenWeComNotice)
|
||||
{
|
||||
string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty;
|
||||
|
||||
var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]);
|
||||
var baseUrl = uri.GetLeftPart(UriPartial.Authority);
|
||||
|
||||
var userList = _config.GetSection("WeComNoticeConfig:APINoticeUserList").Get<string[]>();
|
||||
|
||||
var requestBody = context.HttpContext.Items["RequestBody"] as string;
|
||||
|
||||
var alert = new WeComAlert
|
||||
{
|
||||
Env = baseUrl,
|
||||
UserName = $"{_userInfo.UserName}({_userInfo.UserTypeShortName})",
|
||||
Api = context.HttpContext.Request.Path,
|
||||
Message = exception.Message,
|
||||
JsonData = requestBody,
|
||||
Stack = exception.StackTrace,
|
||||
AtUsers = userList ?? []
|
||||
};
|
||||
|
||||
_ = WeComNotifier.SendAlertAsync(webhook, alert);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
_logger.LogError($"异常过滤器里发送企业微信出现错误:{ex.Message}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -58,6 +98,7 @@ public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, ISt
|
||||
|
||||
_logger.LogError(errorInfo);
|
||||
|
||||
|
||||
//_logger.LogError(exception, exception.Message);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace IRaCIS.Core.Domain.Share;
|
||||
[Description("多环境 配置环境实体")]
|
||||
public class ServiceVerifyConfigOption
|
||||
{
|
||||
public bool QCRiskControl { get; set; }
|
||||
public bool OpenUserComplexPassword { get; set; }
|
||||
|
||||
public bool OpenSignDocumentBeforeWork { get; set; }
|
||||
@@ -49,6 +50,8 @@ public class SystemEmailSendConfig
|
||||
public string Host { get; set; } = string.Empty;
|
||||
|
||||
public string Imap { get; set; } = string.Empty;
|
||||
|
||||
public int ImapPort { get; set; }
|
||||
public string FromEmail { get; set; } = string.Empty;
|
||||
|
||||
public string FromName { get; set; } = string.Empty;
|
||||
@@ -57,6 +60,10 @@ public class SystemEmailSendConfig
|
||||
|
||||
public string SiteUrl { get; set; } = string.Empty;
|
||||
|
||||
|
||||
public string PlatformName { get; set; } = string.Empty;
|
||||
public string PlatformNameCN { get; set; } = string.Empty;
|
||||
|
||||
public string SystemShortName { get; set; } = string.Empty;
|
||||
|
||||
public string OrganizationName { get; set; } = string.Empty;
|
||||
|
||||
@@ -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;
|
||||
@@ -13,6 +14,9 @@ namespace IRaCIS.Core.Application.Helper
|
||||
|
||||
public class StudyDIRInfo
|
||||
{
|
||||
public Guid SubjectId { get; set; }
|
||||
public bool IsTaskStudy { get; set; }
|
||||
|
||||
public Guid SubjectVisitId { get; set; }
|
||||
// Study
|
||||
public Guid DicomStudyId { get; set; }
|
||||
@@ -54,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();
|
||||
|
||||
@@ -97,11 +102,11 @@ namespace IRaCIS.Core.Application.Helper
|
||||
var dicomFile = new DicomFile(dataset);
|
||||
|
||||
// 文件名递增格式:IM_00001, IM_00002, ...
|
||||
string filename = $@"IMAGE/IM_{index:D5}"; // :D5 表示补足5位
|
||||
string filename = $@"IMAGE\IM_{index:D5}"; // :D5 表示补足5位
|
||||
|
||||
mappings.Add($"{filename} => {item.InstanceId}");
|
||||
|
||||
dic.Add(item.InstanceId.ToString(), Path.GetFileName(filename));
|
||||
dic.Add(item.InstanceId.ToString(), filename.TrimEnd('/', '\\').Split('/', '\\').Last());
|
||||
|
||||
dicomDir.AddFile(dicomFile, filename);
|
||||
|
||||
@@ -125,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());
|
||||
}
|
||||
|
||||
//清理临时文件
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using MailKit;
|
||||
using MailKit.Security;
|
||||
using MimeKit;
|
||||
using Org.BouncyCastle.Tls;
|
||||
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper;
|
||||
@@ -9,12 +10,15 @@ namespace IRaCIS.Core.Application.Helper;
|
||||
public static class SendEmailHelper
|
||||
{
|
||||
|
||||
public static async Task SendEmailAsync(MimeMessage messageToSend, SystemEmailSendConfig _systemEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
|
||||
public static async Task<string> SendEmailAsync(MimeMessage messageToSend, SystemEmailSendConfig _systemEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
|
||||
{
|
||||
string result = string.Empty;
|
||||
result = messageToSend.MessageId;
|
||||
|
||||
//没有收件人 那么不发送
|
||||
if (messageToSend.To.Count == 0)
|
||||
{
|
||||
return;
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
@@ -52,23 +56,78 @@ public static class SendEmailHelper
|
||||
throw new Exception(I18n.T("SendEmail_SendFail"), new Exception(ex.Message));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置发送邮箱
|
||||
/// </summary>
|
||||
/// <param name="messageToSend"></param>
|
||||
/// <param name="senderName"></param>
|
||||
public static void ChangeEmailSenderName(MimeMessage messageToSend, string senderName)
|
||||
|
||||
|
||||
public static async Task<string> SendEmailAsync(MimeMessage messageToSend, Trial trial, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
|
||||
{
|
||||
|
||||
// 项目的需要重设 发件地址与邮件地址
|
||||
var fromAddress = messageToSend.From.Mailboxes.FirstOrDefault();
|
||||
if (fromAddress != null)
|
||||
{
|
||||
messageToSend.From.Clear();
|
||||
messageToSend.From.Add(new MailboxAddress(senderName, fromAddress.Address));
|
||||
messageToSend.From.Add(new MailboxAddress(trial.EmailFromName, trial.EmailFromEmail));
|
||||
}
|
||||
|
||||
|
||||
string result = string.Empty;
|
||||
result = messageToSend.MessageId;
|
||||
|
||||
//没有收件人 那么不发送
|
||||
if (messageToSend.To.Count == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (var smtp = new MailKit.Net.Smtp.SmtpClient())
|
||||
{
|
||||
if (messageSentSuccess != null)
|
||||
{
|
||||
smtp.MessageSent += messageSentSuccess;
|
||||
}
|
||||
|
||||
|
||||
smtp.ServerCertificateValidationCallback = (s, c, h, e) => true;
|
||||
|
||||
//await smtp.ConnectAsync("smtp.qq.com", 465, SecureSocketOptions.SslOnConnect);
|
||||
|
||||
//await smtp.AuthenticateAsync("zhou941003@qq.com", "sqfhlpfdvnexbcab");
|
||||
|
||||
|
||||
//await smtp.ConnectAsync(_systemEmailConfig.Host, _systemEmailConfig.Port, SecureSocketOptions.Auto);
|
||||
|
||||
//await smtp.AuthenticateAsync(_systemEmailConfig.FromEmail, _systemEmailConfig.AuthorizationCode);
|
||||
|
||||
|
||||
await smtp.ConnectAsync(trial.EmailSMTPServerAddress, trial.EmailSMTPServerPort, SecureSocketOptions.Auto);
|
||||
|
||||
await smtp.AuthenticateAsync(trial.EmailFromEmail, trial.EmailAuthorizationCode);
|
||||
|
||||
|
||||
await smtp.SendAsync(messageToSend);
|
||||
|
||||
await smtp.DisconnectAsync(true);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
//---邮件发送失败,您进行的操作未能成功,请检查邮箱或联系维护人员
|
||||
throw new Exception(I18n.T("SendEmail_SendFail"), new Exception(ex.Message));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<bool> TestEmailConfigAsync(SystemEmailSendConfig _systemEmailConfig)
|
||||
{
|
||||
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||
@@ -88,7 +147,7 @@ public static class SendEmailHelper
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async Task SendEmailAsync(SMTPEmailConfig sMTPEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
|
||||
public static async Task SendEmailAsync(SMTPEmailConfig sMTPEmailConfig,Trial? trial, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
|
||||
{
|
||||
var messageToSend = new MimeMessage();
|
||||
|
||||
@@ -158,7 +217,7 @@ public static class SendEmailHelper
|
||||
|
||||
await smtp.ConnectAsync(sMTPEmailConfig.Host, sMTPEmailConfig.Port, SecureSocketOptions.Auto);
|
||||
|
||||
await smtp.AuthenticateAsync(sMTPEmailConfig.UserName, sMTPEmailConfig.AuthorizationCode);
|
||||
await smtp.AuthenticateAsync(trial.EmailFromEmail, trial.EmailAuthorizationCode);
|
||||
|
||||
await smtp.SendAsync(messageToSend);
|
||||
|
||||
|
||||
+12
@@ -60,4 +60,16 @@ namespace IRaCIS.Core.Application.Helper
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
|
||||
public class DateTimeTranaslateAttribute : Attribute
|
||||
{
|
||||
public string Formart { get; set; }
|
||||
|
||||
public DateTimeTranaslateAttribute(string formart)
|
||||
{
|
||||
Formart = formart;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using FellowOakDicom.Imaging.LUT;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Application.Interfaces;
|
||||
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore.Migrations;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MiniExcelLibs;
|
||||
using MiniExcelLibs.OpenXml;
|
||||
using Newtonsoft.Json;
|
||||
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;
|
||||
@@ -681,13 +686,61 @@ public static class ExcelExportHelper
|
||||
if (itemDicName.IsNotNullOrEmpty())
|
||||
{
|
||||
|
||||
var translatedItemData = dynamicTranslateDataList[itemDicName].Where(t => t.Code.ToLower() == itemValue?.ToLower()).Select(t => isEn_US ? t.Value : t.ValueCN).FirstOrDefault() ?? String.Empty;
|
||||
var optionTypeEnumStr = iteObjDic.ContainsKey("OptionTypeEnum") ? iteObjDic["OptionTypeEnum"]?.ToString() : "0";
|
||||
|
||||
var translatedItemData = "";
|
||||
//多选
|
||||
if (optionTypeEnumStr == "1")
|
||||
{
|
||||
int[] enumValues = new int[0];
|
||||
// 1. 反序列化 JSON 数组 (字符串枚举)
|
||||
if (!itemValue.StartsWith("[") || !itemValue.EndsWith("]"))
|
||||
{
|
||||
enumValues = new int[1] { int.Parse(itemValue) };
|
||||
}
|
||||
else
|
||||
{
|
||||
enumValues = JsonConvert.DeserializeObject<int[]>(itemValue) ?? new int[0];
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 2. 翻译每一项并输出逗号拼接字符串
|
||||
translatedItemData = string.Join(",",
|
||||
enumValues.Select(code =>
|
||||
dynamicTranslateDataList[itemDicName]
|
||||
.FirstOrDefault(t =>
|
||||
string.Equals(code.ToString(), t.Code, StringComparison.OrdinalIgnoreCase)
|
||||
) is var r && r != null
|
||||
? (isEn_US ? r.Value : r.ValueCN)
|
||||
: string.Empty
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
translatedItemData = dynamicTranslateDataList[itemDicName].Where(t => t.Code.ToLower() == itemValue?.ToLower()).Select(t => isEn_US ? t.Value : t.ValueCN).FirstOrDefault() ?? String.Empty;
|
||||
|
||||
}
|
||||
|
||||
row.GetCell(writeIndex).SetCellValue(translatedItemData);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
row.GetCell(writeIndex).SetCellValue(itemValue);
|
||||
var unit = iteObjDic.ContainsKey("Unit") ? iteObjDic["Unit"]?.ToString() : null;
|
||||
|
||||
if (unit.IsNotNullOrEmpty() && unit == "9")
|
||||
{
|
||||
row.GetCell(writeIndex).SetCellValue(itemValue + "%");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
row.GetCell(writeIndex).SetCellValue(itemValue);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -818,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;
|
||||
|
||||
@@ -933,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;
|
||||
|
||||
}
|
||||
|
||||
@@ -1047,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();
|
||||
|
||||
@@ -1055,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);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit.Mediator;
|
||||
using System.Globalization;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
@@ -57,25 +58,29 @@ namespace IRaCIS.Core.Application.Helper
|
||||
}
|
||||
|
||||
|
||||
public static void AddOrUpdateTrialCronJob(string jobId, Guid trialId, EmailBusinessScenario businessScenario, string emailCron)
|
||||
public static void AddOrUpdateTrialCronJob(string jobId, Guid trialId, EmailBusinessScenario businessScenario, string emailCron,string defaultCulture="")
|
||||
{
|
||||
if (defaultCulture == "")
|
||||
{
|
||||
defaultCulture = CultureInfo.CurrentCulture.Name;
|
||||
}
|
||||
|
||||
switch (businessScenario)
|
||||
{
|
||||
|
||||
case EmailBusinessScenario.QCTask:
|
||||
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new ImageQCRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new ImageQCRecurringEvent() { TrialId = trialId,CultureInfoName= defaultCulture }, default), emailCron);
|
||||
break;
|
||||
case EmailBusinessScenario.CRCToQCQuestion:
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new CRCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new CRCImageQuestionRecurringEvent() { TrialId = trialId, CultureInfoName = defaultCulture }, default), emailCron);
|
||||
break;
|
||||
case EmailBusinessScenario.QCToCRCImageQuestion:
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId, CultureInfoName = defaultCulture }, default), emailCron);
|
||||
break;
|
||||
//加急阅片 10分钟
|
||||
case EmailBusinessScenario.ExpeditedReading:
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new UrgentIRUnReadTaskRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new UrgentIRUnReadTaskRecurringEvent() { TrialId = trialId, CultureInfoName = defaultCulture }, default), emailCron);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -83,20 +88,25 @@ namespace IRaCIS.Core.Application.Helper
|
||||
|
||||
}
|
||||
|
||||
public static void AddOrUpdateTimingCronJob(string jobId, EmailBusinessScenario businessScenario, string emailCron)
|
||||
public static void AddOrUpdateTimingCronJob(string jobId, EmailBusinessScenario businessScenario, string emailCron, string defaultCulture="")
|
||||
{
|
||||
if (defaultCulture == "")
|
||||
{
|
||||
defaultCulture = CultureInfo.CurrentCulture.Name;
|
||||
}
|
||||
|
||||
switch (businessScenario)
|
||||
{
|
||||
|
||||
case EmailBusinessScenario.GeneralTraining_ExpirationNotification:
|
||||
|
||||
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new SystemDocumentErverDayEvent() { }, default), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new SystemDocumentErverDayEvent() { CultureInfoName = defaultCulture }, default), emailCron);
|
||||
break;
|
||||
case EmailBusinessScenario.TrialTraining_ExpirationNotification:
|
||||
|
||||
Console.WriteLine("更新项目到期job");
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new TrialDocumentErverDayEvent() { }, default), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new TrialDocumentErverDayEvent() { CultureInfoName = defaultCulture }, default), emailCron);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -16,6 +16,7 @@ using Minio;
|
||||
using Minio.DataModel;
|
||||
using Minio.DataModel.Args;
|
||||
using Minio.Exceptions;
|
||||
using System.IO;
|
||||
using System.Reactive.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Web;
|
||||
@@ -142,6 +143,10 @@ public enum ObjectStoreUse
|
||||
|
||||
public interface IOSSService
|
||||
{
|
||||
public Task SetImmediateArchiveRule(string prefix, string ruleId = "immediate-archive", bool isDelete = false);
|
||||
public Task RestoreFilesByPrefixAsync(string prefix, int restoreDays = 3, int batchSize = 100);
|
||||
|
||||
|
||||
public Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true);
|
||||
public Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true, bool randomFileName = false);
|
||||
|
||||
@@ -155,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();
|
||||
|
||||
@@ -182,7 +187,518 @@ public class OSSService : IOSSService
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定前缀下的所有现有文件立即转为目标存储类型
|
||||
/// </summary>
|
||||
/// <param name="prefix">要转换的文件前缀,如 "project-a/logs/"</param>
|
||||
/// <param name="ruleId">规则ID,默认为"immediate-archive"</param>
|
||||
/// <param name="isDelete">默认是添加/更新 </param>
|
||||
public async Task SetImmediateArchiveRule(string prefix, string ruleId = "immediate-archive", bool isDelete = false)
|
||||
{
|
||||
|
||||
BackBatchGetToken();
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
|
||||
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||
|
||||
try
|
||||
{
|
||||
// 1. 先获取现有的所有生命周期规则(避免覆盖)
|
||||
var existingRules = new List<Aliyun.OSS.LifecycleRule>();
|
||||
try
|
||||
{
|
||||
var existingRuleList = _ossClient.GetBucketLifecycle(aliConfig.BucketName);
|
||||
if (existingRuleList != null)
|
||||
{
|
||||
existingRules.AddRange(existingRuleList);
|
||||
Console.WriteLine($"找到 {existingRules.Count} 条现有规则");
|
||||
}
|
||||
}
|
||||
catch (OssException ex) when (ex.ErrorCode == "NoSuchLifecycle")
|
||||
{
|
||||
// 如果没有生命周期规则,继续创建新规则
|
||||
Console.WriteLine("当前Bucket无生命周期规则,将创建新规则");
|
||||
}
|
||||
|
||||
// 2. 创建立即生效的转换规则
|
||||
|
||||
ruleId = $"{ruleId}_{prefix}";
|
||||
var immediateRule = new Aliyun.OSS.LifecycleRule
|
||||
{
|
||||
ID = ruleId,
|
||||
Prefix = prefix,
|
||||
Status = RuleStatus.Enabled,
|
||||
Transitions = new Aliyun.OSS.LifecycleRule.LifeCycleTransition[]
|
||||
{
|
||||
new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
{
|
||||
|
||||
LifeCycleExpiration =
|
||||
{
|
||||
Days = 1
|
||||
},
|
||||
StorageClass = StorageClass.IA
|
||||
},
|
||||
new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
{
|
||||
|
||||
LifeCycleExpiration =
|
||||
{
|
||||
Days = 30 //最后一次修改时间
|
||||
},
|
||||
StorageClass = StorageClass.Archive
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// 3. 移除同名的旧规则(如果存在)
|
||||
existingRules.RemoveAll(r => r.ID == ruleId);
|
||||
|
||||
// 4. 添加新规则到规则列表
|
||||
if (isDelete == false)
|
||||
{
|
||||
existingRules.Add(immediateRule);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
var request = new SetBucketLifecycleRequest(aliConfig.BucketName)
|
||||
{
|
||||
LifecycleRules = existingRules
|
||||
};
|
||||
|
||||
|
||||
_ossClient.SetBucketLifecycle(request);
|
||||
|
||||
|
||||
}
|
||||
catch (OssException ex)
|
||||
{
|
||||
Log.Logger.Error($"❌ 设置失败 [错误码: {ex.ErrorCode}] 详细: {ex.Message}");
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error($"❌ 发生未知错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||
|
||||
|
||||
|
||||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
||||
// 1. 获取现有的生命周期配置(避免覆盖)
|
||||
LifecycleConfiguration existingConfig = null;
|
||||
|
||||
var getRequest = new GetLifecycleConfigurationRequest { BucketName = awsConfig.BucketName };
|
||||
var response = await amazonS3Client.GetLifecycleConfigurationAsync(getRequest);
|
||||
existingConfig = response.Configuration;
|
||||
Console.WriteLine($"找到 {existingConfig?.Rules?.Count ?? 0} 条现有规则");
|
||||
|
||||
// 2. 生成唯一的规则ID
|
||||
ruleId = $"{ruleId}_{prefix.Replace('/', '_').Trim('_')}";
|
||||
|
||||
// 3. 创建新的生命周期规则
|
||||
var immediateRule = new Amazon.S3.Model.LifecycleRule
|
||||
{
|
||||
Id = ruleId,
|
||||
Filter = new LifecycleFilter
|
||||
{
|
||||
// 使用前缀筛选对象
|
||||
LifecycleFilterPredicate = new LifecyclePrefixPredicate { Prefix = prefix }
|
||||
},
|
||||
Status = LifecycleRuleStatus.Enabled,
|
||||
// 定义多个转换阶段
|
||||
Transitions = new List<LifecycleTransition>
|
||||
{
|
||||
// 1天后转为低频访问 (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, //创建时间
|
||||
StorageClass = S3StorageClass.GlacierInstantRetrieval // 对应归档(即时检索)
|
||||
}
|
||||
// 如果需要更深的归档,可以继续添加:
|
||||
// new LifecycleTransition { Days = 90, StorageClass = S3StorageClass.GlacierFlexibleRetrieval },
|
||||
// new LifecycleTransition { Days = 180, StorageClass = S3StorageClass.DeepArchive }
|
||||
}
|
||||
// 注意:S3的生命周期规则不支持设置“立即生效(Days=0)”。
|
||||
// 如果要对存量文件立即生效,需要配合其他方法(如批量修改存储类型)。
|
||||
};
|
||||
|
||||
// 4. 更新规则列表(移除同名旧规则,添加新规则)
|
||||
var existingRules = existingConfig.Rules ?? new List<Amazon.S3.Model.LifecycleRule>();
|
||||
existingRules.RemoveAll(r => r.Id == ruleId);
|
||||
|
||||
if (isDelete == false)
|
||||
{
|
||||
existingRules.Add(immediateRule);
|
||||
|
||||
}
|
||||
|
||||
// 5. 提交新的生命周期配置
|
||||
var putRequest = new PutLifecycleConfigurationRequest
|
||||
{
|
||||
BucketName = awsConfig.BucketName,
|
||||
Configuration = new LifecycleConfiguration { Rules = existingRules }
|
||||
};
|
||||
|
||||
await amazonS3Client.PutLifecycleConfigurationAsync(putRequest);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
|
||||
/// 解冻指定前缀下的所有归档/冷归档文件
|
||||
/// </summary>
|
||||
/// <param name="prefix">要解冻的文件前缀</param>
|
||||
/// <param name="restoreDays">解冻后文件保持可读的天数(默认3天)</param>
|
||||
/// <param name="restoreTier">解冻优先级(仅AWS有效)</param>
|
||||
/// <param name="batchSize">批量处理大小(默认100)</param>
|
||||
public async Task RestoreFilesByPrefixAsync(string prefix, int restoreDays = 3, int batchSize = 100)
|
||||
{
|
||||
BackBatchGetToken();
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
var client = new OssClient(
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint,
|
||||
AliyunOSSTempToken.AccessKeyId,
|
||||
AliyunOSSTempToken.AccessKeySecret,
|
||||
AliyunOSSTempToken.SecurityToken
|
||||
);
|
||||
|
||||
var bucketName = aliConfig.BucketName;
|
||||
int totalRestored = 0;
|
||||
int totalSkipped = 0;
|
||||
int totalFailed = 0;
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"开始解冻阿里云OSS文件,前缀: {prefix}");
|
||||
|
||||
var allObjects = new List<OssObjectSummary>();
|
||||
|
||||
// 1. 分页列举文件
|
||||
string nextMarker = null;
|
||||
ObjectListing result = null;
|
||||
|
||||
do
|
||||
{
|
||||
var listRequest = new Aliyun.OSS.ListObjectsRequest(bucketName)
|
||||
{
|
||||
Prefix = prefix,
|
||||
Marker = nextMarker,
|
||||
MaxKeys = batchSize
|
||||
};
|
||||
|
||||
result = client.ListObjects(listRequest);
|
||||
|
||||
allObjects.AddRange(result.ObjectSummaries);
|
||||
|
||||
|
||||
|
||||
nextMarker = result.NextMarker;
|
||||
|
||||
} while (result.IsTruncated);
|
||||
|
||||
// 2️⃣ 并行解冻(控制并发)
|
||||
Parallel.ForEach(
|
||||
allObjects,
|
||||
new ParallelOptions
|
||||
{
|
||||
MaxDegreeOfParallelism = 5 // ⭐ 推荐 5~10
|
||||
},
|
||||
obj =>
|
||||
{
|
||||
// 只处理归档
|
||||
if (obj.StorageClass != StorageClass.Archive.ToString())
|
||||
{
|
||||
Interlocked.Increment(ref totalSkipped);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var restoreRequest = new Aliyun.OSS.RestoreObjectRequest(bucketName, obj.Key)
|
||||
{
|
||||
Days = restoreDays
|
||||
};
|
||||
|
||||
client.RestoreObject(restoreRequest);
|
||||
|
||||
Interlocked.Increment(ref totalRestored);
|
||||
Console.WriteLine($"✅ 提交解冻: {obj.Key}");
|
||||
}
|
||||
catch (OssException ex) when (ex.ErrorCode == "RestoreAlreadyInProgress")
|
||||
{
|
||||
// 已在解冻中,算成功
|
||||
Interlocked.Increment(ref totalSkipped);
|
||||
Console.WriteLine($"⚠️ 已在解冻中: {obj.Key}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Interlocked.Increment(ref totalFailed);
|
||||
Console.WriteLine($"❌ 解冻失败: {obj.Key} - {ex.Message}");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 3. 输出统计结果
|
||||
Console.WriteLine("\n================ 解冻完成 ================");
|
||||
Console.WriteLine($"总计处理: {totalRestored + totalSkipped + totalFailed} 个文件");
|
||||
Console.WriteLine($"成功解冻: {totalRestored} 个");
|
||||
Console.WriteLine($"跳过文件: {totalSkipped} 个 (非归档类型)");
|
||||
Console.WriteLine($"解冻失败: {totalFailed} 个");
|
||||
|
||||
if (totalRestored > 0)
|
||||
{
|
||||
Console.WriteLine($"\n📋 解冻说明:");
|
||||
Console.WriteLine($" • 解冻任务已提交,文件将在后台处理");
|
||||
Console.WriteLine($" • 解冻完成后,文件将保持可读状态 {restoreDays} 天");
|
||||
Console.WriteLine($" • 归档文件约需1分钟,冷归档需数小时");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error($"❌ 阿里云解冻操作失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||
var credentials = new SessionAWSCredentials(
|
||||
AWSTempToken.AccessKeyId,
|
||||
AWSTempToken.SecretAccessKey,
|
||||
AWSTempToken.SessionToken
|
||||
);
|
||||
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region),
|
||||
UseHttp = true,
|
||||
};
|
||||
|
||||
using var client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
||||
var bucketName = awsConfig.BucketName;
|
||||
int totalRestored = 0;
|
||||
int totalSkipped = 0;
|
||||
int totalFailed = 0;
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"开始解冻AWS S3文件,前缀: {prefix}");
|
||||
|
||||
var allObjects = new List<S3Object>();
|
||||
|
||||
// 1. 分页列举文件
|
||||
string continuationToken = null;
|
||||
ListObjectsV2Response response = null;
|
||||
|
||||
do
|
||||
{
|
||||
var listRequest = new ListObjectsV2Request
|
||||
{
|
||||
BucketName = bucketName,
|
||||
Prefix = prefix,
|
||||
ContinuationToken = continuationToken,
|
||||
MaxKeys = batchSize
|
||||
};
|
||||
|
||||
response = await client.ListObjectsV2Async(listRequest);
|
||||
|
||||
allObjects.AddRange(response.S3Objects);
|
||||
|
||||
|
||||
continuationToken = response.NextContinuationToken;
|
||||
|
||||
} while (response.IsTruncated == true);
|
||||
|
||||
// 2️⃣ 并行解冻(控制并发)
|
||||
await Parallel.ForEachAsync(
|
||||
allObjects,
|
||||
new ParallelOptions
|
||||
{
|
||||
MaxDegreeOfParallelism = 5 // ⭐ 推荐 5~10
|
||||
},
|
||||
async (obj, ct) =>
|
||||
{
|
||||
// 只处理归档
|
||||
if (obj.StorageClass != S3StorageClass.Glacier)
|
||||
{
|
||||
Interlocked.Increment(ref totalSkipped);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var restoreRequest = new Amazon.S3.Model.RestoreObjectRequest
|
||||
{
|
||||
BucketName = bucketName,
|
||||
Key = obj.Key,
|
||||
Days = restoreDays,
|
||||
|
||||
};
|
||||
|
||||
await client.RestoreObjectAsync(restoreRequest);
|
||||
|
||||
Interlocked.Increment(ref totalRestored);
|
||||
Console.WriteLine($"✅ 提交解冻: {obj.Key}");
|
||||
}
|
||||
catch (OssException ex) when (ex.ErrorCode == "RestoreAlreadyInProgress")
|
||||
{
|
||||
// 已在解冻中,算成功
|
||||
Interlocked.Increment(ref totalSkipped);
|
||||
Console.WriteLine($"⚠️ 已在解冻中: {obj.Key}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Interlocked.Increment(ref totalFailed);
|
||||
Console.WriteLine($"❌ 解冻失败: {obj.Key} - {ex.Message}");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 3. 输出统计结果
|
||||
Console.WriteLine("\n================ 解冻完成 ================");
|
||||
Console.WriteLine($"总计处理: {totalRestored + totalSkipped + totalFailed} 个文件");
|
||||
Console.WriteLine($"成功解冻: {totalRestored} 个");
|
||||
Console.WriteLine($"跳过文件: {totalSkipped} 个 (非归档类型)");
|
||||
Console.WriteLine($"解冻失败: {totalFailed} 个");
|
||||
|
||||
if (totalRestored > 0)
|
||||
{
|
||||
Console.WriteLine($"\n📋 AWS解冻说明:");
|
||||
Console.WriteLine($" • 解冻任务已提交到Glacier服务");
|
||||
Console.WriteLine($" • 标准解冻: 3-5小时 (Glacier Flexible Retrieval)");
|
||||
Console.WriteLine($" • 加急解冻: 1-5分钟 (额外收费)");
|
||||
Console.WriteLine($" • 解冻后文件可读 {restoreDays} 天");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error($"❌ AWS解冻操作失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 坑方法,会清空之前的规则
|
||||
/// </summary>
|
||||
/// <param name="prefix"></param>
|
||||
/// <param name="ruleId"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||
public async Task SetLifecycle(string prefix, string ruleId = "immediate-archive")
|
||||
{
|
||||
BackBatchGetToken();
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||
|
||||
ruleId = $"{ruleId}_{prefix}";
|
||||
var rule = new Aliyun.OSS.LifecycleRule
|
||||
{
|
||||
ID = ruleId,
|
||||
Prefix = prefix,
|
||||
Status = RuleStatus.Enabled,
|
||||
Transitions = new Aliyun.OSS.LifecycleRule.LifeCycleTransition[]
|
||||
{
|
||||
new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
{
|
||||
|
||||
LifeCycleExpiration =
|
||||
{
|
||||
Days = 1
|
||||
},
|
||||
StorageClass = StorageClass.IA
|
||||
},
|
||||
new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
{
|
||||
|
||||
LifeCycleExpiration =
|
||||
{
|
||||
Days = 30
|
||||
},
|
||||
StorageClass = StorageClass.Archive
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//会清空之前历史的规则,不能用。。。
|
||||
var request = new SetBucketLifecycleRequest(aliConfig.BucketName);
|
||||
request.AddLifecycleRule(rule);
|
||||
|
||||
_ossClient.SetBucketLifecycle(request);
|
||||
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||
|
||||
|
||||
|
||||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||
@@ -249,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);
|
||||
@@ -375,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);
|
||||
@@ -450,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);
|
||||
@@ -504,11 +1020,14 @@ public class OSSService : IOSSService
|
||||
|
||||
var result = _ossClient.GetObject(aliConfig.BucketName, ossRelativePath);
|
||||
|
||||
// 将OSS返回的流复制到内存流中并返回
|
||||
var memoryStream = new MemoryStream();
|
||||
await result.Content.CopyToAsync(memoryStream);
|
||||
memoryStream.Position = 0; // 重置位置以便读取
|
||||
return memoryStream;
|
||||
// 直接返回流
|
||||
return result.Content;
|
||||
|
||||
//// 将OSS返回的流复制到内存流中并返回
|
||||
//var memoryStream = new MemoryStream();
|
||||
//await result.Content.CopyToAsync(memoryStream);
|
||||
//memoryStream.Position = 0; // 重置位置以便读取
|
||||
//return memoryStream;
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
@@ -520,16 +1039,30 @@ public class OSSService : IOSSService
|
||||
.WithSSL(minIOConfig.UseSSL)
|
||||
.Build();
|
||||
|
||||
var memoryStream = new MemoryStream();
|
||||
var pipe = new System.IO.Pipelines.Pipe();
|
||||
|
||||
var getObjectArgs = new GetObjectArgs()
|
||||
.WithBucket(minIOConfig.BucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithCallbackStream(stream => stream.CopyToAsync(memoryStream));
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var args = new GetObjectArgs()
|
||||
.WithBucket(minIOConfig.BucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithCallbackStream(stream =>
|
||||
{
|
||||
stream.CopyTo(pipe.Writer.AsStream());
|
||||
});
|
||||
|
||||
await minioClient.GetObjectAsync(getObjectArgs);
|
||||
memoryStream.Position = 0;
|
||||
return memoryStream;
|
||||
await minioClient.GetObjectAsync(args);
|
||||
await pipe.Writer.CompleteAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await pipe.Writer.CompleteAsync(ex);
|
||||
}
|
||||
});
|
||||
|
||||
return pipe.Reader.AsStream();
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
@@ -557,10 +1090,13 @@ public class OSSService : IOSSService
|
||||
|
||||
var response = await amazonS3Client.GetObjectAsync(getObjectRequest);
|
||||
|
||||
var memoryStream = new MemoryStream();
|
||||
await response.ResponseStream.CopyToAsync(memoryStream);
|
||||
memoryStream.Position = 0;
|
||||
return memoryStream;
|
||||
// ⭐ 直接返回流
|
||||
return response.ResponseStream;
|
||||
|
||||
//var memoryStream = new MemoryStream();
|
||||
//await response.ResponseStream.CopyToAsync(memoryStream);
|
||||
//memoryStream.Position = 0;
|
||||
//return memoryStream;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -636,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);
|
||||
@@ -898,7 +1434,7 @@ public class OSSService : IOSSService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除某个目录的文件
|
||||
/// 删除某个目录的文件 (包含单个文件,oss单个文件需要去除前缀/)
|
||||
/// </summary>
|
||||
/// <param name="prefix"></param>
|
||||
/// <returns></returns>
|
||||
@@ -1010,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);
|
||||
@@ -1025,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
|
||||
@@ -1055,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();
|
||||
|
||||
@@ -1065,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));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1103,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);
|
||||
@@ -1178,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
|
||||
@@ -1263,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
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using RestSharp;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityModel;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper.OtherTool;
|
||||
|
||||
|
||||
public class WeComAlert
|
||||
{
|
||||
public string Env { get; set; } = "";
|
||||
public string UserName { get; set; } = "";
|
||||
public string Api { get; set; } = "";
|
||||
public string Message { get; set; } = "";
|
||||
public string? JsonData { get; set; }
|
||||
public string? Stack { get; set; }
|
||||
public bool HasStack => !string.IsNullOrWhiteSpace(Stack);
|
||||
public string[] AtUsers { get; set; } = [];
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class WeComNotifier
|
||||
{
|
||||
|
||||
public static async Task SendAlertAsync(string webhook, WeComAlert alert)
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = new RestClient();
|
||||
var request = new RestRequest(webhook, Method.Post);
|
||||
|
||||
var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// 处理 @
|
||||
var atText = alert.AtUsers != null && alert.AtUsers.Any()
|
||||
? string.Join("", alert.AtUsers.Select(u => $" <@{u}>"))
|
||||
: "";
|
||||
|
||||
// 处理堆栈
|
||||
var stack = alert.Stack;
|
||||
if (!string.IsNullOrWhiteSpace(stack))
|
||||
{
|
||||
stack = stack.Replace("\n", "\n> ");
|
||||
if (stack.Length > 600)
|
||||
stack = stack[..600] + "...(已截断)";
|
||||
}
|
||||
|
||||
var markdown = $@"## 🚨 系统告警
|
||||
> {atText}
|
||||
> **部署环境:** [{alert.Env}]({alert.Env})
|
||||
> **发生时间:** {time}
|
||||
> **操作人:** {alert.UserName}
|
||||
> **接口地址:** {alert.Api}
|
||||
### ❗ 告警信息
|
||||
<font color=""warning"">{alert.Message}</font>";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(alert.JsonData))
|
||||
{
|
||||
markdown += $@"
|
||||
> **📦 请求数据(JSON 格式):**
|
||||
```json
|
||||
{alert.JsonData}
|
||||
```";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(stack))
|
||||
{
|
||||
markdown += $@"
|
||||
### 堆栈信息(部分)
|
||||
<font color=""comment"">{stack}</font>";
|
||||
}
|
||||
|
||||
var payload = new
|
||||
{
|
||||
msgtype = "markdown",
|
||||
markdown = new { content = markdown }
|
||||
};
|
||||
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
request.AddStringBody(JsonConvert.SerializeObject(payload), DataFormat.Json);
|
||||
|
||||
await client.ExecuteAsync(request);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error("企业微信告警发送失败: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -43,6 +43,10 @@ namespace IRaCIS.Core.Application.MassTransit.Command
|
||||
public DateTime CheckTime { get; set; }
|
||||
|
||||
public string CheckResult { get; set; }
|
||||
|
||||
|
||||
[DictionaryTranslateAttribute("CheckState")]
|
||||
public CheckStateEnum CheckState { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||
|
||||
var noneDicomQuey = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
|
||||
join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id
|
||||
join noneDicomStudy in _noneDicomStudyRepository.AsQueryable() on sv.Id equals noneDicomStudy.SubjectVisitId
|
||||
join noneDicomStudy in _noneDicomStudyRepository.Where(t => t.FileCount > 0) on sv.Id equals noneDicomStudy.SubjectVisitId
|
||||
select new CheckDBModel()
|
||||
{
|
||||
SubjectVisitId = sv.Id,
|
||||
@@ -320,7 +320,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||
join study in _studyRepository.AsQueryable() on sv.Id equals study.SubjectVisitId
|
||||
select new CheckDBModel()
|
||||
{
|
||||
SubjectStatus = sv.Subject.Status,
|
||||
SubjectStatus = sv.Subject.Status,
|
||||
SubjectVisitId = sv.Id,
|
||||
SiteCode = sv.TrialSite.TrialSiteCode,
|
||||
StudyDate = study.StudyTime == null ? string.Empty : ((DateTime)study.StudyTime).ToString("yyyy-MM-dd"),
|
||||
@@ -332,7 +332,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||
|
||||
var noneDicomQuey = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
|
||||
join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id
|
||||
join noneDicomStudy in _noneDicomStudyRepository.AsQueryable() on sv.Id equals noneDicomStudy.SubjectVisitId
|
||||
join noneDicomStudy in _noneDicomStudyRepository.Where(t => t.FileCount > 0) on sv.Id equals noneDicomStudy.SubjectVisitId
|
||||
select new CheckDBModel()
|
||||
{
|
||||
SubjectStatus = sv.Subject.Status,
|
||||
@@ -403,6 +403,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||
{
|
||||
SubjectStatus = dbCurrentVisitFirst.SubjectStatus,
|
||||
CheckTime = DateTime.Now,
|
||||
CheckState=CheckStateEnum.CVPassed,
|
||||
SiteCode = dbCurrentVisitFirst.SiteCode,
|
||||
SubjectCode = dbCurrentVisitFirst.SubjectCode,
|
||||
VisitName = dbCurrentVisitFirst.VisitName,
|
||||
@@ -420,6 +421,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||
fullCheckResultList.Add(new FullCheckResult()
|
||||
{
|
||||
SubjectStatus = dbCurrentVisitFirst.SubjectStatus,
|
||||
CheckState = CheckStateEnum.None,
|
||||
CheckTime = DateTime.Now,
|
||||
SiteCode = dbCurrentVisitFirst.SiteCode,
|
||||
SubjectCode = dbCurrentVisitFirst.SubjectCode,
|
||||
|
||||
@@ -256,7 +256,7 @@ public class ImageConsumer(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送影像重传相关邮件的通用方法
|
||||
/// 发送影像重传相关邮件的通用方法 已经处理项目
|
||||
/// </summary>
|
||||
private async Task SendImageReuploadEmail(SendImageReuploadEmailInDto inDto)
|
||||
{
|
||||
@@ -339,12 +339,12 @@ public class ImageConsumer(
|
||||
var messageToSend = new MimeMessage();
|
||||
// 发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
// 添加抄送
|
||||
foreach (var ccUser in ccUserList)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, ccUser.EMail));
|
||||
messageToSend.Cc.Add(new MailboxAddress(ccUser.FullName, ccUser.EMail));
|
||||
}
|
||||
// 格式化邮件内容
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
@@ -369,8 +369,7 @@ public class ImageConsumer(
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(inDto.EmailNoticeConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,7 +436,7 @@ public class ImageConsumer(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送阅片人筛选相关邮件的通用方法
|
||||
/// 发送阅片人筛选相关邮件的通用方法 已经处理项目
|
||||
/// </summary>
|
||||
private async Task SendReviewerSelectionEmail(EmailNoticeConfig emailNoticeConfig, Guid trialId, List<Guid> enrollIdList)
|
||||
{
|
||||
@@ -475,12 +474,12 @@ public class ImageConsumer(
|
||||
var messageToSend = new MimeMessage();
|
||||
// 发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
// 添加抄送
|
||||
foreach (var ccUser in ccUserList)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, ccUser.EMail));
|
||||
messageToSend.Cc.Add(new MailboxAddress(ccUser.FullName, ccUser.EMail));
|
||||
}
|
||||
|
||||
// 格式化邮件内容
|
||||
@@ -503,8 +502,7 @@ public class ImageConsumer(
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailNoticeConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ public class UrgentMedicalReviewAddedEventConsumer(
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
@@ -127,8 +127,8 @@ public class UrgentMedicalReviewAddedEventConsumer(
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ public class UrgentIRRepliedMedicalReviewConsumer(
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
@@ -235,8 +235,8 @@ public class UrgentIRRepliedMedicalReviewConsumer(
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -308,7 +308,7 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
@@ -353,8 +353,7 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,7 +418,7 @@ public class UrgentIRApplyedReReadingConsumer(
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == taskInfo.TrialId);
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
@@ -462,8 +461,8 @@ public class UrgentIRApplyedReReadingConsumer(
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using DocumentFormat.OpenXml.Vml;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.MassTransit.Command;
|
||||
@@ -55,6 +56,7 @@ public class UserSiteSurveySubmitedEventConsumer(
|
||||
|
||||
if (emailConfig != null)
|
||||
{
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
|
||||
var trialUserList = await _trialUserRoleRepository.Where(t => t.TrialId == siteSurveyInfo.TrialId && t.TrialUser.IsDeleted == false)
|
||||
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM || t.UserRole.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserRole.UserTypeEnum == UserTypeEnum.APM)
|
||||
@@ -67,8 +69,8 @@ public class UserSiteSurveySubmitedEventConsumer(
|
||||
|
||||
var toUserName = string.Empty;
|
||||
|
||||
//有SPM
|
||||
if (sPMOrCPMList.Count > 0)
|
||||
//有SPM 并且参与
|
||||
if (trialInfo.IsSPMJoinSiteSurvey && sPMOrCPMList.Count > 0)
|
||||
{
|
||||
foreach (var user in sPMOrCPMList)
|
||||
{
|
||||
@@ -96,7 +98,6 @@ public class UserSiteSurveySubmitedEventConsumer(
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
|
||||
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
|
||||
|
||||
@@ -121,8 +122,8 @@ public class UserSiteSurveySubmitedEventConsumer(
|
||||
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -204,8 +205,8 @@ public class SiteSurveySPMSubmitedEventConsumer(
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -283,7 +284,7 @@ public class SiteSurverRejectedEventConsumer(
|
||||
{
|
||||
//没有SPM PM驳回到CRC
|
||||
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, siteSurveyInfo.Email));
|
||||
messageToSend.To.Add(new MailboxAddress(siteSurveyInfo.UserName, siteSurveyInfo.Email));
|
||||
}
|
||||
|
||||
//发件地址
|
||||
@@ -318,8 +319,8 @@ public class SiteSurverRejectedEventConsumer(
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -89,14 +89,14 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
|
||||
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
}
|
||||
|
||||
var userNames = userinfoList.Select(x => x.FullName).ToList();
|
||||
|
||||
foreach (var pm in pmandAPm)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
|
||||
messageToSend.Cc.Add(new MailboxAddress(pm.FullName, pm.EMail));
|
||||
}
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
@@ -121,8 +121,8 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -181,11 +181,11 @@ public class CRCRepliedQCChallengeEventConsumer(
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
foreach (var pm in pmandAPm)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
|
||||
messageToSend.Cc.Add(new MailboxAddress(pm.FullName, pm.EMail));
|
||||
}
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
@@ -220,8 +220,8 @@ public class CRCRepliedQCChallengeEventConsumer(
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,10 +307,10 @@ public class QCRepliedQCChallengeEventConsumer(
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
if (craInfo != null)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, craInfo.EMail));
|
||||
messageToSend.Cc.Add(new MailboxAddress(craInfo.FullName, craInfo.EMail));
|
||||
}
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
@@ -338,8 +338,8 @@ public class QCRepliedQCChallengeEventConsumer(
|
||||
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -438,8 +438,8 @@ public class CRCRepliedCheckChallengeEventConsumer(
|
||||
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -517,10 +517,10 @@ public class PMRepliedCheckChallengeEventConsumer(
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
if (craInfo != null)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, craInfo.EMail));
|
||||
messageToSend.Cc.Add(new MailboxAddress(craInfo.FullName, craInfo.EMail));
|
||||
}
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
@@ -548,8 +548,8 @@ public class PMRepliedCheckChallengeEventConsumer(
|
||||
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -620,7 +620,7 @@ public class CheckStateChangedToAuditEventConsumer(
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
@@ -647,8 +647,8 @@ public class CheckStateChangedToAuditEventConsumer(
|
||||
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -707,11 +707,11 @@ public class QCClaimTaskEventConsumer(
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
foreach (var pm in pmandAPm)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
|
||||
messageToSend.Cc.Add(new MailboxAddress(pm.FullName, pm.EMail));
|
||||
}
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
@@ -756,8 +756,8 @@ public class QCClaimTaskEventConsumer(
|
||||
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using IRaCIS.Core.Application.Helper.OtherTool;
|
||||
using IRaCIS.Core.Domain.BaseModel;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class ConsumeExceptionFilter<T>(ILogger<ConsumeExceptionFilter<T>> _logger) : IFilter<ConsumeContext<T>> where T : DomainEvent
|
||||
public class ConsumeExceptionFilter<T>(ILogger<ConsumeExceptionFilter<T>> _logger, IConfiguration _config) : IFilter<ConsumeContext<T>> where T : DomainEvent
|
||||
{
|
||||
|
||||
public async Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next)
|
||||
@@ -23,6 +25,44 @@ public class ConsumeExceptionFilter<T>(ILogger<ConsumeExceptionFilter<T>> _logge
|
||||
{
|
||||
var errorInfo = $"Exception: {exception.Message}[{exception.StackTrace}]" + (exception.InnerException != null ? $" InnerException: {exception.InnerException.Message}[{exception.InnerException.StackTrace}]" : "");
|
||||
_logger.LogError(errorInfo);
|
||||
|
||||
try
|
||||
{
|
||||
bool isOpenWeComNotice = _config.GetValue<bool>("WeComNoticeConfig:IsOpenWeComNotice");
|
||||
|
||||
if (isOpenWeComNotice)
|
||||
{
|
||||
string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty;
|
||||
|
||||
var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]);
|
||||
var baseUrl = uri.GetLeftPart(UriPartial.Authority);
|
||||
|
||||
var userList = _config.GetSection("WeComNoticeConfig:APINoticeUserList").Get<string[]>();
|
||||
|
||||
|
||||
|
||||
// 🔔 异步告警(不要阻塞请求)
|
||||
_ = WeComNotifier.SendAlertAsync(
|
||||
webhook: webhook,
|
||||
alert: new WeComAlert
|
||||
{
|
||||
Env = baseUrl,
|
||||
UserName = "MassTransit 自动触发邮件",
|
||||
Api = "",
|
||||
Message = $"异常信息:{exception.Message} ",
|
||||
Stack = exception.StackTrace,
|
||||
AtUsers = userList ?? []
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error($"MassTransit里发送企业微信出现错误:{ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,12 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
|
||||
public async Task Consume(ConsumeContext<UrgentIRUnReadTaskRecurringEvent> context)
|
||||
{
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
var trialId = context.Message.TrialId;
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
@@ -163,8 +168,8 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
|
||||
//处理标记已通知的任务
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public static class OldRecurringEmailHelper
|
||||
public static string EmailNamePlaceholder => StaticData.EmailSend.EmailNamePlaceholder;
|
||||
|
||||
|
||||
|
||||
//发送项目邮件的通用方法 已经处理
|
||||
public static async Task SendTrialEmailAsync(
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||
IRepository<Trial> _trialRepository,
|
||||
@@ -34,6 +34,7 @@ public static class OldRecurringEmailHelper
|
||||
var trialEmailConfig = await _trialEmailNoticeConfigRepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == businessScenario, ignoreQueryFilters: true)
|
||||
.Include(t => t.TrialEmailNoticeUserList).Include(t => t.TrialEmailBlackUserList).FirstOrDefaultAsync();
|
||||
|
||||
var trialInfo=await _trialRepository.Where(t=>t.Id== trialId).FirstOrDefaultAsync();
|
||||
|
||||
if (trialEmailConfig == null || trialEmailConfig.IsAutoSend == false || trialEmailConfig.IsEnable == false)
|
||||
{
|
||||
@@ -162,7 +163,7 @@ public static class OldRecurringEmailHelper
|
||||
if (sendEmailConfig != null)
|
||||
{
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(sendEmailConfig);
|
||||
await SendEmailHelper.SendEmailAsync(sendEmailConfig, trialInfo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using MassTransit.Scheduling;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -25,14 +26,20 @@ public class QCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepo
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig,
|
||||
IRepository<TrialSiteUserRole> _trialSiteUserRoleRepository,
|
||||
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig) : IConsumer<QCImageQuestionRecurringEvent>
|
||||
{
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
public async Task Consume(ConsumeContext<QCImageQuestionRecurringEvent> context)
|
||||
{
|
||||
var trialId = context.Message.TrialId;
|
||||
|
||||
var isEn_us = false;
|
||||
var isEn_us = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync();
|
||||
|
||||
@@ -64,8 +71,9 @@ public class QCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepo
|
||||
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc = trialEmailConfig =>
|
||||
{
|
||||
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
|
||||
user.FullName, DateTime.Now, sendStat.ToBeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
var htmlContent = isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN;
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent),
|
||||
user.FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), sendStat.ToBeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
|
||||
return (topicStr, htmlBodyStr, isEn_us, userId);
|
||||
};
|
||||
@@ -91,13 +99,20 @@ public class CRCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRep
|
||||
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<TrialSiteUserRole> _trialSiteUserRoleRepository,
|
||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig,
|
||||
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig) : IConsumer<CRCImageQuestionRecurringEvent>
|
||||
{
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
public async Task Consume(ConsumeContext<CRCImageQuestionRecurringEvent> context)
|
||||
{
|
||||
var trialId = context.Message.TrialId;
|
||||
|
||||
var isEn_us = false;
|
||||
var isEn_us = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr, t.DeclarationTypeEnumList }).FirstNotNullAsync();
|
||||
|
||||
//找到 该项目的IQC 用户Id
|
||||
@@ -131,9 +146,10 @@ public class CRCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRep
|
||||
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc = trialEmailConfig =>
|
||||
{
|
||||
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
|
||||
var htmlContent = isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN;
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent),
|
||||
|
||||
user.FullName, DateTime.Now, sendStat.ToBeDealedCount - sendStat.ReUploadTobeDealedCount, sendStat.ReUploadTobeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
user.FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), sendStat.ToBeDealedCount - sendStat.ReUploadTobeDealedCount, sendStat.ReUploadTobeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
|
||||
|
||||
return (topicStr, htmlBodyStr, false, userId);
|
||||
@@ -158,13 +174,20 @@ public class ImageQCRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
||||
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<TrialSiteUserRole> _trialSiteUserRoleRepository,
|
||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig,
|
||||
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig) : IConsumer<ImageQCRecurringEvent>
|
||||
{
|
||||
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
public async Task Consume(ConsumeContext<ImageQCRecurringEvent> context)
|
||||
{
|
||||
var trialId=context.Message.TrialId;
|
||||
|
||||
var isEn_us = false;
|
||||
var isEn_us = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync();
|
||||
|
||||
@@ -199,9 +222,12 @@ public class ImageQCRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
||||
|
||||
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc = trialEmailConfig =>
|
||||
{
|
||||
|
||||
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
|
||||
user.FullName, DateTime.Now, sendStat.ToBeClaimedCount, sendStat.ToBeReviewedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
|
||||
var htmlContent = isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN;
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent),
|
||||
user.FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), sendStat.ToBeClaimedCount, sendStat.ToBeReviewedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
|
||||
return (topicStr, htmlBodyStr, false, userId);
|
||||
};
|
||||
|
||||
@@ -42,7 +42,12 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
|
||||
DateTime now = DateTime.Now;
|
||||
Console.WriteLine("发送定时过期提醒");
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
var systemDocQuery =
|
||||
from sysDoc in _systemDocumentRepository.AsQueryable(false)
|
||||
from identityUser in _identityUserRepository.AsQueryable(false).Where(t => t.UserRoleList.Where(t => t.IsUserRoleDisabled == false).Any(t => sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
|
||||
@@ -89,7 +94,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
|
||||
|
||||
@@ -150,7 +155,11 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
|
||||
public async Task Consume(ConsumeContext<SystemDocumentPublishEvent> context)
|
||||
{
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
// 记录是否只发送给新增角色的日志
|
||||
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
|
||||
@@ -230,7 +239,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Reactive.Joins;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -52,7 +53,11 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
|
||||
DateTime now = DateTime.Now;
|
||||
Console.WriteLine("发送定时项目过期提醒");
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
var trialDocQuery =
|
||||
from trialDoc in _trialDocumentRepository.AsQueryable(true)
|
||||
|
||||
@@ -75,6 +80,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
CurrentStaffTrainDays = trialDoc.CurrentStaffTrainDays,
|
||||
NewStaffTrainDays = trialDoc.NewStaffTrainDays,
|
||||
Id = trialDoc.Id,
|
||||
TrialId= trialDoc.TrialId,
|
||||
IsSystemDoc = false,
|
||||
CreateTime = trialDoc.CreateTime,
|
||||
FullFilePath = trialDoc.Path,
|
||||
@@ -109,10 +115,11 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
{
|
||||
Console.WriteLine($"{index}发送定时过期提醒,邮箱:{userinfo.EMail},姓名{userinfo.UserName}");
|
||||
index++;
|
||||
var trialInfo = _trialRepository.Where(x => x.Id == userinfo.TrialId).FirstOrDefault();
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
|
||||
|
||||
@@ -138,7 +145,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
{
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -162,6 +169,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<TrialDocument> _trialDocumentRepository,
|
||||
IRepository<IdentityUser> _identityUserRepository,
|
||||
IRepository<Trial> _trialRepository,
|
||||
IRepository<TrialIdentityUser> _trialIdentityUserRepository,
|
||||
IRepository<Dictionary> _dictionaryRepository,
|
||||
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||
@@ -173,7 +181,11 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
|
||||
public async Task Consume(ConsumeContext<TrialDocumentPublishEvent> context)
|
||||
{
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
// 记录是否只发送给新增角色的日志
|
||||
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
|
||||
@@ -198,6 +210,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
{
|
||||
IsSystemDoc = true,
|
||||
Id = trialDoc.Id,
|
||||
TrialId= trialDoc.TrialId,
|
||||
CreateTime = trialDoc.CreateTime,
|
||||
IsDeleted = trialDoc.IsDeleted,
|
||||
SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes,
|
||||
@@ -219,7 +232,9 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
from trialDoc in _trialDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
|
||||
join trialIdentityUser in _trialIdentityUserRepository.Where(x => x.IsDeleted == false) on trialDoc.TrialId equals trialIdentityUser.TrialId
|
||||
join trialUserRole in _trialUserRoleRepository.Where(x=>x.IsDeleted==false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
|
||||
join identityUser in _identityUserRepository.AsQueryable(false).Where(u => u.Status == UserStateEnum.Enable)
|
||||
|
||||
join trial in _trialRepository.AsQueryable(false) on trialDoc.TrialId equals trial.Id
|
||||
join identityUser in _identityUserRepository.AsQueryable(false).Where(u => u.Status == UserStateEnum.Enable)
|
||||
on trialIdentityUser.IdentityUserId equals identityUser.Id
|
||||
where trialIdentityUser.TrialUserRoleList.Any(ur => !ur.IsDeleted &&trialDoc.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == ur.UserRole.UserTypeId))
|
||||
|
||||
@@ -228,6 +243,8 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
{
|
||||
IsSystemDoc = false,
|
||||
Id = trialDoc.Id,
|
||||
TrialId = trialDoc.TrialId,
|
||||
EmailFromName =trial.EmailFromName,
|
||||
CreateTime = trialDoc.CreateTime,
|
||||
IsDeleted = trialDoc.IsDeleted,
|
||||
SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes,
|
||||
@@ -242,6 +259,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
FullFilePath = trialDoc.Path
|
||||
};
|
||||
}
|
||||
|
||||
var datalist = await systemDocQuery.IgnoreQueryFilters().ToListAsync();
|
||||
|
||||
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
|
||||
@@ -257,7 +275,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
|
||||
|
||||
@@ -283,7 +301,12 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
{
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
var trial = datalist.Where(x => x.ConfirmUserId == userinfo.Id).FirstOrDefault();
|
||||
|
||||
var trialInfo = await _trialRepository.Where(x=>x.Id==trial.TrialId).FirstNotNullAsync();
|
||||
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
msg += "发送成功";
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ using MassTransit;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Threading.Tasks;
|
||||
using Subject = IRaCIS.Core.Domain.Models.Subject;
|
||||
|
||||
namespace IRaCIS.Core.Application.Service.Allocation;
|
||||
@@ -45,6 +47,8 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<DicomSeries> _dicomSeriesRepository,
|
||||
IRepository<SubjectCanceDoctor> _subjectCanceDoctorRepository,
|
||||
IRepository<ReadingTaskQuestionMark> _readingTaskQuestionMarkRepository,
|
||||
IRepository<ReadingNoneDicomMark> _readingNoneDicomMarkRepository,
|
||||
IRepository<ReadingNoneDicomMarkBinding> _readingNoneDicomMarkBindingRepository,
|
||||
IRepository<ReadingTableAnswerRowInfo> _readingTableAnswerRowInfoRepository,
|
||||
//IRepository<ReadingCustomTag> _readingCustomTagRepository,
|
||||
IRepository<TaskInfluence> _taskInfluenceRepository,
|
||||
@@ -1027,8 +1031,8 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
//满足前序访视不存在 需要签署但是未签署 sql 相当复杂 同时想查询所有未读的统计数字 就无法统计 byzhouhang
|
||||
//但是加字段 IsFrontTaskNeedSignButNotSign 那么签名临床数据的时候,要对该subject 该标准的有效的任务 这个字段需要在签名的时候维护 采取这种方式 统计数字灵活
|
||||
//.Where(t => t.Subject.SubjectVisitTaskList.AsQueryable().Where(visitTaskLambda).Any(c => c.IsNeedClinicalDataSign == true && c.IsClinicalDataSign == false && c.VisitTaskNum < t.VisitTaskNum))
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => (t.Subject.Code.Contains(inQuery.SubjectCode!) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(inQuery.SubjectCode!) && t.IsAnalysisCreate));
|
||||
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => (t.Subject.Code.Contains(inQuery.SubjectCode!) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(inQuery.SubjectCode!) && t.IsAnalysisCreate))
|
||||
;
|
||||
|
||||
var visitGroupQuery = visitQuery.GroupBy(x => new { x.SubjectId, x.Subject.Code, x.BlindSubjectCode });
|
||||
|
||||
@@ -1171,7 +1175,9 @@ 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(!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.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 });
|
||||
|
||||
@@ -2081,7 +2087,9 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
{
|
||||
if (origenalTask.ReadingCategory == ReadingCategory.Visit)
|
||||
{
|
||||
CopyForms(newTask, origenalTask);
|
||||
newTask.IsCopyLesionAnswer = true;
|
||||
|
||||
//CopyForms(newTask, origenalTask);
|
||||
}
|
||||
|
||||
|
||||
@@ -2179,7 +2187,6 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
if (origenalTask.ReadingCategory == ReadingCategory.Visit)
|
||||
{
|
||||
CopyForms(newTask, origenalTask);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2189,7 +2196,8 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
{
|
||||
if (origenalTask.ReadingCategory == ReadingCategory.Visit)
|
||||
{
|
||||
CopyForms(newTask, origenalTask);
|
||||
newTask.IsCopyLesionAnswer = true;
|
||||
//CopyForms(newTask, origenalTask);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2214,7 +2222,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
|
||||
private void CopyForms(VisitTask newTask, VisitTask origenalTask)
|
||||
{
|
||||
|
||||
newTask.IsCopyLesionAnswer = true;
|
||||
//自定义
|
||||
//var readingCustomTagList = _readingCustomTagRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
|
||||
|
||||
@@ -2256,7 +2264,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
|
||||
//ReadingTableAnswerRowInfo ReadingTableQuestionAnswer 一起加
|
||||
var readingTableAnswerRowInfoList = _readingTableAnswerRowInfoRepository.Where(t => t.VisitTaskId == origenalTask.Id).Include(t => t.LesionAnswerList).ToList();
|
||||
|
||||
Dictionary<Guid, Guid> lesionRelationship = new Dictionary<Guid, Guid>() { };
|
||||
foreach (var item in readingTableAnswerRowInfoList)
|
||||
{
|
||||
|
||||
@@ -2264,7 +2272,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
var originalFristAddTaskId = item.FristAddTaskId;
|
||||
|
||||
var newRowId = NewId.NextSequentialGuid();
|
||||
|
||||
lesionRelationship.Add(item.Id, newRowId);
|
||||
foreach (var mark in readingTaskQuestionMarkList)
|
||||
{
|
||||
mark.RowId = mark.RowId == item.Id ? newRowId : mark.RowId;
|
||||
@@ -2286,13 +2294,54 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in readingTableAnswerRowInfoList)
|
||||
{
|
||||
if (item.SplitRowId != null && lesionRelationship.ContainsKey(item.SplitRowId.Value))
|
||||
{
|
||||
item.SplitRowId = lesionRelationship[item.SplitRowId.Value];
|
||||
}
|
||||
|
||||
if (item.MergeRowId != null && lesionRelationship.ContainsKey(item.MergeRowId.Value))
|
||||
{
|
||||
item.MergeRowId = lesionRelationship[item.MergeRowId.Value];
|
||||
}
|
||||
}
|
||||
|
||||
_ = _readingTaskQuestionMarkRepository.AddRangeAsync(readingTaskQuestionMarkList).Result;
|
||||
_ = _readingTableAnswerRowInfoRepository.AddRangeAsync(readingTableAnswerRowInfoList).Result;
|
||||
|
||||
|
||||
var noneMarkList = _readingNoneDicomMarkRepository.Where(x => x.VisitTaskId == origenalTask.Id).ToList();
|
||||
|
||||
var noneMarkBindingList = _readingNoneDicomMarkBindingRepository.Where(x => x.VisitTaskId == origenalTask.Id).ToList();
|
||||
|
||||
|
||||
foreach (var item in noneMarkList)
|
||||
{
|
||||
var newid = NewId.NextSequentialGuid();
|
||||
|
||||
item.MeasureData = item.MeasureData.Replace(origenalTask.Id.ToString(), newTask.Id.ToString());
|
||||
item.VisitTaskId = newTask.Id;
|
||||
foreach (var item1 in noneMarkBindingList)
|
||||
{
|
||||
if (item1.NoneDicomMarkId == item.Id)
|
||||
{
|
||||
item1.NoneDicomMarkId = newid;
|
||||
}
|
||||
}
|
||||
|
||||
item.Id = newid;
|
||||
}
|
||||
|
||||
|
||||
foreach (var item in noneMarkBindingList)
|
||||
{
|
||||
item.Id = NewId.NextSequentialGuid();
|
||||
item.VisitTaskId = newTask.Id;
|
||||
}
|
||||
|
||||
_ = _readingNoneDicomMarkRepository.AddRangeAsync(noneMarkList).Result;
|
||||
_ = _readingNoneDicomMarkBindingRepository.AddRangeAsync(noneMarkBindingList).Result;
|
||||
}
|
||||
|
||||
|
||||
@@ -2385,6 +2434,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();
|
||||
|
||||
@@ -2542,9 +2595,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()
|
||||
@@ -2706,6 +2762,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,22 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
namespace IRaCIS.Core.Application.ViewModel;
|
||||
|
||||
|
||||
public class GetReSendEmailInDto : PageInput
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
|
||||
public DateTime? EmailStartDate { get; set; }
|
||||
|
||||
public DateTime? EmailEndDate { get; set; }
|
||||
|
||||
public EmailState? EmailStateEnum { get; set; }
|
||||
|
||||
public string ToRecipientName { get; set; } = string.Empty;
|
||||
|
||||
public string CcRecipientName { get; set; } = string.Empty;
|
||||
}
|
||||
public class EmailLogView : EmailLogAddOrEdit
|
||||
{
|
||||
|
||||
@@ -125,12 +141,27 @@ public class GetEmailInfoOutDto:EmailLogView
|
||||
public class GetEmailInfoInDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid? TrialId { get; set; }
|
||||
}
|
||||
|
||||
public class SynchronizationEmailInDto
|
||||
{
|
||||
public Guid? TrialId { get; set; }
|
||||
}
|
||||
|
||||
public class ResendEmailInDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid? TrialId { get; set; }
|
||||
}
|
||||
|
||||
public class EmailAuthorization
|
||||
{
|
||||
public string FromEmail { get; set; } = string.Empty;
|
||||
|
||||
public string AuthorizationCode { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class EmailLogQuery:PageInput
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
// 生成时间 2025-10-28 06:22:42Z
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
||||
//--------------------------------------------------------------------
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using IdentityModel.Client;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.Interfaces;
|
||||
using IRaCIS.Core.Application.ViewModel;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using IdentityModel.Client;
|
||||
using MailKit;
|
||||
using MailKit.Net.Imap;
|
||||
using MailKit.Search;
|
||||
@@ -19,8 +20,10 @@ using MassTransit;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
using Panda.DynamicWebApi.Attributes;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Mail;
|
||||
using System.Threading.Tasks;
|
||||
namespace IRaCIS.Core.Application.Service;
|
||||
@@ -38,6 +41,8 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||
IRepository<Trial> _trialRepository,
|
||||
IRepository<EmailAttachmentLog> _emailAttachmentLogRepository,
|
||||
IOSSService oSSService,
|
||||
IRepository<EmailReSendLog> _emailReSendLog,
|
||||
|
||||
IRepository<EmailRecipientLog> _emailRecipientLogRepository,
|
||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig,
|
||||
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer): BaseService, IEmailLogService
|
||||
@@ -75,6 +80,33 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取重发信息
|
||||
/// </summary>
|
||||
/// <param name="inDto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<PageOutput<EmailLogView>> GetReSendEmail(GetReSendEmailInDto inDto)
|
||||
{
|
||||
var messageId = await _emailLogRepository.Where(x => x.Id == inDto.Id).Select(x => x.MessageId).FirstOrDefaultAsync();
|
||||
|
||||
var reSendMessagelist = await _emailReSendLog.Where(x => x.MainMailMessageId == messageId).Select(x => x.ReMailMessageId).ToListAsync();
|
||||
var emailLogQueryable = _emailLogRepository
|
||||
|
||||
.Where(x => reSendMessagelist.Contains(x.MessageId))
|
||||
.WhereIf(inDto.EmailStartDate.HasValue, x => x.EmailDate >= inDto.EmailStartDate.Value)
|
||||
.WhereIf(inDto.EmailEndDate.HasValue, x => x.EmailDate <= inDto.EmailEndDate.Value)
|
||||
.WhereIf(inDto.EmailStateEnum.HasValue, x => x.EmailStateEnum == inDto.EmailStateEnum.Value)
|
||||
.WhereIf(inDto.CcRecipientName.IsNotNullOrEmpty(), x => x.EmailRecipientLogList.Any(x => x.RecipientTypeEnum == RecipientType.Cc && x.RecipientName.Contains(inDto.CcRecipientName)))
|
||||
.WhereIf(inDto.ToRecipientName.IsNotNullOrEmpty(), x => x.EmailRecipientLogList.Any(x => x.RecipientTypeEnum == RecipientType.To && x.RecipientName.Contains(inDto.ToRecipientName)))
|
||||
.ProjectTo<EmailLogView>(_mapper.ConfigurationProvider);
|
||||
|
||||
var defalutSortArray = new string[] { nameof(EmailLogView.EmailDate) + " desc" };
|
||||
var pageList = await emailLogQueryable.ToPagedListAsync(inDto, defalutSortArray);
|
||||
|
||||
return pageList;
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取单条邮件日志详情
|
||||
/// </summary>
|
||||
@@ -95,8 +127,7 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||
{
|
||||
try
|
||||
{
|
||||
client.Connect(_systemEmailConfig.Imap, 993, SecureSocketOptions.SslOnConnect);
|
||||
AuthenticateImap(client);
|
||||
await AuthenticateImap(client,inDto.TrialId);
|
||||
var sentFolder = OpenSentFolder(client);
|
||||
var uid = new UniqueId(uint.Parse(emailInfo.UniqueId));
|
||||
var message = sentFolder.GetMessage(uid);
|
||||
@@ -181,8 +212,7 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||
{
|
||||
try
|
||||
{
|
||||
client.Connect(_systemEmailConfig.Imap, 993, SecureSocketOptions.SslOnConnect);
|
||||
AuthenticateImap(client);
|
||||
await AuthenticateImap(client,inDto.TrialId);
|
||||
var sentFolder = OpenSentFolder(client);
|
||||
var uid = new UniqueId(uint.Parse(emailInfo.UniqueId));
|
||||
var message = sentFolder.GetMessage(uid);
|
||||
@@ -222,7 +252,41 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||
|
||||
messageToSend.Body = builder.ToMessageBody();
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
SystemEmailSendConfig sendConfig = new SystemEmailSendConfig()
|
||||
{
|
||||
};
|
||||
|
||||
if (inDto.TrialId != null)
|
||||
{
|
||||
sendConfig = await _trialRepository.Where(x => x.Id == inDto.TrialId.Value).Select(x => new SystemEmailSendConfig()
|
||||
{
|
||||
Host = _systemEmailConfig.Host,
|
||||
Port = _systemEmailConfig.Port,
|
||||
FromEmail = x.EmailFromEmail,
|
||||
AuthorizationCode = x.EmailAuthorizationCode
|
||||
}).FirstNotNullAsync();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
sendConfig = _systemEmailConfig;
|
||||
}
|
||||
|
||||
var msgid= await SendEmailHelper.SendEmailAsync(messageToSend, sendConfig);
|
||||
|
||||
|
||||
await _emailReSendLog.AddAsync(new EmailReSendLog()
|
||||
{
|
||||
MainMailMessageId= emailInfo.MessageId,
|
||||
ReMailMessageId= msgid
|
||||
});
|
||||
|
||||
await _emailReSendLog.SaveChangesAsync();
|
||||
|
||||
await SynchronizationEmail(new SynchronizationEmailInDto() {
|
||||
|
||||
TrialId= inDto.TrialId
|
||||
});
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
}
|
||||
@@ -232,34 +296,39 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<IResponseOutput> SynchronizationEmail()
|
||||
public async Task<IResponseOutput> SynchronizationEmail(SynchronizationEmailInDto inDto)
|
||||
{
|
||||
|
||||
var maxTime=await _emailLogRepository.MaxAsync(t => t.EmailDate);
|
||||
var fromEmail = _systemEmailConfig.FromEmail;
|
||||
|
||||
|
||||
if (inDto.TrialId != null)
|
||||
{
|
||||
fromEmail= await _trialRepository.Where(x => x.Id == inDto.TrialId.Value).Select(x => x.EmailFromEmail).FirstNotNullAsync();
|
||||
|
||||
}
|
||||
|
||||
var maxTime = await _emailLogRepository.Where(x=>x.SenderAddress== fromEmail).MaxAsync(t => t.EmailDate);
|
||||
var startDate = maxTime ?? DateTime.MinValue;
|
||||
List<EmailLog> emailList = new List<EmailLog>();
|
||||
List<EmailRecipientLog> EmailRecipientLogList = new List<EmailRecipientLog>();
|
||||
|
||||
// 第一步:同步发件箱邮件
|
||||
using (var client = new ImapClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
client.Connect(_systemEmailConfig.Imap, 993, SecureSocketOptions.SslOnConnect);
|
||||
AuthenticateImap(client);
|
||||
|
||||
await AuthenticateImap(client, inDto.TrialId);
|
||||
var sentFolder = OpenSentFolder(client);
|
||||
|
||||
var startDate = maxTime ?? DateTime.MinValue;
|
||||
var searchQuery = SearchQuery.All.And(SearchQuery.DeliveredAfter(startDate));
|
||||
var uids = sentFolder.Search(searchQuery).ToList();
|
||||
|
||||
|
||||
foreach (var uid in uids)
|
||||
{
|
||||
try
|
||||
{
|
||||
//var message = sentFolder.GetMessage(uid);
|
||||
// var emailView = ConvertToEmailLogView(uid, message);
|
||||
|
||||
var message = sentFolder.GetMessage(uid);
|
||||
if (message.Date.DateTime <= startDate)
|
||||
{
|
||||
@@ -267,7 +336,7 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||
}
|
||||
var emaillog = new EmailLog
|
||||
{
|
||||
Id=NewId.NextGuid(),
|
||||
Id = NewId.NextGuid(),
|
||||
UniqueId = uid.ToString(),
|
||||
MessageId = message.MessageId ?? string.Empty,
|
||||
EmailSubject = message.Subject ?? string.Empty,
|
||||
@@ -282,7 +351,7 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||
emaillog.SenderName = fromMailbox.Name ?? string.Empty;
|
||||
}
|
||||
|
||||
List<EmailRecipientLog> recipientLogs= new List<EmailRecipientLog>();
|
||||
List<EmailRecipientLog> recipientLogs = new List<EmailRecipientLog>();
|
||||
|
||||
int sort = 0;
|
||||
message.To.Mailboxes.ForEach(x =>
|
||||
@@ -318,22 +387,37 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sentFolder.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"同步发件箱邮件时出错: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.Disconnect(true);
|
||||
}
|
||||
|
||||
await _emailLogRepository.AddRangeAsync(emailList);
|
||||
await _emailRecipientLogRepository.AddRangeAsync(EmailRecipientLogList);
|
||||
await _emailLogRepository.SaveChangesAsync();
|
||||
return ResponseOutput.Ok();
|
||||
}
|
||||
|
||||
// 保存同步的发件箱邮件
|
||||
await _emailLogRepository.AddRangeAsync(emailList);
|
||||
await _emailRecipientLogRepository.AddRangeAsync(EmailRecipientLogList);
|
||||
await _emailLogRepository.SaveChangesAsync();
|
||||
|
||||
// 第二步:处理收件箱中的邮件发送失败通知
|
||||
try
|
||||
{
|
||||
await ProcessInboxFailureNotifications(startDate,inDto.TrialId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"处理收件箱失败通知邮件时出错: {ex.Message}");
|
||||
Console.WriteLine($"异常堆栈: {ex.StackTrace}");
|
||||
// 即使处理失败通知邮件出错,也不影响整体同步操作的成功
|
||||
// 记录警告但不中断主流程
|
||||
}
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
}
|
||||
|
||||
private IMailFolder OpenSentFolder(ImapClient client)
|
||||
@@ -391,11 +475,81 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||
return folder;
|
||||
}
|
||||
|
||||
private void AuthenticateImap(ImapClient client)
|
||||
private IMailFolder OpenInboxFolder(ImapClient client)
|
||||
{
|
||||
IMailFolder folder = null;
|
||||
try
|
||||
{
|
||||
folder = client.GetFolder(SpecialFolder.All);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (folder == null)
|
||||
{
|
||||
var candidates = new[] { "收件箱", "Inbox" };
|
||||
foreach (var name in candidates)
|
||||
{
|
||||
try
|
||||
{
|
||||
var f = client.GetFolder(name);
|
||||
if (f != null)
|
||||
{
|
||||
folder = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
if (folder == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var personal = client.GetFolder(client.PersonalNamespaces.FirstOrDefault());
|
||||
if (personal != null)
|
||||
{
|
||||
foreach (var sub in personal.GetSubfolders(false))
|
||||
{
|
||||
if (string.Equals(sub.FullName, "Inbox", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
folder = sub;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (folder == null)
|
||||
throw new InvalidOperationException("未找到收件箱文件夹");
|
||||
|
||||
folder.Open(FolderAccess.ReadOnly);
|
||||
return folder;
|
||||
}
|
||||
|
||||
private async Task AuthenticateImap(ImapClient client, Guid? trialId)
|
||||
{
|
||||
try
|
||||
{
|
||||
client.Authenticate(_systemEmailConfig.FromEmail, _systemEmailConfig.AuthorizationCode);
|
||||
EmailAuthorization authorization = new EmailAuthorization()
|
||||
{
|
||||
FromEmail = _systemEmailConfig.FromEmail,
|
||||
AuthorizationCode = _systemEmailConfig.AuthorizationCode,
|
||||
};
|
||||
|
||||
if (trialId != null)
|
||||
{
|
||||
authorization = await _trialRepository.Where(x => x.Id == trialId.Value).Select(x => new EmailAuthorization()
|
||||
{
|
||||
AuthorizationCode = x.EmailAuthorizationCode,
|
||||
FromEmail = x.EmailFromEmail
|
||||
}).FirstNotNullAsync();
|
||||
}
|
||||
|
||||
client.Connect(_systemEmailConfig.Imap, _systemEmailConfig.ImapPort, SecureSocketOptions.SslOnConnect);
|
||||
client.Authenticate(authorization.FromEmail, authorization.AuthorizationCode);
|
||||
}
|
||||
catch (AuthenticationException)
|
||||
{
|
||||
@@ -420,254 +574,173 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||
}
|
||||
}
|
||||
|
||||
//private string AcquireOAuth2TokenByPassword()
|
||||
//{
|
||||
// if (_systemEmailConfig.OAuthTenantId.IsNullOrEmpty() || _systemEmailConfig.OAuthClientId.IsNullOrEmpty())
|
||||
// return string.Empty;
|
||||
|
||||
// using var http = new HttpClient();
|
||||
// var req = new PasswordTokenRequest
|
||||
// {
|
||||
// Address = $"https://login.microsoftonline.com/{_systemEmailConfig.OAuthTenantId}/oauth2/v2.0/token",
|
||||
// ClientId = _systemEmailConfig.OAuthClientId,
|
||||
// ClientSecret = _systemEmailConfig.OAuthClientSecret,
|
||||
// Scope = "offline_access https://outlook.office365.com/IMAP.AccessAsUser.All",
|
||||
// UserName = _systemEmailConfig.FromEmail,
|
||||
// Password = _systemEmailConfig.AuthorizationCode
|
||||
// };
|
||||
// var resp = http.RequestPasswordTokenAsync(req).GetAwaiter().GetResult();
|
||||
// if (resp.IsError || resp.AccessToken.IsNullOrEmpty())
|
||||
// return string.Empty;
|
||||
// return resp.AccessToken;
|
||||
//}
|
||||
|
||||
// 取skip的值
|
||||
public int CalcReverseSkip(int pageIndex, int pageSize, int totalCount)
|
||||
{
|
||||
|
||||
int lastPageFullSkip = totalCount / pageSize * pageSize; // 最后一页“整页”起点
|
||||
int skip = lastPageFullSkip - pageIndex * pageSize; // 倒着减
|
||||
return Math.Max(skip, 0); // 防止负值
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 邮件筛选条件构建
|
||||
/// 处理收件箱中的邮件发送失败通知
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
private SearchQuery BuildSearchQuery(EmailLogQuery query)
|
||||
public async Task ProcessInboxFailureNotifications(DateTime startTime, Guid? trialId)
|
||||
{
|
||||
var searchQuery = SearchQuery.All;
|
||||
|
||||
// 日期范围筛选
|
||||
if (query.EmailStartDate.HasValue || query.EmailEndDate.HasValue)
|
||||
{
|
||||
var startDate = query.EmailStartDate ?? DateTime.MinValue;
|
||||
var endDate = query.EmailEndDate ?? DateTime.MaxValue;
|
||||
|
||||
searchQuery = searchQuery.And(SearchQuery.DeliveredAfter(startDate)
|
||||
.And(SearchQuery.DeliveredBefore(endDate.AddDays(1)))); // 包含结束日期当天
|
||||
}
|
||||
|
||||
// 主题关键词筛选(如果IMAP服务器支持)
|
||||
if (!string.IsNullOrEmpty(query.ToRecipientName))
|
||||
{
|
||||
searchQuery = searchQuery.And(SearchQuery.HeaderContains("To", query.ToRecipientName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(query.CcRecipientName))
|
||||
{
|
||||
searchQuery = searchQuery.And(SearchQuery.HeaderContains("Cc", query.CcRecipientName));
|
||||
}
|
||||
|
||||
return searchQuery;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 邮件排序处理
|
||||
/// </summary>
|
||||
/// <param name="sentFolder"></param>
|
||||
/// <param name="uids"></param>
|
||||
/// <returns></returns>
|
||||
private List<EmailLogView> GetEmailsWithSorting(IMailFolder sentFolder, IList<UniqueId> uids)
|
||||
{
|
||||
var emailList = new List<EmailLogView>();
|
||||
|
||||
|
||||
|
||||
// 获取完整的邮件信息
|
||||
foreach (var uid in uids)
|
||||
using (var client = new ImapClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
var message = sentFolder.GetMessage(uid);
|
||||
var emailView = ConvertToEmailLogView(uid, message);
|
||||
emailList.Add(emailView);
|
||||
|
||||
await AuthenticateImap(client, trialId);
|
||||
var inboxFolder = OpenInboxFolder(client);
|
||||
|
||||
// 搜索可能包含失败通知的邮件
|
||||
// 通常失败通知邮件会包含"失败"、"错误"、"undeliverable"、"delivery failed"等关键词
|
||||
var searchQuery = SearchQuery.All.Or(SearchQuery.SubjectContains("失败"))
|
||||
.Or(SearchQuery.SubjectContains("错误"))
|
||||
.Or(SearchQuery.SubjectContains("undeliverable"))
|
||||
.Or(SearchQuery.SubjectContains("delivery failed"))
|
||||
.Or(SearchQuery.SubjectContains("Delivery Status Notification"))
|
||||
.Or(SearchQuery.BodyContains("失败"))
|
||||
.Or(SearchQuery.BodyContains("错误"))
|
||||
.Or(SearchQuery.BodyContains("undeliverable"))
|
||||
.Or(SearchQuery.BodyContains("delivery failed")).And(SearchQuery.DeliveredAfter(startTime));
|
||||
|
||||
var uids = inboxFolder.Search(searchQuery).ToList();
|
||||
var processedCount = 0;
|
||||
var updatedCount = 0;
|
||||
|
||||
foreach (var uid in uids)
|
||||
{
|
||||
try
|
||||
{
|
||||
var message = inboxFolder.GetMessage(uid);
|
||||
|
||||
// 尝试从邮件内容中提取原始邮件的Message-Id或相关信息
|
||||
var originalMessageId = ExtractOriginalMessageId(message);
|
||||
if (!string.IsNullOrEmpty(originalMessageId))
|
||||
{
|
||||
// 查找对应的EmailLog记录
|
||||
var emailLog = await _emailLogRepository
|
||||
.Where(x => x.MessageId == originalMessageId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
await _emailLogRepository.BatchUpdateNoTrackingAsync(x => x.MessageId == originalMessageId, x => new EmailLog()
|
||||
{
|
||||
EmailStateEnum = EmailState.Error,
|
||||
ErrorInfo = message.TextBody
|
||||
|
||||
});
|
||||
|
||||
updatedCount++;
|
||||
}
|
||||
|
||||
processedCount++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inboxFolder.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"处理邮件 {uid} 失败: {ex.Message}");
|
||||
Console.WriteLine($"连接收件箱处理失败通知邮件时出错: {ex.Message}");
|
||||
Console.WriteLine($"异常堆栈: {ex.StackTrace}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
client.Disconnect(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"断开IMAP客户端连接时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return emailList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取完整的邮件信息
|
||||
/// 从失败通知邮件中提取原始邮件的Message-Id
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
private EmailLogView ConvertToEmailLogView(UniqueId uid, MimeMessage message)
|
||||
private string ExtractOriginalMessageId(MimeMessage message)
|
||||
{
|
||||
string saveFolder = @"D:\MailAttachments";
|
||||
Directory.CreateDirectory(saveFolder);
|
||||
var emailView = new EmailLogView
|
||||
{
|
||||
UniqueId = uid.ToString(),
|
||||
MessageId = message.MessageId ?? string.Empty,
|
||||
EmailSubject = message.Subject ?? string.Empty,
|
||||
EmailDate = message.Date.UtcDateTime,
|
||||
Content = message.HtmlBody ?? string.Empty,
|
||||
};
|
||||
// 首先检查邮件头中是否有原始Message-Id
|
||||
var originalMessageId = message.Headers["Original-Message-ID"] ??
|
||||
message.Headers["X-Original-Message-ID"] ??
|
||||
message.Headers["In-Reply-To"];
|
||||
|
||||
var toHeader = message.Headers[HeaderId.To]; // 原始头,未解码
|
||||
Console.WriteLine($"原始To头 = |{toHeader}|");
|
||||
|
||||
foreach (var att in message.Attachments)
|
||||
if (!string.IsNullOrEmpty(originalMessageId))
|
||||
{
|
||||
// att 可能是 MimePart 或 MessagePart
|
||||
if (att is MimePart part && !string.IsNullOrEmpty(part.FileName))
|
||||
// 清理Message-Id格式(移除尖括号)
|
||||
originalMessageId = originalMessageId.Trim('<', '>');
|
||||
return originalMessageId;
|
||||
}
|
||||
|
||||
// 检查邮件附件中是否包含.eml文件(这是最常见的失败通知格式)
|
||||
foreach (var attachment in message.Attachments)
|
||||
{
|
||||
if (attachment is MessagePart messagePart)
|
||||
{
|
||||
var file = Path.Combine(saveFolder, part.FileName);
|
||||
using (var stream = File.Create(file))
|
||||
part.Content.DecodeTo(stream);
|
||||
Console.WriteLine($" 附件已保存:{file}");
|
||||
// 直接获取嵌入的邮件消息
|
||||
var originalMessageIdFromAttachment = messagePart.Message?.MessageId;
|
||||
if (!string.IsNullOrEmpty(originalMessageIdFromAttachment))
|
||||
{
|
||||
return originalMessageIdFromAttachment.Trim('<', '>');
|
||||
}
|
||||
}
|
||||
else if (attachment is MimePart mimePart)
|
||||
{
|
||||
// 检查文件扩展名是否为.eml
|
||||
var fileName = mimePart.FileName ?? mimePart.ContentType.Name;
|
||||
if (!string.IsNullOrEmpty(fileName) &&
|
||||
(fileName.EndsWith(".eml", StringComparison.OrdinalIgnoreCase) ||
|
||||
mimePart.ContentType.MimeType.Equals("message/rfc822", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
try
|
||||
{
|
||||
// 解析.eml附件内容
|
||||
using var memoryStream = new MemoryStream();
|
||||
mimePart.Content.DecodeTo(memoryStream);
|
||||
memoryStream.Position = 0;
|
||||
|
||||
var originalMessage = MimeMessage.Load(memoryStream);
|
||||
if (!string.IsNullOrEmpty(originalMessage?.MessageId))
|
||||
{
|
||||
return originalMessage.MessageId.Trim('<', '>');
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"解析.eml附件时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理发件人信息
|
||||
var fromMailbox = message.From.Mailboxes.FirstOrDefault();
|
||||
if (fromMailbox != null)
|
||||
// 如果邮件头和附件中都没有,尝试从邮件正文中提取
|
||||
var content = message.HtmlBody ?? message.TextBody ?? string.Empty;
|
||||
|
||||
// 尝试匹配常见的Message-Id格式
|
||||
var messageIdPattern = @"(?:Message-ID|Message-Id|message-id):\s*<?([^>\s]+)>?";
|
||||
var match = System.Text.RegularExpressions.Regex.Match(content, messageIdPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
|
||||
if (match.Success)
|
||||
{
|
||||
emailView.SenderAddress = fromMailbox.Address;
|
||||
emailView.SenderName = fromMailbox.Name ?? string.Empty;
|
||||
return match.Groups[1].Value;
|
||||
}
|
||||
|
||||
//// 处理收件人
|
||||
//emailView.ToRecipients = message.To.Mailboxes.Select(address => new EmaliSendInfo
|
||||
//{
|
||||
// Name = address.Name ?? string.Empty,
|
||||
// Address = address.Address
|
||||
//}).ToList();
|
||||
// 尝试从邮件引用部分提取
|
||||
var referencePattern = @"^>.*Message-ID:\s*<?([^>\s]+)>?";
|
||||
var matches = System.Text.RegularExpressions.Regex.Matches(content, referencePattern, System.Text.RegularExpressions.RegexOptions.Multiline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
|
||||
if (matches.Count > 0)
|
||||
{
|
||||
return matches[0].Groups[1].Value;
|
||||
}
|
||||
|
||||
//// 处理抄送人
|
||||
//emailView.CcRecipients = message.Cc.Mailboxes.Select(address => new EmaliSendInfo
|
||||
//{
|
||||
// Name = address.Name ?? string.Empty,
|
||||
// Address = address.Address
|
||||
//}).ToList();
|
||||
|
||||
//// 处理密送人
|
||||
//emailView.BccRecipients = message.Bcc.Mailboxes.Select(address => new EmaliSendInfo
|
||||
//{
|
||||
// Name = address.Name ?? string.Empty,
|
||||
// Address = address.Address
|
||||
//}).ToList();
|
||||
|
||||
// 处理附件
|
||||
//emailView.Attachments = ProcessAttachments(message);
|
||||
//emailView.HasAttachments = emailView.Attachments.Any();
|
||||
//emailView.AttachmentCount = emailView.Attachments.Count;
|
||||
|
||||
return emailView;
|
||||
return null;
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 处理附件信息
|
||||
///// </summary>
|
||||
///// <param name="message"></param>
|
||||
///// <returns></returns>
|
||||
//private List<EmailAttachmentInfo> ProcessAttachments(MimeMessage message)
|
||||
//{
|
||||
// var attachments = new List<EmailAttachmentInfo>();
|
||||
|
||||
// foreach (var attachment in message.Attachments)
|
||||
// {
|
||||
// var attachmentInfo = new EmailAttachmentInfo();
|
||||
|
||||
// if (attachment is MimePart mimePart)
|
||||
// {
|
||||
// attachmentInfo.FileName = mimePart.FileName ??
|
||||
// mimePart.ContentType?.Name ??
|
||||
// "未知文件";
|
||||
// attachmentInfo.ContentType = mimePart.ContentType.MimeType;
|
||||
// attachmentInfo.ContentId = mimePart.ContentId ?? string.Empty;
|
||||
// attachmentInfo.IsInline = mimePart.ContentDisposition != null &&
|
||||
// mimePart.ContentDisposition.Disposition.Equals("inline", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// // 获取附件大小
|
||||
// try
|
||||
// {
|
||||
// if (mimePart.ContentObject.Stream != null)
|
||||
// {
|
||||
// attachmentInfo.Size = mimePart.ContentObject.Stream.Length;
|
||||
// }
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// attachmentInfo.Size = 0;
|
||||
// }
|
||||
// }
|
||||
// else if (attachment is MessagePart messagePart)
|
||||
// {
|
||||
// // 处理内嵌消息作为附件的情况
|
||||
// attachmentInfo.FileName = "embedded-message.eml";
|
||||
// attachmentInfo.ContentType = "message/rfc822";
|
||||
// attachmentInfo.IsInline = false;
|
||||
// }
|
||||
|
||||
// attachments.Add(attachmentInfo);
|
||||
// }
|
||||
|
||||
// return attachments;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 应用内存中的过滤条件(对于无法通过IMAP搜索的条件)
|
||||
/// </summary>
|
||||
/// <param name="emails"></param>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
private List<EmailLogView> ApplyMemoryFilters(List<EmailLogView> emails, EmailLogQuery query)
|
||||
{
|
||||
var filtered = emails.AsEnumerable();
|
||||
|
||||
// 收件人姓名筛选
|
||||
//if (!string.IsNullOrEmpty(query.Keyword))
|
||||
//{
|
||||
// filtered = filtered.Where(e =>
|
||||
// e.ToRecipients.Any(r =>
|
||||
// r.Name.Contains(query.Keyword, StringComparison.OrdinalIgnoreCase)) ||
|
||||
// e.CcRecipients.Any(r =>
|
||||
// r.Name.Contains(query.Keyword, StringComparison.OrdinalIgnoreCase))
|
||||
|
||||
// );
|
||||
//}
|
||||
|
||||
// 发件人姓名筛选
|
||||
//if (!string.IsNullOrEmpty(query.SenderName))
|
||||
//{
|
||||
// filtered = filtered.Where(e =>
|
||||
// e.SenderName.Contains(query.SenderName, StringComparison.OrdinalIgnoreCase));
|
||||
//}
|
||||
|
||||
return filtered.ToList();
|
||||
}
|
||||
|
||||
|
||||
public async Task<IResponseOutput> AddOrUpdateEmailLog(EmailLogAddOrEdit addOrEditEmailLog)
|
||||
|
||||
@@ -10,12 +10,13 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.Service.Common;
|
||||
|
||||
|
||||
public class TumorCommonQustionInfo
|
||||
{
|
||||
//问题标识,肿瘤评估用于区分是什么问题
|
||||
public QuestionType? QuestionType { get; set; }
|
||||
|
||||
public OptionType OptionTypeEnum { get; set; }
|
||||
|
||||
public Guid QuestionId { get; set; }
|
||||
public string QuestionName { get; set; }
|
||||
|
||||
@@ -52,6 +53,8 @@ public class TumorLessionAnswerInfo
|
||||
{
|
||||
public QuestionMark? QuestionMark { get; set; }
|
||||
|
||||
public OptionType OptionTypeEnum { get; set; }
|
||||
|
||||
//病灶Id
|
||||
public Guid RowId { get; set; }
|
||||
|
||||
@@ -122,7 +125,23 @@ public class TumorExportBaseModel : TU_TR_RSBaseModel
|
||||
public string VisitNote { get; set; }
|
||||
#endregion
|
||||
|
||||
#region 肿瘤学结果
|
||||
|
||||
|
||||
public List<OncologyExportInfo> OncologyResultList { get; set; }
|
||||
|
||||
//public string OncologyUserName { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class OncologyExportInfo
|
||||
{
|
||||
public decimal? VisitTaskNum { get; set; }
|
||||
public string VisitName { get; set; }
|
||||
public string OncologyResult { get; set; } = string.Empty;
|
||||
|
||||
public string OncologyReason { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+177
-10
@@ -22,6 +22,8 @@ using System.ComponentModel.Design;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using static IRaCIS.Core.Application.Service.ExcelExportHelper;
|
||||
using IDictionaryService = IRaCIS.Application.Interfaces.IDictionaryService;
|
||||
using TrialIdentityUser = IRaCIS.Core.Domain.Models.TrialIdentityUser;
|
||||
@@ -32,6 +34,8 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||
[ApiExplorerSettings(GroupName = "Common")]
|
||||
public class ExcelExportService(IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<Dictionary> _dictionaryRepository,
|
||||
IRepository<Internationalization> _internationalizationRepository,
|
||||
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
||||
IRepository<SystemDocNeedConfirmedUserType> _systemDocNeedConfirmedUserTypeRepository,
|
||||
IRepository<DicomStudy> _dicomStudyRepository,
|
||||
@@ -467,7 +471,10 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.Code), o => o.TrialCode.Contains(inQuery.Code))
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.ResearchProgramNo), o => o.ResearchProgramNo.Contains(inQuery.ResearchProgramNo))
|
||||
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.ExperimentName), o => o.ExperimentName.Contains(inQuery.ExperimentName))
|
||||
.WhereIf(_userInfo.UserTypeEnumInt != (int)UserTypeEnum.SuperAdmin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.Admin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.OP, t => t.TrialIdentityUserList.Any(t => t.IdentityUserId == _userInfo.IdentityUserId && t.IsDeleted == false) && t.IsDeleted == false)
|
||||
.WhereIf(_userInfo.UserTypeEnumInt != (int)UserTypeEnum.SuperAdmin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.Admin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.OP,
|
||||
t => t.TrialIdentityUserList.Any(t => t.IdentityUserId == _userInfo.IdentityUserId && t.IsDeleted == false
|
||||
&& t.TrialUserRoleList.Any(t => t.UserId == _userInfo.UserRoleId && t.IsDeleted == false))
|
||||
&& t.IsDeleted == false)
|
||||
.WhereIf(inQuery.CriterionType != null, o => o.TrialReadingCriterionList.Any(t => t.CriterionType == inQuery.CriterionType && t.IsSigned && t.IsConfirm))
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.PM_EMail), o => o.TrialUserRoleList.Any(t => t.UserRole.IdentityUser.EMail.Contains(inQuery.PM_EMail) && (t.UserRole.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserRole.UserTypeEnum == UserTypeEnum.APM)))
|
||||
.Select(t => new TrialToBeDoneDto()
|
||||
@@ -1758,6 +1765,80 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||
var list = taskMedicalReviewQueryable.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList();
|
||||
|
||||
|
||||
#region 处理翻译对话 by he wen tao
|
||||
|
||||
Dictionary<string, string> i18NKeys = new Dictionary<string, string>()
|
||||
{
|
||||
{ "Msg1","trials:medicalFeedback:message:msg1"},
|
||||
{ "Msg2","trials:medicalFeedback:message:msg2"},
|
||||
{ "Msg3","trials:medicalFeedback:message:msg3"},
|
||||
{ "Msg4", "trials:medicalFeedback:message:msg4"},
|
||||
{ "Msg5", "trials:medicalFeedback:message:msg5"},
|
||||
{ "CloseReasonEnum", "trials:medicalFeedback:title:closeReasonEnum"},
|
||||
{ "IsEndorse", "trials:medicalFeedback:title:isEndorse"},
|
||||
{ "Reason", "trials:medicalFeedback:title:reason"},
|
||||
{ "IsRequestReread", "trials:medicalFeedback:title:isRequestReread"},
|
||||
};
|
||||
|
||||
var i18Values = i18NKeys.Select(x => x.Value).ToList();
|
||||
var i18nList = await _internationalizationRepository.Where(x => i18Values.Contains(x.Code))
|
||||
.Select(x => new
|
||||
{
|
||||
Code = x.Code,
|
||||
Value = _userInfo.IsEn_Us ? x.Value : x.ValueCN
|
||||
}).ToListAsync();
|
||||
|
||||
List<string> dictionaryCodeList = new List<string>()
|
||||
{
|
||||
"AuditAdvice",
|
||||
"MedicalDialogCloseEnum",
|
||||
"MedicalReviewDoctorUserIdea",
|
||||
"YesOrNo",
|
||||
};
|
||||
|
||||
var dictionadParents = await _dictionaryRepository.Where(x => dictionaryCodeList.Contains(x.Parent.Code))
|
||||
.Select(x => new
|
||||
{
|
||||
ParentCode = x.Parent.Code,
|
||||
Code = x.Code,
|
||||
Value = _userInfo.IsEn_Us ? x.Value : x.ValueCN
|
||||
}).ToListAsync();
|
||||
|
||||
JointMedicalReviewI18n i18N = new JointMedicalReviewI18n() { };
|
||||
foreach (var kv in i18NKeys)
|
||||
{
|
||||
PropertyInfo? pi = i18N.GetType().GetProperty(kv.Key);
|
||||
if (pi != null && pi.CanWrite)
|
||||
{
|
||||
var value = i18nList.Where(x => x.Code == kv.Value).Select(x => x.Value).FirstOrDefault() ?? string.Empty;
|
||||
object safeValue = Convert.ChangeType(value, pi.PropertyType);
|
||||
pi.SetValue(i18N, safeValue);
|
||||
}
|
||||
}
|
||||
foreach (var item in dictionaryCodeList)
|
||||
{
|
||||
PropertyInfo? pi = i18N.GetType().GetProperty(item);
|
||||
if (pi != null && pi.CanWrite)
|
||||
{
|
||||
var value = dictionadParents.Where(x => x.ParentCode == item).ToDictionary(x => x.Code, x => x.Value);
|
||||
object safeValue = Convert.ChangeType(value, pi.PropertyType);
|
||||
pi.SetValue(i18N, safeValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (var item in list)
|
||||
{
|
||||
foreach (var dialog in item.DialogList)
|
||||
{
|
||||
dialog.ResultContent = JointMedicalReviewDialog(dialog, i18N);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo<ExcelExportInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException();
|
||||
|
||||
exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId);
|
||||
@@ -1767,6 +1848,88 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||
return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialMedicalReviewList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(TaskMedicalReviewExportDto));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取医学审核对话拼接内容
|
||||
/// </summary>
|
||||
/// <param name="record"></param>
|
||||
/// <param name="i18NAndDic"></param>
|
||||
/// <returns></returns>
|
||||
private string JointMedicalReviewDialog(GetMedicalReviewDialogOutDto record, JointMedicalReviewI18n i18NAndDic)
|
||||
{
|
||||
StringBuilder str = new StringBuilder() { };
|
||||
var userTypes = new List<int>() { 14, 30 };
|
||||
|
||||
// 这里userTypes.Contains(record.UserTypeEnumInt) 可以提出来 但是还是按照前端写吧 免得不好对照
|
||||
|
||||
// v-if="[14, 30].includes(record.UserTypeEnumInt) && record.Questioning
|
||||
if (userTypes.Contains(record.UserTypeEnumInt) && record.Questioning.IsNotNullOrEmpty())
|
||||
{
|
||||
// <!-- 您好,根据医学审核反馈,该阅片任务的评估有如下问题需要您确认或澄清: -->
|
||||
str.AppendLine(i18NAndDic.Msg1);
|
||||
|
||||
str.AppendLine(record.Questioning);
|
||||
|
||||
if (record.FileList.Count() > 0)
|
||||
{
|
||||
str.AppendLine(i18NAndDic.Msg2);
|
||||
record.FileList.ForEach(x =>
|
||||
{
|
||||
str.AppendLine(x.FileName);
|
||||
});
|
||||
}
|
||||
|
||||
str.AppendLine(i18NAndDic.Msg3);
|
||||
|
||||
str.AppendLine(i18NAndDic.AuditAdvice[record.AuditAdviceEnum.GetEnumInt()]);
|
||||
|
||||
str.AppendLine(i18NAndDic.Msg4);
|
||||
}
|
||||
|
||||
//v-if="[14, 30].includes(record.UserTypeEnumInt) && record.MedicalDialogCloseEnum!== null"
|
||||
if (userTypes.Contains(record.UserTypeEnumInt) && record.MedicalDialogCloseEnum != null)
|
||||
{
|
||||
str.AppendLine(i18NAndDic.CloseReasonEnum + i18NAndDic.MedicalDialogCloseEnum[record.MedicalDialogCloseEnum.Value.GetEnumInt()]);
|
||||
}
|
||||
|
||||
// v-if="[14, 30].includes(record.UserTypeEnumInt) && record.Content
|
||||
if (userTypes.Contains(record.UserTypeEnumInt) && record.Content.IsNotNullOrEmpty())
|
||||
{
|
||||
str.AppendLine(record.Content);
|
||||
}
|
||||
|
||||
// v-if="[13].includes(record.UserTypeEnumInt)"
|
||||
|
||||
if (13 == record.UserTypeEnumInt)
|
||||
{
|
||||
str.AppendLine(i18NAndDic.IsEndorse + i18NAndDic.MedicalReviewDoctorUserIdea[record.DoctorUserIdeaEnum.GetEnumInt()]);
|
||||
|
||||
// v-if="record.DoctorUserIdeaEnum===2"
|
||||
if ((int)record.DoctorUserIdeaEnum == 2)
|
||||
{
|
||||
str.AppendLine(i18NAndDic.Reason + record.DisagreeReason);
|
||||
}
|
||||
|
||||
// record.MedicalDialogCloseEnum!== null
|
||||
|
||||
if (record.MedicalDialogCloseEnum != null)
|
||||
{
|
||||
str.AppendLine(i18NAndDic.IsRequestReread + i18NAndDic.YesOrNo[record.IsApplyHeavyReading.ToString()]);
|
||||
}
|
||||
|
||||
// v-if="record.FileList && record.FileList.length > 0"
|
||||
|
||||
if (record.FileList.Count() > 0)
|
||||
{
|
||||
record.FileList.ForEach(x =>
|
||||
{
|
||||
str.AppendLine(x.FileName);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return str.ToString();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 影像下载记录表
|
||||
@@ -2605,9 +2768,13 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||
if (criterion.CriterionType == CriterionType.OCT)
|
||||
{
|
||||
list.Add(new ExportDocumentDes() { Code = StaticData.Export.OCT_ReadingLession_Export, ExportCatogory = ExportResult.OCT_ReadingLession_Export });
|
||||
list.Add(new ExportDocumentDes() { Code = StaticData.Export.OCT_CDISC_Export, ExportCatogory = ExportResult.OCT_CDISC_Export });
|
||||
}
|
||||
|
||||
if (criterion.CriterionGroup != CriterionGroup.Tumor)
|
||||
if (criterion.CriterionType == CriterionType.IVUS)
|
||||
{
|
||||
list.Add(new ExportDocumentDes() { Code = StaticData.Export.IVUS_CDISC_Export, ExportCatogory = ExportResult.IVUS_CDISC_Export });
|
||||
}
|
||||
if (criterion.CriterionGroup != CriterionGroup.Tumor && criterion.CriterionType != CriterionType.OCT && criterion.CriterionType != CriterionType.IVUS)
|
||||
{
|
||||
list.Add(new ExportDocumentDes() { Code = StaticData.Export.CDISC_Reading_Export, ExportCatogory = ExportResult.NoneTumorCDISC });
|
||||
}
|
||||
@@ -2736,7 +2903,7 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||
//阅片结果表
|
||||
export_Template = StaticData.Export.CommonReading_Export;
|
||||
}
|
||||
|
||||
//斑块表
|
||||
else if (inQuery.ReadingExportType == ExportResult.OCT_ReadingLession_Export)
|
||||
{
|
||||
//OCT
|
||||
@@ -3003,7 +3170,7 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||
addLessionInfoList.Add(new CommonQuesionInfo() { QuestionName = _userInfo.IsEn_Us ? "Lesion Type" : "病灶类型", QuestionValue = lession.LessionType, TranslateDicName = "LesionType" });
|
||||
}
|
||||
|
||||
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName });
|
||||
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionName = t.QuestionName, OptionTypeEnum = t.OptionTypeEnum, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName ,Unit=t.Unit});
|
||||
|
||||
//有三部分组成 外层问题+ 没有配置病灶编号和类型+ 动态的表格问题
|
||||
var dynamicLessionInfoList = item.QuestionAnswerList.Union(addLessionInfoList).Union(dynamicPartialLessionInfoList).ToList();
|
||||
@@ -3151,14 +3318,14 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||
|
||||
addLessionInfoList.Add(new CommonQuesionInfo() { QuestionId = Guid.Empty, QuestionName = _userInfo.IsEn_Us ? "Table Name" : "表格名称", QuestionValue = firstLessionAnser.TableName });
|
||||
|
||||
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId, QuestionName = t.TableName + "_" + t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode });
|
||||
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId, OptionTypeEnum = t.OptionTypeEnum, QuestionName = t.TableName + "_" + t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode, Unit = t.Unit });
|
||||
|
||||
//有三部分组成 外层问题+ 固定列表格名称 + 动态的表格问题
|
||||
dynamicLessionInfoList = item.QuestionAnswerList.Union(addLessionInfoList).Union(dynamicPartialLessionInfoList).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId, QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode });
|
||||
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId, OptionTypeEnum = t.OptionTypeEnum, QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode, Unit = t.Unit });
|
||||
|
||||
//两部分组成 外层问题+ 动态的表格问题
|
||||
dynamicLessionInfoList = item.QuestionAnswerList.Union(dynamicPartialLessionInfoList).ToList();
|
||||
@@ -3205,7 +3372,7 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||
TranslateDicNameList = translateDicNameList
|
||||
};
|
||||
|
||||
if (export_Template == StaticData.Export.ReadingLession_Export || export_Template == StaticData.Export.CommonJudgeReadingDetail_Export)
|
||||
if (export_Template == StaticData.Export.ReadingLession_Export || export_Template == StaticData.Export.CommonJudgeReadingDetail_Export || export_Template== StaticData.Export.OCT_ReadingLession_Export)
|
||||
{
|
||||
dynamicColumnConfig.TempalteLastColumnIndex = 8;
|
||||
}
|
||||
@@ -3224,7 +3391,7 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||
var trialConfigTableQuestionList = _trialReadingTableQuestionRepository.Where(t => t.TrialId == trialId && t.TrialCriterionId == trialReadingCriterionId).Where(t => t.ExportResultStr.Contains(((int)inQuery.ReadingExportType).ToString()))
|
||||
.Select(t => new ExportQuestionBasicInfo()
|
||||
{
|
||||
QuestionId = t.Id,
|
||||
QuestionId = t.Id,
|
||||
TableName = _userInfo.IsEn_Us ? t.ReadingQuestionTrial.QuestionEnName : t.ReadingQuestionTrial.QuestionName,
|
||||
QuestionName = _userInfo.IsEn_Us ? t.QuestionEnName : t.QuestionName,
|
||||
CDISCCode = t.CDISCCode,
|
||||
@@ -3268,7 +3435,7 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||
var dynamicLessionInfoList = new List<CommonQuesionInfo>();
|
||||
|
||||
|
||||
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId, QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode });
|
||||
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId,OptionTypeEnum=t.OptionTypeEnum, QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode, Unit = t.Unit });
|
||||
|
||||
//两部分组成 外层问题+ 动态的表格问题
|
||||
dynamicLessionInfoList = item.QuestionAnswerList.Union(dynamicPartialLessionInfoList).ToList();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -240,7 +240,7 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
var query = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsAnalysisCreate == false && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze))
|
||||
|
||||
//访视和全局查询已签名完成的,裁判可以是未签名,未完成的
|
||||
.Where(t => (t.ReadingTaskState == ReadingTaskState.HaveSigned && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global)) || t.ReadingCategory == ReadingCategory.Judge)
|
||||
.Where(t => (t.ReadingTaskState == ReadingTaskState.HaveSigned && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global || t.ReadingCategory == ReadingCategory.Oncology)) || t.ReadingCategory == ReadingCategory.Judge)
|
||||
.WhereIf(inQuery.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId)
|
||||
.WhereIf(inQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == inQuery.TrialSiteId)
|
||||
|
||||
@@ -261,7 +261,7 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
SourceSubjectVisitId = t.SourceSubjectVisitId,
|
||||
VisitTaskNum = t.VisitTaskNum,
|
||||
VisitNum = t.VisitTaskNum,
|
||||
VisitName = t.SourceSubjectVisit.VisitName,
|
||||
VisitName = t.TaskBlindName, //t.SourceSubjectVisit.VisitName,
|
||||
EarliestScanDate = t.SourceSubjectVisit.EarliestScanDate,
|
||||
LatestScanDate = t.SourceSubjectVisit.LatestScanDate,
|
||||
VisitTaskId = t.Id,
|
||||
@@ -314,7 +314,7 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
LesionList = t.LesionList.OrderBy(t => t.RowMark).Select(c => new TumorLessionInfo()
|
||||
{
|
||||
Id = c.Id,
|
||||
OrganInfoId=c.OrganInfoId,
|
||||
OrganInfoId = c.OrganInfoId,
|
||||
LessionType = c.ReadingQuestionTrial.LesionType,
|
||||
LessionCode = c.RowMark,
|
||||
SplitRowId = c.SplitRowId,
|
||||
@@ -335,7 +335,18 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
|
||||
}).ToList(),
|
||||
|
||||
}).ToList()
|
||||
}).ToList(),
|
||||
|
||||
OncologyResultList = t.ReadingCategory == ReadingCategory.Oncology ? t.OncologyResultList.Select(t => new OncologyExportInfo()
|
||||
{
|
||||
VisitName = t.VisitTask.TaskName,
|
||||
VisitTaskNum = t.VisitTask.VisitTaskNum,
|
||||
OncologyReason = t.EvaluationReason,
|
||||
OncologyResult = t.EvaluationResult
|
||||
}).ToList() : new List<OncologyExportInfo>()
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -386,10 +397,10 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
|
||||
var dicNameList = taskList.SelectMany(t => t.QuestionAnswerList).Select(t => t.TranslateDicName)
|
||||
.Union(taskList.SelectMany(t => t.LesionList).SelectMany(t => t.LessionAnswerList).Where(t => t.TranslateDicName.IsNotNullOrEmpty()).Select(c => c.TranslateDicName))
|
||||
.Union(new List<string>() { "ArmEnum", "ValueUnit", "GlobalAssessType", "ImageQualityIssues" })
|
||||
.Union(new List<string>() { "ArmEnum", "ValueUnit", "GlobalAssessType", "ImageQualityIssues", "OncologyAssessType" })
|
||||
.Distinct().ToArray();
|
||||
|
||||
var trialOrganDic = _trialOrganRepository.Where(t => t.TrialCriterionId == criterion.Id).Select(t=>new {t.OrganInfoId,t.Part,t.PartEN}).ToDictionary(
|
||||
var trialOrganDic = _trialOrganRepository.Where(t => t.TrialCriterionId == criterion.Id).Select(t => new { t.OrganInfoId, t.Part, t.PartEN }).ToDictionary(
|
||||
t => t.OrganInfoId,
|
||||
t => new { t.Part, t.PartEN }
|
||||
);
|
||||
@@ -484,6 +495,78 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
//lungano 单独处理
|
||||
if (task.ReadingCategory == ReadingCategory.Oncology)
|
||||
{
|
||||
|
||||
//取最后一次肿瘤学结果
|
||||
|
||||
if (task.VisitTaskId !=
|
||||
taskList.Where(t => t.SubjectCode == task.SubjectCode && t.ReadingCategory == ReadingCategory.Oncology)
|
||||
.OrderByDescending(c => c.VisitTaskNum).First().VisitTaskId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var OncologyResult in task.OncologyResultList)
|
||||
{
|
||||
if (OncologyResult.OncologyReason.IsNotNullOrEmpty())
|
||||
{
|
||||
|
||||
var co = CreatNewCOExport(task, coList.Count + 1);
|
||||
|
||||
co.RemarksQuote = $"{OncologyResult?.VisitName}_ONCOLOGY_COMMENTS";
|
||||
|
||||
co.VisitNum = OncologyResult?.VisitTaskNum;
|
||||
|
||||
co.VisitName = OncologyResult?.VisitName;
|
||||
|
||||
co.Remarks = OncologyResult.OncologyReason;
|
||||
|
||||
|
||||
|
||||
co.CODTC = task.SignTime?.ToString("yyyy-MM-dd");
|
||||
|
||||
coList.Add(co);
|
||||
}
|
||||
|
||||
if (OncologyResult.OncologyResult.IsNotNullOrEmpty())
|
||||
{
|
||||
|
||||
var visitTask = taskList.Where(t => t.VisitNum == OncologyResult?.VisitTaskNum && t.SubjectCode == task.SubjectCode).FirstOrDefault();
|
||||
|
||||
var rs = CreatNewRSExport(task, rs_subjectIndexNoDic);
|
||||
|
||||
rs.ARM_VisitName = $"{task.ArmEnumStr}_{OncologyResult?.VisitName}";
|
||||
|
||||
rs.VisitNum = OncologyResult?.VisitTaskNum;
|
||||
|
||||
rs.VisitName = OncologyResult?.VisitName;
|
||||
|
||||
rs.LatestScanDate = visitTask?.LatestScanDate;
|
||||
rs.EarliestScanDate = visitTask?.EarliestScanDate;
|
||||
|
||||
|
||||
Fill_RS_IndexValue(rs, 9, Lugano_RSFixed.RSValueList);
|
||||
|
||||
|
||||
rs.RespondEfficacyAssessment = translateDataList["OncologyAssessType"].Where(t => t.Code.ToLower() == OncologyResult.OncologyResult?.ToLower()).Select(t => isEn_Us ? t.Value : t.ValueCN).FirstOrDefault() ?? String.Empty; ;
|
||||
|
||||
var findTask = reReadingList.FirstOrDefault(t => t.UserName == rs.UserName && t.ArmEnum == rs.ArmEnum && t.VisitTaskNum == rs.VisitNum);
|
||||
|
||||
if (findTask != null)
|
||||
{
|
||||
rs.ReAssessmentReason = findTask.RequestReReadingReason;
|
||||
}
|
||||
|
||||
rsList.Add(rs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -520,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
|
||||
|
||||
|
||||
@@ -660,7 +748,7 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
}
|
||||
else
|
||||
{
|
||||
if (questionAnswer.ValueType == ValueOfType.Percentage)
|
||||
if (questionAnswer.ValueType == ValueOfType.Percentage && double.TryParse(questionAnswer.QuestionValue, out _))
|
||||
{
|
||||
tr.OriginalMeasurements = questionAnswer.QuestionValue + "%";
|
||||
|
||||
@@ -680,7 +768,7 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
|
||||
#endregion
|
||||
|
||||
#region rs 处理部分 只有非基线
|
||||
#region rs 处理部分 只有非基线处理PD
|
||||
|
||||
//非基线
|
||||
if (task.VisitNum > 0)
|
||||
@@ -690,24 +778,30 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
|
||||
List<RSValueEnum> rsValueList = new List<RSValueEnum>();
|
||||
|
||||
var overallPD = "";
|
||||
|
||||
if (criterion.CriterionType == CriterionType.RECIST1Point1)
|
||||
{
|
||||
rsValueTypeIndex = Get_Resist_RS_VisitFixed_Index(task, questionAnswer);
|
||||
|
||||
rsValueList = RESIST_RSFixed.RSValueList;
|
||||
|
||||
overallPD = "PD";
|
||||
|
||||
}
|
||||
else if (criterion.CriterionType == CriterionType.Lugano2014)
|
||||
{
|
||||
trValueTypeIndex = Get_Lugano_RS_VisitFixed_Index(task, questionAnswer);
|
||||
rsValueTypeIndex = Get_Lugano_RS_VisitFixed_Index(task, questionAnswer);
|
||||
|
||||
rsValueList = Lugano_RSFixed.RSValueList;
|
||||
|
||||
overallPD = "PMD/PD";
|
||||
}
|
||||
|
||||
|
||||
if (rsValueTypeIndex != -1)
|
||||
{
|
||||
var rs = CreatNewRSExport(task, questionAnswer, rs_subjectIndexNoDic, translateDataList, isEn_Us);
|
||||
var rs = CreatNewRSExport(task, rs_subjectIndexNoDic);
|
||||
|
||||
Fill_RS_IndexValue(rs, rsValueTypeIndex, rsValueList);
|
||||
|
||||
@@ -721,13 +815,15 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
rs.RespondEfficacyAssessment = questionAnswer.QuestionValue;
|
||||
}
|
||||
|
||||
if (questionAnswer.QuestionType == QuestionType.Tumor)
|
||||
if ((questionAnswer.QuestionType == QuestionType.Tumor && criterion.CriterionType == CriterionType.RECIST1Point1) ||
|
||||
(questionAnswer.QuestionType == QuestionType.ImgOncology && criterion.CriterionType == CriterionType.Lugano2014))
|
||||
|
||||
{
|
||||
rs.AssessmentReason = task.VisitNote;
|
||||
|
||||
rs.IsOveralResponse = true;
|
||||
|
||||
rs.IsOverallResponsePD = rs.RespondEfficacyAssessment == "PD";
|
||||
rs.IsOverallResponsePD = rs.RespondEfficacyAssessment == overallPD;
|
||||
|
||||
}
|
||||
|
||||
@@ -751,6 +847,8 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
rsList.Add(rs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -833,22 +931,17 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
|
||||
}
|
||||
|
||||
//if (task.IsTrigerJudge == true && task.IsJudgeSelect == true && task.JudgeNote.IsNotNullOrEmpty())
|
||||
//{
|
||||
// var co = CreatNewCOExport(task, coList.Count + 1);
|
||||
|
||||
// co.RemarksQuote = $"{co.VisitName}_ADJUDICATION_COMMENTS";
|
||||
// co.Remarks = task.JudgeNote;
|
||||
// co.CODTC = task.JudgeSignTime?.ToString("yyyy-MM-dd");
|
||||
|
||||
// coList.Add(co);
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#region 全局更新数据--rs 全局更新原因
|
||||
|
||||
|
||||
|
||||
|
||||
//var globalList = taskList.Where(t => t.ReadingCategory == ReadingCategory.Global).GroupBy(t => new { t.SubjectCode, t.ArmEnum }).Select(g => g.OrderByDescending(t => t.VisitNum).FirstOrDefault()).ToList();
|
||||
|
||||
//RS表 整体肿瘤评估了,其他病灶评估输出的也是最早的日期
|
||||
@@ -938,7 +1031,7 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
return co;
|
||||
}
|
||||
|
||||
private RS_Export CreatNewRSExport(TumorExportBaseModel task, TumorCommonQustionInfo questionAnswer, Dictionary<string, int> subjectIndexNoDic, Dictionary<string, List<BasicDicSelectCopy>> translateDataList, bool isEn_Us)
|
||||
private RS_Export CreatNewRSExport(TumorExportBaseModel task, Dictionary<string, int> subjectIndexNoDic)
|
||||
{
|
||||
subjectIndexNoDic[task.SubjectCode]++;
|
||||
|
||||
@@ -1117,6 +1210,38 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
}
|
||||
}
|
||||
|
||||
if (trValueTypeIndex == 0 || trValueTypeIndex == 1)
|
||||
{
|
||||
if (lesion.LessionType == LesionType.TargetLesion)
|
||||
{
|
||||
//处理消失 空为0mm
|
||||
if (lessionAnswer.QuestionValue.IsNullOrEmpty() && lesion.LessionAnswerList.FirstOrDefault(t => t.QuestionMark == QuestionMark.State)?.QuestionValue == "3")
|
||||
{
|
||||
|
||||
lessionAnswer.QuestionValue = "0";
|
||||
}
|
||||
}
|
||||
//有值 就展示,没值就不展示
|
||||
else if (lesion.LessionType == LesionType.NonTargetLesions || lesion.LessionType == LesionType.NewLesions)
|
||||
{
|
||||
|
||||
if (lesion.LessionAnswerList.FirstOrDefault(t => t.QuestionMark == QuestionMark.ShortAxis)?.QuestionValue.IsNotNullOrEmpty() == true
|
||||
|| lesion.LessionAnswerList.FirstOrDefault(t => t.QuestionMark == QuestionMark.MajorAxis)?.QuestionValue.IsNotNullOrEmpty() == true)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
trValueTypeIndex = -1;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
trValueTypeIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return trValueTypeIndex;
|
||||
|
||||
|
||||
@@ -1277,7 +1402,7 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
case QuestionType.NoTargetLesion:
|
||||
rsValueTypeIndex = 1;
|
||||
break;
|
||||
case QuestionType.NewLesions:
|
||||
case QuestionType.NewLesionEvaluation:
|
||||
rsValueTypeIndex = 2;
|
||||
break;
|
||||
|
||||
@@ -1307,9 +1432,6 @@ public class Tumor_CDISC_ExportService(IRepository<ReadingQuestionCriterionTrial
|
||||
rsValueTypeIndex = 8;
|
||||
break;
|
||||
|
||||
case QuestionType.Tumor:
|
||||
rsValueTypeIndex = 3;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
public interface IMailVerificationService
|
||||
{
|
||||
|
||||
Task AnolymousSendEmail(string researchProgramNo, string emailAddress, int verificationCode);
|
||||
Task AnolymousSendEmail(Guid trialId, string emailAddress, int verificationCode);
|
||||
|
||||
Task SendEmailVerification(string emailAddress, int verificationCode);
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
Task AddUserSendEmailAsync(Guid userId, string baseUrl, string routeUrl);
|
||||
|
||||
Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456");
|
||||
Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456", string baseUrl="");
|
||||
|
||||
Task SiteSurveyUserJoinEmail(Guid trialId, Guid userId, string userTypes, string baseUrl, string rootUrl);
|
||||
|
||||
@@ -189,6 +189,14 @@ namespace IRaCIS.Core.Application.Service
|
||||
return str;
|
||||
}
|
||||
|
||||
private string ReplacePlatformName(string needDealtxt)
|
||||
{
|
||||
var platformName = _userInfo.IsEn_Us ? _systemEmailConfig.PlatformName : _systemEmailConfig.PlatformNameCN;
|
||||
|
||||
var str = needDealtxt.Replace("{platformName}", platformName);
|
||||
return str;
|
||||
}
|
||||
|
||||
//MFA
|
||||
public async Task SenMFAVerifyEmail(Guid userId, string userName, string emailAddress, int verificationCode, UserMFAType mfaType = UserMFAType.Login)
|
||||
{
|
||||
@@ -276,10 +284,13 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
|
||||
//中心调研 登陆 发送验证码
|
||||
public async Task AnolymousSendEmail(string researchProgramNo, string emailAddress, int verificationCode)
|
||||
public async Task AnolymousSendEmail(Guid trialId, string emailAddress, int verificationCode)
|
||||
{
|
||||
//throw new BusinessValidationFailedException("模拟邮件取数据或者发送异常!!!");
|
||||
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
@@ -292,7 +303,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, companyName, researchProgramNo);
|
||||
var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo);
|
||||
|
||||
var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
|
||||
|
||||
@@ -312,7 +323,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress);
|
||||
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo, sucessHandle);
|
||||
|
||||
}
|
||||
|
||||
@@ -370,7 +381,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
}
|
||||
|
||||
//管理员重置密码发送邮件
|
||||
public async Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456")
|
||||
public async Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456", string baseUrl = "")
|
||||
{
|
||||
var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).Include(t => t.UserRoleList).ThenInclude(c => c.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException();
|
||||
|
||||
@@ -393,7 +404,9 @@ namespace IRaCIS.Core.Application.Service
|
||||
sysUserInfo.FullName,
|
||||
sysUserInfo.UserName,
|
||||
//string.Join(',', sysUserInfo.UserRoleList.Select(t => t.UserTypeRole.UserTypeShortName)),
|
||||
pwdNotMd5
|
||||
pwdNotMd5,
|
||||
baseUrl
|
||||
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
@@ -533,8 +546,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.SiteUseOrExternalUserFirstrJoinTrial : EmailBusinessScenario.SiteUserOrExternalUserExistJoinTrial, messageToSend, emailConfigFunc);
|
||||
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, null);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo, null);
|
||||
|
||||
}
|
||||
|
||||
@@ -593,8 +605,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.SiteUseOrExternalUserFirstrJoinTrial : EmailBusinessScenario.SiteUserOrExternalUserExistJoinTrial, messageToSend, emailConfigFunc);
|
||||
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
|
||||
}
|
||||
|
||||
@@ -723,8 +734,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.DoctorUserFirstJoinTrial : EmailBusinessScenario.DoctorUserExistJoinTrial, messageToSend, emailConfigFunc);
|
||||
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, null);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo, null);
|
||||
|
||||
//创建账号 和创建角色 一条,更新的时候才记录更新角色
|
||||
if (isNeedCreateNewUser == false)
|
||||
@@ -750,6 +760,8 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var isHaveTrialId = feedBack.TrialId != null;
|
||||
|
||||
var trialinfo = await _trialRepository.Where(x => x.Id == feedBack.TrialId).FirstOrDefaultAsync();
|
||||
|
||||
var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
var emialScenario = feedBack.VisitTaskId != null ? EmailBusinessScenario.IRImageError : (feedBack.SubjectVisitId != null ? EmailBusinessScenario.TrialSubjectVisitFeedBack : (feedBack.TrialId != null ? EmailBusinessScenario.TrialFeedBack : EmailBusinessScenario.SysFeedBack));
|
||||
@@ -884,7 +896,15 @@ namespace IRaCIS.Core.Application.Service
|
||||
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SysFeedBack, messageToSend, emailConfigFunc);
|
||||
}
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
if (trialinfo == null)
|
||||
{
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialinfo);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -909,7 +929,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, companyName);
|
||||
|
||||
var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
|
||||
var htmlBodyStr = string.Format(ReplacePlatformName(ReplaceCompanyName(input.htmlBodyStr)) ,
|
||||
|
||||
sysUserInfo.FullName
|
||||
);
|
||||
@@ -926,7 +946,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
//中心调研核对人员提醒
|
||||
public async Task SiteSuervyCheckUser(Guid trialId, string email, string name)
|
||||
{
|
||||
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.TrialCode, t.EmailFromName, t.ResearchProgramNo }).FirstOrDefaultAsync();
|
||||
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).FirstOrDefaultAsync();
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
@@ -954,14 +974,14 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
|
||||
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SiteSurvey_CheckUser, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
|
||||
public async Task SiteSuervyUpdateUser(Guid trialSiteId, string email, string name, string url)
|
||||
{
|
||||
var trialInfo = await _trialSiteRepository.Where(t => t.Id == trialSiteId).Select(t => new { t.Trial.TrialCode, t.Trial.EmailFromName, t.Trial.ResearchProgramNo, t.TrialSiteCode, t.TrialSiteName, t.TrialSiteAliasName }).FirstOrDefaultAsync();
|
||||
var siteInfo = await _trialSiteRepository.Where(t => t.Id == trialSiteId).Include(x=>x.Trial).FirstOrDefaultAsync();
|
||||
|
||||
var trialInfo = siteInfo.Trial;
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
@@ -980,8 +1000,8 @@ namespace IRaCIS.Core.Application.Service
|
||||
var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
|
||||
name,
|
||||
trialInfo.TrialCode,
|
||||
trialInfo.TrialSiteCode,
|
||||
trialInfo.TrialSiteName,
|
||||
siteInfo.TrialSiteCode,
|
||||
siteInfo.TrialSiteName,
|
||||
url
|
||||
);
|
||||
|
||||
@@ -990,8 +1010,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
|
||||
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SiteSurvey_UpdateUser, messageToSend, emailConfigFunc);
|
||||
SendEmailHelper.ChangeEmailSenderName(messageToSend, trialInfo.EmailFromName);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -120,6 +120,10 @@ namespace IRaCIS.Core.Application.Service
|
||||
CreateMap<TumorExportBaseModel, TR_Export>();
|
||||
CreateMap<TumorExportBaseModel, RS_Export>();
|
||||
CreateMap<TumorExportBaseModel, CO_Export>();
|
||||
|
||||
CreateMap<IVUS_OCTBaseDto, IvusExportDto>();
|
||||
CreateMap<IVUS_OCTBaseDto, OctExportDto>();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -183,6 +183,7 @@ namespace IRaCIS.Application.Contracts
|
||||
|
||||
public class SelectionReviewerDTO : DoctorDTO
|
||||
{
|
||||
public bool IsVacation { get; set; }
|
||||
public int DoctorTrialState { get; set; }
|
||||
|
||||
public bool IsEnroll { get; set; } = false;
|
||||
|
||||
@@ -133,6 +133,29 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 维护 医生基本信息 Code
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task ServiceDoctorBasicInfo()
|
||||
{
|
||||
var doctorList = await _doctorRepository.Where(t => t.Code == 0 && t.TrialId == null).ToListAsync();
|
||||
|
||||
var code = await _doctorRepository.Select(t => t.Code).DefaultIfEmpty().MaxAsync();
|
||||
|
||||
foreach (var doctorItem in doctorList)
|
||||
{
|
||||
code++;
|
||||
doctorItem.Code = code;
|
||||
doctorItem.ReviewerCode = AppSettings.GetCodeStr(doctorItem.Code, nameof(Doctor));
|
||||
await _doctorRepository.BatchUpdateNoTrackingAsync(x => x.Id == doctorItem.Id, x => new Doctor()
|
||||
{
|
||||
Code = doctorItem.Code,
|
||||
ReviewerCode = doctorItem.ReviewerCode
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增修改 医生基本信息和工作
|
||||
/// </summary>
|
||||
|
||||
@@ -131,6 +131,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
#region 医生基本信息
|
||||
CreateMap<Doctor, SelectionReviewerDTO>()
|
||||
.ForMember(d => d.IsVacation, u => u.MapFrom(t => t.VacationList.Any(x=>x.StartDate<DateTime.Now&&x.EndDate>DateTime.Now)))
|
||||
.ForMember(d => d.DoctorUserName, u => u.MapFrom(t => t.UserRole.IdentityUser.UserName));
|
||||
|
||||
|
||||
|
||||
@@ -92,11 +92,16 @@ 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; }
|
||||
public DateTime UserCreateTime { get; set; }
|
||||
|
||||
|
||||
public string EmailFromName { get; set; }
|
||||
|
||||
public DateTime? ConfirmTime { 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; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +98,8 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
[NotDefault]
|
||||
public Guid TrialId { get; set; }
|
||||
|
||||
public string EmailTopic { get; set; } = string.Empty;
|
||||
|
||||
public EmailBusinessScenario? BusinessScenarioEnum { get; set; }
|
||||
|
||||
public CriterionType? CriterionTypeEnum { get; set; }
|
||||
@@ -125,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; }
|
||||
}
|
||||
|
||||
@@ -110,8 +110,8 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
if (sendEmailConfig != null)
|
||||
{
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(sendEmailConfig);
|
||||
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).FirstOrDefaultAsync();
|
||||
await SendEmailHelper.SendEmailAsync(sendEmailConfig, trialInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -286,7 +287,7 @@ namespace IRaCIS.Core.Application.Services
|
||||
|
||||
var isInternal = _userInfo.IsZhiZhun;
|
||||
|
||||
var query = from sysDoc in _systemDocumentRepository.AsQueryable(true)
|
||||
var query = from sysDoc in _systemDocumentRepository.Where(t=>t.IsPublish)
|
||||
.Where(t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId))
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.Name), t => t.Name.Contains(inQuery.Name))
|
||||
//外部人员 只签署 外部需要签署的
|
||||
@@ -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))
|
||||
|
||||
@@ -54,9 +54,9 @@ namespace IRaCIS.Core.Application.Services
|
||||
|
||||
var trialDocumentAttachmentQueryable = _trialDocumentAttachmentRepository
|
||||
.WhereIf(inQuery.TrialDocumentId != null, x => x.TrialDocumentId == inQuery.TrialDocumentId)
|
||||
.WhereIf(inQuery.Name != null, x => x.Name.Contains(inQuery.Name))
|
||||
.WhereIf(inQuery.FileFormat != null, x => x.FileFormat == inQuery.FileFormat)
|
||||
.ProjectTo<TrialDocumentAttachmentView>(_mapper.ConfigurationProvider);
|
||||
.WhereIf(inQuery.Name != null, x => x.Name.Contains(inQuery.Name))
|
||||
.WhereIf(inQuery.FileFormat != null, x => x.FileFormat == inQuery.FileFormat)
|
||||
.ProjectTo<TrialDocumentAttachmentView>(_mapper.ConfigurationProvider);
|
||||
|
||||
var pageList = await trialDocumentAttachmentQueryable.ToPagedListAsync(inQuery);
|
||||
|
||||
@@ -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);
|
||||
await _trialDocumentRepository.SaveChangesAsync();
|
||||
Console.WriteLine("开始 发布项目文档");
|
||||
|
||||
@@ -122,7 +123,7 @@ namespace IRaCIS.Core.Application.Services
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试定时发送
|
||||
/// 测试邮件定时发送
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IResponseOutput> TestPush()
|
||||
@@ -140,6 +141,22 @@ namespace IRaCIS.Core.Application.Services
|
||||
return ResponseOutput.Result(true);
|
||||
}
|
||||
|
||||
|
||||
public async Task<IResponseOutput> TestSendEmail()
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
// 创建独立作用域
|
||||
using (var scope = serviceScopeFactory.CreateScope())
|
||||
{
|
||||
// 从新作用域解析服务
|
||||
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
|
||||
await mediator.Publish(new ImageQCRecurringEvent { TrialId = Guid.Parse("08de2254-5d7d-581a-0242-0a0001000000") });
|
||||
}
|
||||
});
|
||||
return ResponseOutput.Result(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setting 界面的 项目所有文档列表
|
||||
/// </summary>
|
||||
@@ -155,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);
|
||||
@@ -164,7 +181,7 @@ namespace IRaCIS.Core.Application.Services
|
||||
[HttpPost]
|
||||
public async Task<PageOutput<TrialSignDocView>> GetTrialSignDocumentList(TrialDocQuery inQuery)
|
||||
{
|
||||
var trialDocQueryable = from trialDoc in _trialDocumentRepository.AsQueryable(true)
|
||||
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))
|
||||
|
||||
@@ -337,7 +354,7 @@ namespace IRaCIS.Core.Application.Services
|
||||
#region 统一用户修改
|
||||
|
||||
var systemDocQuery =
|
||||
from sysDoc in _systemDocumentRepository.AsQueryable(true).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)
|
||||
@@ -376,7 +393,7 @@ namespace IRaCIS.Core.Application.Services
|
||||
|
||||
//项目文档查询
|
||||
var trialDocQuery =
|
||||
from trialDoc in _trialDocumentRepository.AsQueryable(true).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)))
|
||||
|
||||
@@ -566,13 +583,14 @@ namespace IRaCIS.Core.Application.Services
|
||||
#endregion
|
||||
|
||||
|
||||
var needSignTrialDocCount = await _trialDocumentRepository.AsQueryable(true).Where(t => t.TrialId == inQuery.TrialId && t.Trial.TrialStatusStr != StaticData.TrialState.TrialStopped)
|
||||
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))
|
||||
.CountAsync();
|
||||
|
||||
|
||||
var needSignSystemDocCount = await _systemDocumentRepository.AsQueryable(true)
|
||||
var needSignSystemDocCount = await _systemDocumentRepository.AsQueryable(true).Where(t => t.IsPublish)
|
||||
.WhereIf(isInternal == false, t => t.DocUserSignType == DocUserSignType.InnerAndOuter)
|
||||
.Where(t => t.IsDeleted == false && !t.SystemDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.IdentityUserId && t.ConfirmTime != null) && t.NeedConfirmedUserTypeList.Any(u => u.NeedConfirmUserTypeId == _userInfo.UserTypeId))
|
||||
.CountAsync();
|
||||
@@ -605,7 +623,7 @@ namespace IRaCIS.Core.Application.Services
|
||||
var trialInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId, ignoreQueryFilters: true).Select(t => new { t.TrialFinishedTime, t.TrialStatusStr }).FirstNotNullAsync());
|
||||
|
||||
var trialDocQuery =
|
||||
from trialDoc in _trialDocumentRepository.AsQueryable(false).Where(t => t.TrialId == inQuery.TrialId)
|
||||
from trialDoc in _trialDocumentRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsPublish)
|
||||
.Where(t => inQuery.UserTypeId != null ? t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == inQuery.UserTypeId) : true)
|
||||
from trialUser in _trialIdentityUserRepository.AsQueryable(false).Where(t => t.TrialId == inQuery.TrialId
|
||||
&& t.TrialUserRoleList.AsQueryable().Any(t => trialDoc.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == t.UserRole.UserTypeId))
|
||||
@@ -696,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);
|
||||
|
||||
@@ -906,6 +925,12 @@ namespace IRaCIS.Core.Application.Services
|
||||
//EA 只能查看内部人员文档
|
||||
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)))
|
||||
{
|
||||
return ResponseOutput.Ok();
|
||||
}
|
||||
|
||||
var list = _systemDocConfirmedUserRepository.Where(t => t.ConfirmTime != null)
|
||||
.WhereIf(isEA, t => t.ConfirmUser.IsZhiZhun == true)
|
||||
.Select(t => new { t.ConfirmUserId, t.ConfirmUser.UserName, t.ConfirmUser.FullName }).Distinct().ToList();
|
||||
@@ -922,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))
|
||||
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 只能查看内部人员文档
|
||||
@@ -939,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,
|
||||
@@ -970,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); //不是内部的人,看有签名时间的
|
||||
@@ -1069,6 +1102,7 @@ namespace IRaCIS.Core.Application.Services
|
||||
return ResponseOutput.NotOk(_localizer["TrialD_DuplicateFileInProject"]);
|
||||
}
|
||||
|
||||
entity.IsDeleted = true;
|
||||
//entity.Id = NewId.NextGuid();
|
||||
await _trialDocumentRepository.AddAsync(entity, true);
|
||||
return ResponseOutput.Ok(entity.Id.ToString());
|
||||
@@ -1081,7 +1115,7 @@ namespace IRaCIS.Core.Application.Services
|
||||
return ResponseOutput.NotOk(_localizer["TrialD_DuplicateFileInProject"]);
|
||||
}
|
||||
|
||||
var document = (await _trialDocumentRepository.Where(t => t.Id == addOrEditTrialDocument.Id, true).Include(t => t.NeedConfirmedUserTypeList).FirstOrDefaultAsync()).IfNullThrowException();
|
||||
var document = (await _trialDocumentRepository.Where(t => t.Id == addOrEditTrialDocument.Id, true,true).Include(t => t.NeedConfirmedUserTypeList).FirstOrDefaultAsync()).IfNullThrowException();
|
||||
|
||||
bool beforeIsPublish = document.IsPublish;
|
||||
bool beforeIsDeleted = document.IsDeleted;
|
||||
@@ -1089,6 +1123,7 @@ namespace IRaCIS.Core.Application.Services
|
||||
|
||||
_mapper.Map(addOrEditTrialDocument, document);
|
||||
document.UpdateTime = DateTime.Now;
|
||||
|
||||
#region 不区分路径了
|
||||
|
||||
//if (document.FileTypeId != addOrEditTrialDocument.FileTypeId)
|
||||
|
||||
@@ -13,6 +13,7 @@ using IRaCIS.Core.Application.Interfaces;
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||
using IRaCIS.Core.Application.ViewModel;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Domain.Share.Common;
|
||||
using IRaCIS.Core.Infra.EFCore.Common;
|
||||
@@ -617,8 +618,8 @@ namespace IRaCIS.Core.Application.Service
|
||||
FileStream = File.OpenRead(phyPath),
|
||||
});
|
||||
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(sendEmailConfig);
|
||||
var trialInfo = await _trialRepository.Where(t => t.Id == taskInfo.TrialId).FirstOrDefaultAsync();
|
||||
await SendEmailHelper.SendEmailAsync(sendEmailConfig, trialInfo);
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
@@ -733,8 +734,8 @@ namespace IRaCIS.Core.Application.Service
|
||||
FileStream = pdfMemoryStream
|
||||
});
|
||||
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(sendEmailConfig);
|
||||
var trialInfo = await _trialRepository.Where(t => t.Id == taskInfo.TrialId).FirstOrDefaultAsync();
|
||||
await SendEmailHelper.SendEmailAsync(sendEmailConfig, trialInfo);
|
||||
|
||||
}
|
||||
|
||||
@@ -772,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();
|
||||
@@ -1670,9 +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.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))
|
||||
@@ -1690,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);
|
||||
}
|
||||
|
||||
@@ -1763,22 +1768,22 @@ x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.LesionNumber && x.Readi
|
||||
|
||||
await _trialEmailNoticeConfigRepository.SaveChangesAsync();
|
||||
|
||||
var cronInfo = await _trialEmailNoticeConfigRepository.Where(t => t.Id == addOrEditTrialEmailNoticeConfig.Id)
|
||||
.Select(t => new { t.Id, t.Code, TrialCode = t.Trial.TrialCode, t.EmailCron, t.BusinessScenarioEnum, t.TrialId })
|
||||
.FirstAsync();
|
||||
// var cronInfo = await _trialEmailNoticeConfigRepository.Where(t => t.Id == addOrEditTrialEmailNoticeConfig.Id)
|
||||
//.Select(t => new { t.Id, t.Code, TrialCode = t.Trial.TrialCode, t.EmailCron, t.BusinessScenarioEnum, t.TrialId })
|
||||
//.FirstAsync();
|
||||
|
||||
var jobId = $"{cronInfo.TrialId}({cronInfo.TrialCode})_({cronInfo.BusinessScenarioEnum})";
|
||||
// var jobId = $"{cronInfo.TrialId}({cronInfo.TrialCode})_({cronInfo.BusinessScenarioEnum})";
|
||||
|
||||
|
||||
if (addOrEditTrialEmailNoticeConfig.IsAutoSend)
|
||||
{
|
||||
HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, addOrEditTrialEmailNoticeConfig.TrialId, addOrEditTrialEmailNoticeConfig.BusinessScenarioEnum, addOrEditTrialEmailNoticeConfig.EmailCron);
|
||||
// if (addOrEditTrialEmailNoticeConfig.IsAutoSend)
|
||||
// {
|
||||
// HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, addOrEditTrialEmailNoticeConfig.TrialId, addOrEditTrialEmailNoticeConfig.BusinessScenarioEnum, addOrEditTrialEmailNoticeConfig.EmailCron);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
HangfireJobHelper.RemoveCronJob(jobId);
|
||||
}
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// HangfireJobHelper.RemoveCronJob(jobId);
|
||||
// }
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -269,8 +271,7 @@ namespace IRaCIS.Core.Application.Contracts
|
||||
|
||||
public AddOrUpdateStudyDto Study { get; set; }
|
||||
|
||||
[NotDefault]
|
||||
public Guid VisitTaskId { get; set; }
|
||||
public Guid? VisitTaskId { get; set; }
|
||||
|
||||
public bool? IsImageSegmentLabel { get; set; }
|
||||
}
|
||||
@@ -506,8 +507,9 @@ namespace IRaCIS.Core.Application.Contracts
|
||||
|
||||
public class IRTaskUploadedDicomStudyQuery
|
||||
{
|
||||
[NotDefault]
|
||||
public Guid VisitTaskId { get; set; }
|
||||
public Guid? VisitTaskId { get; set; }
|
||||
|
||||
public Guid? SubjectVisitId { get; set; }
|
||||
}
|
||||
|
||||
public class IRUploadTaskDicomStudyDto : DicomStudyBasicInfo
|
||||
@@ -981,7 +983,9 @@ namespace IRaCIS.Core.Application.Contracts
|
||||
public int? VisitCount { get; set; }
|
||||
|
||||
|
||||
public int? MarkVisitCount { get; set; }
|
||||
public int? MarkIVUSVisitCount { get; set; }
|
||||
|
||||
public int? MarkOCTVisitCount { get; set; }
|
||||
|
||||
|
||||
public int? DicomStudyCount { get; set; }
|
||||
@@ -1027,7 +1031,8 @@ namespace IRaCIS.Core.Application.Contracts
|
||||
|
||||
public Guid? SourceSubjectVisitId => SubjectVisitId;
|
||||
|
||||
|
||||
//防止前端null 故意返回
|
||||
public Guid VisitTaskId { get; set; }
|
||||
|
||||
public List<StudyBasicInfo> OrginalStudyList { get; set; }
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
IDistributedLockProvider _distributedLockProvider,
|
||||
IRepository<TrialImageDownload> _trialImageDownloadRepository,
|
||||
IRepository<Subject> _subjectRepository,
|
||||
IRepository<DicomInstance> _instanceRepository,
|
||||
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IFusionCache _fusionCache) : BaseService, IDownloadAndUploadService
|
||||
{
|
||||
|
||||
@@ -125,7 +126,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
|
||||
//要根据标准阅片顺序,确定是否查询单个任务的,还是查询所有的
|
||||
var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == inQuery.TrialReadingCriterionId)
|
||||
.Select(t => new { t.IsReadingTaskViewInOrder, t.Trial.Modalitys }).FirstNotNullAsync();
|
||||
.Select(t => new { t.IsReadingTaskViewInOrder, t.Trial.Modalitys, t.IsImageFilter, t.CriterionModalitys }).FirstNotNullAsync();
|
||||
|
||||
var subjectCode = string.Empty;
|
||||
var subjectId = inQuery.SubjectId;
|
||||
@@ -159,7 +160,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
|
||||
if (inQuery.IsImageSegmentLabel == true)
|
||||
{
|
||||
var querySV = _subjectVisitRepository.WhereIf(inQuery.SubjectVisitId != null, t => t.Id == inQuery.SubjectVisitId)
|
||||
var querySV = _subjectVisitRepository.Where(t => t.CheckState == CheckStateEnum.CVPassed)
|
||||
.WhereIf(inQuery.SubjectVisitId != null, t => t.Id == inQuery.SubjectVisitId)
|
||||
.WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId)
|
||||
.Select(u => new SubjectVisitMarkUploadDto()
|
||||
{
|
||||
@@ -174,6 +176,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
.Select(t => new StudyBasicInfo()
|
||||
{
|
||||
Id = t.Id,
|
||||
SubjectVisitId = t.SubjectVisitId,
|
||||
|
||||
StudyInstanceUid = t.StudyInstanceUid,
|
||||
ModalityForEdit = t.ModalityForEdit,
|
||||
BodyPartExamined = t.BodyPartExamined,
|
||||
@@ -194,6 +198,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
UploadStudyList = u.TaskStudyList.Select(t => new StudyBasicInfo()
|
||||
{
|
||||
Id = t.Id,
|
||||
SubjectVisitId = t.SubjectVisitId,
|
||||
StudyInstanceUid = t.StudyInstanceUid,
|
||||
ModalityForEdit = t.ModalityForEdit,
|
||||
BodyPartExamined = t.BodyPartExamined,
|
||||
@@ -227,7 +232,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
|
||||
|
||||
|
||||
var query = _visitTaskRepository.Where(t => t.SubjectId == subjectId && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SourceSubjectVisitId != null
|
||||
var query = _visitTaskRepository.Where(t => t.SubjectId == subjectId && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SourceSubjectVisit.CheckState == CheckStateEnum.CVPassed
|
||||
&& t.DoctorUserId == _userInfo.UserRoleId && t.TaskState == TaskState.Effect)
|
||||
//满足 有序,或者随机只看到当前任务的dicom 非dicom检查
|
||||
.WhereIf(criterionInfo.IsReadingTaskViewInOrder != ReadingOrder.SubjectRandom && inQuery.VisitTaskId != null, t => t.Id == inQuery.VisitTaskId)
|
||||
@@ -252,7 +257,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
|
||||
OrginalStudyList = u.SourceSubjectVisit.StudyList
|
||||
.Where(t => u.TrialReadingCriterion.IsImageFilter ? ("|" + u.TrialReadingCriterion.CriterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|") : true)
|
||||
.Where(t => inQuery.IsImageSegmentLabel == false ? t.ModalityForEdit == "XA" : true)
|
||||
.Where(t => inQuery.IsImageSegmentLabel == false ? t.ModalityForEdit == "XA" || t.ModalityForEdit == "DSA" || t.ModalityForEdit == "OCT" : true)
|
||||
.Select(t => new StudyBasicInfo()
|
||||
{
|
||||
Id = t.Id,
|
||||
@@ -326,7 +331,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
{
|
||||
foreach (var item in list)
|
||||
{
|
||||
item.OrginalStudyList.AddRange(item.OriginalTaskStudyList);
|
||||
item.OrginalStudyList.AddRange(item.OriginalTaskStudyList.WhereIf(criterionInfo.IsImageFilter, t => ("|" + criterionInfo.CriterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,9 +553,11 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
|
||||
var @uploadLock = _distributedLockProvider.CreateLock($"UploadTaskDicom");
|
||||
|
||||
var visiTaskId = incommand.VisitTaskId ?? Guid.Empty;
|
||||
|
||||
using (await @uploadLock.AcquireAsync())
|
||||
{
|
||||
if (_fusionCache.GetOrDefault<Guid>(CacheKeys.TrialTaskStudyUidDBLock(incommand.TrialId, incommand.VisitTaskId, incommand.Study.StudyInstanceUid)) != Guid.Empty)
|
||||
if (_fusionCache.GetOrDefault<Guid>(CacheKeys.TrialTaskStudyUidDBLock(incommand.TrialId, visiTaskId, incommand.Study.StudyInstanceUid)) != Guid.Empty)
|
||||
{
|
||||
//---当前已有人正在上传和归档该检查!
|
||||
return ResponseOutput.NotOk(I18n.T("UploadDownLoad_ArchiveInProgress"));
|
||||
@@ -558,7 +565,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
else
|
||||
{
|
||||
//在事务未完成前 防止前端重复提交
|
||||
await _fusionCache.SetAsync(CacheKeys.TrialTaskStudyUidDBLock(incommand.TrialId, incommand.VisitTaskId, incommand.Study.StudyInstanceUid), _userInfo.UserRoleId, TimeSpan.FromMinutes(1));
|
||||
await _fusionCache.SetAsync(CacheKeys.TrialTaskStudyUidDBLock(incommand.TrialId, visiTaskId, incommand.Study.StudyInstanceUid), _userInfo.UserRoleId, TimeSpan.FromMinutes(1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,7 +577,6 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
svId = _dicomStudyRepository.Where(t => t.TrialId == incommand.TrialId && t.StudyInstanceUid == incommand.Study.StudyInstanceUid).Select(t => t.SubjectVisitId).FirstOrDefault();
|
||||
}
|
||||
|
||||
var visiTaskId = incommand.VisitTaskId;
|
||||
|
||||
var modalitys = string.Empty;
|
||||
|
||||
@@ -805,8 +811,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
}
|
||||
finally
|
||||
{
|
||||
await _fusionCache.RemoveAsync(CacheKeys.TrialTaskStudyUidDBLock(incommand.TrialId, incommand.VisitTaskId, incommand.Study.StudyInstanceUid));
|
||||
await _fusionCache.RemoveAsync(CacheKeys.TrialTaskStudyUidUploading(incommand.TrialId, incommand.VisitTaskId, incommand.Study.StudyInstanceUid));
|
||||
await _fusionCache.RemoveAsync(CacheKeys.TrialTaskStudyUidDBLock(incommand.TrialId, visiTaskId, incommand.Study.StudyInstanceUid));
|
||||
await _fusionCache.RemoveAsync(CacheKeys.TrialTaskStudyUidUploading(incommand.TrialId, visiTaskId, incommand.Study.StudyInstanceUid));
|
||||
}
|
||||
|
||||
|
||||
@@ -820,39 +826,68 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
|
||||
|
||||
[HttpDelete]
|
||||
public async Task<IResponseOutput> DeleteTaskStudy(Guid visitTaskId, bool isDicom, Guid? dicomStudyId, Guid? noneDicomStudyId)
|
||||
public async Task<IResponseOutput> DeleteTaskStudy(Guid visitTaskId, Guid? subjectVisitId, bool isDicom, Guid? dicomStudyId, Guid? noneDicomStudyId)
|
||||
{
|
||||
if (isDicom)
|
||||
{
|
||||
if (dicomStudyId == null)
|
||||
//靶段标注删除访视影像
|
||||
if (visitTaskId == Guid.Empty && subjectVisitId != null && subjectVisitId != Guid.Empty)
|
||||
{
|
||||
var deleteStudyPathList = await _taskInstanceRepository.Where(t => t.VisitTaskId == visitTaskId).GroupBy(t => t.StudyId).Select(g => g.First().Path).ToListAsync();
|
||||
|
||||
foreach (var fisrtPath in deleteStudyPathList)
|
||||
{
|
||||
var prefix = fisrtPath.Substring(1, fisrtPath.LastIndexOf('/') - 1);
|
||||
await _oSSService.DeleteFromPrefix(prefix, true);
|
||||
}
|
||||
|
||||
|
||||
await _taskStudyRepository.DeleteFromQueryAsync(t => t.VisitTaskId == visitTaskId);
|
||||
await _taskSeriesRepository.BatchDeleteNoTrackingAsync(t => t.VisitTaskId == visitTaskId);
|
||||
await _taskInstanceRepository.BatchDeleteNoTrackingAsync(t => t.VisitTaskId == visitTaskId);
|
||||
}
|
||||
else
|
||||
{
|
||||
var fisrtPath = await _taskInstanceRepository.Where(t => t.VisitTaskId == visitTaskId && t.StudyId == dicomStudyId).Select(t => t.Path).FirstOrDefaultAsync();
|
||||
var fisrtPath = await _taskInstanceRepository.Where(t => t.SubjectVisitId == subjectVisitId).Select(t => t.Path).FirstOrDefaultAsync();
|
||||
var prefix = fisrtPath.Substring(1, fisrtPath.LastIndexOf('/') - 1);
|
||||
await _oSSService.DeleteFromPrefix(prefix, true);
|
||||
|
||||
await _taskStudyRepository.DeleteFromQueryAsync(t => t.VisitTaskId == visitTaskId && t.Id == dicomStudyId);
|
||||
await _taskSeriesRepository.BatchDeleteNoTrackingAsync(t => t.VisitTaskId == visitTaskId && t.Id == dicomStudyId);
|
||||
await _taskInstanceRepository.BatchDeleteNoTrackingAsync(t => t.VisitTaskId == visitTaskId && t.Id == dicomStudyId);
|
||||
await _taskStudyRepository.DeleteFromQueryAsync(t => t.SubjectVisitId == subjectVisitId);
|
||||
await _taskSeriesRepository.BatchDeleteNoTrackingAsync(t => t.SubjectVisitId == subjectVisitId);
|
||||
await _taskInstanceRepository.BatchDeleteNoTrackingAsync(t => t.SubjectVisitId == subjectVisitId);
|
||||
}
|
||||
|
||||
if (visitTaskId != Guid.Empty)
|
||||
{
|
||||
if (dicomStudyId == null)
|
||||
{
|
||||
var deleteStudyPathList = await _taskInstanceRepository.Where(t => t.VisitTaskId == visitTaskId).GroupBy(t => t.StudyId).Select(g => g.First().Path).ToListAsync();
|
||||
|
||||
foreach (var fisrtPath in deleteStudyPathList)
|
||||
{
|
||||
var prefix = fisrtPath.Substring(1, fisrtPath.LastIndexOf('/') - 1);
|
||||
await _oSSService.DeleteFromPrefix(prefix, true);
|
||||
}
|
||||
|
||||
|
||||
await _taskStudyRepository.DeleteFromQueryAsync(t => t.VisitTaskId == visitTaskId);
|
||||
await _taskSeriesRepository.BatchDeleteNoTrackingAsync(t => t.VisitTaskId == visitTaskId);
|
||||
await _taskInstanceRepository.BatchDeleteNoTrackingAsync(t => t.VisitTaskId == visitTaskId);
|
||||
}
|
||||
else
|
||||
{
|
||||
var fisrtPath = await _taskInstanceRepository.Where(t => t.VisitTaskId == visitTaskId && t.StudyId == dicomStudyId).Select(t => t.Path).FirstOrDefaultAsync();
|
||||
var prefix = fisrtPath.Substring(1, fisrtPath.LastIndexOf('/') - 1);
|
||||
await _oSSService.DeleteFromPrefix(prefix, true);
|
||||
|
||||
await _taskStudyRepository.DeleteFromQueryAsync(t => t.VisitTaskId == visitTaskId && t.Id == dicomStudyId);
|
||||
await _taskSeriesRepository.BatchDeleteNoTrackingAsync(t => t.VisitTaskId == visitTaskId && t.Id == dicomStudyId);
|
||||
await _taskInstanceRepository.BatchDeleteNoTrackingAsync(t => t.VisitTaskId == visitTaskId && t.Id == dicomStudyId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
await _noneDicomStudyFileReposiotry.DeleteFromQueryAsync(t => t.VisitTaskId == visitTaskId && t.OriginNoneDicomStudyId == noneDicomStudyId);
|
||||
if (visitTaskId == Guid.Empty)
|
||||
{
|
||||
|
||||
//删除靶段标注非dicom
|
||||
await _noneDicomStudyFileReposiotry.DeleteFromQueryAsync(t => t.ImageLabelNoneDicomStudyId == noneDicomStudyId);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//删除后处理非dicom
|
||||
await _noneDicomStudyFileReposiotry.DeleteFromQueryAsync(t => t.VisitTaskId == visitTaskId && t.OriginNoneDicomStudyId == noneDicomStudyId);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await _noneDicomStudyFileReposiotry.SaveChangesAsync();
|
||||
@@ -868,7 +903,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
/// <param name="inQuery"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<List<TaskNoneDicomStudyDTO>> GetIRUploadTaskNoneDicomStudyList(IRUploadStudyQuery inQuery)
|
||||
public async Task<IResponseOutput<List<TaskNoneDicomStudyDTO>>> GetIRUploadTaskNoneDicomStudyList([FromServices] INoneDicomStudyService _noneDicomStudyService, IRUploadStudyQuery inQuery)
|
||||
{
|
||||
var subjectCode = inQuery.SubjectCode;
|
||||
var subjectId = inQuery.SubjectId;
|
||||
@@ -893,12 +928,71 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
}
|
||||
|
||||
var info = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == inQuery.TrialReadingCriterionId)
|
||||
.Select(t => new { t.IsImageFilter, t.CriterionModalitys, t.IsReadingTaskViewInOrder }).FirstNotNullAsync();
|
||||
.Select(t => new { t.IsImageFilter, t.CriterionType, t.TrialId, t.CriterionModalitys, t.IsReadingTaskViewInOrder }).FirstNotNullAsync();
|
||||
|
||||
var config = await _subjectRepository.Where(t => t.Id == subjectId).Select(t => new { t.Trial.ImageFormatList, t.Trial.StudyNameList, t.Trial.IsShowStudyName }).FirstOrDefaultAsync();
|
||||
|
||||
|
||||
//靶段标注上传,查看访视级别,上传绑定访视级别
|
||||
if (inQuery.IsImageSegmentLabel == true)
|
||||
{
|
||||
var list = _noneDicomStudyReposiotry.Where(t => t.SubjectId == subjectId && t.SubjectVisitId == inQuery.SubjectVisitId)
|
||||
//存在ivus 和oct 这两种的项目
|
||||
if (_readingQuestionCriterionTrialRepository.Where(t => t.TrialId == info.TrialId &&
|
||||
(t.CriterionType == CriterionType.IVUS || t.CriterionType == CriterionType.OCT)).Distinct().Count() == 2
|
||||
&& !_noneDicomStudyReposiotry.Any(t => t.SubjectId == inQuery.SubjectId && t.Modality == "IVUS"))
|
||||
{
|
||||
#region ivus 自动创建非dicom检查
|
||||
|
||||
var addList = await _dicomStudyRepository.Where(t => t.SubjectId == inQuery.SubjectId)
|
||||
.Select(t => new NoneDicomStudyAddOrEdit()
|
||||
{
|
||||
TrialId = t.TrialId,
|
||||
SubjectId = t.SubjectId,
|
||||
SubjectVisitId = t.SubjectVisitId,
|
||||
|
||||
ImageDate = (DateTime)t.StudyTime,
|
||||
BodyPart = t.BodyPartForEdit,
|
||||
BodyPartForEditOther = t.BodyPartForEditOther,
|
||||
Modality = "IVUS"
|
||||
|
||||
}).ToListAsync();
|
||||
|
||||
|
||||
var @lock = _distributedLockProvider.CreateLock($"NoneDicomCode");
|
||||
|
||||
using (await @lock.AcquireAsync())
|
||||
{
|
||||
|
||||
var trialId = addList.FirstOrDefault().TrialId;
|
||||
//默认会是0
|
||||
var code = await _noneDicomStudyReposiotry.Where(t => t.TrialId == trialId).Select(x => x.Code).DefaultIfEmpty().MaxAsync();
|
||||
|
||||
foreach (var g in addList.GroupBy(t => t.SubjectVisitId))
|
||||
{
|
||||
var addOrEditNoneDicomStudy = g.First();
|
||||
|
||||
var optEntity = await _noneDicomStudyReposiotry.InsertFromDTOAsync(addOrEditNoneDicomStudy);
|
||||
|
||||
optEntity.Code = code + 1;
|
||||
|
||||
|
||||
optEntity.StudyCode = AppSettings.GetCodeStr(optEntity.Code, nameof(NoneDicomStudy));
|
||||
|
||||
code++;
|
||||
}
|
||||
|
||||
await _noneDicomStudyReposiotry.SaveChangesAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var list = _noneDicomStudyReposiotry.Where(t => t.SubjectId == subjectId && t.SubjectVisit.CheckState == CheckStateEnum.CVPassed)
|
||||
.WhereIf(inQuery.SubjectVisitId != null, t => t.SubjectVisitId == inQuery.SubjectVisitId)
|
||||
//.WhereIf(info.IsImageFilter, t => ("|" + info.CriterionModalitys + "|").Contains("|" + t.Modality + "|"))
|
||||
.Select(u => new TaskNoneDicomStudyDTO()
|
||||
{
|
||||
@@ -924,7 +1018,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
Path = t.Path
|
||||
}).ToList(),
|
||||
|
||||
ReadingFileCount = u.FileCount,
|
||||
ReadingFileCount = u.NoneDicomFileList.Where(t => t.IsReading).Count(),
|
||||
|
||||
}).ToList();
|
||||
|
||||
@@ -950,12 +1044,12 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
item.UploadedFileList = noneDicomStudyList.Where(t => t.OriginNoneDicomStudyId == item.Id).ToList();
|
||||
}
|
||||
|
||||
return list;
|
||||
return ResponseOutput.Ok(list, config);
|
||||
}
|
||||
else
|
||||
{
|
||||
var query = from u in _visitTaskRepository.Where(t => t.SubjectId == subjectId && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId
|
||||
&& t.SourceSubjectVisitId != null && t.DoctorUserId == doctorUserId && t.TaskState == TaskState.Effect)
|
||||
&& t.SourceSubjectVisit.CheckState == CheckStateEnum.CVPassed && t.DoctorUserId == doctorUserId && t.TaskState == TaskState.Effect)
|
||||
//满足 有序,或者随机只看到当前任务的dicom 非dicom检查
|
||||
.WhereIf(info.IsReadingTaskViewInOrder != ReadingOrder.SubjectRandom && inQuery.VisitTaskId != null, t => t.Id == inQuery.VisitTaskId)
|
||||
join ns in _noneDicomStudyReposiotry.Where(t => t.SubjectId == subjectId).WhereIf(info.IsImageFilter, t => ("|" + info.CriterionModalitys + "|").Contains("|" + t.Modality + "|"))
|
||||
@@ -976,12 +1070,12 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
ImageDate = ns.ImageDate,
|
||||
BodyPart = ns.BodyPart,
|
||||
BodyPartForEditOther = ns.BodyPartForEditOther,
|
||||
FileCount = ns.FileCount,
|
||||
|
||||
Modality = ns.Modality,
|
||||
StudyCode = ns.StudyCode,
|
||||
|
||||
//靶段标注 阅片IR 上传时IsImageSegmentLabel == false ,正常后处理上传IsImageSegmentLabel 该字段为空
|
||||
FileList = (inQuery.IsImageSegmentLabel == false ? ns.NoneDicomFileList : ns.ImageLabelNoneDicomFileList)
|
||||
FileList = (inQuery.IsImageSegmentLabel == false ? ns.ImageLabelNoneDicomFileList : ns.NoneDicomFileList)
|
||||
.Select(t => new NoneDicomFileInfo()
|
||||
{
|
||||
FileType = t.FileType,
|
||||
@@ -990,7 +1084,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
Path = t.Path
|
||||
}).ToList(),
|
||||
|
||||
ReadingFileCount = ns.FileCount,
|
||||
FileCount = (inQuery.IsImageSegmentLabel == false ? ns.ImageLabelNoneDicomFileList : ns.NoneDicomFileList).Count(),
|
||||
ReadingFileCount = (inQuery.IsImageSegmentLabel == false ? ns.ImageLabelNoneDicomFileList : ns.NoneDicomFileList).Where(t => t.IsReading).Count(),
|
||||
|
||||
};
|
||||
|
||||
@@ -1018,7 +1113,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
item.UploadedFileList = taskNoneDicomStudyList.Where(t => t.OriginNoneDicomStudyId == item.Id).ToList();
|
||||
}
|
||||
|
||||
return list;
|
||||
return ResponseOutput.Ok(list, config);
|
||||
}
|
||||
|
||||
|
||||
@@ -1044,8 +1139,11 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
var dirDic = new Dictionary<string, string>();
|
||||
#region DIR处理导出文件名,并将对应关系上传到OSS里面存储
|
||||
|
||||
var info = await _subjectVisitRepository.Where(t => t.Id == inQuery.SubjectVisitId).Select(t => new { t.Trial.TrialCode }).FirstOrDefaultAsync();
|
||||
|
||||
//有传输语法值的导出 才生成DIR
|
||||
if (_subjectVisitRepository.Where(t => t.Id == inQuery.SubjectVisitId).SelectMany(t => t.StudyList.SelectMany(t => t.InstanceList)).All(c => c.TransferSytaxUID != string.Empty))
|
||||
//if (_subjectVisitRepository.Where(t => t.Id == inQuery.SubjectVisitId).SelectMany(t => t.StudyList.SelectMany(t => t.InstanceList)).All(c => c.TransferSytaxUID != string.Empty))
|
||||
if (!_instanceRepository.Where(t => inQuery.SubjectVisitId == t.SubjectVisitId).Any(c => c.TransferSytaxUID == string.Empty))
|
||||
{
|
||||
var list = _subjectVisitRepository.Where(t => t.Id == inQuery.SubjectVisitId).SelectMany(t => t.StudyList)
|
||||
.Where(t => isQueryDicom ? inQuery.DicomStudyIdList.Contains(t.Id) : false)
|
||||
@@ -1055,7 +1153,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
|
||||
DicomStudyId = t.DicomStudy.Id,
|
||||
|
||||
PatientId = t.DicomStudy.PatientId,
|
||||
PatientId = info.TrialCode + "-" + t.DicomStudy.Subject.Code,
|
||||
PatientName = t.DicomStudy.PatientName,
|
||||
PatientBirthDate = t.DicomStudy.PatientBirthDate,
|
||||
PatientSex = t.DicomStudy.PatientSex,
|
||||
@@ -1089,6 +1187,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));
|
||||
@@ -1096,7 +1197,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"]}" });
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1236,7 +1337,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
|
||||
if (inQuery.IsImageSegmentLabel == true)
|
||||
{
|
||||
var list = await _subjectVisitRepository
|
||||
var list = await _subjectVisitRepository.Where(t => t.CheckState == CheckStateEnum.CVPassed)
|
||||
.WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId)
|
||||
.ProjectTo<SubjectVisitMarkStudyDto>(_mapper.ConfigurationProvider).ToListAsync();
|
||||
|
||||
@@ -1248,7 +1349,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
var dicomStudyList = list.Where(item => item.DicomStudyList.Count > 0)
|
||||
.Select(item => new SubjectVisitMarkStudyDto
|
||||
{
|
||||
SourceSubjectVisitId=item.SourceSubjectVisitId,
|
||||
SourceSubjectVisitId = item.SourceSubjectVisitId,
|
||||
VisitTaskId = item.VisitTaskId,
|
||||
SubjectId = item.SubjectId,
|
||||
SubjectCode = item.SubjectCode,
|
||||
@@ -1311,7 +1412,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
}
|
||||
|
||||
var query = _visitTaskRepository.Where(t => t.SubjectId == subjectId && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId
|
||||
&& t.SourceSubjectVisitId != null && t.DoctorUserId == doctorUserId)
|
||||
&& t.SourceSubjectVisit.CheckState == CheckStateEnum.CVPassed && t.DoctorUserId == doctorUserId)
|
||||
//满足 有序,或者随机只看到当前任务的dicom 非dicom检查
|
||||
.WhereIf(inQuery.VisitTaskId == null, t => t.TaskState == TaskState.Effect)//从待阅列表进入,要筛选出有效的,任务可能重阅了,也要看到该任务的
|
||||
.WhereIf(criterionInfo.IsReadingTaskViewInOrder != ReadingOrder.SubjectRandom && inQuery.VisitTaskId != null, t => t.Id == inQuery.VisitTaskId)
|
||||
@@ -1329,8 +1430,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
{
|
||||
var subjectVisitIdList = list.Select(t => t.SourceSubjectVisitId).Distinct().ToList();
|
||||
|
||||
var preDicomStudyList = _subjectVisitRepository.Where(t => subjectVisitIdList.Contains(t.Id)).SelectMany(t => t.TaskStudyList)
|
||||
.ProjectTo<DicomStudyBasicInfo>(_mapper.ConfigurationProvider).ToList();
|
||||
//var preDicomStudyList = _subjectVisitRepository.Where(t => subjectVisitIdList.Contains(t.Id)).SelectMany(t => t.TaskStudyList)
|
||||
// .ProjectTo<DicomStudyBasicInfo>(_mapper.ConfigurationProvider).ToList();
|
||||
|
||||
var preNoneDicomStudyList = _subjectVisitRepository.Where(t => subjectVisitIdList.Contains(t.Id))
|
||||
.SelectMany(t => t.NoneDicomStudyList)
|
||||
@@ -1338,8 +1439,11 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
|
||||
foreach (var item in list)
|
||||
{
|
||||
item.DicomStudyList.Where(t => inQuery.IsImageSegmentLabel == false ? t.ModalityForEdit == "XA" : true).ToList()
|
||||
.AddRange(preDicomStudyList.Where(t => t.SubjectVisitId == item.SourceSubjectVisitId).ToList());
|
||||
var dicomList = item.DicomStudyList.Where(t => inQuery.IsImageSegmentLabel == false ? t.ModalityForEdit == "XA" || t.ModalityForEdit == "DSA" || t.ModalityForEdit == "OCT" : true).ToList();
|
||||
|
||||
//dicomList.AddRange(preDicomStudyList.Where(t => t.SubjectVisitId == item.SourceSubjectVisitId).ToList());
|
||||
|
||||
item.DicomStudyList = dicomList;
|
||||
|
||||
item.NoneDicomStudyList = _mapper.Map<List<NoneDicomStudyBasicInfo>>(preNoneDicomStudyList.Where(t => t.SubjectVisitId == item.SourceSubjectVisitId).ToList());
|
||||
}
|
||||
@@ -1412,7 +1516,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
public async Task<IResponseOutput> GetIRReadingDownloadStudyInfo(IRDownloadQuery inQuery)
|
||||
{
|
||||
var info = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == inQuery.TrialReadingCriterionId)
|
||||
.Select(t => new { t.IsImageFilter, t.CriterionModalitys, t.TrialId, t.IsReadingTaskViewInOrder }).FirstNotNullAsync();
|
||||
.Select(t => new { t.IsImageFilter, t.CriterionModalitys, t.TrialId, t.IsReadingTaskViewInOrder, t.Trial.TrialCode }).FirstNotNullAsync();
|
||||
|
||||
var isQueryDicom = inQuery.DicomStudyIdList.Count > 0;
|
||||
var isQueryNoneDicom = inQuery.NoneDicomStudyIdList.Count > 0;
|
||||
@@ -1430,12 +1534,12 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
#region 在下载前先处理DIR文件
|
||||
|
||||
//有传输语法值的导出 才生成DIR
|
||||
if (_subjectVisitRepository.Where(t => t.SubjectId == inQuery.SubjectId).SelectMany(t => t.StudyList.SelectMany(t => t.InstanceList)).All(c => c.TransferSytaxUID != string.Empty))
|
||||
if (_subjectVisitRepository.Where(t => t.SubjectId == inQuery.SubjectId && t.CheckState == CheckStateEnum.CVPassed).SelectMany(t => t.StudyList.SelectMany(t => t.InstanceList)).All(c => c.TransferSytaxUID != string.Empty))
|
||||
{
|
||||
var dirInfolist = _subjectRepository.Where(t => t.Id == inQuery.SubjectId).SelectMany(t => t.SubjectVisitList.Where(t => subjectVisitIdList.Contains(t.Id))).SelectMany(t => t.StudyList)
|
||||
.Where(t => isQueryDicom ? inQuery.DicomStudyIdList.Contains(t.Id) : false)
|
||||
.Where(t => info.IsImageFilter ? ("|" + info.CriterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|") : true)
|
||||
.Where(t => inQuery.IsImageSegmentLabel == false ? t.ModalityForEdit == "XA" : true)
|
||||
.Where(t => info.IsImageFilter && inQuery.IsImageSegmentLabel == null ? ("|" + info.CriterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|") : true)
|
||||
.Where(t => inQuery.IsImageSegmentLabel == false ? t.ModalityForEdit == "XA" || t.ModalityForEdit == "DSA" : true)
|
||||
.SelectMany(t => t.InstanceList.Where(t => t.IsReading && t.DicomSerie.IsReading))
|
||||
.Select(t => new StudyDIRInfo()
|
||||
{
|
||||
@@ -1443,7 +1547,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
|
||||
DicomStudyId = t.DicomStudy.Id,
|
||||
|
||||
PatientId = t.DicomStudy.PatientId,
|
||||
PatientId = info.TrialCode + "-" + t.DicomStudy.PatientId,
|
||||
PatientName = t.DicomStudy.PatientName,
|
||||
PatientBirthDate = t.DicomStudy.PatientBirthDate,
|
||||
PatientSex = t.DicomStudy.PatientSex,
|
||||
@@ -1478,11 +1582,12 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
var taskDirInfolist = _taskInstanceRepository.Where(t => t.SubjectVisitId != null)
|
||||
.Where(t => subjectVisitIdList.Contains((Guid)t.SubjectVisitId)).Select(t => new StudyDIRInfo()
|
||||
{
|
||||
IsTaskStudy = true,
|
||||
SubjectVisitId = (Guid)t.SubjectVisitId,
|
||||
|
||||
DicomStudyId = t.TaskStudy.Id,
|
||||
|
||||
PatientId = t.TaskStudy.PatientId,
|
||||
PatientId = info.TrialCode + "-" + t.TaskStudy.Subject.Code,
|
||||
PatientName = t.TaskStudy.PatientName,
|
||||
PatientBirthDate = t.TaskStudy.PatientBirthDate,
|
||||
PatientSex = t.TaskStudy.PatientSex,
|
||||
@@ -1521,14 +1626,25 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
{
|
||||
var visitId = item.First().SubjectVisitId;
|
||||
|
||||
var isTaskStudy = item.First().IsTaskStudy;
|
||||
|
||||
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));
|
||||
|
||||
|
||||
if (isSucess)
|
||||
{
|
||||
await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/DICOMDIR" });
|
||||
if (isTaskStudy)
|
||||
{
|
||||
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}/{dirDic[$"{studyUid}_DICOMDIR"]}" });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1536,23 +1652,23 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
|
||||
#endregion
|
||||
|
||||
var query = from sv in _subjectRepository.Where(t => t.Id == inQuery.SubjectId).SelectMany(t => t.SubjectVisitList.Where(t => subjectVisitIdList.Contains(t.Id)))
|
||||
var query = from sv in _subjectRepository.Where(t => t.Id == inQuery.SubjectId).SelectMany(t => t.SubjectVisitList.Where(t => subjectVisitIdList.Contains(t.Id) && t.CheckState == CheckStateEnum.CVPassed))
|
||||
//一致性分析,导致查询出来两条数据
|
||||
join visitTask in _visitTaskRepository.Where(t => taskIdList.Contains(t.Id)) on sv.Id equals visitTask.SourceSubjectVisitId into cc
|
||||
from leftVisitTask in cc.DefaultIfEmpty()
|
||||
select new ImageDownloadDto()
|
||||
{
|
||||
TrialId=sv.TrialId,
|
||||
SubjectId=sv.SubjectId,
|
||||
TrialSiteCode=sv.TrialSite.TrialSiteCode,
|
||||
VisitId=sv.Id,
|
||||
TrialId = sv.TrialId,
|
||||
SubjectId = sv.SubjectId,
|
||||
TrialSiteCode = sv.TrialSite.TrialSiteCode,
|
||||
VisitId = sv.Id,
|
||||
|
||||
SubjectCode = inQuery.SubjectCode,
|
||||
VisitName = sv.VisitName,
|
||||
TaskBlindName = leftVisitTask.TaskBlindName,
|
||||
StudyList = sv.StudyList.Where(t => isQueryDicom ? inQuery.DicomStudyIdList.Contains(t.Id) : false)
|
||||
.Where(t => info.IsImageFilter ? ("|" + info.CriterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|") : true)
|
||||
.Where(t => inQuery.IsImageSegmentLabel == false ? t.ModalityForEdit == "XA" : true)
|
||||
.Where(t => info.IsImageFilter && inQuery.IsImageSegmentLabel == null ? ("|" + info.CriterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|") : true)
|
||||
.Where(t => inQuery.IsImageSegmentLabel == false ? t.ModalityForEdit == "XA" || t.ModalityForEdit == "DSA" : true)
|
||||
.Select(u => new DownloadDicomStudyDto()
|
||||
{
|
||||
PatientId = u.PatientId,
|
||||
@@ -1576,35 +1692,35 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
}).ToList()
|
||||
}).ToList(),
|
||||
|
||||
TaskStudyList =
|
||||
sv.TaskStudyList.Where(t => isQueryDicom ? inQuery.DicomStudyIdList.Contains(t.Id) : false)
|
||||
//.Where(t => info.IsImageFilter ? ("|" + info.CriterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|") : true)
|
||||
.Select(u => new DownloadDicomStudyDto()
|
||||
{
|
||||
PatientId = u.PatientId,
|
||||
StudyTime = u.StudyTime,
|
||||
StudyCode = u.StudyCode,
|
||||
StudyInstanceUid = u.StudyInstanceUid,
|
||||
StudyDIRPath = u.StudyDIRPath,
|
||||
//TaskStudyList =
|
||||
//sv.TaskStudyList.Where(t => isQueryDicom ? inQuery.DicomStudyIdList.Contains(t.Id) : false)
|
||||
////.Where(t => info.IsImageFilter ? ("|" + info.CriterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|") : true)
|
||||
//.Select(u => new DownloadDicomStudyDto()
|
||||
//{
|
||||
// PatientId = u.PatientId,
|
||||
// StudyTime = u.StudyTime,
|
||||
// StudyCode = u.StudyCode,
|
||||
// StudyInstanceUid = u.StudyInstanceUid,
|
||||
// StudyDIRPath = u.StudyDIRPath,
|
||||
|
||||
SeriesList = u.SeriesList.Select(z => new DownloadDicomSeriesDto()
|
||||
{
|
||||
Modality = z.Modality,
|
||||
// SeriesList = u.SeriesList.Select(z => new DownloadDicomSeriesDto()
|
||||
// {
|
||||
// Modality = z.Modality,
|
||||
|
||||
InstanceList = z.InstanceList.Select(k => new DownloadDicomInstanceDto()
|
||||
{
|
||||
IsEncapsulated = k.IsEncapsulated,
|
||||
InstanceId = k.Id,
|
||||
FileName = string.Empty,
|
||||
Path = k.Path,
|
||||
FileSize = k.FileSize
|
||||
}).ToList()
|
||||
}).ToList()
|
||||
}).ToList()
|
||||
,
|
||||
// InstanceList = z.InstanceList.Select(k => new DownloadDicomInstanceDto()
|
||||
// {
|
||||
// IsEncapsulated = k.IsEncapsulated,
|
||||
// InstanceId = k.Id,
|
||||
// FileName = string.Empty,
|
||||
// Path = k.Path,
|
||||
// FileSize = k.FileSize
|
||||
// }).ToList()
|
||||
// }).ToList()
|
||||
//}).ToList()
|
||||
//,
|
||||
|
||||
NoneDicomStudyList = inQuery.IsImageSegmentLabel != false ? sv.NoneDicomStudyList.Where(t => isQueryNoneDicom ? inQuery.NoneDicomStudyIdList.Contains(t.Id) : false)
|
||||
.Where(t => info.IsImageFilter ? ("|" + info.CriterionModalitys + "|").Contains("|" + t.Modality + "|") : true)
|
||||
.Where(t => info.IsImageFilter && inQuery.IsImageSegmentLabel == null ? ("|" + info.CriterionModalitys + "|").Contains("|" + t.Modality + "|") : true)
|
||||
.Where(t => t.IsReading)
|
||||
.Select(nd => new DownloadNoneDicomStudyDto()
|
||||
{
|
||||
@@ -1648,7 +1764,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
//靶段标注处理
|
||||
if (inQuery.IsImageSegmentLabel == false)
|
||||
{
|
||||
result.StudyList.AddRange(result.TaskStudyList);
|
||||
//result.StudyList.AddRange(result.TaskStudyList);
|
||||
}
|
||||
|
||||
foreach (var item in result.StudyList.SelectMany(t => t.SeriesList).SelectMany(t => t.InstanceList))
|
||||
@@ -1696,11 +1812,14 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
[HttpPost]
|
||||
public async Task<IResponseOutput> GetTaskUploadedDicomStudyList(IRTaskUploadedDicomStudyQuery inQuery)
|
||||
{
|
||||
var info = await _visitTaskRepository.Where(t => t.Id == inQuery.VisitTaskId)
|
||||
var info = await _visitTaskRepository
|
||||
.WhereIf(inQuery.VisitTaskId != null, t => t.Id == inQuery.VisitTaskId)//正常后处理
|
||||
.Select(t => new { t.TrialReadingCriterion.IsImageFilter, t.TrialReadingCriterion.CriterionModalitys }).FirstNotNullAsync();
|
||||
|
||||
var query = _taskStudyRepository.Where(t => t.VisitTaskId == inQuery.VisitTaskId)
|
||||
.Where(t => info.IsImageFilter ? ("|" + info.CriterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|") : true)
|
||||
var query = _taskStudyRepository
|
||||
.WhereIf(inQuery.VisitTaskId != null, t => t.VisitTaskId == inQuery.VisitTaskId)//正常后处理
|
||||
.WhereIf(inQuery.VisitTaskId == null && inQuery.SubjectVisitId != null, t => t.SubjectVisitId == inQuery.SubjectVisitId)//靶段标注处理的影像
|
||||
.WhereIf(info.IsImageFilter && inQuery.VisitTaskId != null, t => ("|" + info.CriterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|"))
|
||||
.Select(t => new
|
||||
{
|
||||
StudyId = t.Id,
|
||||
@@ -1890,6 +2009,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
|
||||
var isExportReading = inCommand.IsExportReading == true;
|
||||
|
||||
var dirDic = new Dictionary<string, string>();
|
||||
|
||||
if (inCommand.IsKeyImage)
|
||||
{
|
||||
var downloadInfoList = _visitTaskRepository.Where(t => t.TrialId == inCommand.TrialId && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global)
|
||||
@@ -2137,6 +2258,77 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
}
|
||||
else
|
||||
{
|
||||
#region 在下载前先处理DIR文件
|
||||
|
||||
//sql 超时
|
||||
//_subjectVisitRepository.Where(t => inCommand.SubjectVisitIdList.Contains(t.Id))
|
||||
// .SelectMany(t => t.StudyList.SelectMany(t => t.InstanceList.Where(t => isExportReading ? (t.IsReading && t.DicomSerie.IsReading) : true)))
|
||||
// .All(c => c.TransferSytaxUID != string.Empty)
|
||||
//有传输语法值的导出 才生成DIR
|
||||
if (!_instanceRepository.Where(t => isExportReading ? (t.IsReading && t.DicomSerie.IsReading) : true)
|
||||
.Where(t => inCommand.SubjectVisitIdList.Contains(t.SubjectVisitId)).Any(c => c.TransferSytaxUID == string.Empty))
|
||||
{
|
||||
var dirInfolist = _subjectVisitRepository.Where(t => inCommand.SubjectVisitIdList.Contains(t.Id)).SelectMany(t => t.StudyList)
|
||||
.SelectMany(t => t.InstanceList.Where(t => isExportReading ? (t.IsReading && t.DicomSerie.IsReading) : true))
|
||||
.Select(t => new StudyDIRInfo()
|
||||
{
|
||||
SubjectId = t.SubjectId,
|
||||
SubjectVisitId = t.SubjectVisitId,
|
||||
|
||||
DicomStudyId = t.DicomStudy.Id,
|
||||
|
||||
PatientId = t.Trial.TrialCode + "-" + t.DicomStudy.Subject.Code,
|
||||
PatientName = t.DicomStudy.PatientName,
|
||||
PatientBirthDate = t.DicomStudy.PatientBirthDate,
|
||||
PatientSex = t.DicomStudy.PatientSex,
|
||||
|
||||
StudyInstanceUid = t.StudyInstanceUid,
|
||||
StudyId = t.DicomStudy.StudyId,
|
||||
DicomStudyDate = t.DicomStudy.DicomStudyDate,
|
||||
DicomStudyTime = t.DicomStudy.DicomStudyTime,
|
||||
AccessionNumber = t.DicomStudy.AccessionNumber,
|
||||
|
||||
StudyDescription = t.DicomStudy.Description,
|
||||
|
||||
SeriesInstanceUid = t.DicomSerie.SeriesInstanceUid,
|
||||
Modality = t.DicomSerie.Modality,
|
||||
DicomSeriesDate = t.DicomSerie.DicomSeriesDate,
|
||||
DicomSeriesTime = t.DicomSerie.DicomSeriesTime,
|
||||
SeriesNumber = t.DicomSerie.SeriesNumber,
|
||||
SeriesDescription = t.DicomSerie.Description,
|
||||
|
||||
InstanceId = t.Id,
|
||||
SopInstanceUid = t.SopInstanceUid,
|
||||
SOPClassUID = t.SOPClassUID,
|
||||
InstanceNumber = t.InstanceNumber,
|
||||
MediaStorageSOPClassUID = t.MediaStorageSOPClassUID,
|
||||
MediaStorageSOPInstanceUID = t.MediaStorageSOPInstanceUID,
|
||||
TransferSytaxUID = t.TransferSytaxUID,
|
||||
|
||||
}).ToList();
|
||||
|
||||
|
||||
|
||||
foreach (var item in dirInfolist.GroupBy(t => new { t.StudyInstanceUid, t.DicomStudyId }))
|
||||
{
|
||||
var visitId = item.First().SubjectVisitId;
|
||||
|
||||
var subjectId = item.First().SubjectId;
|
||||
|
||||
var studyUid = item.Key.StudyInstanceUid;
|
||||
|
||||
var ossFolder = $"{inCommand.TrialId}/Image/{subjectId}/{visitId}/{item.Key.StudyInstanceUid}";
|
||||
|
||||
var isSucess = await SafeBussinessHelper.RunAsync(async () => await DicomDIRHelper.GenerateStudyDIRAndUploadAsync(item.ToList(), dirDic, ossFolder, _oSSService));
|
||||
|
||||
if (isSucess)
|
||||
{
|
||||
await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/{dirDic[$"{studyUid}_DICOMDIR"]}" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
var downloadInfo = _trialRepository.Where(t => t.Id == inCommand.TrialId).Select(t => new
|
||||
{
|
||||
@@ -2152,14 +2344,19 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
u.PatientId,
|
||||
u.StudyTime,
|
||||
u.StudyCode,
|
||||
StudyDIRPath = u.StudyDIRPath,
|
||||
|
||||
SeriesList = u.SeriesList.Where(t => isExportReading ? t.IsReading : true).Select(z => new
|
||||
{
|
||||
z.Modality,
|
||||
|
||||
InstancePathList = z.DicomInstanceList.Where(t => isExportReading ? t.IsReading : true).Select(k => new
|
||||
InstanceList = z.DicomInstanceList.Where(t => isExportReading ? t.IsReading : true).Select(k => new DownloadDicomInstanceDto()
|
||||
{
|
||||
k.Path
|
||||
IsEncapsulated = k.IsEncapsulated,
|
||||
InstanceId = k.Id,
|
||||
FileName = string.Empty,
|
||||
Path = k.Path,
|
||||
FileSize = k.FileSize
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2183,6 +2380,18 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
}).FirstOrDefault();
|
||||
|
||||
|
||||
|
||||
|
||||
foreach (var item in downloadInfo.VisitList.SelectMany(t => t.StudyList.SelectMany(t => t.SeriesList).SelectMany(t => t.InstanceList)))
|
||||
{
|
||||
var key = item.InstanceId.ToString();
|
||||
if (dirDic.ContainsKey(key))
|
||||
{
|
||||
item.FileName = dirDic[key];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return ResponseOutput.Ok(downloadInfo);
|
||||
}
|
||||
|
||||
@@ -2202,23 +2411,27 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||
[HttpPost]
|
||||
public async Task<IResponseOutput<PageOutput<SubjectVisitMarkDTO>>> GetTrialSubjectVisitMarkList(SubjectVisitMarkQuery inQuery)
|
||||
{
|
||||
var list = await _subjectRepository.Where(t => t.TrialId == inQuery.TrialId)
|
||||
var list = await _subjectRepository.Where(t => t.TrialId == inQuery.TrialId && t.SubjectDoctorList.Any())
|
||||
.WhereIf(inQuery.SubjectCode.IsNotNullOrEmpty(), t => t.Code.Contains(inQuery.SubjectCode))
|
||||
.WhereIf(inQuery.IsUrgent != null, t => t.IsUrgent == inQuery.IsUrgent)
|
||||
.Select(t => new SubjectVisitMarkDTO()
|
||||
{
|
||||
TrialId = t.TrialId,
|
||||
SubjectId = t.Id,
|
||||
SubjectCode=t.Code,
|
||||
SubjectCode = t.Code,
|
||||
IsUrgent = t.IsUrgent,
|
||||
VisitCount = t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted).Count(),
|
||||
MarkVisitCount = t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted).Count(t => t.TaskStudyList.Any()),
|
||||
|
||||
DicomStudyCount = t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted).SelectMany(t => t.StudyList).Count(),
|
||||
MarkDicomStudyCount = t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted).SelectMany(t => t.TaskStudyList).Count(),
|
||||
VisitCount = t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted && t.CheckState == CheckStateEnum.CVPassed).Count(),
|
||||
|
||||
NoneDicomStudyCount = t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted).SelectMany(t => t.NoneDicomStudyList).Count(),
|
||||
MarkNoneDicomStudyCount = t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted).SelectMany(t => t.NoneDicomStudyList).Where(t=>t.ImageLabelNoneDicomFileList.Any()).Count(),
|
||||
MarkIVUSVisitCount = t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted && t.CheckState == CheckStateEnum.CVPassed).Count(t => t.NoneDicomStudyList.Where(t => t.Modality == "IVUS").SelectMany(c => c.ImageLabelNoneDicomFileList).Any()),
|
||||
|
||||
MarkOCTVisitCount = t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted && t.CheckState == CheckStateEnum.CVPassed).Count(t => t.NoneDicomStudyList.Where(t => t.Modality == "OCT").SelectMany(c => c.ImageLabelNoneDicomFileList).Any()),
|
||||
|
||||
DicomStudyCount = t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted && t.CheckState == CheckStateEnum.CVPassed).SelectMany(t => t.StudyList).Count(),
|
||||
//MarkDicomStudyCount = t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted).SelectMany(t => t.TaskStudyList).Count(),
|
||||
|
||||
NoneDicomStudyCount = t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted && t.CheckState == CheckStateEnum.CVPassed).SelectMany(t => t.NoneDicomStudyList).Count(),
|
||||
//MarkNoneDicomStudyCount = t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted).SelectMany(t => t.NoneDicomStudyList).Where(t => t.ImageLabelNoneDicomFileList.Any()).Count(),
|
||||
}).ToPagedListAsync(inQuery);
|
||||
|
||||
return ResponseOutput.Ok(list);
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
// 生成时间 2021-12-06 10:54:55
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
||||
//--------------------------------------------------------------------
|
||||
using DocumentFormat.OpenXml.EMMA;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Medallion.Threading;
|
||||
@@ -34,7 +36,8 @@ namespace IRaCIS.Core.Application.Contracts
|
||||
[FromQuery] Guid? nonedicomStudyId,
|
||||
[FromQuery] bool isFilterZip,
|
||||
[FromQuery] Guid? visitTaskId,
|
||||
[FromQuery] bool isReading)
|
||||
[FromQuery] bool isReading,
|
||||
[FromQuery] bool? isImageSegmentLabel)
|
||||
{
|
||||
var qcAuditState = await _subjectVisitRepository.Where(s => s.Id == subjectVisitId).Select(t => t.AuditState).FirstOrDefaultAsync();
|
||||
|
||||
@@ -44,33 +47,74 @@ namespace IRaCIS.Core.Application.Contracts
|
||||
//质控过程中并且不是IQC时,可以看到删除的(不需要忽略过滤器) 质控中iqc 也需要看到删除的
|
||||
var isViewDelete = !isQCFinished;
|
||||
|
||||
var isFilterIVUSNoneDicom = false;
|
||||
|
||||
CriterionType? criterionType = null;
|
||||
|
||||
IQueryable<NoneDicomStudyView> noneDicomStudyQueryable = default;
|
||||
if (visitTaskId == null)
|
||||
if (visitTaskId == null || visitTaskId == Guid.Empty)
|
||||
{
|
||||
//质控过程中,需要忽略过滤质控设置删除的检查,以及设置删除的文件,质控通过后才
|
||||
noneDicomStudyQueryable = _noneDicomStudyRepository.Where(t => t.SubjectVisitId == subjectVisitId, ignoreQueryFilters: isViewDelete)
|
||||
.WhereIf(nonedicomStudyId != null, t => t.Id == nonedicomStudyId)
|
||||
.WhereIf(isReading, t => t.IsReading && t.IsDeleted == false)
|
||||
if (visitTaskId == null)
|
||||
{
|
||||
|
||||
//质控过程中,需要忽略过滤质控设置删除的检查,以及设置删除的文件,质控通过后才
|
||||
noneDicomStudyQueryable = _noneDicomStudyRepository.Where(t => t.SubjectVisitId == subjectVisitId, ignoreQueryFilters: isViewDelete)
|
||||
.WhereIf(nonedicomStudyId != null, t => t.Id == nonedicomStudyId)
|
||||
.WhereIf(isReading, t => t.IsReading && t.IsDeleted == false)
|
||||
.ProjectTo<NoneDicomStudyView>(_mapper.ConfigurationProvider, new { isFilterZip = isFilterZip, isReading = isReading });
|
||||
}
|
||||
else
|
||||
{
|
||||
//靶段标注上传后查看影像
|
||||
|
||||
noneDicomStudyQueryable = _noneDicomStudyRepository.Where(t => t.SubjectVisitId == subjectVisitId, ignoreQueryFilters: isViewDelete)
|
||||
.WhereIf(isReading, t => t.IsReading && t.IsDeleted == false)
|
||||
.WhereIf(nonedicomStudyId != null, t => t.Id == nonedicomStudyId)
|
||||
.ProjectTo<ImageLabelNoneDicomStudyView>(_mapper.ConfigurationProvider, new { isFilterZip = isFilterZip, isReading = isReading });
|
||||
|
||||
}
|
||||
|
||||
|
||||
.ProjectTo<NoneDicomStudyView>(_mapper.ConfigurationProvider, new { isFilterZip = isFilterZip, isReading = isReading });
|
||||
//ivus-在访视的时候visitTaskId==null 需要过滤掉空检查 ,但是在靶段标注的查看访视级别的时候不能过滤
|
||||
if (_subjectVisitRepository.Where(t => t.Id == subjectVisitId).SelectMany(t => t.Trial.TrialReadingCriterionList)
|
||||
.Where(t => t.CriterionType == CriterionType.IVUS || t.CriterionType == CriterionType.OCT).Distinct().Count() == 2
|
||||
&& visitTaskId == null)
|
||||
{
|
||||
isFilterIVUSNoneDicom = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).Select(t => new { t.BlindSubjectCode, t.TrialReadingCriterionId, t.TrialReadingCriterion.IsImageFilter, t.TrialReadingCriterion.CriterionModalitys }).FirstNotNullAsync();
|
||||
var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).Select(t => new { t.BlindSubjectCode, t.TrialReadingCriterionId, t.TrialReadingCriterion.CriterionType, t.TrialReadingCriterion.IsImageFilter, t.TrialReadingCriterion.CriterionModalitys }).FirstNotNullAsync();
|
||||
criterionType = taskinfo.CriterionType;
|
||||
|
||||
if ((taskinfo.CriterionType == CriterionType.IVUS || taskinfo.CriterionType == CriterionType.OCT) && isImageSegmentLabel == false)
|
||||
{
|
||||
//后处理原始影像预览
|
||||
noneDicomStudyQueryable = _noneDicomStudyRepository.Where(t => t.SubjectVisitId == subjectVisitId, ignoreQueryFilters: isViewDelete)
|
||||
.WhereIf(isReading, t => t.IsReading && t.IsDeleted == false)
|
||||
.WhereIf(nonedicomStudyId != null, t => t.Id == nonedicomStudyId)
|
||||
.Where(t => taskinfo.IsImageFilter ? ("|" + taskinfo.CriterionModalitys + "|").Contains("|" + t.Modality + "|") : true)
|
||||
.ProjectTo<ImageLabelNoneDicomStudyView>(_mapper.ConfigurationProvider, new { isFilterZip = isFilterZip, visiTaskId = visitTaskId, isReading = isReading });
|
||||
}
|
||||
else
|
||||
{
|
||||
noneDicomStudyQueryable = _noneDicomStudyRepository.Where(t => t.TaskNoneDicomFileList.Any(t => t.VisitTaskId == visitTaskId), ignoreQueryFilters: isViewDelete)
|
||||
.WhereIf(isReading, t => t.IsReading && t.IsDeleted == false)
|
||||
.Where(t => taskinfo.IsImageFilter ? ("|" + taskinfo.CriterionModalitys + "|").Contains("|" + t.Modality + "|") : true)
|
||||
.WhereIf(nonedicomStudyId != null, t => t.Id == nonedicomStudyId)
|
||||
.ProjectTo<TaskDicomStudyView>(_mapper.ConfigurationProvider, new { isFilterZip = isFilterZip, visiTaskId = visitTaskId, isReading = isReading });
|
||||
}
|
||||
|
||||
|
||||
noneDicomStudyQueryable = _noneDicomStudyRepository.Where(t => t.TaskNoneDicomFileList.Any(t => t.VisitTaskId == visitTaskId), ignoreQueryFilters: isViewDelete)
|
||||
.WhereIf(isReading, t => t.IsReading && t.IsDeleted == false)
|
||||
.Where(t => taskinfo.IsImageFilter ? ("|" + taskinfo.CriterionModalitys + "|").Contains("|" + t.Modality + "|") : true)
|
||||
.WhereIf(nonedicomStudyId != null, t => t.Id == nonedicomStudyId)
|
||||
.ProjectTo<TaskDicomStudyView>(_mapper.ConfigurationProvider, new { isFilterZip = isFilterZip, visiTaskId = visitTaskId, isReading = isReading });
|
||||
}
|
||||
|
||||
|
||||
var list = await noneDicomStudyQueryable.OrderBy(x => x.ImageDate).ThenBy(x => x.CreateTime).ToListAsync();
|
||||
|
||||
var config = await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { t.Trial.ImageFormatList, t.Trial.StudyNameList, t.Trial.IsShowStudyName, AuditState = qcAuditState }).FirstOrDefaultAsync();
|
||||
var list = await noneDicomStudyQueryable.Where(t => isFilterIVUSNoneDicom ? t.Modality != "IVUS" : true)
|
||||
.OrderBy(x => x.ImageDate).ThenBy(x => x.CreateTime).ToListAsync();
|
||||
|
||||
var config = await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { t.Trial.ImageFormatList, t.Trial.StudyNameList, t.Trial.IsShowStudyName, AuditState = qcAuditState, CriterionType=criterionType }).FirstOrDefaultAsync();
|
||||
return ResponseOutput.Ok(list, config);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -92,6 +92,9 @@ namespace IRaCIS.Core.Application.Service
|
||||
CreateMap<DicomSeries, DicomSeriesDTO>()
|
||||
.ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.SubjectVisit.Subject.Code))
|
||||
.ForMember(o => o.VisitName, t => t.MapFrom(u => u.SubjectVisit.VisitName));
|
||||
CreateMap<TaskSeries, DicomSeriesDTO>()
|
||||
.ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.SubjectVisit.Subject.Code))
|
||||
.ForMember(o => o.VisitName, t => t.MapFrom(u => u.SubjectVisit.VisitName));
|
||||
CreateMap<SCPSeries, DicomSeriesDTO>();
|
||||
|
||||
|
||||
|
||||
@@ -846,10 +846,26 @@ namespace IRaCIS.Core.Application.Service
|
||||
if (jsonObject["DictionaryCode"] != null && jsonObject["DictionaryCode"].ToString() != string.Empty)
|
||||
{
|
||||
|
||||
jsonObject[item.Code] = await _dictionaryRepository.Where(x => x.Code == jsonObject["DictionaryCode"].ToString()).Join(_dictionaryRepository.Where(x => x.Code == jsonObject[item.Code].ToString()), a => a.Id, b => b.ParentId, (a, b) => new
|
||||
|
||||
if (jsonObject[item.Code].ToString().Contains(","))
|
||||
{
|
||||
value = _userInfo.IsEn_Us ? b.Value : b.ValueCN
|
||||
}).Select(x => x.value).FirstOrDefaultAsync();
|
||||
var data = jsonObject[item.Code].ToString().Split(',').ToList();
|
||||
|
||||
var codeList = await _dictionaryRepository.Where(x => x.Code == jsonObject["DictionaryCode"].ToString()).Join(_dictionaryRepository.Where(x => data.Contains(x.Code)), a => a.Id, b => b.ParentId, (a, b) => new
|
||||
{
|
||||
value = _userInfo.IsEn_Us ? b.Value : b.ValueCN
|
||||
}).Select(x => x.value).ToListAsync();
|
||||
jsonObject[item.Code] = string.Join(",", codeList);
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonObject[item.Code] = await _dictionaryRepository.Where(x => x.Code == jsonObject["DictionaryCode"].ToString()).Join(_dictionaryRepository.Where(x => x.Code == jsonObject[item.Code].ToString()), a => a.Id, b => b.ParentId, (a, b) => new
|
||||
{
|
||||
value = _userInfo.IsEn_Us ? b.Value : b.ValueCN
|
||||
}).Select(x => x.value).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
jsonList.Add(jsonObject);
|
||||
@@ -917,10 +933,25 @@ namespace IRaCIS.Core.Application.Service
|
||||
//通过字典项的code 翻译 枚举或者 bool
|
||||
else
|
||||
{
|
||||
jsonDataDic[item.Key] = await _dictionaryRepository.Where(x => x.Code == item.Code).Join(_dictionaryRepository.Where(x => x.Code == value.ToString()), a => a.Id, b => b.ParentId, (a, b) => new
|
||||
if (value.ToString().Contains(","))
|
||||
{
|
||||
value = _userInfo.IsEn_Us ? b.Value : b.ValueCN
|
||||
}).Select(x => x.value).FirstOrDefaultAsync();
|
||||
var data = value.ToString().Split(',').ToList();
|
||||
|
||||
var codeList = await _dictionaryRepository.Where(x => x.Code == item.Code).Join(_dictionaryRepository.Where(x => data.Contains(x.Code)), a => a.Id, b => b.ParentId, (a, b) => new
|
||||
{
|
||||
value = _userInfo.IsEn_Us ? b.Value : b.ValueCN
|
||||
}).Select(x => x.value).ToListAsync();
|
||||
jsonDataDic[item.Key] = string.Join(",", codeList);
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonDataDic[item.Key] = await _dictionaryRepository.Where(x => x.Code == item.Code).Join(_dictionaryRepository.Where(x => x.Code == value.ToString()), a => a.Id, b => b.ParentId, (a, b) => new
|
||||
{
|
||||
value = _userInfo.IsEn_Us ? b.Value : b.ValueCN
|
||||
}).Select(x => x.value).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,10 @@ namespace IRaCIS.Application.Contracts
|
||||
public string UserCode { get; set; }
|
||||
public string EMail { get; set; }
|
||||
|
||||
public string HiddenEmail { get; set; }
|
||||
|
||||
|
||||
|
||||
public int Status { get; set; }
|
||||
public bool IsTestUser { get; set; }
|
||||
public bool IsZhiZhun { get; set; }
|
||||
@@ -279,8 +283,15 @@ namespace IRaCIS.Application.Contracts
|
||||
public string OldPassWord { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class AdminResetUserPwdCommand
|
||||
{
|
||||
public Guid IdentityUserId { get; set; }
|
||||
|
||||
|
||||
public string BaseUrl { get; set; } = string.Empty;
|
||||
public string RouteUrl { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class UserAccountDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
Task<IResponseOutput> SendMFAEmail(SendMfaCommand sendMfa);
|
||||
Task<UserBasicInfo> GetUserBasicInfo(Guid userId, string pwd);
|
||||
Task<IResponseOutput> ModifyPassword(EditPasswordCommand editPwModel);
|
||||
Task<IResponseOutput> ResetPassword(Guid userId);
|
||||
Task<IResponseOutput> ResetPassword(AdminResetUserPwdCommand inCommand);
|
||||
|
||||
Task<IResponseOutput> UpdateUser(UserCommand model);
|
||||
//Task<IResponseOutput> UpdateUserState(Guid userId, UserStateEnum state);
|
||||
|
||||
@@ -286,17 +286,17 @@ namespace IRaCIS.Core.Application.Service
|
||||
/// <summary>
|
||||
/// 重置密码为 默认密码
|
||||
/// </summary>
|
||||
/// <param name="identityUserId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("{identityUserId:guid}")]
|
||||
[UnitOfWork]
|
||||
public async Task<IResponseOutput> ResetPassword(Guid identityUserId)
|
||||
[HttpPost]
|
||||
public async Task<IResponseOutput> ResetPassword(AdminResetUserPwdCommand inCommand)
|
||||
{
|
||||
var identityUserId = inCommand.IdentityUserId;
|
||||
|
||||
var pwd = IRCEmailPasswordHelper.GenerateRandomPassword(10);
|
||||
|
||||
|
||||
await _mailVerificationService.AdminResetPwdSendEmailAsync(identityUserId, pwd);
|
||||
await _mailVerificationService.AdminResetPwdSendEmailAsync(identityUserId, pwd, inCommand.BaseUrl);
|
||||
|
||||
await _identityUserRepository.UpdatePartialFromQueryAsync(t => t.Id == identityUserId, u => new IdentityUser()
|
||||
{
|
||||
@@ -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}");
|
||||
}
|
||||
|
||||
|
||||
@@ -925,11 +936,11 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet]
|
||||
public async Task<IResponseOutput> LoginOut(Guid identityUserId, Guid userRoleId)
|
||||
public async Task<IResponseOutput> LoginOut(Guid identityUserId, Guid? userRoleId)
|
||||
{
|
||||
await _fusionCache.RemoveAsync(CacheKeys.UserToken(identityUserId));
|
||||
|
||||
if (_identityUserRepository.Any(t => t.Id == identityUserId))
|
||||
if (_identityUserRepository.Any(t => t.Id == identityUserId) && userRoleId != null)
|
||||
{
|
||||
|
||||
var userName = await _userRoleRepository.Where(t => t.Id == userRoleId).Select(t => t.IdentityUser.UserName).FirstOrDefaultAsync();
|
||||
@@ -989,14 +1000,6 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var userLog = new UserLog();
|
||||
|
||||
if (failCount >= maxFailures)
|
||||
{
|
||||
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionUserName = userName, LoginPassword = password, OptType = UserOptType.TempLockLogin }, true);
|
||||
|
||||
//$"密码连续错误{maxFailures}次,当前账号已被限制登录,请等待 {lockoutMinutes} 分钟后再试。"
|
||||
throw new BusinessValidationFailedException(_localizer["User_ErrorLimit", maxFailures, lockoutMinutes]);
|
||||
}
|
||||
|
||||
var userLoginReturnModel = new IRCLoginReturnDTO();
|
||||
|
||||
|
||||
@@ -1008,7 +1011,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var isLoginUncommonly = false;
|
||||
|
||||
#region //登录用户是系统用户的时候,就要要记录异地登录
|
||||
#region 登录用户是系统用户的时候,就要要记录异地登录
|
||||
|
||||
//账号在系统存在
|
||||
if (isExistAccount || loginUser != null)
|
||||
@@ -1066,7 +1069,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
//异地登录
|
||||
loginUser.LoginState = 2;
|
||||
|
||||
|
||||
await _fusionCache.RemoveByTagAsync(CacheKeys.UserMFATag(loginUser.IdentityUserId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1082,6 +1085,14 @@ namespace IRaCIS.Core.Application.Service
|
||||
//错误次数累加
|
||||
failCount++;
|
||||
|
||||
if (failCount >= maxFailures)
|
||||
{
|
||||
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionUserName = userName, LoginPassword = password, OptType = UserOptType.TempLockLogin }, true);
|
||||
|
||||
//$"密码连续错误{maxFailures}次,当前账号已被限制登录,请等待 {lockoutMinutes} 分钟后再试。"
|
||||
throw new BusinessValidationFailedException(_localizer["User_ErrorLimit", maxFailures, lockoutMinutes]);
|
||||
}
|
||||
|
||||
await _fusionCache.SetAsync(cacheKey, failCount, TimeSpan.FromMinutes(lockoutMinutes));
|
||||
|
||||
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionUserName = userName, LoginPassword = password, OptType = UserOptType.AccountOrPasswordError, IsLoginUncommonly = isLoginUncommonly }, true);
|
||||
@@ -1176,22 +1187,27 @@ 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;
|
||||
|
||||
var hiddenEmail = IRCEmailPasswordHelper.MaskEmail(email);
|
||||
|
||||
userLoginReturnModel.BasicInfo.EMail = hiddenEmail;
|
||||
userLoginReturnModel.BasicInfo.HiddenEmail = hiddenEmail;
|
||||
|
||||
//修改密码 || 90天修改密码再mfa 之前
|
||||
if (userLoginReturnModel.BasicInfo.IsFirstAdd || userLoginReturnModel.BasicInfo.NeedChangePassWord)
|
||||
@@ -1207,6 +1223,10 @@ namespace IRaCIS.Core.Application.Service
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
userLoginReturnModel.BasicInfo.HiddenEmail = userLoginReturnModel.BasicInfo.EMail;
|
||||
}
|
||||
|
||||
|
||||
await _fusionCache.SetAsync(CacheKeys.UserToken(identityUserId), userLoginReturnModel.JWTStr, TimeSpan.FromDays(7));
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace IRaCIS.Core.Application.Contracts
|
||||
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
public bool IsBeMark { get; set; }
|
||||
|
||||
public DateTime? MarkTime { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace IRaCIS.Core.Application.Contracts
|
||||
{
|
||||
}
|
||||
|
||||
public class ImageLabelNoneDicomStudyView : NoneDicomStudyView
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
///<summary>NoneDicomStudyQuery 列表查询参数模型</summary>
|
||||
public class NoneDicomStudyQuery
|
||||
{
|
||||
|
||||
@@ -265,6 +265,7 @@ namespace IRaCIS.Core.Application.Contracts.DTO
|
||||
{
|
||||
public bool ExistsManual { get; set; }
|
||||
public bool IsHaveStudyClinicalData { get; set; }
|
||||
public bool QCRiskControl { get; set; }
|
||||
public SubjectClinicalDataDto SubjectClinicalData { get; set; } = new SubjectClinicalDataDto();
|
||||
|
||||
public List<NoneDicomStudyView> NoneDicomStudyList { get; set; } = new List<NoneDicomStudyView>();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
|
||||
using IRaCIS.Core.Application.Contracts.DTO;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MiniExcelLibs.Attributes;
|
||||
using Newtonsoft.Json;
|
||||
@@ -989,6 +990,13 @@ namespace IRaCIS.Core.Application.Contracts
|
||||
/// </summary>
|
||||
[DictionaryTranslateAttribute("YesOrNo")]
|
||||
public bool IsInvalid { get; set; }
|
||||
|
||||
|
||||
|
||||
public List<GetMedicalReviewDialogOutDto> DialogList { get; set; }
|
||||
|
||||
public string DialogStr => string.Join("\n\n", DialogList.OrderBy(t => t.CreateTime).Select(c => c.CreateUserName + " (" + ExportExcelConverterDate.DateTimeInternationalToString(c.CreateTime) + ") :" + c.ResultContent));
|
||||
|
||||
}
|
||||
|
||||
public class TrialImageDownloadExportDto
|
||||
@@ -1248,6 +1256,10 @@ namespace IRaCIS.Core.Application.Contracts
|
||||
//问题标识,肿瘤评估用于区分是什么问题
|
||||
public QuestionType? QuestionType { get; set; }
|
||||
|
||||
public OptionType OptionTypeEnum { get; set; }
|
||||
|
||||
public ValueUnit? Unit { get; set; }
|
||||
|
||||
|
||||
public Guid QuestionId { get; set; }
|
||||
public string QuestionName { get; set; }
|
||||
@@ -1413,6 +1425,7 @@ namespace IRaCIS.Core.Application.Contracts
|
||||
public string TableName { get; set; }
|
||||
#endregion
|
||||
|
||||
public OptionType OptionTypeEnum { get; set; }
|
||||
|
||||
public Guid TableQuesionId { get; set; }
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using MassTransit.Initializers;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using System.Linq;
|
||||
|
||||
@@ -25,6 +26,7 @@ namespace IRaCIS.Core.Application.Image.QA
|
||||
IRepository<DicomSeries> _dicomSeriesRepository,
|
||||
IRepository<NoneDicomStudy> _noneDicomStudyRepository,
|
||||
IRepository<QCChallenge> _qcChallengeRepository,
|
||||
IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig,
|
||||
IRepository<SubjectVisitImageBackRecord> _subjectVisitImageBackRecordReposiotry,
|
||||
IRepository<UserRole> _userRoleReposiotry,
|
||||
IReadingImageTaskService _IReadingImageTaskService, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IQCListService
|
||||
@@ -456,7 +458,7 @@ namespace IRaCIS.Core.Application.Image.QA
|
||||
return new TrialVisitQADTO
|
||||
{
|
||||
QCQuestionAnswerList = qacheckList,
|
||||
|
||||
QCRiskControl = _verifyConfig.CurrentValue.QCRiskControl,
|
||||
SecondReviewList = secondReviewList,
|
||||
|
||||
IsHaveStudyClinicalData = await _clinicalDataTrialSetRepository.AnyAsync(x => x.IsConfirm && x.TrialId == sv.TrialId && x.ClinicalDataLevel == ClinicalLevel.Study),
|
||||
@@ -772,7 +774,7 @@ namespace IRaCIS.Core.Application.Image.QA
|
||||
var query = _inspectionFileRepository.Where(t => t.TrialId == inQuery.TrialId && (t.CheckState == EDCCheckState.Success || t.CheckState == EDCCheckState.Failed))
|
||||
.ProjectTo<GetUserUploadFileDto>(_mapper.ConfigurationProvider);
|
||||
|
||||
return await query.ToPagedListAsync(inQuery);
|
||||
return await query.ToPagedListAsync(inQuery,nameof(GetUserUploadFileDto.CreateTime));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1981,6 +1981,23 @@ namespace IRaCIS.Core.Application.Image.QA
|
||||
SubjectId = dbSubjectVisit.SubjectId
|
||||
});
|
||||
|
||||
|
||||
if (await _readingQuestionCriterionTrialRepository.AnyAsync(x => x.Id == trialReadingCriterionId && x.IsOncologyReading))
|
||||
{
|
||||
await _readModuleRepository.AddAsync(new ReadModule()
|
||||
{
|
||||
TrialReadingCriterionId = trialReadingCriterionId,
|
||||
ReadingSetType = ReadingSetType.TumorReading,
|
||||
SubjectVisitId = dbSubjectVisit.Id,
|
||||
ModuleName = $"O-{dbSubjectVisit.VisitName}",
|
||||
ModuleType = ModuleTypeEnum.Oncology,
|
||||
ReadModuleAddTypeEnum = dbSubjectVisit.PDState == PDStateEnum.PDProgress ? ReadModuleAddType.PDConfirmation : ReadModuleAddType.FinalVisit,
|
||||
IsUrgent = dbSubjectVisit.IsUrgent,
|
||||
TrialId = dbSubjectVisit.TrialId,
|
||||
SubjectId = dbSubjectVisit.SubjectId
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -179,13 +179,14 @@ namespace IRaCIS.Core.Application.Service
|
||||
.ForMember(o => o.MedicalNo, t => t.MapFrom(u => u.VisitTask.Subject.MedicalNo))
|
||||
.ForMember(o => o.DoctorUserName, t => t.MapFrom(u => u.VisitTask.DoctorUser.UserName))
|
||||
.ForMember(o => o.MedicalManagerUserName, t => t.MapFrom(u => u.MedicalManagerUser.UserName))
|
||||
.ForMember(o => o.QuestionContent, t => t.MapFrom(u => string.Join("\n\n", u.ReadingMedicalReviewDialogList.Where(t => t.IsHaveQuestion).Select(t => t.Questioning))));
|
||||
.ForMember(o => o.QuestionContent, t => t.MapFrom(u => string.Join("\n\n", u.ReadingMedicalReviewDialogList.Where(t => t.IsHaveQuestion).Select(t => t.Questioning))))
|
||||
.ForMember(o => o.DialogList, t => t.MapFrom(u => u.ReadingMedicalReviewDialogList));
|
||||
|
||||
CreateMap<VisitTask, TaskMedicalReviewExportDto>()
|
||||
.ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionName))
|
||||
.ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.IsSelfAnalysis == true ? u.BlindTrialSiteCode : u.Subject.TrialSite.TrialSiteCode))
|
||||
.ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.IsSelfAnalysis == true ? u.BlindSubjectCode : u.Subject.Code))
|
||||
|
||||
|
||||
;
|
||||
|
||||
|
||||
@@ -245,6 +246,8 @@ namespace IRaCIS.Core.Application.Service
|
||||
.OrderBy(k => k.ReadingQuestionTrial.ShowOrder)
|
||||
.Select(c => new CommonQuesionInfo()
|
||||
{
|
||||
Unit=c.ReadingQuestionTrial.Unit,
|
||||
OptionTypeEnum=c.ReadingQuestionTrial.OptionTypeEnum,
|
||||
QuestionType = c.ReadingQuestionTrial.QuestionType,
|
||||
QuestionName = isEn_Us ? c.ReadingQuestionTrial.QuestionEnName : c.ReadingQuestionTrial.QuestionName,
|
||||
QuestionValue = c.IsGlobalChange ? c.GlobalChangeAnswer : c.Answer,
|
||||
@@ -284,7 +287,10 @@ namespace IRaCIS.Core.Application.Service
|
||||
.OrderBy(k => k.ReadingQuestionTrial.ShowOrder)
|
||||
.Select(c => new CommonQuesionInfo()
|
||||
{
|
||||
Unit=c.ReadingQuestionTrial.Unit,
|
||||
Group = isEn_Us ? c.ReadingQuestionTrial.GroupEnName : c.ReadingQuestionTrial.GroupName,
|
||||
QuestionType = c.ReadingQuestionTrial.QuestionType,
|
||||
OptionTypeEnum=c.ReadingQuestionTrial.OptionTypeEnum,
|
||||
QuestionId = c.ReadingQuestionTrial.Id,
|
||||
QuestionName = isEn_Us ? c.ReadingQuestionTrial.QuestionEnName : c.ReadingQuestionTrial.QuestionName,
|
||||
QuestionValue = c.IsGlobalChange ? c.GlobalChangeAnswer : c.Answer,
|
||||
@@ -303,9 +309,10 @@ namespace IRaCIS.Core.Application.Service
|
||||
.ForMember(o => o.LessionAnswerList, t => t.MapFrom(u => u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.ExportResultStr.Contains(((int)readingExportType).ToString()))));
|
||||
|
||||
CreateMap<ReadingTableQuestionAnswer, CommonLessionQuestionAnswerInfo>()
|
||||
//.ForMember(o => o.LessionCode, t => t.MapFrom(u => u.Lesion.RowMark))
|
||||
//.ForMember(o => o.LessionType, t => t.MapFrom(u => (int?)u.ReadingQuestionTrial.LesionType))
|
||||
.ForMember(o => o.ShowOrder, t => t.MapFrom(u => u.ReadingTableQuestionTrial.ShowOrder))
|
||||
//.ForMember(o => o.LessionCode, t => t.MapFrom(u => u.Lesion.RowMark))
|
||||
//.ForMember(o => o.LessionType, t => t.MapFrom(u => (int?)u.ReadingQuestionTrial.LesionType))
|
||||
.ForMember(o => o.OptionTypeEnum, t => t.MapFrom(u => u.ReadingTableQuestionTrial.OptionTypeEnum))
|
||||
.ForMember(o => o.ShowOrder, t => t.MapFrom(u => u.ReadingTableQuestionTrial.ShowOrder))
|
||||
.ForMember(o => o.CustomUnit, t => t.MapFrom(u => u.ReadingTableQuestionTrial.CustomUnit))
|
||||
.ForMember(o => o.Unit, t => t.MapFrom(u => u.ReadingTableQuestionTrial.Unit))
|
||||
.ForMember(o => o.TableQuesionId, t => t.MapFrom(u => u.ReadingTableQuestionTrial.Id))
|
||||
@@ -754,6 +761,13 @@ namespace IRaCIS.Core.Application.Service
|
||||
.OrderBy(t => t.CreateTime).ThenBy(t => t.FileName)))
|
||||
.ForMember(d => d.CodeView, u => u.MapFrom(s => s.StudyCode));
|
||||
|
||||
CreateMap<NoneDicomStudy, ImageLabelNoneDicomStudyView>()
|
||||
.ForMember(d => d.NoneDicomStudyFileList, u => u.MapFrom(s =>
|
||||
s.ImageLabelNoneDicomFileList.Where(t => isFilterZip ? !t.FileType.Contains(StaticData.FileType.Zip) : true)
|
||||
.Where(t => isReading ? t.IsReading && t.IsDeleted == false : true)
|
||||
.OrderBy(t => t.CreateTime).ThenBy(t => t.FileName)))
|
||||
.ForMember(d => d.CodeView, u => u.MapFrom(s => s.StudyCode));
|
||||
|
||||
|
||||
CreateMap<SubjectVisitImageBackRecord, ImageBackViewModel>()
|
||||
.ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.SubjectVisit.Subject.Code))
|
||||
|
||||
@@ -252,21 +252,50 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
/// <summary>
|
||||
/// 第一次
|
||||
/// </summary>
|
||||
public int FirstData { get; set; }
|
||||
public decimal FirstData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 第二次
|
||||
/// </summary>
|
||||
public int SecondData { get; set; }
|
||||
public decimal SecondData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 第三次
|
||||
/// </summary>
|
||||
public int ThirdData { get; set; }
|
||||
public decimal ThirdData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 巨噬细胞浸润测量
|
||||
/// </summary>
|
||||
public string MacrophageInfiltrationMeasurement { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 巨噬细胞浸润角度测量
|
||||
/// </summary>
|
||||
public decimal? MacrophageInfiltrationAngle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 微通道测量
|
||||
/// </summary>
|
||||
public string MicrochannelMeasurement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 胆固醇结晶测量
|
||||
/// </summary>
|
||||
public string CholesterolCrystalMeasurement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 官腔面积测量
|
||||
/// </summary>
|
||||
public decimal? LumenAreaMeasurement { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
public decimal Avg { get {
|
||||
|
||||
return ( FirstData*1m + SecondData * 1m + ThirdData * 1m) / 3;
|
||||
return ( FirstData + SecondData + ThirdData) / 3;
|
||||
} }
|
||||
}
|
||||
|
||||
@@ -282,6 +311,27 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
/// 值
|
||||
/// </summary>
|
||||
public decimal Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 巨噬细胞浸润测量
|
||||
/// </summary>
|
||||
public string MacrophageInfiltrationMeasurement { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 巨噬细胞浸润角度测量
|
||||
/// </summary>
|
||||
public decimal? MacrophageInfiltrationAngle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 微通道测量
|
||||
/// </summary>
|
||||
public string MicrochannelMeasurement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 胆固醇结晶测量
|
||||
/// </summary>
|
||||
public string CholesterolCrystalMeasurement { get; set; }
|
||||
}
|
||||
|
||||
public class FileToDataTableDto
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
@@ -143,6 +143,11 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
/// </summary>
|
||||
public ShowChartType ShowChartTypeEnum { get; set; } = ShowChartType.NotShow;
|
||||
|
||||
/// <summary>
|
||||
/// 选项类型
|
||||
/// </summary>
|
||||
public OptionType OptionTypeEnum { get; set; } = OptionType.Defalut;
|
||||
|
||||
/// <summary>
|
||||
/// 高亮问题的答案
|
||||
/// </summary>
|
||||
@@ -170,6 +175,8 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
|
||||
public string Answer { get; set; }
|
||||
|
||||
public bool IsHighlight { get; set; } = false;
|
||||
|
||||
public bool IsGlobalChange { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
@@ -191,9 +198,9 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
public GetReportVerifyOutDto CalculateResult { get; set; }
|
||||
public ReadingTaskState ReadingTaskState { get; set; }
|
||||
|
||||
public List<VisitTaskInfo> VisitTaskList { get; set; }
|
||||
public List<VisitTaskInfo> VisitTaskList { get; set; } =new List<VisitTaskInfo> { };
|
||||
|
||||
public List<ReadingReportDto> TaskQuestions { get; set; }
|
||||
public List<ReadingReportDto> TaskQuestions { get; set; } =new List<ReadingReportDto> { };
|
||||
|
||||
public List<LesionDto> LesionCountList { get; set; } = new List<LesionDto>();
|
||||
|
||||
@@ -484,6 +491,56 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
public List<string> Value { get; set; }
|
||||
}
|
||||
|
||||
public class GetReportsChartTypeDataInDto
|
||||
{
|
||||
public ReportChartType ReportChartTypeEnum { get; set; }
|
||||
|
||||
public CriterionType CriterionType { get; set; }
|
||||
public GetReadingReportEvaluationOutDto Data { get; set; }
|
||||
}
|
||||
|
||||
public class GetReportsChartSummaryInDto
|
||||
{
|
||||
public Guid SubjectId { get; set; }
|
||||
|
||||
public Guid TrialCriterionId { get; set; }
|
||||
}
|
||||
|
||||
public class GetReportsChartSummaryOutDto
|
||||
{
|
||||
public EvaluationColumn Evaluation { get; set; }
|
||||
|
||||
public List<ChartItem> TargetCharts { get; set; } = new List<ChartItem>();
|
||||
|
||||
public List<ChartItem> QuestionCharts { get; set; } = new List<ChartItem>();
|
||||
}
|
||||
|
||||
|
||||
public class EvaluationColumn
|
||||
{
|
||||
public List<List<EvaluationValue>> Evaluation { get; set; }
|
||||
public string DictionaryCode { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class EvaluationValue
|
||||
{
|
||||
public string Value { get; set; }
|
||||
|
||||
public string DictionaryCode { get; set; }
|
||||
}
|
||||
|
||||
public class ChartItem
|
||||
{
|
||||
public Arm Arm { get; set; }
|
||||
public ReportChartType? ChartType { get; set; }
|
||||
public string GroupName { get; set; }
|
||||
public ValueUnit? Unit { get; set; }
|
||||
public List<string> VisitTaskNameList { get; set; } = new List<string>();
|
||||
public List<DateTime?> LatestScanDateList { get; set; } = new List<DateTime?>();
|
||||
public List<ReportChartData> ChartDataList { get; set; } = new List<ReportChartData>();
|
||||
}
|
||||
|
||||
public class GetReportsChartDataInDto
|
||||
{
|
||||
public Guid VisitTaskId { get; set; }
|
||||
@@ -954,6 +1011,9 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
|
||||
public class CopyTableAnswerRowInfo : ReadingTableAnswerRowInfoBase
|
||||
{
|
||||
|
||||
public Guid? IdentityRowId { get; set; }
|
||||
|
||||
public Guid OriginalId { get; set; }
|
||||
|
||||
public LesionType? LesionType { get; set; }
|
||||
@@ -1514,6 +1574,19 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
|
||||
public string Answer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数值类型
|
||||
/// </summary>
|
||||
public ValueOfType? ValueType { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 自定义单位
|
||||
/// </summary>
|
||||
public string? CustomUnit { get; set; }
|
||||
|
||||
public ValueUnit? Unit { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 问题类型
|
||||
@@ -1855,6 +1928,13 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
/// </summary>
|
||||
public ValueOfType? ValueType { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 自定义单位
|
||||
/// </summary>
|
||||
public string? CustomUnit { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 类型
|
||||
/// </summary>
|
||||
@@ -2305,6 +2385,17 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
|
||||
public ValueUnit? Unit { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 自定义单位
|
||||
/// </summary>
|
||||
public string? CustomUnit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数值类型
|
||||
/// </summary>
|
||||
public ValueOfType? ValueType { get; set; }
|
||||
|
||||
public JudgeReadingQuestionType QuestionType { get; set; } = JudgeReadingQuestionType.Question;
|
||||
|
||||
}
|
||||
|
||||
@@ -343,6 +343,8 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
public List<Domain.Models.OSSImageInfo> FileList { get; set; } = new List<Domain.Models.OSSImageInfo>();
|
||||
|
||||
|
||||
//存储最后翻译的内容
|
||||
public string ResultContent { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -631,6 +633,7 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
/// </summary>
|
||||
public string TypeValue { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用
|
||||
/// </summary>
|
||||
@@ -756,6 +759,19 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
|
||||
public string Answer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数值类型
|
||||
/// </summary>
|
||||
public ValueOfType? ValueType { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 自定义单位
|
||||
/// </summary>
|
||||
public string? CustomUnit { get; set; }
|
||||
|
||||
public ValueUnit? Unit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序
|
||||
/// </summary>
|
||||
|
||||
@@ -437,6 +437,11 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
/// </summary>
|
||||
public ShowChartType ShowChartTypeEnum { get; set; } = ShowChartType.NotShow;
|
||||
|
||||
/// <summary>
|
||||
/// 选项类型
|
||||
/// </summary>
|
||||
public OptionType OptionTypeEnum { get; set; } = OptionType.Defalut;
|
||||
|
||||
public Guid? Id { get; set; }
|
||||
|
||||
|
||||
@@ -572,6 +577,11 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
/// </summary>
|
||||
public ShowChartType ShowChartTypeEnum { get; set; } = ShowChartType.NotShow;
|
||||
|
||||
/// <summary>
|
||||
/// 选项类型
|
||||
/// </summary>
|
||||
public OptionType OptionTypeEnum { get; set; } = OptionType.Defalut;
|
||||
|
||||
/// <summary>
|
||||
/// 单位
|
||||
/// </summary>
|
||||
@@ -973,6 +983,10 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
/// </summary>
|
||||
public ImageMark? ImageMarkEnum { get; set; }
|
||||
|
||||
public string QuestionGroupName { get; set; }
|
||||
|
||||
public string QuestionGroupEnName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 影像工具
|
||||
/// </summary>
|
||||
@@ -1257,6 +1271,11 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
/// </summary>
|
||||
public ShowChartType ShowChartTypeEnum { get; set; } = ShowChartType.NotShow;
|
||||
|
||||
/// <summary>
|
||||
/// 选项类型
|
||||
/// </summary>
|
||||
public OptionType OptionTypeEnum { get; set; } = OptionType.Defalut;
|
||||
|
||||
/// <summary>
|
||||
/// 问题英文名称
|
||||
/// </summary>
|
||||
@@ -1373,6 +1392,11 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
/// </summary>
|
||||
public ShowChartType ShowChartTypeEnum { get; set; } = ShowChartType.NotShow;
|
||||
|
||||
/// <summary>
|
||||
/// 选项类型
|
||||
/// </summary>
|
||||
public OptionType OptionTypeEnum { get; set; } = OptionType.Defalut;
|
||||
|
||||
/// <summary>
|
||||
/// 单位
|
||||
/// </summary>
|
||||
@@ -1843,6 +1867,11 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
/// </summary>
|
||||
public ShowChartType ShowChartTypeEnum { get; set; } = ShowChartType.NotShow;
|
||||
|
||||
/// <summary>
|
||||
/// 选项类型
|
||||
/// </summary>
|
||||
public OptionType OptionTypeEnum { get; set; } = OptionType.Defalut;
|
||||
|
||||
[Comment("限制编辑")]
|
||||
public LimitEdit LimitEdit { get; set; } = LimitEdit.None;
|
||||
|
||||
@@ -2254,6 +2283,11 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
/// </summary>
|
||||
public ShowChartType ShowChartTypeEnum { get; set; } = ShowChartType.NotShow;
|
||||
|
||||
/// <summary>
|
||||
/// 选项类型
|
||||
/// </summary>
|
||||
public OptionType OptionTypeEnum { get; set; } = OptionType.Defalut;
|
||||
|
||||
/// <summary>
|
||||
/// 单位
|
||||
/// </summary>
|
||||
@@ -2666,6 +2700,11 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
|
||||
/// </summary>
|
||||
public ShowChartType ShowChartTypeEnum { get; set; } = ShowChartType.NotShow;
|
||||
|
||||
/// <summary>
|
||||
/// 选项类型
|
||||
/// </summary>
|
||||
public OptionType OptionTypeEnum { get; set; } = OptionType.Defalut;
|
||||
|
||||
/// <summary>
|
||||
/// 单位
|
||||
/// </summary>
|
||||
|
||||
@@ -250,6 +250,9 @@ namespace IRaCIS.Core.Application.Service
|
||||
var visitTaskAnswer = await _readingTaskQuestionAnswerRepository.Where(x => taskIds.Contains(x.VisitTaskId) && x.ReadingQuestionTrial.IsJudgeQuestion).Select(x => new JudgeQuestionAnswerInfo
|
||||
{
|
||||
Answer = x.Answer,
|
||||
Unit=x.ReadingQuestionTrial.Unit,
|
||||
ValueType=x.ReadingQuestionTrial.ValueType,
|
||||
CustomUnit=x.ReadingQuestionTrial.CustomUnit,
|
||||
VisitTaskId = x.VisitTaskId,
|
||||
DictionaryCode = x.ReadingQuestionTrial.DictionaryCode,
|
||||
ShowOrder = x.ReadingQuestionTrial.ShowOrder,
|
||||
@@ -338,6 +341,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
ShowOrder = data.ShowOrder,
|
||||
ParentId = data.ParentId,
|
||||
TypeValue = data.TypeValue,
|
||||
|
||||
Answer = leftquestionAnswer.Answer
|
||||
};
|
||||
|
||||
|
||||
@@ -565,7 +565,9 @@ namespace IRaCIS.Core.Application.Service
|
||||
{
|
||||
if (indto.Id != null)
|
||||
{
|
||||
if (await _readingQuestionTrialRepository.AnyAsync(x => x.Id == indto.Id && x.IsJudgeQuestion && x.JudgeType != JudgeTypeEnum.None))
|
||||
var isArbitrationReading = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == indto.ReadingQuestionCriterionTrialId).Select(x => x.IsArbitrationReading).FirstOrDefaultAsync();
|
||||
|
||||
if (isArbitrationReading && (await _readingQuestionTrialRepository.AnyAsync(x => x.Id == indto.Id && x.IsJudgeQuestion && x.JudgeType != JudgeTypeEnum.None)))
|
||||
{
|
||||
//---当前问题已经设置了裁判标准了,修改失败
|
||||
throw new BusinessValidationFailedException(_localizer["ReadingQuestion_JudgmentSet"]);
|
||||
|
||||
+5
-4
@@ -269,7 +269,8 @@ namespace IRaCIS.Core.Application.Service
|
||||
ShowOrder = y.ReadingQuestionTrial.ShowOrder,
|
||||
Type = y.ReadingQuestionTrial.Type,
|
||||
TypeValue = y.ReadingQuestionTrial.TypeValue,
|
||||
ValueType = y.ReadingQuestionTrial.ValueType,
|
||||
CustomUnit = y.ReadingQuestionTrial.CustomUnit,
|
||||
Unit = y.ReadingQuestionTrial.Unit,
|
||||
IsJudgeQuestion = y.ReadingQuestionTrial.IsJudgeQuestion,
|
||||
Answer = y.Answer,
|
||||
}).ToList()
|
||||
@@ -313,7 +314,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
ValueType = y.ValueType,
|
||||
IsJudgeQuestion = y.IsJudgeQuestion,
|
||||
Answer = string.Empty,
|
||||
|
||||
CustomUnit = y.CustomUnit,
|
||||
});
|
||||
|
||||
}
|
||||
@@ -399,7 +400,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
JudgeType = lr.question.JudgeType,
|
||||
TypeValue = lr.question.TypeValue,
|
||||
ValueType = lr.question.ValueType,
|
||||
|
||||
CustomUnit = lr.question.CustomUnit,
|
||||
}).ToList();
|
||||
|
||||
|
||||
@@ -524,7 +525,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
ShowOrder = x.Dictionary.ShowOrder,
|
||||
ParentCode = x.Dictionary.Parent.Code,
|
||||
Value = x.Dictionary.Value,
|
||||
ValueCN = x.Dictionary.ValueCN
|
||||
ValueCN = x.Dictionary.ValueCN,
|
||||
}).OrderBy(x => x.ParentCode).ThenBy(x => x.ShowOrder).ToListAsync();
|
||||
result.AssessTypeList = assessTypeList;
|
||||
|
||||
|
||||
+78
-13
@@ -1,4 +1,5 @@
|
||||
using DocumentFormat.OpenXml;
|
||||
using Amazon.Runtime.Internal.Transform;
|
||||
using DocumentFormat.OpenXml;
|
||||
using DocumentFormat.OpenXml.Drawing.Charts;
|
||||
using DocumentFormat.OpenXml.Office2019.Excel.ThreadedComments;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
@@ -93,6 +94,17 @@ namespace IRaCIS.Core.Application.Service
|
||||
return await _readingCalculateService.GetReportsChartData(inDto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取图表数据汇总
|
||||
/// </summary>
|
||||
/// <param name="inDto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<GetReportsChartSummaryOutDto> GetReportsChartSummary(GetReportsChartSummaryInDto inDto)
|
||||
{
|
||||
return await _readingCalculateService.GetReportsChartSummary(inDto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置已阅读关键文件
|
||||
/// </summary>
|
||||
@@ -463,7 +475,6 @@ namespace IRaCIS.Core.Application.Service
|
||||
.WhereIf(userType == UserTypeEnum.IndependentReviewer, t => t.FileType.Code == "2" || t.FileType.Code == "6")
|
||||
.WhereIf(userType == UserTypeEnum.IQC, t => t.FileType.Code == "4" || t.FileType.Code == "5")
|
||||
.WhereIf(!canViewUserType.Contains(userType), t => false)
|
||||
.IgnoreQueryFilters()
|
||||
.Select(x => new GetManualListOutDto()
|
||||
{
|
||||
Id = x.Id,
|
||||
@@ -1777,6 +1788,8 @@ namespace IRaCIS.Core.Application.Service
|
||||
answers.Add("CTSeriesId", rowInfo == null ? string.Empty : rowInfo.CTSeriesId.ToString());
|
||||
answers.Add("SeriesId", rowInfo == null ? string.Empty : rowInfo.SeriesId.ToString());
|
||||
answers.Add("IsCurrentTaskAdd", rowInfo == null ? false.ToString() : rowInfo.IsCurrentTaskAdd.ToString());
|
||||
answers.Add("SplitRowId", rowInfo == null ? string.Empty : rowInfo.SplitRowId==null?string.Empty: rowInfo.SplitRowId.ToString());
|
||||
answers.Add("MergeRowId", rowInfo == null ? string.Empty : rowInfo.MergeRowId == null ? string.Empty : rowInfo.MergeRowId.ToString());
|
||||
answers.Add("SplitOrMergeLesionName", rowInfo == null ? string.Empty : rowInfo.SplitName.IsNullOrEmpty() ? rowInfo.MergeName : rowInfo.SplitName);
|
||||
answers.Add("SplitOrMergeType", rowInfo == null ? string.Empty : rowInfo.SplitOrMergeType == null ? string.Empty : ((int)rowInfo.SplitOrMergeType).ToString());
|
||||
|
||||
@@ -1959,24 +1972,70 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var mark = mergeRow.ReadingQuestionTrial.OrderMark + mergeRow.RowIndex.GetLesionMark();
|
||||
|
||||
|
||||
await _readingTableQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => inDto.MergeRowIdList.Contains(x.RowId) && x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State, x => new ReadingTableQuestionAnswer()
|
||||
{
|
||||
Answer = TargetState.Loss.GetEnumInt(),
|
||||
});
|
||||
var isLymph = ReadingYesOrNo.Yes.GetEnumInt();
|
||||
|
||||
await _readingTableQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => inDto.MergeRowIdList.Contains(x.RowId) && (x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.MajorAxis || x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.ShortAxis), x => new ReadingTableQuestionAnswer()
|
||||
foreach (var item in inDto.MergeRowIdList)
|
||||
{
|
||||
Answer = "0",
|
||||
});
|
||||
|
||||
switch (taskinfo.TrialReadingCriterion.CriterionType)
|
||||
{
|
||||
case CriterionType.Lugano2014:
|
||||
case CriterionType.Lugano2014WithoutPET:
|
||||
await _readingTableQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.RowId == item &&
|
||||
(x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.MajorAxis || x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.ShortAxis), x => new ReadingTableQuestionAnswer()
|
||||
{
|
||||
Answer = "0",
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
//淋巴结的长短经 为0 非淋巴的 长径0 短径为空
|
||||
if (await _readingTableQuestionAnswerRepository.AnyAsync(x => x.RowId == item && x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.IsLymph && x.Answer == isLymph))
|
||||
{
|
||||
await _readingTableQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.RowId == item &&
|
||||
(x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.MajorAxis || x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.ShortAxis), x => new ReadingTableQuestionAnswer()
|
||||
{
|
||||
Answer = "0",
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
await _readingTableQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.RowId == item &&
|
||||
x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.ShortAxis, x => new ReadingTableQuestionAnswer()
|
||||
{
|
||||
Answer = string.Empty,
|
||||
});
|
||||
|
||||
await _readingTableQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.RowId == item &&
|
||||
x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.MajorAxis, x => new ReadingTableQuestionAnswer()
|
||||
{
|
||||
Answer = "0",
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
await _readingTableAnswerRowInfoRepository.UpdatePartialFromQueryAsync(x => inDto.MergeRowIdList.Contains(x.Id), x => new ReadingTableAnswerRowInfo()
|
||||
{
|
||||
MergeRowId = inDto.MergeMainRowId,
|
||||
SplitOrMergeType = SplitOrMergeType.Merge,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
await _readingTableAnswerRowInfoRepository.UpdatePartialFromQueryAsync(inDto.MergeMainRowId, x => new ReadingTableAnswerRowInfo()
|
||||
|
||||
|
||||
|
||||
|
||||
await _readingTableAnswerRowInfoRepository.UpdatePartialFromQueryAsync(x => inDto.MergeRowIdList.Contains(x.Id), x => new ReadingTableAnswerRowInfo()
|
||||
{
|
||||
MergeRowId = inDto.MergeMainRowId,
|
||||
SplitOrMergeType = SplitOrMergeType.Merge,
|
||||
});
|
||||
|
||||
await _readingTableAnswerRowInfoRepository.BatchUpdateNoTrackingAsync(x=>x.Id== inDto.MergeMainRowId, x => new ReadingTableAnswerRowInfo()
|
||||
{
|
||||
SplitOrMergeType = SplitOrMergeType.MergeMain,
|
||||
});
|
||||
@@ -2049,6 +2108,9 @@ namespace IRaCIS.Core.Application.Service
|
||||
);
|
||||
|
||||
break;
|
||||
default:
|
||||
query = query.Where(x => !x.LesionAnswerList.Any(y => y.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State&&y.Answer!=string.Empty));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
@@ -3075,6 +3137,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
//await _readingTableQuestionAnswerRepository.BatchDeleteNoTrackingAsync(x => x.RowId == (inDto.RowId ?? default(Guid)));
|
||||
//await _readingTableAnswerRowInfoRepository.BatchDeleteNoTrackingAsync(x => x.Id == (inDto.RowId ?? default(Guid)));
|
||||
_mapper.Map(inDto, rowInfo);
|
||||
rowInfo.IdentityRowId = rowInfo.IdentityRowId == null ? NewId.NextGuid() : rowInfo.IdentityRowId;
|
||||
rowInfo.Id = inDto.RowId == null ? NewId.NextGuid() : inDto.RowId.Value;
|
||||
result.RowId = rowInfo.Id;
|
||||
rowInfo.IsCurrentTaskAdd = isCurrentTaskAdd;
|
||||
@@ -3301,7 +3364,9 @@ namespace IRaCIS.Core.Application.Service
|
||||
//验证后处理影像必须传
|
||||
if (_visitTaskRepository.Any(t => t.Id == inDto.VisitTaskId && t.TrialReadingCriterion.ImageUploadEnum != ReadingImageUpload.None))
|
||||
{
|
||||
if (!_taskStudyRepository.Any(t => t.VisitTaskId == inDto.VisitTaskId))
|
||||
// 校验当前访视存在上传的非dicom文件就可以提交了
|
||||
|
||||
if (!_taskStudyRepository.Any(t => t.VisitTaskId == inDto.VisitTaskId) && !_noneDicomStudyFileRepository.Any(t=>t.VisitTaskId==inDto.VisitTaskId) )
|
||||
{
|
||||
return ResponseOutput.NotOk(_localizer["ReadingImage_BackImageNotExist"]);
|
||||
}
|
||||
@@ -3775,7 +3840,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
task.ReadingVersionEnum = criterionInfo.ReadingVersionEnum;
|
||||
task.ReadingToolList = criterionInfo.ReadingToolList;
|
||||
task.IsExistUnprocessedFeedback = await _userFeedBackRepository.AnyAsync(x => x.VisitTaskId == task.VisitTaskId && x.State == 0);
|
||||
task.IsViewStudyPart= visitTaskInfo.ReadingCategory==ReadingCategory.Judge|| visitTaskInfo.IsViewStudyPart;
|
||||
task.IsViewStudyPart = visitTaskInfo.ReadingCategory == ReadingCategory.Judge || visitTaskInfo.IsViewStudyPart;
|
||||
task.IsReadKeyFile = await _criterionKeyFileReadRepository.AnyAsync(x => x.IdentityUserId == _userInfo.IdentityUserId && x.TrialCriterionId == task.TrialReadingCriterionId);
|
||||
task.IsHaveKeyFile = await _trialCriterionKeyFileRepository.AnyAsync(x => x.TrialCriterionId == task.TrialReadingCriterionId);
|
||||
// 添加默认答案
|
||||
|
||||
@@ -265,6 +265,9 @@ namespace IRaCIS.Core.Application.Service
|
||||
Answer = x.Answer,
|
||||
Type = x.ReadingQuestionTrial.Type,
|
||||
Unit = x.ReadingQuestionTrial.Unit,
|
||||
CustomUnit= x.ReadingQuestionTrial.CustomUnit,
|
||||
ValueType=x.ReadingQuestionTrial.ValueType,
|
||||
|
||||
QuestionId = x.ReadingQuestionTrial.Id,
|
||||
QuestionName = x.ReadingQuestionTrial.QuestionName.LanguageName(x.ReadingQuestionTrial.QuestionEnName, _userInfo.IsEn_Us),
|
||||
QuestionGenre = x.ReadingQuestionTrial.QuestionGenre,
|
||||
@@ -314,10 +317,13 @@ namespace IRaCIS.Core.Application.Service
|
||||
Answer = x.Answer,
|
||||
Type = x.Type,
|
||||
Unit = x.Unit,
|
||||
CustomUnit=x.CustomUnit,
|
||||
|
||||
QuestionId = x.QuestionId!.Value,
|
||||
QuestionName = x.QuestionName,
|
||||
QuestionGenre = x.QuestionGenre,
|
||||
DictionaryCode = x.DictionaryCode,
|
||||
ValueType=x.ValueType,
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
@@ -358,6 +364,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
QuestionName = x.QuestionName,
|
||||
QuestionGenre = x.QuestionGenre,
|
||||
DictionaryCode = x.DictionaryCode,
|
||||
ValueType=x.ValueType,
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
|
||||
+45
-15
@@ -1,10 +1,15 @@
|
||||
using Amazon.Runtime.Internal.Auth;
|
||||
using DocumentFormat.OpenXml.Drawing.Spreadsheet;
|
||||
using DocumentFormat.OpenXml.Office.SpreadSheetML.Y2023.MsForms;
|
||||
using DocumentFormat.OpenXml.Office2010.Excel;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||
using IRaCIS.Core.Application.ViewModel;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore.Common;
|
||||
using IRaCIS.Core.Infra.EFCore.Migrations;
|
||||
using MassTransit;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -99,22 +104,34 @@ namespace IRaCIS.Core.Application.Service
|
||||
await _readingNoneDicomMarkRepository.AddAsync(mark);
|
||||
}
|
||||
|
||||
await _readingNoneDicomMarkBindingRepository.BatchDeleteNoTrackingAsync(x =>
|
||||
x.VisitTaskId == inDto.VisitTaskId &&
|
||||
|
||||
var binding = await _readingNoneDicomMarkBindingRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId &&
|
||||
x.QuestionId == inDto.QuestionId &&
|
||||
x.RowId == inDto.RowId &&
|
||||
x.TableQuestionId == inDto.TableQuestionId);
|
||||
|
||||
await _readingNoneDicomMarkBindingRepository.AddAsync(new ReadingNoneDicomMarkBinding()
|
||||
x.TableQuestionId == inDto.TableQuestionId).FirstOrDefaultAsync();
|
||||
if (binding == null)
|
||||
{
|
||||
Id = NewId.NextGuid(),
|
||||
VisitTaskId = inDto.VisitTaskId,
|
||||
QuestionId = inDto.QuestionId,
|
||||
RowId = inDto.RowId,
|
||||
TableQuestionId = inDto.TableQuestionId,
|
||||
NoneDicomMarkId = mark.Id,
|
||||
MarkId = mark.MarkId
|
||||
});
|
||||
await _readingNoneDicomMarkBindingRepository.AddAsync(new ReadingNoneDicomMarkBinding()
|
||||
{
|
||||
Id = NewId.NextGuid(),
|
||||
VisitTaskId = inDto.VisitTaskId,
|
||||
QuestionId = inDto.QuestionId,
|
||||
RowId = inDto.RowId,
|
||||
TableQuestionId = inDto.TableQuestionId,
|
||||
NoneDicomMarkId = mark.Id,
|
||||
MarkId = mark.MarkId
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
await _readingNoneDicomMarkBindingRepository.UpdatePartialFromQueryAsync(binding.Id,x=> new ReadingNoneDicomMarkBinding()
|
||||
{
|
||||
NoneDicomMarkId = mark.Id,
|
||||
MarkId = mark.MarkId
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -468,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();
|
||||
@@ -489,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
|
||||
{
|
||||
@@ -503,6 +531,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
x.CriterionName,
|
||||
x.Id,
|
||||
x.ReadingTool,
|
||||
x.CriterionType,
|
||||
|
||||
}).FirstOrDefaultAsync();
|
||||
|
||||
@@ -533,7 +562,8 @@ namespace IRaCIS.Core.Application.Service
|
||||
IsReadingShowSubjectInfo = criterionInfo.IsReadingShowSubjectInfo,
|
||||
IsExistsClinicalData = isExistsClinicalData,
|
||||
TrialCriterionName = criterionInfo.CriterionName,
|
||||
TrialCriterionId = criterionInfo.Id
|
||||
TrialCriterionId = criterionInfo.Id,
|
||||
criterionInfo.CriterionType,
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -202,6 +202,8 @@ namespace IRaCIS.Core.Application.Service
|
||||
TypeValue = y.ReadingQuestionTrial.TypeValue,
|
||||
ValueType = y.ReadingQuestionTrial.ValueType,
|
||||
IsJudgeQuestion = y.ReadingQuestionTrial.IsJudgeQuestion,
|
||||
Unit = y.ReadingQuestionTrial.Unit,
|
||||
CustomUnit = y.ReadingQuestionTrial.CustomUnit,
|
||||
Answer = y.Answer,
|
||||
}).ToList()
|
||||
}).ToListAsync();
|
||||
@@ -285,6 +287,9 @@ namespace IRaCIS.Core.Application.Service
|
||||
QuestionName = y.QuestionName,
|
||||
QuestionGenre = y.QuestionGenre,
|
||||
DictionaryCode = y.DictionaryCode,
|
||||
Unit=y.Unit,
|
||||
CustomUnit=y.CustomUnit,
|
||||
ValueType = y.ValueType,
|
||||
Answer = y.Answer
|
||||
}).ToList();
|
||||
x.IsHaveChange = result.ReadingCategory == ReadingCategory.Visit ? false : globalVisits.Where(y => x.VisitTaskId == y.VisitTaskId).SelectMany(y => y.AfterQuestionList).Any(y => y.IsHaveChange);
|
||||
|
||||
@@ -374,7 +374,8 @@ 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.QuestionGroupEnName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupEnName : s.GroupInfo.GroupEnName))
|
||||
.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))
|
||||
|
||||
+184
-2
@@ -1,4 +1,5 @@
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using DocumentFormat.OpenXml.Drawing.Spreadsheet;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
@@ -16,6 +17,9 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
{
|
||||
public class GeneralCalculateService(IRepository<ReadingTableQuestionAnswer> _readingTableQuestionAnswerRepository,
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<ReadingTaskQuestionMark> _readingTaskQuestionMarkRepository,
|
||||
IRepository<ReadingNoneDicomMark> _readingNoneDicomMarkRepository,
|
||||
IRepository<ReadingNoneDicomMarkBinding> _readingNoneDicomMarkBindingRepository,
|
||||
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
||||
ILogger<GeneralCalculateService> _logger,
|
||||
IRepository<ReadingTableQuestionTrial> _readingTableQuestionTrialRepository,
|
||||
@@ -62,6 +66,7 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
foreach (var item in questions)
|
||||
{
|
||||
item.QuestionName = item.QuestionName.LanguageName(item.QuestionEnName, _userInfo.IsEn_Us);
|
||||
item.GroupName = item.GroupName.LanguageName(item.GroupEnName, _userInfo.IsEn_Us);
|
||||
item.ReportLayType = ReportLayType.Group;
|
||||
|
||||
item.Childrens = _mapper.Map<List<ReadingReportDto>>(questionList.Where(x => x.GroupId == item.QuestionId).OrderBy(x => x.ShowOrder));
|
||||
@@ -208,6 +213,183 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
return questions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 复制既往新病灶答案
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task CopyHistoryAnswer(VisitTask taskinfo, List<ReadingTableAnswerRowInfo> tableRowList,List<ReadingTableQuestionAnswer> tableAnswerList)
|
||||
{
|
||||
if (_userInfo.RequestUrl == "ReadingImageTask/resetReadingTask")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (taskinfo.IsCopyLesionAnswer)
|
||||
{
|
||||
var historyTaskId = await _visitTaskRepository.Where(x =>
|
||||
x.ReadingCategory == ReadingCategory.Visit &&
|
||||
x.TrialReadingCriterionId == taskinfo.TrialReadingCriterionId &&
|
||||
x.IsAnalysisCreate == taskinfo.IsAnalysisCreate &&
|
||||
x.DoctorUserId == taskinfo.DoctorUserId &&
|
||||
x.IsSelfAnalysis == taskinfo.IsSelfAnalysis &&
|
||||
x.SubjectId == taskinfo.SubjectId &&
|
||||
x.ReadingTaskState == ReadingTaskState.HaveSigned &&
|
||||
x.VisitTaskNum == taskinfo.VisitTaskNum &&
|
||||
x.TaskState != TaskState.Effect &&
|
||||
x.ArmEnum == taskinfo.ArmEnum)
|
||||
.OrderByDescending(x => x.SignTime)
|
||||
.Select(x => x.Id).FirstOrDefaultAsync();
|
||||
|
||||
// 1.标记信息复制有问题 2. 标记
|
||||
|
||||
if (historyTaskId != Guid.Empty)
|
||||
{
|
||||
var historyTableRowList = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == historyTaskId).ToListAsync();
|
||||
var historyTableAnswerList = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == historyTaskId).ToListAsync();
|
||||
|
||||
|
||||
var answerList = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == historyTaskId).ToListAsync();
|
||||
|
||||
|
||||
var questionMarkList=await _readingTaskQuestionMarkRepository.Where(x => x.VisitTaskId == historyTaskId).ToListAsync();
|
||||
var noneDicomMarkList=await _readingNoneDicomMarkRepository.Where(x => x.VisitTaskId == historyTaskId).ToListAsync();
|
||||
var noneDicomMarkBindingList=await _readingNoneDicomMarkBindingRepository.Where(x => x.VisitTaskId == historyTaskId).ToListAsync();
|
||||
|
||||
foreach (var item in tableRowList)
|
||||
{
|
||||
if (item.SplitOrMergeType == SplitOrMergeType.Merged)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var historyRow = historyTableRowList.Where(x =>
|
||||
x.QuestionId == item.QuestionId &&
|
||||
x.RowIndex == item.RowIndex &&
|
||||
x.IdentityRowId==item.IdentityRowId &&
|
||||
x.IdentityRowId !=null &&
|
||||
x.OrganInfoId==item.OrganInfoId
|
||||
).FirstOrDefault();
|
||||
|
||||
if (historyRow != null)
|
||||
{
|
||||
item.StudyId= historyRow.StudyId;
|
||||
item.SeriesId= historyRow.SeriesId;
|
||||
item.InstanceId= historyRow.InstanceId;
|
||||
|
||||
item.OtherStudyId = historyRow.OtherStudyId;
|
||||
item.OtherSeriesId = historyRow.OtherSeriesId;
|
||||
item.OtherInstanceId= historyRow.OtherInstanceId;
|
||||
|
||||
item.MeasureData= historyRow.MeasureData.Replace(historyTaskId.ToString(),taskinfo.Id.ToString());
|
||||
item.OtherMeasureData = historyRow.OtherMeasureData.Replace(historyTaskId.ToString(), taskinfo.Id.ToString());
|
||||
|
||||
|
||||
tableAnswerList.Where(x => x.RowId == item.Id).ForEach(x =>
|
||||
{
|
||||
x.Answer = x.Answer.IsNullOrEmpty() ?
|
||||
historyTableAnswerList.Where(y => y.RowId == historyRow.Id && y.TableQuestionId == x.TableQuestionId).Select(x => x.Answer).FirstOrDefault()??string.Empty :
|
||||
x.Answer;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 复制外层问题
|
||||
answerList.ForEach(x =>
|
||||
{
|
||||
x.Id = NewId.NextGuid();
|
||||
x.VisitTaskId = taskinfo.Id;
|
||||
});
|
||||
|
||||
await _readingTaskQuestionAnswerRepository.AddRangeAsync(answerList);
|
||||
|
||||
|
||||
// 处理标记
|
||||
Dictionary<Guid,Guid> dicomKeys=new Dictionary<Guid, Guid> ();
|
||||
|
||||
foreach (var item in questionMarkList)
|
||||
{
|
||||
item.Id = NewId.NextGuid();
|
||||
item.VisitTaskId = taskinfo.Id;
|
||||
item.FristAddTaskId = taskinfo.Id;
|
||||
if (item.MarkId != null)
|
||||
{
|
||||
if (dicomKeys.ContainsKey(item.MarkId.Value))
|
||||
{
|
||||
item.MarkId = dicomKeys[item.MarkId.Value];
|
||||
item.MeasureData = item.MeasureData.Replace(historyTaskId.ToString(), taskinfo.Id.ToString()).Replace(item.MarkId.Value.ToString(), dicomKeys[item.MarkId.Value].ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
var newguid = NewId.NextGuid();
|
||||
|
||||
item.MeasureData = item.MeasureData.Replace(historyTaskId.ToString(), taskinfo.Id.ToString()).Replace(item.MarkId.Value.ToString(), newguid.ToString());
|
||||
dicomKeys.Add(item.MarkId.Value, newguid);
|
||||
item.MarkId = dicomKeys[item.MarkId.Value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await _readingTaskQuestionMarkRepository.AddRangeAsync(questionMarkList);
|
||||
|
||||
|
||||
// 处理非Dicom标记
|
||||
Dictionary<Guid, Guid> noneKeys = new Dictionary<Guid, Guid>();
|
||||
foreach (var item in noneDicomMarkList)
|
||||
{
|
||||
var newid= NewId.NextGuid();
|
||||
|
||||
item.VisitTaskId = taskinfo.Id;
|
||||
if (item.MarkId != null)
|
||||
{
|
||||
if (noneKeys.ContainsKey(item.MarkId.Value))
|
||||
{
|
||||
item.MarkId = noneKeys[item.MarkId.Value];
|
||||
item.MeasureData = item.MeasureData.Replace(historyTaskId.ToString(), taskinfo.Id.ToString()).Replace(item.MarkId.Value.ToString(), noneKeys[item.MarkId.Value].ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
var newguid = NewId.NextGuid();
|
||||
|
||||
item.MeasureData = item.MeasureData.Replace(historyTaskId.ToString(), taskinfo.Id.ToString()).Replace(item.MarkId.Value.ToString(), newguid.ToString());
|
||||
noneKeys.Add(item.MarkId.Value, newguid);
|
||||
item.MarkId = noneKeys[item.MarkId.Value];
|
||||
}
|
||||
}
|
||||
|
||||
#region 处理非DICOM绑定
|
||||
// 处理非DICOM绑定
|
||||
foreach (var binding in noneDicomMarkBindingList.Where(z => z.MarkId == item.MarkId))
|
||||
{
|
||||
binding.Id = NewId.NextGuid();
|
||||
binding.VisitTaskId = taskinfo.Id;
|
||||
if (binding.MarkId != null)
|
||||
{
|
||||
binding.NoneDicomMarkId = newid;
|
||||
|
||||
if (noneKeys.ContainsKey(binding.MarkId.Value))
|
||||
{
|
||||
binding.MarkId = noneKeys[binding.MarkId.Value];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
item.Id = NewId.NextGuid();
|
||||
}
|
||||
|
||||
await _readingNoneDicomMarkRepository.AddRangeAsync(noneDicomMarkList);
|
||||
|
||||
|
||||
await _readingNoneDicomMarkBindingRepository.AddRangeAsync(noneDicomMarkBindingList);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从上传文件中获取Datatable
|
||||
@@ -350,7 +532,7 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
/// <returns></returns>
|
||||
public async Task<ReadingCalculateDto> GetReadingCalculateDto(Guid visitTaskId)
|
||||
{
|
||||
var visitTask = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync();
|
||||
var visitTask = await _visitTaskRepository.Where(x => x.Id == visitTaskId).Include(x=>x.SourceSubjectVisit).FirstNotNullAsync();
|
||||
|
||||
var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == visitTask.TrialReadingCriterionId).FirstNotNullAsync();
|
||||
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == (visitTask.SourceSubjectVisitId ?? default(Guid))).FirstOrDefaultAsync();
|
||||
|
||||
+485
-64
@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MiniExcelLibs;
|
||||
using Org.BouncyCastle.Asn1.X509;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
|
||||
@@ -22,8 +23,20 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
IHttpContextAccessor httpContext,
|
||||
IOSSService oSSService,
|
||||
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
||||
IStringLocalizer _localizer, IUserInfo _userInfo
|
||||
IStringLocalizer _localizer, IUserInfo _userInfo,
|
||||
IRepository<ReadingTableQuestionAnswer> _readingTableQuestionAnswerRepository,
|
||||
IRepository<ReadingTableQuestionTrial> _readingTableQuestionTrialRepository,
|
||||
IRepository<ReadingTableAnswerRowInfo> _readingTableAnswerRowInfoRepository,
|
||||
IRepository<ReadingQuestionTrial> _readingQuestionTrialRepository,
|
||||
IRepository<OrganInfo> _organInfoRepository,
|
||||
IRepository<ReadingGlobalTaskInfo> _readingGlobalTaskInfoRepository,
|
||||
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||
IRepository<TumorAssessment_IRECIST1Point1> _tumorAssessmentRepository,
|
||||
IGeneralCalculateService _generalCalculateService,
|
||||
IRepository<ReadingTaskQuestionAnswer> _readingTaskQuestionAnswerRepository,
|
||||
IVisitTaskHelpeService _visitTaskHelpeService,
|
||||
|
||||
IMapper _mapper
|
||||
) : BaseService, IReadingCalculateService
|
||||
{
|
||||
private ICriterionCalculateService _useCriterion;
|
||||
@@ -75,14 +88,14 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
|
||||
await _readingImportFileRepository.AddAsync(new ReadingImportFile()
|
||||
{
|
||||
|
||||
|
||||
FilePath = ossRelativePath,
|
||||
VisitTaskId = visitTaskId,
|
||||
TrialId = visitTaskInfo.TrialId,
|
||||
SubjectId = visitTaskInfo.SubjectId,
|
||||
SubjectVisitId = visitTaskInfo.SourceSubjectVisitId,
|
||||
TrialReadingCriterionId = visitTaskInfo.TrialReadingCriterionId,
|
||||
TableName= tableName,
|
||||
TableName = tableName,
|
||||
|
||||
});
|
||||
|
||||
@@ -247,6 +260,7 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
/// </summary>
|
||||
/// <param name="inDto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<GetReportsChartDataOutDto> GetReportsChartData(GetReportsChartDataInDto inDto)
|
||||
{
|
||||
var criterionId = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Select(x => x.TrialReadingCriterionId).FirstNotNullAsync();
|
||||
@@ -259,77 +273,26 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
VisitTaskId = inDto.VisitTaskId,
|
||||
});
|
||||
|
||||
GetReportsChartDataOutDto result = new GetReportsChartDataOutDto()
|
||||
GetReportsChartDataOutDto result = new GetReportsChartDataOutDto()
|
||||
{
|
||||
VisitTaskNameList = data.VisitTaskList.Select(x => x.BlindName).ToList(),
|
||||
LatestScanDateList = data.VisitTaskList.Select(x => x.LatestScanDate).ToList(),
|
||||
ChartDataList =new List<ReportChartData>() { },
|
||||
ChartDataList = new List<ReportChartData>() { },
|
||||
};
|
||||
|
||||
|
||||
|
||||
result.VisitTaskNameList = data.VisitTaskList.Select(x => x.BlindName).ToList();
|
||||
|
||||
if (inDto.ReportChartTypeEnum != null)
|
||||
{
|
||||
switch (inDto.ReportChartTypeEnum)
|
||||
var reportData = await GetReportsChartTypeData(new GetReportsChartTypeDataInDto()
|
||||
{
|
||||
case ReportChartType.Target:
|
||||
{
|
||||
// 这是病灶
|
||||
var target = data.TaskQuestions.SelectMany(x => x.Childrens)
|
||||
.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x=>x.Childrens)
|
||||
.ToList();
|
||||
|
||||
|
||||
foreach (var item in target)
|
||||
{
|
||||
ReportChartData chartData = new ReportChartData()
|
||||
{
|
||||
Name = item.QuestionName,
|
||||
Value= new List<string>(),
|
||||
};
|
||||
|
||||
for (var i = 0; i < result.VisitTaskNameList.Count; i++)
|
||||
{
|
||||
|
||||
switch (criterionType)
|
||||
{
|
||||
case CriterionType.RECIST1Point1:
|
||||
case CriterionType.RECIST1Pointt1_MB:
|
||||
case CriterionType.mRECISTHCC:
|
||||
case CriterionType.IRECIST1Point1:
|
||||
// 淋巴结的短径
|
||||
if (item.Childrens.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer[i].Answer.EqEnum(ReadingYesOrNo.Yes)))
|
||||
{
|
||||
chartData.Value.Add(item.Childrens.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Answer[i].Answer).FirstOrDefault());
|
||||
result.Unit = item.Childrens.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Unit).FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
chartData.Value.Add(item.Childrens.Where(x => x.QuestionMark == QuestionMark.MajorAxis).Select(x => x.Answer[i].Answer).FirstOrDefault());
|
||||
result.Unit = item.Childrens.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Unit).FirstOrDefault();
|
||||
}
|
||||
break;
|
||||
|
||||
case CriterionType.Lugano2014:
|
||||
case CriterionType.Lugano2014WithoutPET:
|
||||
chartData.Value.Add(item.Childrens.Where(x => x.QuestionMark == QuestionMark.PPD).Select(x => x.Answer[i].Answer).FirstOrDefault());
|
||||
result.Unit = item.Childrens.Where(x => x.QuestionMark == QuestionMark.PPD).Select(x => x.Unit).FirstOrDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
result.ChartDataList.Add(chartData);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
CriterionType = criterionType,
|
||||
Data = data,
|
||||
ReportChartTypeEnum = inDto.ReportChartTypeEnum.Value,
|
||||
});
|
||||
result.ChartDataList = reportData.ChartDataList;
|
||||
result.Unit = reportData.Unit;
|
||||
}
|
||||
else if (inDto.QuestionId != null)
|
||||
{
|
||||
@@ -384,6 +347,431 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private async Task<GetReportsChartDataOutDto> GetReportsChartTypeData(GetReportsChartTypeDataInDto inDto)
|
||||
{
|
||||
var visitTaskNameList = inDto.Data.VisitTaskList.Select(x => x.BlindName).ToList();
|
||||
GetReportsChartDataOutDto result = new GetReportsChartDataOutDto() {
|
||||
|
||||
ChartDataList=new List<ReportChartData>() { },
|
||||
VisitTaskNameList= visitTaskNameList,
|
||||
|
||||
};
|
||||
switch (inDto.ReportChartTypeEnum)
|
||||
{
|
||||
case ReportChartType.Target:
|
||||
{
|
||||
// 这是病灶
|
||||
var target = inDto.Data.TaskQuestions.SelectMany(x => x.Childrens)
|
||||
.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.Childrens)
|
||||
.ToList();
|
||||
|
||||
|
||||
foreach (var item in target)
|
||||
{
|
||||
ReportChartData chartData = new ReportChartData()
|
||||
{
|
||||
Name = item.QuestionName,
|
||||
Value = new List<string>(),
|
||||
};
|
||||
|
||||
for (var i = 0; i < visitTaskNameList.Count; i++)
|
||||
{
|
||||
|
||||
switch (inDto.CriterionType)
|
||||
{
|
||||
case CriterionType.RECIST1Point1:
|
||||
case CriterionType.RECIST1Pointt1_MB:
|
||||
case CriterionType.mRECISTHCC:
|
||||
case CriterionType.IRECIST1Point1:
|
||||
// 淋巴结的短径
|
||||
if (item.Childrens.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer[i].Answer.EqEnum(ReadingYesOrNo.Yes)))
|
||||
{
|
||||
chartData.Value.Add(item.Childrens.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Answer[i].Answer).FirstOrDefault());
|
||||
result.Unit = item.Childrens.Where(x => x.QuestionMark == QuestionMark.MajorAxis).Select(x => x.Unit).FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
chartData.Value.Add(item.Childrens.Where(x => x.QuestionMark == QuestionMark.MajorAxis).Select(x => x.Answer[i].Answer).FirstOrDefault());
|
||||
result.Unit = item.Childrens.Where(x => x.QuestionMark == QuestionMark.MajorAxis).Select(x => x.Unit).FirstOrDefault();
|
||||
}
|
||||
break;
|
||||
|
||||
case CriterionType.Lugano2014:
|
||||
case CriterionType.Lugano2014WithoutPET:
|
||||
chartData.Value.Add(item.Childrens.Where(x => x.QuestionMark == QuestionMark.PPD).Select(x => x.Answer[i].Answer).FirstOrDefault());
|
||||
result.Unit = item.Childrens.Where(x => x.QuestionMark == QuestionMark.PPD).Select(x => x.Unit).FirstOrDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
result.ChartDataList.Add(chartData);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case ReportChartType.BaseLineTarget:
|
||||
// 这是病灶
|
||||
var baseTarget = inDto.Data.TaskQuestions.SelectMany(x => x.Childrens)
|
||||
.Where(x => x.LesionType == LesionType.BaselineLesions).SelectMany(x => x.Childrens)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in baseTarget)
|
||||
{
|
||||
ReportChartData chartData = new ReportChartData()
|
||||
{
|
||||
Name = item.QuestionName,
|
||||
Value = new List<string>(),
|
||||
};
|
||||
|
||||
for (var i = 0; i < result.VisitTaskNameList.Count; i++)
|
||||
{
|
||||
|
||||
chartData.Value.Add(item.Childrens.Where(x => x.QuestionMark == QuestionMark.LesionNumber).Select(x => x.Answer[i].Answer).FirstOrDefault());
|
||||
result.Unit = item.Childrens.Where(x => x.QuestionMark == QuestionMark.LesionNumber).Select(x => x.Unit).FirstOrDefault();
|
||||
|
||||
|
||||
}
|
||||
|
||||
result.ChartDataList.Add(chartData);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取报告图表汇总
|
||||
/// </summary>
|
||||
/// <param name="inDto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<GetReportsChartSummaryOutDto> GetReportsChartSummary(GetReportsChartSummaryInDto inDto)
|
||||
{
|
||||
var criterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialCriterionId).FirstNotNullAsync();
|
||||
var result= new GetReportsChartSummaryOutDto();
|
||||
var r1Data = await GetData(new List<Arm>() { Arm.SingleReadingArm, Arm.DoubleReadingArm1 });
|
||||
var r2Data = await GetData(new List<Arm>() { Arm.DoubleReadingArm2 });
|
||||
|
||||
var alldata = r1Data.VisitTaskList.Count() > r2Data.VisitTaskList.Count() ? r1Data : r2Data;
|
||||
var visitTaskName = alldata.VisitTaskList.Select(x => x.BlindName).ToList();
|
||||
var length = alldata.VisitTaskList.Count();
|
||||
|
||||
// -1转为空
|
||||
List<QuestionType?> negativeToString = new List<QuestionType?>()
|
||||
{
|
||||
QuestionType.DaysBetween,
|
||||
};
|
||||
|
||||
async Task<GetReadingReportEvaluationOutDto> GetData(List<Arm> arms)
|
||||
{
|
||||
var data = new GetReadingReportEvaluationOutDto() { };
|
||||
var task = await _visitTaskRepository.Where(x =>
|
||||
x.SubjectId == inDto.SubjectId
|
||||
&& arms.Contains(x.ArmEnum)
|
||||
&& x.ReadingCategory== ReadingCategory.Visit
|
||||
&& x.ReadingTaskState == ReadingTaskState.HaveSigned
|
||||
&& x.TaskState == TaskState.Effect
|
||||
&& x.TrialReadingCriterionId==inDto.TrialCriterionId
|
||||
).OrderByDescending(x => x.VisitTaskNum).FirstOrDefaultAsync();
|
||||
if (task != null)
|
||||
{
|
||||
data = await GetReportsChartData(new GetReadingReportEvaluationInDto()
|
||||
{
|
||||
TrialId = task.TrialId,
|
||||
VisitTaskId = task.Id,
|
||||
});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
EvaluationColumn BuildEvaluationTable(GetReadingReportEvaluationOutDto r1, GetReadingReportEvaluationOutDto r2)
|
||||
{
|
||||
EvaluationColumn result = new EvaluationColumn()
|
||||
{
|
||||
Evaluation = new List<List<EvaluationValue>>() { }
|
||||
};
|
||||
|
||||
var baseLineAnswerType= QuestionType.ExistDisease;
|
||||
|
||||
var visitAnswerType = QuestionType.Tumor;
|
||||
switch (criterion.CriterionType)
|
||||
{
|
||||
case CriterionType.PCWG3:
|
||||
baseLineAnswerType= QuestionType.SiteVisitForTumorEvaluation;
|
||||
visitAnswerType = QuestionType.SiteVisitForTumorEvaluation;
|
||||
break;
|
||||
case CriterionType.Lugano2014WithoutPET:
|
||||
visitAnswerType = QuestionType.CTandMRI;
|
||||
break;
|
||||
case CriterionType.Lugano2014:
|
||||
visitAnswerType = QuestionType.ImgOncology;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
result.Evaluation.Add(visitTaskName.Select(x=> new EvaluationValue() {
|
||||
Value=x
|
||||
}).ToList());
|
||||
|
||||
var r1baseLine= r1.TaskQuestions
|
||||
.SelectMany(x => x.Childrens)
|
||||
.Where(x => x.QuestionType == baseLineAnswerType)
|
||||
.SelectMany(x => x.Answer.Select(a => new EvaluationValue
|
||||
{
|
||||
DictionaryCode = x.DictionaryCode,
|
||||
Value = a.Answer
|
||||
}))
|
||||
.ToList();
|
||||
|
||||
|
||||
var r1data = r1.TaskQuestions
|
||||
.SelectMany(x => x.Childrens)
|
||||
.Where(x => x.QuestionType == visitAnswerType)
|
||||
.SelectMany(x => x.Answer.Select(a => new EvaluationValue
|
||||
{
|
||||
DictionaryCode = x.DictionaryCode,
|
||||
Value = a.Answer
|
||||
}))
|
||||
.ToList();
|
||||
|
||||
r1data = r1baseLine.Take(1).Concat(r1data.Skip(1)).ToList();
|
||||
|
||||
r1data= r1data.Concat(Enumerable.Repeat(new EvaluationValue() { Value = "" }, length))
|
||||
.Take(length)
|
||||
.ToList();
|
||||
result.Evaluation.Add(r1data);
|
||||
|
||||
|
||||
if (criterion.ReadingType == ReadingMethod.Double)
|
||||
{
|
||||
var r2baseLine = r2.TaskQuestions
|
||||
.SelectMany(x => x.Childrens)
|
||||
.Where(x => x.QuestionType == baseLineAnswerType)
|
||||
.SelectMany(x => x.Answer.Select(a => new EvaluationValue
|
||||
{
|
||||
DictionaryCode = x.DictionaryCode,
|
||||
Value = a.Answer
|
||||
}))
|
||||
.ToList();
|
||||
|
||||
|
||||
var r2data = r2.TaskQuestions
|
||||
.SelectMany(x => x.Childrens)
|
||||
.Where(x => x.QuestionType == visitAnswerType)
|
||||
.SelectMany(x => x.Answer.Select(a => new EvaluationValue
|
||||
{
|
||||
DictionaryCode = x.DictionaryCode,
|
||||
Value = a.Answer
|
||||
}))
|
||||
.ToList();
|
||||
|
||||
r2data = r2baseLine.Take(1).Concat(r2data.Skip(1)).ToList();
|
||||
|
||||
r2data = r2data.Concat(Enumerable.Repeat(new EvaluationValue() { Value = "" }, length))
|
||||
.Take(length)
|
||||
.ToList();
|
||||
result.Evaluation.Add(r2data);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
List<ChartItem> BuildShowChartGroups(GetReadingReportEvaluationOutDto data, Arm arm)
|
||||
{
|
||||
var groups = new List<ChartItem>();
|
||||
if (alldata.TaskQuestions == null || alldata.VisitTaskList == null) return groups;
|
||||
foreach (var g in alldata.TaskQuestions)
|
||||
{
|
||||
var items = g.Childrens?.Where(c => c.ShowChartTypeEnum != ShowChartType.NotShow).ToList() ?? new List<ReadingReportDto>();
|
||||
if (items.Count == 0) continue;
|
||||
var chartList = new List<ReportChartData>();
|
||||
foreach (var item in items)
|
||||
{
|
||||
var itemdata= data.TaskQuestions.SelectMany(x => x.Childrens)
|
||||
.Where(x => x.QuestionId == item.QuestionId)
|
||||
.FirstOrDefault();
|
||||
var cd = new ReportChartData
|
||||
{
|
||||
Name = item?.QuestionName??string.Empty,
|
||||
Value = itemdata?.Answer?.Select(a => a.Answer).ToList() ?? new List<string>()
|
||||
};
|
||||
if (negativeToString.Contains(item.QuestionType))
|
||||
{
|
||||
cd.Value = cd.Value.Select(item => item == "-1"|| item == "-2" ? string.Empty : item).ToList();
|
||||
}
|
||||
chartList.Add(cd);
|
||||
}
|
||||
groups.Add(new ChartItem
|
||||
{
|
||||
Arm = arm,
|
||||
ChartType = null,
|
||||
GroupName = g.GroupName,
|
||||
Unit = items.FirstOrDefault()?.Unit,
|
||||
VisitTaskNameList = alldata.VisitTaskList.Select(x => x.BlindName).ToList(),
|
||||
LatestScanDateList = alldata.VisitTaskList.Select(x => x.LatestScanDate).ToList(),
|
||||
ChartDataList = chartList
|
||||
});
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
async Task<ChartItem> BuildTargetChart(GetReadingReportEvaluationOutDto data, Arm arm)
|
||||
{
|
||||
var item = new ChartItem
|
||||
{
|
||||
Arm = arm,
|
||||
ChartType = ReportChartType.Target,
|
||||
GroupName = _localizer["Tumor_TargetLesions"],
|
||||
VisitTaskNameList = alldata.VisitTaskList?.Select(x => x.BlindName).ToList() ?? new List<string>(),
|
||||
LatestScanDateList = alldata.VisitTaskList?.Select(x => x.LatestScanDate).ToList() ?? new List<DateTime?>(),
|
||||
ChartDataList = new List<ReportChartData>(),
|
||||
};
|
||||
if (data.VisitTaskList != null && data.TaskQuestions != null)
|
||||
{
|
||||
var chart = await GetReportsChartTypeData(new GetReportsChartTypeDataInDto
|
||||
{
|
||||
CriterionType = criterion.CriterionType,
|
||||
Data = data,
|
||||
ReportChartTypeEnum = ReportChartType.Target
|
||||
});
|
||||
item.ChartDataList = chart.ChartDataList;
|
||||
item.Unit = chart.Unit;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
result.Evaluation = BuildEvaluationTable(r1Data, r2Data);
|
||||
if (r1Data != null)
|
||||
{
|
||||
var r1Target = await BuildTargetChart(r1Data, Arm.DoubleReadingArm1);
|
||||
result.TargetCharts.Add(r1Target);
|
||||
var r1Groups = BuildShowChartGroups(r1Data, Arm.DoubleReadingArm1);
|
||||
result.QuestionCharts.AddRange(r1Groups);
|
||||
}
|
||||
|
||||
if (r2Data != null)
|
||||
{
|
||||
var r2Target = await BuildTargetChart(r2Data, Arm.DoubleReadingArm2);
|
||||
result.TargetCharts.Add(r2Target);
|
||||
var r2Groups = BuildShowChartGroups(r2Data, Arm.DoubleReadingArm2);
|
||||
result.QuestionCharts.AddRange(r2Groups);
|
||||
}
|
||||
|
||||
if (criterion.CriterionType == CriterionType.PCWG3)
|
||||
{
|
||||
result.TargetCharts = new List<ChartItem>() { };
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取报表数据
|
||||
/// </summary>
|
||||
/// <param name="indto"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<GetReadingReportEvaluationOutDto> GetReportsChartData(GetReadingReportEvaluationInDto indto)
|
||||
{
|
||||
GetReadingReportEvaluationOutDto result = new GetReadingReportEvaluationOutDto();
|
||||
var isConvertTask = await _visitTaskRepository.Where(x => x.Id == indto.VisitTaskId).Select(x => x.IsConvertedTask).FirstNotNullAsync();
|
||||
var visitTaskInfo = await _visitTaskRepository.Where(x => x.Id == indto.VisitTaskId).FirstNotNullAsync();
|
||||
result.CalculateResult = await this.GetReportVerify(new GetReportVerifyInDto()
|
||||
{
|
||||
VisitTaskId = indto.VisitTaskId,
|
||||
IsConvertTask = isConvertTask,
|
||||
BeforeConvertedTaskId = visitTaskInfo.BeforeConvertedTaskId,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
result.ReadingTaskState = visitTaskInfo.ReadingTaskState;
|
||||
var taskInfoList = await _generalCalculateService.GetReadingReportTaskList(indto.VisitTaskId);
|
||||
|
||||
result.VisitTaskList = taskInfoList;
|
||||
|
||||
var visitTaskIds = taskInfoList.Select(x => x.VisitTaskId).ToList();
|
||||
|
||||
var criterionId = visitTaskInfo.TrialReadingCriterionId;
|
||||
var questionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == criterionId)
|
||||
.ToListAsync();
|
||||
var tableQuestionList = await _readingTableQuestionTrialRepository.Where(x => x.TrialCriterionId == criterionId).OrderBy(x => x.ShowOrder).ToListAsync();
|
||||
var tableAnsweRowInfos = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == indto.VisitTaskId).ProjectTo<TableAnsweRowInfo>(_mapper.ConfigurationProvider).ToListAsync();
|
||||
|
||||
result.LesionCountList = tableAnsweRowInfos.GroupBy(x => x.LesionType).Select(x => new LesionDto
|
||||
{
|
||||
LesionType = x.Key!.Value,
|
||||
Count = x.ToList().Count()
|
||||
}).ToList();
|
||||
|
||||
var lesionsIndexs = await _readingTableAnswerRowInfoRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).GroupBy(x => new { x.QuestionId }).Select(x => new lesionsIndexDto()
|
||||
{
|
||||
QuestionId = x.Key.QuestionId,
|
||||
Rowindexs = x.Select(x => x.RowIndex).Distinct().OrderBy(x => x).ToList()
|
||||
|
||||
}).ToListAsync();
|
||||
|
||||
var answers = await _readingTaskQuestionAnswerRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).ToListAsync();
|
||||
var tableAnswers = await _readingTableQuestionAnswerRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).ToListAsync();
|
||||
var globalanswerList = await _readingGlobalTaskInfoRepository.Where(x => visitTaskIds.Contains(x.TaskId) && x.GlobalVisitTask.TaskState == TaskState.Effect && x.Answer != string.Empty).Select(x => new Globalanswer()
|
||||
{
|
||||
TaskId = x.TaskId,
|
||||
VisitTaskNum = x.GlobalVisitTask.VisitTaskNum,
|
||||
QuestionId = x.QuestionId,
|
||||
Answer = x.Answer
|
||||
}).ToListAsync();
|
||||
var alltableAnsweRowInfos = await _readingTableAnswerRowInfoRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).ToListAsync();
|
||||
var organIds = alltableAnsweRowInfos.Where(x => x.OrganInfoId != null).Select(x => x.OrganInfoId).Distinct().ToList();
|
||||
var organInfos = await _organInfoRepository.Where(x => organIds.Contains(x.Id)).ToListAsync();
|
||||
|
||||
var needChangeType = new List<QuestionMark?>() {
|
||||
QuestionMark.Organ,
|
||||
QuestionMark.Location,
|
||||
QuestionMark.Part,
|
||||
};
|
||||
|
||||
// 第一级
|
||||
|
||||
List<ReadingReportDto> questions = await _generalCalculateService.GetReadingReportQuestion
|
||||
(
|
||||
questionList,
|
||||
taskInfoList,
|
||||
globalanswerList,
|
||||
answers,
|
||||
tableAnsweRowInfos,
|
||||
tableQuestionList,
|
||||
alltableAnsweRowInfos,
|
||||
tableAnswers,
|
||||
organInfos,
|
||||
needChangeType
|
||||
);
|
||||
|
||||
|
||||
|
||||
result.TaskQuestions = questions;
|
||||
|
||||
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取阅片报告
|
||||
/// </summary>
|
||||
@@ -408,6 +796,39 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
SetCrterionDictionaryGroup(y, dictionaryGroup);
|
||||
});
|
||||
|
||||
// 处理标红 高亮
|
||||
#region 当第N个 存在被评估为NE的单个靶病灶 评估为 为是 之后 N+1 之后的最低点访视 都标红
|
||||
|
||||
var index = result.TaskQuestions.SelectMany(x => x.Childrens).Where(x => x.QuestionType == QuestionType.NETarget).SelectMany(x => x.Answer).ToList().FindIndex(x => x.Answer == ExistOrNA.Exist.GetEnumInt());
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
result.TaskQuestions.ForEach(x =>
|
||||
{
|
||||
x.Childrens.ForEach(y =>
|
||||
{
|
||||
|
||||
if (y.QuestionType == QuestionType.LowVisit)
|
||||
{
|
||||
int idx = 0;
|
||||
y.Answer.ForEach(z =>
|
||||
{
|
||||
if (idx > index)
|
||||
{
|
||||
z.IsHighlight = true;
|
||||
}
|
||||
idx++;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+52
-21
@@ -633,7 +633,9 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
|
||||
string errorMassage = string.Empty;
|
||||
|
||||
var rowAnswerList = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && (x.MeasureData == string.Empty || x.MeasureData == null))
|
||||
var rowAnswerList = await _readingTableAnswerRowInfoRepository.Where(x =>
|
||||
x.SplitOrMergeType!= SplitOrMergeType.Merge &&x.SplitOrMergeType != SplitOrMergeType.Merged &&
|
||||
x.VisitTaskId == inDto.VisitTaskId && (x.MeasureData == string.Empty || x.MeasureData == null))
|
||||
.Select(x => new
|
||||
{
|
||||
x.ReadingQuestionTrial.OrderMark,
|
||||
@@ -661,30 +663,33 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
List<string> stateIsNullList = tableAnswerList.Select(x => x.OrderMark + x.RowIndex.GetLesionMark()).ToList();
|
||||
|
||||
|
||||
// 交集
|
||||
List<string> allExists = measureDataList.Intersect(stateIsNullList).ToList();
|
||||
// 差集
|
||||
measureDataList = measureDataList.Except(allExists).ToList();
|
||||
stateIsNullList = stateIsNullList.Except(allExists).ToList();
|
||||
|
||||
|
||||
if (measureDataList.Count() > 0)
|
||||
{
|
||||
// 无标记
|
||||
errorMassage += _localizer["ReadingCalculate_NoMarker", string.Join(',', measureDataList)] + ",";
|
||||
}
|
||||
|
||||
|
||||
if (tableAnswerList.Count > 0)
|
||||
if (stateIsNullList.Count > 0)
|
||||
{
|
||||
// 状态为空
|
||||
errorMassage += _localizer["ReadingCalculate_StatusIsEmpty", string.Join(',', stateIsNullList)] + ",";
|
||||
}
|
||||
|
||||
if (allExists.Count > 0)
|
||||
{
|
||||
errorMassage += _localizer["ReadingCalculate_NoMarkerEmpty", string.Join(',', stateIsNullList)] + ",";
|
||||
// 未做标记且状态为空
|
||||
errorMassage += _localizer["ReadingCalculate_NoMarkerEmpty", string.Join(',', allExists)] + ",";
|
||||
}
|
||||
|
||||
|
||||
if (errorMassage != string.Empty)
|
||||
{
|
||||
errorMassage = _localizer["ReadingCalculate_Questionable"] + errorMassage;
|
||||
throw new BusinessValidationFailedException(errorMassage);
|
||||
}
|
||||
|
||||
@@ -704,7 +709,7 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
{
|
||||
var visitTaskId = inDto.VisitTaskId;
|
||||
|
||||
var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).ProjectTo<VisitTaskDto>(_mapper.ConfigurationProvider).FirstNotNullAsync();
|
||||
var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync();
|
||||
|
||||
var baseLineVisitId = await _subjectVisitRepository.Where(x => x.SubjectId == taskinfo.SubjectId && x.IsBaseLine).Select(x => x.Id).FirstOrDefaultAsync();
|
||||
|
||||
@@ -748,6 +753,20 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
|
||||
tableRowAnswers.ForEach(x =>
|
||||
{
|
||||
switch (x.SplitOrMergeType)
|
||||
{
|
||||
case SplitOrMergeType.Merge:
|
||||
case SplitOrMergeType.Merged:
|
||||
x.SplitOrMergeType = SplitOrMergeType.Merged;
|
||||
break;
|
||||
case SplitOrMergeType.MergeMain:
|
||||
case SplitOrMergeType.MergeMained:
|
||||
x.SplitOrMergeType = SplitOrMergeType.MergeMained;
|
||||
break;
|
||||
default:
|
||||
x.SplitOrMergeType = null;
|
||||
break;
|
||||
}
|
||||
x.VisitTaskId = visitTaskId;
|
||||
x.IsCurrentTaskAdd = false;
|
||||
x.Id = NewId.NextGuid();
|
||||
@@ -779,24 +798,35 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
// QuestionMark.ShortAxis,
|
||||
//};
|
||||
//}
|
||||
|
||||
var tableAnswers = copyTableAnswers.Select(x => new ReadingTableQuestionAnswer
|
||||
{
|
||||
Id = NewId.NextGuid(),
|
||||
Answer = notNeedCopyMarks.Contains(x.QuestionMark) ? string.Empty : x.Answer,
|
||||
QuestionId = x.QuestionId,
|
||||
RowIndex = x.RowIndex,
|
||||
RowId = tableRowAnswers.Where(y => y.OriginalId == x.RowId).Select(x => x.Id).FirstOrDefault(),
|
||||
TableQuestionId = x.TableQuestionId,
|
||||
TrialId = x.TrialId,
|
||||
VisitTaskId = visitTaskId,
|
||||
});
|
||||
var mergedRowIds = tableRowAnswers.Where(x => x.SplitOrMergeType == SplitOrMergeType.Merged).Select(x => x.Id).ToHashSet();
|
||||
var tableAnswers = copyTableAnswers
|
||||
.Select(x =>
|
||||
{
|
||||
var rowId = tableRowAnswers.Where(y => y.OriginalId == x.RowId).Select(y => y.Id).FirstOrDefault();
|
||||
var answer = notNeedCopyMarks.Contains(x.QuestionMark) ? string.Empty : x.Answer;
|
||||
if (mergedRowIds.Contains(rowId) && (x.QuestionMark == QuestionMark.State || x.QuestionMark == QuestionMark.ShortAxis || x.QuestionMark == QuestionMark.MajorAxis))
|
||||
{
|
||||
answer = x.Answer;
|
||||
}
|
||||
return new ReadingTableQuestionAnswer
|
||||
{
|
||||
Id = NewId.NextGuid(),
|
||||
Answer = answer,
|
||||
QuestionId = x.QuestionId,
|
||||
RowIndex = x.RowIndex,
|
||||
RowId = rowId,
|
||||
TableQuestionId = x.TableQuestionId,
|
||||
TrialId = x.TrialId,
|
||||
VisitTaskId = visitTaskId,
|
||||
};
|
||||
})
|
||||
.ToList();
|
||||
|
||||
|
||||
|
||||
|
||||
var addList = _mapper.Map<List<ReadingTableAnswerRowInfo>>(tableRowAnswers);
|
||||
|
||||
await _generalCalculateService.CopyHistoryAnswer(taskinfo, addList, tableAnswers);
|
||||
await _readingTableAnswerRowInfoRepository.AddRangeAsync(addList);
|
||||
await _readingTableQuestionAnswerRepository.AddRangeAsync(tableAnswers);
|
||||
await _readingTableQuestionAnswerRepository.SaveChangesAsync();
|
||||
@@ -1110,7 +1140,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
|
||||
return ExistOrNA.NA.GetEnumInt();
|
||||
}
|
||||
|
||||
var result = inDto.QuestionInfo.Any(x => x.QuestionType == QuestionType.TargetLesion && x.Answer.EqEnum(TargetAssessment.NE));
|
||||
var result = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).ToList().Any(x => x.TableQuestionList.Any(y =>
|
||||
y.QuestionMark == QuestionMark.State && y.Answer.EqEnum(TargetState.UnableEvaluate)));
|
||||
|
||||
return result ? ExistOrNA.Exist.GetEnumInt() : ExistOrNA.NotExist.GetEnumInt();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user