From 645462abd0455eaad30a3393574edd3bb14bd3db Mon Sep 17 00:00:00 2001
From: zhuoqinglian <1035449612@qq.com>
Date: Sat, 28 Feb 2026 17:51:46 +0800
Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E8=A7=92?=
=?UTF-8?q?=E8=89=B2=E6=A0=87=E7=AD=BE=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
main/manager-web/src/apis/module/agent.js | 31 +++++
.../manager-web/src/components/DeviceItem.vue | 68 ++++++++-
main/manager-web/src/i18n/de.js | 3 +-
main/manager-web/src/i18n/en.js | 3 +-
main/manager-web/src/i18n/pt_BR.js | 3 +-
main/manager-web/src/i18n/vi.js | 3 +-
main/manager-web/src/i18n/zh_CN.js | 3 +-
main/manager-web/src/i18n/zh_TW.js | 3 +-
main/manager-web/src/views/roleConfig.vue | 131 +++++++++++++++++-
9 files changed, 235 insertions(+), 13 deletions(-)
diff --git a/main/manager-web/src/apis/module/agent.js b/main/manager-web/src/apis/module/agent.js
index b90c932c..5cbd60cb 100644
--- a/main/manager-web/src/apis/module/agent.js
+++ b/main/manager-web/src/apis/module/agent.js
@@ -398,4 +398,35 @@ export default {
});
}).send();
},
+ // 获取智能体标签
+ getAgentTags(agentId, callback) {
+ RequestService.sendRequest()
+ .url(`${getServiceUrl()}/agent/${agentId}/tags`)
+ .method('GET')
+ .success((res) => {
+ RequestService.clearRequestTime();
+ callback(res);
+ })
+ .networkFail(() => {
+ RequestService.reAjaxFun(() => {
+ this.getAgentTags(agentId, callback);
+ });
+ }).send();
+ },
+ // 保存智能体标签
+ saveAgentTags(agentId, tags, callback) {
+ RequestService.sendRequest()
+ .url(`${getServiceUrl()}/agent/${agentId}/tags`)
+ .method('PUT')
+ .data(tags)
+ .success((res) => {
+ RequestService.clearRequestTime();
+ callback(res);
+ })
+ .networkFail(() => {
+ RequestService.reAjaxFun(() => {
+ this.saveAgentTags(agentId, tags, callback);
+ });
+ }).send();
+ },
}
diff --git a/main/manager-web/src/components/DeviceItem.vue b/main/manager-web/src/components/DeviceItem.vue
index c29d3243..28cbcb5b 100644
--- a/main/manager-web/src/components/DeviceItem.vue
+++ b/main/manager-web/src/components/DeviceItem.vue
@@ -39,6 +39,11 @@
{{ $t('home.lastConversation') }}:{{ formattedLastConnectedTime }}
+
@@ -81,6 +86,10 @@ export default {
} else {
return this.device.lastConnectedAt;
}
+ },
+ tags() {
+ if (!this.device.tags) return [];
+ return this.device.tags.map((tag) => tag.tagName);
}
},
methods: {
@@ -102,15 +111,34 @@ export default {
}
this.$emit('chat-history', { agentId: this.device.agentId, agentName: this.device.agentName })
}
+ },
+ watch: {
+ tags: {
+ handler(newTags) {
+ if (newTags.length === 0) return;
+ this.$nextTick(() => {
+ const scrollWidth = this.$refs.scrollRef.clientWidth;
+ const tagsWidth = this.$refs.tagsRef.clientWidth;
+ if (tagsWidth < scrollWidth) {
+ this.$refs.tagsRef.style.width = '100%';
+ this.$refs.tagsRef.style.justifyContent = 'flex-end';
+ } else {
+ this.$refs.tagsRef.style.width = 'fit-content';
+ this.$refs.tagsRef.style.justifyContent = 'flex-start';
+ }
+ })
+ },
+ immediate: true
+ }
}
}
-
From f2c6cb8641589f9a8af49ff30e979756d7cd8a39 Mon Sep 17 00:00:00 2001
From: zhuoqinglian <1035449612@qq.com>
Date: Mon, 2 Mar 2026 15:33:38 +0800
Subject: [PATCH 2/4] =?UTF-8?q?update:=20=E4=BC=98=E5=8C=96=E6=96=87?=
=?UTF-8?q?=E6=9C=AC=E6=BA=A2=E5=87=BA=E6=82=AC=E6=B5=AE=E7=AA=97=E6=A0=B7?=
=?UTF-8?q?=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
main/manager-web/src/components/DeviceItem.vue | 16 ++++++++++++++--
main/manager-web/src/styles/global.scss | 10 ++++++++++
main/manager-web/src/views/roleConfig.vue | 9 ++++++---
3 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/main/manager-web/src/components/DeviceItem.vue b/main/manager-web/src/components/DeviceItem.vue
index 28cbcb5b..87a82687 100644
--- a/main/manager-web/src/components/DeviceItem.vue
+++ b/main/manager-web/src/components/DeviceItem.vue
@@ -1,13 +1,15 @@
-
+
+
{{ device.agentName }}
+

-
@@ -140,6 +142,16 @@ export default {
background: #fafcfe;
padding: 22px 22px 14px;
box-sizing: border-box;
+ &-title {
+ flex: 1;
+ font-weight: bold;
+ font-size: 18px;
+ color: #3d4566;
+ text-align: left;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ }
}
.device-name {
diff --git a/main/manager-web/src/styles/global.scss b/main/manager-web/src/styles/global.scss
index 3aba4bdb..15bbe6b1 100644
--- a/main/manager-web/src/styles/global.scss
+++ b/main/manager-web/src/styles/global.scss
@@ -19,4 +19,14 @@ select:-webkit-autofill:focus {
.el-icon-video-play, .el-icon-video-pause {
font-size: 18px !important;
+}
+
+.is-light {
+ border: 1px solid #e4e7ed !important;
+ color: #595959;
+ font-size: 14px;
+ background: #fafcfe !important;
+ .popper__arrow {
+ border-top-color: #e4e7ed !important;
+ }
}
\ No newline at end of file
diff --git a/main/manager-web/src/views/roleConfig.vue b/main/manager-web/src/views/roleConfig.vue
index 75cea7fe..470b7c66 100644
--- a/main/manager-web/src/views/roleConfig.vue
+++ b/main/manager-web/src/views/roleConfig.vue
@@ -32,7 +32,7 @@
v-model="inputValue"
ref="saveTagInput"
size="small"
- maxLength="100"
+ maxLength="64"
@keyup.enter.native="handleInputConfirm"
@blur="handleInputConfirm"
>
@@ -63,7 +63,7 @@
@@ -387,7 +387,6 @@ export default {
asr: false, // 语音识别功能状态
},
dynamicTags: [],
- maxVisibleTags: 5,
inputVisible: false,
inputValue: ''
};
@@ -471,6 +470,7 @@ export default {
intentModelId: "",
},
};
+ this.dynamicTags = [];
this.currentFunctions = [];
this.$message.success({
message: i18n.t("roleConfig.resetSuccess"),
@@ -1531,5 +1531,8 @@ export default {
}
.input-new-tag {
width: 90px;
+ &::v-deep(.el-input__inner) {
+ width: 90px !important;
+ }
}
From 90ebb8d49995e8c36a322463dec88034a2e56c98 Mon Sep 17 00:00:00 2001
From: zhuoqinglian <1035449612@qq.com>
Date: Mon, 2 Mar 2026 15:35:47 +0800
Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E8=AF=AD=E9=9F=B3=E5=90=88?=
=?UTF-8?q?=E6=88=90-=E9=9F=B3=E8=89=B2=E7=AE=A1=E7=90=86=EF=BC=8C?=
=?UTF-8?q?=E7=BC=96=E8=BE=91=E6=A8=A1=E5=BC=8F=E4=B8=8B=E6=B7=BB=E5=8A=A0?=
=?UTF-8?q?=E5=8F=96=E6=B6=88=E6=93=8D=E4=BD=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/components/AddWisdomBodyDialog.vue | 2 +-
main/manager-web/src/components/TtsModel.vue | 21 +++++++++++++++++--
2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/main/manager-web/src/components/AddWisdomBodyDialog.vue b/main/manager-web/src/components/AddWisdomBodyDialog.vue
index 33507fab..2c886526 100644
--- a/main/manager-web/src/components/AddWisdomBodyDialog.vue
+++ b/main/manager-web/src/components/AddWisdomBodyDialog.vue
@@ -14,7 +14,7 @@
*
{{ $t('addAgentDialog.agentName') }}:
-
+
diff --git a/main/manager-web/src/components/TtsModel.vue b/main/manager-web/src/components/TtsModel.vue
index 4038b8a8..6960af8f 100644
--- a/main/manager-web/src/components/TtsModel.vue
+++ b/main/manager-web/src/components/TtsModel.vue
@@ -71,8 +71,14 @@
{{ $t('ttsModel.delete') }}
-
{{ $t('ttsModel.save') }}
+
+
+ {{ $t('button.cancel') }}
+
+ {{ $t('ttsModel.save') }}
+
+
@@ -340,6 +346,17 @@ export default {
this.$set(row, 'originalData', { ...row });
},
+ cancelEdit(row) {
+ // 通过新增创建的数据,取消编辑时,需要从数组中移除
+ if (!row.id) {
+ this.ttsModels.shift(row);
+ } else {
+ Object.assign(row, row.originalData);
+ delete row.originalData;
+ }
+ row.editing = false;
+ },
+
saveEdit(row) {
if (!row.voiceCode || !row.voiceName || !row.languageType) {
this.$message.error({
@@ -447,7 +464,7 @@ export default {
referenceText: '',
selected: false,
editing: true,
- sort: maxSort + 1
+ sort: 0 // 新增数据默认排序在顶部
};
this.ttsModels.unshift(newRow);
From 0eafd37bf6ddb76ea871800a0143785e77bc6b20 Mon Sep 17 00:00:00 2001
From: rainv123 <2148537152@qq.com>
Date: Mon, 2 Mar 2026 15:51:25 +0800
Subject: [PATCH 4/4] =?UTF-8?q?feat:=E6=99=BA=E8=83=BD=E4=BD=93=E6=B7=BB?=
=?UTF-8?q?=E5=8A=A0=E6=A0=87=E7=AD=BE=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../xiaozhi/common/exception/ErrorCode.java | 5 +
.../agent/controller/AgentController.java | 50 ++++-
.../modules/agent/dao/AgentTagDao.java | 24 +++
.../agent/dao/AgentTagRelationDao.java | 18 ++
.../xiaozhi/modules/agent/dto/AgentDTO.java | 4 +
.../modules/agent/dto/AgentTagDTO.java | 20 ++
.../modules/agent/entity/AgentTagEntity.java | 41 ++++
.../agent/entity/AgentTagRelationEntity.java | 38 ++++
.../agent/service/AgentTagService.java | 25 +++
.../agent/service/impl/AgentServiceImpl.java | 31 ++-
.../service/impl/AgentTagServiceImpl.java | 199 ++++++++++++++++++
.../resources/db/changelog/202602281000.sql | 29 +++
.../db/changelog/db.changelog-master.yaml | 7 +
.../main/resources/i18n/messages.properties | 5 +-
.../resources/i18n/messages_de_DE.properties | 5 +-
.../resources/i18n/messages_en_US.properties | 5 +-
.../resources/i18n/messages_vi_VN.properties | 5 +-
.../resources/i18n/messages_zh_TW.properties | 5 +-
.../resources/mapper/agent/AgentTagDao.xml | 56 +++++
.../mapper/agent/AgentTagRelationDao.xml | 22 ++
20 files changed, 586 insertions(+), 8 deletions(-)
create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagDao.java
create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagRelationDao.java
create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentTagDTO.java
create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagEntity.java
create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagRelationEntity.java
create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentTagService.java
create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentTagServiceImpl.java
create mode 100644 main/manager-api/src/main/resources/db/changelog/202602281000.sql
create mode 100644 main/manager-api/src/main/resources/mapper/agent/AgentTagDao.xml
create mode 100644 main/manager-api/src/main/resources/mapper/agent/AgentTagRelationDao.xml
diff --git a/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java b/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java
index 3f914e4c..59a7dcd3 100644
--- a/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java
+++ b/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java
@@ -245,4 +245,9 @@ public interface ErrorCode {
int DEVICE_NOT_EXIST = 10194; // 设备不存在
int OTA_UPLOAD_COUNT_EXCEED = 10195; // OTA上传次数超过限制
+ // 智能体标签相关错误码
+ int AGENT_TAG_NAME_DUPLICATE = 10196; // 标签名称已存在
+ int AGENT_TAG_NAME_EMPTY = 10197; // 标签名称不能为空
+ int AGENT_TAG_NOT_EXIST = 10198; // 标签不存在
+
}
diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java
index 76669e67..b669ed6b 100644
--- a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java
+++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java
@@ -42,6 +42,9 @@ import xiaozhi.modules.agent.dto.AgentMemoryDTO;
import xiaozhi.modules.agent.dto.AgentUpdateDTO;
import xiaozhi.modules.agent.entity.AgentEntity;
import xiaozhi.modules.agent.entity.AgentTemplateEntity;
+import xiaozhi.modules.agent.dto.AgentTagDTO;
+import xiaozhi.modules.agent.entity.AgentTagEntity;
+import xiaozhi.modules.agent.service.AgentTagService;
import xiaozhi.modules.agent.service.AgentChatAudioService;
import xiaozhi.modules.agent.service.AgentChatHistoryService;
import xiaozhi.modules.agent.service.AgentChatSummaryService;
@@ -69,6 +72,7 @@ public class AgentController {
private final AgentContextProviderService agentContextProviderService;
private final AgentChatSummaryService agentChatSummaryService;
private final RedisUtils redisUtils;
+ private final AgentTagService agentTagService;
@GetMapping("/list")
@Operation(summary = "获取用户智能体列表")
@@ -77,7 +81,7 @@ public class AgentController {
@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "searchType", defaultValue = "name") String searchType) {
UserDetail user = SecurityUser.getUser();
-
+
// 直接调用整合后的getUserAgents方法,无需再区分搜索和普通查询
List agents = agentService.getUserAgents(user.getId(), keyword, searchType);
return new Result>().ok(agents);
@@ -275,6 +279,50 @@ public class AgentController {
.body(audioData);
}
+ @PostMapping("/tag")
+ @Operation(summary = "创建标签")
+ @RequiresPermissions("sys:role:normal")
+ public Result createTag(@RequestBody Map params) {
+ String tagName = params.get("tagName");
+ if (StringUtils.isBlank(tagName)) {
+ return new Result().error("标签名称不能为空");
+ }
+ AgentTagEntity tag = agentTagService.saveTag(tagName);
+ return new Result().ok(tag);
+ }
+ @GetMapping("/tag/list")
+ @Operation(summary = "获取所有标签列表")
+ @RequiresPermissions("sys:role:normal")
+ public Result> getAllTags() {
+ List tags = agentTagService.getAllTags();
+ return new Result>().ok(tags);
+ }
+
+ @DeleteMapping("/tag/{id}")
+ @Operation(summary = "删除标签")
+ @RequiresPermissions("sys:role:normal")
+ public Result deleteTag(@PathVariable String id) {
+ agentTagService.deleteTag(id);
+ return new Result().ok(null);
+ }
+
+ @GetMapping("/{id}/tags")
+ @Operation(summary = "获取智能体的标签")
+ @RequiresPermissions("sys:role:normal")
+ public Result> getAgentTags(@PathVariable String id) {
+ List tags = agentTagService.getTagsByAgentId(id);
+ return new Result>().ok(tags);
+ }
+
+ @PutMapping("/{id}/tags")
+ @Operation(summary = "保存智能体的标签")
+ @RequiresPermissions("sys:role:normal")
+ public Result saveAgentTags(@PathVariable String id, @RequestBody Map params) {
+ List tagIds = (List) params.get("tagIds");
+ List tagNames = (List) params.get("tagNames");
+ agentTagService.saveAgentTags(id, tagIds, tagNames);
+ return new Result().ok(null);
+ }
}
\ No newline at end of file
diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagDao.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagDao.java
new file mode 100644
index 00000000..e50f7c34
--- /dev/null
+++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagDao.java
@@ -0,0 +1,24 @@
+package xiaozhi.modules.agent.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.agent.entity.AgentTagEntity;
+
+import java.util.List;
+
+@Mapper
+public interface AgentTagDao extends BaseDao {
+
+ List selectByAgentId(@Param("agentId") String agentId);
+
+ List selectByAgentIds(@Param("agentIds") List agentIds);
+
+ List selectAll();
+
+ List selectAgentIdsByTagName(@Param("tagName") String tagName);
+
+ List selectByTagNames(@Param("tagNames") List tagNames);
+
+ int batchInsert(@Param("list") List tagList);
+}
diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagRelationDao.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagRelationDao.java
new file mode 100644
index 00000000..7169b5d5
--- /dev/null
+++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagRelationDao.java
@@ -0,0 +1,18 @@
+package xiaozhi.modules.agent.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.agent.entity.AgentTagRelationEntity;
+
+import java.util.List;
+
+@Mapper
+public interface AgentTagRelationDao extends BaseDao {
+
+ int deleteByAgentId(@Param("agentId") String agentId);
+
+ int insertRelation(AgentTagRelationEntity relation);
+
+ int batchInsertRelation(@Param("list") List relations);
+}
diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java
index edad88db..77028f2a 100644
--- a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java
+++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java
@@ -5,6 +5,7 @@ import java.util.List;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import xiaozhi.modules.agent.dto.AgentTagDTO;
/**
* 智能体数据传输对象
@@ -46,4 +47,7 @@ public class AgentDTO {
@Schema(description = "设备数量", example = "10")
private Integer deviceCount;
+
+ @Schema(description = "标签列表")
+ private List tags;
}
\ No newline at end of file
diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentTagDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentTagDTO.java
new file mode 100644
index 00000000..5dfc5f52
--- /dev/null
+++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentTagDTO.java
@@ -0,0 +1,20 @@
+package xiaozhi.modules.agent.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@Schema(description = "智能体标签DTO")
+public class AgentTagDTO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "标签ID")
+ private String id;
+
+ @Schema(description = "标签名称")
+ private String tagName;
+}
diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagEntity.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagEntity.java
new file mode 100644
index 00000000..82555c3e
--- /dev/null
+++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagEntity.java
@@ -0,0 +1,41 @@
+package xiaozhi.modules.agent.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@TableName("ai_agent_tag")
+@Schema(description = "智能体标签")
+public class AgentTagEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "主键")
+ private String id;
+
+ @Schema(description = "标签名称")
+ private String tagName;
+
+ @Schema(description = "排序")
+ private Integer sort;
+
+ @Schema(description = "创建者")
+ private Long creator;
+
+ @Schema(description = "创建时间")
+ private Date createdAt;
+
+ @Schema(description = "更新者")
+ private Long updater;
+
+ @Schema(description = "更新时间")
+ private Date updatedAt;
+
+ @Schema(description = "删除标记")
+ private Integer deleted;
+}
diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagRelationEntity.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagRelationEntity.java
new file mode 100644
index 00000000..e142b727
--- /dev/null
+++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagRelationEntity.java
@@ -0,0 +1,38 @@
+package xiaozhi.modules.agent.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@TableName("ai_agent_tag_relation")
+@Schema(description = "智能体标签关联")
+public class AgentTagRelationEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "主键")
+ private String id;
+
+ @Schema(description = "智能体ID")
+ private String agentId;
+
+ @Schema(description = "标签ID")
+ private String tagId;
+
+ @Schema(description = "创建者")
+ private Long creator;
+
+ @Schema(description = "创建时间")
+ private Date createdAt;
+
+ @Schema(description = "更新者")
+ private Long updater;
+
+ @Schema(description = "更新时间")
+ private Date updatedAt;
+}
diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentTagService.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentTagService.java
new file mode 100644
index 00000000..2ef1694a
--- /dev/null
+++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentTagService.java
@@ -0,0 +1,25 @@
+package xiaozhi.modules.agent.service;
+
+import java.util.List;
+
+import xiaozhi.modules.agent.dto.AgentTagDTO;
+import xiaozhi.modules.agent.entity.AgentTagEntity;
+
+public interface AgentTagService {
+
+ AgentTagEntity saveTag(String tagName);
+
+ void deleteTag(String tagId);
+
+ List getTagsByAgentId(String agentId);
+
+ void saveAgentTags(String agentId, List tagIds, List tagNames);
+
+ void deleteAgentTags(String agentId);
+
+ List getTagsByAgentIds(List agentIds);
+
+ List getAllTags();
+
+ List getAgentIdsByTagName(String tagName);
+}
diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java
index 5485e425..3624a8f9 100644
--- a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java
+++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java
@@ -31,17 +31,21 @@ import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.common.utils.ToolUtil;
import xiaozhi.modules.agent.dao.AgentDao;
+import xiaozhi.modules.agent.dao.AgentTagDao;
import xiaozhi.modules.agent.dto.AgentCreateDTO;
import xiaozhi.modules.agent.dto.AgentDTO;
+import xiaozhi.modules.agent.dto.AgentTagDTO;
import xiaozhi.modules.agent.dto.AgentUpdateDTO;
import xiaozhi.modules.agent.entity.AgentContextProviderEntity;
import xiaozhi.modules.agent.entity.AgentEntity;
import xiaozhi.modules.agent.entity.AgentPluginMapping;
+import xiaozhi.modules.agent.entity.AgentTagEntity;
import xiaozhi.modules.agent.entity.AgentTemplateEntity;
import xiaozhi.modules.agent.service.AgentChatHistoryService;
import xiaozhi.modules.agent.service.AgentContextProviderService;
import xiaozhi.modules.agent.service.AgentPluginMappingService;
import xiaozhi.modules.agent.service.AgentService;
+import xiaozhi.modules.agent.service.AgentTagService;
import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.agent.vo.AgentInfoVO;
import xiaozhi.modules.device.entity.DeviceEntity;
@@ -59,6 +63,7 @@ import xiaozhi.modules.timbre.service.TimbreService;
@AllArgsConstructor
public class AgentServiceImpl extends BaseServiceImpl implements AgentService {
private final AgentDao agentDao;
+ private final AgentTagDao agentTagDao;
private final TimbreService timbreModelService;
private final ModelConfigService modelConfigService;
private final RedisUtils redisUtils;
@@ -68,6 +73,7 @@ public class AgentServiceImpl extends BaseServiceImpl imp
private final AgentTemplateService agentTemplateService;
private final ModelProviderService modelProviderService;
private final AgentContextProviderService agentContextProviderService;
+ private final AgentTagService agentTagService;
@Override
public PageData adminAgentList(Map params) {
@@ -151,8 +157,16 @@ public class AgentServiceImpl extends BaseServiceImpl imp
return new ArrayList<>();
}
} else {
- // 按名称搜索
- queryWrapper.like("agent_name", keyword);
+ // 按名称搜索(默认):同时搜索智能体名称和标签名
+ List tagAgentIds = agentTagService.getAgentIdsByTagName(keyword);
+ if (ToolUtil.isNotEmpty(tagAgentIds)) {
+ queryWrapper.and(wrapper -> wrapper
+ .like("agent_name", keyword)
+ .or()
+ .in("id", tagAgentIds));
+ } else {
+ queryWrapper.like("agent_name", keyword);
+ }
}
}
@@ -193,6 +207,19 @@ public class AgentServiceImpl extends BaseServiceImpl imp
// 获取设备数量
dto.setDeviceCount(getDeviceCountByAgentId(agent.getId()));
+ // 获取标签列表
+ List tags = agentTagDao.selectByAgentId(agent.getId());
+ if (ToolUtil.isNotEmpty(tags)) {
+ dto.setTags(tags.stream().map(this::convertTagToDTO).collect(Collectors.toList()));
+ }
+
+ return dto;
+ }
+
+ private AgentTagDTO convertTagToDTO(AgentTagEntity entity) {
+ AgentTagDTO dto = new AgentTagDTO();
+ dto.setId(entity.getId());
+ dto.setTagName(entity.getTagName());
return dto;
}
diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentTagServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentTagServiceImpl.java
new file mode 100644
index 00000000..f0bf0a2e
--- /dev/null
+++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentTagServiceImpl.java
@@ -0,0 +1,199 @@
+package xiaozhi.modules.agent.service.impl;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+
+import lombok.AllArgsConstructor;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.modules.agent.dao.AgentTagDao;
+import xiaozhi.modules.agent.dao.AgentTagRelationDao;
+import xiaozhi.modules.agent.dto.AgentTagDTO;
+import xiaozhi.modules.agent.entity.AgentTagEntity;
+import xiaozhi.modules.agent.entity.AgentTagRelationEntity;
+import xiaozhi.modules.agent.service.AgentTagService;
+
+@Service
+@AllArgsConstructor
+public class AgentTagServiceImpl extends BaseServiceImpl implements AgentTagService {
+
+ private final AgentTagRelationDao agentTagRelationDao;
+
+ @Override
+ public AgentTagEntity saveTag(String tagName) {
+ if (tagName == null || tagName.trim().isEmpty()) {
+ throw new RenException(ErrorCode.AGENT_TAG_NAME_EMPTY);
+ }
+
+ QueryWrapper wrapper = new QueryWrapper<>();
+ wrapper.eq("tag_name", tagName);
+ wrapper.eq("deleted", 0);
+ AgentTagEntity existTag = baseDao.selectOne(wrapper);
+ if (existTag != null) {
+ return existTag;
+ }
+
+ AgentTagEntity tag = new AgentTagEntity();
+ tag.setId(UUID.randomUUID().toString().replace("-", ""));
+ tag.setTagName(tagName);
+ tag.setSort(0);
+ tag.setCreatedAt(new Date());
+ tag.setUpdatedAt(new Date());
+ tag.setDeleted(0);
+ baseDao.insert(tag);
+ return tag;
+ }
+
+ @Override
+ public void deleteTag(String tagId) {
+ AgentTagEntity tag = baseDao.selectById(tagId);
+ if (tag != null) {
+ tag.setDeleted(1);
+ tag.setUpdatedAt(new Date());
+ baseDao.updateById(tag);
+ }
+ }
+
+ @Override
+ public List getTagsByAgentId(String agentId) {
+ List tags = baseDao.selectByAgentId(agentId);
+ return tags.stream().map(this::convertToDTO).collect(Collectors.toList());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void saveAgentTags(String agentId, List tagIds, List tagNames) {
+ agentTagRelationDao.deleteByAgentId(agentId);
+
+ List currentTags = baseDao.selectByAgentId(agentId);
+ List currentTagNames = currentTags.stream()
+ .map(AgentTagEntity::getTagName)
+ .collect(Collectors.toList());
+
+ List allTagIds = new ArrayList<>();
+ List newTagNames = new ArrayList<>();
+
+ if (tagNames != null && !tagNames.isEmpty()) {
+ Set addedTagNames = new HashSet<>();
+ for (String tagName : tagNames) {
+ if (tagName == null || tagName.trim().isEmpty()) {
+ throw new RenException(ErrorCode.AGENT_TAG_NAME_EMPTY);
+ }
+
+ if (currentTagNames.contains(tagName) || addedTagNames.contains(tagName)) {
+ throw new RenException(ErrorCode.AGENT_TAG_NAME_DUPLICATE);
+ }
+ addedTagNames.add(tagName);
+ newTagNames.add(tagName);
+ }
+ }
+
+ List existTags = new ArrayList<>();
+ if (!newTagNames.isEmpty()) {
+ existTags = baseDao.selectByTagNames(newTagNames);
+ }
+
+ List existTagNames = existTags.stream()
+ .map(AgentTagEntity::getTagName)
+ .collect(Collectors.toList());
+
+ List tagsToInsert = new ArrayList<>();
+ for (String tagName : newTagNames) {
+ if (!existTagNames.contains(tagName)) {
+ AgentTagEntity tag = new AgentTagEntity();
+ tag.setId(UUID.randomUUID().toString().replace("-", ""));
+ tag.setTagName(tagName);
+ tag.setSort(0);
+ tag.setDeleted(0);
+ tag.setCreatedAt(new Date());
+ tag.setUpdatedAt(new Date());
+ tagsToInsert.add(tag);
+ }
+ }
+
+ if (!tagsToInsert.isEmpty()) {
+ baseDao.batchInsert(tagsToInsert);
+ for (AgentTagEntity tag : tagsToInsert) {
+ allTagIds.add(tag.getId());
+ }
+ }
+
+ for (AgentTagEntity existTag : existTags) {
+ if (!allTagIds.contains(existTag.getId())) {
+ allTagIds.add(existTag.getId());
+ }
+ }
+
+ if (tagIds != null && !tagIds.isEmpty()) {
+ List tagIdEntities = baseDao.selectBatchIds(tagIds);
+ for (AgentTagEntity tag : tagIdEntities) {
+ if (tag != null && (currentTagNames.contains(tag.getTagName()) ||
+ newTagNames.contains(tag.getTagName()))) {
+ throw new RenException(ErrorCode.AGENT_TAG_NAME_DUPLICATE);
+ }
+ }
+ allTagIds.addAll(tagIds);
+ }
+
+ if (allTagIds.isEmpty()) {
+ return;
+ }
+
+ List relations = new ArrayList<>();
+ Date now = new Date();
+ for (String tagId : allTagIds) {
+ AgentTagRelationEntity relation = new AgentTagRelationEntity();
+ relation.setId(UUID.randomUUID().toString().replace("-", ""));
+ relation.setAgentId(agentId);
+ relation.setTagId(tagId);
+ relation.setCreatedAt(now);
+ relation.setUpdatedAt(now);
+ relations.add(relation);
+ }
+ agentTagRelationDao.batchInsertRelation(relations);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteAgentTags(String agentId) {
+ agentTagRelationDao.deleteByAgentId(agentId);
+ }
+
+ @Override
+ public List getTagsByAgentIds(List agentIds) {
+ if (agentIds == null || agentIds.isEmpty()) {
+ return List.of();
+ }
+ List tags = baseDao.selectByAgentIds(agentIds);
+ return tags.stream().map(this::convertToDTO).collect(Collectors.toList());
+ }
+
+ @Override
+ public List getAllTags() {
+ List tags = baseDao.selectAll();
+ return tags.stream().map(this::convertToDTO).collect(Collectors.toList());
+ }
+
+ @Override
+ public List getAgentIdsByTagName(String tagName) {
+ return baseDao.selectAgentIdsByTagName(tagName);
+ }
+
+ private AgentTagDTO convertToDTO(AgentTagEntity entity) {
+ AgentTagDTO dto = new AgentTagDTO();
+ dto.setId(entity.getId());
+ dto.setTagName(entity.getTagName());
+ return dto;
+ }
+}
diff --git a/main/manager-api/src/main/resources/db/changelog/202602281000.sql b/main/manager-api/src/main/resources/db/changelog/202602281000.sql
new file mode 100644
index 00000000..5efa9696
--- /dev/null
+++ b/main/manager-api/src/main/resources/db/changelog/202602281000.sql
@@ -0,0 +1,29 @@
+-- 标签表
+CREATE TABLE IF NOT EXISTS ai_agent_tag (
+ id VARCHAR(32) NOT NULL COMMENT '主键',
+ tag_name VARCHAR(64) NOT NULL COMMENT '标签名称',
+ sort INT UNSIGNED DEFAULT 0 COMMENT '排序',
+ creator BIGINT COMMENT '创建者',
+ created_at DATETIME COMMENT '创建时间',
+ updater BIGINT COMMENT '更新者',
+ updated_at DATETIME COMMENT '更新时间',
+ deleted TINYINT DEFAULT 0 COMMENT '删除标记',
+ PRIMARY KEY (id),
+ UNIQUE KEY uk_tag_name (tag_name),
+ INDEX idx_sort (sort)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能体标签表';
+
+-- 智能体标签关联表
+CREATE TABLE IF NOT EXISTS ai_agent_tag_relation (
+ id VARCHAR(32) NOT NULL COMMENT '主键',
+ agent_id VARCHAR(32) NOT NULL COMMENT '智能体ID',
+ tag_id VARCHAR(32) NOT NULL COMMENT '标签ID',
+ creator BIGINT COMMENT '创建者',
+ created_at DATETIME COMMENT '创建时间',
+ updater BIGINT COMMENT '更新者',
+ updated_at DATETIME COMMENT '更新时间',
+ PRIMARY KEY (id),
+ UNIQUE KEY uk_agent_tag (agent_id, tag_id),
+ INDEX idx_agent_id (agent_id),
+ INDEX idx_tag_id (tag_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能体标签关联表';
diff --git a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml
index ded39ce1..9e4811d5 100755
--- a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml
+++ b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml
@@ -529,3 +529,10 @@ databaseChangeLog:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202602051125.sql
+ - changeSet:
+ id: 202602281000
+ author: rainv123
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202602281000.sql
diff --git a/main/manager-api/src/main/resources/i18n/messages.properties b/main/manager-api/src/main/resources/i18n/messages.properties
index 25cd7021..75a3ad4e 100644
--- a/main/manager-api/src/main/resources/i18n/messages.properties
+++ b/main/manager-api/src/main/resources/i18n/messages.properties
@@ -201,4 +201,7 @@
10192=\u9002\u914D\u5668\u7C7B\u578B\u672A\u627E\u5230
10193=\u8BBE\u5907ID\u4E0D\u80FD\u4E3A\u7A7A
10194=\u8BBE\u5907\u4E0D\u5B58\u5728\u6216\u4E0D\u5728\u7EBF
-10195=OTA\u4E0A\u4F20\u6B21\u6570\u8D85\u8FC7\u9650\u5236
\ No newline at end of file
+10195=OTA\u4E0A\u4F20\u6B21\u6570\u8D85\u8FC7\u9650\u5236
+10196=\u6807\u7B7E\u540D\u79F0\u5DF2\u5B58\u5728
+10197=\u6807\u7B7E\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+10198=\u6807\u7B7E\u4E0D\u5B58\u5728
\ No newline at end of file
diff --git a/main/manager-api/src/main/resources/i18n/messages_de_DE.properties b/main/manager-api/src/main/resources/i18n/messages_de_DE.properties
index 0b9ed96d..f6595601 100644
--- a/main/manager-api/src/main/resources/i18n/messages_de_DE.properties
+++ b/main/manager-api/src/main/resources/i18n/messages_de_DE.properties
@@ -201,4 +201,7 @@
10192=Adaptertyp nicht gefunden
10193=Adapter-ID darf nicht leer sein
10194=Adapter nicht gefunden oder nicht online
-10195=OTA-Upload-Anzahl \u00FCberschreitet das Limit
\ No newline at end of file
+10195=OTA-Upload-Anzahl \u00FCberschreitet das Limit
+10196=Tag-Name existiert bereits
+10197=Tag-Name darf nicht leer sein
+10198=Tag nicht gefunden
\ No newline at end of file
diff --git a/main/manager-api/src/main/resources/i18n/messages_en_US.properties b/main/manager-api/src/main/resources/i18n/messages_en_US.properties
index 17b1b961..a0f882c4 100644
--- a/main/manager-api/src/main/resources/i18n/messages_en_US.properties
+++ b/main/manager-api/src/main/resources/i18n/messages_en_US.properties
@@ -201,4 +201,7 @@
10192=Adapter type not found
10193=Device ID cannot be empty
10194=Device not found or not online
-10195=OTA upload times exceed the limit
\ No newline at end of file
+10195=OTA upload times exceed the limit
+10196=Tag name already exists
+10197=Tag name cannot be empty
+10198=Tag not found
\ No newline at end of file
diff --git a/main/manager-api/src/main/resources/i18n/messages_vi_VN.properties b/main/manager-api/src/main/resources/i18n/messages_vi_VN.properties
index 3b80a6dc..547a62b4 100644
--- a/main/manager-api/src/main/resources/i18n/messages_vi_VN.properties
+++ b/main/manager-api/src/main/resources/i18n/messages_vi_VN.properties
@@ -201,4 +201,7 @@
10192=Kh\u00F4ng t\u00ECm th\u1EA5y lo\u1EA1i b\u1ED9 chuy\u1EC3n \u0111\u1ED5i
10193=ID thi\u1EBFt b\u1ECB kh\u00F4ng th\u1EC3 tr\u1ED1ng
10194=Kh\u00F4ng t\u00ECm th\u1EA5y thi\u1EBFt b\u1ECB ho\u1EB7c thi\u1EBFt b\u1ECB kh\u00F4ng tr\u1EF1c tuy\u1EBFn
-10195=S\u1ED1 l\u1EA7n t\u1EA3i l\u00EAn OTA v\u01B0\u1EE3t qu\u00E1 gi\u1EDBi h\u1EA1n
\ No newline at end of file
+10195=S\u1ED1 l\u1EA7n t\u1EA3i l\u00EAn OTA v\u01B0\u1EE3t qu\u00E1 gi\u1EDBi h\u1EA1n
+10196=T\u00EAn th\u1EB9� \u0111\u00E3 t\u1ED3n t\u1EA1i
+10197=T\u00EAn th\u1EB9� kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10198=Kh\u00F4ng t\u00ECm th\u1EA5y th\u1EB9�
\ No newline at end of file
diff --git a/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties b/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties
index f6913eea..864be596 100644
--- a/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties
+++ b/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties
@@ -201,4 +201,7 @@
10192=\u9002\u914D\u5668\u985E\u578B\u672A\u627E\u5230
10193=\u8A2D\u5099ID\u4E0D\u80FD\u4E3A\u7A7A
10194=\u8A2D\u5099\u4E0D\u5B58\u5728\u6216\u672A\u5728\u7DDA
-10195=OTA\u4E0A\u4F20\u6B21\u6578\u8D85\u904E\u9650\u5236
\ No newline at end of file
+10195=OTA\u4E0A\u4F20\u6B21\u6578\u8D85\u904E\u9650\u5236
+10196=\u6A19\u7C64\u540D\u7A31\u5DF2\u5B58\u5728
+10197=\u6A19\u7C64\u540D\u7A31\u4E0D\u80FD\u4E3A\u7A7A
+10198=\u6A19\u7C64\u4E0D\u5B58\u5728
\ No newline at end of file
diff --git a/main/manager-api/src/main/resources/mapper/agent/AgentTagDao.xml b/main/manager-api/src/main/resources/mapper/agent/AgentTagDao.xml
new file mode 100644
index 00000000..b3d8c6c8
--- /dev/null
+++ b/main/manager-api/src/main/resources/mapper/agent/AgentTagDao.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO ai_agent_tag (id, tag_name, sort, deleted, creator, created_at, updater, updated_at)
+ VALUES
+
+ (#{item.id}, #{item.tagName}, #{item.sort}, #{item.deleted}, #{item.creator}, #{item.createdAt}, #{item.updater}, #{item.updatedAt})
+
+
+
+
diff --git a/main/manager-api/src/main/resources/mapper/agent/AgentTagRelationDao.xml b/main/manager-api/src/main/resources/mapper/agent/AgentTagRelationDao.xml
new file mode 100644
index 00000000..4569f616
--- /dev/null
+++ b/main/manager-api/src/main/resources/mapper/agent/AgentTagRelationDao.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+ DELETE FROM ai_agent_tag_relation WHERE agent_id = #{agentId}
+
+
+
+ INSERT INTO ai_agent_tag_relation (id, agent_id, tag_id, creator, created_at, updater, updated_at)
+ VALUES (#{id}, #{agentId}, #{tagId}, #{creator}, #{createdAt}, #{updater}, #{updatedAt})
+
+
+
+ INSERT INTO ai_agent_tag_relation (id, agent_id, tag_id, creator, created_at, updater, updated_at)
+ VALUES
+
+ (#{item.id}, #{item.agentId}, #{item.tagId}, #{item.creator}, #{item.createdAt}, #{item.updater}, #{item.updatedAt})
+
+
+
+