pom.xml
@@ -212,6 +212,13 @@ <artifactId>groovy</artifactId> <version>${groovy.version}</version> </dependency> <!--邮件依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> </dependencies> <build> src/main/java/org/springblade/modules/email/config/MailProperties.java
New file @@ -0,0 +1,26 @@ package org.springblade.modules.email.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * @author crush */ @Data @Component @ConfigurationProperties(prefix = "spring.mail") public class MailProperties { /** * 用户名 */ private String username; /** * 授权码 */ private String password; /** * host */ private String host; /** * 端口 */ private Integer port; /*** 协议 */ private String protocol; /** * 默认编码*/ private String defaultEncoding; } src/main/java/org/springblade/modules/email/config/MailSenderConfig.java
New file @@ -0,0 +1,71 @@ package org.springblade.modules.email.config; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springblade.core.mp.support.Condition; import org.springblade.modules.email.entity.EmailEntity; import org.springblade.modules.email.mapper.EmailMapper; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.stereotype.Component; import java.util.List; @Slf4j @Component @AllArgsConstructor public class MailSenderConfig { private final MailProperties mailProperties; private final EmailMapper emailMapper; /** * 初始化 sender * PostConstruct注解用于需要在依赖注入完成后执行任何初始化的方法。 必须在类投入使用之前调用此方法 * 因为刚开始我觉得这种方式(@PostConstruct) 不合适,就是没能做到修改了马上就能用的那种感觉。 * 但是后来写完才发现,其实只要每次添加新的邮件发送人时,都重新初始化一次就可以了。 * 后来我又用启动事件监听器。@PostConstruct 后来就没去测试了。 * 理论添加、修改完 调用这个初始化方法就可以了。 */ // @PostConstruct public JavaMailSenderImpl buildMailSender() { log.info("初始化mailSender"); //获取数据库中启用的邮件配置 EmailEntity params = new EmailEntity(); params.setStatus(2); EmailEntity email = emailMapper.selectOne(Condition.getQueryWrapper(params)); JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); //如果数据库中没有配置邮件,则使用默认的邮件配置 if(email != null){ javaMailSender.setDefaultEncoding(email.getDefaultEncoding()); javaMailSender.setHost(email.getHost()); javaMailSender.setPort(email.getPort()); javaMailSender.setProtocol(email.getProtocol()); javaMailSender.setUsername(email.getUsername()); javaMailSender.setPassword(email.getPassword()); } else{ javaMailSender.setDefaultEncoding(mailProperties.getDefaultEncoding()); javaMailSender.setHost(mailProperties.getHost()); javaMailSender.setPort(mailProperties.getPort()); javaMailSender.setProtocol(mailProperties.getProtocol()); javaMailSender.setUsername(mailProperties.getUsername()); javaMailSender.setPassword(mailProperties.getPassword()); } return javaMailSender; } /** * 获取MailSender * * @return CustomMailSender */ public JavaMailSenderImpl getSender() { return buildMailSender(); } } src/main/java/org/springblade/modules/email/config/StartListener.java
New file @@ -0,0 +1,28 @@ package org.springblade.modules.email.config; import groovyjarjarantlr4.v4.runtime.misc.NotNull; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; @Slf4j @Configuration @Order(Ordered.HIGHEST_PRECEDENCE) public class StartListener implements ApplicationListener<ApplicationStartedEvent> { MailSenderConfig mailSenderConfig; public StartListener(MailSenderConfig mailSenderConfig) { this.mailSenderConfig = mailSenderConfig; } @SneakyThrows @Override public void onApplicationEvent(@NotNull ApplicationStartedEvent event) { this.mailSenderConfig.buildMailSender(); } } src/main/java/org/springblade/modules/email/config/ThreadPoolTaskExecutorConfig.java
New file @@ -0,0 +1,33 @@ package org.springblade.modules.email.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.TaskExecutor; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.ThreadPoolExecutor; @Configuration @EnableAsync // 开启异步配置 public class ThreadPoolTaskExecutorConfig { @Bean public TaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //设置核心线程数 executor.setCorePoolSize(10); //设置最大线程数 executor.setMaxPoolSize(20); //缓冲队列200:用来缓冲执行任务的队列 executor.setQueueCapacity(200); //线程活路时间 60 秒 executor.setKeepAliveSeconds(60); //线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池 executor.setThreadNamePrefix("taskExecutor-"); //设置拒绝策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.setWaitForTasksToCompleteOnShutdown(true); return executor; } } src/main/java/org/springblade/modules/email/controller/EmailController.java
New file @@ -0,0 +1,128 @@ package org.springblade.modules.email.controller; import com.baomidou.mybatisplus.core.metadata.IPage; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.AllArgsConstructor; import org.springblade.core.boot.ctrl.BladeController; import org.springblade.core.cache.utils.CacheUtil; import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Query; import org.springblade.core.tenant.annotation.NonDS; import org.springblade.core.tool.api.R; import org.springblade.core.tool.utils.Func; import org.springblade.modules.email.entity.EmailAccount; import org.springblade.modules.email.entity.EmailEntity; import org.springblade.modules.email.service.IEmailAccountService; import org.springblade.modules.email.service.IEmailService; import org.springblade.modules.email.vo.EmailVO; import org.springblade.modules.email.wrapper.EmailWrapper; import org.springframework.web.bind.annotation.*; import springfox.documentation.annotations.ApiIgnore; import javax.validation.Valid; import static org.springblade.core.cache.constant.CacheConstant.RESOURCE_CACHE; @NonDS @ApiIgnore @RestController @RequestMapping("/blade-email/email") @AllArgsConstructor public class EmailController extends BladeController { private final IEmailAccountService emailAccountService; private final IEmailService emailService; /** * 邮件配置 详情 */ @GetMapping("/detail") @ApiOperationSupport(order = 1) @ApiOperation(value = "详情", notes = "传入email") public R<EmailVO> detail(EmailEntity email) { EmailEntity detail = emailService.getOne(Condition.getQueryWrapper(email)); return R.data(EmailWrapper.build().entityVO(detail)); } /** * 邮件配置 分页 */ @GetMapping("/list") @ApiOperationSupport(order = 2) @ApiOperation(value = "分页", notes = "传入email") public R<IPage<EmailVO>> list(EmailEntity email, Query query) { IPage<EmailEntity> pages = emailService.page(Condition.getPage(query), Condition.getQueryWrapper(email)); return R.data(EmailWrapper.build().pageVO(pages)); } /** * 邮件配置 自定义分页 */ @GetMapping("/page") @ApiOperationSupport(order = 3) @ApiOperation(value = "分页", notes = "传入email") public R<IPage<EmailVO>> page(EmailVO email, Query query) { IPage<EmailVO> pages = emailService.selectEmailPage(Condition.getPage(query), email); return R.data(pages); } /** * 邮件配置 新增 */ @PostMapping("/save") @ApiOperationSupport(order = 4) @ApiOperation(value = "新增", notes = "传入email") public R save(@Valid @RequestBody EmailEntity email) { return R.status(emailService.save(email)); } /** * 邮件配置 修改 */ @PostMapping("/update") @ApiOperationSupport(order = 5) @ApiOperation(value = "修改", notes = "传入email") public R update(@Valid @RequestBody EmailEntity email) { return R.status(emailService.updateById(email)); } /** * 邮件配置 新增或修改 */ @PostMapping("/submit") @ApiOperationSupport(order = 6) @ApiOperation(value = "新增或修改", notes = "传入email") public R submit(@Valid @RequestBody EmailEntity email) { return R.status(emailService.saveOrUpdate(email)); } /** * 邮件配置 删除 */ @PostMapping("/remove") @ApiOperationSupport(order = 7) @ApiOperation(value = "逻辑删除", notes = "传入ids") public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) { return R.status(emailService.deleteLogic(Func.toLongList(ids))); } /** * 启用 */ @PostMapping("/enable") @ApiOperationSupport(order = 8) @ApiOperation(value = "配置启用", notes = "传入id") public R enable(@ApiParam(value = "主键", required = true) @RequestParam Long id) { return R.status(emailService.enable(id)); } @PostMapping("/sendEmail") public R sendEmail( EmailAccount emailAccount){ emailAccountService.senderEmail(emailAccount); return R.success("发送成功"); } } src/main/java/org/springblade/modules/email/entity/EmailAccount.java
New file @@ -0,0 +1,18 @@ package org.springblade.modules.email.entity; import lombok.Data; import java.util.List; @Data public class EmailAccount { //主题 private String subject; //内容 private String content; private List<String> emails; } src/main/java/org/springblade/modules/email/entity/EmailEntity.java
New file @@ -0,0 +1,75 @@ /* * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: Chill 庄骞 (smallchill@163.com) */ package org.springblade.modules.email.entity; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.util.Date; import lombok.EqualsAndHashCode; import org.springblade.core.tenant.mp.TenantEntity; /** * 邮件配置 实体类 * * @author BladeX * @since 2024-01-18 */ @Data @TableName("blade_email") @ApiModel(value = "Email对象", description = "邮件配置") @EqualsAndHashCode(callSuper = true) public class EmailEntity extends TenantEntity { /** * 发送者邮箱 */ @ApiModelProperty(value = "发送者邮箱") private String username; /** * 授权码 */ @ApiModelProperty(value = "授权码") private String password; /** * 服务器地址 */ @ApiModelProperty(value = "服务器地址") private String host; /** * 端口号 */ @ApiModelProperty(value = "端口号") private Integer port; /** * 默认编码 */ @ApiModelProperty(value = "默认编码(默认UTF-8)") private String defaultEncoding; /** * 协议(默认stmp) */ @ApiModelProperty(value = "协议(默认stmps)") private String protocol; /** * 备注 */ @ApiModelProperty(value = "备注") private String remark; } src/main/java/org/springblade/modules/email/mapper/EmailMapper.java
New file @@ -0,0 +1,43 @@ /* * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: Chill 庄骞 (smallchill@163.com) */ package org.springblade.modules.email.mapper; import org.springblade.modules.email.entity.EmailEntity; import org.springblade.modules.email.vo.EmailVO; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import java.util.List; /** * 邮件配置 Mapper 接口 * * @author BladeX * @since 2024-01-18 */ public interface EmailMapper extends BaseMapper<EmailEntity> { /** * 自定义分页 * * @param page * @param email * @return */ List<EmailVO> selectEmailPage(IPage page, EmailVO email); } src/main/java/org/springblade/modules/email/mapper/EmailMapper.xml
New file @@ -0,0 +1,31 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="org.springblade.modules.email.mapper.EmailMapper"> <!-- 通用查询映射结果 --> <resultMap id="emailResultMap" type="org.springblade.modules.email.entity.EmailEntity"> <result column="id" property="id"/> <result column="username" property="username"/> <result column="password" property="password"/> <result column="host" property="host"/> <result column="port" property="port"/> <result column="default_encoding" property="defaultEncoding"/> <result column="protocol" property="protocol"/> <result column="remark" property="remark"/> <result column="tenant_id" property="tenantId"/> <result column="create_user" property="createUser"/> <result column="create_dept" property="createDept"/> <result column="create_time" property="createTime"/> <result column="update_user" property="updateUser"/> <result column="update_time" property="updateTime"/> <result column="status" property="status"/> <result column="is_deleted" property="isDeleted"/> </resultMap> <select id="selectEmailPage" resultMap="emailResultMap"> select * from blade_email where is_deleted = 0 </select> </mapper> src/main/java/org/springblade/modules/email/service/IEmailAccountService.java
New file @@ -0,0 +1,9 @@ package org.springblade.modules.email.service; import org.springblade.modules.email.entity.EmailAccount; public interface IEmailAccountService { /**用于注册成功后发送邮件 @param account 账号信息*/ void senderEmail(EmailAccount account); } src/main/java/org/springblade/modules/email/service/IEmailService.java
New file @@ -0,0 +1,43 @@ /* * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: Chill 庄骞 (smallchill@163.com) */ package org.springblade.modules.email.service; import org.springblade.modules.email.entity.EmailEntity; import org.springblade.modules.email.vo.EmailVO; import org.springblade.core.mp.base.BaseService; import com.baomidou.mybatisplus.core.metadata.IPage; /** * 邮件配置 服务类 * * @author BladeX * @since 2024-01-18 */ public interface IEmailService extends BaseService<EmailEntity> { /** * 自定义分页 * * @param page * @param email * @return */ IPage<EmailVO> selectEmailPage(IPage<EmailVO> page, EmailVO email); boolean enable(Long id); } src/main/java/org/springblade/modules/email/service/impl/EmailAccountServiceImpl.java
New file @@ -0,0 +1,55 @@ package org.springblade.modules.email.service.impl; import lombok.extern.slf4j.Slf4j; import org.springblade.modules.email.config.MailProperties; import org.springblade.modules.email.config.MailSenderConfig; import org.springblade.modules.email.entity.EmailAccount; import org.springblade.modules.email.service.IEmailAccountService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; @Service @Slf4j public class EmailAccountServiceImpl implements IEmailAccountService { @Autowired MailSenderConfig senderConfig; @Autowired MailProperties mailProperties; @Override public void senderEmail(EmailAccount account) { log.info(Thread.currentThread().getName()); JavaMailSenderImpl javaMailSender = senderConfig.getSender(); //一个复杂的邮件 MimeMessage message = javaMailSender.createMimeMessage(); try { //组装 MimeMessageHelper helper = new MimeMessageHelper(message, true); //主题(标题) helper.setSubject(account.getSubject()); helper.setText(account.getContent(),true); helper.setTo(account.getEmails().toArray(new String[account.getEmails().size()])); helper.setFrom(javaMailSender.getUsername()); javaMailSender.send(message); } catch (MessagingException e) { e.printStackTrace(); } } } src/main/java/org/springblade/modules/email/service/impl/EmailServiceImpl.java
New file @@ -0,0 +1,55 @@ /* * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: Chill 庄骞 (smallchill@163.com) */ package org.springblade.modules.email.service.impl; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import org.springblade.modules.email.entity.EmailEntity; import org.springblade.modules.email.vo.EmailVO; import org.springblade.modules.email.mapper.EmailMapper; import org.springblade.modules.email.service.IEmailService; import org.springblade.core.mp.base.BaseServiceImpl; import org.springblade.modules.resource.entity.Oss; import org.springframework.stereotype.Service; import com.baomidou.mybatisplus.core.metadata.IPage; import org.springframework.transaction.annotation.Transactional; /** * 邮件配置 服务实现类 * * @author BladeX * @since 2024-01-18 */ @Service public class EmailServiceImpl extends BaseServiceImpl<EmailMapper, EmailEntity> implements IEmailService { @Override public IPage<EmailVO> selectEmailPage(IPage<EmailVO> page, EmailVO email) { return page.setRecords(baseMapper.selectEmailPage(page, email)); } @Override @Transactional(rollbackFor = Exception.class) public boolean enable(Long id) { // 先禁用 boolean temp1 = this.update(Wrappers.<EmailEntity>update().lambda().set(EmailEntity::getStatus, 1)); // 在启用 boolean temp2 = this.update(Wrappers.<EmailEntity>update().lambda().set(EmailEntity::getStatus, 2).eq(EmailEntity::getId, id)); return temp1 && temp2; } } src/main/java/org/springblade/modules/email/vo/EmailVO.java
New file @@ -0,0 +1,37 @@ /* * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: Chill 庄骞 (smallchill@163.com) */ package org.springblade.modules.email.vo; import org.springblade.modules.email.entity.EmailEntity; import org.springblade.core.tool.node.INode; import lombok.Data; import lombok.EqualsAndHashCode; /** * 邮件配置 视图实体类 * * @author BladeX * @since 2024-01-18 */ @Data @EqualsAndHashCode(callSuper = true) public class EmailVO extends EmailEntity { private static final long serialVersionUID = 1L; private String statusName; } src/main/java/org/springblade/modules/email/wrapper/EmailWrapper.java
New file @@ -0,0 +1,53 @@ /* * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: Chill 庄骞 (smallchill@163.com) */ package org.springblade.modules.email.wrapper; import org.springblade.common.cache.DictCache; import org.springblade.common.enums.DictEnum; import org.springblade.core.mp.support.BaseEntityWrapper; import org.springblade.core.tool.utils.BeanUtil; import org.springblade.modules.email.entity.EmailEntity; import org.springblade.modules.email.vo.EmailVO; import java.util.Objects; /** * 邮件配置 包装类,返回视图层所需的字段 * * @author BladeX * @since 2024-01-18 */ public class EmailWrapper extends BaseEntityWrapper<EmailEntity, EmailVO> { public static EmailWrapper build() { return new EmailWrapper(); } @Override public EmailVO entityVO(EmailEntity email) { EmailVO emailVO = Objects.requireNonNull(BeanUtil.copy(email, EmailVO.class)); //User createUser = UserCache.getUser(email.getCreateUser()); //User updateUser = UserCache.getUser(email.getUpdateUser()); //emailVO.setCreateUserName(createUser.getName()); //emailVO.setUpdateUserName(updateUser.getName()); String statusName = DictCache.getValue(DictEnum.YES_NO, email.getStatus()); emailVO.setStatusName(statusName); return emailVO; } } src/main/resources/application.yml
@@ -48,6 +48,26 @@ session-stat-max-count: 10 main: allow-circular-references: true #邮件配置 mail: # 配置 SMTP 服务器地址 host: smtp.qq.com # 发送者邮箱 username: 503*****@qq.com # 配置密码,注意不是真正的密码,而是刚刚申请到的授权码 password: apbpxb****bwtbiha # 端口号465或587 port: 587 # 默认的邮件编码为UTF-8 default-encoding: UTF-8 # 配置SSL 加密工厂 properties: mail: smtp: socketFactoryClass: javax.net.ssl.SSLSocketFactory #表示开启 DEBUG 模式,这样,邮件发送过程的日志会在控制台打印出来,方便排查错误 debug: true protocol: smtps # jackson: # date-format: yyyy-MM-dd HH:mm:ss