/7 分钟/9 阅读/0 评论
creeks-ai-img 项目全链路分析
AI 图片生成微服务,为前端 Web 平台提供 **文生图 / 图生图** 能力,底层对接 **OpenAI DALL-E** 和 **Stability AI** 两大 AI 平台,生成的图片统一存储到 **七牛云**,通过 **MySQ
一、项目定位
AI 图片生成微服务,为前端 Web 平台提供 文生图 / 图生图 能力,底层对接 OpenAI DALL-E 和 Stability AI 两大 AI 平台,生成的图片统一存储到 七牛云,通过 MySQL 持久化任务记录,Redis 缓存图片访问链接。
二、技术栈
| 分类 | 技术 | 版本 | 说明 |
|---|---|---|---|
| Web 框架 | Gin | v1.10.0 | HTTP 服务 |
| ORM | GORM + MySQL | gorm v1.25.8 | 数据持久化 |
| 缓存 | go-redis | v8.11.5 | 七牛私有链接缓存 |
| 日志 | zap + lumberjack | zap v1.27.0 | 结构化日志 + 日志轮转 |
| 异常上报 | codo-error-collect-go-sdk | v0.2.3 | 内部统一错误采集 SDK |
| AI SDK (OpenAI) | sashabaranov/go-openai | v1.41.2 | DALL-E 2/3 图片生成 |
| AI SDK (Stability) | 自封装 HTTP Client | - | Stability Core + SD v1.6 |
| 对象存储 | 七牛云 SDK | go-sdk/v7 v7.19.0 | 私域/公域双 Bucket |
| 数值计算 | shopspring/decimal | v1.3.1 | 费用统计高精度计算 |
| 容器化 | Docker 多阶段构建 | golang:1.23.4 → alpine | K8s 部署 |
| Go 版本 | Go | 1.23.4 |
三、数据库设计
数据库名:creeks_aigc,三张核心表:
3.1 ai_img_generation_info(图片生成信息表)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | BIGINT PK | 主键 |
| user_id | BIGINT | 用户 ID |
| status | TINYINT | 0=处理中, 1=已完成, 2=已失败 |
| file_type | VARCHAR | 输出文件类型 |
| input_url | VARCHAR | 用户输入的原始图片 URL(图生图场景) |
| output_url | VARCHAR | AI 生成的图片 URL |
| api_platform | VARCHAR | "OpenAI" / "Stability" |
| operator_id | BIGINT | 操作人 ID |
| operator_name | VARCHAR | 操作人姓名 |
| remark | VARCHAR | 备注/错误信息 |
| deleted | TINYINT | 逻辑删除标记 |
| created_at / updated_at | TIMESTAMP | 审计时间戳 |
3.2 ai_img_request(API 请求记录表)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | BIGINT PK | 主键 |
| user_id | BIGINT | 用户 ID |
| info_id | BIGINT FK | 关联 generation_info |
| model | VARCHAR | 模型名(dall-e-3 / stable-diffusion-v1-6 / Core) |
| api_type | VARCHAR | TextToImage / ImageToImageByPrompt / TextToImageCore |
| api_platform | VARCHAR | OpenAI / Stability |
| request_json | TEXT | 序列化后的请求体 |
| response_json | TEXT | 序列化后的响应体 |
| image_num | INT | 生成图片数量 |
| image_quality | VARCHAR | 图片质量(standard/hd) |
| image_size | VARCHAR | 图片尺寸(1024x1024) |
| image_style | VARCHAR | 图片风格(vivid/natural 等) |
| cfg_scale | INT | Stability 专属:内容接近 prompt 程度 |
| steps | INT | Stability 专属:扩散步骤数 |
| created_at / updated_at | TIMESTAMP | 审计时间戳 |
3.3 ai_media_info(AI 媒体信息表)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | BIGINT PK | 主键 |
| generation_info_id | BIGINT FK | 关联 generation_info |
| persistence_type | INT | 0=模型生成, 1=用户上传 |
| file_type | VARCHAR | 文件 MIME 类型 |
| file_size | BIGINT | 文件大小(字节) |
| file_url | VARCHAR | 七牛云 URL |
| pixel_size | VARCHAR | 像素尺寸 |
| created_at / updated_at | TIMESTAMP | 审计时间戳 |
ER 关系
erDiagram
AI_IMG_GENERATION_INFO ||--o| AI_IMG_REQUEST : "1:N (foreignKey:info_id)"
AI_IMG_GENERATION_INFO ||--o| AI_MEDIA_INFO : "1:N (foreignKey:generation_info_id)"
四、API 接口
4.1 对外暴露接口(Feign 远程调用入口)
路由前缀:/remote/ai/image
| 方法 | 路径 | 参数 | 说明 | 调用方 |
|---|---|---|---|---|
| POST | /generation |
FormData(multipart/form-data) | 生成图片(异步) | creekson-api (Java Feign) |
| POST | /queryTaskList |
JSON(分页查询) | 查询任务列表 | creekson-api (Java Feign) |
| GET | /stat/openai?month=YYYY-MM |
Query | OpenAI 月度使用统计(CSV 导出) | 内部运维 |
| GET | /stat/stability?month=YYYY-MM |
Query | Stability 月度使用统计(CSV 导出) | 内部运维 |
| GET | /health |
- | K8s 健康检查 | K8s |
4.2 Java 侧 Feign 接口(creekson-api → creeks-ai-img)
文件位置:E:\code\creekson-api\.../ai/img/AiImageClient.java
@FeignClient(
name = "creeks-ai-image",
url = "${ai-image.service.url:http://creeks-ai-image:8080}",
path = "/remote/ai/image")
Java 侧还对外暴露了用户端 API:
文件位置:E:\code\creekson-api\.../ai/img/AiImageController.java
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/v1/ai/img/generation |
前端调用的图片生成接口 |
| POST | /api/v1/ai/img/queryTaskList |
前端调用的任务查询接口 |
| GET | /api/v1/ai/img/check |
健康检查 |
4.3 前端调用链路
文件位置:E:\code\web\src\services\ai\model.ts
前端 → POST /ai/img/generation → Nginx/网关 → creekson-api(/api/v1/ai/img/generation)
↓ Feign
creeks-ai-img(/remote/ai/image/generation)
五、完整业务链路
5.1 完整流程(前端发起 → 异步生成 → 轮询获取)
整体流程分两个阶段:
阶段一:发起生成
前端调用生成接口 → Java 透传 → Go 异步调 AI → 立即返回任务记录
阶段二:前端轮询
前端定时轮询图片列表接口 → Java 透传 → Go 查 DB → 返回已生成的图片
sequenceDiagram
participant FE as Web 前端
participant Java as creekson-api (Java)
participant Go as creeks-ai-img (Go)
participant DB as MySQL
participant AI as OpenAI / Stability AI
participant Qiniu as 七牛云
Note over FE,Qiniu: 阶段一:发起图片生成
FE->>Java: POST /api/v1/ai/img/generation (FormData)
Java->>Java: 获取当前登录用户信息
Java->>Go: POST /remote/ai/image/generation (Feign, FormData)
Go->>Go: 解析表单参数 (handleForm)
Go->>Go: 如有用户上传的图片,先上传到七牛云
Go->>DB: 创建 generation_info 记录 (status=0 处理中)
Go->>DB: 创建 ai_media_info (用户上传的图片)
Go->>DB: 创建 ai_img_request 记录
Go-->>Java: 返回 generation_info (status=0)
Java-->>FE: 返回任务信息
Note over Go: 异步执行图片生成(goroutine)
Go->>AI: 调用 OpenAI/Stability API
AI-->>Go: 返回图片 (Base64 或 URL)
Go->>Qiniu: 上传生成的图片到七牛云私有空间
Go->>DB: 更新 ai_img_request (写入 response_json)
Go->>DB: 创建 ai_media_info (生成的图片)
Go->>DB: 更新 generation_info (status=1 完成 / 2 失败)
Note over FE,Qiniu: 阶段二:前端轮询获取已生成的图片
loop 每5秒轮询一次
FE->>Java: POST /api/v1/ai/img/queryTaskList
Java->>Go: POST /remote/ai/image/queryTaskList
Go->>DB: 分页查询 + JOIN AiMediaInfo
Go->>Go: 将七牛私有 URL 转为带签名的临时访问链接 (Redis 缓存 1h)
Go-->>Java: 返回分页结果(含图片列表)
Java-->>FE: 返回图片列表
FE->>FE: 检查是否还有 status=0 的任务
end
Note over FE: 全部完成后停止轮询
5.2 生成策略路由
Go 服务内部使用 策略模式 + Map 路由:
key = apiPlatform + ":" + apiType
GenerationMap = {
"OpenAI:TextToImage" → OpenAiText2Img()
"OpenAI:ImageToImageByPrompt" → OpenAiImg2ImgByPrompt()
"Stability:TextToImage" → StabilityAiText2Img() // SD v1.6
"Stability:ImageToImageByPrompt" → StabilityAiImg2ImgByPrompt()
"Stability:TextToImageCore" → StabilityAiText2ImgCore() // Stability Core API
}
5.3 OpenAI 文生图子流程
5.4 Stability AI 文生图子流程
flowchart TD
A[StabilityAiText2ImgCore] --> B[构建请求 prompt + aspect_ratio + style + negative_prompt]
B --> C[POST v2beta stable-image generate core]
C --> D[返回 Base64 图片]
D --> E[Base64 解码]
E --> F[上传七牛云]
F --> G[构建 GenerationResult]
A2[StabilityAiText2Img] --> B2[构建请求 text_prompts + cfg_scale + height + width + steps + style]
B2 --> C2[POST v1 generation model text-to-image]
C2 --> D2[返回多张 Base64 图片]
D2 --> E2[遍历解码 + 上传七牛]
E2 --> F2[构建 GenerationResult 列表]
六、外部依赖
6.1 远程服务依赖
| 依赖 | 类型 | 地址 | 说明 |
|---|---|---|---|
| OpenAI API | HTTPS | OPENAI_API_BASE_URL (环境变量) |
DALL-E 图片生成,支持 Azure 模式 |
| Stability AI API | HTTPS | STABILITY_API_HOST (默认 api.stability.ai) |
Stable Diffusion 图片生成 |
| 七牛云 | HTTPS | SDK 内置 | 图片存储(私有+公共 Bucket) |
| Redis | TCP | REDIS_HOST:REDIS_PORT |
七牛私有链接签名缓存 |
| MySQL | TCP | MYSQL_HOST:MYSQL_PORT |
数据持久化 |
| 用户服务 | HTTP | http://localhost:8080/remote/user/userServiceV1/listUserInfoByUserIds |
统计报表时获取用户信息 |
注意:用户服务调用硬编码为
localhost:8080,生产环境可能有问题,这是个坑。
6.2 Java 侧交互
creekson-api (Java Spring Boot)
├── AiImageController ← 前端直接调用
├── AiImageClient ← Feign 远程调用 creeks-ai-img
├── AiImgGenerationInfo ← 数据模型
├── AiImgRequest ← 数据模型
├── AiMediaInfo ← 数据模型
└── ReqPage/ResPage ← 分页封装
七、异步处理机制
图片生成是 异步执行 的:
- 用户请求到达 → 创建 DB 记录 (status=0) → 立即返回
go asyncExecGeneration(info, aiImgRequest)在后台 goroutine 执行- 前端通过 5 秒轮询
/queryTaskList获取最新状态 - 当有正在中的任务时持续轮询,全部完成后停止
八、七牛云存储策略
| Bucket | 用途 | 访问方式 |
|---|---|---|
私域 Bucket (creeks-dev-private) |
用户上传图片 + AI 生成图片 | 签名 URL,有效期 1 小时,Redis 缓存 |
公域 Bucket (creeks-dev-public) |
未使用(预留) | 公开访问 |
域名配置:
- 私域域名:
QINIU_IMAGE_PRIVATE_DOMAIN - 公域域名:
QINIU_IMAGE_PUBLIC_DOMAIN
九、统计报表功能
两个 GET 接口用于月度使用统计:
GET /remote/ai/image/stat/openai?month=2024-03
GET /remote/ai/image/stat/stability?month=2024-03
流程:
- 从
ai_img_request查询指定月份的请求记录 - 按模型计算每次请求的费用(decimal 高精度)
- 按 userId 分组累加请求次数和金额
- 调用户服务接口填充用户信息(企业/部门/姓名)
- 导出为 CSV 文件到本地目录
十、代码结构
creeks-ai-img/
├── main.go # 入口:初始化 logger/db/cache/api → 启动 Gin
├── Dockerfile # 多阶段构建 (golang:1.23.4 → alpine)
├── src/
│ ├── api/
│ │ ├── api.go # 健康检查 + 初始化
│ │ ├── handle_func.go # GenerationHandler / QueryTaskListHandler (路由入口)
│ │ ├── private_func.go # 策略路由 Map + GenerationResult + 异步执行逻辑
│ │ ├── openai.go # OpenAI 文生图 / 图生图实现
│ │ ├── stability.go # Stability 文生图 / 图生图实现
│ │ ├── external_api.go # 外部 API 初始化
│ │ └── result.go # 统一 Result 响应结构
│ ├── db/
│ │ ├── db.go # GORM 初始化 + BaseModel
│ │ ├── ai_img_generation_task.go # generation_info 表的 CRUD + 分页查询
│ │ ├── ai_img_request.go # api_request 表的 CRUD
│ │ ├── ai_media_info_db.go # media_info 表的 CRUD
│ │ └── page.go # 分页通用结构体
│ ├── router/
│ │ └── route.go # Gin 路由注册
│ ├── qiniu/
│ │ └── client.go # 七牛云上传 + 私有链接签名
│ ├── cache/
│ │ └── cache.go # Redis 初始化 + 签名 URL 缓存
│ ├── openai/
│ │ └── dall-e.go # OpenAI SDK 封装
│ ├── stability/
│ │ ├── client.go # Stability Client 结构体
│ │ ├── text_2_image.go # SD v1.6 text-to-image
│ │ ├── text_2_image_core.go # Core API text-to-image
│ │ ├── image_2_image_*.go # 图生图相关
│ ├── stat/
│ │ ├── do_stat.go # 月度统计逻辑
│ │ └── month_price_stat.go # 价格计算
│ ├── model/
│ │ └── user.go # 用户模型(调远程 Java 用户服务)
│ ├── logger/
│ │ └── err_collect_logger.go # 日志 + 错误上报
│ ├── consts/
│ │ └── Const.go # 常量:平台名、API 类型、状态码
│ └── utils/ # 工具集
│ ├── http_util.go # HTTP 调远程用户服务
│ ├── img_util.go # 图片尺寸获取 / Base64 解码
│ ├── file_util.go # 临时文件操作
│ ├── price_util.go # 价格计算
│ ├── time.go # 月份时间处理
│ └── ...
十一、已知问题和坑
- 用户服务地址硬编码:
http://localhost:8080/remote/user/userServiceV1/...在 K8s 环境下localhost指向自己,应该改成环境变量控制 - **StabilityAiText2Img 方法最后一行返回了 **
nil, err:即使成功也返回了nil结果列表(stability.go:150),这是个 bug - 无分布式锁:如果多个 Pod 同时处理同一个任务,没有并发控制
- 异步 goroutine 无恢复机制:如果 Pod 在异步生成过程中被杀死,任务状态会卡在
status=0(处理中) - Stability HTTP 调用无超时控制(text_2_image.go 用
http.DefaultClient,没有设置 timeout)