1
zj
2024-07-17 85eb20cf0c8b70b1bf83e952baf13f581841387b
1
9 files modified
10 files added
819 ■■■■■ changed files
.idea/uiDesigner.xml 124 ●●●●● patch | view | raw | blame | history
websocketClient/src/main/resources/application.yml 2 ●●● patch | view | raw | blame | history
websocketSerivce/pom.xml 6 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/common/MarketDataClient.java 62 ●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/common/MexcMarketDataClient.java 43 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/enums/Bourse.java 18 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/pojo/Currency.java 10 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/pojo/bo/CurrencyBitgetBo.java 22 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/pojo/bo/CurrencyGateBo.java 20 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/pojo/bo/CurrencyKucoin.java 22 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/pojo/bo/CurrencyMexcBo.java 22 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/server/CurrencySerivce.java 9 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/server/impl/CurrencySerivceImpl.java 15 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/task/BitgetStock.java 118 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/task/GateStock.java 91 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/task/KucoinStock.java 91 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/task/MexcStock.java 101 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/util/ConverterUtil.java 40 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/resources/application.yml 3 ●●●● patch | view | raw | blame | history
.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: