fix: 完成系统向智能体数据链路切换
- 切换工作台、聊天历史、资源候选与公共调用到 Agent - 加固资源绑定、删除保护及发布运行并发控制 - 隔离旧 Bot 专属服务和组件并保留兼容入口
This commit is contained in:
@@ -1,14 +1,11 @@
|
||||
package tech.easyflow.ai.publish;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
import tech.easyflow.ai.entity.BotDocumentCollection;
|
||||
import tech.easyflow.ai.entity.DocumentCollection;
|
||||
import tech.easyflow.ai.entity.DocumentCollectionCategory;
|
||||
import tech.easyflow.ai.entity.Model;
|
||||
import tech.easyflow.ai.enums.PublishStatus;
|
||||
import tech.easyflow.ai.service.BotDocumentCollectionService;
|
||||
import tech.easyflow.ai.service.DocumentCollectionCategoryService;
|
||||
import tech.easyflow.ai.service.DocumentCollectionService;
|
||||
import tech.easyflow.ai.service.ModelService;
|
||||
@@ -37,7 +34,6 @@ public class KnowledgeApprovalSubjectHandler extends AbstractAiResourceLifecycle
|
||||
|
||||
private final DocumentCollectionService documentCollectionService;
|
||||
private final ResourceAccessService resourceAccessService;
|
||||
private final BotDocumentCollectionService botDocumentCollectionService;
|
||||
private final ModelService modelService;
|
||||
private final DocumentCollectionCategoryService documentCollectionCategoryService;
|
||||
private final SysDeptService sysDeptService;
|
||||
@@ -46,7 +42,6 @@ public class KnowledgeApprovalSubjectHandler extends AbstractAiResourceLifecycle
|
||||
public KnowledgeApprovalSubjectHandler(DocumentCollectionService documentCollectionService,
|
||||
ResourceAccessService resourceAccessService,
|
||||
ApprovalInstanceService approvalInstanceService,
|
||||
BotDocumentCollectionService botDocumentCollectionService,
|
||||
ModelService modelService,
|
||||
DocumentCollectionCategoryService documentCollectionCategoryService,
|
||||
SysDeptService sysDeptService,
|
||||
@@ -55,7 +50,6 @@ public class KnowledgeApprovalSubjectHandler extends AbstractAiResourceLifecycle
|
||||
super(approvalInstanceService, objectMapper);
|
||||
this.documentCollectionService = documentCollectionService;
|
||||
this.resourceAccessService = resourceAccessService;
|
||||
this.botDocumentCollectionService = botDocumentCollectionService;
|
||||
this.modelService = modelService;
|
||||
this.documentCollectionCategoryService = documentCollectionCategoryService;
|
||||
this.sysDeptService = sysDeptService;
|
||||
@@ -200,26 +194,22 @@ public class KnowledgeApprovalSubjectHandler extends AbstractAiResourceLifecycle
|
||||
if (!impact.isCanProceed()) {
|
||||
throw new BusinessException(buildWorkflowUsageBlockMessage(impact));
|
||||
}
|
||||
if (impact.isHasBotBindings()) {
|
||||
snapshot.put("botBindings", impact.getBotBindings());
|
||||
if (impact.isHasAgentBindings()) {
|
||||
snapshot.put("agentBindings", impact.getAgentBindings());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void validateDelete(DocumentCollection resource, PublishStatus currentStatus) {
|
||||
if (hasBotBinding(resource.getId())) {
|
||||
throw new BusinessException("此知识库还关联着bot,请先取消关联!");
|
||||
OfflineImpactCheckVo impact = resourceOfflineImpactService.checkKnowledgeImpact(resource.getId());
|
||||
if (impact.isHasAgentBindings()) {
|
||||
throw new BusinessException("此知识库仍被智能体使用,请先取消绑定后再删除");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterOffline(BigInteger resourceId) {
|
||||
resourceOfflineImpactService.unbindKnowledgeFromBots(resourceId);
|
||||
}
|
||||
|
||||
private boolean hasBotBinding(BigInteger knowledgeId) {
|
||||
QueryWrapper queryWrapper = QueryWrapper.create().eq(BotDocumentCollection::getDocumentCollectionId, knowledgeId);
|
||||
return botDocumentCollectionService.exists(queryWrapper);
|
||||
resourceOfflineImpactService.unbindKnowledgeFromAgents(resourceId);
|
||||
}
|
||||
|
||||
private String buildWorkflowUsageBlockMessage(OfflineImpactCheckVo impact) {
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package tech.easyflow.ai.publish;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
import tech.easyflow.ai.entity.BotWorkflow;
|
||||
import tech.easyflow.ai.entity.Workflow;
|
||||
import tech.easyflow.ai.enums.PublishStatus;
|
||||
import tech.easyflow.ai.plugin.workflow.binding.WorkflowPluginBindingService;
|
||||
import tech.easyflow.ai.plugin.workflow.snapshot.WorkflowPluginSnapshotResolver;
|
||||
import tech.easyflow.ai.service.BotWorkflowService;
|
||||
import tech.easyflow.ai.service.ResourceOfflineImpactService;
|
||||
import tech.easyflow.ai.service.WorkflowService;
|
||||
import tech.easyflow.ai.vo.OfflineImpactCheckVo;
|
||||
@@ -31,7 +28,6 @@ public class WorkflowApprovalSubjectHandler extends AbstractAiResourceLifecycleH
|
||||
|
||||
private final WorkflowService workflowService;
|
||||
private final ResourceAccessService resourceAccessService;
|
||||
private final BotWorkflowService botWorkflowService;
|
||||
private final ResourceOfflineImpactService resourceOfflineImpactService;
|
||||
private final WorkflowPluginBindingService workflowPluginBindingService;
|
||||
private final WorkflowPluginSnapshotResolver workflowPluginSnapshotResolver;
|
||||
@@ -39,7 +35,6 @@ public class WorkflowApprovalSubjectHandler extends AbstractAiResourceLifecycleH
|
||||
public WorkflowApprovalSubjectHandler(WorkflowService workflowService,
|
||||
ResourceAccessService resourceAccessService,
|
||||
ApprovalInstanceService approvalInstanceService,
|
||||
BotWorkflowService botWorkflowService,
|
||||
ResourceOfflineImpactService resourceOfflineImpactService,
|
||||
WorkflowPluginBindingService workflowPluginBindingService,
|
||||
WorkflowPluginSnapshotResolver workflowPluginSnapshotResolver,
|
||||
@@ -47,7 +42,6 @@ public class WorkflowApprovalSubjectHandler extends AbstractAiResourceLifecycleH
|
||||
super(approvalInstanceService, objectMapper);
|
||||
this.workflowService = workflowService;
|
||||
this.resourceAccessService = resourceAccessService;
|
||||
this.botWorkflowService = botWorkflowService;
|
||||
this.resourceOfflineImpactService = resourceOfflineImpactService;
|
||||
this.workflowPluginBindingService = workflowPluginBindingService;
|
||||
this.workflowPluginSnapshotResolver = workflowPluginSnapshotResolver;
|
||||
@@ -178,8 +172,8 @@ public class WorkflowApprovalSubjectHandler extends AbstractAiResourceLifecycleH
|
||||
@Override
|
||||
protected void enrichOfflineSnapshot(Workflow resource, Map<String, Object> snapshot) {
|
||||
OfflineImpactCheckVo impact = resourceOfflineImpactService.checkWorkflowImpact(resource.getId());
|
||||
if (impact.isHasBotBindings()) {
|
||||
snapshot.put("botBindings", impact.getBotBindings());
|
||||
if (impact.isHasAgentBindings()) {
|
||||
snapshot.put("agentBindings", impact.getAgentBindings());
|
||||
}
|
||||
if (impact.isHasPluginBindings()) {
|
||||
snapshot.put("pluginBindings", impact.getPluginBindings());
|
||||
@@ -188,18 +182,14 @@ public class WorkflowApprovalSubjectHandler extends AbstractAiResourceLifecycleH
|
||||
|
||||
@Override
|
||||
protected void validateDelete(Workflow resource, PublishStatus currentStatus) {
|
||||
if (hasBotBinding(resource.getId())) {
|
||||
throw new BusinessException("此工作流还关联有bot,请先取消关联后再删除!");
|
||||
OfflineImpactCheckVo impact = resourceOfflineImpactService.checkWorkflowImpact(resource.getId());
|
||||
if (impact.isHasAgentBindings()) {
|
||||
throw new BusinessException("此工作流仍被智能体使用,请先取消绑定后再删除");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterOffline(BigInteger resourceId) {
|
||||
resourceOfflineImpactService.unbindWorkflowFromBots(resourceId);
|
||||
}
|
||||
|
||||
private boolean hasBotBinding(BigInteger workflowId) {
|
||||
QueryWrapper queryWrapper = QueryWrapper.create().eq(BotWorkflow::getWorkflowId, workflowId);
|
||||
return botWorkflowService.exists(queryWrapper);
|
||||
resourceOfflineImpactService.unbindWorkflowFromAgents(resourceId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package tech.easyflow.ai.service;
|
||||
|
||||
import tech.easyflow.ai.vo.OfflineImpactBindingVo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Agent 对共享 AI 资源的绑定查询与解绑契约。
|
||||
*
|
||||
* <p>契约定义在 AI 模块中,由 Agent 模块实现,避免共享资源生命周期反向依赖 Agent 实体。</p>
|
||||
*/
|
||||
public interface AgentResourceBindingProvider {
|
||||
|
||||
/**
|
||||
* 查询绑定指定工作流的 Agent。
|
||||
*
|
||||
* @param workflowId 工作流 ID
|
||||
* @return Agent 摘要列表
|
||||
*/
|
||||
List<OfflineImpactBindingVo> listAgentsByWorkflowId(BigInteger workflowId);
|
||||
|
||||
/**
|
||||
* 查询绑定指定知识库的 Agent。
|
||||
*
|
||||
* @param knowledgeId 知识库 ID
|
||||
* @return Agent 摘要列表
|
||||
*/
|
||||
List<OfflineImpactBindingVo> listAgentsByKnowledgeId(BigInteger knowledgeId);
|
||||
|
||||
/**
|
||||
* 查询绑定指定插件工具的 Agent。
|
||||
*
|
||||
* @param pluginItemId 插件工具 ID
|
||||
* @return Agent 摘要列表
|
||||
*/
|
||||
List<OfflineImpactBindingVo> listAgentsByPluginItemId(BigInteger pluginItemId);
|
||||
|
||||
/**
|
||||
* 查询绑定指定 MCP 的 Agent。
|
||||
*
|
||||
* @param mcpId MCP ID
|
||||
* @return Agent 摘要列表
|
||||
*/
|
||||
List<OfflineImpactBindingVo> listAgentsByMcpId(BigInteger mcpId);
|
||||
|
||||
/**
|
||||
* 查询使用指定模型的 Agent。
|
||||
*
|
||||
* @param modelId 模型 ID
|
||||
* @return Agent 摘要列表
|
||||
*/
|
||||
List<OfflineImpactBindingVo> listAgentsByModelId(BigInteger modelId);
|
||||
|
||||
/**
|
||||
* 解绑指定工作流及 Agent 发布快照中的对应绑定。
|
||||
*
|
||||
* @param workflowId 工作流 ID
|
||||
*/
|
||||
void unbindWorkflow(BigInteger workflowId);
|
||||
|
||||
/**
|
||||
* 解绑指定知识库及 Agent 发布快照中的对应绑定。
|
||||
*
|
||||
* @param knowledgeId 知识库 ID
|
||||
*/
|
||||
void unbindKnowledge(BigInteger knowledgeId);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package tech.easyflow.ai.service;
|
||||
|
||||
import tech.easyflow.ai.vo.OfflineImpactBindingVo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Agent 对共享 AI 资源的统一引用查询服务。
|
||||
*/
|
||||
public interface AgentResourceReferenceService {
|
||||
|
||||
/**
|
||||
* 查询引用指定工作流的 Agent。
|
||||
*
|
||||
* @param workflowId 工作流 ID
|
||||
* @return Agent 摘要列表
|
||||
*/
|
||||
List<OfflineImpactBindingVo> listAgentsByWorkflowId(BigInteger workflowId);
|
||||
|
||||
/**
|
||||
* 查询引用指定知识库的 Agent。
|
||||
*
|
||||
* @param knowledgeId 知识库 ID
|
||||
* @return Agent 摘要列表
|
||||
*/
|
||||
List<OfflineImpactBindingVo> listAgentsByKnowledgeId(BigInteger knowledgeId);
|
||||
|
||||
/**
|
||||
* 校验插件工具没有被 Agent 草稿或发布快照引用。
|
||||
*
|
||||
* @param pluginItemIds 插件工具 ID 集合
|
||||
*/
|
||||
void assertPluginItemsUnused(Collection<BigInteger> pluginItemIds);
|
||||
|
||||
/**
|
||||
* 校验 MCP 没有被 Agent 草稿或发布快照引用。
|
||||
*
|
||||
* @param mcpId MCP ID
|
||||
*/
|
||||
void assertMcpUnused(BigInteger mcpId);
|
||||
|
||||
/**
|
||||
* 校验模型没有被 Agent 草稿或发布快照引用。
|
||||
*
|
||||
* @param modelIds 模型 ID 集合
|
||||
*/
|
||||
void assertModelsUnused(Collection<BigInteger> modelIds);
|
||||
|
||||
/**
|
||||
* 从 Agent 草稿绑定和发布快照中解绑工作流。
|
||||
*
|
||||
* @param workflowId 工作流 ID
|
||||
*/
|
||||
void unbindWorkflow(BigInteger workflowId);
|
||||
|
||||
/**
|
||||
* 从 Agent 草稿绑定和发布快照中解绑知识库。
|
||||
*
|
||||
* @param knowledgeId 知识库 ID
|
||||
*/
|
||||
void unbindKnowledge(BigInteger knowledgeId);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package tech.easyflow.ai.service;
|
||||
|
||||
import tech.easyflow.ai.entity.Bot;
|
||||
import tech.easyflow.ai.entity.DocumentCollection;
|
||||
import tech.easyflow.ai.entity.Workflow;
|
||||
|
||||
@@ -42,17 +41,4 @@ public interface AiResourceApprovalStateService {
|
||||
*/
|
||||
void fillKnowledgeApprovalState(Collection<DocumentCollection> collections);
|
||||
|
||||
/**
|
||||
* 填充聊天助手审批展示状态。
|
||||
*
|
||||
* @param bot 聊天助手
|
||||
*/
|
||||
void fillBotApprovalState(Bot bot);
|
||||
|
||||
/**
|
||||
* 批量填充聊天助手审批展示状态。
|
||||
*
|
||||
* @param bots 聊天助手集合
|
||||
*/
|
||||
void fillBotApprovalState(Collection<Bot> bots);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package tech.easyflow.ai.service;
|
||||
|
||||
import tech.easyflow.ai.entity.Bot;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 旧 Bot 审批展示状态派生服务。
|
||||
*/
|
||||
public interface BotApprovalStateService {
|
||||
|
||||
/**
|
||||
* 填充 Bot 审批展示状态。
|
||||
*
|
||||
* @param bot Bot
|
||||
*/
|
||||
void fillApprovalState(Bot bot);
|
||||
|
||||
/**
|
||||
* 批量填充 Bot 审批展示状态。
|
||||
*
|
||||
* @param bots Bot 集合
|
||||
*/
|
||||
void fillApprovalState(Collection<Bot> bots);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package tech.easyflow.ai.service;
|
||||
import com.mybatisflex.core.service.IService;
|
||||
import tech.easyflow.ai.entity.BotPlugin;
|
||||
import tech.easyflow.ai.entity.Plugin;
|
||||
import tech.easyflow.ai.entity.PluginItem;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
@@ -22,4 +23,21 @@ public interface BotPluginService extends IService<BotPlugin> {
|
||||
List<BigInteger> getBotPluginToolIds(String botId);
|
||||
|
||||
void saveBotAndPluginTool(BigInteger botId, BigInteger[] pluginToolIds);
|
||||
|
||||
/**
|
||||
* 查询插件工具,并标记指定 Bot 已绑定的工具。
|
||||
*
|
||||
* @param pluginId 插件 ID
|
||||
* @param botId Bot ID
|
||||
* @return 插件工具列表
|
||||
*/
|
||||
List<PluginItem> searchPluginTools(BigInteger pluginId, BigInteger botId);
|
||||
|
||||
/**
|
||||
* 查询指定 Bot 已绑定的插件工具。
|
||||
*
|
||||
* @param botId Bot ID
|
||||
* @return 已绑定插件工具列表
|
||||
*/
|
||||
List<PluginItem> getPluginTools(BigInteger botId);
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ import com.easyagents.core.model.chat.tool.Tool;
|
||||
import com.easyagents.mcp.client.McpEnvironmentCheckResult;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.service.IService;
|
||||
import tech.easyflow.ai.entity.BotMcp;
|
||||
import tech.easyflow.ai.entity.Mcp;
|
||||
import tech.easyflow.common.domain.Result;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 服务层。
|
||||
@@ -24,7 +24,14 @@ public interface McpService extends IService<Mcp> {
|
||||
|
||||
void removeMcp(Serializable id);
|
||||
|
||||
Tool toFunction(BotMcp botMcp);
|
||||
/**
|
||||
* 将指定 MCP 工具转换为运行时工具。
|
||||
*
|
||||
* @param mcpId MCP ID
|
||||
* @param mcpToolName MCP 工具名称
|
||||
* @return 运行时工具,不存在时返回 {@code null}
|
||||
*/
|
||||
Tool toFunction(BigInteger mcpId, String mcpToolName);
|
||||
|
||||
Result<Page<Mcp>> pageMcp(Result<Page<Mcp>> page);
|
||||
|
||||
|
||||
@@ -21,10 +21,6 @@ public interface PluginItemService extends IService<PluginItem> {
|
||||
|
||||
boolean updatePlugin(PluginItem pluginItem);
|
||||
|
||||
List<PluginItem> searchPluginToolByPluginId(BigInteger pluginId, BigInteger botId);
|
||||
|
||||
List<PluginItem> getPluginToolList(BigInteger botId);
|
||||
|
||||
Result pluginToolTest(String inputData, BigInteger pluginToolId);
|
||||
|
||||
List<PluginItem> getByPluginId(String id);
|
||||
|
||||
@@ -26,16 +26,16 @@ public interface ResourceOfflineImpactService {
|
||||
OfflineImpactCheckVo checkKnowledgeImpact(BigInteger knowledgeId);
|
||||
|
||||
/**
|
||||
* 工作流下线后,静默解绑所有关联 Bot。
|
||||
* 工作流下线后,静默解绑所有关联 Agent。
|
||||
*
|
||||
* @param workflowId 工作流 ID
|
||||
*/
|
||||
void unbindWorkflowFromBots(BigInteger workflowId);
|
||||
void unbindWorkflowFromAgents(BigInteger workflowId);
|
||||
|
||||
/**
|
||||
* 知识库下线后,静默解绑所有关联 Bot。
|
||||
* 知识库下线后,静默解绑所有关联 Agent。
|
||||
*
|
||||
* @param knowledgeId 知识库 ID
|
||||
*/
|
||||
void unbindKnowledgeFromBots(BigInteger knowledgeId);
|
||||
void unbindKnowledgeFromAgents(BigInteger knowledgeId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
package tech.easyflow.ai.service.impl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.ai.service.AgentResourceBindingProvider;
|
||||
import tech.easyflow.ai.service.AgentResourceReferenceService;
|
||||
import tech.easyflow.ai.vo.OfflineImpactBindingVo;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Agent 资源引用查询服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class AgentResourceReferenceServiceImpl implements AgentResourceReferenceService {
|
||||
|
||||
private final List<AgentResourceBindingProvider> providers;
|
||||
|
||||
/**
|
||||
* 创建 Agent 资源引用查询服务。
|
||||
*
|
||||
* @param providers Agent 资源绑定提供者
|
||||
*/
|
||||
public AgentResourceReferenceServiceImpl(List<AgentResourceBindingProvider> providers) {
|
||||
this.providers = providers == null ? List.of() : List.copyOf(providers);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<OfflineImpactBindingVo> listAgentsByWorkflowId(BigInteger workflowId) {
|
||||
return merge(provider -> provider.listAgentsByWorkflowId(workflowId));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<OfflineImpactBindingVo> listAgentsByKnowledgeId(BigInteger knowledgeId) {
|
||||
return merge(provider -> provider.listAgentsByKnowledgeId(knowledgeId));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void assertPluginItemsUnused(Collection<BigInteger> pluginItemIds) {
|
||||
if (pluginItemIds == null || pluginItemIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (BigInteger pluginItemId : pluginItemIds) {
|
||||
assertUnused(
|
||||
merge(provider -> provider.listAgentsByPluginItemId(pluginItemId)),
|
||||
"插件工具"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void assertMcpUnused(BigInteger mcpId) {
|
||||
assertUnused(merge(provider -> provider.listAgentsByMcpId(mcpId)), "MCP");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void assertModelsUnused(Collection<BigInteger> modelIds) {
|
||||
if (modelIds == null || modelIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (BigInteger modelId : modelIds) {
|
||||
assertUnused(merge(provider -> provider.listAgentsByModelId(modelId)), "模型");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void unbindWorkflow(BigInteger workflowId) {
|
||||
for (AgentResourceBindingProvider provider : requireProviders()) {
|
||||
provider.unbindWorkflow(workflowId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void unbindKnowledge(BigInteger knowledgeId) {
|
||||
for (AgentResourceBindingProvider provider : requireProviders()) {
|
||||
provider.unbindKnowledge(knowledgeId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 汇总所有提供者返回的 Agent 摘要。
|
||||
*
|
||||
* @param loader 单个提供者查询函数
|
||||
* @return 按 Agent ID 去重后的摘要
|
||||
*/
|
||||
private List<OfflineImpactBindingVo> merge(
|
||||
Function<AgentResourceBindingProvider, List<OfflineImpactBindingVo>> loader) {
|
||||
Map<BigInteger, OfflineImpactBindingVo> merged = new LinkedHashMap<>();
|
||||
for (AgentResourceBindingProvider provider : requireProviders()) {
|
||||
List<OfflineImpactBindingVo> bindings = loader.apply(provider);
|
||||
if (bindings == null) {
|
||||
continue;
|
||||
}
|
||||
for (OfflineImpactBindingVo binding : bindings) {
|
||||
if (binding != null && binding.getId() != null) {
|
||||
merged.putIfAbsent(binding.getId(), binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(merged.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验资源未被任何 Agent 引用。
|
||||
*
|
||||
* @param bindings Agent 引用摘要
|
||||
* @param resourceLabel 资源名称
|
||||
*/
|
||||
private void assertUnused(List<OfflineImpactBindingVo> bindings, String resourceLabel) {
|
||||
if (bindings == null || bindings.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String agentTitle = bindings.get(0).getTitle();
|
||||
throw new BusinessException(
|
||||
resourceLabel + "仍被智能体“" + (agentTitle == null ? "未命名智能体" : agentTitle)
|
||||
+ "”使用,请先取消绑定或重新发布智能体后再删除"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已注册提供者;缺失时阻止破坏性资源操作。
|
||||
*
|
||||
* @return Agent 资源绑定提供者
|
||||
*/
|
||||
private List<AgentResourceBindingProvider> requireProviders() {
|
||||
if (providers.isEmpty()) {
|
||||
throw new BusinessException("Agent 资源引用检查服务不可用,请稍后重试");
|
||||
}
|
||||
return providers;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package tech.easyflow.ai.service.impl;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import tech.easyflow.ai.entity.Bot;
|
||||
import tech.easyflow.ai.entity.DocumentCollection;
|
||||
import tech.easyflow.ai.entity.Workflow;
|
||||
import tech.easyflow.ai.enums.PublishStatus;
|
||||
@@ -88,31 +87,6 @@ public class AiResourceApprovalStateServiceImpl implements AiResourceApprovalSta
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void fillBotApprovalState(Bot bot) {
|
||||
fillBotApprovalState(bot == null ? List.of() : List.of(bot));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void fillBotApprovalState(Collection<Bot> bots) {
|
||||
fillApprovalState(
|
||||
bots,
|
||||
ApprovalResourceType.BOT.getCode(),
|
||||
Bot::getCurrentApprovalInstanceId,
|
||||
bot -> PublishStatus.from(bot.getPublishStatus()),
|
||||
Bot::getPublishedSnapshotJson,
|
||||
Bot::setApprovalPending,
|
||||
Bot::setCurrentApprovalActionType,
|
||||
Bot::setDisplayPublishStatus
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一派生审批展示状态。
|
||||
*
|
||||
@@ -126,7 +100,7 @@ public class AiResourceApprovalStateServiceImpl implements AiResourceApprovalSta
|
||||
* @param displaySetter 展示状态写入器
|
||||
* @param <T> 资源类型
|
||||
*/
|
||||
private <T> void fillApprovalState(Collection<T> resources,
|
||||
<T> void fillApprovalState(Collection<T> resources,
|
||||
String resourceType,
|
||||
Function<T, BigInteger> instanceIdGetter,
|
||||
Function<T, PublishStatus> statusGetter,
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package tech.easyflow.ai.service.impl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.ai.entity.Bot;
|
||||
import tech.easyflow.ai.enums.PublishStatus;
|
||||
import tech.easyflow.ai.service.BotApprovalStateService;
|
||||
import tech.easyflow.approval.enums.ApprovalResourceType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 旧 Bot 审批展示状态派生服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class BotApprovalStateServiceImpl implements BotApprovalStateService {
|
||||
|
||||
private final AiResourceApprovalStateServiceImpl approvalStateService;
|
||||
|
||||
/**
|
||||
* 创建 Bot 审批展示状态派生服务。
|
||||
*
|
||||
* @param approvalStateService 通用 AI 资源审批状态服务
|
||||
*/
|
||||
public BotApprovalStateServiceImpl(AiResourceApprovalStateServiceImpl approvalStateService) {
|
||||
this.approvalStateService = approvalStateService;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void fillApprovalState(Bot bot) {
|
||||
fillApprovalState(bot == null ? List.of() : List.of(bot));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void fillApprovalState(Collection<Bot> bots) {
|
||||
approvalStateService.fillApprovalState(
|
||||
bots,
|
||||
ApprovalResourceType.BOT.getCode(),
|
||||
Bot::getCurrentApprovalInstanceId,
|
||||
bot -> PublishStatus.from(bot.getPublishStatus()),
|
||||
Bot::getPublishedSnapshotJson,
|
||||
Bot::setApprovalPending,
|
||||
Bot::setCurrentApprovalActionType,
|
||||
Bot::setDisplayPublishStatus
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,9 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.ai.entity.BotPlugin;
|
||||
import tech.easyflow.ai.entity.Plugin;
|
||||
import tech.easyflow.ai.entity.PluginItem;
|
||||
import tech.easyflow.ai.mapper.BotPluginMapper;
|
||||
import tech.easyflow.ai.mapper.PluginItemMapper;
|
||||
import tech.easyflow.ai.mapper.PluginMapper;
|
||||
import tech.easyflow.ai.service.BotPluginService;
|
||||
import tech.easyflow.common.cache.RedisLockExecutor;
|
||||
@@ -40,6 +42,9 @@ public class BotPluginServiceImpl extends ServiceImpl<BotPluginMapper, BotPlugin
|
||||
@Resource
|
||||
private PluginMapper pluginMapper;
|
||||
|
||||
@Resource
|
||||
private PluginItemMapper pluginItemMapper;
|
||||
|
||||
@Resource
|
||||
private RedisLockExecutor redisLockExecutor;
|
||||
|
||||
@@ -97,4 +102,50 @@ public class BotPluginServiceImpl extends ServiceImpl<BotPluginMapper, BotPlugin
|
||||
this.saveBatch(list);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<PluginItem> searchPluginTools(BigInteger pluginId, BigInteger botId) {
|
||||
List<PluginItem> pluginItems = pluginItemMapper.selectListByQuery(
|
||||
QueryWrapper.create().eq(PluginItem::getPluginId, pluginId)
|
||||
);
|
||||
if (pluginItems == null || pluginItems.isEmpty() || botId == null) {
|
||||
return pluginItems == null ? List.of() : pluginItems;
|
||||
}
|
||||
List<BigInteger> boundToolIdList = botPluginMapper.selectListByQueryAs(
|
||||
QueryWrapper.create()
|
||||
.select(BOT_PLUGIN.PLUGIN_ITEM_ID)
|
||||
.where(BOT_PLUGIN.BOT_ID.eq(botId)),
|
||||
BigInteger.class
|
||||
);
|
||||
Set<BigInteger> boundToolIds = boundToolIdList == null
|
||||
? Set.of()
|
||||
: new LinkedHashSet<>(boundToolIdList);
|
||||
for (PluginItem pluginItem : pluginItems) {
|
||||
pluginItem.setJoinBot(boundToolIds.contains(pluginItem.getId()));
|
||||
}
|
||||
return pluginItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<PluginItem> getPluginTools(BigInteger botId) {
|
||||
if (botId == null) {
|
||||
return List.of();
|
||||
}
|
||||
List<BigInteger> pluginToolIds = botPluginMapper.selectListByQueryAs(
|
||||
QueryWrapper.create()
|
||||
.select(BOT_PLUGIN.PLUGIN_ITEM_ID)
|
||||
.where(BOT_PLUGIN.BOT_ID.eq(botId)),
|
||||
BigInteger.class
|
||||
);
|
||||
if (pluginToolIds == null || pluginToolIds.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
return pluginItemMapper.selectListByIds(pluginToolIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,7 +568,7 @@ public class BotServiceImpl extends ServiceImpl<BotMapper, Bot> implements BotSe
|
||||
queryWrapper.eq(BotMcp::getBotId, botId);
|
||||
List<BotMcp> botMcpList = botMcpService.getMapper().selectListWithRelationsByQuery(queryWrapper);
|
||||
botMcpList.forEach(botMcp -> {
|
||||
Tool tool = mcpService.toFunction(botMcp);
|
||||
Tool tool = mcpService.toFunction(botMcp.getMcpId(), botMcp.getMcpToolName());
|
||||
functionList.add(tool);
|
||||
});
|
||||
|
||||
|
||||
@@ -540,7 +540,7 @@ public class DocumentCollectionServiceImpl extends ServiceImpl<DocumentCollectio
|
||||
public boolean updateById(DocumentCollection entity) {
|
||||
DocumentCollection documentCollection = getById(entity.getId());
|
||||
if (documentCollection == null) {
|
||||
throw new BusinessException("bot 不存在");
|
||||
throw new BusinessException("知识库不存在");
|
||||
}
|
||||
|
||||
CustomBeanUtils.copyPropertiesIgnoreNull(entity, documentCollection);
|
||||
|
||||
@@ -15,7 +15,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.ai.easyagents.tool.McpTool;
|
||||
import tech.easyflow.ai.entity.BotMcp;
|
||||
import tech.easyflow.ai.entity.Mcp;
|
||||
import tech.easyflow.ai.mapper.McpMapper;
|
||||
import tech.easyflow.ai.mcp.McpTransportType;
|
||||
@@ -29,6 +28,7 @@ import tech.easyflow.common.util.StringUtil;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -163,15 +163,15 @@ public class McpServiceImpl extends ServiceImpl<McpMapper, Mcp> implements McpS
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tool toFunction(BotMcp botMcp) {
|
||||
Mcp mcpInfo = this.getById(botMcp.getMcpId());
|
||||
public Tool toFunction(BigInteger mcpId, String mcpToolName) {
|
||||
Mcp mcpInfo = this.getById(mcpId);
|
||||
String configJson = mcpInfo.getConfigJson();
|
||||
String mcpServerName = getFirstMcpServerName(configJson);
|
||||
if (StringUtil.hasText(mcpServerName)) {
|
||||
McpSyncClient mcpClient = mcpClientManager.getMcpClient(mcpServerName);
|
||||
List<McpSchema.Tool> tools = mcpClient.listTools().tools();
|
||||
for (McpSchema.Tool tool : tools) {
|
||||
if (tool.name().equals(botMcp.getMcpToolName())) {
|
||||
if (tool.name().equals(mcpToolName)) {
|
||||
Map<String, Object> properties = tool.inputSchema().properties();
|
||||
List<String> required = tool.inputSchema().required();
|
||||
McpTool mcpTool = new McpTool();
|
||||
|
||||
@@ -200,7 +200,10 @@ public class ModelServiceImpl extends ServiceImpl<ModelMapper, Model> implements
|
||||
|
||||
@Override
|
||||
public void removeByEntity(Model entity) {
|
||||
QueryWrapper queryWrapper = QueryWrapper.create().eq(Model::getProviderId, entity.getProviderId()).eq(Model::getGroupName, entity.getGroupName());
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.eq(Model::getProviderId, entity.getProviderId())
|
||||
.eq(Model::getGroupName, entity.getGroupName())
|
||||
.eq(Model::getTenantId, entity.getTenantId());
|
||||
modelMapper.deleteByQuery(queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package tech.easyflow.ai.service.impl;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.ai.entity.BotPlugin;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.ai.entity.Plugin;
|
||||
import tech.easyflow.ai.entity.PluginItem;
|
||||
import tech.easyflow.ai.entity.Workflow;
|
||||
@@ -11,10 +11,10 @@ import tech.easyflow.ai.enums.PluginType;
|
||||
import tech.easyflow.ai.plugin.workflow.availability.WorkflowPluginAvailabilityDecision;
|
||||
import tech.easyflow.ai.plugin.workflow.availability.WorkflowPluginAvailabilityService;
|
||||
import tech.easyflow.ai.plugin.workflow.snapshot.WorkflowPluginSnapshotResolver;
|
||||
import tech.easyflow.ai.mapper.BotPluginMapper;
|
||||
import tech.easyflow.ai.mapper.PluginMapper;
|
||||
import tech.easyflow.ai.mapper.PluginItemMapper;
|
||||
import tech.easyflow.ai.service.PluginItemService;
|
||||
import tech.easyflow.ai.service.PluginVisibilityService;
|
||||
import tech.easyflow.ai.service.WorkflowService;
|
||||
import tech.easyflow.common.constant.Constants;
|
||||
import tech.easyflow.common.entity.LoginAccount;
|
||||
@@ -26,8 +26,6 @@ import javax.annotation.Resource;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
import static tech.easyflow.ai.entity.table.BotPluginTableDef.BOT_PLUGIN;
|
||||
|
||||
/**
|
||||
* 服务层实现。
|
||||
*
|
||||
@@ -43,19 +41,35 @@ public class PluginItemServiceImpl extends ServiceImpl<PluginItemMapper, PluginI
|
||||
@Resource
|
||||
private PluginMapper pluginMapper;
|
||||
|
||||
@Resource
|
||||
private BotPluginMapper botPluginMapper;
|
||||
@Resource
|
||||
private WorkflowPluginAvailabilityService workflowPluginAvailabilityService;
|
||||
@Resource
|
||||
private WorkflowPluginSnapshotResolver workflowPluginSnapshotResolver;
|
||||
@Resource
|
||||
private WorkflowService workflowService;
|
||||
@Resource
|
||||
private PluginVisibilityService pluginVisibilityService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean savePluginTool(PluginItem pluginItem) {
|
||||
Plugin plugin = pluginMapper.selectOneById(pluginItem.getPluginId());
|
||||
if (plugin != null && PluginType.isWorkflow(plugin.getType())) {
|
||||
if (pluginItem == null || pluginItem.getPluginId() == null) {
|
||||
throw new BusinessException("插件工具参数不完整");
|
||||
}
|
||||
Plugin plugin = pluginMapper.selectOneByQuery(QueryWrapper.create()
|
||||
.eq(Plugin::getId, pluginItem.getPluginId())
|
||||
.forUpdate());
|
||||
if (plugin == null) {
|
||||
throw new BusinessException("插件不存在");
|
||||
}
|
||||
LoginAccount account = SaTokenUtil.getLoginAccount();
|
||||
if (account == null || account.getTenantId() == null || plugin.getTenantId() == null
|
||||
|| !account.getTenantId().toString().equals(plugin.getTenantId().toString())) {
|
||||
throw new BusinessException("无权限新增该插件工具");
|
||||
}
|
||||
pluginVisibilityService.assertPluginVisible(
|
||||
plugin.getCreatedBy(), plugin.getId(), "无权限新增该插件工具");
|
||||
if (PluginType.isWorkflow(plugin.getType())) {
|
||||
throw new BusinessException("工作流插件工具由系统自动维护,不支持手动新增");
|
||||
}
|
||||
pluginItem.setCreated(new Date());
|
||||
@@ -100,41 +114,6 @@ public class PluginItemServiceImpl extends ServiceImpl<PluginItemMapper, PluginI
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PluginItem> searchPluginToolByPluginId(BigInteger pluginId, BigInteger botId) {
|
||||
QueryWrapper queryAiPluginToolWrapper = QueryWrapper.create()
|
||||
.select()
|
||||
.eq(PluginItem::getPluginId, pluginId);
|
||||
List<PluginItem> pluginItems = pluginItemMapper.selectListByQueryAs(queryAiPluginToolWrapper, PluginItem.class);
|
||||
// 查询当前bot有哪些插件工具方法
|
||||
QueryWrapper queryBotPluginTools = QueryWrapper.create()
|
||||
.select()
|
||||
.eq(BotPlugin::getBotId, botId);
|
||||
List<BigInteger> aiBotPluginToolIds = botPluginMapper.selectListWithRelationsByQueryAs(queryBotPluginTools, BigInteger.class);
|
||||
aiBotPluginToolIds.forEach(botPluginTooId -> {
|
||||
pluginItems.forEach(item -> {
|
||||
if (Objects.equals(botPluginTooId, item.getId())) {
|
||||
item.setJoinBot(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
return pluginItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PluginItem> getPluginToolList(BigInteger botId) {
|
||||
QueryWrapper queryAiPluginToolWrapper = QueryWrapper.create()
|
||||
.select(BOT_PLUGIN.PLUGIN_ITEM_ID)
|
||||
.from(BOT_PLUGIN)
|
||||
.where(BOT_PLUGIN.BOT_ID.eq(botId));
|
||||
List<BigInteger> pluginToolIds = botPluginMapper.selectListByQueryAs(queryAiPluginToolWrapper, BigInteger.class);
|
||||
if (pluginToolIds == null || pluginToolIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 查询当前bots对应的有哪些pluginTool
|
||||
return pluginItemMapper.selectListByIds(pluginToolIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<?> pluginToolTest(String inputData, BigInteger pluginToolId) {
|
||||
PluginItem pluginItem = pluginItemMapper.selectOneById(pluginToolId);
|
||||
|
||||
@@ -15,10 +15,10 @@ import tech.easyflow.ai.mapper.PluginMapper;
|
||||
import tech.easyflow.ai.plugin.workflow.availability.WorkflowPluginAvailabilityDecision;
|
||||
import tech.easyflow.ai.plugin.workflow.availability.WorkflowPluginAvailabilityService;
|
||||
import tech.easyflow.ai.plugin.workflow.binding.WorkflowPluginBindingService;
|
||||
import tech.easyflow.ai.service.BotPluginService;
|
||||
import tech.easyflow.ai.service.PluginItemService;
|
||||
import tech.easyflow.ai.service.PluginService;
|
||||
import tech.easyflow.ai.service.PluginVisibilityService;
|
||||
import tech.easyflow.ai.service.AgentResourceReferenceService;
|
||||
import tech.easyflow.common.domain.Result;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.common.entity.LoginAccount;
|
||||
@@ -57,9 +57,6 @@ public class PluginServiceImpl extends ServiceImpl<PluginMapper, Plugin> impleme
|
||||
@Resource
|
||||
PluginCategoryMappingMapper pluginCategoryMappingMapper;
|
||||
|
||||
@Resource
|
||||
private BotPluginService botPluginService;
|
||||
|
||||
@Resource
|
||||
private PluginItemService pluginItemService;
|
||||
@Resource
|
||||
@@ -70,6 +67,8 @@ public class PluginServiceImpl extends ServiceImpl<PluginMapper, Plugin> impleme
|
||||
private WorkflowPluginBindingService workflowPluginBindingService;
|
||||
@Resource
|
||||
private WorkflowPluginAvailabilityService workflowPluginAvailabilityService;
|
||||
@Resource
|
||||
private AgentResourceReferenceService agentResourceReferenceService;
|
||||
|
||||
@Override
|
||||
public Plugin savePlugin(Plugin plugin) {
|
||||
@@ -87,34 +86,42 @@ public class PluginServiceImpl extends ServiceImpl<PluginMapper, Plugin> impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean removePlugin(String id) {
|
||||
Plugin plugin = pluginMapper.selectOneByQuery(QueryWrapper.create()
|
||||
.eq(Plugin::getId, id)
|
||||
.forUpdate());
|
||||
if (plugin == null) {
|
||||
throw new BusinessException("插件不存在");
|
||||
}
|
||||
LoginAccount loginAccount = SaTokenUtil.getLoginAccount();
|
||||
if (loginAccount == null || loginAccount.getTenantId() == null || plugin.getTenantId() == null
|
||||
|| !loginAccount.getTenantId().toString().equals(plugin.getTenantId().toString())) {
|
||||
throw new BusinessException("无权限删除该插件");
|
||||
}
|
||||
pluginVisibilityService.assertPluginVisible(
|
||||
plugin.getCreatedBy(), plugin.getId(), "无权限删除该插件");
|
||||
|
||||
List<PluginItem> pluginItems = pluginItemService.getByPluginId(id);
|
||||
// 父插件行先于工具行锁定,与工具新增和 Agent 绑定保持一致锁顺序。
|
||||
List<PluginItem> pluginItems = pluginItemService.list(QueryWrapper.create()
|
||||
.eq(PluginItem::getPluginId, plugin.getId())
|
||||
.orderBy(PluginItem::getId, true)
|
||||
.forUpdate());
|
||||
List<BigInteger> pluginToolIds = new ArrayList<>();
|
||||
|
||||
if (pluginItems != null && !pluginItems.isEmpty()) {
|
||||
|
||||
pluginToolIds = pluginItems.stream().map(PluginItem::getId).collect(Collectors.toList());
|
||||
QueryWrapper queryWrapper = QueryWrapper.create();
|
||||
queryWrapper.in(BotPlugin::getPluginItemId, pluginToolIds);
|
||||
boolean exists = botPluginService.exists(queryWrapper);
|
||||
}
|
||||
|
||||
if (exists){
|
||||
throw new BusinessException("插件中有工具还关联着bot,请先取消关联!");
|
||||
agentResourceReferenceService.assertPluginItemsUnused(pluginToolIds);
|
||||
if (!pluginToolIds.isEmpty()) {
|
||||
boolean result = pluginItemService.removeByIds(pluginToolIds);
|
||||
if (!result) {
|
||||
log.error("删除插件工具表结果为0");
|
||||
throw new BusinessException("删除失败,请稍后重试!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( !pluginToolIds.isEmpty()) {
|
||||
boolean result = pluginItemService.removeByIds(pluginToolIds);
|
||||
if (!result){
|
||||
log.error("删除插件工具表结果为0");
|
||||
throw new BusinessException("删除失败,请稍后重试!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int remove = pluginMapper.deleteById(id);
|
||||
if (remove <= 0) {
|
||||
log.error("删除插件结果为0");
|
||||
@@ -122,7 +129,6 @@ public class PluginServiceImpl extends ServiceImpl<PluginMapper, Plugin> impleme
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,65 +5,52 @@ import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import tech.easyflow.ai.entity.Bot;
|
||||
import tech.easyflow.ai.entity.BotDocumentCollection;
|
||||
import tech.easyflow.ai.entity.BotWorkflow;
|
||||
import tech.easyflow.ai.entity.DocumentCollection;
|
||||
import tech.easyflow.ai.entity.Workflow;
|
||||
import tech.easyflow.ai.plugin.workflow.dependency.WorkflowPluginDependencyService;
|
||||
import tech.easyflow.ai.service.BotDocumentCollectionService;
|
||||
import tech.easyflow.ai.service.BotService;
|
||||
import tech.easyflow.ai.service.BotWorkflowService;
|
||||
import tech.easyflow.ai.service.AgentResourceReferenceService;
|
||||
import tech.easyflow.ai.service.DocumentCollectionService;
|
||||
import tech.easyflow.ai.service.ResourceOfflineImpactService;
|
||||
import tech.easyflow.ai.service.WorkflowService;
|
||||
import tech.easyflow.ai.vo.OfflineImpactBindingVo;
|
||||
import tech.easyflow.ai.vo.OfflineImpactCheckVo;
|
||||
import tech.easyflow.common.cache.RedisLockExecutor;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 资源下线影响检查与 Bot 静默解绑实现。
|
||||
* 资源下线影响检查与 Agent 静默解绑实现。
|
||||
*/
|
||||
@Service
|
||||
public class ResourceOfflineImpactServiceImpl implements ResourceOfflineImpactService {
|
||||
|
||||
private static final String KNOWLEDGE_NODE_TYPE = "knowledgeNode";
|
||||
private static final String BOT_BINDING_LOCK_KEY_PREFIX = "easyflow:lock:bot:binding:";
|
||||
private static final Duration LOCK_WAIT_TIMEOUT = Duration.ofSeconds(2);
|
||||
private static final Duration LOCK_LEASE_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
private final BotWorkflowService botWorkflowService;
|
||||
private final BotDocumentCollectionService botDocumentCollectionService;
|
||||
private final BotService botService;
|
||||
private final WorkflowService workflowService;
|
||||
private final RedisLockExecutor redisLockExecutor;
|
||||
private final DocumentCollectionService documentCollectionService;
|
||||
private final WorkflowPluginDependencyService workflowPluginDependencyService;
|
||||
private final AgentResourceReferenceService agentResourceReferenceService;
|
||||
|
||||
public ResourceOfflineImpactServiceImpl(BotWorkflowService botWorkflowService,
|
||||
BotDocumentCollectionService botDocumentCollectionService,
|
||||
BotService botService,
|
||||
WorkflowService workflowService,
|
||||
RedisLockExecutor redisLockExecutor,
|
||||
WorkflowPluginDependencyService workflowPluginDependencyService) {
|
||||
this.botWorkflowService = botWorkflowService;
|
||||
this.botDocumentCollectionService = botDocumentCollectionService;
|
||||
this.botService = botService;
|
||||
/**
|
||||
* 创建资源下线影响服务。
|
||||
*
|
||||
* @param workflowService 工作流服务
|
||||
* @param documentCollectionService 知识库服务
|
||||
* @param workflowPluginDependencyService 工作流插件依赖服务
|
||||
* @param agentResourceReferenceService Agent 资源引用服务
|
||||
*/
|
||||
public ResourceOfflineImpactServiceImpl(WorkflowService workflowService,
|
||||
DocumentCollectionService documentCollectionService,
|
||||
WorkflowPluginDependencyService workflowPluginDependencyService,
|
||||
AgentResourceReferenceService agentResourceReferenceService) {
|
||||
this.workflowService = workflowService;
|
||||
this.redisLockExecutor = redisLockExecutor;
|
||||
this.documentCollectionService = documentCollectionService;
|
||||
this.workflowPluginDependencyService = workflowPluginDependencyService;
|
||||
this.agentResourceReferenceService = agentResourceReferenceService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,17 +58,18 @@ public class ResourceOfflineImpactServiceImpl implements ResourceOfflineImpactSe
|
||||
*/
|
||||
@Override
|
||||
public OfflineImpactCheckVo checkWorkflowImpact(BigInteger workflowId) {
|
||||
List<OfflineImpactBindingVo> botBindings = listBotsByWorkflowId(workflowId);
|
||||
List<OfflineImpactBindingVo> pluginBindings = workflowPluginDependencyService.listPluginsByWorkflowId(workflowId);
|
||||
List<OfflineImpactBindingVo> agentBindings = listAgentsByWorkflowId(workflowId);
|
||||
List<OfflineImpactBindingVo> pluginBindings =
|
||||
workflowPluginDependencyService.listPluginsByWorkflowId(workflowId);
|
||||
OfflineImpactCheckVo result = new OfflineImpactCheckVo();
|
||||
result.setCanProceed(true);
|
||||
result.setBotBindings(botBindings);
|
||||
result.setHasBotBindings(!botBindings.isEmpty());
|
||||
result.setAgentBindings(agentBindings);
|
||||
result.setHasAgentBindings(!agentBindings.isEmpty());
|
||||
result.setPluginBindings(pluginBindings);
|
||||
result.setHasPluginBindings(!pluginBindings.isEmpty());
|
||||
result.setWorkflowUsages(Collections.emptyList());
|
||||
result.setHasWorkflowUsages(false);
|
||||
result.setMessage(resolveWorkflowOfflineImpactMessage(botBindings, pluginBindings));
|
||||
result.setMessage(resolveWorkflowOfflineImpactMessage(agentBindings, pluginBindings));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -90,18 +78,18 @@ public class ResourceOfflineImpactServiceImpl implements ResourceOfflineImpactSe
|
||||
*/
|
||||
@Override
|
||||
public OfflineImpactCheckVo checkKnowledgeImpact(BigInteger knowledgeId) {
|
||||
List<OfflineImpactBindingVo> botBindings = listBotsByKnowledgeId(knowledgeId);
|
||||
List<OfflineImpactBindingVo> agentBindings = listAgentsByKnowledgeId(knowledgeId);
|
||||
List<OfflineImpactBindingVo> workflowUsages = listWorkflowsUsingKnowledge(knowledgeId);
|
||||
OfflineImpactCheckVo result = new OfflineImpactCheckVo();
|
||||
result.setBotBindings(botBindings);
|
||||
result.setHasBotBindings(!botBindings.isEmpty());
|
||||
result.setAgentBindings(agentBindings);
|
||||
result.setHasAgentBindings(!agentBindings.isEmpty());
|
||||
result.setWorkflowUsages(workflowUsages);
|
||||
result.setHasWorkflowUsages(!workflowUsages.isEmpty());
|
||||
result.setCanProceed(workflowUsages.isEmpty());
|
||||
result.setMessage(workflowUsages.isEmpty()
|
||||
? (botBindings.isEmpty()
|
||||
? (agentBindings.isEmpty()
|
||||
? "当前知识库下线后不会影响已有绑定"
|
||||
: "当前知识库下线成功后,将自动从相关聊天助手中解绑")
|
||||
: "当前知识库下线成功后,将自动从相关智能体中解绑")
|
||||
: "当前知识库仍被工作流使用,请先调整工作流后再下线");
|
||||
return result;
|
||||
}
|
||||
@@ -110,84 +98,53 @@ public class ResourceOfflineImpactServiceImpl implements ResourceOfflineImpactSe
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void unbindWorkflowFromBots(BigInteger workflowId) {
|
||||
List<BotWorkflow> relations = botWorkflowService.list(QueryWrapper.create()
|
||||
.eq(BotWorkflow::getWorkflowId, workflowId));
|
||||
Set<BigInteger> botIds = collectBotIds(relations, BotWorkflow::getBotId);
|
||||
for (BigInteger botId : botIds) {
|
||||
redisLockExecutor.executeWithLock(
|
||||
BOT_BINDING_LOCK_KEY_PREFIX + botId,
|
||||
LOCK_WAIT_TIMEOUT,
|
||||
LOCK_LEASE_TIMEOUT,
|
||||
() -> {
|
||||
botWorkflowService.remove(QueryWrapper.create()
|
||||
.eq(BotWorkflow::getBotId, botId)
|
||||
.eq(BotWorkflow::getWorkflowId, workflowId));
|
||||
trimPublishedSnapshotBindings(botId, "workflowBindings", "workflowId", workflowId);
|
||||
}
|
||||
);
|
||||
}
|
||||
public void unbindWorkflowFromAgents(BigInteger workflowId) {
|
||||
agentResourceReferenceService.unbindWorkflow(workflowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void unbindKnowledgeFromBots(BigInteger knowledgeId) {
|
||||
List<BotDocumentCollection> relations = botDocumentCollectionService.list(QueryWrapper.create()
|
||||
.eq(BotDocumentCollection::getDocumentCollectionId, knowledgeId));
|
||||
Set<BigInteger> botIds = collectBotIds(relations, BotDocumentCollection::getBotId);
|
||||
for (BigInteger botId : botIds) {
|
||||
redisLockExecutor.executeWithLock(
|
||||
BOT_BINDING_LOCK_KEY_PREFIX + botId,
|
||||
LOCK_WAIT_TIMEOUT,
|
||||
LOCK_LEASE_TIMEOUT,
|
||||
() -> {
|
||||
botDocumentCollectionService.remove(QueryWrapper.create()
|
||||
.eq(BotDocumentCollection::getBotId, botId)
|
||||
.eq(BotDocumentCollection::getDocumentCollectionId, knowledgeId));
|
||||
trimPublishedSnapshotBindings(botId, "knowledgeBindings", "knowledgeId", knowledgeId);
|
||||
}
|
||||
);
|
||||
}
|
||||
public void unbindKnowledgeFromAgents(BigInteger knowledgeId) {
|
||||
agentResourceReferenceService.unbindKnowledge(knowledgeId);
|
||||
}
|
||||
|
||||
private List<OfflineImpactBindingVo> listBotsByWorkflowId(BigInteger workflowId) {
|
||||
List<BotWorkflow> relations = botWorkflowService.list(QueryWrapper.create()
|
||||
.eq(BotWorkflow::getWorkflowId, workflowId));
|
||||
return listBotsByIds(collectBotIds(relations, BotWorkflow::getBotId));
|
||||
/**
|
||||
* 汇总绑定指定工作流的 Agent。
|
||||
*
|
||||
* @param workflowId 工作流 ID
|
||||
* @return 去重后的 Agent 摘要
|
||||
*/
|
||||
private List<OfflineImpactBindingVo> listAgentsByWorkflowId(BigInteger workflowId) {
|
||||
return agentResourceReferenceService.listAgentsByWorkflowId(workflowId);
|
||||
}
|
||||
|
||||
private List<OfflineImpactBindingVo> listBotsByKnowledgeId(BigInteger knowledgeId) {
|
||||
List<BotDocumentCollection> relations = botDocumentCollectionService.list(QueryWrapper.create()
|
||||
.eq(BotDocumentCollection::getDocumentCollectionId, knowledgeId));
|
||||
return listBotsByIds(collectBotIds(relations, BotDocumentCollection::getBotId));
|
||||
}
|
||||
|
||||
private List<OfflineImpactBindingVo> listBotsByIds(Set<BigInteger> botIds) {
|
||||
if (botIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Bot> bots = botService.listByIds(botIds);
|
||||
Map<BigInteger, Bot> botMap = new HashMap<>();
|
||||
for (Bot bot : bots) {
|
||||
botMap.put(bot.getId(), bot);
|
||||
}
|
||||
List<OfflineImpactBindingVo> result = new ArrayList<>(botIds.size());
|
||||
for (BigInteger botId : botIds) {
|
||||
Bot bot = botMap.get(botId);
|
||||
if (bot == null) {
|
||||
continue;
|
||||
}
|
||||
result.add(toBindingVo(bot.getId(), bot.getTitle()));
|
||||
}
|
||||
return result;
|
||||
/**
|
||||
* 汇总绑定指定知识库的 Agent。
|
||||
*
|
||||
* @param knowledgeId 知识库 ID
|
||||
* @return 去重后的 Agent 摘要
|
||||
*/
|
||||
private List<OfflineImpactBindingVo> listAgentsByKnowledgeId(BigInteger knowledgeId) {
|
||||
return agentResourceReferenceService.listAgentsByKnowledgeId(knowledgeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询仍在设计内容中引用指定知识库的工作流。
|
||||
*
|
||||
* @param knowledgeId 知识库 ID
|
||||
* @return 工作流摘要
|
||||
*/
|
||||
private List<OfflineImpactBindingVo> listWorkflowsUsingKnowledge(BigInteger knowledgeId) {
|
||||
List<Workflow> workflows = workflowService.list();
|
||||
DocumentCollection knowledge = documentCollectionService.getById(knowledgeId);
|
||||
if (knowledge == null) {
|
||||
throw new BusinessException("知识库不存在,无法检查下线影响");
|
||||
}
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.select(Workflow::getId, Workflow::getTitle, Workflow::getContent)
|
||||
.eq(Workflow::getTenantId, knowledge.getTenantId());
|
||||
List<Workflow> workflows = workflowService.list(queryWrapper);
|
||||
if (workflows == null || workflows.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -197,26 +154,43 @@ public class ResourceOfflineImpactServiceImpl implements ResourceOfflineImpactSe
|
||||
continue;
|
||||
}
|
||||
if (containsKnowledgeReference(workflow.getContent(), knowledgeId)) {
|
||||
result.add(toBindingVo(workflow.getId(), workflow.getTitle()));
|
||||
OfflineImpactBindingVo item = new OfflineImpactBindingVo();
|
||||
item.setId(workflow.getId());
|
||||
item.setTitle(workflow.getTitle());
|
||||
result.add(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String resolveWorkflowOfflineImpactMessage(List<OfflineImpactBindingVo> botBindings,
|
||||
/**
|
||||
* 生成工作流下线影响提示。
|
||||
*
|
||||
* @param agentBindings Agent 绑定
|
||||
* @param pluginBindings 插件绑定
|
||||
* @return 提示信息
|
||||
*/
|
||||
private String resolveWorkflowOfflineImpactMessage(List<OfflineImpactBindingVo> agentBindings,
|
||||
List<OfflineImpactBindingVo> pluginBindings) {
|
||||
if (!pluginBindings.isEmpty() && !botBindings.isEmpty()) {
|
||||
return "当前工作流被插件和聊天助手引用,下线后插件将不可用,聊天助手将自动解绑";
|
||||
if (!pluginBindings.isEmpty() && !agentBindings.isEmpty()) {
|
||||
return "当前工作流被插件和智能体引用,下线后插件将不可用,智能体将自动解绑";
|
||||
}
|
||||
if (!pluginBindings.isEmpty()) {
|
||||
return "当前工作流被插件引用,下线后相关插件将不可用";
|
||||
}
|
||||
if (!botBindings.isEmpty()) {
|
||||
return "当前工作流下线成功后,将自动从相关聊天助手中解绑";
|
||||
if (!agentBindings.isEmpty()) {
|
||||
return "当前工作流下线成功后,将自动从相关智能体中解绑";
|
||||
}
|
||||
return "当前工作流下线后不会影响已有绑定";
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断工作流内容是否引用指定知识库。
|
||||
*
|
||||
* @param content 工作流内容
|
||||
* @param knowledgeId 知识库 ID
|
||||
* @return 是否引用
|
||||
*/
|
||||
private boolean containsKnowledgeReference(String content, BigInteger knowledgeId) {
|
||||
if (!StringUtils.hasText(content) || knowledgeId == null) {
|
||||
return false;
|
||||
@@ -224,7 +198,7 @@ public class ResourceOfflineImpactServiceImpl implements ResourceOfflineImpactSe
|
||||
try {
|
||||
Object parsed = JSON.parse(content);
|
||||
if (!(parsed instanceof JSONObject root)) {
|
||||
return false;
|
||||
throw new BusinessException("工作流定义格式异常,无法确认知识库下线影响");
|
||||
}
|
||||
JSONArray nodes = root.getJSONArray("nodes");
|
||||
if (nodes == null || nodes.isEmpty()) {
|
||||
@@ -246,73 +220,10 @@ public class ResourceOfflineImpactServiceImpl implements ResourceOfflineImpactSe
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Exception ignored) {
|
||||
return false;
|
||||
} catch (BusinessException exception) {
|
||||
throw exception;
|
||||
} catch (Exception exception) {
|
||||
throw new BusinessException("工作流定义解析失败,无法确认知识库下线影响");
|
||||
}
|
||||
}
|
||||
|
||||
private void trimPublishedSnapshotBindings(BigInteger botId,
|
||||
String bindingsKey,
|
||||
String idKey,
|
||||
BigInteger resourceId) {
|
||||
Bot bot = botService.getById(botId);
|
||||
if (bot == null || bot.getPublishedSnapshotJson() == null || bot.getPublishedSnapshotJson().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> snapshot = new LinkedHashMap<>(bot.getPublishedSnapshotJson());
|
||||
Object rawBindings = snapshot.get(bindingsKey);
|
||||
if (!(rawBindings instanceof List<?> bindings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Map<String, Object>> filtered = new ArrayList<>();
|
||||
boolean changed = false;
|
||||
String expectedId = resourceId == null ? null : resourceId.toString();
|
||||
for (Object item : bindings) {
|
||||
if (!(item instanceof Map<?, ?> bindingMap)) {
|
||||
continue;
|
||||
}
|
||||
Object currentId = bindingMap.get(idKey);
|
||||
if (expectedId != null && currentId != null && expectedId.equals(String.valueOf(currentId))) {
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
filtered.add(new LinkedHashMap<>((Map<String, Object>) bindingMap));
|
||||
}
|
||||
if (!changed) {
|
||||
return;
|
||||
}
|
||||
snapshot.put(bindingsKey, filtered);
|
||||
Bot update = new Bot();
|
||||
update.setId(botId);
|
||||
update.setPublishedSnapshotJson(snapshot);
|
||||
botService.updateById(update);
|
||||
}
|
||||
|
||||
private <T> Set<BigInteger> collectBotIds(Collection<T> relations, BotIdGetter<T> getter) {
|
||||
if (relations == null || relations.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<BigInteger> result = new LinkedHashSet<>();
|
||||
for (T relation : relations) {
|
||||
BigInteger botId = getter.getBotId(relation);
|
||||
if (botId != null) {
|
||||
result.add(botId);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private OfflineImpactBindingVo toBindingVo(BigInteger id, String title) {
|
||||
OfflineImpactBindingVo vo = new OfflineImpactBindingVo();
|
||||
vo.setId(id);
|
||||
vo.setTitle(title);
|
||||
return vo;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface BotIdGetter<T> {
|
||||
|
||||
BigInteger getBotId(T relation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@ public class OfflineImpactCheckVo {
|
||||
|
||||
private boolean canProceed;
|
||||
|
||||
private boolean hasBotBindings;
|
||||
private boolean hasAgentBindings;
|
||||
|
||||
private boolean hasWorkflowUsages;
|
||||
|
||||
private boolean hasPluginBindings;
|
||||
|
||||
private List<OfflineImpactBindingVo> botBindings = new ArrayList<>();
|
||||
private List<OfflineImpactBindingVo> agentBindings = new ArrayList<>();
|
||||
|
||||
private List<OfflineImpactBindingVo> workflowUsages = new ArrayList<>();
|
||||
|
||||
@@ -43,21 +43,21 @@ public class OfflineImpactCheckVo {
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在 Bot 绑定。
|
||||
* 是否存在 Agent 绑定。
|
||||
*
|
||||
* @return 是否存在 Bot 绑定
|
||||
* @return 是否存在 Agent 绑定
|
||||
*/
|
||||
public boolean isHasBotBindings() {
|
||||
return hasBotBindings;
|
||||
public boolean isHasAgentBindings() {
|
||||
return hasAgentBindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否存在 Bot 绑定。
|
||||
* 设置是否存在 Agent 绑定。
|
||||
*
|
||||
* @param hasBotBindings 是否存在 Bot 绑定
|
||||
* @param hasAgentBindings 是否存在 Agent 绑定
|
||||
*/
|
||||
public void setHasBotBindings(boolean hasBotBindings) {
|
||||
this.hasBotBindings = hasBotBindings;
|
||||
public void setHasAgentBindings(boolean hasAgentBindings) {
|
||||
this.hasAgentBindings = hasAgentBindings;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,21 +79,21 @@ public class OfflineImpactCheckVo {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Bot 绑定列表。
|
||||
* 获取 Agent 绑定列表。
|
||||
*
|
||||
* @return Bot 绑定列表
|
||||
* @return Agent 绑定列表
|
||||
*/
|
||||
public List<OfflineImpactBindingVo> getBotBindings() {
|
||||
return botBindings;
|
||||
public List<OfflineImpactBindingVo> getAgentBindings() {
|
||||
return agentBindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Bot 绑定列表。
|
||||
* 设置 Agent 绑定列表。
|
||||
*
|
||||
* @param botBindings Bot 绑定列表
|
||||
* @param agentBindings Agent 绑定列表
|
||||
*/
|
||||
public void setBotBindings(List<OfflineImpactBindingVo> botBindings) {
|
||||
this.botBindings = botBindings;
|
||||
public void setAgentBindings(List<OfflineImpactBindingVo> agentBindings) {
|
||||
this.agentBindings = agentBindings;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package tech.easyflow.ai.service.impl;
|
||||
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import tech.easyflow.ai.entity.DocumentCollection;
|
||||
import tech.easyflow.ai.entity.Workflow;
|
||||
import tech.easyflow.ai.plugin.workflow.dependency.WorkflowPluginDependencyService;
|
||||
import tech.easyflow.ai.service.AgentResourceReferenceService;
|
||||
import tech.easyflow.ai.service.DocumentCollectionService;
|
||||
import tech.easyflow.ai.service.WorkflowService;
|
||||
import tech.easyflow.ai.vo.OfflineImpactBindingVo;
|
||||
import tech.easyflow.ai.vo.OfflineImpactCheckVo;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Agent 资源下线影响检查测试。
|
||||
*/
|
||||
public class ResourceOfflineImpactServiceImplTest {
|
||||
|
||||
/**
|
||||
* 验证工作流影响结果使用 Agent 绑定字段并委托 Agent 解绑。
|
||||
*/
|
||||
@Test
|
||||
public void shouldReportAndUnbindAgentWorkflowBindings() {
|
||||
WorkflowService workflowService = mock(WorkflowService.class);
|
||||
DocumentCollectionService documentCollectionService =
|
||||
mock(DocumentCollectionService.class);
|
||||
WorkflowPluginDependencyService pluginDependencyService =
|
||||
mock(WorkflowPluginDependencyService.class);
|
||||
AgentResourceReferenceService referenceService = mock(AgentResourceReferenceService.class);
|
||||
BigInteger workflowId = BigInteger.valueOf(10);
|
||||
OfflineImpactBindingVo binding = binding(BigInteger.ONE, "测试智能体");
|
||||
when(referenceService.listAgentsByWorkflowId(workflowId)).thenReturn(List.of(binding));
|
||||
when(pluginDependencyService.listPluginsByWorkflowId(workflowId))
|
||||
.thenReturn(Collections.emptyList());
|
||||
ResourceOfflineImpactServiceImpl service = new ResourceOfflineImpactServiceImpl(
|
||||
workflowService, documentCollectionService, pluginDependencyService, referenceService);
|
||||
|
||||
OfflineImpactCheckVo result = service.checkWorkflowImpact(workflowId);
|
||||
service.unbindWorkflowFromAgents(workflowId);
|
||||
|
||||
Assert.assertTrue(result.isHasAgentBindings());
|
||||
Assert.assertEquals(List.of(binding), result.getAgentBindings());
|
||||
Assert.assertTrue(result.getMessage().contains("智能体"));
|
||||
verify(referenceService).unbindWorkflow(workflowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证知识库影响结果使用 Agent 绑定字段并委托 Agent 解绑。
|
||||
*/
|
||||
@Test
|
||||
public void shouldReportAndUnbindAgentKnowledgeBindings() {
|
||||
WorkflowService workflowService = mock(WorkflowService.class);
|
||||
DocumentCollectionService documentCollectionService =
|
||||
mock(DocumentCollectionService.class);
|
||||
WorkflowPluginDependencyService pluginDependencyService =
|
||||
mock(WorkflowPluginDependencyService.class);
|
||||
AgentResourceReferenceService referenceService = mock(AgentResourceReferenceService.class);
|
||||
BigInteger knowledgeId = BigInteger.valueOf(20);
|
||||
OfflineImpactBindingVo binding = binding(BigInteger.TWO, "知识智能体");
|
||||
DocumentCollection knowledge = new DocumentCollection();
|
||||
knowledge.setTenantId(BigInteger.ONE);
|
||||
when(documentCollectionService.getById(knowledgeId)).thenReturn(knowledge);
|
||||
when(referenceService.listAgentsByKnowledgeId(knowledgeId)).thenReturn(List.of(binding));
|
||||
when(workflowService.list(any(QueryWrapper.class))).thenReturn(Collections.emptyList());
|
||||
ResourceOfflineImpactServiceImpl service = new ResourceOfflineImpactServiceImpl(
|
||||
workflowService, documentCollectionService, pluginDependencyService, referenceService);
|
||||
|
||||
OfflineImpactCheckVo result = service.checkKnowledgeImpact(knowledgeId);
|
||||
service.unbindKnowledgeFromAgents(knowledgeId);
|
||||
|
||||
Assert.assertTrue(result.isHasAgentBindings());
|
||||
Assert.assertEquals(List.of(binding), result.getAgentBindings());
|
||||
Assert.assertTrue(result.getMessage().contains("智能体"));
|
||||
verify(referenceService).unbindKnowledge(knowledgeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 工作流定义损坏时必须阻止知识库下线,避免漏判引用关系。
|
||||
*/
|
||||
@Test(expected = BusinessException.class)
|
||||
public void shouldFailClosedWhenWorkflowContentIsInvalid() {
|
||||
WorkflowService workflowService = mock(WorkflowService.class);
|
||||
DocumentCollectionService documentCollectionService =
|
||||
mock(DocumentCollectionService.class);
|
||||
WorkflowPluginDependencyService pluginDependencyService =
|
||||
mock(WorkflowPluginDependencyService.class);
|
||||
AgentResourceReferenceService referenceService = mock(AgentResourceReferenceService.class);
|
||||
BigInteger knowledgeId = BigInteger.valueOf(20);
|
||||
DocumentCollection knowledge = new DocumentCollection();
|
||||
knowledge.setTenantId(BigInteger.ONE);
|
||||
Workflow workflow = new Workflow();
|
||||
workflow.setId(BigInteger.TEN);
|
||||
workflow.setContent("{invalid");
|
||||
when(documentCollectionService.getById(knowledgeId)).thenReturn(knowledge);
|
||||
when(referenceService.listAgentsByKnowledgeId(knowledgeId))
|
||||
.thenReturn(Collections.emptyList());
|
||||
when(workflowService.list(any(QueryWrapper.class))).thenReturn(List.of(workflow));
|
||||
ResourceOfflineImpactServiceImpl service = new ResourceOfflineImpactServiceImpl(
|
||||
workflowService, documentCollectionService, pluginDependencyService, referenceService);
|
||||
|
||||
service.checkKnowledgeImpact(knowledgeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建绑定摘要。
|
||||
*
|
||||
* @param id 资源 ID
|
||||
* @param title 标题
|
||||
* @return 绑定摘要
|
||||
*/
|
||||
private OfflineImpactBindingVo binding(BigInteger id, String title) {
|
||||
OfflineImpactBindingVo binding = new OfflineImpactBindingVo();
|
||||
binding.setId(id);
|
||||
binding.setTitle(title);
|
||||
return binding;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user