吸收数字员工步骤自动调度

This commit is contained in:
baiyanyun
2026-06-10 11:23:22 +08:00
parent 047651e404
commit d12db03216
12 changed files with 772 additions and 191 deletions

View File

@@ -26,6 +26,7 @@ import type {
IngressRouteResponse,
GovernanceCommandRequest,
GovernanceCommandResponse,
SchedulerCheckResponse,
DigitalEmployeeManagedService,
DigitalEmployeeWorkdayProjection,
DigitalEmployeeWorkdayActionRequest,
@@ -709,8 +710,8 @@ export function taskAction(taskId: string, action: string): Promise<any> {
* Current React pages must trigger scheduler checks through runSchedulerNow()
* so backend governance rejection and command errors are surfaced correctly.
*/
export function triggerCronCheck(): Promise<{ ok?: boolean; activated: number }> {
return apiFetch<{ ok?: boolean; activated: number }>('/api/cron/check', { method: 'POST' });
export function triggerCronCheck(): Promise<SchedulerCheckResponse> {
return apiFetch<SchedulerCheckResponse>('/api/cron/check', { method: 'POST' });
}
export function getAuditTrace(kind: string, id: string): Promise<any> {

View File

@@ -3241,6 +3241,7 @@ function buildNotifications(snapshot: QimingclawSnapshot | null, state: AdapterS
...approvalActionNotifications(snapshot),
...syncFailureNotifications(snapshot),
...managedServiceNotifications(snapshot),
...planStepDispatchNotifications(snapshot),
...taskNotifications(snapshot),
]);
const overlays = state.notificationStateById ?? {};
@@ -3353,6 +3354,28 @@ function taskNotifications(snapshot: QimingclawSnapshot | null): UserNotificatio
});
}
function planStepDispatchNotifications(snapshot: QimingclawSnapshot | null): UserNotification[] {
return runtimeEvents(snapshot)
.filter((event) => event.kind === 'plan_step_dispatch_failed')
.map((event) => {
const payload = asRecord(event.payload) ?? {};
const stepId = stringValue(payload.step_id) || event.id;
const reason = stringValue(payload.reason);
return {
notification_id: `plan-step-dispatch:${stepId}:${slugifyIdPart(event.occurredAt)}`,
plan_id: event.planId ?? stringValue(payload.plan_id),
task_id: event.taskId ?? stringValue(payload.task_id),
kind: 'task_failed',
title: 'Ready 步骤派发失败',
body: reason ? `${stepId}${reason}` : (event.message || stepId),
level: 'error',
status: 'unread',
correlation_id: stepId,
created_at: event.occurredAt,
} satisfies UserNotification;
});
}
function applyNotificationOverlay(notification: UserNotification, overlay?: NotificationState): UserNotification {
if (!overlay) return notification;
return {

View File

@@ -11,6 +11,7 @@ import {
postDigitalEmployeeWorkdayAction,
recordApprovalAction,
restartManagedService,
triggerCronCheck,
} from '@/lib/api';
import { isTauri } from '@/lib/tauri';
import type {
@@ -1600,8 +1601,15 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
throw new Error(response.error || '执行接口未接受请求');
}
const dispatchedCount = response.dispatched_tasks?.length ?? 0;
const schedulerResult = await triggerCronCheck();
const readyDispatched = schedulerResult.readyStepDispatch?.dispatched ?? 0;
const readyFailed = schedulerResult.readyStepDispatch?.failed ?? 0;
setExecutePlanItem(null);
setActionNotice(`已触发执行:${planItem.title},已派发 ${dispatchedCount} 个步骤。`);
setActionNotice(
readyDispatched > 0 || readyFailed > 0
? `已触发执行:${planItem.title},新建 ${dispatchedCount} 个步骤Ready 步骤自动派发 ${readyDispatched}${readyFailed > 0 ? `,失败 ${readyFailed}` : ''}`
: `已触发执行:${planItem.title},已派发 ${dispatchedCount} 个步骤。`,
);
await fetchProjection();
} catch (error) {
if (mountedRef.current) {

View File

@@ -382,6 +382,24 @@ export interface GovernanceCommandResponse {
message?: string;
}
export interface ReadyStepDispatchResult {
candidates: number;
dispatched: number;
skipped: number;
failed: number;
errors?: Array<{ stepId: string; error: string }>;
}
export interface SchedulerCheckResponse {
ok?: boolean;
checkedAt?: string;
due?: number;
activated: number;
skipped?: number;
errors?: Array<{ scheduleId: string; error: string }>;
readyStepDispatch?: ReadyStepDispatchResult;
}
// ═══════════════════════════════════════════════════════════════════════
// Console types — Debug API responses, Plan/Task/Run, Swimlane
// ═══════════════════════════════════════════════════════════════════════