diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java index 8782c83c..5140ffce 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java @@ -133,6 +133,10 @@ public class DigitalEmployeeAdminWorkbenchRowDto { private String reportId; @JsonProperty("daily_report_fact_id") private String dailyReportFactId; + @JsonProperty("daily_report_table") + private String dailyReportTable; + @JsonProperty("daily_report_materialized") + private Boolean dailyReportMaterialized; @JsonProperty("daily_report_physical_fact_status") private String dailyReportPhysicalFactStatus; @JsonProperty("send_record_id") @@ -141,6 +145,10 @@ public class DigitalEmployeeAdminWorkbenchRowDto { private String sendChannel; @JsonProperty("send_request_status") private String sendRequestStatus; + @JsonProperty("send_record_status") + private String sendRecordStatus; + @JsonProperty("formal_daily_report_page_disabled") + private Boolean formalDailyReportPageDisabled; @JsonProperty("external_send_disabled") private Boolean externalSendDisabled; @JsonProperty("third_party_channel_disabled") diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/repository/entity/DigitalEmployeeDailyReportFact.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/repository/entity/DigitalEmployeeDailyReportFact.java index 670dd700..15145e01 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/repository/entity/DigitalEmployeeDailyReportFact.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/repository/entity/DigitalEmployeeDailyReportFact.java @@ -25,12 +25,20 @@ public class DigitalEmployeeDailyReportFact { private String clientKey; private String deviceId; private String reportId; + private String dailyReportFactId; private String planId; private String status; + private String physicalFactStatus; private String deliveryStatus; private String approvalStatus; + private String approvalDecisionStatus; private String confirmedAt; private String reportDate; + private String sendRecordId; + private String sendChannel; + private String sendRequestStatus; + private String sendRecordStatus; + private String materializedSource; private String businessView; private Date syncedAt; private Date modified; diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java index 15037831..fb6ab6c4 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java @@ -300,12 +300,20 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye .clientKey(record.getClientKey()) .deviceId(record.getDeviceId()) .reportId(reportId) + .dailyReportFactId(firstString(view.get("daily_report_fact_id"), view.get("dailyReportFactId"))) .planId(row.getPlanId()) .status(row.getStatus()) + .physicalFactStatus(firstString(view.get("daily_report_physical_fact_status"), view.get("dailyReportPhysicalFactStatus"), view.get("physical_fact_status"), view.get("physicalFactStatus"))) .deliveryStatus(firstString(view.get("delivery_status"), view.get("deliveryStatus"))) .approvalStatus(firstString(view.get("approval_status"), view.get("approvalStatus"))) + .approvalDecisionStatus(firstString(view.get("approval_decision_status"), view.get("approvalDecisionStatus"))) .confirmedAt(firstString(view.get("confirmed_at"), view.get("confirmedAt"))) .reportDate(firstString(view.get("report_date"), view.get("reportDate"))) + .sendRecordId(firstString(view.get("send_record_id"), view.get("sendRecordId"))) + .sendChannel(firstString(view.get("send_channel"), view.get("sendChannel"))) + .sendRequestStatus(firstString(view.get("send_request_status"), view.get("sendRequestStatus"))) + .sendRecordStatus(firstString(view.get("send_record_status"), view.get("sendRecordStatus"), view.get("send_request_status"), view.get("sendRequestStatus"))) + .materializedSource(firstString(view.get("materialized_source"), view.get("materializedSource"), "digital_employee_daily_report_fact")) .businessView(record.getBusinessView()) .syncedAt(record.getSyncedAt()) .modified(now) @@ -1160,10 +1168,14 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye .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"))) + .dailyReportTable(firstString(businessView.get("daily_report_table"), businessView.get("dailyReportTable"), payload.get("daily_report_table"), payload.get("dailyReportTable"), "digital_employee_daily_report_fact")) + .dailyReportMaterialized(nullableBooleanValue(firstPresent(businessView.get("daily_report_materialized"), businessView.get("dailyReportMaterialized"), payload.get("daily_report_materialized"), payload.get("dailyReportMaterialized")))) .dailyReportPhysicalFactStatus(firstString(businessView.get("daily_report_physical_fact_status"), businessView.get("dailyReportPhysicalFactStatus"), payload.get("daily_report_physical_fact_status"), payload.get("dailyReportPhysicalFactStatus"))) .sendRecordId(firstString(businessView.get("send_record_id"), businessView.get("sendRecordId"), payload.get("send_record_id"), payload.get("sendRecordId"))) .sendChannel(firstString(businessView.get("send_channel"), businessView.get("sendChannel"), payload.get("send_channel"), payload.get("sendChannel"))) .sendRequestStatus(firstString(businessView.get("send_request_status"), businessView.get("sendRequestStatus"), payload.get("send_request_status"), payload.get("sendRequestStatus"))) + .sendRecordStatus(firstString(businessView.get("send_record_status"), businessView.get("sendRecordStatus"), payload.get("send_record_status"), payload.get("sendRecordStatus"), businessView.get("send_request_status"), payload.get("send_request_status"))) + .formalDailyReportPageDisabled(nullableBooleanValue(firstPresent(businessView.get("formal_daily_report_page_disabled"), businessView.get("formalDailyReportPageDisabled"), payload.get("formal_daily_report_page_disabled"), payload.get("formalDailyReportPageDisabled")))) .externalSendDisabled(nullableBooleanValue(firstPresent(businessView.get("external_send_disabled"), businessView.get("externalSendDisabled"), payload.get("external_send_disabled"), payload.get("externalSendDisabled")))) .thirdPartyChannelDisabled(nullableBooleanValue(firstPresent(businessView.get("third_party_channel_disabled"), businessView.get("thirdPartyChannelDisabled"), payload.get("third_party_channel_disabled"), payload.get("thirdPartyChannelDisabled")))) .approvalDecisionStatus(firstString(businessView.get("approval_decision_status"), businessView.get("approvalDecisionStatus"), payload.get("approval_decision_status"), payload.get("approvalDecisionStatus"))) @@ -1448,8 +1460,12 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye || startsWithAny(row.getKind(), "daily_report") || StringUtils.isNotBlank(row.getReportId()) || StringUtils.isNotBlank(row.getDailyReportPhysicalFactStatus()) + || StringUtils.isNotBlank(row.getDailyReportTable()) + || row.getDailyReportMaterialized() != null || StringUtils.isNotBlank(row.getSendChannel()) || StringUtils.isNotBlank(row.getSendRequestStatus()) + || StringUtils.isNotBlank(row.getSendRecordStatus()) + || row.getFormalDailyReportPageDisabled() != null || row.getExternalSendDisabled() != null || row.getThirdPartyChannelDisabled() != null || StringUtils.isNotBlank(row.getDailyReportApprovalId()) diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java index 1313c2c8..53b9115e 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java @@ -27,6 +27,7 @@ import com.xspaceagi.agent.core.adapter.repository.DigitalEmployeeDailyReportFac import com.xspaceagi.agent.core.adapter.repository.DigitalEmployeeSyncRecordRepository; import com.xspaceagi.agent.core.adapter.repository.entity.DigitalEmployeeAdminAction; import com.xspaceagi.agent.core.adapter.repository.entity.DigitalEmployeeBusinessFact; +import com.xspaceagi.agent.core.adapter.repository.entity.DigitalEmployeeDailyReportFact; import com.xspaceagi.agent.core.adapter.repository.entity.DigitalEmployeeSyncRecord; import com.xspaceagi.system.spec.common.RequestContext; import org.junit.jupiter.api.AfterEach; @@ -123,6 +124,59 @@ class DigitalEmployeeSyncApplicationServiceImplTest { assertThat(factCaptor.getValue().getStatus()).isEqualTo("active"); } + @Test + void syncOutboxMaterializesDailyReportPhysicalFactAndSendRecord() { + DigitalEmployeeSyncItemDto item = new DigitalEmployeeSyncItemDto(); + item.setOutboxId("outbox-daily-report-1"); + item.setEntityType("daily_report"); + item.setEntityId("report-1"); + item.setOperation("upsert"); + item.setBusinessView(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("daily_report_physical_fact_status", "materialized"), + Map.entry("approval_decision_status", "approved"), + Map.entry("send_record_id", "send-record-1"), + Map.entry("send_channel", "email"), + Map.entry("send_request_status", "recorded"), + Map.entry("send_record_status", "materialized"), + Map.entry("materialized_source", "digital_employee_daily_report_fact"), + Map.entry("summary", "日报已物化") + )); + item.setPayload(Map.of("id", "report-1", "kind", "daily_report_generated")); + + DigitalEmployeeSyncRequestDto request = new DigitalEmployeeSyncRequestDto(); + request.setClientKey("client-key-001"); + request.setDeviceId("device-001"); + request.setItems(List.of(item)); + doAnswer(invocation -> { + DigitalEmployeeSyncRecord saved = invocation.getArgument(0); + saved.setId(1002L); + return true; + }).when(repository).save(any(DigitalEmployeeSyncRecord.class)); + + service.syncOutbox(request); + + ArgumentCaptor factCaptor = ArgumentCaptor.forClass(DigitalEmployeeDailyReportFact.class); + verify(dailyReportFactRepository).upsertBySyncRecord(factCaptor.capture()); + DigitalEmployeeDailyReportFact fact = factCaptor.getValue(); + assertThat(fact.getSyncRecordId()).isEqualTo(1002L); + assertThat(fact.getReportId()).isEqualTo("report-1"); + assertThat(fact.getDailyReportFactId()).isEqualTo("daily-report-fact-1"); + assertThat(fact.getPhysicalFactStatus()).isEqualTo("materialized"); + assertThat(fact.getApprovalDecisionStatus()).isEqualTo("approved"); + assertThat(fact.getSendRecordId()).isEqualTo("send-record-1"); + assertThat(fact.getSendChannel()).isEqualTo("email"); + assertThat(fact.getSendRequestStatus()).isEqualTo("recorded"); + assertThat(fact.getSendRecordStatus()).isEqualTo("materialized"); + assertThat(fact.getMaterializedSource()).isEqualTo("digital_employee_daily_report_fact"); + } + @Test void queryRecordsReturnsStoredBusinessMetadata() { DigitalEmployeeSyncRecord record = planRecord("plan-1", "客户回访", "active", 101L, new Date(1_000)); @@ -2053,10 +2107,14 @@ class DigitalEmployeeSyncApplicationServiceImplTest { Map.entry("title", "6 月 13 日日报"), Map.entry("report_id", "report-1"), Map.entry("daily_report_fact_id", "daily-report-fact-1"), + Map.entry("daily_report_table", "digital_employee_daily_report_fact"), + Map.entry("daily_report_materialized", true), Map.entry("daily_report_physical_fact_status", "pending_materialization"), Map.entry("send_record_id", "send-record-1"), Map.entry("send_channel", "email"), Map.entry("send_request_status", "recorded"), + Map.entry("send_record_status", "materialized"), + Map.entry("formal_daily_report_page_disabled", true), Map.entry("external_send_disabled", true), Map.entry("third_party_channel_disabled", true), Map.entry("approval_decision_status", "pending"), @@ -2128,10 +2186,14 @@ class DigitalEmployeeSyncApplicationServiceImplTest { 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).getDailyReportTable()).isEqualTo("digital_employee_daily_report_fact"); + assertThat(result.getRecords().get(0).getDailyReportMaterialized()).isTrue(); assertThat(result.getRecords().get(0).getDailyReportPhysicalFactStatus()).isEqualTo("pending_materialization"); assertThat(result.getRecords().get(0).getSendRecordId()).isEqualTo("send-record-1"); assertThat(result.getRecords().get(0).getSendChannel()).isEqualTo("email"); assertThat(result.getRecords().get(0).getSendRequestStatus()).isEqualTo("recorded"); + assertThat(result.getRecords().get(0).getSendRecordStatus()).isEqualTo("materialized"); + assertThat(result.getRecords().get(0).getFormalDailyReportPageDisabled()).isTrue(); assertThat(result.getRecords().get(0).getExternalSendDisabled()).isTrue(); assertThat(result.getRecords().get(0).getThirdPartyChannelDisabled()).isTrue(); assertThat(result.getRecords().get(0).getApprovalDecisionStatus()).isEqualTo("pending"); diff --git a/qiming-backend/sql/update-20260612.sql b/qiming-backend/sql/update-20260612.sql index 6d17da77..1b48883e 100644 --- a/qiming-backend/sql/update-20260612.sql +++ b/qiming-backend/sql/update-20260612.sql @@ -92,12 +92,20 @@ CREATE TABLE IF NOT EXISTS `digital_employee_daily_report_fact` ( `client_key` VARCHAR(255) DEFAULT NULL COMMENT '客户端Key', `device_id` VARCHAR(255) DEFAULT NULL COMMENT '设备ID', `report_id` VARCHAR(255) NOT NULL COMMENT '日报ID', + `daily_report_fact_id` VARCHAR(255) DEFAULT NULL COMMENT '日报事实ID', `plan_id` VARCHAR(255) DEFAULT NULL COMMENT '计划ID', `status` VARCHAR(128) DEFAULT NULL COMMENT '状态', + `physical_fact_status` VARCHAR(128) DEFAULT NULL COMMENT '物理事实状态', `delivery_status` VARCHAR(128) DEFAULT NULL COMMENT '交付状态', `approval_status` VARCHAR(128) DEFAULT NULL COMMENT '审批状态', + `approval_decision_status` VARCHAR(128) DEFAULT NULL COMMENT '审批裁决状态', `confirmed_at` VARCHAR(64) DEFAULT NULL COMMENT '确认时间', `report_date` VARCHAR(64) DEFAULT NULL COMMENT '日报日期', + `send_record_id` VARCHAR(255) DEFAULT NULL COMMENT '发送记录ID', + `send_channel` VARCHAR(128) DEFAULT NULL COMMENT '发送渠道', + `send_request_status` VARCHAR(128) DEFAULT NULL COMMENT '发送请求状态', + `send_record_status` VARCHAR(128) DEFAULT NULL COMMENT '发送记录状态', + `materialized_source` VARCHAR(128) DEFAULT NULL COMMENT '物化来源', `business_view` JSON DEFAULT NULL COMMENT '业务视图快照', `synced_at` DATETIME DEFAULT NULL COMMENT '同步时间', `modified` DATETIME NOT NULL COMMENT '修改时间', diff --git a/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx b/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx index 82097ef8..e9e59c78 100644 --- a/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx +++ b/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx @@ -1223,6 +1223,10 @@ const DigitalEmployeeOps: React.FC = () => { {detailRecord.report_id || detailRecord.entity_id || '--'} {detailRecord.daily_report_fact_id && ` · 事实 ${detailRecord.daily_report_fact_id}`} + {detailRecord.daily_report_table && + ` · 正式日报物理表 ${detailRecord.daily_report_table}`} + {detailRecord.daily_report_materialized != null && + ` · 物化 ${detailRecord.daily_report_materialized ? '已写入' : '未写入'}`} {detailRecord.daily_report_physical_fact_status && ` · 物理事实 ${detailRecord.daily_report_physical_fact_status}`} {detailRecord.send_record_id && @@ -1230,8 +1234,11 @@ const DigitalEmployeeOps: React.FC = () => { {detailRecord.send_channel && ` · 渠道 ${detailRecord.send_channel}`} {detailRecord.send_request_status && ` · 发送请求状态 ${detailRecord.send_request_status}`} + {detailRecord.send_record_status && + ` · 发送记录物化 ${detailRecord.send_record_status}`} {detailRecord.approval_decision_status && ` · 审批状态 ${detailRecord.approval_decision_status}`} + {detailRecord.formal_daily_report_page_disabled && ' · 正式外部日报页面禁用'} {detailRecord.external_send_disabled && ' · 外部发送禁用'} {detailRecord.third_party_channel_disabled && ' · 第三方发送禁用'} {detailRecord.daily_report_approval_id && @@ -1246,7 +1253,7 @@ const DigitalEmployeeOps: React.FC = () => { ` · 裁决记录 ${detailRecord.daily_report_approval_recorded_at}`} {' '} - · 展示发送记录、审批状态、审批裁决、物理事实状态和事实来源,只记录发送请求/发送结果/审批/回执/裁决意图,不触发真实外部发送或第三方发送。 + · 展示正式日报物理表、发送记录物化、审批状态、审批裁决、物理事实状态和事实来源,只记录发送请求/发送结果/审批/回执/裁决意图,不触发真实外部发送或第三方发送,正式外部日报页面仍禁用。 )} diff --git a/qiming/src/types/interfaces/systemManage.ts b/qiming/src/types/interfaces/systemManage.ts index 0253735c..98126e42 100644 --- a/qiming/src/types/interfaces/systemManage.ts +++ b/qiming/src/types/interfaces/systemManage.ts @@ -652,10 +652,14 @@ export interface DigitalEmployeeAdminWorkbenchRow { conflict_status?: string; report_id?: string; daily_report_fact_id?: string; + daily_report_table?: string; + daily_report_materialized?: boolean; daily_report_physical_fact_status?: string; send_record_id?: string; send_channel?: string; send_request_status?: string; + send_record_status?: string; + formal_daily_report_page_disabled?: boolean; external_send_disabled?: boolean; third_party_channel_disabled?: boolean; approval_decision_status?: string; diff --git a/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js b/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js index 7112cf59..091d03d7 100644 --- a/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js +++ b/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js @@ -3129,20 +3129,66 @@ function checkDigitalAdminDailyReportWorkbench() { const qimingOps = fs.readFileSync(qimingOpsPath, "utf8"); const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8"); const docs = fs.readFileSync(docsPath, "utf8"); + const dailyReportFactEntity = fs.readFileSync( + path.join( + backendRoot, + "app-platform-modules", + "app-platform-agent", + "app-platform-agent-core-adapter", + "src", + "main", + "java", + "com", + "xspaceagi", + "agent", + "core", + "adapter", + "repository", + "entity", + "DigitalEmployeeDailyReportFact.java", + ), + "utf8", + ); + const sql = fs.readFileSync(path.join(backendRoot, "sql", "update-20260612.sql"), "utf8"); const requiredSnippets = [ [service, "daily_reports", "backend daily report view"], [service, "isDailyReportRow", "backend daily report matcher"], [service, "daily_report", "backend daily report category"], [service, "daily_report:record_approval_decision", "backend daily report approval decision action"], + [service, "daily_report_table", "backend daily report table projection"], + [service, "daily_report_materialized", "backend daily report materialized projection"], + [service, "send_record_status", "backend send record status projection"], + [service, "formal_daily_report_page_disabled", "backend formal daily report page guard projection"], [dto, "report_id", "backend report id row field"], + [dto, "daily_report_table", "backend daily report table row field"], + [dto, "daily_report_materialized", "backend daily report materialized row field"], + [dto, "send_record_status", "backend send record status row field"], + [dto, "formal_daily_report_page_disabled", "backend formal daily report page guard row field"], [dto, "daily_report_approval_id", "backend daily report approval id row field"], [dto, "daily_report_approval_action", "backend daily report approval action row field"], [dto, "daily_report_approval_recorded_at", "backend daily report approval recorded at row field"], + [dailyReportFactEntity, "dailyReportFactId", "daily report fact entity fact id field"], + [dailyReportFactEntity, "physicalFactStatus", "daily report fact entity physical status field"], + [dailyReportFactEntity, "sendRecordStatus", "daily report fact entity send status field"], + [dailyReportFactEntity, "materializedSource", "daily report fact entity materialized source field"], + [sql, "daily_report_fact_id", "daily report fact sql fact id column"], + [sql, "physical_fact_status", "daily report fact sql physical status column"], + [sql, "send_record_status", "daily report fact sql send status column"], + [sql, "materialized_source", "daily report fact sql materialized source column"], [test, "queryAdminWorkbenchDailyReportsReturnsDailyReportRowsOnly", "backend daily report workbench test"], + [test, "syncOutboxMaterializesDailyReportPhysicalFactAndSendRecord", "backend daily report physical fact materialization test"], + [test, "queryAdminWorkbenchDailyReportsReturnsPhysicalFactAndSendShellRows", "backend daily report physical send shell test"], [test, "getDailyReportApprovalId", "backend daily report approval assertion"], [qimingTypes, "daily_reports", "qiming daily report view type"], + [qimingTypes, "daily_report_table", "qiming daily report table type field"], + [qimingTypes, "daily_report_materialized", "qiming daily report materialized type field"], + [qimingTypes, "send_record_status", "qiming send record status type field"], + [qimingTypes, "formal_daily_report_page_disabled", "qiming formal daily report page guard type field"], [qimingTypes, "daily_report_approval_id", "qiming daily report approval id type field"], [qimingOps, "日报运营", "qiming daily report tab"], + [qimingOps, "正式日报物理表", "qiming formal daily report physical table copy"], + [qimingOps, "发送记录物化", "qiming send record materialized copy"], + [qimingOps, "正式外部日报页面禁用", "qiming formal daily report page disabled copy"], [qimingOps, "记录已送达", "qiming daily report delivered action"], [qimingOps, "确认回执", "qiming daily report confirmed action"], [qimingOps, "请求审批", "qiming daily report approval action"], @@ -3150,6 +3196,7 @@ function checkDigitalAdminDailyReportWorkbench() { [qimingOps, "记录发送结果", "qiming daily report send record action"], [docs, "正式管理端日报运营预览", "docs daily report absorption"], [docs, "管理端日报审批裁决回写预览", "docs daily report approval decision absorption"], + [docs, "正式日报物理事实与发送记录预览已开始吸收", "docs daily report physical fact send record absorption"], ]; const missing = requiredSnippets .filter(([content, snippet]) => !content.includes(snippet)) diff --git a/qimingclaw/docs/digital-employee-frontend-integration-plan.md b/qimingclaw/docs/digital-employee-frontend-integration-plan.md index e9e2b59f..7916f5ee 100644 --- a/qimingclaw/docs/digital-employee-frontend-integration-plan.md +++ b/qimingclaw/docs/digital-employee-frontend-integration-plan.md @@ -614,9 +614,9 @@ 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 运营台可在日报详情中“记录发送结果”,但不接真实第三方发送通道。正式管理端协作运营收口已开始吸收,日报运营详情会展示 `daily_report_fact_id`、`send_record_id` 和 `approval_decision_status`,用于解释事实来源、发送记录和审批状态。管理端日报审批裁决回写预览已开始吸收,日报运营详情可展示 `daily_report_approval_id`、裁决动作、裁决人、原因和记录时间,并只记录审批裁决意图,不触发真实发送或自动审批。资产通知日报业务查询总收口已开始吸收,日报运营详情会补充 `daily_report_physical_fact_status`、发送渠道、发送请求状态、外部发送禁用和第三方发送禁用说明。 - - 缺口:没有正式 DailyReport 物理表、真实管理端发送通道和正式外部管理端日报页面。 - - 建议:先稳定 Artifact/Event 派生日报交付模型,再根据管理端联调需要决定是否物化 DailyReport 表与发送记录表。 + - 已吸收: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`,用于解释事实来源、发送记录和审批状态。管理端日报审批裁决回写预览已开始吸收,日报运营详情可展示 `daily_report_approval_id`、裁决动作、裁决人、原因和记录时间,并只记录审批裁决意图,不触发真实发送或自动审批。资产通知日报业务查询总收口已开始吸收,日报运营详情会补充 `daily_report_physical_fact_status`、发送渠道、发送请求状态、外部发送禁用和第三方发送禁用说明。正式日报物理事实与发送记录预览已开始吸收,后端复用 `digital_employee_daily_report_fact` 增量物化日报事实、物理事实状态、审批裁决状态、发送记录状态和物化来源,qiming 日报运营详情展示正式日报物理表、发送记录物化和正式外部日报页面禁用状态。 + - 缺口:真实管理端发送通道、第三方发送通道、更完整 DailyReport 物理模型和正式外部管理端日报页面。 + - 建议:先稳定 `digital_employee_daily_report_fact` 增量物化与 Artifact/Event 派生交付模型,再根据管理端联调需要决定是否扩展正式 DailyReport 表、发送记录表和外部页面。 ## 管理端联动原则