📁 Domain/Events/Hikvision/FaceRecognitionDomainEvent.cs
using Domain.Common;
using MediatR;
namespace Domain.Events.Hikvision
{
public class FaceRecognitionDomainEvent : BaseEvent, INotification
{
public string DeviceId { get; }
public string PersonId { get; }
public string FaceImageUrl { get; }
public double Confidence { get; }
public DateTime RecognizedAt { get; }
public FaceRecognitionDomainEvent(string deviceId, string personId,
string faceImageUrl, double confidence, DateTime recognizedAt)
{
DeviceId = deviceId;
PersonId = personId;
FaceImageUrl = faceImageUrl;
Confidence = confidence;
RecognizedAt = recognizedAt;
}
}
}📁 Shared/Contracts/IntegrationEvents/Hikvision/FaceRecognitionIntegrationEvent.cs
using Shared.Common;
namespace Shared.IntegrationEvents.Contracts.Hikvision
{
public class FaceRecognitionIntegrationEvent : IntegrationEvent
{
public string DeviceId { get; set; } = string.Empty;
public string PersonId { get; set; } = string.Empty;
public string FaceImageUrl { get; set; } = string.Empty;
public double Confidence { get; set; }
public DateTime RecognizedAt { get; set; }
}
}📁 Application/EventHandlers/Hikvision/FaceRecognitionDomainEventHandler.cs
using Application.Abstractions.Common;
using Domain.Events.Hikvision;
using MediatR;
using Shared.IntegrationEvents.Contracts.Hikvision;
namespace Application.EventHandlers.Hikvision
{
public class FaceRecognitionDomainEventHandler
: INotificationHandler<FaceRecognitionDomainEvent>
{
private readonly IUnitOfWork _uow;
public FaceRecognitionDomainEventHandler(IUnitOfWork uow)
{
_uow = uow;
}
public async Task Handle(FaceRecognitionDomainEvent notification,
CancellationToken cancellationToken)
{
var integrationEvent = new FaceRecognitionIntegrationEvent
{
DeviceId = notification.DeviceId,
PersonId = notification.PersonId,
FaceImageUrl = notification.FaceImageUrl,
Confidence = notification.Confidence,
RecognizedAt = notification.RecognizedAt
};
await _uow.AddIntegrationEventToOutboxAsync(integrationEvent);
}
}
}📁 Worker/Consumers/Hikvision/FaceRecognitionConsumer.cs
using Infrastructure.Consumers.Common;
using MassTransit;
using Microsoft.Extensions.Logging;
using Shared.IntegrationEvents.Contracts.Hikvision;
namespace Infrastructure.Consumers.Hikvision
{
public class FaceRecognitionConsumer : TConsumer<FaceRecognitionIntegrationEvent>
{
public FaceRecognitionConsumer(ILogger<FaceRecognitionConsumer> logger)
: base(logger) { }
protected override async Task ProcessMessageAsync(
ConsumeContext<FaceRecognitionIntegrationEvent> context)
{
var msg = context.Message;
Logger.LogInformation(
"Face Recognition | Device: {DeviceId}, Person: {PersonId}, Confidence: {Confidence}%",
msg.DeviceId, msg.PersonId, msg.Confidence * 100);
// TODO: Implement business logic
// - Verify confidence threshold
// - Update attendance system
// - Send notification if VIP
// - Store face image to blob storage
await Task.CompletedTask;
}
}
}📁 Shared/appsettings.Shared.json
{
"RabbitMq": {
"Exchanges": {
// ... existing exchanges ...
"HikvisionFaceRecognition": {
"Name": "hikvision.face.recognition.exchange",
"Type": "topic",
"RoutingKey": "hikvision.face.#",
"Queue": "hikvision.face.recognition.queue"
}
}
}
}📁 Worker/Program.cs
// Thêm vào services.AddMassTransit(x => { ... })
x.AddConsumer<FaceRecognitionConsumer>();
// Thêm vào switch case trong MassTransitConfig.AddMassTransitConsumers()
case "HikvisionFaceRecognition":
endpoint.ConfigureConsumer<FaceRecognitionConsumer>(context);
break;public class AccessControlService : IAccessControlService
{
private readonly IUnitOfWork _uow;
private readonly IMediator _mediator;
public async Task<Result> RecordFaceRecognitionAsync(FaceRecognitionRequest request)
{
await _uow.BeginTransactionAsync();
// Business logic: Save to database
// ...
// Raise domain event
var domainEvent = new FaceRecognitionDomainEvent(
request.DeviceId,
request.PersonId,
request.FaceImageUrl,
request.Confidence,
DateTime.UtcNow
);
// MediatR sẽ trigger handler → Add to Outbox
await _mediator.Publish(domainEvent);
await _uow.CommitTransactionAsync();
return Result.Success();
}
}[FaceRecognitionIntegrationEvent] Processing started | MessageId: abc123
Face Recognition | Device: CAM-01, Person: EMP-456, Confidence: 98.5%
[FaceRecognitionIntegrationEvent] Processing completed | Duration: 12ms
- Domain Event created (kế thừa
BaseEvent, INotification) - Integration Event created (kế thừa
IntegrationEvent) - Domain Event Handler implemented (
INotificationHandler<T>) - Consumer created (kế thừa
TConsumer<T>) - Exchange & Queue added to
appsettings.Shared.json - Consumer registered in
Worker/Program.cs - Business logic implemented in
ProcessMessageAsync() - Tested end-to-end
SELECT * FROM OutboxMessages
WHERE Type LIKE '%FaceRecognition%'
ORDER BY OccurredOn DESC;URL: http://localhost:15672
Username: guest
Password: guest
Navigate to: Queues → hikvision.face.recognition.queue
Check: Message rates, Consumer count
📁 appsettings.Development.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Infrastructure.Consumers": "Debug",
"MassTransit": "Debug"
}
}
}Nếu cần xử lý nhiều events cùng lúc:
public class FaceRecognitionConsumer : TConsumer<FaceRecognitionIntegrationEvent>
{
private static readonly List<FaceRecognitionIntegrationEvent> _batch = new();
private static readonly SemaphoreSlim _lock = new(1);
protected override async Task ProcessMessageAsync(...)
{
await _lock.WaitAsync();
try
{
_batch.Add(msg);
if (_batch.Count >= 10) // Batch size
{
await ProcessBatchAsync(_batch);
_batch.Clear();
}
}
finally
{
_lock.Release();
}
}
}Cho critical events (VIP face recognition):
{
"HikvisionFaceRecognitionVIP": {
"Name": "hikvision.face.vip.exchange",
"Type": "direct",
"RoutingKey": "vip",
"Queue": "hikvision.face.vip.queue",
"Priority": 10
}
}Trong Domain Event Handler:
public async Task Handle(FaceRecognitionDomainEvent notification, ...)
{
// Route to VIP queue if confidence > 95%
if (notification.Confidence > 0.95)
{
var vipEvent = new FaceRecognitionVIPIntegrationEvent { ... };
await _uow.AddIntegrationEventToOutboxAsync(vipEvent);
}
else
{
var normalEvent = new FaceRecognitionIntegrationEvent { ... };
await _uow.AddIntegrationEventToOutboxAsync(normalEvent);
}
}- RABBITMQ_ARCHITECTURE.md - Full architecture guide
- KAFKA_VS_RABBITMQ.md - Why RabbitMQ?
- MassTransit Docs
- RabbitMQ Tutorials
Happy Coding! 🚀