From 1daf58a94c18721771316b4ae5275ae9e37e868d Mon Sep 17 00:00:00 2001 From: baiyanyun Date: Sat, 13 Jun 2026 00:03:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=B8=E6=94=B6=E6=95=B0=E5=AD=97=E5=91=98?= =?UTF-8?q?=E5=B7=A5=E7=AE=A1=E7=90=86=E7=AB=AF=E9=80=9A=E7=9F=A5=E8=BF=90?= =?UTF-8?q?=E8=90=A5=E9=A2=84=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...mployeeSyncApplicationServiceImplTest.java | 64 ++++++++++++++ .../scripts/check-digital-employee.js | 87 ++++++++++++++++++- ...ital-employee-frontend-integration-plan.md | 2 +- 3 files changed, 151 insertions(+), 2 deletions(-) diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java index 0c5e5935..149aad4f 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java @@ -468,6 +468,70 @@ class DigitalEmployeeSyncApplicationServiceImplTest { assertThat(result.getRecords().get(2).getRouteDecisionId()).isEqualTo("route-3"); } + @Test + void queryAdminWorkbenchNotificationsReturnsNotificationRowsOnly() { + when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L))) + .thenReturn(page(List.of( + record("outbox-notification", "notification", "notification-1", 101L, new Date(10_000), + Map.of( + "entity_type", "notification", + "category", "notification", + "kind", "notification_received", + "status", "unread", + "title", "通知未读", + "notification_id", "notification-1", + "summary", "等待处理" + ), + Map.of("id", "notification-1", "kind", "notification_received")), + record("outbox-action-notification", "event", "event-notification-1", 101L, new Date(11_000), + Map.of( + "entity_type", "event", + "category", "action_notification", + "kind", "digital_workday_notification_reply", + "status", "recorded", + "title", "通知回复已记录", + "notification_id", "notification-2" + ), + Map.of("id", "event-notification-1", "kind", "digital_workday_notification_reply")), + record("outbox-preference", "event", "event-notification-2", 101L, new Date(12_000), + Map.of( + "entity_type", "event", + "kind", "notification_preference_updated", + "status", "recorded", + "title", "通知订阅偏好更新", + "notification_id", "notification-preference" + ), + Map.of("id", "event-notification-2", "kind", "notification_preference_updated")), + record("outbox-task", "task", "task-1", 101L, new Date(13_000), + Map.of("entity_type", "task", "title", "普通任务", "status", "running"), + Map.of("id", "task-1", "status", "running")), + record("outbox-approval", "approval", "approval-1", 101L, new Date(14_000), + Map.of("entity_type", "approval", "title", "普通审批", "status", "pending"), + Map.of("id", "approval-1", "status", "pending")), + record("outbox-route", "event", "event-route-1", 101L, new Date(15_000), + Map.of("entity_type", "event", "category", "route_governance", "title", "路由", "status", "blocked"), + Map.of("id", "event-route-1", "status", "blocked")), + record("outbox-audit", "event", "event-audit-1", 101L, new Date(16_000), + Map.of("entity_type", "event", "category", "tool_call_audit", "title", "审计", "status", "warn"), + Map.of("id", "event-audit-1", "status", "warn")) + ))); + + DigitalEmployeeAdminWorkbenchPageDto result = service.queryAdminWorkbench( + "notifications", null, null, null, null, null, null, null, null, null, null, null, 1L, 20L + ); + + assertThat(result.getTotal()).isEqualTo(3); + assertThat(result.getRecords()).extracting(DigitalEmployeeAdminWorkbenchRowDto::getEntityId) + .containsExactly("notification-1", "event-notification-1", "event-notification-2"); + assertThat(result.getRecords().get(0).getView()).isEqualTo("notifications"); + assertThat(result.getRecords().get(0).getNotificationId()).isEqualTo("notification-1"); + assertThat(result.getRecords().get(0).getCategory()).isEqualTo("notification"); + assertThat(result.getRecords().get(0).getStatus()).isEqualTo("unread"); + assertThat(result.getRecords().get(0).getSummary()).isEqualTo("等待处理"); + assertThat(result.getRecords().get(1).getCategory()).isEqualTo("action_notification"); + assertThat(result.getRecords().get(2).getNotificationId()).isEqualTo("notification-preference"); + } + @Test void queryAdminWorkbenchAuditsRedactsSensitivePayloadFields() { when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L))) diff --git a/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js b/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js index 69e0cc3a..206ab9d9 100644 --- a/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js +++ b/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js @@ -1083,7 +1083,7 @@ function checkDigitalOpsNotificationReportWorkbenches() { [dailyReportWorkbench, "请求审批", "daily report approval UI"], [dailyReportWorkbench, "确认回执", "daily report confirmation UI"], [docs, "完整人机介入入口", "docs managed service ops absorption"], - [docs, "真实管理端通知页面预览", "docs notification workbench absorption"], + [docs, "正式管理端通知运营预览", "docs notification workbench absorption"], [docs, "诊断修复动作预览", "docs diagnostic repair absorption"], [docs, "管理端正式日报页面预览", "docs daily report workbench absorption"], ]; @@ -1602,6 +1602,90 @@ function checkDigitalAdminRouteGovernanceWorkbench() { console.log("[DigitalEmployeeCheck] admin route governance workbench hooks OK"); } +function checkDigitalAdminNotificationWorkbench() { + console.log("\n[DigitalEmployeeCheck] Verify admin notification workbench hooks"); + const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend"); + const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming"); + const servicePath = path.join( + backendRoot, + "app-platform-modules", + "app-platform-agent", + "app-platform-agent-core-application", + "src", + "main", + "java", + "com", + "xspaceagi", + "agent", + "core", + "application", + "service", + "DigitalEmployeeSyncApplicationServiceImpl.java", + ); + const dtoPath = path.join( + backendRoot, + "app-platform-modules", + "app-platform-agent", + "app-platform-agent-core-adapter", + "src", + "main", + "java", + "com", + "xspaceagi", + "agent", + "core", + "adapter", + "dto", + "digital", + "DigitalEmployeeAdminWorkbenchRowDto.java", + ); + const testPath = path.join( + backendRoot, + "app-platform-modules", + "app-platform-agent", + "app-platform-agent-core-application", + "src", + "test", + "java", + "com", + "xspaceagi", + "agent", + "core", + "application", + "service", + "DigitalEmployeeSyncApplicationServiceImplTest.java", + ); + const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx"); + const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts"); + const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md"); + const service = fs.readFileSync(servicePath, "utf8"); + const dto = fs.readFileSync(dtoPath, "utf8"); + const test = fs.readFileSync(testPath, "utf8"); + const qimingOps = fs.readFileSync(qimingOpsPath, "utf8"); + const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8"); + const docs = fs.readFileSync(docsPath, "utf8"); + const requiredSnippets = [ + [service, "notifications", "backend notification view"], + [service, "isNotificationRow", "backend notification matcher"], + [dto, "notification_id", "backend notification id row field"], + [service, "action_notification", "backend action notification classifier"], + [service, "notification", "backend notification kind classifier"], + [test, "queryAdminWorkbenchNotificationsReturnsNotificationRowsOnly", "backend notification workbench test"], + [qimingTypes, "notifications", "qiming notification view type"], + [qimingOps, "通知运营", "qiming notification workbench tab"], + [qimingOps, "标记已读", "qiming notification read action"], + [qimingOps, "忽略", "qiming notification dismiss action"], + [docs, "正式管理端通知运营预览", "docs notification workbench absorption"], + ]; + const missing = requiredSnippets + .filter(([content, snippet]) => !content.includes(snippet)) + .map(([, , label]) => label); + if (missing.length > 0) { + throw new Error(`Missing admin notification workbench hooks: ${missing.join(", ")}`); + } + console.log("[DigitalEmployeeCheck] admin notification workbench hooks OK"); +} + function checkLocalDatabaseSchema() { console.log("\n[DigitalEmployeeCheck] Verify local SQLite digital schema"); const dbPath = path.join(os.homedir(), ".qimingclaw", "qimingclaw.db"); @@ -1667,6 +1751,7 @@ function main() { checkDigitalAdminApprovalGovernanceWorkbench(); checkDigitalAdminServiceOperationsWorkbench(); checkDigitalAdminRouteGovernanceWorkbench(); + checkDigitalAdminNotificationWorkbench(); checkLocalDatabaseSchema(); console.log("\n[DigitalEmployeeCheck] All checks passed"); } catch (error) { diff --git a/qimingclaw/docs/digital-employee-frontend-integration-plan.md b/qimingclaw/docs/digital-employee-frontend-integration-plan.md index d3db202d..139850a9 100644 --- a/qimingclaw/docs/digital-employee-frontend-integration-plan.md +++ b/qimingclaw/docs/digital-employee-frontend-integration-plan.md @@ -594,7 +594,7 @@ GET /api/digital-employee/approvals 9. **通知 / Inbox / 用户消息** - 已吸收:运行事件和同步失败能在数字员工页面展示;embedded adapter 已接管 sgRobot 风格 `/api/notifications`、未读数、read/dismiss/reply,本地 UI state 会保存通知已读、忽略和回复 overlay;顶部 Bell 已开始展示 approval、同步失败、服务异常和任务完成通知;本地通知订阅偏好已接入 `/api/notifications/preferences`,可按总开关、桌面通知开关、等级和常用类型过滤 Inbox;`/api/digital-employee/notifications` 已提供管理端可消费的本地通知业务视图,通知动作和订阅偏好事件的 sync `business_view` 会归类为 notification 并提炼状态字段;`need_input` 通知首次回复会同步转化为 `provide_task_input` 治理命令,保留通知回复 overlay 的同时写入 Task/Run/Event/outbox;客户端已支持管理端通知状态拉取与本地通知动作回写,未读 action/warn/error 通知会按偏好触发一次系统级桌面提醒,系统权限引导 UI 已开始吸收,Bell 设置面板可展示桌面通知可用性、测试发送结果、自动通知失败原因,并通过系统设置入口辅助修复,`digital_workday_notification_*` 同步事件只暴露通知 ID、状态、endpoint、错误和回复长度,通知投影、本地订阅策略、桌面提醒、系统权限引导、跨端未读状态与任务输入链路已开始闭环。 - - 缺口:真实管理端通知页面预览已开始吸收,embedded 新增“通知运营”入口,可筛选全部/未读/待处理/异常通知,支持批量已读、批量忽略、拉取远端通知、打开系统通知设置和单条回复;仍缺少正式外部管理端通知页面。 + - 缺口:正式管理端通知运营预览已开始吸收,embedded 新增“通知运营”入口,可筛选全部/未读/待处理/异常通知,支持批量已读、批量忽略、拉取远端通知、打开系统通知设置和单条回复;qiming“数字员工运营台”的“通知运营”tab 可只读聚合通知业务视图,并通过低风险 admin action 记录 `mark_read` / `dismiss` 意图,等待客户端拉取执行;仍缺少正式外部通知协作工作台、复杂订阅策略 UI、真实通知发送/推送通道和跨设备通知冲突合并。 - 建议:下一轮围绕正式管理端通知工作台,把 Inbox 从嵌入页投影推进到完整跨端协作入口。 10. **入口路由 / 任务分流(路由业务视图与通知联动已开始)**