feat: 实现AI分类设置功能 (v0.1.7)
新增功能: - AI分类CRUD操作 (创建、读取、更新、删除) - 实时提示词预览功能 - 分类排序和状态管理 - 完整的表单验证和错误处理 后端架构: - 数据层: AiClassification模型和仓储 - 业务层: AiClassificationService业务逻辑 - 表示层: 10个Tauri命令接口 - 数据库: ai_classifications表和索引 前端架构: - 类型系统: 完整的TypeScript类型定义 - 服务层: AiClassificationService API封装 - 组件层: 5个专用组件 (主页面、表单、预览、删除确认、实时预览) - 路由集成: /ai-classification-settings 质量保证: - 52个单元测试 (100%通过) - TypeScript和Rust编译无错误 - 遵循promptx开发规范 核心特性: - 支持分类名称和提示词定义 - 实时生成完整AI分类提示词 - 拖拽排序和批量操作 - 优雅的用户界面和交互体验
This commit is contained in:
195
apps/desktop/src-tauri/src/data/models/ai_classification.rs
Normal file
195
apps/desktop/src-tauri/src/data/models/ai_classification.rs
Normal file
@@ -0,0 +1,195 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
/// AI分类数据模型
|
||||
/// 遵循 Tauri 开发规范的数据模型设计原则
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AiClassification {
|
||||
/// 分类唯一标识符
|
||||
pub id: String,
|
||||
/// 分类名称
|
||||
pub name: String,
|
||||
/// 分类提示词
|
||||
pub prompt_text: String,
|
||||
/// 分类描述
|
||||
pub description: Option<String>,
|
||||
/// 是否激活
|
||||
pub is_active: bool,
|
||||
/// 排序顺序
|
||||
pub sort_order: i32,
|
||||
/// 创建时间
|
||||
pub created_at: DateTime<Utc>,
|
||||
/// 更新时间
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// 创建AI分类请求
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CreateAiClassificationRequest {
|
||||
/// 分类名称
|
||||
pub name: String,
|
||||
/// 分类提示词
|
||||
pub prompt_text: String,
|
||||
/// 分类描述
|
||||
pub description: Option<String>,
|
||||
/// 排序顺序
|
||||
pub sort_order: Option<i32>,
|
||||
}
|
||||
|
||||
/// 更新AI分类请求
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpdateAiClassificationRequest {
|
||||
/// 分类名称
|
||||
pub name: Option<String>,
|
||||
/// 分类提示词
|
||||
pub prompt_text: Option<String>,
|
||||
/// 分类描述
|
||||
pub description: Option<String>,
|
||||
/// 是否激活
|
||||
pub is_active: Option<bool>,
|
||||
/// 排序顺序
|
||||
pub sort_order: Option<i32>,
|
||||
}
|
||||
|
||||
/// AI分类查询参数
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AiClassificationQuery {
|
||||
/// 是否只查询激活的分类
|
||||
pub active_only: Option<bool>,
|
||||
/// 排序字段
|
||||
pub sort_by: Option<String>,
|
||||
/// 排序方向
|
||||
pub sort_order: Option<String>,
|
||||
/// 分页大小
|
||||
pub limit: Option<i32>,
|
||||
/// 分页偏移
|
||||
pub offset: Option<i32>,
|
||||
}
|
||||
|
||||
impl Default for AiClassificationQuery {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
active_only: Some(true),
|
||||
sort_by: Some("sort_order".to_string()),
|
||||
sort_order: Some("ASC".to_string()),
|
||||
limit: None,
|
||||
offset: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// AI分类预览数据
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AiClassificationPreview {
|
||||
/// 分类列表
|
||||
pub classifications: Vec<AiClassification>,
|
||||
/// 生成的完整提示词
|
||||
pub full_prompt: String,
|
||||
}
|
||||
|
||||
impl AiClassification {
|
||||
/// 创建新的AI分类实例
|
||||
pub fn new(
|
||||
id: String,
|
||||
name: String,
|
||||
prompt_text: String,
|
||||
description: Option<String>,
|
||||
sort_order: i32,
|
||||
) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id,
|
||||
name,
|
||||
prompt_text,
|
||||
description,
|
||||
is_active: true,
|
||||
sort_order,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新分类信息
|
||||
pub fn update(&mut self, request: UpdateAiClassificationRequest) {
|
||||
if let Some(name) = request.name {
|
||||
self.name = name;
|
||||
}
|
||||
if let Some(prompt_text) = request.prompt_text {
|
||||
self.prompt_text = prompt_text;
|
||||
}
|
||||
if let Some(description) = request.description {
|
||||
self.description = Some(description);
|
||||
}
|
||||
if let Some(is_active) = request.is_active {
|
||||
self.is_active = is_active;
|
||||
}
|
||||
if let Some(sort_order) = request.sort_order {
|
||||
self.sort_order = sort_order;
|
||||
}
|
||||
self.updated_at = Utc::now();
|
||||
}
|
||||
|
||||
/// 生成分类字符串(用于提示词模板)
|
||||
pub fn to_category_string(&self) -> String {
|
||||
format!("**{}**: {}", self.name, self.prompt_text)
|
||||
}
|
||||
}
|
||||
|
||||
/// 生成完整的AI分类提示词
|
||||
pub fn generate_full_prompt(classifications: &[AiClassification]) -> String {
|
||||
let active_classifications: Vec<&AiClassification> = classifications
|
||||
.iter()
|
||||
.filter(|c| c.is_active)
|
||||
.collect();
|
||||
|
||||
if active_classifications.is_empty() {
|
||||
return "暂无激活的分类,无法生成提示词".to_string();
|
||||
}
|
||||
|
||||
// 生成分类名称列表
|
||||
let category_names = active_classifications
|
||||
.iter()
|
||||
.map(|c| c.name.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n - ");
|
||||
|
||||
// 生成详细分类描述
|
||||
let categories_str = active_classifications
|
||||
.iter()
|
||||
.map(|c| c.to_category_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n - ");
|
||||
|
||||
format!(
|
||||
r#"请分析这个视频的内容,并将其分类到以下类别之一:
|
||||
- {}
|
||||
|
||||
请按以下步骤进行分析:
|
||||
|
||||
1. **冲突处理**:
|
||||
- 按最大可见面积分类
|
||||
|
||||
2. **内容分类**(仅对包含目标商品且质量合格的视频):
|
||||
- {}
|
||||
|
||||
请返回JSON格式的结果:
|
||||
|
||||
{{
|
||||
"category": "分类结果",
|
||||
"confidence": 0.85,
|
||||
"reasoning": "详细的分类理由,包括商品匹配情况和内容特征",
|
||||
"features": ["观察到的关键特征1", "关键特征2", "关键特征3"],
|
||||
"product_match": true/false,
|
||||
"quality_score": 0.9
|
||||
}}
|
||||
|
||||
**分类优先级**:
|
||||
1. 商品匹配 > 内容分类
|
||||
2. 质量合格 > 内容丰富
|
||||
3. 明确分类 > 模糊归类
|
||||
|
||||
请仔细观察视频内容,确保分类准确性。"#,
|
||||
category_names,
|
||||
categories_str
|
||||
)
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod project;
|
||||
pub mod material;
|
||||
pub mod model;
|
||||
pub mod ai_classification;
|
||||
|
||||
Reference in New Issue
Block a user