zyy
2025-07-09 5c5d5112e7b999381f8b20c97e53314e6cedada2
股票主页、市场页面接口
14 files modified
1 files added
489 ■■■■■ changed files
src/main/java/com/nq/config/ThreadPoolConfig.java 20 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/controller/StockApiController.java 35 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/pojo/DataStockBean.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/service/IStockService.java 21 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/service/impl/PriceServicesImpl.java 60 ●●●● patch | view | raw | blame | history
src/main/java/com/nq/service/impl/SiteNewsServiceImpl.java 3 ●●●● patch | view | raw | blame | history
src/main/java/com/nq/service/impl/StockServiceImpl.java 172 ●●●● patch | view | raw | blame | history
src/main/java/com/nq/utils/redis/RedisKeyConstant.java 5 ●●●● patch | view | raw | blame | history
src/main/java/com/nq/utils/redis/RedisKeyUtil.java 21 ●●●● patch | view | raw | blame | history
src/main/java/com/nq/utils/redis/RedisShardedPoolUtils.java 8 ●●●● patch | view | raw | blame | history
src/main/java/com/nq/utils/task/stock/StockTask.java 121 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/ws/MXWebsocketRunClient.java 4 ●●● patch | view | raw | blame | history
src/main/java/com/nq/ws/WebsocketRunClient.java 5 ●●●● patch | view | raw | blame | history
src/main/resources/application.properties 6 ●●●● patch | view | raw | blame | history
src/main/resources/mapper/StockMapper.xml 6 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/config/ThreadPoolConfig.java
New file
@@ -0,0 +1,20 @@
package com.nq.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class ThreadPoolConfig {
    @Bean
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(4);
        executor.setQueueCapacity(10);
        executor.setThreadNamePrefix("stock-sync-");
        executor.initialize();
        return executor;
    }
}
src/main/java/com/nq/controller/StockApiController.java
@@ -54,6 +54,7 @@
    @RequestMapping("getOptionStock.do")
    @ResponseBody()
    public  ServerResponse getOptionStock(HttpServletRequest request){
@@ -117,4 +118,38 @@
    public ServerResponse getVipByCode(String code) {
        return this.iStockService.getVipByCode(code);
    }
    /**
     * 获取首页数据 新闻和指数列表
     * @param pageSize
     * @return
     */
    @PostMapping({"getHomePageData.do"})
    @ResponseBody
    public ServerResponse getHomePageData(@RequestParam(value = "pageSize", defaultValue = "5") int pageSize) {
        return this.iStockService.getIndicesIndexListAndNews(pageSize);
    }
    /**
     * 获取指数id和name 默认US
     * @param stockType
     * @return
     */
    @RequestMapping("getIndicesList.do")
    @ResponseBody
    public ServerResponse getIndicesList(@RequestParam(value = "stockType", defaultValue = "US") String stockType)   {
        return this.iStockService.getIndicesList(stockType);
    }
    /**
     * 获取单个指数数据和k线图 默认US
     * @param stockType
     * @return
     */
    @RequestMapping("getIndicesAndKData.do")
    @ResponseBody
    public ServerResponse getIndicesAndKData(@RequestParam(value = "pid") String pid,
                                             @RequestParam(value = "stockType", defaultValue = "US") String stockType) {
        return this.iStockService.getIndicesAndKData(pid, stockType);
    }
}
src/main/java/com/nq/pojo/DataStockBean.java
@@ -64,5 +64,7 @@
    private String Ticker;
    //k线
    private Object kData;
}
src/main/java/com/nq/service/IStockService.java
@@ -104,5 +104,24 @@
  Object getKData(String pid,String interval,String stockType);
    ServerResponse getOptionStock(HttpServletRequest request);
  ServerResponse getOptionStock(HttpServletRequest request);
  /**
   * 获取首页指数列表
   * @param pageSize
   * @return
   */
  ServerResponse getIndicesIndexListAndNews(Integer pageSize);
  /**
   * 获取指数数据 获取指数id和name
   * @return
   */
  ServerResponse getIndicesList(String stockType);
  /**
   * 获取单个指数数据和k线图
   * @return
   */
  ServerResponse getIndicesAndKData(String pid, String stockType);
}
src/main/java/com/nq/service/impl/PriceServicesImpl.java
@@ -5,7 +5,6 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.nq.dao.StockDzMapper;
import com.nq.dao.StockMapper;
import com.nq.dao.StockSettingMapper;
@@ -13,23 +12,14 @@
import com.nq.pojo.*;
import com.nq.service.IPriceServices;
import com.nq.service.IStockConfigServices;
import com.nq.utils.PropertiesUtil;
import com.nq.utils.http.HttpClientRequest;
import com.nq.utils.redis.RedisKeyUtil;
import com.nq.utils.timeutil.TimeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.*;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import  java.io.BufferedReader;
import  java.io.InputStreamReader;
import  java.net.HttpURLConnection;
import  java.net.URL;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -67,7 +57,7 @@
                if(stockSetting.getType().equals("0")){
                    return  new BigDecimal(stockSetting.getPrice());
                }else{
                    String s = doPost(stock.getStockCode());
                    String s = RedisKeyUtil.doPost(stock.getStockCode(), stock.getStockType());
                    if(null != s){
                        Map<String, Object> stringObjectMap = jsonToMap(s);
                        return   new BigDecimal(stringObjectMap.get("Last").toString()).multiply(new BigDecimal(stockSetting.getPrice()));
@@ -76,7 +66,7 @@
            }
        }
        String s = doPost(stock.getStockCode());
        String s = RedisKeyUtil.doPost(stock.getStockCode(), stock.getStockType());
        if(null != s) {
            Map<String, Object> stringObjectMap = jsonToMap(s);
            return  new BigDecimal(stringObjectMap.get("Last").toString());
@@ -87,7 +77,7 @@
    @Override
    public Map<String, Object> getNewStock(String stockCode) {
        Stock stock = stockMapper.selectOne(new QueryWrapper<Stock>().eq("stock_code",stockCode));
        String s = doPost(stock.getStockCode());
        String s = RedisKeyUtil.doPost(stock.getStockCode(), stock.getStockType());
        if(null != s){
            Map<String, Object> stringObjectMap = jsonToMap(s);
            return   stringObjectMap;
@@ -101,6 +91,9 @@
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            Object[] array = objectMapper.readValue(json, Object[].class);
            if (array.length == 0) {
                return Collections.emptyMap();
            }
            Gson gson = new Gson();
            String s = gson.toJson(array[0]);
            Map<String, Object> map = objectMapper.readValue(s, Map.class);
@@ -108,45 +101,6 @@
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
    public String doPost(String pid) {
        // 从配置中获取 API URL,并拼接 key
        String apiUrl = PropertiesUtil.getProperty("JS_IN_HTTP_URL") + "stock?key=" + PropertiesUtil.getProperty("JS_IN_KEY");
        String result = null;
        try {
            URL url = new URL(apiUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            // 设置请求方法为 POST
            connection.setRequestMethod("POST");
            // 设置请求头
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            connection.setDoOutput(true); // 允许向连接输出
            // 构建 POST 数据
            String postData = "pid=" + pid;
            // 发送 POST 请求
            try (OutputStream os = connection.getOutputStream()) {
                byte[] input = postData.getBytes("utf-8");
                os.write(input, 0, input.length);
            }
            // 读取响应
            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String inputLine;
            StringBuffer response = new StringBuffer();
            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();
            result = response.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
src/main/java/com/nq/service/impl/SiteNewsServiceImpl.java
@@ -165,7 +165,8 @@
                        siteNews.setSourceId(newsId);
                        siteNews.setSourceName("");
                        siteNews.setTitle(jsonObject.getString("title"));
                        Long showTime = jsonObject.getLong("time");
                        //Long showTime = jsonObject.getLong("time");
                        long showTime = jsonObject.optLong("time", System.currentTimeMillis() / 1000); // 默认值,表示未找到或转换失败
                        siteNews.setShowTime(new Date(showTime * 1000));
                        if(jsonObject.has("img")){
                            convertBase64ToImage(jsonObject.getString("img"),PropertiesUtil.getProperty("ftp.address")+newsId+".jpg");
src/main/java/com/nq/service/impl/StockServiceImpl.java
@@ -1,8 +1,6 @@
package com.nq.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
@@ -10,10 +8,8 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import com.nq.common.ServerResponse;
import com.nq.config.StockPoll;
import com.nq.dao.*;
import com.nq.enums.EStockType;
import com.nq.pojo.*;
@@ -26,28 +22,18 @@
import com.nq.utils.stock.pinyin.GetPyByChinese;
import com.nq.utils.stock.qq.QqStockApi;
import com.nq.utils.stock.sina.StockApi;
import com.nq.utils.translate.GoogleTranslateUtil;
import com.nq.vo.stock.*;
import com.nq.vo.stock.k.MinDataVO;
import com.nq.vo.stock.k.echarts.EchartsDataVO;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.Data;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -63,14 +49,8 @@
    @Autowired
    StockMapper stockMapper;
    @Resource
    RealTimeMapper realTimeMapper;
    @Autowired
    IStockMarketsDayService iStockMarketsDayService;
    @Autowired
    StockPoll stockPoll;
    @Resource
    StockFuturesMapper stockFuturesMapper;
@@ -82,20 +62,16 @@
    IUserService iUserService;
    @Autowired
    IStockOptionService iStockOptionService;
    @Autowired
    ISiteSettingService iSiteSettingService;
    @Autowired
    InStockMapper inStockMapper;
    @Resource
    StockDzMapper stockDzMapper;
    @Autowired
    IPriceServices iPriceServices;
    @Autowired
    SiteNewsMapper siteNewsMapper;
    public ServerResponse getMarket() {
@@ -170,17 +146,26 @@
    @Override
    public ServerResponse getStockByType(int pageNum, int pageSize, String orderBy, String keyWords, String stockType, HttpServletRequest request) {
        List<Stock> stockList = new ArrayList<>();
        PageHelper.startPage(pageNum, pageSize);
        stockList.addAll(stockMapper.findStockByType(orderBy,stockType,keyWords));
        List<Stock> stockList = stockMapper.findStockByType(orderBy, stockType, keyWords);
        // 获取分页信息
        PageInfo<Stock> pageInfo = new PageInfo<>(stockList);
        List<StockListVO> stockListVOS = Lists.newArrayList();
        if (stockList.size() > 0){
        if (stockList.size() > 0) {
            stockListVOS.addAll(Objects.requireNonNull(StockApi.getStockReailTimes(stockList)));
        }
        RPageInfo pageInfo = new RPageInfo();
        pageInfo.setList(stockListVOS);
        pageInfo.setStockType(stockType);
        return ServerResponse.createBySuccess(pageInfo);
        RPageInfo resultPageInfo = new RPageInfo();
        resultPageInfo.setList(stockListVOS);
        resultPageInfo.setStockType(stockType);
        // 设置分页信息
        resultPageInfo.setTotal(pageInfo.getTotal());
        resultPageInfo.setPageNum(pageInfo.getPageNum());
        resultPageInfo.setPageSize(pageInfo.getPageSize());
        resultPageInfo.setPages(pageInfo.getPages());
        resultPageInfo.setHasNextPage(pageInfo.isHasNextPage());
        return ServerResponse.createBySuccess(resultPageInfo);
    }
    @Override
@@ -193,7 +178,7 @@
        if (StringUtils.isBlank(code))
            return ServerResponse.createByErrorMsg("");
        Stock stock = stockMapper.findStockByCode(code);
        DataStockBean cacheBaseStock = RedisKeyUtil.getCacheBaseStock(stock);
        DataStockBean cacheBaseStock = RedisKeyUtil.getCacheBaseStock(stock.getStockType(), stock.getStockCode());
        Integer depositAmt = 0;
        String introduction = null;
        StockVO   stockVO = StockApi.assembleInStockVO(stock);
@@ -223,7 +208,7 @@
        if (StringUtils.isBlank(code))
            return null;
        Stock stock = stockMapper.findStockByCode(code);
        DataStockBean cacheBaseStock = RedisKeyUtil.getCacheBaseStock(stock);
        DataStockBean cacheBaseStock = RedisKeyUtil.getCacheBaseStock(stock.getStockType(), stock.getStockCode());
        Integer depositAmt = 0;
        String introduction = null;
        StockVO   stockVO = StockApi.assembleInStockVO(stock);
@@ -711,4 +696,115 @@
        }
        return ServerResponse.createBySuccess(pool);
    }
    @Override
    public ServerResponse getIndicesIndexListAndNews(Integer pageSize) {
        try {
            //新闻列表
            List<SiteNews> newsList = this.siteNewsMapper.getTopNewsList(pageSize);
            String usCodeList = PropertiesUtil.getProperty("us_home_indices_code");
            String mxCodeList = PropertiesUtil.getProperty("mx_home_indices_code");
            List<String> usStockCodeList = Arrays.asList(usCodeList.split(","));
            List<String> mxStockCodeList = Arrays.asList(mxCodeList.split(","));
            List<DataStockBean> usDataStockBeans = Lists.newArrayList();
            List<DataStockBean> mxDataStockBeans = Lists.newArrayList();
            for (int i = 0; i < usStockCodeList.size(); i++) {
                String stockCode = usStockCodeList.get(i);
                String stockType = EStockType.US.getCode();
                DataStockBean cacheBaseStock = RedisKeyUtil.getCacheBaseStock(stockType, stockCode);
                // 获取K线数据
                Object kData = RedisKeyUtil.getCaCheStockKData(stockType, stockCode);
                if (kData == null) {
                    //重新获取并缓存redis
                    kData = getKData(stockCode, "D", stockType);
                    RedisKeyUtil.setCaCheStockKData(stockType, stockCode, kData);
                }
                Gson gson = new Gson();
                List<kData> dataList = gson.fromJson(kData.toString(), new TypeToken<List<kData>>(){}.getType());
                cacheBaseStock.setKData(dataList);
                usDataStockBeans.add(cacheBaseStock);
            }
            for (int i = 0; i < mxStockCodeList.size(); i++) {
                String stockCode = mxStockCodeList.get(i);
                String stockType = EStockType.MX.getCode();
                DataStockBean cacheBaseStock = RedisKeyUtil.getCacheBaseStock(stockType, stockCode);
                // 获取K线数据
                Object kData = RedisKeyUtil.getCaCheStockKData(stockType, stockCode);
                if (kData == null) {
                    //重新获取并缓存redis
                    kData = getKData(stockCode, "D", stockType);
                    RedisKeyUtil.setCaCheStockKData(stockType, stockCode, kData);
                }
                Gson gson = new Gson();
                List<kData> dataList = gson.fromJson(kData.toString(), new TypeToken<List<kData>>(){}.getType());
                cacheBaseStock.setKData(dataList);
                mxDataStockBeans.add(cacheBaseStock);
            }
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("newsList", newsList);                //新闻列表
            resultMap.put("usDataStockBeans", usDataStockBeans);//美国指数
            resultMap.put("mxDataStockBeans", mxDataStockBeans);//墨西哥指数
            return ServerResponse.createBySuccess(resultMap);
        } catch (Exception e){
            e.printStackTrace();
        }
        return ServerResponse.createByError();
    }
    @Override
    public ServerResponse getIndicesList(String stockType) {
        try {
            String codeList;
            if (stockType.equals(EStockType.US.getCode())) {
                codeList = PropertiesUtil.getProperty("us_home_indices_code");
            } else {
                codeList = PropertiesUtil.getProperty("mx_home_indices_code");
            }
            List<String> stockCodeList = Arrays.asList(codeList.split(","));
            Map<Integer, Object> resultMap = new HashMap<>();
            for (int i = 0; i < stockCodeList.size(); i++) {
                String stockCode = stockCodeList.get(i);
                DataStockBean cacheBaseStock = RedisKeyUtil.getCacheBaseStock(stockType, stockCode);
                Map<String, Object> stockInfo = new HashMap<>();
                stockInfo.put("pid", cacheBaseStock.getId());
                stockInfo.put("name", cacheBaseStock.getName());
                stockInfo.put("stockType", stockType);
                resultMap.put(i+1, stockInfo);
            }
            return ServerResponse.createBySuccess(resultMap);
        } catch (Exception e){
            e.printStackTrace();
        }
        return ServerResponse.createByError();
    }
    @Override
    public ServerResponse getIndicesAndKData(String pid, String stockType) {
        try {
            Map<String, Object> resultMap = new HashMap<>();
            DataStockBean cacheBaseStock = RedisKeyUtil.getCacheBaseStock(stockType, pid);
            if (cacheBaseStock != null) {
                resultMap.put("last", cacheBaseStock.getLast());
                resultMap.put("chg", cacheBaseStock.getChg());
                resultMap.put("chgPct", cacheBaseStock.getChgPct());
                resultMap.put("kData", cacheBaseStock.getChg());
                // 获取K线数据
                Object kData = RedisKeyUtil.getCaCheStockKData(stockType, pid);
                if (kData == null) {
                    //重新获取并缓存redis
                    kData = getKData(pid, "D", stockType);
                    RedisKeyUtil.setCaCheStockKData(stockType, pid, kData);
                }
                Gson gson = new Gson();
                List<kData> dataList = gson.fromJson(kData.toString(), new TypeToken<List<kData>>(){}.getType());
                resultMap.put("kData", dataList);
                return ServerResponse.createBySuccess(resultMap);
            }
        } catch (Exception e){
            e.printStackTrace();
        }
        return ServerResponse.createByError();
    }
}
src/main/java/com/nq/utils/redis/RedisKeyConstant.java
@@ -25,5 +25,8 @@
     * */
    public static final String RK_COMPANY_INFO = "rk_company_info";
    /**
     * 股票K线图数据key
     * */
    public static final String RK_STOCK_KDATA = "rk_stock_kdata";
}
src/main/java/com/nq/utils/redis/RedisKeyUtil.java
@@ -37,8 +37,8 @@
    /**
     * 获取股票数据
     * */
    public static DataStockBean getCacheBaseStock(Stock stock){
        String cacheBaseData = RedisShardedPoolUtils.get(RedisKeyConstant.RK_BASE_STOCK+":"+stock.getStockType()+":"+stock.getStockCode());
    public static DataStockBean getCacheBaseStock(String stockType, String stockCode){
        String cacheBaseData = RedisShardedPoolUtils.get(RedisKeyConstant.RK_BASE_STOCK+":"+stockType+":"+stockCode);
        return  new Gson().fromJson(cacheBaseData, DataStockBean.class);
    }
@@ -60,7 +60,7 @@
            stockRealTimeBean = new Gson().fromJson(cacheBaseData, StockRealTimeBean.class);
        }
        if(stockRealTimeBean == null){
            String s = doPost(stock.getStockCode());
            String s = doPost(stock.getStockCode(), stock.getStockType());
            Map<String, Object> stringObjectMap = jsonToMap(s);
            stockRealTimeBean = new StockRealTimeBean();
            stockRealTimeBean.setPcp(stringObjectMap.get("ChgPct").toString());
@@ -89,9 +89,11 @@
        }
    }
    public static String doPost(String pid) {
    public static String doPost(String pid, String stockType) {
        EStockType eStockType = EStockType.getEStockTypeByCode(stockType);
        // 从配置中获取 API URL,并拼接 key
        String apiUrl = PropertiesUtil.getProperty("JS_IN_HTTP_URL") + "stock?key=" + PropertiesUtil.getProperty("JS_IN_KEY");
        //String apiUrl = PropertiesUtil.getProperty("JS_IN_HTTP_URL") + "stock?key=" + PropertiesUtil.getProperty("JS_IN_KEY");
        String apiUrl = eStockType.getStockUrl() + "stock?key=" + eStockType.getStockKey();
        String result = null;
        try {
            URL url = new URL(apiUrl);
@@ -143,6 +145,15 @@
       return  JSONObject.parseObject(companiesInfo);
    }
    /**
     * 缓存K线数据源到redis
     * */
    public static void setCaCheStockKData(String stockType, String stockCode, Object kData){
        RedisShardedPoolUtils.set(RedisKeyConstant.RK_STOCK_KDATA+":"+stockType+":"+stockCode, kData.toString());
    }
    public static Object getCaCheStockKData(String stockType, String stockCode){
        return RedisShardedPoolUtils.get(RedisKeyConstant.RK_STOCK_KDATA+":"+stockType+":"+stockCode);
    }
}
src/main/java/com/nq/utils/redis/RedisShardedPoolUtils.java
@@ -142,11 +142,11 @@
        try {
            if (key == null) {
                jedis = RedisShardedPool.getJedis();
            jedis = RedisShardedPool.getJedis();
            result = jedis.del(key);
                result = jedis.del(key);
            }
        } catch (Exception e) {
            log.error("redis del key: {} error ", key, e);
src/main/java/com/nq/utils/task/stock/StockTask.java
@@ -1,13 +1,8 @@
package com.nq.utils.task.stock;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import com.nq.Repository.StockRepository;
import com.nq.dao.StockMapper;
@@ -17,24 +12,21 @@
import com.nq.service.IMandatoryLiquidationService;
import com.nq.service.IStockService;
import com.nq.service.IUserPositionService;
import com.nq.service.impl.StockServiceImpl;
import com.nq.utils.PropertiesUtil;
import com.nq.utils.http.HttpClientRequest;
import com.nq.utils.http.HttpRequest;
import com.nq.utils.redis.RedisKeyUtil;
import net.sf.json.JSONArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -44,85 +36,79 @@
@Component
public class StockTask {
    @Autowired
    IStockService stockService;
    @Autowired
    StockMapper stockMapper;
    @Autowired
    IUserPositionService userPositionService;
    @Autowired
    UserPositionMapper userPositionMapper;
    IStockService istockService;
    @Autowired
    StockRepository stockRepository;
    private final Lock stockConstraintLock = new ReentrantLock();
    @Autowired
    IMandatoryLiquidationService mandatoryLiquidationService;
    private static final Logger log = LoggerFactory.getLogger(StockTask.class);
    private final AtomicBoolean syncINStockData = new AtomicBoolean(false);
    private final Lock syncINStockDataLock = new ReentrantLock();
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private StockServiceImpl iStockService;
    /**
     * test
     */
    //@Scheduled(cron = "0 0/1 * * * ?")
    @Scheduled(cron = "*/5 * * * * *")
    public void test() {
        //iStockService.getStockByType(1, 20, "desc","st" , "US", null);
        //istockService.getIndicesList("US");
        //istockService.getIndicesAndKData("15882", "US");
    }
    /**
     * 同步系统所需要的股票
     */
    @Scheduled(cron = "0 0/1 * * * ?")
    public void syncINStockData() {
        if (syncINStockData.get()) { // 判断任务是否在处理中
            return;
        }
        if (syncINStockDataLock.tryLock()) {
            ExecutorService executor = Executors.newFixedThreadPool(4);
            Future<?> future1 = null;
            Future<?> future2 = null;
            Future<?> future3 = null;
            Future<?> future4 = null;
            try {
                syncINStockData.set(true); // 设置处理中标识为true
                syncINStockData.set(true);
                // 同步股票数据
                future1 = executor.submit(() -> loadAllStock(EStockType.US));
                future2 = executor.submit(() -> loadAllStock(EStockType.MX));
                // 同步指数数据
                future3 = executor.submit(() -> syncIndices(EStockType.US));
                future4 = executor.submit(() -> syncIndices(EStockType.MX));
                // 使用CompletableFuture并行执行任务
                CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> loadAllStock(EStockType.US), taskExecutor);
                CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> loadAllStock(EStockType.MX), taskExecutor);
                CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> syncIndices(EStockType.US), taskExecutor);
                CompletableFuture<Void> future4 = CompletableFuture.runAsync(() -> syncIndices(EStockType.MX), taskExecutor);
                // 等待任务都完成
                future1.get();
                future2.get();
                future3.get();
                future4.get();
                // 等待所有任务完成
                CompletableFuture.allOf(future1, future2, future3, future4).join();
            } catch (Exception e) {
                Thread.currentThread().interrupt();
                log.error("同步股票数据出错", e);
            } finally {
                // 关闭线程池
                if (executor != null) {
                    executor.shutdown();
                }
                syncINStockDataLock.unlock();
                syncINStockData.set(false); // 设置处理中标识为false
                syncINStockData.set(false);
            }
        }
    }
    /**
     * 加载所有指数数据
     */
    private void syncIndices(EStockType eStockType) {
        List<DataStockBean> list = new ArrayList<>();
        int totleStock = 1;
        try {
            while (totleStock > list.size()) {
                try {
                    //String result = HttpClientRequest.doGet(eStockType.stockUrl + "indices?key=" + eStockType.getStockKey() + "&country_id=" + eStockType.getContryId());
                    //ReponseBase reponseBase = new Gson().fromJson(result, ReponseBase.class);
                    String result = HttpRequest.doGrabGet(eStockType.stockUrl + "indices?key=" + eStockType.getStockKey() + "&country_id=" + eStockType.getContryId());
                    // 把JSON数据解析为List<DataStockBean>
                    Type listType = new TypeToken<List<DataStockBean>>(){}.getType();
@@ -169,12 +155,44 @@
                    stockMapper.updateById(stock);
                }*/
                RedisKeyUtil.setCaCheKeyBaseStock(eStockType, o);
                /*StockRealTimeBean stockRealTimeBean = new StockRealTimeBean();
                stockRealTimeBean.setPid(o.getId());
                stockRealTimeBean.setLast(o.getLast());
                stockRealTimeBean.setHigh(o.getHigh());
                stockRealTimeBean.setLow(o.getLow());
                stockRealTimeBean.setPc(o.getChg());
                stockRealTimeBean.setPcp(o.getChgPct()+ "%");
                stockRealTimeBean.setTime(o.getTime());
                RedisKeyUtil.setCacheRealTimeStock(eStockType, stockRealTimeBean);*/
            }
            stockRepository.saveAll(updateStockList);
            cacheKData(eStockType.getCode(), list);
            log.info("同步指数 数据 成功 {}  总共同步数据 {}", eStockType.getCode(), list.size());
        } catch (Exception e) {
            e.printStackTrace();
            log.error("同步指数列表出现异常: {}", e.getMessage());
        }
    }
    /**
     * 同步指数股票后缓存k线图
     */
    public void cacheKData(String stockType, List<DataStockBean> list) {
        String usCodeList = PropertiesUtil.getProperty("us_home_indices_code");
        String mxCodeList = PropertiesUtil.getProperty("mx_home_indices_code");
        for (DataStockBean dataStockBean : list) {
            //缓存首页指数k线图
            if (usCodeList.contains(dataStockBean.getId()) || mxCodeList.contains(dataStockBean.getId())) {
                // 获取K线数据
                Object kData = istockService.getKData(dataStockBean.getId(), "D", stockType);
                if (kData != null) {
                    //缓存redis
                    RedisKeyUtil.setCaCheStockKData(stockType, dataStockBean.getId(), kData);
                }
            }
        }
        log.info("同步指数k线图 数据 成功 {}  总共同步数据 {}", stockType, list.size());
    }
    /**
@@ -218,7 +236,7 @@
        try {
            while (totleStock > list.size()) {
                try {
                    String result = HttpClientRequest.doGet(eStockType.stockUrl + "list?country_id=" + eStockType.getContryId() + "&size=1000&page=" + page + "&key=" + eStockType.stockKey);
                    String result = HttpClientRequest.doGet(eStockType.stockUrl + "list?country_id=" + eStockType.getContryId() + "&size=100000&page=" + page + "&key=" + eStockType.stockKey);
                    ReponseBase reponseBase = new Gson().fromJson(result, ReponseBase.class);
                    list.addAll(reponseBase.getData());
                    page++;
@@ -233,9 +251,7 @@
            }
            List<String> stockCodeList = list.stream().map(DataStockBean::getId).collect(Collectors.toList());
            List<Stock> stockList = stockMapper.selectList(new QueryWrapper<Stock>().in("stock_code", stockCodeList));
            int i = 1;
            List<Stock> updateStockList = new ArrayList<>();
            System.out.println(new Date());
            for (DataStockBean o : list) {
                //Stock stock = stockMapper.findStockByCode(o.getId());
                Stock stock = stockList.stream()
@@ -276,11 +292,8 @@
                }
                updateStockList.add(stock);
                RedisKeyUtil.setCaCheKeyBaseStock(eStockType, o);
                System.out.println(i);
                i++;
            }
            stockRepository.saveAll(updateStockList);
            System.out.println(new Date());
            log.info("同步股票 数据 成功 {}  总共同步数据 {}", eStockType.getCode(), list.size());
        } catch (
                Exception e) {
src/main/java/com/nq/ws/MXWebsocketRunClient.java
@@ -51,7 +51,9 @@
        heartbeatTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                send(("key:"+ eStockType.getStockKey()+":"+eStockType.getContryId()).getBytes());
                if (isOpen()) {
                    send(("key:"+ eStockType.getStockKey()+":"+eStockType.getContryId()).getBytes());
                }
            }
        }, 0, 3000); // 每3秒发送一次心跳消息
    }
src/main/java/com/nq/ws/WebsocketRunClient.java
@@ -52,7 +52,10 @@
        heartbeatTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                send(("key:"+ eStockType.getStockKey()+":"+eStockType.getContryId()).getBytes());
                if (isOpen()) {
                    send(("key:"+ eStockType.getStockKey()+":"+eStockType.getContryId()).getBytes());
                }
            }
        }, 0, 3000); // 每3秒发送一次心跳消息
    }
src/main/resources/application.properties
@@ -92,9 +92,9 @@
SZHB_WS_URL = ws://api-in-ws.js-stock.top
SZHB_KEY = GBZAcUPLKZzDMDjvV9Ea
#默认首页显示指数code
us_home_indices_code=15882,15881,16571
mx_home_indices_code=535610327,535610374,535610333
#?? ?? - ????
admin.auth.email.subject=???? - ??????
src/main/resources/mapper/StockMapper.xml
@@ -319,8 +319,10 @@
        <include refid="Base_Column_List"/>
         FROM stock
    where stock_type = #{stockType}
    and stock_spell not like '%.st%'
    where stock_spell not like '%.st%'
    <if test="stockType != null and stockType != '' ">
      and stock_type = #{stockType}
    </if>
    <if test="keyWords != null and keyWords != '' ">
      and (stock_spell like concat('%',#{keyWords},'%')  or stock_name like concat('%',#{keyWords},'%')  )
    </if>