diff --git a/IRaCIS.Core.Domain/BaseModel/DomainEvent.cs b/IRaCIS.Core.Domain/BaseModel/DomainEvent.cs
new file mode 100644
index 000000000..f645ebf3e
--- /dev/null
+++ b/IRaCIS.Core.Domain/BaseModel/DomainEvent.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IRaCIS.Core.Domain.BaseModel
+{
+ ///
+ /// 领域实体事件基类
+ ///
+ public abstract class DomainEvent
+ {
+
+ }
+
+ public class FailedDomainEvent
+ {
+ public Guid Id { get; set; }
+ public string EventType { get; set; }
+ public string EventData { get; set; }
+ public DateTime FailedAt { get; set; }
+ }
+}
diff --git a/IRaCIS.Core.Domain/BaseModel/Entity.cs b/IRaCIS.Core.Domain/BaseModel/Entity.cs
index 8482a17d4..d6d45e8da 100644
--- a/IRaCIS.Core.Domain/BaseModel/Entity.cs
+++ b/IRaCIS.Core.Domain/BaseModel/Entity.cs
@@ -1,21 +1,57 @@
-using System;
+using IRaCIS.Core.Domain.BaseModel;
+using System;
+using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
+using System.Security.Cryptography;
namespace IRaCIS.Core.Domain.Models
{
+
+
+ public interface IAggregateRoot;
+ public interface IEntity
+ {
+ abstract TKey Id { get; set; }
+
+ }
+
public abstract class Entity : IEntity
{
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
+
+ #region 领域事件 仅仅允许通过提供的方法进行操作
+
+ private readonly List _domainEvents = [];
+
+ [NotMapped]
+ public IReadOnlyCollection DomainEvents => _domainEvents.AsReadOnly();
+
+
+ public void AddDomainEvent(DomainEvent domainEvent)
+ {
+ _domainEvents.Add(domainEvent);
+ }
+
+ public void RemoveDomainEvent(DomainEvent domainEvent)
+ {
+ _domainEvents.Remove(domainEvent);
+ }
+
+ public void ClearDomainEvents()
+ {
+ _domainEvents.Clear();
+ }
+ #endregion
+
+
}
- public interface IEntity
- {
- abstract TKey Id { get; set; }
- }
+
+
#region 减少实体属性,增加基类
diff --git a/IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj b/IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj
index 9b2ef5d9c..bbedc4058 100644
--- a/IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj
+++ b/IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj
@@ -25,7 +25,6 @@
-
diff --git a/IRaCIS.Core.Infra.EFCore/Interceptor/AuditEntityInterceptor.cs b/IRaCIS.Core.Infra.EFCore/Interceptor/AuditEntityInterceptor.cs
index b2f99decd..b8235e0e8 100644
--- a/IRaCIS.Core.Infra.EFCore/Interceptor/AuditEntityInterceptor.cs
+++ b/IRaCIS.Core.Infra.EFCore/Interceptor/AuditEntityInterceptor.cs
@@ -12,13 +12,14 @@ namespace IRaCIS.Core.Infra.EFCore;
public class AuditEntityInterceptor(IUserInfo _userInfo) : SaveChangesInterceptor
{
- public override InterceptionResult SavingChanges(DbContextEventData eventData, InterceptionResult result)
- {
- AuditEntities(eventData.Context);
-
- return base.SavingChanges(eventData, result);
- }
+ ///
+ /// 在事务提交之前执行
+ ///
+ ///
+ ///
+ ///
+ ///
public override ValueTask> SavingChangesAsync(DbContextEventData eventData,
InterceptionResult result, CancellationToken cancellationToken = default)
{
@@ -26,7 +27,12 @@ public class AuditEntityInterceptor(IUserInfo _userInfo) : SaveChangesIntercepto
return base.SavingChangesAsync(eventData, result, cancellationToken);
}
+ public override InterceptionResult SavingChanges(DbContextEventData eventData, InterceptionResult result)
+ {
+ AuditEntities(eventData.Context);
+ return base.SavingChanges(eventData, result);
+ }
public void AuditEntities(DbContext? context)
{
if (context == null) return;
diff --git a/IRaCIS.Core.Infra.EFCore/Interceptor/DispatchDomainEventsInterceptor.cs b/IRaCIS.Core.Infra.EFCore/Interceptor/DispatchDomainEventsInterceptor.cs
new file mode 100644
index 000000000..e12a02704
--- /dev/null
+++ b/IRaCIS.Core.Infra.EFCore/Interceptor/DispatchDomainEventsInterceptor.cs
@@ -0,0 +1,59 @@
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using IRaCIS.Core.Domain.Models;
+using MassTransit;
+
+namespace IRaCIS.Core.Infra.EFCore.Interceptor
+{
+ public class DispatchDomainEventsInterceptor(IPublishEndpoint publishEndpoint) : SaveChangesInterceptor
+ {
+
+ //领域事件通常与数据变更密切相关。如果在 SaveChanges 之前发布事件,有可能事件发布时的数据状态还没有被持久化到数据库。这可能导致事件消费者看到的是一个不一致的状态
+
+ ///
+ /// 在事务提交之后分发事件
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override async ValueTask SavedChangesAsync(SaveChangesCompletedEventData eventData, int result,
+ CancellationToken cancellationToken = default)
+ {
+ await DispatchDomainEvents(eventData.Context);
+ return await base.SavedChangesAsync(eventData, result, cancellationToken);
+ }
+ public override int SavedChanges(SaveChangesCompletedEventData eventData, int result)
+ {
+ DispatchDomainEvents(eventData.Context).GetAwaiter().GetResult();
+ return base.SavedChanges(eventData, result);
+ }
+ private async Task DispatchDomainEvents(DbContext? context)
+ {
+ if (context == null) return;
+
+ var entities = context.ChangeTracker
+ .Entries()
+ .Where(e => e.Entity.DomainEvents.Any())
+ .Select(e => e.Entity)
+ .ToList();
+
+ var domainEvents = entities
+ .SelectMany(e => e.DomainEvents)
+ .ToList();
+
+ entities.ForEach(e => e.ClearDomainEvents());
+
+ foreach (var domainEvent in domainEvents)
+ {
+ await publishEndpoint.Publish(domainEvent);
+ }
+ }
+ }
+}
diff --git a/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj b/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj
index 324ac91a0..39d3b1d66 100644
--- a/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj
+++ b/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj
@@ -11,6 +11,7 @@
+