吸收数字员工管理端协作运营收口
This commit is contained in:
@@ -37,10 +37,29 @@ public class DigitalEmployeeAdminWorkbenchRowDto {
|
||||
private String approvalId;
|
||||
@JsonProperty("artifact_id")
|
||||
private String artifactId;
|
||||
@JsonProperty("cleanup_request_id")
|
||||
private String cleanupRequestId;
|
||||
@JsonProperty("cleanup_eligible_count")
|
||||
private Long cleanupEligibleCount;
|
||||
@JsonProperty("batch_cleanup_disabled")
|
||||
private Boolean batchCleanupDisabled;
|
||||
@JsonProperty("notification_id")
|
||||
private String notificationId;
|
||||
@JsonProperty("subscription_policy_id")
|
||||
private String subscriptionPolicyId;
|
||||
private String channel;
|
||||
@JsonProperty("delivery_status")
|
||||
private String deliveryStatus;
|
||||
@JsonProperty("conflict_status")
|
||||
private String conflictStatus;
|
||||
@JsonProperty("report_id")
|
||||
private String reportId;
|
||||
@JsonProperty("daily_report_fact_id")
|
||||
private String dailyReportFactId;
|
||||
@JsonProperty("send_record_id")
|
||||
private String sendRecordId;
|
||||
@JsonProperty("approval_decision_status")
|
||||
private String approvalDecisionStatus;
|
||||
@JsonProperty("route_decision_id")
|
||||
private String routeDecisionId;
|
||||
@JsonProperty("service_id")
|
||||
|
||||
@@ -68,7 +68,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
||||
"route_decision:mark_reviewed", "route_decision:dismiss", "route_decision:retry_policy_check",
|
||||
"route:mark_reviewed", "route:dismiss", "route:retry_policy_check",
|
||||
"notification:mark_read", "notification:dismiss",
|
||||
"artifact:mark_keep", "artifact:mark_expire", "artifact:mark_reviewed",
|
||||
"artifact:mark_keep", "artifact:mark_expire", "artifact:mark_reviewed", "artifact:record_cleanup_request",
|
||||
"daily_report:mark_delivered", "daily_report:mark_confirmed", "daily_report:request_approval",
|
||||
"memory:archive_memory", "memory:restore_memory", "memory:mark_reviewed",
|
||||
"skill:mark_reviewed", "skill:dismiss",
|
||||
@@ -997,8 +997,18 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
||||
.runId(businessRow.getRunId())
|
||||
.approvalId(firstString(businessView.get("approval_id"), payload.get("approval_id"), "approval".equals(record.getEntityType()) ? record.getEntityId() : null))
|
||||
.artifactId(firstString(businessView.get("artifact_id"), payload.get("artifact_id"), "artifact".equals(record.getEntityType()) ? record.getEntityId() : null))
|
||||
.cleanupRequestId(firstString(businessView.get("cleanup_request_id"), businessView.get("cleanupRequestId"), payload.get("cleanup_request_id"), payload.get("cleanupRequestId")))
|
||||
.cleanupEligibleCount(numberValue(firstPresent(businessView.get("cleanup_eligible_count"), businessView.get("cleanupEligibleCount"), payload.get("cleanup_eligible_count"), payload.get("cleanupEligibleCount"))))
|
||||
.batchCleanupDisabled(nullableBooleanValue(firstPresent(businessView.get("batch_cleanup_disabled"), businessView.get("batchCleanupDisabled"), payload.get("batch_cleanup_disabled"), payload.get("batchCleanupDisabled"))))
|
||||
.notificationId(firstString(businessView.get("notification_id"), payload.get("notification_id")))
|
||||
.subscriptionPolicyId(firstString(businessView.get("subscription_policy_id"), businessView.get("subscriptionPolicyId"), payload.get("subscription_policy_id"), payload.get("subscriptionPolicyId")))
|
||||
.channel(firstString(businessView.get("channel"), payload.get("channel")))
|
||||
.deliveryStatus(firstString(businessView.get("delivery_status"), businessView.get("deliveryStatus"), payload.get("delivery_status"), payload.get("deliveryStatus")))
|
||||
.conflictStatus(firstString(businessView.get("conflict_status"), businessView.get("conflictStatus"), payload.get("conflict_status"), payload.get("conflictStatus")))
|
||||
.reportId(firstString(businessView.get("report_id"), payload.get("report_id")))
|
||||
.dailyReportFactId(firstString(businessView.get("daily_report_fact_id"), businessView.get("dailyReportFactId"), payload.get("daily_report_fact_id"), payload.get("dailyReportFactId")))
|
||||
.sendRecordId(firstString(businessView.get("send_record_id"), businessView.get("sendRecordId"), payload.get("send_record_id"), payload.get("sendRecordId")))
|
||||
.approvalDecisionStatus(firstString(businessView.get("approval_decision_status"), businessView.get("approvalDecisionStatus"), payload.get("approval_decision_status"), payload.get("approvalDecisionStatus")))
|
||||
.routeDecisionId(firstString(businessView.get("route_decision_id"), payload.get("route_decision_id")))
|
||||
.serviceId(firstString(businessView.get("service_id"), businessView.get("serviceId"), payload.get("service_id"), payload.get("serviceId")))
|
||||
.memoryId(firstString(businessView.get("memory_id"), businessView.get("memoryId"), payload.get("memory_id"), payload.get("memoryId"), "memory".equals(record.getEntityType()) ? record.getEntityId() : null))
|
||||
@@ -1377,6 +1387,13 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
||||
return false;
|
||||
}
|
||||
|
||||
private Boolean nullableBooleanValue(Object value) {
|
||||
if (value == null) return null;
|
||||
if (value instanceof Boolean bool) return bool;
|
||||
if (value instanceof String text && StringUtils.isNotBlank(text)) return Boolean.parseBoolean(text.trim());
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean contains(String value, String fragment) {
|
||||
return StringUtils.containsIgnoreCase(value, fragment);
|
||||
}
|
||||
@@ -1447,6 +1464,13 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object firstPresent(Object... values) {
|
||||
for (Object value : values) {
|
||||
if (value != null) return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String stringValue(Object value, String fallback) {
|
||||
if (value == null) return fallback;
|
||||
String text = Objects.toString(value, "").trim();
|
||||
|
||||
@@ -870,14 +870,18 @@ 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-notification", "notification", "notification-1", 101L, new Date(10_000),
|
||||
Map.of(
|
||||
"entity_type", "notification",
|
||||
"category", "notification",
|
||||
"kind", "notification_received",
|
||||
"status", "unread",
|
||||
"title", "通知未读",
|
||||
"notification_id", "notification-1",
|
||||
"summary", "等待处理"
|
||||
Map.ofEntries(
|
||||
Map.entry("entity_type", "notification"),
|
||||
Map.entry("category", "notification"),
|
||||
Map.entry("kind", "notification_received"),
|
||||
Map.entry("status", "unread"),
|
||||
Map.entry("title", "通知未读"),
|
||||
Map.entry("notification_id", "notification-1"),
|
||||
Map.entry("subscription_policy_id", "subscription-policy-1"),
|
||||
Map.entry("channel", "desktop"),
|
||||
Map.entry("delivery_status", "delivered"),
|
||||
Map.entry("conflict_status", "none"),
|
||||
Map.entry("summary", "等待处理")
|
||||
),
|
||||
Map.of("id", "notification-1", "kind", "notification_received")),
|
||||
record("outbox-action-notification", "event", "event-notification-1", 101L, new Date(11_000),
|
||||
@@ -925,6 +929,10 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
|
||||
assertThat(result.getRecords().get(0).getCategory()).isEqualTo("notification");
|
||||
assertThat(result.getRecords().get(0).getStatus()).isEqualTo("unread");
|
||||
assertThat(result.getRecords().get(0).getSummary()).isEqualTo("等待处理");
|
||||
assertThat(result.getRecords().get(0).getSubscriptionPolicyId()).isEqualTo("subscription-policy-1");
|
||||
assertThat(result.getRecords().get(0).getChannel()).isEqualTo("desktop");
|
||||
assertThat(result.getRecords().get(0).getDeliveryStatus()).isEqualTo("delivered");
|
||||
assertThat(result.getRecords().get(0).getConflictStatus()).isEqualTo("none");
|
||||
assertThat(result.getRecords().get(1).getCategory()).isEqualTo("action_notification");
|
||||
assertThat(result.getRecords().get(2).getNotificationId()).isEqualTo("notification-preference");
|
||||
}
|
||||
@@ -941,6 +949,9 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
|
||||
"artifact_id", "artifact-1",
|
||||
"retention_status", "expired",
|
||||
"cleanup_status", "pending",
|
||||
"cleanup_request_id", "cleanup-request-1",
|
||||
"cleanup_eligible_count", 3,
|
||||
"batch_cleanup_disabled", true,
|
||||
"summary", "等待清理"
|
||||
),
|
||||
Map.of("id", "artifact-1", "status", "available")),
|
||||
@@ -995,6 +1006,9 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
|
||||
assertThat(result.getRecords().get(0).getCategory()).isEqualTo("artifact_lifecycle");
|
||||
assertThat(result.getRecords().get(0).getStatus()).isEqualTo("available");
|
||||
assertThat(result.getRecords().get(0).getSummary()).isEqualTo("等待清理");
|
||||
assertThat(result.getRecords().get(0).getCleanupRequestId()).isEqualTo("cleanup-request-1");
|
||||
assertThat(result.getRecords().get(0).getCleanupEligibleCount()).isEqualTo(3L);
|
||||
assertThat(result.getRecords().get(0).getBatchCleanupDisabled()).isTrue();
|
||||
assertThat(result.getRecords().get(0).getBusinessView()).containsEntry("retention_status", "expired");
|
||||
assertThat(result.getRecords().get(1).getArtifactId()).isEqualTo("artifact-2");
|
||||
assertThat(result.getRecords().get(2).getBusinessView()).containsEntry("cleanup_status", "deleted");
|
||||
@@ -1137,15 +1151,18 @@ 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-daily-report", "daily_report", "report-1", 101L, new Date(10_000),
|
||||
Map.of(
|
||||
"entity_type", "daily_report",
|
||||
"category", "daily_report",
|
||||
"kind", "daily_report_generated",
|
||||
"status", "generated",
|
||||
"title", "6 月 13 日日报",
|
||||
"report_id", "report-1",
|
||||
"summary", "日报已生成",
|
||||
"artifact_id", "artifact-report-1"
|
||||
Map.ofEntries(
|
||||
Map.entry("entity_type", "daily_report"),
|
||||
Map.entry("category", "daily_report"),
|
||||
Map.entry("kind", "daily_report_generated"),
|
||||
Map.entry("status", "generated"),
|
||||
Map.entry("title", "6 月 13 日日报"),
|
||||
Map.entry("report_id", "report-1"),
|
||||
Map.entry("daily_report_fact_id", "daily-report-fact-1"),
|
||||
Map.entry("send_record_id", "send-record-1"),
|
||||
Map.entry("approval_decision_status", "pending"),
|
||||
Map.entry("summary", "日报已生成"),
|
||||
Map.entry("artifact_id", "artifact-report-1")
|
||||
),
|
||||
Map.of("id", "report-1", "kind", "daily_report_generated")),
|
||||
record("outbox-daily-report-delivered", "event", "event-daily-report-1", 101L, new Date(11_000),
|
||||
@@ -1195,6 +1212,9 @@ class DigitalEmployeeSyncApplicationServiceImplTest {
|
||||
assertThat(result.getRecords().get(0).getCategory()).isEqualTo("daily_report");
|
||||
assertThat(result.getRecords().get(0).getStatus()).isEqualTo("generated");
|
||||
assertThat(result.getRecords().get(0).getSummary()).isEqualTo("日报已生成");
|
||||
assertThat(result.getRecords().get(0).getDailyReportFactId()).isEqualTo("daily-report-fact-1");
|
||||
assertThat(result.getRecords().get(0).getSendRecordId()).isEqualTo("send-record-1");
|
||||
assertThat(result.getRecords().get(0).getApprovalDecisionStatus()).isEqualTo("pending");
|
||||
assertThat(result.getRecords().get(1).getReportId()).isEqualTo("report-2");
|
||||
assertThat(result.getRecords().get(1).getBusinessView()).containsEntry("delivery_status", "delivered");
|
||||
assertThat(result.getRecords().get(2).getBusinessView()).containsEntry("confirmed_at", "2026-06-13T10:00:00+08:00");
|
||||
|
||||
@@ -111,8 +111,11 @@ function renderSummary(record: DigitalEmployeeAdminWorkbenchRow) {
|
||||
record.approval_id && `Approval ${record.approval_id}`,
|
||||
record.service_id && `Service ${record.service_id}`,
|
||||
record.artifact_id && `Artifact ${record.artifact_id}`,
|
||||
record.cleanup_request_id && `Cleanup ${record.cleanup_request_id}`,
|
||||
record.route_decision_id && `Route ${record.route_decision_id}`,
|
||||
record.policy_kind && `Policy ${record.policy_kind}`,
|
||||
record.subscription_policy_id && `Subscription ${record.subscription_policy_id}`,
|
||||
record.daily_report_fact_id && `ReportFact ${record.daily_report_fact_id}`,
|
||||
record.memory_id && `Memory ${record.memory_id}`,
|
||||
record.skill_id && `Skill ${record.skill_id}`,
|
||||
record.tool_name && `Tool ${record.tool_name}`,
|
||||
@@ -261,6 +264,7 @@ function allowedActions(record: DigitalEmployeeAdminWorkbenchRow, view: DigitalE
|
||||
{ action: 'mark_keep', label: '标记保留' },
|
||||
{ action: 'mark_expire', label: '标记到期' },
|
||||
{ action: 'mark_reviewed', label: '标记复核' },
|
||||
{ action: 'record_cleanup_request', label: '记录批量清理申请' },
|
||||
];
|
||||
}
|
||||
if (view === 'daily_reports' && record.report_id) {
|
||||
@@ -781,9 +785,17 @@ const DigitalEmployeeOps: React.FC = () => {
|
||||
detailRecord.approval_id && `Approval ${detailRecord.approval_id}`,
|
||||
detailRecord.service_id && `Service ${detailRecord.service_id}`,
|
||||
detailRecord.artifact_id && `Artifact ${detailRecord.artifact_id}`,
|
||||
detailRecord.cleanup_request_id &&
|
||||
`CleanupRequest ${detailRecord.cleanup_request_id}`,
|
||||
detailRecord.notification_id &&
|
||||
`Notification ${detailRecord.notification_id}`,
|
||||
detailRecord.subscription_policy_id &&
|
||||
`Subscription ${detailRecord.subscription_policy_id}`,
|
||||
detailRecord.report_id && `Report ${detailRecord.report_id}`,
|
||||
detailRecord.daily_report_fact_id &&
|
||||
`ReportFact ${detailRecord.daily_report_fact_id}`,
|
||||
detailRecord.send_record_id &&
|
||||
`SendRecord ${detailRecord.send_record_id}`,
|
||||
detailRecord.route_decision_id &&
|
||||
`Route ${detailRecord.route_decision_id}`,
|
||||
detailRecord.policy_kind && `Policy ${detailRecord.policy_kind}`,
|
||||
@@ -839,6 +851,50 @@ const DigitalEmployeeOps: React.FC = () => {
|
||||
</Typography.Text>
|
||||
</Typography.Paragraph>
|
||||
)}
|
||||
{activeView === 'artifact_lifecycle' && (
|
||||
<Typography.Paragraph>
|
||||
<Typography.Text strong>产物协作:</Typography.Text>
|
||||
{detailRecord.artifact_id || detailRecord.entity_id || '--'}
|
||||
{detailRecord.cleanup_eligible_count != null &&
|
||||
` · 可清理 ${detailRecord.cleanup_eligible_count} 项`}
|
||||
{detailRecord.batch_cleanup_disabled && ' · 批量真实清理禁用'}
|
||||
<Typography.Text type="secondary">
|
||||
{' '}
|
||||
· 只允许记录批量清理申请和保留策略意图,不执行批量删除。
|
||||
</Typography.Text>
|
||||
</Typography.Paragraph>
|
||||
)}
|
||||
{activeView === 'notifications' && (
|
||||
<Typography.Paragraph>
|
||||
<Typography.Text strong>通知协作:</Typography.Text>
|
||||
{detailRecord.notification_id || detailRecord.entity_id || '--'}
|
||||
{detailRecord.subscription_policy_id &&
|
||||
` · 订阅策略 ${detailRecord.subscription_policy_id}`}
|
||||
{detailRecord.channel && ` · ${detailRecord.channel}`}
|
||||
{detailRecord.delivery_status && ` · ${detailRecord.delivery_status}`}
|
||||
{detailRecord.conflict_status && ` · ${detailRecord.conflict_status}`}
|
||||
<Typography.Text type="secondary">
|
||||
{' '}
|
||||
· 展示跨设备状态,只记录已读/忽略意图。
|
||||
</Typography.Text>
|
||||
</Typography.Paragraph>
|
||||
)}
|
||||
{activeView === 'daily_reports' && (
|
||||
<Typography.Paragraph>
|
||||
<Typography.Text strong>日报协作:</Typography.Text>
|
||||
{detailRecord.report_id || detailRecord.entity_id || '--'}
|
||||
{detailRecord.daily_report_fact_id &&
|
||||
` · 事实 ${detailRecord.daily_report_fact_id}`}
|
||||
{detailRecord.send_record_id &&
|
||||
` · 发送记录 ${detailRecord.send_record_id}`}
|
||||
{detailRecord.approval_decision_status &&
|
||||
` · 审批状态 ${detailRecord.approval_decision_status}`}
|
||||
<Typography.Text type="secondary">
|
||||
{' '}
|
||||
· 展示发送记录、审批状态和事实来源,只记录发送结果/审批/回执意图。
|
||||
</Typography.Text>
|
||||
</Typography.Paragraph>
|
||||
)}
|
||||
{activeView === 'governance_commands' && (
|
||||
<Typography.Paragraph>
|
||||
<Typography.Text strong>治理命令:</Typography.Text>
|
||||
|
||||
@@ -594,8 +594,18 @@ export interface DigitalEmployeeAdminWorkbenchRow {
|
||||
run_id?: string;
|
||||
approval_id?: string;
|
||||
artifact_id?: string;
|
||||
cleanup_request_id?: string;
|
||||
cleanup_eligible_count?: number;
|
||||
batch_cleanup_disabled?: boolean;
|
||||
notification_id?: string;
|
||||
subscription_policy_id?: string;
|
||||
channel?: string;
|
||||
delivery_status?: string;
|
||||
conflict_status?: string;
|
||||
report_id?: string;
|
||||
daily_report_fact_id?: string;
|
||||
send_record_id?: string;
|
||||
approval_decision_status?: string;
|
||||
route_decision_id?: string;
|
||||
service_id?: string;
|
||||
memory_id?: string;
|
||||
|
||||
@@ -1737,6 +1737,99 @@ function checkDigitalAdminPlanGovernanceWorkbenches() {
|
||||
console.log("[DigitalEmployeeCheck] admin plan governance and revision hooks OK");
|
||||
}
|
||||
|
||||
function checkDigitalAdminCollaborationOperationsClosure() {
|
||||
console.log("\n[DigitalEmployeeCheck] Verify admin artifact, notification and daily report collaboration closure 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 dtoPath = path.join(
|
||||
backendRoot,
|
||||
"app-platform-modules",
|
||||
"app-platform-agent",
|
||||
"app-platform-agent-core-adapter",
|
||||
"src",
|
||||
"main",
|
||||
"java",
|
||||
"com",
|
||||
"xspaceagi",
|
||||
"agent",
|
||||
"core",
|
||||
"adapter",
|
||||
"dto",
|
||||
"digital",
|
||||
"DigitalEmployeeAdminWorkbenchRowDto.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 dto = fs.readFileSync(dtoPath, "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, "record_cleanup_request", "backend cleanup request admin action"],
|
||||
[service, "cleanup_request_id", "backend cleanup request projection"],
|
||||
[service, "batch_cleanup_disabled", "backend batch cleanup disabled projection"],
|
||||
[service, "subscription_policy_id", "backend subscription policy projection"],
|
||||
[service, "daily_report_fact_id", "backend daily report fact projection"],
|
||||
[dto, "cleanup_request_id", "backend cleanup request row field"],
|
||||
[dto, "cleanup_eligible_count", "backend cleanup eligible count row field"],
|
||||
[dto, "batch_cleanup_disabled", "backend batch cleanup disabled row field"],
|
||||
[dto, "subscription_policy_id", "backend subscription policy row field"],
|
||||
[dto, "daily_report_fact_id", "backend daily report fact row field"],
|
||||
[test, "getCleanupRequestId", "backend cleanup request test assertion"],
|
||||
[test, "getSubscriptionPolicyId", "backend subscription policy test assertion"],
|
||||
[test, "getDailyReportFactId", "backend daily report fact test assertion"],
|
||||
[qimingTypes, "cleanup_request_id", "qiming cleanup request type field"],
|
||||
[qimingTypes, "subscription_policy_id", "qiming subscription policy type field"],
|
||||
[qimingTypes, "daily_report_fact_id", "qiming daily report fact type field"],
|
||||
[qimingOps, "记录批量清理申请", "qiming cleanup request action"],
|
||||
[qimingOps, "订阅策略", "qiming subscription policy copy"],
|
||||
[qimingOps, "审批状态", "qiming daily approval status copy"],
|
||||
[docs, "正式管理端协作运营收口", "docs collaboration closure absorption"],
|
||||
];
|
||||
const missing = requiredSnippets
|
||||
.filter(([content, snippet]) => !content.includes(snippet))
|
||||
.map(([, , label]) => label);
|
||||
if (missing.length > 0) {
|
||||
throw new Error(`Missing admin collaboration operations closure hooks: ${missing.join(", ")}`);
|
||||
}
|
||||
console.log("[DigitalEmployeeCheck] admin collaboration operations closure hooks OK");
|
||||
}
|
||||
|
||||
function checkDigitalAdminServiceOperationsWorkbench() {
|
||||
console.log("\n[DigitalEmployeeCheck] Verify admin service operations workbench hooks");
|
||||
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
|
||||
@@ -2344,6 +2437,7 @@ function main() {
|
||||
checkDigitalAdminMemorySkillWorkbench();
|
||||
checkDigitalAdminPolicySchedulerWorkbenches();
|
||||
checkDigitalAdminPlanGovernanceWorkbenches();
|
||||
checkDigitalAdminCollaborationOperationsClosure();
|
||||
checkDigitalAdminServiceOperationsWorkbench();
|
||||
checkDigitalAdminRouteGovernanceWorkbench();
|
||||
checkDigitalAdminNotificationWorkbench();
|
||||
|
||||
@@ -584,7 +584,7 @@ GET /api/digital-employee/approvals
|
||||
|
||||
7. **产物交付闭环**
|
||||
- 已吸收:运行 payload 中的 artifacts / outputs / files / uri 可提取为正式 `digital_artifacts` 并同步;File Server workspace / upload / download / export / file update 生命周期状态已开始写入 Artifact payload 的 `delivery` 对象,embedded `/api/artifact` 和日报产物来源可读取交付阶段、状态、文件与 workspace 信息;Artifact 预览/下载权限已开始闭环,嵌入页通过受控 bridge 访问本地产物,并写入 `artifact_access_allowed` / `artifact_access_rejected` 审计事件;Artifact 版本/保留策略已开始闭环,业务产物视图可读取 version / retention 摘要,保留标记会写入 `artifact_retention_marked` / `artifact_retention_rejected` / `artifact_version_observed` 事件。真实文件清理执行已开始吸收,过期本地普通文件可通过独立 `cleanup` bridge 删除,清理结果会写入 `artifact_cleanup_executed` / `artifact_cleanup_rejected` / `artifact_cleanup_failed`,artifact payload 与业务视图会投影 `cleanup_status`、`deleted_at` 和原因字段。保留策略批量治理和跨设备聚合视图已开始吸收,embedded 业务视图新增 `artifact-groups` 产物聚合入口,可按版本/来源聚合产物、统计设备/版本/保留/清理状态,并通过受控批量接口写入保留、到期或复核标记;日报产物来源也提供批量标记到期入口。
|
||||
- 缺口:正式管理端产物生命周期预览已开始吸收,qiming“数字员工运营台”的“产物治理”tab 可只读聚合产物 lifecycle / access / retention / cleanup / version 业务视图,并通过低风险 admin action 记录 `mark_keep` / `mark_expire` / `mark_reviewed` 意图,等待客户端拉取执行;仍缺少批量真实文件清理、复杂跨设备图谱、正式外部生命周期工作台和更完整物化业务表。
|
||||
- 缺口:正式管理端产物生命周期预览已开始吸收,qiming“数字员工运营台”的“产物治理”tab 可只读聚合产物 lifecycle / access / retention / cleanup / version 业务视图,并通过低风险 admin action 记录 `mark_keep` / `mark_expire` / `mark_reviewed` 意图,等待客户端拉取执行;正式管理端协作运营收口已开始吸收,产物治理详情会展示 `cleanup_request_id`、`cleanup_eligible_count` 和 `batch_cleanup_disabled`,只允许记录批量清理申请,不执行批量真实清理。仍缺少批量真实文件清理、复杂跨设备图谱、正式外部生命周期工作台和更完整物化业务表。
|
||||
- 建议:下一轮围绕正式管理端生命周期工作台和跨设备图谱,把 embedded 预览聚合推进到跨端运营治理。
|
||||
|
||||
8. **审批 / 人工介入增强**
|
||||
@@ -594,7 +594,7 @@ GET /api/digital-employee/approvals
|
||||
|
||||
9. **通知 / Inbox / 用户消息**
|
||||
- 已吸收:运行事件和同步失败能在数字员工页面展示;embedded adapter 已接管 sgRobot 风格 `/api/notifications`、未读数、read/dismiss/reply,本地 UI state 会保存通知已读、忽略和回复 overlay;顶部 Bell 已开始展示 approval、同步失败、服务异常和任务完成通知;本地通知订阅偏好已接入 `/api/notifications/preferences`,可按总开关、桌面通知开关、等级和常用类型过滤 Inbox;`/api/digital-employee/notifications` 已提供管理端可消费的本地通知业务视图,通知动作和订阅偏好事件的 sync `business_view` 会归类为 notification 并提炼状态字段;`need_input` 通知首次回复会同步转化为 `provide_task_input` 治理命令,保留通知回复 overlay 的同时写入 Task/Run/Event/outbox;客户端已支持管理端通知状态拉取与本地通知动作回写,未读 action/warn/error 通知会按偏好触发一次系统级桌面提醒,系统权限引导 UI 已开始吸收,Bell 设置面板可展示桌面通知可用性、测试发送结果、自动通知失败原因,并通过系统设置入口辅助修复,`digital_workday_notification_*` 同步事件只暴露通知 ID、状态、endpoint、错误和回复长度,通知投影、本地订阅策略、桌面提醒、系统权限引导、跨端未读状态与任务输入链路已开始闭环。
|
||||
- 缺口:正式管理端通知运营预览已开始吸收,embedded 新增“通知运营”入口,可筛选全部/未读/待处理/异常通知,支持批量已读、批量忽略、拉取远端通知、打开系统通知设置和单条回复;qiming“数字员工运营台”的“通知运营”tab 可只读聚合通知业务视图,并通过低风险 admin action 记录 `mark_read` / `dismiss` 意图,等待客户端拉取执行;仍缺少正式外部通知协作工作台、复杂订阅策略 UI、真实通知发送/推送通道和跨设备通知冲突合并。
|
||||
- 缺口:正式管理端通知运营预览已开始吸收,embedded 新增“通知运营”入口,可筛选全部/未读/待处理/异常通知,支持批量已读、批量忽略、拉取远端通知、打开系统通知设置和单条回复;qiming“数字员工运营台”的“通知运营”tab 可只读聚合通知业务视图,并通过低风险 admin action 记录 `mark_read` / `dismiss` 意图,等待客户端拉取执行;正式管理端协作运营收口已开始吸收,通知运营详情会展示 `subscription_policy_id`、`channel`、`delivery_status` 和 `conflict_status`,用于解释订阅策略和跨设备状态。仍缺少正式外部通知协作工作台、复杂订阅策略 UI、真实通知发送/推送通道和跨设备通知冲突合并。
|
||||
- 建议:下一轮围绕正式管理端通知工作台,把 Inbox 从嵌入页投影推进到完整跨端协作入口。
|
||||
|
||||
10. **入口路由 / 任务分流(路由业务视图与通知联动已开始)**
|
||||
@@ -614,7 +614,7 @@ GET /api/digital-employee/approvals
|
||||
- 建议:先使用 sync record 派生业务查询模型和 embedded 业务视图预览承接联调,再按容量和查询压力决定是否拆 Plan / Task / Run / Event 物理业务表。
|
||||
|
||||
13. **日报实体与导出交付**
|
||||
- 已吸收:embedded 页面可基于当前 workday projection 导出本地文本日报;生成日报时会通过 qimingclaw bridge 写入 `daily_report` text / HTML / PDF Artifact 与 `daily_report_generated` Event,并可通过 `/api/digital-employee/daily-reports` 和详情接口读取只读日报投影。日报 HTML/PDF 导出、发送记录、管理端确认回执和日报发送审批已开始吸收,交付动作会写入 `daily_report_send_recorded` / `daily_report_confirmed` / `daily_report_approval_requested` 事件,sync `business_view.category = daily_report` 可被管理端筛选消费。管理端正式日报页面预览和正式管理端日报运营预览已开始吸收,embedded 新增“日报运营”入口,展示日报生成、交付、确认、审批和关联产物状态,并复用现有生成、记录发送、确认回执和请求审批动作;qiming-backend 的 `daily_reports` 运营台视图会聚合日报生成、发送记录、确认回执和审批请求,qiming“数字员工运营台”的“日报运营”tab 可记录已送达、确认回执、请求审批和发送结果。发送账单审计外壳已开始吸收,qiming-backend 新增 `/api/digital-employee/admin/daily-reports/send-records`,qiming 运营台可在日报详情中“记录发送结果”,但不接真实第三方发送通道。
|
||||
- 已吸收:embedded 页面可基于当前 workday projection 导出本地文本日报;生成日报时会通过 qimingclaw bridge 写入 `daily_report` text / HTML / PDF Artifact 与 `daily_report_generated` Event,并可通过 `/api/digital-employee/daily-reports` 和详情接口读取只读日报投影。日报 HTML/PDF 导出、发送记录、管理端确认回执和日报发送审批已开始吸收,交付动作会写入 `daily_report_send_recorded` / `daily_report_confirmed` / `daily_report_approval_requested` 事件,sync `business_view.category = daily_report` 可被管理端筛选消费。管理端正式日报页面预览和正式管理端日报运营预览已开始吸收,embedded 新增“日报运营”入口,展示日报生成、交付、确认、审批和关联产物状态,并复用现有生成、记录发送、确认回执和请求审批动作;qiming-backend 的 `daily_reports` 运营台视图会聚合日报生成、发送记录、确认回执和审批请求,qiming“数字员工运营台”的“日报运营”tab 可记录已送达、确认回执、请求审批和发送结果。发送账单审计外壳已开始吸收,qiming-backend 新增 `/api/digital-employee/admin/daily-reports/send-records`,qiming 运营台可在日报详情中“记录发送结果”,但不接真实第三方发送通道。正式管理端协作运营收口已开始吸收,日报运营详情会展示 `daily_report_fact_id`、`send_record_id` 和 `approval_decision_status`,用于解释事实来源、发送记录和审批状态。
|
||||
- 缺口:没有正式 DailyReport 物理表、真实管理端发送通道、正式外部管理端日报页面和审批裁决回写闭环。
|
||||
- 建议:先稳定 Artifact/Event 派生日报交付模型,再根据管理端联调需要决定是否物化 DailyReport 表与发送记录表。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user