From dc144189639a1216883fd10ca95779865caef555 Mon Sep 17 00:00:00 2001 From: baiyanyun Date: Fri, 5 Jun 2026 18:40:44 +0800 Subject: [PATCH] feat(backend): expose digital employee sync records --- ...DigitalEmployeeSyncApplicationService.java | 9 +++ .../digital/DigitalEmployeeSyncRecordDto.java | 29 +++++++++ .../DigitalEmployeeSyncRecordPageDto.java | 18 ++++++ .../DigitalEmployeeSyncRecordRepository.java | 9 +++ ...talEmployeeSyncApplicationServiceImpl.java | 62 +++++++++++++++++++ ...gitalEmployeeSyncRecordRepositoryImpl.java | 23 +++++++ .../DigitalEmployeeSyncController.java | 19 ++++++ qiming-backend/sql/update-20260605.sql | 1 + ...ital-employee-frontend-integration-plan.md | 12 +++- 9 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeSyncRecordDto.java create mode 100644 qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeSyncRecordPageDto.java diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/application/DigitalEmployeeSyncApplicationService.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/application/DigitalEmployeeSyncApplicationService.java index 72c4e62a..f7647772 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/application/DigitalEmployeeSyncApplicationService.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/application/DigitalEmployeeSyncApplicationService.java @@ -1,8 +1,17 @@ package com.xspaceagi.agent.core.adapter.application; +import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncRecordPageDto; import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncRequestDto; import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncResponseDto; public interface DigitalEmployeeSyncApplicationService { DigitalEmployeeSyncResponseDto syncOutbox(DigitalEmployeeSyncRequestDto request); + + DigitalEmployeeSyncRecordPageDto queryRecords( + String clientKey, + String entityType, + String entityId, + Long pageNo, + Long pageSize + ); } 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/DigitalEmployeeSyncRecordDto.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/DigitalEmployeeSyncRecordDto.java new file mode 100644 index 00000000..321b6973 --- /dev/null +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeSyncRecordDto.java @@ -0,0 +1,29 @@ +package com.xspaceagi.agent.core.adapter.dto.digital; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Data; + +import java.util.Date; + +@Data +@Builder +public class DigitalEmployeeSyncRecordDto { + private Long id; + @JsonProperty("client_key") + private String clientKey; + @JsonProperty("device_id") + private String deviceId; + @JsonProperty("outbox_id") + private String outboxId; + @JsonProperty("entity_type") + private String entityType; + @JsonProperty("entity_id") + private String entityId; + private String operation; + private String payload; + @JsonProperty("synced_at") + private Date syncedAt; + private Date modified; + private Date created; +} 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/DigitalEmployeeSyncRecordPageDto.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/DigitalEmployeeSyncRecordPageDto.java new file mode 100644 index 00000000..b7ea600f --- /dev/null +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeSyncRecordPageDto.java @@ -0,0 +1,18 @@ +package com.xspaceagi.agent.core.adapter.dto.digital; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +public class DigitalEmployeeSyncRecordPageDto { + private Long total; + @JsonProperty("page_no") + private Long pageNo; + @JsonProperty("page_size") + private Long pageSize; + private List records; +} 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/DigitalEmployeeSyncRecordRepository.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/repository/DigitalEmployeeSyncRecordRepository.java index e16698da..5b0f58cc 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/repository/DigitalEmployeeSyncRecordRepository.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/repository/DigitalEmployeeSyncRecordRepository.java @@ -1,7 +1,16 @@ package com.xspaceagi.agent.core.adapter.repository; +import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import com.xspaceagi.agent.core.adapter.repository.entity.DigitalEmployeeSyncRecord; public interface DigitalEmployeeSyncRecordRepository extends IService { + IPage queryRecords( + Long tenantId, + String clientKey, + String entityType, + String entityId, + long pageNo, + long pageSize + ); } 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 9fe5e3e8..81e4bc8f 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 @@ -2,11 +2,14 @@ package com.xspaceagi.agent.core.application.service; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.xspaceagi.agent.core.adapter.application.DigitalEmployeeSyncApplicationService; import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncAckDto; import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncItemDto; +import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncRecordDto; +import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncRecordPageDto; import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncRequestDto; import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncResponseDto; import com.xspaceagi.agent.core.adapter.repository.DigitalEmployeeSyncRecordRepository; @@ -23,6 +26,10 @@ import java.util.List; @Service public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmployeeSyncApplicationService { + private static final long DEFAULT_PAGE_NO = 1L; + private static final long DEFAULT_PAGE_SIZE = 20L; + private static final long MAX_PAGE_SIZE = 100L; + @Resource private DigitalEmployeeSyncRecordRepository digitalEmployeeSyncRecordRepository; @@ -81,6 +88,61 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye return DigitalEmployeeSyncResponseDto.builder().acked(acked).build(); } + @Override + public DigitalEmployeeSyncRecordPageDto queryRecords( + String clientKey, + String entityType, + String entityId, + Long pageNo, + Long pageSize + ) { + long currentPage = pageNo == null || pageNo < 1 ? DEFAULT_PAGE_NO : pageNo; + long currentPageSize = pageSize == null || pageSize < 1 + ? DEFAULT_PAGE_SIZE + : Math.min(pageSize, MAX_PAGE_SIZE); + Long tenantId = RequestContext.get() == null ? null : RequestContext.get().getTenantId(); + if (tenantId == null) { + return DigitalEmployeeSyncRecordPageDto.builder() + .total(0L) + .pageNo(currentPage) + .pageSize(currentPageSize) + .records(List.of()) + .build(); + } + + IPage page = digitalEmployeeSyncRecordRepository.queryRecords( + tenantId, + clientKey, + entityType, + entityId, + currentPage, + currentPageSize + ); + + return DigitalEmployeeSyncRecordPageDto.builder() + .total(page.getTotal()) + .pageNo(page.getCurrent()) + .pageSize(page.getSize()) + .records(page.getRecords().stream().map(this::toRecordDto).toList()) + .build(); + } + + private DigitalEmployeeSyncRecordDto toRecordDto(DigitalEmployeeSyncRecord record) { + return DigitalEmployeeSyncRecordDto.builder() + .id(record.getId()) + .clientKey(record.getClientKey()) + .deviceId(record.getDeviceId()) + .outboxId(record.getOutboxId()) + .entityType(record.getEntityType()) + .entityId(record.getEntityId()) + .operation(record.getOperation()) + .payload(record.getPayload()) + .syncedAt(record.getSyncedAt()) + .modified(record.getModified()) + .created(record.getCreated()) + .build(); + } + private String toJson(Object payload) { if (payload == null) { return "{}"; diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-infra/src/main/java/com/xspaceagi/agent/core/infra/repository/DigitalEmployeeSyncRecordRepositoryImpl.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-infra/src/main/java/com/xspaceagi/agent/core/infra/repository/DigitalEmployeeSyncRecordRepositoryImpl.java index 9a46dae1..622a63ff 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-infra/src/main/java/com/xspaceagi/agent/core/infra/repository/DigitalEmployeeSyncRecordRepositoryImpl.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-infra/src/main/java/com/xspaceagi/agent/core/infra/repository/DigitalEmployeeSyncRecordRepositoryImpl.java @@ -1,13 +1,36 @@ package com.xspaceagi.agent.core.infra.repository; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xspaceagi.agent.core.adapter.repository.DigitalEmployeeSyncRecordRepository; import com.xspaceagi.agent.core.adapter.repository.entity.DigitalEmployeeSyncRecord; import com.xspaceagi.agent.core.infra.dao.mapper.DigitalEmployeeSyncRecordMapper; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @Service public class DigitalEmployeeSyncRecordRepositoryImpl extends ServiceImpl implements DigitalEmployeeSyncRecordRepository { + + @Override + public IPage queryRecords( + Long tenantId, + String clientKey, + String entityType, + String entityId, + long pageNo, + long pageSize + ) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .eq(tenantId != null, DigitalEmployeeSyncRecord::getTenantId, tenantId) + .eq(StringUtils.isNotBlank(clientKey), DigitalEmployeeSyncRecord::getClientKey, clientKey) + .eq(StringUtils.isNotBlank(entityType), DigitalEmployeeSyncRecord::getEntityType, entityType) + .eq(StringUtils.isNotBlank(entityId), DigitalEmployeeSyncRecord::getEntityId, entityId) + .orderByDesc(DigitalEmployeeSyncRecord::getSyncedAt) + .orderByDesc(DigitalEmployeeSyncRecord::getId); + return page(new Page<>(pageNo, pageSize), queryWrapper); + } } diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-ui/src/main/java/com/xspaceagi/agent/web/ui/controller/DigitalEmployeeSyncController.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-ui/src/main/java/com/xspaceagi/agent/web/ui/controller/DigitalEmployeeSyncController.java index 9bdcaace..d9a2d8c5 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-ui/src/main/java/com/xspaceagi/agent/web/ui/controller/DigitalEmployeeSyncController.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-ui/src/main/java/com/xspaceagi/agent/web/ui/controller/DigitalEmployeeSyncController.java @@ -1,6 +1,7 @@ package com.xspaceagi.agent.web.ui.controller; import com.xspaceagi.agent.core.adapter.application.DigitalEmployeeSyncApplicationService; +import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncRecordPageDto; import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncRequestDto; import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncResponseDto; import com.xspaceagi.system.spec.dto.ReqResult; @@ -8,6 +9,7 @@ import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @@ -21,4 +23,21 @@ public class DigitalEmployeeSyncController { public ReqResult syncOutbox(@RequestBody DigitalEmployeeSyncRequestDto request) { return ReqResult.success(digitalEmployeeSyncApplicationService.syncOutbox(request)); } + + @RequestMapping(path = "/records", method = RequestMethod.GET) + public ReqResult queryRecords( + @RequestParam(name = "client_key", required = false) String clientKey, + @RequestParam(name = "entity_type", required = false) String entityType, + @RequestParam(name = "entity_id", required = false) String entityId, + @RequestParam(name = "page_no", required = false) Long pageNo, + @RequestParam(name = "page_size", required = false) Long pageSize + ) { + return ReqResult.success(digitalEmployeeSyncApplicationService.queryRecords( + clientKey, + entityType, + entityId, + pageNo, + pageSize + )); + } } diff --git a/qiming-backend/sql/update-20260605.sql b/qiming-backend/sql/update-20260605.sql index e1e47f4a..7e82dcb1 100644 --- a/qiming-backend/sql/update-20260605.sql +++ b/qiming-backend/sql/update-20260605.sql @@ -16,6 +16,7 @@ CREATE TABLE `digital_employee_sync_record` PRIMARY KEY (`id`), UNIQUE KEY `uk_client_outbox` (`client_key`, `outbox_id`), KEY `idx_client_entity` (`client_key`, `entity_type`, `entity_id`), + KEY `idx_tenant_client_entity_time` (`_tenant_id`, `client_key`, `entity_type`, `entity_id`, `synced_at`), KEY `idx_tenant_user_time` (`_tenant_id`, `user_id`, `synced_at`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 diff --git a/qimingclaw/docs/digital-employee-frontend-integration-plan.md b/qimingclaw/docs/digital-employee-frontend-integration-plan.md index 40b08568..e1991d23 100644 --- a/qimingclaw/docs/digital-employee-frontend-integration-plan.md +++ b/qimingclaw/docs/digital-employee-frontend-integration-plan.md @@ -470,10 +470,20 @@ qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-a qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/.../DigitalEmployeeSyncRecord.java ``` +后端观察接口: + +```text +GET /api/digital-employee/sync/records +``` + +- 支持 `client_key`、`entity_type`、`entity_id`、`page_no`、`page_size` 查询参数。 +- 按当前登录上下文租户隔离,默认每页 20 条,最大每页 100 条。 +- 当前返回通用同步记录和原始 payload,用于管理端页面或联调工具先查看同步结果;后续再拆分为更丰富的 Plan / Task / Run / Event 查询模型。 + 下一步: - 在有 Maven 的环境验证 qiming-backend 编译与接口启动。 -- 数字员工页面继续细化同步状态交互,例如手动同步按钮和失败原因展开。 +- 数字员工页面继续细化同步状态交互,例如失败原因展开。 ## 管理端联动原则