Commit 930509dc by 邓敏

定时任务

1 parent 6dad6971
......@@ -197,6 +197,10 @@
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
......
package com.subsidy.common.configure;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
/**
* <p>
* JobFactory实例
* </p>
*
* @author DengMin
* @since 2020/12/9
*/
@Component
public class JobFactory extends AdaptableJobFactory {
private AutowireCapableBeanFactory factory;
public JobFactory(AutowireCapableBeanFactory factory) {
this.factory = factory;
}
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object job = super.createJobInstance(bundle);
factory.autowireBean(job);
return job;
}
}
package com.subsidy.common.configure;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
/**
* <p>
* Quartz配置
* </p>
*
* @author DengMin
* @since 2020/12/8
*/
@Configuration
public class QuartzConfig {
@Autowired
private JobFactory jobFactory;
@Autowired
private DataSource dataSource;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setStartupDelay(60);
schedulerFactoryBean.setJobFactory(jobFactory);
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setStartupDelay(1);
schedulerFactoryBean.setDataSource(dataSource);
schedulerFactoryBean.setConfigLocation(new ClassPathResource("/application-quartz.properties"));
return schedulerFactoryBean;
}
@Bean
public Scheduler scheduler() {
return schedulerFactoryBean().getScheduler();
}
}
package com.subsidy.common.constant;
public class CourseNotification {
public static final String UNSENT = "待发送";
public static final String SENT = "已发送";
public static final String ALL = "全部成员";
public static final String NOT_SIGNED_IN = "未签到成员";
public static final String UNFINISHED = "未完课成员";
}
......@@ -3,6 +3,7 @@ package com.subsidy.controller;
import com.subsidy.common.ResponseData;
import com.subsidy.common.ResponseVO;
import com.subsidy.dto.classNotice.SendNotificationDTO;
import com.subsidy.model.ClassNoticeDO;
import com.subsidy.service.ClassNoticeService;
import io.swagger.annotations.ApiOperation;
......@@ -23,7 +24,7 @@ import io.swagger.annotations.Api;
* @since 2022-01-21
*/
@RestController
@Api(tags = "")
@Api(tags = "课程通知")
@RequestMapping("/classNotice")
public class ClassNoticeController {
......@@ -31,7 +32,7 @@ public class ClassNoticeController {
private ClassNoticeService classNoticeService;
@PostMapping("addNotice")
@ApiOperation("新增一个通知 classId noticeType isAuto noticeTime")
@ApiOperation("新增一个通知 classId noticeType isAuto noticeTime")
public ResponseVO addNotice(@RequestBody ClassNoticeDO classNoticeDO){
return ResponseData.generateCreatedResponse(0,classNoticeService.addNotice(classNoticeDO));
}
......@@ -49,12 +50,15 @@ public class ClassNoticeController {
}
@PostMapping("queryClassNotices")
@ApiOperation("查看某个课程的通知提醒 classId 课程id")
@ApiOperation("查看某个课程的通知提醒 classId 课程id status 状态(待发送/已发送)")
public ResponseVO queryClassNotices(ClassNoticeDO classNoticeDO){
return ResponseData.generateCreatedResponse(0,classNoticeService.queryClassNotices(classNoticeDO));
}
@PostMapping("sendClassNotices")
@ApiOperation("发送通知:classId/课程Id sendType/发送类型(全部成员/未签到成员/未完课成员)")
public ResponseVO sendClassNotices(@RequestBody SendNotificationDTO sendNotificationDTO) {
classNoticeService.sendNotification(sendNotificationDTO);
return ResponseData.generateCreatedResponse(0);
}
}
package com.subsidy.dto.classNotice;
import lombok.Data;
@Data
public class SendNotificationDTO {
private Long classId;
private String sendType;
}
package com.subsidy.jobs;
import com.subsidy.common.constant.CourseNotification;
import com.subsidy.mapper.ClassNoticeMapper;
import com.subsidy.mapper.MemberMapper;
import com.subsidy.model.ClassNoticeDO;
import com.subsidy.model.MemberDO;
import com.subsidy.util.SMSUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
* <p>
* 课程通知
* </p>
*
* @author DengMin
* @since 2022/2/14
*/
@Component
public class CourseNotificationJob implements Job {
@Autowired
private MemberMapper memberMapper;
@Autowired
private ClassNoticeMapper classNoticeMapper;
@Autowired
private SMSUtils smsUtils;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Map<String, Object> map = jobExecutionContext.getJobDetail().getJobDataMap();
Map<String, Object> params = (Map<String, Object>) map.get("params");
if(params != null) {
List<MemberDO> list = memberMapper.getMemberList((Long) params.get("classId"));
if(list != null && list.size() > 0) {
for (MemberDO memberDO : list) {
if(memberDO != null) {
// smsUtils.send(memberDO.getTelephone(), "");
}
}
ClassNoticeDO classNoticeDO = new ClassNoticeDO();
classNoticeDO.setId((Long) params.get("id"));
classNoticeDO.setStatus(CourseNotification.SENT);
classNoticeMapper.updateById(classNoticeDO);
}
}
}
}
......@@ -31,7 +31,7 @@ import java.util.Set;
*/
@Component
public class Scheduler {
public class SchedulerJob {
@Autowired
private OprAdmDictMapper oprAdmDictMapper;
......
......@@ -55,4 +55,9 @@ public interface MemberMapper extends BaseMapper<MemberDO> {
*/
IPage<ManageMemberVO> manageMember(IPage iPage,Long companyId,String userName);
List<MemberDO> getMemberList(Long classId);
List<MemberDO> getMemberListBySignInRecord(Long classId);
List<MemberDO> getUnfinishedMemberList(Long classId);
}
package com.subsidy.service;
import com.subsidy.dto.classNotice.SendNotificationDTO;
import com.subsidy.model.ClassNoticeDO;
import com.baomidou.mybatisplus.extension.service.IService;
......@@ -22,4 +23,6 @@ public interface ClassNoticeService extends IService<ClassNoticeDO> {
String deleteNotice(ClassNoticeDO classNoticeDO);
List<ClassNoticeDO> queryClassNotices(ClassNoticeDO classNoticeDO);
void sendNotification(SendNotificationDTO sendNotificationDTO);
}
package com.subsidy.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.subsidy.common.constant.CourseNotification;
import com.subsidy.common.exception.HttpException;
import com.subsidy.dto.classNotice.SendNotificationDTO;
import com.subsidy.jobs.CourseNotificationJob;
import com.subsidy.mapper.MemberMapper;
import com.subsidy.model.ClassDictDO;
import com.subsidy.model.ClassNoticeDO;
import com.subsidy.mapper.ClassNoticeMapper;
import com.subsidy.model.MemberDO;
import com.subsidy.service.ClassDictService;
import com.subsidy.service.ClassNoticeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.subsidy.util.ConstantUtils;
import com.subsidy.util.DateFormatUtil;
import com.subsidy.util.QuartzUtil;
import com.subsidy.util.SMSUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
......@@ -21,8 +36,34 @@ import java.util.List;
@Service
public class ClassNoticeServiceImpl extends ServiceImpl<ClassNoticeMapper, ClassNoticeDO> implements ClassNoticeService {
@Autowired
private QuartzUtil quartzUtil;
@Autowired
private ClassDictService classDictService;
@Autowired
private MemberMapper memberMapper;
@Autowired
private SMSUtils smsUtils;
@Transactional(rollbackFor = Exception.class)
@Override
public String addNotice(ClassNoticeDO classNoticeDO) {
if(DateFormatUtil.parse(classNoticeDO.getNoticeTime(),"yyyy-MM-dd").before(new Date())) {
throw new HttpException(70001);
}
classNoticeDO.setStatus(CourseNotification.UNSENT);
this.baseMapper.insert(classNoticeDO);
ClassDictDO classDictDO = classDictService.getById(classNoticeDO.getClassId());
Map<String, Object> params = new HashMap<>();
params.put("classId", classNoticeDO.getClassId());
params.put("id", classNoticeDO.getId());
String name = classDictDO.getClassName()+"-"+classNoticeDO.getNoticeType()+"-"+classNoticeDO.getNoticeTime();
quartzUtil.addSimpleJob(CourseNotificationJob.class,DateFormatUtil.parse(classNoticeDO.getNoticeTime(), "yyyy-MM-dd") , params, name, "CourseNotificationJob");
return ConstantUtils.ADD_SUCCESS;
}
......@@ -31,15 +72,48 @@ public class ClassNoticeServiceImpl extends ServiceImpl<ClassNoticeMapper, Class
return ConstantUtils.SET_SUCCESS;
}
@Transactional(rollbackFor = Exception.class)
public String deleteNotice(ClassNoticeDO classNoticeDO) {
ClassNoticeDO noticeDO = this.baseMapper.selectById(classNoticeDO.getId());
this.baseMapper.deleteById(classNoticeDO.getId());
ClassDictDO classDictDO = classDictService.getById(noticeDO.getClassId());
String name = classDictDO.getClassName()+"-"+noticeDO.getNoticeType()+"-"+noticeDO.getNoticeTime();
quartzUtil.deleteJob(name, "CourseNotificationJob");
return ConstantUtils.DELETE_SUCCESS;
}
public List<ClassNoticeDO> queryClassNotices(ClassNoticeDO classNoticeDO){
return this.baseMapper.selectList(new QueryWrapper<ClassNoticeDO>()
.lambda()
.eq(ClassNoticeDO::getStatus, classNoticeDO.getStatus())
.eq(ClassNoticeDO::getClassId,classNoticeDO.getClassId()));
}
@Override
public void sendNotification(SendNotificationDTO sendNotificationDTO) {
if(sendNotificationDTO.getSendType().equals(CourseNotification.ALL)) {
List<MemberDO> list = memberMapper.getMemberList(sendNotificationDTO.getClassId());
if(list != null) {
for (MemberDO memberDO : list) {
smsUtils.send(memberDO.getTelephone(), "");
}
}
} else if(sendNotificationDTO.getSendType().equals(CourseNotification.NOT_SIGNED_IN)) {
List<MemberDO> list = memberMapper.getMemberListBySignInRecord(sendNotificationDTO.getClassId());
if(list != null) {
for (MemberDO memberDO : list) {
smsUtils.send(memberDO.getTelephone(), "");
}
}
} else if(sendNotificationDTO.getSendType().equals(CourseNotification.UNFINISHED)) {
List<MemberDO> list = memberMapper.getUnfinishedMemberList(sendNotificationDTO.getClassId());
if(list != null) {
for (MemberDO memberDO : list) {
smsUtils.send(memberDO.getTelephone(), "");
}
}
}
}
}
package com.subsidy.util;
import com.subsidy.common.exception.HttpException;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.DateBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static org.quartz.DateBuilder.futureDate;
@Component
public class QuartzUtil {
@Autowired
private Scheduler scheduler;
/**
* 添加定时任务,只执行一次的定时任务
*
* @param cls 执行类
* @param date 时间
* @param params 参数
* @param name 定时器名称
* @param group 定时器组名
*/
public void addSimpleJob(Class cls, Date date, Map<String, Object> params, String name, String group) {
try {
JobKey key = new JobKey(name, group);
List<Trigger> triggers = (List<Trigger>) scheduler.getTriggersOfJob(key);
if (triggers.size() == 0) {
int time = (int) (date.getTime() - System.currentTimeMillis()) / 1000;
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("params", params);
JobDetail jobDetail = JobBuilder.newJob(cls)
.withIdentity(name, group)
.usingJobData(jobDataMap)
.build();
SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
.withIdentity(name, group)
.startAt(futureDate(time, DateBuilder.IntervalUnit.SECOND))
.build();
scheduler.scheduleJob(jobDetail, trigger);
if (!scheduler.isShutdown()) {
scheduler.start();
}
}
} catch (Exception e) {
throw new HttpException(70002);
}
}
/**
* 添加定时任务,循环不断执行的定时任务
*
* @param cls 执行类
* @param cron cron 表达式
* @param params 参数
* @param name 定时器名称
* @param group 定时器组名
*/
public void addCronJob(Class cls, String cron, Map<String, Object> params, String name, String group) {
try {
JobKey key = new JobKey(name, group);
List<Trigger> triggers = (List<Trigger>) scheduler.getTriggersOfJob(key);
if (triggers.size() == 0) {
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("params", params);
JobDetail jobDetail = JobBuilder.newJob(cls)
.withIdentity(name, group)
.usingJobData(jobDataMap)
.build();
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(name, group)
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
scheduler.scheduleJob(jobDetail, trigger);
if (!scheduler.isShutdown()) {
scheduler.start();
}
}
} catch (Exception e) {
throw new HttpException(70002);
}
}
/**
* 删除定时器
*
* @param name 定时器名称
* @param group 定时器组名
*/
public void deleteJob(String name, String group) {
try {
JobKey key = new JobKey(name, group);
List<Trigger> triggers = (List<Trigger>) scheduler.getTriggersOfJob(key);
if (triggers.size() > 0) {
TriggerKey triggerKey = TriggerKey.triggerKey(name, group);
scheduler.pauseTrigger(triggerKey);
scheduler.unscheduleJob(triggerKey);
scheduler.deleteJob(JobKey.jobKey(name, group));
}
} catch (Exception e) {
throw new HttpException(70003);
}
}
}
\ No newline at end of file
spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
spring.quartz.properties.org.quartz.jobStore.tablePrefix=QRTZ_
spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
spring.quartz.properties.org.quartz.threadPool.threadCount=15
spring.quartz.properties.org.quartz.threadPool.threadPriority=5
spring.quartz.properties.org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
\ No newline at end of file
......@@ -44,4 +44,8 @@ meishu.code-message[10001]=导入失败
meishu.code-message[11001]=该行业已存在
meishu.code-message[70001]=添加通知失败,通知时间不能小于今天时间
meishu.code-message[70002]=定时任务创建失败
meishu.code-message[70003]=定时任务删除失败
meishu.code-message[12001]=该职级已存在
......@@ -206,4 +206,60 @@
</if>
</select>
<select id="getMemberList" resultType="com.subsidy.model.MemberDO">
select
m.*
from member m
left join class_member_mapping cmm on cmm.member_id = m.id
where cmm.class_id = #{classId}
and m.delete_date is null
and cmm.delete_date is null
</select>
<select id="getMemberListBySignInRecord" resultType="com.subsidy.model.MemberDO">
SELECT
m.*
FROM
member m
LEFT JOIN class_member_mapping cmm ON cmm.member_id = m.id
LEFT JOIN ( SELECT member_id, count( id ) AS signInCount FROM sign_in_record WHERE class_id = #{classId} AND delete_date IS NULL GROUP BY member_id ) s ON s.member_id = m.id
WHERE
cmm.class_id = #{classId}
AND m.delete_date IS NULL
AND cmm.delete_date IS NULL
AND s.signInCount > 0
GROUP BY
m.id
</select>
<select id="getUnfinishedMemberList" resultType="com.subsidy.model.MemberDO">
SELECT
m.*
FROM
member m
LEFT JOIN class_member_mapping cmm ON cmm.member_id = m.id
LEFT JOIN (
SELECT
vph.member_id,
vph.class_id,
vd.vod_length,
IF
( sum( vph.play_length ) >= vd.vod_length, 1, 0 ) AS ps
FROM
vod_play_history vph
LEFT JOIN vod_dict vd ON vd.id = vph.vod_id
WHERE
vph.delete_date IS NULL
AND vd.delete_date IS NULL
GROUP BY
vod_id,
member_id
) h ON h.member_id = m.id
AND h.class_id = cmm.class_id
WHERE
cmm.class_id = #{classId}
AND h.ps = 0
GROUP BY
m.id
</select>
</mapper>
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!