feat: 生图模型可选(火山/gpt-image) + 工作台图「加入资产库」收纳 + 任务中心排序修复

- AI 工作室生图支持显式选模型:resolve_image_model 解析 volcano(Seedream 图生图,
  无 image_edit 时走 image_generation 带参考图)/ gpt-image(image_edit 多图编辑),
  未选回落系统默认;enqueue_standalone_images 透传 image_model
- 资产库收纳:Asset 加 in_library 字段(迁移 0007,既有资产 db_default=True 不动);
  工作台生成图默认 in_library=False,只在工作台展示,用户「加入资产库」后才进库列表;
  assets 视图/序列化器/library 页/types 配套
- 任务中心修复:AITaskViewSet 默认 order_by(-created_at),前端 aiTasks 取 page_size=200。
  原因:列表无排序时 MySQL 按 UUID 主键乱序返回,把一批旧失败记录顶到首页,
  前端又只取首页 20 条算 全部/已完成/失败 → 误显示「全部失败」(实为 421 成功/少量失败)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zyc
2026-06-27 10:34:12 +08:00
co-authored by Claude Opus 4.8
parent de4b20cc7b
commit 26577b99b1
15 changed files with 472 additions and 104 deletions
+21 -2
View File
@@ -466,6 +466,18 @@ export const api = {
if (meta) for (const [k, v] of Object.entries(meta)) if (v) qs.set(`m_${k}`, v);
return request<Paginated<Asset>>(`/api/assets/?${qs.toString()}`);
},
// 图片趴(模特上身图/平台套图/自由创作)按生成批次成组分页:每批一张卡(封面+张数),点开看整批图
assetBatches(params: {
tab?: string; source?: string; q?: string; ordering?: string; page?: number; pageSize?: number;
meta?: Record<string, string>;
} = {}) {
const qs = new URLSearchParams();
const { meta, pageSize, ...rest } = params;
for (const [k, v] of Object.entries(rest)) if (v !== undefined && v !== "" && v !== null) qs.set(k, String(v));
if (pageSize) qs.set("page_size", String(pageSize));
if (meta) for (const [k, v] of Object.entries(meta)) if (v) qs.set(`m_${k}`, v);
return request<Paginated<import("./types").AssetBatch>>(`/api/assets/batches/?${qs.toString()}`);
},
// 资产库各 tab 计数(tab 徽标用,不必取全量)
assetSummary() {
return request<Record<string, number>>("/api/assets/summary/");
@@ -478,6 +490,11 @@ export const api = {
submitAssetReview(id: string) {
return request<{ review_status: string; review_error: string }>(`/api/assets/${id}/submit-review/`, { method: "POST" });
},
// 单图「加入资产库 / 取消加入」:置 in_library。加入 → 出现在资产库列表;取消 → 移出列表。
// 资产生成时已落库并扣费,此口只控制是否进资产库展示列表,不增删真实资产、不二次扣费。
setAssetLibrary(id: string, inLibrary: boolean) {
return request<{ id: string; in_library: boolean }>(`/api/assets/${id}/set-library/`, { method: "POST", body: JSON.stringify({ in_library: inLibrary }) });
},
// 某 tab 下真实存在的筛选项(来源/类型/指定 metadata 键取值),供下拉「只列真有的」
assetFacets(tab?: string, metaKeys: string[] = []) {
const qs = new URLSearchParams();
@@ -563,10 +580,12 @@ export const api = {
return request<Paginated<ModelConfig>>("/api/ai/models/");
},
aiTasks() {
return request<Paginated<AITask>>("/api/ai/tasks/");
// 取一大页(后端上限 200):任务中心要按真实总量算 全部/已完成/失败 三个 tab 数,
// 只取默认首页(20 条)会把统计算偏,甚至误显示「全部失败」。
return request<Paginated<AITask>>("/api/ai/tasks/?page_size=200");
},
// 异步生图:提交后秒级返回任务列表(慢出图在 worker 跑),再用 generateImageStatus 轮询取结果
submitGenerateImage(payload: { prompt: string; mode?: "image" | "model" | "cover"; count?: number; product_id?: string; reference_product?: boolean; model_id?: string; model_entity_id?: string; ratio?: string }) {
submitGenerateImage(payload: { prompt: string; mode?: "image" | "model" | "cover"; count?: number; product_id?: string; reference_product?: boolean; model_id?: string; model_entity_id?: string; ratio?: string; image_model?: string }) {
return request<{ tasks: { id: string; status: string }[] }>("/api/ai/generate-image/", { method: "POST", body: JSON.stringify(payload) });
},
generateImageStatus(ids: string[]) {