diff --git a/src/main/java/cn/stock/market/domain/basic/service/StockService.java b/src/main/java/cn/stock/market/domain/basic/service/StockService.java index b0ad5c3..84ab368 100644 --- a/src/main/java/cn/stock/market/domain/basic/service/StockService.java +++ b/src/main/java/cn/stock/market/domain/basic/service/StockService.java @@ -13,11 +13,9 @@ import cn.hutool.core.text.StrFormatter; import cn.stock.market.dto.model.*; import cn.stock.market.infrastructure.api.EttechchartsApis; import cn.stock.market.infrastructure.api.GrowwInApis; +import cn.stock.market.infrastructure.api.HomeApiIndex; import cn.stock.market.infrastructure.api.TodayApis; -import cn.stock.market.infrastructure.api.investing.IndiaIndexVo; -import cn.stock.market.infrastructure.api.investing.IndiaStockVO; -import cn.stock.market.infrastructure.api.investing.InvestingApis; -import cn.stock.market.infrastructure.api.investing.InvestingInvokerApis; +import cn.stock.market.infrastructure.api.investing.*; import cn.stock.market.infrastructure.api.sina.vo.HotSearchVO; import cn.stock.market.utils.*; import com.ag.utils.CollectionUtils; @@ -1048,70 +1046,33 @@ public class StockService { return ServerResponse.createBySuccess(indexVoList); } - public ServerResponse getIndexByBtoday(){ - List indexVoList = new ArrayList<>(); - try { - String exchange = "bse"; - IndiaIndexVo vo1 = new IndiaIndexVo(); - String coCode = "20558"; - JSONObject object = TodayApis.getStockDetail("in%3BSEN", coCode); - IndiaStockVO market = objToVo(object); - market.setName("BSESENSEX指数"); + public ServerResponse getIndexByBtoday() throws Exception { + List list = HomeApiIndex.fetchStockIndices(); + + List indexVoList = new ArrayList<>(); + + for (StockIndex stockIndex : list) { + IndiaIndexNewVo vo1 = new IndiaIndexNewVo(); + IndiaStockVO market = new IndiaStockVO(); + market.setClose(String.valueOf(stockIndex.getClose())); + market.setHigh(String.valueOf(stockIndex.getHigh())); + market.setLow(String.valueOf(stockIndex.getLow())); + market.setName(stockIndex.getName()); + market.setNowPrice(String.valueOf(stockIndex.getClose())); + market.setOpen(String.valueOf(stockIndex.getOpen())); + market.setRate(String.valueOf(stockIndex.getPercentChange())); vo1.setIndexVo(market); - //获取k线图 1D 当天的数据 - String format = "S"; - String durationType = "D"; - String duration = "1"; - List kine = TodayApis.getStockKline(exchange,coCode,format,durationType,duration); - vo1.setKLine(kine); + List kLines = HomeApiIndex.fetchChartData(stockIndex.getId(), 419); +// List kline = HomeApiIndex.convertToJsonList(kLines); + vo1.setKLine(kLines); indexVoList.add(vo1); - }catch (Exception e){ - log.error("BToday获取BSESENSEX指数数据异常,异常信息。。。。", e); - try { - GrowwInApis.requestSenSexData(indexVoList); - } catch (Exception e1) { - log.error("GrowwIn获取BSESENSEX指数数据异常,异常信息。。。。", e1); - try{ - EttechchartsApis.requestSensexData(indexVoList); - } catch (Exception e2) { - log.error("Ettechcharts获取BSESENSEX指数数据异常,异常信息。。。。", e2); - } - } } - try { - String exchange = "nse"; - IndiaIndexVo vo1 = new IndiaIndexVo(); - String coCode = "20559"; - JSONObject object = TodayApis.getStockDetail("in%3BNSX", coCode); - IndiaStockVO market = objToVo(object); - market.setName("NIFTY50指数"); - vo1.setIndexVo(market); - - //获取k线图 1D 当天的数据 - String format = "S"; - String durationType = "D"; - String duration = "1"; - List kine = TodayApis.getStockKline(exchange,coCode,format,durationType,duration); - vo1.setKLine(kine); - indexVoList.add(vo1); - }catch (Exception e){ - log.error("BToday获取NIFTY50指数数据异常,异常信息。。。。", e); - try { - GrowwInApis.requestNifty50Data(indexVoList); - } catch (Exception e1) { - log.error("GrowwIn获取NIFTY50指数数据异常,异常信息。。。。", e1); - try{ - EttechchartsApis.requestNifty50Data(indexVoList); - } catch (Exception e2) { - log.error("Ettechcharts获取NIFTY50指数数据异常,异常信息。。。。", e2); - } - } - } return ServerResponse.createBySuccess(indexVoList); } + private IndiaStockVO objToVo(JSONObject object){ IndiaStockVO market = new IndiaStockVO(); if(object.containsKey("priceprevclose")){ diff --git a/src/main/java/cn/stock/market/dto/RawStockApiResponse.java b/src/main/java/cn/stock/market/dto/RawStockApiResponse.java new file mode 100644 index 0000000..9f8a8a0 --- /dev/null +++ b/src/main/java/cn/stock/market/dto/RawStockApiResponse.java @@ -0,0 +1,10 @@ +package cn.stock.market.dto; + +import lombok.Data; + +import java.util.List; + +@Data +public class RawStockApiResponse { + private List data; +} diff --git a/src/main/java/cn/stock/market/dto/StockDataDto.java b/src/main/java/cn/stock/market/dto/StockDataDto.java new file mode 100644 index 0000000..4e9c9d6 --- /dev/null +++ b/src/main/java/cn/stock/market/dto/StockDataDto.java @@ -0,0 +1,13 @@ +package cn.stock.market.dto; + +import lombok.Data; + +@Data +public class StockDataDto { + private long time; + private double open; + private double close; + private double max; + private double min; + private long volume; +} \ No newline at end of file diff --git a/src/main/java/cn/stock/market/dto/StockQuoteData.java b/src/main/java/cn/stock/market/dto/StockQuoteData.java new file mode 100644 index 0000000..098ce04 --- /dev/null +++ b/src/main/java/cn/stock/market/dto/StockQuoteData.java @@ -0,0 +1,31 @@ +package cn.stock.market.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class StockQuoteData { + private String id; + private String symbol; + private String name; + private String exchange; + private String mic_code; + private String datetime; + private long timestamp; + private double open; + private double high; + private double low; + private double close; + private long volume; + private double previous_close; + private double change; + private double percent_change; + private double average_volume; + @JsonProperty("is_market_open") + private boolean market_open; + private Long market_cap; + private Object fifty_two_week; + private int icon; +} diff --git a/src/main/java/cn/stock/market/dto/StockQuoteResponse.java b/src/main/java/cn/stock/market/dto/StockQuoteResponse.java new file mode 100644 index 0000000..e7fea88 --- /dev/null +++ b/src/main/java/cn/stock/market/dto/StockQuoteResponse.java @@ -0,0 +1,13 @@ +package cn.stock.market.dto; + +import lombok.Data; + +import java.util.List; + +@Data +public class StockQuoteResponse { + private List data; + private boolean success; + private String message; + private int status; +} diff --git a/src/main/java/cn/stock/market/dto/model/ChartCandle.java b/src/main/java/cn/stock/market/dto/model/ChartCandle.java new file mode 100644 index 0000000..ef77fb8 --- /dev/null +++ b/src/main/java/cn/stock/market/dto/model/ChartCandle.java @@ -0,0 +1,11 @@ +package cn.stock.market.dto.model; + +import lombok.Data; + +@Data +public class ChartCandle { + private String upd_date; + private Double price; + + // Getters & Setters (hoặc @Data nếu dùng Lombok) +} diff --git a/src/main/java/cn/stock/market/dto/model/StockIndex.java b/src/main/java/cn/stock/market/dto/model/StockIndex.java new file mode 100644 index 0000000..9dd8a84 --- /dev/null +++ b/src/main/java/cn/stock/market/dto/model/StockIndex.java @@ -0,0 +1,31 @@ +package cn.stock.market.dto.model; + +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class StockIndex { + private String id; + private String symbol; + private String name; + private String exchange; + private String micCode; + private String datetime; + private long timestamp; + private Double open; + private Double high; + private Double low; + private Double close; + private Long volume; + private Double previousClose; + private Double change; + private Double percentChange; + private Long averageVolume; + private Boolean isMarketOpen; + private Long marketCap; + private String fiftyTwoWeek; + private Integer icon; + + // Getters & setters (hoặc dùng @Data nếu có Lombok) +} \ No newline at end of file diff --git a/src/main/java/cn/stock/market/infrastructure/api/HomeApiIndex.java b/src/main/java/cn/stock/market/infrastructure/api/HomeApiIndex.java new file mode 100644 index 0000000..2eaa3c9 --- /dev/null +++ b/src/main/java/cn/stock/market/infrastructure/api/HomeApiIndex.java @@ -0,0 +1,139 @@ +package cn.stock.market.infrastructure.api; +import cn.stock.market.dto.model.ChartCandle; +import cn.stock.market.dto.model.StockIndex; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +public class HomeApiIndex { + private static final OkHttpClient client = new OkHttpClient(); + private static final String API_URL = "https://apinode-dgdev.moneytj.com/api/ger-market/stocks/query-list?symbols=XETR:DAX,XETR:MDAX,XETR:SDXP,XETR:HDAX"; + private static final String BASE_URL = "https://apinode-dgdev.moneytj.com/api/ger-market/chart"; + + public static List fetchStockIndices() throws Exception { + List result = new ArrayList<>(); + + Request request = new Request.Builder() + .url(API_URL) + .addHeader("User-Agent", "Mozilla/5.0") + .addHeader("Accept", "application/json") + .build(); + + Response response = client.newCall(request).execute(); + + if (!response.isSuccessful()) { + throw new RuntimeException("HTTP error code: " + response.code()); + } + + String body = response.body().string(); + JSONObject json = new JSONObject(body); + JSONArray data = json.getJSONArray("data"); + + for (int i = 0; i < data.length(); i++) { + JSONObject obj = data.getJSONObject(i); + StockIndex index = new StockIndex(); + + index.setId(obj.optString("id")); + index.setSymbol(obj.optString("symbol")); + index.setName(obj.optString("name")); + index.setExchange(obj.optString("exchange")); + index.setMicCode(obj.optString("mic_code")); + index.setDatetime(obj.optString("datetime")); + index.setTimestamp(obj.optLong("timestamp")); + index.setOpen(getDoubleOrNull(obj, "open")); + index.setHigh(getDoubleOrNull(obj, "high")); + index.setLow(getDoubleOrNull(obj, "low")); + index.setClose(getDoubleOrNull(obj, "close")); + index.setVolume(getLongOrNull(obj, "volume")); + index.setPreviousClose(getDoubleOrNull(obj, "previous_close")); + index.setChange(getDoubleOrNull(obj, "change")); + index.setPercentChange(getDoubleOrNull(obj, "percent_change")); + index.setAverageVolume(getLongOrNull(obj, "average_volume")); + index.setIsMarketOpen(obj.optBoolean("is_market_open")); + index.setMarketCap(getLongOrNull(obj, "market_cap")); + index.setFiftyTwoWeek(obj.optString("fifty_two_week", null)); + index.setIcon(obj.optInt("icon", 0)); + + result.add(index); + } + + return result; + } + + public static List fetchChartData(String symbol, int amount) throws Exception { + List result = new ArrayList<>(); + + String url = BASE_URL + "?symbol=" + symbol + "&interval=D&amount=" + amount; + + Request request = new Request.Builder() + .url(url) + .addHeader("User-Agent", "Mozilla/5.0") + .addHeader("Accept", "application/json") + .build(); + + Response response = client.newCall(request).execute(); + + if (!response.isSuccessful()) { + throw new RuntimeException("HTTP error code: " + response.code()); + } + + String body = response.body().string(); + JSONObject json = new JSONObject(body); + JSONArray data = json.getJSONArray("data"); + + for (int i = 0; i < data.length(); i++) { + JSONObject obj = data.getJSONObject(i); + ChartCandle candle = new ChartCandle(); + + long ts = obj.optLong("time"); + String formattedTime = convertToGermanTime(ts); + + candle.setUpd_date(formattedTime); + candle.setPrice(getDoubleOrNull(obj, "close")); + + result.add(candle); + } + + return result; + } + +// public static List convertToJsonList(List candles) { +// List result = new ArrayList<>(); +// +// for (ChartCandle c : candles) { +// JSONObject obj = new JSONObject(); +// String formattedTime = convertToGermanTime(c.getTime()); +// obj.put("upd_date", formattedTime); +// obj.put("price", c.getClose()); +// result.add(obj); +// } +// +// return result; +// } + + public static String convertToGermanTime(long epochSeconds) { + ZoneId germanyZone = ZoneId.of("Europe/Berlin"); + Instant instant = Instant.ofEpochSecond(epochSeconds); + ZonedDateTime zonedDateTime = instant.atZone(germanyZone); + DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; + return formatter.format(zonedDateTime); + } + + // Helper to safely parse nullable numbers + private static Double getDoubleOrNull(JSONObject obj, String key) { + return obj.isNull(key) ? null : obj.optDouble(key); + } + + private static Long getLongOrNull(JSONObject obj, String key) { + return obj.isNull(key) ? null : obj.optLong(key); + } +} diff --git a/src/main/java/cn/stock/market/infrastructure/api/investing/IndiaIndexNewVo.java b/src/main/java/cn/stock/market/infrastructure/api/investing/IndiaIndexNewVo.java new file mode 100644 index 0000000..2eefa48 --- /dev/null +++ b/src/main/java/cn/stock/market/infrastructure/api/investing/IndiaIndexNewVo.java @@ -0,0 +1,18 @@ +package cn.stock.market.infrastructure.api.investing; + +import cn.stock.market.dto.model.ChartCandle; +import com.alibaba.fastjson.JSONObject; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +@Data +@ApiModel(value = "指数信息") +public class IndiaIndexNewVo { + @ApiModelProperty(value = "指数详情") + private IndiaStockVO indexVo; + @ApiModelProperty(value = "指数k线") + private List kLine; +} diff --git a/src/main/java/cn/stock/market/web/MoneyApiController.java b/src/main/java/cn/stock/market/web/MoneyApiController.java index e905a43..026ea21 100644 --- a/src/main/java/cn/stock/market/web/MoneyApiController.java +++ b/src/main/java/cn/stock/market/web/MoneyApiController.java @@ -4,8 +4,11 @@ import cn.hutool.core.date.DateUtil; import cn.stock.market.MoneyStockSuggestDTO; import cn.stock.market.domain.basic.entity.MoneyStock; import cn.stock.market.domain.basic.entity.OptionalStock; +import cn.stock.market.domain.basic.entity.Stock; import cn.stock.market.domain.basic.repository.MoneyStockRepository; import cn.stock.market.domain.basic.repository.OptionalStockRepository; +import cn.stock.market.domain.basic.repository.StockRepository; +import cn.stock.market.domain.basic.service.StockService; import cn.stock.market.dto.*; import cn.stock.market.dto.query.StockChartDto; import cn.stock.market.infrastructure.db.po.QMoneyStockPO; @@ -13,6 +16,7 @@ import cn.stock.market.utils.HttpRequest; import cn.stock.market.utils.NseIndiaRequest; import cn.stock.market.utils.ServerResponse; import cn.stock.market.web.annotations.EncryptFilter; +import cn.stock.market.web.service.MoneyApiService; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -87,8 +91,14 @@ public class MoneyApiController { @Autowired private ObjectMapper objectMapper; + @Autowired + private MoneyApiService moneyApiService; + private static final String EXTERNAL_API_URL = "https://priceapi.moneycontrol.com/techCharts/indianMarket/stock/history"; private static final String OPTIONAL_STOCK_MONEYCONTROL_URL = "https://priceapi.moneycontrol.com/pricefeed/notapplicable/inidicesindia/"; + @Autowired + private StockRepository stockRepository; + @ApiOperation(value = "股票详情信息", httpMethod = "GET") @ApiImplicitParams({ @@ -197,136 +207,12 @@ public class MoneyApiController { @ResponseBody @EncryptFilter(decryptRequest = false) public ServerResponse getStockDetail(@RequestParam String stockType, @RequestParam String symbol) { - MoneyStock moneyStock = moneyStockRepository.findOne(QMoneyStockPO.moneyStockPO.stockType.eq(stockType) - .and(QMoneyStockPO.moneyStockPO.moneyScId.eq(symbol)) - .and(QMoneyStockPO.moneyStockPO.isLock.eq(0)) - .and(QMoneyStockPO.moneyStockPO.isShow.eq(0))) - .orElse(null); - /* if(moneyStock==null){ - return ServerResponse.createByErrorMsg("没有找到该股票"); - }*/ - // 设置重试次数 - if ("ANI".equals(symbol)) { - JSONObject json1 = new JSONObject(); - json1.put("company", "Archit Nuwood Industries Ltd"); - json1.put("pricepercentchange", "Archit Nuwood Industries Ltd"); - json1.put("stockType", stockType); - json1.put("pricecurrent", "386"); - json1.put("dataSourceType", "3"); - json1.put("symbol", "ANI"); - json1.put("BSEID", "ANI"); - json1.put("NSEID", "ANI"); - return ServerResponse.createBySuccess(json1); + Stock stock = stockRepository.findStockByCode(symbol); + if (stock == null) { + return ServerResponse.createByErrorMsg("Stock is not exist!"); } - - String url = String.format("https://priceapi.moneycontrol.com/pricefeed/%s/equitycash/%s", stockType.toLowerCase(), symbol); - int maxRetries = 3; - if (moneyStock.getUseFromBseindia()) { - String bseUrl = "https://api.bseindia.com/BseIndiaAPI/api/getScripHeaderData/w?Debtflag=&scripcode=" + moneyStock.getNseIndiaId() + "&seriesid="; - HttpHeaders headers = new HttpHeaders(); - headers.add("accept", "application/json, text/plain, */*"); - headers.add("accept-language", "en-US,en;q=0.9,vi;q=0.8"); - headers.add("origin", "https://www.bseindia.com"); - headers.add("referer", "https://www.bseindia.com/"); - headers.add("sec-ch-ua", "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\""); - headers.add("sec-ch-ua-mobile", "?0"); - headers.add("sec-ch-ua-platform", "\"Windows\""); - headers.add("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"); - - HttpEntity entity = new HttpEntity<>(headers); - - try { - ResponseEntity responseEntity = restTemplate.exchange(bseUrl, HttpMethod.GET, entity, String.class); - if (responseEntity.getStatusCode().value() == 200 && responseEntity.getBody() != null) { - JSONObject bseData = JSONObject.parseObject(responseEntity.getBody()); - JSONObject json1 = new JSONObject(); - json1.put("company", bseData.getJSONObject("Cmpname").getString("FullN")); - json1.put("pricepercentchange", bseData.getJSONObject("CurrRate").getString("PcChg")); - json1.put("stockType", stockType); - json1.put("pricechange", bseData.getJSONObject("CurrRate").getString("Chg")); - json1.put("pricecurrent", bseData.getJSONObject("CurrRate").getString("LTP")); - json1.put("priceprevclose", bseData.getJSONObject("Header").getString("PrevClose")); - json1.put("PREVDATE", ""); - - json1.put("dataSourceType", "3"); - json1.put("symbol", symbol); - json1.put("BSEID", moneyStock.getNseIndiaId()); - json1.put("NSEID", moneyStock.getNseIndiaId()); - json1.put("LTH", bseData.getJSONObject("Header").getString("High")); - json1.put("LTL", bseData.getJSONObject("Header").getString("Low")); - json1.put("OPN", bseData.getJSONObject("Header").getString("Open")); - - - if (moneyStock != null) { - json1.put("id", moneyStock.getId()); - } - - json1.put("VOL", this.getVolume(moneyStock.getNseIndiaId())); - return ServerResponse.createBySuccess(json1); - } - } catch (Exception e) { - System.err.println("Error fetching data from BSE India: " + e.getMessage()); - } - } - - for (int retry = 1; retry <= maxRetries; retry++) { - try { - ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, String.class); - JSONObject json1 = new JSONObject(); - if (responseEntity.getStatusCode().value() == 200 && responseEntity.getBody() != null) { - JSONObject data = JSONObject.parseObject(responseEntity.getBody()).getJSONObject("data"); - if (data != null) { - json1.put("company", data.getString("SC_FULLNM")); - json1.put("pricepercentchange", data.getString("pricepercentchange")); - json1.put("stockType", stockType); - json1.put("pricechange", data.getString("pricechange")); - json1.put("pricecurrent", data.getString("pricecurrent")); - json1.put("priceprevclose", data.getString("priceprevclose")); - json1.put("PREVDATE", data.getString("PREVDATE")); - json1.put("VOL", data.getString("VOL")); - json1.put("dataSourceType", "3"); - json1.put("symbol", data.getString("symbol")); - json1.put("BSEID", data.getString("BSEID")); - json1.put("NSEID", data.getString("NSEID")); - json1.put("LTH", data.getString("HP")); - json1.put("LTL", data.getString("LP")); - json1.put("OPN", data.getString("OPN")); - if (null != moneyStock) { - json1.put("id", moneyStock.getId()); - } - if (StringUtils.equals(data.getString("pricecurrent"), "0.00") - && (!StringUtils.equals(data.getString("priceprevclose"), "0.00"))) { - json1.put("pricecurrent", data.getString("priceprevclose")); - } - } - if (json1.size() > 0) - return ServerResponse.createBySuccess(json1); - } - } catch (Exception e) { - } - // 如果不是最后一次重试,则等待一段时间再进行下一次重试 - if (retry < maxRetries) { - try { - // 1秒钟 - Thread.sleep(100); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - if (moneyStock != null && moneyStock.getNseIndiaId() != null && !moneyStock.getNseIndiaId().isEmpty()) { - try { - // Get data from nseindia - JSONObject json = NseIndiaRequest.stockByJYSFromHttp(stockType, symbol, moneyStock.getNseIndiaId()); - json.put("id", moneyStock.getId()); - json.put("company",moneyStock.getStockName()); - return ServerResponse.createBySuccess(json); - - } catch (Exception e) { - return null; - } - } - return null; + StockQuoteData data = moneyApiService.getSingleStockQuote(symbol); + return ServerResponse.createBySuccess(data); } private String getVolume(String scripcode) { @@ -834,343 +720,15 @@ public class MoneyApiController { } } - - @GetMapping({"/market/api/market/money/history/kLine", "/api/market/money/history/kLine"}) @ApiOperation(value = "获取kline的money数据源", notes = "获取kline的money数据源", response = StockHistoryResponse.class) - @ApiImplicitParams({ - @ApiImplicitParam(name = "symbol", value = "Stock symbol 对应的是NSEID 或者是BSEID", required = true, dataType = "String", paramType = "query"), - @ApiImplicitParam(name = "resolution", value = "单位:60 1D 1W 1D 对应H,D,W,Y", required = true, dataType = "String", paramType = "query"), - @ApiImplicitParam(name = "from", value = "Start timestamp", required = true, dataType = "long", paramType = "query"), - @ApiImplicitParam(name = "to", value = "End timestamp", required = true, dataType = "long", paramType = "query"), - @ApiImplicitParam(name = "countback", value = "开始时间和结束时间区间的计划展示的数量", required = true, dataType = "int", paramType = "query"), - @ApiImplicitParam(name = "currencyCode", value = "INR 不变", required = true, dataType = "String", paramType = "query") - }) @ResponseBody @EncryptFilter(decryptRequest = false) - public ResponseEntity getStockHistory(@RequestParam String symbol, - @RequestParam String resolution + public ResponseEntity getStockChart(@RequestParam String symbol, + @RequestParam String interval, + @RequestParam(required = false) Integer amount ) { - MoneyStock moneyStock1 = moneyStockRepository.findOne( - QMoneyStockPO.moneyStockPO.nseIndiaId.eq(symbol) - .and(QMoneyStockPO.moneyStockPO.isLock.eq(0)) - .and(QMoneyStockPO.moneyStockPO.isShow.eq(0))) - .orElse(null); - - if (moneyStock1 != null && moneyStock1.getUseFromBseindia()) { - - String json = this.getChartData(moneyStock1.getNseIndiaId(), resolution); - - Gson gson = new Gson(); - - JsonObject outerJson = gson.fromJson(json, JsonObject.class); - - String innerJsonString; - - if (resolution.equals("H")) { - innerJsonString = outerJson.get("getDatIResult").getAsString(); - } else { - innerJsonString = outerJson.get("getDatResult").getAsString(); - } - - JsonObject innerJson = gson.fromJson(innerJsonString, JsonObject.class); - - JsonArray dataInputValues = innerJson.getAsJsonArray("DataInputValues"); - - List openList = new ArrayList<>(); - List highList = new ArrayList<>(); - List lowList = new ArrayList<>(); - List closeList = new ArrayList<>(); - List volumeList = new ArrayList<>(); - List dateList = new ArrayList<>(); - - Map latestDataMap = new LinkedHashMap<>(); - - if (dataInputValues.size() > 0) { - JsonObject dataObject = dataInputValues.get(0).getAsJsonObject(); - - JsonArray openArray = dataObject.getAsJsonArray("OpenData"); - JsonArray highArray = dataObject.getAsJsonArray("HighData"); - JsonArray lowArray = dataObject.getAsJsonArray("LowData"); - JsonArray closeArray = dataObject.getAsJsonArray("CloseData"); - JsonArray volumeArray = dataObject.getAsJsonArray("VolumeData"); - JsonArray dateArray = dataObject.getAsJsonArray("DateData"); - - if (resolution.equals("H")) { - openArray.forEach(o -> addToListDouble(openList, o.getAsJsonObject().get("Open").getAsString())); - highArray.forEach(h -> addToListDouble(highList, h.getAsJsonObject().get("High").getAsString())); - lowArray.forEach(l -> addToListDouble(lowList, l.getAsJsonObject().get("Low").getAsString())); - closeArray.forEach(c -> addToListDouble(closeList, c.getAsJsonObject().get("Close").getAsString())); - volumeArray.forEach(v -> addToList(volumeList, v.getAsJsonObject().get("Volume").getAsString())); - dateArray.forEach(d -> addDatesToList(dateList, d.getAsJsonObject().get("Date").getAsString())); - } else { - for (int i = 0; i < openArray.size(); i++) { - String open = openArray.get(i).getAsJsonObject().get("Open").getAsString(); - String high = highArray.get(i).getAsJsonObject().get("High").getAsString(); - String low = lowArray.get(i).getAsJsonObject().get("Low").getAsString(); - String close = closeArray.get(i).getAsJsonObject().get("Close").getAsString(); - String volume = volumeArray.get(i).getAsJsonObject().get("Volume").getAsString(); - String dateStr = dateArray.get(i).getAsJsonObject().get("Date").getAsString(); - - Long timestamp = convertToTimestamp(dateStr); - - String dateTimeKey = dateStr; - - JsonObject dataJson = new JsonObject(); - dataJson.addProperty("Open", open); - dataJson.addProperty("High", high); - dataJson.addProperty("Low", low); - dataJson.addProperty("Close", close); - dataJson.addProperty("Volume", volume); - dataJson.addProperty("Date", dateStr); - - latestDataMap.put(dateTimeKey, dataJson); - } - - latestDataMap.forEach((key, value) -> { - // Lấy giá trị các trường Open, High, Low, Close, Volume, Date từ dữ liệu - String openData = value.get("Open").getAsString(); - String highData = value.get("High").getAsString(); - String lowData = value.get("Low").getAsString(); - String closeData = value.get("Close").getAsString(); - String volumeData = value.get("Volume").getAsString(); - String dateData = value.get("Date").getAsString(); - - // Tách các giá trị trong chuỗi bằng dấu phẩy - String[] openValues = openData.split(","); - String[] highValues = highData.split(","); - String[] lowValues = lowData.split(","); - String[] closeValues = closeData.split(","); - String[] volumeValues = volumeData.split(","); - String[] dateValues = dateData.split(","); - - for (int i = 0; i < openValues.length; i++) { - double open = Double.parseDouble(openValues[i]); - double high = Double.parseDouble(highValues[i]); - double low = Double.parseDouble(lowValues[i]); - double close = Double.parseDouble(closeValues[i]); - long volume = Math.round(Double.parseDouble(volumeValues[i])); - long timestamp = convertToTimestamp(dateValues[i]); - - int index = dateList.indexOf(timestamp); - if (index == -1) { - dateList.add(timestamp); - openList.add(open); - highList.add(high); - lowList.add(low); - closeList.add(close); - volumeList.add(volume); - } else { - openList.set(index, open); - highList.set(index, high); - lowList.set(index, low); - closeList.set(index, close); - volumeList.set(index, volume); - } - } - }); - } - - StockHistoryResponse response = new StockHistoryResponse(); - response.setS("ok"); - response.setT(dateList); - response.setO(openList); - response.setH(highList); - response.setL(lowList); - response.setC(closeList); - response.setV(volumeList); - -// if (resolution.equals("H")) { -// Set desiredTimes = new HashSet<>(Arrays.asList("09:30", "10:30", "11:30", "12:30", "13:30", "14:30")); -// -// // Filter data based on timestamps -// SimpleDateFormat sdf = new SimpleDateFormat("HH:mm"); -// List indicesToKeep = new ArrayList<>(); -// for (int i = 0; i < dateList.size(); i++) { -// String time = sdf.format(new Date(dateList.get(i) * 1000)); -// if (desiredTimes.contains(time)) { -// indicesToKeep.add(i); -// } -// } -// -// // Filter corresponding data -// List filteredT = indicesToKeep.stream().map(dateList::get).collect(Collectors.toList()); -// List filteredO = indicesToKeep.stream().map(openList::get).collect(Collectors.toList()); -// List filteredH = indicesToKeep.stream().map(highList::get).collect(Collectors.toList()); -// List filteredL = indicesToKeep.stream().map(lowList::get).collect(Collectors.toList()); -// List filteredC = indicesToKeep.stream().map(closeList::get).collect(Collectors.toList()); -// List filteredV = indicesToKeep.stream().map(volumeList::get).collect(Collectors.toList()); -// response.setT(filteredT); -// response.setO(filteredO); -// response.setH(filteredH); -// response.setL(filteredL); -// response.setC(filteredC); -// response.setV(filteredV); -// } - return ResponseEntity.ok(response); - } - } - - // 向外部API发起请求,并获取响应 - StockHistoryRequest request = new StockHistoryRequest(); - request.setSymbol(symbol); - Long to = null; - Long from = null; - int countback = 5; - if(StringUtils.equals("H",resolution)){ - to = (long) (System.currentTimeMillis() / 1000); - from = to - (10 * 60 * 60 ); - countback = 328; - request.setResolution("60"); - }else if(StringUtils.equals("D",resolution)){ - to = (long) (System.currentTimeMillis() / 1000); - from = to - (2 * 30 * 24 * 60 * 60 ); - countback = 730; - request.setResolution("1D"); - } else if (StringUtils.equals("W", resolution)) { - to = (long) (System.currentTimeMillis() / 1000); - from = to - (7 * 24 * 60 * 60); - countback = 730; - request.setResolution("1W"); - } else if (StringUtils.equals("M", resolution)) { - to = (long) (System.currentTimeMillis() / 1000); - from = to - (15 * 30 * 24 * 60 * 60); - countback = 730; - request.setResolution("1D"); - } - - request.setFrom(from); - request.setTo(to); - request.setCountback(countback); - request.setCurrencyCode("INR"); - String apiUrl = buildApiUrl(request); - log.info("request url:" + apiUrl); - StockHistoryResponse response = null; - int maxRetries = 3; - int retryCount = 0; - - while (response == null && retryCount < maxRetries) { - try { - if (StringUtils.equals("M", resolution)) { - CloseableHttpClient client = HttpClients.createDefault(); - HttpGet req = new HttpGet(apiUrl); - HttpResponse resp = client.execute(req); - String jsonResponse = EntityUtils.toString(resp.getEntity(), "UTF-8"); - ObjectMapper mapper = new ObjectMapper(); - JsonNode rootNode = mapper.readTree(jsonResponse); - - JsonNode timeNode = rootNode.get("t"); - JsonNode lowNode = rootNode.get("l"); - JsonNode highNode = rootNode.get("h"); - JsonNode openNode = rootNode.get("o"); - JsonNode closeNode = rootNode.get("c"); - JsonNode volumeNote = rootNode.get("v"); - - List stocks = new ArrayList<>(); - for (int i = 0; i < timeNode.size(); i++) { - long timestamp = timeNode.get(i).asLong(); - double closePrice = closeNode.get(i).asDouble(); - double openPrice = openNode.get(i).asDouble(); - double volume = volumeNote.get(i).asDouble(); - double low = lowNode.get(i).asDouble(); - double high = highNode.get(i).asDouble(); - - stocks.add(new StockChartDto(timestamp, openPrice, high, low, closePrice, volume)); - } - - Map> groupedByMonth = stocks.stream() - .collect(Collectors.groupingBy( - sp -> Instant.ofEpochSecond(sp.getTimestamp()) - .atZone(ZoneId.systemDefault()) - .toLocalDate() - .format(DateTimeFormatter.ofPattern("yyyy-MM")), - LinkedHashMap::new, - Collectors.toList() - )); - - List timestamps = new ArrayList<>(); - List opens = new ArrayList<>(); - List closes = new ArrayList<>(); - List highs = new ArrayList<>(); - List lows = new ArrayList<>(); - List volumes = new ArrayList<>(); - - response = new StockHistoryResponse(); - groupedByMonth.forEach((month, prices) -> { - double open = prices.get(0).getOpen(); - double close = prices.get(prices.size() - 1).getClose(); - double high = prices.stream().mapToDouble(StockChartDto::getHigh).max().orElse(0); - double low = prices.stream().mapToDouble(StockChartDto::getLow).min().orElse(0); - double volume = prices.stream().mapToDouble(StockChartDto::getVolume).sum(); - - long timestamp = YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyy-MM")).atDay(1).atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli() / 1000; - timestamps.add(timestamp); - opens.add(open); - closes.add(close); - highs.add(high); - lows.add(low); - volumes.add((long) volume); - - System.out.println("Month: " + month); - System.out.println("Open: " + open + ", Close: " + close + ", High: " + high + ", Low: " + low); - }); - - response.setS("ok"); - response.setT(timestamps); - response.setL(lows); - response.setH(highs); - response.setO(opens); - response.setC(closes); - response.setV(volumes); - - } else { - response = restTemplate.getForObject(apiUrl, StockHistoryResponse.class); - } - } catch (RestClientException e) { - // Log the exception or perform any other error handling - log.error("Error while making API request. Retrying... (Retry count: {})", retryCount + 1); - - // Increment the retry count - retryCount++; - // Add some delay before the next retry (you can adjust this as needed) - try { - Thread.sleep(300); // 1 second delay - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } catch (ClientProtocolException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - if (response != null && !response.getS().equals("error")) { -// setResponse(response, resolution); - // API request successful, return the response - return ResponseEntity.ok(response); - } else { - try { - MoneyStock moneyStock = moneyStockRepository.findOne((QMoneyStockPO.moneyStockPO.moneyScId.eq(symbol)) - .and(QMoneyStockPO.moneyStockPO.isLock.eq(0)) - .and(QMoneyStockPO.moneyStockPO.isShow.eq(0))) - .orElse(null); - - if (moneyStock != null && moneyStock.getNseIndiaChartId() != null && !moneyStock.getNseIndiaChartId().isEmpty()) { - request.setSymbol(moneyStock.getNseIndiaChartId()); - response = NseIndiaRequest.stockKLineFromHttp(request, resolution); - return ResponseEntity.ok(response); - } - } catch (Exception e) { - log.error("Failed to get data from nseindia.", e.getMessage()); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); - } - - // All retries failed, return an error response - log.error("Failed to get a successful response after {} retries.", maxRetries); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); - } - // 返回响应 + return ResponseEntity.ok(moneyApiService.getStockHistory(symbol, interval, amount)); } @GetMapping({"/market/api/market/stock/optional", "/api/market/stock/optional"}) diff --git a/src/main/java/cn/stock/market/web/StockApiController.java b/src/main/java/cn/stock/market/web/StockApiController.java index 3d25e95..16bb8d9 100644 --- a/src/main/java/cn/stock/market/web/StockApiController.java +++ b/src/main/java/cn/stock/market/web/StockApiController.java @@ -249,8 +249,12 @@ public class StockApiController { public ServerResponse getIndiaIndexByToday() { String INDEX_CODE = "TODAY_INDEX"; return RequestCacheUtils.cache("getIndiaIndexByToday.do", INDEX_CODE,6000, (string) -> { - return this.stockService.getIndexByBtoday(); - }); + try { + return this.stockService.getIndexByBtoday(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); } @RequestMapping({"getIndiaIndexByEttech.do"}) @ApiOperation(value = "印度--获取指定指数信息-Ettech", httpMethod = "GET") diff --git a/src/main/java/cn/stock/market/web/config/Config.java b/src/main/java/cn/stock/market/web/config/Config.java index 7349203..cd18242 100644 --- a/src/main/java/cn/stock/market/web/config/Config.java +++ b/src/main/java/cn/stock/market/web/config/Config.java @@ -39,4 +39,5 @@ public class Config { String aliyunAccessKeyId; String aliyunAccessKeySecret; String aliyunAppCode; + String stockUrlPrefix; } diff --git a/src/main/java/cn/stock/market/web/service/MoneyApiService.java b/src/main/java/cn/stock/market/web/service/MoneyApiService.java new file mode 100644 index 0000000..5afb7c3 --- /dev/null +++ b/src/main/java/cn/stock/market/web/service/MoneyApiService.java @@ -0,0 +1,120 @@ +package cn.stock.market.web.service; + +import cn.qutaojing.common.utils.SpringUtils; +import cn.stock.market.domain.basic.entity.Stock; +import cn.stock.market.domain.basic.repository.StockRepository; +import cn.stock.market.dto.*; +import cn.stock.market.web.config.Config; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.ArrayList; + +@Service +public class MoneyApiService { + @Autowired + private ObjectMapper objectMapper; + @Autowired + private StockRepository stockRepository; + + public StockHistoryResponse getStockHistory(String symbol, String interval, Integer amount) { + Stock stock = stockRepository.findStockByCode(symbol); + if (stock != null) { + RawStockApiResponse rawData = fetchChartData(symbol, interval, amount); + return convertToHistoryResponse(rawData); + } else { + StockHistoryResponse stockHistoryResponse = new StockHistoryResponse(); + stockHistoryResponse.setS("fail!"); + return stockHistoryResponse; + } + } + + private RawStockApiResponse fetchChartData(String symbol, String interval, Integer amount) { + Config config = SpringUtils.getBean(Config.class); + String url = config.getStockUrlPrefix() + "/api/ger-market/chart?symbol=" + symbol + "&interval=" + interval; + if (amount != null) { + url += "&amount=" + amount; + } + + HttpHeaders headers = new HttpHeaders(); + headers.add("accept", "application/json, text/plain, */*"); + headers.add("accept-language", "en-US,en;q=0.9,vi;q=0.8"); + headers.add("origin", "https://moneytj.com"); + headers.add("referer", "https://moneytj.com/"); + headers.add("sec-ch-ua", "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\""); + headers.add("sec-ch-ua-mobile", "?0"); + headers.add("sec-ch-ua-platform", "\"Windows\""); + headers.add("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"); + + HttpEntity entity = new HttpEntity<>(headers); + RestTemplate restTemplate = new RestTemplate(); + + try { + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class); + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(response.getBody(), RawStockApiResponse.class); + } catch (Exception e) { + e.printStackTrace(); + return new RawStockApiResponse(); + } + } + + public StockQuoteData getSingleStockQuote(String symbol) { + Config config = SpringUtils.getBean(Config.class); + String url = config.getStockUrlPrefix() + "/api/ger-market/stocks/query-list?symbols=" + symbol; + + HttpHeaders headers = new HttpHeaders(); + headers.add("accept", "application/json, text/plain, */*"); + headers.add("accept-language", "en-US,en;q=0.9,vi;q=0.8"); + headers.add("origin", "https://moneytj.com"); + headers.add("referer", "https://moneytj.com/"); + headers.add("sec-ch-ua", "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\""); + headers.add("sec-ch-ua-mobile", "?0"); + headers.add("sec-ch-ua-platform", "\"Windows\""); + headers.add("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"); + + HttpEntity entity = new HttpEntity<>(headers); + RestTemplate restTemplate = new RestTemplate(); + + try { + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class); + ObjectMapper mapper = new ObjectMapper(); + StockQuoteResponse quoteResponse = mapper.readValue(response.getBody(), StockQuoteResponse.class); + if (quoteResponse != null && quoteResponse.getData() != null && !quoteResponse.getData().isEmpty()) { + return quoteResponse.getData().get(0); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + private StockHistoryResponse convertToHistoryResponse(RawStockApiResponse apiResponse) { + StockHistoryResponse response = new StockHistoryResponse(); + response.setS("ok"); + response.setT(new ArrayList<>()); + response.setO(new ArrayList<>()); + response.setH(new ArrayList<>()); + response.setL(new ArrayList<>()); + response.setC(new ArrayList<>()); + response.setV(new ArrayList<>()); + + for (StockDataDto item : apiResponse.getData()) { + response.getT().add(item.getTime()); + response.getO().add(item.getOpen()); + response.getH().add(item.getMax()); + response.getL().add(item.getMin()); + response.getC().add(item.getClose()); + response.getV().add(item.getVolume()); + } + + return response; + } +} diff --git a/src/main/resources/application-base-alpha.yml b/src/main/resources/application-base-alpha.yml index 4ae3fcb..b2e9501 100644 --- a/src/main/resources/application-base-alpha.yml +++ b/src/main/resources/application-base-alpha.yml @@ -28,7 +28,7 @@ spring: stock: driver-class-name: com.mysql.cj.jdbc.Driver #url: jdbc:mysql://129.226.172.67:3306/vip_huananyong_c?useUnicode=true&characterEncoding=utf-8&useSSL=true&verifyServerCertificate=true&requireSSL=true&clientCertificateKeyStoreUrl=classpath:keystoremysql&clientCertificateKeyStorePassword=abs1234567890&trustCertificateKeyStoreUrl=classpath:truststoremysql&trustCertificateKeyStorePassword=abs1234567890 - url: jdbc:mysql://124.156.133.209:33306/vip_huananyong_c?useUnicode=true&characterEncoding=utf-8&useSSL=true + url: jdbc:mysql://43.153.142.41:33306/vip_huananyong_c?useUnicode=true&characterEncoding=utf-8&useSSL=true username: root password: 33BsUUcnXRYgwt maxActive: 500 @@ -42,6 +42,8 @@ gugudataAppKey: K5LZKV8KAYM4 aliyunAccessKeyId: LTAI5tJi2z8cegG8fTW7BSQu aliyunAccessKeySecret: fnCI9LUcqLuH7D6nkhvSHQMob1JSm8 aliyunAppCode: 75ef2615da614eaaa71e2e2058fc53b0 + +stockUrlPrefix: https://apinode-dgdev.moneytj.com # 具体看类:CloudStorageConfig oss: type: 2 # 类型 1:七牛 2:阿里云 3:腾讯云