吸收数字员工管理端路由治理预览
This commit is contained in:
@@ -1084,7 +1084,8 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
||||
if (booleanValue(businessView.get("approval_conflict_active"))) return "approval_conflict";
|
||||
String kind = firstString(row.getKind(), businessView.get("kind"), payload.get("kind"));
|
||||
if (contains(kind, "route_decision_blocked") || contains(kind, "route_decision_approval_required")
|
||||
|| contains(kind, "route_action_missing_context") || contains(kind, "route_action_not_mapped")) {
|
||||
|| contains(kind, "route_action_policy_blocked") || contains(kind, "route_action_missing_context")
|
||||
|| contains(kind, "route_action_not_mapped")) {
|
||||
return "route_intervention";
|
||||
}
|
||||
String category = firstString(businessView.get("category"), payload.get("category"));
|
||||
|
||||
@@ -403,6 +403,71 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
|
||||
assertThat(result.getRecords().get(2).getServiceId()).isEqualTo("agent-server");
|
||||
}
|
||||
|
||||
@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)))
|
||||
.thenReturn(page(List.of(
|
||||
record("outbox-route-blocked", "event", "event-route-1", 101L, new Date(10_000),
|
||||
Map.of(
|
||||
"entity_type", "event",
|
||||
"category", "route_governance",
|
||||
"kind", "route_decision_blocked",
|
||||
"status", "blocked",
|
||||
"title", "路由策略阻断",
|
||||
"route_decision_id", "route-1",
|
||||
"reason", "route_action_policy_blocked",
|
||||
"summary", "控制动作被策略阻断"
|
||||
),
|
||||
Map.of("id", "event-route-1", "kind", "route_decision_blocked")),
|
||||
record("outbox-route-intervention", "event", "event-route-2", 101L, new Date(11_000),
|
||||
Map.of(
|
||||
"entity_type", "event",
|
||||
"category", "route_intervention",
|
||||
"kind", "route_action_missing_context",
|
||||
"status", "pending",
|
||||
"title", "路由缺少上下文",
|
||||
"route_decision_id", "route-2",
|
||||
"reason", "route_action_missing_context"
|
||||
),
|
||||
Map.of("id", "event-route-2", "kind", "route_action_missing_context")),
|
||||
record("outbox-governance", "event", "event-route-3", 101L, new Date(12_000),
|
||||
Map.of(
|
||||
"entity_type", "event",
|
||||
"category", "governance",
|
||||
"kind", "governance_retry_task",
|
||||
"status", "recorded",
|
||||
"title", "治理命令已映射",
|
||||
"route_decision_id", "route-3",
|
||||
"command_id", "command-1"
|
||||
),
|
||||
Map.of("id", "event-route-3", "kind", "governance_retry_task")),
|
||||
record("outbox-task", "task", "task-1", 101L, new Date(13_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(14_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(15_000),
|
||||
Map.of("entity_type", "notification", "title", "普通通知", "status", "unread"),
|
||||
Map.of("id", "notification-1", "status", "unread"))
|
||||
)));
|
||||
|
||||
DigitalEmployeeAdminWorkbenchPageDto result = service.queryAdminWorkbench(
|
||||
"route_governance", null, null, null, null, null, null, null, null, null, null, null, 1L, 20L
|
||||
);
|
||||
|
||||
assertThat(result.getTotal()).isEqualTo(3);
|
||||
assertThat(result.getRecords()).extracting(DigitalEmployeeAdminWorkbenchRowDto::getEntityId)
|
||||
.containsExactly("event-route-1", "event-route-2", "event-route-3");
|
||||
assertThat(result.getRecords().get(0).getView()).isEqualTo("route_governance");
|
||||
assertThat(result.getRecords().get(0).getRouteDecisionId()).isEqualTo("route-1");
|
||||
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(1).getCategory()).isEqualTo("route_intervention");
|
||||
assertThat(result.getRecords().get(2).getRouteDecisionId()).isEqualTo("route-3");
|
||||
}
|
||||
|
||||
@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)))
|
||||
|
||||
@@ -1534,6 +1534,74 @@ function checkDigitalAdminServiceOperationsWorkbench() {
|
||||
console.log("[DigitalEmployeeCheck] admin service operations workbench hooks OK");
|
||||
}
|
||||
|
||||
function checkDigitalAdminRouteGovernanceWorkbench() {
|
||||
console.log("\n[DigitalEmployeeCheck] Verify admin route governance workbench hooks");
|
||||
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
|
||||
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
|
||||
const servicePath = path.join(
|
||||
backendRoot,
|
||||
"app-platform-modules",
|
||||
"app-platform-agent",
|
||||
"app-platform-agent-core-application",
|
||||
"src",
|
||||
"main",
|
||||
"java",
|
||||
"com",
|
||||
"xspaceagi",
|
||||
"agent",
|
||||
"core",
|
||||
"application",
|
||||
"service",
|
||||
"DigitalEmployeeSyncApplicationServiceImpl.java",
|
||||
);
|
||||
const testPath = path.join(
|
||||
backendRoot,
|
||||
"app-platform-modules",
|
||||
"app-platform-agent",
|
||||
"app-platform-agent-core-application",
|
||||
"src",
|
||||
"test",
|
||||
"java",
|
||||
"com",
|
||||
"xspaceagi",
|
||||
"agent",
|
||||
"core",
|
||||
"application",
|
||||
"service",
|
||||
"DigitalEmployeeSyncApplicationServiceImplTest.java",
|
||||
);
|
||||
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
|
||||
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
|
||||
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
|
||||
const service = fs.readFileSync(servicePath, "utf8");
|
||||
const test = fs.readFileSync(testPath, "utf8");
|
||||
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
|
||||
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
|
||||
const docs = fs.readFileSync(docsPath, "utf8");
|
||||
const requiredSnippets = [
|
||||
[service, "route_governance", "backend route governance view"],
|
||||
[service, "isRouteGovernanceRow", "backend route governance matcher"],
|
||||
[service, "route_decision_blocked", "backend route blocked classifier"],
|
||||
[service, "route_action_policy_blocked", "backend route action policy blocked classifier"],
|
||||
[service, "route_action_missing_context", "backend route action missing context classifier"],
|
||||
[service, "governance_", "backend governance event classifier"],
|
||||
[test, "queryAdminWorkbenchRouteGovernanceReturnsRouteAndGovernanceRowsOnly", "backend route governance test"],
|
||||
[qimingTypes, "route_governance", "qiming route governance view type"],
|
||||
[qimingOps, "路由治理", "qiming route governance tab"],
|
||||
[qimingOps, "重试策略检查", "qiming route policy retry action"],
|
||||
[qimingOps, "标记已看", "qiming route mark reviewed action"],
|
||||
[qimingOps, "忽略", "qiming route dismiss action"],
|
||||
[docs, "正式管理端路由治理预览", "docs route governance absorption"],
|
||||
];
|
||||
const missing = requiredSnippets
|
||||
.filter(([content, snippet]) => !content.includes(snippet))
|
||||
.map(([, , label]) => label);
|
||||
if (missing.length > 0) {
|
||||
throw new Error(`Missing admin route governance workbench hooks: ${missing.join(", ")}`);
|
||||
}
|
||||
console.log("[DigitalEmployeeCheck] admin route governance workbench hooks OK");
|
||||
}
|
||||
|
||||
function checkLocalDatabaseSchema() {
|
||||
console.log("\n[DigitalEmployeeCheck] Verify local SQLite digital schema");
|
||||
const dbPath = path.join(os.homedir(), ".qimingclaw", "qimingclaw.db");
|
||||
@@ -1598,6 +1666,7 @@ function main() {
|
||||
checkDigitalAdminRecordBillingArchiveShell();
|
||||
checkDigitalAdminApprovalGovernanceWorkbench();
|
||||
checkDigitalAdminServiceOperationsWorkbench();
|
||||
checkDigitalAdminRouteGovernanceWorkbench();
|
||||
checkLocalDatabaseSchema();
|
||||
console.log("\n[DigitalEmployeeCheck] All checks passed");
|
||||
} catch (error) {
|
||||
|
||||
@@ -599,8 +599,8 @@ GET /api/digital-employee/approvals
|
||||
|
||||
10. **入口路由 / 任务分流(路由业务视图与通知联动已开始)**
|
||||
- 已吸收:管理端通过 `/computer/chat` 进入 qimingclaw 后会生成本地任务事实;embedded `/api/ingress/route`、`/api/route-decisions` 和 `/api/digital-employee/route-decisions` 已由 qimingclaw adapter 接管。
|
||||
- 当前能力:入口请求会按上下文、ready PlanStep、调度、查询和控制动作生成 route decision,并写入 `digital_events.kind = route_decision` 与现有 event outbox;路由时间线优先从 qimingclaw 本地事件读取;低风险 route decision 已开始映射为明确的本地 governance command,并把 `created_command_id` 回填到 route decision;自动映射前会执行低风险/受保护高风险 action guard,未允许的控制动作会记录 `route_action_policy_blocked` 或 `route_action_missing_context`,避免静默创建命令;入口路由策略可从管理端远端下发,支持按 intent 阻断、按 intent 要求审批、限制自动创建 governance command,并在策略干预时先记录 route decision 再停止本地命令执行;首页已展示路由干预队列和治理审计摘要,审批通过/拒绝会写入本地 approval decision 并回写管理端,管理端审批更新拉取后会自动扫描已批准 route approval 并按幂等规则恢复执行;scheduler check 已接入 ready PlanStep 安全派发,首页立即执行后会触发一次派发 sweep 并展示派发结果;路由决策本地业务视图、首页摘要、治理审计、策略来源/拉取错误、阻断/待审/待干预/已恢复计数和通知联动已开始闭环。管理端正式路由 UI 预览已开始吸收,“策略中心”会把入口路由策略纳入统一拉取与状态展示。
|
||||
- 后续缺口:正式外部管理端路由 UI、路由策略编辑和跨设备路由干预冲突可视化仍待吸收。
|
||||
- 当前能力:入口请求会按上下文、ready PlanStep、调度、查询和控制动作生成 route decision,并写入 `digital_events.kind = route_decision` 与现有 event outbox;路由时间线优先从 qimingclaw 本地事件读取;低风险 route decision 已开始映射为明确的本地 governance command,并把 `created_command_id` 回填到 route decision;自动映射前会执行低风险/受保护高风险 action guard,未允许的控制动作会记录 `route_action_policy_blocked` 或 `route_action_missing_context`,避免静默创建命令;入口路由策略可从管理端远端下发,支持按 intent 阻断、按 intent 要求审批、限制自动创建 governance command,并在策略干预时先记录 route decision 再停止本地命令执行;首页已展示路由干预队列和治理审计摘要,审批通过/拒绝会写入本地 approval decision 并回写管理端,管理端审批更新拉取后会自动扫描已批准 route approval 并按幂等规则恢复执行;scheduler check 已接入 ready PlanStep 安全派发,首页立即执行后会触发一次派发 sweep 并展示派发结果;路由决策本地业务视图、首页摘要、治理审计、策略来源/拉取错误、阻断/待审/待干预/已恢复计数和通知联动已开始闭环。管理端正式路由 UI 预览已开始吸收,“策略中心”会把入口路由策略纳入统一拉取与状态展示。正式管理端路由治理预览已开始吸收,qiming-backend 的 `route_governance` 运营台视图会聚合 route blocked / approval required / missing context / not mapped / governance audit,qiming“数字员工运营台”的“路由治理”tab 只开放标记已看、忽略和重试策略检查等低风险动作意图。
|
||||
- 后续缺口:路由策略编辑、跨设备路由干预冲突可视化和高风险恢复执行策略 UI 仍待吸收。
|
||||
- 建议:下一轮围绕管理端正式路由 UI 和跨设备冲突可视化,把 embedded 批量干预入口推进到管理端运营台。
|
||||
|
||||
11. **诊断 / 成本 / 审计视图**
|
||||
|
||||
Reference in New Issue
Block a user