From c3ec1f2fdc677f00dd239cd79d29ed9f6bdabccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=82=85=E5=93=A5?= <184172133@qq.com> Date: Sun, 22 Oct 2023 11:13:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20Ai=20=E8=87=AA=E5=8A=A8=E5=9B=9E?= =?UTF-8?q?=E5=B8=96=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/images/mock.png | Bin 0 -> 62924 bytes pom.xml | 24 +++ xfg-dev-tech-app/pom.xml | 15 ++ .../xfg/dev/tech/config/ChatGLMSDKConfig.java | 35 ++++ .../config/ChatGLMSDKConfigProperties.java | 22 +++ .../src/main/resources/application-dev.yml | 19 +++ .../bugstack/xfg/dev/tech/test/ApiTest.java | 32 ++++ xfg-dev-tech-domain/pom.xml | 56 ++++++ .../domain/zsxq/adapter/IZSXQAdapter.java | 18 ++ .../domain/zsxq/model/vo/TopicsItemVO.java | 35 ++++ .../dev/tech/domain/zsxq/service/AiReply.java | 159 ++++++++++++++++++ .../tech/domain/zsxq/service/IAiReply.java | 12 ++ xfg-dev-tech-infrastructure/pom.xml | 46 +++++ .../gateway/adapter/ZSXQAdapter.java | 65 +++++++ .../infrastructure/gateway/api/IZSXQApi.java | 30 ++++ .../gateway/api/impl/ZSXQApi.java | 112 ++++++++++++ .../infrastructure/gateway/dto/Group.java | 20 +++ .../infrastructure/gateway/dto/Owner.java | 20 +++ .../infrastructure/gateway/dto/RespData.java | 13 ++ .../gateway/dto/ResponseDTO.java | 14 ++ .../gateway/dto/ShowCommentsItem.java | 29 ++++ .../tech/infrastructure/gateway/dto/Talk.java | 14 ++ .../gateway/dto/TopicsItem.java | 52 ++++++ .../gateway/dto/UserSpecific.java | 14 ++ xfg-dev-tech-trigger/pom.xml | 63 +++++++ .../bugstack/xfg/dev/tech/job/ReplyJob.java | 27 +++ 26 files changed, 946 insertions(+) create mode 100644 docs/images/mock.png create mode 100644 xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfig.java create mode 100644 xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfigProperties.java create mode 100644 xfg-dev-tech-app/src/test/java/cn/bugstack/xfg/dev/tech/test/ApiTest.java create mode 100644 xfg-dev-tech-domain/pom.xml create mode 100644 xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/adapter/IZSXQAdapter.java create mode 100644 xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/model/vo/TopicsItemVO.java create mode 100644 xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/AiReply.java create mode 100644 xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/IAiReply.java create mode 100644 xfg-dev-tech-infrastructure/pom.xml create mode 100644 xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/adapter/ZSXQAdapter.java create mode 100644 xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/IZSXQApi.java create mode 100644 xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/impl/ZSXQApi.java create mode 100644 xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Group.java create mode 100644 xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Owner.java create mode 100644 xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/RespData.java create mode 100644 xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ResponseDTO.java create mode 100644 xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ShowCommentsItem.java create mode 100644 xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Talk.java create mode 100644 xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/TopicsItem.java create mode 100644 xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/UserSpecific.java create mode 100644 xfg-dev-tech-trigger/pom.xml create mode 100644 xfg-dev-tech-trigger/src/main/java/cn/bugstack/xfg/dev/tech/job/ReplyJob.java diff --git a/docs/images/mock.png b/docs/images/mock.png new file mode 100644 index 0000000000000000000000000000000000000000..a73bb3307ec3dee9ca8c7a95a97765de3e77ba0e GIT binary patch literal 62924 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 I)P4Vd01KgG`2YX_ literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index 36975c5..920214a 100644 --- a/pom.xml +++ b/pom.xml @@ -10,6 +10,9 @@ pom xfg-dev-tech-app + xfg-dev-tech-infrastructure + xfg-dev-tech-domain + xfg-dev-tech-trigger @@ -45,6 +48,27 @@ guava 32.1.2-jre + + com.squareup.retrofit2 + converter-gson + 2.9.0 + + + cn.bugstack + chatglm-sdk-java + 1.0-SNAPSHOT + + + + org.apache.httpcomponents + httpclient + 4.5.14 + + + org.apache.httpcomponents + httpmime + 4.5.10 + diff --git a/xfg-dev-tech-app/pom.xml b/xfg-dev-tech-app/pom.xml index c04b355..588a296 100644 --- a/xfg-dev-tech-app/pom.xml +++ b/xfg-dev-tech-app/pom.xml @@ -54,6 +54,21 @@ com.google.guava guava + + cn.bugstack + chatglm-sdk-java + + + + cn.bugstack + xfg-dev-tech-infrastructure + 1.0-SNAPSHOT + + + cn.bugstack + xfg-dev-tech-trigger + 1.0-SNAPSHOT + diff --git a/xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfig.java b/xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfig.java new file mode 100644 index 0000000..4c93d43 --- /dev/null +++ b/xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfig.java @@ -0,0 +1,35 @@ +package cn.bugstack.xfg.dev.tech.config; + +import cn.bugstack.chatglm.session.OpenAiSession; +import cn.bugstack.chatglm.session.OpenAiSessionFactory; +import cn.bugstack.chatglm.session.defaults.DefaultOpenAiSessionFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description ChatGLMSDK + * @create 2023-10-22 10:00 + */ +@Configuration +@EnableConfigurationProperties(ChatGLMSDKConfigProperties.class) +public class ChatGLMSDKConfig { + + @Bean + @ConditionalOnProperty(value = "chatglm.sdk.config.enabled", havingValue = "true", matchIfMissing = false) + public OpenAiSession openAiSession(ChatGLMSDKConfigProperties properties) { + // 1. 配置文件 + cn.bugstack.chatglm.session.Configuration configuration = new cn.bugstack.chatglm.session.Configuration(); + configuration.setApiHost(properties.getApiHost()); + configuration.setApiSecretKey(properties.getApiSecretKey()); + + // 2. 会话工厂 + OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration); + + // 3. 开启会话 + return factory.openSession(); + } + +} diff --git a/xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfigProperties.java b/xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfigProperties.java new file mode 100644 index 0000000..ae57a3d --- /dev/null +++ b/xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfigProperties.java @@ -0,0 +1,22 @@ +package cn.bugstack.xfg.dev.tech.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description ChatGLMSDK Config + * @create 2023-10-22 10:00 + */ +@Data +@ConfigurationProperties(prefix = "chatglm.sdk.config", ignoreInvalidFields = true) +public class ChatGLMSDKConfigProperties { + + /** 状态;open = 开启、close 关闭 */ + private boolean enable; + /** 转发地址 */ + private String apiHost; + /** 可以申请 sk-*** */ + private String apiSecretKey; + +} diff --git a/xfg-dev-tech-app/src/main/resources/application-dev.yml b/xfg-dev-tech-app/src/main/resources/application-dev.yml index a86d5e8..3728d3a 100644 --- a/xfg-dev-tech-app/src/main/resources/application-dev.yml +++ b/xfg-dev-tech-app/src/main/resources/application-dev.yml @@ -2,6 +2,25 @@ server: port: 8091 application: name: xfg-dev-tech-mock + +# ChatGLM SDK Config +chatglm: + sdk: + config: + # 状态;true = 开启、false 关闭 + enabled: true + # 官网地址 + api-host: https://open.bigmodel.cn/ + # 官网申请 https://open.bigmodel.cn/usercenter/apikeys + api-secret-key: d570f7c5d289cdac2abdfdc562e39f3f.trqz1dH8ZK6ED7Pg + +# 知识星球配置信息;id -> 星球ID、user-id -> 用户ID、cookie -> 登录cookie +zsxq: + config: + id: 28885518425541 + user-id: 241858242255511 + cookie: zsxq_access_token=E0538FF2-B440-69E9-57D1-59EA37B9C0C6_9D76421394C6F474 + # 日志 logging: level: diff --git a/xfg-dev-tech-app/src/test/java/cn/bugstack/xfg/dev/tech/test/ApiTest.java b/xfg-dev-tech-app/src/test/java/cn/bugstack/xfg/dev/tech/test/ApiTest.java new file mode 100644 index 0000000..20a429e --- /dev/null +++ b/xfg-dev-tech-app/src/test/java/cn/bugstack/xfg/dev/tech/test/ApiTest.java @@ -0,0 +1,32 @@ +package cn.bugstack.xfg.dev.tech.test; + +import cn.bugstack.xfg.dev.tech.domain.zsxq.service.IAiReply; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.annotation.Resource; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 单元测试 + * @create 2023-10-22 09:08 + */ +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class ApiTest { + + @Resource + private IAiReply aiReply; + + @Test + public void test_IAiReply() { + aiReply.doAiReply(); + + + } + +} diff --git a/xfg-dev-tech-domain/pom.xml b/xfg-dev-tech-domain/pom.xml new file mode 100644 index 0000000..87d9188 --- /dev/null +++ b/xfg-dev-tech-domain/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + cn.bugstack + xfg-dev-tech-mock + 1.0-SNAPSHOT + + + xfg-dev-tech-domain + + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + + + com.alibaba + fastjson + + + org.apache.commons + commons-lang3 + + + com.google.guava + guava + + + cn.bugstack + chatglm-sdk-java + + + + + xfg-dev-tech-domain + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + ${java.version} + + + + + + \ No newline at end of file diff --git a/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/adapter/IZSXQAdapter.java b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/adapter/IZSXQAdapter.java new file mode 100644 index 0000000..34b4f2f --- /dev/null +++ b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/adapter/IZSXQAdapter.java @@ -0,0 +1,18 @@ +package cn.bugstack.xfg.dev.tech.domain.zsxq.adapter; + +import cn.bugstack.xfg.dev.tech.domain.zsxq.model.vo.TopicsItemVO; + +import java.util.List; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 知识星球接口适配 + * @create 2023-10-22 10:11 + */ +public interface IZSXQAdapter { + + List queryTopics(); + + void comment(long topicId, String content); + +} diff --git a/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/model/vo/TopicsItemVO.java b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/model/vo/TopicsItemVO.java new file mode 100644 index 0000000..de3a3b9 --- /dev/null +++ b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/model/vo/TopicsItemVO.java @@ -0,0 +1,35 @@ +package cn.bugstack.xfg.dev.tech.domain.zsxq.model.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 话题 + * @create 2023-10-22 10:13 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class TopicsItemVO { + + private long topicId; + + private String talk; + + private List showCommentsItems; + + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class ShowCommentsItem { + private long userId; + } + +} diff --git a/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/AiReply.java b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/AiReply.java new file mode 100644 index 0000000..74579b3 --- /dev/null +++ b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/AiReply.java @@ -0,0 +1,159 @@ +package cn.bugstack.xfg.dev.tech.domain.zsxq.service; + +import cn.bugstack.chatglm.model.*; +import cn.bugstack.chatglm.session.OpenAiSession; +import cn.bugstack.xfg.dev.tech.domain.zsxq.adapter.IZSXQAdapter; +import cn.bugstack.xfg.dev.tech.domain.zsxq.model.vo.TopicsItemVO; +import com.alibaba.fastjson.JSON; +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.extern.slf4j.Slf4j; +import okhttp3.sse.EventSource; +import okhttp3.sse.EventSourceListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.Nullable; +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 只能回帖 + * @create 2023-10-22 10:10 + */ +@Slf4j +@Service +public class AiReply implements IAiReply { + + @Autowired(required = false) + private OpenAiSession openAiSession; + @Resource + private IZSXQAdapter zsxqAdapter; + @Value("${zsxq.config.user-id}") + private Long userId; + + private final String regex = " (.*)"; + private volatile Set topicIds = new HashSet<>(); + + @Override + public void doAiReply() { + List topicsItemVOS = zsxqAdapter.queryTopics(); + + for (TopicsItemVO topicsItem : topicsItemVOS) { + // 是否回答过判断 + if (!isCommentDone(topicsItem)) continue; + // 找到圈我我帖子 + long topicId = topicsItem.getTopicId(); + String text = topicsItem.getTalk(); + + // " 提问 java 冒泡排序" + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(text); + + if (!matcher.find()) continue; + String uid = matcher.group(1); + String remainingText = matcher.group(3); + + if (this.userId.equals(Long.valueOf(uid))) { + + if (null == openAiSession) { + log.info("你没有开启 ChatGLM 参考yml配置文件来开启"); + // 你可以使用 ChatGLM SDK 进行回答,回复问题; + zsxqAdapter.comment(topicId, "【测试,只回答圈我的帖子】对接 ChatGLM SDK https://bugstack.cn/md/project/chatgpt/sdk/chatglm-sdk-java.html 回答:" + remainingText); + } else { + log.info("ChatGLM 进入回答 {} {}", topicId, remainingText); + if (topicIds.contains(topicId)) { + continue; + } else { + topicIds.add(topicId); + } + new Thread(() -> { + // 入参;模型、请求信息 + ChatCompletionRequest request = new ChatCompletionRequest(); + request.setModel(Model.CHATGLM_LITE); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro + request.setPrompt(new ArrayList() { + private static final long serialVersionUID = -7988151926241837899L; + + { + add(ChatCompletionRequest.Prompt.builder() + .role(Role.user.getCode()) + .content(remainingText) + .build()); + } + }); + + // 请求 + try { + StringBuilder content = new StringBuilder(); + openAiSession.completions(request, new EventSourceListener() { + @Override + public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) { + ChatCompletionResponse chatCompletionResponse = com.alibaba.fastjson.JSON.parseObject(data, ChatCompletionResponse.class); + log.info("测试结果 onEvent:{}", chatCompletionResponse.getData()); + // type 消息类型,add 增量,finish 结束,error 错误,interrupted 中断 + if (EventType.finish.getCode().equals(type)) { + ChatCompletionResponse.Meta meta = com.alibaba.fastjson.JSON.parseObject(chatCompletionResponse.getMeta(), ChatCompletionResponse.Meta.class); + log.info("[输出结束] Tokens {}", com.alibaba.fastjson.JSON.toJSONString(meta)); + } + content.append(chatCompletionResponse.getData()); + } + + @Override + public void onClosed(EventSource eventSource) { + log.info("对话完成"); + + // 你可以使用 ChatGLM SDK 进行回答,回复问题; + String contents = "ChatGLM 回答:" + content; + int maxLength = 5000; + int contentLength = contents.length(); + int startIndex = 0; + int endIndex = maxLength; + + while (startIndex < contentLength) { + if (endIndex > contentLength) { + endIndex = contentLength; + } + + String subContent = contents.substring(startIndex, endIndex); + zsxqAdapter.comment(topicId, subContent); + + startIndex = endIndex; + endIndex += maxLength; + } + + topicIds.remove(topicId); + } + + }); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }).start(); + } + + } + + } + + log.info("AI回复:{}", JSON.toJSONString(topicsItemVOS)); + } + + private boolean isCommentDone(TopicsItemVO topicsItem) { + List showComments = topicsItem.getShowCommentsItems(); + if (null == showComments || showComments.isEmpty()) return true; + for (TopicsItemVO.ShowCommentsItem item : topicsItem.getShowCommentsItems()) { + long userId = item.getUserId(); + if (this.userId == userId) { + return false; + } + } + return true; + } + +} diff --git a/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/IAiReply.java b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/IAiReply.java new file mode 100644 index 0000000..8f16237 --- /dev/null +++ b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/IAiReply.java @@ -0,0 +1,12 @@ +package cn.bugstack.xfg.dev.tech.domain.zsxq.service; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 智能AI回帖 + * @create 2023-10-22 10:08 + */ +public interface IAiReply { + + void doAiReply(); + +} diff --git a/xfg-dev-tech-infrastructure/pom.xml b/xfg-dev-tech-infrastructure/pom.xml new file mode 100644 index 0000000..bf0b4da --- /dev/null +++ b/xfg-dev-tech-infrastructure/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + cn.bugstack + xfg-dev-tech-mock + 1.0-SNAPSHOT + + + xfg-dev-tech-infrastructure + + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + + + com.squareup.retrofit2 + converter-gson + + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpmime + + + cn.bugstack + xfg-dev-tech-domain + 1.0-SNAPSHOT + + + + + xfg-dev-tech-infrastructure + + + \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/adapter/ZSXQAdapter.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/adapter/ZSXQAdapter.java new file mode 100644 index 0000000..2a5e0eb --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/adapter/ZSXQAdapter.java @@ -0,0 +1,65 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.adapter; + +import cn.bugstack.xfg.dev.tech.domain.zsxq.adapter.IZSXQAdapter; +import cn.bugstack.xfg.dev.tech.domain.zsxq.model.vo.TopicsItemVO; +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.api.IZSXQApi; +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto.RespData; +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto.ResponseDTO; +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto.TopicsItem; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 知识星球适配器接口 + * @create 2023-10-22 10:18 + */ +@Slf4j +@Service +public class ZSXQAdapter implements IZSXQAdapter { + + @Resource + private IZSXQApi zsxqApi; + + @Override + public List queryTopics() { + try { + ResponseDTO responseDTO = zsxqApi.topics(); + RespData respData = responseDTO.getRespData(); + List topics = respData.getTopics(); + List topicsItemVOList = new ArrayList<>(); + + for (TopicsItem topicsItem : topics) { + TopicsItemVO topicsItemVO = TopicsItemVO.builder() + .topicId(topicsItem.getTopicId()) + .talk(topicsItem.getTalk().getText()) + .showCommentsItems(topicsItem.getShowComments() != null ? topicsItem.getShowComments().stream() + .map(showCommentsItem -> { + TopicsItemVO.ShowCommentsItem item = new TopicsItemVO.ShowCommentsItem(); + item.setUserId(showCommentsItem.getOwner().getUserId()); + return item; + }) + .collect(Collectors.toList()) : new ArrayList<>()) + .build(); + + topicsItemVOList.add(topicsItemVO); + } + + return topicsItemVOList; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void comment(long topicId, String content) { + zsxqApi.comment(topicId, content); + } + +} diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/IZSXQApi.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/IZSXQApi.java new file mode 100644 index 0000000..663f1ca --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/IZSXQApi.java @@ -0,0 +1,30 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.api; + +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto.ResponseDTO; + +import java.io.IOException; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 知识星球API接口 + * @create 2023-10-22 09:47 + */ +public interface IZSXQApi { + + /** + * 查询知识星球帖子内容 + * + * @return 帖子数据 + * @throws IOException 异常 + */ + ResponseDTO topics() throws IOException; + + /** + * 回复帖子 + * + * @param topicId 帖子ID + * @param content 回复内容 + */ + void comment(long topicId, String content); + +} diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/impl/ZSXQApi.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/impl/ZSXQApi.java new file mode 100644 index 0000000..26c1fd1 --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/impl/ZSXQApi.java @@ -0,0 +1,112 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.api.impl; + +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.api.IZSXQApi; +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto.ResponseDTO; +import com.alibaba.fastjson2.JSON; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 知识星球Api实现 + * @create 2023-10-22 09:53 + */ +@Slf4j +@Component +public class ZSXQApi implements IZSXQApi { + + @Value("${zsxq.config.id}") + private String id; + @Value("${zsxq.config.cookie}") + private String cookie; + + @Override + public ResponseDTO topics() throws IOException { + CloseableHttpClient httpClient = HttpClientBuilder.create().build(); + + HttpGet get = new HttpGet("https://api.zsxq.com/v2/groups/" + id + "/topics?scope=all&count=20"); + + get.addHeader("Accept-Encoding", "deflate, gzip"); + get.addHeader("accept", "application/json, text/plain, */*"); + get.addHeader("accept-language", "zh-CN,zh;q=0.9,en;q=0.8"); + get.addHeader("authority", "api.zsxq.com"); + get.addHeader("cookie", cookie); + get.addHeader("dnt", "1"); + get.addHeader("origin", "https://wx.zsxq.com"); + get.addHeader("referer", "https://wx.zsxq.com/"); + get.addHeader("sec-ch-ua", "\"Chromium\";v=\"118\", \"Google Chrome\";v=\"118\", \"Not=A?Brand\";v=\"99\""); + get.addHeader("sec-ch-ua-mobile", "?0"); + get.addHeader("sec-ch-ua-platform", "\"macOS\""); + get.addHeader("sec-fetch-dest", "empty"); + get.addHeader("sec-fetch-mode", "cors"); + get.addHeader("sec-fetch-site", "same-site"); + get.addHeader("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"); + get.addHeader("x-request-id", "372177b46-4e7d-9373-d891-98a22adaeb7"); + get.addHeader("x-signature", "32b39b5d1af5995e3b5022e58a8d8f23cd427434"); + get.addHeader("x-timestamp", "1697249698"); + get.addHeader("x-version", "2.45.0"); + + CloseableHttpResponse response = httpClient.execute(get); + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + String jsonStr = EntityUtils.toString(response.getEntity()); + return JSON.parseObject(jsonStr, ResponseDTO.class); + } else { + throw new RuntimeException("Err Code is " + response.getStatusLine().getStatusCode()); + } + } + + @Override + public void comment(long topicId, String content) { + CloseableHttpClient httpClient = HttpClientBuilder.create().build(); + + HttpPost httpPost = new HttpPost("https://api.zsxq.com/v2/topics/" + topicId + "/comments"); + + httpPost.setHeader("Accept-Encoding", "deflate, gzip"); + httpPost.setHeader("accept", "application/json, text/plain, */*"); + httpPost.setHeader("accept-language", "zh-CN,zh;q=0.9,en;q=0.8"); + httpPost.setHeader("authority", "api.zsxq.com"); + httpPost.setHeader("content-type", "application/json"); + httpPost.setHeader("cookie", cookie); + httpPost.setHeader("dnt", "1"); + httpPost.setHeader("origin", "https://wx.zsxq.com"); + httpPost.setHeader("referer", "https://wx.zsxq.com/"); + httpPost.setHeader("sec-ch-ua", "\"Chromium\";v=\"118\", \"Google Chrome\";v=\"118\", \"Not=A?Brand\";v=\"99\""); + httpPost.setHeader("sec-ch-ua-mobile", "?0"); + httpPost.setHeader("sec-ch-ua-platform", "\"macOS\""); + httpPost.setHeader("sec-fetch-dest", "empty"); + httpPost.setHeader("sec-fetch-mode", "cors"); + httpPost.setHeader("sec-fetch-site", "same-site"); + httpPost.setHeader("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"); + httpPost.setHeader("x-request-id", "162ae5f17-2123-4ae3-67df-8b9775414e0"); + httpPost.setHeader("x-signature", "698895e3ec4e651128b3d16755546bd2bc659687"); + httpPost.setHeader("x-timestamp", "1697257286"); + httpPost.setHeader("x-version", "2.45.0"); + + String requestBody = "{\"req_data\":{\"text\":\"" + StringEscapeUtils.escapeJava(content) + "\",\"image_ids\":[],\"mentioned_user_ids\":[]}}"; + try { + httpPost.setEntity(new StringEntity(requestBody)); + HttpResponse response = httpClient.execute(httpPost); + HttpEntity entity = response.getEntity(); + String responseString = EntityUtils.toString(entity); + log.info("回贴结果 {}", responseString); + httpClient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Group.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Group.java new file mode 100644 index 0000000..042bcf7 --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Group.java @@ -0,0 +1,20 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class Group{ + + @SerializedName("group_id") + private long groupId; + + @SerializedName("background_url") + private String backgroundUrl; + + @SerializedName("name") + private String name; + + @SerializedName("type") + private String type; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Owner.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Owner.java new file mode 100644 index 0000000..8994d12 --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Owner.java @@ -0,0 +1,20 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class Owner{ + + @SerializedName("avatar_url") + private String avatarUrl; + + @SerializedName("user_id") + private long userId; + + @SerializedName("name") + private String name; + + @SerializedName("location") + private String location; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/RespData.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/RespData.java new file mode 100644 index 0000000..5420255 --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/RespData.java @@ -0,0 +1,13 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.List; + +@Data +public class RespData{ + + @SerializedName("topics") + private List topics; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ResponseDTO.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ResponseDTO.java new file mode 100644 index 0000000..eea7a31 --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ResponseDTO.java @@ -0,0 +1,14 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class ResponseDTO { + + @SerializedName("resp_data") + private RespData respData; + + @SerializedName("succeeded") + private boolean succeeded; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ShowCommentsItem.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ShowCommentsItem.java new file mode 100644 index 0000000..3fb8ad0 --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ShowCommentsItem.java @@ -0,0 +1,29 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class ShowCommentsItem{ + + @SerializedName("owner") + private Owner owner; + + @SerializedName("likes_count") + private int likesCount; + + @SerializedName("create_time") + private String createTime; + + @SerializedName("rewards_count") + private int rewardsCount; + + @SerializedName("sticky") + private boolean sticky; + + @SerializedName("text") + private String text; + + @SerializedName("comment_id") + private long commentId; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Talk.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Talk.java new file mode 100644 index 0000000..c79c6dd --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Talk.java @@ -0,0 +1,14 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class Talk{ + + @SerializedName("owner") + private Owner owner; + + @SerializedName("text") + private String text; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/TopicsItem.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/TopicsItem.java new file mode 100644 index 0000000..9a2e41e --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/TopicsItem.java @@ -0,0 +1,52 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.List; + +@Data +public class TopicsItem{ + + @SerializedName("reading_count") + private int readingCount; + + @SerializedName("create_time") + private String createTime; + + @SerializedName("user_specific") + private UserSpecific userSpecific; + + @SerializedName("rewards_count") + private int rewardsCount; + + @SerializedName("show_comments") + private List showComments; + + @SerializedName("type") + private String type; + + @SerializedName("digested") + private boolean digested; + + @SerializedName("likes_count") + private int likesCount; + + @SerializedName("comments_count") + private int commentsCount; + + @SerializedName("sticky") + private boolean sticky; + + @SerializedName("talk") + private Talk talk; + + @SerializedName("topic_id") + private long topicId; + + @SerializedName("readers_count") + private int readersCount; + + @SerializedName("group") + private Group group; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/UserSpecific.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/UserSpecific.java new file mode 100644 index 0000000..d5d9274 --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/UserSpecific.java @@ -0,0 +1,14 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class UserSpecific{ + + @SerializedName("subscribed") + private boolean subscribed; + + @SerializedName("liked") + private boolean liked; +} \ No newline at end of file diff --git a/xfg-dev-tech-trigger/pom.xml b/xfg-dev-tech-trigger/pom.xml new file mode 100644 index 0000000..838aefc --- /dev/null +++ b/xfg-dev-tech-trigger/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + cn.bugstack + xfg-dev-tech-mock + 1.0-SNAPSHOT + + + xfg-dev-tech-trigger + + + + org.springframework.boot + spring-boot-starter-web + + + com.alibaba + fastjson + + + org.springframework + spring-tx + + + org.apache.commons + commons-lang3 + + + org.projectlombok + lombok + 1.18.26 + + + org.springframework + spring-context + + + cn.bugstack + xfg-dev-tech-domain + 1.0-SNAPSHOT + compile + + + + + xfg-dev-tech-trigger + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + ${java.version} + + + + + + \ No newline at end of file diff --git a/xfg-dev-tech-trigger/src/main/java/cn/bugstack/xfg/dev/tech/job/ReplyJob.java b/xfg-dev-tech-trigger/src/main/java/cn/bugstack/xfg/dev/tech/job/ReplyJob.java new file mode 100644 index 0000000..1336057 --- /dev/null +++ b/xfg-dev-tech-trigger/src/main/java/cn/bugstack/xfg/dev/tech/job/ReplyJob.java @@ -0,0 +1,27 @@ +package cn.bugstack.xfg.dev.tech.job; + +import cn.bugstack.xfg.dev.tech.domain.zsxq.service.IAiReply; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 回帖JOB + * @create 2023-10-22 11:02 + */ +@Slf4j +@Component() +public class ReplyJob { + + @Resource + private IAiReply aiReply; + + @Scheduled(cron = "0/10 * * * * ?") + public void exec() throws Exception { + aiReply.doAiReply(); + } + +} -- GitLab