diff --git a/apps/desktop/AI_CLASSIFICATION_STATS_FIX.md b/apps/desktop/AI_CLASSIFICATION_STATS_FIX.md new file mode 100644 index 0000000..15c38bb --- /dev/null +++ b/apps/desktop/AI_CLASSIFICATION_STATS_FIX.md @@ -0,0 +1,135 @@ +# AI分类统计数值溢出问题修复 + +## 问题描述 + +用户报告AI分析日志显示异常数据: +- 总记录数:214(正常) +- 成功分类:18446744073709552(异常,接近u64最大值) + +## 问题分析 + +### 根本原因 +在 `ai_analysis_log_service.rs` 中,计算成功分类数的逻辑有误: + +```rust +// 错误的计算方式 +successful_classifications: (classification_stats.total_classifications - classification_stats.failed_tasks) as u64, +``` + +### 问题详解 +1. `total_classifications` 是分类记录总数(来自 `video_classification_records` 表) +2. `failed_tasks` 是失败的任务数(来自 `video_classification_tasks` 表) +3. 这两个数据来源不同,不应该直接相减 +4. 当 `failed_tasks` > `total_classifications` 时,减法结果为负数 +5. 负数转换为 `u64` 时会发生溢出,变成接近 `u64::MAX` 的大数 + +### 数值分析 +- `u64::MAX` = 18446744073709551615 +- 异常值 = 18446744073709552 +- 差值 = 18446744073709551615 - 18446744073709552 = 63 + +这表明原始计算结果是 -63,转换为 u64 后变成了这个大数。 + +## 修复方案 + +### 1. 扩展 ClassificationStats 结构体 +添加分类记录状态的详细统计: + +```rust +pub struct ClassificationStats { + // 原有字段... + pub successful_classifications: i32, // 新增:成功分类记录数 + pub failed_classifications: i32, // 新增:失败分类记录数 + pub needs_review_classifications: i32, // 新增:需要审核记录数 +} +``` + +### 2. 改进数据库查询 +在 `video_classification_repository.rs` 中添加分类记录状态统计: + +```rust +// 获取分类记录状态统计 +let mut stmt = conn.prepare(&format!( + "SELECT + SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) as classified, + SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) as failed, + SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) as needs_review + FROM video_classification_records{}", record_where_clause +))?; +``` + +### 3. 修正统计计算逻辑 +使用正确的数据源计算各项统计: + +```rust +Ok(AiAnalysisLogStats { + total_records: classification_stats.total_classifications.max(0) as u64, + successful_classifications: classification_stats.successful_classifications.max(0) as u64, + failed_classifications: classification_stats.failed_classifications.max(0) as u64, + needs_review: classification_stats.needs_review_classifications.max(0) as u64, + // ... +}) +``` + +## 修改文件列表 + +### 后端文件 +1. `apps/desktop/src-tauri/src/data/models/video_classification.rs` + - 扩展 `ClassificationStats` 结构体 + +2. `apps/desktop/src-tauri/src/data/repositories/video_classification_repository.rs` + - 添加分类记录状态统计查询 + - 更新返回的统计数据 + +3. `apps/desktop/src-tauri/src/business/services/ai_analysis_log_service.rs` + - 修正成功分类数的计算逻辑 + - 添加数值安全检查 + +### 前端文件 +1. `apps/desktop/src/types/videoClassification.ts` + - 更新 `ClassificationStats` 接口 + +2. `apps/desktop/src/store/videoClassificationStore.ts` + - 更新 `ClassificationStats` 接口 + +## 安全改进 + +### 1. 数值溢出防护 +使用 `.max(0)` 确保不会有负数转换为无符号整数: + +```rust +classification_stats.successful_classifications.max(0) as u64 +``` + +### 2. 数据一致性 +确保统计数据来源一致: +- 任务相关统计来自 `video_classification_tasks` 表 +- 分类记录相关统计来自 `video_classification_records` 表 + +### 3. 类型安全 +明确区分有符号和无符号整数的使用场景。 + +## 测试建议 + +1. **边界测试**:测试没有分类记录的项目 +2. **数据一致性测试**:验证各项统计数据的逻辑关系 +3. **大数据测试**:测试大量分类记录的性能和准确性 +4. **异常数据测试**:测试数据库中存在异常状态的记录 + +## 预期结果 + +修复后,AI分析统计应该显示: +- 总记录数:214 +- 成功分类:实际成功分类的记录数(≤ 214) +- 失败分类:实际失败分类的记录数 +- 需要审核:需要人工审核的记录数 + +所有数值应该在合理范围内,且满足逻辑关系: +`成功分类 + 失败分类 + 需要审核 = 总记录数` + +## 后续优化 + +1. 添加数据一致性检查 +2. 实现24小时活动统计 +3. 添加更详细的错误日志 +4. 考虑添加数据库索引优化查询性能 diff --git a/apps/desktop/src-tauri/src/business/services/ai_analysis_log_service.rs b/apps/desktop/src-tauri/src/business/services/ai_analysis_log_service.rs index 8e63c96..27bc645 100644 --- a/apps/desktop/src-tauri/src/business/services/ai_analysis_log_service.rs +++ b/apps/desktop/src-tauri/src/business/services/ai_analysis_log_service.rs @@ -265,14 +265,14 @@ impl AiAnalysisLogService { let recent_24h_activity = 0; // TODO: 实现24小时内的活动统计 Ok(AiAnalysisLogStats { - total_records: classification_stats.total_classifications as u64, - successful_classifications: (classification_stats.total_classifications - classification_stats.failed_tasks) as u64, - failed_classifications: classification_stats.failed_tasks as u64, - needs_review: 0, // TODO: 从数据库查询需要审核的数量 - total_tasks: classification_stats.total_tasks as u64, - completed_tasks: classification_stats.completed_tasks as u64, - failed_tasks: classification_stats.failed_tasks as u64, - processing_tasks: classification_stats.processing_tasks as u64, + total_records: classification_stats.total_classifications.max(0) as u64, + successful_classifications: classification_stats.successful_classifications.max(0) as u64, + failed_classifications: classification_stats.failed_classifications.max(0) as u64, + needs_review: classification_stats.needs_review_classifications.max(0) as u64, + total_tasks: classification_stats.total_tasks.max(0) as u64, + completed_tasks: classification_stats.completed_tasks.max(0) as u64, + failed_tasks: classification_stats.failed_tasks.max(0) as u64, + processing_tasks: classification_stats.processing_tasks.max(0) as u64, average_confidence: classification_stats.average_confidence, average_quality_score: classification_stats.average_quality_score, recent_24h_activity, diff --git a/apps/desktop/src-tauri/src/data/models/video_classification.rs b/apps/desktop/src-tauri/src/data/models/video_classification.rs index 0e2cbdd..9aef991 100644 --- a/apps/desktop/src-tauri/src/data/models/video_classification.rs +++ b/apps/desktop/src-tauri/src/data/models/video_classification.rs @@ -175,6 +175,12 @@ pub struct ClassificationStats { 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, /// 平均质量评分 diff --git a/apps/desktop/src-tauri/src/data/repositories/video_classification_repository.rs b/apps/desktop/src-tauri/src/data/repositories/video_classification_repository.rs index ec9b392..a5a1fcb 100644 --- a/apps/desktop/src-tauri/src/data/repositories/video_classification_repository.rs +++ b/apps/desktop/src-tauri/src/data/repositories/video_classification_repository.rs @@ -536,6 +536,27 @@ impl VideoClassificationRepository { )) })?; + // 获取分类记录状态统计 + let mut stmt = conn.prepare(&format!( + "SELECT + SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) as classified, + SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) as failed, + SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) as needs_review + FROM video_classification_records{}", record_where_clause + ))?; + + let classified_json = serde_json::to_string(&ClassificationStatus::Classified).unwrap(); + let failed_json = serde_json::to_string(&ClassificationStatus::Failed).unwrap(); + let needs_review_json = serde_json::to_string(&ClassificationStatus::NeedsReview).unwrap(); + + let record_status_stats = stmt.query_row([&classified_json, &failed_json, &needs_review_json], |row| { + Ok(( + row.get::<_, Option>(0)?.unwrap_or(0), + row.get::<_, Option>(1)?.unwrap_or(0), + row.get::<_, Option>(2)?.unwrap_or(0), + )) + })?; + Ok(ClassificationStats { total_tasks: task_stats.0, pending_tasks: task_stats.1, @@ -543,6 +564,9 @@ impl VideoClassificationRepository { completed_tasks: task_stats.3, failed_tasks: task_stats.4, total_classifications: record_stats.0, + successful_classifications: record_status_stats.0, + failed_classifications: record_status_stats.1, + needs_review_classifications: record_status_stats.2, average_confidence: record_stats.1, average_quality_score: record_stats.2, }) diff --git a/apps/desktop/src/store/videoClassificationStore.ts b/apps/desktop/src/store/videoClassificationStore.ts index 534058f..b85bd12 100644 --- a/apps/desktop/src/store/videoClassificationStore.ts +++ b/apps/desktop/src/store/videoClassificationStore.ts @@ -60,6 +60,9 @@ export interface ClassificationStats { completed_tasks: number; failed_tasks: number; total_classifications: number; + successful_classifications: number; + failed_classifications: number; + needs_review_classifications: number; average_confidence: number; average_quality_score: number; } diff --git a/apps/desktop/src/types/videoClassification.ts b/apps/desktop/src/types/videoClassification.ts index f3cf0a5..e1bb911 100644 --- a/apps/desktop/src/types/videoClassification.ts +++ b/apps/desktop/src/types/videoClassification.ts @@ -121,7 +121,10 @@ export interface ClassificationStats { completed_tasks: number; failed_tasks: number; total_classifications: number; - average_confidence_score: number; + successful_classifications: number; + failed_classifications: number; + needs_review_classifications: number; + average_confidence: number; average_quality_score: number; }