feat: filter digital sync links by device

This commit is contained in:
baiyanyun
2026-06-07 11:24:01 +08:00
parent 029ca6e7f6
commit bf25bc5a47
11 changed files with 27 additions and 5 deletions

View File

@@ -9,6 +9,7 @@ public interface DigitalEmployeeSyncApplicationService {
DigitalEmployeeSyncRecordPageDto queryRecords(
String clientKey,
String deviceId,
String entityType,
String entityId,
String outboxId,

View File

@@ -8,6 +8,7 @@ public interface DigitalEmployeeSyncRecordRepository extends IService<DigitalEmp
IPage<DigitalEmployeeSyncRecord> queryRecords(
Long tenantId,
String clientKey,
String deviceId,
String entityType,
String entityId,
String outboxId,

View File

@@ -91,6 +91,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
@Override
public DigitalEmployeeSyncRecordPageDto queryRecords(
String clientKey,
String deviceId,
String entityType,
String entityId,
String outboxId,
@@ -114,6 +115,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye
IPage<DigitalEmployeeSyncRecord> page = digitalEmployeeSyncRecordRepository.queryRecords(
tenantId,
clientKey,
deviceId,
entityType,
entityId,
outboxId,

View File

@@ -19,6 +19,7 @@ public class DigitalEmployeeSyncRecordRepositoryImpl
public IPage<DigitalEmployeeSyncRecord> queryRecords(
Long tenantId,
String clientKey,
String deviceId,
String entityType,
String entityId,
String outboxId,
@@ -28,6 +29,7 @@ public class DigitalEmployeeSyncRecordRepositoryImpl
LambdaQueryWrapper<DigitalEmployeeSyncRecord> queryWrapper = new LambdaQueryWrapper<DigitalEmployeeSyncRecord>()
.eq(tenantId != null, DigitalEmployeeSyncRecord::getTenantId, tenantId)
.eq(StringUtils.isNotBlank(clientKey), DigitalEmployeeSyncRecord::getClientKey, clientKey)
.eq(StringUtils.isNotBlank(deviceId), DigitalEmployeeSyncRecord::getDeviceId, deviceId)
.eq(StringUtils.isNotBlank(entityType), DigitalEmployeeSyncRecord::getEntityType, entityType)
.eq(StringUtils.isNotBlank(entityId), DigitalEmployeeSyncRecord::getEntityId, entityId)
.eq(StringUtils.isNotBlank(outboxId), DigitalEmployeeSyncRecord::getOutboxId, outboxId)

View File

@@ -31,6 +31,7 @@ public class DigitalEmployeeSyncController {
@RequestMapping(path = "/records", method = RequestMethod.GET)
public ReqResult<DigitalEmployeeSyncRecordPageDto> queryRecords(
@RequestParam(name = "client_key", required = false) String clientKey,
@RequestParam(name = "device_id", required = false) String deviceId,
@RequestParam(name = "entity_type", required = false) String entityType,
@RequestParam(name = "entity_id", required = false) String entityId,
@RequestParam(name = "outbox_id", required = false) String outboxId,
@@ -39,6 +40,7 @@ public class DigitalEmployeeSyncController {
) {
return ReqResult.success(digitalEmployeeSyncApplicationService.queryRecords(
clientKey,
deviceId,
entityType,
entityId,
outboxId,

View File

@@ -217,6 +217,7 @@ const DigitalEmployeeSync: React.FC = () => {
const params = new URLSearchParams(location.search || '');
const values = {
client_key: readSearchValue(params, 'client_key'),
device_id: readSearchValue(params, 'device_id'),
entity_type: readSearchValue(params, 'entity_type'),
entity_id: readSearchValue(params, 'entity_id'),
outbox_id: readSearchValue(params, 'outbox_id'),
@@ -228,6 +229,7 @@ const DigitalEmployeeSync: React.FC = () => {
formRef.current?.resetFields();
formRef.current?.setFieldsValue({
client_key: undefined,
device_id: undefined,
entity_type: undefined,
entity_id: undefined,
outbox_id: undefined,
@@ -270,7 +272,10 @@ const DigitalEmployeeSync: React.FC = () => {
dataIndex: 'device_id',
width: 150,
ellipsis: true,
hideInSearch: true,
fieldProps: {
placeholder: '输入设备 ID',
allowClear: true,
},
renderText: (value) => value || '--',
},
{
@@ -360,6 +365,7 @@ const DigitalEmployeeSync: React.FC = () => {
pageSize?: number;
current?: number;
client_key?: string;
device_id?: string;
entity_type?: string;
entity_id?: string;
outbox_id?: string;
@@ -368,6 +374,7 @@ const DigitalEmployeeSync: React.FC = () => {
pageNo: params.current || 1,
pageSize: params.pageSize || 15,
clientKey: params.client_key,
deviceId: params.device_id,
entityType: params.entity_type,
entityId: params.entity_id,
outboxId: params.outbox_id,
@@ -431,6 +438,10 @@ const DigitalEmployeeSync: React.FC = () => {
<Typography.Text strong> Key</Typography.Text>
{maskClientKey(payloadRecord.client_key)}
</Typography.Paragraph>
<Typography.Paragraph>
<Typography.Text strong> ID</Typography.Text>
{payloadRecord.device_id || '--'}
</Typography.Paragraph>
<Typography.Paragraph>
<Typography.Text strong></Typography.Text>
{payloadRecord.entity_type} / {payloadRecord.entity_id}

View File

@@ -274,6 +274,7 @@ export async function apiDigitalEmployeeSyncRecordList(
method: 'GET',
params: {
client_key: data.clientKey,
device_id: data.deviceId,
entity_type: data.entityType,
entity_id: data.entityId,
outbox_id: data.outboxId,

View File

@@ -542,6 +542,7 @@ export interface DigitalEmployeeSyncRecordInfo {
export interface DigitalEmployeeSyncRecordListParams
extends SystemPaginationParams {
clientKey?: string;
deviceId?: string;
entityType?: string;
entityId?: string;
outboxId?: string;

View File

@@ -147,7 +147,7 @@ describe("digital employee sync service", () => {
id: "plan:upsert:plan-2",
dueForRetry: false,
managementRecordUrl:
"https://manage.example.com/system/content/content-digital-employee-sync?entity_type=plan&entity_id=plan-2&outbox_id=plan%3Aupsert%3Aplan-2",
"https://manage.example.com/system/content/content-digital-employee-sync?device_id=device-001&entity_type=plan&entity_id=plan-2&outbox_id=plan%3Aupsert%3Aplan-2",
attemptHistory: [
expect.objectContaining({
attemptNo: 1,

View File

@@ -636,6 +636,7 @@ function buildManagementRecordUrl(
try {
const endpoint = new URL(config.endpoint);
const url = new URL(MANAGEMENT_SYNC_RECORD_PATH, endpoint.origin);
url.searchParams.set("device_id", getDeviceId());
url.searchParams.set("entity_type", row.entity_type);
url.searchParams.set("entity_id", row.entity_id);
url.searchParams.set("outbox_id", row.id);

View File

@@ -448,7 +448,7 @@ crates/agent-electron-client/src/preload/webviewPerfBridge.ts
- 同步状态通过 bridge 返回最近失败 outbox 明细首页可直接看到实体类型、outbox ID、操作类型、重试次数、失败时间和下次自动重试时间。
- 本地 SQLite 额外记录 `digital_sync_attempts`,每次 outbox 推送成功、失败或部分 ack 缺失都会留下一次尝试历史,默认保留最近 30 天。
- 点击失败 outbox 可打开详情弹窗,查看完整错误、实体 ID、退避状态、最近重试历史并可复制或导出诊断 JSON也可触发手动 `flushSync` 立即重试。若已配置管理端同步地址,详情弹窗可直接打开对应的管理端同步记录深链。
- qimingclaw 自动生成的管理端深链只携带 `entity_type``entity_id``outbox_id`,避免在 URL 中暴露客户端 Key管理端列表仍保留客户端 Key 手动筛选能力。
- qimingclaw 自动生成的管理端深链只携带 `device_id``entity_type``entity_id``outbox_id`,避免在 URL 中暴露客户端 Key,同时仍能区分同租户下的不同客户端;管理端列表仍保留客户端 Key 手动筛选能力。
新增 IPC / Bridge
@@ -482,10 +482,10 @@ qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-a
GET /api/digital-employee/sync/records
```
- 支持 `client_key``entity_type``entity_id``outbox_id``page_no``page_size` 查询参数。
- 支持 `client_key``device_id``entity_type``entity_id``outbox_id``page_no``page_size` 查询参数。
- 按当前登录上下文租户隔离,默认每页 20 条,最大每页 100 条。
- 当前返回通用同步记录和原始 payload用于管理端页面或联调工具先查看同步结果后续再拆分为更丰富的 Plan / Task / Run / Event 查询模型。
- 管理端已增加“数字员工同步记录”菜单、权限资源和列表页,可按客户端 Key、实体类型、实体 ID、Outbox ID 查询并查看 payloadURL 查询参数会预填筛选条件,并在命中 Outbox ID 时自动打开 payload 抽屉。
- 管理端已增加“数字员工同步记录”菜单、权限资源和列表页,可按客户端 Key、设备 ID、实体类型、实体 ID、Outbox ID 查询并查看 payloadURL 查询参数会预填筛选条件,并在命中 Outbox ID 时自动打开 payload 抽屉。
- 管理端同步记录页会从通用 payload 中提炼标题、状态、Plan/Task/Run 关联、发生时间等业务摘要,列表和详情抽屉不再只能依赖原始 JSON 排障。
- 管理端列表和抽屉内的客户端 Key 仅脱敏展示,筛选框仍支持输入完整客户端 Key 精准查询。