296 lines
10 KiB
Markdown
296 lines
10 KiB
Markdown
# Design Document
|
||
|
||
## Overview
|
||
|
||
本设计文档描述图片上传功能的技术实现方案。系统采用策略模式实现多存储提供者支持,后端使用 ASP.NET Core 实现上传接口,前端使用 Vue 3 + Element Plus 实现上传组件。
|
||
|
||
## Architecture
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Frontend (Vue 3) │
|
||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||
│ │ ImageUpload │ │ Business Pages │ │
|
||
│ │ Component │◄───│ (10 pages) │ │
|
||
│ └────────┬────────┘ └─────────────────┘ │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ ┌─────────────────┐ │
|
||
│ │ upload.ts API │ │
|
||
│ └────────┬────────┘ │
|
||
└───────────┼─────────────────────────────────────────────────────┘
|
||
│ HTTP POST /api/admin/upload/image
|
||
▼
|
||
┌───────────────────────────────────────────────────────────────────┐
|
||
│ Backend (ASP.NET Core) │
|
||
│ ┌─────────────────┐ │
|
||
│ │ UploadController│ │
|
||
│ └────────┬────────┘ │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||
│ │ IUploadService │◄───│ UploadService │ │
|
||
│ └─────────────────┘ └────────┬────────┘ │
|
||
│ │ │
|
||
│ ┌──────────────────────┼──────────────────────┐ │
|
||
│ ▼ ▼ ▼ │
|
||
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │
|
||
│ │ IStorageProvider│ │LocalStorage │ │TencentCos │ │
|
||
│ │ (Interface) │◄───│Provider │ │Provider │ │
|
||
│ └─────────────────┘ └─────────────────┘ └──────────────┘ │
|
||
└───────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## Components and Interfaces
|
||
|
||
### Backend Components
|
||
|
||
#### 1. IStorageProvider 接口
|
||
|
||
```csharp
|
||
public interface IStorageProvider
|
||
{
|
||
/// <summary>
|
||
/// 存储类型标识
|
||
/// </summary>
|
||
string StorageType { get; }
|
||
|
||
/// <summary>
|
||
/// 上传文件
|
||
/// </summary>
|
||
Task<UploadResult> UploadAsync(Stream fileStream, string fileName, string contentType);
|
||
|
||
/// <summary>
|
||
/// 删除文件
|
||
/// </summary>
|
||
Task<bool> DeleteAsync(string fileUrl);
|
||
}
|
||
```
|
||
|
||
#### 2. LocalStorageProvider 本地存储
|
||
|
||
```csharp
|
||
public class LocalStorageProvider : IStorageProvider
|
||
{
|
||
public string StorageType => "1";
|
||
|
||
// 保存到 wwwroot/uploads/{yyyy}/{MM}/{dd}/{filename}
|
||
// 返回 /uploads/{yyyy}/{MM}/{dd}/{filename}
|
||
}
|
||
```
|
||
|
||
#### 3. TencentCosProvider 腾讯云COS存储
|
||
|
||
```csharp
|
||
public class TencentCosProvider : IStorageProvider
|
||
{
|
||
public string StorageType => "3";
|
||
|
||
// 使用腾讯云COS SDK上传
|
||
// 返回 {Domain}/uploads/{yyyy}/{MM}/{dd}/{filename}
|
||
}
|
||
```
|
||
|
||
#### 4. IUploadService 上传服务接口
|
||
|
||
```csharp
|
||
public interface IUploadService
|
||
{
|
||
/// <summary>
|
||
/// 上传图片
|
||
/// </summary>
|
||
Task<UploadResponse> UploadImageAsync(IFormFile file);
|
||
|
||
/// <summary>
|
||
/// 批量上传图片
|
||
/// </summary>
|
||
Task<List<UploadResponse>> UploadImagesAsync(List<IFormFile> files);
|
||
}
|
||
```
|
||
|
||
#### 5. UploadService 上传服务实现
|
||
|
||
```csharp
|
||
public class UploadService : IUploadService
|
||
{
|
||
// 根据配置选择存储提供者
|
||
// 验证文件格式和大小
|
||
// 生成唯一文件名
|
||
// 调用存储提供者上传
|
||
}
|
||
```
|
||
|
||
#### 6. UploadController 上传控制器
|
||
|
||
```csharp
|
||
[Route("api/admin/upload")]
|
||
public class UploadController : BusinessControllerBase
|
||
{
|
||
[HttpPost("image")]
|
||
public async Task<IActionResult> UploadImage(IFormFile file);
|
||
|
||
[HttpPost("images")]
|
||
public async Task<IActionResult> UploadImages(List<IFormFile> files);
|
||
}
|
||
```
|
||
|
||
### Frontend Components
|
||
|
||
#### 1. ImageUpload 组件 (已创建)
|
||
|
||
位置: `src/components/ImageUpload/index.vue`
|
||
|
||
Props:
|
||
- `modelValue`: string - 图片URL (v-model)
|
||
- `disabled`: boolean - 是否禁用
|
||
- `placeholder`: string - 占位文字
|
||
- `showUrlInput`: boolean - 是否显示URL输入框
|
||
- `accept`: string - 接受的文件类型
|
||
- `maxSize`: number - 最大文件大小(MB)
|
||
- `tip`: string - 提示文字
|
||
|
||
Events:
|
||
- `update:modelValue` - URL变化
|
||
- `change` - 图片变化
|
||
- `upload-success` - 上传成功
|
||
- `upload-error` - 上传失败
|
||
|
||
#### 2. upload.ts API (已创建)
|
||
|
||
位置: `src/api/upload.ts`
|
||
|
||
```typescript
|
||
export function uploadFile(file: File, onProgress?: (percent: number) => void): Promise<ApiResponse<UploadResponse>>
|
||
export function uploadFiles(files: File[], onProgress?: (percent: number) => void): Promise<ApiResponse<UploadResponse[]>>
|
||
```
|
||
|
||
## Data Models
|
||
|
||
### Backend Models
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 上传响应
|
||
/// </summary>
|
||
public class UploadResponse
|
||
{
|
||
/// <summary>
|
||
/// 文件URL
|
||
/// </summary>
|
||
public string Url { get; set; } = string.Empty;
|
||
|
||
/// <summary>
|
||
/// 文件名
|
||
/// </summary>
|
||
public string FileName { get; set; } = string.Empty;
|
||
|
||
/// <summary>
|
||
/// 文件大小(字节)
|
||
/// </summary>
|
||
public long FileSize { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 上传结果(内部使用)
|
||
/// </summary>
|
||
public class UploadResult
|
||
{
|
||
public bool Success { get; set; }
|
||
public string? Url { get; set; }
|
||
public string? ErrorMessage { get; set; }
|
||
}
|
||
```
|
||
|
||
### 上传配置 (已存在)
|
||
|
||
```csharp
|
||
public class UploadSetting
|
||
{
|
||
public string? Type { get; set; } // 1=本地, 3=腾讯云
|
||
public string? Bucket { get; set; } // COS Bucket名称
|
||
public string? Region { get; set; } // COS 地域
|
||
public string? AccessKeyId { get; set; } // COS SecretId
|
||
public string? AccessKeySecret { get; set; } // COS SecretKey
|
||
public string? Domain { get; set; } // 访问域名
|
||
}
|
||
```
|
||
|
||
## Correctness Properties
|
||
|
||
*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
||
|
||
### Property 1: 文件格式验证
|
||
|
||
*For any* 上传的文件,如果文件扩展名不在允许列表(jpg, jpeg, png, gif, webp)中,THE Upload_Service SHALL 返回格式错误。
|
||
|
||
**Validates: Requirements 1.2, 1.4**
|
||
|
||
### Property 2: 文件大小验证
|
||
|
||
*For any* 上传的文件,如果文件大小超过10MB,THE Upload_Service SHALL 返回大小超限错误。
|
||
|
||
**Validates: Requirements 1.3**
|
||
|
||
### Property 3: 文件名唯一性
|
||
|
||
*For any* 两次上传操作,即使上传相同的文件,生成的文件名 SHALL 不同。
|
||
|
||
**Validates: Requirements 1.5**
|
||
|
||
### Property 4: 存储策略一致性
|
||
|
||
*For any* 上传操作,返回的URL格式 SHALL 与当前配置的存储类型一致:
|
||
- 本地存储: URL以 `/uploads/` 开头
|
||
- 腾讯云COS: URL以配置的 Domain 开头
|
||
|
||
**Validates: Requirements 2.3, 3.3**
|
||
|
||
### Property 5: 上传成功返回有效URL
|
||
|
||
*For any* 成功的上传操作,返回的URL SHALL 是可访问的有效图片地址。
|
||
|
||
**Validates: Requirements 1.1**
|
||
|
||
## Error Handling
|
||
|
||
| 错误场景 | 错误码 | 错误信息 |
|
||
|---------|--------|---------|
|
||
| 文件为空 | 400 | 请选择要上传的文件 |
|
||
| 文件格式不支持 | 400 | 只支持 jpg、jpeg、png、gif、webp 格式的图片 |
|
||
| 文件大小超限 | 400 | 文件大小不能超过10MB |
|
||
| 存储配置无效 | 500 | 存储配置无效,请检查上传配置 |
|
||
| COS上传失败 | 500 | 上传到云存储失败: {具体错误} |
|
||
| 本地存储失败 | 500 | 保存文件失败: {具体错误} |
|
||
|
||
## Testing Strategy
|
||
|
||
### 单元测试
|
||
|
||
1. **UploadService 测试**
|
||
- 测试文件格式验证
|
||
- 测试文件大小验证
|
||
- 测试文件名生成唯一性
|
||
- 测试存储提供者选择逻辑
|
||
|
||
2. **LocalStorageProvider 测试**
|
||
- 测试目录创建
|
||
- 测试文件保存
|
||
- 测试URL生成
|
||
|
||
3. **TencentCosProvider 测试**
|
||
- 测试配置读取
|
||
- 测试上传逻辑(使用Mock)
|
||
|
||
### 集成测试
|
||
|
||
1. **API 集成测试**
|
||
- 测试上传接口完整流程
|
||
- 测试错误响应格式
|
||
|
||
### 前端测试
|
||
|
||
1. **ImageUpload 组件测试**
|
||
- 测试文件选择
|
||
- 测试上传进度显示
|
||
- 测试v-model绑定
|