feat(sms):优化生日短信发送逻辑并添加去重机制

- 修改查询条件,增加对专家生日短信已发送标记的过滤
- 实现发送前手机号去重逻辑,避免重复发送
- 添加发送成功后更新专家短信发送标记的逻辑
- 实现定时任务每天重置专家短信发送标记- 添加定时任务,每天凌晨重置短信发送状态
- 增加发送短信抄送功能,抄送指定人员
- 优化线程安全控制,防止重复执行短信发送任务
- 更新 Specialist 实体类,增加短信发送标记字段
- 修改 SpecialistMapper,添加重置短信发送标记的方法- 更新数据库表结构,添加短信发送标记字段
master
李一帆 2025-09-26 10:55:25 +08:00
parent adecc084dd
commit c32e27b174
4 changed files with 82 additions and 35 deletions

View File

@ -9,7 +9,9 @@ CREATE TABLE SPECIALIST (
ADDRESS VARCHAR(20) COMMENT '区域', ADDRESS VARCHAR(20) COMMENT '区域',
PHONE VARCHAR(20) UNIQUE COMMENT '电话号码', PHONE VARCHAR(20) UNIQUE COMMENT '电话号码',
STATUS INT DEFAULT 1 COMMENT '生效状态 1:有效 0:无效', STATUS INT DEFAULT 1 COMMENT '生效状态 1:有效 0:无效',
CREATE_TM DATETIME DEFAULT CURRENT_TIME COMMENT '创建日期' CREATE_TM DATETIME DEFAULT CURRENT_TIME COMMENT '创建日期',
FLAG_BIRTHDAY_SENT_TODAY INT DEFAULT 0
FLAG_HOLIDAY_SENT_TODAY INT DEFAULT 0
); );

View File

@ -2,6 +2,7 @@ package com.whdc.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.whdc.model.entity.Specialist; import com.whdc.model.entity.Specialist;
import org.apache.ibatis.annotations.Update;
/** /**
* Mapper * Mapper
@ -10,4 +11,6 @@ import com.whdc.model.entity.Specialist;
* @since 2025-09-23 * @since 2025-09-23
*/ */
public interface SpecialistMapper extends BaseMapper<Specialist> { public interface SpecialistMapper extends BaseMapper<Specialist> {
@Update("update specialist set flag_birthday_sent_today = 0")
void resetFlagBirthdaySentToday();
} }

View File

@ -83,4 +83,12 @@ public class Specialist implements Serializable {
@ApiModelProperty(value = "创建日期") @ApiModelProperty(value = "创建日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTm; private Date createTm;
//标记今天已经发送过生日短信的flag
@TableField("FLAG_BIRTHDAY_SENT_TODAY")
private Integer flagBirthdaySentToday;
//标记今天已经发送过节日短信的flag
@TableField("FLAG_HOLIDAY_SENT_TODAY")
private Integer flagHolidaySentToday;
} }

View File

@ -18,8 +18,7 @@ import org.springframework.stereotype.Service;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Collections; import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
/** /**
@ -42,7 +41,7 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
private SmsHelper smsHelper; private SmsHelper smsHelper;
// 记录今日是否已发送生日短信 - 使用原子变量保证线程安全 // 记录今日是否已发送生日短信 - 使用原子变量保证线程安全
private final AtomicBoolean birthdaySmsSentToday = new AtomicBoolean(false); private final AtomicBoolean sending = new AtomicBoolean(false);
@Override @Override
public List<Specialist> listBirthdayToday() { public List<Specialist> listBirthdayToday() {
@ -54,8 +53,9 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
// 查询今天过生日的专家(状态为有效的) // 查询今天过生日的专家(状态为有效的)
LambdaQueryWrapper<Specialist> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<Specialist> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper queryWrapper
.apply("DATE_FORMAT(BIRTHDAY, '%m-%d') = {0}", monthDay) .apply("DATE_FORMAT(BIRTHDAY, '%m-%d') = {0}", monthDay)
.eq(Specialist::getStatus, 1); // 1:有效 .eq(Specialist::getFlagBirthdaySentToday, 0)
.eq(Specialist::getStatus, 1); // 1:有效
List<Specialist> specialists = specialistMapper.selectList(queryWrapper); List<Specialist> specialists = specialistMapper.selectList(queryWrapper);
return specialists != null ? specialists : Collections.emptyList(); return specialists != null ? specialists : Collections.emptyList();
@ -89,8 +89,14 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
int totalSpecialists = specialists.size(); int totalSpecialists = specialists.size();
log.info("开始向{}位专家发送生日短信", totalSpecialists); log.info("开始向{}位专家发送生日短信", totalSpecialists);
//去重
Set<String> distinct = new HashSet<>();
for (int i = 0; i < specialists.size(); i++) { for (int i = 0; i < specialists.size(); i++) {
Specialist specialist = specialists.get(i); Specialist specialist = specialists.get(i);
if (distinct.contains(specialist.getPhone())) {
continue;
}
String content = template.replace("{姓名}", specialist.getName()); String content = template.replace("{姓名}", specialist.getName());
try { try {
log.info("正在发送第{}/{}位专家{}的生日短信", i + 1, totalSpecialists, specialist.getName()); log.info("正在发送第{}/{}位专家{}的生日短信", i + 1, totalSpecialists, specialist.getName());
@ -98,14 +104,18 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
// 创建短信日志记录 // 创建短信日志记录
SmsLog smsLog = new SmsLog(); SmsLog smsLog = new SmsLog();
smsLog.setName(specialist.getName()) smsLog.setName(specialist.getName())
.setPhone(specialist.getPhone()) .setPhone(specialist.getPhone())
.setContent(content) .setContent(content)
.setRemark("生日短信") .setRemark("生日短信")
.setSendTm(new java.util.Date()); .setSendTm(new java.util.Date());
// 使用SmsHelper发送个性化短信 // 使用SmsHelper发送个性化短信
List<String> phoneList = Collections.singletonList(specialist.getPhone()); List<String> phoneList = Collections.singletonList(specialist.getPhone());
String sendResult = smsHelper.send(phoneList, content); String sendResult = smsHelper.send(phoneList, content);
// 更新专家的flagBirthdaySentToday
specialist.setFlagBirthdaySentToday(1);
specialistMapper.updateById(specialist);
distinct.add(specialist.getPhone());
log.info("向专家{}发送生日短信结果: {}", specialist.getName(), sendResult); log.info("向专家{}发送生日短信结果: {}", specialist.getName(), sendResult);
// 根据发送结果设置备注 // 根据发送结果设置备注
@ -114,7 +124,8 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
} else { } else {
smsLog.setRemark("生日短信-发送失败: " + sendResult); smsLog.setRemark("生日短信-发送失败: " + sendResult);
} }
//应唐科要求,每次发送短讯要抄送给唐威,陈锋
carbonCopy(content);
// 保存短信日志记录 // 保存短信日志记录
smsLogMapper.insert(smsLog); smsLogMapper.insert(smsLog);
@ -137,10 +148,10 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
try { try {
SmsLog failedSmsLog = new SmsLog(); SmsLog failedSmsLog = new SmsLog();
failedSmsLog.setName(specialist.getName()) failedSmsLog.setName(specialist.getName())
.setPhone(specialist.getPhone()) .setPhone(specialist.getPhone())
.setContent(content) .setContent(content)
.setRemark("生日短信-发送异常: " + e.getMessage()) .setRemark("生日短信-发送异常: " + e.getMessage())
.setSendTm(new java.util.Date()); .setSendTm(new java.util.Date());
smsLogMapper.insert(failedSmsLog); smsLogMapper.insert(failedSmsLog);
} catch (Exception logException) { } catch (Exception logException) {
log.error("保存发送失败的短信日志时发生异常: {}", logException.getMessage(), logException); log.error("保存发送失败的短信日志时发生异常: {}", logException.getMessage(), logException);
@ -151,6 +162,24 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
log.info("生日短信批量发送任务完成"); log.info("生日短信批量发送任务完成");
} }
/**
*
*/
private void carbonCopy(String content) {
List<String> phoneList = new ArrayList<>();
phoneList.add("18154318312"); //唐威
phoneList.add("13247155309"); //陈锋
phoneList.add("15671545233"); //李
smsHelper.send(phoneList, content);
}
/**
* 0flagBirthdaySentToday0
*/
@Scheduled(cron = "0 0 0 * * ?")
public void resetFlagBirthdaySentToday() {
specialistMapper.resetFlagBirthdaySentToday();
}
/** /**
* *
* *
@ -158,12 +187,6 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
@Scheduled(cron = "0 * * * * ?") @Scheduled(cron = "0 * * * * ?")
public void checkAndSendBirthdaySms() { public void checkAndSendBirthdaySms() {
try { try {
// 如果已经发送过,不再重复发送
if (birthdaySmsSentToday.get()) {
log.debug("今日已经发送过生日短信,跳过执行");
return;
}
// 获取生日短信配置 // 获取生日短信配置
SmsBirthday smsBirthday = this.getById(1L); SmsBirthday smsBirthday = this.getById(1L);
if (smsBirthday == null || smsBirthday.getStatus() != 1) { if (smsBirthday == null || smsBirthday.getStatus() != 1) {
@ -178,6 +201,12 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
return; return;
} }
// 如果已经发送过,不再重复发送
if (sending.get()) {
log.debug("其他线程正在执行生日短信发送任务");
return;
}
// 获取当前时间 // 获取当前时间
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
@ -187,14 +216,14 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
// 解析为完整的LocalDateTime // 解析为完整的LocalDateTime
LocalDateTime scheduledTime = LocalDateTime.parse( LocalDateTime scheduledTime = LocalDateTime.parse(
todayWithExecutionTime, todayWithExecutionTime,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
); );
// 检查当前时间是否大于等于执行时间 // 检查当前时间是否大于等于执行时间
if (now.isEqual(scheduledTime) || now.isAfter(scheduledTime)) { if (now.isEqual(scheduledTime) || now.isAfter(scheduledTime)) {
// 原子性检查和设置发送状态 // 原子性检查和设置发送状态
if (birthdaySmsSentToday.compareAndSet(false, true)) { if (sending.compareAndSet(false, true)) {
log.info("到达生日短信发送时间: {}, 开始执行发送任务", executionTime); log.info("到达生日短信发送时间: {}, 开始执行发送任务", executionTime);
// 查询今天过生日的专家 // 查询今天过生日的专家
@ -205,8 +234,12 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
} else { } else {
log.info("今天有{}位专家过生日,开始发送生日短信", birthdaySpecialists.size()); log.info("今天有{}位专家过生日,开始发送生日短信", birthdaySpecialists.size());
// 发送生日短信 try {
sendBirthdaySms(birthdaySpecialists); // 发送生日短信
sendBirthdaySms(birthdaySpecialists);
} finally {
sending.set(false);
}
log.info("生日短信发送任务完成"); log.info("生日短信发送任务完成");
} }
} else { } else {
@ -229,13 +262,14 @@ public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBi
* *
* 00:00:01 * 00:00:01
*/ */
@Scheduled(cron = "1 0 0 * * ?") //todo delete this
public void resetDailySendStatus() { // @Scheduled(cron = "1 0 0 * * ?")
try { // public void resetDailySendStatus() {
birthdaySmsSentToday.set(false); // try {
log.info("生日短信每日发送状态已重置"); // sending.set(false);
} catch (Exception e) { // log.info("生日短信每日发送状态已重置");
log.error("重置生日短信每日发送状态异常: {}", e.getMessage(), e); // } catch (Exception e) {
} // log.error("重置生日短信每日发送状态异常: {}", e.getMessage(), e);
} // }
// }
} }