In the first two articles, we implemented synchronous/asynchronous sending SMS messages and limiting the frequency of sending SMS messages. In this article, we introduce the limiting the number of times we send SMS messages to the same user (judging based on mobile phone number and IP) every day.
1. Data table structure
Since we need to record the sending records throughout the day, we save the data to the database here. The data table structure is as follows:
type is the type of verification code, such as registration, reset password, etc.
The default value of sendTime is the current time.
2. Limit the number of daily sending times
We need to use the interface and entity classes mentioned in the previous article here.
DailyCountFilter.java
public class DailyCountFilter implements SmsFilter { private int ipDailyMaxSendCount; private int mobileDailyMaxSendCount; private SmsDao smsDao; // Some useless code is omitted @Override public boolean filter(SmsEntity smsEntity) { if (smsDao.getMobileCount(smsEntity.getMobile()) >= mobileDailyMaxSendCount) { return false; } if (smsDao.getIPCount(smsEntity.getIp()) >= ipDailyMaxSendCount) { return false; } smsDao.saveEntity(smsEntity); return true; }}The main code is very simple. First, determine whether the number of times sent to the specified mobile phone number has reached the maximum number of sending times, and then determine whether the number of times sent by the specified IP request has reached the maximum number. If none of them are, save the mobile phone number, IP and other information sent this time to the database.
Of course, there are certain problems with this class: other threads may have saved new data between judging whether the maximum number exceeds the maximum number and the entity data being saved. This results in the above two judgments not absolutely accurate.
We can use serialization level transactions to ensure that there will be no errors, but the cost is too high. Therefore, we will not do the processing here. Because we have implemented limiting the transmission frequency before. If we use FrequencyFilter to filter once and limit the transmission frequency, then the problem mentioned above is basically impossible.
There is another problem: over time, this table will become larger and larger, resulting in a rather poor query performance. We can delete useless data every once in a while as we did in the previous article; we can also create tables dynamically and then insert data into the new table.
3. Use dynamic tables
Here we adopt the second solution: the name of the data table is "sms_four-digit year_two-digit month", such as "sms_2016_02". When inserting data, get the table name based on the current time, and then insert it. In addition, use Quartz to generate the data table for the next month and next month at 2 o'clock on the 20th of each month:
We first modify the DailyCountFilter class, add task plan to this class, and generate data tables regularly:
DailyCountFilter.java
// Based on the above code, add the following code public class DailyCountFilter implements SmsFilter { private Scheduler sched; @Override public void init() throws SchedulerException { smsDao.createTable(0); // Create this month's data table smsDao.createTable(1); // Create next month's data table SchedulerFactory sf = new StdSchedulerFactory(); sched = sf.getScheduler(); // Create Quartz container JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("smsDao", smsDao); // Create a data map that needs to be used when running a task // Create a job object, which executes the actual task JobDetail job = JobBuilder.newJob(CreateSmsTableJob.class) .usingJobData(jobDataMap) .withIdentity("create sms table job").build(); // Create a trigger object, which is used to describe the time rules for triggering the execution of the job // For example, CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity("create sms table trigger") .withSchedule(CronScheduleBuilder.cronSchedule("0 0 2 20 * ?"))// 2 o'clock on the 20th of each month.build(); sched.scheduleJob(job, trigger); // Register the task and trigger the rules sched.start(); // Start scheduling} @Override public void destroy() { try { sched.shutdown(); } catch (SchedulerException e) {} } public static class CreateSmsTableJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap dataMap = context.getJobDetail().getJobDataMap(); SmsDao smsDao = (SmsDao) dataMap.get("smsDao"); // Get the passed smsDao object smsDao.createTable(1); // Create the next month's data table smsDao.createTable(2); // Create the next month's data table } }}Next, let's take a look at some of the codes of SmsDao:
SmsDao.java
public class SmsDao { /** * Create a new log table* * @param monthExcursion Number of months offset*/ public void createTable(int monthExcursion){ String sql = "CREATE TABLE IF NOT EXISTS " + getTableName(monthExcursion) + " LIKE sms"; // Execute sql statement} /** * Save SmsEntity entity object*/ public void saveEntity(SmsEntity smsEntity){ String sql = "INSERT INTO " + getNowTableName() + " (mobile, ip, type) VALUES(?, ?, ?)"; // Execute the sql statement} /** * Get the specified mobile number and request the SMS today* * @param mobile user mobile number * @return Number of times request the SMS today*/ public long getMobileCount(String mobile){ String sql = "SELECT count(id) FROM " + getNowTableName() + " WHERE mobile=? AND time >= CURDATE()"; // Execute the sql statement and return the query result} // The getIPCount method is omitted/** * Get the name of the table used now*/ private String getNowTableName() { return getTableName(0); } private DateFormat dateFormat = new SimpleDateFormat("yyyy_MM"); /** * Get the table name of the month offset month* * @param monthExcursion Number of months offset* @return Table name of the corresponding month*/ private String getTableName(int monthExcursion) { Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.MONTH, monthExcursion); Date date = calendar.getTime(); return "sms_" + dateFormat.format(date); }}There is a prerequisite for the successful operation of the createTable method in SmsDao, which is that there is a sms data table. The createTable method will copy the structure of the sms table to create a new data table.
We retain the data for sending text messages (mobile phone number, ip, time, etc.) instead of directly deleting them, because we may need to analyze these data in the future to obtain the information we want, such as judging the arrival rate of the service provider's SMS, whether someone maliciously sent text messages, etc. We may even get unexpected "surprise".
The above is all about this article, I hope you can continue to pay attention.