add send noti and bulk insert (not complete)
This commit is contained in:
parent
0f1ec072ad
commit
5a3fadc574
7 changed files with 232 additions and 160 deletions
|
|
@ -755,6 +755,7 @@ namespace BMA.EHR.Recruit.Controllers
|
|||
ImportDocId = import_doc_id,
|
||||
UserId = UserId,
|
||||
FullName = FullName,
|
||||
Token = token,
|
||||
Request = req,
|
||||
});
|
||||
await _importJobQueue.EnqueueAsync(job);
|
||||
|
|
@ -943,6 +944,7 @@ namespace BMA.EHR.Recruit.Controllers
|
|||
ImportDocId = import_doc_id,
|
||||
UserId = UserId,
|
||||
FullName = FullName,
|
||||
Token = token,
|
||||
});
|
||||
await _importJobQueue.EnqueueAsync(job);
|
||||
|
||||
|
|
@ -1013,6 +1015,7 @@ namespace BMA.EHR.Recruit.Controllers
|
|||
ImportDocId = import_doc_id,
|
||||
UserId = UserId,
|
||||
FullName = FullName,
|
||||
Token = token,
|
||||
});
|
||||
await _importJobQueue.EnqueueAsync(job);
|
||||
|
||||
|
|
@ -1082,6 +1085,7 @@ namespace BMA.EHR.Recruit.Controllers
|
|||
RecruitImportId = id,
|
||||
UserId = UserId,
|
||||
FullName = FullName,
|
||||
Token = token,
|
||||
});
|
||||
await _importJobQueue.EnqueueAsync(job);
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ builder.Services.AddAuthorization();
|
|||
// Register Services
|
||||
builder.Services.AddTransient<RecruitService>();
|
||||
builder.Services.AddTransient<MinIOService>();
|
||||
builder.Services.AddTransient<NotificationService>();
|
||||
builder.Services.AddTransient<PermissionRepository>();
|
||||
builder.Services.AddSingleton<ImportJobQueue>();
|
||||
builder.Services.AddSingleton<ImportJobTracker>();
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ public class ImportBackgroundService : BackgroundService
|
|||
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
var minioService = scope.ServiceProvider.GetRequiredService<MinIOService>();
|
||||
var recruitService = scope.ServiceProvider.GetRequiredService<RecruitService>();
|
||||
var notificationService = scope.ServiceProvider.GetRequiredService<NotificationService>();
|
||||
var webHostEnv = scope.ServiceProvider.GetRequiredService<IWebHostEnvironment>();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<ImportBackgroundService>>();
|
||||
|
||||
|
|
@ -71,11 +72,15 @@ public class ImportBackgroundService : BackgroundService
|
|||
}
|
||||
|
||||
_tracker.UpdateStatus(job.JobId, ImportJobStatus.Completed, job.TotalCount);
|
||||
|
||||
await notificationService.SendImportNotificationAsync(job.Token, false, "ระบบนำเข้าข้อมูลสำเร็จ");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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
|
||||
if (!string.IsNullOrEmpty(job.ImportDocId))
|
||||
|
|
@ -321,6 +326,7 @@ public class ImportBackgroundService : BackgroundService
|
|||
_context.ChangeTracker.Clear();
|
||||
|
||||
var importId = imported.Id;
|
||||
var importRef = _context.Attach(new RecruitImport { Id = importId }).Entity;
|
||||
|
||||
using var c_package = new ExcelPackage(new FileInfo(job.ImportFile));
|
||||
for (int i = 0; i < c_package.Workbook.Worksheets.Count; i++)
|
||||
|
|
@ -344,7 +350,10 @@ public class ImportBackgroundService : BackgroundService
|
|||
var cell1 = workSheet?.Cells[row, 1]?.GetValue<string>();
|
||||
if (cell1 == "" || cell1 == null) break;
|
||||
|
||||
try
|
||||
{
|
||||
var r = new Models.Recruits.Recruit();
|
||||
r.Id = Guid.NewGuid();
|
||||
r.ExamId = workSheet?.Cells[row, 1]?.GetValue<string>() ?? "";
|
||||
r.PositionName = workSheet?.Cells[row, 3]?.GetValue<string>() ?? "";
|
||||
r.HddPosition = workSheet?.Cells[row, 4]?.GetValue<string>() ?? "";
|
||||
|
|
@ -371,10 +380,12 @@ public class ImportBackgroundService : BackgroundService
|
|||
r.LastUpdatedAt = DateTime.Now;
|
||||
r.LastUpdateUserId = job.UserId ?? "";
|
||||
r.LastUpdateFullName = job.FullName ?? "System Administrator";
|
||||
r.RecruitImport = importRef;
|
||||
|
||||
// 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 = "",
|
||||
|
|
@ -384,6 +395,7 @@ public class ImportBackgroundService : BackgroundService
|
|||
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",
|
||||
|
|
@ -394,11 +406,13 @@ public class ImportBackgroundService : BackgroundService
|
|||
|
||||
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",
|
||||
|
|
@ -409,6 +423,7 @@ public class ImportBackgroundService : BackgroundService
|
|||
|
||||
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>() ?? "",
|
||||
|
|
@ -427,6 +442,7 @@ public class ImportBackgroundService : BackgroundService
|
|||
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",
|
||||
|
|
@ -437,6 +453,7 @@ public class ImportBackgroundService : BackgroundService
|
|||
|
||||
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>() ?? "",
|
||||
|
|
@ -453,6 +470,7 @@ public class ImportBackgroundService : BackgroundService
|
|||
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",
|
||||
|
|
@ -461,16 +479,16 @@ public class ImportBackgroundService : BackgroundService
|
|||
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);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Row {row}: {ex.Message}", ex);
|
||||
}
|
||||
|
||||
row++;
|
||||
batchCount++;
|
||||
|
|
@ -478,26 +496,19 @@ public class ImportBackgroundService : BackgroundService
|
|||
|
||||
if (batchCount >= batchSize)
|
||||
{
|
||||
// BulkInsert Recruits first (with SetOutputIdentity to get generated Ids)
|
||||
await _context.BulkInsertAsync(batchRecruits, options =>
|
||||
try
|
||||
{
|
||||
options.SetOutputIdentity = true;
|
||||
});
|
||||
|
||||
// Assign generated Recruit Id to child entities
|
||||
for (int j = 0; j < batchRecruits.Count; j++)
|
||||
{
|
||||
batchEducations[j].Recruit = batchRecruits[j];
|
||||
batchOccupations[j].Recruit = batchRecruits[j];
|
||||
batchAddresses[j].Recruit = batchRecruits[j];
|
||||
batchPayments[j].Recruit = batchRecruits[j];
|
||||
}
|
||||
|
||||
// BulkInsert child entities (no identity output needed)
|
||||
await _context.BulkInsertAsync(batchRecruits);
|
||||
await _context.BulkInsertAsync(batchEducations);
|
||||
await _context.BulkInsertAsync(batchOccupations);
|
||||
await _context.BulkInsertAsync(batchAddresses);
|
||||
await _context.BulkInsertAsync(batchPayments);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var batchStartRow = row - batchCount + 1;
|
||||
throw new Exception($"BulkInsert failed (rows {batchStartRow}-{row - 1}, {batchRecruits.Count} records): {ex.InnerException?.Message ?? ex.Message}", ex);
|
||||
}
|
||||
|
||||
// Clear all lists for next batch
|
||||
batchRecruits.Clear();
|
||||
|
|
@ -513,24 +524,20 @@ public class ImportBackgroundService : BackgroundService
|
|||
// Process remaining records
|
||||
if (batchRecruits.Count > 0)
|
||||
{
|
||||
await _context.BulkInsertAsync(batchRecruits, options =>
|
||||
try
|
||||
{
|
||||
options.SetOutputIdentity = true;
|
||||
});
|
||||
|
||||
for (int j = 0; j < batchRecruits.Count; j++)
|
||||
{
|
||||
batchEducations[j].Recruit = batchRecruits[j];
|
||||
batchOccupations[j].Recruit = batchRecruits[j];
|
||||
batchAddresses[j].Recruit = batchRecruits[j];
|
||||
batchPayments[j].Recruit = batchRecruits[j];
|
||||
}
|
||||
|
||||
await _context.BulkInsertAsync(batchRecruits);
|
||||
await _context.BulkInsertAsync(batchEducations);
|
||||
await _context.BulkInsertAsync(batchOccupations);
|
||||
await _context.BulkInsertAsync(batchAddresses);
|
||||
await _context.BulkInsertAsync(batchPayments);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var batchStartRow = row - batchCount + 1;
|
||||
throw new Exception($"BulkInsert failed (rows {batchStartRow}-{row - 1}, {batchRecruits.Count} records): {ex.InnerException?.Message ?? ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
job.TotalCount = _tracker.GetJob(job.JobId)?.ProcessedCount ?? 0;
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ public class ImportJobInfo
|
|||
public string ImportDocId { get; set; } = "";
|
||||
public string? UserId { get; set; }
|
||||
public string? FullName { get; set; }
|
||||
public string? Token { get; set; }
|
||||
|
||||
// For CandidateFile
|
||||
public PostRecruitImportRequest? Request { get; set; }
|
||||
|
|
|
|||
59
Services/NotificationService.cs
Normal file
59
Services/NotificationService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
"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",
|
||||
"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": {
|
||||
"Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
"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",
|
||||
"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": {
|
||||
"Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue