feat(backend): expose digital employee sync records
This commit is contained in:
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<DigitalEmployeeSyncRecordDto> records;
|
||||
}
|
||||
@@ -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<DigitalEmployeeSyncRecord> {
|
||||
IPage<DigitalEmployeeSyncRecord> queryRecords(
|
||||
Long tenantId,
|
||||
String clientKey,
|
||||
String entityType,
|
||||
String entityId,
|
||||
long pageNo,
|
||||
long pageSize
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<DigitalEmployeeSyncRecord> 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 "{}";
|
||||
|
||||
@@ -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<DigitalEmployeeSyncRecordMapper, DigitalEmployeeSyncRecord>
|
||||
implements DigitalEmployeeSyncRecordRepository {
|
||||
|
||||
@Override
|
||||
public IPage<DigitalEmployeeSyncRecord> queryRecords(
|
||||
Long tenantId,
|
||||
String clientKey,
|
||||
String entityType,
|
||||
String entityId,
|
||||
long pageNo,
|
||||
long pageSize
|
||||
) {
|
||||
LambdaQueryWrapper<DigitalEmployeeSyncRecord> queryWrapper = new LambdaQueryWrapper<DigitalEmployeeSyncRecord>()
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<DigitalEmployeeSyncResponseDto> syncOutbox(@RequestBody DigitalEmployeeSyncRequestDto request) {
|
||||
return ReqResult.success(digitalEmployeeSyncApplicationService.syncOutbox(request));
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/records", method = RequestMethod.GET)
|
||||
public ReqResult<DigitalEmployeeSyncRecordPageDto> 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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 编译与接口启动。
|
||||
- 数字员工页面继续细化同步状态交互,例如手动同步按钮和失败原因展开。
|
||||
- 数字员工页面继续细化同步状态交互,例如失败原因展开。
|
||||
|
||||
## 管理端联动原则
|
||||
|
||||
|
||||
Reference in New Issue
Block a user