吸收数字员工跨设备调度派发策略冲突预览

This commit is contained in:
baiyanyun
2026-06-14 20:59:41 +08:00
parent 39371861f3
commit cc55fd819f
7 changed files with 134 additions and 3 deletions

View File

@@ -51,6 +51,16 @@ public class DigitalEmployeePolicySnapshotRowDto {
private Boolean autoDecisionDisabled;
@JsonProperty("policy_reason")
private String policyReason;
@JsonProperty("dispatch_policy_key")
private String dispatchPolicyKey;
@JsonProperty("max_per_sweep")
private Long maxPerSweep;
@JsonProperty("auto_dispatch_ready_steps")
private Boolean autoDispatchReadySteps;
@JsonProperty("catch_up_on_startup")
private Boolean catchUpOnStartup;
@JsonProperty("max_run_history")
private Long maxRunHistory;
@JsonProperty("sync_record_url")
private String syncRecordUrl;
@JsonProperty("client_key_masked")

View File

@@ -614,6 +614,11 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
.batchAcceptRemoteDisabled(nullableBooleanValue(firstPresent(latestView.get("batch_accept_remote_disabled"), latestView.get("batchAcceptRemoteDisabled"))))
.autoDecisionDisabled(nullableBooleanValue(firstPresent(latestView.get("auto_decision_disabled"), latestView.get("autoDecisionDisabled"))))
.policyReason(firstString(latestView.get("policy_reason"), latestView.get("policyReason")))
.dispatchPolicyKey(firstString(latestView.get("dispatch_policy_key"), latestView.get("dispatchPolicyKey")))
.maxPerSweep(numberValue(firstPresent(latestView.get("max_per_sweep"), latestView.get("maxPerSweep"))))
.autoDispatchReadySteps(nullableBooleanValue(firstPresent(latestView.get("auto_dispatch_ready_steps"), latestView.get("autoDispatchReadySteps"))))
.catchUpOnStartup(nullableBooleanValue(firstPresent(latestView.get("catch_up_on_startup"), latestView.get("catchUpOnStartup"))))
.maxRunHistory(numberValue(firstPresent(latestView.get("max_run_history"), latestView.get("maxRunHistory"))))
.syncRecordUrl(syncRecordUrl(latest.getOutboxId()))
.clientKeyMasked(maskClientKey(latest.getClientKey()))
.deviceId(latest.getDeviceId())
@@ -687,7 +692,9 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
"policy_source",
"auto_create_governance_commands",
"max_per_sweep",
"auto_dispatch_ready_steps",
"catch_up_on_startup",
"max_run_history",
"approval_required_risk_levels",
"approval_required_actions",
"auto_reject_actions",

View File

@@ -1882,6 +1882,82 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
assertThat(row.getRecommendedResolution()).isEqualTo("review_latest_policy_snapshot");
}
@Test
void queryAdminPolicySnapshotsSummarizesDispatchAndSchedulerPolicyConflicts() {
DigitalEmployeeSyncRecord dispatchA = record("outbox-dispatch-policy-a", "event", "event-dispatch-policy-a", 101L, new Date(10_000),
Map.of(
"entity_type", "event",
"category", "policy_snapshot",
"kind", "plan_step_dispatch_policy_pulled",
"policy_kind", "dispatch",
"policy_key", "plan_step_dispatch",
"enabled", true,
"max_per_sweep", 3,
"auto_dispatch_ready_steps", true,
"dispatch_policy_key", "plan_step_dispatch"
),
Map.of("kind", "plan_step_dispatch_policy_pulled"));
dispatchA.setDeviceId("device-a");
DigitalEmployeeSyncRecord dispatchB = record("outbox-dispatch-policy-b", "event", "event-dispatch-policy-b", 101L, new Date(11_000),
Map.of(
"entity_type", "event",
"category", "policy_snapshot",
"kind", "plan_step_dispatch_policy_pulled",
"policy_kind", "dispatch",
"policy_key", "plan_step_dispatch",
"enabled", true,
"max_per_sweep", 8,
"auto_dispatch_ready_steps", false,
"dispatch_policy_key", "plan_step_dispatch"
),
Map.of("kind", "plan_step_dispatch_policy_pulled"));
dispatchB.setDeviceId("device-b");
DigitalEmployeeSyncRecord schedulerA = record("outbox-scheduler-policy-a", "event", "event-scheduler-policy-a", 101L, new Date(12_000),
Map.of(
"entity_type", "event",
"category", "policy_snapshot",
"kind", "scheduler_policy_snapshot",
"policy_kind", "scheduler",
"policy_key", "scheduler",
"enabled", true,
"catch_up_on_startup", true,
"max_run_history", 20
),
Map.of("kind", "scheduler_policy_snapshot"));
schedulerA.setDeviceId("device-a");
DigitalEmployeeSyncRecord schedulerB = record("outbox-scheduler-policy-b", "event", "event-scheduler-policy-b", 101L, new Date(13_000),
Map.of(
"entity_type", "event",
"category", "policy_snapshot",
"kind", "scheduler_policy_snapshot",
"policy_kind", "scheduler",
"policy_key", "scheduler",
"enabled", true,
"catch_up_on_startup", false,
"max_run_history", 50
),
Map.of("kind", "scheduler_policy_snapshot"));
schedulerB.setDeviceId("device-b");
when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq("event"), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(100L)))
.thenReturn(page(List.of(dispatchA, dispatchB, schedulerA, schedulerB)));
DigitalEmployeePolicySnapshotPageDto dispatchResult = service.queryAdminPolicySnapshots(null, null, "conflict", "dispatch", null, null, 1L, 20L);
DigitalEmployeePolicySnapshotPageDto schedulerResult = service.queryAdminPolicySnapshots(null, null, "conflict", "scheduler", null, null, 1L, 20L);
DigitalEmployeePolicySnapshotRowDto dispatchRow = dispatchResult.getRecords().get(0);
assertThat(dispatchRow.getPolicyKind()).isEqualTo("dispatch");
assertThat(dispatchRow.getConflictKeys()).contains("max_per_sweep", "auto_dispatch_ready_steps");
assertThat(dispatchRow.getDispatchPolicyKey()).isEqualTo("plan_step_dispatch");
assertThat(dispatchRow.getMaxPerSweep()).isEqualTo(8L);
assertThat(dispatchRow.getAutoDispatchReadySteps()).isFalse();
DigitalEmployeePolicySnapshotRowDto schedulerRow = schedulerResult.getRecords().get(0);
assertThat(schedulerRow.getPolicyKind()).isEqualTo("scheduler");
assertThat(schedulerRow.getConflictKeys()).contains("catch_up_on_startup", "max_run_history");
assertThat(schedulerRow.getCatchUpOnStartup()).isFalse();
assertThat(schedulerRow.getMaxRunHistory()).isEqualTo(50L);
}
@Test
void recordDailyReportSendResultStoresRecordedObservation() {
DigitalEmployeeAdminRecordRequestDto request = adminRecordRequest("daily_report", "report-1");