feat(sms-birthday): 完善生日短信功能实现
- 添加今天过生日专家查询方法 - 实现生日短信发送功能,支持个性化模板 - 添加定时任务:每分钟检查并发送生日短信 - 添加每日状态重置定时任务 - 优化代码注释和异常处理 - 修正专家实体描述字段 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>master
parent
8bc1fe4e94
commit
9f1baf17d2
|
|
@ -36,6 +36,7 @@ public class SmsBirthday implements Serializable {
|
|||
|
||||
/**
|
||||
* 任务执行时间
|
||||
* 例:08:00:00
|
||||
*/
|
||||
@TableField("EXECUTION_TM_STR")
|
||||
@ApiModelProperty(value = "任务执行时间")
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import java.util.Date;
|
|||
@Data
|
||||
@EqualsAndHashCode
|
||||
@Accessors(chain = true)
|
||||
@ApiModel(description = "专家通讯录")
|
||||
@ApiModel(description = "专家")
|
||||
@TableName("SPECIALIST")
|
||||
public class Specialist implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ package com.whdc.service;
|
|||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.whdc.model.entity.SmsBirthday;
|
||||
import com.whdc.model.entity.Specialist;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生日短信服务接口
|
||||
|
|
@ -10,4 +13,13 @@ import com.whdc.model.entity.SmsBirthday;
|
|||
* @since 2025-09-23
|
||||
*/
|
||||
public interface ISmsBirthdayService extends IService<SmsBirthday> {
|
||||
/**
|
||||
* 查询今天过生日专家
|
||||
*/
|
||||
List<Specialist> listBirthdayToday();
|
||||
|
||||
/**
|
||||
* 发送生日短信
|
||||
*/
|
||||
void sendBirthdaySms(List<Specialist> specialists);
|
||||
}
|
||||
|
|
@ -1,11 +1,28 @@
|
|||
package com.whdc.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.whdc.mapper.SmsBirthdayMapper;
|
||||
import com.whdc.mapper.SmsLogMapper;
|
||||
import com.whdc.mapper.SpecialistMapper;
|
||||
import com.whdc.model.entity.SmsBirthday;
|
||||
import com.whdc.model.entity.SmsLog;
|
||||
import com.whdc.model.entity.Specialist;
|
||||
import com.whdc.service.ISmsBirthdayService;
|
||||
import com.whdc.utils.SmsHelper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* 生日短信服务实现类
|
||||
*
|
||||
|
|
@ -13,5 +30,197 @@ import org.springframework.stereotype.Service;
|
|||
* @since 2025-09-23
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class SmsBirthdayServiceImpl extends ServiceImpl<SmsBirthdayMapper, SmsBirthday> implements ISmsBirthdayService {
|
||||
|
||||
@Autowired
|
||||
private SpecialistMapper specialistMapper;
|
||||
|
||||
@Autowired
|
||||
private SmsLogMapper smsLogMapper;
|
||||
|
||||
@Autowired
|
||||
private SmsHelper smsHelper;
|
||||
|
||||
// 记录今日是否已发送生日短信 - 使用原子变量保证线程安全
|
||||
private final AtomicBoolean birthdaySmsSentToday = new AtomicBoolean(false);
|
||||
|
||||
@Override
|
||||
public List<Specialist> listBirthdayToday() {
|
||||
try {
|
||||
// 获取当前日期的月和日
|
||||
LocalDate today = LocalDate.now();
|
||||
String monthDay = today.format(DateTimeFormatter.ofPattern("MM-dd"));
|
||||
|
||||
// 查询今天过生日的专家(状态为有效的)
|
||||
LambdaQueryWrapper<Specialist> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper
|
||||
.apply("DATE_FORMAT(BIRTHDAY, '%m-%d') = {0}", monthDay)
|
||||
.eq(Specialist::getStatus, 1); // 1:有效
|
||||
|
||||
List<Specialist> specialists = specialistMapper.selectList(queryWrapper);
|
||||
return specialists != null ? specialists : Collections.emptyList();
|
||||
} catch (Exception e) {
|
||||
// 记录异常并返回空列表
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBirthdaySms(List<Specialist> specialists) {
|
||||
if (specialists == null || specialists.isEmpty()) {
|
||||
log.info("没有需要发送生日短信的专家");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取生日短信模板
|
||||
SmsBirthday smsBirthday = this.getById(1L); // 使用ID=1的记录
|
||||
if (smsBirthday == null || smsBirthday.getStatus() != 1) {
|
||||
log.warn("生日短信模板不存在或未启用");
|
||||
return; // 模板不存在或未启用
|
||||
}
|
||||
|
||||
String template = smsBirthday.getTemplate();
|
||||
if (template == null || template.trim().isEmpty()) {
|
||||
log.warn("生日短信模板内容为空");
|
||||
return; // 模板内容为空
|
||||
}
|
||||
|
||||
// 逐个发送个性化短信
|
||||
for (Specialist specialist : specialists) {
|
||||
try {
|
||||
// 替换模板中的占位符
|
||||
String content = template.replace("{姓名}", specialist.getName())
|
||||
.replace("{称呼}", specialist.getTitle() != null ? specialist.getTitle() : "");
|
||||
|
||||
// 创建短信日志记录
|
||||
SmsLog smsLog = new SmsLog();
|
||||
smsLog.setName(specialist.getName())
|
||||
.setPhone(specialist.getPhone())
|
||||
.setContent(content)
|
||||
.setRemark("生日短信")
|
||||
.setSendTm(new java.util.Date());
|
||||
|
||||
// 使用SmsHelper发送个性化短信
|
||||
List<String> phoneList = Collections.singletonList(specialist.getPhone());
|
||||
String sendResult = smsHelper.send(phoneList, content);
|
||||
log.info("向专家{}发送生日短信结果: {}", specialist.getName(), sendResult);
|
||||
|
||||
// 根据发送结果设置备注
|
||||
if ("发送成功".equals(sendResult)) {
|
||||
smsLog.setRemark("生日短信-发送成功");
|
||||
} else {
|
||||
smsLog.setRemark("生日短信-发送失败: " + sendResult);
|
||||
}
|
||||
|
||||
// 保存短信日志记录
|
||||
smsLogMapper.insert(smsLog);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("向专家{}发送生日短信时发生异常: {}", specialist.getName(), e.getMessage(), e);
|
||||
|
||||
// 即使发送失败,也保存短信记录
|
||||
try {
|
||||
SmsLog failedSmsLog = new SmsLog();
|
||||
failedSmsLog.setName(specialist.getName())
|
||||
.setPhone(specialist.getPhone())
|
||||
.setContent(template.replace("{name}", specialist.getName())
|
||||
.replace("{title}", specialist.getTitle() != null ? specialist.getTitle() : ""))
|
||||
.setRemark("生日短信-发送异常: " + e.getMessage())
|
||||
.setSendTm(new java.util.Date());
|
||||
smsLogMapper.insert(failedSmsLog);
|
||||
} catch (Exception logException) {
|
||||
log.error("保存发送失败的短信日志时发生异常: {}", logException.getMessage(), logException);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时检查并发送生日短信
|
||||
* 每分钟执行一次
|
||||
*/
|
||||
@Scheduled(cron = "0 * * * * ?")
|
||||
public void checkAndSendBirthdaySms() {
|
||||
try {
|
||||
// 如果已经发送过,不再重复发送
|
||||
if (birthdaySmsSentToday.get()) {
|
||||
log.debug("今日已经发送过生日短信,跳过执行");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取生日短信配置
|
||||
SmsBirthday smsBirthday = this.getById(1L);
|
||||
if (smsBirthday == null || smsBirthday.getStatus() != 1) {
|
||||
log.debug("生日短信功能未启用");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查执行时间
|
||||
String executionTime = smsBirthday.getExecutionTmStr();
|
||||
if (executionTime == null || executionTime.trim().isEmpty()) {
|
||||
log.warn("生日短信执行时间未配置");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前时间
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
try {
|
||||
// 将执行时间字符串与当前日期拼接
|
||||
String todayWithExecutionTime = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " " + executionTime;
|
||||
|
||||
// 解析为完整的LocalDateTime
|
||||
LocalDateTime scheduledTime = LocalDateTime.parse(
|
||||
todayWithExecutionTime,
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||
);
|
||||
|
||||
// 检查当前时间是否大于等于执行时间
|
||||
if (now.isEqual(scheduledTime) || now.isAfter(scheduledTime)) {
|
||||
// 原子性检查和设置发送状态
|
||||
if (birthdaySmsSentToday.compareAndSet(false, true)) {
|
||||
log.info("到达生日短信发送时间: {}, 开始执行发送任务", executionTime);
|
||||
|
||||
// 查询今天过生日的专家
|
||||
List<Specialist> birthdaySpecialists = listBirthdayToday();
|
||||
|
||||
if (birthdaySpecialists.isEmpty()) {
|
||||
log.info("今天没有专家过生日,无需发送生日短信");
|
||||
} else {
|
||||
log.info("今天有{}位专家过生日,开始发送生日短信", birthdaySpecialists.size());
|
||||
|
||||
// 发送生日短信
|
||||
sendBirthdaySms(birthdaySpecialists);
|
||||
log.info("生日短信发送任务完成");
|
||||
}
|
||||
} else {
|
||||
log.debug("其他线程正在执行生日短信发送任务");
|
||||
}
|
||||
} else {
|
||||
log.debug("当前时间{}未到执行时间{}", now.format(DateTimeFormatter.ofPattern("HH:mm:ss")), executionTime);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("执行时间格式解析失败: {}", executionTime, e);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("定时检查生日短信任务执行异常: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置每日发送状态
|
||||
* 每天凌晨00:00:01执行
|
||||
*/
|
||||
@Scheduled(cron = "1 0 0 * * ?")
|
||||
public void resetDailySendStatus() {
|
||||
try {
|
||||
birthdaySmsSentToday.set(false);
|
||||
log.info("生日短信每日发送状态已重置");
|
||||
} catch (Exception e) {
|
||||
log.error("重置生日短信每日发送状态异常: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue