diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java
index 5af3497e..f34bd288 100644
--- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java
+++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java
@@ -43,6 +43,8 @@ public class DigitalEmployeeAdminWorkbenchRowDto {
private String reportId;
@JsonProperty("route_decision_id")
private String routeDecisionId;
+ @JsonProperty("service_id")
+ private String serviceId;
@JsonProperty("occurred_at")
private String occurredAt;
@JsonProperty("synced_at")
diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java
index 238b60ab..83a1a554 100644
--- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java
+++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java
@@ -979,6 +979,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
.notificationId(firstString(businessView.get("notification_id"), payload.get("notification_id")))
.reportId(firstString(businessView.get("report_id"), payload.get("report_id")))
.routeDecisionId(firstString(businessView.get("route_decision_id"), payload.get("route_decision_id")))
+ .serviceId(firstString(businessView.get("service_id"), businessView.get("serviceId"), payload.get("service_id"), payload.get("serviceId")))
.occurredAt(businessRow.getOccurredAt())
.syncedAt(record.getSyncedAt())
.clientKeyMasked(maskClientKey(record.getClientKey()))
@@ -997,6 +998,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
return switch (view) {
case "interventions" -> isInterventionRow(row);
case "approval_governance" -> isApprovalGovernanceRow(row);
+ case "service_operations" -> isServiceOperationsRow(row);
case "artifact_lifecycle" -> isArtifactLifecycleRow(row);
case "notifications" -> isNotificationRow(row);
case "audits" -> isAuditRow(row);
@@ -1037,6 +1039,20 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|| StringUtils.equals(row.getCategory(), "artifact_lifecycle");
}
+ private boolean isServiceOperationsRow(DigitalEmployeeAdminWorkbenchRowDto row) {
+ String kind = firstString(row.getKind());
+ String category = firstString(row.getCategory());
+ return StringUtils.equals(category, "managed_service")
+ || StringUtils.equals(category, "service_operation")
+ || StringUtils.equals(category, "service_health")
+ || contains(kind, "managed_service")
+ || contains(kind, "service_snapshot")
+ || contains(kind, "service_restart")
+ || contains(kind, "doctor_service")
+ || contains(kind, "doctor_issue")
+ || StringUtils.isNotBlank(row.getServiceId());
+ }
+
private boolean isNotificationRow(DigitalEmployeeAdminWorkbenchRowDto row) {
return StringUtils.equals(row.getCategory(), "notification")
|| StringUtils.equals(row.getCategory(), "action_notification")
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 15f98aef..43547fa8 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
@@ -349,6 +349,60 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
assertThat(result.getRecords().get(3).getApprovalId()).isEqualTo("approval-1");
}
+ @Test
+ void queryAdminWorkbenchServiceOperationsReturnsServiceHealthAndRestartRowsOnly() {
+ 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-service-snapshot", "event", "event-service-1", 101L, new Date(10_000),
+ Map.of(
+ "entity_type", "event",
+ "category", "managed_service",
+ "kind", "service_snapshot",
+ "status", "degraded",
+ "title", "File Server 异常",
+ "service_id", "file-server",
+ "level", "warn",
+ "summary", "端口不可用"
+ ),
+ Map.of("id", "event-service-1", "kind", "service_snapshot")),
+ record("outbox-service-restart", "event", "event-service-2", 101L, new Date(11_000),
+ Map.of(
+ "entity_type", "event",
+ "category", "service_operation",
+ "kind", "managed_service_restart",
+ "status", "recorded",
+ "title", "GUI Server 安全重启",
+ "service_id", "gui-server"
+ ),
+ Map.of("id", "event-service-2", "kind", "managed_service_restart")),
+ record("outbox-doctor-service", "event", "event-service-3", 101L, new Date(12_000),
+ Map.of(
+ "entity_type", "event",
+ "kind", "doctor_service_issue",
+ "status", "failed",
+ "title", "服务诊断失败",
+ "service_id", "agent-server",
+ "reason", "health_check_failed"
+ ),
+ Map.of("id", "event-service-3", "kind", "doctor_service_issue")),
+ record("outbox-approval", "approval", "approval-1", 101L, new Date(13_000),
+ Map.of("entity_type", "approval", "title", "审批", "status", "pending"),
+ Map.of("id", "approval-1", "status", "pending"))
+ )));
+
+ DigitalEmployeeAdminWorkbenchPageDto result = service.queryAdminWorkbench(
+ "service_operations", 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("event-service-1", "event-service-2", "event-service-3");
+ assertThat(result.getRecords().get(0).getView()).isEqualTo("service_operations");
+ assertThat(result.getRecords().get(0).getServiceId()).isEqualTo("file-server");
+ assertThat(result.getRecords().get(1).getCategory()).isEqualTo("service_operation");
+ assertThat(result.getRecords().get(2).getServiceId()).isEqualTo("agent-server");
+ }
+
@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/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx b/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx
index d4721e98..78197654 100644
--- a/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx
+++ b/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx
@@ -39,6 +39,7 @@ const WORKBENCH_TABS: Array<{
{ key: 'business', label: '业务视图' },
{ key: 'interventions', label: '介入台' },
{ key: 'approval_governance', label: '审批治理' },
+ { key: 'service_operations', label: '服务运营' },
{ key: 'artifact_lifecycle', label: '产物治理' },
{ key: 'notifications', label: '通知运营' },
{ key: 'audits', label: '审计诊断' },
@@ -101,6 +102,7 @@ function renderSummary(record: DigitalEmployeeAdminWorkbenchRow) {
record.task_id && `Task ${record.task_id}`,
record.run_id && `Run ${record.run_id}`,
record.approval_id && `Approval ${record.approval_id}`,
+ record.service_id && `Service ${record.service_id}`,
record.artifact_id && `Artifact ${record.artifact_id}`,
record.route_decision_id && `Route ${record.route_decision_id}`,
record.policy_kind && `Policy ${record.policy_kind}`,
@@ -154,6 +156,9 @@ function actionTarget(record: DigitalEmployeeAdminWorkbenchRow, view: DigitalEmp
if (view === 'notifications' && record.notification_id) {
return { entityType: 'notification', entityId: record.notification_id };
}
+ if (view === 'service_operations') {
+ return { entityType: 'event', entityId: record.entity_id };
+ }
if (view === 'artifact_lifecycle' && record.artifact_id) {
return { entityType: 'artifact', entityId: record.artifact_id };
}
@@ -211,6 +216,12 @@ function allowedActions(record: DigitalEmployeeAdminWorkbenchRow, view: DigitalE
{ action: 'dismiss', label: '忽略' },
];
}
+ if (view === 'service_operations') {
+ return [
+ { action: 'mark_reviewed', label: '标记已看' },
+ { action: 'dismiss', label: '忽略' },
+ ];
+ }
if (view === 'artifact_lifecycle' && record.artifact_id) {
return [
{ action: 'mark_keep', label: '标记保留' },
@@ -686,6 +697,7 @@ const DigitalEmployeeOps: React.FC = () => {
detailRecord.task_id && `Task ${detailRecord.task_id}`,
detailRecord.run_id && `Run ${detailRecord.run_id}`,
detailRecord.approval_id && `Approval ${detailRecord.approval_id}`,
+ detailRecord.service_id && `Service ${detailRecord.service_id}`,
detailRecord.artifact_id && `Artifact ${detailRecord.artifact_id}`,
detailRecord.notification_id &&
`Notification ${detailRecord.notification_id}`,
@@ -725,6 +737,18 @@ const DigitalEmployeeOps: React.FC = () => {
)}
+ {activeView === 'service_operations' && (
+