mixvideo-v2/OPTIMIZATION_PLAN.md

9.2 KiB

模特管理功能优化计划

1. 性能优化 (高优先级)

1.1 数据库连接池优化

// 实现连接池管理
use r2d2::{Pool, PooledConnection};
use r2d2_sqlite::SqliteConnectionManager;

pub struct DatabasePool {
    pool: Pool<SqliteConnectionManager>,
}

impl DatabasePool {
    pub fn new(database_url: &str) -> Result<Self> {
        let manager = SqliteConnectionManager::file(database_url);
        let pool = Pool::new(manager)?;
        Ok(Self { pool })
    }
    
    pub fn get_connection(&self) -> Result<PooledConnection<SqliteConnectionManager>> {
        self.pool.get().map_err(|e| anyhow!("获取数据库连接失败: {}", e))
    }
}

1.2 异步查询优化

// 使用异步数据库操作
use tokio_rusqlite::{Connection, Result};

impl ModelRepository {
    pub async fn get_all_async(&self) -> Result<Vec<Model>> {
        // 异步查询实现
        let models = self.query_models_async().await?;
        
        // 并行加载照片
        let mut tasks = Vec::new();
        for model in models {
            let repo = self.clone();
            let model_id = model.id.clone();
            tasks.push(tokio::spawn(async move {
                repo.get_photos_async(&model_id).await
            }));
        }
        
        // 等待所有照片加载完成
        let photos_results = futures::future::join_all(tasks).await;
        // 组装结果...
    }
}

1.3 缓存机制

use std::collections::HashMap;
use std::sync::{Arc, RwLock};

pub struct ModelCache {
    models: Arc<RwLock<HashMap<String, Model>>>,
    photos: Arc<RwLock<HashMap<String, Vec<ModelPhoto>>>>,
}

impl ModelCache {
    pub fn get_model(&self, id: &str) -> Option<Model> {
        self.models.read().unwrap().get(id).cloned()
    }
    
    pub fn invalidate_model(&self, id: &str) {
        self.models.write().unwrap().remove(id);
        self.photos.write().unwrap().remove(id);
    }
}

2. 错误处理优化 (中优先级)

2.1 自定义错误类型

use thiserror::Error;

#[derive(Error, Debug)]
pub enum ModelError {
    #[error("模特不存在: {id}")]
    NotFound { id: String },
    
    #[error("数据验证失败: {message}")]
    ValidationError { message: String },
    
    #[error("数据库操作失败: {source}")]
    DatabaseError { source: rusqlite::Error },
    
    #[error("文件操作失败: {path}")]
    FileError { path: String },
}

2.2 用户友好的错误信息

// 前端错误处理
export class ModelErrorHandler {
    static handleError(error: string): string {
        if (error.includes('NotFound')) {
            return '找不到指定的模特,可能已被删除';
        }
        if (error.includes('ValidationError')) {
            return '输入的信息格式不正确,请检查后重试';
        }
        if (error.includes('DatabaseError')) {
            return '数据保存失败,请稍后重试';
        }
        return '操作失败,请联系技术支持';
    }
}

3. 测试体系建设 (高优先级)

3.1 单元测试

#[cfg(test)]
mod tests {
    use super::*;
    
    #[tokio::test]
    async fn test_create_model() {
        let repo = create_test_repository().await;
        let request = CreateModelRequest {
            name: "测试模特".to_string(),
            gender: Gender::Female,
            // ...
        };
        
        let result = ModelService::create_model(&repo, request).await;
        assert!(result.is_ok());
        
        let model = result.unwrap();
        assert_eq!(model.name, "测试模特");
    }
    
    #[tokio::test]
    async fn test_model_validation() {
        let mut model = Model::new("".to_string(), Gender::Female);
        let result = model.validate();
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("姓名不能为空"));
    }
}

3.2 集成测试

// tests/integration_test.rs
use mixvideo_desktop::*;

#[tokio::test]
async fn test_model_crud_workflow() {
    let app_state = setup_test_app().await;
    
    // 测试创建
    let create_result = create_model(app_state.clone(), test_model_data()).await;
    assert!(create_result.is_ok());
    
    // 测试读取
    let model_id = create_result.unwrap().id;
    let get_result = get_model_by_id(app_state.clone(), model_id.clone()).await;
    assert!(get_result.is_ok());
    
    // 测试更新
    let update_result = update_model(app_state.clone(), model_id.clone(), update_data()).await;
    assert!(update_result.is_ok());
    
    // 测试删除
    let delete_result = delete_model(app_state.clone(), model_id).await;
    assert!(delete_result.is_ok());
}

3.3 前端测试

// src/components/__tests__/ModelList.test.tsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { ModelList } from '../ModelList';
import { modelService } from '../../services/modelService';

jest.mock('../../services/modelService');

describe('ModelList', () => {
    beforeEach(() => {
        jest.clearAllMocks();
    });
    
    test('应该正确加载和显示模特列表', async () => {
        const mockModels = [
            { id: '1', name: '测试模特1', gender: 'Female' },
            { id: '2', name: '测试模特2', gender: 'Male' }
        ];
        
        (modelService.getAllModels as jest.Mock).mockResolvedValue(mockModels);
        
        render(<ModelList />);
        
        await waitFor(() => {
            expect(screen.getByText('测试模特1')).toBeInTheDocument();
            expect(screen.getByText('测试模特2')).toBeInTheDocument();
        });
    });
    
    test('应该正确处理删除操作', async () => {
        // 测试删除功能...
    });
});

4. 架构优化 (中优先级)

4.1 事件驱动架构

use tokio::sync::broadcast;

#[derive(Clone, Debug)]
pub enum ModelEvent {
    Created(Model),
    Updated(Model),
    Deleted(String),
}

pub struct EventBus {
    sender: broadcast::Sender<ModelEvent>,
}

impl EventBus {
    pub fn publish(&self, event: ModelEvent) {
        let _ = self.sender.send(event);
    }
    
    pub fn subscribe(&self) -> broadcast::Receiver<ModelEvent> {
        self.sender.subscribe()
    }
}

4.2 分页和虚拟滚动

// 前端分页优化
export interface PaginationParams {
    page: number;
    pageSize: number;
    sortBy?: string;
    sortOrder?: 'asc' | 'desc';
}

export class ModelListManager {
    private cache = new Map<string, Model[]>();
    
    async loadPage(params: PaginationParams): Promise<PaginatedResult<Model>> {
        const cacheKey = this.getCacheKey(params);
        
        if (this.cache.has(cacheKey)) {
            return this.cache.get(cacheKey)!;
        }
        
        const result = await modelService.getModelsPaginated(params);
        this.cache.set(cacheKey, result);
        
        return result;
    }
}

5. 用户体验优化 (中优先级)

5.1 加载状态优化

// 骨架屏组件
export const ModelCardSkeleton: React.FC = () => (
    <div className="animate-pulse">
        <div className="bg-gray-300 h-48 w-full rounded-t-lg"></div>
        <div className="p-4">
            <div className="bg-gray-300 h-4 w-3/4 mb-2 rounded"></div>
            <div className="bg-gray-300 h-3 w-1/2 rounded"></div>
        </div>
    </div>
);

5.2 离线支持

// Service Worker for offline support
export class OfflineManager {
    private cache = new Map<string, any>();
    
    async getModels(): Promise<Model[]> {
        try {
            const models = await modelService.getAllModels();
            this.cache.set('models', models);
            return models;
        } catch (error) {
            // 返回缓存数据
            return this.cache.get('models') || [];
        }
    }
}

6. 安全性增强 (高优先级)

6.1 输入验证

use validator::{Validate, ValidationError};

#[derive(Validate)]
pub struct CreateModelRequest {
    #[validate(length(min = 1, max = 50, message = "姓名长度必须在1-50字符之间"))]
    pub name: String,
    
    #[validate(email(message = "邮箱格式不正确"))]
    pub email: Option<String>,
    
    #[validate(range(min = 1, max = 100, message = "年龄必须在1-100之间"))]
    pub age: Option<u32>,
}

6.2 权限控制

pub enum Permission {
    ReadModel,
    WriteModel,
    DeleteModel,
    ManagePhotos,
}

pub struct PermissionChecker;

impl PermissionChecker {
    pub fn check_permission(user_role: &str, permission: Permission) -> bool {
        match (user_role, permission) {
            ("admin", _) => true,
            ("editor", Permission::DeleteModel) => false,
            ("editor", _) => true,
            ("viewer", Permission::ReadModel) => true,
            _ => false,
        }
    }
}

实施优先级

  1. 立即实施 (本周)

    • 移除调试日志
    • 添加基本单元测试
    • 优化错误信息
  2. 短期实施 (2周内)

    • 实现连接池
    • 添加缓存机制
    • 完善测试覆盖
  3. 中期实施 (1个月内)

    • 事件驱动架构
    • 分页优化
    • 离线支持
  4. 长期实施 (3个月内)

    • 完整的权限系统
    • 性能监控
    • 用户体验优化