feat(web): add manual trigger action support for Hangfire recurring jobs
Validators (Pushes and Pull Requests) / validate-ui-and-storage (push) Successful in 22s
Validators (Pushes and Pull Requests) / validate-core (push) Has been cancelled
Prepare Release / Build & Create Release (push) Successful in 1m9s
Prepare Release / Release Notification (push) Successful in 2s

This commit is contained in:
2026-07-12 13:36:52 +09:00
parent 27a56e65b7
commit 475950be36
2 changed files with 44 additions and 2 deletions
@@ -16,6 +16,18 @@
</div>
<div class="page-body">
@if (TempData["SuccessMessage"] != null)
{
<div class="alert alert-success" role="alert" style="background-color: rgba(76, 175, 80, 0.15); border: 1px solid rgba(76, 175, 80, 0.3); color: #81c784; padding: 12px; border-radius: 6px; margin-bottom: 16px;">
@TempData["SuccessMessage"]
</div>
}
@if (TempData["ErrorMessage"] != null)
{
<div class="alert alert-danger" role="alert" style="background-color: rgba(244, 67, 54, 0.15); border: 1px solid rgba(244, 67, 54, 0.3); color: #ff7675; padding: 12px; border-radius: 6px; margin-bottom: 16px;">
@TempData["ErrorMessage"]
</div>
}
<div class="row row-deck row-cards">
<!-- 예약된 작업 -->
<div class="col-12">
@@ -56,7 +68,12 @@
}
</td>
<td>
<a href="javascript:void(0)" class="btn btn-sm btn-link">수정</a>
<form method="post" asp-page-handler="TriggerJob" class="d-inline">
@Html.AntiForgeryToken()
<input type="hidden" name="jobId" value="@job.JobId" />
<button type="submit" class="btn btn-sm btn-success text-white">즉시 실행</button>
</form>
<a href="javascript:void(0)" class="btn btn-sm btn-link ms-2">수정</a>
</td>
</tr>
}
@@ -1,6 +1,7 @@
using Hangfire;
using Hangfire.Storage;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using QuantEngine.Web.Services;
@@ -32,6 +33,29 @@ public class IndexModel : PageModel
return Task.CompletedTask;
}
public async Task<IActionResult> OnPostTriggerJobAsync(string jobId)
{
if (string.IsNullOrEmpty(jobId))
{
TempData["ErrorMessage"] = "올바르지 않은 작업 ID입니다.";
return RedirectToPage();
}
try
{
RecurringJob.TriggerJob(jobId);
TempData["SuccessMessage"] = $"작업 '{DescribeJobId(jobId)}'이(가) 즉시 실행 큐에 등록되었습니다.";
_logger.LogInformation("Manually triggered Hangfire recurring job: {JobId}", jobId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to trigger Hangfire job: {JobId}", jobId);
TempData["ErrorMessage"] = $"작업 실행 실패: {ex.Message}";
}
return RedirectToPage();
}
private void LoadOperationsData()
{
try
@@ -51,6 +75,7 @@ public class IndexModel : PageModel
var recurringJobs = connection.GetRecurringJobs();
ScheduledJobs = recurringJobs
.Select(j => new ScheduledJobInfo(
j.Id,
DescribeJobId(j.Id),
DescribeCron(j.Cron),
j.NextExecution,
@@ -127,5 +152,5 @@ public class IndexModel : PageModel
};
}
public record ScheduledJobInfo(string JobName, string Schedule, DateTime? NextRun, bool IsEnabled);
public record ScheduledJobInfo(string JobId, string JobName, string Schedule, DateTime? NextRun, bool IsEnabled);
public record JobExecutionInfo(string JobName, DateTime StartedAt, DateTime? CompletedAt, bool IsSuccess);