事件存储准备

IRC_NewDev
hang 2024-10-10 14:24:56 +08:00
parent 2527d709fa
commit 2c1300acb4
13 changed files with 18205 additions and 12 deletions

View File

@ -12727,6 +12727,21 @@
<param name="_trialRepository"></param> <param name="_trialRepository"></param>
<param name="_mapper"></param> <param name="_mapper"></param>
</member> </member>
<member name="T:IRaCIS.Core.Application.MassTransit.Consumer.UserSiteSurveySubmitedEventConsumer">
<summary>
用户提交 发送邮件 通知SPM 或者PM
</summary>
</member>
<member name="T:IRaCIS.Core.Application.MassTransit.Consumer.SiteSurveySPMSubmitedEventConsumer">
<summary>
调研表初审通过,进行复审发送邮件
</summary>
</member>
<member name="T:IRaCIS.Core.Application.MassTransit.Consumer.SiteSurverRejectedEventConsumer">
<summary>
调研表驳回发送邮件 之前已有,需要迁移过来
</summary>
</member>
<member name="T:IRaCIS.Core.Application.MassTransit.Consumer.MediatorHttpContextScopeFilterExtensions"> <member name="T:IRaCIS.Core.Application.MassTransit.Consumer.MediatorHttpContextScopeFilterExtensions">
<summary> <summary>
参考链接https://github.com/MassTransit/MassTransit/discussions/2498 参考链接https://github.com/MassTransit/MassTransit/discussions/2498

View File

@ -0,0 +1,48 @@
using IRaCIS.Core.Application.MassTransit.Command;
using IRaCIS.Core.Domain;
using IRaCIS.Core.Domain.BaseModel;
using MassTransit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.MassTransit.Consumer
{
/// <summary>
/// 用户提交 发送邮件 通知SPM 或者PM
/// </summary>
public class UserSiteSurveySubmitedEventConsumer : IConsumer<UserSiteSurveySubmitedEvent>
{
public Task Consume(ConsumeContext<UserSiteSurveySubmitedEvent> context)
{
throw new NotImplementedException();
}
}
/// <summary>
/// 调研表初审通过,进行复审发送邮件
/// </summary>
public class SiteSurveySPMSubmitedEventConsumer : IConsumer<SiteSurveySPMSubmitedEvent>
{
public Task Consume(ConsumeContext<SiteSurveySPMSubmitedEvent> context)
{
throw new NotImplementedException();
}
}
/// <summary>
/// 调研表驳回发送邮件 之前已有,需要迁移过来
/// </summary>
public class SiteSurverRejectedEventConsumer : IConsumer<SiteSurverRejectedEvent>
{
public Task Consume(ConsumeContext<SiteSurverRejectedEvent> context)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,4 +1,5 @@
using System.ComponentModel; using IRaCIS.Core.Domain.Models;
using System.ComponentModel;
namespace IRaCIS.Core.Domain.BaseModel; namespace IRaCIS.Core.Domain.BaseModel;
@ -11,6 +12,7 @@ namespace IRaCIS.Core.Domain.BaseModel;
public abstract class DomainEvent public abstract class DomainEvent
{ {
} }
/// <summary> /// <summary>
@ -29,3 +31,4 @@ public class FailedDomainEvent
public string EventData { get; set; } = string.Empty; public string EventData { get; set; } = string.Empty;
public DateTime FailedAt { get; set; } public DateTime FailedAt { get; set; }
} }

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Domain.Models;
[Comment("记录触发的事件,以及状态,从而方便重试操作")]
[Table("EventStoreRecord")]
public class EventStoreRecord : BaseFullAuditEntity
{
public string EventType { get; set; }
[MaxLength]
public string EventData { get; set; }
public EventStateEnum EventState { get; set; } = EventStateEnum.HavePublished;
}
public enum EventStateEnum
{
None = 0,
HavePublished = 1,
ConsumeSuccessed = 3
}

View File

@ -19,8 +19,4 @@
<PackageReference Include="EntityFrameworkCore.Projectables.Abstractions" Version="3.0.4" /> <PackageReference Include="EntityFrameworkCore.Projectables.Abstractions" Version="3.0.4" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="_DomainEvent\" />
</ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,27 @@
using IRaCIS.Core.Domain.BaseModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Domain
{
public class UserSiteSurveySubmitedEvent:DomainEvent
{
public Guid TrialSiteSurveyId { get; set; }
}
public class SiteSurveySPMSubmitedEvent : DomainEvent
{
public Guid TrialSiteSurveyId { get; set; }
}
public class SiteSurverRejectedEvent : DomainEvent
{
public Guid TrialSiteSurveyId { get; set; }
}
}

View File

@ -28,4 +28,24 @@ public class NoForeignKeyMigrationsSqlGenerator : SqlServerMigrationsSqlGenerato
} }
protected override void Generate(DropForeignKeyOperation operation, IModel? model, MigrationCommandListBuilder builder, bool terminate = true)
{
// 忽略所有删除外键的操作
// 不调用 base.Generate 来跳过生成删除外键的SQL
}
////忽略掉唯一约束因为dicom Id 的问题
//protected override void Generate(CreateIndexOperation operation, IModel? model, MigrationCommandListBuilder builder, bool terminate = true)
//{
// // 如果索引是唯一的则忽略不生成SQL语句
// if (operation.IsUnique)
// {
// return; // 跳过唯一约束的索引创建
// }
// // 如果不是唯一索引,则继续生成
// base.Generate(operation, model, builder, terminate);
//}
} }

View File

@ -1,4 +1,5 @@
using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Domain.BaseModel;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infra.EFCore.Common; using IRaCIS.Core.Infra.EFCore.Common;
using IRaCIS.Core.Infrastructure.Encryption; using IRaCIS.Core.Infrastructure.Encryption;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
@ -558,7 +559,7 @@ public class IRaCISDBContext : DbContext
public virtual DbSet<TestLength> TestLength { get; set; } public virtual DbSet<TestLength> TestLength { get; set; }
public virtual DbSet<EventStoreRecord> EventStoreRecord { get; set; }

View File

@ -1,8 +1,12 @@
 
using IRaCIS.Core.Domain; using IRaCIS.Core.Domain;
using IRaCIS.Core.Domain.BaseModel;
using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using Newtonsoft.Json;
namespace IRaCIS.Core.Infra.EFCore; namespace IRaCIS.Core.Infra.EFCore;
@ -12,17 +16,77 @@ public static class DBContext_Ext
{ {
public static void AddDomainEvents(this DbContext context) public static void AddDomainEvents(this DbContext context)
{ {
var dbContext = (IRaCISDBContext)context;
var changeTracker = context.ChangeTracker; var changeTracker = context.ChangeTracker;
//记录要保存到数据库的事件
var eventStoreList = new List<EventStoreRecord>();
#region 中心调研邮件通知
foreach (var entry in changeTracker.Entries<TrialSiteSurvey>())
{
var trialSiteSurvey = entry.Entity;
var originState = entry.Property(p => p.State).OriginalValue;
//状态从待提交 变为CRC提交 (驳回也会变为已提交,所以必须设置前置状态是待提交)
if (trialSiteSurvey.State == TrialSiteSurveyEnum.CRCSubmitted && originState == TrialSiteSurveyEnum.ToSubmit)
{
trialSiteSurvey.AddDomainEvent(new UserSiteSurveySubmitedEvent() { TrialSiteSurveyId = trialSiteSurvey.Id });
}
//SPM CPM 提交的时候
else if (trialSiteSurvey.State == TrialSiteSurveyEnum.SPMApproved && originState == TrialSiteSurveyEnum.CRCSubmitted)
{
trialSiteSurvey.AddDomainEvent(new SiteSurveySPMSubmitedEvent() { TrialSiteSurveyId = trialSiteSurvey.Id });
}
//PM 驳回 (在消费者中具体判断 是驳回给谁发邮件)
else if ((trialSiteSurvey.State == TrialSiteSurveyEnum.CRCSubmitted || trialSiteSurvey.State == TrialSiteSurveyEnum.ToSubmit) && originState == TrialSiteSurveyEnum.SPMApproved)
{
trialSiteSurvey.AddDomainEvent(new SiteSurverRejectedEvent() { TrialSiteSurveyId = trialSiteSurvey.Id });
}
//添加进记录
eventStoreList.AddRange(trialSiteSurvey.DomainEvents.Select(t => new EventStoreRecord() { EventType = t.GetType().Name, EventData = t.ToJsonStr() }));
} }
#endregion
#region PD 入组发送邮件(不侵入之前的代码,有些判断过于复杂的,在代码里面有的,就手动 在跟踪的实体添加领域事件)
foreach (var entry in changeTracker.Entries<SubjectVisit>())
{
}
#endregion
//跟随事务一起保存数据库
dbContext.EventStoreRecord.AddRangeAsync(eventStoreList);
}
#region 暂时废弃
/// <summary>
/// 暂时废弃,没有场景使用
/// </summary>
/// <param name="context"></param>
public static void AddDomainCommands(this DbContext context) public static void AddDomainCommands(this DbContext context)
{ {
var changeTracker = context.ChangeTracker; var changeTracker = context.ChangeTracker;
//遍历 ChangeTracker 中的实体 //遍历 ChangeTracker 中的实体
//foreach (var entry in changeTracker.Entries<UserLog>()) //foreach (var entry in changeTracker.Entries<UserLog>())
//{ //{
@ -51,5 +115,8 @@ public static class DBContext_Ext
//} //}
} }
#endregion
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,153 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace IRaCIS.Core.Infra.EFCore.Migrations
{
/// <inheritdoc />
public partial class EventStore : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_ReadingTableAnswerRowInfo_DicomInstance_InstanceId",
table: "ReadingTableAnswerRowInfo");
migrationBuilder.DropForeignKey(
name: "FK_SubjectCriteriaEvaluationVisitStudyFilter_DicomSeries_SeriesId",
table: "SubjectCriteriaEvaluationVisitStudyFilter");
migrationBuilder.DropForeignKey(
name: "FK_SubjectCriteriaEvaluationVisitStudyFilter_DicomStudy_StudyId",
table: "SubjectCriteriaEvaluationVisitStudyFilter");
migrationBuilder.AlterColumn<Guid>(
name: "JudgeResultTaskId",
table: "VisitTask",
type: "uniqueidentifier",
nullable: true,
comment: "裁判结果的任务ID访视或者全局",
oldClrType: typeof(Guid),
oldType: "uniqueidentifier",
oldNullable: true,
oldComment: "裁判结果的任务ID");
migrationBuilder.AddUniqueConstraint(
name: "AK_DicomInstance_Id",
table: "DicomInstance",
column: "Id");
migrationBuilder.CreateTable(
name: "EventStoreRecord",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
EventType = table.Column<string>(type: "nvarchar(400)", maxLength: 400, nullable: false),
EventData = table.Column<string>(type: "nvarchar(max)", nullable: false),
EventState = table.Column<int>(type: "int", nullable: false),
CreateUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
CreateTime = table.Column<DateTime>(type: "datetime2", nullable: false),
UpdateUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
UpdateTime = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_EventStoreRecord", x => x.Id);
table.ForeignKey(
name: "FK_EventStoreRecord_User_CreateUserId",
column: x => x.CreateUserId,
principalTable: "User",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
},
comment: "记录触发的事件,以及状态,从而方便重试操作");
migrationBuilder.CreateIndex(
name: "IX_EventStoreRecord_CreateUserId",
table: "EventStoreRecord",
column: "CreateUserId");
migrationBuilder.AddForeignKey(
name: "FK_ReadingTableAnswerRowInfo_DicomInstance_InstanceId",
table: "ReadingTableAnswerRowInfo",
column: "InstanceId",
principalTable: "DicomInstance",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_SubjectCriteriaEvaluationVisitStudyFilter_DicomSeries_SeriesId",
table: "SubjectCriteriaEvaluationVisitStudyFilter",
column: "SeriesId",
principalTable: "DicomSeries",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_SubjectCriteriaEvaluationVisitStudyFilter_DicomStudy_StudyId",
table: "SubjectCriteriaEvaluationVisitStudyFilter",
column: "StudyId",
principalTable: "DicomStudy",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_ReadingTableAnswerRowInfo_DicomInstance_InstanceId",
table: "ReadingTableAnswerRowInfo");
migrationBuilder.DropForeignKey(
name: "FK_SubjectCriteriaEvaluationVisitStudyFilter_DicomSeries_SeriesId",
table: "SubjectCriteriaEvaluationVisitStudyFilter");
migrationBuilder.DropForeignKey(
name: "FK_SubjectCriteriaEvaluationVisitStudyFilter_DicomStudy_StudyId",
table: "SubjectCriteriaEvaluationVisitStudyFilter");
migrationBuilder.DropTable(
name: "EventStoreRecord");
migrationBuilder.DropUniqueConstraint(
name: "AK_DicomInstance_Id",
table: "DicomInstance");
migrationBuilder.AlterColumn<Guid>(
name: "JudgeResultTaskId",
table: "VisitTask",
type: "uniqueidentifier",
nullable: true,
comment: "裁判结果的任务ID",
oldClrType: typeof(Guid),
oldType: "uniqueidentifier",
oldNullable: true,
oldComment: "裁判结果的任务ID访视或者全局");
migrationBuilder.AddForeignKey(
name: "FK_ReadingTableAnswerRowInfo_DicomInstance_InstanceId",
table: "ReadingTableAnswerRowInfo",
column: "InstanceId",
principalTable: "DicomInstance",
principalColumn: "SeqId");
migrationBuilder.AddForeignKey(
name: "FK_SubjectCriteriaEvaluationVisitStudyFilter_DicomSeries_SeriesId",
table: "SubjectCriteriaEvaluationVisitStudyFilter",
column: "SeriesId",
principalTable: "DicomSeries",
principalColumn: "SeqId",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_SubjectCriteriaEvaluationVisitStudyFilter_DicomStudy_StudyId",
table: "SubjectCriteriaEvaluationVisitStudyFilter",
column: "StudyId",
principalTable: "DicomStudy",
principalColumn: "SeqId",
onDelete: ReferentialAction.Cascade);
}
}
}

View File

@ -2109,6 +2109,45 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
}); });
}); });
modelBuilder.Entity("IRaCIS.Core.Domain.Models.EventStoreRecord", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreateTime")
.HasColumnType("datetime2");
b.Property<Guid>("CreateUserId")
.HasColumnType("uniqueidentifier");
b.Property<string>("EventData")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("EventState")
.HasColumnType("int");
b.Property<string>("EventType")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<DateTime>("UpdateTime")
.HasColumnType("datetime2");
b.Property<Guid>("UpdateUserId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("CreateUserId");
b.ToTable("EventStoreRecord", t =>
{
t.HasComment("记录触发的事件,以及状态,从而方便重试操作");
});
});
modelBuilder.Entity("IRaCIS.Core.Domain.Models.ExchangeRate", b => modelBuilder.Entity("IRaCIS.Core.Domain.Models.ExchangeRate", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -13099,7 +13138,7 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
b.Property<Guid?>("JudgeResultTaskId") b.Property<Guid?>("JudgeResultTaskId")
.HasColumnType("uniqueidentifier") .HasColumnType("uniqueidentifier")
.HasComment("裁判结果的任务ID"); .HasComment("裁判结果的任务ID(访视或者全局)");
b.Property<Guid?>("JudgeVisitTaskId") b.Property<Guid?>("JudgeVisitTaskId")
.HasColumnType("uniqueidentifier") .HasColumnType("uniqueidentifier")
@ -13987,6 +14026,17 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
b.Navigation("Enroll"); b.Navigation("Enroll");
}); });
modelBuilder.Entity("IRaCIS.Core.Domain.Models.EventStoreRecord", b =>
{
b.HasOne("IRaCIS.Core.Domain.Models.User", "CreateUser")
.WithMany()
.HasForeignKey("CreateUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CreateUser");
});
modelBuilder.Entity("IRaCIS.Core.Domain.Models.ExchangeRate", b => modelBuilder.Entity("IRaCIS.Core.Domain.Models.ExchangeRate", b =>
{ {
b.HasOne("IRaCIS.Core.Domain.Models.User", "CreateUser") b.HasOne("IRaCIS.Core.Domain.Models.User", "CreateUser")
@ -14971,8 +15021,9 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
.IsRequired(); .IsRequired();
b.HasOne("IRaCIS.Core.Domain.Models.DicomInstance", "Instance") b.HasOne("IRaCIS.Core.Domain.Models.DicomInstance", "Instance")
.WithMany() .WithMany("ReadingTableAnswerRowInfoList")
.HasForeignKey("InstanceId"); .HasForeignKey("InstanceId")
.HasPrincipalKey("Id");
b.HasOne("IRaCIS.Core.Domain.Models.ReadingTableAnswerRowInfo", "MergeRow") b.HasOne("IRaCIS.Core.Domain.Models.ReadingTableAnswerRowInfo", "MergeRow")
.WithMany() .WithMany()
@ -15628,12 +15679,14 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
b.HasOne("IRaCIS.Core.Domain.Models.DicomSeries", "Series") b.HasOne("IRaCIS.Core.Domain.Models.DicomSeries", "Series")
.WithMany("SubjectCriteriaEvaluationVisitStudyFilterList") .WithMany("SubjectCriteriaEvaluationVisitStudyFilterList")
.HasForeignKey("SeriesId") .HasForeignKey("SeriesId")
.HasPrincipalKey("Id")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("IRaCIS.Core.Domain.Models.DicomStudy", "Study") b.HasOne("IRaCIS.Core.Domain.Models.DicomStudy", "Study")
.WithMany("SubjectCriteriaEvaluationVisitStudyFilterList") .WithMany("SubjectCriteriaEvaluationVisitStudyFilterList")
.HasForeignKey("StudyId") .HasForeignKey("StudyId")
.HasPrincipalKey("Id")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
@ -17310,6 +17363,11 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
b.Navigation("TrialClinicalQuestionList"); b.Navigation("TrialClinicalQuestionList");
}); });
modelBuilder.Entity("IRaCIS.Core.Domain.Models.DicomInstance", b =>
{
b.Navigation("ReadingTableAnswerRowInfoList");
});
modelBuilder.Entity("IRaCIS.Core.Domain.Models.DicomSeries", b => modelBuilder.Entity("IRaCIS.Core.Domain.Models.DicomSeries", b =>
{ {
b.Navigation("DicomInstanceList"); b.Navigation("DicomInstanceList");