Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
commit
f97cfd58c0
|
@ -0,0 +1,68 @@
|
|||
using Microsoft.Extensions.Hosting;
|
||||
using System.Threading;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MassTransit;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Hangfire;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit.Scheduling;
|
||||
using Hangfire.Storage;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MassTransit.Mediator;
|
||||
|
||||
namespace IRaCIS.Core.API.HostService;
|
||||
|
||||
public class HangfireHostService(IRecurringMessageScheduler _recurringMessageScheduler,
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||
IMediator _mediator,
|
||||
ILogger<HangfireHostService> _logger) : IHostedService
|
||||
{
|
||||
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行开始~");
|
||||
|
||||
|
||||
//创建邮件定时任务
|
||||
//项目定时任务都在default 队列
|
||||
var dbJobIdList = JobStorage.Current.GetConnection().GetRecurringJobs().Where(t => t.Queue == "default").Select(t => t.Id).ToList();
|
||||
|
||||
foreach (var jobId in dbJobIdList)
|
||||
{
|
||||
HangfireJobHelper.RemoveCronJob(jobId);
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
|
||||
foreach (var task in taskInfoList)
|
||||
{
|
||||
//利用主键作为任务Id
|
||||
var jobId = $"{task.TrialId}({task.TrialCode})_({task.BusinessScenarioEnum})";
|
||||
|
||||
var trialId = task.TrialId;
|
||||
|
||||
HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, trialId, task.BusinessScenarioEnum, task.EmailCron);
|
||||
}
|
||||
|
||||
|
||||
//await _recurringMessageScheduler.ScheduleRecurringPublish(new QCImageQuestionSchedule() { CronExpression = "0/3 * * * * ? " }, new MasstransiTestCommand { value = "message at " + DateTime.Now.ToString() });
|
||||
|
||||
|
||||
|
||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行结束");
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
using IRaCIS.Core.API;
|
||||
using IRaCIS.Core.API.HostService;
|
||||
using IRaCIS.Core.Application.BusinessFilter;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
using IRaCIS.Core.Application.Service.BackGroundJob;
|
||||
using IRaCIS.Core.Application.Service.BusinessFilter;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
|
@ -72,6 +72,8 @@ var _configuration = builder.Configuration;
|
|||
//手动注册服务
|
||||
builder.Services.ConfigureServices(_configuration);
|
||||
|
||||
builder.Services.AddHostedService<HangfireHostService>();
|
||||
|
||||
//minimal api 异常处理
|
||||
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
|
||||
//builder.Services.AddProblemDetails();
|
||||
|
@ -251,11 +253,6 @@ SerilogExtension.AddSerilogSetup(enviromentName, app.Services);
|
|||
|
||||
|
||||
|
||||
var hangfireJobService = app.Services.GetRequiredService<IIRaCISHangfireJob>();
|
||||
|
||||
await hangfireJobService.InitHangfireJobTaskAsync();
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
#region 运行环境 部署平台
|
||||
|
@ -288,6 +285,8 @@ try
|
|||
#endregion
|
||||
|
||||
app.Run();
|
||||
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
@ -28,38 +28,26 @@ namespace IRaCIS.Core.API
|
|||
//添加 MassTransit 和 InMemory 传输
|
||||
services.AddMassTransit(cfg =>
|
||||
{
|
||||
// 自动扫描程序集中的消费者并进行注册
|
||||
cfg.AddConsumers(typeof(UserSiteSurveySubmitedEventConsumer).Assembly);
|
||||
|
||||
|
||||
|
||||
//Uri schedulerEndpoint = new Uri("queue:scheduler");
|
||||
//cfg.AddMessageScheduler(schedulerEndpoint);
|
||||
|
||||
cfg.AddPublishMessageScheduler();
|
||||
cfg.AddHangfireConsumers();
|
||||
|
||||
|
||||
|
||||
// 使用 InMemory 作为消息传递机制
|
||||
cfg.UsingInMemory((context, cfg) =>
|
||||
{
|
||||
//https://github.com/MassTransit/Sample-Hangfire/blob/master/src/Sample.Hangfire.Console/Program.cs
|
||||
cfg.UsePublishMessageScheduler();
|
||||
|
||||
//cfg.UseMessageScheduler(schedulerEndpoint);
|
||||
|
||||
|
||||
//使用 Hangfire 进行消息调度
|
||||
//cfg.UseHangfireScheduler();
|
||||
|
||||
cfg.UseConsumeFilter(typeof(ConsumeExceptionFilter<>), context,
|
||||
x => x.Include(type => type.IsAssignableTo(typeof(DomainEvent))));
|
||||
|
||||
cfg.UseConsumeFilter(typeof(CultureInfoFilter<>), context,
|
||||
x => x.Include(type => type.IsAssignableTo(typeof(DomainEvent))));
|
||||
|
||||
|
||||
// 这里可以进行额外的配置
|
||||
cfg.ConfigureEndpoints(context); // 自动配置所有消费者的端点
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ using IP2Region.Net.XDB;
|
|||
using IRaCIS.Core.Application.BackGroundJob;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
using IRaCIS.Core.Application.Service.BackGroundJob;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
@ -52,7 +51,6 @@ public static class ServiceCollectionSetup
|
|||
services.AddScoped<IRepository, Repository>();
|
||||
|
||||
services.AddScoped<IObtainTaskAutoCancelJob, ObtainTaskAutoCancelJob>();
|
||||
services.AddScoped<IIRaCISHangfireJob, IRaCISCHangfireJob>();
|
||||
|
||||
// 注册以Service 结尾的服务
|
||||
services.Scan(scan => scan
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
using Hangfire;
|
||||
using Hangfire.Storage;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit;
|
||||
using MassTransit.Mediator;
|
||||
using MassTransit.Scheduling;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace IRaCIS.Core.Application.Service.BackGroundJob
|
||||
{
|
||||
|
||||
public interface IIRaCISHangfireJob
|
||||
{
|
||||
Task InitHangfireJobTaskAsync();
|
||||
|
||||
}
|
||||
public class IRaCISCHangfireJob(ILogger<IRaCISCHangfireJob> _logger,
|
||||
IRepository<Internationalization> _internationalizationRepository,
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||
IMessageScheduler _iMessageScheduler,
|
||||
IBus _bus
|
||||
) : IIRaCISHangfireJob
|
||||
{
|
||||
public static string JsonFileFolder = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources);
|
||||
|
||||
|
||||
public async Task InitHangfireJobTaskAsync()
|
||||
{
|
||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行开始~");
|
||||
|
||||
|
||||
|
||||
//创建邮件定时任务
|
||||
await InitSysAndTrialCronJobAsync();
|
||||
|
||||
|
||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行结束");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task InitSysAndTrialCronJobAsync()
|
||||
{
|
||||
|
||||
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, t.EmailCron, t.BusinessScenarioEnum, t.TrialId })
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var task in taskInfoList)
|
||||
{
|
||||
//利用主键作为任务Id
|
||||
var jobId = $"{task.TrialId}_{task.Id}";
|
||||
|
||||
switch (task.BusinessScenarioEnum)
|
||||
{
|
||||
|
||||
case EmailBusinessScenario.QCTask:
|
||||
|
||||
break;
|
||||
|
||||
case EmailBusinessScenario.CRCToQCQuestion:
|
||||
|
||||
break;
|
||||
case EmailBusinessScenario.QCToCRCImageQuestion:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//var schedulerEndpoint = await _bus.GetSendEndpoint(new Uri("queue:hangfire"));
|
||||
//await schedulerEndpoint.ScheduleRecurringSend<QCImageQuestionSchedule>(new Uri("queue:mt-message-queue"), new QCImageQuestionSchedule() { ScheduleId = jobId, CronExpression = "0 0/1 * 1/1 * ? *" }, new PollExternalSystem { });
|
||||
//await schedulerEndpoint.ScheduleRecurringSend<QCImageQuestionSchedule>(new Uri("queue:hangfire"), new QCImageQuestionSchedule() { ScheduleId = jobId + jobId, CronExpression = "0 0/1 * 1/1 * ? *" }, new PollExternalSystem { });
|
||||
|
||||
|
||||
HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, task.TrialId, task.BusinessScenarioEnum, task.EmailCron);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class PollExternalSystem { }
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
using Hangfire;
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit.Mediator;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
|
@ -62,15 +64,15 @@ namespace IRaCIS.Core.Application.Helper
|
|||
{
|
||||
|
||||
case EmailBusinessScenario.QCTask:
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IEmailSendService>(jobId, t => t.SendTrialImageQCTaskEmailAsync(trialId), emailCron);
|
||||
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new ImageQCRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||
break;
|
||||
case EmailBusinessScenario.CRCToQCQuestion:
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IEmailSendService>(jobId, t => t.SendTrialQCQuestionEmailAsync(trialId), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new CRCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||
break;
|
||||
case EmailBusinessScenario.QCToCRCImageQuestion:
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IEmailSendService>(jobId, t => t.SendTrialImageQuestionAsync(trialId), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1286,27 +1286,6 @@
|
|||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.Service.EmailSendService.SendTrialImageQCTaskEmailAsync(System.Guid)">
|
||||
<summary>
|
||||
影像质控
|
||||
</summary>
|
||||
<param name="trialId"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.Service.EmailSendService.SendTrialQCQuestionEmailAsync(System.Guid)">
|
||||
<summary>
|
||||
QC质疑
|
||||
</summary>
|
||||
<param name="trialId"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.Service.EmailSendService.SendTrialImageQuestionAsync(System.Guid)">
|
||||
<summary>
|
||||
影像质疑
|
||||
</summary>
|
||||
<param name="trialId"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.Application.Service.TrialEmailNoticeConfigService">
|
||||
<summary>
|
||||
TrialEmailNoticeConfigService
|
||||
|
@ -12920,6 +12899,18 @@
|
|||
MIM 回复医学返回通知IR
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.MassTransit.Consumer.UrgentMIMRepliedMedicalReviewConsumer.#ctor(IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.User},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TaskMedicalReview},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Trial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectVisit},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingQuestionCriterionTrial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.VisitTask},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Dictionary},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.EmailNoticeConfig},Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.SystemEmailSendConfig})">
|
||||
<summary>
|
||||
MIM 回复医学返回通知IR
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.MassTransit.Consumer.UrgentMIMRepliedMedicalReviewConsumer.Consume(MassTransit.ConsumeContext{IRaCIS.Core.Domain.UrgentMIMRepliedMedicalReview})">
|
||||
<summary>
|
||||
MIM 回复医学返回通知IR
|
||||
</summary>
|
||||
<param name="context"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.Application.MassTransit.Consumer.UrgentIRApplyedReReadingConsumer">
|
||||
<summary>
|
||||
加急阅片 IR 申请重阅 或者PM 申请重阅
|
||||
|
@ -13006,9 +12997,49 @@
|
|||
</summary>
|
||||
<param name="_userRepository"></param>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.Application.MassTransit.Consumer.MediatorHttpContextScopeFilterExtensions">
|
||||
<member name="T:IRaCIS.Core.Application.MassTransit.Consumer.QCImageQuestionRecurringEventConsumer">
|
||||
<summary>
|
||||
参考链接:https://github.com/MassTransit/MassTransit/discussions/2498
|
||||
QC 影像质疑
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.MassTransit.Consumer.QCImageQuestionRecurringEventConsumer.#ctor(IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Trial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TrialEmailNoticeConfig},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TrialUser},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.VisitTask},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TrialSiteUser},Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.SystemEmailSendConfig})">
|
||||
<summary>
|
||||
QC 影像质疑
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.Application.MassTransit.Consumer.CRCImageQuestionRecurringEventConsumer">
|
||||
<summary>
|
||||
CRC 影像质疑
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.MassTransit.Consumer.CRCImageQuestionRecurringEventConsumer.#ctor(IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Trial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TrialEmailNoticeConfig},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TrialUser},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.VisitTask},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TrialSiteUser},Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.SystemEmailSendConfig})">
|
||||
<summary>
|
||||
CRC 影像质疑
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.Application.MassTransit.Consumer.ImageQCRecurringEventConsumer">
|
||||
<summary>
|
||||
影像质控 发送给QC的
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.MassTransit.Consumer.ImageQCRecurringEventConsumer.#ctor(IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Trial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TrialEmailNoticeConfig},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TrialUser},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.VisitTask},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TrialSiteUser},Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.SystemEmailSendConfig})">
|
||||
<summary>
|
||||
影像质控 发送给QC的
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.Application.MassTransit.Consumer.QCImageQuestionRecurringEvent">
|
||||
<summary>
|
||||
QC 影像质疑待处理
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.Application.MassTransit.Consumer.CRCImageQuestionRecurringEvent">
|
||||
<summary>
|
||||
CRC 影像质疑
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.Application.MassTransit.Consumer.ImageQCRecurringEvent">
|
||||
<summary>
|
||||
影像质控
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.Application.MassTransit.Consumer.QCImageQuestionSchedule">
|
||||
|
@ -13026,6 +13057,16 @@
|
|||
影像质控
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.Application.MassTransit.Recurring.UrgentIRUnReadTaskRecurringEventConsumer">
|
||||
<summary>
|
||||
10分钟检测通知IR 已通知的进行标注,下次不会再通知
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.MassTransit.Recurring.UrgentIRUnReadTaskRecurringEventConsumer.#ctor(IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.ReadingQuestionCriterionTrial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.VisitTask},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Dictionary},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.TrialUser},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.EmailNoticeConfig},Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.SystemEmailSendConfig})">
|
||||
<summary>
|
||||
10分钟检测通知IR 已通知的进行标注,下次不会再通知
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.Application.ViewModel.TaskAllocationRuleView">
|
||||
<summary> TaskAllocationRuleView 列表视图模型 </summary>
|
||||
</member>
|
||||
|
|
|
@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Components.Routing;
|
|||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using NPOI.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
@ -66,6 +67,11 @@ public class UserSiteSurveySubmitedEventConsumer(
|
|||
|
||||
toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
|
||||
|
||||
foreach (var user in pmAndAPMList)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -77,6 +83,9 @@ public class UserSiteSurveySubmitedEventConsumer(
|
|||
toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
|
||||
}
|
||||
|
||||
//发件地址
|
||||
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);
|
||||
|
@ -151,7 +160,8 @@ public class SiteSurveySPMSubmitedEventConsumer(
|
|||
toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
|
||||
}
|
||||
|
||||
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
|
||||
|
@ -178,7 +188,7 @@ public class SiteSurveySPMSubmitedEventConsumer(
|
|||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository, EmailBusinessScenario.SiteSurveyReject, messageToSend, emailConfigFunc);
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository, EmailBusinessScenario.Approval_SubmitSiteSurvey, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
}
|
||||
|
@ -209,26 +219,37 @@ public class SiteSurverRejectedEventConsumer(
|
|||
|
||||
var trialId = siteSurveyInfo.TrialId;
|
||||
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
|
||||
var name = siteSurveyInfo.UserName;
|
||||
var toUserName = siteSurveyInfo.UserName;
|
||||
|
||||
if (context.Message.IsHaveSPMOrCPM)
|
||||
{
|
||||
//PM 驳回到SPM
|
||||
if (siteSurveyInfo.State == TrialSiteSurveyEnum.CRCSubmitted)
|
||||
{
|
||||
var user = await _userRepository.FirstOrDefaultAsync(t => t.Id == siteSurveyInfo.PreliminaryUserId);
|
||||
//var user = await _userRepository.FirstOrDefaultAsync(t => t.Id == siteSurveyInfo.PreliminaryUserId);
|
||||
|
||||
name = user.FullName;
|
||||
//name = user.FullName;
|
||||
|
||||
var sPMOrCPMList = _trialRepository.Where(t => t.Id == trialId)
|
||||
.SelectMany(t => t.TrialUserList)
|
||||
.Where(t => t.User.UserTypeEnum == UserTypeEnum.SPM || t.User.UserTypeEnum == UserTypeEnum.CPM)
|
||||
.Select(t => new { t.User.EMail, t.User.FullName, t.User.UserTypeEnum }).ToList();
|
||||
|
||||
|
||||
foreach (var user in sPMOrCPMList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||
}
|
||||
|
||||
toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
|
||||
|
||||
messageToSend.To.Add(new MailboxAddress(name, user.EMail));
|
||||
}
|
||||
//SPM 驳回到CRC
|
||||
else if (siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(name, siteSurveyInfo.Email));
|
||||
messageToSend.To.Add(new MailboxAddress(toUserName, siteSurveyInfo.Email));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -255,7 +276,7 @@ public class SiteSurverRejectedEventConsumer(
|
|||
{
|
||||
var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
name,
|
||||
toUserName,
|
||||
trialInfo.TrialCode,
|
||||
trialInfo.ResearchProgramNo,
|
||||
trialInfo.ExperimentName,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit;
|
||||
using MassTransit.Mediator;
|
||||
using MassTransit.Scheduling;
|
||||
using Medallion.Threading;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
@ -46,6 +47,7 @@ public class TestMasstransitService : BaseService
|
|||
|
||||
|
||||
public async Task<IResponseOutput> TestMasstransitRequest([FromServices] IMessageScheduler _scheduler,
|
||||
[FromServices] IRecurringMessageScheduler _recurringMessageScheduler,
|
||||
[FromServices] IRepository<TestLength> _testLengthRepository,
|
||||
[FromServices] IRequestClient<MasstransiTestCommand> _requestClient,
|
||||
[FromServices] IScopedClientFactory _clientFactory,
|
||||
|
@ -60,17 +62,17 @@ public class TestMasstransitService : BaseService
|
|||
//IScopedMediator 上下文一致, IMediator上下文不一致
|
||||
|
||||
//通过命令不获取结果 进入消费者后再返回 数据库上下文 不同
|
||||
await _mediator.Send(new MasstransiTestCommand { value = "message at " + DateTime.Now.ToString() });
|
||||
//await _mediator.Send(new MasstransiTestCommand { value = "message at " + DateTime.Now.ToString() });
|
||||
|
||||
//通过命令获取结果 进入消费者后再返回 数据库上下文 相同
|
||||
var dd = await _mediatorScoped.CreateRequest(new MasstransiTestCommand() { value = "message at " + DateTime.Now.ToString() })
|
||||
.GetResponse<IResponseOutput>();
|
||||
////通过命令获取结果 进入消费者后再返回 数据库上下文 相同
|
||||
//var dd = await _mediatorScoped.CreateRequest(new MasstransiTestCommand() { value = "message at " + DateTime.Now.ToString() })
|
||||
// .GetResponse<IResponseOutput>();
|
||||
|
||||
//发布后,不会立即进入消费者,消费者是另外的线程执行
|
||||
await _mediatorScoped.Publish(new MasstransiTestCommand { value = "message at " + DateTime.Now.ToString() });
|
||||
////发布后,不会立即进入消费者,消费者是另外的线程执行
|
||||
//await _mediatorScoped.Publish(new MasstransiTestCommand { value = "message at " + DateTime.Now.ToString() });
|
||||
|
||||
|
||||
await _scheduler.SchedulePublish(DateTime.Now.AddSeconds(10), new MasstransiTestCommand() { value = "message at " + DateTime.Now.ToString() });
|
||||
await _recurringMessageScheduler.ScheduleRecurringPublish(new QCImageQuestionSchedule() { CronExpression = "0/3 * * * * ? " }, new MasstransiTestCommand { value = "message at " + DateTime.Now.ToString() });
|
||||
//await _scheduler.SchedulePublish(DateTime.Now.AddSeconds(10), new MasstransiTestCommand() { value = "message at " + DateTime.Now.ToString() });
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
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) : IFilter<ConsumeContext<T>> where T : DomainEvent
|
||||
{
|
||||
|
||||
public async Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
await next.Send(context);
|
||||
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var errorInfo = $"Exception: {exception.Message}[{exception.StackTrace}]" + (exception.InnerException != null ? $" InnerException: {exception.InnerException.Message}[{exception.InnerException.StackTrace}]" : "");
|
||||
_logger.LogError(errorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public void Probe(ProbeContext context)
|
||||
{
|
||||
context.CreateFilterScope("ConsumeException");
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
|
||||
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using MassTransit;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
|
||||
/// <summary>
|
||||
/// 参考链接:https://github.com/MassTransit/MassTransit/discussions/2498
|
||||
/// </summary>
|
||||
public static class MediatorHttpContextScopeFilterExtensions
|
||||
{
|
||||
public static void UseHttpContextScopeFilter(this IMediatorConfigurator configurator, IServiceProvider serviceProvider)
|
||||
{
|
||||
var filter = new HttpContextScopeFilter(serviceProvider.GetRequiredService<IHttpContextAccessor>());
|
||||
|
||||
configurator.ConfigurePublish(x => x.UseFilter(filter));
|
||||
configurator.ConfigureSend(x => x.UseFilter(filter));
|
||||
configurator.UseFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
public class HttpContextScopeFilter :
|
||||
IFilter<PublishContext>,
|
||||
IFilter<SendContext>,
|
||||
IFilter<ConsumeContext>
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public HttpContextScopeFilter(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
private void AddPayload(PipeContext context)
|
||||
{
|
||||
if (_httpContextAccessor.HttpContext == null)
|
||||
return;
|
||||
|
||||
var serviceProvider = _httpContextAccessor.HttpContext.RequestServices;
|
||||
context.GetOrAddPayload(() => serviceProvider);
|
||||
context.GetOrAddPayload<IServiceScope>(() => new NoopScope(serviceProvider));
|
||||
}
|
||||
|
||||
public Task Send(PublishContext context, IPipe<PublishContext> next)
|
||||
{
|
||||
AddPayload(context);
|
||||
return next.Send(context);
|
||||
}
|
||||
|
||||
public Task Send(SendContext context, IPipe<SendContext> next)
|
||||
{
|
||||
AddPayload(context);
|
||||
return next.Send(context);
|
||||
}
|
||||
|
||||
public Task Send(ConsumeContext context, IPipe<ConsumeContext> next)
|
||||
{
|
||||
AddPayload(context);
|
||||
return next.Send(context);
|
||||
}
|
||||
|
||||
public void Probe(ProbeContext context)
|
||||
{
|
||||
}
|
||||
|
||||
private class NoopScope :
|
||||
IServiceScope
|
||||
{
|
||||
public NoopScope(IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||
using MassTransit;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reactive.Joins;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 10分钟检测通知IR 已通知的进行标注,下次不会再通知
|
||||
/// </summary>
|
||||
public class UrgentIRUnReadTaskRecurringEventConsumer(
|
||||
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<Dictionary> _dictionaryRepository,
|
||||
IRepository<TrialUser> _trialUserRepository,
|
||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<UrgentIRUnReadTaskRecurringEvent>
|
||||
{
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
|
||||
public async Task Consume(ConsumeContext<UrgentIRUnReadTaskRecurringEvent> context)
|
||||
{
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
var trialId = context.Message.TrialId;
|
||||
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
{
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
{
|
||||
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue="1" }
|
||||
}
|
||||
});
|
||||
|
||||
//找到该项目所有的IR 并且有加急 和Pd 未读的任务
|
||||
|
||||
Expression<Func<VisitTask, bool>> comonTaskFilter = t => t.TrialId == trialId && t.TaskState == TaskState.Effect && t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskAllocationState == TaskAllocationState.Allocated;
|
||||
|
||||
var trialUserList = _trialUserRepository.Where(t => t.TrialId == trialId).Select(t => new
|
||||
{
|
||||
t.UserId,
|
||||
t.User.FullName,
|
||||
t.User.EMail,
|
||||
t.User.UserName,
|
||||
t.Trial.TrialCode,
|
||||
t.Trial.ResearchProgramNo
|
||||
//TrialReadingCriterionList = t.Trial.TrialReadingCriterionList.Select(t => new { t.CriterionName, TrialReadingCriterionId = t.Id }).ToList()
|
||||
});
|
||||
|
||||
|
||||
foreach (var trialUser in trialUserList)
|
||||
{
|
||||
|
||||
var userId=trialUser.UserId;
|
||||
|
||||
var doctorCriterionList = await _trialReadingCriterionRepository.Where(t => t.IsSigned && t.IsConfirm && t.Trial.TrialUserList.Any(t => t.UserId == userId))
|
||||
.Select(c => new
|
||||
{
|
||||
|
||||
TrialReadingCriterionId = c.Id,
|
||||
CriterionName=c.CriterionName,
|
||||
|
||||
UnReadList = c.VisitTaskList
|
||||
.Where(t => t.DoctorUserId == userId && t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskState == TaskState.Effect)
|
||||
// 前序 不存在 未一致性核查未通过的
|
||||
.Where(t => !t.Subject.SubjectVisitList.Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum > sv.VisitNum))
|
||||
//前序 不存在 未生成任务的访视
|
||||
.Where(t => c.IsAutoCreate == false ? !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(d => d.TrialReadingCriterionId == t.TrialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum) : true)
|
||||
|
||||
.Where(y => y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true))
|
||||
|
||||
.Where(t=> t.SourceSubjectVisit.PDState==PDStateEnum.PDProgress || t.SourceSubjectVisit.IsEnrollmentConfirm ||
|
||||
t.ReadModule.SubjectVisit.PDState == PDStateEnum.PDProgress || t.ReadModule.SubjectVisit.IsEnrollmentConfirm)
|
||||
.Select(c=>new
|
||||
{
|
||||
SubejctCode= c.IsAnalysisCreate? c.BlindSubjectCode: c.Subject.Code,
|
||||
c.TaskBlindName,
|
||||
c.TaskName
|
||||
}).ToList()
|
||||
|
||||
}).ToListAsync();
|
||||
|
||||
|
||||
var toTalUnreadCount= doctorCriterionList.SelectMany(t=>t.UnReadList).Count();
|
||||
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(trialUser.FullName, trialUser.EMail));
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, trialUser.ResearchProgramNo);
|
||||
|
||||
var template = input.htmlBodyStr;
|
||||
|
||||
//正则提取循环的部分 {%for%}(.*?){%end for%}
|
||||
string pattern = @"{%for%}(.*?){%end for%}";
|
||||
var match = Regex.Match(template, pattern, RegexOptions.Singleline);
|
||||
|
||||
string criteriaTemplate = match.Groups[1].Value; // 提取循环模板
|
||||
|
||||
// 构建循环内容
|
||||
string criteriaDetails = "";
|
||||
foreach (var criteria in doctorCriterionList)
|
||||
{
|
||||
criteriaDetails += string.Format(criteriaTemplate, criteria.CriterionName, criteria.UnReadList.Select(t=>t.SubejctCode).Distinct().Count(), criteria.UnReadList.Count());
|
||||
}
|
||||
|
||||
// 用循环内容替换原始模板中的循环部分
|
||||
string emailContent = Regex.Replace(template, pattern, criteriaDetails);
|
||||
|
||||
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
trialUser.FullName,
|
||||
|
||||
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
toTalUnreadCount,
|
||||
|
||||
trialUser.ResearchProgramNo,
|
||||
dictionValue[0],
|
||||
_systemEmailConfig.SiteUrl
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository, EmailBusinessScenario.ExpeditedReading, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
using Amazon.Runtime.Internal.Util;
|
||||
using DocumentFormat.OpenXml;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using MimeKit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
|
||||
public static class OldRecurringEmailHelper
|
||||
{
|
||||
public static string EmailNamePlaceholder => StaticData.EmailSend.EmailNamePlaceholder;
|
||||
|
||||
|
||||
|
||||
public static async Task SendTrialEmailAsync(
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||
IRepository<Trial> _trialRepository,
|
||||
IRepository<TrialUser> _trialUserRepository,
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<TrialSiteUser> _trialSiteUserRepository,
|
||||
|
||||
Guid trialId, EmailBusinessScenario businessScenario,
|
||||
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc,
|
||||
Guid? trialSiteId = null, Guid? trialReadingCriterionId = null)
|
||||
{
|
||||
//找到配置
|
||||
var trialEmailConfig = await _trialEmailNoticeConfigRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionId && t.BusinessScenarioEnum == businessScenario, ignoreQueryFilters: true)
|
||||
.Include(t => t.TrialEmailNoticeUserList).Include(t => t.TrialEmailBlackUserList).FirstOrDefaultAsync();
|
||||
|
||||
|
||||
if (trialEmailConfig == null || trialEmailConfig.IsAutoSend == false || trialEmailConfig.IsEnable == false)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var sendEmailConfig = new SMTPEmailConfig();
|
||||
|
||||
var (topicStr, htmlBodyStr, isEn_us, onlyToUserId) = topicAndHtmlFunc(trialEmailConfig);
|
||||
|
||||
|
||||
sendEmailConfig.TopicDescription = topicStr;
|
||||
sendEmailConfig.HtmlBodyStr = htmlBodyStr;
|
||||
|
||||
|
||||
var blackUserIdList = trialEmailConfig.TrialEmailBlackUserList.Select(t => t.UserId).ToList();
|
||||
|
||||
|
||||
var toUserTypeEnumList = trialEmailConfig.TrialEmailNoticeUserList.Where(t => t.EmailUserType == EmailUserType.To).Select(c => c.UserType).ToList();
|
||||
|
||||
var copyUserTypeEnumList = trialEmailConfig.TrialEmailNoticeUserList.Where(t => t.EmailUserType == EmailUserType.Copy).Select(c => c.UserType).ToList();
|
||||
|
||||
var allUserTypeEnumList = toUserTypeEnumList.Union(copyUserTypeEnumList).Distinct().ToList();
|
||||
|
||||
var allUserList = await _trialUserRepository.Where(t => t.TrialId == trialId && allUserTypeEnumList.Contains(t.User.UserTypeEnum)).Select(t => new { t.UserId, t.User.EMail, t.User.FullName, t.User.UserTypeEnum }).ToListAsync();
|
||||
|
||||
|
||||
var toUserList = allUserList.Where(t => toUserTypeEnumList.Contains(t.UserTypeEnum))
|
||||
.ToList();
|
||||
|
||||
//收件人 有CRC CRA , CRC CRA的账户要按照中心发送
|
||||
if (trialSiteId == null && toUserTypeEnumList.Any(t => t == UserTypeEnum.ClinicalResearchCoordinator || t == UserTypeEnum.CRA) && onlyToUserId == null)
|
||||
{
|
||||
throw new BusinessValidationFailedException("当前场景收件人包含CRC CRA,但是没有siteId,请联系后端开发");
|
||||
}
|
||||
if (trialSiteId != null && toUserTypeEnumList.Any(t => t == UserTypeEnum.ClinicalResearchCoordinator || t == UserTypeEnum.CRA))
|
||||
{
|
||||
var curentSiteUserIdList = _trialSiteUserRepository.Where(t => t.TrialId == trialId && t.TrialSiteId == trialSiteId).Select(t => t.UserId).ToList();
|
||||
|
||||
toUserList = toUserList.Where(t => (t.UserTypeEnum != UserTypeEnum.CRA && t.UserTypeEnum != UserTypeEnum.ClinicalResearchCoordinator) || curentSiteUserIdList.Contains(t.UserId)).ToList();
|
||||
}
|
||||
|
||||
|
||||
//去除黑名单
|
||||
toUserList = toUserList.Where(t => !blackUserIdList.Contains(t.UserId)).ToList();
|
||||
|
||||
var copyUserList = allUserList.Where(t => copyUserTypeEnumList.Contains(t.UserTypeEnum))
|
||||
.Where(t => !blackUserIdList.Contains(t.UserId)).ToList();
|
||||
|
||||
if (trialSiteId != null && copyUserTypeEnumList.Any(t => t == UserTypeEnum.ClinicalResearchCoordinator || t == UserTypeEnum.CRA))
|
||||
{
|
||||
var curentSiteUserIdList = _trialSiteUserRepository.Where(t => t.TrialId == trialId && t.TrialSiteId == trialSiteId).Select(t => t.UserId).ToList();
|
||||
|
||||
copyUserList = copyUserList.Where(t => (t.UserTypeEnum != UserTypeEnum.CRA && t.UserTypeEnum != UserTypeEnum.ClinicalResearchCoordinator) || curentSiteUserIdList.Contains(t.UserId)).ToList();
|
||||
}
|
||||
|
||||
if (onlyToUserId != null)
|
||||
{
|
||||
toUserList = toUserList.Where(t => t.UserId == onlyToUserId).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
sendEmailConfig.HtmlBodyStr = htmlBodyStr.Replace(EmailNamePlaceholder, string.Join(isEn_us ? ", " : "、", toUserList.Select(t => t.FullName).ToList()));
|
||||
}
|
||||
|
||||
if (toUserList.Count == 0)
|
||||
{
|
||||
//---没有收件人,无法发送邮件
|
||||
throw new BusinessValidationFailedException(I18n.T("TrialEmailN_NoRecipient"));
|
||||
}
|
||||
|
||||
|
||||
if (trialEmailConfig.FromEmail.Contains("@") && !string.IsNullOrEmpty(trialEmailConfig.FromEmail))
|
||||
{
|
||||
|
||||
sendEmailConfig.FromEmailAddress = new MimeKit.MailboxAddress(trialEmailConfig.FromName, trialEmailConfig.FromEmail);
|
||||
sendEmailConfig.AuthorizationCode = trialEmailConfig.AuthorizationCode;
|
||||
sendEmailConfig.UserName = trialEmailConfig.FromEmail;
|
||||
|
||||
sendEmailConfig.Host = trialEmailConfig.SMTPServerAddress;
|
||||
sendEmailConfig.Port = trialEmailConfig.SMTPServerPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
//---项目发件邮箱配置有误,请核实
|
||||
throw new BusinessValidationFailedException(I18n.T("TrialEmailN_InvalidEmailConfig"));
|
||||
}
|
||||
|
||||
foreach (var item in toUserList)
|
||||
{
|
||||
|
||||
if (item.EMail.Contains("@") && !string.IsNullOrEmpty(item.EMail))
|
||||
{
|
||||
|
||||
sendEmailConfig.ToMailAddressList.Add(new MimeKit.MailboxAddress(item.FullName, item.EMail));
|
||||
|
||||
}
|
||||
}
|
||||
foreach (var item in copyUserList)
|
||||
{
|
||||
|
||||
if (item.EMail.Contains("@") && !string.IsNullOrEmpty(item.EMail))
|
||||
{
|
||||
|
||||
sendEmailConfig.CopyToMailAddressList.Add(new MimeKit.MailboxAddress(item.FullName, item.EMail));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//邮件附件 这里是原格式发送,不是PDF
|
||||
|
||||
//if (trialEmailConfig.AttachCNPath != string.Empty && trialEmailConfig.AttachPath != string.Empty)
|
||||
//{
|
||||
// var phyPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, isEn_us? trialEmailConfig.AttachName: trialEmailConfig.AttachNameCN);
|
||||
|
||||
// //先预先生成了邮件,发送预先生成的邮件
|
||||
// sendEmailConfig.EmailAttachMentConfigList.Add(new EmailAttachMentConfig()
|
||||
// {
|
||||
// FileName = $"{attachPrefix}_{Path.GetFileName(_userInfo.IsEn_Us ? trialEmailConfig.AttachName : trialEmailConfig.AttachNameCN)}",
|
||||
|
||||
// FileStream = File.OpenRead(phyPath),
|
||||
// });
|
||||
//}
|
||||
|
||||
if (sendEmailConfig != null)
|
||||
{
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(sendEmailConfig);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
using IRaCIS.Core.Application.MassTransit.Command;
|
||||
using IRaCIS.Core.Domain.BaseModel;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit;
|
||||
using MassTransit.Scheduling;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// QC 影像质疑
|
||||
/// </summary>
|
||||
|
||||
public class QCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||
IRepository<TrialUser> _trialUserRepository,
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<TrialSiteUser> _trialSiteUserRepository,
|
||||
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig) : IConsumer<QCImageQuestionRecurringEvent>
|
||||
{
|
||||
public async Task Consume(ConsumeContext<QCImageQuestionRecurringEvent> context)
|
||||
{
|
||||
var trialId = context.Message.TrialId;
|
||||
|
||||
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();
|
||||
|
||||
//找到 该项目的CRC 用户Id
|
||||
var userList = await _trialUserRepository.Where(t => t.TrialId == trialId).Where(t => t.User.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Select(t => new { t.UserId, t.User.FullName }).ToListAsync();
|
||||
|
||||
//判断是否任务可以领取 ,可以的话 发送邮件
|
||||
|
||||
foreach (var user in userList)
|
||||
{
|
||||
var userId = user.UserId;
|
||||
//过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id
|
||||
var query = _trialRepository.Where(t => t.Id == trialId)
|
||||
|
||||
.Select(t => new
|
||||
{
|
||||
//质疑待处理
|
||||
ToBeDealedCount = t.SubjectVisitList.Where(t => t.TrialSite.CRCUserList.Any(t => t.UserId == userId)).SelectMany(c => c.QCChallengeList)
|
||||
.Where(u => u.IsClosed == false && (u.LatestReplyUser.UserTypeEnum == UserTypeEnum.IQC || u.LatestReplyUserId == null)).Count(),
|
||||
|
||||
});
|
||||
|
||||
var sendStat = await query.FirstOrDefaultAsync();
|
||||
|
||||
//当前人
|
||||
if (sendStat != null && (sendStat.ToBeDealedCount > 0))
|
||||
{
|
||||
|
||||
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc = trialEmailConfig =>
|
||||
{
|
||||
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
|
||||
user.FullName, DateTime.Now, sendStat.ToBeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
|
||||
return (topicStr, htmlBodyStr, isEn_us, userId);
|
||||
};
|
||||
|
||||
await OldRecurringEmailHelper.SendTrialEmailAsync(
|
||||
_trialEmailNoticeConfigRepository,
|
||||
_trialRepository,
|
||||
_trialUserRepository,
|
||||
_visitTaskRepository,
|
||||
_trialSiteUserRepository,
|
||||
trialId, EmailBusinessScenario.QCToCRCImageQuestion, topicAndHtmlFunc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// CRC 影像质疑
|
||||
/// </summary>
|
||||
public class CRCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||
IRepository<TrialUser> _trialUserRepository,
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<TrialSiteUser> _trialSiteUserRepository,
|
||||
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig) : IConsumer<CRCImageQuestionRecurringEvent>
|
||||
{
|
||||
public async Task Consume(ConsumeContext<CRCImageQuestionRecurringEvent> context)
|
||||
{
|
||||
var trialId = context.Message.TrialId;
|
||||
|
||||
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
|
||||
var userList = await _trialUserRepository.Where(t => t.TrialId == trialId).Where(t => t.User.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.User.FullName }).ToListAsync();
|
||||
|
||||
//判断是否任务可以领取 ,可以的话 发送邮件
|
||||
|
||||
foreach (var user in userList)
|
||||
{
|
||||
var userId = user.UserId;
|
||||
|
||||
//过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id
|
||||
var query = _trialRepository
|
||||
.Where(t => t.Id == trialId)
|
||||
.Select(t => new
|
||||
{
|
||||
ReUploadTobeDealedCount = t.SubjectVisitList.SelectMany(c => c.QCChallengeList)
|
||||
.Where(u => u.CreateUserId == userId && u.IsClosed == false && u.LatestReplyUser.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator && u.ReuploadEnum == QCChanllengeReuploadEnum.CRCRequestReupload).Count(),
|
||||
|
||||
//质疑待处理 发送邮件的时候 需要减去ReUploadTobeDealedCount
|
||||
ToBeDealedCount = t.SubjectVisitList.SelectMany(c => c.QCChallengeList)
|
||||
.Where(u => u.CreateUserId == userId && u.IsClosed == false && u.LatestReplyUser.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Count(),
|
||||
});
|
||||
|
||||
var sendStat = await query.FirstOrDefaultAsync();
|
||||
|
||||
//当前人
|
||||
if (sendStat != null && (sendStat.ToBeDealedCount > 0 || sendStat.ReUploadTobeDealedCount > 0))
|
||||
{
|
||||
|
||||
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc = trialEmailConfig =>
|
||||
{
|
||||
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
|
||||
|
||||
user.FullName, DateTime.Now, sendStat.ToBeDealedCount - sendStat.ReUploadTobeDealedCount, sendStat.ReUploadTobeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
|
||||
|
||||
return (topicStr, htmlBodyStr, false, userId);
|
||||
};
|
||||
|
||||
await OldRecurringEmailHelper.SendTrialEmailAsync(_trialEmailNoticeConfigRepository,
|
||||
_trialRepository,
|
||||
_trialUserRepository,
|
||||
_visitTaskRepository,
|
||||
_trialSiteUserRepository,
|
||||
trialId, EmailBusinessScenario.CRCToQCQuestion, topicAndHtmlFunc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 影像质控 发送给QC的
|
||||
/// </summary>
|
||||
public class ImageQCRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||
IRepository<TrialUser> _trialUserRepository,
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<TrialSiteUser> _trialSiteUserRepository,
|
||||
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig) : IConsumer<ImageQCRecurringEvent>
|
||||
{
|
||||
public async Task Consume(ConsumeContext<ImageQCRecurringEvent> context)
|
||||
{
|
||||
var trialId=context.Message.TrialId;
|
||||
|
||||
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();
|
||||
|
||||
//找到 该项目的IQC 用户Id
|
||||
var userList = await _trialUserRepository.Where(t => t.TrialId == trialId).Where(t => t.User.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.User.FullName }).ToListAsync();
|
||||
|
||||
//判断是否任务可以领取 ,可以的话 发送邮件
|
||||
var userIdList = userList.Select(t => t.UserId).ToList();
|
||||
|
||||
foreach (var user in userList)
|
||||
{
|
||||
var userId = user.UserId;
|
||||
|
||||
//过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id
|
||||
var query = _trialRepository.Where(t => t.Id == trialId)
|
||||
.Where(t => t.QCProcessEnum != TrialQCProcess.NotAudit)
|
||||
.Select(t => new
|
||||
{
|
||||
//待领取量
|
||||
ToBeClaimedCount = t.SubjectVisitList.Where(u => u.SubmitState == SubmitStateEnum.Submitted && u.CurrentActionUserId == null && (u.PreliminaryAuditUserId == null || (u.PreliminaryAuditUserId != userId && u.ReviewAuditUserId == null))).Count(),
|
||||
|
||||
//待审核通过,统计从已领取到QC提交之间的 已领取 待审核 审核中 (审核完成 领取人就会清理 所以只用查询当前领取人是自己的就好了)
|
||||
ToBeReviewedCount = t.SubjectVisitList.Where(u => u.CurrentActionUserId == userId).Count()
|
||||
|
||||
});
|
||||
|
||||
var sendStat = await query.FirstOrDefaultAsync();
|
||||
|
||||
//当前人 有待领取的或者有待审核的才发邮件
|
||||
if (sendStat != null && (sendStat.ToBeClaimedCount > 0 || sendStat.ToBeReviewedCount > 0))
|
||||
{
|
||||
|
||||
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc = trialEmailConfig =>
|
||||
{
|
||||
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
|
||||
user.FullName, DateTime.Now, sendStat.ToBeClaimedCount, sendStat.ToBeReviewedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
|
||||
return (topicStr, htmlBodyStr, false, userId);
|
||||
};
|
||||
|
||||
await OldRecurringEmailHelper.SendTrialEmailAsync(
|
||||
_trialEmailNoticeConfigRepository,
|
||||
_trialRepository,
|
||||
_trialUserRepository,
|
||||
_visitTaskRepository,
|
||||
_trialSiteUserRepository,
|
||||
trialId, EmailBusinessScenario.QCTask, topicAndHtmlFunc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -8,50 +8,7 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
public abstract class IRCRecurringSchedule :
|
||||
RecurringSchedule
|
||||
{
|
||||
protected IRCRecurringSchedule()
|
||||
{
|
||||
ScheduleGroup = GetType().Name;
|
||||
|
||||
TimeZoneId = TimeZoneInfo.Local.Id;
|
||||
StartTime = DateTime.Now;
|
||||
}
|
||||
|
||||
public MissedEventPolicy MisfirePolicy { get; protected set; }
|
||||
public string TimeZoneId { get; protected set; }
|
||||
public DateTimeOffset StartTime { get; protected set; }
|
||||
public DateTimeOffset? EndTime { get; protected set; }
|
||||
public string ScheduleId { get; set; }
|
||||
public string ScheduleGroup { get; private set; }
|
||||
public string CronExpression { get; set; }
|
||||
public string Description { get; protected set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// QC 影像质疑待处理
|
||||
/// </summary>
|
||||
public class QCImageQuestionSchedule : IRCRecurringSchedule
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CRC 影像质疑
|
||||
/// </summary>
|
||||
public class CRCImageQuestionSchedule : IRCRecurringSchedule
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 影像质控
|
||||
/// </summary>
|
||||
public class ImageQCSchedule : IRCRecurringSchedule
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class QCImageQuestionScheduleConsumer : IConsumer<QCImageQuestionSchedule>
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
using IRaCIS.Core.Domain.BaseModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// QC 影像质疑待处理
|
||||
/// </summary>
|
||||
public class QCImageQuestionRecurringEvent : DomainEvent
|
||||
{
|
||||
public Guid TrialId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CRC 影像质疑
|
||||
/// </summary>
|
||||
public class CRCImageQuestionRecurringEvent : DomainEvent
|
||||
{
|
||||
public Guid TrialId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 影像质控
|
||||
/// </summary>
|
||||
public class ImageQCRecurringEvent : DomainEvent
|
||||
{
|
||||
public Guid TrialId { get; set; }
|
||||
}
|
||||
|
||||
//加急的待阅任务时,过10分钟,统计10分钟内该IR账号的全部待阅任务
|
||||
public class UrgentIRUnReadTaskRecurringEvent : DomainEvent
|
||||
{
|
||||
public Guid TrialId { get; set; }
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
using IRaCIS.Core.Application.MassTransit.Command;
|
||||
using MassTransit;
|
||||
using MassTransit.Scheduling;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
public abstract class IRCRecurringSchedule :
|
||||
RecurringSchedule
|
||||
{
|
||||
protected IRCRecurringSchedule()
|
||||
{
|
||||
ScheduleGroup = GetType().Name;
|
||||
|
||||
TimeZoneId = TimeZoneInfo.Local.Id;
|
||||
StartTime = DateTime.Now;
|
||||
}
|
||||
|
||||
public MissedEventPolicy MisfirePolicy { get; protected set; }
|
||||
public string TimeZoneId { get; protected set; }
|
||||
public DateTimeOffset StartTime { get; protected set; }
|
||||
public DateTimeOffset? EndTime { get; protected set; }
|
||||
public string ScheduleId { get; set; }
|
||||
public string ScheduleGroup { get; private set; }
|
||||
public string CronExpression { get; set; }
|
||||
public string Description { get; protected set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// QC 影像质疑待处理
|
||||
/// </summary>
|
||||
public class QCImageQuestionSchedule : IRCRecurringSchedule
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CRC 影像质疑
|
||||
/// </summary>
|
||||
public class CRCImageQuestionSchedule : IRCRecurringSchedule
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 影像质控
|
||||
/// </summary>
|
||||
public class ImageQCSchedule : IRCRecurringSchedule
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ namespace IRaCIS.Core.Application.Contracts
|
|||
.Any(t => t.Value.Contains(" ")))
|
||||
{
|
||||
//邮件模板占位符不允许有空格,请核查占位符的地方
|
||||
return ResponseOutput.NotOk("EmailNoticeConfig_ContainEmpty");
|
||||
return ResponseOutput.NotOk(I18n.T("EmailNoticeConfig_ContainEmpty"));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ public class EventStoreRecordService(IRepository<EventStoreRecord> _eventStoreRe
|
|||
var eventStoreRecordQueryable = _eventStoreRecordRepository.WhereIf(inQuery.EventTypeName.IsNotNullOrEmpty(),t=>t.EventTypeName.Contains(inQuery.EventTypeName))
|
||||
.ProjectTo<EventStoreRecordView>(_mapper.ConfigurationProvider);
|
||||
|
||||
var pageList = await eventStoreRecordQueryable.ToPagedListAsync(inQuery);
|
||||
var pageList = await eventStoreRecordQueryable.ToPagedListAsync(inQuery,nameof(EventStoreRecordView.CreateTime));
|
||||
|
||||
return pageList;
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ public class EventStoreRecordService(IRepository<EventStoreRecord> _eventStoreRe
|
|||
/// </summary>
|
||||
/// <param name="eventId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<IResponseOutput> RePublishEvent(Guid eventId)
|
||||
{
|
||||
var storedEvent = await _eventStoreRecordRepository.FirstOrDefaultAsync(t => t.Id == eventId);
|
||||
|
|
|
@ -9,11 +9,7 @@ namespace IRaCIS.Core.Application.Service
|
|||
public interface IEmailSendService
|
||||
{
|
||||
Task SendEnrollOrPdEmail(Guid visitTaskId, bool? isEnrollment, bool? isPDConfirm);
|
||||
Task SendTrialImageQCTaskEmailAsync(Guid trialId);
|
||||
|
||||
Task SendTrialQCQuestionEmailAsync(Guid trialId);
|
||||
Task SendTrialImageQuestionAsync(Guid trialId);
|
||||
|
||||
|
||||
Task<(TrialEmailNoticeConfig?, SMTPEmailConfig?)> BuildEmailConfig(Guid trialId, EmailBusinessScenario businessScenario, Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc, Guid? siteId = null, Guid? trialReadingCriterionId = null);
|
||||
}
|
||||
|
||||
|
@ -101,162 +97,6 @@ namespace IRaCIS.Core.Application.Service
|
|||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 影像质控
|
||||
/// </summary>
|
||||
/// <param name="trialId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task SendTrialImageQCTaskEmailAsync(Guid trialId)
|
||||
{
|
||||
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();
|
||||
|
||||
//找到 该项目的IQC 用户Id
|
||||
var userList = await _trialUserRepository.Where(t => t.TrialId == trialId).Where(t => t.User.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.User.FullName }).ToListAsync();
|
||||
|
||||
//判断是否任务可以领取 ,可以的话 发送邮件
|
||||
var userIdList = userList.Select(t => t.UserId).ToList();
|
||||
|
||||
foreach (var user in userList)
|
||||
{
|
||||
var userId = user.UserId;
|
||||
|
||||
//过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id
|
||||
var query = _trialRepository.Where(t => t.Id == trialId)
|
||||
.Where(t => t.QCProcessEnum != TrialQCProcess.NotAudit)
|
||||
.Select(t => new
|
||||
{
|
||||
//待领取量
|
||||
ToBeClaimedCount = t.SubjectVisitList.Where(u => u.SubmitState == SubmitStateEnum.Submitted && u.CurrentActionUserId == null && (u.PreliminaryAuditUserId == null || (u.PreliminaryAuditUserId != userId && u.ReviewAuditUserId == null))).Count(),
|
||||
|
||||
//待审核通过,统计从已领取到QC提交之间的 已领取 待审核 审核中 (审核完成 领取人就会清理 所以只用查询当前领取人是自己的就好了)
|
||||
ToBeReviewedCount = t.SubjectVisitList.Where(u => u.CurrentActionUserId == userId).Count()
|
||||
|
||||
});
|
||||
|
||||
var sendStat = await query.FirstOrDefaultAsync();
|
||||
|
||||
//当前人 有待领取的或者有待审核的才发邮件
|
||||
if (sendStat != null && (sendStat.ToBeClaimedCount > 0 || sendStat.ToBeReviewedCount > 0))
|
||||
{
|
||||
|
||||
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc = trialEmailConfig =>
|
||||
{
|
||||
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
|
||||
user.FullName, DateTime.Now, sendStat.ToBeClaimedCount, sendStat.ToBeReviewedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
|
||||
return (topicStr, htmlBodyStr, false, userId);
|
||||
};
|
||||
|
||||
await SendTrialEmailAsync(trialId, EmailBusinessScenario.QCTask, topicAndHtmlFunc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// QC质疑
|
||||
/// </summary>
|
||||
/// <param name="trialId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task SendTrialQCQuestionEmailAsync(Guid trialId)
|
||||
{
|
||||
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
|
||||
var userList = await _trialUserRepository.Where(t => t.TrialId == trialId).Where(t => t.User.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.User.FullName }).ToListAsync();
|
||||
|
||||
//判断是否任务可以领取 ,可以的话 发送邮件
|
||||
|
||||
foreach (var user in userList)
|
||||
{
|
||||
var userId = user.UserId;
|
||||
|
||||
//过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id
|
||||
var query = _trialRepository
|
||||
.Where(t => t.Id == trialId)
|
||||
.Select(t => new
|
||||
{
|
||||
ReUploadTobeDealedCount = t.SubjectVisitList.SelectMany(c => c.QCChallengeList)
|
||||
.Where(u => u.CreateUserId == userId && u.IsClosed == false && u.LatestReplyUser.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator && u.ReuploadEnum == QCChanllengeReuploadEnum.CRCRequestReupload).Count(),
|
||||
|
||||
//质疑待处理 发送邮件的时候 需要减去ReUploadTobeDealedCount
|
||||
ToBeDealedCount = t.SubjectVisitList.SelectMany(c => c.QCChallengeList)
|
||||
.Where(u => u.CreateUserId == userId && u.IsClosed == false && u.LatestReplyUser.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Count(),
|
||||
});
|
||||
|
||||
var sendStat = await query.FirstOrDefaultAsync();
|
||||
|
||||
//当前人
|
||||
if (sendStat != null && (sendStat.ToBeDealedCount > 0 || sendStat.ReUploadTobeDealedCount > 0))
|
||||
{
|
||||
|
||||
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc = trialEmailConfig =>
|
||||
{
|
||||
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
|
||||
|
||||
user.FullName, DateTime.Now, sendStat.ToBeDealedCount - sendStat.ReUploadTobeDealedCount, sendStat.ReUploadTobeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
|
||||
|
||||
return (topicStr, htmlBodyStr, false, userId);
|
||||
};
|
||||
|
||||
await SendTrialEmailAsync(trialId, EmailBusinessScenario.CRCToQCQuestion, topicAndHtmlFunc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 影像质疑
|
||||
/// </summary>
|
||||
/// <param name="trialId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task SendTrialImageQuestionAsync(Guid trialId)
|
||||
{
|
||||
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();
|
||||
|
||||
//找到 该项目的CRC 用户Id
|
||||
var userList = await _trialUserRepository.Where(t => t.TrialId == trialId).Where(t => t.User.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Select(t => new { t.UserId, t.User.FullName }).ToListAsync();
|
||||
|
||||
//判断是否任务可以领取 ,可以的话 发送邮件
|
||||
|
||||
foreach (var user in userList)
|
||||
{
|
||||
var userId = user.UserId;
|
||||
//过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id
|
||||
var query = _trialRepository.Where(t => t.Id == trialId)
|
||||
|
||||
.Select(t => new
|
||||
{
|
||||
//质疑待处理
|
||||
ToBeDealedCount = t.SubjectVisitList.Where(t => t.TrialSite.CRCUserList.Any(t => t.UserId == userId)).SelectMany(c => c.QCChallengeList)
|
||||
.Where(u => u.IsClosed == false && (u.LatestReplyUser.UserTypeEnum == UserTypeEnum.IQC || u.LatestReplyUserId == null)).Count(),
|
||||
|
||||
});
|
||||
|
||||
var sendStat = await query.FirstOrDefaultAsync();
|
||||
|
||||
//当前人
|
||||
if (sendStat != null && (sendStat.ToBeDealedCount > 0))
|
||||
{
|
||||
|
||||
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc = trialEmailConfig =>
|
||||
{
|
||||
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN,
|
||||
user.FullName, DateTime.Now, sendStat.ToBeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
|
||||
return (topicStr, htmlBodyStr, isEn_us, userId);
|
||||
};
|
||||
|
||||
await SendTrialEmailAsync(trialId, EmailBusinessScenario.QCToCRCImageQuestion, topicAndHtmlFunc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -685,7 +685,7 @@ namespace IRaCIS.Core.Application.Contracts
|
|||
|
||||
|
||||
|
||||
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.Undefined)
|
||||
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.Undefined || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator)
|
||||
{
|
||||
var hasSPMOrCPM = await _trialSiteSurveyRepository.AnyAsync(t => t.TrialId == trialId && t.Trial.TrialUserList.Any(u => u.User.UserTypeEnum == UserTypeEnum.SPM || u.User.UserTypeEnum == UserTypeEnum.CPM));
|
||||
|
||||
|
|
|
@ -32,5 +32,5 @@ public enum EventStateEnum
|
|||
|
||||
HavePublished = 1,
|
||||
|
||||
ConsumeSuccessed = 3
|
||||
ConsumeSuccessed = 2
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace IRaCIS.Core.Infra.EFCore.Interceptor
|
|||
// 发件箱模式参考: https://dev.to/antonmartyniuk/use-masstransit-to-implement-outbox-pattern-with-ef-core-and-mongodb-oep#:~:text=MongoDB%20replica%20set%20is%20required%20for%20both%20publisher%20and%20consumer
|
||||
// 1、IPublishEndpoint 才会将事件存储到发件箱表中, 高级IBus接口时 - 消息不会存储在发件箱中,必须有savechanges 才会一起提交保存到数据库中
|
||||
// 2、进入消息代理之前,发布事件在OutboxState OutboxMessage, 进入消费者以后(已经删除OutboxState OutboxMessage),消费失败,需要修改代码重新发布,然后之前消费事件的重新处理,错误处理参考:https://www.youtube.com/watch?v=3TMKUu7c4lc
|
||||
public class DispatchDomainEventsInterceptor(IMediator _mediator, IMessageScheduler _scheduler) : SaveChangesInterceptor
|
||||
public class DispatchDomainEventsInterceptor(IMediator _mediator, IMessageScheduler _scheduler/*, IPublishEndpoint _publishEndpoint*/) : SaveChangesInterceptor
|
||||
{
|
||||
|
||||
//领域事件通常与数据变更密切相关。如果在 SaveChanges 之前发布事件,有可能事件发布时的数据状态还没有被持久化到数据库。这可能导致事件消费者看到的是一个不一致的状态
|
||||
|
|
17863
IRaCIS.Core.Infra.EFCore/Migrations/20241017065221_EmailModify.Designer.cs
generated
Normal file
17863
IRaCIS.Core.Infra.EFCore/Migrations/20241017065221_EmailModify.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,40 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace IRaCIS.Core.Infra.EFCore.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class EmailModify : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "EmailDelaySeconds",
|
||||
table: "TrialEmailNoticeConfig",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
comment: "邮件延时秒数,比如一个事件触发,延迟多少s后才发邮件");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "EmailDelaySeconds",
|
||||
table: "EmailNoticeConfig",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
comment: "邮件延时秒数,比如一个事件触发,延迟多少s后才发邮件");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EmailDelaySeconds",
|
||||
table: "TrialEmailNoticeConfig");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EmailDelaySeconds",
|
||||
table: "EmailNoticeConfig");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1817,6 +1817,10 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
|
|||
.HasColumnType("nvarchar(400)")
|
||||
.HasComment("发送周期");
|
||||
|
||||
b.Property<int?>("EmailDelaySeconds")
|
||||
.HasColumnType("int")
|
||||
.HasComment("邮件延时秒数,比如一个事件触发,延迟多少s后才发邮件");
|
||||
|
||||
b.Property<string>("EmailHtmlContent")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
@ -11028,6 +11032,10 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
|
|||
.HasMaxLength(400)
|
||||
.HasColumnType("nvarchar(400)");
|
||||
|
||||
b.Property<int?>("EmailDelaySeconds")
|
||||
.HasColumnType("int")
|
||||
.HasComment("邮件延时秒数,比如一个事件触发,延迟多少s后才发邮件");
|
||||
|
||||
b.Property<string>("EmailHtmlContent")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
|
|
Loading…
Reference in New Issue