1
zyy3
2025-11-05 91bbc719126310f09031e55967c059c0a01f20b1
1
7 files modified
210 ■■■■ changed files
trading-order-admin/src/main/java/com/yami/trading/admin/task/StockTask.java 85 ●●●● patch | view | raw | blame | history
trading-order-admin/src/main/java/com/yami/trading/api/controller/KlineController.java 16 ●●●● patch | view | raw | blame | history
trading-order-huobi/src/main/java/com/yami/trading/huobi/data/internal/KlineService.java 7 ●●●● patch | view | raw | blame | history
trading-order-huobi/src/main/java/com/yami/trading/huobi/data/internal/KlineServiceImpl.java 52 ●●●●● patch | view | raw | blame | history
trading-order-huobi/src/main/java/com/yami/trading/huobi/jsws/WebSocketClientBeanConfig.java 13 ●●●●● patch | view | raw | blame | history
trading-order-huobi/src/main/java/com/yami/trading/huobi/jsws/WebsocketRunClient.java 19 ●●●●● patch | view | raw | blame | history
trading-order-service/src/main/java/com/yami/trading/service/item/ItemService.java 18 ●●●●● patch | view | raw | blame | history
trading-order-admin/src/main/java/com/yami/trading/admin/task/StockTask.java
@@ -79,6 +79,8 @@
        }
    }
    /**
     * 加载所有股票数据
     */
@@ -111,55 +113,54 @@
                    .eq("type", Item.indices));
            log.info("同步股票 已有数据 {} 获取数据 {}", stockList.size(), list.size());
            System.out.println(stockList);
            List<Item> updateStockList = new ArrayList<>();
            for (StockRealTimeBean o : list) {
                //System.out.println(o);
                Item indices = indicesList.stream()
                        .filter(x -> x.getSymbol().equals(o.getSymbol()))
                        .findFirst()
                        .orElse(null);
                if (indices != null) {  //指数不添加
                    continue;
                }
                Item item = stockList.stream()
                        .filter(x -> x.getSymbol().equals(o.getSymbol()) &&
                                x.getStockCode().equals(o.getId()))
                        .findFirst()
                        .orElse(null);
                if (item != null) {  //已有不添加
                    continue;
                }
                item = indicesList.stream()
                        .filter(x -> x.getSymbol().equals(o.getSymbol()))
                        .findFirst()
                        .orElse(null);
                if (item != null) {  //已有不添加
                    continue;
                }
                if (item == null) {
                    item = new Item();
                    String name = StringUtils.trim(o.getName());
                    item.setEnName(name);
                    item.setName(name);
                    item.setSymbolFullName(name);
                    item.setSymbol(o.getSymbol());
                    item.setSymbolData(o.getSymbol());
                    item.setPips(BigDecimal.valueOf(0.01).doubleValue());
                    item.setPipsAmount(BigDecimal.valueOf(0.02).doubleValue());
                    item.setAdjustmentValue(BigDecimal.ZERO.doubleValue());
                    item.setUnitAmount(BigDecimal.valueOf(1000).doubleValue());
                    item.setUnitFee(BigDecimal.valueOf(30).doubleValue());
                    item.setMarket("FOREVER");
                    item.setDecimals(2);
                    item.setMultiple(BigDecimal.ZERO.doubleValue());
                    item.setBorrowingRate(BigDecimal.ZERO.doubleValue());
                    item.setDelFlag(0);
                    item.setType(Item.US_STOCKS);
                    item.setCategory(Item.US_STOCKS);
                    item.setSorted("100");
                    item.setOpenCloseType(Item.US_STOCKS);
                    item.setFake("0");
                    item.setShowStatus("1");
                    item.setTradeStatus("1");
                    item.setQuoteCurrency("USDT");
                    item.setCrawlStatus("default_active");
                    item.setStockCode(o.getId());
                item = new Item();
                String name = StringUtils.trim(o.getName());
                item.setEnName(name);
                item.setName(name);
                item.setSymbolFullName(name);
                item.setSymbol(o.getSymbol());
                item.setSymbolData(o.getSymbol());
                item.setPips(BigDecimal.valueOf(0.01).doubleValue());
                item.setPipsAmount(BigDecimal.valueOf(0.02).doubleValue());
                item.setAdjustmentValue(BigDecimal.ZERO.doubleValue());
                item.setUnitAmount(BigDecimal.valueOf(1000).doubleValue());
                item.setUnitFee(BigDecimal.valueOf(30).doubleValue());
                item.setMarket("FOREVER");
                item.setDecimals(2);
                item.setMultiple(BigDecimal.ZERO.doubleValue());
                item.setBorrowingRate(BigDecimal.ZERO.doubleValue());
                item.setDelFlag(0);
                item.setType(Item.US_STOCKS);
                item.setCategory(Item.US_STOCKS);
                item.setSorted("100");
                item.setOpenCloseType(Item.US_STOCKS);
                item.setFake("0");
                item.setShowStatus("1");
                item.setTradeStatus("1");
                item.setQuoteCurrency("USDT");
                item.setCrawlStatus("default_active");
                item.setStockCode(o.getId());
                updateStockList.add(item);
                    updateStockList.add(item);
                }
                Realtime realtime = new Realtime();
@@ -191,6 +192,10 @@
        }
    }
    public static void main(String[] args) {
        StockTask task = new StockTask();
        task.loadAllStock(EStockType.US);
trading-order-admin/src/main/java/com/yami/trading/api/controller/KlineController.java
@@ -8,6 +8,7 @@
import com.yami.trading.common.web.ResultObject;
import com.yami.trading.huobi.constants.KlinePeriodEnum;
import com.yami.trading.huobi.data.TimeZoneConverterService;
import com.yami.trading.huobi.data.internal.KlineService;
import com.yami.trading.huobi.tradingview.service.TradingViewService;
import com.yami.trading.service.data.DataService;
import com.yami.trading.service.item.ItemService;
@@ -50,6 +51,9 @@
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    KlineService klineService;
    @ApiOperation(value = "行情")
    @GetMapping("/api/hobi!getKline.action")
@@ -62,13 +66,21 @@
            // Fetch item details based on symbol
            Item bySymbol = itemService.findBySymbol(symbol);
            List<Kline> data;
            // Handle non-cryptos separately
            if (!Item.cryptos.equals(bySymbol.getType())) {
                return getKlineForNonCryptos(bySymbol, line, symbol);
                if (bySymbol.getType().equalsIgnoreCase(Item.US_STOCKS)) {
                    data = klineService.getKData(bySymbol, line);
                    formatKlineTimestamps(data, line);
                    return Result.succeed(this.build(data, line, symbol));
                } else {
                    return getKlineForNonCryptos(bySymbol, line, symbol);
                }
            }
            data = this.dataService.kline(symbol, line);
            // Fetch Kline data from service (for cryptos)
            List<Kline> data = this.dataService.kline(symbol, line);
            // Return an empty response if no data is found
            if (Objects.isNull(data)) {
trading-order-huobi/src/main/java/com/yami/trading/huobi/data/internal/KlineService.java
@@ -2,6 +2,7 @@
import com.yami.trading.bean.data.domain.Kline;
import com.yami.trading.bean.data.domain.Realtime;
import com.yami.trading.bean.item.domain.Item;
import java.util.List;
import java.util.Map;
@@ -115,4 +116,8 @@
    public List<Kline> calculateKline(String symbol, int seq, String period, List<Kline> klineList) ;
    }
    List<Kline> getKData(Item item, String interval);
}
trading-order-huobi/src/main/java/com/yami/trading/huobi/data/internal/KlineServiceImpl.java
@@ -1,13 +1,19 @@
package com.yami.trading.huobi.data.internal;
import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.yami.trading.bean.data.domain.Kline;
import com.yami.trading.bean.data.domain.Realtime;
import com.yami.trading.bean.item.domain.Item;
import com.yami.trading.common.config.RequestDataHelper;
import com.yami.trading.common.constants.Constants;
import com.yami.trading.common.domain.Result;
import com.yami.trading.common.exception.YamiShopBindException;
import com.yami.trading.common.util.DateUtils;
import com.yami.trading.huobi.websocket.constant.enums.EStockType;
import com.yami.trading.service.MarketOpenChecker;
import com.yami.trading.common.util.ThreadUtils;
import com.yami.trading.common.util.UTCDateUtils;
@@ -16,6 +22,7 @@
import com.yami.trading.service.data.RealtimeService;
import com.yami.trading.service.item.ItemService;
import com.yami.trading.service.syspara.SysparaService;
import lombok.Data;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -952,4 +959,49 @@
        }
        return result;
    }
    @Data
    class kData {
        long t;
        String c;
        String o;
        String h;
        String l;
        String v;
        String vo;
    }
    @Override
    public List<Kline> getKData(Item item, String interval) {
        try {
            EStockType eStockType = EStockType.US;
            Object object = HttpUtil.get(eStockType.stockUrl + "kline?pid=" + item.getStockCode() + "&interval=" + interval + "&key=" + eStockType.stockKey);
            Gson gson = new Gson();
            List<kData> dataList = gson.fromJson(object.toString(), new TypeToken<List<kData>>(){}.getType());
            logger.info("{}获取{}条K线" , item.getSymbol() ,dataList.size());
            Realtime realtime = DataCache.getRealtime(item.getSymbol());
            // 修改 List 中的最后一条数据
            kData lastData = dataList.get(dataList.size() - 1);
            lastData.setC(String.valueOf(realtime.getClose()));
            lastData.setO(String.valueOf(realtime.getOpen()));
            lastData.setH(String.valueOf(realtime.getHigh()));
            lastData.setL(String.valueOf(realtime.getLow()));
            List<Kline> list = new ArrayList<>();
            for (int i = 0; i < dataList.size(); i++) {
                kData kData = dataList.get(i);
                Kline kline = new Kline();
                kline.setTs(Long.valueOf(kData.getT() + "000"));
                kline.setClose(Double.valueOf(kData.getC()));
                kline.setOpen(Double.valueOf(kData.getO()));
                kline.setHigh(Double.valueOf(kData.getH()));
                kline.setLow(Double.valueOf(kData.getL()));
                list.add(kline);
            }
            return list;
        } catch (Exception e) {
            logger.error("getKData error", e);
        }
        return null;
    }
}
trading-order-huobi/src/main/java/com/yami/trading/huobi/jsws/WebSocketClientBeanConfig.java
@@ -4,6 +4,8 @@
import com.yami.trading.huobi.websocket.constant.enums.EStockType;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@@ -15,13 +17,22 @@
@Component
public class WebSocketClientBeanConfig {
    // 注入Spring上下文,用于获取原型Bean
    @Autowired
    private ApplicationContext applicationContext;
    @Bean
    public Map<String, WebSocketClient> websocketRunClientMap() {
        Map<String, WebSocketClient> retMap = new HashMap<>(1);
        try {
            WebsocketRunClient websocketRunClient = new WebsocketRunClient(new URI(EStockType.US.getWsUrl()), EStockType.US);
            // 关键:通过Spring上下文获取WebsocketRunClient实例(而非直接new)
            // 传入参数创建实例(需配合原型作用域)
            WebsocketRunClient websocketRunClient = applicationContext.getBean(
                    WebsocketRunClient.class,
                    new URI(EStockType.US.getWsUrl()),  // 第一个参数:serverUri
                    EStockType.US                       // 第二个参数:eStockType
            );
            websocketRunClient.connect();
            websocketRunClient.setConnectionLostTimeout(0);
            startHeartbeatThread(websocketRunClient);
trading-order-huobi/src/main/java/com/yami/trading/huobi/jsws/WebsocketRunClient.java
@@ -12,6 +12,8 @@
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.lang.reflect.Type;
import java.math.BigDecimal;
@@ -20,6 +22,8 @@
import java.util.*;
@Slf4j
@Component
@Scope("prototype")
public class WebsocketRunClient extends WebSocketClient {
    private EStockType eStockType;
@@ -27,16 +31,20 @@
    @Autowired
    ItemService itemService;
    public WebsocketRunClient() {
        super(URI.create("wss://ws.jinmanxuan.com/websocket-server"));
    }
    public WebsocketRunClient(URI serverUri, EStockType eStockType) {
        // 修改为新的WebSocket服务器地址
        super(URI.create("wss://usws.yanshiz.com/websocket-server"));
        super(URI.create("wss://ws.jinmanxuan.com/websocket-server"));
        this.eStockType = eStockType;
    }
    @Override
    public void onOpen(ServerHandshake serverHandshake) {
        log.info("WebSocket连接已建立,连接到: wss://usws.yanshiz.com/websocket-server");
        log.info("WebSocket连接已建立,连接到: wss://ws.jinmanxuan.com/websocket-server");
        // 发送身份验证消息
        send(("key:"+ eStockType.getStockKey()+":"+eStockType.getContryId()).getBytes());
        Timer heartbeatTimer;
@@ -58,10 +66,9 @@
        if(!s.equals("pong") && !s.equals("身份验证成功")){
            try {
                Map<String, String> stringObjectMap = jsonToMap(s);
                String symbol = stringObjectMap.get("symbol");
                Item item = itemService.findBySymbol(symbol);
                if (!item.getType().equalsIgnoreCase(Item.US_STOCKS)) {
                String symbol = stringObjectMap.get("symbol").trim();
                Item item = itemService.findCaCheBySymbol(symbol);
                if (item == null || !item.getType().equalsIgnoreCase(Item.US_STOCKS)) {
                    return;
                }
                Realtime realtime = new Realtime();
trading-order-service/src/main/java/com/yami/trading/service/item/ItemService.java
@@ -165,6 +165,24 @@
    }
    /**
     * 通过 symbol 找去缓存对象
     *
     * @param symbol
     * @return
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public Item findCaCheBySymbol(String symbol) {
        Item item;
        if (CollectionUtil.isNotEmpty(symbolItem)) {
            item = symbolItem.get(symbol);
            if (item != null) {
                return item;
            }
        }
        return null;
    }
    /**
     * 基于 remarks 字段去缓存提取 item 对象,特性是基于 remarks 映射 symbol,从缓存中提取 item;
     * 如果缓存中没有数据,则直接去数据库查询返回。
     *