30 KiB
资产铸造(AI 生成版)流程设计文档
📋 文档信息
- 版本: v1.0
- 创建日期: 2026-01-20
- 状态: 待确认
- 作者: 开发团队
📊 文档状态总结
✅ 已确认的设计
- ✅ 采用异步处理机制(AI 生成耗时,不阻塞用户请求)
- ✅ AI 生成的图片必须存储到 OSS(确保持久性)
- ✅ 使用预签名 URL 机制访问私有 OSS 资源
- ✅ 复用现有的
Asset和MintOrder数据模型 - ✅ AI 服务先使用 Mock 版本,实现完整的前后端对接流程
- ✅ 铸造订单失败时自动退回水晶费用(无论 AI 问题还是链问题)
- ✅ 所有图片 URL 自动生成预签名 URL(在查询接口中)
- ✅ 前端轮询频率为 3 秒
- ✅ AI 生成失败时,前端查询返回失败消息
- ✅ 暂不支持 WebSocket、进度提示、批量铸造
⚠️ 后续优化项(暂不实现)
- WebSocket 实时通知(当前使用轮询)
- AI 生成进度百分比显示
- 批量铸造功能(一次提交多个素材)
1. 业务概述
1.1 功能描述
用户通过上传原始图片(素材)并填写元数据信息,发起“铸造”请求。系统将:
- 扣除用户的水晶余额作为铸造费用
- 异步调用 AI 服务,基于原始素材生成一张新的艺术图片
- 将 AI 生成的图片上传到 OSS 存储
- 模拟区块链上链过程(生成交易哈希和模拟地址)
- 更新资产状态为“已激活”,用户可在“我的藏品”中查看
1.2 核心特点
- 异步处理:AI 生成耗时较长(通常 10-60 秒),采用后台异步处理,不阻塞用户请求
- 持久化存储:AI 生成的图片必须转存到自己的 OSS,避免临时链接失效
- 状态可追踪:用户可通过
order_id实时查询铸造进度 - 安全访问:私有 OSS 资源通过预签名 URL 机制访问
2. 系统架构
2.1 参与角色
| 角色 | 职责 |
|---|---|
| 前端 (FE) | 用户交互、图片上传、状态展示、结果呈现 |
| 网关 (GW) | 鉴权、限流、请求转发 |
| 资产服务 (AssetService) | 订单管理、资产状态更新、异步任务调度 |
| 用户服务 (UserService) | 水晶余额校验与扣减 |
| AI 代理层 (AI Task) | 对接 AI 接口、图片转存 OSS、模拟上链 |
| 阿里云 OSS | 存储原始素材图和 AI 生成的目标图 |
2.2 数据流向
用户上传素材 → OSS (material_url)
↓
创建铸造订单 → 数据库 (Asset + MintOrder)
↓
异步 AI 处理 → AI 服务 → 下载图片 → 上传 OSS (cover_url)
↓
更新数据库 → 状态变更 (Pending → Active)
↓
前端轮询查询 → 返回结果(含预签名 URL)
3. 详细交互流程
3.1 完整流程图
sequenceDiagram
autonumber
participant 用户
participant 前端
participant 网关
participant 资产服务
participant 用户服务
participant 异步任务(AI+OSS)
participant AI服务
participant OSS存储
Note over 用户, OSS存储: 第一阶段:素材准备
用户->>前端: 选择图片并填写信息
前端->>网关: GET /api/v1/assets/oss/upload-signature?type=asset
网关-->>前端: 返回STS临时凭证 + 目录路径
前端->>OSS存储: 直传素材图 (POST Object)
OSS存储-->>前端: 上传成功,返回 material_url
Note over 用户, OSS存储: 第二阶段:发起铸造 (同步,立即返回)
前端->>网关: POST /api/v1/assets/mints
Note right of 前端: {<br/> "name": "资产名称",<br/> "material_url": "...",<br/> "description": "描述"<br/>}
网关->>资产服务: CreateMintOrder (RPC)
资产服务->>用户服务: 扣除铸造费用 (UpdateCrystalBalance)
用户服务-->>资产服务: 扣费成功
资产服务->>资产服务: DB事务:创建 Asset (Status=0) + Order (Status=PROCESSING)
资产服务-->>网关: 返回 order_id
网关-->>前端: 返回 200 OK { order_id, status: "PROCESSING" }
前端->>用户: 界面显示 "AI创作中,请稍候..."
Note over 用户, OSS存储: 第三阶段:异步生成 (后台处理)
rect rgb(240, 240, 240)
资产服务->>异步任务(AI+OSS): 启动 Goroutine 处理
alt AI处理成功
异步任务(AI+OSS)->>AI服务: 调用AI接口 (输入: material_url) [Mock版本]
AI服务-->>异步任务(AI+OSS): 返回AI生成图片 (临时URL或Base64)
异步任务(AI+OSS)->>异步任务(AI+OSS): 下载AI图片到内存
异步任务(AI+OSS)->>OSS存储: 上传到 OSS (asset/{user_id}/{star_id}/covers/{asset_id}.png)
OSS存储-->>异步任务(AI+OSS): 返回 cover_url
异步任务(AI+OSS)->>异步任务(AI+OSS): 生成模拟 TxHash (0x...)
异步任务(AI+OSS)->>资产服务: 更新 DB: Asset (Status=1, CoverURL, TxHash)
资产服务->>资产服务: 更新 Order 为 SUCCESS
else AI处理失败
异步任务(AI+OSS)->>资产服务: 更新 Order 为 FAILED + error_message
资产服务->>用户服务: 退回水晶费用 (UpdateCrystalBalance)
用户服务-->>资产服务: 退费成功
end
end
Note over 用户, OSS存储: 第四阶段:结果获取 (轮询/展示)
loop 状态轮询 (每3秒)
前端->>网关: GET /api/v1/assets/mints/:order_id
网关->>资产服务: 查询订单状态 (GetMintOrder)
资产服务-->>前端: 返回 Status=SUCCESS, cover_url=...
end
alt 状态为SUCCESS
资产服务-->>前端: 返回 Status=SUCCESS, cover_url_signed=... (已自动生成预签名URL)
前端->>用户: 动画展示生成的藏品 & 模拟链上地址
else 状态为FAILED
资产服务-->>前端: 返回 Status=FAILED, error_message="AI生成失败,已退回费用"
前端->>用户: 显示失败提示 & 费用已退回消息
end
3.2 阶段说明
阶段一:素材准备(前端完成)
- 用户在前端选择图片并填写元数据(名称、描述等)
- 前端调用
GET /api/v1/assets/oss/upload-signature?type=asset获取 OSS 上传凭证 - 前端使用临时凭证直接上传图片到 OSS(路径:
asset/{user_id}/{star_id}/materials/{filename}) - 前端获得
material_url(原始素材的 OSS 地址)
阶段二:发起铸造(同步,立即返回)
- 前端调用
POST /api/v1/assets/mints,传入material_url和元数据 - 后端在事务中完成:
- 扣除用户水晶余额(调用 UserService)
- 创建
Asset记录(Status = 0 (Pending),MaterialURL已设置,CoverURL为空) - 创建
MintOrder记录(Status = PROCESSING)
- 立即返回
order_id,不等待 AI 处理
阶段三:异步 AI 处理(后台 Goroutine)
- 启动独立的 Goroutine 处理 AI 生成任务
- 调用 AI 服务接口(输入:
material_url)- Mock 版本:随机选择预设图片或直接复制
material_url的图片
- Mock 版本:随机选择预设图片或直接复制
- AI 服务返回生成的图片(可能是临时 URL 或 Base64)
- 下载 AI 生成的图片到内存
- 上传到 OSS(路径:
asset/{user_id}/{star_id}/covers/{asset_id}_{timestamp}.png) - 生成模拟链上信息:
TxHash:0x+ 64位随机16进制字符串BlockNumber: 随机生成MintedAt: 当前时间戳
- 更新数据库:
Asset.Status = 1 (Active)Asset.CoverURL = <OSS地址>Asset.TxHash = <模拟哈希>MintOrder.Status = SUCCESS
失败处理(AI 超时、AI 调用失败、OSS 上传失败等):
- 记录错误信息到
MintOrder.error_message - 更新订单状态为
FAILED - 自动退回水晶费用:调用
UserService.UpdateCrystalBalance,退回cost_crystal金额 - 记录日志,便于后续排查
阶段四:结果获取(前端轮询)
- 前端每 3 秒轮询一次
GET /api/v1/assets/mints/:order_id - 成功场景:当
status = SUCCESS时- 前端获取到
cover_url_signed(后端已自动生成预签名 URL) - 展示生成的藏品图片和模拟链上地址
- 前端获取到
- 失败场景:当
status = FAILED时- 前端获取到
error_message字段 - 展示失败提示:"AI生成失败,已退回水晶费用"
- 用户可重新提交订单
- 前端获取到
4. API 接口定义
4.1 创建铸造订单
Endpoint: POST /api/v1/assets/mints
认证: 需要 JWT Token(从 Header 中获取 user_id 和 star_id)
Request Body:
{
"name": "我的数字艺术藏品",
"material_url": "https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/7/87/materials/original.jpg",
"description": "这是由AI生成的数字艺术品",
"rarity": 1,
"tags": ["AI", "Art", "Digital"]
}
字段说明:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name |
string | ✅ | 资产名称 |
material_url |
string | ✅ | 原始素材的 OSS URL(前端已上传到 asset/{user_id}/{star_id}/materials/ 目录) |
description |
string | ❌ | 资产描述 |
rarity |
int32 | ❌ | 稀有度(预留字段) |
tags |
string[] | ❌ | 标签数组 |
重要说明:
- ✅
material_url是必填字段,前端必须在调用此接口前完成素材上传 - ✅
cover_url不需要前端传入,由后端 AI 处理完成后自动生成 - ✅ 创建订单时,
Asset.CoverURL字段为空,等待异步任务完成后回填
Response (200 OK):
{
"code": 200,
"message": "success",
"data": {
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"asset_id": 12345,
"status": "PROCESSING",
"created_at": 1705747200000
}
}
错误响应:
400 Bad Request: 参数错误(缺少必填字段、URL 格式错误等)401 Unauthorized: 未授权(Token 无效)402 Payment Required: 水晶余额不足500 Internal Server Error: 服务器内部错误
4.2 查询铸造订单状态
Endpoint: GET /api/v1/assets/mints/:order_id
认证: 需要 JWT Token(只能查询自己的订单)
URL 参数:
order_id: 订单 ID(UUID 格式)
Response (200 OK) - 成功状态:
{
"code": 200,
"message": "success",
"data": {
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"asset_id": 12345,
"status": "SUCCESS",
"cover_url": "https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/7/87/covers/12345_1705747200.png",
"cover_url_signed": "https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/7/87/covers/12345_1705747200.png?Expires=...&Signature=...",
"tx_hash": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
"minted_at": 1705747260000,
"created_at": 1705747200000,
"updated_at": 1705747260000
}
}
Response (200 OK) - 处理中状态:
{
"code": 200,
"message": "success",
"data": {
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"asset_id": 12345,
"status": "PROCESSING",
"cover_url": null,
"cover_url_signed": null,
"tx_hash": null,
"minted_at": null,
"created_at": 1705747200000,
"updated_at": 1705747200000
}
}
Response (200 OK) - 失败状态:
{
"code": 200,
"message": "success",
"data": {
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"asset_id": 12345,
"status": "FAILED",
"error_message": "AI生成超时,已自动退回水晶费用",
"cover_url": null,
"cover_url_signed": null,
"tx_hash": null,
"minted_at": null,
"created_at": 1705747200000,
"updated_at": 1705747300000
}
}
状态值说明:
PROCESSING: AI 生成中(前端继续轮询)SUCCESS: 生成成功(前端展示结果)FAILED: 生成失败(前端显示error_message,提示用户费用已退回)
关键设计点:
- ✅ 自动预签名:
status = SUCCESS时,自动返回cover_url_signed预签名 URL - ✅ 失败信息返回:
status = FAILED时,返回error_message字段,前端直接展示 - ✅ 费用已退回:失败时,后端已自动退回水晶费用,前端无需额外处理
错误响应:
404 Not Found: 订单不存在或不属于当前用户401 Unauthorized: 未授权
4.3 获取我的藏品列表
Endpoint: GET /api/v1/assets/me
认证: 需要 JWT Token
Query 参数:
page: 页码(默认 1)page_size: 每页数量(默认 20,最大 100)status: 筛选状态(可选:pending,active,默认返回全部)
Response (200 OK):
{
"code": 200,
"message": "success",
"data": {
"total": 50,
"page": 1,
"page_size": 20,
"items": [
{
"asset_id": 12345,
"name": "我的数字艺术藏品",
"cover_url": "https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/7/87/covers/12345_1705747200.png",
"cover_url_signed": "https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/7/87/covers/12345_1705747200.png?Expires=...&Signature=...",
"status": "active",
"tx_hash": "0x1a2b3c4d...",
"minted_at": 1705747260000,
"like_count": 10,
"created_at": 1705747200000
},
{
"asset_id": 12346,
"name": "另一个藏品",
"cover_url": null,
"cover_url_signed": null,
"status": "pending",
"tx_hash": null,
"minted_at": null,
"like_count": 0,
"created_at": 1705747300000
}
]
}
}
关键设计点:
- ✅ 自动预签名:后端在返回数据前,自动为所有
cover_url和material_url生成预签名 URL,放入*_signed字段 - ✅ 状态区分:
status = pending时,cover_url为null,前端显示“生成中”占位图 - ✅ 状态筛选:支持按状态筛选,方便用户查看“进行中”或“已完成”的藏品
- ✅ 失败状态展示:
status = failed的藏品也会出现在列表中,cover_url为null,前端可显示“生成失败”提示
4.4 获取藏品详情
Endpoint: GET /api/v1/assets/:asset_id
认证: 需要 JWT Token(只能查看自己的藏品或公开藏品)
Response (200 OK):
{
"code": 200,
"message": "success",
"data": {
"asset_id": 12345,
"name": "我的数字艺术藏品",
"description": "这是由AI生成的数字艺术品",
"material_url": "https://.../materials/original.jpg",
"material_url_signed": "https://...?Expires=...&Signature=...",
"cover_url": "https://.../covers/12345_1705747200.png",
"cover_url_signed": "https://...?Expires=...&Signature=...",
"status": "active",
"tx_hash": "0x1a2b3c4d...",
"block_number": 12345678,
"minted_at": 1705747260000,
"like_count": 10,
"is_liked": false,
"created_at": 1705747200000
}
}
5. 数据模型设计
5.1 Asset 表(资产表)
现有字段复用:
| 字段 | 类型 | 用途 | 说明 |
|---|---|---|---|
id |
int64 | 资产ID | 主键 |
owner_uid |
int64 | 所有者ID | 外键关联 users 表 |
star_id |
int64 | 明星ID | 用于数据隔离 |
name |
string | 资产名称 | 用户填写 |
cover_url |
string | AI生成图URL | 异步处理完成后回填 |
material_url |
string | 原始素材URL | 前端上传时设置 |
description |
text | 描述 | 用户填写 |
status |
int32 | 状态 | 0=Pending, 1=Active |
tx_hash |
string | 交易哈希 | 模拟生成(0x...) |
block_number |
int64 | 区块号 | 模拟生成 |
minted_at |
int64 | 上链时间 | 毫秒时间戳 |
like_count |
int32 | 点赞数 | 默认 0 |
created_at |
int64 | 创建时间 | 毫秒时间戳 |
updated_at |
int64 | 更新时间 | 毫秒时间戳 |
关键字段映射:
- ✅
MaterialURL→ 用户上传的原始素材(前端已上传到 OSS) - ✅
CoverURL→ AI 生成的最终图片(异步任务完成后回填) - ✅
Status→0表示处理中,1表示已完成
5.2 MintOrder 表(铸造订单表)
现有字段复用:
| 字段 | 类型 | 用途 | 说明 |
|---|---|---|---|
order_id |
string | 订单ID | UUID,主键 |
user_id |
int64 | 用户ID | 外键关联 users 表 |
star_id |
int64 | 明星ID | 用于数据隔离 |
asset_id |
int64 | 资产ID | 外键关联 assets 表 |
status |
string | 订单状态 | PROCESSING, SUCCESS, FAILED |
cost_crystal |
int64 | 消耗水晶 | 铸造费用 |
error_message |
text | 错误信息 | 失败时记录原因 |
retry_count |
int32 | 重试次数 | 默认 0 |
created_at |
int64 | 创建时间 | 毫秒时间戳 |
updated_at |
int64 | 更新时间 | 毫秒时间戳 |
minted_at |
int64 | 完成时间 | 毫秒时间戳 |
6. 状态机设计
6.1 Asset 状态流转
[Pending (0)] ──AI处理成功──> [Active (1)]
│ │
└──AI处理失败──> [Pending (0)] ──┘
(保持Pending,记录错误信息)
状态说明:
- Pending (0): 资产创建后,等待 AI 处理或处理失败
- Active (1): AI 处理成功,资产可用(
CoverURL已设置)
6.2 MintOrder 状态流转
[PROCESSING] ──AI处理成功──> [SUCCESS]
│ │
└──AI处理失败──> [FAILED] ────┘
(记录 error_message)
状态说明:
- PROCESSING: 订单创建后,AI 处理中
- SUCCESS: AI 处理成功,资产已激活
- FAILED: AI 处理失败(超时、API 错误等)
7. 关键设计决策
7.1 异步处理机制
决策: 采用 Goroutine 异步处理 AI 生成任务,不阻塞用户请求。
理由:
- AI 生成通常需要 10-60 秒,同步等待会导致请求超时
- 用户体验更好:用户提交后立即获得
order_id,可继续其他操作 - 系统可扩展:后续可引入消息队列(如 RocketMQ)进行任务调度
实现方式:
// 在 CreateMintOrder 中,创建订单后立即启动异步任务
go s.processAIGeneration(mintOrder.OrderID, asset.ID, req.MaterialUrl)
7.2 AI 图片必须存储到 OSS
决策: AI 服务返回的图片(临时 URL 或 Base64)必须下载并上传到自己的 OSS。
理由:
- AI 服务的临时 URL 通常有效期仅 1 小时,不转存会导致链接失效
- 统一管理:所有资产图片都在自己的 OSS 中,便于权限控制和 CDN 加速
- 数据安全:避免依赖第三方服务的稳定性
实现方式:
- 调用 AI 服务,获取生成的图片(临时 URL 或 Base64)
- 下载图片到内存(如果是 URL,使用 HTTP GET;如果是 Base64,解码)
- 上传到 OSS(路径:
asset/{user_id}/{star_id}/covers/{asset_id}_{timestamp}.png) - 将 OSS URL 回填到
Asset.CoverURL
7.3 预签名 URL 自动生成
决策: 在 GetMyAssets、GetAssetDetail 和 GetMintOrder 接口中,后端自动为所有图片 URL 生成预签名 URL。
理由:
- 减少前端请求次数:前端不需要为每张图片单独调用预签名接口
- 统一管理:后端统一控制访问权限和有效期
- 性能优化:批量生成预签名 URL,减少 OSS API 调用
- 用户体验:前端拿到数据即可直接展示图片,无需额外请求
实现方式:
// 在 DTO 转换层,自动为所有图片 URL 生成预签名 URL
func ToAssetDTO(asset *models.Asset) *dto.AssetDTO {
dto := &dto.AssetDTO{
AssetID: asset.ID,
CoverURL: asset.CoverURL,
MaterialURL: asset.MaterialURL,
}
// 为 cover_url 生成预签名 URL(如果存在)
if asset.CoverURL != "" {
signedURL, _ := generatePresignedURL(asset.CoverURL, 3600)
dto.CoverURLSigned = signedURL
}
// 为 material_url 生成预签名 URL(如果存在)
if asset.MaterialURL != nil && *asset.MaterialURL != "" {
signedURL, _ := generatePresignedURL(*asset.MaterialURL, 3600)
dto.MaterialURLSigned = &signedURL
}
return dto
}
适用范围:
- ✅
GET /api/v1/assets/me- 藏品列表 - ✅
GET /api/v1/assets/:asset_id- 藏品详情 - ✅
GET /api/v1/assets/mints/:order_id- 订单状态查询(成功时)
7.4 模拟链上信息生成
决策: 在前期不引入链端 API 的情况下,后端模拟生成 TxHash 和 BlockNumber。
实现方式:
// 生成模拟交易哈希
txHash := fmt.Sprintf("0x%x", rand.Int63()) // 0x + 64位16进制
// 生成模拟区块号
blockNumber := rand.Int63n(1000000) + 1000000
// 设置上链时间
mintedAt := time.Now().UnixMilli()
后续扩展: 当引入真实区块链时,只需替换此处的模拟逻辑为真实的链端 SDK 调用。
8. 错误处理与异常场景
8.1 AI 生成超时
场景: AI 服务响应时间超过 60 秒。
处理:
- 设置超时时间(60 秒,可配置)
- 超时后,将订单状态更新为
FAILED - 记录错误信息:
error_message = "AI生成超时" - ✅ 自动回退水晶费用:调用
UserService.UpdateCrystalBalance,退回cost_crystal金额 - 记录日志,便于后续排查
8.2 AI 服务调用失败
场景: AI 服务返回错误(API 限流、服务不可用等)。
处理:
- 记录错误信息到
MintOrder.error_message(如:"AI服务调用失败: API限流") - 更新订单状态为
FAILED - ✅ 自动回退水晶费用:调用
UserService.UpdateCrystalBalance,退回cost_crystal金额 - 暂不重试:当前版本不自动重试,失败即退回费用(后续版本可考虑重试机制)
8.3 OSS 上传失败
场景: AI 图片下载成功,但上传到 OSS 失败。
处理:
- 记录错误信息:
error_message = "OSS上传失败: <具体错误>" - 订单状态更新为
FAILED - ✅ 自动回退水晶费用:调用
UserService.UpdateCrystalBalance,退回cost_crystal金额 - 建议:保留 AI 服务的临时 URL(如果还在有效期内),记录到日志,便于后续手动重试
8.4 链上模拟失败(预留)
场景: 模拟链上信息生成失败(当前版本为模拟,此场景理论上不会发生,但预留处理逻辑)。
处理:
- 记录错误信息
- 订单状态更新为
FAILED - ✅ 自动回退水晶费用:调用
UserService.UpdateCrystalBalance,退回cost_crystal金额
8.5 用户余额不足
场景: 创建订单时,用户水晶余额不足。
处理:
- 在事务中先检查余额,不足则直接返回错误
- 不创建
Asset和MintOrder记录 - 返回 HTTP 402 状态码,前端提示“余额不足”
8.6 费用回退统一处理
决策: 所有失败场景(AI 超时、AI 调用失败、OSS 上传失败、链上失败)都必须自动退回水晶费用。
实现方式:
// 在异步任务失败时,统一调用费用回退
func (s *mintService) refundCrystalBalance(order *models.MintOrder) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 退回水晶费用
_, err := s.userClient.UpdateCrystalBalance(ctx, order.UserID, order.StarID, order.CostCrystal)
if err != nil {
logger.Logger.Error("Failed to refund crystal balance",
zap.String("order_id", order.OrderID),
zap.Error(err),
)
return err
}
logger.Logger.Info("Crystal balance refunded",
zap.String("order_id", order.OrderID),
zap.Int64("amount", order.CostCrystal),
)
return nil
}
注意事项:
- 费用回退必须在订单状态更新为
FAILED之前完成(确保原子性) - 如果费用回退失败,需要记录告警日志,并可能需要人工介入
9. 性能优化建议
9.1 异步任务并发控制
建议: 限制同时处理的 AI 任务数量,避免过载。
实现方式:
// 使用 channel 作为信号量,限制并发数
var semaphore = make(chan struct{}, 10) // 最多10个并发任务
func (s *mintService) processAIGeneration(...) {
semaphore <- struct{}{} // 获取信号量
defer func() { <-semaphore }() // 释放信号量
// AI 处理逻辑
}
9.2 预签名 URL 批量生成
建议: 在 GetMyAssets 接口中,批量生成预签名 URL,减少 OSS API 调用。
实现方式:
// 一次性为所有 cover_url 生成预签名 URL
for i := range assets {
if assets[i].CoverURL != "" {
signedURL, _ := generatePresignedURL(assets[i].CoverURL, 3600)
assets[i].CoverURLSigned = signedURL
}
}
9.3 数据库索引优化
建议: 确保以下字段已建立索引:
assets.owner_uid + star_id(复合索引,用于查询用户藏品)assets.status(用于筛选状态)mint_orders.user_id + star_id(复合索引,用于查询用户订单)mint_orders.status(用于筛选订单状态)
10. 已确认问题清单
✅ 已确认的设计决策
-
AI 服务选择 ✅
- ✅ 先使用 Mock 版本:实现完整的前后端对接流程,AI 服务暂时模拟(返回一张预设图片或随机选择)
- ✅ 后续集成真实 AI 服务:Phase 4 再集成真实的 AI 服务 API
- ✅ Mock 实现方式:可以随机选择一张预设图片,或直接复制
material_url作为cover_url(仅用于测试)
-
AI 生成超时与重试 ✅
- ✅ 超时时间:60 秒(可配置)
- ✅ 失败处理:不自动重试,失败即退回费用(后续版本可考虑重试机制)
-
费用回退策略 ✅
- ✅ 自动回退:所有失败场景(AI 超时、AI 调用失败、OSS 上传失败、链上失败)都必须自动退回水晶费用
- ✅ 回退时机:在订单状态更新为
FAILED时,立即调用UserService.UpdateCrystalBalance退回费用 - ✅ 回退金额:退回
MintOrder.cost_crystal的完整金额
-
藏品列表预签名 ✅
- ✅ 自动生成:在
GetMyAssets、GetAssetDetail和GetMintOrder接口中,自动为所有图片 URL 生成预签名 URL - ✅ 有效期:3600 秒(1 小时)
- ✅ 适用范围:
cover_url和material_url都需要生成预签名 URL
- ✅ 自动生成:在
-
状态轮询频率 ✅
- ✅ 轮询频率:3 秒(前端每 3 秒轮询一次
GET /api/v1/assets/mints/:order_id)
- ✅ 轮询频率:3 秒(前端每 3 秒轮询一次
-
错误信息展示 ✅
- ✅ 失败消息返回:AI 生成失败时,
GET /api/v1/assets/mints/:order_id接口返回status = FAILED和error_message字段 - ✅ 前端展示:前端直接展示
error_message,提示用户费用已退回 - ✅ 暂不提供重新生成:当前版本不提供“重新生成”功能,用户需要重新提交订单
- ✅ 失败消息返回:AI 生成失败时,
🟢 暂不实现的功能(后续优化)
-
WebSocket 实时通知 ❌
- ❌ 暂不实现:当前版本使用轮询机制,后续版本可考虑 WebSocket
-
AI 生成进度 ❌
- ❌ 暂不实现:当前版本不显示进度百分比,后续版本可考虑(需要 AI 服务支持)
-
批量铸造 ❌
- ❌ 暂不实现:当前版本不支持一次提交多个素材,后续版本可考虑
11. 实现计划
Phase 1: 核心功能实现(预计 3-5 天)
- 修改
CreateMintOrder接口,支持material_url参数(替换cover_url为必填) - 实现异步 AI 处理 Goroutine(Mock 版本:随机选择预设图片或复制
material_url) - 实现 AI 图片下载与 OSS 上传逻辑(Mock 版本:直接上传
material_url的图片到covers目录) - 实现模拟链上信息生成(
TxHash、BlockNumber、MintedAt) - 实现订单状态查询接口
GET /api/v1/assets/mints/:order_id - 实现费用回退逻辑(所有失败场景自动退回水晶)
Phase 2: 藏品查询优化(预计 1-2 天)
- 优化
GetMyAssets接口,自动为所有图片 URL 生成预签名 URL - 实现
GetAssetDetail接口,自动生成预签名 URL - 优化
GetMintOrder接口,成功时自动生成cover_url_signed - 添加状态筛选功能(
status参数)
Phase 3: 错误处理与测试(预计 2-3 天)
- 实现超时处理机制(60 秒超时)
- 实现失败场景的统一错误处理(AI 超时、AI 调用失败、OSS 上传失败)
- 实现费用回退的统一处理逻辑
- 编写单元测试和集成测试
- 编写 HTTP 完整测试流程文档
Phase 4: 真实 AI 服务集成(预计 2-3 天,后续版本)
- 集成真实的 AI 服务 API(OpenAI DALL-E、Stable Diffusion 或其他)
- 配置 AI 服务的 API Key 和 Endpoint
- 实现真实 AI 图片下载与转存逻辑
- 测试真实场景下的性能与稳定性
- 替换 Mock 版本的 AI 处理逻辑
12. 参考文档
13. 更新日志
| 版本 | 日期 | 更新内容 | 作者 |
|---|---|---|---|
| v1.0 | 2026-01-20 | 初始版本,包含完整流程设计和待确认问题 | 开发团队 |
| v1.1 | 2026-01-20 | 根据确认结果更新:AI Mock 版本、费用自动回退、预签名 URL 自动生成、轮询频率 3 秒、失败消息返回、暂不实现的功能 | 开发团队 |
文档状态: ✅ 已确认,可开始实现
下一步: 开始 Phase 1 的核心功能实现,优先完成前后端完整对接流程。