feat: add digital employee management sync endpoint

This commit is contained in:
baiyanyun
2026-06-05 17:21:48 +08:00
parent b9132c421d
commit ab1d022905
14 changed files with 308 additions and 5 deletions

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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<String, Object> payload;
}

View File

@@ -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<DigitalEmployeeSyncItemDto> items;
}

View File

@@ -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<DigitalEmployeeSyncAckDto> acked;
}

View File

@@ -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<DigitalEmployeeSyncRecord> {
}

View File

@@ -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;
}

View File

@@ -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<DigitalEmployeeSyncAckDto> 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 "{}";
}
}
}

View File

@@ -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<DigitalEmployeeSyncRecord> {
}

View File

@@ -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<DigitalEmployeeSyncRecordMapper, DigitalEmployeeSyncRecord>
implements DigitalEmployeeSyncRecordRepository {
}

View File

@@ -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<DigitalEmployeeSyncResponseDto> syncOutbox(@RequestBody DigitalEmployeeSyncRequestDto request) {
return ReqResult.success(digitalEmployeeSyncApplicationService.syncOutbox(request));
}
}