需求背景
实现功能:
需求分析:
项目技术架构选型描述
代码过程展示
1、build.gradle 文件 的dependencies { }中先导包,如果你使用的是Maven原理也是一样,可以去 Maven中央仓库搜索你想要的包的版本的Maven写法,在此就不再赘述
//poi
implementation('org.apache.poi:poi:3.16')
implementation('org.apache.poi:poi-ooxml:3.14')
implementation('org.apache.poi:poi-scratchpad:3.15')
//email
implementation('org.springframework:spring-context-support')
implementation('javax.mail:javax.mail-api:1.5.1')
implementation('com.sun.mail:javax.mail:1.5.4')
2、yml文件内配置邮箱相关参数
email:
host: smtpdm.aliyun.com
port: 80
senderMail: [email protected]
senderPassword: Wahaha2019
sendTo: [email protected],[email protected]
3、律师信息实体类–使用的Lombok插件通过@Data生成setter 和getter
@Data
public class LawyerAuthenticate {
/**
* 主键
* */
private Long id;
/**
* 律师姓名
* */
private String lawName;
/**
* 所属律所
* */
private String lawFirm;
/**
* 律师手机号
* */
private String phone;
/**
* 律师常用邮箱
* */
private String mail;
/**
* 律师执业证号
* */
private String licenseNumber;
/**
* 律师执业证照片名字
* */
private String licenseName;
/**
* 律师执业证照片url
* */
private String licenseUrl;
/**
* 身份证照片名字
* */
private String idcardName;
/**
* 身份证照片url
* */
private String idcardUrl;
/**
* 创建日期
* */
private Date createDate;
/**
* 起始时间--不存库
* */
private Date startTime;
/**
* 截止时间--不存库
* */
private Date endTime;
}
4、日期工具类–在本功能内仅仅用到了:nextDay(int num) 这个方法,其他日期常用方法,为了篇幅的简洁,故在此就不一一列举了
/**
* 日期相关工具
*/
public class DateUtils {
/**
* 获取当前日期指定天数之后的日期.
* @param num 相隔天数
* @return Date 日期
* @since 1.0
*/
public static Date nextDay(int num) {
Calendar curr = Calendar.getInstance();
curr.set(Calendar.DAY_OF_MONTH, curr.get(Calendar.DAY_OF_MONTH) + num);
return curr.getTime();
}
}
4、定时任务脚本代码
*注意该接口类下的方法,为本次需求的核心方法:sendMailLawyerAuthService.sendMail();
@Slf4j
@Component
public class SendMailOfLawyerAuthJob {
@Autowired
private SendMailLawyerAuthService sendMailLawyerAuthService;
@Autowired
private RedisTemplate redisTemplate;
@Scheduled(cron = "0 0 10 ? * TUE")
protected synchronized void executeInternal() throws InterruptedException {
Thread.sleep((int) (Math.random() * 8000));
Object judgementCount = this.getJudgementCount();
if (null == judgementCount) {
// 分布式锁
redisTemplate.execute(new RedisCallback
5、核心方法sendMail() 代码示例
/**发送邮件核心方法*/
public interface SendMailLawyerAuthService {
void sendMail() throws IOException;
}
实现类方法—此处非常重要,此处非常重要,此处非常重要(重要的是说三遍!)
/**
* Created by 陈二狗 on 2019-09-18.
* Desc: 新版律师信息汇总定期发送邮件
*/
@Service
public class SendMailLawyerAuthServiceImpl implements SendMailLawyerAuthService {
@Autowired
private LawyerAuthenticateMapper lawyerAuthenticateMapper;
private static final Logger log = LoggerFactory.getLogger(SendMailLawyerAuthServiceImpl.class);
@Value("${email.host}")
private String emailHost;
@Value("${email.port}")
private int emailPort;
@Value("${email.senderMail}")
private String senderMail;
@Value("${email.senderPassword}")
private String senderPassword;
@Value("${email.sendTo}")
private List sendTo;
/**发送邮件的附件标题后缀*/
private static final String FILENAME_END_PREFIX = "新版律师信息汇总.xls";
/**
* 发送带Excel附件的阿里企业邮件
* @throws IOException
*/
@Override
public void sendMail() throws IOException {
//获取当前日期的前一周日期
Date startTime = DateUtils.nextDay(-7);
Date endTime = new Date();
//首先查库获取周期范围内的数据信息
List lists = lawyerAuthenticateMapper.sendMailOfLawyerAuth(startTime, endTime);
if (CollectionUtils.isEmpty(lists)) {
log.info("没有需要发送的认证律师相关的邮件!");
return ;
}
//1、调用创建邮件对象辅助方法
Map resultMap = createSendMailHelper();
Properties props = (Properties)resultMap.get("props");
Authenticator authenticator = (Authenticator)resultMap.get("authenticator");
Session session = Session.getInstance(props,authenticator);
MimeMessage message = new MimeMessage(session);
try {
/**
* 2、设置发件人
* 其中 InternetAddress 的三个参数分别为: 邮箱, 显示的昵称(只用于显示, 没有特别的要求), 昵称的字符集编码
*/
message.setFrom(new InternetAddress(senderMail, "wusong", "UTF-8"));
/**
* 3、设置收件人--支持多个,基于收件人肯定存在的情况下哈,因为发邮件如果一个收件人都没有,我·····
* To收件人 CC 抄送 BCC密送
*/
if (sendTo.size() > 0) {
InternetAddress[] sendToInfo = new InternetAddress[sendTo.size()];
for (int i = 0; i < sendTo.size(); i++) {
sendToInfo[i] = new InternetAddress(sendTo.get(i), "", "UTF-8");
}
message.addRecipients(MimeMessage.RecipientType.TO, sendToInfo);
}
/**
* 4、设置标题
*/
log.info("导出到Excel");
message.setSubject("新版律师认证信息汇总表","UTF-8");
HSSFWorkbook workbook = new HSSFWorkbook();
CreationHelper helper = workbook.getCreationHelper();
HSSFSheet sheet = workbook.createSheet("新版律师认证信息汇总表");
//调用设置文件以及填充对象数据方法,具象模板文件
setFileFromLawyerInfolHelper(sheet, lists);
/****workBook写入输出流******/
log.info("workBook写入输出流");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
workbook.write(baos);
baos.flush();
baos.close();
DataSource fds = new ByteArrayDataSource(baos.toByteArray(), "application/vnd.ms-excel");
MimeBodyPart mbp1 = new MimeBodyPart();
//此处设置的是邮件内容的body部分,可以对比效果图查看
mbp1.setText("你好:\n 律师信息邮件汇总,请注意查收!");
MimeBodyPart mbp2 = new MimeBodyPart();
mbp2.setDataHandler(new DataHandler(fds));
//5、调用格式化日期方法,生成附件标题
String fileNameStr = formatDateHelper();
//Javamail源码中MimeBodyPart在setFileName 时候会对文件名进行强制转码,So,针对此进行强制设置编码,避免出现乱码现象
mbp2.setFileName(new String(fileNameStr.getBytes(), "ISO8859-1"));
Multipart mp = new MimeMultipart();
mp.addBodyPart(mbp1);
mp.addBodyPart(mbp2);
message.setContent(mp);
/**
* 6、保存邮件并发送
*/
log.info("保存邮件并发送");
message.saveChanges();
Transport transport = session.getTransport("smtp");
transport.connect(emailHost, senderMail, senderPassword);
transport.sendMessage(message,message.getAllRecipients());
transport.close();
} catch (MessagingException e) {
log.error("异常信息为:"+e.getMessage());
} catch (UnsupportedEncodingException e) {
log.error("异常信息为:"+e.getMessage());
}
}
/**
*
* 创建邮件相关对象辅助方法-001
* */
private Map createSendMailHelper(){
Properties props = new Properties();
/**表示SMTP发送邮件,需要进行身份验证*/
props.put("mail.smtp.auth", "true");
/**是否启用调试*/
props.put("mail.debug", "false");
/**设置链接超时*/
props.put("mail.smtp.timeout", "50000");
/**设置端口*/
props.put("mail.smtp.port", Integer.toString(emailPort));
/**设置ssl端口*/
props.put("mail.smtp.socketFactory.port", Integer.toString(emailPort));
props.put("mail.smtp.socketFactory.fallback", "false");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
// 发件人的账号
props.put("mail.user", senderMail);
// 访问SMTP服务时需要提供的密码(邮箱密码)
props.put("mail.password", senderPassword);
// 构建授权信息,用于进行SMTP进行身份验证
Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
// 用户名、密码
String userName = props.getProperty("mail.user");
String password = props.getProperty("mail.password");
return new PasswordAuthentication(userName, password);
}
};
//封装返回对象参数数据
Map mailMap = new HashMap<>();
mailMap.put("props",props);
mailMap.put("authenticator",authenticator);
return mailMap;
}
/**
* 设置文件以及填充数据辅助方法-002
* */
private void setFileFromLawyerInfolHelper(HSSFSheet sheet, List lists){
//设置列宽度
sheet.setColumnWidth(0, 20 * 256);
sheet.setColumnWidth(1, 20 * 256);
sheet.setColumnWidth(2, 20 * 256);
sheet.setColumnWidth(3, 20 * 256);
sheet.setColumnWidth(4, 20 * 256);
sheet.setColumnWidth(5, 20 * 256);
sheet.setColumnWidth(6, 80 * 256);
sheet.setColumnWidth(7, 80 * 256);
//新增数据行,并且设置单元格数据
int rowNum = 1;
String[] headers = { "序号","律师姓名","手机号","所属律所","邮箱","执业证号","律师执照照片","手持身份证照片"};
//headers表示excel表中第一行的表头
HSSFRow row = sheet.createRow(0);
//在excel表中添加表头
for(int i=0;i
邮件发送代码说明:
1)附件主题乱码,通过读取源码的setFilename()方法可以看到,源码底层对字符串进行了转码,所以我们在代码中,进行了处理,需要注意的是,不同的邮箱可能处理的方式不尽相同,此处我这里使用的是阿里企业邮箱,以下为我的处理方式:
String fileNameStr = formatDateHelper();
//Javamail源码中MimeBodyPart在setFileName 时候会对文件名进行强制转码,So,针对此进行强制设置编码,避免出现乱码现象
mbp2.setFileName(new String(fileNameStr.getBytes(), "ISO8859-1"));
网上很多资料说的方式是在发送邮件之前,添加修改编码代码,如果大家感兴趣可以自行百度,此处不再过多赘述;
2)附件的excel 内各列的宽度可以自定义设置调整,各位看官根据实际需要变更即可
附上效果图:红色1:发件人;红色2:多个收件人
附件详情效果图: