feat: 完善知识库批量导入与公共 API

- 新增批量异步导入、状态查询、失败重试与中断恢复链路

- 拆分知识库读取、导入、维护权限并完善 Public API 契约

- 补充数据库迁移、管理端交互、接口说明与相关测试
This commit is contained in:
2026-08-03 11:13:48 +08:00
parent 6df3dd9981
commit 51dbfd41b6
99 changed files with 16274 additions and 480 deletions

View File

@@ -0,0 +1,142 @@
package tech.easyflow.admin.controller.system;
import org.testng.annotations.Test;
import tech.easyflow.ai.service.KnowledgeSharePermissionService;
import tech.easyflow.ai.service.WorkflowApiPermissionService;
import tech.easyflow.common.domain.Result;
import tech.easyflow.system.entity.SysApiKey;
import tech.easyflow.system.service.SysApiKeyResourceMappingService;
import tech.easyflow.system.service.SysApiKeyService;
import java.lang.reflect.Field;
import java.math.BigInteger;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotEquals;
/**
* {@link SysApiKeyController} 更新访问令牌测试。
*/
public class SysApiKeyControllerTest {
/**
* 验证只更新权限时不会执行缺少主表更新字段的 SQL。
*/
@Test
public void updateShouldSkipMainTableForPermissionOnlyRequest() {
BigInteger apiKeyId = BigInteger.valueOf(100);
SysApiKeyService apiKeyService = mock(SysApiKeyService.class);
KnowledgeSharePermissionService knowledgePermissionService =
mock(KnowledgeSharePermissionService.class);
SysApiKeyController controller = controller(apiKeyService, knowledgePermissionService);
SysApiKey existing = new SysApiKey();
existing.setId(apiKeyId);
when(apiKeyService.getById(apiKeyId)).thenReturn(existing);
SysApiKey request = new SysApiKey();
request.setId(apiKeyId);
request.setKnowledgeReadEnabled(true);
request.setKnowledgeImportEnabled(false);
request.setKnowledgeMaintenanceEnabled(false);
Result<?> result = controller.update(request);
assertEquals(result.getErrorCode(), 0);
verify(apiKeyService, never()).updateById(request);
verify(knowledgePermissionService).replaceApiPermissions(apiKeyId, true, false, false);
}
/**
* 验证基础字段与权限同时更新时,两类数据都被保存。
*/
@Test
public void updateShouldPersistMainTableAndPermissionsTogether() {
BigInteger apiKeyId = BigInteger.valueOf(101);
SysApiKeyService apiKeyService = mock(SysApiKeyService.class);
KnowledgeSharePermissionService knowledgePermissionService =
mock(KnowledgeSharePermissionService.class);
SysApiKeyController controller = controller(apiKeyService, knowledgePermissionService);
SysApiKey existing = new SysApiKey();
existing.setId(apiKeyId);
when(apiKeyService.getById(apiKeyId)).thenReturn(existing);
SysApiKey request = new SysApiKey();
request.setId(apiKeyId);
request.setStatus(1);
request.setKnowledgeReadEnabled(true);
request.setKnowledgeImportEnabled(true);
request.setKnowledgeMaintenanceEnabled(true);
Result<?> result = controller.update(request);
assertEquals(result.getErrorCode(), 0);
verify(apiKeyService).updateById(request);
verify(knowledgePermissionService).replaceApiPermissions(apiKeyId, true, true, true);
}
/**
* 验证新版知识库权限缺少字段时拒绝更新,避免遗漏字段被隐式关闭。
*/
@Test
public void updateShouldRejectPartialKnowledgePermissions() {
SysApiKeyService apiKeyService = mock(SysApiKeyService.class);
KnowledgeSharePermissionService knowledgePermissionService =
mock(KnowledgeSharePermissionService.class);
SysApiKeyController controller = controller(apiKeyService, knowledgePermissionService);
SysApiKey request = new SysApiKey();
request.setId(BigInteger.valueOf(102));
request.setKnowledgeReadEnabled(true);
Result<?> result = controller.update(request);
assertNotEquals(result.getErrorCode(), 0);
verifyNoInteractions(apiKeyService, knowledgePermissionService);
}
/**
* 创建注入模拟依赖的控制器。
*
* @param apiKeyService 访问令牌服务
* @param knowledgePermissionService 知识库权限服务
* @return 测试控制器
*/
private SysApiKeyController controller(
SysApiKeyService apiKeyService,
KnowledgeSharePermissionService knowledgePermissionService
) {
SysApiKeyController controller = new SysApiKeyController(apiKeyService);
setField(controller, "sysApiKeyResourceMappingService", mock(SysApiKeyResourceMappingService.class));
setField(controller, "knowledgeSharePermissionService", knowledgePermissionService);
setField(controller, "workflowApiPermissionService", mock(WorkflowApiPermissionService.class));
return controller;
}
/**
* 通过反射注入控制器字段。
*
* @param target 目标对象
* @param fieldName 字段名
* @param value 字段值
*/
private void setField(Object target, String fieldName, Object value) {
Class<?> current = target.getClass();
while (current != null) {
try {
Field field = current.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(target, value);
return;
} catch (NoSuchFieldException ignored) {
current = current.getSuperclass();
} catch (IllegalAccessException e) {
throw new IllegalStateException("设置测试字段失败: " + fieldName, e);
}
}
throw new IllegalArgumentException("未找到字段: " + fieldName);
}
}