From e15e9d80c4137165030050db4b601fc59b2503c2 Mon Sep 17 00:00:00 2001 From: zhangdaiscott Date: Wed, 6 Mar 2024 16:22:03 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=903.6.3=E7=89=88=E6=9C=AC=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E3=80=91ai=E8=81=8A=E5=A4=A9=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jeecg/config/shiro/ShiroConfig.java | 19 +- jeecg-module-demo/pom.xml | 6 + .../modules/demo/gpt/cache/LocalCache.java | 34 +++ .../demo/gpt/controller/ChatController.java | 74 +++++++ .../OpenAISSEEventSourceListener.java | 136 ++++++++++++ .../modules/demo/gpt/service/ChatService.java | 56 +++++ .../gpt/service/impl/ChatServiceImpl.java | 199 ++++++++++++++++++ .../modules/demo/gpt/vo/ChatHistoryVO.java | 25 +++ .../src/main/resources/application-dev.yml | 14 ++ pom.xml | 12 +- 10 files changed, 571 insertions(+), 4 deletions(-) create mode 100644 jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/cache/LocalCache.java create mode 100644 jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/controller/ChatController.java create mode 100644 jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/listeners/OpenAISSEEventSourceListener.java create mode 100644 jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/service/ChatService.java create mode 100644 jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/service/impl/ChatServiceImpl.java create mode 100644 jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/vo/ChatHistoryVO.java diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java index 6a274a62..8a4d959a 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java @@ -17,6 +17,7 @@ import org.jeecg.config.shiro.filters.CustomShiroFilterFactoryBean; import org.jeecg.config.shiro.filters.JwtFilter; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; @@ -25,10 +26,12 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import org.springframework.web.filter.DelegatingFilterProxy; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisCluster; import javax.annotation.Resource; +import javax.servlet.DispatcherType; import javax.servlet.Filter; import java.util.*; @@ -50,7 +53,7 @@ public class ShiroConfig { private JeecgBaseConfig jeecgBaseConfig; @Autowired(required = false) private RedisProperties redisProperties; - + /** * Filter Chain定义说明 * @@ -181,6 +184,20 @@ public class ShiroConfig { return shiroFilterFactoryBean; } + //update-begin---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------ + @Bean + public FilterRegistrationBean shiroFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new DelegatingFilterProxy("shiroFilterFactoryBean")); + registration.setEnabled(true); + registration.addUrlPatterns("/*"); + //支持异步 + registration.setAsyncSupported(true); + registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC); + return registration; + } + //update-end---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------ + @Bean("securityManager") public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); diff --git a/jeecg-module-demo/pom.xml b/jeecg-module-demo/pom.xml index 2f71a867..dfdf150e 100644 --- a/jeecg-module-demo/pom.xml +++ b/jeecg-module-demo/pom.xml @@ -16,6 +16,12 @@ org.jeecgframework.boot jeecg-boot-base-core + + + + org.jeecgframework.boot + jeecg-boot-starter-chatgpt + \ No newline at end of file diff --git a/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/cache/LocalCache.java b/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/cache/LocalCache.java new file mode 100644 index 00000000..554b42dc --- /dev/null +++ b/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/cache/LocalCache.java @@ -0,0 +1,34 @@ +package org.jeecg.modules.demo.gpt.cache; + +import cn.hutool.cache.CacheUtil; +import cn.hutool.cache.impl.TimedCache; +import cn.hutool.core.date.DateUnit; + +//update-begin---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------ + +/** + * 聊天记录本地缓存 + * @author chenrui + * @date 2024/1/26 20:06 + */ +public class LocalCache { + /** + * 缓存时长 + */ + public static final long TIMEOUT = 5 * DateUnit.MINUTE.getMillis(); + /** + * 清理间隔 + */ + private static final long CLEAN_TIMEOUT = 5 * DateUnit.MINUTE.getMillis(); + /** + * 缓存对象 + */ + public static final TimedCache CACHE = CacheUtil.newTimedCache(TIMEOUT); + + static { + //启动定时任务 + CACHE.schedulePrune(CLEAN_TIMEOUT); + } +} + +//update-end---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------ \ No newline at end of file diff --git a/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/controller/ChatController.java b/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/controller/ChatController.java new file mode 100644 index 00000000..e4728b0e --- /dev/null +++ b/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/controller/ChatController.java @@ -0,0 +1,74 @@ +package org.jeecg.modules.demo.gpt.controller; + +import org.jeecg.common.api.vo.Result; +import org.jeecg.modules.demo.gpt.service.ChatService; +import org.jeecg.modules.demo.gpt.vo.ChatHistoryVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +//update-begin---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------ + +/** + * @Description: chatGpt-聊天接口 + * @Author: chenrui + * @Date: 2024/1/9 16:30 + */ +@Controller +@RequestMapping("/ai/chat") +public class ChatController { + + @Autowired + ChatService chatService; + + /** + * 创建sse连接 + * + * @return + */ + @GetMapping(value = "/send") + public SseEmitter createConnect(@RequestParam(name = "topicId", required = false) String topicId, @RequestParam(name = "message", required = true) String message) { + SseEmitter sse = chatService.createChat(); + chatService.sendMessage(topicId, message); + return sse; + } + + //update-begin---author:chenrui ---date:20240223 for:[QQYUN-8225]聊天记录保存------------ + /** + * 保存聊天记录 + * @param chatHistoryVO + * @return + * @author chenrui + * @date 2024/2/22 13:54 + */ + @PostMapping(value = "/history/save") + @ResponseBody + public Result saveHistory(@RequestBody ChatHistoryVO chatHistoryVO) { + return chatService.saveHistory(chatHistoryVO); + } + + /** + * 查询聊天记录 + * @return + * @author chenrui + * @date 2024/2/22 14:03 + */ + @GetMapping(value = "/history/get") + @ResponseBody + public Result getHistoryByTopic() { + return chatService.getHistoryByTopic(); + } + //update-end---author:chenrui ---date:20240223 for:[QQYUN-8225]聊天记录保存------------ + + /** + * 关闭连接 + */ + @GetMapping(value = "/close") + public void closeConnect() { + chatService.closeChat(); + } + + +} +//update-end---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------ diff --git a/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/listeners/OpenAISSEEventSourceListener.java b/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/listeners/OpenAISSEEventSourceListener.java new file mode 100644 index 00000000..034587b8 --- /dev/null +++ b/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/listeners/OpenAISSEEventSourceListener.java @@ -0,0 +1,136 @@ +package org.jeecg.modules.demo.gpt.listeners; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.unfbx.chatgpt.entity.chat.ChatCompletionResponse; +import com.unfbx.chatgpt.entity.chat.Message; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.sse.EventSource; +import okhttp3.sse.EventSourceListener; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.Objects; + +//update-begin---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------ +/** + * OpenAI的SSE监听 + * @author chenrui + * @date 2024/1/26 20:06 + */ +@Slf4j +public class OpenAISSEEventSourceListener extends EventSourceListener { + + private long tokens; + + private SseEmitter sseEmitter; + + private String topicId; + + public OpenAISSEEventSourceListener(SseEmitter sseEmitter) { + this.sseEmitter = sseEmitter; + } + + public OpenAISSEEventSourceListener(String topicId,SseEmitter sseEmitter){ + this.topicId = topicId; + this.sseEmitter = sseEmitter; + } + + /** + * {@inheritDoc} + */ + @Override + public void onOpen(@NotNull EventSource eventSource, @NotNull Response response) { + log.info("OpenAI建立sse连接..."); + } + + /** + * {@inheritDoc} + */ + @SneakyThrows + @Override + public void onEvent(@NotNull EventSource eventSource, String id, String type, @NotNull String data) { + log.debug("OpenAI返回数据:{}", data); + tokens += 1; + if (data.equals("[DONE]")) { + log.info("OpenAI返回数据结束了"); + sseEmitter.send(SseEmitter.event() + .id("[TOKENS]") + .data("

tokens:" + tokens()) + .reconnectTime(3000)); + sseEmitter.send(SseEmitter.event() + .id("[DONE]") + .data("[DONE]") + .reconnectTime(3000)); + // 传输完成后自动关闭sse + sseEmitter.complete(); + return; + } + ObjectMapper mapper = new ObjectMapper(); + ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class); // 读取Json + try { + sseEmitter.send(SseEmitter.event() + .id(this.topicId) + .data(completionResponse.getChoices().get(0).getDelta()) + .reconnectTime(3000)); + } catch (Exception e) { + log.error(e.getMessage(),e); + eventSource.cancel(); + } + } + + + @Override + public void onClosed(@NotNull EventSource eventSource) { + log.info("流式输出返回值总共{}tokens", tokens() - 2); + log.info("OpenAI关闭sse连接..."); + } + + + @SneakyThrows + @Override + public void onFailure(@NotNull EventSource eventSource, Throwable t, Response response) { + String errMsg = ""; + ResponseBody body = null == response ? null:response.body(); + if (Objects.nonNull(body)) { + log.error("OpenAI sse连接异常data:{},异常:{}", body.string(), t.getMessage()); + errMsg = body.string(); + } else { + log.error("OpenAI sse连接异常data:{},异常:{}", response, t.getMessage()); + errMsg = t.getMessage(); + } + eventSource.cancel(); + sseEmitter.send(SseEmitter.event() + .id("[ERR]") + .data(Message.builder().content(explainErr(errMsg)).build()) + .reconnectTime(3000)); + sseEmitter.send(SseEmitter.event() + .id("[DONE]") + .data("[DONE]") + .reconnectTime(3000)); + sseEmitter.complete(); + } + + private String explainErr(String errMsg){ + if(StringUtils.isEmpty(errMsg)){ + return ""; + } + if(errMsg.contains("Rate limit")){ + return "请求频率太快了,请等待20秒再试."; + } + return errMsg; + } + + /** + * tokens + * @return + */ + public long tokens() { + return tokens; + } +} + +//update-end---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------ \ No newline at end of file diff --git a/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/service/ChatService.java b/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/service/ChatService.java new file mode 100644 index 00000000..57013a2e --- /dev/null +++ b/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/service/ChatService.java @@ -0,0 +1,56 @@ +package org.jeecg.modules.demo.gpt.service; + +import org.jeecg.common.api.vo.Result; +import org.jeecg.modules.demo.gpt.vo.ChatHistoryVO; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +//update-begin---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------ + +/** + * AI助手聊天Service + * @author chenrui + * @date 2024/1/26 20:08 + */ +public interface ChatService { + /** + * 创建SSE + * @return + */ + SseEmitter createChat(); + + /** + * 关闭SSE + */ + void closeChat(); + + /** + * 客户端发送消息到服务端 + * + * @param topicId + * @param message + * @author chenrui + * @date 2024/1/26 20:01 + */ + void sendMessage(String topicId, String message); + + //update-begin---author:chenrui ---date:20240223 for:[QQYUN-8225]聊天记录保存------------ + /** + * 保存聊天记录 + * @param chatHistoryVO + * @return + * @author chenrui + * @date 2024/2/22 13:37 + */ + Result saveHistory(ChatHistoryVO chatHistoryVO); + + /** + * 查询聊天记录 + * @return + * @author chenrui + * @date 2024/2/22 13:59 + */ + Result getHistoryByTopic(); + //update-end---author:chenrui ---date:20240223 for:[QQYUN-8225]聊天记录保存------------ +} + +//update-end---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------ \ No newline at end of file diff --git a/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/service/impl/ChatServiceImpl.java b/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/service/impl/ChatServiceImpl.java new file mode 100644 index 00000000..16c21e9b --- /dev/null +++ b/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/service/impl/ChatServiceImpl.java @@ -0,0 +1,199 @@ +package org.jeecg.modules.demo.gpt.service.impl; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSONArray; +import com.unfbx.chatgpt.OpenAiStreamClient; +import com.unfbx.chatgpt.entity.chat.ChatCompletion; +import com.unfbx.chatgpt.entity.chat.Message; +import com.unfbx.chatgpt.exception.BaseException; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.exception.JeecgBootException; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.util.SpringContextUtils; +import org.jeecg.common.util.UUIDGenerator; +import org.jeecg.modules.demo.gpt.cache.LocalCache; +import org.jeecg.modules.demo.gpt.listeners.OpenAISSEEventSourceListener; +import org.jeecg.modules.demo.gpt.service.ChatService; +import org.jeecg.modules.demo.gpt.vo.ChatHistoryVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +//update-begin---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------ + +/** + * AI助手聊天Service + * @author chenrui + * @date 2024/1/26 20:07 + */ +@Service +@Slf4j +public class ChatServiceImpl implements ChatService { + + //update-begin---author:chenrui ---date:20240223 for:[QQYUN-8225]聊天记录保存------------ + private static final String CACHE_KEY_PREFIX = "ai:chart:"; + + /** + * + */ + private static final String CACHE_KEY_MSG_CONTEXT = "msg_content"; + + + /** + * + */ + private static final String CACHE_KEY_MSG_HISTORY = "msg_history"; + + @Autowired + RedisTemplate redisTemplate; + //update-end---author:chenrui ---date:20240223 for:[QQYUN-8225]聊天记录保存------------ + + private OpenAiStreamClient openAiStreamClient = null; + + //update-begin---author:chenrui ---date:20240131 for:[QQYUN-8212]fix 没有配置启动报错------------ + public ChatServiceImpl() { + try { + this.openAiStreamClient = SpringContextUtils.getBean(OpenAiStreamClient.class); + } catch (Exception ignored) { + } + } + + /** + * 防止client不能成功注入 + * @return + * @author chenrui + * @date 2024/2/3 23:08 + */ + private OpenAiStreamClient ensureClient(){ + if(null == this.openAiStreamClient){ + this.openAiStreamClient = SpringContextUtils.getBean(OpenAiStreamClient.class); + } + return this.openAiStreamClient; + } + //update-end---author:chenrui ---date:20240131 for:[QQYUN-8212]fix 没有配置启动报错------------ + + private String getUserId() { + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + return sysUser.getId(); + } + + @Override + public SseEmitter createChat() { + String uid = getUserId(); + //默认30秒超时,设置为0L则永不超时 + SseEmitter sseEmitter = new SseEmitter(-0L); + //完成后回调 + sseEmitter.onCompletion(() -> { + log.info("[{}]结束连接...................",uid); + LocalCache.CACHE.remove(uid); + }); + //超时回调 + sseEmitter.onTimeout(() -> { + log.info("[{}]连接超时...................", uid); + }); + //异常回调 + sseEmitter.onError( + throwable -> { + try { + log.info("[{}]连接异常,{}", uid, throwable.toString()); + sseEmitter.send(SseEmitter.event() + .id(uid) + .name("发生异常!") + .data(Message.builder().content("发生异常请重试!").build()) + .reconnectTime(3000)); + LocalCache.CACHE.put(uid, sseEmitter); + } catch (IOException e) { + log.error(e.getMessage(),e); + } + } + ); + try { + sseEmitter.send(SseEmitter.event().reconnectTime(5000)); + } catch (IOException e) { + log.error(e.getMessage(),e); + } + LocalCache.CACHE.put(uid, sseEmitter); + log.info("[{}]创建sse连接成功!", uid); + return sseEmitter; + } + + @Override + public void closeChat() { + String uid = getUserId(); + SseEmitter sse = (SseEmitter) LocalCache.CACHE.get(uid); + if (sse != null) { + sse.complete(); + //移除 + LocalCache.CACHE.remove(uid); + } + } + + @Override + public void sendMessage(String topicId, String message) { + String uid = getUserId(); + if (StrUtil.isBlank(message)) { + log.info("参数异常,message为null"); + throw new BaseException("参数异常,message不能为空~"); + } + if (StrUtil.isBlank(topicId)) { + topicId = UUIDGenerator.generate(); + } + //update-begin---author:chenrui ---date:20240223 for:[QQYUN-8225]聊天记录保存------------ + log.info("话题id:{}", topicId); + String cacheKey = CACHE_KEY_PREFIX + uid + "_" + topicId; + String messageContext = (String) redisTemplate.opsForHash().get(cacheKey, CACHE_KEY_MSG_CONTEXT); + List msgHistory = new ArrayList<>(); + if (StrUtil.isNotBlank(messageContext)) { + List messages = JSONArray.parseArray(messageContext, Message.class); + msgHistory = messages == null ? new ArrayList<>() : messages; + } + Message currentMessage = Message.builder().content(message).role(Message.Role.USER).build(); + msgHistory.add(currentMessage); + + SseEmitter sseEmitter = (SseEmitter) LocalCache.CACHE.get(uid); + if (sseEmitter == null) { + log.info("聊天消息推送失败uid:[{}],没有创建连接,请重试。", uid); + throw new JeecgBootException("聊天消息推送失败uid:[{}],没有创建连接,请重试。~"); + } + OpenAISSEEventSourceListener openAIEventSourceListener = new OpenAISSEEventSourceListener(topicId, sseEmitter); + ChatCompletion completion = ChatCompletion + .builder() + .messages(msgHistory) + .model(ChatCompletion.Model.GPT_3_5_TURBO.getName()) + .build(); + ensureClient().streamChatCompletion(completion, openAIEventSourceListener); + redisTemplate.opsForHash().put(cacheKey, CACHE_KEY_MSG_CONTEXT, JSONUtil.toJsonStr(msgHistory)); + //update-end---author:chenrui ---date:20240223 for:[QQYUN-8225]聊天记录保存------------ + Result.ok(completion.tokens()); + } + + //update-begin---author:chenrui ---date:20240223 for:[QQYUN-8225]聊天记录保存------------ + @Override + public Result saveHistory(ChatHistoryVO chatHistoryVO) { + String uid = getUserId(); + String cacheKey = CACHE_KEY_PREFIX + CACHE_KEY_MSG_HISTORY + ":" + uid; + redisTemplate.opsForValue().set(cacheKey, chatHistoryVO.getContent()); + return Result.OK("保存成功"); + } + + @Override + public Result getHistoryByTopic() { + String uid = getUserId(); + String cacheKey = CACHE_KEY_PREFIX + CACHE_KEY_MSG_HISTORY + ":" + uid; + String historyContent = (String) redisTemplate.opsForValue().get(cacheKey); + ChatHistoryVO chatHistoryVO = new ChatHistoryVO(); + chatHistoryVO.setContent(historyContent); + return Result.OK(chatHistoryVO); + } + //update-end---author:chenrui ---date:20240223 for:[QQYUN-8225]聊天记录保存------------ +} + +//update-end---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------ diff --git a/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/vo/ChatHistoryVO.java b/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/vo/ChatHistoryVO.java new file mode 100644 index 00000000..db8f942a --- /dev/null +++ b/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/vo/ChatHistoryVO.java @@ -0,0 +1,25 @@ +package org.jeecg.modules.demo.gpt.vo; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @Description: 聊天记录 + * @Author: chenrui + * @Date: 2024/2/22 13:36 + */ +@Data +public class ChatHistoryVO implements Serializable { + private static final long serialVersionUID = 3238429500037511283L; + + /** + * 话题id + */ + String topicId; + + /** + * 聊天记录内容 + */ + String content; +} diff --git a/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml b/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml index ffa0fa21..783ec60a 100644 --- a/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml +++ b/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml @@ -261,6 +261,20 @@ jeecg: password: type: STANDALONE enabled: true + # ChartGPT对接配置 + ai-chat: + # 是否开启;必须。 + enabled: false + # openAi接口秘钥,填写自己的apiKey;必须。 + apiKey: "????" + # openAi域名,有代理就填代理的域名。默认:openAI官方apiHost + apiHost: "https://api.openai.com" + # 超时时间单位:s。默认 60s + timeout: 60 + # 本地代理地址 +# proxy: +# host: "http://127.0.0.1" +# port: "7890" #cas单点登录 cas: prefixUrl: http://cas.example.org:8443/cas diff --git a/pom.xml b/pom.xml index f91856b3..30350290 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.10 + 2.7.18 @@ -47,7 +47,7 @@ 11.2.0.3 4.0 8.0.27 - 5.8.23 + 5.8.25 8.1.1.49 @@ -58,7 +58,7 @@ 1.9.5 - 1.6.6 + 1.7.1 2.6 2.1.0 3.11.2 @@ -390,6 +390,12 @@ jimureport-nosql-starter 1.6.0 + + + org.jeecgframework.boot + jeecg-boot-starter-chatgpt + ${jeecgboot.version} +