CoinServiceImpl.java 154 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830
  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. // tags
  1728. bookmarkInfo.setTags(StringUtils.isEmpty(bookmarkInfo.getTags()) ? "--" : bookmarkInfo.getTags());
  1729. // sourceUrl
  1730. if (StringUtils.isNotEmpty(bookmarkInfo.getSourceUrl())) {
  1731. bookmarkInfo.setSourceUrl("<a target=\"_blank\" href=\" " + bookmarkInfo.getSourceUrl() + "\">" + "goGO" + "</a>");
  1732. } else {
  1733. bookmarkInfo.setSourceUrl("--");
  1734. }
  1735. // 备注
  1736. String remark;
  1737. if (StringUtils.isNotEmpty(bookmarkInfo.getRemark())) {
  1738. if (bookmarkInfo.getRemark().length() > 20) {
  1739. remark = bookmarkInfo.getRemark().substring(0, 20) + "...";
  1740. } else {
  1741. remark = bookmarkInfo.getRemark();
  1742. }
  1743. } else {
  1744. remark = "--";
  1745. }
  1746. bookmarkInfo.setRemark("<span class=\"primary\" title=\"" + bookmarkInfo.getRemark() + "\" avid=\"" + bookmarkInfo.getId() + " \" >" + remark + " </span>");
  1747. }
  1748. }
  1749. private void renderMainSearch4CurrencyHolding(List<CoinCurrencyHolding> currentHoldingList) {
  1750. for (CoinCurrencyHolding coinCurrencyHolding : currentHoldingList) {
  1751. // 名称
  1752. coinCurrencyHolding.setSymbolStyle(" style=\"background-color:rgba(70,169,244,.72);font-weight: bold;\"");
  1753. // 当前价格
  1754. coinCurrencyHolding.setCurrentPriceStyle(" style=\"color:#252B31;background-color:#C4ADE9;\"");
  1755. if (StringUtils.isNotEmpty(coinCurrencyHolding.getCurrentPrice())) {
  1756. coinCurrencyHolding.setCurrentPrice(new BigDecimal(coinCurrencyHolding.getCurrentPrice()).divide(BigDecimal.ONE, new MathContext(3)).toPlainString());
  1757. }
  1758. // 入场价格
  1759. if (StringUtils.isNotEmpty(coinCurrencyHolding.getBuyPrice())) {
  1760. coinCurrencyHolding.setBuyPrice(new BigDecimal(coinCurrencyHolding.getBuyPrice()).divide(BigDecimal.ONE, new MathContext(3)).toPlainString());
  1761. }
  1762. coinCurrencyHolding.setStatusStr(coinCurrencyHolding.getStatus() == 1 ? "正常" : "失效");
  1763. // 涨跌幅比例
  1764. if (coinCurrencyHolding.getChangePercentage() == null) {
  1765. } else if (coinCurrencyHolding.getChangePercentage().compareTo(BigDecimal.ONE) < 0) {
  1766. coinCurrencyHolding.setChangePercentageStyle(" style=\"color:#000000;background-color:#f1a8a4;\"");
  1767. } else {
  1768. coinCurrencyHolding.setChangePercentageStyle(" style=\"color:#000000;background-color:#aad6f5;\"");
  1769. }
  1770. }
  1771. }
  1772. private void renderMainSearch4CmcMap(List<CoinCmcMap> cmcMapList) {
  1773. for (CoinCmcMap coinCmcMap : cmcMapList) {
  1774. String platform = coinCmcMap.getPlatform();
  1775. if (StringUtils.isNotEmpty(platform)) {
  1776. JSONObject jsonObject = JSONObject.parseObject(platform);
  1777. jsonObject.remove("token_address");
  1778. coinCmcMap.setPlatform(jsonObject.toJSONString());
  1779. }
  1780. }
  1781. }
  1782. private void renderMainSearch4Image(List<FileImage> fileImageList) {
  1783. // String ftpBaseurl = InitRunner.dicCodeMap.get("ftp_baseurl").getCodeValue();
  1784. for (FileImage fileImage : fileImageList) {
  1785. // String newPath = "<a target=\"_blank\" href=\" " + ftpBaseurl + fileImage.getPath() + "\">" + fileImage.getNewName() + "</a>";
  1786. // fileImage.setNewName(newPath);
  1787. // fileImage.setOldName("<span class=\"primary\" avid=\"" + fileImage.getId() + " \" >" + fileImage.getOldName() + " </span>");
  1788. fileImage.setRemark("<span class=\"primary\" avid=\"" + fileImage.getId() + " \" >" + fileImage.getRemark() + " </span>");
  1789. }
  1790. }
  1791. private void renderMainSearch4Music(List<FileMusicCollection> fileMusicCollectionList) {
  1792. List<JSONObject> fileMusicCategoryList = coinApiConfigMapper.findFileMusicCategoryList();
  1793. Map<String, String> fileMusicCategoryMap = fileMusicCategoryList.stream().collect(Collectors.toMap(e -> e.getString("id"), e -> e.getString("categoryName")));
  1794. String ftpBasePath = InitRunner.dicCodeMap.get("ftp_music_basepath").getCodeValue();
  1795. String ftpBaseUrl = InitRunner.dicCodeMap.get("ftp_baseurl").getCodeValue();
  1796. String[] scoreTitleArr = {"很差", "较差", "还行", "推荐", "力荐"};
  1797. for (FileMusicCollection fileMusicCollection : fileMusicCollectionList) {
  1798. String categoryName = Arrays.stream(fileMusicCollection.getCategoryId().split(",")).map(original -> fileMusicCategoryMap.getOrDefault(original, original)).collect(Collectors.joining(","));
  1799. fileMusicCollection.setCategoryName(categoryName);
  1800. String remark = StringUtils.isNotEmpty(fileMusicCollection.getRemark()) && fileMusicCollection.getRemark().length() > 20 ? (fileMusicCollection.getRemark().substring(0, 20) + "...") : fileMusicCollection.getRemark();
  1801. fileMusicCollection.setRemark("<span class=\"primary\" avid=\"" + fileMusicCollection.getId() + " \" >" + remark + " </span>");
  1802. if (StringUtils.isNotEmpty(fileMusicCollection.getLowQualityUrl())) {
  1803. fileMusicCollection.setLowQualityUrl(ftpBaseUrl + ftpBasePath + fileMusicCollection.getLowQualityUrl());
  1804. }
  1805. if (StringUtils.isNotEmpty(fileMusicCollection.getHighQualityUrl())) {
  1806. fileMusicCollection.setHighQualityUrl(ftpBaseUrl + ftpBasePath + fileMusicCollection.getHighQualityUrl());
  1807. }
  1808. // 优先级
  1809. Integer score = Integer.valueOf(fileMusicCollection.getScore());
  1810. score = score > 5 ? 5 : score;
  1811. StringBuffer scoreSB = new StringBuffer("<ul class=\"rating\">");
  1812. for (int i = 0; i < scoreTitleArr.length; i++) {
  1813. String style = (i + 1) <= score ? "fa-star" : "fa-star-o";
  1814. scoreSB.append("<li id=\"" +fileMusicCollection.getId() + "\" title=\"" + scoreTitleArr[i] + "\" val=\"" + (i + 1) + "\"><i class=\"fa " + style + "\"></i></li>");
  1815. }
  1816. scoreSB.append("</ul>");
  1817. fileMusicCollection.setScore(scoreSB.toString());
  1818. }
  1819. }
  1820. private void renderMainSearch4Watchlist(List<CoinWatchlist> watchlistList, Integer userId) {
  1821. BigDecimal bigDecimal10000 = new BigDecimal("10000");
  1822. List<String> popularTrackCategoryList = Arrays.asList("DePIN", "AI", "RWA", "大饼生态", "以太Layer-2", "Restaking再质押", "NFT|链游|元宇宙", "WEB3社交");
  1823. // 赛道分类预处理
  1824. List<String> trackCategoryList = coinApiConfigMapper.findTrackCategoryListByUserId(userId).stream().filter(StringUtils::isNotEmpty).collect(Collectors.toList());
  1825. List<String> trackCategory2List = coinApiConfigMapper.findTrackCategory2ListByUserId(userId).stream().filter(StringUtils::isNotEmpty).collect(Collectors.toList());
  1826. Map<String, String> trackCategoryMap = new HashMap<>();
  1827. Map<String, String> trackCategory2Map = new HashMap<>();
  1828. List<String> colorList = coinApiConfigMapper.findColorStyleList();
  1829. int j = 0;
  1830. for (int i = 0; i < trackCategoryList.size(); i++) {
  1831. if (j > colorList.size() - 1) {
  1832. j = 0;
  1833. }
  1834. String popularTrackCategoryStyle = popularTrackCategoryList.contains(trackCategoryList.get(i)) ? "font-weight:bold;" : "";
  1835. trackCategoryMap.put(trackCategoryList.get(i), colorList.get(j) + popularTrackCategoryStyle);
  1836. j++;
  1837. }
  1838. j = 0;
  1839. for (int i = 0; i < trackCategory2List.size(); i++) {
  1840. if (j > colorList.size() - 1) {
  1841. j = 0;
  1842. }
  1843. String popularTrackCategoryStyle = popularTrackCategoryList.contains(trackCategory2List.get(i)) ? "font-weight:bold;" : "";
  1844. trackCategory2Map.put(trackCategory2List.get(i), colorList.get(j) + popularTrackCategoryStyle);
  1845. j++;
  1846. }
  1847. String[] scoreTitleArr = {"很差", "较差", "还行", "推荐", "力荐"};
  1848. String regex = "[+-]?\\d*\\.?\\d*[eE][+-]?\\d+"; // 科学计数法正则表达式
  1849. for (CoinWatchlist coinWatchlist : watchlistList) {
  1850. // 优先级
  1851. Integer score = Integer.valueOf(coinWatchlist.getScore());
  1852. score = score > 5 ? 5 : score;
  1853. StringBuffer scoreSB = new StringBuffer("<ul class=\"rating\">");
  1854. for (int i = 0; i < scoreTitleArr.length; i++) {
  1855. String style = (i + 1) <= score ? "fa-star" : "fa-star-o";
  1856. scoreSB.append("<li id=\"" + coinWatchlist.getSymbol() + "\" title=\"" + scoreTitleArr[i] + "\" val=\"" + (i + 1) + "\"><i class=\"fa " + style + "\"></i></li>");
  1857. }
  1858. scoreSB.append("</ul>");
  1859. coinWatchlist.setScore(scoreSB.toString());
  1860. // 流通市值
  1861. if (null != coinWatchlist.getTotalMarketValue()) {
  1862. BigDecimal divide = coinWatchlist.getTotalMarketValue().divide(bigDecimal10000, 8, RoundingMode.HALF_UP);
  1863. if (divide.compareTo(bigDecimal10000) <= 0) {
  1864. coinWatchlist.setTotalMarketValueStr(divide.setScale(2, RoundingMode.HALF_UP) + "万");
  1865. } else {
  1866. divide = divide.divide(bigDecimal10000, 2, RoundingMode.HALF_UP);
  1867. coinWatchlist.setTotalMarketValueStr(divide + "亿");
  1868. }
  1869. coinWatchlist.setTotalMarketValueStr("<span class=\"primary\" avid=\"" + coinWatchlist.getSymbol() + "\" >" + coinWatchlist.getTotalMarketValueStr() + " </span>");
  1870. }
  1871. // 赛道分类
  1872. String[] trackCategoryArr = coinWatchlist.getTrackCategory().split(",");
  1873. StringBuffer sb = new StringBuffer();
  1874. String trackCategoryLength = "";
  1875. for (int i = 0; i < trackCategoryArr.length; i++) {
  1876. if (i == trackCategoryArr.length - 1) {
  1877. if (trackCategoryArr.length > 1 && trackCategoryLength.length() > 10) {
  1878. sb.append("<br>");
  1879. }
  1880. sb.append("<span class=\"selected-value\" style=\"" + trackCategoryMap.get(trackCategoryArr[i]) + " \" >" + trackCategoryArr[i] + " </span>");
  1881. } else {
  1882. trackCategoryLength += trackCategoryArr[i];
  1883. sb.append("<span class=\"selected-value\" style=\"margin-right:0.3em;" + trackCategoryMap.get(trackCategoryArr[i]) + " \" >" + trackCategoryArr[i] + " </span>");
  1884. }
  1885. }
  1886. coinWatchlist.setTrackCategoryStyle(" style=\"padding:0em 0.3em;\"");
  1887. coinWatchlist.setTrackCategory(sb.toString());
  1888. String[] trackCategory2Arr = coinWatchlist.getTrackCategory2().split(",");
  1889. StringBuffer sb2 = new StringBuffer();
  1890. for (int i = 0; i < trackCategory2Arr.length; i++) {
  1891. sb2.append("<span class=\"selected-value\" style=\"margin-right:0.3em;" + trackCategory2Map.get(trackCategory2Arr[i]) + " \" >" + trackCategory2Arr[i] + " </span>");
  1892. if (i % 2 == 1 && i != trackCategory2Arr.length - 1) {
  1893. sb2.append("<br>");
  1894. }
  1895. }
  1896. coinWatchlist.setTrackCategory2Style(" style=\"padding:0em 0.3em;\"");
  1897. coinWatchlist.setTrackCategory2(sb2.toString());
  1898. // 名称
  1899. coinWatchlist.setSymbolStyle(" style=\"background-color:rgba(70,169,244,.72);font-weight: bold;\"");
  1900. // 市场价格
  1901. coinWatchlist.setMarkPriceStyle(" style=\"color:#252B31;background-color:#C4ADE9;\"");
  1902. if (StringUtils.isNotEmpty(coinWatchlist.getMarkPrice())) {
  1903. if (!coinWatchlist.getMarkPrice().matches(regex)) {
  1904. //coinWatchlist.setMarkPrice(new BigDecimal(coinWatchlist.getMarkPrice()).divide(BigDecimal.ONE, new MathContext(3)).toPlainString());
  1905. coinWatchlist.setMarkPrice(formatNumber(coinWatchlist.getMarkPrice()));
  1906. }
  1907. }
  1908. if (StringUtils.isNotEmpty(coinWatchlist.getHighestHistoricalPrice())) {
  1909. if (!coinWatchlist.getHighestHistoricalPrice().matches(regex)) {
  1910. //coinWatchlist.setHighestHistoricalPrice(new BigDecimal(coinWatchlist.getHighestHistoricalPrice()).divide(BigDecimal.ONE, new MathContext(3)).toPlainString());
  1911. coinWatchlist.setHighestHistoricalPrice(formatNumber(coinWatchlist.getHighestHistoricalPrice()));
  1912. }
  1913. }
  1914. if (StringUtils.isNotEmpty(coinWatchlist.getLowestHistoricalPrice())) {
  1915. if (!coinWatchlist.getLowestHistoricalPrice().matches(regex)) {
  1916. //coinWatchlist.setLowestHistoricalPrice(new BigDecimal(coinWatchlist.getLowestHistoricalPrice()).divide(BigDecimal.ONE, new MathContext(3)).toPlainString());
  1917. coinWatchlist.setLowestHistoricalPrice(formatNumber(coinWatchlist.getLowestHistoricalPrice()));
  1918. }
  1919. }
  1920. // 24小时价格变化
  1921. if (coinWatchlist.getPriceChangePercentage24h() == null) {
  1922. } else if (coinWatchlist.getPriceChangePercentage24h().compareTo(BigDecimal.ZERO) < 0) {
  1923. coinWatchlist.setPriceChangePercentage24hStyle(" style=\"color:#000000;background-color:#f1a8a4;\"");
  1924. } else {
  1925. coinWatchlist.setPriceChangePercentage24hStyle(" style=\"color:#000000;background-color:#aad6f5;\"");
  1926. }
  1927. /*// 历史最高点涨幅比例
  1928. if (coinWatchlist.getAthChangePercentage() == null) {
  1929. } else if (coinWatchlist.getAthChangePercentage().compareTo(BigDecimal.ZERO) < 0) {
  1930. coinWatchlist.setAthChangePercentageStyle(" style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1931. } else {
  1932. coinWatchlist.setAthChangePercentageStyle(" style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1933. }
  1934. // 历史最低点涨幅比例
  1935. if (coinWatchlist.getAtlChangePercentage() == null) {
  1936. } else if (coinWatchlist.getAtlChangePercentage().compareTo(BigDecimal.ZERO) < 0) {
  1937. coinWatchlist.setAtlChangePercentageStyle(" style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1938. } else {
  1939. coinWatchlist.setAtlChangePercentageStyle(" style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1940. }*/
  1941. // 涨幅倍数2
  1942. if (StringUtils.isNotEmpty(coinWatchlist.getHighestHistoricalPrice()) && StringUtils.isNotEmpty(coinWatchlist.getMarkPrice())) {
  1943. BigDecimal increaseMultiple = new BigDecimal(coinWatchlist.getHighestHistoricalPrice()).divide(new BigDecimal(coinWatchlist.getMarkPrice()), 1, RoundingMode.HALF_UP);
  1944. coinWatchlist.setIncreaseMultiple2(increaseMultiple.toPlainString());
  1945. if (increaseMultiple.compareTo(new BigDecimal("50")) >= 0) {
  1946. coinWatchlist.setIncreaseMultiple2Style(" style=\"color:#000000;background-color:#5a964e;\"");
  1947. } else if (increaseMultiple.compareTo(new BigDecimal("10")) >= 0) {
  1948. coinWatchlist.setIncreaseMultiple2Style(" style=\"color:#000000;background-color:#9cc494;\"");
  1949. } else {
  1950. coinWatchlist.setIncreaseMultiple2Style(" style=\"color:#000000;background-color:#dae8d7;\"");
  1951. }
  1952. }
  1953. }
  1954. }
  1955. private static String formatNumber(String str) {
  1956. double number = Double.parseDouble(str);
  1957. int dotIndex = str.indexOf('.');
  1958. if (dotIndex == -1) {
  1959. return str; // 无小数点,直接返回
  1960. }
  1961. // 检查小数点后连续0的数量
  1962. int zeroCount = 0;
  1963. for (int i = dotIndex + 1; i < str.length(); i++) {
  1964. if (str.charAt(i) == '0') {
  1965. zeroCount++;
  1966. } else {
  1967. break; // 遇到非0字符停止计数
  1968. }
  1969. }
  1970. // 如果0的数量 >=5,用科学计数法;否则用普通小数
  1971. if (zeroCount >= 5) {
  1972. DecimalFormat df = new DecimalFormat("0.###E0"); // 科学计数法
  1973. return df.format(number).replace("E-0", "E-"); // 修正E-09为E-9
  1974. } else {
  1975. return new BigDecimal(str).divide(BigDecimal.ONE, new MathContext(3)).stripTrailingZeros().toPlainString();
  1976. }
  1977. }
  1978. private void renderMainSearch4TraderList(List<CoinTrader> mixTraderList) {
  1979. for (CoinTrader mixTrader : mixTraderList) {
  1980. mixTrader.setLastTradeTime(DateUtils.longToString(Long.valueOf(mixTrader.getLastTradeTime())));
  1981. }
  1982. }
  1983. /**
  1984. * 渲染获取当前计划委托(止盈止损)列表
  1985. *
  1986. * @param result
  1987. */
  1988. private void renderMainSearch4CurrentPlan(JSONArray result, Integer chaRateSort) {
  1989. forkJoinPool3.submit(() -> result.parallelStream().forEach(e -> {
  1990. JSONObject jsonObject = (JSONObject) e;
  1991. // 币对名称
  1992. String symbol = jsonObject.getString("symbol");
  1993. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol.replace("USDT_UMCBL", "") + "</font></strong>USDT_UMCBL");
  1994. // 订单状态
  1995. jsonObject.put("status", InitRunner.publicParamsMap.get("status").getString(jsonObject.getString("status")));
  1996. // 交易类型
  1997. jsonObject.put("orderType", InitRunner.publicParamsMap.get("orderType").getString(jsonObject.getString("orderType")));
  1998. // 订单类型
  1999. jsonObject.put("planType", InitRunner.publicParamsMap.get("planType").getString(jsonObject.getString("planType")));
  2000. // 开单方向
  2001. String side = jsonObject.getString("side");
  2002. jsonObject.put("side", InitRunner.publicParamsMap.get("side").getString(side));
  2003. if (side.equals("open_long")) {
  2004. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2005. } else if (side.equals("open_short")) {
  2006. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2007. } else {
  2008. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\"");
  2009. }
  2010. // 触发类型
  2011. jsonObject.put("triggerType", InitRunner.publicParamsMap.get("triggerType").getString(jsonObject.getString("triggerType")));
  2012. jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime")));
  2013. jsonObject.put("uTime", StringUtils.isEmpty(jsonObject.getString("uTime")) ? "--" : DateUtils.longToString(jsonObject.getLong("uTime")));
  2014. // 获取合约标记价格
  2015. String requestUrl = mainUrl + "/api/mix/v1/market/mark-price?symbol=" + symbol;
  2016. try {
  2017. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  2018. String markPrice = JSONObject.parseObject(response.body()).getJSONObject("data").getString("markPrice");
  2019. BigDecimal chaRate = BigDecimal.ZERO;
  2020. BigDecimal triggerPriceDecimal = new BigDecimal(jsonObject.getString("triggerPrice"));
  2021. BigDecimal markPriceDecimal = new BigDecimal(markPrice);
  2022. if (markPriceDecimal.compareTo(triggerPriceDecimal) < 0) {
  2023. chaRate = markPriceDecimal.divide(triggerPriceDecimal, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  2024. } else if (markPriceDecimal.compareTo(triggerPriceDecimal) > 0) {
  2025. chaRate = triggerPriceDecimal.divide(markPriceDecimal, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  2026. }
  2027. jsonObject.put("markPrice", markPrice);
  2028. jsonObject.put("markPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  2029. jsonObject.put("chaRate", chaRate);
  2030. jsonObject.put("chaRateStyle", " style=\"color:#FFFFFF;background-color:#5EA294;\"");
  2031. } catch (Exception ex) {
  2032. throw new RuntimeException(ex);
  2033. }
  2034. })).join();
  2035. if (chaRateSort != 0) {
  2036. Collections.sort(result, (o1, o2) -> chaRateSort * (((JSONObject) o1).getBigDecimal("chaRate").compareTo(((JSONObject) o2).getBigDecimal("chaRate"))));
  2037. }
  2038. }
  2039. /**
  2040. * 渲染监控币种列表
  2041. *
  2042. * @param monitorCurrencyList
  2043. */
  2044. private JSONArray renderMainSearch4MonitorCurrency(Map<String, JSONArray> resultMulti, List<CoinMonitorCurrency> monitorCurrencyList, Integer changeUtcSort) {
  2045. Map<String, CoinMonitorCurrency> monitorCurrencyMap4Mix = monitorCurrencyList.stream().filter(e -> e.getType().equals("2")).collect(Collectors.toMap(CoinMonitorCurrency::getSymbol, Function.identity(), (key1, key2) -> key1));
  2046. Set<String> symbolSet4Mix = monitorCurrencyMap4Mix.keySet();
  2047. Map<String, CoinMonitorCurrency> monitorCurrencyMap4Spot = monitorCurrencyList.stream().filter(e -> e.getType().contains("1")).collect(Collectors.toMap(CoinMonitorCurrency::getSymbol, Function.identity(), (key1, key2) -> key1));
  2048. Set<String> symbolSet4Spot = monitorCurrencyMap4Spot.keySet();
  2049. JSONArray array4Spot = resultMulti.get("/api/spot/v1/market/tickers").stream()
  2050. .filter(iter -> symbolSet4Spot.contains(((JSONObject) iter).getString("symbol")))
  2051. .collect(Collectors.toCollection(JSONArray::new));
  2052. JSONArray array4Mix = resultMulti.get("/api/mix/v1/market/tickers?productType=umcbl").stream()
  2053. .filter(iter -> symbolSet4Mix.contains(((JSONObject) iter).getString("symbol")))
  2054. .collect(Collectors.toCollection(JSONArray::new));
  2055. forkJoinPool.submit(() -> array4Spot.parallelStream().forEach(e -> {
  2056. JSONObject jsonObject = (JSONObject) e;
  2057. jsonObject.put("changeUtc", jsonObject.getBigDecimal("changeUtc").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  2058. jsonObject.put("change", jsonObject.getBigDecimal("change").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  2059. jsonObject.put("ts", DateUtils.longToString(jsonObject.getLong("ts")));
  2060. jsonObject.put("category", monitorCurrencyMap4Spot.get(jsonObject.getString("symbol")).getCategory());
  2061. // UTC0时涨跌幅
  2062. if (jsonObject.getBigDecimal("changeUtc").compareTo(BigDecimal.ZERO) < 0) {
  2063. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2064. } else {
  2065. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2066. }
  2067. // 24小时涨跌幅
  2068. if (jsonObject.getBigDecimal("change").compareTo(BigDecimal.ZERO) < 0) {
  2069. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2070. } else {
  2071. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2072. }
  2073. // 币对名称
  2074. String symbol = jsonObject.getString("symbol").replace("USDT", "");
  2075. if ("BTC".equals(symbol) || "ETH".equals(symbol)) {
  2076. jsonObject.put("symbol", "<strong style=\"background-color:#FF6EB4;\"><font color=\"#242A30\">" + symbol + "</font></strong>USDT");
  2077. } else {
  2078. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol + "</font></strong>USDT");
  2079. }
  2080. // 标记价格
  2081. jsonObject.put("closeStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  2082. // 基础币量 计价币量 usdt币量
  2083. jsonObject.put("baseVol", readableFileSize(jsonObject.getDouble("baseVol")));
  2084. jsonObject.put("quoteVol", readableFileSize(jsonObject.getDouble("quoteVol")));
  2085. jsonObject.put("usdtVol", readableFileSize(jsonObject.getDouble("usdtVol")));
  2086. })).join();
  2087. forkJoinPool.submit(() -> array4Mix.parallelStream().forEach(e -> {
  2088. JSONObject jsonObject = (JSONObject) e;
  2089. jsonObject.put("changeUtc", jsonObject.getBigDecimal("chgUtc").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  2090. jsonObject.put("change", jsonObject.getBigDecimal("priceChangePercent").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  2091. jsonObject.put("ts", DateUtils.longToString(jsonObject.getLong("timestamp")));
  2092. jsonObject.put("category", monitorCurrencyMap4Mix.get(jsonObject.getString("symbol")).getCategory());
  2093. // UTC0时涨跌幅
  2094. if (jsonObject.getBigDecimal("changeUtc").compareTo(BigDecimal.ZERO) < 0) {
  2095. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2096. } else {
  2097. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2098. }
  2099. // 24小时涨跌幅
  2100. if (jsonObject.getBigDecimal("change").compareTo(BigDecimal.ZERO) < 0) {
  2101. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2102. } else {
  2103. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2104. }
  2105. // 币对名称
  2106. String symbol = jsonObject.getString("symbol").replace("USDT_UMCBL", "");
  2107. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol + "</font></strong>USDT_UMCBL");
  2108. // 标记价格
  2109. jsonObject.put("close", jsonObject.getString("last"));
  2110. jsonObject.put("closeStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  2111. // 基础币量 计价币量 usdt币量
  2112. jsonObject.put("baseVol", readableFileSize(jsonObject.getDouble("baseVolume")));
  2113. jsonObject.put("quoteVol", readableFileSize(jsonObject.getDouble("quoteVolume")));
  2114. jsonObject.put("usdtVol", readableFileSize(jsonObject.getDouble("quoteVolume")));
  2115. // 其他字段兼容
  2116. jsonObject.put("openUtc0", jsonObject.getString("openUtc"));
  2117. jsonObject.put("buyOne", jsonObject.getString("bestBid"));
  2118. jsonObject.put("sellOne", jsonObject.getString("bestAsk"));
  2119. })).join();
  2120. array4Spot.addAll(array4Mix);
  2121. if (changeUtcSort != 0) {
  2122. Collections.sort(array4Spot, (o1, o2) -> changeUtcSort * (((JSONObject) o1).getBigDecimal("changeUtc").compareTo(((JSONObject) o2).getBigDecimal("changeUtc"))));
  2123. }
  2124. return array4Spot;
  2125. }
  2126. /**
  2127. * 渲染获取全部历史委托
  2128. *
  2129. * @param historyOrderList
  2130. */
  2131. private void renderMainSearch4OrderHistoryProductType(List<CoinHistoryOrder> historyOrderList) {
  2132. for (CoinHistoryOrder coinHistoryOrder : historyOrderList) {
  2133. // 币种名称
  2134. coinHistoryOrder.setSymbol(coinHistoryOrder.getSymbol().replace("USDT_UMCBL", ""));
  2135. // 订单状态
  2136. coinHistoryOrder.setState(InitRunner.publicParamsMap.get("state").getString(coinHistoryOrder.getState()));
  2137. // 开单方向
  2138. coinHistoryOrder.setSide(InitRunner.publicParamsMap.get("side").getString(coinHistoryOrder.getSide()));
  2139. // 总盈亏
  2140. String TotalProfits = "0E-8";
  2141. if (!coinHistoryOrder.getTotalProfits().contains("0E-8")) {
  2142. TotalProfits = new BigDecimal(coinHistoryOrder.getTotalProfits()).setScale(2, RoundingMode.HALF_UP).toPlainString();
  2143. }
  2144. coinHistoryOrder.setTotalProfits(TotalProfits);
  2145. // 手续费
  2146. String fee = "0E-8";
  2147. if (!coinHistoryOrder.getFee().contains("0E-8")) {
  2148. fee = new BigDecimal(coinHistoryOrder.getFee()).setScale(2, RoundingMode.HALF_UP).toPlainString();
  2149. }
  2150. coinHistoryOrder.setFee(fee);
  2151. // 持仓方向
  2152. coinHistoryOrder.setPosSide(InitRunner.publicParamsMap.get("posSide").getString(coinHistoryOrder.getPosSide()));
  2153. // 仓位模式
  2154. coinHistoryOrder.setMarginMode(InitRunner.publicParamsMap.get("marginMode").getString(coinHistoryOrder.getMarginMode()));
  2155. // 交易类型
  2156. coinHistoryOrder.setOrderType(InitRunner.publicParamsMap.get("orderType").getString(coinHistoryOrder.getOrderType()));
  2157. // 交易方向
  2158. coinHistoryOrder.setTradeSide(InitRunner.publicParamsMap.get("tradeSide").getString(coinHistoryOrder.getTradeSide()));
  2159. // 持仓模式
  2160. coinHistoryOrder.setHoldMode(InitRunner.publicParamsMap.get("holdMode").getString(coinHistoryOrder.getHoldMode()));
  2161. // orderSource
  2162. coinHistoryOrder.setOrderSource(InitRunner.publicParamsMap.get("orderSource").getString(coinHistoryOrder.getOrderSource()));
  2163. coinHistoryOrder.setCTime(DateUtils.longToString(Long.valueOf(coinHistoryOrder.getCTime())));
  2164. coinHistoryOrder.setUTime(DateUtils.longToString(Long.valueOf(coinHistoryOrder.getUTime())));
  2165. }
  2166. }
  2167. /**
  2168. * 渲染获取全部当前委托
  2169. *
  2170. * @param result
  2171. */
  2172. private void renderMainSearch4OrderMarginCoinCurrent(JSONArray result, Integer chaRateSort) {
  2173. forkJoinPool2.submit(() -> result.parallelStream().forEach(e -> {
  2174. JSONObject jsonObject = (JSONObject) e;
  2175. // 币对名称
  2176. String symbol = jsonObject.getString("symbol");
  2177. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol.replace("USDT_UMCBL", "") + "</font></strong>USDT_UMCBL");
  2178. // 订单状态
  2179. jsonObject.put("state", InitRunner.publicParamsMap.get("state").getString(jsonObject.getString("state")));
  2180. // 开单方向
  2181. String side = jsonObject.getString("side");
  2182. jsonObject.put("side", InitRunner.publicParamsMap.get("side").getString(side));
  2183. if (side.equals("open_long")) {
  2184. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2185. } else if (side.equals("open_short")) {
  2186. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2187. } else {
  2188. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\"");
  2189. }
  2190. // 交易类型
  2191. jsonObject.put("orderType", InitRunner.publicParamsMap.get("orderType").getString(jsonObject.getString("orderType")));
  2192. // 止盈止损
  2193. jsonObject.put("presetTakeProfitPrice", StringUtils.isEmpty(jsonObject.getString("presetTakeProfitPrice")) ? "--" : jsonObject.getString("presetTakeProfitPrice"));
  2194. jsonObject.put("presetStopLossPrice", StringUtils.isEmpty(jsonObject.getString("presetTakeProfitPrice")) ? "--" : jsonObject.getString("presetTakeProfitPrice"));
  2195. // 持仓模式
  2196. jsonObject.put("holdMode", InitRunner.publicParamsMap.get("holdMode").getString(jsonObject.getString("holdMode")));
  2197. // orderSource
  2198. jsonObject.put("orderSource", InitRunner.publicParamsMap.get("orderSource").getString(jsonObject.getString("orderSource")));
  2199. // 仓位模式
  2200. jsonObject.put("marginMode", InitRunner.publicParamsMap.get("marginMode").getString(jsonObject.getString("marginMode")));
  2201. jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime")));
  2202. jsonObject.put("uTime", DateUtils.longToString(jsonObject.getLong("uTime")));
  2203. // 获取合约标记价格
  2204. String requestUrl = mainUrl + "/api/mix/v1/market/mark-price?symbol=" + symbol;
  2205. try {
  2206. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  2207. String markPrice = JSONObject.parseObject(response.body()).getJSONObject("data").getString("markPrice");
  2208. BigDecimal chaRate = BigDecimal.ZERO;
  2209. if ("open_short".equals(side)) {
  2210. chaRate = new BigDecimal(markPrice).divide(new BigDecimal(jsonObject.getString("price")), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  2211. } else if ("open_long".equals(side)) {
  2212. chaRate = new BigDecimal(jsonObject.getString("price")).divide(new BigDecimal(markPrice), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  2213. }
  2214. jsonObject.put("markPrice", markPrice);
  2215. jsonObject.put("markPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  2216. jsonObject.put("chaRate", chaRate);
  2217. jsonObject.put("chaRateStyle", " style=\"color:#FFFFFF;background-color:#5EA294;\"");
  2218. } catch (Exception ex) {
  2219. throw new RuntimeException(ex);
  2220. }
  2221. })).join();
  2222. if (chaRateSort != 0) {
  2223. Collections.sort(result, (o1, o2) -> chaRateSort * (((JSONObject) o1).getBigDecimal("chaRate").compareTo(((JSONObject) o2).getBigDecimal("chaRate"))));
  2224. }
  2225. }
  2226. /**
  2227. * 渲染获取全部合约仓位信息V2
  2228. *
  2229. * @param result
  2230. */
  2231. private void renderMainSearch4AllPositionv2(JSONArray result, Integer unrealizedPLSort) {
  2232. forkJoinPool4.submit(() -> result.parallelStream().forEach(e -> {
  2233. JSONObject jsonObject = (JSONObject) e;
  2234. // 币对名称
  2235. String symbol = jsonObject.getString("symbol");
  2236. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol.replace("USDT_UMCBL", "") + "</font></strong>USDT_UMCBL");
  2237. // 持仓方向
  2238. String holdSide = jsonObject.getString("holdSide");
  2239. jsonObject.put("holdSide", InitRunner.publicParamsMap.get("holdSide").getString(holdSide));
  2240. if (holdSide.equals("long")) {
  2241. jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2242. } else if (holdSide.equals("short")) {
  2243. jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2244. } else {
  2245. jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\"");
  2246. }
  2247. // 保证金模式
  2248. jsonObject.put("marginMode", InitRunner.publicParamsMap.get("marginMode").getString(jsonObject.getString("marginMode")));
  2249. // 持仓模式
  2250. jsonObject.put("holdMode", InitRunner.publicParamsMap.get("holdMode").getString(jsonObject.getString("holdMode")));
  2251. // 最近更新时间 保证金数量 (保证金币种) 平均开仓价 未实现盈亏 预估强平价
  2252. jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime")));
  2253. jsonObject.put("margin", new BigDecimal(jsonObject.getString("margin")).setScale(4, RoundingMode.HALF_UP));
  2254. jsonObject.put("averageOpenPrice", new BigDecimal(jsonObject.getString("averageOpenPrice")).divide(BigDecimal.ONE, new MathContext(4)));
  2255. jsonObject.put("unrealizedPL", new BigDecimal(jsonObject.getString("unrealizedPL")).setScale(4, RoundingMode.HALF_UP));
  2256. jsonObject.put("liquidationPrice", new BigDecimal(jsonObject.getString("liquidationPrice")).divide(BigDecimal.ONE, new MathContext(4)));
  2257. // 未实现盈亏
  2258. if (jsonObject.getBigDecimal("unrealizedPL").compareTo(BigDecimal.ZERO) < 0) {
  2259. jsonObject.put("unrealizedPLStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2260. } else {
  2261. jsonObject.put("unrealizedPLStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2262. }
  2263. // 回报率=未实现盈亏/保证金
  2264. BigDecimal returnRate = jsonObject.getBigDecimal("unrealizedPL").divide(jsonObject.getBigDecimal("margin"), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  2265. jsonObject.put("returnRate", returnRate);
  2266. if (returnRate.compareTo(BigDecimal.ZERO) < 0) {
  2267. jsonObject.put("returnRateStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2268. } else {
  2269. jsonObject.put("returnRateStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2270. }
  2271. // 获取当前资金费率
  2272. String requestUrl = mainUrl + "/api/mix/v1/market/current-fundRate?symbol=" + symbol;
  2273. try {
  2274. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  2275. String fundingRate = JSONObject.parseObject(response.body()).getJSONObject("data").getString("fundingRate");
  2276. if (new BigDecimal(fundingRate).compareTo(BigDecimal.ZERO) < 0) {
  2277. jsonObject.put("fundingRateStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  2278. } else {
  2279. jsonObject.put("fundingRateStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  2280. }
  2281. jsonObject.put("fundingRate", new BigDecimal(fundingRate).multiply(BigDecimal.valueOf(100)).setScale(4, RoundingMode.HALF_UP).toPlainString() + "%");
  2282. } catch (Exception ex) {
  2283. throw new RuntimeException(ex);
  2284. }
  2285. // 标记价格
  2286. jsonObject.put("marketPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  2287. })).join();
  2288. if (unrealizedPLSort != 0) {
  2289. Collections.sort(result, (o1, o2) -> unrealizedPLSort * (((JSONObject) o1).getBigDecimal("unrealizedPL").compareTo(((JSONObject) o2).getBigDecimal("unrealizedPL"))));
  2290. }
  2291. }
  2292. /**
  2293. * Java实现字节转换,可以自动转换为B、KB、MB、GB、TB
  2294. *
  2295. * @param size
  2296. * @return
  2297. */
  2298. private String readableFileSize(double size) {
  2299. if (size <= 0) {
  2300. return "0";
  2301. }
  2302. final String[] units = new String[]{"B", "K", "M", "G", "T"};
  2303. int digitGroups = (int) (Math.log10(size) / Math.log10(1000));
  2304. return df1.format(size / Math.pow(1000, digitGroups)) + units[digitGroups];
  2305. }
  2306. public String getMonitorJobStatus(String jobName) {
  2307. Map<String, JSONObject> monitorJobConfig = getMonitorJobConfig();
  2308. if (null != monitorJobConfig && monitorJobConfig.containsKey(jobName)) {
  2309. return monitorJobConfig.get(jobName).getString("job_status");
  2310. }
  2311. return null;
  2312. }
  2313. @Override
  2314. public Map<String, JSONObject> getMonitorJobConfig() {
  2315. String cacheKey = "coin:monitor:job:list";
  2316. // 1. 缓存有,直接返回
  2317. if (redisUtils.hasKey(cacheKey) && redisUtils.get(cacheKey) != null) {
  2318. return (Map<String, JSONObject>) redisUtils.get(cacheKey);
  2319. }
  2320. // 加锁防止同时对一个数据发送多次请求
  2321. RLock lock = redissonClient.getLock("lock:" + cacheKey);
  2322. try {
  2323. // 2. 尝试加分布式锁,最多等待30秒,上锁以后60秒自动解锁
  2324. boolean lockFlag = lock.tryLock(30, 60, TimeUnit.SECONDS);
  2325. if (lockFlag) {
  2326. // 3. 加锁成功,二次检查,缓存有,直接返回
  2327. if (redisUtils.hasKey(cacheKey) && redisUtils.get(cacheKey) != null) {
  2328. return (Map<String, JSONObject>) redisUtils.get(cacheKey);
  2329. }
  2330. // 4. 查数据库,并且按给定的时长加到缓存中
  2331. Map<String, JSONObject> monitorJobConfigMap = coinMapper.findMonitorJobConfig();
  2332. // 缓存监控任务配置信息
  2333. if (monitorJobConfigMap != null) {
  2334. redisUtils.set(cacheKey, monitorJobConfigMap, 60, TimeUnit.MINUTES);
  2335. }
  2336. return monitorJobConfigMap;
  2337. } else {
  2338. log.error("getMonitorJobConfig 加锁失败 error,lockFlag: false");
  2339. return null;
  2340. }
  2341. } catch (Exception e) {
  2342. log.error("getMonitorJobConfig Exception", e);
  2343. return InitRunner.monitorJobConfigMap;
  2344. } finally {
  2345. if (lock.isLocked() && lock.isHeldByCurrentThread()) {
  2346. lock.unlock();
  2347. }
  2348. }
  2349. }
  2350. @Override
  2351. public Object mainSearchDetail(String userName, String nameEn, String id) {
  2352. if ("image".equals(nameEn)) {
  2353. FileImage fileImage = pictureInfoMapper.findFileImageById(Long.valueOf(id));
  2354. String ftpBaseurl = InitRunner.dicCodeMap.get("ftp_baseurl").getCodeValue();
  2355. String ftpBasePath = InitRunner.dicCodeMap.get("ftp_basepath").getCodeValue();
  2356. String ftpThumbnailBasePath = InitRunner.dicCodeMap.get("ftp_thumbnail_basepath").getCodeValue();
  2357. String path = fileImage.getPath();
  2358. fileImage.setPath(ftpBaseurl + ftpBasePath + path);
  2359. fileImage.setThumbnailPath(ftpBaseurl + ftpThumbnailBasePath + path);
  2360. return fileImage;
  2361. } else if ("watchlist".equals(nameEn)) {
  2362. Integer userId;
  2363. if (StringUtils.isEmpty(userName)) {
  2364. throw new ParameterException("userName为空!");
  2365. } else {
  2366. JSONObject coinUser = coinMapper.findUserByUsername(userName);
  2367. if (coinUser == null) {
  2368. throw new ParameterException("用户不存在!");
  2369. }
  2370. userId = coinUser.getInteger("id");
  2371. }
  2372. CoinWatchlist coinWatchlist = coinMapper.findWatchlistUserBySymbolAndUserId(id, userId);
  2373. String watchlistOtherStr = parseWatchlistOther(id);
  2374. coinWatchlist.setRemark(watchlistOtherStr + MarkdownToHtmlUtils.markdownToHtmlExtensions(coinWatchlist.getRemark()));
  2375. coinWatchlist.setFeixiaohaoUrl("https://www.feixiaohao.com/currencies/" + coinWatchlist.getFeixiaohaoUrl());
  2376. coinWatchlist.setCmcUrl("https://coinmarketcap.com/zh/currencies/" + coinWatchlist.getCmcUrl());
  2377. coinWatchlist.setCoingeckoUrl("https://www.coingecko.com/zh/%E6%95%B0%E5%AD%97%E8%B4%A7%E5%B8%81/" + coinWatchlist.getCoingeckoUrl());
  2378. return coinWatchlist;
  2379. }
  2380. return null;
  2381. }
  2382. private String parseWatchlistOther(String id) {
  2383. CoinWatchlistOther coinWatchlistOther = coinMapper.findWatchlistOtherBySymbol(id);
  2384. StringBuffer sb = new StringBuffer();
  2385. sb.append("<table border=\"1\" cellspacing=\"0\">");
  2386. sb.append("<tr><td>").append("流通市值").append("</td>").append("<td>").append(convertHumanReadable(coinWatchlistOther.getMarketCap())).append("</td></tr>");
  2387. sb.append("<tr><td>").append("流通供应量").append("</td>").append("<td>").append(convertHumanReadable(coinWatchlistOther.getCirculatingSupply())).append("</td></tr>");
  2388. 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>");
  2389. sb.append("<tr><td>").append("总供应量").append("</td>").append("<td>").append(convertHumanReadable(coinWatchlistOther.getTotalSupply())).append("</td></tr>");
  2390. sb.append("<tr><td>").append("最大供应量").append("</td>").append("<td>").append(convertHumanReadable(coinWatchlistOther.getMaxSupply())).append("</td></tr>");
  2391. sb.append("<tr><td>").append("完全稀释的市值").append("</td>").append("<td>").append(convertHumanReadable(coinWatchlistOther.getFullyDilutedMarketCap())).append("</td></tr>");
  2392. String[] coingeckoExchangeNameArr = InitRunner.dicCodeMap.get("coingecko_exchange_names").getCodeValue().split(",");
  2393. List<String> coingeckoExchangeNameList = new ArrayList<>();
  2394. if (StringUtils.isNotEmpty(coinWatchlistOther.getCexSpot()) && coingeckoExchangeNameArr.length == coinWatchlistOther.getCexSpot().length()) {
  2395. for (int i = 0; i < coingeckoExchangeNameArr.length; i++) {
  2396. if ("1".equals(String.valueOf(coinWatchlistOther.getCexSpot().charAt(i)))) {
  2397. coingeckoExchangeNameList.add(coingeckoExchangeNameArr[i]);
  2398. }
  2399. }
  2400. }
  2401. sb.append("<tr><td>").append("现货").append("</td>").append("<td>").append(coingeckoExchangeNameList.size() > 0 ? String.join("、", coingeckoExchangeNameList) : "--").append("</td></tr>");
  2402. String[] coingeckoExchangeFuturesNameArr = InitRunner.dicCodeMap.get("coingecko_exchange_futures_names").getCodeValue().split(",");
  2403. List<String> coingeckoExchangeFuturesNameList = new ArrayList<>();
  2404. if (StringUtils.isNotEmpty(coinWatchlistOther.getCexPerpetual()) && coingeckoExchangeFuturesNameArr.length == coinWatchlistOther.getCexPerpetual().length()) {
  2405. for (int i = 0; i < coingeckoExchangeFuturesNameArr.length; i++) {
  2406. if ("1".equals(String.valueOf(coinWatchlistOther.getCexPerpetual().charAt(i)))) {
  2407. coingeckoExchangeFuturesNameList.add(coingeckoExchangeFuturesNameArr[i]);
  2408. }
  2409. }
  2410. }
  2411. sb.append("<tr><td>").append("合约").append("</td>").append("<td>").append(coingeckoExchangeFuturesNameList.size() > 0 ? String.join("、", coingeckoExchangeFuturesNameList) : "--").append("</td></tr>");
  2412. sb.append("</table>");
  2413. return sb.toString();
  2414. }
  2415. @Override
  2416. public void debugTest() {
  2417. Map<String, Object> params = new HashMap<>();
  2418. params.put("sortField", Collections.singletonList("create_time"));
  2419. params.put("sort", "desc");
  2420. ArrayList symbolList = new ArrayList();
  2421. symbolList.add("PYTH");
  2422. symbolList.add("SNX");
  2423. symbolList.add("REEF");
  2424. //List<CoinWatchlist> watchlistList = coinMapper.findWatchlistList(params);
  2425. // watchlistList = watchlistList.stream().filter(x -> symbolList.contains(x.getSymbol())).collect(Collectors.toList());
  2426. //Map<String, CoinWatchlist> coinWatchlistMap4CoingeckoId = watchlistList.stream().collect(Collectors.toMap(CoinWatchlist::getCoingeckoId, coinWatchlist -> coinWatchlist));
  2427. //parseWatchlistMap4Coingecko(coinWatchlistMap4CoingeckoId);
  2428. //Map<Long, CoinWatchlist> coinWatchlistMap4CmcId = watchlistList.stream().collect(Collectors.toMap(CoinWatchlist::getCmcId, coinWatchlist -> coinWatchlist));
  2429. // parseWatchlistMap4CmC(coinWatchlistMap4CmcId);
  2430. syncCexFlag("PONKE");
  2431. }
  2432. @Override
  2433. @Async("coinTaskExecutor")
  2434. public void initWatchlist(CoinWatchlist coinWatchlist2) {
  2435. Map<String, Object> params = new HashMap<>();
  2436. params.put("symbol", coinWatchlist2.getSymbol());
  2437. params.put("sortField", Collections.singletonList("create_time"));
  2438. params.put("sort", "desc");
  2439. List<CoinWatchlist> watchlistListCKO = coinMapper.findWatchlistList(params);
  2440. Map<String, CoinWatchlist> coinWatchlistMap4CoingeckoId = watchlistListCKO.stream().collect(Collectors.toMap(CoinWatchlist::getCoingeckoId, coinWatchlist -> coinWatchlist));
  2441. parseWatchlistMap4Coingecko(coinWatchlistMap4CoingeckoId);
  2442. List<CoinWatchlist> watchlistListCMC = coinMapper.findWatchlistList(params);
  2443. Map<Long, CoinWatchlist> coinWatchlistMap4CmcId = watchlistListCMC.stream().collect(Collectors.toMap(CoinWatchlist::getCmcId, coinWatchlist -> coinWatchlist));
  2444. parseWatchlistMap4CmC(coinWatchlistMap4CmcId);
  2445. // 初始化cexflag信息
  2446. syncCexFlag(coinWatchlist2.getSymbol());
  2447. }
  2448. public String convertHumanReadable(String number) {
  2449. if (StringUtils.isEmpty(number)) {
  2450. return "--";
  2451. }
  2452. BigDecimal bigDecimal10000 = new BigDecimal("10000");
  2453. BigDecimal divide = new BigDecimal(number).divide(bigDecimal10000, 8, RoundingMode.HALF_UP);
  2454. if (divide.compareTo(bigDecimal10000) <= 0) {
  2455. return divide.setScale(2, RoundingMode.HALF_UP) + "万";
  2456. } else {
  2457. return divide.divide(bigDecimal10000, 2, RoundingMode.HALF_UP) + "亿";
  2458. }
  2459. }
  2460. }