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.SmsSpecialistMapper; import com.whdc.model.entity.SmsBirthday; import com.whdc.model.entity.SmsLog; import com.whdc.model.entity.SmsSpecialist; 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.*; import java.util.concurrent.atomic.AtomicBoolean; /** * 生日短信服务实现类 * * @author lyf * @since 2025-09-23 */ @Service @Slf4j public class SmsBirthdayServiceImpl extends ServiceImpl implements ISmsBirthdayService { @Autowired private SmsSpecialistMapper specialistMapper; @Autowired private SmsLogMapper smsLogMapper; @Autowired private SmsHelper smsHelper; // 记录今日是否已发送生日短信 - 使用原子变量保证线程安全 private final AtomicBoolean sending = new AtomicBoolean(false); @Override public List listBirthdayToday() { try { // 获取当前日期的月和日 LocalDate today = LocalDate.now(); String monthDay = today.format(DateTimeFormatter.ofPattern("MM-dd")); // 查询今天过生日的专家(状态为有效的) LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper .apply("DATE_FORMAT(BIRTHDAY, '%m-%d') = {0}", monthDay) .eq(SmsSpecialist::getFlagBirthdaySentToday, 0) .eq(SmsSpecialist::getStatus, 1); // 1:有效 List specialists = specialistMapper.selectList(queryWrapper); return specialists != null ? specialists : Collections.emptyList(); } catch (Exception e) { // 记录异常并返回空列表 return Collections.emptyList(); } } @Override public void sendBirthdaySms(List 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; // 模板内容为空 } // 逐个发送个性化短信 int totalSpecialists = specialists.size(); log.info("开始向{}位专家发送生日短信", totalSpecialists); //去重 Set distinct = new HashSet<>(); for (int i = 0; i < specialists.size(); i++) { SmsSpecialist specialist = specialists.get(i); if (distinct.contains(specialist.getPhone())) { continue; } String content = template.replace("{姓名}", specialist.getName()); try { log.info("正在发送第{}/{}位专家{}的生日短信", i + 1, totalSpecialists, specialist.getName()); // 创建短信日志记录 SmsLog smsLog = new SmsLog(); smsLog.setName(specialist.getName()) .setPhone(specialist.getPhone()) .setContent(content) .setRemark("生日短信") .setSendTm(new java.util.Date()); // 使用SmsHelper发送个性化短信 List phoneList = Collections.singletonList(specialist.getPhone()); String sendResult = smsHelper.send(phoneList, content); // 更新专家的flagBirthdaySentToday specialist.setFlagBirthdaySentToday(1); specialistMapper.updateById(specialist); distinct.add(specialist.getPhone()); log.info("向专家{}发送生日短信结果: {}", specialist.getName(), sendResult); // 根据发送结果设置备注 if ("发送成功".equals(sendResult)) { smsLog.setRemark("生日短信-发送成功"); } else { smsLog.setRemark("生日短信-发送失败: " + sendResult); } //应唐科要求,每次发送短讯要抄送给唐威,陈锋 carbonCopy(content); // 保存短信日志记录 smsLogMapper.insert(smsLog); // 如果不是最后一位专家,等待3秒再发送下一条 if (i < specialists.size() - 1) { try { log.info("等待3秒后发送下一条短信..."); Thread.sleep(3000); // 3秒间隔 } catch (InterruptedException ie) { Thread.currentThread().interrupt(); log.warn("生日短信发送间隔被中断"); break; } } } catch (Exception e) { log.error("向专家{}发送生日短信时发生异常: {}", specialist.getName(), e.getMessage(), e); // 即使发送失败,也保存短信记录 try { SmsLog failedSmsLog = new SmsLog(); failedSmsLog.setName(specialist.getName()) .setPhone(specialist.getPhone()) .setContent(content) .setRemark("生日短信-发送异常: " + e.getMessage()) .setSendTm(new java.util.Date()); smsLogMapper.insert(failedSmsLog); } catch (Exception logException) { log.error("保存发送失败的短信日志时发生异常: {}", logException.getMessage(), logException); } } } log.info("生日短信批量发送任务完成"); } /** * 抄送 */ private void carbonCopy(String content) { List phoneList = new ArrayList<>(); phoneList.add("18154318312"); //唐威 phoneList.add("13247155309"); //陈锋 phoneList.add("15671545233"); //李 smsHelper.send(phoneList, content); } /** * 每天0点重置所有专家的flagBirthdaySentToday为0 */ @Scheduled(cron = "0 0 0 * * ?") public void resetFlagBirthdaySentToday() { specialistMapper.resetFlagBirthdaySentToday(); } /** * 定时检查并发送生日短信 * 每分钟执行一次 */ @Scheduled(cron = "0 * * * * ?") public void checkAndSendBirthdaySms() { try { // 获取生日短信配置 SmsBirthday smsBirthday = this.getById(1L); if (smsBirthday == null || smsBirthday.getStatus() != 1) { log.info("生日短信功能未启用"); return; } // 检查执行时间 String executionTime = smsBirthday.getExecutionTmStr(); if (executionTime == null || executionTime.trim().isEmpty()) { log.warn("生日短信执行时间未配置"); return; } // 如果已经发送过,不再重复发送 if (sending.get()) { log.info("其他线程正在执行生日短信发送任务"); 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 (sending.compareAndSet(false, true)) { try { log.info("到达生日短信发送时间: {}, 开始执行发送任务", executionTime); // 查询今天过生日的专家 List birthdaySpecialists = listBirthdayToday(); if (birthdaySpecialists.isEmpty()) { log.info("今天没有专家过生日,无需发送生日短信"); } else { log.info("今天有{}位专家过生日,开始发送生日短信", birthdaySpecialists.size()); // 发送生日短信 sendBirthdaySms(birthdaySpecialists); log.info("生日短信发送任务完成"); } } finally { sending.set(false); } } else { log.info("其他线程正在执行生日短信发送任务"); } } else { log.info("当前时间{}未到执行时间{}", 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执行 */ //todo delete this // @Scheduled(cron = "1 0 0 * * ?") // public void resetDailySendStatus() { // try { // sending.set(false); // log.info("生日短信每日发送状态已重置"); // } catch (Exception e) { // log.error("重置生日短信每日发送状态异常: {}", e.getMessage(), e); // } // } }