367 lines
16 KiB
Java
367 lines
16 KiB
Java
package com.emotion.service.impl;
|
||
|
||
import com.alibaba.fastjson2.JSON;
|
||
import com.alibaba.fastjson2.JSONObject;
|
||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||
import com.emotion.dto.request.EpicScriptCreateWithDialogueRequest;
|
||
import com.emotion.dto.request.ai.AiRuntimeRequest;
|
||
import com.emotion.dto.response.EpicScriptCreateWithDialogueResponse;
|
||
import com.emotion.dto.response.ai.AiRuntimeTestResponse;
|
||
import com.emotion.entity.Conversation;
|
||
import com.emotion.entity.EpicScript;
|
||
import com.emotion.entity.Message;
|
||
import com.emotion.mapper.EpicScriptMapper;
|
||
import com.emotion.service.AiRuntimeService;
|
||
import com.emotion.service.ConversationService;
|
||
import com.emotion.service.EpicScriptDialogueService;
|
||
import com.emotion.service.EpicScriptService;
|
||
import com.emotion.service.MessageService;
|
||
import com.emotion.util.SnowflakeIdGenerator;
|
||
import com.emotion.util.UserContextHolder;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.springframework.beans.BeanUtils;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.stereotype.Service;
|
||
import org.springframework.transaction.annotation.Transactional;
|
||
import org.springframework.util.StringUtils;
|
||
|
||
import java.time.LocalDateTime;
|
||
import java.util.HashMap;
|
||
import java.util.Map;
|
||
|
||
/**
|
||
* 剧本对话创建服务实现类
|
||
*/
|
||
@Slf4j
|
||
@Service
|
||
public class EpicScriptDialogueServiceImpl implements EpicScriptDialogueService {
|
||
|
||
@Autowired
|
||
private EpicScriptService epicScriptService;
|
||
|
||
@Autowired
|
||
private ConversationService conversationService;
|
||
|
||
@Autowired
|
||
private MessageService messageService;
|
||
|
||
@Autowired
|
||
private AiRuntimeService aiRuntimeService;
|
||
|
||
@Autowired
|
||
private SnowflakeIdGenerator snowflakeIdGenerator;
|
||
|
||
@Override
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public EpicScriptCreateWithDialogueResponse createWithDialogue(EpicScriptCreateWithDialogueRequest request) {
|
||
String currentUserId = UserContextHolder.getCurrentUserId();
|
||
if (currentUserId == null) {
|
||
throw new IllegalStateException("用户未登录");
|
||
}
|
||
|
||
String scriptId = snowflakeIdGenerator.nextIdAsString();
|
||
String conversationId = snowflakeIdGenerator.nextIdAsString();
|
||
LocalDateTime now = LocalDateTime.now();
|
||
|
||
// 1. 创建剧本
|
||
EpicScript script = new EpicScript();
|
||
script.setId(scriptId);
|
||
script.setUserId(currentUserId);
|
||
script.setTitle("我的人生剧本"); // 默认标题,后续会被 AI 生成的标题覆盖
|
||
script.setTheme(request.getTheme());
|
||
script.setStyle(StringUtils.hasText(request.getStyle()) ? request.getStyle() : "career");
|
||
script.setLength(StringUtils.hasText(request.getLength()) ? request.getLength() : "medium");
|
||
script.setConversationId(conversationId);
|
||
script.setPlotJson(new HashMap<>());
|
||
script.setIsSelected(0);
|
||
epicScriptService.save(script);
|
||
|
||
// 2. 创建对话
|
||
Conversation conversation = new Conversation();
|
||
conversation.setId(conversationId);
|
||
conversation.setUserId(currentUserId);
|
||
conversation.setUserType("registered");
|
||
conversation.setScriptId(scriptId);
|
||
conversation.setTitle(script.getTitle());
|
||
conversation.setType("script");
|
||
conversation.setConversationStatus("active");
|
||
conversation.setStartTime(now);
|
||
conversation.setLastActiveTime(now);
|
||
conversation.setMessageCount(0);
|
||
conversationService.save(conversation);
|
||
|
||
// 3. 调用 AI 生成完整剧本
|
||
String aiOutput = invokeScriptGenerate(request, currentUserId, conversationId, null);
|
||
|
||
// 4. 解析 AI 输出获取剧本元数据
|
||
Map<String, Object> metadata = parseScriptOutput(aiOutput);
|
||
|
||
// 从 AI 输出中提取标题,如果没有则使用默认值
|
||
String aiTitle = (String) metadata.getOrDefault("title", null);
|
||
if (StringUtils.hasText(aiTitle)) {
|
||
script.setTitle(aiTitle);
|
||
}
|
||
script.setPlotIntro((String) metadata.getOrDefault("plotIntro", ""));
|
||
script.setPlotTurning((String) metadata.getOrDefault("plotTurning", ""));
|
||
script.setPlotClimax((String) metadata.getOrDefault("plotClimax", ""));
|
||
script.setPlotEnding((String) metadata.getOrDefault("plotEnding", ""));
|
||
Map<String, Object> plotJson = script.getPlotJson();
|
||
plotJson.putAll(metadata);
|
||
script.setPlotJson(plotJson);
|
||
|
||
// 5. 创建系统欢迎消息(messageOrder = 1)
|
||
Message systemWelcome = new Message();
|
||
systemWelcome.setId(snowflakeIdGenerator.nextIdAsString());
|
||
systemWelcome.setConversationId(conversationId);
|
||
systemWelcome.setScriptId(scriptId);
|
||
systemWelcome.setUserId(currentUserId);
|
||
systemWelcome.setContent(buildSystemWelcome(metadata));
|
||
systemWelcome.setType("system");
|
||
systemWelcome.setSender("system");
|
||
systemWelcome.setTimestamp(now);
|
||
systemWelcome.setMessageOrder(1L);
|
||
systemWelcome.setStatus("sent");
|
||
systemWelcome.setIsRead(1);
|
||
messageService.createMessage(systemWelcome);
|
||
|
||
// 6. 创建用户消息(messageOrder = 2)
|
||
Message userMessage = new Message();
|
||
userMessage.setId(snowflakeIdGenerator.nextIdAsString());
|
||
userMessage.setConversationId(conversationId);
|
||
userMessage.setScriptId(scriptId);
|
||
userMessage.setUserId(currentUserId);
|
||
userMessage.setContent(request.getTheme());
|
||
userMessage.setType("chat");
|
||
userMessage.setSender("user");
|
||
userMessage.setTimestamp(now);
|
||
userMessage.setMessageOrder(2L);
|
||
userMessage.setStatus("sent");
|
||
userMessage.setIsRead(1);
|
||
messageService.createMessage(userMessage);
|
||
|
||
// 7. 创建 AI 助手开场白消息(messageOrder = 3)
|
||
String aiContent = buildDisplayContent(metadata);
|
||
Message aiMessage = new Message();
|
||
aiMessage.setId(snowflakeIdGenerator.nextIdAsString());
|
||
aiMessage.setConversationId(conversationId);
|
||
aiMessage.setScriptId(scriptId);
|
||
aiMessage.setUserId(currentUserId);
|
||
aiMessage.setContent(aiContent);
|
||
aiMessage.setType("script");
|
||
aiMessage.setSender("assistant");
|
||
aiMessage.setTimestamp(LocalDateTime.now());
|
||
aiMessage.setMessageOrder(3L);
|
||
aiMessage.setVersionNumber(1);
|
||
aiMessage.setStatus("sent");
|
||
aiMessage.setMetadata(JSON.toJSONString(metadata));
|
||
messageService.createMessage(aiMessage);
|
||
|
||
// 8. 更新剧本信息(已在前面完成)
|
||
script.setCurrentVersionMessageId(aiMessage.getId());
|
||
epicScriptService.updateById(script);
|
||
|
||
// 9. 更新对话当前消息和消息计数
|
||
conversation.setCurrentMessageId(aiMessage.getId());
|
||
conversation.setMessageCount(3);
|
||
conversationService.updateById(conversation);
|
||
|
||
// 10. 构建响应
|
||
EpicScriptCreateWithDialogueResponse response = new EpicScriptCreateWithDialogueResponse();
|
||
BeanUtils.copyProperties(script, response);
|
||
response.setScriptId(scriptId);
|
||
response.setConversationId(conversationId);
|
||
response.setCurrentVersionMessageId(aiMessage.getId());
|
||
return response;
|
||
}
|
||
|
||
private String invokeScriptGenerate(EpicScriptCreateWithDialogueRequest request, String userId,
|
||
String conversationId, String userMessageId) {
|
||
JSONObject inputs = new JSONObject();
|
||
inputs.put("input", request.getTheme());
|
||
inputs.put("prompt", request.getTheme());
|
||
inputs.put("theme", request.getTheme());
|
||
inputs.put("style", StringUtils.hasText(request.getStyle()) ? request.getStyle() : "career");
|
||
inputs.put("length", StringUtils.hasText(request.getLength()) ? request.getLength() : "medium");
|
||
inputs.put("characterInfo", request.getCharacterInfo());
|
||
inputs.put("lifeEventsSummary", request.getLifeEventsSummary());
|
||
inputs.put("useSocialInsights", request.getUseSocialInsights());
|
||
inputs.put("conversationId", conversationId);
|
||
inputs.put("userMessageId", userMessageId);
|
||
|
||
// 使用 AiRuntimeRequest 调用 AI 运行时
|
||
AiRuntimeRequest runtimeRequest = new AiRuntimeRequest();
|
||
runtimeRequest.setSceneCode("script_generate");
|
||
runtimeRequest.setUserId(userId);
|
||
runtimeRequest.setInputs(inputs);
|
||
|
||
AiRuntimeTestResponse response = aiRuntimeService.test(runtimeRequest);
|
||
if (response == null || !"success".equals(response.getStatus()) || !StringUtils.hasText(response.getOutput())) {
|
||
String message = response == null ? "AI_RUNTIME_EMPTY_RESPONSE" : response.getErrorMessage();
|
||
throw new IllegalStateException(StringUtils.hasText(message) ? message : "AI_RUNTIME_FAILED");
|
||
}
|
||
return response.getOutput();
|
||
}
|
||
|
||
private Map<String, Object> parseScriptOutput(String output) {
|
||
if (!StringUtils.hasText(output)) {
|
||
return new HashMap<>();
|
||
}
|
||
try {
|
||
return JSON.parseObject(output, Map.class);
|
||
} catch (Exception e) {
|
||
log.warn("剧本输出解析失败,使用原始文本作为内容: {}", e.getMessage());
|
||
Map<String, Object> fallback = new HashMap<>();
|
||
fallback.put("title", "我的人生剧本");
|
||
fallback.put("plotIntro", output);
|
||
return fallback;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 保存小说生成结果到数据库
|
||
* 创建 EpicScript、Conversation 和 3 条 Message 记录
|
||
*
|
||
* @param currentUserId 当前用户 ID
|
||
* @param theme 用户主题
|
||
* @param fullText 完整小说文本
|
||
* @param metadata 大纲元数据(标题等)
|
||
* @return Map 包含 scriptId、conversationId、currentVersionMessageId
|
||
*/
|
||
public Map<String, String> saveNovelResult(String currentUserId, String theme,
|
||
String fullText, Map<String, Object> metadata) {
|
||
String scriptId = snowflakeIdGenerator.nextIdAsString();
|
||
String conversationId = snowflakeIdGenerator.nextIdAsString();
|
||
LocalDateTime now = LocalDateTime.now();
|
||
|
||
// 1. 创建剧本
|
||
EpicScript script = new EpicScript();
|
||
script.setId(scriptId);
|
||
script.setUserId(currentUserId);
|
||
script.setTitle(metadata != null && metadata.get("title") != null
|
||
? (String) metadata.get("title") : "我的人生剧本");
|
||
script.setTheme(theme);
|
||
script.setStyle("career");
|
||
script.setLength("medium");
|
||
script.setConversationId(conversationId);
|
||
script.setPlotIntro(fullText);
|
||
script.setPlotTurning("");
|
||
script.setPlotClimax("");
|
||
script.setPlotEnding("");
|
||
script.setPlotJson(metadata != null ? metadata : new HashMap<>());
|
||
script.setIsSelected(0);
|
||
epicScriptService.save(script);
|
||
|
||
// 2. 创建对话
|
||
Conversation conversation = new Conversation();
|
||
conversation.setId(conversationId);
|
||
conversation.setUserId(currentUserId);
|
||
conversation.setUserType("registered");
|
||
conversation.setScriptId(scriptId);
|
||
conversation.setTitle(script.getTitle());
|
||
conversation.setType("script");
|
||
conversation.setConversationStatus("active");
|
||
conversation.setStartTime(now);
|
||
conversation.setLastActiveTime(now);
|
||
conversation.setMessageCount(3);
|
||
conversationService.save(conversation);
|
||
|
||
// 3. 系统欢迎消息(messageOrder = 1)
|
||
Message systemWelcome = new Message();
|
||
systemWelcome.setId(snowflakeIdGenerator.nextIdAsString());
|
||
systemWelcome.setConversationId(conversationId);
|
||
systemWelcome.setScriptId(scriptId);
|
||
systemWelcome.setUserId(currentUserId);
|
||
systemWelcome.setContent("欢迎来到《" + script.getTitle() + "》剧本世界。\n\n让我们一起探索你的心愿故事。");
|
||
systemWelcome.setType("system");
|
||
systemWelcome.setSender("system");
|
||
systemWelcome.setTimestamp(now);
|
||
systemWelcome.setMessageOrder(1L);
|
||
systemWelcome.setStatus("sent");
|
||
systemWelcome.setIsRead(1);
|
||
messageService.createMessage(systemWelcome);
|
||
|
||
// 4. 用户输入消息(messageOrder = 2)
|
||
Message userMessage = new Message();
|
||
userMessage.setId(snowflakeIdGenerator.nextIdAsString());
|
||
userMessage.setConversationId(conversationId);
|
||
userMessage.setScriptId(scriptId);
|
||
userMessage.setUserId(currentUserId);
|
||
userMessage.setContent(theme);
|
||
userMessage.setType("chat");
|
||
userMessage.setSender("user");
|
||
userMessage.setTimestamp(now);
|
||
userMessage.setMessageOrder(2L);
|
||
userMessage.setStatus("sent");
|
||
userMessage.setIsRead(1);
|
||
messageService.createMessage(userMessage);
|
||
|
||
// 5. AI 小说全文消息(messageOrder = 3)
|
||
Message aiMessage = new Message();
|
||
aiMessage.setId(snowflakeIdGenerator.nextIdAsString());
|
||
aiMessage.setConversationId(conversationId);
|
||
aiMessage.setScriptId(scriptId);
|
||
aiMessage.setUserId(currentUserId);
|
||
aiMessage.setContent(fullText);
|
||
aiMessage.setType("script");
|
||
aiMessage.setSender("assistant");
|
||
aiMessage.setTimestamp(now);
|
||
aiMessage.setMessageOrder(3L);
|
||
aiMessage.setVersionNumber(1);
|
||
aiMessage.setStatus("sent");
|
||
aiMessage.setMetadata(metadata != null ? JSON.toJSONString(metadata) : "{}");
|
||
messageService.createMessage(aiMessage);
|
||
|
||
// 6. 更新剧本和对话
|
||
script.setCurrentVersionMessageId(aiMessage.getId());
|
||
epicScriptService.updateById(script);
|
||
|
||
conversation.setCurrentMessageId(aiMessage.getId());
|
||
conversationService.updateById(conversation);
|
||
|
||
Map<String, String> result = new HashMap<>();
|
||
result.put("scriptId", scriptId);
|
||
result.put("conversationId", conversationId);
|
||
result.put("currentVersionMessageId", aiMessage.getId());
|
||
return result;
|
||
}
|
||
|
||
private String buildDisplayContent(Map<String, Object> metadata) {
|
||
StringBuilder sb = new StringBuilder();
|
||
appendSection(sb, metadata.get("title"), null);
|
||
appendSection(sb, metadata.get("plotIntro"), "序幕:低谷回响");
|
||
appendSection(sb, metadata.get("plotTurning"), "转折:契机出现");
|
||
appendSection(sb, metadata.get("plotClimax"), "高潮:命运抉择");
|
||
appendSection(sb, metadata.get("plotEnding"), "结局:新的开始");
|
||
return sb.toString();
|
||
}
|
||
|
||
private String buildSystemWelcome(Map<String, Object> metadata) {
|
||
if (metadata == null || metadata.isEmpty()) {
|
||
return "欢迎来到情绪博物馆,让我们一起探索你的内心世界。";
|
||
}
|
||
String title = (String) metadata.getOrDefault("title", "我的人生剧本");
|
||
StringBuilder sb = new StringBuilder();
|
||
sb.append("欢迎来到《").append(title).append("》剧本世界。\n\n");
|
||
sb.append("在这里,你将体验一段关于");
|
||
sb.append(metadata.getOrDefault("plotIntro", "情感探索")).append("的旅程。");
|
||
return sb.toString();
|
||
}
|
||
|
||
private void appendSection(StringBuilder sb, Object value, String label) {
|
||
if (value == null) {
|
||
return;
|
||
}
|
||
String text = String.valueOf(value);
|
||
if (!StringUtils.hasText(text)) {
|
||
return;
|
||
}
|
||
if (sb.length() > 0) {
|
||
sb.append("\n\n");
|
||
}
|
||
if (StringUtils.hasText(label)) {
|
||
sb.append("【").append(label).append("】\n");
|
||
}
|
||
sb.append(text);
|
||
}
|
||
}
|