NavEmailParser.java 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. package com.simuwang.daq.service;
  2. import cn.hutool.core.collection.CollUtil;
  3. import cn.hutool.core.exceptions.ExceptionUtil;
  4. import cn.hutool.core.lang.Pair;
  5. import cn.hutool.core.map.MapUtil;
  6. import cn.hutool.core.util.StrUtil;
  7. import com.simuwang.base.common.conts.EmailDataDirectionConst;
  8. import com.simuwang.base.common.conts.EmailFieldConst;
  9. import com.simuwang.base.common.conts.EmailTypeConst;
  10. import com.simuwang.base.common.util.DateUtils;
  11. import com.simuwang.base.common.util.ExcelUtil;
  12. import com.simuwang.base.common.util.StringUtil;
  13. import com.simuwang.base.pojo.dto.EmailContentInfoDTO;
  14. import com.simuwang.base.pojo.dto.EmailFundNavDTO;
  15. import com.simuwang.base.pojo.dto.FieldPositionDTO;
  16. import org.apache.poi.ss.usermodel.Cell;
  17. import org.apache.poi.ss.usermodel.Row;
  18. import org.apache.poi.ss.usermodel.Sheet;
  19. import org.slf4j.Logger;
  20. import org.slf4j.LoggerFactory;
  21. import org.springframework.beans.factory.annotation.Value;
  22. import org.springframework.stereotype.Component;
  23. import java.io.File;
  24. import java.util.*;
  25. import java.util.stream.Collectors;
  26. /**
  27. * @author mozuwen
  28. * @date 2024-09-04
  29. * @description 净值邮件解析器
  30. */
  31. @Component
  32. public class NavEmailParser extends AbstractEmailParser {
  33. private static final Logger log = LoggerFactory.getLogger(AbstractEmailParser.class);
  34. @Value("${email.file.path}")
  35. private String path;
  36. private final ValuationEmailParser valuationEmailParser;
  37. private final EmailTemplateService emailTemplateService;
  38. private static final int MAX_COLUMN = 20;
  39. public NavEmailParser(ValuationEmailParser valuationEmailParser, EmailTemplateService emailTemplateService) {
  40. this.valuationEmailParser = valuationEmailParser;
  41. this.emailTemplateService = emailTemplateService;
  42. }
  43. @Override
  44. public boolean isSupport(Integer emailType) {
  45. return EmailTypeConst.NAV_EMAIL_TYPE.equals(emailType);
  46. }
  47. @Override
  48. public List<EmailFundNavDTO> parse(EmailContentInfoDTO emailContentInfoDTO, Map<String, List<String>> emailFieldMap) {
  49. List<EmailFundNavDTO> emailFundNavDTOList = CollUtil.newArrayList();
  50. String emailContent = emailContentInfoDTO.getEmailContent();
  51. // 1.解析邮件正文
  52. if (StrUtil.isNotBlank(emailContentInfoDTO.getFilePath()) && ExcelUtil.isHTML(emailContentInfoDTO.getFileName())) {
  53. String excelFilePath = emailContentInfoDTO.getFilePath().replace(".html", ".xlsx");
  54. emailFundNavDTOList = parseEmailContent(emailContent, excelFilePath, emailFieldMap);
  55. }
  56. // 2.解析邮件excel附件
  57. if (StrUtil.isNotBlank(emailContentInfoDTO.getFilePath()) && ExcelUtil.isExcel(emailContentInfoDTO.getFileName())) {
  58. List<EmailFundNavDTO> fundNavDTOList = parseExcelFile(emailContentInfoDTO.getFilePath(), emailFieldMap);
  59. Optional.ofNullable(fundNavDTOList).ifPresent(emailFundNavDTOList::addAll);
  60. }
  61. // 3.解析邮件pdf附件
  62. if (StrUtil.isNotBlank(emailContentInfoDTO.getFilePath()) && ExcelUtil.isPdf(emailContentInfoDTO.getFileName())) {
  63. String excelFilePath = path + emailContentInfoDTO.getEmailAddress() + "/" + emailContentInfoDTO.getEmailDate().substring(0, 10).replaceAll("-", "")
  64. + "/" + emailContentInfoDTO.getFileName().replace(".pdf", ".xlsx").replace(".PDF", ".xlsx");
  65. List<EmailFundNavDTO> fundNavDTOList = parsePdfFile(emailContentInfoDTO.getFilePath(), excelFilePath, emailFieldMap);
  66. Optional.ofNullable(fundNavDTOList).ifPresent(emailFundNavDTOList::addAll);
  67. }
  68. // 4.解析邮件zip,rar附件
  69. if (StrUtil.isNotBlank(emailContentInfoDTO.getFilePath()) && ExcelUtil.isZip(emailContentInfoDTO.getFileName())) {
  70. List<EmailFundNavDTO> fundNavDTOList = parsePackageFile(emailContentInfoDTO, emailContentInfoDTO.getFileName(), emailContentInfoDTO.getFilePath(), emailFieldMap);
  71. Optional.ofNullable(fundNavDTOList).ifPresent(emailFundNavDTOList::addAll);
  72. }
  73. // 兼容净值邮件,但附件是估值表的情况
  74. if (CollUtil.isEmpty(emailFundNavDTOList)) {
  75. // 判断文件名是否包含"估值表"
  76. String fileName = emailContentInfoDTO.getFileName();
  77. if (StrUtil.isNotBlank(fileName) && fileName.contains("估值表")) {
  78. emailFundNavDTOList = valuationEmailParser.parse(emailContentInfoDTO, emailFieldMap);
  79. }
  80. }
  81. // email_fund_nav增加template_id字段
  82. if (CollUtil.isNotEmpty(emailFundNavDTOList)) {
  83. emailFundNavDTOList.forEach(e -> e.setTemplateId(0));
  84. }
  85. // 通用模版解析 -> 根据配置的模板进行解析
  86. if (CollUtil.isEmpty(emailFundNavDTOList)) {
  87. emailFundNavDTOList = emailTemplateService.parseUsingTemplate(emailContentInfoDTO);
  88. }
  89. return emailFundNavDTOList;
  90. }
  91. private List<EmailFundNavDTO> parsePackageFile(EmailContentInfoDTO emailContentInfoDTO, String fileName, String filePath, Map<String, List<String>> emailFieldMap) {
  92. String destPath = filePath.replaceAll(".zip", "").replaceAll(".ZIP", "");
  93. log.info("压缩包地址:{},解压后文件地址:{}", filePath, destPath);
  94. List<String> dir = ExcelUtil.extractCompressedFiles(filePath, destPath);
  95. List<EmailFundNavDTO> emailFundNavDTOList = CollUtil.newArrayList();
  96. for (String zipFilePath : dir) {
  97. emailFundNavDTOList.addAll(parseZipFile(emailContentInfoDTO, zipFilePath, emailFieldMap));
  98. File file = new File(zipFilePath);
  99. if (file.isDirectory()) {
  100. for (String navFilePath : Objects.requireNonNull(file.list())) {
  101. emailFundNavDTOList.addAll(parseZipFile(emailContentInfoDTO, navFilePath, emailFieldMap));
  102. }
  103. }
  104. }
  105. return emailFundNavDTOList;
  106. }
  107. private List<EmailFundNavDTO> parseZipFile(EmailContentInfoDTO emailContentInfoDTO, String zipFilePath, Map<String, List<String>> emailFieldMap) {
  108. List<EmailFundNavDTO> fundNavDTOList = CollUtil.newArrayList();
  109. if (ExcelUtil.isPdf(zipFilePath)) {
  110. String excelFilePath = zipFilePath.replace(".pdf", ".xlsx").replace(".PDF", ".xlsx");
  111. fundNavDTOList = parsePdfFile(zipFilePath, excelFilePath, emailFieldMap);
  112. }
  113. if (ExcelUtil.isExcel(zipFilePath)) {
  114. fundNavDTOList = parseExcelFile(zipFilePath, emailFieldMap);
  115. }
  116. if (ExcelUtil.isZip(zipFilePath)) {
  117. String name = new File(zipFilePath).getName();
  118. fundNavDTOList = parsePackageFile(emailContentInfoDTO, name, zipFilePath, emailFieldMap);
  119. }
  120. return fundNavDTOList;
  121. }
  122. private List<EmailFundNavDTO> parsePdfFile(String filePath, String excelFilePath, Map<String, List<String>> emailFieldMap) {
  123. excelFilePath = ExcelUtil.pdfConvertToExcel(filePath, excelFilePath);
  124. if (StrUtil.isBlank(excelFilePath)) {
  125. return CollUtil.newArrayList();
  126. }
  127. return parseExcelFile(excelFilePath, emailFieldMap);
  128. }
  129. /**
  130. * 解析邮件excel附件
  131. *
  132. * @param filePath 邮件excel附件地址
  133. * @param emailFieldMap 邮件字段识别规则映射表
  134. * @return 解析到的净值数据
  135. */
  136. private List<EmailFundNavDTO> parseExcelFile(String filePath, Map<String, List<String>> emailFieldMap) {
  137. Sheet sheet = ExcelUtil.getFirstSheet(filePath);
  138. if (sheet == null) {
  139. log.info("获取不到有效的sheet页面,文件路径:{}", filePath);
  140. return CollUtil.newArrayList();
  141. }
  142. // 1.找到表头所在位置
  143. Map<String, Pair<Integer, Integer>> fieldPositionMap = getFieldPosition(sheet, emailFieldMap);
  144. if (MapUtil.isEmpty(fieldPositionMap)) {
  145. log.warn("找不到文件表头字段 -> 文件:{}", filePath);
  146. return CollUtil.newArrayList();
  147. }
  148. // 2.解析sheet中的净值数据
  149. List<EmailFundNavDTO> emailFundNavDTOList = parseSheetData(filePath, sheet, fieldPositionMap, null);
  150. // 3.校验净值数据格式
  151. if (CollUtil.isNotEmpty(emailFundNavDTOList)) {
  152. emailFundNavDTOList = emailFundNavDTOList.stream().filter(super::dataFormat).collect(Collectors.toList());
  153. }
  154. return emailFundNavDTOList;
  155. }
  156. /**
  157. * 解析邮件正文
  158. *
  159. * @param emailContent 正文内容
  160. * @param emailFieldMap 邮件字段识别规则映射表
  161. * @return 解析到的净值数据
  162. */
  163. private List<EmailFundNavDTO> parseEmailContent(String emailContent, String excelFilePath, Map<String, List<String>> emailFieldMap) {
  164. excelFilePath = ExcelUtil.contentConvertToExcel(emailContent, excelFilePath);
  165. if (StrUtil.isBlank(excelFilePath)) {
  166. return CollUtil.newArrayList();
  167. }
  168. return parseExcelFile(excelFilePath, emailFieldMap);
  169. }
  170. /**
  171. * 根据字段所在表格的位置提取净值数据
  172. *
  173. * @param filePath 文件路径·
  174. * @param sheet 表格中的sheet页
  175. * @param fieldPositionMap 字段所在表格中的位置
  176. * @param direction 表格数据的形式:1-行,2-列
  177. * @return 净值数据
  178. */
  179. private List<EmailFundNavDTO> parseSheetData(String filePath, Sheet sheet, Map<String, Pair<Integer, Integer>> fieldPositionMap, Integer direction) {
  180. List<EmailFundNavDTO> fundNavDTOList = CollUtil.newArrayList();
  181. // 通过表头所在位置判断是行数据还是列数据
  182. Integer dataDirectionType = direction != null ? direction : ExcelUtil.detectDataDirection(fieldPositionMap);
  183. // 数据起始行,起始列
  184. int initRow = dataDirectionType.equals(EmailDataDirectionConst.ROW_DIRECTION_TYPE) ? fieldPositionMap.values().stream().map(Pair::getKey).max(Integer::compareTo).orElse(0)
  185. : fieldPositionMap.values().stream().map(Pair::getKey).min(Integer::compareTo).orElse(0);
  186. int initColumn = fieldPositionMap.values().stream().map(Pair::getValue).min(Integer::compareTo).orElse(0);
  187. if (dataDirectionType.equals(EmailDataDirectionConst.ROW_DIRECTION_TYPE)) {
  188. // 表头字段-列号映射关系
  189. Map<String, Integer> fieldColumnMap = getFieldRow(fieldPositionMap);
  190. int lastRowNum = sheet.getLastRowNum();
  191. // 遍历可能的数据行
  192. for (int rowNum = initRow + 1; rowNum <= lastRowNum; rowNum++) {
  193. Row sheetRow = sheet.getRow(rowNum);
  194. try {
  195. Optional.ofNullable(readSheetRowData(sheetRow, fieldColumnMap)).ifPresent(fundNavDTOList::addAll);
  196. } catch (Exception e) {
  197. log.error("读取行数据报错 -> 行号:{},文件路径:{},堆栈信息:{}", rowNum, filePath, ExceptionUtil.stacktraceToString(e));
  198. }
  199. }
  200. }
  201. if (dataDirectionType.equals(EmailDataDirectionConst.COLUMN_DIRECTION_TYPE)) {
  202. // 表头字段-行号映射关系
  203. Map<Integer, String> fieldRowMap = getRowField(fieldPositionMap);
  204. int lastRow = fieldPositionMap.values().stream().map(Pair::getKey).max(Integer::compareTo).orElse(0);
  205. // 遍历每一列
  206. for (int columnNum = initColumn + 1; columnNum < EmailDataDirectionConst.MAX_ROW_COLUMN; columnNum++) {
  207. Map<String, String> fieldValueMap = MapUtil.newHashMap();
  208. for (int rowNum = initRow; rowNum <= lastRow; rowNum++) {
  209. Row row = sheet.getRow(rowNum);
  210. Cell cell = row.getCell(columnNum);
  211. if (cell == null) {
  212. continue;
  213. }
  214. fieldValueMap.put(fieldRowMap.get(rowNum), ExcelUtil.getCellValue(cell));
  215. }
  216. Optional.ofNullable(buildEmailFundNavDTO(fieldValueMap)).ifPresent(fundNavDTOList::add);
  217. }
  218. }
  219. // 兼容净值日期为空的情况
  220. addPriceDateIfMiss(fundNavDTOList, getPriceDateFromSheet(sheet, initRow));
  221. return fundNavDTOList;
  222. }
  223. private void addPriceDateIfMiss(List<EmailFundNavDTO> fundNavDTOList, String priceDate) {
  224. if (fundNavDTOList.stream().map(EmailFundNavDTO::getPriceDate).allMatch(StrUtil::isBlank)) {
  225. fundNavDTOList.forEach(e -> e.setPriceDate(priceDate));
  226. }
  227. }
  228. private String getPriceDateFromSheet(Sheet sheet, Integer maxRowNum) {
  229. Map<Integer, String> priceDateMap = MapUtil.newHashMap();
  230. for (int rowNum = 0; rowNum < maxRowNum; rowNum++) {
  231. Row row = sheet.getRow(rowNum);
  232. if (row == null) {
  233. continue;
  234. }
  235. int lastCellNum = row.getLastCellNum();
  236. for (int columnNum = 0; columnNum < lastCellNum; columnNum++) {
  237. Cell cell = row.getCell(columnNum);
  238. if (cell == null) {
  239. continue;
  240. }
  241. String cellValue = ExcelUtil.getCellValue(cell);
  242. if (StrUtil.isNotBlank(cellValue) && cellValue.contains("截至")) {
  243. int index = cellValue.indexOf("截至");
  244. String date = cellValue.substring(index + 2, index + 2 + 10);
  245. if (StrUtil.isNotBlank(date)) {
  246. date = date.replaceAll("年", "-").replaceAll("月", "-");
  247. }
  248. priceDateMap.put(1, date);
  249. continue;
  250. }
  251. String priceDate = DateUtils.stringToDate(cellValue);
  252. if (StrUtil.isNotBlank(priceDate)) {
  253. priceDateMap.put(2, priceDate);
  254. }
  255. }
  256. }
  257. if (MapUtil.isNotEmpty(priceDateMap)) {
  258. Integer key = priceDateMap.keySet().stream().min(Integer::compareTo).orElse(null);
  259. return priceDateMap.get(key);
  260. }
  261. return null;
  262. }
  263. private EmailFundNavDTO buildEmailFundNavDTO(Map<String, String> fieldValueMap) {
  264. if (MapUtil.isEmpty(fieldValueMap) || fieldValueMap.values().stream().allMatch(StrUtil::isBlank)) {
  265. return null;
  266. }
  267. EmailFundNavDTO fundNavDTO = new EmailFundNavDTO();
  268. fundNavDTO.setFundName(fieldValueMap.get(EmailFieldConst.FUND_NAME));
  269. fundNavDTO.setRegisterNumber(fieldValueMap.get(EmailFieldConst.REGISTER_NUMBER));
  270. fundNavDTO.setPriceDate(DateUtils.stringToDate(fieldValueMap.get(EmailFieldConst.PRICE_DATE)));
  271. fundNavDTO.setNav(fieldValueMap.get(EmailFieldConst.NAV));
  272. fundNavDTO.setCumulativeNavWithdrawal(fieldValueMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL));
  273. // pdf解析到的值带有",",比如:"10,656,097.37"
  274. String assetNet = fieldValueMap.get(EmailFieldConst.ASSET_NET);
  275. fundNavDTO.setAssetNet(ExcelUtil.numberDataStripCommas(assetNet));
  276. String assetShares = fieldValueMap.get(EmailFieldConst.ASSET_SHARE);
  277. fundNavDTO.setAssetShare(ExcelUtil.numberDataStripCommas(assetShares));
  278. return fundNavDTO;
  279. }
  280. private List<EmailFundNavDTO> readSheetRowData(Row sheetRow, Map<String, Integer> columnFieldMap) {
  281. if (sheetRow == null) {
  282. return null;
  283. }
  284. String nav = columnFieldMap.get(EmailFieldConst.NAV) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.NAV)) != null
  285. ? ExcelUtil.getCellValue(sheetRow.getCell(columnFieldMap.get(EmailFieldConst.NAV))) : null;
  286. String cumulativeNavWithdrawal = columnFieldMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL)) != null ?
  287. ExcelUtil.getCellValue(sheetRow.getCell(columnFieldMap.get(EmailFieldConst.CUMULATIVE_NAV_WITHDRAWAL))) : null;
  288. String assetNet = columnFieldMap.get(EmailFieldConst.ASSET_NET) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.ASSET_NET)) != null ?
  289. ExcelUtil.getCellValue(sheetRow.getCell(columnFieldMap.get(EmailFieldConst.ASSET_NET))) : null;
  290. if (StrUtil.isBlank(nav) && StrUtil.isBlank(cumulativeNavWithdrawal) && StrUtil.isBlank(assetNet)) {
  291. return null;
  292. }
  293. List<EmailFundNavDTO> fundNavDTOList = CollUtil.newArrayList();
  294. EmailFundNavDTO emailFundNavDTO = new EmailFundNavDTO();
  295. String priceDate = columnFieldMap.get(EmailFieldConst.PRICE_DATE) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PRICE_DATE)) != null ?
  296. ExcelUtil.getCellValue(sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PRICE_DATE))) : null;
  297. priceDate = DateUtils.stringToDate(priceDate);
  298. // 份额基金净值文件格式
  299. long parentFiledCount = columnFieldMap.keySet().stream().filter(e -> e.contains("parent")).count();
  300. if (parentFiledCount >= 1) {
  301. Optional.ofNullable(buildParentNav(sheetRow, columnFieldMap, priceDate)).ifPresent(fundNavDTOList::add);
  302. }
  303. emailFundNavDTO.setPriceDate(priceDate);
  304. String fundName = ExcelUtil.getPriorityFieldValue(sheetRow, columnFieldMap.get(EmailFieldConst.LEVEL_FUND_NAME), columnFieldMap.get(EmailFieldConst.FUND_NAME));
  305. emailFundNavDTO.setFundName(fundName);
  306. String registerNumber = ExcelUtil.getPriorityFieldValue(sheetRow, columnFieldMap.get(EmailFieldConst.LEVEL_REGISTER_NUMBER), columnFieldMap.get(EmailFieldConst.REGISTER_NUMBER));
  307. emailFundNavDTO.setRegisterNumber(registerNumber);
  308. emailFundNavDTO.setNav(nav);
  309. emailFundNavDTO.setCumulativeNavWithdrawal(cumulativeNavWithdrawal);
  310. String virtualNav = columnFieldMap.get(EmailFieldConst.VIRTUAL_NAV) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.VIRTUAL_NAV)) != null ?
  311. ExcelUtil.getCellValue(sheetRow.getCell(columnFieldMap.get(EmailFieldConst.VIRTUAL_NAV))) : null;
  312. emailFundNavDTO.setVirtualNav(virtualNav);
  313. emailFundNavDTO.setAssetNet(ExcelUtil.numberDataStripCommas(assetNet));
  314. String assetShares = columnFieldMap.get(EmailFieldConst.ASSET_SHARE) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.ASSET_SHARE)) != null ?
  315. ExcelUtil.getCellValue(sheetRow.getCell(columnFieldMap.get(EmailFieldConst.ASSET_SHARE))) : null;
  316. emailFundNavDTO.setAssetShare(ExcelUtil.numberDataStripCommas(assetShares));
  317. fundNavDTOList.add(emailFundNavDTO);
  318. return fundNavDTOList;
  319. }
  320. private EmailFundNavDTO buildParentNav(Row sheetRow, Map<String, Integer> columnFieldMap, String priceDate) {
  321. EmailFundNavDTO emailFundNavDTO = new EmailFundNavDTO();
  322. String nav = columnFieldMap.get(EmailFieldConst.PARENT_NAV) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_NAV)) != null ?
  323. ExcelUtil.getCellValue(sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_NAV))) : null;
  324. String cumulativeNavWithdrawal = columnFieldMap.get(EmailFieldConst.PARENT_CUMULATIVE_NAV_WITHDRAWAL) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_CUMULATIVE_NAV_WITHDRAWAL)) != null ?
  325. ExcelUtil.getCellValue(sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_CUMULATIVE_NAV_WITHDRAWAL))) : null;
  326. if (StrUtil.isBlank(nav) && StrUtil.isBlank(cumulativeNavWithdrawal)) {
  327. return null;
  328. }
  329. emailFundNavDTO.setPriceDate(priceDate);
  330. String fundName = columnFieldMap.get(EmailFieldConst.PARENT_FUND_NAME) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_FUND_NAME)).getStringCellValue() != null ?
  331. ExcelUtil.getCellValue(sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_FUND_NAME))) : null;
  332. emailFundNavDTO.setFundName(fundName);
  333. String registerNumber = columnFieldMap.get(EmailFieldConst.PARENT_REGISTER_NUMBER) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_REGISTER_NUMBER)) != null ?
  334. ExcelUtil.getCellValue(sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_REGISTER_NUMBER))) : null;
  335. emailFundNavDTO.setRegisterNumber(registerNumber);
  336. emailFundNavDTO.setNav(nav);
  337. emailFundNavDTO.setCumulativeNavWithdrawal(cumulativeNavWithdrawal);
  338. String virtualNav = columnFieldMap.get(EmailFieldConst.PARENT_VIRTUAL_NAV) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_VIRTUAL_NAV)) != null ?
  339. ExcelUtil.getCellValue(sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_VIRTUAL_NAV))) : null;
  340. emailFundNavDTO.setVirtualNav(virtualNav);
  341. String assetNet = columnFieldMap.get(EmailFieldConst.PARENT_ASSET_NET) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_ASSET_NET)) != null ?
  342. ExcelUtil.getCellValue(sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_ASSET_NET))) : null;
  343. emailFundNavDTO.setAssetNet(ExcelUtil.numberDataStripCommas(assetNet));
  344. String assetShares = columnFieldMap.get(EmailFieldConst.PARENT_ASSET_SHARE) != null && sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_ASSET_SHARE)) != null ?
  345. ExcelUtil.getCellValue(sheetRow.getCell(columnFieldMap.get(EmailFieldConst.PARENT_ASSET_SHARE))) : null;
  346. emailFundNavDTO.setAssetShare(ExcelUtil.numberDataStripCommas(assetShares));
  347. return emailFundNavDTO;
  348. }
  349. private Map<String, Integer> getFieldRow(Map<String, Pair<Integer, Integer>> fieldPositionMap) {
  350. // 考虑日期字段识别逻辑的问题
  351. long rowNumCount = fieldPositionMap.values().stream().map(Pair::getKey).distinct().count();
  352. if (rowNumCount > 1) {
  353. // 存在合并单元格的方式 -> 日期字段所在位置可能会存在错误
  354. fieldPositionMap.remove(EmailFieldConst.PRICE_DATE);
  355. }
  356. Map<String, Integer> fieldRowMap = MapUtil.newHashMap();
  357. for (Map.Entry<String, Pair<Integer, Integer>> fieldPositionEntry : fieldPositionMap.entrySet()) {
  358. String field = fieldPositionEntry.getKey();
  359. Integer column = fieldPositionEntry.getValue().getValue();
  360. fieldRowMap.put(field, column);
  361. }
  362. return fieldRowMap;
  363. }
  364. private Map<Integer, String> getRowField(Map<String, Pair<Integer, Integer>> fieldPositionMap) {
  365. Map<Integer, String> fieldRowMap = MapUtil.newHashMap();
  366. for (Map.Entry<String, Pair<Integer, Integer>> fieldPositionEntry : fieldPositionMap.entrySet()) {
  367. String field = fieldPositionEntry.getKey();
  368. Integer column = fieldPositionEntry.getValue().getKey();
  369. fieldRowMap.put(column, field);
  370. }
  371. return fieldRowMap;
  372. }
  373. /**
  374. * 找出excel中表头所在的位置
  375. *
  376. * @param sheet 表格工作簿
  377. * @param emailFieldMap 邮件字段识别规则映射表
  378. * @return excel中表头所在的位置(行, 列)
  379. */
  380. private Map<String, Pair<Integer, Integer>> getFieldPosition(Sheet sheet, Map<String, List<String>> emailFieldMap) {
  381. Map<String, List<FieldPositionDTO>> tempFieldPositionMap = MapUtil.newHashMap();
  382. int lastRowNum = sheet.getLastRowNum();
  383. for (int rowNum = 0; rowNum <= lastRowNum; rowNum++) {
  384. Row sheetRow = sheet.getRow(rowNum);
  385. if (sheetRow == null) {
  386. continue;
  387. }
  388. int lastCellNum = sheetRow.getLastCellNum();
  389. for (int cellNum = 0; cellNum < lastCellNum; cellNum++) {
  390. Cell cell = sheetRow.getCell(cellNum);
  391. if (cell == null) {
  392. continue;
  393. }
  394. String cellValue = ExcelUtil.getCellValue(cell);
  395. // 移除掉非中文字符
  396. String newCellValue = StringUtil.retainChineseCharacters(cellValue);
  397. String field = fieldMatch(newCellValue, emailFieldMap);
  398. if (StrUtil.isNotBlank(field)) {
  399. List<FieldPositionDTO> fieldPositionDTOList = tempFieldPositionMap.getOrDefault(field, new ArrayList<>());
  400. fieldPositionDTOList.add(new FieldPositionDTO(newCellValue, Pair.of(rowNum, cellNum)));
  401. tempFieldPositionMap.put(field, fieldPositionDTOList);
  402. }
  403. }
  404. }
  405. // 判断是不是份额基金净值文件格式(同时存在两个备案编码字段)
  406. return handlerFieldPosition(tempFieldPositionMap);
  407. }
  408. private Map<String, Pair<Integer, Integer>> handlerFieldPosition(Map<String, List<FieldPositionDTO>> tempFieldPositionMap) {
  409. Map<String, Pair<Integer, Integer>> fieldPositionMap = MapUtil.newHashMap();
  410. boolean hasParentField = tempFieldPositionMap.keySet().stream().anyMatch(e -> e.contains("parent"));
  411. for (Map.Entry<String, List<FieldPositionDTO>> entry : tempFieldPositionMap.entrySet()) {
  412. String field = entry.getKey();
  413. List<FieldPositionDTO> fieldPositionDTOList = entry.getValue();
  414. int size = fieldPositionDTOList.size();
  415. if (size == 1) {
  416. fieldPositionMap.put(field, fieldPositionDTOList.get(0).getPair());
  417. continue;
  418. }
  419. if ((!hasParentField && size > 1)) {
  420. if (EmailFieldConst.REGISTER_NUMBER.equals(field)) {
  421. Pair<Integer, Integer> pair = fieldPositionDTOList.stream()
  422. .filter(e -> !e.getFieldValue().contains("协会") && !e.getFieldValue().contains("备案")).map(FieldPositionDTO::getPair).findFirst().orElse(null);
  423. fieldPositionMap.put(field, pair);
  424. } else {
  425. fieldPositionMap.put(field, fieldPositionDTOList.get(size - 1).getPair());
  426. }
  427. continue;
  428. }
  429. if ((hasParentField && size > 1)) {
  430. fieldPositionMap.put(field, fieldPositionDTOList.get(0).getPair());
  431. }
  432. }
  433. // 母基金缺少代码的情况
  434. if (hasParentField && fieldPositionMap.get(EmailFieldConst.PARENT_REGISTER_NUMBER) == null) {
  435. List<FieldPositionDTO> fieldPositionDTOS = tempFieldPositionMap.get(EmailFieldConst.REGISTER_NUMBER);
  436. if (CollUtil.isNotEmpty(fieldPositionDTOS)) {
  437. Pair<Integer, Integer> parentRegisterNumberPair = fieldPositionDTOS.stream()
  438. .filter(e -> e.getFieldValue().contains("协会") || e.getFieldValue().contains("备案")).map(FieldPositionDTO::getPair).findFirst().orElse(null);
  439. fieldPositionMap.put(EmailFieldConst.PARENT_REGISTER_NUMBER, parentRegisterNumberPair);
  440. }
  441. }
  442. return fieldPositionMap;
  443. }
  444. /**
  445. * 判断单元格值是否为表头字段
  446. *
  447. * @param cellValue 单元格值
  448. * @param emailFieldMap 邮件字段识别规则映射表
  449. * @return 表头对应的标识
  450. */
  451. public String fieldMatch(String cellValue, Map<String, List<String>> emailFieldMap) {
  452. if (StrUtil.isBlank(cellValue)) {
  453. return null;
  454. }
  455. for (Map.Entry<String, List<String>> fieldEntry : emailFieldMap.entrySet()) {
  456. List<String> fieldList = fieldEntry.getValue();
  457. for (String field : fieldList) {
  458. if (cellValue.equals(field)) {
  459. return fieldEntry.getKey();
  460. }
  461. }
  462. }
  463. return null;
  464. }
  465. }