├── .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 | --------------------------------------------------------------------------------