refactor(jobs): centralize status lifecycle
This commit is contained in:
@@ -28,6 +28,69 @@ public sealed class JobApplicationLifecycleController : ControllerBase
|
||||
stage.Category.ToString(),
|
||||
stage.Group.ToString())));
|
||||
|
||||
[HttpPatch("{id:int}/status")]
|
||||
public async Task<IActionResult> UpdateStatus(
|
||||
[FromRoute] int id,
|
||||
[FromBody] UpdateStatusRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var job = await _db.JobApplications.FirstOrDefaultAsync(item => item.Id == id, cancellationToken);
|
||||
if (job is null) return NotFound();
|
||||
if (string.IsNullOrWhiteSpace(request.Status)) return BadRequest("Status is required.");
|
||||
|
||||
var oldStatus = job.Status;
|
||||
var changedAt = DateTime.Now;
|
||||
job.Status = JobPipeline.Normalize(request.Status);
|
||||
JobLifecycleEvents.SyncAppliedDateWithHistory(_db, job, changedAt);
|
||||
JobLifecycleEvents.RecordStatusChange(_db, job, oldStatus, changedAt);
|
||||
await _db.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Suggests a pipeline status from the job's latest inbound message. Applying it remains a
|
||||
/// separate, user-confirmed status update.
|
||||
/// </summary>
|
||||
[HttpGet("{id:int}/status-suggestion")]
|
||||
public async Task<ActionResult<StatusSuggestionDto>> GetStatusSuggestion(
|
||||
[FromRoute] int id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var job = await _db.JobApplications.AsNoTracking()
|
||||
.FirstOrDefaultAsync(item => item.Id == id, cancellationToken);
|
||||
if (job is null) return NotFound();
|
||||
|
||||
var none = new StatusSuggestionDto(false, null, job.Status, null, null, null, null);
|
||||
var latestInbound = await _db.Correspondences
|
||||
.AsNoTracking()
|
||||
.Where(item => item.JobApplicationId == id
|
||||
&& item.Direction != "outbound"
|
||||
&& item.From != "Me")
|
||||
.OrderByDescending(item => item.Date)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (latestInbound is null) return Ok(none);
|
||||
|
||||
var suggestion = EmailStatusClassifier.Classify(latestInbound.Subject, latestInbound.Content);
|
||||
if (suggestion is null) return Ok(none);
|
||||
|
||||
var currentOrder = JobPipeline.OrderOf(job.Status);
|
||||
var suggestedOrder = JobPipeline.OrderOf(suggestion.SuggestedStatus);
|
||||
if (JobPipeline.Normalize(job.Status) == suggestion.SuggestedStatus || currentOrder >= suggestedOrder)
|
||||
{
|
||||
return Ok(none);
|
||||
}
|
||||
|
||||
return Ok(new StatusSuggestionDto(
|
||||
HasSuggestion: true,
|
||||
SuggestedStatus: suggestion.SuggestedStatus,
|
||||
CurrentStatus: job.Status,
|
||||
Signal: suggestion.Signal,
|
||||
Confidence: suggestion.Confidence,
|
||||
MessageDate: latestInbound.Date,
|
||||
MessageSubject: latestInbound.Subject));
|
||||
}
|
||||
|
||||
[HttpDelete("{id:int}")]
|
||||
public async Task<IActionResult> SoftDelete([FromRoute] int id, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user