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 {@Autowiredprivate LoanInMapper loanInMapper;@Autowiredprivate RemindExpireMapper remindExpireMapper;@Autowiredprivate 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 {@Autowiredprivate 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,可以实现自动化到期提醒系统。如果需要完整项目代码或进一步优化,欢迎评论交流!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/96063.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/96063.shtml
英文地址,请注明出处:http://en.pswp.cn/web/96063.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springboot配置请求日志

springboot配置请求日志 一般情况下&#xff0c;接口请求都需要日志记录&#xff0c;Java springboot中的日志记录相对复杂一点 经过实践&#xff0c;以下方案可行&#xff0c;记录一下完整过程 一、创建日志数据模型 创建实体类&#xff0c;也就是日志文件中要记录的数据格式 …

Redis(50) Redis哨兵如何与客户端进行交互?

Redis 哨兵&#xff08;Sentinel&#xff09;不仅负责监控和管理 Redis 主从复制集群的高可用性&#xff0c;还需要与客户端进行有效的交互来实现故障转移后的透明连接切换。下面详细探讨 Redis 哨兵如何与客户端进行交互&#xff0c;并结合代码示例加以说明。 哨兵与客户端的交…

【.Net技术栈梳理】04-核心框架与运行时(线程处理)

文章目录1. 线程管理1.1 线程的核心概念&#xff1a;System.Threading.Thread1.2 现代线程管理&#xff1a;System.Threading.Tasks.Task 和 Task Parallel Library (TPL)1.3 状态管理和异常处理1.4 协调任务&#xff1a;async/await 模式2. 线程间通信2.1 共享内存与竞态条件2…

(JVM)四种垃圾回收算法

在 JVM 中&#xff0c;垃圾回收&#xff08;GC&#xff09;是核心机制之一。为了提升性能与内存利用率&#xff0c;JVM 采用了多种垃圾回收算法。本文总结了 四种常见的 GC 算法&#xff0c;并结合其优缺点与应用场景进行说明。1. 标记-清除&#xff08;Mark-Sweep&#xff09;…

论文阅读:VGGT Visual Geometry Grounded Transformer

论文阅读&#xff1a;VGGT: Visual Geometry Grounded Transformer 今天介绍一篇 CVPR 2025 的 best paper&#xff0c;这篇文章是牛津大学的 VGG 团队的工作&#xff0c;主要围绕着 3D 视觉中的各种任务&#xff0c;这篇文章提出了一种多任务统一的架构&#xff0c;实现一次输…

python编程:一文掌握pypiserver的详细使用

更多内容请见: python3案例和总结-专栏介绍和目录 文章目录 一、 pypiserver 概述 1.1 pypiserver是什么? 1.2 核心特性 1.3 典型应用场景 1.4 pypiserver优缺点 二、 安装与基本使用 2.1 安装 pypiserver 2.2 快速启动(最简模式) 2.3 使用私有服务器安装包 2.4 向私有服务…

Git reset 回退版本

- 第 121 篇 - Date: 2025 - 09 - 06 Author: 郑龙浩&#xff08;仟墨&#xff09; 文章目录Git reset 回退版本1 介绍三种命令区别3 验证三种的区别3 如果不小心git reset --hard将「工作区」和「暂存区」中的内容删除&#xff0c;刚才的记录找不到了&#xff0c;怎么办呢&…

ARM 基础(2)

ARM内核工作模式及其切换条件用户模式(User Mode, usr) 权限最低&#xff0c;运行普通应用程序。只能通过异常被动切换到其他模式。快速中断模式(FIQ Mode, fiq) 处理高速外设中断&#xff0c;专用寄存器减少上下文保存时间&#xff0c;响应周期约4个时钟周期。触发条件为FIQ中…

Flutter 性能优化

Flutter 性能优化是一个系统性的工程&#xff0c;涉及多个层面。 一、性能分析工具&#xff08;Profiling Tools&#xff09; 在开始优化前&#xff0c;必须使用工具定位瓶颈。切忌盲目优化。 1. DevTools 性能视图 DevTools 性能视图 (Performance View) 作用&#xff1a;…

Spring事件监听机制(三)

为了理解EvenListener注解的底层原理&#xff0c;我们可以自己实现一个类似的注解模拟实现。1.定义MyListener注解Target({ElementType.METHOD})Retention(RetentionPolicy.RUNTIME)public interface MyListener {}2.注解使用Componentstatic class SmsService {private static…

基于Springboot + vue3实现的小区物业管理系统

项目描述本系统包含管理员和用户两个角色。管理员角色&#xff1a;用户管理&#xff1a;管理系统中所有用户的信息&#xff0c;包括添加、删除和修改用户。房屋信息管理&#xff1a;管理房屋信息&#xff0c;包括新增、查看、修改和删除房屋信息。车辆信息管理&#xff1a;管理…

交叉熵和KL散度

这个问题之前我也是傻傻分不清&#xff0c;决定整理一下&#xff0c;用更印象深刻的方式让人记住。核心联系&#xff1a;交叉熵 KL 散度 真实分布的熵 交叉熵作为 “绝对” 度量&#xff0c;会综合真实分布的熵&#xff08;固有难度&#xff09;与预测误差&#xff0c;直接体…

HTML 各种事件的使用说明书

HTML 各种事件的使用说明书 1. HTML 事件简介 HTML事件是浏览器或用户在网页上执行的动作或发生的事情。当这些事件发生时&#xff0c;可以通过JavaScript来响应和处理这些事件&#xff0c;从而实现网页的交互功能。事件处理是Web前端开发中实现动态交互的核心机制。 基本概…

Kafka面试精讲 Day 10:事务机制与幂等性保证

【Kafka面试精讲 Day 10】事务机制与幂等性保证 在分布式消息系统中&#xff0c;如何确保消息不丢失、不重复&#xff0c;是系统可靠性的核心挑战。Kafka自0.11版本起引入了幂等性Producer和事务性消息机制&#xff0c;彻底解决了“至少一次”语义下可能产生的重复消息问题&am…

时序数据库简介和安装

一、简介1. 什么是时序数据库&#xff1f;时序数据库是专门用于存储和处理时间序列数据的数据库系统。时间序列数据是指按时间顺序索引的一系列数据点。每个数据点都包含&#xff1a;一个时间戳&#xff1a;记录数据产生的时间。一个或多个指标值&#xff1a;例如温度、湿度、C…

comfyUI 暴露网络restful http接口

https://zhuanlan.zhihu.com/p/686893291 暴露websocket接口。 打开开发者选项 如图

linux系统address already in use问题解决

linux系统上某个端口被占用&#xff0c;如何解决&#xff1f;1.找到占用的进程编号&#xff1a;netstat -tulnp | grep :80002.强制杀死该进程kill -9 80603其他说明&#xff1a;1.查找占用端口的进程&#xff0c;可以用&#xff1a;lsof -i :8001 # 或者使用 netstat -tulnp |…

基于SpringBoot的家政保洁预约系统【计算机毕业设计选题 计算机毕业设计项目 计算机毕业论文题目推荐】

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

【Linux系统】 4. 权限(一)

一. shell 命令及运行原理基本理解1&#xff09;广义理解的操作系统包括&#xff1a;操作系统内核、外壳程序&#xff08;shell命令行、图形化界面&#xff09;、必要的软件。2&#xff09;狭义的操作系统&#xff1a;操作系统内核。3&#xff09;在用户和内核之间有一个外壳程…

6.python——字符串

python中用’ 和" "创建字符串 python的子字符串截取用[]取字符串拼接可以直接用相加。 python三引号允许一个字符串跨多行&#xff0c;其中无需进行转义&#xff08;所见即所得&#xff09;。 当你需要一块HTML或者SQL时&#xff0c;这时用字符串组合&#xff0c;特…