add send noti and bulk insert (not complete)

This commit is contained in:
Suphonchai Phoonsawat 2026-05-14 16:29:16 +07:00
parent 0f1ec072ad
commit 5a3fadc574
7 changed files with 232 additions and 160 deletions

View file

@ -755,6 +755,7 @@ namespace BMA.EHR.Recruit.Controllers
ImportDocId = import_doc_id, ImportDocId = import_doc_id,
UserId = UserId, UserId = UserId,
FullName = FullName, FullName = FullName,
Token = token,
Request = req, Request = req,
}); });
await _importJobQueue.EnqueueAsync(job); await _importJobQueue.EnqueueAsync(job);
@ -943,6 +944,7 @@ namespace BMA.EHR.Recruit.Controllers
ImportDocId = import_doc_id, ImportDocId = import_doc_id,
UserId = UserId, UserId = UserId,
FullName = FullName, FullName = FullName,
Token = token,
}); });
await _importJobQueue.EnqueueAsync(job); await _importJobQueue.EnqueueAsync(job);
@ -1013,6 +1015,7 @@ namespace BMA.EHR.Recruit.Controllers
ImportDocId = import_doc_id, ImportDocId = import_doc_id,
UserId = UserId, UserId = UserId,
FullName = FullName, FullName = FullName,
Token = token,
}); });
await _importJobQueue.EnqueueAsync(job); await _importJobQueue.EnqueueAsync(job);
@ -1082,6 +1085,7 @@ namespace BMA.EHR.Recruit.Controllers
RecruitImportId = id, RecruitImportId = id,
UserId = UserId, UserId = UserId,
FullName = FullName, FullName = FullName,
Token = token,
}); });
await _importJobQueue.EnqueueAsync(job); await _importJobQueue.EnqueueAsync(job);

View file

@ -69,6 +69,7 @@ builder.Services.AddAuthorization();
// Register Services // Register Services
builder.Services.AddTransient<RecruitService>(); builder.Services.AddTransient<RecruitService>();
builder.Services.AddTransient<MinIOService>(); builder.Services.AddTransient<MinIOService>();
builder.Services.AddTransient<NotificationService>();
builder.Services.AddTransient<PermissionRepository>(); builder.Services.AddTransient<PermissionRepository>();
builder.Services.AddSingleton<ImportJobQueue>(); builder.Services.AddSingleton<ImportJobQueue>();
builder.Services.AddSingleton<ImportJobTracker>(); builder.Services.AddSingleton<ImportJobTracker>();

View file

@ -47,6 +47,7 @@ public class ImportBackgroundService : BackgroundService
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>(); var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
var minioService = scope.ServiceProvider.GetRequiredService<MinIOService>(); var minioService = scope.ServiceProvider.GetRequiredService<MinIOService>();
var recruitService = scope.ServiceProvider.GetRequiredService<RecruitService>(); var recruitService = scope.ServiceProvider.GetRequiredService<RecruitService>();
var notificationService = scope.ServiceProvider.GetRequiredService<NotificationService>();
var webHostEnv = scope.ServiceProvider.GetRequiredService<IWebHostEnvironment>(); var webHostEnv = scope.ServiceProvider.GetRequiredService<IWebHostEnvironment>();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<ImportBackgroundService>>(); var logger = scope.ServiceProvider.GetRequiredService<ILogger<ImportBackgroundService>>();
@ -71,11 +72,15 @@ public class ImportBackgroundService : BackgroundService
} }
_tracker.UpdateStatus(job.JobId, ImportJobStatus.Completed, job.TotalCount); _tracker.UpdateStatus(job.JobId, ImportJobStatus.Completed, job.TotalCount);
await notificationService.SendImportNotificationAsync(job.Token, false, "ระบบนำเข้าข้อมูลสำเร็จ");
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.LogError(ex, "Import job {JobId} failed: {Message}", job.JobId, ex.Message); logger.LogError(ex, "Import job {JobId} failed: {Message}", job.JobId, ex.Message);
_tracker.UpdateStatus(job.JobId, ImportJobStatus.Failed, 0, ex.InnerException?.Message ?? ex.Message); _tracker.UpdateStatus(job.JobId, ImportJobStatus.Failed, 0, ex.Message);
await notificationService.SendImportNotificationAsync(job.Token, true, ex.Message);
// cleanup minio file on failure // cleanup minio file on failure
if (!string.IsNullOrEmpty(job.ImportDocId)) if (!string.IsNullOrEmpty(job.ImportDocId))
@ -321,6 +326,7 @@ public class ImportBackgroundService : BackgroundService
_context.ChangeTracker.Clear(); _context.ChangeTracker.Clear();
var importId = imported.Id; var importId = imported.Id;
var importRef = _context.Attach(new RecruitImport { Id = importId }).Entity;
using var c_package = new ExcelPackage(new FileInfo(job.ImportFile)); using var c_package = new ExcelPackage(new FileInfo(job.ImportFile));
for (int i = 0; i < c_package.Workbook.Worksheets.Count; i++) for (int i = 0; i < c_package.Workbook.Worksheets.Count; i++)
@ -344,133 +350,145 @@ public class ImportBackgroundService : BackgroundService
var cell1 = workSheet?.Cells[row, 1]?.GetValue<string>(); var cell1 = workSheet?.Cells[row, 1]?.GetValue<string>();
if (cell1 == "" || cell1 == null) break; if (cell1 == "" || cell1 == null) break;
var r = new Models.Recruits.Recruit(); try
r.ExamId = workSheet?.Cells[row, 1]?.GetValue<string>() ?? "";
r.PositionName = workSheet?.Cells[row, 3]?.GetValue<string>() ?? "";
r.HddPosition = workSheet?.Cells[row, 4]?.GetValue<string>() ?? "";
r.Prefix = workSheet?.Cells[row, 5]?.GetValue<string>() == "อื่น ๆ" ? workSheet?.Cells[row, 6]?.GetValue<string>() ?? "" : workSheet?.Cells[row, 5]?.GetValue<string>() ?? "";
r.FirstName = workSheet?.Cells[row, 7]?.GetValue<string>() ?? "";
r.LastName = workSheet?.Cells[row, 8]?.GetValue<string>() ?? "";
r.Gendor = workSheet?.Cells[row, 98]?.GetValue<string>() ?? "";
r.National = workSheet?.Cells[row, 9]?.GetValue<string>() ?? "";
r.Race = "";
r.Religion = workSheet?.Cells[row, 10]?.GetValue<string>() ?? "";
r.DateOfBirth = !string.IsNullOrWhiteSpace(workSheet?.Cells[row, 11]?.GetValue<string>()) ? _recruitService.CheckDateTime(workSheet?.Cells[row, 11]?.GetValue<string>() ?? "", "dd/MM/yyyy") : null;
r.CitizenId = workSheet?.Cells[row, 12]?.GetValue<string>() ?? "";
r.typeTest = workSheet?.Cells[row, 13]?.GetValue<string>() ?? "";
r.Marry = "";
r.Isspecial = "N";
r.CitizenCardExpireDate = null;
r.ModifiedDate = null;
r.ApplyDate = !string.IsNullOrWhiteSpace(workSheet?.Cells[row, 87]?.GetValue<string>()) ? _recruitService.CheckDateTime(workSheet?.Cells[row, 87]?.GetValue<string>() ?? "", "dd/MM/yyyy") : null;
r.PositionType = "";
r.PositionLevel = "";
r.CreatedAt = DateTime.Now;
r.CreatedUserId = job.UserId ?? "";
r.CreatedFullName = job.FullName ?? "System Administrator";
r.LastUpdatedAt = DateTime.Now;
r.LastUpdateUserId = job.UserId ?? "";
r.LastUpdateFullName = job.FullName ?? "System Administrator";
// Store child entities in separate lists for bulk insert
var education = new RecruitEducation()
{ {
Degree = workSheet?.Cells[row, 18]?.GetValue<string>() ?? "", var r = new Models.Recruits.Recruit();
Major = workSheet?.Cells[row, 19]?.GetValue<string>() == "อื่น ๆ" ? workSheet?.Cells[row, 20]?.GetValue<string>() ?? "" : workSheet?.Cells[row, 19]?.GetValue<string>() ?? "", r.Id = Guid.NewGuid();
MajorGroupId = "", r.ExamId = workSheet?.Cells[row, 1]?.GetValue<string>() ?? "";
MajorGroupName = "", r.PositionName = workSheet?.Cells[row, 3]?.GetValue<string>() ?? "";
University = workSheet?.Cells[row, 21]?.GetValue<string>() == "อื่น ๆ" ? workSheet?.Cells[row, 22]?.GetValue<string>() ?? "" : workSheet?.Cells[row, 21]?.GetValue<string>() ?? "", r.HddPosition = workSheet?.Cells[row, 4]?.GetValue<string>() ?? "";
GPA = (double)workSheet?.Cells[row, 26]?.GetValue<double>(), r.Prefix = workSheet?.Cells[row, 5]?.GetValue<string>() == "อื่น ๆ" ? workSheet?.Cells[row, 6]?.GetValue<string>() ?? "" : workSheet?.Cells[row, 5]?.GetValue<string>() ?? "";
Specialist = "", r.FirstName = workSheet?.Cells[row, 7]?.GetValue<string>() ?? "";
HighDegree = workSheet?.Cells[row, 27]?.GetValue<string>() ?? "", r.LastName = workSheet?.Cells[row, 8]?.GetValue<string>() ?? "";
BachelorDate = !string.IsNullOrWhiteSpace(workSheet?.Cells[row, 25]?.GetValue<string>()) ? _recruitService.CheckDateTime(workSheet?.Cells[row, 25]?.GetValue<string>() ?? "", "dd/MM/yyyy") : null, r.Gendor = workSheet?.Cells[row, 98]?.GetValue<string>() ?? "";
CreatedAt = DateTime.Now, r.National = workSheet?.Cells[row, 9]?.GetValue<string>() ?? "";
CreatedUserId = job.UserId ?? "", r.Race = "";
CreatedFullName = job.FullName ?? "System Administrator", r.Religion = workSheet?.Cells[row, 10]?.GetValue<string>() ?? "";
LastUpdatedAt = DateTime.Now, r.DateOfBirth = !string.IsNullOrWhiteSpace(workSheet?.Cells[row, 11]?.GetValue<string>()) ? _recruitService.CheckDateTime(workSheet?.Cells[row, 11]?.GetValue<string>() ?? "", "dd/MM/yyyy") : null;
LastUpdateUserId = job.UserId ?? "", r.CitizenId = workSheet?.Cells[row, 12]?.GetValue<string>() ?? "";
LastUpdateFullName = job.FullName ?? "System Administrator" r.typeTest = workSheet?.Cells[row, 13]?.GetValue<string>() ?? "";
}; r.Marry = "";
r.Isspecial = "N";
r.CitizenCardExpireDate = null;
r.ModifiedDate = null;
r.ApplyDate = !string.IsNullOrWhiteSpace(workSheet?.Cells[row, 87]?.GetValue<string>()) ? _recruitService.CheckDateTime(workSheet?.Cells[row, 87]?.GetValue<string>() ?? "", "dd/MM/yyyy") : null;
r.PositionType = "";
r.PositionLevel = "";
r.CreatedAt = DateTime.Now;
r.CreatedUserId = job.UserId ?? "";
r.CreatedFullName = job.FullName ?? "System Administrator";
r.LastUpdatedAt = DateTime.Now;
r.LastUpdateUserId = job.UserId ?? "";
r.LastUpdateFullName = job.FullName ?? "System Administrator";
r.RecruitImport = importRef;
var occupation = new RecruitOccupation() // Store child entities in separate lists for bulk insert
var education = new RecruitEducation()
{
Id = Guid.NewGuid(),
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>() ?? "",
MajorGroupId = "",
MajorGroupName = "",
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>(),
Specialist = "",
HighDegree = workSheet?.Cells[row, 27]?.GetValue<string>() ?? "",
BachelorDate = !string.IsNullOrWhiteSpace(workSheet?.Cells[row, 25]?.GetValue<string>()) ? _recruitService.CheckDateTime(workSheet?.Cells[row, 25]?.GetValue<string>() ?? "", "dd/MM/yyyy") : null,
Recruit = r,
CreatedAt = DateTime.Now,
CreatedUserId = job.UserId ?? "",
CreatedFullName = job.FullName ?? "System Administrator",
LastUpdatedAt = DateTime.Now,
LastUpdateUserId = job.UserId ?? "",
LastUpdateFullName = job.FullName ?? "System Administrator"
};
var occupation = new RecruitOccupation()
{
Id = Guid.NewGuid(),
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>() ?? "",
Workplace = $"{(workSheet?.Cells[row, 36]?.GetValue<string>() ?? "")} {(workSheet?.Cells[row, 35]?.GetValue<string>() ?? "")}",
Telephone = workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "",
WorkAge = workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "",
Recruit = r,
CreatedAt = DateTime.Now,
CreatedUserId = job.UserId ?? "",
CreatedFullName = job.FullName ?? "System Administrator",
LastUpdatedAt = DateTime.Now,
LastUpdateUserId = job.UserId ?? "",
LastUpdateFullName = job.FullName ?? "System Administrator"
};
var address = new RecruitAddress()
{
Id = Guid.NewGuid(),
Address = $"{(workSheet?.Cells[row, 49]?.GetValue<string>() ?? "")} {(workSheet?.Cells[row, 50]?.GetValue<string>() ?? "")}",
Moo = workSheet?.Cells[row, 51]?.GetValue<string>() ?? "",
Soi = workSheet?.Cells[row, 52]?.GetValue<string>() ?? "",
Road = workSheet?.Cells[row, 53]?.GetValue<string>() ?? "",
District = workSheet?.Cells[row, 54]?.GetValue<string>() ?? "",
Amphur = workSheet?.Cells[row, 55]?.GetValue<string>() ?? "",
Province = workSheet?.Cells[row, 56]?.GetValue<string>() ?? "",
ZipCode = (workSheet?.Cells[row, 57]?.GetValue<string>() ?? "").Trim(),
Telephone = workSheet?.Cells[row, 58]?.GetValue<string>() ?? "",
Mobile = "",
Address1 = $"{(workSheet?.Cells[row, 61]?.GetValue<string>() ?? "")} {(workSheet?.Cells[row, 62]?.GetValue<string>() ?? "")}",
Moo1 = workSheet?.Cells[row, 63]?.GetValue<string>() ?? "",
Soi1 = workSheet?.Cells[row, 64]?.GetValue<string>() ?? "",
Road1 = workSheet?.Cells[row, 65]?.GetValue<string>() ?? "",
District1 = workSheet?.Cells[row, 66]?.GetValue<string>() ?? "",
Amphur1 = workSheet?.Cells[row, 67]?.GetValue<string>() ?? "",
Province1 = workSheet?.Cells[row, 68]?.GetValue<string>() ?? "",
ZipCode1 = (workSheet?.Cells[row, 69]?.GetValue<string>() ?? "").Trim(),
Recruit = r,
CreatedAt = DateTime.Now,
CreatedUserId = job.UserId ?? "",
CreatedFullName = job.FullName ?? "System Administrator",
LastUpdatedAt = DateTime.Now,
LastUpdateUserId = job.UserId ?? "",
LastUpdateFullName = job.FullName ?? "System Administrator"
};
var payment = new RecruitPayment()
{
Id = Guid.NewGuid(),
PaymentId = workSheet?.Cells[row, 104]?.GetValue<string>() ?? "",
CompanyCode = workSheet?.Cells[row, 105]?.GetValue<string>() ?? "",
TextFile = workSheet?.Cells[row, 106]?.GetValue<string>() ?? "",
BankCode = workSheet?.Cells[row, 107]?.GetValue<string>() ?? "",
AccountNumber = workSheet?.Cells[row, 108]?.GetValue<string>() ?? "",
TransDate = workSheet?.Cells[row, 109]?.GetValue<string>() ?? "",
TransTime = workSheet?.Cells[row, 110]?.GetValue<string>() ?? "",
CustomerName = workSheet?.Cells[row, 111]?.GetValue<string>() ?? "",
RefNo1 = workSheet?.Cells[row, 112]?.GetValue<string>() ?? "",
TermBranch = workSheet?.Cells[row, 113]?.GetValue<string>() ?? "",
TellerId = workSheet?.Cells[row, 114]?.GetValue<string>() ?? "",
CreditDebit = workSheet?.Cells[row, 115]?.GetValue<string>() ?? "",
PaymentType = workSheet?.Cells[row, 116]?.GetValue<string>() ?? "",
ChequeNo = workSheet?.Cells[row, 117]?.GetValue<string>() ?? "",
Amount = (decimal)workSheet?.Cells[row, 118]?.GetValue<decimal>(),
ChqueBankCode = workSheet?.Cells[row, 119]?.GetValue<string>() ?? "",
Recruit = r,
CreatedAt = DateTime.Now,
CreatedUserId = job.UserId ?? "",
CreatedFullName = job.FullName ?? "System Administrator",
LastUpdatedAt = DateTime.Now,
LastUpdateUserId = job.UserId ?? "",
LastUpdateFullName = job.FullName ?? "System Administrator"
};
batchRecruits.Add(r);
batchEducations.Add(education);
batchOccupations.Add(occupation);
batchAddresses.Add(address);
batchPayments.Add(payment);
}
catch (Exception ex)
{ {
Occupation = workSheet?.Cells[row, 33]?.GetValue<string>() == "อื่น ๆ" ? workSheet?.Cells[row, 34]?.GetValue<string>() ?? "" : workSheet?.Cells[row, 33]?.GetValue<string>() ?? "", throw new Exception($"Row {row}: {ex.Message}", ex);
Position = workSheet?.Cells[row, 37]?.GetValue<string>() ?? "", }
Workplace = $"{(workSheet?.Cells[row, 36]?.GetValue<string>() ?? "")} {(workSheet?.Cells[row, 35]?.GetValue<string>() ?? "")}",
Telephone = workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "",
WorkAge = workSheet?.Cells[row, 9999]?.GetValue<string>() ?? "",
CreatedAt = DateTime.Now,
CreatedUserId = job.UserId ?? "",
CreatedFullName = job.FullName ?? "System Administrator",
LastUpdatedAt = DateTime.Now,
LastUpdateUserId = job.UserId ?? "",
LastUpdateFullName = job.FullName ?? "System Administrator"
};
var address = new RecruitAddress()
{
Address = $"{(workSheet?.Cells[row, 49]?.GetValue<string>() ?? "")} {(workSheet?.Cells[row, 50]?.GetValue<string>() ?? "")}",
Moo = workSheet?.Cells[row, 51]?.GetValue<string>() ?? "",
Soi = workSheet?.Cells[row, 52]?.GetValue<string>() ?? "",
Road = workSheet?.Cells[row, 53]?.GetValue<string>() ?? "",
District = workSheet?.Cells[row, 54]?.GetValue<string>() ?? "",
Amphur = workSheet?.Cells[row, 55]?.GetValue<string>() ?? "",
Province = workSheet?.Cells[row, 56]?.GetValue<string>() ?? "",
ZipCode = (workSheet?.Cells[row, 57]?.GetValue<string>() ?? "").Trim(),
Telephone = workSheet?.Cells[row, 58]?.GetValue<string>() ?? "",
Mobile = "",
Address1 = $"{(workSheet?.Cells[row, 61]?.GetValue<string>() ?? "")} {(workSheet?.Cells[row, 62]?.GetValue<string>() ?? "")}",
Moo1 = workSheet?.Cells[row, 63]?.GetValue<string>() ?? "",
Soi1 = workSheet?.Cells[row, 64]?.GetValue<string>() ?? "",
Road1 = workSheet?.Cells[row, 65]?.GetValue<string>() ?? "",
District1 = workSheet?.Cells[row, 66]?.GetValue<string>() ?? "",
Amphur1 = workSheet?.Cells[row, 67]?.GetValue<string>() ?? "",
Province1 = workSheet?.Cells[row, 68]?.GetValue<string>() ?? "",
ZipCode1 = (workSheet?.Cells[row, 69]?.GetValue<string>() ?? "").Trim(),
CreatedAt = DateTime.Now,
CreatedUserId = job.UserId ?? "",
CreatedFullName = job.FullName ?? "System Administrator",
LastUpdatedAt = DateTime.Now,
LastUpdateUserId = job.UserId ?? "",
LastUpdateFullName = job.FullName ?? "System Administrator"
};
var payment = new RecruitPayment()
{
PaymentId = workSheet?.Cells[row, 104]?.GetValue<string>() ?? "",
CompanyCode = workSheet?.Cells[row, 105]?.GetValue<string>() ?? "",
TextFile = workSheet?.Cells[row, 106]?.GetValue<string>() ?? "",
BankCode = workSheet?.Cells[row, 107]?.GetValue<string>() ?? "",
AccountNumber = workSheet?.Cells[row, 108]?.GetValue<string>() ?? "",
TransDate = workSheet?.Cells[row, 109]?.GetValue<string>() ?? "",
TransTime = workSheet?.Cells[row, 110]?.GetValue<string>() ?? "",
CustomerName = workSheet?.Cells[row, 111]?.GetValue<string>() ?? "",
RefNo1 = workSheet?.Cells[row, 112]?.GetValue<string>() ?? "",
TermBranch = workSheet?.Cells[row, 113]?.GetValue<string>() ?? "",
TellerId = workSheet?.Cells[row, 114]?.GetValue<string>() ?? "",
CreditDebit = workSheet?.Cells[row, 115]?.GetValue<string>() ?? "",
PaymentType = workSheet?.Cells[row, 116]?.GetValue<string>() ?? "",
ChequeNo = workSheet?.Cells[row, 117]?.GetValue<string>() ?? "",
Amount = (decimal)workSheet?.Cells[row, 118]?.GetValue<decimal>(),
ChqueBankCode = workSheet?.Cells[row, 119]?.GetValue<string>() ?? "",
CreatedAt = DateTime.Now,
CreatedUserId = job.UserId ?? "",
CreatedFullName = job.FullName ?? "System Administrator",
LastUpdatedAt = DateTime.Now,
LastUpdateUserId = job.UserId ?? "",
LastUpdateFullName = job.FullName ?? "System Administrator"
};
r.Educations.Add(education);
r.Occupations.Add(occupation);
r.Addresses.Add(address);
r.Payments.Add(payment);
batchRecruits.Add(r);
batchEducations.Add(education);
batchOccupations.Add(occupation);
batchAddresses.Add(address);
batchPayments.Add(payment);
row++; row++;
batchCount++; batchCount++;
@ -478,26 +496,19 @@ public class ImportBackgroundService : BackgroundService
if (batchCount >= batchSize) if (batchCount >= batchSize)
{ {
// BulkInsert Recruits first (with SetOutputIdentity to get generated Ids) try
await _context.BulkInsertAsync(batchRecruits, options =>
{ {
options.SetOutputIdentity = true; await _context.BulkInsertAsync(batchRecruits);
}); await _context.BulkInsertAsync(batchEducations);
await _context.BulkInsertAsync(batchOccupations);
// Assign generated Recruit Id to child entities await _context.BulkInsertAsync(batchAddresses);
for (int j = 0; j < batchRecruits.Count; j++) await _context.BulkInsertAsync(batchPayments);
{ }
batchEducations[j].Recruit = batchRecruits[j]; catch (Exception ex)
batchOccupations[j].Recruit = batchRecruits[j]; {
batchAddresses[j].Recruit = batchRecruits[j]; var batchStartRow = row - batchCount + 1;
batchPayments[j].Recruit = batchRecruits[j]; throw new Exception($"BulkInsert failed (rows {batchStartRow}-{row - 1}, {batchRecruits.Count} records): {ex.InnerException?.Message ?? ex.Message}", ex);
} }
// BulkInsert child entities (no identity output needed)
await _context.BulkInsertAsync(batchEducations);
await _context.BulkInsertAsync(batchOccupations);
await _context.BulkInsertAsync(batchAddresses);
await _context.BulkInsertAsync(batchPayments);
// Clear all lists for next batch // Clear all lists for next batch
batchRecruits.Clear(); batchRecruits.Clear();
@ -513,23 +524,19 @@ public class ImportBackgroundService : BackgroundService
// Process remaining records // Process remaining records
if (batchRecruits.Count > 0) if (batchRecruits.Count > 0)
{ {
await _context.BulkInsertAsync(batchRecruits, options => try
{ {
options.SetOutputIdentity = true; await _context.BulkInsertAsync(batchRecruits);
}); await _context.BulkInsertAsync(batchEducations);
await _context.BulkInsertAsync(batchOccupations);
for (int j = 0; j < batchRecruits.Count; j++) await _context.BulkInsertAsync(batchAddresses);
{ await _context.BulkInsertAsync(batchPayments);
batchEducations[j].Recruit = batchRecruits[j]; }
batchOccupations[j].Recruit = batchRecruits[j]; catch (Exception ex)
batchAddresses[j].Recruit = batchRecruits[j]; {
batchPayments[j].Recruit = batchRecruits[j]; var batchStartRow = row - batchCount + 1;
throw new Exception($"BulkInsert failed (rows {batchStartRow}-{row - 1}, {batchRecruits.Count} records): {ex.InnerException?.Message ?? ex.Message}", ex);
} }
await _context.BulkInsertAsync(batchEducations);
await _context.BulkInsertAsync(batchOccupations);
await _context.BulkInsertAsync(batchAddresses);
await _context.BulkInsertAsync(batchPayments);
} }
} }

View file

@ -36,6 +36,7 @@ public class ImportJobInfo
public string ImportDocId { get; set; } = ""; public string ImportDocId { get; set; } = "";
public string? UserId { get; set; } public string? UserId { get; set; }
public string? FullName { get; set; } public string? FullName { get; set; }
public string? Token { get; set; }
// For CandidateFile // For CandidateFile
public PostRecruitImportRequest? Request { get; set; } public PostRecruitImportRequest? Request { get; set; }

View file

@ -0,0 +1,59 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Text;
namespace BMA.EHR.Recruit.Services;
public class NotificationService
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<NotificationService> _logger;
private readonly IConfiguration _configuration;
private string NotifyEndpoint = "https://hrmsbkk.case-collection.com/api/v1/org/through-socket/notify-from-token";
public NotificationService(IHttpClientFactory httpClientFactory, ILogger<NotificationService> logger, IConfiguration configuration)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
_configuration = configuration;
NotifyEndpoint = $"{_configuration["API"]}/org/through-socket/notify-from-token";
}
public async Task SendImportNotificationAsync(string? token, bool error, string message)
{
if (string.IsNullOrEmpty(token))
{
_logger.LogWarning("Cannot send import notification: token is null or empty.");
return;
}
try
{
var client = _httpClientFactory.CreateClient("default");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", ""));
var payload = new
{
error,
message
};
var json = JsonConvert.SerializeObject(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(NotifyEndpoint, content);
if (!response.IsSuccessStatusCode)
{
var responseBody = await response.Content.ReadAsStringAsync();
_logger.LogWarning("Import notification failed with status {StatusCode}: {Body}", response.StatusCode, responseBody);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to send import notification: {Message}", ex.Message);
}
}
}

View file

@ -18,7 +18,7 @@
"MongoConnection": "mongodb://admin:adminVM123@127.0.0.1:27017", "MongoConnection": "mongodb://admin:adminVM123@127.0.0.1:27017",
"DefaultConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;SslMode=None", "DefaultConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;SslMode=None",
"OrgConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;database=hrms_organization;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;SslMode=None", "OrgConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;database=hrms_organization;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;SslMode=None",
"RecruitConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;database=hrms_recruit;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;SslMode=None" "RecruitConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;database=hrms_recruit;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;SslMode=None;AllowLoadLocalInfile=true;"
}, },
"Jwt": { "Jwt": {
"Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc", "Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc",

View file

@ -18,7 +18,7 @@
"MongoConnection": "mongodb://admin:adminVM123@127.0.0.1:27017", "MongoConnection": "mongodb://admin:adminVM123@127.0.0.1:27017",
"DefaultConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;SslMode=None", "DefaultConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;SslMode=None",
"OrgConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;database=hrms_organization;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;SslMode=None", "OrgConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;database=hrms_organization;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;SslMode=None",
"RecruitConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;database=hrms_recruit;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;SslMode=None" "RecruitConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;database=hrms_recruit;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;SslMode=None;AllowLoadLocalInfile=true;"
}, },
"Jwt": { "Jwt": {
"Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc", "Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc",