topfans/docs/superpowers/specs/2026-04-07-minimax-image-generation-design.md
zerosaturation 286db74837 docs: 更新 MiniMax 设计文档,修复审核问题
- 修复时间戳不一致问题,统一使用毫秒时间戳
- 添加 CompletedAt 字段
- 添加 JobStatus 枚举类型
- 添加 HTTP 状态码表格
- 添加 SSRF 防护说明
- 添加轮询间隔(3秒)和超时(120秒)建议
- 添加任务清理机制(24h过期)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 01:30:57 +08:00

273 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MiniMax 图生图 API 集成设计方案
## 概述
前端传递参数 → 后端异步调用 MiniMax 图生图 API → 前端轮询任务状态 → 返回结果
## 现有架构
| 层级 | 技术栈 | 说明 |
|------|--------|------|
| 前端 | uni-app (Vue 3) | 使用 `uni.request` 发起请求,已有 loading 页面 |
| 后端 | Go + Gin | API Gateway 模式Dubbo RPC 调用微服务 |
| 微服务 | Go | `assetService` 等独立服务 |
| 配置 | `.env` 文件 | API keys 等敏感配置 |
## API 设计
### 1. 创建图生图任务
```
POST /api/v1/assets/mints/image/generation
```
**请求头:**
```
Authorization: Bearer <token>
Content-Type: application/json
```
**请求体:**
```json
{
"model": "image-01",
"prompt": "描述文本",
"aspect_ratio": "16:9",
"subject_reference": [
{
"type": "character",
"image_file": "https://..."
}
],
"n": 2
}
```
**响应 (202 Accepted):**
```json
{
"code": 202,
"message": "任务已创建",
"data": {
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "PROCESSING",
"created_at": 1744118400000
}
}
```
### 2. 查询任务状态
```
GET /api/v1/assets/mints/image/generation/:job_id
```
**响应 (PROCESSING):**
```json
{
"code": 200,
"message": "处理中",
"data": {
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "PROCESSING",
"progress": 50,
"created_at": 1744118400000
}
}
```
**响应 (COMPLETED):**
```json
{
"code": 200,
"message": "成功",
"data": {
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "COMPLETED",
"progress": 100,
"images": [
"https://api.minimaxi.com/v1/images/xxx.png"
],
"created_at": 1744118400000,
"completed_at": 1744118490000
}
}
```
**响应 (FAILED):**
```json
{
"code": 200,
"message": "失败",
"data": {
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "FAILED",
"progress": 0,
"error_msg": "MiniMax API 调用失败: timeout",
"created_at": 1744118400000
}
}
```
## 数据模型
### Job 状态 (内存存储)
```go
// JobStatus 任务状态枚举
type JobStatus string
const (
StatusPending JobStatus = "PENDING"
StatusProcessing JobStatus = "PROCESSING"
StatusCompleted JobStatus = "COMPLETED"
StatusFailed JobStatus = "FAILED"
)
// ImageGenerationJob 图生图任务
type ImageGenerationJob struct {
JobID string `json:"job_id"`
UserID int64 `json:"user_id"`
StarID int64 `json:"star_id"`
Status JobStatus `json:"status"`
Progress int `json:"progress"` // 0-100
Images []string `json:"images,omitempty"`
ErrorMsg string `json:"error_msg,omitempty"`
Request *ImageGenerationRequest `json:"request,omitempty"`
CreatedAt int64 `json:"created_at"` //毫秒时间戳
UpdatedAt int64 `json:"updated_at"`
CompletedAt int64 `json:"completed_at,omitempty"` // 毫秒时间戳
}
```
### DTO
```go
// ImageGenerationRequest MiniMax 图生图请求
type ImageGenerationRequest struct {
Model string `json:"model" binding:"required"`
Prompt string `json:"prompt" binding:"required"`
AspectRatio string `json:"aspect_ratio"`
SubjectReference []SubjectReference `json:"subject_reference"`
N int `json:"n"` // 1-4
}
type SubjectReference struct {
Type string `json:"type"`
ImageFile string `json:"image_file"` // 必须为有效 URL需 SSRF 校验
}
// ImageJobResponse 图生图任务响应
type ImageJobResponse struct {
JobID string `json:"job_id"`
Status string `json:"status"`
Progress int `json:"progress"`
Images []string `json:"images,omitempty"`
ErrorMsg string `json:"error_msg,omitempty"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
CompletedAt int64 `json:"completed_at,omitempty"`
}
```
## 后端实现
### 文件结构
```
backend/
├── services/assetService/
│ └── service/
│ └── minimax_service.go # MiniMax API 转发服务 + 任务管理
└── gateway/
├── controller/
│ └── asset_controller.go # 新增 ImageGeneration, GetImageJob
├── dto/
│ └── image_dto.go # 请求/响应 DTO
└── router/
└── router.go # 注册路由
```
### 核心逻辑
1. **创建任务**: 生成 job_id存储任务到内存 map返回 202
2. **异步处理**: goroutine 调用 MiniMax API图片压缩(最大边1024px),更新 job 状态
3. **查询状态**: 从内存读取 job 状态返回
4. **任务清理**: 后台 goroutine 定期清理超期(>24h)的已完成任务
### 图片压缩
- 最大边压缩至 1024px保持宽高比
- 格式转换: PNG/GIF → JPEG(质量85%)
- 返回 base64 data URI 格式
### SSRF 防护
`subject_reference[].image_file` 必须是有效 URL下载前需校验:
- 不能是私有 IP (10.x, 172.16-31.x, 192.168.x)
- 不能是 localhost
- 不能是内网域名
- 校验失败则拒绝请求 (400)
## 前端改动
**generation-loading.vue**:
- 调用 `POST /generation` 获取 job_id
- 每 3 秒轮询 `GET /generation/:job_id`
- 超时时间: 120 秒后显示"生成超时,请重试"
- 完成后跳转到结果页
## HTTP 状态码
| 场景 | HTTP 状态码 |
|------|-------------|
| 成功 (创建/查询) | 200 / 202 |
| 参数校验失败 | 400 |
| 未认证 | 401 |
| 无权访问 job | 403 |
| Job 不存在 | 404 |
| MiniMax API 失败 | 500 |
## 错误处理
| 场景 | 处理方式 |
|------|----------|
| MiniMax API 超时 | 标记 job 为 FAILEDerror_msg 包含原因 |
| 图片压缩失败 | 使用原图,继续处理 |
| SSRF 校验失败 | 返回 400"无效的图片URL" |
| Job 不存在 | 返回 404 |
| 无权访问 job | 返回 403 |
## 文件修改清单
| 操作 | 文件路径 | 说明 |
|------|----------|------|
| 新增 | `backend/gateway/dto/image_dto.go` | 请求/响应 DTO |
| 新增 | `backend/services/assetService/service/minimax_service.go` | MiniMax API 转发 + 图片压缩 + 任务管理 |
| 修改 | `backend/gateway/controller/asset_controller.go` | 新增 ImageGeneration, GetImageJob |
| 修改 | `backend/gateway/router/router.go` | 注册路由 |
| 修改 | `frontend/pages/discover/generation-loading.vue` | 改为轮询模式 |
## 依赖
```bash
cd backend/services/assetService && go get github.com/nfnt/resize
```
## 验证方案
1. 启动后端服务
2. 获取 JWT token
3. 测试创建任务:
```bash
curl -X POST http://localhost:8080/api/v1/assets/mints/image/generation \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"model":"image-01","prompt":"test","aspect_ratio":"16:9","n":1,"subject_reference":[]}'
```
4. 使用返回的 job_id 轮询状态:
```bash
curl http://localhost:8080/api/v1/assets/mints/image/generation/<job_id> \
-H "Authorization: Bearer <token>"
```