package xiaozhi.modules.timbre.service.impl; import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import cn.hutool.core.collection.CollectionUtil; import lombok.AllArgsConstructor; 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.service.impl.BaseServiceImpl; import xiaozhi.common.utils.ConvertUtils; import xiaozhi.common.utils.MessageUtils; import xiaozhi.modules.model.dto.VoiceDTO; import xiaozhi.modules.security.user.SecurityUser; import xiaozhi.modules.timbre.dao.TimbreDao; import xiaozhi.modules.timbre.dto.TimbreDataDTO; import xiaozhi.modules.timbre.dto.TimbrePageDTO; import xiaozhi.modules.timbre.entity.TimbreEntity; import xiaozhi.modules.timbre.service.TimbreService; import xiaozhi.modules.timbre.vo.TimbreDetailsVO; import xiaozhi.modules.voiceclone.dao.VoiceCloneDao; import xiaozhi.modules.voiceclone.entity.VoiceCloneEntity; /** * 音色的业务层的实现 * * @author zjy * @since 2025-3-21 */ @AllArgsConstructor @Service public class TimbreServiceImpl extends BaseServiceImpl implements TimbreService { private static final Pattern LANGUAGE_SEPARATOR = Pattern.compile("[、;;,,]"); private final TimbreDao timbreDao; private final VoiceCloneDao voiceCloneDao; private final RedisUtils redisUtils; @Override public PageData page(TimbrePageDTO dto) { Map params = new HashMap(); params.put(Constant.PAGE, dto.getPage()); params.put(Constant.LIMIT, dto.getLimit()); IPage page = baseDao.selectPage( getPage(params, null, true), // 定义查询条件 new QueryWrapper() // 必须按照ttsID查找 .eq("tts_model_id", dto.getTtsModelId()) // 如果有音色名字,按照音色名模糊查找 .like(StringUtils.isNotBlank(dto.getName()), "name", dto.getName())); return getPageData(page, TimbreDetailsVO.class); } @Override public TimbreDetailsVO get(String timbreId) { if (StringUtils.isBlank(timbreId)) { return null; } // 先从Redis获取缓存 String key = RedisKeys.getTimbreDetailsKey(timbreId); TimbreDetailsVO cachedDetails = (TimbreDetailsVO) redisUtils.get(key); if (cachedDetails != null) { return cachedDetails; } // 如果缓存中没有,则从数据库获取 TimbreEntity entity = baseDao.selectById(timbreId); if (entity == null) { return null; } // 转换为VO对象 TimbreDetailsVO details = ConvertUtils.sourceToTarget(entity, TimbreDetailsVO.class); // 存入Redis缓存 if (details != null) { redisUtils.set(key, details); } return details; } @Override @Transactional(rollbackFor = Exception.class) public void save(TimbreDataDTO dto) { isTtsModelId(dto.getTtsModelId()); if (dto.getSort() == null) { dto.setSort(0L); } TimbreEntity timbreEntity = ConvertUtils.sourceToTarget(dto, TimbreEntity.class); baseDao.insert(timbreEntity); } @Override @Transactional(rollbackFor = Exception.class) public void update(String timbreId, TimbreDataDTO dto) { isTtsModelId(dto.getTtsModelId()); TimbreEntity timbreEntity = ConvertUtils.sourceToTarget(dto, TimbreEntity.class); timbreEntity.setId(timbreId); baseDao.updateById(timbreEntity); // 删除缓存 redisUtils.delete(RedisKeys.getTimbreDetailsKey(timbreId)); } @Override @Transactional(rollbackFor = Exception.class) public void delete(String[] ids) { baseDao.deleteByIds(Arrays.asList(ids)); } @Override public List getVoiceNames(String ttsModelId, String voiceName) { QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("tts_model_id", StringUtils.isBlank(ttsModelId) ? "" : ttsModelId); if (StringUtils.isNotBlank(voiceName)) { queryWrapper.like("name", voiceName); } List timbreEntities = Optional.ofNullable(timbreDao.selectList(queryWrapper)).orElseGet(ArrayList::new); List voiceDTOs = timbreEntities.stream() .map(entity -> { VoiceDTO dto = new VoiceDTO(entity.getId(), entity.getName()); dto.setVoiceDemo(entity.getVoiceDemo()); dto.setLanguages(entity.getLanguages()); // 设置语言类型 dto.setIsClone(false); // 设置为普通音色 return dto; }) .collect(Collectors.toList()); // 获取当前登录用户ID Long currentUserId = SecurityUser.getUser().getId(); if (currentUserId != null) { // 查询用户的所有克隆音色记录 List cloneEntities = voiceCloneDao.getTrainSuccess(ttsModelId, currentUserId); for (VoiceDTO entity : cloneEntities) { // 只添加训练成功的克隆音色,且模型ID匹配 VoiceDTO voiceDTO = new VoiceDTO(); voiceDTO.setId(entity.getId()); voiceDTO.setName(MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + entity.getName()); // 保留从数据库查询到的voiceDemo字段 voiceDTO.setVoiceDemo(entity.getVoiceDemo()); voiceDTO.setLanguages(entity.getLanguages()); voiceDTO.setIsClone(true); // 设置为克隆音色 redisUtils.set(RedisKeys.getTimbreNameById(voiceDTO.getId()), voiceDTO.getName(), RedisUtils.NOT_EXPIRE); voiceDTOs.add(0, voiceDTO); } } return CollectionUtil.isEmpty(voiceDTOs) ? null : voiceDTOs; } @Override public String getDefaultLanguageById(String id) { if (StringUtils.isBlank(id)) { return null; } TimbreEntity timbre = timbreDao.selectById(id); if (timbre != null) { return firstNonBlankLanguage(timbre.getLanguages()); } VoiceCloneEntity voiceClone = voiceCloneDao.selectById(id); return voiceClone == null ? null : firstNonBlankLanguage(voiceClone.getLanguages()); } private String firstNonBlankLanguage(String languages) { if (StringUtils.isBlank(languages)) { return null; } return LANGUAGE_SEPARATOR.splitAsStream(languages) .map(StringUtils::trimToNull) .filter(Objects::nonNull) .findFirst() .orElse(null); } /** * 处理是不是tts模型的id */ private void isTtsModelId(String ttsModelId) { // 等模型配置那边写好调用方法判断 } @Override public String getTimbreNameById(String id) { if (StringUtils.isBlank(id)) { return null; } String cachedName = (String) redisUtils.get(RedisKeys.getTimbreNameById(id)); if (StringUtils.isNotBlank(cachedName)) { return cachedName; } TimbreEntity entity = timbreDao.selectById(id); if (entity != null) { String name = entity.getName(); if (StringUtils.isNotBlank(name)) { redisUtils.set(RedisKeys.getTimbreNameById(id), name); } return name; } else { VoiceCloneEntity cloneEntity = voiceCloneDao.selectById(id); if (cloneEntity != null) { String name = MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + cloneEntity.getName(); redisUtils.set(RedisKeys.getTimbreNameById(id), name); return name; } } return null; } @Override public VoiceDTO getByVoiceCode(String ttsModelId, String voiceCode) { if (StringUtils.isBlank(voiceCode)) { return null; } QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("tts_model_id", ttsModelId); queryWrapper.eq("tts_voice", voiceCode); List list = timbreDao.selectList(queryWrapper); if (list.isEmpty()) { return null; } TimbreEntity entity = list.get(0); VoiceDTO dto = new VoiceDTO(entity.getId(), entity.getName()); dto.setVoiceDemo(entity.getVoiceDemo()); dto.setIsClone(false); // 设置为普通音色 return dto; } }