diff --git a/BMA.EHR.Insignia/BMA.EHR.Insignia.csproj b/BMA.EHR.Insignia/BMA.EHR.Insignia.csproj
index 93282da5..5903ceb7 100644
--- a/BMA.EHR.Insignia/BMA.EHR.Insignia.csproj
+++ b/BMA.EHR.Insignia/BMA.EHR.Insignia.csproj
@@ -41,6 +41,7 @@
+
diff --git a/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs b/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs
index 29cab3d2..dbb553ee 100644
--- a/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs
+++ b/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs
@@ -13,6 +13,8 @@ using BMA.EHR.Domain.ModelsExam.Candidate;
using BMA.EHR.Domain.Shared;
using BMA.EHR.Infrastructure.Persistence;
using BMA.EHR.Insignia.Service.Requests;
+using BMA.EHR.Insignia.Service.Services;
+using Hangfire.Processing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -51,6 +53,8 @@ namespace BMA.EHR.Insignia.Service.Controllers
private readonly IConfiguration _configuration;
private readonly PermissionRepository _permission;
+ private readonly IBackgroundTaskQueue _queue;
+
///
///
///
@@ -73,7 +77,8 @@ namespace BMA.EHR.Insignia.Service.Controllers
InsigniaPeriodsRepository insigniaPeriodRepository,
InsigniaReportRepository insigniaReportRepository,
IConfiguration configuration,
- PermissionRepository permission)
+ PermissionRepository permission,
+ IBackgroundTaskQueue queue)
{
_context = context;
_documentService = documentService;
@@ -86,6 +91,8 @@ namespace BMA.EHR.Insignia.Service.Controllers
_configuration = configuration;
_permission = permission;
_insigniaReportRepository = insigniaReportRepository;
+
+ _queue = queue;
}
#region " Properties "
@@ -626,6 +633,17 @@ namespace BMA.EHR.Insignia.Service.Controllers
return Success();
}
+ [HttpGet("bg/{type}/{insigniaPeriodId:length(36)}")]
+ public async Task> BackgroundCalculateInsigniaRequestByTypeAsync(string type, Guid insigniaPeriodId)
+ {
+ await _queue.QueueBackgroundWorkItemAsync(async token =>
+ {
+ // Logic งาน background จริงเช่น:
+ //await LongRunningProcess(jobInfo, token);
+ });
+ return Success("Background job started.");
+ }
+
///
/// คำนวณราชชื่อผู้ได้รับเครื่องราช (แยกตาม officer, employee)
///
diff --git a/BMA.EHR.Insignia/Program.cs b/BMA.EHR.Insignia/Program.cs
index 2b8f4537..cd3684fe 100644
--- a/BMA.EHR.Insignia/Program.cs
+++ b/BMA.EHR.Insignia/Program.cs
@@ -5,6 +5,7 @@ using BMA.EHR.Infrastructure;
using BMA.EHR.Infrastructure.Persistence;
using BMA.EHR.Insignia.Service;
using BMA.EHR.Insignia.Service.Filters;
+using BMA.EHR.Insignia.Service.Services;
using Hangfire;
using Hangfire.Common;
using Hangfire.MySql;
@@ -26,7 +27,6 @@ var builder = WebApplication.CreateBuilder(args);
var issuer = builder.Configuration["Jwt:Issuer"];
var key = builder.Configuration["Jwt:Key"];
-
IdentityModelEventSource.ShowPII = true;
builder.Services.AddHttpContextAccessor();
@@ -88,6 +88,9 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.AddLeaveApplication();
builder.Services.AddLeavePersistence(builder.Configuration);
+ builder.Services.AddSingleton();
+ builder.Services.AddHostedService();
+
builder.Services.AddControllers(options =>
diff --git a/BMA.EHR.Insignia/Services/BackgroundTaskQueue.cs b/BMA.EHR.Insignia/Services/BackgroundTaskQueue.cs
new file mode 100644
index 00000000..31805486
--- /dev/null
+++ b/BMA.EHR.Insignia/Services/BackgroundTaskQueue.cs
@@ -0,0 +1,26 @@
+using System.Collections.Concurrent;
+
+namespace BMA.EHR.Insignia.Service.Services
+{
+ public class BackgroundTaskQueue : IBackgroundTaskQueue
+ {
+ private readonly ConcurrentQueue> _workItems = new();
+ private readonly SemaphoreSlim _signal = new(0);
+
+ public ValueTask QueueBackgroundWorkItemAsync(Func workItem)
+ {
+ if (workItem == null)
+ throw new ArgumentNullException(nameof(workItem));
+ _workItems.Enqueue(workItem);
+ _signal.Release();
+ return ValueTask.CompletedTask;
+ }
+
+ public async ValueTask> DequeueAsync(CancellationToken cancellationToken)
+ {
+ await _signal.WaitAsync(cancellationToken);
+ _workItems.TryDequeue(out var workItem);
+ return workItem!;
+ }
+ }
+}
diff --git a/BMA.EHR.Insignia/Services/IBackgroundTaskQueue.cs b/BMA.EHR.Insignia/Services/IBackgroundTaskQueue.cs
new file mode 100644
index 00000000..7259a5fb
--- /dev/null
+++ b/BMA.EHR.Insignia/Services/IBackgroundTaskQueue.cs
@@ -0,0 +1,8 @@
+namespace BMA.EHR.Insignia.Service.Services
+{
+ public interface IBackgroundTaskQueue
+ {
+ ValueTask QueueBackgroundWorkItemAsync(Func workItem);
+ ValueTask> DequeueAsync(CancellationToken cancellationToken);
+ }
+}
diff --git a/BMA.EHR.Insignia/Services/InsigniaRequestProcessService.cs b/BMA.EHR.Insignia/Services/InsigniaRequestProcessService.cs
new file mode 100644
index 00000000..2894a375
--- /dev/null
+++ b/BMA.EHR.Insignia/Services/InsigniaRequestProcessService.cs
@@ -0,0 +1,153 @@
+using Microsoft.Extensions.Hosting;
+using Quobject.SocketIoClientDotNet.Client;
+using Sentry;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using System;
+
+namespace BMA.EHR.Insignia.Service.Services;
+
+public class InsigniaRequestProcessService : BackgroundService
+{
+ private readonly IBackgroundTaskQueue _queue;
+ private Socket _socket;
+ private bool _isConnected = false;
+
+ public InsigniaRequestProcessService(IBackgroundTaskQueue queue)
+ {
+ _queue = queue;
+ }
+
+ public override Task StartAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ _socket = IO.Socket("https://bma-ehr.frappet.synology.me", new IO.Options
+ {
+ Path = "/api/v1/org-socket",
+ //Query = new Dictionary
+ //{
+ // { "EIO", "4" },
+ // { "transport", "polling" },
+ // { "t", "tkitfptn" }
+ //}
+ });
+
+ _socket.On(Socket.EVENT_CONNECT, () =>
+ {
+ _isConnected = true;
+ Console.WriteLine("Connected to WebSocket server at: https://bma-ehr.frappet.synology.me/api/v1/org-socket");
+ });
+
+ _socket.On(Socket.EVENT_DISCONNECT, () =>
+ {
+ _isConnected = false;
+ Console.WriteLine("Disconnected from WebSocket server");
+ });
+
+ _socket.On(Socket.EVENT_ERROR, (data) =>
+ {
+ _isConnected = false;
+ Console.WriteLine($"WebSocket error: {data}");
+ });
+
+ _socket.Connect();
+ }
+ catch (Exception ex)
+ {
+ _isConnected = false;
+ Console.WriteLine($"Failed to initialize WebSocket connection: {ex.Message}");
+ }
+
+ return base.StartAsync(cancellationToken);
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ var userId = "4064c2b2-0414-464a-97c6-4a47c325b9a3";
+
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ try
+ {
+ var workItem = await _queue.DequeueAsync(stoppingToken);
+ if (workItem != null)
+ {
+ Console.WriteLine($"Starting background task at: {DateTime.Now}");
+ await workItem(stoppingToken);
+ Console.WriteLine($"Finished background task at: {DateTime.Now}");
+
+ // Send notification to WebSocket after task completion
+ if (_socket != null && _isConnected)
+ {
+ _socket.Emit("send-command-notification",
+ new
+ {
+ success = true,
+ message = "Background Task Completed Successfully",
+ payload = new {
+ completedAt = DateTime.Now,
+ taskType = "background_processing"
+ }
+ },
+ new
+ {
+ userId = userId
+ });
+
+ Console.WriteLine("WebSocket notification sent successfully");
+ }
+ else
+ {
+ Console.WriteLine("WebSocket is not connected. Unable to send notification.");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error processing background task: {ex.Message}");
+
+ // Send error notification to WebSocket
+ if (_socket != null && _isConnected)
+ {
+ _socket.Emit("send-command-notification",
+ new
+ {
+ success = false,
+ message = "Background Task Failed",
+ payload = new {
+ error = ex.Message,
+ failedAt = DateTime.Now,
+ taskType = "background_processing"
+ }
+ },
+ new
+ {
+ userId = userId
+ });
+ }
+ }
+ }
+ }
+
+ public override Task StopAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ if (_socket != null)
+ {
+ Console.WriteLine("Disconnecting from WebSocket server...");
+ _socket.Disconnect();
+ _isConnected = false;
+ _socket = null;
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error disconnecting WebSocket: {ex.Message}");
+ }
+
+ return base.StopAsync(cancellationToken);
+ }
+}