吸收数字员工风险策略本地管理

This commit is contained in:
baiyanyun
2026-06-11 21:14:29 +08:00
parent 245a210b25
commit 84d34236e8
10 changed files with 394 additions and 189 deletions

View File

@@ -18,6 +18,7 @@ import type {
PlanStepDispatchPolicy,
PlanStepDispatchPolicyPullResult,
ReadyStepDispatchResult,
RiskApprovalPolicy,
NotificationPreferences,
NotificationUpdatesPullResult,
UserNotification,
@@ -568,7 +569,7 @@ export function getEventLog(params: {
// Notifications
// ---------------------------------------------------------------------------
export { type ApprovalSubscriptionPreferences, type NotificationPreferences, type NotificationUpdatesPullResult, type PlanStepDispatchPolicy, type UserNotification };
export { type ApprovalSubscriptionPreferences, type NotificationPreferences, type NotificationUpdatesPullResult, type PlanStepDispatchPolicy, type RiskApprovalPolicy, type UserNotification };
export function getNotifications(
status: string = 'unread',
@@ -663,6 +664,17 @@ export function pullRiskApprovalPolicy(): Promise<{ ok: boolean; skipped?: boole
});
}
export function getRiskApprovalPolicy(): Promise<RiskApprovalPolicy> {
return apiFetch<RiskApprovalPolicy>('/api/risk-approval/policy');
}
export function patchRiskApprovalPolicy(patch: Partial<RiskApprovalPolicy>): Promise<RiskApprovalPolicy> {
return apiFetch<RiskApprovalPolicy>('/api/risk-approval/policy', {
method: 'PATCH',
body: JSON.stringify(patch),
});
}
export function pullRouteDecisionPolicy(): Promise<RouteDecisionPolicyPullResult> {
return apiFetch<RouteDecisionPolicyPullResult>('/api/route-decision/policy/pull', {
method: 'POST',

View File

@@ -38,6 +38,7 @@ import type {
PlanStepDispatchPolicy,
PlanStepDispatchPolicyPullResult,
PlanStepSummary,
RiskApprovalPolicy,
RouteDecisionPolicy,
RouteDecisionPolicyPullResult,
RouteDecisionRecord,
@@ -113,6 +114,7 @@ interface AdapterState {
notificationPreferences?: NotificationPreferences;
approvalSubscriptionPreferences?: ApprovalSubscriptionPreferences;
planStepDispatchPolicy?: PlanStepDispatchPolicy;
riskApprovalPolicy?: RiskApprovalPolicy;
routeDecisionPolicy?: RouteDecisionPolicy;
consoleRole?: string;
rolePreferences?: Record<string, unknown>;
@@ -909,6 +911,15 @@ export async function handleQimingclawDigitalApi<T>(
if (method === 'GET' && pathname === '/api/plan-step/dispatch-policy') {
return planStepDispatchPolicy(notificationAdapterState(await readQimingclawSnapshot())) as T;
}
if (method === 'GET' && pathname === '/api/risk-approval/policy') {
return riskApprovalPolicy(notificationAdapterState(await readQimingclawSnapshot())) as T;
}
if (method === 'PATCH' && pathname === '/api/risk-approval/policy') {
return await handleRiskApprovalPolicyPatch(
parseJsonBody<Partial<RiskApprovalPolicy>>(options.body),
await readQimingclawSnapshot(),
) as T;
}
if (method === 'POST' && pathname === '/api/plan-step/dispatch-policy/pull') {
return await handlePlanStepDispatchPolicyPull(await readQimingclawSnapshot()) as T;
}
@@ -1140,6 +1151,7 @@ function defaultState(): AdapterState {
notificationPreferences: defaultNotificationPreferences(),
approvalSubscriptionPreferences: defaultApprovalSubscriptionPreferences(),
planStepDispatchPolicy: defaultPlanStepDispatchPolicy(),
riskApprovalPolicy: defaultRiskApprovalPolicy(),
routeDecisionPolicy: defaultRouteDecisionPolicy(),
consoleRole: 'sales',
rolePreferences: {},
@@ -1168,6 +1180,7 @@ function normalizeAdapterState(value: Partial<AdapterState>): AdapterState {
notificationPreferences: notificationPreferences(value),
approvalSubscriptionPreferences: approvalSubscriptionPreferences(value),
planStepDispatchPolicy: planStepDispatchPolicy(value),
riskApprovalPolicy: riskApprovalPolicy(value),
routeDecisionPolicy: routeDecisionPolicy(value),
};
}
@@ -4384,6 +4397,20 @@ function defaultPlanStepDispatchPolicy(): PlanStepDispatchPolicy {
};
}
function defaultRiskApprovalPolicy(): RiskApprovalPolicy {
return {
enabled: true,
approval_required_risk_levels: ['high', 'critical'],
approval_required_actions: [],
auto_reject_actions: [],
updated_at: null,
source: 'local',
remote_updated_at: null,
last_pulled_at: null,
last_pull_error: null,
};
}
function defaultRouteDecisionPolicy(): RouteDecisionPolicy {
return {
enabled: true,
@@ -4435,6 +4462,25 @@ function planStepDispatchPolicy(state: Partial<AdapterState> | null | undefined)
};
}
function riskApprovalPolicy(state: Partial<AdapterState> | null | undefined): RiskApprovalPolicy {
const stored = state?.riskApprovalPolicy;
const fallback = defaultRiskApprovalPolicy();
if (!stored || typeof stored !== 'object') return fallback;
const riskLevels = uniqueStrings(arrayValue(stored.approval_required_risk_levels).map((value) => stringValue(value).toLowerCase()))
.filter((value) => ['normal', 'medium', 'high', 'critical'].includes(value));
return {
enabled: stored.enabled !== false,
approval_required_risk_levels: riskLevels.length > 0 ? riskLevels : fallback.approval_required_risk_levels,
approval_required_actions: uniqueStrings(arrayValue(stored.approval_required_actions)),
auto_reject_actions: uniqueStrings(arrayValue(stored.auto_reject_actions)),
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,
};
}
function routeDecisionPolicy(state: Partial<AdapterState> | null | undefined): RouteDecisionPolicy {
const stored = state?.routeDecisionPolicy;
const fallback = defaultRouteDecisionPolicy();
@@ -4561,6 +4607,33 @@ async function handlePlanStepDispatchPolicyPatch(
return next;
}
async function handleRiskApprovalPolicyPatch(
patch: Partial<RiskApprovalPolicy>,
snapshot: QimingclawSnapshot | null,
): Promise<RiskApprovalPolicy> {
const state = snapshot?.uiState ?? await readBridgeUiState() ?? readState();
const current = riskApprovalPolicy(state);
const next = riskApprovalPolicy({
riskApprovalPolicy: {
...current,
...patch,
source: 'local',
updated_at: new Date().toISOString(),
last_pull_error: null,
},
});
await saveState(
{
...normalizeAdapterState(state),
riskApprovalPolicy: next,
},
'update_risk_approval_policy',
undefined,
{ risk_approval_policy: next },
);
return next;
}
async function handlePlanStepDispatchPolicyPull(
snapshot: QimingclawSnapshot | null,
): Promise<PlanStepDispatchPolicyPullResult> {