# 03 — 图片异步生成 **What to build:** 缩略图生成管线骨架——`ThumbnailRenderer` 接口 + 图片类型实现 + 异步生成任务 + 上传触发。新建 `ThumbnailRenderer` 接口(`supports(contentType, ext)` + `render(originalStream, ext, width, maxHeight)`),提供图片实现(Java ImageIO 读取 → 等比缩放固定宽度 200px 上限高度 400px → JPEG 输出)。新建 `ThumbnailGenerationTask`(@Async),执行流程:从 MinIO 拉原文件 → 调 ThumbnailRenderer.render → 缩略图字节数组写入 MinIO `thumbnails/{fileId}.jpg` → 更新 `thumbnail_status = READY` + `thumbnail_retry_count = 0`。`FileApiImpl` 的 `upload()`/`uploadDirect()`/`completeMultipart()` 完成后:判断文件类型——图片(jpg/png/webp/gif/bmp)置 `PENDING` 并提交异步任务,其余类型置 `UNSUPPORTED` 不提交。本票只覆盖图片类型,PDF/Office 在后续票扩展。完成后上传一张图片,等异步任务跑完,调 02 的 API 即可拿到真缩略图。 **Blocked by:** 02 — 缩略图 API 读路径 + 占位图 **Status:** ready-for-agent - [x] `ThumbnailRenderer` 接口:`boolean supports(String contentType, String ext)` + `byte[] render(InputStream original, String ext, int width, int maxHeight)` - [x] 图片实现(Java ImageIO):读取 → 等比缩放至 width=200px,高度上限 400px → JPEG 输出 byte[] - [x] `ThumbnailGenerationTask` 新建(@Async),从 MinIO 拉原文件 → render → 写 `thumbnails/{fileId}.jpg` → 状态置 READY + retry_count 归零 - [x] `FileApiImpl` 注入 `ThumbnailRenderer`(作为可选依赖或列表注入,支持后续多实现)和 `ThumbnailGenerationTask` - [x] `upload()`/`uploadDirect()`/`completeMultipart()` 完成后判断类型:图片 → 置 PENDING + 提交异步任务;非图片 → 置 UNSUPPORTED - [x] 类型判断逻辑:从扩展名/contentType 判断是否为支持的图片格式(jpg/png/webp/gif/bmp) - [x] 测试:upload 图片后 → thumbnail_status 初始为 PENDING,异步任务被提交 - [x] 测试:upload 非图片(如 .txt)→ thumbnail_status 初始为 UNSUPPORTED,不提交异步任务 - [x] 测试:completeMultipart 图片后 → 同上 PENDING + 提交 - [x] 测试:异步任务生成成功 → 写 MinIO 缩略图对象 + 状态置 READY + retry_count 归零