mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-16 13:23:42 +00:00
feat: 优化会话管理查询逻辑
This commit is contained in:
@@ -29,13 +29,11 @@ public class ChatSessionBo extends BaseEntity {
|
|||||||
/**
|
/**
|
||||||
* 用户id
|
* 用户id
|
||||||
*/
|
*/
|
||||||
@NotNull(message = "用户id不能为空", groups = { AddGroup.class, EditGroup.class })
|
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 会话标题
|
* 会话标题
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "会话标题不能为空", groups = { AddGroup.class, EditGroup.class })
|
|
||||||
private String sessionTitle;
|
private String sessionTitle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,7 +45,6 @@ public class ChatSessionBo extends BaseEntity {
|
|||||||
/**
|
/**
|
||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
|
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.ruoyi.service.impl;
|
|||||||
|
|
||||||
import org.ruoyi.common.core.utils.MapstructUtils;
|
import org.ruoyi.common.core.utils.MapstructUtils;
|
||||||
import org.ruoyi.common.core.utils.StringUtils;
|
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.TableDataInfo;
|
||||||
import org.ruoyi.core.page.PageQuery;
|
import org.ruoyi.core.page.PageQuery;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
@@ -45,6 +46,10 @@ public class ChatMessageServiceImpl implements IChatMessageService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public TableDataInfo<ChatMessageVo> queryPageList(ChatMessageBo bo, PageQuery pageQuery) {
|
public TableDataInfo<ChatMessageVo> queryPageList(ChatMessageBo bo, PageQuery pageQuery) {
|
||||||
|
if(!LoginHelper.isLogin()){
|
||||||
|
return TableDataInfo.build();
|
||||||
|
}
|
||||||
|
bo.setUserId(LoginHelper.getUserId());
|
||||||
LambdaQueryWrapper<ChatMessage> lqw = buildQueryWrapper(bo);
|
LambdaQueryWrapper<ChatMessage> lqw = buildQueryWrapper(bo);
|
||||||
Page<ChatMessageVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
Page<ChatMessageVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||||
return TableDataInfo.build(result);
|
return TableDataInfo.build(result);
|
||||||
@@ -64,9 +69,8 @@ public class ChatMessageServiceImpl implements IChatMessageService {
|
|||||||
LambdaQueryWrapper<ChatMessage> lqw = Wrappers.lambdaQuery();
|
LambdaQueryWrapper<ChatMessage> lqw = Wrappers.lambdaQuery();
|
||||||
lqw.eq(bo.getUserId() != null, ChatMessage::getUserId, bo.getUserId());
|
lqw.eq(bo.getUserId() != null, ChatMessage::getUserId, bo.getUserId());
|
||||||
lqw.eq(StringUtils.isNotBlank(bo.getContent()), ChatMessage::getContent, bo.getContent());
|
lqw.eq(StringUtils.isNotBlank(bo.getContent()), ChatMessage::getContent, bo.getContent());
|
||||||
lqw.eq(StringUtils.isNotBlank(bo.getRole()), ChatMessage::getRole, bo.getRole());
|
lqw.eq(bo.getSessionId() != null, ChatMessage::getSessionId, bo.getSessionId());
|
||||||
lqw.eq(bo.getDeductCost() != null, ChatMessage::getDeductCost, bo.getDeductCost());
|
lqw.like(StringUtils.isNotBlank(bo.getRole()), ChatMessage::getRole, bo.getRole());
|
||||||
lqw.eq(bo.getTotalTokens() != null, ChatMessage::getTotalTokens, bo.getTotalTokens());
|
|
||||||
lqw.like(StringUtils.isNotBlank(bo.getModelName()), ChatMessage::getModelName, bo.getModelName());
|
lqw.like(StringUtils.isNotBlank(bo.getModelName()), ChatMessage::getModelName, bo.getModelName());
|
||||||
return lqw;
|
return lqw;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import jakarta.validation.constraints.*;
|
|||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||||
|
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||||
import org.ruoyi.core.page.TableDataInfo;
|
import org.ruoyi.core.page.TableDataInfo;
|
||||||
import org.ruoyi.domain.bo.ChatSessionBo;
|
import org.ruoyi.domain.bo.ChatSessionBo;
|
||||||
import org.ruoyi.domain.vo.ChatSessionVo;
|
import org.ruoyi.domain.vo.ChatSessionVo;
|
||||||
@@ -42,6 +43,12 @@ public class ChatSessionController extends BaseController {
|
|||||||
@SaCheckPermission("system:session:list")
|
@SaCheckPermission("system:session:list")
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
public TableDataInfo<ChatSessionVo> list(ChatSessionBo bo, PageQuery pageQuery) {
|
public TableDataInfo<ChatSessionVo> list(ChatSessionBo bo, PageQuery pageQuery) {
|
||||||
|
if(!LoginHelper.isLogin()){
|
||||||
|
// 如果用户没有登录,返回空会话列表
|
||||||
|
return TableDataInfo.build();
|
||||||
|
}
|
||||||
|
// 默认查询当前用户会话
|
||||||
|
bo.setUserId(LoginHelper.getUserId());
|
||||||
return chatSessionService.queryPageList(bo, pageQuery);
|
return chatSessionService.queryPageList(bo, pageQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
package org.ruoyi.chat.service.chat.impl;
|
package org.ruoyi.chat.service.chat.impl;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
||||||
import com.google.protobuf.ServiceException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import okhttp3.ResponseBody;
|
import okhttp3.ResponseBody;
|
||||||
import org.ruoyi.chat.enums.ChatModeType;
|
|
||||||
import org.ruoyi.chat.factory.ChatServiceFactory;
|
import org.ruoyi.chat.factory.ChatServiceFactory;
|
||||||
import org.ruoyi.chat.service.chat.IChatCostService;
|
import org.ruoyi.chat.service.chat.IChatCostService;
|
||||||
import org.ruoyi.chat.service.chat.IChatService;
|
import org.ruoyi.chat.service.chat.IChatService;
|
||||||
import org.ruoyi.chat.service.chat.ISseService;
|
import org.ruoyi.chat.service.chat.ISseService;
|
||||||
import org.ruoyi.chat.util.IpUtil;
|
|
||||||
import org.ruoyi.chat.util.SSEUtil;
|
import org.ruoyi.chat.util.SSEUtil;
|
||||||
import org.ruoyi.common.chat.config.LocalCache;
|
import org.ruoyi.common.chat.config.LocalCache;
|
||||||
import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
|
import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
|
||||||
@@ -26,15 +21,14 @@ import org.ruoyi.common.core.utils.DateUtils;
|
|||||||
import org.ruoyi.common.core.utils.StringUtils;
|
import org.ruoyi.common.core.utils.StringUtils;
|
||||||
import org.ruoyi.common.core.utils.file.FileUtils;
|
import org.ruoyi.common.core.utils.file.FileUtils;
|
||||||
import org.ruoyi.common.core.utils.file.MimeTypeUtils;
|
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.ChatSessionBo;
|
||||||
import org.ruoyi.domain.bo.QueryVectorBo;
|
import org.ruoyi.domain.bo.QueryVectorBo;
|
||||||
import org.ruoyi.domain.vo.ChatModelVo;
|
import org.ruoyi.domain.vo.ChatModelVo;
|
||||||
import org.ruoyi.domain.vo.KnowledgeInfoVo;
|
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.IChatModelService;
|
||||||
import org.ruoyi.service.IChatSessionService;
|
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.InputStreamResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -49,10 +43,12 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ageer
|
||||||
|
*/
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -81,24 +77,20 @@ public class SseServiceImpl implements ISseService {
|
|||||||
try {
|
try {
|
||||||
// 构建消息列表
|
// 构建消息列表
|
||||||
buildChatMessageList(chatRequest);
|
buildChatMessageList(chatRequest);
|
||||||
if (!StpUtil.isLogin()) {
|
|
||||||
// 未登录用户限制对话次数
|
LocalCache.CACHE.put("userId", chatCostService.getUserId());
|
||||||
checkUnauthenticatedUserChatLimit(request);
|
chatRequest.setUserId(chatCostService.getUserId());
|
||||||
}else {
|
// 保存会话信息
|
||||||
LocalCache.CACHE.put("userId", chatCostService.getUserId());
|
if(chatRequest.getSessionId()==null){
|
||||||
chatRequest.setUserId(chatCostService.getUserId());
|
ChatSessionBo chatSessionBo = new ChatSessionBo();
|
||||||
// 保存会话信息
|
chatSessionBo.setUserId(chatCostService.getUserId());
|
||||||
if(chatRequest.getSessionId()==null){
|
chatSessionBo.setSessionTitle(getFirst10Characters(chatRequest.getPrompt()));
|
||||||
ChatSessionBo chatSessionBo = new ChatSessionBo();
|
chatSessionBo.setSessionContent(chatRequest.getPrompt());
|
||||||
chatSessionBo.setUserId(chatCostService.getUserId());
|
chatSessionService.insertByBo(chatSessionBo);
|
||||||
chatSessionBo.setSessionTitle(getFirst10Characters(chatRequest.getPrompt()));
|
chatRequest.setSessionId(chatSessionBo.getId());
|
||||||
chatSessionBo.setSessionContent(chatRequest.getPrompt());
|
|
||||||
chatSessionService.insertByBo(chatSessionBo);
|
|
||||||
chatRequest.setSessionId(chatSessionBo.getId());
|
|
||||||
}
|
|
||||||
// 保存消息记录 并扣除费用
|
|
||||||
chatCostService.deductToken(chatRequest);
|
|
||||||
}
|
}
|
||||||
|
// 保存消息记录 并扣除费用
|
||||||
|
chatCostService.deductToken(chatRequest);
|
||||||
// 根据模型分类调用不同的处理逻辑
|
// 根据模型分类调用不同的处理逻辑
|
||||||
IChatService chatService = chatServiceFactory.getChatService(chatModelVo.getCategory());
|
IChatService chatService = chatServiceFactory.getChatService(chatModelVo.getCategory());
|
||||||
chatService.chat(chatRequest, sseEmitter);
|
chatService.chat(chatRequest, sseEmitter);
|
||||||
@@ -126,33 +118,6 @@ public class SseServiceImpl implements ISseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查未登录用户是否超过当日对话次数限制
|
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建消息列表
|
* 构建消息列表
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -330,10 +330,11 @@ public class KnowledgeInfoServiceImpl implements IKnowledgeInfoService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 第一步 定时 拆解PDF文件中的图片
|
* 第一步 定时 拆解PDF文件中的图片
|
||||||
*/
|
*/
|
||||||
@Scheduled(fixedDelay = 15000) // 每3秒执行一次
|
//@Scheduled(fixedDelay = 15000) // 每3秒执行一次
|
||||||
public void dealKnowledgeAttachPic() throws Exception {
|
public void dealKnowledgeAttachPic() throws Exception {
|
||||||
//处理 拆解PDF文件中的图片的记录
|
//处理 拆解PDF文件中的图片的记录
|
||||||
List<KnowledgeAttach> knowledgeAttaches = attachMapper.selectList(
|
List<KnowledgeAttach> knowledgeAttaches = attachMapper.selectList(
|
||||||
@@ -349,10 +350,11 @@ public class KnowledgeInfoServiceImpl implements IKnowledgeInfoService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 第二步 定时 解析图片内容
|
* 第二步 定时 解析图片内容
|
||||||
*/
|
*/
|
||||||
@Scheduled(fixedDelay = 15000)
|
//@Scheduled(fixedDelay = 15000)
|
||||||
public void dealKnowledgeAttachPicAnys() throws Exception {
|
public void dealKnowledgeAttachPicAnys() throws Exception {
|
||||||
//获取未处理的图片记录
|
//获取未处理的图片记录
|
||||||
List<KnowledgeAttachPic> knowledgeAttachPics = picMapper.selectList(
|
List<KnowledgeAttachPic> knowledgeAttachPics = picMapper.selectList(
|
||||||
@@ -369,7 +371,7 @@ public class KnowledgeInfoServiceImpl implements IKnowledgeInfoService {
|
|||||||
/**
|
/**
|
||||||
* 第三步 定时 处理 附件上传后上传向量数据库
|
* 第三步 定时 处理 附件上传后上传向量数据库
|
||||||
*/
|
*/
|
||||||
@Scheduled(fixedDelay = 30000) // 每3秒执行一次
|
//@Scheduled(fixedDelay = 30000) // 每3秒执行一次
|
||||||
public void dealKnowledgeAttachVector() throws Exception {
|
public void dealKnowledgeAttachVector() throws Exception {
|
||||||
//处理 需要上传向量数据库的记录
|
//处理 需要上传向量数据库的记录
|
||||||
List<KnowledgeAttach> knowledgeAttaches = attachMapper.selectList(
|
List<KnowledgeAttach> knowledgeAttaches = attachMapper.selectList(
|
||||||
@@ -388,7 +390,7 @@ public class KnowledgeInfoServiceImpl implements IKnowledgeInfoService {
|
|||||||
/**
|
/**
|
||||||
* 第四步 定时 处理 失败数据
|
* 第四步 定时 处理 失败数据
|
||||||
*/
|
*/
|
||||||
@Scheduled(fixedDelay = 30 * 60 * 1000)
|
//@Scheduled(fixedDelay = 30 * 60 * 1000)
|
||||||
public void dealKnowledge40Status() throws Exception {
|
public void dealKnowledge40Status() throws Exception {
|
||||||
//拆解PDF失败 重新设置状态
|
//拆解PDF失败 重新设置状态
|
||||||
attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>()
|
attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>()
|
||||||
|
|||||||
Reference in New Issue
Block a user