package com.simuwang.manage.service.impl; import cn.hutool.core.date.DateUtil; import com.simuwang.base.common.enums.DeletionType; import com.simuwang.base.common.enums.DistributeType; import com.simuwang.base.common.enums.Frequency; import com.simuwang.base.common.support.MybatisPage; import com.simuwang.base.common.util.DateUtils; import com.simuwang.base.common.util.StringUtil; import com.simuwang.base.mapper.*; import com.simuwang.base.pojo.dos.*; import com.simuwang.base.pojo.dto.DeletionDownParam; import com.simuwang.base.pojo.dto.ExcelDeletionInfoDTO; import com.simuwang.base.pojo.dto.query.DeletionPageQuery; import com.simuwang.base.pojo.dto.query.FundDeletionPageQuery; import com.simuwang.base.pojo.vo.*; import com.simuwang.manage.service.DeletionService; import com.simuwang.shiro.utils.UserUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; /** * FileName: DeletionServiceImpl * Author: chenjianhua * Date: 2024/9/17 18:54 * Description: ${DESCRIPTION} */ @Service public class DeletionServiceImpl implements DeletionService { @Autowired private DeletionInfoMapper deletionInfoMapper; @Autowired private FundInfoMapper fundInfoMapper; @Autowired private AssetMapper assetMapper; @Autowired private FundReportFrequencyMapper fundReportFrequencyMapper; @Autowired private TradeDateMapper tradeDateMapper; @Autowired private DistributionMapper distributionMapper; @Autowired private NavMapper navMapper; @Override public MybatisPage searchDeletionList(DeletionPageQuery deletionPageQuery) { List deletionInfoDOList = deletionInfoMapper.searchDeletionList(deletionPageQuery); long total = deletionInfoMapper.countDeletion(deletionPageQuery); List dataList = deletionInfoDOList.stream().map(DeletionInfoDO::toVO).collect(Collectors.toList()); for(DeletionInfoVO vo : dataList){ String fundId = vo.getFundId(); String lastDeletionDate = deletionInfoMapper.getLastDeletionDateByFundId(fundId,vo.getDeletionType()); vo.setLastDeletionDate(lastDeletionDate); } return MybatisPage.of(total,dataList); } @Override public MybatisPage searchFundDeletionList(FundDeletionPageQuery fundDeletionPageQuery) { List deletionInfoDOList = deletionInfoMapper.searchFundDeletionList(fundDeletionPageQuery); long total = deletionInfoMapper.countFundDeletionList(fundDeletionPageQuery); List dataList = deletionInfoDOList.stream().map(FundDeletionInfoDO::toVO).collect(Collectors.toList()); for(FundDeletionInfoVO infoVO : dataList){ infoVO.setFundName(fundInfoMapper.getFundNameByFundId(infoVO.getFundId())); } return MybatisPage.of(total,dataList); } @Override public void saveFundDeletionList(List fundDeletionInfoVOList) { List fundDeletionInfoDOList = new ArrayList<>(); for(FundDeletionInfoVO vo : fundDeletionInfoVOList){ FundDeletionInfoDO fundDeletionInfoDO = new FundDeletionInfoDO(); fundDeletionInfoDO.setId(vo.getId()); fundDeletionInfoDO.setDeletionType(vo.getDeletionType()); fundDeletionInfoDO.setFundId(vo.getFundId()); fundDeletionInfoDO.setDeletionDate(vo.getDeletionDate()); fundDeletionInfoDO.setRemark(vo.getRemark()); fundDeletionInfoDO.setIsvalid(1); fundDeletionInfoDO.setUpdateTime(DateUtils.getNowDate()); fundDeletionInfoDO.setUpdaterId(UserUtils.getLoginUser().getUserId()); fundDeletionInfoDOList.add(fundDeletionInfoDO); } deletionInfoMapper.batchUpdate(fundDeletionInfoDOList); } @Override public void saveBatchDeletionRemark(FundDeletionRemarkVO fundDeletionRemarkVO) { String remark = fundDeletionRemarkVO.getRemark(); Integer userId = UserUtils.getLoginUser().getUserId(); for(FundDeletionTypeVO remarkVO : fundDeletionRemarkVO.getFundDeletionTypeList()){ deletionInfoMapper.updateRemark(remarkVO.getFundId(),remarkVO.getDeletionType(),remarkVO.getDeletionDate(),remark,userId); } } @Override public List selectFundDeletionInfoVOList(FundDeletionListVO fundDeletionListVO) { List result = new ArrayList<>(); List deletionDownParamList = fundDeletionListVO.getDeletionDownParamList(); for(DeletionDownParam deletionDownParam : deletionDownParamList){ List deletionInfoDOList = deletionInfoMapper.selectFundDeletionInfoVOList(deletionDownParam.getFundId(),deletionDownParam.getDeletionType()); for(FundDeletionInfoDO infoDO : deletionInfoDOList){ ExcelDeletionInfoDTO dto = new ExcelDeletionInfoDTO(); dto.setFundName(fundInfoMapper.getFundNameByFundId(infoDO.getFundId())); dto.setCompanyName(fundInfoMapper.getCompanyNameByFundId(infoDO.getFundId())); dto.setDeletionType(DeletionType.getDeletionTypeByCode(infoDO.getDeletionType()).getInfo()); dto.setDeletionDate(infoDO.getDeletionDate()); dto.setRemark(infoDO.getRemark()); dto.setFundId(infoDO.getFundId()); result.add(dto); } } return result; } @Override public void computeDeletion(DeletionDownParam deletionDownParam) { String fundId = deletionDownParam.getFundId(); String liquidateDate = fundInfoMapper.getLiquidateDateByFundId(fundId); if(StringUtil.isNotEmpty(liquidateDate)){ return; } String inceptionDate = fundInfoMapper.getInceptionDateByFundId(fundId); if(StringUtil.isEmpty(inceptionDate)){ return; } String today = DateUtils.getAroundToday(0); //获取基金对应的净值报送频率 FundReportFrequencyDO fundReportFrequencyDO = fundReportFrequencyMapper.getFrequencyByFundId(fundId); if(StringUtil.isNull(fundReportFrequencyDO)){ return; } List navDOList = navMapper.selectNavByFundId(fundId); List assetDOList = assetMapper.selectAssetByFundId(fundId); //查询成立日到今天为止的交易日集合 List tradeDateDOList = tradeDateMapper.selectTradeDate(inceptionDate,today); if(deletionDownParam.getDeletionType() != null && deletionDownParam.getDeletionType().equals(DeletionType.NAV_DELETION.getCode())){ navDeletion(fundId,navDOList,tradeDateDOList,fundReportFrequencyDO); }else if(deletionDownParam.getDeletionType() != null && deletionDownParam.getDeletionType().equals(DeletionType.ASSET_DELETION.getCode())){ assetDeletion(fundId,assetDOList,tradeDateDOList,fundReportFrequencyDO); }else if(deletionDownParam.getDeletionType() != null && deletionDownParam.getDeletionType().equals(DeletionType.DISTRIBUTION_DELETION.getCode())){ distributionDeletion(fundId,navDOList); }else{ navDeletion(fundId,navDOList,tradeDateDOList,fundReportFrequencyDO); assetDeletion(fundId,assetDOList,tradeDateDOList,fundReportFrequencyDO); distributionDeletion(fundId,navDOList); } } private void distributionDeletion(String fundId, List navDOList) { if(navDOList.size() < 1){ return; } //查询是否存在拆分 List distributionDOS = distributionMapper.getDistributionByFundId(fundId, DistributeType.DIVIDENDS_SPLIT); if(distributionDOS.size() > 0){ //存在拆分,不做分红缺失计算,同时把以往的数据添加备注 deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),null,DeletionType.EXIST_SPLIT.getInfo(),null); return; } BigDecimal threshold = new BigDecimal(0.0035); BigDecimal preDifference = new BigDecimal(0); for(int navIdx=0;navIdx < navDOList.size() ;navIdx++){ NavDO navDO = navDOList.get(navIdx); //获取当前净值日期下的分红总和 BigDecimal sumDistribute = distributionMapper.getSumDistributeByFundId(navDO.getFundId(),DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD)); BigDecimal nav = navDO.getNav(); BigDecimal cumulativeNavWithdrawal = navDO.getCumulativeNavWithdrawal(); if(sumDistribute == null){ sumDistribute = new BigDecimal(0); } //不存在分红的时候,判断当前的差值是否符合要求 BigDecimal difference = cumulativeNavWithdrawal.subtract(nav.add(sumDistribute)); if(difference.compareTo(threshold) > 0){ //存在缺失 String tradeDate = null; if(navIdx == 0 || navDOList.size() ==1){ //有且仅有一条数据 tradeDate = DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD)+"~"+DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD); }else{ //判断前一条的差值与当前是否一致,一致就不处理 if(difference.compareTo(preDifference) == 0){ continue; } tradeDate = DateUtils.format(navDOList.get(navIdx-1).getPriceDate(),DateUtils.YYYY_MM_DD)+"~"+DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD); } saveDeletionInfoDO(fundId,tradeDate,DeletionType.DISTRIBUTION_DELETION.getCode()); } preDifference = difference; } } private void assetDeletion(String fundId, List assetDOList, List tradeDateDOList, FundReportFrequencyDO fundReportFrequencyDO) { if(Frequency.DAY == Frequency.getFrequencyByCode(fundReportFrequencyDO.getAssetFrequency())){ Map> navListMap = assetDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD))); Map> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getTradeDate(),DateUtils.YYYY_MM_DD))); for(String tradeDate : tradeListMap.keySet()){ if(navListMap.containsKey(tradeDate)){ deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo(),null); continue; } if(tradeDateDOList.size() <= 3){ continue; } TradeDateDO tradeDateDO = tradeDateDOList.get(tradeDateDOList.size()-3); if(tradeDateDO.getTradeDate().compareTo(DateUtils.parse(tradeDate,DateUtils.YYYY_MM_DD)) < 0){ continue; } //写入缺失信息表 saveDeletionInfoDO(fundId,tradeDate,DeletionType.ASSET_DELETION.getCode()); } } if(Frequency.WEEK == Frequency.getFrequencyByCode(fundReportFrequencyDO.getAssetFrequency())){ Map> navListMap = assetDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD))); TreeMap> weekNavListMap = new TreeMap<>(); //按周数整合 for(String priceDate : navListMap.keySet()){ Integer weekOfYear = Integer.parseInt(priceDate.substring(0,4)+DateUtil.weekOfYear(DateUtils.parse(priceDate,DateUtils.YYYY_MM_DD))); if(weekNavListMap.containsKey(weekOfYear)){ List assetDOS = weekNavListMap.get(weekOfYear); assetDOS.addAll(navListMap.get(priceDate)); weekNavListMap.put(weekOfYear,assetDOS); }else{ List navDOS = new ArrayList<>(); navDOS.addAll(navListMap.get(priceDate)); weekNavListMap.put(weekOfYear,navDOS); } } Map> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getYearWeek())); for(Integer weekOfYear : tradeListMap.keySet()){ List assetDOS = weekNavListMap.get(weekOfYear); if(weekNavListMap.containsKey(weekOfYear)){ for(AssetDO assetDO : assetDOS){ String tradeDate = DateUtils.format(assetDO.getPriceDate(),DateUtils.YYYY_MM_DD); deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo(),null); } continue; }else{ if(StringUtil.isNotEmpty(assetDOS)){ for(int idx=0;idx < assetDOS.size()-1 ; idx++){ AssetDO assetDO = assetDOS.get(idx); String tradeDate = DateUtils.format(assetDO.getPriceDate(),DateUtils.YYYY_MM_DD); deletionInfoMapper.deleteDeletionRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate); } } } //不包含的话,默认取每周的最后一个交易日作为周净值日期 List tradeDateDOS = tradeListMap.get(weekOfYear); String tradeDate = null; if(tradeDateDOS.size() > 0){ tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD); } //判断当前缺失交易日是否是近三个交易日 if(tradeDateDOList.size() <= 3){ continue; } TradeDateDO tradeDateDO = tradeDateDOList.get(tradeDateDOList.size()-3); if(tradeDateDO.getTradeDate().compareTo(DateUtils.parse(tradeDate,DateUtils.YYYY_MM_DD)) < 0){ continue; } //写入缺失信息表 saveDeletionInfoDO(fundId,tradeDate,DeletionType.ASSET_DELETION.getCode()); } } if(Frequency.MONTH == Frequency.getFrequencyByCode(fundReportFrequencyDO.getAssetFrequency())){ Map> navListMap = assetDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD))); TreeMap> monthNavListMap = new TreeMap<>(); //按周数整合 for(String priceDate : navListMap.keySet()){ String yearMonth = priceDate.substring(0,7); if(monthNavListMap.containsKey(yearMonth)){ List navDOS = monthNavListMap.get(yearMonth); navDOS.addAll(navListMap.get(priceDate)); monthNavListMap.put(yearMonth,navDOS); }else{ List navDOS = new ArrayList<>(); navDOS.addAll(navListMap.get(priceDate)); monthNavListMap.put(yearMonth,navDOS); } } Map> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getYearmonth())); for(String yearMonth : tradeListMap.keySet()){ //本月的数据不考虑 String thisMonth = DateUtils.format(new Date(),DateUtils.YYYY_MM); if(yearMonth.equals(thisMonth)){ continue; } List assetDOS = monthNavListMap.get(yearMonth); if(monthNavListMap.containsKey(yearMonth)){ for(AssetDO assetDO : assetDOS){ String tradeDate = DateUtils.format(assetDO.getPriceDate(),DateUtils.YYYY_MM_DD); deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo(),null); } continue; }else{ if(StringUtil.isNotEmpty(assetDOS)){ for(int idx=0;idx < assetDOS.size()-1 ; idx++){ AssetDO assetDO = assetDOS.get(idx); String tradeDate = DateUtils.format(assetDO.getPriceDate(),DateUtils.YYYY_MM_DD); deletionInfoMapper.deleteDeletionRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate); } } } //不包含的话,默认取每周的最后一个交易日作为周净值日期 List tradeDateDOS = tradeListMap.get(yearMonth); String tradeDate = null; if(tradeDateDOS.size() > 0){ tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD); } if(tradeDateDOList.size() <= 3){ continue; } TradeDateDO tradeDateDO = tradeDateDOList.get(tradeDateDOList.size()-3); if(tradeDateDO.getTradeDate().compareTo(DateUtils.parse(tradeDate,DateUtils.YYYY_MM_DD)) < 0){ continue; } //写入缺失信息表 saveDeletionInfoDO(fundId,tradeDate,DeletionType.ASSET_DELETION.getCode()); } } } private void navDeletion(String fundId,List navDOList, List tradeDateDOList,FundReportFrequencyDO fundReportFrequencyDO) { //只处理日月季频率 if(Frequency.DAY == Frequency.getFrequencyByCode(fundReportFrequencyDO.getNavFrequency())){ Map> navListMap = navDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD))); Map> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getTradeDate(),DateUtils.YYYY_MM_DD))); for(String tradeDate : tradeListMap.keySet()){ if(navListMap.containsKey(tradeDate)){ deletionInfoMapper.updateRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo(),null); continue; } //写入缺失信息表 saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode()); } } if(Frequency.WEEK == Frequency.getFrequencyByCode(fundReportFrequencyDO.getNavFrequency())){ Map> navListMap = navDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD))); TreeMap> weekNavListMap = new TreeMap<>(); //按周数整合 for(String priceDate : navListMap.keySet()){ Integer weekOfYear = Integer.parseInt(priceDate.substring(0,4)+DateUtil.weekOfYear(DateUtils.parse(priceDate,DateUtils.YYYY_MM_DD)));; if(weekNavListMap.containsKey(weekOfYear)){ List navDOS = weekNavListMap.get(weekOfYear); navDOS.addAll(navListMap.get(priceDate)); weekNavListMap.put(weekOfYear,navDOS); }else{ List navDOS = new ArrayList<>(); navDOS.addAll(navListMap.get(priceDate)); weekNavListMap.put(weekOfYear,navDOS); } } Map> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getYearWeek())); for(Integer weekOfYear : tradeListMap.keySet()){ List navDOS = weekNavListMap.get(weekOfYear); if(weekNavListMap.containsKey(weekOfYear)){ for(NavDO navDO : navDOS){ String tradeDate = DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD); deletionInfoMapper.updateRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo(),null); } continue; }else{ //防止频率变更导致数据异常,需要把当前日期下所在的频率全部无效掉 if(StringUtil.isNotEmpty(navDOS)){ for(int idx=0;idx < navDOS.size()-1 ; idx++){ NavDO navDO = navDOS.get(idx); String tradeDate = DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD); deletionInfoMapper.deleteDeletionRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate); } } } //不包含的话,默认取每周的最后一个交易日作为周净值日期 List tradeDateDOS = tradeListMap.get(weekOfYear); String tradeDate = null; if(tradeDateDOS.size() > 0){ tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD); } if(tradeDateDOList.size() <= 3){ continue; } TradeDateDO tradeDateDO = tradeDateDOList.get(tradeDateDOList.size()-3); if(tradeDateDO.getTradeDate().compareTo(DateUtils.parse(tradeDate,DateUtils.YYYY_MM_DD)) < 0){ continue; } //写入缺失信息表 saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode()); } } if(Frequency.MONTH == Frequency.getFrequencyByCode(fundReportFrequencyDO.getNavFrequency())){ Map> navListMap = navDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD))); TreeMap> monthNavListMap = new TreeMap<>(); //按周数整合 for(String priceDate : navListMap.keySet()){ String yearMonth = priceDate.substring(0,7); if(monthNavListMap.containsKey(yearMonth)){ List navDOS = monthNavListMap.get(yearMonth); navDOS.addAll(navListMap.get(priceDate)); monthNavListMap.put(yearMonth,navDOS); }else{ List navDOS = new ArrayList<>(); navDOS.addAll(navListMap.get(priceDate)); monthNavListMap.put(yearMonth,navDOS); } } Map> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getYearmonth())); for(String yearMonth : tradeListMap.keySet()){ //本月的数据不考虑 String thisMonth = DateUtils.format(new Date(),DateUtils.YYYY_MM); if(yearMonth.equals(thisMonth)){ continue; } List navDOS = monthNavListMap.get(yearMonth); if(monthNavListMap.containsKey(yearMonth)){ for(NavDO navDO : navDOS){ String tradeDate = DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD); deletionInfoMapper.updateRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo(),null); } continue; }else{ //防止频率变更导致数据异常,需要把当前日期下所在的频率全部无效掉 if(StringUtil.isNotEmpty(navDOS)){ for(NavDO navDO : navDOS){ String tradeDate = DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD); deletionInfoMapper.deleteDeletionRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate); } } } //不包含的话,默认取每周的最后一个交易日作为周净值日期 List tradeDateDOS = tradeListMap.get(yearMonth); String tradeDate = null; if(tradeDateDOS.size() > 0){ tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD); } if(tradeDateDOList.size() <= 3){ continue; } TradeDateDO tradeDateDO = tradeDateDOList.get(tradeDateDOList.size()-3); if(tradeDateDO.getTradeDate().compareTo(DateUtils.parse(tradeDate,DateUtils.YYYY_MM_DD)) < 0){ continue; } //写入缺失信息表 saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode()); } } } private void saveDeletionInfoDO(String fundId, String tradeDate, Integer code) { if(tradeDate == null){ return; } DeletionInfoDO deletionInfoDO = new DeletionInfoDO(); deletionInfoDO.setFundId(fundId); deletionInfoDO.setDeletionDate(tradeDate); deletionInfoDO.setDeletionType(code); DeletionInfoDO oldDeletionDO = deletionInfoMapper.getDeletionInfoDO(deletionInfoDO); if(StringUtil.isNull(oldDeletionDO)){ deletionInfoDO.setIsvalid(1); deletionInfoDO.setIsSend(0); deletionInfoDO.setUpdateTime(DateUtils.getNowDate()); deletionInfoDO.setCreateTime(DateUtils.getNowDate()); deletionInfoMapper.saveDeletionInfoDO(deletionInfoDO); } } }