use serde::{Deserialize, Serialize}; use chrono::{DateTime, Utc}; /// AI视频分类记录数据模型 /// 遵循 Tauri 开发规范的数据模型设计原则 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct VideoClassificationRecord { /// 分类记录唯一标识符 pub id: String, /// 素材片段ID pub segment_id: String, /// 素材ID pub material_id: String, /// 项目ID pub project_id: String, /// 分类结果 pub category: String, /// 置信度 (0.0 - 1.0) pub confidence: f64, /// 分类理由 pub reasoning: String, /// 关键特征 pub features: Vec, /// 商品匹配度 pub product_match: bool, /// 质量评分 (0.0 - 1.0) pub quality_score: f64, /// Gemini文件URI pub gemini_file_uri: Option, /// 原始AI响应 pub raw_response: Option, /// 分类状态 pub status: ClassificationStatus, /// 错误信息 pub error_message: Option, /// 创建时间 pub created_at: DateTime, /// 更新时间 pub updated_at: DateTime, } /// AI视频分类任务数据模型 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct VideoClassificationTask { /// 任务唯一标识符 pub id: String, /// 素材片段ID pub segment_id: String, /// 素材ID pub material_id: String, /// 项目ID pub project_id: String, /// 视频文件路径 pub video_file_path: String, /// 任务状态 pub status: TaskStatus, /// 任务优先级 pub priority: i32, /// 重试次数 pub retry_count: i32, /// 最大重试次数 pub max_retries: i32, /// Gemini文件URI(上传后获得) pub gemini_file_uri: Option, /// 使用的提示词 pub prompt_text: Option, /// 错误信息 pub error_message: Option, /// 开始处理时间 pub started_at: Option>, /// 完成时间 pub completed_at: Option>, /// 创建时间 pub created_at: DateTime, /// 更新时间 pub updated_at: DateTime, } /// 分类状态枚举 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum ClassificationStatus { /// 已分类 Classified, /// 分类失败 Failed, /// 需要人工审核 NeedsReview, } /// 任务状态枚举 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum TaskStatus { /// 等待处理 Pending, /// 上传中 Uploading, /// 分析中 Analyzing, /// 已完成 Completed, /// 失败 Failed, /// 已取消 Cancelled, } /// 批量分类请求 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BatchClassificationRequest { /// 素材ID pub material_id: String, /// 项目ID pub project_id: String, /// 是否覆盖已有分类 pub overwrite_existing: bool, /// 任务优先级 pub priority: Option, } /// 项目一键分类请求 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ProjectBatchClassificationRequest { /// 项目ID pub project_id: String, /// 是否覆盖已有分类 pub overwrite_existing: Option, /// 要处理的素材类型(可选,默认只处理视频) pub material_types: Option>, /// 任务优先级 pub priority: Option, } /// 项目一键分类响应 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ProjectBatchClassificationResponse { /// 项目中总素材数 pub total_materials: u32, /// 符合条件的素材数 pub eligible_materials: u32, /// 创建的任务数 pub created_tasks: u32, /// 创建的任务ID列表 pub task_ids: Vec, /// 跳过的素材ID列表(已有分类) pub skipped_materials: Vec, } /// 分类任务查询参数 #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ClassificationTaskQuery { /// 项目ID过滤 pub project_id: Option, /// 素材ID过滤 pub material_id: Option, /// 状态过滤 pub status: Option, /// 分页大小 pub limit: Option, /// 分页偏移 pub offset: Option, } /// 分类统计信息 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ClassificationStats { /// 总任务数 pub total_tasks: i32, /// 等待中任务数 pub pending_tasks: i32, /// 处理中任务数 pub processing_tasks: i32, /// 已完成任务数 pub completed_tasks: i32, /// 失败任务数 pub failed_tasks: i32, /// 总分类记录数 pub total_classifications: i32, /// 成功分类记录数 pub successful_classifications: i32, /// 失败分类记录数 pub failed_classifications: i32, /// 需要审核记录数 pub needs_review_classifications: i32, /// 平均置信度 pub average_confidence: f64, /// 平均质量评分 pub average_quality_score: f64, } /// Gemini API响应结构 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GeminiClassificationResponse { /// 分类结果 pub category: String, /// 置信度 pub confidence: f64, /// 分类理由 pub reasoning: String, /// 关键特征 pub features: Vec, /// 商品匹配度 pub product_match: bool, /// 质量评分 pub quality_score: f64, } impl VideoClassificationRecord { /// 创建新的分类记录 pub fn new( segment_id: String, material_id: String, project_id: String, gemini_response: GeminiClassificationResponse, gemini_file_uri: Option, raw_response: Option, ) -> Self { Self { id: uuid::Uuid::new_v4().to_string(), segment_id, material_id, project_id, category: gemini_response.category, confidence: gemini_response.confidence, reasoning: gemini_response.reasoning, features: gemini_response.features, product_match: gemini_response.product_match, quality_score: gemini_response.quality_score, gemini_file_uri, raw_response, status: ClassificationStatus::Classified, error_message: None, created_at: Utc::now(), updated_at: Utc::now(), } } /// 标记为失败 pub fn mark_as_failed(&mut self, error_message: String) { self.status = ClassificationStatus::Failed; self.error_message = Some(error_message); self.updated_at = Utc::now(); } /// 标记为需要审核 pub fn mark_as_needs_review(&mut self, reason: String) { self.status = ClassificationStatus::NeedsReview; self.error_message = Some(reason); self.updated_at = Utc::now(); } /// 是否需要人工审核(低置信度或低质量) pub fn needs_review(&self) -> bool { self.confidence < 0.7 || self.quality_score < 0.6 } } impl VideoClassificationTask { /// 创建新的分类任务 pub fn new( segment_id: String, material_id: String, project_id: String, video_file_path: String, priority: Option, ) -> Self { Self { id: uuid::Uuid::new_v4().to_string(), segment_id, material_id, project_id, video_file_path, status: TaskStatus::Pending, priority: priority.unwrap_or(0), retry_count: 0, max_retries: 3, gemini_file_uri: None, prompt_text: None, error_message: None, started_at: None, completed_at: None, created_at: Utc::now(), updated_at: Utc::now(), } } /// 开始处理任务 pub fn start_processing(&mut self) { self.status = TaskStatus::Uploading; self.started_at = Some(Utc::now()); self.updated_at = Utc::now(); } /// 设置为分析状态 pub fn set_analyzing(&mut self, gemini_file_uri: String, prompt_text: String) { self.status = TaskStatus::Analyzing; self.gemini_file_uri = Some(gemini_file_uri); self.prompt_text = Some(prompt_text); self.updated_at = Utc::now(); } /// 完成任务 pub fn complete(&mut self) { self.status = TaskStatus::Completed; self.completed_at = Some(Utc::now()); self.updated_at = Utc::now(); } /// 任务失败 pub fn fail(&mut self, error_message: String) { self.status = TaskStatus::Failed; self.error_message = Some(error_message); self.retry_count += 1; self.updated_at = Utc::now(); } /// 是否可以重试 pub fn can_retry(&self) -> bool { self.retry_count < self.max_retries && self.status == TaskStatus::Failed } /// 重置为等待状态(用于重试) pub fn reset_for_retry(&mut self) { self.status = TaskStatus::Pending; self.error_message = None; self.started_at = None; self.completed_at = None; self.updated_at = Utc::now(); } } impl Default for ClassificationStatus { fn default() -> Self { ClassificationStatus::Classified } } impl Default for TaskStatus { fn default() -> Self { TaskStatus::Pending } }