EmailTemplateService.java 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  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 replaceText = fieldReplaceMap.get(fieldName);
  220. String value = ExcelUtil.getCellValue(cell);
  221. value=replaceText(value,replaceText);
  222. String cellValue = getValueByPattern(value, fieldRule);
  223. BigDecimal unitConvert = fieldUnitConvertMap.get(fieldName);
  224. cellValue = getValueAfterUnitConvert(cellValue, unitConvert);
  225. fieldValueMap.put(fieldName, cellValue);
  226. }
  227. fieldValueMapList.add(fieldValueMap);
  228. }
  229. }
  230. // 表头和数据在同一个单元格
  231. if (direction.equals(EmailDataDirectionConst.OTHER_DIRECTION_TYPE)) {
  232. Map<String, String> fieldValueMap = MapUtil.newHashMap();
  233. for (Map.Entry<String, Pair<Integer, Integer>> fieldEntry : fieldPositionMap.entrySet()) {
  234. String fieldName = fieldEntry.getKey();
  235. Integer rowIndex = fieldEntry.getValue().getKey();
  236. Integer columnIndex = fieldEntry.getValue().getValue();
  237. Row row = sheet.getRow(rowIndex);
  238. if (row == null) {
  239. continue;
  240. }
  241. Cell cell = row.getCell(columnIndex);
  242. String fieldRule = fieldPatternMap.get(fieldName);
  243. String cellValue = getValueByPattern(ExcelUtil.getCellValue(cell), fieldRule);
  244. BigDecimal unitConvert = fieldUnitConvertMap.get(fieldName);
  245. cellValue = getValueAfterUnitConvert(cellValue, unitConvert);
  246. fieldValueMap.put(fieldName, cellValue);
  247. }
  248. fieldValueMapList.add(fieldValueMap);
  249. }
  250. return fieldValueMapList;
  251. }
  252. private String replaceText(String value, String replaceText) {
  253. if(StringUtil.isEmpty(replaceText) || StringUtil.isEmpty(value)){
  254. return value;
  255. }
  256. value = value.replaceAll(" ","");
  257. for(int i=0;i<replaceText.length();i++){
  258. value = value.replaceAll(String.valueOf(replaceText.charAt(i)),"");
  259. }
  260. return value;
  261. }
  262. /**
  263. * 封装解析的数据为净值数据
  264. *
  265. * @param textFieldValueMap 从正文表格中解析到的数据
  266. * @param excelFieldValueMapList 从正文文本中解析到的数据
  267. * @return 净值数据
  268. */
  269. private List<EmailFundNavDTO> buildEmailFundNavDTO(Map<String, String> textFieldValueMap, List<Map<String, String>> excelFieldValueMapList) {
  270. if (MapUtil.isEmpty(textFieldValueMap) && CollUtil.isEmpty(excelFieldValueMapList)) {
  271. return CollUtil.newArrayList();
  272. }
  273. List<EmailFundNavDTO> fundNavDTOList = CollUtil.newArrayList();
  274. if (CollUtil.isNotEmpty(excelFieldValueMapList)) {
  275. for (Map<String, String> excelFieldValueMap : excelFieldValueMapList) {
  276. fundNavDTOList.add(buildEmailFundNavDTO(excelFieldValueMap, textFieldValueMap));
  277. }
  278. }
  279. if (MapUtil.isNotEmpty(textFieldValueMap) && CollUtil.isEmpty(excelFieldValueMapList)) {
  280. fundNavDTOList.add(buildEmailFundNavDTO(null, textFieldValueMap));
  281. }
  282. return fundNavDTOList;
  283. }
  284. /**
  285. * 获取模板配置信息
  286. *
  287. * @param emailContentInfoDTO 采集到的邮件原始信息
  288. * @param textContent 邮件正文内容
  289. * @param filePath 文件路径(excel类型)
  290. * @return 模板配置信息列表
  291. */
  292. public List<TemplateDetailDTO> getTemplateDetail(EmailContentInfoDTO emailContentInfoDTO, String textContent, String filePath) {
  293. List<TemplateDetailDTO> templateDetailDTOList = CollUtil.newArrayList();
  294. String senderEmail = emailContentInfoDTO.getSenderEmail();
  295. if (StrUtil.isBlank(senderEmail)) {
  296. return templateDetailDTOList;
  297. }
  298. // 1-excel类型,2-正文类型
  299. int type = StrUtil.isNotBlank(emailContentInfoDTO.getFilePath()) && ExcelUtil.isHTML(emailContentInfoDTO.getFilePath()) ? 2 : 1;
  300. // 查询邮箱配置的模板Id
  301. List<EmailTemplateInfoDO> emailTemplateInfoDOList = emailTemplateMappingMapper.queryByEmail(senderEmail, type);
  302. List<Integer> templateIdList = emailTemplateInfoDOList.stream().map(EmailTemplateInfoDO::getId).distinct().collect(Collectors.toList());
  303. if (CollUtil.isEmpty(templateIdList)) {
  304. return templateDetailDTOList;
  305. }
  306. Map<Integer, EmailTemplateInfoDO> templateIdDirectionMap = emailTemplateInfoDOList.stream()
  307. .collect(Collectors.toMap(EmailTemplateInfoDO::getId, v -> v, (oldValue, newValue) -> newValue));
  308. // 查询模版适用性规则 -> 判断邮件是否满足模板适用性规则(模板的适应性规则允许为空 -> 这种情况下满足模板的适用条件)
  309. List<EmailTemplateApplicationRuleDO> templateApplicationRuleDOList = emailTemplateApplicationRuleMapper.queryByTemplateId(templateIdList);
  310. Map<Integer, List<EmailTemplateApplicationRuleDO>> templateIdApplicationRuleMap = MapUtil.newHashMap();
  311. if (CollUtil.isNotEmpty(templateApplicationRuleDOList)) {
  312. templateIdApplicationRuleMap = templateApplicationRuleDOList.stream().collect(Collectors.groupingBy(EmailTemplateApplicationRuleDO::getTemplateId));
  313. }
  314. // 查询模版数据规则
  315. List<EmailTemplateDataRuleDO> templateDataRuleDOList = emailTemplateDataRuleMapper.queryByTemplateId(templateIdList);
  316. if (CollUtil.isEmpty(templateDataRuleDOList)) {
  317. return templateDetailDTOList;
  318. }
  319. // 过滤掉(表格行和表格列)或者(提取规则)都为空的数据
  320. Map<Integer, List<EmailTemplateDataRuleDO>> templateIdDataRuleMap = templateDataRuleDOList.stream()
  321. .filter(e -> (e.getRow() != null && StrUtil.isNotBlank(e.getColumn())) || StrUtil.isNotBlank(e.getFieldRule()))
  322. .collect(Collectors.groupingBy(EmailTemplateDataRuleDO::getTemplateId));
  323. for (Integer templateId : templateIdList) {
  324. List<EmailTemplateApplicationRuleDO> applicationRuleDOList = templateIdApplicationRuleMap.get(templateId);
  325. // 判断是否满足模板适用性规则
  326. boolean isMatchTemplate = isMatchTemplate(textContent, emailContentInfoDTO.getEmailTitle(), emailContentInfoDTO.getFileName(), filePath, applicationRuleDOList);
  327. if (!isMatchTemplate) {
  328. continue;
  329. }
  330. TemplateDetailDTO templateDetailDTO = new TemplateDetailDTO();
  331. templateDetailDTO.setTemplateId(templateId);
  332. EmailTemplateInfoDO emailTemplateInfoDO = templateIdDirectionMap.get(templateId);
  333. templateDetailDTO.setDirection(emailTemplateInfoDO.getDirection());
  334. templateDetailDTO.setType(emailTemplateInfoDO.getType());
  335. templateDetailDTO.setStartIndex(emailTemplateInfoDO.getStartIndex());
  336. templateDetailDTO.setEndIndex(emailTemplateInfoDO.getEndIndex());
  337. List<EmailTemplateDataRuleDO> dataRuleDOList = templateIdDataRuleMap.get(templateId);
  338. List<TemplateDataRuleDTO> dataRuleDetailList = dataRuleDOList.stream().map(e -> BeanUtil.copyProperties(e, TemplateDataRuleDTO.class)).toList();
  339. templateDetailDTO.setDataRuleDetailList(dataRuleDetailList);
  340. templateDetailDTOList.add(templateDetailDTO);
  341. }
  342. return templateDetailDTOList;
  343. }
  344. /**
  345. * 判断邮件是否满足模板的适用性规则
  346. *
  347. * @param emailContent 邮件正文内容(已转成纯文本,以换行符分割每行内容)
  348. * @param emailTitle 邮件主题
  349. * @param fileName 附件名称
  350. * @param filePath 文件路径(邮件正文html转成)
  351. * @param applicationRuleDOList 模板配置的适用性规则列表(为空时 -> 返回true)
  352. * @return true or false
  353. */
  354. private boolean isMatchTemplate(String emailContent, String emailTitle, String fileName,
  355. String filePath, List<EmailTemplateApplicationRuleDO> applicationRuleDOList) {
  356. if (CollUtil.isEmpty(applicationRuleDOList)) {
  357. return true;
  358. }
  359. return applicationRuleDOList.stream()
  360. .allMatch(e -> isMathApplicationRule(e, emailContent, emailTitle, fileName, filePath));
  361. }
  362. /**
  363. * 判断邮件是否满足模板的某条适用性规则
  364. *
  365. * @param applicationRuleDO 适用性规则
  366. * @param emailContent 邮件正文内容(可为空)
  367. * @param emailTitle 邮件主题
  368. * @param fileName 附件名称
  369. * @param filePath 文件路径(邮件正文html转成)
  370. * @return true or false
  371. */
  372. private boolean isMathApplicationRule(EmailTemplateApplicationRuleDO applicationRuleDO, String emailContent, String emailTitle, String fileName, String filePath) {
  373. boolean isMatch = true;
  374. Integer type = applicationRuleDO.getType();
  375. String containKeyword = applicationRuleDO.getContainKeyword();
  376. String notContainKeyword = applicationRuleDO.getNotContainKeyword();
  377. if (ApplicationRuleFileConst.EMAIL_TITLE_FILE.equals(type)) {
  378. isMatch = iskeywordMatch(emailTitle, containKeyword, notContainKeyword);
  379. } else if (ApplicationRuleFileConst.EMAIL_CONTENT_FILE.equals(type)) {
  380. isMatch = iskeywordMatch(emailContent, containKeyword, notContainKeyword);
  381. } else if (ApplicationRuleFileConst.EMAIL_FILE_NAME_FILE.equals(type)) {
  382. isMatch = iskeywordMatch(fileName, containKeyword, notContainKeyword);
  383. } else if (ApplicationRuleFileConst.EMAIL_EXCEL_CONTENT_FILE.equals(type)) {
  384. if (StrUtil.isNotBlank(fileName) && ExcelUtil.isExcel(fileName)) {
  385. Integer rowIndex = applicationRuleDO.getRow();
  386. if (rowIndex == null || StrUtil.isBlank(applicationRuleDO.getColumn())) {
  387. return false;
  388. }
  389. Sheet sheet = ExcelUtil.getFirstSheet(filePath);
  390. if (sheet == null) {
  391. return false;
  392. }
  393. rowIndex = rowIndex - 1;
  394. Row row = sheet.getRow(rowIndex);
  395. if (row == null) {
  396. return false;
  397. }
  398. int column = columnLetterToIndex(applicationRuleDO.getColumn());
  399. Cell cell = row.getCell(column);
  400. if (cell == null) {
  401. return false;
  402. }
  403. String cellValue = ExcelUtil.getCellValue(cell);
  404. if (StrUtil.isBlank(cellValue)) {
  405. return false;
  406. }
  407. isMatch = iskeywordMatch(cellValue, containKeyword, notContainKeyword);
  408. }
  409. }
  410. return isMatch;
  411. }
  412. /**
  413. * 判断字符串是否包含指定的关键词列表,并且不包含指定的排除关键词列表。
  414. *
  415. * @param emailTitle 要检查的字符串
  416. * @param containKeyword 逗号分隔的必须包含的关键词列表
  417. * @param notContainKeyword 逗号分隔的必须不包含的关键词列表
  418. * @return true or false
  419. */
  420. public boolean iskeywordMatch(String emailTitle, String containKeyword, String notContainKeyword) {
  421. boolean isMatch = true;
  422. if (StrUtil.isNotBlank(containKeyword)) {
  423. isMatch &= Arrays.stream(containKeyword.split(","))
  424. .allMatch(emailTitle::contains);
  425. }
  426. if (StrUtil.isNotBlank(notContainKeyword)) {
  427. isMatch &= Arrays.stream(notContainKeyword.split(","))
  428. .noneMatch(emailTitle::contains);
  429. }
  430. return isMatch;
  431. }
  432. /**
  433. * 进行单位转换
  434. *
  435. * @param cellValue 待进行单位转换的值
  436. * @param unitConvert 转换单位
  437. * @return 单位转换后的值
  438. */
  439. private String getValueAfterUnitConvert(String cellValue, BigDecimal unitConvert) {
  440. if (unitConvert == null) {
  441. return cellValue;
  442. }
  443. try {
  444. BigDecimal bigDecimal = new BigDecimal(cellValue);
  445. return String.valueOf(bigDecimal.multiply(unitConvert));
  446. } catch (Exception e) {
  447. return cellValue;
  448. }
  449. }
  450. /**
  451. * 根据正则表达式提取内容
  452. *
  453. * @param text 文本
  454. * @param fieldRule 正则表达式
  455. * @return 内容
  456. */
  457. private String getValueByPattern(String text, String fieldRule) {
  458. if (StrUtil.isBlank(text) || StrUtil.isBlank(fieldRule)) {
  459. return text;
  460. }
  461. Pattern pattern = Pattern.compile(fieldRule);
  462. Matcher matcher = pattern.matcher(text);
  463. try {
  464. while (matcher.find()) {
  465. return matcher.group(1);
  466. }
  467. } catch (Exception e) {
  468. return null;
  469. }
  470. return null;
  471. }
  472. private EmailFundNavDTO buildEmailFundNavDTO(Map<String, String> excelFieldValueMap, Map<String, String> textFieldValueMap) {
  473. EmailFundNavDTO fundNavDTO = new EmailFundNavDTO();
  474. String registerNumber = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.REGISTER_NUMBER))
  475. ? excelFieldValueMap.get(EmailFieldConst.REGISTER_NUMBER) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.REGISTER_NUMBER) : null;
  476. String fundName = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.FUND_NAME))
  477. ? excelFieldValueMap.get(EmailFieldConst.FUND_NAME) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.FUND_NAME) : null;
  478. String priceDate = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.PRICE_DATE))
  479. ? excelFieldValueMap.get(EmailFieldConst.PRICE_DATE) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.PRICE_DATE) : null;
  480. //错误的日期格式也要保留下来,后续展示需要,这里处理的话会变为null,需要注释掉
  481. // priceDate = DateUtils.stringToDate(priceDate);
  482. String nav = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.NAV))
  483. ? excelFieldValueMap.get(EmailFieldConst.NAV) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.NAV) : null;
  484. String cumulativeNavWithdrawal = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL))
  485. ? excelFieldValueMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL) : null;
  486. String assetShare = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.ASSET_SHARE))
  487. ? excelFieldValueMap.get(EmailFieldConst.ASSET_SHARE) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.ASSET_SHARE) : null;
  488. String assetNet = MapUtil.isNotEmpty(excelFieldValueMap) && StrUtil.isNotBlank(excelFieldValueMap.get(EmailFieldConst.ASSET_NET))
  489. ? excelFieldValueMap.get(EmailFieldConst.ASSET_NET) : MapUtil.isNotEmpty(textFieldValueMap) ? textFieldValueMap.get(EmailFieldConst.ASSET_NET) : null;
  490. fundNavDTO.setRegisterNumber(registerNumber);
  491. fundNavDTO.setFundName(fundName);
  492. fundNavDTO.setPriceDate(priceDate);
  493. fundNavDTO.setNav(nav);
  494. fundNavDTO.setCumulativeNavWithdrawal(cumulativeNavWithdrawal);
  495. fundNavDTO.setAssetShare(assetShare);
  496. fundNavDTO.setAssetNet(assetNet);
  497. return fundNavDTO;
  498. }
  499. public List<EmailFileContentDTO> getRealFilePath(String filePath, String content) {
  500. List<EmailFileContentDTO> emailFileContentDTOList = CollUtil.newArrayList();
  501. if (StrUtil.isBlank(filePath)) {
  502. return emailFileContentDTOList;
  503. }
  504. if (ExcelUtil.isExcel(filePath)) {
  505. emailFileContentDTOList.add(new EmailFileContentDTO(filePath, null));
  506. } else if (ExcelUtil.isHTML(filePath)) {
  507. String excelFilePath = filePath.replace(".html", ".xlsx");
  508. excelFilePath = ExcelUtil.contentConvertToExcel(content, excelFilePath);
  509. emailFileContentDTOList.add(new EmailFileContentDTO(excelFilePath, getTextFromHtml(content)));
  510. } else if (ExcelUtil.isPdf(filePath)) {
  511. String excelFilePath = filePath.replace(".pdf", ".xlsx").replace(".PDF", ".xlsx");
  512. excelFilePath = ExcelUtil.pdfConvertToExcel(filePath, excelFilePath);
  513. emailFileContentDTOList.add(new EmailFileContentDTO(excelFilePath, null));
  514. } else if (ExcelUtil.isZip(filePath)) {
  515. String destPath = filePath.replaceAll(".zip", "").replaceAll(".ZIP", "");
  516. List<String> dir = ExcelUtil.extractCompressedFiles(filePath, destPath);
  517. for (String zipFilePath : dir) {
  518. File file = new File(zipFilePath);
  519. if (file.isDirectory()) {
  520. for (String navFilePath : Objects.requireNonNull(file.list())) {
  521. Optional.ofNullable(getRealFilePath(navFilePath, null)).ifPresent(emailFileContentDTOList::addAll);
  522. }
  523. } else {
  524. Optional.ofNullable(getRealFilePath(zipFilePath, null)).ifPresent(emailFileContentDTOList::addAll);
  525. }
  526. }
  527. }
  528. return emailFileContentDTOList;
  529. }
  530. /**
  531. * 获取html所有文本内容
  532. *
  533. * @param htmlContent 内容(html格式)
  534. * @return 文本内容
  535. */
  536. private String getTextFromHtml(String htmlContent) {
  537. if (StrUtil.isBlank(htmlContent)) {
  538. return null;
  539. }
  540. Document doc = Jsoup.parse(htmlContent);
  541. if (doc == null) {
  542. return htmlContent;
  543. }
  544. Elements allElements = doc.getAllElements();
  545. String textContent = allElements.stream().map(Element::ownText).collect(Collectors.joining("\n"));
  546. return textContent.trim();
  547. }
  548. /**
  549. * 将数据列转成数字型索引
  550. *
  551. * @param columnName 数据列名:如A,B,....
  552. * @return 数据列数字型索引
  553. */
  554. public static int columnLetterToIndex(String columnName) {
  555. int columnIndex = 0;
  556. int columnLength = columnName.length();
  557. // Excel列的基础是26
  558. int base = 26;
  559. for (int i = 0; i < columnLength; i++) {
  560. char letter = columnName.charAt(i);
  561. // 计算字母的位置(A=0, B=1, ..., Z=25)
  562. int position = letter - 'A';
  563. columnIndex += (int) (position * Math.pow(base, columnLength - i - 1));
  564. }
  565. return columnIndex;
  566. }
  567. }