fix
This commit is contained in:
parent
26cf5ad37b
commit
4994734ca1
|
|
@ -0,0 +1,337 @@
|
|||
# Resource Category 统一入口改造
|
||||
|
||||
## 概述
|
||||
|
||||
将 `resource_category.rs` 改造成统一入口模式,参考 `template.rs` 的实现,使用 `execute_python_cli_command` 统一执行 Python CLI 命令。
|
||||
|
||||
## 改造内容
|
||||
|
||||
### 1. Rust 端改造
|
||||
|
||||
#### 新增数据结构
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ResourceCategoryResponse {
|
||||
pub success: bool,
|
||||
pub message: String,
|
||||
pub data: Option<serde_json::Value>,
|
||||
pub output: Option<String>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
// 各种请求结构体
|
||||
pub struct CategoryListRequest { ... }
|
||||
pub struct CategoryGetRequest { ... }
|
||||
pub struct CategoryCreateRequest { ... }
|
||||
pub struct CategoryUpdateRequest { ... }
|
||||
pub struct CategoryDeleteRequest { ... }
|
||||
pub struct CategorySearchRequest { ... }
|
||||
pub struct CategoryColorRequest { ... }
|
||||
pub struct CategoryBatchCreateRequest { ... }
|
||||
```
|
||||
|
||||
#### 统一执行函数
|
||||
|
||||
```rust
|
||||
async fn execute_python_cli_command(
|
||||
app: AppHandle,
|
||||
command_args: Vec<String>,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
let mut args = vec!["-m".to_string(), "python_core.cli".to_string()];
|
||||
args.extend(command_args);
|
||||
|
||||
match execute_python_command(app, &args, None).await {
|
||||
Ok(output) => {
|
||||
// 解析JSON响应或返回文本输出
|
||||
}
|
||||
Err(error) => {
|
||||
// 返回错误响应
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 新增 CLI 命令
|
||||
|
||||
- `get_all_resource_categories_cli()` - 获取所有分类
|
||||
- `get_resource_category_by_id_cli()` - 根据ID获取分类
|
||||
- `create_resource_category_cli()` - 创建分类
|
||||
- `update_resource_category_cli()` - 更新分类
|
||||
- `delete_resource_category_cli()` - 删除分类
|
||||
- `search_resource_categories_cli()` - 搜索分类
|
||||
- `get_resource_categories_by_color_cli()` - 按颜色获取分类
|
||||
- `get_cloud_resource_categories_cli()` - 获取云端分类
|
||||
- `batch_create_resource_categories_cli()` - 批量创建分类
|
||||
- `get_resource_category_count_cli()` - 获取分类数量
|
||||
- `activate_resource_category_cli()` - 激活分类
|
||||
- `deactivate_resource_category_cli()` - 停用分类
|
||||
|
||||
#### 向后兼容
|
||||
|
||||
保留原有的命令函数,确保现有前端代码不受影响:
|
||||
|
||||
```rust
|
||||
#[command]
|
||||
pub async fn get_all_resource_categories(app: AppHandle) -> Result<String, String> {
|
||||
// 原有实现保持不变
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Python CLI 端实现
|
||||
|
||||
#### 新增命令文件
|
||||
|
||||
创建 `python_core/cli/commands/resource_category.py`:
|
||||
|
||||
```python
|
||||
resource_category_app = typer.Typer(help="素材分类管理命令")
|
||||
|
||||
@resource_category_app.command("list")
|
||||
def list_categories(...):
|
||||
"""获取所有素材分类"""
|
||||
|
||||
@resource_category_app.command("get")
|
||||
def get_category(...):
|
||||
"""根据ID获取素材分类"""
|
||||
|
||||
@resource_category_app.command("create")
|
||||
def create_category(...):
|
||||
"""创建新的素材分类"""
|
||||
|
||||
# ... 其他命令
|
||||
```
|
||||
|
||||
#### 注册到主 CLI
|
||||
|
||||
在 `python_core/cli/cli.py` 中注册:
|
||||
|
||||
```python
|
||||
from python_core.cli.commands.resource_category import resource_category_app
|
||||
|
||||
app.add_typer(resource_category_app, name="resource-category")
|
||||
```
|
||||
|
||||
### 3. 支持的 CLI 命令
|
||||
|
||||
#### 基本操作
|
||||
|
||||
```bash
|
||||
# 获取所有分类
|
||||
python -m python_core.cli resource-category list --user-id USER_ID
|
||||
|
||||
# 根据ID获取分类
|
||||
python -m python_core.cli resource-category get CATEGORY_ID --user-id USER_ID
|
||||
|
||||
# 创建分类
|
||||
python -m python_core.cli resource-category create "分类名称" \
|
||||
--ai-prompt "AI提示词" --color "#FF0000" --user-id USER_ID
|
||||
|
||||
# 更新分类
|
||||
python -m python_core.cli resource-category update CATEGORY_ID \
|
||||
--title "新标题" --color "#00FF00" --user-id USER_ID
|
||||
|
||||
# 删除分类
|
||||
python -m python_core.cli resource-category delete CATEGORY_ID \
|
||||
--hard-delete --user-id USER_ID
|
||||
```
|
||||
|
||||
#### 高级查询
|
||||
|
||||
```bash
|
||||
# 搜索分类
|
||||
python -m python_core.cli resource-category search "关键词" --user-id USER_ID
|
||||
|
||||
# 按颜色获取分类
|
||||
python -m python_core.cli resource-category by-color "#FF0000" --user-id USER_ID
|
||||
|
||||
# 获取云端分类
|
||||
python -m python_core.cli resource-category cloud --limit 50
|
||||
|
||||
# 获取分类数量
|
||||
python -m python_core.cli resource-category count --user-id USER_ID
|
||||
```
|
||||
|
||||
#### 状态管理
|
||||
|
||||
```bash
|
||||
# 激活分类
|
||||
python -m python_core.cli resource-category activate CATEGORY_ID --user-id USER_ID
|
||||
|
||||
# 停用分类
|
||||
python -m python_core.cli resource-category deactivate CATEGORY_ID --user-id USER_ID
|
||||
```
|
||||
|
||||
#### 批量操作
|
||||
|
||||
```bash
|
||||
# 批量创建分类
|
||||
python -m python_core.cli resource-category batch-create \
|
||||
--data '[{"title":"分类1","color":"#FF0000"},{"title":"分类2","color":"#00FF00"}]' \
|
||||
--user-id USER_ID
|
||||
```
|
||||
|
||||
## 主要特点
|
||||
|
||||
### 🔄 **统一模式**
|
||||
- 所有新命令都使用 `execute_python_cli_command` 模式
|
||||
- 统一的错误处理和响应格式
|
||||
- 与 `template.rs` 保持一致的架构
|
||||
|
||||
### 🔗 **向后兼容**
|
||||
- 保留所有原有的命令函数
|
||||
- 现有前端代码无需修改
|
||||
- 渐进式迁移到新的 CLI 模式
|
||||
|
||||
### 🚀 **功能增强**
|
||||
- 支持更多的查询和过滤选项
|
||||
- 批量操作支持
|
||||
- 状态管理功能
|
||||
- 云端分类管理
|
||||
|
||||
### 📊 **统一响应格式**
|
||||
|
||||
```rust
|
||||
pub struct ResourceCategoryResponse {
|
||||
pub success: bool, // 操作是否成功
|
||||
pub message: String, // 操作消息
|
||||
pub data: Option<Value>, // 返回数据
|
||||
pub output: Option<String>, // 原始输出
|
||||
pub error: Option<String>, // 错误信息
|
||||
}
|
||||
```
|
||||
|
||||
### 🎯 **丰富的参数支持**
|
||||
|
||||
每个命令都支持:
|
||||
- `--user-id`: 用户ID
|
||||
- `--verbose`: 详细输出
|
||||
- `--json`: JSON格式输出
|
||||
- 特定的业务参数
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 前端调用新的 CLI 命令
|
||||
|
||||
```typescript
|
||||
// 获取所有分类
|
||||
const response = await invoke<ResourceCategoryResponse>('get_all_resource_categories_cli', {
|
||||
request: {
|
||||
user_id: "user_123",
|
||||
include_cloud: true,
|
||||
limit: 50,
|
||||
verbose: false,
|
||||
json_output: true
|
||||
}
|
||||
});
|
||||
|
||||
// 创建分类
|
||||
const createResponse = await invoke<ResourceCategoryResponse>('create_resource_category_cli', {
|
||||
request: {
|
||||
title: "新分类",
|
||||
ai_prompt: "AI提示词",
|
||||
color: "#FF0000",
|
||||
is_cloud: false,
|
||||
user_id: "user_123",
|
||||
verbose: false,
|
||||
json_output: true
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 直接使用 CLI
|
||||
|
||||
```bash
|
||||
# 完整的工作流示例
|
||||
python -m python_core.cli resource-category create "视频素材" \
|
||||
--ai-prompt "用于识别视频文件" \
|
||||
--color "#FF6B6B" \
|
||||
--user-id "demo_user"
|
||||
|
||||
python -m python_core.cli resource-category list --user-id "demo_user"
|
||||
|
||||
python -m python_core.cli resource-category search "视频" --user-id "demo_user"
|
||||
```
|
||||
|
||||
## 注册的 Tauri 命令
|
||||
|
||||
在 `lib.rs` 中注册了以下新命令:
|
||||
|
||||
```rust
|
||||
commands::get_all_resource_categories_cli,
|
||||
commands::get_resource_category_by_id_cli,
|
||||
commands::create_resource_category_cli,
|
||||
commands::update_resource_category_cli,
|
||||
commands::delete_resource_category_cli,
|
||||
commands::search_resource_categories_cli,
|
||||
commands::get_resource_categories_by_color_cli,
|
||||
commands::get_cloud_resource_categories_cli,
|
||||
commands::batch_create_resource_categories_cli,
|
||||
commands::get_resource_category_count_cli,
|
||||
commands::activate_resource_category_cli,
|
||||
commands::deactivate_resource_category_cli,
|
||||
```
|
||||
|
||||
## 迁移建议
|
||||
|
||||
### 1. 渐进式迁移
|
||||
|
||||
```typescript
|
||||
// 阶段1:继续使用原有命令
|
||||
const categories = await invoke<string>('get_all_resource_categories');
|
||||
|
||||
// 阶段2:迁移到新的 CLI 命令
|
||||
const response = await invoke<ResourceCategoryResponse>('get_all_resource_categories_cli', {
|
||||
request: { user_id: "user_123" }
|
||||
});
|
||||
```
|
||||
|
||||
### 2. 错误处理改进
|
||||
|
||||
```typescript
|
||||
// 新的错误处理模式
|
||||
if (response.success) {
|
||||
const categories = response.data;
|
||||
// 处理成功结果
|
||||
} else {
|
||||
console.error('操作失败:', response.error);
|
||||
// 处理错误
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 利用新功能
|
||||
|
||||
```typescript
|
||||
// 利用新的查询功能
|
||||
const colorCategories = await invoke<ResourceCategoryResponse>('get_resource_categories_by_color_cli', {
|
||||
request: {
|
||||
color: "#FF0000",
|
||||
user_id: "user_123",
|
||||
include_cloud: true
|
||||
}
|
||||
});
|
||||
|
||||
// 利用批量操作
|
||||
const batchResult = await invoke<ResourceCategoryResponse>('batch_create_resource_categories_cli', {
|
||||
request: {
|
||||
categories: [
|
||||
{ title: "分类1", color: "#FF0000" },
|
||||
{ title: "分类2", color: "#00FF00" }
|
||||
],
|
||||
user_id: "user_123"
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
通过这次改造,`resource_category.rs` 现在:
|
||||
|
||||
- ✅ 使用统一的 CLI 执行模式
|
||||
- ✅ 保持向后兼容性
|
||||
- ✅ 提供丰富的功能和参数选项
|
||||
- ✅ 支持批量操作和高级查询
|
||||
- ✅ 统一的错误处理和响应格式
|
||||
- ✅ 与 `template.rs` 架构保持一致
|
||||
|
||||
这为后续的功能扩展和维护提供了良好的基础。
|
||||
|
|
@ -10,7 +10,7 @@ import typer
|
|||
# 导入命令模块
|
||||
from python_core.cli.commands import scene_detect
|
||||
from python_core.cli.commands.template import template_app
|
||||
from python_core.cli.commands.category import category_app
|
||||
from python_core.cli.commands.resource_category import resource_category_app
|
||||
from python_core.cli.commands.auth import auth_app
|
||||
|
||||
app = typer.Typer(
|
||||
|
|
@ -33,7 +33,7 @@ app = typer.Typer(
|
|||
# 添加命令组到主应用
|
||||
app.add_typer(scene_detect, name="scene")
|
||||
app.add_typer(template_app, name="template")
|
||||
app.add_typer(category_app, name="category")
|
||||
app.add_typer(resource_category_app, name="resource-category")
|
||||
app.add_typer(auth_app, name="auth")
|
||||
|
||||
@app.command()
|
||||
|
|
|
|||
|
|
@ -1,491 +0,0 @@
|
|||
"""
|
||||
资源分类管理CLI命令
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
import typer
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
from rich.panel import Panel
|
||||
import json
|
||||
import re
|
||||
|
||||
from python_core.services.resource_category_manager_v2 import resource_category_manager_v2, ResourceCategory
|
||||
from python_core.utils.logger import logger
|
||||
|
||||
console = Console()
|
||||
category_app = typer.Typer(name="category", help="资源分类管理命令")
|
||||
|
||||
|
||||
def validate_color(color: str) -> bool:
|
||||
"""验证颜色格式(hex格式)"""
|
||||
pattern = r'^#[0-9A-Fa-f]{6}$'
|
||||
return bool(re.match(pattern, color))
|
||||
|
||||
|
||||
@category_app.command("list")
|
||||
def list_categories(
|
||||
include_inactive: bool = typer.Option(False, "--include-inactive", "-i", help="包含已禁用的分类"),
|
||||
limit: int = typer.Option(20, "--limit", "-l", help="显示数量限制"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
format_output: str = typer.Option("table", "--format", "-f", help="输出格式: table, json")
|
||||
):
|
||||
"""列出所有资源分类"""
|
||||
|
||||
try:
|
||||
console.print(f"📋 [bold blue]资源分类列表[/bold blue]")
|
||||
|
||||
# 创建分类管理器
|
||||
manager = resource_category_manager_v2
|
||||
|
||||
# 获取分类列表
|
||||
categories = manager.get_all_categories()
|
||||
|
||||
# 过滤非活跃分类
|
||||
if not include_inactive:
|
||||
categories = [cat for cat in categories if cat.get('is_active', True)]
|
||||
|
||||
if not categories:
|
||||
console.print("📭 没有分类")
|
||||
return
|
||||
|
||||
# 限制显示数量
|
||||
total_count = len(categories)
|
||||
if len(categories) > limit:
|
||||
categories = categories[:limit]
|
||||
console.print(f"📊 显示前 {limit} 个分类(共 {total_count} 个)")
|
||||
else:
|
||||
console.print(f"📊 共 {total_count} 个分类")
|
||||
|
||||
if format_output == "json":
|
||||
# JSON格式输出
|
||||
console.print(json.dumps(categories, ensure_ascii=False, indent=2))
|
||||
return
|
||||
|
||||
# 表格格式输出
|
||||
table = Table(title="资源分类列表")
|
||||
table.add_column("ID", style="cyan", width=12)
|
||||
table.add_column("标题", style="green")
|
||||
table.add_column("AI提示词", style="yellow")
|
||||
table.add_column("颜色", style="magenta")
|
||||
table.add_column("状态", style="blue")
|
||||
table.add_column("创建时间", style="dim")
|
||||
|
||||
if verbose:
|
||||
table.add_column("更新时间", style="dim")
|
||||
|
||||
for category in categories:
|
||||
# 状态
|
||||
status = "✅ 启用" if category.get('is_active', True) else "❌ 禁用"
|
||||
|
||||
# 格式化创建时间
|
||||
created_at = category.get('created_at', '')
|
||||
if 'T' in created_at:
|
||||
created_at = created_at.split('T')[0] + ' ' + created_at.split('T')[1][:8]
|
||||
|
||||
# 颜色显示
|
||||
color = category.get('color', '')
|
||||
color_display = f"[{color}]●[/{color}] {color}" if color else color
|
||||
|
||||
row = [
|
||||
category.get('id', '')[:12],
|
||||
category.get('title', ''),
|
||||
category.get('ai_prompt', '')[:30] + ('...' if len(category.get('ai_prompt', '')) > 30 else ''),
|
||||
color_display,
|
||||
status,
|
||||
created_at
|
||||
]
|
||||
|
||||
if verbose:
|
||||
updated_at = category.get('updated_at', '')
|
||||
if 'T' in updated_at:
|
||||
updated_at = updated_at.split('T')[0] + ' ' + updated_at.split('T')[1][:8]
|
||||
row.append(updated_at)
|
||||
|
||||
table.add_row(*row)
|
||||
|
||||
console.print(table)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]❌ 获取分类列表失败: {str(e)}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
@category_app.command("create")
|
||||
def create_category(
|
||||
title: str = typer.Argument(..., help="分类标题"),
|
||||
ai_prompt: str = typer.Argument(..., help="AI识别提示词"),
|
||||
color: str = typer.Argument(..., help="展示颜色(hex格式,如 #FF5733)"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出")
|
||||
):
|
||||
"""创建新的资源分类"""
|
||||
|
||||
try:
|
||||
console.print(f"🆕 [bold blue]创建资源分类[/bold blue]")
|
||||
console.print(f"标题: {title}")
|
||||
console.print(f"AI提示词: {ai_prompt}")
|
||||
console.print(f"颜色: {color}")
|
||||
|
||||
# 验证颜色格式
|
||||
if not validate_color(color):
|
||||
console.print(f"[red]❌ 颜色格式无效: {color}[/red]")
|
||||
console.print("颜色必须是hex格式,如: #FF5733")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# 创建分类管理器
|
||||
manager = resource_category_manager_v2
|
||||
|
||||
# 创建分类
|
||||
result = manager.create_category(title, ai_prompt, color)
|
||||
|
||||
console.print(f"\n✅ [bold green]分类创建成功![/bold green]")
|
||||
console.print(f"分类ID: {result['id']}")
|
||||
console.print(f"创建时间: {result['created_at']}")
|
||||
|
||||
if verbose:
|
||||
# 显示分类详细信息
|
||||
category_info = f"""
|
||||
🆔 分类ID: {result['id']}
|
||||
📝 标题: {result['title']}
|
||||
🤖 AI提示词: {result['ai_prompt']}
|
||||
🎨 颜色: {result['color']}
|
||||
📅 创建时间: {result['created_at']}
|
||||
🔄 更新时间: {result['updated_at']}
|
||||
✅ 状态: {'启用' if result['is_active'] else '禁用'}
|
||||
"""
|
||||
|
||||
panel = Panel(category_info.strip(), title="分类详情", border_style="green")
|
||||
console.print(panel)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]❌ 创建分类失败: {str(e)}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
@category_app.command("get")
|
||||
def get_category(
|
||||
category_id: str = typer.Argument(..., help="分类ID"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
format_output: str = typer.Option("panel", "--format", "-f", help="输出格式: panel, json")
|
||||
):
|
||||
"""获取分类详情"""
|
||||
|
||||
try:
|
||||
console.print(f"🔍 [bold blue]获取分类详情[/bold blue]")
|
||||
console.print(f"分类ID: {category_id}")
|
||||
|
||||
# 创建分类管理器
|
||||
manager = resource_category_manager_v2
|
||||
|
||||
# 获取分类
|
||||
category = manager.get_category_by_id(category_id)
|
||||
|
||||
if not category:
|
||||
console.print(f"[red]❌ 未找到分类: {category_id}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
if format_output == "json":
|
||||
# JSON格式输出
|
||||
console.print(json.dumps(category, ensure_ascii=False, indent=2))
|
||||
return
|
||||
|
||||
# 面板格式输出
|
||||
console.print(f"\n✅ [bold green]分类详情[/bold green]")
|
||||
|
||||
# 基本信息
|
||||
basic_info = f"""
|
||||
🆔 分类ID: {category.get('id', '')}
|
||||
📝 标题: {category.get('title', '')}
|
||||
🤖 AI提示词: {category.get('ai_prompt', '')}
|
||||
🎨 颜色: {category.get('color', '')}
|
||||
📅 创建时间: {category.get('created_at', '')}
|
||||
🔄 更新时间: {category.get('updated_at', '')}
|
||||
✅ 状态: {'启用' if category.get('is_active', True) else '禁用'}
|
||||
"""
|
||||
|
||||
panel = Panel(basic_info.strip(), title="分类基本信息", border_style="green")
|
||||
console.print(panel)
|
||||
|
||||
if verbose:
|
||||
# 显示颜色预览
|
||||
color = category.get('color', '')
|
||||
if color:
|
||||
color_info = f"""
|
||||
🎨 颜色预览: [{color}]████████[/{color}] {color}
|
||||
"""
|
||||
color_panel = Panel(color_info.strip(), title="颜色预览", border_style="blue")
|
||||
console.print(color_panel)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]❌ 获取分类详情失败: {str(e)}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
@category_app.command("update")
|
||||
def update_category(
|
||||
category_id: str = typer.Argument(..., help="分类ID"),
|
||||
title: Optional[str] = typer.Option(None, "--title", "-t", help="新的标题"),
|
||||
ai_prompt: Optional[str] = typer.Option(None, "--ai-prompt", "-p", help="新的AI提示词"),
|
||||
color: Optional[str] = typer.Option(None, "--color", "-c", help="新的颜色(hex格式)"),
|
||||
active: Optional[bool] = typer.Option(None, "--active", "-a", help="是否启用"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出")
|
||||
):
|
||||
"""更新分类信息"""
|
||||
|
||||
try:
|
||||
console.print(f"🔄 [bold blue]更新分类[/bold blue]")
|
||||
console.print(f"分类ID: {category_id}")
|
||||
|
||||
# 验证至少有一个更新参数
|
||||
if not any([title, ai_prompt, color, active is not None]):
|
||||
console.print(f"[red]❌ 请至少提供一个要更新的参数[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# 验证颜色格式(如果提供)
|
||||
if color and not validate_color(color):
|
||||
console.print(f"[red]❌ 颜色格式无效: {color}[/red]")
|
||||
console.print("颜色必须是hex格式,如: #FF5733")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# 创建分类管理器
|
||||
manager = resource_category_manager_v2
|
||||
|
||||
# 更新分类
|
||||
result = manager.update_category(
|
||||
category_id=category_id,
|
||||
title=title,
|
||||
ai_prompt=ai_prompt,
|
||||
color=color,
|
||||
is_active=active
|
||||
)
|
||||
|
||||
if not result:
|
||||
console.print(f"[red]❌ 未找到分类或更新失败: {category_id}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
console.print(f"\n✅ [bold green]分类更新成功![/bold green]")
|
||||
console.print(f"更新时间: {result['updated_at']}")
|
||||
|
||||
if verbose:
|
||||
# 显示更新后的分类信息
|
||||
category_info = f"""
|
||||
🆔 分类ID: {result['id']}
|
||||
📝 标题: {result['title']}
|
||||
🤖 AI提示词: {result['ai_prompt']}
|
||||
🎨 颜色: {result['color']}
|
||||
📅 创建时间: {result['created_at']}
|
||||
🔄 更新时间: {result['updated_at']}
|
||||
✅ 状态: {'启用' if result['is_active'] else '禁用'}
|
||||
"""
|
||||
|
||||
panel = Panel(category_info.strip(), title="更新后的分类信息", border_style="green")
|
||||
console.print(panel)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]❌ 更新分类失败: {str(e)}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
@category_app.command("delete")
|
||||
def delete_category(
|
||||
category_id: str = typer.Argument(..., help="分类ID"),
|
||||
force: bool = typer.Option(False, "--force", "-f", help="强制删除,不询问确认")
|
||||
):
|
||||
"""删除分类"""
|
||||
|
||||
try:
|
||||
console.print(f"🗑️ [bold red]删除分类[/bold red]")
|
||||
console.print(f"分类ID: {category_id}")
|
||||
|
||||
# 创建分类管理器
|
||||
manager = resource_category_manager_v2
|
||||
|
||||
# 获取分类信息
|
||||
category = manager.get_category_by_id(category_id)
|
||||
if not category:
|
||||
console.print(f"[red]❌ 未找到分类: {category_id}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
console.print(f"分类标题: {category.get('title', 'Unknown')}")
|
||||
console.print(f"AI提示词: {category.get('ai_prompt', 'Unknown')}")
|
||||
|
||||
# 确认删除
|
||||
if not force:
|
||||
confirm = typer.confirm("确定要删除这个分类吗?此操作不可恢复。")
|
||||
if not confirm:
|
||||
console.print("❌ 操作已取消")
|
||||
return
|
||||
|
||||
# 删除分类
|
||||
success = manager.delete_category(category_id)
|
||||
|
||||
if success:
|
||||
console.print(f"✅ [bold green]分类删除成功[/bold green]")
|
||||
else:
|
||||
console.print(f"[red]❌ 分类删除失败[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]❌ 删除分类失败: {str(e)}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
@category_app.command("search")
|
||||
def search_categories(
|
||||
keyword: str = typer.Argument(..., help="搜索关键词"),
|
||||
limit: int = typer.Option(10, "--limit", "-l", help="显示数量限制"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出")
|
||||
):
|
||||
"""搜索分类"""
|
||||
|
||||
try:
|
||||
console.print(f"🔍 [bold blue]搜索分类[/bold blue]")
|
||||
console.print(f"关键词: {keyword}")
|
||||
|
||||
# 创建分类管理器
|
||||
manager = resource_category_manager_v2
|
||||
|
||||
# 搜索分类
|
||||
categories = manager.search_categories(keyword)
|
||||
|
||||
if not categories:
|
||||
console.print(f"📭 没有找到匹配 '{keyword}' 的分类")
|
||||
return
|
||||
|
||||
console.print(f"✅ 找到 {len(categories)} 个匹配的分类")
|
||||
|
||||
# 限制显示数量
|
||||
if len(categories) > limit:
|
||||
categories = categories[:limit]
|
||||
console.print(f"📊 显示前 {limit} 个结果")
|
||||
|
||||
# 创建表格
|
||||
table = Table(title=f"搜索结果: {keyword}")
|
||||
table.add_column("ID", style="cyan", width=12)
|
||||
table.add_column("标题", style="green")
|
||||
table.add_column("AI提示词", style="yellow")
|
||||
table.add_column("颜色", style="magenta")
|
||||
table.add_column("状态", style="blue")
|
||||
|
||||
if verbose:
|
||||
table.add_column("创建时间", style="dim")
|
||||
|
||||
for category in categories:
|
||||
status = "✅ 启用" if category.get('is_active', True) else "❌ 禁用"
|
||||
|
||||
# 颜色显示
|
||||
color = category.get('color', '')
|
||||
color_display = f"[{color}]●[/{color}] {color}" if color else color
|
||||
|
||||
row = [
|
||||
category.get('id', '')[:12],
|
||||
category.get('title', ''),
|
||||
category.get('ai_prompt', '')[:30] + ('...' if len(category.get('ai_prompt', '')) > 30 else ''),
|
||||
color_display,
|
||||
status
|
||||
]
|
||||
|
||||
if verbose:
|
||||
created_at = category.get('created_at', '')
|
||||
if 'T' in created_at:
|
||||
created_at = created_at.split('T')[0] + ' ' + created_at.split('T')[1][:8]
|
||||
row.append(created_at)
|
||||
|
||||
table.add_row(*row)
|
||||
|
||||
console.print(table)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]❌ 搜索分类失败: {str(e)}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
@category_app.command("stats")
|
||||
def show_stats():
|
||||
"""显示分类统计信息"""
|
||||
|
||||
try:
|
||||
console.print(f"📊 [bold blue]分类统计信息[/bold blue]")
|
||||
|
||||
# 创建分类管理器
|
||||
manager = resource_category_manager_v2
|
||||
|
||||
# 获取所有分类
|
||||
categories = manager.get_all_categories()
|
||||
|
||||
if not categories:
|
||||
console.print("📭 没有分类")
|
||||
return
|
||||
|
||||
# 计算统计信息
|
||||
total_categories = len(categories)
|
||||
active_categories = len([cat for cat in categories if cat.get('is_active', True)])
|
||||
inactive_categories = total_categories - active_categories
|
||||
|
||||
# 颜色统计
|
||||
colors = {}
|
||||
for category in categories:
|
||||
color = category.get('color', '')
|
||||
if color:
|
||||
colors[color] = colors.get(color, 0) + 1
|
||||
|
||||
# 创建统计面板
|
||||
stats_content = f"""
|
||||
📈 [bold green]总体统计[/bold green]
|
||||
分类总数: {total_categories}
|
||||
启用分类: {active_categories}
|
||||
禁用分类: {inactive_categories}
|
||||
使用的颜色: {len(colors)}
|
||||
"""
|
||||
|
||||
stats_panel = Panel(stats_content.strip(), title="分类统计", border_style="green")
|
||||
console.print(stats_panel)
|
||||
|
||||
# 显示最近的分类
|
||||
recent_categories = sorted(categories, key=lambda x: x.get('created_at', ''), reverse=True)[:5]
|
||||
|
||||
if recent_categories:
|
||||
console.print(f"\n🕒 [bold green]最近创建的分类[/bold green]")
|
||||
|
||||
recent_table = Table()
|
||||
recent_table.add_column("标题", style="green")
|
||||
recent_table.add_column("颜色", style="magenta")
|
||||
recent_table.add_column("创建时间", style="dim")
|
||||
|
||||
for category in recent_categories:
|
||||
color = category.get('color', '')
|
||||
color_display = f"[{color}]●[/{color}] {color}" if color else color
|
||||
|
||||
created_at = category.get('created_at', '')
|
||||
if 'T' in created_at:
|
||||
created_at = created_at.split('T')[0] + ' ' + created_at.split('T')[1][:8]
|
||||
|
||||
recent_table.add_row(
|
||||
category.get('title', 'Unknown'),
|
||||
color_display,
|
||||
created_at
|
||||
)
|
||||
|
||||
console.print(recent_table)
|
||||
|
||||
# 显示颜色使用统计
|
||||
if colors:
|
||||
console.print(f"\n🎨 [bold green]颜色使用统计[/bold green]")
|
||||
|
||||
color_table = Table()
|
||||
color_table.add_column("颜色", style="magenta")
|
||||
color_table.add_column("使用次数", style="cyan")
|
||||
|
||||
for color, count in sorted(colors.items(), key=lambda x: x[1], reverse=True):
|
||||
color_display = f"[{color}]●[/{color}] {color}"
|
||||
color_table.add_row(color_display, str(count))
|
||||
|
||||
console.print(color_table)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]❌ 获取统计信息失败: {str(e)}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
category_app()
|
||||
|
|
@ -0,0 +1,328 @@
|
|||
"""
|
||||
素材分类管理 CLI 命令
|
||||
"""
|
||||
|
||||
import typer
|
||||
import json
|
||||
from typing import Optional, List, Dict, Any
|
||||
from dataclasses import asdict
|
||||
|
||||
from python_core.utils.logger import logger
|
||||
from python_core.utils.jsonrpc import create_response_handler
|
||||
from python_core.services.resource_category_manager_v2 import ResourceCategoryManagerV2
|
||||
|
||||
# 创建子应用
|
||||
resource_category_app = typer.Typer(help="素材分类管理命令")
|
||||
|
||||
|
||||
@resource_category_app.command("list")
|
||||
def list_categories(
|
||||
user_id: Optional[str] = typer.Option(None, "--user-id", help="用户ID"),
|
||||
include_cloud: bool = typer.Option(True, "--include-cloud/--no-include-cloud", help="是否包含云端分类"),
|
||||
limit: int = typer.Option(100, "--limit", help="最大返回数量"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
json_output: bool = typer.Option(True, "--json", help="JSON格式输出")
|
||||
):
|
||||
"""获取所有素材分类"""
|
||||
response = create_response_handler()
|
||||
try:
|
||||
manager = ResourceCategoryManagerV2(user_id=user_id or "default")
|
||||
categories = manager.get_all_categories(include_cloud=include_cloud)
|
||||
|
||||
# 限制返回数量
|
||||
if limit > 0:
|
||||
categories = categories[:limit]
|
||||
|
||||
response.success(categories)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取分类列表失败: {e}")
|
||||
response.error(-32603, f"获取分类列表失败: {str(e)}")
|
||||
|
||||
|
||||
@resource_category_app.command("get")
|
||||
def get_category(
|
||||
category_id: str = typer.Argument(..., help="分类ID"),
|
||||
user_id: Optional[str] = typer.Option(None, "--user-id", help="用户ID"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
json_output: bool = typer.Option(True, "--json", help="JSON格式输出")
|
||||
):
|
||||
"""根据ID获取素材分类"""
|
||||
response = create_response_handler()
|
||||
try:
|
||||
manager = ResourceCategoryManagerV2(user_id=user_id or "default")
|
||||
category = manager.get_category_by_id(category_id)
|
||||
|
||||
if category:
|
||||
response.success(category)
|
||||
else:
|
||||
response.error(-32603, f"分类不存在: {category_id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取分类失败: {e}")
|
||||
response.error(-32603, f"获取分类失败: {str(e)}")
|
||||
|
||||
|
||||
@resource_category_app.command("create")
|
||||
def create_category(
|
||||
title: str = typer.Argument(..., help="分类标题"),
|
||||
ai_prompt: Optional[str] = typer.Option("", "--ai-prompt", help="AI识别提示词"),
|
||||
color: Optional[str] = typer.Option("#FF5733", "--color", help="展示颜色"),
|
||||
is_cloud: bool = typer.Option(False, "--is-cloud", help="是否为云端分类"),
|
||||
user_id: Optional[str] = typer.Option(None, "--user-id", help="用户ID"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
json_output: bool = typer.Option(True, "--json", help="JSON格式输出")
|
||||
):
|
||||
"""创建新的素材分类"""
|
||||
response = create_response_handler()
|
||||
try:
|
||||
manager = ResourceCategoryManagerV2(user_id=user_id or "default")
|
||||
category = manager.create_category(
|
||||
title=title,
|
||||
ai_prompt=ai_prompt,
|
||||
color=color,
|
||||
is_cloud=is_cloud
|
||||
)
|
||||
|
||||
response.success(category)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"创建分类失败: {e}")
|
||||
response.error(-32603, f"创建分类失败: {str(e)}")
|
||||
|
||||
|
||||
@resource_category_app.command("update")
|
||||
def update_category(
|
||||
category_id: str = typer.Argument(..., help="分类ID"),
|
||||
title: Optional[str] = typer.Option(None, "--title", help="新标题"),
|
||||
ai_prompt: Optional[str] = typer.Option(None, "--ai-prompt", help="新AI提示词"),
|
||||
color: Optional[str] = typer.Option(None, "--color", help="新颜色"),
|
||||
activate: bool = typer.Option(False, "--activate", help="激活分类"),
|
||||
deactivate: bool = typer.Option(False, "--deactivate", help="停用分类"),
|
||||
user_id: Optional[str] = typer.Option(None, "--user-id", help="用户ID"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
json_output: bool = typer.Option(True, "--json", help="JSON格式输出")
|
||||
):
|
||||
"""更新素材分类"""
|
||||
response = create_response_handler()
|
||||
try:
|
||||
manager = ResourceCategoryManagerV2(user_id=user_id or "default")
|
||||
|
||||
# 处理激活/停用状态
|
||||
is_active = None
|
||||
if activate and deactivate:
|
||||
response.error(-32602, "不能同时指定激活和停用")
|
||||
return
|
||||
elif activate:
|
||||
is_active = True
|
||||
elif deactivate:
|
||||
is_active = False
|
||||
|
||||
category = manager.update_category(
|
||||
category_id=category_id,
|
||||
title=title,
|
||||
ai_prompt=ai_prompt,
|
||||
color=color,
|
||||
is_active=is_active
|
||||
)
|
||||
|
||||
if category:
|
||||
response.success(category)
|
||||
else:
|
||||
response.error(-32603, f"更新分类失败: {category_id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"更新分类失败: {e}")
|
||||
response.error(-32603, f"更新分类失败: {str(e)}")
|
||||
|
||||
|
||||
@resource_category_app.command("delete")
|
||||
def delete_category(
|
||||
category_id: str = typer.Argument(..., help="分类ID"),
|
||||
hard_delete: bool = typer.Option(True, "--hard-delete/--soft-delete", help="硬删除或软删除"),
|
||||
user_id: Optional[str] = typer.Option(None, "--user-id", help="用户ID"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
json_output: bool = typer.Option(True, "--json", help="JSON格式输出")
|
||||
):
|
||||
"""删除素材分类"""
|
||||
response = create_response_handler()
|
||||
try:
|
||||
manager = ResourceCategoryManagerV2(user_id=user_id or "default")
|
||||
success = manager.delete_category(category_id, hard_delete=hard_delete)
|
||||
|
||||
if success:
|
||||
delete_type = "硬删除" if hard_delete else "软删除"
|
||||
response.success({"message": f"分类{delete_type}成功", "category_id": category_id})
|
||||
else:
|
||||
response.error(-32603, f"删除分类失败: {category_id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"删除分类失败: {e}")
|
||||
response.error(-32603, f"删除分类失败: {str(e)}")
|
||||
|
||||
|
||||
@resource_category_app.command("search")
|
||||
def search_categories(
|
||||
query: str = typer.Argument(..., help="搜索关键词"),
|
||||
user_id: Optional[str] = typer.Option(None, "--user-id", help="用户ID"),
|
||||
include_cloud: bool = typer.Option(True, "--include-cloud/--no-include-cloud", help="是否包含云端分类"),
|
||||
limit: int = typer.Option(50, "--limit", help="最大返回数量"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
json_output: bool = typer.Option(True, "--json", help="JSON格式输出")
|
||||
):
|
||||
"""搜索素材分类"""
|
||||
response = create_response_handler()
|
||||
try:
|
||||
manager = ResourceCategoryManagerV2(user_id=user_id or "default")
|
||||
categories = manager.search_categories(query, include_cloud=include_cloud)
|
||||
|
||||
# 限制返回数量
|
||||
if limit > 0:
|
||||
categories = categories[:limit]
|
||||
|
||||
response.success(categories)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"搜索分类失败: {e}")
|
||||
response.error(-32603, f"搜索分类失败: {str(e)}")
|
||||
|
||||
|
||||
@resource_category_app.command("by-color")
|
||||
def get_categories_by_color(
|
||||
color: str = typer.Argument(..., help="颜色值(hex格式)"),
|
||||
user_id: Optional[str] = typer.Option(None, "--user-id", help="用户ID"),
|
||||
include_cloud: bool = typer.Option(True, "--include-cloud/--no-include-cloud", help="是否包含云端分类"),
|
||||
limit: int = typer.Option(50, "--limit", help="最大返回数量"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
json_output: bool = typer.Option(True, "--json", help="JSON格式输出")
|
||||
):
|
||||
"""根据颜色获取素材分类"""
|
||||
response = create_response_handler()
|
||||
try:
|
||||
manager = ResourceCategoryManagerV2(user_id=user_id or "default")
|
||||
categories = manager.get_categories_by_color(color, include_cloud=include_cloud)
|
||||
|
||||
# 限制返回数量
|
||||
if limit > 0:
|
||||
categories = categories[:limit]
|
||||
|
||||
response.success(categories)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"按颜色获取分类失败: {e}")
|
||||
response.error(-32603, f"按颜色获取分类失败: {str(e)}")
|
||||
|
||||
|
||||
@resource_category_app.command("cloud")
|
||||
def get_cloud_categories(
|
||||
limit: int = typer.Option(100, "--limit", help="最大返回数量"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
json_output: bool = typer.Option(True, "--json", help="JSON格式输出")
|
||||
):
|
||||
"""获取云端素材分类"""
|
||||
response = create_response_handler()
|
||||
try:
|
||||
manager = ResourceCategoryManagerV2()
|
||||
categories = manager.get_cloud_categories()
|
||||
|
||||
# 限制返回数量
|
||||
if limit > 0:
|
||||
categories = categories[:limit]
|
||||
|
||||
response.success(categories)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取云端分类失败: {e}")
|
||||
response.error(-32603, f"获取云端分类失败: {str(e)}")
|
||||
|
||||
|
||||
@resource_category_app.command("count")
|
||||
def get_category_count(
|
||||
user_id: Optional[str] = typer.Option(None, "--user-id", help="用户ID"),
|
||||
include_cloud: bool = typer.Option(True, "--include-cloud/--no-include-cloud", help="是否包含云端分类"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
json_output: bool = typer.Option(True, "--json", help="JSON格式输出")
|
||||
):
|
||||
"""获取素材分类数量"""
|
||||
response = create_response_handler()
|
||||
try:
|
||||
manager = ResourceCategoryManagerV2(user_id=user_id or "default")
|
||||
count = manager.get_category_count(include_cloud=include_cloud)
|
||||
|
||||
response.success({"count": count})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取分类数量失败: {e}")
|
||||
response.error(-32603, f"获取分类数量失败: {str(e)}")
|
||||
|
||||
|
||||
@resource_category_app.command("activate")
|
||||
def activate_category(
|
||||
category_id: str = typer.Argument(..., help="分类ID"),
|
||||
user_id: Optional[str] = typer.Option(None, "--user-id", help="用户ID"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
json_output: bool = typer.Option(True, "--json", help="JSON格式输出")
|
||||
):
|
||||
"""激活素材分类"""
|
||||
response = create_response_handler()
|
||||
try:
|
||||
manager = ResourceCategoryManagerV2(user_id=user_id or "default")
|
||||
success = manager.activate_category(category_id)
|
||||
|
||||
if success:
|
||||
response.success({"message": "分类激活成功", "category_id": category_id})
|
||||
else:
|
||||
response.error(-32603, f"激活分类失败: {category_id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"激活分类失败: {e}")
|
||||
response.error(-32603, f"激活分类失败: {str(e)}")
|
||||
|
||||
|
||||
@resource_category_app.command("deactivate")
|
||||
def deactivate_category(
|
||||
category_id: str = typer.Argument(..., help="分类ID"),
|
||||
user_id: Optional[str] = typer.Option(None, "--user-id", help="用户ID"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
json_output: bool = typer.Option(True, "--json", help="JSON格式输出")
|
||||
):
|
||||
"""停用素材分类"""
|
||||
response = create_response_handler()
|
||||
try:
|
||||
manager = ResourceCategoryManagerV2(user_id=user_id or "default")
|
||||
success = manager.deactivate_category(category_id)
|
||||
|
||||
if success:
|
||||
response.success({"message": "分类停用成功", "category_id": category_id})
|
||||
else:
|
||||
response.error(-32603, f"停用分类失败: {category_id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"停用分类失败: {e}")
|
||||
response.error(-32603, f"停用分类失败: {str(e)}")
|
||||
|
||||
|
||||
@resource_category_app.command("batch-create")
|
||||
def batch_create_categories(
|
||||
data: str = typer.Option(..., "--data", help="批量创建数据(JSON格式)"),
|
||||
user_id: Optional[str] = typer.Option(None, "--user-id", help="用户ID"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出"),
|
||||
json_output: bool = typer.Option(True, "--json", help="JSON格式输出")
|
||||
):
|
||||
"""批量创建素材分类"""
|
||||
response = create_response_handler()
|
||||
try:
|
||||
# 解析批量数据
|
||||
categories_data = json.loads(data)
|
||||
|
||||
manager = ResourceCategoryManagerV2(user_id=user_id or "default")
|
||||
result = manager.batch_create_categories(categories_data)
|
||||
|
||||
response.success(result)
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"解析批量数据失败: {e}")
|
||||
response.error(-32602, f"解析批量数据失败: {str(e)}")
|
||||
except Exception as e:
|
||||
logger.error(f"批量创建分类失败: {e}")
|
||||
response.error(-32603, f"批量创建分类失败: {str(e)}")
|
||||
|
|
@ -1,7 +1,107 @@
|
|||
/**
|
||||
* ResourceCategory 命令封装
|
||||
* 统一使用 execute_python_cli_command 模式
|
||||
*/
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri::{command, AppHandle};
|
||||
use crate::python_executor::execute_python_command;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ResourceCategoryResponse {
|
||||
pub success: bool,
|
||||
pub message: String,
|
||||
pub data: Option<serde_json::Value>,
|
||||
pub output: Option<String>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CategoryListRequest {
|
||||
pub user_id: Option<String>,
|
||||
pub include_cloud: Option<bool>,
|
||||
pub limit: Option<i32>,
|
||||
pub verbose: Option<bool>,
|
||||
pub json_output: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CategoryGetRequest {
|
||||
pub category_id: String,
|
||||
pub user_id: Option<String>,
|
||||
pub verbose: Option<bool>,
|
||||
pub json_output: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CategoryCreateRequest {
|
||||
pub title: String,
|
||||
pub ai_prompt: Option<String>,
|
||||
pub color: Option<String>,
|
||||
pub is_cloud: Option<bool>,
|
||||
pub user_id: Option<String>,
|
||||
pub verbose: Option<bool>,
|
||||
pub json_output: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CategoryUpdateRequest {
|
||||
pub category_id: String,
|
||||
pub title: Option<String>,
|
||||
pub ai_prompt: Option<String>,
|
||||
pub color: Option<String>,
|
||||
pub is_active: Option<bool>,
|
||||
pub user_id: Option<String>,
|
||||
pub verbose: Option<bool>,
|
||||
pub json_output: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CategoryDeleteRequest {
|
||||
pub category_id: String,
|
||||
pub hard_delete: Option<bool>,
|
||||
pub user_id: Option<String>,
|
||||
pub verbose: Option<bool>,
|
||||
pub json_output: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CategorySearchRequest {
|
||||
pub query: String,
|
||||
pub user_id: Option<String>,
|
||||
pub include_cloud: Option<bool>,
|
||||
pub limit: Option<i32>,
|
||||
pub verbose: Option<bool>,
|
||||
pub json_output: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CategoryColorRequest {
|
||||
pub color: String,
|
||||
pub user_id: Option<String>,
|
||||
pub include_cloud: Option<bool>,
|
||||
pub limit: Option<i32>,
|
||||
pub verbose: Option<bool>,
|
||||
pub json_output: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CategoryBatchCreateRequest {
|
||||
pub categories: Vec<CategoryBatchItem>,
|
||||
pub user_id: Option<String>,
|
||||
pub verbose: Option<bool>,
|
||||
pub json_output: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CategoryBatchItem {
|
||||
pub title: String,
|
||||
pub ai_prompt: Option<String>,
|
||||
pub color: Option<String>,
|
||||
pub is_cloud: Option<bool>,
|
||||
}
|
||||
|
||||
// 保持向后兼容的旧结构体
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateCategoryRequest {
|
||||
pub title: String,
|
||||
|
|
@ -17,7 +117,85 @@ pub struct UpdateCategoryRequest {
|
|||
pub is_active: Option<bool>,
|
||||
}
|
||||
|
||||
/// 获取所有资源分类
|
||||
/// 执行Python CLI命令的通用函数(统一模式)
|
||||
async fn execute_python_cli_command(
|
||||
app: AppHandle,
|
||||
command_args: Vec<String>,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
// 构建完整的命令参数
|
||||
let mut args = vec!["-m".to_string(), "python_core.cli".to_string()];
|
||||
args.extend(command_args);
|
||||
|
||||
println!("Executing ResourceCategory CLI: python {}", args.join(" "));
|
||||
|
||||
match execute_python_command(app, &args, None).await {
|
||||
Ok(output) => {
|
||||
// 尝试解析JSON响应
|
||||
if let Ok(json_value) = serde_json::from_str::<serde_json::Value>(&output) {
|
||||
Ok(ResourceCategoryResponse {
|
||||
success: true,
|
||||
message: "素材分类命令执行成功".to_string(),
|
||||
data: Some(json_value),
|
||||
output: Some(output),
|
||||
error: None,
|
||||
})
|
||||
} else {
|
||||
// 如果不是JSON,直接返回文本输出
|
||||
Ok(ResourceCategoryResponse {
|
||||
success: true,
|
||||
message: "素材分类命令执行成功".to_string(),
|
||||
data: None,
|
||||
output: Some(output),
|
||||
error: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
Ok(ResourceCategoryResponse {
|
||||
success: false,
|
||||
message: "素材分类命令执行失败".to_string(),
|
||||
data: None,
|
||||
output: None,
|
||||
error: Some(error),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取所有资源分类(新版本,使用CLI模式)
|
||||
#[command]
|
||||
pub async fn get_all_resource_categories_cli(
|
||||
app: AppHandle,
|
||||
request: CategoryListRequest,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
let mut args = vec!["resource-category".to_string(), "list".to_string()];
|
||||
|
||||
if let Some(user_id) = request.user_id {
|
||||
args.push("--user-id".to_string());
|
||||
args.push(user_id);
|
||||
}
|
||||
|
||||
if request.include_cloud.unwrap_or(true) {
|
||||
args.push("--include-cloud".to_string());
|
||||
}
|
||||
|
||||
if let Some(limit) = request.limit {
|
||||
args.push("--limit".to_string());
|
||||
args.push(limit.to_string());
|
||||
}
|
||||
|
||||
if request.verbose.unwrap_or(false) {
|
||||
args.push("--verbose".to_string());
|
||||
}
|
||||
|
||||
if request.json_output.unwrap_or(true) {
|
||||
args.push("--json".to_string());
|
||||
}
|
||||
|
||||
execute_python_cli_command(app, args).await
|
||||
}
|
||||
|
||||
/// 获取所有资源分类(保持向后兼容)
|
||||
#[command]
|
||||
pub async fn get_all_resource_categories(app: AppHandle) -> Result<String, String> {
|
||||
let args = vec![
|
||||
|
|
@ -29,7 +207,31 @@ pub async fn get_all_resource_categories(app: AppHandle) -> Result<String, Strin
|
|||
execute_python_command(app, &args, None).await
|
||||
}
|
||||
|
||||
/// 根据ID获取资源分类
|
||||
/// 根据ID获取资源分类(新版本,使用CLI模式)
|
||||
#[command]
|
||||
pub async fn get_resource_category_by_id_cli(
|
||||
app: AppHandle,
|
||||
request: CategoryGetRequest,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
let mut args = vec!["resource-category".to_string(), "get".to_string(), request.category_id];
|
||||
|
||||
if let Some(user_id) = request.user_id {
|
||||
args.push("--user-id".to_string());
|
||||
args.push(user_id);
|
||||
}
|
||||
|
||||
if request.verbose.unwrap_or(false) {
|
||||
args.push("--verbose".to_string());
|
||||
}
|
||||
|
||||
if request.json_output.unwrap_or(true) {
|
||||
args.push("--json".to_string());
|
||||
}
|
||||
|
||||
execute_python_cli_command(app, args).await
|
||||
}
|
||||
|
||||
/// 根据ID获取资源分类(保持向后兼容)
|
||||
#[command]
|
||||
pub async fn get_resource_category_by_id(app: AppHandle, category_id: String) -> Result<String, String> {
|
||||
let args = vec![
|
||||
|
|
@ -42,7 +244,45 @@ pub async fn get_resource_category_by_id(app: AppHandle, category_id: String) ->
|
|||
execute_python_command(app, &args, None).await
|
||||
}
|
||||
|
||||
/// 创建新的资源分类
|
||||
/// 创建新的资源分类(新版本,使用CLI模式)
|
||||
#[command]
|
||||
pub async fn create_resource_category_cli(
|
||||
app: AppHandle,
|
||||
request: CategoryCreateRequest,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
let mut args = vec!["resource-category".to_string(), "create".to_string(), request.title];
|
||||
|
||||
if let Some(ai_prompt) = request.ai_prompt {
|
||||
args.push("--ai-prompt".to_string());
|
||||
args.push(ai_prompt);
|
||||
}
|
||||
|
||||
if let Some(color) = request.color {
|
||||
args.push("--color".to_string());
|
||||
args.push(color);
|
||||
}
|
||||
|
||||
if request.is_cloud.unwrap_or(false) {
|
||||
args.push("--is-cloud".to_string());
|
||||
}
|
||||
|
||||
if let Some(user_id) = request.user_id {
|
||||
args.push("--user-id".to_string());
|
||||
args.push(user_id);
|
||||
}
|
||||
|
||||
if request.verbose.unwrap_or(false) {
|
||||
args.push("--verbose".to_string());
|
||||
}
|
||||
|
||||
if request.json_output.unwrap_or(true) {
|
||||
args.push("--json".to_string());
|
||||
}
|
||||
|
||||
execute_python_cli_command(app, args).await
|
||||
}
|
||||
|
||||
/// 创建新的资源分类(保持向后兼容)
|
||||
#[command]
|
||||
pub async fn create_resource_category(app: AppHandle, request: CreateCategoryRequest) -> Result<String, String> {
|
||||
let args = vec![
|
||||
|
|
@ -57,7 +297,54 @@ pub async fn create_resource_category(app: AppHandle, request: CreateCategoryReq
|
|||
execute_python_command(app, &args, None).await
|
||||
}
|
||||
|
||||
/// 更新资源分类
|
||||
/// 更新资源分类(新版本,使用CLI模式)
|
||||
#[command]
|
||||
pub async fn update_resource_category_cli(
|
||||
app: AppHandle,
|
||||
request: CategoryUpdateRequest,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
let mut args = vec!["resource-category".to_string(), "update".to_string(), request.category_id];
|
||||
|
||||
if let Some(title) = request.title {
|
||||
args.push("--title".to_string());
|
||||
args.push(title);
|
||||
}
|
||||
|
||||
if let Some(ai_prompt) = request.ai_prompt {
|
||||
args.push("--ai-prompt".to_string());
|
||||
args.push(ai_prompt);
|
||||
}
|
||||
|
||||
if let Some(color) = request.color {
|
||||
args.push("--color".to_string());
|
||||
args.push(color);
|
||||
}
|
||||
|
||||
if let Some(is_active) = request.is_active {
|
||||
if is_active {
|
||||
args.push("--activate".to_string());
|
||||
} else {
|
||||
args.push("--deactivate".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(user_id) = request.user_id {
|
||||
args.push("--user-id".to_string());
|
||||
args.push(user_id);
|
||||
}
|
||||
|
||||
if request.verbose.unwrap_or(false) {
|
||||
args.push("--verbose".to_string());
|
||||
}
|
||||
|
||||
if request.json_output.unwrap_or(true) {
|
||||
args.push("--json".to_string());
|
||||
}
|
||||
|
||||
execute_python_cli_command(app, args).await
|
||||
}
|
||||
|
||||
/// 更新资源分类(保持向后兼容)
|
||||
#[command]
|
||||
pub async fn update_resource_category(
|
||||
app: AppHandle,
|
||||
|
|
@ -78,7 +365,35 @@ pub async fn update_resource_category(
|
|||
execute_python_command(app, &args, None).await
|
||||
}
|
||||
|
||||
/// 删除资源分类
|
||||
/// 删除资源分类(新版本,使用CLI模式)
|
||||
#[command]
|
||||
pub async fn delete_resource_category_cli(
|
||||
app: AppHandle,
|
||||
request: CategoryDeleteRequest,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
let mut args = vec!["resource-category".to_string(), "delete".to_string(), request.category_id];
|
||||
|
||||
if request.hard_delete.unwrap_or(true) {
|
||||
args.push("--hard-delete".to_string());
|
||||
}
|
||||
|
||||
if let Some(user_id) = request.user_id {
|
||||
args.push("--user-id".to_string());
|
||||
args.push(user_id);
|
||||
}
|
||||
|
||||
if request.verbose.unwrap_or(false) {
|
||||
args.push("--verbose".to_string());
|
||||
}
|
||||
|
||||
if request.json_output.unwrap_or(true) {
|
||||
args.push("--json".to_string());
|
||||
}
|
||||
|
||||
execute_python_cli_command(app, args).await
|
||||
}
|
||||
|
||||
/// 删除资源分类(保持向后兼容)
|
||||
#[command]
|
||||
pub async fn delete_resource_category(app: AppHandle, category_id: String) -> Result<String, String> {
|
||||
let args = vec![
|
||||
|
|
@ -91,7 +406,40 @@ pub async fn delete_resource_category(app: AppHandle, category_id: String) -> Re
|
|||
execute_python_command(app, &args, None).await
|
||||
}
|
||||
|
||||
/// 搜索资源分类
|
||||
/// 搜索资源分类(新版本,使用CLI模式)
|
||||
#[command]
|
||||
pub async fn search_resource_categories_cli(
|
||||
app: AppHandle,
|
||||
request: CategorySearchRequest,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
let mut args = vec!["resource-category".to_string(), "search".to_string(), request.query];
|
||||
|
||||
if let Some(user_id) = request.user_id {
|
||||
args.push("--user-id".to_string());
|
||||
args.push(user_id);
|
||||
}
|
||||
|
||||
if request.include_cloud.unwrap_or(true) {
|
||||
args.push("--include-cloud".to_string());
|
||||
}
|
||||
|
||||
if let Some(limit) = request.limit {
|
||||
args.push("--limit".to_string());
|
||||
args.push(limit.to_string());
|
||||
}
|
||||
|
||||
if request.verbose.unwrap_or(false) {
|
||||
args.push("--verbose".to_string());
|
||||
}
|
||||
|
||||
if request.json_output.unwrap_or(true) {
|
||||
args.push("--json".to_string());
|
||||
}
|
||||
|
||||
execute_python_cli_command(app, args).await
|
||||
}
|
||||
|
||||
/// 搜索资源分类(保持向后兼容)
|
||||
#[command]
|
||||
pub async fn search_resource_categories(app: AppHandle, keyword: String) -> Result<String, String> {
|
||||
let args = vec![
|
||||
|
|
@ -103,3 +451,168 @@ pub async fn search_resource_categories(app: AppHandle, keyword: String) -> Resu
|
|||
|
||||
execute_python_command(app, &args, None).await
|
||||
}
|
||||
|
||||
/// 按颜色获取资源分类(新版本,使用CLI模式)
|
||||
#[command]
|
||||
pub async fn get_resource_categories_by_color_cli(
|
||||
app: AppHandle,
|
||||
request: CategoryColorRequest,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
let mut args = vec!["resource-category".to_string(), "by-color".to_string(), request.color];
|
||||
|
||||
if let Some(user_id) = request.user_id {
|
||||
args.push("--user-id".to_string());
|
||||
args.push(user_id);
|
||||
}
|
||||
|
||||
if request.include_cloud.unwrap_or(true) {
|
||||
args.push("--include-cloud".to_string());
|
||||
}
|
||||
|
||||
if let Some(limit) = request.limit {
|
||||
args.push("--limit".to_string());
|
||||
args.push(limit.to_string());
|
||||
}
|
||||
|
||||
if request.verbose.unwrap_or(false) {
|
||||
args.push("--verbose".to_string());
|
||||
}
|
||||
|
||||
if request.json_output.unwrap_or(true) {
|
||||
args.push("--json".to_string());
|
||||
}
|
||||
|
||||
execute_python_cli_command(app, args).await
|
||||
}
|
||||
|
||||
/// 获取云端资源分类(新版本,使用CLI模式)
|
||||
#[command]
|
||||
pub async fn get_cloud_resource_categories_cli(
|
||||
app: AppHandle,
|
||||
request: CategoryListRequest,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
let mut args = vec!["resource-category".to_string(), "cloud".to_string()];
|
||||
|
||||
if let Some(limit) = request.limit {
|
||||
args.push("--limit".to_string());
|
||||
args.push(limit.to_string());
|
||||
}
|
||||
|
||||
if request.verbose.unwrap_or(false) {
|
||||
args.push("--verbose".to_string());
|
||||
}
|
||||
|
||||
if request.json_output.unwrap_or(true) {
|
||||
args.push("--json".to_string());
|
||||
}
|
||||
|
||||
execute_python_cli_command(app, args).await
|
||||
}
|
||||
|
||||
/// 批量创建资源分类(新版本,使用CLI模式)
|
||||
#[command]
|
||||
pub async fn batch_create_resource_categories_cli(
|
||||
app: AppHandle,
|
||||
request: CategoryBatchCreateRequest,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
// 将批量数据序列化为JSON
|
||||
let categories_json = serde_json::to_string(&request.categories)
|
||||
.map_err(|e| format!("Failed to serialize categories: {}", e))?;
|
||||
|
||||
let mut args = vec!["resource-category".to_string(), "batch-create".to_string()];
|
||||
|
||||
// 通过临时文件传递批量数据
|
||||
args.push("--data".to_string());
|
||||
args.push(categories_json);
|
||||
|
||||
if let Some(user_id) = request.user_id {
|
||||
args.push("--user-id".to_string());
|
||||
args.push(user_id);
|
||||
}
|
||||
|
||||
if request.verbose.unwrap_or(false) {
|
||||
args.push("--verbose".to_string());
|
||||
}
|
||||
|
||||
if request.json_output.unwrap_or(true) {
|
||||
args.push("--json".to_string());
|
||||
}
|
||||
|
||||
execute_python_cli_command(app, args).await
|
||||
}
|
||||
|
||||
/// 获取资源分类数量(新版本,使用CLI模式)
|
||||
#[command]
|
||||
pub async fn get_resource_category_count_cli(
|
||||
app: AppHandle,
|
||||
request: CategoryListRequest,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
let mut args = vec!["resource-category".to_string(), "count".to_string()];
|
||||
|
||||
if let Some(user_id) = request.user_id {
|
||||
args.push("--user-id".to_string());
|
||||
args.push(user_id);
|
||||
}
|
||||
|
||||
if request.include_cloud.unwrap_or(true) {
|
||||
args.push("--include-cloud".to_string());
|
||||
}
|
||||
|
||||
if request.verbose.unwrap_or(false) {
|
||||
args.push("--verbose".to_string());
|
||||
}
|
||||
|
||||
if request.json_output.unwrap_or(true) {
|
||||
args.push("--json".to_string());
|
||||
}
|
||||
|
||||
execute_python_cli_command(app, args).await
|
||||
}
|
||||
|
||||
/// 激活资源分类(新版本,使用CLI模式)
|
||||
#[command]
|
||||
pub async fn activate_resource_category_cli(
|
||||
app: AppHandle,
|
||||
request: CategoryGetRequest,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
let mut args = vec!["resource-category".to_string(), "activate".to_string(), request.category_id];
|
||||
|
||||
if let Some(user_id) = request.user_id {
|
||||
args.push("--user-id".to_string());
|
||||
args.push(user_id);
|
||||
}
|
||||
|
||||
if request.verbose.unwrap_or(false) {
|
||||
args.push("--verbose".to_string());
|
||||
}
|
||||
|
||||
if request.json_output.unwrap_or(true) {
|
||||
args.push("--json".to_string());
|
||||
}
|
||||
|
||||
execute_python_cli_command(app, args).await
|
||||
}
|
||||
|
||||
/// 停用资源分类(新版本,使用CLI模式)
|
||||
#[command]
|
||||
pub async fn deactivate_resource_category_cli(
|
||||
app: AppHandle,
|
||||
request: CategoryGetRequest,
|
||||
) -> Result<ResourceCategoryResponse, String> {
|
||||
let mut args = vec!["resource-category".to_string(), "deactivate".to_string(), request.category_id];
|
||||
|
||||
if let Some(user_id) = request.user_id {
|
||||
args.push("--user-id".to_string());
|
||||
args.push(user_id);
|
||||
}
|
||||
|
||||
if request.verbose.unwrap_or(false) {
|
||||
args.push("--verbose".to_string());
|
||||
}
|
||||
|
||||
if request.json_output.unwrap_or(true) {
|
||||
args.push("--json".to_string());
|
||||
}
|
||||
|
||||
execute_python_cli_command(app, args).await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,11 +46,23 @@ pub fn run() {
|
|||
commands::get_template_detail_cli,
|
||||
commands::delete_template,
|
||||
commands::get_all_resource_categories,
|
||||
commands::get_all_resource_categories_cli,
|
||||
commands::get_resource_category_by_id,
|
||||
commands::get_resource_category_by_id_cli,
|
||||
commands::create_resource_category,
|
||||
commands::create_resource_category_cli,
|
||||
commands::update_resource_category,
|
||||
commands::update_resource_category_cli,
|
||||
commands::delete_resource_category,
|
||||
commands::delete_resource_category_cli,
|
||||
commands::search_resource_categories,
|
||||
commands::search_resource_categories_cli,
|
||||
commands::get_resource_categories_by_color_cli,
|
||||
commands::get_cloud_resource_categories_cli,
|
||||
commands::batch_create_resource_categories_cli,
|
||||
commands::get_resource_category_count_cli,
|
||||
commands::activate_resource_category_cli,
|
||||
commands::deactivate_resource_category_cli,
|
||||
commands::get_all_projects,
|
||||
commands::get_project_by_id,
|
||||
commands::create_project,
|
||||
|
|
|
|||
Loading…
Reference in New Issue