FundDeletionTask.java 17 KB

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