在上次设计好实现思路与搭好整体实现框架后,目前实现了websocket+redis存储的核心逻辑。
首先声明建立存储房间:
private static StringRedisTemplate redisTemplateUserRoom;
@Resource
public void setRedisTemplateUserRoom(StringRedisTemplate redisTemplateUserRoom){WebSocketServer.redisTemplateUserRoom = redisTemplateUserRoom;}
由于websocket是双工通讯,需要建立发送确认机制,避免丢包;发送方为用户与AI进行对话,因此需要指定AI的Id:
private String AI = "123";
private boolean isAckSay = true;
private boolean isAckCorr = true;
private long startTimeSay;
private long startTimeCorr;
当用户向客户端发送消息后,会调用OnMessage函数,后端向大模型发送请求,获得对话,并将其存在redis房间中,加入消息确认功能逻辑:
@OnMessage
public void onMessage(String message, Session session){
//TODO: 用户向Websocket客户端发送消息,会调用该函数
//TODO: ack#say#{AI消息序号},用户向Websocket客户端发送消息,会调用该函数
String[] request;
request = message.split("#");
//后续存储在MySQL里的时候,只需要判断dialog的sender是否为AI就可以吧
if(request[0].equals("ack")){
if(request[1].equals("say")){
//TODO:格式ack#say#{AI消息序号} 发过去的回应被收到了
isAckSay = true;//接收到了ack才能继续
}else if(request[1].equals("corr")){
isAckCorr = true;
}else{
throw new RuntimeException();
}
}else if(request[0].equals("say")&&isAckSay&&isAckCorr){
//格式say#{用户消息序号}#{用户回应},用户传来文本,需要调用大模型
//TODO:先把用户的文本存一个dialog
redisStor(userId,userId,request[2]);
//收到用户的say后向用户回确认
String messageToUser = "ack#say#{用户消息序号}";
sendMessageTo(userId,messageToUser);
//TODO:存储大模型纠错作为一个dialog
//纠错我就理解为对一个dialog纠错了
String content = modelTestCorr(request[2]);
redisStor(userId,AI,content);
messageToUser = "corr#{用户消息序号}#"+content;
isAckCorr = false;
sendMessageTo(userId,messageToUser);
startTimeCorr = System.currentTimeMillis();
while(!isAckCorr&&((System.currentTimeMillis()-startTimeCorr)/1000)>5){
//当没收到ack并且已经过了5秒了,超时重传
sendMessageTo(userId,messageToUser);
startTimeCorr = System.currentTimeMillis();
}
//TODO:存储大模型说的话返回结果为一个dialog
//这里实际上要把redis中的数据拿出来给大模型,是否为直接转成字符串?
//需要什么格式,下面仅是个测试
content = modelTestSay("AI回复");
redisStor(userId,AI,content);
//向用户发送AI回复
messageToUser = "say#{AI消息序号}#"+content;
sendMessageTo(userId,messageToUser);
isAckSay = false;
startTimeSay = System.currentTimeMillis();
while(!isAckSay&&((System.currentTimeMillis()-startTimeSay)/1000)>5){
//当没收到ack并且已经过了5秒了
sendMessageTo(userId,messageToUser);
startTimeSay = System.currentTimeMillis();
}
}
if(!StringUtil.isNullOrEmpty(message)){
logger.info("收到id为" + userId + "的用户发来消息:" + message);
}
}
Redis存储逻辑:
private void redisStor(String userId,String senderId,String content){
Date currentTimeUser = new Date();
Dialog dialogUser = new Dialog();
User sender = new User();
Optional <User> uop = userRepository.findUserById(Long.parseLong(senderId));
if(uop.isEmpty()){
sendMessage("用户不存在"); //这不能不存在吧,token都找到了,需要异常处理吗?
}else{
sender = uop.get();
}
boolean keyExists = redisTemplateUserRoom.hasKey(userId);
dialogUser.setSender(sender);
dialogUser.setContent(content);
dialogUser.setTime(currentTimeUser);
//没有userId这个键值,会自己创建一个空的吧
RedisUtils.add(userId,dialogUser,redisTemplateUserRoom);
}
连接断开后,将redis里的内容转存mysql数据库逻辑:
//断开连接后,会把数据存到数据库
List<Dialog> dialogs = RedisUtils.getList(userId,redisTemplateUserRoom,Dialog.class);
//将dialog存入数据库
dialogRepository.saveAll(dialogs);
TrainingData trainingData = new TrainingData();
trainingData.setUser(userRepository.findUserById(Long.parseLong(userId)).get());
trainingData.setDialogs(dialogs);
//调用接口,获取score
//将trainingData存入数据库
trainingDataRepository.save(trainingData);
redisTemplateUserRoom.delete(userId);