吸收数字员工管理端通知视图
This commit is contained in:
@@ -533,6 +533,68 @@ describe("digital employee sync service", () => {
|
||||
status: "failed",
|
||||
reason: "computer chat failed",
|
||||
});
|
||||
insertEventEntity("event-notification-read", "digital_workday_notification_read", {
|
||||
metadata: {
|
||||
notification_id: "task:task-1",
|
||||
action: "read",
|
||||
},
|
||||
state: {
|
||||
notificationStateById: {
|
||||
"task:task-1": {
|
||||
status: "read",
|
||||
read_at: "2026-06-07T08:01:00.000Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
insertEventEntity("event-notification-dismiss", "digital_workday_notification_dismiss", {
|
||||
metadata: {
|
||||
notification_id: "task:task-2",
|
||||
action: "dismiss",
|
||||
},
|
||||
state: {
|
||||
notificationStateById: {
|
||||
"task:task-2": {
|
||||
status: "dismissed",
|
||||
dismissed_at: "2026-06-07T08:02:00.000Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
insertEventEntity("event-notification-reply", "digital_workday_notification_reply", {
|
||||
metadata: {
|
||||
notification_id: "input:task-3",
|
||||
action: "reply",
|
||||
},
|
||||
state: {
|
||||
notificationStateById: {
|
||||
"input:task-3": {
|
||||
status: "read",
|
||||
read_at: "2026-06-07T08:03:00.000Z",
|
||||
reply: "请改用客户备用手机号",
|
||||
replied_at: "2026-06-07T08:03:30.000Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
insertEventEntity("event-notification-preferences", "digital_workday_update_notification_preferences", {
|
||||
metadata: {
|
||||
notification_preferences: {
|
||||
enabled: false,
|
||||
muted_kinds: ["task_finished", "need_input"],
|
||||
muted_levels: ["info"],
|
||||
updated_at: "2026-06-07T08:04:00.000Z",
|
||||
},
|
||||
},
|
||||
state: {
|
||||
notificationPreferences: {
|
||||
enabled: false,
|
||||
muted_kinds: ["task_finished", "need_input"],
|
||||
muted_levels: ["info"],
|
||||
updated_at: "2026-06-07T08:04:00.000Z",
|
||||
},
|
||||
},
|
||||
});
|
||||
mockState.fetch.mockResolvedValue(
|
||||
jsonResponse({
|
||||
success: true,
|
||||
@@ -542,6 +604,10 @@ describe("digital employee sync service", () => {
|
||||
{ entity_type: "event", entity_id: "event-artifact" },
|
||||
{ entity_type: "event", entity_id: "event-skill" },
|
||||
{ entity_type: "event", entity_id: "event-dispatch" },
|
||||
{ entity_type: "event", entity_id: "event-notification-read" },
|
||||
{ entity_type: "event", entity_id: "event-notification-dismiss" },
|
||||
{ entity_type: "event", entity_id: "event-notification-reply" },
|
||||
{ entity_type: "event", entity_id: "event-notification-preferences" },
|
||||
],
|
||||
}),
|
||||
);
|
||||
@@ -602,6 +668,36 @@ describe("digital employee sync service", () => {
|
||||
status: "failed",
|
||||
reason: "computer chat failed",
|
||||
});
|
||||
expect(businessView("event-notification-read")).toMatchObject({
|
||||
category: "notification",
|
||||
notification_id: "task:task-1",
|
||||
action: "read",
|
||||
status: "read",
|
||||
read_at: "2026-06-07T08:01:00.000Z",
|
||||
});
|
||||
expect(businessView("event-notification-dismiss")).toMatchObject({
|
||||
category: "notification",
|
||||
notification_id: "task:task-2",
|
||||
action: "dismiss",
|
||||
status: "dismissed",
|
||||
dismissed_at: "2026-06-07T08:02:00.000Z",
|
||||
});
|
||||
expect(businessView("event-notification-reply")).toMatchObject({
|
||||
category: "notification",
|
||||
notification_id: "input:task-3",
|
||||
action: "reply",
|
||||
status: "read",
|
||||
read_at: "2026-06-07T08:03:00.000Z",
|
||||
replied_at: "2026-06-07T08:03:30.000Z",
|
||||
reply_length: 10,
|
||||
});
|
||||
expect(businessView("event-notification-reply")).not.toHaveProperty("reply");
|
||||
expect(businessView("event-notification-preferences")).toMatchObject({
|
||||
category: "notification",
|
||||
preferences_enabled: false,
|
||||
muted_kinds: ["task_finished", "need_input"],
|
||||
muted_levels: ["info"],
|
||||
});
|
||||
expect(readEntity("event", "event-route")).toMatchObject({
|
||||
sync_status: "synced",
|
||||
sync_error: null,
|
||||
|
||||
@@ -568,6 +568,7 @@ function eventSpecificBusinessView(
|
||||
}
|
||||
if (kind.startsWith("skill_call_")) return skillCallBusinessView(payload);
|
||||
if (kind.startsWith("plan_step_dispatch_")) return planStepDispatchBusinessView(payload);
|
||||
if (isNotificationEvent(kind)) return notificationBusinessView(payload);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -637,16 +638,46 @@ function planStepDispatchBusinessView(payload: Record<string, unknown>): Record<
|
||||
};
|
||||
}
|
||||
|
||||
function notificationBusinessView(payload: Record<string, unknown>): Record<string, unknown> {
|
||||
const metadata = objectRecord(payload.metadata) ?? {};
|
||||
const state = objectRecord(payload.state) ?? {};
|
||||
const preferences = objectRecord(metadata.notification_preferences) ?? objectRecord(state.notificationPreferences) ?? {};
|
||||
const overlays = objectRecord(state.notificationStateById) ?? {};
|
||||
const notificationId = stringField(metadata.notification_id ?? payload.notification_id ?? payload.notificationId) || null;
|
||||
const overlay = notificationId ? objectRecord(overlays[notificationId]) ?? {} : {};
|
||||
const reply = stringField(overlay.reply ?? metadata.reply ?? payload.reply);
|
||||
return {
|
||||
notification_id: notificationId,
|
||||
action: stringField(metadata.action ?? payload.action) || null,
|
||||
status: stringField(overlay.status ?? payload.status) || null,
|
||||
read_at: stringField(overlay.read_at ?? payload.read_at ?? payload.readAt) || null,
|
||||
dismissed_at: stringField(overlay.dismissed_at ?? payload.dismissed_at ?? payload.dismissedAt) || null,
|
||||
replied_at: stringField(overlay.replied_at ?? payload.replied_at ?? payload.repliedAt) || null,
|
||||
reply_length: reply ? reply.length : null,
|
||||
preferences_enabled: typeof preferences.enabled === "boolean" ? preferences.enabled : null,
|
||||
muted_kinds: stringArrayField(preferences.muted_kinds ?? preferences.mutedKinds),
|
||||
muted_levels: stringArrayField(preferences.muted_levels ?? preferences.mutedLevels),
|
||||
};
|
||||
}
|
||||
|
||||
function eventBusinessCategory(kind: string): string {
|
||||
if (kind === "route_decision") return "route";
|
||||
if (kind.startsWith("approval_")) return "approval";
|
||||
if (kind.startsWith("artifact_")) return "artifact";
|
||||
if (kind.startsWith("skill_call_")) return "skill";
|
||||
if (kind.startsWith("plan_step_dispatch_")) return "dispatch";
|
||||
if (isNotificationEvent(kind)) return "notification";
|
||||
if (kind.startsWith("governance_")) return "governance";
|
||||
return "runtime";
|
||||
}
|
||||
|
||||
function isNotificationEvent(kind: string): boolean {
|
||||
return kind === "digital_workday_update_notification_preferences"
|
||||
|| kind === "digital_workday_notification_read"
|
||||
|| kind === "digital_workday_notification_dismiss"
|
||||
|| kind === "digital_workday_notification_reply";
|
||||
}
|
||||
|
||||
function routeDecisionAttentionLevel(policyResult: string, reasonCodes: string[], createdCommandId: string | null): "info" | "action" | "warn" | "error" {
|
||||
if (policyResult && policyResult !== "accepted") return "error";
|
||||
if (reasonCodes.includes("candidate_skills_disabled") || reasonCodes.includes("route_action_missing_context")) return "warn";
|
||||
|
||||
Reference in New Issue
Block a user