From ab1d0229053b03d99b187f7aeb5fd7b5a1d3b4a0 Mon Sep 17 00:00:00 2001 From: baiyanyun Date: Fri, 5 Jun 2026 17:21:48 +0800 Subject: [PATCH] feat: add digital employee management sync endpoint --- ...DigitalEmployeeSyncApplicationService.java | 8 ++ .../digital/DigitalEmployeeSyncAckDto.java | 18 ++++ .../digital/DigitalEmployeeSyncItemDto.java | 18 ++++ .../DigitalEmployeeSyncRequestDto.java | 15 +++ .../DigitalEmployeeSyncResponseDto.java | 12 +++ .../DigitalEmployeeSyncRecordRepository.java | 7 ++ .../entity/DigitalEmployeeSyncRecord.java | 35 +++++++ ...talEmployeeSyncApplicationServiceImpl.java | 94 +++++++++++++++++++ .../DigitalEmployeeSyncRecordMapper.java | 9 ++ ...gitalEmployeeSyncRecordRepositoryImpl.java | 13 +++ .../DigitalEmployeeSyncController.java | 24 +++++ qiming-backend/sql/update-20260605.sql | 23 +++++ .../services/digitalEmployee/syncService.ts | 21 ++++- ...ital-employee-frontend-integration-plan.md | 16 +++- 14 files changed, 308 insertions(+), 5 deletions(-) create mode 100644 qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/application/DigitalEmployeeSyncApplicationService.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/DigitalEmployeeSyncAckDto.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/DigitalEmployeeSyncItemDto.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/DigitalEmployeeSyncRequestDto.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/DigitalEmployeeSyncResponseDto.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/repository/DigitalEmployeeSyncRecordRepository.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/repository/entity/DigitalEmployeeSyncRecord.java create mode 100644 qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java create mode 100644 qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-infra/src/main/java/com/xspaceagi/agent/core/infra/dao/mapper/DigitalEmployeeSyncRecordMapper.java create mode 100644 qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-infra/src/main/java/com/xspaceagi/agent/core/infra/repository/DigitalEmployeeSyncRecordRepositoryImpl.java create mode 100644 qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-ui/src/main/java/com/xspaceagi/agent/web/ui/controller/DigitalEmployeeSyncController.java create mode 100644 qiming-backend/sql/update-20260605.sql 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 new file mode 100644 index 00000000..72c4e62a --- /dev/null +++ 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 @@ -0,0 +1,8 @@ +package com.xspaceagi.agent.core.adapter.application; + +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); +} 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/DigitalEmployeeSyncAckDto.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/DigitalEmployeeSyncAckDto.java new file mode 100644 index 00000000..4e49fab5 --- /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/DigitalEmployeeSyncAckDto.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; + +@Data +@Builder +public class DigitalEmployeeSyncAckDto { + @JsonProperty("outbox_id") + private String outboxId; + @JsonProperty("entity_type") + private String entityType; + @JsonProperty("entity_id") + private String entityId; + @JsonProperty("remote_id") + private String remoteId; +} 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/DigitalEmployeeSyncItemDto.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/DigitalEmployeeSyncItemDto.java new file mode 100644 index 00000000..649ecdaf --- /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/DigitalEmployeeSyncItemDto.java @@ -0,0 +1,18 @@ +package com.xspaceagi.agent.core.adapter.dto.digital; + +import com.fasterxml.jackson.annotation.JsonAlias; +import lombok.Data; + +import java.util.Map; + +@Data +public class DigitalEmployeeSyncItemDto { + @JsonAlias("outbox_id") + private String outboxId; + @JsonAlias("entity_type") + private String entityType; + @JsonAlias("entity_id") + private String entityId; + private String operation; + private Map payload; +} 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/DigitalEmployeeSyncRequestDto.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/DigitalEmployeeSyncRequestDto.java new file mode 100644 index 00000000..955dbf72 --- /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/DigitalEmployeeSyncRequestDto.java @@ -0,0 +1,15 @@ +package com.xspaceagi.agent.core.adapter.dto.digital; + +import com.fasterxml.jackson.annotation.JsonAlias; +import lombok.Data; + +import java.util.List; + +@Data +public class DigitalEmployeeSyncRequestDto { + @JsonAlias("client_key") + private String clientKey; + @JsonAlias("device_id") + private String deviceId; + private List items; +} 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/DigitalEmployeeSyncResponseDto.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/DigitalEmployeeSyncResponseDto.java new file mode 100644 index 00000000..9f1ebdf5 --- /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/DigitalEmployeeSyncResponseDto.java @@ -0,0 +1,12 @@ +package com.xspaceagi.agent.core.adapter.dto.digital; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +public class DigitalEmployeeSyncResponseDto { + private List acked; +} 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 new file mode 100644 index 00000000..e16698da --- /dev/null +++ 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 @@ -0,0 +1,7 @@ +package com.xspaceagi.agent.core.adapter.repository; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xspaceagi.agent.core.adapter.repository.entity.DigitalEmployeeSyncRecord; + +public interface DigitalEmployeeSyncRecordRepository extends IService { +} 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/DigitalEmployeeSyncRecord.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/DigitalEmployeeSyncRecord.java new file mode 100644 index 00000000..f579a891 --- /dev/null +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/repository/entity/DigitalEmployeeSyncRecord.java @@ -0,0 +1,35 @@ +package com.xspaceagi.agent.core.adapter.repository.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +@TableName("digital_employee_sync_record") +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class DigitalEmployeeSyncRecord { + @TableId(value = "id", type = IdType.AUTO) + private Long id; + @TableField(value = "_tenant_id") + private Long tenantId; + private Long userId; + private String clientKey; + private String deviceId; + private String outboxId; + private String entityType; + private String entityId; + private String operation; + private String payload; + private Date syncedAt; + private Date modified; + private Date created; +} 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 new file mode 100644 index 00000000..9fe5e3e8 --- /dev/null +++ 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 @@ -0,0 +1,94 @@ +package com.xspaceagi.agent.core.application.service; + +import com.baomidou.dynamic.datasource.annotation.DSTransactional; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +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.DigitalEmployeeSyncRequestDto; +import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncResponseDto; +import com.xspaceagi.agent.core.adapter.repository.DigitalEmployeeSyncRecordRepository; +import com.xspaceagi.agent.core.adapter.repository.entity.DigitalEmployeeSyncRecord; +import com.xspaceagi.system.spec.common.RequestContext; +import jakarta.annotation.Resource; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Service +public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmployeeSyncApplicationService { + + @Resource + private DigitalEmployeeSyncRecordRepository digitalEmployeeSyncRecordRepository; + + @Resource + private ObjectMapper objectMapper; + + @Override + @DSTransactional + public DigitalEmployeeSyncResponseDto syncOutbox(DigitalEmployeeSyncRequestDto request) { + List acked = new ArrayList<>(); + if (request == null || CollectionUtils.isEmpty(request.getItems())) { + return DigitalEmployeeSyncResponseDto.builder().acked(acked).build(); + } + + Date now = new Date(); + for (DigitalEmployeeSyncItemDto item : request.getItems()) { + if (item == null || item.getOutboxId() == null || item.getEntityType() == null || item.getEntityId() == null) { + continue; + } + + DigitalEmployeeSyncRecord existing = digitalEmployeeSyncRecordRepository.getOne( + new QueryWrapper<>(DigitalEmployeeSyncRecord.builder() + .clientKey(request.getClientKey()) + .outboxId(item.getOutboxId()) + .build()), + false + ); + + DigitalEmployeeSyncRecord record = existing == null ? new DigitalEmployeeSyncRecord() : existing; + record.setTenantId(RequestContext.get().getTenantId()); + record.setUserId(RequestContext.get().getUserId()); + record.setClientKey(request.getClientKey()); + record.setDeviceId(request.getDeviceId()); + record.setOutboxId(item.getOutboxId()); + record.setEntityType(item.getEntityType()); + record.setEntityId(item.getEntityId()); + record.setOperation(item.getOperation()); + record.setPayload(toJson(item.getPayload())); + record.setSyncedAt(now); + record.setModified(now); + if (record.getId() == null) { + record.setCreated(now); + digitalEmployeeSyncRecordRepository.save(record); + } else { + digitalEmployeeSyncRecordRepository.updateById(record); + } + + acked.add(DigitalEmployeeSyncAckDto.builder() + .outboxId(item.getOutboxId()) + .entityType(item.getEntityType()) + .entityId(item.getEntityId()) + .remoteId(String.valueOf(record.getId())) + .build()); + } + + return DigitalEmployeeSyncResponseDto.builder().acked(acked).build(); + } + + private String toJson(Object payload) { + if (payload == null) { + return "{}"; + } + try { + return objectMapper.writeValueAsString(payload); + } catch (JsonProcessingException e) { + 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/dao/mapper/DigitalEmployeeSyncRecordMapper.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-infra/src/main/java/com/xspaceagi/agent/core/infra/dao/mapper/DigitalEmployeeSyncRecordMapper.java new file mode 100644 index 00000000..6fe65a49 --- /dev/null +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-infra/src/main/java/com/xspaceagi/agent/core/infra/dao/mapper/DigitalEmployeeSyncRecordMapper.java @@ -0,0 +1,9 @@ +package com.xspaceagi.agent.core.infra.dao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xspaceagi.agent.core.adapter.repository.entity.DigitalEmployeeSyncRecord; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface DigitalEmployeeSyncRecordMapper extends BaseMapper { +} 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 new file mode 100644 index 00000000..9a46dae1 --- /dev/null +++ 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 @@ -0,0 +1,13 @@ +package com.xspaceagi.agent.core.infra.repository; + +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.springframework.stereotype.Service; + +@Service +public class DigitalEmployeeSyncRecordRepositoryImpl + extends ServiceImpl + implements DigitalEmployeeSyncRecordRepository { +} 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 new file mode 100644 index 00000000..9bdcaace --- /dev/null +++ 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 @@ -0,0 +1,24 @@ +package com.xspaceagi.agent.web.ui.controller; + +import com.xspaceagi.agent.core.adapter.application.DigitalEmployeeSyncApplicationService; +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; +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.RestController; + +@RestController +@RequestMapping("/api/digital-employee/sync") +public class DigitalEmployeeSyncController { + + @Resource + private DigitalEmployeeSyncApplicationService digitalEmployeeSyncApplicationService; + + @RequestMapping(path = "/outbox", method = RequestMethod.POST) + public ReqResult syncOutbox(@RequestBody DigitalEmployeeSyncRequestDto request) { + return ReqResult.success(digitalEmployeeSyncApplicationService.syncOutbox(request)); + } +} diff --git a/qiming-backend/sql/update-20260605.sql b/qiming-backend/sql/update-20260605.sql new file mode 100644 index 00000000..e1e47f4a --- /dev/null +++ b/qiming-backend/sql/update-20260605.sql @@ -0,0 +1,23 @@ +CREATE TABLE `digital_employee_sync_record` +( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', + `_tenant_id` BIGINT DEFAULT NULL COMMENT '租户ID', + `user_id` BIGINT DEFAULT NULL COMMENT '用户ID', + `client_key` VARCHAR(128) NOT NULL COMMENT '客户端配置Key', + `device_id` VARCHAR(128) DEFAULT NULL COMMENT '设备ID', + `outbox_id` VARCHAR(255) NOT NULL COMMENT '客户端outbox ID', + `entity_type` VARCHAR(32) NOT NULL COMMENT '实体类型:plan/task/run/event/artifact/approval', + `entity_id` VARCHAR(255) NOT NULL COMMENT '客户端实体ID', + `operation` VARCHAR(32) NOT NULL COMMENT '操作类型', + `payload` JSON DEFAULT NULL COMMENT '实体快照', + `synced_at` DATETIME NOT NULL COMMENT '最近同步时间', + `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + 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_user_time` (`_tenant_id`, `user_id`, `synced_at`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci + COMMENT = '数字员工客户端同步记录'; diff --git a/qimingclaw/crates/agent-electron-client/src/main/services/digitalEmployee/syncService.ts b/qimingclaw/crates/agent-electron-client/src/main/services/digitalEmployee/syncService.ts index b3480842..220b225b 100644 --- a/qimingclaw/crates/agent-electron-client/src/main/services/digitalEmployee/syncService.ts +++ b/qimingclaw/crates/agent-electron-client/src/main/services/digitalEmployee/syncService.ts @@ -16,6 +16,7 @@ interface DigitalEmployeeSyncConfig { enabled: boolean; endpoint: string | null; clientKey: string | null; + authToken: string | null; batchSize: number; } @@ -96,7 +97,7 @@ export async function flushDigitalEmployeeSyncOutbox(): Promise