.idea/uiDesigner.xml
New file @@ -0,0 +1,124 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="Palette2"> <group name="Swing"> <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false"> <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> </item> <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false"> <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> </item> <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false"> <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> </item> <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true"> <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> </item> <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false"> <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> <initial-values> <property name="text" value="Button" /> </initial-values> </item> <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false"> <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> <initial-values> <property name="text" value="RadioButton" /> </initial-values> </item> <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false"> <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> <initial-values> <property name="text" value="CheckBox" /> </initial-values> </item> <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false"> <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> <initial-values> <property name="text" value="Label" /> </initial-values> </item> <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true"> <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> <preferred-size width="150" height="-1" /> </default-constraints> </item> <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true"> <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> <preferred-size width="150" height="-1" /> </default-constraints> </item> <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true"> <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> <preferred-size width="150" height="-1" /> </default-constraints> </item> <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true"> <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> <preferred-size width="150" height="50" /> </default-constraints> </item> <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true"> <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> <preferred-size width="150" height="50" /> </default-constraints> </item> <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true"> <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> <preferred-size width="150" height="50" /> </default-constraints> </item> <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true"> <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> </item> <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false"> <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> <preferred-size width="150" height="50" /> </default-constraints> </item> <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false"> <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> <preferred-size width="150" height="50" /> </default-constraints> </item> <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false"> <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> <preferred-size width="150" height="50" /> </default-constraints> </item> <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false"> <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> <preferred-size width="200" height="200" /> </default-constraints> </item> <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false"> <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> <preferred-size width="200" height="200" /> </default-constraints> </item> <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true"> <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> </item> <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false"> <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> </item> <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false"> <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> </item> <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false"> <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> </item> <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false"> <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> <preferred-size width="-1" height="20" /> </default-constraints> </item> <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false"> <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> </item> <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false"> <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> </item> </group> </component> </project> websocketClient/src/main/resources/application.yml
@@ -1,3 +1,3 @@ server: port: 8002 port: 8090 websocketSerivce/pom.xml
@@ -15,6 +15,7 @@ <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <commons-lang3-version>3.7</commons-lang3-version> </properties> <dependencies> @@ -45,6 +46,11 @@ <version>5.7.12</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons-lang3-version}</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> websocketSerivce/src/main/java/org/example/common/MarketDataClient.java
@@ -1,43 +1,37 @@ package org.example.common; import com.fasterxml.jackson.core.type.TypeReference; import com.google.gson.Gson; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.logging.HttpLoggingInterceptor; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.io.IOException; import java.util.Map; import java.util.concurrent.TimeUnit; /** * @program: demo * @description: * @create: 2024-07-17 16:04 **/ public class MarketDataClient { private static final String REQUEST_HOST = "https://api.mexc.com"; private static final OkHttpClient OK_HTTP_CLIENT = createOkHttpClient(); private static OkHttpClient createOkHttpClient() { HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(); httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); return new OkHttpClient.Builder() .connectTimeout(45, TimeUnit.SECONDS) .readTimeout(45, TimeUnit.SECONDS) .writeTimeout(45, TimeUnit.SECONDS) //.addInterceptor(httpLoggingInterceptor) .build(); } public static <T> T get(String uri, Map<String, String> params, TypeReference<T> ref) { public static String doGet(String apiUrl) { try { Request.Builder builder = new Request.Builder().url(REQUEST_HOST + uri + "?" + SignatureUtil.toQueryString(params)).get(); Response response = OK_HTTP_CLIENT.newCall(builder.build()).execute(); Gson gson = new Gson(); assert response.body() != null; String content = response.body().string(); return gson.fromJson(content, ref.getType()); } catch (IOException e) { throw new RuntimeException(e); URL url = new URL(apiUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String inputLine; StringBuilder response = new StringBuilder(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); return response.toString(); } catch (Exception e) { e.printStackTrace(); } return null; } } websocketSerivce/src/main/java/org/example/common/MexcMarketDataClient.java
New file @@ -0,0 +1,43 @@ package org.example.common; import com.fasterxml.jackson.core.type.TypeReference; import com.google.gson.Gson; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.logging.HttpLoggingInterceptor; import java.io.IOException; import java.util.Map; import java.util.concurrent.TimeUnit; public class MexcMarketDataClient { private static final String REQUEST_HOST = "https://api.mexc.com"; private static final OkHttpClient OK_HTTP_CLIENT = createOkHttpClient(); private static OkHttpClient createOkHttpClient() { HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(); httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); return new OkHttpClient.Builder() .connectTimeout(45, TimeUnit.SECONDS) .readTimeout(45, TimeUnit.SECONDS) .writeTimeout(45, TimeUnit.SECONDS) //.addInterceptor(httpLoggingInterceptor) .build(); } public static <T> T get(String uri, Map<String, String> params, TypeReference<T> ref) { try { Request.Builder builder = new Request.Builder().url(REQUEST_HOST + uri + "?" + SignatureUtil.toQueryString(params)).get(); Response response = OK_HTTP_CLIENT.newCall(builder.build()).execute(); Gson gson = new Gson(); assert response.body() != null; String content = response.body().string(); return gson.fromJson(content, ref.getType()); } catch (IOException e) { throw new RuntimeException(e); } } } websocketSerivce/src/main/java/org/example/enums/Bourse.java
New file @@ -0,0 +1,18 @@ package org.example.enums; public enum Bourse { MEXC("mexc"), GATE("gate"), BITGET("bitget"), KUCOIN("kucoin"); private String code; Bourse(String code) { this.code = code; } } websocketSerivce/src/main/java/org/example/pojo/Currency.java
@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import java.util.Date; /** * @program: demo * @description: 交易对 @@ -16,10 +18,10 @@ //交易对 private String symbol; //交易币 private String base_asset; private String baseAsset; //计价币 private String quote_asset; //修改时间 private String update_time; private String quoteAsset; //来源 private String source; } websocketSerivce/src/main/java/org/example/pojo/bo/CurrencyBitgetBo.java
New file @@ -0,0 +1,22 @@ package org.example.pojo.bo; import lombok.Data; /** * @program: demo * @description:bitget交易对 * @create: 2024-07-17 17:00 **/ @Data public class CurrencyBitgetBo { //交易对 private String symbol; //交易币 private String baseCoin; //计价币 private String quoteCoin; //来源 private String source = "bitget"; } websocketSerivce/src/main/java/org/example/pojo/bo/CurrencyGateBo.java
New file @@ -0,0 +1,20 @@ package org.example.pojo.bo; import lombok.Data; /** * @program: demo * @description: gate交易对 * @create: 2024-07-17 11:50 **/ @Data public class CurrencyGateBo { //交易对名字 private String id; //币名字 private String base; //交易币 private String quote; //来源 private String source = "gate"; } websocketSerivce/src/main/java/org/example/pojo/bo/CurrencyKucoin.java
New file @@ -0,0 +1,22 @@ package org.example.pojo.bo; import lombok.Data; /** * @program: demo * @description: kucoin交易对 * @create: 2024-07-17 17:00 **/ @Data public class CurrencyKucoin { //交易对 private String symbol; //交易币 private String baseCurrency; //计价币 private String quoteCurrency; //来源 private String source = "kucoin"; } websocketSerivce/src/main/java/org/example/pojo/bo/CurrencyMexcBo.java
New file @@ -0,0 +1,22 @@ package org.example.pojo.bo; import lombok.Data; /** * @program: demo * @description: * @create: 2024-07-17 11:53 **/ @Data public class CurrencyMexcBo { //交易对 private String symbol; //交易币 private String baseAsset; //计价币 private String quoteAsset; //来源 private String source = "mexc"; } websocketSerivce/src/main/java/org/example/server/CurrencySerivce.java
New file @@ -0,0 +1,9 @@ package org.example.server; /** * @program: demo * @description: * @create: 2024-07-16 15:23 **/ public interface CurrencySerivce { } websocketSerivce/src/main/java/org/example/server/impl/CurrencySerivceImpl.java
New file @@ -0,0 +1,15 @@ package org.example.server.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.example.dao.CurrencyMapper; import org.example.pojo.Currency; import org.springframework.stereotype.Service; /** * @program: demo * @description: * @create: 2024-07-16 15:23 **/ @Service public class CurrencySerivceImpl extends ServiceImpl<CurrencyMapper, Currency> { } websocketSerivce/src/main/java/org/example/task/BitgetStock.java
@@ -1,9 +1,127 @@ package org.example.task; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Maps; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.example.common.*; import org.example.pojo.Currency; import org.example.pojo.bo.CurrencyBitgetBo; import org.example.server.impl.CurrencySerivceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; /** * @program: demo * @description: bitget交易所 * @create: 2024-07-15 17:24 **/ @Component @Slf4j public class BitgetStock { @Autowired private CurrencySerivceImpl currencyService; private final Lock syncCurrencyLock = new ReentrantLock(); /** * 同步bitget交易所交易对 */ @Scheduled(cron = "0 0/45 * * * ?") public void syncCurrency() { // 使用Lock来确保同步 syncCurrencyLock.lock(); log.info("【同步bitget交易所交易对】---->开始"); try { sync(); } catch (Exception e) { log.error("【同步bitget交易所交易对】出现异常: " + e.getMessage()); e.printStackTrace(); } finally { syncCurrencyLock.unlock(); log.info("【同步bitget交易所交易对】---->结束"); } } public void sync() throws IOException { // 调用外部接口获取数据 String json = doGet(); // 对返回数据格式进行校验 if (json != null && !json.isEmpty()) { ObjectMapper objectMapper = new ObjectMapper(); Map<String, Object> map = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() { }); String symbolsJson = objectMapper.writeValueAsString(map.get("data")); Gson gson = new Gson(); List<CurrencyBitgetBo> getList = gson.fromJson(symbolsJson, new TypeToken<List<CurrencyBitgetBo>>() { }.getType()); getList.parallelStream().forEach(person -> person.setSymbol(StringUtils.remove(person.getSymbol(), "-"))); // 获取数据库中已有的symbol列表 List<Currency> dbList = currencyService.list(); Set<String> symbolSet = dbList.stream().map(Currency::getSymbol).collect(Collectors.toSet()); // 比对接口返回的数据和数据库中已有的数据,找出新增的数据 List<Currency> saveList = getList.stream() .filter(currency -> !symbolSet.contains(currency.getSymbol())) .map(currency -> { Currency newCurrency = new Currency(); newCurrency.setSymbol(currency.getSymbol()); newCurrency.setBaseAsset(currency.getBaseCoin()); newCurrency.setQuoteAsset(currency.getQuoteCoin()); newCurrency.setSource(currency.getSource()); return newCurrency; }) .collect(Collectors.toList()); // 批量保存新增数据到数据库 if (CollectionUtils.isNotEmpty(saveList)) { currencyService.saveBatch(saveList); } } else { log.info("同步bitget交易所交易对,外部接口返回数据为空"); } } public static String doGet() throws IOException { HttpClient httpClient = HttpClients.createDefault(); HttpGet request = new HttpGet("https://api.bitget.com/api/v2/spot/public/symbols"); HttpResponse response = httpClient.execute(request); try { // 处理响应内容 HttpEntity entity = response.getEntity(); String responseBody = EntityUtils.toString(entity); return responseBody; } finally { // 确保释放资源 EntityUtils.consume(response.getEntity()); } } } websocketSerivce/src/main/java/org/example/task/GateStock.java
@@ -1,9 +1,100 @@ package org.example.task; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.example.pojo.Currency; import org.example.pojo.bo.CurrencyGateBo; import org.example.pojo.bo.CurrencyMexcBo; import org.example.server.impl.CurrencySerivceImpl; import org.example.common.MarketDataClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; /** * @program: demo * @description: gate交易所 * @create: 2024-07-15 17:23 **/ @Component @Slf4j public class GateStock { @Autowired private CurrencySerivceImpl currencyService; private final Lock syncCurrencyLock = new ReentrantLock(); /** * 同步gate交易所交易对 */ @Scheduled(cron = "0 0/40 * * * ?") public void syncCurrency() { // 使用Lock来确保同步 syncCurrencyLock.lock(); log.info("【同步gate交易所交易对】---->开始"); try { sync(); } catch (Exception e) { log.error("【同步gate交易所交易对】出现异常: " + e.getMessage()); e.printStackTrace(); } finally { syncCurrencyLock.unlock(); log.info("【同步gate交易所交易对】---->结束"); } } public void sync() throws JsonProcessingException { // 调用外部接口获取数据 String json = MarketDataClient.doGet("https://api.gateio.ws/api/v4/spot/currency_pairs"); // 对返回数据格式进行校验 if (json != null && !json.isEmpty()) { Gson gson = new Gson(); List<CurrencyGateBo> getList = gson.fromJson(json, new TypeToken<List<CurrencyGateBo>>() { }.getType()); getList.parallelStream().forEach(person -> person.setId(StringUtils.remove(person.getId(), "_"))); // 获取数据库中已有的symbol列表 List<Currency> dbList = currencyService.list(); Set<String> symbolSet = dbList.stream().map(Currency::getSymbol).collect(Collectors.toSet()); // 比对接口返回的数据和数据库中已有的数据,找出新增的数据 List<Currency> saveList = getList.stream() .filter(currency -> !symbolSet.contains(currency.getId())) .map(currency -> { Currency newCurrency = new Currency(); newCurrency.setSymbol(currency.getId()); newCurrency.setBaseAsset(currency.getBase()); newCurrency.setQuoteAsset(currency.getQuote()); newCurrency.setSource(currency.getSource()); return newCurrency; }) .collect(Collectors.toList()); // 批量保存新增数据到数据库 if(CollectionUtils.isNotEmpty(saveList)){ currencyService.saveBatch(saveList); } } else { log.info("外部接口返回数据为空"); } } } websocketSerivce/src/main/java/org/example/task/KucoinStock.java
@@ -1,9 +1,100 @@ package org.example.task; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.example.common.MarketDataClient; import org.example.pojo.Currency; import org.example.pojo.bo.CurrencyKucoin; import org.example.server.impl.CurrencySerivceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; /** * @program: demo * @description: kucoin交易所 * @create: 2024-07-15 17:23 **/ @Component @Slf4j public class KucoinStock { @Autowired private CurrencySerivceImpl currencyService; private final Lock syncCurrencyLock = new ReentrantLock(); /** * 同步kucoin交易所交易对 */ @Scheduled(cron = "0 0/35 * * * ?") public void syncCurrency() { // 使用Lock来确保同步 syncCurrencyLock.lock(); log.info("【同步kucoin交易所交易对】---->开始"); try { sync(); } catch (Exception e) { log.error("【同步kucoin交易所交易对】出现异常: " + e.getMessage()); e.printStackTrace(); } finally { syncCurrencyLock.unlock(); log.info("【同步kucoin交易所交易对】---->结束"); } } public void sync() throws JsonProcessingException { // 调用外部接口获取数据 String json = MarketDataClient.doGet("https://api.kucoin.com/api/v2/symbols"); // 对返回数据格式进行校验 if (json != null && !json.isEmpty()) { ObjectMapper objectMapper = new ObjectMapper(); Map<String, Object> map = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {}); String symbolsJson = objectMapper.writeValueAsString(map.get("data")); Gson gson = new Gson(); List<CurrencyKucoin> getList = gson.fromJson(symbolsJson, new TypeToken<List<CurrencyKucoin>>() { }.getType()); getList.parallelStream().forEach(person -> person.setSymbol(StringUtils.remove(person.getSymbol(), "-"))); // 获取数据库中已有的symbol列表 List<Currency> dbList = currencyService.list(); Set<String> symbolSet = dbList.stream().map(Currency::getSymbol).collect(Collectors.toSet()); // 比对接口返回的数据和数据库中已有的数据,找出新增的数据 List<Currency> saveList = getList.stream() .filter(currency -> !symbolSet.contains(currency.getSymbol())) .map(currency -> { Currency newCurrency = new Currency(); newCurrency.setSymbol(currency.getSymbol()); newCurrency.setBaseAsset(currency.getBaseCurrency()); newCurrency.setQuoteAsset(currency.getQuoteCurrency()); newCurrency.setSource(currency.getSource()); return newCurrency; }) .collect(Collectors.toList()); // 批量保存新增数据到数据库 if(CollectionUtils.isNotEmpty(saveList)){ currencyService.saveBatch(saveList); } } else { log.info("外部接口返回数据为空"); } } } websocketSerivce/src/main/java/org/example/task/MexcStock.java
@@ -1,75 +1,98 @@ package org.example.task; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org.example.common.MarketDataClient; import org.example.common.MexcMarketDataClient; import org.example.pojo.Currency; import org.example.pojo.ExchangeInfo; import org.example.util.JsonUtil; import org.example.pojo.bo.CurrencyMexcBo; import org.example.server.impl.CurrencySerivceImpl; import org.example.util.ConverterUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import static com.alibaba.druid.sql.ast.SQLPartitionValue.Operator.List; import java.util.stream.Collectors; /** * @program: demo * @description: mexc交易所 * @create: 2024-07-15 17:22 * @create: 2024-07-15 17:22 **/ @Component @Slf4j public class MexcStock { private final AtomicBoolean syncCurrency = new AtomicBoolean(false); @Autowired private CurrencySerivceImpl currencyService; private final Lock syncCurrencyLock = new ReentrantLock(); /** * 同步mexc交易所交易对 */ @Scheduled(cron = "0 0/5 * * * ?") @Scheduled(cron = "0 0/30 * * * ?") public void syncCurrency() { if (syncCurrency.get()) { return; } if (syncCurrencyLock.tryLock()) { System.out.println("【同步mexc交易所交易对】---->开市"); try { syncCurrency.set(true); sync(); } catch (Exception e) { System.err.println("【同步mexc交易所交易对】出现异常: " + e.getMessage()); } finally { syncCurrencyLock.unlock(); syncCurrency.set(false); System.out.println("【同步mexc交易所交易对】---->结束"); } // 使用Lock来确保同步 syncCurrencyLock.lock(); log.info("【同步mexc交易所交易对】---->开始"); try { sync(); } catch (Exception e) { log.error("【同步mexc交易所交易对】出现异常: " + e.getMessage()); e.printStackTrace(); } finally { syncCurrencyLock.unlock(); log.info("【同步mexc交易所交易对】---->结束"); } } public void sync() { HashMap<String, String> symbolParams = Maps.newHashMap(ImmutableMap.<String, String>builder() .build()); String json = JsonUtil.toJson(exchangeInfo(symbolParams)); Gson gson = new Gson(); Map map = gson.fromJson(json, Map.class); String symbols = JsonUtil.toJson(map.get("symbols")); ArrayList arrayList = gson.fromJson(symbols, ArrayList.class); public void sync() throws JsonProcessingException { Map<String, String> symbolParams = new HashMap<>(); ObjectMapper objectMapper = new ObjectMapper(); // 调用外部接口获取数据 ExchangeInfo exchangeInfo = exchangeInfo(symbolParams); String json = objectMapper.writeValueAsString(exchangeInfo); // 对返回数据格式进行校验 if (json != null && !json.isEmpty()) { Map<String, Object> map = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() { }); // 获取接口返回的symbols数据 String symbolsJson = objectMapper.writeValueAsString(map.get("symbols")); Gson gson = new Gson(); List<CurrencyMexcBo> getList = gson.fromJson(symbolsJson, new TypeToken<List<CurrencyMexcBo>>() { }.getType()); // 获取数据库中已有的symbol列表 List<Currency> dbList = currencyService.list(); Set<String> symbolSet = dbList.stream().map(Currency::getSymbol).collect(Collectors.toSet()); // 比对接口返回的数据和数据库中已有的数据,找出新增的数据 List<CurrencyMexcBo> saveList = getList.stream() .filter(CurrencyMexcBo -> !symbolSet.contains(CurrencyMexcBo.getSymbol())) .collect(Collectors.toList()); List<Currency> currencies = ConverterUtil.convertToList(saveList, Currency.class); // 批量保存新增数据到数据库 if(CollectionUtils.isNotEmpty(currencies)){ currencyService.saveBatch(currencies); } } else { log.info("外部接口返回数据为空"); } } public static ExchangeInfo exchangeInfo(Map<String, String> params) { return MarketDataClient.get("/api/v3/exchangeInfo", params, new TypeReference<ExchangeInfo>() { return MexcMarketDataClient.get("/api/v3/exchangeInfo", params, new TypeReference<ExchangeInfo>() { }); } } } websocketSerivce/src/main/java/org/example/util/ConverterUtil.java
New file @@ -0,0 +1,40 @@ package org.example.util; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; public class ConverterUtil { public static <T, V> V convert(T pojo, Class<V> voClass) { try { V vo = voClass.newInstance(); Field[] pojoFields = pojo.getClass().getDeclaredFields(); Field[] voFields = voClass.getDeclaredFields(); for (Field pojoField : pojoFields) { pojoField.setAccessible(true); for (Field voField : voFields) { voField.setAccessible(true); if (pojoField.getName().equals(voField.getName()) && pojoField.getType().equals(voField.getType())) { voField.set(vo, pojoField.get(pojo)); break; } } } return vo; } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); return null; } } public static <T, V> List<V> convertToList(List<T> pojoList, Class<V> voClass) { List<V> voList = new ArrayList<>(); for (T pojo : pojoList) { V vo = convert(pojo, voClass); voList.add(vo); } return voList; } } websocketSerivce/src/main/resources/application.yml
@@ -1,6 +1,5 @@ server: # 服务器的HTTP端口,默认为8090 port: 8001 port: 8091 spring: datasource: