Merge branch 'develop' into dev
All checks were successful
Build & Deploy on Dev / build (push) Successful in 52s

* develop:
  แก้แสดงปีงบเป็นพศ. issue #1873
  แก้ cal  api เยอะไป
  แก้ time out
  fix bug โอนคนไปบรรจุ (#4)
  migrate + ปรับสอบคัดเลือกผู้พิการ (#3)
This commit is contained in:
Warunee Tamkoo 2025-10-21 12:05:49 +07:00
commit c7024e02fc
13 changed files with 6634 additions and 162 deletions

View file

@ -1299,19 +1299,19 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers
r.LastName = workSheet?.Cells[row, 8]?.GetValue<string>() ?? ""; r.LastName = workSheet?.Cells[row, 8]?.GetValue<string>() ?? "";
r.Gendor = workSheet?.Cells[row, 98]?.GetValue<string>() ?? ""; r.Gendor = workSheet?.Cells[row, 98]?.GetValue<string>() ?? "";
r.National = workSheet?.Cells[row, 9]?.GetValue<string>() ?? ""; r.National = workSheet?.Cells[row, 9]?.GetValue<string>() ?? "";
r.Race = "";//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? ""; r.Race = "";
r.Religion = workSheet?.Cells[row, 10]?.GetValue<string>() ?? ""; r.Religion = workSheet?.Cells[row, 10]?.GetValue<string>() ?? "";
r.DateOfBirth = !string.IsNullOrWhiteSpace(workSheet?.Cells[row, 11]?.GetValue<string>()) ? _disableService.CheckDateTime(workSheet?.Cells[row, 11]?.GetValue<string>() ?? "", "dd/MM/yyyy") : DateTime.MinValue; r.DateOfBirth = !string.IsNullOrWhiteSpace(workSheet?.Cells[row, 11]?.GetValue<string>()) ? _disableService.CheckDateTime(workSheet?.Cells[row, 11]?.GetValue<string>() ?? "", "dd/MM/yyyy") : null;
r.CitizenId = workSheet?.Cells[row, 12]?.GetValue<string>() ?? ""; r.CitizenId = workSheet?.Cells[row, 12]?.GetValue<string>() ?? "";
r.typeTest = workSheet?.Cells[row, 13]?.GetValue<string>() ?? ""; r.typeTest = workSheet?.Cells[row, 13]?.GetValue<string>() ?? "";
r.Marry = "";//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? ""; r.Marry = "";
r.Isspecial = "N"; r.Isspecial = "N";
r.CitizenCardIssuer = "";//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? ""; r.CitizenCardIssuer = "";
r.CitizenCardExpireDate = DateTime.MinValue;//Convert.ToDateTime(workSheet?.Cells[row, 9999]?.GetValue<string>().ToDateTime(DateTimeFormat.Ymd, "-")); r.CitizenCardExpireDate = null;
r.ApplyDate = !string.IsNullOrWhiteSpace(workSheet?.Cells[row, 87]?.GetValue<string>()) ? _disableService.CheckDateTime(workSheet?.Cells[row, 87]?.GetValue<string>() ?? "", "dd/MM/yyyy") : DateTime.MinValue; r.ModifiedDate = null;
r.PositionType = "";//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? ""; r.ApplyDate = !string.IsNullOrWhiteSpace(workSheet?.Cells[row, 87]?.GetValue<string>()) ? _disableService.CheckDateTime(workSheet?.Cells[row, 87]?.GetValue<string>() ?? "", "dd/MM/yyyy") : null;
r.PositionLevel = "";//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? ""; r.PositionType = "";
r.PositionLevel = "";
r.CreatedAt = DateTime.Now; r.CreatedAt = DateTime.Now;
r.CreatedUserId = UserId ?? ""; r.CreatedUserId = UserId ?? "";
r.CreatedFullName = FullName ?? "System Administrator"; r.CreatedFullName = FullName ?? "System Administrator";
@ -1324,13 +1324,13 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers
{ {
Degree = workSheet?.Cells[row, 18]?.GetValue<string>() ?? "", Degree = workSheet?.Cells[row, 18]?.GetValue<string>() ?? "",
Major = workSheet?.Cells[row, 19]?.GetValue<string>() == "อื่น ๆ" ? workSheet?.Cells[row, 20]?.GetValue<string>() ?? "" : workSheet?.Cells[row, 19]?.GetValue<string>() ?? "", Major = workSheet?.Cells[row, 19]?.GetValue<string>() == "อื่น ๆ" ? workSheet?.Cells[row, 20]?.GetValue<string>() ?? "" : workSheet?.Cells[row, 19]?.GetValue<string>() ?? "",
MajorGroupId = "",//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "", MajorGroupId = "",
MajorGroupName = "",//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "", MajorGroupName = "",
University = workSheet?.Cells[row, 21]?.GetValue<string>() == "อื่น ๆ" ? workSheet?.Cells[row, 22]?.GetValue<string>() ?? "" : workSheet?.Cells[row, 21]?.GetValue<string>() ?? "", University = workSheet?.Cells[row, 21]?.GetValue<string>() == "อื่น ๆ" ? workSheet?.Cells[row, 22]?.GetValue<string>() ?? "" : workSheet?.Cells[row, 21]?.GetValue<string>() ?? "",
GPA = (double)workSheet?.Cells[row, 26]?.GetValue<double>(), GPA = (double)workSheet?.Cells[row, 26]?.GetValue<double>(),
Specialist = "",//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "", Specialist = "",
HighDegree = workSheet?.Cells[row, 27]?.GetValue<string>() ?? "", HighDegree = workSheet?.Cells[row, 27]?.GetValue<string>() ?? "",
BachelorDate = !string.IsNullOrWhiteSpace(workSheet?.Cells[row, 25]?.GetValue<string>()) ? _disableService.CheckDateTime(workSheet?.Cells[row, 11]?.GetValue<string>() ?? "", "dd/MM/yyyy") : DateTime.MinValue, BachelorDate = !string.IsNullOrWhiteSpace(workSheet?.Cells[row, 25]?.GetValue<string>()) ? _disableService.CheckDateTime(workSheet?.Cells[row, 25]?.GetValue<string>() ?? "", "dd/MM/yyyy") : null,
CreatedAt = DateTime.Now, CreatedAt = DateTime.Now,
CreatedUserId = UserId ?? "", CreatedUserId = UserId ?? "",
CreatedFullName = FullName ?? "System Administrator", CreatedFullName = FullName ?? "System Administrator",
@ -1345,8 +1345,8 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers
Occupation = workSheet?.Cells[row, 33]?.GetValue<string>() == "อื่น ๆ" ? workSheet?.Cells[row, 34]?.GetValue<string>() ?? "" : workSheet?.Cells[row, 33]?.GetValue<string>() ?? "", Occupation = workSheet?.Cells[row, 33]?.GetValue<string>() == "อื่น ๆ" ? workSheet?.Cells[row, 34]?.GetValue<string>() ?? "" : workSheet?.Cells[row, 33]?.GetValue<string>() ?? "",
Position = workSheet?.Cells[row, 37]?.GetValue<string>() ?? "", Position = workSheet?.Cells[row, 37]?.GetValue<string>() ?? "",
Workplace = $"{(workSheet?.Cells[row, 36]?.GetValue<string>() ?? "")} {(workSheet?.Cells[row, 35]?.GetValue<string>() ?? "")}", Workplace = $"{(workSheet?.Cells[row, 36]?.GetValue<string>() ?? "")} {(workSheet?.Cells[row, 35]?.GetValue<string>() ?? "")}",
Telephone = "",//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "", Telephone = "",
WorkAge = "",//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "", WorkAge = "",
CreatedAt = DateTime.Now, CreatedAt = DateTime.Now,
CreatedUserId = UserId ?? "", CreatedUserId = UserId ?? "",
CreatedFullName = FullName ?? "System Administrator", CreatedFullName = FullName ?? "System Administrator",
@ -1367,7 +1367,7 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers
Province = workSheet?.Cells[row, 56]?.GetValue<string>() ?? "", Province = workSheet?.Cells[row, 56]?.GetValue<string>() ?? "",
ZipCode = workSheet?.Cells[row, 57]?.GetValue<string>() ?? "", ZipCode = workSheet?.Cells[row, 57]?.GetValue<string>() ?? "",
Telephone = workSheet?.Cells[row, 58]?.GetValue<string>() ?? "", Telephone = workSheet?.Cells[row, 58]?.GetValue<string>() ?? "",
Mobile = "",//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "", Mobile = "",
Address1 = $"{(workSheet?.Cells[row, 61]?.GetValue<string>() ?? "")} {(workSheet?.Cells[row, 62]?.GetValue<string>() ?? "")}", Address1 = $"{(workSheet?.Cells[row, 61]?.GetValue<string>() ?? "")} {(workSheet?.Cells[row, 62]?.GetValue<string>() ?? "")}",
Moo1 = workSheet?.Cells[row, 63]?.GetValue<string>() ?? "", Moo1 = workSheet?.Cells[row, 63]?.GetValue<string>() ?? "",
Soi1 = workSheet?.Cells[row, 64]?.GetValue<string>() ?? "", Soi1 = workSheet?.Cells[row, 64]?.GetValue<string>() ?? "",
@ -1410,21 +1410,21 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers
LastUpdateUserId = UserId ?? "", LastUpdateUserId = UserId ?? "",
LastUpdateFullName = FullName ?? "System Administrator" LastUpdateFullName = FullName ?? "System Administrator"
}); });
/* Comment ข้อมูลใบประกอบวิชาชีพ เพราะใน template ยังไม่มีคอลัมน์ที่ระบุข้อมูลส่วนนี้ */
// certificate //// certificate
r.Certificates.Add(new DisableCertificate() //r.Certificates.Add(new DisableCertificate()
{ //{
CertificateNo = "",//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "", // CertificateNo = "",//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "",
Description = "",//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "", // Description = "",//workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "",
IssueDate = DateTime.MinValue,//Convert.ToDateTime(workSheet?.Cells[row, 9999]?.GetValue<string>().ToDateTime(DateTimeFormat.Ymd, "-")), // IssueDate = DateTime.MinValue,//Convert.ToDateTime(workSheet?.Cells[row, 9999]?.GetValue<string>().ToDateTime(DateTimeFormat.Ymd, "-")),
ExpiredDate = DateTime.MinValue,//Convert.ToDateTime(workSheet?.Cells[row, 9999]?.GetValue<string>().ToDateTime(DateTimeFormat.Ymd, "-")), // ExpiredDate = DateTime.MinValue,//Convert.ToDateTime(workSheet?.Cells[row, 9999]?.GetValue<string>().ToDateTime(DateTimeFormat.Ymd, "-")),
CreatedAt = DateTime.Now, // CreatedAt = DateTime.Now,
CreatedUserId = UserId ?? "", // CreatedUserId = UserId ?? "",
CreatedFullName = FullName ?? "System Administrator", // CreatedFullName = FullName ?? "System Administrator",
LastUpdatedAt = DateTime.Now, // LastUpdatedAt = DateTime.Now,
LastUpdateUserId = UserId ?? "", // LastUpdateUserId = UserId ?? "",
LastUpdateFullName = FullName ?? "System Administrator" // LastUpdateFullName = FullName ?? "System Administrator"
}); //});
r.PeriodExam = imported; r.PeriodExam = imported;
_context.Disables.Add(r); _context.Disables.Add(r);
@ -2035,13 +2035,8 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers
); );
} }
int total = await query.CountAsync(); // join กับ DisableScores ก่อน เพื่อ filter ตาม ExamStatus
var queryWithScores = query
query = query
.Skip((req.Page - 1) * req.PageSize)
.Take(req.PageSize);
var data = await query
.GroupJoin( .GroupJoin(
_context.DisableScores.Include(x => x.ScoreImport), _context.DisableScores.Include(x => x.ScoreImport),
rc => new { rc.PeriodExam!.Id, rc.ExamId }, rc => new { rc.PeriodExam!.Id, rc.ExamId },
@ -2050,49 +2045,78 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers
) )
.SelectMany( .SelectMany(
x => x.scores.DefaultIfEmpty(), x => x.scores.DefaultIfEmpty(),
(x, sr) => new (x, sr) => new { x.disable, score = sr }
{ );
examID = x.disable.ExamId,
profileID = x.disable.CitizenId, // filter ตาม req.ExamResult
prefix = x.disable.Prefix, if (!string.IsNullOrWhiteSpace(req.ExamResult))
fullName = $"{x.disable.FirstName} {x.disable.LastName}", {
dateOfBirth = x.disable.DateOfBirth != null && x.disable.DateOfBirth != DateTime.MinValue switch (req.ExamResult.ToLower())
? x.disable.DateOfBirth.ToThaiShortDate() {
: "", case "missed_exam":
gender = x.disable.Gendor, queryWithScores = queryWithScores.Where(x => x.score != null && x.score.ExamStatus == "ขส.");
degree = x.disable.Educations.Any() ? x.disable.Educations.First().Degree : "", break;
major = x.disable.Educations.Any() ? x.disable.Educations.First().Major : "", case "pass":
certificateNo = x.disable.Certificates.Any() queryWithScores = queryWithScores.Where(x => x.score != null && x.score.ExamStatus == "ผ่าน");
? x.disable.Certificates.First().CertificateNo ?? "" break;
: "", case "notpass":
certificateIssueDate = x.disable.Certificates.Any() && x.disable.Certificates.First().IssueDate != null && x.disable.Certificates.First().IssueDate != DateTime.MinValue queryWithScores = queryWithScores.Where(x => x.score != null && x.score.ExamStatus == "ไม่ผ่าน");
? x.disable.Certificates.First().IssueDate.ToThaiShortDate() break;
: "", }
examScore = sr == null ? 0.0 : sr.TotalScore, }
examResult = sr == null ? "" : sr.ExamStatus,
examAttribute = x.disable.Certificates.Any() && x.disable.Certificates.First().IssueDate != null // total count หลังกรอง
? _disableService.CheckValidCertificate(x.disable.Certificates.First().IssueDate, 5) int total = await queryWithScores.CountAsync();
? "มีคุณสมบัติ" : "ไม่มีคุณสมบัติ"
: "ไม่มีคุณสมบัติ", // pagination
remark = x.disable.Remark, queryWithScores = queryWithScores
isSpecial = x.disable.Isspecial == "Y" ? x.disable.Isspecial : "", .Skip((req.Page - 1) * req.PageSize)
applyDate = x.disable.ApplyDate != null && x.disable.ApplyDate != DateTime.MinValue .Take(req.PageSize);
? x.disable.ApplyDate.ToThaiShortDate()
: "", // mapping
university = x.disable.Educations.Any() ? x.disable.Educations.First().University : "", var data = await queryWithScores
position_name = x.disable.PositionName, .Select(x => new
hddPosition = x.disable.HddPosition ?? "", {
typeTest = x.disable.typeTest ?? "", examID = x.disable.ExamId,
position_level = x.disable.PositionLevel ?? "", profileID = x.disable.CitizenId,
position_type = x.disable.PositionType ?? "", prefix = x.disable.Prefix,
exam_name = x.disable.PeriodExam!.Name, fullName = $"{x.disable.FirstName} {x.disable.LastName}",
exam_order = x.disable.PeriodExam != null && x.disable.PeriodExam.Round != null dateOfBirth = x.disable.DateOfBirth.HasValue && x.disable.DateOfBirth.Value != DateTime.MinValue
? x.disable.PeriodExam.Round.ToString() ? x.disable.DateOfBirth.Value.ToThaiShortDate()
: "", : "",
score_year = x.disable.PeriodExam != null && x.disable.PeriodExam.Year != null gender = x.disable.Gendor,
? (x.disable.PeriodExam.Year > 2500 ? x.disable.PeriodExam.Year : x.disable.PeriodExam.Year + 543).ToString() degree = x.disable.Educations.Any() ? x.disable.Educations.First().Degree : "",
: "", major = x.disable.Educations.Any() ? x.disable.Educations.First().Major : "",
}) certificateNo = x.disable.Certificates.Any() ? x.disable.Certificates.First().CertificateNo ?? "" : "",
certificateIssueDate = x.disable.Certificates.Any() && x.disable.Certificates.First().IssueDate != null && x.disable.Certificates.First().IssueDate != DateTime.MinValue
? x.disable.Certificates.First().IssueDate.ToThaiShortDate()
: "",
examScore = x.score == null ? 0.0 : x.score.TotalScore,
examResult = x.score == null ? "" : x.score.ExamStatus,
examAttribute = x.disable.Certificates.Any() && x.disable.Certificates.First().IssueDate != null
? _disableService.CheckValidCertificate(x.disable.Certificates.First().IssueDate, 5)
? "มีคุณสมบัติ" : "ไม่มีคุณสมบัติ"
: "ไม่มีคุณสมบัติ",
remark = x.disable.Remark,
isSpecial = x.disable.Isspecial == "Y" ? x.disable.Isspecial : "",
applyDate = x.disable.ApplyDate.HasValue && x.disable.ApplyDate.Value != DateTime.MinValue
? x.disable.ApplyDate.Value.ToThaiShortDate()
: "",
university = x.disable.Educations.Any() ? x.disable.Educations.First().University : "",
position_name = x.disable.PositionName,
hddPosition = x.disable.HddPosition ?? "",
typeTest = x.disable.typeTest ?? "",
position_level = x.disable.PositionLevel ?? "",
position_type = x.disable.PositionType ?? "",
exam_name = x.disable.PeriodExam!.Name,
exam_order = x.disable.PeriodExam != null && x.disable.PeriodExam.Round != null
? x.disable.PeriodExam.Round.ToString()
: "",
score_year = x.disable.PeriodExam != null && x.disable.PeriodExam.Year != null
? (x.disable.PeriodExam.Year > 2500 ? x.disable.PeriodExam.Year : x.disable.PeriodExam.Year + 543).ToString()
: "",
number = x.score == null ? "" : x.score.Number,
})
.ToListAsync(); .ToListAsync();
// --------------------------- // ---------------------------
@ -2146,7 +2170,7 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers
total = total, total = total,
header = header, header = header,
round = periodExam.Round, round = periodExam.Round,
year = periodExam.Year year = periodExam.Year.Value.ToThaiYear()
}); });
} }
catch (Exception ex) catch (Exception ex)
@ -2177,10 +2201,8 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers
ProfileID = p.CitizenId, ProfileID = p.CitizenId,
p.Prefix, p.Prefix,
FullName = $"{p.FirstName} {p.LastName}", FullName = $"{p.FirstName} {p.LastName}",
DateOfBirth = p.DateOfBirth != null DateOfBirth = p.DateOfBirth.HasValue && p.DateOfBirth.Value != DateTime.MinValue
? p.DateOfBirth != DateTime.MinValue ? p.DateOfBirth.Value.ToThaiShortDate()
? p.DateOfBirth.ToThaiShortDate()
: ""
: "", : "",
Gender = p.Gendor, Gender = p.Gendor,
Degree = p.Educations.First().Degree, Degree = p.Educations.First().Degree,
@ -2194,7 +2216,11 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers
: "" : ""
: "", : "",
ExamResult = sr == null ? "" : sr.ExamStatus, ExamResult = sr == null ? "" : sr.ExamStatus,
ExamAttribute = _disableService.CheckValidCertificate(p.Certificates.First().IssueDate, 5) ? "มีคุณสมบัติ" : "ไม่มีคุณสมบัติ", ExamAttribute = p.Certificates.Count > 0 ?
_disableService.CheckValidCertificate(p.Certificates.First().IssueDate, 5)
? "มีคุณสมบัติ"
: "ไม่มีคุณสมบัติ"
: "ไม่มีคุณสมบัติ",
IsSpecial = p.Isspecial, IsSpecial = p.Isspecial,
Remark = p.Remark, Remark = p.Remark,
University = p.Educations.First().University, University = p.Educations.First().University,
@ -2484,10 +2510,8 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers
ExamID = p.ExamId != null ? p.ExamId.ToThaiNumber() : "", ExamID = p.ExamId != null ? p.ExamId.ToThaiNumber() : "",
CitizenId = p.CitizenId != null ? p.CitizenId.ToThaiNumber() : "", CitizenId = p.CitizenId != null ? p.CitizenId.ToThaiNumber() : "",
FullName = $"{p.Prefix}{p.FirstName} {p.LastName}", FullName = $"{p.Prefix}{p.FirstName} {p.LastName}",
DateOfBirth = p.DateOfBirth != null DateOfBirth = p.DateOfBirth.HasValue && p.DateOfBirth.Value != DateTime.MinValue
? p.DateOfBirth != DateTime.MinValue ? p.DateOfBirth.Value.ToThaiShortDate().ToThaiNumber()
? p.DateOfBirth.ToThaiShortDate().ToString().ToThaiNumber()
: ""
: "", : "",
Gender = p.Gendor, Gender = p.Gendor,
Degree = p.Educations.First().Degree, Degree = p.Educations.First().Degree,

View file

@ -176,7 +176,9 @@ namespace BMA.EHR.Recurit.Exam.Service.Controllers
CitizenId = p.CitizenId, CitizenId = p.CitizenId,
p.Prefix, p.Prefix,
FullName = $"{p.Prefix}{p.FirstName} {p.LastName}", FullName = $"{p.Prefix}{p.FirstName} {p.LastName}",
DateOfBirth = p.DateOfBirth.ToThaiShortDate(), DateOfBirth = p.DateOfBirth.HasValue && p.DateOfBirth.Value != DateTime.MinValue
? p.DateOfBirth.Value.ToThaiShortDate()
: "",
Gender = p.Gendor, Gender = p.Gendor,
Degree = p.Educations.First().Degree, Degree = p.Educations.First().Degree,
Major = p.Educations.First().Major, Major = p.Educations.First().Major,

View file

@ -37,21 +37,32 @@ namespace BMA.EHR.Recurit.Exam.Service.Core
{ {
using (var client = new HttpClient()) using (var client = new HttpClient())
{ {
// Set timeout to 30 seconds instead of default 100 seconds
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Replace("Bearer ", "")); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Replace("Bearer ", ""));
client.DefaultRequestHeaders.Add("api_key", apiKey); client.DefaultRequestHeaders.Add("api_key", apiKey);
var _res = await client.GetAsync(apiPath);
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var _res = await client.GetAsync(apiPath, cts.Token);
if (_res.IsSuccessStatusCode) if (_res.IsSuccessStatusCode)
{ {
var _result = await _res.Content.ReadAsStringAsync(); var _result = await _res.Content.ReadAsStringAsync();
return _result; return _result;
} }
return string.Empty; return string.Empty;
} }
} }
catch catch (TaskCanceledException)
{ {
throw; // Log timeout but don't throw - return empty result instead
Console.WriteLine($"API call timed out: {apiPath}");
return string.Empty;
}
catch (Exception ex)
{
// Log other exceptions but don't throw - return empty result instead
Console.WriteLine($"API call failed: {apiPath}, Error: {ex.Message}");
return string.Empty;
} }
} }
@ -60,10 +71,10 @@ namespace BMA.EHR.Recurit.Exam.Service.Core
try try
{ {
var apiPath = $"{_configuration["API"]}/org/dotnet/keycloak/{keycloakId}"; var apiPath = $"{_configuration["API"]}/org/dotnet/keycloak/{keycloakId}";
var apiKey = _configuration["API_KEY"]; var apiKey = _configuration["API_KEY"] ?? "";
var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey); var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey);
if (apiResult != null) if (!string.IsNullOrEmpty(apiResult))
{ {
var raw = JsonConvert.DeserializeObject<GetProfileByKeycloakIdResultLocal>(apiResult); var raw = JsonConvert.DeserializeObject<GetProfileByKeycloakIdResultLocal>(apiResult);
if (raw != null) if (raw != null)
@ -72,9 +83,11 @@ namespace BMA.EHR.Recurit.Exam.Service.Core
return null; return null;
} }
catch catch (Exception ex)
{ {
throw; // Log exception but don't throw - return null instead
Console.WriteLine($"GetProfileByKeycloakIdAsync failed for {keycloakId}: {ex.Message}");
return null;
} }
} }

View file

@ -1004,10 +1004,10 @@ namespace BMA.EHR.Recurit.Exam.Service.Data.Migrations
.HasComment("PrimaryKey") .HasComment("PrimaryKey")
.HasAnnotation("Relational:JsonPropertyName", "id"); .HasAnnotation("Relational:JsonPropertyName", "id");
b.Property<DateTime>("ApplyDate") b.Property<DateTime?>("ApplyDate")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<DateTime>("CitizenCardExpireDate") b.Property<DateTime?>("CitizenCardExpireDate")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<string>("CitizenCardIssuer") b.Property<string>("CitizenCardIssuer")
@ -1043,7 +1043,7 @@ namespace BMA.EHR.Recurit.Exam.Service.Data.Migrations
.HasColumnOrder(101) .HasColumnOrder(101)
.HasComment("User Id ที่สร้างข้อมูล"); .HasComment("User Id ที่สร้างข้อมูล");
b.Property<DateTime>("DateOfBirth") b.Property<DateTime?>("DateOfBirth")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<string>("ExamId") b.Property<string>("ExamId")
@ -1099,7 +1099,7 @@ namespace BMA.EHR.Recurit.Exam.Service.Data.Migrations
.HasMaxLength(20) .HasMaxLength(20)
.HasColumnType("varchar(20)"); .HasColumnType("varchar(20)");
b.Property<DateTime>("ModifiedDate") b.Property<DateTime?>("ModifiedDate")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<string>("National") b.Property<string>("National")
@ -1454,7 +1454,7 @@ namespace BMA.EHR.Recurit.Exam.Service.Data.Migrations
.HasComment("PrimaryKey") .HasComment("PrimaryKey")
.HasAnnotation("Relational:JsonPropertyName", "id"); .HasAnnotation("Relational:JsonPropertyName", "id");
b.Property<DateTime>("BachelorDate") b.Property<DateTime?>("BachelorDate")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<DateTime>("CreatedAt") b.Property<DateTime>("CreatedAt")

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,91 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BMA.EHR.Recurit.Exam.Service.Migrations
{
/// <inheritdoc />
public partial class update_nullable_ : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "ModifiedDate",
table: "Disables",
type: "datetime(6)",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "datetime(6)");
migrationBuilder.AlterColumn<DateTime>(
name: "CitizenCardExpireDate",
table: "Disables",
type: "datetime(6)",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "datetime(6)");
migrationBuilder.AlterColumn<DateTime>(
name: "ApplyDate",
table: "Disables",
type: "datetime(6)",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "datetime(6)");
migrationBuilder.AlterColumn<DateTime>(
name: "BachelorDate",
table: "DisableEducations",
type: "datetime(6)",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "datetime(6)");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "ModifiedDate",
table: "Disables",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
oldClrType: typeof(DateTime),
oldType: "datetime(6)",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "CitizenCardExpireDate",
table: "Disables",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
oldClrType: typeof(DateTime),
oldType: "datetime(6)",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "ApplyDate",
table: "Disables",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
oldClrType: typeof(DateTime),
oldType: "datetime(6)",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "BachelorDate",
table: "DisableEducations",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
oldClrType: typeof(DateTime),
oldType: "datetime(6)",
oldNullable: true);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BMA.EHR.Recurit.Exam.Service.Migrations
{
/// <inheritdoc />
public partial class update_DateOfbirth_No_require : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "DateOfBirth",
table: "Disables",
type: "datetime(6)",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "datetime(6)");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "DateOfBirth",
table: "Disables",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
oldClrType: typeof(DateTime),
oldType: "datetime(6)",
oldNullable: true);
}
}
}

View file

@ -34,8 +34,8 @@ namespace BMA.EHR.Recurit.Exam.Service.Models.Disables
[MaxLength(200)] [MaxLength(200)]
public string Religion { get; set; } = string.Empty;// public string Religion { get; set; } = string.Empty;//
[Required] //[Required]
public DateTime DateOfBirth { get; set; }// public DateTime? DateOfBirth { get; set; }//
[MaxLength(20)] [MaxLength(20)]
public string Marry { get; set; } = string.Empty;// public string Marry { get; set; } = string.Empty;//
@ -49,7 +49,7 @@ namespace BMA.EHR.Recurit.Exam.Service.Models.Disables
[MaxLength(200)] [MaxLength(200)]
public string CitizenCardIssuer { get; set; } = string.Empty;// public string CitizenCardIssuer { get; set; } = string.Empty;//
public DateTime CitizenCardExpireDate { get; set; }// public DateTime? CitizenCardExpireDate { get; set; }//
[MaxLength(200)] [MaxLength(200)]
public string Remark { get; set; } = string.Empty; public string Remark { get; set; } = string.Empty;
@ -73,9 +73,9 @@ namespace BMA.EHR.Recurit.Exam.Service.Models.Disables
public DateTime CreatedDate { get; set; } = DateTime.Now; public DateTime CreatedDate { get; set; } = DateTime.Now;
public DateTime ModifiedDate { get; set; } public DateTime? ModifiedDate { get; set; }
public DateTime ApplyDate { get; set; } public DateTime? ApplyDate { get; set; }
public string? PositionName { get; set; }// public string? PositionName { get; set; }//
public string? PositionType { get; set; } public string? PositionType { get; set; }

View file

@ -26,7 +26,7 @@ namespace BMA.EHR.Recurit.Exam.Service.Models.Disables
[MaxLength(200)] [MaxLength(200)]
public string HighDegree { get; set; }// public string HighDegree { get; set; }//
public DateTime BachelorDate { get; set; }// public DateTime? BachelorDate { get; set; }//
public Disable Disable { get; set; } public Disable Disable { get; set; }
} }

View file

@ -93,6 +93,12 @@ builder.Services.AddTransient<MailService>();
builder.Services.AddTransient<CMSCandidateService>(); builder.Services.AddTransient<CMSCandidateService>();
builder.Services.AddTransient<PermissionRepository>(); builder.Services.AddTransient<PermissionRepository>();
// Configure HttpClient with timeout
builder.Services.AddHttpClient("default", client =>
{
client.Timeout = TimeSpan.FromSeconds(30);
});
// Add services to the container. // Add services to the container.
builder.Services.AddControllers(options => builder.Services.AddControllers(options =>
{ {
@ -128,11 +134,11 @@ app.UseHttpsRedirection();
app.UseCors(); app.UseCors();
app.UseAuthentication(); app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
app.UseMiddleware<RequestLoggingMiddleware>();
app.UseDefaultFiles(); app.UseDefaultFiles();
app.UseStaticFiles(); app.UseStaticFiles();
app.MapControllers(); app.MapControllers();
app.UseMiddleware<RequestLoggingMiddleware>();
// apply migrations // apply migrations
await using var scope = app.Services.CreateAsyncScope(); await using var scope = app.Services.CreateAsyncScope();

View file

@ -64,13 +64,13 @@ namespace BMA.EHR.Recurit.Exam.Service.Services
return valid; return valid;
} }
public DateTime CheckDateTime(string Date, string Formate) public DateTime? CheckDateTime(string Date, string Formate)
{ {
// ตอนนี้ทำไว้ให้รองรับแค่ "dd/MM/yyyy", "yyyy-MM-dd" // ตอนนี้ทำไว้ให้รองรับแค่ "dd/MM/yyyy", "yyyy-MM-dd"
Date = Date.Trim(); Date = Date.Trim();
if (string.IsNullOrWhiteSpace(Date)) if (string.IsNullOrWhiteSpace(Date))
return DateTime.MinValue; return null;
// จะเข้าเฉพาะกรณีที่ string เป็นตัวเลข เช่น "35635", "44561.5" // จะเข้าเฉพาะกรณีที่ string เป็นตัวเลข เช่น "35635", "44561.5"
if (double.TryParse(Date, out double oaDate)) if (double.TryParse(Date, out double oaDate))
@ -88,7 +88,7 @@ namespace BMA.EHR.Recurit.Exam.Service.Services
string[] parts = Date.Trim().Replace("-", "/").Split("/"); string[] parts = Date.Trim().Replace("-", "/").Split("/");
if (parts.Length != 3) if (parts.Length != 3)
return DateTime.MinValue; return null;
int year; int year;
int month; int month;
@ -102,14 +102,14 @@ namespace BMA.EHR.Recurit.Exam.Service.Services
} }
else if (!int.TryParse(parts[2], out year)) else if (!int.TryParse(parts[2], out year))
{ {
return DateTime.MinValue; return null;
} }
if (!int.TryParse(parts[1], out month)) if (!int.TryParse(parts[1], out month))
return DateTime.MinValue; return null;
if (!int.TryParse(parts[0], out day)) if (!int.TryParse(parts[0], out day))
return DateTime.MinValue; return null;
break; break;
@ -120,18 +120,18 @@ namespace BMA.EHR.Recurit.Exam.Service.Services
} }
else if (!int.TryParse(parts[0], out year)) else if (!int.TryParse(parts[0], out year))
{ {
return DateTime.MinValue; return null;
} }
if (!int.TryParse(parts[1], out month)) if (!int.TryParse(parts[1], out month))
return DateTime.MinValue; return null;
if (!int.TryParse(parts[2], out day)) if (!int.TryParse(parts[2], out day))
return DateTime.MinValue; return null;
break; break;
default: default:
return DateTime.MinValue; return null;
} }
if (month < 1 || month > 12) if (month < 1 || month > 12)
@ -144,13 +144,13 @@ namespace BMA.EHR.Recurit.Exam.Service.Services
else if (day > maxDay) else if (day > maxDay)
day = maxDay; day = maxDay;
var normalDate = $"{day}/{(month >= 1 && month <= 9 ? $"0{month}" : month)}/{year}"; var normalDate = $"{(day >= 1 && day <= 9 ? $"0{day}" : day)}/{(month >= 1 && month <= 9 ? $"0{month}" : month)}/{(year > 2500 ? year-543 : year)}";
if (DateTime.TryParseExact(normalDate, "dd/MM/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime parsedDate)) if (DateTime.TryParseExact(normalDate, "dd/MM/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime parsedDate))
{ {
return parsedDate; return parsedDate;
} }
return DateTime.MinValue; return null;
} }
} }
} }

View file

@ -27,6 +27,7 @@ namespace BMA.EHR.Recurit.Exam.Service.Services
private readonly MinIOService _minioService; private readonly MinIOService _minioService;
private readonly MailService _mailService; private readonly MailService _mailService;
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly IHttpClientFactory _httpClientFactory;
#endregion #endregion
@ -38,7 +39,8 @@ namespace BMA.EHR.Recurit.Exam.Service.Services
IHttpContextAccessor httpContextAccessor, IHttpContextAccessor httpContextAccessor,
MinIOService minioService, MinIOService minioService,
MailService mailService, MailService mailService,
IConfiguration configuration) IConfiguration configuration,
IHttpClientFactory httpClientFactory)
{ {
_context = context; _context = context;
_contextMetadata = contextMetadata; _contextMetadata = contextMetadata;
@ -47,6 +49,7 @@ namespace BMA.EHR.Recurit.Exam.Service.Services
_minioService = minioService; _minioService = minioService;
_mailService = mailService; _mailService = mailService;
_configuration = configuration; _configuration = configuration;
_httpClientFactory = httpClientFactory;
} }
#endregion #endregion
@ -3003,13 +3006,25 @@ namespace BMA.EHR.Recurit.Exam.Service.Services
{ {
try try
{ {
// 🚀 Prepare HTTP client once // 🚀 Prepare HTTP client once via factory with timeout
var httpClient1 = new HttpClient(); var clientForPos = _httpClientFactory.CreateClient("default");
httpClient1.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", "")); clientForPos.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", ""));
httpClient1.DefaultRequestHeaders.Add("api_key", _configuration["API_KEY"]); clientForPos.DefaultRequestHeaders.Remove("api_key");
clientForPos.DefaultRequestHeaders.Add("api_key", _configuration["API_KEY"] ?? "");
var apiUrl1 = $"{_configuration["API"]}/org/pos/level"; var apiUrl1 = $"{_configuration["API"]}/org/pos/level";
var response1 = await httpClient1.GetStringAsync(apiUrl1); var response1 = string.Empty;
var posOptions = JsonConvert.DeserializeObject<RecruitPosRequest>(response1); try
{
using var ctsPos = new CancellationTokenSource(TimeSpan.FromSeconds(30));
response1 = await clientForPos.GetStringAsync(apiUrl1, ctsPos.Token);
}
catch (TaskCanceledException)
{
// timeout - fallback to empty posOptions
response1 = string.Empty;
}
var posOptions = string.IsNullOrWhiteSpace(response1) ? null : JsonConvert.DeserializeObject<RecruitPosRequest>(response1);
var periodExam = await _context.PeriodExams.AsQueryable() var periodExam = await _context.PeriodExams.AsQueryable()
.Where(x => x.CheckDisability == true) .Where(x => x.CheckDisability == true)
@ -3069,26 +3084,41 @@ namespace BMA.EHR.Recurit.Exam.Service.Services
.Where(x => !string.IsNullOrWhiteSpace(x.ExamId)) .Where(x => !string.IsNullOrWhiteSpace(x.ExamId))
.ToDictionary(x => x.ExamId, x => x); .ToDictionary(x => x.ExamId, x => x);
// 🚀 Prepare HTTP client once // 🚀 Batch HTTP requests using IHttpClientFactory with concurrency limit and cancellation
var httpClient = new HttpClient(); var clientForOrg = _httpClientFactory.CreateClient("default");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", "")); clientForOrg.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", ""));
httpClient.DefaultRequestHeaders.Add("api_key", _configuration["API_KEY"]); clientForOrg.DefaultRequestHeaders.Remove("api_key");
clientForOrg.DefaultRequestHeaders.Add("api_key", _configuration["API_KEY"] ?? "");
// 🚀 Batch HTTP requests var semaphore = new SemaphoreSlim(10); // limit concurrency
var orgTasks = candidates.Select(async candidate => var orgTasks = candidates.Select(async candidate =>
{ {
if (string.IsNullOrWhiteSpace(candidate.CitizenId)) if (string.IsNullOrWhiteSpace(candidate.CitizenId))
return new { CitizenId = candidate.CitizenId ?? "", org = (dynamic?)null }; return new { CitizenId = candidate.CitizenId ?? "", org = (dynamic?)null };
var apiUrl = $"{_configuration["API"]}/org/profile/citizenid/position/{candidate.CitizenId}"; await semaphore.WaitAsync();
try try
{ {
var response = await httpClient.GetStringAsync(apiUrl); var apiUrl = $"{_configuration["API"]}/org/profile/citizenid/position/{candidate.CitizenId}";
return new { CitizenId = candidate.CitizenId, org = JsonConvert.DeserializeObject<dynamic>(response) }; try
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var response = await clientForOrg.GetStringAsync(apiUrl, cts.Token);
return new { CitizenId = candidate.CitizenId, org = JsonConvert.DeserializeObject<dynamic>(response) };
}
catch (TaskCanceledException)
{
// timeout
return new { CitizenId = candidate.CitizenId ?? "", org = (dynamic?)null };
}
catch (Exception)
{
return new { CitizenId = candidate.CitizenId ?? "", org = (dynamic?)null };
}
} }
catch finally
{ {
return new { CitizenId = candidate.CitizenId ?? "", org = (dynamic?)null }; semaphore.Release();
} }
}).ToList(); }).ToList();
@ -3098,7 +3128,8 @@ namespace BMA.EHR.Recurit.Exam.Service.Services
// 🚀 Prepare batch inserts // 🚀 Prepare batch inserts
var placementProfiles = new List<PlacementProfile>(); var placementProfiles = new List<PlacementProfile>();
var placementEducations = new List<PlacementEducation>(); var placementEducations = new List<PlacementEducation>();
var placementCertificates = new List<PlacementCertificate>(); /*Comment ข้อมูลใบประกอบวิชาชีพ เพราะในไฟล์นำเข้ายังไม่มีคอลัมน์ที่ระบุข้อมูลส่วนนี้*/
//var placementCertificates = new List<PlacementCertificate>();
foreach (var candidate in candidates) foreach (var candidate in candidates)
{ {
@ -3252,33 +3283,32 @@ namespace BMA.EHR.Recurit.Exam.Service.Services
LastUpdateFullName = FullName ?? "", LastUpdateFullName = FullName ?? "",
}; };
placementEducations.Add(placementEducation); placementEducations.Add(placementEducation);
/*Comment ข้อมูลใบประกอบวิชาชีพ เพราะในไฟล์นำเข้ายังไม่มีคอลัมน์ที่ระบุข้อมูลส่วนนี้*/
var placementCertificate = new PlacementCertificate //var placementCertificate = new PlacementCertificate
{ //{
PlacementProfile = placementProfile, // PlacementProfile = placementProfile,
CertificateNo = firstCertificate?.CertificateNo ?? "", // CertificateNo = firstCertificate?.CertificateNo ?? "",
IssueDate = firstCertificate?.IssueDate, // IssueDate = firstCertificate?.IssueDate,
ExpireDate = firstCertificate?.ExpiredDate, // ExpireDate = firstCertificate?.ExpiredDate,
CertificateType = firstCertificate?.Description ?? "", // CertificateType = firstCertificate?.Description ?? "",
CreatedAt = DateTime.Now, // CreatedAt = DateTime.Now,
CreatedUserId = UserId ?? "", // CreatedUserId = UserId ?? "",
LastUpdatedAt = DateTime.Now, // LastUpdatedAt = DateTime.Now,
LastUpdateUserId = UserId ?? "", // LastUpdateUserId = UserId ?? "",
CreatedFullName = FullName ?? "", // CreatedFullName = FullName ?? "",
LastUpdateFullName = FullName ?? "", // LastUpdateFullName = FullName ?? "",
}; //};
placementCertificates.Add(placementCertificate); //placementCertificates.Add(placementCertificate);
} }
// 🚀 Batch insert all records // 🚀 Batch insert all records
await _contextMetadata.PlacementProfiles.AddRangeAsync(placementProfiles); await _contextMetadata.PlacementProfiles.AddRangeAsync(placementProfiles);
await _contextMetadata.PlacementEducations.AddRangeAsync(placementEducations); await _contextMetadata.PlacementEducations.AddRangeAsync(placementEducations);
await _contextMetadata.PlacementCertificates.AddRangeAsync(placementCertificates); /*Comment ข้อมูลใบประกอบวิชาชีพ เพราะในไฟล์นำเข้ายังไม่มีคอลัมน์ที่ระบุข้อมูลส่วนนี้*/
//await _contextMetadata.PlacementCertificates.AddRangeAsync(placementCertificates);
// 🚀 Single SaveChanges at the end // 🚀 Single SaveChanges at the end
await _contextMetadata.SaveChangesAsync(); await _contextMetadata.SaveChangesAsync();
httpClient.Dispose();
} }
catch catch
{ {