481 lines
18 KiB
Markdown
481 lines
18 KiB
Markdown
# Design Document
|
||
|
||
## Overview
|
||
|
||
随工水印相机 2.0 系统升级设计,主要包含四个核心模块:
|
||
1. **图片上传改造** - 将图片存储从服务器本地迁移到腾讯云 COS,实现客户端直传
|
||
2. **数据导出 API** - 提供分页查询接口供 CS 客户端调用
|
||
3. **CS 客户端** - Windows 桌面应用,实现本地 Excel 导出
|
||
4. **历史数据迁移** - 将服务器本地图片迁移到 COS
|
||
|
||
## Architecture
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────┐
|
||
│ System Architecture │
|
||
├─────────────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
|
||
│ │ UniApp │ │ CS Client │ │ 腾讯云 COS │ │
|
||
│ │ (Mobile) │ │ (Windows) │ │ (ap-shanghai) │ │
|
||
│ └──────┬───────┘ └──────┬───────┘ └────────────┬─────────────┘ │
|
||
│ │ │ │ │
|
||
│ │ 1.获取预签名URL │ │ │
|
||
│ ├────────────────────┼──────────────────────────┤ │
|
||
│ │ │ │ │
|
||
│ │ 2.直传图片 ────────┼──────────────────────────► │
|
||
│ │ │ │ │
|
||
│ │ 3.保存记录 │ 4.查询/导出 │ │
|
||
│ ├────────────────────┼──────────────────────────┤ │
|
||
│ │ │ │ │
|
||
│ ▼ ▼ │ │
|
||
│ ┌──────────────────────────────────────────────────────┤ │
|
||
│ │ .NET API Server │ │
|
||
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
|
||
│ │ │ CosService │ │ WorkRecord │ │ Migration │ │ │
|
||
│ │ │ │ │ Service │ │ Service │ │ │
|
||
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
|
||
│ └──────────────────────────────────────────────────────┘ │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ ┌──────────────────┐ │
|
||
│ │ SQL Server │ │
|
||
│ │ Database │ │
|
||
│ └──────────────────┘ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## Components and Interfaces
|
||
|
||
### 1. COS Service (后端)
|
||
|
||
负责腾讯云 COS 相关操作,包括生成预签名 URL 和临时密钥。
|
||
|
||
```csharp
|
||
public interface ICosService
|
||
{
|
||
/// <summary>
|
||
/// 生成批量上传预签名URL
|
||
/// </summary>
|
||
/// <param name="request">上传请求参数</param>
|
||
/// <returns>预签名URL列表</returns>
|
||
CosUploadUrlsResponse GetUploadUrls(CosUploadUrlsRequest request);
|
||
|
||
/// <summary>
|
||
/// 获取临时密钥(用于迁移)
|
||
/// </summary>
|
||
/// <returns>临时密钥信息</returns>
|
||
CosTempCredentials GetTempCredentials();
|
||
}
|
||
```
|
||
|
||
### 2. COS Controller (后端)
|
||
|
||
提供 COS 相关的 HTTP 接口。
|
||
|
||
```csharp
|
||
[Route("api/cos")]
|
||
public class CosController : BaseController
|
||
{
|
||
/// <summary>
|
||
/// 获取上传预签名URL
|
||
/// POST /api/cos/getUploadUrls
|
||
/// </summary>
|
||
Task<IActionResult> GetUploadUrls([FromBody] CosUploadUrlsRequest request);
|
||
|
||
/// <summary>
|
||
/// 获取临时密钥
|
||
/// GET /api/cos/getTempCredentials
|
||
/// </summary>
|
||
Task<IActionResult> GetTempCredentials();
|
||
}
|
||
```
|
||
|
||
### 3. WorkRecord V3 接口 (后端)
|
||
|
||
新增支持 COS URL 的工作记录保存接口。
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 添加工作记录(COS直传 v3)
|
||
/// POST /addworkrecordv3
|
||
/// </summary>
|
||
Task<IActionResult> AddCamWorkRecordV3([FromBody] CamRecordWorkV3Dto parm);
|
||
```
|
||
|
||
### 4. Export API (后端)
|
||
|
||
提供数据导出查询接口。
|
||
|
||
```csharp
|
||
[Route("api/workrecord/export")]
|
||
public class WorkRecordExportController : BaseController
|
||
{
|
||
/// <summary>
|
||
/// 分页查询工作记录(导出用)
|
||
/// GET /api/workrecord/export/list
|
||
/// </summary>
|
||
Task<IActionResult> GetExportList([FromQuery] WorkRecordExportQueryDto query);
|
||
}
|
||
```
|
||
|
||
### 5. Migration API (后端)
|
||
|
||
提供历史数据迁移相关接口。
|
||
|
||
```csharp
|
||
[Route("api/workrecord/migration")]
|
||
public class WorkRecordMigrationController : BaseController
|
||
{
|
||
/// <summary>
|
||
/// 获取待迁移记录列表
|
||
/// GET /api/workrecord/migration/list
|
||
/// </summary>
|
||
Task<IActionResult> GetMigrationList([FromQuery] MigrationQueryDto query);
|
||
|
||
/// <summary>
|
||
/// 更新迁移后的URL
|
||
/// POST /api/workrecord/migration/update
|
||
/// </summary>
|
||
Task<IActionResult> UpdateMigrationUrls([FromBody] MigrationUpdateDto dto);
|
||
}
|
||
```
|
||
|
||
### 6. CS Client Architecture
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ CS Client (WinForms) │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
||
│ │ MainForm │ │ MigrationForm │ │ LoginForm │ │
|
||
│ │ (导出界面) │ │ (迁移界面) │ │ (登录界面) │ │
|
||
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
|
||
│ │ │ │ │
|
||
│ ▼ ▼ ▼ │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ Services Layer │ │
|
||
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ │
|
||
│ │ │ ApiService │ │ CosService │ │ ExcelService │ │ │
|
||
│ │ │ (HTTP调用) │ │ (COS上传) │ │ (Excel生成) │ │ │
|
||
│ │ └─────────────┘ └─────────────┘ └─────────────────┘ │ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## Data Models
|
||
|
||
### 1. COS 上传请求/响应 DTO
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// COS上传URL请求
|
||
/// </summary>
|
||
public class CosUploadUrlsRequest
|
||
{
|
||
public DateTime RecordTime { get; set; }
|
||
public string DeptName { get; set; }
|
||
public string Content { get; set; }
|
||
public List<string> Workers { get; set; }
|
||
public string FileExt { get; set; } = ".jpg";
|
||
public int ImageCount { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// COS上传URL响应
|
||
/// </summary>
|
||
public class CosUploadUrlsResponse
|
||
{
|
||
public List<CosImageUploadInfo> Images { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单张图片上传信息
|
||
/// </summary>
|
||
public class CosImageUploadInfo
|
||
{
|
||
public string ImageId { get; set; }
|
||
public string FileName { get; set; }
|
||
public CosUploadUrls UploadUrls { get; set; }
|
||
public string AccessUrl { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 各分类目录的上传URL
|
||
/// </summary>
|
||
public class CosUploadUrls
|
||
{
|
||
public string Daily { get; set; }
|
||
public Dictionary<string, string> Workers { get; set; }
|
||
public string Content { get; set; }
|
||
public string Dept { get; set; }
|
||
}
|
||
```
|
||
|
||
### 2. 工作记录 V3 DTO
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 工作记录V3请求(COS直传)
|
||
/// </summary>
|
||
public class CamRecordWorkV3Dto
|
||
{
|
||
public int? Id { get; set; }
|
||
public string DeptName { get; set; }
|
||
public DateTime? RecordTime { get; set; }
|
||
public string Longitude { get; set; }
|
||
public string Latitude { get; set; }
|
||
public string Address { get; set; }
|
||
public string Content { get; set; }
|
||
public string StatusName { get; set; }
|
||
public string Remarks { get; set; }
|
||
public List<string> Workers { get; set; }
|
||
/// <summary>
|
||
/// COS图片URL列表(替代Base64)
|
||
/// </summary>
|
||
public List<string> ImageUrls { get; set; }
|
||
}
|
||
```
|
||
|
||
### 3. 导出查询 DTO
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 导出查询请求
|
||
/// </summary>
|
||
public class WorkRecordExportQueryDto
|
||
{
|
||
public int PageNum { get; set; } = 1;
|
||
public int PageSize { get; set; } = 50;
|
||
public DateTime? StartDate { get; set; }
|
||
public DateTime? EndDate { get; set; }
|
||
public string DeptName { get; set; }
|
||
public string WorkerName { get; set; }
|
||
public string Content { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 导出记录响应
|
||
/// </summary>
|
||
public class WorkRecordExportDto
|
||
{
|
||
public int Id { get; set; }
|
||
public string DeptName { get; set; }
|
||
public DateTime? RecordTime { get; set; }
|
||
public string Longitude { get; set; }
|
||
public string Latitude { get; set; }
|
||
public string Address { get; set; }
|
||
public string Content { get; set; }
|
||
public string StatusName { get; set; }
|
||
public List<string> Workers { get; set; }
|
||
public List<string> Images { get; set; }
|
||
public DateTime? CreateTime { get; set; }
|
||
public DateTime? UpdateTime { get; set; }
|
||
}
|
||
```
|
||
|
||
### 4. 迁移相关 DTO
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 迁移查询请求
|
||
/// </summary>
|
||
public class MigrationQueryDto
|
||
{
|
||
public int PageNum { get; set; } = 1;
|
||
public int PageSize { get; set; } = 50;
|
||
public DateTime? StartDate { get; set; }
|
||
public DateTime? EndDate { get; set; }
|
||
public int? Status { get; set; } // 0-未迁移 1-已迁移 2-失败
|
||
}
|
||
|
||
/// <summary>
|
||
/// 迁移记录响应
|
||
/// </summary>
|
||
public class MigrationRecordDto
|
||
{
|
||
public int Id { get; set; }
|
||
public DateTime? RecordTime { get; set; }
|
||
public string DeptName { get; set; }
|
||
public string Content { get; set; }
|
||
public int ImageCount { get; set; }
|
||
public List<MigrationImageDto> Images { get; set; }
|
||
public int MigrationStatus { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 迁移URL更新请求
|
||
/// </summary>
|
||
public class MigrationUpdateDto
|
||
{
|
||
public int RecordId { get; set; }
|
||
public List<MigrationUrlPair> ImageUrls { get; set; }
|
||
}
|
||
|
||
public class MigrationUrlPair
|
||
{
|
||
public string OldUrl { get; set; }
|
||
public string NewUrl { get; set; }
|
||
}
|
||
```
|
||
|
||
## COS Configuration
|
||
|
||
腾讯云 COS 配置将存储在 `appsettings.json` 中:
|
||
|
||
```json
|
||
{
|
||
"TencentCOS": {
|
||
"AppId": "1308826010",
|
||
"Region": "ap-shanghai",
|
||
"SecretId": "AKIDVyMfzKZdZP8zkNyOdsFuSsBJDB7EScs0",
|
||
"SecretKey": "89GWr7JPWYTL8ueHlAYowGZnvzKZjqs9",
|
||
"BucketName": "miaoyu",
|
||
"DomainUrl": "https://miaoyu-1308826010.cos.ap-shanghai.myqcloud.com",
|
||
"MaxSize": 100,
|
||
"DurationSecond": 600,
|
||
"Prefixes": "workfiles"
|
||
}
|
||
}
|
||
```
|
||
|
||
## COS Directory Structure
|
||
|
||
保持与现有本地存储一致的目录结构:
|
||
|
||
```
|
||
/workfiles/{yyyyMM}/{yyyyMMdd}/
|
||
├── 当日照片/{timestamp}_{random}.jpg
|
||
├── 参与人员/{人员姓名}/{timestamp}_{random}.jpg
|
||
├── 工作内容/{工作内容}/{timestamp}_{random}.jpg
|
||
└── 部门/{部门名称}/{timestamp}_{random}.jpg
|
||
```
|
||
|
||
## 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: Pre-signed URL Generation Completeness
|
||
*For any* valid upload request with N images and M workers, the API SHALL return exactly N image entries, each containing:
|
||
- 1 daily photo URL
|
||
- M worker directory URLs (one per worker)
|
||
- 1 content directory URL
|
||
- 1 department directory URL
|
||
- All URLs following the pattern `/workfiles/{yyyyMM}/{yyyyMMdd}/{category}/{subcategory}/{timestamp}_{random}.{ext}`
|
||
|
||
**Validates: Requirements 1.1, 1.2, 1.4, 1.5**
|
||
|
||
### Property 2: COS URL Validation
|
||
*For any* URL submitted to the V3 interface, the system SHALL accept only URLs matching the COS domain pattern `https://miaoyu-1308826010.cos.ap-shanghai.myqcloud.com/workfiles/...` and reject all other URLs.
|
||
|
||
**Validates: Requirements 3.3**
|
||
|
||
### Property 3: V3 Record Save Integrity
|
||
*For any* valid V3 save request with N image URLs, the system SHALL:
|
||
- Create exactly one work record in database
|
||
- Store all N image URLs in the image table
|
||
- Return the created record ID and image count = N
|
||
|
||
**Validates: Requirements 3.1, 3.4**
|
||
|
||
### Property 4: Export Pagination Consistency
|
||
*For any* export query with total N records and page size P, iterating through all ceil(N/P) pages SHALL return exactly N unique records with no duplicates or missing entries. Page size SHALL be capped at 50 regardless of requested value.
|
||
|
||
**Validates: Requirements 4.1, 4.2, 4.4**
|
||
|
||
### Property 5: Export Filter Accuracy
|
||
*For any* export query with filters (date range, department, worker, content), all returned records SHALL match ALL specified filter criteria.
|
||
|
||
**Validates: Requirements 4.3**
|
||
|
||
### Property 6: Migration List Filter
|
||
*For any* migration list query with status filter, all returned records SHALL have the specified migration status (0-unmigrated, 1-migrated, 2-failed).
|
||
|
||
**Validates: Requirements 10.1, 10.2**
|
||
|
||
### Property 7: Migration URL Update Integrity
|
||
*For any* migration update request with N URL pairs, the system SHALL:
|
||
- Validate all new URLs are valid COS URLs
|
||
- Update all N URLs atomically (all succeed or all fail)
|
||
- Preserve the mapping between old and new URLs
|
||
|
||
**Validates: Requirements 10.3, 10.4**
|
||
|
||
## Error Handling
|
||
|
||
### API Error Responses
|
||
|
||
| 错误码 | 说明 | 处理方式 |
|
||
|--------|------|----------|
|
||
| 400 | 参数错误 | 返回具体错误字段 |
|
||
| 401 | 未授权 | 要求重新登录 |
|
||
| 403 | 无权限 | 提示权限不足 |
|
||
| 500 | 服务器错误 | 记录日志,返回友好提示 |
|
||
|
||
### COS 上传错误处理
|
||
|
||
1. **预签名 URL 过期** - 客户端重新获取 URL
|
||
2. **上传失败** - 客户端重试最多 3 次
|
||
3. **部分上传成功** - 记录失败的目录,允许重试
|
||
|
||
### CS 客户端错误处理
|
||
|
||
1. **网络超时** - 自动重试,显示重试进度
|
||
2. **下载失败** - 跳过失败图片,记录日志
|
||
3. **Excel 生成失败** - 保存已下载数据,允许重新生成
|
||
|
||
## Testing Strategy
|
||
|
||
### Unit Tests
|
||
|
||
使用 xUnit 进行单元测试:
|
||
|
||
1. **CosService Tests**
|
||
- 测试预签名 URL 生成逻辑
|
||
- 测试目录路径生成
|
||
- 测试文件名生成
|
||
|
||
2. **WorkRecordService Tests**
|
||
- 测试 V3 接口数据保存
|
||
- 测试查询过滤逻辑
|
||
- 测试分页逻辑
|
||
|
||
3. **MigrationService Tests**
|
||
- 测试迁移状态判断
|
||
- 测试 URL 更新逻辑
|
||
|
||
### Property-Based Tests
|
||
|
||
使用 FsCheck 进行属性测试:
|
||
|
||
1. **Property 1: Pre-signed URL Generation Completeness**
|
||
- 生成随机的上传请求参数
|
||
- 验证返回的 URL 数量和结构
|
||
|
||
2. **Property 4: Pagination Consistency**
|
||
- 生成随机数量的测试数据
|
||
- 验证分页遍历的完整性
|
||
|
||
### Integration Tests
|
||
|
||
1. **COS 集成测试**
|
||
- 测试实际上传到 COS
|
||
- 测试预签名 URL 有效性
|
||
|
||
2. **API 集成测试**
|
||
- 测试完整的上传流程
|
||
- 测试导出 API 响应
|
||
|
||
### CS Client Tests
|
||
|
||
1. **API 调用测试**
|
||
- Mock API 响应测试
|
||
- 错误处理测试
|
||
|
||
2. **Excel 生成测试**
|
||
- 测试 Excel 格式正确性
|
||
- 测试图片嵌入功能
|