using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Text.Json; using System.IO; using BMA.EHR.Application.Common.Interfaces; using BMA.EHR.Application.Repositories.Leaves.LeaveRequests; using BMA.EHR.Application.Repositories.MetaData; using BMA.EHR.Application.Responses.Profiles; using BMA.EHR.Domain.Extensions; using BMA.EHR.Domain.Models.Leave.TimeAttendants; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants { public class LeaveProcessJobStatusRepository: GenericLeaveRepository { #region " Fields " private readonly ILeaveDbContext _dbContext; private readonly IHttpContextAccessor _httpContextAccessor; private readonly UserProfileRepository _userProfileRepository; private readonly HolidayRepository _holidayRepository; private readonly DutyTimeRepository _dutyTimeRepository; private readonly UserDutyTimeRepository _userDutyTimeRepository; private readonly ProcessUserTimeStampRepository _processUserTimeStampRepository; private readonly LeaveRequestRepository _leaveRequestRepository; #endregion #region " Constructor and Destructor " public LeaveProcessJobStatusRepository(ILeaveDbContext dbContext, IHttpContextAccessor httpContextAccessor, UserProfileRepository userProfileRepository, HolidayRepository holidayRepository, DutyTimeRepository dutyTimeRepository, UserDutyTimeRepository userDutyTimeRepository, ProcessUserTimeStampRepository processUserTimeStampRepository, LeaveRequestRepository leaveRequestRepository) : base(dbContext, httpContextAccessor) { _dbContext = dbContext; _httpContextAccessor = httpContextAccessor; _userProfileRepository = userProfileRepository; _holidayRepository = holidayRepository; _leaveRequestRepository = leaveRequestRepository; _dutyTimeRepository = dutyTimeRepository; _userDutyTimeRepository = userDutyTimeRepository; _processUserTimeStampRepository = processUserTimeStampRepository; } #endregion #region " Methods " /// /// ดึงข้อมูล Job Status จาก TaskId /// public async Task GetByTaskIdAsync(Guid id) { var data = await _dbContext.Set() .Where(x => x.Id == id) .FirstOrDefaultAsync(); return data; } /// /// ดึงข้อมูล Job Status จาก UserId และสถานะ /// public async Task> GetByUserIdAndStatusAsync(Guid userId, string status) { var data = await _dbContext.Set() .Where(x => x.CreatedUserId == userId.ToString("D") && x.Status == status) .OrderByDescending(x => x.CreatedDate) .ToListAsync(); return data; } /// /// ดึงข้อมูล Job Status ที่ยัง pending หรือ processing /// public async Task> GetPendingOrProcessingJobsAsync(Guid userId) { var data = await _dbContext.Set() .Where(x => x.CreatedUserId == userId.ToString("D") && (x.Status == "PENDING" || x.Status == "PROCESSING")) //.OrderByDescending(x => x.CreatedDate) .ToListAsync(); return data; } public async Task> GetPendingJobsAsync() { var data = await _dbContext.Set() .Where(x => x.Status == "PENDING") .ToListAsync(); return data; } /// /// อัปเดตสถานะเป็น Processing /// public async Task UpdateToProcessingAsync(Guid id) { var job = await GetByTaskIdAsync(id); if (job != null) { job.Status = "PROCESSING"; job.ProcessingDate = DateTime.Now; await UpdateAsync(job); } return job!; } /// /// อัปเดตสถานะเป็น Completed /// public async Task UpdateToCompletedAsync(Guid id, string? additionalData = null) { var job = await GetByTaskIdAsync(id); if (job != null) { job.Status = "COMPLETED"; job.CompletedDate = DateTime.Now; await UpdateAsync(job); } return job!; } /// /// อัปเดตสถานะเป็น Failed /// public async Task UpdateToFailedAsync(Guid id, string errorMessage) { var job = await GetByTaskIdAsync(id); if (job != null) { job.Status = "FAILED"; job.CompletedDate = DateTime.Now; job.ErrorMessage = errorMessage; await UpdateAsync(job); } return job!; } public async Task ProcessTaskAsync(Guid rootDnaId, DateTime? startDate, DateTime? endDate) { var profiles = new List(); var dateStart = startDate?.Date ?? DateTime.Now.Date; var dateEnd = endDate?.Date ?? DateTime.Now.Date; var holidays = await _holidayRepository.GetHolidayAsync(dateStart, dateEnd); var weekend = _holidayRepository.GetWeekEnd(dateStart, dateEnd); var excludeDates = holidays.Union(weekend).ToList(); var dateList = new List(); for (DateTime i = dateStart; i <= dateEnd; i = i.AddDays(1)) { if (holidays.Contains(i)) { var d = await _holidayRepository.GetHolidayAsync(i); dateList.Add(new LoopDate { date = i, isHoliday = true, isWeekEnd = false, dateRemark = d }); } else if (weekend.Contains(i)) { dateList.Add(new LoopDate { date = i, isHoliday = true, isWeekEnd = false, dateRemark = "วันหยุด" }); } else { dateList.Add(new LoopDate { date = i, isHoliday = false, isWeekEnd = false, dateRemark = "" }); } } var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { throw new Exception("ไม่พบรอบการลงเวลา Default"); } var employees = new List(); foreach (var dd in dateList.Where(x => !x.isHoliday && !x.isWeekEnd)) { profiles = await _userProfileRepository.GetAllOfficerByRootDnaId(rootDnaId.ToString(),dd.date); foreach (var p in profiles) { var count = 1; var keycloakUserId = p.Keycloak ?? Guid.Empty; var timeStamps = await _processUserTimeStampRepository.GetTimestampByDateAsync(keycloakUserId, dd.date); var fullName = $"{p.Prefix}{p.FirstName} {p.LastName}"; var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id, dd.date); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); var duty = userRound ?? defaultRound; // check วันลาของแต่ละคน var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(keycloakUserId, dd.date); var remarkStr = string.Empty; if (leaveReq != null) { switch (leaveReq.Type.Code.ToUpper()) { case "LV-001": case "LV-002": case "LV-005": remarkStr += leaveReq.Type.Name; var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); if (leaveRange == "MORNING") remarkStr += "ครึ่งวันเช้า"; else if (leaveRange == "AFTERNOON") remarkStr += "ครึ่งวันบ่าย"; // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); // if (leaveRangeEnd == "MORNING") // remarkStr += "ครึ่งวันเช้า"; // else if (leaveRangeEnd == "AFTERNOON") // remarkStr += "ครึ่งวันบ่าย"; var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); if (leaveRange != leaveRangeEnd) { if (leaveRangeEnd == "MORNING") remarkStr += " - ครึ่งวันเช้า"; else if (leaveRangeEnd == "AFTERNOON") remarkStr += " - ครึ่งวันบ่าย"; } break; default: remarkStr += leaveReq.Type.Name; break; } } else { if (timeStamps == null) { if (dd.date <= DateTime.Now.Date) { remarkStr = "ขาดราชการ"; if (dd.isHoliday == true) { remarkStr = $"วันหยุด ({dd.dateRemark})"; } else if (dd.isWeekEnd) { remarkStr = dd.dateRemark; } } else remarkStr = ""; } else { // check status ของการลงเวลา if (timeStamps.CheckOut != null) { if (timeStamps.CheckOutStatus == "ABSENT") remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckOut ? $" (นอกสถานที่:{timeStamps.CheckOutLocationName})".Trim() : ""); else if (timeStamps.CheckInStatus == "ABSENT") remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); else if (timeStamps.CheckInStatus == "LATE") { remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); //lateTotal += 1; } else remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; } else { if (timeStamps.CheckInStatus == "ABSENT") remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); else if (timeStamps.CheckInStatus == "LATE") { remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); //lateTotal += 1; } else remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; } } } var emp = new DateResultReport { no = count, fullName = fullName, dutyTimeName = $"{duty.StartTimeMorning} - {duty.EndTimeAfternoon} น.", checkInLocation = timeStamps == null ? "" : timeStamps.CheckInPOI, checkInTime = timeStamps == null ? "" : $"{timeStamps.CheckIn.ToString("HH:mm")} น.", checkOutLocation = timeStamps == null ? "" : timeStamps.CheckOutPOI ?? "", checkOutTime = timeStamps == null ? "" : timeStamps.CheckOut != null ? $"{timeStamps.CheckOut.Value.ToString("HH:mm")} น." : "", remark = remarkStr, checkInDate = timeStamps == null ? dd.date.Date.ToThaiFullDate2() : timeStamps.CheckIn.Date.ToThaiFullDate2(), checkedOutDate = timeStamps == null ? dd.date.Date.ToThaiFullDate2() : timeStamps.CheckOut != null ? timeStamps.CheckOut.Value.ToThaiFullDate2() : "", checkInTimeRaw = timeStamps == null ? dd.date.Date : timeStamps?.CheckIn, checkOutTimeRaw = timeStamps == null ? dd.date.Date : timeStamps?.CheckOut != null ? timeStamps?.CheckOut : null, }; employees.Add(emp); count++; } // Write employees to JSON file var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Exports", fileName); // Ensure directory exists var directory = Path.GetDirectoryName(filePath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } var jsonOptions = new JsonSerializerOptions { WriteIndented = true, Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); await File.WriteAllTextAsync(filePath, jsonContent); } } public async Task ProcessPendingJobsAsync() { var pendingJobs = await GetPendingJobsAsync(); Console.WriteLine($"พบงานที่ค้างอยู่ในสถานะ PENDING จำนวน {pendingJobs.Count} งาน"); foreach (var job in pendingJobs) { try { // อัปเดตสถานะเป็น Processing await UpdateToProcessingAsync(job.Id); // ทำงานที่ต้องการที่นี่ (เช่น เรียก API, ประมวลผลข้อมูล ฯลฯ) await ProcessTaskAsync(job.RootDnaId,job.StartDate, job.EndDate); // อัปเดตสถานะเป็น Completed await UpdateToCompletedAsync(job.Id); } catch (Exception ex) { // หากเกิดข้อผิดพลาด อัปเดตสถานะเป็น Failed พร้อมข้อความแสดงข้อผิดพลาด await UpdateToFailedAsync(job.Id, ex.Message); } } } #endregion } class LoopDate { public DateTime date { get; set; } public bool isHoliday { get; set; } public bool isWeekEnd { get; set; } public string dateRemark { get; set; } } class DateResultReport { public int no { get; set; } public string fullName { get; set; } public string dutyTimeName { get; set; } public string checkInLocation { get; set; } public string checkInTime { get; set; } public string checkOutLocation { get; set; } public string checkOutTime { get; set; } public string remark { get; set; } public string checkInDate { get; set; } public string checkedOutDate { get; set; } public DateTime? checkInTimeRaw { get; set; } public DateTime? checkOutTimeRaw { get; set; } } }