diff --git a/docs/images/demo9.png b/docs/images/demo9.png index 74b6e463..ce74a02d 100644 Binary files a/docs/images/demo9.png and b/docs/images/demo9.png differ diff --git a/main/manager-api/src/main/java/xiaozhi/modules/admin/contrloler/AdminController.java b/main/manager-api/src/main/java/xiaozhi/modules/admin/contrloler/AdminController.java new file mode 100644 index 00000000..ee46f2eb --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/admin/contrloler/AdminController.java @@ -0,0 +1,84 @@ +package xiaozhi.modules.admin.contrloler; + + +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.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; +import xiaozhi.common.constant.Constant; +import xiaozhi.common.page.PageData; +import xiaozhi.common.utils.Result; +import xiaozhi.common.validator.ValidatorUtils; +import xiaozhi.modules.sys.dto.AdminPageUserDTO; +import xiaozhi.modules.sys.service.SysUserService; +import xiaozhi.modules.sys.vo.AdminPageUserVO; + +import java.util.Map; + +/** + * 管理员控制层 + * + * @author zjy + * @since 2025-3-25 + */ +@AllArgsConstructor +@RestController +@RequestMapping("/admin") +@Tag(name = "管理员管理") +public class AdminController { + private final SysUserService sysUserService; + + @GetMapping("/users") + @Operation(summary = "分页查找用户") + @RequiresPermissions("sys:role:superAdmin") + @Parameters({ + @Parameter(name = "mobile", description = "用户手机号码", required = true), + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true), + }) + public Result> pageUser( + @Parameter(hidden = true) @RequestParam Map params) { + AdminPageUserDTO dto = new AdminPageUserDTO(); + dto.setMobile((String) params.get("mobile")); + dto.setLimit((String) params.get(Constant.LIMIT)); + dto.setPage((String) params.get("pages")); + + ValidatorUtils.validateEntity(dto); + PageData page = sysUserService.page(dto); + return new Result>().ok(page); + } + + @PutMapping("/users/{id}") + @Operation(summary = "重置密码") + @RequiresPermissions("sys:role:superAdmin") + public Result update( + @PathVariable Long id) { + String password = sysUserService.resetPassword(id); + return new Result().ok(password); + } + + @DeleteMapping("/users/{id}") + @Operation(summary = "用户删除") + @RequiresPermissions("sys:role:superAdmin") + public Result delete(@PathVariable Long id) { + sysUserService.delete(new Long[]{id}); + return new Result<>(); + } + + @GetMapping("/device/all") + @Operation(summary = "分页查找设备") + @RequiresPermissions("sys:role:superAdmin") + @Parameters({ + @Parameter(name = "keywords", description = "用户手机号码", required = true), + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true), + }) + public Result pageDevice( + @Parameter(hidden = true) @RequestParam Map params) { + //TODO 等设备功能模块写好 + return new Result().error(600,"等设备功能模块写好"); + } +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/UserAgentController.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/UserAgentController.java index 20246e62..bde48b97 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/UserAgentController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/UserAgentController.java @@ -2,6 +2,7 @@ package xiaozhi.modules.agent.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 io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -80,6 +81,40 @@ public class UserAgentController extends BaseController { return new Result().ok(agent); } + @PutMapping("{agentId}") + @Operation(summary = "更新智能体") + @RequiresPermissions("sys:role:normal") + public Result updateAgent(@PathVariable String agentId, @RequestBody Agent agent) { + UserDetail user = SecurityUser.getUser(); + if (StringUtils.isBlank(agentId)) { + log.error("智能体ID不能为空"); + return new Result().error("更新失败,智能体ID不能为空"); + } + Agent oldAgent = agentService.getById(agentId); + if (ObjectUtils.isNull(oldAgent)) { + log.error("智能体不存在"); + return new Result().error("更新失败,智能体不存在"); + } else { + UpdateWrapper 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().error("更新失败"); + } + return new Result().ok(agent); + } + @DeleteMapping("/{agentId}") @Operation(summary = "删除智能体") @RequiresPermissions("sys:role:normal") @@ -113,7 +148,7 @@ public class UserAgentController extends BaseController { UserDetail user = SecurityUser.getUser(); List agents = agentService.list(new QueryWrapper().eq("user_id", user.getId())); List list = new ArrayList<>(); - this.convertAgetVOList(list, agents); + this.convertAgentVOList(list, agents); return new Result>().ok(list); } @@ -129,6 +164,7 @@ public class UserAgentController extends BaseController { return new Result().error("智能体不存在"); } AgentConfigVO agentConfigVO = new AgentConfigVO(); + agentConfigVO.setPrompt(agent.getSystemPrompt()); // return new Result().ok(agentConfigVO); String json = "{\n" + " \"ASR\": {\n" + @@ -188,7 +224,7 @@ public class UserAgentController extends BaseController { * @param agentVOList 转换后的AgentVO对象列表 * @param agentList 原始的Agent对象列表 */ - private void convertAgetVOList(List agentVOList, List agentList) { + private void convertAgentVOList(List agentVOList, List agentList) { // 遍历Agent对象列表 for (Agent agent : agentList) { // 创建一个新的AgentVO对象 diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/domain/Agent.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/domain/Agent.java index 74f75491..8ae48dec 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/domain/Agent.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/domain/Agent.java @@ -64,7 +64,7 @@ public class Agent implements Serializable { /** * 记忆模型标识 */ - private String memModelId; + private String memoryModelId; /** * 意图模型标识 diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/domain/AgentTemplate.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/domain/AgentTemplate.java index b25dc407..594fc219 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/domain/AgentTemplate.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/domain/AgentTemplate.java @@ -59,7 +59,7 @@ public class AgentTemplate implements Serializable { /** * 记忆模型标识 */ - private String memModelId; + private String memoryModelId; /** * 意图模型标识 diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelConfigController.java~ b/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelConfigController.java~ new file mode 100644 index 00000000..d1693839 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelConfigController.java~ @@ -0,0 +1,2 @@ +package xiaozhi.modules.model.controller;public class ModelConfigController { +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java b/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java index 79e84074..f38586dc 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java @@ -1,33 +1,24 @@ package xiaozhi.modules.model.controller; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; -import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 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 org.springframework.web.bind.annotation.GetMapping; +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.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 xiaozhi.modules.model.domain.ModelConfig; +import xiaozhi.modules.model.domain.TtsVoice; +import xiaozhi.modules.model.service.ModelConfigService; +import xiaozhi.modules.model.service.TtsVoiceService; -import java.util.Date; +import java.util.List; @Slf4j @Tag(name = "模型数据管理") @@ -35,112 +26,99 @@ import java.util.Date; @RestController @RequestMapping("/model/") public class ModelController extends BaseController { - private final DeviceService deviceService; - private final OtaService otaService; - @Resource - private RedisUtils redisUtils; - - @CrossOrigin(originPatterns = "*", methods = {RequestMethod.GET, RequestMethod.POST}) - @RequestMapping(path = "/", method = {RequestMethod.POST, RequestMethod.GET}, produces = "application/json") - @Operation(summary = "设备OTA升级") - public String ota(@RequestHeader(required = false) HttpHeaders headers, @RequestBody(required = false) JSONObject jsonObject) { - log.info("OTA升级请求:header:{},body:{}", headers, jsonObject); - if (ObjectUtils.isNull(headers) || StringUtils.isBlank(headers.getFirst("device-id"))) { - log.error("设备ID不能为空"); - return "{\"error\": \"Device ID is required\"}"; - } - - DeviceOtaVO otaVO = new DeviceOtaVO(); - Device device = deviceService.getOne(new UpdateWrapper().eq("mac_address", headers.getFirst("device-id").toUpperCase()).eq("id", headers.getFirst("client-Id").replace("-", ""))); - if (ObjectUtils.isNull(device)) { - // 从 Redis 中获取设备信息 - Object redisValue = redisUtils.hGet(DeviceConstant.REDIS_KEY_PREFIX_DEVICE_ACTIVATION_MAC, headers.getFirst("device-id").toUpperCase()); - if (ObjectUtils.isNull(redisValue)) { - String code = CodeGeneratorUtil.generateCode(6); - log.info("[gen]授权码已广播:{}", code); - otaVO.setActivation(new DeviceOtaVO.Activation(code, "youxlife.com\n" + code)); - redisUtils.hSet(DeviceConstant.REDIS_KEY_PREFIX_DEVICE_ACTIVATION_MAC, headers.getFirst("device-id").toUpperCase(), code, RedisUtils.HOUR_ONE_EXPIRE); - redisUtils.hSet(DeviceConstant.REDIS_KEY_PREFIX_DEVICE_ACTIVATION_CODE, code, jsonObject, RedisUtils.HOUR_ONE_EXPIRE); - } else { - log.warn("[get]授权码已广播:{}", redisValue); - otaVO.setActivation(new DeviceOtaVO.Activation(redisValue.toString(), "youxlife.com\n" + redisValue)); - } - } + private final TtsVoiceService ttsVoiceService; + private final ModelConfigService modelConfigService; - if (ObjectUtils.isNull(device) || device.getAutoUpdate() == 1) { - //{"version":"1.0.0","url":"http://https://youxlife.oss-cn-zhangjiakou.aliyuncs.com/v1.4.5.bin"} - String board = ObjectUtils.isNull(device)?headers.getFirst("user-agent").split("/")[0]:device.getBoard(); - Ota ota = otaService.getOne(new UpdateWrapper().eq("board", board).eq("is_enabled", 1)); - if (ObjectUtils.isNull(ota)) { - log.warn("OTA升级信息未配置"); - } else { - otaVO.setFirmware(new DeviceOtaVO.Firmware(ota.getAppVersion(), ota.getUrl())); - } - } - - otaVO.setServer_time(new DeviceOtaVO.ServerTime(new Date().getTime(), 8 * 60)); - return JSONUtil.toJsonStr(otaVO); + @GetMapping("/list") + @Operation(summary = "模型配置列表") + @RequiresPermissions("sys:role:normal") + public Result> getModelList() { + List list = modelConfigService.list(new QueryWrapper().eq("is_enabled", 1)); + return new Result>().ok(list); } - @GetMapping("/sys/getOtalist") - @Operation(summary = "设备OTA列表") - @RequiresPermissions("sys:role:superAdmin") - public Result> getOtalist(@RequestParam Integer pageNo, @RequestParam Integer pageSize) { - UserDetail user = SecurityUser.getUser(); - Page page = new Page(pageNo, pageSize); - page = otaService.page(page); - return new Result>().ok(page); - } - @PostMapping("/sys/save") - @Operation(summary = "添加设备OTA") - @RequiresPermissions("sys:role:superAdmin") - public Result save(@RequestBody Ota ota) { - UserDetail user = SecurityUser.getUser(); - ota.setCreator(user.getId()); - ota.setCreateDate(new Date()); - boolean bool = otaService.save(ota); - if (!bool) { - return new Result().error("设备OTA添加失败"); - } - return new Result().ok(null); - } - - @PostMapping("/sys/update") - @Operation(summary = "更新设备OTA") - @RequiresPermissions("sys:role:superAdmin") - public Result update(@RequestBody Ota ota) { - UserDetail user = SecurityUser.getUser(); - boolean bool = otaService.update( - new UpdateWrapper().eq("id", ota.getId()) - .set("board", ota.getBoard().trim().toLowerCase()) - .set("app_version", ota.getAppVersion()) - .set("url", ota.getUrl()) - .set("is_enabled", ota.getIsEnabled()) - .set("updater", user.getId()) - .set("update_date", new Date()) - ); - if (!bool) { - return new Result().error("设备OTA更新失败"); - } - return new Result().ok(null); - } - - @PostMapping("/sys/toggleEnabled") - @Operation(summary = "切换设备OTA可用状态") - @RequiresPermissions("sys:role:superAdmin") - public Result toggleEnabled(@RequestBody Ota ota) { - UserDetail user = SecurityUser.getUser(); - boolean bool = otaService.update( - new UpdateWrapper().eq("id", ota.getId()) - .set("is_enabled", ota.getIsEnabled()) - .set("updater", user.getId()) - .set("update_date", new Date()) - ); - if (!bool) { - return new Result().error("切换设备OTA可用状态失败"); - } - return new Result().ok(null); - } +// private final ModelProviderService modelProviderService; +// +// private final ModelConfigService modelConfigService; +// +// @GetMapping("/models/names") +// @Operation(summary = "获取所有模型名称") +// @RequiresPermissions("sys:role:superAdmin") +// public Result> getModelNames(@RequestParam String modelType, +// @RequestParam(required = false) String modelName) { +// List modelNameList = modelConfigService.getModelCodeList(modelType, modelName); +// return new Result>().ok(modelNameList); +// } +// +// @GetMapping("/{modelType}/provideTypes") +// @Operation(summary = "获取模型供应器列表") +// @RequiresPermissions("sys:role:superAdmin") +// public Result> getModelProviderList(@PathVariable String modelType) { +// List modelProviderDTOS = modelProviderService.getListByModelType(modelType); +// return new Result>().ok(modelProviderDTOS); +// } +// +// @GetMapping("/{modelType}/{provideCode}/fields") +// @Operation(summary = "获取模型供应器字段") +// @RequiresPermissions("sys:role:superAdmin") +// public Result> getModelProviderFields(@PathVariable String modelType, @PathVariable String provideCode) { +// List fieldList = modelProviderService.getFieldList(modelType, provideCode); +// return new Result>().ok(fieldList); +// } +// +// +// @GetMapping("/models/list") +// @Operation(summary = "获取模型配置列表") +// @RequiresPermissions("sys:role:superAdmin") +// public Result> getModelConfigList(@RequestParam String modelType, +// @RequestParam(required = false) String modelName, +// @RequestParam(required = false, defaultValue = "0") Integer page, +// @RequestParam(required = false,defaultValue = "10") Integer limit) { +// PageData pageList = modelConfigService.getPageList(modelType, modelName, page, limit); +// return new Result>().ok(pageList); +// } +// +// +// @PostMapping("/models/{modelType}/{provideCode}") +// @Operation(summary = "新增模型配置") +// @RequiresPermissions("sys:role:superAdmin") +// public Result addModelConfig(@PathVariable String modelType, +// @PathVariable String provideCode, +// @RequestBody ModelConfigBodyDTO modelConfigBodyDTO) { +// ModelConfigDTO modelConfigDTO = modelConfigService.add(modelType, provideCode, modelConfigBodyDTO); +// return new Result().ok(modelConfigDTO); +// } +// +// +// @PutMapping("/models/{modelType}/{provideCode}/{id}") +// @Operation(summary = "编辑模型配置") +// @RequiresPermissions("sys:role:superAdmin") +// public Result editModelConfig(@PathVariable String modelType, +// @PathVariable String provideCode, +// @PathVariable String id, +// @RequestBody ModelConfigBodyDTO modelConfigBodyDTO) { +// ModelConfigDTO modelConfigDTO = modelConfigService.edit(modelType, provideCode, id, modelConfigBodyDTO); +// return new Result().ok(modelConfigDTO); +// } +// +// +// @DeleteMapping("/models/{modelType}/{provideCode}/{id}") +// @Operation(summary = "删除模型配置") +// @RequiresPermissions("sys:role:superAdmin") +// public Result 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> getVoiceList(@PathVariable String modelName, +// @RequestParam(required = false) String voiceName) { +// +// List voiceList = modelConfigService.getVoiceList(modelName, voiceName); +// return new Result>().ok(voiceList); +// } } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/controller/TtsController.java b/main/manager-api/src/main/java/xiaozhi/modules/model/controller/TtsController.java new file mode 100644 index 00000000..9a29ee2b --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/controller/TtsController.java @@ -0,0 +1,123 @@ +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> getTtsList(@RequestParam(required = false) String ttsModelId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StringUtils.isNotBlank(ttsModelId)) { + queryWrapper.eq("ttsModelId", ttsModelId); + } + List list = ttsVoiceService.list(queryWrapper); + return new Result>().ok(list); + } + + +// private final ModelProviderService modelProviderService; +// +// private final ModelConfigService modelConfigService; +// +// @GetMapping("/models/names") +// @Operation(summary = "获取所有模型名称") +// @RequiresPermissions("sys:role:superAdmin") +// public Result> getModelNames(@RequestParam String modelType, +// @RequestParam(required = false) String modelName) { +// List modelNameList = modelConfigService.getModelCodeList(modelType, modelName); +// return new Result>().ok(modelNameList); +// } +// +// @GetMapping("/{modelType}/provideTypes") +// @Operation(summary = "获取模型供应器列表") +// @RequiresPermissions("sys:role:superAdmin") +// public Result> getModelProviderList(@PathVariable String modelType) { +// List modelProviderDTOS = modelProviderService.getListByModelType(modelType); +// return new Result>().ok(modelProviderDTOS); +// } +// +// @GetMapping("/{modelType}/{provideCode}/fields") +// @Operation(summary = "获取模型供应器字段") +// @RequiresPermissions("sys:role:superAdmin") +// public Result> getModelProviderFields(@PathVariable String modelType, @PathVariable String provideCode) { +// List fieldList = modelProviderService.getFieldList(modelType, provideCode); +// return new Result>().ok(fieldList); +// } +// +// +// @GetMapping("/models/list") +// @Operation(summary = "获取模型配置列表") +// @RequiresPermissions("sys:role:superAdmin") +// public Result> getModelConfigList(@RequestParam String modelType, +// @RequestParam(required = false) String modelName, +// @RequestParam(required = false, defaultValue = "0") Integer page, +// @RequestParam(required = false,defaultValue = "10") Integer limit) { +// PageData pageList = modelConfigService.getPageList(modelType, modelName, page, limit); +// return new Result>().ok(pageList); +// } +// +// +// @PostMapping("/models/{modelType}/{provideCode}") +// @Operation(summary = "新增模型配置") +// @RequiresPermissions("sys:role:superAdmin") +// public Result addModelConfig(@PathVariable String modelType, +// @PathVariable String provideCode, +// @RequestBody ModelConfigBodyDTO modelConfigBodyDTO) { +// ModelConfigDTO modelConfigDTO = modelConfigService.add(modelType, provideCode, modelConfigBodyDTO); +// return new Result().ok(modelConfigDTO); +// } +// +// +// @PutMapping("/models/{modelType}/{provideCode}/{id}") +// @Operation(summary = "编辑模型配置") +// @RequiresPermissions("sys:role:superAdmin") +// public Result editModelConfig(@PathVariable String modelType, +// @PathVariable String provideCode, +// @PathVariable String id, +// @RequestBody ModelConfigBodyDTO modelConfigBodyDTO) { +// ModelConfigDTO modelConfigDTO = modelConfigService.edit(modelType, provideCode, id, modelConfigBodyDTO); +// return new Result().ok(modelConfigDTO); +// } +// +// +// @DeleteMapping("/models/{modelType}/{provideCode}/{id}") +// @Operation(summary = "删除模型配置") +// @RequiresPermissions("sys:role:superAdmin") +// public Result 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> getVoiceList(@PathVariable String modelName, +// @RequestParam(required = false) String voiceName) { +// +// List voiceList = modelConfigService.getVoiceList(modelName, voiceName); +// return new Result>().ok(voiceList); +// } +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelConfigDao.java b/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelConfigDao.java new file mode 100644 index 00000000..a35967e4 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelConfigDao.java @@ -0,0 +1,17 @@ +package xiaozhi.modules.model.dao; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import xiaozhi.common.dao.BaseDao; +import xiaozhi.modules.model.entity.ModelConfigEntity; + +import java.util.List; + +@Mapper +public interface ModelConfigDao extends BaseDao { + + /** + * get model_code list + */ + List getModelCodeList(@Param("modelType") String modelType, @Param("modelName") String modelName); +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelProviderDao.java b/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelProviderDao.java new file mode 100644 index 00000000..51042e8d --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelProviderDao.java @@ -0,0 +1,14 @@ +package xiaozhi.modules.model.dao; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import xiaozhi.common.dao.BaseDao; +import xiaozhi.modules.model.entity.ModelProviderEntity; + +import java.util.List; + +@Mapper +public interface ModelProviderDao extends BaseDao { + + List getFieldList(@Param("modelType") String modelType, @Param("provideCode") String provideCode); +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigBodyDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigBodyDTO.java new file mode 100644 index 00000000..639e19a2 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigBodyDTO.java @@ -0,0 +1,41 @@ +package xiaozhi.modules.model.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; + +@Data +@Schema(description = "模型供应器/商") +public class ModelConfigBodyDTO { + + @Serial + private static final long serialVersionUID = 1L; + +// @Schema(description = "模型类型(Memory/ASR/VAD/LLM/TTS)") +// private String modelType; +// + @Schema(description = "模型编码(如AliLLM、DoubaoTTS)") + private String modelCode; + + @Schema(description = "模型名称") + private String modelName; + + @Schema(description = "是否默认配置(0否 1是)") + private Integer isDefault; + + @Schema(description = "是否启用") + private Integer isEnabled; + + @Schema(description = "模型配置(JSON格式)") + private String configJson; + + @Schema(description = "官方文档链接") + private String docLink; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "排序") + private Integer sort; +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigDTO.java new file mode 100644 index 00000000..95a44921 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigDTO.java @@ -0,0 +1,45 @@ +package xiaozhi.modules.model.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +@Data +@Schema(description = "模型供应器/商") +public class ModelConfigDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + + @Schema(description = "模型类型(Memory/ASR/VAD/LLM/TTS)") + private String modelType; + + @Schema(description = "模型编码(如AliLLM、DoubaoTTS)") + private String modelCode; + + @Schema(description = "模型名称") + private String modelName; + + @Schema(description = "是否默认配置(0否 1是)") + private Integer isDefault; + + @Schema(description = "是否启用") + private Integer isEnabled; + + @Schema(description = "模型配置(JSON格式)") + private String configJson; + + @Schema(description = "官方文档链接") + private String docLink; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "排序") + private Integer sort; +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelProviderDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelProviderDTO.java new file mode 100644 index 00000000..acce3036 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelProviderDTO.java @@ -0,0 +1,45 @@ +package xiaozhi.modules.model.dto; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@Schema(description = "模型供应器/商") +public class ModelProviderDTO implements Serializable { +// +// @Schema(description = "主键") +// private Long id; + + @Schema(description = "模型类型(Memory/ASR/VAD/LLM/TTS)") + private String modelType; + + @Schema(description = "供应器类型") + private String providerCode; + + @Schema(description = "供应器名称") + private String name; + + @Schema(description = "供应器字段列表(JSON格式)") + @TableField(typeHandler = JacksonTypeHandler.class) + private String fields; + + @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; +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelConfigEntity.java b/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelConfigEntity.java new file mode 100644 index 00000000..c98705f3 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelConfigEntity.java @@ -0,0 +1,62 @@ +package xiaozhi.modules.model.entity; + +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 com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; + + +@Data +@TableName("ai_model_config") +@Schema(description = "模型配置表") +public class ModelConfigEntity { + + @TableId(type = IdType.ASSIGN_UUID) + @Schema(description = "主键") + private String id; + + @Schema(description = "模型类型(Memory/ASR/VAD/LLM/TTS)") + private String modelType; + + @Schema(description = "模型编码(如AliLLM、DoubaoTTS)") + private String modelCode; + + @Schema(description = "模型名称") + private String modelName; + + @Schema(description = "是否默认配置(0否 1是)") + private Integer isDefault; + + @Schema(description = "是否启用") + private Integer isEnabled; + + @TableField(typeHandler = JacksonTypeHandler.class) + @Schema(description = "模型配置(JSON格式)") + private String configJson; + + @Schema(description = "官方文档链接") + private String docLink; + + @Schema(description = "备注") + private String remark; + + @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; +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelProviderEntity.java b/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelProviderEntity.java new file mode 100644 index 00000000..0c526b88 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelProviderEntity.java @@ -0,0 +1,49 @@ +package xiaozhi.modules.model.entity; + +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 com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; + +@Data +@TableName("ai_model_provider") +@Schema(description = "模型供应器表") +public class ModelProviderEntity { + + @TableId(type = IdType.ASSIGN_UUID) + @Schema(description = "主键") + private String id; + + @Schema(description = "模型类型(Memory/ASR/VAD/LLM/TTS)") + private String modelType; + + @Schema(description = "供应器类型,如 openai、") + private String providerCode; + + @Schema(description = "供应器名称") + private String name; + + @Schema(description = "供应器字段列表(JSON格式)") + @TableField(typeHandler = JacksonTypeHandler.class) + private String fields; + + @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; +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java b/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java index a82905eb..21e97c11 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java @@ -1,13 +1,24 @@ -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 { - -} +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 { + +// List getModelCodeList(String modelType, String modelName); +// +// PageData 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 getVoiceList(String modelName, String voiceName); +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelProviderService.java b/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelProviderService.java index f5bf272f..182bf917 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelProviderService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelProviderService.java @@ -1,13 +1,26 @@ -package xiaozhi.modules.model.service; - -import xiaozhi.modules.model.domain.ModelProvider; -import com.baomidou.mybatisplus.extension.service.IService; - -/** -* @author chenerlei -* @description 针对表【ai_model_provider(模型配置表)】的数据库操作Service -* @createDate 2025-03-24 18:24:13 -*/ -public interface ModelProviderService extends IService { - -} +package xiaozhi.modules.model.service; + +import xiaozhi.modules.model.domain.ModelProvider; +import com.baomidou.mybatisplus.extension.service.IService; + +/** +* @author chenerlei +* @description 针对表【ai_model_provider(模型配置表)】的数据库操作Service +* @createDate 2025-03-24 18:24:13 +*/ +public interface ModelProviderService extends IService { + +//// List getModelNames(String modelType, String modelName); +// +// List getListByModelType(String modelType); +// +// ModelProviderDTO add(ModelProviderEntity modelProviderEntity); +// +// ModelProviderDTO edit(ModelProviderEntity modelProviderEntity); +// +// void delete(); +// +// List getList(String modelType, String provideCode); +// +// List getFieldList(String modelType, String provideCode); +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java index f057e270..8418e8de 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java @@ -1,22 +1,113 @@ -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 - implements ModelConfigService{ - -} - - - - +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 + 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 getModelCodeList(String modelType, String modelName) { +// return modelConfigDao.getModelCodeList(modelType, modelName); +// } +// +// @Override +// public PageData getPageList(String modelType, String modelName, Integer page, Integer limit) { +// Map params = new HashMap(); +// params.put(Constant.PAGE, page); +// params.put(Constant.LIMIT, limit); +// IPage modelConfigEntityIPage = modelConfigDao.selectPage( +// getPage(params, "sort", true), +// new QueryWrapper() +// .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 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 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 providerList = modelProviderService.getList(modelType, provideCode); +// if (CollectionUtil.isEmpty(providerList)) { +// throw new RenException("供应器不存在"); +// } +// +// modelConfigDao.deleteById(Long.getLong(id)); +// } +// +// @Override +// public List getVoiceList(String modelName, String voiceName) { +// QueryWrapper queryWrapper = new QueryWrapper<>(); +// queryWrapper.eq("model_name", StringUtils.isBlank(modelName) ? "" : modelName); +// queryWrapper.eq("model_type", "TTS"); +// List 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); +// } +} + + + + diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelProviderServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelProviderServiceImpl.java index d327e7b4..ed124560 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelProviderServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelProviderServiceImpl.java @@ -1,22 +1,61 @@ -package xiaozhi.modules.model.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import xiaozhi.modules.model.domain.ModelProvider; -import xiaozhi.modules.model.service.ModelProviderService; -import xiaozhi.modules.model.mapper.ModelProviderMapper; -import org.springframework.stereotype.Service; - -/** -* @author chenerlei -* @description 针对表【ai_model_provider(模型配置表)】的数据库操作Service实现 -* @createDate 2025-03-24 18:24:13 -*/ -@Service -public class ModelProviderServiceImpl extends ServiceImpl - implements ModelProviderService{ - -} - - - - +package xiaozhi.modules.model.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import xiaozhi.modules.model.domain.ModelProvider; +import xiaozhi.modules.model.service.ModelProviderService; +import xiaozhi.modules.model.mapper.ModelProviderMapper; +import org.springframework.stereotype.Service; + +/** +* @author chenerlei +* @description 针对表【ai_model_provider(模型配置表)】的数据库操作Service实现 +* @createDate 2025-03-24 18:24:13 +*/ +@Service +public class ModelProviderServiceImpl extends ServiceImpl + implements ModelProviderService{ + +// private final ModelProviderDao modelProviderDao; +// +// @Override +// public List getListByModelType(String modelType) { +// +// QueryWrapper queryWrapper = new QueryWrapper<>(); +// queryWrapper.eq("model_type", StringUtils.isBlank(modelType) ? "" : modelType); +// List 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 getList(String modelType, String provideCode) { +// QueryWrapper queryWrapper = new QueryWrapper<>(); +// queryWrapper.eq("model_type", StringUtils.isBlank(modelType) ? "" : modelType); +// queryWrapper.eq("provide_code", StringUtils.isBlank(provideCode) ? "" : provideCode); +// List providerEntities = modelProviderDao.selectList(queryWrapper); +// return ConvertUtils.sourceToTarget(providerEntities, ModelProviderDTO.class); +// } +// +// @Override +// public List getFieldList(String modelType, String provideCode) { +// return modelProviderDao.getFieldList(modelType, provideCode); +// } +} + + + + diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Filter.java b/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Filter.java index 1520a850..8f110941 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Filter.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Filter.java @@ -8,6 +8,8 @@ import org.apache.commons.lang3.StringUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.web.filter.authc.AuthenticatingFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestMethod; import xiaozhi.common.constant.Constant; import xiaozhi.common.exception.ErrorCode; @@ -24,12 +26,15 @@ import java.io.IOException; */ public class Oauth2Filter extends AuthenticatingFilter { + private static final Logger logger = LoggerFactory.getLogger(Oauth2Filter.class); + @Override protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception { //获取请求token String token = getRequestToken((HttpServletRequest) request); if (StringUtils.isBlank(token)) { + logger.warn("createToken:token is empty"); return null; } @@ -49,7 +54,15 @@ public class Oauth2Filter extends AuthenticatingFilter { protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { //获取请求token,如果token不存在,直接返回401 String token = getRequestToken((HttpServletRequest) request); + + // TODO 调试接口,临时取消登录限制,需要 token 参数的除外 +// if (true) { +// return true; +// } + if (StringUtils.isBlank(token)) { + logger.warn("onAccessDenied:token is empty"); + HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setContentType("application/json;charset=utf-8"); httpResponse.setHeader("Access-Control-Allow-Credentials", "true"); @@ -73,13 +86,14 @@ public class Oauth2Filter extends AuthenticatingFilter { httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin()); try { //处理登录失败的异常 + logger.error("onLoginFailure:登录失败!", e); Throwable throwable = e.getCause() == null ? e : e.getCause(); Result r = new Result().error(ErrorCode.UNAUTHORIZED, throwable.getMessage()); String json = JsonUtils.toJsonString(r); httpResponse.getWriter().print(json); } catch (IOException e1) { - + logger.error("onLoginFailure:登录失败! msg:{}", e1.getMessage(), e1); } return false; diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java index fdc0d5b5..761ad139 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java @@ -4,8 +4,11 @@ import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.wf.captcha.SpecCaptcha; import com.wf.captcha.base.Captcha; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import xiaozhi.common.redis.RedisKeys; import xiaozhi.common.redis.RedisUtils; +import xiaozhi.modules.security.oauth2.Oauth2Realm; import xiaozhi.modules.security.service.CaptchaService; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; @@ -29,6 +32,7 @@ public class CaptchaServiceImpl implements CaptchaService { * Local Cache 5分钟过期 */ Cache localCache = CacheBuilder.newBuilder().maximumSize(1000).expireAfterAccess(5, TimeUnit.MINUTES).build(); + private static final Logger logger = LoggerFactory.getLogger(Oauth2Realm.class); @Override public void create(HttpServletResponse response, String uuid) throws IOException { diff --git a/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/AdminPageUserDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/AdminPageUserDTO.java new file mode 100644 index 00000000..8c0ab77c --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/AdminPageUserDTO.java @@ -0,0 +1,25 @@ +package xiaozhi.modules.sys.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * 管理员分页用户的参数DTO + * @author zjy + * @since 2025-3-21 + */ +@Data +@Schema(description = "音色分页参数") +public class AdminPageUserDTO { + + + @Schema(description = "手机号码") + private String mobile; + + @Schema(description = "页数") + private String page; + + @Schema(description = "显示列数") + private String limit; +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserService.java b/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserService.java index 08179409..f8f609dc 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserService.java @@ -1,9 +1,12 @@ package xiaozhi.modules.sys.service; +import xiaozhi.common.page.PageData; import xiaozhi.common.service.BaseService; +import xiaozhi.modules.sys.dto.AdminPageUserDTO; import xiaozhi.modules.sys.dto.PasswordDTO; import xiaozhi.modules.sys.dto.SysUserDTO; import xiaozhi.modules.sys.entity.SysUserEntity; +import xiaozhi.modules.sys.vo.AdminPageUserVO; /** @@ -19,5 +22,30 @@ public interface SysUserService extends BaseService { void delete(Long[] ids); + /** + * 验证是否允许修改密码更改 + * @param userId 用户id + * @param passwordDTO 验证密码的参数 + */ void changePassword(Long userId, PasswordDTO passwordDTO); + + /** + * 直接修改密码,不需要验证 + * @param userId 用户id + * @param password 密码 + */ + void changePasswordDirectly(Long userId, String password); + + /** + * 重置密码 + * @param userId 用户id + * @return 随机生成符合规范的密码 + */ + String resetPassword(Long userId); + /** + * 管理员分页用户信息 + * @param dto 分页查找参数 + * @return 用户列表分页数据 + */ + PageData page(AdminPageUserDTO dto); } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserServiceImpl.java index 17d1939c..f1a1ad60 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserServiceImpl.java @@ -1,23 +1,28 @@ package xiaozhi.modules.sys.service.impl; 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.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import xiaozhi.common.constant.Constant; import xiaozhi.common.exception.ErrorCode; import xiaozhi.common.exception.RenException; +import xiaozhi.common.page.PageData; import xiaozhi.common.service.impl.BaseServiceImpl; import xiaozhi.common.utils.ConvertUtils; import xiaozhi.modules.security.password.PasswordUtils; import xiaozhi.modules.sys.dao.SysUserDao; +import xiaozhi.modules.sys.dto.AdminPageUserDTO; import xiaozhi.modules.sys.dto.PasswordDTO; import xiaozhi.modules.sys.dto.SysUserDTO; import xiaozhi.modules.sys.entity.SysUserEntity; import xiaozhi.modules.sys.enums.SuperAdminEnum; import xiaozhi.modules.sys.service.SysUserService; +import xiaozhi.modules.sys.vo.AdminPageUserVO; -import java.util.Arrays; -import java.util.List; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -80,9 +85,11 @@ public class SysUserServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); return baseDao.selectCount(queryWrapper); } + @Override + public PageData page(AdminPageUserDTO dto) { + Map params = new HashMap(); + params.put(Constant.PAGE, dto.getPage()); + params.put(Constant.LIMIT,dto.getLimit()); + IPage page = baseDao.selectPage( + getPage(params, "id", true), + //定义查询条件 + new QueryWrapper() + //必须按照手机号码查找 + .eq(StringUtils.isNotBlank(dto.getMobile()),"username",dto.getMobile())); + List list = page.getRecords().stream().map(user -> { + AdminPageUserVO adminPageUserVO = new AdminPageUserVO(); + adminPageUserVO.setUserid(user.getId().toString()); + adminPageUserVO.setMobile(user.getUsername()); + //TODO 2. 等设备功能写好,获取对应数据 + adminPageUserVO.setDeviceCount("0"); + return adminPageUserVO; + }).toList(); + return new PageData<>(list, page.getTotal()); + } private boolean isStrongPassword(String password) { // 弱密码的正则表达式 @@ -120,4 +166,19 @@ public class SysUserServiceImpl extends BaseServiceImpl { * @param ids 需要被删除的音色id列表 */ void delete(Long[] ids); + + List getVoiceNames(String ttsModelId, String voiceName); } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImpl.java index ce3c2b7b..fa2731bd 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImpl.java @@ -1,7 +1,9 @@ package xiaozhi.modules.timbre.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.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -12,12 +14,13 @@ import xiaozhi.common.utils.ConvertUtils; import xiaozhi.modules.timbre.dao.TimbreDao; import xiaozhi.modules.timbre.dto.TimbreDataDTO; import xiaozhi.modules.timbre.dto.TimbrePageDTO; -import xiaozhi.modules.timbre.service.TimbreService; import xiaozhi.modules.timbre.entity.TimbreEntity; +import xiaozhi.modules.timbre.service.TimbreService; import xiaozhi.modules.timbre.vo.TimbreDetailsVO; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -25,9 +28,11 @@ import java.util.Map; * @author zjy * @since 2025-3-21 */ +@AllArgsConstructor @Service public class TimbreServiceImpl extends BaseServiceImpl implements TimbreService { + private final TimbreDao timbreDao; @Override public PageData page(TimbrePageDTO dto) { @@ -76,6 +81,21 @@ public class TimbreServiceImpl extends BaseServiceImpl baseDao.deleteBatchIds(Arrays.asList(ids)); } + @Override + public List getVoiceNames(String ttsModelId, String voiceName) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("ttsModelId", StringUtils.isBlank(ttsModelId) ? "" : ttsModelId); + if (StringUtils.isNotBlank(voiceName)) { + queryWrapper.like("name", voiceName); + } + List timbreEntities = timbreDao.selectList(queryWrapper); + if (CollectionUtil.isEmpty(timbreEntities)) { + return null; + } + + return timbreEntities.stream().map(TimbreEntity::getName).toList(); + } + /** * 处理是不是tts模型的id */ diff --git a/main/manager-api/src/main/resources/mapper/agent/AgentMapper.xml b/main/manager-api/src/main/resources/mapper/agent/AgentMapper.xml index 7d8ddc74..c618401e 100644 --- a/main/manager-api/src/main/resources/mapper/agent/AgentMapper.xml +++ b/main/manager-api/src/main/resources/mapper/agent/AgentMapper.xml @@ -14,7 +14,7 @@ - + @@ -27,9 +27,9 @@ - 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, + 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 diff --git a/main/manager-api/src/main/resources/mapper/agent/AgentTemplateMapper.xml b/main/manager-api/src/main/resources/mapper/agent/AgentTemplateMapper.xml index 833bcd0e..d099171a 100644 --- a/main/manager-api/src/main/resources/mapper/agent/AgentTemplateMapper.xml +++ b/main/manager-api/src/main/resources/mapper/agent/AgentTemplateMapper.xml @@ -13,7 +13,7 @@ - + @@ -27,9 +27,9 @@ - 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, + 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 diff --git a/main/manager-api/src/main/resources/mapper/model/ModelConfigDao.xml b/main/manager-api/src/main/resources/mapper/model/ModelConfigDao.xml new file mode 100644 index 00000000..3f01ea5b --- /dev/null +++ b/main/manager-api/src/main/resources/mapper/model/ModelConfigDao.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/main/manager-api/src/main/resources/mapper/model/ModelProviderDao.xml b/main/manager-api/src/main/resources/mapper/model/ModelProviderDao.xml new file mode 100644 index 00000000..b17e24fc --- /dev/null +++ b/main/manager-api/src/main/resources/mapper/model/ModelProviderDao.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/main/manager-api/src/main/resources/mapper/sys/SysUserTokenDao.xml b/main/manager-api/src/main/resources/mapper/security/SysUserTokenDao.xml similarity index 100% rename from main/manager-api/src/main/resources/mapper/sys/SysUserTokenDao.xml rename to main/manager-api/src/main/resources/mapper/security/SysUserTokenDao.xml diff --git a/main/manager-web/src/apis/api.js b/main/manager-web/src/apis/api.js index e69beda2..178b2bfc 100755 --- a/main/manager-web/src/apis/api.js +++ b/main/manager-web/src/apis/api.js @@ -3,6 +3,7 @@ import agent from './module/agent.js' import device from './module/device.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' /** @@ -31,5 +32,6 @@ export default { agent, device, ota, + model, admin } diff --git a/main/manager-web/src/apis/module/admin.js b/main/manager-web/src/apis/module/admin.js index 52468d6b..231443c3 100644 --- a/main/manager-web/src/apis/module/admin.js +++ b/main/manager-web/src/apis/module/admin.js @@ -5,16 +5,16 @@ import {getServiceUrl} from '../api' export default { // 用户列表 getUserList(callback) { - RequestService.sendRequest().url(`${getServiceUrl()}/api/v1/admin/users`) + RequestService.sendRequest().url(`${getServiceUrl()}/admin/users`) .method('GET') .success((res) => { RequestService.clearRequestTime() callback(res) }) .fail(() => { - RequestService.reAjaxFun(() => { - this.getList() - }) + // RequestService.reAjaxFun(() => { + // this.getUserList() + // }) }).send() }, } diff --git a/main/manager-web/src/apis/module/model.js b/main/manager-web/src/apis/module/model.js new file mode 100755 index 00000000..c5ee9087 --- /dev/null +++ b/main/manager-web/src/apis/module/model.js @@ -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() + }, +} diff --git a/main/manager-web/src/apis/module/user.js b/main/manager-web/src/apis/module/user.js index 8cad2198..6aee61bc 100755 --- a/main/manager-web/src/apis/module/user.js +++ b/main/manager-web/src/apis/module/user.js @@ -18,7 +18,8 @@ export default { // this.login(loginForm, callback) // }) }).send() - }, // 获取验证码 + }, + // 获取验证码 getCaptcha(uuid, callback) { RequestService.sendRequest() @@ -98,4 +99,24 @@ export default { // }) }).send() }, + // 修改用户密码 + changePassword(oldPassword, newPassword, successCallback, errorCallback) { + RequestService.sendRequest() + .url(`${getServiceUrl()}/user/change-password`) // 修改URL + .method('PUT') // 修改方法为PUT + .data({ + old_password: oldPassword, // 修改参数名 + new_password: newPassword // 修改参数名 + }) + .success((res) => { + RequestService.clearRequestTime(); + successCallback(res); + }) + .fail((error) => { + // RequestService.reAjaxFun(() => { + // this.changePassword(oldPassword, newPassword, successCallback, errorCallback); + // }); + }) + .send(); + }, } diff --git a/main/manager-web/src/assets/model/model.png b/main/manager-web/src/assets/model/model.png new file mode 100644 index 00000000..e7b6c257 Binary files /dev/null and b/main/manager-web/src/assets/model/model.png differ diff --git a/main/manager-web/src/components/AddModelDialog.vue b/main/manager-web/src/components/AddModelDialog.vue index f8370a5f..074be175 100644 --- a/main/manager-web/src/components/AddModelDialog.vue +++ b/main/manager-web/src/components/AddModelDialog.vue @@ -1,16 +1,25 @@ @@ -216,6 +219,7 @@ export default { border-radius: 20px; overflow: hidden; background: white; + padding-bottom: 17px; } .custom-dialog .el-dialog__header { @@ -223,6 +227,18 @@ export default { border-bottom: none; } +.center-dialog { + display: flex; + align-items: center; + justify-content: center; +} + +.center-dialog .el-dialog { + margin: 4% 0 auto !important; + display: flex; + flex-direction: column; +} + .custom-close-btn { position: absolute; top: 20px; @@ -249,19 +265,15 @@ export default { border-color: #409EFF; } -.custom-select .el-input__inner { - padding-right: 30px !important; -} - .custom-select .el-input__suffix { background: #e6e8ea; - right: 7px; - width: 24px; - height: 24px; + right: 6px; + width: 20px; + height: 20px; display: flex; justify-content: center; align-items: center; - top: 7px; + top: 9px; } .custom-select .el-input__suffix-inner { @@ -276,42 +288,34 @@ export default { display: inline-block; width: 0; height: 0; - border-left: 6px solid transparent; - border-right: 6px solid transparent; - border-top: 8px solid #c0c4cc; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 7px solid #c0c4cc; position: relative; - top: -1px; + top: -2px; transform: rotate(180deg); } -.custom-select .el-select .el-input .el-select__caret { - color: #c0c4cc; - font-size: 14px; - transition: transform .3s; - transform: rotateZ(0deg); - cursor: pointer; -} - -.custom-select .el-select .el-input.is-focus .el-icon-arrow-up:before { - transform: rotateZ(180deg); -} - -/* 表单样式调整 */ .custom-form .el-form-item { - margin-bottom: 0; + margin-bottom: 20px; /* 统一设置所有表单项的间距 */ } .custom-form .el-form-item__label { color: #3d4566; font-weight: normal; text-align: right; - padding-right: 15px; + padding-right: 20px; + +} + +.custom-form .el-form-item.prop-remark .el-form-item__label { + margin-top: -4px; } /* 修改placeholder颜色 */ .custom-input-bg .el-input__inner::-webkit-input-placeholder, .custom-input-bg .el-textarea__inner::-webkit-input-placeholder { - color: #9c9f9e ; + color: #9c9f9e; } /* 输入框背景色 */ @@ -320,16 +324,6 @@ export default { background-color: #f6f8fc; } -.custom-form .el-input__inner, -.custom-form .el-textarea__inner { - border-radius: 4px; - border: 1px solid #DCDFE6; -} - -.custom-form .el-input__inner:focus, -.custom-form .el-textarea__inner:focus { - border-color: #409EFF; -} .save-btn { background: #e6f0fd; @@ -347,10 +341,6 @@ export default { border: none; } -/* 修复select宽度问题 */ -.el-select { - display: block; -} /* 修改开关样式 */ .custom-switch .el-switch__core { @@ -381,4 +371,17 @@ export default { margin-left: -18px; background-color: #1b47ee; } + + +/* 调整flex布局的gap */ +[style*="display: flex"] { + gap: 20px; /* 扩大flex项间距 */ +} + +/* 调整输入框高度 */ +.custom-input-bg .el-input__inner { + height: 32px; /* 固定输入框高度 */ +} + + \ No newline at end of file diff --git a/main/manager-web/src/components/Footer.vue b/main/manager-web/src/components/Footer.vue index 3ccb543c..c362ffcb 100644 --- a/main/manager-web/src/components/Footer.vue +++ b/main/manager-web/src/components/Footer.vue @@ -23,8 +23,7 @@ export default { .message { font-size: 14px; font-weight: 400; - margin-top: auto; - padding-top: 30px; + padding: 20px; color: #979db1; } \ No newline at end of file diff --git a/main/manager-web/src/components/HeaderBar.vue b/main/manager-web/src/components/HeaderBar.vue index be3ebcad..77523e39 100644 --- a/main/manager-web/src/components/HeaderBar.vue +++ b/main/manager-web/src/components/HeaderBar.vue @@ -14,9 +14,11 @@ 控制台