BaseInfoServiceImpl.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. package com.smppw.analysis.domain.service.impl;
  2. import cn.hutool.core.collection.CollUtil;
  3. import cn.hutool.core.collection.ListUtil;
  4. import cn.hutool.core.map.MapUtil;
  5. import cn.hutool.core.util.NumberUtil;
  6. import cn.hutool.core.util.StrUtil;
  7. import com.smppw.analysis.domain.dao.FundInformationDao;
  8. import com.smppw.analysis.domain.dao.IndexesProfileDao;
  9. import com.smppw.analysis.domain.dao.RongzhiIndexNavDao;
  10. import com.smppw.analysis.domain.dataobject.FundSimilarDo;
  11. import com.smppw.analysis.domain.dataobject.MonetaryFundProfitDO;
  12. import com.smppw.analysis.domain.dto.info.FundSimilarParams;
  13. import com.smppw.analysis.domain.event.SaveCacheEvent;
  14. import com.smppw.analysis.domain.gateway.CacheFactory;
  15. import com.smppw.analysis.domain.gateway.CacheGateway;
  16. import com.smppw.analysis.domain.service.BaseInfoService;
  17. import com.smppw.analysis.infrastructure.config.AnalysisProperty;
  18. import com.smppw.analysis.infrastructure.consts.RedisConst;
  19. import com.smppw.common.cache.CaffeineLocalCache;
  20. import com.smppw.common.pojo.IStrategy;
  21. import com.smppw.common.pojo.enums.Frequency;
  22. import com.smppw.common.pojo.enums.Indicator;
  23. import com.smppw.common.pojo.enums.strategy.Strategy;
  24. import com.smppw.constants.SecType;
  25. import com.smppw.utils.StrategyHandleUtils;
  26. import org.slf4j.Logger;
  27. import org.slf4j.LoggerFactory;
  28. import org.springframework.beans.BeansException;
  29. import org.springframework.context.ApplicationContext;
  30. import org.springframework.context.ApplicationContextAware;
  31. import org.springframework.lang.NonNull;
  32. import org.springframework.stereotype.Service;
  33. import java.util.ArrayList;
  34. import java.util.HashMap;
  35. import java.util.List;
  36. import java.util.Map;
  37. import java.util.concurrent.TimeUnit;
  38. import java.util.function.Function;
  39. import java.util.stream.Collectors;
  40. @Service
  41. public class BaseInfoServiceImpl implements BaseInfoService, ApplicationContextAware {
  42. public static final String HF = "HF";
  43. public static final String MF = "MF";
  44. public static final String CF = "CF";
  45. public static final String FA = "FA";
  46. public static final String CI = "CI";
  47. public static final String CO = "CO";
  48. public static final String PL = "PL";
  49. public static final String PO = "PO";
  50. public static final String IN = "IN";
  51. public static final String AP = "AP";
  52. private static final Map<String, Boolean> INDEX_EXIST = MapUtil.newConcurrentHashMap();
  53. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  54. private final CacheGateway<Object> cacheGateway;
  55. private final IndexesProfileDao indexesProfileDao;
  56. private final RongzhiIndexNavDao rongzhiIndexNavDao;
  57. private final FundInformationDao fundInformationDao;
  58. private ApplicationContext applicationContext;
  59. public BaseInfoServiceImpl(AnalysisProperty property, CacheFactory factory,
  60. IndexesProfileDao indexesProfileDao, RongzhiIndexNavDao rongzhiIndexNavDao, FundInformationDao fundInformationDao) {
  61. this.cacheGateway = factory.getCacheGateway(property.getCacheType());
  62. this.indexesProfileDao = indexesProfileDao;
  63. this.rongzhiIndexNavDao = rongzhiIndexNavDao;
  64. this.fundInformationDao = fundInformationDao;
  65. }
  66. @Override
  67. public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
  68. this.applicationContext = applicationContext;
  69. }
  70. @Override
  71. public String getLatestRankRat() {
  72. // todo 排名期
  73. return "2023-07";
  74. }
  75. @Override
  76. public String getSecType(String secId) {
  77. if (secId == null) {
  78. return null;
  79. }
  80. if (secId.startsWith(HF)) {
  81. return SecType.PRIVATELY_OFFERED_FUND;
  82. } else if (secId.startsWith(MF)) {
  83. return SecType.PUBLICLY_OFFERED_FUNDS;
  84. } else if (secId.startsWith(CF)) {
  85. return SecType.PRIVATE_FUND;
  86. } else if (secId.startsWith(FA)) {
  87. return SecType.FACTOR;
  88. } else if (secId.startsWith(CI)) {
  89. return SecType.UDF_INDEX;
  90. } else if (secId.startsWith(CO)) {
  91. return SecType.COMPANY;
  92. } else if (secId.startsWith(PL)) {
  93. return SecType.MANAGER;
  94. } else if (secId.startsWith(PO)) {
  95. return SecType.COMBINATION;
  96. } else if (StrUtil.isNumeric(secId)) {
  97. if (Strategy.isStrategy(secId)) {
  98. return SecType.STRATEGY;
  99. }
  100. } else if (secId.startsWith(IN)) {
  101. List<String> thirdIndexes = CaffeineLocalCache.getThirdIndexes();
  102. if (thirdIndexes != null && thirdIndexes.contains(secId)) {
  103. return SecType.THIRD_INDEX_FUND;
  104. }
  105. List<String> riskOfFreeIdList = CaffeineLocalCache.getRiskOfFreeId();
  106. if (riskOfFreeIdList != null && riskOfFreeIdList.contains(secId)) {
  107. return SecType.RISK_OF_FREE;
  108. }
  109. Boolean isExist = INDEX_EXIST.get(secId);
  110. if (isExist == null) {
  111. isExist = rongzhiIndexNavDao.isExist(secId);
  112. INDEX_EXIST.put(secId, isExist);
  113. }
  114. if (isExist) {
  115. return SecType.INDEX_FUND;
  116. } else {
  117. return SecType.RONGZHI_INDEX;
  118. }
  119. } else if (secId.startsWith(AP)) {
  120. return SecType.ADVISORY_POOL_CURVE;
  121. }
  122. return null;
  123. }
  124. @Override
  125. public Map<String, List<String>> getTypeSecMap(List<String> secIdList) {
  126. Map<String, List<String>> secIdTypeMap = new HashMap<>(10);
  127. for (String secId : secIdList) {
  128. String secIdType = getSecType(secId);
  129. if (secIdTypeMap.containsKey(secIdType)) {
  130. List<String> list = secIdTypeMap.get(secIdType);
  131. list.add(secId);
  132. } else {
  133. List<String> list = new ArrayList<>();
  134. list.add(secId);
  135. secIdTypeMap.put(secIdType, list);
  136. }
  137. }
  138. return secIdTypeMap;
  139. }
  140. @Override
  141. public Map<String, String> querySecsType(List<String> secIdList) {
  142. if (CollUtil.isEmpty(secIdList)) {
  143. return MapUtil.newHashMap(8);
  144. }
  145. return secIdList.stream().collect(Collectors.toMap(e -> e, this::getSecType));
  146. }
  147. @Override
  148. public Frequency getNavFrequency(String secId) {
  149. Frequency frequency;
  150. String fundType = this.getSecType(secId);
  151. if (SecType.MANAGER.equals(fundType) || SecType.COMPANY.equals(fundType)) {
  152. frequency = Frequency.Monthly;
  153. } else if (SecType.PRIVATELY_OFFERED_FUND.equals(fundType) ||
  154. SecType.PUBLICLY_OFFERED_FUNDS.equals(fundType) ||
  155. SecType.INDEX_FUND.equals(fundType) ||
  156. SecType.RONGZHI_INDEX.equals(fundType)) {
  157. String freq = this.fundInformationDao.getNavFrequencyByFundId(secId);
  158. if ("天".equals(freq)) {
  159. frequency = Frequency.Daily;
  160. } else if ("周".equals(freq)) {
  161. frequency = Frequency.Weekly;
  162. } else {
  163. frequency = Frequency.Monthly;
  164. }
  165. } else {
  166. frequency = Frequency.Daily;
  167. }
  168. return frequency;
  169. }
  170. @Override
  171. public Map<String, String> querySecName(List<String> allSecIdList) {
  172. if (CollUtil.isEmpty(allSecIdList)) {
  173. return MapUtil.empty();
  174. }
  175. int size = allSecIdList.size();
  176. String key = RedisConst.INFO_NAME;
  177. Map<String, Object> hget = this.cacheGateway.hget(key);
  178. if (MapUtil.isEmpty(hget)) {
  179. hget = MapUtil.empty();
  180. }
  181. Map<Boolean, List<String>> redisSecMap = allSecIdList.stream().collect(Collectors.groupingBy(hget::containsKey));
  182. List<String> redisSecIds = redisSecMap.getOrDefault(Boolean.TRUE, ListUtil.empty());
  183. List<String> noRedisSecIds = redisSecMap.getOrDefault(Boolean.FALSE, ListUtil.empty());
  184. Map<String, Object> secNameMap = MapUtil.newHashMap(size, false);
  185. if (CollUtil.isNotEmpty(noRedisSecIds)) {
  186. Map<String, List<String>> typeSecMap = this.getTypeSecMap(noRedisSecIds);
  187. // 市场基金
  188. List<String> marketFundIds = ListUtil.of(SecType.PRIVATELY_OFFERED_FUND, SecType.PUBLICLY_OFFERED_FUNDS);
  189. this.loadNameMap(secNameMap, typeSecMap, marketFundIds, this.fundInformationDao::getMarketFundIdNameMap);
  190. // 市场指数
  191. List<String> marketIndexIds = ListUtil.of(SecType.INDEX_FUND, SecType.RONGZHI_INDEX, SecType.THIRD_INDEX_FUND);
  192. this.loadNameMap(secNameMap, typeSecMap, marketIndexIds, this.indexesProfileDao::getFundIdNameMap);
  193. // 推送事件,存缓存
  194. SaveCacheEvent<Map<String, Object>> event = new SaveCacheEvent<>(key, secNameMap, t -> {
  195. this.cacheGateway.hset(key, t);
  196. return this.cacheGateway.expire(key, 1, TimeUnit.DAYS);
  197. });
  198. this.applicationContext.publishEvent(event);
  199. }
  200. // 解决乱序问题
  201. Map<String, String> result = MapUtil.newHashMap(size, true);
  202. for (String secId : allSecIdList) {
  203. String name = redisSecIds.contains(secId) ? MapUtil.getStr(hget, secId) : MapUtil.getStr(secNameMap, secId);
  204. result.put(secId, name);
  205. }
  206. return result;
  207. }
  208. @Override
  209. public List<MonetaryFundProfitDO> queryMonetaryFund(String fundId) {
  210. return this.fundInformationDao.queryMonetaryFund(fundId);
  211. }
  212. @Override
  213. public List<Map<String, Object>> getFundRank(String rankDate, String fundId, List<String> indexIds, Indicator indicator) {
  214. return this.fundInformationDao.getFundRank(rankDate, fundId, indexIds, indicator);
  215. }
  216. @Override
  217. public List<FundSimilarDo> getFundSimilarList(FundSimilarParams params) {
  218. if (StrUtil.isBlank(params.getThreshold()) || !NumberUtil.isNumber(params.getThreshold())) {
  219. logger.warn(String.format("相关性阈值 %s 设置错误,提供默认值:%s", params.getThreshold(), FundSimilarParams.DEFAULT_THRESHOLD));
  220. params.setThreshold(FundSimilarParams.DEFAULT_THRESHOLD);
  221. }
  222. IStrategy strategy = StrategyHandleUtils.getStrategy(params.getStrategy());
  223. String rankDate = this.getLatestRankRat();
  224. List<FundSimilarDo> tempList = ListUtil.list(true);
  225. if (params.getCalcType() == 2) {
  226. Map<String, Object> req = MapUtil.<String, Object>builder().put("strategy", strategy.getStrategyId())
  227. .put("tableName", "rz_hfdb_core.nav").put("trustId", params.getTrustId()).put("rankDate", rankDate).build();
  228. tempList.addAll(this.fundInformationDao.getSameCompanyFundIds(req));
  229. } else {
  230. Map<String, Object> req = MapUtil.<String, Object>builder().put("strategy", strategy.getStrategyId()).put("rankNum", 50)
  231. .put("tableName", "rz_hfdb_core.fund_indicator_ranking").put("fundId", params.getRefId()).put("rankDate", rankDate).build();
  232. tempList.addAll(this.fundInformationDao.getSameStrategyFundIds(req));
  233. }
  234. List<FundSimilarDo> dataList = ListUtil.list(true);
  235. // 过滤当前基金
  236. dataList.addAll(tempList.stream().filter(e -> !params.getRefId().equals(e.getFundId())).collect(Collectors.toList()));
  237. return dataList;
  238. }
  239. /**
  240. * 把指定类型的标的的名称映射查询出来
  241. *
  242. * @param secNameMap 保存的map
  243. * @param typeSecMap 指定类型对应的标的
  244. * @param types 指定类型列表
  245. * @param function 查询操作封装
  246. */
  247. private void loadNameMap(Map<String, Object> secNameMap, Map<String, List<String>> typeSecMap,
  248. List<String> types, Function<List<String>, Map<String, String>> function) {
  249. List<String> refIds = ListUtil.list(true);
  250. for (String type : types) {
  251. CollUtil.addAllIfNotContains(refIds, typeSecMap.getOrDefault(type, ListUtil.empty()));
  252. }
  253. if (CollUtil.isNotEmpty(refIds)) {
  254. secNameMap.putAll(function.apply(refIds));
  255. }
  256. }
  257. }