吸收数字员工管理端审批裁决策略预览

This commit is contained in:
baiyanyun
2026-06-14 18:49:08 +08:00
parent 8b651e4ae1
commit 89eec8afc1
7 changed files with 68 additions and 12 deletions

View File

@@ -52,6 +52,18 @@ public class DigitalEmployeeAdminWorkbenchRowDto {
private String triggerSource;
@JsonProperty("approval_id")
private String approvalId;
@JsonProperty("approval_policy_id")
private String approvalPolicyId;
@JsonProperty("conflict_policy_id")
private String conflictPolicyId;
@JsonProperty("decision_guard_status")
private String decisionGuardStatus;
@JsonProperty("batch_accept_remote_disabled")
private Boolean batchAcceptRemoteDisabled;
@JsonProperty("auto_decision_disabled")
private Boolean autoDecisionDisabled;
@JsonProperty("policy_reason")
private String policyReason;
@JsonProperty("artifact_id")
private String artifactId;
@JsonProperty("artifact_group_id")

View File

@@ -1068,6 +1068,12 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
.orchestrationId(firstString(businessView.get("orchestration_id"), businessView.get("orchestrationId"), payload.get("orchestration_id"), payload.get("orchestrationId")))
.triggerSource(firstString(businessView.get("trigger_source"), businessView.get("triggerSource"), payload.get("trigger_source"), payload.get("triggerSource")))
.approvalId(firstString(businessView.get("approval_id"), payload.get("approval_id"), "approval".equals(record.getEntityType()) ? record.getEntityId() : null))
.approvalPolicyId(firstString(businessView.get("approval_policy_id"), businessView.get("approvalPolicyId"), payload.get("approval_policy_id"), payload.get("approvalPolicyId")))
.conflictPolicyId(firstString(businessView.get("conflict_policy_id"), businessView.get("conflictPolicyId"), payload.get("conflict_policy_id"), payload.get("conflictPolicyId")))
.decisionGuardStatus(firstString(businessView.get("decision_guard_status"), businessView.get("decisionGuardStatus"), payload.get("decision_guard_status"), payload.get("decisionGuardStatus")))
.batchAcceptRemoteDisabled(nullableBooleanValue(firstPresent(businessView.get("batch_accept_remote_disabled"), businessView.get("batchAcceptRemoteDisabled"), payload.get("batch_accept_remote_disabled"), payload.get("batchAcceptRemoteDisabled"))))
.autoDecisionDisabled(nullableBooleanValue(firstPresent(businessView.get("auto_decision_disabled"), businessView.get("autoDecisionDisabled"), payload.get("auto_decision_disabled"), payload.get("autoDecisionDisabled"))))
.policyReason(firstString(businessView.get("policy_reason"), businessView.get("policyReason"), payload.get("policy_reason"), payload.get("policyReason")))
.artifactId(firstString(businessView.get("artifact_id"), payload.get("artifact_id"), "artifact".equals(record.getEntityType()) ? record.getEntityId() : null))
.artifactGroupId(firstString(businessView.get("artifact_group_id"), businessView.get("artifactGroupId"), payload.get("artifact_group_id"), payload.get("artifactGroupId")))
.artifactCount(numberValue(firstPresent(businessView.get("artifact_count"), businessView.get("artifactCount"), payload.get("artifact_count"), payload.get("artifactCount"))))

View File

@@ -283,16 +283,22 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
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-approval-conflict", "approval", "approval-1", 101L, new Date(10_000),
Map.of(
"entity_type", "approval",
"title", "远端审批冲突",
"status", "pending",
"approval_conflict_active", true,
"approval_conflict_reason", "terminal_status_mismatch",
"approval_id", "approval-1",
"plan_id", "plan-1",
"task_id", "task-1",
"priority", "high"
Map.ofEntries(
Map.entry("entity_type", "approval"),
Map.entry("title", "远端审批冲突"),
Map.entry("status", "pending"),
Map.entry("approval_conflict_active", true),
Map.entry("approval_conflict_reason", "terminal_status_mismatch"),
Map.entry("approval_id", "approval-1"),
Map.entry("plan_id", "plan-1"),
Map.entry("task_id", "task-1"),
Map.entry("priority", "high"),
Map.entry("approval_policy_id", "approval-policy-1"),
Map.entry("conflict_policy_id", "conflict-policy-1"),
Map.entry("decision_guard_status", "protected"),
Map.entry("batch_accept_remote_disabled", true),
Map.entry("auto_decision_disabled", true),
Map.entry("policy_reason", "manual_review_required")
),
Map.of("id", "approval-1", "status", "pending")),
record("outbox-risk-approval", "event", "event-risk-1", 101L, new Date(11_000),
@@ -344,6 +350,12 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
assertThat(result.getRecords().get(0).getView()).isEqualTo("approval_governance");
assertThat(result.getRecords().get(0).getApprovalId()).isEqualTo("approval-1");
assertThat(result.getRecords().get(0).getCategory()).isEqualTo("approval_conflict");
assertThat(result.getRecords().get(0).getApprovalPolicyId()).isEqualTo("approval-policy-1");
assertThat(result.getRecords().get(0).getConflictPolicyId()).isEqualTo("conflict-policy-1");
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).getPolicyReason()).isEqualTo("manual_review_required");
assertThat(result.getRecords().get(1).getCategory()).isEqualTo("risk_approval");
assertThat(result.getRecords().get(2).getRouteDecisionId()).isEqualTo("route-1");
assertThat(result.getRecords().get(3).getApprovalId()).isEqualTo("approval-1");

View File

@@ -114,6 +114,9 @@ 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.approval_policy_id && `ApprovalPolicy ${record.approval_policy_id}`,
record.conflict_policy_id && `ConflictPolicy ${record.conflict_policy_id}`,
record.decision_guard_status && `DecisionGuard ${record.decision_guard_status}`,
record.service_id && `Service ${record.service_id}`,
record.service_lease_id && `Lease ${record.service_lease_id}`,
record.restart_record_id && `Restart ${record.restart_record_id}`,
@@ -903,9 +906,15 @@ const DigitalEmployeeOps: React.FC = () => {
: '审批相关事件'}
{detailRecord.category && ` · ${detailRecord.category}`}
{detailRecord.priority && ` · 优先级 ${detailRecord.priority}`}
{detailRecord.approval_policy_id && ` · 审批策略 ${detailRecord.approval_policy_id}`}
{detailRecord.conflict_policy_id && ` · 冲突策略 ${detailRecord.conflict_policy_id}`}
{detailRecord.decision_guard_status && ` · 裁决保护 ${detailRecord.decision_guard_status}`}
{detailRecord.policy_reason && ` · 策略原因 ${detailRecord.policy_reason}`}
{detailRecord.batch_accept_remote_disabled && ' · 批量采纳远端禁用'}
{detailRecord.auto_decision_disabled && ' · 自动裁决禁用'}
<Typography.Text type="secondary">
{' '}
· /
· /
</Typography.Text>
</Typography.Paragraph>
)}

View File

@@ -602,6 +602,12 @@ export interface DigitalEmployeeAdminWorkbenchRow {
orchestration_id?: string;
trigger_source?: string;
approval_id?: string;
approval_policy_id?: string;
conflict_policy_id?: string;
decision_guard_status?: string;
batch_accept_remote_disabled?: boolean;
auto_decision_disabled?: boolean;
policy_reason?: string;
artifact_id?: string;
artifact_group_id?: string;
artifact_count?: number;

View File

@@ -1495,13 +1495,24 @@ function checkDigitalAdminApprovalGovernanceWorkbench() {
[service, "approval_conflict_active", "backend approval conflict classifier"],
[service, "risk_approval", "backend risk approval classifier"],
[service, "approval_action_recorded", "backend approval action history classifier"],
[service, "approval_policy_id", "backend approval policy field extraction"],
[service, "batch_accept_remote_disabled", "backend batch accept remote guard field"],
[service, "auto_decision_disabled", "backend auto decision guard field"],
[test, "queryAdminWorkbenchApprovalGovernanceReturnsApprovalConflictRiskAndHistoryRowsOnly", "backend approval governance test"],
[test, "getApprovalPolicyId", "backend approval policy assertion"],
[test, "getBatchAcceptRemoteDisabled", "backend batch accept disabled assertion"],
[qimingTypes, "approval_governance", "qiming approval governance view type"],
[qimingTypes, "approval_policy_id", "qiming approval policy field"],
[qimingTypes, "decision_guard_status", "qiming decision guard field"],
[qimingOps, "审批治理", "qiming approval governance tab"],
[qimingOps, "保留本地", "qiming approval keep local action"],
[qimingOps, "标记复核", "qiming approval mark reviewed action"],
[qimingOps, "裁决保护", "qiming decision guard copy"],
[qimingOps, "批量采纳远端禁用", "qiming batch accept remote guard copy"],
[qimingOps, "自动裁决禁用", "qiming auto decision guard copy"],
[qimingOps, "批量/高风险裁决不开放", "qiming approval governance safety copy"],
[docs, "正式管理端审批工作台预览", "docs approval governance absorption"],
[docs, "正式管理端审批裁决策略预览", "docs approval decision policy absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))

View File

@@ -588,7 +588,7 @@ GET /api/digital-employee/approvals
- 建议:下一轮围绕正式管理端生命周期工作台和跨设备图谱,把 embedded 预览聚合推进到跨端运营治理。
8. **审批 / 人工介入增强**
- 已吸收approval 记录、workday decision、ACP permission 响应桥已有雏形;用户处理审批时会写入 `approval_decision` runtime event评论、转交、超时、风险标记会写入 `approval_action_recorded` / `approval_action_rejected`,并联动 embedded `/api/approval/:approvalId/timeline`、审批列表摘要、管理端 `/api/digital-employee/approval-history` 历史视图、`/api/approval/subscriptions` 订阅策略和 notification 投影;风险审批策略已支持管理端远端下发、手动拉取、本地策略评估、高风险动作前置拦截和本地设置页管理,`/api/risk-approval/policy` 可读写本地策略并记录 `update_risk_approval_policy`;多步审批 workflow 已复用 `digital_approvals.payload`,支持步骤推进、转交/超时/风险动作更新当前步骤、管理端审批更新拉取和审批处理回写,审批动作历史、风险策略、多步流程与通知联动已开始闭环;跨端审批冲突可视化已开始吸收,管理端远端更新与本地终态/更新时间/current step 不一致时会保留本地状态、记录 `approval_conflict_detected`、在业务视图投影 `approval_conflict_*` 字段,并在首页审批卡暂停直接同意/驳回;冲突解决动作闭环已开始吸收,客户端可通过独立 `resolveApprovalConflict` bridge 选择保留本地、采纳管理端或标记已复核,裁决会写入 `approval_conflict_resolved` / `approval_conflict_resolution_rejected`,并在审批业务视图投影 `approval_conflict_resolution``approval_conflict_resolved_at` 和裁决人摘要。跨设备批量冲突排查已开始吸收embedded 新增“介入台”聚合审批冲突、风险审批、路由干预和待处理通知,支持筛选、分页、批量保留本地和批量标记复核;批量采纳管理端被 `batch_accept_remote_disabled` 保护避免扩大状态分叉。正式管理端审批工作台预览已开始吸收qiming-backend 新增 `approval_governance` 运营台视图聚合审批冲突、风险审批、路由待审和审批动作历史qiming“数字员工运营台”新增“审批治理” tab并只开放保留本地、标记复核、标记已看、忽略和重试策略检查等低风险动作意图。
- 已吸收approval 记录、workday decision、ACP permission 响应桥已有雏形;用户处理审批时会写入 `approval_decision` runtime event评论、转交、超时、风险标记会写入 `approval_action_recorded` / `approval_action_rejected`,并联动 embedded `/api/approval/:approvalId/timeline`、审批列表摘要、管理端 `/api/digital-employee/approval-history` 历史视图、`/api/approval/subscriptions` 订阅策略和 notification 投影;风险审批策略已支持管理端远端下发、手动拉取、本地策略评估、高风险动作前置拦截和本地设置页管理,`/api/risk-approval/policy` 可读写本地策略并记录 `update_risk_approval_policy`;多步审批 workflow 已复用 `digital_approvals.payload`,支持步骤推进、转交/超时/风险动作更新当前步骤、管理端审批更新拉取和审批处理回写,审批动作历史、风险策略、多步流程与通知联动已开始闭环;跨端审批冲突可视化已开始吸收,管理端远端更新与本地终态/更新时间/current step 不一致时会保留本地状态、记录 `approval_conflict_detected`、在业务视图投影 `approval_conflict_*` 字段,并在首页审批卡暂停直接同意/驳回;冲突解决动作闭环已开始吸收,客户端可通过独立 `resolveApprovalConflict` bridge 选择保留本地、采纳管理端或标记已复核,裁决会写入 `approval_conflict_resolved` / `approval_conflict_resolution_rejected`,并在审批业务视图投影 `approval_conflict_resolution``approval_conflict_resolved_at` 和裁决人摘要。跨设备批量冲突排查已开始吸收embedded 新增“介入台”聚合审批冲突、风险审批、路由干预和待处理通知,支持筛选、分页、批量保留本地和批量标记复核;批量采纳管理端被 `batch_accept_remote_disabled` 保护避免扩大状态分叉。正式管理端审批工作台预览已开始吸收qiming-backend 新增 `approval_governance` 运营台视图聚合审批冲突、风险审批、路由待审和审批动作历史qiming“数字员工运营台”新增“审批治理” tab并只开放保留本地、标记复核、标记已看、忽略和重试策略检查等低风险动作意图。正式管理端审批裁决策略预览已开始吸收,审批治理详情可展示 `approval_policy_id``conflict_policy_id``decision_guard_status``batch_accept_remote_disabled``auto_decision_disabled` 和策略原因,用于解释为什么只允许低风险复核或保留本地。
- 缺口:仍缺少管理端裁决策略 UI、批量采纳远端和高风险审批自动执行。
- 建议:下一轮围绕正式管理端审批工作台,把 embedded 介入台预览推进到外部管理端批量排查和策略化回写。