Compare commits

..

139 Commits

Author SHA1 Message Date
hang 8580c64ef7 非dicom bodyPart 修改 2025-11-24 15:24:25 +08:00
hang 64c657d233 修改检查部位bug 2025-11-24 14:35:47 +08:00
hang 73b7aae6c0 提交后允许删除检查特殊逻辑修改
continuous-integration/drone/push Build is passing Details
2025-11-13 16:25:54 +08:00
he 14e212c4a8 覆盖TrialImageDownloadService
continuous-integration/drone/push Build is passing Details
2025-10-16 13:33:42 +08:00
he 344146a46e 验证修改
continuous-integration/drone/push Build is failing Details
2025-10-16 13:24:25 +08:00
he cd5a1fcbb7 修改 2025-10-16 13:24:03 +08:00
he 424448db2f 添加表格预设 2025-10-16 13:23:46 +08:00
he 629116cb6a 合并
continuous-integration/drone/push Build is failing Details
2025-10-16 13:20:01 +08:00
hang 64c3d96807 修改维护统计数字问题
continuous-integration/drone/push Build is passing Details
2025-09-23 09:37:26 +08:00
hang d607018832 数据库存在脏数据,导致归档失败
continuous-integration/drone/push Build is passing Details
2025-09-22 16:04:07 +08:00
hang 0d65625caa 修改维护数据代码-3测试
continuous-integration/drone/push Build is passing Details
2025-09-22 15:41:34 +08:00
hang 2b82044213 修改维护数据代码-2
continuous-integration/drone/push Build is passing Details
2025-09-22 14:49:51 +08:00
hang 2769cf7efd 修改请求方式
continuous-integration/drone/push Build is passing Details
2025-09-18 17:24:25 +08:00
hang 1c84a374a1 参数必填限制
continuous-integration/drone/push Build is passing Details
2025-09-18 17:22:09 +08:00
hang 2a59cfe3b5 修改实际恢复情况
continuous-integration/drone/push Build is passing Details
2025-09-18 13:44:44 +08:00
hang 0dff3d6e4b 记录重复修改
continuous-integration/drone/push Build is passing Details
2025-09-18 13:17:18 +08:00
hang 1d6731e3a4 设置文件大小
continuous-integration/drone/push Build is passing Details
2025-09-18 08:58:14 +08:00
hang 224318e0c4 配置文件修改
continuous-integration/drone/push Build is passing Details
2025-09-17 17:08:30 +08:00
hang 7096062d76 增加日志测试
continuous-integration/drone/push Build is passing Details
2025-09-17 17:05:40 +08:00
hang a0b06fbc28 修改维护数据
continuous-integration/drone/push Build is passing Details
2025-09-17 16:44:00 +08:00
hang 4a76705ec7 Uat 分支维护数据提交
continuous-integration/drone/push Build is passing Details
2025-09-17 16:02:29 +08:00
hang 7676e3cc4d 维护数据第二次提交
continuous-integration/drone/push Build is passing Details
2025-09-16 10:18:16 +08:00
hang 234a5706a8 下载删除标记方法提交
continuous-integration/drone/push Build is passing Details
2025-09-15 17:49:12 +08:00
he 956e214632 Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-08-18 11:44:27 +08:00
he 974adb4367 Merge branch 'Uat_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-08-15 13:18:39 +08:00
he 00189b992f 修改稽查 2025-08-15 13:18:37 +08:00
hang f192219621 合并问题
continuous-integration/drone/push Build is passing Details
2025-08-07 11:37:24 +08:00
hang 4b0d6fe8b9 Test 合并到Uat 冲突解决
continuous-integration/drone/push Build is failing Details
2025-08-07 11:30:37 +08:00
hang bb6dc19924 更新里面有添加bug
continuous-integration/drone/push Build is passing Details
2025-07-29 15:21:18 +08:00
hang ec9af97742 初审通过bug 修改
continuous-integration/drone/push Build is passing Details
2025-07-29 09:07:55 +08:00
hang a5472e1509 取tag 做判断
continuous-integration/drone/push Build is passing Details
2025-07-25 10:30:31 +08:00
hang a1bb730cfe Merge branch 'Uat_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-07-23 15:00:27 +08:00
hang 1aa411c0f8 下一个任务不会跳复核任务 2025-07-23 15:00:25 +08:00
he 10de82132d Merge branch 'Uat_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-07-23 13:34:56 +08:00
he db11656862 邮件修改 区分CRC与CRA 2025-07-23 13:34:54 +08:00
hang a47ac76729 复核指派bug修改
continuous-integration/drone/push Build is passing Details
2025-07-22 15:29:09 +08:00
hang f86a88332c 复核稽查修改016
continuous-integration/drone/push Build is passing Details
2025-07-18 13:15:23 +08:00
hang f6be5e4355 复核稽查修改015
continuous-integration/drone/push Build is failing Details
2025-07-18 13:04:58 +08:00
hang 8deeb0290a 复核稽查修改014
continuous-integration/drone/push Build is failing Details
2025-07-18 12:17:55 +08:00
hang fb6231373b 复核稽查修改013
continuous-integration/drone/push Build is passing Details
2025-07-18 11:34:13 +08:00
hang a407313a95 复核稽查修改012
continuous-integration/drone/push Build is passing Details
2025-07-18 09:25:56 +08:00
hang b1806d2eff 复核稽查修改011
continuous-integration/drone/push Build is passing Details
2025-07-17 17:25:45 +08:00
hang 19351f34e1 复核稽查修改010
continuous-integration/drone/push Build is passing Details
2025-07-17 17:10:06 +08:00
hang e0f708082d 复核稽查修改009
continuous-integration/drone/push Build is passing Details
2025-07-17 16:49:20 +08:00
hang 44464a809d 复核稽查修改008
continuous-integration/drone/push Build is passing Details
2025-07-17 15:26:28 +08:00
hang c7c01b7ba3 复核稽查修改007
continuous-integration/drone/push Build is passing Details
2025-07-17 15:08:11 +08:00
hang 38b71d5267 复核稽查修改006
continuous-integration/drone/push Build is passing Details
2025-07-17 14:00:03 +08:00
hang 3950a3453a 复核稽查修改005
continuous-integration/drone/push Build is passing Details
2025-07-17 13:26:44 +08:00
hang 7aa59da586 复核稽查修改004
continuous-integration/drone/push Build is passing Details
2025-07-17 11:51:41 +08:00
hang 9b2fbdb13f 复核稽查修改003
continuous-integration/drone/push Build is passing Details
2025-07-17 11:28:47 +08:00
hang c98b9e1bd4 复核稽查修改002
continuous-integration/drone/push Build is passing Details
2025-07-17 11:08:02 +08:00
hang 9522461508 复核稽查修改
continuous-integration/drone/push Build is passing Details
2025-07-17 10:34:15 +08:00
hang 5bb99180b6 修改复核-退回-003
continuous-integration/drone/push Build is passing Details
2025-07-16 17:59:42 +08:00
hang 62ec1e4955 修改复核查询接口-002
continuous-integration/drone/push Build is passing Details
2025-07-16 17:34:09 +08:00
hang 58e1af24c9 修改复核查询接口-001
continuous-integration/drone/push Build is passing Details
2025-07-16 16:30:03 +08:00
hang a7394e7119 指派其他质控-uat-13
continuous-integration/drone/push Build is passing Details
2025-07-16 10:30:55 +08:00
hang a93d81de0d 指派其他质控-uat-12 2025-07-16 10:30:51 +08:00
hang 6aafe02c6c 指派其他质控-uat-11 2025-07-16 10:30:48 +08:00
hang 1511ef1608 修改QC复核 -二次提交-uat-10 2025-07-16 10:30:43 +08:00
hang 1141439c8e 修改QC复核 -二次提交-uat-9 2025-07-16 10:30:32 +08:00
hang 559663082b 修改QC复核 -二次提交-uat-8 2025-07-16 10:26:30 +08:00
hang 3a298518f9 修改QC复核 -二次提交-uat-7 2025-07-16 10:26:25 +08:00
hang 7f20232401 修改QC复核 -二次提交-uat-6 2025-07-16 10:26:21 +08:00
hang 20b0849b40 修改QC复核 -二次提交-uat-5 2025-07-16 10:26:16 +08:00
hang db951e9195 修改QC复核 -二次提交-uat-4 2025-07-16 10:26:13 +08:00
hang d5d78a99d2 QC复核 -二次提交-uat-3 2025-07-16 10:26:08 +08:00
hang 1609a7a88a QC复核 -二次提交-uat-3 2025-07-16 10:26:03 +08:00
hang 289831fce8 QC复核 -二次提交-uat-2 2025-07-16 10:25:59 +08:00
hang dde058be18 QC 审核提交--迁移uat 2025-07-16 10:25:26 +08:00
hang be8b730ee4 返回文件大小
continuous-integration/drone/push Build is passing Details
2025-07-14 15:00:36 +08:00
hang 28360861aa 修改subject 随机阅片下载影像重阅bug
continuous-integration/drone/push Build is passing Details
2025-07-14 13:36:27 +08:00
hang 3e097fa42b uat 序列获取Instance 增加文件大小 2025-07-14 13:32:21 +08:00
hang b8a7cd6ebc 修改非dicom 设置删除,检查上的数量不变更 2025-07-14 13:32:16 +08:00
hang a9aefaf240 修改预览看到阅片影像 修改后受试者随机bug修改 4 2025-07-14 13:32:10 +08:00
hang 266a0c5dd5 修改后受试者随机bug修改 3 2025-07-14 13:32:05 +08:00
hang 54822ae038 修改后受试者随机bug修改 2 2025-07-14 13:31:07 +08:00
hang 05203d6716 受试者随机bug修改 2025-07-14 13:31:00 +08:00
hang 2ce8edde80 uat升级excel 组件测试
continuous-integration/drone/push Build is passing Details
2025-07-05 13:34:09 +08:00
hang 3c30f5dc14 完全随机跟下载关联
continuous-integration/drone/push Build is passing Details
2025-07-03 14:54:23 +08:00
hang e4ad1c0653 增加失访可读配置-uat
continuous-integration/drone/push Build is passing Details
2025-06-25 15:31:59 +08:00
he 84df49ca07 单位修改2
continuous-integration/drone/push Build is passing Details
2025-06-24 14:58:39 +08:00
he 400edd0b70 修改单位1 2025-06-24 14:58:37 +08:00
he c4add1ecc8 修改单位 2025-06-24 14:58:35 +08:00
he d5879e28d7 修改OCT计算触发逻辑
continuous-integration/drone/push Build is passing Details
2025-06-24 14:07:31 +08:00
he 37e083face 阅片标准默认值修改 2025-06-24 14:07:28 +08:00
he 290506a060 保存测量值
continuous-integration/drone/push Build is passing Details
2025-06-24 09:28:27 +08:00
he 1b4bc3690a 稽查保存Ivus和Oct 2025-06-24 09:28:25 +08:00
he 6374781d26 修改阅片期计划的排序
continuous-integration/drone/push Build is failing Details
2025-06-20 17:33:23 +08:00
he b28013a02f 阅片期计划可以选择基线 2025-06-20 17:32:07 +08:00
he fe429d2bdc 修改受试者内随机
continuous-integration/drone/push Build is passing Details
2025-06-20 09:37:48 +08:00
he d63af2ed20 修改稽查的单位翻译
continuous-integration/drone/push Build is passing Details
2025-06-19 17:16:42 +08:00
hang cebf9875b1 禁用人员系统查看文档
continuous-integration/drone/push Build is passing Details
2025-06-19 10:31:24 +08:00
hang 131681a0b3 完全随机触发系统盲态名称 2025-06-19 10:22:06 +08:00
hang f833adc710 修改有序阅片生成任务bug 2025-06-19 10:21:59 +08:00
hang df80d1e551 修改影像下载bug
continuous-integration/drone/push Build is passing Details
2025-06-18 10:36:26 +08:00
he 82dc092247 修改阅片期计划生成裁判的逻辑
continuous-integration/drone/push Build is passing Details
2025-06-17 16:42:47 +08:00
he 44894fcaa9 生成阅片期计划的时候 产生裁判
continuous-integration/drone/push Build is passing Details
2025-06-17 16:24:00 +08:00
hang 083bebdc03 真实随机阅片导表-1
continuous-integration/drone/push Build is passing Details
2025-06-17 10:06:41 +08:00
hang e808764dd6 随机阅片排序-5 2025-06-17 10:06:33 +08:00
hang 77d198cb09 随机阅片-NextTask-5 2025-06-17 10:05:55 +08:00
hang 180b02b28c 随机阅片NextTask-4 2025-06-17 10:04:26 +08:00
hang c49a74d742 Merge branch 'Uat_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-17 10:02:34 +08:00
he c76fc8f052 修改阅片期选择访视下拉框的数据
continuous-integration/drone/push Build is running Details
2025-06-17 10:02:04 +08:00
he 14875dfacc 生成了裁判任务 阅片期不让删除和修改 2025-06-17 10:02:02 +08:00
hang 0a68788c0f 随机阅片序号3 2025-06-17 10:00:39 +08:00
hang b17e5d9ff3 随机阅片序号2 2025-06-17 10:00:34 +08:00
hang 05ce4b7155 随机阅片随机序号 2025-06-17 10:00:28 +08:00
he c8bbb3da48 Merge branch 'Uat_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-16 15:16:20 +08:00
he 65de108ecd 添加阅片期 产生裁判 2025-06-16 15:16:19 +08:00
hang d20c306219 末次访视的展示+ 展示基线选择阅片期
continuous-integration/drone/push Build is passing Details
2025-06-16 14:50:23 +08:00
hang e69991ae37 修改重置阅片稽查3
continuous-integration/drone/push Build is passing Details
2025-06-13 09:57:33 +08:00
hang e1e2544ee3 修改阅片重置稽查 2025-06-13 09:57:30 +08:00
hang 318333bae4 修改重置阅片稽查 2025-06-13 09:57:26 +08:00
hang d569de4dfe 确认浏览临床数据
continuous-integration/drone/push Build is passing Details
2025-06-12 10:52:46 +08:00
hang 05627ff126 肿瘤学稽查修改
continuous-integration/drone/push Build is passing Details
2025-06-11 10:03:01 +08:00
hang 627618c854 增加进入阅片中稽查 2025-06-11 10:02:57 +08:00
hang 2711c5eb04 稽查 申请PM 重阅,有SPM 判断 2025-06-11 10:00:57 +08:00
hang a6982f79c3 稽查修改,区分标识 2025-06-11 10:00:53 +08:00
hang 2a542949cd 增加稽查标识 2025-06-11 10:00:49 +08:00
he a8ccbce76f 稽查添加
continuous-integration/drone/push Build is passing Details
2025-06-05 16:46:06 +08:00
he df7cf30aed 测量值可以为空
continuous-integration/drone/push Build is passing Details
2025-06-04 14:25:57 +08:00
he 0664fbdfb7 验证修改
continuous-integration/drone/push Build is passing Details
2025-06-04 09:57:08 +08:00
he 0a6ee05417 Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-22 18:01:11 +08:00
hang f9bd8c3f5c Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-21 15:16:26 +08:00
he cb83c2a737 Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-21 14:05:30 +08:00
he fdd9afd4f0 Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-20 15:46:24 +08:00
he 53d26c0445 Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-20 10:55:36 +08:00
he 40d035e7a7 解决合并的冲突
continuous-integration/drone/push Build is passing Details
2025-05-15 09:33:17 +08:00
he a13807ae5f 合并冲突修改
continuous-integration/drone/push Build is failing Details
2025-05-15 09:29:41 +08:00
he 9d5aaf1e26 Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is failing Details
2025-05-15 09:20:22 +08:00
he c347d08e2b 合并
continuous-integration/drone/push Build is failing Details
2025-05-15 09:14:10 +08:00
hang 87f2d0e429 非dicom 删除上传
continuous-integration/drone/push Build is passing Details
2025-04-15 13:27:33 +08:00
he 89d009fcbf Revert "修改"
continuous-integration/drone/push Build is passing Details
This reverts commit 7d65cf5051.
2025-04-09 11:00:45 +08:00
he 7d65cf5051 修改
continuous-integration/drone/push Build is passing Details
2025-04-09 09:45:49 +08:00
he e74427c45c 解决冲突
continuous-integration/drone/push Build is passing Details
2025-04-08 11:05:31 +08:00
he e8825c7efa 修改查询 2025-04-08 11:04:52 +08:00
hang 628e0ad034 修改异地登录bug 设置最后一次登录的iP
continuous-integration/drone/push Build is passing Details
2025-04-03 13:55:04 +08:00
hang 2f3f639918 修改异地登录ip逻辑 2025-04-03 13:55:00 +08:00
hang 87caf24dd1 验证环境生成缩略图测试ok提交
continuous-integration/drone/push Build is passing Details
2025-04-03 13:49:34 +08:00
308 changed files with 5664 additions and 830605 deletions

View File

@ -14,9 +14,9 @@
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.401.81" />
<PackageReference Include="DistributedLock.Core" Version="1.0.8" />
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
<PackageReference Include="fo-dicom" Version="5.2.4" />
<PackageReference Include="fo-dicom.Codecs" Version="5.16.4" />
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.4" />
<PackageReference Include="fo-dicom" Version="5.2.1" />
<PackageReference Include="fo-dicom.Codecs" Version="5.16.1" />
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Minio" Version="6.0.3" />

View File

@ -113,9 +113,6 @@ builder.Services.AddAutoMapper(automapper =>
//EF ORM QueryWithNoLock
builder.Services.AddEFSetup(_configuration);
// FusionCache
builder.Services.AddFusionCache();
builder.Services.AddMediator(cfg =>
{
@ -194,7 +191,6 @@ 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();

View File

@ -1,29 +1,28 @@
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 FellowOakDicom.Network;
using FellowOakDicom;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ZiggyCreatures.Caching.Fusion;
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;
namespace IRaCIS.Core.SCP.Service
{
@ -52,8 +51,6 @@ namespace IRaCIS.Core.SCP.Service
private Guid _trialSiteId { get; set; }
private bool _releasedNormally = false;
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
@ -121,7 +118,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)
{
@ -130,7 +127,7 @@ namespace IRaCIS.Core.SCP.Service
isCanReceiveIamge = true;
}
}
if (association.CallingAE == "test-callingAE")
@ -183,15 +180,12 @@ namespace IRaCIS.Core.SCP.Service
await AddUploadLogAsync();
_releasedNormally = true;
Log.Logger.Information($"进入释放连接请求 {_releasedNormally}");
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
//将检查设置为传输结束
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
//var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
////将检查设置为传输结束
//await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
//await _studyRepository.SaveChangesAndClearAllTrackingAsync();
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
}
@ -205,25 +199,15 @@ namespace IRaCIS.Core.SCP.Service
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
//有时候没有建立连接会走关闭通过日志发现
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.EndTime = DateTime.Now;
_upload.StudyCount = _ImageUploadList.Count;
_upload.TrialId = _trialId;
_upload.TrialSiteId = _trialSiteId;
_upload.UploadJsonStr = (new SCPImageLog() { UploadList = _ImageUploadList }).ToJsonStr();
//可能是测试echo 导致记录了
await _SCPImageUploadRepository.AddAsync(_upload, _upload.FileCount > 0 ? true : false);
}
@ -278,26 +262,23 @@ namespace IRaCIS.Core.SCP.Service
public async void OnConnectionClosed(Exception exception)
{
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
/* nothing to do here */
if (exception != null || _releasedNormally == false)
//奇怪的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.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = true });
//记录日志
await AddUploadLogAsync();
//await _studyRepository.SaveChangesAndClearAllTrackingAsync();
}
else
{
//将检查设置为传输结束
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = false });
//记录日志
await AddUploadLogAsync();
}
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
}
@ -335,11 +316,6 @@ 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}";
@ -348,383 +324,74 @@ namespace IRaCIS.Core.SCP.Service
long fileSize = 0;
try
{
// 直接拿 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)
using (MemoryStream ms = new MemoryStream())
{
await request.File.SaveAsync(ms);
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
#region 1帧拆成多个固定大小的方便移动端浏览
dataset.AddOrUpdate(dicomTag, item.ReplaceValue);
}
// 回到开头,读取 dicom
ms.Position = 0;
var dicomFile = DicomFile.Open(ms);
foreach (var item in ircFiledList)
{
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
if (dicomTag == DicomTag.ClinicalTrialProtocolID)
{
dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolID, trialSiteInfo.TrialCode);
}
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);
var syntax = pixelData.Syntax;
if (pixelData != null)
// 每个 fragment 固定大小 (64KB 示例,可以自己调整)
int fragmentSize = 20 * 1024;
if (syntax.IsEncapsulated)
{
try
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
for (int n = 0; n < pixelData.NumberOfFrames; n++)
{
var frameData = pixelData.GetFrame(n); // 获取完整一帧
var data = frameData.Data;
int offset = 0;
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)
while (offset < data.Length)
{
int size = Math.Min(fragmentSize, data.Length - offset);
var buffer = new byte[size];
Buffer.BlockCopy(data, offset, buffer, 0, 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);
newFragments.Fragments.Add(new MemoryByteBuffer(buffer));
offset += size;
}
//传递过来的就是拆分的,但是是没有偏移表的,我需要自己创建偏移表,不然生成缩略图失败
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
#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;
#endregion
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
#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);
using (await @lock.AcquireAsync())
{
try
{
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(dicomFile, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE, fileSize);
#endregion
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++;
}
}
//irc 从路径最后一截取Guid
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
fileSize = ms.Length;
}
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
@ -735,6 +402,87 @@ 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);
}

View File

@ -1,101 +0,0 @@
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;
}
}

View File

@ -1,32 +1,49 @@
using FellowOakDicom;
using FellowOakDicom.Imaging.Codec;
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 MassTransit;
using Medallion.Threading;
using Microsoft.AspNetCore.Hosting;
using Serilog.Sinks.File;
using System.Data;
using System.Runtime.Intrinsics.X86;
using IRaCIS.Core.Domain.Share;
using System.Text;
using ZiggyCreatures.Caching.Fusion;
using static IRaCIS.Core.SCP.Service.CacheHelper;
using Microsoft.AspNetCore.Hosting;
using IRaCIS.Core.Infrastructure;
using Medallion.Threading;
using FellowOakDicom;
using FellowOakDicom.Imaging.Codec;
using System.Data;
using IRaCIS.Core.Domain.Models;
using FellowOakDicom.Network;
using IRaCIS.Core.SCP.Service;
using IRaCIS.Core.Infra.EFCore;
using MassTransit;
using System.Runtime.Intrinsics.X86;
using Serilog.Sinks.File;
using IRaCIS.Core.Infrastructure.Extention;
namespace IRaCIS.Core.SCP.Service
{
public class DicomArchiveService(IRepository<SCPPatient> _patientRepository,
IRepository<SCPStudy> _studyRepository,
IRepository<SCPSeries> _seriesRepository,
IRepository<SCPInstance> _instanceRepository
) : BaseService, IDicomArchiveService
public class DicomArchiveService : 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;
}
@ -36,19 +53,18 @@ 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());
@ -61,9 +77,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);
@ -77,8 +93,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),
@ -106,7 +122,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;
// 如果生日还未到,年龄减去一岁
@ -204,12 +220,6 @@ namespace IRaCIS.Core.SCP.Service
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
Manufacturer = dataset.GetSingleValueOrDefault(DicomTag.Manufacturer, string.Empty),
ManufacturerModelName = dataset.GetSingleValueOrDefault(DicomTag.ManufacturerModelName, string.Empty),
DeviceSerialNumber = dataset.GetSingleValueOrDefault(DicomTag.DeviceSerialNumber, string.Empty),
DeviceUID = dataset.GetSingleValueOrDefault(DicomTag.DeviceUID, string.Empty),
SoftwareVersions = dataset.GetSingleValueOrDefault(DicomTag.SoftwareVersions, string.Empty),
PatientWeight = dataset.GetSingleValueOrDefault(DicomTag.PatientWeight, string.Empty),
//IsDoubleReview = addtionalInfo.IsDoubleReview,
@ -229,7 +239,6 @@ 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);
@ -272,9 +281,6 @@ namespace IRaCIS.Core.SCP.Service
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
RadiopharmaceuticalInformationSequence = dataset.GetSingleValueOrDefault(DicomTag.RadiopharmaceuticalInformationSequence, string.Empty),
AcquisitionDate = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionDate, string.Empty),
InstanceCount = 0
};
@ -319,31 +325,16 @@ namespace IRaCIS.Core.SCP.Service
SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0),
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1),
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0),
PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty),
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty),
WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty),
WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty),
PhotometricInterpretation = dataset.GetSingleValueOrDefault(DicomTag.PhotometricInterpretation, string.Empty),
BitsAllocated = dataset.GetSingleValueOrDefault(DicomTag.BitsAllocated, 0),
PixelRepresentation = dataset.GetSingleValueOrDefault(DicomTag.PixelRepresentation, string.Empty),
RescaleIntercept = dataset.GetSingleValueOrDefault(DicomTag.RescaleIntercept, string.Empty),
RescaleSlope = dataset.GetSingleValueOrDefault(DicomTag.RescaleSlope, string.Empty),
ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty),
ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty),
SequenceOfUltrasoundRegions = dataset.GetSingleValueOrDefault(DicomTag.SequenceOfUltrasoundRegions, string.Empty),
FrameTime = dataset.GetSingleValueOrDefault(DicomTag.FrameTime, string.Empty),
CorrectedImage = dataset.GetSingleValueOrDefault(DicomTag.CorrectedImage, string.Empty),
Units = dataset.GetSingleValueOrDefault(DicomTag.Units, string.Empty),
DecayCorrection = dataset.GetSingleValueOrDefault(DicomTag.DecayCorrection, string.Empty),
EncapsulatedDocument = dataset.GetSingleValueOrDefault(DicomTag.EncapsulatedDocument, string.Empty),
Path = fileRelativePath,
FileSize = fileSize,
FileSize= fileSize,
};
@ -383,7 +374,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();

View File

@ -126,7 +126,7 @@ namespace IRaCIS.Api.Controllers
var token = _tokenService.GetToken(new UserTokenInfo()
{
IdentityUserId = Guid.NewGuid(),
UserName = "ImageShare",
UserName = "Share001",
UserTypeEnum = UserTypeEnum.ShareImage,
});
@ -279,7 +279,7 @@ namespace IRaCIS.Api.Controllers
var isExpire = _tokenService.IsTokenExpired(token);
if ( Guid.TryParse(userId,out _) == false || isExpire || !await _useRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd) )
if (!await _useRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd) || isExpire)
{
decodeUrl = errorUrl + $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(I18n.T("UserRedirect_InitializationLinkExpire"))} ";
}

View File

@ -273,7 +273,6 @@ namespace IRaCIS.Core.API.Controllers
public List<OSSFileDTO> UploadedFileList { get; set; } = new List<OSSFileDTO>();
public bool? IsImageSegmentLabel { get; set; }
public class OSSFileDTO
{
@ -522,17 +521,7 @@ namespace IRaCIS.Core.API.Controllers
}
else
{
if (incommand.IsImageSegmentLabel == true)
{
await _noneDicomStudyFileRepository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, ImageLabelNoneDicomStudyId = noneDicomStudyId.Value, FileType = item.FileType, FileSize = item.FileFize });
}
else
{
await _noneDicomStudyFileRepository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, NoneDicomStudyId = noneDicomStudyId.Value, FileType = item.FileType, FileSize = item.FileFize });
}
await _noneDicomStudyFileRepository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, NoneDicomStudyId = noneDicomStudyId.Value, FileType = item.FileType, FileSize = item.FileFize });
}
@ -568,7 +557,6 @@ namespace IRaCIS.Core.API.Controllers
/// 一致性核查 excel上传 支持三种格式
/// </summary>
/// <param name="trialId"></param>
/// <param name="isFullCheck"></param>
/// <param name="oSSService"></param>
/// <param name="_inspectionFileRepository"></param>
/// <returns></returns>
@ -576,14 +564,12 @@ namespace IRaCIS.Core.API.Controllers
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
[TrialGlobalLimit("AfterStopCannNotOpt")]
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, bool isFullCheck, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
{
var fileName = string.Empty;
var templateFileStream = new MemoryStream();
var inspectionFileId = Guid.Empty;
await FileUploadToOSSAsync(async (realFileName, fileStream) =>
{
fileName = realFileName;
@ -600,9 +586,7 @@ namespace IRaCIS.Core.API.Controllers
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, $"{trialId.ToString()}/InspectionUpload/DataReconciliation", realFileName);
var addEntity = await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId }, true);
inspectionFileId = addEntity.Id;
await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId }, true);
return ossRelativePath;
@ -748,11 +732,8 @@ namespace IRaCIS.Core.API.Controllers
if (etcCheckList == null || etcCheckList.Count == 0)
{
await _inspectionFileRepository.BatchUpdateNoTrackingAsync(t => t.Id == inspectionFileId, u => new InspectionFile() { CheckState = EDCCheckState.Failed });
//---请保证上传数据符合模板文件中的样式,且存在有效数据。
return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]);
}
else
{
@ -772,8 +753,6 @@ namespace IRaCIS.Core.API.Controllers
if (etcCheckList.Count == 0)
{
await _inspectionFileRepository.BatchUpdateNoTrackingAsync(t => t.Id == inspectionFileId, u => new InspectionFile() { CheckState = EDCCheckState.Failed });
//---请保证上传数据符合模板文件中的样式,且存在有效数据。
return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]);
}
@ -785,19 +764,8 @@ namespace IRaCIS.Core.API.Controllers
//var client = _mediator.CreateRequestClient<ConsistenCheckCommand>();
//await client.GetResponse<ConsistenCheckResult>(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
if (isFullCheck)
{
await _mediator.Send(new ConsistenFullCheckCommand() { ETCList = etcCheckList, TrialId = trialId, InspectionFileId = inspectionFileId });
}
else
{
//不获取结果,不用定义返回类型
await _mediator.Send(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
}
//不获取结果,不用定义返回类型
await _mediator.Send(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
return ResponseOutput.Ok();
@ -1004,11 +972,7 @@ namespace IRaCIS.Core.API.Controllers
EmailBodyHtml = 4,
ReadKeyFile = 5,
Other = 6,
Other = 5
}
/// <summary>
@ -1040,9 +1004,6 @@ namespace IRaCIS.Core.API.Controllers
case UploadFileType.EmailBodyHtml:
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemFileUploadPath(_hostEnvironment, StaticData.Folder.EmailTemplate, fileName));
break;
case UploadFileType.ReadKeyFile:
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemFileUploadPath(_hostEnvironment, StaticData.Folder.ReadKetFile, fileName));
break;
default:
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetOtherFileUploadPath(_hostEnvironment, StaticData.Folder.TempFile, fileName));

View File

@ -15,13 +15,12 @@ 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
@ -57,9 +56,7 @@ 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 })
.ToListAsync();
@ -71,11 +68,11 @@ public class HangfireHostService(IRecurringMessageScheduler _recurringMessageSch
var trialId = task.TrialId;
HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, trialId, task.BusinessScenarioEnum, task.EmailCron, defaultCulture);
HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, trialId, task.BusinessScenarioEnum, task.EmailCron);
}
// 系统邮件定时任务 要设置默认语言
// 系统邮件定时任务
var systemTaskInfoList = await _emailNoticeConfigrepository.Where(t => t.EmailCron != string.Empty && t.IsAutoSend)
.Select(t => new { t.Id, t.Code, t.EmailCron, t.BusinessScenarioEnum, })
.ToListAsync();
@ -85,7 +82,7 @@ public class HangfireHostService(IRecurringMessageScheduler _recurringMessageSch
//利用主键作为任务Id
var jobId = $"{task.Id}_({task.BusinessScenarioEnum})";
HangfireJobHelper.AddOrUpdateTimingCronJob(jobId, task.BusinessScenarioEnum, task.EmailCron, defaultCulture);
HangfireJobHelper.AddOrUpdateTimingCronJob(jobId, task.BusinessScenarioEnum, task.EmailCron);
}

View File

@ -318,12 +318,11 @@
<param name="_noneDicomStudyFileRepository"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadVisitCheckExcel(System.Guid,System.Boolean,IRaCIS.Core.Application.Helper.IOSSService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.InspectionFile})">
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadVisitCheckExcel(System.Guid,IRaCIS.Core.Application.Helper.IOSSService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.InspectionFile})">
<summary>
一致性核查 excel上传 支持三种格式
</summary>
<param name="trialId"></param>
<param name="isFullCheck"></param>
<param name="oSSService"></param>
<param name="_inspectionFileRepository"></param>
<returns></returns>

View File

@ -1,7 +1,6 @@
using IRaCIS.Core.API;
using IRaCIS.Core.API.HostService;
using IRaCIS.Core.Application.BusinessFilter;
using IRaCIS.Core.Application.BusinessFilter.LegacyController.Database.Api;
using IRaCIS.Core.Application.Filter;
using IRaCIS.Core.Application.MassTransit.Consumer;
using IRaCIS.Core.Application.Service;
@ -22,7 +21,6 @@ using Newtonsoft.Json;
using Serilog;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
@ -30,10 +28,6 @@ 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()
@ -101,18 +95,11 @@ 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>();
options.Filters.Add<LimitUserRequestAuthorization>();
options.Filters.Add<TrialGlobalLimitActionFilter>();
options.Filters.Add<RequestDuplicationFilter>();
})
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理

View File

@ -4,7 +4,7 @@
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://0.0.0.0:3305",
"applicationUrl": "http://localhost:3305",
"sslPort": 0
}
},
@ -23,7 +23,7 @@
"ASPNETCORE_ENVIRONMENT": "Test_IRC",
"ASPNETCORE_OpenSwagger": "true"
},
"applicationUrl": "http://0.0.0.0:6100"
"applicationUrl": "http://localhost:6100"
},
"IRaCIS.Test_IRC_PGSQL": {
"commandName": "Project",
@ -31,7 +31,7 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Test_IRC_PGSQL"
},
"applicationUrl": "http://0.0.0.0:6100"
"applicationUrl": "http://localhost:6100"
},
"IRaCIS.Event_IRC": {
"commandName": "Project",
@ -39,7 +39,7 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Event_IRC"
},
"applicationUrl": "http://0.0.0.0:6100"
"applicationUrl": "http://localhost:6100"
},
"Docker": {
"commandName": "Docker",
@ -53,7 +53,7 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Uat_IRC"
},
"applicationUrl": "http://0.0.0.0:6100"
"applicationUrl": "http://localhost:6100"
},
"IRaCIS.Prod_IRC": {
"commandName": "Project",
@ -61,7 +61,7 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Prod_IRC"
},
"applicationUrl": "http://0.0.0.0:6100"
"applicationUrl": "http://localhost:6100"
},
"IRaCIS.US_Uat_IRC": {
"commandName": "Project",
@ -69,7 +69,7 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "US_Uat_IRC"
},
"applicationUrl": "http://0.0.0.0:6100"
"applicationUrl": "http://localhost:6100"
},
"IRaCIS.US_Prod_IRC": {
"commandName": "Project",
@ -77,7 +77,7 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "US_Prod_IRC"
},
"applicationUrl": "http://0.0.0.0:6100"
"applicationUrl": "http://localhost:6100"
}
}

View File

@ -1,3 +1,2 @@
{
}

View File

@ -39,8 +39,6 @@ namespace IRaCIS.Core.API._PipelineExtensions.Serilog
var requestBodyPayload = await ReadRequestBody(context.Request);
context.Items["RequestBody"] = requestBodyPayload;
using (LogContext.PushProperty("RequestBody", requestBodyPayload))
{
//await _next.Invoke()

View File

@ -45,7 +45,7 @@ namespace IRaCIS.Core.API
}
else
{
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure()/*.CommandTimeout(60)*/);
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
}

View File

@ -71,27 +71,29 @@ namespace IRaCIS.Core.API
DateTime dateTime;
// 2. 检查目标类型是否可空
bool isNullable = objectType == typeof(DateTime?);
var canConvert = DateTime.TryParse(reader.Value?.ToString(), out dateTime);
if (isNullable == false && canConvert == false)
if (reader.ValueType == typeof(DateTime) || reader.ValueType == typeof(DateTime?))
{
DateTime? nullableDateTime = reader.Value as DateTime?;
throw new JsonSerializationException($"Could not convert string to DateTime: {reader.Value} Path {reader.Path}");
if (nullableDateTime != null && nullableDateTime.HasValue)
{
dateTime = nullableDateTime.Value;
}
else
{
return null;
}
}
else
{
if (canConvert == false)
if (DateTime.TryParse((string)reader.Value, out dateTime) == false)
{
return null;
}
}
////如果前端传递带时区的那么转换会报错需要DateTimeKind.Unspecified
//dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
// 将客户端时间转换为服务器时区的时间
var serverZoneTime = TimeZoneInfo.ConvertTime(dateTime, _clientTimeZone, TimeZoneInfo.Local);
@ -111,10 +113,7 @@ namespace IRaCIS.Core.API
//第一个参数默认使用系统本地时区 也就是应用服务器的时区
DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone);
//// 最简单的方式:创建 DateTimeOffset
//DateTimeOffset dateTimeOffset = new DateTimeOffset(clientZoneTime, _clientTimeZone.GetUtcOffset(clientZoneTime));
//writer.WriteValue(dateTimeOffset.ToString("yyyy-MM-dd HH:mm:sszzz"));
//writer.WriteValue(clientZoneTime);
writer.WriteValue(clientZoneTime.ToString(_dateFormat));
}

View File

@ -3,10 +3,8 @@ 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
{
@ -53,90 +51,34 @@ namespace IRaCIS.Core.API
}
public class NullToEmptyStringValueProvider : IValueProvider
{
PropertyInfo _memberInfo;
private readonly bool _isNullable;
PropertyInfo _MemberInfo;
public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
{
_memberInfo = memberInfo;
_MemberInfo = memberInfo;
}
// DTO → JSON 返回前端的时候 处理null 变为"" 方便前端判断
public object GetValue(object target)
{
var result = _memberInfo.GetValue(target);
// 检查类型是否为string或string?
if (_memberInfo.PropertyType == typeof(string) && result == null)
{
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;
#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)
{
_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
if (_MemberInfo.PropertyType == typeof(string))
{
//去掉前后空格
_MemberInfo.SetValue(target, value == null ? string.Empty : value.ToString() == string.Empty ? value : value/*.ToString().Trim()*/);
}
else
{
_MemberInfo.SetValue(target, value);
}
}
}

View File

@ -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:365);
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,retainedFileCountLimit:60);
#region 根据环境配置是否打开错误发送邮件通知

View File

@ -28,13 +28,12 @@ public static class ServiceCollectionSetup
services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
services.AddOptions().Configure<IRCEncreptOption>(_configuration.GetSection("EncrypteResponseConfig"));
services.AddOptions().Configure<RequestDuplicationOptions>(_configuration.GetSection("RequestDuplicationOptions"));
services.AddOptions().Configure<SystemPacsConfig>(_configuration.GetSection("SystemPacsConfig"));
services.Configure<IRaCISBasicConfigOption>(_configuration.GetSection("IRaCISBasicConfig"));
services.Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
//ת<EFBFBD><EFBFBD>ͷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><>ȡ<EFBFBD><C8A1>ʵIP
//转发头设置 获取真实IP
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
@ -52,7 +51,7 @@ public static class ServiceCollectionSetup
services.AddScoped<IObtainTaskAutoCancelJob, ObtainTaskAutoCancelJob>();
// ע<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Service <20><>β<EFBFBD>ķ<EFBFBD><C4B7><EFBFBD>
// 注册以Service 结尾的服务
services.Scan(scan => scan
.FromAssemblies(typeof(BaseService).Assembly)
.AddClasses(classes => classes.Where(t => t.Name.Contains("Service")))
@ -71,23 +70,23 @@ public static class ServiceCollectionSetup
#endregion
// MediatR <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ <20>¼<EFBFBD><C2BC><EFBFBD><EFBFBD><EFBFBD> <20>ӳ<EFBFBD><D3B3><EFBFBD><EFBFBD><EFBFBD> ע<><D7A2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>handler<65><72>Ӧ<EFBFBD><D3A6>ϵ
// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
//builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
#region <EFBFBD><EFBFBD>ʷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
#region 历史废弃配置
//builder.Services.AddMemoryCache();
////<EFBFBD>ϴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
////上传限制 配置
//builder.Services.Configure<FormOptions>(options =>
//{
// options.MultipartBodyLengthLimit = int.MaxValue;
// options.ValueCountLimit = int.MaxValue;
// options.ValueLengthLimit = int.MaxValue;
//});
//IP <EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ð<EFBFBD><C3B0><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>ߺ<EFBFBD><DFBA><EFBFBD><EFBFBD><EFBFBD>
//IP 限流 可设置白名单 或者黑名单
//services.AddIpPolicyRateLimitSetup(_configuration);
// <EFBFBD>û<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ
// 用户类型 策略授权
//services.AddAuthorizationPolicySetup(_configuration);
#endregion
}
@ -95,25 +94,25 @@ public static class ServiceCollectionSetup
}
#region Autofac <EFBFBD><EFBFBD><EFBFBD><EFBFBD>
#region Autofac 废弃
//public class AutofacModuleSetup : Autofac.Module
//{
// protected override void Load(ContainerBuilder containerBuilder)
// {
// #region byzhouhang 20210917 <EFBFBD>˴<EFBFBD>ע<EFBFBD><EFBFBD>Ͳִ<EFBFBD> <20><><EFBFBD>Լ<EFBFBD><D4BC><EFBFBD>Domain<69><6E> <20><>Infra.EFcore <20><><EFBFBD><EFBFBD> <20>յIJִ<C4B2><D6B4>ӿڶ<D3BF><DAB6><EFBFBD><EFBFBD> <20>ִ<EFBFBD><D6B4>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD>
// #region byzhouhang 20210917 此处注册泛型仓储 可以减少Domain层 和Infra.EFcore 两层 空的仓储接口定义和 仓储文件定义
// containerBuilder.RegisterGeneric(typeof(Repository<>))
// .As(typeof(IRepository<>)).InstancePerLifetimeScope();//ע<EFBFBD><EFBFBD>Ͳִ<EFBFBD>
// .As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储
// containerBuilder.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
// #endregion
// #region ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҳ<EFBFBD><EFBFBD>autofac <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʵ<EFBFBD><CAB5><EFBFBD><EFBFBD>ȡ https://www.cnblogs.com/xwhqwer/p/15320838.html
// #region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html
// //<EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD><EFBFBD>п<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ͳ<EFBFBD>ʹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ע<EFBFBD><EFBFBD>
// //获取所有控制器类型并使用属性注入
// containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
// .Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
// .PropertiesAutowired();
@ -129,7 +128,7 @@ public static class ServiceCollectionSetup
// //containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
// //ע<EFBFBD><EFBFBD>hangfire<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>ע<EFBFBD><D7A2>
// //注册hangfire任务 依赖注入
// containerBuilder.RegisterType<ObtainTaskAutoCancelJob>().As<IObtainTaskAutoCancelJob>().InstancePerDependency();
// }

View File

@ -35,8 +35,7 @@
},
"BasicSystemConfig": {
//
"QCRiskControl": true,
"OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true,
@ -56,15 +55,11 @@
"ChangePassWordDays": 90,
// 1 Elevate 2 Extensive
"TemplateType": 2,
"UserMFAVerifyMinutes": 1440
"TemplateType": 2
},
"SystemEmailSendConfig": {
"Port": 465,
"Host": "smtp.qiye.aliyun.com",
"Imap": "imap.qiye.aliyun.com",
"ImapPort": 993,
"FromEmail": "uat@extimaging.com",
"FromName": "UAT_IRC",
"AuthorizationCode": "SHzyyl2021",
@ -74,13 +69,7 @@
"CompanyShortName": "Extensive Imaging",
"CompanyShortNameCN": "展影医疗",
"IsEnv_US": false
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
}
}

View File

@ -12,12 +12,6 @@
//"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": {
@ -42,8 +36,7 @@
}
},
"BasicSystemConfig": {
//
"QCRiskControl": true,
"OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true,
@ -60,26 +53,19 @@
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90,
// 1 Elevate 2 Extensive
"TemplateType": 2,
//MFA
"UserMFAVerifyMinutes": 1440
"TemplateType": 2
},
"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": "ExtImaging",
"OrganizationNameCN": "ExtImaging",
"OrganizationName": "Extlmaging",
"OrganizationNameCN": "Extlmaging",
"CompanyName": "Extensive Imaging",
"CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Extensive Imaging",
@ -87,18 +73,11 @@
"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": {
"Port": "11113",
"IP": "101.132.193.237"
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
}
}

View File

@ -18,12 +18,6 @@
// 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": {
// 使
@ -94,8 +88,6 @@
},
//
"BasicSystemConfig": {
//
"QCRiskControl": true,
//
"OpenUserComplexPassword": false,
//
@ -118,14 +110,12 @@
"IsNeedChangePassWord": true,
//
"ChangePassWordDays": 90,
// 1 ElevateLiLi 2 Extensive()
// 1 Elevate 2 Extensive
"TemplateType": 2,
//
"OpenTrialRelationDelete": true,
// PDF
"ThirdPdfUrl": "http://106.14.89.110:30088/api/v1/convert/file/pdf",
//MFA
"UserMFAVerifyMinutes": 1440
"ThirdPdfUrl": "http://106.14.89.110:30088/api/v1/convert/file/pdf"
},
//
"SystemEmailSendConfig": {
@ -133,10 +123,6 @@
"Port": 465,
// SMTP
"Host": "smtp.qiye.aliyun.com",
"Imap": "imap.qiye.aliyun.com",
"ImapPort": 993,
//
"FromEmail": "test@extimaging.com",
//
@ -145,15 +131,12 @@
"AuthorizationCode": "SHzyyl2021",
// 访
"SiteUrl": "http://irc.test.extimaging.com/login",
"PlatformName": "EICS",
"PlatformNameCN": "展影云平台",
// 使
//
"SystemShortName": "IRC",
//
"OrganizationName": "ExtImaging",
"OrganizationName": "Extlmaging",
//
"OrganizationNameCN": "ExtImaging",
"OrganizationNameCN": "Extlmaging",
//
"CompanyName": "Extensive Imaging",
//
@ -168,7 +151,6 @@
"IsOpenErrorNoticeEmail": false,
//
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
"CronEmailDefaultCulture": "zh-CN",
//
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
},
@ -178,16 +160,5 @@
"Port": "11113",
// PACSIP
"IP": "106.14.89.110"
},
//
"RequestDuplicationOptions": {
//
"IsEnabled": true,
//
"DuplicationWindowMs": 200,
//
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
}
}

View File

@ -47,8 +47,7 @@
},
"BasicSystemConfig": {
//
"QCRiskControl": true,
"OpenUserComplexPassword": false,
"OpenSignDocumentBeforeWork": false,
@ -76,15 +75,13 @@
"SystemEmailSendConfig": {
"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": "ExtImaging",
"OrganizationNameCN": "ExtImaging",
"OrganizationName": "Extlmaging",
"OrganizationNameCN": "Extlmaging",
"CompanyName": "Extensive Imaging",
"CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Extensive Imaging",
@ -94,12 +91,5 @@
"SystemPacsConfig": {
"Port": "11113",
"IP": "106.14.89.110"
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
}
}

View File

@ -12,12 +12,7 @@
//"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": {
@ -43,8 +38,7 @@
}
},
"BasicSystemConfig": {
//
"QCRiskControl": true,
"OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true,
@ -63,24 +57,17 @@
// 1 Elevate 2 Extensive
"TemplateType": 1,
"OpenTrialRelationDelete": false,
//MFA
"UserMFAVerifyMinutes": 1440
"OpenTrialRelationDelete": false
},
"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",
@ -92,20 +79,12 @@
"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" ]
},
"SystemPacsConfig": {
"Port": "104",
"IP": "44.210.231.169"
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
}
}

View File

@ -44,8 +44,7 @@
}
},
"BasicSystemConfig": {
//
"QCRiskControl": true,
"OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true,
@ -67,24 +66,16 @@
"ChangePassWordDays": 90,
// 1 Elevate 2 Extensive
"TemplateType": 1,
"OpenLoginMFA": true,
//MFA
"UserMFAVerifyMinutes": 1440
"OpenLoginMFA": true
},
"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",
@ -101,13 +92,6 @@
"SystemPacsConfig": {
"Port": "104",
"IP": "3.226.182.187"
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
}
}

View File

@ -13,12 +13,6 @@
"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": {
@ -54,8 +48,7 @@
}
},
"BasicSystemConfig": {
//
"QCRiskControl": true,
"OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true,
@ -72,22 +65,16 @@
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90,
// 1 Elevate 2 Extensive
"TemplateType": 1,
//MFA
"UserMFAVerifyMinutes": 1440
"TemplateType": 1
},
"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",
@ -99,19 +86,12 @@
"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" ]
},
"SystemPacsConfig": {
"Port": "104",
"IP": "3.226.182.187"
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
}
}

View File

@ -10,12 +10,6 @@
"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",
@ -60,8 +54,7 @@
},
"BasicSystemConfig": {
//
"QCRiskControl": true,
"OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true,
@ -79,26 +72,20 @@
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90,
// 1 Elevate 2 Extensive
"TemplateType": 2,
//MFA
"UserMFAVerifyMinutes": 1440
"TemplateType": 2
},
"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": "ExtImaging",
"OrganizationNameCN": "ExtImaging",
"OrganizationName": "Extlmaging",
"OrganizationNameCN": "Extlmaging",
"CompanyName": "Extensive Imaging",
"CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Extensive Imaging",
@ -106,20 +93,12 @@
"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": {
"Port": "11113",
"IP": "101.132.253.119"
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
}
}

View File

@ -3,7 +3,7 @@
"SecurityKey": "ShangHaiZhanYing_SecurityKey_SHzyyl@2021",
"Issuer": "Extimaging",
"Audience": "EICS",
"TokenExpireMinute": "10080" //7
"TokenExpireMinute": "10080"//7
},
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
@ -74,12 +74,5 @@
"redirect_uri": "https://oauthlogin.net/oauth/githubcallback",
"scope": "repo"
}
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
}
}

View File

@ -4,6 +4,7 @@
<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" />

View File

@ -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', 'user1wj');
createInput(modalUxContent, 'password', 'Password', 'password', '1');
createInput(modalUxContent, 'userName', 'Username or email address', 'text', 'cyldev');
createInput(modalUxContent, 'password', 'Password', 'password', '123456');
createInput(modalUxContent, 'pwdMd5', 'PwdMd5', 'text', '');
createSelect(modalUxContent, 'roleSelect', 'role', [])

View File

@ -1,22 +1,14 @@
using IRaCIS.Core.Application.Helper.OtherTool;
using Microsoft.AspNetCore.Mvc;
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, IConfiguration _config, IUserInfo _userInfo) : ActionFilterAttribute, IActionFilter
public class ModelActionFilter(IStringLocalizer _localizer) : ActionFilterAttribute, IActionFilter
{
@ -31,55 +23,8 @@ public class ModelActionFilter(IStringLocalizer _localizer, IConfiguration _conf
.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)));
}
}
}

View File

@ -1,16 +1,13 @@
using IRaCIS.Core.Application.Helper.OtherTool;
using IRaCIS.Core.Domain.Share;
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, IConfiguration _config, IUserInfo _userInfo) : Attribute, IExceptionFilter
public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, IStringLocalizer _localizer) : Attribute, IExceptionFilter
{
public void OnException(ExceptionContext context)
@ -49,44 +46,7 @@ public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, ISt
else
{
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (exception.InnerException is null ? (exception.Message)
: (exception.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}");
}
: (exception.InnerException?.Message )), ApiResponseCodeEnum.ProgramException));
}
@ -98,7 +58,6 @@ public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, ISt
_logger.LogError(errorInfo);
//_logger.LogError(exception, exception.Message);
}
else

View File

@ -1,137 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.BusinessFilter.LegacyController
{
using Database.Api;
using DocumentFormat.OpenXml.InkML;
using IRaCIS.Core.Application.Service.Common;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Components.Endpoints;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Minio.Helper;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace Database.Api
{
/// <summary>
/// 请求拦截 请求前后的操作
/// </summary>
/// <param name="logger">logger</param>
/// <param name="accessor">loggerHelper</param>
public class RequestDuplicationFilter(ILogger<RequestDuplicationFilter> logger, IHttpContextAccessor accessor, IUserInfo _userInfo, IStringLocalizer _localizer, IOptionsMonitor<RequestDuplicationOptions> RequestDuplicationOptionsMonitor) : ExceptionFilterAttribute, IAsyncActionFilter
{
/// <summary>
/// 传入的参数
/// </summary>
public string Intoparam { get; set; }
/// <summary>
/// 这个是正常记录(请求刚进入的时候)
/// </summary>
/// <param name="context">context</param>
/// <param name="next">next</param>
/// <returns>返回的对象</returns>
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (RequestDuplicationOptionsMonitor.CurrentValue.IsEnabled)
{
Dictionary<string, object?> dic = new Dictionary<string, object?>();
var desc = context.ActionDescriptor as ControllerActionDescriptor;
foreach (var p in desc.Parameters)
{
// 关键判断:绑定源是否是 Services
if (p.BindingInfo?.BindingSource == BindingSource.Services) continue;
// 普通参数,取值
if (context.ActionArguments.TryGetValue(p.Name, out var value) && value != null)
{
dic.Add(p.Name, value);
}
}
this.Intoparam = JsonConvert.SerializeObject(dic);
try
{
this.RequestDuplication();
}
catch (Exception)
{
}
}
var resultContext = await next();
}
/// <summary>
///
/// </summary>
private void RequestDuplication()
{
var requestPath = accessor?.HttpContext?.Request?.Path.ToString() ?? string.Empty;
// 验证请求频繁情况
if (
!requestPath
.Split("/", StringSplitOptions.RemoveEmptyEntries)
.Any(segment => segment.StartsWith("get", StringComparison.OrdinalIgnoreCase)) &&
_userInfo.UserRoleId != default(Guid)&&
RequestDuplicationOptionsMonitor.CurrentValue.IsEnabled &&
!RequestDuplicationOptionsMonitor.CurrentValue.ExcludePaths.Contains(requestPath))
{
RequestInfo requestInfo = new RequestInfo
{
UserRoleId = _userInfo.UserRoleId,
RequestPath = requestPath,
ParameterHash = GenerateParameterHash(this.Intoparam),
RequestTime = DateTime.Now
};
IRCSystemInfo.RequestRecordList= IRCSystemInfo.RequestRecordList.Where(x => x.RequestTime >= DateTime.Now.AddSeconds(-RequestDuplicationOptionsMonitor.CurrentValue.CacheTimeSeconds)).ToList();
var requestsTimes = IRCSystemInfo.RequestRecordList.Any(x=>
x.RequestTime>= requestInfo.RequestTime.AddMilliseconds(-RequestDuplicationOptionsMonitor.CurrentValue.DuplicationWindowMs)&&
x.RequestKey== requestInfo.RequestKey);
if (requestsTimes)
{
throw new BusinessValidationFailedException(_localizer["RequestDuplicationFilter_RequestDuplication"], ApiResponseCodeEnum.BusinessValidationFailed);
}
IRCSystemInfo.RequestRecordList.Add(requestInfo);
}
}
public string GenerateParameterHash(string parameters)
{
if (string.IsNullOrEmpty(parameters))
return string.Empty;
using var sha256 = SHA256.Create();
var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(parameters));
return Convert.ToBase64String(hashBytes);
}
}
}
}

View File

@ -7,7 +7,6 @@ namespace IRaCIS.Core.Domain.Share;
[Description("多环境 配置环境实体")]
public class ServiceVerifyConfigOption
{
public bool QCRiskControl { get; set; }
public bool OpenUserComplexPassword { get; set; }
public bool OpenSignDocumentBeforeWork { get; set; }
@ -39,8 +38,6 @@ public class ServiceVerifyConfigOption
public string ThirdPdfUrl { get; set; }
public int UserMFAVerifyMinutes { get; set; } = 1440;
}
public class SystemEmailSendConfig
@ -48,10 +45,6 @@ public class SystemEmailSendConfig
public int Port { get; set; }
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;
@ -60,11 +53,7 @@ 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 SystemShortName { get; set; } = string.Empty;
public string OrganizationName { get; set; } = string.Empty;
public string OrganizationNameCN { get; set; } = string.Empty;
@ -83,12 +72,12 @@ public class SystemEmailSendConfig
public string EmailRegexStr { get; set; }
public List<string> ErrorNoticeEmailList { get; set; } = new List<string>();
public List<string> ErrorNoticeEmailList { get; set; } =new List<string>();
}
public class SystemEmailSendConfigView
{
public string SystemShortName { get; set; } = string.Empty;
public string SystemShortName { get; set; } = string.Empty;
public string CompanyName { get; set; } = string.Empty;
public string CompanyNameCN { get; set; } = string.Empty;
@ -116,54 +105,6 @@ public class IRCEncreptOption
public List<string> ApiPathList { get; set; }
}
/// <summary>
/// 请求缓存配置
/// </summary>
public class RequestDuplicationOptions
{
/// <summary>
/// 缓存时间默认5秒
/// </summary>
public int CacheTimeSeconds { get; set; } = 5;
/// <summary>
/// 重复请求检测时间窗口毫秒默认500毫秒
/// </summary>
public int DuplicationWindowMs { get; set; } = 500;
/// <summary>
/// 是否启用防重复请求
/// </summary>
public bool IsEnabled { get; set; } = true;
/// <summary>
/// 需要排除的路径(不进行重复检测)
/// </summary>
public List<string> ExcludePaths { get; set; } = new List<string>();
}
public static class IRCSystemInfo
{
public static List<RequestInfo> RequestRecordList { get; set; } = new List<RequestInfo>();
}
public class RequestInfo
{
public Guid UserRoleId { get; set; }
public string RequestPath { get; set; } = string.Empty;
public string ParameterHash { get; set; } = string.Empty;
public DateTime RequestTime { get; set; }
/// <summary>
/// 请求的唯一标识
/// </summary>
public string RequestKey => $"{UserRoleId}_{RequestPath}_{ParameterHash}";
}
public class IRaCISBasicConfigOption
{
public string DoctorCodePrefix { get; set; }

View File

@ -61,11 +61,6 @@ public static class CacheKeys
/// <returns></returns>
public static string StartRestTime(Guid userId) => $"{userId}StartRestTime";
//每个用户 每个浏览器独立时间
public static string UserMFAVerifyPass(Guid userId,string browserFingerprint) => $"UserMFAVerifyPass:{userId}:{browserFingerprint}";
public static string UserMFATag(Guid userId) => $"UserMFAVerifyPass:{userId}";
}
public static class CacheHelper

View File

@ -1,5 +1,4 @@
using DocumentFormat.OpenXml.Office.CustomUI;
using FellowOakDicom;
using FellowOakDicom;
using FellowOakDicom.Media;
using System;
using System.Collections.Generic;
@ -14,9 +13,6 @@ 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; }
@ -58,7 +54,6 @@ namespace IRaCIS.Core.Application.Helper
var mappings = new List<string>();
int index = 1;
var studyUid=list.FirstOrDefault()?.StudyInstanceUid;
var dicomDir = new DicomDirectory();
@ -102,11 +97,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(), filename.TrimEnd('/', '\\').Split('/', '\\').Last());
dic.Add(item.InstanceId.ToString(), Path.GetFileName(filename));
dicomDir.AddFile(dicomFile, filename);
@ -130,9 +125,7 @@ namespace IRaCIS.Core.Application.Helper
// 重置流位置
memoryStream.Position = 0;
var relativePath= await _oSSService.UploadToOSSAsync(memoryStream, ossFolder, "DICOMDIR", true);
dic.Add($"{studyUid}_DICOMDIR" , relativePath.Split('/').Last());
await _oSSService.UploadToOSSAsync(memoryStream, ossFolder, "DICOMDIR", false);
}
//清理临时文件

View File

@ -2,7 +2,6 @@
using MailKit;
using MailKit.Security;
using MimeKit;
using Org.BouncyCastle.Tls;
namespace IRaCIS.Core.Application.Helper;
@ -10,15 +9,12 @@ namespace IRaCIS.Core.Application.Helper;
public static class SendEmailHelper
{
public static async Task<string> SendEmailAsync(MimeMessage messageToSend, SystemEmailSendConfig _systemEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
public static async Task SendEmailAsync(MimeMessage messageToSend, SystemEmailSendConfig _systemEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
{
string result = string.Empty;
result = messageToSend.MessageId;
//没有收件人 那么不发送
if (messageToSend.To.Count == 0)
{
return string.Empty;
return;
}
try
@ -46,7 +42,6 @@ public static class SendEmailHelper
await smtp.DisconnectAsync(true);
}
}
catch (Exception ex)
@ -56,76 +51,7 @@ public static class SendEmailHelper
throw new Exception(I18n.T("SendEmail_SendFail"), new Exception(ex.Message));
}
return result;
}
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(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)
@ -147,7 +73,7 @@ public static class SendEmailHelper
return true;
}
public static async Task SendEmailAsync(SMTPEmailConfig sMTPEmailConfig,Trial? trial, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
public static async Task SendEmailAsync(SMTPEmailConfig sMTPEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
{
var messageToSend = new MimeMessage();
@ -217,7 +143,7 @@ public static class SendEmailHelper
await smtp.ConnectAsync(sMTPEmailConfig.Host, sMTPEmailConfig.Port, SecureSocketOptions.Auto);
await smtp.AuthenticateAsync(trial.EmailFromEmail, trial.EmailAuthorizationCode);
await smtp.AuthenticateAsync(sMTPEmailConfig.UserName, sMTPEmailConfig.AuthorizationCode);
await smtp.SendAsync(messageToSend);

View File

@ -60,16 +60,4 @@ 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;
}
}
}

View File

@ -1,24 +1,19 @@
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;
@ -686,61 +681,13 @@ public static class ExcelExportHelper
if (itemDicName.IsNotNullOrEmpty())
{
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;
}
var 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
{
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);
}
row.GetCell(writeIndex).SetCellValue(itemValue);
}
@ -871,16 +818,17 @@ public static class ExcelExportHelper
//模板路径
var tplPath = physicalPath;
// 获取文件流
var templateStream = new MemoryStream();
#region 根据中英文 删除模板sheet
// 打开模板文件
var templateFileStream = new FileStream(tplPath, FileMode.Open, FileAccess.Read);
var templateFile = new FileStream(tplPath, FileMode.Open, FileAccess.Read);
var workbook = new XSSFWorkbook(templateFileStream);
// 获取文件流
var templateStream = new MemoryStream();
templateFile.CopyTo(templateStream);
templateStream.Seek(0, SeekOrigin.Begin);
var workbook = new XSSFWorkbook(templateStream);
int sheetCount = workbook.NumberOfSheets;
@ -985,8 +933,14 @@ public static class ExcelExportHelper
}
}
workbook.Write(templateStream, leaveOpen: true);
templateStream.Position = 0;
using (var memoryStream2 = new MemoryStream())
{
workbook.Write(memoryStream2, true);
memoryStream2.Seek(0, SeekOrigin.Begin);
templateStream = memoryStream2;
}
}
@ -1084,91 +1038,6 @@ public static class ExcelExportHelper
public static async Task<IActionResult> MutiSheetDataExportAsync(string code, object data, string exportFileNamePrefix, IRepository<CommonDocument> _commonDocumentRepository, IWebHostEnvironment _hostEnvironment)
{
var (physicalPath, fileName) = await FileStoreHelper.GetCommonDocPhysicalFilePathAsync(_hostEnvironment, _commonDocumentRepository, code);
//模板路径
var tplPath = physicalPath;
var templateStream = new MemoryStream();
#region npoi 移除某一行
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
if (isEn_US)
{
// 打开模板文件
using var templateFileStream = new FileStream(tplPath, FileMode.Open, FileAccess.Read);
using var workbook = new XSSFWorkbook(templateFileStream);
int sheetCount = workbook.NumberOfSheets;
int removeRowIndex = 1; // 要删除的行0-based
for (int i = 0; i < sheetCount; i++)
{
var sheet = workbook.GetSheetAt(i);
// 2 删除行
var row = sheet.GetRow(removeRowIndex);
if (row != null)
{
sheet.RemoveRow(row);
}
// 3 上移后续行
if (removeRowIndex < sheet.LastRowNum)
{
sheet.ShiftRows(
removeRowIndex + 1,
sheet.LastRowNum,
-1,
true, // copyRowHeight
false // resetOriginalRowHeight
);
}
}
workbook.Write(templateStream, leaveOpen: true);
templateStream.Position = 0;
}
else
{
using (var fs = new FileStream(tplPath, FileMode.Open, FileAccess.Read))
{
fs.CopyTo(templateStream);
}
templateStream.Position = 0;
}
#endregion
var memoryStream = new MemoryStream();
var config = new OpenXmlConfiguration()
{
IgnoreTemplateParameterMissing = true,
};
await MiniExcel.SaveAsByTemplateAsync(memoryStream, templateStream.ToArray(), data, config);
memoryStream.Seek(0, SeekOrigin.Begin);
return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
FileDownloadName = $"{(string.IsNullOrEmpty(exportFileNamePrefix) ? "" : exportFileNamePrefix + "_")}{Path.GetFileNameWithoutExtension(fileName)}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx"
};
}
/// <summary>
/// 导出文件模板
/// </summary>
@ -1250,7 +1119,4 @@ public static class ExcelExportHelper
};
}
}

View File

@ -2,7 +2,6 @@
using IRaCIS.Core.Application.MassTransit.Consumer;
using IRaCIS.Core.Domain.Share;
using MassTransit.Mediator;
using System.Globalization;
namespace IRaCIS.Core.Application.Helper
{
@ -58,29 +57,25 @@ namespace IRaCIS.Core.Application.Helper
}
public static void AddOrUpdateTrialCronJob(string jobId, Guid trialId, EmailBusinessScenario businessScenario, string emailCron,string defaultCulture="")
public static void AddOrUpdateTrialCronJob(string jobId, Guid trialId, EmailBusinessScenario businessScenario, string emailCron)
{
if (defaultCulture == "")
{
defaultCulture = CultureInfo.CurrentCulture.Name;
}
switch (businessScenario)
{
case EmailBusinessScenario.QCTask:
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new ImageQCRecurringEvent() { TrialId = trialId,CultureInfoName= defaultCulture }, default), emailCron);
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new ImageQCRecurringEvent() { TrialId = trialId }, default), emailCron);
break;
case EmailBusinessScenario.CRCToQCQuestion:
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new CRCImageQuestionRecurringEvent() { TrialId = trialId, CultureInfoName = defaultCulture }, default), emailCron);
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new CRCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
break;
case EmailBusinessScenario.QCToCRCImageQuestion:
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId, CultureInfoName = defaultCulture }, default), emailCron);
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
break;
//加急阅片 10分钟
case EmailBusinessScenario.ExpeditedReading:
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new UrgentIRUnReadTaskRecurringEvent() { TrialId = trialId, CultureInfoName = defaultCulture }, default), emailCron);
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new UrgentIRUnReadTaskRecurringEvent() { TrialId = trialId }, default), emailCron);
break;
default:
break;
@ -88,25 +83,20 @@ namespace IRaCIS.Core.Application.Helper
}
public static void AddOrUpdateTimingCronJob(string jobId, EmailBusinessScenario businessScenario, string emailCron, string defaultCulture="")
public static void AddOrUpdateTimingCronJob(string jobId, EmailBusinessScenario businessScenario, string emailCron)
{
if (defaultCulture == "")
{
defaultCulture = CultureInfo.CurrentCulture.Name;
}
switch (businessScenario)
{
case EmailBusinessScenario.GeneralTraining_ExpirationNotification:
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new SystemDocumentErverDayEvent() { CultureInfoName = defaultCulture }, default), emailCron);
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new SystemDocumentErverDayEvent() { }, default), emailCron);
break;
case EmailBusinessScenario.TrialTraining_ExpirationNotification:
Console.WriteLine("更新项目到期job");
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new TrialDocumentErverDayEvent() { CultureInfoName = defaultCulture }, default), emailCron);
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new TrialDocumentErverDayEvent() { }, default), emailCron);
break;
default:

View File

@ -16,7 +16,6 @@ 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;
@ -143,12 +142,8 @@ 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);
public Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true);
public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath);
@ -160,7 +155,7 @@ public interface IOSSService
public Task DeleteFromPrefix(string prefix, bool isCache = false);
public Task DeleteObjects(List<string> objectKeys, bool isCache = false);
public Task DeleteObjects(List<string> objectKeys);
List<string> GetRootFolderNames();
@ -187,518 +182,7 @@ 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
@ -765,8 +249,8 @@ public class OSSService : IOSSService
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
//,UseHttp = true,
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
@ -838,10 +322,9 @@ public class OSSService : IOSSService
/// <param name="localFilePath"></param>
/// <param name="oosFolderPath"></param>
/// <param name="isFileNameAddGuid"></param>
/// <param name="randomFileName">随机文件名</param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true, bool randomFileName = false)
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true)
{
BackBatchGetToken();
@ -849,11 +332,6 @@ public class OSSService : IOSSService
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}";
if (randomFileName)
{
var fileExtension = localFileName.Split(".").LastOrDefault();
ossRelativePath = $"{oosFolderPath}/{Guid.NewGuid()}.{fileExtension}";
}
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
@ -891,8 +369,8 @@ public class OSSService : IOSSService
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
//,UseHttp = true,
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
@ -966,8 +444,8 @@ public class OSSService : IOSSService
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
//,UseHttp = true,
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
@ -1020,14 +498,11 @@ public class OSSService : IOSSService
var result = _ossClient.GetObject(aliConfig.BucketName, ossRelativePath);
// 直接返回流
return result.Content;
//// 将OSS返回的流复制到内存流中并返回
//var memoryStream = new MemoryStream();
//await result.Content.CopyToAsync(memoryStream);
//memoryStream.Position = 0; // 重置位置以便读取
//return memoryStream;
// 将OSS返回的流复制到内存流中并返回
var memoryStream = new MemoryStream();
await result.Content.CopyToAsync(memoryStream);
memoryStream.Position = 0; // 重置位置以便读取
return memoryStream;
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
@ -1039,30 +514,16 @@ public class OSSService : IOSSService
.WithSSL(minIOConfig.UseSSL)
.Build();
var pipe = new System.IO.Pipelines.Pipe();
var memoryStream = new MemoryStream();
_ = Task.Run(async () =>
{
try
{
var args = new GetObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(ossRelativePath)
.WithCallbackStream(stream =>
{
stream.CopyTo(pipe.Writer.AsStream());
});
var getObjectArgs = new GetObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(ossRelativePath)
.WithCallbackStream(stream => stream.CopyToAsync(memoryStream));
await minioClient.GetObjectAsync(args);
await pipe.Writer.CompleteAsync();
}
catch (Exception ex)
{
await pipe.Writer.CompleteAsync(ex);
}
});
return pipe.Reader.AsStream();
await minioClient.GetObjectAsync(getObjectArgs);
memoryStream.Position = 0;
return memoryStream;
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
@ -1090,13 +551,10 @@ public class OSSService : IOSSService
var response = await amazonS3Client.GetObjectAsync(getObjectRequest);
// ⭐ 直接返回流
return response.ResponseStream;
//var memoryStream = new MemoryStream();
//await response.ResponseStream.CopyToAsync(memoryStream);
//memoryStream.Position = 0;
//return memoryStream;
var memoryStream = new MemoryStream();
await response.ResponseStream.CopyToAsync(memoryStream);
memoryStream.Position = 0;
return memoryStream;
}
else
{
@ -1172,8 +630,8 @@ public class OSSService : IOSSService
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
//,UseHttp = true,
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
@ -1434,7 +892,7 @@ public class OSSService : IOSSService
}
/// <summary>
/// 删除某个目录的文件 (包含单个文件oss单个文件需要去除前缀/)
/// 删除某个目录的文件
/// </summary>
/// <param name="prefix"></param>
/// <returns></returns>
@ -1546,8 +1004,8 @@ public class OSSService : IOSSService
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
//,UseHttp = true,
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
@ -1561,7 +1019,7 @@ public class OSSService : IOSSService
var listObjectsResponse = await amazonS3Client.ListObjectsV2Async(listObjectsRequest);
if (listObjectsResponse.S3Objects?.Count > 0)
if (listObjectsResponse.S3Objects.Count > 0)
{
// 准备删除请求
var deleteObjectsRequest = new Amazon.S3.Model.DeleteObjectsRequest
@ -1591,7 +1049,7 @@ public class OSSService : IOSSService
}
}
public async Task DeleteObjects(List<string> objectKeys, bool isCache = false)
public async Task DeleteObjects(List<string> objectKeys)
{
GetObjectStoreTempToken();
@ -1601,23 +1059,9 @@ 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(bucketName, objectKeys, false));
var result = _ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, objectKeys, false));
}
}
@ -1653,8 +1097,8 @@ public class OSSService : IOSSService
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
//,UseHttp = true,
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
@ -1728,8 +1172,8 @@ public class OSSService : IOSSService
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
//,UseHttp = true,
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var request = new Amazon.S3.Model.GetObjectMetadataRequest
@ -1813,16 +1257,9 @@ 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, stsConfig);
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
// 使用 AssumeRole 请求临时凭证
var assumeRoleRequest = new AssumeRoleRequest

View File

@ -1,98 +0,0 @@
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);
}
}
}

View File

@ -64,6 +64,7 @@
<PackageReference Include="RestSharp" Version="112.1.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.4.0" />
</ItemGroup>
<ItemGroup>
@ -74,8 +75,4 @@
<Folder Include="Service\MinimalApiService\CodeTemplate\FrontTemplate\" />
</ItemGroup>
<ItemGroup>
<None Include="Service\TrialSiteUser\TrialStatService.cs" />
</ItemGroup>
</Project>

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,8 @@
using IRaCIS.Core.Application.Helper;
using MiniExcelLibs.Attributes;
using MiniExcelLibs.Attributes;
using System.ComponentModel.DataAnnotations;
namespace IRaCIS.Core.Application.MassTransit.Command
{
/// <summary>
/// 全量一致性核查
/// </summary>
public record ConsistenFullCheckCommand
{
public List<CheckViewModel> ETCList { get; set; } = new List<CheckViewModel>();
public Guid TrialId { get; set; }
public Guid InspectionFileId { get; set; }
}
public record ConsistenCheckCommand
{
public List<CheckViewModel> ETCList { get; set; } = new List<CheckViewModel>();
@ -30,24 +17,13 @@ namespace IRaCIS.Core.Application.MassTransit.Command
public class CheckDBModel : CheckViewModel
{
public Guid SubjectVisitId { get; set; }
public Guid StudyId { get; set; }
}
public class FullCheckResult: CheckViewModel
{
public string LatestScanDateStr { get; set; } = string.Empty;
public string Modalitys { get; set; } = string.Empty;
public DateTime CheckTime { get; set; }
public string CheckResult { get; set; }
[DictionaryTranslateAttribute("CheckState")]
public CheckStateEnum CheckState { get; set; }
}
//[ExcelImporter(/*ImportResultFilter = typeof(ImportResultFilteTest),*/ IsLabelingError = true)]
@ -86,13 +62,8 @@ namespace IRaCIS.Core.Application.MassTransit.Command
[ExcelColumnName("Modality")]
public string Modality { get; set; } = string.Empty;
#region 全量一致性核查加入
[DictionaryTranslateAttribute("Subject_Visit_Status")]
public SubjectStatus SubjectStatus { get; set; }
#endregion
public override bool Equals(object? obj)
{

View File

@ -1,49 +1,53 @@
using AutoMapper;
using IRaCIS.Application.Contracts;
using IRaCIS.Application.Interfaces;
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Contracts.DTO;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.MassTransit.Command;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using MassTransit;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System.IO;
using System.Text;
namespace IRaCIS.Core.Application.MassTransit.Consumer
{
public class ConsistencyCheckConsumer(
IRepository<DicomStudy> _studyRepository,
IUserInfo _userInfo,
IRepository<Subject> _subjectRepository,
IRepository<NoneDicomStudy> _noneDicomStudyRepository,
IRepository<SubjectVisit> _subjectVisitRepository,
IRepository<Dictionary> _dictionaryRepository,
IOSSService _oSSService,
IMapper _mapper,
IDictionaryService _dictionaryService,
IRepository<CommonDocument> _commonDocumentRepository,
IStringLocalizer _localizer,
IWebHostEnvironment _hostEnvironment,
IRepository<InspectionFile> _inspectionFileRepository,
IOptionsMonitor<SystemEmailSendConfig> _systemEmailSendConfig
) : IConsumer<ConsistenCheckCommand>, IConsumer<ConsistenFullCheckCommand>
public class ConsistencyCheckConsumer : IConsumer<ConsistenCheckCommand>
{
private readonly IRepository<DicomStudy> _studyRepository;
private readonly IUserInfo _userInfo;
private readonly IRepository<Subject> _subjectRepository;
private readonly IRepository<SubjectVisit> _subjectVisitRepository;
private readonly IRepository<TrialSite> _trialSiteRepository;
private readonly IMapper _mapper;
private readonly IRepository<NoneDicomStudy> _noneDicomStudyRepository;
public IStringLocalizer _localizer { get; set; }
private readonly SystemEmailSendConfig _systemEmailConfig;
private readonly SystemEmailSendConfig _systemEmailConfig = _systemEmailSendConfig.CurrentValue;
private readonly IRepository<Dictionary> _dictionaryRepository;
/// <summary>
/// 构造函数注入
/// </summary>
public ConsistencyCheckConsumer(IRepository<DicomStudy> studyRepository, IUserInfo userInfo,
IRepository<Subject> subjectRepository, IRepository<SubjectVisit> subjectVisitRepository,
IRepository<TrialSite> trialSiteRepository, IRepository<NoneDicomStudy> noneDicomStudyRepository,
IMapper mapper, IStringLocalizer localizer, IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig, IRepository<Dictionary> dictionaryRepository)
{
_noneDicomStudyRepository = noneDicomStudyRepository;
_studyRepository = studyRepository;
_userInfo = userInfo;
_subjectRepository = subjectRepository;
_subjectVisitRepository = subjectVisitRepository;
_trialSiteRepository = trialSiteRepository;
_mapper = mapper;
_localizer = localizer;
_systemEmailConfig = systemEmailConfig.CurrentValue;
_dictionaryRepository = dictionaryRepository;
}
@ -63,8 +67,8 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
//subjectVisitLambda2= subjectVisitLambda2.And(x => x.CheckState == CheckStateEnum.ToCheck && x.AuditState == AuditStateEnum.QCPassed || (x.CheckState == CheckStateEnum.CVIng && x.AuditState == AuditStateEnum.QCPassed));
Expression<Func<SubjectVisit, bool>> subjectVisitLambda = x => x.TrialId == trialId && x.Subject.IsSubjectQuit == false && x.AuditState == AuditStateEnum.QCPassed &&
(x.CheckState == CheckStateEnum.ToCheck || x.CheckState == CheckStateEnum.CVIng);
Expression<Func<SubjectVisit, bool>> subjectVisitLambda = x => x.TrialId == trialId && x.Subject.IsSubjectQuit == false &&
(x.CheckState == CheckStateEnum.ToCheck && x.AuditState == AuditStateEnum.QCPassed || (x.CheckState == CheckStateEnum.CVIng && x.AuditState == AuditStateEnum.QCPassed));
var dicomQuery = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id
@ -82,7 +86,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.Where(t => t.FileCount > 0) on sv.Id equals noneDicomStudy.SubjectVisitId
join noneDicomStudy in _noneDicomStudyRepository.AsQueryable() on sv.Id equals noneDicomStudy.SubjectVisitId
select new CheckDBModel()
{
SubjectVisitId = sv.Id,
@ -113,7 +117,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
foreach (var sv in svExcelGroup)
{
//Excel 的数据 在IRC 中可以找到该访视(一致性核查中,或者待核查)
//Excel 的数据 在IRC 中可以找到该访视
if (dbCheckList.Any(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName))
{
@ -197,7 +201,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
var dbExceptExcel = dbVisitStudyList.Except(etcVisitStudyList);
// excel 存在
var excelExceptDB = etcVisitStudyList.Except(dbVisitStudyList);
var excelExceptDB = etcVisitStudyList.Except(dbCheckList);
//ETC 和系统的完全一致 两者没有差别
if (dbExceptExcel.Count() == 0 && excelExceptDB.Count() == 0)
@ -297,169 +301,12 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
}
await _subjectVisitRepository.SaveChangesAsync();
//await context.RespondAsync<ConsistenCheckResult>(new
//{
//});
}
public async Task Consume(ConsumeContext<ConsistenFullCheckCommand> context)
{
var inspectionFileId = context.Message.InspectionFileId;
var trialId = context.Message.TrialId;
//系统定义MRI 到底是MR 还是MRI
var mriModality = await _dictionaryRepository.Where(t => t.Parent.Code == "Modality").Where(t => t.Code == "MRI").Select(t => t.Value).FirstNotNullAsync();
//处理Excel大小写
context.Message.ETCList.ForEach(t => { t.Modality = t.Modality.ToUpper().Trim(); t.StudyDate = Convert.ToDateTime(t.StudyDate).ToString("yyyy-MM-dd"); t.SiteCode = t.SiteCode.ToUpper().Trim(); t.VisitName = t.VisitName.ToUpper().Trim(); t.SubjectCode = t.SubjectCode.ToUpper().Trim(); });
var etcList = context.Message.ETCList;
Expression<Func<SubjectVisit, bool>> subjectVisitLambda = x => x.TrialId == trialId /*&& x.Subject.IsSubjectQuit == false*/ && x.AuditState == AuditStateEnum.QCPassed;
var dicomQuery = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id
join study in _studyRepository.AsQueryable() on sv.Id equals study.SubjectVisitId
select new CheckDBModel()
{
SubjectStatus = sv.Subject.Status,
SubjectVisitId = sv.Id,
SiteCode = sv.TrialSite.TrialSiteCode,
StudyDate = study.StudyTime == null ? string.Empty : ((DateTime)study.StudyTime).ToString("yyyy-MM-dd"),
StudyId = study.Id,
Modality = study.ModalityForEdit,
SubjectCode = subject.Code,
VisitName = sv.VisitName,
};
var noneDicomQuey = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id
join noneDicomStudy in _noneDicomStudyRepository.Where(t => t.FileCount > 0) on sv.Id equals noneDicomStudy.SubjectVisitId
select new CheckDBModel()
{
SubjectStatus = sv.Subject.Status,
SubjectVisitId = sv.Id,
SiteCode = sv.TrialSite.TrialSiteCode,
StudyDate = noneDicomStudy.ImageDate.ToString("yyyy-MM-dd"),
StudyId = noneDicomStudy.Id,
Modality = noneDicomStudy.Modality,
SubjectCode = subject.Code,
VisitName = sv.VisitName,
};
var dbList = (await dicomQuery.ToListAsync()).Union(await noneDicomQuey.ToListAsync()).ToList();
//处理数据库 大小写
dbList.ForEach(t => { t.Modality = t.Modality.ToUpper().Trim(); t.SiteCode = t.SiteCode.ToUpper().Trim(); t.VisitName = t.VisitName.ToUpper().Trim(); t.SubjectCode = t.SubjectCode.ToUpper().Trim(); });
var dbCheckList = _mapper.Map<List<CheckViewModel>>(dbList);
//按照Excel数据访视分组 按照数据库的数据 一个个的访视对比
var svExcelGroup = etcList.GroupBy(t => new { t.SiteCode, t.SubjectCode, t.VisitName })
.Select(g => new { g.Key.SubjectCode, g.Key.VisitName, g.Key.SiteCode, ExcelStudyList = g.ToList() }).ToList();
var fullCheckResultList = new List<FullCheckResult>();
foreach (var sv in svExcelGroup)
{
//Excel 的数据 在IRC 中可以找到该访视(一致性核查中,或者待核查)
if (dbCheckList.Any(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName))
{
var dbVisitStudyList = dbList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList();
//找到etc 当前visit site 和subject 一致的检查列表
var etcVisitStudyList = etcList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList();
//以我们系统数据库为准 判断是MR 还是MRI, 把Excel 里的MR 或者MRI 处理成一致MR MRI 是一致的
foreach (var item in etcVisitStudyList)
{
if (item.Modality == "MR" || item.Modality == "MRI")
{
item.Modality = mriModality;
}
}
//etc 和数据库 并集
var unionList = dbVisitStudyList.Union(etcVisitStudyList);
// 数据库存在
var dbExceptExcel = dbVisitStudyList.Except(etcVisitStudyList);
// excel 存在
var excelExceptDB = etcVisitStudyList.Except(dbVisitStudyList);
var dbCurrentVisitFirst = dbVisitStudyList.First();
//ETC 和系统的完全一致 两者没有差别
if (dbExceptExcel.Count() == 0 && excelExceptDB.Count() == 0)
{
//每个受试者每个访视加一条记录
fullCheckResultList.Add(new FullCheckResult()
{
SubjectStatus = dbCurrentVisitFirst.SubjectStatus,
CheckTime = DateTime.Now,
CheckState=CheckStateEnum.CVPassed,
SiteCode = dbCurrentVisitFirst.SiteCode,
SubjectCode = dbCurrentVisitFirst.SubjectCode,
VisitName = dbCurrentVisitFirst.VisitName,
Modalitys = string.Join('、', dbVisitStudyList.Select(t => t.Modality)),
LatestScanDateStr = dbVisitStudyList.Select(t => t.StudyDate).MaxBy(d => DateTime.Parse(d)) ?? ""
});
}
else
{
var checkResult =
String.Join(" | ", dbExceptExcel.Select(t => $"{_localizer["ConsistencyVerification_EdcL", t.StudyDate, t.Modality/*, _systemEmailConfig.SystemShortName*/]}")) + " | "
+ String.Join(" | ", excelExceptDB.Select(t => $"{_localizer["ConsistencyVerification_IrcLi", t.StudyDate, t.Modality, _systemEmailConfig.SystemShortName]}"));
//每个受试者每个访视加一条记录
fullCheckResultList.Add(new FullCheckResult()
{
SubjectStatus = dbCurrentVisitFirst.SubjectStatus,
CheckState = CheckStateEnum.None,
CheckTime = DateTime.Now,
SiteCode = dbCurrentVisitFirst.SiteCode,
SubjectCode = dbCurrentVisitFirst.SubjectCode,
VisitName = dbCurrentVisitFirst.VisitName,
Modalitys = string.Join('、', dbVisitStudyList.Select(t => t.Modality)),
LatestScanDateStr = dbVisitStudyList.Select(t => t.StudyDate).MaxBy(d => DateTime.Parse(d)) ?? "",
CheckResult = checkResult
});
}
}
}
//导到Excel 上传oss 回更记录状态
var list = fullCheckResultList;
var exportInfo = new ExcelExportInfo();
exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId);
exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId);
var fileStreamResult = (FileStreamResult)await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialConsistentFUllCheckList_Export, exportInfo, exportInfo.TrialCode, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(FullCheckResult));
var ossRelativePath = await _oSSService.UploadToOSSAsync(fileStreamResult.FileStream, $"{trialId.ToString()}/InspectionUpload/DataReconciliation", "DataReconciliation");
//var add = await _inspectionFileRepository.FindAsync(inspectionFileId);
//add.CheckState = EDCCheckState.Success;
//add.ResultPath = ossRelativePath;
//更新
await _inspectionFileRepository.BatchUpdateNoTrackingAsync(t => t.Id == inspectionFileId, u => new InspectionFile() { CheckState = EDCCheckState.Success, ResultPath = ossRelativePath });
await _subjectVisitRepository.SaveChangesAsync();
}
}
}

View File

@ -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(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
// 添加抄送
foreach (var ccUser in ccUserList)
{
messageToSend.Cc.Add(new MailboxAddress(ccUser.FullName, ccUser.EMail));
messageToSend.Cc.Add(new MailboxAddress(String.Empty, ccUser.EMail));
}
// 格式化邮件内容
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
@ -365,11 +365,9 @@ public class ImageConsumer(
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(inDto.EmailNoticeConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
@ -436,7 +434,7 @@ public class ImageConsumer(
}
/// <summary>
/// 发送阅片人筛选相关邮件的通用方法 已经处理项目
/// 发送阅片人筛选相关邮件的通用方法
/// </summary>
private async Task SendReviewerSelectionEmail(EmailNoticeConfig emailNoticeConfig, Guid trialId, List<Guid> enrollIdList)
{
@ -474,12 +472,12 @@ public class ImageConsumer(
var messageToSend = new MimeMessage();
// 发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
// 添加抄送
foreach (var ccUser in ccUserList)
{
messageToSend.Cc.Add(new MailboxAddress(ccUser.FullName, ccUser.EMail));
messageToSend.Cc.Add(new MailboxAddress(String.Empty, ccUser.EMail));
}
// 格式化邮件内容
@ -502,7 +500,7 @@ public class ImageConsumer(
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailNoticeConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}

View File

@ -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(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@ -128,7 +128,7 @@ public class UrgentMedicalReviewAddedEventConsumer(
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
@ -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(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@ -236,7 +236,7 @@ public class UrgentIRRepliedMedicalReviewConsumer(
};
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
@ -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(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@ -353,7 +353,8 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
};
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
@ -418,7 +419,7 @@ public class UrgentIRApplyedReReadingConsumer(
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == taskInfo.TrialId);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@ -462,7 +463,7 @@ public class UrgentIRApplyedReReadingConsumer(
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}

View File

@ -2,7 +2,6 @@
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;
@ -56,7 +55,6 @@ 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)
@ -69,8 +67,8 @@ public class UserSiteSurveySubmitedEventConsumer(
var toUserName = string.Empty;
//有SPM 并且参与
if (trialInfo.IsSPMJoinSiteSurvey && sPMOrCPMList.Count > 0)
//有SPM
if (sPMOrCPMList.Count > 0)
{
foreach (var user in sPMOrCPMList)
{
@ -98,6 +96,7 @@ 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);
@ -123,7 +122,7 @@ public class UserSiteSurveySubmitedEventConsumer(
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
@ -206,7 +205,7 @@ public class SiteSurveySPMSubmitedEventConsumer(
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
@ -284,7 +283,7 @@ public class SiteSurverRejectedEventConsumer(
{
//没有SPM PM驳回到CRC
messageToSend.To.Add(new MailboxAddress(siteSurveyInfo.UserName, siteSurveyInfo.Email));
messageToSend.To.Add(new MailboxAddress(String.Empty, siteSurveyInfo.Email));
}
//发件地址
@ -320,7 +319,7 @@ public class SiteSurverRejectedEventConsumer(
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}

View File

@ -89,14 +89,14 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
foreach (var userinfo in userinfoList)
{
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
}
var userNames = userinfoList.Select(x => x.FullName).ToList();
foreach (var pm in pmandAPm)
{
messageToSend.Cc.Add(new MailboxAddress(pm.FullName, pm.EMail));
messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
}
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@ -122,7 +122,7 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
@ -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(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var pm in pmandAPm)
{
messageToSend.Cc.Add(new MailboxAddress(pm.FullName, pm.EMail));
messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
}
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@ -221,7 +221,7 @@ public class CRCRepliedQCChallengeEventConsumer(
};
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
@ -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(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
if (craInfo != null)
{
messageToSend.Cc.Add(new MailboxAddress(craInfo.FullName, craInfo.EMail));
messageToSend.Cc.Add(new MailboxAddress(String.Empty, craInfo.EMail));
}
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@ -339,7 +339,7 @@ public class QCRepliedQCChallengeEventConsumer(
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
@ -439,7 +439,7 @@ public class CRCRepliedCheckChallengeEventConsumer(
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
@ -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(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
if (craInfo != null)
{
messageToSend.Cc.Add(new MailboxAddress(craInfo.FullName, craInfo.EMail));
messageToSend.Cc.Add(new MailboxAddress(String.Empty, craInfo.EMail));
}
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@ -549,7 +549,7 @@ public class PMRepliedCheckChallengeEventConsumer(
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
@ -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(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@ -648,7 +648,7 @@ public class CheckStateChangedToAuditEventConsumer(
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
@ -703,19 +703,15 @@ public class QCClaimTaskEventConsumer(
var userinfo = subjectVisit.CurrentActionUser;
if (userinfo == null)
{
return;
}
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var pm in pmandAPm)
{
messageToSend.Cc.Add(new MailboxAddress(pm.FullName, pm.EMail));
messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
}
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@ -761,7 +757,7 @@ public class QCClaimTaskEventConsumer(
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}

View File

@ -1,16 +1,14 @@
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 Newtonsoft.Json;
using System;
using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using IRaCIS.Core.Domain.BaseModel;
using IRaCIS.Core.Domain.Share;
using MassTransit;
using Microsoft.Extensions.Logging;
using NPOI.SS.Formula.Functions;
public class ConsumeExceptionFilter<T>(ILogger<ConsumeExceptionFilter<T>> _logger, IConfiguration _config) : IFilter<ConsumeContext<T>> where T : DomainEvent
public class ConsumeExceptionFilter<T>(ILogger<ConsumeExceptionFilter<T>> _logger) : IFilter<ConsumeContext<T>> where T : DomainEvent
{
public async Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next)
@ -25,44 +23,6 @@ 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}");
}
}
}

View File

@ -5,7 +5,6 @@ using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.MassTransit.Consumer;
using IRaCIS.Core.Application.Service.Reading.Dto;
using IRaCIS.Core.Domain.Models;
using MassTransit;
using Microsoft.Extensions.Options;
using MimeKit;
@ -27,7 +26,6 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
public class UrgentIRUnReadTaskRecurringEventConsumer(
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
IRepository<VisitTask> _visitTaskRepository,
IRepository<Trial> _trialRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<TrialUserRole> _trialUserRoleRepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
@ -37,15 +35,9 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
public async Task Consume(ConsumeContext<UrgentIRUnReadTaskRecurringEvent> context)
{
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var trialId = context.Message.TrialId;
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
var scenario = EmailBusinessScenario.ExpeditedReading;
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
@ -169,7 +161,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
//处理标记已通知的任务

View File

@ -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,7 +34,6 @@ 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)
{
@ -163,7 +162,7 @@ public static class OldRecurringEmailHelper
if (sendEmailConfig != null)
{
await SendEmailHelper.SendEmailAsync(sendEmailConfig, trialInfo);
await SendEmailHelper.SendEmailAsync(sendEmailConfig);
}

View File

@ -7,7 +7,6 @@ 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;
@ -15,7 +14,7 @@ using System.Threading.Tasks;
namespace IRaCIS.Core.Application.MassTransit.Consumer;
//项目手动选择 周期性邮件
/// <summary>
@ -26,20 +25,14 @@ 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 = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var isEn_us = false;
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync();
@ -71,9 +64,8 @@ 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 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);
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
user.FullName, DateTime.Now, sendStat.ToBeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
return (topicStr, htmlBodyStr, isEn_us, userId);
};
@ -99,20 +91,13 @@ 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 = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var isEn_us = false;
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
@ -146,10 +131,9 @@ 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 htmlContent = isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN;
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent),
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
user.FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), sendStat.ToBeDealedCount - sendStat.ReUploadTobeDealedCount, sendStat.ReUploadTobeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
user.FullName, DateTime.Now, sendStat.ToBeDealedCount - sendStat.ReUploadTobeDealedCount, sendStat.ReUploadTobeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
return (topicStr, htmlBodyStr, false, userId);
@ -174,20 +158,13 @@ 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 = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var isEn_us = false;
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync();
@ -222,12 +199,9 @@ 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 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);
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
user.FullName, DateTime.Now, sendStat.ToBeClaimedCount, sendStat.ToBeReviewedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
return (topicStr, htmlBodyStr, false, userId);
};

View File

@ -42,12 +42,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
DateTime now = DateTime.Now;
Console.WriteLine("发送定时过期提醒");
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
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)))
@ -94,7 +89,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(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
@ -119,7 +114,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
if (emailConfig != null)
{
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
@ -155,11 +150,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
public async Task Consume(ConsumeContext<SystemDocumentPublishEvent> context)
{
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
// 记录是否只发送给新增角色的日志
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
@ -239,7 +230,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(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));

View File

@ -14,7 +14,6 @@ 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;
@ -53,11 +52,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
DateTime now = DateTime.Now;
Console.WriteLine("发送定时项目过期提醒");
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var trialDocQuery =
from trialDoc in _trialDocumentRepository.AsQueryable(true)
@ -80,7 +75,6 @@ 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,
@ -115,11 +109,10 @@ 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(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
@ -144,8 +137,8 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
if (emailConfig != null)
{
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
@ -169,7 +162,6 @@ 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,
@ -181,11 +173,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
public async Task Consume(ConsumeContext<TrialDocumentPublishEvent> context)
{
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
// 记录是否只发送给新增角色的日志
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
@ -210,7 +198,6 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
{
IsSystemDoc = true,
Id = trialDoc.Id,
TrialId= trialDoc.TrialId,
CreateTime = trialDoc.CreateTime,
IsDeleted = trialDoc.IsDeleted,
SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes,
@ -232,9 +219,7 @@ 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 trial in _trialRepository.AsQueryable(false) on trialDoc.TrialId equals trial.Id
join identityUser in _identityUserRepository.AsQueryable(false).Where(u => u.Status == UserStateEnum.Enable)
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))
@ -243,8 +228,6 @@ 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,
@ -259,7 +242,6 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
FullFilePath = trialDoc.Path
};
}
var datalist = await systemDocQuery.IgnoreQueryFilters().ToListAsync();
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
@ -275,7 +257,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(userinfo.FullName, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
@ -301,12 +283,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
{
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
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);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
msg += "发送成功";
}

View File

@ -490,8 +490,6 @@ namespace IRaCIS.Core.Application.ViewModel
public int? RandomOrder { get; set; }
public bool? IsRandomOrderList { get; set; }
public CriterionType? CriterionType { get; set; }
}

View File

@ -17,9 +17,7 @@ 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;
@ -47,8 +45,6 @@ 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,
@ -1031,8 +1027,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 });
@ -1175,9 +1171,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
var visitQuery = _visitTaskRepository.Where(x => x.TrialId == trialId && x.DoctorUserId == _userInfo.UserRoleId && x.TaskState == TaskState.Effect)
.WhereIf(inQuery.SubjectId != null, x => x.SubjectId == inQuery.SubjectId)
.WhereIf(!string.IsNullOrEmpty(subjectCode), t => (t.Subject.Code.Contains(subjectCode!) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(subjectCode!) && t.IsAnalysisCreate))
.WhereIf(critrion.CriterionType == CriterionType.OCT, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.NoneDicomStudyList.Where(t => t.Modality == "OCT").SelectMany(t => t.ImageLabelNoneDicomFileList).Any() : true)
.WhereIf(critrion.CriterionType == CriterionType.IVUS, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.NoneDicomStudyList.Where(t => t.Modality == "IVUS").SelectMany(t => t.ImageLabelNoneDicomFileList).Any() : true);
.WhereIf(!string.IsNullOrEmpty(subjectCode), t => (t.Subject.Code.Contains(subjectCode!) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(subjectCode!) && t.IsAnalysisCreate));
var visitGroupQuery = visitQuery.GroupBy(x => new { x.SubjectId, x.Subject.Code, x.BlindSubjectCode });
@ -2087,9 +2081,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
{
if (origenalTask.ReadingCategory == ReadingCategory.Visit)
{
newTask.IsCopyLesionAnswer = true;
//CopyForms(newTask, origenalTask);
CopyForms(newTask, origenalTask);
}
@ -2187,6 +2179,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
if (origenalTask.ReadingCategory == ReadingCategory.Visit)
{
CopyForms(newTask, origenalTask);
}
}
@ -2196,8 +2189,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
{
if (origenalTask.ReadingCategory == ReadingCategory.Visit)
{
newTask.IsCopyLesionAnswer = true;
//CopyForms(newTask, origenalTask);
CopyForms(newTask, origenalTask);
}
}
@ -2222,7 +2214,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();
@ -2264,7 +2256,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)
{
@ -2272,7 +2264,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;
@ -2294,54 +2286,13 @@ 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;
}
@ -2434,10 +2385,6 @@ 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();
@ -2595,12 +2542,9 @@ 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()
@ -2762,13 +2706,6 @@ 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));
}
}

View File

@ -1,187 +0,0 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-10-28 06:22:47Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using IRaCIS.Core.Domain.Share;
using System;
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
{
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
public List<EmailRecipientLogView> RecipientList { get; set; }
public List<EmaliAttachmentInfo> AttachmentList { get; set; }
}
public class EmaliAttachmentInfo
{
/// <summary>
/// 附件名称
/// </summary>
public string AttachmentName { get; set; }
/// <summary>
/// 附件路径
/// </summary>
public string AttachmentPath { get; set; }
}
public class EmailRecipientLogView
{
public Guid Id { get; set; }
/// <summary>
/// 邮件Id
/// </summary>
public Guid EmailLogId { get; set; }
/// <summary>
/// 收件人姓名
/// </summary>
public string RecipientName { get; set; }
/// <summary>
/// 收件人地址
/// </summary>
public string RecipientAddress { get; set; }
/// <summary>
/// 收件人类型
/// </summary>
public RecipientType RecipientTypeEnum { get; set; }
/// <summary>
/// 排序
/// </summary>
public int Sort { get; set; }
}
public class EmailLogAddOrEdit
{
public Guid? Id { get; set; }
/// <summary>
/// 邮件Id
/// </summary>
public string MessageId { get; set; }
/// <summary>
/// 唯一Id
/// </summary>
public string UniqueId { get; set; }
/// <summary>
/// 邮件主题
/// </summary>
public string EmailSubject { get; set; } = string.Empty;
/// <summary>
/// 日期
/// </summary>
public DateTime? EmailDate { get; set; }
/// <summary>
/// 错误信息
/// </summary>
public string ErrorInfo { get; set; }
/// <summary>
/// 发件人地址
/// </summary>
public string SenderAddress { get; set; }
/// <summary>
/// 发件人姓名
/// </summary>
public string SenderName { get; set; }
/// <summary>
/// 邮件内容
/// </summary>
public string Content { get; set; }
/// <summary>
/// 邮件状态
/// </summary>
public EmailState EmailStateEnum { get; set; }
}
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
{
public Guid? TrialId { 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;
}

View File

@ -159,12 +159,6 @@ namespace IRaCIS.Core.Application.ViewModel
public class FrontAuditConfigAddOrEdit
{
public Guid? Id { get; set; }
/// <summary>
/// 适用的标准
/// </summary>
public List<CriterionType> ApplyCriterionList { get; set; } = new List<CriterionType>() { };
public string Value { get; set; } = string.Empty;
public string ValueCN { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;

View File

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.Service.Common.DTO
{
//public class MailModel
//{
//}
}

View File

@ -1,768 +0,0 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 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 MailKit;
using MailKit.Net.Imap;
using MailKit.Search;
using MailKit.Security;
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;
/// <summary>
/// 邮件日志
/// </summary>
/// <param name="_emailLogRepository"></param>
/// <param name="systemEmailConfig"></param>
/// <param name="_mapper"></param>
/// <param name="_userInfo"></param>
/// <param name="_localizer"></param>
[ApiExplorerSettings(GroupName = "Common")]
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
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
/// <summary>
/// 获取邮件日志列表
/// </summary>
/// <param name="inQuery"></param>
/// <returns></returns>
[HttpPost]
public async Task<PageOutput<EmailLogView>> GetEmailLogList(EmailLogQuery inDto)
{
var emailFromName = await _trialRepository.Where(x=>x.Id==inDto.TrialId).Select(x=>x.EmailFromName).FirstOrDefaultAsync();
if (emailFromName.IsNullOrEmpty())
{
emailFromName = _systemEmailConfig.FromName;
}
var emailLogQueryable = _emailLogRepository
.Where(x=>x.SenderName== emailFromName)
.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>
/// <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>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<GetEmailInfoOutDto> GetEmailInfo(GetEmailInfoInDto inDto)
{
var emailInfo=await _emailLogRepository
.Where(x => x.Id == inDto.Id).Include(x=>x.EmailRecipientLogList)
.ProjectTo<GetEmailInfoOutDto>(_mapper.ConfigurationProvider).AsNoTracking()
.FirstNotNullAsync();
if (emailInfo.UniqueId.IsNotNullOrEmpty())
{
using (var client = new ImapClient())
{
try
{
await AuthenticateImap(client,inDto.TrialId);
var sentFolder = OpenSentFolder(client);
var uid = new UniqueId(uint.Parse(emailInfo.UniqueId));
var message = sentFolder.GetMessage(uid);
emailInfo.Content = message.HtmlBody ?? message.TextBody ?? string.Empty;
if (emailInfo.AttachmentList.Count == 0)
{
List< EmaliAttachmentInfo > attachmentInfos = new List<EmaliAttachmentInfo>();
foreach (var att in message.Attachments)
{
EmaliAttachmentInfo emaliAttachmentInfo = new EmaliAttachmentInfo();
emaliAttachmentInfo.AttachmentName = att.ContentDisposition?.FileName ?? att.ContentType.Name ?? "unknown";
// 2. 解码后的流直接上传,不落盘
if (att is MimePart part)
{
// 重要:每次上传新建一个独立流,否则迭代过程中流位置会乱
await using var decodeStream = new MemoryStream();
await part.Content.DecodeToAsync(decodeStream);
decodeStream.Position = 0;
emaliAttachmentInfo.AttachmentPath = await oSSService.UploadToOSSAsync(
fileStream: decodeStream,
oosFolderPath: $"EmailAttachment/{emailInfo.Id}", // OSS 虚拟目录
fileRealName: emaliAttachmentInfo.AttachmentName,
isFileNameAddGuid: true); // 让方法自己在文件名前加 Guid
attachmentInfos.Add(emaliAttachmentInfo);
}
}
List<EmailAttachmentLog> emailAttachmentLog = attachmentInfos.Select(x => new EmailAttachmentLog()
{
EmailLogId = emailInfo.Id.Value,
AttachmentName = x.AttachmentName,
AttachmentPath = x.AttachmentPath,
}).ToList();
await _emailAttachmentLogRepository.AddRangeAsync(emailAttachmentLog);
await _emailAttachmentLogRepository.SaveChangesAsync();
emailInfo.AttachmentList = attachmentInfos;
}
sentFolder.Close();
}
catch (Exception ex)
{
}
finally
{
client.Disconnect(true);
}
}
}
//var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, $"{trialId.ToString()}/InspectionUpload/DataReconciliation", realFileName);
return emailInfo;
}
/// <summary>
/// 重发邮件
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> ResendEmail(ResendEmailInDto inDto)
{
var emailInfo = await _emailLogRepository
.Where(x => x.Id == inDto.Id)
.ProjectTo<GetEmailInfoOutDto>(_mapper.ConfigurationProvider)
.FirstNotNullAsync();
if (emailInfo.UniqueId.IsNotNullOrEmpty())
{
using (var client = new ImapClient())
{
try
{
await AuthenticateImap(client,inDto.TrialId);
var sentFolder = OpenSentFolder(client);
var uid = new UniqueId(uint.Parse(emailInfo.UniqueId));
var message = sentFolder.GetMessage(uid);
emailInfo.Content = message.HtmlBody ?? message.TextBody ?? string.Empty;
sentFolder.Close();
}
catch (Exception ex)
{
}
finally
{
client.Disconnect(true);
}
}
}
var messageToSend = new MimeMessage();
// 发件地址
messageToSend.From.Add(new MailboxAddress(emailInfo.SenderName, emailInfo.SenderAddress));
foreach (var item in emailInfo.RecipientList.Where(x => x.RecipientTypeEnum == RecipientType.To))
{
messageToSend.To.Add(new MailboxAddress(item.RecipientName, item.RecipientAddress));
}
foreach (var item in emailInfo.RecipientList.Where(x => x.RecipientTypeEnum == RecipientType.Cc))
{
messageToSend.Cc.Add(new MailboxAddress(item.RecipientName, item.RecipientAddress));
}
messageToSend.Subject = emailInfo.EmailSubject;
var builder = new BodyBuilder();
builder.HtmlBody = emailInfo.Content;
messageToSend.Body = builder.ToMessageBody();
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();
}
/// <summary>
/// 同步邮件
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> SynchronizationEmail(SynchronizationEmailInDto inDto)
{
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
{
await AuthenticateImap(client, inDto.TrialId);
var sentFolder = OpenSentFolder(client);
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);
if (message.Date.DateTime <= startDate)
{
continue;
}
var emaillog = new EmailLog
{
Id = NewId.NextGuid(),
UniqueId = uid.ToString(),
MessageId = message.MessageId ?? string.Empty,
EmailSubject = message.Subject ?? string.Empty,
EmailDate = message.Date.DateTime,
EmailStateEnum = EmailState.Success,
};
var fromMailbox = message.From.Mailboxes.FirstOrDefault();
if (fromMailbox != null)
{
emaillog.SenderAddress = fromMailbox.Address;
emaillog.SenderName = fromMailbox.Name ?? string.Empty;
}
List<EmailRecipientLog> recipientLogs = new List<EmailRecipientLog>();
int sort = 0;
message.To.Mailboxes.ForEach(x =>
{
recipientLogs.Add(new EmailRecipientLog()
{
RecipientName = x.Name ?? string.Empty,
RecipientAddress = x.Address,
EmailLogId = emaillog.Id,
RecipientTypeEnum = RecipientType.To,
Sort = sort++,
});
});
sort = 0;
message.Cc.Mailboxes.ForEach(x =>
{
recipientLogs.Add(new EmailRecipientLog()
{
RecipientName = x.Name ?? string.Empty,
RecipientAddress = x.Address,
EmailLogId = emaillog.Id,
RecipientTypeEnum = RecipientType.Cc,
Sort = sort++,
});
});
emailList.Add(emaillog);
EmailRecipientLogList.AddRange(recipientLogs);
}
catch (Exception ex)
{
Console.WriteLine($"处理邮件 {uid} 失败: {ex.Message}");
}
}
sentFolder.Close();
}
catch (Exception ex)
{
Console.WriteLine($"同步发件箱邮件时出错: {ex.Message}");
}
finally
{
client.Disconnect(true);
}
}
// 保存同步的发件箱邮件
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)
{
IMailFolder folder = null;
try
{
folder = client.GetFolder(SpecialFolder.Sent);
}
catch { }
if (folder == null)
{
var candidates = new[] { "已发送", "已发送邮件", "Sent", "Sent Items", "[Gmail]/Sent Mail" };
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, "Sent", StringComparison.OrdinalIgnoreCase) ||
string.Equals(sub.FullName, "Sent Items", StringComparison.OrdinalIgnoreCase))
{
folder = sub;
break;
}
}
}
}
catch { }
}
if (folder == null)
throw new InvalidOperationException("未找到已发送文件夹");
folder.Open(FolderAccess.ReadOnly);
return folder;
}
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
{
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)
{
//if (_systemEmailConfig.UseOAuth2 && _systemEmailConfig.OAuth2AccessToken.IsNotNullOrEmpty())
//{
// var sasl = new SaslMechanismOAuth2(_systemEmailConfig.FromEmail, _systemEmailConfig.OAuth2AccessToken);
// client.Authenticate(sasl);
// return;
//}
//if (_systemEmailConfig.OAuth2AccessToken.IsNullOrEmpty())
//{
// var token = AcquireOAuth2TokenByPassword();
// if (token.IsNotNullOrEmpty())
// {
// _systemEmailConfig.OAuth2AccessToken = token;
// var sasl = new SaslMechanismOAuth2(_systemEmailConfig.FromEmail, token);
// client.Authenticate(sasl);
// return;
// }
//}
throw;
}
}
/// <summary>
/// 处理收件箱中的邮件发送失败通知
/// </summary>
/// <returns></returns>
public async Task ProcessInboxFailureNotifications(DateTime startTime, Guid? trialId)
{
using (var client = new ImapClient())
{
try
{
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($"连接收件箱处理失败通知邮件时出错: {ex.Message}");
Console.WriteLine($"异常堆栈: {ex.StackTrace}");
}
finally
{
try
{
client.Disconnect(true);
}
catch (Exception ex)
{
Console.WriteLine($"断开IMAP客户端连接时出错: {ex.Message}");
}
}
}
}
/// <summary>
/// 从失败通知邮件中提取原始邮件的Message-Id
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
private string ExtractOriginalMessageId(MimeMessage message)
{
// 首先检查邮件头中是否有原始Message-Id
var originalMessageId = message.Headers["Original-Message-ID"] ??
message.Headers["X-Original-Message-ID"] ??
message.Headers["In-Reply-To"];
if (!string.IsNullOrEmpty(originalMessageId))
{
// 清理Message-Id格式移除尖括号
originalMessageId = originalMessageId.Trim('<', '>');
return originalMessageId;
}
// 检查邮件附件中是否包含.eml文件这是最常见的失败通知格式
foreach (var attachment in message.Attachments)
{
if (attachment is MessagePart messagePart)
{
// 直接获取嵌入的邮件消息
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 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)
{
return match.Groups[1].Value;
}
// 尝试从邮件引用部分提取
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;
}
return null;
}
public async Task<IResponseOutput> AddOrUpdateEmailLog(EmailLogAddOrEdit addOrEditEmailLog)
{
var entity = await _emailLogRepository.InsertOrUpdateAsync(addOrEditEmailLog, true);
return ResponseOutput.Ok(entity.Id.ToString());
}
[HttpDelete("{emailLogId:guid}")]
public async Task<IResponseOutput> DeleteEmailLog(Guid emailLogId)
{
var success = await _emailLogRepository.DeleteFromQueryAsync(t => t.Id == emailLogId,true);
return ResponseOutput.Ok();
}
}

View File

@ -4,18 +4,9 @@
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using DocumentFormat.OpenXml.Spreadsheet;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Domain.Share;
using MailKit;
using MailKit.Net.Imap;
using MailKit.Search;
using MailKit.Security;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using NPOI.SS.Formula.Functions;
using System.Text.RegularExpressions;
@ -26,75 +17,8 @@ namespace IRaCIS.Core.Application.Contracts
/// </summary>
[ApiExplorerSettings(GroupName = "Common")]
public class EmailNoticeConfigService(IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<EmailNoticeUserType> _emailNoticeUserTypeRepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig,
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IEmailNoticeConfigService
IRepository<EmailNoticeUserType> _emailNoticeUserTypeRepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IEmailNoticeConfigService
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
/// <summary>
/// 获取邮件列表
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<object> GetEmailList()
{
List<IMessageSummary> emailList = new List<IMessageSummary>();
using (var client = new ImapClient())
{
try
{
// 连接阿里邮箱 IMAP 服务器(使用 SSL
client.Connect(_systemEmailConfig.Imap, 993, SecureSocketOptions.SslOnConnect);
// 登录
client.Authenticate(_systemEmailConfig.FromEmail, _systemEmailConfig.AuthorizationCode);
// 3. 获取发件箱文件夹 - 使用你找到的“已发送”
var sentFolder = client.GetFolder("已发送");
sentFolder.Open(FolderAccess.ReadOnly);
// 4. 搜索所有邮件(你可以在这里添加更精确的搜索条件)
var uids = sentFolder.Search(SearchQuery.All);
Console.WriteLine($"找到 {uids.Count} 封已发送邮件");
// 5. 遍历并处理邮件示例中处理前10封
foreach (var uid in uids.Take(10))
{
var message = sentFolder.GetMessage(uid);
// 输出邮件基本信息
Console.WriteLine($"主题: {message.Subject}");
Console.WriteLine($"收件人: {string.Join(", ", message.To.Mailboxes.Select(m => m.Address))}");
Console.WriteLine($"日期: {message.Date.LocalDateTime:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($"发件人: {message.From}");
Console.WriteLine("----------------------------------");
}
sentFolder.Close();
}
catch (Exception ex)
{
Console.WriteLine($"操作失败: {ex.Message}");
}
finally
{
client.Disconnect(true);
}
}
return true;
}
[HttpPost]
public async Task<PageOutput<EmailNoticeConfigView>> GetEmailNoticeConfigList(EmailNoticeConfigQuery inQuery)

View File

@ -22,8 +22,6 @@ 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;
@ -34,8 +32,6 @@ 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,
@ -471,10 +467,7 @@ 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.TrialUserRoleList.Any(t => t.UserId == _userInfo.UserRoleId && 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.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()
@ -1626,7 +1619,6 @@ namespace IRaCIS.Core.Application.Service.Common
exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId);
if (inQuery.IsRandomOrderList == true)
{
list = list.OrderBy(t => t.RandomOrder).ToList();
@ -1765,80 +1757,6 @@ 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);
@ -1848,88 +1766,6 @@ 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>
/// 影像下载记录表
@ -2474,263 +2310,8 @@ namespace IRaCIS.Core.Application.Service.Common
#region 通用阅片结果导出
/// <summary>
/// 一致性分析结果导出 7 8 分别是自身 和组件一致性
/// </summary>
/// <param name="inQuery"></param>
/// <param name="_commonDocumentRepository"></param>
/// <param name="_dictionaryService"></param>
/// <param name="_trialRepository"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> GetAnalysisTaskList_Export(VisitTaskQuery inQuery,
[FromServices] IRepository<CommonDocument> _commonDocumentRepository,
[FromServices] IDictionaryService _dictionaryService,
[FromServices] IRepository<Trial> _trialRepository)
{
//每次查询必须是单标准的
var criterion = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == inQuery.TrialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionName, t.ArbitrationRule }).FirstNotNullAsync();
var query = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && t.TaskAllocationState == TaskAllocationState.Allocated && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze))
//一致性分析
.WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults, t => t.IsSelfAnalysis == true || t.IsSelfAnalysis == null)
.WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults, t => t.IsSelfAnalysis == null ? t.Subject.SubjectVisitTaskList.Any(u => u.IsSelfAnalysis == true && u.VisitTaskNum == t.VisitTaskNum && u.DoctorUserId == t.DoctorUserId && u.TrialReadingCriterionId == t.TrialReadingCriterionId) : true)
.WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfInterReaderAnalysisResults, t => t.IsSelfAnalysis == false || t.IsSelfAnalysis == null)
//访视和全局查询已签名完成的,裁判可以是未签名,未完成的
.Where(t => (t.ReadingTaskState == ReadingTaskState.HaveSigned && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global)) || t.ReadingCategory == ReadingCategory.Judge)
.WhereIf(inQuery.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId)
.WhereIf(inQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == inQuery.TrialSiteId)
.WhereIf(inQuery.IsUrgent != null, t => t.IsUrgent == inQuery.IsUrgent)
.WhereIf(inQuery.DoctorUserId != null, t => t.DoctorUserId == inQuery.DoctorUserId)
.WhereIf(inQuery.ReadingCategory != null, t => t.ReadingCategory == inQuery.ReadingCategory)
//.WhereIf(inQuery.ReadingTaskState != null, t => t.ReadingTaskState == inQuery.ReadingTaskState)
.WhereIf(inQuery.TaskAllocationState != null, t => t.TaskAllocationState == inQuery.TaskAllocationState)
.WhereIf(inQuery.ArmEnum != null, t => t.ArmEnum == inQuery.ArmEnum)
.WhereIf(!string.IsNullOrEmpty(inQuery.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.IsAnalysisCreate == false))
.WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.TaskName.Contains(inQuery.TaskName) || t.TaskBlindName.Contains(inQuery.TaskName))
.WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.Subject.Code.Contains(inQuery.SubjectCode))
.WhereIf(inQuery.BeginAllocateDate != null, t => t.AllocateTime > inQuery.BeginAllocateDate)
.WhereIf(inQuery.EndAllocateDate != null, t => t.AllocateTime < inQuery.EndAllocateDate);
var list = await query.ProjectTo<AnalysisDynamicCommonExport>(_mapper.ConfigurationProvider,
new
{
readingExportType = inQuery.ReadingExportType,
criterionType = criterion.CriterionType,
trialReadingCriterionId = inQuery.TrialReadingCriterionId,
isEn_Us = _userInfo.IsEn_Us
}).ToListAsync();
list = list.OrderBy(t => t.SubjectCode).ThenBy(t => t.ArmEnum).ThenBy(t => t.VisitTaskNum).ToList();
var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo<ExcelExportInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException();
exportInfo.CriterionName = criterion.CriterionName;
#region 处理系统标准存在疾病和整体肿瘤合并
//如果是以合并后的找翻译字典,会少,所以必须放在前面
var translateDicNameList = list.SelectMany(t => t.QuestionAnswerList).Where(t => t.TranslateDicName.IsNotNullOrEmpty()).Select(t => t.TranslateDicName).Distinct().ToList();
//针对1.1 整体肿瘤评估 有的两列要合并一列
if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.RECIST1Pointt1_MB || criterion.CriterionType == CriterionType.IRECIST1Point1)
{
foreach (var item in list)
{
//处理合并表头
var questionType = item.IsBaseline == true ? QuestionType.ExistDisease : QuestionType.Tumor;
var findItem = item.QuestionAnswerList.Where(t => t.QuestionType == questionType).FirstOrDefault();
if (findItem != null)
{
findItem.QuestionName = _userInfo.IsEn_Us ? "Overall Response" : "整体肿瘤评估";
}
if (item.IsBaseline == true)
{
item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.Tumor).ToList();
}
else
{
item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.ExistDisease).ToList();
}
}
}
else if (criterion.CriterionType == CriterionType.Lugano2014 || criterion.CriterionType == CriterionType.Lugano2014WithoutPET)
{
foreach (var item in list)
{
//处理合并表头
var questionType = item.IsBaseline == true ? QuestionType.ExistDisease : QuestionType.ImgOncology;
var findItem = item.QuestionAnswerList.Where(t => t.QuestionType == questionType).FirstOrDefault();
if (findItem != null)
{
findItem.QuestionName = _userInfo.IsEn_Us ? "Overall Response" : "整体肿瘤评估";
}
if (item.IsBaseline == true)
{
item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.ImgOncology).ToList();
}
else
{
item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.ExistDisease).ToList();
}
}
}
else if (criterion.CriterionType == CriterionType.PCWG3)
{
}
else if (criterion.CriterionType == CriterionType.SelfDefine)
{
//自定义的又问题名称重复 所以统一加上组名
//有重复的就加,没有重复的就不加
if (list.Any(t => t.QuestionAnswerList.Select(t => t.QuestionName).Count() != t.QuestionAnswerList.Select(t => t.QuestionName).Distinct().Count()))
{
foreach (var item in list)
{
foreach (var qs in item.QuestionAnswerList)
{
qs.QuestionName = qs.Group + "_" + qs.QuestionName;
}
}
}
}
#endregion
var export_Template = StaticData.Export.TrialSelfAnalysisList_Export;
#region 自身一致性分析和组间一致性分析
if (inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults)
{
//找到非一致性分析的任务
var selfExportList = list.Where(t => t.IsSelfAnalysis == null).ToList();
//处理一致性分析结果是否和原始阅片是否一致
foreach (var item in selfExportList)
{
//找到一致性分析的结果
var selfAnalysisTask = list.Where(t => t.IsSelfAnalysis == true && t.SubjectCode == item.SubjectCode && t.VisitTaskNum == item.VisitTaskNum && t.TaskName == t.TaskName && t.UserName == item.UserName).FirstOrDefault();
//将自身一致性分析的字段 赋值到访视任务这个字段
item.IsAnalysisDiffToOriginalData = selfAnalysisTask?.IsAnalysisDiffToOriginalData;
//处理再次阅片人的结果
if (selfAnalysisTask != null)
{
var cloneQuestionAnswerList = selfAnalysisTask.QuestionAnswerList.Clone();
foreach (var qItem in cloneQuestionAnswerList)
{
qItem.QuestionName = qItem.QuestionName + $"{(_userInfo.IsEn_Us ? "(Again)" : "()")}";
}
item.QuestionAnswerList = item.QuestionAnswerList.Union(cloneQuestionAnswerList).ToList();
}
}
list = selfExportList;
}
else
{
export_Template = StaticData.Export.TrialGroupAnalysisList_Export;
var newList = new List<AnalysisDynamicCommonExport>();
foreach (var group in list.GroupBy(t => new { t.SubjectCode, t.VisitTaskNum, t.TaskName }).OrderBy(g => g.Key.SubjectCode).ThenBy(g => g.Key.VisitTaskNum))
{
var subjectVisitGroupList = group.ToList();
//找到当前访视组间一致性分析的任务结果
var groupOtherTaskList = subjectVisitGroupList.Where(t => t.IsSelfAnalysis == false).ToList();
foreach (var subjectVisitTaskArm in subjectVisitGroupList.Where(t => t.IsSelfAnalysis == null).OrderBy(t => t.ArmEnum))
{
foreach (var otherTask in groupOtherTaskList)
{
//非一致性分析任务
var cloneObj = subjectVisitTaskArm.Clone();
var otherTaskQuestionAnserList = otherTask.QuestionAnswerList.Clone();
foreach (var qItem in otherTaskQuestionAnserList)
{
qItem.QuestionName = qItem.QuestionName + $"{(_userInfo.IsEn_Us ? "(Again)" : "()")}";
}
//处理 再次阅片人,再次阅片人角色 两列
var addQuestionList = new List<CommonQuesionInfo>();
addQuestionList.Add(new CommonQuesionInfo() { QuestionName = _userInfo.IsEn_Us ? "Reviwer(Again)" : "阅片人(再次)", QuestionValue = otherTask.UserName });
addQuestionList.Add(new CommonQuesionInfo() { QuestionName = _userInfo.IsEn_Us ? "Reviwer Role(Again)" : "阅片人角色(再次)", QuestionValue = ((int)otherTask.ArmEnum).ToString(), TranslateDicName = "ArmEnum" });
cloneObj.QuestionAnswerList = cloneObj.QuestionAnswerList.Union(addQuestionList).Union(otherTaskQuestionAnserList).ToList();
cloneObj.IsGroupAnalysisDiffToOriginalData = cloneObj.ArmEnum == Arm.DoubleReadingArm1 ? otherTask.IsGroupDiffArm1 : otherTask.IsGroupDiffArm2;
newList.Add(cloneObj);
}
}
}
translateDicNameList.Add("ArmEnum");
list = newList;
}
#endregion
var columNameList = list.SelectMany(t => t.QuestionAnswerList).Where(t => t.QuestionName.IsNotNullOrEmpty()).Select(t => t.QuestionName).Distinct().ToList();
exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list.Where(t => t.ReadingCategory != ReadingCategory.Global).ToList(), _userInfo.TimeZoneId);
exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId);
var dynamicColumnConfig = new DynamicColumnConfig()
{
//可读的列表名行索引,不是{{}} 模板行索引
AutoColumnTitleRowIndex = 2,
AutoColumnStartIndex = 5,
TempalteLastColumnIndex = 4,
DynamicItemDicName = "TranslateDicName",
DynamicItemValueName = "QuestionValue",
DynamicItemTitleName = "QuestionName",
DynamicListName = "QuestionAnswerList",
RemoveColunmIndexList = new List<int>() { },
ColumnIdNameList = columNameList.Select(t => new DynamicColumnConfig.ColumItem() { Id = Guid.Empty, Name = t }).ToList(),
TranslateDicNameList = translateDicNameList ?? new List<string>()
};
var (memoryStream, fileName) = await ExcelExportHelper.DataExport_NpoiTestAsync(export_Template, exportInfo, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(AnalysisDynamicCommonExport), criterion.CriterionType, dynamicColumnConfig);
return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
FileDownloadName = $"{exportInfo.ResearchProgramNo}_{exportInfo.CriterionName}_{fileName}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx"
};
}
/// <summary>
/// 获取阅片标准可以导出的列表
@ -2760,23 +2341,13 @@ namespace IRaCIS.Core.Application.Service.Common
list.Add(new ExportDocumentDes() { Code = StaticData.Export.ReadingLession_Export, ExportCatogory = ExportResult.DetailedTableOfLesions });
}
if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.Lugano2014)
{
list.Add(new ExportDocumentDes() { Code = StaticData.Export.TumorCDISC_Export, ExportCatogory = ExportResult.TumorCDISC_Export });
}
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.CriterionType == CriterionType.IVUS)
//else if (criterion.CriterionType == CriterionType.SelfDefine)
{
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 });
list.Add(new ExportDocumentDes() { Code = StaticData.Export.CDISC_Reading_Export, ExportCatogory = ExportResult.CDISC });
}
switch (criterion.ArbitrationRule)
@ -2903,7 +2474,7 @@ namespace IRaCIS.Core.Application.Service.Common
//阅片结果表
export_Template = StaticData.Export.CommonReading_Export;
}
//斑块表
else if (inQuery.ReadingExportType == ExportResult.OCT_ReadingLession_Export)
{
//OCT
@ -2957,7 +2528,7 @@ namespace IRaCIS.Core.Application.Service.Common
}
// 非肿瘤标准 包括自定义,以及系统非肿瘤 会配置到表格层级 问题名称是 表格名称_表格子问题名
else if (
criterion.CriterionGroup == CriterionGroup.Nontumorous && inQuery.ReadingExportType != ExportResult.NoneTumorCDISC
criterion.CriterionGroup == CriterionGroup.Nontumorous && inQuery.ReadingExportType != ExportResult.CDISC
)
{
@ -2974,7 +2545,7 @@ namespace IRaCIS.Core.Application.Service.Common
}
// CDISC 导出 只管到 外层问题层级 和阅片结果表是保持一致
else if (inQuery.ReadingExportType == ExportResult.NoneTumorCDISC)
else if (inQuery.ReadingExportType == ExportResult.CDISC)
{
if (criterion.CriterionGroup == CriterionGroup.Tumor)
@ -3042,7 +2613,7 @@ namespace IRaCIS.Core.Application.Service.Common
#endregion
if (inQuery.ReadingExportType != ExportResult.NoneTumorCDISC)
if (inQuery.ReadingExportType != ExportResult.CDISC)
{
//最终EXCEL 列
var configCoumNameList = new List<DynamicColumnConfig.ColumItem>();
@ -3170,7 +2741,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, OptionTypeEnum = t.OptionTypeEnum, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName ,Unit=t.Unit});
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName });
//有三部分组成 外层问题+ 没有配置病灶编号和类型+ 动态的表格问题
var dynamicLessionInfoList = item.QuestionAnswerList.Union(addLessionInfoList).Union(dynamicPartialLessionInfoList).ToList();
@ -3198,7 +2769,7 @@ namespace IRaCIS.Core.Application.Service.Common
#endregion
#region 不管是list 还是taskList 最终处理的数据都是list 处理好数据后合并
if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.RECIST1Pointt1_MB
if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.RECIST1Pointt1_MB
|| criterion.CriterionType == CriterionType.IRECIST1Point1 || criterion.CriterionType == CriterionType.mRECISTHCC)
{
//针对1.1 整体肿瘤评估 有的两列要合并一列
@ -3318,14 +2889,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, OptionTypeEnum = t.OptionTypeEnum, QuestionName = t.TableName + "_" + t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode, Unit = t.Unit });
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 });
//有三部分组成 外层问题+ 固定列表格名称 + 动态的表格问题
dynamicLessionInfoList = item.QuestionAnswerList.Union(addLessionInfoList).Union(dynamicPartialLessionInfoList).ToList();
}
else
{
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 });
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId, QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode });
//两部分组成 外层问题+ 动态的表格问题
dynamicLessionInfoList = item.QuestionAnswerList.Union(dynamicPartialLessionInfoList).ToList();
@ -3372,7 +2943,7 @@ namespace IRaCIS.Core.Application.Service.Common
TranslateDicNameList = translateDicNameList
};
if (export_Template == StaticData.Export.ReadingLession_Export || export_Template == StaticData.Export.CommonJudgeReadingDetail_Export || export_Template== StaticData.Export.OCT_ReadingLession_Export)
if (export_Template == StaticData.Export.ReadingLession_Export || export_Template == StaticData.Export.CommonJudgeReadingDetail_Export)
{
dynamicColumnConfig.TempalteLastColumnIndex = 8;
}
@ -3391,7 +2962,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,
@ -3435,7 +3006,7 @@ namespace IRaCIS.Core.Application.Service.Common
var dynamicLessionInfoList = new List<CommonQuesionInfo>();
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 });
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId, QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode });
//两部分组成 外层问题+ 动态的表格问题
dynamicLessionInfoList = item.QuestionAnswerList.Union(dynamicPartialLessionInfoList).ToList();
@ -3648,7 +3219,7 @@ namespace IRaCIS.Core.Application.Service.Common
string fileName = "";
if (inQuery.ReadingExportType == ExportResult.NoneTumorCDISC)
if (inQuery.ReadingExportType == ExportResult.CDISC)
{
(memoryStream, fileName) = await ExcelExportHelper.DataExport_NpoiTestAsync(export_Template, exportInfo, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(CommonEvaluationExport), criterion.CriterionType, dynamicColumnConfig);

View File

@ -1,533 +0,0 @@
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Helper;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
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; }
public string QuestionValue { get; set; }
public string TranslateDicName { get; set; }
public ValueUnit? Unit { get; set; }
public ValueOfType? ValueType { get; set; }
}
public class TumorLessionInfo
{
public Guid Id { get; set; }
public Guid? OrganInfoId { get; set; }
//病灶编号
public string LessionCode { get; set; }
public LesionType? LessionType { get; set; }
public Guid? SplitRowId { get; set; }
public string? DicomModality { get; set; }
public string? NoneDicomModality { get; set; }
//病灶答案
public List<TumorLessionAnswerInfo> LessionAnswerList { get; set; }
}
public class TumorLessionAnswerInfo
{
public QuestionMark? QuestionMark { get; set; }
public OptionType OptionTypeEnum { get; set; }
//病灶Id
public Guid RowId { get; set; }
//如果是4 就取CustomUnit 否则就是字典翻译
[Comment("单位")]
public ValueUnit? Unit { get; set; }
public int ShowOrder { get; set; }
public Guid TableQuesionId { get; set; }
public string QuestionName { get; set; }
public string QuestionValue { get; set; }
public string TranslateDicName { get; set; }
}
public class TumorGlobalQuestionAnserInfo
{
[Comment("原任务ID")]
public Guid TaskId { get; set; }
public GlobalAnswerType GlobalAnswerType { get; set; }
[Comment("问题答案")]
public string Answer { get; set; } = string.Empty;
}
public class TumorExportBaseModel : TU_TR_RSBaseModel
{
public List<TumorLessionInfo> LesionList { get; set; } = new List<TumorLessionInfo>();
public List<TumorCommonQustionInfo> QuestionAnswerList { get; set; }
public List<TumorGlobalQuestionAnserInfo> GlobalResultList { get; set; }
#region 后续处理额外添加字段
public DateTime? JudgeSignTime { get; set; }
public Guid? SourceSubjectVisitId { get; set; }
public List<decimal> SubjectCriterionReadingPeriodVisitNumList { get; set; }
public decimal VisitTaskNum { get; set; }
public ReadingTaskState ReadingTaskState { get; set; }
public ReadingCategory ReadingCategory { get; set; }
//裁判结果选择的访视或者全局任务Id
public Arm? JudgeArmEnum { get; set; }
//在当前访视触发裁判,或者在截止日期小于等于当前访视的阅片期触发裁判
[DictionaryTranslateAttribute("YesOrNoAudit")]
public bool? IsTrigerJudge { get; set; }
//(如果是访视点裁判,则仅在所选阅片人对应访视 显示;如果是阅片期裁判,则在所选阅片人 阅片期内的所有访视 显示此原因)
public string JudgeNote { get; set; } = string.Empty;
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;
}
public class TU_TR_RSBaseModel
{
/// <summary>
/// 方案编号 STUDYID
/// </summary>
public string ResearchProgramNo { get; set; }
/// <summary>
/// 域 DOMAIN TU TR RS
/// </summary>
public string Domain { get; set; }
/// <summary>
/// 取值类型 TUSPID TRSPID RSSPID
/// </summary>
public string ValueType { get; set; }
/// <summary>
/// 受试者编号 USUBJID 实际展示TrialSiteSubjectCode
/// </summary>
public string SubjectCode { get; set; }
/// <summary>
/// 供应商 TUNAM (Extensive Imaging)
/// </summary>
public string Vendor { get; set; } = "Extensive Imaging";
/// <summary>
/// 阅片人 TUEVAL TREVAL RSEVAL
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 阅片人标识 TUEVALID TREVALID RSEVALID
/// </summary>
public Arm ArmEnum { get; set; }
/// <summary>
/// 访视编号 VISITNUM
/// </summary>
public decimal? VisitNum { get; set; }
/// <summary>
/// 访视名称 VISIT
/// </summary>
public string? VisitName { get; set; }
/// <summary>
/// 拍片日期 TUDTC TRDTC RSDTC
/// </summary>
public DateTime? LatestScanDate { get; set; }
public DateTime? EarliestScanDate { get; set; }
/// <summary>
/// eCRF标识 TUREFID TRREFID RSREFID
/// </summary>
public Guid VisitTaskId { get; set; }
#region 移动位置
/// <summary>
/// RSCAT 阅片标准
/// </summary>
public string CriterionName { get; set; }
/// <summary>
/// RSACPTFL 裁定标记 TUACPTFL
/// </summary>
//裁判选择标记
//根据裁判的任务结果 设置访视任务的这个字段 该字段表示 裁判认同该任务的结果
public bool? IsJudgeSelect { get; set; }
#endregion
public DateTime? SignTime { get; set; }
public string TaskName { get; set; }
#region 额外翻译字段
public string IsJudgeSelectStr => IsJudgeSelect == true ? "Y" : "";
public bool IsTargetPD { get; set; } = false;
public bool IsOverallResponsePD { get; set; } = false;
//TR表 靶病灶PD了访视层级的都是最早拍片日期 RS表 整体肿瘤评估PD了那么疗效评估的拍片日期都给最早的
public bool IsPD => Domain == "TR" ? IsTargetPD : IsOverallResponsePD;
public string ArmEnumStr { get; set; }
public string LatestScanDateStr
{
get
{
var date = IsPD ? EarliestScanDate : LatestScanDate;
return date?.ToString("yyyy-MM-dd") ?? "";
}
}
public string TrialSiteSubjectCode => ResearchProgramNo + SubjectCode;
#endregion
}
public class TU_Export : TU_TR_RSBaseModel
{
///// <summary>
///// 取值类型 TUSPID
///// </summary>
//public string TUValueType { get; set; }
/// <summary>
/// 序号 TUSEQ (同一个访视,所有阅片人选择病灶给个顺序号)
/// </summary>
public int No { get; set; }
/// <summary>
/// 链接ID TULNKID (阅片人角色_病灶编号)不同访视可以重复
/// </summary>
public string ARM_TumorNo { get; set; }
/// <summary>
/// 肿瘤识别简称 TUTESTCD
/// </summary>
public string TumorIdentificationSimple { get; set; }
/// <summary>
/// 肿瘤识别全称 TUTEST
/// </summary>
public string TumorIdentificationFullName { get; set; }
/// <summary>
/// 肿瘤鉴定结果 TUORRES
/// </summary>
public string TumorIdentificationResult { get; set; }
/// <summary>
/// 肿瘤识别结果类型 TUSTRESC
/// </summary>
public string TumorIdentificationResultType { get; set; }
/// <summary>
/// 部位 TULOC (对应病灶表的部位,需要国际化)
/// </summary>
public string BodyPart { get; set; }
/// <summary>
/// 鉴定方法 TUMETHOD (Modality?)
/// </summary>
public string IdentificationMethod { get; set; }
///// <summary>
///// 裁定标记 TUACPTFL
///// </summary>
//public bool? IsJudgeSelect { get; set; }
/// <summary>
/// 部位描述 LOCTEXT
/// </summary>
public string BodyPartDes { get; set; }
}
public class TR_Export : TU_TR_RSBaseModel
{
///// <summary>
///// 取值类型 TRSPID
///// </summary>
//public string TRValueType { get; set; }
/// <summary>
/// 每个subject 按照顺序编号 TRSEQ
/// </summary>
public int TRSEQ { get; set; }
/// <summary>
///TRGRPID 组ID 对应TU表肿瘤鉴定结果 TumorIdentificationResult
/// </summary>
public string TRGRPID { get; set; }
/// <summary>
/// TRLNKID 链接ID 对应TU表的链接ID TumorNo(阅片人角色_病灶编号)
/// </summary>
public string ARM_TumorNo { get; set; }
/// <summary>
///TRLNKGRP 链接组 ARM-任务名(访视名) 对应RS的链接组
/// </summary>
public string ARM_VisitName { get; set; }
/// <summary>
/// 肿瘤评估简称 TRTESTCD
/// </summary>
public string TumorAssessmentSimpleName { get; set; }
/// <summary>
/// 肿瘤评估全称 TRTEST
/// </summary>
public string TumorAssessmentFullName { get; set; }
/// <summary>
/// 原始测量 TRORRES
/// </summary>
public string OriginalMeasurements { get; set; }
/// <summary>
/// 原始单位 TRORRESU
/// </summary>
public string OriginalUnit { get; set; }
/// <summary>
/// 标准结果(字符) TRSTRESC
/// </summary>
public string StandardResult_Character => OriginalMeasurements;
/// <summary>
/// 标准结果(数值) TRORRESU
/// </summary>
public string StandardResult_Numeric => double.TryParse(OriginalMeasurements, out _) ||
(OriginalMeasurements?.EndsWith("%") == true &&
double.TryParse(OriginalMeasurements.TrimEnd('%'), out _))
? OriginalMeasurements
: "";
/// <summary>
/// 标准单位 TRSTRESU
/// </summary>
public string StandardUnit => OriginalUnit;
/// <summary>
/// 完成状态 TRSTAT
/// </summary>
public string CompletionStatus { get; set; }
/// <summary>
/// 完成状态 TRMETHOD
/// </summary>
public string IdentificationMethod { get; set; }
/// <summary>
/// 无法测量原因 TRREASND
/// </summary>
public string NotMeasuredReason { get; set; }
}
public class RS_Export : TU_TR_RSBaseModel
{
/// <summary>
/// RSSEQ 按照subject 的数据顺序编号
/// </summary>
public int RSSEQ { get; set; }
/// <summary>
/// RSLNKGRP 链接组 ARM_任务名(访视名)
/// </summary>
public string ARM_VisitName { get; set; }
/// <summary>
/// RSTESTCD 疗效评估简称
/// </summary>
public string EfficacyEvaluationSimpleName { get; set; }
/// <summary>
/// RSTEST 疗效评估全称
/// </summary>
public string EfficacyEvaluationName { get; set; }
/// <summary>
/// RSORRES 响应评估原始结果
/// </summary>
public string RespondEfficacyAssessment { get; set; }
/// <summary>
/// RSSTRESC 标准疗效评估
/// </summary>
public string StandardEfficacyAssessment => RespondEfficacyAssessment;
/// <summary>
/// RSSTAT 完成状态
/// </summary>
public string CompletionStatus { get; set; }
/// <summary>
/// RSREASND 无法评估原因
/// </summary>
public string NotAssessmentReason { get; set; }
///// <summary>
///// 裁定标记 RSACPTFL
///// </summary>
//public bool? IsJudgeSelect { get; set; }
/// <summary>
/// REASASM 评估原因
/// </summary>
public string AssessmentReason { get; set; }
/// <summary>
/// REASOVR 重新评估原因
/// </summary>
public string ReAssessmentReason { get; set; }
/// <summary>
/// REASUPD 更新评估原因
/// </summary>
public string UpdateAssessmentReason { get; set; }
[JsonIgnore]
public bool? IsOveralResponse { get; set; }
}
public class CO_Export : TU_TR_RSBaseModel
{
/// <summary>
/// 关联域 RS(访视点备注) 空:裁判选择原因
/// </summary>
public string RDOMAIN { get; set; }
/// <summary>
/// COSEQ 序号
/// </summary>
public int COSEQ { get; set; }
/// <summary>
/// IDVAR 标识变量 RSSEQ 空:裁判选择原因
/// </summary>
public string IdentificationVariable { get; set; }
/// <summary>
/// 标识 IDVARVAL RSSEQ具体的值 空:裁判选择原因
/// </summary>
public string Identification { get; set; }
/// <summary>
/// COREF 备注引用
/// </summary>
public string RemarksQuote { get; set; }
/// <summary>
/// 备注 COVAL
/// </summary>
public string Remarks { get; set; }
/// <summary>
/// 裁决日期 CODTC
/// </summary>
public string CODTC { get; set; }
}

View File

@ -1,23 +0,0 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-10-28 06:22:47Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using IRaCIS.Core.Infrastructure.Extention;
using System.Threading.Tasks;
using IRaCIS.Core.Application.ViewModel;
namespace IRaCIS.Core.Application.Interfaces;
public interface IEmailLogService
{
Task<PageOutput<EmailLogView>> GetEmailLogList(EmailLogQuery inQuery);
Task<IResponseOutput> AddOrUpdateEmailLog(EmailLogAddOrEdit addOrEditEmailLog);
Task<IResponseOutput> DeleteEmailLog(Guid emailLogId);
}

View File

@ -5,16 +5,11 @@ using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure;
using MailKit;
using MailKit.Net.Imap;
using MailKit.Search;
using MailKit.Security;
using Medallion.Threading;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using MimeKit;
using System.Net.Mail;
using System.Runtime.CompilerServices;
namespace IRaCIS.Core.Application.Service
@ -53,7 +48,7 @@ namespace IRaCIS.Core.Application.Service
public interface IMailVerificationService
{
Task AnolymousSendEmail(Guid trialId, string emailAddress, int verificationCode);
Task AnolymousSendEmail(string researchProgramNo, string emailAddress, int verificationCode);
Task SendEmailVerification(string emailAddress, int verificationCode);
@ -66,7 +61,7 @@ namespace IRaCIS.Core.Application.Service
Task AddUserSendEmailAsync(Guid userId, string baseUrl, string routeUrl);
Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456", string baseUrl="");
Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456");
Task SiteSurveyUserJoinEmail(Guid trialId, Guid userId, string userTypes, string baseUrl, string rootUrl);
@ -75,14 +70,8 @@ namespace IRaCIS.Core.Application.Service
Task<(Guid identityUserId, Guid userRoleId)> DoctorJoinTrialEmail(Guid trialId, Guid doctorId, string baseUrl, string rootUrl);
Task UserFeedBackMail(Guid feedBackId);
Task AfterUserModifyPasswordSendEmailAsync(Guid userId);
Task SiteSuervyCheckUser(Guid trialId, string email, string name);
Task SiteSuervyUpdateUser(Guid trialSiteId, string email, string name, string url);
}
[ApiExplorerSettings(GroupName = "Common")]
public class MailVerificationService(IRepository<VerificationCode> _verificationCodeRepository,
IRepository<SystemBasicData> _systemBasicDatarepository,
IRepository<VisitTask> _visitTaskRepository,
@ -97,14 +86,12 @@ namespace IRaCIS.Core.Application.Service
IRepository<UserType> _userTypeRepository,
IRepository<Doctor> _doctorTypeRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig,
IDistributedLockProvider _distributedLockProvider, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IMailVerificationService
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
private async Task<EmailNoticeConfig> GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario scenario, MimeMessage messageToSend,
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc)
@ -189,14 +176,6 @@ 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)
{
@ -284,13 +263,10 @@ namespace IRaCIS.Core.Application.Service
//中心调研 登陆 发送验证码
public async Task AnolymousSendEmail(Guid trialId, string emailAddress, int verificationCode)
public async Task AnolymousSendEmail(string researchProgramNo, 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));
@ -303,7 +279,7 @@ namespace IRaCIS.Core.Application.Service
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo);
var topicStr = string.Format(input.topicStr, companyName, researchProgramNo);
var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
@ -323,7 +299,7 @@ namespace IRaCIS.Core.Application.Service
var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo, sucessHandle);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle);
}
@ -354,7 +330,7 @@ namespace IRaCIS.Core.Application.Service
var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login"));
var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}";
var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@ -381,7 +357,7 @@ namespace IRaCIS.Core.Application.Service
}
//管理员重置密码发送邮件
public async Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456", string baseUrl = "")
public async Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456")
{
var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).Include(t => t.UserRoleList).ThenInclude(c => c.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException();
@ -404,9 +380,7 @@ namespace IRaCIS.Core.Application.Service
sysUserInfo.FullName,
sysUserInfo.UserName,
//string.Join(',', sysUserInfo.UserRoleList.Select(t => t.UserTypeRole.UserTypeShortName)),
pwdNotMd5,
baseUrl
pwdNotMd5
);
return (topicStr, htmlBodyStr);
@ -546,7 +520,8 @@ namespace IRaCIS.Core.Application.Service
await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.SiteUseOrExternalUserFirstrJoinTrial : EmailBusinessScenario.SiteUserOrExternalUserExistJoinTrial, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo, null);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, null);
}
@ -605,7 +580,8 @@ namespace IRaCIS.Core.Application.Service
await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.SiteUseOrExternalUserFirstrJoinTrial : EmailBusinessScenario.SiteUserOrExternalUserExistJoinTrial, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
@ -734,7 +710,8 @@ namespace IRaCIS.Core.Application.Service
await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.DoctorUserFirstJoinTrial : EmailBusinessScenario.DoctorUserExistJoinTrial, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo, null);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, null);
//创建账号 和创建角色 一条,更新的时候才记录更新角色
if (isNeedCreateNewUser == false)
@ -760,8 +737,6 @@ 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));
@ -896,122 +871,8 @@ namespace IRaCIS.Core.Application.Service
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SysFeedBack, messageToSend, emailConfigFunc);
}
if (trialinfo == null)
{
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
else
{
await SendEmailHelper.SendEmailAsync(messageToSend, trialinfo);
}
}
//用户修改密码发送邮件
public async Task AfterUserModifyPasswordSendEmailAsync(Guid userId)
{
var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).FirstOrDefaultAsync()).IfNullThrowException();
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
//收件地址
messageToSend.To.Add(new MailboxAddress(sysUserInfo.FullName, sysUserInfo.EMail));
//主题
//---[来自展影IRC] 关于重置邮箱的提醒
var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format(ReplacePlatformName(ReplaceCompanyName(input.htmlBodyStr)) ,
sysUserInfo.FullName
);
return (topicStr, htmlBodyStr);
};
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.IdentityUser_ModifyPassword, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
//中心调研核对人员提醒
public async Task SiteSuervyCheckUser(Guid trialId, string email, string name)
{
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).FirstOrDefaultAsync();
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
//收件地址
messageToSend.To.Add(new MailboxAddress(name, email));
//主题
//---[来自展影IRC] 关于重置邮箱的提醒
var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
name,
trialInfo.TrialCode,
trialInfo.ResearchProgramNo
);
return (topicStr, htmlBodyStr);
};
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SiteSurvey_CheckUser, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
}
public async Task SiteSuervyUpdateUser(Guid trialSiteId, string email, string name, string url)
{
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));
//收件地址
messageToSend.To.Add(new MailboxAddress(name, email));
//主题
//---[来自展影IRC] 关于重置邮箱的提醒
var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
name,
trialInfo.TrialCode,
siteInfo.TrialSiteCode,
siteInfo.TrialSiteName,
url
);
return (topicStr, htmlBodyStr);
};
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SiteSurvey_UpdateUser, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, trialInfo);
}
}
}

View File

@ -2,7 +2,6 @@
using IRaCIS.Application.Contracts;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.Service.Common;
using IRaCIS.Core.Application.ViewModel;
namespace IRaCIS.Core.Application.Service
@ -11,18 +10,7 @@ namespace IRaCIS.Core.Application.Service
{
public CommonConfig()
{
// 在此处拷贝automapper 映射
CreateMap<EmailAttachmentLog, EmaliAttachmentInfo>();
CreateMap<EmailRecipientLog, EmailRecipientLogView>();
CreateMap<EmailLog, EmailLogView>()
.ForMember(t => t.RecipientList, u => u.MapFrom(c => c.EmailRecipientLogList))
.ForMember(t => t.AttachmentList, u => u.MapFrom(c => c.AttachmentList));
CreateMap<EmailLog, GetEmailInfoOutDto>()
.ForMember(t => t.RecipientList, u => u.MapFrom(c => c.EmailRecipientLogList))
.ForMember(t => t.AttachmentList, u => u.MapFrom(c => c.AttachmentList));
CreateMap<EmailLog, EmailLogAddOrEdit>().ReverseMap();
CreateMap<FrontAuditConfig, FrontAuditConfigAddOrEdit>().ReverseMap();
@ -116,14 +104,6 @@ namespace IRaCIS.Core.Application.Service
.ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.Subject.TrialSite.TrialSiteCode));
CreateMap<TumorExportBaseModel , TU_Export>();
CreateMap<TumorExportBaseModel, TR_Export>();
CreateMap<TumorExportBaseModel, RS_Export>();
CreateMap<TumorExportBaseModel, CO_Export>();
CreateMap<IVUS_OCTBaseDto, IvusExportDto>();
CreateMap<IVUS_OCTBaseDto, OctExportDto>();
}
}

View File

@ -183,7 +183,6 @@ namespace IRaCIS.Application.Contracts
public class SelectionReviewerDTO : DoctorDTO
{
public bool IsVacation { get; set; }
public int DoctorTrialState { get; set; }
public bool IsEnroll { get; set; } = false;

View File

@ -133,29 +133,6 @@ 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>

View File

@ -131,7 +131,6 @@ 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));

View File

@ -97,7 +97,6 @@ public class AuditDocumentService(IRepository<AuditDocument> _auditDocumentRepos
.WhereIf(inQuery.IdentityUserName.IsNotNullOrEmpty(), t => t.AuditRecordIdentityUserList.Any(c => c.IdentityUser.UserName.Contains(inQuery.IdentityUserName) || c.IdentityUser.FullName.Contains(inQuery.IdentityUserName)))
.WhereIf(inQuery.CompanyName.IsNotNullOrEmpty(), t => t.CompanyName.Contains(inQuery.CompanyName))
.WhereIf(inQuery.AuditContent.IsNotNullOrEmpty(), t => t.AuditContent.Contains(inQuery.AuditContent))
.WhereIf(inQuery.IsViewTrainingRecord != null, t => t.IsViewTrainingRecord == inQuery.IsViewTrainingRecord)
.ProjectTo<AuditRecordView>(_mapper.ConfigurationProvider);
var pageList = await auditRecordQueryable.ToPagedListAsync(inQuery);
@ -668,7 +667,7 @@ public class AuditDocumentService(IRepository<AuditDocument> _auditDocumentRepos
#endregion
// 不能动到自己父类这个文件夹
// 不能动到自己父类这个文件夹
if (await _auditDocumentRepository.AnyAsync(x => x.ParentId == inDto.ParentId && inDto.Ids.Contains(x.Id)))
{
throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotMoveToParent"]);
@ -694,7 +693,7 @@ public class AuditDocumentService(IRepository<AuditDocument> _auditDocumentRepos
public async Task<IResponseOutput> CopyFileOrFolder(MovieFileOrFolderInDto inDto)
{
foreach (var item in inDto.Ids)
{

View File

@ -49,8 +49,6 @@ public class AuditRecordAddOrEdit
public AuditType AuditType { get; set; }
public bool IsViewTrainingRecord { get; set; }
//public List<Guid> IdnetityUserIdList { get; set; }
}
@ -87,8 +85,6 @@ public class AuditRecordQuery : PageInput
public DateTime? EndCreateTime { get; set; }
public string? IdentityUserName { get; set; }
public bool? IsViewTrainingRecord { get; set; }
}
public class SetAuditRecordPermissionCommand

View File

@ -92,16 +92,11 @@ 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; }

View File

@ -43,10 +43,6 @@ namespace IRaCIS.Core.Application.Contracts
[NotDefault]
public Guid TrialId { get; set; }
public bool? IsPublish { get; set; }
public string? FileTypeCode { get; set; }
}
///<summary> TrialDocumentAddOrEdit 列表查询参数模型</summary>

View File

@ -30,7 +30,6 @@ 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; }
}
@ -98,8 +97,6 @@ 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; }
@ -128,7 +125,7 @@ namespace IRaCIS.Core.Application.ViewModel
{
public Guid SubjectId { get; set; }
public CriterionType CriterionType { get; set; }
public Guid TrialReadingCriterionId { get; set; }
public EmailBusinessScenario BusinessScenarioEnum { get; set; }
}

View File

@ -1,77 +0,0 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-10-20 06:17:15Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using IRaCIS.Core.Domain.Share;
using System.Collections.Generic;
namespace IRaCIS.Core.Application.Service.DTO;
public class UserAgreementView : UserAgreementAddOrEdit
{
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
}
public class UserAgreementAddOrEdit
{
public Guid? Id { get; set; }
public DateTime EffectiveDate { get; set; }
public string FileContent { get; set; }
public string FileEnContent { get; set; }
public string FileName { get; set; }
public string FileVersion { get; set; }
public bool IsCurrentVersion { get; set; }
public DateTime UpdateDate { get; set; }
public UserAgreementType UserAgreementTypeEnum { get; set; }
}
public class SetUserAgreementCurrentVersionInDto
{
public Guid Id { get; set; }
}
public class GetUserAgreementByIdInDto
{
public Guid Id { get; set; }
}
public class UserAgreementQuery:PageInput
{
public DateTime? StartEffectiveDate { get; set; }
public DateTime? EndEffectiveDate { get; set; }
public string? FileContent { get; set; }
public string? FileName { get; set; }
public string? FileVersion { get; set; }
public bool? IsCurrentVersion { get; set; }
public DateTime? StartUpdateDate { get; set; }
public DateTime? EndUpdateDate { get; set; }
public UserAgreementType? UserAgreementTypeEnum { get; set; }
}

View File

@ -29,7 +29,6 @@ namespace IRaCIS.Core.Application.Service
//入组确认/PD确认
public async Task SendEnrollOrPdEmail(Guid visitTaskId, bool? isEnrollment, bool? isPDConfirm)
{
EmailBusinessScenario businessScenarioEnum;
@ -110,8 +109,8 @@ namespace IRaCIS.Core.Application.Service
if (sendEmailConfig != null)
{
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).FirstOrDefaultAsync();
await SendEmailHelper.SendEmailAsync(sendEmailConfig, trialInfo);
await SendEmailHelper.SendEmailAsync(sendEmailConfig);
}
}

View File

@ -19,8 +19,6 @@ 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);

View File

@ -1,23 +0,0 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-10-20 06:17:15Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using IRaCIS.Core.Infrastructure.Extention;
using System.Threading.Tasks;
using IRaCIS.Core.Application.Service.DTO;
namespace IRaCIS.Core.Application.Service.Interface;
public interface IUserAgreementService
{
Task<PageOutput<UserAgreementView>> GetUserAgreementList(UserAgreementQuery inQuery);
Task<IResponseOutput> AddOrUpdateUserAgreement(UserAgreementAddOrEdit addOrEditUserAgreement);
Task<IResponseOutput> DeleteUserAgreement(Guid userAgreementId);
}

View File

@ -208,7 +208,6 @@ namespace IRaCIS.Core.Application.Services
await _systemDocumentRepository.BatchUpdateNoTrackingAsync(x => inDto.Ids.Contains(x.Id), x => new SystemDocument()
{
IsPublish = true,
PublishDate= DateTime.Now,
IsDeleted = false,
});
@ -287,7 +286,7 @@ namespace IRaCIS.Core.Application.Services
var isInternal = _userInfo.IsZhiZhun;
var query = from sysDoc in _systemDocumentRepository.Where(t=>t.IsPublish)
var query = from sysDoc in _systemDocumentRepository.AsQueryable(true)
.Where(t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId))
.WhereIf(!string.IsNullOrEmpty(inQuery.Name), t => t.Name.Contains(inQuery.Name))
//外部人员 只签署 外部需要签署的
@ -302,7 +301,6 @@ 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,
@ -328,7 +326,6 @@ namespace IRaCIS.Core.Application.Services
//UserTypeShortName = user.UserTypeRole.UserTypeShortName
};
var list = await query
//过滤掉删除的,并且没有签名的
.Where(t => !(t.IsDeleted == true && t.ConfirmTime == null))

View File

@ -27,7 +27,6 @@ namespace IRaCIS.Core.Application.Services
[ApiExplorerSettings(GroupName = "Trial")]
public class TrialDocumentService(IRepository<TrialDocument> _trialDocumentRepository,
IRepository<Trial> _trialRepository,
IRepository<AuditRecord> _auditRecordRepository,
IRepository<TrialDocumentAttachment> _trialDocumentAttachmentRepository,
ISystemDocumentService _systemDocumentService,
IRepository<SystemDocConfirmedIdentityUser> _systemDocConfirmedUserRepository,
@ -54,9 +53,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,9 +100,8 @@ 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("开始 发布项目文档");
@ -123,7 +121,7 @@ namespace IRaCIS.Core.Application.Services
}
/// <summary>
/// 测试邮件定时发送
/// 测试定时发送
/// </summary>
/// <returns></returns>
public async Task<IResponseOutput> TestPush()
@ -141,22 +139,6 @@ 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>
@ -169,10 +151,8 @@ namespace IRaCIS.Core.Application.Services
var trialDocumentQueryable = _trialDocumentRepository.AsQueryable(true).Where(t => t.TrialId == inQuery.TrialId)
.WhereIf(!string.IsNullOrEmpty(inQuery.Name), t => t.Name.Contains(inQuery.Name))
.WhereIf(inQuery.FileTypeId != null, t => t.FileTypeId == inQuery.FileTypeId)
.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(inQuery.UserTypeId != null, t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == inQuery.UserTypeId))
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted)
.ProjectTo<TrialDocumentView>(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken, isEn_Us = _userInfo.IsEn_Us });
return await trialDocumentQueryable.ToPagedListAsync(inQuery);
@ -181,7 +161,7 @@ namespace IRaCIS.Core.Application.Services
[HttpPost]
public async Task<PageOutput<TrialSignDocView>> GetTrialSignDocumentList(TrialDocQuery inQuery)
{
var trialDocQueryable = from trialDoc in _trialDocumentRepository.Where(t => t.IsPublish)
var trialDocQueryable = from trialDoc in _trialDocumentRepository.AsQueryable(true)
.WhereIf(inQuery.TrialId != null, t => t.TrialId == inQuery.TrialId)
.Where(t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId))
@ -354,7 +334,7 @@ namespace IRaCIS.Core.Application.Services
#region 统一用户修改
var systemDocQuery =
from sysDoc in _systemDocumentRepository.Where(t => t.IsPublish).Where(t => t.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == _userInfo.UserTypeId))
from sysDoc in _systemDocumentRepository.AsQueryable(true).Where(t => t.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == _userInfo.UserTypeId))
//外部人员 只签署 外部需要签署的
.WhereIf(isInternal == false, t => t.DocUserSignType == DocUserSignType.InnerAndOuter)
from trialUser in _trialIdentityUserRepository.AsQueryable(false)
@ -393,7 +373,7 @@ namespace IRaCIS.Core.Application.Services
//项目文档查询
var trialDocQuery =
from trialDoc in _trialDocumentRepository.Where(t => t.IsPublish).Where(t => t.TrialId == inQuery.TrialId).Where(t => t.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == _userInfo.UserTypeId))
from trialDoc in _trialDocumentRepository.AsQueryable(true).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)))
@ -583,14 +563,13 @@ namespace IRaCIS.Core.Application.Services
#endregion
var needSignTrialDocCount = await _trialDocumentRepository.AsQueryable(true).Where(t => t.IsPublish)
.Where(t => t.TrialId == inQuery.TrialId && t.Trial.TrialStatusStr != StaticData.TrialState.TrialStopped)
var needSignTrialDocCount = await _trialDocumentRepository.AsQueryable(true).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).Where(t => t.IsPublish)
var needSignSystemDocCount = await _systemDocumentRepository.AsQueryable(true)
.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();
@ -623,7 +602,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.Where(t => t.TrialId == inQuery.TrialId && t.IsPublish)
from trialDoc in _trialDocumentRepository.AsQueryable(false).Where(t => t.TrialId == inQuery.TrialId)
.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))
@ -714,8 +693,7 @@ 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(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.EA, t => t.ConfirmTime != null);
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted);
var result = await unionQuery.ToPagedListAsync(inQuery);
@ -925,12 +903,6 @@ 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();
@ -946,19 +918,10 @@ namespace IRaCIS.Core.Application.Services
var isEA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.EA;
//EA 但是没有在进行的培训记录查看权限,那么返回空数据
if (isEA && !_auditRecordRepository.Any(t => t.IsViewTrainingRecord && t.AuditState == AuditState.Ongoing && t.AuditRecordIdentityUserList.Any(c => c.IdentityUserId == _userInfo.IdentityUserId)))
{
return ResponseOutput.Ok(new PageOutput<UnionDocumentWithConfirmInfoView>());
}
var systemDocQuery =
from sysDoc in _systemDocumentRepository.Where(t => t.IsPublish)
from sysDoc in _systemDocumentRepository.AsQueryable(false)
.Where(t => inQuery.UserTypeId != null ? t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == inQuery.UserTypeId) : true)
from identityUser in _identityUserRepository.AsQueryable(false)
.Where(t => t.Status == UserStateEnum.Enable && t.UserRoleList.Where(t => t.IsUserRoleDisabled == false).Any(t => sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
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 只能查看内部人员文档
@ -967,9 +930,6 @@ 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,
@ -1001,16 +961,14 @@ 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); //不是内部的人,看有签名时间的
@ -1102,7 +1060,6 @@ 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());
@ -1115,7 +1072,7 @@ namespace IRaCIS.Core.Application.Services
return ResponseOutput.NotOk(_localizer["TrialD_DuplicateFileInProject"]);
}
var document = (await _trialDocumentRepository.Where(t => t.Id == addOrEditTrialDocument.Id, true,true).Include(t => t.NeedConfirmedUserTypeList).FirstOrDefaultAsync()).IfNullThrowException();
var document = (await _trialDocumentRepository.Where(t => t.Id == addOrEditTrialDocument.Id, true).Include(t => t.NeedConfirmedUserTypeList).FirstOrDefaultAsync()).IfNullThrowException();
bool beforeIsPublish = document.IsPublish;
bool beforeIsDeleted = document.IsDeleted;
@ -1123,7 +1080,6 @@ namespace IRaCIS.Core.Application.Services
_mapper.Map(addOrEditTrialDocument, document);
document.UpdateTime = DateTime.Now;
#region 不区分路径了
//if (document.FileTypeId != addOrEditTrialDocument.FileTypeId)

View File

@ -13,7 +13,6 @@ 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;
@ -618,8 +617,8 @@ namespace IRaCIS.Core.Application.Service
FileStream = File.OpenRead(phyPath),
});
var trialInfo = await _trialRepository.Where(t => t.Id == taskInfo.TrialId).FirstOrDefaultAsync();
await SendEmailHelper.SendEmailAsync(sendEmailConfig, trialInfo);
await SendEmailHelper.SendEmailAsync(sendEmailConfig);
return string.Empty;
}
@ -734,8 +733,8 @@ namespace IRaCIS.Core.Application.Service
FileStream = pdfMemoryStream
});
var trialInfo = await _trialRepository.Where(t => t.Id == taskInfo.TrialId).FirstOrDefaultAsync();
await SendEmailHelper.SendEmailAsync(sendEmailConfig, trialInfo);
await SendEmailHelper.SendEmailAsync(sendEmailConfig);
}
@ -773,12 +772,10 @@ namespace IRaCIS.Core.Application.Service
{
var subjectId = generateEmailCommand.SubjectId;
var businessScenarioEnum = generateEmailCommand.BusinessScenarioEnum;
var criterionType = generateEmailCommand.CriterionType;
var trialReadingCriterionId = generateEmailCommand.TrialReadingCriterionId;
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();
var trialConfig = await _subjectRepository.Where(t => t.Id == subjectId).Select(t => new { t.Trial.IsEnrollementQualificationConfirm, t.Trial.IsPDProgressView }).FirstNotNullAsync();
//找到入组确认 或者Pd 进展 已生成任务的 访视
var subjectVisitList = await _subjectVisitRepository.Where(t => t.SubjectId == subjectId & t.CheckState == CheckStateEnum.CVPassed && (t.IsEnrollmentConfirm == true || t.PDState == PDStateEnum.PDProgress)).ToListAsync();
@ -1673,10 +1670,9 @@ 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, TrialCriterionTypeList = t.TrialReadingCriterionList.Where(t => t.IsSigned).Select(t => t.CriterionType).ToList() }).First();
var trialConfig = _trialRepository.Where(t => t.Id == inQuery.TrialId).Select(t => new { t.IsEnrollementQualificationConfirm, t.IsPDProgressView }).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))
@ -1694,7 +1690,6 @@ 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);
}
@ -1768,22 +1763,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();
}

View File

@ -1,131 +0,0 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-10-20 06:17:11Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using IRaCIS.Core.Domain.Models;
using Microsoft.AspNetCore.Mvc;
using IRaCIS.Core.Infrastructure.Extention;
using System.Threading.Tasks;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Application.Service.Interface;
using IRaCIS.Core.Application.Service.DTO;
using Microsoft.AspNetCore.Authorization;
namespace IRaCIS.Core.Application.Service;
/// <summary>
/// 用户协议和隐私采集服务
/// </summary>
/// <param name="_userAgreementRepository"></param>
/// <param name="_mapper"></param>
/// <param name="_userInfo"></param>
/// <param name="_localizer"></param>
[ApiExplorerSettings(GroupName = "FileRecord")]
public class UserAgreementService(IRepository<UserAgreement> _userAgreementRepository,
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer): BaseService, IUserAgreementService
{
/// <summary>
/// 获取用户协议和隐私采集列表
/// </summary>
/// <param name="inQuery"></param>
/// <returns></returns>
[HttpPost]
public async Task<PageOutput<UserAgreementView>> GetUserAgreementList(UserAgreementQuery inQuery)
{
var userAgreementQueryable =_userAgreementRepository
.WhereIf(inQuery.StartEffectiveDate.HasValue, t => t.EffectiveDate >= inQuery.StartEffectiveDate.Value)
.WhereIf(inQuery.EndEffectiveDate.HasValue, t => t.EffectiveDate <= inQuery.EndEffectiveDate.Value)
.WhereIf(inQuery.FileContent != null, t => t.FileContent.Contains(inQuery.FileContent))
.WhereIf(inQuery.FileName != null, t => t.FileName.Contains(inQuery.FileName))
.WhereIf(inQuery.FileVersion != null, t => t.FileVersion.Contains(inQuery.FileVersion))
.WhereIf(inQuery.IsCurrentVersion != null, t => t.IsCurrentVersion==inQuery.IsCurrentVersion)
.WhereIf(inQuery.UserAgreementTypeEnum != null, t => t.UserAgreementTypeEnum == inQuery.UserAgreementTypeEnum)
.WhereIf(inQuery.StartUpdateDate.HasValue, t => t.UpdateDate >= inQuery.StartUpdateDate.Value)
.WhereIf(inQuery.EndUpdateDate.HasValue, t => t.UpdateDate <= inQuery.EndUpdateDate.Value)
.ProjectTo<UserAgreementView>(_mapper.ConfigurationProvider);
var pageList= await userAgreementQueryable.ToPagedListAsync(inQuery);
return pageList;
}
/// <summary>
/// 获取当前版本的用户协议和隐私采集
/// </summary>
/// <returns></returns>
[AllowAnonymous]
[HttpPost]
public async Task<List<UserAgreementView>> GetCurrentVersionUserAgreements()
{
var userAgreementList = await _userAgreementRepository.Where(x=>x.IsCurrentVersion)
.ProjectTo<UserAgreementView>(_mapper.ConfigurationProvider)
.ToListAsync();
return userAgreementList;
}
/// <summary>
/// 根据Id获取用户协议和隐私采集
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost]
public async Task<UserAgreementView> GetUserAgreementById(GetUserAgreementByIdInDto inDto)
{
var userAgreement = await _userAgreementRepository.Where(x => x.Id == inDto.Id)
.ProjectTo<UserAgreementView>(_mapper.ConfigurationProvider)
.FirstNotNullAsync();
return userAgreement;
}
/// <summary>
/// 设置为当前版本
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> SetCurrentVersion(SetUserAgreementCurrentVersionInDto inDto)
{
var userAgreement=await _userAgreementRepository.Where(x=>x.Id==inDto.Id).FirstNotNullAsync();
await _userAgreementRepository.UpdatePartialFromQueryAsync(
x => x.UserAgreementTypeEnum== userAgreement.UserAgreementTypeEnum&&x.IsCurrentVersion,
x => new UserAgreement { IsCurrentVersion = false },true);
await _userAgreementRepository.UpdatePartialFromQueryAsync(
inDto.Id,
x => new UserAgreement { IsCurrentVersion = true },true);
return ResponseOutput.Ok();
}
/// <summary>
/// 新增或者修改用户协议和隐私采集
/// </summary>
/// <param name="addOrEditUserAgreement"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> AddOrUpdateUserAgreement(UserAgreementAddOrEdit addOrEditUserAgreement)
{
var entity = await _userAgreementRepository.InsertOrUpdateAsync(addOrEditUserAgreement, true);
return ResponseOutput.Ok(entity.Id.ToString());
}
/// <summary>
/// 删除用户协议和隐私采集
/// </summary>
/// <param name="userAgreementId"></param>
/// <returns></returns>
[HttpDelete("{userAgreementId:guid}")]
public async Task<IResponseOutput> DeleteUserAgreement(Guid userAgreementId)
{
var success = await _userAgreementRepository.DeleteFromQueryAsync(t => t.Id == userAgreementId,true);
return ResponseOutput.Ok();
}
}

View File

@ -16,11 +16,6 @@ namespace IRaCIS.Core.Application.Service
var userId = Guid.Empty;
var isEn_Us = false;
// 在此处拷贝automapper 映射
CreateMap<UserAgreement, UserAgreementView>();
CreateMap<UserAgreement, UserAgreementAddOrEdit>().ReverseMap();
// 在此处拷贝automapper 映射
CreateMap<TrialDocumentAttachment, TrialDocumentAttachmentView>();
CreateMap<TrialDocumentAttachment, TrialDocumentAttachmentAddOrEdit>().ReverseMap();

View File

@ -117,10 +117,4 @@ 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; }
}
}

View File

@ -1,5 +1,4 @@
using IRaCIS.Core.Domain.Share;
using Newtonsoft.Json;
namespace IRaCIS.Core.Application.Service.ImageAndDoc.DTO
{
@ -28,9 +27,6 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc.DTO
public List<StudyBasicInfo> UploadStudyList { get; set; }
[JsonIgnore]
public List<StudyBasicInfo> OriginalTaskStudyList { get; set; }
}
@ -120,8 +116,6 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc.DTO
public int ReadingSeriesCount { get; set; }
public int ReadingInstanceCount { get; set; }
public Guid? SubjectVisitId { get; set; }
}
public class NoneDicomStudyBasicInfo
@ -146,10 +140,5 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc.DTO
public int ReadingFileCount { get; set; }
}
public class ImageMarkNoneDicomStudyBasicInfo: NoneDicomStudyBasicInfo
{
public Guid SubjectVisitId { get; set; }
}
}

View File

@ -134,8 +134,6 @@ namespace IRaCIS.Core.Application.Contracts
public string Bodypart { get; set; } = string.Empty;
public string BodyPartForEditOther { get; set; } = string.Empty;
public DateTime? StudyTime { get; set; }
@ -190,7 +188,6 @@ namespace IRaCIS.Core.Application.Contracts
public Guid SubjectVisitId { get; set; }
public int FileCount { get; set; }
}
public class PreArchiveDicomStudyCommand
@ -240,9 +237,6 @@ namespace IRaCIS.Core.Application.Contracts
public Guid? VisitTaskId { get; set; }
public bool? IsImageSegmentLabel { get; set; }
}
public class TaskStudyArchiveConfirmResult
@ -271,9 +265,8 @@ namespace IRaCIS.Core.Application.Contracts
public AddOrUpdateStudyDto Study { get; set; }
public Guid? VisitTaskId { get; set; }
public bool? IsImageSegmentLabel { get; set; }
[NotDefault]
public Guid VisitTaskId { get; set; }
}
public class NewArchiveStudyCommand
@ -331,26 +324,13 @@ namespace IRaCIS.Core.Application.Contracts
public string BodyPartExamined { get; set; } = string.Empty;
public string DicomStudyDate { get; set; } = string.Empty;
public string DicomStudyDate { get; set; }
public string DicomStudyTime { get; set; } = string.Empty;
public string DicomStudyTime { get; set; }
public List<AddOrUpdateSeriesDto> SeriesList { get; set; }
#region 模态支持增加字段
public string Manufacturer { get; set; } = string.Empty;
public string ManufacturerModelName { get; set; } = string.Empty;
public string DeviceSerialNumber { get; set; } = string.Empty;
public string DeviceUID { get; set; } = string.Empty;
public string SoftwareVersions { get; set; } = string.Empty;
public string PatientWeight { get; set; } = string.Empty;
#endregion
}
@ -378,22 +358,14 @@ namespace IRaCIS.Core.Application.Contracts
public string ImageResizePath { get; set; } = string.Empty;
public string DicomSeriesDate { get; set; } = string.Empty;
public string DicomSeriesDate { get; set; }
public string DicomSeriesTime { get; set; } = string.Empty;
public string DicomSeriesTime { get; set; }
public List<AddInstanceDto> InstanceList { get; set; }
public Guid? VisitTaskId { get; set; }
#region 模态增加
public string RadiopharmaceuticalInformationSequence { get; set; } = string.Empty;
public string AcquisitionDate { get; set; } = string.Empty;
#endregion
}
@ -426,46 +398,15 @@ namespace IRaCIS.Core.Application.Contracts
public long FileSize { get; set; }
public string SOPClassUID { get; set; } = string.Empty;
public string SOPClassUID { get; set; }
public string MediaStorageSOPClassUID { get; set; } = string.Empty;
public string MediaStorageSOPClassUID { get; set; }
public string TransferSytaxUID { get; set; } = string.Empty;
public string TransferSytaxUID { get; set; }
public string MediaStorageSOPInstanceUID { get; set; } = string.Empty;
public string MediaStorageSOPInstanceUID { get; set; }
public bool IsEncapsulated => !string.IsNullOrEmpty(TransferSytaxUID) ? DicomTransferSyntax.Lookup(DicomUID.Parse(TransferSytaxUID)).IsEncapsulated : false;
#region 模态支持增加字段
public string PhotometricInterpretation { get; set; } = string.Empty;
public int BitsAllocated { get; set; }
public string PixelRepresentation { get; set; } = string.Empty;
public string RescaleIntercept { get; set; } = string.Empty;
public string RescaleSlope { get; set; } = string.Empty;
public string ImagePositionPatient { get; set; } = string.Empty;
public string ImageOrientationPatient { get; set; } = string.Empty;
//可能大
public string SequenceOfUltrasoundRegions { get; set; } = string.Empty;
public string FrameTime { get; set; } = string.Empty;
public string CorrectedImage { get; set; } = string.Empty;
public string Units { get; set; } = string.Empty;
public string DecayCorrection { get; set; } = string.Empty;
public string EncapsulatedDocument { get; set; } = string.Empty;
#endregion
public bool IsEncapsulated => DicomTransferSyntax.Lookup(DicomUID.Parse(TransferSytaxUID)).IsEncapsulated;
}
@ -499,17 +440,12 @@ namespace IRaCIS.Core.Application.Contracts
public string? SubjectCode { get; set; }
public Guid? VisitTaskId { get; set; }
public bool? IsImageSegmentLabel { get; set; }
public Guid? SubjectVisitId { get; set; }
}
public class IRTaskUploadedDicomStudyQuery
{
public Guid? VisitTaskId { get; set; }
public Guid? SubjectVisitId { get; set; }
[NotDefault]
public Guid VisitTaskId { get; set; }
}
public class IRUploadTaskDicomStudyDto : DicomStudyBasicInfo
@ -583,8 +519,6 @@ namespace IRaCIS.Core.Application.Contracts
public string TaskBlindName { get; set; }
public string TaskName { get; set; }
public string VisitName { get; set; }
public Guid? SourceSubjectVisitId { get; set; }
public ReadingTaskState ReadingTaskState { get; set; }
@ -602,15 +536,11 @@ namespace IRaCIS.Core.Application.Contracts
public string TaskBlindName { get; set; }
public Guid VisitId { get; set; }
public List<DownloadDicomStudyDto> StudyList { get; set; }
[JsonIgnore]
public List<DownloadDicomStudyDto> TaskStudyList { get; set; }
public List<DownloadNoneDicomStudyDto> NoneDicomStudyList { get; set; }
public List<DownloadNoneDicomStudyDto> NoneDicomStudyList { get; set; }
}
public class DownloadDicomStudyDto
@ -621,14 +551,14 @@ namespace IRaCIS.Core.Application.Contracts
public string StudyInstanceUid { get; set; }
public string StudyDIRPath { get; set; }
public List<DownloadDicomSeriesDto> SeriesList { get; set; }
public List<DownloadDicomSeriesDto> SeriesList { get; set; }
}
public class DownloadDicomSeriesDto
{
public string Modality { get; set; }
public List<DownloadDicomInstanceDto> InstanceList { get; set; }
public List<DownloadDicomInstanceDto> InstanceList { get; set; }
}
public class DownloadDicomInstanceDto
@ -679,8 +609,6 @@ namespace IRaCIS.Core.Application.Contracts
public string? SubjectCode { get; set; }
public Guid? VisitTaskId { get; set; }
public bool? IsImageSegmentLabel { get; set; }
}
@ -701,8 +629,6 @@ namespace IRaCIS.Core.Application.Contracts
public List<Guid> NoneDicomStudyIdList { get; set; }
public bool? IsImageSegmentLabel { get; set; }
}
public class TrialKeyImageExportDTO
@ -809,7 +735,7 @@ namespace IRaCIS.Core.Application.Contracts
? $"{TotalReadingImageSize.Value / 1024d / 1024d:F3} MB"
: "0.000 MB";
public string ImageTypeStr => $"{(IsHaveDicom ? "DICOM" : "")}{(IsHaveNoneDicom && IsHaveDicom ? " , " : "")}{(IsHaveNoneDicom ? "Non-DICOM" : "")}";
public string ImageTypeStr => $"{(IsHaveDicom ? "DICOM" : "")}{(IsHaveNoneDicom&&IsHaveDicom?" , ":"")}{(IsHaveNoneDicom ? "Non-DICOM" : "")}";
public bool IsHaveDicom { get; set; }
@ -952,90 +878,4 @@ namespace IRaCIS.Core.Application.Contracts
public Guid TaskId { get; set; }
}
public class SubjectVisitMarkQuery:PageInput
{
public Guid TrialId { get; set; }
public Guid? SubjectId { get; set; }
public Guid? TrialReadingCriterionId { get; set; }
public string? SubjectCode { get; set; }
public bool? IsUrgent { get; set; }
}
public class SubjectVisitMarkDTO
{
public Guid TrialId { get; set; }
public Guid SubjectId { get; set; }
public bool IsUrgent { get; set; }
public string SubjectCode { get; set; }
public int? VisitCount { get; set; }
public int? MarkIVUSVisitCount { get; set; }
public int? MarkOCTVisitCount { get; set; }
public int? DicomStudyCount { get; set; }
public int? MarkDicomStudyCount { get; set; }
public int? NoneDicomStudyCount { get; set; }
public int? MarkNoneDicomStudyCount { get; set; }
}
public class SubjectVisitMarkStudyDto
{
public bool IsDicom => DicomStudyList.Count() > 0;
public Guid VisitTaskId { get; set; }
public Guid SubjectId { get; set; }
public string SubjectCode { get; set; }
public string VisitName { get; set; }
public Guid SourceSubjectVisitId { get; set; }
public List<DicomStudyBasicInfo> DicomStudyList { get; set; } = new List<DicomStudyBasicInfo>();
public List<NoneDicomStudyBasicInfo> NoneDicomStudyList { get; set; }
}
public class SubjectVisitMarkUploadDto
{
public Guid SubjectVisitId { get; set; }
public Guid TrialSiteId { get; set; }
public Guid SubejctId { get; set; }
public string SubjectCode { get; set; }
public string VisitName { get; set; }
public Guid? SourceSubjectVisitId => SubjectVisitId;
//防止前端null 故意返回
public Guid VisitTaskId { get; set; }
public List<StudyBasicInfo> OrginalStudyList { get; set; }
public List<StudyBasicInfo> UploadStudyList { get; set; }
}
}

View File

@ -3,9 +3,7 @@
// 生成时间 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;
@ -36,8 +34,7 @@ namespace IRaCIS.Core.Application.Contracts
[FromQuery] Guid? nonedicomStudyId,
[FromQuery] bool isFilterZip,
[FromQuery] Guid? visitTaskId,
[FromQuery] bool isReading,
[FromQuery] bool? isImageSegmentLabel)
[FromQuery] bool isReading)
{
var qcAuditState = await _subjectVisitRepository.Where(s => s.Id == subjectVisitId).Select(t => t.AuditState).FirstOrDefaultAsync();
@ -47,74 +44,33 @@ namespace IRaCIS.Core.Application.Contracts
//质控过程中并且不是IQC时可以看到删除的不需要忽略过滤器 质控中iqc 也需要看到删除的
var isViewDelete = !isQCFinished;
var isFilterIVUSNoneDicom = false;
CriterionType? criterionType = null;
IQueryable<NoneDicomStudyView> noneDicomStudyQueryable = default;
if (visitTaskId == null || visitTaskId == Guid.Empty)
if (visitTaskId == null)
{
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 });
}
//质控过程中,需要忽略过滤质控设置删除的检查,以及设置删除的文件,质控通过后才
noneDicomStudyQueryable = _noneDicomStudyRepository.Where(t => t.SubjectVisitId == subjectVisitId, ignoreQueryFilters: isViewDelete)
.WhereIf(nonedicomStudyId != null, t => t.Id == nonedicomStudyId)
.WhereIf(isReading, t => t.IsReading && t.IsDeleted == false)
//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;
}
.ProjectTo<NoneDicomStudyView>(_mapper.ConfigurationProvider, new { isFilterZip = isFilterZip, isReading = isReading });
}
else
{
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 });
}
var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).Select(t => new { t.BlindSubjectCode, t.TrialReadingCriterionId, t.TrialReadingCriterion.IsImageFilter, t.TrialReadingCriterion.CriterionModalitys }).FirstNotNullAsync();
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 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();
var config = await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { t.Trial.ImageFormatList, t.Trial.StudyNameList, t.Trial.IsShowStudyName, AuditState = qcAuditState }).FirstOrDefaultAsync();
return ResponseOutput.Ok(list, config);
}

View File

@ -17,18 +17,7 @@ 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>

View File

@ -382,8 +382,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
Id = t.Id,
Bodypart = t.BodyPartForEdit,
BodyPartForEditOther = t.BodyPartForEditOther,
Bodypart = t.BodyPartExamined,
Modalities = t.Modalities,
@ -434,7 +433,6 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
Id = t.Id,
Bodypart = t.BodyPart,
BodyPartForEditOther=t.BodyPartForEditOther,
Modalities = t.Modality,

View File

@ -91,10 +91,7 @@ namespace IRaCIS.Core.Application.Service
CreateMap<DicomStudy, DicomStudyDTO>();
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));
.ForMember(o => o.VisitName, t => t.MapFrom(u => u.SubjectVisit.VisitName));
CreateMap<SCPSeries, DicomSeriesDTO>();
@ -139,11 +136,6 @@ namespace IRaCIS.Core.Application.Service
.ForMember(d => d.ReadingSeriesCount, u => u.MapFrom(s => s.SeriesList.Where(t => t.IsReading).Count()))
.ForMember(d => d.ReadingInstanceCount, u => u.MapFrom(s => s.InstanceList.Where(t => t.IsReading && t.DicomSerie.IsReading).Count()));
CreateMap<TaskStudy, DicomStudyBasicInfo>()
//.ForMember(d => d.SubjectVisitId, u => u.MapFrom(s => s.SubjectVisitId))
.ForMember(d => d.ReadingSeriesCount, u => u.MapFrom(s => s.SeriesCount))
.ForMember(d => d.ReadingInstanceCount, u => u.MapFrom(s => s.InstanceCount));
CreateMap<NoneDicomStudy, NoneDicomStudyBasicInfo>()
.ForMember(d => d.ReadingFileCount, u => u.MapFrom(s => s.NoneDicomFileList.Where(t => t.IsReading).Count()));
@ -153,7 +145,7 @@ namespace IRaCIS.Core.Application.Service
.ForMember(d => d.CriterionModalitys, u => u.MapFrom(s => s.TrialReadingCriterion.CriterionModalitys))
.ForMember(d => d.SubjectCode, u => u.MapFrom(u => u.IsAnalysisCreate == true ? u.BlindSubjectCode : u.Subject.Code))
.ForMember(d => d.DicomStudyList, u => u.MapFrom(s => s.SourceSubjectVisit.StudyList))
.ForMember(d => d.NoneDicomStudyList, u => u.MapFrom(s => s.SourceSubjectVisit.NoneDicomStudyList.Where(t => t.IsReading)));
.ForMember(d => d.NoneDicomStudyList, u => u.MapFrom(s => s.SourceSubjectVisit.NoneDicomStudyList.Where(t => t.IsReading)));
CreateMap<TrialImageDownload, TrialImageDownloadView>()
.ForMember(d => d.UserFullName, u => u.MapFrom(s => s.CreateUserRole.FullName))
@ -166,25 +158,7 @@ namespace IRaCIS.Core.Application.Service
CreateMap<SCPSeries, DicomSeriesDTO>();
CreateMap<NoneDicomEdit, NoneDicomStudy>();
CreateMap<SubjectVisit, SubjectVisitMarkStudyDto>()
.ForMember(d => d.SourceSubjectVisitId, u => u.MapFrom(u => u.Id))
.ForMember(d => d.SubjectCode, u => u.MapFrom(u => u.Subject.Code))
.ForMember(d => d.DicomStudyList, u => u.MapFrom(s => s.StudyList))
.ForMember(d => d.NoneDicomStudyList, u => u.MapFrom(s => s.NoneDicomStudyList.Where(t => t.IsReading)));
CreateMap<NoneDicomStudy, ImageMarkNoneDicomStudyBasicInfo>()
.ForMember(d => d.SubjectVisitId, u => u.MapFrom(s => s.SubjectVisitId))
.ForMember(d => d.FileCount, u => u.MapFrom(s => s.ImageLabelNoneDicomFileList.Count()))
.ForMember(d => d.ReadingFileCount, u => u.MapFrom(s => s.ImageLabelNoneDicomFileList.Count()));
CreateMap<ImageMarkNoneDicomStudyBasicInfo, NoneDicomStudyBasicInfo>();
}
}

View File

@ -372,10 +372,7 @@ namespace IRaCIS.Core.Application.Service.Inspection.DTO
public string ModuleTypeName { get; set; } = string.Empty;
public string ModuleTypeNameCN { get; set; } = string.Empty;
/// <summary>
/// 标准枚举
/// </summary>
public CriterionType? CriterionType { get; set; }
public string SignText { get; set; } = string.Empty;
public decimal? VisitNum { get; set; }

View File

@ -846,26 +846,10 @@ namespace IRaCIS.Core.Application.Service
if (jsonObject["DictionaryCode"] != null && jsonObject["DictionaryCode"].ToString() != string.Empty)
{
if (jsonObject[item.Code].ToString().Contains(","))
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
{
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();
}
value = _userInfo.IsEn_Us ? b.Value : b.ValueCN
}).Select(x => x.value).FirstOrDefaultAsync();
}
jsonList.Add(jsonObject);
@ -933,25 +917,10 @@ namespace IRaCIS.Core.Application.Service
//通过字典项的code 翻译 枚举或者 bool
else
{
if (value.ToString().Contains(","))
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
{
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();
}
value = _userInfo.IsEn_Us ? b.Value : b.ValueCN
}).Select(x => x.value).FirstOrDefaultAsync();
}
}
}
@ -1263,7 +1232,6 @@ namespace IRaCIS.Core.Application.Service
from leftObjectTypeIdtemp in ObjectTypeIdtemp.DefaultIfEmpty()
select new FrontAuditConfigView()
{
ApplyCriterionList=data.ApplyCriterionList,
IsShowParent = data.IsShowParent,
ChildrenTypeId = data.ChildrenTypeId,
Code = data.Code,

View File

@ -14,7 +14,6 @@ namespace IRaCIS.Core.Application.Service.Inspection
public class InspectionService(IRepository<DataInspection> _dataInspectionRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<TrialSign> _trialSignRepository,
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
IRepository<IdentityUser> _identityUserRepository,
IRepository<TrialAuditShow> _trialAuditShowRepository,
IRepository<UserRole> _userRoleRepository,
@ -103,9 +102,6 @@ namespace IRaCIS.Core.Application.Service.Inspection
join trial in _trialRepository.Where().IgnoreQueryFilters() on data.TrialId equals trial.Id into trialtemp
from leftrial in trialtemp.DefaultIfEmpty()
join readingQuestionCriterionTrial in _readingQuestionCriterionTrialRepository.Where().IgnoreQueryFilters() on data.TrialReadingCriterionId equals readingQuestionCriterionTrial.Id into readingQuestionCriterionTrialtemp
from leftreadingQuestionCriterionTrial in readingQuestionCriterionTrialtemp.DefaultIfEmpty()
join trialSite in _trialSiteRepository.Where().IgnoreQueryFilters() on data.TrialSiteId equals trialSite.Id into trialSitetemp
from lefttrialSite in trialSitetemp.DefaultIfEmpty()
@ -140,7 +136,6 @@ namespace IRaCIS.Core.Application.Service.Inspection
{
IsShow = lefttrialShow != null ? lefttrialShow.IsShow : leftfrontAuditConfig.IsDefaultChoice,
CreateTime = data.CreateTime,
CriterionType= leftreadingQuestionCriterionTrial==null? null: leftreadingQuestionCriterionTrial.CriterionType,
CreateUserId = data.CreateUserId,
ModuleTypeId = leftmoduleTypec.Id,
BlindName = data.VisitTask.TaskBlindName,

View File

@ -94,10 +94,6 @@ 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; }
@ -116,15 +112,7 @@ namespace IRaCIS.Application.Contracts
/// </summary>
public DateTime? LastChangePassWordTime { get; set; }
/// <summary>
/// 用户协议Id
/// </summary>
public Guid? UserAgreementId { get; set; }
/// <summary>
/// 隐私政策Id
/// </summary>
public Guid? PrivacyPolicyId { get; set; }
}
public class MenuFuncTreeNodeView
@ -283,15 +271,8 @@ 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; }

View File

@ -11,12 +11,12 @@ namespace IRaCIS.Core.Application.Service
//Task<UserDetailDTO> GetUser(Guid id);
//Task<PageOutput<UserListDTO>> GetUserList(UserListQueryDTO param);
//Task<IResponseOutput<LoginReturnDTO>> Login(string userName, string password);
Task<IResponseOutput> VerifyMFACodeAsync(string Code,bool isRemember);
Task<IResponseOutput> VerifyMFACodeAsync(string Code);
Task<IResponseOutput> SendMFAEmail(SendMfaCommand sendMfa);
Task<UserBasicInfo> GetUserBasicInfo(Guid userId, string pwd);
Task<IResponseOutput> ModifyPassword(EditPasswordCommand editPwModel);
Task<IResponseOutput> ResetPassword(AdminResetUserPwdCommand inCommand);
Task<IResponseOutput> ResetPassword(Guid userId);
Task<IResponseOutput> UpdateUser(UserCommand model);
//Task<IResponseOutput> UpdateUserState(Guid userId, UserStateEnum state);

View File

@ -29,7 +29,6 @@ namespace IRaCIS.Core.Application.Service
public class UserService(IRepository<UserRole> _userRoleRepository,
IMailVerificationService _mailVerificationService,
IRepository<VerificationCode> _verificationCodeRepository,
IRepository<UserAgreement> _userAgreementRepository,
IRepository<TrialUserRole> _userTrialRepository,
IRepository<UserLog> _userLogRepository,
IRepository<UserPassWordLog> _userPassWordLogRepository,
@ -41,7 +40,7 @@ namespace IRaCIS.Core.Application.Service
IRepository<Doctor> _doctorRepository,
ISearcher _searcher, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IFusionCache _fusionCache) : BaseService, IUserService
{
private ServiceVerifyConfigOption _serviceVerifyConfigConfig = _verifyConfig.CurrentValue;
private SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
private async Task VerifyUserNameAsync(Guid? identityUserId, string userName)
@ -286,17 +285,17 @@ namespace IRaCIS.Core.Application.Service
/// <summary>
/// 重置密码为 默认密码
/// </summary>
/// <param name="identityUserId"></param>
/// <returns></returns>
[HttpGet("{identityUserId:guid}")]
[UnitOfWork]
[HttpPost]
public async Task<IResponseOutput> ResetPassword(AdminResetUserPwdCommand inCommand)
public async Task<IResponseOutput> ResetPassword(Guid identityUserId)
{
var identityUserId = inCommand.IdentityUserId;
var pwd = IRCEmailPasswordHelper.GenerateRandomPassword(10);
await _mailVerificationService.AdminResetPwdSendEmailAsync(identityUserId, pwd, inCommand.BaseUrl);
await _mailVerificationService.AdminResetPwdSendEmailAsync(identityUserId, pwd);
await _identityUserRepository.UpdatePartialFromQueryAsync(t => t.Id == identityUserId, u => new IdentityUser()
{
@ -308,8 +307,6 @@ 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();
@ -318,7 +315,7 @@ namespace IRaCIS.Core.Application.Service
/// <summary>
/// 重置密码发邮件 (未登陆修改-忘记密码
/// 重置密码发邮件 (未登陆修改
/// </summary>
/// <param name="email"></param>
/// <returns></returns>
@ -359,8 +356,6 @@ namespace IRaCIS.Core.Application.Service
await _mailVerificationService.AnolymousSendEmailForResetAccount(email, verificationCode);
await _fusionCache.RemoveByTagAsync(CacheKeys.UserMFATag(existUser.Id));
return ResponseOutput.Ok();
}
@ -440,11 +435,6 @@ namespace IRaCIS.Core.Application.Service
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = identityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = identityUserId, OptType = UserOptType.UnloginModifyPasswoed }, true);
await _mailVerificationService.AfterUserModifyPasswordSendEmailAsync(identityUserId);
var find = await _identityUserRepository.FindAsync(identityUserId);
await _fusionCache.RemoveAsync(CacheKeys.UserLoginError(find.UserName));
return ResponseOutput.Ok();
@ -491,10 +481,6 @@ namespace IRaCIS.Core.Application.Service
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = _userInfo.IdentityUserId, OptType = UserOptType.LoginModifyPassword }, true);
await _mailVerificationService.AfterUserModifyPasswordSendEmailAsync(_userInfo.IdentityUserId);
await _fusionCache.RemoveByTagAsync(CacheKeys.UserMFATag(_userInfo.IdentityUserId));
return ResponseOutput.Result(success);
@ -836,10 +822,9 @@ namespace IRaCIS.Core.Application.Service
/// 验证MFA 邮件
/// </summary>
/// <param name="Code"></param>
/// <param name="isRemember"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
public async Task<IResponseOutput> VerifyMFACodeAsync(string Code, bool isRemember)
public async Task<IResponseOutput> VerifyMFACodeAsync(string Code)
{
var identityUserId = _userInfo.IdentityUserId;
@ -869,14 +854,6 @@ namespace IRaCIS.Core.Application.Service
}
}
if (isRemember)
{
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}");
}
return ResponseOutput.Ok();
}
@ -936,23 +913,13 @@ 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) && userRoleId != null)
{
var userName = await _userRoleRepository.Where(t => t.Id == userRoleId).Select(t => t.IdentityUser.UserName).FirstOrDefaultAsync();
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = identityUserId, ActionUserName = userName, OptType = UserOptType.LoginOut }, true);
}
else
{
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = null, ActionUserName = _userInfo.UserName, OptType = UserOptType.LoginOut }, true);
}
var userName = await _userRoleRepository.Where(t => t.Id == userRoleId).Select(t => t.IdentityUser.UserName).FirstOrDefaultAsync();
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = identityUserId, ActionUserName = userName, OptType = UserOptType.LoginOut }, true);
return ResponseOutput.Ok();
}
@ -1000,6 +967,14 @@ 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();
@ -1011,7 +986,7 @@ namespace IRaCIS.Core.Application.Service
var isLoginUncommonly = false;
#region 登录用户是系统用户的时候,就要要记录异地登录
#region //登录用户是系统用户的时候,就要要记录异地登录
//账号在系统存在
if (isExistAccount || loginUser != null)
@ -1054,7 +1029,7 @@ namespace IRaCIS.Core.Application.Service
var lastLoginIPRegion = await _userLogRepository.Where(t => t.ActionUserName == actionUserName && userOptTypes.Contains(t.OptType))
.OrderByDescending(t => t.CreateTime).Select(t => t.IPRegion).FirstOrDefaultAsync();
if (lastLoginIPRegion != null && lastLoginIPRegion != string.Empty)
if (lastLoginIPRegion != string.Empty)
{
// 与上一次区域不一致
//if (SplitAndConcatenate(existUserLoginInfo.LastLoginIP) != SplitAndConcatenate(iPRegion))
@ -1069,7 +1044,7 @@ namespace IRaCIS.Core.Application.Service
//异地登录
loginUser.LoginState = 2;
await _fusionCache.RemoveByTagAsync(CacheKeys.UserMFATag(loginUser.IdentityUserId));
}
}
}
@ -1085,14 +1060,6 @@ 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);
@ -1113,53 +1080,6 @@ namespace IRaCIS.Core.Application.Service
//登录成功 清除缓存
await _fusionCache.SetAsync(cacheKey, 0, TimeSpan.FromMinutes(lockoutMinutes));
// 记录同意用户协议以及隐私政策
#region 记录同意用户协议以及隐私政策
var userAgreementList = await _userAgreementRepository.Where(t => t.IsCurrentVersion).OrderByDescending(t => t.CreateTime).ToListAsync();
var userAgreement = userAgreementList.FirstOrDefault(t => t.UserAgreementTypeEnum == UserAgreementType.UserAgreement);
if (userAgreement != null && loginUser.UserAgreementId != userAgreement.Id)
{
await _identityUserRepository.BatchUpdateNoTrackingAsync(x => x.Id == loginUser.IdentityUserId, x => new IdentityUser()
{
UserAgreementId = userAgreement.Id,
});
var obj = new
{
UserAgreementTypeEnum = UserAgreementType.UserAgreement,
FileVersion = userAgreement.FileVersion,
UserAgreementId = userAgreement.Id,
IsEn_Us = _userInfo.IsEn_Us,
};
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = loginUser.IdentityUserId, ActionUserName = loginUser.UserName, OptType = UserOptType.AcceptUserAgreement, JsonObj = obj.ToJsonStr() }, true);
}
var privacyCollection = userAgreementList.FirstOrDefault(t => t.UserAgreementTypeEnum == UserAgreementType.PrivacyCollection);
if (privacyCollection != null && loginUser.PrivacyPolicyId != privacyCollection.Id)
{
await _identityUserRepository.BatchUpdateNoTrackingAsync(x => x.Id == loginUser.IdentityUserId, x => new IdentityUser()
{
PrivacyPolicyId = privacyCollection.Id,
});
var obj = new
{
UserAgreementTypeEnum = UserAgreementType.PrivacyCollection,
FileVersion = privacyCollection.FileVersion,
UserAgreementId = privacyCollection.Id
};
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = loginUser.IdentityUserId, ActionUserName = loginUser.UserName, OptType = UserOptType.AcceptPrivacyPolicy, JsonObj = obj.ToJsonStr() }, true);
}
#endregion
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = loginUser.IdentityUserId, ActionUserName = loginUser.UserName, OptType = UserOptType.Login, IsLoginUncommonly = isLoginUncommonly }, true);
@ -1187,35 +1107,22 @@ namespace IRaCIS.Core.Application.Service
if (_verifyConfig.CurrentValue.OpenLoginMFA)
{
//MFA 发送邮件
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} 判断缓存已经不存在");
}
userLoginReturnModel.IsMFA = true;
var email = userLoginReturnModel.BasicInfo.EMail;
var hiddenEmail = IRCEmailPasswordHelper.MaskEmail(email);
userLoginReturnModel.BasicInfo.HiddenEmail = hiddenEmail;
userLoginReturnModel.BasicInfo.EMail = hiddenEmail;
//修改密码 || 90天修改密码再mfa 之前
if (userLoginReturnModel.BasicInfo.IsFirstAdd || userLoginReturnModel.BasicInfo.NeedChangePassWord)
{
//移动到上面去了
//userLoginReturnModel.JWTStr = _tokenService.GetToken(userLoginReturnModel.BasicInfo);
}
else if (userLoginReturnModel.IsMFA == true)
else
{
//正常登录才发送邮件
await SendMFAEmail(new SendMfaCommand() { IdentityUserId = identityUserId, MFAType = UserMFAType.Login });
@ -1223,10 +1130,6 @@ namespace IRaCIS.Core.Application.Service
}
}
else
{
userLoginReturnModel.BasicInfo.HiddenEmail = userLoginReturnModel.BasicInfo.EMail;
}
await _fusionCache.SetAsync(CacheKeys.UserToken(identityUserId), userLoginReturnModel.JWTStr, TimeSpan.FromDays(7));

Some files were not shown because too many files have changed in this diff Show More