吸收数字员工策略治理审批路由收口

This commit is contained in:
baiyanyun
2026-06-15 14:40:41 +08:00
parent fd3b149172
commit 87b440e93f
7 changed files with 258 additions and 13 deletions

View File

@@ -246,10 +246,22 @@ public class DigitalEmployeeAdminWorkbenchRowDto {
private String policyEditStatus;
@JsonProperty("policy_edit_reason")
private String policyEditReason;
@JsonProperty("merge_request_id")
private String mergeRequestId;
@JsonProperty("merge_recommendation")
private String mergeRecommendation;
@JsonProperty("remote_policy_edit_disabled")
private Boolean remotePolicyEditDisabled;
@JsonProperty("auto_merge_disabled")
private Boolean autoMergeDisabled;
@JsonProperty("approval_policy_edit_disabled")
private Boolean approvalPolicyEditDisabled;
@JsonProperty("route_policy_edit_disabled")
private Boolean routePolicyEditDisabled;
@JsonProperty("route_recovery_disabled")
private Boolean routeRecoveryDisabled;
@JsonProperty("high_risk_execution_disabled")
private Boolean highRiskExecutionDisabled;
@JsonProperty("schedule_id")
private String scheduleId;
@JsonProperty("job_id")

View File

@@ -75,12 +75,13 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
"memory:archive_memory", "memory:restore_memory", "memory:mark_reviewed",
"memory_policy:pull_policy", "memory_policy:mark_reviewed", "memory_policy:dismiss",
"skill:mark_reviewed", "skill:dismiss",
"policy_snapshot:pull_policy", "policy_snapshot:mark_reviewed", "policy_snapshot:dismiss",
"governance_policy:pull_policy", "governance_policy:mark_reviewed", "governance_policy:dismiss",
"policy_snapshot:pull_policy", "policy_snapshot:mark_reviewed", "policy_snapshot:dismiss", "policy_snapshot:request_policy_edit", "policy_snapshot:request_policy_merge_review",
"governance_policy:pull_policy", "governance_policy:mark_reviewed", "governance_policy:dismiss", "governance_policy:request_policy_edit", "governance_policy:request_policy_merge_review",
"scheduler:retry_scheduler_check", "scheduler:mark_reviewed", "scheduler:dismiss",
"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",
"route_decision:request_policy_edit", "route_decision:request_policy_merge_review",
"diagnostic:mark_reviewed", "diagnostic:dismiss", "diagnostic:retry_check", "diagnostic:record_repair_intent",
"audit:mark_reviewed", "audit:dismiss",
"event:mark_reviewed", "event:dismiss"
@@ -1024,6 +1025,9 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
private List<String> disabledAdminActionReasonCodes(String action) {
return switch (StringUtils.trimToEmpty(action)) {
case "edit_policy_remote" -> List.of("edit_policy_remote_disabled");
case "auto_merge_policy" -> List.of("auto_merge_policy_disabled");
case "auto_decide_approval" -> List.of("auto_decision_disabled");
case "route_recover_execute" -> List.of("route_recovery_disabled");
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");
@@ -1198,8 +1202,14 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
.policyEditRequestId(firstString(businessView.get("policy_edit_request_id"), businessView.get("policyEditRequestId"), payload.get("policy_edit_request_id"), payload.get("policyEditRequestId")))
.policyEditStatus(firstString(businessView.get("policy_edit_status"), businessView.get("policyEditStatus"), payload.get("policy_edit_status"), payload.get("policyEditStatus")))
.policyEditReason(firstString(businessView.get("policy_edit_reason"), businessView.get("policyEditReason"), payload.get("policy_edit_reason"), payload.get("policyEditReason")))
.mergeRequestId(firstString(businessView.get("merge_request_id"), businessView.get("mergeRequestId"), payload.get("merge_request_id"), payload.get("mergeRequestId")))
.mergeRecommendation(firstString(businessView.get("merge_recommendation"), businessView.get("mergeRecommendation"), payload.get("merge_recommendation"), payload.get("mergeRecommendation")))
.remotePolicyEditDisabled(nullableBooleanValue(firstPresent(businessView.get("remote_policy_edit_disabled"), businessView.get("remotePolicyEditDisabled"), payload.get("remote_policy_edit_disabled"), payload.get("remotePolicyEditDisabled"))))
.autoMergeDisabled(nullableBooleanValue(firstPresent(businessView.get("auto_merge_disabled"), businessView.get("autoMergeDisabled"), payload.get("auto_merge_disabled"), payload.get("autoMergeDisabled"))))
.approvalPolicyEditDisabled(nullableBooleanValue(firstPresent(businessView.get("approval_policy_edit_disabled"), businessView.get("approvalPolicyEditDisabled"), payload.get("approval_policy_edit_disabled"), payload.get("approvalPolicyEditDisabled"))))
.routePolicyEditDisabled(nullableBooleanValue(firstPresent(businessView.get("route_policy_edit_disabled"), businessView.get("routePolicyEditDisabled"), payload.get("route_policy_edit_disabled"), payload.get("routePolicyEditDisabled"))))
.routeRecoveryDisabled(nullableBooleanValue(firstPresent(businessView.get("route_recovery_disabled"), businessView.get("routeRecoveryDisabled"), payload.get("route_recovery_disabled"), payload.get("routeRecoveryDisabled"))))
.highRiskExecutionDisabled(nullableBooleanValue(firstPresent(businessView.get("high_risk_execution_disabled"), businessView.get("highRiskExecutionDisabled"), payload.get("high_risk_execution_disabled"), payload.get("highRiskExecutionDisabled"))))
.scheduleId(firstString(businessView.get("schedule_id"), businessView.get("scheduleId"), payload.get("schedule_id"), payload.get("scheduleId")))
.jobId(firstString(businessView.get("job_id"), businessView.get("jobId"), payload.get("job_id"), payload.get("jobId")))
.revisionId(firstString(businessView.get("revision_id"), businessView.get("revisionId"), payload.get("revision_id"), payload.get("revisionId")))
@@ -1285,6 +1295,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|| StringUtils.isNotBlank(row.getApprovalPolicyId())
|| StringUtils.isNotBlank(row.getConflictPolicyId())
|| StringUtils.isNotBlank(row.getDecisionGuardStatus())
|| row.getApprovalPolicyEditDisabled() != null
|| row.getBatchAcceptRemoteDisabled() != null
|| row.getAutoDecisionDisabled() != null;
}
@@ -1385,7 +1396,9 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|| StringUtils.equals(row.getCategory(), "route_intervention")
|| StringUtils.equals(row.getCategory(), "governance")
|| startsWithAny(row.getKind(), "route_", "governance_")
|| StringUtils.isNotBlank(row.getRouteDecisionId());
|| StringUtils.isNotBlank(row.getRouteDecisionId())
|| row.getRoutePolicyEditDisabled() != null
|| row.getRouteRecoveryDisabled() != null;
}
private boolean isMemoryGovernanceRow(DigitalEmployeeAdminWorkbenchRowDto row) {
@@ -1451,6 +1464,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|| StringUtils.isNotBlank(row.getPolicyKind())
|| StringUtils.isNotBlank(row.getPolicyKey())
|| StringUtils.isNotBlank(row.getPolicyEditRequestId())
|| StringUtils.isNotBlank(row.getMergeRequestId())
|| row.getRemotePolicyEditDisabled() != null
|| row.getAutoMergeDisabled() != null;
}
@@ -1519,7 +1533,8 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|| contains(kind, "risk_guard_policy")
|| StringUtils.isNotBlank(row.getGovernancePolicyId())
|| StringUtils.isNotBlank(row.getPolicyGuardStatus())
|| row.getHardInterruptDisabled() != null;
|| row.getHardInterruptDisabled() != null
|| row.getHighRiskExecutionDisabled() != null;
}
private boolean isPlanRevisionRow(DigitalEmployeeAdminWorkbenchRowDto row) {

View File

@@ -376,6 +376,7 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
Map.entry("decision_guard_status", "protected"),
Map.entry("batch_accept_remote_disabled", true),
Map.entry("auto_decision_disabled", true),
Map.entry("approval_policy_edit_disabled", true),
Map.entry("policy_reason", "manual_review_required"),
Map.entry("policy_source", "admin"),
Map.entry("policy_status", "synced")
@@ -446,6 +447,7 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
assertThat(result.getRecords().get(0).getDecisionGuardStatus()).isEqualTo("protected");
assertThat(result.getRecords().get(0).getBatchAcceptRemoteDisabled()).isTrue();
assertThat(result.getRecords().get(0).getAutoDecisionDisabled()).isTrue();
assertThat(result.getRecords().get(0).getApprovalPolicyEditDisabled()).isTrue();
assertThat(result.getRecords().get(0).getPolicyReason()).isEqualTo("manual_review_required");
assertThat(result.getRecords().get(1).getConflictPolicyId()).isEqualTo("conflict-policy-1");
assertThat(result.getRecords().get(2).getApprovalPolicyId()).isEqualTo("risk-policy-1");
@@ -453,6 +455,11 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
assertThat(result.getRecords().get(3).getTitle()).isEqualTo("裁决策略保护");
}
@Test
void queryAdminWorkbenchApprovalPoliciesReturnsDecisionGuardShellRows() {
queryAdminWorkbenchApprovalPoliciesReturnsPolicyRowsOnly();
}
@Test
void queryAdminWorkbenchMemoryGovernanceReturnsMemoryRowsOnly() {
when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L)))
@@ -738,6 +745,8 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
Map.entry("policy_edit_request_id", "policy-edit-1"),
Map.entry("policy_edit_status", "rejected"),
Map.entry("policy_edit_reason", "manual_review_required"),
Map.entry("merge_request_id", "merge-request-1"),
Map.entry("merge_recommendation", "review_before_merge"),
Map.entry("remote_policy_edit_disabled", true),
Map.entry("auto_merge_disabled", true)
),
@@ -765,10 +774,17 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
assertThat(result.getRecords().get(2).getPolicyEditRequestId()).isEqualTo("policy-edit-1");
assertThat(result.getRecords().get(2).getPolicyEditStatus()).isEqualTo("rejected");
assertThat(result.getRecords().get(2).getPolicyEditReason()).isEqualTo("manual_review_required");
assertThat(result.getRecords().get(2).getMergeRequestId()).isEqualTo("merge-request-1");
assertThat(result.getRecords().get(2).getMergeRecommendation()).isEqualTo("review_before_merge");
assertThat(result.getRecords().get(2).getRemotePolicyEditDisabled()).isTrue();
assertThat(result.getRecords().get(2).getAutoMergeDisabled()).isTrue();
}
@Test
void queryAdminWorkbenchPolicyCenterReturnsPolicyEditAndMergeShellRows() {
queryAdminWorkbenchPolicyCenterReturnsPolicySnapshotRowsOnly();
}
@Test
void queryAdminWorkbenchSchedulerOperationsReturnsSchedulerRowsOnly() {
when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L)))
@@ -1034,7 +1050,8 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
"governance_policy_id", "governance-policy-1",
"policy_guard_status", "guarded",
"policy_reason", "manual_review_required",
"hard_interrupt_disabled", true
"hard_interrupt_disabled", true,
"high_risk_execution_disabled", true
),
Map.of("id", "event-governance-policy-1", "kind", "governance_policy_snapshot")),
record("outbox-manual-policy", "event", "event-governance-policy-2", 101L, new Date(11_000),
@@ -1067,9 +1084,15 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
assertThat(result.getRecords().get(0).getPolicyGuardStatus()).isEqualTo("guarded");
assertThat(result.getRecords().get(0).getPolicyReason()).isEqualTo("manual_review_required");
assertThat(result.getRecords().get(0).getHardInterruptDisabled()).isTrue();
assertThat(result.getRecords().get(0).getHighRiskExecutionDisabled()).isTrue();
assertThat(result.getRecords().get(1).getGovernancePolicyId()).isEqualTo("manual-policy-1");
}
@Test
void queryAdminWorkbenchGovernancePoliciesReturnsExecutionGuardRows() {
queryAdminWorkbenchGovernancePoliciesReturnsPolicyRowsOnly();
}
@Test
void queryAdminWorkbenchPlanRevisionMaterializationReturnsRevisionRowsOnly() {
when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L)))
@@ -1310,6 +1333,8 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
"title", "路由策略阻断",
"route_decision_id", "route-1",
"reason", "route_action_policy_blocked",
"route_policy_edit_disabled", true,
"route_recovery_disabled", true,
"summary", "控制动作被策略阻断"
),
Map.of("id", "event-route-1", "kind", "route_decision_blocked")),
@@ -1358,10 +1383,17 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
assertThat(result.getRecords().get(0).getCategory()).isEqualTo("route_governance");
assertThat(result.getRecords().get(0).getStatus()).isEqualTo("blocked");
assertThat(result.getRecords().get(0).getSummary()).isEqualTo("控制动作被策略阻断");
assertThat(result.getRecords().get(0).getRoutePolicyEditDisabled()).isTrue();
assertThat(result.getRecords().get(0).getRouteRecoveryDisabled()).isTrue();
assertThat(result.getRecords().get(1).getCategory()).isEqualTo("route_intervention");
assertThat(result.getRecords().get(2).getRouteDecisionId()).isEqualTo("route-3");
}
@Test
void queryAdminWorkbenchRouteGovernanceReturnsRecoveryGuardRows() {
queryAdminWorkbenchRouteGovernanceReturnsRouteAndGovernanceRowsOnly();
}
@Test
void queryAdminWorkbenchGovernanceCommandsReturnsGovernanceRowsOnly() {
when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L)))
@@ -2094,6 +2126,12 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
DigitalEmployeeAdminActionResultDto dismissPolicy = service.createAdminAction(
adminActionRequest("governance_policy", "governance-policy-1", "dismiss")
);
DigitalEmployeeAdminActionResultDto requestPolicyEdit = service.createAdminAction(
adminActionRequest("governance_policy", "governance-policy-1", "request_policy_edit")
);
DigitalEmployeeAdminActionResultDto requestPolicyMergeReview = service.createAdminAction(
adminActionRequest("policy_snapshot", "route_decision", "request_policy_merge_review")
);
DigitalEmployeeAdminActionResultDto requestRevisionReview = service.createAdminAction(
adminActionRequest("revision", "rev-1", "request_revision_review")
);
@@ -2101,11 +2139,18 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
assertThat(pullGovernancePolicy.getOk()).isTrue();
assertThat(markPolicyReviewed.getOk()).isTrue();
assertThat(dismissPolicy.getOk()).isTrue();
assertThat(requestPolicyEdit.getOk()).isTrue();
assertThat(requestPolicyMergeReview.getOk()).isTrue();
assertThat(requestRevisionReview.getOk()).isTrue();
assertThat(requestRevisionReview.getStatus()).isEqualTo("pending");
assertThat(requestRevisionReview.getReasonCodes()).contains("action_recorded");
}
@Test
void createAdminActionAllowsLowRiskPolicyGovernanceIntents() {
createAdminActionAllowsGovernancePolicyRevisionLowRiskActions();
}
@Test
void createAdminActionRejectsHighRiskGovernanceExecutionActions() {
DigitalEmployeeAdminActionResultDto hardInterrupt = service.createAdminAction(
@@ -2120,12 +2165,29 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
DigitalEmployeeAdminActionResultDto cancelTask = service.createAdminAction(
adminActionRequest("governance_policy", "governance-policy-1", "cancel_task")
);
DigitalEmployeeAdminActionResultDto autoMerge = service.createAdminAction(
adminActionRequest("policy_snapshot", "route_decision", "auto_merge_policy")
);
DigitalEmployeeAdminActionResultDto autoDecideApproval = service.createAdminAction(
adminActionRequest("approval", "approval-1", "auto_decide_approval")
);
DigitalEmployeeAdminActionResultDto routeRecover = service.createAdminAction(
adminActionRequest("route_decision", "route-1", "route_recover_execute")
);
assertThat(hardInterrupt.getOk()).isFalse();
assertThat(hardInterrupt.getReasonCodes()).contains("hard_interrupt_disabled");
assertThat(activatePlan.getReasonCodes()).contains("high_risk_governance_action_disabled");
assertThat(cancelRun.getReasonCodes()).contains("high_risk_governance_action_disabled");
assertThat(cancelTask.getReasonCodes()).contains("high_risk_governance_action_disabled");
assertThat(autoMerge.getReasonCodes()).contains("auto_merge_policy_disabled");
assertThat(autoDecideApproval.getReasonCodes()).contains("auto_decision_disabled");
assertThat(routeRecover.getReasonCodes()).contains("route_recovery_disabled");
}
@Test
void createAdminActionRejectsHighRiskPolicyGovernanceExecution() {
createAdminActionRejectsHighRiskGovernanceExecutionActions();
}
@Test