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,可以實現自動化到期提醒系統。如果需要完整項目代碼或進一步優化,歡迎評論交流!