HaniBlindBox/.kiro/specs/image-upload-feature/design.md
2026-01-19 15:05:52 +08:00

10 KiB
Raw Blame History

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 接口

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 本地存储

public class LocalStorageProvider : IStorageProvider
{
    public string StorageType => "1";
    
    // 保存到 wwwroot/uploads/{yyyy}/{MM}/{dd}/{filename}
    // 返回 /uploads/{yyyy}/{MM}/{dd}/{filename}
}

3. TencentCosProvider 腾讯云COS存储

public class TencentCosProvider : IStorageProvider
{
    public string StorageType => "3";
    
    // 使用腾讯云COS SDK上传
    // 返回 {Domain}/uploads/{yyyy}/{MM}/{dd}/{filename}
}

4. IUploadService 上传服务接口

public interface IUploadService
{
    /// <summary>
    /// 上传图片
    /// </summary>
    Task<UploadResponse> UploadImageAsync(IFormFile file);
    
    /// <summary>
    /// 批量上传图片
    /// </summary>
    Task<List<UploadResponse>> UploadImagesAsync(List<IFormFile> files);
}

5. UploadService 上传服务实现

public class UploadService : IUploadService
{
    // 根据配置选择存储提供者
    // 验证文件格式和大小
    // 生成唯一文件名
    // 调用存储提供者上传
}

6. UploadController 上传控制器

[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

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

/// <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; }
}

上传配置 (已存在)

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 上传的文件如果文件大小超过10MBTHE 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绑定