FundDeletionTask.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. package com.simuwang.manage.task;
  2. import cn.hutool.core.date.DateUtil;
  3. import com.simuwang.base.common.enums.DeletionType;
  4. import com.simuwang.base.common.enums.DistributeType;
  5. import com.simuwang.base.common.enums.Frequency;
  6. import com.simuwang.base.common.util.DateUtils;
  7. import com.simuwang.base.common.util.StringUtil;
  8. import com.simuwang.base.mapper.*;
  9. import com.simuwang.base.pojo.dos.*;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.scheduling.annotation.EnableScheduling;
  12. import org.springframework.scheduling.annotation.Scheduled;
  13. import org.springframework.stereotype.Component;
  14. import java.math.BigDecimal;
  15. import java.util.*;
  16. import java.util.stream.Collectors;
  17. /**
  18. * FileName: FundDeletionTask
  19. * Author: chenjianhua
  20. * Date: 2024/9/18 22:43
  21. * Description: ${DESCRIPTION}
  22. */
  23. @EnableScheduling
  24. @Component
  25. public class FundDeletionTask {
  26. @Autowired
  27. private NavMapper navMapper;
  28. @Autowired
  29. private AssetMapper assetMapper;
  30. @Autowired
  31. private FundInfoMapper fundInfoMapper;
  32. @Autowired
  33. private FundReportFrequencyMapper fundReportFrequencyMapper;
  34. @Autowired
  35. private TradeDateMapper tradeDateMapper;
  36. @Autowired
  37. private DeletionInfoMapper deletionInfoMapper;
  38. @Autowired
  39. private DistributionMapper distributionMapper;
  40. // @Scheduled(cron = "0 0 5,12,18 * * ?")
  41. public void computeDeletion(){
  42. List<String> fundIdList = navMapper.getAllFundId();
  43. for(String fundId : fundIdList){
  44. String liquidateDate = fundInfoMapper.getLiquidateDateByFundId(fundId);
  45. if(StringUtil.isNotEmpty(liquidateDate)){
  46. continue;
  47. }
  48. String inceptionDate = fundInfoMapper.getInceptionDateByFundId(fundId);
  49. if(StringUtil.isEmpty(inceptionDate)){
  50. continue;
  51. }
  52. String today = DateUtils.getAroundToday(0);
  53. //获取基金对应的净值报送频率
  54. FundReportFrequencyDO fundReportFrequencyDO = fundReportFrequencyMapper.getFrequencyByFundId(fundId);
  55. if(StringUtil.isNull(fundReportFrequencyDO)){
  56. continue;
  57. }
  58. List<NavDO> navDOList = navMapper.selectNavByFundId(fundId);
  59. List<AssetDO> assetDOList = assetMapper.selectAssetByFundId(fundId);
  60. //查询成立日到今天为止的交易日集合
  61. List<TradeDateDO> tradeDateDOList = tradeDateMapper.selectTradeDate(inceptionDate,today);
  62. //延迟天数为3天
  63. tradeDateDOList = tradeDateDOList.subList(0,tradeDateDOList.size()-3);
  64. navDeletion(fundId,navDOList,tradeDateDOList,fundReportFrequencyDO);
  65. assetDeletion(fundId,assetDOList,tradeDateDOList,fundReportFrequencyDO);
  66. distributionDeletion(fundId,navDOList);
  67. }
  68. }
  69. private void distributionDeletion(String fundId, List<NavDO> navDOList) {
  70. if(navDOList.size() < 1){
  71. return;
  72. }
  73. //查询是否存在拆分
  74. List<DistributionDO> distributionDOS = distributionMapper.getDistributionByFundId(fundId, DistributeType.DIVIDENDS_SPLIT);
  75. if(distributionDOS.size() > 0){
  76. //存在拆分,不做分红缺失计算,同时把以往的数据添加备注
  77. deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),null,DeletionType.EXIST_SPLIT.getInfo(),null);
  78. return;
  79. }
  80. BigDecimal threshold = new BigDecimal(0.0035);
  81. BigDecimal preDifference = new BigDecimal(0);
  82. for(int navIdx=0;navIdx < navDOList.size() ;navIdx++){
  83. NavDO navDO = navDOList.get(navIdx);
  84. //获取当前净值日期下的分红总和
  85. BigDecimal sumDistribute = distributionMapper.getSumDistributeByFundId(navDO.getFundId(),DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD));
  86. BigDecimal nav = navDO.getNav();
  87. BigDecimal cumulativeNavWithdrawal = navDO.getCumulativeNavWithdrawal();
  88. if(sumDistribute == null){
  89. sumDistribute = new BigDecimal(0);
  90. }
  91. //不存在分红的时候,判断当前的差值是否符合要求
  92. BigDecimal difference = cumulativeNavWithdrawal.subtract(nav.add(sumDistribute));
  93. if(difference.compareTo(threshold) > 0){
  94. //存在缺失
  95. String tradeDate = null;
  96. if(navIdx == 0 || navDOList.size() ==1){
  97. //有且仅有一条数据
  98. tradeDate = DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD)+"~"+DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD);
  99. }else{
  100. //判断前一条的差值与当前是否一致,一致就不处理
  101. if(difference.compareTo(preDifference) == 0){
  102. continue;
  103. }
  104. tradeDate = DateUtils.format(navDOList.get(navIdx-1).getPriceDate(),DateUtils.YYYY_MM_DD)+"~"+DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD);
  105. }
  106. saveDeletionInfoDO(fundId,tradeDate,DeletionType.DISTRIBUTION_DELETION.getCode());
  107. }
  108. preDifference = difference;
  109. }
  110. }
  111. private void assetDeletion(String fundId, List<AssetDO> assetDOList, List<TradeDateDO> tradeDateDOList, FundReportFrequencyDO fundReportFrequencyDO) {
  112. if(Frequency.DAY == Frequency.getFrequencyByCode(fundReportFrequencyDO.getAssetFrequency())){
  113. Map<String,List<AssetDO>> navListMap = assetDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
  114. Map<String,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getTradeDate(),DateUtils.YYYY_MM_DD)));
  115. for(String tradeDate : tradeListMap.keySet()){
  116. if(navListMap.containsKey(tradeDate)){
  117. deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo(),null);
  118. continue;
  119. }
  120. //写入缺失信息表
  121. saveDeletionInfoDO(fundId,tradeDate,DeletionType.ASSET_DELETION.getCode());
  122. }
  123. }
  124. if(Frequency.WEEK == Frequency.getFrequencyByCode(fundReportFrequencyDO.getAssetFrequency())){
  125. Map<String,List<AssetDO>> navListMap = assetDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
  126. TreeMap<Integer,List<AssetDO>> weekNavListMap = new TreeMap<>();
  127. //按周数整合
  128. for(String priceDate : navListMap.keySet()){
  129. Integer weekOfYear = DateUtil.weekOfYear(DateUtils.parse(priceDate,DateUtils.YYYY_MM_DD));
  130. if(weekNavListMap.containsKey(weekOfYear)){
  131. List<AssetDO> assetDOS = weekNavListMap.get(weekOfYear);
  132. assetDOS.addAll(navListMap.get(priceDate));
  133. weekNavListMap.put(weekOfYear,assetDOS);
  134. }else{
  135. List<AssetDO> navDOS = new ArrayList<>();
  136. navDOS.addAll(navListMap.get(priceDate));
  137. weekNavListMap.put(weekOfYear,navDOS);
  138. }
  139. }
  140. Map<Integer,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getWeekOfYear()));
  141. for(Integer weekOfYear : tradeListMap.keySet()){
  142. if(weekNavListMap.containsKey(weekOfYear)){
  143. List<AssetDO> assetDOS = weekNavListMap.get(weekOfYear);
  144. String tradeDate = DateUtils.format(assetDOS.get(assetDOS.size()-1).getPriceDate(),DateUtils.YYYY_MM_DD);
  145. deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo(),null);
  146. continue;
  147. }
  148. //不包含的话,默认取每周的最后一个交易日作为周净值日期
  149. List<TradeDateDO> tradeDateDOS = tradeListMap.get(weekOfYear);
  150. String tradeDate = null;
  151. if(tradeDateDOS.size() > 0){
  152. tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD);
  153. }
  154. //写入缺失信息表
  155. saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode());
  156. }
  157. }
  158. if(Frequency.MONTH == Frequency.getFrequencyByCode(fundReportFrequencyDO.getAssetFrequency())){
  159. Map<String,List<AssetDO>> navListMap = assetDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
  160. TreeMap<String,List<AssetDO>> monthNavListMap = new TreeMap<>();
  161. //按周数整合
  162. for(String priceDate : navListMap.keySet()){
  163. String yearMonth = priceDate.substring(0,7);
  164. if(monthNavListMap.containsKey(yearMonth)){
  165. List<AssetDO> navDOS = monthNavListMap.get(yearMonth);
  166. navDOS.addAll(navListMap.get(priceDate));
  167. monthNavListMap.put(yearMonth,navDOS);
  168. }else{
  169. List<AssetDO> navDOS = new ArrayList<>();
  170. navDOS.addAll(navListMap.get(priceDate));
  171. monthNavListMap.put(yearMonth,navDOS);
  172. }
  173. }
  174. Map<String,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getYearmonth()));
  175. for(String yearMonth : tradeListMap.keySet()){
  176. if(monthNavListMap.containsKey(yearMonth)){
  177. List<AssetDO> assetDOS = monthNavListMap.get(yearMonth);
  178. String tradeDate = DateUtils.format(assetDOS.get(assetDOS.size()-1).getPriceDate(),DateUtils.YYYY_MM_DD);
  179. deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo(),null);
  180. continue;
  181. }
  182. //不包含的话,默认取每周的最后一个交易日作为周净值日期
  183. List<TradeDateDO> tradeDateDOS = tradeListMap.get(yearMonth);
  184. String tradeDate = null;
  185. if(tradeDateDOS.size() > 0){
  186. tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD);
  187. }
  188. //写入缺失信息表
  189. saveDeletionInfoDO(fundId,tradeDate,DeletionType.ASSET_DELETION.getCode());
  190. }
  191. }
  192. }
  193. private void navDeletion(String fundId,List<NavDO> navDOList, List<TradeDateDO> tradeDateDOList,FundReportFrequencyDO fundReportFrequencyDO) {
  194. //只处理日月季频率
  195. if(Frequency.DAY == Frequency.getFrequencyByCode(fundReportFrequencyDO.getNavFrequency())){
  196. Map<String,List<NavDO>> navListMap = navDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
  197. Map<String,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getTradeDate(),DateUtils.YYYY_MM_DD)));
  198. for(String tradeDate : tradeListMap.keySet()){
  199. if(navListMap.containsKey(tradeDate)){
  200. deletionInfoMapper.updateRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo(),null);
  201. continue;
  202. }
  203. //写入缺失信息表
  204. saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode());
  205. }
  206. }
  207. if(Frequency.WEEK == Frequency.getFrequencyByCode(fundReportFrequencyDO.getNavFrequency())){
  208. Map<String,List<NavDO>> navListMap = navDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
  209. TreeMap<Integer,List<NavDO>> weekNavListMap = new TreeMap<>();
  210. //按周数整合
  211. for(String priceDate : navListMap.keySet()){
  212. Integer weekOfYear = DateUtil.weekOfYear(DateUtils.parse(priceDate,DateUtils.YYYY_MM_DD));
  213. if(weekNavListMap.containsKey(weekOfYear)){
  214. List<NavDO> navDOS = weekNavListMap.get(weekOfYear);
  215. navDOS.addAll(navListMap.get(priceDate));
  216. weekNavListMap.put(weekOfYear,navDOS);
  217. }else{
  218. List<NavDO> navDOS = new ArrayList<>();
  219. navDOS.addAll(navListMap.get(priceDate));
  220. weekNavListMap.put(weekOfYear,navDOS);
  221. }
  222. }
  223. Map<Integer,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getWeekOfYear()));
  224. for(Integer weekOfYear : tradeListMap.keySet()){
  225. if(weekNavListMap.containsKey(weekOfYear)){
  226. List<NavDO> navDOS = weekNavListMap.get(weekOfYear);
  227. String tradeDate = DateUtils.format(navDOS.get(navDOS.size()-1).getPriceDate(),DateUtils.YYYY_MM_DD);
  228. deletionInfoMapper.updateRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo(),null);
  229. continue;
  230. }
  231. //不包含的话,默认取每周的最后一个交易日作为周净值日期
  232. List<TradeDateDO> tradeDateDOS = tradeListMap.get(weekOfYear);
  233. String tradeDate = null;
  234. if(tradeDateDOS.size() > 0){
  235. tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD);
  236. }
  237. //写入缺失信息表
  238. saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode());
  239. }
  240. }
  241. if(Frequency.MONTH == Frequency.getFrequencyByCode(fundReportFrequencyDO.getNavFrequency())){
  242. Map<String,List<NavDO>> navListMap = navDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
  243. TreeMap<String,List<NavDO>> monthNavListMap = new TreeMap<>();
  244. //按周数整合
  245. for(String priceDate : navListMap.keySet()){
  246. String yearMonth = priceDate.substring(0,7);
  247. if(monthNavListMap.containsKey(yearMonth)){
  248. List<NavDO> navDOS = monthNavListMap.get(yearMonth);
  249. navDOS.addAll(navListMap.get(priceDate));
  250. monthNavListMap.put(yearMonth,navDOS);
  251. }else{
  252. List<NavDO> navDOS = new ArrayList<>();
  253. navDOS.addAll(navListMap.get(priceDate));
  254. monthNavListMap.put(yearMonth,navDOS);
  255. }
  256. }
  257. Map<String,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getYearmonth()));
  258. for(String yearMonth : tradeListMap.keySet()){
  259. if(monthNavListMap.containsKey(yearMonth)){
  260. List<NavDO> navDOS = monthNavListMap.get(yearMonth);
  261. String tradeDate = DateUtils.format(navDOS.get(navDOS.size()-1).getPriceDate(),DateUtils.YYYY_MM_DD);
  262. deletionInfoMapper.updateRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo(),null);
  263. continue;
  264. }
  265. //不包含的话,默认取每周的最后一个交易日作为周净值日期
  266. List<TradeDateDO> tradeDateDOS = tradeListMap.get(yearMonth);
  267. String tradeDate = null;
  268. if(tradeDateDOS.size() > 0){
  269. tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD);
  270. }
  271. //写入缺失信息表
  272. saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode());
  273. }
  274. }
  275. }
  276. private void saveDeletionInfoDO(String fundId, String tradeDate, Integer code) {
  277. if(tradeDate == null){
  278. return;
  279. }
  280. DeletionInfoDO deletionInfoDO = new DeletionInfoDO();
  281. deletionInfoDO.setFundId(fundId);
  282. deletionInfoDO.setDeletionDate(tradeDate);
  283. deletionInfoDO.setDeletionType(code);
  284. DeletionInfoDO oldDeletionDO = deletionInfoMapper.getDeletionInfoDO(deletionInfoDO);
  285. if(StringUtil.isNull(oldDeletionDO)){
  286. deletionInfoDO.setIsvalid(1);
  287. deletionInfoDO.setIsSend(0);
  288. deletionInfoDO.setUpdateTime(DateUtils.getNowDate());
  289. deletionInfoDO.setCreateTime(DateUtils.getNowDate());
  290. deletionInfoMapper.saveDeletionInfoDO(deletionInfoDO);
  291. }
  292. }
  293. }