meti-backend/internal/manager/job_manager.go
2025-08-15 22:42:58 +07:00

117 lines
2.9 KiB
Go

package manager
import (
"context"
"sync"
"time"
"eslogad-be/internal/contract"
"github.com/google/uuid"
)
type JobStatus string
const (
JobStatusPending JobStatus = "pending"
JobStatusProcessing JobStatus = "processing"
JobStatusCompleted JobStatus = "completed"
JobStatusFailed JobStatus = "failed"
)
type BulkJobResult struct {
JobID uuid.UUID `json:"job_id"`
Status JobStatus `json:"status"`
Message string `json:"message"`
StartedAt time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at,omitempty"`
Summary contract.BulkCreationSummary `json:"summary"`
Created []contract.UserResponse `json:"created"`
Failed []contract.BulkUserErrorResult `json:"failed"`
}
type JobManager struct {
jobs sync.Map
}
var jobManagerInstance *JobManager
var once sync.Once
func GetJobManager() *JobManager {
once.Do(func() {
jobManagerInstance = &JobManager{}
})
return jobManagerInstance
}
func (jm *JobManager) CreateJob() uuid.UUID {
jobID := uuid.New()
job := &BulkJobResult{
JobID: jobID,
Status: JobStatusPending,
Message: "Job created, waiting to start",
StartedAt: time.Now(),
Summary: contract.BulkCreationSummary{
Total: 0,
Succeeded: 0,
Failed: 0,
},
Created: []contract.UserResponse{},
Failed: []contract.BulkUserErrorResult{},
}
jm.jobs.Store(jobID, job)
return jobID
}
func (jm *JobManager) UpdateJob(jobID uuid.UUID, status JobStatus, message string) {
if val, ok := jm.jobs.Load(jobID); ok {
job := val.(*BulkJobResult)
job.Status = status
job.Message = message
if status == JobStatusCompleted || status == JobStatusFailed {
now := time.Now()
job.FinishedAt = &now
}
jm.jobs.Store(jobID, job)
}
}
func (jm *JobManager) UpdateJobResults(jobID uuid.UUID, created []contract.UserResponse, failed []contract.BulkUserErrorResult, summary contract.BulkCreationSummary) {
if val, ok := jm.jobs.Load(jobID); ok {
job := val.(*BulkJobResult)
job.Created = append(job.Created, created...)
job.Failed = append(job.Failed, failed...)
job.Summary.Total = summary.Total
job.Summary.Succeeded += summary.Succeeded
job.Summary.Failed += summary.Failed
jm.jobs.Store(jobID, job)
}
}
func (jm *JobManager) GetJob(jobID uuid.UUID) (*BulkJobResult, bool) {
if val, ok := jm.jobs.Load(jobID); ok {
return val.(*BulkJobResult), true
}
return nil, false
}
func (jm *JobManager) CleanupOldJobs(ctx context.Context, maxAge time.Duration) {
ticker := time.NewTicker(1 * time.Hour)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
cutoff := time.Now().Add(-maxAge)
jm.jobs.Range(func(key, value interface{}) bool {
job := value.(*BulkJobResult)
if job.FinishedAt != nil && job.FinishedAt.Before(cutoff) {
jm.jobs.Delete(key)
}
return true
})
}
}
}