吸收数字员工管理端诊断修复预览
This commit is contained in:
@@ -164,6 +164,22 @@ public class DigitalEmployeeAdminWorkbenchRowDto {
|
||||
@JsonProperty("billing_source")
|
||||
private String billingSource;
|
||||
private Boolean estimated;
|
||||
@JsonProperty("diagnostic_id")
|
||||
private String diagnosticId;
|
||||
@JsonProperty("diagnostic_kind")
|
||||
private String diagnosticKind;
|
||||
@JsonProperty("issue_code")
|
||||
private String issueCode;
|
||||
@JsonProperty("repair_action")
|
||||
private String repairAction;
|
||||
@JsonProperty("repair_status")
|
||||
private String repairStatus;
|
||||
@JsonProperty("repair_reason")
|
||||
private String repairReason;
|
||||
@JsonProperty("safe_repair_disabled")
|
||||
private Boolean safeRepairDisabled;
|
||||
@JsonProperty("recommended_action")
|
||||
private String recommendedAction;
|
||||
@JsonProperty("policy_kind")
|
||||
private String policyKind;
|
||||
@JsonProperty("policy_key")
|
||||
|
||||
@@ -78,6 +78,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
||||
"plan_step:mark_reviewed", "plan_step:dismiss", "plan_step:retry_policy_check",
|
||||
"plan:mark_reviewed", "plan:dismiss", "plan:request_revision_review", "plan:record_escalation",
|
||||
"revision:mark_reviewed", "revision:dismiss", "revision:request_revision_review",
|
||||
"diagnostic:mark_reviewed", "diagnostic:dismiss", "diagnostic:retry_check", "diagnostic:record_repair_intent",
|
||||
"audit:mark_reviewed", "audit:dismiss",
|
||||
"event:mark_reviewed", "event:dismiss"
|
||||
);
|
||||
@@ -1023,6 +1024,11 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
||||
case "run_schedule_now" -> List.of("run_schedule_now_disabled");
|
||||
case "start_scheduler" -> List.of("start_scheduler_disabled");
|
||||
case "hard_interrupt" -> List.of("hard_interrupt_disabled");
|
||||
case "run_shell_repair" -> List.of("run_shell_repair_disabled");
|
||||
case "modify_system_config" -> List.of("modify_system_config_disabled");
|
||||
case "restart_unlisted_service" -> List.of("restart_unlisted_service_disabled");
|
||||
case "delete_file_repair" -> List.of("delete_file_repair_disabled");
|
||||
case "remote_policy_edit" -> List.of("remote_policy_edit_disabled");
|
||||
case "activate_plan", "cancel_run", "cancel_task", "accept_remote_approval_batch" -> List.of("high_risk_governance_action_disabled");
|
||||
default -> List.of();
|
||||
};
|
||||
@@ -1144,6 +1150,14 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
||||
.billingEstimateId(firstString(businessView.get("billing_estimate_id"), businessView.get("billingEstimateId"), payload.get("billing_estimate_id"), payload.get("billingEstimateId")))
|
||||
.billingSource(firstString(businessView.get("billing_source"), businessView.get("billingSource"), payload.get("billing_source"), payload.get("billingSource")))
|
||||
.estimated(nullableBooleanValue(firstPresent(businessView.get("estimated"), payload.get("estimated"))))
|
||||
.diagnosticId(firstString(businessView.get("diagnostic_id"), businessView.get("diagnosticId"), payload.get("diagnostic_id"), payload.get("diagnosticId")))
|
||||
.diagnosticKind(firstString(businessView.get("diagnostic_kind"), businessView.get("diagnosticKind"), payload.get("diagnostic_kind"), payload.get("diagnosticKind")))
|
||||
.issueCode(firstString(businessView.get("issue_code"), businessView.get("issueCode"), payload.get("issue_code"), payload.get("issueCode")))
|
||||
.repairAction(firstString(businessView.get("repair_action"), businessView.get("repairAction"), payload.get("repair_action"), payload.get("repairAction")))
|
||||
.repairStatus(firstString(businessView.get("repair_status"), businessView.get("repairStatus"), payload.get("repair_status"), payload.get("repairStatus")))
|
||||
.repairReason(firstString(businessView.get("repair_reason"), businessView.get("repairReason"), payload.get("repair_reason"), payload.get("repairReason")))
|
||||
.safeRepairDisabled(nullableBooleanValue(firstPresent(businessView.get("safe_repair_disabled"), businessView.get("safeRepairDisabled"), payload.get("safe_repair_disabled"), payload.get("safeRepairDisabled"))))
|
||||
.recommendedAction(firstString(businessView.get("recommended_action"), businessView.get("recommendedAction"), payload.get("recommended_action"), payload.get("recommendedAction")))
|
||||
.policyKind(firstString(businessView.get("policy_kind"), businessView.get("policyKind"), payload.get("policy_kind"), payload.get("policyKind")))
|
||||
.policyKey(firstString(businessView.get("policy_key"), businessView.get("policyKey"), payload.get("policy_key"), payload.get("policyKey")))
|
||||
.scheduleId(firstString(businessView.get("schedule_id"), businessView.get("scheduleId"), payload.get("schedule_id"), payload.get("scheduleId")))
|
||||
@@ -1264,6 +1278,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
||||
|
||||
private boolean isAuditRow(DigitalEmployeeAdminWorkbenchRowDto row) {
|
||||
return Set.of("sandbox_audit", "token_cost", "tool_call_audit", "governance", "sync_failure", "audit_archive", "billing_estimate").contains(row.getCategory())
|
||||
|| isDiagnosticRepairRow(row)
|
||||
|| contains(row.getKind(), "audit")
|
||||
|| contains(row.getKind(), "cost")
|
||||
|| contains(row.getKind(), "billing")
|
||||
@@ -1271,6 +1286,18 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
||||
|| StringUtils.isNotBlank(row.getBillingEstimateId());
|
||||
}
|
||||
|
||||
private boolean isDiagnosticRepairRow(DigitalEmployeeAdminWorkbenchRowDto row) {
|
||||
String category = firstString(row.getCategory());
|
||||
String kind = firstString(row.getKind());
|
||||
return Set.of("diagnostic_repair", "diagnostic", "doctor_issue", "policy_pull_error").contains(category)
|
||||
|| startsWithAny(kind, "doctor_", "diagnostic_", "repair_")
|
||||
|| contains(kind, "policy_pull_error")
|
||||
|| StringUtils.isNotBlank(row.getDiagnosticId())
|
||||
|| StringUtils.isNotBlank(row.getIssueCode())
|
||||
|| StringUtils.isNotBlank(row.getRepairAction())
|
||||
|| StringUtils.isNotBlank(row.getRecommendedAction());
|
||||
}
|
||||
|
||||
private boolean isDailyReportRow(DigitalEmployeeAdminWorkbenchRowDto row) {
|
||||
return StringUtils.equals(row.getCategory(), "daily_report")
|
||||
|| startsWithAny(row.getKind(), "daily_report")
|
||||
@@ -1427,6 +1454,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
||||
if (startsWithAny(kind, "daily_report")) return "daily_report";
|
||||
if (startsWithAny(kind, "memory_")) return "memory_governance";
|
||||
if (startsWithAny(kind, "skill_call_")) return "skill_call_audit";
|
||||
if (startsWithAny(kind, "doctor_", "diagnostic_", "repair_") || contains(kind, "policy_pull_error")) return "diagnostic_repair";
|
||||
if (contains(kind, "policy")) return "policy_snapshot";
|
||||
if (contains(kind, "scheduler") || contains(kind, "cron") || contains(kind, "lease")) return "scheduler_operation";
|
||||
if (startsWithAny(kind, "plan_revision", "governance_revision")) return "plan_revision";
|
||||
|
||||
@@ -1474,6 +1474,110 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
|
||||
assertThat(result.getRecords().get(4).getBusinessView()).containsEntry("command_id", "command-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryAdminWorkbenchAuditsReturnsDiagnosticRepairRowsOnly() {
|
||||
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-doctor-issue", "event", "diagnostic-1", 101L, new Date(10_000),
|
||||
Map.ofEntries(
|
||||
Map.entry("entity_type", "event"),
|
||||
Map.entry("category", "diagnostic_repair"),
|
||||
Map.entry("kind", "doctor_issue_detected"),
|
||||
Map.entry("status", "warn"),
|
||||
Map.entry("title", "File Server 异常"),
|
||||
Map.entry("summary", "建议复核服务状态"),
|
||||
Map.entry("diagnostic_id", "diagnostic-1"),
|
||||
Map.entry("diagnostic_kind", "doctor_service"),
|
||||
Map.entry("issue_code", "file_server_unavailable"),
|
||||
Map.entry("recommended_action", "safe_restart_service"),
|
||||
Map.entry("safe_repair_disabled", true)
|
||||
),
|
||||
Map.of("id", "diagnostic-1", "kind", "doctor_issue_detected")),
|
||||
record("outbox-repair-intent", "event", "repair-1", 101L, new Date(11_000),
|
||||
Map.ofEntries(
|
||||
Map.entry("entity_type", "event"),
|
||||
Map.entry("category", "diagnostic_repair"),
|
||||
Map.entry("kind", "repair_intent_recorded"),
|
||||
Map.entry("status", "recorded"),
|
||||
Map.entry("title", "记录修复意图"),
|
||||
Map.entry("diagnostic_id", "diagnostic-1"),
|
||||
Map.entry("issue_code", "file_server_unavailable"),
|
||||
Map.entry("repair_action", "record_repair_intent"),
|
||||
Map.entry("repair_status", "recorded"),
|
||||
Map.entry("repair_reason", "operator_reviewed")
|
||||
),
|
||||
Map.of("id", "repair-1", "kind", "repair_intent_recorded")),
|
||||
record("outbox-policy-pull-error", "event", "diagnostic-2", 101L, new Date(12_000),
|
||||
Map.of(
|
||||
"entity_type", "event",
|
||||
"kind", "policy_pull_error",
|
||||
"status", "failed",
|
||||
"title", "策略拉取失败",
|
||||
"diagnostic_id", "diagnostic-2",
|
||||
"issue_code", "policy_pull_error",
|
||||
"recommended_action", "retry_check"
|
||||
),
|
||||
Map.of("id", "diagnostic-2", "kind", "policy_pull_error")),
|
||||
record("outbox-sandbox-audit", "event", "audit-1", 101L, new Date(13_000),
|
||||
Map.of(
|
||||
"entity_type", "event",
|
||||
"category", "sandbox_audit",
|
||||
"kind", "sandbox_audit",
|
||||
"status", "warn",
|
||||
"title", "Sandbox 风险",
|
||||
"diagnostic_id", "diagnostic-3",
|
||||
"issue_code", "sandbox_warn"
|
||||
),
|
||||
Map.of("id", "audit-1", "kind", "sandbox_audit")),
|
||||
record("outbox-tool-audit", "event", "audit-2", 101L, new Date(14_000),
|
||||
Map.of(
|
||||
"entity_type", "event",
|
||||
"category", "tool_call_audit",
|
||||
"kind", "tool_call_audit",
|
||||
"status", "rejected",
|
||||
"title", "工具调用被拒绝",
|
||||
"diagnostic_id", "diagnostic-4",
|
||||
"issue_code", "tool_rejected"
|
||||
),
|
||||
Map.of("id", "audit-2", "kind", "tool_call_audit")),
|
||||
record("outbox-task", "task", "task-1", 101L, new Date(15_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(16_000),
|
||||
Map.of("entity_type", "approval", "title", "普通审批", "status", "pending"),
|
||||
Map.of("id", "approval-1", "status", "pending")),
|
||||
record("outbox-notification", "notification", "notification-1", 101L, new Date(17_000),
|
||||
Map.of("entity_type", "notification", "title", "普通通知", "status", "unread"),
|
||||
Map.of("id", "notification-1", "status", "unread")),
|
||||
record("outbox-artifact", "artifact", "artifact-1", 101L, new Date(18_000),
|
||||
Map.of("entity_type", "artifact", "title", "普通产物", "status", "available"),
|
||||
Map.of("id", "artifact-1", "status", "available")),
|
||||
record("outbox-route", "event", "route-1", 101L, new Date(19_000),
|
||||
Map.of("entity_type", "event", "category", "route_intervention", "title", "普通路由", "status", "blocked"),
|
||||
Map.of("id", "route-1", "status", "blocked"))
|
||||
)));
|
||||
|
||||
DigitalEmployeeAdminWorkbenchPageDto result = service.queryAdminWorkbench(
|
||||
"audits", null, null, null, null, null, null, null, null, null, null, null, 1L, 20L
|
||||
);
|
||||
|
||||
assertThat(result.getTotal()).isEqualTo(5);
|
||||
assertThat(result.getRecords()).extracting(DigitalEmployeeAdminWorkbenchRowDto::getEntityId)
|
||||
.containsExactly("diagnostic-1", "repair-1", "diagnostic-2", "audit-1", "audit-2");
|
||||
DigitalEmployeeAdminWorkbenchRowDto row = result.getRecords().get(0);
|
||||
assertThat(row.getView()).isEqualTo("audits");
|
||||
assertThat(row.getDiagnosticId()).isEqualTo("diagnostic-1");
|
||||
assertThat(row.getDiagnosticKind()).isEqualTo("doctor_service");
|
||||
assertThat(row.getIssueCode()).isEqualTo("file_server_unavailable");
|
||||
assertThat(row.getRecommendedAction()).isEqualTo("safe_restart_service");
|
||||
assertThat(row.getSafeRepairDisabled()).isTrue();
|
||||
assertThat(row.getSummary()).isEqualTo("建议复核服务状态");
|
||||
assertThat(result.getRecords().get(1).getRepairAction()).isEqualTo("record_repair_intent");
|
||||
assertThat(result.getRecords().get(1).getRepairStatus()).isEqualTo("recorded");
|
||||
assertThat(result.getRecords().get(1).getRepairReason()).isEqualTo("operator_reviewed");
|
||||
assertThat(result.getRecords().get(2).getIssueCode()).isEqualTo("policy_pull_error");
|
||||
}
|
||||
|
||||
@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)))
|
||||
@@ -1666,6 +1770,56 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
|
||||
assertThat(takeoverLease.getReasonCodes()).contains("unsupported_admin_action");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createAdminActionAllowsLowRiskDiagnosticRepairIntent() {
|
||||
DigitalEmployeeAdminActionResultDto markReviewed = service.createAdminAction(
|
||||
adminActionRequest("diagnostic", "diagnostic-1", "mark_reviewed")
|
||||
);
|
||||
DigitalEmployeeAdminActionResultDto dismiss = service.createAdminAction(
|
||||
adminActionRequest("diagnostic", "diagnostic-1", "dismiss")
|
||||
);
|
||||
DigitalEmployeeAdminActionResultDto retryCheck = service.createAdminAction(
|
||||
adminActionRequest("diagnostic", "diagnostic-1", "retry_check")
|
||||
);
|
||||
DigitalEmployeeAdminActionResultDto recordRepairIntent = service.createAdminAction(
|
||||
adminActionRequest("diagnostic", "diagnostic-1", "record_repair_intent")
|
||||
);
|
||||
|
||||
assertThat(markReviewed.getOk()).isTrue();
|
||||
assertThat(dismiss.getOk()).isTrue();
|
||||
assertThat(retryCheck.getOk()).isTrue();
|
||||
assertThat(recordRepairIntent.getOk()).isTrue();
|
||||
assertThat(recordRepairIntent.getStatus()).isEqualTo("pending");
|
||||
assertThat(recordRepairIntent.getReasonCodes()).contains("action_recorded");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createAdminActionRejectsHighRiskDiagnosticRepair() {
|
||||
DigitalEmployeeAdminActionResultDto shellRepair = service.createAdminAction(
|
||||
adminActionRequest("diagnostic", "diagnostic-1", "run_shell_repair")
|
||||
);
|
||||
DigitalEmployeeAdminActionResultDto modifyConfig = service.createAdminAction(
|
||||
adminActionRequest("diagnostic", "diagnostic-1", "modify_system_config")
|
||||
);
|
||||
DigitalEmployeeAdminActionResultDto restartService = service.createAdminAction(
|
||||
adminActionRequest("diagnostic", "diagnostic-1", "restart_unlisted_service")
|
||||
);
|
||||
DigitalEmployeeAdminActionResultDto deleteFileRepair = service.createAdminAction(
|
||||
adminActionRequest("diagnostic", "diagnostic-1", "delete_file_repair")
|
||||
);
|
||||
DigitalEmployeeAdminActionResultDto remotePolicyEdit = service.createAdminAction(
|
||||
adminActionRequest("diagnostic", "diagnostic-1", "remote_policy_edit")
|
||||
);
|
||||
|
||||
assertThat(shellRepair.getOk()).isFalse();
|
||||
assertThat(shellRepair.getStatus()).isEqualTo("rejected");
|
||||
assertThat(shellRepair.getReasonCodes()).contains("run_shell_repair_disabled");
|
||||
assertThat(modifyConfig.getReasonCodes()).contains("modify_system_config_disabled");
|
||||
assertThat(restartService.getReasonCodes()).contains("restart_unlisted_service_disabled");
|
||||
assertThat(deleteFileRepair.getReasonCodes()).contains("delete_file_repair_disabled");
|
||||
assertThat(remotePolicyEdit.getReasonCodes()).contains("remote_policy_edit_disabled");
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryPendingAdminActionsFiltersTenantClientDeviceAndMasksKey() {
|
||||
when(adminActionRepository.queryActions(eq(101L), eq("client-key-001"), eq("device-001"), eq("pending"), eq(1L), eq(20L)))
|
||||
|
||||
Reference in New Issue
Block a user