first commit

This commit is contained in:
liukai
2025-09-03 15:08:39 +08:00
commit a02be6d1ea
20 changed files with 1196 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
package com.ai.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringAiAppApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiAppApplication.class, args);
}
}

View File

@@ -0,0 +1,79 @@
package com.ai.app.config;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @describe ai配置类
* @Author kai.liu
* @Date 2025/8/28 10:02
*/
@Configuration
public class AIConfig {
@Bean
public InMemoryChatMemoryRepository inMemoryChatMemoryRepository() {
return new InMemoryChatMemoryRepository();
}
@Bean
public MessageWindowChatMemory messageWindowChatMemory(InMemoryChatMemoryRepository inMemoryChatMemoryRepository) {
return MessageWindowChatMemory.builder().chatMemoryRepository(inMemoryChatMemoryRepository).build();
}
/**
* 配置会话记忆环绕增强Advisor
*
* @param messageWindowChatMemory 消息窗口会话记忆
* @return: org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor
* @author kai.liu
* @date: 2025/8/29 9:05
*/
@Bean
public MessageChatMemoryAdvisor messageChatMemoryAdvisor(MessageWindowChatMemory messageWindowChatMemory) {
return MessageChatMemoryAdvisor.builder(messageWindowChatMemory).build();
}
/**
* 配置日志环绕增强Advisor
*
* @return: org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor
* @author kai.liu
* @date: 2025/8/29 9:05
*/
@Bean
public SimpleLoggerAdvisor simpleLoggerAdvisor() {
return new SimpleLoggerAdvisor();
}
/**
* 配置AI对话客户端
*
* @param deepSeekChatModel deepseek大模型
* @param simpleLoggerAdvisor 日志Advisor
* @param messageChatMemoryAdvisor 会话记忆Advisor
* @return: org.springframework.ai.chat.client.ChatClient
* @author kai.liu
* @date: 2025/8/29 9:06
*/
@Bean
public ChatClient chatClient(DeepSeekChatModel deepSeekChatModel,
SimpleLoggerAdvisor simpleLoggerAdvisor,
MessageChatMemoryAdvisor messageChatMemoryAdvisor) {
return ChatClient
//模型
.builder(deepSeekChatModel)
//系统角色
.defaultSystem("你是一个小团团")
//环绕增强
.defaultAdvisors(simpleLoggerAdvisor, messageChatMemoryAdvisor)
.build();
}
}

View File

@@ -0,0 +1,30 @@
package com.ai.app.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 跨域配置
*
* @author shiyong
* 2022/10/24 16:23
*/
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("*")
.maxAge(3600);
}
};
}
}

View File

@@ -0,0 +1,41 @@
package com.ai.app.config;
import com.ai.app.service.AIChatMessageService;
import org.springframework.ai.chat.memory.ChatMemoryRepository;
import org.springframework.ai.chat.messages.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @describe
* @Author kai.liu
* @Date 2025/8/29 9:01
*/
@Component
public class RedisChatMemoryRepositor implements ChatMemoryRepository {
@Autowired
private AIChatMessageService aiChatMessageService;
@Override
public List<String> findConversationIds() {
return aiChatMessageService.getConversationIds();
}
@Override
public List<Message> findByConversationId(String conversationId) {
return aiChatMessageService.getMessage(conversationId);
}
@Override
public void saveAll(String conversationId, List<Message> messages) {
aiChatMessageService.saveAll(conversationId, messages);
}
@Override
public void deleteByConversationId(String conversationId) {
aiChatMessageService.delete(conversationId);
}
}

View File

@@ -0,0 +1,40 @@
package com.ai.app.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @describe
* @Author kai.liu
* @Date 2025/9/1 9:33
*/
@Configuration
public class RedisConfig {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
RedisObjectSerializer redisObjectSerializer = new RedisObjectSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用字节数组
template.setValueSerializer(redisObjectSerializer);
// hash的value序列化方式采用字节数组
template.setHashValueSerializer(redisObjectSerializer);
template.afterPropertiesSet();
return template;
}
}

View File

@@ -0,0 +1,49 @@
package com.ai.app.config;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.serializer.support.DeserializingConverter;
import org.springframework.core.serializer.support.SerializingConverter;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
/**
* 自定义Redis序列化类
*
* @author shiyong
* 2020-01-03 18:01
*/
public class RedisObjectSerializer implements RedisSerializer<Object> {
private Converter<Object, byte[]> serializer = new SerializingConverter();
private Converter<byte[], Object> deserializer = new DeserializingConverter();
static final byte[] EMPTY_ARRAY = new byte[0];
/**
* Serialize the given object to binary data.
*
* @param o object to serialize. Can be {@literal null}.
* @return the equivalent binary data. Can be {@literal null}.
*/
@Override
public byte[] serialize(Object o) throws SerializationException {
if (o == null) {
return EMPTY_ARRAY;
}
return serializer.convert(o);
}
/**
* Deserialize an object from the given binary data.
*
* @param bytes object binary representation. Can be {@literal null}.
* @return the equivalent object instance. Can be {@literal null}.
*/
@Override
public Object deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
return deserializer.convert(bytes);
}
}

View File

@@ -0,0 +1,55 @@
package com.ai.app.controller;
import com.ai.app.dto.MessageDTO;
import com.ai.app.service.AIChatMessageService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.messages.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.List;
import java.util.stream.Collectors;
/**
* @describe AI对话控制器
* @Author kai.liu
* @Date 2025/8/28 10:06
*/
@RestController
@RequestMapping("/ai")
public class ChatController {
@Autowired
private ChatClient chatClient;
@Autowired
private InMemoryChatMemoryRepository inMemoryChatMemoryRepository;
@RequestMapping(value = "/chat", produces = "text/stream;charset=UTF-8")
public Flux<String> chat(String prompt, String chatId) {
return chatClient
.prompt()
.user(prompt)
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
.stream().content();
}
@RequestMapping("/history/chat")
public List<String> history() {
return inMemoryChatMemoryRepository.findConversationIds();
}
@RequestMapping("/history/chat/{chatId}")
public List<MessageDTO> history(@PathVariable("chatId") String chatId) {
List<Message> messages = inMemoryChatMemoryRepository.findByConversationId(chatId);
return messages.stream().map(e -> {
return new MessageDTO(e.getMessageType().getValue(), e.getText());
}).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,23 @@
package com.ai.app.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import java.io.Serializable;
/**
* @describe
* @Author kai.liu
* @Date 2025/8/29 15:09
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MessageDTO implements Serializable {
private String role;
private String content;
}

View File

@@ -0,0 +1,30 @@
package com.ai.app.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
/**
* @describe
* @Author kai.liu
* @Date 2025/8/29 9:20
*/
@Data
@TableName("ai_chat_message")
public class AIChatMessage {
@TableId
private String id;
private String conversationId;
private String conversationType;
private String messageType;
private String message;
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,29 @@
package com.ai.app.mapper;
import com.ai.app.entity.AIChatMessage;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @describe
* @Author kai.liu
* @Date 2025/8/29 9:18
*/
@Mapper
public interface AIChatMessageMapper extends BaseMapper<AIChatMessage> {
@Select("SELECT DISTINCT conversation_id FROM ai_chat_message ")
List<String> selectConversationIds();
@Select("SELECT * FROM ai_chat_message WHERE conversation_id = #{conversationId}")
List<AIChatMessage> selectByConversationId(String conversationId);
void batchInsert(List<AIChatMessage> messageList);
@Select("DELETE FROM ai_chat_message WHERE conversation_id = #{conversationId}")
void deleteByConversationId(String conversationId);
}

View File

@@ -0,0 +1,23 @@
package com.ai.app.service;
import org.springframework.ai.chat.messages.Message;
import java.util.List;
/**
* @describe
* @Author kai.liu
* @Date 2025/8/29 9:27
*/
public interface AIChatMessageService {
List<String> getConversationIds(String type);
List<String> getConversationIds();
List<Message> getMessage(String conversationId);
void saveAll(String conversationId, List<Message> messages);
void delete(String conversationId);
}

View File

@@ -0,0 +1,192 @@
package com.ai.app.service;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* @describe
* @Author kai.liu
* @Date 2025/9/1 9:28
*/
public interface RedisTemplateService {
/**
* 缓存对象信息
*
* @param key 键
* @param value 值
* @author 施勇
* 2018年12月11日 上午10:54:49
*/
void set(String key, Object value);
/**
* 缓存信息,并设置缓存时间
*
* @param key 键
* @param value 值
* @param time 时间
* @param timeUnit 时间单位
* @author 施勇
* 2018年12月11日 上午10:54:27
*/
void set(String key, Object value, long time, TimeUnit timeUnit);
/**
* 原子递增1
*
* @param key 键
* @return java.lang.Long
*/
Long increment(String key);
/**
* 原子递增
*
* @param key 键
* @param delta 增量
* @return java.lang.Long
*/
Long increment(String key, Long delta);
/**
* 递增
*
* @param key 键
* @param delta 递增因子
* @param time 时间
* @param timeUnit 时间单位
* @return java.lang.Long
* @author shiyong
* 2022/10/18 14:58
*/
Long increment(String key, Long delta, long time, TimeUnit timeUnit);
/**
* 从缓存中获取信息
*
* @param key 键
* @return Object
* @author 施勇
* 2018年12月11日 上午10:54:10
*/
Object get(Object key);
/**
* 从redis中获取key对应的过期时间;
* 如果该值有过期时间,就返回相应的过期时间;
* 如果该值没有设置过期时间,就返回-1;
* 如果没有该值,就返回-2;
*/
Long getExpire(String key);
/**
* 设置key的失效时间
*
* @param key 键
* @param time 时长
* @param timeUnit 时间单位
* @return java.lang.Boolean
*/
Boolean expire(String key, long time, TimeUnit timeUnit);
/**
* 是否存在key
*
* @param key 键
* @return boolean
* @author 施勇
* 2018年12月11日 上午10:53:54
*/
boolean hasKey(String key);
/**
* 删除KEY对应的对象
*
* @param key 键
* @author 施勇
* 2018年12月24日 下午4:14:26
*/
void delete(String key);
/**
* 查找匹配的key
*
* @param pattern 匹配字符串
* @return java.util.Set<java.lang.String>
* @author shiyong
* 2020/1/3 13:42
*/
Set<String> getKeys(String pattern);
/**
* 在队尾追加元素
*
* @param key 键
* @param value 值
* @return 添加后队列的长度
*/
Long rightPush(String key, Object value);
/**
* 从队首移除元素
*
* @param key 键
* @return 移除的元素
*/
Object leftPop(String key);
/**
* 从队首开始取值,获取指定区间
*
* @param key 键
* @param start 开始下标
* @param stop 结束下标
* @return 区间内的全部值
*/
List<?> leftRange(String key, long start, long stop);
/**
* 将值加到集合中
*
* @param key 键
* @param values 值
* @return 添加是否成功1成功0失败
*/
Long addToSet(String key, Object... values);
/**
* 集合的数量
*
* @param key 键
* @return 集合数量
*/
Long sizeOfSet(String key);
/**
* 集合列表
*
* @param key 键
* @return 集合列表
*/
Set<Object> getOfSet(String key);
/**
* 集合中是否有该值
*
* @param key 键
* @param value 值
* @return Boolean
*/
Boolean hasOfSet(String key, Object value);
/**
* 删除集合中值
*
* @param key 键
* @param value 值
* @return 删除是否成功1成功0失败
*/
Long removeOfSet(String key, Object value);
}

View File

@@ -0,0 +1,72 @@
package com.ai.app.service.impl;
import com.ai.app.service.AIChatMessageService;
import com.ai.app.service.RedisTemplateService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.messages.Message;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @describe
* @Author kai.liu
* @Date 2025/8/29 9:27
*/
@Slf4j
@Service
public class AIChatMessageServiceImpl implements AIChatMessageService {
@Resource
private RedisTemplateService redisTemplateService;
public static final String AI_MESSAGE_CONVERSATION_ID = "ai:message:conversationId:";
public static final String AI_MESSAGE_CONVERSATION_TYPE = "ai:message:conversationType:";
@Override
public List<String> getConversationIds(String type) {
String conversationTypeKey = getConversationTypeKey(type);
if (redisTemplateService.hasKey(conversationTypeKey)) {
return (List<String>) redisTemplateService.get(conversationTypeKey);
}
return List.of();
}
@Override
public List<String> getConversationIds() {
Set<String> keys = redisTemplateService.getKeys(AI_MESSAGE_CONVERSATION_ID);
return new ArrayList<>(keys);
}
@Override
public List<Message> getMessage(String conversationId) {
String conversationIdKey = getConversationIdKey(conversationId);
if (redisTemplateService.hasKey(conversationIdKey)) {
return (List<Message>) redisTemplateService.get(conversationIdKey);
}
return List.of();
}
@Override
public void saveAll(String conversationId, List<Message> messages) {
String conversationIdKey = getConversationIdKey(conversationId);
redisTemplateService.set(conversationIdKey, messages);
}
@Override
public void delete(String conversationId) {
String conversationIdKey = getConversationIdKey(conversationId);
redisTemplateService.delete(conversationIdKey);
}
private String getConversationIdKey(String conversationId) {
return AI_MESSAGE_CONVERSATION_ID + conversationId;
}
private String getConversationTypeKey(String type) {
return AI_MESSAGE_CONVERSATION_TYPE + type;
}
}

View File

@@ -0,0 +1,256 @@
package com.ai.app.service.impl;
import com.ai.app.service.RedisTemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* @describe
* @Author kai.liu
* @Date 2025/9/1 9:28
*/
@Service
@Slf4j
public class RedisTemplateServiceImpl implements RedisTemplateService {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
/**
* 缓存对象信息
* @param key 键
* @param value 值
* @author 施勇
* 2018年12月11日 上午10:54:49
*/
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 缓存信息,并设置缓存时间
* @param key 键
* @param value 值
* @param time 时间
* @param timeUnit 时间单位
* @author 施勇
* 2018年12月11日 上午10:54:27
*/
public void set(String key, Object value, long time, TimeUnit timeUnit){
if(time>0){
redisTemplate.opsForValue().set(key, value, time, timeUnit);
}else{
set(key, value);
}
}
/**
* 原子递增1
*
* @param key 键
* @return java.lang.Long
*/
public Long increment(String key) {
return redisTemplate.opsForValue().increment(key);
}
/**
* 原子递增
*
* @param key 键
* @param delta 增量
* @return java.lang.Long
*/
public Long increment(String key, Long delta) {
if (delta == null || delta < 1) {
delta = 1L;
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递增
*
* @param key 键
* @param delta 递增因子
* @param time 时间
* @param timeUnit 时间单位
* @return java.lang.Long
* @author shiyong
* 2022/10/18 14:58
*/
public Long increment(String key, Long delta, long time, TimeUnit timeUnit) {
if (null == delta || delta < 1) {
delta = 1L;
}
Long increment = redisTemplate.opsForValue().increment(key, delta);
redisTemplate.expire(key, time, timeUnit);
return increment;
}
/**
* 从缓存中获取信息
* @param key 键
* @return Object
* @author 施勇
* 2018年12月11日 上午10:54:10
*/
public Object get(Object key) {
return key==null?"":redisTemplate.opsForValue().get(key);
}
/**
* 从redis中获取key对应的过期时间;
* 如果该值有过期时间,就返回相应的过期时间;
* 如果该值没有设置过期时间,就返回-1;
* 如果没有该值,就返回-2;
*/
public Long getExpire(String key) {
return redisTemplate.opsForValue().getOperations().getExpire(key);
}
/**
* 设置key的失效时间
*
* @param key 键
* @param time 时长
* @param timeUnit 时间单位
* @return java.lang.Boolean
*/
public Boolean expire(String key, long time, TimeUnit timeUnit) {
return redisTemplate.expire(key, time, timeUnit);
}
/**
* 是否存在key
* @param key 键
* @return boolean
* @author 施勇
* 2018年12月11日 上午10:53:54
*/
public boolean hasKey(String key) {
if (null == key || "".equals(key)) {
return false;
}
Boolean result = redisTemplate.hasKey(key);
if (null == result) {
return false;
} else {
return result;
}
}
/**
* 删除KEY对应的对象
* @param key 键
* @author 施勇
* 2018年12月24日 下午4:14:26
*/
public void delete(String key){
redisTemplate.delete(key);
}
/**
* 查找匹配的key
* @param pattern 匹配字符串
* @return java.util.Set<java.lang.String>
* @author shiyong
* 2020/1/3 13:42
*/
public Set<String> getKeys(String pattern) {
return redisTemplate.keys(pattern);
}
/**
* 在队尾追加元素
*
* @param key 键
* @param value 值
* @return 添加后队列的长度
*/
public Long rightPush(String key, Object value) {
return redisTemplate.opsForList().rightPush(key, value);
}
/**
* 从队首移除元素
*
* @param key 键
* @return 移除的元素
*/
public Object leftPop(String key) {
return redisTemplate.opsForList().leftPop(key);
}
/**
* 从队首开始取值,获取指定区间
*
* @param key 键
* @param start 开始下标
* @param stop 结束下标
* @return 区间内的全部值
*/
public List<?> leftRange(String key, long start, long stop) {
return redisTemplate.opsForList().range(key, start, stop);
}
/**
* 将值加到集合中
*
* @param key 键
* @param values 值
* @return 添加是否成功1成功0失败
*/
public Long addToSet(String key, Object... values) {
return redisTemplate.opsForSet().add(key, values);
}
/**
* 集合的数量
*
* @param key 键
* @return 集合数量
*/
public Long sizeOfSet(String key) {
return redisTemplate.opsForSet().size(key);
}
/**
* 集合列表
*
* @param key 键
* @return 集合列表
*/
public Set<Object> getOfSet(String key) {
return redisTemplate.opsForSet().members(key);
}
/**
* 集合中是否有该值
*
* @param key 键
* @param value 值
* @return Boolean
*/
public Boolean hasOfSet(String key,Object value) {
return redisTemplate.opsForSet().isMember(key,value);
}
/**
* 删除集合中值
*
* @param key 键
* @param value 值
* @return 删除是否成功1成功0失败
*/
public Long removeOfSet(String key,Object value) {
return redisTemplate.opsForSet().remove(key,value);
}
}

View File

View File

@@ -0,0 +1,95 @@
server:
port: 8080
# servlet:
# context-path: /ai
tomcat:
# 等待队列长度 (所有线程忙碌时,排队等待的连接数, 默认: 100)
accept-count: 1000
# 最大连接数 (默认: 8192)
max-connections: 20000
# 连接超时 (接受连接后等待请求数据的最大时间, 默认值通常与系统有关)
connection-timeout: 30000
threads:
# 最大工作线程数 (默认: 200)
max: 2000
# 最小工作线程数(初始启动的线程数, 默认: 10
min-spare: 200
# 限制表单提交(application/x-www-form-urlencoded)的POST请求最大大小 (默认: 2MB)
# 这是 Boot 2 中 'server.tomcat.max-http-post-size' 的替代品
max-http-form-post-size: 100MB
# 限制整个请求体(包括文件上传)的最大大小。超过此限制的请求将被拒绝 (默认: 2MB)
max-swallow-size: 100MB
# 定义响应的字符编码 (默认: UTF-8)
uri-encoding: UTF-8
compression:
enabled: true
http2:
enabled: true
spring:
application:
name: spring-ai-app
# 指定运行环境
profiles:
# 指定默认的运行环境为dev运行时通过--spring.profiles.active参数切换运行环境
active: dev
main:
allow-bean-definition-overriding: true
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
ai:
deepseek:
api-key: sk-f970111b34c84a8c812919fb4fad8b65
chat:
options:
model: deepseek-reasoner
data:
redis:
host: 180.76.119.46
port: 6379
password: qwer@1234
database: 0
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 3000
logging:
level:
org.springframework.ai.chat.client.advisor: debug
com.ai.app: debug
spring.datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://180.76.119.46:3306/spring_ai_app?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&failOverReadOnly=false&useSSL=false&allowMultiQueries=true
username: lk
password: Qwer@1234
type: com.zaxxer.hikari.HikariDataSource
# Hikari 连接池配置
hikari:
# 最小空闲连接数量
minimum-idle: 10
# 空闲连接存活最大时间默认60000010分钟
idle-timeout: 180000
# 连接池最大连接数默认是10
maximum-pool-size: 100
# 此属性控制从池返回的连接的默认自动提交行为,默认值true
auto-commit: true
# 连接池名称
pool-name: HikariCP
# 此属性控制池中连接的最长生命周期值0表示无限生命周期默认1800000即30分钟
max-lifetime: 1800000
# 数据库连接超时时间,默认30秒即30000
connection-timeout: 30000
connection-test-query: SELECT 1
mybatis-plus:
mapper-locations: classpath:mapper/**/*.xml
type-aliases-package: com.ai.app.entity
configuration:
# 日志打印
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
# 开启驼峰命名转换Table(create_time) -> Entity(createTime)。
# 不需要我们关心怎么进行字段匹配mybatis会自动识别`大写字母与下划线`
map-underscore-to-camel-case: true

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ai.app.mapper.AIChatMessageMapper">
<insert id="batchInsert" parameterType="com.ai.app.entity.AIChatMessage">
INSERT INTO ai_chat_message (id,conversation_id,conversation_type,message_type,message,create_time)
VALUES
<foreach collection="messageList" item="message" separator=",">
(#{message.id}, #{message.conversationId}, #{message.conversationType}, #{message.messageType},#{message.message}, #{message.createTime})
</foreach>
</insert>
</mapper>

View File

@@ -0,0 +1,19 @@
package com.ai.app;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.regex.Pattern;
@SpringBootTest
class SpringAiAppApplicationTests {
@Test
void contextLoads(){
System.out.println(new BigDecimal("-0.25"));
}
}