1, Foreword
Finally, when it's time to get the second shot of vaccine, I have no choice but to open the "Guangzhou Health Link" or "Guangdong Health Link" applet every time. Every time, I am booked. Guangzhou has a large population. Maybe many people have been waiting for the number in front of the applet, so this article was born.
2, Principle
This program is simple to say. It can't directly help you make an appointment, and remind yourself to go to the applet to make an appointment. Using scheduled tasks, every 5 or 10 minutes, Go to the interface of "Guangzhou Health Link" to query the remaining number of people in each district It is too laggy to say that I am breaking down the server. It is no wonder that every time I open the applet, it is very expensive. I will adjust the frequency of the 5 minute adjustment in one hour, and then I will tune it 12 times in an hour. If it can not carry the number of visits I have made in an hour or 12 times, how can it serve the masses of the people better? If the remaining amount of more than 50 people is monitored, notify me by sending QQ email. Wechat can be bound to QQ email. Therefore, if there are more than 50 people remaining, my wechat and QQ will receive email reminders at the same time. Well, I thought it was great from the beginning.
Target interface: https://m.r.umiaohealth.com/Home/GetTicketReport
Of course, this interface can not be adjusted casually. You need to add authentication. You can download a packet capturing tool on your mobile phone. I use HttpCanary software. You can obtain the required Cookie authentication value through packet capturing through this tool.
3, Realize
To build the SpringBoot project, I quote Hutool's email tool here to be lazy. pom relies on the following:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.1</version> <relativePath /> <!-- lookup parent from repository --> </parent> <groupId>com.chen</groupId> <artifactId>AppointmentTask</artifactId> <packaging>jar</packaging> <version>0.0.1-SNAPSHOT</version> <name>AppointmentTask</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.6.7</version> </dependency> <dependency> <groupId>com.sun.mail</groupId> <artifactId>javax.mail</artifactId> <version>1.6.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <finalName>App</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application. The configuration of properties is as follows. There is only one cron expression configuration. The timing task I use here is the latest Spring Task, which is more concise and convenient than quartz. This expression means that every hour starts from zero and every 5 minutes
mytask.corn1=0 0/5 * * * ?
I have written two interfaces in the controller layer, one is to start the scheduled task and the other is to close the scheduled task. The code is as follows:
package com.chen.controller; import java.util.Date; import java.util.concurrent.ScheduledFuture; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.TriggerContext; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.support.CronTrigger; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.chen.entity.TaskConfiguration; import com.chen.task.MyRunnableTask; @RestController @RequestMapping("/quartz/task") public class DynamicTaskController { @Autowired private TaskConfiguration taskConfiguration; @Autowired private ThreadPoolTaskScheduler threadPoolTaskScheduler; private ScheduledFuture<?> future1; @Bean public ThreadPoolTaskScheduler threadPoolTaskScheduler() { return new ThreadPoolTaskScheduler(); } @GetMapping("/start") public String start() { future1 = threadPoolTaskScheduler.schedule(new MyRunnableTask(),new Trigger(){ @Override public Date nextExecutionTime(TriggerContext triggerContext){ return new CronTrigger(taskConfiguration.getCorn1()).nextExecutionTime(triggerContext); } }); System.out.println("Scheduled task started successfully"); return "success"; } @GetMapping("/stop") public String stop() { if (future1 != null) { future1.cancel(true); } System.out.println("Stop scheduled tasks"); return "success"; } }
Configure cron and associate the expression written in the configuration file
package com.chen.entity; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "mytask") public class TaskConfiguration { private String corn1; public String getCorn1() { return corn1; } public void setCorn1(String corn1) { this.corn1 = corn1; } }
In terms of interface adjustment, I use RestTemplate, which is simple and rough. Note that two mailboxes are needed here, one is the sender's mailbox and the other is the receiver's mailbox. The sender's mailbox needs to go to the mailbox account to start the POP3/SMTP service and obtain the authorization password. It needs to be used in the code. In addition, it should be noted that if the code is written, the project should be deployed to Alibaba cloud server, The port number cannot be written with port 25 as described by Hutool. It should be changed to port 465 as I did. Otherwise, I will go to Alibaba cloud to apply for unsealing port 25. This is due to the disabling of some Alibaba cloud security policies (I stepped on this pit).
Another point is that I have limited 50 people here, which means that I will be notified by email only when the remaining 50 people can make an appointment. After all, there is a delay in the data on the applet. It clearly shows that there are several people remaining, but I can't find the vaccination point. I guess the back end of this small program should use message oriented middleware or some frameworks to cope with this large amount of data and "peak shaving".
package com.chen.task; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Map; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import com.chen.controller.DynamicTaskController; import cn.hutool.core.collection.CollUtil; import cn.hutool.extra.mail.MailAccount; import cn.hutool.extra.mail.MailUtil; public class MyRunnableTask implements Runnable { private final String APPOINTMENTURL = "https://m.r.umiaohealth.com/Home/GetTicketReport"; private final String COOKIE = "Write here from the bag grabbing tool HttpCanary I got it from the request header of the interface Cookie value"; private static int num = 0; // To prevent sending emails all the time, I have added a property to limit it. It is only allowed to send emails once @Override public void run() { RestTemplate restTemplate = new RestTemplate(); System.out.println("first DynamicTask," + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); HttpHeaders headers = new HttpHeaders(); headers.set("Cookie", COOKIE); if (num <= 2) { System.out.println("Start getting appointment data....."); @SuppressWarnings("rawtypes") HttpEntity<Map> res = restTemplate.exchange(APPOINTMENTURL, HttpMethod.GET, new HttpEntity<>(null, headers), Map.class); Object object = res.getBody().get("aaData"); List<Map<String, Object>> cityData = (List<Map<String, Object>>) object; for (int i = 0; i < cityData.size(); i++) { if (cityData.get(i).get("District").toString().equals("Tianhe District")) { int remainingNumber = (int) cityData.get(i).get("RemainingNumber"); System.out.println("Tianhe District surplus " + remainingNumber + " people"); if (remainingNumber >= 50 && num <= 0) { MailAccount account = new MailAccount(); account.setHost("smtp.163.com"); account.setPort(465); account.setAuth(true); account.setFrom("Write your email here@163.com"); // Sender email account.setUser("Write your email here@163.com"); // sender account.setPass("Write the authorization password"); // Authorization password account.setSslEnable(true); MailUtil.send(account, CollUtil.newArrayList("Recipient email@qq.com"), "Appointment reminder", "Tianhe District surplus " + remainingNumber + " People, please go to Guangzhou Health Link applet to make an appointment as soon as possible", false); System.out.println("Mail sent successfully"); num++; } break; } } } else { System.out.println("Reservation data will not be obtained temporarily......"); } } }
Adding annotations on the entry program allows the scheduled task to start
package com.chen; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling public class AppointmentTaskApplication { public static void main(String[] args) { SpringApplication.run(AppointmentTaskApplication.class, args); } }
After running this program on Alibaba cloud for less than an hour, I successfully made an appointment. The simple happiness of programmers is no more than that.