吸收数字员工管理端服务恢复安全壳预览

This commit is contained in:
baiyanyun
2026-06-15 11:05:02 +08:00
parent 053a321ec1
commit ff7576f33d
7 changed files with 106 additions and 3 deletions

View File

@@ -139,6 +139,16 @@ public class DigitalEmployeeAdminWorkbenchRowDto {
private String recoveryPolicyId;
@JsonProperty("auto_recovery_status")
private String autoRecoveryStatus;
@JsonProperty("service_start_disabled")
private Boolean serviceStartDisabled;
@JsonProperty("service_stop_disabled")
private Boolean serviceStopDisabled;
@JsonProperty("lease_takeover_disabled")
private Boolean leaseTakeoverDisabled;
@JsonProperty("auto_recovery_disabled")
private Boolean autoRecoveryDisabled;
@JsonProperty("recovery_action_request_id")
private String recoveryActionRequestId;
@JsonProperty("memory_id")
private String memoryId;
@JsonProperty("memory_key")

View File

@@ -1029,6 +1029,10 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
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 "start_service" -> List.of("start_service_disabled");
case "stop_service" -> List.of("stop_service_disabled");
case "lease_takeover" -> List.of("lease_takeover_disabled");
case "execute_auto_recovery" -> List.of("auto_recovery_execution_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");
@@ -1139,6 +1143,11 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
.lastRestartAt(firstString(businessView.get("last_restart_at"), businessView.get("lastRestartAt"), payload.get("last_restart_at"), payload.get("lastRestartAt")))
.recoveryPolicyId(firstString(businessView.get("recovery_policy_id"), businessView.get("recoveryPolicyId"), payload.get("recovery_policy_id"), payload.get("recoveryPolicyId")))
.autoRecoveryStatus(firstString(businessView.get("auto_recovery_status"), businessView.get("autoRecoveryStatus"), payload.get("auto_recovery_status"), payload.get("autoRecoveryStatus")))
.serviceStartDisabled(nullableBooleanValue(firstPresent(businessView.get("service_start_disabled"), businessView.get("serviceStartDisabled"), payload.get("service_start_disabled"), payload.get("serviceStartDisabled"))))
.serviceStopDisabled(nullableBooleanValue(firstPresent(businessView.get("service_stop_disabled"), businessView.get("serviceStopDisabled"), payload.get("service_stop_disabled"), payload.get("serviceStopDisabled"))))
.leaseTakeoverDisabled(nullableBooleanValue(firstPresent(businessView.get("lease_takeover_disabled"), businessView.get("leaseTakeoverDisabled"), payload.get("lease_takeover_disabled"), payload.get("leaseTakeoverDisabled"))))
.autoRecoveryDisabled(nullableBooleanValue(firstPresent(businessView.get("auto_recovery_disabled"), businessView.get("autoRecoveryDisabled"), payload.get("auto_recovery_disabled"), payload.get("autoRecoveryDisabled"))))
.recoveryActionRequestId(firstString(businessView.get("recovery_action_request_id"), businessView.get("recoveryActionRequestId"), payload.get("recovery_action_request_id"), payload.get("recoveryActionRequestId")))
.memoryId(firstString(businessView.get("memory_id"), businessView.get("memoryId"), payload.get("memory_id"), payload.get("memoryId"), "memory".equals(record.getEntityType()) ? record.getEntityId() : null))
.memoryKey(firstString(businessView.get("memory_key"), businessView.get("memoryKey"), businessView.get("key"), payload.get("memory_key"), payload.get("memoryKey"), payload.get("key")))
.governanceStatus(firstString(businessView.get("governance_status"), businessView.get("governanceStatus"), payload.get("governance_status"), payload.get("governanceStatus")))
@@ -1295,7 +1304,12 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|| StringUtils.isNotBlank(row.getServiceId())
|| StringUtils.isNotBlank(row.getServiceLeaseId())
|| StringUtils.isNotBlank(row.getRestartRecordId())
|| StringUtils.isNotBlank(row.getRecoveryPolicyId());
|| StringUtils.isNotBlank(row.getRecoveryPolicyId())
|| row.getServiceStartDisabled() != null
|| row.getServiceStopDisabled() != null
|| row.getLeaseTakeoverDisabled() != null
|| row.getAutoRecoveryDisabled() != null
|| StringUtils.isNotBlank(row.getRecoveryActionRequestId());
}
private boolean isNotificationRow(DigitalEmployeeAdminWorkbenchRowDto row) {

View File

@@ -1157,6 +1157,40 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
assertThat(result.getRecords().get(3).getSummary()).isEqualTo("health_check_failed");
}
@Test
void queryAdminWorkbenchServiceOperationsReturnsSafetyShellFields() {
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-recovery-shell", "event", "event-service-shell-1", 101L, new Date(10_000),
Map.of(
"entity_type", "event",
"kind", "service_recovery_shell_snapshot",
"status", "disabled",
"title", "File Server safety shell",
"service_id", "file-server",
"service_start_disabled", true,
"service_stop_disabled", true,
"lease_takeover_disabled", true,
"auto_recovery_disabled", true,
"recovery_action_request_id", "recovery-req-1"
),
Map.of("id", "event-service-shell-1", "kind", "service_recovery_shell_snapshot"))
)));
DigitalEmployeeAdminWorkbenchPageDto result = service.queryAdminWorkbench(
"service_operations", null, null, null, null, null, null, null, null, null, null, null, 1L, 20L
);
assertThat(result.getTotal()).isEqualTo(1);
DigitalEmployeeAdminWorkbenchRowDto row = result.getRecords().get(0);
assertThat(row.getServiceStartDisabled()).isTrue();
assertThat(row.getServiceStopDisabled()).isTrue();
assertThat(row.getLeaseTakeoverDisabled()).isTrue();
assertThat(row.getAutoRecoveryDisabled()).isTrue();
assertThat(row.getRecoveryActionRequestId()).isEqualTo("recovery-req-1");
assertThat(row.getTitle()).isEqualTo("File Server safety shell");
}
@Test
void queryAdminWorkbenchRouteGovernanceReturnsRouteAndGovernanceRowsOnly() {
when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L)))
@@ -1993,6 +2027,28 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
assertThat(remotePolicyEdit.getReasonCodes()).contains("remote_policy_edit_disabled");
}
@Test
void createAdminActionRejectsHighRiskServiceRecoveryExecution() {
DigitalEmployeeAdminActionResultDto startService = service.createAdminAction(
adminActionRequest("service", "file-server", "start_service")
);
DigitalEmployeeAdminActionResultDto stopService = service.createAdminAction(
adminActionRequest("service", "file-server", "stop_service")
);
DigitalEmployeeAdminActionResultDto leaseTakeover = service.createAdminAction(
adminActionRequest("service", "file-server", "lease_takeover")
);
DigitalEmployeeAdminActionResultDto autoRecovery = service.createAdminAction(
adminActionRequest("service", "file-server", "execute_auto_recovery")
);
assertThat(startService.getOk()).isFalse();
assertThat(startService.getReasonCodes()).contains("start_service_disabled");
assertThat(stopService.getReasonCodes()).contains("stop_service_disabled");
assertThat(leaseTakeover.getReasonCodes()).contains("lease_takeover_disabled");
assertThat(autoRecovery.getReasonCodes()).contains("auto_recovery_execution_disabled");
}
@Test
void queryPendingAdminActionsFiltersTenantClientDeviceAndMasksKey() {
when(adminActionRepository.queryActions(eq(101L), eq("client-key-001"), eq("device-001"), eq("pending"), eq(1L), eq(20L)))