diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/CheckInJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/CheckInJobStatusRepository.cs index 302bdd12..85c575d3 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/CheckInJobStatusRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/CheckInJobStatusRepository.cs @@ -114,6 +114,60 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants return job!; } + /// + /// ดึงข้อมูลงานที่ค้างอยู่ในสถานะ PENDING หรือ PROCESSING เกินเวลาที่กำหนด (นาที) + /// + public async Task> GetStalePendingOrProcessingJobsAsync(int timeoutMinutes = 30) + { + var cutoffDate = DateTime.Now.AddMinutes(-timeoutMinutes); + var staleJobs = await _dbContext.Set() + .Where(x => (x.Status == "PENDING" || x.Status == "PROCESSING") + && x.CreatedDate < cutoffDate) + .OrderBy(x => x.CreatedDate) + .ToListAsync(); + + return staleJobs; + } + + /// + /// ดึงข้อมูลงานที่ค้างอยู่ในสถานะ PENDING หรือ PROCESSING เกินเวลาที่กำหนด (นาที) ของ user คนใดคนหนึ่ง + /// + public async Task> GetStalePendingOrProcessingJobsByUserAsync(Guid userId, int timeoutMinutes = 30) + { + var cutoffDate = DateTime.Now.AddMinutes(-timeoutMinutes); + var staleJobs = await _dbContext.Set() + .Where(x => x.KeycloakUserId == userId + && (x.Status == "PENDING" || x.Status == "PROCESSING") + && x.CreatedDate < cutoffDate) + .OrderBy(x => x.CreatedDate) + .ToListAsync(); + + return staleJobs; + } + + /// + /// Mark งานที่ค้างเกินเวลาที่กำหนดเป็น FAILED + /// + public async Task MarkStaleJobsAsFailedAsync(int timeoutMinutes = 30) + { + var staleJobs = await GetStalePendingOrProcessingJobsAsync(timeoutMinutes); + + foreach (var job in staleJobs) + { + job.Status = "FAILED"; + job.CompletedDate = DateTime.Now; + job.ErrorMessage = $"งานค้างในสถานะ {job.Status} เกิน {timeoutMinutes} นาที ระบบทำเครื่องหมายเป็น FAILED อัตโนมัติ"; + } + + if (staleJobs.Any()) + { + _dbContext.Set().UpdateRange(staleJobs); + await _dbContext.SaveChangesAsync(); + } + + return staleJobs.Count; + } + /// /// ล้างข้อมูล Job Status ที่เก่าเกิน X วัน /// diff --git a/BMA.EHR.Leave/BMA.EHR.Leave.csproj b/BMA.EHR.Leave/BMA.EHR.Leave.csproj index 28f42590..e7c58efa 100644 --- a/BMA.EHR.Leave/BMA.EHR.Leave.csproj +++ b/BMA.EHR.Leave/BMA.EHR.Leave.csproj @@ -74,6 +74,9 @@ PreserveNewest + + PreserveNewest + diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 3b1ec5b7..f04c00ce 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -536,7 +536,18 @@ namespace BMA.EHR.Leave.Service.Controllers // prepare data and convert request body and send to queue var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var currentDate = DateTime.Now; - + + // ตรวจสอบและ mark งานเก่าที่ค้างเกิน 30 นาทีเป็น FAILED อัตโนมัติ + var staleJobs = await _checkInJobStatusRepository.GetStalePendingOrProcessingJobsByUserAsync(userId, 30); + if (staleJobs != null && staleJobs.Count > 0) + { + foreach (var staleJob in staleJobs) + { + await _checkInJobStatusRepository.UpdateToFailedAsync(staleJob.TaskId, + $"งานค้างในสถานะ {staleJob.Status} เกิน 30 นาที ระบบทำเครื่องหมายเป็น FAILED อัตโนมัติ"); + } + } + // ตรวจสอบว่ามีงานที่กำลัง pending หรือ processing อยู่หรือไม่ var existingJobs = await _checkInJobStatusRepository.GetPendingOrProcessingJobsAsync(userId); if (existingJobs != null && existingJobs.Count > 0) @@ -544,10 +555,10 @@ namespace BMA.EHR.Leave.Service.Controllers // กรองเฉพาะงานที่เป็นประเภทเดียวกัน (CHECK_IN หรือ CHECK_OUT) var checkType = data.CheckInId == null ? "CHECK_IN" : "CHECK_OUT"; var sameTypeJob = existingJobs.FirstOrDefault(j => j.CheckType == checkType); - + if (sameTypeJob != null) { - + return Error($"มีงาน {checkType} กำลังดำเนินการอยู่", StatusCodes.Status500InternalServerError); // var timeDiff = (currentDate - sameTypeJob.CreatedDate).TotalMinutes; // if (timeDiff < 2) @@ -603,8 +614,17 @@ namespace BMA.EHR.Leave.Service.Controllers var properties = channel.CreateBasicProperties(); properties.Persistent = true; properties.MessageId = taskId; + + // ส่งไป RabbitMQ + channel.BasicPublish(exchange: "", + routingKey: queue, + basicProperties: properties, + body: body); - // บันทึกสถานะงานก่อนส่งไป RabbitMQ + // Clear Byte data Before Save to DB + checkData.CheckInFileBytes = new byte[0]; + + // บันทึกสถานะงานหลังส่งไป RabbitMQ jobStatus = new CheckInJobStatus { TaskId = Guid.Parse(taskId), @@ -613,23 +633,10 @@ namespace BMA.EHR.Leave.Service.Controllers 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, - KeycloakId = userId, - Token = AccessToken, - }) + AdditionalData = JsonConvert.SerializeObject(checkData) }; await _checkInJobStatusRepository.AddAsync(jobStatus); - - // ส่งไป RabbitMQ - channel.BasicPublish(exchange: "", - routingKey: queue, - basicProperties: properties, - body: body); - + return Success(new { date = currentDate, taskId = taskId, keycloakId = userId }); } catch (Exception ex) @@ -727,6 +734,117 @@ namespace BMA.EHR.Leave.Service.Controllers return Success(new { count = result.Count, jobs = result }); } + /// + /// ประมวลผลงาน CheckIn ที่ค้างอยู่ในสถานะ PENDING/PROCESSING เกินเวลาที่กำหนดใหม่อีกครั้ง + /// + /// + /// เมื่อทำรายการสำเร็จ + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpPost("reprocess-stale-checkin-jobs")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> ReprocessStaleCheckInJobsAsync([FromQuery] int timeoutMinutes = 30) + { + try + { + var staleJobs = await _checkInJobStatusRepository.GetStalePendingOrProcessingJobsAsync(timeoutMinutes); + + if (staleJobs == null || staleJobs.Count == 0) + { + return Success(new { message = "ไม่พบงานที่ค้างอยู่", count = 0 }); + } + + var results = new List(); + foreach (var job in staleJobs) + { + try + { + // อ่านข้อมูลเดิมจาก AdditionalData + if (string.IsNullOrEmpty(job.AdditionalData)) + { + await _checkInJobStatusRepository.UpdateToFailedAsync(job.TaskId, + "ไม่พบข้อมูลสำหรับประมวลผลซ้ำ (AdditionalData is null)"); + results.Add(new + { + taskId = job.TaskId, + keycloakUserId = job.KeycloakUserId, + checkType = job.CheckType, + createdDate = job.CreatedDate, + previousStatus = job.Status, + newStatus = "FAILED", + errorMessage = "ไม่พบข้อมูลสำหรับประมวลผลซ้ำ" + }); + continue; + } + + var checkData = JsonConvert.DeserializeObject(job.AdditionalData); + checkData.UserId = job.KeycloakUserId; + checkData.CurrentDate = job.CreatedDate; + if (checkData == null) + { + await _checkInJobStatusRepository.UpdateToFailedAsync(job.TaskId, + "ไม่สามารถอ่านข้อมูลสำหรับประมวลผลซ้ำได้"); + results.Add(new + { + taskId = job.TaskId, + keycloakUserId = job.KeycloakUserId, + checkType = job.CheckType, + createdDate = job.CreatedDate, + previousStatus = job.Status, + newStatus = "FAILED", + errorMessage = "ไม่สามารถอ่านข้อมูลสำหรับประมวลผลซ้ำได้" + }); + continue; + } + + // ตั้ง TaskId ให้ตรงกับ job เดิม + checkData.TaskId = job.TaskId; + + // เรียก ProcessCheckInAsync ด้วยข้อมูลเดิม + var processResult = await ProcessCheckInAsync(checkData); + + results.Add(new + { + taskId = job.TaskId, + keycloakUserId = job.KeycloakUserId, + checkType = job.CheckType, + createdDate = job.CreatedDate, + previousStatus = job.Status, + result = processResult + }); + } + catch (Exception ex) + { + await _checkInJobStatusRepository.UpdateToFailedAsync(job.TaskId, + $"เกิดข้อผิดพลาดในการประมวลผลซ้ำ: {ex.Message}"); + results.Add(new + { + taskId = job.TaskId, + keycloakUserId = job.KeycloakUserId, + checkType = job.CheckType, + createdDate = job.CreatedDate, + previousStatus = job.Status, + newStatus = "FAILED", + errorMessage = ex.Message + }); + } + } + + return Success(new + { + message = $"ประมวลผลซ้ำงาน {staleJobs.Count} รายการเสร็จสิ้น", + count = staleJobs.Count, + jobs = results + }); + } + catch (Exception ex) + { + return Error(ex); + } + } + [HttpGet("check-status")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] @@ -958,16 +1076,18 @@ namespace BMA.EHR.Leave.Service.Controllers var currentDate = data.CurrentDate ?? DateTime.Now; - if (data.CheckInFileName == "no-file") + if (data.CheckInFileName == "no-file") { //throw new Exception(GlobalMessages.NoFileToUpload); await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, GlobalMessages.NoFileToUpload); - await _notificationService.SendNotificationAsync(data.Token, true, $"ลงเวลาไม่สำเร็จ \r\nเนื่องจาก {GlobalMessages.NoFileToUpload}\r\nกรุณาลองใหม่อีกครั้ง"); + await _notificationService.SendNotificationAsync(data.Token, true, + $"ลงเวลาไม่สำเร็จ \r\nเนื่องจาก {GlobalMessages.NoFileToUpload}\r\nกรุณาลองใหม่อีกครั้ง"); // send notification to user var noti1 = new Notification { - Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจาก {GlobalMessages.NoFileToUpload}", + Body = + $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจาก {GlobalMessages.NoFileToUpload}", ReceiverUserId = profile.Id, Type = "", Payload = "", @@ -977,46 +1097,54 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(GlobalMessages.NoFileToUpload, StatusCodes.Status400BadRequest); } - + // last check-in record var lastCheckIn = await _userTimeStampRepository.GetLastRecord(userId); var check_status = data.CheckInId == null ? "check-in-picture" : "check-out-picture"; var check_out_status = "check-out-picture"; - var fileName = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_status}/{data.CheckInFileName}"; - var fileNameCheckOut = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_out_status}/{data.CheckInFileName}"; - using (var ms = new MemoryStream(data.CheckInFileBytes ?? new byte[0])) + // ถ้าไม่มี CheckInFileBytes ให้ใช้ภาพ blank.jpeg แทน + var fileBytes = data.CheckInFileBytes; + if (fileBytes == null || fileBytes.Length == 0) + { + var blankPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot", "blank.jpeg"); + fileBytes = await System.IO.File.ReadAllBytesAsync(blankPath); + data.CheckInFileName = "blank.jpeg"; + } + + var fileName = + $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_status}/{data.CheckInFileName}"; + var fileNameCheckOut = + $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_out_status}/{data.CheckInFileName}"; + using (var ms = new MemoryStream(fileBytes)) { try { await _minIOService.UploadFileAsync(fileName, ms); - // if (lastCheckIn != null && lastCheckIn.CheckOut == null) - // { - // // ยังไม่เคย check-out มาก่อน หรือ check-out เป็น null ให้ใช้ชื่อไฟล์แบบ check-out - // await _minIOService.UploadFileAsync(fileNameCheckOut, ms); - // } } catch (Exception ex) { - await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, $"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}"); - await _notificationService.SendNotificationAsync(data.Token, true, $"ลงเวลาไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}\r\nกรุณาลองใหม่อีกครั้ง"); + await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, + $"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}"); + await _notificationService.SendNotificationAsync(data.Token, true, + $"ลงเวลาไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}\r\nกรุณาลองใหม่อีกครั้ง"); // send notification to user - var noti1 = new Notification + var noti2 = new Notification { - Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}", + Body = + $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}", ReceiverUserId = profile.Id, Type = "", Payload = "", }; - _appDbContext.Set().Add(noti1); + _appDbContext.Set().Add(noti2); await _appDbContext.SaveChangesAsync(); - - return Error($"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}", StatusCodes.Status500InternalServerError); + return Error($"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}", + StatusCodes.Status500InternalServerError); } - } if (lastCheckIn != null && lastCheckIn.CheckOut == null) @@ -1026,36 +1154,32 @@ namespace BMA.EHR.Leave.Service.Controllers try { await _minIOService.UploadFileAsync(fileNameCheckOut, ms2); - // if (lastCheckIn != null && lastCheckIn.CheckOut == null) - // { - // // ยังไม่เคย check-out มาก่อน หรือ check-out เป็น null ให้ใช้ชื่อไฟล์แบบ check-out - // await _minIOService.UploadFileAsync(fileNameCheckOut, ms); - // } } catch (Exception ex) { - await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, $"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}"); - await _notificationService.SendNotificationAsync(data.Token, true, $"ลงเวลาไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}\r\nกรุณาลองใหม่อีกครั้ง"); + await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, + $"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}"); + await _notificationService.SendNotificationAsync(data.Token, true, + $"ลงเวลาไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}\r\nกรุณาลองใหม่อีกครั้ง"); // send notification to user - var noti1 = new Notification + var noti3 = new Notification { - Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}", + Body = + $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}", ReceiverUserId = profile.Id, Type = "", Payload = "", }; - _appDbContext.Set().Add(noti1); + _appDbContext.Set().Add(noti3); await _appDbContext.SaveChangesAsync(); - - return Error($"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}", StatusCodes.Status500InternalServerError); + return Error($"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}", + StatusCodes.Status500InternalServerError); } - } } - var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { diff --git a/BMA.EHR.Leave/Program.cs b/BMA.EHR.Leave/Program.cs index 9d42133e..3dd83203 100644 --- a/BMA.EHR.Leave/Program.cs +++ b/BMA.EHR.Leave/Program.cs @@ -198,12 +198,20 @@ if (manager != null) // ทำความสะอาดข้อมูล CheckIn Job Status ที่เก่ากว่า 30 วัน - รันทุกวันเวลา 02:00 น. manager.AddOrUpdate("ทำความสะอาดข้อมูล CheckIn Job Status", Job.FromExpression(x => x.CleanupOldJobsAsync(30)), "0 2 * * *", bangkokTimeZone); - manager.AddOrUpdate("ประมวลผลงานที่ค้างอยู่ในสถานะ Pending หรือ Processing", Job.FromExpression(x => x.ProcessPendingJobsAsync()), "0 3 * * *", - new RecurringJobOptions - { - TimeZone = bangkokTimeZone, - QueueName = "leave" // ← กำหนด queue - }); + // ตรวจสอบและ mark งาน CheckIn ที่ค้างเกิน 30 นาทีเป็น FAILED - รันทุก 15 นาที + // manager.AddOrUpdate("ตรวจสอบงาน CheckIn ที่ค้างเกินเวลา", Job.FromExpression(x => x.MarkStaleJobsAsFailedAsync(30)), "*/15 * * * *", + // new RecurringJobOptions + // { + // TimeZone = bangkokTimeZone, + // QueueName = "leave" + // }); + // + // manager.AddOrUpdate("ประมวลผลงานที่ค้างอยู่ในสถานะ Pending หรือ Processing", Job.FromExpression(x => x.ProcessPendingJobsAsync()), "0 3 * * *", + // new RecurringJobOptions + // { + // TimeZone = bangkokTimeZone, + // QueueName = "leave" // ← กำหนด queue + // }); } // apply migrations diff --git a/BMA.EHR.Leave/wwwroot/blank.jpeg b/BMA.EHR.Leave/wwwroot/blank.jpeg new file mode 100644 index 00000000..2b153486 Binary files /dev/null and b/BMA.EHR.Leave/wwwroot/blank.jpeg differ diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs index df4be162..786222b1 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs @@ -676,6 +676,7 @@ namespace BMA.EHR.Placement.Service.Controllers uppdated.posMasterNo = req.posMasterNo; uppdated.position = req.positionName; uppdated.PositionExecutive = req.posExecutiveName; + uppdated.posExecutiveId = req.posExecutiveId; uppdated.positionExecutiveField = req.positionExecutiveField; uppdated.positionArea = req.positionArea; uppdated.positionField = req.positionField; @@ -1014,6 +1015,8 @@ namespace BMA.EHR.Placement.Service.Controllers positionLevel = p.posLevelName, posmasterId = p.posmasterId, positionId = p.positionId, + posExecutiveId = p.posExecutiveId, + positionField = p.positionField, commandId = r.commandId, orgRoot = p.root, orgChild1 = p.child1, @@ -1226,6 +1229,8 @@ namespace BMA.EHR.Placement.Service.Controllers positionLevel = p.posLevelName, posmasterId = p.posmasterId, positionId = p.positionId, + posExecutiveId = p.posExecutiveId, + positionField = p.positionField, commandId = r.commandId, orgRoot = p.root, orgChild1 = p.child1, @@ -1855,6 +1860,8 @@ namespace BMA.EHR.Placement.Service.Controllers positionLevel = p.posLevelName, posmasterId = p.posmasterId, positionId = p.positionId, + posExecutiveId = p.posExecutiveId, + positionField = p.positionField, commandId = r.commandId, orgRoot = p.root, orgChild1 = p.child1, @@ -2016,6 +2023,8 @@ namespace BMA.EHR.Placement.Service.Controllers positionLevel = p.posLevelName, posmasterId = p.posmasterId, positionId = p.positionId, + posExecutiveId = p.posExecutiveId, + positionField = p.positionField, commandId = r.commandId, orgRoot = p.root, orgChild1 = p.child1, diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs index 39b7fc63..db3e9e86 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs @@ -1065,6 +1065,7 @@ namespace BMA.EHR.Placement.Service.Controllers person.positionId = req.positionId; person.posMasterNo = req.posMasterNo; person.positionName = req.positionName; + person.posExecutiveId = req.posExecutiveId; person.PositionExecutive = req.posExecutiveName; person.positionExecutiveField = req.positionExecutiveField; person.positionArea = req.positionArea; @@ -1453,6 +1454,10 @@ namespace BMA.EHR.Placement.Service.Controllers profile.posTypeName = null; profile.posLevelId = null; profile.posLevelName = null; + profile.PositionExecutive = null; + profile.posExecutiveId = null; + profile.positionArea = null; + profile.positionExecutiveField = null; // profile.PositionLevel = null; // profile.PositionType = null; @@ -1810,16 +1815,27 @@ namespace BMA.EHR.Placement.Service.Controllers [HttpPost("recruit/report/excecute")] public async Task> PostReportExecuteRecruit([FromBody] ReportExecuteRequest req) { + Console.WriteLine($"[RecruitReportExcecute] Starting execution at {DateTime.Now}"); + try { + Console.WriteLine($"[RecruitReportExcecute] Request received with {req?.refIds?.Length ?? 0} refIds"); + var placementProfile = await _context.PlacementProfiles .Include(x => x.PlacementCertificates) .Include(x => x.PlacementEducations) .Where(x => req.refIds.Select(x => x.refId).Contains(x.Id.ToString())) .ToListAsync(); + Console.WriteLine($"[RecruitReportExcecute] Found {placementProfile?.Count ?? 0} placement profiles"); + if (placementProfile == null) + { + Console.Error.WriteLine("[RecruitReportExcecute] PlacementProfile is null - returning NotFound"); return NotFound(); + } + + Console.WriteLine("[RecruitReportExcecute] Building resultData from placement profiles and refIds"); var resultData = (from p in placementProfile join r in req.refIds @@ -1936,7 +1952,14 @@ namespace BMA.EHR.Placement.Service.Controllers bodyPosition = new { posmasterId = p.posmasterId, - positionId = p.positionId + positionId = p.positionId, + positionName = p.positionName, + positionField = p.positionField, + posTypeId = p.posTypeId, + posLevelId = p.posLevelId, + posExecutiveId = p.posExecutiveId, + positionExecutiveField = p.positionExecutiveField, + positionArea = p.positionArea, }, bodyMarry = new { @@ -1965,6 +1988,9 @@ namespace BMA.EHR.Placement.Service.Controllers }, }).ToList(); + Console.WriteLine($"[RecruitReportExcecute] resultData built successfully with {resultData?.Count ?? 0} records"); + + Console.WriteLine($"[RecruitReportExcecute] Calling external API: {_configuration["API"]}/org/command/excexute/create-officer-profile"); var apiUrl = $"{_configuration["API"]}/org/command/excexute/create-officer-profile"; using (var client = new HttpClient()) { @@ -1976,8 +2002,10 @@ namespace BMA.EHR.Placement.Service.Controllers data = resultData }); var _result = await _res.Content.ReadAsStringAsync(); + Console.WriteLine($"[RecruitReportExcecute] External API response status: {_res.StatusCode}"); if (_res.IsSuccessStatusCode) { + Console.WriteLine("[RecruitReportExcecute] External API call successful - updating placement profiles"); placementProfile.ForEach(profile => { profile.PlacementStatus = "DONE"; @@ -1991,14 +2019,24 @@ namespace BMA.EHR.Placement.Service.Controllers profile.templateDoc = req.refIds[0].remark; } }); + Console.WriteLine($"[RecruitReportExcecute] Saving changes to database for {placementProfile.Count} profiles"); await _context.SaveChangesAsync(); + Console.WriteLine("[RecruitReportExcecute] Database save completed successfully"); + } + else + { + Console.Error.WriteLine($"[RecruitReportExcecute] External API call failed with status: {_res.StatusCode}"); + Console.Error.WriteLine($"[RecruitReportExcecute] Response content: {_result}"); } } + Console.WriteLine($"[RecruitReportExcecute] Process completed successfully at {DateTime.Now}"); return Success(); } - catch + catch (Exception ex) { + Console.Error.WriteLine($"[RecruitReportExcecute] Error occurred: {ex.Message}"); + Console.Error.WriteLine($"[RecruitReportExcecute] Stack trace: {ex.StackTrace}"); throw; } } @@ -2292,7 +2330,14 @@ namespace BMA.EHR.Placement.Service.Controllers bodyPosition = new { posmasterId = p.posmasterId, - positionId = p.positionId + positionId = p.positionId, + positionName = p.positionName, + positionField = p.positionField, + posTypeId = p.posTypeId, + posLevelId = p.posLevelId, + posExecutiveId = p.posExecutiveId, + positionExecutiveField = p.positionExecutiveField, + positionArea = p.positionArea, }, bodyMarry = new { @@ -2563,6 +2608,8 @@ namespace BMA.EHR.Placement.Service.Controllers positionLevel = p.posLevelName, posmasterId = p.posmasterId, positionId = p.positionId, + posExecutiveId = p.posExecutiveId, + positionField = p.positionField, commandId = r.commandId, orgRoot = p.root, orgChild1 = p.child1, @@ -2804,6 +2851,8 @@ namespace BMA.EHR.Placement.Service.Controllers positionLevel = p.posLevelName, posmasterId = p.posmasterId, positionId = p.positionId, + posExecutiveId = p.posExecutiveId, + positionField = p.positionField, commandId = r.commandId, orgRoot = p.root, orgChild1 = p.child1, @@ -3030,6 +3079,8 @@ namespace BMA.EHR.Placement.Service.Controllers positionLevel = p.posLevelName, posmasterId = p.posmasterId, positionId = p.positionId, + posExecutiveId = p.posExecutiveId, + positionField = p.positionField, commandId = r.commandId, orgRoot = p.root, orgChild1 = p.child1, diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs index a8af9ad7..fa647940 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs @@ -782,6 +782,7 @@ namespace BMA.EHR.Placement.Service.Controllers uppdated.posMasterNo = req.posMasterNo; uppdated.position = req.positionName; uppdated.PositionExecutive = req.posExecutiveName; + uppdated.posExecutiveId = req.posExecutiveId; uppdated.positionExecutiveField = req.positionExecutiveField; uppdated.positionArea = req.positionArea; uppdated.positionField = req.positionField; @@ -1216,8 +1217,15 @@ namespace BMA.EHR.Placement.Service.Controllers }, bodyPosition = new { - posmasterId = p.posmasterId, - positionId = p.positionId + posmasterId = p.posmasterId, + positionId = p.positionId, + positionName = p.position, + positionField = p.positionField, + posTypeId = p.posTypeId, + posLevelId = p.posLevelId, + posExecutiveId = p.posExecutiveId, + positionExecutiveField = p.positionExecutiveField, + positionArea = p.positionArea, } }).ToList(); diff --git a/BMA.EHR.Placement.Service/Requests/PersonSelectPositionAppointmentRequest.cs b/BMA.EHR.Placement.Service/Requests/PersonSelectPositionAppointmentRequest.cs index 8f6c31ba..e64444ed 100644 --- a/BMA.EHR.Placement.Service/Requests/PersonSelectPositionAppointmentRequest.cs +++ b/BMA.EHR.Placement.Service/Requests/PersonSelectPositionAppointmentRequest.cs @@ -30,6 +30,7 @@ namespace BMA.EHR.Placement.Service.Requests public string? posLevelName { get; set; } public string? typeCommand { get; set; } public string? posExecutiveName { get; set; } + public string? posExecutiveId { get; set; } public string? positionExecutiveField { get; set; } public string? positionArea { get; set; } } diff --git a/BMA.EHR.Placement.Service/Requests/PersonSelectPositionReceiveRequest.cs b/BMA.EHR.Placement.Service/Requests/PersonSelectPositionReceiveRequest.cs index 9b22874b..29e5d096 100644 --- a/BMA.EHR.Placement.Service/Requests/PersonSelectPositionReceiveRequest.cs +++ b/BMA.EHR.Placement.Service/Requests/PersonSelectPositionReceiveRequest.cs @@ -30,6 +30,7 @@ namespace BMA.EHR.Placement.Service.Requests public string? posLevelName { get; set; } public string? typeCommand { get; set; } public string? posExecutiveName { get; set; } + public string? posExecutiveId { get; set; } public string? positionExecutiveField { get; set; } public string? positionArea { get; set; } } diff --git a/BMA.EHR.Placement.Service/Requests/PersonSelectPositionRequest.cs b/BMA.EHR.Placement.Service/Requests/PersonSelectPositionRequest.cs index 66042358..681bc1d5 100644 --- a/BMA.EHR.Placement.Service/Requests/PersonSelectPositionRequest.cs +++ b/BMA.EHR.Placement.Service/Requests/PersonSelectPositionRequest.cs @@ -39,6 +39,7 @@ namespace BMA.EHR.Placement.Service.Requests public string? posLevelName { get; set; } public string? typeCommand { get; set; } public string? posExecutiveName { get; set; } + public string? posExecutiveId { get; set; } public string? positionExecutiveField { get; set; } public string? positionArea { get; set; } diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs index 588ccabe..3f36af1d 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs @@ -569,6 +569,7 @@ namespace BMA.EHR.Retirement.Service.Controllers uppdated.positionId = req.positionId; uppdated.posMasterNo = req.posMasterNo; uppdated.position = req.positionName; + uppdated.posExecutiveId = req.posExecutiveId; uppdated.PositionExecutive = req.posExecutiveName; uppdated.positionExecutiveField = req.positionExecutiveField; uppdated.positionArea = req.positionArea; @@ -889,6 +890,9 @@ namespace BMA.EHR.Retirement.Service.Controllers commandCode = r.commandCode, commandName = r.commandName, remark = r.remark, + positionId = p.positionId, + posExecutiveId = p.posExecutiveId, + positionField = p.positionField, positionTypeNew = p.posTypeId, positionLevelNew = p.posLevelId, positionNameNew = p.position, @@ -1138,6 +1142,9 @@ namespace BMA.EHR.Retirement.Service.Controllers commandCode = r.commandCode, commandName = r.commandName, remark = r.remark, + positionId = p.positionId, + posExecutiveId = p.posExecutiveId, + positionField = p.positionField, positionTypeNew = p.posTypeId, positionLevelNew = p.posLevelId, positionNameNew = p.position, diff --git a/BMA.EHR.Retirement.Service/Requests/PersonSelectPositionOtherRequest.cs b/BMA.EHR.Retirement.Service/Requests/PersonSelectPositionOtherRequest.cs index 8252d1af..44c2da54 100644 --- a/BMA.EHR.Retirement.Service/Requests/PersonSelectPositionOtherRequest.cs +++ b/BMA.EHR.Retirement.Service/Requests/PersonSelectPositionOtherRequest.cs @@ -29,6 +29,7 @@ namespace BMA.EHR.Retirement.Service.Requests public string? posLevelId { get; set; } public string? posLevelName { get; set; } public string? typeCommand { get; set; } + public string? posExecutiveId { get; set; } public string? posExecutiveName { get; set; } public string? positionExecutiveField { get; set; } public string? positionArea { get; set; }