B079-项目实战--支付模块 定时任务 项目总结

目录

      • 概述
      • 示例
        • jar包
        • 配置类
        • 任务详情
      • 项目应用
        • 封装的工具类QuartzUtils
        • 封装IQuartzSrvice和QuartzServiceImpl
        • 封装参数QuartzJobInfo
        • 编写任务逻辑MainJob
        • 调用第三方支付前添加定时任务
        • 异步回调后移除定时任务
      • 订单支付整体流程

概述

优势:Tmer不支持持久化,重启需要重新安排,Quartz可以持久化,满足大部分需要
缺点:高并发情况下,大量定时任务存在会影响性能。

jvm默认大小500m,定时任务对象太多会内存溢出宕机,该方案只能在1000并发以下比较适合。正常来说我们就满足,只有秒杀,抢单等。。。
高并发情况下要换用消息队列。

原理图
B079-项目实战--支付模块 定时任务 项目总结_第1张图片

示例

jar包

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-quartzartifactId>
dependency>

配置类

添加定时任务的配置类 - 交给spring容器管理就自动会跑起来

@Configuration
public class QuartzConfig {
   @Bean
    public JobDetail newJob(){
        return JobBuilder.newJob(PrintTimeJob.class)//PrintTimeJob我们的业务类
                .withIdentity("9527")//可以给该JobDetail起一个id
                //每个JobDetail内都有一个Map,包含了关联到这个Job的数据,在Job类中可以通过context获取
                .usingJobData("msg", "Hello Quartz")//关联键值对
                .storeDurably()//即使没有Trigger关联时,也不需要删除该JobDetail
                .build();
    }
    @Bean
    public Trigger printTimeJobTrigger() {
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/1 * * * * ? ");
        return TriggerBuilder.newTrigger()
                .forJob(newJob())//关联上述的JobDetail
                .withIdentity("quartzTaskService")//给Trigger起个名字
                .withSchedule(cronScheduleBuilder)
                .build();
    }
}

任务详情

/**
 * 定时任务业务处理类,我们继承QuartzJobBean
 * 重写executeInternal方法来实现具体的定时业务逻辑
 */
public class PrintTimeJob extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        //获取JobDetail中关联的数据
        String msg = (String) context.getJobDetail().getJobDataMap().get("msg");
        System.out.println("current time :"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "---" + msg);

    }
}

项目应用

封装的工具类QuartzUtils

import org.quartz.*;

import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

/**
 * Quartz调度管理器
 */
public class QuartzUtils {
   private static String JOB_GROUP_NAME = "JOB_GROUP_SYSTEM";
   private static String TRIGGER_GROUP_NAME = "TRIGGER_GROUP_SYSTEM";

   /**
    * @Description: 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
    * @param sched 调度器
    * @param jobName 任务名
    * @param cls 任务
    * @param params 任务参数
    * @param time 时间设置,参考quartz说明文档
    * @Title: QuartzManager.java
    */
   public static void addJob(Scheduler sched, String jobName, @SuppressWarnings("rawtypes") Class cls, Object params,
                             String time) {
      try {
         JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);// 任务名,任务组,任务执行类
         @SuppressWarnings("unchecked")

         JobDataMap jobDataMap = new JobDataMap();
         jobDataMap.put("params", params);
         JobDetail jobDetail = newJob(cls).withIdentity(jobKey).setJobData(jobDataMap).build();
         TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);// 触发器
         Trigger trigger = newTrigger().withIdentity(triggerKey).withSchedule(cronSchedule(time)).build();// 触发器时间设定
         sched.scheduleJob(jobDetail, trigger);
         if (!sched.isShutdown()) {
            sched.start();// 启动
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description: 添加一个定时任务
    * @param sched 调度器
    * @param jobName 任务名
    * @param jobGroupName 任务组名
    * @param triggerName 触发器名
    * @param triggerGroupName 触发器组名
    * @param jobClass 任务
    * @param params 任务参数
    * @param time 时间设置,参考quartz说明文档
    * @Title: QuartzManager.java
    */
   public static void addJob(Scheduler sched, String jobName, String jobGroupName, String triggerName,
                             String triggerGroupName, @SuppressWarnings("rawtypes") Class jobClass,  Object params, String time) {
      try {
         JobKey jobKey = new JobKey(jobName, jobGroupName);
         JobDataMap jobDataMap = new JobDataMap();
         jobDataMap.put("params", params);
         @SuppressWarnings("unchecked")
         JobDetail jobDetail = newJob(jobClass).withIdentity(jobKey).setJobData(jobDataMap).build();
         // 触发器
         TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
         Trigger trigger = newTrigger().withIdentity(triggerKey).withSchedule(cronSchedule(time)).build();
         sched.scheduleJob(jobDetail, trigger);
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description: 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)
    * @param sched 调度器
    * @param jobName
    * @param time
    * @Title: QuartzManager.java
    */
   @SuppressWarnings("rawtypes")
   public static void modifyJobTime(Scheduler sched, String jobName, String time) {
      try {
         TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);
         CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
         if (trigger == null) {
            return;
         }
         String oldTime = trigger.getCronExpression();
         if (!oldTime.equalsIgnoreCase(time)) {
            JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);
            JobDetail jobDetail = sched.getJobDetail(jobKey);
            Class objJobClass = jobDetail.getJobClass();
            Object params = jobDetail.getJobDataMap().get("params");
            removeJob(sched, jobName);
            System.out.println("修改任务:" + jobName);
            addJob(sched, jobName, objJobClass, params,time);
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description: 修改一个任务的触发时间
    * @param sched 调度器
    * @param sched 调度器
    * @param triggerName
    * @param triggerGroupName
    * @param time
    * @Title: QuartzManager.java
    */
   public static void modifyJobTime(Scheduler sched, String triggerName, String triggerGroupName, String time) {
      try {
         TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
         CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
         if (trigger == null) {
            return;
         }
         String oldTime = trigger.getCronExpression();
         if (!oldTime.equalsIgnoreCase(time)) {
            // 修改时间
            trigger.getTriggerBuilder().withSchedule(cronSchedule(time));
            // 重启触发器
            sched.resumeTrigger(triggerKey);
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description: 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
    * @param sched 调度器
    * @param jobName
    * @Title: QuartzManager.java
    */
   public static void removeJob(Scheduler sched, String jobName) {
      try {
         TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);
         sched.pauseTrigger(triggerKey);// 停止触发器
         sched.unscheduleJob(triggerKey);// 移除触发器
         JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);
         boolean b = sched.deleteJob(jobKey);// 删除任务
         System.out.println(b);
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description: 移除一个任务
    * @param sched 调度器
    * @param jobName
    * @param jobGroupName
    * @param triggerName
    * @param triggerGroupName
    * @Title: QuartzManager.java
    */
   public static void removeJob(Scheduler sched, String jobName, String jobGroupName, String triggerName,
                                String triggerGroupName) {
      try {
         TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
         sched.pauseTrigger(triggerKey);// 停止触发器
         sched.unscheduleJob(triggerKey);// 移除触发器
         JobKey jobKey = new JobKey(jobName, jobGroupName);
         sched.deleteJob(jobKey);// 删除任务
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description:启动所有定时任务
    * @param sched  调度器
    * @Title: QuartzManager.java
    */
   public static void startJobs(Scheduler sched) {
      try {
         sched.start();
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description:关闭所有定时任务
    * @param sched 调度器
    */
   public static void shutdownJobs(Scheduler sched) {
      try {
         if (!sched.isShutdown()) {
            sched.shutdown();
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }
}

封装IQuartzSrvice和QuartzServiceImpl

IQuartzService

public interface IQuartzService {

    //1.添加定时任务
    void addJob(QuartzJobInfo quartzJobInfo);

    // 2. 移出定时任务
    void removeJob(String jobName);

}

QuartzServiceImpl

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;

@Service
public class QuartzServiceImpl implements IQuartzService {

    @Autowired
    private SchedulerFactoryBean schedulerFactory;

    /*
     * @param scheduler
     * @param jobName
     * @param cls
     * @param params
     * @param time 时间表达式 定时任务执行的时间   已知 执行的时间  Date--->0/1 * * * * ?
     */
    @Override
    public void addJob(QuartzJobInfo quartzJobInfo) {
        /*  Scheduler sched, String jobName, @SuppressWarnings("rawtypes") Class cls, Object params,String time  */
        try {
            QuartzUtils.addJob(schedulerFactory.getScheduler(),quartzJobInfo.getJobName() , MainJob.class,quartzJobInfo.getParams() , quartzJobInfo.getCronj());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void removeJob(String orderSn) {
       QuartzUtils.removeJob(schedulerFactory.getScheduler(),orderSn);
    }
}

封装参数QuartzJobInfo

import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;

@Data
public class QuartzJobInfo implements Serializable {
    private byte type;
    private String jobName;
    private Map<String, Object> params;
    private String cronj;
    private Date fireDate;

    public void setFireDate(Date fireDate) {
        this.fireDate = fireDate;
        String[] cronArr = new String[7];
        for (int i = 0; i < cronArr.length; i++) {
            cronArr[i] = "";
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(fireDate);
        int second = calendar.get(Calendar.SECOND);
        int minute = calendar.get(Calendar.MINUTE);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        int month = calendar.get(Calendar.MONTH) + 1;
        int year = calendar.get(Calendar.YEAR);
        cronArr[0] = second + "";
        cronArr[1] = minute + "";
        cronArr[2] = hour + "";
        cronArr[3] = day + "";
        cronArr[4] = month + "";
        cronArr[5] = "?";
        cronArr[6] = year + "";
        String cron = StringUtils.join(cronArr," ").trim();
        this.setCronj(cron);
    }
}

编写任务逻辑MainJob

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.Map;

@Component
public class MainJob implements Job {

    @Autowired
    private IPayBillService payBillService;
    @Autowired
    private IAdoptOrderService adoptOrderService;
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        //获取定时器里面的数据
        JobDataMap jobDataMap = context.getMergedJobDataMap();
        Map<String,Object> params = (Map<String, Object>) jobDataMap.get("params");
        //获取定时任务的订单号
        String orderSn = params.get("orderSn").toString();

        //执行 修改订单状态的业务逻辑
        //1.改订单的状态
        PayBill payBill = payBillService.loadByUnionPaySn(orderSn);
        payBill.setState(-1);
        payBill.setUpdateTime(new Date());
        payBillService.update(payBill);

        if(PayConstants.BUSINESS_TYPE_ADOPT.equals(payBill.getBusinessType())){//领养订单
            Long orderId = payBill.getBusinessKey();
            AdoptOrder order = adoptOrderService.getById(orderId);
            order.setState(-1);
            adoptOrderService.update(order);
        }
    }
}

调用第三方支付前添加定时任务

AdoptOrderServiceImpl

@Service
public class AdoptOrderServiceImpl extends BaseServiceImpl<AdoptOrder> implements IAdoptOrderService {
    @Autowired
    private PetMapper petMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private AdoptOrderMapper adoptOrderMapper;
    @Autowired
    private UserAddressMapper userAddressMapper;
    @Autowired
    private OrderAddressMapper orderAddressMapper;
    @Autowired
    private EmployeeMapper employeeMapper;
    @Autowired
    private PayBillMapper payBillMapper;
    @Autowired
    private IPayBillService payBillService;
    @Autowired
    private IQuartzService quartzService;

    /**
     * 领养订单结算
     */
    @Override
    @Transactional
    public String submit(Map<String, Object> params, Logininfo loginIn) {
        Long petId = Long.valueOf(params.get("pet_id").toString());
        Long addressId = Long.valueOf(params.get("address_id").toString());//收货地址:t_user_address的id
        Integer payMethod = Integer.valueOf(params.get("pay_method").toString());//1支付宝 2微信  3银联 0余额
        Integer serviceMethod = Integer.valueOf(params.get("service_method").toString());//送货方式

        //1.修改状态  下架
        Pet pet = petMapper.loadById(petId);
        pet.setState(0);
        pet.setOffsaletime(new Date());
        //2.绑定用户
        User user = userMapper.loadByloingInfoId(loginIn.getId());
        pet.setUser(user);
        pet.setUser_id(user.getId());
        pet.setShop_id(pet.getShop().getId());
        petMapper.update(pet);
        //3.生成订单  一次性
        AdoptOrder order = initAdoptOrder(pet, user);
        //骚操作:为了后边操作支付单,不用再来修改订单,先生成统一的支付单号
        String unionPaySn = CodeGenerateUtils.generateUnionPaySn();
        order.setPaySn(unionPaySn);

        adoptOrderMapper.save(order);

        // 3.1 生成订单地址
        UserAddress userAddress = userAddressMapper.loadById(addressId);
        OrderAddress orderAddress = userAddress2OrderAddress(order, userAddress);
        orderAddressMapper.save(orderAddress);

        // 4.支付单
        PayBill payBill = initPayBill(payMethod, pet, user, order);
        payBillMapper.save(payBill);

        // 5.针对支付单添加定时任务
        QuartzJobInfo info = new QuartzJobInfo();
        info.setJobName(order.getPaySn());//定时任务的唯一标识
        Map<String, Object> map = new HashMap<>();
        map.put("orderSn", payBill.getUnionPaySn());
        // 定时任务 需要的 数据
        info.setParams(map);
        info.setFireDate(payBill.getLastPayTime());//最后的支付时间,定时任务的执行时间
        quartzService.addJob(info);

        //调用统一支付接口(老杨定义的)
        return payBillService.pay(payBill);
    }

    private PayBill initPayBill(Integer payMethod, Pet pet, User user, AdoptOrder order) {
        PayBill payBill = new PayBill();
        payBill.setDigest(order.getDigest()+"支付单");
        payBill.setMoney(order.getPrice());
        //重点:支付唯一表示
        payBill.setUnionPaySn(order.getPaySn());
        payBill.setLastPayTime(new Date(System.currentTimeMillis() + 15*60*1000));
        payBill.setPayChannel(Long.valueOf(payMethod));//0 余额 1 支付宝 2 微信 3 银联
        payBill.setBusinessType(PayConstants.BUSINESS_TYPE_ADOPT);
        payBill.setBusinessKey(order.getId());
        payBill.setUser_id(user.getId());
        payBill.setShop_id(pet.getShop().getId());
        payBill.setNickName(user.getUsername());
        return payBill;
    }

    @Override
    public PageList<AdoptOrder> queryAdmin(AdoptOrderQuery query, Long loginInfoId) {
        //1.通过loginInfoID查询出Employee
        Employee employee = employeeMapper.loadByLoginInfoId(loginInfoId);
        //2.如果employee中的shopID不为null,就是店铺。否则就是平台员工
        if (employee.getShop_id() != null) {
            query.setShopId(employee.getShop_id());
        }
        return super.queryPage(query);
    }

    @Override
    public PageList<AdoptOrder> queryUser(AdoptOrderQuery query, Long loginInfoId) {
        User user = userMapper.loadByloingInfoId(loginInfoId);
        query.setUserId(user.getId());
        return super.queryPage(query);
    }

    private OrderAddress userAddress2OrderAddress(AdoptOrder order, UserAddress userAddress) {
        OrderAddress orderAddress = new OrderAddress();
        BeanUtils.copyProperties(userAddress, orderAddress);
        orderAddress.setId(null);
        orderAddress.setOrder_id(order.getId());
        orderAddress.setOrderSn(order.getOrderSn());
        return orderAddress;
    }

    /**
     * alt  + shift + m
     *
     * @param petId
     * @param pet
     * @param user
     * @return
     */
    private AdoptOrder initAdoptOrder(Pet pet, User user) {

        AdoptOrder order = new AdoptOrder();
        order.setDigest("【摘要】" + pet.getName());
        order.setPrice(pet.getSaleprice());
        order.setOrderSn(CodeGenerateUtils.generateOrderSn(user.getId()));
        order.setLastConfirmTime(new Date(System.currentTimeMillis() + PayConstants.LAST_TIME));//最后确认时间
        order.setPet_id(pet.getId());
        order.setUser_id(user.getId());
        order.setShop_id(pet.getShop().getId());
        return order;
    }
}

异步回调后移除定时任务

AlipayController

@RestController
public class AlipayController {

    @Autowired
    private IPayBillService payBillService;
    @Autowired
    private IAlipayInfoService alipayInfoService;
    @Autowired
    private IAdoptOrderService adoptOrderService;
    @Autowired
    private IQuartzService quartzService;

    @PostMapping("/notify")
    public void notify(HttpServletRequest request){
        System.out.println("支付宝异步回调!");
        //获取支付宝POST过来反馈信息
        try {
            Map<String,String> params = new HashMap<String,String>();
            Map<String,String[]> requestParams = request.getParameterMap();
            for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
                String name = (String) iter.next();
                String[] values = (String[]) requestParams.get(name);
                String valueStr = "";
                for (int i = 0; i < values.length; i++) {
                    valueStr = (i == values.length - 1) ? valueStr + values[i]
                            : valueStr + values[i] + ",";
                }
                valueStr = new String(valueStr);
                params.put(name, valueStr);
            }

            String unionPaySn = params.get("out_trade_no");
            PayBill payBill = payBillService.loadByUnionPaySn(unionPaySn);
            if(payBill != null){
                AlipayInfo info = alipayInfoService.getByShopId(payBill.getShop_id());
                boolean signVerified = AlipaySignature.rsaCheckV1(params,
                        info.getAlipay_public_key(),
                        AlipayConfig.charset,
                        AlipayConfig.sign_type); //调用SDK验证签名

                if(signVerified) {//验证成功
                    //商户订单号
                    String out_trade_no = unionPaySn;
                    //支付宝交易号
                    String trade_no = request.getParameter("trade_no");
                    //交易状态
                    String trade_status = request.getParameter("trade_status");
                    if(trade_status.equals("TRADE_FINISHED")){
                        //用户确认
                    }else if (trade_status.equals("TRADE_SUCCESS")){
                        //1. 改支付单状态
                        payBill.setState(1);
                        payBill.setUpdateTime(new Date());
                        payBillService.update(payBill);
                        String businessType = payBill.getBusinessType();
                        //2.再修改对应(领养)的订单状态  businessType   businessKey 订单
                        if(PayConstants.BUSINESS_TYPE_ADOPT.equals(businessType)){//领养订单
                            Long orderId = payBill.getBusinessKey();
                            AdoptOrder order = adoptOrderService.getById(orderId);
                            order.setState(1);
                            adoptOrderService.update(order);
                        }
                        //移除针对当前订单的定时任务
                        quartzService.removeJob(out_trade_no);
                    }
                }else {//验证失败
                    System.out.println("老宋,不要搞事");
                    //调试用,写文本函数记录程序运行情况是否正常
                    //String sWord = AlipaySignature.getSignCheckContentV1(params);
                    //AlipayConfig.logResult(sWord);
                }
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

订单支付整体流程

B079-项目实战--支付模块 定时任务 项目总结_第2张图片

你可能感兴趣的:(笔记总结,java,后端)