added pause and resume alarm diagnosis review campaign service
This commit is contained in:
parent
e72f16f26b
commit
062fd5141f
|
|
@ -1275,7 +1275,21 @@ public class Controller : ControllerBase
|
||||||
public ActionResult StopAlarmReviewCampaign()
|
public ActionResult StopAlarmReviewCampaign()
|
||||||
{
|
{
|
||||||
AlarmReviewService.StopCampaign();
|
AlarmReviewService.StopCampaign();
|
||||||
return Ok(new { message = "Campaign stopped and progress file deleted. Safe to redeploy." });
|
return Ok(new { message = "Campaign paused — progress preserved. Use ResumeAlarmReviewCampaign to restart timers." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost(nameof(ResumeAlarmReviewCampaign))]
|
||||||
|
public ActionResult ResumeAlarmReviewCampaign()
|
||||||
|
{
|
||||||
|
AlarmReviewService.ResumeCampaign();
|
||||||
|
return Ok(new { message = "Campaign resumed — timers restarted from existing progress." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost(nameof(ResetAlarmReviewCampaign))]
|
||||||
|
public ActionResult ResetAlarmReviewCampaign()
|
||||||
|
{
|
||||||
|
AlarmReviewService.ResetCampaign();
|
||||||
|
return Ok(new { message = "Campaign fully reset — all progress deleted. Use StartAlarmReviewCampaign to begin again." });
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet(nameof(CorrectAlarm))]
|
[HttpGet(nameof(CorrectAlarm))]
|
||||||
|
|
|
||||||
|
|
@ -206,8 +206,33 @@ public static class AlarmReviewService
|
||||||
Console.WriteLine("[AlarmReviewService] Daily scheduler started (8AM + 2PM jobs).");
|
Console.WriteLine("[AlarmReviewService] Daily scheduler started (8AM + 2PM jobs).");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Stops the scheduler and deletes the progress file. Safe to call at any time.</summary>
|
/// <summary>Pauses the scheduler (timers stopped) but keeps all progress. Resume with ResumeCampaign().</summary>
|
||||||
public static void StopCampaign()
|
public static void StopCampaign()
|
||||||
|
{
|
||||||
|
_morningTimer?.Dispose();
|
||||||
|
_afternoonTimer?.Dispose();
|
||||||
|
_morningTimer = null;
|
||||||
|
_afternoonTimer = null;
|
||||||
|
_testBatch = null;
|
||||||
|
Console.WriteLine("[AlarmReviewService] Campaign paused — progress preserved. Call ResumeCampaign() to restart timers.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Resumes timers without touching progress. Use after StopCampaign() or server restart.</summary>
|
||||||
|
public static void ResumeCampaign()
|
||||||
|
{
|
||||||
|
if (!File.Exists(ProgressFile))
|
||||||
|
{
|
||||||
|
Console.WriteLine("[AlarmReviewService] No progress file found — use StartCampaign() to begin.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LoadGermanNames();
|
||||||
|
ScheduleTimer(ref _morningTimer, 8, 0, () => RunMorningJobAsync() .GetAwaiter().GetResult());
|
||||||
|
ScheduleTimer(ref _afternoonTimer, 14, 0, () => RunAfternoonJobAsync().GetAwaiter().GetResult());
|
||||||
|
Console.WriteLine("[AlarmReviewService] Campaign resumed — timers restarted.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deletes all progress and stops timers. Only use to start over from scratch.</summary>
|
||||||
|
public static void ResetCampaign()
|
||||||
{
|
{
|
||||||
_morningTimer?.Dispose();
|
_morningTimer?.Dispose();
|
||||||
_afternoonTimer?.Dispose();
|
_afternoonTimer?.Dispose();
|
||||||
|
|
@ -217,8 +242,10 @@ public static class AlarmReviewService
|
||||||
|
|
||||||
if (File.Exists(ProgressFile))
|
if (File.Exists(ProgressFile))
|
||||||
File.Delete(ProgressFile);
|
File.Delete(ProgressFile);
|
||||||
|
if (File.Exists(CheckedFilePath))
|
||||||
|
File.Delete(CheckedFilePath);
|
||||||
|
|
||||||
Console.WriteLine("[AlarmReviewService] Campaign stopped and progress file deleted.");
|
Console.WriteLine("[AlarmReviewService] Campaign fully reset — all progress deleted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ScheduleTimer(ref Timer? timer, int hour, int minute, Action action)
|
private static void ScheduleTimer(ref Timer? timer, int hour, int minute, Action action)
|
||||||
|
|
@ -265,6 +292,15 @@ public static class AlarmReviewService
|
||||||
{
|
{
|
||||||
var submissionCount = current.Submissions.Values.Count(s => s != null);
|
var submissionCount = current.Submissions.Values.Count(s => s != null);
|
||||||
|
|
||||||
|
// If the batch was sent today, don't treat 0 submissions as a stall —
|
||||||
|
// reviewers haven't had a full day yet. Skip and wait for tomorrow's job.
|
||||||
|
var sentToday = current.SentDate == DateTime.Now.ToString("yyyy-MM-dd");
|
||||||
|
if (submissionCount == 0 && sentToday)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[AlarmReviewService] Batch {current.BatchNumber}: sent today, 0 submissions so far — waiting for tomorrow.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (submissionCount == 0)
|
if (submissionCount == 0)
|
||||||
{
|
{
|
||||||
const int MaxResends = 3;
|
const int MaxResends = 3;
|
||||||
|
|
@ -1457,6 +1493,46 @@ render();
|
||||||
var html = BuildReviewerEmailHtml(name, reviewUrl, batch.BatchNumber, batch.AlarmKeys.Count, quote, isResend);
|
var html = BuildReviewerEmailHtml(name, reviewUrl, batch.BatchNumber, batch.AlarmKeys.Count, quote, isResend);
|
||||||
await SendEmailAsync(email, subject, html);
|
await SendEmailAsync(email, subject, html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify admin with a preview of what reviewers received
|
||||||
|
await SendAdminBatchDispatchNoticeAsync(batch, isResend);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task SendAdminBatchDispatchNoticeAsync(BatchRecord batch, bool isResend)
|
||||||
|
{
|
||||||
|
var totalBatches = (int)Math.Ceiling((double)AllAlarmKeys.Length / BatchSize);
|
||||||
|
var action = isResend ? "erneut gesendet" : "gesendet";
|
||||||
|
var subject = $"[Admin] Stapel {batch.BatchNumber}/{totalBatches} {action} an {Reviewers.Length} Prüfer";
|
||||||
|
|
||||||
|
var previewUrl = $"{BaseUrl}/ReviewAlarms?batch={batch.BatchNumber}&reviewer={Uri.EscapeDataString(Reviewers[0].Name)}";
|
||||||
|
var reviewerList = string.Join(", ", Reviewers.Select(r => r.Name));
|
||||||
|
|
||||||
|
var alarmRows = string.Join("\n", batch.AlarmKeys.Select((key, i) =>
|
||||||
|
$"""<tr><td style="padding:4px 12px;border-bottom:1px solid #f5f5f5;font-size:12px;color:#555">{i + 1}. {SplitCamelCase(key)} <span style="color:#aaa;font-size:11px">({(SinexcelKeySet.Contains(key) ? "Sinexcel" : "Growatt")})</span></td></tr>"""));
|
||||||
|
|
||||||
|
var html = $"""
|
||||||
|
<!DOCTYPE html><html><head><meta charset="utf-8"></head>
|
||||||
|
<body style="font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#333;padding:24px;max-width:650px">
|
||||||
|
<h2 style="color:#2c3e50;margin-bottom:4px">Stapel {batch.BatchNumber} {action}</h2>
|
||||||
|
<p style="color:#888;font-size:12px;margin-bottom:20px">{DateTime.Now:yyyy-MM-dd HH:mm} · {batch.AlarmKeys.Count} Alarme · Deadline: 8:00 Uhr morgen früh</p>
|
||||||
|
|
||||||
|
<p style="margin-bottom:16px">Gesendet an: <strong>{reviewerList}</strong></p>
|
||||||
|
|
||||||
|
<p style="margin-bottom:20px">
|
||||||
|
<a href="{previewUrl}" style="background:#3498db;color:#fff;text-decoration:none;padding:9px 22px;border-radius:6px;font-size:13px;display:inline-block">Formular ansehen →</a>
|
||||||
|
<span style="font-size:12px;color:#888;margin-left:10px">(alle Prüfer sehen denselben Inhalt)</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 style="color:#2c3e50;margin-bottom:8px">Alarme in diesem Stapel</h3>
|
||||||
|
<table style="border-collapse:collapse;width:100%;margin-bottom:24px">
|
||||||
|
{alarmRows}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p style="font-size:11px;color:#bbb">Diese E-Mail dient nur zur Information — keine Aktion erforderlich.</p>
|
||||||
|
</body></html>
|
||||||
|
""";
|
||||||
|
|
||||||
|
await SendEmailAsync(AdminEmail, subject, html);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task SendReminderEmailAsync(BatchRecord batch, string name, string email)
|
private static async Task SendReminderEmailAsync(BatchRecord batch, string name, string email)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue