feat(inference): 实现文生图与图片编辑 API 调用,新增管线与编辑 HTTP 端点,JWT 认证中间件
- inference.go: GenerateImages 改为 API 优先(callImageGenAPI),无 key 回退 mock;新增 EditImages/callImageEditAPI(multipart 上传编辑);提取 parseImageResponse 共享响应解析 - handler/generate.go: POST /api/v1/generate 触发生成管线,返回 base64 图片 - handler/edit.go: POST /api/v1/images/edit 图片编辑端点 - mildware/auth.go: JWT Bearer token 认证中间件 - main.go: 路由拆分公开/认证组,generate 与 images/edit 需鉴权
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
|
||||
"gen2d/internal/model"
|
||||
"gen2d/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// EditImageRequest 图片编辑请求。
|
||||
type EditImageRequest struct {
|
||||
Image string `json:"image" binding:"required"` // 底图 base64 编码
|
||||
Prompt string `json:"prompt" binding:"required"` // 编辑指令
|
||||
Count int `json:"count"` // 生成数量,默认 1
|
||||
}
|
||||
|
||||
// EditImageResponse 图片编辑响应体。
|
||||
type EditImageResponse struct {
|
||||
Assets []AssetResponse `json:"assets"`
|
||||
}
|
||||
|
||||
// EditImage 图片编辑接口,基于已有图片和文本指令生成修改后的图片。
|
||||
func EditImage(c *gin.Context) {
|
||||
var req EditImageRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Fail(http.StatusBadRequest, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
imageData, err := base64.StdEncoding.DecodeString(req.Image)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Fail(http.StatusBadRequest, "图片 base64 解码失败: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
count := req.Count
|
||||
if count <= 0 {
|
||||
count = 1
|
||||
}
|
||||
|
||||
images, err := service.EditImages(c.Request.Context(), imageData, req.Prompt, count)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Fail(http.StatusInternalServerError, "图片编辑失败: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
assets := make([]AssetResponse, len(images))
|
||||
for i, img := range images {
|
||||
assets[i] = AssetResponse{
|
||||
Data: base64.StdEncoding.EncodeToString(img.Data),
|
||||
Format: img.Format,
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.OK(EditImageResponse{Assets: assets}))
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
|
||||
"gen2d/internal/model"
|
||||
"gen2d/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// GenerateRequest 素材生成请求。
|
||||
type GenerateRequest struct {
|
||||
Prompt string `json:"prompt"`
|
||||
AssetType string `json:"assetType" binding:"required"`
|
||||
Tags []string `json:"tags"`
|
||||
UserNote string `json:"userNote"`
|
||||
ProjectStyle map[string]string `json:"projectStyle"`
|
||||
TaskStyle map[string]string `json:"taskStyle"`
|
||||
Resolution int `json:"resolution"`
|
||||
Directions int `json:"directions"`
|
||||
FramesPerDir int `json:"framesPerDir"`
|
||||
Format string `json:"format"`
|
||||
}
|
||||
|
||||
// GenerateResponse 素材生成响应体。
|
||||
type GenerateResponse struct {
|
||||
Assets []AssetResponse `json:"assets"`
|
||||
Metadata service.AssetMetadata `json:"metadata"`
|
||||
}
|
||||
|
||||
// AssetResponse 单个素材响应(二进制 Data 转 base64)。
|
||||
type AssetResponse struct {
|
||||
Data string `json:"data"`
|
||||
Format string `json:"format"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// Generate 素材生成接口,调用完整生成管线(PromptOptimizer → AssetGenerator → QualitySupervisor → FormatAdapter)。
|
||||
func Generate(c *gin.Context) {
|
||||
var req GenerateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Fail(http.StatusBadRequest, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
in := service.PipelineInput{
|
||||
Prompt: req.Prompt,
|
||||
AssetType: req.AssetType,
|
||||
Tags: req.Tags,
|
||||
UserNote: req.UserNote,
|
||||
ProjectStyle: req.ProjectStyle,
|
||||
TaskStyle: req.TaskStyle,
|
||||
Params: service.AssetParams{
|
||||
Resolution: req.Resolution,
|
||||
Frames: service.FrameParams{
|
||||
Directions: req.Directions,
|
||||
FramesPerDirection: req.FramesPerDir,
|
||||
},
|
||||
Format: req.Format,
|
||||
},
|
||||
}
|
||||
|
||||
output, err := service.RunPipeline(c.Request.Context(), in)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Fail(http.StatusInternalServerError, "素材生成失败: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
assets := make([]AssetResponse, len(output.Assets))
|
||||
for i, a := range output.Assets {
|
||||
assets[i] = AssetResponse{
|
||||
Data: base64.StdEncoding.EncodeToString(a.Data),
|
||||
Format: a.Format,
|
||||
URL: a.URL,
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.OK(GenerateResponse{
|
||||
Assets: assets,
|
||||
Metadata: output.Metadata,
|
||||
}))
|
||||
}
|
||||
Reference in New Issue
Block a user