feat(config): 新增异步线程池与WebMvc异步配置并扩展emoji字段支持
This commit is contained in:
@@ -1,10 +1,45 @@
|
|||||||
package com.yolo.keyborad.config;
|
package com.yolo.keyborad.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.scheduling.annotation.EnableAsync;
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步任务配置
|
||||||
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
public class AsyncConfig {
|
public class AsyncConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认异步任务执行器
|
||||||
|
* 用于处理 @Async 注解的异步方法
|
||||||
|
*/
|
||||||
|
@Bean(name = "taskExecutor")
|
||||||
|
public Executor taskExecutor() {
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
|
||||||
|
// 核心线程数
|
||||||
|
executor.setCorePoolSize(10);
|
||||||
|
// 最大线程数
|
||||||
|
executor.setMaxPoolSize(20);
|
||||||
|
// 队列容量
|
||||||
|
executor.setQueueCapacity(200);
|
||||||
|
// 线程名称前缀
|
||||||
|
executor.setThreadNamePrefix("async-task-");
|
||||||
|
// 拒绝策略:由调用线程处理
|
||||||
|
executor.setRejectedExecutionHandler(new java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy());
|
||||||
|
// 线程空闲时间(秒)
|
||||||
|
executor.setKeepAliveSeconds(60);
|
||||||
|
// 等待所有任务结束后再关闭线程池
|
||||||
|
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||||
|
// 等待时间(秒)
|
||||||
|
executor.setAwaitTerminationSeconds(60);
|
||||||
|
|
||||||
|
executor.initialize();
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.yolo.keyborad.config;
|
package com.yolo.keyborad.config;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
|
||||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
@@ -24,4 +26,31 @@ public class CorsConfig implements WebMvcConfigurer {
|
|||||||
.allowedHeaders("*")
|
.allowedHeaders("*")
|
||||||
.exposedHeaders("*");
|
.exposedHeaders("*");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置 WebMvc 异步请求处理的执行器
|
||||||
|
* 用于处理异步 HTTP 请求(如 DeferredResult、Callable 等)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
|
||||||
|
// 核心线程数
|
||||||
|
executor.setCorePoolSize(10);
|
||||||
|
// 最大线程数
|
||||||
|
executor.setMaxPoolSize(30);
|
||||||
|
// 队列容量
|
||||||
|
executor.setQueueCapacity(100);
|
||||||
|
// 线程名称前缀
|
||||||
|
executor.setThreadNamePrefix("mvc-async-");
|
||||||
|
// 线程空闲时间(秒)
|
||||||
|
executor.setKeepAliveSeconds(60);
|
||||||
|
|
||||||
|
executor.initialize();
|
||||||
|
|
||||||
|
// 设置异步请求超时时间(毫秒)
|
||||||
|
configurer.setDefaultTimeout(30000);
|
||||||
|
// 设置异步请求执行器
|
||||||
|
configurer.setTaskExecutor(executor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,9 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
|||||||
"/user/verifyMailCode",
|
"/user/verifyMailCode",
|
||||||
"/character/listWithNotLogin",
|
"/character/listWithNotLogin",
|
||||||
"/character/listByTagWithNotLogin",
|
"/character/listByTagWithNotLogin",
|
||||||
"/character/detailWithNotLogin"
|
"/character/detailWithNotLogin",
|
||||||
|
"/character/addUserCharacter",
|
||||||
|
"/api/apple/validate-receipt"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
package com.yolo.keyborad.config;
|
package com.yolo.keyborad.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@@ -33,4 +37,13 @@ public class SecurityConfig {
|
|||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供一个空的 UserDetailsService,防止 Spring Security 自动配置生成默认密码
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(UserDetailsService.class)
|
||||||
|
public UserDetailsService userDetailsService() {
|
||||||
|
return new InMemoryUserDetailsManager();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.yolo.keyborad.controller;
|
package com.yolo.keyborad.controller;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.yolo.keyborad.common.BaseResponse;
|
import com.yolo.keyborad.common.BaseResponse;
|
||||||
import com.yolo.keyborad.common.ResultUtils;
|
import com.yolo.keyborad.common.ResultUtils;
|
||||||
@@ -18,11 +19,13 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.ai.chat.client.ChatClient;
|
import org.springframework.ai.chat.client.ChatClient;
|
||||||
import org.springframework.ai.embedding.Embedding;
|
import org.springframework.ai.embedding.Embedding;
|
||||||
import org.springframework.ai.embedding.EmbeddingResponse;
|
import org.springframework.ai.embedding.EmbeddingResponse;
|
||||||
|
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||||
import org.springframework.ai.openai.OpenAiEmbeddingModel;
|
import org.springframework.ai.openai.OpenAiEmbeddingModel;
|
||||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -59,15 +62,34 @@ public class DemoController {
|
|||||||
@Operation(summary = "测试聊天接口", description = "测试接口")
|
@Operation(summary = "测试聊天接口", description = "测试接口")
|
||||||
@Parameter(name = "userInput",required = true,description = "测试聊天接口",example = "talk to something")
|
@Parameter(name = "userInput",required = true,description = "测试聊天接口",example = "talk to something")
|
||||||
public Flux<String> testTalk(@DefaultValue("you are so cute!") String userInput){
|
public Flux<String> testTalk(@DefaultValue("you are so cute!") String userInput){
|
||||||
|
String delimiter = "/t";
|
||||||
|
|
||||||
return client
|
return client
|
||||||
.prompt("You're a 25-year-old guy—witty and laid-back, always replying in English.\n" +
|
.prompt("""
|
||||||
"Read the user's last message, then write three short and funny replies that sound like something a guy would say. Go easy on the emojis.\n" +
|
You're a 25-year-old guy—witty and laid-back, always replying in English.
|
||||||
"Keep each under 20 words. Use '/t' to separate them—no numbers or extra labels.")
|
|
||||||
|
Read the user's last message, then write three short and funny replies that sound like something a guy would say.
|
||||||
|
Go easy on the emojis.
|
||||||
|
Keep each under 20 words.
|
||||||
|
|
||||||
|
User message: %s
|
||||||
|
""".formatted(userInput))
|
||||||
|
.system("""
|
||||||
|
Format rules (very important):
|
||||||
|
- Return EXACTLY 3 replies.
|
||||||
|
- Use "<SPLIT>" as the separator between replies.
|
||||||
|
- Output format: reply1<SPLIT>reply2<SPLIT>reply3
|
||||||
|
- Do NOT use "<SPLIT>" inside any reply.
|
||||||
|
""")
|
||||||
.user(userInput)
|
.user(userInput)
|
||||||
|
.options(OpenAiChatOptions.builder()
|
||||||
|
.user(StpUtil.getLoginIdAsString()) // ✅ 这里每次请求都会重新取当前登录用户
|
||||||
|
.build())
|
||||||
.stream()
|
.stream()
|
||||||
.content();
|
.content();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/embed")
|
@PostMapping("/embed")
|
||||||
@Operation(summary = "测试向量接口", description = "测试向量接口")
|
@Operation(summary = "测试向量接口", description = "测试向量接口")
|
||||||
@Parameter(name = "userInput",required = true,description = "测试向量接口",example = "you are so cute!")
|
@Parameter(name = "userInput",required = true,description = "测试向量接口",example = "you are so cute!")
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.yolo.keyborad.model.dto.userCharacter;
|
package com.yolo.keyborad.model.dto.userCharacter;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -12,4 +13,6 @@ public class KeyboardUserCharacterAddDTO {
|
|||||||
@Schema(description="键盘人设 id")
|
@Schema(description="键盘人设 id")
|
||||||
private Long characterId;
|
private Long characterId;
|
||||||
|
|
||||||
|
@Schema(description = "emoji")
|
||||||
|
private String emoji;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.yolo.keyborad.model.dto.userCharacter;
|
package com.yolo.keyborad.model.dto.userCharacter;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -25,4 +26,8 @@ public class KeyboardUserCharacterDTO {
|
|||||||
|
|
||||||
@Schema(description = "是否删除")
|
@Schema(description = "是否删除")
|
||||||
private Boolean deleted;
|
private Boolean deleted;
|
||||||
|
|
||||||
|
@TableField(value = "emoji")
|
||||||
|
@Schema(description = "emoji")
|
||||||
|
private String emoji;
|
||||||
}
|
}
|
||||||
@@ -60,4 +60,8 @@ public class KeyboardCharacter {
|
|||||||
@TableField(value = "\"rank\"")
|
@TableField(value = "\"rank\"")
|
||||||
@Schema(description="排名顺序")
|
@Schema(description="排名顺序")
|
||||||
private Integer rank;
|
private Integer rank;
|
||||||
|
|
||||||
|
@TableField(value = "emoji")
|
||||||
|
@Schema(description = "emoji")
|
||||||
|
private String emoji;
|
||||||
}
|
}
|
||||||
@@ -40,4 +40,7 @@ public class KeyboardCharacterRespVO {
|
|||||||
|
|
||||||
@Schema(description = "是否添加")
|
@Schema(description = "是否添加")
|
||||||
private Boolean added;
|
private Boolean added;
|
||||||
|
|
||||||
|
@Schema(description = "emoji")
|
||||||
|
private String emoji;
|
||||||
}
|
}
|
||||||
@@ -14,9 +14,10 @@
|
|||||||
<result column="updated_at" jdbcType="TIMESTAMP" property="updatedAt" />
|
<result column="updated_at" jdbcType="TIMESTAMP" property="updatedAt" />
|
||||||
<result column="prompt" jdbcType="VARCHAR" property="prompt" />
|
<result column="prompt" jdbcType="VARCHAR" property="prompt" />
|
||||||
<result column="rank" jdbcType="INTEGER" property="rank" />
|
<result column="rank" jdbcType="INTEGER" property="rank" />
|
||||||
|
<result column="emoji" jdbcType="BOOLEAN" property="emoji" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
id, character_name, "character_background", avatar_url, download, tag, deleted, created_at,
|
id, character_name, "character_background", avatar_url, download, tag, deleted, created_at,
|
||||||
updated_at, prompt, "rank"
|
updated_at, prompt, "rank",emoji
|
||||||
</sql>
|
</sql>
|
||||||
</mapper>
|
</mapper>
|
||||||
Reference in New Issue
Block a user