Add migration to create CheckInJobStatuses table for RMQ task control
- Introduced a new migration that creates the CheckInJobStatuses table. - The table includes fields for tracking job statuses, timestamps, user information, and error messages. - Supports various statuses such as PENDING, PROCESSING, COMPLETED, and FAILED.
This commit is contained in:
parent
3532df32fd
commit
a463df5716
12 changed files with 2259 additions and 126 deletions
|
|
@ -53,6 +53,7 @@ namespace BMA.EHR.Application
|
||||||
services.AddTransient<UserDutyTimeRepository>();
|
services.AddTransient<UserDutyTimeRepository>();
|
||||||
services.AddTransient<AdditionalCheckRequestRepository>();
|
services.AddTransient<AdditionalCheckRequestRepository>();
|
||||||
services.AddTransient<UserCalendarRepository>();
|
services.AddTransient<UserCalendarRepository>();
|
||||||
|
services.AddTransient<CheckInJobStatusRepository>();
|
||||||
|
|
||||||
services.AddTransient<LeaveTypeRepository>();
|
services.AddTransient<LeaveTypeRepository>();
|
||||||
services.AddTransient<LeaveRequestRepository>();
|
services.AddTransient<LeaveRequestRepository>();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
using BMA.EHR.Application.Common.Interfaces;
|
||||||
|
using BMA.EHR.Domain.Models.Leave.TimeAttendants;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants
|
||||||
|
{
|
||||||
|
public class CheckInJobStatusRepository : GenericLeaveRepository<Guid, CheckInJobStatus>
|
||||||
|
{
|
||||||
|
#region " Fields "
|
||||||
|
|
||||||
|
private readonly ILeaveDbContext _dbContext;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region " Constructor and Destructor "
|
||||||
|
|
||||||
|
public CheckInJobStatusRepository(ILeaveDbContext dbContext,
|
||||||
|
IHttpContextAccessor httpContextAccessor) : base(dbContext, httpContextAccessor)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region " Methods "
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ดึงข้อมูล Job Status จาก TaskId
|
||||||
|
/// </summary>
|
||||||
|
public async Task<CheckInJobStatus?> GetByTaskIdAsync(Guid taskId)
|
||||||
|
{
|
||||||
|
var data = await _dbContext.Set<CheckInJobStatus>()
|
||||||
|
.Where(x => x.TaskId == taskId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ดึงข้อมูล Job Status จาก UserId และสถานะ
|
||||||
|
/// </summary>
|
||||||
|
public async Task<List<CheckInJobStatus>> GetByUserIdAndStatusAsync(Guid userId, string status)
|
||||||
|
{
|
||||||
|
var data = await _dbContext.Set<CheckInJobStatus>()
|
||||||
|
.Where(x => x.KeycloakUserId == userId && x.Status == status)
|
||||||
|
.OrderByDescending(x => x.CreatedDate)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ดึงข้อมูล Job Status ที่ยัง pending หรือ processing
|
||||||
|
/// </summary>
|
||||||
|
public async Task<List<CheckInJobStatus>> GetPendingOrProcessingJobsAsync(Guid userId)
|
||||||
|
{
|
||||||
|
var data = await _dbContext.Set<CheckInJobStatus>()
|
||||||
|
.Where(x => x.KeycloakUserId == userId &&
|
||||||
|
(x.Status == "PENDING" || x.Status == "PROCESSING"))
|
||||||
|
//.OrderByDescending(x => x.CreatedDate)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// อัปเดตสถานะเป็น Processing
|
||||||
|
/// </summary>
|
||||||
|
public async Task<CheckInJobStatus> UpdateToProcessingAsync(Guid taskId)
|
||||||
|
{
|
||||||
|
var job = await GetByTaskIdAsync(taskId);
|
||||||
|
if (job != null)
|
||||||
|
{
|
||||||
|
job.Status = "PROCESSING";
|
||||||
|
job.ProcessingDate = DateTime.Now;
|
||||||
|
await UpdateAsync(job);
|
||||||
|
}
|
||||||
|
return job!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// อัปเดตสถานะเป็น Completed
|
||||||
|
/// </summary>
|
||||||
|
public async Task<CheckInJobStatus> UpdateToCompletedAsync(Guid taskId, string? additionalData = null)
|
||||||
|
{
|
||||||
|
var job = await GetByTaskIdAsync(taskId);
|
||||||
|
if (job != null)
|
||||||
|
{
|
||||||
|
job.Status = "COMPLETED";
|
||||||
|
job.CompletedDate = DateTime.Now;
|
||||||
|
if (!string.IsNullOrEmpty(additionalData))
|
||||||
|
{
|
||||||
|
job.AdditionalData = additionalData;
|
||||||
|
}
|
||||||
|
await UpdateAsync(job);
|
||||||
|
}
|
||||||
|
return job!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// อัปเดตสถานะเป็น Failed
|
||||||
|
/// </summary>
|
||||||
|
public async Task<CheckInJobStatus> UpdateToFailedAsync(Guid taskId, string errorMessage)
|
||||||
|
{
|
||||||
|
var job = await GetByTaskIdAsync(taskId);
|
||||||
|
if (job != null)
|
||||||
|
{
|
||||||
|
job.Status = "FAILED";
|
||||||
|
job.CompletedDate = DateTime.Now;
|
||||||
|
job.ErrorMessage = errorMessage;
|
||||||
|
await UpdateAsync(job);
|
||||||
|
}
|
||||||
|
return job!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ล้างข้อมูล Job Status ที่เก่าเกิน X วัน
|
||||||
|
/// </summary>
|
||||||
|
public async Task<int> CleanupOldJobsAsync(int daysOld = 30)
|
||||||
|
{
|
||||||
|
var cutoffDate = DateTime.Now.AddDays(-daysOld);
|
||||||
|
var oldJobs = await _dbContext.Set<CheckInJobStatus>()
|
||||||
|
.Where(x => x.CreatedDate < cutoffDate)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
_dbContext.Set<CheckInJobStatus>().RemoveRange(oldJobs);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
return oldJobs.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,63 +21,52 @@ var queue = configuration["Rabbit:Queue"] ?? "basic-queue";
|
||||||
// create connection
|
// create connection
|
||||||
var factory = new ConnectionFactory()
|
var factory = new ConnectionFactory()
|
||||||
{
|
{
|
||||||
HostName = host,
|
//Uri = new Uri("amqp://admin:P@ssw0rd@192.168.4.11:5672")
|
||||||
UserName = user,
|
HostName = host,// หรือ hostname ของ RabbitMQ Server ที่คุณใช้
|
||||||
Password = pass
|
UserName = user, // ใส่ชื่อผู้ใช้ของคุณ
|
||||||
|
Password = pass // ใส่รหัสผ่านของคุณ
|
||||||
};
|
};
|
||||||
|
|
||||||
using var connection = factory.CreateConnection();
|
using var connection = factory.CreateConnection();
|
||||||
using var channel = connection.CreateModel();
|
using var channel = connection.CreateModel();
|
||||||
|
|
||||||
|
//channel.QueueDeclare(queue: "bma-checkin-queue", durable: true, exclusive: false, autoDelete: false, arguments: null);
|
||||||
channel.QueueDeclare(queue: queue, durable: true, exclusive: false, autoDelete: false, arguments: null);
|
channel.QueueDeclare(queue: queue, durable: true, exclusive: false, autoDelete: false, arguments: null);
|
||||||
|
|
||||||
var consumer = new EventingBasicConsumer(channel);
|
var consumer = new EventingBasicConsumer(channel);
|
||||||
string? consumerTag = null;
|
|
||||||
bool isConsuming = false;
|
|
||||||
|
|
||||||
consumer.Received += async (model, ea) =>
|
consumer.Received += async (model, ea) =>
|
||||||
{
|
{
|
||||||
var body = ea.Body.ToArray();
|
var body = ea.Body.ToArray();
|
||||||
var message = Encoding.UTF8.GetString(body);
|
var message = Encoding.UTF8.GetString(body);
|
||||||
|
|
||||||
// Double-check time before processing (safety check)
|
|
||||||
if (!IsWithinOperatingHours())
|
|
||||||
{
|
|
||||||
WriteToConsole($"Message received outside operating hours. Requeuing message.");
|
|
||||||
channel.BasicNack(ea.DeliveryTag, false, true); // Requeue the message
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await CallRestApi(message);
|
await CallRestApi(message);
|
||||||
|
|
||||||
|
// convert string into object
|
||||||
|
//var request = JsonConvert.DeserializeObject<CheckInRequest>(message);
|
||||||
|
//using (var db = new ApplicationDbContext())
|
||||||
|
//{
|
||||||
|
// var item = new AttendantItem
|
||||||
|
// {
|
||||||
|
// Name = request.Name,
|
||||||
|
// CheckInDateTime = request.CheckInDateTime,
|
||||||
|
// };
|
||||||
|
// db.AttendantItems.Add(item);
|
||||||
|
// db.SaveChanges();
|
||||||
|
|
||||||
|
// WriteToConsole($"ได้รับคำขอจาก Queue: {message}");
|
||||||
|
// WriteToConsole($"ตอบกลับจาก REST API: {JsonConvert.SerializeObject(item)}");
|
||||||
|
//}
|
||||||
|
|
||||||
WriteToConsole($"ได้รับคำขอจาก Queue: {message}");
|
WriteToConsole($"ได้รับคำขอจาก Queue: {message}");
|
||||||
|
//WriteToConsole($"ตอบกลับจาก REST API: {JsonConvert.SerializeObject(item)}");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Monitor and control consumer based on time schedule
|
//channel.BasicConsume(queue: "bma-checkin-queue", autoAck: true, consumer: consumer);
|
||||||
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
|
channel.BasicConsume(queue: queue, autoAck: true, consumer: consumer);
|
||||||
|
|
||||||
while (await timer.WaitForNextTickAsync())
|
//Console.WriteLine("\nPress 'Enter' to exit the process...");
|
||||||
{
|
|
||||||
var shouldBeConsuming = IsWithinOperatingHours();
|
|
||||||
|
|
||||||
if (shouldBeConsuming && !isConsuming)
|
await Task.Delay(-1);
|
||||||
{
|
|
||||||
// Start consuming
|
|
||||||
consumerTag = channel.BasicConsume(queue: queue, autoAck: true, consumer: consumer);
|
|
||||||
isConsuming = true;
|
|
||||||
WriteToConsole($"✅ Started consuming messages at {GetCurrentBangkokTime():yyyy-MM-dd HH:mm:ss}");
|
|
||||||
}
|
|
||||||
else if (!shouldBeConsuming && isConsuming)
|
|
||||||
{
|
|
||||||
// Stop consuming
|
|
||||||
if (consumerTag != null)
|
|
||||||
{
|
|
||||||
channel.BasicCancel(consumerTag);
|
|
||||||
consumerTag = null;
|
|
||||||
}
|
|
||||||
isConsuming = false;
|
|
||||||
WriteToConsole($"⏸️ Stopped consuming messages at {GetCurrentBangkokTime():yyyy-MM-dd HH:mm:ss}. Will resume after 08:10 tomorrow.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void WriteToConsole(string message)
|
static void WriteToConsole(string message)
|
||||||
{
|
{
|
||||||
|
|
@ -106,25 +95,6 @@ async Task CallRestApi(string requestData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime GetCurrentBangkokTime()
|
|
||||||
{
|
|
||||||
var bangkokTimeZone = TimeZoneInfo.FindSystemTimeZoneById("SE Asia Standard Time");
|
|
||||||
return TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, bangkokTimeZone);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsWithinOperatingHours()
|
|
||||||
{
|
|
||||||
var currentTime = GetCurrentBangkokTime();
|
|
||||||
var startTime = new TimeSpan(8, 10, 0); // 8:10 AM
|
|
||||||
var endTime = new TimeSpan(23, 59, 59); // End of day
|
|
||||||
|
|
||||||
var currentTimeOfDay = currentTime.TimeOfDay;
|
|
||||||
|
|
||||||
// Consumer should only work from 8:10 AM to end of day
|
|
||||||
return currentTimeOfDay >= startTime && currentTimeOfDay <= endTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class ResponseObject
|
public class ResponseObject
|
||||||
{
|
{
|
||||||
[JsonPropertyName("status")]
|
[JsonPropertyName("status")]
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"Rabbit": {
|
"Rabbit": {
|
||||||
"Host": "192.168.1.40",
|
"Host": "192.168.1.63",
|
||||||
"User": "admin",
|
"User": "admin",
|
||||||
"Password": "Test123456",
|
"Password": "12345678",
|
||||||
"Queue": "bma-checkin-queue"
|
"Queue": "hrms-checkin-queue-dev"
|
||||||
},
|
},
|
||||||
"API": "https://localhost:7283/api/v1"
|
"API": "https://localhost:7283/api/v1"
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
using BMA.EHR.Domain.Models.Base;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace BMA.EHR.Domain.Models.Leave.TimeAttendants
|
||||||
|
{
|
||||||
|
public class CheckInJobStatus : EntityBase
|
||||||
|
{
|
||||||
|
[Required, Comment("Task ID สำหรับติดตามสถานะงาน")]
|
||||||
|
public Guid TaskId { get; set; } = Guid.Empty;
|
||||||
|
|
||||||
|
[Required, Comment("รหัส User ของ Keycloak")]
|
||||||
|
public Guid KeycloakUserId { get; set; } = Guid.Empty;
|
||||||
|
|
||||||
|
[Comment("วันเวลาที่สร้างงาน")]
|
||||||
|
public DateTime CreatedDate { get; set; } = DateTime.Now;
|
||||||
|
|
||||||
|
[Comment("วันเวลาที่เริ่มประมวลผล")]
|
||||||
|
public DateTime? ProcessingDate { get; set; }
|
||||||
|
|
||||||
|
[Comment("วันเวลาที่เสร็จสิ้นการประมวลผล")]
|
||||||
|
public DateTime? CompletedDate { get; set; }
|
||||||
|
|
||||||
|
[Required, Comment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED")]
|
||||||
|
public string Status { get; set; } = "PENDING";
|
||||||
|
|
||||||
|
[Comment("ประเภทการลงเวลา: CHECK_IN, CHECK_OUT")]
|
||||||
|
public string? CheckType { get; set; }
|
||||||
|
|
||||||
|
[Comment("CheckInId สำหรับ Check-Out")]
|
||||||
|
public Guid? CheckInId { get; set; }
|
||||||
|
|
||||||
|
[Comment("ข้อความแสดงข้อผิดพลาด")]
|
||||||
|
public string? ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
[Comment("ข้อมูลเพิ่มเติม (JSON)")]
|
||||||
|
public string? AdditionalData { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
1705
BMA.EHR.Infrastructure/Migrations/LeaveDb/20260120032158_Add RMQ Task Control.Designer.cs
generated
Normal file
1705
BMA.EHR.Infrastructure/Migrations/LeaveDb/20260120032158_Add RMQ Task Control.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,58 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddRMQTaskControl : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "CheckInJobStatuses",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, comment: "PrimaryKey", collation: "ascii_general_ci"),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false, comment: "สร้างข้อมูลเมื่อ"),
|
||||||
|
CreatedUserId = table.Column<string>(type: "varchar(40)", maxLength: 40, nullable: false, comment: "User Id ที่สร้างข้อมูล")
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
LastUpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true, comment: "แก้ไขข้อมูลล่าสุดเมื่อ"),
|
||||||
|
LastUpdateUserId = table.Column<string>(type: "varchar(40)", maxLength: 40, nullable: false, comment: "User Id ที่แก้ไขข้อมูลล่าสุด")
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
CreatedFullName = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: false, comment: "ชื่อ User ที่สร้างข้อมูล")
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
LastUpdateFullName = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: false, comment: "ชื่อ User ที่แก้ไขข้อมูลล่าสุด")
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
TaskId = table.Column<Guid>(type: "char(36)", nullable: false, comment: "Task ID สำหรับติดตามสถานะงาน", collation: "ascii_general_ci"),
|
||||||
|
KeycloakUserId = table.Column<Guid>(type: "char(36)", nullable: false, comment: "รหัส User ของ Keycloak", collation: "ascii_general_ci"),
|
||||||
|
CreatedDate = table.Column<DateTime>(type: "datetime(6)", nullable: false, comment: "วันเวลาที่สร้างงาน"),
|
||||||
|
ProcessingDate = table.Column<DateTime>(type: "datetime(6)", nullable: true, comment: "วันเวลาที่เริ่มประมวลผล"),
|
||||||
|
CompletedDate = table.Column<DateTime>(type: "datetime(6)", nullable: true, comment: "วันเวลาที่เสร็จสิ้นการประมวลผล"),
|
||||||
|
Status = table.Column<string>(type: "longtext", nullable: false, comment: "สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED")
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
CheckType = table.Column<string>(type: "longtext", nullable: true, comment: "ประเภทการลงเวลา: CHECK_IN, CHECK_OUT")
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
CheckInId = table.Column<Guid>(type: "char(36)", nullable: true, comment: "CheckInId สำหรับ Check-Out", collation: "ascii_general_ci"),
|
||||||
|
ErrorMessage = table.Column<string>(type: "longtext", nullable: true, comment: "ข้อความแสดงข้อผิดพลาด")
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
AdditionalData = table.Column<string>(type: "longtext", nullable: true, comment: "ข้อมูลเพิ่มเติม (JSON)")
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_CheckInJobStatuses", x => x.Id);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "CheckInJobStatuses");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -882,6 +882,99 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb
|
||||||
b.ToTable("AdditionalCheckRequests");
|
b.ToTable("AdditionalCheckRequests");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.CheckInJobStatus", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)")
|
||||||
|
.HasColumnOrder(0)
|
||||||
|
.HasComment("PrimaryKey")
|
||||||
|
.HasAnnotation("Relational:JsonPropertyName", "id");
|
||||||
|
|
||||||
|
b.Property<string>("AdditionalData")
|
||||||
|
.HasColumnType("longtext")
|
||||||
|
.HasComment("ข้อมูลเพิ่มเติม (JSON)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CheckInId")
|
||||||
|
.HasColumnType("char(36)")
|
||||||
|
.HasComment("CheckInId สำหรับ Check-Out");
|
||||||
|
|
||||||
|
b.Property<string>("CheckType")
|
||||||
|
.HasColumnType("longtext")
|
||||||
|
.HasComment("ประเภทการลงเวลา: CHECK_IN, CHECK_OUT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("CompletedDate")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnOrder(100)
|
||||||
|
.HasComment("สร้างข้อมูลเมื่อ");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedDate")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasComment("วันเวลาที่สร้างงาน");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedFullName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnOrder(104)
|
||||||
|
.HasComment("ชื่อ User ที่สร้างข้อมูล");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedUserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(40)
|
||||||
|
.HasColumnType("varchar(40)")
|
||||||
|
.HasColumnOrder(101)
|
||||||
|
.HasComment("User Id ที่สร้างข้อมูล");
|
||||||
|
|
||||||
|
b.Property<string>("ErrorMessage")
|
||||||
|
.HasColumnType("longtext")
|
||||||
|
.HasComment("ข้อความแสดงข้อผิดพลาด");
|
||||||
|
|
||||||
|
b.Property<Guid>("KeycloakUserId")
|
||||||
|
.HasColumnType("char(36)")
|
||||||
|
.HasComment("รหัส User ของ Keycloak");
|
||||||
|
|
||||||
|
b.Property<string>("LastUpdateFullName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnOrder(105)
|
||||||
|
.HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด");
|
||||||
|
|
||||||
|
b.Property<string>("LastUpdateUserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(40)
|
||||||
|
.HasColumnType("varchar(40)")
|
||||||
|
.HasColumnOrder(103)
|
||||||
|
.HasComment("User Id ที่แก้ไขข้อมูลล่าสุด");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastUpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnOrder(102)
|
||||||
|
.HasComment("แก้ไขข้อมูลล่าสุดเมื่อ");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("ProcessingDate")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasComment("วันเวลาที่เริ่มประมวลผล");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext")
|
||||||
|
.HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED");
|
||||||
|
|
||||||
|
b.Property<Guid>("TaskId")
|
||||||
|
.HasColumnType("char(36)")
|
||||||
|
.HasComment("Task ID สำหรับติดตามสถานะงาน");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("CheckInJobStatuses");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", b =>
|
modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ namespace BMA.EHR.Infrastructure.Persistence
|
||||||
|
|
||||||
public DbSet<UserCalendar> UserCalendars { get; set; }
|
public DbSet<UserCalendar> UserCalendars { get; set; }
|
||||||
|
|
||||||
|
public DbSet<CheckInJobStatus> CheckInJobStatuses { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region " Leave System "
|
#region " Leave System "
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
private readonly LeaveRequestRepository _leaveRequestRepository;
|
private readonly LeaveRequestRepository _leaveRequestRepository;
|
||||||
private readonly UserCalendarRepository _userCalendarRepository;
|
private readonly UserCalendarRepository _userCalendarRepository;
|
||||||
private readonly PermissionRepository _permission;
|
private readonly PermissionRepository _permission;
|
||||||
|
private readonly CheckInJobStatusRepository _checkInJobStatusRepository;
|
||||||
|
|
||||||
private readonly CommandRepository _commandRepository;
|
private readonly CommandRepository _commandRepository;
|
||||||
|
|
||||||
|
|
@ -92,6 +93,7 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
ObjectPool<IModel> objectPool,
|
ObjectPool<IModel> objectPool,
|
||||||
PermissionRepository permission,
|
PermissionRepository permission,
|
||||||
NotificationRepository notificationRepository,
|
NotificationRepository notificationRepository,
|
||||||
|
CheckInJobStatusRepository checkInJobStatusRepository,
|
||||||
HttpClient httpClient)
|
HttpClient httpClient)
|
||||||
{
|
{
|
||||||
_dutyTimeRepository = dutyTimeRepository;
|
_dutyTimeRepository = dutyTimeRepository;
|
||||||
|
|
@ -109,6 +111,7 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
_commandRepository = commandRepository;
|
_commandRepository = commandRepository;
|
||||||
_leaveRequestRepository = leaveRequestRepository;
|
_leaveRequestRepository = leaveRequestRepository;
|
||||||
_notificationRepository = notificationRepository;
|
_notificationRepository = notificationRepository;
|
||||||
|
_checkInJobStatusRepository = checkInJobStatusRepository;
|
||||||
|
|
||||||
_objectPool = objectPool;
|
_objectPool = objectPool;
|
||||||
_permission = permission;
|
_permission = permission;
|
||||||
|
|
@ -540,11 +543,15 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add task id for check in queue
|
||||||
|
string taskId = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
var checkData = new CheckTimeDtoRB
|
var checkData = new CheckTimeDtoRB
|
||||||
{
|
{
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
CurrentDate = currentDate,
|
CurrentDate = currentDate,
|
||||||
CheckInId = data.CheckInId,
|
CheckInId = data.CheckInId,
|
||||||
|
TaskId = Guid.Parse(taskId),
|
||||||
Lat = data.Lat,
|
Lat = data.Lat,
|
||||||
Lon = data.Lon,
|
Lon = data.Lon,
|
||||||
POI = data.POI,
|
POI = data.POI,
|
||||||
|
|
@ -564,11 +571,27 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
var serializedObject = JsonConvert.SerializeObject(checkData);
|
var serializedObject = JsonConvert.SerializeObject(checkData);
|
||||||
var body = Encoding.UTF8.GetBytes(serializedObject);
|
var body = Encoding.UTF8.GetBytes(serializedObject);
|
||||||
|
|
||||||
// add task id for check in queue
|
|
||||||
string taskId = Guid.NewGuid().ToString();
|
|
||||||
var properties = channel.CreateBasicProperties();
|
var properties = channel.CreateBasicProperties();
|
||||||
properties.Persistent = true;
|
properties.Persistent = true;
|
||||||
properties.MessageId = userId.ToString("D");// ระบบลงเวลาต้องมีการเช็คสถานะใน rabbitMQ ด้วยว่ามีการรอรันอยู่ไหม ลงเวลาเข้า/ออกงาน #894
|
properties.MessageId = taskId;
|
||||||
|
|
||||||
|
// บันทึกสถานะงานก่อนส่งไป RabbitMQ
|
||||||
|
var jobStatus = new CheckInJobStatus
|
||||||
|
{
|
||||||
|
TaskId = Guid.Parse(taskId),
|
||||||
|
KeycloakUserId = userId,
|
||||||
|
CreatedDate = currentDate,
|
||||||
|
Status = "PENDING",
|
||||||
|
CheckType = data.CheckInId == null ? "CHECK_IN" : "CHECK_OUT",
|
||||||
|
CheckInId = data.CheckInId,
|
||||||
|
AdditionalData = JsonConvert.SerializeObject(new
|
||||||
|
{
|
||||||
|
IsLocation = data.IsLocation,
|
||||||
|
LocationName = data.LocationName,
|
||||||
|
POI = data.POI
|
||||||
|
})
|
||||||
|
};
|
||||||
|
await _checkInJobStatusRepository.AddAsync(jobStatus);
|
||||||
|
|
||||||
channel.BasicPublish(exchange: "",
|
channel.BasicPublish(exchange: "",
|
||||||
routingKey: queue,
|
routingKey: queue,
|
||||||
|
|
@ -583,6 +606,78 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ตรวจสอบสถานะงาน check-in ด้วย Task ID
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="taskId">Task ID ที่ได้จากการเรียก CheckInAsync</param>
|
||||||
|
/// <returns>
|
||||||
|
/// </returns>
|
||||||
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
||||||
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
||||||
|
/// <response code="404">ไม่พบข้อมูลงาน</response>
|
||||||
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
||||||
|
[HttpGet("job-status/{taskId:guid}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||||
|
public async Task<ActionResult<ResponseObject>> GetJobStatusAsync(Guid taskId)
|
||||||
|
{
|
||||||
|
var jobStatus = await _checkInJobStatusRepository.GetByTaskIdAsync(taskId);
|
||||||
|
|
||||||
|
if (jobStatus == null)
|
||||||
|
{
|
||||||
|
return Error("ไม่พบข้อมูลงาน", StatusCodes.Status404NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new
|
||||||
|
{
|
||||||
|
taskId = jobStatus.TaskId,
|
||||||
|
keycloakUserId = jobStatus.KeycloakUserId,
|
||||||
|
status = jobStatus.Status,
|
||||||
|
checkType = jobStatus.CheckType,
|
||||||
|
checkInId = jobStatus.CheckInId,
|
||||||
|
createdDate = jobStatus.CreatedDate,
|
||||||
|
processingDate = jobStatus.ProcessingDate,
|
||||||
|
completedDate = jobStatus.CompletedDate,
|
||||||
|
errorMessage = jobStatus.ErrorMessage,
|
||||||
|
additionalData = jobStatus.AdditionalData != null ?
|
||||||
|
JsonConvert.DeserializeObject(jobStatus.AdditionalData) : null
|
||||||
|
};
|
||||||
|
|
||||||
|
return Success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ดึงรายการงานที่ยัง pending หรือ processing ของผู้ใช้
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// </returns>
|
||||||
|
/// <response code="200">เมื่อทำรายการสำเร็จ</response>
|
||||||
|
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
|
||||||
|
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
|
||||||
|
[HttpGet("pending-jobs")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||||
|
public async Task<ActionResult<ResponseObject>> GetPendingJobsAsync()
|
||||||
|
{
|
||||||
|
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
|
||||||
|
var jobs = await _checkInJobStatusRepository.GetPendingOrProcessingJobsAsync(userId);
|
||||||
|
|
||||||
|
var result = jobs.Select(job => new
|
||||||
|
{
|
||||||
|
taskId = job.TaskId,
|
||||||
|
status = job.Status,
|
||||||
|
checkType = job.CheckType,
|
||||||
|
checkInId = job.CheckInId,
|
||||||
|
createdDate = job.CreatedDate,
|
||||||
|
processingDate = job.ProcessingDate
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
return Success(new { count = result.Count, jobs = result });
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("check-status")]
|
[HttpGet("check-status")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
|
|
@ -590,61 +685,62 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
public async Task<ActionResult<ResponseObject>> CheckInCheckStatus()
|
public async Task<ActionResult<ResponseObject>> CheckInCheckStatus()
|
||||||
{
|
{
|
||||||
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
|
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
|
||||||
var currentDate = DateTime.Now;
|
// var currentDate = DateTime.Now;
|
||||||
var channel = _objectPool.Get();
|
// var channel = _objectPool.Get();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var _url = _configuration["Rabbit:URL"] ?? "";
|
// var _url = _configuration["Rabbit:URL"] ?? "";
|
||||||
var _queue = _configuration["Rabbit:Queue"] ?? "basic-queue";
|
// var _queue = _configuration["Rabbit:Queue"] ?? "basic-queue";
|
||||||
|
|
||||||
// Step 1: ตรวจสอบจำนวน message ทั้งหมดในคิว
|
// // Step 1: ตรวจสอบจำนวน message ทั้งหมดในคิว
|
||||||
string queueUrl = $"{_url}{_queue}";
|
// string queueUrl = $"{_url}{_queue}";
|
||||||
var queueResponse = await _httpClient.GetAsync(queueUrl);
|
// var queueResponse = await _httpClient.GetAsync(queueUrl);
|
||||||
if (!queueResponse.IsSuccessStatusCode)
|
// if (!queueResponse.IsSuccessStatusCode)
|
||||||
{
|
// {
|
||||||
return Error("Error accessing RabbitMQ API", (int)queueResponse.StatusCode);
|
// return Error("Error accessing RabbitMQ API", (int)queueResponse.StatusCode);
|
||||||
}
|
// }
|
||||||
|
|
||||||
var queueContent = await queueResponse.Content.ReadAsStringAsync();
|
// var queueContent = await queueResponse.Content.ReadAsStringAsync();
|
||||||
var queueData = JObject.Parse(queueContent);
|
// var queueData = JObject.Parse(queueContent);
|
||||||
int totalMessages = queueData["messages"]?.Value<int>() ?? 0;
|
// int totalMessages = queueData["messages"]?.Value<int>() ?? 0;
|
||||||
|
|
||||||
// Step 2: วนลูปดึง message ทีละ 100 งาน
|
// // Step 2: วนลูปดึง message ทีละ 100 งาน
|
||||||
int batchSize = 100;
|
// int batchSize = 100;
|
||||||
var allMessages = new List<string>();
|
// var allMessages = new List<string>();
|
||||||
int processedMessages = 0;
|
// int processedMessages = 0;
|
||||||
|
|
||||||
while (processedMessages < totalMessages)
|
// while (processedMessages < totalMessages)
|
||||||
{
|
// {
|
||||||
var requestBody = new StringContent(
|
// var requestBody = new StringContent(
|
||||||
$"{{\"count\":{batchSize},\"requeue\":true,\"encoding\":\"auto\",\"ackmode\":\"ack_requeue_true\"}}",
|
// $"{{\"count\":{batchSize},\"requeue\":true,\"encoding\":\"auto\",\"ackmode\":\"ack_requeue_true\"}}",
|
||||||
Encoding.UTF8,
|
// Encoding.UTF8,
|
||||||
"application/json"
|
// "application/json"
|
||||||
);
|
// );
|
||||||
|
|
||||||
string getMessagesUrl = $"{_url}{_queue}/get";
|
// string getMessagesUrl = $"{_url}{_queue}/get";
|
||||||
var response = await _httpClient.PostAsync(getMessagesUrl, requestBody);
|
// var response = await _httpClient.PostAsync(getMessagesUrl, requestBody);
|
||||||
if (!response.IsSuccessStatusCode)
|
// if (!response.IsSuccessStatusCode)
|
||||||
{
|
// {
|
||||||
return StatusCode((int)response.StatusCode, "Error retrieving messages from RabbitMQ.");
|
// return StatusCode((int)response.StatusCode, "Error retrieving messages from RabbitMQ.");
|
||||||
}
|
// }
|
||||||
|
|
||||||
var content = await response.Content.ReadAsStringAsync();
|
// var content = await response.Content.ReadAsStringAsync();
|
||||||
var messages = JArray.Parse(content);
|
// var messages = JArray.Parse(content);
|
||||||
|
|
||||||
if (messages.Count == 0)
|
// if (messages.Count == 0)
|
||||||
{
|
// {
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
processedMessages += messages.Count;
|
// processedMessages += messages.Count;
|
||||||
allMessages.AddRange(messages.Select(m => m["properties"].ToString()));
|
// allMessages.AddRange(messages.Select(m => m["properties"].ToString()));
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
var jobs = await _checkInJobStatusRepository.GetPendingOrProcessingJobsAsync(userId);
|
||||||
// Step 3: ค้นหา taskIds ที่อยู่ใน messages ทั้งหมด
|
// Step 3: ค้นหา taskIds ที่อยู่ใน messages ทั้งหมด
|
||||||
var foundTasks = allMessages.FirstOrDefault(x => x.Contains(userId.ToString("D")));
|
//var foundTasks = allMessages.FirstOrDefault(x => x.Contains(userId.ToString("D")));
|
||||||
|
|
||||||
return Success(new { keycloakId = userId, InQueue = foundTasks != null });
|
return Success(new { keycloakId = userId, InQueue = (jobs != null && jobs.Count > 0) });
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -653,7 +749,7 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_objectPool.Return(channel);
|
//_objectPool.Return(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -790,21 +886,31 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
public async Task<ActionResult<ResponseObject>> ProcessCheckInAsync([FromBody] CheckTimeDtoRB data)
|
public async Task<ActionResult<ResponseObject>> ProcessCheckInAsync([FromBody] CheckTimeDtoRB data)
|
||||||
{
|
{
|
||||||
var userId = data.UserId ?? Guid.Empty;
|
var userId = data.UserId ?? Guid.Empty;
|
||||||
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, data.Token);
|
var taskId = data.TaskId ?? Guid.Empty;
|
||||||
|
|
||||||
if (profile == null)
|
try
|
||||||
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
|
||||||
|
|
||||||
if (data.CheckInFileName == "no-file") throw new Exception(GlobalMessages.NoFileToUpload);
|
|
||||||
var currentDate = data.CurrentDate ?? DateTime.Now;
|
|
||||||
|
|
||||||
var check_status = data.CheckInId == null ? "check-in-picture" : "check-out-picture";
|
|
||||||
|
|
||||||
var fileName = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_status}/{data.CheckInFileName}";
|
|
||||||
using (var ms = new MemoryStream(data.CheckInFileBytes ?? new byte[0]))
|
|
||||||
{
|
{
|
||||||
await _minIOService.UploadFileAsync(fileName, ms);
|
// อัปเดตสถานะเป็น PROCESSING
|
||||||
}
|
if (taskId != Guid.Empty)
|
||||||
|
{
|
||||||
|
await _checkInJobStatusRepository.UpdateToProcessingAsync(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, data.Token);
|
||||||
|
|
||||||
|
if (profile == null)
|
||||||
|
return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound);
|
||||||
|
|
||||||
|
if (data.CheckInFileName == "no-file") throw new Exception(GlobalMessages.NoFileToUpload);
|
||||||
|
var currentDate = data.CurrentDate ?? DateTime.Now;
|
||||||
|
|
||||||
|
var check_status = data.CheckInId == null ? "check-in-picture" : "check-out-picture";
|
||||||
|
|
||||||
|
var fileName = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_status}/{data.CheckInFileName}";
|
||||||
|
using (var ms = new MemoryStream(data.CheckInFileBytes ?? new byte[0]))
|
||||||
|
{
|
||||||
|
await _minIOService.UploadFileAsync(fileName, ms);
|
||||||
|
}
|
||||||
|
|
||||||
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
||||||
if (defaultRound == null)
|
if (defaultRound == null)
|
||||||
|
|
@ -1058,9 +1164,32 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// อัปเดตสถานะเป็น COMPLETED
|
||||||
|
if (taskId != Guid.Empty)
|
||||||
|
{
|
||||||
|
var additionalData = JsonConvert.SerializeObject(new
|
||||||
|
{
|
||||||
|
CheckInType = data.CheckInId == null ? "check-in" : "check-out",
|
||||||
|
FileName = fileName,
|
||||||
|
ProcessedDate = currentDate
|
||||||
|
});
|
||||||
|
await _checkInJobStatusRepository.UpdateToCompletedAsync(taskId, additionalData);
|
||||||
|
}
|
||||||
|
|
||||||
var checkInType = data.CheckInId == null ? "check-in" : "check-out";
|
var checkInType = data.CheckInId == null ? "check-in" : "check-out";
|
||||||
return Success(new { user = $"{profile.FirstName} {profile.LastName}", date = currentDate, type = checkInType }); ;
|
return Success(new { user = $"{profile.FirstName} {profile.LastName}", date = currentDate, type = checkInType }); ;
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// อัปเดตสถานะเป็น FAILED
|
||||||
|
if (taskId != Guid.Empty)
|
||||||
|
{
|
||||||
|
await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, ex.Message);
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// LV1_005 - ลงเวลาเข้า-ออกงาน (USER)
|
/// LV1_005 - ลงเวลาเข้า-ออกงาน (USER)
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ namespace BMA.EHR.Leave.Service.DTOs.CheckIn
|
||||||
|
|
||||||
public Guid? CheckInId { get; set; }
|
public Guid? CheckInId { get; set; }
|
||||||
|
|
||||||
|
public Guid? TaskId { get; set; }
|
||||||
|
|
||||||
public double Lat { get; set; } = 0;
|
public double Lat { get; set; } = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
"Host": "192.168.1.63",
|
"Host": "192.168.1.63",
|
||||||
"User": "admin",
|
"User": "admin",
|
||||||
"Password": "12345678",
|
"Password": "12345678",
|
||||||
"Queue": "hrms-checkin-queue",
|
"Queue": "hrms-checkin-queue-dev",
|
||||||
"URL": "http://192.168.1.63:9122/api/queues/%2F/"
|
"URL": "http://192.168.1.63:9122/api/queues/%2F/"
|
||||||
},
|
},
|
||||||
"Mail": {
|
"Mail": {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue