吸收数字员工工作台入口重构收口

This commit is contained in:
baiyanyun
2026-06-29 19:53:34 +08:00
parent d0b7223b53
commit f84dc1d44e
42 changed files with 18514 additions and 3816 deletions

View File

@@ -0,0 +1,121 @@
import type {
ApprovalDecisionView,
GovernanceCommandResponse,
UserNotification,
} from '@/types/api';
import { governanceCommand } from './api';
export type ApprovalDecisionResolution = 'approve' | 'reject';
const REJECT_ACTION_IDS = new Set(['reject', 'rejected', 'defer', 'deferred']);
const APPROVE_SHORTCUTS = new Set(['同意', '批准', '通过', '同意执行', '批准执行', 'approve', 'approved', 'yes', 'ok']);
const REJECT_SHORTCUTS = new Set(['拒绝', '驳回', '不同意', '否决', '拒绝执行', '驳回执行', 'reject', 'rejected', 'no']);
export function isApprovalOpen(status?: string | null): boolean {
return ['pending', 'requested'].includes(String(status || '').toLowerCase());
}
export function approvalDecisionId(decision: ApprovalDecisionView): string {
return decision.approval_id || decision.id;
}
export function approvalResolutionFromAction(actionId: string): ApprovalDecisionResolution {
return REJECT_ACTION_IDS.has(actionId.toLowerCase()) ? 'reject' : 'approve';
}
export function parseApprovalShortcut(input: string): ApprovalDecisionResolution | null {
const normalized = input
.trim()
.toLowerCase()
.replace(/[\s。.!?]/g, '');
if (APPROVE_SHORTCUTS.has(normalized)) return 'approve';
if (REJECT_SHORTCUTS.has(normalized)) return 'reject';
return null;
}
export function notificationToApprovalDecision(
notification: UserNotification,
): ApprovalDecisionView | null {
if (notification.kind !== 'need_approval' || notification.status !== 'unread') {
return null;
}
const approvalId = notification.approval_id || notification.correlation_id;
if (!approvalId) return null;
return {
id: approvalId,
approval_id: approvalId,
plan_id: notification.plan_id || null,
task_id: notification.task_id || null,
run_id: null,
target_kind: notification.task_id ? 'task' : null,
target_id: notification.task_id || notification.plan_id || approvalId,
title: notification.title || '真实执行事项需要确认',
scenario: notification.body || '请确认下一步处理方式。',
status: 'requested',
status_label: '待决策',
plan_title: notification.plan_id || '真实计划',
task_title: notification.task_id || approvalId,
actions: [
{ id: 'approve', label: '同意', tone: 'primary' },
{ id: 'reject', label: '驳回', tone: 'secondary' },
],
};
}
export function mergeApprovalDecisions(
canonical: ApprovalDecisionView[],
legacy: ApprovalDecisionView[],
): ApprovalDecisionView[] {
const merged = new Map<string, ApprovalDecisionView>();
canonical.forEach((decision) => {
merged.set(approvalDecisionId(decision), decision);
});
legacy.forEach((decision) => {
const id = approvalDecisionId(decision);
if (!merged.has(id)) merged.set(id, decision);
});
return Array.from(merged.values());
}
export async function submitApprovalDecision(
decision: ApprovalDecisionView,
actionIdOrResolution: string,
options: {
source: string;
comment?: string;
correlationPrefix?: string;
},
): Promise<GovernanceCommandResponse> {
const approvalId = approvalDecisionId(decision);
if (!approvalId) {
throw new Error('缺少 approval_id无法处理审批。');
}
const resolution = approvalResolutionFromAction(actionIdOrResolution);
const action = decision.actions.find((item) => item.id === actionIdOrResolution);
const comment = options.comment || action?.label || (resolution === 'reject' ? '驳回' : '同意');
const stamp = Date.now();
const prefix = (options.correlationPrefix || options.source || 'approval')
.replace(/[^a-zA-Z0-9_-]/g, '-')
.replace(/-+/g, '-');
const response = await governanceCommand({
command_kind: resolution === 'reject' ? 'reject_approval' : 'approve_approval',
context_refs: {
approval_id: approvalId,
plan_id: decision.plan_id || undefined,
task_id: decision.task_id || undefined,
run_id: decision.run_id || undefined,
},
payload: {
source: options.source,
comment,
target_kind: decision.target_kind || undefined,
target_id: decision.target_id || undefined,
},
correlation_id: `${prefix}-${approvalId}-${resolution}-${stamp}`,
idempotency_key: `${prefix}-${approvalId}-${resolution}-${stamp}`,
});
if (!response.accepted) {
throw new Error(response.message || `审批命令未被接受:${response.command_id || approvalId}`);
}
return response;
}