feat(chat): 新增重置会话接口并优化主键策略

- ChatController 增加 /session/reset 端点,支持用户主动重置与 AI 角色的会话
- 会话重置逻辑:将当前活跃会话置为失效,并创建版本号递增的新会话
- 新增 SessionResetReq DTO 与 ChatSessionVO 返回视图
- KeyboardAiChatSession 主键生成策略由 AUTO 改为 ASSIGN_ID,适配分布式场景
This commit is contained in:
2026-01-28 17:56:31 +08:00
parent 234ea0c241
commit ecab353802
5 changed files with 119 additions and 0 deletions

View File

@@ -14,12 +14,15 @@ import com.yolo.keyborad.model.dto.chat.ChatMessageReq;
import com.yolo.keyborad.model.dto.chat.ChatReq; import com.yolo.keyborad.model.dto.chat.ChatReq;
import com.yolo.keyborad.model.dto.chat.ChatSaveReq; import com.yolo.keyborad.model.dto.chat.ChatSaveReq;
import com.yolo.keyborad.model.dto.chat.ChatStreamMessage; import com.yolo.keyborad.model.dto.chat.ChatStreamMessage;
import com.yolo.keyborad.model.dto.chat.SessionResetReq;
import com.yolo.keyborad.model.vo.AudioTaskVO; import com.yolo.keyborad.model.vo.AudioTaskVO;
import com.yolo.keyborad.model.vo.ChatMessageHistoryVO; import com.yolo.keyborad.model.vo.ChatMessageHistoryVO;
import com.yolo.keyborad.model.vo.ChatMessageVO; import com.yolo.keyborad.model.vo.ChatMessageVO;
import com.yolo.keyborad.model.vo.ChatSessionVO;
import com.yolo.keyborad.model.vo.ChatVoiceVO; import com.yolo.keyborad.model.vo.ChatVoiceVO;
import com.yolo.keyborad.service.ChatService; import com.yolo.keyborad.service.ChatService;
import com.yolo.keyborad.service.KeyboardAiChatMessageService; import com.yolo.keyborad.service.KeyboardAiChatMessageService;
import com.yolo.keyborad.service.KeyboardAiChatSessionService;
import com.yolo.keyborad.service.impl.QdrantVectorService; import com.yolo.keyborad.service.impl.QdrantVectorService;
import io.qdrant.client.grpc.JsonWithInt; import io.qdrant.client.grpc.JsonWithInt;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@@ -55,6 +58,9 @@ public class ChatController {
@Resource @Resource
private KeyboardAiChatMessageService aiChatMessageService; private KeyboardAiChatMessageService aiChatMessageService;
@Resource
private KeyboardAiChatSessionService aiChatSessionService;
@PostMapping("/message") @PostMapping("/message")
@Operation(summary = "同步对话", description = "发送消息给大模型,同步返回 AI 响应,异步生成音频") @Operation(summary = "同步对话", description = "发送消息给大模型,同步返回 AI 响应,异步生成音频")
@@ -131,4 +137,24 @@ public class ChatController {
userId, req.getCompanionId(), req.getPageNum(), req.getPageSize()); userId, req.getCompanionId(), req.getPageNum(), req.getPageSize());
return ResultUtils.success(result); return ResultUtils.success(result);
} }
@PostMapping("/session/reset")
@Operation(summary = "重置会话", description = "重置与AI角色的聊天会话将当前会话设为不活跃并创建新会话后续聊天记录将绑定到新会话")
public BaseResponse<ChatSessionVO> resetSession(@RequestBody SessionResetReq req) {
if (req.getCompanionId() == null) {
throw new BusinessException(ErrorCode.COMPANION_ID_EMPTY);
}
Long userId = StpUtil.getLoginIdAsLong();
var newSession = aiChatSessionService.resetSession(userId, req.getCompanionId());
ChatSessionVO vo = ChatSessionVO.builder()
.sessionId(newSession.getId())
.companionId(newSession.getCompanionId())
.resetVersion(newSession.getResetVersion())
.createdAt(newSession.getCreatedAt())
.build();
return ResultUtils.success(vo);
}
} }

View File

@@ -0,0 +1,16 @@
package com.yolo.keyborad.model.dto.chat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/*
* @author: ziin
* @date: 2026/1/28
*/
@Data
@Schema(description = "会话重置请求")
public class SessionResetReq {
@Schema(description = "AI陪聊角色ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long companionId;
}

View File

@@ -0,0 +1,29 @@
package com.yolo.keyborad.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Data;
import java.util.Date;
/*
* @author: ziin
* @date: 2026/1/28
*/
@Data
@Builder
@Schema(description = "会话信息VO")
public class ChatSessionVO {
@Schema(description = "会话ID")
private Long sessionId;
@Schema(description = "AI陪聊角色ID")
private Long companionId;
@Schema(description = "会话版本号")
private Integer resetVersion;
@Schema(description = "会话创建时间")
private Date createdAt;
}

View File

@@ -26,4 +26,13 @@ public interface KeyboardAiChatSessionService extends IService<KeyboardAiChatSes
* @return 活跃会话ID * @return 活跃会话ID
*/ */
Long getOrCreateActiveSessionId(Long userId, Long companionId); Long getOrCreateActiveSessionId(Long userId, Long companionId);
/**
* 重置会话:将当前活跃会话设为不活跃,并创建新的活跃会话
*
* @param userId 用户ID
* @param companionId AI角色ID
* @return 新创建的活跃会话
*/
KeyboardAiChatSession resetSession(Long userId, Long companionId);
} }

View File

@@ -55,4 +55,43 @@ public class KeyboardAiChatSessionServiceImpl extends ServiceImpl<KeyboardAiChat
public Long getOrCreateActiveSessionId(Long userId, Long companionId) { public Long getOrCreateActiveSessionId(Long userId, Long companionId) {
return getOrCreateActiveSession(userId, companionId).getId(); return getOrCreateActiveSession(userId, companionId).getId();
} }
@Override
public KeyboardAiChatSession resetSession(Long userId, Long companionId) {
Date now = new Date();
// 查询当前活跃会话
LambdaQueryWrapper<KeyboardAiChatSession> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(KeyboardAiChatSession::getUserId, userId)
.eq(KeyboardAiChatSession::getCompanionId, companionId)
.eq(KeyboardAiChatSession::getIsActive, true);
KeyboardAiChatSession activeSession = this.getOne(queryWrapper);
// 如果存在活跃会话,将其设为不活跃
if (activeSession != null) {
activeSession.setIsActive(false);
activeSession.setEndedAt(now);
this.updateById(activeSession);
}
// 查询该用户与该角色的最大版本号
LambdaQueryWrapper<KeyboardAiChatSession> maxVersionWrapper = new LambdaQueryWrapper<>();
maxVersionWrapper.eq(KeyboardAiChatSession::getUserId, userId)
.eq(KeyboardAiChatSession::getCompanionId, companionId)
.orderByDesc(KeyboardAiChatSession::getResetVersion)
.last("LIMIT 1");
KeyboardAiChatSession lastSession = this.getOne(maxVersionWrapper);
int newVersion = lastSession != null ? lastSession.getResetVersion() + 1 : 1;
// 创建新的活跃会话
KeyboardAiChatSession newSession = new KeyboardAiChatSession();
newSession.setUserId(userId);
newSession.setCompanionId(companionId);
newSession.setResetVersion(newVersion);
newSession.setIsActive(true);
newSession.setCreatedAt(now);
this.save(newSession);
return newSession;
}
} }