content: package org.ruoyi.chat.service.chat.impl;
  
  import cn.dev33.satoken.stp.StpUtil;
  import cn.hutool.core.collection.CollectionUtil;
  import jakarta.servlet.http.HttpServletRequest;
  import lombok.RequiredArgsConstructor;
  import lombok.extern.slf4j.Slf4j;
  import okhttp3.ResponseBody;
  import org.ruoyi.chat.enums.promptTemplateEnum;
  import org.ruoyi.chat.factory.ChatServiceFactory;
  import org.ruoyi.chat.service.chat.IChatCostService;
  import org.ruoyi.chat.service.chat.IChatService;
  import org.ruoyi.chat.service.chat.ISseService;
  import org.ruoyi.chat.support.ChatRetryHelper;
  import org.ruoyi.chat.support.RetryNotifier;
  import org.ruoyi.chat.util.SSEUtil;
  import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
  import org.ruoyi.common.chat.entity.chat.Message;
  import org.ruoyi.common.chat.entity.files.UploadFileResponse;
  import org.ruoyi.common.chat.entity.whisper.WhisperResponse;
  import org.ruoyi.common.chat.openai.OpenAiStreamClient;
  import org.ruoyi.common.chat.request.ChatRequest;
  import org.ruoyi.common.core.utils.DateUtils;
  import org.ruoyi.common.core.utils.StringUtils;
  import org.ruoyi.common.core.utils.file.FileUtils;
  import org.ruoyi.common.core.utils.file.MimeTypeUtils;
  import org.ruoyi.common.satoken.utils.LoginHelper;
  import org.ruoyi.domain.bo.ChatSessionBo;
  import org.ruoyi.domain.bo.QueryVectorBo;
  import org.ruoyi.domain.vo.ChatModelVo;
  import org.ruoyi.domain.vo.KnowledgeInfoVo;
  import org.ruoyi.domain.vo.PromptTemplateVo;
  import org.ruoyi.service.IChatModelService;
  import org.ruoyi.service.IChatSessionService;
  import org.ruoyi.service.IKnowledgeInfoService;
  import org.ruoyi.service.IPromptTemplateService;
  import org.ruoyi.service.VectorStoreService;
  import org.springframework.core.io.InputStreamResource;
  import org.springframework.core.io.Resource;
  import org.springframework.http.MediaType;
  import org.springframework.http.ResponseEntity;
  import org.springframework.stereotype.Service;
  import org.springframework.web.multipart.MultipartFile;
  import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
  
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.nio.file.Files;
  import java.nio.file.Path;
  import java.util.List;
  import java.util.Objects;
  
  /**
   * @author ageer
   */
  @Service
  @Slf4j
  @RequiredArgsConstructor
  public class SseServiceImpl implements ISseService {
  
      private final OpenAiStreamClient openAiStreamClient;
  
      private final VectorStoreService vectorStoreService;
  
      private final IChatCostService chatCostService;
  
      private final IChatModelService chatModelService;
  
      private final ChatServiceFactory chatServiceFactory;
  
      private final IChatSessionService chatSessionService;
  
      private final IKnowledgeInfoService knowledgeInfoService;
  
      private ChatModelVo chatModelVo;
  
      // 提示词模板服务
      private final IPromptTemplateService promptTemplateService;
  
  
      @Override
      public SseEmitter sseChat(ChatRequest chatRequest, HttpServletRequest request) {
          SseEmitter sseEmitter = new SseEmitter(0L);
          try {
              // 记录当前会话令牌，供异步线程使用
              try {
                  chatRequest.setToken(StpUtil.getTokenValue());
              } catch (Exception ignore) {
                  // 保底：无token场景下忽略
              }
              // 构建消息列表
              buildChatMessageList(chatRequest);
              // 设置对话角色
              chatRequest.setRole(Message.Role.USER.getName());
  
              if (LoginHelper.isLogin()) {
  
                  // 设置用户id
                  chatRequest.setUserId(LoginHelper.getUserId());
  
  
                  //待优化的地方 （这里请前端提交send的时候传递uuid进来或者sessionId）
                  //待优化的地方 （这里请前端提交send的时候传递uuid进来或者sessionId）
                  //待优化的地方 （这里请前端提交send的时候传递uuid进来或者sessionId）
                  {
                      // 设置会话id
                      if (chatRequest.getUuid() == null) {
                          //暂时随机生成会话id
                          chatRequest.setSessionId(System.currentTimeMillis());
                      } else {
                          //这里或许需要修改一下，这里应该用uuid 或者 前端传递 sessionId
                          chatRequest.setSessionId(chatRequest.getUuid());
                      }
                  }
  
  
  
                  chatRequest.setUserId(chatCostService.getUserId());
                  if (chatRequest.getSessionId() == null) {
                      ChatSessionBo chatSessionBo = new ChatSessionBo();
                      chatSessionBo.setUserId(chatCostService.getUserId());
                      chatSessionBo.setSessionTitle(getFirst10Characters(chatRequest.getPrompt()));
                      chatSessionBo.setSessionContent(chatRequest.getPrompt());
                      chatSessionService.insertByBo(chatSessionBo);
                      chatRequest.setSessionId(chatSessionBo.getId());
                  }
                  
                  // 保存用户消息
                  chatCostService.saveMessage(chatRequest);
              }
              // 自动选择模型并获取对应的聊天服务
              IChatService chatService = autoSelectModelAndGetService(chatRequest);
  
              // 用户消息只保存不计费，AI回复由BillingChatServiceProxy自动处理计费
              // chatCostService.publishBillingEvent(chatRequest); // 用户输入不计费
              if (Boolean.TRUE.equals(chatRequest.getAutoSelectModel())) {
                  ChatModelVo currentModel = this.chatModelVo;
                  String currentCategory = currentModel.getCategory();
                  ChatRetryHelper.executeWithRetry(
                          currentModel,
                          currentCategory,
                          chatModelService,
                          sseEmitter,
                          (modelForTry, onFailure) -> {
                              // 替换请求中的模型名称
                              chatRequest.setModel(modelForTry.getModelName());
                              // 以 emitter 实例为唯一键注册失败回调
                              RetryNotifier.setFailureCallback(sseEmitter, onFailure);
                              try {
                                  autoSelectServiceByCategoryAndInvoke(chatRequest, sseEmitter,
                                          modelForTry.getCategory());
                              } finally {
                                  // 不在此处清理，待下游结束/失败时清理
                              }
                          }
                  );
              } else {
                  // 不重试不降级，直接调用
                  chatService.chat(chatRequest, sseEmitter);
              }
          } catch (Exception e) {
              log.error(e.getMessage(), e);
              SSEUtil.sendErrorEvent(sseEmitter, e.getMessage());
          }
          return sseEmitter;
      }
  
      /**
       * 自动选择模型并获取对应的聊天服务
       */
      private IChatService autoSelectModelAndGetService(ChatRequest chatRequest) {
          try {
              if (Boolean.TRUE.equals(chatRequest.getHasAttachment())) {
                  chatModelVo = selectModelByCategory("image");
              } else if (Boolean.TRUE.equals(chatRequest.getAutoSelectModel())) {
                  chatModelVo = selectModelByCategory("chat");
              } else {
                  chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
              }
  
              if (chatModelVo == null) {
                  throw new IllegalStateException("未找到模型名称：" + chatRequest.getModel());
              }
              // 自动设置请求参数中的模型名称
              chatRequest.setModel(chatModelVo.getModelName());
              // 直接返回对应的聊天服务
              return chatServiceFactory.getChatService(chatModelVo.getCategory());
          } catch (Exception e) {
              log.error("模型选择和服务获取失败: {}", e.getMessage(), e);
              throw new IllegalStateException("模型选择和服务获取失败: " + e.getMessage());
          }
      }
  
      /**
       * 根据给定分类获取服务并发起调用（避免在降级时重复选择模型）
       */
      private void autoSelectServiceByCategoryAndInvoke(ChatRequest chatRequest, SseEmitter sseEmitter, String category) {
          IChatService service = chatServiceFactory.getChatService(category);
          service.chat(chatRequest, sseEmitter);
      }
  
      /**
       * 根据分类选择优先级最高的模型
       */
      private ChatModelVo selectModelByCategory(String category) {
          ChatModelVo model = chatModelService.selectModelByCategoryWithHighestPriority(category);
          if (model == null) {
              throw new IllegalStateException("未找到" + category + "分类的模型配置");
          }
          return model;
      }
  
      /**
       * 获取对话标题
       *
       * @param str 原字符
       * @return 截取后的字符
       */
      public static String getFirst10Characters(String str) {
          // 判断字符串长度
          if (str.length() > 10) {
              // 如果长度大于10，截取前10个字符
              return str.substring(0, 10);
          } else {
              // 如果长度不足10，返回整个字符串
              return str;
          }
      }
  
      /**
       * 构建消息列表
       */
      private void buildChatMessageList(ChatRequest chatRequest) {
          List<Message> messages = chatRequest.getMessages();
  
          // 处理知识库相关逻辑
          String sysPrompt = processKnowledgeBase(chatRequest, messages);
  
          // 设置系统提示词
          Message sysMessage = Message.builder()
                  .content(sysPrompt)
                  .role(Message.Role.SYSTEM)
                  .build();
          messages.add(0, sysMessage);
  
          chatRequest.setSysPrompt(sysPrompt);
  
          // 用户对话内容
          String chatString = null;
          // 获取用户对话信息
          Object content = messages.get(messages.size() - 1).getContent();
          if (content instanceof List<?> listContent) {
              if (CollectionUtil.isNotEmpty(listContent)) {
                  chatString = listContent.get(0).toString();
              }
          } else {
              chatString = content.toString();
          }
          chatRequest.setPrompt(chatString);
      }
  
      /**
       * 处理知识库相关逻辑
       */
      private String processKnowledgeBase(ChatRequest chatRequest, List<Message> messages) {
          if (StringUtils.isEmpty(chatRequest.getKid())) {
              return getPromptTemplatePrompt(promptTemplateEnum.VECTOR.getDesc());
          }
  
          try {
              // 查询知识库信息
              KnowledgeInfoVo knowledgeInfoVo = knowledgeInfoService.queryById(Long.valueOf(chatRequest.getKid()));
              if (knowledgeInfoVo == null) {
                  log.warn("知识库信息不存在，kid: {}", chatRequest.getKid());
                  return getPromptTemplatePrompt(promptTemplateEnum.VECTOR.getDesc());
              }
  
              // 查询向量模型配置信息
              ChatModelVo chatModel = chatModelService.selectModelByName(knowledgeInfoVo.getEmbeddingModelName());
              if (chatModel == null) {
                  log.warn("向量模型配置不存在，模型名称: {}", knowledgeInfoVo.getEmbeddingModelName());
                  return getPromptTemplatePrompt(promptTemplateEnum.VECTOR.getDesc());
              }
  
              // 构建向量查询参数
              QueryVectorBo queryVectorBo = buildQueryVectorBo(chatRequest, knowledgeInfoVo, chatModel);
  
              // 获取向量查询结果
              List<String> nearestList = vectorStoreService.getQueryVector(queryVectorBo);
  
              // 添加知识库消息到上下文
              addKnowledgeMessages(messages, nearestList);
  
              // 返回知识库系统提示词
              return getKnowledgeSystemPrompt(knowledgeInfoVo);
  
          } catch (Exception e) {
              log.error("处理知识库信息失败: {}", e.getMessage(), e);
              return getPromptTemplatePrompt(promptTemplateEnum.VECTOR.getDesc());
          }
      }
  
      /**
       * 构建向量查询参数
       */
      private QueryVectorBo buildQueryVectorBo(ChatRequest chatRequest, KnowledgeInfoVo knowledgeInfoVo,
                                               ChatModelVo chatModel) {
          String content = chatRequest.getMessages().get(chatRequest.getMessages().size() - 1).getContent().toString();
  
          QueryVectorBo queryVectorBo = new QueryVectorBo();
          queryVectorBo.setQuery(content);
          queryVectorBo.setKid(chatRequest.getKid());
          queryVectorBo.setApiKey(chatModel.getApiKey());
          queryVectorBo.setBaseUrl(chatModel.getApiHost());
          queryVectorBo.setVectorModelName(knowledgeInfoVo.getVectorModelName());
          queryVectorBo.setEmbeddingModelName(knowledgeInfoVo.getEmbeddingModelName());
          queryVectorBo.setMaxResults(knowledgeInfoVo.getRetrieveLimit());
  
          return queryVectorBo;
      }
  
      /**
       * 添加知识库消息到上下文
       */
      private void addKnowledgeMessages(List<Message> messages, List<String> nearestList) {
          for (String prompt : nearestList) {
              Message userMessage = Message.builder()
                      .content(prompt)
                      .role(Message.Role.USER)
                      .build();
              messages.add(userMessage);
          }
      }
  
      /**
       * 获取知识库系统提示词
       */
      private String getKnowledgeSystemPrompt(KnowledgeInfoVo knowledgeInfoVo) {
          String sysPrompt = knowledgeInfoVo.getSystemPrompt();
          if (StringUtils.isEmpty(sysPrompt)) {
              sysPrompt = "###角色设定\n" +
                      "你是一个智能知识助手，专注于利用上下文中的信息来提供准确和相关的回答。\n" +
                      "###指令\n" +
                      "当用户的问题与上下文知识匹配时，利用上下文信息进行回答。如果问题与上下文不匹配，运用自身的推理能力生成合适的回答。\n" +
                      "###限制\n" +
                      "确保回答清晰简洁，避免提供不必要的细节。始终保持语气友好\n" +
                      "当前时间：" + DateUtils.getDate();
          }
          return sysPrompt;
      }
  
  
      /**
       * 获取提示词模板提示词
       */
      private String getPromptTemplatePrompt(String category) {
          PromptTemplateVo promptTemplateVo = promptTemplateService.queryByCategory(category);
          if (Objects.isNull(promptTemplateVo) || StringUtils.isEmpty(promptTemplateVo.getTemplateContent())) {
              return getDefaultSystemPrompt();
          }
          return promptTemplateVo.getTemplateContent();
      }
  
      /**
       * 获取默认系统提示词
       */
      private String getDefaultSystemPrompt() {
          String sysPrompt = chatModelVo != null ? chatModelVo.getSystemPrompt() : null;
          if (StringUtils.isEmpty(sysPrompt)) {
              sysPrompt = "你是一个由RuoYI-AI开发的人工智能助手，名字叫RuoYI人工智能助手。"
                      + "你擅长中英文对话，能够理解并处理各种问题，提供安全、有帮助、准确的回答。"
                      + "当前时间：" + DateUtils.getDate()
                      + "#注意：回复之前注意结合上下文和工具返回内容进行回复。";
          }
          return sysPrompt;
      }
  
  
      /**
       * 文字转语音
       */
      @Override
      public ResponseEntity<Resource> textToSpeed(TextToSpeech textToSpeech) {
          ResponseBody body = openAiStreamClient.textToSpeech(textToSpeech);
          if (body != null) {
              // 将ResponseBody转换为InputStreamResource
              InputStreamResource resource = new InputStreamResource(body.byteStream());
              // 创建并返回ResponseEntity
              return ResponseEntity.ok()
                      .contentType(MediaType.parseMediaType("audio/mpeg"))
                      .body(resource);
          } else {
              // 如果ResponseBody为空，返回404状态码
              return ResponseEntity.notFound().build();
          }
      }
  
      /**
       * 语音转文字
       */
      @Override
      public WhisperResponse speechToTextTranscriptionsV2(MultipartFile file) {
          // 确保文件不为空
          if (file.isEmpty()) {
              throw new IllegalStateException("Cannot convert an empty MultipartFile");
          }
          if (!FileUtils.isValidFileExtention(file, MimeTypeUtils.AUDIO__EXTENSION)) {
              throw new IllegalStateException("File Extention not supported");
          }
          // 创建一个文件对象
          File fileA = new File(System.getProperty("java.io.tmpdir") + File.separator + file.getOriginalFilename());
          try {
              // 将 MultipartFile 的内容写入文件
              file.transferTo(fileA);
          } catch (IOException e) {
              throw new RuntimeException("Failed to convert MultipartFile to File", e);
          }
          return openAiStreamClient.speechToTextTranscriptions(fileA);
      }
  
  
      @Override
      public UploadFileResponse upload(MultipartFile file) {
          if (file.isEmpty()) {
              throw new IllegalStateException("Cannot upload an empty MultipartFile");
          }
          if (!FileUtils.isValidFileExtention(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION)) {
              throw new IllegalStateException("File Extention not supported");
          }
          return openAiStreamClient.uploadFile("fine-tune", convertMultiPartToFile(file));
      }
  
      private File convertMultiPartToFile(MultipartFile multipartFile) {
          File file = null;
          try {
              // 获取原始文件名
              String originalFileName = multipartFile.getOriginalFilename();
              // 默认扩展名
              String extension = ".tmp";
              // 尝试从原始文件名中获取扩展名
              if (originalFileName != null && originalFileName.contains(".")) {
                  extension = originalFileName.substring(originalFileName.lastIndexOf("."));
              }
  
              // 使用原始文件的扩展名创建临时文件
              Path tempFile = Files.createTempFile(null, extension);
              file = tempFile.toFile();
  
              // 将MultipartFile的内容写入文件
              try (InputStream inputStream = multipartFile.getInputStream();
                   FileOutputStream outputStream = new FileOutputStream(file)) {
                  int read;
                  byte[] bytes = new byte[1024];
                  while ((read = inputStream.read(bytes)) != -1) {
                      outputStream.write(bytes, 0, read);
                  }
              } catch (IOException e) {
                  // 处理文件写入异常
                  e.printStackTrace();
              }
          } catch (IOException e) {
              // 处理临时文件创建异常
              e.printStackTrace();
          }
          return file;
      }
  
  }
  