关于对逾期提醒的定时任务~改进完善

Spring Boot 中实现到期提醒任务的定时Job详解

在金融或借贷系统中,到期提醒是常见的功能需求。通过定时任务,可以定期扫描即将到期的借款记录,并生成或更新提醒信息。本文基于提供的三个JobHandler类(FarExpireRemindJob、MidExpireRemindJob 和 RecentExpireRemindJob),详细分析其实现逻辑。这些类使用Spring Boot的组件注解(@Component),并实现了JobHandler接口(假设这是自定义的定时任务接口),结合MyBatis-Plus的QueryWrapper进行数据库操作。代码涉及借款入库(LoanInDO)、借款出库(LoanOutDO)和提醒记录(RemindExpireDO)的处理。

本文将逐一拆解每个类的代码结构、核心逻辑、潜在优化点和注意事项,确保覆盖所有提供的代码片段。假设这些Job是集成在XXL-JOB或类似调度框架中的定时任务。

1. 整体功能概述
  • FarExpireRemindJob:负责远期到期提醒(默认30天)。扫描即将到期的借款记录,生成提醒记录,并更新借款记录的提醒标志。
  • MidExpireRemindJob:负责中期到期提醒(默认15天)。更新已存在的提醒记录,调整提醒状态和时间。
  • RecentExpireRemindJob:负责近期到期提醒(默认7天)。类似于中期提醒,更新提醒记录的状态和时间。 这些Job通过参数(param)支持自定义天数,支持事务回滚(仅FarExpireRemindJob有@Transactional注解),并返回执行结果字符串,便于日志记录或监控。

依赖的Mapper(如LoanInMapper、RemindExpireMapper等)使用MyBatis-Plus进行CRUD操作。DO类(如LoanInDO)是数据库实体,包含字段如id、custId、expireTime等。

2. FarExpireRemindJob 详细分析

这个类是三个Job中最复杂的,涉及多表操作和批量更新。它的目的是在借款到期前一定天数(默认30天)生成提醒记录,并标记借款记录已提醒。

代码结构:

  • 类注解和依赖注入:

复制代码
  @Component
  public class FarExpireRemindJob implements JobHandler {
      
      @Autowired
      private LoanInMapper loanInMapper;
      @Autowired
      private RemindExpireMapper remindExpireMapper;
      @Autowired
      private LoanOutMapper loanOutMapper;
    • @Component:将类注册为Spring Bean,便于调度框架注入和调用。
    • 注入三个Mapper:用于查询和更新借款入库、出库和提醒表。
  • execute方法(核心执行逻辑):

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String execute(String param) throws Exception {
    // 到期提醒默认值
    int day = (StringUtils.isEmpty(param)) ? 30 : Integer.parseInt(param);
    List<LoanInDO> loanInDOList = loanInMapper.selectList(
    new QueryWrapperX<LoanInDO>()
    .eq("overdue_flag", 0)
    .eq("expire_remind_flag", 0)
    .le("expire_time", LocalDate.now().plusDays(day))
    );
    List<LoanOutDO> loanOutDOList = loanOutMapper.selectList(
    new QueryWrapper<LoanOutDO>()
    .eq("overdue_flag", 0)
    .eq("expire_remind_flag", 0)
    .le("expire_time", LocalDate.now().plusDays(day))
    );

  • 解析参数:如果param为空,默认day=30,否则转换为整数。

  • 查询借款入库记录:使用QueryWrapperX(MyBatis-Plus扩展)过滤未逾期(overdue_flag=0)、未提醒(expire_remind_flag=0)、到期时间 <= 当前日期 + day 的记录。

  • 类似查询借款出库记录。注意:QueryWrapperX可能是自定义的Wrapper,支持更灵活的查询。

    复制代码
      List<RemindExpireDO> remindExpireDOList = new ArrayList<>();
      loanInDOList.forEach(loanInDO -> {
          loanInDO.setExpireRemindFlag(1);
          RemindExpireDO remindExpireDO = RemindExpireDO.builder()
                  .loanId(loanInDO.getId())
                  .custId(loanInDO.getCustId())
                  .custCode(loanInDO.getCustCode())
                  .custName(loanInDO.getCustName())
                  .contractCode(loanInDO.getContractCode())
                  .amount(loanInDO.getAmount())
                  .balance(loanInDO.getBalance())
                  .beginTime(loanInDO.getBeginTime())
                  .expireTime(loanInDO.getExpireTime())
                  .remindTime(LocalDateTime.now())
                  .showFlag(1)
                  .remindStatus(day)
                  .remindFlag(1)
                  .userName(loanInDO.getUserName())
                  .userId(loanInDO.getUserId())
                  .deptName(loanInDO.getDeptName())
                  .deptId(loanInDO.getDeptId())
                  .build();
          loanInDO.setExpireRemindFlag(1);  // 重复设置,可能是笔误
          remindExpireDOList.add(remindExpireDO);
      });
  • 处理借款入库列表:遍历每个LoanInDO,设置expireRemindFlag=1(标记已提醒)。

  • 使用Lombok的builder模式创建RemindExpireDO,复制借款相关字段,并设置提醒时间、状态等。

  • 注意:loanInDO.setExpireRemindFlag(1); 出现了两次,第二次是多余的,可能为代码笔误,会导致无谓操作但不影响功能。

  • 添加到remindExpireDOList中。

    复制代码
      loanOutDOList.forEach(loanOutDO -> {
          loanOutDO.setExpireRemindFlag(1);
          RemindExpireDO remindExpireDO = RemindExpireDO.builder()
                  // ... 与入库类似,复制字段 ...
                  .build();
          loanOutDO.setExpireRemindFlag(1);  // 同样重复设置
          remindExpireDOList.add(remindExpireDO);
      });

处理借款出库列表:逻辑与入库完全相同,包括重复的setExpireRemindFlag。

复制代码
    if (!loanInDOList.isEmpty()) {
        loanInMapper.updateBatch(loanInDOList);
    }
    if (!loanOutDOList.isEmpty()) {
        loanOutMapper.updateBatch(loanOutDOList);
    }
    if (!remindExpireDOList.isEmpty()) {
        remindExpireMapper.insertBatch(remindExpireDOList);
    }
    return String.format("已产生 %s 条到期提醒", remindExpireDOList.size());
}
    • 批量更新借款记录(如果列表非空)。
    • 批量插入提醒记录。
    • 返回结果字符串,报告生成的提醒条数。
    • @Transactional:确保所有操作在事务中,如果异常则回滚,防止数据不一致。

分析与优化点:

  • 优点:批量操作高效,避免了循环中的单条更新;事务保障数据一致性;参数化day支持灵活配置。
  • 潜在问题:如果借款记录量大,查询和遍历可能耗时,需要监控性能。重复的setExpireRemindFlag是小bug,可移除第二次调用。未处理param解析异常(e.g., 非数字),建议添加try-catch。
  • 改进建议:添加日志记录(e.g., SLF4J);如果day很大,查询条件可优化索引(expire_time字段应有索引);考虑分页查询以防内存溢出。
3. MidExpireRemindJob 详细分析

这个Job专注于更新中期提醒(默认15天),不生成新记录,只更新现有提醒的status和time。

代码结构:

  • 类注解和依赖注入:

    @Component
    public class MidExpireRemindJob implements JobHandler {

    复制代码
      @Autowired
      private RemindExpireMapper remindExpireMapper;
    • 类似前者,只注入提醒Mapper。
  • execute方法:

    @Override
    public String execute(String param) throws Exception {
    int day = (StringUtils.isEmpty(param)) ? 15 : Integer.parseInt(param);
    List<RemindExpireDO> remindExpireDOS = remindExpireMapper.selectList(
    new QueryWrapperX<RemindExpireDO>()
    .between("expire_time",
    LocalDate.now().plusDays(day),
    LocalDate.now().plusDays(day + 1))
    );
    remindExpireDOS.forEach(remindExpireDO -> {
    remindExpireDO.setRemindTime(LocalDateTime.now());
    remindExpireDO.setRemindStatus(day);
    });
    if (!remindExpireDOS.isEmpty()) {
    remindExpireMapper.updateBatch(remindExpireDOS);
    }
    return String.format("更新了 %d 条提醒", remindExpireDOS.size());
    }

    • 解析day,默认15。
    • 查询提醒记录:到期时间在 [当前+day, 当前+day+1) 之间,使用between条件。
    • 遍历更新:设置当前提醒时间和status=day。
    • 批量更新,如果非空。
    • 返回更新条数。

分析与优化点:

  • 优点:简单高效,只更新必要字段;between条件精确匹配特定天窗。
  • 潜在问题:未加@Transactional,如果更新失败无回滚(与其他Job不一致)。查询未过滤已逾期或已处理记录,可能重复更新。
  • 改进建议:添加事务注解;添加过滤条件如.eq("remindStatus", > day) 以避免重复;考虑day的边界(如day=0时)。
4. RecentExpireRemindJob 详细分析

与MidExpireRemindJob几乎相同,仅默认day=7和返回字符串略不同,用于近期提醒。

代码结构:

  • 类注解和依赖注入: 同Mid,仅注入RemindExpireMapper。

  • execute方法:

    @Override
    public String execute(String param) throws Exception {
    int day = (StringUtils.isEmpty(param)) ? 7 : Integer.parseInt(param);
    List<RemindExpireDO> remindExpireDOS = remindExpireMapper.selectList(
    new QueryWrapperX<RemindExpireDO>()
    .between("expire_time",
    LocalDate.now().plusDays(day),
    LocalDate.now().plusDays(day + 1))
    );
    remindExpireDOS.forEach(remindExpireDO -> {
    remindExpireDO.setRemindTime(LocalDateTime.now());
    remindExpireDO.setRemindStatus(day);
    });
    if (!remindExpireDOS.isEmpty()) {
    remindExpireMapper.updateBatch(remindExpireDOS);
    }
    return String.format("更新了 %d 条提醒记录", remindExpireDOS.size());
    }

    • 逻辑完全一致,仅day默认7,返回消息为"提醒记录"。

分析与优化点:

  • 优点:代码复用性高,与Mid类似,便于维护。
  • 潜在问题:同Mid,无事务;可能与Mid重叠更新(如果day重合)。
  • 改进建议:合并Mid和Recent为一个Job,通过param区分;添加唯一性检查避免多Job冲突。
5. 整体系统设计与注意事项
  • 定时调度:这些Job适合每天运行一次(e.g., 凌晨)。FarJob生成初始提醒,Mid/Recent逐步更新status,形成多级提醒(如30→15→7天)。
  • 数据一致性:Far有事务,其他无,建议统一添加。所有Job使用LocalDate.now(),注意时区。
  • 性能考虑:批量操作好,但大表需索引(expire_time、flags)。错误处理:param非数字时抛异常,可加校验。
  • 扩展性:可添加短信/邮件通知在更新后;DO类字段丰富,支持报表。
  • 测试建议:单元测试查询条件;集成测试事务回滚。

通过这些Job,可以实现自动化到期提醒系统。如果需要完整项目代码或进一步优化,欢迎评论交流!

相关推荐
Sally璐璐5 小时前
Go正则表达式实战指南
数据库·mysql·golang
百锦再6 小时前
脚本语言的大浪淘沙或百花争艳
java·开发语言·人工智能·python·django·virtualenv·pygame
小猪咪piggy6 小时前
【JavaEE】(23) 综合练习--博客系统
java·数据库·java-ee
周航宇JoeZhou6 小时前
JP4-7-MyLesson后台前端(五)
java·前端·vue·elementplus·前端项目·mylesson·管理平台
David爱编程6 小时前
从 JVM 到内核:synchronized 与操作系统互斥量的深度联系
java·后端
bikong76 小时前
一种高效绘制余晖波形的方法Qt/C++
数据库·c++·qt
渣哥6 小时前
Java Set 不会重复?原来它有“记仇”的本事!
java
一叶飘零_sweeeet6 小时前
从 0 到 1 攻克订单表分表分库:亿级流量下的数据库架构实战指南
java·数据库·mysql·数据库架构·分库分表
苹果醋36 小时前
数据库索引设计:在 MongoDB 中创建高效索引的策略
java·运维·spring boot·mysql·nginx