use super::models::*; use axum::{extract::{Multipart, State}, http::StatusCode, Json}; use std::{path::Path, sync::atomic::Ordering}; use tempfile::NamedTempFile; use tokio::fs; use tracing::info; pub async fn create_print( State(store): State, mut multipart: Multipart, ) -> Result, (StatusCode, String)> { info!("📥 Получен запрос на печать"); let mut settings_list: Vec> = Vec::new(); let mut files_data: Vec<(String, bytes::Bytes)> = Vec::new(); while let Some(field) = multipart.next_field().await.unwrap_or(None) { let field_name = field.name().unwrap_or("").to_string(); if field_name == "settings" { let parsed = match field.text().await { Ok(text) => serde_json::from_str::(&text).ok(), Err(_) => None, }; settings_list.push(parsed); } else if field_name == "files" { let file_name = field.file_name().unwrap_or("unknown").to_string(); if let Ok(data) = field.bytes().await { files_data.push((file_name, data)); } } } if files_data.is_empty() { return Err((StatusCode::BAD_REQUEST, "Нет файлов".into())); } let mut job_files: Vec = Vec::new(); for (i, (file_name, data)) in files_data.into_iter().enumerate() { let ext = Path::new(&file_name).extension().and_then(|e| e.to_str()).unwrap_or("").to_lowercase(); let temp = if ext.is_empty() { NamedTempFile::new() } else { NamedTempFile::with_suffix(&format!(".{}", ext)) }; if let Ok(temp) = temp { let path = temp.into_temp_path(); if fs::write(&*path, &data).await.is_ok() { let settings = settings_list.get(i).cloned().flatten(); job_files.push(JobFile { name: file_name, path, settings, status: FileStatus::Queued, error: None, }); } } } if job_files.is_empty() { return Err((StatusCode::BAD_REQUEST, "Не удалось сохранить файлы".into())); } // ВСЕГДА начинаем с WaitingStart. // 1. Если первый файл односторонний - он просто поедет печататься. // 2. Если первый файл двусторонний - лоток и так пуст, очистка не нужна. // Очистка (AwaitingClearOutput) будет запрошена ТОЛЬКО при переходе // от предыдущего файла к следующему двустороннему в advance.rs let initial_phase = JobPhase::WaitingStart; let job_id = format!("job-{}", JOB_COUNTER.fetch_add(1, Ordering::SeqCst)); let job = Job { id: job_id.clone(), files: job_files, current: Some(0), phase: initial_phase, cups_id: None, duplex_first_pass_done: false, }; let view = job.view(); store.lock().unwrap().insert(job_id.clone(), job); Ok(Json(view)) }