fix(chat): 优化聊天流式接口异常处理与拦截器分发类型过滤
- talk 接口改为 TEXT_EVENT_STREAM 输出并包装 Flux 异常处理,返回 error/done 事件 - 登录与签名校验拦截器忽略 ASYNC/ERROR 分发,防止二次校验导致上下文丢失 - 更新嵌入模型与 Qdrant 配置,保持与线上环境一致
This commit is contained in:
@@ -2,6 +2,7 @@ package com.yolo.keyborad.interceptor;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yolo.keyborad.utils.SignUtils;
|
||||
import jakarta.servlet.DispatcherType;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
@@ -35,6 +36,10 @@ public class SignInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
// 异步/错误二次分发不重复验签,避免 nonce 重放误判
|
||||
if (request.getDispatcherType() != DispatcherType.REQUEST) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String appId = request.getHeader("X-App-Id");
|
||||
String timestamp = request.getHeader("X-Timestamp");
|
||||
|
||||
@@ -57,7 +57,7 @@ public class LLMConfig {
|
||||
this.openAiApi(),
|
||||
MetadataMode.EMBED,
|
||||
OpenAiEmbeddingOptions.builder()
|
||||
.model("text-embedding-v4")
|
||||
.model("qwen/qwen3-embedding-8b")
|
||||
.dimensions(1536)
|
||||
.user("user-6")
|
||||
.build(),
|
||||
|
||||
@@ -12,11 +12,11 @@ import org.springframework.context.annotation.Configuration;
|
||||
@Configuration
|
||||
public class QdrantClientConfig {
|
||||
|
||||
private final String qdrantHost = "b0c7f1ee-0eb9-469e-83e0-654249d9bd04.us-east4-0.gcp.cloud.qdrant.io";
|
||||
private final String qdrantHost = "00044004-d9d2-4705-8b7b-9defab34ca1b.us-west-1-0.aws.cloud.qdrant.io";
|
||||
|
||||
private final Integer qdrantPort = 6334;
|
||||
|
||||
private final String apiKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3MiOiJtIn0.HX_GxjXCrnhw2DQbMnMFzvDeaHbmNpI2tj2hoUjkvVU";
|
||||
private final String apiKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3MiOiJtIn0.M9nnZ14QXQqI3sG8JzRzRwq48x9u5g-4MWF2jnxiOHA";
|
||||
|
||||
@Bean
|
||||
public QdrantClient qdrantClient() {
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
package com.yolo.keyborad.config;
|
||||
|
||||
import cn.dev33.satoken.fun.strategy.SaCorsHandleFunction;
|
||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||
import cn.dev33.satoken.router.SaHttpMethod;
|
||||
import cn.dev33.satoken.router.SaRouter;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.yolo.keyborad.interceptor.SignInterceptor;
|
||||
import jakarta.servlet.DispatcherType;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@@ -36,7 +39,17 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验。
|
||||
registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
|
||||
registry.addInterceptor(new HandlerInterceptor() {
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
// 仅在首次 REQUEST 分发时做登录校验,避免 ASYNC/ERROR 二次分发阶段上下文丢失
|
||||
if (request.getDispatcherType() != DispatcherType.REQUEST) {
|
||||
return true;
|
||||
}
|
||||
StpUtil.checkLogin();
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns(getExcludePaths());
|
||||
appSecretMap.put(appId, appSecret);
|
||||
@@ -48,6 +61,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
return new String[]{
|
||||
// Swagger & Knife4j 相关
|
||||
"/doc.html",
|
||||
"/error",
|
||||
"/webjars/**",
|
||||
"/swagger-resources/**",
|
||||
"/v2/api-docs",
|
||||
@@ -57,7 +71,6 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
"/swagger-ui/**",
|
||||
"/favicon.ico",
|
||||
// 你的其他放行路径,例如登录接口
|
||||
"/error",
|
||||
"/user/appleLogin",
|
||||
"/user/logout",
|
||||
"/tag/list",
|
||||
|
||||
@@ -30,6 +30,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingModel;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.ServerSentEvent;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -90,10 +91,22 @@ public class ChatController {
|
||||
return ResultUtils.success(result);
|
||||
}
|
||||
|
||||
@PostMapping("/talk")
|
||||
@PostMapping(value = "/talk", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
@Operation(summary = "聊天润色接口", description = "聊天润色接口")
|
||||
public Flux<ServerSentEvent<ChatStreamMessage>> talk(@RequestBody ChatReq chatReq){
|
||||
return chatService.talk(chatReq);
|
||||
return Flux.defer(() -> chatService.talk(chatReq))
|
||||
.onErrorResume(e -> {
|
||||
log.error("聊天流式接口异常", e);
|
||||
String message = StrUtil.isBlank(e.getMessage()) ? "服务暂时不可用,请稍后重试" : e.getMessage();
|
||||
return Flux.just(
|
||||
ServerSentEvent.builder(new ChatStreamMessage("error", message))
|
||||
.event("error")
|
||||
.build(),
|
||||
ServerSentEvent.builder(new ChatStreamMessage("done", null))
|
||||
.event("done")
|
||||
.build()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ spring:
|
||||
model: google/gemini-2.5-flash-lite
|
||||
embedding:
|
||||
options:
|
||||
model: text-embedding-v4
|
||||
model: qwen/qwen3-embedding-8b
|
||||
# model: qwen/qwen3-embedding-8b
|
||||
dashscope:
|
||||
api-key: 11
|
||||
|
||||
Reference in New Issue
Block a user