Skip to main content

koprogo_api/infrastructure/jobs/
sla_escalation_job.rs

1//! SLA escalation cron job stub (Story 3.7 — FR32 INV-23).
2//!
3//! Phase A scope: expose a single `run_once()` entrypoint that performs one
4//! pass of SLA escalation by delegating to
5//! [`SyndicResponseUseCases::escalate_overdue`]. The real scheduler (tokio
6//! interval task) lands in Phase B / a follow-up; tests call `run_once`
7//! manually for now.
8
9use crate::application::error::AppError;
10use crate::application::ports::{SyndicResponseRepository, TicketRepository};
11use crate::application::use_cases::SyndicResponseUseCases;
12use chrono::Utc;
13use std::sync::Arc;
14
15pub struct SlaEscalationJob<R, T>
16where
17    R: SyndicResponseRepository,
18    T: TicketRepository,
19{
20    use_cases: Arc<SyndicResponseUseCases<R, T>>,
21}
22
23impl<R, T> SlaEscalationJob<R, T>
24where
25    R: SyndicResponseRepository,
26    T: TicketRepository,
27{
28    pub fn new(use_cases: Arc<SyndicResponseUseCases<R, T>>) -> Self {
29        Self { use_cases }
30    }
31
32    /// One escalation pass. Returns the number of tickets whose SLA was
33    /// just consumed by this pass. Subsequent calls at roughly the same
34    /// time are no-ops (idempotency at the use-case layer).
35    pub async fn run_once(&self) -> Result<usize, AppError> {
36        let now = Utc::now();
37        let escalated = self.use_cases.escalate_overdue(now).await?;
38        Ok(escalated.len())
39    }
40}