CoinServiceImpl.java 153 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813
  1. package top.lvzhiqiang.service.impl;
  2. import com.alibaba.fastjson.JSONArray;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.github.pagehelper.PageHelper;
  5. import com.github.pagehelper.PageInfo;
  6. import lombok.extern.slf4j.Slf4j;
  7. import me.chanjar.weixin.common.error.WxErrorException;
  8. import me.chanjar.weixin.cp.api.WxCpGroupRobotService;
  9. import me.chanjar.weixin.cp.api.WxCpService;
  10. import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
  11. import me.chanjar.weixin.cp.bean.article.NewArticle;
  12. import me.chanjar.weixin.cp.bean.message.WxCpMessage;
  13. import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult;
  14. import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
  15. import org.apache.commons.lang3.time.DurationFormatUtils;
  16. import org.jsoup.Connection;
  17. import org.redisson.api.RLock;
  18. import org.redisson.api.RedissonClient;
  19. import org.springframework.beans.factory.annotation.Autowired;
  20. import org.springframework.scheduling.annotation.Async;
  21. import org.springframework.stereotype.Service;
  22. import org.springframework.transaction.annotation.Propagation;
  23. import org.springframework.transaction.annotation.Transactional;
  24. import org.springframework.util.StopWatch;
  25. import top.lvzhiqiang.config.InitRunner;
  26. import top.lvzhiqiang.config.WorkWeixinProperties;
  27. import top.lvzhiqiang.dto.CoinYoutubeYt2140LiveChapterDTO;
  28. import top.lvzhiqiang.dto.R;
  29. import top.lvzhiqiang.entity.*;
  30. import top.lvzhiqiang.enumeration.ResultCodeEnum;
  31. import top.lvzhiqiang.exception.BusinessException;
  32. import top.lvzhiqiang.exception.ParameterException;
  33. import top.lvzhiqiang.mapper.*;
  34. import top.lvzhiqiang.service.CoinService;
  35. import top.lvzhiqiang.util.*;
  36. import javax.annotation.Resource;
  37. import java.io.UnsupportedEncodingException;
  38. import java.math.BigDecimal;
  39. import java.math.MathContext;
  40. import java.math.RoundingMode;
  41. import java.net.Proxy;
  42. import java.security.InvalidKeyException;
  43. import java.text.DecimalFormat;
  44. import java.time.Duration;
  45. import java.time.LocalDate;
  46. import java.time.LocalDateTime;
  47. import java.util.*;
  48. import java.util.concurrent.*;
  49. import java.util.concurrent.atomic.AtomicInteger;
  50. import java.util.function.Function;
  51. import java.util.stream.Collectors;
  52. import java.util.stream.Stream;
  53. /**
  54. * Coin ServiceImpl
  55. *
  56. * @author lvzhiqiang
  57. * 2023/9/5 15:23
  58. */
  59. @Service
  60. @Slf4j
  61. public class CoinServiceImpl implements CoinService {
  62. /**
  63. * 任务告警方式-应用文本卡片
  64. */
  65. public static final String JOB_ALARM_MODE_APP_TEXT_CARD = "1";
  66. /**
  67. * 任务告警方式-群聊机器人
  68. */
  69. public static final String JOB_ALARM_MODE_CHAT_BOT = "2";
  70. /**
  71. * 任务告警方式(1:应用文本卡片,2:群聊机器人文本消息)
  72. */
  73. public static String JOB_ALARM_MODE = "1";
  74. // 所有REST请求的header都必须包含以下key:
  75. private static final Map<String, String> basicHeaderMap = new HashMap<>();
  76. private static final Map<String, String> basicHeaderMap4OKX = new HashMap<>();
  77. // 主域名 URL
  78. private static final String mainUrl = "https://api.bitget.com";
  79. // 私钥,由系统随机生成,用于签名的生成。
  80. private static final String secretKey = "1fdd0fc2976bea80189ba13710e12825ca3ef6c5e25a0d76fd03f8f6cd4a61d9";
  81. private static final String secretKey4OKX = "32AC470662FBB633374B9A41950995A9";
  82. @Resource
  83. private CoinMapper coinMapper;
  84. @Resource
  85. private WxCpService wxCpService;
  86. @Autowired(required = false)
  87. private WorkWeixinProperties properties;
  88. private final Map<String, String> orderMap = new ConcurrentHashMap<>();
  89. private final Map<String, JSONObject> mixMap = new ConcurrentHashMap<>();
  90. private final static ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(10);
  91. private final ForkJoinPool forkJoinPool = new ForkJoinPool(16);
  92. private final ForkJoinPool forkJoinPool2 = new ForkJoinPool(16);
  93. private final ForkJoinPool forkJoinPool3 = new ForkJoinPool(16);
  94. private final ForkJoinPool forkJoinPool4 = new ForkJoinPool(16);
  95. private final ForkJoinPool forkJoinPool5 = new ForkJoinPool(16);
  96. private static final DecimalFormat df1 = new DecimalFormat("#,##0.00");
  97. private static final DecimalFormat df2 = new DecimalFormat("#,##0");
  98. private static final WxCpServiceImpl wxCpService4News;
  99. @Resource
  100. private CoinApiConfigMapper coinApiConfigMapper;
  101. @Resource
  102. private PictureInfoMapper pictureInfoMapper;
  103. @Resource
  104. private MusicInfoMapper musicInfoMapper;
  105. @Resource
  106. private CoinYoutubeMapper coinYoutubeMapper;
  107. @Resource
  108. private GoldenQuotesMapper goldenQuotesMapper;
  109. @Resource
  110. private BookmarkMapper bookmarkMapper;
  111. @Resource
  112. private RedissonClient redissonClient;
  113. @Resource
  114. private RedisUtils redisUtils;
  115. static {
  116. // API KEY作为一个字符串。
  117. basicHeaderMap.put("ACCESS-KEY", "bg_433d37306df0e8901c6d107c6d9e9111");
  118. // 使用base64编码签名(请参阅签名消息)。
  119. basicHeaderMap.put("ACCESS-SIGN", "");
  120. // 您请求的时间戳。
  121. basicHeaderMap.put("ACCESS-TIMESTAMP", "");
  122. // 您在创建API KEY时设置的口令。
  123. basicHeaderMap.put("ACCESS-PASSPHRASE", "7f934f62f2701bee932204580d115228");
  124. // 统一设置为application/json。
  125. basicHeaderMap.put("Content-Type", "application/json");
  126. // 支持多语言, 如:中文(zh-CN),英语(en-US)
  127. basicHeaderMap.put("locale", "zh-CN");
  128. // 字符串类型的APIKey
  129. basicHeaderMap4OKX.put("OK-ACCESS-KEY", "25e4f515-5efd-4bb9-a934-3949b21d9f10");
  130. // 使用HMAC SHA256哈希函数获得哈希值,再使用Base-64编码(请参阅签名)
  131. basicHeaderMap4OKX.put("OK-ACCESS-SIGN", "");
  132. // 发起请求的时间(UTC),如:2020-12-08T09:08:57.715Z
  133. basicHeaderMap4OKX.put("OK-ACCESS-TIMESTAMP", "");
  134. // 您在创建API密钥时指定的Passphrase
  135. basicHeaderMap4OKX.put("OK-ACCESS-PASSPHRASE", "tmvxeGY#Q#Y2qm8");
  136. // 统一设置为application/json
  137. basicHeaderMap4OKX.put("Content-Type", "application/json");
  138. df1.setRoundingMode(RoundingMode.HALF_UP);
  139. WxCpDefaultConfigImpl wxCpDefaultConfig = new WxCpDefaultConfigImpl();
  140. wxCpDefaultConfig.setCorpId("ww95a4adba56acb55f");
  141. wxCpDefaultConfig.setAgentId(1000004);
  142. wxCpDefaultConfig.setCorpSecret("hG50gbVZ8pXm3tSzY3BpwTzbrMmm6sTf8_bPfJG_6Yc");
  143. wxCpService4News = new WxCpServiceImpl();
  144. wxCpService4News.setWxCpConfigStorage(wxCpDefaultConfig);
  145. }
  146. @Override
  147. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  148. public void syncData(String startTime, String endTime, String pageSize) {
  149. // 获取全部历史委托
  150. Map<String, String> paramMap = new LinkedHashMap<>();
  151. paramMap.put("productType", "umcbl");
  152. paramMap.put("startTime", startTime);
  153. paramMap.put("endTime", endTime);
  154. paramMap.put("pageSize", pageSize);
  155. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  156. JSONObject response = null;
  157. try {
  158. response = requestApi4Common("/api/mix/v1/order/historyProductType", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  159. JSONArray orderList = response.getJSONObject("data").getJSONArray("orderList");
  160. if (orderList != null && orderList.size() > 0) {
  161. coinMapper.insertHistoryOrderList(JSONArray.parseArray(orderList.toJSONString(), CoinHistoryOrder.class));
  162. log.warn("syncData->insertHistoryOrderList,startTime={},endTime={},size={}", startTime, endTime, orderList.size());
  163. }
  164. } catch (Exception e) {
  165. log.error("syncData->insertHistoryOrderList error,response={}", response, e);
  166. }
  167. }
  168. @Override
  169. public void syncData4TraderList() {
  170. StopWatch stopWatch = new StopWatch();
  171. stopWatch.start();
  172. // 获取交易员列表
  173. Map<String, String> paramMap = new LinkedHashMap<>();
  174. paramMap.put("sortRule", "composite");
  175. paramMap.put("sortFlag", "desc");
  176. paramMap.put("languageType", "en-US");
  177. paramMap.put("pageSize", "20");
  178. int i = 0;
  179. String url = "/api/mix/v1/trace/traderList";
  180. JSONObject response;
  181. int totalNum = 0;
  182. for (; ; ) {
  183. paramMap.put("pageNo", String.valueOf(++i));
  184. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  185. response = requestApi4Common(url, signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  186. JSONArray dataList = response.getJSONArray("data");
  187. if (dataList.size() == 0) {
  188. break;
  189. }
  190. try {
  191. Thread.sleep(5000L);
  192. syncData4TraderListSub(dataList);
  193. } catch (Exception e) {
  194. log.error("syncData4TraderListSub error,paramMap={},response={}", paramMap, response.toJSONString(), e);
  195. }
  196. totalNum += dataList.size();
  197. }
  198. log.warn("syncData4TraderList 结束:time={},totalNum={}", stopWatch.getTotalTimeSeconds(), totalNum);
  199. }
  200. @Override
  201. public void syncCoinmarketcapCMap() {
  202. StopWatch stopWatch = new StopWatch();
  203. stopWatch.start();
  204. String coinmarketcapApikey = InitRunner.dicCodeMap.get("coinmarketcap_apikey").getCodeValue();
  205. String coinmarketcapIdmapUrl = InitRunner.dicCodeMap.get("coinmarketcap_idmap_url").getCodeValue();
  206. String coinmarketcapIdmapParams4listingStatus = InitRunner.dicCodeMap.get("coinmarketcap_idmap_params_listing_status").getCodeValue();
  207. String coinmarketcapIdmapParams4aux = InitRunner.dicCodeMap.get("coinmarketcap_idmap_params_aux").getCodeValue();
  208. Map<String, String> headerMap = new HashMap<>();
  209. headerMap.put("Accept", "application/json");
  210. headerMap.put("Accept-Encoding", "deflate,gzip");
  211. headerMap.put("X-CMC_PRO_API_KEY", coinmarketcapApikey);
  212. String[] listingStatusArr = coinmarketcapIdmapParams4listingStatus.split(",");
  213. Map<String, String> paramMap = new LinkedHashMap<>();
  214. int MAX_NUMBER = 1000;
  215. int MAX_NUMBER2 = 5000;
  216. Long totalNum = 0L;
  217. for (String listingStatus : listingStatusArr) {
  218. paramMap.put("listing_status", listingStatus);
  219. paramMap.put("aux", coinmarketcapIdmapParams4aux);
  220. try {
  221. int j = 0;
  222. Long totalNum2 = 0L;
  223. for (; ; ) {
  224. Thread.sleep(3000L);
  225. j++;
  226. paramMap.put("start", String.valueOf((j - 1) * MAX_NUMBER2 + 1));
  227. paramMap.put("limit", String.valueOf(MAX_NUMBER2));
  228. Connection.Response response = JsoupUtil.requestBody(coinmarketcapIdmapUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, headerMap, paramMap);
  229. JSONObject result = JSONObject.parseObject(response.body());
  230. JSONArray dataJA = result.getJSONArray("data");
  231. List<CoinCmcMap> cmcMapList = new ArrayList<>();
  232. CoinCmcMap coinCmcMap;
  233. for (int i = 0; i < dataJA.size(); i++) {
  234. JSONObject dataJO = dataJA.getJSONObject(i);
  235. coinCmcMap = new CoinCmcMap();
  236. coinCmcMap.setCmcId(dataJO.getLong("id"));
  237. coinCmcMap.setCmcRank(dataJO.getLong("rank"));
  238. coinCmcMap.setName(dataJO.getString("name"));
  239. coinCmcMap.setSymbol(dataJO.getString("symbol"));
  240. coinCmcMap.setSlug(dataJO.getString("slug"));
  241. coinCmcMap.setIsActive(dataJO.getInteger("is_active"));
  242. coinCmcMap.setStatus(dataJO.getString("status"));
  243. coinCmcMap.setFirstHistoricalData(DateUtils.stringutcToLocalDateTime(dataJO.getString("first_historical_data")));
  244. coinCmcMap.setLastHistoricalData(DateUtils.stringutcToLocalDateTime(dataJO.getString("last_historical_data")));
  245. coinCmcMap.setPlatform(dataJO.getString("platform"));
  246. cmcMapList.add(coinCmcMap);
  247. }
  248. // 新增或者更新
  249. Stream.iterate(0, n -> n + 1).limit((cmcMapList.size() + MAX_NUMBER - 1) / MAX_NUMBER)
  250. .forEach(i -> {
  251. List<CoinCmcMap> list = cmcMapList.stream().skip((long) i * MAX_NUMBER).limit(MAX_NUMBER).collect(Collectors.toList());
  252. coinMapper.insertCmcMapList(list);
  253. });
  254. totalNum += cmcMapList.size();
  255. totalNum2 += cmcMapList.size();
  256. if (dataJA.size() < MAX_NUMBER2) {
  257. break;
  258. }
  259. }
  260. log.warn("syncCoinmarketcapCMap {} success,totalNum={}", listingStatus, totalNum2);
  261. } catch (Exception e) {
  262. log.error("syncCoinmarketcapCMap {} error", listingStatus, e);
  263. }
  264. }
  265. log.warn("syncCoinmarketcapCMap 结束:time={},totalNum={}", stopWatch.getTotalTimeSeconds(), totalNum);
  266. }
  267. @Override
  268. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  269. public void syncData4TraderListSub(JSONArray dataList) {
  270. coinMapper.insertMixTradeList(parseMixTradeList(dataList));
  271. }
  272. @Override
  273. public String watchlistDetail(Integer userId, String symbol, String operationType) {
  274. CoinWatchlist coinWatchlist = coinMapper.findWatchlistUserBySymbolAndUserId(symbol, userId);
  275. if (coinWatchlist == null) {
  276. throw new BusinessException(1, "symbol不存在!");
  277. }
  278. if ("detail".equals(operationType)) {
  279. return parseWatchlistOther(symbol) + MarkdownToHtmlUtils.markdownToHtmlExtensions(coinWatchlist.getRemark());
  280. } else if ("update".equals(operationType)) {
  281. return coinWatchlist.getRemark();
  282. } else {
  283. return "暂不支持该操作!";
  284. }
  285. }
  286. @Override
  287. public Object watchlistUpdate(String symbol, Integer userId, String remark, String score) {
  288. CoinWatchlist coinWatchlist = new CoinWatchlist();
  289. coinWatchlist.setSymbol(symbol);
  290. coinWatchlist.setUserId(userId);
  291. coinWatchlist.setRemark(remark);
  292. coinWatchlist.setScore(score);
  293. int num = coinMapper.updateCoinWatchlistRemarkAndScore(coinWatchlist);
  294. return num;
  295. }
  296. private List<CoinTrader> parseMixTradeList(JSONArray dataList) {
  297. List<CoinTrader> mixTraderList = JSONArray.parseArray(dataList.toJSONString(), CoinTrader.class);
  298. mixTraderList.stream().forEach(e -> {
  299. Map<String, String> columnMap = e.getColumnList().stream().filter(Objects::nonNull)
  300. .collect(Collectors.toMap(
  301. object -> {
  302. JSONObject item = (JSONObject) object;
  303. return item.getString("describe");
  304. },
  305. object -> {
  306. JSONObject item = (JSONObject) object;
  307. return item.getString("value");
  308. }
  309. ));
  310. e.setRoi(columnMap.get("ROI"));
  311. e.setTotalProfit(columnMap.containsKey("Total PnL") ? columnMap.get("Total PnL").replace("$", "").replace(",", "") : null);
  312. e.setTotalFollowersProfit(columnMap.containsKey("Total followers PnL") ? columnMap.get("Total followers PnL").replace("$", "").replace(",", "") : null);
  313. e.setAum(columnMap.containsKey("AUM") ? columnMap.get("AUM").replace("$", "").replace(",", "") : null);
  314. e.setMaxCallbackRate(columnMap.get("Max drawdown"));
  315. e.setLast3wWinRate(columnMap.get("Last 3W win rate"));
  316. e.setAverageWinRate(StringUtils.isNotEmpty(e.getAverageWinRate()) ? new BigDecimal(e.getAverageWinRate()).setScale(2, RoundingMode.HALF_UP).toPlainString() : "0.00");
  317. e.setTraderNickName(StringUtils.isNotEmpty(e.getTraderNickName()) ? e.getTraderNickName() : "--");
  318. });
  319. return mixTraderList;
  320. }
  321. @Override
  322. public String orderDetail(String trackingNo) {
  323. Map<String, String> paramMap = new LinkedHashMap<>();
  324. paramMap.put("traderId", "b1b5467f8bb73f53ac97");
  325. paramMap.put("pageSize", "20");
  326. StringBuffer sb = new StringBuffer();
  327. // 交易员当前带单列表筛选
  328. for (int j = 1; j < 5; j++) {
  329. try {
  330. paramMap.put("pageNo", j + "");
  331. JSONObject response = requestApi4Common("/api/mix/v1/trace/report/order/currentList", null, JSONObject.toJSONString(paramMap), JsoupUtil.HTTP_POST, paramMap);
  332. JSONArray orderList = response.getJSONArray("data");
  333. for (int i = 0; i < orderList.size(); i++) {
  334. JSONObject order = orderList.getJSONObject(i);
  335. String trackingNo1 = order.getString("trackingNo");
  336. if (trackingNo.equals(trackingNo1)) {
  337. sb.append("<table border=\"1\" cellspacing=\"0\"><tr><th>键</th><th>值</th></tr>");
  338. sb.append("<tr><td>交易对</td><td>").append(order.getString("symbol")).append("</td></tr>");
  339. sb.append("<tr><td>持仓方向</td><td>").append(InitRunner.publicParamsMap.get("holdSide").getString(order.getString("holdSide"))).append("</td></tr>");
  340. sb.append("<tr><td>杠杆倍数</td><td>").append(order.getString("leverage")).append("</td></tr>");
  341. sb.append("<tr><td>开仓均价</td><td>").append(order.getString("openPrice")).append("</td></tr>");
  342. sb.append("<tr><td>开仓时间</td><td>").append(DateUtils.longToString(order.getLong("openTime"))).append("</td></tr>");
  343. sb.append("<tr><td>此笔订单跟单人数</td><td>").append(order.getString("followerNum")).append("</td></tr>");
  344. sb.append("<tr><td>保证金</td><td>").append(order.getString("marginAmount")).append("</td></tr>");
  345. sb.append("<tr><td>止盈价</td><td>").append(order.getString("takeProfitPrice")).append("</td></tr>");
  346. sb.append("<tr><td>止损价</td><td>").append(order.getString("stopLossPrice")).append("</td></tr>");
  347. sb.append("<tr><td>交易员</td><td>").append("hale").append("</td></tr>");
  348. sb.append("</table>");
  349. break;
  350. }
  351. }
  352. } catch (Exception e) {
  353. }
  354. }
  355. // 交易员历史带单列表筛选
  356. if (sb.length() == 0) {
  357. for (int j = 1; j < 5; j++) {
  358. try {
  359. paramMap.put("pageNo", j + "");
  360. JSONObject response = requestApi4Common("/api/mix/v1/trace/report/order/historyList", null, JSONObject.toJSONString(paramMap), JsoupUtil.HTTP_POST, paramMap);
  361. JSONArray orderList = response.getJSONArray("data");
  362. for (int i = 0; i < orderList.size(); i++) {
  363. JSONObject order = orderList.getJSONObject(i);
  364. String trackingNo1 = order.getString("trackingNo");
  365. if (trackingNo.equals(trackingNo1)) {
  366. sb.append("<table border=\"1\" cellspacing=\"0\" style=\"font-size: 20px;\"><tr><th>键</th><th>值</th></tr>");
  367. sb.append("<tr><td>交易对</td><td>").append(order.getString("symbol")).append("</td></tr>");
  368. sb.append("<tr><td>持仓方向</td><td>").append(InitRunner.publicParamsMap.get("holdSide").getString(order.getString("holdSide"))).append("</td></tr>");
  369. sb.append("<tr><td>杠杆倍数</td><td>").append(order.getString("leverage")).append("</td></tr>");
  370. sb.append("<tr><td>开仓均价</td><td>").append(order.getString("openPrice")).append("</td></tr>");
  371. sb.append("<tr><td>开仓时间</td><td>").append(DateUtils.longToString(order.getLong("openTime"))).append("</td></tr>");
  372. sb.append("<tr><td>此笔订单跟单人数</td><td>").append(order.getString("followerNum")).append("</td></tr>");
  373. sb.append("<tr><td>保证金</td><td>").append(order.getString("marginAmount")).append("</td></tr>");
  374. sb.append("<tr><td>平仓均价</td><td>").append(order.getString("closePrice")).append("</td></tr>");
  375. sb.append("<tr><td>平仓时间</td><td>").append(DateUtils.longToString(order.getLong("closeTime"))).append("</td></tr>");
  376. sb.append("<tr><td>平仓数量</td><td>").append(order.getString("closeAmount")).append("</td></tr>");
  377. sb.append("<tr><td>交易员</td><td>").append("hale").append("</td></tr>");
  378. sb.append("</table>");
  379. break;
  380. }
  381. }
  382. } catch (Exception e) {
  383. }
  384. }
  385. }
  386. return sb.toString();
  387. }
  388. @Override
  389. public String orderDetail2(String orderId, String symbol) {
  390. Map<String, String> paramMap = new LinkedHashMap<>();
  391. paramMap.put("symbol", symbol);
  392. paramMap.put("orderId", orderId);
  393. StringBuffer sb = new StringBuffer("<table border=\"1\" cellspacing=\"0\" style=\"font-size: 20px;\"><tr><th>键</th><th>值</th></tr>");
  394. // 获取订单详情
  395. try {
  396. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  397. JSONObject response = requestApi4Common("/api/mix/v1/order/detail", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  398. JSONObject order = response.getJSONObject("data");
  399. sb.append("<tr><td>交易对</td><td>").append(order.getString("symbol")).append("</td></tr>");
  400. sb.append("<tr><td>交易方向</td><td>").append(InitRunner.publicParamsMap.get("side").getString(order.getString("side"))).append("</td></tr>");
  401. sb.append("<tr><td>杠杆倍数</td><td>").append(order.getString("leverage")).append("</td></tr>");
  402. sb.append("<tr><td>成交均价</td><td>").append(order.getString("priceAvg")).append("</td></tr>");
  403. sb.append("<tr><td>委托价格</td><td>").append(order.getString("price")).append("</td></tr>");
  404. sb.append("<tr><td>手续费</td><td>").append(order.getString("fee")).append("</td></tr>");
  405. sb.append("<tr><td>订单状态</td><td>").append(InitRunner.publicParamsMap.get("state").getString(order.getString("state"))).append("</td></tr>");
  406. sb.append("<tr><td>交易类型</td><td>").append(InitRunner.publicParamsMap.get("orderType").getString(order.getString("orderType"))).append("</td></tr>");
  407. sb.append("<tr><td>总盈亏</td><td>").append(order.getString("totalProfits")).append("</td></tr>");
  408. sb.append("<tr><td>预设止盈价格</td><td>").append(order.getString("presetTakeProfitPrice")).append("</td></tr>");
  409. sb.append("<tr><td>预设止损价格</td><td>").append(order.getString("presetStopLossPrice")).append("</td></tr>");
  410. sb.append("<tr><td>创建时间</td><td>").append(DateUtils.longToString(order.getLong("cTime"))).append("</td></tr>");
  411. sb.append("<tr><td>更新时间</td><td>").append(DateUtils.longToString(order.getLong("uTime"))).append("</td></tr>");
  412. } catch (Exception e) {
  413. log.error("orderDetail2 error,orderId={},symbol={}", orderId, symbol, e);
  414. }
  415. sb.append("</table>");
  416. return sb.toString();
  417. }
  418. @Override
  419. public String monitorJob() {
  420. // BITGET开仓平仓监控报警
  421. scheduler.scheduleWithFixedDelay(() -> {
  422. monitorBitgetMixOrder();
  423. }, 0, 10, TimeUnit.SECONDS);
  424. // OKX开仓平仓监控报警
  425. scheduler.scheduleWithFixedDelay(() -> {
  426. if (!"1".equals(getMonitorJobStatus("okx-mix-order"))) {
  427. return;
  428. }
  429. LocalDateTime endTime = LocalDateTime.now();
  430. // 查看历史持仓信息
  431. Map<String, String> paramMap = new LinkedHashMap<>();
  432. paramMap.put("instType", "SWAP");
  433. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  434. try {
  435. JSONObject response = requestApi4Common4OKX("/api/v5/account/positions-history", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  436. JSONArray orderList = response.getJSONArray("data");
  437. for (int i = 0; i < orderList.size(); i++) {
  438. JSONObject order = orderList.getJSONObject(i);
  439. //LocalDateTime cTime = DateUtils.longToLocalDateTime(order.getLong("cTime"));
  440. String orderId = "okx" + order.getString("posId");
  441. String symbol = order.getString("ccy");
  442. if (!orderMap.containsKey(orderId)) {
  443. orderMap.put(orderId, "1");
  444. log.warn("okx ={}", order);
  445. String content = "<div class=\"highlight\">交易对:" + symbol + "</div>";
  446. JSONObject params = new JSONObject();
  447. params.put("title", "OKX报警");
  448. params.put("logUrl", "https://jav.lvzhiqiang.top/coin/orderDetail2/" + orderId + "/" + symbol);
  449. params.put("btnTxt", "订单详情");
  450. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, null);
  451. }
  452. }
  453. } catch (Exception e) {
  454. log.error("okx-mix-order error", e);
  455. }
  456. }, 0, 10, TimeUnit.SECONDS);
  457. scheduler.scheduleWithFixedDelay(() -> {
  458. monitorBitgetMixReturnrate();
  459. }, 0, 10, TimeUnit.SECONDS);
  460. // BITGET跟单员监控报警
  461. scheduler.scheduleWithFixedDelay(() -> {
  462. if (!"1".equals(getMonitorJobStatus("bitget-mix-trader"))) {
  463. return;
  464. }
  465. try {
  466. List<String> monitorTraderList = coinMapper.findMonitorTraderList();
  467. forkJoinPool5.submit(() -> monitorTraderList.parallelStream().forEach(e -> {
  468. LocalDateTime endTime = LocalDateTime.now();
  469. // 交易员当前带单列表
  470. Map<String, String> paramMap = new LinkedHashMap<>();
  471. String[] split = e.split("\\|");
  472. paramMap.put("traderId", split[0]);
  473. paramMap.put("pageNo", "1");
  474. paramMap.put("pageSize", "20");
  475. try {
  476. JSONObject response = requestApi4Common("/api/mix/v1/trace/report/order/currentList", null, JSONObject.toJSONString(paramMap), JsoupUtil.HTTP_POST, paramMap);
  477. JSONArray orderList = response.getJSONArray("data");
  478. if (null != orderList) {
  479. for (int i = 0; i < orderList.size(); i++) {
  480. JSONObject order = orderList.getJSONObject(i);
  481. LocalDateTime openTime = DateUtils.longToLocalDateTime(order.getLong("openTime"));
  482. String trackingNo = order.getString("trackingNo");
  483. if (Duration.between(openTime, endTime).getSeconds() < 50 && !orderMap.containsKey(trackingNo)) {
  484. orderMap.put(trackingNo, "1");
  485. String content = "<div class=\"highlight\">交易对:" + order.getString("symbol") + "</div>" +
  486. "<div>持仓方向:" + InitRunner.publicParamsMap.get("holdSide").getString(order.getString("holdSide")) + "</div>" +
  487. "<div>杠杆倍数:" + order.getString("leverage") + "</div>" +
  488. "<div>开仓均价:" + order.getString("openPrice") + "</div>" +
  489. "<div>止盈价:" + order.getString("takeProfitPrice") + "</div>" +
  490. "<div>止损价:" + order.getString("stopLossPrice") + "</div>" +
  491. "<div >交易员:" + split[1] + "</div>" +
  492. "<div class=\"gray\">开仓时间:" + DateUtils.longToString(order.getLong("openTime")) + "</div>";
  493. JSONObject params = new JSONObject();
  494. params.put("title", "BITGET交易员开单报警");
  495. params.put("logUrl", "https://jav.lvzhiqiang.top/coin/orderDetail/" + order.getString("trackingNo"));
  496. params.put("btnTxt", "跟单详情");
  497. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, null);
  498. }
  499. }
  500. }
  501. } catch (Exception ex) {
  502. log.error("bitget-mix-trader error,param={}", paramMap, ex);
  503. }
  504. })).join();
  505. } catch (Exception e) {
  506. log.error("bitget-mix-trader top error", e);
  507. }
  508. }, 0, 10, TimeUnit.SECONDS);
  509. // 星球日报新闻快讯监控报警
  510. scheduler.scheduleWithFixedDelay(() -> {
  511. if (!"1".equals(getMonitorJobStatus("news-odaily"))) {
  512. return;
  513. }
  514. try {
  515. Connection.Response response = JsoupUtil.requestBody("https://www.odaily.news/v1/openapi/feeds", JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  516. JSONObject result = JSONObject.parseObject(response.body());
  517. JSONArray newsList = result.getJSONObject("data").getJSONArray("arr_news");
  518. LocalDateTime endTime = LocalDateTime.now();
  519. for (int i = 0; i < newsList.size(); i++) {
  520. if (i == 5) {
  521. break;
  522. }
  523. JSONObject news = newsList.getJSONObject(i);
  524. String id = "Odaily" + news.getString("id");
  525. String publishedAt = news.getString("published_at");
  526. LocalDateTime publishedAtTime = DateUtils.stringToLocalDateTime(publishedAt);
  527. if (Duration.between(publishedAtTime, endTime).getSeconds() < 20 && !orderMap.containsKey(id)) {
  528. orderMap.put(id, "1");
  529. String type = news.getString("type");
  530. String title = news.getString("title");
  531. String link = news.getString("link");
  532. String content = "";
  533. JSONObject params = new JSONObject();
  534. params.put("title", "Odaily监控报警");
  535. params.put("btnTxt", "新闻详情");
  536. params.put("logUrl", link);
  537. params.put("user", "@all");
  538. params.put("agentId", 1000004);
  539. if ("newsflashes".equals(type)) {
  540. String newsUrl = news.getString("news_url");
  541. content = "<div class=\"highlight\">标题:" + title + "</div>" +
  542. "<div>类型:" + "新闻快讯" + "</div>" +
  543. "<div>发布时间:" + publishedAt + "</div>" +
  544. "<div class=\"gray\">描述:" + news.getString("description").replace("\n", "&nbsp;&nbsp;&nbsp;&nbsp;") + "</div>";
  545. } else if ("posts".equals(type)) {
  546. content = "<div class=\"highlight\">标题:" + title + "</div>" +
  547. "<div>类型:" + "帖子" + "</div>" +
  548. "<div>发布时间:" + publishedAt + "</div>" +
  549. "<div class=\"gray\">描述:" + news.getString("summary").replace("\n", "&nbsp;&nbsp;&nbsp;&nbsp;") + "</div>";
  550. }
  551. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, wxCpService4News);
  552. }
  553. }
  554. } catch (Exception e) {
  555. log.error("news-odaily top error", e);
  556. }
  557. }, 0, 10, TimeUnit.SECONDS);
  558. // 律动日报新闻快讯监控报警
  559. scheduler.scheduleWithFixedDelay(() -> {
  560. if (!"1".equals(getMonitorJobStatus("news-theblockbeats"))) {
  561. return;
  562. }
  563. try {
  564. Map<String, String> paramMap = new HashMap<>();
  565. paramMap.put("size", "10");
  566. paramMap.put("page", "1");
  567. Connection.Response response = JsoupUtil.requestBody("https://api.theblockbeats.news/v1/open-api/open-flash", JsoupUtil.HTTP_GET, InitRunner.proxy, null, paramMap);
  568. JSONObject result = JSONObject.parseObject(response.body());
  569. JSONArray newsList = result.getJSONObject("data").getJSONArray("data");
  570. LocalDateTime endTime = LocalDateTime.now();
  571. for (int i = 0; i < newsList.size(); i++) {
  572. if (i == 5) {
  573. break;
  574. }
  575. JSONObject news = newsList.getJSONObject(i);
  576. String id = "BlockBeats" + news.getString("id");
  577. String createTime = news.getString("create_time");
  578. LocalDateTime createTimeTime = DateUtils.longToLocalDateTime_(Long.valueOf(createTime));
  579. createTime = DateUtils.localDateTimeToString(createTimeTime);
  580. if (Duration.between(createTimeTime, endTime).getSeconds() < 20 && !orderMap.containsKey(id)) {
  581. orderMap.put(id, "1");
  582. String title = news.getString("title");
  583. String content = news.getString("content");
  584. String pic = news.getString("pic");
  585. String link = news.getString("link");
  586. String url = news.getString("url");
  587. JSONObject params = new JSONObject();
  588. params.put("title", "BlockBeats监控报警");
  589. params.put("btnTxt", "新闻详情");
  590. params.put("logUrl", link);
  591. params.put("user", "@all");
  592. params.put("agentId", 1000004);
  593. if (StringUtils.isEmpty(pic)) {
  594. // 文本卡片
  595. String contentStr = "<div class=\"highlight\">标题:" + title + "</div>" +
  596. "<div>类型:" + "新闻快讯" + "</div>" +
  597. "<div>发布时间:" + createTime + "</div>" +
  598. "<div class=\"gray\">描述:" + content.replace("\n", "&nbsp;&nbsp;&nbsp;&nbsp;") + "</div>";
  599. if (title.contains("Upbit") || title.contains("upbit")) {
  600. JSONObject params4Upbit = new JSONObject();
  601. params4Upbit.put("title", "BlockBeats监控报警");
  602. params4Upbit.put("logUrl", link);
  603. params4Upbit.put("btnTxt", "新闻详情");
  604. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(contentStr, params4Upbit, null);
  605. }
  606. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(contentStr, params, wxCpService4News);
  607. } else {
  608. // 图文
  609. NewArticle article1 = new NewArticle();
  610. article1.setUrl(link);
  611. article1.setPicUrl(pic);
  612. article1.setDescription(content);
  613. article1.setTitle(title);
  614. if (title.contains("Upbit") || title.contains("upbit")) {
  615. JSONObject params4Upbit = new JSONObject();
  616. params4Upbit.put("title", "BlockBeats监控报警");
  617. params4Upbit.put("logUrl", link);
  618. params4Upbit.put("btnTxt", "新闻详情");
  619. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4NEWS(params4Upbit, null, article1);
  620. }
  621. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4NEWS(params, wxCpService4News, article1);
  622. }
  623. }
  624. }
  625. } catch (Exception e) {
  626. log.error("news-theblockbeats top error", e);
  627. }
  628. }, 0, 10, TimeUnit.SECONDS);
  629. // coingecko
  630. scheduler.scheduleWithFixedDelay(() -> {
  631. if (!"1".equals(getMonitorJobStatus("watchlist-coingecko-cmc"))) {
  632. log.warn("watchlist-coingecko-cmc scheduler return");
  633. return;
  634. }
  635. log.warn("watchlist-coingecko-cmc scheduler start");
  636. int MAX_NUMBER = 100;
  637. Map<String, Object> params = new HashMap<>();
  638. params.put("sortField", Collections.singletonList("create_time"));
  639. params.put("sort", "desc");
  640. List<CoinWatchlist> watchlistListCKO = coinMapper.findWatchlistList(params);
  641. Stream.iterate(0, n -> n + 1).limit((watchlistListCKO.size() + MAX_NUMBER - 1) / MAX_NUMBER)
  642. .forEach(i -> {
  643. try {
  644. Thread.sleep(5000L);
  645. } catch (InterruptedException e) {
  646. }
  647. Map<String, CoinWatchlist> coinWatchlistMap4CoingeckoId = watchlistListCKO.stream().skip((long) i * MAX_NUMBER).limit(MAX_NUMBER).collect(Collectors.toMap(CoinWatchlist::getCoingeckoId, coinWatchlist -> coinWatchlist));
  648. parseWatchlistMap4Coingecko(coinWatchlistMap4CoingeckoId);
  649. });
  650. List<CoinWatchlist> watchlistListCMC = coinMapper.findWatchlistList(params);
  651. Stream.iterate(0, n -> n + 1).limit((watchlistListCMC.size() + MAX_NUMBER - 1) / MAX_NUMBER)
  652. .forEach(i -> {
  653. try {
  654. Thread.sleep(5000L);
  655. } catch (InterruptedException e) {
  656. }
  657. Map<Long, CoinWatchlist> coinWatchlistMap4CmcId = watchlistListCMC.stream().skip((long) i * MAX_NUMBER).limit(MAX_NUMBER).collect(Collectors.toMap(CoinWatchlist::getCmcId, coinWatchlist -> coinWatchlist));
  658. parseWatchlistMap4CmC(coinWatchlistMap4CmcId);
  659. });
  660. // CoinCurrencyHolding同步
  661. params.put("sortField", Collections.singletonList("cch.buy_time"));
  662. params.put("sort", "desc");
  663. params.put("status", "1");
  664. List<CoinCurrencyHolding> currentHoldingList = coinMapper.findCurrentHoldingList(params);
  665. parseCurrentHoldingMap4Coingecko(currentHoldingList);
  666. log.warn("watchlist-coingecko-cmc scheduler end");
  667. }, 0, 1, TimeUnit.HOURS);
  668. // Upbit交易所监控报警
  669. scheduler.scheduleWithFixedDelay(() -> {
  670. if (!"1".equals(getMonitorJobStatus("upbit-digitalasset-notices"))) {
  671. return;
  672. }
  673. try {
  674. Map<String, String> paramMap = new HashMap<>();
  675. paramMap.put("page", "1");
  676. paramMap.put("per_page", "20");
  677. paramMap.put("thread_name", "general");
  678. Connection.Response response = JsoupUtil.requestBody("https://api-manager.upbit.com/api/v1/notices", JsoupUtil.HTTP_GET, InitRunner.proxy, null, paramMap);
  679. JSONObject result = JSONObject.parseObject(response.body());
  680. JSONArray noticeList = result.getJSONObject("data").getJSONArray("list");
  681. for (int i = 0; i < noticeList.size(); i++) {
  682. if (i == 5) {
  683. break;
  684. }
  685. JSONObject notice = noticeList.getJSONObject(i);
  686. String idOri = notice.getString("id");
  687. String id = "Upbit" + idOri;
  688. String title = notice.getString("title");
  689. if (title.contains("New digital asset on KRW Market") && !orderMap.containsKey(id)) {
  690. orderMap.put(id, "1");
  691. String createTime = notice.getString("created_at");
  692. String updateTime = notice.getString("updated_at");
  693. String viewCount = notice.getString("view_count");
  694. JSONObject params = new JSONObject();
  695. params.put("title", "Upbit监控报警");
  696. params.put("btnTxt", "通知详情");
  697. params.put("logUrl", "https://sg-api-manager.upbit.com/api/v1/notices/" + idOri);
  698. // 文本卡片
  699. String contentStr = "<div class=\"highlight\">标题:" + title + "</div>" +
  700. "<div>发布时间:" + createTime + "</div>" +
  701. "<div>更新时间:" + updateTime + "</div>" +
  702. "<div class=\"gray\">查看次数:" + viewCount + "</div>";
  703. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(contentStr, params, null);
  704. }
  705. }
  706. } catch (Exception e) {
  707. log.error("upbit-digitalasset-notices top error", e);
  708. try {
  709. Thread.sleep(600000L);
  710. } catch (InterruptedException ex) {
  711. }
  712. }
  713. }, 0, 10, TimeUnit.SECONDS);
  714. return null;
  715. }
  716. private void parseCurrentHoldingMap4Coingecko(List<CoinCurrencyHolding> currentHoldingList) {
  717. String coingeckoCoinsMarketsUrl = InitRunner.dicCodeMap.get("coingecko_coins_markets_url").getCodeValue();
  718. Map<String, String> headerMap = new HashMap<>();
  719. headerMap.put("Accept", "application/json");
  720. headerMap.put("Accept-Encoding", "deflate,gzip");
  721. Map<String, String> paramMap = new LinkedHashMap<>();
  722. paramMap.put("ids", currentHoldingList.stream().map(CoinCurrencyHolding::getCoingeckoId).distinct().collect(Collectors.joining(",")));
  723. paramMap.put("vs_currency", "usd");
  724. AtomicInteger i = new AtomicInteger();
  725. Connection.Response response = null;
  726. try {
  727. response = JsoupUtil.requestBody(coingeckoCoinsMarketsUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, headerMap, paramMap);
  728. JSONArray result = JSONArray.parseArray(response.body());
  729. Map<String, List<CoinCurrencyHolding>> currencyHoldingMap = currentHoldingList.stream().collect(Collectors.groupingBy(CoinCurrencyHolding::getCoingeckoId));
  730. for (int j = 0; j < result.size(); j++) {
  731. JSONObject marketData = result.getJSONObject(j);
  732. String id = marketData.getString("id");
  733. if (currencyHoldingMap.containsKey(id)) {
  734. List<CoinCurrencyHolding> currencyHoldingSubList = currencyHoldingMap.get(id);
  735. for (CoinCurrencyHolding currencyHolding : currencyHoldingSubList) {
  736. // 市场价格
  737. if (marketData.containsKey("current_price") && null != marketData.get("current_price")) {
  738. String markPrice = marketData.getBigDecimal("current_price").toPlainString();
  739. currencyHolding.setCurrentPrice(markPrice);
  740. }
  741. // 入场总额
  742. if (StringUtils.isNotEmpty(currencyHolding.getBuyPrice()) && StringUtils.isNotEmpty(currencyHolding.getBuyQuantity())) {
  743. currencyHolding.setBuyAmount(new BigDecimal(currencyHolding.getBuyPrice()).multiply(new BigDecimal(currencyHolding.getBuyQuantity())).setScale(2, RoundingMode.HALF_UP));
  744. }
  745. // 当前总额
  746. if (StringUtils.isNotEmpty(currencyHolding.getCurrentPrice()) && StringUtils.isNotEmpty(currencyHolding.getCurrentQuantity())) {
  747. currencyHolding.setCurrentAmount(new BigDecimal(currencyHolding.getCurrentPrice()).multiply(new BigDecimal(currencyHolding.getCurrentQuantity())).setScale(2, RoundingMode.HALF_UP));
  748. }
  749. // 涨跌幅比例
  750. if (StringUtils.isNotEmpty(currencyHolding.getBuyPrice()) && StringUtils.isNotEmpty(currencyHolding.getCurrentPrice())) {
  751. BigDecimal oriChangePercentage = currencyHolding.getChangePercentage();
  752. BigDecimal changePercentage = new BigDecimal(currencyHolding.getCurrentPrice()).divide(new BigDecimal(currencyHolding.getBuyPrice()), 2, RoundingMode.HALF_UP);
  753. currencyHolding.setChangePercentage(changePercentage);
  754. // 监控报警
  755. if (oriChangePercentage != null && !"USDT".equals(currencyHolding.getSymbol())) {
  756. BigDecimal roundedDown = changePercentage.setScale(0, RoundingMode.FLOOR);
  757. if (changePercentage.compareTo(roundedDown) > 0 && oriChangePercentage.compareTo(roundedDown) < 0) {
  758. String content = "<div class=\"highlight\">币种名称:" + currencyHolding.getSymbol() + "</div>" +
  759. "<div>入场价格:" + currencyHolding.getBuyPrice() + "</div>" +
  760. "<div>当前价格:" + currencyHolding.getCurrentPrice() + "</div>" +
  761. "<div>入场总额:" + currencyHolding.getBuyAmount() + "</div>" +
  762. "<div>当前总额:" + currencyHolding.getCurrentAmount() + "</div>" +
  763. "<div>所属平台:" + currencyHolding.getExchangeCategory() + "</div>" +
  764. "<div>买入时间:" + currencyHolding.getBuyTime() + "</div>" +
  765. "<div class=\"gray\">涨跌幅:" + currencyHolding.getChangePercentage() + "</div>";
  766. JSONObject params = new JSONObject();
  767. params.put("title", "持仓币种盈利报警");
  768. params.put("logUrl", "https://jav.lvzhiqiang.top/coin.html");
  769. params.put("btnTxt", "详情");
  770. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, null);
  771. }
  772. }
  773. }
  774. coinMapper.updateCurrentHolding(currencyHolding);
  775. }
  776. }
  777. }
  778. } catch (Exception e) {
  779. log.error("parseCurrentHoldingMap4Coingecko error,response={},size={},i={}", response != null ? response.body() : "--", currentHoldingList.size(), i.get(), e);
  780. }
  781. }
  782. @Override
  783. public Object getCurrentHoldingTotalAmout(JSONObject params) {
  784. BigDecimal totalAmout = coinMapper.getCurrentHoldingTotalAmout(params.toJavaObject(Map.class));
  785. return totalAmout;
  786. }
  787. @Override
  788. public Object login(String username, String password) {
  789. Integer exist = coinMapper.existUserByUsernameAndPassword(username, password);
  790. if (exist != null) {
  791. return R.ok().data("success");
  792. } else {
  793. return R.error().message("用户名或者密码错误!");
  794. }
  795. }
  796. @Override
  797. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  798. public R insertOrUpdateCurrentHolding(JSONObject params) {
  799. String operationType = params.getString("operationType");
  800. if ("buy".equals(operationType)) {
  801. String symbol = params.getString("symbol");
  802. String buyPrice = params.getString("buyPrice");
  803. String buyQuantity = params.getString("buyQuantity");
  804. String currentQuantity = params.getString("currentQuantity");
  805. String exchangeCategoryId = params.getString("exchangeCategory");
  806. String coingeckoId = params.getString("coingeckoId");
  807. String buyTime = params.getString("buyTime");
  808. String remark = params.getString("remark");
  809. if (StringUtils.isEmpty(symbol) || StringUtils.isEmpty(buyPrice) || StringUtils.isEmpty(buyQuantity) || StringUtils.isEmpty(currentQuantity) || StringUtils.isEmpty(exchangeCategoryId)) {
  810. throw new ParameterException("必填参数不能为空!");
  811. }
  812. symbol = symbol.trim();
  813. Integer existCmcMapBySymbol = coinMapper.existCmcMapBySymbol(symbol);
  814. if (existCmcMapBySymbol == null) {
  815. throw new BusinessException(ResultCodeEnum.UNKNOWN_ERROR.getCode(), "symbol不存在!");
  816. }
  817. if (StringUtils.isEmpty(coingeckoId)) {
  818. coingeckoId = coinMapper.findCoingeckoIdBySymbol(symbol);
  819. if (StringUtils.isEmpty(coingeckoId)) {
  820. throw new BusinessException(ResultCodeEnum.UNKNOWN_ERROR.getCode(), "coingeckoId不存在!");
  821. }
  822. }
  823. CoinCurrencyHolding coinCurrencyHolding = new CoinCurrencyHolding();
  824. coinCurrencyHolding.setSymbol(symbol);
  825. coinCurrencyHolding.setBuyPrice(buyPrice);
  826. coinCurrencyHolding.setBuyQuantity(buyQuantity);
  827. coinCurrencyHolding.setCurrentQuantity(currentQuantity);
  828. coinCurrencyHolding.setExchangeCategoryId(Integer.valueOf(exchangeCategoryId));
  829. coinCurrencyHolding.setCoingeckoId(coingeckoId);
  830. coinCurrencyHolding.setBuyTime(StringUtils.isNotEmpty(buyTime) ? LocalDateTime.parse(buyTime, DateUtils.dateTimeFormatter) : LocalDateTime.now());
  831. coinCurrencyHolding.setStatus(1);
  832. coinCurrencyHolding.setRemark(remark);
  833. coinMapper.buyCurrentHolding(coinCurrencyHolding);
  834. List<CoinCurrencyHolding> currentHoldingList = new ArrayList<>();
  835. currentHoldingList.add(coinCurrencyHolding);
  836. parseCurrentHoldingMap4Coingecko(currentHoldingList);
  837. } else if ("sell".equals(operationType)) {
  838. String id = params.getString("id");
  839. String sellPrice = params.getString("sellPrice");
  840. String sellTime = params.getString("sellTime");
  841. String sellAmount = params.getString("sellAmount");
  842. String remark = params.getString("remark");
  843. if (StringUtils.isEmpty(id) || StringUtils.isEmpty(sellPrice)) {
  844. throw new ParameterException("必填参数不能为空!");
  845. }
  846. id = id.trim();
  847. CoinCurrencyHolding coinCurrencyHolding = coinMapper.findCurrencyHoldingById(id);
  848. if (coinCurrencyHolding == null) {
  849. throw new BusinessException(ResultCodeEnum.UNKNOWN_ERROR.getCode(), "id不存在!");
  850. }
  851. coinCurrencyHolding.setSellTime(StringUtils.isNotEmpty(sellTime) ? LocalDateTime.parse(sellTime, DateUtils.dateTimeFormatter) : LocalDateTime.now());
  852. coinCurrencyHolding.setSellPrice(sellPrice);
  853. coinCurrencyHolding.setStatus(2);
  854. if (StringUtils.isEmpty(sellAmount)) {
  855. coinCurrencyHolding.setSellAmount(new BigDecimal(sellPrice).multiply(new BigDecimal(coinCurrencyHolding.getCurrentQuantity())).setScale(2,RoundingMode.HALF_UP));
  856. }else{
  857. coinCurrencyHolding.setSellAmount(new BigDecimal(sellAmount).setScale(2,RoundingMode.HALF_UP));
  858. }
  859. if (StringUtils.isNotEmpty(remark)) {
  860. coinCurrencyHolding.setRemark(remark + (StringUtils.isNotEmpty(coinCurrencyHolding.getRemark()) ? System.lineSeparator() + coinCurrencyHolding.getRemark() : ""));
  861. }
  862. // 涨跌幅比例
  863. if (StringUtils.isNotEmpty(coinCurrencyHolding.getBuyPrice()) && StringUtils.isNotEmpty(sellPrice)) {
  864. BigDecimal changePercentage = new BigDecimal(sellPrice).divide(new BigDecimal(coinCurrencyHolding.getBuyPrice()), 2, RoundingMode.HALF_UP);
  865. coinCurrencyHolding.setChangePercentage(changePercentage);
  866. }
  867. coinMapper.updateCurrentHolding(coinCurrencyHolding);
  868. // 更新余额
  869. CoinCurrencyHolding usdtCurrencyHolding = coinMapper.findCurrentHoldingBySymbolAndExchangeCategoryId("USDT", coinCurrencyHolding.getExchangeCategoryId());
  870. if (usdtCurrencyHolding == null) {
  871. throw new BusinessException(ResultCodeEnum.UNKNOWN_ERROR.getCode(), "USDT不存在!");
  872. }
  873. usdtCurrencyHolding.setCurrentAmount(usdtCurrencyHolding.getCurrentAmount().add(coinCurrencyHolding.getSellAmount()));
  874. coinMapper.updateCurrentHolding(usdtCurrencyHolding);
  875. } else if ("transfer".equals(operationType)) {
  876. String remitter = params.getString("remitter");
  877. String payee = params.getString("payee");
  878. String amount = params.getString("amount");
  879. if (StringUtils.isEmpty(remitter) || StringUtils.isEmpty(payee) || StringUtils.isEmpty(amount)) {
  880. throw new ParameterException("必填参数不能为空!");
  881. }
  882. // 更新余额
  883. CoinCurrencyHolding usdtCurrencyHolding4Remitter = coinMapper.findCurrentHoldingBySymbolAndExchangeCategoryId("USDT", Integer.valueOf(remitter));
  884. if (usdtCurrencyHolding4Remitter == null) {
  885. throw new BusinessException(ResultCodeEnum.UNKNOWN_ERROR.getCode(), "汇款人USDT不存在!");
  886. }
  887. CoinCurrencyHolding usdtCurrencyHolding4Payee = coinMapper.findCurrentHoldingBySymbolAndExchangeCategoryId("USDT", Integer.valueOf(payee));
  888. if (usdtCurrencyHolding4Payee == null) {
  889. throw new BusinessException(ResultCodeEnum.UNKNOWN_ERROR.getCode(), "收款人USDT不存在!");
  890. }
  891. usdtCurrencyHolding4Remitter.setCurrentAmount(usdtCurrencyHolding4Remitter.getCurrentAmount().subtract(new BigDecimal(amount)));
  892. usdtCurrencyHolding4Payee.setCurrentAmount(usdtCurrencyHolding4Payee.getCurrentAmount().add(new BigDecimal(amount)));
  893. coinMapper.updateCurrentHolding(usdtCurrencyHolding4Remitter);
  894. coinMapper.updateCurrentHolding(usdtCurrencyHolding4Payee);
  895. } else if ("modifyCurrentAmount".equals(operationType)) {
  896. String id = params.getString("id");
  897. String currentAmount = params.getString("currentAmount");
  898. if (StringUtils.isEmpty(id) || StringUtils.isEmpty(currentAmount)) {
  899. throw new ParameterException("必填参数不能为空!");
  900. }
  901. id = id.trim();
  902. CoinCurrencyHolding coinCurrencyHolding = coinMapper.findCurrencyHoldingById(id);
  903. if (coinCurrencyHolding == null) {
  904. throw new BusinessException(ResultCodeEnum.UNKNOWN_ERROR.getCode(), "id不存在!");
  905. }
  906. coinCurrencyHolding.setCurrentAmount(new BigDecimal(currentAmount).setScale(2, RoundingMode.HALF_UP));
  907. coinMapper.updateCurrentHolding(coinCurrencyHolding);
  908. } else {
  909. throw new ParameterException("暂不支持的操作!");
  910. }
  911. return R.ok().data("success");
  912. }
  913. @Override
  914. public void monitorBitgetMixOrder() {
  915. if (!"1".equals(getMonitorJobStatus("bitget-mix-order"))) {
  916. return;
  917. }
  918. LocalDateTime endTime = LocalDateTime.now();
  919. // 全部历史委托列表
  920. Map<String, String> paramMap = new LinkedHashMap<>();
  921. paramMap.put("productType", "umcbl");
  922. paramMap.put("startTime", String.valueOf(DateUtils.localDateTimeToMilliseconds(endTime.minusMinutes(1))));
  923. //paramMap.put("startTime", String.valueOf(DateUtils.localDateTimeToMilliseconds(endTime.minusDays(3))));
  924. paramMap.put("endTime", String.valueOf(DateUtils.localDateTimeToMilliseconds(endTime)));
  925. paramMap.put("pageSize", "100");
  926. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  927. try {
  928. JSONObject response = requestApi4Common("/api/mix/v1/order/historyProductType", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  929. JSONArray orderList = response.getJSONObject("data").getJSONArray("orderList");
  930. if (null == orderList) {
  931. return;
  932. }
  933. log.warn("bitget-mix-order exec,orderList={}", orderList.size());
  934. for (int i = 0; i < orderList.size(); i++) {
  935. JSONObject order = orderList.getJSONObject(i);
  936. LocalDateTime cTime = DateUtils.longToLocalDateTime(order.getLong("cTime"));
  937. String orderId = order.getString("orderId");
  938. String symbol = order.getString("symbol");
  939. if (Duration.between(cTime, endTime).getSeconds() < 200 && !orderMap.containsKey(orderId)) {
  940. orderMap.put(orderId, "1");
  941. log.warn("bitget-mix-order exec,orderId={}", orderId);
  942. String content = "<div class=\"highlight\">交易对:" + order.getString("symbol") + "</div>" +
  943. "<div>交易方向:" + InitRunner.publicParamsMap.get("tradeSide").getString(order.getString("tradeSide")) + "</div>" +
  944. "<div>杠杆倍数:" + order.getString("leverage") + "</div>" +
  945. "<div>成交均价:" + order.getString("priceAvg") + "</div>" +
  946. "<div>委托价格:" + order.getString("price") + "</div>" +
  947. "<div>订单状态:" + InitRunner.publicParamsMap.get("state").getString(order.getString("state")) + "</div>" +
  948. "<div>订单类型:" + InitRunner.publicParamsMap.get("orderType").getString(order.getString("orderType")) + "</div>" +
  949. "<div class=\"gray\">订单时间:" + DateUtils.longToString(order.getLong("cTime")) + "</div>";
  950. JSONObject params = new JSONObject();
  951. params.put("title", (order.getString("side").contains("open") ? "BITGET合约开单" : "BITGET合约平单") + "报警");
  952. params.put("logUrl", "https://jav.lvzhiqiang.top/coin/orderDetail2/" + orderId + "/" + symbol);
  953. params.put("btnTxt", "订单详情");
  954. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, null);
  955. }
  956. }
  957. } catch (Exception e) {
  958. log.error("bitget-mix-order error", e);
  959. }
  960. }
  961. @Override
  962. public void monitorBitgetMixReturnrate() {
  963. if (!"1".equals(getMonitorJobStatus("bitget-mix-returnrate"))) {
  964. return;
  965. }
  966. // BITGET全部合约仓位信息V2
  967. Map<String, String> paramMap = new HashMap<>();
  968. paramMap.put("productType", "umcbl");
  969. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  970. try {
  971. JSONObject response = requestApi4Common("/api/mix/v1/position/allPosition-v2", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  972. JSONArray mixList = response.getJSONArray("data");
  973. for (int i = 0; i < mixList.size(); i++) {
  974. JSONObject mixData = mixList.getJSONObject(i);
  975. String symbol = mixData.getString("symbol");
  976. String margin = mixData.getString("margin");
  977. String averageOpenPrice = mixData.getString("averageOpenPrice");
  978. String key = symbol + margin + averageOpenPrice;
  979. // 回报率=未实现盈亏/保证金
  980. // 持仓方向 long:多头 short:空头
  981. String holdSide = mixData.getString("holdSide");
  982. BigDecimal returnRate = new BigDecimal(mixData.getString("unrealizedPL")).divide(new BigDecimal(margin), 4, RoundingMode.HALF_UP);
  983. for (int j = 1; j <= 10; j++) {
  984. BigDecimal grid = BigDecimal.valueOf(0.5).multiply(BigDecimal.valueOf(j));
  985. BigDecimal minusGrid = BigDecimal.valueOf(-0.5).multiply(BigDecimal.valueOf(j));
  986. if (returnRate.compareTo(grid) < 0) {
  987. if (mixMap.containsKey(key)) {
  988. mixMap.get(key).put("returnRate", returnRate);
  989. } else {
  990. JSONObject jsonObject = new JSONObject();
  991. jsonObject.put("returnRate", returnRate);
  992. mixMap.put(key, jsonObject);
  993. }
  994. break;
  995. }
  996. if (returnRate.compareTo(grid) > 0) {
  997. if (mixMap.containsKey(key)) {
  998. mixMap.get(key).put("returnRate", returnRate);
  999. if (mixMap.get(key).containsKey(grid.toPlainString())) {
  1000. continue;
  1001. } else {
  1002. mixMap.get(key).put(grid.toPlainString(), true);
  1003. String requestUrl = mainUrl + "/api/mix/v1/market/ticker?symbol=" + symbol;
  1004. String last = "--";
  1005. try {
  1006. Connection.Response responseTicker = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  1007. last = JSONObject.parseObject(responseTicker.body()).getJSONObject("data").getString("last");
  1008. } catch (Exception e) {
  1009. }
  1010. String content = "币对名称:" + symbol + "\n" +
  1011. "持仓方向:" + InitRunner.publicParamsMap.get("holdSide").getString(mixData.getString("holdSide")) + "\n" +
  1012. "杠杆倍数:" + mixData.getString("leverage") + "\n" +
  1013. "开仓均价:" + mixData.getString("averageOpenPrice") + "\n" +
  1014. "当前价格:" + last + "\n" +
  1015. "回报率:" + returnRate.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString() + ",超过" + grid.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString();
  1016. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4CHAT_BOT(content, null);
  1017. }
  1018. } else {
  1019. JSONObject jsonObject = new JSONObject();
  1020. jsonObject.put("returnRate", returnRate);
  1021. jsonObject.put(grid.toPlainString(), true);
  1022. mixMap.put(key, jsonObject);
  1023. String requestUrl = mainUrl + "/api/mix/v1/market/ticker?symbol=" + symbol;
  1024. String last = "--";
  1025. try {
  1026. Connection.Response responseTicker = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  1027. last = JSONObject.parseObject(responseTicker.body()).getJSONObject("data").getString("last");
  1028. } catch (Exception e) {
  1029. }
  1030. String content = "币对名称:" + symbol + "\n" +
  1031. "持仓方向:" + InitRunner.publicParamsMap.get("holdSide").getString(mixData.getString("holdSide")) + "\n" +
  1032. "杠杆倍数:" + mixData.getString("leverage") + "\n" +
  1033. "开仓均价:" + mixData.getString("averageOpenPrice") + "\n" +
  1034. "当前价格:" + last + "\n" +
  1035. "回报率:" + returnRate.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString() + ",超过" + grid.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString();
  1036. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4CHAT_BOT(content, null);
  1037. //break;
  1038. }
  1039. }
  1040. }
  1041. }
  1042. } catch (Exception e) {
  1043. log.error("bitget-mix-returnrate error", e);
  1044. }
  1045. }
  1046. private void initCexSpotFlag(Map<String, CoinWatchlistOther> coinWatchlistOtherMap4Symbol) {
  1047. // spot
  1048. String coingeckoExchangeTickersUrl = InitRunner.dicCodeMap.get("coingecko_exchange_tickers_url").getCodeValue();
  1049. String[] coingeckoExchangeIdArr = InitRunner.dicCodeMap.get("coingecko_exchange_ids").getCodeValue().split(",");
  1050. Map<String, String> headerMap = new HashMap<>();
  1051. headerMap.put("Accept", "application/json");
  1052. headerMap.put("Accept-Encoding", "deflate,gzip");
  1053. Map<String, String> paramMap = new LinkedHashMap<>();
  1054. paramMap.put("coin_ids", StringUtils.join(coinWatchlistOtherMap4Symbol.keySet(), ","));
  1055. int iii = 0;
  1056. JSONArray result = null;
  1057. for (String coingeckoExchangeId : coingeckoExchangeIdArr) {
  1058. iii++;
  1059. try {
  1060. Thread.sleep(10000L);
  1061. Connection.Response response = JsoupUtil.requestBody(String.format(coingeckoExchangeTickersUrl, coingeckoExchangeId), JsoupUtil.HTTP_GET, Proxy.NO_PROXY, headerMap, paramMap);
  1062. result = JSONObject.parseObject(response.body()).getJSONArray("tickers");
  1063. if (result == null) {
  1064. Thread.sleep(60000L);
  1065. response = JsoupUtil.requestBody(String.format(coingeckoExchangeTickersUrl, coingeckoExchangeId), JsoupUtil.HTTP_GET, Proxy.NO_PROXY, headerMap, paramMap);
  1066. result = JSONObject.parseObject(response.body()).getJSONArray("tickers");
  1067. }
  1068. Map<String, JSONObject> resultMap = result.stream().collect(Collectors.toMap(i -> {
  1069. JSONObject a = (JSONObject) i;
  1070. String k = a.getString("base").concat(a.getString("target"));
  1071. return k;
  1072. }, i -> (JSONObject) i, (existing, replacement) -> existing));
  1073. for (CoinWatchlistOther coinWatchlistOther : coinWatchlistOtherMap4Symbol.values()) {
  1074. String baseTarget = coinWatchlistOther.getSymbol().concat("USDT");
  1075. if (iii == 1) {
  1076. coinWatchlistOther.setCexSpot("");
  1077. }
  1078. if (resultMap.containsKey(baseTarget)) {
  1079. coinWatchlistOther.setCexSpot(coinWatchlistOther.getCexSpot() + "1");
  1080. } else {
  1081. coinWatchlistOther.setCexSpot(coinWatchlistOther.getCexSpot() + "0");
  1082. }
  1083. }
  1084. } catch (Exception e) {
  1085. log.error("initCexSpotFlag error,url={},paramMap={},result={}", String.format(coingeckoExchangeTickersUrl, coingeckoExchangeId), paramMap, result, e);
  1086. throw new RuntimeException("initCexSpotFlag error");
  1087. }
  1088. }
  1089. }
  1090. /**
  1091. * 解析交易所现货+合约标志
  1092. */
  1093. @Override
  1094. @Async
  1095. public void syncCexFlag(String symbol) {
  1096. List<CoinWatchlistOther> watchlistOtherList;
  1097. if (StringUtils.isNotEmpty(symbol)) {
  1098. CoinWatchlistOther watchlistOther = coinMapper.findWatchlistOtherBySymbol2(symbol);
  1099. if (watchlistOther == null) {
  1100. return;
  1101. }
  1102. watchlistOtherList = new ArrayList<>();
  1103. watchlistOtherList.add(watchlistOther);
  1104. } else {
  1105. watchlistOtherList = coinMapper.findAllCoinWatchlistOther();
  1106. }
  1107. // Perpetual
  1108. String coingeckoExchangeFuturesTickersUrl = InitRunner.dicCodeMap.get("coingecko_exchange_futures_tickers_url").getCodeValue();
  1109. String[] coingeckoExchangeFutruesIdArr = InitRunner.dicCodeMap.get("coingecko_exchange_futures_ids").getCodeValue().split(",");
  1110. Map<String, String> headerMap = new HashMap<>();
  1111. headerMap.put("Accept", "application/json");
  1112. headerMap.put("Accept-Encoding", "deflate,gzip");
  1113. Map<String, String> paramMap = new LinkedHashMap<>();
  1114. paramMap.put("include_tickers", "unexpired");
  1115. int jjj = 0;
  1116. JSONArray result = null;
  1117. for (String coingeckoExchangeFutruesId : coingeckoExchangeFutruesIdArr) {
  1118. jjj++;
  1119. try {
  1120. Thread.sleep(10000L);
  1121. Connection.Response response = JsoupUtil.requestBody(String.format(coingeckoExchangeFuturesTickersUrl, coingeckoExchangeFutruesId), JsoupUtil.HTTP_GET, Proxy.NO_PROXY, headerMap, paramMap);
  1122. result = JSONObject.parseObject(response.body()).getJSONArray("tickers");
  1123. if (result == null) {
  1124. Thread.sleep(10000L);
  1125. response = JsoupUtil.requestBody(String.format(coingeckoExchangeFuturesTickersUrl, coingeckoExchangeFutruesId), JsoupUtil.HTTP_GET, Proxy.NO_PROXY, headerMap, paramMap);
  1126. result = JSONObject.parseObject(response.body()).getJSONArray("tickers");
  1127. }
  1128. Map<String, JSONObject> resultMap = result.stream().collect(Collectors.toMap(i -> {
  1129. JSONObject a = (JSONObject) i;
  1130. String k = a.getString("base").concat(a.getString("target"));
  1131. return k;
  1132. }, i -> (JSONObject) i, (existing, replacement) -> existing));
  1133. for (CoinWatchlistOther coinWatchlistOther : watchlistOtherList) {
  1134. String baseTarget = coinWatchlistOther.getSymbol().concat("USDT");
  1135. if (jjj == 1) {
  1136. coinWatchlistOther.setCexPerpetual("");
  1137. }
  1138. if (resultMap.containsKey(baseTarget) || resultMap.containsKey("1000".concat(baseTarget))) {
  1139. coinWatchlistOther.setCexPerpetual(coinWatchlistOther.getCexPerpetual() + "1");
  1140. } else {
  1141. coinWatchlistOther.setCexPerpetual(coinWatchlistOther.getCexPerpetual() + "0");
  1142. }
  1143. }
  1144. } catch (Exception e) {
  1145. log.error("initCexFlag error,url={},paramMap={},result={}", String.format(coingeckoExchangeFuturesTickersUrl, coingeckoExchangeFutruesId), paramMap, result, e);
  1146. throw new RuntimeException("initCexFlag error");
  1147. }
  1148. }
  1149. int MAX_NUMBER2 = 10;
  1150. Stream.iterate(0, n -> n + 1).limit((watchlistOtherList.size() + MAX_NUMBER2 - 1) / MAX_NUMBER2)
  1151. .forEach(i -> {
  1152. try {
  1153. Thread.sleep(5000L);
  1154. } catch (InterruptedException e) {
  1155. throw new RuntimeException(e);
  1156. }
  1157. Map<String, CoinWatchlistOther> coinWatchlistOtherMap4Symbol = watchlistOtherList.stream().skip((long) i * MAX_NUMBER2).limit(MAX_NUMBER2).collect(Collectors.toMap(CoinWatchlistOther::getCoingeckoId, coinWatchlistOther -> coinWatchlistOther));
  1158. initCexSpotFlag(coinWatchlistOtherMap4Symbol);
  1159. });
  1160. boolean fail = watchlistOtherList.stream().anyMatch(e -> e.getCexSpot() == null || e.getCexSpot().length() < 6);
  1161. if (!fail) {
  1162. coinMapper.insertOrUpdateCoinWatchlistOtherList(watchlistOtherList);
  1163. }
  1164. }
  1165. public void parseWatchlistMap4Coingecko(Map<String, CoinWatchlist> watchlistMap4Coingecko) {
  1166. String coingeckoCoinsMarketsUrl = InitRunner.dicCodeMap.get("coingecko_coins_markets_url").getCodeValue();
  1167. Map<String, String> headerMap = new HashMap<>();
  1168. headerMap.put("Accept", "application/json");
  1169. headerMap.put("Accept-Encoding", "deflate,gzip");
  1170. Map<String, String> paramMap = new LinkedHashMap<>();
  1171. paramMap.put("ids", StringUtils.join(watchlistMap4Coingecko.keySet(), ","));
  1172. paramMap.put("vs_currency", "usd");
  1173. AtomicInteger i = new AtomicInteger();
  1174. Connection.Response response = null;
  1175. try {
  1176. response = JsoupUtil.requestBody(coingeckoCoinsMarketsUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, headerMap, paramMap);
  1177. JSONArray result = JSONArray.parseArray(response.body());
  1178. for (int j = 0; j < result.size(); j++) {
  1179. JSONObject marketData = result.getJSONObject(j);
  1180. String id = marketData.getString("id");
  1181. if (watchlistMap4Coingecko.containsKey(id)) {
  1182. CoinWatchlist coinWatchlist = watchlistMap4Coingecko.get(id);
  1183. // 总市值排名
  1184. if (marketData.containsKey("market_cap_rank") && null != marketData.get("market_cap_rank")) {
  1185. Integer totalMarketRanking = marketData.getInteger("market_cap_rank");
  1186. coinWatchlist.setTotalMarketRanking(totalMarketRanking);
  1187. }
  1188. // 总市值
  1189. if (marketData.containsKey("market_cap") && null != marketData.get("market_cap")) {
  1190. BigDecimal totalMarketValue = marketData.getBigDecimal("market_cap").setScale(2, RoundingMode.HALF_UP);
  1191. coinWatchlist.setTotalMarketValue(totalMarketValue);
  1192. }
  1193. // 市场价格
  1194. if (marketData.containsKey("current_price") && null != marketData.get("current_price")) {
  1195. String markPrice = marketData.getBigDecimal("current_price").toPlainString();
  1196. coinWatchlist.setMarkPrice(markPrice);
  1197. }
  1198. // 24小时价格变化
  1199. if (marketData.containsKey("price_change_percentage_24h") && null != marketData.get("price_change_percentage_24h")) {
  1200. BigDecimal priceChangePercentage24h = marketData.getBigDecimal("price_change_percentage_24h").setScale(2, RoundingMode.HALF_UP);
  1201. coinWatchlist.setPriceChangePercentage24h(priceChangePercentage24h);
  1202. }
  1203. // 历史最高价格
  1204. if (marketData.containsKey("ath") && null != marketData.get("ath")) {
  1205. String highestHistoricalPrice = marketData.getBigDecimal("ath").toPlainString();
  1206. coinWatchlist.setHighestHistoricalPrice(highestHistoricalPrice);
  1207. }
  1208. // 历史最高点涨幅比例
  1209. if (marketData.containsKey("ath_change_percentage") && null != marketData.get("ath_change_percentage")) {
  1210. BigDecimal athChangePercentage = marketData.getBigDecimal("ath_change_percentage").setScale(2, RoundingMode.HALF_UP);
  1211. coinWatchlist.setAthChangePercentage(athChangePercentage);
  1212. }
  1213. // 历史最高日期
  1214. if (marketData.containsKey("ath_date") && null != marketData.get("ath_date")) {
  1215. LocalDate highestHistoricalDate = LocalDate.parse(marketData.getString("ath_date"), DateUtils.utcTimeFormatter);
  1216. coinWatchlist.setHighestHistoricalDate(highestHistoricalDate);
  1217. }
  1218. // 历史最低价格
  1219. if (marketData.containsKey("atl") && null != marketData.get("atl")) {
  1220. String lowestHistoricalPrice = marketData.getString("atl");
  1221. coinWatchlist.setLowestHistoricalPrice(lowestHistoricalPrice);
  1222. }
  1223. // 历史最低点涨幅比例
  1224. if (marketData.containsKey("atl_change_percentage") && null != marketData.get("atl_change_percentage")) {
  1225. BigDecimal atlChangePercentage = marketData.getBigDecimal("atl_change_percentage").setScale(2, RoundingMode.HALF_UP);
  1226. coinWatchlist.setAtlChangePercentage(atlChangePercentage);
  1227. }
  1228. // 历史最低日期
  1229. if (marketData.containsKey("atl_date") && null != marketData.get("atl_date")) {
  1230. LocalDate lowestHistoricalDate = LocalDate.parse(marketData.getString("atl_date"), DateUtils.utcTimeFormatter);
  1231. coinWatchlist.setLowestHistoricalDate(lowestHistoricalDate);
  1232. }
  1233. // 涨幅倍数
  1234. if (StringUtils.isNotEmpty(coinWatchlist.getHighestHistoricalPrice()) && StringUtils.isNotEmpty(coinWatchlist.getLowestHistoricalPrice())) {
  1235. BigDecimal increaseMultiple = new BigDecimal(coinWatchlist.getHighestHistoricalPrice()).divide(new BigDecimal(coinWatchlist.getLowestHistoricalPrice()), 0, RoundingMode.HALF_UP);
  1236. coinWatchlist.setIncreaseMultiple(increaseMultiple.intValue());
  1237. }
  1238. coinMapper.updateCoinWatchlist(coinWatchlist);
  1239. }
  1240. }
  1241. } catch (Exception e) {
  1242. log.error("parseWatchlistMap4Coingecko error,response={},size={},i={}", response != null ? response.body() : "--", watchlistMap4Coingecko.size(), i.get(), e);
  1243. }
  1244. }
  1245. public void parseWatchlistMap4CmC(Map<Long, CoinWatchlist> watchlistMap4CmC) {
  1246. String coinmarketcapApikey = InitRunner.dicCodeMap.get("coinmarketcap_apikey").getCodeValue();
  1247. String coinmarketcapQuotesLatestUrl = InitRunner.dicCodeMap.get("coinmarketcap_quotes_latest_url").getCodeValue();
  1248. Map<String, String> headerMap = new HashMap<>();
  1249. headerMap.put("Accept", "application/json");
  1250. headerMap.put("Accept-Encoding", "deflate,gzip");
  1251. headerMap.put("X-CMC_PRO_API_KEY", coinmarketcapApikey);
  1252. Map<String, String> paramMap = new LinkedHashMap<>();
  1253. paramMap.put("id", StringUtils.join(watchlistMap4CmC.keySet(), ","));
  1254. AtomicInteger i = new AtomicInteger();
  1255. try {
  1256. Connection.Response response = JsoupUtil.requestBody(coinmarketcapQuotesLatestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, headerMap, paramMap);
  1257. JSONObject result = JSONObject.parseObject(response.body());
  1258. JSONObject dataJO = result.getJSONObject("data");
  1259. watchlistMap4CmC.forEach((key, value) -> {
  1260. i.getAndIncrement();
  1261. if (dataJO.containsKey(key)) {
  1262. JSONObject jsonObject = dataJO.getJSONObject(String.valueOf(key));
  1263. value.setTotalMarketRanking(jsonObject.getInteger("cmc_rank"));
  1264. BigDecimal totalMarketValue = null;
  1265. try {
  1266. totalMarketValue = jsonObject.getJSONObject("quote").getJSONObject("USD").getBigDecimal("market_cap").setScale(2, RoundingMode.HALF_UP);
  1267. if (totalMarketValue.compareTo(BigDecimal.ZERO) == 0) {
  1268. totalMarketValue = jsonObject.getBigDecimal("self_reported_market_cap").setScale(2, RoundingMode.HALF_UP);
  1269. }
  1270. } catch (Exception e) {
  1271. if (jsonObject.containsKey("self_reported_market_cap") && null != jsonObject.getBigDecimal("self_reported_market_cap")) {
  1272. totalMarketValue = jsonObject.getBigDecimal("self_reported_market_cap").setScale(2, RoundingMode.HALF_UP);
  1273. }
  1274. }
  1275. value.setTotalMarketValue(totalMarketValue);
  1276. // cmc_url
  1277. value.setCmcUrl(jsonObject.getString("slug"));
  1278. // coingecko_url
  1279. if (StringUtils.isEmpty(value.getCoingeckoUrl())) {
  1280. value.setCoingeckoUrl(value.getCmcUrl());
  1281. }
  1282. coinMapper.updateCoinWatchlist(value);
  1283. // 解析CoinWatchlistOther
  1284. CoinWatchlistOther coinWatchlistOther = new CoinWatchlistOther();
  1285. coinWatchlistOther.setSymbol(value.getSymbol());
  1286. if (null != jsonObject.get("max_supply")) {
  1287. coinWatchlistOther.setMaxSupply(jsonObject.getBigDecimal("max_supply").setScale(0, RoundingMode.HALF_UP).toPlainString());
  1288. }
  1289. if (null != jsonObject.get("circulating_supply")) {
  1290. coinWatchlistOther.setCirculatingSupply(jsonObject.getBigDecimal("circulating_supply").setScale(0, RoundingMode.HALF_UP).toPlainString());
  1291. if ("0".equals(coinWatchlistOther.getCirculatingSupply()) && null != jsonObject.get("self_reported_circulating_supply")) {
  1292. coinWatchlistOther.setCirculatingSupply(jsonObject.getBigDecimal("self_reported_circulating_supply").setScale(0, RoundingMode.HALF_UP).toPlainString());
  1293. }
  1294. }
  1295. if (null != coinWatchlistOther.getMaxSupply() && null != coinWatchlistOther.getCirculatingSupply()) {
  1296. coinWatchlistOther.setCirculatingRate(new BigDecimal(coinWatchlistOther.getCirculatingSupply()).divide(new BigDecimal(coinWatchlistOther.getMaxSupply()), 3, RoundingMode.HALF_UP).multiply(new BigDecimal("100")).toPlainString());
  1297. }
  1298. if (null != jsonObject.get("total_supply")) {
  1299. coinWatchlistOther.setTotalSupply(jsonObject.getBigDecimal("total_supply").setScale(0, RoundingMode.HALF_UP).toPlainString());
  1300. }
  1301. JSONObject usdQuote = jsonObject.getJSONObject("quote").getJSONObject("USD");
  1302. if (usdQuote != null && null != usdQuote.get("market_cap")) {
  1303. coinWatchlistOther.setMarketCap(usdQuote.getBigDecimal("market_cap").setScale(0, RoundingMode.HALF_UP).toPlainString());
  1304. if ("0".equals(coinWatchlistOther.getMarketCap()) && null != jsonObject.get("self_reported_market_cap")) {
  1305. coinWatchlistOther.setMarketCap(jsonObject.getBigDecimal("self_reported_market_cap").setScale(0, RoundingMode.HALF_UP).toPlainString());
  1306. }
  1307. }
  1308. if (usdQuote != null && null != usdQuote.get("fully_diluted_market_cap")) {
  1309. coinWatchlistOther.setFullyDilutedMarketCap(usdQuote.getBigDecimal("fully_diluted_market_cap").setScale(0, RoundingMode.HALF_UP).toPlainString());
  1310. }
  1311. coinMapper.insertOrUpdateCoinWatchlistOther(coinWatchlistOther);
  1312. }
  1313. });
  1314. } catch (Exception e) {
  1315. log.error("parseWatchlistMap4CmC error,size={},i={}", watchlistMap4CmC.size(), i.get(), e);
  1316. }
  1317. }
  1318. @Override
  1319. @Async("coinTaskExecutor")
  1320. public void monitorAlarm4APP_TEXT_CARD(String content, JSONObject params, WxCpService wxCpServiceFinal) {
  1321. // 文本卡片模式发消息
  1322. String title = "监控告警明细";
  1323. if (params.containsKey("title")) {
  1324. title = params.getString("title");
  1325. }
  1326. String logUrl = "https://lvzhiqiang.top";
  1327. if (params.containsKey("logUrl")) {
  1328. logUrl = params.getString("logUrl");
  1329. }
  1330. String btnTxt = "日志详情";
  1331. if (params.containsKey("btnTxt")) {
  1332. btnTxt = params.getString("btnTxt");
  1333. }
  1334. String user = "LvZhiQiang";
  1335. if (params.containsKey("user")) {
  1336. user = params.getString("user");
  1337. }
  1338. String party = "";
  1339. if (params.containsKey("party")) {
  1340. party = params.getString("party");
  1341. }
  1342. String tag = "";
  1343. if (params.containsKey("tag")) {
  1344. tag = params.getString("tag");
  1345. }
  1346. Integer agentId = properties.getAgentId();
  1347. if (params.containsKey("agentId")) {
  1348. agentId = params.getInteger("agentId");
  1349. }
  1350. if (wxCpServiceFinal == null) {
  1351. wxCpServiceFinal = wxCpService;
  1352. }
  1353. WxCpMessage wxCpMessage = WxCpMessage.TEXTCARD().agentId(agentId)
  1354. .toUser(user)
  1355. .toParty(party)
  1356. .toTag(tag)
  1357. .title(title).description(content)
  1358. .url(logUrl).btnTxt(btnTxt)
  1359. .build();
  1360. try {
  1361. log.info("企业微信推送消息,send message: {}", wxCpMessage);
  1362. WxCpMessageSendResult sendResult = wxCpServiceFinal.getMessageService().send(wxCpMessage);
  1363. log.info("企业微信推送消息成功,send result: {}", sendResult);
  1364. } catch (WxErrorException e) {
  1365. log.error("企业微信推送消息失败!Detail: ", e);
  1366. }
  1367. }
  1368. @Override
  1369. @Async("coinTaskExecutor")
  1370. public void monitorAlarm4NEWS(JSONObject params, WxCpService wxCpServiceFinal, NewArticle... articles) {
  1371. // 图文消息
  1372. String title = "监控告警明细";
  1373. if (params.containsKey("title")) {
  1374. title = params.getString("title");
  1375. }
  1376. String logUrl = "https://lvzhiqiang.top";
  1377. if (params.containsKey("logUrl")) {
  1378. logUrl = params.getString("logUrl");
  1379. }
  1380. String btnTxt = "日志详情";
  1381. if (params.containsKey("btnTxt")) {
  1382. btnTxt = params.getString("btnTxt");
  1383. }
  1384. String user = "LvZhiQiang";
  1385. if (params.containsKey("user")) {
  1386. user = params.getString("user");
  1387. }
  1388. String party = "";
  1389. if (params.containsKey("party")) {
  1390. party = params.getString("party");
  1391. }
  1392. String tag = "";
  1393. if (params.containsKey("tag")) {
  1394. tag = params.getString("tag");
  1395. }
  1396. Integer agentId = properties.getAgentId();
  1397. if (params.containsKey("agentId")) {
  1398. agentId = params.getInteger("agentId");
  1399. }
  1400. if (wxCpServiceFinal == null) {
  1401. wxCpServiceFinal = wxCpService;
  1402. }
  1403. WxCpMessage wxCpMessage = WxCpMessage.NEWS().agentId(agentId)
  1404. .toUser(user)
  1405. .toParty(party)
  1406. .toTag(tag)
  1407. .addArticle(articles)
  1408. .build();
  1409. try {
  1410. log.info("企业微信推送消息,send message: {}", wxCpMessage);
  1411. WxCpMessageSendResult sendResult = wxCpServiceFinal.getMessageService().send(wxCpMessage);
  1412. log.info("企业微信推送消息成功,send result: {}", sendResult);
  1413. } catch (WxErrorException e) {
  1414. log.error("企业微信推送消息失败!Detail: ", e);
  1415. }
  1416. }
  1417. @Override
  1418. @Async("coinTaskExecutor")
  1419. public void monitorAlarm4CHAT_BOT(String content, JSONObject params) {
  1420. // 调用企业微信群聊机器人发消息
  1421. WxCpGroupRobotService groupRobotService = wxCpService.getGroupRobotService();
  1422. String webhookUrl = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=082970da-2a33-422a-81f6-15f9bde87940";
  1423. List<String> userList = Collections.singletonList("LvZhiQiang");
  1424. if (params != null && params.containsKey("user")) {
  1425. userList = Arrays.asList(params.getString("user").split("[|,]"));
  1426. }
  1427. try {
  1428. log.info("企业微信推送消息,send content: {}, userIdSet: {}", content, userList);
  1429. groupRobotService.sendText(webhookUrl, content, userList, Collections.emptyList());
  1430. log.info("企业微信推送消息成功");
  1431. } catch (WxErrorException e) {
  1432. log.error("企业微信推送消息失败!Detail: ", e);
  1433. }
  1434. }
  1435. @Override
  1436. @Async("coinTaskExecutor")
  1437. public void monitorAlarm(String content, String jobAlarmMode) {
  1438. // 判断告警模式
  1439. if (StringUtils.isEmpty(JOB_ALARM_MODE)) {
  1440. jobAlarmMode = JOB_ALARM_MODE;
  1441. }
  1442. // 文本卡片模式发消息
  1443. if (JOB_ALARM_MODE_APP_TEXT_CARD.equals(jobAlarmMode)) {
  1444. String title = "监控告警明细";
  1445. String logUrl = "https://lvzhiqiang.top";
  1446. String btnTxt = "日志详情";
  1447. WxCpMessage wxCpMessage = WxCpMessage.TEXTCARD().agentId(properties.getAgentId())
  1448. .toUser("LvZhiQiang")
  1449. .toParty("")
  1450. .toTag("")
  1451. .title(title).description(content)
  1452. .url(logUrl).btnTxt(btnTxt)
  1453. .build();
  1454. try {
  1455. log.info("企业微信推送消息,send message: {}", wxCpMessage);
  1456. WxCpMessageSendResult sendResult = wxCpService.getMessageService().send(wxCpMessage);
  1457. log.info("企业微信推送消息成功,send result: {}", sendResult);
  1458. } catch (WxErrorException e) {
  1459. log.error("企业微信推送消息失败!Detail: ", e);
  1460. }
  1461. }
  1462. // 调用企业微信群聊机器人发消息
  1463. if (JOB_ALARM_MODE_CHAT_BOT.equals(jobAlarmMode)) {
  1464. WxCpGroupRobotService groupRobotService = wxCpService.getGroupRobotService();
  1465. String webhookUrl = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=082970da-2a33-422a-81f6-15f9bde87940";
  1466. try {
  1467. log.info("企业微信推送消息,send content: {}, userIdSet: {}", content, "LvZhiQiang");
  1468. groupRobotService.sendText(webhookUrl, content, Collections.singletonList("LvZhiQiang"), Collections.emptyList());
  1469. log.info("企业微信推送消息成功");
  1470. } catch (WxErrorException e) {
  1471. log.error("企业微信推送消息失败!Detail: ", e);
  1472. }
  1473. }
  1474. }
  1475. /**
  1476. * 请求通用API方法
  1477. */
  1478. private JSONObject requestApi4Common(String requestPath, String signQueryString, String signBody, String httpMethod, Map<String, String> paramMap) {
  1479. String timestamp = String.valueOf(System.currentTimeMillis());
  1480. Map<String, String> headerMap = new HashMap<>();
  1481. headerMap.putAll(basicHeaderMap);
  1482. try {
  1483. String accessSign = CheckSign4Bitget.generate(timestamp, httpMethod, requestPath, signQueryString, signBody, secretKey);
  1484. headerMap.put("ACCESS-TIMESTAMP", timestamp);
  1485. headerMap.put("ACCESS-SIGN", accessSign);
  1486. } catch (CloneNotSupportedException e) {
  1487. throw new RuntimeException(e);
  1488. } catch (InvalidKeyException e) {
  1489. throw new RuntimeException(e);
  1490. } catch (UnsupportedEncodingException e) {
  1491. throw new RuntimeException(e);
  1492. }
  1493. try {
  1494. String requestUrl = mainUrl + requestPath;
  1495. if (httpMethod.equals(JsoupUtil.HTTP_GET)) {
  1496. Connection.Response response = JsoupUtil.requestBody(requestUrl, httpMethod, InitRunner.proxy, headerMap, paramMap);
  1497. return JSONObject.parseObject(response.body());
  1498. } else {
  1499. Connection.Response response = JsoupUtil.requestBodyJSON(requestUrl, httpMethod, InitRunner.proxy, null, headerMap, paramMap);
  1500. return JSONObject.parseObject(response.body());
  1501. }
  1502. } catch (Exception e) {
  1503. throw new RuntimeException(e);
  1504. }
  1505. }
  1506. private JSONObject requestApi4Common4OKX(String requestPath, String signQueryString, String signBody, String httpMethod, Map<String, String> paramMap) {
  1507. String timestamp = DateUtils.getUTCTimeStr();
  1508. Map<String, String> headerMap = new HashMap<>();
  1509. headerMap.putAll(basicHeaderMap4OKX);
  1510. try {
  1511. String accessSign = CheckSign4OKX.generate(timestamp, httpMethod, requestPath, signQueryString, signBody, secretKey4OKX);
  1512. headerMap.put("OK-ACCESS-TIMESTAMP", timestamp);
  1513. headerMap.put("OK-ACCESS-SIGN", accessSign);
  1514. } catch (CloneNotSupportedException e) {
  1515. throw new RuntimeException(e);
  1516. } catch (InvalidKeyException e) {
  1517. throw new RuntimeException(e);
  1518. } catch (UnsupportedEncodingException e) {
  1519. throw new RuntimeException(e);
  1520. }
  1521. try {
  1522. String requestUrl = "https://www.okx.com" + requestPath;
  1523. if (httpMethod.equals(JsoupUtil.HTTP_GET)) {
  1524. Connection.Response response = JsoupUtil.requestBody(requestUrl, httpMethod, InitRunner.proxy, headerMap, paramMap);
  1525. return JSONObject.parseObject(response.body());
  1526. } else {
  1527. Connection.Response response = JsoupUtil.requestBodyJSON(requestUrl, httpMethod, InitRunner.proxy, null, headerMap, paramMap);
  1528. return JSONObject.parseObject(response.body());
  1529. }
  1530. } catch (Exception e) {
  1531. throw new RuntimeException(e);
  1532. }
  1533. }
  1534. @Override
  1535. public Object mainSearch(JSONObject params) throws Exception {
  1536. JSONArray result = new JSONArray();
  1537. if (params.getString("nameEn").equals("allPositionv2")) {
  1538. Map<String, String> paramMap = new HashMap<>();
  1539. paramMap.put("productType", "umcbl");
  1540. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  1541. JSONObject response = requestApi4Common(params.getString("url"), signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  1542. result = response.getJSONArray("data");
  1543. renderMainSearch4AllPositionv2(result, params.getInteger("unrealizedPLSort"));
  1544. } else if (params.getString("nameEn").equals("orderMarginCoinCurrent")) {
  1545. Map<String, String> paramMap = new LinkedHashMap<>();
  1546. paramMap.put("productType", "umcbl");
  1547. paramMap.put("marginCoin", "USDT");
  1548. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  1549. JSONObject response = requestApi4Common(params.getString("url"), signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  1550. result = response.getJSONArray("data");
  1551. renderMainSearch4OrderMarginCoinCurrent(result, params.getInteger("chaRateSort"));
  1552. } else if (params.getString("nameEn").equals("orderHistoryProductType")) {
  1553. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1554. List<CoinHistoryOrder> historyOrderList = coinMapper.findHistoryOrderList(params.toJavaObject(Map.class));
  1555. PageInfo<CoinHistoryOrder> historyOrderPageInfo = new PageInfo<>(historyOrderList);
  1556. renderMainSearch4OrderHistoryProductType(historyOrderList);
  1557. //result = (JSONArray) JSON.toJSON(historyOrderList);
  1558. return historyOrderPageInfo;
  1559. } else if (params.getString("nameEn").equals("traderList")) {
  1560. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1561. List<CoinTrader> mixTraderList = coinMapper.findMixTraderList(params.toJavaObject(Map.class));
  1562. PageInfo<CoinTrader> coinTraderPageInfo = new PageInfo<>(mixTraderList);
  1563. renderMainSearch4TraderList(mixTraderList);
  1564. //result = (JSONArray) JSON.toJSON(mixTraderList);
  1565. return coinTraderPageInfo;
  1566. } else if (params.getString("nameEn").equals("watchlist")) {
  1567. JSONObject coinUser = coinMapper.findUserByUsername(params.getString("userName"));
  1568. if (coinUser == null) {
  1569. throw new ParameterException("用户不存在!");
  1570. }
  1571. Integer userId = coinUser.getInteger("id");
  1572. params.put("userId",userId);
  1573. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1574. if (params.containsKey("sortField")) {
  1575. params.put("sortField", Arrays.asList(params.getString("sortField").split(",")));
  1576. }
  1577. if (params.containsKey("cexFilterField") && StringUtils.isNotEmpty(params.getString("cexFilterField"))) {
  1578. String[] cexFilterFieldArr = params.getString("cexFilterField").split(",");
  1579. params.put("cexFilterName", cexFilterFieldArr[0]);
  1580. params.put("cexFilterIndex", cexFilterFieldArr[1]);
  1581. }
  1582. List<CoinWatchlist> watchlistList = coinMapper.findWatchlistList2ByUserId(params.toJavaObject(Map.class));
  1583. PageInfo<CoinWatchlist> watchlistPageInfo = new PageInfo<>(watchlistList);
  1584. renderMainSearch4Watchlist(watchlistList, userId);
  1585. return watchlistPageInfo;
  1586. } else if (params.getString("nameEn").equals("image")) {
  1587. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1588. List<FileImage> fileImageList = pictureInfoMapper.findImageList(params.toJavaObject(Map.class));
  1589. PageInfo<FileImage> imagePageInfo = new PageInfo<>(fileImageList);
  1590. renderMainSearch4Image(fileImageList);
  1591. return imagePageInfo;
  1592. } else if (params.getString("nameEn").equals("cmcmap")) {
  1593. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1594. List<CoinCmcMap> cmcMapList = coinMapper.findCmcMapList(params.toJavaObject(Map.class));
  1595. PageInfo<CoinCmcMap> cmcMapPageInfo = new PageInfo<>(cmcMapList);
  1596. renderMainSearch4CmcMap(cmcMapList);
  1597. return cmcMapPageInfo;
  1598. } else if (params.getString("nameEn").equals("monitorCurrency")) {
  1599. List<CoinMonitorCurrency> monitorCurrencyList = coinMapper.findMonitorCurrencyList();
  1600. Map<String, JSONArray> resultMulti = new ConcurrentHashMap<>();
  1601. Arrays.stream(params.getString("url").split(",")).parallel().forEach(e -> {
  1602. String requestUrl = mainUrl + e;
  1603. try {
  1604. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  1605. resultMulti.put(e, JSONObject.parseObject(response.body()).getJSONArray("data"));
  1606. } catch (Exception ex) {
  1607. throw new RuntimeException(ex);
  1608. }
  1609. });
  1610. result = renderMainSearch4MonitorCurrency(resultMulti, monitorCurrencyList, params.getInteger("changeUtcSort"));
  1611. } else if (params.getString("nameEn").equals("currentPlan")) {
  1612. Map<String, String> paramMap = new LinkedHashMap<>();
  1613. paramMap.put("productType", "umcbl");
  1614. paramMap.put("isPlan", "plan");
  1615. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  1616. JSONObject response = requestApi4Common(params.getString("url"), signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  1617. result = response.getJSONArray("data");
  1618. renderMainSearch4CurrentPlan(result, params.getInteger("chaRateSort"));
  1619. } else if (params.getString("nameEn").equals("music")) {
  1620. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1621. List<FileMusicCollection> fileMusicCollectionList = musicInfoMapper.findMusicCollectionList(params.toJavaObject(Map.class));
  1622. PageInfo<FileMusicCollection> musicCollectionPageInfo = new PageInfo<>(fileMusicCollectionList);
  1623. renderMainSearch4Music(fileMusicCollectionList);
  1624. return musicCollectionPageInfo;
  1625. } else if (params.getString("nameEn").equals("currentHolding")) {
  1626. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1627. if (params.containsKey("sortField")) {
  1628. params.put("sortField", Arrays.asList(params.getString("sortField").split(",")));
  1629. }
  1630. List<CoinCurrencyHolding> currentHoldingList = coinMapper.findCurrentHoldingList(params.toJavaObject(Map.class));
  1631. PageInfo<CoinCurrencyHolding> currentHoldingPageInfo = new PageInfo<>(currentHoldingList);
  1632. renderMainSearch4CurrencyHolding(currentHoldingList);
  1633. return currentHoldingPageInfo;
  1634. } else if (params.getString("nameEn").equals("bookmark")) {
  1635. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1636. if (params.containsKey("sortField")) {
  1637. params.put("sortField", Arrays.asList(params.getString("sortField").split(",")));
  1638. }
  1639. String level1CategoryField = params.getString("level1CategoryField");
  1640. String level2CategoryField = params.getString("level2CategoryField");
  1641. String level3CategoryField = params.getString("level3CategoryField");
  1642. String categoryId = "999999999999999";
  1643. if (StringUtils.isNotEmpty(level3CategoryField)) {
  1644. categoryId = level3CategoryField;
  1645. } else if (StringUtils.isNotEmpty(level2CategoryField)) {
  1646. categoryId = level2CategoryField;
  1647. } else if (StringUtils.isNotEmpty(level1CategoryField)) {
  1648. categoryId = level1CategoryField;
  1649. }
  1650. String path = bookmarkMapper.selectPathByCategoryId(Long.valueOf(categoryId));
  1651. List<Long> categoryIds = bookmarkMapper.selectCategoryAndChildrenIds(Long.valueOf(categoryId), path);
  1652. params.put("categoryIds", categoryIds);
  1653. List<BookmarkInfo> bookmarkInfoList = bookmarkMapper.findBookmarkList(params.toJavaObject(Map.class));
  1654. PageInfo<BookmarkInfo> bookmarkInfoPageInfo = new PageInfo<>(bookmarkInfoList);
  1655. renderMainSearch4Bookmark(bookmarkInfoList);
  1656. return bookmarkInfoPageInfo;
  1657. } else if (params.getString("nameEn").equals("youtubeLive")) {
  1658. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1659. if (params.containsKey("sortField")) {
  1660. params.put("sortField", Arrays.asList(params.getString("sortField").split(",")));
  1661. }
  1662. List<CoinYoutubeYt2140LiveChapterDTO> yt2140LiveChapterList = coinYoutubeMapper.findYoutubeLiveList(params.toJavaObject(Map.class));
  1663. PageInfo<CoinYoutubeYt2140LiveChapterDTO> yt2140LiveChapterPageInfo = new PageInfo<>(yt2140LiveChapterList);
  1664. renderMainSearch4YoutubeYt2140LiveChapter(yt2140LiveChapterList);
  1665. return yt2140LiveChapterPageInfo;
  1666. } else if (params.getString("nameEn").equals("goldenQuotes")) {
  1667. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1668. if (params.containsKey("sortField")) {
  1669. params.put("sortField", Arrays.asList(params.getString("sortField").split(",")));
  1670. }
  1671. List<GoldenQuotes> goldenQuotesList = goldenQuotesMapper.findGoldenQuotesList(params.toJavaObject(Map.class));
  1672. PageInfo<GoldenQuotes> goldenQuotesPageInfo = new PageInfo<>(goldenQuotesList);
  1673. renderMainSearch4GoldenQuotes(goldenQuotesList);
  1674. return goldenQuotesPageInfo;
  1675. }
  1676. return result;
  1677. }
  1678. private void renderMainSearch4GoldenQuotes(List<GoldenQuotes> goldenQuotesList) {
  1679. for (GoldenQuotes goldenQuotes : goldenQuotesList) {
  1680. // 是否收藏
  1681. goldenQuotes.setIsPinnedStr(1 == goldenQuotes.getIsPinned() ? "是" : "否");
  1682. goldenQuotes.setTags(StringUtils.isNotEmpty(goldenQuotes.getTags()) ? goldenQuotes.getTags() : "");
  1683. String content = goldenQuotes.getContent().length() > 50 ? (goldenQuotes.getContent().substring(0, 50) + "...") : goldenQuotes.getContent();
  1684. goldenQuotes.setContent("<span class=\"primary\" title=\"" + goldenQuotes.getContent() + " \" >" + content + " </span>");
  1685. String remark;
  1686. if (StringUtils.isNotEmpty(goldenQuotes.getRemark())) {
  1687. if (goldenQuotes.getRemark().length() > 15) {
  1688. remark = goldenQuotes.getRemark().substring(0, 15) + "...";
  1689. } else {
  1690. remark = goldenQuotes.getRemark();
  1691. }
  1692. } else {
  1693. remark = "";
  1694. }
  1695. goldenQuotes.setRemark("<span class=\"primary\" title=\"" + goldenQuotes.getRemark() + " \" >" + remark + " </span>");
  1696. if (StringUtils.isNotEmpty(goldenQuotes.getOriginalUrl())) {
  1697. goldenQuotes.setSourcePlatform("<a target=\"_blank\" href=\" " + goldenQuotes.getOriginalUrl() + "\">" + goldenQuotes.getSourcePlatform() + "</a>");
  1698. }
  1699. }
  1700. }
  1701. private void renderMainSearch4YoutubeYt2140LiveChapter(List<CoinYoutubeYt2140LiveChapterDTO> yt2140LiveChapterList) {
  1702. for (CoinYoutubeYt2140LiveChapterDTO yt2140LiveChapter : yt2140LiveChapterList) {
  1703. String chapterUrl = yt2140LiveChapter.getOriginalUrl().concat("&t=").concat(String.valueOf(yt2140LiveChapter.getStartTime())).concat("s");
  1704. // 顺序
  1705. yt2140LiveChapter.setChapterSort(yt2140LiveChapter.getSort() + "/" + yt2140LiveChapter.getChapterCount());
  1706. // 开始时间
  1707. yt2140LiveChapter.setReadableStartTime("<a target=\"_blank\" href=\" " + chapterUrl + "\">" + formatTime(yt2140LiveChapter.getStartTime()) + "</a>");
  1708. // 结束时间
  1709. yt2140LiveChapter.setReadableEndTime(formatTime(yt2140LiveChapter.getEndTime() - 1));
  1710. }
  1711. }
  1712. public String formatTime(long totalSeconds) {
  1713. if (totalSeconds >= 3600) {
  1714. return DurationFormatUtils.formatDuration(totalSeconds * 1000, "H:mm:ss"); // 2:20:20
  1715. } else {
  1716. return DurationFormatUtils.formatDuration(totalSeconds * 1000, "mm:ss"); // 20:20
  1717. }
  1718. }
  1719. private void renderMainSearch4Bookmark(List<BookmarkInfo> bookmarkInfoList) {
  1720. for (BookmarkInfo bookmarkInfo : bookmarkInfoList) {
  1721. // 名称
  1722. bookmarkInfo.setTitle("<a target=\"_blank\" href=\" " + bookmarkInfo.getUrl() + "\">" + bookmarkInfo.getTitle() + "</a>");
  1723. // 是否收藏
  1724. bookmarkInfo.setIsFavoriteStr(bookmarkInfo.getIsFavorite() == 1 ? "是" : "否");
  1725. // 状态
  1726. bookmarkInfo.setStatusStr(bookmarkInfo.getStatus() == 1 ? "正常" : "失效");
  1727. // 备注
  1728. String remark = StringUtils.isNotEmpty(bookmarkInfo.getRemark()) ? (bookmarkInfo.getRemark().length() > 20 ? (bookmarkInfo.getRemark().substring(0, 20) + "...") : bookmarkInfo.getRemark()) : "--";
  1729. bookmarkInfo.setRemark("<span class=\"primary\" avid=\"" + bookmarkInfo.getId() + " \" >" + remark + " </span>");
  1730. }
  1731. }
  1732. private void renderMainSearch4CurrencyHolding(List<CoinCurrencyHolding> currentHoldingList) {
  1733. for (CoinCurrencyHolding coinCurrencyHolding : currentHoldingList) {
  1734. // 名称
  1735. coinCurrencyHolding.setSymbolStyle(" style=\"background-color:rgba(70,169,244,.72);font-weight: bold;\"");
  1736. // 当前价格
  1737. coinCurrencyHolding.setCurrentPriceStyle(" style=\"color:#252B31;background-color:#C4ADE9;\"");
  1738. if (StringUtils.isNotEmpty(coinCurrencyHolding.getCurrentPrice())) {
  1739. coinCurrencyHolding.setCurrentPrice(new BigDecimal(coinCurrencyHolding.getCurrentPrice()).divide(BigDecimal.ONE, new MathContext(3)).toPlainString());
  1740. }
  1741. // 入场价格
  1742. if (StringUtils.isNotEmpty(coinCurrencyHolding.getBuyPrice())) {
  1743. coinCurrencyHolding.setBuyPrice(new BigDecimal(coinCurrencyHolding.getBuyPrice()).divide(BigDecimal.ONE, new MathContext(3)).toPlainString());
  1744. }
  1745. coinCurrencyHolding.setStatusStr(coinCurrencyHolding.getStatus() == 1 ? "正常" : "失效");
  1746. // 涨跌幅比例
  1747. if (coinCurrencyHolding.getChangePercentage() == null) {
  1748. } else if (coinCurrencyHolding.getChangePercentage().compareTo(BigDecimal.ONE) < 0) {
  1749. coinCurrencyHolding.setChangePercentageStyle(" style=\"color:#000000;background-color:#f1a8a4;\"");
  1750. } else {
  1751. coinCurrencyHolding.setChangePercentageStyle(" style=\"color:#000000;background-color:#aad6f5;\"");
  1752. }
  1753. }
  1754. }
  1755. private void renderMainSearch4CmcMap(List<CoinCmcMap> cmcMapList) {
  1756. for (CoinCmcMap coinCmcMap : cmcMapList) {
  1757. String platform = coinCmcMap.getPlatform();
  1758. if (StringUtils.isNotEmpty(platform)) {
  1759. JSONObject jsonObject = JSONObject.parseObject(platform);
  1760. jsonObject.remove("token_address");
  1761. coinCmcMap.setPlatform(jsonObject.toJSONString());
  1762. }
  1763. }
  1764. }
  1765. private void renderMainSearch4Image(List<FileImage> fileImageList) {
  1766. // String ftpBaseurl = InitRunner.dicCodeMap.get("ftp_baseurl").getCodeValue();
  1767. for (FileImage fileImage : fileImageList) {
  1768. // String newPath = "<a target=\"_blank\" href=\" " + ftpBaseurl + fileImage.getPath() + "\">" + fileImage.getNewName() + "</a>";
  1769. // fileImage.setNewName(newPath);
  1770. // fileImage.setOldName("<span class=\"primary\" avid=\"" + fileImage.getId() + " \" >" + fileImage.getOldName() + " </span>");
  1771. fileImage.setRemark("<span class=\"primary\" avid=\"" + fileImage.getId() + " \" >" + fileImage.getRemark() + " </span>");
  1772. }
  1773. }
  1774. private void renderMainSearch4Music(List<FileMusicCollection> fileMusicCollectionList) {
  1775. List<JSONObject> fileMusicCategoryList = coinApiConfigMapper.findFileMusicCategoryList();
  1776. Map<String, String> fileMusicCategoryMap = fileMusicCategoryList.stream().collect(Collectors.toMap(e -> e.getString("id"), e -> e.getString("categoryName")));
  1777. String ftpBasePath = InitRunner.dicCodeMap.get("ftp_music_basepath").getCodeValue();
  1778. String ftpBaseUrl = InitRunner.dicCodeMap.get("ftp_baseurl").getCodeValue();
  1779. String[] scoreTitleArr = {"很差", "较差", "还行", "推荐", "力荐"};
  1780. for (FileMusicCollection fileMusicCollection : fileMusicCollectionList) {
  1781. String categoryName = Arrays.stream(fileMusicCollection.getCategoryId().split(",")).map(original -> fileMusicCategoryMap.getOrDefault(original, original)).collect(Collectors.joining(","));
  1782. fileMusicCollection.setCategoryName(categoryName);
  1783. String remark = StringUtils.isNotEmpty(fileMusicCollection.getRemark()) && fileMusicCollection.getRemark().length() > 20 ? (fileMusicCollection.getRemark().substring(0, 20) + "...") : fileMusicCollection.getRemark();
  1784. fileMusicCollection.setRemark("<span class=\"primary\" avid=\"" + fileMusicCollection.getId() + " \" >" + remark + " </span>");
  1785. if (StringUtils.isNotEmpty(fileMusicCollection.getLowQualityUrl())) {
  1786. fileMusicCollection.setLowQualityUrl(ftpBaseUrl + ftpBasePath + fileMusicCollection.getLowQualityUrl());
  1787. }
  1788. if (StringUtils.isNotEmpty(fileMusicCollection.getHighQualityUrl())) {
  1789. fileMusicCollection.setHighQualityUrl(ftpBaseUrl + ftpBasePath + fileMusicCollection.getHighQualityUrl());
  1790. }
  1791. // 优先级
  1792. Integer score = Integer.valueOf(fileMusicCollection.getScore());
  1793. score = score > 5 ? 5 : score;
  1794. StringBuffer scoreSB = new StringBuffer("<ul class=\"rating\">");
  1795. for (int i = 0; i < scoreTitleArr.length; i++) {
  1796. String style = (i + 1) <= score ? "fa-star" : "fa-star-o";
  1797. scoreSB.append("<li id=\"" +fileMusicCollection.getId() + "\" title=\"" + scoreTitleArr[i] + "\" val=\"" + (i + 1) + "\"><i class=\"fa " + style + "\"></i></li>");
  1798. }
  1799. scoreSB.append("</ul>");
  1800. fileMusicCollection.setScore(scoreSB.toString());
  1801. }
  1802. }
  1803. private void renderMainSearch4Watchlist(List<CoinWatchlist> watchlistList, Integer userId) {
  1804. BigDecimal bigDecimal10000 = new BigDecimal("10000");
  1805. List<String> popularTrackCategoryList = Arrays.asList("DePIN", "AI", "RWA", "大饼生态", "以太Layer-2", "Restaking再质押", "NFT|链游|元宇宙", "WEB3社交");
  1806. // 赛道分类预处理
  1807. List<String> trackCategoryList = coinApiConfigMapper.findTrackCategoryListByUserId(userId).stream().filter(StringUtils::isNotEmpty).collect(Collectors.toList());
  1808. List<String> trackCategory2List = coinApiConfigMapper.findTrackCategory2ListByUserId(userId).stream().filter(StringUtils::isNotEmpty).collect(Collectors.toList());
  1809. Map<String, String> trackCategoryMap = new HashMap<>();
  1810. Map<String, String> trackCategory2Map = new HashMap<>();
  1811. List<String> colorList = coinApiConfigMapper.findColorStyleList();
  1812. int j = 0;
  1813. for (int i = 0; i < trackCategoryList.size(); i++) {
  1814. if (j > colorList.size() - 1) {
  1815. j = 0;
  1816. }
  1817. String popularTrackCategoryStyle = popularTrackCategoryList.contains(trackCategoryList.get(i)) ? "font-weight:bold;" : "";
  1818. trackCategoryMap.put(trackCategoryList.get(i), colorList.get(j) + popularTrackCategoryStyle);
  1819. j++;
  1820. }
  1821. j = 0;
  1822. for (int i = 0; i < trackCategory2List.size(); i++) {
  1823. if (j > colorList.size() - 1) {
  1824. j = 0;
  1825. }
  1826. String popularTrackCategoryStyle = popularTrackCategoryList.contains(trackCategory2List.get(i)) ? "font-weight:bold;" : "";
  1827. trackCategory2Map.put(trackCategory2List.get(i), colorList.get(j) + popularTrackCategoryStyle);
  1828. j++;
  1829. }
  1830. String[] scoreTitleArr = {"很差", "较差", "还行", "推荐", "力荐"};
  1831. String regex = "[+-]?\\d*\\.?\\d*[eE][+-]?\\d+"; // 科学计数法正则表达式
  1832. for (CoinWatchlist coinWatchlist : watchlistList) {
  1833. // 优先级
  1834. Integer score = Integer.valueOf(coinWatchlist.getScore());
  1835. score = score > 5 ? 5 : score;
  1836. StringBuffer scoreSB = new StringBuffer("<ul class=\"rating\">");
  1837. for (int i = 0; i < scoreTitleArr.length; i++) {
  1838. String style = (i + 1) <= score ? "fa-star" : "fa-star-o";
  1839. scoreSB.append("<li id=\"" + coinWatchlist.getSymbol() + "\" title=\"" + scoreTitleArr[i] + "\" val=\"" + (i + 1) + "\"><i class=\"fa " + style + "\"></i></li>");
  1840. }
  1841. scoreSB.append("</ul>");
  1842. coinWatchlist.setScore(scoreSB.toString());
  1843. // 流通市值
  1844. if (null != coinWatchlist.getTotalMarketValue()) {
  1845. BigDecimal divide = coinWatchlist.getTotalMarketValue().divide(bigDecimal10000, 8, RoundingMode.HALF_UP);
  1846. if (divide.compareTo(bigDecimal10000) <= 0) {
  1847. coinWatchlist.setTotalMarketValueStr(divide.setScale(2, RoundingMode.HALF_UP) + "万");
  1848. } else {
  1849. divide = divide.divide(bigDecimal10000, 2, RoundingMode.HALF_UP);
  1850. coinWatchlist.setTotalMarketValueStr(divide + "亿");
  1851. }
  1852. coinWatchlist.setTotalMarketValueStr("<span class=\"primary\" avid=\"" + coinWatchlist.getSymbol() + "\" >" + coinWatchlist.getTotalMarketValueStr() + " </span>");
  1853. }
  1854. // 赛道分类
  1855. String[] trackCategoryArr = coinWatchlist.getTrackCategory().split(",");
  1856. StringBuffer sb = new StringBuffer();
  1857. String trackCategoryLength = "";
  1858. for (int i = 0; i < trackCategoryArr.length; i++) {
  1859. if (i == trackCategoryArr.length - 1) {
  1860. if (trackCategoryArr.length > 1 && trackCategoryLength.length() > 10) {
  1861. sb.append("<br>");
  1862. }
  1863. sb.append("<span class=\"selected-value\" style=\"" + trackCategoryMap.get(trackCategoryArr[i]) + " \" >" + trackCategoryArr[i] + " </span>");
  1864. } else {
  1865. trackCategoryLength += trackCategoryArr[i];
  1866. sb.append("<span class=\"selected-value\" style=\"margin-right:0.3em;" + trackCategoryMap.get(trackCategoryArr[i]) + " \" >" + trackCategoryArr[i] + " </span>");
  1867. }
  1868. }
  1869. coinWatchlist.setTrackCategoryStyle(" style=\"padding:0em 0.3em;\"");
  1870. coinWatchlist.setTrackCategory(sb.toString());
  1871. String[] trackCategory2Arr = coinWatchlist.getTrackCategory2().split(",");
  1872. StringBuffer sb2 = new StringBuffer();
  1873. for (int i = 0; i < trackCategory2Arr.length; i++) {
  1874. sb2.append("<span class=\"selected-value\" style=\"margin-right:0.3em;" + trackCategory2Map.get(trackCategory2Arr[i]) + " \" >" + trackCategory2Arr[i] + " </span>");
  1875. if (i % 2 == 1 && i != trackCategory2Arr.length - 1) {
  1876. sb2.append("<br>");
  1877. }
  1878. }
  1879. coinWatchlist.setTrackCategory2Style(" style=\"padding:0em 0.3em;\"");
  1880. coinWatchlist.setTrackCategory2(sb2.toString());
  1881. // 名称
  1882. coinWatchlist.setSymbolStyle(" style=\"background-color:rgba(70,169,244,.72);font-weight: bold;\"");
  1883. // 市场价格
  1884. coinWatchlist.setMarkPriceStyle(" style=\"color:#252B31;background-color:#C4ADE9;\"");
  1885. if (StringUtils.isNotEmpty(coinWatchlist.getMarkPrice())) {
  1886. if (!coinWatchlist.getMarkPrice().matches(regex)) {
  1887. //coinWatchlist.setMarkPrice(new BigDecimal(coinWatchlist.getMarkPrice()).divide(BigDecimal.ONE, new MathContext(3)).toPlainString());
  1888. coinWatchlist.setMarkPrice(formatNumber(coinWatchlist.getMarkPrice()));
  1889. }
  1890. }
  1891. if (StringUtils.isNotEmpty(coinWatchlist.getHighestHistoricalPrice())) {
  1892. if (!coinWatchlist.getHighestHistoricalPrice().matches(regex)) {
  1893. //coinWatchlist.setHighestHistoricalPrice(new BigDecimal(coinWatchlist.getHighestHistoricalPrice()).divide(BigDecimal.ONE, new MathContext(3)).toPlainString());
  1894. coinWatchlist.setHighestHistoricalPrice(formatNumber(coinWatchlist.getHighestHistoricalPrice()));
  1895. }
  1896. }
  1897. if (StringUtils.isNotEmpty(coinWatchlist.getLowestHistoricalPrice())) {
  1898. if (!coinWatchlist.getLowestHistoricalPrice().matches(regex)) {
  1899. //coinWatchlist.setLowestHistoricalPrice(new BigDecimal(coinWatchlist.getLowestHistoricalPrice()).divide(BigDecimal.ONE, new MathContext(3)).toPlainString());
  1900. coinWatchlist.setLowestHistoricalPrice(formatNumber(coinWatchlist.getLowestHistoricalPrice()));
  1901. }
  1902. }
  1903. // 24小时价格变化
  1904. if (coinWatchlist.getPriceChangePercentage24h() == null) {
  1905. } else if (coinWatchlist.getPriceChangePercentage24h().compareTo(BigDecimal.ZERO) < 0) {
  1906. coinWatchlist.setPriceChangePercentage24hStyle(" style=\"color:#000000;background-color:#f1a8a4;\"");
  1907. } else {
  1908. coinWatchlist.setPriceChangePercentage24hStyle(" style=\"color:#000000;background-color:#aad6f5;\"");
  1909. }
  1910. /*// 历史最高点涨幅比例
  1911. if (coinWatchlist.getAthChangePercentage() == null) {
  1912. } else if (coinWatchlist.getAthChangePercentage().compareTo(BigDecimal.ZERO) < 0) {
  1913. coinWatchlist.setAthChangePercentageStyle(" style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1914. } else {
  1915. coinWatchlist.setAthChangePercentageStyle(" style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1916. }
  1917. // 历史最低点涨幅比例
  1918. if (coinWatchlist.getAtlChangePercentage() == null) {
  1919. } else if (coinWatchlist.getAtlChangePercentage().compareTo(BigDecimal.ZERO) < 0) {
  1920. coinWatchlist.setAtlChangePercentageStyle(" style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1921. } else {
  1922. coinWatchlist.setAtlChangePercentageStyle(" style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1923. }*/
  1924. // 涨幅倍数2
  1925. if (StringUtils.isNotEmpty(coinWatchlist.getHighestHistoricalPrice()) && StringUtils.isNotEmpty(coinWatchlist.getMarkPrice())) {
  1926. BigDecimal increaseMultiple = new BigDecimal(coinWatchlist.getHighestHistoricalPrice()).divide(new BigDecimal(coinWatchlist.getMarkPrice()), 1, RoundingMode.HALF_UP);
  1927. coinWatchlist.setIncreaseMultiple2(increaseMultiple.toPlainString());
  1928. if (increaseMultiple.compareTo(new BigDecimal("50")) >= 0) {
  1929. coinWatchlist.setIncreaseMultiple2Style(" style=\"color:#000000;background-color:#5a964e;\"");
  1930. } else if (increaseMultiple.compareTo(new BigDecimal("10")) >= 0) {
  1931. coinWatchlist.setIncreaseMultiple2Style(" style=\"color:#000000;background-color:#9cc494;\"");
  1932. } else {
  1933. coinWatchlist.setIncreaseMultiple2Style(" style=\"color:#000000;background-color:#dae8d7;\"");
  1934. }
  1935. }
  1936. }
  1937. }
  1938. private static String formatNumber(String str) {
  1939. double number = Double.parseDouble(str);
  1940. int dotIndex = str.indexOf('.');
  1941. if (dotIndex == -1) {
  1942. return str; // 无小数点,直接返回
  1943. }
  1944. // 检查小数点后连续0的数量
  1945. int zeroCount = 0;
  1946. for (int i = dotIndex + 1; i < str.length(); i++) {
  1947. if (str.charAt(i) == '0') {
  1948. zeroCount++;
  1949. } else {
  1950. break; // 遇到非0字符停止计数
  1951. }
  1952. }
  1953. // 如果0的数量 >=5,用科学计数法;否则用普通小数
  1954. if (zeroCount >= 5) {
  1955. DecimalFormat df = new DecimalFormat("0.###E0"); // 科学计数法
  1956. return df.format(number).replace("E-0", "E-"); // 修正E-09为E-9
  1957. } else {
  1958. return new BigDecimal(str).divide(BigDecimal.ONE, new MathContext(3)).stripTrailingZeros().toPlainString();
  1959. }
  1960. }
  1961. private void renderMainSearch4TraderList(List<CoinTrader> mixTraderList) {
  1962. for (CoinTrader mixTrader : mixTraderList) {
  1963. mixTrader.setLastTradeTime(DateUtils.longToString(Long.valueOf(mixTrader.getLastTradeTime())));
  1964. }
  1965. }
  1966. /**
  1967. * 渲染获取当前计划委托(止盈止损)列表
  1968. *
  1969. * @param result
  1970. */
  1971. private void renderMainSearch4CurrentPlan(JSONArray result, Integer chaRateSort) {
  1972. forkJoinPool3.submit(() -> result.parallelStream().forEach(e -> {
  1973. JSONObject jsonObject = (JSONObject) e;
  1974. // 币对名称
  1975. String symbol = jsonObject.getString("symbol");
  1976. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol.replace("USDT_UMCBL", "") + "</font></strong>USDT_UMCBL");
  1977. // 订单状态
  1978. jsonObject.put("status", InitRunner.publicParamsMap.get("status").getString(jsonObject.getString("status")));
  1979. // 交易类型
  1980. jsonObject.put("orderType", InitRunner.publicParamsMap.get("orderType").getString(jsonObject.getString("orderType")));
  1981. // 订单类型
  1982. jsonObject.put("planType", InitRunner.publicParamsMap.get("planType").getString(jsonObject.getString("planType")));
  1983. // 开单方向
  1984. String side = jsonObject.getString("side");
  1985. jsonObject.put("side", InitRunner.publicParamsMap.get("side").getString(side));
  1986. if (side.equals("open_long")) {
  1987. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1988. } else if (side.equals("open_short")) {
  1989. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1990. } else {
  1991. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\"");
  1992. }
  1993. // 触发类型
  1994. jsonObject.put("triggerType", InitRunner.publicParamsMap.get("triggerType").getString(jsonObject.getString("triggerType")));
  1995. jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime")));
  1996. jsonObject.put("uTime", StringUtils.isEmpty(jsonObject.getString("uTime")) ? "--" : DateUtils.longToString(jsonObject.getLong("uTime")));
  1997. // 获取合约标记价格
  1998. String requestUrl = mainUrl + "/api/mix/v1/market/mark-price?symbol=" + symbol;
  1999. try {
  2000. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  2001. String markPrice = JSONObject.parseObject(response.body()).getJSONObject("data").getString("markPrice");
  2002. BigDecimal chaRate = BigDecimal.ZERO;
  2003. BigDecimal triggerPriceDecimal = new BigDecimal(jsonObject.getString("triggerPrice"));
  2004. BigDecimal markPriceDecimal = new BigDecimal(markPrice);
  2005. if (markPriceDecimal.compareTo(triggerPriceDecimal) < 0) {
  2006. chaRate = markPriceDecimal.divide(triggerPriceDecimal, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  2007. } else if (markPriceDecimal.compareTo(triggerPriceDecimal) > 0) {
  2008. chaRate = triggerPriceDecimal.divide(markPriceDecimal, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  2009. }
  2010. jsonObject.put("markPrice", markPrice);
  2011. jsonObject.put("markPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  2012. jsonObject.put("chaRate", chaRate);
  2013. jsonObject.put("chaRateStyle", " style=\"color:#FFFFFF;background-color:#5EA294;\"");
  2014. } catch (Exception ex) {
  2015. throw new RuntimeException(ex);
  2016. }
  2017. })).join();
  2018. if (chaRateSort != 0) {
  2019. Collections.sort(result, (o1, o2) -> chaRateSort * (((JSONObject) o1).getBigDecimal("chaRate").compareTo(((JSONObject) o2).getBigDecimal("chaRate"))));
  2020. }
  2021. }
  2022. /**
  2023. * 渲染监控币种列表
  2024. *
  2025. * @param monitorCurrencyList
  2026. */
  2027. private JSONArray renderMainSearch4MonitorCurrency(Map<String, JSONArray> resultMulti, List<CoinMonitorCurrency> monitorCurrencyList, Integer changeUtcSort) {
  2028. Map<String, CoinMonitorCurrency> monitorCurrencyMap4Mix = monitorCurrencyList.stream().filter(e -> e.getType().equals("2")).collect(Collectors.toMap(CoinMonitorCurrency::getSymbol, Function.identity(), (key1, key2) -> key1));
  2029. Set<String> symbolSet4Mix = monitorCurrencyMap4Mix.keySet();
  2030. Map<String, CoinMonitorCurrency> monitorCurrencyMap4Spot = monitorCurrencyList.stream().filter(e -> e.getType().contains("1")).collect(Collectors.toMap(CoinMonitorCurrency::getSymbol, Function.identity(), (key1, key2) -> key1));
  2031. Set<String> symbolSet4Spot = monitorCurrencyMap4Spot.keySet();
  2032. JSONArray array4Spot = resultMulti.get("/api/spot/v1/market/tickers").stream()
  2033. .filter(iter -> symbolSet4Spot.contains(((JSONObject) iter).getString("symbol")))
  2034. .collect(Collectors.toCollection(JSONArray::new));
  2035. JSONArray array4Mix = resultMulti.get("/api/mix/v1/market/tickers?productType=umcbl").stream()
  2036. .filter(iter -> symbolSet4Mix.contains(((JSONObject) iter).getString("symbol")))
  2037. .collect(Collectors.toCollection(JSONArray::new));
  2038. forkJoinPool.submit(() -> array4Spot.parallelStream().forEach(e -> {
  2039. JSONObject jsonObject = (JSONObject) e;
  2040. jsonObject.put("changeUtc", jsonObject.getBigDecimal("changeUtc").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  2041. jsonObject.put("change", jsonObject.getBigDecimal("change").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  2042. jsonObject.put("ts", DateUtils.longToString(jsonObject.getLong("ts")));
  2043. jsonObject.put("category", monitorCurrencyMap4Spot.get(jsonObject.getString("symbol")).getCategory());
  2044. // UTC0时涨跌幅
  2045. if (jsonObject.getBigDecimal("changeUtc").compareTo(BigDecimal.ZERO) < 0) {
  2046. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2047. } else {
  2048. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2049. }
  2050. // 24小时涨跌幅
  2051. if (jsonObject.getBigDecimal("change").compareTo(BigDecimal.ZERO) < 0) {
  2052. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2053. } else {
  2054. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2055. }
  2056. // 币对名称
  2057. String symbol = jsonObject.getString("symbol").replace("USDT", "");
  2058. if ("BTC".equals(symbol) || "ETH".equals(symbol)) {
  2059. jsonObject.put("symbol", "<strong style=\"background-color:#FF6EB4;\"><font color=\"#242A30\">" + symbol + "</font></strong>USDT");
  2060. } else {
  2061. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol + "</font></strong>USDT");
  2062. }
  2063. // 标记价格
  2064. jsonObject.put("closeStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  2065. // 基础币量 计价币量 usdt币量
  2066. jsonObject.put("baseVol", readableFileSize(jsonObject.getDouble("baseVol")));
  2067. jsonObject.put("quoteVol", readableFileSize(jsonObject.getDouble("quoteVol")));
  2068. jsonObject.put("usdtVol", readableFileSize(jsonObject.getDouble("usdtVol")));
  2069. })).join();
  2070. forkJoinPool.submit(() -> array4Mix.parallelStream().forEach(e -> {
  2071. JSONObject jsonObject = (JSONObject) e;
  2072. jsonObject.put("changeUtc", jsonObject.getBigDecimal("chgUtc").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  2073. jsonObject.put("change", jsonObject.getBigDecimal("priceChangePercent").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  2074. jsonObject.put("ts", DateUtils.longToString(jsonObject.getLong("timestamp")));
  2075. jsonObject.put("category", monitorCurrencyMap4Mix.get(jsonObject.getString("symbol")).getCategory());
  2076. // UTC0时涨跌幅
  2077. if (jsonObject.getBigDecimal("changeUtc").compareTo(BigDecimal.ZERO) < 0) {
  2078. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2079. } else {
  2080. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2081. }
  2082. // 24小时涨跌幅
  2083. if (jsonObject.getBigDecimal("change").compareTo(BigDecimal.ZERO) < 0) {
  2084. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2085. } else {
  2086. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2087. }
  2088. // 币对名称
  2089. String symbol = jsonObject.getString("symbol").replace("USDT_UMCBL", "");
  2090. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol + "</font></strong>USDT_UMCBL");
  2091. // 标记价格
  2092. jsonObject.put("close", jsonObject.getString("last"));
  2093. jsonObject.put("closeStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  2094. // 基础币量 计价币量 usdt币量
  2095. jsonObject.put("baseVol", readableFileSize(jsonObject.getDouble("baseVolume")));
  2096. jsonObject.put("quoteVol", readableFileSize(jsonObject.getDouble("quoteVolume")));
  2097. jsonObject.put("usdtVol", readableFileSize(jsonObject.getDouble("quoteVolume")));
  2098. // 其他字段兼容
  2099. jsonObject.put("openUtc0", jsonObject.getString("openUtc"));
  2100. jsonObject.put("buyOne", jsonObject.getString("bestBid"));
  2101. jsonObject.put("sellOne", jsonObject.getString("bestAsk"));
  2102. })).join();
  2103. array4Spot.addAll(array4Mix);
  2104. if (changeUtcSort != 0) {
  2105. Collections.sort(array4Spot, (o1, o2) -> changeUtcSort * (((JSONObject) o1).getBigDecimal("changeUtc").compareTo(((JSONObject) o2).getBigDecimal("changeUtc"))));
  2106. }
  2107. return array4Spot;
  2108. }
  2109. /**
  2110. * 渲染获取全部历史委托
  2111. *
  2112. * @param historyOrderList
  2113. */
  2114. private void renderMainSearch4OrderHistoryProductType(List<CoinHistoryOrder> historyOrderList) {
  2115. for (CoinHistoryOrder coinHistoryOrder : historyOrderList) {
  2116. // 币种名称
  2117. coinHistoryOrder.setSymbol(coinHistoryOrder.getSymbol().replace("USDT_UMCBL", ""));
  2118. // 订单状态
  2119. coinHistoryOrder.setState(InitRunner.publicParamsMap.get("state").getString(coinHistoryOrder.getState()));
  2120. // 开单方向
  2121. coinHistoryOrder.setSide(InitRunner.publicParamsMap.get("side").getString(coinHistoryOrder.getSide()));
  2122. // 总盈亏
  2123. String TotalProfits = "0E-8";
  2124. if (!coinHistoryOrder.getTotalProfits().contains("0E-8")) {
  2125. TotalProfits = new BigDecimal(coinHistoryOrder.getTotalProfits()).setScale(2, RoundingMode.HALF_UP).toPlainString();
  2126. }
  2127. coinHistoryOrder.setTotalProfits(TotalProfits);
  2128. // 手续费
  2129. String fee = "0E-8";
  2130. if (!coinHistoryOrder.getFee().contains("0E-8")) {
  2131. fee = new BigDecimal(coinHistoryOrder.getFee()).setScale(2, RoundingMode.HALF_UP).toPlainString();
  2132. }
  2133. coinHistoryOrder.setFee(fee);
  2134. // 持仓方向
  2135. coinHistoryOrder.setPosSide(InitRunner.publicParamsMap.get("posSide").getString(coinHistoryOrder.getPosSide()));
  2136. // 仓位模式
  2137. coinHistoryOrder.setMarginMode(InitRunner.publicParamsMap.get("marginMode").getString(coinHistoryOrder.getMarginMode()));
  2138. // 交易类型
  2139. coinHistoryOrder.setOrderType(InitRunner.publicParamsMap.get("orderType").getString(coinHistoryOrder.getOrderType()));
  2140. // 交易方向
  2141. coinHistoryOrder.setTradeSide(InitRunner.publicParamsMap.get("tradeSide").getString(coinHistoryOrder.getTradeSide()));
  2142. // 持仓模式
  2143. coinHistoryOrder.setHoldMode(InitRunner.publicParamsMap.get("holdMode").getString(coinHistoryOrder.getHoldMode()));
  2144. // orderSource
  2145. coinHistoryOrder.setOrderSource(InitRunner.publicParamsMap.get("orderSource").getString(coinHistoryOrder.getOrderSource()));
  2146. coinHistoryOrder.setCTime(DateUtils.longToString(Long.valueOf(coinHistoryOrder.getCTime())));
  2147. coinHistoryOrder.setUTime(DateUtils.longToString(Long.valueOf(coinHistoryOrder.getUTime())));
  2148. }
  2149. }
  2150. /**
  2151. * 渲染获取全部当前委托
  2152. *
  2153. * @param result
  2154. */
  2155. private void renderMainSearch4OrderMarginCoinCurrent(JSONArray result, Integer chaRateSort) {
  2156. forkJoinPool2.submit(() -> result.parallelStream().forEach(e -> {
  2157. JSONObject jsonObject = (JSONObject) e;
  2158. // 币对名称
  2159. String symbol = jsonObject.getString("symbol");
  2160. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol.replace("USDT_UMCBL", "") + "</font></strong>USDT_UMCBL");
  2161. // 订单状态
  2162. jsonObject.put("state", InitRunner.publicParamsMap.get("state").getString(jsonObject.getString("state")));
  2163. // 开单方向
  2164. String side = jsonObject.getString("side");
  2165. jsonObject.put("side", InitRunner.publicParamsMap.get("side").getString(side));
  2166. if (side.equals("open_long")) {
  2167. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2168. } else if (side.equals("open_short")) {
  2169. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2170. } else {
  2171. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\"");
  2172. }
  2173. // 交易类型
  2174. jsonObject.put("orderType", InitRunner.publicParamsMap.get("orderType").getString(jsonObject.getString("orderType")));
  2175. // 止盈止损
  2176. jsonObject.put("presetTakeProfitPrice", StringUtils.isEmpty(jsonObject.getString("presetTakeProfitPrice")) ? "--" : jsonObject.getString("presetTakeProfitPrice"));
  2177. jsonObject.put("presetStopLossPrice", StringUtils.isEmpty(jsonObject.getString("presetTakeProfitPrice")) ? "--" : jsonObject.getString("presetTakeProfitPrice"));
  2178. // 持仓模式
  2179. jsonObject.put("holdMode", InitRunner.publicParamsMap.get("holdMode").getString(jsonObject.getString("holdMode")));
  2180. // orderSource
  2181. jsonObject.put("orderSource", InitRunner.publicParamsMap.get("orderSource").getString(jsonObject.getString("orderSource")));
  2182. // 仓位模式
  2183. jsonObject.put("marginMode", InitRunner.publicParamsMap.get("marginMode").getString(jsonObject.getString("marginMode")));
  2184. jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime")));
  2185. jsonObject.put("uTime", DateUtils.longToString(jsonObject.getLong("uTime")));
  2186. // 获取合约标记价格
  2187. String requestUrl = mainUrl + "/api/mix/v1/market/mark-price?symbol=" + symbol;
  2188. try {
  2189. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  2190. String markPrice = JSONObject.parseObject(response.body()).getJSONObject("data").getString("markPrice");
  2191. BigDecimal chaRate = BigDecimal.ZERO;
  2192. if ("open_short".equals(side)) {
  2193. chaRate = new BigDecimal(markPrice).divide(new BigDecimal(jsonObject.getString("price")), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  2194. } else if ("open_long".equals(side)) {
  2195. chaRate = new BigDecimal(jsonObject.getString("price")).divide(new BigDecimal(markPrice), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  2196. }
  2197. jsonObject.put("markPrice", markPrice);
  2198. jsonObject.put("markPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  2199. jsonObject.put("chaRate", chaRate);
  2200. jsonObject.put("chaRateStyle", " style=\"color:#FFFFFF;background-color:#5EA294;\"");
  2201. } catch (Exception ex) {
  2202. throw new RuntimeException(ex);
  2203. }
  2204. })).join();
  2205. if (chaRateSort != 0) {
  2206. Collections.sort(result, (o1, o2) -> chaRateSort * (((JSONObject) o1).getBigDecimal("chaRate").compareTo(((JSONObject) o2).getBigDecimal("chaRate"))));
  2207. }
  2208. }
  2209. /**
  2210. * 渲染获取全部合约仓位信息V2
  2211. *
  2212. * @param result
  2213. */
  2214. private void renderMainSearch4AllPositionv2(JSONArray result, Integer unrealizedPLSort) {
  2215. forkJoinPool4.submit(() -> result.parallelStream().forEach(e -> {
  2216. JSONObject jsonObject = (JSONObject) e;
  2217. // 币对名称
  2218. String symbol = jsonObject.getString("symbol");
  2219. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol.replace("USDT_UMCBL", "") + "</font></strong>USDT_UMCBL");
  2220. // 持仓方向
  2221. String holdSide = jsonObject.getString("holdSide");
  2222. jsonObject.put("holdSide", InitRunner.publicParamsMap.get("holdSide").getString(holdSide));
  2223. if (holdSide.equals("long")) {
  2224. jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2225. } else if (holdSide.equals("short")) {
  2226. jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2227. } else {
  2228. jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\"");
  2229. }
  2230. // 保证金模式
  2231. jsonObject.put("marginMode", InitRunner.publicParamsMap.get("marginMode").getString(jsonObject.getString("marginMode")));
  2232. // 持仓模式
  2233. jsonObject.put("holdMode", InitRunner.publicParamsMap.get("holdMode").getString(jsonObject.getString("holdMode")));
  2234. // 最近更新时间 保证金数量 (保证金币种) 平均开仓价 未实现盈亏 预估强平价
  2235. jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime")));
  2236. jsonObject.put("margin", new BigDecimal(jsonObject.getString("margin")).setScale(4, RoundingMode.HALF_UP));
  2237. jsonObject.put("averageOpenPrice", new BigDecimal(jsonObject.getString("averageOpenPrice")).divide(BigDecimal.ONE, new MathContext(4)));
  2238. jsonObject.put("unrealizedPL", new BigDecimal(jsonObject.getString("unrealizedPL")).setScale(4, RoundingMode.HALF_UP));
  2239. jsonObject.put("liquidationPrice", new BigDecimal(jsonObject.getString("liquidationPrice")).divide(BigDecimal.ONE, new MathContext(4)));
  2240. // 未实现盈亏
  2241. if (jsonObject.getBigDecimal("unrealizedPL").compareTo(BigDecimal.ZERO) < 0) {
  2242. jsonObject.put("unrealizedPLStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2243. } else {
  2244. jsonObject.put("unrealizedPLStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2245. }
  2246. // 回报率=未实现盈亏/保证金
  2247. BigDecimal returnRate = jsonObject.getBigDecimal("unrealizedPL").divide(jsonObject.getBigDecimal("margin"), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  2248. jsonObject.put("returnRate", returnRate);
  2249. if (returnRate.compareTo(BigDecimal.ZERO) < 0) {
  2250. jsonObject.put("returnRateStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2251. } else {
  2252. jsonObject.put("returnRateStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2253. }
  2254. // 获取当前资金费率
  2255. String requestUrl = mainUrl + "/api/mix/v1/market/current-fundRate?symbol=" + symbol;
  2256. try {
  2257. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  2258. String fundingRate = JSONObject.parseObject(response.body()).getJSONObject("data").getString("fundingRate");
  2259. if (new BigDecimal(fundingRate).compareTo(BigDecimal.ZERO) < 0) {
  2260. jsonObject.put("fundingRateStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2261. } else {
  2262. jsonObject.put("fundingRateStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2263. }
  2264. jsonObject.put("fundingRate", new BigDecimal(fundingRate).multiply(BigDecimal.valueOf(100)).setScale(4, RoundingMode.HALF_UP).toPlainString() + "%");
  2265. } catch (Exception ex) {
  2266. throw new RuntimeException(ex);
  2267. }
  2268. // 标记价格
  2269. jsonObject.put("marketPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  2270. })).join();
  2271. if (unrealizedPLSort != 0) {
  2272. Collections.sort(result, (o1, o2) -> unrealizedPLSort * (((JSONObject) o1).getBigDecimal("unrealizedPL").compareTo(((JSONObject) o2).getBigDecimal("unrealizedPL"))));
  2273. }
  2274. }
  2275. /**
  2276. * Java实现字节转换,可以自动转换为B、KB、MB、GB、TB
  2277. *
  2278. * @param size
  2279. * @return
  2280. */
  2281. private String readableFileSize(double size) {
  2282. if (size <= 0) {
  2283. return "0";
  2284. }
  2285. final String[] units = new String[]{"B", "K", "M", "G", "T"};
  2286. int digitGroups = (int) (Math.log10(size) / Math.log10(1000));
  2287. return df1.format(size / Math.pow(1000, digitGroups)) + units[digitGroups];
  2288. }
  2289. public String getMonitorJobStatus(String jobName) {
  2290. Map<String, JSONObject> monitorJobConfig = getMonitorJobConfig();
  2291. if (null != monitorJobConfig && monitorJobConfig.containsKey(jobName)) {
  2292. return monitorJobConfig.get(jobName).getString("job_status");
  2293. }
  2294. return null;
  2295. }
  2296. @Override
  2297. public Map<String, JSONObject> getMonitorJobConfig() {
  2298. String cacheKey = "coin:monitor:job:list";
  2299. // 1. 缓存有,直接返回
  2300. if (redisUtils.hasKey(cacheKey) && redisUtils.get(cacheKey) != null) {
  2301. return (Map<String, JSONObject>) redisUtils.get(cacheKey);
  2302. }
  2303. // 加锁防止同时对一个数据发送多次请求
  2304. RLock lock = redissonClient.getLock("lock:" + cacheKey);
  2305. try {
  2306. // 2. 尝试加分布式锁,最多等待30秒,上锁以后60秒自动解锁
  2307. boolean lockFlag = lock.tryLock(30, 60, TimeUnit.SECONDS);
  2308. if (lockFlag) {
  2309. // 3. 加锁成功,二次检查,缓存有,直接返回
  2310. if (redisUtils.hasKey(cacheKey) && redisUtils.get(cacheKey) != null) {
  2311. return (Map<String, JSONObject>) redisUtils.get(cacheKey);
  2312. }
  2313. // 4. 查数据库,并且按给定的时长加到缓存中
  2314. Map<String, JSONObject> monitorJobConfigMap = coinMapper.findMonitorJobConfig();
  2315. // 缓存监控任务配置信息
  2316. if (monitorJobConfigMap != null) {
  2317. redisUtils.set(cacheKey, monitorJobConfigMap, 60, TimeUnit.MINUTES);
  2318. }
  2319. return monitorJobConfigMap;
  2320. } else {
  2321. log.error("getMonitorJobConfig 加锁失败 error,lockFlag: false");
  2322. return null;
  2323. }
  2324. } catch (Exception e) {
  2325. log.error("getMonitorJobConfig Exception", e);
  2326. return InitRunner.monitorJobConfigMap;
  2327. } finally {
  2328. if (lock.isLocked() && lock.isHeldByCurrentThread()) {
  2329. lock.unlock();
  2330. }
  2331. }
  2332. }
  2333. @Override
  2334. public Object mainSearchDetail(String userName, String nameEn, String id) {
  2335. if ("image".equals(nameEn)) {
  2336. FileImage fileImage = pictureInfoMapper.findFileImageById(Long.valueOf(id));
  2337. String ftpBaseurl = InitRunner.dicCodeMap.get("ftp_baseurl").getCodeValue();
  2338. String ftpBasePath = InitRunner.dicCodeMap.get("ftp_basepath").getCodeValue();
  2339. String ftpThumbnailBasePath = InitRunner.dicCodeMap.get("ftp_thumbnail_basepath").getCodeValue();
  2340. String path = fileImage.getPath();
  2341. fileImage.setPath(ftpBaseurl + ftpBasePath + path);
  2342. fileImage.setThumbnailPath(ftpBaseurl + ftpThumbnailBasePath + path);
  2343. return fileImage;
  2344. } else if ("watchlist".equals(nameEn)) {
  2345. Integer userId;
  2346. if (StringUtils.isEmpty(userName)) {
  2347. throw new ParameterException("userName为空!");
  2348. } else {
  2349. JSONObject coinUser = coinMapper.findUserByUsername(userName);
  2350. if (coinUser == null) {
  2351. throw new ParameterException("用户不存在!");
  2352. }
  2353. userId = coinUser.getInteger("id");
  2354. }
  2355. CoinWatchlist coinWatchlist = coinMapper.findWatchlistUserBySymbolAndUserId(id, userId);
  2356. String watchlistOtherStr = parseWatchlistOther(id);
  2357. coinWatchlist.setRemark(watchlistOtherStr + MarkdownToHtmlUtils.markdownToHtmlExtensions(coinWatchlist.getRemark()));
  2358. coinWatchlist.setFeixiaohaoUrl("https://www.feixiaohao.com/currencies/" + coinWatchlist.getFeixiaohaoUrl());
  2359. coinWatchlist.setCmcUrl("https://coinmarketcap.com/zh/currencies/" + coinWatchlist.getCmcUrl());
  2360. coinWatchlist.setCoingeckoUrl("https://www.coingecko.com/zh/%E6%95%B0%E5%AD%97%E8%B4%A7%E5%B8%81/" + coinWatchlist.getCoingeckoUrl());
  2361. return coinWatchlist;
  2362. }
  2363. return null;
  2364. }
  2365. private String parseWatchlistOther(String id) {
  2366. CoinWatchlistOther coinWatchlistOther = coinMapper.findWatchlistOtherBySymbol(id);
  2367. StringBuffer sb = new StringBuffer();
  2368. sb.append("<table border=\"1\" cellspacing=\"0\">");
  2369. sb.append("<tr><td>").append("流通市值").append("</td>").append("<td>").append(convertHumanReadable(coinWatchlistOther.getMarketCap())).append("</td></tr>");
  2370. sb.append("<tr><td>").append("流通供应量").append("</td>").append("<td>").append(convertHumanReadable(coinWatchlistOther.getCirculatingSupply())).append("</td></tr>");
  2371. sb.append("<tr><td>").append("流通率").append("</td>").append("<td>").append(StringUtils.isNotEmpty(coinWatchlistOther.getCirculatingRate()) ? new BigDecimal(coinWatchlistOther.getCirculatingRate()).setScale(1, RoundingMode.HALF_UP) + "%" : "--").append("</td></tr>");
  2372. sb.append("<tr><td>").append("总供应量").append("</td>").append("<td>").append(convertHumanReadable(coinWatchlistOther.getTotalSupply())).append("</td></tr>");
  2373. sb.append("<tr><td>").append("最大供应量").append("</td>").append("<td>").append(convertHumanReadable(coinWatchlistOther.getMaxSupply())).append("</td></tr>");
  2374. sb.append("<tr><td>").append("完全稀释的市值").append("</td>").append("<td>").append(convertHumanReadable(coinWatchlistOther.getFullyDilutedMarketCap())).append("</td></tr>");
  2375. String[] coingeckoExchangeNameArr = InitRunner.dicCodeMap.get("coingecko_exchange_names").getCodeValue().split(",");
  2376. List<String> coingeckoExchangeNameList = new ArrayList<>();
  2377. if (StringUtils.isNotEmpty(coinWatchlistOther.getCexSpot()) && coingeckoExchangeNameArr.length == coinWatchlistOther.getCexSpot().length()) {
  2378. for (int i = 0; i < coingeckoExchangeNameArr.length; i++) {
  2379. if ("1".equals(String.valueOf(coinWatchlistOther.getCexSpot().charAt(i)))) {
  2380. coingeckoExchangeNameList.add(coingeckoExchangeNameArr[i]);
  2381. }
  2382. }
  2383. }
  2384. sb.append("<tr><td>").append("现货").append("</td>").append("<td>").append(coingeckoExchangeNameList.size() > 0 ? String.join("、", coingeckoExchangeNameList) : "--").append("</td></tr>");
  2385. String[] coingeckoExchangeFuturesNameArr = InitRunner.dicCodeMap.get("coingecko_exchange_futures_names").getCodeValue().split(",");
  2386. List<String> coingeckoExchangeFuturesNameList = new ArrayList<>();
  2387. if (StringUtils.isNotEmpty(coinWatchlistOther.getCexPerpetual()) && coingeckoExchangeFuturesNameArr.length == coinWatchlistOther.getCexPerpetual().length()) {
  2388. for (int i = 0; i < coingeckoExchangeFuturesNameArr.length; i++) {
  2389. if ("1".equals(String.valueOf(coinWatchlistOther.getCexPerpetual().charAt(i)))) {
  2390. coingeckoExchangeFuturesNameList.add(coingeckoExchangeFuturesNameArr[i]);
  2391. }
  2392. }
  2393. }
  2394. sb.append("<tr><td>").append("合约").append("</td>").append("<td>").append(coingeckoExchangeFuturesNameList.size() > 0 ? String.join("、", coingeckoExchangeFuturesNameList) : "--").append("</td></tr>");
  2395. sb.append("</table>");
  2396. return sb.toString();
  2397. }
  2398. @Override
  2399. public void debugTest() {
  2400. Map<String, Object> params = new HashMap<>();
  2401. params.put("sortField", Collections.singletonList("create_time"));
  2402. params.put("sort", "desc");
  2403. ArrayList symbolList = new ArrayList();
  2404. symbolList.add("PYTH");
  2405. symbolList.add("SNX");
  2406. symbolList.add("REEF");
  2407. //List<CoinWatchlist> watchlistList = coinMapper.findWatchlistList(params);
  2408. // watchlistList = watchlistList.stream().filter(x -> symbolList.contains(x.getSymbol())).collect(Collectors.toList());
  2409. //Map<String, CoinWatchlist> coinWatchlistMap4CoingeckoId = watchlistList.stream().collect(Collectors.toMap(CoinWatchlist::getCoingeckoId, coinWatchlist -> coinWatchlist));
  2410. //parseWatchlistMap4Coingecko(coinWatchlistMap4CoingeckoId);
  2411. //Map<Long, CoinWatchlist> coinWatchlistMap4CmcId = watchlistList.stream().collect(Collectors.toMap(CoinWatchlist::getCmcId, coinWatchlist -> coinWatchlist));
  2412. // parseWatchlistMap4CmC(coinWatchlistMap4CmcId);
  2413. syncCexFlag("PONKE");
  2414. }
  2415. @Override
  2416. @Async("coinTaskExecutor")
  2417. public void initWatchlist(CoinWatchlist coinWatchlist2) {
  2418. Map<String, Object> params = new HashMap<>();
  2419. params.put("symbol", coinWatchlist2.getSymbol());
  2420. params.put("sortField", Collections.singletonList("create_time"));
  2421. params.put("sort", "desc");
  2422. List<CoinWatchlist> watchlistListCKO = coinMapper.findWatchlistList(params);
  2423. Map<String, CoinWatchlist> coinWatchlistMap4CoingeckoId = watchlistListCKO.stream().collect(Collectors.toMap(CoinWatchlist::getCoingeckoId, coinWatchlist -> coinWatchlist));
  2424. parseWatchlistMap4Coingecko(coinWatchlistMap4CoingeckoId);
  2425. List<CoinWatchlist> watchlistListCMC = coinMapper.findWatchlistList(params);
  2426. Map<Long, CoinWatchlist> coinWatchlistMap4CmcId = watchlistListCMC.stream().collect(Collectors.toMap(CoinWatchlist::getCmcId, coinWatchlist -> coinWatchlist));
  2427. parseWatchlistMap4CmC(coinWatchlistMap4CmcId);
  2428. // 初始化cexflag信息
  2429. syncCexFlag(coinWatchlist2.getSymbol());
  2430. }
  2431. public String convertHumanReadable(String number) {
  2432. if (StringUtils.isEmpty(number)) {
  2433. return "--";
  2434. }
  2435. BigDecimal bigDecimal10000 = new BigDecimal("10000");
  2436. BigDecimal divide = new BigDecimal(number).divide(bigDecimal10000, 8, RoundingMode.HALF_UP);
  2437. if (divide.compareTo(bigDecimal10000) <= 0) {
  2438. return divide.setScale(2, RoundingMode.HALF_UP) + "万";
  2439. } else {
  2440. return divide.divide(bigDecimal10000, 2, RoundingMode.HALF_UP) + "亿";
  2441. }
  2442. }
  2443. }