fix(platform): 任务并发 - 账号活动任务上限统一跟随后台热配置

This commit is contained in:
ddCat
2026-07-27 14:23:18 +08:00
parent 0b064e532a
commit dd4af80b98
9 changed files with 58 additions and 122 deletions

View File

@@ -41,7 +41,7 @@ const resourceFields: PolicyField[] = [
const concurrencyFields: PolicyField[] = [
{ group: "task", name: "workerConcurrency", label: "Worker 并发", extra: "集群同时执行的后台任务数。", unit: "个", max: 999 },
{ group: "task", name: "channelConcurrency", label: "全局渠道并发", extra: "渠道选择跟随系统时采用的并发上限。", unit: "个", max: 999 },
{ group: "task", name: "activeTaskLimit", label: "账号活动任务", extra: "单账号同时排队或运行的任务数。", unit: "个", max: 999 },
{ group: "task", name: "activeTaskLimit", label: "账号活动任务", extra: "单账号跨项目同时排队或运行的任务总数。", unit: "个", max: 999 },
];
const timeoutFields: PolicyField[] = [

View File

@@ -1,6 +1,6 @@
import { useEffect, useMemo, useState, type ReactNode } from "react";
import { useMutation } from "@tanstack/react-query";
import { App, Button, Input, InputNumber, Modal, Select, Tooltip } from "antd";
import { App, Button, Input, Modal, Select, Tooltip } from "antd";
import { Archive, Check, Save, ShieldAlert } from "lucide-react";
import { canvasStylePresets } from "@/components/canvas/canvas-style-picker-modal";
@@ -16,16 +16,15 @@ export default function ProjectSettingsView({ detail, refreshProject }: ProjectD
const [aspectRatio, setAspectRatio] = useState(project.aspectRatio);
const [sourceType, setSourceType] = useState(project.sourceType);
const [stylePresetId, setStylePresetId] = useState(project.stylePresetId || "");
const [activeTaskLimit, setActiveTaskLimit] = useState(project.activeTaskLimit || 3);
const [archiveOpen, setArchiveOpen] = useState(false);
useEffect(() => { setName(project.name); setDescription(project.description || ""); setAspectRatio(project.aspectRatio); setSourceType(project.sourceType); setStylePresetId(project.stylePresetId || ""); setActiveTaskLimit(project.activeTaskLimit || 3); }, [project]);
const dirty = useMemo(() => name.trim() !== project.name || description !== (project.description || "") || aspectRatio !== project.aspectRatio || sourceType !== project.sourceType || stylePresetId !== (project.stylePresetId || "") || activeTaskLimit !== (project.activeTaskLimit || 3), [activeTaskLimit, aspectRatio, description, name, project, sourceType, stylePresetId]);
const saveMutation = useMutation({ mutationFn: () => updateProject(project.id, { name: name.trim(), description, aspectRatio, sourceType, stylePresetId, activeTaskLimit }), onSuccess: () => { refreshProject(); message.success("项目设置已保存"); }, onError: (error) => message.error(error instanceof Error ? error.message : "项目设置保存失败") });
useEffect(() => { setName(project.name); setDescription(project.description || ""); setAspectRatio(project.aspectRatio); setSourceType(project.sourceType); setStylePresetId(project.stylePresetId || ""); }, [project]);
const dirty = useMemo(() => name.trim() !== project.name || description !== (project.description || "") || aspectRatio !== project.aspectRatio || sourceType !== project.sourceType || stylePresetId !== (project.stylePresetId || ""), [aspectRatio, description, name, project, sourceType, stylePresetId]);
const saveMutation = useMutation({ mutationFn: () => updateProject(project.id, { name: name.trim(), description, aspectRatio, sourceType, stylePresetId }), onSuccess: () => { refreshProject(); message.success("项目设置已保存"); }, onError: (error) => message.error(error instanceof Error ? error.message : "项目设置保存失败") });
const archiveMutation = useMutation({ mutationFn: () => updateProject(project.id, { status: project.status === "archived" ? "active" : "archived" }), onSuccess: () => { setArchiveOpen(false); refreshProject(); message.success(project.status === "archived" ? "项目已恢复" : "项目已归档"); }, onError: (error) => message.error(error instanceof Error ? error.message : "项目状态更新失败") });
return (
<div>
<header className="flex items-end justify-between gap-3 border-b border-border/70 pb-3"><div><h2 className="text-lg font-semibold">项目设置</h2><p className="mt-1 text-xs text-foreground/48">基础信息、项目画风与运行限额</p></div><Button type={dirty ? "primary" : "default"} icon={dirty ? <Save className="size-3.5" /> : <Check className="size-3.5" />} disabled={!dirty || !name.trim()} loading={saveMutation.isPending} onClick={() => saveMutation.mutate()}>{dirty ? "保存设置" : "已保存"}</Button></header>
<header className="flex items-end justify-between gap-3 border-b border-border/70 pb-3"><div><h2 className="text-lg font-semibold">项目设置</h2><p className="mt-1 text-xs text-foreground/48">基础信息、项目画风与归档管理</p></div><Button type={dirty ? "primary" : "default"} icon={dirty ? <Save className="size-3.5" /> : <Check className="size-3.5" />} disabled={!dirty || !name.trim()} loading={saveMutation.isPending} onClick={() => saveMutation.mutate()}>{dirty ? "保存设置" : "已保存"}</Button></header>
<section className="border-b border-border/70 py-4">
<h3 className="mb-3 text-sm font-semibold">基础设置</h3>
@@ -33,8 +32,7 @@ export default function ProjectSettingsView({ detail, refreshProject }: ProjectD
<Field label="项目名称" className="xl:col-span-2"><Input value={name} onChange={(event) => setName(event.target.value)} /></Field>
<Field label="默认画幅"><Select className="w-full" value={aspectRatio} options={[{ label: "9:16 · 竖屏短剧", value: "9:16" }, { label: "16:9 · 横屏", value: "16:9" }, { label: "1:1 · 方形", value: "1:1" }]} onChange={setAspectRatio} /></Field>
<Field label="内容来源"><Select className="w-full" value={sourceType} options={[{ label: "空白开始", value: "blank" }, { label: "导入小说", value: "novel" }, { label: "粘贴文本", value: "text" }]} onChange={setSourceType} /></Field>
<Field label="项目简介" className="md:col-span-2 xl:col-span-3"><Input value={description} onChange={(event) => setDescription(event.target.value)} placeholder="一句话说明项目目标" /></Field>
<Field label="活跃任务上限(个)"><InputNumber className="!w-full" min={1} max={20} value={activeTaskLimit} onChange={(value) => setActiveTaskLimit(value || 1)} /></Field>
<Field label="项目简介" className="md:col-span-2 xl:col-span-4"><Input value={description} onChange={(event) => setDescription(event.target.value)} placeholder="一句话说明项目目标" /></Field>
</div>
</section>

View File

@@ -13,7 +13,6 @@ export type Project = {
sourceType: string;
description: string;
stylePresetId: string;
activeTaskLimit: number;
status: "active" | "archived" | string;
revision: number;
createdAt: string;
@@ -187,11 +186,11 @@ export function getProject(id: string) {
return request<ProjectDetail>(api.get(`/projects/${encodeURIComponent(id)}`));
}
export function createProject(input: { name: string; type: string; aspectRatio: string; sourceType: string; description?: string; stylePresetId?: string; activeTaskLimit?: number }) {
export function createProject(input: { name: string; type: string; aspectRatio: string; sourceType: string; description?: string; stylePresetId?: string }) {
return request<{ project: Project }>(api.post("/projects", input));
}
export function updateProject(projectId: string, input: Partial<Pick<Project, "name" | "type" | "aspectRatio" | "sourceType" | "description" | "stylePresetId" | "activeTaskLimit" | "status">>) {
export function updateProject(projectId: string, input: Partial<Pick<Project, "name" | "type" | "aspectRatio" | "sourceType" | "description" | "stylePresetId" | "status">>) {
return request<{ project: Project }>(api.patch(`/projects/${encodeURIComponent(projectId)}`, input));
}