|
@@ -14,6 +14,7 @@ import com.simuwang.base.common.util.StringUtil;
|
|
import com.simuwang.base.pojo.dto.EmailContentInfoDTO;
|
|
import com.simuwang.base.pojo.dto.EmailContentInfoDTO;
|
|
import com.simuwang.base.pojo.dto.EmailFundNavDTO;
|
|
import com.simuwang.base.pojo.dto.EmailFundNavDTO;
|
|
import com.simuwang.base.pojo.dto.FieldPositionDTO;
|
|
import com.simuwang.base.pojo.dto.FieldPositionDTO;
|
|
|
|
+import com.simuwang.base.pojo.dto.TemplateDetailDTO;
|
|
import org.apache.pdfbox.Loader;
|
|
import org.apache.pdfbox.Loader;
|
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
|
import org.apache.poi.ss.usermodel.Cell;
|
|
import org.apache.poi.ss.usermodel.Cell;
|
|
@@ -53,14 +54,19 @@ public class NavEmailParser extends AbstractEmailParser {
|
|
@Value("${email.file.path}")
|
|
@Value("${email.file.path}")
|
|
private String path;
|
|
private String path;
|
|
|
|
|
|
|
|
+ @Value("${email.force-template.enable}")
|
|
|
|
+ private boolean forceTemplateEnable;
|
|
|
|
+
|
|
private final ValuationEmailParser valuationEmailParser;
|
|
private final ValuationEmailParser valuationEmailParser;
|
|
|
|
+ private final EmailTemplateService emailTemplateService;
|
|
|
|
|
|
private static final Integer ROW_DIRECTION_TYPE = 1;
|
|
private static final Integer ROW_DIRECTION_TYPE = 1;
|
|
private static final Integer COLUMN_DIRECTION_TYPE = 2;
|
|
private static final Integer COLUMN_DIRECTION_TYPE = 2;
|
|
private static final int MAX_COLUMN = 20;
|
|
private static final int MAX_COLUMN = 20;
|
|
|
|
|
|
- public NavEmailParser(ValuationEmailParser valuationEmailParser) {
|
|
|
|
|
|
+ public NavEmailParser(ValuationEmailParser valuationEmailParser, EmailTemplateService emailTemplateService) {
|
|
this.valuationEmailParser = valuationEmailParser;
|
|
this.valuationEmailParser = valuationEmailParser;
|
|
|
|
+ this.emailTemplateService = emailTemplateService;
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -94,10 +100,7 @@ public class NavEmailParser extends AbstractEmailParser {
|
|
Optional.ofNullable(fundNavDTOList).ifPresent(emailFundNavDTOList::addAll);
|
|
Optional.ofNullable(fundNavDTOList).ifPresent(emailFundNavDTOList::addAll);
|
|
}
|
|
}
|
|
|
|
|
|
- // 校验净值数据格式
|
|
|
|
- if (CollUtil.isNotEmpty(emailFundNavDTOList)) {
|
|
|
|
- emailFundNavDTOList = emailFundNavDTOList.stream().filter(super::dataFormat).collect(Collectors.toList());
|
|
|
|
- }
|
|
|
|
|
|
+ // 兼容净值邮件,但附件是估值表的情况
|
|
if (CollUtil.isEmpty(emailFundNavDTOList)) {
|
|
if (CollUtil.isEmpty(emailFundNavDTOList)) {
|
|
// 判断文件名是否包含"估值表"
|
|
// 判断文件名是否包含"估值表"
|
|
String fileName = emailContentInfoDTO.getFileName();
|
|
String fileName = emailContentInfoDTO.getFileName();
|
|
@@ -105,11 +108,16 @@ public class NavEmailParser extends AbstractEmailParser {
|
|
emailFundNavDTOList = valuationEmailParser.parse(emailContentInfoDTO, emailFieldMap);
|
|
emailFundNavDTOList = valuationEmailParser.parse(emailContentInfoDTO, emailFieldMap);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // 通用模版解析 -> 根据配置的模板进行解析
|
|
|
|
+ if (CollUtil.isEmpty(emailFundNavDTOList) || forceTemplateEnable) {
|
|
|
|
+ emailFundNavDTOList = parseUsingTemplate(emailContentInfoDTO);
|
|
|
|
+ }
|
|
return emailFundNavDTOList;
|
|
return emailFundNavDTOList;
|
|
}
|
|
}
|
|
|
|
|
|
private List<EmailFundNavDTO> parsePackageFile(EmailContentInfoDTO emailContentInfoDTO, String fileName, String filePath, Map<String, List<String>> emailFieldMap) {
|
|
private List<EmailFundNavDTO> parsePackageFile(EmailContentInfoDTO emailContentInfoDTO, String fileName, String filePath, Map<String, List<String>> emailFieldMap) {
|
|
- String destPath = filePath.substring(0, filePath.indexOf(fileName)) + fileName.replaceAll(".zip", "").replaceAll(".ZIP", "");
|
|
|
|
|
|
+ String destPath = filePath.replaceAll(".zip", "").replaceAll(".ZIP", "");
|
|
log.info("压缩包地址:{},解压后文件地址:{}", filePath, destPath);
|
|
log.info("压缩包地址:{},解压后文件地址:{}", filePath, destPath);
|
|
List<String> dir = ExcelUtil.extractCompressedFiles(filePath, destPath);
|
|
List<String> dir = ExcelUtil.extractCompressedFiles(filePath, destPath);
|
|
List<EmailFundNavDTO> emailFundNavDTOList = CollUtil.newArrayList();
|
|
List<EmailFundNavDTO> emailFundNavDTOList = CollUtil.newArrayList();
|
|
@@ -141,15 +149,16 @@ public class NavEmailParser extends AbstractEmailParser {
|
|
return fundNavDTOList;
|
|
return fundNavDTOList;
|
|
}
|
|
}
|
|
|
|
|
|
- /**
|
|
|
|
- * 解析邮件pdf附件
|
|
|
|
- *
|
|
|
|
- * @param filePath 邮件pdf附件路径
|
|
|
|
- * @param excelFilePath pdf转excel路径
|
|
|
|
- * @param emailFieldMap 邮件字段识别规则映射表
|
|
|
|
- * @return 解析到的净值数据
|
|
|
|
- */
|
|
|
|
|
|
+
|
|
private List<EmailFundNavDTO> parsePdfFile(String filePath, String excelFilePath, Map<String, List<String>> emailFieldMap) {
|
|
private List<EmailFundNavDTO> parsePdfFile(String filePath, String excelFilePath, Map<String, List<String>> emailFieldMap) {
|
|
|
|
+ excelFilePath = pdfConvertToExcel(filePath, excelFilePath);
|
|
|
|
+ if (StrUtil.isBlank(excelFilePath)) {
|
|
|
|
+ return CollUtil.newArrayList();
|
|
|
|
+ }
|
|
|
|
+ return parseExcelFile(excelFilePath, emailFieldMap);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private String pdfConvertToExcel(String filePath, String excelFilePath) {
|
|
File savefile = new File(excelFilePath);
|
|
File savefile = new File(excelFilePath);
|
|
if (!savefile.exists()) {
|
|
if (!savefile.exists()) {
|
|
if (!savefile.getParentFile().exists()) {
|
|
if (!savefile.getParentFile().exists()) {
|
|
@@ -184,8 +193,9 @@ public class NavEmailParser extends AbstractEmailParser {
|
|
workbook.write(outputStream);
|
|
workbook.write(outputStream);
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
log.error("解析邮件pdf附件报错 -> 堆栈信息:{}", ExceptionUtil.stacktraceToString(e));
|
|
log.error("解析邮件pdf附件报错 -> 堆栈信息:{}", ExceptionUtil.stacktraceToString(e));
|
|
|
|
+ return null;
|
|
}
|
|
}
|
|
- return parseExcelFile(excelFilePath, emailFieldMap);
|
|
|
|
|
|
+ return excelFilePath;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -196,8 +206,7 @@ public class NavEmailParser extends AbstractEmailParser {
|
|
* @return 解析到的净值数据
|
|
* @return 解析到的净值数据
|
|
*/
|
|
*/
|
|
private List<EmailFundNavDTO> parseExcelFile(String filePath, Map<String, List<String>> emailFieldMap) {
|
|
private List<EmailFundNavDTO> parseExcelFile(String filePath, Map<String, List<String>> emailFieldMap) {
|
|
- File file = new File(filePath);
|
|
|
|
- Sheet sheet = ExcelUtil.getSheet(file, 0);
|
|
|
|
|
|
+ Sheet sheet = ExcelUtil.getFirstSheet(filePath);
|
|
// 1.找到表头所在位置
|
|
// 1.找到表头所在位置
|
|
Map<String, Pair<Integer, Integer>> fieldPositionMap = getFieldPosition(sheet, emailFieldMap);
|
|
Map<String, Pair<Integer, Integer>> fieldPositionMap = getFieldPosition(sheet, emailFieldMap);
|
|
if (MapUtil.isEmpty(fieldPositionMap)) {
|
|
if (MapUtil.isEmpty(fieldPositionMap)) {
|
|
@@ -205,7 +214,12 @@ public class NavEmailParser extends AbstractEmailParser {
|
|
return CollUtil.newArrayList();
|
|
return CollUtil.newArrayList();
|
|
}
|
|
}
|
|
// 2.解析sheet中的净值数据
|
|
// 2.解析sheet中的净值数据
|
|
- return parseSheetData(sheet, fieldPositionMap);
|
|
|
|
|
|
+ List<EmailFundNavDTO> emailFundNavDTOList = parseSheetData(sheet, fieldPositionMap, null);
|
|
|
|
+ // 3.校验净值数据格式
|
|
|
|
+ if (CollUtil.isNotEmpty(emailFundNavDTOList)) {
|
|
|
|
+ emailFundNavDTOList = emailFundNavDTOList.stream().filter(super::dataFormat).collect(Collectors.toList());
|
|
|
|
+ }
|
|
|
|
+ return emailFundNavDTOList;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -217,7 +231,15 @@ public class NavEmailParser extends AbstractEmailParser {
|
|
* @return 解析到的净值数据
|
|
* @return 解析到的净值数据
|
|
*/
|
|
*/
|
|
private List<EmailFundNavDTO> parseEmailContent(EmailContentInfoDTO emailContentInfoDTO, String emailContent, Map<String, List<String>> emailFieldMap) {
|
|
private List<EmailFundNavDTO> parseEmailContent(EmailContentInfoDTO emailContentInfoDTO, String emailContent, Map<String, List<String>> emailFieldMap) {
|
|
- Document doc = Jsoup.parse(emailContent);
|
|
|
|
|
|
+ String excelFilePath = contentConvertToExcel(emailContentInfoDTO);
|
|
|
|
+ if (StrUtil.isBlank(excelFilePath)) {
|
|
|
|
+ return CollUtil.newArrayList();
|
|
|
|
+ }
|
|
|
|
+ return parseExcelFile(excelFilePath, emailFieldMap);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public String contentConvertToExcel(EmailContentInfoDTO emailContentInfoDTO) {
|
|
|
|
+ Document doc = Jsoup.parse(emailContentInfoDTO.getEmailContent());
|
|
Element table = doc.select("table").first();
|
|
Element table = doc.select("table").first();
|
|
Elements rows = table.select("tr");
|
|
Elements rows = table.select("tr");
|
|
String excelFilePath = path + "/content/" + emailContentInfoDTO.getEmailAddress() + "/" + emailContentInfoDTO.getEmailDate().substring(0, 10).replaceAll("-", "") + "/"
|
|
String excelFilePath = path + "/content/" + emailContentInfoDTO.getEmailAddress() + "/" + emailContentInfoDTO.getEmailDate().substring(0, 10).replaceAll("-", "") + "/"
|
|
@@ -238,14 +260,87 @@ public class NavEmailParser extends AbstractEmailParser {
|
|
workbook.write(outputStream);
|
|
workbook.write(outputStream);
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
log.error("解析正文报错 -> 邮件主题:{},邮件日期:{},堆栈信息:{}", emailContentInfoDTO.getEmailTitle(), emailContentInfoDTO.getEmailDate(), ExceptionUtil.stacktraceToString(e));
|
|
log.error("解析正文报错 -> 邮件主题:{},邮件日期:{},堆栈信息:{}", emailContentInfoDTO.getEmailTitle(), emailContentInfoDTO.getEmailDate(), ExceptionUtil.stacktraceToString(e));
|
|
|
|
+ return null;
|
|
}
|
|
}
|
|
- return parseExcelFile(excelFilePath, emailFieldMap);
|
|
|
|
|
|
+ return excelFilePath;
|
|
}
|
|
}
|
|
|
|
|
|
- private List<EmailFundNavDTO> parseSheetData(Sheet sheet, Map<String, Pair<Integer, Integer>> fieldPositionMap) {
|
|
|
|
|
|
+ private List<EmailFundNavDTO> parseUsingTemplate(EmailContentInfoDTO emailContentInfoDTO) {
|
|
|
|
+ // 考虑文件为PDF,html,zip等情况 -> 将相关文件转成Excel
|
|
|
|
+ List<String> filePathList = getRealFilePath(emailContentInfoDTO.getFilePath(), emailContentInfoDTO);
|
|
|
|
+ if (CollUtil.isEmpty(filePathList)) {
|
|
|
|
+ return CollUtil.newArrayList();
|
|
|
|
+ }
|
|
|
|
+ List<EmailFundNavDTO> emailFundNavDTOList = CollUtil.newArrayList();
|
|
|
|
+ for (String filePath : filePathList) {
|
|
|
|
+ List<TemplateDetailDTO> templateDetailDTOList = emailTemplateService.getTemplateDetail(emailContentInfoDTO, filePath);
|
|
|
|
+ if (CollUtil.isEmpty(templateDetailDTOList) || ExcelUtil.getFirstSheet(filePath) == null) {
|
|
|
|
+ return CollUtil.newArrayList();
|
|
|
|
+ }
|
|
|
|
+ // 获取文件的sheet页
|
|
|
|
+ Sheet sheet = ExcelUtil.getFirstSheet(filePath);
|
|
|
|
+ // 按照模板分别进行解析 -> 只要有一个模板解析到数据就停止
|
|
|
|
+ for (TemplateDetailDTO templateDetailDTO : templateDetailDTOList) {
|
|
|
|
+ try {
|
|
|
|
+ Integer direction = templateDetailDTO.getDirection();
|
|
|
|
+ Map<String, Pair<Integer, Integer>> fieldPositionMap = templateDetailDTO.getFieldPositionMap();
|
|
|
|
+ List<EmailFundNavDTO> fundNavDTOList = parseSheetData(sheet, fieldPositionMap, direction);
|
|
|
|
+ fundNavDTOList = fundNavDTOList.stream().filter(super::dataFormat).collect(Collectors.toList());
|
|
|
|
+ if (CollUtil.isNotEmpty(fundNavDTOList)) {
|
|
|
|
+ emailFundNavDTOList.addAll(fundNavDTOList);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("净值模板解析报错 -> 模板id:{},文件:{},堆栈信息:{}", templateDetailDTO.getTemplateId(), filePath, ExceptionUtil.stacktraceToString(e));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return emailFundNavDTOList;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private List<String> getRealFilePath(String filePath, EmailContentInfoDTO emailContentInfoDTO) {
|
|
|
|
+ List<String> filePathList = CollUtil.newArrayList();
|
|
|
|
+ if (StrUtil.isBlank(filePath)) {
|
|
|
|
+ return filePathList;
|
|
|
|
+ }
|
|
|
|
+ if (ExcelUtil.isExcel(filePath)) {
|
|
|
|
+ filePathList.add(filePath);
|
|
|
|
+ } else if (ExcelUtil.isHTML(filePath)) {
|
|
|
|
+ String excelFilePath = contentConvertToExcel(emailContentInfoDTO);
|
|
|
|
+ Optional.ofNullable(excelFilePath).ifPresent(filePathList::add);
|
|
|
|
+ } else if (ExcelUtil.isPdf(filePath)) {
|
|
|
|
+ String excelFilePath = filePath.replace(".pdf", ".xlsx").replace(".PDF", ".xlsx");
|
|
|
|
+ excelFilePath = pdfConvertToExcel(filePath, excelFilePath);
|
|
|
|
+ Optional.ofNullable(excelFilePath).ifPresent(filePathList::add);
|
|
|
|
+ } else if (ExcelUtil.isZip(filePath)) {
|
|
|
|
+ String destPath = filePath.replaceAll(".zip", "").replaceAll(".ZIP", "");
|
|
|
|
+ List<String> dir = ExcelUtil.extractCompressedFiles(filePath, destPath);
|
|
|
|
+ for (String zipFilePath : dir) {
|
|
|
|
+ File file = new File(zipFilePath);
|
|
|
|
+ if (file.isDirectory()) {
|
|
|
|
+ for (String navFilePath : Objects.requireNonNull(file.list())) {
|
|
|
|
+ Optional.ofNullable(getRealFilePath(navFilePath, emailContentInfoDTO)).ifPresent(filePathList::addAll);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ Optional.ofNullable(getRealFilePath(zipFilePath, emailContentInfoDTO)).ifPresent(filePathList::addAll);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return filePathList;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 根据字段所在表格的位置提取净值数据
|
|
|
|
+ *
|
|
|
|
+ * @param sheet 表格中的sheet页
|
|
|
|
+ * @param fieldPositionMap 字段所在表格中的位置
|
|
|
|
+ * @param direction 表格数据的形式:1-行,2-列
|
|
|
|
+ * @return 净值数据
|
|
|
|
+ */
|
|
|
|
+ private List<EmailFundNavDTO> parseSheetData(Sheet sheet, Map<String, Pair<Integer, Integer>> fieldPositionMap, Integer direction) {
|
|
List<EmailFundNavDTO> fundNavDTOList = CollUtil.newArrayList();
|
|
List<EmailFundNavDTO> fundNavDTOList = CollUtil.newArrayList();
|
|
// 通过表头所在位置判断是行数据还是列数据
|
|
// 通过表头所在位置判断是行数据还是列数据
|
|
- Integer dataDirectionType = detectDataDirection(fieldPositionMap);
|
|
|
|
|
|
+ Integer dataDirectionType = direction != null ? direction : detectDataDirection(fieldPositionMap);
|
|
// 数据起始行,起始列
|
|
// 数据起始行,起始列
|
|
int initRow = dataDirectionType.equals(ROW_DIRECTION_TYPE) ? fieldPositionMap.values().stream().map(Pair::getKey).max(Integer::compareTo).orElse(0)
|
|
int initRow = dataDirectionType.equals(ROW_DIRECTION_TYPE) ? fieldPositionMap.values().stream().map(Pair::getKey).max(Integer::compareTo).orElse(0)
|
|
: fieldPositionMap.values().stream().map(Pair::getKey).min(Integer::compareTo).orElse(0);
|
|
: fieldPositionMap.values().stream().map(Pair::getKey).min(Integer::compareTo).orElse(0);
|