修复泥点消耗刷新不及时 (#138)
refactor: - 主站复用game agent的zustand store fix: - 修复旧请求覆盖问题 - 修复账号切换问题 - 主站原本已经每 4 秒轮询 external generation 任务状态, 在这里补充触发余额刷新的时机 --------- Co-authored-by: 段舒康 <kdletters@qq.com> Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/138 Co-authored-by: 王德宇 <kvtodev@outlook.com> Co-committed-by: 王德宇 <kvtodev@outlook.com>
This commit was merged in pull request #138.
This commit is contained in:
@@ -26,6 +26,7 @@ const COMPLETED_TASK_LIST_LIMIT = 20;
|
||||
const COMPLETED_TASK_LIST_MAX_LIMIT = 100;
|
||||
const ACTIVE_TASK_LIST_LIMIT = 100;
|
||||
const ACTIVE_TASK_POLL_INTERVAL_MS = 4000;
|
||||
const TASK_LIST_BOOTSTRAP_RETRY_DELAYS_MS = [1000, 2000, 4000] as const;
|
||||
|
||||
type TaskSidebarTab = 'active' | 'completed';
|
||||
|
||||
@@ -49,6 +50,7 @@ type ImageCanvasTaskSidebarViewProps = {
|
||||
onToggleOpen: () => void;
|
||||
onFocusExternalTask: (task: ExternalGenerationTaskRecord) => void;
|
||||
onExternalTasksCompleted?: (tasks: ExternalGenerationTaskRecord[]) => void;
|
||||
onExternalTaskWalletMayHaveChanged?: () => void;
|
||||
};
|
||||
|
||||
function parseTaskTimeMs(value?: string | null) {
|
||||
@@ -280,6 +282,7 @@ export function ImageCanvasTaskSidebarView({
|
||||
onToggleOpen,
|
||||
onFocusExternalTask,
|
||||
onExternalTasksCompleted,
|
||||
onExternalTaskWalletMayHaveChanged,
|
||||
}: ImageCanvasTaskSidebarViewProps) {
|
||||
const { refreshCanvas } = useImageCanvasActions();
|
||||
const projectId = useImageCanvasContextStore((state) => state.projectId);
|
||||
@@ -289,6 +292,8 @@ export function ImageCanvasTaskSidebarView({
|
||||
const [externalTasks, setExternalTasks] = useState<
|
||||
ExternalGenerationTaskRecord[]
|
||||
>([]);
|
||||
const [walletActiveExternalTaskIds, setWalletActiveExternalTaskIds] =
|
||||
useState<string[]>([]);
|
||||
const [completedListState, setCompletedListState] = useState(() => ({
|
||||
limit: COMPLETED_TASK_LIST_LIMIT,
|
||||
projectId: normalizedProjectId,
|
||||
@@ -353,54 +358,123 @@ export function ImageCanvasTaskSidebarView({
|
||||
|
||||
useEffect(() => {
|
||||
let disposed = false;
|
||||
const controller = new AbortController();
|
||||
Promise.all([
|
||||
listExternalGenerationTasks({
|
||||
let controller: AbortController | null = null;
|
||||
let retryTimerId: number | null = null;
|
||||
let retryIndex = 0;
|
||||
let hasLoadedActiveTasks = false;
|
||||
let hasLoadedCompletedTasks = false;
|
||||
const loadTaskLists = () => {
|
||||
const attemptController = new AbortController();
|
||||
controller = attemptController;
|
||||
const activeTasksRequest = listExternalGenerationTasks({
|
||||
limit: ACTIVE_TASK_LIST_LIMIT,
|
||||
includeAcknowledgedTerminal: false,
|
||||
statuses: ['running', 'queued'],
|
||||
signal: controller.signal,
|
||||
}),
|
||||
listExternalGenerationTasks({
|
||||
limit: completedListLimit,
|
||||
includeAcknowledgedTerminal: true,
|
||||
statuses: ['completed', 'failed'],
|
||||
signal: controller.signal,
|
||||
}),
|
||||
])
|
||||
.then(([activeResponse, completedResponse]) => {
|
||||
if (disposed) {
|
||||
signal: attemptController.signal,
|
||||
}).then((activeResponse) => {
|
||||
if (disposed || attemptController.signal.aborted) {
|
||||
return;
|
||||
}
|
||||
const visibleActiveTasks = filterVisibleExternalTasks(
|
||||
activeResponse.tasks,
|
||||
);
|
||||
const visibleCompletedTasks = filterVisibleExternalTasks(
|
||||
completedResponse.tasks,
|
||||
);
|
||||
notifyCompletedExternalTasks(visibleCompletedTasks, {
|
||||
unacknowledgedOnly: true,
|
||||
});
|
||||
setExternalTasks(
|
||||
hasLoadedActiveTasks = true;
|
||||
const activeTasks = activeResponse.tasks.filter(isActiveExternalTask);
|
||||
const visibleActiveTasks = filterVisibleExternalTasks(activeTasks);
|
||||
setWalletActiveExternalTaskIds(activeTasks.map((task) => task.jobId));
|
||||
if (activeTasks.length > 0) {
|
||||
onExternalTaskWalletMayHaveChanged?.();
|
||||
}
|
||||
setExternalTasks((currentTasks) =>
|
||||
trimStoredExternalTasks(
|
||||
mergeExternalTasks(visibleActiveTasks, visibleCompletedTasks),
|
||||
mergeExternalTasks(
|
||||
visibleActiveTasks,
|
||||
currentTasks.filter(isTerminalExternalTask),
|
||||
),
|
||||
completedListLimit,
|
||||
),
|
||||
);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!disposed) {
|
||||
setExternalTasks([]);
|
||||
}
|
||||
});
|
||||
const completedTasksRequest = listExternalGenerationTasks({
|
||||
limit: completedListLimit,
|
||||
includeAcknowledgedTerminal: true,
|
||||
statuses: ['completed', 'failed'],
|
||||
signal: attemptController.signal,
|
||||
}).then((completedResponse) => {
|
||||
if (disposed || attemptController.signal.aborted) {
|
||||
return;
|
||||
}
|
||||
hasLoadedCompletedTasks = true;
|
||||
const visibleCompletedTasks = filterVisibleExternalTasks(
|
||||
completedResponse.tasks,
|
||||
);
|
||||
if (
|
||||
completedResponse.tasks.some(
|
||||
(task) =>
|
||||
isTerminalExternalTask(task) && !task.notificationAcknowledgedAt,
|
||||
)
|
||||
) {
|
||||
onExternalTaskWalletMayHaveChanged?.();
|
||||
}
|
||||
notifyCompletedExternalTasks(visibleCompletedTasks, {
|
||||
unacknowledgedOnly: true,
|
||||
});
|
||||
setExternalTasks((currentTasks) =>
|
||||
trimStoredExternalTasks(
|
||||
mergeExternalTasks(
|
||||
currentTasks.filter(isActiveExternalTask),
|
||||
visibleCompletedTasks,
|
||||
),
|
||||
completedListLimit,
|
||||
),
|
||||
);
|
||||
});
|
||||
Promise.all([activeTasksRequest, completedTasksRequest])
|
||||
.catch(() => {
|
||||
attemptController.abort();
|
||||
if (controller === attemptController) {
|
||||
controller = null;
|
||||
}
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
if (retryIndex >= TASK_LIST_BOOTSTRAP_RETRY_DELAYS_MS.length) {
|
||||
setExternalTasks((currentTasks) =>
|
||||
trimStoredExternalTasks(
|
||||
currentTasks.filter(
|
||||
(task) =>
|
||||
(hasLoadedActiveTasks && isActiveExternalTask(task)) ||
|
||||
(hasLoadedCompletedTasks && isTerminalExternalTask(task)),
|
||||
),
|
||||
completedListLimit,
|
||||
),
|
||||
);
|
||||
if (!hasLoadedActiveTasks) {
|
||||
setWalletActiveExternalTaskIds([]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const retryDelayMs = TASK_LIST_BOOTSTRAP_RETRY_DELAYS_MS[retryIndex];
|
||||
retryIndex += 1;
|
||||
retryTimerId = window.setTimeout(loadTaskLists, retryDelayMs);
|
||||
})
|
||||
.finally(() => {
|
||||
if (controller === attemptController) {
|
||||
controller = null;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
loadTaskLists();
|
||||
return () => {
|
||||
disposed = true;
|
||||
controller.abort();
|
||||
controller?.abort();
|
||||
if (retryTimerId !== null) {
|
||||
window.clearTimeout(retryTimerId);
|
||||
}
|
||||
};
|
||||
}, [
|
||||
completedListLimit,
|
||||
filterVisibleExternalTasks,
|
||||
notifyCompletedExternalTasks,
|
||||
onExternalTaskWalletMayHaveChanged,
|
||||
refreshKey,
|
||||
]);
|
||||
|
||||
@@ -412,13 +486,14 @@ export function ImageCanvasTaskSidebarView({
|
||||
[externalTasks],
|
||||
);
|
||||
const activeExternalTaskKey = activeExternalTaskIds.join('|');
|
||||
const walletActiveExternalTaskKey = walletActiveExternalTaskIds.join('|');
|
||||
|
||||
useEffect(() => {
|
||||
activeExternalTaskIdsRef.current = new Set(activeExternalTaskIds);
|
||||
}, [activeExternalTaskKey, activeExternalTaskIds]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeExternalTaskIds.length) {
|
||||
if (!walletActiveExternalTaskKey) {
|
||||
return undefined;
|
||||
}
|
||||
let disposed = false;
|
||||
@@ -448,6 +523,15 @@ export function ImageCanvasTaskSidebarView({
|
||||
filterVisibleExternalTasks(activeResponse.tasks),
|
||||
filterVisibleExternalTasks(completedResponse.tasks),
|
||||
);
|
||||
setWalletActiveExternalTaskIds(
|
||||
activeResponse.tasks
|
||||
.filter(isActiveExternalTask)
|
||||
.map((task) => task.jobId),
|
||||
);
|
||||
// external job 在 worker 领取后才预扣泥点,running 可能先于扣费落账。
|
||||
// 只要仍有全局 active task,每轮成功轮询都推动钱包收敛;终态这一轮
|
||||
// 同时覆盖成功结算或失败退款。共享 Store 会合并并发刷新。
|
||||
onExternalTaskWalletMayHaveChanged?.();
|
||||
notifyCompletedExternalTasks(refreshedTasks, {
|
||||
activeTaskIds: activeExternalTaskIdsRef.current,
|
||||
});
|
||||
@@ -483,11 +567,11 @@ export function ImageCanvasTaskSidebarView({
|
||||
}
|
||||
};
|
||||
}, [
|
||||
activeExternalTaskIds,
|
||||
activeExternalTaskKey,
|
||||
completedListLimit,
|
||||
filterVisibleExternalTasks,
|
||||
notifyCompletedExternalTasks,
|
||||
onExternalTaskWalletMayHaveChanged,
|
||||
walletActiveExternalTaskKey,
|
||||
]);
|
||||
|
||||
const handleTaskListScroll = useCallback(
|
||||
|
||||
Reference in New Issue
Block a user