feat: 添加二维码生成功能并简化架构
- 新增二维码生成服务层,支持自定义内容和配置 - 移除 Platform 抽象层,简化为三层架构 - 更新开发文档和架构说明 - 添加前端二维码生成页面和状态管理
This commit is contained in:
292
docs/开发指南.md
292
docs/开发指南.md
@@ -13,7 +13,7 @@
|
||||
|
||||
## 架构概述
|
||||
|
||||
### 四层架构
|
||||
### 三层架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
@@ -25,10 +25,6 @@
|
||||
│ 职责:业务逻辑、流程编排、状态管理 │
|
||||
│ 文件:src/services/*.rs │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ Platform 层 │
|
||||
│ 职责:平台差异抽象、统一接口 │
|
||||
│ 文件:src/platforms/*.rs │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ Utils 层 │
|
||||
│ 职责:纯算法、无状态工具函数 │
|
||||
│ 文件:src/utils/*.rs │
|
||||
@@ -38,9 +34,9 @@
|
||||
### 依赖原则
|
||||
|
||||
- **单向依赖**:上层依赖下层,下层不依赖上层
|
||||
- **依赖链**:Command → Service → Platform → Utils
|
||||
- **依赖链**:Command → Service → Utils
|
||||
- **Utils 完全独立**:只依赖标准库和 models
|
||||
- **Platform 通过 trait 解耦**:Service 层通过 trait 调用,不关心具体实现
|
||||
- **Service 可以直接调用 Windows API**
|
||||
|
||||
---
|
||||
|
||||
@@ -53,8 +49,7 @@
|
||||
1. **功能描述**:这个功能做什么?
|
||||
2. **用户交互**:用户如何触发这个功能?
|
||||
3. **输入输出**:需要什么参数?返回什么结果?
|
||||
4. **平台差异**:不同平台是否需要不同实现?
|
||||
5. **错误场景**:哪些情况下会出错?如何处理?
|
||||
4. **错误场景**:哪些情况下会出错?如何处理?
|
||||
|
||||
### 步骤 2:设计数据模型(如需要)
|
||||
|
||||
@@ -140,88 +135,7 @@ mod tests {
|
||||
- ✅ 包含单元测试
|
||||
- ✅ 不依赖任何外部状态
|
||||
|
||||
### 步骤 4:定义平台抽象(如果需要平台特定实现)
|
||||
|
||||
如果功能需要访问平台 API,定义 trait:
|
||||
|
||||
```rust
|
||||
// src/platforms/new_feature.rs
|
||||
|
||||
use crate::error::AppResult;
|
||||
|
||||
/// 新功能平台抽象 trait
|
||||
pub trait NewFeatureAccessor {
|
||||
/// 执行平台特定的操作
|
||||
///
|
||||
/// # 参数
|
||||
///
|
||||
/// * `param1` - 参数 1 说明
|
||||
///
|
||||
/// # 返回
|
||||
///
|
||||
/// 返回操作结果
|
||||
///
|
||||
/// # 错误
|
||||
///
|
||||
/// 操作失败时返回错误
|
||||
fn execute_platform_operation(&self, param1: &str) -> AppResult<String>;
|
||||
}
|
||||
|
||||
/// 平台特定实现类型别名
|
||||
#[cfg(windows)]
|
||||
pub type PlatformNewFeature = crate::platforms::windows::new_feature_impl::WindowsNewFeature;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub type PlatformNewFeature = crate::platforms::windows::new_feature_impl::DummyNewFeature;
|
||||
```
|
||||
|
||||
**规范**:
|
||||
- ✅ 使用 trait 定义接口
|
||||
- ✅ 所有方法返回 `AppResult<T>`
|
||||
- ✅ 方法参数使用引用(`&str` 而不是 `String`)
|
||||
- ✅ 添加详细的文档注释
|
||||
|
||||
### 步骤 5:实现平台特定代码
|
||||
|
||||
```rust
|
||||
// src/platforms/windows/new_feature_impl.rs
|
||||
|
||||
use crate::error::{AppError, AppResult};
|
||||
use crate::platforms::new_feature::NewFeatureAccessor;
|
||||
|
||||
/// Windows 平台实现
|
||||
#[cfg(windows)]
|
||||
pub struct WindowsNewFeature;
|
||||
|
||||
#[cfg(windows)]
|
||||
impl NewFeatureAccessor for WindowsNewFeature {
|
||||
fn execute_platform_operation(&self, param1: &str) -> AppResult<String> {
|
||||
// Windows 特定实现
|
||||
Ok(format!("Windows: {}", param1))
|
||||
}
|
||||
}
|
||||
|
||||
/// 其他平台占位实现
|
||||
#[cfg(not(windows))]
|
||||
pub struct DummyNewFeature;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
impl NewFeatureAccessor for DummyNewFeature {
|
||||
fn execute_platform_operation(&self, _param1: &str) -> AppResult<String> {
|
||||
Err(AppError::PlatformNotSupported(
|
||||
"此平台暂不支持该功能".to_string()
|
||||
))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**规范**:
|
||||
- ✅ 使用 `#[cfg(windows)]` 条件编译
|
||||
- ✅ 提供其他平台的占位实现
|
||||
- ✅ 占位实现返回 `PlatformNotSupported` 错误
|
||||
- ✅ 使用中文错误消息
|
||||
|
||||
### 步骤 6:实现业务逻辑(Service 层)
|
||||
### 步骤 4:实现业务逻辑(Service 层)
|
||||
|
||||
```rust
|
||||
// src/services/new_feature_service.rs
|
||||
@@ -252,7 +166,6 @@ impl NewFeatureService {
|
||||
/// # 错误
|
||||
///
|
||||
/// - 配置无效时返回 `AppError::InvalidColorData`
|
||||
/// - 平台不支持时返回 `AppError::PlatformNotSupported`
|
||||
pub fn execute(&self, config: &NewFeatureConfig) -> AppResult<NewFeatureResult> {
|
||||
// 1. 参数验证
|
||||
if config.option1.is_empty() {
|
||||
@@ -285,10 +198,11 @@ impl NewFeatureService {
|
||||
- ✅ 使用 struct 命名空间(如 `NewFeatureService`)
|
||||
- ✅ 所有方法返回 `AppResult<T>`
|
||||
- ✅ 参数验证放在 Service 层
|
||||
- ✅ 可以直接调用 Windows API
|
||||
- ✅ 添加详细的文档注释
|
||||
- ✅ 包含同步和异步两个版本(如需要)
|
||||
|
||||
### 步骤 7:创建 Tauri 命令(Command 层)
|
||||
### 步骤 5:创建 Tauri 命令(Command 层)
|
||||
|
||||
```rust
|
||||
// src/commands/new_feature_commands.rs
|
||||
@@ -366,7 +280,7 @@ pub async fn execute_new_feature_async(config: NewFeatureConfig) -> Result<NewFe
|
||||
- ✅ 参数使用结构体,便于扩展
|
||||
- ✅ 添加详细的文档注释
|
||||
|
||||
### 步骤 8:注册模块
|
||||
### 步骤 6:注册模块
|
||||
|
||||
更新各模块的 `mod.rs` 和 `lib.rs`:
|
||||
|
||||
@@ -396,7 +310,7 @@ pub mod new_feature_commands; // 新增
|
||||
- ✅ 导出常用类型
|
||||
- ✅ 注册所有新命令
|
||||
|
||||
### 步骤 9:错误处理扩展(如需要)
|
||||
### 步骤 7:错误处理扩展(如需要)
|
||||
|
||||
如果需要新的错误类型,在 `error.rs` 中添加:
|
||||
|
||||
@@ -426,7 +340,7 @@ impl fmt::Display for AppError {
|
||||
- ✅ 携带详细的错误信息(`String`)
|
||||
- ✅ 在 `Display` 实现中添加上下文
|
||||
|
||||
### 步骤 10:更新前端类型定义
|
||||
### 步骤 8:更新前端类型定义
|
||||
|
||||
在 `src/types/` 或相关位置添加 TypeScript 类型定义:
|
||||
|
||||
@@ -460,7 +374,7 @@ export type NewFeatureCommands = {
|
||||
};
|
||||
```
|
||||
|
||||
### 步骤 11:编写测试
|
||||
### 步骤 9:编写测试
|
||||
|
||||
```rust
|
||||
// src/services/new_feature_service.rs 中的测试
|
||||
@@ -500,7 +414,7 @@ mod tests {
|
||||
- ✅ 使用 `assert!` 和 `assert_eq!` 断言
|
||||
- ✅ 运行 `cargo test` 验证
|
||||
|
||||
### 步骤 12:验证和测试
|
||||
### 步骤 10:验证和测试
|
||||
|
||||
```bash
|
||||
# 1. 检查代码编译
|
||||
@@ -588,33 +502,6 @@ mod tests {
|
||||
}
|
||||
```
|
||||
|
||||
### Platform 层规范
|
||||
|
||||
**职责**:抽象平台差异,提供统一接口
|
||||
|
||||
**规范清单**:
|
||||
- ✅ 使用 trait 定义接口
|
||||
- ✅ 通过类型别名选择实现
|
||||
- ✅ 提供所有平台的占位实现
|
||||
- ✅ 使用 `#[cfg(windows)]` 条件编译
|
||||
- ✅ 占位实现返回 `PlatformNotSupported`
|
||||
|
||||
**示例**:
|
||||
```rust
|
||||
// trait 定义
|
||||
pub trait FileAccessor {
|
||||
fn read_file(&self, path: &str) -> AppResult<String>;
|
||||
}
|
||||
|
||||
// Windows 实现
|
||||
#[cfg(windows)]
|
||||
pub type PlatformFile = WindowsFile;
|
||||
|
||||
// 占位实现
|
||||
#[cfg(not(windows))]
|
||||
pub type PlatformFile = DummyFile;
|
||||
```
|
||||
|
||||
### Service 层规范
|
||||
|
||||
**职责**:业务逻辑实现和流程编排
|
||||
@@ -623,7 +510,7 @@ pub type PlatformFile = DummyFile;
|
||||
- ✅ 使用 struct 命名空间
|
||||
- ✅ 所有方法返回 `AppResult<T>`
|
||||
- ✅ 参数验证在 Service 层进行
|
||||
- ✅ 通过 trait 调用 Platform 层
|
||||
- ✅ 可以直接调用 Windows API
|
||||
- ✅ 可以调用 Utils 层函数
|
||||
- ✅ 提供同步和异步版本(如需要)
|
||||
- ✅ 使用详细的中文档注释
|
||||
@@ -639,8 +526,8 @@ impl FileService {
|
||||
return Err(AppError::InvalidData("路径不能为空".to_string()));
|
||||
}
|
||||
|
||||
// 2. 调用 Platform 层
|
||||
let content = PlatformFile::read_file(path)?;
|
||||
// 2. 调用 Windows API 或第三方库
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
|
||||
// 3. 调用 Utils 层处理
|
||||
let processed = utils::text::trim_whitespace(&content);
|
||||
@@ -709,55 +596,7 @@ pub struct ScreenshotResult {
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 定义平台抽象
|
||||
|
||||
```rust
|
||||
// src/platforms/screenshot.rs
|
||||
|
||||
use crate::error::AppResult;
|
||||
use crate::models::screenshot::ScreenshotConfig;
|
||||
|
||||
/// 屏幕截图 trait
|
||||
pub trait ScreenshotCapturer {
|
||||
/// 截取整个屏幕
|
||||
///
|
||||
/// # 参数
|
||||
///
|
||||
/// * `config` - 截图配置
|
||||
///
|
||||
/// # 返回
|
||||
///
|
||||
/// 返回截图结果,包含 Base64 编码的图片数据
|
||||
fn capture_screen(&self, config: &ScreenshotConfig) -> AppResult<ScreenshotResult>;
|
||||
|
||||
/// 截取指定区域
|
||||
///
|
||||
/// # 参数
|
||||
///
|
||||
/// * `x` - 起始 X 坐标
|
||||
/// * `y` - 起始 Y 坐标
|
||||
/// * `width` - 宽度
|
||||
/// * `height` - 高度
|
||||
/// * `config` - 截图配置
|
||||
fn capture_region(
|
||||
&self,
|
||||
x: u32,
|
||||
y: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
config: &ScreenshotConfig,
|
||||
) -> AppResult<ScreenshotResult>;
|
||||
}
|
||||
|
||||
// 类型别名
|
||||
#[cfg(windows)]
|
||||
pub type PlatformScreenshot = crate::platforms::windows::screenshot_impl::WindowsScreenshot;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub type PlatformScreenshot = crate::platforms::windows::screenshot_impl::DummyScreenshot;
|
||||
```
|
||||
|
||||
#### 3. 实现 Service
|
||||
#### 2. 实现 Service
|
||||
|
||||
```rust
|
||||
// src/services/screenshot_service.rs
|
||||
@@ -768,7 +607,6 @@ pub type PlatformScreenshot = crate::platforms::windows::screenshot_impl::DummyS
|
||||
|
||||
use crate::error::{AppError, AppResult};
|
||||
use crate::models::screenshot::{ScreenshotConfig, ScreenshotResult};
|
||||
use crate::platforms::screenshot::ScreenshotCapturer;
|
||||
|
||||
/// 截图服务
|
||||
pub struct ScreenshotService;
|
||||
@@ -793,8 +631,30 @@ impl ScreenshotService {
|
||||
));
|
||||
}
|
||||
|
||||
// 调用平台实现
|
||||
PlatformScreenshot::capture_screen(config)
|
||||
// 调用 Windows API 或使用第三方库
|
||||
// 例如:使用 screenshots-rs crate
|
||||
let screenshots = screenshots::Screen::all().map_err(|e| {
|
||||
AppError::InvalidData(format!("获取屏幕失败: {}", e))
|
||||
})?;
|
||||
|
||||
if let Some(screen) = screenshots.first() {
|
||||
let image = screen.capture().map_err(|e| {
|
||||
AppError::InvalidData(format!("截图失败: {}", e))
|
||||
})?;
|
||||
|
||||
// 转换为 Base64
|
||||
let buffer = image.buffer();
|
||||
let base64_data = base64::encode(buffer);
|
||||
|
||||
Ok(ScreenshotResult {
|
||||
data: base64_data,
|
||||
format: "png".to_string(),
|
||||
width: image.width(),
|
||||
height: image.height(),
|
||||
})
|
||||
} else {
|
||||
Err(AppError::InvalidData("未找到屏幕".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
/// 截取指定区域
|
||||
@@ -811,12 +671,19 @@ impl ScreenshotService {
|
||||
));
|
||||
}
|
||||
|
||||
PlatformScreenshot::capture_region(x, y, width, height, config)
|
||||
// 调用 Windows API 实现区域截图
|
||||
// ...
|
||||
Ok(ScreenshotResult {
|
||||
data: "base64_data".to_string(),
|
||||
format: "png".to_string(),
|
||||
width,
|
||||
height,
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 创建命令
|
||||
#### 3. 创建命令
|
||||
|
||||
```rust
|
||||
// src/commands/screenshot_commands.rs
|
||||
@@ -881,7 +748,7 @@ pub fn capture_region(
|
||||
| 函数 | snake_case | `get_pixel_color`, `toggle_window` |
|
||||
| 常量 | SCREAMING_SNAKE_CASE | `MAX_RETRY_COUNT` |
|
||||
| Trait | PascalCase + 能力 | `ScreenAccessor`, `CursorController` |
|
||||
| 类型别名 | PascalCase + Platform/Type | `PlatformScreen`, `AppResult` |
|
||||
| 类型别名 | PascalCase + Type | `AppResult`, `JsonResult` |
|
||||
|
||||
### 2. 文档注释规范
|
||||
|
||||
@@ -975,33 +842,7 @@ impl SomeService {
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 平台检测规范
|
||||
|
||||
```rust
|
||||
// ✅ 使用条件编译和 trait
|
||||
#[cfg(windows)]
|
||||
fn platform_specific() -> AppResult<()> {
|
||||
WindowsImpl::do_something()
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn platform_specific() -> AppResult<()> {
|
||||
Err(AppError::PlatformNotSupported(
|
||||
"此平台暂不支持".to_string()
|
||||
))
|
||||
}
|
||||
|
||||
// ❌ 不推荐:运行时检测
|
||||
fn platform_specific() -> AppResult<()> {
|
||||
if cfg!(windows) {
|
||||
// Windows 代码
|
||||
} else {
|
||||
Err(AppError::PlatformNotSupported("...".to_string()))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. 代码组织规范
|
||||
### 6. 代码组织规范
|
||||
|
||||
```rust
|
||||
// ✅ 推荐:按功能分组
|
||||
@@ -1114,27 +955,23 @@ impl fmt::Display for AppError {
|
||||
}
|
||||
```
|
||||
|
||||
### Q: 如何在多个平台实现同一个功能?
|
||||
### Q: 如何调用 Windows API?
|
||||
|
||||
A: 为每个平台创建独立的实现文件,并通过条件编译选择:
|
||||
A: 在 Service 层直接使用 Windows 相关的 crate 或调用 Windows API:
|
||||
|
||||
```rust
|
||||
// platforms/windows/impl.rs
|
||||
#[cfg(windows)]
|
||||
pub struct PlatformImpl;
|
||||
impl PlatformTrait for PlatformImpl { }
|
||||
// 使用 windows-rs crate
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetCursorPos;
|
||||
|
||||
// platforms/macos/impl.rs
|
||||
#[cfg(target_os = "macos")]
|
||||
pub struct PlatformImpl;
|
||||
impl PlatformTrait for PlatformImpl { }
|
||||
|
||||
// platforms/mod.rs
|
||||
#[cfg(windows)]
|
||||
pub type Platform = crate::platforms::windows::impl::PlatformImpl;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub type Platform = crate::platforms::macos::impl::PlatformImpl;
|
||||
impl CursorService {
|
||||
pub fn get_cursor_position(&self) -> AppResult<(i32, i32)> {
|
||||
let mut pos = POINT { x: 0, y: 0 };
|
||||
unsafe {
|
||||
GetCursorPos(&mut pos)?;
|
||||
}
|
||||
Ok((pos.x, pos.y))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Q: 如何处理异步操作?
|
||||
@@ -1168,7 +1005,6 @@ pub async fn get_data() -> Result<Data, String> {
|
||||
- [ ] 文档注释包含示例代码
|
||||
- [ ] 错误消息使用中文
|
||||
- [ ] 遵循依赖原则(上层依赖下层)
|
||||
- [ ] 平台特定代码正确使用条件编译
|
||||
- [ ] 新增类型已导出(如需要)
|
||||
- [ ] 命令已在 `lib.rs` 中注册
|
||||
|
||||
|
||||
Reference in New Issue
Block a user