diff --git a/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java b/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java index 573269b8..c8cc784a 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java +++ b/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java @@ -188,4 +188,12 @@ public interface ErrorCode { int VOICE_RESOURCE_ACCOUNT_EMPTY = 10148; // 归属账号不能为空 int VOICE_RESOURCE_DELETE_ID_EMPTY = 10149; // 删除的音色资源ID不能为空 int VOICE_RESOURCE_NO_PERMISSION = 10150; // 您没有权限操作该记录 + int VOICE_CLONE_AUDIO_NOT_UPLOADED = 10151; // 请先上传音频文件 + int VOICE_CLONE_MODEL_CONFIG_NOT_FOUND = 10152; // 模型配置未找到 + int VOICE_CLONE_MODEL_TYPE_NOT_FOUND = 10153; // 模型类型未找到 + int VOICE_CLONE_TRAINING_FAILED = 10154; // 训练失败 + int VOICE_CLONE_HUOSHAN_CONFIG_MISSING = 10155; // 火山引擎缺少配置 + int VOICE_CLONE_RESPONSE_FORMAT_ERROR = 10156; // 响应格式错误 + int VOICE_CLONE_REQUEST_FAILED = 10157; // 请求失败 + int VOICE_CLONE_PREFIX = 10158; // 复刻音色前缀 } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/TimbreService.java b/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/TimbreService.java index eebd55be..424ad2b9 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/TimbreService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/TimbreService.java @@ -64,4 +64,12 @@ public interface TimbreService extends BaseService { * @return 音色名称 */ String getTimbreNameById(String id); + + /** + * 检查音色编码(ttsVoice/speaker_id)是否已存在 + * + * @param ttsVoice 音色编码 + * @return 是否存在 + */ + boolean existsByTtsVoice(String ttsVoice); } \ 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 2ed5b1ab..ac26e30f 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,9 +1,12 @@ package xiaozhi.modules.timbre.service.impl; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import xiaozhi.common.exception.ErrorCode; +import xiaozhi.common.utils.MessageUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @@ -120,12 +123,39 @@ public class TimbreServiceImpl extends BaseServiceImpl if (StringUtils.isNotBlank(voiceName)) { queryWrapper.like("name", voiceName); } + List timbreEntities = timbreDao.selectList(queryWrapper); if (CollectionUtil.isEmpty(timbreEntities)) { return null; } - return ConvertUtils.sourceToTarget(timbreEntities, VoiceDTO.class); + // 自定义排序:音色编码以S_开头的排在最前面,其他按sort字段升序排序 + timbreEntities.sort((t1, t2) -> { + // 先判断是否以S_开头 + boolean isT1S_Start = StringUtils.isNotBlank(t1.getTtsVoice()) && t1.getTtsVoice().startsWith("S_"); + boolean isT2S_Start = StringUtils.isNotBlank(t2.getTtsVoice()) && t2.getTtsVoice().startsWith("S_"); + + if (isT1S_Start && !isT2S_Start) return -1; // t1以S_开头,排在前面 + if (!isT1S_Start && isT2S_Start) return 1; // t2以S_开头,排在前面 + + // 都以S_开头或都不以S_开头,则按sort字段升序排序 + return Long.compare(t1.getSort(), t2.getSort()); + }); + + // 排序完成后转换为DTO列表,并为复刻音色添加前缀 + List voiceDTOs = new ArrayList<>(); + for (TimbreEntity entity : timbreEntities) { + VoiceDTO dto = new VoiceDTO(); + dto.setId(entity.getId()); + // 对于音色编码以S_开头的复刻音色,在名称前加上"克隆:"前缀 + String name = entity.getName(); + if (StringUtils.isNotBlank(entity.getTtsVoice()) && entity.getTtsVoice().startsWith("S_") && !name.startsWith("克隆:")) { + name = MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + name; + } + dto.setName(name); + voiceDTOs.add(dto); + } + return voiceDTOs; } /** @@ -158,4 +188,17 @@ public class TimbreServiceImpl extends BaseServiceImpl return null; } + + @Override + public boolean existsByTtsVoice(String ttsVoice) { + if (StringUtils.isBlank(ttsVoice)) { + return false; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("tts_voice", ttsVoice); + + // 检查数据库中是否存在相同的ttsVoice值 + return timbreDao.exists(queryWrapper); + } } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/service/impl/VoiceCloneServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/service/impl/VoiceCloneServiceImpl.java index 6ccbc8a8..9ce6abb9 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/service/impl/VoiceCloneServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/service/impl/VoiceCloneServiceImpl.java @@ -47,6 +47,7 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl page(Map params) { @@ -228,19 +229,19 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl config = modelConfig.getConfigJson(); String type = (String) config.get("type"); if (StringUtils.isBlank(type)) { - throw new RenException("模型类型未找到"); + throw new RenException(ErrorCode.VOICE_CLONE_MODEL_TYPE_NOT_FOUND); } if (type.equals("huoshan_double_stream")) { huoshanClone(config, entity); @@ -252,7 +253,7 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl