SpringBoot+Vue实现动态定时任务

遇到这样一个需求,银行办理业务时,客户跟客服预约一个时间办理业务,预约时,后台代码要判断是邮箱预约还是电话短信预约,预约成功后我们要马上给客户发一个短信告知客户预约成功,然后在距离办理业务开始前五分钟再次发送一次信息,让客户点击短链接直接跳到办理业务的页面。

因为普通的定时任务只能设置到每天固定一个时间点触发任务,而银行办理业务有可能一天几十甚至上百笔的订单,而且是要开始前五分钟触发的,所以一天要触发几十上百次,并且时间是不确定的。

这种情况,其中的一种办法就是我们计算好触发定时任务的时间,存到数据库里面,比如我预约2022年7月1日8:55分---即202207010855办理业务,那么根据业务需求提前五分钟二次发送短信通知,就是2022年7月1日8.50分发短信通知,即202207010850,我们直接拿202207010855-5分钟=202207010850,这个时候是没问题的,那么我预约2022年7月1日9:00办理业务的话,即202207010900,同理二次发送短信的时间为8.55分,即202207010855,这时候我们也拿202207010900-5分钟=202207010895,这时候你会发现,他已经不等于202207010855,即如果是整数点数的话直接拿开始时间减去5分钟来表示定时任务触发时间是不可行的,我们要设置出来一个万能的公式。所以要用到cron表达式,我们可以把时间转成cron表达式来放到数据库,不理解啥是cron表达式的同学自行补一下哈,网上很多。分析完需要做什么之后就好办了,接下来就是一步一步实现。

package com.csii.framework.video.services.action.seat.base;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;

import com.csii.framework.commons.db.DBManager;
import com.csii.framework.video.core.jedis.JedisHelper;
import com.csii.framework.video.db.mapper.AppointmentMapper;
import com.csii.framework.video.services.utils.SeatUtil;
import com.csii.framework.video.services.utils.StringUtil;
import com.csii.framework.video.services.utils.shortUrl.ShortUrl;
import com.csii.pe.action.support.AbstractJdbcExecutableAction;
import com.csii.pe.core.Context;
import com.csii.pe.core.PeException;

/**
 * @Author: chejinkun
 * @Description: 预约接口
 * @Date: 2021/12/27
 * @Version: 1.0
 */
@Component // 此注解加启动类的@EnableScheduling注解自动触发定时任务
public class AppointmentAddAction extends AbstractJdbcExecutableAction {
    private static Log logger = LogFactory.getLog(AppointmentAddAction.class);
    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    private ScheduledFuture<?> future;

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        return new ThreadPoolTaskScheduler();
    }

    @Value("${appoint.tokenUrl}")
    private String tokenUrl;

    @Value("${appoint.shortUrlRealm}")
    private String shortUrlRealm;

    @Value("${appoint.maxDay}")
    private int appointMaxDay;

    private SeatUtil seatUtil;

    public void setSeatUtil(SeatUtil seatUtil) {
        this.seatUtil = seatUtil;
    }

    @Override
    public void execute(Context context) throws PeException {
        logger.info("===============预约接口开始=================");
     
        String lang = context.getString("lang");//获取语种
        String gbiz_sequence_id = StringUtil.getBizId();// 随机全局流水号
        String biz_sequence_id = StringUtil.getBizSequenceId();// 随机流水号
        String msg = context.getString("msg");//短信内容
        String phoneNumber = context.getString("phoneNumber");// 客户号码
        String mainSubject = context.getString("mainSubject");邮件主题
        String inputtime = context.getString("inputtime");
        String shortMessage = context.getString("shortMessage");// 短信二次发送时间---此时间在前端已做处理---减好五分钟
        // 生成短链接
        String[] aResult = ShortUrl.shortUrl(tokenUrl);
        JedisHelper jedisHelper = JedisHelper.getInstance();
        boolean redisresult = jedisHelper.save(aResult[0], tokenUrl, appointMaxDay * 24 * 60 * 60);
        if (redisresult) {
            // 拼接短链接
            String shortUrl = shortUrlRealm + aResult[0];
            if (lang.equals("en_US")){
                msg += ",click " + shortUrl + " One touch call.";
            }else if (lang.equals("zh_TW")){
                msg += ",點擊 " + shortUrl + " 一鍵呼叫,";
            }else {
                msg += ",点击 " + shortUrl + " 一键呼叫,";
            }

            msg += ", click " + shortUrl + " one touch to call.";
        }
        Map map = new HashMap();
        map.put("mainSubject", mainSubject);
        map.put("biz_sequence_id", biz_sequence_id);
        map.put("gbiz_sequence_id", gbiz_sequence_id);
        map.put("inputtime", inputtime);// 预约开始时间
        map.put("updatetime", context.getString("updatetime"));// 预约结束时间
        map.put("name", context.getString("name"));// 客户姓名
        map.put("no", context.getString("no"));// 身份证号
        map.put("phoneNumber", phoneNumber);// 联系方式
        map.put("msg", msg);// 短信回复内容
        map.put("lang",lang);//语言入库
 
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmm");// int类型转Data类型
        Date date = null;
        try {
            date = formatter.parse("" + shortMessage);// date为预约时间的前5分钟
        } catch (ParseException e) {
            e.printStackTrace();
        }
        // String cron= getCron(new Date());
        String cron = getCron(date);// 把日期date转成cron表达式
        map.put("cron", cron);// 定时任务corn表达式入库
        // 随机获取32位16进制数作为客户编号
        String customer_core_no = String.valueOf(StringUtil.getRandom(32));
        map.put("customer_core_no", customer_core_no);
        //根据前端传过来的参数有没有邮件主题来判断是走短信发送还是邮件发送
        if (mainSubject != null) {
            seatUtil.sendEmail(gbiz_sequence_id, biz_sequence_id, phoneNumber, mainSubject, msg);// phoneNumber邮箱
                                                                                                 // mainSubject主题
                                                                                                 // msg邮件内容
        } else {
            seatUtil.sendSms(gbiz_sequence_id, biz_sequence_id, msg, phoneNumber);// phoneNumber号码 msg短信内容
        }
        DBManager.get(AppointmentMapper.class).inserAppointment(map);
        logger.info("===============预约接口结束=================");
    }

    /*
    * 日期转换cron表达式
    * */
    public static String getCron(java.util.Date date) {

        String dateFormat = "mm HH dd MM ? yyyy";

        return formatDateByPattern(date, dateFormat);

    }

    public static String formatDateByPattern(Date date, String dateFormat) {

        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);

        String formatTimeStr = null;

        if (date != null) {
            formatTimeStr = sdf.format(date);

        }
        return formatTimeStr;

    }

}

每个公司用的框架不一样,但是这里的重点就是要前端代码那边处理好触发定时任务的时间点,我们在后端把他转成cron表达式然后把他放到数据库就可以了。

完成了这个步骤之后咱就开始要写触发定时任务的代码。

package com.csii.framework.video.services.action.seat.base;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import com.csii.framework.commons.db.DBManager;
import com.csii.framework.video.core.jedis.JedisHelper;
import com.csii.framework.video.db.mapper.AppointmentMapper;
import com.csii.framework.video.services.utils.SeatUtil;
import com.csii.framework.video.services.utils.shortUrl.ShortUrl;

import lombok.SneakyThrows;
import org.springframework.util.StringUtils;

/**
 * @Author: chejinkun
 * @Description: 定时任务
 * @Date: 2021/12/27
 * @Version: 1.0
 */
@Configuration
@EnableScheduling// 与SpringBoot启动类中的@EnableScheduling注解配好使用使得动态定时任务随时执行
@Component
public class TestTask implements SchedulingConfigurer {
    private SeatUtil seatUtil;

    @Value("${appoint.tokenUrl}")
    private String tokenUrl;

    @Value("${appoint.shortUrlRealm}")
    private String shortUrlRealm;

    @Value("${appoint.maxDay}")
    private int appointMaxDay;

    public void setSeatUtil(SeatUtil seatUtil) {
        this.seatUtil = seatUtil;
    }

    Runnable runnable = new Runnable() {
        @SneakyThrows
        @Override
        public void run() {
            String biz_sequence_id = DBManager.get(AppointmentMapper.class).selectBiz();// 拿业务流水号
            String gbiz_sequence_id = DBManager.get(AppointmentMapper.class).selectGBiz();// 拿全局流水号
            String phoneNumber = DBManager.get(AppointmentMapper.class).selectPho();// 拿号码
            String mainSubject = DBManager.get(AppointmentMapper.class).selectMainSubject();// 拿邮件主题
            String lang = DBManager.get(AppointmentMapper.class).selectLang();// 拿邮件主题
            String msg1 = "您好,距离你办理业务开始时间还剩5分钟,请及时点击软链接进入房间来办理业务";
            String msg2 = "Hello, there are 5 minutes left before you start to handle business. Please click the soft link in time to enter the room to handle business";
            String msg3 = "您好,距離你辦理業務開始時間還剩5分鐘,請及時點擊軟連結進入房間來辦理業務";
            // 生成短链接
            String[] aResult = ShortUrl.shortUrl(tokenUrl);
            JedisHelper jedisHelper = JedisHelper.getInstance();
            boolean redisresult = jedisHelper.save(aResult[0], tokenUrl, appointMaxDay * 24 * 60 * 60);
            if (redisresult) {
                // 拼接短链接
                String shortUrl = shortUrlRealm + aResult[0];
                if (lang.equals(lang.equals("en_US"))){
                    msg1 += ",click " + shortUrl + " One touch call.";
                }else if (lang.equals("zh_TW")){
                    msg1 += ",點擊 " + shortUrl + " 一鍵呼叫,";
                }else {
                    msg1 += ",点击 " + shortUrl + " 一键呼叫。";
                }
            }
            if (mainSubject!=null&&lang.equals("zh_CN")){
                seatUtil.sendEmail(biz_sequence_id,gbiz_sequence_id,msg1,phoneNumber,mainSubject);//phoneNumber:邮箱  mainSubject:邮件主题
            }else if (mainSubject!=null&&lang.equals("en_US")){
                seatUtil.sendEmail(biz_sequence_id,gbiz_sequence_id,msg2,phoneNumber,mainSubject);
            }else if (mainSubject!=null&&lang.equals("zh_TW")){
                seatUtil.sendEmail(biz_sequence_id,gbiz_sequence_id,msg3,phoneNumber,mainSubject);
            }else if (mainSubject==null&&lang.equals("zh_CN")){
                seatUtil.sendSms(biz_sequence_id, gbiz_sequence_id, msg1, phoneNumber);// 短信二次发送   phoneNumber:号码
            }else if (mainSubject==null&&lang.equals("en_US")){
                seatUtil.sendSms(biz_sequence_id, gbiz_sequence_id, msg2, phoneNumber);
            }
            else if (mainSubject==null&&lang.equals("zh_TW")){
                seatUtil.sendSms(biz_sequence_id, gbiz_sequence_id, msg3, phoneNumber);// 短信二次发送   phoneNumber:号码
            }
        }
    };

    /**
     * 执行定时任务.
     */
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(
                //1.添加任务内容(Runnable)
                () -> runnable.run(),//任务内容
                //2.设置执行周期(Trigger)触发定时器
                triggerContext -> {
                    //2.1 从数据库获取执行周期
                    String cron = DBManager.get(AppointmentMapper.class).selectCron();// 数据库查cron表达式作为条件
                    //2.2 合法性校验.
                    if (StringUtils.isEmpty(cron)) {
                        System.out.println("没有定时任务可执行");
                    }
                    //2.3 返回执行周期(Date)
                    return new CronTrigger(cron).nextExecutionTime(triggerContext);
                }
        );
    }
}

编写完定时任务之后记得在SpringBoot启动类上添加@EnableScheduling注解,这个注解配合定时任务上的@Component注解,可以做到实时触发。

拼接短链接时看到很多看似重复的if else,因为我们做的是国外的银行项目,所以要做国际化的功能,做一下语种判断,下一篇顺带把前端Vue的国际化和后台的国际化讲一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值