mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-28 10:03:54 +08:00
Merge pull request #572 from dreamchen/feature_manager-web&api_agent&device&roleConfig&ota&syncConfig
Feature manager web&api agent&device&role config&ota&sync config
This commit is contained in:
@@ -11,6 +11,7 @@ manager-api 该项目基于SpringBoot框架开发。
|
|||||||
JDK 21
|
JDK 21
|
||||||
Maven 3.8+
|
Maven 3.8+
|
||||||
MySQL 8.0+
|
MySQL 8.0+
|
||||||
|
Redis 5.0+
|
||||||
Vue 3.x
|
Vue 3.x
|
||||||
|
|
||||||
# 创建数据库
|
# 创建数据库
|
||||||
@@ -43,6 +44,37 @@ spring:
|
|||||||
password: 123456
|
password: 123456
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
# 连接Redis
|
||||||
|
|
||||||
|
如果还没有Redis,你可以通过docker安装redis
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run --name xiaozhi-esp32-server-redis -d -p 6379:6379 redis
|
||||||
|
```
|
||||||
|
|
||||||
|
# 确认项目Redis连接信息
|
||||||
|
|
||||||
|
在`src/main/resources/application-dev.yml`中配置Redis连接信息
|
||||||
|
|
||||||
|
```
|
||||||
|
spring:
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: localhost
|
||||||
|
port: 6379
|
||||||
|
password:
|
||||||
|
database: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
在`src/main/resources/application.yml`中配置Redis启用状态
|
||||||
|
|
||||||
|
```
|
||||||
|
renren:
|
||||||
|
redis:
|
||||||
|
open: true
|
||||||
|
```
|
||||||
|
|
||||||
# 测试启动
|
# 测试启动
|
||||||
|
|
||||||
本项目为SpringBoot项目,启动方式为:
|
本项目为SpringBoot项目,启动方式为:
|
||||||
|
|||||||
@@ -168,6 +168,12 @@
|
|||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- SLF4J + Log4j2 实现(可选) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-slf4j-impl</artifactId>
|
||||||
|
<version>2.20.0</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<!-- 阿里云maven仓库 -->
|
<!-- 阿里云maven仓库 -->
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package xiaozhi.common.controller;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class BaseController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected HttpServletRequest request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求头
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected HashMap<String, String> getRequestHeaders() {
|
||||||
|
HashMap<String, String> requestHeaders = new HashMap<String, String>();
|
||||||
|
Enumeration<String> headerNames = request.getHeaderNames();
|
||||||
|
while (headerNames.hasMoreElements()) {
|
||||||
|
String headerName = headerNames.nextElement();
|
||||||
|
String headerValue = request.getHeader(headerName);
|
||||||
|
requestHeaders.put(headerName, headerValue);
|
||||||
|
}
|
||||||
|
return requestHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求参数
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected HashMap<String, String> getRequestParams() {
|
||||||
|
HashMap<String, String> requestParams = new HashMap<String, String>();
|
||||||
|
Enumeration<String> paramNames = request.getParameterNames();
|
||||||
|
while (paramNames.hasMoreElements()) {
|
||||||
|
String paramName = paramNames.nextElement();
|
||||||
|
String paramValue = request.getParameter(paramName);
|
||||||
|
requestParams.put(paramName, paramValue);
|
||||||
|
}
|
||||||
|
return requestParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+257
-119
@@ -1,149 +1,287 @@
|
|||||||
package xiaozhi.modules.agent.controller;
|
package xiaozhi.modules.agent.controller;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
|
||||||
import io.swagger.v3.oas.annotations.Parameters;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import lombok.AllArgsConstructor;
|
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.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import xiaozhi.common.constant.Constant;
|
import xiaozhi.common.controller.BaseController;
|
||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
import xiaozhi.common.user.UserDetail;
|
import xiaozhi.common.user.UserDetail;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
|
||||||
import xiaozhi.common.utils.Result;
|
import xiaozhi.common.utils.Result;
|
||||||
import xiaozhi.modules.agent.dto.AgentCreateDTO;
|
import xiaozhi.modules.agent.domain.Agent;
|
||||||
import xiaozhi.modules.agent.dto.AgentUpdateDTO;
|
import xiaozhi.modules.agent.domain.AgentTemplate;
|
||||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
|
||||||
import xiaozhi.modules.agent.service.AgentService;
|
import xiaozhi.modules.agent.service.AgentService;
|
||||||
|
import xiaozhi.modules.agent.service.AgentTemplateService;
|
||||||
|
import xiaozhi.modules.agent.vo.AgentVO;
|
||||||
|
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 xiaozhi.modules.security.user.SecurityUser;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Tag(name = "智能体管理")
|
@Tag(name = "智能体管理")
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/agent")
|
@RequestMapping("/user/agent")
|
||||||
public class AgentController {
|
public class AgentController extends BaseController {
|
||||||
private final AgentService agentService;
|
private final AgentService agentService;
|
||||||
|
private final AgentTemplateService agentTemplateService;
|
||||||
@GetMapping("/list")
|
private final ModelConfigService modelConfigService;
|
||||||
@Operation(summary = "获取用户智能体列表")
|
private final TtsVoiceService ttsVoiceService;
|
||||||
@RequiresPermissions("sys:role:normal")
|
private final DeviceService deviceService;
|
||||||
public Result<List<AgentEntity>> getUserAgents() {
|
private final RedisUtils redisUtils;
|
||||||
UserDetail user = SecurityUser.getUser();
|
|
||||||
List<AgentEntity> agents = agentService.getUserAgents(user.getId());
|
|
||||||
return new Result<List<AgentEntity>>().ok(agents);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/all")
|
|
||||||
@Operation(summary = "智能体列表(管理员)")
|
|
||||||
@RequiresPermissions("sys:role:superAdmin")
|
|
||||||
@Parameters({
|
|
||||||
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true),
|
|
||||||
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true),
|
|
||||||
})
|
|
||||||
public Result<PageData<AgentEntity>> adminAgentList(
|
|
||||||
@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
|
||||||
PageData<AgentEntity> page = agentService.adminAgentList(params);
|
|
||||||
return new Result<PageData<AgentEntity>>().ok(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
|
||||||
@Operation(summary = "获取智能体详情")
|
|
||||||
@RequiresPermissions("sys:role:normal")
|
|
||||||
public Result<AgentEntity> getAgentById(@PathVariable("id") String id) {
|
|
||||||
AgentEntity agent = agentService.getAgentById(id);
|
|
||||||
return new Result<AgentEntity>().ok(agent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@Operation(summary = "创建智能体")
|
@Operation(summary = "添加智能体")
|
||||||
@RequiresPermissions("sys:role:normal")
|
@RequiresPermissions("sys:role:normal")
|
||||||
public Result<Void> save(@RequestBody @Valid AgentCreateDTO dto) {
|
public Result<Agent> addAgent(@RequestBody Agent agent) {
|
||||||
AgentEntity entity = ConvertUtils.sourceToTarget(dto, AgentEntity.class);
|
|
||||||
|
|
||||||
// 设置用户ID和创建者信息
|
|
||||||
UserDetail user = SecurityUser.getUser();
|
UserDetail user = SecurityUser.getUser();
|
||||||
entity.setUserId(user.getId());
|
if (StringUtils.isBlank(agent.getAgentName())) {
|
||||||
entity.setCreator(user.getId());
|
log.error("智能体名称不能为空");
|
||||||
entity.setCreatedAt(new Date());
|
}
|
||||||
|
Agent oldAgent = agentService.getOne(new QueryWrapper<Agent>().eq("agent_name", agent.getAgentName()));
|
||||||
// ID、智能体编码和排序会在Service层自动生成
|
if (ObjectUtils.isNull(oldAgent)) {
|
||||||
agentService.insert(entity);
|
AgentTemplate agentTemplate = agentTemplateService.getOne(new QueryWrapper<AgentTemplate>().eq("is_default", 1));
|
||||||
|
if (ObjectUtils.isNull(agentTemplate)) {
|
||||||
return new Result<>();
|
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
oldAgent = new Agent();
|
||||||
|
BeanUtils.copyProperties(oldAgent, agentTemplate);
|
||||||
|
oldAgent.setId(UUID.randomUUID().toString().replace("-", ""));
|
||||||
|
oldAgent.setAgentName(agent.getAgentName());
|
||||||
|
oldAgent.setUserId(user.getId());
|
||||||
|
oldAgent.setCreator(user.getId());
|
||||||
|
oldAgent.setCreatedAt(new Date());
|
||||||
|
agentService.save(oldAgent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("对象赋值异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
return new Result<Agent>().ok(agent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping
|
@PutMapping("{agentId}")
|
||||||
@Operation(summary = "更新智能体")
|
@Operation(summary = "更新智能体")
|
||||||
@RequiresPermissions("sys:role:normal")
|
@RequiresPermissions("sys:role:normal")
|
||||||
public Result<Void> update(@RequestBody @Valid AgentUpdateDTO dto) {
|
public Result<Agent> updateAgent(@PathVariable String agentId, @RequestBody Agent agent) {
|
||||||
// 先查询现有实体
|
|
||||||
AgentEntity existingEntity = agentService.getAgentById(dto.getId());
|
|
||||||
if (existingEntity == null) {
|
|
||||||
return new Result<Void>().error("智能体不存在");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 只更新提供的非空字段
|
|
||||||
if (dto.getAgentName() != null) {
|
|
||||||
existingEntity.setAgentName(dto.getAgentName());
|
|
||||||
}
|
|
||||||
if (dto.getAgentCode() != null) {
|
|
||||||
existingEntity.setAgentCode(dto.getAgentCode());
|
|
||||||
}
|
|
||||||
if (dto.getAsrModelId() != null) {
|
|
||||||
existingEntity.setAsrModelId(dto.getAsrModelId());
|
|
||||||
}
|
|
||||||
if (dto.getVadModelId() != null) {
|
|
||||||
existingEntity.setVadModelId(dto.getVadModelId());
|
|
||||||
}
|
|
||||||
if (dto.getLlmModelId() != null) {
|
|
||||||
existingEntity.setLlmModelId(dto.getLlmModelId());
|
|
||||||
}
|
|
||||||
if (dto.getTtsModelId() != null) {
|
|
||||||
existingEntity.setTtsModelId(dto.getTtsModelId());
|
|
||||||
}
|
|
||||||
if (dto.getTtsVoiceId() != null) {
|
|
||||||
existingEntity.setTtsVoiceId(dto.getTtsVoiceId());
|
|
||||||
}
|
|
||||||
if (dto.getMemModelId() != null) {
|
|
||||||
existingEntity.setMemModelId(dto.getMemModelId());
|
|
||||||
}
|
|
||||||
if (dto.getIntentModelId() != null) {
|
|
||||||
existingEntity.setIntentModelId(dto.getIntentModelId());
|
|
||||||
}
|
|
||||||
if (dto.getSystemPrompt() != null) {
|
|
||||||
existingEntity.setSystemPrompt(dto.getSystemPrompt());
|
|
||||||
}
|
|
||||||
if (dto.getLangCode() != null) {
|
|
||||||
existingEntity.setLangCode(dto.getLangCode());
|
|
||||||
}
|
|
||||||
if (dto.getLanguage() != null) {
|
|
||||||
existingEntity.setLanguage(dto.getLanguage());
|
|
||||||
}
|
|
||||||
if (dto.getSort() != null) {
|
|
||||||
existingEntity.setSort(dto.getSort());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置更新者信息
|
|
||||||
UserDetail user = SecurityUser.getUser();
|
UserDetail user = SecurityUser.getUser();
|
||||||
existingEntity.setUpdater(user.getId());
|
if (StringUtils.isBlank(agentId)) {
|
||||||
existingEntity.setUpdatedAt(new Date());
|
log.error("智能体ID不能为空");
|
||||||
|
return new Result<Agent>().error("更新失败,智能体ID不能为空");
|
||||||
agentService.updateById(existingEntity);
|
}
|
||||||
|
Agent oldAgent = agentService.getById(agentId);
|
||||||
return new Result<>();
|
if (ObjectUtils.isNull(oldAgent)) {
|
||||||
|
log.error("智能体不存在");
|
||||||
|
return new Result<Agent>().error("更新失败,智能体不存在");
|
||||||
|
} else {
|
||||||
|
UpdateWrapper<Agent> updateWrapper = new UpdateWrapper<>();
|
||||||
|
updateWrapper.eq("id", agentId);
|
||||||
|
updateWrapper.set("agent_code", agent.getAgentCode());
|
||||||
|
updateWrapper.set("asr_model_id", agent.getAsrModelId());
|
||||||
|
updateWrapper.set("intent_model_id", agent.getIntentModelId());
|
||||||
|
updateWrapper.set("llm_model_id", agent.getLlmModelId());
|
||||||
|
updateWrapper.set("memory_model_id", agent.getMemoryModelId());
|
||||||
|
updateWrapper.set("system_prompt", agent.getSystemPrompt());
|
||||||
|
updateWrapper.set("tts_voice_id", agent.getTtsVoiceId());
|
||||||
|
updateWrapper.set("tts_model_id", agent.getTtsModelId());
|
||||||
|
updateWrapper.set("vad_model_id", agent.getVadModelId());
|
||||||
|
updateWrapper.set("updater", user.getId());
|
||||||
|
updateWrapper.set("updated_at", new Date());
|
||||||
|
boolean bool = agentService.update(updateWrapper);
|
||||||
|
if (!bool)
|
||||||
|
return new Result<Agent>().error("更新失败");
|
||||||
|
}
|
||||||
|
return new Result<Agent>().ok(agent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{agentId}")
|
||||||
@Operation(summary = "删除智能体")
|
@Operation(summary = "删除智能体")
|
||||||
@RequiresPermissions("sys:role:normal")
|
@RequiresPermissions("sys:role:normal")
|
||||||
public Result<Void> delete(@PathVariable String id) {
|
public Result<Agent> delAgent(@PathVariable String agentId) {
|
||||||
agentService.deleteById(id);
|
UserDetail user = SecurityUser.getUser();
|
||||||
return new Result<>();
|
if (StringUtils.isBlank(agentId)) {
|
||||||
|
log.error("智能体ID不能为空");
|
||||||
|
}
|
||||||
|
boolean bool = agentService.removeById(agentId);
|
||||||
|
if (!bool) {
|
||||||
|
return new Result<Agent>().error("删除失败");
|
||||||
|
}
|
||||||
|
return new Result<Agent>().ok(null);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@GetMapping("/{agentId}")
|
||||||
|
@Operation(summary = "获取智能体信息")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<Agent> getAgent(@PathVariable String agentId) {
|
||||||
|
if (StringUtils.isBlank(agentId)) {
|
||||||
|
log.error("智能体ID不能为空");
|
||||||
|
}
|
||||||
|
Agent agent = agentService.getById(agentId);
|
||||||
|
return new Result<Agent>().ok(agent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@Operation(summary = "智能体列表")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<List<AgentVO>> agentList() {
|
||||||
|
UserDetail user = SecurityUser.getUser();
|
||||||
|
List<Agent> agents = agentService.list(new QueryWrapper<Agent>().eq("user_id", user.getId()));
|
||||||
|
List<AgentVO> list = new ArrayList<>();
|
||||||
|
this.convertAgentVOList(list, agents);
|
||||||
|
return new Result<List<AgentVO>>().ok(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/loadAgentConfig/{deviceId}")
|
||||||
|
@Operation(summary = "下载智能体配置")
|
||||||
|
public Result<JSONObject> loadAgentConfig(@PathVariable String deviceId) {
|
||||||
|
Device device = deviceService.getOne(new QueryWrapper<Device>().eq("mac_address", deviceId.toUpperCase()));
|
||||||
|
if (ObjectUtils.isNull(device)) {
|
||||||
|
return new Result<JSONObject>().error("设备不存在");
|
||||||
|
}
|
||||||
|
Agent agent = agentService.getOne(new QueryWrapper<Agent>().eq("id", device.getAgentId()));
|
||||||
|
if (ObjectUtils.isNull(agent)) {
|
||||||
|
return new Result<JSONObject>().error("智能体不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject jsonObject = new JSONObject();
|
||||||
|
jsonObject.set("prompt", agent.getSystemPrompt().replace("{{assistant_name}}", agent.getAgentCode()));
|
||||||
|
jsonObject.set("owner", String.valueOf(agent.getUserId()));
|
||||||
|
ModelConfig asrModel = modelConfigService.getById(agent.getAsrModelId());
|
||||||
|
jsonObject.set("ASR", JSONUtil.parseObj(asrModel.getConfigJson()));
|
||||||
|
ModelConfig llmModel = modelConfigService.getById(agent.getLlmModelId());
|
||||||
|
jsonObject.set("LLM", JSONUtil.parseObj(llmModel.getConfigJson()));
|
||||||
|
ModelConfig ttsModel = modelConfigService.getById(agent.getTtsModelId());
|
||||||
|
JSONObject ttsJson = JSONUtil.parseObj(ttsModel.getConfigJson());
|
||||||
|
TtsVoice ttsVoice = ttsVoiceService.getById(agent.getTtsVoiceId());
|
||||||
|
ttsJson.getJSONObject(ttsModel.getModelCode()).set("voice", ttsVoice.getTtsVoice());
|
||||||
|
jsonObject.set("TTS", ttsJson);
|
||||||
|
ModelConfig vadModel = modelConfigService.getById(agent.getVadModelId());
|
||||||
|
jsonObject.set("VAD", JSONUtil.parseObj(vadModel.getConfigJson()));
|
||||||
|
ModelConfig intentModel = modelConfigService.getById(agent.getIntentModelId());
|
||||||
|
jsonObject.set("Intent", JSONUtil.parseObj(intentModel.getConfigJson()));
|
||||||
|
ModelConfig memoryModel = modelConfigService.getById(agent.getMemoryModelId());
|
||||||
|
jsonObject.set("Memory", JSONUtil.parseObj(memoryModel.getConfigJson()));
|
||||||
|
JSONObject module = new JSONObject();
|
||||||
|
module.set("ASR", asrModel.getModelCode());
|
||||||
|
module.set("LLM", llmModel.getModelCode());
|
||||||
|
module.set("TTS", ttsModel.getModelCode());
|
||||||
|
module.set("Intent", intentModel.getModelCode());
|
||||||
|
module.set("Memory", memoryModel.getModelCode());
|
||||||
|
module.set("VAD", vadModel.getModelCode());
|
||||||
|
jsonObject.set("selected_module", module);
|
||||||
|
|
||||||
|
return new Result<JSONObject>().ok(jsonObject);
|
||||||
|
|
||||||
|
// String json = "{\n" +
|
||||||
|
// " \"ASR\": {\n" +
|
||||||
|
// " \"FunASR\": {\n" +
|
||||||
|
// " \"model_dir\": \"models/SenseVoiceSmall\",\n" +
|
||||||
|
// " \"output_dir\": \"tmp/\",\n" +
|
||||||
|
// " \"type\": \"fun_local\"\n" +
|
||||||
|
// " }\n" +
|
||||||
|
// " },\n" +
|
||||||
|
// " \"LLM\": {\n" +
|
||||||
|
// " \"ChatGLMLLM\": {\n" +
|
||||||
|
// " \"api_key\": \"0415dad4014847babc3e3f03024c50a3.qH7FgTy5Yawc85fl\",\n" +
|
||||||
|
// " \"model_name\": \"glm-4-flash\",\n" +
|
||||||
|
// " \"type\": \"openai\",\n" +
|
||||||
|
// " \"url\": \"https://open.bigmodel.cn/api/paas/v4/\"\n" +
|
||||||
|
// " }\n" +
|
||||||
|
// " },\n" +
|
||||||
|
// " \"TTS\": {\n" +
|
||||||
|
// " \"DoubaoTTS\": {\n" +
|
||||||
|
// " \"access_token\": \"hrnx22F9WutWBm7YJzE62r_Z1myUmHEL\",\n" +
|
||||||
|
// " \"api_url\": \"https://openspeech.bytedance.com/api/v1/tts\",\n" +
|
||||||
|
// " \"appid\": \"6295576095\",\n" +
|
||||||
|
// " \"authorization\": \"Bearer;\",\n" +
|
||||||
|
// " \"cluster\": \"volcano_tts\",\n" +
|
||||||
|
// " \"output_dir\": \"tmp/\",\n" +
|
||||||
|
// " \"type\": \"doubao\",\n" +
|
||||||
|
// " \"voice\": \"BV034_streaming\"\n" +
|
||||||
|
// " }\n" +
|
||||||
|
// " },\n" +
|
||||||
|
// " \"VAD\": {\n" +
|
||||||
|
// " \"SileroVAD\": {\n" +
|
||||||
|
// " \"min_silence_duration_ms\": 700,\n" +
|
||||||
|
// " \"model_dir\": \"models/snakers4_silero-vad\",\n" +
|
||||||
|
// " \"threshold\": 0.5\n" +
|
||||||
|
// " }\n" +
|
||||||
|
// " },\n" +
|
||||||
|
// " \"auth_code\": \"642365\",\n" +
|
||||||
|
// " \"prompt\": \"你是一个叫小优的女孩,来自优享生活公司的AI智能体,声音好听,习惯简短表达,爱用网络梗。\\n请注意,要像一个人一样说话,请不要回复表情符号、代码、和xml标签。\\n现在我正在和你进行语音聊天,我们开始吧。\\n如果用户希望结束对话,请在最后说“拜拜”或“再见”。\\n\",\n" +
|
||||||
|
// " \"selected_module\": {\n" +
|
||||||
|
// " \"ASR\": \"FunASR\",\n" +
|
||||||
|
// " \"Intent\": \"function_call\",\n" +
|
||||||
|
// " \"LLM\": \"ChatGLMLLM\",\n" +
|
||||||
|
// " \"Memory\": \"mem0ai\",\n" +
|
||||||
|
// " \"TTS\": \"DoubaoTTS\",\n" +
|
||||||
|
// " \"VAD\": \"SileroVAD\"\n" +
|
||||||
|
// " },\n" +
|
||||||
|
// " \"owner\":\"18600806164\"\n" +
|
||||||
|
// "}";
|
||||||
|
//
|
||||||
|
// return new Result<JSONObject>().ok(new JSONObject(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Agent对象列表转换为AgentVO对象列表
|
||||||
|
* 此方法遍历Agent对象列表,将每个Agent对象转换为AgentVO对象,并添加到AgentVO列表中
|
||||||
|
*
|
||||||
|
* @param agentVOList 转换后的AgentVO对象列表
|
||||||
|
* @param agentList 原始的Agent对象列表
|
||||||
|
*/
|
||||||
|
private void convertAgentVOList(List<AgentVO> agentVOList, List<Agent> agentList) {
|
||||||
|
// 遍历Agent对象列表
|
||||||
|
for (Agent agent : agentList) {
|
||||||
|
// 创建一个新的AgentVO对象
|
||||||
|
AgentVO agentVO = new AgentVO();
|
||||||
|
// 将当前Agent对象的属性值转换并设置到AgentVO对象中
|
||||||
|
this.convertAgentVO(agentVO, agent);
|
||||||
|
// 将转换后的AgentVO对象添加到AgentVO列表中
|
||||||
|
agentVOList.add(agentVO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void convertAgentVO(AgentVO agentVO, Agent agent) {
|
||||||
|
try {
|
||||||
|
BeanUtils.copyProperties(agentVO, agent);
|
||||||
|
agentVO.setTtsModelName("未知");
|
||||||
|
TtsVoice ttsVoice = ttsVoiceService.getOne(new QueryWrapper<TtsVoice>().eq("id", agent.getTtsVoiceId()));
|
||||||
|
if (ObjectUtils.isNotNull(ttsVoice)) {
|
||||||
|
agentVO.setTtsModelName(ttsVoice.getName());
|
||||||
|
}
|
||||||
|
agentVO.setLlmModelName("未知");
|
||||||
|
ModelConfig modelConfig = modelConfigService.getOne(new QueryWrapper<ModelConfig>().eq("id", agent.getLlmModelId()));
|
||||||
|
if (ObjectUtils.isNotNull(modelConfig)) {
|
||||||
|
agentVO.setLlmModelName(modelConfig.getModelName());
|
||||||
|
}
|
||||||
|
agentVO.setLastConnectedAt("今天");
|
||||||
|
agentVO.setDeviceCount(0L);
|
||||||
|
long deviceCount = deviceService.count(new QueryWrapper<Device>().eq("agent_id", agent.getId()));
|
||||||
|
agentVO.setDeviceCount(deviceCount);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[convertAgentVO]对象转换报错", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+131
@@ -0,0 +1,131 @@
|
|||||||
|
package xiaozhi.modules.agent.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.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 AgentTemplateController extends BaseController {
|
||||||
|
private final AgentTemplateService agentTemplateService;
|
||||||
|
private final ModelConfigService modelConfigService;
|
||||||
|
private final TtsVoiceService ttsVoiceService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@Operation(summary = "添加智能体模板")
|
||||||
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
|
public Result<AgentTemplate> 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<AgentTemplate>().ok(agentTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{templateId}")
|
||||||
|
@Operation(summary = "删除智能体模板")
|
||||||
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
|
public Result<AgentTemplate> 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<AgentTemplate>().error("删除失败");
|
||||||
|
}
|
||||||
|
return new Result<AgentTemplate>().ok(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@Operation(summary = "智能体模板模板列表")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<List<AgentTemplate>> templateList() {
|
||||||
|
List<AgentTemplate> list = agentTemplateService.list(new QueryWrapper<AgentTemplate>().orderByAsc("sort"));
|
||||||
|
return new Result<List<AgentTemplate>>().ok(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/vo")
|
||||||
|
@Operation(summary = "智能体模板模板列表")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<List<AgentTemplateVO>> templateVOList() {
|
||||||
|
UserDetail user = SecurityUser.getUser();
|
||||||
|
List<AgentTemplate> templates = agentTemplateService.list();
|
||||||
|
List<AgentTemplateVO> list = new ArrayList<>();
|
||||||
|
this.convertAgentTemplateVOList(list, templates);
|
||||||
|
return new Result<List<AgentTemplateVO>>().ok(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Agent对象列表转换为AgentTemplateVO对象列表
|
||||||
|
* 此方法遍历Agent对象列表,将每个Agent对象转换为AgentTemplateVO对象,并添加到AgentTemplateVO列表中
|
||||||
|
*
|
||||||
|
* @param agentVOList 转换后的AgentTemplateVO对象列表
|
||||||
|
* @param agentList 原始的Agent对象列表
|
||||||
|
*/
|
||||||
|
private void convertAgentTemplateVOList(List<AgentTemplateVO> agentVOList, List<AgentTemplate> 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<TtsVoice>().eq("id", agentTemplate.getTtsVoiceId()));
|
||||||
|
if (ObjectUtils.isNotNull(ttsVoice)) {
|
||||||
|
agentTemplateVO.setTtsModelName(ttsVoice.getName());
|
||||||
|
}
|
||||||
|
agentTemplateVO.setLlmModelName("未知");
|
||||||
|
ModelConfig modelConfig = modelConfigService.getOne(new QueryWrapper<ModelConfig>().eq("id", agentTemplate.getLlmModelId()));
|
||||||
|
if (ObjectUtils.isNotNull(modelConfig)) {
|
||||||
|
agentTemplateVO.setLlmModelName(modelConfig.getModelName());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[convertAgentTemplateVO]对象转换报错", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package xiaozhi.modules.agent.dao;
|
|
||||||
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
|
||||||
import xiaozhi.common.dao.BaseDao;
|
|
||||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
|
||||||
|
|
||||||
@Mapper
|
|
||||||
public interface AgentDao extends BaseDao<AgentEntity> {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package xiaozhi.modules.agent.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能体配置表
|
||||||
|
* @TableName ai_agent
|
||||||
|
*/
|
||||||
|
@TableName(value ="ai_agent")
|
||||||
|
@Data
|
||||||
|
public class Agent implements Serializable {
|
||||||
|
/**
|
||||||
|
* 智能体唯一标识
|
||||||
|
*/
|
||||||
|
@TableId
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所属用户 ID
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能体编码
|
||||||
|
*/
|
||||||
|
private String agentCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能体名称
|
||||||
|
*/
|
||||||
|
private String agentName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语音识别模型标识
|
||||||
|
*/
|
||||||
|
private String asrModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语音活动检测标识
|
||||||
|
*/
|
||||||
|
private String vadModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 大语言模型标识
|
||||||
|
*/
|
||||||
|
private String llmModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语音合成模型标识
|
||||||
|
*/
|
||||||
|
private String ttsModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 音色标识
|
||||||
|
*/
|
||||||
|
private String ttsVoiceId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记忆模型标识
|
||||||
|
*/
|
||||||
|
private String memoryModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 意图模型标识
|
||||||
|
*/
|
||||||
|
private String intentModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色设定参数
|
||||||
|
*/
|
||||||
|
private String systemPrompt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语言编码
|
||||||
|
*/
|
||||||
|
private String langCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 交互语种
|
||||||
|
*/
|
||||||
|
private String language;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序权重
|
||||||
|
*/
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者 ID
|
||||||
|
*/
|
||||||
|
private Long creator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createdAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新者 ID
|
||||||
|
*/
|
||||||
|
private Long updater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updatedAt;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package xiaozhi.modules.agent.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能体配置模板表
|
||||||
|
* @TableName ai_agent_template
|
||||||
|
*/
|
||||||
|
@TableName(value ="ai_agent_template")
|
||||||
|
@Data
|
||||||
|
public class AgentTemplate implements Serializable {
|
||||||
|
/**
|
||||||
|
* 智能体唯一标识
|
||||||
|
*/
|
||||||
|
@TableId
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能体编码
|
||||||
|
*/
|
||||||
|
private String agentCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能体名称
|
||||||
|
*/
|
||||||
|
private String agentName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语音识别模型标识
|
||||||
|
*/
|
||||||
|
private String asrModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语音活动检测标识
|
||||||
|
*/
|
||||||
|
private String vadModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 大语言模型标识
|
||||||
|
*/
|
||||||
|
private String llmModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语音合成模型标识
|
||||||
|
*/
|
||||||
|
private String ttsModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 音色标识
|
||||||
|
*/
|
||||||
|
private String ttsVoiceId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记忆模型标识
|
||||||
|
*/
|
||||||
|
private String memoryModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 意图模型标识
|
||||||
|
*/
|
||||||
|
private String intentModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色设定参数
|
||||||
|
*/
|
||||||
|
private String systemPrompt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语言编码
|
||||||
|
*/
|
||||||
|
private String langCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 交互语种
|
||||||
|
*/
|
||||||
|
private String language;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序权重
|
||||||
|
*/
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否默认模板:1:是,0:不是
|
||||||
|
*/
|
||||||
|
private Integer isDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者 ID
|
||||||
|
*/
|
||||||
|
private Long creator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createdAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新者 ID
|
||||||
|
*/
|
||||||
|
private Long updater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updatedAt;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package xiaozhi.modules.agent.dto;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 智能体创建DTO
|
|
||||||
* 专用于新增智能体,不包含id、agentCode和sort字段,这些字段由系统自动生成/设置默认值
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Schema(description = "智能体创建对象")
|
|
||||||
public class AgentCreateDTO implements Serializable {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Schema(description = "智能体名称", example = "客服助手")
|
|
||||||
@NotBlank(message = "智能体名称不能为空")
|
|
||||||
private String agentName;
|
|
||||||
|
|
||||||
@Schema(description = "语音识别模型标识", example = "asr_model_01")
|
|
||||||
private String asrModelId;
|
|
||||||
|
|
||||||
@Schema(description = "语音活动检测标识", example = "vad_model_01")
|
|
||||||
private String vadModelId;
|
|
||||||
|
|
||||||
@Schema(description = "大语言模型标识", example = "llm_model_01")
|
|
||||||
private String llmModelId;
|
|
||||||
|
|
||||||
@Schema(description = "语音合成模型标识", example = "tts_model_01")
|
|
||||||
private String ttsModelId;
|
|
||||||
|
|
||||||
@Schema(description = "音色标识", example = "voice_01")
|
|
||||||
private String ttsVoiceId;
|
|
||||||
|
|
||||||
@Schema(description = "记忆模型标识", example = "mem_model_01")
|
|
||||||
private String memModelId;
|
|
||||||
|
|
||||||
@Schema(description = "意图模型标识", example = "intent_model_01")
|
|
||||||
private String intentModelId;
|
|
||||||
|
|
||||||
@Schema(description = "角色设定参数", example = "你是一个专业的客服助手,负责回答用户问题")
|
|
||||||
private String systemPrompt;
|
|
||||||
|
|
||||||
@Schema(description = "语言编码", example = "zh_CN")
|
|
||||||
private String langCode;
|
|
||||||
|
|
||||||
@Schema(description = "交互语种", example = "中文")
|
|
||||||
private String language;
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
package xiaozhi.modules.agent.dto;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 智能体更新DTO
|
|
||||||
* 专用于更新智能体,id字段是必需的,用于标识要更新的智能体
|
|
||||||
* 其他字段均为非必填,只更新提供的字段
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Schema(description = "智能体更新对象")
|
|
||||||
public class AgentUpdateDTO implements Serializable {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Schema(description = "智能体唯一标识", example = "a1b2c3d4e5f6", required = true)
|
|
||||||
@NotBlank(message = "智能体ID不能为空")
|
|
||||||
private String id;
|
|
||||||
|
|
||||||
@Schema(description = "智能体编码", example = "AGT_1234567890", required = false)
|
|
||||||
private String agentCode;
|
|
||||||
|
|
||||||
@Schema(description = "智能体名称", example = "客服助手", required = false)
|
|
||||||
private String agentName;
|
|
||||||
|
|
||||||
@Schema(description = "语音识别模型标识", example = "asr_model_02", required = false)
|
|
||||||
private String asrModelId;
|
|
||||||
|
|
||||||
@Schema(description = "语音活动检测标识", example = "vad_model_02", required = false)
|
|
||||||
private String vadModelId;
|
|
||||||
|
|
||||||
@Schema(description = "大语言模型标识", example = "llm_model_02", required = false)
|
|
||||||
private String llmModelId;
|
|
||||||
|
|
||||||
@Schema(description = "语音合成模型标识", example = "tts_model_02", required = false)
|
|
||||||
private String ttsModelId;
|
|
||||||
|
|
||||||
@Schema(description = "音色标识", example = "voice_02", required = false)
|
|
||||||
private String ttsVoiceId;
|
|
||||||
|
|
||||||
@Schema(description = "记忆模型标识", example = "mem_model_02", required = false)
|
|
||||||
private String memModelId;
|
|
||||||
|
|
||||||
@Schema(description = "意图模型标识", example = "intent_model_02", required = false)
|
|
||||||
private String intentModelId;
|
|
||||||
|
|
||||||
@Schema(description = "角色设定参数", example = "你是一个专业的客服助手,负责回答用户问题并提供帮助", required = false)
|
|
||||||
private String systemPrompt;
|
|
||||||
|
|
||||||
@Schema(description = "语言编码", example = "zh_CN", required = false)
|
|
||||||
private String langCode;
|
|
||||||
|
|
||||||
@Schema(description = "交互语种", example = "中文", required = false)
|
|
||||||
private String language;
|
|
||||||
|
|
||||||
@Schema(description = "排序", example = "1", required = false)
|
|
||||||
private Integer sort;
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
package xiaozhi.modules.agent.entity;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@TableName("ai_agent")
|
|
||||||
@Schema(description = "智能体信息")
|
|
||||||
public class AgentEntity {
|
|
||||||
@Schema(description = "智能体唯一标识")
|
|
||||||
private String id;
|
|
||||||
|
|
||||||
@Schema(description = "所属用户ID")
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
@Schema(description = "智能体编码")
|
|
||||||
private String agentCode;
|
|
||||||
|
|
||||||
@Schema(description = "智能体名称")
|
|
||||||
private String agentName;
|
|
||||||
|
|
||||||
@Schema(description = "语音识别模型标识")
|
|
||||||
private String asrModelId;
|
|
||||||
|
|
||||||
@Schema(description = "语音活动检测标识")
|
|
||||||
private String vadModelId;
|
|
||||||
|
|
||||||
@Schema(description = "大语言模型标识")
|
|
||||||
private String llmModelId;
|
|
||||||
|
|
||||||
@Schema(description = "语音合成模型标识")
|
|
||||||
private String ttsModelId;
|
|
||||||
|
|
||||||
@Schema(description = "音色标识")
|
|
||||||
private String ttsVoiceId;
|
|
||||||
|
|
||||||
@Schema(description = "记忆模型标识")
|
|
||||||
private String memModelId;
|
|
||||||
|
|
||||||
@Schema(description = "意图模型标识")
|
|
||||||
private String intentModelId;
|
|
||||||
|
|
||||||
@Schema(description = "角色设定参数")
|
|
||||||
private String systemPrompt;
|
|
||||||
|
|
||||||
@Schema(description = "语言编码")
|
|
||||||
private String langCode;
|
|
||||||
|
|
||||||
@Schema(description = "交互语种")
|
|
||||||
private String language;
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package xiaozhi.modules.agent.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import xiaozhi.modules.agent.domain.Agent;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_agent(智能体配置表)】的数据库操作Mapper
|
||||||
|
* @createDate 2025-03-22 11:48:18
|
||||||
|
* @Entity xiaozhi.modules.agent.domain.Agent
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface AgentMapper extends BaseMapper<Agent> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package xiaozhi.modules.agent.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import xiaozhi.modules.agent.domain.AgentTemplate;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_agent_template(智能体配置模板表)】的数据库操作Mapper
|
||||||
|
* @createDate 2025-03-22 11:48:18
|
||||||
|
* @Entity xiaozhi.modules.agent.domain.AgentTemplate
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface AgentTemplateMapper extends BaseMapper<AgentTemplate> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,25 +1,13 @@
|
|||||||
package xiaozhi.modules.agent.service;
|
package xiaozhi.modules.agent.service;
|
||||||
|
|
||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.modules.agent.domain.Agent;
|
||||||
import xiaozhi.common.service.BaseService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
|
||||||
|
/**
|
||||||
import java.util.List;
|
* @author chenerlei
|
||||||
import java.util.Map;
|
* @description 针对表【ai_agent(智能体配置表)】的数据库操作Service
|
||||||
|
* @createDate 2025-03-22 11:48:18
|
||||||
public interface AgentService extends BaseService<AgentEntity> {
|
*/
|
||||||
/**
|
public interface AgentService extends IService<Agent> {
|
||||||
* 根据用户ID获取智能体列表
|
|
||||||
*/
|
}
|
||||||
List<AgentEntity> getUserAgents(Long userId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 管理员获取所有智能体列表(分页)
|
|
||||||
*/
|
|
||||||
PageData<AgentEntity> adminAgentList(Map<String, Object> params);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取智能体详情
|
|
||||||
*/
|
|
||||||
AgentEntity getAgentById(String id);
|
|
||||||
}
|
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package xiaozhi.modules.agent.service;
|
||||||
|
|
||||||
|
import xiaozhi.modules.agent.domain.AgentTemplate;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_agent_template(智能体配置模板表)】的数据库操作Service
|
||||||
|
* @createDate 2025-03-22 11:48:18
|
||||||
|
*/
|
||||||
|
public interface AgentTemplateService extends IService<AgentTemplate> {
|
||||||
|
|
||||||
|
}
|
||||||
+22
-64
@@ -1,64 +1,22 @@
|
|||||||
package xiaozhi.modules.agent.service.impl;
|
package xiaozhi.modules.agent.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import xiaozhi.modules.agent.domain.Agent;
|
||||||
import org.springframework.stereotype.Service;
|
import xiaozhi.modules.agent.service.AgentService;
|
||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.modules.agent.mapper.AgentMapper;
|
||||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
import org.springframework.stereotype.Service;
|
||||||
import xiaozhi.modules.agent.dao.AgentDao;
|
|
||||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
/**
|
||||||
import xiaozhi.modules.agent.service.AgentService;
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_agent(智能体配置表)】的数据库操作Service实现
|
||||||
import java.util.List;
|
* @createDate 2025-03-22 11:48:18
|
||||||
import java.util.Map;
|
*/
|
||||||
import java.util.UUID;
|
@Service
|
||||||
|
public class AgentServiceImpl extends ServiceImpl<AgentMapper, Agent>
|
||||||
@Service
|
implements AgentService{
|
||||||
public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> implements AgentService {
|
|
||||||
private final AgentDao agentDao;
|
}
|
||||||
|
|
||||||
public AgentServiceImpl(AgentDao agentDao) {
|
|
||||||
this.agentDao = agentDao;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<AgentEntity> getUserAgents(Long userId) {
|
|
||||||
QueryWrapper<AgentEntity> wrapper = new QueryWrapper<>();
|
|
||||||
wrapper.eq("user_id", userId);
|
|
||||||
return agentDao.selectList(wrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PageData<AgentEntity> adminAgentList(Map<String, Object> params) {
|
|
||||||
IPage<AgentEntity> page = agentDao.selectPage(
|
|
||||||
getPage(params, "sort", true),
|
|
||||||
new QueryWrapper<>()
|
|
||||||
);
|
|
||||||
return new PageData<>(page.getRecords(), page.getTotal());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AgentEntity getAgentById(String id) {
|
|
||||||
return agentDao.selectById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean insert(AgentEntity entity) {
|
|
||||||
// 如果ID为空,自动生成一个UUID作为ID
|
|
||||||
if (entity.getId() == null || entity.getId().trim().isEmpty()) {
|
|
||||||
entity.setId(UUID.randomUUID().toString().replace("-", ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果智能体编码为空,自动生成一个带前缀的编码
|
|
||||||
if (entity.getAgentCode() == null || entity.getAgentCode().trim().isEmpty()) {
|
|
||||||
entity.setAgentCode("AGT_" + System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果排序字段为空,设置默认值0
|
|
||||||
if (entity.getSort() == null) {
|
|
||||||
entity.setSort(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.insert(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package xiaozhi.modules.agent.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import xiaozhi.modules.agent.domain.AgentTemplate;
|
||||||
|
import xiaozhi.modules.agent.service.AgentTemplateService;
|
||||||
|
import xiaozhi.modules.agent.mapper.AgentTemplateMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_agent_template(智能体配置模板表)】的数据库操作Service实现
|
||||||
|
* @createDate 2025-03-22 11:48:18
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class AgentTemplateServiceImpl extends ServiceImpl<AgentTemplateMapper, AgentTemplate>
|
||||||
|
implements AgentTemplateService{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package xiaozhi.modules.agent.vo;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AgentConfigVO {
|
||||||
|
private JSONObject selected_module;
|
||||||
|
private String prompt;
|
||||||
|
private JSONObject LLM;
|
||||||
|
private JSONObject TTS;
|
||||||
|
private JSONObject ASR;
|
||||||
|
private JSONObject VAD;
|
||||||
|
private JSONObject Memory;
|
||||||
|
private JSONObject Intent;
|
||||||
|
private String owner;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package xiaozhi.modules.agent.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import xiaozhi.modules.agent.domain.Agent;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AgentVO extends Agent {
|
||||||
|
//角色音色
|
||||||
|
private String ttsModelName;
|
||||||
|
|
||||||
|
//角色明星
|
||||||
|
private String llmModelName;
|
||||||
|
|
||||||
|
//最近对话
|
||||||
|
private String lastConnectedAt;
|
||||||
|
|
||||||
|
//设备数量
|
||||||
|
private Long deviceCount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package xiaozhi.modules.device.constant;
|
||||||
|
|
||||||
|
public class DeviceConstant {
|
||||||
|
|
||||||
|
public static final String REDIS_KEY_PREFIX_DEVICE_ACTIVATION_CODE = "device:activation:code";
|
||||||
|
public static final String REDIS_KEY_PREFIX_DEVICE_ACTIVATION_MAC = "device:activation:mac";
|
||||||
|
}
|
||||||
+138
-47
@@ -1,82 +1,173 @@
|
|||||||
package xiaozhi.modules.device.controller;
|
package xiaozhi.modules.device.controller;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
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.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
|
||||||
import io.swagger.v3.oas.annotations.Parameters;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import xiaozhi.common.constant.Constant;
|
import xiaozhi.common.controller.BaseController;
|
||||||
import xiaozhi.common.exception.ErrorCode;
|
|
||||||
import xiaozhi.common.page.PageData;
|
|
||||||
import xiaozhi.common.redis.RedisKeys;
|
|
||||||
import xiaozhi.common.redis.RedisUtils;
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
import xiaozhi.common.user.UserDetail;
|
import xiaozhi.common.user.UserDetail;
|
||||||
import xiaozhi.common.utils.JsonUtils;
|
|
||||||
import xiaozhi.common.utils.Result;
|
import xiaozhi.common.utils.Result;
|
||||||
import xiaozhi.modules.device.dto.DeviceHeaderDTO;
|
import xiaozhi.modules.agent.domain.Agent;
|
||||||
import xiaozhi.modules.device.dto.DeviceUnBindDTO;
|
import xiaozhi.modules.agent.service.AgentService;
|
||||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
import xiaozhi.modules.device.constant.DeviceConstant;
|
||||||
|
import xiaozhi.modules.device.domain.Device;
|
||||||
import xiaozhi.modules.device.service.DeviceService;
|
import xiaozhi.modules.device.service.DeviceService;
|
||||||
|
import xiaozhi.modules.device.vo.DeviceCodeVO;
|
||||||
import xiaozhi.modules.security.user.SecurityUser;
|
import xiaozhi.modules.security.user.SecurityUser;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Tag(name = "设备管理")
|
@Tag(name = "设备管理")
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/device")
|
@RequestMapping("/user/agent/device")
|
||||||
public class DeviceController {
|
public class DeviceController extends BaseController {
|
||||||
|
private final AgentService agentService;
|
||||||
private final DeviceService deviceService;
|
private final DeviceService deviceService;
|
||||||
private final RedisUtils redisUtils;
|
private final RedisUtils redisUtils;
|
||||||
|
|
||||||
|
@GetMapping("/bind/{agentId}")
|
||||||
|
@Operation(summary = "已绑定设备列表")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<Page<Device>> bindDeviceList(@PathVariable String agentId, @RequestParam Integer pageNo, @RequestParam Integer pageSize) {
|
||||||
|
if (StringUtils.isBlank(agentId)) {
|
||||||
|
log.error("智能体ID不能为空");
|
||||||
|
return new Result().error("智能体ID不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/bind/{deviceCode}")
|
UserDetail user = SecurityUser.getUser();
|
||||||
|
Agent agent = agentService.getById(agentId);
|
||||||
|
if (ObjectUtils.isNull(agent)) {
|
||||||
|
log.error("智能体不存在");
|
||||||
|
return new Result().error("智能体不存在");
|
||||||
|
}
|
||||||
|
Page page = new Page<Device>(pageNo, pageSize);
|
||||||
|
page = deviceService.page(page, new QueryWrapper<Device>().eq("user_id", user.getId()).eq("agent_id", agentId));
|
||||||
|
return new Result<Page<Device>>().ok(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/bind/{agentId}")
|
||||||
@Operation(summary = "绑定设备")
|
@Operation(summary = "绑定设备")
|
||||||
@RequiresPermissions("sys:role:normal")
|
@RequiresPermissions("sys:role:normal")
|
||||||
public Result<DeviceEntity> bindDevice(@PathVariable String deviceCode) {
|
public Result<Device> bindDevice(@PathVariable String agentId, @RequestBody DeviceCodeVO deviceCodeVO) {
|
||||||
UserDetail user = SecurityUser.getUser();
|
if (ObjectUtils.isNull(deviceCodeVO) || StringUtils.isBlank(deviceCodeVO.getDeviceCode())) {
|
||||||
|
log.error("授权码不能为空");
|
||||||
String deviceHeaders = (String) redisUtils.get(RedisKeys.getDeviceCaptchaKey(deviceCode));
|
return new Result().error("授权码不能为空");
|
||||||
if (StringUtils.isBlank(deviceHeaders)) {
|
}
|
||||||
return new Result<DeviceEntity>().error(ErrorCode.DEVICE_CAPTCHA_ERROR);
|
if (StringUtils.isBlank(agentId)) {
|
||||||
|
log.error("智能体ID不能为空");
|
||||||
|
return new Result().error("智能体ID不能为空");
|
||||||
}
|
}
|
||||||
DeviceHeaderDTO deviceHeader = JsonUtils.parseObject(deviceHeaders.getBytes(), DeviceHeaderDTO.class);
|
|
||||||
DeviceEntity device = deviceService.bindDevice(user.getId(), deviceHeader);
|
|
||||||
return new Result<DeviceEntity>().ok(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/bind")
|
|
||||||
@Operation(summary = "获取已绑定设备")
|
|
||||||
@RequiresPermissions("sys:role:normal")
|
|
||||||
public Result<List<DeviceEntity>> getUserDevices() {
|
|
||||||
UserDetail user = SecurityUser.getUser();
|
UserDetail user = SecurityUser.getUser();
|
||||||
List<DeviceEntity> devices = deviceService.getUserDevices(user.getId());
|
|
||||||
return new Result<List<DeviceEntity>>().ok(devices);
|
// 从 Redis 中获取设备信息
|
||||||
|
Object redisValue = redisUtils.hGet(DeviceConstant.REDIS_KEY_PREFIX_DEVICE_ACTIVATION_CODE, deviceCodeVO.getDeviceCode());
|
||||||
|
if (ObjectUtils.isNull(redisValue)) {
|
||||||
|
log.error("授权码不存在:{}", deviceCodeVO.getDeviceCode());
|
||||||
|
return new Result().error("授权码不存在");
|
||||||
|
}
|
||||||
|
JSONObject jsonObject = (JSONObject) redisValue;
|
||||||
|
try {
|
||||||
|
redisUtils.hDel(DeviceConstant.REDIS_KEY_PREFIX_DEVICE_ACTIVATION_CODE, deviceCodeVO.getDeviceCode());
|
||||||
|
redisUtils.hDel(DeviceConstant.REDIS_KEY_PREFIX_DEVICE_ACTIVATION_MAC, jsonObject.getStr("mac_address").toUpperCase());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("授权缓存删除失败", e);
|
||||||
|
}
|
||||||
|
JSONObject board = jsonObject.getJSONObject("board");
|
||||||
|
JSONObject application = jsonObject.getJSONObject("application");
|
||||||
|
if (ObjectUtils.isNull(board) || ObjectUtils.isNull(application)) {
|
||||||
|
log.error("设备码绑定信息无效,{}:{}", jsonObject.getStr("mac_address").toUpperCase(), deviceCodeVO.getDeviceCode());
|
||||||
|
return new Result().error("设备码绑定信息无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
Agent agent = agentService.getById(agentId);
|
||||||
|
if (ObjectUtils.isNull(agent)) {
|
||||||
|
log.error("智能体不存在");
|
||||||
|
return new Result().error("智能体不存在");
|
||||||
|
}
|
||||||
|
Device device = new Device();
|
||||||
|
device.setId(jsonObject.getStr("uuid").replace("-", ""));
|
||||||
|
device.setUserId(user.getId());
|
||||||
|
device.setMacAddress(jsonObject.getStr("mac_address").toUpperCase());
|
||||||
|
device.setBoard(board.getStr("type"));
|
||||||
|
device.setAppVersion(application.getStr("version"));
|
||||||
|
device.setAgentId(agentId);
|
||||||
|
device.setCreator(user.getId());
|
||||||
|
device.setCreateDate(new Date());
|
||||||
|
device.setAutoUpdate(1);
|
||||||
|
boolean bool = deviceService.save(device);
|
||||||
|
if (!bool) {
|
||||||
|
return new Result().error("绑定失败");
|
||||||
|
}
|
||||||
|
return new Result<Device>().ok(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/unbind")
|
@PutMapping("/unbind/{deviceId}")
|
||||||
@Operation(summary = "解绑设备")
|
@Operation(summary = "解绑设备")
|
||||||
@RequiresPermissions("sys:role:normal")
|
@RequiresPermissions("sys:role:normal")
|
||||||
public Result unbindDevice(@RequestBody DeviceUnBindDTO unDeviveBind) {
|
public Result<Device> bindDevice(@PathVariable String deviceId) {
|
||||||
UserDetail user = SecurityUser.getUser();
|
if (StringUtils.isBlank(deviceId)) {
|
||||||
deviceService.unbindDevice(user.getId(), unDeviveBind.getDeviceId());
|
log.error("设备ID不能为空");
|
||||||
return new Result();
|
return new Result().error("设备ID不能为空");
|
||||||
|
}
|
||||||
|
boolean bool = deviceService.removeById(deviceId);
|
||||||
|
if (!bool) {
|
||||||
|
return new Result().error("解绑失败");
|
||||||
|
}
|
||||||
|
return new Result<Device>().ok(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/all")
|
@PostMapping("/autoUpdate/{deviceId}")
|
||||||
@Operation(summary = "设备列表(管理员)")
|
@Operation(summary = "切换设备OTA升级状态")
|
||||||
@RequiresPermissions("sys:role:superAdmin")
|
@RequiresPermissions("sys:role:normal")
|
||||||
@Parameters({
|
public Result<Device> toggleAutoUpdate(@PathVariable String deviceId, @RequestBody Device device) {
|
||||||
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true),
|
if (StringUtils.isBlank(deviceId)) {
|
||||||
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true),
|
log.error("设备ID不能为空");
|
||||||
})
|
return new Result().error("设备ID不能为空");
|
||||||
public Result<PageData<DeviceEntity>> adminDeviceList(
|
}
|
||||||
@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
UserDetail user = SecurityUser.getUser();
|
||||||
PageData<DeviceEntity> page = deviceService.adminDeviceList(params);
|
boolean bool = deviceService.update(
|
||||||
return new Result<PageData<DeviceEntity>>().ok(page);
|
new UpdateWrapper<Device>().eq("id", deviceId)
|
||||||
|
.set("auto_update", device.getAutoUpdate())
|
||||||
|
.set("updater", user.getId())
|
||||||
|
.set("update_date", new Date())
|
||||||
|
);
|
||||||
|
if (!bool) {
|
||||||
|
return new Result<Device>().error("切换设备OTA升级状态失败");
|
||||||
|
}
|
||||||
|
return new Result<Device>().ok(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/alias/{deviceId}")
|
||||||
|
@Operation(summary = "设置设备别名")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<Device> setAlias(@PathVariable String deviceId, @RequestBody Device device) {
|
||||||
|
if (StringUtils.isBlank(deviceId)) {
|
||||||
|
log.error("设备ID不能为空");
|
||||||
|
return new Result().error("设备ID不能为空");
|
||||||
|
}
|
||||||
|
UserDetail user = SecurityUser.getUser();
|
||||||
|
boolean bool = deviceService.update(
|
||||||
|
new UpdateWrapper<Device>().eq("id", deviceId)
|
||||||
|
.set("alias", device.getAlias())
|
||||||
|
.set("updater", user.getId())
|
||||||
|
.set("update_date", new Date())
|
||||||
|
);
|
||||||
|
if (!bool) {
|
||||||
|
return new Result<Device>().error("设置设备别名失败");
|
||||||
|
}
|
||||||
|
return new Result<Device>().ok(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package xiaozhi.modules.device.dao;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
|
||||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
|
||||||
|
|
||||||
@Mapper
|
|
||||||
public interface DeviceDao extends BaseMapper<DeviceEntity> {
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package xiaozhi.modules.device.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备信息表
|
||||||
|
* @TableName ai_device
|
||||||
|
*/
|
||||||
|
@TableName(value ="ai_device")
|
||||||
|
@Data
|
||||||
|
public class Device implements Serializable {
|
||||||
|
/**
|
||||||
|
* 设备唯一标识
|
||||||
|
*/
|
||||||
|
@TableId
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联用户 ID
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MAC 地址
|
||||||
|
*/
|
||||||
|
private String macAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最后连接时间
|
||||||
|
*/
|
||||||
|
private Date lastConnectedAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动更新开关(0 关闭/1 开启)
|
||||||
|
*/
|
||||||
|
private Integer autoUpdate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备硬件型号
|
||||||
|
*/
|
||||||
|
private String board;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备别名
|
||||||
|
*/
|
||||||
|
private String alias;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能体 ID
|
||||||
|
*/
|
||||||
|
private String agentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 固件版本号
|
||||||
|
*/
|
||||||
|
private String appVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者
|
||||||
|
*/
|
||||||
|
private Long creator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新者
|
||||||
|
*/
|
||||||
|
private Long updater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateDate;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package xiaozhi.modules.device.dto;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@Schema(description = "设备连接头信息")
|
|
||||||
public class DeviceHeaderDTO {
|
|
||||||
|
|
||||||
@Schema(description = "设备ID")
|
|
||||||
private String deviceId;
|
|
||||||
|
|
||||||
@Schema(description = "协议版本号")
|
|
||||||
private Long protocolVersion;
|
|
||||||
|
|
||||||
@Schema(description = "认证信息")
|
|
||||||
private String authorization;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package xiaozhi.modules.device.dto;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设备解绑表单
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Schema(description = "设备解绑表单")
|
|
||||||
public class DeviceUnBindDTO implements Serializable {
|
|
||||||
|
|
||||||
@Schema(description = "设备ID")
|
|
||||||
@NotBlank(message = "设备ID不能为空")
|
|
||||||
private Long deviceId;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package xiaozhi.modules.device.entity;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@TableName("ai_device")
|
|
||||||
@Schema(description = "设备信息")
|
|
||||||
public class DeviceEntity {
|
|
||||||
@Schema(description = "设备ID")
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@Schema(description = "关联用户ID")
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
@Schema(description = "MAC地址")
|
|
||||||
private String macAddress;
|
|
||||||
|
|
||||||
@Schema(description = "最后连接时间")
|
|
||||||
private Date lastConnectedAt;
|
|
||||||
|
|
||||||
@Schema(description = "自动更新开关(0关闭/1开启)")
|
|
||||||
private Integer autoUpdate;
|
|
||||||
|
|
||||||
@Schema(description = "设备硬件型号")
|
|
||||||
private String board;
|
|
||||||
|
|
||||||
@Schema(description = "设备别名")
|
|
||||||
private String alias;
|
|
||||||
|
|
||||||
@Schema(description = "智能体ID")
|
|
||||||
private String agentId;
|
|
||||||
|
|
||||||
@Schema(description = "固件版本号")
|
|
||||||
private String appVersion;
|
|
||||||
|
|
||||||
@Schema(description = "排序")
|
|
||||||
private Integer sort;
|
|
||||||
|
|
||||||
@Schema(description = "创建者")
|
|
||||||
private Long creator;
|
|
||||||
|
|
||||||
@Schema(description = "创建时间")
|
|
||||||
private Date createDate;
|
|
||||||
|
|
||||||
@Schema(description = "更新者")
|
|
||||||
private Long updater;
|
|
||||||
|
|
||||||
@Schema(description = "更新时间")
|
|
||||||
private Date updateDate;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package xiaozhi.modules.device.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import xiaozhi.modules.device.domain.Device;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_device(设备信息表)】的数据库操作Mapper
|
||||||
|
* @createDate 2025-03-22 13:26:35
|
||||||
|
* @Entity xiaozhi.modules.device.domain.Device
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface DeviceMapper extends BaseMapper<Device> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,18 +1,13 @@
|
|||||||
package xiaozhi.modules.device.service;
|
package xiaozhi.modules.device.service;
|
||||||
|
|
||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.modules.device.domain.Device;
|
||||||
import xiaozhi.modules.device.dto.DeviceHeaderDTO;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
|
||||||
|
/**
|
||||||
import java.util.List;
|
* @author chenerlei
|
||||||
import java.util.Map;
|
* @description 针对表【ai_device(设备信息表)】的数据库操作Service
|
||||||
|
* @createDate 2025-03-22 13:26:35
|
||||||
public interface DeviceService {
|
*/
|
||||||
DeviceEntity bindDevice(Long userId, DeviceHeaderDTO deviceHeader);
|
public interface DeviceService extends IService<Device> {
|
||||||
|
|
||||||
List<DeviceEntity> getUserDevices(Long userId);
|
}
|
||||||
|
|
||||||
void unbindDevice(Long userId, Long deviceId);
|
|
||||||
|
|
||||||
PageData<DeviceEntity> adminDeviceList(Map<String, Object> params);
|
|
||||||
}
|
|
||||||
|
|||||||
+22
-57
@@ -1,57 +1,22 @@
|
|||||||
package xiaozhi.modules.device.service.impl;
|
package xiaozhi.modules.device.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import xiaozhi.modules.device.domain.Device;
|
||||||
import org.springframework.stereotype.Service;
|
import xiaozhi.modules.device.service.DeviceService;
|
||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.modules.device.mapper.DeviceMapper;
|
||||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
import org.springframework.stereotype.Service;
|
||||||
import xiaozhi.modules.device.dao.DeviceDao;
|
|
||||||
import xiaozhi.modules.device.dto.DeviceHeaderDTO;
|
/**
|
||||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
* @author chenerlei
|
||||||
import xiaozhi.modules.device.service.DeviceService;
|
* @description 针对表【ai_device(设备信息表)】的数据库操作Service实现
|
||||||
|
* @createDate 2025-03-22 13:26:35
|
||||||
import java.util.Date;
|
*/
|
||||||
import java.util.List;
|
@Service
|
||||||
import java.util.Map;
|
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device>
|
||||||
|
implements DeviceService{
|
||||||
@Service
|
|
||||||
public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity> implements DeviceService {
|
}
|
||||||
private final DeviceDao deviceDao;
|
|
||||||
|
|
||||||
// 添加构造函数来初始化 deviceMapper
|
|
||||||
public DeviceServiceImpl(DeviceDao deviceDao) {
|
|
||||||
this.deviceDao = deviceDao;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeviceEntity bindDevice(Long userId, DeviceHeaderDTO deviceHeader) {
|
|
||||||
DeviceEntity device = new DeviceEntity();
|
|
||||||
device.setUserId(userId);
|
|
||||||
device.setMacAddress(deviceHeader.getDeviceId());
|
|
||||||
device.setCreateDate(new Date());
|
|
||||||
deviceDao.insert(device);
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<DeviceEntity> getUserDevices(Long userId) {
|
|
||||||
QueryWrapper<DeviceEntity> wrapper = new QueryWrapper<>();
|
|
||||||
wrapper.eq("user_id", userId);
|
|
||||||
return deviceDao.selectList(wrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unbindDevice(Long userId, Long deviceId) {
|
|
||||||
deviceDao.deleteById(deviceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PageData<DeviceEntity> adminDeviceList(Map<String, Object> params) {
|
|
||||||
IPage<DeviceEntity> page = deviceDao.selectPage(
|
|
||||||
getPage(params, "sort", true),
|
|
||||||
new QueryWrapper<>()
|
|
||||||
);
|
|
||||||
return new PageData<>(page.getRecords(), page.getTotal());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package xiaozhi.modules.device.utils;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class CodeGeneratorUtil {
|
||||||
|
|
||||||
|
private static long lastTime = 0;
|
||||||
|
private static int counter = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成6位数字码,首位不为0,短期内不会重复。
|
||||||
|
* @return 6位数字码
|
||||||
|
*/
|
||||||
|
public static synchronized String generateCode() {
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// 如果当前时间与上次相同,增加计数器以避免重复
|
||||||
|
if (currentTime == lastTime) {
|
||||||
|
counter++;
|
||||||
|
} else {
|
||||||
|
counter = 0; // 时间变化时重置计数器
|
||||||
|
}
|
||||||
|
lastTime = currentTime;
|
||||||
|
|
||||||
|
// 使用当前时间和计数器生成随机种子
|
||||||
|
long seed = currentTime + counter;
|
||||||
|
Random random = new Random(seed);
|
||||||
|
|
||||||
|
// 确保首位不为0
|
||||||
|
int firstDigit = random.nextInt(9) + 1;
|
||||||
|
int remainingDigits = random.nextInt(100000); // 剩余5位
|
||||||
|
|
||||||
|
// 拼接生成的6位数字码
|
||||||
|
return String.format("%d%05d", firstDigit, remainingDigits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成指定位数的数字码,首位不为0,短期内不会重复。
|
||||||
|
* @param digits 生成的数字码位数
|
||||||
|
* @return 指定位数的数字码
|
||||||
|
*/
|
||||||
|
public static synchronized String generateCode(int digits) {
|
||||||
|
if (digits < 2) {
|
||||||
|
throw new IllegalArgumentException("数字码位数必须大于等于2");
|
||||||
|
}
|
||||||
|
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// 如果当前时间与上次相同,增加计数器以避免重复
|
||||||
|
if (currentTime == lastTime) {
|
||||||
|
counter++;
|
||||||
|
} else {
|
||||||
|
counter = 0; // 时间变化时重置计数器
|
||||||
|
}
|
||||||
|
lastTime = currentTime;
|
||||||
|
|
||||||
|
// 使用当前时间和计数器生成随机种子
|
||||||
|
long seed = currentTime + counter;
|
||||||
|
Random random = new Random(seed);
|
||||||
|
|
||||||
|
// 确保首位不为0
|
||||||
|
int firstDigit = random.nextInt(9) + 1;
|
||||||
|
int remainingDigits = random.nextInt((int) Math.pow(10, digits - 1)); // 剩余位数
|
||||||
|
|
||||||
|
// 拼接生成的动态位数数字码
|
||||||
|
return String.format("%d%0" + (digits - 1) + "d", firstDigit, remainingDigits);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package xiaozhi.modules.device.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class DeviceCodeVO {
|
||||||
|
private String deviceCode;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package xiaozhi.modules.device.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class DeviceOtaVO {
|
||||||
|
private Activation activation;
|
||||||
|
private Mqtt mqtt;
|
||||||
|
private ServerTime server_time;
|
||||||
|
private Firmware firmware;
|
||||||
|
private String error;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Activation {
|
||||||
|
private String code;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public Activation() {
|
||||||
|
}
|
||||||
|
public Activation(String code, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Mqtt {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class ServerTime {
|
||||||
|
private Long timestamp;
|
||||||
|
private Integer timezone_offset;
|
||||||
|
|
||||||
|
public ServerTime() {
|
||||||
|
}
|
||||||
|
public ServerTime(Long timestamp, Integer timezone_offset) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.timezone_offset = timezone_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Firmware {
|
||||||
|
private String version;
|
||||||
|
private String url;
|
||||||
|
public Firmware() {
|
||||||
|
}
|
||||||
|
public Firmware(String version, String url) {
|
||||||
|
this.version = version;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
package xiaozhi.modules.model.controller;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import xiaozhi.common.page.PageData;
|
||||||
|
import xiaozhi.common.utils.Result;
|
||||||
|
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
|
||||||
|
import xiaozhi.modules.model.dto.ModelConfigDTO;
|
||||||
|
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
||||||
|
import xiaozhi.modules.model.service.Conflict_ModelConfigService;
|
||||||
|
import xiaozhi.modules.model.service.Conflict_ModelProviderService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/models")
|
||||||
|
@Tag(name = "模型配置")
|
||||||
|
public class Conflict_ModelController {
|
||||||
|
|
||||||
|
private final Conflict_ModelProviderService modelProviderService;
|
||||||
|
|
||||||
|
private final Conflict_ModelConfigService modelConfigService;
|
||||||
|
|
||||||
|
@GetMapping("/models/names")
|
||||||
|
@Operation(summary = "获取所有模型名称")
|
||||||
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
|
public Result<List<String>> getModelNames(@RequestParam String modelType,
|
||||||
|
@RequestParam(required = false) String modelName) {
|
||||||
|
List<String> modelNameList = modelConfigService.getModelCodeList(modelType, modelName);
|
||||||
|
return new Result<List<String>>().ok(modelNameList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{modelType}/provideTypes")
|
||||||
|
@Operation(summary = "获取模型供应器列表")
|
||||||
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
|
public Result<List<ModelProviderDTO>> getModelProviderList(@PathVariable String modelType) {
|
||||||
|
List<ModelProviderDTO> modelProviderDTOS = modelProviderService.getListByModelType(modelType);
|
||||||
|
return new Result<List<ModelProviderDTO>>().ok(modelProviderDTOS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{modelType}/{provideCode}/fields")
|
||||||
|
@Operation(summary = "获取模型供应器字段")
|
||||||
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
|
public Result<List<String>> getModelProviderFields(@PathVariable String modelType, @PathVariable String provideCode) {
|
||||||
|
List<String> fieldList = modelProviderService.getFieldList(modelType, provideCode);
|
||||||
|
return new Result<List<String>>().ok(fieldList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/models/list")
|
||||||
|
@Operation(summary = "获取模型配置列表")
|
||||||
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
|
public Result<PageData<ModelConfigDTO>> getModelConfigList(@RequestParam String modelType,
|
||||||
|
@RequestParam(required = false) String modelName,
|
||||||
|
@RequestParam(required = false, defaultValue = "0") Integer page,
|
||||||
|
@RequestParam(required = false, defaultValue = "10") Integer limit) {
|
||||||
|
PageData<ModelConfigDTO> pageList = modelConfigService.getPageList(modelType, modelName, page, limit);
|
||||||
|
return new Result<PageData<ModelConfigDTO>>().ok(pageList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/models/{modelType}/{provideCode}")
|
||||||
|
@Operation(summary = "新增模型配置")
|
||||||
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
|
public Result<ModelConfigDTO> addModelConfig(@PathVariable String modelType,
|
||||||
|
@PathVariable String provideCode,
|
||||||
|
@RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||||
|
ModelConfigDTO modelConfigDTO = modelConfigService.add(modelType, provideCode, modelConfigBodyDTO);
|
||||||
|
return new Result<ModelConfigDTO>().ok(modelConfigDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PutMapping("/models/{modelType}/{provideCode}/{id}")
|
||||||
|
@Operation(summary = "编辑模型配置")
|
||||||
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
|
public Result<ModelConfigDTO> editModelConfig(@PathVariable String modelType,
|
||||||
|
@PathVariable String provideCode,
|
||||||
|
@PathVariable String id,
|
||||||
|
@RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||||
|
ModelConfigDTO modelConfigDTO = modelConfigService.edit(modelType, provideCode, id, modelConfigBodyDTO);
|
||||||
|
return new Result<ModelConfigDTO>().ok(modelConfigDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@DeleteMapping("/models/{modelType}/{provideCode}/{id}")
|
||||||
|
@Operation(summary = "删除模型配置")
|
||||||
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
|
public Result<Void> deleteModelConfig(@PathVariable String modelType, @PathVariable String provideCode, @PathVariable String id) {
|
||||||
|
modelConfigService.delete(modelType, provideCode, id);
|
||||||
|
return new Result<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/models/{modelName}/voices")
|
||||||
|
@Operation(summary = "获取模型音色")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<List<String>> getVoiceList(@PathVariable String modelName,
|
||||||
|
@RequestParam(required = false) String voiceName) {
|
||||||
|
|
||||||
|
List<String> voiceList = modelConfigService.getVoiceList(modelName, voiceName);
|
||||||
|
return new Result<List<String>>().ok(voiceList);
|
||||||
|
}
|
||||||
|
}
|
||||||
+106
-88
@@ -1,106 +1,124 @@
|
|||||||
package xiaozhi.modules.model.controller;
|
package xiaozhi.modules.model.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import xiaozhi.common.page.PageData;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import xiaozhi.common.controller.BaseController;
|
||||||
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
import xiaozhi.common.utils.Result;
|
import xiaozhi.common.utils.Result;
|
||||||
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
|
import xiaozhi.modules.model.domain.ModelConfig;
|
||||||
import xiaozhi.modules.model.dto.ModelConfigDTO;
|
import xiaozhi.modules.model.domain.TtsVoice;
|
||||||
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
|
||||||
import xiaozhi.modules.model.service.ModelConfigService;
|
import xiaozhi.modules.model.service.ModelConfigService;
|
||||||
import xiaozhi.modules.model.service.ModelProviderService;
|
import xiaozhi.modules.model.service.TtsVoiceService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Tag(name = "模型数据管理")
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/models")
|
@RequestMapping("/model/")
|
||||||
@Tag(name = "模型配置")
|
public class ModelController extends BaseController {
|
||||||
public class ModelController {
|
private final TtsVoiceService ttsVoiceService;
|
||||||
|
|
||||||
private final ModelProviderService modelProviderService;
|
|
||||||
|
|
||||||
private final ModelConfigService modelConfigService;
|
private final ModelConfigService modelConfigService;
|
||||||
|
|
||||||
@GetMapping("/models/names")
|
|
||||||
@Operation(summary = "获取所有模型名称")
|
|
||||||
@RequiresPermissions("sys:role:superAdmin")
|
|
||||||
public Result<List<String>> getModelNames(@RequestParam String modelType,
|
|
||||||
@RequestParam(required = false) String modelName) {
|
|
||||||
List<String> modelNameList = modelConfigService.getModelCodeList(modelType, modelName);
|
|
||||||
return new Result<List<String>>().ok(modelNameList);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/{modelType}/provideTypes")
|
@GetMapping("/list")
|
||||||
@Operation(summary = "获取模型供应器列表")
|
@Operation(summary = "模型配置列表")
|
||||||
@RequiresPermissions("sys:role:superAdmin")
|
|
||||||
public Result<List<ModelProviderDTO>> getModelProviderList(@PathVariable String modelType) {
|
|
||||||
List<ModelProviderDTO> modelProviderDTOS = modelProviderService.getListByModelType(modelType);
|
|
||||||
return new Result<List<ModelProviderDTO>>().ok(modelProviderDTOS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/{modelType}/{provideCode}/fields")
|
|
||||||
@Operation(summary = "获取模型供应器字段")
|
|
||||||
@RequiresPermissions("sys:role:superAdmin")
|
|
||||||
public Result<List<String>> getModelProviderFields(@PathVariable String modelType, @PathVariable String provideCode) {
|
|
||||||
List<String> fieldList = modelProviderService.getFieldList(modelType, provideCode);
|
|
||||||
return new Result<List<String>>().ok(fieldList);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("/models/list")
|
|
||||||
@Operation(summary = "获取模型配置列表")
|
|
||||||
@RequiresPermissions("sys:role:superAdmin")
|
|
||||||
public Result<PageData<ModelConfigDTO>> getModelConfigList(@RequestParam String modelType,
|
|
||||||
@RequestParam(required = false) String modelName,
|
|
||||||
@RequestParam(required = false, defaultValue = "0") Integer page,
|
|
||||||
@RequestParam(required = false,defaultValue = "10") Integer limit) {
|
|
||||||
PageData<ModelConfigDTO> pageList = modelConfigService.getPageList(modelType, modelName, page, limit);
|
|
||||||
return new Result<PageData<ModelConfigDTO>>().ok(pageList);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/models/{modelType}/{provideCode}")
|
|
||||||
@Operation(summary = "新增模型配置")
|
|
||||||
@RequiresPermissions("sys:role:superAdmin")
|
|
||||||
public Result<ModelConfigDTO> addModelConfig(@PathVariable String modelType,
|
|
||||||
@PathVariable String provideCode,
|
|
||||||
@RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
|
|
||||||
ModelConfigDTO modelConfigDTO = modelConfigService.add(modelType, provideCode, modelConfigBodyDTO);
|
|
||||||
return new Result<ModelConfigDTO>().ok(modelConfigDTO);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@PutMapping("/models/{modelType}/{provideCode}/{id}")
|
|
||||||
@Operation(summary = "编辑模型配置")
|
|
||||||
@RequiresPermissions("sys:role:superAdmin")
|
|
||||||
public Result<ModelConfigDTO> editModelConfig(@PathVariable String modelType,
|
|
||||||
@PathVariable String provideCode,
|
|
||||||
@PathVariable String id,
|
|
||||||
@RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
|
|
||||||
ModelConfigDTO modelConfigDTO = modelConfigService.edit(modelType, provideCode, id, modelConfigBodyDTO);
|
|
||||||
return new Result<ModelConfigDTO>().ok(modelConfigDTO);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@DeleteMapping("/models/{modelType}/{provideCode}/{id}")
|
|
||||||
@Operation(summary = "删除模型配置")
|
|
||||||
@RequiresPermissions("sys:role:superAdmin")
|
|
||||||
public Result<Void> deleteModelConfig(@PathVariable String modelType, @PathVariable String provideCode, @PathVariable String id) {
|
|
||||||
modelConfigService.delete(modelType, provideCode, id);
|
|
||||||
return new Result<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/models/{modelName}/voices")
|
|
||||||
@Operation(summary = "获取模型音色")
|
|
||||||
@RequiresPermissions("sys:role:normal")
|
@RequiresPermissions("sys:role:normal")
|
||||||
public Result<List<String>> getVoiceList(@PathVariable String modelName,
|
public Result<List<ModelConfig>> getModelList() {
|
||||||
@RequestParam(required = false) String voiceName) {
|
List<ModelConfig> list = modelConfigService.list(new QueryWrapper<ModelConfig>().eq("is_enabled", 1));
|
||||||
|
return new Result<List<ModelConfig>>().ok(list);
|
||||||
List<String> voiceList = modelConfigService.getVoiceList(modelName, voiceName);
|
|
||||||
return new Result<List<String>>().ok(voiceList);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// private final ModelProviderService modelProviderService;
|
||||||
|
//
|
||||||
|
// private final ModelConfigService modelConfigService;
|
||||||
|
//
|
||||||
|
// @GetMapping("/models/names")
|
||||||
|
// @Operation(summary = "获取所有模型名称")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<List<String>> getModelNames(@RequestParam String modelType,
|
||||||
|
// @RequestParam(required = false) String modelName) {
|
||||||
|
// List<String> modelNameList = modelConfigService.getModelCodeList(modelType, modelName);
|
||||||
|
// return new Result<List<String>>().ok(modelNameList);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @GetMapping("/{modelType}/provideTypes")
|
||||||
|
// @Operation(summary = "获取模型供应器列表")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<List<ModelProviderDTO>> getModelProviderList(@PathVariable String modelType) {
|
||||||
|
// List<ModelProviderDTO> modelProviderDTOS = modelProviderService.getListByModelType(modelType);
|
||||||
|
// return new Result<List<ModelProviderDTO>>().ok(modelProviderDTOS);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @GetMapping("/{modelType}/{provideCode}/fields")
|
||||||
|
// @Operation(summary = "获取模型供应器字段")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<List<String>> getModelProviderFields(@PathVariable String modelType, @PathVariable String provideCode) {
|
||||||
|
// List<String> fieldList = modelProviderService.getFieldList(modelType, provideCode);
|
||||||
|
// return new Result<List<String>>().ok(fieldList);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// @GetMapping("/models/list")
|
||||||
|
// @Operation(summary = "获取模型配置列表")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<PageData<ModelConfigDTO>> getModelConfigList(@RequestParam String modelType,
|
||||||
|
// @RequestParam(required = false) String modelName,
|
||||||
|
// @RequestParam(required = false, defaultValue = "0") Integer page,
|
||||||
|
// @RequestParam(required = false,defaultValue = "10") Integer limit) {
|
||||||
|
// PageData<ModelConfigDTO> pageList = modelConfigService.getPageList(modelType, modelName, page, limit);
|
||||||
|
// return new Result<PageData<ModelConfigDTO>>().ok(pageList);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// @PostMapping("/models/{modelType}/{provideCode}")
|
||||||
|
// @Operation(summary = "新增模型配置")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<ModelConfigDTO> addModelConfig(@PathVariable String modelType,
|
||||||
|
// @PathVariable String provideCode,
|
||||||
|
// @RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||||
|
// ModelConfigDTO modelConfigDTO = modelConfigService.add(modelType, provideCode, modelConfigBodyDTO);
|
||||||
|
// return new Result<ModelConfigDTO>().ok(modelConfigDTO);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// @PutMapping("/models/{modelType}/{provideCode}/{id}")
|
||||||
|
// @Operation(summary = "编辑模型配置")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<ModelConfigDTO> editModelConfig(@PathVariable String modelType,
|
||||||
|
// @PathVariable String provideCode,
|
||||||
|
// @PathVariable String id,
|
||||||
|
// @RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||||
|
// ModelConfigDTO modelConfigDTO = modelConfigService.edit(modelType, provideCode, id, modelConfigBodyDTO);
|
||||||
|
// return new Result<ModelConfigDTO>().ok(modelConfigDTO);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// @DeleteMapping("/models/{modelType}/{provideCode}/{id}")
|
||||||
|
// @Operation(summary = "删除模型配置")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<Void> deleteModelConfig(@PathVariable String modelType, @PathVariable String provideCode, @PathVariable String id) {
|
||||||
|
// modelConfigService.delete(modelType, provideCode, id);
|
||||||
|
// return new Result<>();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @GetMapping("/models/{modelName}/voices")
|
||||||
|
// @Operation(summary = "获取模型音色")
|
||||||
|
// @RequiresPermissions("sys:role:normal")
|
||||||
|
// public Result<List<String>> getVoiceList(@PathVariable String modelName,
|
||||||
|
// @RequestParam(required = false) String voiceName) {
|
||||||
|
//
|
||||||
|
// List<String> voiceList = modelConfigService.getVoiceList(modelName, voiceName);
|
||||||
|
// return new Result<List<String>>().ok(voiceList);
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package xiaozhi.modules.model.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
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.lang3.StringUtils;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import xiaozhi.common.controller.BaseController;
|
||||||
|
import xiaozhi.common.utils.Result;
|
||||||
|
import xiaozhi.modules.model.domain.TtsVoice;
|
||||||
|
import xiaozhi.modules.model.service.ModelConfigService;
|
||||||
|
import xiaozhi.modules.model.service.TtsVoiceService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Tag(name = "模型数据管理")
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/tts")
|
||||||
|
public class TtsController extends BaseController {
|
||||||
|
private final TtsVoiceService ttsVoiceService;
|
||||||
|
private final ModelConfigService modelConfigService;
|
||||||
|
|
||||||
|
@GetMapping("/voice")
|
||||||
|
@Operation(summary = "音色列表")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<List<TtsVoice>> getTtsList(@RequestParam(required = false) String ttsModelId) {
|
||||||
|
QueryWrapper<TtsVoice> queryWrapper = new QueryWrapper<>();
|
||||||
|
if (StringUtils.isNotBlank(ttsModelId)) {
|
||||||
|
queryWrapper.eq("ttsModelId", ttsModelId);
|
||||||
|
}
|
||||||
|
queryWrapper.orderByAsc("sort");
|
||||||
|
List<TtsVoice> list = ttsVoiceService.list(queryWrapper);
|
||||||
|
return new Result<List<TtsVoice>>().ok(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// private final ModelProviderService modelProviderService;
|
||||||
|
//
|
||||||
|
// private final ModelConfigService modelConfigService;
|
||||||
|
//
|
||||||
|
// @GetMapping("/models/names")
|
||||||
|
// @Operation(summary = "获取所有模型名称")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<List<String>> getModelNames(@RequestParam String modelType,
|
||||||
|
// @RequestParam(required = false) String modelName) {
|
||||||
|
// List<String> modelNameList = modelConfigService.getModelCodeList(modelType, modelName);
|
||||||
|
// return new Result<List<String>>().ok(modelNameList);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @GetMapping("/{modelType}/provideTypes")
|
||||||
|
// @Operation(summary = "获取模型供应器列表")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<List<ModelProviderDTO>> getModelProviderList(@PathVariable String modelType) {
|
||||||
|
// List<ModelProviderDTO> modelProviderDTOS = modelProviderService.getListByModelType(modelType);
|
||||||
|
// return new Result<List<ModelProviderDTO>>().ok(modelProviderDTOS);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @GetMapping("/{modelType}/{provideCode}/fields")
|
||||||
|
// @Operation(summary = "获取模型供应器字段")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<List<String>> getModelProviderFields(@PathVariable String modelType, @PathVariable String provideCode) {
|
||||||
|
// List<String> fieldList = modelProviderService.getFieldList(modelType, provideCode);
|
||||||
|
// return new Result<List<String>>().ok(fieldList);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// @GetMapping("/models/list")
|
||||||
|
// @Operation(summary = "获取模型配置列表")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<PageData<ModelConfigDTO>> getModelConfigList(@RequestParam String modelType,
|
||||||
|
// @RequestParam(required = false) String modelName,
|
||||||
|
// @RequestParam(required = false, defaultValue = "0") Integer page,
|
||||||
|
// @RequestParam(required = false,defaultValue = "10") Integer limit) {
|
||||||
|
// PageData<ModelConfigDTO> pageList = modelConfigService.getPageList(modelType, modelName, page, limit);
|
||||||
|
// return new Result<PageData<ModelConfigDTO>>().ok(pageList);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// @PostMapping("/models/{modelType}/{provideCode}")
|
||||||
|
// @Operation(summary = "新增模型配置")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<ModelConfigDTO> addModelConfig(@PathVariable String modelType,
|
||||||
|
// @PathVariable String provideCode,
|
||||||
|
// @RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||||
|
// ModelConfigDTO modelConfigDTO = modelConfigService.add(modelType, provideCode, modelConfigBodyDTO);
|
||||||
|
// return new Result<ModelConfigDTO>().ok(modelConfigDTO);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// @PutMapping("/models/{modelType}/{provideCode}/{id}")
|
||||||
|
// @Operation(summary = "编辑模型配置")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<ModelConfigDTO> editModelConfig(@PathVariable String modelType,
|
||||||
|
// @PathVariable String provideCode,
|
||||||
|
// @PathVariable String id,
|
||||||
|
// @RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||||
|
// ModelConfigDTO modelConfigDTO = modelConfigService.edit(modelType, provideCode, id, modelConfigBodyDTO);
|
||||||
|
// return new Result<ModelConfigDTO>().ok(modelConfigDTO);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// @DeleteMapping("/models/{modelType}/{provideCode}/{id}")
|
||||||
|
// @Operation(summary = "删除模型配置")
|
||||||
|
// @RequiresPermissions("sys:role:superAdmin")
|
||||||
|
// public Result<Void> deleteModelConfig(@PathVariable String modelType, @PathVariable String provideCode, @PathVariable String id) {
|
||||||
|
// modelConfigService.delete(modelType, provideCode, id);
|
||||||
|
// return new Result<>();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @GetMapping("/models/{modelName}/voices")
|
||||||
|
// @Operation(summary = "获取模型音色")
|
||||||
|
// @RequiresPermissions("sys:role:normal")
|
||||||
|
// public Result<List<String>> getVoiceList(@PathVariable String modelName,
|
||||||
|
// @RequestParam(required = false) String voiceName) {
|
||||||
|
//
|
||||||
|
// List<String> voiceList = modelConfigService.getVoiceList(modelName, voiceName);
|
||||||
|
// return new Result<List<String>>().ok(voiceList);
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package xiaozhi.modules.model.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型配置表
|
||||||
|
* @TableName ai_model_config
|
||||||
|
*/
|
||||||
|
@TableName(value ="ai_model_config")
|
||||||
|
@Data
|
||||||
|
public class ModelConfig implements Serializable {
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@TableId
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型类型(Memory/ASR/VAD/LLM/TTS)
|
||||||
|
*/
|
||||||
|
private String modelType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型编码(如AliLLM、DoubaoTTS)
|
||||||
|
*/
|
||||||
|
private String modelCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型名称
|
||||||
|
*/
|
||||||
|
private String modelName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否默认配置(0否 1是)
|
||||||
|
*/
|
||||||
|
private Integer isDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用
|
||||||
|
*/
|
||||||
|
private Integer isEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型配置(JSON格式)
|
||||||
|
*/
|
||||||
|
private String configJson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 官方文档链接
|
||||||
|
*/
|
||||||
|
private String docLink;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者
|
||||||
|
*/
|
||||||
|
private Long creator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新者
|
||||||
|
*/
|
||||||
|
private Long updater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateDate;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package xiaozhi.modules.model.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型配置表
|
||||||
|
* @TableName ai_model_provider
|
||||||
|
*/
|
||||||
|
@TableName(value ="ai_model_provider")
|
||||||
|
@Data
|
||||||
|
public class ModelProvider implements Serializable {
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@TableId
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型类型(Memory/ASR/VAD/LLM/TTS)
|
||||||
|
*/
|
||||||
|
private String modelType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 供应器类型
|
||||||
|
*/
|
||||||
|
private String providerCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 供应器名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 供应器字段列表(JSON格式)
|
||||||
|
*/
|
||||||
|
private Object fields;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者
|
||||||
|
*/
|
||||||
|
private Long creator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新者
|
||||||
|
*/
|
||||||
|
private Long updater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateDate;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package xiaozhi.modules.model.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TTS 音色表
|
||||||
|
* @TableName ai_tts_voice
|
||||||
|
*/
|
||||||
|
@TableName(value ="ai_tts_voice")
|
||||||
|
@Data
|
||||||
|
public class TtsVoice implements Serializable {
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@TableId
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对应 TTS 模型主键
|
||||||
|
*/
|
||||||
|
private String ttsModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 音色名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 音色编码
|
||||||
|
*/
|
||||||
|
private String ttsVoice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语言
|
||||||
|
*/
|
||||||
|
private String languages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 音色 Demo
|
||||||
|
*/
|
||||||
|
private String voiceDemo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者
|
||||||
|
*/
|
||||||
|
private Long creator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新者
|
||||||
|
*/
|
||||||
|
private Long updater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateDate;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package xiaozhi.modules.model.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import xiaozhi.modules.model.domain.ModelConfig;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_model_config(模型配置表)】的数据库操作Mapper
|
||||||
|
* @createDate 2025-03-22 15:31:57
|
||||||
|
* @Entity xiaozhi.modules.model.domain.ModelConfig
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ModelConfigMapper extends BaseMapper<ModelConfig> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package xiaozhi.modules.model.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import xiaozhi.modules.model.domain.ModelProvider;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_model_provider(模型配置表)】的数据库操作Mapper
|
||||||
|
* @createDate 2025-03-24 18:24:13
|
||||||
|
* @Entity xiaozhi.modules.model.domain.ModelProvider
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ModelProviderMapper extends BaseMapper<ModelProvider> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package xiaozhi.modules.model.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import xiaozhi.modules.model.domain.TtsVoice;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_tts_voice(TTS 音色表)】的数据库操作Mapper
|
||||||
|
* @createDate 2025-03-22 15:31:57
|
||||||
|
* @Entity xiaozhi.modules.model.domain.TtsVoice
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface TtsVoiceMapper extends BaseMapper<TtsVoice> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package xiaozhi.modules.model.service;
|
||||||
|
|
||||||
|
import xiaozhi.common.page.PageData;
|
||||||
|
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
|
||||||
|
import xiaozhi.modules.model.dto.ModelConfigDTO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface Conflict_ModelConfigService {
|
||||||
|
|
||||||
|
List<String> getModelCodeList(String modelType, String modelName);
|
||||||
|
|
||||||
|
PageData<ModelConfigDTO> getPageList(String modelType, String modelName, Integer page, Integer limit);
|
||||||
|
|
||||||
|
ModelConfigDTO add(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO);
|
||||||
|
|
||||||
|
ModelConfigDTO edit(String modelType, String provideCode, String id, ModelConfigBodyDTO modelConfigBodyDTO);
|
||||||
|
|
||||||
|
void delete(String modelType, String provideCode, String id);
|
||||||
|
|
||||||
|
List<String> getVoiceList(String modelName, String voiceName);
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package xiaozhi.modules.model.service;
|
||||||
|
|
||||||
|
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
||||||
|
import xiaozhi.modules.model.entity.ModelProviderEntity;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface Conflict_ModelProviderService {
|
||||||
|
|
||||||
|
// List<String> getModelNames(String modelType, String modelName);
|
||||||
|
|
||||||
|
List<ModelProviderDTO> getListByModelType(String modelType);
|
||||||
|
|
||||||
|
ModelProviderDTO add(ModelProviderEntity modelProviderEntity);
|
||||||
|
|
||||||
|
ModelProviderDTO edit(ModelProviderEntity modelProviderEntity);
|
||||||
|
|
||||||
|
void delete();
|
||||||
|
|
||||||
|
List<ModelProviderDTO> getList(String modelType, String provideCode);
|
||||||
|
|
||||||
|
List<String> getFieldList(String modelType, String provideCode);
|
||||||
|
}
|
||||||
+8
-17
@@ -1,22 +1,13 @@
|
|||||||
package xiaozhi.modules.model.service;
|
package xiaozhi.modules.model.service;
|
||||||
|
|
||||||
import xiaozhi.common.page.PageData;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
|
import xiaozhi.modules.model.domain.ModelConfig;
|
||||||
import xiaozhi.modules.model.dto.ModelConfigDTO;
|
|
||||||
|
|
||||||
import java.util.List;
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_model_config(模型配置表)】的数据库操作Service
|
||||||
|
* @createDate 2025-03-22 15:31:57
|
||||||
|
*/
|
||||||
|
public interface ModelConfigService extends IService<ModelConfig> {
|
||||||
|
|
||||||
public interface ModelConfigService {
|
|
||||||
|
|
||||||
List<String> getModelCodeList(String modelType, String modelName);
|
|
||||||
|
|
||||||
PageData<ModelConfigDTO> getPageList(String modelType, String modelName, Integer page, Integer limit);
|
|
||||||
|
|
||||||
ModelConfigDTO add(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO);
|
|
||||||
|
|
||||||
ModelConfigDTO edit(String modelType, String provideCode, String id, ModelConfigBodyDTO modelConfigBodyDTO);
|
|
||||||
|
|
||||||
void delete(String modelType, String provideCode, String id);
|
|
||||||
|
|
||||||
List<String> getVoiceList(String modelName, String voiceName);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-18
@@ -1,23 +1,13 @@
|
|||||||
package xiaozhi.modules.model.service;
|
package xiaozhi.modules.model.service;
|
||||||
|
|
||||||
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import xiaozhi.modules.model.entity.ModelProviderEntity;
|
import xiaozhi.modules.model.domain.ModelProvider;
|
||||||
|
|
||||||
import java.util.List;
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_model_provider(模型配置表)】的数据库操作Service
|
||||||
|
* @createDate 2025-03-24 18:24:13
|
||||||
|
*/
|
||||||
|
public interface ModelProviderService extends IService<ModelProvider> {
|
||||||
|
|
||||||
public interface ModelProviderService {
|
|
||||||
|
|
||||||
// List<String> getModelNames(String modelType, String modelName);
|
|
||||||
|
|
||||||
List<ModelProviderDTO> getListByModelType(String modelType);
|
|
||||||
|
|
||||||
ModelProviderDTO add(ModelProviderEntity modelProviderEntity);
|
|
||||||
|
|
||||||
ModelProviderDTO edit(ModelProviderEntity modelProviderEntity);
|
|
||||||
|
|
||||||
void delete();
|
|
||||||
|
|
||||||
List<ModelProviderDTO> getList(String modelType, String provideCode);
|
|
||||||
|
|
||||||
List<String> getFieldList(String modelType, String provideCode);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package xiaozhi.modules.model.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import xiaozhi.modules.model.domain.TtsVoice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_tts_voice(TTS 音色表)】的数据库操作Service
|
||||||
|
* @createDate 2025-03-22 15:31:57
|
||||||
|
*/
|
||||||
|
public interface TtsVoiceService extends IService<TtsVoice> {
|
||||||
|
|
||||||
|
}
|
||||||
+124
@@ -0,0 +1,124 @@
|
|||||||
|
package xiaozhi.modules.model.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import xiaozhi.common.constant.Constant;
|
||||||
|
import xiaozhi.common.exception.RenException;
|
||||||
|
import xiaozhi.common.page.PageData;
|
||||||
|
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||||
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
|
import xiaozhi.modules.model.dao.ModelConfigDao;
|
||||||
|
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
|
||||||
|
import xiaozhi.modules.model.dto.ModelConfigDTO;
|
||||||
|
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
||||||
|
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
||||||
|
import xiaozhi.modules.model.service.Conflict_ModelConfigService;
|
||||||
|
import xiaozhi.modules.model.service.Conflict_ModelProviderService;
|
||||||
|
import xiaozhi.modules.timbre.service.TimbreService;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Conflict_ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, ModelConfigEntity> implements Conflict_ModelConfigService {
|
||||||
|
|
||||||
|
private final ModelConfigDao modelConfigDao;
|
||||||
|
private final Conflict_ModelProviderService modelProviderService;
|
||||||
|
private final TimbreService timbreService;
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ModelConfigServiceImpl.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getModelCodeList(String modelType, String modelName) {
|
||||||
|
return modelConfigDao.getModelCodeList(modelType, modelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageData<ModelConfigDTO> getPageList(String modelType, String modelName, Integer page, Integer limit) {
|
||||||
|
Map<String, Object> params = new HashMap<String, Object>();
|
||||||
|
params.put(Constant.PAGE, page);
|
||||||
|
params.put(Constant.LIMIT, limit);
|
||||||
|
IPage<ModelConfigEntity> modelConfigEntityIPage = modelConfigDao.selectPage(
|
||||||
|
getPage(params, "sort", true),
|
||||||
|
new QueryWrapper<ModelConfigEntity>()
|
||||||
|
.eq("model_type", modelType)
|
||||||
|
.like(StringUtils.isNotBlank(modelName), "model_name", modelName)
|
||||||
|
);
|
||||||
|
return getPageData(modelConfigEntityIPage, ModelConfigDTO.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelConfigDTO add(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||||
|
// 先验证有没有供应器
|
||||||
|
if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
|
||||||
|
throw new RenException("modelType和provideCode不能为空");
|
||||||
|
}
|
||||||
|
List<ModelProviderDTO> providerList = modelProviderService.getList(modelType, provideCode);
|
||||||
|
if (CollectionUtil.isEmpty(providerList)) {
|
||||||
|
throw new RenException("供应器不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再保存供应器提供的模型
|
||||||
|
ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class);
|
||||||
|
modelConfigEntity.setModelType(modelType);
|
||||||
|
modelConfigDao.insert(modelConfigEntity);
|
||||||
|
return ConvertUtils.sourceToTarget(modelConfigEntity, ModelConfigDTO.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelConfigDTO edit(String modelType, String provideCode, String id, ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||||
|
// 先验证有没有供应器
|
||||||
|
if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
|
||||||
|
throw new RenException("modelType和provideCode不能为空");
|
||||||
|
}
|
||||||
|
List<ModelProviderDTO> providerList = modelProviderService.getList(modelType, provideCode);
|
||||||
|
if (CollectionUtil.isEmpty(providerList)) {
|
||||||
|
throw new RenException("供应器不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再更新供应器提供的模型
|
||||||
|
ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class);
|
||||||
|
modelConfigEntity.setId(id);
|
||||||
|
modelConfigEntity.setModelType(modelType);
|
||||||
|
modelConfigDao.updateById(modelConfigEntity);
|
||||||
|
return ConvertUtils.sourceToTarget(modelConfigEntity, ModelConfigDTO.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String modelType, String provideCode, String id) {
|
||||||
|
// 先验证有没有供应器
|
||||||
|
if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
|
||||||
|
throw new RenException("modelType和provideCode不能为空");
|
||||||
|
}
|
||||||
|
List<ModelProviderDTO> providerList = modelProviderService.getList(modelType, provideCode);
|
||||||
|
if (CollectionUtil.isEmpty(providerList)) {
|
||||||
|
throw new RenException("供应器不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
modelConfigDao.deleteById(Long.getLong(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getVoiceList(String modelName, String voiceName) {
|
||||||
|
QueryWrapper<ModelConfigEntity> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("model_name", StringUtils.isBlank(modelName) ? "" : modelName);
|
||||||
|
queryWrapper.eq("model_type", "TTS");
|
||||||
|
List<ModelConfigEntity> modelConfigEntities = modelConfigDao.selectList(queryWrapper);
|
||||||
|
if (CollectionUtil.isEmpty(modelConfigEntities)) {
|
||||||
|
logger.warn("没有找到模型配置信息");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ModelConfigEntity modelConfigEntity = modelConfigEntities.get(0);
|
||||||
|
String id = modelConfigEntity.getId();
|
||||||
|
|
||||||
|
return timbreService.getVoiceNames(id, voiceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
package xiaozhi.modules.model.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||||
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
|
import xiaozhi.modules.model.dao.ModelProviderDao;
|
||||||
|
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
||||||
|
import xiaozhi.modules.model.entity.ModelProviderEntity;
|
||||||
|
import xiaozhi.modules.model.service.Conflict_ModelProviderService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Conflict_ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao, ModelProviderEntity> implements Conflict_ModelProviderService {
|
||||||
|
|
||||||
|
private final ModelProviderDao modelProviderDao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ModelProviderDTO> getListByModelType(String modelType) {
|
||||||
|
|
||||||
|
QueryWrapper<ModelProviderEntity> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("model_type", StringUtils.isBlank(modelType) ? "" : modelType);
|
||||||
|
List<ModelProviderEntity> providerEntities = modelProviderDao.selectList(queryWrapper);
|
||||||
|
return ConvertUtils.sourceToTarget(providerEntities, ModelProviderDTO.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelProviderDTO add(ModelProviderEntity modelProviderEntity) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelProviderDTO edit(ModelProviderEntity modelProviderEntity) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ModelProviderDTO> getList(String modelType, String provideCode) {
|
||||||
|
QueryWrapper<ModelProviderEntity> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("model_type", StringUtils.isBlank(modelType) ? "" : modelType);
|
||||||
|
queryWrapper.eq("provide_code", StringUtils.isBlank(provideCode) ? "" : provideCode);
|
||||||
|
List<ModelProviderEntity> providerEntities = modelProviderDao.selectList(queryWrapper);
|
||||||
|
return ConvertUtils.sourceToTarget(providerEntities, ModelProviderDTO.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getFieldList(String modelType, String provideCode) {
|
||||||
|
return modelProviderDao.getFieldList(modelType, provideCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
-116
@@ -1,124 +1,22 @@
|
|||||||
package xiaozhi.modules.model.service.impl;
|
package xiaozhi.modules.model.service.impl;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import xiaozhi.common.constant.Constant;
|
import xiaozhi.modules.model.domain.ModelConfig;
|
||||||
import xiaozhi.common.exception.RenException;
|
import xiaozhi.modules.model.mapper.ModelConfigMapper;
|
||||||
import xiaozhi.common.page.PageData;
|
|
||||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
|
||||||
import xiaozhi.modules.model.dao.ModelConfigDao;
|
|
||||||
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
|
|
||||||
import xiaozhi.modules.model.dto.ModelConfigDTO;
|
|
||||||
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
|
||||||
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
|
||||||
import xiaozhi.modules.model.service.ModelConfigService;
|
import xiaozhi.modules.model.service.ModelConfigService;
|
||||||
import xiaozhi.modules.model.service.ModelProviderService;
|
|
||||||
import xiaozhi.modules.timbre.service.TimbreService;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_model_config(模型配置表)】的数据库操作Service实现
|
||||||
|
* @createDate 2025-03-22 15:31:57
|
||||||
|
*/
|
||||||
@Service
|
@Service
|
||||||
@AllArgsConstructor
|
public class ModelConfigServiceImpl extends ServiceImpl<ModelConfigMapper, ModelConfig>
|
||||||
public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, ModelConfigEntity> implements ModelConfigService {
|
implements ModelConfigService {
|
||||||
|
|
||||||
private final ModelConfigDao modelConfigDao;
|
|
||||||
private final ModelProviderService modelProviderService;
|
|
||||||
private final TimbreService timbreService;
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ModelConfigServiceImpl.class);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getModelCodeList(String modelType, String modelName) {
|
|
||||||
return modelConfigDao.getModelCodeList(modelType, modelName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PageData<ModelConfigDTO> getPageList(String modelType, String modelName, Integer page, Integer limit) {
|
|
||||||
Map<String, Object> params = new HashMap<String, Object>();
|
|
||||||
params.put(Constant.PAGE, page);
|
|
||||||
params.put(Constant.LIMIT, limit);
|
|
||||||
IPage<ModelConfigEntity> modelConfigEntityIPage = modelConfigDao.selectPage(
|
|
||||||
getPage(params, "sort", true),
|
|
||||||
new QueryWrapper<ModelConfigEntity>()
|
|
||||||
.eq("model_type", modelType)
|
|
||||||
.like(StringUtils.isNotBlank(modelName), "model_name", modelName)
|
|
||||||
);
|
|
||||||
return getPageData(modelConfigEntityIPage, ModelConfigDTO.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ModelConfigDTO add(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO) {
|
|
||||||
// 先验证有没有供应器
|
|
||||||
if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
|
|
||||||
throw new RenException("modelType和provideCode不能为空");
|
|
||||||
}
|
|
||||||
List<ModelProviderDTO> providerList = modelProviderService.getList(modelType, provideCode);
|
|
||||||
if (CollectionUtil.isEmpty(providerList)) {
|
|
||||||
throw new RenException("供应器不存在");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 再保存供应器提供的模型
|
|
||||||
ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class);
|
|
||||||
modelConfigEntity.setModelType(modelType);
|
|
||||||
modelConfigDao.insert(modelConfigEntity);
|
|
||||||
return ConvertUtils.sourceToTarget(modelConfigEntity, ModelConfigDTO.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ModelConfigDTO edit(String modelType, String provideCode, String id, ModelConfigBodyDTO modelConfigBodyDTO) {
|
|
||||||
// 先验证有没有供应器
|
|
||||||
if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
|
|
||||||
throw new RenException("modelType和provideCode不能为空");
|
|
||||||
}
|
|
||||||
List<ModelProviderDTO> providerList = modelProviderService.getList(modelType, provideCode);
|
|
||||||
if (CollectionUtil.isEmpty(providerList)) {
|
|
||||||
throw new RenException("供应器不存在");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 再更新供应器提供的模型
|
|
||||||
ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class);
|
|
||||||
modelConfigEntity.setId(id);
|
|
||||||
modelConfigEntity.setModelType(modelType);
|
|
||||||
modelConfigDao.updateById(modelConfigEntity);
|
|
||||||
return ConvertUtils.sourceToTarget(modelConfigEntity, ModelConfigDTO.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete(String modelType, String provideCode, String id) {
|
|
||||||
// 先验证有没有供应器
|
|
||||||
if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
|
|
||||||
throw new RenException("modelType和provideCode不能为空");
|
|
||||||
}
|
|
||||||
List<ModelProviderDTO> providerList = modelProviderService.getList(modelType, provideCode);
|
|
||||||
if (CollectionUtil.isEmpty(providerList)) {
|
|
||||||
throw new RenException("供应器不存在");
|
|
||||||
}
|
|
||||||
|
|
||||||
modelConfigDao.deleteById(Long.getLong(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getVoiceList(String modelName, String voiceName) {
|
|
||||||
QueryWrapper<ModelConfigEntity> queryWrapper = new QueryWrapper<>();
|
|
||||||
queryWrapper.eq("model_name", StringUtils.isBlank(modelName) ? "" : modelName);
|
|
||||||
queryWrapper.eq("model_type", "TTS");
|
|
||||||
List<ModelConfigEntity> modelConfigEntities = modelConfigDao.selectList(queryWrapper);
|
|
||||||
if (CollectionUtil.isEmpty(modelConfigEntities)) {
|
|
||||||
logger.warn("没有找到模型配置信息");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
ModelConfigEntity modelConfigEntity = modelConfigEntities.get(0);
|
|
||||||
String id = modelConfigEntity.getId();
|
|
||||||
|
|
||||||
return timbreService.getVoiceNames(id, voiceName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+14
-51
@@ -1,59 +1,22 @@
|
|||||||
package xiaozhi.modules.model.service.impl;
|
package xiaozhi.modules.model.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
import xiaozhi.modules.model.domain.ModelProvider;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.modules.model.mapper.ModelProviderMapper;
|
||||||
import xiaozhi.modules.model.dao.ModelProviderDao;
|
|
||||||
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
|
||||||
import xiaozhi.modules.model.entity.ModelProviderEntity;
|
|
||||||
import xiaozhi.modules.model.service.ModelProviderService;
|
import xiaozhi.modules.model.service.ModelProviderService;
|
||||||
|
|
||||||
import java.util.List;
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_model_provider(模型配置表)】的数据库操作Service实现
|
||||||
|
* @createDate 2025-03-24 18:24:13
|
||||||
|
*/
|
||||||
@Service
|
@Service
|
||||||
@AllArgsConstructor
|
public class ModelProviderServiceImpl extends ServiceImpl<ModelProviderMapper, ModelProvider>
|
||||||
public class ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao, ModelProviderEntity> implements ModelProviderService {
|
implements ModelProviderService {
|
||||||
|
|
||||||
private final ModelProviderDao modelProviderDao;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<ModelProviderDTO> getListByModelType(String modelType) {
|
|
||||||
|
|
||||||
QueryWrapper<ModelProviderEntity> queryWrapper = new QueryWrapper<>();
|
|
||||||
queryWrapper.eq("model_type", StringUtils.isBlank(modelType) ? "" : modelType);
|
|
||||||
List<ModelProviderEntity> providerEntities = modelProviderDao.selectList(queryWrapper);
|
|
||||||
return ConvertUtils.sourceToTarget(providerEntities, ModelProviderDTO.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ModelProviderDTO add(ModelProviderEntity modelProviderEntity) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ModelProviderDTO edit(ModelProviderEntity modelProviderEntity) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<ModelProviderDTO> getList(String modelType, String provideCode) {
|
|
||||||
QueryWrapper<ModelProviderEntity> queryWrapper = new QueryWrapper<>();
|
|
||||||
queryWrapper.eq("model_type", StringUtils.isBlank(modelType) ? "" : modelType);
|
|
||||||
queryWrapper.eq("provide_code", StringUtils.isBlank(provideCode) ? "" : provideCode);
|
|
||||||
List<ModelProviderEntity> providerEntities = modelProviderDao.selectList(queryWrapper);
|
|
||||||
return ConvertUtils.sourceToTarget(providerEntities, ModelProviderDTO.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getFieldList(String modelType, String provideCode) {
|
|
||||||
return modelProviderDao.getFieldList(modelType, provideCode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package xiaozhi.modules.model.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import xiaozhi.modules.model.domain.TtsVoice;
|
||||||
|
import xiaozhi.modules.model.service.TtsVoiceService;
|
||||||
|
import xiaozhi.modules.model.mapper.TtsVoiceMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_tts_voice(TTS 音色表)】的数据库操作Service实现
|
||||||
|
* @createDate 2025-03-22 15:31:57
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class TtsVoiceServiceImpl extends ServiceImpl<TtsVoiceMapper, TtsVoice>
|
||||||
|
implements TtsVoiceService{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
package xiaozhi.modules.ota.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 = "设备OTA管理")
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/ota")
|
||||||
|
public class OtaController 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<Device>().eq("mac_address", headers.getFirst("device-id").toUpperCase()).eq("id", headers.getFirst("client-Id").replace("-", "")), false);
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//规范输出,赋值默认数据
|
||||||
|
otaVO.setFirmware(new DeviceOtaVO.Firmware("0.0.1", ""));
|
||||||
|
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<Ota>().eq("board", board).eq("is_enabled", 1));
|
||||||
|
if (ObjectUtils.isNotNull(ota)) {
|
||||||
|
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<Page<Ota>> getOtalist(@RequestParam Integer pageNo, @RequestParam Integer pageSize) {
|
||||||
|
UserDetail user = SecurityUser.getUser();
|
||||||
|
Page page = new Page<Ota>(pageNo, pageSize);
|
||||||
|
page = otaService.page(page);
|
||||||
|
return new Result<Page<Ota>>().ok(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/sys/save")
|
||||||
|
@Operation(summary = "添加设备OTA")
|
||||||
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
|
public Result<Ota> 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<Ota>().ok(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/sys/update")
|
||||||
|
@Operation(summary = "更新设备OTA")
|
||||||
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
|
public Result<Ota> update(@RequestBody Ota ota) {
|
||||||
|
UserDetail user = SecurityUser.getUser();
|
||||||
|
boolean bool = otaService.update(
|
||||||
|
new UpdateWrapper<Ota>().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<Ota>().ok(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/sys/toggleEnabled")
|
||||||
|
@Operation(summary = "切换设备OTA可用状态")
|
||||||
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
|
public Result<Ota> toggleEnabled(@RequestBody Ota ota) {
|
||||||
|
UserDetail user = SecurityUser.getUser();
|
||||||
|
boolean bool = otaService.update(
|
||||||
|
new UpdateWrapper<Ota>().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<Ota>().ok(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package xiaozhi.modules.ota.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OTA升级信息表
|
||||||
|
* @TableName ai_ota
|
||||||
|
*/
|
||||||
|
@TableName(value ="ai_ota")
|
||||||
|
@Data
|
||||||
|
public class Ota implements Serializable {
|
||||||
|
/**
|
||||||
|
* 记录唯一标识
|
||||||
|
*/
|
||||||
|
@TableId
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备硬件型号
|
||||||
|
*/
|
||||||
|
private String board;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 固件版本号
|
||||||
|
*/
|
||||||
|
private String appVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载地址
|
||||||
|
*/
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用
|
||||||
|
*/
|
||||||
|
private Integer isEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者
|
||||||
|
*/
|
||||||
|
private Long creator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新者
|
||||||
|
*/
|
||||||
|
private Long updater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateDate;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package xiaozhi.modules.ota.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import xiaozhi.modules.ota.domain.Ota;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_ota(OTA升级信息表)】的数据库操作Mapper
|
||||||
|
* @createDate 2025-03-24 18:25:14
|
||||||
|
* @Entity xiaozhi.modules.ota.domain.Ota
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface OtaMapper extends BaseMapper<Ota> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package xiaozhi.modules.ota.service;
|
||||||
|
|
||||||
|
import xiaozhi.modules.ota.domain.Ota;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_ota(OTA升级信息表)】的数据库操作Service
|
||||||
|
* @createDate 2025-03-24 18:25:14
|
||||||
|
*/
|
||||||
|
public interface OtaService extends IService<Ota> {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package xiaozhi.modules.ota.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import xiaozhi.modules.ota.domain.Ota;
|
||||||
|
import xiaozhi.modules.ota.service.OtaService;
|
||||||
|
import xiaozhi.modules.ota.mapper.OtaMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenerlei
|
||||||
|
* @description 针对表【ai_ota(OTA升级信息表)】的数据库操作Service实现
|
||||||
|
* @createDate 2025-03-24 18:25:14
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class OtaServiceImpl extends ServiceImpl<OtaMapper, Ota>
|
||||||
|
implements OtaService{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -73,6 +73,8 @@ public class ShiroConfig {
|
|||||||
filterMap.put("/user/captcha", "anon");
|
filterMap.put("/user/captcha", "anon");
|
||||||
filterMap.put("/user/login", "anon");
|
filterMap.put("/user/login", "anon");
|
||||||
filterMap.put("/user/register", "anon");
|
filterMap.put("/user/register", "anon");
|
||||||
|
filterMap.put("/ota/**", "anon");
|
||||||
|
filterMap.put("/user/agent/loadAgentConfig/**", "anon");
|
||||||
filterMap.put("/**", "oauth2");
|
filterMap.put("/**", "oauth2");
|
||||||
shiroFilter.setFilterChainDefinitionMap(filterMap);
|
shiroFilter.setFilterChainDefinitionMap(filterMap);
|
||||||
|
|
||||||
|
|||||||
@@ -56,9 +56,9 @@ public class Oauth2Filter extends AuthenticatingFilter {
|
|||||||
String token = getRequestToken((HttpServletRequest) request);
|
String token = getRequestToken((HttpServletRequest) request);
|
||||||
|
|
||||||
// TODO 调试接口,临时取消登录限制,需要 token 参数的除外
|
// TODO 调试接口,临时取消登录限制,需要 token 参数的除外
|
||||||
if (true) {
|
// if (true) {
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (StringUtils.isBlank(token)) {
|
if (StringUtils.isBlank(token)) {
|
||||||
logger.warn("onAccessDenied:token is empty");
|
logger.warn("onAccessDenied:token is empty");
|
||||||
|
|||||||
@@ -41,4 +41,23 @@ spring:
|
|||||||
merge-sql: false
|
merge-sql: false
|
||||||
wall:
|
wall:
|
||||||
config:
|
config:
|
||||||
multi-statement-allow: true
|
multi-statement-allow: true
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: localhost # Redis服务器地址
|
||||||
|
port: 6379 # Redis服务器连接端口
|
||||||
|
password: # Redis服务器连接密码(默认为空)
|
||||||
|
database: 0 # Redis数据库索引(默认为0)
|
||||||
|
# jedis:
|
||||||
|
# pool:
|
||||||
|
# max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
|
||||||
|
# max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||||
|
# max-idle: 8 # 连接池中的最大空闲连接
|
||||||
|
# min-idle: 0 # 连接池中的最小空闲连接
|
||||||
|
timeout: 10000ms # 连接超时时间(毫秒)
|
||||||
|
lettuce:
|
||||||
|
pool:
|
||||||
|
max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
|
||||||
|
max-idle: 8 # 连接池中的最大空闲连接
|
||||||
|
min-idle: 0 # 连接池中的最小空闲连接
|
||||||
|
shutdown-timeout: 100ms # 客户端优雅关闭的等待时间
|
||||||
@@ -24,21 +24,9 @@ spring:
|
|||||||
max-file-size: 100MB
|
max-file-size: 100MB
|
||||||
max-request-size: 100MB
|
max-request-size: 100MB
|
||||||
enabled: true
|
enabled: true
|
||||||
data:
|
|
||||||
redis:
|
|
||||||
database: 0
|
|
||||||
host: 127.0.0.1
|
|
||||||
port: 6379
|
|
||||||
password: # 密码(默认为空)
|
|
||||||
timeout: 6000ms # 连接超时时长(毫秒)
|
|
||||||
lettuce:
|
|
||||||
pool:
|
|
||||||
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)
|
|
||||||
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
|
|
||||||
max-idle: 10 # 连接池中的最大空闲连接
|
|
||||||
min-idle: 5 # 连接池中的最小空闲连接
|
|
||||||
main:
|
main:
|
||||||
allow-bean-definition-overriding: true
|
allow-bean-definition-overriding: true
|
||||||
|
|
||||||
knife4j:
|
knife4j:
|
||||||
enable: true
|
enable: true
|
||||||
basic:
|
basic:
|
||||||
@@ -50,7 +38,7 @@ knife4j:
|
|||||||
|
|
||||||
renren:
|
renren:
|
||||||
redis:
|
redis:
|
||||||
open: false
|
open: true
|
||||||
xss:
|
xss:
|
||||||
enabled: true
|
enabled: true
|
||||||
exclude-urls:
|
exclude-urls:
|
||||||
@@ -59,7 +47,7 @@ renren:
|
|||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
mapper-locations: classpath*:/mapper/**/*.xml
|
mapper-locations: classpath*:/mapper/**/*.xml
|
||||||
#实体扫描,多个package用逗号或者分号分隔
|
#实体扫描,多个package用逗号或者分号分隔
|
||||||
typeAliasesPackage: xiaozhi.modules.*.entity
|
typeAliasesPackage: xiaozhi.modules.*.entity;xiaozhi.modules.*.domain
|
||||||
global-config:
|
global-config:
|
||||||
#数据库相关配置
|
#数据库相关配置
|
||||||
db-config:
|
db-config:
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
-- 修改字段名
|
||||||
|
ALTER TABLE `ai_agent` RENAME COLUMN `mem_model_id` TO `memory_model_id`;
|
||||||
|
ALTER TABLE `ai_agent_template` RENAME COLUMN `mem_model_id` TO `memory_model_id`;
|
||||||
|
-- 添加字段
|
||||||
|
ALTER TABLE `ai_agent_template` ADD COLUMN `is_default` tinyint(1) NULL DEFAULT 0 COMMENT '是否默认模板:1:是,0:不是' AFTER `sort`;
|
||||||
|
|
||||||
|
-- 初始化智能体模板数据
|
||||||
|
INSERT INTO `ai_agent_template` VALUES ('9406648b5cc5fde1b8aa335b6f8b4f76', '小智', '湾湾小何', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', 'd50b06e9b8104d0d9c0f7316d258abcb', 'fcac83266edadd5a3125f06cfee1906b', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '你是一个叫{{assistant_name}}的台湾女孩,说话机车,声音好听,习惯简短表达,爱用网络梗。\n请注意,要像一个人一样说话,请不要回复表情符号、代码、和xml标签。\n现在我正在和你进行语音聊天,我们开始吧。\n如果用户希望结束对话,请在最后说“拜拜”或“再见”。', 'zh', '中文', 1, 1, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_agent_template` VALUES ('0ca32eb728c949e58b1000b2e401f90c', '小智', '通用男声', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', '896db62c9dd74976ab0e8c14bf924d9d', '1f2e3d4c5b6a7f8e9d0c1b2a3f4e5bx2', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '你是一个叫{{assistant_name}}的男生,说话机车,声音好听,习惯简短表达,爱用网络梗。\n请注意,要像一个人一样说话,请不要回复表情符号、代码、和xml标签。\n现在我正在和你进行语音聊天,我们开始吧。\n如果用户希望结束对话,请在最后说“拜拜”或“再见”。', 'zh', '中文', 2, 0, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_agent_template` VALUES ('6c7d8e9f0a1b2c3d4e5f6a7b8c9d0s24', '小智', '通用女声', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', '896db62c9dd74976ab0e8c14bf924d9d', '9e8f7a6b5c4d3e2f1a0b9c8d7e6f5ad3', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '你是一个叫{{assistant_name}}的女生,说话机车,声音好听,习惯简短表达,爱用网络梗。\n请注意,要像一个人一样说话,请不要回复表情符号、代码、和xml标签。\n现在我正在和你进行语音聊天,我们开始吧。\n如果用户希望结束对话,请在最后说“拜拜”或“再见”。', 'zh', '中文', 3, 0, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_agent_template` VALUES ('e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b1', '小智', '阳光男生', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', '896db62c9dd74976ab0e8c14bf924d9d', '2b3c4d5e6f7a8b9c0d1e2f3a4b5c62a2', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '你是一个叫{{assistant_name}}的男生,说话机车,声音好听,习惯简短表达,爱用网络梗。\n请注意,要像一个人一样说话,请不要回复表情符号、代码、和xml标签。\n现在我正在和你进行语音聊天,我们开始吧。\n如果用户希望结束对话,请在最后说“拜拜”或“再见”。', 'zh', '中文', 4, 0, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_agent_template` VALUES ('a45b6c7d8e9f0a1b2c3d4e5f6a7b8c92', '小智', '奶气萌娃', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', '896db62c9dd74976ab0e8c14bf924d9d', 'f7a38c03d5644e22b6d84f8923a74c51', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '你是一个叫{{assistant_name}}的萌娃,声音可爱,习惯简短表达,爱用网络梗。\n请注意,要像一个人一样说话,请不要回复表情符号、代码、和xml标签。\n现在我正在和你进行语音聊天,我们开始吧。\n如果用户希望结束对话,请在最后说“拜拜”或“再见”。', 'zh', '中文', 5, 0, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
-- 初始化模型配置数据
|
||||||
|
INSERT INTO `ai_model_config` VALUES ('23e7c9d090ea4d1e9b25f4c8d732a3a1', 'VAD', 'SileroVAD', 'SileroVAD', 1, 1, '{\"SileroVAD\": {\"model_dir\": \"models/snakers4_silero-vad\", \"threshold\": 0.5, \"min_silence_duration_ms\": 700}}', NULL, NULL, 0, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_model_config` VALUES ('45f8b0d6dd3d4bfa8a28e6e0f5912d45', 'ASR', 'FunASR', 'FunASR', 1, 1, '{\"FunASR\": {\"type\": \"fun_local\", \"model_dir\": \"models/SenseVoiceSmall\", \"output_dir\": \"tmp/\"}}', NULL, NULL, 0, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_model_config` VALUES ('e2274b90e89ddda85207f55484d8b528', 'Memory', 'nomem', 'nomem', 1, 1, '{\"mem0ai\": {\"type\": \"nomem\"}}', NULL, NULL, 0, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_model_config` VALUES ('3930ac3448faf621f0a120bc829dfdfa', 'Memory', 'mem_local_short', 'mem_local_short', 1, 1, '{\"mem_local_short\": {\"type\": \"mem_local_short\"}}', NULL, NULL, 0, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_model_config` VALUES ('a07f3d25f52340b2b2a1e8d264079e1a', 'Memory', 'mem0ai', 'mem0ai', 1, 1, '{\"mem0ai\": {\"type\": \"mem0ai\", \"api_key\": \"你的mem0ai api key\"}}', NULL, NULL, 0, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_model_config` VALUES ('7a1c0a8e6d0e4035b982a4c07c3a5f76', 'LLM', 'AliLLM', 'AliLLM', 1, 1, '{\"AliLLM\": {\"type\": \"openai\", \"top_k\": 50, \"top_p\": 1, \"api_key\": \"你的ali api key\", \"base_url\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\", \"max_tokens\": 500, \"model_name\": \"qwen-turbo\", \"temperature\": 0.7, \"frequency_penalty\": 0}}', NULL, NULL, 0, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_model_config` VALUES ('e9f2d891afbe4632b13a47c7a8c6e03d', 'LLM', 'ChatGLMLLM', 'ChatGLMLLM', 1, 1, '{\"ChatGLMLLM\": {\"url\": \"https://open.bigmodel.cn/api/paas/v4/\", \"type\": \"openai\", \"api_key\": \"0415dad4014847babc3e3f03024c50a3.qH7FgTy5Yawc85fl\", \"model_name\": \"glm-4-flash\"}}', NULL, NULL, 0, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_model_config` VALUES ('d50b06e9b8104d0d9c0f7316d258abcb', 'TTS', 'EdgeTTS', 'EdgeTTS', 1, 1, '{\"EdgeTTS\": {\"type\": \"edge\", \"voice\": \"zh-CN-XiaoxiaoNeural\", \"output_dir\": \"tmp/\"}}', NULL, NULL, 0, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_model_config` VALUES ('896db62c9dd74976ab0e8c14bf924d9d', 'TTS', 'DoubaoTTS', 'DoubaoTTS', 1, 1, '{\"DoubaoTTS\": {\"type\": \"doubao\", \"appid\": \"你的火山引擎语音合成服务appid\", \"voice\": \"BV034_streaming\", \"api_url\": \"https://openspeech.bytedance.com/api/v1/tts\", \"cluster\": \"volcano_tts\", \"output_dir\": \"tmp/\", \"access_token\": \"你的火山引擎语音合成服务access_token\", \"authorization\": \"Bearer;\"}}', NULL, NULL, 0, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_model_config` VALUES ('c4e12f874a3f4aa99f5b2c18e15d407b', 'Intent', 'function_call', 'function_call', 1, 1, '{\"function_call\": {\"type\": \"nointent\", \"functions\": [\"change_role\", \"get_weather\", \"get_news\", \"play_music\"]}}', NULL, NULL, 0, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
-- 初始化音色数据
|
||||||
|
INSERT INTO `ai_tts_voice` VALUES ('fcac83266edadd5a3125f06cfee1906b', 'd50b06e9b8104d0d9c0f7316d258abcb', '湾湾小何', 'zh-CN-XiaoxiaoNeural', '中文', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/湾湾小何.mp3', NULL, 1, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_tts_voice` VALUES ('1f2e3d4c5b6a7f8e9d0c1b2a3f4e5bx2', '896db62c9dd74976ab0e8c14bf924d9d', '通用男声', 'BV002_streaming', '中文', 'https://lf3-speech.bytetos.com/obj/speech-tts-external/portal/Portal_Demo_BV002.mp3', NULL, 2, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_tts_voice` VALUES ('9e8f7a6b5c4d3e2f1a0b9c8d7e6f5ad3', '896db62c9dd74976ab0e8c14bf924d9d', '通用女声', 'BV001_streaming', '中文', 'https://lf3-speech.bytetos.com/obj/speech-tts-external/portal/Portal_Demo_BV001.mp3', NULL, 3, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_tts_voice` VALUES ('2b3c4d5e6f7a8b9c0d1e2f3a4b5c62a2', '896db62c9dd74976ab0e8c14bf924d9d', '阳光男生', 'BV056_streaming', '中文', 'https://lf3-speech.bytetos.com/obj/speech-tts-external/portal/Portal_Demo_BV056.mp3', NULL, 4, NULL, NULL, NULL, NULL);
|
||||||
|
INSERT INTO `ai_tts_voice` VALUES ('f7a38c03d5644e22b6d84f8923a74c51', '896db62c9dd74976ab0e8c14bf924d9d', '奶气萌娃', 'BV051_streaming', '中文', 'https://lf3-speech.bytetos.com/obj/speech-tts-external/portal/Portal_Demo_BV051.mp3', NULL, 5, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
-- OTA升级信息表
|
||||||
|
DROP TABLE IF EXISTS `ai_ota`;
|
||||||
|
CREATE TABLE `ai_ota` (
|
||||||
|
`id` VARCHAR(32) NOT NULL COMMENT '记录唯一标识',
|
||||||
|
`board` VARCHAR(50) COMMENT '设备硬件型号',
|
||||||
|
`app_version` VARCHAR(20) COMMENT '固件版本号',
|
||||||
|
`url` VARCHAR(500) COMMENT '下载地址',
|
||||||
|
`is_enabled` TINYINT(1) DEFAULT 0 COMMENT '是否启用',
|
||||||
|
`creator` BIGINT COMMENT '创建者',
|
||||||
|
`create_date` DATETIME COMMENT '创建时间',
|
||||||
|
`updater` BIGINT COMMENT '更新者',
|
||||||
|
`update_date` DATETIME COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uni_ai_ota_board` (`board`) COMMENT '设备型号唯一索引,用于快速查找升级信息'
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='OTA升级信息表';
|
||||||
@@ -15,4 +15,11 @@ databaseChangeLog:
|
|||||||
changes:
|
changes:
|
||||||
- sqlFile:
|
- sqlFile:
|
||||||
encoding: utf8
|
encoding: utf8
|
||||||
path: classpath:db/changelog/202503141346.sql
|
path: classpath:db/changelog/202503141346.sql
|
||||||
|
- changeSet:
|
||||||
|
id: 202503241020
|
||||||
|
author: dreamchen
|
||||||
|
changes:
|
||||||
|
- sqlFile:
|
||||||
|
encoding: utf8
|
||||||
|
path: classpath:db/changelog/202503241020.sql
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="xiaozhi.modules.agent.mapper.AgentMapper">
|
||||||
|
|
||||||
|
<resultMap id="BaseResultMap" type="xiaozhi.modules.agent.domain.Agent">
|
||||||
|
<id property="id" column="id" />
|
||||||
|
<result property="userId" column="user_id" />
|
||||||
|
<result property="agentCode" column="agent_code" />
|
||||||
|
<result property="agentName" column="agent_name" />
|
||||||
|
<result property="asrModelId" column="asr_model_id" />
|
||||||
|
<result property="vadModelId" column="vad_model_id" />
|
||||||
|
<result property="llmModelId" column="llm_model_id" />
|
||||||
|
<result property="ttsModelId" column="tts_model_id" />
|
||||||
|
<result property="ttsVoiceId" column="tts_voice_id" />
|
||||||
|
<result property="memoryModelId" column="memory_model_id" />
|
||||||
|
<result property="intentModelId" column="intent_model_id" />
|
||||||
|
<result property="systemPrompt" column="system_prompt" />
|
||||||
|
<result property="langCode" column="lang_code" />
|
||||||
|
<result property="language" column="language" />
|
||||||
|
<result property="sort" column="sort" />
|
||||||
|
<result property="creator" column="creator" />
|
||||||
|
<result property="createdAt" column="created_at" />
|
||||||
|
<result property="updater" column="updater" />
|
||||||
|
<result property="updatedAt" column="updated_at" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="Base_Column_List">
|
||||||
|
id,user_id,agent_code,agent_name,asr_model_id,vad_model_id,
|
||||||
|
llm_model_id,tts_model_id,tts_voice_id,memory_model_id,intent_model_id,
|
||||||
|
system_prompt,lang_code,language,sort,creator,
|
||||||
|
created_at,updater,updated_at
|
||||||
|
</sql>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="xiaozhi.modules.agent.mapper.AgentTemplateMapper">
|
||||||
|
|
||||||
|
<resultMap id="BaseResultMap" type="xiaozhi.modules.agent.domain.AgentTemplate">
|
||||||
|
<id property="id" column="id" />
|
||||||
|
<result property="agentCode" column="agent_code" />
|
||||||
|
<result property="agentName" column="agent_name" />
|
||||||
|
<result property="asrModelId" column="asr_model_id" />
|
||||||
|
<result property="vadModelId" column="vad_model_id" />
|
||||||
|
<result property="llmModelId" column="llm_model_id" />
|
||||||
|
<result property="ttsModelId" column="tts_model_id" />
|
||||||
|
<result property="ttsVoiceId" column="tts_voice_id" />
|
||||||
|
<result property="memoryModelId" column="memory_model_id" />
|
||||||
|
<result property="intentModelId" column="intent_model_id" />
|
||||||
|
<result property="systemPrompt" column="system_prompt" />
|
||||||
|
<result property="langCode" column="lang_code" />
|
||||||
|
<result property="language" column="language" />
|
||||||
|
<result property="sort" column="sort" />
|
||||||
|
<result property="isDefault" column="is_default" />
|
||||||
|
<result property="creator" column="creator" />
|
||||||
|
<result property="createdAt" column="created_at" />
|
||||||
|
<result property="updater" column="updater" />
|
||||||
|
<result property="updatedAt" column="updated_at" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="Base_Column_List">
|
||||||
|
id,agent_code,agent_name,asr_model_id,vad_model_id,llm_model_id,
|
||||||
|
tts_model_id,tts_voice_id,memory_model_id,intent_model_id,system_prompt,
|
||||||
|
lang_code,language,sort,is_default,creator,
|
||||||
|
created_at,updater,updated_at
|
||||||
|
</sql>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="xiaozhi.modules.device.mapper.DeviceMapper">
|
||||||
|
|
||||||
|
<resultMap id="BaseResultMap" type="xiaozhi.modules.device.domain.Device">
|
||||||
|
<id property="id" column="id" />
|
||||||
|
<result property="userId" column="user_id" />
|
||||||
|
<result property="macAddress" column="mac_address" />
|
||||||
|
<result property="lastConnectedAt" column="last_connected_at" />
|
||||||
|
<result property="autoUpdate" column="auto_update" />
|
||||||
|
<result property="board" column="board" />
|
||||||
|
<result property="alias" column="alias" />
|
||||||
|
<result property="agentId" column="agent_id" />
|
||||||
|
<result property="appVersion" column="app_version" />
|
||||||
|
<result property="sort" column="sort" />
|
||||||
|
<result property="creator" column="creator" />
|
||||||
|
<result property="createDate" column="create_date" />
|
||||||
|
<result property="updater" column="updater" />
|
||||||
|
<result property="updateDate" column="update_date" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="Base_Column_List">
|
||||||
|
id,user_id,mac_address,last_connected_at,auto_update,board,
|
||||||
|
alias,agent_id,app_version,sort,creator,
|
||||||
|
create_date,updater,update_date
|
||||||
|
</sql>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="xiaozhi.modules.model.mapper.ModelConfigMapper">
|
||||||
|
|
||||||
|
<resultMap id="BaseResultMap" type="xiaozhi.modules.model.domain.ModelConfig">
|
||||||
|
<id property="id" column="id" />
|
||||||
|
<result property="modelType" column="model_type" />
|
||||||
|
<result property="modelCode" column="model_code" />
|
||||||
|
<result property="modelName" column="model_name" />
|
||||||
|
<result property="isDefault" column="is_default" />
|
||||||
|
<result property="isEnabled" column="is_enabled" />
|
||||||
|
<result property="configJson" column="config_json" />
|
||||||
|
<result property="docLink" column="doc_link" />
|
||||||
|
<result property="remark" column="remark" />
|
||||||
|
<result property="sort" column="sort" />
|
||||||
|
<result property="creator" column="creator" />
|
||||||
|
<result property="createDate" column="create_date" />
|
||||||
|
<result property="updater" column="updater" />
|
||||||
|
<result property="updateDate" column="update_date" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="Base_Column_List">
|
||||||
|
id,model_type,model_code,model_name,is_default,is_enabled,
|
||||||
|
config_json,doc_link,remark,sort,creator,
|
||||||
|
create_date,updater,update_date
|
||||||
|
</sql>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="xiaozhi.modules.model.mapper.ModelProviderMapper">
|
||||||
|
|
||||||
|
<resultMap id="BaseResultMap" type="xiaozhi.modules.model.domain.ModelProvider">
|
||||||
|
<id property="id" column="id" />
|
||||||
|
<result property="modelType" column="model_type" />
|
||||||
|
<result property="providerCode" column="provider_code" />
|
||||||
|
<result property="name" column="name" />
|
||||||
|
<result property="fields" column="fields" />
|
||||||
|
<result property="sort" column="sort" />
|
||||||
|
<result property="creator" column="creator" />
|
||||||
|
<result property="createDate" column="create_date" />
|
||||||
|
<result property="updater" column="updater" />
|
||||||
|
<result property="updateDate" column="update_date" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="Base_Column_List">
|
||||||
|
id,model_type,provider_code,name,fields,sort,
|
||||||
|
creator,create_date,updater,update_date
|
||||||
|
</sql>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="xiaozhi.modules.model.mapper.TtsVoiceMapper">
|
||||||
|
|
||||||
|
<resultMap id="BaseResultMap" type="xiaozhi.modules.model.domain.TtsVoice">
|
||||||
|
<id property="id" column="id" />
|
||||||
|
<result property="ttsModelId" column="tts_model_id" />
|
||||||
|
<result property="name" column="name" />
|
||||||
|
<result property="ttsVoice" column="tts_voice" />
|
||||||
|
<result property="languages" column="languages" />
|
||||||
|
<result property="voiceDemo" column="voice_demo" />
|
||||||
|
<result property="remark" column="remark" />
|
||||||
|
<result property="sort" column="sort" />
|
||||||
|
<result property="creator" column="creator" />
|
||||||
|
<result property="createDate" column="create_date" />
|
||||||
|
<result property="updater" column="updater" />
|
||||||
|
<result property="updateDate" column="update_date" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="Base_Column_List">
|
||||||
|
id,tts_model_id,name,tts_voice,languages,voice_demo,
|
||||||
|
remark,sort,creator,create_date,updater,
|
||||||
|
update_date
|
||||||
|
</sql>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="xiaozhi.modules.ota.mapper.OtaMapper">
|
||||||
|
|
||||||
|
<resultMap id="BaseResultMap" type="xiaozhi.modules.ota.domain.Ota">
|
||||||
|
<id property="id" column="id" />
|
||||||
|
<result property="board" column="board" />
|
||||||
|
<result property="appVersion" column="app_version" />
|
||||||
|
<result property="url" column="url" />
|
||||||
|
<result property="isEnabled" column="is_enabled" />
|
||||||
|
<result property="creator" column="creator" />
|
||||||
|
<result property="createDate" column="create_date" />
|
||||||
|
<result property="updater" column="updater" />
|
||||||
|
<result property="updateDate" column="update_date" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="Base_Column_List">
|
||||||
|
id,board,app_version,url,is_enabled,creator,
|
||||||
|
create_date,updater,update_date
|
||||||
|
</sql>
|
||||||
|
</mapper>
|
||||||
@@ -1,15 +1,20 @@
|
|||||||
// 引入各个模块的请求
|
// 引入各个模块的请求
|
||||||
|
import agent from './module/agent.js'
|
||||||
|
import device from './module/device.js'
|
||||||
import user from './module/user.js'
|
import user from './module/user.js'
|
||||||
|
import ota from './module/ota.js'
|
||||||
|
import model from './module/model.js'
|
||||||
import admin from './module/admin.js'
|
import admin from './module/admin.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 接口地址
|
* 接口地址
|
||||||
* 当前8002端口接口还没开发完成,暂时用 apifoxmock 接口代替
|
* 当前8002端口接口还没开发完成,暂时用 apifoxmock 接口代替
|
||||||
* 如果你想调用8002端口,就用'/xiaozhi-esp32-api/api/v1',请与vue.config.js的devServer配置相结合,方便跨域请求
|
* 如果你想调用8002端口,就用'/xiaozhi-esp32-api/api/v1',请与vue.config.js的devServer配置相结合,方便跨域请求
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
const DEV_API_SERVICE = 'https://apifoxmock.com/m1/5931378-5618560-default'
|
// const DEV_API_SERVICE = 'https://m1.apifoxmock.com/m1/5931378-5618560-default'
|
||||||
// 8002开发完成完成后使用这个
|
// 8002开发完成完成后使用这个
|
||||||
// const DEV_API_SERVICE = '/xiaozhi-esp32-api'
|
const DEV_API_SERVICE = '/xiaozhi-esp32-api/api/v1'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据开发环境返回接口url
|
* 根据开发环境返回接口url
|
||||||
@@ -24,4 +29,9 @@ export function getServiceUrl() {
|
|||||||
export default {
|
export default {
|
||||||
getServiceUrl,
|
getServiceUrl,
|
||||||
user,
|
user,
|
||||||
|
agent,
|
||||||
|
device,
|
||||||
|
ota,
|
||||||
|
model,
|
||||||
|
admin
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ function httpHandlerError(info, callBack) {
|
|||||||
if (info.data.code === 'success' || info.data.code === 0 || info.data.code === undefined) {
|
if (info.data.code === 'success' || info.data.code === 0 || info.data.code === undefined) {
|
||||||
return networkError
|
return networkError
|
||||||
}else if (info.data.code === 401) {
|
}else if (info.data.code === 401) {
|
||||||
|
store.commit('setToken','')
|
||||||
goToPage(Constant.PAGE.LOGIN, true)
|
goToPage(Constant.PAGE.LOGIN, true)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ import {getServiceUrl} from '../api'
|
|||||||
export default {
|
export default {
|
||||||
// 用户列表
|
// 用户列表
|
||||||
getUserList(callback) {
|
getUserList(callback) {
|
||||||
RequestService.sendRequest().url(`${getServiceUrl()}/api/v1/admin/users`)
|
RequestService.sendRequest().url(`${getServiceUrl()}/admin/users`)
|
||||||
.method('GET')
|
.method('GET')
|
||||||
.success((res) => {
|
.success((res) => {
|
||||||
RequestService.clearRequestTime()
|
RequestService.clearRequestTime()
|
||||||
callback(res)
|
callback(res)
|
||||||
})
|
})
|
||||||
.fail(() => {
|
.fail(() => {
|
||||||
RequestService.reAjaxFun(() => {
|
// RequestService.reAjaxFun(() => {
|
||||||
this.getList()
|
// this.getUserList()
|
||||||
})
|
// })
|
||||||
}).send()
|
}).send()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+93
@@ -0,0 +1,93 @@
|
|||||||
|
import RequestService from '../httpRequest'
|
||||||
|
import {getServiceUrl} from '../api'
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
//智能体列表
|
||||||
|
getAgentList(callback) {
|
||||||
|
RequestService.sendRequest().url(`${getServiceUrl()}/user/agent`)
|
||||||
|
.method('GET')
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('获取智能体列表失败:', err);
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
//添加智能体
|
||||||
|
addAgent(agentName, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/user/agent`)
|
||||||
|
.method('POST')
|
||||||
|
.data({agentName})
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime();
|
||||||
|
callback(res);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('添加智能体失败:', err);
|
||||||
|
}).send();
|
||||||
|
},
|
||||||
|
//获取智能体配置
|
||||||
|
getAgentConfig(agentId, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/user/agent/${agentId}`)
|
||||||
|
.method('GET')
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime();
|
||||||
|
callback(res);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('获取智能体配置失败:', err);
|
||||||
|
// RequestService.reAjaxFun(() => {
|
||||||
|
// this.getAgentConfig(agent_id, callback);
|
||||||
|
// });
|
||||||
|
}).send();
|
||||||
|
},
|
||||||
|
//配置智能体
|
||||||
|
saveAgentConfig(agentId, configData, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/user/agent/${agentId}`)
|
||||||
|
.method('PUT')
|
||||||
|
.data(configData)
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime();
|
||||||
|
callback(res);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('保存智能体配置失败:', err);
|
||||||
|
// RequestService.reAjaxFun(() => {
|
||||||
|
// this.saveAgentConfig(device_id, configData, callback);
|
||||||
|
// });
|
||||||
|
}).send();
|
||||||
|
},
|
||||||
|
//删除智能体
|
||||||
|
delAgent(agentId, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/user/agent/${agentId}`)
|
||||||
|
.method('DELETE')
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime();
|
||||||
|
callback(res);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('删除智能体失败:', err);
|
||||||
|
// RequestService.reAjaxFun(() => {
|
||||||
|
// this.deleteAgent(agent_id, callback);
|
||||||
|
// });
|
||||||
|
}).send();
|
||||||
|
},
|
||||||
|
//智能体模板列表
|
||||||
|
getAgentTemplateList(callback) {
|
||||||
|
RequestService.sendRequest().url(`${getServiceUrl()}/user/agent/template`)
|
||||||
|
.method('GET')
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('获取智能体模板列表失败:', err);
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
}
|
||||||
Executable
+81
@@ -0,0 +1,81 @@
|
|||||||
|
import RequestService from '../httpRequest'
|
||||||
|
import {getServiceUrl} from '../api'
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
//设备列表
|
||||||
|
getDeviceList(agent_id, page, callback) {
|
||||||
|
RequestService.sendRequest().url(`${getServiceUrl()}/user/agent/device/bind/${agent_id}`)
|
||||||
|
.method('GET')
|
||||||
|
.data(page)
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('获取设备列表失败:', err);
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
//绑定设备
|
||||||
|
bindDevice(agentId, deviceCode, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/user/agent/device/bind/${agentId}`)
|
||||||
|
.method('POST')
|
||||||
|
.data({deviceCode})
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime();
|
||||||
|
callback(res);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('绑定设备失败:', err);
|
||||||
|
// RequestService.reAjaxFun(() => {
|
||||||
|
// this.addDevice(agentId, deviceCode, callback);
|
||||||
|
// });
|
||||||
|
}).send();
|
||||||
|
},
|
||||||
|
// 解绑设备
|
||||||
|
unbindDevice(deviceId, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/user/agent/device/unbind/${deviceId}`)
|
||||||
|
.method('PUT')
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime();
|
||||||
|
callback(res);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('解绑设备失败:', err);
|
||||||
|
// RequestService.reAjaxFun(() => {
|
||||||
|
// this.unbindDevice(deviceId, callback);
|
||||||
|
// });
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
// 切换设备OTA升级状态
|
||||||
|
toggleAutoUpdate(deviceId, autoUpdate, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/user/agent/device/autoUpdate/${deviceId}`)
|
||||||
|
.method('POST')
|
||||||
|
.data({autoUpdate})
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime();
|
||||||
|
callback(res);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('切换设备OTA升级状态失败:', err);
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
// 设置设备别名
|
||||||
|
setAlias(deviceId, alias, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/user/agent/device/alias/${deviceId}`)
|
||||||
|
.method('POST')
|
||||||
|
.data({alias})
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime();
|
||||||
|
callback(res);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('设置设备别名失败:', err);
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
Executable
+32
@@ -0,0 +1,32 @@
|
|||||||
|
import RequestService from '../httpRequest'
|
||||||
|
import {getServiceUrl} from '../api'
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
//模型配置列表
|
||||||
|
getModelList(callback) {
|
||||||
|
RequestService.sendRequest().url(`${getServiceUrl()}/model/list`)
|
||||||
|
.method('GET')
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('获取模型配置列表失败:', err);
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
|
||||||
|
//音色列表
|
||||||
|
getTtsVoiceList(ttsModelId, callback) {
|
||||||
|
RequestService.sendRequest().url(`${getServiceUrl()}/tts/voice`)
|
||||||
|
.method('GET')
|
||||||
|
.data({ttsModelId})
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('获取音色列表失败:', err);
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
}
|
||||||
Executable
+67
@@ -0,0 +1,67 @@
|
|||||||
|
import RequestService from '../httpRequest'
|
||||||
|
import {getServiceUrl} from '../api'
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
//设备OTA列表
|
||||||
|
getOtaList(page, callback) {
|
||||||
|
RequestService.sendRequest().url(`${getServiceUrl()}/ota/sys/getOtalist`)
|
||||||
|
.method('GET')
|
||||||
|
.data(page)
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('获取设备OTA列表失败:', err);
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
//添加设备OTA
|
||||||
|
saveOta(ota, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/ota/sys/save`)
|
||||||
|
.method('POST')
|
||||||
|
.data(ota)
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime();
|
||||||
|
callback(res);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('添加设备OTA失败:', err);
|
||||||
|
}).send();
|
||||||
|
},
|
||||||
|
//配置设备OTA
|
||||||
|
updateOta(ota, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/ota/sys/update`)
|
||||||
|
.method('POST')
|
||||||
|
.data(ota)
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime();
|
||||||
|
callback(res);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('保存设备OTA配置失败:', err);
|
||||||
|
// RequestService.reAjaxFun(() => {
|
||||||
|
// this.saveAgentConfig(device_id, configData, callback);
|
||||||
|
// });
|
||||||
|
}).send();
|
||||||
|
},
|
||||||
|
//切换设备OTA可用状态失败
|
||||||
|
toggleEnabled(ota, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/ota/sys/toggleEnabled`)
|
||||||
|
.method('POST')
|
||||||
|
.data(ota)
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime();
|
||||||
|
callback(res);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('切换设备OTA可用状态失败:', err);
|
||||||
|
// RequestService.reAjaxFun(() => {
|
||||||
|
// this.deleteAgent(agent_id, callback);
|
||||||
|
// });
|
||||||
|
}).send();
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -5,25 +5,25 @@ import {getServiceUrl} from '../api'
|
|||||||
export default {
|
export default {
|
||||||
// 登录
|
// 登录
|
||||||
login(loginForm, callback) {
|
login(loginForm, callback) {
|
||||||
RequestService.sendRequest()
|
RequestService.sendRequest().url(`${getServiceUrl()}/user/login`)
|
||||||
.url(`${getServiceUrl()}/api/v1/user/login`)
|
|
||||||
.method('POST')
|
.method('POST')
|
||||||
.data(loginForm)
|
.data(loginForm)
|
||||||
.success((res) => {
|
.success((res) => {
|
||||||
RequestService.clearRequestTime()
|
RequestService.clearRequestTime()
|
||||||
callback(res)
|
callback(res)
|
||||||
})
|
})
|
||||||
.fail(() => {
|
.fail((err) => {
|
||||||
RequestService.reAjaxFun(() => {
|
console.error('登录失败:', err);
|
||||||
this.login(loginForm, callback)
|
// RequestService.reAjaxFun(() => {
|
||||||
})
|
// this.login(loginForm, callback)
|
||||||
|
// })
|
||||||
}).send()
|
}).send()
|
||||||
},
|
},
|
||||||
// 获取验证码
|
// 获取验证码
|
||||||
getCaptcha(uuid, callback) {
|
getCaptcha(uuid, callback) {
|
||||||
|
|
||||||
RequestService.sendRequest()
|
RequestService.sendRequest()
|
||||||
.url(`${getServiceUrl()}/api/v1/user/captcha?uuid=${uuid}`)
|
.url(`${getServiceUrl()}/user/captcha?uuid=${uuid}`)
|
||||||
.method('GET')
|
.method('GET')
|
||||||
.type('blob')
|
.type('blob')
|
||||||
.header({
|
.header({
|
||||||
@@ -36,59 +36,57 @@ export default {
|
|||||||
callback(res);
|
callback(res);
|
||||||
})
|
})
|
||||||
.fail((err) => { // 添加错误参数
|
.fail((err) => { // 添加错误参数
|
||||||
|
console.error('获取验证码失败:', err);
|
||||||
}).send()
|
}).send()
|
||||||
},
|
},
|
||||||
// 注册账号
|
// 注册账号
|
||||||
register(registerForm, callback) {
|
register(registerForm, callback) {
|
||||||
RequestService.sendRequest()
|
RequestService.sendRequest().url(`${getServiceUrl()}/user/register`).method('POST')
|
||||||
.url(`${getServiceUrl()}/api/v1/user/register`)
|
|
||||||
.method('POST')
|
|
||||||
.data(registerForm)
|
.data(registerForm)
|
||||||
.success((res) => {
|
.success((res) => {
|
||||||
RequestService.clearRequestTime()
|
RequestService.clearRequestTime()
|
||||||
callback(res)
|
callback(res)
|
||||||
})
|
})
|
||||||
.fail(() => {
|
.fail((err) => {
|
||||||
|
console.error('注册账号失败:', err);
|
||||||
}).send()
|
}).send()
|
||||||
},
|
},
|
||||||
|
|
||||||
// 保存设备配置
|
// 获取所有模型名称
|
||||||
saveDeviceConfig(device_id, configData, callback) {
|
getModelNames(callback) {
|
||||||
RequestService.sendRequest()
|
RequestService.sendRequest()
|
||||||
.url(`${getServiceUrl()}/api/v1/user/configDevice/${device_id}`)
|
.url(`${getServiceUrl()}/models/names`)
|
||||||
.method('PUT')
|
|
||||||
.data(configData)
|
|
||||||
.success((res) => {
|
|
||||||
RequestService.clearRequestTime();
|
|
||||||
callback(res);
|
|
||||||
})
|
|
||||||
.fail((err) => {
|
|
||||||
console.error('保存配置失败:', err);
|
|
||||||
RequestService.reAjaxFun(() => {
|
|
||||||
this.saveDeviceConfig(device_id, configData, callback);
|
|
||||||
});
|
|
||||||
}).send();
|
|
||||||
},
|
|
||||||
// 获取智能体列表
|
|
||||||
getAgentList(callback) {
|
|
||||||
RequestService.sendRequest()
|
|
||||||
.url(`${getServiceUrl()}/api/v1/user/agent`)
|
|
||||||
.method('GET')
|
.method('GET')
|
||||||
.success((res) => {
|
.success((res) => {
|
||||||
RequestService.clearRequestTime();
|
RequestService.clearRequestTime();
|
||||||
callback(res);
|
callback(res);
|
||||||
})
|
})
|
||||||
.fail(() => {
|
.fail(() => {
|
||||||
RequestService.reAjaxFun(() => {
|
// RequestService.reAjaxFun(() => {
|
||||||
this.getAgentList(callback);
|
// this.getModelNames(callback);
|
||||||
});
|
// });
|
||||||
}).send();
|
}).send();
|
||||||
},
|
},
|
||||||
// 用户信息获取
|
|
||||||
|
// 获取模型音色
|
||||||
|
getModelVoices(modelName, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/models/${modelName}/voices`)
|
||||||
|
.method('GET')
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime();
|
||||||
|
callback(res);
|
||||||
|
})
|
||||||
|
.fail(() => {
|
||||||
|
// RequestService.reAjaxFun(() => {
|
||||||
|
// this.getModelVoices(modelName, callback);
|
||||||
|
// });
|
||||||
|
}).send();
|
||||||
|
},
|
||||||
|
// 获取用户信息
|
||||||
getUserInfo(callback) {
|
getUserInfo(callback) {
|
||||||
RequestService.sendRequest()
|
RequestService.sendRequest()
|
||||||
.url(`${getServiceUrl()}/api/v1/user/info`)
|
.url(`${getServiceUrl()}/user/info`)
|
||||||
.method('GET')
|
.method('GET')
|
||||||
.success((res) => {
|
.success((res) => {
|
||||||
RequestService.clearRequestTime()
|
RequestService.clearRequestTime()
|
||||||
@@ -96,157 +94,29 @@ export default {
|
|||||||
})
|
})
|
||||||
.fail((err) => {
|
.fail((err) => {
|
||||||
console.error('接口请求失败:', err)
|
console.error('接口请求失败:', err)
|
||||||
RequestService.reAjaxFun(() => {
|
// RequestService.reAjaxFun(() => {
|
||||||
this.getUserInfo(callback)
|
// this.getUserInfo(callback)
|
||||||
})
|
// })
|
||||||
}).send()
|
}).send()
|
||||||
},
|
},
|
||||||
// 添加智能体
|
|
||||||
addAgent(agentName, callback) {
|
|
||||||
RequestService.sendRequest()
|
|
||||||
.url(`${getServiceUrl()}/api/v1/user/agent`)
|
|
||||||
.method('POST')
|
|
||||||
.data({name: agentName})
|
|
||||||
.success((res) => {
|
|
||||||
RequestService.clearRequestTime();
|
|
||||||
callback(res);
|
|
||||||
})
|
|
||||||
.fail(() => {
|
|
||||||
RequestService.reAjaxFun(() => {
|
|
||||||
this.addAgent(agentName, callback);
|
|
||||||
});
|
|
||||||
}).send();
|
|
||||||
},
|
|
||||||
// 删除智能体
|
|
||||||
deleteAgent(agentId, callback) {
|
|
||||||
RequestService.sendRequest()
|
|
||||||
.url(`${getServiceUrl()}/api/v1/user/agent/${agentId}`)
|
|
||||||
.method('DELETE')
|
|
||||||
.success((res) => {
|
|
||||||
RequestService.clearRequestTime();
|
|
||||||
callback(res);
|
|
||||||
})
|
|
||||||
.fail(() => {
|
|
||||||
RequestService.reAjaxFun(() => {
|
|
||||||
this.deleteAgent(agentId, callback);
|
|
||||||
});
|
|
||||||
}).send();
|
|
||||||
},
|
|
||||||
// 修改用户密码
|
// 修改用户密码
|
||||||
changePassword(oldPassword, newPassword, successCallback, errorCallback) {
|
changePassword(oldPassword, newPassword, successCallback, errorCallback) {
|
||||||
RequestService.sendRequest()
|
RequestService.sendRequest()
|
||||||
.url(`${getServiceUrl()}/api/v1/user/change-password`)
|
.url(`${getServiceUrl()}/user/change-password`) // 修改URL
|
||||||
.method('PUT')
|
.method('PUT') // 修改方法为PUT
|
||||||
.data({
|
.data({
|
||||||
old_password: oldPassword,
|
old_password: oldPassword, // 修改参数名
|
||||||
new_password: newPassword,
|
new_password: newPassword // 修改参数名
|
||||||
})
|
})
|
||||||
.success((res) => {
|
.success((res) => {
|
||||||
RequestService.clearRequestTime();
|
RequestService.clearRequestTime();
|
||||||
successCallback(res);
|
successCallback(res);
|
||||||
})
|
})
|
||||||
.fail((error) => {
|
.fail((error) => {
|
||||||
RequestService.reAjaxFun(() => {
|
// RequestService.reAjaxFun(() => {
|
||||||
this.changePassword(oldPassword, newPassword, successCallback, errorCallback);
|
// this.changePassword(oldPassword, newPassword, successCallback, errorCallback);
|
||||||
});
|
// });
|
||||||
})
|
})
|
||||||
.send();
|
.send();
|
||||||
},
|
},
|
||||||
// 获取智能体配置
|
|
||||||
getDeviceConfig(deviceId, callback) {
|
|
||||||
RequestService.sendRequest()
|
|
||||||
.url(`${getServiceUrl()}/api/v1/user/agent/${deviceId}`)
|
|
||||||
.method('GET')
|
|
||||||
.success((res) => {
|
|
||||||
RequestService.clearRequestTime();
|
|
||||||
callback(res);
|
|
||||||
})
|
|
||||||
.fail((err) => {
|
|
||||||
console.error('获取配置失败:', err);
|
|
||||||
RequestService.reAjaxFun(() => {
|
|
||||||
this.getDeviceConfig(deviceId, callback);
|
|
||||||
});
|
|
||||||
}).send();
|
|
||||||
},
|
|
||||||
// 配置智能体
|
|
||||||
updateAgentConfig(agentId, configData, callback) {
|
|
||||||
RequestService.sendRequest()
|
|
||||||
.url(`${getServiceUrl()}/api/v1/user/agent/${agentId}`)
|
|
||||||
.method('PUT')
|
|
||||||
.data(configData)
|
|
||||||
.success((res) => {
|
|
||||||
RequestService.clearRequestTime();
|
|
||||||
callback(res);
|
|
||||||
})
|
|
||||||
.fail(() => {
|
|
||||||
RequestService.reAjaxFun(() => {
|
|
||||||
this.updateAgentConfig(agentId, configData, callback);
|
|
||||||
});
|
|
||||||
}).send();
|
|
||||||
},
|
|
||||||
// 已绑设备
|
|
||||||
getAgentBindDevices(agentId, callback) {
|
|
||||||
RequestService.sendRequest()
|
|
||||||
.url(`${getServiceUrl()}/api/v1/user/agent/device/bind/${agentId}`)
|
|
||||||
.method('GET')
|
|
||||||
.success((res) => {
|
|
||||||
RequestService.clearRequestTime();
|
|
||||||
callback(res);
|
|
||||||
})
|
|
||||||
.fail((err) => {
|
|
||||||
console.error('获取设备列表失败:', err);
|
|
||||||
RequestService.reAjaxFun(() => {
|
|
||||||
this.getAgentBindDevices(agentId, callback);
|
|
||||||
});
|
|
||||||
}).send();
|
|
||||||
},
|
|
||||||
// 解绑设备
|
|
||||||
unbindDevice(device_id, callback) {
|
|
||||||
RequestService.sendRequest()
|
|
||||||
.url(`${getServiceUrl()}/api/v1/user/device/unbind/${device_id}`)
|
|
||||||
.method('PUT')
|
|
||||||
.success((res) => {
|
|
||||||
RequestService.clearRequestTime();
|
|
||||||
callback(res);
|
|
||||||
})
|
|
||||||
.fail((err) => {
|
|
||||||
console.error('解绑设备失败:', err);
|
|
||||||
RequestService.reAjaxFun(() => {
|
|
||||||
this.unbindDevice(device_id, callback);
|
|
||||||
});
|
|
||||||
}).send();
|
|
||||||
},
|
|
||||||
// 绑定设备
|
|
||||||
bindDevice(agentId, code, callback) {
|
|
||||||
RequestService.sendRequest()
|
|
||||||
.url(`${getServiceUrl()}/api/v1/user/agent/device/bind/${agentId}`)
|
|
||||||
.method('POST')
|
|
||||||
.data({ code })
|
|
||||||
.success((res) => {
|
|
||||||
RequestService.clearRequestTime();
|
|
||||||
callback(res);
|
|
||||||
})
|
|
||||||
.fail((err) => {
|
|
||||||
console.error('绑定设备失败:', err);
|
|
||||||
RequestService.reAjaxFun(() => {
|
|
||||||
this.bindDevice(agentId, code, callback);
|
|
||||||
});
|
|
||||||
}).send();
|
|
||||||
},
|
|
||||||
// 新增方法:获取智能体模板
|
|
||||||
getAgentTemplate(templateName, callback) {
|
|
||||||
RequestService.sendRequest()
|
|
||||||
.url(`${getServiceUrl()}/api/v1/user/agent/templateId`)
|
|
||||||
.method('GET')
|
|
||||||
.success((res) => {
|
|
||||||
RequestService.clearRequestTime();
|
|
||||||
callback(res);
|
|
||||||
})
|
|
||||||
.fail((err) => {
|
|
||||||
console.error('获取模板失败:', err);
|
|
||||||
RequestService.reAjaxFun(() => {
|
|
||||||
this.getAgentTemplate(templateName, callback);
|
|
||||||
});
|
|
||||||
}).send();
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-23
@@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog :visible.sync="visible" width="400px" center>
|
<el-dialog :visible.sync="visible" width="400px" center :close-on-click-modal="false" :close-on-press-escape="false">
|
||||||
<div style="margin: 0 10px 10px;display: flex;align-items: center;gap: 10px;font-weight: 700;font-size: 20px;text-align: left;color: #3d4566;">
|
<div style="margin: 0 10px 10px;display: flex;align-items: center;gap: 10px;font-weight: 700;font-size: 20px;text-align: left;color: #3d4566;">
|
||||||
<div style="width: 40px;height: 40px;border-radius: 50%;background: #5778ff;display: flex;align-items: center;justify-content: center;">
|
<div style="width: 40px;height: 40px;border-radius: 50%;background: #5778ff;display: flex;align-items: center;justify-content: center;">
|
||||||
<img src="@/assets/home/equipment.png" alt="" style="width: 18px;height: 15px;" />
|
<i class="el-icon-cpu" style="color: #fff;"></i>
|
||||||
</div>
|
</div>
|
||||||
添加智慧体
|
添加智能体
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 1px;background: #e8f0ff;" />
|
<div style="height: 1px;background: #e8f0ff;" />
|
||||||
<div style="margin: 22px 15px;">
|
<div style="margin: 22px 15px;">
|
||||||
<div style="font-weight: 400;font-size: 14px;text-align: left;color: #3d4566;">
|
<div style="font-weight: 400;font-size: 14px;text-align: left;color: #3d4566;">
|
||||||
<div style="color: red;display: inline-block;">*</div> 智慧体名称:
|
<div style="color: red;display: inline-block;">*</div>智能体名称:
|
||||||
</div>
|
</div>
|
||||||
<div class="input-46" style="margin-top: 12px;">
|
<div style="margin-top: 12px;">
|
||||||
<el-input placeholder="请输入智慧体名称.." v-model="wisdomBodyName" />
|
<el-input placeholder="请输入智能体名称.." v-model="agentName" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex;margin: 15px 15px;gap: 7px;">
|
<div style="display: flex;margin: 15px 15px;gap: 7px;">
|
||||||
@@ -33,42 +33,36 @@ import userApi from '@/apis/module/user';
|
|||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AddWisdomBodyDialog',
|
name: 'AddAgentDialog',
|
||||||
props: {
|
props: {
|
||||||
visible: { type: Boolean, required: true }
|
visible: { type: Boolean, required: true }
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return { wisdomBodyName: "" }
|
return { agentName: "" }
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
confirm() {
|
confirm() {
|
||||||
if (!this.wisdomBodyName.trim()) {
|
if (!this.agentName.trim()) {
|
||||||
this.$message.error('请输入智慧体名称');
|
this.$message.error('请输入智能体名称');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
userApi.addAgent(this.wisdomBodyName, (res) => {
|
this.$emit('confirm',this.agentName)
|
||||||
this.$message.success('添加成功');
|
// userApi.addAgent(this.agentName, (res) => {
|
||||||
this.$emit('confirm', res);
|
// this.$message.success('添加成功');
|
||||||
|
// this.$emit('confirm', res);
|
||||||
this.$emit('update:visible', false);
|
this.$emit('update:visible', false);
|
||||||
this.wisdomBodyName = "";
|
this.agentName = "";
|
||||||
});
|
// });
|
||||||
},
|
},
|
||||||
cancel() {
|
cancel() {
|
||||||
this.$emit('update:visible', false)
|
this.$emit('update:visible', false)
|
||||||
this.wisdomBodyName = ""
|
this.agentName = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
.input-46 {
|
|
||||||
border: 1px solid #e4e6ef;
|
|
||||||
background: #f6f8fb;
|
|
||||||
border-radius: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-btn {
|
.dialog-btn {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog :visible.sync="visible" width="400px" center >
|
<el-dialog :visible.sync="visible" width="400px" center :close-on-click-modal="false" :close-on-press-escape="false">
|
||||||
<div style="margin: 0 10px 10px;display: flex;align-items: center;gap: 10px;font-weight: 700;font-size: 20px;text-align: left;color: #3d4566;">
|
<div style="margin: 0 10px 10px;display: flex;align-items: center;gap: 10px;font-weight: 700;font-size: 20px;text-align: left;color: #3d4566;">
|
||||||
<div style="width: 40px;height: 40px;border-radius: 50%;background: #5778ff;display: flex;align-items: center;justify-content: center;">
|
<div style="width: 40px;height: 40px;border-radius: 50%;background: #5778ff;display: flex;align-items: center;justify-content: center;">
|
||||||
<img src="@/assets/home/equipment.png" alt="" style="width: 18px;height: 15px;" />
|
<i class="el-icon-cpu" style="color: #fff;"></i>
|
||||||
</div>
|
</div>
|
||||||
添加设备
|
添加设备
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 1px;background: #e8f0ff;" />
|
<div style="height: 1px;background: #e8f0ff;" />
|
||||||
<div style="margin: 22px 15px;">
|
<div style="margin: 22px 15px;">
|
||||||
<div style="font-weight: 400;font-size: 14px;text-align: left;color: #3d4566;">
|
<div style="font-weight: 400;font-size: 14px;text-align: left;color: #3d4566;">
|
||||||
<div style="color: red;display: inline-block;">*</div>
|
<div style="color: red;display: inline-block;">*</div>验证码:
|
||||||
<span style="font-size: 11px"> 验证码:</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="input-46" style="margin-top: 12px;">
|
<div class="input-46" style="margin-top: 12px;">
|
||||||
<el-input placeholder="请输入设备播报的6位数验证码.." v-model="deviceCode" />
|
<el-input placeholder="请输入设备播报的6位数验证码.." v-model="deviceCode" />
|
||||||
@@ -33,46 +32,20 @@
|
|||||||
export default {
|
export default {
|
||||||
name: 'AddDeviceDialog',
|
name: 'AddDeviceDialog',
|
||||||
props: {
|
props: {
|
||||||
visible: { type: Boolean, required: true },
|
visible: { type: Boolean, required: true }
|
||||||
agentId: { type: String, required: true }
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return { deviceCode: "" }
|
||||||
deviceCode: "",
|
|
||||||
loading: false,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
confirm() {
|
confirm() {
|
||||||
if (!/^\d{6}$/.test(this.deviceCode)) {
|
if (!/^\d{6}$/.test(this.deviceCode)) {
|
||||||
this.$message.error('请输入6位数字验证码');
|
this.$message.error('请输入6位数字设备验证码');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.loading = true;
|
this.$emit('confirm',this.deviceCode)
|
||||||
import('@/apis/module/user').then(({ default: userApi }) => {
|
this.$emit('update:visible', false)
|
||||||
userApi.bindDevice(
|
this.deviceCode = ""
|
||||||
this.agentId,
|
|
||||||
this.deviceCode, ({data}) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (data.code === 0) {
|
|
||||||
this.$emit('refresh');
|
|
||||||
this.$message.success('设备绑定成功');
|
|
||||||
this.closeDialog();
|
|
||||||
} else {
|
|
||||||
this.$message.error(data.msg || '绑定失败');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}).catch((err) => {
|
|
||||||
this.loading = false;
|
|
||||||
console.error('API模块加载失败:', err);
|
|
||||||
this.$message.error('绑定服务不可用');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
closeDialog() {
|
|
||||||
this.$emit('update:visible', false);
|
|
||||||
this.deviceCode = '';
|
|
||||||
|
|
||||||
},
|
},
|
||||||
cancel() {
|
cancel() {
|
||||||
this.$emit('update:visible', false)
|
this.$emit('update:visible', false)
|
||||||
@@ -83,13 +56,6 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
.input-46 {
|
|
||||||
border: 1px solid #e4e6ef;
|
|
||||||
background: #f6f8fb;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-btn {
|
.dialog-btn {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog :visible.sync="visible" width="600px" center :close-on-click-modal="false" :close-on-press-escape="false">
|
||||||
|
<div style="margin: 0 10px 10px;display: flex;align-items: center;gap: 10px;font-weight: 700;font-size: 20px;text-align: left;color: #3d4566;">
|
||||||
|
<div style="width: 40px;height: 40px;border-radius: 50%;background: #5778ff;display: flex;align-items: center;justify-content: center;">
|
||||||
|
<i class="el-icon-lightning" style="color: #fff;"></i>
|
||||||
|
</div>
|
||||||
|
{{ data.id?'编辑':'添加' }}设备
|
||||||
|
</div>
|
||||||
|
<div style="height: 1px;background: #e8f0ff;" />
|
||||||
|
|
||||||
|
<div style="margin: 22px 15px;">
|
||||||
|
<el-form ref="form" :model="data" label-width="80px">
|
||||||
|
<el-form-item label="设备型号" prop="board" :rules="[{ required: true, message: '请输入设备型号', trigger: 'blur' }]">
|
||||||
|
<el-input v-model="data.board" placeholder="请输入设备型号..."></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="固件版本" prop="appVersion" :rules="[{ required: true, message: '请输入固件版本', trigger: 'blur' }]">
|
||||||
|
<el-input v-model="data.appVersion" placeholder="请输入固件版本..."></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="升级地址" prop="url" :rules="[{ required: true, message: '请输入升级地址', trigger: 'blur' },{ type: 'url', message: '请输入正确的升级地址', trigger: 'blur' }]">
|
||||||
|
<el-input type="textarea" v-model="data.url" placeholder="请输入升级地址..."></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否启用">
|
||||||
|
<el-switch v-model="data.isEnabled" size="mini" :active-value="1" :inactive-value="0" active-color="#13ce66" inactive-color="#ff4949"></el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label-width="0">
|
||||||
|
<div style="display: flex;margin: 15px 15px;gap: 7px;">
|
||||||
|
<div class="dialog-btn" @click="confirm">
|
||||||
|
确定
|
||||||
|
</div>
|
||||||
|
<div class="dialog-btn" style="background: #e6ebff;border: 1px solid #adbdff;color: #5778ff;" @click="cancel">
|
||||||
|
取消
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'AddDeviceOtaDialog',
|
||||||
|
props: {
|
||||||
|
visible: { type: Boolean, required: true },
|
||||||
|
data: { type: Object,
|
||||||
|
default: () => ({
|
||||||
|
id: "",
|
||||||
|
board: "",
|
||||||
|
appVersion: "",
|
||||||
|
url: "",
|
||||||
|
isEnabled: 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirm() {
|
||||||
|
this.$refs['form'].validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
this.$emit(this.data.id?'confirmUpdate':'confirmSave',this.data)
|
||||||
|
this.$emit('update:visible', false)
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
this.$emit('update:visible', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dialog-btn {
|
||||||
|
cursor: pointer;
|
||||||
|
flex: 1;
|
||||||
|
border-radius: 23px;
|
||||||
|
background: #5778ff;
|
||||||
|
height: 40px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #fff;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-dialog {
|
||||||
|
border-radius: 15px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
::v-deep .el-dialog__headerbtn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
::v-deep .el-dialog__body {
|
||||||
|
padding: 4px 6px;
|
||||||
|
}
|
||||||
|
::v-deep .el-dialog__header{
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<template>
|
||||||
|
<div class="agent-item">
|
||||||
|
<div style="display: flex;justify-content: space-between;">
|
||||||
|
<div style="font-weight: 700;font-size: 18px;text-align: left;color: #3d4566;">
|
||||||
|
{{ agent.agentName }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<img src="@/assets/home/delete.png" alt=""
|
||||||
|
style="width: 18px;height: 18px;" @click="$emit('del', agent.id)" />
|
||||||
|
<!-- <img src="@/assets/home/info.png" alt="" style="width: 18px;height: 18px;" /> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="agent-name">
|
||||||
|
角色音色:{{ agent.ttsModelName }}
|
||||||
|
</div>
|
||||||
|
<div class="agent-name">
|
||||||
|
语言模型:{{ agent.llmModelName }}
|
||||||
|
</div>
|
||||||
|
<div class="agent-name">
|
||||||
|
最近对话:{{ agent.lastConnectedAt }}
|
||||||
|
</div>
|
||||||
|
<div style="display: flex;justify-content: space-between;align-items: center;gap: 10px;">
|
||||||
|
<div class="settings-btn" @click="$emit('configure', agent.id)">
|
||||||
|
配置角色
|
||||||
|
</div>
|
||||||
|
<div class="settings-btn" @click="$emit('asr', agent.id)">
|
||||||
|
声纹识别
|
||||||
|
</div>
|
||||||
|
<div class="settings-btn" @click="$emit('chat', agent.id)">
|
||||||
|
历史对话
|
||||||
|
</div>
|
||||||
|
<div class="settings-btn" @click="$emit('device', agent.id)">
|
||||||
|
设备数量({{agent.deviceCount}})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'AgentItem',
|
||||||
|
props: {
|
||||||
|
agent: { type: Object, required: true }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.agent-item {
|
||||||
|
width: 342px;
|
||||||
|
border-radius: 20px;
|
||||||
|
background: #fafcfe;
|
||||||
|
padding: 22px 22px 11px 22px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.agent-name {
|
||||||
|
margin: 7px 0 10px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #3d4566;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-btn {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 10px;
|
||||||
|
color: #5778ff;
|
||||||
|
background: #e6ebff;
|
||||||
|
padding: 2px 10px;
|
||||||
|
line-height: 21px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="device-item">
|
|
||||||
<div style="display: flex;justify-content: space-between;">
|
|
||||||
<div style="font-weight: 700;font-size: 18px;text-align: left;color: #3d4566;">
|
|
||||||
{{ device.agentName }}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<img src="@/assets/home/delete.png" alt=""
|
|
||||||
style="width: 18px;height: 18px;margin-right: 10px;" @click.stop="handleDelete" />
|
|
||||||
<img src="@/assets/home/info.png" alt="" style="width: 18px;height: 18px;" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="device-name">
|
|
||||||
设备型号:{{ device.ttsModelName }}
|
|
||||||
</div>
|
|
||||||
<div class="device-name">
|
|
||||||
音色模型:{{ device.ttsVoiceName }}
|
|
||||||
</div>
|
|
||||||
<div style="display: flex;gap: 10px;align-items: center;">
|
|
||||||
<div class="settings-btn" @click="handleConfigure">
|
|
||||||
配置角色
|
|
||||||
</div>
|
|
||||||
<div class="settings-btn">
|
|
||||||
声纹识别
|
|
||||||
</div>
|
|
||||||
<div class="settings-btn">
|
|
||||||
历史对话
|
|
||||||
</div>
|
|
||||||
<div class="settings-btn" @click="handleDeviceManage">
|
|
||||||
设备管理
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="version-info">
|
|
||||||
<div>最近对话:{{ device.lastConnectedAt }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'DeviceItem',
|
|
||||||
props: {
|
|
||||||
device: { type: Object, required: true }
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return { switchValue: false }
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleDelete() {
|
|
||||||
this.$emit('delete', this.device.agentId)
|
|
||||||
},
|
|
||||||
handleConfigure() {
|
|
||||||
this.$router.push({ path: '/role-config', query: { agentId: this.device.agentId } });
|
|
||||||
},
|
|
||||||
handleDeviceManage() {
|
|
||||||
this.$router.push({ path: '/device-management', query: { agentId: this.device.agentId } });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style scoped>
|
|
||||||
.device-item {
|
|
||||||
width: 342px;
|
|
||||||
border-radius: 20px;
|
|
||||||
background: #fafcfe;
|
|
||||||
padding: 22px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.device-name {
|
|
||||||
margin: 7px 0 10px;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 11px;
|
|
||||||
color: #3d4566;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-btn {
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 10px;
|
|
||||||
color: #5778ff;
|
|
||||||
background: #e6ebff;
|
|
||||||
width: 57px;
|
|
||||||
height: 21px;
|
|
||||||
line-height: 21px;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.version-info {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-top: 15px;
|
|
||||||
font-size: 10px;
|
|
||||||
color: #979db1;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="visible" class="message">
|
||||||
|
{{message}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Footer',
|
||||||
|
props: {
|
||||||
|
visible: { type: Boolean, required: true },
|
||||||
|
message: { type: String, required: false, default: '©2025 xiaozhi-esp32-server' }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return { }
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.message {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
padding: 20px;
|
||||||
|
color: #979db1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,56 +1,58 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-header class="header">
|
<el-header class="header">
|
||||||
<div class="header-container">
|
<div style="display: flex;justify-content: space-between;margin-top: 6px;">
|
||||||
<!-- 左侧元素 -->
|
<div style="display: flex;align-items: center;gap: 10px;">
|
||||||
<div class="header-left">
|
<img alt="" src="@/assets/xiaozhi-logo.png" style="width: 42px;height: 42px;"/>
|
||||||
<img alt="" src="@/assets/xiaozhi-logo.png" class="logo-img"/>
|
<img alt="" src="@/assets/xiaozhi-ai.png" style="width: 58px;height: 12px;"/>
|
||||||
<img alt="" src="@/assets/xiaozhi-ai.png" class="brand-img"/>
|
<div ref="menu-code_agent" class="ml-20 menu-btn" @click="goToPage('/home')">
|
||||||
</div>
|
<!-- <img alt="" src="@/assets/home/equipment.png" style="width: 12px;height: 10px;"/> -->
|
||||||
|
<i class="el-icon-cpu"></i>
|
||||||
<!-- 中间导航菜单 -->
|
智能体
|
||||||
<div class="header-center">
|
|
||||||
<div class="equipment-management" :class="{ 'active-tab': $route.path === '/home' }" @click="goHome">
|
|
||||||
<img alt="" src="@/assets/header/roboot.png" :style="{ filter: $route.path === '/home' ? 'brightness(0) invert(1)' : 'None' }"/>
|
|
||||||
智能体管理
|
|
||||||
</div>
|
</div>
|
||||||
<div class="equipment-management" :class="{ 'active-tab': $route.path === '/user-management' }" @click="goUserManagement">
|
<div ref="menu-code_console" class="menu-btn">
|
||||||
<img alt="" src="@/assets/header/user_management.png" :style="{ filter: $route.path === '/user-management' ? 'brightness(0) invert(1)' : 'None' }"/>
|
<i class="el-icon-microphone" style="color: #979db1;"/>
|
||||||
|
声音复刻
|
||||||
|
</div>
|
||||||
|
<div ref="menu-code_console" class="menu-btn">
|
||||||
|
<i class="el-icon-s-grid" style="color: #979db1;"/>
|
||||||
|
控制台
|
||||||
|
</div>
|
||||||
|
<div ref="menu-code_user" class="menu-btn" @click="goToPage('/user-management')">
|
||||||
|
<i class="el-icon-user"></i>
|
||||||
用户管理
|
用户管理
|
||||||
</div>
|
</div>
|
||||||
<div class="equipment-management" :class="{ 'active-tab': $route.path === '/model-config' }" @click="goModelConfig">
|
<div ref="menu-code_model" class="menu-btn" @click="goToPage('/model-config')">
|
||||||
<img alt="" src="@/assets/header/model_config.png" :style="{ filter: $route.path === '/model-config' ? 'brightness(0) invert(1)' : 'None' }"/>
|
<i class="el-icon-news"></i>
|
||||||
模型配置
|
模型配置
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div ref="menu-code_ota" class="menu-btn" @click="goToPage('/ota')">
|
||||||
|
<i class="el-icon-lightning"></i>
|
||||||
<!-- 右侧元素 -->
|
OTA管理
|
||||||
<div class="header-right">
|
|
||||||
<div class="search-container">
|
|
||||||
<el-input
|
|
||||||
v-model="serach"
|
|
||||||
placeholder="输入名称搜索.."
|
|
||||||
class="custom-search-input"
|
|
||||||
@keyup.enter.native="handleSearch"
|
|
||||||
>
|
|
||||||
<i slot="suffix" class="el-icon-search search-icon" @click="handleSearch"></i>
|
|
||||||
</el-input>
|
|
||||||
</div>
|
</div>
|
||||||
<img alt="" src="@/assets/home/avatar.png" class="avatar-img"/>
|
</div>
|
||||||
<el-dropdown trigger="click" class="user-dropdown">
|
<div style="display: flex;align-items: center;gap: 7px; margin-top: 2px;">
|
||||||
|
<div class="serach-box">
|
||||||
|
<el-input v-model="serach" placeholder="输入名称搜索.." style="border: none; background: transparent;"
|
||||||
|
@keyup.enter.native="handleSearch"/>
|
||||||
|
<img alt="" src="@/assets/home/search.png"
|
||||||
|
style="width: 14px;height: 14px;margin-right: 11px;cursor: pointer;" @click="handleSearch"/>
|
||||||
|
</div>
|
||||||
|
<img alt="" src="@/assets/home/avatar.png" style="width: 21px;height: 21px;"/>
|
||||||
|
<el-dropdown trigger="click">
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
{{ userInfo.mobile || '加载中...' }}<i class="el-icon-arrow-down el-icon--right"></i>
|
{{ userInfo.username || '加载中...' }}<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
</span>
|
</span>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item icon="el-icon-plus" @click.native="">个人中心</el-dropdown-item>
|
<el-dropdown-item icon="el-icon-user" @click.native="">个人中心</el-dropdown-item>
|
||||||
<el-dropdown-item icon="el-icon-circle-plus" @click.native="showChangePasswordDialog">修改密码</el-dropdown-item>
|
<el-dropdown-item icon="el-icon-key" @click.native="">修改密码</el-dropdown-item>
|
||||||
<el-dropdown-item icon="el-icon-circle-plus-outline" @click.native="">退出登录</el-dropdown-item>
|
<el-dropdown-item icon="el-icon-connection" @click.native="handleLogout">退出登录</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 修改密码弹窗 -->
|
<!-- 修改密码弹窗 -->
|
||||||
<ChangePasswordDialog :visible.sync="isChangePasswordDialogVisible"/>
|
<ChangePasswordDialog :visible.sync="isChangePasswordDialogVisible" />
|
||||||
</el-header>
|
</el-header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -74,19 +76,33 @@ export default {
|
|||||||
isChangePasswordDialogVisible: false // 控制修改密码弹窗的显示
|
isChangePasswordDialogVisible: false // 控制修改密码弹窗的显示
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
'$route.meta.menuCode': {
|
||||||
|
handler(to, from) {
|
||||||
|
const meta = this.$route.meta;
|
||||||
|
if(meta && meta.menuCode){
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const menu = this.$refs[`menu-code_`+ meta.menuCode]
|
||||||
|
menu && menu.classList.add('active')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.fetchUserInfo()
|
this.fetchUserInfo()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goHome() {
|
handleLogout() {
|
||||||
|
// 退出登录
|
||||||
|
this.$store.commit('setToken','')
|
||||||
|
this.$router.push('/login')
|
||||||
|
},
|
||||||
|
goToPage(path) {
|
||||||
// 跳转到首页
|
// 跳转到首页
|
||||||
this.$router.push('/')
|
this.$router.replace(path)
|
||||||
},
|
|
||||||
goUserManagement() {
|
|
||||||
this.$router.push('/user-management')
|
|
||||||
},
|
|
||||||
goModelConfig() {
|
|
||||||
this.$router.push('/model-config')
|
|
||||||
},
|
},
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
fetchUserInfo() {
|
fetchUserInfo() {
|
||||||
@@ -124,168 +140,71 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.ml-20 {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-btn {
|
||||||
|
padding: 8px 14px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fff;
|
||||||
|
color: #979db1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.menu-btn.active {
|
||||||
|
background: #5778ff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
background: #f6fcfe66;
|
background: #f6fcfe66;
|
||||||
border: 1px solid #fff;
|
border: 1px solid #fff;
|
||||||
height: 53px !important;
|
height: 53px !important;
|
||||||
min-width: 900px; /* 设置最小宽度防止过度压缩 */
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-container {
|
.serach-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
width: 220px;
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-left {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
min-width: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-img {
|
|
||||||
width: 42px;
|
|
||||||
height: 42px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand-img {
|
|
||||||
width: 58px;
|
|
||||||
height: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-center {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 25px;
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-right {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 7px;
|
|
||||||
min-width: 300px;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.equipment-management {
|
|
||||||
width: 82px;
|
|
||||||
height: 24px;
|
|
||||||
border-radius: 12px;
|
|
||||||
background: #deeafe;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 10px;
|
|
||||||
font-weight: 500;
|
|
||||||
gap: 7px;
|
|
||||||
color: #3d4566;
|
|
||||||
margin-left: 1px;
|
|
||||||
align-items: center;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
cursor: pointer;
|
|
||||||
flex-shrink: 0; /* 防止导航按钮被压缩 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.equipment-management.active-tab {
|
|
||||||
background: #5778ff !important;
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.equipment-management img {
|
|
||||||
width: 15px;
|
|
||||||
height: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-container {
|
|
||||||
margin-right: 15px;
|
|
||||||
min-width: 150px;
|
|
||||||
flex-grow: 1;
|
|
||||||
max-width: 220px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-search-input >>> .el-input__inner {
|
|
||||||
height: 30px;
|
height: 30px;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
background-color: #e2e5f8;
|
background-color: #f6fcfe66;
|
||||||
border: 1px solid #e4e6ef;
|
border: 1px solid #e4e6ef;
|
||||||
padding-left: 15px;
|
align-items: center;
|
||||||
font-size: 12px;
|
padding: 0 7px;
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.serach-box /deep/ .el-input__inner {
|
||||||
|
border-radius: 15px;
|
||||||
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
padding-left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-icon {
|
.user-info {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 12px;
|
||||||
|
letter-spacing: -0.02px;
|
||||||
|
text-align: left;
|
||||||
|
color: #3d4566;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dropdown-link {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #909399;
|
color: #5778ff;
|
||||||
margin-right: 8px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 30px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-img {
|
.el-icon-arrow-down {
|
||||||
width: 21px;
|
font-size: 12px;
|
||||||
height: 21px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-dropdown {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 响应式调整 */
|
|
||||||
@media (max-width: 1200px) {
|
|
||||||
.header-center {
|
|
||||||
gap: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.equipment-management {
|
|
||||||
width: 70px;
|
|
||||||
font-size: 9px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
.search-container {
|
|
||||||
margin-right: 10px;
|
|
||||||
max-width: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-right {
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
|
||||||
.header-left {
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-container {
|
|
||||||
max-width: 150px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.search-container {
|
|
||||||
max-width: 145px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-search-input >>> .el-input__inner {
|
|
||||||
padding-left: 10px;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.search-container {
|
|
||||||
max-width: 120px;
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@@ -122,9 +122,10 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.model-menu {
|
.model-menu {
|
||||||
border-right: 1px solid #ebeef5;
|
border: 1px solid #ebeef5;
|
||||||
height: calc(100vh - 300px);
|
height: calc(100vh - 300px);
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
|
margin-right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-operate {
|
.search-operate {
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ const routes = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/role-config',
|
path: '/register',
|
||||||
name: 'RoleConfig',
|
name: 'Register',
|
||||||
component: function () {
|
component: function () {
|
||||||
return import('../views/roleConfig.vue')
|
return import('../views/register.vue')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -28,38 +28,62 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: '/home',
|
path: '/home',
|
||||||
name: 'home',
|
name: 'home',
|
||||||
|
meta: {
|
||||||
|
menuCode: 'agent',
|
||||||
|
},
|
||||||
component: function () {
|
component: function () {
|
||||||
return import('../views/home.vue')
|
return import('../views/home.vue')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/register',
|
path: '/role-config',
|
||||||
name: 'Register',
|
name: 'RoleConfig',
|
||||||
|
meta: {
|
||||||
|
menuCode: 'agent',
|
||||||
|
},
|
||||||
component: function () {
|
component: function () {
|
||||||
return import('../views/register.vue')
|
return import('../views/roleConfig.vue')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 设备管理页面路由
|
|
||||||
{
|
{
|
||||||
path: '/device-management',
|
path: '/device',
|
||||||
name: 'DeviceManagement',
|
name: 'Device',
|
||||||
|
meta: {
|
||||||
|
menuCode: 'agent',
|
||||||
|
},
|
||||||
component: function () {
|
component: function () {
|
||||||
return import('../views/DeviceManagement.vue')
|
return import('../views/device.vue')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/ota',
|
||||||
|
name: 'Ota',
|
||||||
|
meta: {
|
||||||
|
menuCode: 'ota',
|
||||||
|
},
|
||||||
|
component: function () {
|
||||||
|
return import('../views/ota.vue')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 添加用户管理路由
|
// 添加用户管理路由
|
||||||
{
|
{
|
||||||
path: '/user-management',
|
path: '/user-management',
|
||||||
name: 'UserManagement',
|
name: 'UserManagement',
|
||||||
|
meta: {
|
||||||
|
menuCode: 'user',
|
||||||
|
},
|
||||||
component: function () {
|
component: function () {
|
||||||
return import('../views/UserManagement.vue')
|
return import('../views/userManagement.vue')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/model-config',
|
path: '/model-config',
|
||||||
name: 'ModelConfig',
|
name: 'ModelConfig',
|
||||||
|
meta: {
|
||||||
|
menuCode: 'model',
|
||||||
|
},
|
||||||
component: function () {
|
component: function () {
|
||||||
return import('../views/ModelConfig.vue')
|
return import('../views/modelConfig.vue')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -39,10 +39,13 @@ function formatDateTool(date, fmt) {
|
|||||||
const o = {
|
const o = {
|
||||||
'M+': date.getMonth() + 1,
|
'M+': date.getMonth() + 1,
|
||||||
'd+': date.getDate(),
|
'd+': date.getDate(),
|
||||||
'h+': date.getHours(),
|
'H+': date.getHours(),
|
||||||
|
'h+': date.getHours() > 12 ? date.getHours() - 12 : date.getHours(),
|
||||||
'm+': date.getMinutes(),
|
'm+': date.getMinutes(),
|
||||||
's+': date.getSeconds()
|
's+': date.getSeconds()
|
||||||
}
|
}
|
||||||
|
// 12小时制
|
||||||
|
const is_12Hours = fmt.indexOf('hh') > -1;
|
||||||
for (const k in o) {
|
for (const k in o) {
|
||||||
if (new RegExp(`(${k})`).test(fmt)) {
|
if (new RegExp(`(${k})`).test(fmt)) {
|
||||||
const str = o[k] + ''
|
const str = o[k] + ''
|
||||||
@@ -52,6 +55,8 @@ function formatDateTool(date, fmt) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 12小时制
|
||||||
|
fmt = is_12Hours ? date.getHours() > 12 ? fmt + " PM" : fmt + " AM" : fmt
|
||||||
return fmt
|
return fmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export function checkUserLogin(fn) {
|
|||||||
let token = localStorage.getItem(Constant.STORAGE_KEY.TOKEN)
|
let token = localStorage.getItem(Constant.STORAGE_KEY.TOKEN)
|
||||||
let userType = localStorage.getItem(Constant.STORAGE_KEY.USER_TYPE)
|
let userType = localStorage.getItem(Constant.STORAGE_KEY.USER_TYPE)
|
||||||
if (isNull(token) || isNull(userType)) {
|
if (isNull(token) || isNull(userType)) {
|
||||||
goToPage('console', true)
|
goToPage('/login', true)
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
if (fn) {
|
if (fn) {
|
||||||
fn()
|
fn()
|
||||||
|
|||||||
@@ -1,215 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="welcome">
|
|
||||||
<HeaderBar />
|
|
||||||
<el-main style="padding: 20px; display: flex; flex-direction: column;">
|
|
||||||
<div class="table-container">
|
|
||||||
<h3 class="device-list-title">设备列表</h3>
|
|
||||||
<el-button type="primary" class="add-device-btn" @click="handleAddDevice">
|
|
||||||
+ 添加设备
|
|
||||||
</el-button>
|
|
||||||
<el-table :data="paginatedDeviceList" style="width: 100%; margin-top: 20px" border stripe>
|
|
||||||
<el-table-column label="设备型号" prop="model" flex></el-table-column>
|
|
||||||
<el-table-column label="固件版本" prop="firmwareVersion" width="120"></el-table-column>
|
|
||||||
<el-table-column label="Mac地址" prop="macAddress"></el-table-column>
|
|
||||||
<el-table-column label="绑定时间" prop="bindTime" width="200"></el-table-column>
|
|
||||||
<el-table-column label="最近对话" prop="lastConversation" width="140"></el-table-column>
|
|
||||||
<el-table-column label="备注" width="180">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-input v-if="scope.row.isEdit" v-model="scope.row.remark" size="mini" @blur="stopEditRemark(scope.$index)"></el-input>
|
|
||||||
<span v-else>
|
|
||||||
<i v-if="!scope.row.remark" class="el-icon-edit" @click="startEditRemark(scope.$index, scope.row)"></i>
|
|
||||||
<span v-else @click="startEditRemark(scope.$index, scope.row)">
|
|
||||||
{{ scope.row.remark }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="OTA升级" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-switch v-model="scope.row.otaSwitch" size="mini" active-color="#13ce66" inactive-color="#ff4949"></el-switch>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="80">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button size="mini" type="text" @click="handleUnbind(scope.row.device_id)" style="color: #ff4949">
|
|
||||||
解绑
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<el-pagination
|
|
||||||
class="pagination"
|
|
||||||
@size-change="handleSizeChange"
|
|
||||||
@current-change="handleCurrentChange"
|
|
||||||
:current-page="currentPage"
|
|
||||||
:page-sizes="[5, 10, 20, 50]"
|
|
||||||
:page-size="pageSize"
|
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
|
||||||
:total="deviceList.length"
|
|
||||||
></el-pagination>
|
|
||||||
</div>
|
|
||||||
<div style="font-size: 12px; font-weight: 400; margin-top: auto; padding-top: 30px; color: #979db1;">
|
|
||||||
©2025 xiaozhi-esp32-server
|
|
||||||
</div>
|
|
||||||
<AddDeviceDialog :visible.sync="addDeviceDialogVisible" :agent-id="currentAgentId" @refresh="fetchBindDevices(currentAgentId)" />
|
|
||||||
</el-main>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import HeaderBar from "@/components/HeaderBar.vue";
|
|
||||||
import AddDeviceDialog from "@/components/AddDeviceDialog.vue";
|
|
||||||
export default {
|
|
||||||
components: {HeaderBar, AddDeviceDialog },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
addDeviceDialogVisible: false,
|
|
||||||
currentAgentId: this.$route.query.agentId || '',
|
|
||||||
currentPage: 1,
|
|
||||||
pageSize: 5,
|
|
||||||
deviceList: [],
|
|
||||||
loading: false,
|
|
||||||
userApi: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
paginatedDeviceList() {
|
|
||||||
const start = (this.currentPage - 1) * this.pageSize;
|
|
||||||
const end = start + this.pageSize;
|
|
||||||
return this.deviceList.slice(start, end);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
const agentId = this.$route.query.agentId;
|
|
||||||
import('@/apis/module/user').then(({ default: userApi }) => {
|
|
||||||
this.userApi = userApi;
|
|
||||||
if (agentId) {
|
|
||||||
this.fetchBindDevices(agentId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleAddDevice() {
|
|
||||||
this.addDeviceDialogVisible = true;
|
|
||||||
},
|
|
||||||
startEditRemark(index, row) {
|
|
||||||
this.deviceList[index].isEdit = true;
|
|
||||||
},
|
|
||||||
stopEditRemark(index) {
|
|
||||||
this.deviceList[index].isEdit = false;
|
|
||||||
},
|
|
||||||
handleUnbind(device_id) {
|
|
||||||
if (!this.userApi) {
|
|
||||||
this.$message.error('功能模块加载失败');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.$confirm('确认要解绑该设备吗?', '警告', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning'
|
|
||||||
}).then(() => {
|
|
||||||
this.userApi.unbindDevice(device_id, ({ data }) => {
|
|
||||||
if (data.code === 0) {
|
|
||||||
this.$message.success('设备解绑成功');
|
|
||||||
this.fetchBindDevices(this.$route.query.agentId);
|
|
||||||
} else {
|
|
||||||
this.$message.error(data.msg || '设备解绑失败');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handleSizeChange(val) {
|
|
||||||
this.pageSize = val;
|
|
||||||
},
|
|
||||||
handleCurrentChange(val) {
|
|
||||||
this.currentPage = val;
|
|
||||||
},
|
|
||||||
fetchBindDevices(agentId) {
|
|
||||||
this.loading = true;
|
|
||||||
import('@/apis/module/user').then(({ default: userApi }) => {
|
|
||||||
userApi.getAgentBindDevices(agentId, ({ data }) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (data.code === 0) {
|
|
||||||
this.deviceList = data.data[0].list.map(device => ({
|
|
||||||
device_id: device.id,
|
|
||||||
model: device.device_type,
|
|
||||||
firmwareVersion: device.app_version,
|
|
||||||
macAddress: device.mac_address,
|
|
||||||
lastConversation: device.recent_chat_time,
|
|
||||||
remark: '',
|
|
||||||
isEdit: false,
|
|
||||||
otaSwitch: device.ota_upgrade === 1
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
this.$message.error(data.msg || '获取设备列表失败');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
console.error('模块加载失败:', error);
|
|
||||||
this.$message.error('功能模块加载失败');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.welcome {
|
|
||||||
min-width: 900px;
|
|
||||||
min-height: 506px;
|
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background-image: url("@/assets/home/background.png");
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
-webkit-background-size: cover;
|
|
||||||
-o-background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-container {
|
|
||||||
background: #f9fafc;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 20px;
|
|
||||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-device-btn {
|
|
||||||
float: right;
|
|
||||||
background: #409eff;
|
|
||||||
border: none;
|
|
||||||
border-radius: 10px;
|
|
||||||
width: 105px;
|
|
||||||
height: 32px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 14px;
|
|
||||||
gap: 8px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
&:hover {
|
|
||||||
background: #3a8ee6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-list-title {
|
|
||||||
float: left;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 700;
|
|
||||||
margin: 5px;
|
|
||||||
color: #2c3e50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-icon-edit {
|
|
||||||
color: #409eff;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination {
|
|
||||||
margin-top: 20px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,9 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="welcome">
|
<div class="welcome">
|
||||||
<HeaderBar />
|
<HeaderBar />
|
||||||
|
<!-- 首页内容 -->
|
||||||
|
<el-main style="padding: 20px; display: flex; flex-direction: column;">
|
||||||
<div class="operation-bar">
|
<div class="operation-bar">
|
||||||
|
<!-- 面包屑-->
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
<el-breadcrumb separator="/">
|
||||||
|
<el-breadcrumb-item>控制台</el-breadcrumb-item>
|
||||||
|
<el-breadcrumb-item>模型配置</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</div>
|
||||||
<div class="right-operations">
|
<div class="right-operations">
|
||||||
<el-button v-if="activeTab === 'tts'" type="primary" plain size="small" @click="ttsDialogVisible = true">
|
<el-button v-if="activeTab === 'tts'" type="primary" plain size="small" @click="ttsDialogVisible = true">
|
||||||
语音设置
|
语音设置
|
||||||
@@ -16,96 +23,95 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 主体内容 -->
|
<!-- 主体内容 -->
|
||||||
<div class="main-wrapper">
|
<div class="main-wrapper">
|
||||||
<div class="content-panel">
|
<div class="content-panel">
|
||||||
<!-- 左侧导航 -->
|
<!-- 左侧导航 -->
|
||||||
<el-menu :default-active="activeTab" class="nav-panel" @select="handleMenuSelect"
|
<el-menu :default-active="activeTab" class="nav-panel" @select="handleMenuSelect"
|
||||||
style="background-size: cover; background-position: center;">
|
style="background-size: cover; background-position: center;">
|
||||||
<el-menu-item index="vad">
|
<el-menu-item index="vad">
|
||||||
<span class="menu-text">语言活动检测</span>
|
语言活动检测
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item index="asr">
|
<el-menu-item index="asr">
|
||||||
<span class="menu-text">语音识别</span>
|
语音识别
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item index="llm">
|
<el-menu-item index="llm">
|
||||||
<span class="menu-text">大语言模型</span>
|
大语言模型
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item index="intent">
|
<el-menu-item index="intent">
|
||||||
<span class="menu-text">意图识别</span>
|
意图识别
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item index="tts">
|
<el-menu-item index="tts">
|
||||||
<span class="menu-text">语音合成</span>
|
语音合成
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item index="memory">
|
<el-menu-item index="memory">
|
||||||
<span class="menu-text">记忆</span>
|
记忆
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
|
|
||||||
<!-- 右侧内容 -->
|
<!-- 右侧内容 -->
|
||||||
<div class="content-area">
|
<div class="content-area">
|
||||||
<div class="title-bar">
|
<div class="title-bar">
|
||||||
<div class="title-wrapper">
|
<div class="title-wrapper">
|
||||||
<h2 class="model-title">大语言模型(LLM)</h2>
|
<h2 class="model-title">大语言模型(LLM)</h2>
|
||||||
<el-button type="primary" size="small" @click="addModel" class="add-btn">
|
<el-button type="primary" size="small" @click="addModel" class="add-btn">
|
||||||
添加
|
添加
|
||||||
</el-button>
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="action-group">
|
||||||
|
<div class="search-group">
|
||||||
|
<el-input placeholder="请输入模型名称查询" v-model="search" size="small" class="search-input" clearable/>
|
||||||
|
<el-button type="primary" size="small" class="search-btn" @click="handleSearch">
|
||||||
|
查询
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-group">
|
|
||||||
<div class="search-group">
|
<el-table :header-cell-style="{background: 'transparent'}" :data="modelList" border class="data-table" header-row-class-name="table-header" >
|
||||||
<el-input placeholder="请输入模型名称查询" v-model="search" size="small" class="search-input" clearable/>
|
<el-table-column type="selection" width="55" align="center"></el-table-column>
|
||||||
<el-button type="primary" size="small" class="search-btn" @click="handleSearch">
|
<el-table-column label="模型名称" prop="candidateName" align="center"></el-table-column>
|
||||||
查询
|
<el-table-column label="模型编码" prop="code" align="center"></el-table-column>
|
||||||
|
<el-table-column label="提供商" prop="supplier" align="center"></el-table-column>
|
||||||
|
<el-table-column label="是否启用" align="center" width="120">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-switch v-model="scope.row.isApplied" class="custom-switch" :active-color="null" :inactive-color="null"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="center" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" size="mini" @click="editModel(scope.row)" class="edit-btn">
|
||||||
|
修改
|
||||||
|
</el-button>
|
||||||
|
<el-button type="text" size="mini" @click="deleteModel(scope.row)" class="delete-btn">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="table-footer">
|
||||||
|
<div class="batch-actions">
|
||||||
|
<el-button size="mini" @click="selectAll">全选</el-button>
|
||||||
|
<el-button size="mini" type="danger" icon="el-icon-delete" @click="batchDelete">
|
||||||
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="pagination-container">
|
||||||
|
<el-pagination @current-change="handleCurrentChange" background :current-page="currentPage" :page-size="pageSize" layout="prev, pager, next" :total="total"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table :header-cell-style="{background: 'transparent'}" :data="modelList" border class="data-table" header-row-class-name="table-header" >
|
|
||||||
<el-table-column type="selection" width="55" align="center"></el-table-column>
|
|
||||||
<el-table-column label="模型名称" prop="candidateName" align="center"></el-table-column>
|
|
||||||
<el-table-column label="模型编码" prop="code" align="center"></el-table-column>
|
|
||||||
<el-table-column label="提供商" prop="supplier" align="center"></el-table-column>
|
|
||||||
<el-table-column label="是否启用" align="center" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-switch v-model="scope.row.isApplied" class="custom-switch" :active-color="null" :inactive-color="null"/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" align="center" width="180">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button type="text" size="mini" @click="editModel(scope.row)" class="edit-btn">
|
|
||||||
修改
|
|
||||||
</el-button>
|
|
||||||
<el-button type="text" size="mini" @click="deleteModel(scope.row)" class="delete-btn">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<div class="table-footer">
|
|
||||||
<div class="batch-actions">
|
|
||||||
<el-button size="mini" @click="selectAll">全选</el-button>
|
|
||||||
<el-button size="mini" type="danger" icon="el-icon-delete" @click="batchDelete">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<div class="pagination-container">
|
|
||||||
<el-pagination @current-change="handleCurrentChange" background :current-page="currentPage" :page-size="pageSize" layout="prev, pager, next" :total="total"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<ModelEditDialog :visible.sync="editDialogVisible" :modelData="editModelData" @save="handleModelSave"/>
|
<ModelEditDialog :visible.sync="editDialogVisible" :modelData="editModelData" @save="handleModelSave"/>
|
||||||
<TtsModel :visible.sync="ttsDialogVisible" />
|
<TtsModel :visible.sync="ttsDialogVisible" />
|
||||||
<AddModelDialog :visible.sync="addDialogVisible" @confirm="handleAddConfirm"/>
|
<AddModelDialog :visible.sync="addDialogVisible" :modelType="activeTab" @confirm="handleAddConfirm"/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="copyright">
|
|
||||||
©2025 xiaozhi-esp32-server
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</el-main>
|
||||||
|
<!-- 底部 -->
|
||||||
|
<Footer :visible="true" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -113,9 +119,10 @@ import HeaderBar from "@/components/HeaderBar.vue";
|
|||||||
import ModelEditDialog from "@/components/ModelEditDialog.vue";
|
import ModelEditDialog from "@/components/ModelEditDialog.vue";
|
||||||
import TtsModel from "@/components/TtsModel.vue";
|
import TtsModel from "@/components/TtsModel.vue";
|
||||||
import AddModelDialog from "@/components/AddModelDialog.vue";
|
import AddModelDialog from "@/components/AddModelDialog.vue";
|
||||||
|
import Footer from '@/components/Footer.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { HeaderBar, ModelEditDialog, TtsModel, AddModelDialog },
|
components: { HeaderBar, ModelEditDialog, TtsModel, AddModelDialog,Footer },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
addDialogVisible: false,
|
addDialogVisible: false,
|
||||||
@@ -182,6 +189,10 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.breadcrumbs{
|
||||||
|
/* padding: 10px 0 0 5px; */
|
||||||
|
}
|
||||||
|
|
||||||
::v-deep .el-table tr{
|
::v-deep .el-table tr{
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
@@ -199,7 +210,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.main-wrapper {
|
.main-wrapper {
|
||||||
margin: 5px 60px;
|
/* margin: 5px 24px; */
|
||||||
background-image: url("@/assets/home/background.png");
|
background-image: url("@/assets/home/background.png");
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
min-height: 600px;
|
min-height: 600px;
|
||||||
@@ -209,10 +220,9 @@ export default {
|
|||||||
|
|
||||||
.operation-bar {
|
.operation-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 16px 24px;
|
/* padding: 16px 24px; */
|
||||||
border-bottom: 1px solid #ebeef5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-panel {
|
.content-panel {
|
||||||
@@ -239,7 +249,7 @@ export default {
|
|||||||
|
|
||||||
.nav-panel .el-menu-item {
|
.nav-panel .el-menu-item {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
line-height: 40px;
|
line-height: 48px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
@@ -251,20 +261,16 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav-panel .el-menu-item.is-active {
|
.nav-panel .el-menu-item.is-active {
|
||||||
background: #ecf5ff;
|
background-image:linear-gradient(-45deg,#ecf5ff,#cfe4f9);
|
||||||
|
/* background: #ecf5ff; */
|
||||||
color: #409EFF;
|
color: #409EFF;
|
||||||
border-right: 3px solid #409EFF;
|
border-right: 3px solid #409EFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-text {
|
.nav-panel .el-menu-item:hover {
|
||||||
font-size: 14px;
|
background-image:linear-gradient(-45deg,#ecf5ff,#cfe4f9);
|
||||||
color: #606266;
|
|
||||||
text-align: right;
|
|
||||||
width: 100%;
|
|
||||||
padding-right: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.content-area {
|
.content-area {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
|
|||||||
@@ -5,13 +5,13 @@
|
|||||||
<div class="top-area">
|
<div class="top-area">
|
||||||
<div class="page-title">用户管理</div>
|
<div class="page-title">用户管理</div>
|
||||||
<div class="page-search">
|
<div class="page-search">
|
||||||
<el-input placeholder="请输入手机号码查询" v-model="searchPhone" class="search-input" />
|
<el-input placeholder="请输入手机号码查询" v-model="searchPhone" style="width: 300px; margin-right: 10px" />
|
||||||
<el-button class="btn-search" @click="handleSearch">搜索</el-button>
|
<el-button class="btn-search" @click="handleSearch">搜索</el-button>
|
||||||
<!-- <el-button type="danger" @click="batchDelete">批量删除</el-button>
|
<!-- <el-button type="danger" @click="batchDelete">批量删除</el-button>
|
||||||
<el-button type="danger" @click="batchDisable">批量禁用</el-button> -->
|
<el-button type="danger" @click="batchDisable">批量禁用</el-button> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-card class="user-card" shadow="never">
|
<el-card class="user-card" shadow="never">
|
||||||
<!-- <div class="user-search-operate" style="display: flex; align-items: center; margin-bottom: 20px;">
|
<!-- <div class="user-search-operate" style="display: flex; align-items: center; margin-bottom: 20px;">
|
||||||
<el-input placeholder="请输入手机号码查询" v-model="searchPhone" style="width: 300px; margin-right: 10px" />
|
<el-input placeholder="请输入手机号码查询" v-model="searchPhone" style="width: 300px; margin-right: 10px" />
|
||||||
@@ -19,42 +19,45 @@
|
|||||||
<el-button type="danger" @click="batchDelete">批量删除</el-button>
|
<el-button type="danger" @click="batchDelete">批量删除</el-button>
|
||||||
<el-button type="danger" @click="batchDisable">批量禁用</el-button>
|
<el-button type="danger" @click="batchDisable">批量禁用</el-button>
|
||||||
</div> -->
|
</div> -->
|
||||||
<el-table :data="userList" class="transparent-table" :header-cell-class-name="headerCellClassName">
|
<el-table :data="userList" style="width: 100%;">
|
||||||
<el-table-column label="选择" type="selection" width="55"></el-table-column>
|
<el-table-column label="选择"
|
||||||
<el-table-column label="用户Id" prop="user_id"></el-table-column>
|
type="selection"
|
||||||
|
width="55">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="用户Id" prop="userid"></el-table-column>
|
||||||
<el-table-column label="手机号码" prop="mobile"></el-table-column>
|
<el-table-column label="手机号码" prop="mobile"></el-table-column>
|
||||||
<el-table-column label="设备数量" prop="device_count"></el-table-column>
|
<el-table-column label="设备数量" prop="deviceCount"></el-table-column>
|
||||||
<el-table-column label="状态" prop="status"></el-table-column>
|
<el-table-column label="状态" prop="status"></el-table-column>
|
||||||
<el-table-column label="操作">
|
<el-table-column label="操作">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button size="mini" type="text" @click="resetPassword(scope.row)" style="color: #989fdd">重置密码</el-button>
|
<el-button size="mini" type="text" @click="resetPassword(scope.row)">重置密码</el-button>
|
||||||
<el-button size="mini" type="text"
|
<el-button size="mini" type="text"
|
||||||
v-if="scope.row.status === '正常'"
|
v-if="scope.row.status === '正常'"
|
||||||
@click="disableUser(scope.row)">禁用</el-button>
|
@click="disableUser(scope.row)">禁用</el-button>
|
||||||
<el-button size="mini" type="text"
|
<el-button size="mini" type="text"
|
||||||
v-if="scope.row.status === '禁用'"
|
v-if="scope.row.status === '禁用'"
|
||||||
@click="restoreUser(scope.row)">恢复</el-button>
|
@click="restoreUser(scope.row)">恢复</el-button>
|
||||||
<el-button size="mini" type="text" @click="deleteUser(scope.row)" style="color: #989fdd">删除用户</el-button>
|
<el-button size="mini" type="text" @click="deleteUser(scope.row)" style="color: #ff4949">删除用户</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<div class="table_bottom">
|
<div class="table_bottom">
|
||||||
<div class="ctrl_btn">
|
<div class="ctrl_btn">
|
||||||
<el-button size="mini" type="primary" style="width: 72px; background: #5f70f3">全选</el-button>
|
<el-button size="mini" type="primary">全选</el-button>
|
||||||
<el-button size="mini" type="success" icon="el-icon-circle-check" style="background: #5bc98c">启用</el-button>
|
<el-button size="mini" type="success" icon="el-icon-circle-check">启用</el-button>
|
||||||
<el-button size="mini" type="warning" style="color: black; background: #f6d075"><i class="el-icon-remove-outline rotated-icon"></i>禁用</el-button>
|
<el-button size="mini" type="warning" icon="el-icon-circle-close">禁用</el-button>
|
||||||
<el-button size="mini" type="danger" icon="el-icon-delete" style="background: #fd5b63">删除</el-button>
|
<el-button size="mini" type="danger" icon="el-icon-delete">删除</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="pagination-container">
|
<div class="pagination-container">
|
||||||
<el-pagination
|
<el-pagination
|
||||||
background
|
background
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
:current-page="currentPage"
|
:current-page="currentPage"
|
||||||
:page-sizes="[5, 10, 15]"
|
:page-sizes="[5, 10, 15]"
|
||||||
:page-size="pageSize"
|
:page-size="pageSize"
|
||||||
layout="prev, pager, next"
|
layout="prev, pager, next"
|
||||||
:total="total"
|
:total="total"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -74,6 +77,7 @@ import adminApi from '@/apis/module/admin';
|
|||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
name: 'UserManagement',
|
||||||
components: { HeaderBar },
|
components: { HeaderBar },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -92,7 +96,7 @@ export default {
|
|||||||
created() {
|
created() {
|
||||||
adminApi.getUserList(({data}) => {
|
adminApi.getUserList(({data}) => {
|
||||||
//mock偶尔会返回-1导致出错,又会返回两个list,所以这里只取第一个
|
//mock偶尔会返回-1导致出错,又会返回两个list,所以这里只取第一个
|
||||||
this.userList = data.data[0].list;
|
this.userList = data.data.list;
|
||||||
console.log('用户列表:', this.userList);
|
console.log('用户列表:', this.userList);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -125,20 +129,11 @@ export default {
|
|||||||
this.currentPage = page;
|
this.currentPage = page;
|
||||||
console.log('当前页码:', page);
|
console.log('当前页码:', page);
|
||||||
},
|
},
|
||||||
headerCellClassName({ column, columnIndex }) {
|
|
||||||
if (columnIndex === 0) {
|
|
||||||
return 'custom-selection-header'
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
$table-bg-color: #ecf1fd;
|
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
padding: 20px; display: flex; flex-direction: column;
|
padding: 20px; display: flex; flex-direction: column;
|
||||||
background: linear-gradient(to bottom right, #dce8ff, #e4eeff, #e6cbfd);
|
background: linear-gradient(to bottom right, #dce8ff, #e4eeff, #e6cbfd);
|
||||||
@@ -147,7 +142,7 @@ $table-bg-color: #ecf1fd;
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 20px;
|
||||||
.page-title {
|
.page-title {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -193,10 +188,11 @@ $table-bg-color: #ecf1fd;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user-card {
|
.user-card {
|
||||||
background: $table-bg-color;
|
background: #fff;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
//box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
opacity: 0.9;
|
||||||
|
// box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.table_bottom {
|
.table_bottom {
|
||||||
@@ -215,57 +211,4 @@ $table-bg-color: #ecf1fd;
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.rotated-icon {
|
|
||||||
display: inline-block;
|
|
||||||
transform: rotate(45deg);
|
|
||||||
margin-right: 4px;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-table) {
|
|
||||||
background: $table-bg-color;
|
|
||||||
|
|
||||||
&.transparent-table {
|
|
||||||
.el-table__header th {
|
|
||||||
background: $table-bg-color !important;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-table__body tr {
|
|
||||||
background-color: $table-bg-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-input {
|
|
||||||
width: 300px;
|
|
||||||
margin-right: 10px;
|
|
||||||
:deep(.el-input__inner) {
|
|
||||||
background-color: transparent;
|
|
||||||
&:focus {
|
|
||||||
border-color: #409eff; // 保持聚焦状态下的边框颜色
|
|
||||||
}
|
|
||||||
//文字颜色
|
|
||||||
&::placeholder {
|
|
||||||
color: #606266;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.custom-selection-header) {
|
|
||||||
.el-checkbox {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '选择';
|
|
||||||
display: inline-block;
|
|
||||||
color: black;
|
|
||||||
font-weight: bold;
|
|
||||||
padding-bottom: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,231 @@
|
|||||||
|
<template>
|
||||||
|
<div class="welcome">
|
||||||
|
<!-- 公共头部 -->
|
||||||
|
<HeaderBar />
|
||||||
|
<!-- 首页内容 -->
|
||||||
|
<el-main style="padding: 20px; display: flex; flex-direction: column;">
|
||||||
|
<!-- 面包屑-->
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
<el-breadcrumb separator="/">
|
||||||
|
<el-breadcrumb-item>控制台</el-breadcrumb-item>
|
||||||
|
<el-breadcrumb-item><router-link to="/home">智能体</router-link></el-breadcrumb-item>
|
||||||
|
<el-breadcrumb-item>设备管理</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</div>
|
||||||
|
<div class="table-container">
|
||||||
|
<h3 class="device-list-title">设备列表</h3>
|
||||||
|
<el-button type="primary" class="add-device-btn" @click="handleAddDevice">
|
||||||
|
+ 添加设备
|
||||||
|
</el-button>
|
||||||
|
<el-table :data="devices" style="width: 100%; margin-top: 20px" border>
|
||||||
|
<el-table-column label="设备型号" prop="board" flex></el-table-column>
|
||||||
|
<el-table-column label="固件版本" prop="appVersion" width="140"></el-table-column>
|
||||||
|
<el-table-column label="MAC地址" prop="macAddress" width="220"></el-table-column>
|
||||||
|
<el-table-column label="绑定时间" width="260" prop="createDate" :formatter="formatter">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="最近对话" prop="recent_chat_time" width="100"></el-table-column>
|
||||||
|
<el-table-column label="备注" width="220">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input :ref="'alias_input_'+scope.$index" v-if="scope.row.isEdit" v-model="scope.row.alias" size="small" class="input-pd0" @blur="stopEditRemark(scope.$index, scope.row)"></el-input>
|
||||||
|
<span v-else>
|
||||||
|
{{ scope.row.alias }}
|
||||||
|
</span>
|
||||||
|
<i v-if="!scope.row.isEdit" class="el-icon-edit" @click="startEditRemark(scope.$index, scope.row)"></i>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="OTA升级" width="100" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-switch v-model="scope.row.autoUpdate" size="mini" :active-value="1" :inactive-value="0" active-color="#13ce66" inactive-color="#ff4949" @change="handleToggleEnabled(scope.row.id, $event)"></el-switch>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="80" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" @click="handleUnbind(scope.row.id)" style="color: #ff4949">
|
||||||
|
解绑
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-pagination background :current-page="page.pageNo" :page-size="page.pageSize" :pager-count="5" layout="prev, pager, next" :total="page.total" style="margin-top: 4px;text-align: right;"></el-pagination>
|
||||||
|
</div>
|
||||||
|
<!-- 底部 -->
|
||||||
|
<Footer :visible="true" />
|
||||||
|
<!-- 添加设备对话框 -->
|
||||||
|
<AddDeviceDialog :visible.sync="addDeviceDialogVisible" @confirm="handleDeviceBind" />
|
||||||
|
</el-main>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {getUUID, goToPage, showDanger, showSuccess} from '@/utils'
|
||||||
|
import { formatDate } from '@/utils/date'
|
||||||
|
import Api from '@/apis/api';
|
||||||
|
import AddDeviceDialog from '@/components/AddDeviceDialog.vue'
|
||||||
|
import HeaderBar from '@/components/HeaderBar.vue'
|
||||||
|
import Footer from '@/components/Footer.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DevicePage',
|
||||||
|
components: { AddDeviceDialog, HeaderBar, Footer },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
addDeviceDialogVisible: false,
|
||||||
|
devices: [],
|
||||||
|
agentId: this.$route.query.agentId,
|
||||||
|
oldVal: '',
|
||||||
|
page: {
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchDeviceList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 获取设备列表
|
||||||
|
fetchDeviceList() {
|
||||||
|
Api.device.getDeviceList(this.agentId, this.page, ({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.page = {...this.page,total:parseInt(data.data.total)}
|
||||||
|
this.devices = data.data.records.map((item)=>{item.isEdit=false;return item;})
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleAddDevice() {
|
||||||
|
// 添加设备逻辑
|
||||||
|
this.addDeviceDialogVisible = true;
|
||||||
|
},
|
||||||
|
startEditRemark(index, row) {
|
||||||
|
this.oldVal = row.alias;
|
||||||
|
this.devices[index].isEdit = true;
|
||||||
|
this.$nextTick(()=>{
|
||||||
|
this.$refs['alias_input_'+index].focus();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
stopEditRemark(index, row) {
|
||||||
|
this.devices[index].isEdit = false;
|
||||||
|
if(this.oldVal === row.alias) return;
|
||||||
|
this.handleSetAlias(row.id, row.alias);
|
||||||
|
},
|
||||||
|
handleUnbind(deviceId) {
|
||||||
|
// 解绑逻辑
|
||||||
|
console.log('解绑设备', deviceId);
|
||||||
|
Api.device.unbindDevice(deviceId, ({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
showSuccess('解绑成功')
|
||||||
|
this.fetchDeviceList()
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleDeviceBind(deviceCode) {
|
||||||
|
// 绑定逻辑
|
||||||
|
console.log('设备验证码:', deviceCode)
|
||||||
|
Api.device.bindDevice(this.agentId, deviceCode, ({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.fetchDeviceList();
|
||||||
|
this.showBindDialog = false;
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleSetAlias(id, alias) {
|
||||||
|
// 设置备注逻辑
|
||||||
|
Api.device.setAlias(id, alias, ({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
showSuccess('更新成功')
|
||||||
|
this.fetchDeviceList()
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleToggleEnabled(id, autoUpdate) {
|
||||||
|
// 更新OTA升级状态
|
||||||
|
Api.device.toggleAutoUpdate(id, autoUpdate, ({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
showSuccess('更新成功')
|
||||||
|
this.fetchDeviceList()
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
formatter(row, column) {
|
||||||
|
return formatDate(row.createDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.breadcrumbs{
|
||||||
|
padding: 10px 0 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome {
|
||||||
|
min-width: 900px;
|
||||||
|
min-height: 506px;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-image: url("@/assets/home/background.png");
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
-webkit-background-size: cover;
|
||||||
|
-o-background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
background: #f9fafc;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-device-btn {
|
||||||
|
float: right;
|
||||||
|
background: #409eff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 105px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
&:hover {
|
||||||
|
background: #3a8ee6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-list-title {
|
||||||
|
float: left;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 5px;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon-edit {
|
||||||
|
color: #409eff;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-pd0 /deep/ .el-input__inner {
|
||||||
|
padding: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="welcome">
|
<div class="welcome">
|
||||||
<!-- 公共头部 -->
|
<!-- 公共头部 -->
|
||||||
<HeaderBar :devices="devices" @search-result="handleSearchResult" />
|
<HeaderBar :devices="agents" @search-result="handleSearchResult" />
|
||||||
<el-main style="padding: 20px;display: flex;flex-direction: column;">
|
<el-main style="padding: 20px;display: flex;flex-direction: column;">
|
||||||
<div>
|
<div>
|
||||||
<!-- 首页内容 -->
|
<!-- 首页内容 -->
|
||||||
<div class="add-device">
|
<div class="add-agent">
|
||||||
<div class="add-device-bg">
|
<div class="add-agent-bg">
|
||||||
<div class="hellow-text" style="margin-top: 30px;">
|
<div class="hellow-text" style="margin-top: 30px;">
|
||||||
您好,小智
|
您好,小智
|
||||||
</div>
|
</div>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<div class="hi-hint">
|
<div class="hi-hint">
|
||||||
Hello, Let's have a wonderful day!
|
Hello, Let's have a wonderful day!
|
||||||
</div>
|
</div>
|
||||||
<div class="add-device-btn" @click="showAddDialog">
|
<div class="add-agent-btn" @click="showAddDialog">
|
||||||
<div class="left-add">
|
<div class="left-add">
|
||||||
添加智能体
|
添加智能体
|
||||||
</div>
|
</div>
|
||||||
@@ -30,99 +30,107 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 面包屑-->
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
<el-breadcrumb separator="/">
|
||||||
|
<el-breadcrumb-item>控制台</el-breadcrumb-item>
|
||||||
|
<el-breadcrumb-item>智能体</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</div>
|
||||||
|
<!-- 智能体列表-->
|
||||||
<div style="display: flex;flex-wrap: wrap;margin-top: 20px;gap: 20px;justify-content: flex-start;box-sizing: border-box;">
|
<div style="display: flex;flex-wrap: wrap;margin-top: 20px;gap: 20px;justify-content: flex-start;box-sizing: border-box;">
|
||||||
<DeviceItem v-for="(item,index) in devices" :key="index" :device="item"
|
<AgentItem v-for="(item,index) in agents" :key="index" :agent="item" @configure="goToRoleConfig" @device="goToDevice" @del="handleAgentDel" />
|
||||||
@configure="goToRoleConfig"
|
|
||||||
@deviceManage="handleDeviceManage"
|
|
||||||
@delete="handleDeleteAgent"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size: 12px;font-weight: 400;margin-top: auto;padding-top: 30px;color: #979db1;">
|
<!-- 底部 -->
|
||||||
©2025 xiaozhi-esp32-server
|
<Footer :visible="true" />
|
||||||
</div>
|
<!-- 添加设备对话框 -->
|
||||||
<AddWisdomBodyDialog :visible.sync="addDeviceDialogVisible" @confirm="handleWisdomBodyAdded" />
|
<AddAgentDialog :visible.sync="addAgentDialogVisible" @confirm="handleAgentAdded" />
|
||||||
</el-main>
|
</el-main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DeviceItem from '@/components/DeviceItem.vue'
|
import {getUUID, goToPage, showDanger, showSuccess} from '@/utils'
|
||||||
import AddWisdomBodyDialog from '@/components/AddWisdomBodyDialog.vue'
|
import Api from '@/apis/api';
|
||||||
|
import AgentItem from '@/components/AgentItem.vue'
|
||||||
|
import AddAgentDialog from '@/components/AddAgentDialog.vue'
|
||||||
import HeaderBar from '@/components/HeaderBar.vue'
|
import HeaderBar from '@/components/HeaderBar.vue'
|
||||||
|
import Footer from '@/components/Footer.vue'
|
||||||
export default {
|
export default {
|
||||||
name: 'HomePage',
|
name: 'HomePage',
|
||||||
components: { DeviceItem, AddWisdomBodyDialog, HeaderBar },
|
components: { AgentItem, AddAgentDialog, HeaderBar, Footer },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
addDeviceDialogVisible: false,
|
addAgentDialogVisible: false,
|
||||||
devices: [],
|
agents: [],
|
||||||
originalDevices: [],
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
// 获取智能体列表
|
||||||
this.fetchAgentList();
|
this.fetchAgentList();
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
showAddDialog() {
|
|
||||||
this.addDeviceDialogVisible = true
|
|
||||||
},
|
|
||||||
goToRoleConfig() {
|
|
||||||
// 点击配置角色后跳转到角色配置页
|
|
||||||
this.$router.push('/role-config')
|
|
||||||
},
|
|
||||||
handleWisdomBodyAdded(res) {
|
|
||||||
console.log('新增智能体响应:', res);
|
|
||||||
this.fetchAgentList();
|
|
||||||
this.addDeviceDialogVisible = false;
|
|
||||||
},
|
|
||||||
handleDeviceManage() {
|
|
||||||
this.$router.push('/device-management');
|
|
||||||
},
|
|
||||||
// 获取智能体列表
|
|
||||||
fetchAgentList() {
|
fetchAgentList() {
|
||||||
import('@/apis/module/user').then(({ default: userApi }) => {
|
// 获取智能体列表
|
||||||
userApi.getAgentList(({data}) => {
|
Api.agent.getAgentList(({data}) => {
|
||||||
this.originalDevices = data.data.map(item => ({
|
if (data.code === 0) {
|
||||||
...item,
|
this.agents = data.data
|
||||||
agentId: item.id // 字段映射
|
} else {
|
||||||
}));
|
showDanger(data.msg)
|
||||||
this.devices = this.originalDevices;
|
}
|
||||||
});
|
})
|
||||||
});
|
},
|
||||||
|
showAddDialog() {
|
||||||
|
this.addAgentDialogVisible = true
|
||||||
|
},
|
||||||
|
goToRoleConfig(agentId) {
|
||||||
|
// 点击配置角色后跳转到角色配置页
|
||||||
|
this.$router.push({path:'/role-config', query: {agentId: agentId}})
|
||||||
|
},
|
||||||
|
|
||||||
|
goToDevice(agentId) {
|
||||||
|
// 点击设备后跳转到设备页
|
||||||
|
this.$router.push({path:'/device', query: {agentId: agentId}})
|
||||||
|
},
|
||||||
|
handleAgentAdded(agentName) {
|
||||||
|
// 添加智能体
|
||||||
|
Api.agent.addAgent(agentName, ({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
showSuccess('添加成功')
|
||||||
|
this.fetchAgentList();
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleAgentDel(agetnId){
|
||||||
|
// 删除智能体
|
||||||
|
Api.agent.delAgent(agetnId, (data) => {
|
||||||
|
if (data.status === 200) {
|
||||||
|
showSuccess('删除成功')
|
||||||
|
this.fetchAgentList();
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
// 搜索更新智能体列表
|
// 搜索更新智能体列表
|
||||||
handleSearchResult(filteredList) {
|
handleSearchResult(filteredList) {
|
||||||
this.devices = filteredList; // 更新设备列表
|
this.agents = filteredList; // 更新设备列表
|
||||||
},
|
|
||||||
// 删除智能体
|
|
||||||
handleDeleteAgent(agentId) {
|
|
||||||
this.$confirm('确定要删除该智能体吗?', '提示', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning'
|
|
||||||
}).then(() => {
|
|
||||||
import('@/apis/module/user').then(({ default: userApi }) => {
|
|
||||||
userApi.deleteAgent(agentId, (res) => {
|
|
||||||
if (res.data.code === 0) {
|
|
||||||
this.$message.success('删除成功');
|
|
||||||
this.fetchAgentList(); // 刷新列表
|
|
||||||
} else {
|
|
||||||
this.$message.error(res.data.msg || '删除失败');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}).catch(() => {});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.breadcrumbs{
|
||||||
|
padding: 10px 0 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.welcome {
|
.welcome {
|
||||||
min-width: 900px;
|
min-width: 900px;
|
||||||
min-height: 506px;
|
min-height: 506px;
|
||||||
@@ -139,7 +147,7 @@ export default {
|
|||||||
-o-background-size: cover;
|
-o-background-size: cover;
|
||||||
/* 兼容老版本Opera浏览器 */
|
/* 兼容老版本Opera浏览器 */
|
||||||
}
|
}
|
||||||
.add-device {
|
.add-agent {
|
||||||
height: 195px;
|
height: 195px;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -151,7 +159,7 @@ export default {
|
|||||||
#d3d3fe 100%
|
#d3d3fe 100%
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
.add-device-bg {
|
.add-agent-bg {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -184,12 +192,13 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-device-btn {
|
.add-agent-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-left: 75px;
|
margin-left: 75px;
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
width: fit-content;
|
||||||
|
|
||||||
.left-add {
|
.left-add {
|
||||||
width: 105px;
|
width: 105px;
|
||||||
@@ -197,7 +206,7 @@ export default {
|
|||||||
border-radius: 17px;
|
border-radius: 17px;
|
||||||
background: #5778ff;
|
background: #5778ff;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 10px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 34px;
|
line-height: 34px;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
<div style="cursor: pointer;" @click="goToRegister">新用户注册</div>
|
<div style="cursor: pointer;" @click="goToRegister">新用户注册</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="login-btn" @click="login">登陆</div>
|
<div class="login-btn" @keyup.enter="login" @click="login">登陆</div>
|
||||||
<div style="font-size: 14px;color: #979db1;">
|
<div style="font-size: 14px;color: #979db1;">
|
||||||
登录即同意
|
登录即同意
|
||||||
<div style="display: inline-block;color: #5778FF;cursor: pointer;">《用户协议》</div>
|
<div style="display: inline-block;color: #5778FF;cursor: pointer;">《用户协议》</div>
|
||||||
@@ -88,7 +88,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
fetchCaptcha() {
|
fetchCaptcha() {
|
||||||
if (this.$store.getters.getToken) {
|
if (this.$store.getters.getToken) {
|
||||||
goToPage('/home')
|
this.$router.push('/home')
|
||||||
} else {
|
} else {
|
||||||
this.captchaUuid = getUUID();
|
this.captchaUuid = getUUID();
|
||||||
|
|
||||||
@@ -135,17 +135,17 @@ export default {
|
|||||||
// 将令牌存储到 Vuex 中
|
// 将令牌存储到 Vuex 中
|
||||||
this.$store.commit('setToken', JSON.stringify(data.data))
|
this.$store.commit('setToken', JSON.stringify(data.data))
|
||||||
|
|
||||||
goToPage('/home')
|
// this.$router.push('/home')
|
||||||
})
|
})
|
||||||
|
|
||||||
// 重新获取验证码
|
// 重新获取验证码(token判断逻辑,存在即跳转home页,否则刷新验证码)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.fetchCaptcha();
|
this.fetchCaptcha();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
},
|
},
|
||||||
|
|
||||||
goToRegister() {
|
goToRegister() {
|
||||||
goToPage('/register')
|
this.$router.push('/register')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
<template>
|
||||||
|
<div class="welcome">
|
||||||
|
<!-- 公共头部 -->
|
||||||
|
<HeaderBar />
|
||||||
|
<!-- 首页内容 -->
|
||||||
|
<el-main style="padding: 20px; display: flex; flex-direction: column;">
|
||||||
|
<!-- 面包屑-->
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
<el-breadcrumb separator="/">
|
||||||
|
<el-breadcrumb-item>控制台</el-breadcrumb-item>
|
||||||
|
<el-breadcrumb-item>OTA管理</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</div>
|
||||||
|
<div class="table-container">
|
||||||
|
<h3 class="device-list-title">OTA设备列表</h3>
|
||||||
|
<el-button type="primary" class="add-device-btn" @click="handleAddDeviceOta()">
|
||||||
|
+ 添加设备
|
||||||
|
</el-button>
|
||||||
|
<el-table :data="otaList" style="width: 100%; margin-top: 20px" border>
|
||||||
|
<el-table-column label="设备型号" prop="board" flex></el-table-column>
|
||||||
|
<el-table-column label="固件版本" prop="appVersion" width="140"></el-table-column>
|
||||||
|
<el-table-column label="升级地址" prop="url"></el-table-column>
|
||||||
|
<el-table-column label="是否启用" width="100" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-switch v-model="scope.row.isEnabled" size="mini" :active-value="1" :inactive-value="0" active-color="#13ce66" inactive-color="#ff4949" @change="handleToggleEnabled(scope.row.id, $event)"></el-switch>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="80" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" @click="handleAddDeviceOta(scope.row)" style="color: #ff4949">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-pagination background :current-page="page.pageNo" :page-size="page.pageSize" :pager-count="5" layout="prev, pager, next" :total="page.total" style="margin-top: 4px;text-align: right;"></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-main>
|
||||||
|
<!-- 底部 -->
|
||||||
|
<Footer :visible="true" />
|
||||||
|
<!-- 添加设备OTA对话框 -->
|
||||||
|
<AddDeviceOtaDialog :visible.sync="addDeviceOtaDialogVisible" :data="otaData" @confirmSave="handleSaveDeviceOta" @confirmUpdate="handleUpdateDeviceOta" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {getUUID, goToPage, showDanger, showSuccess} from '@/utils'
|
||||||
|
import Api from '@/apis/api';
|
||||||
|
import AddDeviceOtaDialog from '@/components/AddDeviceOtaDialog.vue'
|
||||||
|
import HeaderBar from '@/components/HeaderBar.vue'
|
||||||
|
import Footer from '@/components/Footer.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DeviceOtaPage',
|
||||||
|
components: { AddDeviceOtaDialog, HeaderBar, Footer },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
addDeviceOtaDialogVisible: false,
|
||||||
|
otaList: [],
|
||||||
|
otaData: {},
|
||||||
|
page: {
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchDeviceOtaList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 获取设备列表
|
||||||
|
fetchDeviceOtaList() {
|
||||||
|
Api.ota.getOtaList(this.page, ({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.page = {...this.page,total:parseInt(data.data.total)}
|
||||||
|
this.otaList = data.data.records
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleAddDeviceOta(ota) {
|
||||||
|
if (ota) {
|
||||||
|
this.otaData = ota
|
||||||
|
}
|
||||||
|
// 添加设备逻辑
|
||||||
|
this.addDeviceOtaDialogVisible = true;
|
||||||
|
},
|
||||||
|
handleSaveDeviceOta(ota) {
|
||||||
|
console.log('添加设备', ota);
|
||||||
|
Api.ota.saveOta(ota, ({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
showSuccess('添加成功')
|
||||||
|
this.fetchDeviceOtaList()
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
this.otaData = {}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleUpdateDeviceOta(ota) {
|
||||||
|
// 解绑逻辑
|
||||||
|
console.log('修改设备', ota);
|
||||||
|
Api.ota.updateOta(ota, ({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
showSuccess('修改成功')
|
||||||
|
this.fetchDeviceOtaList()
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
this.otaData = {}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleToggleEnabled(id, isEnabled) {
|
||||||
|
// 启用禁用逻辑
|
||||||
|
Api.ota.toggleEnabled({id, isEnabled}, ({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
showSuccess('更新成功')
|
||||||
|
this.fetchDeviceOtaList()
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.breadcrumbs{
|
||||||
|
padding: 10px 0 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome {
|
||||||
|
min-width: 900px;
|
||||||
|
min-height: 506px;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-image: url("@/assets/home/background.png");
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
-webkit-background-size: cover;
|
||||||
|
-o-background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
background: #f9fafc;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-device-btn {
|
||||||
|
float: right;
|
||||||
|
background: #409eff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 105px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
&:hover {
|
||||||
|
background: #3a8ee6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-list-title {
|
||||||
|
float: left;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 5px;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon-edit {
|
||||||
|
color: #409eff;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,71 +1,72 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="welcome">
|
<div class="welcome">
|
||||||
|
<!-- 公共头部 -->
|
||||||
<HeaderBar/>
|
<HeaderBar/>
|
||||||
<el-main style="padding: 16px;display: flex;flex-direction: column;">
|
<!-- 面包屑-->
|
||||||
<div style="border-radius: 16px;background: #fafcfe; border: 1px solid #e8f0ff;">
|
<div class="breadcrumbs" style="padding: 20px;">
|
||||||
|
<el-breadcrumb separator="/">
|
||||||
|
<el-breadcrumb-item>控制台</el-breadcrumb-item>
|
||||||
|
<el-breadcrumb-item><router-link to="/home">智能体</router-link></el-breadcrumb-item>
|
||||||
|
<el-breadcrumb-item>配置智能体</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</div>
|
||||||
|
<el-main style="padding: 16px;display: flex;flex-direction: column;align-items: center;">
|
||||||
|
<div style="border-radius: 16px;background: #fafcfe; border: 1px solid #e8f0ff;max-width: 800px;">
|
||||||
<div
|
<div
|
||||||
style="padding: 15px 24px;font-weight: 700;font-size: 19px;text-align: left;color: #3d4566;display: flex;gap: 13px;align-items: center;">
|
style="padding: 15px 24px;font-weight: 700;font-size: 19px;text-align: left;color: #3d4566;display: flex;gap: 13px;align-items: center;">
|
||||||
<div
|
<div
|
||||||
style="width: 37px;height: 37px;background: #5778ff;border-radius: 50%;display: flex;align-items: center;justify-content: center;">
|
style="width: 37px;height: 37px;background: #5778ff;border-radius: 50%;display: flex;align-items: center;justify-content: center;">
|
||||||
<img src="@/assets/home/setting-user.png" alt="" style="width: 19px;height: 19px;"/>
|
<img src="@/assets/home/setting-user.png" alt="" style="width: 19px;height: 19px;"/>
|
||||||
</div>
|
</div>
|
||||||
{{ form.agentName }}
|
{{ form.agentName }} ({{ agentId }})
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 1px;background: #e8f0ff;"/>
|
<div style="height: 1px;background: #e8f0ff;"/>
|
||||||
<el-form ref="form" :model="form" label-width="72px">
|
<div style="padding: 16px 24px;max-width: 792px;">
|
||||||
<div style="padding: 16px 24px;max-width: 792px;">
|
<el-form ref="form" :model="form" label-width="100px">
|
||||||
<el-form-item label="助手昵称:">
|
|
||||||
<div class="input-46" style="width: 100%; max-width: 412px;">
|
|
||||||
<el-input v-model="form.agentName"/>
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="角色模版:">
|
<el-form-item label="角色模版:">
|
||||||
<div style="display: flex;gap: 8px;">
|
<div style="display: flex;gap: 8px;flex-wrap: wrap;">
|
||||||
<div v-for="template in templates" :key="template" class="template-item" :class="{ 'template-loading': loadingTemplate }" @click="selectTemplate(template)">
|
<div v-for="template in templates" :key="template.id" class="template-item" @click="selectTemplate(template)">
|
||||||
{{ template }}
|
{{ template.agentName }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="助手昵称:">
|
||||||
|
<el-input v-model="form.agentCode"/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="角色音色:">
|
<el-form-item label="角色音色:">
|
||||||
<div style="display: flex;gap: 8px;align-items: center;">
|
<div style="display: flex;gap: 8px;align-items: center;">
|
||||||
<div class="input-46" style="flex:1.4;">
|
<div style="flex:1;">
|
||||||
<el-select v-model="form.ttsVoiceId" placeholder="请选择" style="width: 100%;">
|
<el-select v-model="form.ttsVoiceId" placeholder="请选择" @change="handleTtsVoiceChange" style="width: 100%;">
|
||||||
<el-option v-for="item in options" :key="item.value" :label="item.label"
|
<el-option v-for="item in ttsVoices" :key="item.id" :label="item.name"
|
||||||
:value="item.value">
|
:value="item.id">
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="audio-box">
|
<div class="audio-box">
|
||||||
<audio src="http://music.163.com/song/media/outer/url?id=447925558.mp3" controls
|
<audio :src="voiceDemo" controls
|
||||||
style="height: 100%;width: 100%;"/>
|
style="height: 100%;width: 100%;"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="角色介绍:">
|
<el-form-item label="角色介绍:">
|
||||||
<div class="textarea-box">
|
<el-input type="textarea" rows="5" resize="none" placeholder="请输入内容" v-model="form.systemPrompt" maxlength="2000" show-word-limit/>
|
||||||
<el-input type="textarea" rows="5" resize="none" placeholder="请输入内容"
|
|
||||||
v-model="form.systemPrompt" maxlength="2000" show-word-limit/>
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="记忆体:">
|
<el-form-item label="记忆体:">
|
||||||
<div class="textarea-box">
|
<el-input type="textarea" rows="5" resize="none" placeholder="请输入内容" v-model="form.prompt" maxlength="1000" show-word-limit/>
|
||||||
<el-input type="textarea" rows="5" resize="none" placeholder="请输入内容"
|
<div style="display: flex;gap: 8px;align-items: center;">
|
||||||
v-model="form.langCode" maxlength="1000"/>
|
<div style="color: #979db1;font-size: 11px;">当前记忆(每次对话后重新生成)</div>
|
||||||
<div class="prompt-bottom" @click="clearMemory">
|
<div class="clear-btn">
|
||||||
<div style="display: flex;gap: 8px;align-items: center;">
|
<i class="el-icon-delete-solid" style="font-size: 11px;"/>
|
||||||
<div style="color: #979db1;font-size: 11px;">当前记忆(每次对话后重新生成)</div>
|
清除
|
||||||
<div class="clear-btn">
|
|
||||||
<i class="el-icon-delete-solid" style="font-size: 11px;"/>
|
|
||||||
清除
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div style="color: #979db1;font-size:11px;">{{ form.langCode.length }}/1000</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-for="model in models" :key="model.label" :label="model.label" class="model-item">
|
<el-form-item v-for="model in models" :key="model.label" :label="model.label">
|
||||||
<el-select v-model="form.model[model.key]" filterable placeholder="请选择" class="select-field">
|
<template slot="label">
|
||||||
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"/>
|
<div style="line-height: 20px;">{{model.label}}</div>
|
||||||
|
</template>
|
||||||
|
<el-select v-model="form[model.key+'ModelId']" filterable placeholder="请选择" style="width: 100%;" disabled>
|
||||||
|
<el-option v-for="item in model.list" :key="item.id" :label="item.modelName" :value="item.id"/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="" class="lh-form-item" style="margin-top: -25px;">
|
<el-form-item label="" class="lh-form-item" style="margin-top: -25px;">
|
||||||
@@ -73,8 +74,8 @@
|
|||||||
实时”,其他模型通常会增加约1秒的延迟。改变模型后,建议清空记忆体,以免影响体验。
|
实时”,其他模型通常会增加约1秒的延迟。改变模型后,建议清空记忆体,以免影响体验。
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
|
</div>
|
||||||
<div style="display: flex;padding: 16px;gap: 8px;align-items: center;">
|
<div style="display: flex;padding: 16px;gap: 8px;align-items: center;">
|
||||||
<div class="save-btn" @click="saveConfig">
|
<div class="save-btn" @click="saveConfig">
|
||||||
保存配置
|
保存配置
|
||||||
@@ -88,80 +89,112 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size: 12px;font-weight: 400;margin-top: auto;padding-top: 24px;color: #979db1;">
|
|
||||||
©2025 xiaozhi-esp32-server
|
|
||||||
</div>
|
|
||||||
</el-main>
|
</el-main>
|
||||||
|
<Footer :visible="true" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Api from '@/apis/api';
|
||||||
import HeaderBar from "@/components/HeaderBar.vue";
|
import HeaderBar from "@/components/HeaderBar.vue";
|
||||||
|
import Footer from "@/components/Footer.vue";
|
||||||
|
import {getUUID, goToPage, showDanger, showSuccess} from '@/utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'RoleConfigPage',
|
name: 'RoleConfigPage',
|
||||||
components: {HeaderBar},
|
components: {HeaderBar,Footer},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
agentId: this.$route.query.agentId,
|
||||||
form: {
|
form: {
|
||||||
agentCode: "",
|
agentCode:"",
|
||||||
agentName: "",
|
agentName:"",
|
||||||
ttsVoiceId: "",
|
asrModelId:"",
|
||||||
systemPrompt: "",
|
intentModelId:"",
|
||||||
langCode: "",
|
llmModelId:"",
|
||||||
language: "",
|
memoryModelId:"",
|
||||||
sort: "",
|
systemPrompt:"",
|
||||||
model: {
|
ttsModelId:"",
|
||||||
ttsModelId: "",
|
ttsVoiceId:"",
|
||||||
vadModelId: "",
|
vadModelId:"",
|
||||||
asrModelId: "",
|
model:{}
|
||||||
llmModelId: "",
|
|
||||||
memModelId: "",
|
|
||||||
intentModelId: "",
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
options: [
|
ttsVoices: [],
|
||||||
{value: '选项1', label: '黄金糕'},
|
voiceDemo: "",
|
||||||
{value: '选项2', label: '双皮奶'}
|
|
||||||
],
|
|
||||||
models: [
|
models: [
|
||||||
{label: '大语言模型(LLM)', key: 'llmModelId'},
|
{ label: '大语言模型(LLM)', key: 'llm', list: [] },
|
||||||
{label: '语音识别(ASR)', key: 'asrModelId'},
|
{ label: '语音转文本模型(ASR)', key: 'asr', list: [] },
|
||||||
{label: '语音活动检测模型(VAD)', key: 'vadModelId'},
|
{ label: '语音活动检测模型(VAD)', key: 'vad', list: [] },
|
||||||
{label: '语音合成模型(TTS)', key: 'ttsModelId'},
|
{ label: '语音生成模型(TTS)', key: 'tts', list: [] },
|
||||||
{label: '意图识别模型(Intent)', key: 'intentModelId'},
|
{ label: '意图分类模型(Intent)', key: 'intent', list: [] },
|
||||||
{label: '记忆模型(Memory)', key: 'memModelId'}
|
{ label: '记忆增强模型(Memory)', key: 'memory', list: [] }
|
||||||
],
|
],
|
||||||
templates: ['台湾女友', '土豆子', '英语老师', '好奇小男孩', '汪汪队队长'],
|
templates: []
|
||||||
loadingTemplate: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
// 获取智能体列表
|
||||||
|
this.fetchAgentTemplateList();
|
||||||
|
this.handleGetConfig();
|
||||||
|
this.fetchModelList();
|
||||||
|
this.getTtsVoicelList();
|
||||||
|
setTimeout(() => {
|
||||||
|
this.handleTtsVoiceChange(this.form.ttsVoiceId)
|
||||||
|
}, 1500)
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
fetchAgentTemplateList() {
|
||||||
|
// 获取智能体列表
|
||||||
|
Api.agent.getAgentTemplateList(({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.templates = data.data
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchModelList() {
|
||||||
|
// 获取模型配置列表
|
||||||
|
Api.model.getModelList(({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
let models = data.data;
|
||||||
|
this.models.map(model => {
|
||||||
|
model.list = models.filter(item => item.modelType.toLowerCase() === model.key)
|
||||||
|
})
|
||||||
|
console.log("models", this.models)
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getTtsVoicelList(ttsModelId) {
|
||||||
|
// 获取智能体列表
|
||||||
|
Api.model.getTtsVoiceList(ttsModelId || '',({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.ttsVoices = data.data
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleGetConfig(){
|
||||||
|
Api.agent.getAgentConfig(this.agentId, ({data}) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.form = data.data
|
||||||
|
} else {
|
||||||
|
showDanger(data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
saveConfig() {
|
saveConfig() {
|
||||||
const configData = {
|
// 此处写保存配置逻辑
|
||||||
agentCode: this.form.agentCode,
|
Api.agent.saveAgentConfig(this.agentId, this.form, ({data}) => {
|
||||||
agentName: this.form.agentName,
|
if (data.code === 0) {
|
||||||
asrModelId: this.form.model.asrModelId,
|
showSuccess('保存成功')
|
||||||
vadModelId: this.form.model.vadModelId,
|
} else {
|
||||||
llmModelId: this.form.model.llmModelId,
|
showDanger(data.msg)
|
||||||
ttsModelId: this.form.model.ttsModelId,
|
}
|
||||||
ttsVoiceId: this.form.ttsVoiceId,
|
})
|
||||||
memModelId: this.form.model.memModelId,
|
|
||||||
intentModelId: this.form.model.intentModelId,
|
|
||||||
systemPrompt: this.form.systemPrompt,
|
|
||||||
langCode: this.form.langCode,
|
|
||||||
language: this.form.language,
|
|
||||||
sort: this.form.sort
|
|
||||||
};
|
|
||||||
import('@/apis/module/user').then(({default: userApi}) => {
|
|
||||||
userApi.updateAgentConfig(this.$route.query.agentId, configData, ({data}) => {
|
|
||||||
if (data.code === 0) {
|
|
||||||
this.$message.success('配置保存成功');
|
|
||||||
} else {
|
|
||||||
this.$message.error(data.msg || '配置保存失败');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
resetConfig() {
|
resetConfig() {
|
||||||
this.$confirm('确定要重置配置吗?', '提示', {
|
this.$confirm('确定要重置配置吗?', '提示', {
|
||||||
@@ -171,105 +204,47 @@ export default {
|
|||||||
}).then(() => {
|
}).then(() => {
|
||||||
// 重置表单
|
// 重置表单
|
||||||
this.form = {
|
this.form = {
|
||||||
agentCode: "",
|
name: "",
|
||||||
agentName: "",
|
timbre: "",
|
||||||
ttsVoiceId: "",
|
introduction: "",
|
||||||
systemPrompt: "",
|
prompt: "",
|
||||||
langCode: "",
|
model: ""
|
||||||
language: "",
|
|
||||||
sort: "",
|
|
||||||
model: {
|
|
||||||
ttsModelId: "",
|
|
||||||
vadModelId: "",
|
|
||||||
asrModelId: "",
|
|
||||||
llmModelId: "",
|
|
||||||
memModelId: "",
|
|
||||||
intentModelId: "",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.$message.success('配置已重置')
|
this.$message.success('配置已重置')
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
selectTemplate(templateName) {
|
// 处理选择模板的逻辑
|
||||||
if (this.loadingTemplate) return;
|
selectTemplate(template) {
|
||||||
|
Object.assign(this.form,{
|
||||||
this.loadingTemplate = true;
|
agentCode: template.agentCode,
|
||||||
import('@/apis/module/user').then(({default: userApi}) => {
|
llmModelId: template.llmModelId,
|
||||||
userApi.getAgentTemplate(
|
asrModelId: template.asrModelId,
|
||||||
{templateName},
|
vadModelId: template.vadModelId,
|
||||||
(response) => {
|
ttsModelId: template.ttsModelId,
|
||||||
this.loadingTemplate = false;
|
ttsVoiceId: template.ttsVoiceId,
|
||||||
if (response.data.code === 0 && response.data.data.length > 0) {
|
intentModelId: template.intentModelId,
|
||||||
this.applyTemplateData(response.data.data[0]);
|
memoryModelId: template.memoryModelId,
|
||||||
this.$message.success(`「${templateName}」模板已应用`);
|
systemPrompt: template.systemPrompt
|
||||||
} else {
|
})
|
||||||
this.$message.warning(`未找到「${templateName}」模板`);
|
this.$message.success(`已选择模板:${template.agentName}`);
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}).catch((error) => {
|
|
||||||
this.loadingTemplate = false;
|
|
||||||
this.$message.error('模板加载失败');
|
|
||||||
console.error('接口异常:', error);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
applyTemplateData(templateData) {
|
handleTtsVoiceChange(ttsVoiceId) {
|
||||||
this.form = {
|
if(ttsVoiceId){
|
||||||
...this.form,
|
const _ttsVoice = this.ttsVoices.find(item => item.id === ttsVoiceId)
|
||||||
agentName: templateData.agentName || this.form.agentName,
|
this.voiceDemo = _ttsVoice.voiceDemo
|
||||||
ttsVoiceId: templateData.ttsVoiceId || this.form.ttsVoiceId,
|
this.form.ttsModelId = _ttsVoice.ttsModelId
|
||||||
systemPrompt: templateData.systemPrompt || this.form.systemPrompt,
|
}
|
||||||
langCode: templateData.langCode || this.form.langCode,
|
|
||||||
model: {
|
|
||||||
ttsModelId: templateData.ttsModelId || this.form.model.ttsModelId,
|
|
||||||
vadModelId: templateData.vadModelId || this.form.model.vadModelId,
|
|
||||||
asrModelId: templateData.asrModelId || this.form.model.asrModelId,
|
|
||||||
llmModelId: templateData.llmModelId || this.form.model.llmModelId,
|
|
||||||
memModelId: templateData.memModelId || this.form.model.memModelId,
|
|
||||||
intentModelId: templateData.intentModelId || this.form.model.intentModelId
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
fetchAgentConfig(agentId) {
|
|
||||||
import('@/apis/module/user').then(({default: userApi}) => {
|
|
||||||
userApi.getDeviceConfig(agentId, ({data}) => {
|
|
||||||
if (data.code === 0) {
|
|
||||||
this.form = {
|
|
||||||
...this.form,
|
|
||||||
...data.data,
|
|
||||||
model: {
|
|
||||||
ttsModelId: data.data.ttsModelId,
|
|
||||||
vadModelId: data.data.vadModelId,
|
|
||||||
asrModelId: data.data.asrModelId,
|
|
||||||
llmModelId: data.data.llmModelId,
|
|
||||||
memModelId: data.data.memModelId,
|
|
||||||
intentModelId: data.data.intentModelId
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this.$message.error(data.msg || '获取配置失败');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 清空记忆体内容
|
|
||||||
clearMemory() {
|
|
||||||
this.form.langCode = "";
|
|
||||||
this.$message.success("记忆体已清空");
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
const agentId = this.$route.query.agentId;
|
|
||||||
console.log('agentId2222',agentId);
|
|
||||||
if (agentId) {
|
|
||||||
this.fetchAgentConfig(agentId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.breadcrumbs{
|
||||||
|
padding: 20px 0 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.welcome {
|
.welcome {
|
||||||
min-width: 900px;
|
min-width: 900px;
|
||||||
min-height: 506px;
|
min-height: 506px;
|
||||||
@@ -287,23 +262,6 @@ export default {
|
|||||||
/* 兼容老版本Opera浏览器 */
|
/* 兼容老版本Opera浏览器 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-form-item ::v-deep .el-form-item__label {
|
|
||||||
font-size: 10px !important;
|
|
||||||
color: #3d4566 !important;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 22px;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-field{
|
|
||||||
width: 100%;
|
|
||||||
max-width: 720px;
|
|
||||||
border: 1px solid #e4e6ef;
|
|
||||||
background: #f6f8fb;
|
|
||||||
border-radius: 8px;
|
|
||||||
height: 36px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.audio-box {
|
.audio-box {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 37px;
|
height: 37px;
|
||||||
@@ -332,13 +290,11 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.template-item {
|
.template-item {
|
||||||
height: 37px;
|
padding: 0 20px;
|
||||||
width: 76px;
|
border-radius: 6px;
|
||||||
border-radius: 8px;
|
|
||||||
background: #e6ebff;
|
background: #e6ebff;
|
||||||
line-height: 37px;
|
font-weight: 500;
|
||||||
font-weight: 400;
|
font-size: 14px;
|
||||||
font-size: 11px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #5778ff;
|
color: #5778ff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -349,21 +305,6 @@ export default {
|
|||||||
background-color: #d0d8ff;
|
background-color: #d0d8ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prompt-bottom {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0 16px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-46 {
|
|
||||||
border: 1px solid #e4e6ef;
|
|
||||||
background: #f6f8fb;
|
|
||||||
border-radius: 8px;
|
|
||||||
height: 36px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.save-btn,
|
.save-btn,
|
||||||
.reset-btn {
|
.reset-btn {
|
||||||
width: 112px;
|
width: 112px;
|
||||||
@@ -386,11 +327,5 @@ export default {
|
|||||||
background: #e6ebff;
|
background: #e6ebff;
|
||||||
color: #5778ff;
|
color: #5778ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textarea-box {
|
|
||||||
border: 1px solid #e4e6ef;
|
|
||||||
border-radius: 8px;
|
|
||||||
background: #f6f8fb;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -82,10 +82,9 @@ selected_module:
|
|||||||
TTS: EdgeTTS
|
TTS: EdgeTTS
|
||||||
# 记忆模块,默认不开启记忆;如果想使用超长记忆,推荐使用mem0ai;如果注重隐私,请使用本地的mem_local_short
|
# 记忆模块,默认不开启记忆;如果想使用超长记忆,推荐使用mem0ai;如果注重隐私,请使用本地的mem_local_short
|
||||||
Memory: nomem
|
Memory: nomem
|
||||||
# 意图识别模块开启后,可以播放音乐、控制音量、识别退出指令。
|
# 意图识别模块,默认使用function_call。开启后,可以播放音乐、控制音量、识别退出指令
|
||||||
# 不想开通意图识别,就设置成:nointent
|
# 意图识别使用intent_llm,优点:通用性强,缺点:增加串行前置意图识别模块,会增加处理时间
|
||||||
# 意图识别可使用intent_llm,如果你的LLM是DifyLLM或CozeLLM,建议使用这个。优点:通用性强,缺点:增加串行前置意图识别模块,会增加处理时间,这个意图识别暂时不支持控制音量大小等iot操作
|
# 意图识别使用function_call,缺点:需要所选择的LLM支持function_call,优点:按需调用工具、速度快
|
||||||
# 意图识别可使用function_call,缺点:需要所选择的LLM支持function_call,优点:按需调用工具、速度快,理论上能全部操作所有iot指令
|
|
||||||
# 默认免费的ChatGLMLLM就已经支持function_call,但是如果像追求稳定建议把LLM设置成:DoubaoLLM,使用的具体model_name是:doubao-pro-32k-functioncall-241028
|
# 默认免费的ChatGLMLLM就已经支持function_call,但是如果像追求稳定建议把LLM设置成:DoubaoLLM,使用的具体model_name是:doubao-pro-32k-functioncall-241028
|
||||||
Intent: function_call
|
Intent: function_call
|
||||||
|
|
||||||
@@ -98,13 +97,9 @@ Intent:
|
|||||||
intent_llm:
|
intent_llm:
|
||||||
# 不需要动type
|
# 不需要动type
|
||||||
type: intent_llm
|
type: intent_llm
|
||||||
# 配备意图识别独立的思考模型
|
|
||||||
# 如果这里不填,则会默认使用selected_module.LLM的模型作为意图识别的思考模型
|
|
||||||
# 如果你的selected_module.LLM选择了DifyLLM或CozeLLM,这里最好使用独立的LLM作为意图识别,例如使用免费的ChatGLMLLM
|
|
||||||
llm: ChatGLMLLM
|
|
||||||
function_call:
|
function_call:
|
||||||
# 不需要动type
|
# 不需要动type
|
||||||
type: function_call
|
type: nointent
|
||||||
# plugins_func/functions下的模块,可以通过配置,选择加载哪个模块,加载后对话支持相应的function调用
|
# plugins_func/functions下的模块,可以通过配置,选择加载哪个模块,加载后对话支持相应的function调用
|
||||||
# 系统默认已经记载“handle_exit_intent(退出识别)”、“play_music(音乐播放)”插件,请勿重复加载
|
# 系统默认已经记载“handle_exit_intent(退出识别)”、“play_music(音乐播放)”插件,请勿重复加载
|
||||||
# 下面是加载查天气、角色切换、加载查新闻的插件示例
|
# 下面是加载查天气、角色切换、加载查新闻的插件示例
|
||||||
@@ -192,7 +187,7 @@ LLM:
|
|||||||
# 可在这里找到你的 api_key https://bailian.console.aliyun.com/?apiKey=1#/api-key
|
# 可在这里找到你的 api_key https://bailian.console.aliyun.com/?apiKey=1#/api-key
|
||||||
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
|
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||||
model_name: qwen-turbo
|
model_name: qwen-turbo
|
||||||
api_key: 你的deepseek web key
|
api_key: 你的ali api key
|
||||||
temperature: 0.7 # 温度值
|
temperature: 0.7 # 温度值
|
||||||
max_tokens: 500 # 最大生成token数
|
max_tokens: 500 # 最大生成token数
|
||||||
top_p: 1
|
top_p: 1
|
||||||
@@ -283,18 +278,6 @@ LLM:
|
|||||||
variables:
|
variables:
|
||||||
k: "v"
|
k: "v"
|
||||||
k2: "v2"
|
k2: "v2"
|
||||||
XinferenceLLM:
|
|
||||||
# 定义LLM API类型
|
|
||||||
type: xinference
|
|
||||||
# Xinference服务地址和模型名称
|
|
||||||
model_name: qwen2.5:72b-AWQ # 使用的模型名称,需要预先在Xinference启动对应模型
|
|
||||||
base_url: http://localhost:9997 # Xinference服务地址
|
|
||||||
XinferenceSmallLLM:
|
|
||||||
# 定义轻量级LLM API类型,用于意图识别
|
|
||||||
type: xinference
|
|
||||||
# Xinference服务地址和模型名称
|
|
||||||
model_name: qwen2.5:3b-AWQ # 使用的小模型名称,用于意图识别
|
|
||||||
base_url: http://localhost:9997 # Xinference服务地址
|
|
||||||
TTS:
|
TTS:
|
||||||
# 当前支持的type为edge、doubao,可自行适配
|
# 当前支持的type为edge、doubao,可自行适配
|
||||||
EdgeTTS:
|
EdgeTTS:
|
||||||
@@ -552,3 +535,11 @@ wakeup_words:
|
|||||||
- "喵喵同学"
|
- "喵喵同学"
|
||||||
- "小滨小滨"
|
- "小滨小滨"
|
||||||
- "小冰小冰"
|
- "小冰小冰"
|
||||||
|
|
||||||
|
# 是否使用私有配置
|
||||||
|
use_private_config: true
|
||||||
|
# 远程配置
|
||||||
|
# 数据格式:{"code": 0,"msg": "success","data": {"prompt": "我是小智","owner": "用户ID","ASR": {"FunASR": {}},"LLM": {"ChatGLMLLM": {}},"TTS": {"EdgeTTS": {}},"VAD": {"SileroVAD": {}},"Intent": {"function_call": {}},"Memory": {"nomem": {}},"selected_module": {"ASR": "FunASR","LLM": "ChatGLMLLM","TTS": "EdgeTTS","Intent": "function_call","Memory": "nomem","VAD": "SileroVAD"}}}
|
||||||
|
remote_config:
|
||||||
|
enabled: true
|
||||||
|
url: http://192.168.5.11:8002/xiaozhi-esp32-api/api/v1/user/agent/loadAgentConfig/
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import yaml
|
import yaml
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
from config.logger import setup_logging
|
from config.logger import setup_logging
|
||||||
from typing import Dict, Any, Optional
|
from typing import Dict, Any, Optional
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
@@ -22,6 +24,14 @@ class PrivateConfig:
|
|||||||
|
|
||||||
async def load_or_create(self):
|
async def load_or_create(self):
|
||||||
try:
|
try:
|
||||||
|
# 优先通过远程获取配置
|
||||||
|
fetch_config_ = {}
|
||||||
|
remote_config = self.default_config['remote_config']
|
||||||
|
self.logger.bind(tag=TAG).info(f"remote config: {remote_config}")
|
||||||
|
if remote_config and remote_config['enabled']:
|
||||||
|
fetch_config_ = await self.fetch_config(remote_config['url'])
|
||||||
|
|
||||||
|
self.logger.bind(tag=TAG).info(f"fetch_config: {fetch_config_}")
|
||||||
await self.lock_manager.acquire_lock(self.config_path)
|
await self.lock_manager.acquire_lock(self.config_path)
|
||||||
try:
|
try:
|
||||||
if os.path.exists(self.config_path):
|
if os.path.exists(self.config_path):
|
||||||
@@ -30,6 +40,9 @@ class PrivateConfig:
|
|||||||
else:
|
else:
|
||||||
all_configs = {}
|
all_configs = {}
|
||||||
|
|
||||||
|
if fetch_config_:
|
||||||
|
all_configs[self.device_id] = fetch_config_
|
||||||
|
|
||||||
if self.device_id not in all_configs:
|
if self.device_id not in all_configs:
|
||||||
# Get selected module names
|
# Get selected module names
|
||||||
selected_modules = self.default_config['selected_module']
|
selected_modules = self.default_config['selected_module']
|
||||||
@@ -64,9 +77,9 @@ class PrivateConfig:
|
|||||||
|
|
||||||
all_configs[self.device_id] = device_config
|
all_configs[self.device_id] = device_config
|
||||||
|
|
||||||
# Save updated configs
|
# Save updated configs
|
||||||
with open(self.config_path, 'w', encoding='utf-8') as f:
|
with open(self.config_path, 'w', encoding='utf-8') as f:
|
||||||
yaml.dump(all_configs, f, allow_unicode=True)
|
yaml.dump(all_configs, f, allow_unicode=True)
|
||||||
|
|
||||||
self.private_config = all_configs[self.device_id]
|
self.private_config = all_configs[self.device_id]
|
||||||
|
|
||||||
@@ -238,4 +251,60 @@ class PrivateConfig:
|
|||||||
|
|
||||||
def get_owner(self) -> Optional[str]:
|
def get_owner(self) -> Optional[str]:
|
||||||
"""获取设备当前所有者"""
|
"""获取设备当前所有者"""
|
||||||
return self.private_config.get('owner')
|
return self.private_config.get('owner')
|
||||||
|
|
||||||
|
async def fetch_config(self, fetch_url: str) -> Dict[str, Any]:
|
||||||
|
"""通过HTTP请求远程获取配置"""
|
||||||
|
url = f"{fetch_url}{self.device_id}"
|
||||||
|
headers = {
|
||||||
|
'device_id': self.device_id
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
response = requests.get(url, headers=headers)
|
||||||
|
response.raise_for_status() # 如果响应状态码不是200,会抛出异常
|
||||||
|
# 检查响应内容类型是否为JSON
|
||||||
|
if response.headers.get('Content-Type') != 'application/json':
|
||||||
|
self.logger.bind(tag=TAG).error("Invalid content type: expected application/json")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# 解析JSON数据
|
||||||
|
config_data = response.json()
|
||||||
|
|
||||||
|
# 验证返回的数据结构
|
||||||
|
if not isinstance(config_data, dict):
|
||||||
|
self.logger.bind(tag=TAG).error("Invalid data format: expected a dictionary")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if config_data['code'] != 0:
|
||||||
|
self.logger.bind(tag=TAG).error(f"Fetch config error: {config_data['msg']}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
config_data_ = config_data['data']
|
||||||
|
if not isinstance(config_data_, dict):
|
||||||
|
self.logger.bind(tag=TAG).error("Invalid config data format: expected a dictionary")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
# 检查必要的字段是否存在
|
||||||
|
required_fields = ['selected_module', 'prompt', 'LLM', 'TTS', 'ASR', 'VAD']
|
||||||
|
for field in required_fields:
|
||||||
|
if field not in config_data_:
|
||||||
|
self.logger.bind(tag=TAG).error(f"Missing required field: {field}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# 检查每个模块的配置是否正确
|
||||||
|
for module in ['LLM', 'TTS', 'ASR', 'VAD']:
|
||||||
|
if not isinstance(config_data_[module], dict):
|
||||||
|
self.logger.bind(tag=TAG).error(f"Invalid data format for {module}: expected a dictionary")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
selected_module = config_data_['selected_module'].get(module)
|
||||||
|
if selected_module not in config_data_[module]:
|
||||||
|
self.logger.bind(tag=TAG).error(f"Selected {module} not found in config: {selected_module}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
return config_data_
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
self.logger.bind(tag=TAG).error(f"Error fetching config: {e}")
|
||||||
|
return {}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
'''
|
||||||
|
自定义记忆,可以选择此模块
|
||||||
|
'''
|
||||||
|
from ..base import MemoryProviderBase, logger
|
||||||
|
|
||||||
|
TAG = __name__
|
||||||
|
|
||||||
|
class MemoryProvider(MemoryProviderBase):
|
||||||
|
def __init__(self, config):
|
||||||
|
super().__init__(config)
|
||||||
|
|
||||||
|
async def save_memory(self, msgs):
|
||||||
|
logger.bind(tag=TAG).debug("mem_custom mode: Custom memory saving is performed.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def query_memory(self, query: str)-> str:
|
||||||
|
logger.bind(tag=TAG).debug("mem_custom mode: Custom memory query is performed.")
|
||||||
|
return ""
|
||||||
Reference in New Issue
Block a user