1.所需要工具类
1.1ExcelUtil工具
package com.finlabtech.pinjamancepatanalyse.util; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * excel util */ public class ExcelUtil implements Closeable { private final Logger logger = LoggerFactory.getLogger(ExcelUtil.class); private Workbook workbook; private Sheet sheet; private CellStyle[] cellStyles; private LinkedHashMap<String, String> propertyMapping; private Map<String, Short> map; private PropertyDescriptor[] propertyDescriptors; /** * @param propertyMapping LinkedHashMap:对象属性和excel表头对应,对象属性为key,表头中文名为value,map.put的顺序即为excel列的顺序 * @return */ public ExcelUtil setPropertyMapping(LinkedHashMap<String, String> propertyMapping){ this.propertyMapping = propertyMapping; this.map = new HashMap<String, Short>(); Short i = 0; for(String e : propertyMapping.keySet()){ this.map.put(e, i++); } return this; } /** * 创建一个工作簿 * @param <T> * @return this */ public <T> ExcelUtil createWorkbook() { workbook = new XSSFWorkbook(); return this; } /**导出 * @param sheetName sheet * @param datas datas * @param clazz clazz * @return this */ public <T> void createExcel(String sheetName, List<?> datas, Class<T> clazz) { setPropertyDescriptors(clazz); //初始化 样式数组大小 cellStyles = new CellStyle[propertyMapping.size()]; sheet = workbook.createSheet(sheetName); //写表头按照map 的顺序 Row titleRow = sheet.createRow(0); // 头 int titleCellIndex = 0; for (String titleName : propertyMapping.values()) { Cell cell = titleRow.createCell(titleCellIndex); cellStyles[titleCellIndex] = workbook.createCellStyle(); cell.setCellValue(titleName); formatHeadCell(cell, 0, titleCellIndex++); } //数据 if(datas != null && !datas.isEmpty()) { for (int i = 0; i < datas.size(); i++) { Object dataObj = datas.get(i); Row row = sheet.createRow(i + 1); Object[] fields = propertyMapping.keySet().toArray(); for(int j = 0; j < fields.length; j++){ Cell cell = row.createCell(j); for (int p =0 ; p < propertyDescriptors.length ; p++) { String name = propertyDescriptors[p].getName(); if(fields[j].toString().equals(name)){ Method method = propertyDescriptors[p].getReadMethod(); Object value = null; try { value = method.invoke(dataObj, null); } catch (Exception e) { logger.error(e.getMessage(),e); } if(value == null) { cell.setCellValue(""); }else { if(value instanceof String) { cell.setCellValue((String)value); }else if(value instanceof Integer) { cell.setCellValue((double)value); } } formatContentCell(cell, i, j,value); } } } } } } /** * 设置属性描述器 * @param c * @param <T> */ private <T> void setPropertyDescriptors(Class<T> c) { BeanInfo beaninfo; try { beaninfo = Introspector.getBeanInfo(c); this.propertyDescriptors = beaninfo.getPropertyDescriptors(); } catch (IntrospectionException e) { logger.error(e.getMessage(),e); } } /** * 将工作簿转成字节数组 * @return byte[] */ public byte[] toByteArray() { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { workbook.write(out); } catch (IOException e) { logger.error("[ExcelUtil] 关流异常", e); } finally{ try { workbook.close(); out.close(); } catch (IOException e) { logger.error("[ExcelUtil] 关流异常", e); } } return out.toByteArray(); } /** * 需要子类实现抽象方法,目的每个导出所需样式 可个性化配置,excel主体的样式 */ protected void formatContentCell(Cell cell, int rowIndex, int colIndex,Object value) { if(value == null) { cell.setCellValue(""); }else { // 自适应宽度 int cellLength = value.toString().getBytes().length; // excel有列宽限制的 255字符 if (cellLength > 125) { cellLength = 125; } else if (cellLength < 10) { cellLength = 10; } sheet.setColumnWidth(colIndex, cellLength * 2 * 256); } cell.setCellStyle(cellStyles[colIndex]); } /** * 需要子类实现抽象方法,目的每个导出所需样式 可 个性化配置,excel主体的样式 */ protected void formatHeadCell(Cell cell, int rowIndex, int colIndex) { sheet.setColumnWidth(colIndex, 10 * 2 * 256); setGeneralProperty(cellStyles[colIndex]); cell.setCellStyle(cellStyles[colIndex]); } /** * 功能描述:设置EXCEL表格基本样式 */ protected void setGeneralProperty(CellStyle style) { style.setBorderBottom(BorderStyle.THIN); style.setBottomBorderColor(HSSFColor.BLACK.index); style.setBorderLeft(BorderStyle.THIN); style.setLeftBorderColor(HSSFColor.BLACK.index); style.setBorderRight(BorderStyle.THIN); style.setRightBorderColor(HSSFColor.BLACK.index); style.setBorderTop(BorderStyle.THIN); style.setTopBorderColor(HSSFColor.BLACK.index); style.setAlignment(HorizontalAlignment.CENTER); } @Override public void close(){ try { if (workbook != null) { workbook.close(); } } catch (IOException e) { logger.error("[ExcelUtil] 关流异常"); } } } |
1.2MailUtil工具
package com.finlabtech.pinjamancepatanalyse.util; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import javax.mail.internet.MimeMessage; import java.io.Serializable; @Slf4j public class MailUtil implements Serializable { /** * 发送一个支持html 和 附件 的邮件 * @param param Sender,Subject,Receiver,ContentText,AttachmentFileName,Attachment必选,Copier选填 */ public static void sendHtmlAndAttachmentMail(SendMailParam param){ JavaMailSender mailSender = ApplicationContextHolder.getBean("sysMailSender"); MimeMessage mimeMessage; try { mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true); helper.setFrom(param.getSender()); helper.setSubject(param.getSubject()); helper.setTo(param.getReceiver()); if (ArrayUtils.isNotEmpty(param.getCopier())) { helper.setCc(param.getCopier()); } helper.setText(param.getContentText(),Boolean.TRUE); helper.addAttachment(param.getAttachmentFileName(), param.getInputStreamSource()); mailSender.send(mimeMessage); } catch (Exception e) { log.error("[MailUtilSendMail] 邮件发送失败。", e); } } } |
1.3SendMailParam
package com.finlabtech.pinjamancepatanalyse.util; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.core.io.InputStreamSource; import java.io.Serializable; @Data @NoArgsConstructor public class SendMailParam implements Serializable { private static final long serialVersionUID = -1L; private String subject; private String sender; private String[] receiver; private String[] copier; private String contentText; private String attachmentFileName; private InputStreamSource inputStreamSource; /** * 附件邮件参数 * @param subject 主题 * @param sender 发件人 * @param receiver 收件人 * @param copier 抄送人 * @param contentText 内容 * @param attachmentFileName 附件名称 * @param inputStreamSource 附件流 */ public SendMailParam(String subject, String sender, String[] receiver, String[] copier, String contentText, String attachmentFileName, InputStreamSource inputStreamSource) { this.subject = subject; this.sender = sender; this.receiver = receiver; this.copier = copier; this.contentText = contentText; this.attachmentFileName = attachmentFileName; this.inputStreamSource = inputStreamSource; } } |
1.4需要写自己的对应实体类
eg:
package com.finlabtech.pinjamancepatanalyse.model.analyse; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data @NoArgsConstructor public class BillData implements Serializable { private static final long serialVersionUID = -1L; private String payId; private String amount; private String tradeTime; private String bankCode; private String accountNumber; private String userId; private String orderId; private String type; private String diff; private String differenceBelong; private String payStatus; private String status; private String channel; } |
1.5邮件的config配置
package com.finlabtech.pinjamancepatanalyse.config.mail; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSenderImpl; import java.util.Properties; @Configuration public class MailConfig { @Value("${sys.mail.from}") private String username; @Value("${sys.mail.host}") private String host; @Value("${sys.mail.port}") private int port; @Value("${sys.mail.password}") private String password; @Bean(name = "sysMailSender") public JavaMailSender getSender(){ JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); javaMailSender.setUsername(username); javaMailSender.setHost(host); javaMailSender.setPort(port); javaMailSender.setDefaultEncoding("UTF-8"); javaMailSender.setPassword(password); javaMailSender.setProtocol("smtp"); Properties prop = new Properties() ; prop.put("mail.smtp.auth", "true") ; prop.put("mail.smtp.timeout", "25000") ; javaMailSender.setJavaMailProperties(prop); return javaMailSender; } } |
1.6properties配置
sys.mail.from=system@*.com sys.mail.host=smtp.exmail.qq.com sys.mail.password=* sys.mail.port=25 sys.mail.receiver=**.*@*.com,***.*@*.com |
1.7ApplicationContextHolder调用
package com.finlabtech.pinjamancepatanalyse.util; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class ApplicationContextHolder implements ApplicationContextAware { /** * Spring应用上下文环境 */ private static ApplicationContext applicationContext; /** * 实现ApplicationContextAware接口的回调方法,设置上下文环境. * * @param applicationContext Spring ApplicationContext上下文 */ @Override public void setApplicationContext(ApplicationContext applicationContext) { ApplicationContextHolder.applicationContext = applicationContext; } /** * 获取一个ApplicationContext. * * @return ApplicationContext */ public static ApplicationContext getApplicationContext() { return applicationContext; } /** * 根据名称获取一个对象. * * @param name bean名称 * @return Object 指定的bean * @throws BeansException 如果找不到bean */ public static <T> T getBean(String name) throws BeansException { return (T) applicationContext.getBean(name); } /** * 获取名称为name的bean,自动转为所需类型. * * @param <T> 需求的bean类型 * @param name bean名称 * @param requiredType 需求的bean类型 * @return 指定类型的bean * @throws BeansException 如果找不到匹配的类型,或是类型不能被转换,或是bean实例化失败 */ public static <T> T getBean(String name, Class<T> requiredType) throws BeansException { return applicationContext.getBean(name, requiredType); } /** * 获取类型为requiredType的对象. * * @param <T> 需求的bean类型 * @param requiredType 需求的bean类型 * @return 指定类型的bean * @throws BeansException 如果找不到匹配的类型 */ public static <T> T getBean(Class<T> requiredType) throws BeansException { return applicationContext.getBean(requiredType); } /** * 检测一个bean是否已经被定义. * * @param name bean名称 * @return boolean 如果bean已经被定义,则返回true,否则返回false */ public static boolean containsBean(String name) { return applicationContext.containsBean(name); } /** * 判断以给定名字注册的bean定义是一个singleton还是一个prototype. * * @param name bean名称 * @return boolean 如果是singleton则返回true * @throws NoSuchBeanDefinitionException 如果bean名称不存在 */ public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { return applicationContext.isSingleton(name); } /** * 获取给定名字的bean的类型. * * @param name bean名称 * @return Class bean类型 * @throws NoSuchBeanDefinitionException 如果bean名称不存在 */ public static Class getType(String name) throws NoSuchBeanDefinitionException { return applicationContext.getType(name); } /** * 取出指定bean的别名列表. * * @param name bean名称 * @return 如果有别名,返回别名,否则返回空数组. * @throws NoSuchBeanDefinitionException 如果bean名称不存在 */ public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { return applicationContext.getAliases(name); } } |
2.例子
List<BillData> billData = new LinkedList<>(); for (TradeCheck tradeCheck : weTradeChecks) { BillData check = new BillData(); check.setPayId(tradeCheck.getPayId()); check.setAccountNumber(tradeCheck.getAccountNumber()); check.setAmount(tradeCheck.getAmount().toString()); check.setBankCode(tradeCheck.getBankCode()); check.setChannel(tradeCheck.getChannel().equals(1) ? "xendit" : "其他"); check.setOrderId(tradeCheck.getOrderId().toString()); check.setPayStatus(tradeCheck.getPayStatus()); String diff; switch (tradeCheck.getDiff()) { case 0: diff = "我方单边"; break; case 1: diff = "Xendit单边"; break; case 2: diff = "双方差异"; break; case 3: diff = "无差异"; break; default: diff = "其他"; } check.setDiff(diff); String belong; switch (tradeCheck.getDifferenceBelong()) { case 0: belong = "没有差异"; break; case 1: belong = "我方记录"; break; case 2: belong = "对方记录"; break; default: belong = "其他"; } check.setDifferenceBelong(belong); check.setStatus(tradeCheck.getStatus().equals(1) ? "已处理" : "未处理"); check.setTradeTime(tradeCheck.getTradeTime().toString()); check.setType(tradeCheck.getType() == 1 ? "放款" : "还款"); check.setUserId(tradeCheck.getUserId().toString()); billData.add(check); } int noDiff = noTradeChecks.size(); String content = "对账后差异总笔数=" + weTradeChecks.size() + ",一致笔数=" + noDiff + ",部分差异数据如下,该天的对账差异数据在附件中,请及时查看并处理"; SendMailParam param = new SendMailParam(); param.setReceiver(receiver.split(",")); param.setSender(username); param.setSubject("Finlabtech-Xendit贷款/还款资金对账差异_" + paramMap.get("start") + "~" + paramMap.get("end")); param.setContentText(content); param.setAttachmentFileName("对账结果.xlsx"); ExcelUtil excel = null; try { excel = new ExcelUtil(); excel.createWorkbook(); excel.setPropertyMapping(getFeedbackMap()).createExcel("对账详情", billData, BillData.class); param.setInputStreamSource(new ByteArrayResource(excel.toByteArray())); MailUtil.sendHtmlAndAttachmentMail(param); } catch (Exception e) { log.error("对账邮件异常 e={}", e); } finally { if (excel != null) { excel.close(); } } } private LinkedHashMap<String, String> getFeedbackMap() { LinkedHashMap<String, String> param = new LinkedHashMap<>(); param.put("payId", "支付id(还/放)"); param.put("amount", "交易金额"); param.put("tradeTime", "交易时间(放/还)"); param.put("bankCode", "银行代码"); param.put("accountNumber", "用户银行账号"); param.put("userId", "用户id"); param.put("orderId", "订单id"); param.put("type", "交易类型"); param.put("diff", "对账差异"); param.put("differenceBelong", "差异归属"); param.put("payStatus", "交易状态"); param.put("status", "对账处理状态"); param.put("channel", "渠道"); return param; } |