EmailTemplateService.java 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. package com.simuwang.daq.service;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import cn.hutool.core.collection.CollUtil;
  4. import cn.hutool.core.exceptions.ExceptionUtil;
  5. import cn.hutool.core.lang.Pair;
  6. import cn.hutool.core.map.MapUtil;
  7. import cn.hutool.core.util.StrUtil;
  8. import com.simuwang.base.common.conts.ApplicationRuleFileConst;
  9. import com.simuwang.base.common.conts.EmailDataDirectionConst;
  10. import com.simuwang.base.common.conts.EmailFieldConst;
  11. import com.simuwang.base.common.util.DateUtils;
  12. import com.simuwang.base.common.util.ExcelUtil;
  13. import com.simuwang.base.common.util.NavDataUtil;
  14. import com.simuwang.base.common.util.StringUtil;
  15. import com.simuwang.base.mapper.daq.EmailTemplateApplicationRuleMapper;
  16. import com.simuwang.base.mapper.daq.EmailTemplateDataRuleMapper;
  17. import com.simuwang.base.mapper.daq.EmailTemplateMappingMapper;
  18. import com.simuwang.base.pojo.dto.EmailFileContentDTO;
  19. import com.simuwang.base.pojo.dos.EmailTemplateApplicationRuleDO;
  20. import com.simuwang.base.pojo.dos.EmailTemplateDataRuleDO;
  21. import com.simuwang.base.pojo.dos.EmailTemplateInfoDO;
  22. import com.simuwang.base.pojo.dto.EmailContentInfoDTO;
  23. import com.simuwang.base.pojo.dto.EmailFundNavDTO;
  24. import com.simuwang.base.pojo.dto.TemplateDataRuleDTO;
  25. import com.simuwang.base.pojo.dto.TemplateDetailDTO;
  26. import org.apache.poi.ss.usermodel.Cell;
  27. import org.apache.poi.ss.usermodel.Row;
  28. import org.apache.poi.ss.usermodel.Sheet;
  29. import org.jsoup.Jsoup;
  30. import org.jsoup.nodes.Document;
  31. import org.jsoup.nodes.Element;
  32. import org.jsoup.select.Elements;
  33. import org.slf4j.Logger;
  34. import org.slf4j.LoggerFactory;
  35. import org.springframework.stereotype.Service;
  36. import java.io.File;
  37. import java.math.BigDecimal;
  38. import java.util.*;
  39. import java.util.regex.Matcher;
  40. import java.util.regex.Pattern;
  41. import java.util.stream.Collectors;
  42. /**
  43. * @author mozuwen
  44. * @date 2024-09-23
  45. * @description 净值模板解析
  46. */
  47. @Service
  48. public class EmailTemplateService {
  49. private static final Logger log = LoggerFactory.getLogger(EmailTemplateService.class);
  50. private final EmailTemplateMappingMapper emailTemplateMappingMapper;
  51. private final EmailTemplateApplicationRuleMapper emailTemplateApplicationRuleMapper;
  52. private final EmailTemplateDataRuleMapper emailTemplateDataRuleMapper;
  53. private final static Map<Integer, String> FILE_TYPE_MAP = new HashMap<>();
  54. static {
  55. // 1-备案编码,2-基金名称,3-净值日期,4-单位净值,5-累计净值,6-资产份额,7-资产净值
  56. FILE_TYPE_MAP.put(1, EmailFieldConst.REGISTER_NUMBER);
  57. FILE_TYPE_MAP.put(2, EmailFieldConst.FUND_NAME);
  58. FILE_TYPE_MAP.put(3, EmailFieldConst.PRICE_DATE);
  59. FILE_TYPE_MAP.put(4, EmailFieldConst.NAV);
  60. FILE_TYPE_MAP.put(5, EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL);
  61. FILE_TYPE_MAP.put(6, EmailFieldConst.ASSET_SHARE);
  62. FILE_TYPE_MAP.put(7, EmailFieldConst.ASSET_NET);
  63. }
  64. public EmailTemplateService(EmailTemplateMappingMapper emailTemplateMappingMapper, EmailTemplateApplicationRuleMapper emailTemplateApplicationRuleMapper,
  65. EmailTemplateDataRuleMapper emailTemplateDataRuleMapper) {
  66. this.emailTemplateMappingMapper = emailTemplateMappingMapper;
  67. this.emailTemplateApplicationRuleMapper = emailTemplateApplicationRuleMapper;
  68. this.emailTemplateDataRuleMapper = emailTemplateDataRuleMapper;
  69. }
  70. public List<EmailFundNavDTO> parseUsingTemplate(EmailContentInfoDTO emailContentInfoDTO) {
  71. // 考虑文件为PDF,html,zip等情况 -> 将正文html和pdf文件转成Excel
  72. List<EmailFileContentDTO> emailFileContentDTOList = getRealFilePath(emailContentInfoDTO.getFilePath(), emailContentInfoDTO.getEmailContent());
  73. if (CollUtil.isEmpty(emailFileContentDTOList)) {
  74. return CollUtil.newArrayList();
  75. }
  76. List<EmailFundNavDTO> emailFundNavDTOList = CollUtil.newArrayList();
  77. for (EmailFileContentDTO emailFileContentDTO : emailFileContentDTOList) {
  78. String filePath = emailFileContentDTO.getFilePath();
  79. String textContent = emailFileContentDTO.getContent();
  80. // 获取模板信息
  81. List<TemplateDetailDTO> templateDetailDTOList = getTemplateDetail(emailContentInfoDTO, textContent, filePath);
  82. if (CollUtil.isEmpty(templateDetailDTOList)) {
  83. log.info("未匹配模板 -> 参数:{}", emailFileContentDTO);
  84. return CollUtil.newArrayList();
  85. }
  86. // 按照模板分别进行解析
  87. for (TemplateDetailDTO templateDetailDTO : templateDetailDTOList) {
  88. try {
  89. List<EmailFundNavDTO> fundNavDTOList = extraFundNav(filePath, textContent, templateDetailDTO);
  90. //因为异常数据也要保留,不能直接过滤,所以这里需要注释掉
  91. // fundNavDTOList = fundNavDTOList.stream().filter(NavDataUtil::navDataFormatCheck).toList();
  92. if (CollUtil.isEmpty(fundNavDTOList)) {
  93. log.info("模板配置解析不到数据 -> 模板id:{}", templateDetailDTO.getTemplateId());
  94. continue;
  95. }
  96. fundNavDTOList.forEach(e -> e.setTemplateId(templateDetailDTO.getTemplateId()));
  97. emailFundNavDTOList.addAll(fundNavDTOList);
  98. log.info("模板配置解析成功 -> 模板id:{},数据:{}", templateDetailDTO.getTemplateId(), fundNavDTOList);
  99. } catch (Exception e) {
  100. log.error("净值模板解析报错 -> 模板id:{},文件:{},堆栈信息:{}", templateDetailDTO.getTemplateId(), filePath, ExceptionUtil.stacktraceToString(e));
  101. }
  102. }
  103. }
  104. // 过滤掉相同的数据(考虑到多个模板解析到相同数据的情况)
  105. return emailFundNavDTOList.stream().distinct().toList();
  106. }
  107. /**
  108. * 根据模板信息提取净值数据
  109. *
  110. * @param filePath 文件路径(excel类型)
  111. * @param textContent 邮件正文内容
  112. * @param templateDetailDTO 模板配置信息
  113. * @return 净值数据
  114. */
  115. private List<EmailFundNavDTO> extraFundNav(String filePath, String textContent, TemplateDetailDTO templateDetailDTO) {
  116. List<TemplateDataRuleDTO> dataRuleDetailList = templateDetailDTO.getDataRuleDetailList();
  117. Map<String, String> fieldValueMap = MapUtil.newHashMap(8);
  118. List<Map<String, String>> fieldValueMapList = CollUtil.newArrayList();
  119. boolean hasParsedTableData = false;
  120. Integer direction = templateDetailDTO.getDirection();
  121. for (TemplateDataRuleDTO templateDataRuleDTO : dataRuleDetailList) {
  122. Integer fieldName = templateDataRuleDTO.getFieldName();
  123. Integer position = templateDataRuleDTO.getPosition() == null ? 2 : templateDataRuleDTO.getPosition();
  124. // 提取位置-正文
  125. if (position == 1) {
  126. String valueByPattern = getValueByPattern(textContent, templateDataRuleDTO.getFieldRule());
  127. fieldValueMap.put(FILE_TYPE_MAP.get(fieldName), valueByPattern);
  128. }
  129. // 提取位置-正文表格或附件表格(表格数据提取一次后不再提取)
  130. if (position == 2 && !hasParsedTableData) {
  131. hasParsedTableData = true;
  132. List<TemplateDataRuleDTO> dataRuleDTOS = dataRuleDetailList.stream().filter(e -> e.getPosition() == null || e.getPosition() == 2).toList();
  133. fieldValueMapList = parsedTableData(filePath, direction, dataRuleDTOS);
  134. }
  135. }
  136. return buildEmailFundNavDTO(fieldValueMap, fieldValueMapList);
  137. }
  138. /**
  139. * 根据数据提取规则提取数据
  140. *
  141. * @param filePath 文件路径(excel类型)
  142. * @param direction 数据方向:1-行,2-列,3-其他(无规律)
  143. * @param dataRuleDTOS 数据提取规则
  144. * @return 数据字段-字段值映射关系
  145. */
  146. private List<Map<String, String>> parsedTableData(String filePath, Integer direction, List<TemplateDataRuleDTO> dataRuleDTOS) {
  147. if (StrUtil.isBlank(filePath) || CollUtil.isEmpty(dataRuleDTOS)) {
  148. return CollUtil.newArrayList();
  149. }
  150. Sheet sheet = ExcelUtil.getFirstSheet(filePath);
  151. if (sheet == null) {
  152. return CollUtil.newArrayList();
  153. }
  154. Map<String, Pair<Integer, Integer>> fieldPositionMap = MapUtil.newHashMap();
  155. Map<String, String> fieldPatternMap = MapUtil.newHashMap(8);
  156. Map<String, BigDecimal> fieldUnitConvertMap = MapUtil.newHashMap(8);
  157. Map<String, String> fieldReplaceMap = MapUtil.newHashMap(8);
  158. for (TemplateDataRuleDTO dataRuleDTO : dataRuleDTOS) {
  159. if (dataRuleDTO.getRow() == null) {
  160. continue;
  161. }
  162. Integer row = dataRuleDTO.getRow() - 1;
  163. int column = columnLetterToIndex(dataRuleDTO.getColumn());
  164. Pair<Integer, Integer> pair = new Pair<>(row, column);
  165. fieldPositionMap.put(FILE_TYPE_MAP.get(dataRuleDTO.getFieldName()), pair);
  166. if (StrUtil.isNotBlank(dataRuleDTO.getFieldRule())) {
  167. fieldPatternMap.put(FILE_TYPE_MAP.get(dataRuleDTO.getFieldName()), dataRuleDTO.getFieldRule());
  168. }
  169. if (dataRuleDTO.getUnitConvert() != null) {
  170. fieldUnitConvertMap.put(FILE_TYPE_MAP.get(dataRuleDTO.getFieldName()), dataRuleDTO.getUnitConvert());
  171. }
  172. if (StringUtil.isNotEmpty(dataRuleDTO.getReplaceText())) {
  173. fieldReplaceMap.put(FILE_TYPE_MAP.get(dataRuleDTO.getFieldName()), dataRuleDTO.getReplaceText());
  174. }
  175. }
  176. List<Map<String, String>> fieldValueMapList = CollUtil.newArrayList();
  177. if (direction.equals(EmailDataDirectionConst.ROW_DIRECTION_TYPE)) {
  178. int startRow = fieldPositionMap.values().stream().map(Pair::getKey).min(Integer::compareTo).orElse(0);
  179. for (int i = startRow + 1; i <= sheet.getLastRowNum(); i++) {
  180. Map<String, String> fieldValueMap = MapUtil.newHashMap(8);
  181. Row row = sheet.getRow(i);
  182. if (row == null) {
  183. continue;
  184. }
  185. for (Map.Entry<String, Pair<Integer, Integer>> fieldEntry : fieldPositionMap.entrySet()) {
  186. String fieldName = fieldEntry.getKey();
  187. int columnIndex = fieldEntry.getValue().getValue();
  188. Cell cell = row.getCell(columnIndex);
  189. String value = ExcelUtil.getCellValue(cell);
  190. String fieldRule = fieldPatternMap.get(fieldName);
  191. String replaceText = fieldReplaceMap.get(fieldName);
  192. boolean isDateFormat = fieldName.equals(EmailFieldConst.PRICE_DATE) && StringUtil.isNumeric(value) && StrUtil.isNotBlank(value) && StringUtil.compare2NumericValue(value);
  193. if (isDateFormat) {
  194. value = ExcelUtil.convertExcelDateToString(value);
  195. }
  196. value=replaceText(value,replaceText);
  197. String cellValue = getValueByPattern(value, fieldRule);
  198. BigDecimal unitConvert = fieldUnitConvertMap.get(fieldName);
  199. cellValue = getValueAfterUnitConvert(cellValue, unitConvert);
  200. fieldValueMap.put(fieldName, cellValue);
  201. }
  202. fieldValueMapList.add(fieldValueMap);
  203. }
  204. }
  205. if (direction.equals(EmailDataDirectionConst.COLUMN_DIRECTION_TYPE)) {
  206. int startColumn = fieldPositionMap.values().stream().map(Pair::getValue).min(Integer::compareTo).orElse(0);
  207. for (int columnNum = startColumn + 1; columnNum < EmailDataDirectionConst.MAX_ROW_COLUMN; columnNum++) {
  208. Map<String, String> fieldValueMap = MapUtil.newHashMap();
  209. for (Map.Entry<String, Pair<Integer, Integer>> fieldEntry : fieldPositionMap.entrySet()) {
  210. String fieldName = fieldEntry.getKey();
  211. Integer rowIndex = fieldEntry.getValue().getKey();
  212. Row row = sheet.getRow(rowIndex);
  213. if (row == null) {
  214. continue;
  215. }
  216. Integer columnIndex = fieldEntry.getValue().getValue();
  217. Cell cell = row.getCell(columnIndex + 1);
  218. String fieldRule = fieldPatternMap.get(fieldName);
  219. String cellValue = getValueByPattern(ExcelUtil.getCellValue(cell), fieldRule);
  220. BigDecimal unitConvert = fieldUnitConvertMap.get(fieldName);
  221. cellValue = getValueAfterUnitConvert(cellValue, unitConvert);
  222. fieldValueMap.put(fieldName, cellValue);
  223. }
  224. fieldValueMapList.add(fieldValueMap);
  225. }
  226. }
  227. // 表头和数据在同一个单元格
  228. if (direction.equals(EmailDataDirectionConst.OTHER_DIRECTION_TYPE)) {
  229. Map<String, String> fieldValueMap = MapUtil.newHashMap();
  230. for (Map.Entry<String, Pair<Integer, Integer>> fieldEntry : fieldPositionMap.entrySet()) {
  231. String fieldName = fieldEntry.getKey();
  232. Integer rowIndex = fieldEntry.getValue().getKey();
  233. Integer columnIndex = fieldEntry.getValue().getValue();
  234. Row row = sheet.getRow(rowIndex);
  235. if (row == null) {
  236. continue;
  237. }
  238. Cell cell = row.getCell(columnIndex);
  239. String fieldRule = fieldPatternMap.get(fieldName);
  240. String cellValue = getValueByPattern(ExcelUtil.getCellValue(cell), fieldRule);
  241. BigDecimal unitConvert = fieldUnitConvertMap.get(fieldName);
  242. cellValue = getValueAfterUnitConvert(cellValue, unitConvert);
  243. fieldValueMap.put(fieldName, cellValue);
  244. }
  245. fieldValueMapList.add(fieldValueMap);
  246. }
  247. return fieldValueMapList;
  248. }
  249. private String replaceText(String value, String replaceText) {
  250. if(StringUtil.isEmpty(replaceText) || StringUtil.isEmpty(value)){
  251. return value;
  252. }
  253. for(int i=0;i<replaceText.length();i++){
  254. value = value.replaceAll(String.valueOf(replaceText.charAt(i)),"");
  255. }
  256. return value;
  257. }
  258. /**
  259. * 封装解析的数据为净值数据
  260. *
  261. * @param textFieldValueMap 从正文表格中解析到的数据
  262. * @param excelFieldValueMapList 从正文文本中解析到的数据
  263. * @return 净值数据
  264. */
  265. private List<EmailFundNavDTO> buildEmailFundNavDTO(Map<String, String> textFieldValueMap, List<Map<String, String>> excelFieldValueMapList) {
  266. if (MapUtil.isEmpty(textFieldValueMap) && CollUtil.isEmpty(excelFieldValueMapList)) {
  267. return CollUtil.newArrayList();
  268. }
  269. List<EmailFundNavDTO> fundNavDTOList = CollUtil.newArrayList();
  270. if (CollUtil.isNotEmpty(excelFieldValueMapList)) {
  271. for (Map<String, String> excelFieldValueMap : excelFieldValueMapList) {
  272. fundNavDTOList.add(buildEmailFundNavDTO(excelFieldValueMap, textFieldValueMap));
  273. }
  274. }
  275. if (MapUtil.isNotEmpty(textFieldValueMap) && CollUtil.isEmpty(excelFieldValueMapList)) {
  276. fundNavDTOList.add(buildEmailFundNavDTO(null, textFieldValueMap));
  277. }
  278. return fundNavDTOList;
  279. }
  280. /**
  281. * 获取模板配置信息
  282. *
  283. * @param emailContentInfoDTO 采集到的邮件原始信息
  284. * @param textContent 邮件正文内容
  285. * @param filePath 文件路径(excel类型)
  286. * @return 模板配置信息列表
  287. */
  288. public List<TemplateDetailDTO> getTemplateDetail(EmailContentInfoDTO emailContentInfoDTO, String textContent, String filePath) {
  289. List<TemplateDetailDTO> templateDetailDTOList = CollUtil.newArrayList();
  290. String senderEmail = emailContentInfoDTO.getSenderEmail();
  291. if (StrUtil.isBlank(senderEmail)) {
  292. return templateDetailDTOList;
  293. }
  294. // 1-excel类型,2-正文类型
  295. int type = StrUtil.isNotBlank(emailContentInfoDTO.getFilePath()) && ExcelUtil.isHTML(emailContentInfoDTO.getFilePath()) ? 2 : 1;
  296. // 查询邮箱配置的模板Id
  297. List<EmailTemplateInfoDO> emailTemplateInfoDOList = emailTemplateMappingMapper.queryByEmail(senderEmail, type);
  298. List<Integer> templateIdList = emailTemplateInfoDOList.stream().map(EmailTemplateInfoDO::getId).distinct().collect(Collectors.toList());
  299. if (CollUtil.isEmpty(templateIdList)) {
  300. return templateDetailDTOList;
  301. }
  302. Map<Integer, EmailTemplateInfoDO> templateIdDirectionMap = emailTemplateInfoDOList.stream()
  303. .collect(Collectors.toMap(EmailTemplateInfoDO::getId, v -> v, (oldValue, newValue) -> newValue));
  304. // 查询模版适用性规则 -> 判断邮件是否满足模板适用性规则(模板的适应性规则允许为空 -> 这种情况下满足模板的适用条件)
  305. List<EmailTemplateApplicationRuleDO> templateApplicationRuleDOList = emailTemplateApplicationRuleMapper.queryByTemplateId(templateIdList);
  306. Map<Integer, List<EmailTemplateApplicationRuleDO>> templateIdApplicationRuleMap = MapUtil.newHashMap();
  307. if (CollUtil.isNotEmpty(templateApplicationRuleDOList)) {
  308. templateIdApplicationRuleMap = templateApplicationRuleDOList.stream().collect(Collectors.groupingBy(EmailTemplateApplicationRuleDO::getTemplateId));
  309. }
  310. // 查询模版数据规则
  311. List<EmailTemplateDataRuleDO> templateDataRuleDOList = emailTemplateDataRuleMapper.queryByTemplateId(templateIdList);
  312. if (CollUtil.isEmpty(templateDataRuleDOList)) {
  313. return templateDetailDTOList;
  314. }
  315. // 过滤掉(表格行和表格列)或者(提取规则)都为空的数据
  316. Map<Integer, List<EmailTemplateDataRuleDO>> templateIdDataRuleMap = templateDataRuleDOList.stream()
  317. .filter(e -> (e.getRow() != null && StrUtil.isNotBlank(e.getColumn())) || StrUtil.isNotBlank(e.getFieldRule()))
  318. .collect(Collectors.groupingBy(EmailTemplateDataRuleDO::getTemplateId));
  319. for (Integer templateId : templateIdList) {
  320. List<EmailTemplateApplicationRuleDO> applicationRuleDOList = templateIdApplicationRuleMap.get(templateId);
  321. // 判断是否满足模板适用性规则
  322. boolean isMatchTemplate = isMatchTemplate(textContent, emailContentInfoDTO.getEmailTitle(), emailContentInfoDTO.getFileName(), filePath, applicationRuleDOList);
  323. if (!isMatchTemplate) {
  324. continue;
  325. }
  326. TemplateDetailDTO templateDetailDTO = new TemplateDetailDTO();
  327. templateDetailDTO.setTemplateId(templateId);
  328. EmailTemplateInfoDO emailTemplateInfoDO = templateIdDirectionMap.get(templateId);
  329. templateDetailDTO.setDirection(emailTemplateInfoDO.getDirection());
  330. templateDetailDTO.setType(emailTemplateInfoDO.getType());
  331. templateDetailDTO.setStartIndex(emailTemplateInfoDO.getStartIndex());
  332. templateDetailDTO.setEndIndex(emailTemplateInfoDO.getEndIndex());
  333. List<EmailTemplateDataRuleDO> dataRuleDOList = templateIdDataRuleMap.get(templateId);
  334. List<TemplateDataRuleDTO> dataRuleDetailList = dataRuleDOList.stream().map(e -> BeanUtil.copyProperties(e, TemplateDataRuleDTO.class)).toList();
  335. templateDetailDTO.setDataRuleDetailList(dataRuleDetailList);
  336. templateDetailDTOList.add(templateDetailDTO);
  337. }
  338. return templateDetailDTOList;
  339. }
  340. /**
  341. * 判断邮件是否满足模板的适用性规则
  342. *
  343. * @param emailContent 邮件正文内容(已转成纯文本,以换行符分割每行内容)
  344. * @param emailTitle 邮件主题
  345. * @param fileName 附件名称
  346. * @param filePath 文件路径(邮件正文html转成)
  347. * @param applicationRuleDOList 模板配置的适用性规则列表(为空时 -> 返回true)
  348. * @return true or false
  349. */
  350. private boolean isMatchTemplate(String emailContent, String emailTitle, String fileName,
  351. String filePath, List<EmailTemplateApplicationRuleDO> applicationRuleDOList) {
  352. if (CollUtil.isEmpty(applicationRuleDOList)) {
  353. return true;
  354. }
  355. return applicationRuleDOList.stream()
  356. .allMatch(e -> isMathApplicationRule(e, emailContent, emailTitle, fileName, filePath));
  357. }
  358. /**
  359. * 判断邮件是否满足模板的某条适用性规则
  360. *
  361. * @param applicationRuleDO 适用性规则
  362. * @param emailContent 邮件正文内容(可为空)
  363. * @param emailTitle 邮件主题
  364. * @param fileName 附件名称
  365. * @param filePath 文件路径(邮件正文html转成)
  366. * @return true or false
  367. */
  368. private boolean isMathApplicationRule(EmailTemplateApplicationRuleDO applicationRuleDO, String emailContent, String emailTitle, String fileName, String filePath) {
  369. boolean isMatch = true;
  370. Integer type = applicationRuleDO.getType();
  371. String containKeyword = applicationRuleDO.getContainKeyword();
  372. String notContainKeyword = applicationRuleDO.getNotContainKeyword();
  373. if (ApplicationRuleFileConst.EMAIL_TITLE_FILE.equals(type)) {
  374. isMatch = iskeywordMatch(emailTitle, containKeyword, notContainKeyword);
  375. } else if (ApplicationRuleFileConst.EMAIL_CONTENT_FILE.equals(type)) {
  376. isMatch = iskeywordMatch(emailContent, containKeyword, notContainKeyword);
  377. } else if (ApplicationRuleFileConst.EMAIL_FILE_NAME_FILE.equals(type)) {
  378. isMatch = iskeywordMatch(fileName, containKeyword, notContainKeyword);
  379. } else if (ApplicationRuleFileConst.EMAIL_EXCEL_CONTENT_FILE.equals(type)) {
  380. if (StrUtil.isNotBlank(fileName) && ExcelUtil.isExcel(fileName)) {
  381. Integer rowIndex = applicationRuleDO.getRow();
  382. if (rowIndex == null || StrUtil.isBlank(applicationRuleDO.getColumn())) {
  383. return false;
  384. }
  385. Sheet sheet = ExcelUtil.getFirstSheet(filePath);
  386. if (sheet == null) {
  387. return false;
  388. }
  389. rowIndex = rowIndex - 1;
  390. Row row = sheet.getRow(rowIndex);
  391. if (row == null) {
  392. return false;
  393. }
  394. int column = columnLetterToIndex(applicationRuleDO.getColumn());
  395. Cell cell = row.getCell(column);
  396. if (cell == null) {
  397. return false;
  398. }
  399. String cellValue = ExcelUtil.getCellValue(cell);
  400. if (StrUtil.isBlank(cellValue)) {
  401. return false;
  402. }
  403. isMatch = iskeywordMatch(cellValue, containKeyword, notContainKeyword);
  404. }
  405. }
  406. return isMatch;
  407. }
  408. /**
  409. * 判断字符串是否包含指定的关键词列表,并且不包含指定的排除关键词列表。
  410. *
  411. * @param emailTitle 要检查的字符串
  412. * @param containKeyword 逗号分隔的必须包含的关键词列表
  413. * @param notContainKeyword 逗号分隔的必须不包含的关键词列表
  414. * @return true or false
  415. */
  416. public boolean iskeywordMatch(String emailTitle, String containKeyword, String notContainKeyword) {
  417. boolean isMatch = true;
  418. if (StrUtil.isNotBlank(containKeyword)) {
  419. isMatch &= Arrays.stream(containKeyword.split(","))
  420. .allMatch(emailTitle::contains);
  421. }
  422. if (StrUtil.isNotBlank(notContainKeyword)) {
  423. isMatch &= Arrays.stream(notContainKeyword.split(","))
  424. .noneMatch(emailTitle::contains);
  425. }
  426. return isMatch;
  427. }
  428. /**
  429. * 进行单位转换
  430. *
  431. * @param cellValue 待进行单位转换的值
  432. * @param unitConvert 转换单位
  433. * @return 单位转换后的值
  434. */
  435. private String getValueAfterUnitConvert(String cellValue, BigDecimal unitConvert) {
  436. if (unitConvert == null) {
  437. return cellValue;
  438. }
  439. try {
  440. BigDecimal bigDecimal = new BigDecimal(cellValue);
  441. return String.valueOf(bigDecimal.multiply(unitConvert));
  442. } catch (Exception e) {
  443. return cellValue;
  444. }
  445. }
  446. /**
  447. * 根据正则表达式提取内容
  448. *
  449. * @param text 文本
  450. * @param fieldRule 正则表达式
  451. * @return 内容
  452. */
  453. private String getValueByPattern(String text, String fieldRule) {
  454. if (StrUtil.isBlank(text) || StrUtil.isBlank(fieldRule)) {
  455. return text;
  456. }
  457. Pattern pattern = Pattern.compile(fieldRule);
  458. Matcher matcher = pattern.matcher(text);
  459. try {
  460. while (matcher.find()) {
  461. return matcher.group(1);
  462. }
  463. } catch (Exception e) {
  464. return null;
  465. }
  466. return null;
  467. }
  468. private EmailFundNavDTO buildEmailFundNavDTO(Map<String, String> excelFieldValueMap, Map<String, String> textFieldValueMap) {
  469. EmailFundNavDTO fundNavDTO = new EmailFundNavDTO();
  470. String registerNumber = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.REGISTER_NUMBER))
  471. ? excelFieldValueMap.get(EmailFieldConst.REGISTER_NUMBER) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.REGISTER_NUMBER) : null;
  472. String fundName = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.FUND_NAME))
  473. ? excelFieldValueMap.get(EmailFieldConst.FUND_NAME) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.FUND_NAME) : null;
  474. String priceDate = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.PRICE_DATE))
  475. ? excelFieldValueMap.get(EmailFieldConst.PRICE_DATE) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.PRICE_DATE) : null;
  476. //错误的日期格式也要保留下来,后续展示需要,这里处理的话会变为null,需要注释掉
  477. // priceDate = DateUtils.stringToDate(priceDate);
  478. String nav = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.NAV))
  479. ? excelFieldValueMap.get(EmailFieldConst.NAV) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.NAV) : null;
  480. String cumulativeNavWithdrawal = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL))
  481. ? excelFieldValueMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL) : null;
  482. String assetShare = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.ASSET_SHARE))
  483. ? excelFieldValueMap.get(EmailFieldConst.ASSET_SHARE) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.ASSET_SHARE) : null;
  484. String assetNet = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.ASSET_NET))
  485. ? excelFieldValueMap.get(EmailFieldConst.ASSET_NET) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.ASSET_NET) : null;
  486. fundNavDTO.setRegisterNumber(registerNumber);
  487. fundNavDTO.setFundName(fundName);
  488. fundNavDTO.setPriceDate(priceDate);
  489. fundNavDTO.setNav(nav);
  490. fundNavDTO.setCumulativeNavWithdrawal(cumulativeNavWithdrawal);
  491. fundNavDTO.setAssetShare(assetShare);
  492. fundNavDTO.setAssetNet(assetNet);
  493. return fundNavDTO;
  494. }
  495. public List<EmailFileContentDTO> getRealFilePath(String filePath, String content) {
  496. List<EmailFileContentDTO> emailFileContentDTOList = CollUtil.newArrayList();
  497. if (StrUtil.isBlank(filePath)) {
  498. return emailFileContentDTOList;
  499. }
  500. if (ExcelUtil.isExcel(filePath)) {
  501. emailFileContentDTOList.add(new EmailFileContentDTO(filePath, null));
  502. } else if (ExcelUtil.isHTML(filePath)) {
  503. String excelFilePath = filePath.replace(".html", ".xlsx");
  504. excelFilePath = ExcelUtil.contentConvertToExcel(content, excelFilePath);
  505. emailFileContentDTOList.add(new EmailFileContentDTO(excelFilePath, getTextFromHtml(content)));
  506. } else if (ExcelUtil.isPdf(filePath)) {
  507. String excelFilePath = filePath.replace(".pdf", ".xlsx").replace(".PDF", ".xlsx");
  508. excelFilePath = ExcelUtil.pdfConvertToExcel(filePath, excelFilePath);
  509. emailFileContentDTOList.add(new EmailFileContentDTO(excelFilePath, null));
  510. } else if (ExcelUtil.isZip(filePath)) {
  511. String destPath = filePath.replaceAll(".zip", "").replaceAll(".ZIP", "");
  512. List<String> dir = ExcelUtil.extractCompressedFiles(filePath, destPath);
  513. for (String zipFilePath : dir) {
  514. File file = new File(zipFilePath);
  515. if (file.isDirectory()) {
  516. for (String navFilePath : Objects.requireNonNull(file.list())) {
  517. Optional.ofNullable(getRealFilePath(navFilePath, null)).ifPresent(emailFileContentDTOList::addAll);
  518. }
  519. } else {
  520. Optional.ofNullable(getRealFilePath(zipFilePath, null)).ifPresent(emailFileContentDTOList::addAll);
  521. }
  522. }
  523. }
  524. return emailFileContentDTOList;
  525. }
  526. /**
  527. * 获取html所有文本内容
  528. *
  529. * @param htmlContent 内容(html格式)
  530. * @return 文本内容
  531. */
  532. private String getTextFromHtml(String htmlContent) {
  533. if (StrUtil.isBlank(htmlContent)) {
  534. return null;
  535. }
  536. Document doc = Jsoup.parse(htmlContent);
  537. if (doc == null) {
  538. return htmlContent;
  539. }
  540. Elements allElements = doc.getAllElements();
  541. String textContent = allElements.stream().map(Element::ownText).collect(Collectors.joining("\n"));
  542. return textContent.trim();
  543. }
  544. /**
  545. * 将数据列转成数字型索引
  546. *
  547. * @param columnName 数据列名:如A,B,....
  548. * @return 数据列数字型索引
  549. */
  550. public static int columnLetterToIndex(String columnName) {
  551. int columnIndex = 0;
  552. int columnLength = columnName.length();
  553. // Excel列的基础是26
  554. int base = 26;
  555. for (int i = 0; i < columnLength; i++) {
  556. char letter = columnName.charAt(i);
  557. // 计算字母的位置(A=0, B=1, ..., Z=25)
  558. int position = letter - 'A';
  559. columnIndex += (int) (position * Math.pow(base, columnLength - i - 1));
  560. }
  561. return columnIndex;
  562. }
  563. }