WorkCamera/.kiro/specs/work-camera-2.0.1/design.md
2026-01-05 23:58:56 +08:00

15 KiB
Raw Blame History

Design Document

Overview

随工水印相机 2.0.1 客户端是一个 Windows 桌面应用程序,使用 WinForms + .NET 8 开发。主要功能包括:

  • 主页统计信息展示
  • 工作记录管理(查询、新增、编辑、删除、导出)
  • 月报表查看与导出
  • 照片 ZIP 下载
  • 通用功能(登录管理、网络处理、设置、日志)

核心设计原则:所有导出功能在客户端本地执行,减轻服务器压力。

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        CS Client (WinForms)                      │
├─────────────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐              │
│  │  MainForm   │  │ WorkRecord  │  │ MonthReport │              │
│  │  (主页)     │  │   Form      │  │   Form      │              │
│  └─────────────┘  └─────────────┘  └─────────────┘              │
│         │                │                │                      │
│  ┌──────┴────────────────┴────────────────┴──────┐              │
│  │                  Services Layer                │              │
│  ├───────────────────────────────────────────────┤              │
│  │  ApiService  │ ExportService │ ConfigService  │              │
│  │  ImageService│ LogService    │ AuthService    │              │
│  └───────────────────────────────────────────────┘              │
│         │                │                │                      │
│  ┌──────┴────────────────┴────────────────┴──────┐              │
│  │                  Models Layer                  │              │
│  ├───────────────────────────────────────────────┤              │
│  │  WorkRecord  │ MonthlyReport │ Statistics     │              │
│  │  ExportTask  │ AppConfig     │ ApiResponse    │              │
│  └───────────────────────────────────────────────┘              │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                        Backend Server                            │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  /business/CamWorkrecord/*  │  /business/CamWorkers/*       ││
│  │  /api/workrecord/statistics │  /api/workrecord/monthImages  ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘

Components and Interfaces

1. Forms (UI Layer)

MainForm - 主页窗体

public class MainForm : Form
{
    // 统计信息显示
    void LoadStatistics();
    void DisplayStatistics(StatisticsDto data);
    
    // 导航
    void NavigateToWorkRecord();
    void NavigateToMonthReport();
    void NavigateToMigration();
}

WorkRecordForm - 工作记录管理窗体

public class WorkRecordForm : Form
{
    // 查询
    void Search(WorkRecordQueryDto query);
    void ResetQuery();
    void LoadPage(int pageNum);
    
    // CRUD
    void ShowAddDialog();
    void ShowEditDialog(int recordId);
    void ShowDetailDialog(int recordId);
    void DeleteRecord(int recordId);
    
    // 导出
    void ExportAll();
    void ExportSelected(List<int> ids);
    
    // 多选
    void SelectAll();
    void ClearSelection();
    List<int> GetSelectedIds();
}

MonthReportForm - 月报表窗体

public class MonthReportForm : Form
{
    // 查询
    void Search(MonthReportQueryDto query);
    void ResetQuery();
    
    // 导出
    void ExportExcel();
    void DownloadPhotosZip();
}

ImageViewerForm - 图片查看器窗体

public class ImageViewerForm : Form
{
    // 图片列表
    List<string> ImageUrls { get; set; }
    int CurrentIndex { get; set; }
    
    // 操作
    void ShowImage(int index);
    void NextImage();
    void PreviousImage();
    void ZoomIn();
    void ZoomOut();
    void ResetZoom();
}

2. Services Layer

ApiService - API 通信服务

public interface IApiService
{
    // 认证
    Task<LoginResult> LoginAsync(string username, string password);
    Task<bool> RefreshTokenAsync();
    
    // 统计
    Task<StatisticsDto> GetStatisticsAsync();
    
    // 工作记录
    Task<PagedResult<WorkRecordDto>> GetWorkRecordsAsync(WorkRecordQueryDto query);
    Task<WorkRecordDto> GetWorkRecordAsync(int id);
    Task<int> AddWorkRecordAsync(WorkRecordDto record);
    Task UpdateWorkRecordAsync(WorkRecordDto record);
    Task DeleteWorkRecordAsync(int id);
    
    // 月报表
    Task<List<MonthlyReportDto>> GetMonthlyReportAsync(MonthReportQueryDto query);
    Task<List<MonthImageDto>> GetMonthImagesAsync(string yearMonth);
}

ExportService - 导出服务

public interface IExportService
{
    // 工作记录导出
    Task ExportWorkRecordsAsync(
        WorkRecordQueryDto query,
        string outputPath,
        IProgress<ExportProgress> progress,
        CancellationToken cancellationToken);
    
    Task ExportWorkRecordsByIdsAsync(
        List<int> ids,
        string outputPath,
        IProgress<ExportProgress> progress,
        CancellationToken cancellationToken);
    
    // 月报表导出
    Task ExportMonthlyReportAsync(
        List<MonthlyReportDto> data,
        string outputPath);
    
    // 照片ZIP下载
    Task DownloadPhotosZipAsync(
        string yearMonth,
        string outputPath,
        IProgress<DownloadProgress> progress,
        CancellationToken cancellationToken);
}

ImageService - 图片服务

public interface IImageService
{
    // 下载
    Task<byte[]> DownloadImageAsync(string url);
    Task DownloadImagesAsync(
        List<string> urls,
        string outputDir,
        int concurrency,
        IProgress<int> progress,
        CancellationToken cancellationToken);
    
    // 处理
    byte[] CompressImage(byte[] imageData, int quality);
    byte[] ResizeImage(byte[] imageData, int width, int height);
}

ConfigService - 配置服务

public interface IConfigService
{
    AppConfig LoadConfig();
    void SaveConfig(AppConfig config);
    
    // 登录凭证
    void SaveCredentials(string serverUrl, string token);
    (string serverUrl, string token)? LoadCredentials();
    void ClearCredentials();
}

LogService - 日志服务

public interface ILogService
{
    void Info(string message);
    void Error(string message, Exception ex = null);
    void Warn(string message);
    
    Task ExportLogsAsync(string outputPath);
}

3. Models Layer

// 工作记录
public class WorkRecordDto
{
    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<ImageDto> Images { get; set; }
    public string Remarks { get; set; }
    public DateTime CreateTime { get; set; }
    public DateTime UpdateTime { get; set; }
}

// 月报表
public class MonthlyReportDto
{
    public string YearMonth { get; set; }
    public string DeptName { get; set; }
    public string WorkerName { get; set; }
    public int WorkDays { get; set; }
}

// 统计信息
public class StatisticsDto
{
    public int MonthRecordCount { get; set; }
    public int MonthImageCount { get; set; }
    public int MonthWorkerCount { get; set; }
    public int TodayRecordCount { get; set; }
    public int TotalRecordCount { get; set; }
    public int PendingMigrationCount { get; set; }
}

// 导出进度
public class ExportProgress
{
    public int TotalRecords { get; set; }
    public int ProcessedRecords { get; set; }
    public int TotalImages { get; set; }
    public int DownloadedImages { get; set; }
    public string Status { get; set; }
}

// 应用配置
public class AppConfig
{
    public string DefaultSavePath { get; set; }
    public int ImageDownloadConcurrency { get; set; } = 5;
    public int ImageCompressQuality { get; set; } = 50;
    public bool AutoCleanTempFiles { get; set; } = true;
}

Data Models

数据库表结构(后端已有)

cam_workrecord (工作记录表)
├── Id (int, PK)
├── DeptName (varchar)
├── RecordTime (datetime)
├── Longitude (varchar)
├── Latitude (varchar)
├── Address (varchar)
├── Content (varchar)
├── StatusName (varchar)
├── ImageUrl (varchar) - 封面图
├── Remarks (text)
├── CreateTime (datetime)
└── UpdateTime (datetime)

cam_workrecord_image (工作记录图片表)
├── Id (int, PK)
├── WorkrecordId (int, FK)
├── ImageUrl (varchar)
├── SortOrder (int)
├── CreateTime (datetime)
└── UpdateTime (datetime)

cam_worker (施工人员表)
├── Id (int, PK)
├── WorkrecordId (int, FK)
├── WorkerName (varchar)
├── CreateTime (datetime)
└── UpdateTime (datetime)

客户端本地存储

%AppData%/WorkCameraExport/
├── config.json          # 应用配置
├── credentials.dat      # 加密的登录凭证
├── logs/
│   ├── app_2025-01-05.log
│   └── error_2025-01-05.log
└── temp/                # 临时文件目录
    └── export_xxx/      # 导出临时目录

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 工作记录,如果图片数量大于 3则列表中显示的缩略图数量应等于 3且徽章显示的数字应等于总图片数减 3。 Validates: Requirements 3.1, 3.2

Property 2: 图片轮播索引边界

For any 图片列表,当前索引应始终在 [0, 图片数量-1] 范围内。点击下一张时,如果当前是最后一张则循环到第一张;点击上一张时,如果当前是第一张则循环到最后一张。 Validates: Requirements 3.6

Property 3: 图片上传数量限制

For any 上传操作,图片列表长度应始终不超过 15。当尝试添加图片使总数超过 15 时,应拒绝添加并提示用户。 Validates: Requirements 4.5

Property 4: 图片删除后列表更新

For any 图片删除操作,删除后图片列表长度应减少 1且被删除的图片不应存在于列表中。 Validates: Requirements 4.6

Property 5: 导出数据完整性

For any 导出操作,导出的 Excel 文件中的记录数应等于查询结果的记录数(导出全部)或选中的记录数(导出所选)。 Validates: Requirements 6.1, 6.2

Property 6: 分页数据获取

For any 分页查询,每页返回的数据量应不超过 pageSize50且所有页的数据合并后应等于总记录数。 Validates: Requirements 6.3

Property 7: 并发下载控制

For any 图片下载任务,同时进行的下载数应不超过配置的并发数(默认 5Validates: Requirements 6.4

Property 8: Excel 图片布局

For any 导出的 Excel 文件,每个单元格中的图片应按水平排列,图片尺寸应为 100x60 像素。 Validates: Requirements 6.6, 6.7

Property 9: ZIP 目录结构

For any 下载的照片 ZIP 文件,解压后的目录结构应包含:当日照片/、参与人员/{人员姓名}/、工作内容/{工作内容}/、部门/{部门名称}/ 四个分类目录。 Validates: Requirements 9.2, 9.3

Property 10: Token 自动刷新

For any API 请求,如果 Token 即将过期(剩余时间小于阈值),应在请求前自动刷新 Token。 Validates: Requirements 10.3

Property 11: 请求重试机制

For any 失败的 API 请求,应自动重试,重试次数不超过 3 次。 Validates: Requirements 11.2

Property 12: 配置持久化

For any 配置修改操作,保存后重新加载配置应得到相同的值。 Validates: Requirements 12.2

Property 13: 日志记录

For any 用户操作,应在日志文件中记录操作信息,包含时间戳和操作类型。 Validates: Requirements 12.3

Error Handling

网络错误

  • 连接超时:显示"网络连接超时,请检查网络设置",自动重试 3 次
  • 服务器错误5xx显示"服务器繁忙,请稍后重试"
  • 认证失败401跳转到登录界面

业务错误

  • 数据不存在404显示"数据不存在或已被删除"
  • 参数错误400显示具体的错误信息
  • 权限不足403显示"您没有权限执行此操作"

导出错误

  • 磁盘空间不足:显示"磁盘空间不足,请清理后重试"
  • 文件被占用:显示"文件被其他程序占用,请关闭后重试"
  • 图片下载失败:记录失败的图片,继续导出其他内容,最后提示用户

Testing Strategy

单元测试

  • 使用 xUnit 测试框架
  • 测试 Services 层的业务逻辑
  • 测试 Models 的数据验证
  • Mock 外部依赖API、文件系统

属性测试

  • 使用 FsCheck 进行属性测试
  • 每个属性测试运行 100 次迭代
  • 测试边界条件和随机输入

集成测试

  • 测试 API 通信
  • 测试文件导出功能
  • 测试配置持久化

UI 测试

  • 手动测试 UI 交互
  • 测试窗体导航
  • 测试表单验证