feature:

1.OTA基本能力(固定配置),待优化
2.智能体管理与设备管理对接api,实现流程闭环(已测试)
This commit is contained in:
Erlei Chen
2025-03-24 09:26:45 +08:00
parent 66352de1eb
commit bdd18b384b
55 changed files with 1747 additions and 332 deletions
@@ -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;
}
}
@@ -0,0 +1,195 @@
package xiaozhi.modules.agent.controller;
import cn.hutool.json.JSONObject;
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.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.agent.domain.Agent;
import xiaozhi.modules.agent.domain.AgentTemplate;
import xiaozhi.modules.agent.service.AgentService;
import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.agent.vo.AgentConfigVO;
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 java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Slf4j
@Tag(name = "智能体管理")
@AllArgsConstructor
@RestController
@RequestMapping("/user/agent")
public class UserAgentController extends BaseController {
private final AgentService agentService;
private final AgentTemplateService agentTemplateService;
private final ModelConfigService modelConfigService;
private final TtsVoiceService ttsVoiceService;
private final DeviceService deviceService;
private final RedisUtils redisUtils;
@PostMapping
@Operation(summary = "添加智能体")
public Result<Agent> addAgent(@RequestBody Agent agent) {
UserDetail user = SecurityUser.getUser();
if(StringUtils.isBlank(agent.getAgentName())){
log.error("智能体名称不能为空");
}
Agent oldAgent = agentService.getOne(new QueryWrapper<Agent>().eq("agent_name", agent.getAgentName()));
if(ObjectUtils.isNull(oldAgent)){
AgentTemplate agentTemplate = agentTemplateService.getOne(new QueryWrapper<AgentTemplate>().eq("is_default", 1));
if(ObjectUtils.isNull(agentTemplate)){
}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);
}
@GetMapping
@Operation(summary = "智能体列表")
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.convertAgetVOList(list, agents);
return new Result<List<AgentVO>>().ok(list);
}
@GetMapping("/loadAgentConfig/{deviceId}")
@Operation(summary = "下载智能体配置")
// public Result<AgentConfigVO> loadAgentConfig(@PathVariable String deviceId){
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("智能体不存在");
}
AgentConfigVO agentConfigVO = new AgentConfigVO();
// return new Result<AgentConfigVO>().ok(agentConfigVO);
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 convertAgetVOList(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);
}
}
}
@@ -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 memModelId;
/**
* 意图模型标识
*/
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 memModelId;
/**
* 意图模型标识
*/
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;
}
@@ -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> {
}
@@ -0,0 +1,13 @@
package xiaozhi.modules.agent.service;
import xiaozhi.modules.agent.domain.Agent;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author chenerlei
* @description 针对表【ai_agent(智能体配置表)】的数据库操作Service
* @createDate 2025-03-22 11:48:18
*/
public interface AgentService extends IService<Agent> {
}
@@ -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> {
}
@@ -0,0 +1,22 @@
package xiaozhi.modules.agent.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import xiaozhi.modules.agent.domain.Agent;
import xiaozhi.modules.agent.service.AgentService;
import xiaozhi.modules.agent.mapper.AgentMapper;
import org.springframework.stereotype.Service;
/**
* @author chenerlei
* @description 针对表【ai_agent(智能体配置表)】的数据库操作Service实现
* @createDate 2025-03-22 11:48:18
*/
@Service
public class AgentServiceImpl extends ServiceImpl<AgentMapper, Agent>
implements AgentService{
}
@@ -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,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";
}
@@ -1,82 +0,0 @@
package xiaozhi.modules.device.controller;
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 lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;
import xiaozhi.common.constant.Constant;
import xiaozhi.common.exception.ErrorCode;
import xiaozhi.common.page.PageData;
import xiaozhi.common.redis.RedisKeys;
import xiaozhi.common.redis.RedisUtils;
import xiaozhi.common.user.UserDetail;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.common.utils.Result;
import xiaozhi.modules.device.dto.DeviceHeaderDTO;
import xiaozhi.modules.device.dto.DeviceUnBindDTO;
import xiaozhi.modules.device.entity.DeviceEntity;
import xiaozhi.modules.device.service.DeviceService;
import xiaozhi.modules.security.user.SecurityUser;
import java.util.List;
import java.util.Map;
@Tag(name = "设备管理")
@AllArgsConstructor
@RestController
@RequestMapping("/device")
public class DeviceController {
private final DeviceService deviceService;
private final RedisUtils redisUtils;
@PostMapping("/bind/{deviceCode}")
@Operation(summary = "绑定设备")
@RequiresPermissions("sys:role:normal")
public Result<DeviceEntity> bindDevice(@PathVariable String deviceCode) {
UserDetail user = SecurityUser.getUser();
String deviceHeaders = (String) redisUtils.get(RedisKeys.getDeviceCaptchaKey(deviceCode));
if (StringUtils.isBlank(deviceHeaders)) {
return new Result<DeviceEntity>().error(ErrorCode.DEVICE_CAPTCHA_ERROR);
}
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();
List<DeviceEntity> devices = deviceService.getUserDevices(user.getId());
return new Result<List<DeviceEntity>>().ok(devices);
}
@PostMapping("/unbind")
@Operation(summary = "解绑设备")
@RequiresPermissions("sys:role:normal")
public Result unbindDevice(@RequestBody DeviceUnBindDTO unDeviveBind) {
UserDetail user = SecurityUser.getUser();
deviceService.unbindDevice(user.getId(), unDeviveBind.getDeviceId());
return new Result();
}
@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<DeviceEntity>> adminDeviceList(
@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
PageData<DeviceEntity> page = deviceService.adminDeviceList(params);
return new Result<PageData<DeviceEntity>>().ok(page);
}
}
@@ -0,0 +1,81 @@
package xiaozhi.modules.device.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 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.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import xiaozhi.common.controller.BaseController;
import xiaozhi.common.redis.RedisUtils;
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.sys.service.SysParamsService;
import java.util.Date;
@Slf4j
@Tag(name = "设备OTA管理")
@AllArgsConstructor
@RestController
@RequestMapping("/ota")
public class OtaController extends BaseController {
private final DeviceService deviceService;
private final SysParamsService sysParamsService;
@Resource
private RedisUtils redisUtils;
private final String OTA_PARAM_CODE = "OTA";
@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("-", "")));
if (ObjectUtils.isNull(device)) {
// 从 Redis 中获取设备信息
Object redisValue = redisUtils.hGet(DeviceConstant.REDIS_KEY_PREFIX_DEVICE_ACTIVATION_MAC, headers.getFirst("device-id").toUpperCase());
if (ObjectUtils.isNull(redisValue)) {
String code = CodeGeneratorUtil.generateCode(6);
log.info("[gen]授权码已广播:{}", code);
otaVO.setActivation(new DeviceOtaVO.Activation(code, "youxlife.com\n" + code));
redisUtils.hSet(DeviceConstant.REDIS_KEY_PREFIX_DEVICE_ACTIVATION_MAC, headers.getFirst("device-id").toUpperCase(), code, RedisUtils.HOUR_ONE_EXPIRE);
redisUtils.hSet(DeviceConstant.REDIS_KEY_PREFIX_DEVICE_ACTIVATION_CODE, code, jsonObject, RedisUtils.HOUR_ONE_EXPIRE);
} else {
log.warn("[get]授权码已广播:{}", redisValue);
otaVO.setActivation(new DeviceOtaVO.Activation(redisValue.toString(), "youxlife.com\n" + redisValue));
}
}
if (ObjectUtils.isNull(device) || device.getAutoUpdate() == 1) {
//{"version":"1.0.0","url":"http://https://youxlife.oss-cn-zhangjiakou.aliyuncs.com/v1.4.5.bin"}
String value = sysParamsService.getValue(OTA_PARAM_CODE);
if (StringUtils.isBlank(value)) {
log.warn("OTA升级信息未配置");
} else {
JSONObject object = new JSONObject(value);
otaVO.setFirmware(new DeviceOtaVO.Firmware(object.getStr("version"), object.getStr("url")));
}
}
otaVO.setServer_time(new DeviceOtaVO.ServerTime(new Date().getTime(), 8 * 60));
return JSONUtil.toJsonStr(otaVO);
}
}
@@ -0,0 +1,127 @@
package xiaozhi.modules.device.controller;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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 lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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.agent.domain.Agent;
import xiaozhi.modules.agent.service.AgentService;
import xiaozhi.modules.device.constant.DeviceConstant;
import xiaozhi.modules.device.domain.Device;
import xiaozhi.modules.device.service.DeviceService;
import xiaozhi.modules.device.vo.DeviceCodeVO;
import xiaozhi.modules.security.user.SecurityUser;
import java.util.Date;
@Slf4j
@Tag(name = "设备管理")
@AllArgsConstructor
@RestController
@RequestMapping("/user/agent/device")
public class UserDeviceController extends BaseController {
private final AgentService agentService;
private final DeviceService deviceService;
private final RedisUtils redisUtils;
@GetMapping("/bind/{agentId}")
@Operation(summary = "已绑定设备列表")
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不能为空");
}
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 = "绑定设备")
public Result<Device> bindDevice(@PathVariable String agentId, @RequestBody DeviceCodeVO deviceCodeVO) {
if (ObjectUtils.isNull(deviceCodeVO) || StringUtils.isBlank(deviceCodeVO.getDeviceCode())) {
log.error("授权码不能为空");
return new Result().error("授权码不能为空");
}
if (StringUtils.isBlank(agentId)) {
log.error("智能体ID不能为空");
return new Result().error("智能体ID不能为空");
}
UserDetail user = SecurityUser.getUser();
// 从 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);
}
@PutMapping("/unbind/{deviceId}")
@Operation(summary = "解绑设备")
public Result<Device> bindDevice(@PathVariable String deviceId) {
if (StringUtils.isBlank(deviceId)) {
log.error("设备ID不能为空");
return new Result().error("设备ID不能为空");
}
UserDetail user = SecurityUser.getUser();
boolean bool = deviceService.removeById(deviceId);
if (!bool) {
return new Result().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;
import xiaozhi.common.page.PageData;
import xiaozhi.modules.device.dto.DeviceHeaderDTO;
import xiaozhi.modules.device.entity.DeviceEntity;
import java.util.List;
import java.util.Map;
public interface DeviceService {
DeviceEntity bindDevice(Long userId, DeviceHeaderDTO deviceHeader);
List<DeviceEntity> getUserDevices(Long userId);
void unbindDevice(Long userId, Long deviceId);
PageData<DeviceEntity> adminDeviceList(Map<String, Object> params);
}
package xiaozhi.modules.device.service;
import xiaozhi.modules.device.domain.Device;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author chenerlei
* @description 针对表【ai_device(设备信息表)】的数据库操作Service
* @createDate 2025-03-22 13:26:35
*/
public interface DeviceService extends IService<Device> {
}
@@ -1,57 +1,22 @@
package xiaozhi.modules.device.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.stereotype.Service;
import xiaozhi.common.page.PageData;
import xiaozhi.common.service.impl.BaseServiceImpl;
import xiaozhi.modules.device.dao.DeviceDao;
import xiaozhi.modules.device.dto.DeviceHeaderDTO;
import xiaozhi.modules.device.entity.DeviceEntity;
import xiaozhi.modules.device.service.DeviceService;
import java.util.Date;
import java.util.List;
import java.util.Map;
@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());
}
}
package xiaozhi.modules.device.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import xiaozhi.modules.device.domain.Device;
import xiaozhi.modules.device.service.DeviceService;
import xiaozhi.modules.device.mapper.DeviceMapper;
import org.springframework.stereotype.Service;
/**
* @author chenerlei
* @description 针对表【ai_device(设备信息表)】的数据库操作Service实现
* @createDate 2025-03-22 13:26:35
*/
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device>
implements DeviceService{
}
@@ -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,10 @@
package xiaozhi.modules.device.vo;
import lombok.Data;
@Data
public class DeviceCodeVO {
private String deviceId;
private String deviceCode;
private String token;
}
@@ -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;
}
}
}
@@ -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 Object 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,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.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> {
}
@@ -0,0 +1,13 @@
package xiaozhi.modules.model.service;
import xiaozhi.modules.model.domain.ModelConfig;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author chenerlei
* @description 针对表【ai_model_config(模型配置表)】的数据库操作Service
* @createDate 2025-03-22 15:31:57
*/
public interface ModelConfigService extends IService<ModelConfig> {
}
@@ -0,0 +1,13 @@
package xiaozhi.modules.model.service;
import xiaozhi.modules.model.domain.TtsVoice;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author chenerlei
* @description 针对表【ai_tts_voice(TTS 音色表)】的数据库操作Service
* @createDate 2025-03-22 15:31:57
*/
public interface TtsVoiceService extends IService<TtsVoice> {
}
@@ -0,0 +1,22 @@
package xiaozhi.modules.model.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import xiaozhi.modules.model.domain.ModelConfig;
import xiaozhi.modules.model.service.ModelConfigService;
import xiaozhi.modules.model.mapper.ModelConfigMapper;
import org.springframework.stereotype.Service;
/**
* @author chenerlei
* @description 针对表【ai_model_config(模型配置表)】的数据库操作Service实现
* @createDate 2025-03-22 15:31:57
*/
@Service
public class ModelConfigServiceImpl extends ServiceImpl<ModelConfigMapper, ModelConfig>
implements ModelConfigService{
}
@@ -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{
}
@@ -73,6 +73,8 @@ public class ShiroConfig {
filterMap.put("/user/captcha", "anon");
filterMap.put("/user/login", "anon");
filterMap.put("/user/register", "anon");
filterMap.put("/ota/**", "anon");
filterMap.put("/user/agent/loadAgentConfig/**", "anon");
filterMap.put("/**", "oauth2");
shiroFilter.setFilterChainDefinitionMap(filterMap);
@@ -41,4 +41,22 @@ spring:
merge-sql: false
wall:
config:
multi-statement-allow: true
multi-statement-allow: true
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,19 +24,6 @@ spring:
max-file-size: 100MB
max-request-size: 100MB
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:
allow-bean-definition-overriding: true
@@ -51,7 +38,7 @@ knife4j:
renren:
redis:
open: false
open: true
xss:
enabled: true
exclude-urls:
@@ -60,7 +47,7 @@ renren:
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: xiaozhi.modules.*.entity
typeAliasesPackage: xiaozhi.modules.*.entity;xiaozhi.modules.*.domain
global-config:
#数据库相关配置
db-config:
@@ -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="memModelId" column="mem_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,mem_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="memModelId" column="mem_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,mem_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,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>