feat: 缺少更新定时任务接口

This commit is contained in:
Bunny 2024-12-19 20:43:30 +08:00
parent 2c6a6e9b4b
commit 841f48e291
16 changed files with 200 additions and 96 deletions

View File

@ -55,6 +55,13 @@ public class GlobalExceptionHandler {
return Result.error(null, 500, "[" + primaryKeyErrorMatcher.group(1) + "]已存在");
}
// corn表达式错误
String cronExpression = "CronExpression '(.*?)' is invalid";
Matcher cronExpressionMatcher = Pattern.compile(cronExpression).matcher(message);
if (cronExpressionMatcher.find()) {
return Result.error(null, 500, "表达式 " + cronExpressionMatcher.group(1) + " 不合法");
}
log.error("GlobalExceptionHandler===>运行时异常信息:{}", message);
exception.printStackTrace();
return Result.error(null, 500, "服务器异常");

View File

@ -4,14 +4,38 @@ import cn.bunny.dao.pojo.common.EmailSend;
import cn.bunny.dao.pojo.common.EmailSendInit;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import org.jetbrains.annotations.NotNull;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.web.multipart.MultipartFile;
import java.util.Objects;
import java.util.Properties;
public class MailSenderUtil {
/**
* 如果启用SSL需要配置以下
*
* @param emailSendInit 邮件发送初始化
*/
private static @NotNull Properties getProperties(EmailSendInit emailSendInit) {
Properties properties = new Properties();
// 开启认证
properties.setProperty("mail.smtp.auth", "true");
// 启用调试
properties.setProperty("mail.debug", "false");
// 设置链接超时
properties.setProperty("mail.smtp.timeout", "200000");
// 设置端口
properties.setProperty("mail.smtp.port", Integer.toString(25));
// 设置ssl端口
properties.setProperty("mail.smtp.socketFactory.port", Integer.toString(emailSendInit.getPort()));
properties.setProperty("mail.smtp.socketFactory.fallback", "false");
properties.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
return properties;
}
/**
* * 邮件发送初始化
*
@ -26,6 +50,12 @@ public class MailSenderUtil {
javaMailSender.setProtocol(emailSendInit.getProtocol());
javaMailSender.setDefaultEncoding("UTF-8");
// 如果开启SSL
if (emailSendInit.getOpenSSL()) {
Properties properties = getProperties(emailSendInit);
javaMailSender.setJavaMailProperties(properties);
}
return javaMailSender;
}

View File

@ -88,7 +88,7 @@ public class MinioUtil {
return minioFile;
} catch (Exception exception) {
exception.printStackTrace();
throw new AuthCustomerException(ResultCodeEnum.UPDATE_ERROR);
throw new AuthCustomerException(ResultCodeEnum.UPLOAD_ERROR);
}
}
@ -136,7 +136,7 @@ public class MinioUtil {
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(filename).stream(inputStream, size, -1).build());
} catch (Exception exception) {
log.error("上传文件失败:{}", (Object) exception.getStackTrace());
throw new AuthCustomerException(ResultCodeEnum.UPDATE_ERROR);
throw new AuthCustomerException(ResultCodeEnum.UPLOAD_ERROR);
}
}

View File

@ -2,7 +2,6 @@ package cn.bunny.dao.dto.quartz.schedule;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -17,32 +16,19 @@ public class SchedulersUpdateDto {
@Schema(name = "jobName", title = "任务名称")
@NotBlank(message = "任务名称不能为空")
@NotNull(message = "任务名称不能为空")
private String jobName;
@Schema(name = "jobGroup", title = "任务分组")
@NotBlank(message = "任务分组不能为空")
@NotNull(message = "任务分组不能为空")
private String jobGroup;
@Schema(name = "description", title = "任务详情")
@NotBlank(message = "任务详情不能为空")
@NotNull(message = "任务详情不能为空")
private String description;
@Schema(name = "jobClassName", title = "任务类名称")
@NotBlank(message = "corn表达式不能为空")
@NotNull(message = "corn表达式不能为空")
private String jobClassName;
@Schema(name = "cronExpression", title = "corn表达式")
@NotBlank(message = "corn表达式不能为空")
@NotNull(message = "corn表达式不能为空")
private String cronExpression;
@Schema(name = "jobMethodName", title = "执行方法")
@NotBlank(message = "执行方法不能为空")
@NotNull(message = "执行方法不能为空")
private String jobMethodName;
}

View File

@ -40,4 +40,7 @@ public class EmailSendInit {
@NotNull(message = "密码不能为空")
private String password;
@Schema(name = "openSSL", description = "启用SSL")
private Boolean openSSL;
}

View File

@ -60,6 +60,7 @@ public enum ResultCodeEnum {
THE_SAME_USER_HAS_LOGGED_IN(209, "相同用户已登录"),
// 提示错误
UPDATE_ERROR(216, "修改失败"),
URL_ENCODE_ERROR(216, "URL编码失败"),
ILLEGAL_CALLBACK_REQUEST_ERROR(217, "非法回调请求"),
FETCH_USERINFO_ERROR(219, "获取用户信息失败"),
@ -77,7 +78,7 @@ public enum ResultCodeEnum {
// 系统错误 500
UNKNOWN_EXCEPTION(500, "服务异常"),
SERVICE_ERROR(500, "服务异常"),
UPDATE_ERROR(500, "上传文件失败"),
UPLOAD_ERROR(500, "上传失败"),
FAIL(500, "失败"),
;

View File

@ -3,6 +3,7 @@ package cn.bunny.services.controller.schedule;
import cn.bunny.dao.dto.quartz.SchedulersOperationDto;
import cn.bunny.dao.dto.quartz.schedule.SchedulersAddDto;
import cn.bunny.dao.dto.quartz.schedule.SchedulersDto;
import cn.bunny.dao.dto.quartz.schedule.SchedulersUpdateDto;
import cn.bunny.dao.entity.quartz.Schedulers;
import cn.bunny.dao.pojo.result.PageResult;
import cn.bunny.dao.pojo.result.Result;
@ -64,6 +65,13 @@ public class SchedulersController {
return Mono.just(Result.success(ResultCodeEnum.ADD_SUCCESS));
}
@Operation(summary = "更新任务", description = "更新任务")
@PutMapping("updateSchedulers")
public Result<String> updateSchedulers(@Valid @RequestBody SchedulersUpdateDto dto) {
schedulersService.updateSchedulers(dto);
return Result.success(ResultCodeEnum.UPDATE_SUCCESS);
}
@Operation(summary = "暂停任务", description = "暂停任务")
@PutMapping("/pauseSchedulers")
public Result<String> pause(@RequestBody SchedulersOperationDto dto) {

View File

@ -1,19 +1,24 @@
package cn.bunny.services.factory;
import cn.bunny.common.service.exception.AuthCustomerException;
import cn.bunny.dao.dto.financial.bill.BillDto;
import cn.bunny.dao.dto.financial.bill.IncomeExpenseQueryDto;
import cn.bunny.dao.dto.financial.bill.excel.BillExportDto;
import cn.bunny.dao.entity.system.AdminUser;
import cn.bunny.dao.entity.system.EmailTemplate;
import cn.bunny.dao.excel.BillExportExcelByUser;
import cn.bunny.dao.pojo.constant.LocalDateTimeConstant;
import cn.bunny.dao.pojo.enums.EmailTemplateEnums;
import cn.bunny.dao.pojo.result.ResultCodeEnum;
import cn.bunny.dao.vo.financial.admin.BillVo;
import cn.bunny.dao.vo.financial.user.expendAndIncome.ExpendWithIncome;
import cn.bunny.services.mapper.email.EmailTemplateMapper;
import cn.bunny.services.mapper.financial.BillMapper;
import cn.bunny.services.mapper.system.UserMapper;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@ -42,6 +47,12 @@ public class BillFactory {
@Autowired
private EmailFactory emailFactory;
@Autowired
private EmailTemplateMapper emailTemplateMapper;
@Autowired
private UserMapper userMapper;
public void exportBill(BillExportDto dto, HttpServletResponse response) {
LocalDate startDate = dto.getStartDate();
LocalDate endDate = dto.getEndDate();
@ -106,6 +117,39 @@ public class BillFactory {
}
}
/**
* 账单报告
*
* @param startOfDay 开始日期
* @param endOfDay 结束日期
*/
public void billReport(LocalDate startOfDay, LocalDate endOfDay) {
// 查询验证码邮件模板
LambdaQueryWrapper<EmailTemplate> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(EmailTemplate::getIsDefault, true);
lambdaQueryWrapper.eq(EmailTemplate::getType, EmailTemplateEnums.BILL_WEEK_NOTIFICATION.getType());
EmailTemplate emailTemplate = emailTemplateMapper.selectOne(lambdaQueryWrapper);
// 查询需要发送邮件的用户
LambdaQueryWrapper<AdminUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AdminUser::getWeekBillReport, true)
.eq(AdminUser::getStatus, false)// 没有被禁用的
.isNotNull(AdminUser::getEmail);// 有邮箱的
List<AdminUser> userList = userMapper.selectList(queryWrapper);
userList.forEach(user -> {
// 查询当前用户账单数据
BillDto billDto = new BillDto();
billDto.setUserId(user.getId());
billDto.setStartDate(startOfDay);
billDto.setEndDate(endOfDay.plusDays(1));
List<BillVo> billVoList = billMapper.selectListByBillNotification(billDto);
// 发送邮件
executeSendEmail(startOfDay, endOfDay, billVoList, user, emailTemplate);
});
}
/**
* 整理当前日期和参数并发送邮件
*

View File

@ -50,6 +50,7 @@ public class EmailFactory {
BeanUtils.copyProperties(emailUsers, emailSendInit);
emailSendInit.setUsername(emailUsers.getEmail());
emailSendInit.setProtocol(emailUsers.getSmtpAgreement());
emailSendInit.setOpenSSL(true);
// 邮件发送模板
EmailSend emailSend = new EmailSend();

View File

@ -0,0 +1,29 @@
package cn.bunny.services.quartz;
import cn.bunny.services.aop.annotation.QuartzSchedulers;
import cn.bunny.services.factory.BillFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
@QuartzSchedulers(type = "email-bill-mouth-report", description = "账单邮件任务-月")
@Component
public class MailBillReportByMouth implements Job {
@Autowired
private BillFactory billFactory;
@Override
public void execute(JobExecutionContext jobExecutionContext) {
LocalDate today = LocalDate.now();
// 这个日期的开始月份和结束月份
LocalDate startOfMouth = today.withDayOfMonth(1);
LocalDate endOfMouth = today.withDayOfMonth(today.lengthOfMonth());
billFactory.billReport(startOfMouth, endOfMouth);
}
}

View File

@ -0,0 +1,31 @@
package cn.bunny.services.quartz;
import cn.bunny.services.aop.annotation.QuartzSchedulers;
import cn.bunny.services.factory.BillFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
@QuartzSchedulers(type = "email-bill-week-report", description = "账单邮件任务-周")
@Component
public class MailBillReportByWeek implements Job {
@Autowired
private BillFactory billFactory;
@Override
public void execute(JobExecutionContext jobExecutionContext) {
LocalDate today = LocalDate.now();
// 这周的开始日期和结束日期
LocalDate startOfWeek = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
LocalDate endOfWeek = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
billFactory.billReport(startOfWeek, endOfWeek);
}
}

View File

@ -1,73 +0,0 @@
package cn.bunny.services.quartz;
import cn.bunny.dao.dto.financial.bill.BillDto;
import cn.bunny.dao.entity.system.AdminUser;
import cn.bunny.dao.entity.system.EmailTemplate;
import cn.bunny.dao.pojo.enums.EmailTemplateEnums;
import cn.bunny.dao.vo.financial.admin.BillVo;
import cn.bunny.services.aop.annotation.QuartzSchedulers;
import cn.bunny.services.factory.BillFactory;
import cn.bunny.services.mapper.email.EmailTemplateMapper;
import cn.bunny.services.mapper.financial.BillMapper;
import cn.bunny.services.mapper.system.UserMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.List;
@QuartzSchedulers(type = "email-bill-week-report", description = "账单邮件任务-周")
@Component
public class MailingJob implements Job {
@Autowired
private EmailTemplateMapper emailTemplateMapper;
@Autowired
private BillMapper billMapper;
@Autowired
private UserMapper userMapper;
@Autowired
private BillFactory billFactory;
@Override
public void execute(JobExecutionContext jobExecutionContext) {
LocalDate today = LocalDate.now();
LocalDate startOfWeek = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
LocalDate endOfWeek = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
// 查询验证码邮件模板
LambdaQueryWrapper<EmailTemplate> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(EmailTemplate::getIsDefault, true);
lambdaQueryWrapper.eq(EmailTemplate::getType, EmailTemplateEnums.BILL_WEEK_NOTIFICATION.getType());
EmailTemplate emailTemplate = emailTemplateMapper.selectOne(lambdaQueryWrapper);
// 查询需要发送邮件的用户
LambdaQueryWrapper<AdminUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AdminUser::getWeekBillReport, true)
.eq(AdminUser::getStatus, false)// 没有被禁用的
.isNotNull(AdminUser::getEmail);// 有邮箱的
List<AdminUser> userList = userMapper.selectList(queryWrapper);
userList.forEach(user -> {
Long userId = user.getId();
String email = user.getEmail();
// 查询当前用户账单数据
BillDto billDto = new BillDto();
billDto.setUserId(userId);
billDto.setStartDate(startOfWeek);
billDto.setEndDate(endOfWeek.plusDays(1));
List<BillVo> billVoList = billMapper.selectListByBillNotification(billDto);
billFactory.executeSendEmail(startOfWeek, endOfWeek, billVoList, user, emailTemplate);
});
}
}

View File

@ -278,7 +278,7 @@ public class BillServiceImpl extends ServiceImpl<BillMapper, Bill> implements Bi
try {
EasyExcel.read(file.getInputStream(), BillImportByUserDto.class, new BillAddUserListener(categoryMapper, messageMapper, messageReceivedMapper, this)).sheet().doRead();
} catch (IOException e) {
throw new AuthCustomerException(ResultCodeEnum.UPDATE_ERROR);
throw new AuthCustomerException(ResultCodeEnum.UPLOAD_ERROR);
}
}

View File

@ -3,6 +3,7 @@ package cn.bunny.services.service.schedule;
import cn.bunny.dao.dto.quartz.SchedulersOperationDto;
import cn.bunny.dao.dto.quartz.schedule.SchedulersAddDto;
import cn.bunny.dao.dto.quartz.schedule.SchedulersDto;
import cn.bunny.dao.dto.quartz.schedule.SchedulersUpdateDto;
import cn.bunny.dao.entity.quartz.Schedulers;
import cn.bunny.dao.pojo.result.PageResult;
import cn.bunny.dao.vo.quartz.SchedulersVo;
@ -64,4 +65,11 @@ public interface SchedulersService extends IService<Schedulers> {
* @return 所有调度任务内容
*/
List<Map<String, String>> getAllScheduleJobList();
/**
* 更新任务
*
* @param dto 更新任务表单
*/
void updateSchedulers(SchedulersUpdateDto dto);
}

View File

@ -4,8 +4,10 @@ import cn.bunny.common.service.exception.AuthCustomerException;
import cn.bunny.dao.dto.quartz.SchedulersOperationDto;
import cn.bunny.dao.dto.quartz.schedule.SchedulersAddDto;
import cn.bunny.dao.dto.quartz.schedule.SchedulersDto;
import cn.bunny.dao.dto.quartz.schedule.SchedulersUpdateDto;
import cn.bunny.dao.entity.quartz.Schedulers;
import cn.bunny.dao.pojo.result.PageResult;
import cn.bunny.dao.pojo.result.ResultCodeEnum;
import cn.bunny.dao.vo.quartz.SchedulersVo;
import cn.bunny.services.aop.AnnotationScanner;
import cn.bunny.services.aop.annotation.QuartzSchedulers;
@ -20,10 +22,7 @@ import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
* <p>
@ -92,6 +91,36 @@ public class SchedulersServiceImpl extends ServiceImpl<SchedulersMapper, Schedul
}).toList();
}
/**
* 更新任务
*
* @param dto 更新任务表单
*/
@Override
public void updateSchedulers(SchedulersUpdateDto dto) {
String jobName = dto.getJobName();
String jobGroup = dto.getJobGroup();
String cronExpression = dto.getCronExpression();
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobName, jobGroup)
.withDescription(dto.getDescription())
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.build();
try {
TriggerKey key = new TriggerKey(jobName, jobGroup);
Trigger oldTrigger = scheduler.getTrigger(key);
Date date = scheduler.rescheduleJob(oldTrigger.getKey(), trigger);
if (date == null) {
throw new AuthCustomerException(ResultCodeEnum.UPDATE_ERROR);
}
} catch (SchedulerException e) {
throw new AuthCustomerException(ResultCodeEnum.UPDATE_ERROR);
}
}
/**
* 添加Schedulers视图
*

View File

@ -27,7 +27,7 @@ import java.util.List;
import java.util.stream.Collectors;
@SpringBootTest
class MailingJobTest {
class MailBillReportByWeekTest {
@Autowired
private EmailTemplateMapper emailTemplateMapper;