From 64a85c6a5b3b21a7ce4fb91dd265411960bb7079 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E5=AD=90=E9=BB=98?= <925456043@qq.com>
Date: Thu, 13 Aug 2026 22:29:59 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BB=9F=E4=B8=80=E5=88=97=E8=A1=A8?=
=?UTF-8?q?=E6=A8=A1=E7=B3=8A=E6=90=9C=E7=B4=A2=E8=A1=8C=E4=B8=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 统一管理端和用户中心搜索参数及多字段包含匹配
- 修复聊天搜索竞态并优化部门重名路径展示
- 补充部门展开、模型空白词和聊天查询回归测试
---
.../controller/agent/AgentController.java | 10 ++
.../admin/controller/ai/BotController.java | 10 ++
.../ai/DocumentCollectionController.java | 10 ++
.../controller/ai/DocumentController.java | 20 ++-
.../controller/ai/FaqItemController.java | 12 +-
.../admin/controller/ai/McpController.java | 10 ++
.../admin/controller/ai/ModelController.java | 6 +-
.../admin/controller/ai/PluginController.java | 26 ++-
.../controller/ai/PluginItemController.java | 10 ++
.../controller/ai/ResourceController.java | 10 ++
.../ai/ShareKnowledgeController.java | 12 +-
.../controller/ai/WorkflowController.java | 16 +-
.../ai/WorkflowExecResultController.java | 10 ++
.../ai/WorkflowExecStepController.java | 12 +-
.../controller/job/SysJobController.java | 10 ++
.../skill/SkillCategoryController.java | 9 +-
.../controller/skill/SkillController.java | 4 +-
.../system/SysAccountController.java | 17 +-
.../system/SysApiKeyController.java | 10 ++
.../system/SysApiKeyResourceController.java | 10 ++
.../controller/system/SysDeptController.java | 45 ++++-
.../controller/system/SysLogController.java | 23 +++
.../system/SysPositionController.java | 23 ++-
.../controller/system/SysRoleController.java | 10 ++
.../system/SysUserFeedbackController.java | 12 +-
.../ai/WorkflowDesignerOptionService.java | 28 ++-
.../controller/ai/ModelControllerTest.java | 24 +++
.../system/SysDeptControllerTest.java | 78 +++++++++
.../controller/ai/UcBotController.java | 14 ++
.../controller/ai/UcWorkflowController.java | 16 ++
.../common/util/SearchKeywordUtil.java | 35 ++++
.../web/controller/BaseCurdController.java | 106 ++++++++++-
.../java/tech/easyflow/ai/entity/Bot.java | 21 +++
.../tech/easyflow/ai/entity/Workflow.java | 21 +++
.../easyflow/ai/service/PluginService.java | 4 +-
.../ai/service/impl/DocumentServiceImpl.java | 3 +-
.../ai/service/impl/PluginServiceImpl.java | 16 +-
.../impl/ApprovalAssigneeServiceImpl.java | 3 +-
.../service/impl/ApprovalFlowServiceImpl.java | 3 +-
.../impl/ApprovalQueryServiceImpl.java | 3 +-
.../chatlog/domain/query/ChatPageQuery.java | 19 ++
.../ChatAnalyticalDBRepository.java | 3 +-
.../mysql/MySqlChatSessionRepository.java | 37 ++++
.../impl/ChatSessionQueryServiceImpl.java | 2 +-
.../impl/ChatSessionQueryServiceImplTest.java | 31 ++++
...killCapabilityTargetAccessServiceImpl.java | 3 +-
.../tech/easyflow/system/entity/SysDept.java | 22 +++
.../tech/easyflow/system/entity/SysDict.java | 3 +-
.../CommonSelectDataModal.vue | 12 +-
.../components/headerSearch/HeaderSearch.vue | 7 +
.../app/src/locales/langs/en-US/plugin.json | 2 +-
.../app/src/locales/langs/en-US/sysDept.json | 1 +
.../app/src/locales/langs/en-US/sysLog.json | 2 +-
.../app/src/locales/langs/zh-CN/plugin.json | 2 +-
.../app/src/locales/langs/zh-CN/sysDept.json | 1 +
.../app/src/locales/langs/zh-CN/sysLog.json | 2 +-
.../app/src/views/ai/agents/AgentList.vue | 20 +--
.../app/src/views/ai/bots/index.vue | 15 +-
.../app/src/views/ai/chat/index.vue | 32 ++--
.../app/src/views/ai/chatHistory/index.vue | 4 +-
.../documentCollection/DocumentCollection.vue | 15 +-
.../ai/documentCollection/DocumentTable.vue | 2 +-
.../views/ai/documentCollection/FaqTable.vue | 2 +-
.../app/src/views/ai/mcp/Mcp.vue | 2 +-
.../src/views/ai/plugin/plugin-query.test.ts | 13 +-
.../app/src/views/ai/plugin/plugin-query.ts | 10 +-
.../src/views/ai/resource/ResourceList.vue | 18 +-
.../src/views/ai/workflow/WorkflowList.vue | 6 +-
.../execute/WorkflowExecResultList.vue | 6 +-
.../workflow/execute/WorkflowExecStepList.vue | 4 +-
.../src/views/config/apikey/SysApiKeyList.vue | 2 +-
.../SysApiKeyResourcePermissionList.vue | 4 +-
.../views/system/sysDept/SysDeptList.test.ts | 164 ++++++++++++++++++
.../src/views/system/sysDept/SysDeptList.vue | 31 +++-
.../system/sysFeedback/sysFeedbackList.vue | 10 +-
.../src/views/system/sysJob/SysJobList.vue | 9 +-
.../src/views/system/sysLog/SysLogList.vue | 2 +-
.../system/sysPosition/SysPositionList.vue | 3 +-
.../src/views/system/sysRole/SysRoleList.vue | 2 +-
.../components/headerSearch/HeaderSearch.vue | 30 +++-
.../app/src/views/assistantMarket/index.vue | 6 +-
.../app/src/views/bots/index.vue | 6 +-
.../app/src/views/chatHistory/index.vue | 53 +++---
83 files changed, 1134 insertions(+), 208 deletions(-)
create mode 100644 easyflow-commons/easyflow-common-base/src/main/java/tech/easyflow/common/util/SearchKeywordUtil.java
create mode 100644 easyflow-ui-admin/app/src/views/system/sysDept/SysDeptList.test.ts
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/agent/AgentController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/agent/AgentController.java
index 8894668f..6a35dbc2 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/agent/AgentController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/agent/AgentController.java
@@ -101,6 +101,16 @@ public class AgentController extends BaseCurdController {
super(service);
}
+ /**
+ * 获取 Agent 列表关键字搜索字段。
+ *
+ * @return Agent 名称和描述属性
+ */
+ @Override
+ protected String[] getKeywordSearchProperties() {
+ return new String[]{"name", "description"};
+ }
+
/**
* 获取 Agent 详情。
*
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/BotController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/BotController.java
index 3ee2f0b4..37be4678 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/BotController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/BotController.java
@@ -97,6 +97,16 @@ public class BotController extends BaseCurdController {
this.botMessageService = botMessageService;
}
+ /**
+ * 获取智能体列表关键字搜索字段。
+ *
+ * @return 标题和描述属性
+ */
+ @Override
+ protected String[] getKeywordSearchProperties() {
+ return new String[]{"title", "description"};
+ }
+
@Resource
private BotPluginService botPluginService;
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentCollectionController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentCollectionController.java
index a99aff78..70523874 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentCollectionController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentCollectionController.java
@@ -86,6 +86,16 @@ public class DocumentCollectionController extends BaseCurdController onSaveOrUpdateBefore(DocumentCollection entity, boolean isSave) {
normalizeVisibilityScope(entity, isSave);
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentController.java
index 2d81c131..ad92f16b 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentController.java
@@ -99,6 +99,16 @@ public class DocumentController extends BaseCurdController> documentList(@RequestParam(name="title", required = false) String fileName, @RequestParam(name="pageSize") int pageSize, @RequestParam(name = "pageNumber") int pageNumber) {
+ public Result> documentList(
+ @RequestParam(name = "keyword", required = false) String keyword,
+ @RequestParam(name = "title", required = false) String legacyTitle,
+ @RequestParam(name = "pageSize") int pageSize,
+ @RequestParam(name = "pageNumber") int pageNumber) {
String kbSlug = RequestUtil.getParamAsString("id");
if (StringUtil.noText(kbSlug)) {
throw new BusinessException("知识库id不能为空");
}
DocumentCollection knowledge = getDocumentCollection(kbSlug, ResourceAction.READ, "无权限访问知识库");
- Page documentList = documentService.getDocumentList(knowledge.getId().toString(), pageSize, pageNumber,fileName);
+ String effectiveKeyword = StringUtil.hasText(keyword) ? keyword : legacyTitle;
+ Page documentList = documentService.getDocumentList(
+ knowledge.getId().toString(), pageSize, pageNumber, effectiveKeyword);
return Result.ok(documentList);
}
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/FaqItemController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/FaqItemController.java
index dbcdf93a..27580be0 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/FaqItemController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/FaqItemController.java
@@ -2,6 +2,7 @@ package tech.easyflow.admin.controller.ai;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.mybatisflex.core.paginate.Page;
+import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -114,9 +115,14 @@ public class FaqItemController extends BaseCurdController {
super(service);
}
+ /**
+ * 获取 MCP 列表关键字搜索字段。
+ *
+ * @return 标题和描述属性
+ */
+ @Override
+ protected String[] getKeywordSearchProperties() {
+ return new String[]{"title", "description"};
+ }
+
@Resource
private AgentResourceReferenceService agentResourceReferenceService;
@Override
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/ModelController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/ModelController.java
index 988a8f96..a7bf1633 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/ModelController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/ModelController.java
@@ -193,8 +193,10 @@ public class ModelController extends BaseCurdController {
QueryWrapper queryWrapper = QueryWrapper.create();
queryWrapper.eq(Model::getProviderId, providerId);
queryWrapper.eq(Model::getModelType, modelType);
- if (StringUtils.hasLength(selectText)) {
- queryWrapper.and(ModelTableDef.MODEL.TITLE.like(selectText).or(ModelTableDef.MODEL.MODEL_NAME.like(selectText)));
+ String keyword = normalizeSearchKeyword(selectText);
+ if (StringUtils.hasText(keyword)) {
+ queryWrapper.and(buildLiteralContainsCondition(
+ keyword, ModelTableDef.MODEL.TITLE, ModelTableDef.MODEL.MODEL_NAME));
}
List totalList = service.getMapper().selectListWithRelationsByQuery(queryWrapper);
Map> groupList = totalList.stream().collect(Collectors.groupingBy(Model::getGroupName));
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/PluginController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/PluginController.java
index ebc3b78f..ca1ca71d 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/PluginController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/PluginController.java
@@ -2,6 +2,7 @@ package tech.easyflow.admin.controller.ai;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.mybatisflex.core.paginate.Page;
+import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
@@ -58,6 +59,16 @@ public class PluginController extends BaseCurdController
this.pluginService = service;
}
+ /**
+ * 获取插件列表关键字搜索字段。
+ *
+ * @return 插件名称和描述属性
+ */
+ @Override
+ protected String[] getKeywordSearchProperties() {
+ return new String[]{"name", "description"};
+ }
+
@Resource
PluginService pluginService;
@Resource
@@ -118,7 +129,7 @@ public class PluginController extends BaseCurdController
}
/**
- * 按分类分页查询插件,并支持按名称模糊查询。
+ * 按分类分页查询插件,并支持按名称、描述模糊查询。
*
* @param request 当前请求
* @param sortKey 排序字段
@@ -126,13 +137,15 @@ public class PluginController extends BaseCurdController
* @param pageNumber 页码
* @param pageSize 每页数量
* @param category 分类 ID,0 表示全部分类
- * @param name 插件名称关键字
+ * @param keyword 插件名称或描述关键字
+ * @param name 兼容旧客户端的插件名称关键字
* @return 插件分页结果
*/
@GetMapping("/pageByCategory")
@SaCheckPermission("/api/v1/plugin/query")
public Result> pageByCategory(HttpServletRequest request, String sortKey, String sortType,
- Long pageNumber, Long pageSize, int category, String name) {
+ Long pageNumber, Long pageSize, int category,
+ String keyword, String name) {
if (pageNumber == null || pageNumber < 1) {
pageNumber = 1L;
}
@@ -145,7 +158,10 @@ public class PluginController extends BaseCurdController
queryWrapper.orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
return Result.ok(queryPage(new Page<>(pageNumber, pageSize), queryWrapper));
} else {
- Result> result = pluginService.pageByCategory(pageNumber, pageSize, category, name);
+ String effectiveKeyword = normalizeSearchKeyword(
+ keyword == null || keyword.isBlank() ? name : keyword);
+ Result> result = pluginService.pageByCategory(
+ pageNumber, pageSize, category, effectiveKeyword);
if (result != null && result.getData() != null) {
aiResourceCreatorNameSupport.fillPluginCreatorNames(result.getData().getRecords());
}
@@ -160,7 +176,7 @@ public class PluginController extends BaseCurdController
workflowVisibilityQueryHelper.applyReadableAccess(queryWrapper);
queryWrapper.eq("publish_status", tech.easyflow.ai.enums.PublishStatus.PUBLISHED.getCode());
if (keyword != null && !keyword.isBlank()) {
- queryWrapper.like("title", keyword.trim());
+ queryWrapper.and(buildLiteralContainsCondition(keyword, new QueryColumn("title")));
}
queryWrapper.orderBy("modified desc");
LoginAccount loginAccount = SaTokenUtil.getLoginAccount();
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/PluginItemController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/PluginItemController.java
index a3e43cac..30e898e6 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/PluginItemController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/PluginItemController.java
@@ -67,6 +67,16 @@ public class PluginItemController extends BaseCurdController onSaveOrUpdateBefore(Resource entity, boolean isSave) {
LoginAccount loginUser = SaTokenUtil.getLoginAccount();
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/ShareKnowledgeController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/ShareKnowledgeController.java
index 47155c46..f3d2f0ec 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/ShareKnowledgeController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/ShareKnowledgeController.java
@@ -6,6 +6,7 @@ import com.easyagents.core.store.DocumentStore;
import com.easyagents.core.store.StoreOptions;
import com.easyagents.core.store.StoreResult;
import com.mybatisflex.core.paginate.Page;
+import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -47,6 +48,7 @@ import tech.easyflow.ai.vo.FaqImportResultVo;
import tech.easyflow.ai.vo.KnowledgeShareAuthContext;
import tech.easyflow.ai.vo.KnowledgeShareViewDetail;
import tech.easyflow.common.domain.Result;
+import tech.easyflow.common.util.SearchKeywordUtil;
import tech.easyflow.common.filestorage.FileStorageService;
import tech.easyflow.common.vo.UploadResVo;
import tech.easyflow.common.web.exceptions.BusinessException;
@@ -668,9 +670,15 @@ public class ShareKnowledgeController {
faqCategoryService.ensureDefaultCategory(context.getKnowledge().getId());
QueryWrapper queryWrapper = QueryWrapper.create()
.eq(FaqItem::getCollectionId, context.getKnowledge().getId());
+ String keyword = request.getParameter("keyword");
String question = request.getParameter("question");
- if (StringUtils.hasText(question)) {
- queryWrapper.like(FaqItem::getQuestion, question.trim());
+ if (StringUtils.hasText(keyword)) {
+ String pattern = SearchKeywordUtil.literalContainsPattern(keyword);
+ queryWrapper.and(new QueryColumn("question").likeRaw(pattern)
+ .or(new QueryColumn("answer_text").likeRaw(pattern)));
+ } else if (StringUtils.hasText(question)) {
+ queryWrapper.and(new QueryColumn("question")
+ .likeRaw(SearchKeywordUtil.literalContainsPattern(question)));
}
String categoryId = request.getParameter("categoryId");
if (StringUtils.hasText(categoryId)) {
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/WorkflowController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/WorkflowController.java
index f3a8c955..45b6bfab 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/WorkflowController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/WorkflowController.java
@@ -113,6 +113,16 @@ public class WorkflowController extends BaseCurdController> designerPlugins(
Long pageNumber,
- Long pageSize) {
- return Result.ok(workflowDesignerOptionService.pagePlugins(pageNumber, pageSize));
+ Long pageSize,
+ String keyword) {
+ return Result.ok(workflowDesignerOptionService.pagePlugins(pageNumber, pageSize, keyword));
}
/**
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/WorkflowExecResultController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/WorkflowExecResultController.java
index 1b5d4b82..de7ce6c7 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/WorkflowExecResultController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/WorkflowExecResultController.java
@@ -37,6 +37,16 @@ public class WorkflowExecResultController extends BaseCurdController> getListByRecordId(BigInteger recordId) {
if (recordId == null) {
@@ -60,4 +70,4 @@ public class WorkflowExecStepController extends BaseCurdController
this.workflowRunningParameterResolver = workflowRunningParameterResolver;
}
+ /**
+ * 获取定时任务关键字搜索字段。
+ *
+ * @return 任务名称和备注属性
+ */
+ @Override
+ protected String[] getKeywordSearchProperties() {
+ return new String[]{"jobName", "remark"};
+ }
+
@GetMapping("/start")
@SaCheckPermission("/api/v1/sysJob/save")
@LogRecord("启动定时任务")
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/skill/SkillCategoryController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/skill/SkillCategoryController.java
index d6a3873d..5418a52d 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/skill/SkillCategoryController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/skill/SkillCategoryController.java
@@ -10,6 +10,7 @@ import tech.easyflow.common.annotation.UsePermission;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
+import tech.easyflow.common.util.SearchKeywordUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.common.web.jsonbody.JsonBody;
import tech.easyflow.skill.entity.SkillCategory;
@@ -69,9 +70,11 @@ public class SkillCategoryController {
queryWrapper.eq(SkillCategory::getId, entity.getId(), entity.getId() != null)
.eq(SkillCategory::getParentId, entity.getParentId(), entity.getParentId() != null)
.eq(SkillCategory::getLevelNo, entity.getLevelNo(), entity.getLevelNo() != null)
- .eq(SkillCategory::getStatus, entity.getStatus(), entity.getStatus() != null)
- .like(SkillCategory::getCategoryName, entity.getCategoryName(),
- entity.getCategoryName() != null && !entity.getCategoryName().isBlank());
+ .eq(SkillCategory::getStatus, entity.getStatus(), entity.getStatus() != null);
+ if (entity.getCategoryName() != null && !entity.getCategoryName().isBlank()) {
+ queryWrapper.and("category_name LIKE ?",
+ SearchKeywordUtil.literalContainsPattern(entity.getCategoryName()));
+ }
}
RoleCategoryAccessSnapshot access = categoryPermissionService.getCurrentAccess(CategoryResourceType.SKILL.getCode());
if (access.isRestricted()) {
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/skill/SkillController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/skill/SkillController.java
index d7049fd7..d21bdc80 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/skill/SkillController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/skill/SkillController.java
@@ -24,6 +24,7 @@ import tech.easyflow.approval.entity.vo.ApprovalActionResult;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.satoken.util.SaTokenUtil;
+import tech.easyflow.common.util.SearchKeywordUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.common.web.jsonbody.JsonBody;
import tech.easyflow.skill.capability.SkillCapabilityBindingService;
@@ -159,7 +160,7 @@ public class SkillController {
.eq("source_type", sourceType, hasText(sourceType));
String keyword = hasText(displayName) ? displayName : name;
if (hasText(keyword)) {
- String pattern = "%" + keyword + "%";
+ String pattern = SearchKeywordUtil.literalContainsPattern(keyword);
query.and("(name LIKE ? OR display_name LIKE ? OR description LIKE ?)", pattern, pattern, pattern);
}
if (hasText(capabilityType)) {
@@ -695,4 +696,5 @@ public class SkillController {
private boolean hasText(String value) {
return value != null && !value.isBlank();
}
+
}
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysAccountController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysAccountController.java
index 67eb0802..c9e0a8e8 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysAccountController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysAccountController.java
@@ -111,7 +111,7 @@ public class SysAccountController extends BaseCurdController> list(SysDept entity, Boolean asTree, String sortKey, String sortType) {
QueryWrapper queryWrapper = QueryWrapper.create(entity, buildOperators(entity));
+ String keyword = entity == null ? "" : normalizeSearchKeyword(entity.getKeyword());
+ if (tech.easyflow.common.util.StringUtil.hasText(keyword)) {
+ queryWrapper.and(buildLiteralContainsCondition(
+ keyword, new QueryColumn("dept_name"), new QueryColumn("dept_code")));
+ }
queryWrapper.orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
- List sysMenus = service.list(queryWrapper);
- return Result.ok(Tree.tryToTree(sysMenus, "id", "parentId"));
+ List matchedDepartments = service.list(queryWrapper);
+ if (!tech.easyflow.common.util.StringUtil.hasText(keyword)) {
+ return Result.ok(Tree.tryToTree(matchedDepartments, "id", "parentId"));
+ }
+ if (matchedDepartments.isEmpty()) {
+ return Result.ok(List.of());
+ }
+
+ // 搜索结果保留所有重名命中项,并补齐各自祖先节点以维持可定位的树结构。
+ Set visibleIds = new LinkedHashSet<>();
+ for (SysDept department : matchedDepartments) {
+ visibleIds.add(department.getId());
+ addAncestorIds(visibleIds, department.getAncestors());
+ }
+ QueryWrapper visibleDepartmentQuery = QueryWrapper.create()
+ .in(SysDept::getId, visibleIds)
+ .orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
+ List visibleDepartments = service.list(visibleDepartmentQuery);
+ return Result.ok(Tree.tryToTree(visibleDepartments, "id", "parentId"));
+ }
+
+ /**
+ * 将逗号分隔的祖先 ID 加入可见集合。
+ *
+ * @param visibleIds 可见部门 ID 集合
+ * @param ancestors 祖先路径
+ */
+ private void addAncestorIds(Set visibleIds, String ancestors) {
+ if (!tech.easyflow.common.util.StringUtil.hasText(ancestors)) {
+ return;
+ }
+ for (String ancestor : ancestors.split(",")) {
+ String normalized = ancestor.trim();
+ if (!normalized.isEmpty() && !"0".equals(normalized)) {
+ visibleIds.add(new BigInteger(normalized));
+ }
+ }
}
/**
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysLogController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysLogController.java
index 8a6afe9d..5229e1ca 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysLogController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysLogController.java
@@ -1,12 +1,16 @@
package tech.easyflow.admin.controller.system;
import com.mybatisflex.core.paginate.Page;
+import com.mybatisflex.core.query.QueryCondition;
+import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.relation.RelationManager;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.common.util.StringUtil;
+import tech.easyflow.common.entity.LoginAccount;
+import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.log.annotation.LogRecord;
@@ -20,6 +24,9 @@ import java.time.format.DateTimeParseException;
import java.util.Collections;
import java.util.Date;
+import static tech.easyflow.system.entity.table.SysAccountTableDef.SYS_ACCOUNT;
+import static tech.easyflow.system.entity.table.SysLogTableDef.SYS_LOG;
+
/**
* 操作日志表 控制层。
*
@@ -53,6 +60,22 @@ public class SysLogController extends BaseCurdController
@Override
protected QueryWrapper buildQueryWrapper(HttpServletRequest request) {
QueryWrapper queryWrapper = super.buildQueryWrapper(request);
+ String keyword = normalizeSearchKeyword(request.getParameter("keyword"));
+ if (StringUtil.hasText(keyword)) {
+ LoginAccount account = SaTokenUtil.getLoginAccount();
+ String pattern = buildLiteralContainsPattern(keyword);
+ QueryCondition accountKeyword = SYS_ACCOUNT.LOGIN_NAME.likeRaw(pattern)
+ .or(SYS_ACCOUNT.NICKNAME.likeRaw(pattern));
+ QueryWrapper accountExists = QueryMethods.selectOne()
+ .from(SYS_ACCOUNT)
+ .where(SYS_ACCOUNT.ID.eq(SYS_LOG.ACCOUNT_ID))
+ .and(SYS_ACCOUNT.TENANT_ID.eq(account.getTenantId()))
+ .and(accountKeyword);
+ QueryCondition keywordCondition = SYS_LOG.ACTION_NAME.likeRaw(pattern)
+ .or(SYS_LOG.ACTION_IP.likeRaw(pattern))
+ .or(QueryMethods.exists(accountExists));
+ queryWrapper.and(keywordCondition);
+ }
Date createdStart = parseQueryTime(request.getParameter("createdStart"));
Date createdEnd = parseQueryTime(request.getParameter("createdEnd"));
if (createdStart != null && createdEnd != null && createdStart.after(createdEnd)) {
diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysPositionController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysPositionController.java
index 3f09f703..b1071527 100644
--- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysPositionController.java
+++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysPositionController.java
@@ -41,7 +41,7 @@ public class SysPositionController extends BaseCurdController
- * 支持按岗位名称模糊查询,按状态、编码精确查询。
+ * 支持按岗位名称、岗位编码统一模糊查询,状态保持精确查询。
*
*
* @param request 请求对象
@@ -67,17 +67,22 @@ public class SysPositionController extends BaseCurdController onSaveOrUpdateBefore(SysUserFeedback entity, boolean isSave) {
if (!isSave) {
@@ -33,4 +43,4 @@ public class SysUserFeedbackController extends BaseCurdController pagePlugins(Long pageNumber, Long pageSize) {
+ public Page pagePlugins(
+ Long pageNumber, Long pageSize, String keyword) {
LoginAccount account = requireAccount();
QueryWrapper wrapper = QueryWrapper.create()
.eq(Plugin::getTenantId, account.getTenantId().longValue())
.orderBy(Plugin::getCreated, false);
List plugins = pluginService.getMapper().selectListWithRelationsByQuery(wrapper);
- List availablePlugins = pluginService.preparePluginsForCurrentUser(plugins, false, true);
+ String normalizedKeyword = keyword == null ? "" : keyword.trim().toLowerCase(java.util.Locale.ROOT);
+ List availablePlugins = pluginService.preparePluginsForCurrentUser(plugins, false, true).stream()
+ .filter(plugin -> matchesKeyword(normalizedKeyword, plugin.getName(), plugin.getDescription()))
+ .toList();
List options = availablePlugins.stream()
.map(this::toPluginOption)
.toList();
@@ -229,6 +234,25 @@ public class WorkflowDesignerOptionService {
);
}
+ /**
+ * 判断任一候选文本是否包含关键字。
+ *
+ * @param keyword 已归一化的小写关键字
+ * @param values 候选文本
+ * @return 空关键字或任一文本命中时返回 {@code true}
+ */
+ private boolean matchesKeyword(String keyword, String... values) {
+ if (keyword == null || keyword.isEmpty()) {
+ return true;
+ }
+ for (String value : values) {
+ if (value != null && value.toLowerCase(java.util.Locale.ROOT).contains(keyword)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* 查询一个插件工具的工作流节点安全配置。
*
diff --git a/easyflow-api/easyflow-api-admin/src/test/java/tech/easyflow/admin/controller/ai/ModelControllerTest.java b/easyflow-api/easyflow-api-admin/src/test/java/tech/easyflow/admin/controller/ai/ModelControllerTest.java
index 16fac409..e15cf734 100644
--- a/easyflow-api/easyflow-api-admin/src/test/java/tech/easyflow/admin/controller/ai/ModelControllerTest.java
+++ b/easyflow-api/easyflow-api-admin/src/test/java/tech/easyflow/admin/controller/ai/ModelControllerTest.java
@@ -1,13 +1,18 @@
package tech.easyflow.admin.controller.ai;
+import com.mybatisflex.core.query.QueryWrapper;
import org.testng.Assert;
import org.testng.annotations.Test;
import tech.easyflow.ai.entity.Model;
+import tech.easyflow.ai.mapper.ModelMapper;
import tech.easyflow.ai.service.ModelService;
import tech.easyflow.common.domain.Result;
+import java.math.BigInteger;
import java.util.List;
+import java.util.Map;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -38,4 +43,23 @@ public class ModelControllerTest {
result.getData().get(0).getContextWindowTokens());
verify(service).listSelectableModels(query, false, "id", "desc");
}
+
+ /**
+ * 验证仅包含空白字符的选择文本不会生成模糊查询条件。
+ */
+ @Test
+ public void selectLlmShouldIgnoreBlankSearchText() {
+ ModelService service = mock(ModelService.class);
+ ModelMapper mapper = mock(ModelMapper.class);
+ when(service.getMapper()).thenReturn(mapper);
+ when(mapper.selectListWithRelationsByQuery(any(QueryWrapper.class)))
+ .thenReturn(List.of());
+ ModelController controller = new ModelController(service);
+
+ Result