feat(chat): 新增AI陪聊角色voiceId字段及使用逻辑
- AiCompanionVO、KeyboardAiCompanion实体新增voiceId字段与Swagger注解 - ChatServiceImpl同步/异步消息接口优先使用角色配置voiceId进行TTS - 补充角色状态校验,下线角色禁止对话
This commit is contained in:
@@ -66,7 +66,8 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
"/user/verifyMailCode",
|
||||
"/character/listWithNotLogin",
|
||||
"/character/listByTagWithNotLogin",
|
||||
"/ai-companion/report"
|
||||
"/ai-companion/report",
|
||||
"/apple/notification"
|
||||
};
|
||||
}
|
||||
@Bean
|
||||
|
||||
@@ -146,4 +146,8 @@ public class KeyboardAiCompanion {
|
||||
@TableField(value = "prologue_audio")
|
||||
@Schema(description="开场白音频")
|
||||
private String prologueAudio;
|
||||
|
||||
@TableField(value = "voice_id")
|
||||
@Schema(description="角色音频Id")
|
||||
private String voiceId;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.yolo.keyborad.model.vo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -66,4 +67,9 @@ public class AiCompanionVO {
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private Date createdAt;
|
||||
|
||||
@TableField(value = "voice_id")
|
||||
@Schema(description="角色音频Id")
|
||||
private String voiceId;
|
||||
|
||||
}
|
||||
|
||||
@@ -422,6 +422,7 @@ public class ApplePurchaseServiceImpl implements ApplePurchaseService {
|
||||
// 检查VIP是否已过期
|
||||
if (user.getVipExpiry() != null && user.getVipExpiry().toInstant().isBefore(Instant.now())) {
|
||||
user.setIsVip(false);
|
||||
user.setVipLevel(null);
|
||||
userService.updateById(user);
|
||||
log.info("User VIP expired: userId={}", userId);
|
||||
}
|
||||
@@ -464,6 +465,7 @@ public class ApplePurchaseServiceImpl implements ApplePurchaseService {
|
||||
} else {
|
||||
// 倒扣后已过期,取消VIP
|
||||
user.setIsVip(false);
|
||||
user.setVipLevel(null);
|
||||
user.setVipExpiry(Date.from(newExpiry)); // 保留倒扣后的时间,而不是设为null
|
||||
log.info("Subscription refunded, VIP expired after deduction: userId={}, newExpiry={}",
|
||||
userId, newExpiry);
|
||||
@@ -607,6 +609,7 @@ public class ApplePurchaseServiceImpl implements ApplePurchaseService {
|
||||
|
||||
// 4. 更新用户VIP状态
|
||||
user.setIsVip(true);
|
||||
user.setVipLevel(product.getLevel());
|
||||
user.setVipExpiry(Date.from(newExpiry));
|
||||
|
||||
// 5. 保存用户信息到数据库
|
||||
@@ -764,6 +767,7 @@ public class ApplePurchaseServiceImpl implements ApplePurchaseService {
|
||||
|
||||
// 6. 更新用户VIP状态
|
||||
user.setIsVip(true); // 设置为VIP用户
|
||||
user.setVipLevel(product.getLevel());
|
||||
user.setVipExpiry(Date.from(newExpiry)); // 更新VIP过期时间
|
||||
|
||||
// 7. 保存用户信息到数据库
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.yolo.keyborad.config.NacosAppConfigCenter;
|
||||
import com.yolo.keyborad.exception.BusinessException;
|
||||
import com.yolo.keyborad.model.dto.chat.ChatReq;
|
||||
import com.yolo.keyborad.model.dto.chat.ChatStreamMessage;
|
||||
import com.yolo.keyborad.model.entity.KeyboardAiCompanion;
|
||||
import com.yolo.keyborad.model.entity.KeyboardAiChatMessage;
|
||||
import com.yolo.keyborad.model.entity.KeyboardCharacter;
|
||||
import com.yolo.keyborad.model.entity.KeyboardUser;
|
||||
@@ -406,8 +407,16 @@ public class ChatServiceImpl implements ChatService {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// 获取AI人设的系统提示词
|
||||
String systemPrompt = aiCompanionService.getSystemPromptById(companionId);
|
||||
// 获取AI角色信息(用于系统提示词 + voiceId)
|
||||
KeyboardAiCompanion companion = aiCompanionService.getById(companionId);
|
||||
if (companion == null) {
|
||||
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "AI陪聊角色不存在");
|
||||
}
|
||||
if (companion.getStatus() == null || companion.getStatus() != 1) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "AI陪聊角色已下线");
|
||||
}
|
||||
String systemPrompt = companion.getSystemPrompt();
|
||||
String voiceId = companion.getVoiceId();
|
||||
|
||||
// 获取最近20条聊天记录作为上下文
|
||||
List<KeyboardAiChatMessage> historyMessages = aiChatMessageService.getRecentMessages(
|
||||
@@ -444,8 +453,8 @@ public class ChatServiceImpl implements ChatService {
|
||||
// 初始化音频任务状态为 processing
|
||||
setAudioTaskStatus(audioId, AudioTaskVO.STATUS_PROCESSING, null, null);
|
||||
|
||||
// 异步执行 TTS + R2 上传
|
||||
CompletableFuture.runAsync(() -> processAudioAsync(audioId, response, userId));
|
||||
// 异步执行 TTS + R2 上传(优先使用角色配置的 voiceId)
|
||||
CompletableFuture.runAsync(() -> processAudioAsync(audioId, response, userId, voiceId));
|
||||
|
||||
return ChatMessageVO.builder()
|
||||
.aiResponse(response)
|
||||
@@ -515,14 +524,14 @@ public class ChatServiceImpl implements ChatService {
|
||||
/**
|
||||
* 异步处理音频:TTS 转换 + 上传 R2
|
||||
*/
|
||||
private void processAudioAsync(String audioId, String text, String userId) {
|
||||
private void processAudioAsync(String audioId, String text, String userId, String voiceId) {
|
||||
try {
|
||||
log.info("开始异步音频处理, audioId: {}", audioId);
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// 1. TTS 转换
|
||||
long ttsStart = System.currentTimeMillis();
|
||||
TextToSpeechVO ttsResult = elevenLabsService.textToSpeechWithTimestamps(text);
|
||||
TextToSpeechVO ttsResult = elevenLabsService.textToSpeechWithTimestamps(text, voiceId);
|
||||
long ttsDuration = System.currentTimeMillis() - ttsStart;
|
||||
log.info("TTS 完成, audioId: {}, 耗时: {}ms", audioId, ttsDuration);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user