topfans/docs/MiniMax 图生图 API 集成方案.md
2026-04-08 01:30:58 +08:00

383 lines
11 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` 发起请求 |
| 后端 | Go + Gin | API Gateway 模式,端口 8080 |
| 配置 | `.env` 文件 | API keys 等敏感配置 |
## 三、MiniMax API 信息
**接口地址**: `POST https://api.minimaxi.com/v1/image_generation`
**请求头**:
```json
{
"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
}
```
## 四、后端设计方案
### 4.1 配置文件新增 (.env)
```bash
# MiniMax API 配置
MINIMAX_API_KEY=your_api_key_here
MINIMAX_API_URL=https://api.minimaxi.com/v1/image_generation
```
### 4.2 新增 DTO (gateway/dto/image_dto.go)
```go
// 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"`
}
type SubjectReference struct {
Type string `json:"type"`
ImageFile string `json:"image_file"`
}
// MiniMax 图生图响应
type ImageGenerationResponse struct {
Model string `json:"model"`
Prompt string `json:"prompt"`
AspectRatio string `json:"aspect_ratio"`
Images []Image `json:"images"`
}
type Image struct {
URL string `json:"url"`
}
```
### 4.3 新增 Controller (gateway/controller/image_controller.go)
```go
// ImageGeneration 图生图 - 调用 MiniMax API
// @Summary 图生图
// @Description 前端传递参数,后端调用 MiniMax 图生图 API
// @Tags assets
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param request body dto.ImageGenerationRequest true "图生图请求"
// @Success 200 {object} response.Response
// @Router /api/v1/assets/mints/image/generation [post]
func (ctrl *AssetController) ImageGeneration(c *gin.Context) {
var req dto.ImageGenerationRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "Invalid request parameters: "+err.Error())
return
}
// 从 context 获取 userID 和 starID由认证中间件设置
userID := c.GetInt64("userID")
starID := c.GetInt64("starID")
// 调用资产服务的 MiniMax 转发服务
result, err := ctrl.assetService.CallMiniMaxImageAPI(c.Request.Context(), userID, starID, &req)
if err != nil {
response.Error(c, 500, "Image generation failed: "+err.Error())
return
}
response.Success(c, result)
}
```
### 4.4 新增 Service (assetService/service/minimax_service.go)
```go
package service
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"image"
"image/gif"
"image/jpeg"
"image/png"
"net/http"
"time"
"github.com/nfnt/resize"
"github.com/topfans/backend/services/assetService/config"
dto "github.com/topfans/backend/gateway/dto"
"go.uber.org/zap"
)
// MinimaxService MiniMax API 转发服务接口
type MinimaxService interface {
// CallMiniMaxImageAPI 调用 MiniMax 图生图 API
CallMiniMaxImageAPI(ctx context.Context, userID, starID int64, req *dto.ImageGenerationRequest) (*dto.ImageGenerationResponse, error)
}
// minimaxService MiniMax API 转发服务实现
type minimaxService struct {
config *config.AssetConfig
}
// NewMinimaxService 创建 MiniMax 服务实例
func NewMinimaxService(cfg *config.AssetConfig) MinimaxService {
return &minimaxService{config: cfg}
}
// CallMiniMaxImageAPI 调用 MiniMax 图生图 API
func (s *minimaxService) CallMiniMaxImageAPI(ctx context.Context, userID, starID int64, req *dto.ImageGenerationRequest) (*dto.ImageGenerationResponse, error) {
// 1. 压缩 subject_reference 中的图片
processedRefs := make([]dto.SubjectReference, len(req.SubjectReference))
for i, ref := range req.SubjectReference {
compressedURL, err := s.compressImageIfNeeded(ref.ImageFile)
if err != nil {
// 压缩失败时使用原图
compressedURL = ref.ImageFile
zap.S().Warnf("Image compression failed, using original: %v", err)
}
processedRefs[i] = dto.SubjectReference{
Type: ref.Type,
ImageFile: compressedURL,
}
}
// 2. 构建请求体
payload := map[string]interface{}{
"model": req.Model,
"prompt": req.Prompt,
"aspect_ratio": req.AspectRatio,
"subject_reference": processedRefs,
"n": req.N,
}
// 3. 发送 HTTP POST 请求到 MiniMax
apiURL := s.config.GetMiniMaxAPIURL()
apiKey := s.config.GetMiniMaxAPIKey()
client := &http.Client{Timeout: 120 * time.Second}
jsonData, _ := json.Marshal(payload)
httpReq, err := http.NewRequestWithContext(ctx, "POST", apiURL, bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
httpReq.Header.Set("Authorization", "Bearer "+apiKey)
httpReq.Header.Set("Content-Type", "application/json")
resp, err := client.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("failed to call MiniMax API: %w", err)
}
defer resp.Body.Close()
// 4. 解析响应
var result dto.ImageGenerationResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("failed to parse response: %w", err)
}
return &result, nil
}
// compressImageIfNeeded 下载图片、压缩后返回 base64 编码
func (s *minimaxService) compressImageIfNeeded(imageURL string) (string, error) {
// 下载图片
resp, err := http.Get(imageURL)
if err != nil {
return "", err
}
defer resp.Body.Close()
// 读取图片数据
imgData, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
// 解码图片(自动识别格式)
img, format, err := image.Decode(bytes.NewReader(imgData))
if err != nil {
return "", err
}
// 计算压缩后的尺寸(最大边 1024px保持宽高比
bounds := img.Bounds()
maxDim := uint(1024)
newWidth := uint(bounds.Dx())
newHeight := uint(bounds.Dy())
if newWidth > maxDim || newHeight > maxDim {
if newWidth > newHeight {
ratio := float64(maxDim) / float64(newWidth)
newWidth = maxDim
newHeight = uint(float64(newHeight) * ratio)
} else {
ratio := float64(maxDim) / float64(newHeight)
newHeight = maxDim
newWidth = uint(float64(newWidth) * ratio)
}
}
// 如果图片尺寸没变,不压缩直接返回原图
if newWidth == uint(bounds.Dx()) && newHeight == uint(bounds.Dy()) {
return "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(imgData), nil
}
// 缩放图片Lanczos 算法,质量好)
resized := resize.Thumbnail(newWidth, newHeight, img, resize.Lanczos)
// 重新编码为 JPEG质量 85%,体积小)
var buf bytes.Buffer
switch format {
case "png":
err = png.Encode(&buf, resized)
case "gif":
err = gif.Encode(&buf, resized, nil)
default:
err = jpeg.Encode(&buf, resized, &jpeg.Options{Quality: 85})
}
if err != nil {
return "", err
}
// 返回 base64 编码data URI 格式)
encoded := base64.StdEncoding.EncodeToString(buf.Bytes())
mimeType := "image/jpeg"
if format == "png" {
mimeType = "image/png"
} else if format == "gif" {
mimeType = "image/gif"
}
return "data:" + mimeType + ";base64," + encoded, nil
}
```
### 4.5 路由注册 (gateway/router/router.go)
在 assets 路由组内添加:
```go
// 资产相关路由(需要认证)
assets := v1.Group("/assets")
assets.Use(middleware.AuthMiddleware())
{
// ... 现有路由 ...
assets.POST("/mints/image/generation", assetCtrl.ImageGeneration) // 图生图MiniMax API
}
```
## 五、前端调用方案
**架构说明**:前端只与后端 API 通信,不直接调用 MiniMax。后端作为代理调用 MiniMax。
### 5.1 前端 API (frontend/utils/api.js) - 已存在
```javascript
// 图生图 API - 前端传递必要参数,后端调用 MiniMax
export function imageGenerationApi(params) {
return request({
url: '/api/v1/assets/mints/image/generation',
method: 'POST',
data: params
})
}
```
### 5.2 前端调用示例
```javascript
// 前端只需要传递这些参数,具体的 MiniMax API 调用由后端完成
imageGenerationApi({
prompt: '描述文字',
aspect_ratio: '16:9',
subject_reference: [
{
type: 'character',
image_file: '用户选择的图片URL'
}
],
n: 2
}).then(res => {
// res.data.images 是 MiniMax 返回的图片 URL 列表
console.log('生成的图片:', res.data.images)
})
```
### 5.3 数据流向
```
前端参数 {prompt, aspect_ratio, subject_reference, n}
↓ POST /api/v1/assets/mints/image/generation
后端接收参数 → 调用 assetService 的 MiniMax 转发服务 → 调用 MiniMax API
后端获取响应 → 直接透传 images 数组 → 返回给前端
前端收到 {code: 200, data: {images: [...]}}
```
## 六、文件修改清单
| 操作 | 文件路径 | 说明 |
|------|----------|------|
| 修改 | `backend/.env` | 添加 MiniMax 配置 |
| 新增 | `backend/gateway/dto/image_dto.go` | 请求/响应 DTO |
| 新增 | `backend/services/assetService/service/minimax_service.go` | MiniMax API 转发 + 图片压缩 |
| 新增 | `backend/services/assetService/config/minimax_config.go` | MiniMax 配置读取 |
| 修改 | `backend/gateway/controller/asset_controller.go` | 新增 ImageGeneration 处理器 |
| 修改 | `backend/gateway/router/router.go` | 注册 `/mints/image/generation` 路由 |
| 修改 | `frontend/utils/api.js` | 已存在,无需修改 |
### 依赖安装
```bash
cd backend/services/assetService && go get github.com/nfnt/resize
```
## 七、验证方案
1. **后端启动**: 启动 `backend/gateway/main.go``backend/services/assetService/main.go`
2. **接口测试**: 使用 curl 或 Postman 测试接口
```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":[]}'
```
3. **前端测试**: 在页面中调用 `imageGenerationApi()` 并展示返回的图片
## 八、已确认事项
- 图片直接返回 MiniMax 的 URL不经过 OSS
- subject_reference.image_file 由前端自定义传递给后端(用户选择的自定义图片)
- 转发服务位于 `assetService` 中,不在 Gateway