From b296304697fa47aa430802ec7b8a9ff1367650b6 Mon Sep 17 00:00:00 2001 From: kittapath Date: Mon, 13 Oct 2025 22:30:10 +0700 Subject: [PATCH] =?UTF-8?q?test=20=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=88?= =?UTF-8?q?=E0=B8=B8=20=E0=B8=9E=E0=B8=B4=E0=B8=81=E0=B8=B2=E0=B8=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Controllers/DisableController.cs | 9 +- Models/EducationLevel.cs | 4 +- Models/SubDistrict.cs | 8 +- Request/RecruitDateRequest.cs | 9 + Request/RecruitPosTypeRequest.cs | 18 ++ Services/PeriodExamService.cs | 464 ++++++++++++++++++------------- 6 files changed, 309 insertions(+), 203 deletions(-) create mode 100644 Request/RecruitDateRequest.cs create mode 100644 Request/RecruitPosTypeRequest.cs diff --git a/Controllers/DisableController.cs b/Controllers/DisableController.cs index ea0477b..8ff0ff8 100644 --- a/Controllers/DisableController.cs +++ b/Controllers/DisableController.cs @@ -27,6 +27,7 @@ using System.Text; using Newtonsoft.Json.Linq; using Newtonsoft.Json; using System.Net.Http.Headers; +using BMA.EHR.Recurit.Exam.Service.Request; namespace BMA.EHR.Recurit.Exam.Service.Controllers { @@ -2098,7 +2099,7 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers // 3️. ดึงสรุปคะแนน // --------------------------- dynamic header = null; - int _count = await _context.Disables.Where(x=> x.PeriodExam.Id == id).CountAsync(); + int _count = await _context.Disables.Where(x => x.PeriodExam.Id == id).CountAsync(); if (data.Count > 0) { header = await _context.DisableScores @@ -2432,15 +2433,15 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers /// เมื่อโอนคนสรรหาไปบรรจุสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน - [HttpGet("placement/{examId:length(36)}")] + [HttpPost("placement/{examId:length(36)}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> UpdateAsyncDisableToPlacement(Guid examId) + public async Task> UpdateAsyncDisableToPlacement(Guid examId, [FromBody] RecruitDateRequest req) { try { - await _periodExamService.UpdateAsyncDisableToPlacement(examId); + await _periodExamService.UpdateAsyncDisableToPlacement(examId, req.AccountStartDate); return Success(); } catch (Exception ex) diff --git a/Models/EducationLevel.cs b/Models/EducationLevel.cs index 55f249e..c33cf6c 100644 --- a/Models/EducationLevel.cs +++ b/Models/EducationLevel.cs @@ -6,8 +6,8 @@ namespace BMA.EHR.Recurit.Exam.Service.Models { public class EducationLevel : EntityBase { - [Required, MaxLength(100), Column(Order = 1), Comment("ระดับการศึกษา")] - public string name { get; set; } = string.Empty; + [MaxLength(255), Column(Order = 1), Comment("ระดับการศึกษา")] + public string? name { get; set; } = null; // [Column(Order = 2), Comment("สถานะการใช้งาน")] // public bool IsActive { get; set; } = true; diff --git a/Models/SubDistrict.cs b/Models/SubDistrict.cs index 71688a1..0610c37 100644 --- a/Models/SubDistrict.cs +++ b/Models/SubDistrict.cs @@ -6,11 +6,11 @@ namespace BMA.EHR.Recurit.Exam.Service.Models { public class SubDistrict : EntityBase { - [Required, MaxLength(150), Column(Order = 1), Comment("เขต/อำเภอ")] - public string name { get; set; } = string.Empty; + [MaxLength(255), Column(Order = 1), Comment("แขวง")] + public string? name { get; set; } = null; - [Required, MaxLength(10), Column(Order = 2), Comment("รหัสไปรษณีย์")] - public string zipCode { get; set; } = string.Empty; + [MaxLength(10), Column(Order = 2), Comment("รหัสไปรษณีย์")] + public string? zipCode { get; set; } = null; // [Column(Order = 3), Comment("สถานะการใช้งาน")] // public bool IsActive { get; set; } = true; diff --git a/Request/RecruitDateRequest.cs b/Request/RecruitDateRequest.cs new file mode 100644 index 0000000..b5eaee7 --- /dev/null +++ b/Request/RecruitDateRequest.cs @@ -0,0 +1,9 @@ +using System.Net; + +namespace BMA.EHR.Recurit.Exam.Service.Request +{ + public class RecruitDateRequest + { + public DateTime AccountStartDate { get; set; } + } +} diff --git a/Request/RecruitPosTypeRequest.cs b/Request/RecruitPosTypeRequest.cs new file mode 100644 index 0000000..ffd4e7a --- /dev/null +++ b/Request/RecruitPosTypeRequest.cs @@ -0,0 +1,18 @@ +using System.Net; + +namespace BMA.EHR.Recurit.Exam.Service.Request +{ + public class RecruitPosRequest + { + public List result { get; set; } = new(); + } + public class RecruitPosLevelRequest + { + public string posLevelName { get; set; } + public RecruitPosTypeRequest posTypes { get; set; } = new(); + } + public class RecruitPosTypeRequest + { + public string posTypeName { get; set; } + } +} diff --git a/Services/PeriodExamService.cs b/Services/PeriodExamService.cs index fc0d0d3..d206a1f 100644 --- a/Services/PeriodExamService.cs +++ b/Services/PeriodExamService.cs @@ -2999,213 +2999,291 @@ namespace BMA.EHR.Recurit.Exam.Service.Services await _contextMetadata.SaveChangesAsync(); } - public async Task UpdateAsyncDisableToPlacement(Guid examId) + public async Task UpdateAsyncDisableToPlacement(Guid examId, DateTime accountStartDate) { - var periodExam = await _context.PeriodExams.AsQueryable() - .Where(x => x.CheckDisability == true) - .FirstOrDefaultAsync(x => x.Id == examId); - - if (periodExam == null) - throw new Exception(GlobalMessages.ExamNotFound); - - var _placement = await _contextMetadata.Placements.AsQueryable() - .FirstOrDefaultAsync(x => x.PlacementType.Name == "คัดเลือกคนพิการ" && x.RefId == periodExam.Id); - if (_placement != null) - throw new Exception("รอบการสอบนี้ได้ทำการบรรจุไปแล้ว"); - - var placement = new Placement + try { - Name = periodExam.Name, - RefId = periodExam.Id, - Round = periodExam.Round == null ? "" : periodExam.Round.ToString(), - Year = (int)(periodExam.Year == null ? 0 : periodExam.Year), - Number = await _context.Disables.AsQueryable().Where(x => x.PeriodExam == periodExam).CountAsync(), - PlacementType = await _contextMetadata.PlacementTypes.FirstOrDefaultAsync(x => x.Name.Trim().ToUpper().Contains("คัดเลือกคนพิการ")) == null ? await _contextMetadata.PlacementTypes.FirstOrDefaultAsync() : await _contextMetadata.PlacementTypes.FirstOrDefaultAsync(x => x.Name.Trim().ToUpper().Contains("คัดเลือกคนพิการ")), - StartDate = DateTime.Now, - EndDate = DateTime.Now.AddYears(2).AddDays(-1), - CreatedAt = DateTime.Now, - CreatedUserId = UserId ?? "", - CreatedFullName = FullName ?? "", - LastUpdatedAt = DateTime.Now, - LastUpdateUserId = UserId ?? "", - LastUpdateFullName = FullName ?? "", - }; - await _contextMetadata.Placements.AddAsync(placement); - var candidates = await _context.Disables.AsQueryable() - .Include(x => x.Addresses) - .Include(x => x.Certificates) - .Include(x => x.Educations) - .Include(x => x.Occupations) - .Where(x => x.PeriodExam == periodExam) - .ToListAsync(); - foreach (var candidate in candidates) - { - var IsOfficer = false; - dynamic org = null; - var apiUrl = $"{_configuration["API"]}/org/profile/citizenid/position/{candidate.CitizenId}"; - using (var client = new HttpClient()) + // 🚀 Prepare HTTP client once + var httpClient1 = new HttpClient(); + httpClient1.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", "")); + httpClient1.DefaultRequestHeaders.Add("api_key", _configuration["API_KEY"]); + var apiUrl1 = $"{_configuration["API"]}/org/pos/level"; + var response1 = await httpClient1.GetStringAsync(apiUrl1); + var posOptions = JsonConvert.DeserializeObject(response1); + + var periodExam = await _context.PeriodExams.AsQueryable() + .Where(x => x.CheckDisability == true) + .FirstOrDefaultAsync(x => x.Id == examId); + + if (periodExam == null) + throw new Exception(GlobalMessages.ExamNotFound); + + var _placement = await _contextMetadata.Placements.AsQueryable() + .FirstOrDefaultAsync(x => x.PlacementType.Name == "คัดเลือกคนพิการ" && x.RefId == periodExam.Id); + if (_placement != null) + throw new Exception("รอบการสอบนี้ได้ทำการบรรจุไปแล้ว"); + + // 🚀 Pre-load all lookup data once + var placementTypesCache = await _contextMetadata.PlacementTypes.ToListAsync(); + var provincesCache = await _contextOrg.province.ToListAsync(); + var districtsCache = await _contextOrg.district.ToListAsync(); + var subDistrictsCache = await _contextOrg.subDistrict.ToListAsync(); + var educationLevelsCache = await _contextOrg.educationLevel.ToListAsync(); + + var placement = new Placement { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api_key", _configuration["API_KEY"]); - var _req = new HttpRequestMessage(HttpMethod.Get, apiUrl); - var _res = await client.SendAsync(_req); - var _result = await _res.Content.ReadAsStringAsync(); + Name = periodExam.Name, + RefId = periodExam.Id, + Round = periodExam.Round?.ToString() ?? "", + Year = (int)(periodExam.Year ?? 0), + Number = await _context.Disables.AsQueryable().Where(x => x.PeriodExam == periodExam).CountAsync(), + PlacementType = placementTypesCache.FirstOrDefault(x => x.Name.Trim().ToUpper().Contains("คัดเลือกคนพิการ")) ?? placementTypesCache.First(), + StartDate = accountStartDate, + EndDate = accountStartDate.AddYears(2).AddDays(-1), + CreatedAt = DateTime.Now, + CreatedUserId = UserId ?? "", + CreatedFullName = FullName ?? "", + LastUpdatedAt = DateTime.Now, + LastUpdateUserId = UserId ?? "", + LastUpdateFullName = FullName ?? "", + }; + await _contextMetadata.Placements.AddAsync(placement); - org = JsonConvert.DeserializeObject(_result); + // 🚀 Load all related data with single queries + var candidates = await _context.Disables.AsQueryable() + .Include(x => x.Addresses) + .Include(x => x.Certificates) + .Include(x => x.Educations) + .Include(x => x.Occupations) + .Where(x => x.PeriodExam == periodExam) + .ToListAsync(); - if (org == null || org.result == null) - { - IsOfficer = false; - } - else - { - IsOfficer = true; - } - } - var Address = candidate.Addresses.FirstOrDefault() == null ? null : $"{candidate.Addresses.FirstOrDefault().Address}"; - var Moo = candidate.Addresses.FirstOrDefault() == null ? null : $" หมู่ {candidate.Addresses.FirstOrDefault().Moo}"; - var Soi = candidate.Addresses.FirstOrDefault() == null ? null : $" ซอย {candidate.Addresses.FirstOrDefault().Soi}"; - var Road = candidate.Addresses.FirstOrDefault() == null ? null : $" ถนน {candidate.Addresses.FirstOrDefault().Road}"; - var Address1 = candidate.Addresses.FirstOrDefault() == null ? null : $"{candidate.Addresses.FirstOrDefault().Address1}"; - var Moo1 = candidate.Addresses.FirstOrDefault() == null ? null : $" หมู่ {candidate.Addresses.FirstOrDefault().Moo1}"; - var Soi1 = candidate.Addresses.FirstOrDefault() == null ? null : $" ซอย {candidate.Addresses.FirstOrDefault().Soi1}"; - var Road1 = candidate.Addresses.FirstOrDefault() == null ? null : $" ถนน {candidate.Addresses.FirstOrDefault().Road1}"; var scoreImport = await _context.ScoreImports.AsQueryable() .FirstOrDefaultAsync(x => x.PeriodExam == periodExam); - var disableScore = await _context.DisableScores.AsQueryable() - .Where(x => x.ScoreImport == scoreImport) - .Where(x => x.ExamId == candidate.ExamId) - .Where(x => x.ExamStatus == "ผ่าน") - .FirstOrDefaultAsync(x => x.ExamId == candidate.ExamId && x.ScoreImport == scoreImport); - if (disableScore == null) - continue; - var placementProfile = new PlacementProfile + + var disableScores = await _context.DisableScores.AsQueryable() + .Where(x => x.ScoreImport == scoreImport && x.ExamStatus == "ผ่าน") + .ToListAsync(); + + var disableScoresDict = disableScores + .Where(x => !string.IsNullOrWhiteSpace(x.ExamId)) + .ToDictionary(x => x.ExamId, x => x); + + // 🚀 Prepare HTTP client once + var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", "")); + httpClient.DefaultRequestHeaders.Add("api_key", _configuration["API_KEY"]); + + // 🚀 Batch HTTP requests + var orgTasks = candidates.Select(async candidate => { - Placement = placement, - PositionCandidate = candidate.PositionName, - PositionType = candidate.PositionType, - PositionLevel = candidate.PositionLevel, - Prefix = candidate.Prefix, - Firstname = candidate.FirstName, - Lastname = candidate.LastName, - Gender = candidate.Gendor, - Nationality = candidate.National, - Race = candidate.Race, - Religion = candidate.Religion, - DateOfBirth = candidate.DateOfBirth, - Relationship = candidate.Marry, - CitizenId = candidate.CitizenId, - CitizenProvinceId = _contextOrg.province.FirstOrDefault(x => x.name == candidate.CitizenCardIssuer)?.Id ?? null, - CitizenDate = candidate.CitizenCardExpireDate, - Telephone = candidate?.Addresses?.FirstOrDefault()?.Telephone ?? null, - MobilePhone = candidate?.Addresses?.FirstOrDefault()?.Mobile ?? null, - RegistAddress = $"{Address}{Moo}{Soi}{Road}", - RegistProvinceId = _contextOrg.province.FirstOrDefault(x => x.name == (candidate!.Addresses!.FirstOrDefault()!.Province ?? ""))?.Id ?? null, - RegistDistrictId = _contextOrg.district.FirstOrDefault(x => x.name == (candidate!.Addresses!.FirstOrDefault()!.District ?? ""))?.Id ?? null, - RegistSubDistrictId = _contextOrg.subDistrict.FirstOrDefault(x => x.name == (candidate!.Addresses!.FirstOrDefault()!.Soi ?? ""))?.Id ?? null, - RegistZipCode = candidate?.Addresses?.FirstOrDefault()?.ZipCode ?? null, - RegistSame = false, - CurrentAddress = $"{Address1}{Moo1}{Soi1}{Road1}", - CurrentProvinceId = _contextOrg.province.FirstOrDefault(x => x.name == (candidate!.Addresses!.FirstOrDefault()!.Province1 ?? ""))?.Id ?? null, - CurrentDistrictId = _contextOrg.district.FirstOrDefault(x => x.name == (candidate!.Addresses!.FirstOrDefault()!.District1 ?? ""))?.Id ?? null, - CurrentSubDistrictId = _contextOrg.subDistrict.FirstOrDefault(x => x.name == (candidate!.Addresses!.FirstOrDefault()!.Soi1 ?? ""))?.Id ?? null, - CurrentZipCode = candidate?.Addresses?.FirstOrDefault()?.ZipCode1 ?? null, - Marry = candidate?.Marry?.Contains("สมรส") ?? false, + if (string.IsNullOrWhiteSpace(candidate.CitizenId)) + return new { CitizenId = candidate.CitizenId ?? "", org = (dynamic?)null }; - OccupationPositionType = "other", - OccupationTelephone = candidate?.Occupations?.FirstOrDefault()?.Telephone ?? null, - OccupationPosition = candidate?.Occupations?.FirstOrDefault()?.Position ?? null, + var apiUrl = $"{_configuration["API"]}/org/profile/citizenid/position/{candidate.CitizenId}"; + try + { + var response = await httpClient.GetStringAsync(apiUrl); + return new { CitizenId = candidate.CitizenId, org = JsonConvert.DeserializeObject(response) }; + } + catch + { + return new { CitizenId = candidate.CitizenId ?? "", org = (dynamic?)null }; + } + }).ToList(); - PointTotalA = disableScore == null ? null : Convert.ToDouble(disableScore.FullA), - PointA = disableScore == null ? null : Convert.ToDouble(disableScore.SumA), - PointTotalB = disableScore == null ? null : Convert.ToDouble(disableScore.FullB), - PointB = disableScore == null ? null : Convert.ToDouble(disableScore.SumB), - PointTotalC = disableScore == null ? null : Convert.ToDouble(disableScore.FullC), - PointC = disableScore == null ? null : Convert.ToDouble(disableScore.SumC), - ExamNumber = disableScore == null || int.TryParse(disableScore.Number, out int n) == false ? null : Convert.ToInt32(disableScore.Number), - ExamRound = null, - IsRelief = false, - PlacementStatus = "UN-CONTAIN", - Pass = disableScore == null ? null : disableScore.ExamStatus, - RemarkHorizontal = "โดยมีเงื่อนไขว่าต้องปฏิบัติงานให้กรุงเทพมหานครเป็นระยะเวลาไม่น้อยกว่า ๕ ปี นับแต่วันที่ได้รับการบรรจุและแต่งตั้ง โดยห้ามโอนไปหน่วยงานหรือส่วนราชการอื่น เว้นเเต่ลาออกจากราชการ", - Amount = org?.result?.amount ?? null, - PositionSalaryAmount = org?.result?.positionSalaryAmount ?? null, - MouthSalaryAmount = org?.result?.mouthSalaryAmount ?? null, - CreatedAt = DateTime.Now, - CreatedUserId = UserId ?? "", - CreatedFullName = FullName ?? "", - LastUpdatedAt = DateTime.Now, - LastUpdateUserId = UserId ?? "", - LastUpdateFullName = FullName ?? "", - IsOfficer = IsOfficer, - profileId = org?.result?.profileId ?? null, - IsOld = org == null || org.result == null ? false : true, - AmountOld = org?.result?.AmountOld ?? null, - nodeOld = org?.result?.node ?? null, - nodeIdOld = org?.result?.nodeId ?? null, - posmasterIdOld = org?.result?.posmasterId ?? null, - rootOld = org?.result?.root ?? null, - rootIdOld = org?.result?.rootId ?? null, - rootShortNameOld = org?.result?.rootShortName ?? null, - child1Old = org?.result?.child1 ?? null, - child1IdOld = org?.result?.child1Id ?? null, - child1ShortNameOld = org?.result?.child1ShortName ?? null, - child2Old = org?.result?.child2 ?? null, - child2IdOld = org?.result?.child2Id ?? null, - child2ShortNameOld = org?.result?.child2ShortName ?? null, - child3Old = org?.result?.child3 ?? null, - child3IdOld = org?.result?.child3Id ?? null, - child3ShortNameOld = org?.result?.child3ShortName ?? null, - child4Old = org?.result?.child4 ?? null, - child4IdOld = org?.result?.child4Id ?? null, - child4ShortNameOld = org?.result?.child4ShortName ?? null, - orgRevisionIdOld = org?.result?.orgRevisionId ?? null, - posMasterNoOld = org?.result?.posMasterNo ?? null, - positionNameOld = org?.result?.position ?? null, - posTypeIdOld = org?.result?.posTypeId ?? null, - posTypeNameOld = org?.result?.posTypeName ?? null, - posLevelIdOld = org?.result?.posLevelId ?? null, - posLevelNameOld = org?.result?.posLevelName ?? null, - }; - await _contextMetadata.PlacementProfiles.AddAsync(placementProfile); + var orgResults = await Task.WhenAll(orgTasks); + var orgDict = orgResults.ToDictionary(x => x.CitizenId ?? "", x => x.org); - var placementEducation = new PlacementEducation + // 🚀 Prepare batch inserts + var placementProfiles = new List(); + var placementEducations = new List(); + var placementCertificates = new List(); + + foreach (var candidate in candidates) { - PlacementProfile = placementProfile, - EducationLevelId = _contextOrg.educationLevel.FirstOrDefault(x => x.name == (candidate!.Educations!.FirstOrDefault()!.HighDegree ?? ""))?.Id ?? null, - EducationLevelName = _contextOrg.educationLevel.FirstOrDefault(x => x.name == (candidate!.Educations!.FirstOrDefault()!.HighDegree ?? ""))?.name ?? null, - Field = candidate?.Educations?.FirstOrDefault()?.Major ?? null, - Gpa = candidate?.Educations?.FirstOrDefault()?.GPA!.ToString() ?? null, - Institute = candidate?.Educations?.FirstOrDefault()?.University ?? null, - Degree = candidate?.Educations?.FirstOrDefault()?.Degree ?? null, - FinishDate = candidate?.Educations?.FirstOrDefault()?.BachelorDate ?? null, - IsDate = true, - CreatedAt = DateTime.Now, - CreatedUserId = UserId ?? "", - LastUpdatedAt = DateTime.Now, - LastUpdateUserId = UserId ?? "", - CreatedFullName = FullName ?? "", - LastUpdateFullName = FullName ?? "", - }; - await _contextMetadata.PlacementEducations.AddAsync(placementEducation); + if (string.IsNullOrWhiteSpace(candidate.ExamId) || + !disableScoresDict.TryGetValue(candidate.ExamId, out var disableScore)) + continue; - var placementCertificate = new PlacementCertificate - { - PlacementProfile = placementProfile, - CertificateNo = candidate?.Certificates?.FirstOrDefault()?.CertificateNo ?? null, - IssueDate = candidate?.Certificates?.FirstOrDefault()?.IssueDate ?? null, - ExpireDate = candidate?.Certificates?.FirstOrDefault()?.ExpiredDate ?? null, - CertificateType = candidate?.Certificates?.FirstOrDefault()?.Description ?? null, - CreatedAt = DateTime.Now, - CreatedUserId = UserId ?? "", - LastUpdatedAt = DateTime.Now, - LastUpdateUserId = UserId ?? "", - CreatedFullName = FullName ?? "", - LastUpdateFullName = FullName ?? "", - }; - await _contextMetadata.PlacementCertificates.AddAsync(placementCertificate); + var org = orgDict.TryGetValue(candidate.CitizenId ?? "", out var orgValue) ? orgValue : null; + var isOfficer = org?.result != null; + + // 🚀 Cache repeated calculations + var firstAddress = candidate.Addresses?.FirstOrDefault(); + var firstEducation = candidate.Educations?.FirstOrDefault(); + var firstCertificate = candidate.Certificates?.FirstOrDefault(); + var firstOccupation = candidate.Occupations?.FirstOrDefault(); + + var registAddress = string.Join("", new[] { + firstAddress?.Address ?? "", + string.IsNullOrWhiteSpace(firstAddress?.Moo) ? "" : $" หมู่ {firstAddress.Moo}", + string.IsNullOrWhiteSpace(firstAddress?.Soi) ? "" : $" ซอย {firstAddress.Soi}", + string.IsNullOrWhiteSpace(firstAddress?.Road) ? "" : $" ถนน {firstAddress.Road}" + }); + + var currentAddress = string.Join("", new[] { + firstAddress?.Address1 ?? "", + string.IsNullOrWhiteSpace(firstAddress?.Moo1) ? "" : $" หมู่ {firstAddress.Moo1}", + string.IsNullOrWhiteSpace(firstAddress?.Soi1) ? "" : $" ซอย {firstAddress.Soi1}", + string.IsNullOrWhiteSpace(firstAddress?.Road1) ? "" : $" ถนน {firstAddress.Road1}" + }); + + // หาค่า posLevelName หลังสุด + var posLevelObject = posOptions?.result?.FirstOrDefault(x => + !string.IsNullOrWhiteSpace(x.posLevelName) && + !string.IsNullOrWhiteSpace(candidate.PositionName) && + candidate.PositionName.Contains(x.posLevelName)); + + // เก็บเฉพาะค่า posLevelName + var posLevelName = posLevelObject?.posLevelName; + + // สร้างตัวแปร PositionName ที่ตัดค่า posLevelName ออก + var positionNameWithoutLevel = candidate.PositionName ?? ""; + if (!string.IsNullOrWhiteSpace(posLevelName)) + { + positionNameWithoutLevel = positionNameWithoutLevel.Replace(posLevelName, "").Trim(); + } + + var placementProfile = new PlacementProfile + { + Placement = placement, + PositionCandidate = positionNameWithoutLevel ?? "", + PositionType = posLevelObject?.posTypes?.posTypeName ?? "", + PositionLevel = posLevelName ?? "", + Prefix = candidate.Prefix ?? "", + Firstname = candidate.FirstName ?? "", + Lastname = candidate.LastName ?? "", + Gender = candidate.Gendor ?? "", + Nationality = candidate.National ?? "", + Race = candidate.Race ?? "", + Religion = candidate.Religion ?? "", + DateOfBirth = candidate.DateOfBirth, + Relationship = candidate.Marry ?? "", + CitizenId = candidate.CitizenId ?? "", + CitizenProvinceId = provincesCache.FirstOrDefault(x => x.name == candidate.CitizenCardIssuer)?.Id, + CitizenDate = candidate.CitizenCardExpireDate, + Telephone = firstAddress?.Telephone ?? "", + MobilePhone = firstAddress?.Mobile ?? "", + RegistAddress = registAddress, + RegistProvinceId = provincesCache.FirstOrDefault(x => x.name == firstAddress?.Province)?.Id, + RegistDistrictId = districtsCache.FirstOrDefault(x => x.name == firstAddress?.District)?.Id, + RegistSubDistrictId = subDistrictsCache.FirstOrDefault(x => x.name == firstAddress?.Soi)?.Id, + RegistZipCode = firstAddress?.ZipCode ?? "", + RegistSame = false, + CurrentAddress = currentAddress, + CurrentProvinceId = provincesCache.FirstOrDefault(x => x.name == firstAddress?.Province1)?.Id, + CurrentDistrictId = districtsCache.FirstOrDefault(x => x.name == firstAddress?.District1)?.Id, + CurrentSubDistrictId = subDistrictsCache.FirstOrDefault(x => x.name == firstAddress?.Soi1)?.Id, + CurrentZipCode = firstAddress?.ZipCode1, + Marry = candidate.Marry?.Contains("สมรส") ?? false, + OccupationPositionType = "other", + OccupationTelephone = firstOccupation?.Telephone ?? "", + OccupationPosition = firstOccupation?.Position ?? "", + PointTotalA = Convert.ToDouble(disableScore.FullA), + PointA = Convert.ToDouble(disableScore.SumA), + PointTotalB = Convert.ToDouble(disableScore.FullB), + PointB = Convert.ToDouble(disableScore.SumB), + PointTotalC = Convert.ToDouble(disableScore.FullC), + PointC = Convert.ToDouble(disableScore.SumC), + ExamNumber = !string.IsNullOrWhiteSpace(disableScore.Number) && int.TryParse(disableScore.Number, out int n) ? n : null, + ExamRound = null, + IsRelief = false, + PlacementStatus = "UN-CONTAIN", + Pass = disableScore.ExamStatus ?? "", + RemarkHorizontal = "โดยมีเงื่อนไขว่าต้องปฏิบัติงานให้กรุงเทพมหานครเป็นระยะเวลาไม่น้อยกว่า ๕ ปี นับแต่วันที่ได้รับการบรรจุและแต่งตั้ง โดยห้ามโอนไปหน่วยงานหรือส่วนราชการอื่น เว้นเเต่ลาออกจากราชการ", + Amount = org?.result?.amount, + PositionSalaryAmount = org?.result?.positionSalaryAmount, + MouthSalaryAmount = org?.result?.mouthSalaryAmount, + CreatedAt = DateTime.Now, + CreatedUserId = UserId ?? "", + CreatedFullName = FullName ?? "", + LastUpdatedAt = DateTime.Now, + LastUpdateUserId = UserId ?? "", + LastUpdateFullName = FullName ?? "", + IsOfficer = isOfficer, + profileId = org?.result?.profileId ?? "", + IsOld = org?.result != null, + AmountOld = org?.result?.AmountOld, + nodeOld = org?.result?.node ?? "", + nodeIdOld = org?.result?.nodeId ?? "", + posmasterIdOld = org?.result?.posmasterId ?? "", + rootOld = org?.result?.root ?? "", + rootIdOld = org?.result?.rootId ?? "", + rootShortNameOld = org?.result?.rootShortName ?? "", + child1Old = org?.result?.child1 ?? "", + child1IdOld = org?.result?.child1Id ?? "", + child1ShortNameOld = org?.result?.child1ShortName ?? "", + child2Old = org?.result?.child2 ?? "", + child2IdOld = org?.result?.child2Id ?? "", + child2ShortNameOld = org?.result?.child2ShortName ?? "", + child3Old = org?.result?.child3 ?? "", + child3IdOld = org?.result?.child3Id ?? "", + child3ShortNameOld = org?.result?.child3ShortName ?? "", + child4Old = org?.result?.child4 ?? "", + child4IdOld = org?.result?.child4Id ?? "", + child4ShortNameOld = org?.result?.child4ShortName ?? "", + orgRevisionIdOld = org?.result?.orgRevisionId ?? "", + posMasterNoOld = org?.result?.posMasterNo, + positionNameOld = org?.result?.position ?? "", + posTypeIdOld = org?.result?.posTypeId ?? "", + posTypeNameOld = org?.result?.posTypeName ?? "", + posLevelIdOld = org?.result?.posLevelId ?? "", + posLevelNameOld = org?.result?.posLevelName ?? "", + }; + placementProfiles.Add(placementProfile); + + var placementEducation = new PlacementEducation + { + PlacementProfile = placementProfile, + EducationLevelId = educationLevelsCache.FirstOrDefault(x => x.name == firstEducation?.HighDegree)?.Id, + EducationLevelName = educationLevelsCache.FirstOrDefault(x => x.name == firstEducation?.HighDegree)?.name, + Field = firstEducation?.Major ?? "", + Gpa = firstEducation?.GPA.ToString() ?? "", + Institute = firstEducation?.University ?? "", + Degree = firstEducation?.Degree ?? "", + FinishDate = firstEducation?.BachelorDate, + IsDate = true, + CreatedAt = DateTime.Now, + CreatedUserId = UserId ?? "", + LastUpdatedAt = DateTime.Now, + LastUpdateUserId = UserId ?? "", + CreatedFullName = FullName ?? "", + LastUpdateFullName = FullName ?? "", + }; + placementEducations.Add(placementEducation); + + var placementCertificate = new PlacementCertificate + { + PlacementProfile = placementProfile, + CertificateNo = firstCertificate?.CertificateNo ?? "", + IssueDate = firstCertificate?.IssueDate, + ExpireDate = firstCertificate?.ExpiredDate, + CertificateType = firstCertificate?.Description ?? "", + CreatedAt = DateTime.Now, + CreatedUserId = UserId ?? "", + LastUpdatedAt = DateTime.Now, + LastUpdateUserId = UserId ?? "", + CreatedFullName = FullName ?? "", + LastUpdateFullName = FullName ?? "", + }; + placementCertificates.Add(placementCertificate); + } + + // 🚀 Batch insert all records + await _contextMetadata.PlacementProfiles.AddRangeAsync(placementProfiles); + await _contextMetadata.PlacementEducations.AddRangeAsync(placementEducations); + await _contextMetadata.PlacementCertificates.AddRangeAsync(placementCertificates); + + // 🚀 Single SaveChanges at the end + await _contextMetadata.SaveChangesAsync(); + + httpClient.Dispose(); + } + catch + { + throw; } - await _contextMetadata.SaveChangesAsync(); }