1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150 |
- package com.simuwang.daq.service;
- import cn.hutool.core.bean.BeanUtil;
- import cn.hutool.core.collection.CollUtil;
- import cn.hutool.core.collection.CollectionUtil;
- import cn.hutool.core.collection.ListUtil;
- import cn.hutool.core.date.DateUtil;
- import cn.hutool.core.exceptions.ExceptionUtil;
- import cn.hutool.core.map.MapUtil;
- import cn.hutool.core.util.StrUtil;
- import com.simuwang.base.common.conts.*;
- import com.simuwang.base.common.enums.ReportParserFileType;
- import com.simuwang.base.common.enums.ReportType;
- import com.simuwang.base.common.exception.ReportParseException;
- import com.simuwang.base.common.util.*;
- import com.simuwang.base.config.DaqProperties;
- import com.simuwang.base.config.EmailRuleConfig;
- import com.simuwang.base.mapper.*;
- import com.simuwang.base.pojo.dos.*;
- import com.simuwang.base.pojo.dto.EmailContentInfoDTO;
- import com.simuwang.base.pojo.dto.EmailFundNavDTO;
- import com.simuwang.base.pojo.dto.MailboxInfoDTO;
- import com.simuwang.base.pojo.dto.query.DataboardQuery;
- import com.simuwang.base.pojo.dto.report.ParseResult;
- import com.simuwang.base.pojo.dto.report.ReportData;
- import com.simuwang.base.pojo.dto.report.ReportParseStatus;
- import com.simuwang.base.pojo.dto.report.ReportParserParams;
- import com.simuwang.base.pojo.valuation.CmValuationTableAttribute;
- import com.simuwang.base.pojo.vo.*;
- import com.simuwang.daq.components.report.parser.ReportParser;
- import com.simuwang.daq.components.report.parser.ReportParserFactory;
- import com.simuwang.daq.components.report.writer.ReportWriter;
- import com.simuwang.daq.components.report.writer.ReportWriterFactory;
- import jakarta.mail.*;
- import jakarta.mail.internet.MimeMessage;
- import jakarta.mail.internet.MimeMultipart;
- import jakarta.mail.search.ComparisonTerm;
- import jakarta.mail.search.ReceivedDateTerm;
- import jakarta.mail.search.SearchTerm;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Service;
- import org.springframework.util.StopWatch;
- import java.io.File;
- import java.math.BigDecimal;
- import java.util.*;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import java.util.stream.Collectors;
- /**
- * @author mozuwen
- * @date 2024-09-04
- * @description 邮件解析服务
- */
- @Service
- public class EmailParseService {
- public static final int stepSize = 10000;
- private static final Logger log = LoggerFactory.getLogger(EmailParseService.class);
- private final EmailTypeRuleMapper emailTypeRuleMapper;
- private final EmailRuleConfig emailRuleConfig;
- private final EmailFieldMappingMapper emailFieldMapper;
- private final EmailParserFactory emailParserFactory;
- private final EmailParseInfoMapper emailParseInfoMapper;
- private final EmailFileInfoMapper emailFileInfoMapper;
- private final EmailFundNavMapper emailFundNavMapper;
- private final EmailFundAssetMapper emailFundAssetMapper;
- private final AssetMapper assetMapper;
- private final NavMapper navMapper;
- private final FundService fundService;
- private final FundAliasMapper fundAliasMapper;
- private final ValuationTableMapper valuationTableMapper;
- private final ValuationTableAttributeMapper valuationTableAttributeMapper;
- private final FundPositionDetailMapper fundPositionDetailMapper;
- private final DistributionMapper distributionMapper;
- private final CompanyInformationMapper companyInformationMapper;
- @Autowired
- private FundInfoMapper fundInfoMapper;
- @Value("${email.file.path}")
- private String path;
- @Autowired
- private DaqProperties properties;
- /* 报告解析和入库的方法 */
- @Autowired
- private ReportParserFactory reportParserFactory;
- @Autowired
- private ReportWriterFactory reportWriterFactory;
- public EmailParseService(EmailTypeRuleMapper emailTypeRuleMapper, EmailRuleConfig emailRuleConfig,
- EmailFieldMappingMapper emailFieldMapper, EmailParserFactory emailParserFactory,
- EmailParseInfoMapper emailParseInfoMapper, EmailFileInfoMapper emailFileInfoMapper,
- EmailFundNavMapper emailFundNavMapper, EmailFundAssetMapper emailFundAssetMapper,
- AssetMapper assetMapper, NavMapper navMapper, FundService fundService,
- FundAliasMapper fundAliasMapper,
- ValuationTableMapper valuationTableMapper, ValuationTableAttributeMapper valuationTableAttributeMapper,
- FundPositionDetailMapper fundPositionDetailMapper, DistributionMapper distributionMapper, CompanyInformationMapper companyInformationMapper) {
- this.emailTypeRuleMapper = emailTypeRuleMapper;
- this.emailRuleConfig = emailRuleConfig;
- this.emailFieldMapper = emailFieldMapper;
- this.emailParserFactory = emailParserFactory;
- this.emailParseInfoMapper = emailParseInfoMapper;
- this.emailFileInfoMapper = emailFileInfoMapper;
- this.emailFundNavMapper = emailFundNavMapper;
- this.emailFundAssetMapper = emailFundAssetMapper;
- this.assetMapper = assetMapper;
- this.navMapper = navMapper;
- this.fundService = fundService;
- this.fundAliasMapper = fundAliasMapper;
- this.valuationTableMapper = valuationTableMapper;
- this.valuationTableAttributeMapper = valuationTableAttributeMapper;
- this.fundPositionDetailMapper = fundPositionDetailMapper;
- this.distributionMapper = distributionMapper;
- this.companyInformationMapper = companyInformationMapper;
- }
- /**
- * 解析指定邮箱指定时间范围内的邮件
- *
- * @param mailboxInfoDTO 邮箱配置信息
- * @param startDate 邮件起始日期(yyyy-MM-dd HH:mm:ss)
- * @param endDate 邮件截止日期(yyyy-MM-dd HH:mm:ss, 为null,将解析邮件日期小于等于startDate的当天邮件)
- */
- public void parseEmail(MailboxInfoDTO mailboxInfoDTO, Date startDate, Date endDate) {
- log.info("开始邮件解析 -> 邮箱信息:{},开始时间:{},结束时间:{}", mailboxInfoDTO, DateUtil.format(startDate, DateConst.YYYY_MM_DD_HH_MM_SS), DateUtil.format(endDate, DateConst.YYYY_MM_DD_HH_MM_SS));
- // 邮件类型配置
- Map<Integer, List<String>> emailTypeMap = getEmailType();
- // 邮件字段识别映射表
- Map<String, List<String>> emailFieldMap = getEmailFieldMapping();
- Map<String, List<EmailContentInfoDTO>> emailContentMap;
- try {
- emailContentMap = realEmail(mailboxInfoDTO, emailTypeMap, startDate, endDate);
- } catch (Exception e) {
- log.info("采集邮件失败 -> 邮箱配置信息:{},堆栈信息:{}", mailboxInfoDTO, ExceptionUtil.stacktraceToString(e));
- return;
- }
- if (MapUtil.isEmpty(emailContentMap)) {
- log.info("未采集到邮件 -> 邮箱配置信息:{},开始时间:{},结束时间:{}", mailboxInfoDTO, DateUtil.format(startDate, DateConst.YYYY_MM_DD_HH_MM_SS), DateUtil.format(endDate, DateConst.YYYY_MM_DD_HH_MM_SS));
- return;
- }
- for (Map.Entry<String, List<EmailContentInfoDTO>> emailEntry : emailContentMap.entrySet()) {
- List<EmailContentInfoDTO> emailContentInfoDTOList = emailEntry.getValue();
- if (CollUtil.isEmpty(emailContentInfoDTOList)) {
- log.warn("未采集到正文或附件");
- continue;
- }
- log.info("开始解析邮件数据 -> 邮件主题:{},邮件日期:{}", emailContentInfoDTOList.get(0).getEmailTitle(), emailContentInfoDTOList.get(0).getEmailDate());
- List<EmailFundNavDTO> emailFundNavDTOList = CollUtil.newArrayList();
- Map<EmailContentInfoDTO, List<EmailFundNavDTO>> fileNameNavMap = MapUtil.newHashMap();
- for (EmailContentInfoDTO emailContentInfoDTO : emailContentInfoDTOList) {
- try {
- List<EmailFundNavDTO> fundNavDTOList = parseEmail(emailContentInfoDTO, emailFieldMap);
- fileNameNavMap.put(emailContentInfoDTO, fundNavDTOList);
- emailFundNavDTOList.addAll(fundNavDTOList);
- } catch (Exception e) {
- log.error("堆栈信息:{}", ExceptionUtil.stacktraceToString(e));
- }
- }
- // 保存相关信息 -> 邮件信息表,邮件文件表,邮件净值表,邮件规模表,基金净值表
- saveRelatedTable(mailboxInfoDTO.getAccount(), emailContentInfoDTOList, fileNameNavMap);
- log.info("结束邮件解析 -> 邮箱信息:{},开始时间:{},结束时间:{}", mailboxInfoDTO, DateUtil.format(startDate, DateConst.YYYY_MM_DD_HH_MM_SS), DateUtil.format(endDate, DateConst.YYYY_MM_DD_HH_MM_SS));
- }
- }
- public List<EmailFundNavDTO> parseEmail(EmailContentInfoDTO emailContentInfoDTO, Map<String, List<String>> emailFieldMap) {
- Integer emailType = emailContentInfoDTO.getEmailType();
- AbstractEmailParser emailParser = emailParserFactory.getInstance(emailType);
- return emailParser.parse(emailContentInfoDTO, emailFieldMap);
- }
- public void saveRelatedTable(String emailAddress, List<EmailContentInfoDTO> emailContentInfoDTOList, Map<EmailContentInfoDTO, List<EmailFundNavDTO>> fileNameNavMap) {
- String emailTitle = CollUtil.isNotEmpty(emailContentInfoDTOList) ? emailContentInfoDTOList.get(0).getEmailTitle() : null;
- String emailDate = CollUtil.isNotEmpty(emailContentInfoDTOList) ? emailContentInfoDTOList.get(0).getEmailDate() : null;
- Integer emailType = CollUtil.isNotEmpty(emailContentInfoDTOList) ? emailContentInfoDTOList.get(0).getEmailType() : null;
- Integer emailId = CollUtil.isNotEmpty(emailContentInfoDTOList) ? emailContentInfoDTOList.get(0).getEmailId() : null;
- String senderEmail = CollUtil.isNotEmpty(emailContentInfoDTOList) ? emailContentInfoDTOList.get(0).getSenderEmail() : null;
- Date parseDate = new Date();
- int emailParseStatus = EmailParseStatusConst.SUCCESS;
- EmailParseInfoDO emailParseInfoDO = buildEmailParseInfo(emailId, emailAddress, senderEmail, emailDate, emailTitle, emailType, emailParseStatus, parseDate);
- emailId = saveEmailParseInfo(emailParseInfoDO);
- // python 报告解析接口结果
- List<ParseResult<ReportData>> dataList = ListUtil.list(false);
- for (Map.Entry<EmailContentInfoDTO, List<EmailFundNavDTO>> fileNameNavEntry : fileNameNavMap.entrySet()) {
- // 保存邮件文件表
- EmailContentInfoDTO emailContentInfoDTO = fileNameNavEntry.getKey();
- String fileName = emailContentInfoDTO.getFileName();
- if (Objects.equals(EmailTypeConst.REPORT_EMAIL_TYPE, emailType) && fileName.endsWith(".html")) {
- continue;
- }
- Integer fileId = saveEmailFileInfo(emailId, emailContentInfoDTO.getFileId(), fileName, emailContentInfoDTO.getFilePath(), parseDate);
- List<EmailFundNavDTO> fundNavDTOList = fileNameNavEntry.getValue();
- if (CollUtil.isNotEmpty(fundNavDTOList)) {
- // 过滤出解析成功的数据
- fundNavDTOList = fundNavDTOList.stream().filter(e -> e != null && StrUtil.isBlank(e.getFailReason())).toList();
- }
- if (CollUtil.isEmpty(fundNavDTOList) && !Objects.equals(EmailTypeConst.REPORT_EMAIL_TYPE, emailType)) {
- continue;
- }
- if (Objects.equals(EmailTypeConst.REPORT_EMAIL_TYPE, emailType)) {
- // 解析结果(可以从python获取或者自行解析)并保存报告
- ParseResult<ReportData> parseResult = this.parseReportAndHandleResult(fileId, emailContentInfoDTO);
- dataList.add(parseResult);
- }
- for (EmailFundNavDTO fundNavDTO : fundNavDTOList) {
- // 设置净值数据的解析状态
- setNavParseStatus(fundNavDTO, emailTitle);
- }
- // 保存净值表和规模表
- saveNavAndAssetNet(fileId, fundNavDTOList, parseDate);
- saveValuationInfo(fileId, fundNavDTOList);
- }
- // 更新邮件解析结果 -> 当【净值日期】和【备案编码/基金名称】能正常解读,即识别为【成功】
- long successNavCount = fileNameNavMap.values().stream().flatMap(List::stream).filter(e -> e != null && StrUtil.isBlank(e.getFailReason())).count();
- emailParseStatus = successNavCount >= 1 ? EmailParseStatusConst.SUCCESS : EmailParseStatusConst.FAIL;
- String failReason = null;
- if (emailParseStatus == EmailParseStatusConst.FAIL) {
- // 邮件解析失败时 -> 保存失败原因
- int hasPdfFile = emailContentInfoDTOList.stream().map(EmailContentInfoDTO::getFilePath).anyMatch(ExcelUtil::isPdf) ? 1 : 0;
- List<EmailFundNavDTO> navDTOList = fileNameNavMap.values().stream().flatMap(List::stream).toList();
- failReason = hasPdfFile == 1 && CollUtil.isEmpty(navDTOList) ? "无法从PDF文件中获取到数据" : navDTOList.stream().map(EmailFundNavDTO::getFailReason).distinct().collect(Collectors.joining("/"));
- if (StrUtil.isBlank(failReason)) {
- failReason = "模板不支持";
- }
- }
- // 报告邮件有一条失败就表示整个邮件解析失败
- if (Objects.equals(EmailTypeConst.REPORT_EMAIL_TYPE, emailType) && CollUtil.isNotEmpty(dataList)) {
- long sucNum = dataList.stream().filter(e -> Objects.equals(1, e.getStatus())).count();
- if (sucNum > 0) {
- emailParseStatus = EmailParseStatusConst.SUCCESS;
- } else {
- emailParseStatus = EmailParseStatusConst.FAIL;
- failReason = dataList.stream().map(ParseResult::getMsg).collect(Collectors.joining("/"));
- }
- }
- emailParseInfoMapper.updateParseStatus(emailId, emailParseStatus, failReason);
- }
- private void saveValuationInfo(Integer fileId, List<EmailFundNavDTO> fundNavDTOList) {
- if (CollUtil.isEmpty(fundNavDTOList)) {
- return;
- }
- valuationTableMapper.unValid(fileId);
- for (EmailFundNavDTO fundNavDTO : fundNavDTOList) {
- ValuationTableDO valuationTableDO = fundNavDTO.getValuationTableDO();
- if (valuationTableDO == null) {
- continue;
- }
- List<String> fundIdList = fundNavDTO.getFundIdList();
- valuationTableDO.setFileId(fileId);
- List<FundPositionDetailDO> fundPositionDetailDOList = fundNavDTO.getFundPositionDetailDOList();
- List<CmValuationTableAttribute> valuationTableAttributeList = fundNavDTO.getValuationTableAttributeList();
- if (CollUtil.isEmpty(fundIdList)) {
- valuationTableMapper.insert(valuationTableDO);
- int valuationId = valuationTableDO.getId();
- saveValuationTableAttribute(valuationId, valuationTableAttributeList);
- fundPositionDetailDOList.forEach(e -> e.setValuationId(valuationId));
- // 不匹配基金的情况下 -> 不写fund_position_detail
- // saveFundPositionDetail(fundPositionDetailDOList, null, fundNavDTO.getPriceDate());
- continue;
- }
- for (String fundId : fundIdList) {
- valuationTableDO.setFundId(fundId);
- valuationTableMapper.insert(valuationTableDO);
- int valuationId = valuationTableDO.getId();
- fundPositionDetailDOList.forEach(e -> {
- e.setFundId(fundId);
- e.setValuationId(valuationId);
- });
- saveValuationTableAttribute(valuationId, valuationTableAttributeList);
- saveFundPositionDetail(fundPositionDetailDOList, fundId, fundNavDTO.getPriceDate());
- }
- }
- }
- public void saveFundPositionDetail(List<FundPositionDetailDO> fundPositionDetails, String fundId, String valuationDate) {
- int subBegin = 0;
- int subEnd = stepSize;
- int insertNum = 0;
- int hasStock = 0;
- int hasBond = 0;
- int hasFuture = 0;
- if (CollectionUtil.isNotEmpty(fundPositionDetails)) {
- //插入持仓数据 cm_fund_position_detail(记录数较多,得分批)
- // 先删除原先的数据 然后写入数据
- fundPositionDetailMapper.deleteUnUsed(fundId, valuationDate);
- // 将最终结果写入 cm_fund_position_detail
- while (subBegin < fundPositionDetails.size()) {
- List<FundPositionDetailDO> segment = fundPositionDetails.subList(subBegin, Math.min(subEnd, fundPositionDetails.size()));
- for (FundPositionDetailDO fundPositionDetail : segment) {
- //实收信托、实收资本对应的编码为107
- if (StrUtil.isNotBlank(fundPositionDetail.getSecuritiesName()) && "实收信托".equals(fundPositionDetail.getSecuritiesName().trim())) {
- fundPositionDetail.setSubjectCode("107");
- fundPositionDetail.setSecuritiesCode("107");
- }
- //设置创建者id,在拿取最近修改人时有用
- fundPositionDetail.setCreatorId(0);
- if (fundPositionDetail.getLevel() != null && fundPositionDetail.getLevel() >= 4) {
- Integer secType = fundPositionDetail.getSecType();
- Integer subType = fundPositionDetail.getSubType();
- if (secType != null && subType != null) {
- if (secType == HoldingType.Stock.getId() && subType == HoldingType.Stock.getId()) {
- hasStock = 1;
- }
- // 正逆回购不属于债券
- String subjectCode = fundPositionDetail.getSubjectCode();
- String[] SALE_CODES = {"2202", "1202"};
- if (secType == HoldingType.Bond.getId() && !StrUtil.containsAny(subjectCode, SALE_CODES)) {
- hasBond = 1;
- }
- if (secType == HoldingType.Future.getId() || secType == HoldingType.Option.getId()) {
- hasFuture = 1;
- }
- }
- }
- }
- insertNum += fundPositionDetailMapper.insertMulti(segment);
- subBegin = subEnd;
- subEnd += stepSize;
- }
- }
- // 更新 cm_user_valuation_table
- if (insertNum > 0) {
- valuationTableMapper.updateUpdateAnalyzed(fundId, valuationDate, hasStock, hasBond, hasFuture);
- }
- }
- private void saveValuationTableAttribute(Integer valuationId, List<CmValuationTableAttribute> valuationTableAttributes) {
- if (valuationId == null || CollUtil.isEmpty(valuationTableAttributes)) {
- return;
- }
- List<ValuationTableAttributeDO> valuationTableAttributeDOList = buildValuationTableAttributeDO(valuationId, valuationTableAttributes);
- valuationTableAttributeMapper.deleteByValuationId(valuationId);
- if (CollUtil.isNotEmpty(valuationTableAttributeDOList)) {
- valuationTableAttributeMapper.batchInsert(valuationTableAttributeDOList);
- }
- }
- private List<ValuationTableAttributeDO> buildValuationTableAttributeDO(Integer valuationId, List<CmValuationTableAttribute> valuationTableAttributes) {
- if (CollUtil.isEmpty(valuationTableAttributes)) {
- return CollUtil.newArrayList();
- }
- return valuationTableAttributes.stream().map(e -> {
- ValuationTableAttributeDO tableAttributeDO = new ValuationTableAttributeDO();
- tableAttributeDO.setValuationId(valuationId);
- tableAttributeDO.setSubjectCode(e.getOriginalSubjectCode());
- tableAttributeDO.setSubjectName(e.getSubjectName());
- tableAttributeDO.setCurrency(e.getCurrency());
- tableAttributeDO.setExchangeRate(e.getExchangeRate());
- tableAttributeDO.setSecuritiesAmount(e.getSecuritiesAmount());
- tableAttributeDO.setUnitCost(e.getUnitCost());
- tableAttributeDO.setOriCurrencyCost(e.getOCurrencyCost());
- tableAttributeDO.setNetCost(e.getNetCost());
- tableAttributeDO.setNetCostRatio(e.getNetCostRatio());
- tableAttributeDO.setMarketPrice(e.getMarketPrice());
- tableAttributeDO.setOriCurrencyMarketValue(e.getOCurrencyMarketValue());
- tableAttributeDO.setMarketValue(e.getMarketValue());
- tableAttributeDO.setMarketValueRatio(e.getMarketValueRatio());
- tableAttributeDO.setOriCurrencyIncrement(e.getOCurrencyMarketValue());
- tableAttributeDO.setIncrement(e.getIncrement());
- tableAttributeDO.setHaltInfo(e.getHaltInfo());
- tableAttributeDO.setRightsInterestsInfo(e.getRightsInterestsInfo());
- tableAttributeDO.setIsvalid(1);
- tableAttributeDO.setCreatorId(0);
- tableAttributeDO.setUpdaterId(0);
- tableAttributeDO.setCreateTime(new Date());
- tableAttributeDO.setUpdateTime(new Date());
- return tableAttributeDO;
- }).collect(Collectors.toList());
- }
- private ParseResult<ReportData> parseReportAndHandleResult(int fileId, EmailContentInfoDTO emailContentInfoDTO) {
- ParseResult<ReportData> result = new ParseResult<>();
- String fileName = emailContentInfoDTO.getFileName();
- Integer emailType = emailContentInfoDTO.getEmailType();
- if (!Objects.equals(EmailTypeConst.REPORT_EMAIL_TYPE, emailType) || StrUtil.isBlank(fileName)) {
- result.setStatus(ReportParseStatus.NOT_A_REPORT.getCode());
- result.setMsg(ReportParseStatus.NOT_A_REPORT.getMsg());
- return result;
- }
- Pattern pattern = Pattern.compile("S(?:[A-Z]{0}[0-9]{5}|[A-Z][0-9]{4}|[A-Z]{2}[0-9]{3}|[A-Z]{3}[0-9]{2})");
- Matcher matcher = pattern.matcher(fileName);
- String registerNumber = null;
- if (matcher.find()) {
- registerNumber = matcher.group();
- }
- // 类型识别---先识别季度报告,没有季度再识别年度报告,最后识别月报
- ReportType reportType = null;
- if (StrUtil.containsAny(fileName, ReportType.QUARTERLY.getPatterns())) {
- reportType = ReportType.QUARTERLY;
- } else if (StrUtil.containsAny(fileName, ReportType.ANNUALLY.getPatterns())) {
- reportType = ReportType.ANNUALLY;
- } else if (StrUtil.containsAny(fileName, ReportType.MONTHLY.getPatterns())) {
- reportType = ReportType.MONTHLY;
- }
- // 解析器--如果开启python解析则直接调用python接口,否则根据文件后缀获取对应解析器
- ReportParserFileType fileType;
- if (Objects.equals(Boolean.TRUE, this.properties.getEnablePyParser())) {
- fileType = ReportParserFileType.PYTHON;
- } else {
- String fileSuffix = StrUtil.subAfter(fileName, ".", true);
- fileType = ReportParserFileType.getBySuffix(fileSuffix);
- }
- // 不支持的格式
- if (fileType == null) {
- result.setStatus(ReportParseStatus.NO_SUPPORT_TEMPLATE.getCode());
- result.setMsg(StrUtil.format(ReportParseStatus.NO_SUPPORT_TEMPLATE.getMsg(), fileName));
- return result;
- }
- // 不是定期报告的判断逻辑放在不支持的格式下面
- if (reportType == null) {
- result.setStatus(ReportParseStatus.NOT_A_REPORT.getCode());
- result.setMsg(StrUtil.format(ReportParseStatus.NOT_A_REPORT.getMsg(), fileName));
- return result;
- }
- // 解析报告
- ReportData reportData = null;
- StopWatch parserWatch = new StopWatch();
- parserWatch.start();
- try {
- ReportParserParams params = ReportParserParams.builder().fileId(fileId).filename(fileName)
- .filepath(emailContentInfoDTO.getFilePath()).registerNumber(registerNumber).build();
- ReportParser<ReportData> instance = this.reportParserFactory.getInstance(reportType, fileType);
- reportData = instance.parse(params);
- result.setStatus(1);
- result.setMsg("报告解析成功");
- result.setData(reportData);
- } catch (ReportParseException e) {
- log.error("解析失败\n{}", e.getMsg());
- result.setStatus(e.getCode());
- result.setMsg(e.getMsg());
- } catch (Exception e) {
- log.error("解析错误\n{}", ExceptionUtil.stacktraceToString(e));
- result.setStatus(ReportParseStatus.PARSE_FAIL.getCode());
- result.setMsg(StrUtil.format(ReportParseStatus.PARSE_FAIL.getMsg(), e.getMessage()));
- } finally {
- parserWatch.stop();
- if (log.isInfoEnabled()) {
- log.info("报告{}解析结果为{},耗时{}ms", fileName, reportData, parserWatch.getTotalTimeMillis());
- }
- }
- // 保存报告解析结果
- if (reportData != null) {
- StopWatch writeWatch = new StopWatch();
- writeWatch.start();
- try {
- ReportWriter<ReportData> instance = this.reportWriterFactory.getInstance(reportType);
- instance.write(reportData);
- } catch (Exception e) {
- log.error("报告{}结果保存失败\n{}", fileName, ExceptionUtil.stacktraceToString(e));
- } finally {
- writeWatch.stop();
- if (log.isInfoEnabled()) {
- log.info("报告{}解析结果保存完成,耗时{}ms", fileName, writeWatch.getTotalTimeMillis());
- }
- }
- }
- return result;
- }
- private void saveNavAndAssetNet(Integer fileId, List<EmailFundNavDTO> fundNavDTOList, Date parseDate) {
- if (CollUtil.isEmpty(fundNavDTOList)) {
- return;
- }
- // 净值数据
- List<EmailFundNavDO> emailFundNavDOList = fundNavDTOList.stream()
- .map(e -> buildEmailFundNavDo(fileId, e, parseDate)).filter(CollUtil::isNotEmpty).flatMap(List::stream).collect(Collectors.toList());
- if (CollUtil.isNotEmpty(emailFundNavDOList)) {
- // 先删除文件id下的净值数据(考虑到重新解析的需求,如果是首次解析,那么file_id下不存在净值数据)
- emailFundNavMapper.deleteByFileId(fileId);
- emailFundNavMapper.batchInsert(emailFundNavDOList);
- List<NavDO> navDOList = emailFundNavDOList.stream().filter(e -> StrUtil.isNotBlank(e.getFundId()))
- .map(e -> BeanUtil.copyProperties(e, NavDO.class)).collect(Collectors.toList());
- saveNavDo(navDOList);
- }
- // 保存规模数据
- List<EmailFundAssetDO> emailFundAssetDOList = fundNavDTOList.stream()
- .map(e -> buildEmailFundAssetDo(fileId, e, parseDate)).filter(CollUtil::isNotEmpty).flatMap(List::stream).collect(Collectors.toList());
- if (CollUtil.isNotEmpty(emailFundAssetDOList)) {
- // 先删除file_id下的规模数据(考虑到重新解析的需求,如果是首次解析,那么file_id下不存在规模数据)
- emailFundAssetMapper.deleteByFileId(fileId);
- emailFundAssetMapper.batchInsert(emailFundAssetDOList);
- List<AssetDO> assetDOList = emailFundAssetDOList.stream().filter(e -> StrUtil.isNotBlank(e.getFundId()))
- .map(e -> BeanUtil.copyProperties(e, AssetDO.class)).collect(Collectors.toList());
- saveAssetDo(assetDOList);
- }
- }
- public void saveNavDo(List<NavDO> navDOList) {
- if (CollUtil.isEmpty(navDOList)) {
- return;
- }
- Map<String, List<NavDO>> fundIdNavMap = navDOList.stream().collect(Collectors.groupingBy(NavDO::getFundId));
- for (Map.Entry<String, List<NavDO>> entry : fundIdNavMap.entrySet()) {
- List<NavDO> navDOS = entry.getValue();
- List<String> priceDateList = navDOS.stream().map(NavDO::getPriceDate).map(e -> DateUtil.format(e, DateConst.YYYY_MM_DD)).collect(Collectors.toList());
- List<String> dateList = navMapper.queryFundNavByDate(entry.getKey(), priceDateList);
- List<NavDO> updateNavDoList = navDOS.stream().filter(e -> dateList.contains(DateUtil.format(e.getPriceDate(), DateConst.YYYY_MM_DD))).collect(Collectors.toList());
- List<NavDO> insertNavDoList = navDOS.stream().filter(e -> !dateList.contains(DateUtil.format(e.getPriceDate(), DateConst.YYYY_MM_DD))).collect(Collectors.toList());
- if (CollUtil.isNotEmpty(insertNavDoList)) {
- Map<Date, List<NavDO>> priceDateNavDoListMap = insertNavDoList.stream().collect(Collectors.groupingBy(NavDO::getPriceDate));
- boolean hasDuplicationDateData = priceDateNavDoListMap.values().stream().map(List::size).anyMatch(e -> e > 1);
- if (!hasDuplicationDateData) {
- navMapper.batchInsert(insertNavDoList);
- }
- // 要插入的数据中存在相同日期的数据 -> 只能一条一条的插入数据了
- insertNavDoList.forEach(e -> saveNavDo(ListUtil.toList(e)));
- }
- if (CollUtil.isNotEmpty(updateNavDoList)) {
- navMapper.batchUpdate(updateNavDoList);
- }
- }
- }
- public void saveAssetDo(List<AssetDO> assetDOList) {
- if (CollUtil.isEmpty(assetDOList)) {
- return;
- }
- Map<String, List<AssetDO>> fundIdNavMap = assetDOList.stream().collect(Collectors.groupingBy(AssetDO::getFundId));
- for (Map.Entry<String, List<AssetDO>> entry : fundIdNavMap.entrySet()) {
- List<AssetDO> assetDOS = entry.getValue();
- List<String> priceDateList = assetDOS.stream().map(AssetDO::getPriceDate).map(e -> DateUtil.format(e, DateConst.YYYY_MM_DD)).collect(Collectors.toList());
- List<String> dateList = assetMapper.queryFundNavByDate(entry.getKey(), priceDateList);
- List<AssetDO> updateAssetDoList = assetDOS.stream().filter(e -> dateList.contains(DateUtil.format(e.getPriceDate(), DateConst.YYYY_MM_DD))).collect(Collectors.toList());
- List<AssetDO> insertAssetDoList = assetDOS.stream().filter(e -> !dateList.contains(DateUtil.format(e.getPriceDate(), DateConst.YYYY_MM_DD))).collect(Collectors.toList());
- if (CollUtil.isNotEmpty(insertAssetDoList)) {
- Map<Date, List<AssetDO>> priceDateAssetDoListMap = insertAssetDoList.stream().collect(Collectors.groupingBy(AssetDO::getPriceDate));
- boolean hasDuplicationDateData = priceDateAssetDoListMap.values().stream().map(List::size).anyMatch(e -> e > 1);
- if (!hasDuplicationDateData) {
- assetMapper.batchInsert(insertAssetDoList);
- }
- // 要插入的数据中存在相同日期的数据 -> 只能一条一条的插入数据了
- insertAssetDoList.forEach(e -> saveAssetDo(ListUtil.toList(e)));
- }
- if (CollUtil.isNotEmpty(updateAssetDoList)) {
- assetMapper.batchUpdate(updateAssetDoList);
- }
- }
- }
- private List<EmailFundAssetDO> buildEmailFundAssetDo(Integer fileId, EmailFundNavDTO fundNavDTO, Date parseDate) {
- List<EmailFundAssetDO> fundAssetDOList = CollUtil.newArrayList();
- BigDecimal assetNet = StrUtil.isNotBlank(fundNavDTO.getAssetNet()) ? new BigDecimal(fundNavDTO.getAssetNet()) : null;
- BigDecimal assetShare = StrUtil.isNotBlank(fundNavDTO.getAssetShare()) ? new BigDecimal(fundNavDTO.getAssetShare()) : null;
- if (assetNet == null) {
- return fundAssetDOList;
- }
- Integer isStored = fundNavDTO.getParseStatus() != null
- && (fundNavDTO.getParseStatus().equals(NavParseStatusConst.ASSET_NET_NEGATIVE) || fundNavDTO.getParseStatus().equals(NavParseStatusConst.SUCCESS)) ? 1 : 0;
- Date priceDate = DateUtil.parse(fundNavDTO.getPriceDate(), DateConst.YYYY_MM_DD);
- if (CollUtil.isNotEmpty(fundNavDTO.getFundIdList())) {
- for (String fundId : fundNavDTO.getFundIdList()) {
- EmailFundAssetDO emailFundAssetDO = new EmailFundAssetDO();
- emailFundAssetDO.setFileId(fileId);
- emailFundAssetDO.setPriceDate(priceDate);
- emailFundAssetDO.setFundId(fundId);
- emailFundAssetDO.setFundName(fundNavDTO.getFundName());
- emailFundAssetDO.setRegisterNumber(fundNavDTO.getRegisterNumber());
- emailFundAssetDO.setAssetNet(assetNet);
- emailFundAssetDO.setAssetShare(assetShare);
- emailFundAssetDO.setIsStored(isStored);
- emailFundAssetDO.setExceptionStatus(fundNavDTO.getParseStatus());
- emailFundAssetDO.setIsvalid(1);
- emailFundAssetDO.setCreatorId(0);
- emailFundAssetDO.setCreateTime(parseDate);
- emailFundAssetDO.setUpdaterId(0);
- emailFundAssetDO.setUpdateTime(parseDate);
- fundAssetDOList.add(emailFundAssetDO);
- }
- } else {
- EmailFundAssetDO emailFundAssetDO = new EmailFundAssetDO();
- emailFundAssetDO.setFileId(fileId);
- emailFundAssetDO.setPriceDate(priceDate);
- emailFundAssetDO.setFundName(fundNavDTO.getFundName());
- emailFundAssetDO.setRegisterNumber(fundNavDTO.getRegisterNumber());
- emailFundAssetDO.setAssetNet(assetNet);
- emailFundAssetDO.setAssetShare(assetShare);
- emailFundAssetDO.setIsStored(isStored);
- emailFundAssetDO.setExceptionStatus(fundNavDTO.getParseStatus());
- emailFundAssetDO.setIsvalid(1);
- emailFundAssetDO.setCreatorId(0);
- emailFundAssetDO.setCreateTime(parseDate);
- emailFundAssetDO.setUpdaterId(0);
- emailFundAssetDO.setUpdateTime(parseDate);
- fundAssetDOList.add(emailFundAssetDO);
- }
- return fundAssetDOList;
- }
- private List<EmailFundNavDO> buildEmailFundNavDo(Integer fileId, EmailFundNavDTO fundNavDTO, Date parseDate) {
- List<EmailFundNavDO> fundNavDOList = CollUtil.newArrayList();
- Date priceDate = DateUtil.parse(fundNavDTO.getPriceDate(), DateConst.YYYY_MM_DD);
- BigDecimal nav = StrUtil.isNotBlank(fundNavDTO.getNav()) ? new BigDecimal(fundNavDTO.getNav()) : null;
- BigDecimal cumulativeNavWithdrawal = StrUtil.isNotBlank(fundNavDTO.getCumulativeNavWithdrawal()) ? new BigDecimal(fundNavDTO.getCumulativeNavWithdrawal()) : null;
- Integer isStored = fundNavDTO.getParseStatus() != null && !fundNavDTO.getParseStatus().equals(NavParseStatusConst.NAV_DEFICIENCY)
- && !fundNavDTO.getParseStatus().equals(NavParseStatusConst.NOT_MATCH) ? 1 : 0;
- if (CollUtil.isNotEmpty(fundNavDTO.getFundIdList())) {
- for (String fundId : fundNavDTO.getFundIdList()) {
- EmailFundNavDO emailFundNavDO = new EmailFundNavDO();
- emailFundNavDO.setFileId(fileId);
- emailFundNavDO.setIsStored(isStored);
- emailFundNavDO.setPriceDate(priceDate);
- emailFundNavDO.setNav(nav);
- emailFundNavDO.setFundId(fundId);
- emailFundNavDO.setCumulativeNavWithdrawal(cumulativeNavWithdrawal);
- emailFundNavDO.setFundName(fundNavDTO.getFundName());
- emailFundNavDO.setRegisterNumber(fundNavDTO.getRegisterNumber());
- emailFundNavDO.setExceptionStatus(fundNavDTO.getParseStatus());
- emailFundNavDO.setTemplateId(fundNavDTO.getTemplateId());
- emailFundNavDO.setIsvalid(1);
- emailFundNavDO.setCreatorId(0);
- emailFundNavDO.setCreateTime(parseDate);
- emailFundNavDO.setUpdaterId(0);
- emailFundNavDO.setUpdateTime(parseDate);
- fundNavDOList.add(emailFundNavDO);
- }
- } else {
- EmailFundNavDO emailFundNavDO = new EmailFundNavDO();
- emailFundNavDO.setFileId(fileId);
- emailFundNavDO.setPriceDate(priceDate);
- emailFundNavDO.setNav(nav);
- emailFundNavDO.setCumulativeNavWithdrawal(cumulativeNavWithdrawal);
- emailFundNavDO.setFundName(fundNavDTO.getFundName());
- emailFundNavDO.setRegisterNumber(fundNavDTO.getRegisterNumber());
- emailFundNavDO.setExceptionStatus(fundNavDTO.getParseStatus());
- emailFundNavDO.setTemplateId(fundNavDTO.getTemplateId());
- emailFundNavDO.setIsStored(isStored);
- emailFundNavDO.setIsvalid(1);
- emailFundNavDO.setCreatorId(0);
- emailFundNavDO.setCreateTime(parseDate);
- emailFundNavDO.setUpdaterId(0);
- emailFundNavDO.setUpdateTime(parseDate);
- fundNavDOList.add(emailFundNavDO);
- }
- return fundNavDOList;
- }
- private Integer saveEmailFileInfo(Integer emailId, Integer fileId, String fileName, String filePath, Date parseDate) {
- EmailFileInfoDO emailFileInfoDO = buildEmailFileInfoDO(emailId, fileId, fileName, filePath, parseDate);
- if (emailFileInfoDO.getId() != null) {
- emailFileInfoMapper.updateTimeById(fileId, parseDate);
- return emailFileInfoDO.getId();
- }
- emailFileInfoMapper.insert(emailFileInfoDO);
- return emailFileInfoDO.getId();
- }
- private EmailFileInfoDO buildEmailFileInfoDO(Integer emailId, Integer fileId, String fileName, String filePath, Date parseDate) {
- EmailFileInfoDO emailFileInfoDO = new EmailFileInfoDO();
- emailFileInfoDO.setId(fileId);
- emailFileInfoDO.setEmailId(emailId);
- emailFileInfoDO.setFileName(fileName);
- emailFileInfoDO.setFilePath(filePath);
- emailFileInfoDO.setIsvalid(1);
- emailFileInfoDO.setCreatorId(0);
- emailFileInfoDO.setCreateTime(parseDate);
- emailFileInfoDO.setUpdaterId(0);
- emailFileInfoDO.setUpdateTime(parseDate);
- return emailFileInfoDO;
- }
- private void setNavParseStatus(EmailFundNavDTO fundNavDTO, String emailTitle) {
- // 1.单位净值或累计净值缺失
- if (StrUtil.isBlank(fundNavDTO.getNav()) || StrUtil.isBlank(fundNavDTO.getCumulativeNavWithdrawal())) {
- fundNavDTO.setParseStatus(NavParseStatusConst.NAV_DEFICIENCY);
- return;
- }
- // 考虑单独规模文件时 -> 无单位净值和累计净值
- // 2.单位净值或累计净值不大于0
- if (!emailTitle.contains("规模")) {
- if (StrUtil.isBlank(fundNavDTO.getNav()) || StrUtil.isBlank(fundNavDTO.getCumulativeNavWithdrawal())
- || (fundNavDTO.getNav().compareTo("0") <= 0 || fundNavDTO.getCumulativeNavWithdrawal().compareTo("0") <= 0)) {
- fundNavDTO.setParseStatus(NavParseStatusConst.NAV_NEGATIVE);
- return;
- }
- }
- // 3.资产净值不大于0
- if (StrUtil.isNotBlank(fundNavDTO.getAssetNet()) && fundNavDTO.getAssetNet().compareTo("0") <= 0) {
- fundNavDTO.setParseStatus(NavParseStatusConst.ASSET_NET_NEGATIVE);
- return;
- }
- // 4.匹配基金(考虑到解析估值表时已经匹配上基金的情况)
- List<String> fundIdList = fundNavDTO.getFundIdList();
- if (CollUtil.isEmpty(fundIdList)) {
- fundIdList = fundService.getFundIdByNamesAndCode(fundNavDTO.getFundName(), fundNavDTO.getRegisterNumber());
- if (CollUtil.isEmpty(fundIdList)) {
- fundNavDTO.setParseStatus(NavParseStatusConst.NOT_MATCH);
- }
- }
- fundNavDTO.setFundIdList(fundIdList);
- // 写入别名管理表fund_alias
- saveFundAlias(fundNavDTO.getFundName(), fundNavDTO.getRegisterNumber(), fundIdList);
- if (CollUtil.isEmpty(fundIdList)) {
- return;
- }
- fundNavDTO.setParseStatus(NavParseStatusConst.SUCCESS);
- }
- private void saveFundAlias(String fundName, String registerNumber, List<String> fundIdList) {
- // 未识别到基金名称和备案编码的数据不写入别名管理
- if (StrUtil.isBlank(fundName) && StrUtil.isBlank(registerNumber)) {
- return;
- }
- List<FundAliasDO> fundAliasDOList = CollUtil.newArrayList();
- if (StrUtil.isNotBlank(fundName) && StrUtil.isNotBlank(registerNumber)) {
- fundAliasDOList = fundAliasMapper.queryFundIdByNameAndRegisterNumber(fundName, registerNumber);
- }
- if (StrUtil.isBlank(fundName) && StrUtil.isNotBlank(registerNumber)) {
- fundAliasDOList = fundAliasMapper.queryFundIdByRegisterNumber(registerNumber);
- }
- if (StrUtil.isNotBlank(fundName) && StrUtil.isBlank(registerNumber)) {
- fundAliasDOList = fundAliasMapper.queryFundIdByName(fundName);
- }
- // 未匹配基金且已写入别名表
- if (CollUtil.isEmpty(fundIdList) && CollUtil.isNotEmpty(fundAliasDOList)) {
- return;
- }
- // 未匹配基金且未写入别名表
- if (CollUtil.isEmpty(fundIdList) && CollUtil.isEmpty(fundAliasDOList)) {
- fundAliasMapper.batchInsert(ListUtil.toList(buildFundAliasDO(fundName, registerNumber, null)));
- log.info("写入别名表 -> 基金名称:{},备案编码:{},基金id:{}", fundName, registerNumber, fundIdList);
- return;
- }
- // 匹配上基金 -> 需要判断此时基金id是否已经在别名管理表
- if (CollUtil.isNotEmpty(fundAliasDOList)) {
- List<String> collect = fundAliasDOList.stream().filter(Objects::nonNull).map(FundAliasDO::getTargetFundId).filter(Objects::nonNull).distinct().toList();
- fundIdList = fundIdList.stream().filter(e -> !collect.contains(e)).toList();
- }
- List<FundAliasDO> fundAliasDOS = CollUtil.isNotEmpty(fundIdList) ? fundIdList.stream().map(e -> buildFundAliasDO(fundName, registerNumber, e)).toList() : null;
- if (CollUtil.isNotEmpty(fundAliasDOS)) {
- fundAliasMapper.batchInsert(fundAliasDOS);
- log.info("写入别名表 -> 基金名称:{},备案编码:{},基金id:{}", fundName, registerNumber, fundIdList);
- }
- }
- public FundAliasDO buildFundAliasDO(String fundName, String registerNumber, String fundId) {
- FundAliasDO fundAliasDO = new FundAliasDO();
- fundAliasDO.setTargetFundId(fundId);
- fundAliasDO.setSourceFundName(fundName);
- fundAliasDO.setSourceRegisterNumber(registerNumber);
- fundAliasDO.setIsvalid(1);
- fundAliasDO.setCreatorId(0);
- fundAliasDO.setCreateTime(new Date());
- fundAliasDO.setUpdateTime(new Date());
- fundAliasDO.setUpdaterId(0);
- return fundAliasDO;
- }
- private Integer saveEmailParseInfo(EmailParseInfoDO emailParseInfoDO) {
- if (emailParseInfoDO == null) {
- return null;
- }
- // 重新邮件功能 -> 修改解析时间和更新时间
- if (emailParseInfoDO.getId() != null) {
- emailParseInfoMapper.updateParseTime(emailParseInfoDO.getId(), emailParseInfoDO.getParseDate());
- return emailParseInfoDO.getId();
- }
- emailParseInfoMapper.insert(emailParseInfoDO);
- return emailParseInfoDO.getId();
- }
- private EmailParseInfoDO buildEmailParseInfo(Integer emailId, String emailAddress, String senderEmail, String emailDate,
- String emailTitle, Integer emailType, Integer parseStatus, Date parseDate) {
- EmailParseInfoDO emailParseInfoDO = new EmailParseInfoDO();
- emailParseInfoDO.setId(emailId);
- emailParseInfoDO.setSenderEmail(senderEmail);
- emailParseInfoDO.setEmail(emailAddress);
- emailParseInfoDO.setEmailDate(DateUtil.parse(emailDate, DateConst.YYYY_MM_DD_HH_MM_SS));
- emailParseInfoDO.setParseDate(parseDate);
- emailParseInfoDO.setEmailTitle(emailTitle);
- emailParseInfoDO.setEmailType(emailType);
- emailParseInfoDO.setParseStatus(parseStatus);
- emailParseInfoDO.setIsvalid(1);
- emailParseInfoDO.setCreatorId(0);
- emailParseInfoDO.setCreateTime(parseDate);
- emailParseInfoDO.setUpdaterId(0);
- emailParseInfoDO.setUpdateTime(parseDate);
- return emailParseInfoDO;
- }
- public Map<String, List<String>> getEmailFieldMapping() {
- List<EmailFieldMappingDO> emailFieldMappingDOList = emailFieldMapper.getEmailFieldMapping(1);
- return emailFieldMappingDOList.stream()
- .collect(Collectors.toMap(EmailFieldMappingDO::getCode, v -> Arrays.stream(v.getName().split(",")).toList()));
- }
- public Map<Integer, List<String>> getEmailType() {
- Map<Integer, List<String>> emailTypeMap = MapUtil.newHashMap(3, true);
- EmailTypeRuleDO emailTypeRuleDO = emailTypeRuleMapper.getEmailTypeRule();
- String nav = emailTypeRuleDO != null && StrUtil.isNotBlank(emailTypeRuleDO.getNav()) ? emailTypeRuleDO.getNav() : emailRuleConfig.getNav();
- String valuation = emailTypeRuleDO != null && StrUtil.isNotBlank(emailTypeRuleDO.getValuation()) ? emailTypeRuleDO.getValuation() : emailRuleConfig.getValuation();
- String report = emailTypeRuleDO != null && StrUtil.isNotBlank(emailTypeRuleDO.getReport()) ? emailTypeRuleDO.getReport() : emailRuleConfig.getReport();
- emailTypeMap.put(EmailTypeConst.VALUATION_EMAIL_TYPE, Arrays.stream(valuation.split(",")).toList());
- emailTypeMap.put(EmailTypeConst.REPORT_EMAIL_TYPE, Arrays.stream(report.split(",")).toList());
- emailTypeMap.put(EmailTypeConst.NAV_EMAIL_TYPE, Arrays.stream(nav.split(",")).toList());
- return emailTypeMap;
- }
- /**
- * 读取邮件
- *
- * @param mailboxInfoDTO 邮箱配置信息
- * @param emailTypeMap 邮件类型识别规则映射表
- * @param startDate 邮件起始日期
- * @param endDate 邮件截止日期(为null,将解析邮件日期小于等于startDate的当天邮件)
- * @return 读取到的邮件信息
- * @throws Exception 异常信息
- */
- private Map<String, List<EmailContentInfoDTO>> realEmail(MailboxInfoDTO mailboxInfoDTO, Map<Integer, List<String>> emailTypeMap, Date startDate, Date endDate) throws Exception {
- Store store = EmailUtil.getStoreNew(mailboxInfoDTO);
- if (store == null) {
- return MapUtil.newHashMap();
- }
- // 默认读取收件箱的邮件
- Folder folder = store.getFolder("INBOX");
- folder.open(Folder.READ_ONLY);
- Message[] messages = getEmailMessage(folder, mailboxInfoDTO.getProtocol(), startDate);
- if (messages == null || messages.length == 0) {
- log.info("获取不到邮件 -> 邮箱信息:{},开始时间:{},结束时间:{}", mailboxInfoDTO, startDate, endDate);
- return MapUtil.newHashMap();
- }
- Map<String, List<EmailContentInfoDTO>> emailMessageMap = MapUtil.newHashMap();
- for (Message message1 : messages) {
- MimeMessage message = (MimeMessage) message1;
- List<EmailContentInfoDTO> emailContentInfoDTOList = CollUtil.newArrayList();
- String uuidKey = UUID.randomUUID().toString().replaceAll("-", "");
- Integer emailType;
- String senderEmail;
- try {
- Date emailDate = message.getSentDate();
- boolean isNotParseConditionSatisfied = emailDate == null || (endDate != null && emailDate.compareTo(endDate) > 0) || (startDate != null && emailDate.compareTo(startDate) < 0);
- if (isNotParseConditionSatisfied) {
- continue;
- }
- senderEmail = getSenderEmail(message.getFrom());
- emailType = EmailUtil.getEmailTypeBySubject(message.getSubject(), emailTypeMap);
- String emailDateStr = DateUtil.format(emailDate, DateConst.YYYY_MM_DD_HH_MM_SS);
- if (emailType == null) {
- log.info("邮件不满足解析条件 -> 邮件主题:{},邮件日期:{}", message.getSubject(), emailDateStr);
- continue;
- }
- log.info("邮件采集成功 -> 邮件主题:{},邮件日期:{}", message.getSubject(), emailDateStr);
- Object content = message.getContent();
- // 1.邮件为MIME多部分消息体:可能既有邮件又有正文
- if (content instanceof MimeMultipart) {
- emailContentInfoDTOList = EmailUtil.collectMimeMultipart(message, mailboxInfoDTO.getAccount(), path);
- }
- // 2.邮件只有正文
- if (content instanceof String) {
- EmailContentInfoDTO emailContentInfoDTO = new EmailContentInfoDTO();
- emailContentInfoDTO.setEmailContent(content.toString());
- emailContentInfoDTO.setEmailDate(emailDateStr);
- emailContentInfoDTO.setEmailTitle(message.getSubject());
- String fileName = message.getSubject() + DateUtil.format(emailDate, DateConst.YYYYMMDDHHMMSS24);
- String filePath = path + mailboxInfoDTO.getAccount() + "/" + DateUtil.format(emailDate, DateConst.YYYY_MM_DD) + "/" + fileName + ".html";
- File saveFile = new File(filePath);
- saveFile.setReadable(true);
- if (!saveFile.exists()) {
- if (!saveFile.getParentFile().exists()) {
- saveFile.getParentFile().mkdirs();
- saveFile.getParentFile().setExecutable(true);
- }
- }
- FileUtil.writeFile(filePath, content.toString());
- emailContentInfoDTO.setFilePath(filePath);
- emailContentInfoDTO.setFileName(fileName);
- emailContentInfoDTOList.add(emailContentInfoDTO);
- }
- if (CollUtil.isNotEmpty(emailContentInfoDTOList)) {
- // 估值表或定期报告邮件不展示正文html文件
- if (emailType.equals(EmailTypeConst.VALUATION_EMAIL_TYPE) || emailType.equals(EmailTypeConst.REPORT_EMAIL_TYPE)) {
- emailContentInfoDTOList = emailContentInfoDTOList.stream().filter(e -> !ExcelUtil.isHTML(e.getFilePath())).toList();
- }
- emailContentInfoDTOList.forEach(e -> {
- e.setEmailType(emailType);
- e.setSenderEmail(senderEmail);
- });
- emailMessageMap.put(uuidKey, emailContentInfoDTOList);
- }
- } catch (Exception e) {
- log.error("获取邮箱的邮件报错,堆栈信息:{}", ExceptionUtil.stacktraceToString(e));
- }
- }
- folder.close(false);
- store.close();
- return emailMessageMap;
- }
- private String getSenderEmail(Address[] senderAddress) {
- if (senderAddress == null || senderAddress.length == 0) {
- log.info("发件人获取失败=============================");
- return null;
- }
- // 此时的address是含有编码(MIME编码方式)后的文本和实际的邮件地址
- String address = "";
- for(Address from : senderAddress){
- if(StringUtil.isNotEmpty(from.toString())){
- address = from.toString();
- break;
- }
- }
- log.info("发件人地址:"+address+"========================");
- // 正则表达式匹配邮件地址
- Pattern pattern = Pattern.compile("<(\\S+)>");
- Matcher matcher = pattern.matcher(address);
- if (matcher.find()) {
- return matcher.group(1);
- }
- return address;
- }
- public List<NameValueVO> searchEmailCount(DataboardQuery databoardQuery) {
- if(StringUtil.isNotEmpty(databoardQuery.getEndDate())){
- databoardQuery.setEndDate(com.smppw.utils.DateUtil.getAroundDate(DateUtils.parse(databoardQuery.getEndDate(),DateUtils.YYYY_MM_DD),1));
- }
- List<Map<String, Object>> dataList = emailParseInfoMapper.searchEmailDataBoard(databoardQuery);
- List<NameValueVO> result = new ArrayList<>();
- Long total = 0L;
- for (Map<String, Object> data : dataList) {
- NameValueVO vo = new NameValueVO();
- if (1 == ((Integer) data.get("parse_status")).intValue()) {
- vo.setValue((Long) data.get("total"));
- vo.setName("解析成功");
- } else {
- vo.setValue((Long) data.get("total"));
- vo.setName("解析失败");
- }
- total += (Long) data.get("total");
- result.add(vo);
- }
- NameValueVO vo = new NameValueVO();
- vo.setName("解析总数");
- vo.setValue(total);
- result.add(vo);
- return result;
- }
- public List<NameValueVO> searchEmailTypeCount(DataboardQuery databoardQuery) {
- if(StringUtil.isNotEmpty(databoardQuery.getEndDate())){
- databoardQuery.setEndDate(com.smppw.utils.DateUtil.getAroundDate(DateUtils.parse(databoardQuery.getEndDate(),DateUtils.YYYY_MM_DD),1));
- }
- //邮件类型,1-净值,2-估值表,3-定期报告
- List<Map<String, Object>> dataList = emailParseInfoMapper.searchEmailTypeCount(databoardQuery);
- List<NameValueVO> result = new ArrayList<>();
- Integer total = 0;
- for (Map<String, Object> data : dataList) {
- NameValueVO vo = new NameValueVO();
- Integer emailType = (Integer) data.get("email_type");
- Long totalType = (Long) data.get("total");
- if (1 == emailType) {
- vo.setName("净值规模");
- vo.setValue(totalType);
- } else if (2 == emailType) {
- vo.setName("估值表");
- vo.setValue(totalType);
- } else {
- vo.setName("定期报告");
- vo.setValue(totalType);
- }
- result.add(vo);
- }
- return result;
- }
- public EmailParseFailAnalysisVO parseFailAnalysis(DataboardQuery databoardQuery) {
- if(StringUtil.isNotEmpty(databoardQuery.getEndDate())){
- databoardQuery.setEndDate(com.smppw.utils.DateUtil.getAroundDate(DateUtils.parse(databoardQuery.getEndDate(),DateUtils.YYYY_MM_DD),1));
- }
- EmailParseFailAnalysisVO result = new EmailParseFailAnalysisVO();
- //净值规模
- List<NameValueVO> navNameValueVOS = new ArrayList<>();
- NameValueVO pdfNoDataVO = new NameValueVO();
- databoardQuery.setEmailType(1);
- Long pdfNoData = emailParseInfoMapper.countpdfNoData(databoardQuery, "无法从PDF文件中获取到数据");
- pdfNoDataVO.setName("无法从PDF文件中获取到数据");
- pdfNoDataVO.setValue(pdfNoData);
- navNameValueVOS.add(pdfNoDataVO);
- NameValueVO priceDateMissVO = new NameValueVO();
- Long priceDateMiss = emailParseInfoMapper.countpdfNoData(databoardQuery, "缺少净值日期");
- priceDateMissVO.setValue(priceDateMiss);
- priceDateMissVO.setName("缺少净值日期");
- navNameValueVOS.add(priceDateMissVO);
- NameValueVO navMissVO = new NameValueVO();
- Long navMiss = emailParseInfoMapper.countpdfNoData(databoardQuery, "单位净值和累计净值和资产净值均缺失");
- navMissVO.setName("单位净值和累计净值和资产净值均缺失");
- navMissVO.setValue(navMiss);
- navNameValueVOS.add(navMissVO);
- NameValueVO fundNameNumberMissVO = new NameValueVO();
- Long fundNameNumberMiss = emailParseInfoMapper.countpdfNoData(databoardQuery, "基金名称和备案编码均缺失");
- fundNameNumberMissVO.setName("基金名称和备案编码均缺失");
- fundNameNumberMissVO.setValue(fundNameNumberMiss);
- navNameValueVOS.add(fundNameNumberMissVO);
- result.setNavFailAnalysisVO(navNameValueVOS);
- NameValueVO templateUnSupportVO = new NameValueVO();
- Long templateUnSupport = emailParseInfoMapper.countpdfNoData(databoardQuery, "模板不支持");
- templateUnSupportVO.setName("模板不支持");
- templateUnSupportVO.setValue(templateUnSupport);
- navNameValueVOS.add(templateUnSupportVO);
- result.setNavFailAnalysisVO(navNameValueVOS);
- //估值表规模
- databoardQuery.setEmailType(2);
- List<NameValueVO> valuationNameValueVOS = new ArrayList<>();
- NameValueVO fileTypeErrorVO = new NameValueVO();
- Long fileTypeError = emailParseInfoMapper.countpdfNoData(databoardQuery, "文件格式错误");
- fileTypeErrorVO.setName("文件格式错误");
- fileTypeErrorVO.setValue(fileTypeError);
- valuationNameValueVOS.add(fileTypeErrorVO);
- NameValueVO columnMissVO = new NameValueVO();
- Long columnMiss = emailParseInfoMapper.countpdfNoData(databoardQuery, "无市值列或无数量列");
- columnMissVO.setName("无市值列或无数量列");
- columnMissVO.setValue(columnMiss);
- valuationNameValueVOS.add(columnMissVO);
- NameValueVO numbericMissVO = new NameValueVO();
- Long numbericMiss = emailParseInfoMapper.countpdfNoData(databoardQuery, "非数值数据");
- numbericMissVO.setName("非数值数据");
- numbericMissVO.setValue(numbericMiss);
- valuationNameValueVOS.add(numbericMissVO);
- NameValueVO noDataVO = new NameValueVO();
- Long noData = emailParseInfoMapper.countpdfNoData(databoardQuery, "无数据");
- noDataVO.setValue(noData);
- noDataVO.setName("无数据");
- valuationNameValueVOS.add(noDataVO);
- NameValueVO templateErrorVO = new NameValueVO();
- Long templateError = emailParseInfoMapper.countpdfNoData(databoardQuery, "模板不支持");
- templateErrorVO.setValue(templateError);
- templateErrorVO.setName("模板不支持");
- valuationNameValueVOS.add(templateErrorVO);
- result.setValuationFailAnalysisVO(valuationNameValueVOS);
- //定期报告
- /**
- * PARSE_FAIL(21000, "定期报告解析错误:{}"),
- * NOT_A_REPORT(21001, "[{}]不是定期报告"),
- * REPORT_IS_SCAN(21002, "报告[{}]为扫描件"),
- * NO_SUPPORT_TEMPLATE(21003, "报告[{}]是不支持的文件格式"),
- * NOT_A_FIXED_FORMAT(21004, "报告[{}]不是基协统一格式"),
- *
- * PARSE_FUND_INFO_FAIL(21010, "报告[{}]没有解析到基金基本信息"),
- * PARSE_NAV_INFO_FAIL(21011, "报告[{}]没有解析到基金净值信息"),
- * PARSE_FINANCIAL_INFO_FAIL(21012, "报告[{}]没有解析到基金财务指标信息"),
- * PARSE_INDUSTRY_INFO_FAIL(21013, "报告[{}]没有解析到基金行业配置信息"),
- * PARSE_ASSET_INFO_FAIL(21014, "报告[{}]没有解析到基金资产配置信息"),
- * PARSE_SHARE_INFO_FAIL(21015, "报告[{}]没有解析到基金份额变动信息"),
- */
- databoardQuery.setEmailType(3);
- List<NameValueVO> reportNameValueVOS = new ArrayList<>();
- NameValueVO scannedFileVO = new NameValueVO();
- Long scannedFile = emailParseInfoMapper.countpdfNoData(databoardQuery, "为扫描件");
- scannedFileVO.setName("报告为扫描件");
- scannedFileVO.setValue(scannedFile);
- reportNameValueVOS.add(scannedFileVO);
- NameValueVO errorAmacFileTypeVO = new NameValueVO();
- Long errorAmacFileType = emailParseInfoMapper.countpdfNoData(databoardQuery, "不是基协统一格式");
- errorAmacFileTypeVO.setName("报告不是基协统一格式");
- errorAmacFileTypeVO.setValue(errorAmacFileType);
- reportNameValueVOS.add(errorAmacFileTypeVO);
- NameValueVO watermarkFileErrorVO = new NameValueVO();
- Long watermarkFileError = emailParseInfoMapper.countpdfNoData(databoardQuery, "没有解析到");
- watermarkFileErrorVO.setName("报告水印干扰导致部分没有解析");
- watermarkFileErrorVO.setValue(watermarkFileError);
- reportNameValueVOS.add(watermarkFileErrorVO);
- NameValueVO noReportVO = new NameValueVO();
- Long noReport = emailParseInfoMapper.countpdfNoData(databoardQuery, "不是定期报告");
- noReportVO.setName("报告不是定期报告");
- noReportVO.setValue(noReport);
- reportNameValueVOS.add(noReportVO);
- result.setReportFailAnalysisVO(reportNameValueVOS);
- return result;
- }
- public EmailParseDataViewVO dataOverview(DataboardQuery databoardQuery) {
- EmailParseDataViewVO dataViewVO = new EmailParseDataViewVO();
- dataViewVO.setEmailNum(emailParseInfoMapper.countEmailTotal(null));
- dataViewVO.setNavEmailNum(emailParseInfoMapper.countEmailTotal(1));
- dataViewVO.setValuationEmailNum(emailParseInfoMapper.countEmailTotal(2));
- dataViewVO.setReportEmailNum(emailParseInfoMapper.countEmailTotal(3));
- dataViewVO.setParseNavNum(emailFundNavMapper.countEmailNavTotal());
- dataViewVO.setParseAssetNum(emailFundAssetMapper.countEmailAssetTotal());
- dataViewVO.setNavNum(navMapper.countNavTotal());
- dataViewVO.setAssetNum(assetMapper.countAssetTotal());
- dataViewVO.setDistribute(distributionMapper.countDistributionTotal());
- dataViewVO.setFundNum(fundInfoMapper.countFundTotal());
- dataViewVO.setCompanyNum(companyInformationMapper.countCompanyTotal());
- return dataViewVO;
- }
- private Message[] getEmailMessage(Folder folder, String protocol, Date startDate) {
- try {
- if (protocol.contains("imap")) {
- // 获取邮件日期大于等于startDate的邮件(搜索条件只支持按天)
- SearchTerm startDateTerm = new ReceivedDateTerm(ComparisonTerm.GE, startDate);
- return folder.search(startDateTerm);
- } else {
- return folder.getMessages();
- }
- } catch (MessagingException e) {
- throw new RuntimeException(e);
- }
- }
- private static class PythonData {
- private Integer fileId;
- private Integer status;
- private String msg;
- private String register_number;
- public Integer getFileId() {
- return fileId;
- }
- public void setFileId(Integer fileId) {
- this.fileId = fileId;
- }
- public Integer getStatus() {
- return status;
- }
- public void setStatus(Integer status) {
- this.status = status;
- }
- public String getMsg() {
- return msg;
- }
- public void setMsg(String msg) {
- this.msg = msg;
- }
- public String getRegister_number() {
- return register_number;
- }
- public void setRegister_number(String register_number) {
- this.register_number = register_number;
- }
- }
- }
|