Files
happy-life-star/server/src/main/java/com/emotion/service/impl/EpicScriptDialogueServiceImpl.java
T

367 lines
16 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}