吸收数字员工工作台入口重构收口
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user