Merge branch 'master-jdk21' of https://gitee.com/zhijiantianya/ruoyi-vue-pro
# Conflicts: # yudao-dependencies/pom.xml # yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobServiceImpl.java # yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/mail/MailAccountConvert.java # yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java # yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java
This commit is contained in:
commit
6c3f88bacd
@ -48,7 +48,7 @@ public class SchedulerManager {
|
|||||||
.withIdentity(jobHandlerName).build();
|
.withIdentity(jobHandlerName).build();
|
||||||
// 创建 Trigger 对象
|
// 创建 Trigger 对象
|
||||||
Trigger trigger = this.buildTrigger(jobHandlerName, jobHandlerParam, cronExpression, retryCount, retryInterval);
|
Trigger trigger = this.buildTrigger(jobHandlerName, jobHandlerParam, cronExpression, retryCount, retryInterval);
|
||||||
// 新增调度
|
// 新增 Job 调度
|
||||||
scheduler.scheduleJob(jobDetail, trigger);
|
scheduler.scheduleJob(jobDetail, trigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,6 +80,10 @@ public class SchedulerManager {
|
|||||||
*/
|
*/
|
||||||
public void deleteJob(String jobHandlerName) throws SchedulerException {
|
public void deleteJob(String jobHandlerName) throws SchedulerException {
|
||||||
validateScheduler();
|
validateScheduler();
|
||||||
|
// 暂停 Trigger 对象
|
||||||
|
scheduler.pauseTrigger(new TriggerKey(jobHandlerName));
|
||||||
|
// 取消并删除 Job 调度
|
||||||
|
scheduler.unscheduleJob(new TriggerKey(jobHandlerName));
|
||||||
scheduler.deleteJob(new JobKey(jobHandlerName));
|
scheduler.deleteJob(new JobKey(jobHandlerName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@ public interface ErrorCodeConstants {
|
|||||||
ErrorCode JOB_CHANGE_STATUS_EQUALS = new ErrorCode(1_001_001_003, "定时任务已经处于该状态,无需修改");
|
ErrorCode JOB_CHANGE_STATUS_EQUALS = new ErrorCode(1_001_001_003, "定时任务已经处于该状态,无需修改");
|
||||||
ErrorCode JOB_UPDATE_ONLY_NORMAL_STATUS = new ErrorCode(1_001_001_004, "只有开启状态的任务,才可以修改");
|
ErrorCode JOB_UPDATE_ONLY_NORMAL_STATUS = new ErrorCode(1_001_001_004, "只有开启状态的任务,才可以修改");
|
||||||
ErrorCode JOB_CRON_EXPRESSION_VALID = new ErrorCode(1_001_001_005, "CRON 表达式不正确");
|
ErrorCode JOB_CRON_EXPRESSION_VALID = new ErrorCode(1_001_001_005, "CRON 表达式不正确");
|
||||||
|
ErrorCode JOB_HANDLER_BEAN_NOT_EXISTS = new ErrorCode(1_001_001_006, "定时任务的处理器 Bean 不存在");
|
||||||
|
ErrorCode JOB_HANDLER_BEAN_TYPE_ERROR = new ErrorCode(1_001_001_007, "定时任务的处理器 Bean 类型不正确,未实现 JobHandler 接口");
|
||||||
|
|
||||||
// ========== API 错误日志 1-001-002-000 ==========
|
// ========== API 错误日志 1-001-002-000 ==========
|
||||||
ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1_001_002_000, "API 错误日志不存在");
|
ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1_001_002_000, "API 错误日志不存在");
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package cn.iocoder.yudao.module.infra.service.job;
|
package cn.iocoder.yudao.module.infra.service.job;
|
||||||
|
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
|
||||||
import cn.iocoder.yudao.framework.quartz.core.scheduler.SchedulerManager;
|
import cn.iocoder.yudao.framework.quartz.core.scheduler.SchedulerManager;
|
||||||
import cn.iocoder.yudao.framework.quartz.core.util.CronUtils;
|
import cn.iocoder.yudao.framework.quartz.core.util.CronUtils;
|
||||||
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobPageReqVO;
|
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobPageReqVO;
|
||||||
@ -9,15 +11,12 @@ import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobSaveReqVO;
|
|||||||
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobDO;
|
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobDO;
|
||||||
import cn.iocoder.yudao.module.infra.dal.mysql.job.JobMapper;
|
import cn.iocoder.yudao.module.infra.dal.mysql.job.JobMapper;
|
||||||
import cn.iocoder.yudao.module.infra.enums.job.JobStatusEnum;
|
import cn.iocoder.yudao.module.infra.enums.job.JobStatusEnum;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import org.quartz.SchedulerException;
|
import org.quartz.SchedulerException;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.containsAny;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.containsAny;
|
||||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
|
||||||
@ -41,24 +40,25 @@ public class JobServiceImpl implements JobService {
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Long createJob(JobSaveReqVO createReqVO) throws SchedulerException {
|
public Long createJob(JobSaveReqVO createReqVO) throws SchedulerException {
|
||||||
validateCronExpression(createReqVO.getCronExpression());
|
validateCronExpression(createReqVO.getCronExpression());
|
||||||
// 校验唯一性
|
// 1.1 校验唯一性
|
||||||
if (jobMapper.selectByHandlerName(createReqVO.getHandlerName()) != null) {
|
if (jobMapper.selectByHandlerName(createReqVO.getHandlerName()) != null) {
|
||||||
throw exception(JOB_HANDLER_EXISTS);
|
throw exception(JOB_HANDLER_EXISTS);
|
||||||
}
|
}
|
||||||
// 插入
|
// 1.2 校验 JobHandler 是否存在
|
||||||
|
validateJobHandlerExists(createReqVO.getHandlerName());
|
||||||
|
|
||||||
|
// 2. 插入 JobDO
|
||||||
JobDO job = BeanUtils.toBean(createReqVO, JobDO.class);
|
JobDO job = BeanUtils.toBean(createReqVO, JobDO.class);
|
||||||
job.setStatus(JobStatusEnum.INIT.getStatus());
|
job.setStatus(JobStatusEnum.INIT.getStatus());
|
||||||
fillJobMonitorTimeoutEmpty(job);
|
fillJobMonitorTimeoutEmpty(job);
|
||||||
jobMapper.insert(job);
|
jobMapper.insert(job);
|
||||||
|
|
||||||
// 添加 Job 到 Quartz 中
|
// 3.1 添加 Job 到 Quartz 中
|
||||||
schedulerManager.addJob(job.getId(), job.getHandlerName(), job.getHandlerParam(), job.getCronExpression(),
|
schedulerManager.addJob(job.getId(), job.getHandlerName(), job.getHandlerParam(), job.getCronExpression(),
|
||||||
createReqVO.getRetryCount(), createReqVO.getRetryInterval());
|
createReqVO.getRetryCount(), createReqVO.getRetryInterval());
|
||||||
// 更新
|
// 3.2 更新 JobDO
|
||||||
JobDO updateObj = JobDO.builder().id(job.getId()).status(JobStatusEnum.NORMAL.getStatus()).build();
|
JobDO updateObj = JobDO.builder().id(job.getId()).status(JobStatusEnum.NORMAL.getStatus()).build();
|
||||||
jobMapper.updateById(updateObj);
|
jobMapper.updateById(updateObj);
|
||||||
|
|
||||||
// 返回
|
|
||||||
return job.getId();
|
return job.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,22 +66,35 @@ public class JobServiceImpl implements JobService {
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void updateJob(JobSaveReqVO updateReqVO) throws SchedulerException {
|
public void updateJob(JobSaveReqVO updateReqVO) throws SchedulerException {
|
||||||
validateCronExpression(updateReqVO.getCronExpression());
|
validateCronExpression(updateReqVO.getCronExpression());
|
||||||
// 校验存在
|
// 1.1 校验存在
|
||||||
JobDO job = validateJobExists(updateReqVO.getId());
|
JobDO job = validateJobExists(updateReqVO.getId());
|
||||||
// 只有开启状态,才可以修改.原因是,如果出暂停状态,修改 Quartz Job 时,会导致任务又开始执行
|
// 1.2 只有开启状态,才可以修改.原因是,如果出暂停状态,修改 Quartz Job 时,会导致任务又开始执行
|
||||||
if (!job.getStatus().equals(JobStatusEnum.NORMAL.getStatus())) {
|
if (!job.getStatus().equals(JobStatusEnum.NORMAL.getStatus())) {
|
||||||
throw exception(JOB_UPDATE_ONLY_NORMAL_STATUS);
|
throw exception(JOB_UPDATE_ONLY_NORMAL_STATUS);
|
||||||
}
|
}
|
||||||
// 更新
|
// 1.3 校验 JobHandler 是否存在
|
||||||
|
validateJobHandlerExists(updateReqVO.getHandlerName());
|
||||||
|
|
||||||
|
// 2. 更新 JobDO
|
||||||
JobDO updateObj = BeanUtils.toBean(updateReqVO, JobDO.class);
|
JobDO updateObj = BeanUtils.toBean(updateReqVO, JobDO.class);
|
||||||
fillJobMonitorTimeoutEmpty(updateObj);
|
fillJobMonitorTimeoutEmpty(updateObj);
|
||||||
jobMapper.updateById(updateObj);
|
jobMapper.updateById(updateObj);
|
||||||
|
|
||||||
// 更新 Job 到 Quartz 中
|
// 3. 更新 Job 到 Quartz 中
|
||||||
schedulerManager.updateJob(job.getHandlerName(), updateReqVO.getHandlerParam(), updateReqVO.getCronExpression(),
|
schedulerManager.updateJob(job.getHandlerName(), updateReqVO.getHandlerParam(), updateReqVO.getCronExpression(),
|
||||||
updateReqVO.getRetryCount(), updateReqVO.getRetryInterval());
|
updateReqVO.getRetryCount(), updateReqVO.getRetryInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateJobHandlerExists(String handlerName) {
|
||||||
|
Object handler = SpringUtil.getBean(handlerName);
|
||||||
|
if (handler == null) {
|
||||||
|
throw exception(JOB_HANDLER_BEAN_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
if (!(handler instanceof JobHandler)) {
|
||||||
|
throw exception(JOB_HANDLER_BEAN_TYPE_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void updateJobStatus(Long id, Integer status) throws SchedulerException {
|
public void updateJobStatus(Long id, Integer status) throws SchedulerException {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package cn.iocoder.yudao.module.infra.service.job;
|
package cn.iocoder.yudao.module.infra.service.job;
|
||||||
|
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.quartz.core.scheduler.SchedulerManager;
|
import cn.iocoder.yudao.framework.quartz.core.scheduler.SchedulerManager;
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||||
@ -8,7 +9,9 @@ import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobSaveReqVO;
|
|||||||
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobDO;
|
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobDO;
|
||||||
import cn.iocoder.yudao.module.infra.dal.mysql.job.JobMapper;
|
import cn.iocoder.yudao.module.infra.dal.mysql.job.JobMapper;
|
||||||
import cn.iocoder.yudao.module.infra.enums.job.JobStatusEnum;
|
import cn.iocoder.yudao.module.infra.enums.job.JobStatusEnum;
|
||||||
|
import cn.iocoder.yudao.module.infra.job.job.JobLogCleanJob;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
import org.quartz.SchedulerException;
|
import org.quartz.SchedulerException;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
@ -23,6 +26,7 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString
|
|||||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mockStatic;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
@Import(JobServiceImpl.class)
|
@Import(JobServiceImpl.class)
|
||||||
@ -35,6 +39,9 @@ public class JobServiceImplTest extends BaseDbUnitTest {
|
|||||||
@MockBean
|
@MockBean
|
||||||
private SchedulerManager schedulerManager;
|
private SchedulerManager schedulerManager;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private JobLogCleanJob jobLogCleanJob;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateJob_cronExpressionValid() {
|
public void testCreateJob_cronExpressionValid() {
|
||||||
// 准备参数。Cron 表达式为 String 类型,默认随机字符串。
|
// 准备参数。Cron 表达式为 String 类型,默认随机字符串。
|
||||||
@ -48,11 +55,15 @@ public class JobServiceImplTest extends BaseDbUnitTest {
|
|||||||
public void testCreateJob_jobHandlerExists() throws SchedulerException {
|
public void testCreateJob_jobHandlerExists() throws SchedulerException {
|
||||||
// 准备参数 指定 Cron 表达式
|
// 准备参数 指定 Cron 表达式
|
||||||
JobSaveReqVO reqVO = randomPojo(JobSaveReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
|
JobSaveReqVO reqVO = randomPojo(JobSaveReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
|
||||||
|
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
|
||||||
|
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(reqVO.getHandlerName())))
|
||||||
|
.thenReturn(jobLogCleanJob);
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
jobService.createJob(reqVO);
|
jobService.createJob(reqVO);
|
||||||
// 调用,并断言异常
|
// 调用,并断言异常
|
||||||
assertServiceException(() -> jobService.createJob(reqVO), JOB_HANDLER_EXISTS);
|
assertServiceException(() -> jobService.createJob(reqVO), JOB_HANDLER_EXISTS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -60,18 +71,22 @@ public class JobServiceImplTest extends BaseDbUnitTest {
|
|||||||
// 准备参数 指定 Cron 表达式
|
// 准备参数 指定 Cron 表达式
|
||||||
JobSaveReqVO reqVO = randomPojo(JobSaveReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"))
|
JobSaveReqVO reqVO = randomPojo(JobSaveReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"))
|
||||||
.setId(null);
|
.setId(null);
|
||||||
|
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
|
||||||
|
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(reqVO.getHandlerName())))
|
||||||
|
.thenReturn(jobLogCleanJob);
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
Long jobId = jobService.createJob(reqVO);
|
Long jobId = jobService.createJob(reqVO);
|
||||||
// 断言
|
// 断言
|
||||||
assertNotNull(jobId);
|
assertNotNull(jobId);
|
||||||
// 校验记录的属性是否正确
|
// 校验记录的属性是否正确
|
||||||
JobDO job = jobMapper.selectById(jobId);
|
JobDO job = jobMapper.selectById(jobId);
|
||||||
assertPojoEquals(reqVO, job, "id");
|
assertPojoEquals(reqVO, job, "id");
|
||||||
assertEquals(JobStatusEnum.NORMAL.getStatus(), job.getStatus());
|
assertEquals(JobStatusEnum.NORMAL.getStatus(), job.getStatus());
|
||||||
// 校验调用
|
// 校验调用
|
||||||
verify(schedulerManager).addJob(eq(job.getId()), eq(job.getHandlerName()), eq(job.getHandlerParam()),
|
verify(schedulerManager).addJob(eq(job.getId()), eq(job.getHandlerName()), eq(job.getHandlerParam()),
|
||||||
eq(job.getCronExpression()), eq(reqVO.getRetryCount()), eq(reqVO.getRetryInterval()));
|
eq(job.getCronExpression()), eq(reqVO.getRetryCount()), eq(reqVO.getRetryInterval()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -109,15 +124,19 @@ public class JobServiceImplTest extends BaseDbUnitTest {
|
|||||||
o.setId(job.getId());
|
o.setId(job.getId());
|
||||||
o.setCronExpression("0 0/1 * * * ? *");
|
o.setCronExpression("0 0/1 * * * ? *");
|
||||||
});
|
});
|
||||||
|
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
|
||||||
|
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(updateReqVO.getHandlerName())))
|
||||||
|
.thenReturn(jobLogCleanJob);
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
jobService.updateJob(updateReqVO);
|
jobService.updateJob(updateReqVO);
|
||||||
// 校验记录的属性是否正确
|
// 校验记录的属性是否正确
|
||||||
JobDO updateJob = jobMapper.selectById(updateReqVO.getId());
|
JobDO updateJob = jobMapper.selectById(updateReqVO.getId());
|
||||||
assertPojoEquals(updateReqVO, updateJob);
|
assertPojoEquals(updateReqVO, updateJob);
|
||||||
// 校验调用
|
// 校验调用
|
||||||
verify(schedulerManager).updateJob(eq(job.getHandlerName()), eq(updateReqVO.getHandlerParam()),
|
verify(schedulerManager).updateJob(eq(job.getHandlerName()), eq(updateReqVO.getHandlerParam()),
|
||||||
eq(updateReqVO.getCronExpression()), eq(updateReqVO.getRetryCount()), eq(updateReqVO.getRetryInterval()));
|
eq(updateReqVO.getCronExpression()), eq(updateReqVO.getRetryCount()), eq(updateReqVO.getRetryInterval()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -127,6 +127,12 @@
|
|||||||
<groupId>com.xingyuv</groupId>
|
<groupId>com.xingyuv</groupId>
|
||||||
<artifactId>spring-boot-starter-captcha-plus</artifactId> <!-- 验证码,一般用于登录使用 -->
|
<artifactId>spring-boot-starter-captcha-plus</artifactId> <!-- 验证码,一般用于登录使用 -->
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara.hutool</groupId>
|
||||||
|
<artifactId>hutool-extra</artifactId> <!-- 邮件 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.system.convert.mail;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import cn.hutool.extra.mail.MailAccount;
|
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
|
|
||||||
import org.mapstruct.Mapper;
|
|
||||||
import org.mapstruct.factory.Mappers;
|
|
||||||
|
|
||||||
@Mapper
|
|
||||||
public interface MailAccountConvert {
|
|
||||||
|
|
||||||
MailAccountConvert INSTANCE = Mappers.getMapper(MailAccountConvert.class);
|
|
||||||
|
|
||||||
default MailAccount convert(MailAccountDO account, String nickname) {
|
|
||||||
String from = StrUtil.isNotEmpty(nickname) ? nickname + " <" + account.getMail() + ">" : account.getMail();
|
|
||||||
return new MailAccount().setFrom(from).setAuth(true)
|
|
||||||
.setUser(account.getUsername()).setPass(account.getPassword())
|
|
||||||
.setHost(account.getHost()).setPort(account.getPort()).setSslEnable(account.getSslEnable());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,11 +1,8 @@
|
|||||||
package cn.iocoder.yudao.module.system.service.mail;
|
package cn.iocoder.yudao.module.system.service.mail;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.extra.mail.MailAccount;
|
|
||||||
import cn.hutool.extra.mail.MailUtil;
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
|
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||||
@ -14,11 +11,12 @@ import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
|
|||||||
import cn.iocoder.yudao.module.system.service.member.MemberService;
|
import cn.iocoder.yudao.module.system.service.member.MemberService;
|
||||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.hutool.extra.mail.*;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
@ -105,11 +103,11 @@ public class MailSendServiceImpl implements MailSendService {
|
|||||||
public void doSendMail(MailSendMessage message) {
|
public void doSendMail(MailSendMessage message) {
|
||||||
// 1. 创建发送账号
|
// 1. 创建发送账号
|
||||||
MailAccountDO account = validateMailAccount(message.getAccountId());
|
MailAccountDO account = validateMailAccount(message.getAccountId());
|
||||||
MailAccount mailAccount = MailAccountConvert.INSTANCE.convert(account, message.getNickname());
|
MailAccount mailAccount = buildMailAccount(account, message.getNickname());
|
||||||
// 2. 发送邮件
|
// 2. 发送邮件
|
||||||
try {
|
try {
|
||||||
String messageId = MailUtil.send(mailAccount, message.getMail(),
|
String messageId = MailUtil.send(mailAccount, message.getMail(),
|
||||||
message.getTitle(), message.getContent(),true);
|
message.getTitle(), message.getContent(), true);
|
||||||
// 3. 更新结果(成功)
|
// 3. 更新结果(成功)
|
||||||
mailLogService.updateMailSendResult(message.getLogId(), messageId, null);
|
mailLogService.updateMailSendResult(message.getLogId(), messageId, null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -118,6 +116,13 @@ public class MailSendServiceImpl implements MailSendService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MailAccount buildMailAccount(MailAccountDO account, String nickname) {
|
||||||
|
String from = StrUtil.isNotEmpty(nickname) ? nickname + " <" + account.getMail() + ">" : account.getMail();
|
||||||
|
return new MailAccount().setFrom(from).setAuth(true)
|
||||||
|
.setUser(account.getUsername()).setPass(account.getPassword().toCharArray())
|
||||||
|
.setHost(account.getHost()).setPort(account.getPort()).setSslEnable(account.getSslEnable());
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
MailTemplateDO validateMailTemplate(String templateCode) {
|
MailTemplateDO validateMailTemplate(String templateCode) {
|
||||||
// 获得邮件模板。考虑到效率,从缓存中获取
|
// 获得邮件模板。考虑到效率,从缓存中获取
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package cn.iocoder.yudao.module.system.service.mail;
|
package cn.iocoder.yudao.module.system.service.mail;
|
||||||
|
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.extra.mail.MailAccount;
|
|
||||||
import cn.hutool.extra.mail.MailUtil;
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
@ -15,6 +13,7 @@ import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
|
|||||||
import cn.iocoder.yudao.module.system.service.member.MemberService;
|
import cn.iocoder.yudao.module.system.service.member.MemberService;
|
||||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||||
import org.assertj.core.util.Lists;
|
import org.assertj.core.util.Lists;
|
||||||
|
import org.dromara.hutool.extra.mail.*;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
@ -28,8 +27,7 @@ import static cn.hutool.core.util.RandomUtil.randomEle;
|
|||||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
@ -61,7 +59,7 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
|||||||
// .setFrom("奥特曼 <ydym_test@163.com>")
|
// .setFrom("奥特曼 <ydym_test@163.com>")
|
||||||
.setFrom("ydym_test@163.com") // 邮箱地址
|
.setFrom("ydym_test@163.com") // 邮箱地址
|
||||||
.setHost("smtp.163.com").setPort(465).setSslEnable(true) // SMTP 服务器
|
.setHost("smtp.163.com").setPort(465).setSslEnable(true) // SMTP 服务器
|
||||||
.setAuth(true).setUser("ydym_test@163.com").setPass("WBZTEINMIFVRYSOE"); // 登录账号密码
|
.setAuth(true).setUser("ydym_test@163.com").setPass("WBZTEINMIFVRYSOE".toCharArray()); // 登录账号密码
|
||||||
String messageId = MailUtil.send(mailAccount, "7685413@qq.com", "主题", "内容", false);
|
String messageId = MailUtil.send(mailAccount, "7685413@qq.com", "主题", "内容", false);
|
||||||
System.out.println("发送结果:" + messageId);
|
System.out.println("发送结果:" + messageId);
|
||||||
}
|
}
|
||||||
@ -268,7 +266,7 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDoSendMail_success() {
|
public void testDoSendMail_success() {
|
||||||
try (MockedStatic<MailUtil> mailUtilMock = mockStatic(MailUtil.class)) {
|
try (final MockedStatic<MailUtil> mailUtilMock = mockStatic(MailUtil.class)) {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
MailSendMessage message = randomPojo(MailSendMessage.class, o -> o.setNickname("芋艿"));
|
MailSendMessage message = randomPojo(MailSendMessage.class, o -> o.setNickname("芋艿"));
|
||||||
// mock 方法(获得邮箱账号)
|
// mock 方法(获得邮箱账号)
|
||||||
@ -283,7 +281,7 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
|||||||
assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom());
|
assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom());
|
||||||
assertTrue(mailAccount.isAuth());
|
assertTrue(mailAccount.isAuth());
|
||||||
assertEquals(account.getUsername(), mailAccount.getUser());
|
assertEquals(account.getUsername(), mailAccount.getUser());
|
||||||
assertEquals(account.getPassword(), mailAccount.getPass());
|
assertArrayEquals(account.getPassword().toCharArray(), mailAccount.getPass());
|
||||||
assertEquals(account.getHost(), mailAccount.getHost());
|
assertEquals(account.getHost(), mailAccount.getHost());
|
||||||
assertEquals(account.getPort(), mailAccount.getPort());
|
assertEquals(account.getPort(), mailAccount.getPort());
|
||||||
assertEquals(account.getSslEnable(), mailAccount.isSslEnable());
|
assertEquals(account.getSslEnable(), mailAccount.isSslEnable());
|
||||||
@ -311,16 +309,15 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
|||||||
// mock 方法(发送邮件)
|
// mock 方法(发送邮件)
|
||||||
Exception e = new NullPointerException("啦啦啦");
|
Exception e = new NullPointerException("啦啦啦");
|
||||||
mailUtilMock.when(() -> MailUtil.send(argThat(mailAccount -> {
|
mailUtilMock.when(() -> MailUtil.send(argThat(mailAccount -> {
|
||||||
assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom());
|
assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom());
|
||||||
assertTrue(mailAccount.isAuth());
|
assertTrue(mailAccount.isAuth());
|
||||||
assertEquals(account.getUsername(), mailAccount.getUser());
|
assertEquals(account.getUsername(), mailAccount.getUser());
|
||||||
assertEquals(account.getPassword(), mailAccount.getPass());
|
assertArrayEquals(account.getPassword().toCharArray(), mailAccount.getPass());
|
||||||
assertEquals(account.getHost(), mailAccount.getHost());
|
assertEquals(account.getHost(), mailAccount.getHost());
|
||||||
assertEquals(account.getPort(), mailAccount.getPort());
|
assertEquals(account.getPort(), mailAccount.getPort());
|
||||||
assertEquals(account.getSslEnable(), mailAccount.isSslEnable());
|
assertEquals(account.getSslEnable(), mailAccount.isSslEnable());
|
||||||
return true;
|
return true;
|
||||||
}), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true)))
|
}), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true))).thenThrow(e);
|
||||||
.thenThrow(e);
|
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
mailSendService.doSendMail(message);
|
mailSendService.doSendMail(message);
|
||||||
|
Loading…
Reference in New Issue
Block a user