吸收数字员工审批历史联动

This commit is contained in:
baiyanyun
2026-06-10 10:46:28 +08:00
parent ed2f19d257
commit 047651e404
15 changed files with 893 additions and 192 deletions

View File

@@ -263,6 +263,32 @@ export interface DigitalEmployeeArtifactLifecycleRecord {
payload: Record<string, unknown>;
}
export type DigitalEmployeeApprovalActionKind = "comment" | "delegate" | "mark_timeout" | "mark_risk" | "clear_risk";
export interface DigitalEmployeeApprovalActionInput {
approvalId?: string | null;
action: DigitalEmployeeApprovalActionKind | string;
comment?: string | null;
delegateTo?: string | null;
delegateLabel?: string | null;
dueAt?: string | null;
riskLevel?: string | null;
reason?: string | null;
actor?: string | null;
source?: string | null;
planId?: string | null;
taskId?: string | null;
runId?: string | null;
occurredAt?: string | null;
message?: string | null;
}
export interface DigitalEmployeeApprovalActionRecord {
eventId: string;
kind: "approval_action_recorded" | "approval_action_rejected";
payload: Record<string, unknown>;
}
export interface DigitalEmployeeRouteDecisionRecord {
id: string;
route_kind: string;
@@ -1155,6 +1181,104 @@ export function recordDigitalEmployeeArtifactLifecycle(
return { eventId, kind, payload };
}
export function recordDigitalEmployeeApprovalAction(
input: DigitalEmployeeApprovalActionInput,
): DigitalEmployeeApprovalActionRecord | null {
const db = getDigitalEmployeeDb();
if (!db) return null;
const now = input.occurredAt || new Date().toISOString();
const approvalId = stringValue(input.approvalId) || "unknown-approval";
const action = stringValue(input.action) || "unknown_action";
const accepted = isDigitalEmployeeApprovalAction(action);
const kind: DigitalEmployeeApprovalActionRecord["kind"] = accepted
? "approval_action_recorded"
: "approval_action_rejected";
const eventId = `approval-action:${safeId(approvalId)}:${safeId(action)}:${safeId(now)}`;
const payload: Record<string, unknown> = {
approval_id: approvalId,
action,
status: accepted ? "recorded" : "rejected",
comment_summary: sanitizeApprovalComment(input.comment),
delegate_summary: sanitizeApprovalDelegate(input.delegateTo, input.delegateLabel),
sla_summary: sanitizeApprovalSla(input.dueAt, action),
risk_summary: sanitizeApprovalRisk(input.riskLevel, action),
reason: stringValue(input.reason) || null,
actor: stringValue(input.actor) || null,
source: stringValue(input.source) || "qimingclaw-approval-action",
plan_id: stringValue(input.planId) || null,
task_id: stringValue(input.taskId) || null,
run_id: stringValue(input.runId) || null,
};
insertEvent(
{
event_id: eventId,
kind,
occurred_at: now,
message: input.message || approvalActionMessage(kind, action, approvalId),
plan_id: stringValue(input.planId) || null,
task_id: stringValue(input.taskId) || null,
},
stringValue(input.runId) || null,
payload,
);
return { eventId, kind, payload };
}
function isDigitalEmployeeApprovalAction(action: string): action is DigitalEmployeeApprovalActionKind {
return ["comment", "delegate", "mark_timeout", "mark_risk", "clear_risk"].includes(action);
}
function sanitizeApprovalComment(comment: string | null | undefined): Record<string, unknown> | null {
const text = stringValue(comment);
if (!text) return null;
return {
preview: text.slice(0, 160),
length: text.length,
};
}
function sanitizeApprovalDelegate(delegateTo: string | null | undefined, delegateLabel: string | null | undefined): Record<string, unknown> | null {
const target = stringValue(delegateTo);
const label = stringValue(delegateLabel);
if (!target && !label) return null;
return {
target: target || null,
label: label || target || null,
};
}
function sanitizeApprovalSla(dueAt: string | null | undefined, action: string): Record<string, unknown> {
const due = stringValue(dueAt);
return {
due_at: due || null,
sla_status: action === "mark_timeout" ? "timeout" : due ? "tracked" : "untracked",
};
}
function sanitizeApprovalRisk(riskLevel: string | null | undefined, action: string): Record<string, unknown> {
const explicit = stringValue(riskLevel);
const level = action === "mark_risk" ? explicit || "high" : action === "clear_risk" ? "normal" : explicit || null;
return { risk_level: level };
}
function approvalActionMessage(
kind: DigitalEmployeeApprovalActionRecord["kind"],
action: string,
approvalId: string,
): string {
if (kind === "approval_action_rejected") return `审批动作已拒绝:${approvalId}`;
const actionLabel = action === "comment"
? "评论"
: action === "delegate"
? "转交"
: action === "mark_timeout"
? "标记超时"
: action === "mark_risk"
? "标记风险"
: "清除风险";
return `审批动作已记录:${actionLabel} ${approvalId}`;
}
function isDigitalEmployeeArtifactRetentionAction(action: string): action is DigitalEmployeeArtifactRetentionAction {
return ["mark_keep", "mark_expire", "mark_review", "clear_retention"].includes(action);
}