package com.train.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.train.entity.TrainAuditLog; import com.train.entity.TrainPlan; import com.train.mapper.TrainAuditLogMapper; import com.train.mapper.TrainPlanMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Date; import java.util.List; /** * T+ 跟进定时任务:每日定时扫描已提交 7 天行动计划的学员, * 相对计划提交日(T0)生成 T+1 / T+3 / T+7 提醒记录,落 audit_log(幂等)。 * 骨架版本以落账为主,生产可在此处改为推送订阅消息(复用 SubscribeMessageService)。 */ @Slf4j @Component public class FollowUpTask { @Resource private TrainPlanMapper trainPlanMapper; @Resource private TrainAuditLogMapper trainAuditLogMapper; private static final long DAY_MILLIS = 24L * 60 * 60 * 1000; /** 每日 09:30 执行(课后跟进节奏:T+1 成果合集 / T+3 补齐 / T+7 回访) */ @Scheduled(cron = "0 30 9 * * ?") public void runFollowUp() { List plans = trainPlanMapper.selectList( new LambdaQueryWrapper().orderByAsc(TrainPlan::getSubmittedAt)); Date now = new Date(); for (TrainPlan plan : plans) { if (plan.getUid() == null || plan.getSubmittedAt() == null) { continue; } long elapsedDays = (now.getTime() - plan.getSubmittedAt().getTime()) / DAY_MILLIS; if (elapsedDays >= 1) { ensureLog(plan.getUid(), "followup_t1", "T+1 跟进提醒:课堂成果合集与照片已同步,请查收"); } if (elapsedDays >= 3) { ensureLog(plan.getUid(), "followup_t3", "T+3 跟进提醒:未完成项建议补齐(三本账/自动化)"); } if (elapsedDays >= 7) { ensureLog(plan.getUid(), "followup_t7", "T+7 回访:7 天行动计划完成度自检,欢迎约 1v1"); } } log.info("FollowUpTask 完成一轮 T+ 跟进扫描,共 {} 条计划", plans.size()); } /** 幂等写入 audit_log:同 action + targetId 已存在则跳过 */ private void ensureLog(Long uid, String action, String detail) { Long exists = trainAuditLogMapper.selectCount( new LambdaQueryWrapper() .eq(TrainAuditLog::getAction, action) .eq(TrainAuditLog::getTargetId, uid)); if (exists != null && exists > 0) { return; } TrainAuditLog entry = new TrainAuditLog(); entry.setAction(action); entry.setActorUid(null); entry.setTargetType("user"); entry.setTargetId(uid); entry.setDetail(detail); entry.setTs(new Date()); trainAuditLogMapper.insert(entry); } }