BaseInfoServiceImpl.java 12 KB

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