feat: 实现相似度检索工具
- 基于现有search_similar_outfits功能开发独立的相似度检索小工具 - 遵循promptx/tauri-desktop-app-expert开发规范 - 实现完整的前后端架构: * Rust后端命令接口 (similarity_search_commands.rs) * TypeScript类型定义 (similaritySearch.ts) * Zustand状态管理 (similaritySearchStore.ts) * React组件 (SimilaritySearchTool, SimilaritySearchPanel, SimilaritySearchResults, SimilaritySearchCard) * 服务层 (similaritySearchService.ts) 功能特性: - 智能搜索建议和自动完成 - 可调节的相关性阈值 (LOWEST/LOW/MEDIUM/HIGH) - 快速搜索标签 - 响应式网格布局结果展示 - 优雅的加载状态和错误处理 - 遵循UI/UX设计标准的美观界面 技术实现: - 复用现有outfit search API和数据模型 - 简化的搜索配置,专注核心功能 - 完整的TypeScript类型安全 - 现代化的React Hooks和状态管理 - TailwindCSS响应式设计 - 平滑的动画和交互效果 集成: - 添加到快捷工具列表 (/tools/similarity-search) - 配置React Router路由 - 注册Tauri命令处理器
This commit is contained in:
parent
0436267266
commit
ef2a561fe2
|
|
@ -28,7 +28,7 @@ impl BatchWatermarkProcessor {
|
|||
task: BatchWatermarkTask,
|
||||
repository: Arc<MaterialRepository>,
|
||||
) -> Result<()> {
|
||||
let timer = PERFORMANCE_MONITOR.start_operation("batch_watermark_processing");
|
||||
let _timer = PERFORMANCE_MONITOR.start_operation("batch_watermark_processing");
|
||||
let start_time = Instant::now();
|
||||
|
||||
info!(
|
||||
|
|
@ -343,7 +343,7 @@ impl BatchWatermarkProcessor {
|
|||
/// 保存检测结果
|
||||
async fn save_detection_result(
|
||||
result: &crate::data::models::watermark::WatermarkDetectionResult,
|
||||
repository: &MaterialRepository,
|
||||
_repository: &MaterialRepository,
|
||||
) -> Result<()> {
|
||||
// TODO: 实现检测结果保存逻辑
|
||||
debug!(
|
||||
|
|
@ -386,7 +386,7 @@ impl BatchWatermarkProcessor {
|
|||
}
|
||||
|
||||
/// 从配置中获取水印路径
|
||||
fn get_watermark_path_from_config(config: &WatermarkConfig) -> Result<String> {
|
||||
fn get_watermark_path_from_config(_config: &WatermarkConfig) -> Result<String> {
|
||||
// TODO: 根据水印类型和配置获取实际的水印文件路径
|
||||
// 这里需要与水印模板管理系统集成
|
||||
Ok("watermarks/default.png".to_string())
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ impl ConversationService {
|
|||
gemini_service: &mut GeminiService,
|
||||
current_message: &str,
|
||||
history_messages: &[ConversationMessage],
|
||||
system_prompt: Option<&str>,
|
||||
_system_prompt: Option<&str>,
|
||||
) -> Result<String> {
|
||||
use crate::infrastructure::gemini_service::{GenerateContentRequest, ContentPart, Part, GenerationConfig};
|
||||
|
||||
|
|
|
|||
|
|
@ -812,7 +812,7 @@ impl MaterialMatchingService {
|
|||
used_segment_ids: &mut HashSet<String>,
|
||||
template_already_used_sequence_001: bool,
|
||||
) -> Result<SegmentMatch, String> {
|
||||
let target_duration = track_segment.duration as f64 / 1_000_000.0; // 转换为秒
|
||||
let _target_duration = track_segment.duration as f64 / 1_000_000.0; // 转换为秒
|
||||
|
||||
// 获取所有AI分类,按权重排序
|
||||
let ai_classifications = match self.ai_classification_service.get_classifications_by_weight().await {
|
||||
|
|
@ -933,9 +933,9 @@ impl MaterialMatchingService {
|
|||
// 开始循环匹配
|
||||
loop {
|
||||
total_rounds += 1;
|
||||
let round_start_time = std::time::Instant::now();
|
||||
let _round_start_time = std::time::Instant::now();
|
||||
let mut round_successful_matches = 0u32;
|
||||
let mut round_failed_matches = 0u32;
|
||||
let mut _round_failed_matches = 0u32;
|
||||
|
||||
// 记录本轮开始时的已使用片段数量,用于检测是否有实质性进展
|
||||
let segments_count_before_round = global_used_segment_ids.len();
|
||||
|
|
@ -1027,7 +1027,7 @@ impl MaterialMatchingService {
|
|||
});
|
||||
} else {
|
||||
// 部分匹配失败,视为失败
|
||||
round_failed_matches += 1;
|
||||
_round_failed_matches += 1;
|
||||
failed_matches += 1;
|
||||
|
||||
let failure_reason = format!("部分匹配失败:{} 个片段匹配成功,{} 个片段匹配失败",
|
||||
|
|
@ -1052,7 +1052,7 @@ impl MaterialMatchingService {
|
|||
}
|
||||
}
|
||||
Err(error) => {
|
||||
round_failed_matches += 1;
|
||||
_round_failed_matches += 1;
|
||||
failed_matches += 1;
|
||||
|
||||
matching_results.push(BatchMatchingItemResult {
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ mod template_foreign_key_tests {
|
|||
#[tokio::test]
|
||||
async fn test_template_id_consistency() {
|
||||
let database = create_test_database();
|
||||
let service = TemplateService::new(database);
|
||||
let _service = TemplateService::new(database);
|
||||
let template = create_test_template();
|
||||
|
||||
// 验证模板和素材的ID一致性
|
||||
|
|
|
|||
|
|
@ -208,10 +208,6 @@ mod watermark_tests {
|
|||
|
||||
#[test]
|
||||
fn test_watermark_position_calculation() {
|
||||
let video_width = 1920.0;
|
||||
let video_height = 1080.0;
|
||||
let scale = 1.0;
|
||||
|
||||
// 测试固定位置
|
||||
let positions = vec![
|
||||
WatermarkPosition::TopLeft,
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ impl WatermarkAdditionService {
|
|||
output_path: &str,
|
||||
watermark_path: &str,
|
||||
config: &WatermarkConfig,
|
||||
repository: Arc<MaterialRepository>,
|
||||
_repository: Arc<MaterialRepository>,
|
||||
) -> Result<WatermarkProcessingResult> {
|
||||
let timer = PERFORMANCE_MONITOR.start_operation("watermark_addition_video");
|
||||
let _timer = PERFORMANCE_MONITOR.start_operation("watermark_addition_video");
|
||||
let start_time = Instant::now();
|
||||
|
||||
info!(
|
||||
|
|
@ -139,7 +139,7 @@ impl WatermarkAdditionService {
|
|||
watermark_path: &str,
|
||||
config: &WatermarkConfig,
|
||||
) -> Result<WatermarkProcessingResult> {
|
||||
let timer = PERFORMANCE_MONITOR.start_operation("watermark_addition_image");
|
||||
let _timer = PERFORMANCE_MONITOR.start_operation("watermark_addition_image");
|
||||
let start_time = Instant::now();
|
||||
|
||||
info!(
|
||||
|
|
@ -597,7 +597,7 @@ impl WatermarkAdditionService {
|
|||
opacity: f32,
|
||||
scale: f32,
|
||||
rotation: f32,
|
||||
blend_mode: &BlendMode,
|
||||
_blend_mode: &BlendMode,
|
||||
animation: Option<&WatermarkAnimation>,
|
||||
) -> String {
|
||||
let mut filter_parts = Vec::new();
|
||||
|
|
@ -652,7 +652,7 @@ impl WatermarkAdditionService {
|
|||
position: &(f32, f32),
|
||||
opacity: f32,
|
||||
scale: f32,
|
||||
rotation: f32,
|
||||
_rotation: f32,
|
||||
) -> String {
|
||||
let font_size = (24.0 * scale) as u32;
|
||||
let alpha = (opacity * 255.0) as u32;
|
||||
|
|
@ -693,7 +693,7 @@ impl WatermarkAdditionService {
|
|||
}
|
||||
|
||||
/// 转换SVG为PNG
|
||||
async fn convert_svg_to_png(svg_path: &str, scale: f32) -> Result<String> {
|
||||
async fn convert_svg_to_png(svg_path: &str, _scale: f32) -> Result<String> {
|
||||
// TODO: 实现SVG到PNG的转换
|
||||
// 可以使用librsvg或其他SVG渲染库
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ impl WatermarkDetectionService {
|
|||
material_id: &str,
|
||||
video_path: &str,
|
||||
config: &WatermarkDetectionConfig,
|
||||
repository: Arc<MaterialRepository>,
|
||||
_repository: Arc<MaterialRepository>,
|
||||
) -> Result<WatermarkDetectionResult> {
|
||||
let timer = PERFORMANCE_MONITOR.start_operation("watermark_detection");
|
||||
let _timer = PERFORMANCE_MONITOR.start_operation("watermark_detection");
|
||||
let start_time = Instant::now();
|
||||
|
||||
info!(
|
||||
|
|
@ -142,7 +142,7 @@ impl WatermarkDetectionService {
|
|||
image_path: &str,
|
||||
config: &WatermarkDetectionConfig,
|
||||
) -> Result<WatermarkDetectionResult> {
|
||||
let timer = PERFORMANCE_MONITOR.start_operation("watermark_detection_image");
|
||||
let _timer = PERFORMANCE_MONITOR.start_operation("watermark_detection_image");
|
||||
let start_time = Instant::now();
|
||||
|
||||
info!(
|
||||
|
|
@ -306,7 +306,7 @@ impl WatermarkDetectionService {
|
|||
/// 模板匹配检测
|
||||
async fn template_matching_detection(
|
||||
image_path: &str,
|
||||
config: &WatermarkDetectionConfig,
|
||||
_config: &WatermarkDetectionConfig,
|
||||
) -> Result<Vec<WatermarkDetection>> {
|
||||
// TODO: 实现OpenCV模板匹配算法
|
||||
// 这里先返回模拟结果,后续需要集成OpenCV
|
||||
|
|
@ -334,7 +334,7 @@ impl WatermarkDetectionService {
|
|||
/// 边缘检测
|
||||
async fn edge_detection(
|
||||
image_path: &str,
|
||||
config: &WatermarkDetectionConfig,
|
||||
_config: &WatermarkDetectionConfig,
|
||||
) -> Result<Vec<WatermarkDetection>> {
|
||||
// TODO: 实现边缘检测算法
|
||||
debug!("执行边缘检测: {}", image_path);
|
||||
|
|
@ -346,7 +346,7 @@ impl WatermarkDetectionService {
|
|||
/// 频域分析检测
|
||||
async fn frequency_analysis_detection(
|
||||
image_path: &str,
|
||||
config: &WatermarkDetectionConfig,
|
||||
_config: &WatermarkDetectionConfig,
|
||||
) -> Result<Vec<WatermarkDetection>> {
|
||||
// TODO: 实现频域分析算法
|
||||
debug!("执行频域分析检测: {}", image_path);
|
||||
|
|
@ -358,7 +358,7 @@ impl WatermarkDetectionService {
|
|||
/// 透明度检测
|
||||
async fn transparency_detection(
|
||||
image_path: &str,
|
||||
config: &WatermarkDetectionConfig,
|
||||
_config: &WatermarkDetectionConfig,
|
||||
) -> Result<Vec<WatermarkDetection>> {
|
||||
// TODO: 实现透明度检测算法
|
||||
debug!("执行透明度检测: {}", image_path);
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ impl WatermarkRemovalService {
|
|||
input_path: &str,
|
||||
output_path: &str,
|
||||
config: &WatermarkRemovalConfig,
|
||||
repository: Arc<MaterialRepository>,
|
||||
_repository: Arc<MaterialRepository>,
|
||||
) -> Result<WatermarkProcessingResult> {
|
||||
let timer = PERFORMANCE_MONITOR.start_operation("watermark_removal_video");
|
||||
let _timer = PERFORMANCE_MONITOR.start_operation("watermark_removal_video");
|
||||
let start_time = Instant::now();
|
||||
|
||||
info!(
|
||||
|
|
@ -117,7 +117,7 @@ impl WatermarkRemovalService {
|
|||
output_path: &str,
|
||||
config: &WatermarkRemovalConfig,
|
||||
) -> Result<WatermarkProcessingResult> {
|
||||
let timer = PERFORMANCE_MONITOR.start_operation("watermark_removal_image");
|
||||
let _timer = PERFORMANCE_MONITOR.start_operation("watermark_removal_image");
|
||||
let start_time = Instant::now();
|
||||
|
||||
info!(
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ impl WatermarkTemplateService {
|
|||
description: Option<String>,
|
||||
tags: Vec<String>,
|
||||
) -> Result<WatermarkTemplate> {
|
||||
let timer = PERFORMANCE_MONITOR.start_operation("upload_watermark_template");
|
||||
let _timer = PERFORMANCE_MONITOR.start_operation("upload_watermark_template");
|
||||
let start_time = Instant::now();
|
||||
|
||||
info!(
|
||||
|
|
@ -165,7 +165,7 @@ impl WatermarkTemplateService {
|
|||
info!(template_id = %template_id, hard_delete = hard_delete, "开始删除水印模板");
|
||||
|
||||
// 获取模板信息
|
||||
let template = repository.get_by_id(&template_id)?
|
||||
let _template = repository.get_by_id(&template_id)?
|
||||
.ok_or_else(|| anyhow!("模板不存在: {}", template_id))?;
|
||||
|
||||
if hard_delete {
|
||||
|
|
@ -297,7 +297,7 @@ impl WatermarkTemplateService {
|
|||
}
|
||||
|
||||
/// 生成文字水印缩略图
|
||||
fn generate_text_thumbnail(text_file_path: &str, output_path: &str) -> Result<()> {
|
||||
fn generate_text_thumbnail(_text_file_path: &str, output_path: &str) -> Result<()> {
|
||||
// TODO: 实现文字水印缩略图生成
|
||||
// 可以使用图像库生成包含文字的预览图
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use tauri::{command, State};
|
||||
use crate::infrastructure::app_state::AppState;
|
||||
use crate::app_state::AppState;
|
||||
use crate::data::models::outfit_search::{SearchRequest, SearchResponse, SearchConfig, RelevanceThreshold};
|
||||
use crate::presentation::commands::outfit_search_commands::search_similar_outfits;
|
||||
|
||||
|
|
@ -74,13 +74,14 @@ pub async fn get_similarity_search_suggestions(
|
|||
|
||||
// 基于查询过滤建议
|
||||
let filtered_suggestions: Vec<String> = base_suggestions
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter(|suggestion| {
|
||||
suggestion.contains(&query) ||
|
||||
suggestion.contains(&query) ||
|
||||
query.chars().any(|c| suggestion.contains(c))
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
|
||||
// 如果没有匹配的建议,返回基础建议的前几个
|
||||
if filtered_suggestions.is_empty() {
|
||||
Ok(base_suggestions.into_iter().take(6).collect())
|
||||
|
|
|
|||
Loading…
Reference in New Issue