├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── xzy
│ │ └── wechatmsg
│ │ ├── WechatMessageApplication.java
│ │ ├── annotation
│ │ └── CronMethod.java
│ │ ├── bo
│ │ ├── WechatMsgWithInfoAndType.java
│ │ └── WrappedCronTask.java
│ │ ├── client
│ │ └── WechatRobotClient.java
│ │ ├── config
│ │ ├── RestTemplateConfig.java
│ │ └── ScheduleConfig.java
│ │ ├── constant
│ │ └── ExceptionConstants.java
│ │ ├── controller
│ │ ├── RobotMsgHandleController.java
│ │ └── ScheduleTaskController.java
│ │ ├── domain
│ │ ├── chatgpt
│ │ │ └── model
│ │ │ │ ├── ChatGptReq30.java
│ │ │ │ ├── ChatGptReq35.java
│ │ │ │ ├── ChatGptResp30.java
│ │ │ │ └── ChatGptResp35.java
│ │ ├── robot
│ │ │ └── model
│ │ │ │ ├── WechatMsgDTO.java
│ │ │ │ ├── WechatRsvMsgDTO.java
│ │ │ │ ├── WechatRsvPicMsg.java
│ │ │ │ └── WechatRsvTxtMsg.java
│ │ └── task
│ │ │ ├── mapper
│ │ │ └── TaskMapper.java
│ │ │ ├── model
│ │ │ └── Task.java
│ │ │ └── repository
│ │ │ └── TaskRepository.java
│ │ ├── enums
│ │ ├── TaskStatusEnum.java
│ │ ├── TaskTypeEnum.java
│ │ └── WechatMsgTypeEnum.java
│ │ ├── exception
│ │ └── task
│ │ │ ├── BaseTaskException.java
│ │ │ ├── CustomMethodInvokeException.java
│ │ │ ├── NoSuchMsgTypeException.java
│ │ │ ├── NoSuchTaskIdException.java
│ │ │ └── TaskInvokeException.java
│ │ ├── interceptor
│ │ └── TaskControllerAdvisor.java
│ │ ├── manager
│ │ ├── robot
│ │ │ └── RobotMsgHandler.java
│ │ └── task
│ │ │ ├── AbstractTaskHandler.java
│ │ │ ├── AppTaskHandler.java
│ │ │ ├── BaseCustomMethodHandler.java
│ │ │ ├── CustomMethodHandler.java
│ │ │ ├── DbTaskHandler.java
│ │ │ └── RunnableWechatMsgFactory.java
│ │ ├── request
│ │ └── task
│ │ │ └── TaskRequest.java
│ │ ├── service
│ │ ├── RobotMsgHandleService.java
│ │ ├── ScheduleTaskService.java
│ │ └── impl
│ │ │ ├── RobotMsgHandleServiceImpl.java
│ │ │ └── ScheduleTaskServiceImpl.java
│ │ ├── utils
│ │ └── TimeUtils.java
│ │ └── vo
│ │ ├── BaseResponseVO.java
│ │ └── task
│ │ └── TaskResponseVO.java
└── resources
│ └── application-demo.yml
└── test
└── java
└── com
└── xzy
└── wechatmsg
└── WechatMessageApplicationTests.java
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
35 | src/main/resources/application.yml
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Zayn Xie
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WechatMessage
2 | 搭配WechatRobot使用,可设置定时任务、自动回复、处理历史消息等
3 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.7.3
9 |
10 |
11 | com.xzy
12 | wechat-msg
13 | 0.0.1-SNAPSHOT
14 | WechatMessage
15 | WechatMessage
16 |
17 | 1.8
18 |
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-web
23 |
24 |
25 |
26 | org.projectlombok
27 | lombok
28 | true
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-test
33 | test
34 |
35 |
36 | com.baomidou
37 | mybatis-plus-boot-starter
38 | 3.4.1
39 |
40 |
41 | org.mybatis
42 | mybatis-spring
43 | 2.0.7
44 |
45 |
46 | mysql
47 | mysql-connector-java
48 | runtime
49 | 5.1.47
50 |
51 |
52 | com.alibaba
53 | fastjson
54 | 1.2.76
55 |
56 |
57 | cn.hutool
58 | hutool-http
59 | 5.6.5
60 |
61 |
62 |
63 |
64 |
65 |
66 | org.springframework.boot
67 | spring-boot-maven-plugin
68 |
69 |
70 |
71 | org.projectlombok
72 | lombok
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/WechatMessageApplication.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg;
2 |
3 | import com.xzy.wechatmsg.bo.WrappedCronTask;
4 | import com.xzy.wechatmsg.manager.task.AppTaskHandler;
5 | import org.mybatis.spring.annotation.MapperScan;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.autoconfigure.SpringBootApplication;
8 | import org.springframework.scheduling.annotation.EnableScheduling;
9 |
10 | import java.time.LocalDateTime;
11 | import java.util.List;
12 | import java.util.concurrent.TimeUnit;
13 |
14 | @EnableScheduling
15 | @MapperScan("com.xzy.wechatmsg.domain.task.mapper")
16 | @SpringBootApplication
17 | public class WechatMessageApplication {
18 |
19 | public static void main(String[] args) {
20 | String proxyHost = "127.0.0.1";
21 | String proxyPort = "7890";
22 |
23 | System.setProperty("http.proxyHost", proxyHost);
24 | System.setProperty("http.proxyPort", proxyPort);
25 |
26 | System.setProperty("https.proxyHost", proxyHost);
27 | System.setProperty("https.proxyPort", proxyPort);
28 |
29 | SpringApplication.run(WechatMessageApplication.class, args);
30 | // AppTaskHandler appTaskHandler = new AppTaskHandler();
31 | // List wrappedCronTaskList = appTaskHandler.getWrappedCronTaskList();
32 | // while (true) {
33 | // try {
34 | // System.out.println("------------------" + LocalDateTime.now() + "---------------------------");
35 | // wrappedCronTaskList.forEach(System.out::println);
36 | // TimeUnit.SECONDS.sleep(10);
37 | // } catch (InterruptedException e) {
38 | // e.printStackTrace();
39 | // }
40 | // }
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/annotation/CronMethod.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target({ElementType.METHOD})
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface CronMethod {
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/bo/WechatMsgWithInfoAndType.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.bo;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.xzy.wechatmsg.enums.WechatMsgTypeEnum;
5 | import lombok.AllArgsConstructor;
6 | import lombok.Builder;
7 | import lombok.Data;
8 | import lombok.NoArgsConstructor;
9 |
10 | /**
11 | * @description: WechatMsgWithInfoAndType
12 | * @author: Xie zy
13 | * @create: 2022.09.29
14 | */
15 | @AllArgsConstructor
16 | @NoArgsConstructor
17 | @Builder
18 | @Data
19 | public class WechatMsgWithInfoAndType {
20 | @JsonProperty("msg_info")
21 | WechatMsg msgInfo;
22 | @JsonProperty("msg_type")
23 | WechatMsgTypeEnum msgType;
24 |
25 | @AllArgsConstructor
26 | @NoArgsConstructor
27 | @Builder
28 | @Data
29 | public static class WechatMsg {
30 | @JsonProperty("wxid")
31 | String wxId;
32 | @JsonProperty("content")
33 | String content;
34 | @JsonProperty("roomid")
35 | String roomId;
36 | @JsonProperty("nickname")
37 | String nickName;
38 | @JsonProperty("custom_method_name")
39 | String customMethodName;
40 | @JsonProperty("custom_method_param")
41 | String customMethodParam;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/bo/WrappedCronTask.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.bo;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import org.springframework.scheduling.config.CronTask;
5 |
6 | import java.time.LocalDateTime;
7 |
8 | /**
9 | * @description: WrappedCronTask
10 | * @author: Xie zy
11 | * @create: 2022.09.02
12 | */
13 | public class WrappedCronTask extends CronTask {
14 |
15 | private Integer taskId;
16 | private String msg;
17 | private String cron;
18 | private Integer status;
19 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
20 | LocalDateTime createTime;
21 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
22 | LocalDateTime updateTime;
23 |
24 | public WrappedCronTask(Integer taskId,
25 | String msg,
26 | String cron,
27 | Integer status,
28 | LocalDateTime createTime,
29 | LocalDateTime updateTime,
30 | Runnable runnable) {
31 | super(runnable, cron);
32 | this.taskId = taskId;
33 | this.msg = msg;
34 | this.cron = cron;
35 | this.status = status;
36 | this.createTime = createTime;
37 | this.updateTime = updateTime;
38 | }
39 |
40 | public Integer getTaskId() {
41 | return taskId;
42 | }
43 |
44 | public void setTaskId(Integer taskId) {
45 | this.taskId = taskId;
46 | }
47 |
48 | public String getMsg() {
49 | return msg;
50 | }
51 |
52 | public void setMsg(String msg) {
53 | this.msg = msg;
54 | }
55 |
56 | public String getCron() {
57 | return cron;
58 | }
59 |
60 | public void setCron(String cron) {
61 | this.cron = cron;
62 | }
63 |
64 | public Integer getStatus() {
65 | return status;
66 | }
67 |
68 | public void setStatus(Integer status) {
69 | this.status = status;
70 | }
71 |
72 | public LocalDateTime getCreateTime() {
73 | return createTime;
74 | }
75 |
76 | public void setCreateTime(LocalDateTime createTime) {
77 | this.createTime = createTime;
78 | }
79 |
80 | public LocalDateTime getUpdateTime() {
81 | return updateTime;
82 | }
83 |
84 | public void setUpdateTime(LocalDateTime updateTime) {
85 | this.updateTime = updateTime;
86 | }
87 |
88 | @Override
89 | public String toString() {
90 | return "WrappedCronTask{" +
91 | "taskId=" + taskId +
92 | ", msg='" + msg + '\'' +
93 | ", cron='" + cron + '\'' +
94 | ", status=" + status +
95 | ", createTime=" + createTime +
96 | ", updateTime=" + updateTime +
97 | '}';
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/client/WechatRobotClient.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.client;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.xzy.wechatmsg.bo.WechatMsgWithInfoAndType;
5 | import com.xzy.wechatmsg.domain.robot.model.WechatMsgDTO;
6 | import com.xzy.wechatmsg.domain.robot.model.WechatRsvMsgDTO;
7 | import com.xzy.wechatmsg.exception.task.CustomMethodInvokeException;
8 | import com.xzy.wechatmsg.exception.task.TaskInvokeException;
9 | import com.xzy.wechatmsg.manager.robot.RobotMsgHandler;
10 | import com.xzy.wechatmsg.manager.task.CustomMethodHandler;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.beans.factory.annotation.Value;
13 | import org.springframework.http.HttpEntity;
14 | import org.springframework.http.HttpMethod;
15 | import org.springframework.http.ResponseEntity;
16 | import org.springframework.stereotype.Component;
17 | import org.springframework.web.client.RestTemplate;
18 |
19 | import java.lang.reflect.Method;
20 |
21 | /**
22 | * @description: WechatClient
23 | * @author: Xie zy
24 | * @create: 2022.08.31
25 | */
26 | @Component
27 | public class WechatRobotClient {
28 |
29 | @Value(value = "${wechat.url}")
30 | private String url;
31 |
32 | private final String SEND_PATH_XIAONIUREN_TEMPLATE = "/xiaoniuren?msg={msg}";
33 |
34 | private final String SEND_PATH_SEND_TEXT_MSG = "/sendTextMsg";
35 |
36 | private final String SEND_PATH_SEND_IMG_MSG = "/sendImgMsg";
37 |
38 | private final String SEND_PATH_SEND_AT_MSG = "/sendATMsg";
39 |
40 | private final String SEND_PATH_SEND_ANNEX = "/sendAnnex";
41 |
42 | private final String GET_PATH_WECHAT_USER_LIST = "/getWeChatUserList";
43 |
44 | private final String GET_PATH_GET_MEMBER_ID = "/getMemberId";
45 |
46 | private final String GET_PATH_GET_CHATROOM_MEMBER_NICK_TEMPLATE = "/getChatroomMemberNick";
47 |
48 | @Autowired
49 | RestTemplate restTemplate;
50 |
51 | @Autowired
52 | HttpEntity httpEntity;
53 |
54 | @Autowired
55 | RobotMsgHandler robotMsgHandler;
56 |
57 | @Autowired
58 | CustomMethodHandler customMethodHandler;
59 |
60 | public void sendToXiaoniuren(String msg) {
61 | String url = this.url + SEND_PATH_XIAONIUREN_TEMPLATE;
62 | restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class, msg);
63 | }
64 |
65 | public void sendTextMsg(WechatMsgWithInfoAndType.WechatMsg msg) {
66 | String url = this.url + SEND_PATH_SEND_TEXT_MSG;
67 | HttpEntity request = new HttpEntity<>(msg, httpEntity.getHeaders());
68 | restTemplate.exchange(url, HttpMethod.POST, request, String.class);
69 | }
70 |
71 | public void sendTextMsgWithCustomMethod(WechatMsgWithInfoAndType.WechatMsg msg) {
72 | String customMethodName = msg.getCustomMethodName();
73 | String customMethodParam = msg.getCustomMethodParam();
74 | try {
75 | Method method = customMethodHandler.getClass().getDeclaredMethod(customMethodName, String.class);
76 | method.setAccessible(true);
77 | method.invoke(customMethodHandler, customMethodParam);
78 | } catch (Exception e) {
79 | throw new CustomMethodInvokeException();
80 | }
81 | }
82 |
83 | public void sendImgMsg(WechatMsgWithInfoAndType.WechatMsg msg) {
84 | String url = this.url + SEND_PATH_SEND_IMG_MSG;
85 | HttpEntity request = new HttpEntity<>(robotMsgHandler.buildWechatMsg(msg), httpEntity.getHeaders());
86 | restTemplate.exchange(url, HttpMethod.POST, request, String.class);
87 | }
88 |
89 | public void sendAtMsg(WechatMsgWithInfoAndType.WechatMsg msg) {
90 | String url = this.url + SEND_PATH_SEND_AT_MSG;
91 | HttpEntity request = new HttpEntity<>(robotMsgHandler.buildWechatMsg(msg), httpEntity.getHeaders());
92 | restTemplate.exchange(url, HttpMethod.POST, request, String.class);
93 | }
94 |
95 | public void sendAnnex(WechatMsgWithInfoAndType.WechatMsg msg) {
96 | String url = this.url + SEND_PATH_SEND_ANNEX;
97 | HttpEntity request = new HttpEntity<>(robotMsgHandler.buildWechatMsg(msg), httpEntity.getHeaders());
98 | restTemplate.exchange(url, HttpMethod.POST, request, String.class);
99 | }
100 |
101 | // public void getWeChatUserList(WechatMsgWithInfoAndType.WechatMsg msg){
102 | // String url = this.url + GET_PATH_WECHAT_USER_LIST;
103 | // restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class, msg);
104 | // }
105 | //
106 | // public void getMemberId(WechatMsgWithInfoAndType.WechatMsg msg){
107 | // String url = this.url + GET_PATH_GET_MEMBER_ID;
108 | // restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class, msg);
109 | // }
110 |
111 | public String getChatroomMemberNick(String roomId, String userId){
112 | String url = this.url + GET_PATH_GET_CHATROOM_MEMBER_NICK_TEMPLATE + "/" + roomId + "/" + userId;
113 | ResponseEntity res = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
114 | WechatRsvMsgDTO wechatRsvMsgDTO = JSON.parseObject(res.getBody(), WechatRsvMsgDTO.class);
115 | String content = wechatRsvMsgDTO.getContent();
116 | WechatRsvMsgDTO.NickNameResp nickNameResp = JSON.parseObject(content, WechatRsvMsgDTO.NickNameResp.class);
117 | return nickNameResp.getNick();
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/config/RestTemplateConfig.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.http.HttpEntity;
6 | import org.springframework.http.HttpHeaders;
7 | import org.springframework.http.MediaType;
8 | import org.springframework.http.client.ClientHttpRequestFactory;
9 | import org.springframework.http.client.SimpleClientHttpRequestFactory;
10 | import org.springframework.web.client.RestTemplate;
11 |
12 | /**
13 | * @description: RestTemplateConfig
14 | * @author: Xie zy
15 | * @create: 2022.08.31
16 | */
17 | @Configuration
18 | public class RestTemplateConfig {
19 |
20 | @Bean
21 | public HttpEntity httpEntity() {
22 | HttpHeaders headers = new HttpHeaders();
23 | headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
24 | return new HttpEntity(headers);
25 | }
26 |
27 | @Bean
28 | public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
29 | RestTemplate restTemplate = new RestTemplate(factory);
30 | return restTemplate;
31 | }
32 |
33 | @Bean
34 | public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
35 | SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
36 | factory.setReadTimeout(5000);
37 | factory.setConnectTimeout(15000);
38 | // 设置代理
39 | //factory.setProxy(null);
40 | return factory;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/config/ScheduleConfig.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.config;
2 |
3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
5 | import com.xzy.wechatmsg.bo.WrappedCronTask;
6 | import com.xzy.wechatmsg.domain.task.model.Task;
7 | import com.xzy.wechatmsg.enums.TaskStatusEnum;
8 | import com.xzy.wechatmsg.manager.task.AppTaskHandler;
9 | import com.xzy.wechatmsg.domain.task.mapper.TaskMapper;
10 | import com.xzy.wechatmsg.manager.task.RunnableWechatMsgFactory;
11 | import com.xzy.wechatmsg.utils.TimeUtils;
12 | import org.springframework.beans.factory.annotation.Autowired;
13 | import org.springframework.context.annotation.Bean;
14 | import org.springframework.context.annotation.Configuration;
15 | import org.springframework.scheduling.TaskScheduler;
16 | import org.springframework.scheduling.annotation.SchedulingConfigurer;
17 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
18 | import org.springframework.scheduling.config.ScheduledTaskRegistrar;
19 |
20 | import java.time.LocalDateTime;
21 | import java.util.HashMap;
22 | import java.util.List;
23 | import java.util.concurrent.CountDownLatch;
24 | import java.util.concurrent.ThreadPoolExecutor;
25 |
26 | /**
27 | * @description: ScheduleConfig
28 | * @author: Xie zy
29 | * @create: 2022.08.31
30 | */
31 | @Configuration
32 | public class ScheduleConfig implements SchedulingConfigurer {
33 |
34 | @Autowired
35 | RunnableWechatMsgFactory runnableWechatMsgFactory;
36 |
37 | @Autowired
38 | TaskMapper taskMapper;
39 |
40 | private volatile ScheduledTaskRegistrar taskRegistrar;
41 |
42 | private CountDownLatch countDownLatch = new CountDownLatch(1);
43 |
44 | public ScheduledTaskRegistrar getScheduledTaskRegistrar() {
45 | try {
46 | //等configureTasks方法中放入的注册器
47 | countDownLatch.await();
48 | } catch (InterruptedException e) {
49 | e.printStackTrace();
50 | }
51 | return this.taskRegistrar;
52 | }
53 |
54 | @Override
55 | public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
56 | //注册器中配置定时任务的执行期,SpringBoot对@Configuration的类会特殊处理,所以加入的是本类中的执行期bean
57 | taskRegistrar.setTaskScheduler(taskScheduler());
58 |
59 | //查询数据库中Running状态任务
60 | LambdaQueryWrapper wrapper = new QueryWrapper().lambda();
61 | wrapper.eq(Task::getStatus, TaskStatusEnum.TASK_RUNNING.getValue());
62 | List dbRunningTasks = taskMapper.selectList(wrapper);
63 | //初始化
64 | AppTaskHandler appTaskHandler = new AppTaskHandler();
65 | dbRunningTasks.forEach(dbRunningTask -> {
66 | //取出数据库的值
67 | Integer id = dbRunningTask.getId();
68 | String msg = dbRunningTask.getMsg();
69 | String cron = dbRunningTask.getCron();
70 | Integer status = dbRunningTask.getStatus();
71 | LocalDateTime createTime = dbRunningTask.getCreateTime();
72 | LocalDateTime updateTime = dbRunningTask.getUpdateTime();
73 | //构造runnable
74 | Runnable runnable = runnableWechatMsgFactory.createRunnable(msg);
75 | //Running且updateTime小于2小时的task,就说明这个这个task因为SpringBoot应用停止运行挂过一段时间(一般一个小时以上,特殊情况可能很短,但是也一定挂掉过)
76 | //那就不启动数据库中过时的Running状态的task,并将状态设置成stop,等待用户手动启动各个挂掉过的task
77 | //场景:挂掉以后,用户发现前端的updateTime还是很久之前的,于是重启SpringBoot应用,启动成功以后,给出用户反馈(立即将过期的设置为stop表示启动成功了)
78 | //原本意思是让另一个应用去监控这个应用= =
79 | if (TimeUtils.isUpdateTimeExpired(updateTime)) {
80 | taskMapper.updateStatusWithOutUpdateTime(id, TaskStatusEnum.TASK_RUNNING.getValue(), TaskStatusEnum.TASK_STOP.getValue());
81 | return;
82 | }
83 | //appTask双写
84 | appTaskHandler.addWrappedCronTask(new WrappedCronTask(id, msg, cron, status, createTime, updateTime, runnable), true, taskRegistrar);
85 | });
86 | //应用启动时增加刷新updateTime的cronTask,每小时刷一次,这个不用双写,因为不需要wrapped管理
87 | HashMap cronTasks = new HashMap<>();
88 | cronTasks.put(() -> {
89 | appTaskHandler.getWrappedCronTaskList().forEach((wrappedCronTask) -> {
90 | taskMapper.updateStatusWithUpdateTime(wrappedCronTask.getTaskId(), TaskStatusEnum.TASK_RUNNING.getValue(), TaskStatusEnum.TASK_RUNNING.getValue(), LocalDateTime.now());
91 | });
92 | }, "0 0 * * * ?");
93 | taskRegistrar.setCronTasks(cronTasks);
94 |
95 | //保留注册器
96 | this.taskRegistrar = taskRegistrar;
97 | countDownLatch.countDown();
98 | }
99 |
100 | @Bean
101 | public TaskScheduler taskScheduler() {
102 | ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
103 | taskScheduler.setPoolSize(10);
104 | taskScheduler.setThreadNamePrefix("spring-task-scheduler-thread-");
105 | taskScheduler.setAwaitTerminationSeconds(60);
106 | taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
107 | taskScheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
108 | return taskScheduler;
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/constant/ExceptionConstants.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.constant;
2 |
3 | /**
4 | * @description: ExceptionConstants
5 | * @author: Xie zy
6 | * @create: 2022.09.02
7 | */
8 | public class ExceptionConstants {
9 |
10 | public static final String BASE_TASK_EXCEPTION_MSG = "定时任务异常";
11 | public static final String NO_SUCH_TASK_ID_EXCEPTION_MSG = "没有这个任务ID";
12 | public static final String TASK_INVOKE_FAILED = "定时任务运行失败";
13 | public static final String NO_SUCH_MSG_TYPE = "没有这种消息类型";
14 | public static final String CUSTOM_METHOD_INVOKE_EXCEPTION = "自定义定时任务运行异常";
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/controller/RobotMsgHandleController.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.controller;
2 |
3 | import com.xzy.wechatmsg.domain.robot.model.WechatRsvMsgDTO;
4 | import com.xzy.wechatmsg.manager.robot.RobotMsgHandler;
5 | import com.xzy.wechatmsg.service.RobotMsgHandleService;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.web.bind.annotation.RequestBody;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RequestParam;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 |
13 | /**
14 | * @description: RobotMsgHandleController
15 | * @author: Xie zy
16 | * @create: 2023.01.30
17 | */
18 | @RequestMapping("/wechat-rsv-msg")
19 | @RestController
20 | public class RobotMsgHandleController {
21 |
22 | @Autowired
23 | RobotMsgHandleService robotMsgHandleService;
24 |
25 | @Autowired
26 | RobotMsgHandler robotMsgHandler;
27 |
28 | @RequestMapping("/txt")
29 | public void handleTxt(@RequestBody WechatRsvMsgDTO rsvMsg) {
30 | robotMsgHandleService.handleTxtMsg(rsvMsg);
31 | }
32 |
33 | @RequestMapping("/pic")
34 | public void handlePic(@RequestBody WechatRsvMsgDTO rsvMsg) {
35 | robotMsgHandleService.handlePicMsg(rsvMsg);
36 | }
37 |
38 | @RequestMapping("/testOpenai")
39 | public String handlePic(@RequestParam String input) {
40 | return robotMsgHandler.chat(input, false);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/controller/ScheduleTaskController.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.controller;
2 |
3 | import com.xzy.wechatmsg.request.task.TaskRequest;
4 | import com.xzy.wechatmsg.service.ScheduleTaskService;
5 | import com.xzy.wechatmsg.vo.BaseResponseVO;
6 | import com.xzy.wechatmsg.vo.task.TaskResponseVO;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.web.bind.annotation.RequestBody;
9 | import org.springframework.web.bind.annotation.RequestMapping;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 | import java.util.List;
13 |
14 | /**
15 | * @description: ScheduleTaskController
16 | * @author: Xie zy
17 | * @create: 2022.08.31
18 | */
19 | @RequestMapping("/schedule-task")
20 | @RestController
21 | public class ScheduleTaskController {
22 |
23 | @Autowired
24 | ScheduleTaskService scheduleTaskService;
25 |
26 | @RequestMapping("/create")
27 | public BaseResponseVO createTasks(@RequestBody TaskRequest taskRequest) {
28 | scheduleTaskService.createTasks(taskRequest);
29 | return BaseResponseVO.success();
30 | }
31 |
32 | @RequestMapping("/read")
33 | public BaseResponseVO readTasks() {
34 | List taskVOS = scheduleTaskService.readTasks();
35 | return TaskResponseVO.success(taskVOS);
36 | }
37 |
38 | @RequestMapping("/update")
39 | public BaseResponseVO updateTasks(@RequestBody TaskRequest taskRequest) {
40 | scheduleTaskService.updateTasks(taskRequest);
41 | return BaseResponseVO.success();
42 | }
43 |
44 | @RequestMapping("/delete")
45 | public BaseResponseVO deleteTasks(@RequestBody TaskRequest taskRequest) {
46 | scheduleTaskService.deleteTasks(taskRequest);
47 | return BaseResponseVO.success();
48 | }
49 |
50 | /**让容器中的任务和数据库中保持一致
51 | *
52 | * @return vo
53 | */
54 | @RequestMapping("/refreshApp")
55 | public BaseResponseVO refreshApp() {
56 | scheduleTaskService.refreshApp();
57 | return TaskResponseVO.success();
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/domain/chatgpt/model/ChatGptReq30.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.domain.chatgpt.model;
2 |
3 | import lombok.Data;
4 | import lombok.NoArgsConstructor;
5 |
6 | /**
7 | * @description: Body
8 | * @author: Xie zy
9 | * @create: 2023.02.02
10 | */
11 | @Data
12 | @NoArgsConstructor
13 | public class ChatGptReq30 {
14 | String model;
15 | String prompt;
16 | Integer max_tokens;
17 | Float temperature;
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/domain/chatgpt/model/ChatGptReq35.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.domain.chatgpt.model;
2 |
3 | import lombok.Data;
4 | import lombok.NoArgsConstructor;
5 |
6 | import java.util.List;
7 | import java.util.Map;
8 |
9 | /**
10 | * @description: ChatGptReq35
11 | * @author: Xie zy
12 | * @create: 2023.05.19
13 | */
14 | @Data
15 | @NoArgsConstructor
16 | public class ChatGptReq35 {
17 | String model;
18 | List messages;
19 | // Integer max_tokens;
20 | Float temperature;
21 |
22 | @Data
23 | @NoArgsConstructor
24 | public static class Message {
25 | String role;
26 | String content;
27 | }
28 | }
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/domain/chatgpt/model/ChatGptResp30.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.domain.chatgpt.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * @description: ChatGptResp
11 | * @author: Xie zy
12 | * @create: 2023.02.02
13 | */
14 | @Data
15 | @NoArgsConstructor
16 | public class ChatGptResp30 {
17 | String id;
18 | String object;
19 | Integer created;
20 | String model;
21 | List choices;
22 | Usage usage;
23 |
24 | @Data
25 | @NoArgsConstructor
26 | public static class Choice {
27 | String text;
28 | Integer index;
29 | List logprobs;
30 | @JsonProperty("finish_reason")
31 | String finishReason;
32 | }
33 |
34 | @Data
35 | @NoArgsConstructor
36 | public static class Usage {
37 | @JsonProperty("prompt_tokens")
38 | Integer promptTokens;
39 | @JsonProperty("completion_tokens")
40 | Integer completionTokens;
41 | @JsonProperty("total_tokens")
42 | Integer totalTokens;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/domain/chatgpt/model/ChatGptResp35.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.domain.chatgpt.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * @description: ChatGptResp35
11 | * @author: Xie zy
12 | * @create: 2023.05.19
13 | */
14 | @Data
15 | @NoArgsConstructor
16 | public class ChatGptResp35 {
17 | String id;
18 | String object;
19 | Integer created;
20 | String model;
21 | List choices;
22 | Usage usage;
23 |
24 | @Data
25 | @NoArgsConstructor
26 | public static class Choice {
27 | Integer index;
28 | Message message;
29 | @JsonProperty("finish_reason")
30 | String finishReason;
31 | }
32 |
33 | @Data
34 | @NoArgsConstructor
35 | public static class Message {
36 | String role;
37 | String content;
38 | }
39 |
40 | @Data
41 | @NoArgsConstructor
42 | public static class Usage {
43 | @JsonProperty("prompt_tokens")
44 | Integer promptTokens;
45 | @JsonProperty("completion_tokens")
46 | Integer completionTokens;
47 | @JsonProperty("total_tokens")
48 | Integer totalTokens;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/domain/robot/model/WechatMsgDTO.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.domain.robot.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import lombok.Data;
5 |
6 | /**
7 | * @description: WechatMsg
8 | * @author: Xie zy
9 | * @create: 2023.01.30
10 | */
11 | @Data
12 | public class WechatMsgDTO {
13 | /** 消息id */
14 | private String id;
15 | /** 接收消息人的 微信原始id */
16 | @JsonProperty("wxid")
17 | private String wxId;
18 | /** 消息内容 */
19 | private String content;
20 | /** 群组id 群组内发送@消息时使用 */
21 | @JsonProperty("roomid")
22 | private String roomId;
23 | /** 发送消息类型 */
24 | private Integer type;
25 | /** 昵称 */
26 | @JsonProperty("nickname")
27 | private String nickName;
28 | /** 图片消息的图片地址(绝对路径 D:/xxx.jpg) */
29 | private String path;
30 | private String ext;
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/domain/robot/model/WechatRsvMsgDTO.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.domain.robot.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | import java.util.Date;
9 | import java.util.List;
10 |
11 | /**
12 | * @description: WechatRsvMsg
13 | * @author: Xie zy
14 | * @create: 2023.01.30
15 | */
16 | @Data
17 | public class WechatRsvMsgDTO {
18 | private static final long serialVersionUID = 1L;
19 |
20 | /** 微信消息编号 */
21 | private Long wechatReceiveMsgId;
22 | /** 消息id */
23 | private String id;
24 | /** 消息内容 */
25 | private String content;
26 | /** 群组里消息发送人, 私人对话这个字段为空 */
27 | private String id1;
28 | /** 群组里消息为 群组id, 个人的对话 为个人微信id */
29 | private String id2;
30 | /** srv_id */
31 | private Long srvid;
32 | /** 接收消息时间 */
33 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
34 | private Date time;
35 | /** 接收消息类型 */
36 | private Integer type;
37 | /** 发送消息得对话框id 个人是个人得微信id,群组是群组得id带 */
38 | private String wxid;
39 |
40 | @Data
41 | @NoArgsConstructor
42 | public static class NickNameResp {
43 | String nick;
44 | String roomid;
45 | String wxid;
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/domain/robot/model/WechatRsvPicMsg.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.domain.robot.model;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @description: WechatRsvPicMsg
7 | * @author: Xie zy
8 | * @create: 2023.01.31
9 | */
10 | @Data
11 | public class WechatRsvPicMsg {
12 | //TODO
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/domain/robot/model/WechatRsvTxtMsg.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.domain.robot.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import lombok.Data;
5 |
6 | import java.util.Date;
7 |
8 | /**
9 | * @description: WechatRsvTxtMsg
10 | * @author: Xie zy
11 | * @create: 2023.01.31
12 | */
13 | @Data
14 | public class WechatRsvTxtMsg {
15 | /** 发送时间戳 */
16 | private String id;
17 | /** txt内容 */
18 | private String content;
19 | /** 群组里消息发送人, 私人对话这个字段为空 */
20 | private String id1;
21 | /** 群组里消息为 群组id, 个人的对话 为个人微信id */
22 | private String id2;
23 | /** srv_id */
24 | private Long srvid;
25 | /** 接收消息时间 */
26 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
27 | private Date time;
28 | /** 接收消息类型 */
29 | private Integer type;
30 | /** 发送消息得对话框id 个人是个人得微信id,群组是群组得id带 */
31 | private String wxid;
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/domain/task/mapper/TaskMapper.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.domain.task.mapper;
2 |
3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 | import com.xzy.wechatmsg.domain.task.model.Task;
5 | import org.apache.ibatis.annotations.Param;
6 | import org.apache.ibatis.annotations.Select;
7 |
8 | import java.time.LocalDateTime;
9 |
10 | /**
11 | * @description: TaskMapper
12 | * @author: Xie zy
13 | * @create: 2022.09.02
14 | */
15 | public interface TaskMapper extends BaseMapper {
16 |
17 | @Select("UPDATE task SET status = #{new_status},update_time = #{update_time} WHERE status = #{pre_status} AND id = #{id}")
18 | void updateStatusWithUpdateTime(@Param("id") Integer id, @Param("pre_status") Integer preStatus, @Param("new_status") Integer newStatus, @Param("update_time") LocalDateTime updateTime);
19 |
20 | @Select("UPDATE task SET status = #{new_status} WHERE status = #{pre_status} AND id = #{id}")
21 | void updateStatusWithOutUpdateTime(@Param("id") Integer id, @Param("pre_status") Integer preStatus, @Param("new_status") Integer newStatus);
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/domain/task/model/Task.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.domain.task.model;
2 |
3 | import com.baomidou.mybatisplus.annotation.IdType;
4 | import com.baomidou.mybatisplus.annotation.TableId;
5 | import com.baomidou.mybatisplus.annotation.TableName;
6 | import com.fasterxml.jackson.annotation.JsonFormat;
7 | import lombok.AllArgsConstructor;
8 | import lombok.Builder;
9 | import lombok.Data;
10 | import lombok.NoArgsConstructor;
11 |
12 | import java.time.LocalDateTime;
13 |
14 | /**
15 | * @description: Task
16 | * @author: Xie zy
17 | * @create: 2022.09.02
18 | */
19 | @Data
20 | @Builder
21 | @NoArgsConstructor
22 | @AllArgsConstructor
23 | @TableName(value = "task", autoResultMap = true)
24 | public class Task {
25 | @TableId(type = IdType.AUTO, value = "id")
26 | Integer id;
27 | String msg;
28 | String cron;
29 | /**
30 | * 0-运行结束 | 1-正在运行
31 | */
32 | Integer status;
33 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
34 | LocalDateTime createTime;
35 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
36 | LocalDateTime updateTime;
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/domain/task/repository/TaskRepository.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.domain.task.repository;
2 |
3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
5 | import com.xzy.wechatmsg.domain.task.model.Task;
6 | import com.xzy.wechatmsg.exception.task.NoSuchTaskIdException;
7 | import com.xzy.wechatmsg.domain.task.mapper.TaskMapper;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.stereotype.Repository;
10 | import org.springframework.util.CollectionUtils;
11 |
12 | import java.util.List;
13 |
14 | /**
15 | * @description: TaskRepository
16 | * @author: Xie zy
17 | * @create: 2022.09.03
18 | */
19 | @Repository
20 | public class TaskRepository {
21 |
22 | @Autowired
23 | TaskMapper taskMapper;
24 |
25 | public Task checkTaskId(Integer taskId) {
26 | //去db中查询有没有这个id,没有就报错,有就传来db中的task
27 | LambdaQueryWrapper queryWrapper = new QueryWrapper().lambda();
28 | queryWrapper.eq(Task::getId, taskId);
29 | List tasks = taskMapper.selectList(queryWrapper);
30 | if (CollectionUtils.isEmpty(tasks)) {
31 | throw new NoSuchTaskIdException();
32 | }
33 | return tasks.get(0);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/enums/TaskStatusEnum.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.enums;
2 |
3 | /**
4 | * @description: TaskStatusEnum
5 | * @author: Xie zy
6 | * @create: 2022.09.02
7 | */
8 | public enum TaskStatusEnum {
9 | /**
10 | * stop
11 | */
12 | TASK_STOP(0),
13 | /**
14 | * running
15 | */
16 | TASK_RUNNING(1),
17 | ;
18 |
19 | Integer value;
20 |
21 | TaskStatusEnum(Integer value) {
22 | this.value = value;
23 | }
24 |
25 | public Integer getValue() {
26 | return this.value;
27 | }
28 |
29 | public static TaskStatusEnum findByValue(Integer value) {
30 | for (TaskStatusEnum taskStatusEnum : TaskStatusEnum.values()) {
31 | if (taskStatusEnum.value.equals(value)) {
32 | return taskStatusEnum;
33 | }
34 | }
35 | return null;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/enums/TaskTypeEnum.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.enums;
2 |
3 | /**
4 | * @description: TaskTypeEnum
5 | * @author: Xie zy
6 | * @create: 2022.09.01
7 | */
8 | public enum TaskTypeEnum {
9 | /**
10 | * db
11 | */
12 | TASK_TYPE_DB("db", "dbTaskHandler"),
13 | /**
14 | * app
15 | */
16 | TASK_TYPE_APP("app", "appTaskHandler"),
17 | ;
18 |
19 | String value;
20 | String handler;
21 |
22 | TaskTypeEnum(String value, String handler) {
23 | this.value = value;
24 | this.handler = handler;
25 | }
26 |
27 | public String getValue() {
28 | return this.value;
29 | }
30 |
31 | public String getHandler() {
32 | return this.handler;
33 | }
34 |
35 | public static TaskTypeEnum findByValue(String value) {
36 | for (TaskTypeEnum taskTypeEnum : TaskTypeEnum.values()) {
37 | if (taskTypeEnum.value.equals(value)) {
38 | return taskTypeEnum;
39 | }
40 | }
41 | return null;
42 | }
43 |
44 | public static TaskTypeEnum findByHandler(String handler) {
45 | for (TaskTypeEnum taskTypeEnum : TaskTypeEnum.values()) {
46 | if (taskTypeEnum.handler.equals(handler)) {
47 | return taskTypeEnum;
48 | }
49 | }
50 | return null;
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/enums/WechatMsgTypeEnum.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.enums;
2 |
3 | /**
4 | * @description: WechatMsgTypeEnum
5 | * @author: Xie zy
6 | * @create: 2022.09.29
7 | */
8 | public enum WechatMsgTypeEnum {
9 | /**
10 | * text
11 | */
12 | TEXT_MSG("text_msg"),
13 | /**
14 | * img
15 | */
16 | IMG_MSG("img_msg"),
17 | /**
18 | * at
19 | */
20 | AT_MSG("at_msg"),
21 | /**
22 | * annex
23 | */
24 | ANNEX_MSG("annex_msg"),
25 | /**
26 | * custom_method_txt_msg
27 | */
28 | CUSTOM_METHOD_TXT_MSG("custom_method_txt_msg"),
29 | ;
30 |
31 | String value;
32 |
33 | WechatMsgTypeEnum(String value) {
34 | this.value = value;
35 | }
36 |
37 | public String getValue() {
38 | return this.value;
39 | }
40 |
41 | public static WechatMsgTypeEnum findByValue(String value) {
42 | for (WechatMsgTypeEnum wechatMsgTypeEnum : WechatMsgTypeEnum.values()) {
43 | if (wechatMsgTypeEnum.value.equals(value)) {
44 | return wechatMsgTypeEnum;
45 | }
46 | }
47 | return null;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/exception/task/BaseTaskException.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.exception.task;
2 |
3 | import com.xzy.wechatmsg.constant.ExceptionConstants;
4 |
5 | /**
6 | * @description: BaseTaskException
7 | * @author: Xie zy
8 | * @create: 2022.09.02
9 | */
10 | public class BaseTaskException extends RuntimeException {
11 | public BaseTaskException() {
12 | super(ExceptionConstants.BASE_TASK_EXCEPTION_MSG);
13 | }
14 |
15 | public BaseTaskException(String msg) {
16 | super(msg);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/exception/task/CustomMethodInvokeException.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.exception.task;
2 |
3 | import com.xzy.wechatmsg.constant.ExceptionConstants;
4 |
5 | /**
6 | * @description: CustomMethodInvokeException
7 | * @author: Xie zy
8 | * @create: 2023.09.07
9 | */
10 | public class CustomMethodInvokeException extends BaseTaskException {
11 | public CustomMethodInvokeException() {
12 | super(ExceptionConstants.CUSTOM_METHOD_INVOKE_EXCEPTION);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/exception/task/NoSuchMsgTypeException.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.exception.task;
2 |
3 | import com.xzy.wechatmsg.constant.ExceptionConstants;
4 |
5 | /**
6 | * @description: NoSuchMsgTypeException
7 | * @author: Xie zy
8 | * @create: 2022.09.29
9 | */
10 | public class NoSuchMsgTypeException extends BaseTaskException {
11 | public NoSuchMsgTypeException() {
12 | super(ExceptionConstants.NO_SUCH_MSG_TYPE);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/exception/task/NoSuchTaskIdException.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.exception.task;
2 |
3 | import com.xzy.wechatmsg.constant.ExceptionConstants;
4 |
5 | /**
6 | * @description: NoSuchTaskIdException
7 | * @author: Xie zy
8 | * @create: 2022.09.02
9 | */
10 | public class NoSuchTaskIdException extends BaseTaskException {
11 | public NoSuchTaskIdException() {
12 | super(ExceptionConstants.NO_SUCH_TASK_ID_EXCEPTION_MSG);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/exception/task/TaskInvokeException.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.exception.task;
2 |
3 | import com.xzy.wechatmsg.constant.ExceptionConstants;
4 |
5 | /**
6 | * @description: TaskInvokeException
7 | * @author: Xie zy
8 | * @create: 2022.09.29
9 | */
10 | public class TaskInvokeException extends BaseTaskException {
11 | public TaskInvokeException() {
12 | super(ExceptionConstants.TASK_INVOKE_FAILED);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/interceptor/TaskControllerAdvisor.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.interceptor;
2 |
3 | import com.xzy.wechatmsg.constant.ExceptionConstants;
4 | import com.xzy.wechatmsg.exception.task.NoSuchMsgTypeException;
5 | import com.xzy.wechatmsg.exception.task.NoSuchTaskIdException;
6 | import com.xzy.wechatmsg.vo.BaseResponseVO;
7 | import org.springframework.web.bind.annotation.ExceptionHandler;
8 | import org.springframework.web.bind.annotation.RestControllerAdvice;
9 |
10 | /**
11 | * @description: TaskControllerAdvisor
12 | * @author: Xie zy
13 | * @create: 2022.09.29
14 | */
15 | @RestControllerAdvice
16 | public class TaskControllerAdvisor {
17 |
18 | @ExceptionHandler(Exception.class)
19 | public BaseResponseVO error(Exception e) {
20 | return BaseResponseVO.error();
21 | }
22 |
23 | @ExceptionHandler(NoSuchTaskIdException.class)
24 | public BaseResponseVO noSuchTaskIdException(NoSuchTaskIdException e) {
25 | return BaseResponseVO.error(ExceptionConstants.NO_SUCH_TASK_ID_EXCEPTION_MSG);
26 | }
27 |
28 | @ExceptionHandler(NoSuchMsgTypeException.class)
29 | public BaseResponseVO noSuchMsgTypeException(NoSuchMsgTypeException e) {
30 | return BaseResponseVO.error(ExceptionConstants.NO_SUCH_MSG_TYPE);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/manager/robot/RobotMsgHandler.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.manager.robot;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.xzy.wechatmsg.bo.WechatMsgWithInfoAndType;
5 | import com.xzy.wechatmsg.client.WechatRobotClient;
6 | import com.xzy.wechatmsg.domain.chatgpt.model.ChatGptReq35;
7 | import com.xzy.wechatmsg.domain.chatgpt.model.ChatGptResp35;
8 | import com.xzy.wechatmsg.domain.robot.model.WechatMsgDTO;
9 | import com.xzy.wechatmsg.domain.robot.model.WechatRsvMsgDTO;
10 | import com.xzy.wechatmsg.domain.robot.model.WechatRsvPicMsg;
11 | import com.xzy.wechatmsg.domain.robot.model.WechatRsvTxtMsg;
12 | import org.springframework.beans.factory.annotation.Value;
13 | import org.springframework.http.HttpEntity;
14 | import org.springframework.http.HttpHeaders;
15 | import org.springframework.http.ResponseEntity;
16 | import org.springframework.stereotype.Component;
17 | import org.springframework.util.CollectionUtils;
18 | import org.springframework.web.client.RestTemplate;
19 |
20 | import java.util.ArrayList;
21 | import java.util.HashMap;
22 | import java.util.HashSet;
23 | import java.util.Map;
24 | import java.util.Random;
25 | import java.util.Set;
26 | import java.util.concurrent.ConcurrentHashMap;
27 |
28 | /**
29 | * @description: RobotMsgHandler
30 | * @author: Xie zy
31 | * @create: 2023.01.31
32 | */
33 | @Component
34 | public class RobotMsgHandler {
35 |
36 | @Value(value = "${openai.token}")
37 | private String token;
38 |
39 | public static Map> autoRespMap = new ConcurrentHashMap>(){{
40 | //k为roomId,v为wxId
41 | }};
42 |
43 | public static Set autoRespRoomSet = new HashSet(){{
44 | //元素为roomId
45 | }};
46 |
47 | public static Map restaurantMap = new HashMap() {{
48 | put(0, "新一");
49 | put(1, "新四");
50 | put(2, "老二");
51 | put(3, "学苑");
52 | put(4, "校外(小面抄手等)");
53 | }};
54 |
55 | public WechatMsgDTO buildWechatMsg(WechatMsgWithInfoAndType.WechatMsg msg) {
56 | return JSON.parseObject(JSON.toJSONString(msg), WechatMsgDTO.class);
57 | }
58 |
59 | public WechatRsvTxtMsg parseTxtMsg(WechatRsvMsgDTO msg) {
60 | return JSON.parseObject(JSON.toJSONString(msg), WechatRsvTxtMsg.class);
61 | }
62 |
63 | public WechatRsvPicMsg parsePicMsg(WechatRsvMsgDTO msg) {
64 | //TODO
65 | return null;
66 | }
67 |
68 | public String dealAtGroupTxtMsg(WechatRobotClient wechatRobotClient, String roomId, String userId, String txt) {
69 | // TODO 过滤掉@位置不同,误删
70 | String s = txt.substring(1).trim();
71 | String nickName = wechatRobotClient.getChatroomMemberNick(roomId, userId);
72 | //1.如果是开启自动回复,那就加入map,保存当前群聊,并返回收到
73 | //2.如果是关闭自动回复,那就从map中移除,并返回已关闭自动回复,保留@回复
74 | switch (s) {
75 | case "吃什么":
76 | return randomRestaurant();
77 | // case "开启我的自动回复": {
78 | // Set set = autoRespMap.getOrDefault(roomId, new HashSet<>());
79 | // set.add(userId);
80 | // autoRespMap.put(roomId, set);
81 | // return "已开启" + nickName + "的自动回复";
82 | // }
83 | // case "关闭我的自动回复": {
84 | // Set set = autoRespMap.getOrDefault(roomId, new HashSet<>());
85 | // if (set.contains(userId)) {
86 | // set.remove(userId);
87 | // }
88 | // if (set.isEmpty() && autoRespMap.containsKey(roomId)) {
89 | // autoRespMap.remove(roomId);
90 | // }
91 | // return "已关闭" + nickName + "的自动回复";
92 | // }
93 | // case "开启所有自动回复":
94 | // autoRespRoomSet.add(roomId);
95 | // return "已开启所有自动回复";
96 | // case "关闭所有自动回复":
97 | // if (autoRespRoomSet.contains(roomId)) {
98 | // autoRespRoomSet.remove(roomId);
99 | // }
100 | // return "已关闭所有自动回复";
101 | default:
102 | return "To " + nickName + ":" + System.lineSeparator() + chat(s, true);
103 | }
104 | }
105 |
106 | private String randomRestaurant() {
107 | return restaurantMap.get(new Random().nextInt(restaurantMap.size()));
108 | }
109 |
110 | public String dealNormalGroupTxtMsg(String txt) {
111 | // TODO 过滤掉@位置不同,误删
112 | String s = txt.substring(1).trim();
113 | return chat(s, false);
114 | }
115 |
116 | public String dealPrivateTxtMsg(String txt) {
117 | // TODO 过滤掉@位置不同,误删
118 | String s = txt.substring(1).trim();
119 | return chat(s, false);
120 | }
121 |
122 | public String dealAtGroupPicMsg(WechatRsvPicMsg pic) {
123 | //TODO
124 | return null;
125 | }
126 |
127 | public String dealNormalGroupPicMsg(WechatRsvPicMsg pic) {
128 | //TODO
129 | return null;
130 | }
131 |
132 | public String dealPrivatePicMsg(WechatRsvPicMsg pic) {
133 | //TODO
134 | return null;
135 | }
136 |
137 | /**
138 | * chatGpt chat
139 | */
140 | public String chat(String input, boolean openTips) {
141 | // if (openTips) {
142 | // if (input == null || input.equals("")) {
143 | // return "您好,我是Robot-Niu"
144 | // + System.lineSeparator()
145 | // + "可以艾特我提问哦~"
146 | // + System.lineSeparator()
147 | // + System.lineSeparator()
148 | // + "更改触发方式可艾特我输入如下文字,两次输入请间隔一段时间"
149 | // + System.lineSeparator()
150 | // + "1.开启我的自动回复(不用艾特我就自动回复您的消息)"
151 | // + System.lineSeparator()
152 | // + "2.关闭我的自动回复(关闭自动回复您的消息)"
153 | // + System.lineSeparator()
154 | // + "3.开启所有自动回复(开启所有群消息自动回复,目前只有小牛人可以)"
155 | // + System.lineSeparator()
156 | // + "4.关闭所有自动回复(关闭所有群消息自动回复)";
157 | // }
158 | // } else {
159 | // if (input == null || input.equals("")) {
160 | // return "您好,我是Robot-Niu"
161 | // + System.lineSeparator()
162 | // + "可以提问我哦~";
163 | // }
164 | // }
165 |
166 | // // 以下是gpt3
167 | // String completionsApi = "https://api.openai.com/v1/completions";
168 | //
169 | // RestTemplate restTemplate = new RestTemplate();
170 | //
171 | // //header
172 | // HttpHeaders headers = new HttpHeaders();
173 | // headers.set("Content-Type", "application/json");
174 | // headers.set("Authorization", "Bearer ".concat(token));
175 | //
176 | // //body
177 | // ChatGptReq30 body = new ChatGptReq30();
178 | // body.setModel("text-davinci-003");
179 | // body.setPrompt(input + "\n");
180 | // //返回消息长度
181 | // body.setMax_tokens(3000);
182 | // //0每次回答一样~1每次回答不一样
183 | // body.setTemperature(0.8f);
184 | //
185 | // HttpEntity entity = new HttpEntity<>(body, headers);
186 |
187 | // 以下是gpt3.5
188 | ////////////////////////////////////////////////////////////
189 | String completionsApi = "https://api.openai.com/v1/chat/completions";
190 |
191 | RestTemplate restTemplate = new RestTemplate();
192 |
193 | //header
194 | HttpHeaders headers = new HttpHeaders();
195 | headers.set("Content-Type", "application/json");
196 | headers.set("Authorization", "Bearer ".concat(token));
197 |
198 | //body
199 | ChatGptReq35 body = new ChatGptReq35();
200 | body.setModel("gpt-3.5-turbo");
201 | ChatGptReq35.Message message = new ChatGptReq35.Message();
202 | message.setRole("user");
203 | message.setContent(input);
204 | body.setMessages(new ArrayList() {{
205 | add(message);
206 | }});
207 | //0每次回答一样~1每次回答不一样
208 | body.setTemperature(0.7f);
209 |
210 | HttpEntity entity = new HttpEntity<>(body, headers);
211 | ////////////////////////////////////////////////////////////
212 |
213 | //post
214 | ResponseEntity responseEntity;
215 | try {
216 | responseEntity = restTemplate.postForEntity(completionsApi, entity, String.class);
217 | } catch (Exception e) {
218 | return "GPT的api调用出错,请稍后重试~";
219 | }
220 | // 以下是gpt3
221 | // ChatGptResp30 chatGptResp = JSON.parseObject(responseEntity.getBody(), ChatGptResp30.class);
222 | // 以下是gpt3.5
223 | ChatGptResp35 chatGptResp = JSON.parseObject(responseEntity.getBody(), ChatGptResp35.class);
224 |
225 | if (chatGptResp == null || CollectionUtils.isEmpty(chatGptResp.getChoices())) {
226 | return "不好意思我没理解您的意思";
227 | } else {
228 | // 以下是gpt3
229 | // String text = chatGptResp.getChoices().get(0).getText();
230 | // 以下是gpt3.5
231 | String text = chatGptResp.getChoices().get(0).getMessage().getContent();
232 | return text.replaceAll("^[" + System.lineSeparator() + "]", "").replaceAll("[" + System.lineSeparator() + "]$", "");
233 | }
234 | }
235 |
236 | }
237 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/manager/task/AbstractTaskHandler.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.manager.task;
2 |
3 | import com.xzy.wechatmsg.request.task.TaskRequest;
4 | import com.xzy.wechatmsg.vo.task.TaskResponseVO;
5 | import org.springframework.stereotype.Component;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * @description: AbstractTaskHandler
11 | * @author: Xie zy
12 | * @create: 2022.09.01
13 | */
14 | @Component
15 | public abstract class AbstractTaskHandler {
16 |
17 | public abstract void createTasks(TaskRequest taskRequest);
18 |
19 | public abstract List readTasks();
20 |
21 | public abstract void updateTasks(TaskRequest taskRequest);
22 |
23 | public abstract void deleteTasks(TaskRequest taskRequest);
24 |
25 | public void refreshApp() {}
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/manager/task/AppTaskHandler.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.manager.task;
2 |
3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
5 | import com.xzy.wechatmsg.bo.WrappedCronTask;
6 | import com.xzy.wechatmsg.domain.task.repository.TaskRepository;
7 | import com.xzy.wechatmsg.enums.TaskTypeEnum;
8 | import com.xzy.wechatmsg.config.ScheduleConfig;
9 | import com.xzy.wechatmsg.domain.task.model.Task;
10 | import com.xzy.wechatmsg.enums.TaskStatusEnum;
11 | import com.xzy.wechatmsg.domain.task.mapper.TaskMapper;
12 | import com.xzy.wechatmsg.request.task.TaskRequest;
13 | import com.xzy.wechatmsg.vo.task.TaskResponseVO;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 | import org.springframework.scheduling.config.CronTask;
16 | import org.springframework.scheduling.config.ScheduledTask;
17 | import org.springframework.scheduling.config.ScheduledTaskRegistrar;
18 | import org.springframework.stereotype.Component;
19 | import org.springframework.util.CollectionUtils;
20 |
21 | import javax.annotation.PreDestroy;
22 | import java.time.LocalDateTime;
23 | import java.util.List;
24 | import java.util.Map;
25 | import java.util.concurrent.ConcurrentHashMap;
26 | import java.util.concurrent.CopyOnWriteArrayList;
27 | import java.util.stream.Collectors;
28 |
29 | /**
30 | * @description: AppTaskHandler 处理app中的task
31 | * @author: Xie zy
32 | * @create: 2022.09.01
33 | */
34 | @Component
35 | public class AppTaskHandler extends AbstractTaskHandler {
36 |
37 | private static List wrappedCronTaskList = new CopyOnWriteArrayList<>();
38 |
39 | private static Map scheduledCronTaskMap = new ConcurrentHashMap<>();
40 |
41 | @Autowired
42 | ScheduleConfig scheduleConfig;
43 |
44 | @Autowired
45 | RunnableWechatMsgFactory runnableWechatMsgFactory;
46 |
47 | @Autowired
48 | TaskMapper taskMapper;
49 |
50 | @Autowired
51 | TaskRepository taskRepository;
52 |
53 | @Override
54 | public void createTasks(TaskRequest taskRequest) {
55 | //查db,查不到报错
56 | Task dbTask = taskRepository.checkTaskId(taskRequest.getTaskId());
57 | //查到去更新db状态 && app双写
58 | if (TaskStatusEnum.TASK_STOP.getValue().equals(dbTask.getStatus())) {
59 | //获取db数据
60 | Integer taskId = dbTask.getId();
61 | String msg = dbTask.getMsg();
62 | String cron = dbTask.getCron();
63 | LocalDateTime createTime = dbTask.getCreateTime();
64 | //启动appTask需要更新updateTime
65 | LocalDateTime now = LocalDateTime.now();
66 | //1.更新数据库状态和updateTime
67 | taskMapper.updateStatusWithUpdateTime(taskId, TaskStatusEnum.TASK_STOP.getValue(), TaskStatusEnum.TASK_RUNNING.getValue(), now);
68 | //2.创建app任务
69 | Runnable runnable = runnableWechatMsgFactory.createRunnable(msg);
70 | //app双写任务
71 | this.addWrappedCronTask(new WrappedCronTask(taskId, msg, cron, TaskStatusEnum.TASK_RUNNING.getValue(), createTime, now, runnable), false);
72 | }
73 | }
74 |
75 | @Override
76 | public List readTasks() {
77 | //useless method
78 | return null;
79 | }
80 |
81 | @Override
82 | public void updateTasks(TaskRequest taskRequest) {
83 | //找不到id上游已经判断了,所以直接删除appTask即可
84 | this.removeWrappedCronTask(taskRequest.getTaskId());
85 | //获取上下文
86 | Integer taskId = taskRequest.getTaskId();
87 | String msg = taskRequest.getMsg();
88 | String cron = taskRequest.getCron();
89 | LocalDateTime createTime = taskRequest.getCreateTime();
90 | //找不到id上游已经判断了,所以直接运行app即可
91 | LocalDateTime now = LocalDateTime.now();
92 | //1.更新数据库状态和updateTime
93 | taskMapper.updateStatusWithUpdateTime(taskId, TaskStatusEnum.TASK_STOP.getValue(), TaskStatusEnum.TASK_RUNNING.getValue(), now);
94 | //2.创建app任务
95 | Runnable runnable = runnableWechatMsgFactory.createRunnable(msg);
96 | //双写app任务
97 | this.addWrappedCronTask(new WrappedCronTask(taskId, msg, cron, TaskStatusEnum.TASK_RUNNING.getValue(), createTime, now, runnable), false);
98 | }
99 |
100 | @Override
101 | public void deleteTasks(TaskRequest taskRequest) {
102 | //type = app需要查询数据库,并改状态
103 | if (TaskTypeEnum.TASK_TYPE_APP.getValue().equals(taskRequest.getType())) {
104 | //查db,查不到报错
105 | taskRepository.checkTaskId(taskRequest.getTaskId());
106 | //查到更新状态为stop
107 | taskMapper.updateStatusWithOutUpdateTime(taskRequest.getTaskId(), TaskStatusEnum.TASK_RUNNING.getValue(), TaskStatusEnum.TASK_STOP.getValue());
108 | }
109 | //type = db,上游已经删除了,故肯定查不到
110 | //停止app任务
111 | this.removeWrappedCronTask(taskRequest.getTaskId());
112 | }
113 |
114 | @Override
115 | public void refreshApp() {
116 | //刷新app,使app的运行状态和数据库中新加的一致
117 | //拉闸App,但是不改db
118 | this.shutdownAllAppTaskWithoutAlterDb();
119 | //对于数据库中Running的,重新注册运行appTask
120 | LambdaQueryWrapper wrapper = new QueryWrapper().lambda();
121 | wrapper.eq(Task::getStatus, TaskStatusEnum.TASK_RUNNING.getValue());
122 | List dbRunningTasks = taskMapper.selectList(wrapper);
123 | dbRunningTasks.forEach(dbRunningTask -> {
124 | Integer taskId = dbRunningTask.getId();
125 | String msg = dbRunningTask.getMsg();
126 | String cron = dbRunningTask.getCron();
127 | Integer status = dbRunningTask.getStatus();
128 | LocalDateTime createTime = dbRunningTask.getCreateTime();
129 | LocalDateTime updateTime = dbRunningTask.getUpdateTime();
130 | Runnable runnable = runnableWechatMsgFactory.createRunnable(msg);
131 | this.addWrappedCronTask(new WrappedCronTask(taskId, msg, cron, status, createTime, updateTime, runnable), false);
132 | });
133 | }
134 |
135 | public List getWrappedCronTaskList() {
136 | return AppTaskHandler.wrappedCronTaskList;
137 | }
138 |
139 | /**
140 | * 只在app层面增加,不管db
141 | *
142 | * @param wrappedCronTask wrappedCronTask
143 | * @param initial 是否是初始化
144 | * @param scheduledTaskRegistrars 初始化的时候用到
145 | */
146 | public void addWrappedCronTask(WrappedCronTask wrappedCronTask, boolean initial, ScheduledTaskRegistrar... scheduledTaskRegistrars) {
147 | //双加
148 | //1.加wrappedList
149 | this.getWrappedCronTaskList().add(wrappedCronTask);
150 | //获取task和cron
151 | Runnable runnable = wrappedCronTask.getRunnable();
152 | String cron = wrappedCronTask.getCron();
153 | if (!initial) {
154 | //2.启动并加入cronMap
155 | ScheduledTask scheduledCronTask = this.addCronTask(new CronTask(runnable, cron));
156 | this.getScheduledCronTaskMap().put(wrappedCronTask.getTaskId().toString(), scheduledCronTask);
157 | }
158 |
159 | if (initial) {
160 | //因为ScheduleConfig initial的时候是new的AppTaskHandler
161 | //直接调用this.addCronTask应该是没有注入getScheduledTaskRegistrar的
162 | ScheduledTaskRegistrar taskRegistrar = scheduledTaskRegistrars[0];
163 | ScheduledTask scheduledCronTask = taskRegistrar.scheduleCronTask(new CronTask(runnable, cron));
164 | this.getScheduledCronTaskMap().put(wrappedCronTask.getTaskId().toString(), scheduledCronTask);
165 | }
166 | }
167 |
168 | /**
169 | * 只在app层面删除,不管db
170 | *
171 | * @param taskId taskId
172 | */
173 | public void removeWrappedCronTask(Integer taskId) {
174 | //如果有这个taskId就删除这个App Task
175 | //双删
176 | //1.删除wrappedTask
177 | List wrappedCronTaskList = this.getWrappedCronTaskList().stream()
178 | .filter(wrappedCronTask -> wrappedCronTask.getTaskId().equals(taskId))
179 | .collect(Collectors.toList());
180 | if (CollectionUtils.isEmpty(wrappedCronTaskList)) return;
181 | this.getWrappedCronTaskList().remove(wrappedCronTaskList.get(0));
182 | //2.删除Task
183 | this.removeCronTask(taskId);
184 | }
185 |
186 | private Map getScheduledCronTaskMap() {
187 | return AppTaskHandler.scheduledCronTaskMap;
188 | }
189 |
190 | private ScheduledTask addCronTask(CronTask cronTask) {
191 | ScheduledTask scheduledCronTask = scheduleConfig.getScheduledTaskRegistrar().scheduleCronTask(cronTask);
192 | return scheduledCronTask;
193 | }
194 |
195 | private void removeCronTask(Integer taskId) {
196 | //cancel && remove
197 | this.getScheduledCronTaskMap().get(taskId.toString()).cancel();
198 | this.getScheduledCronTaskMap().remove(taskId.toString());
199 | }
200 |
201 | private void shutdownAllAppTask() {
202 | //拉闸App,双拉
203 | //1.拉闸wrappedList
204 | this.getWrappedCronTaskList().forEach(wrappedCronTask -> {
205 | taskMapper.updateStatusWithOutUpdateTime(wrappedCronTask.getTaskId(), TaskStatusEnum.TASK_RUNNING.getValue(), TaskStatusEnum.TASK_STOP.getValue());
206 | });
207 | //清空数据
208 | this.getWrappedCronTaskList().clear();
209 | //2.拉闸cronMap
210 | this.shutdownAllCronTask();
211 | }
212 |
213 | private void shutdownAllAppTaskWithoutAlterDb() {
214 | //拉闸App,但是不改数据库状态
215 | //1.拉闸wrappedList
216 | this.getWrappedCronTaskList().clear();
217 | //2.拉闸cronMap
218 | this.shutdownAllCronTask();
219 | }
220 |
221 | private void shutdownAllCronTask() {
222 | //cancel && clear
223 | this.getScheduledCronTaskMap().values().forEach(ScheduledTask::cancel);
224 | this.getScheduledCronTaskMap().clear();
225 | }
226 |
227 | @PreDestroy
228 | private void shutdownAppAfterSpringBoot() {
229 | //基本不用的方法,SpringBoot容器关闭后执行
230 | this.shutdownAllAppTask();
231 | }
232 |
233 | }
234 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/manager/task/BaseCustomMethodHandler.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.manager.task;
2 |
3 | import com.xzy.wechatmsg.bo.WechatMsgWithInfoAndType;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.http.HttpEntity;
7 | import org.springframework.http.HttpMethod;
8 | import org.springframework.stereotype.Component;
9 | import org.springframework.web.client.RestTemplate;
10 |
11 | /**
12 | * @description: BaseCustomMethodHandler
13 | * @author: Xie zy
14 | * @create: 2023.09.07
15 | */
16 | @Component
17 | public class BaseCustomMethodHandler {
18 | @Value(value = "${wechat.url}")
19 | private String url;
20 |
21 | private final String SEND_PATH_XIAONIUREN_TEMPLATE = "/xiaoniuren?msg={msg}";
22 |
23 | private static final String SEND_PATH_SEND_TEXT_MSG = "/sendTextMsg";
24 |
25 | @Autowired
26 | RestTemplate restTemplate;
27 |
28 | @Autowired
29 | HttpEntity httpEntity;
30 |
31 | protected void sendToXiaoniuren(String msg) {
32 | String url = this.url + SEND_PATH_XIAONIUREN_TEMPLATE;
33 | restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class, msg);
34 | }
35 |
36 | protected void sendTextMsg(WechatMsgWithInfoAndType.WechatMsg msg) {
37 | String url = this.url + SEND_PATH_SEND_TEXT_MSG;
38 | HttpEntity request = new HttpEntity<>(msg, httpEntity.getHeaders());
39 | restTemplate.exchange(url, HttpMethod.POST, request, String.class);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/manager/task/CustomMethodHandler.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.manager.task;
2 |
3 | import com.xzy.wechatmsg.annotation.CronMethod;
4 | import org.springframework.stereotype.Component;
5 |
6 | /**
7 | * @description: CustomMethod
8 | * @author: Xie zy
9 | * @create: 2023.09.07
10 | */
11 | @Component
12 | public class CustomMethodHandler extends BaseCustomMethodHandler {
13 |
14 | @CronMethod
15 | public void test(String param) {
16 | sendToXiaoniuren(param);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/manager/task/DbTaskHandler.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.manager.task;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
5 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
6 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
7 | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
8 | import com.xzy.wechatmsg.domain.task.model.Task;
9 | import com.xzy.wechatmsg.domain.task.repository.TaskRepository;
10 | import com.xzy.wechatmsg.enums.TaskStatusEnum;
11 | import com.xzy.wechatmsg.domain.task.mapper.TaskMapper;
12 | import com.xzy.wechatmsg.request.task.TaskRequest;
13 | import com.xzy.wechatmsg.vo.task.TaskResponseVO;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 | import org.springframework.stereotype.Component;
16 | import org.springframework.util.StringUtils;
17 |
18 | import java.time.LocalDateTime;
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | /**
23 | * @description: DbTaskHandler 处理db中的task
24 | * @author: Xie zy
25 | * @create: 2022.09.01
26 | */
27 | @Component
28 | public class DbTaskHandler extends AbstractTaskHandler {
29 |
30 | @Autowired
31 | TaskMapper taskMapper;
32 |
33 | @Autowired
34 | TaskRepository taskRepository;
35 |
36 | @Override
37 | public void createTasks(TaskRequest taskRequest) {
38 | LocalDateTime now = LocalDateTime.now();
39 | Task task = Task.builder()
40 | .cron(taskRequest.getCron())
41 | .msg(taskRequest.getMsg())
42 | .status(TaskStatusEnum.TASK_STOP.getValue())
43 | .createTime(now)
44 | .updateTime(now)
45 | .build();
46 | taskMapper.insert(task);
47 | Integer insertId = task.getId();
48 | //把主键带出来给后续方法(App#createTasks)
49 | taskRequest.setTaskId(insertId);
50 | }
51 |
52 | @Override
53 | public List readTasks() {
54 | List tasks = taskMapper.selectList(new QueryWrapper<>());
55 | List res = new ArrayList<>();
56 | tasks.forEach(task -> {
57 | TaskResponseVO.TaskVO taskVO = JSON.parseObject(JSON.toJSONString(task), TaskResponseVO.TaskVO.class);
58 | taskVO.setTaskId(task.getId());
59 | res.add(taskVO);
60 | });
61 | return res;
62 | }
63 |
64 | @Override
65 | public void updateTasks(TaskRequest taskRequest) {
66 | //去db中查询有没有这个id,没有就报错
67 | Task dbTask = taskRepository.checkTaskId(taskRequest.getTaskId());
68 | //转换前端参数
69 | //对前端传进来的进行属性填充,前端一般不会传""过来,空的msg也没意义,不如直接shutdown任务
70 | if (!StringUtils.isEmpty(taskRequest.getMsg())) dbTask.setMsg(taskRequest.getMsg());
71 | if (!StringUtils.isEmpty(taskRequest.getCron())) dbTask.setCron(taskRequest.getCron());
72 | //更新的设为stop
73 | dbTask.setStatus(TaskStatusEnum.TASK_STOP.getValue());
74 | LambdaUpdateWrapper updateWrapper = new UpdateWrapper().lambda();
75 | updateWrapper.eq(Task::getId, dbTask.getId());
76 | taskMapper.update(dbTask, updateWrapper);
77 | //传给下游方法
78 | taskRequest.setMsg(dbTask.getMsg());
79 | taskRequest.setCron(dbTask.getCron());
80 | taskRequest.setCreateTime(dbTask.getCreateTime());
81 | }
82 |
83 | @Override
84 | public void deleteTasks(TaskRequest taskRequest) {
85 | //去db中查询有没有这个id,没有就报错
86 | taskRepository.checkTaskId(taskRequest.getTaskId());
87 | //有的话就删除
88 | LambdaQueryWrapper queryWrapper = new QueryWrapper().lambda();
89 | queryWrapper.eq(Task::getId, taskRequest.getTaskId());
90 | taskMapper.delete(queryWrapper);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/manager/task/RunnableWechatMsgFactory.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.manager.task;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.xzy.wechatmsg.exception.task.NoSuchMsgTypeException;
5 | import com.xzy.wechatmsg.bo.WechatMsgWithInfoAndType;
6 | import com.xzy.wechatmsg.client.WechatRobotClient;
7 | import com.xzy.wechatmsg.enums.WechatMsgTypeEnum;
8 | import com.xzy.wechatmsg.exception.task.TaskInvokeException;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.stereotype.Component;
11 |
12 | import java.lang.reflect.Method;
13 | import java.util.HashMap;
14 | import java.util.Map;
15 |
16 | /**
17 | * @description: RunnableWechatMsgFactory
18 | * @author: Xie zy
19 | * @create: 2022.09.29
20 | */
21 | @Component
22 | public class RunnableWechatMsgFactory {
23 |
24 | @Autowired
25 | WechatRobotClient wechatClient;
26 |
27 | private static final Map msgTypeMethodNameMap = new HashMap() {{
28 | put(WechatMsgTypeEnum.TEXT_MSG, "sendTextMsg");
29 | put(WechatMsgTypeEnum.IMG_MSG, "sendImgMsg");
30 | put(WechatMsgTypeEnum.AT_MSG, "sendAtMsg");
31 | put(WechatMsgTypeEnum.ANNEX_MSG, "sendAnnex");
32 | put(WechatMsgTypeEnum.CUSTOM_METHOD_TXT_MSG, "sendTextMsgWithCustomMethod");
33 | }};
34 |
35 | /**
36 | * @param msg WechatMsgWithType的JSON字符串
37 | * @return 可运行的app任务
38 | */
39 | public Runnable createRunnable(String msg) {
40 | //获取不同type的method
41 | WechatMsgWithInfoAndType wechatMsgWithInfoAndType = JSON.parseObject(msg, WechatMsgWithInfoAndType.class);
42 | String methodName = msgTypeMethodNameMap.get(wechatMsgWithInfoAndType.getMsgType());
43 | //反射获取Runnable
44 | Runnable res = () -> {};
45 | try {
46 | Method method = wechatClient.getClass().getDeclaredMethod(methodName, WechatMsgWithInfoAndType.WechatMsg.class);
47 | method.setAccessible(true);
48 | WechatMsgWithInfoAndType.WechatMsg wechatMsg = wechatMsgWithInfoAndType.getMsgInfo();
49 | res = () -> {
50 | try {
51 | method.invoke(wechatClient, JSON.parseObject(JSON.toJSONString(wechatMsg), WechatMsgWithInfoAndType.WechatMsg.class));
52 | } catch (Exception e) {
53 | throw new TaskInvokeException();
54 | }
55 | };
56 | } catch (Exception e) {
57 | throw new NoSuchMsgTypeException();
58 | }
59 | return res;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/request/task/TaskRequest.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.request.task;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Builder;
6 | import lombok.Data;
7 | import lombok.NoArgsConstructor;
8 |
9 | import java.time.LocalDateTime;
10 |
11 | /**
12 | * @description: TaskRequest
13 | * @author: Xie zy
14 | * @create: 2022.09.01
15 | */
16 | @AllArgsConstructor
17 | @NoArgsConstructor
18 | @Builder
19 | @Data
20 | public class TaskRequest {
21 | Integer taskId;
22 | /**
23 | * bo.WechatMsgWithInfoAndType的JSON字符串
24 | */
25 | String msg;
26 | String cron;
27 | Integer status;
28 | /**
29 | * "db"-数据库 | "app"-SpringBoot应用
30 | */
31 | String type;
32 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
33 | LocalDateTime createTime;
34 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
35 | LocalDateTime updateTime;
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/service/RobotMsgHandleService.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.service;
2 |
3 | import com.xzy.wechatmsg.domain.robot.model.WechatRsvMsgDTO;
4 |
5 | /**
6 | * @description: RobotMsgHandleService
7 | * @author: Xie zy
8 | * @create: 2023.01.30
9 | */
10 | public interface RobotMsgHandleService {
11 |
12 | void handleTxtMsg(WechatRsvMsgDTO rsvMsg);
13 |
14 | void handlePicMsg(WechatRsvMsgDTO rsvMsg);
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/service/ScheduleTaskService.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.service;
2 |
3 | import com.xzy.wechatmsg.request.task.TaskRequest;
4 | import com.xzy.wechatmsg.vo.task.TaskResponseVO;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * @description: ScheduleTaskService
10 | * @author: Xie zy
11 | * @create: 2022.08.31
12 | */
13 | public interface ScheduleTaskService {
14 |
15 | void createTasks(TaskRequest taskRequest);
16 |
17 | List readTasks();
18 |
19 | void updateTasks(TaskRequest taskRequest);
20 |
21 | void deleteTasks(TaskRequest taskRequest);
22 |
23 | void refreshApp();
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/service/impl/RobotMsgHandleServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.service.impl;
2 |
3 | import com.xzy.wechatmsg.bo.WechatMsgWithInfoAndType;
4 | import com.xzy.wechatmsg.client.WechatRobotClient;
5 | import com.xzy.wechatmsg.domain.robot.model.WechatRsvMsgDTO;
6 | import com.xzy.wechatmsg.domain.robot.model.WechatRsvPicMsg;
7 | import com.xzy.wechatmsg.domain.robot.model.WechatRsvTxtMsg;
8 | import com.xzy.wechatmsg.manager.robot.RobotMsgHandler;
9 | import com.xzy.wechatmsg.service.RobotMsgHandleService;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.scheduling.config.ScheduledTask;
12 | import org.springframework.stereotype.Service;
13 |
14 | import java.util.Map;
15 | import java.util.concurrent.CompletableFuture;
16 | import java.util.concurrent.ConcurrentHashMap;
17 |
18 | /**
19 | * @description: RobotMsgHandleServiceImpl
20 | * @author: Xie zy
21 | * @create: 2023.01.30
22 | */
23 | @Service
24 | public class RobotMsgHandleServiceImpl implements RobotMsgHandleService {
25 |
26 | @Autowired
27 | RobotMsgHandler robotMsgHandler;
28 |
29 | @Autowired
30 | WechatRobotClient wechatRobotClient;
31 |
32 | @Override
33 | public void handleTxtMsg(WechatRsvMsgDTO rsvMsg) {
34 | // 使用异步, 避免阻塞
35 | CompletableFuture.runAsync(() -> {
36 | WechatRsvTxtMsg wechatRsvTxtMsg = robotMsgHandler.parseTxtMsg(rsvMsg);
37 | String wxid = wechatRsvTxtMsg.getWxid();
38 | if (wxid.endsWith("@chatroom")) {
39 | if (wechatRsvTxtMsg.getContent().contains("@NiuBot")) {
40 | //1.群聊@消息
41 | String roomId = wxid;
42 | String userId = wechatRsvTxtMsg.getId1();
43 | WechatMsgWithInfoAndType.WechatMsg wechatMsg = new WechatMsgWithInfoAndType.WechatMsg();
44 | String content = robotMsgHandler.dealAtGroupTxtMsg(wechatRobotClient, roomId, userId, wechatRsvTxtMsg.getContent().replace("@NiuBot", ""));
45 | wechatMsg.setContent(content);
46 | wechatMsg.setWxId(roomId);
47 | wechatRobotClient.sendTextMsg(wechatMsg);
48 | } else {
49 | //2.普通群聊消息
50 | //默认关闭,当@机器人开启自动回复以后,@机器人关闭自动回复,保留@回复
51 | String roomId = wxid;
52 | String wxId = wechatRsvTxtMsg.getId1();
53 | if (!RobotMsgHandler.autoRespRoomSet.contains(roomId) && !RobotMsgHandler.autoRespMap.containsKey(roomId) && !RobotMsgHandler.autoRespMap.get(roomId).contains(wxId)) {
54 | return;
55 | }
56 | WechatMsgWithInfoAndType.WechatMsg wechatMsg = new WechatMsgWithInfoAndType.WechatMsg();
57 | wechatMsg.setContent(robotMsgHandler.dealNormalGroupTxtMsg(wechatRsvTxtMsg.getContent()));
58 | wechatMsg.setWxId(roomId);
59 | wechatRobotClient.sendTextMsg(wechatMsg);
60 | }
61 | } else {
62 | //3.私聊消息
63 | WechatMsgWithInfoAndType.WechatMsg wechatMsg = new WechatMsgWithInfoAndType.WechatMsg();
64 | String content = robotMsgHandler.dealPrivateTxtMsg(wechatRsvTxtMsg.getContent());
65 | wechatMsg.setContent(content);
66 | wechatMsg.setWxId(wxid);
67 | wechatRobotClient.sendTextMsg(wechatMsg);
68 | }
69 | });
70 | }
71 |
72 | @Override
73 | public void handlePicMsg(WechatRsvMsgDTO rsvMsg) {
74 | CompletableFuture.runAsync(() -> {
75 | WechatRsvPicMsg wechatRsvPicMsg = robotMsgHandler.parsePicMsg(rsvMsg);
76 | //TODO 分私聊/群聊/艾特
77 | });
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/service/impl/ScheduleTaskServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.service.impl;
2 |
3 | import com.xzy.wechatmsg.enums.TaskTypeEnum;
4 | import com.xzy.wechatmsg.manager.task.AbstractTaskHandler;
5 | import com.xzy.wechatmsg.request.task.TaskRequest;
6 | import com.xzy.wechatmsg.service.ScheduleTaskService;
7 | import com.xzy.wechatmsg.vo.task.TaskResponseVO;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.stereotype.Service;
10 |
11 | import java.util.List;
12 | import java.util.Map;
13 |
14 | /**
15 | * @description: ScheduleTaskServiceImpl
16 | * @author: Xie zy
17 | * @create: 2022.08.31
18 | */
19 | @Service
20 | public class ScheduleTaskServiceImpl implements ScheduleTaskService {
21 |
22 | @Autowired
23 | Map abstractTaskHandlerMap;
24 |
25 | @Override
26 | public void createTasks(TaskRequest taskRequest) {
27 | AbstractTaskHandler dbTaskHandler = abstractTaskHandlerMap.get(TaskTypeEnum.TASK_TYPE_DB.getHandler());
28 | AbstractTaskHandler appTaskHandler = abstractTaskHandlerMap.get(TaskTypeEnum.TASK_TYPE_APP.getHandler());
29 | if(TaskTypeEnum.TASK_TYPE_DB.getValue().equals(taskRequest.getType())){
30 | //db创建任务 && app启动任务
31 | dbTaskHandler.createTasks(taskRequest);
32 | appTaskHandler.createTasks(taskRequest);
33 | } else if(TaskTypeEnum.TASK_TYPE_APP.getValue().equals(taskRequest.getType())){
34 | //app启动任务
35 | appTaskHandler.createTasks(taskRequest);
36 | }
37 | }
38 |
39 | @Override
40 | public List readTasks() {
41 | AbstractTaskHandler dbTaskHandler = abstractTaskHandlerMap.get(TaskTypeEnum.TASK_TYPE_DB.getHandler());
42 | //读db
43 | return dbTaskHandler.readTasks();
44 | }
45 |
46 | @Override
47 | public void updateTasks(TaskRequest taskRequest) {
48 | AbstractTaskHandler dbTaskHandler = abstractTaskHandlerMap.get(TaskTypeEnum.TASK_TYPE_DB.getHandler());
49 | AbstractTaskHandler appTaskHandler = abstractTaskHandlerMap.get(TaskTypeEnum.TASK_TYPE_APP.getHandler());
50 | //更新db && 更新app
51 | dbTaskHandler.updateTasks(taskRequest);
52 | appTaskHandler.updateTasks(taskRequest);
53 | }
54 |
55 | @Override
56 | public void deleteTasks(TaskRequest taskRequest) {
57 | AbstractTaskHandler dbTaskHandler = abstractTaskHandlerMap.get(TaskTypeEnum.TASK_TYPE_DB.getHandler());
58 | AbstractTaskHandler appTaskHandler = abstractTaskHandlerMap.get(TaskTypeEnum.TASK_TYPE_APP.getHandler());
59 | if(TaskTypeEnum.TASK_TYPE_DB.getValue().equals(taskRequest.getType())){
60 | //db删除任务 && app删除任务
61 | dbTaskHandler.deleteTasks(taskRequest);
62 | appTaskHandler.deleteTasks(taskRequest);
63 | } else if(TaskTypeEnum.TASK_TYPE_APP.getValue().equals(taskRequest.getType())){
64 | //app结束任务
65 | appTaskHandler.deleteTasks(taskRequest);
66 | }
67 | }
68 |
69 | @Override
70 | public void refreshApp() {
71 | AbstractTaskHandler appTaskHandler = abstractTaskHandlerMap.get(TaskTypeEnum.TASK_TYPE_APP.getHandler());
72 | appTaskHandler.refreshApp();
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/utils/TimeUtils.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.utils;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.time.LocalDateTime;
5 |
6 | /**
7 | * @description: TimeUtils
8 | * @author: Xie zy
9 | * @create: 2022.09.03
10 | */
11 | public class TimeUtils {
12 | private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
13 |
14 | public static boolean isUpdateTimeExpired(LocalDateTime updateTime){
15 | LocalDateTime now = LocalDateTime.now();
16 | return updateTime.plusHours(2).isBefore(now);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/vo/BaseResponseVO.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.vo;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Builder;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | /**
9 | * @description: BaseResponseVO
10 | * @author: Xie zy
11 | * @create: 2022.09.01
12 | */
13 | @AllArgsConstructor
14 | @NoArgsConstructor
15 | @Builder
16 | @Data
17 | public class BaseResponseVO {
18 | int code;
19 | Boolean success;
20 | T data;
21 |
22 | public static BaseResponseVO success() {
23 | return BaseResponseVO.builder()
24 | .code(0)
25 | .success(true)
26 | .data("success")
27 | .build();
28 | }
29 |
30 | public static BaseResponseVO success(T data) {
31 | return BaseResponseVO.builder()
32 | .code(0)
33 | .success(true)
34 | .data(data)
35 | .build();
36 | }
37 |
38 | public static BaseResponseVO error() {
39 | return BaseResponseVO.builder()
40 | .code(-1)
41 | .success(false)
42 | .data("error")
43 | .build();
44 | }
45 |
46 | public static BaseResponseVO error(String errMsg) {
47 | return BaseResponseVO.builder()
48 | .code(-1)
49 | .success(false)
50 | .data(errMsg)
51 | .build();
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/xzy/wechatmsg/vo/task/TaskResponseVO.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg.vo.task;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import com.xzy.wechatmsg.vo.BaseResponseVO;
5 | import lombok.AllArgsConstructor;
6 | import lombok.Builder;
7 | import lombok.Data;
8 | import lombok.NoArgsConstructor;
9 |
10 | import java.sql.Date;
11 | import java.sql.Timestamp;
12 | import java.time.LocalDateTime;
13 |
14 | /**
15 | * @description: TaskResponseVO
16 | * @author: Xie zy
17 | * @create: 2022.09.01
18 | */
19 | @AllArgsConstructor
20 | @Data
21 | public class TaskResponseVO extends BaseResponseVO {
22 |
23 | @AllArgsConstructor
24 | @NoArgsConstructor
25 | @Builder
26 | @Data
27 | public static class TaskVO{
28 | Integer taskId;
29 | String msg;
30 | String cron;
31 | /**
32 | * 0-运行结束 | 1-正在运行
33 | */
34 | Integer status;
35 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
36 | LocalDateTime createTime;
37 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
38 | LocalDateTime updateTime;
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/resources/application-demo.yml:
--------------------------------------------------------------------------------
1 | #wechat:
2 | # # Wechat Robot暴露的url
3 | # url: xxx
4 |
5 | #spring:
6 | # datasource:
7 | # driver-class-name: com.mysql.jdbc.Driver
8 | # url: jdbc:mysql://xxx?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
9 | # username: xxx
10 | # password: xxx
11 |
--------------------------------------------------------------------------------
/src/test/java/com/xzy/wechatmsg/WechatMessageApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.xzy.wechatmsg;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class WechatMessageApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------