办学质量监测教学评价系统
ageer
2025-05-16 158a0190b5a7b93c0d31541c1e350e5221519fb1
ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/ChatSessionBo.java
@@ -29,13 +29,11 @@
    /**
     * 用户id
     */
    @NotNull(message = "用户id不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long userId;
    /**
     * 会话标题
     */
    @NotBlank(message = "会话标题不能为空", groups = { AddGroup.class, EditGroup.class })
    private String sessionTitle;
    /**
@@ -47,7 +45,6 @@
    /**
     * 备注
     */
    @NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
    private String remark;
ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatMessageServiceImpl.java
@@ -2,6 +2,7 @@
import org.ruoyi.common.core.utils.MapstructUtils;
import org.ruoyi.common.core.utils.StringUtils;
import org.ruoyi.common.satoken.utils.LoginHelper;
import org.ruoyi.core.page.TableDataInfo;
import org.ruoyi.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -45,6 +46,10 @@
     */
    @Override
    public TableDataInfo<ChatMessageVo> queryPageList(ChatMessageBo bo, PageQuery pageQuery) {
        if(!LoginHelper.isLogin()){
            return TableDataInfo.build();
        }
        bo.setUserId(LoginHelper.getUserId());
        LambdaQueryWrapper<ChatMessage> lqw = buildQueryWrapper(bo);
        Page<ChatMessageVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
@@ -64,9 +69,8 @@
        LambdaQueryWrapper<ChatMessage> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getUserId() != null, ChatMessage::getUserId, bo.getUserId());
        lqw.eq(StringUtils.isNotBlank(bo.getContent()), ChatMessage::getContent, bo.getContent());
        lqw.eq(StringUtils.isNotBlank(bo.getRole()), ChatMessage::getRole, bo.getRole());
        lqw.eq(bo.getDeductCost() != null, ChatMessage::getDeductCost, bo.getDeductCost());
        lqw.eq(bo.getTotalTokens() != null, ChatMessage::getTotalTokens, bo.getTotalTokens());
        lqw.eq(bo.getSessionId() != null, ChatMessage::getSessionId, bo.getSessionId());
        lqw.like(StringUtils.isNotBlank(bo.getRole()), ChatMessage::getRole, bo.getRole());
        lqw.like(StringUtils.isNotBlank(bo.getModelName()), ChatMessage::getModelName, bo.getModelName());
        return lqw;
    }
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/chat/ChatSessionController.java
@@ -8,6 +8,7 @@
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.ruoyi.common.excel.utils.ExcelUtil;
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
import org.ruoyi.common.satoken.utils.LoginHelper;
import org.ruoyi.core.page.TableDataInfo;
import org.ruoyi.domain.bo.ChatSessionBo;
import org.ruoyi.domain.vo.ChatSessionVo;
@@ -42,6 +43,12 @@
    @SaCheckPermission("system:session:list")
    @GetMapping("/list")
    public TableDataInfo<ChatSessionVo> list(ChatSessionBo bo, PageQuery pageQuery) {
        if(!LoginHelper.isLogin()){
           // 如果用户没有登录,返回空会话列表
           return TableDataInfo.build();
        }
        // 默认查询当前用户会话
        bo.setUserId(LoginHelper.getUserId());
        return chatSessionService.queryPageList(bo, pageQuery);
    }
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java
@@ -1,19 +1,14 @@
package org.ruoyi.chat.service.chat.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.protobuf.ServiceException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.ResponseBody;
import org.ruoyi.chat.enums.ChatModeType;
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.util.IpUtil;
import org.ruoyi.chat.util.SSEUtil;
import org.ruoyi.common.chat.config.LocalCache;
import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
@@ -26,15 +21,14 @@
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.redis.utils.RedisUtils;
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.service.IKnowledgeInfoService;
import org.ruoyi.service.VectorStoreService;
import org.ruoyi.service.IChatModelService;
import org.ruoyi.service.IChatSessionService;
import org.ruoyi.service.IKnowledgeInfoService;
import org.ruoyi.service.VectorStoreService;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
@@ -49,10 +43,12 @@
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
/**
 * @author ageer
 */
@Service
@Slf4j
@RequiredArgsConstructor
@@ -81,24 +77,20 @@
        try {
            // 构建消息列表
            buildChatMessageList(chatRequest);
            if (!StpUtil.isLogin()) {
                // 未登录用户限制对话次数
                checkUnauthenticatedUserChatLimit(request);
            }else {
                LocalCache.CACHE.put("userId", chatCostService.getUserId());
                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.deductToken(chatRequest);
            LocalCache.CACHE.put("userId", chatCostService.getUserId());
            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.deductToken(chatRequest);
            // 根据模型分类调用不同的处理逻辑
            IChatService chatService = chatServiceFactory.getChatService(chatModelVo.getCategory());
            chatService.chat(chatRequest, sseEmitter);
@@ -124,33 +116,6 @@
            // 如果长度不足10,返回整个字符串
            return str;
        }
    }
    /**
     * 检查未登录用户是否超过当日对话次数限制
     *
     * @param request 当前请求
     * @throws ServiceException 如果当日免费次数已用完
     */
    public void checkUnauthenticatedUserChatLimit(HttpServletRequest request) throws ServiceException {
            String clientIp = IpUtil.getClientIp(request);
            // 访客每天默认只能对话5次
            int timeWindowInSeconds = 5;
            String redisKey = "clientIp:" + clientIp;
            int count = 0;
            // 检查Redis中的对话次数
            if (RedisUtils.getCacheObject(redisKey) == null) {
                // 缓存有效时间1天
                RedisUtils.setCacheObject(redisKey, count, Duration.ofSeconds(86400));
            } else {
                count = RedisUtils.getCacheObject(redisKey);
                if (count >= timeWindowInSeconds) {
                    throw new ServiceException("当日免费次数已用完");
                }
                count++;
                RedisUtils.setCacheObject(redisKey, count);
            }
    }
    /**
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java
@@ -330,10 +330,11 @@
      }
    }
  }
  /**
   * 第一步 定时 拆解PDF文件中的图片
   */
  @Scheduled(fixedDelay = 15000) // 每3秒执行一次
  //@Scheduled(fixedDelay = 15000) // 每3秒执行一次
  public void dealKnowledgeAttachPic() throws Exception {
    //处理 拆解PDF文件中的图片的记录
    List<KnowledgeAttach> knowledgeAttaches = attachMapper.selectList(
@@ -349,10 +350,11 @@
      }
    }
  }
  /**
   * 第二步 定时 解析图片内容
   */
  @Scheduled(fixedDelay = 15000)
  //@Scheduled(fixedDelay = 15000)
  public void dealKnowledgeAttachPicAnys() throws Exception {
    //获取未处理的图片记录
    List<KnowledgeAttachPic> knowledgeAttachPics = picMapper.selectList(
@@ -369,7 +371,7 @@
  /**
   * 第三步 定时 处理 附件上传后上传向量数据库
   */
  @Scheduled(fixedDelay = 30000) // 每3秒执行一次
  //@Scheduled(fixedDelay = 30000) // 每3秒执行一次
  public void dealKnowledgeAttachVector() throws Exception {
    //处理 需要上传向量数据库的记录
    List<KnowledgeAttach> knowledgeAttaches = attachMapper.selectList(
@@ -388,7 +390,7 @@
  /**
   * 第四步 定时 处理 失败数据
   */
  @Scheduled(fixedDelay = 30 * 60 * 1000)
  //@Scheduled(fixedDelay = 30 * 60 * 1000)
  public void dealKnowledge40Status() throws Exception {
      //拆解PDF失败 重新设置状态
      attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>()