diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/UserAgentController.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/UserAgentController.java index 3e5a9c57..20246e62 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/UserAgentController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/UserAgentController.java @@ -95,6 +95,17 @@ public class UserAgentController extends BaseController { return new Result().ok(null); } + @GetMapping("/{agentId}") + @Operation(summary = "获取智能体信息") + @RequiresPermissions("sys:role:normal") + public Result getAgent(@PathVariable String agentId) { + if (StringUtils.isBlank(agentId)) { + log.error("智能体ID不能为空"); + } + Agent agent = agentService.getById(agentId); + return new Result().ok(agent); + } + @GetMapping @Operation(summary = "智能体列表") @RequiresPermissions("sys:role:normal") diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/UserAgentTemplateController.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/UserAgentTemplateController.java new file mode 100644 index 00000000..d410adb7 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/UserAgentTemplateController.java @@ -0,0 +1,134 @@ +package xiaozhi.modules.agentTemplate.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; +import xiaozhi.common.controller.BaseController; +import xiaozhi.common.user.UserDetail; +import xiaozhi.common.utils.Result; +import xiaozhi.modules.agent.domain.AgentTemplate; +import xiaozhi.modules.agent.service.AgentTemplateService; +import xiaozhi.modules.agent.vo.AgentTemplateVO; +import xiaozhi.modules.device.domain.Device; +import xiaozhi.modules.device.service.DeviceService; +import xiaozhi.modules.model.domain.ModelConfig; +import xiaozhi.modules.model.domain.TtsVoice; +import xiaozhi.modules.model.service.ModelConfigService; +import xiaozhi.modules.model.service.TtsVoiceService; +import xiaozhi.modules.security.user.SecurityUser; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +@Slf4j +@Tag(name = "智能体模板管理") +@AllArgsConstructor +@RestController +@RequestMapping("/user/agent/template") +public class UserAgentTemplateController extends BaseController { + private final AgentTemplateService agentTemplateService; + private final ModelConfigService modelConfigService; + private final TtsVoiceService ttsVoiceService; + private final DeviceService deviceService; + + @PostMapping + @Operation(summary = "添加智能体模板") + @RequiresPermissions("sys:role:normal") + public Result addTemplate(@RequestBody AgentTemplate agentTemplate) { + UserDetail user = SecurityUser.getUser(); + if (StringUtils.isBlank(agentTemplate.getAgentName())) { + log.error("智能体模板名称不能为空"); + } + try { + agentTemplate = new AgentTemplate(); + agentTemplate.setId(UUID.randomUUID().toString().replace("-", "")); + agentTemplate.setAgentName(agentTemplate.getAgentName()); + agentTemplate.setCreator(user.getId()); + agentTemplate.setCreatedAt(new Date()); + agentTemplateService.save(agentTemplate); + } catch (Exception e) { + log.error("对象赋值异常", e); + } + return new Result().ok(agentTemplate); + } + + @DeleteMapping("/{templateId}") + @Operation(summary = "删除智能体模板") + @RequiresPermissions("sys:role:normal") + public Result delTemplate(@PathVariable String templateId) { + UserDetail user = SecurityUser.getUser(); + if (StringUtils.isBlank(templateId)) { + log.error("智能体模板ID不能为空"); + } + boolean bool = agentTemplateService.removeById(templateId); + if (!bool) { + return new Result().error("删除失败"); + } + return new Result().ok(null); + } + + @GetMapping + @Operation(summary = "智能体模板模板列表") + @RequiresPermissions("sys:role:normal") + public Result> templateList() { + List list = agentTemplateService.list(); + return new Result>().ok(list); + } + + @GetMapping("/vo") + @Operation(summary = "智能体模板模板列表") + @RequiresPermissions("sys:role:normal") + public Result> templateVOList() { + UserDetail user = SecurityUser.getUser(); + List templates = agentTemplateService.list(); + List list = new ArrayList<>(); + this.convertAgentTemplateVOList(list, templates); + return new Result>().ok(list); + } + + /** + * 将Agent对象列表转换为AgentTemplateVO对象列表 + * 此方法遍历Agent对象列表,将每个Agent对象转换为AgentTemplateVO对象,并添加到AgentTemplateVO列表中 + * + * @param agentVOList 转换后的AgentTemplateVO对象列表 + * @param agentList 原始的Agent对象列表 + */ + private void convertAgentTemplateVOList(List agentVOList, List agentList) { + // 遍历Agent对象列表 + for (AgentTemplate agentTemplate : agentList) { + // 创建一个新的AgentTemplateVO对象 + AgentTemplateVO agentTemplateVO = new AgentTemplateVO(); + // 将当前Agent对象的属性值转换并设置到AgentTemplateVO对象中 + this.convertAgentTemplateVO(agentTemplateVO, agentTemplate); + // 将转换后的AgentTemplateVO对象添加到AgentTemplateVO列表中 + agentVOList.add(agentTemplateVO); + } + } + + private void convertAgentTemplateVO(AgentTemplateVO agentTemplateVO, AgentTemplate agentTemplate) { + try { + BeanUtils.copyProperties(agentTemplateVO, agentTemplate); + agentTemplateVO.setTtsModelName("未知"); + TtsVoice ttsVoice = ttsVoiceService.getOne(new QueryWrapper().eq("id", agentTemplate.getTtsVoiceId())); + if (ObjectUtils.isNotNull(ttsVoice)) { + agentTemplateVO.setTtsModelName(ttsVoice.getName()); + } + agentTemplateVO.setLlmModelName("未知"); + ModelConfig modelConfig = modelConfigService.getOne(new QueryWrapper().eq("id", agentTemplate.getLlmModelId())); + if (ObjectUtils.isNotNull(modelConfig)) { + agentTemplateVO.setLlmModelName(modelConfig.getModelName()); + } + } catch (Exception e) { + log.error("[convertAgentTemplateVO]对象转换报错", e); + } + } +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentTemplateVO.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentTemplateVO.java new file mode 100644 index 00000000..9cd7d79d --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentTemplateVO.java @@ -0,0 +1,14 @@ +package xiaozhi.modules.agent.vo; + +import lombok.Data; +import xiaozhi.modules.agent.domain.Agent; +import xiaozhi.modules.agent.domain.AgentTemplate; + +@Data +public class AgentTemplateVO extends AgentTemplate { + //角色音色 + private String ttsModelName; + + //角色模型 + private String llmModelName; +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java b/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java new file mode 100644 index 00000000..79e84074 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java @@ -0,0 +1,146 @@ +package xiaozhi.modules.model.controller; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.*; +import xiaozhi.common.controller.BaseController; +import xiaozhi.common.redis.RedisUtils; +import xiaozhi.common.user.UserDetail; +import xiaozhi.common.utils.Result; +import xiaozhi.modules.device.constant.DeviceConstant; +import xiaozhi.modules.device.domain.Device; +import xiaozhi.modules.device.service.DeviceService; +import xiaozhi.modules.device.utils.CodeGeneratorUtil; +import xiaozhi.modules.device.vo.DeviceOtaVO; +import xiaozhi.modules.ota.domain.Ota; +import xiaozhi.modules.ota.service.OtaService; +import xiaozhi.modules.security.user.SecurityUser; + +import java.util.Date; + +@Slf4j +@Tag(name = "模型数据管理") +@AllArgsConstructor +@RestController +@RequestMapping("/model/") +public class ModelController extends BaseController { + private final DeviceService deviceService; + private final OtaService otaService; + @Resource + private RedisUtils redisUtils; + + @CrossOrigin(originPatterns = "*", methods = {RequestMethod.GET, RequestMethod.POST}) + @RequestMapping(path = "/", method = {RequestMethod.POST, RequestMethod.GET}, produces = "application/json") + @Operation(summary = "设备OTA升级") + public String ota(@RequestHeader(required = false) HttpHeaders headers, @RequestBody(required = false) JSONObject jsonObject) { + log.info("OTA升级请求:header:{},body:{}", headers, jsonObject); + if (ObjectUtils.isNull(headers) || StringUtils.isBlank(headers.getFirst("device-id"))) { + log.error("设备ID不能为空"); + return "{\"error\": \"Device ID is required\"}"; + } + + DeviceOtaVO otaVO = new DeviceOtaVO(); + Device device = deviceService.getOne(new UpdateWrapper().eq("mac_address", headers.getFirst("device-id").toUpperCase()).eq("id", headers.getFirst("client-Id").replace("-", ""))); + if (ObjectUtils.isNull(device)) { + // 从 Redis 中获取设备信息 + Object redisValue = redisUtils.hGet(DeviceConstant.REDIS_KEY_PREFIX_DEVICE_ACTIVATION_MAC, headers.getFirst("device-id").toUpperCase()); + if (ObjectUtils.isNull(redisValue)) { + String code = CodeGeneratorUtil.generateCode(6); + log.info("[gen]授权码已广播:{}", code); + otaVO.setActivation(new DeviceOtaVO.Activation(code, "youxlife.com\n" + code)); + redisUtils.hSet(DeviceConstant.REDIS_KEY_PREFIX_DEVICE_ACTIVATION_MAC, headers.getFirst("device-id").toUpperCase(), code, RedisUtils.HOUR_ONE_EXPIRE); + redisUtils.hSet(DeviceConstant.REDIS_KEY_PREFIX_DEVICE_ACTIVATION_CODE, code, jsonObject, RedisUtils.HOUR_ONE_EXPIRE); + } else { + log.warn("[get]授权码已广播:{}", redisValue); + otaVO.setActivation(new DeviceOtaVO.Activation(redisValue.toString(), "youxlife.com\n" + redisValue)); + } + } + + + if (ObjectUtils.isNull(device) || device.getAutoUpdate() == 1) { + //{"version":"1.0.0","url":"http://https://youxlife.oss-cn-zhangjiakou.aliyuncs.com/v1.4.5.bin"} + String board = ObjectUtils.isNull(device)?headers.getFirst("user-agent").split("/")[0]:device.getBoard(); + Ota ota = otaService.getOne(new UpdateWrapper().eq("board", board).eq("is_enabled", 1)); + if (ObjectUtils.isNull(ota)) { + log.warn("OTA升级信息未配置"); + } else { + otaVO.setFirmware(new DeviceOtaVO.Firmware(ota.getAppVersion(), ota.getUrl())); + } + } + + otaVO.setServer_time(new DeviceOtaVO.ServerTime(new Date().getTime(), 8 * 60)); + return JSONUtil.toJsonStr(otaVO); + } + + @GetMapping("/sys/getOtalist") + @Operation(summary = "设备OTA列表") + @RequiresPermissions("sys:role:superAdmin") + public Result> getOtalist(@RequestParam Integer pageNo, @RequestParam Integer pageSize) { + UserDetail user = SecurityUser.getUser(); + Page page = new Page(pageNo, pageSize); + page = otaService.page(page); + return new Result>().ok(page); + } + + @PostMapping("/sys/save") + @Operation(summary = "添加设备OTA") + @RequiresPermissions("sys:role:superAdmin") + public Result save(@RequestBody Ota ota) { + UserDetail user = SecurityUser.getUser(); + ota.setCreator(user.getId()); + ota.setCreateDate(new Date()); + boolean bool = otaService.save(ota); + if (!bool) { + return new Result().error("设备OTA添加失败"); + } + return new Result().ok(null); + } + + @PostMapping("/sys/update") + @Operation(summary = "更新设备OTA") + @RequiresPermissions("sys:role:superAdmin") + public Result update(@RequestBody Ota ota) { + UserDetail user = SecurityUser.getUser(); + boolean bool = otaService.update( + new UpdateWrapper().eq("id", ota.getId()) + .set("board", ota.getBoard().trim().toLowerCase()) + .set("app_version", ota.getAppVersion()) + .set("url", ota.getUrl()) + .set("is_enabled", ota.getIsEnabled()) + .set("updater", user.getId()) + .set("update_date", new Date()) + ); + if (!bool) { + return new Result().error("设备OTA更新失败"); + } + return new Result().ok(null); + } + + @PostMapping("/sys/toggleEnabled") + @Operation(summary = "切换设备OTA可用状态") + @RequiresPermissions("sys:role:superAdmin") + public Result toggleEnabled(@RequestBody Ota ota) { + UserDetail user = SecurityUser.getUser(); + boolean bool = otaService.update( + new UpdateWrapper().eq("id", ota.getId()) + .set("is_enabled", ota.getIsEnabled()) + .set("updater", user.getId()) + .set("update_date", new Date()) + ); + if (!bool) { + return new Result().error("切换设备OTA可用状态失败"); + } + return new Result().ok(null); + } +} \ No newline at end of file diff --git a/main/manager-web/src/apis/module/agent.js b/main/manager-web/src/apis/module/agent.js index de6e9324..2cac9840 100755 --- a/main/manager-web/src/apis/module/agent.js +++ b/main/manager-web/src/apis/module/agent.js @@ -78,4 +78,16 @@ export default { // }); }).send(); }, + //智能体模板列表 + getAgentTemplateList(callback) { + RequestService.sendRequest().url(`${getServiceUrl()}/user/agent/template`) + .method('GET') + .success((res) => { + RequestService.clearRequestTime() + callback(res) + }) + .fail((err) => { + console.error('获取智能体模板列表失败:', err); + }).send() + }, } diff --git a/main/manager-web/src/components/AgentItem.vue b/main/manager-web/src/components/AgentItem.vue index eb0b852d..ca55962b 100644 --- a/main/manager-web/src/components/AgentItem.vue +++ b/main/manager-web/src/components/AgentItem.vue @@ -20,7 +20,7 @@ 最近对话:{{ agent.lastConnectedAt }}
-
+
配置角色
diff --git a/main/manager-web/src/views/home.vue b/main/manager-web/src/views/home.vue index 8ac43402..e29bb074 100644 --- a/main/manager-web/src/views/home.vue +++ b/main/manager-web/src/views/home.vue @@ -87,9 +87,9 @@ export default { showAddDialog() { this.addAgentDialogVisible = true }, - goToRoleConfig(agentId, agentName) { + goToRoleConfig(agentId) { // 点击配置角色后跳转到角色配置页 - this.$router.push({path:'/role-config', query: {agentId: agentId, agentName: agentName}}) + this.$router.push({path:'/role-config', query: {agentId: agentId}}) }, goToDevice(agentId) { diff --git a/main/manager-web/src/views/roleConfig.vue b/main/manager-web/src/views/roleConfig.vue index 72fa8c0d..31489d92 100644 --- a/main/manager-web/src/views/roleConfig.vue +++ b/main/manager-web/src/views/roleConfig.vue @@ -18,25 +18,25 @@ style="width: 37px;height: 37px;background: #5778ff;border-radius: 50%;display: flex;align-items: center;justify-content: center;">
- {{ agentName }} ({{ agentId }}) + {{ form.agentName }} ({{ agentId }})
-
- {{ template }} +
+ {{ template.agentName }}
- +
- + @@ -49,10 +49,10 @@
- + - +
当前记忆(每次对话后重新生成)
@@ -95,6 +95,7 @@