From 72dd9105abf0eca14b64e6f636af601e63af67df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=82=85=E5=93=A5?= <184172133@qq.com> Date: Fri, 27 Jan 2023 20:56:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=8F=E5=82=85=E5=93=A5=EF=BC=8Cfeat?= =?UTF-8?q?=EF=BC=9A=E7=AC=AC7=E8=8A=82=EF=BC=9A=E5=A4=9A=E7=BB=84?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E6=9C=8D=E5=8A=A1=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chatbot-api-application/pom.xml | 6 ++ .../ext/TaskRegistrarAutoConfig.java | 74 +++++++++++++++++++ ...{ChatbotSchedule.java => ChatbotTask.java} | 45 ++++++----- chatbot-api-common/pom.xml | 34 +++++++++ .../PropertyUtil.java | 70 ++++++++++++++++++ .../chatbot/api/domain/ai/IOpenAI.java | 2 +- .../chatbot/api/domain/ai/service/OpenAI.java | 5 +- .../src/main/resources/application.yml | 17 ++++- .../cn/bugstack/chatbot/api/test/ApiTest.java | 8 ++ .../chatbot/api/test/SpringBootRunTest.java | 10 ++- pom.xml | 1 + 11 files changed, 237 insertions(+), 35 deletions(-) create mode 100644 chatbot-api-application/src/main/java/cn/bugstack/chatbot/api/application/ext/TaskRegistrarAutoConfig.java rename chatbot-api-application/src/main/java/cn/bugstack/chatbot/api/application/job/{ChatbotSchedule.java => ChatbotTask.java} (59%) create mode 100644 chatbot-api-common/pom.xml create mode 100755 chatbot-api-common/src/main/java/cn.bugstack.chatbot.api.common/PropertyUtil.java diff --git a/chatbot-api-application/pom.xml b/chatbot-api-application/pom.xml index 5286b91..c19421a 100644 --- a/chatbot-api-application/pom.xml +++ b/chatbot-api-application/pom.xml @@ -34,6 +34,12 @@ chatbot-api-domain 1.0-SNAPSHOT + + cn.bugstack.ai + chatbot-api-common + 1.0-SNAPSHOT + compile + \ No newline at end of file diff --git a/chatbot-api-application/src/main/java/cn/bugstack/chatbot/api/application/ext/TaskRegistrarAutoConfig.java b/chatbot-api-application/src/main/java/cn/bugstack/chatbot/api/application/ext/TaskRegistrarAutoConfig.java new file mode 100644 index 0000000..c55e1a9 --- /dev/null +++ b/chatbot-api-application/src/main/java/cn/bugstack/chatbot/api/application/ext/TaskRegistrarAutoConfig.java @@ -0,0 +1,74 @@ +package cn.bugstack.chatbot.api.application.ext; + +import cn.bugstack.chatbot.api.application.job.ChatbotTask; +import cn.bugstack.chatbot.api.common.PropertyUtil; +import cn.bugstack.chatbot.api.domain.ai.IOpenAI; +import cn.bugstack.chatbot.api.domain.zsxq.IZsxqApi; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; + +import javax.annotation.Resource; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * @author 小傅哥,微信:fustack + * @description 任务注册服务,支持多组任务配置 + * @github https://github.com/fuzhengwei + * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获! + */ +@Configuration +@EnableScheduling +public class TaskRegistrarAutoConfig implements EnvironmentAware, SchedulingConfigurer { + + private Logger logger = LoggerFactory.getLogger(TaskRegistrarAutoConfig.class); + + /** + * 任务配置组 + */ + private Map> taskGroupMap = new HashMap<>(); + + @Resource + private IZsxqApi zsxqApi; + @Resource + private IOpenAI openAI; + + @Override + public void setEnvironment(Environment environment) { + String prefix = "chatbot-api."; + String launchListStr = environment.getProperty(prefix + "launchList"); + if (StringUtils.isEmpty(launchListStr)) return; + for (String groupKey : launchListStr.split(",")) { + Map taskGroupProps = PropertyUtil.handle(environment, prefix + groupKey, Map.class); + taskGroupMap.put(groupKey, taskGroupProps); + } + } + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + Set taskGroups = taskGroupMap.keySet(); + for (String groupKey : taskGroups) { + Map taskGroup = taskGroupMap.get(groupKey); + String groupName = taskGroup.get("groupName").toString(); + String groupId = taskGroup.get("groupId").toString(); + String cookie = taskGroup.get("cookie").toString(); + String openAiKey = taskGroup.get("openAiKey").toString(); + String cronExpressionBase64 = taskGroup.get("cronExpression").toString(); + String cronExpression = new String(Base64.getDecoder().decode(cronExpressionBase64), StandardCharsets.UTF_8); + logger.info("创建任务 groupName:{} groupId:{} cronExpression:{}", groupName, groupId, cronExpression); + // 添加任务 + taskRegistrar.addCronTask(new ChatbotTask(groupName, groupId, cookie, openAiKey, zsxqApi, openAI), cronExpression); + } + } + +} diff --git a/chatbot-api-application/src/main/java/cn/bugstack/chatbot/api/application/job/ChatbotSchedule.java b/chatbot-api-application/src/main/java/cn/bugstack/chatbot/api/application/job/ChatbotTask.java similarity index 59% rename from chatbot-api-application/src/main/java/cn/bugstack/chatbot/api/application/job/ChatbotSchedule.java rename to chatbot-api-application/src/main/java/cn/bugstack/chatbot/api/application/job/ChatbotTask.java index 3442328..4eeb0b5 100644 --- a/chatbot-api-application/src/main/java/cn/bugstack/chatbot/api/application/job/ChatbotSchedule.java +++ b/chatbot-api-application/src/main/java/cn/bugstack/chatbot/api/application/job/ChatbotTask.java @@ -7,12 +7,7 @@ import cn.bugstack.chatbot.api.domain.zsxq.model.vo.Topics; import com.alibaba.fastjson.JSON; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.Scheduled; -import javax.annotation.Resource; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; @@ -20,59 +15,63 @@ import java.util.Random; /** * @author 小傅哥,微信:fustack - * @description 问题任务 + * @description 任务体 * @github https://github.com/fuzhengwei * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获! */ -@EnableScheduling -@Configuration -public class ChatbotSchedule { +public class ChatbotTask implements Runnable { - private Logger logger = LoggerFactory.getLogger(ChatbotSchedule.class); + private Logger logger = LoggerFactory.getLogger(ChatbotTask.class); - @Value("${chatbot-api.groupId}") + private String groupName; private String groupId; - @Value("${chatbot-api.cookie}") private String cookie; + private String openAiKey; - @Resource private IZsxqApi zsxqApi; - @Resource private IOpenAI openAI; - // 表达式:cron.qqe2.com - @Scheduled(cron = "0/30 * * * * ?") + public ChatbotTask(String groupName, String groupId, String cookie, String openAiKey, IZsxqApi zsxqApi, IOpenAI openAI) { + this.groupName = groupName; + this.groupId = groupId; + this.cookie = cookie; + this.openAiKey = openAiKey; + this.zsxqApi = zsxqApi; + this.openAI = openAI; + } + + @Override public void run() { try { if (new Random().nextBoolean()) { - logger.info("随机打烊中..."); + logger.info("{} 随机打烊中...", groupName); return; } GregorianCalendar calendar = new GregorianCalendar(); int hour = calendar.get(Calendar.HOUR_OF_DAY); if (hour > 22 || hour < 7) { - logger.info("打烊时间不工作,AI 下班了!"); + logger.info("{} 打烊时间不工作,AI 下班了!", groupName); return; } // 1. 检索问题 UnAnsweredQuestionsAggregates unAnsweredQuestionsAggregates = zsxqApi.queryUnAnsweredQuestionsTopicId(groupId, cookie); - logger.info("检索结果:{}", JSON.toJSONString(unAnsweredQuestionsAggregates)); + logger.info("{} 检索结果:{}", groupName, JSON.toJSONString(unAnsweredQuestionsAggregates)); List topics = unAnsweredQuestionsAggregates.getResp_data().getTopics(); if (null == topics || topics.isEmpty()) { - logger.info("本次检索未查询到待会答问题"); + logger.info("{} 本次检索未查询到待会答问题", groupName); return; } // 2. AI 回答 Topics topic = topics.get(topics.size() - 1); - String answer = openAI.doChatGPT(topic.getQuestion().getText().trim()); + String answer = openAI.doChatGPT(openAiKey, topic.getQuestion().getText().trim()); // 3. 问题回复 boolean status = zsxqApi.answer(groupId, cookie, topic.getTopic_id(), answer, false); - logger.info("编号:{} 问题:{} 回答:{} 状态:{}", topic.getTopic_id(), topic.getQuestion().getText(), answer, status); + logger.info("{} 编号:{} 问题:{} 回答:{} 状态:{}", groupName, topic.getTopic_id(), topic.getQuestion().getText(), answer, status); } catch (Exception e) { - logger.error("自动回答问题异常", e); + logger.error("{} 自动回答问题异常", groupName, e); } } diff --git a/chatbot-api-common/pom.xml b/chatbot-api-common/pom.xml new file mode 100644 index 0000000..db75d12 --- /dev/null +++ b/chatbot-api-common/pom.xml @@ -0,0 +1,34 @@ + + + + chatbot-api + cn.bugstack.ai + 1.0-SNAPSHOT + + 4.0.0 + + chatbot-api-common + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + com.alibaba + fastjson + + + org.apache.commons + commons-lang3 + + + + \ No newline at end of file diff --git a/chatbot-api-common/src/main/java/cn.bugstack.chatbot.api.common/PropertyUtil.java b/chatbot-api-common/src/main/java/cn.bugstack.chatbot.api.common/PropertyUtil.java new file mode 100755 index 0000000..576fded --- /dev/null +++ b/chatbot-api-common/src/main/java/cn.bugstack.chatbot.api.common/PropertyUtil.java @@ -0,0 +1,70 @@ +package cn.bugstack.chatbot.api.common; + +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertyResolver; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class PropertyUtil { + + private static int springBootVersion = 1; + + static { + try { + Class.forName("org.springframework.boot.bind.RelaxedPropertyResolver"); + } catch (ClassNotFoundException e) { + springBootVersion = 2; + } + } + + /** + * Spring Boot 1.x is compatible with Spring Boot 2.x by Using Java Reflect. + * @param environment : the environment context + * @param prefix : the prefix part of property key + * @param targetClass : the target class type of result + * @param : refer to @param targetClass + * @return T + */ + @SuppressWarnings("unchecked") + public static T handle(final Environment environment, final String prefix, final Class targetClass) { + switch (springBootVersion) { + case 1: + return (T) v1(environment, prefix); + default: + return (T) v2(environment, prefix, targetClass); + } + } + + private static Object v1(final Environment environment, final String prefix) { + try { + Class resolverClass = Class.forName("org.springframework.boot.bind.RelaxedPropertyResolver"); + Constructor resolverConstructor = resolverClass.getDeclaredConstructor(PropertyResolver.class); + Method getSubPropertiesMethod = resolverClass.getDeclaredMethod("getSubProperties", String.class); + Object resolverObject = resolverConstructor.newInstance(environment); + String prefixParam = prefix.endsWith(".") ? prefix : prefix + "."; + return getSubPropertiesMethod.invoke(resolverObject, prefixParam); + } catch (final ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException + | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + throw new RuntimeException(ex.getMessage(), ex); + } + } + + private static Object v2(final Environment environment, final String prefix, final Class targetClass) { + try { + Class binderClass = Class.forName("org.springframework.boot.context.properties.bind.Binder"); + Method getMethod = binderClass.getDeclaredMethod("get", Environment.class); + Method bindMethod = binderClass.getDeclaredMethod("bind", String.class, Class.class); + Object binderObject = getMethod.invoke(null, environment); + String prefixParam = prefix.endsWith(".") ? prefix.substring(0, prefix.length() - 1) : prefix; + Object bindResultObject = bindMethod.invoke(binderObject, prefixParam, targetClass); + Method resultGetMethod = bindResultObject.getClass().getDeclaredMethod("get"); + return resultGetMethod.invoke(bindResultObject); + } catch (final ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException ex) { + throw new RuntimeException(ex.getMessage(), ex); + } + } + +} diff --git a/chatbot-api-domain/src/main/java/cn/bugstack/chatbot/api/domain/ai/IOpenAI.java b/chatbot-api-domain/src/main/java/cn/bugstack/chatbot/api/domain/ai/IOpenAI.java index e2cc5a5..5a16d09 100644 --- a/chatbot-api-domain/src/main/java/cn/bugstack/chatbot/api/domain/ai/IOpenAI.java +++ b/chatbot-api-domain/src/main/java/cn/bugstack/chatbot/api/domain/ai/IOpenAI.java @@ -10,6 +10,6 @@ import java.io.IOException; */ public interface IOpenAI { - String doChatGPT(String question) throws IOException; + String doChatGPT(String openAiKey, String question) throws IOException; } diff --git a/chatbot-api-domain/src/main/java/cn/bugstack/chatbot/api/domain/ai/service/OpenAI.java b/chatbot-api-domain/src/main/java/cn/bugstack/chatbot/api/domain/ai/service/OpenAI.java index 7713465..505c9bf 100644 --- a/chatbot-api-domain/src/main/java/cn/bugstack/chatbot/api/domain/ai/service/OpenAI.java +++ b/chatbot-api-domain/src/main/java/cn/bugstack/chatbot/api/domain/ai/service/OpenAI.java @@ -31,11 +31,8 @@ public class OpenAI implements IOpenAI { private Logger logger = LoggerFactory.getLogger(OpenAI.class); - @Value("${chatbot-api.openAiKey}") - private String openAiKey; - @Override - public String doChatGPT(String question) throws IOException { + public String doChatGPT(String openAiKey, String question) throws IOException { CloseableHttpClient httpClient = HttpClientBuilder.create().build(); diff --git a/chatbot-api-interfaces/src/main/resources/application.yml b/chatbot-api-interfaces/src/main/resources/application.yml index c002d21..94215e8 100644 --- a/chatbot-api-interfaces/src/main/resources/application.yml +++ b/chatbot-api-interfaces/src/main/resources/application.yml @@ -1,7 +1,18 @@ server: port: 8090 +# 分组任务配置 chatbot-api: - groupId: 28885518425541 # 知识星球ID - cookie: # 知识星球个人cookie信息 - openAiKey: # 自行申请 https://beta.openai.com/overview 或者联系小傅哥 fustack 付费申请【购买虚拟号码等需要支付】。 \ No newline at end of file + launchList: group01,group02 # 启动几组,就配置几组 + group01: + groupName: ChatGPT AI 问答助手 + groupId: 28885518425541 # 知识星球ID + cookie: # 知识星球个人cookie信息 + openAiKey: # 自行申请 https://beta.openai.com/overview 或者联系小傅哥 fustack 付费申请【购买虚拟号码等需要支付】。 + cronExpression: 0/30 * * * * ? # 执行的频次配置,参考:cron.qqe2.com + group02: + groupName: 码农会锁 + groupId: 28885518425541 # 知识星球ID + cookie: # 知识星球个人cookie信息 + openAiKey: # 自行申请 https://beta.openai.com/overview 或者联系小傅哥 fustack 付费申请【购买虚拟号码等需要支付】。 + cronExpression: 0/30 * * * * ? # 执行的频次配置,参考:cron.qqe2.com \ No newline at end of file diff --git a/chatbot-api-interfaces/src/test/java/cn/bugstack/chatbot/api/test/ApiTest.java b/chatbot-api-interfaces/src/test/java/cn/bugstack/chatbot/api/test/ApiTest.java index d991b3f..6df0092 100644 --- a/chatbot-api-interfaces/src/test/java/cn/bugstack/chatbot/api/test/ApiTest.java +++ b/chatbot-api-interfaces/src/test/java/cn/bugstack/chatbot/api/test/ApiTest.java @@ -12,6 +12,8 @@ import org.apache.http.util.EntityUtils; import org.junit.Test; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; /** * @author 小傅哥,微信:fustack @@ -21,6 +23,12 @@ import java.io.IOException; */ public class ApiTest { + @Test + public void base64(){ + String cronExpression = new String(Base64.getDecoder().decode("MC81MCAqICogKiAqID8="), StandardCharsets.UTF_8); + System.out.println(cronExpression); + } + @Test public void query_unanswered_questions() throws IOException { CloseableHttpClient httpClient = HttpClientBuilder.create().build(); diff --git a/chatbot-api-interfaces/src/test/java/cn/bugstack/chatbot/api/test/SpringBootRunTest.java b/chatbot-api-interfaces/src/test/java/cn/bugstack/chatbot/api/test/SpringBootRunTest.java index b8662ba..1c7e996 100644 --- a/chatbot-api-interfaces/src/test/java/cn/bugstack/chatbot/api/test/SpringBootRunTest.java +++ b/chatbot-api-interfaces/src/test/java/cn/bugstack/chatbot/api/test/SpringBootRunTest.java @@ -29,10 +29,12 @@ public class SpringBootRunTest { private Logger logger = LoggerFactory.getLogger(SpringBootRunTest.class); - @Value("${chatbot-api.groupId}") + @Value("${chatbot-api.group01.groupId}") private String groupId; - @Value("${chatbot-api.cookie}") + @Value("${chatbot-api.group01.cookie}") private String cookie; + @Value("${chatbot-api.group01.openAiKey}") + private String openAiKey; @Resource private IZsxqApi zsxqApi; @@ -51,13 +53,13 @@ public class SpringBootRunTest { logger.info("topicId:{} text:{}", topicId, text); // 回答问题 - zsxqApi.answer(groupId, cookie, topicId, openAI.doChatGPT(text), false); + zsxqApi.answer(groupId, cookie, topicId, openAI.doChatGPT(openAiKey, text), false); } } @Test public void test_openAi() throws IOException { - String response = openAI.doChatGPT("帮我写一个java冒泡排序"); + String response = openAI.doChatGPT(openAiKey, "帮我写一个java冒泡排序"); logger.info("测试结果:{}", response); } diff --git a/pom.xml b/pom.xml index df23ccf..f2a8ebe 100644 --- a/pom.xml +++ b/pom.xml @@ -14,6 +14,7 @@ chatbot-api-application chatbot-api-interfaces chatbot-api-infrastructure + chatbot-api-common -- GitLab