fix: 修复模板匹配结果相关的数据类型错误

1. 修复list_matching_results接口'Invalid column type Integer at index: 18, name: is_active'错误
   - 修正template_matching_result_repository.rs中row_to_matching_result函数的is_active字段处理
   - 使用rusqlite::types::Value枚举正确处理多种数据类型

2. 修复数据插入和读取的类型不一致问题
   - 修正数据插入时将INTEGER字段转换为字符串的错误
   - 统一数据类型:数字字段直接使用原始类型而非字符串

3. 实现应用匹配结果功能
   - 修复ProjectDetails.tsx中handleApplyMatchingResult函数
   - 调用save_matching_result API保存匹配结果到数据库
   - 添加成功/失败通知提示

修复的具体问题:
- template_matching_results表的is_active字段类型处理
- 数字字段(total_segments, matched_segments等)的类型转换
- quality_score字段的NULL值处理
- 前端应用匹配结果后保存到数据库的逻辑
This commit is contained in:
imeepos 2025-07-16 14:05:01 +08:00
parent a4cacf42da
commit 3e6c05c4ac
2 changed files with 58 additions and 26 deletions

View File

@ -53,19 +53,19 @@ impl TemplateMatchingResultRepository {
&final_result.binding_id,
&final_result.result_name,
&final_result.description,
&final_result.total_segments.to_string(),
&final_result.matched_segments.to_string(),
&final_result.failed_segments.to_string(),
&final_result.success_rate.to_string(),
&final_result.used_materials.to_string(),
&final_result.used_models.to_string(),
&final_result.matching_duration_ms.to_string(),
&final_result.quality_score.map(|s| s.to_string()),
&final_result.total_segments,
&final_result.matched_segments,
&final_result.failed_segments,
&final_result.success_rate,
&final_result.used_materials,
&final_result.used_models,
&final_result.matching_duration_ms,
&final_result.quality_score,
&serde_json::to_string(&final_result.status).unwrap(),
&final_result.metadata,
&final_result.created_at.to_rfc3339(),
&final_result.updated_at.to_rfc3339(),
&(final_result.is_active as i32).to_string(),
&(final_result.is_active as i32),
],
)?;
@ -458,8 +458,7 @@ impl TemplateMatchingResultRepository {
let status: MatchingResultStatus = serde_json::from_str(&status_str)
.unwrap_or(MatchingResultStatus::Success);
let quality_score_str: Option<String> = row.get("quality_score")?;
let quality_score = quality_score_str.and_then(|s| s.parse::<f64>().ok());
let quality_score: Option<f64> = row.get("quality_score")?;
let created_at_str: String = row.get("created_at")?;
let updated_at_str: String = row.get("updated_at")?;
@ -470,8 +469,13 @@ impl TemplateMatchingResultRepository {
.unwrap_or_else(|_| Utc::now().into())
.with_timezone(&Utc);
let is_active_str: String = row.get("is_active")?;
let is_active = is_active_str.parse::<i32>().unwrap_or(1) != 0;
// 正确处理 is_active 字段,支持多种数据类型
let is_active = match row.get::<_, rusqlite::types::Value>("is_active")? {
rusqlite::types::Value::Integer(i) => i != 0,
rusqlite::types::Value::Text(s) => s == "1" || s.to_lowercase() == "true",
rusqlite::types::Value::Real(f) => f != 0.0,
_ => true, // 默认为 true
};
Ok(TemplateMatchingResult {
id: row.get("id")?,
@ -480,13 +484,13 @@ impl TemplateMatchingResultRepository {
binding_id: row.get("binding_id")?,
result_name: row.get("result_name")?,
description: row.get("description")?,
total_segments: row.get::<_, String>("total_segments")?.parse().unwrap_or(0),
matched_segments: row.get::<_, String>("matched_segments")?.parse().unwrap_or(0),
failed_segments: row.get::<_, String>("failed_segments")?.parse().unwrap_or(0),
success_rate: row.get::<_, String>("success_rate")?.parse().unwrap_or(0.0),
used_materials: row.get::<_, String>("used_materials")?.parse().unwrap_or(0),
used_models: row.get::<_, String>("used_models")?.parse().unwrap_or(0),
matching_duration_ms: row.get::<_, String>("matching_duration_ms")?.parse().unwrap_or(0),
total_segments: row.get("total_segments")?,
matched_segments: row.get("matched_segments")?,
failed_segments: row.get("failed_segments")?,
success_rate: row.get("success_rate")?,
used_materials: row.get("used_materials")?,
used_models: row.get("used_models")?,
matching_duration_ms: row.get("matching_duration_ms")?,
quality_score,
status,
metadata: row.get("metadata")?,

View File

@ -34,6 +34,7 @@ import { zhCN } from 'date-fns/locale';
import { MaterialSegmentStats } from '../components/MaterialSegmentStats';
import { MaterialSegmentViewMode } from '../types/materialSegmentView';
import { TemplateMatchingResultManager } from '../components/TemplateMatchingResultManager';
import { useNotifications } from '../components/NotificationSystem';
// 格式化时间
const formatTime = (dateString: string) => {
@ -78,6 +79,7 @@ export const ProjectDetails: React.FC = () => {
queueStats,
getProjectQueueStatus
} = useVideoClassificationStore();
const { success: addNotification } = useNotifications();
// 模板绑定状态管理
const {
@ -362,20 +364,46 @@ export const ProjectDetails: React.FC = () => {
const handleApplyMatchingResult = async (result: MaterialMatchingResult) => {
try {
// 这里可以添加应用匹配结果的逻辑
// 例如更新模板绑定的匹配状态等
console.log('应用匹配结果:', result);
if (!currentMatchingBinding) {
throw new Error('没有找到当前匹配绑定信息');
}
// 生成结果名称
const resultName = `匹配结果_${new Date().toLocaleString('zh-CN')}`;
const description = `模板 ${currentMatchingBinding.template_name} 的匹配结果,成功匹配 ${result.statistics.matched_segments} 个片段`;
// 调用后端API保存匹配结果
await invoke('save_matching_result', {
serviceResult: {
project_id: project?.id,
template_id: currentMatchingBinding.binding.template_id,
binding_id: currentMatchingBinding.binding.id,
matches: result.matches,
failed_segments: result.failed_segments,
statistics: result.statistics,
matching_duration_ms: 0 // MaterialMatchingResult 没有这个字段,使用默认值
},
resultName,
description,
matchingDurationMs: 0 // 使用默认值
});
// 关闭对话框
setShowMatchingResultDialog(false);
setMatchingResult(null);
setCurrentMatchingBinding(null);
// 可以显示成功提示
alert('匹配结果已应用');
// 显示成功提示
addNotification('匹配结果已保存', `已成功保存匹配结果"${resultName}",可在匹配记录页面查看。`);
// 如果当前在匹配记录选项卡,刷新数据
if (activeTab === 'matching-results') {
// 触发匹配记录列表刷新
window.location.reload();
}
} catch (error) {
console.error('应用匹配结果失败:', error);
alert(`应用匹配结果失败: ${error}`);
addNotification('保存匹配结果失败', `保存匹配结果时发生错误: ${error}`);
}
};