吸收数字员工PlanStep远端策略拉取
This commit is contained in:
@@ -16,6 +16,7 @@ import type {
|
||||
BusinessViewResponse,
|
||||
PlanStepBusinessRow,
|
||||
PlanStepDispatchPolicy,
|
||||
PlanStepDispatchPolicyPullResult,
|
||||
ReadyStepDispatchResult,
|
||||
NotificationPreferences,
|
||||
UserNotification,
|
||||
@@ -612,6 +613,12 @@ export function patchPlanStepDispatchPolicy(
|
||||
});
|
||||
}
|
||||
|
||||
export function pullPlanStepDispatchPolicy(): Promise<PlanStepDispatchPolicyPullResult> {
|
||||
return apiFetch<PlanStepDispatchPolicyPullResult>('/api/plan-step/dispatch-policy/pull', {
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
|
||||
export function getPlanSteps(params: Record<string, string | number | null | undefined> = {}): Promise<BusinessViewResponse<PlanStepBusinessRow>> {
|
||||
const query = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
|
||||
@@ -36,6 +36,7 @@ import type {
|
||||
PlanDispatchResponse,
|
||||
ReadyStepDispatchResult,
|
||||
PlanStepDispatchPolicy,
|
||||
PlanStepDispatchPolicyPullResult,
|
||||
PlanStepSummary,
|
||||
RouteDecisionRecord,
|
||||
RouteDecisionTimelineResponse,
|
||||
@@ -60,6 +61,7 @@ declare global {
|
||||
saveUiState?: (update: { state: AdapterState; action?: string; date?: string; metadata?: Record<string, unknown> }) => Promise<AdapterState>;
|
||||
getSyncStatus?: () => Promise<QimingclawSyncStatus>;
|
||||
flushSync?: () => Promise<QimingclawSyncStatus>;
|
||||
pullPlanStepDispatchPolicy?: () => Promise<PlanStepDispatchPolicyPullResult>;
|
||||
getCronSettings?: () => Promise<QimingclawCronSettings>;
|
||||
saveCronSettings?: (patch: Partial<QimingclawCronSettings>) => Promise<QimingclawCronSettings>;
|
||||
runSchedulerCheck?: () => Promise<{ activated: number; due?: number; skipped?: number }>;
|
||||
@@ -791,6 +793,9 @@ export async function handleQimingclawDigitalApi<T>(
|
||||
if (method === 'GET' && pathname === '/api/plan-step/dispatch-policy') {
|
||||
return planStepDispatchPolicy(notificationAdapterState(await readQimingclawSnapshot())) as T;
|
||||
}
|
||||
if (method === 'POST' && pathname === '/api/plan-step/dispatch-policy/pull') {
|
||||
return await handlePlanStepDispatchPolicyPull(await readQimingclawSnapshot()) as T;
|
||||
}
|
||||
if (method === 'PATCH' && pathname === '/api/plan-step/dispatch-policy') {
|
||||
return await handlePlanStepDispatchPolicyPatch(
|
||||
parseJsonBody<Partial<PlanStepDispatchPolicy>>(options.body),
|
||||
@@ -3883,6 +3888,10 @@ function defaultPlanStepDispatchPolicy(): PlanStepDispatchPolicy {
|
||||
max_per_sweep: 3,
|
||||
auto_dispatch_ready_steps: true,
|
||||
updated_at: null,
|
||||
source: 'local',
|
||||
remote_updated_at: null,
|
||||
last_pulled_at: null,
|
||||
last_pull_error: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3915,6 +3924,10 @@ function planStepDispatchPolicy(state: Partial<AdapterState> | null | undefined)
|
||||
max_per_sweep: Number.isFinite(maxPerSweep) ? Math.max(1, Math.min(Math.floor(maxPerSweep), 10)) : fallback.max_per_sweep,
|
||||
auto_dispatch_ready_steps: stored.auto_dispatch_ready_steps !== false,
|
||||
updated_at: typeof stored.updated_at === 'string' ? stored.updated_at : fallback.updated_at,
|
||||
source: stored.source === 'remote' ? 'remote' : 'local',
|
||||
remote_updated_at: typeof stored.remote_updated_at === 'string' ? stored.remote_updated_at : null,
|
||||
last_pulled_at: typeof stored.last_pulled_at === 'string' ? stored.last_pulled_at : null,
|
||||
last_pull_error: typeof stored.last_pull_error === 'string' ? stored.last_pull_error : null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4021,6 +4034,27 @@ async function handlePlanStepDispatchPolicyPatch(
|
||||
return next;
|
||||
}
|
||||
|
||||
async function handlePlanStepDispatchPolicyPull(
|
||||
snapshot: QimingclawSnapshot | null,
|
||||
): Promise<PlanStepDispatchPolicyPullResult> {
|
||||
const fallbackPolicy = planStepDispatchPolicy(snapshot?.uiState ?? readState());
|
||||
const bridgeResult = await window.QimingClawBridge?.digital?.pullPlanStepDispatchPolicy?.().catch((error) => ({
|
||||
ok: false,
|
||||
endpoint: null,
|
||||
pulled_at: new Date().toISOString(),
|
||||
policy: fallbackPolicy,
|
||||
error: error instanceof Error ? error.message : 'plan step dispatch policy pull failed',
|
||||
}));
|
||||
if (bridgeResult) return bridgeResult;
|
||||
return {
|
||||
ok: false,
|
||||
endpoint: null,
|
||||
pulled_at: new Date().toISOString(),
|
||||
policy: fallbackPolicy,
|
||||
error: 'qimingclaw_bridge_unavailable',
|
||||
};
|
||||
}
|
||||
|
||||
function approvalNotifications(snapshot: QimingclawSnapshot | null): UserNotification[] {
|
||||
const summaries = new Map(businessApprovalRows(snapshot).map((approval) => [stringValue(approval.approval_id), approval]));
|
||||
return buildApprovals(snapshot)
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
getSchedulerJobs,
|
||||
governanceCommand,
|
||||
postDigitalEmployeeWorkdayAction,
|
||||
pullPlanStepDispatchPolicy,
|
||||
recordApprovalAction,
|
||||
restartManagedService,
|
||||
} from '@/lib/api';
|
||||
@@ -1368,6 +1369,28 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
|
||||
}
|
||||
}, [selectedDate]);
|
||||
|
||||
const refreshPlanStepDispatchPolicy = useCallback(async () => {
|
||||
setActionBusy('pull_plan_step_dispatch_policy');
|
||||
setActionError(null);
|
||||
setActionNotice(null);
|
||||
try {
|
||||
const result = await pullPlanStepDispatchPolicy();
|
||||
if (!mountedRef.current) return;
|
||||
await fetchProjection();
|
||||
if (!mountedRef.current) return;
|
||||
const policy = result.policy;
|
||||
setActionNotice(result.ok
|
||||
? `PlanStep 派发策略已拉取:${policy.enabled ? '启用' : '停用'},每轮最多 ${policy.max_per_sweep} 个。`
|
||||
: `PlanStep 派发策略拉取失败,已沿用本地策略:${result.error || '未知错误'}。`);
|
||||
} catch (error) {
|
||||
if (mountedRef.current) {
|
||||
setActionError(error instanceof Error ? error.message : '拉取PlanStep派发策略失败');
|
||||
}
|
||||
} finally {
|
||||
if (mountedRef.current) setActionBusy(null);
|
||||
}
|
||||
}, [fetchProjection]);
|
||||
|
||||
const copySyncFailureDiagnostic = useCallback(async (failure: ManagementSyncFailure) => {
|
||||
const diagnostic = buildSyncFailureDiagnostic(failure, managementSyncStatus);
|
||||
try {
|
||||
@@ -2016,6 +2039,16 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
|
||||
<RefreshCw size={13} aria-hidden="true" />
|
||||
<span>{actionBusy === 'flush_management_sync' ? '同步中...' : '同步管理端'}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="de-btn-secondary de-btn-sm"
|
||||
disabled={projectionLoading || actionBusy === 'pull_plan_step_dispatch_policy'}
|
||||
onClick={refreshPlanStepDispatchPolicy}
|
||||
title="从管理端拉取 PlanStep 派发策略"
|
||||
>
|
||||
<ShieldCheck size={13} aria-hidden="true" />
|
||||
<span>{actionBusy === 'pull_plan_step_dispatch_policy' ? '拉取中...' : '拉取策略'}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={`de-management-sync-strip de-management-sync-strip--${managementSyncTone}`} title={managementSyncError || managementSyncSummary}>
|
||||
|
||||
@@ -200,6 +200,19 @@ export interface PlanStepDispatchPolicy {
|
||||
max_per_sweep: number;
|
||||
auto_dispatch_ready_steps: boolean;
|
||||
updated_at: string | null;
|
||||
source?: 'local' | 'remote';
|
||||
remote_updated_at?: string | null;
|
||||
last_pulled_at?: string | null;
|
||||
last_pull_error?: string | null;
|
||||
}
|
||||
|
||||
export interface PlanStepDispatchPolicyPullResult {
|
||||
ok: boolean;
|
||||
skipped?: boolean;
|
||||
endpoint: string | null;
|
||||
pulled_at: string | null;
|
||||
policy: PlanStepDispatchPolicy;
|
||||
error?: string | null;
|
||||
}
|
||||
|
||||
export interface PlanStepDependencyDetail {
|
||||
|
||||
Reference in New Issue
Block a user