Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
dengli
2024-01-10 09:46:00 +08:00
19 changed files with 1609 additions and 1 deletions

View File

@@ -0,0 +1,760 @@
package cn.stock.market.web;
import cn.stock.market.MoneyStockSuggestDTO;
import cn.stock.market.domain.basic.entity.MoneyStock;
import cn.stock.market.domain.basic.repository.MoneyStockRepository;
import cn.stock.market.dto.StockHistoryRequest;
import cn.stock.market.dto.StockHistoryResponse;
import cn.stock.market.infrastructure.db.po.QMoneyStockPO;
import cn.stock.market.utils.RequestCacheUtils;
import cn.stock.market.utils.ServerResponse;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* @author gs
* @date 2024/1/4 10:25
*/
@RestController
@Api(value = "/MoneyApiController", tags = "money股票行情")
@Slf4j
public class MoneyApiController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private MoneyStockRepository moneyStockRepository;
private static final String EXTERNAL_API_URL = "https://priceapi.moneycontrol.com/techCharts/indianMarket/stock/history";
@ApiOperation(value = "股票详情信息",httpMethod = "GET")
@ApiImplicitParams({
@ApiImplicitParam(name="stockType",value = "BSE或者NSE"),
@ApiImplicitParam(name="symbol",value = "scId值"),
@ApiImplicitParam(name="id",value = "id值"),
})
@ApiResponses(value = {
@ApiResponse(code = 200, message = "" +
"HN: 公司的名称(हिन्दी में - Hindi\n" +
"HP: 当前股价\n" +
"DISPID: 行业/部门代码\n" +
"cl5yPerChange: 过去5年的百分比变化\n" +
"BIDQ 和 BIDP: 买入报价和数量\n" +
"newSubsector: 新的子部门\n" +
"cl1mDt: 过去1个月的数据日期\n" +
"P_C: 市盈率\n" +
"YR: 年份\n" +
"cl2yVal: 过去2年的数值\n" +
"SC_SUBSEC: 子部门\n" +
"sc_ttm_cons 和 SC_TTM: 市盈率\n" +
"clYtdVal: 本年度截至目前的数值\n" +
"cl6mDt: 过去6个月的数据日期\n" +
"cl1wVal: 过去1周的数值\n" +
"lastupd_epoch: 最后更新时间的 Epoch 时间戳\n" +
"cl1mChange: 过去1个月的数值变化\n" +
"cl3mChange: 过去3个月的数值变化\n" +
"pricecurrent: 当前股价\n" +
"150DayAvg: 过去150天的平均值\n" +
"clYtdChange: 本年度截至目前的数值变化\n" +
"tot_buy_qty: 总买入数量\n" +
"clYtdDt: 本年度截至目前的数据日期\n" +
"Group: 组别\n" +
"ACCOD: 账户代码\n" +
"cl3yDt: 过去3年的数据日期\n" +
"LTHDate: 最高值的日期\n" +
"cl3mDt: 过去3个月的数据日期\n" +
"DYCONS: 息率\n" +
"NSEID: NSE 股票代码\n" +
"52H: 52周最高价\n" +
"AvgDelVolPer_8day: 过去8天的平均成交量百分比\n" +
"52L: 52周最低价\n" +
"BV: 每股净值\n" +
"MKTLOT: 市场手数\n" +
"DVolAvg20: 过去20天的平均成交量\n" +
"OFFERP 和 OFFERQ: 卖出报价和数量\n" +
"cl6mVal: 过去6个月的数值\n" +
"AvgVolQtyDel_20day: 过去20天的平均成交量数量\n" +
"cl1mVal: 过去1个月的数值\n" +
"PBCONS: 市净率\n" +
"5DayAvg: 过去5天的平均值\n" +
"AvgDelVolPer_3day: 过去3天的平均成交量百分比\n" +
"sessionId: 会话ID\n" +
"TID: 交易ID\n" +
"SHRS: 股本总额\n" +
"50DayAvg: 过去50天的平均值\n" +
"cl3yChange: 过去3年的数值变化\n" +
"im_scid: 未知字段\n" +
"DVolAvg10: 过去10天的平均成交量\n" +
"AvgVolQtyTraded_20day: 过去20天的平均成交量交易数量\n" +
"symbol: 股票符号\n" +
"LP: 最后价格\n" +
"lower_circuit_limit 和 upper_circuit_limit: 跌停价和涨停价\n" +
"ty: 未知字段\n" +
"cl1wDt: 过去1周的数据日期\n" +
"cl5yDt: 过去5年的数据日期\n" +
"IND_PE: 行业市盈率\n" +
"cl1yPerChange: 过去1年的百分比变化\n" +
"cl3yVal: 过去3年的数值\n" +
"52HDate: 52周最高价的日期\n" +
"OPN: 开盘价\n" +
"cl1mPerChange: 过去1个月的百分比变化\n" +
"DTTIME: 交易时间\n" +
"SC_FULLNM: 完整的行业/部门名称\n" +
"DVolAvg5: 过去5天的平均成交量\n" +
"LTLDate: 最低值的日期\n" +
"DY: 息率\n" +
"etf: 是否为ETF\n" +
"clYtdPerChange: 本年度截至目前的百分比变化\n" +
"isinid: ISIN 编号\n" +
"cl2yDt: 过去2年的数据日期\n" +
"slug: 股票的 slug\n" +
"AvgDelVolPer_20day: 过去20天的平均成交量百分比\n" +
"cl5yVal: 过去5年的数值\n" +
"market_state: 市场状态\n" +
"cl2yPerChange: 过去2年的百分比变化\n" +
"pricechange: 价格变化\n" +
"AvgDelVolPer_5day: 过去5天的平均成交量百分比\n" +
"200DayAvg: 过去200天的平均值\n" +
"VOL: 成交量\n" +
"pricepercentchange: 价格百分比变化\n" +
"exchange: 交易所\n" +
"sc_mapindex: 行业/部门地图索引\n" +
"DVolAvg30: 过去30天的平均成交量\n" +
"cl1wChange: 过去1周的数值变化\n" +
"AVGP: 平均价格\n" +
"LTH: 最高价\n" +
"cl3yPerChange: 过去3年的百分比变化\n" +
"LTL: 最低价\n" +
"cl3mPerChange: 过去3个月的百分比变化\n" +
"PECONS: 市盈率\n" +
"priceprevclose: 前一交易日的收盘价\n" +
"30DayAvg: 过去30天的平均", response = JSONObject.class),
})
@GetMapping("/api/market/money/getStockDetail")
@ResponseBody
public ServerResponse getStockDetail(@RequestParam String stockType, @RequestParam String symbol ) {
String url = String.format("https://priceapi.moneycontrol.com/pricefeed/%s/equitycash/%s",stockType,symbol);
MoneyStock moneyStock = moneyStockRepository.findOne(QMoneyStockPO.moneyStockPO.stockType.eq(stockType).and(QMoneyStockPO.moneyStockPO.moneyScId.eq(symbol))).orElse(null);
/* if(moneyStock==null){
return ServerResponse.createByErrorMsg("没有找到该股票");
}*/
// 设置重试次数
int maxRetries = 3;
for (int retry = 1; retry <= maxRetries; retry++) {
try {
ResponseEntity<String> 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("company"));
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());
}
}
return ServerResponse.createBySuccess(json1);
}
} catch (Exception e) {
}
// 如果不是最后一次重试,则等待一段时间再进行下一次重试
if (retry < maxRetries) {
try {
// 1秒钟
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
return null;
}
private static List<MoneyStockSuggestDTO> nseActives() {
List<MoneyStockSuggestDTO> list = new ArrayList<>();
String url = "https://www.moneycontrol.com/stocks/marketstats/nse-mostactive-stocks/nifty-50-9/";
try {
Document doc = Jsoup.connect(url).get();
int size = doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div.bsr_table.hist_tbl_hm > table > tbody > tr").size();
for (int i =1;i<=size;i++){
MoneyStockSuggestDTO dto = new MoneyStockSuggestDTO();
Element company_a = doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child("+i+") > td.PR > span > a").first();
if (company_a != null) {
String stockUrl = company_a.attr("href");
String stockName = company_a.text();
dto.setStockName(stockName);
dto.setStockUrl(stockUrl);
}
String highPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child("+i+") > td:nth-child(2)").first());
dto.setHighPrice(highPrice);
String lowPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(3)").first());
dto.setLowPrice(lowPrice);
String lastPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(4)").first());
dto.setLastPrice(lastPrice);
String changePercent = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(5)").first());
dto.setChangePercent(changePercent);
dto.setStockType("nse");
list.add(dto);
log.info("---------------------------------------------" + i);
}
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
private static List<MoneyStockSuggestDTO> bseActives() {
List<MoneyStockSuggestDTO> list = new ArrayList<>();
String url = "https://www.moneycontrol.com/stocks/marketstats/bsemact1/index.php";
try {
Document doc = Jsoup.connect(url).get();
int size = doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr").size();
System.err.println(size);
for (int i =1;i<=size;i++){
MoneyStockSuggestDTO dto = new MoneyStockSuggestDTO();
Element company_a = doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child("+i+") > td.PR > span > a").first();
if (company_a != null) {
String stockUrl = company_a.attr("href");
String stockName = company_a.text();
dto.setStockName(stockName);
dto.setStockUrl(stockUrl);
}
String highPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(2)").first());
dto.setHighPrice(highPrice);
String lowPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(3)").first());
dto.setLowPrice(lowPrice);
String lastPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(4)").first());
dto.setLastPrice(lastPrice);
String prevClosePrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(5)").first());
dto.setPrevClosePrice(prevClosePrice);
String changePercent = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(6)").first());
dto.setChangePercent(changePercent);
dto.setStockType("bse");
list.add(dto);
log.info("---------------------------------------------" + i);
}
} catch (IOException e) {
log.error("occur Exception",e);
}
return list;
}
private static List<MoneyStockSuggestDTO> bseGainer() {
String url = "https://www.moneycontrol.com/stocks/marketstats/bse-gainer/sensex_4/";
List<MoneyStockSuggestDTO> list = Lists.newArrayList();
try {
Document doc = Jsoup.connect(url).get();
int size = doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr").size();
for (int i =1;i<=size;i++){
Element company_a = doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child("+i+") > td.PR > span > a").first();
MoneyStockSuggestDTO dto = new MoneyStockSuggestDTO();
if (company_a != null) {
String stockUrl = company_a.attr("href");
String stockName = company_a.text();
dto.setStockName(stockName);
dto.setStockUrl(stockUrl);
}
String highPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(2)").first());
dto.setHighPrice(highPrice);
String lowPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(3)").first());
dto.setLowPrice(lowPrice);
String lastPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(4)").first());
dto.setLastPrice(lastPrice);
String prevClosePrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(5)").first());
dto.setPrevClosePrice(prevClosePrice);
String change = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(6)").first());
dto.setChange(change);
String changePercent = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(7)").first());
dto.setChangePercent(changePercent);
dto.setStockType("bse");
list.add(dto);
log.info("---------------------------------------------" + i);
}
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
private static List<MoneyStockSuggestDTO> nseGainer() {
String url = "https://www.moneycontrol.com/stocks/marketstats/nsegainer/index.php";
List<MoneyStockSuggestDTO> list = Lists.newArrayList();
try {
Document doc = Jsoup.connect(url).get();
int size = doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr").size();
for (int i =1;i<=size;i++){
MoneyStockSuggestDTO dto = new MoneyStockSuggestDTO();
Element company_a = doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child("+i+") > td.PR > span > h3 > a").first();
if (company_a != null) {
String stockUrl = company_a.attr("href");
String stockName = company_a.text();
dto.setStockUrl(stockUrl);
dto.setStockName(stockName);
}
String highPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(2)").first());
dto.setHighPrice(highPrice);
String lowPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(3)").first());
dto.setLowPrice(lowPrice);
String lastPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(4)").first());
dto.setLastPrice(lastPrice);
String prevClosePrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(5)").first());
dto.setPrevClosePrice(prevClosePrice);
String change = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(6)").first());
dto.setChange(change);
String changePercent = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(7)").first());
dto.setChangePercent(changePercent);
dto.setStockType("nse");
list.add(dto);
}
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
private static List<MoneyStockSuggestDTO> nseTopLoser() {
String url = "https://www.moneycontrol.com/stocks/marketstats/nseloser/index.php";
List<MoneyStockSuggestDTO> list = Lists.newArrayList();
try {
Document doc = Jsoup.connect(url).get();
int size = doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr").size();
for (int i =1;i<=size;i++){
MoneyStockSuggestDTO dto = new MoneyStockSuggestDTO();
Element company_a = doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child("+i+") > td.PR > span > h3 > a").first();
if (company_a != null) {
String stockUrl = company_a.attr("href");
String stockName = company_a.text();
dto.setStockUrl(stockUrl);
dto.setStockName(stockName);
}
String highPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(2)").first());
dto.setHighPrice(highPrice);
String lowPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(3)").first());
dto.setLowPrice(lowPrice);
String lastPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(4)").first());
dto.setLastPrice(lastPrice);
String prevClosePrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(5)").first());
dto.setPrevClosePrice(prevClosePrice);
String change = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(6)").first());
dto.setChange(change);
String changePercent = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(7)").first());
dto.setChangePercent(changePercent);
dto.setStockType("nse");
list.add(dto);
log.info("---------------------------------------------" + i);
}
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
private static List<MoneyStockSuggestDTO> bseTopLoser() {
String url = "https://www.moneycontrol.com/stocks/marketstats/bse-loser/sensex_4/";
List<MoneyStockSuggestDTO> list = Lists.newArrayList();
try {
Document doc = Jsoup.connect(url).get();
int size = doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr").size();
for (int i =1;i<=size;i++){
Element company_a = doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td.PR > span > a").first();
MoneyStockSuggestDTO dto = new MoneyStockSuggestDTO();
if (company_a != null) {
String stockUrl = company_a.attr("href");
String stockName = company_a.text();
dto.setStockName(stockName);
dto.setStockUrl(stockUrl);
}
String highPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(2)").first());
dto.setHighPrice(highPrice);
String lowPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(3)").first());
dto.setLowPrice(lowPrice);
String lastPrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(4)").first());
dto.setLastPrice(lastPrice);
String prevClosePrice = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(5)").first());
dto.setPrevClosePrice(prevClosePrice);
String change = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(6)").first());
dto.setChange(change);
String changePercent = getTextOrEmpty(doc.select("#mc_content > section > section > div.clearfix.stat_container > div.columnst.FR.wbg.brdwht > div > div > div.bsr_table.hist_tbl_hm > table > tbody > tr:nth-child(" + i + ") > td:nth-child(7)").first());
dto.setChangePercent(changePercent);
dto.setStockType("bse");
list.add(dto);
log.info("---------------------------------------------" + i);
}
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
// 辅助方法,获取元素的文本或返回空字符串
private static String getTextOrEmpty(Element element) {
return element != null ? element.text() : "";
}
@ApiOperation(value = "股票推荐TopGainer",httpMethod = "GET")
@ApiImplicitParams({
@ApiImplicitParam(name="stockType",value = "BSE或者NSE"),
})
@ApiResponses(value = {
@ApiResponse(code = 200, message = "" +
"股票推荐相关: top gainer", response = JSONObject.class),
})
@GetMapping("/api/market/money/getTopGainer")
@ResponseBody
public List<MoneyStockSuggestDTO> getTopGainer(@RequestParam String stockType) {
List<MoneyStockSuggestDTO> moneyStockSuggestDTOS = null;
// 尝试从缓存中获取结果
moneyStockSuggestDTOS = gainerStockSuggestCache.getIfPresent(stockType);
if (moneyStockSuggestDTOS == null) {
// 缓存未命中,执行业务查询
if (StringUtils.equals(stockType, "nse")) {
moneyStockSuggestDTOS = nseGainer();
} else if (StringUtils.equals(stockType, "bse")) {
moneyStockSuggestDTOS = bseGainer();
}
moneyStockSuggestDTOS = moneyStockSuggestDTOS.stream().filter(f->StringUtils.isNotBlank(f.getStockName())).collect(Collectors.toList());
if(CollectionUtils.isNotEmpty(moneyStockSuggestDTOS)){
List<String> selfUlrList = moneyStockSuggestDTOS.stream().map(MoneyStockSuggestDTO::getStockName).collect(Collectors.toList());
if(CollectionUtils.isNotEmpty(selfUlrList)){
List<MoneyStock> all = moneyStockRepository.findAll(QMoneyStockPO.moneyStockPO.stockName.in(selfUlrList));
if(CollectionUtils.isNotEmpty(all)){
moneyStockSuggestDTOS.stream().filter(f->all.stream().anyMatch(s->s.getStockName().equals(f.getStockName())))
.forEach(f->f.setScId(all.stream().filter(s->s.getStockName().equals(f.getStockName())).findFirst().orElse(null).getMoneyScId()));
}
}
gainerStockSuggestCache.put(stockType, moneyStockSuggestDTOS);
}
// 将结果放入缓存
}
return moneyStockSuggestDTOS;
}
@ApiOperation(value = "股票推荐TopLoser",httpMethod = "GET")
@ApiImplicitParams({
@ApiImplicitParam(name="stockType",value = "BSE或者NSE"),
})
@ApiResponses(value = {
@ApiResponse(code = 200, message = "" +
"股票推荐相关: TopLoser", response = JSONObject.class),
})
@GetMapping("/api/market/money/getTopLoser")
@ResponseBody
public List<MoneyStockSuggestDTO> getTopLoser(@RequestParam String stockType) {
List<MoneyStockSuggestDTO> moneyStockSuggestDTOS = null;
moneyStockSuggestDTOS = loserStockSuggestCache.getIfPresent(stockType);
if(null==moneyStockSuggestDTOS){
if(StringUtils.equals(stockType,"nse")){
moneyStockSuggestDTOS = nseTopLoser();
}else if(StringUtils.equals(stockType,"bse")){
moneyStockSuggestDTOS = bseTopLoser();
}
moneyStockSuggestDTOS = moneyStockSuggestDTOS.stream().filter(f->StringUtils.isNotBlank(f.getStockName())).collect(Collectors.toList());
if(CollectionUtils.isNotEmpty(moneyStockSuggestDTOS)){
moneyStockSuggestDTOS.stream().forEach(f->f.setDispId(extractLastSegment(f.getStockUrl())));
List<String> selfUlrList = moneyStockSuggestDTOS.stream().map(MoneyStockSuggestDTO::getStockName).collect(Collectors.toList());
if(CollectionUtils.isNotEmpty(selfUlrList)){
List<MoneyStock> all = moneyStockRepository.findAll(QMoneyStockPO.moneyStockPO.stockName.in(selfUlrList));
if(CollectionUtils.isNotEmpty(all)){
moneyStockSuggestDTOS.stream().filter(f->all.stream().anyMatch(s->s.getStockName().equals(f.getStockName())))
.forEach(f->f.setScId(all.stream().filter(s->s.getStockName().equals(f.getStockName())).findFirst().orElse(null).getMoneyScId()));
}
List<MoneyStockSuggestDTO> noScIdList = moneyStockSuggestDTOS.stream().filter(f->StringUtils.isBlank(f.getScId())).collect(Collectors.toList());
if(CollectionUtils.isNotEmpty(noScIdList)){
List<String> dispIdList = noScIdList.stream().map(MoneyStockSuggestDTO::getDispId).collect(Collectors.toList());
List<MoneyStock> all1 = moneyStockRepository.findAll(QMoneyStockPO.moneyStockPO.selfDispId.in(dispIdList));
if(CollectionUtils.isNotEmpty(all1)){
moneyStockSuggestDTOS.stream().filter(f->all1.stream().anyMatch(s->s.getSelfDispId().equals(f.getDispId())))
.forEach(f->f.setScId(all.stream().filter(s->s.getSelfDispId().equals(f.getDispId())).findFirst().orElse(null).getMoneyScId()));
}
}
}
loserStockSuggestCache.put(stockType, moneyStockSuggestDTOS);
}
}
return moneyStockSuggestDTOS;
}
@ApiOperation(value = "股票推荐TopActive",httpMethod = "GET")
@ApiImplicitParams({
@ApiImplicitParam(name="stockType",value = "BSE或者NSE"),
})
@ApiResponses(value = {
@ApiResponse(code = 200, message = "" +
"股票推荐相关: top active", response = JSONObject.class),
})
@GetMapping("/api/market/money/getTopActives")
@ResponseBody
public List<MoneyStockSuggestDTO> getTopActive(@RequestParam String stockType) {
List<MoneyStockSuggestDTO> moneyStockSuggestDTOS = null;
moneyStockSuggestDTOS = activesStockSuggestCache.getIfPresent(stockType);
if(moneyStockSuggestDTOS ==null){
if(StringUtils.equals(stockType,"nse")){
moneyStockSuggestDTOS = nseActives();
}else if(StringUtils.equals(stockType,"bse")){
moneyStockSuggestDTOS = bseActives();
}
moneyStockSuggestDTOS = moneyStockSuggestDTOS.stream().filter(f->StringUtils.isNotBlank(f.getStockName())).collect(Collectors.toList());
if(CollectionUtils.isNotEmpty(moneyStockSuggestDTOS)){
moneyStockSuggestDTOS.stream().forEach(f->f.setDispId(extractLastSegment(f.getStockUrl())));
List<String> selfUlrList = moneyStockSuggestDTOS.stream().map(MoneyStockSuggestDTO::getStockName).collect(Collectors.toList());
if(CollectionUtils.isNotEmpty(selfUlrList)){
List<MoneyStock> all = moneyStockRepository.findAll(QMoneyStockPO.moneyStockPO.stockName.in(selfUlrList));
if(CollectionUtils.isNotEmpty(all)){
moneyStockSuggestDTOS.stream().filter(f->all.stream().anyMatch(s->s.getStockName().equals(f.getStockName())))
.forEach(f->f.setScId(all.stream().filter(s->s.getStockName().equals(f.getStockName())).findFirst().orElse(null).getMoneyScId()));
}
List<MoneyStockSuggestDTO> noScIdList = moneyStockSuggestDTOS.stream().filter(f->StringUtils.isBlank(f.getScId())).collect(Collectors.toList());
if(CollectionUtils.isNotEmpty(noScIdList)){
List<String> dispIdList = noScIdList.stream().map(MoneyStockSuggestDTO::getDispId).collect(Collectors.toList());
List<MoneyStock> all1 = moneyStockRepository.findAll(QMoneyStockPO.moneyStockPO.selfDispId.in(dispIdList));
if(CollectionUtils.isNotEmpty(all1)){
moneyStockSuggestDTOS.stream().filter(f->all1.stream().anyMatch(s->s.getSelfDispId().equals(f.getDispId())))
.forEach(f->f.setScId(all.stream().filter(s->s.getSelfDispId().equals(f.getDispId())).findFirst().orElse(null).getMoneyScId()));
}
}
}
activesStockSuggestCache.put(stockType, moneyStockSuggestDTOS);
}
}
return moneyStockSuggestDTOS;
}
@GetMapping("/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
public ResponseEntity<StockHistoryResponse> getStockHistory( @RequestParam String symbol,
@RequestParam String resolution
) {
// 向外部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 - (1 * 60 * 60 );
countback = 60;
request.setResolution("1");
}else if(StringUtils.equals("D",resolution)){
to = (long) (System.currentTimeMillis() / 1000);
from = to - (24 * 60 * 60 );
countback = 9;
request.setResolution("60");
}else if(StringUtils.equals("W",resolution)){
to = (long) (System.currentTimeMillis() / 1000);
from = to - (10 * 24 * 60 * 60 );
countback = 7;
request.setResolution("1D");
}else if(StringUtils.equals("M",resolution)){
to = (long) (System.currentTimeMillis() / 1000);
from = to - (35 * 24 * 60 * 60 );
countback = 30;
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 {
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();
}
}
}
if (response != null) {
// API request successful, return the response
return ResponseEntity.ok(response);
} else {
// 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();
}
// 返回响应
}
private String buildApiUrl(StockHistoryRequest request) {
// 构建外部API的URL
return String.format("%s?symbol=%s&resolution=%s&from=%d&to=%d&countback=%d&currencyCode=%s",
EXTERNAL_API_URL, request.getSymbol(), request.getResolution(), request.getFrom(),
request.getTo(), request.getCountback(), request.getCurrencyCode());
};
private static String extractLastSegment(String url) {
if (url == null) {
return null;
}
int lastSlashIndex = url.lastIndexOf('/');
if (lastSlashIndex != -1 && lastSlashIndex < url.length() - 1) {
return url.substring(lastSlashIndex + 1);
}
return url; // 如果没有斜杠,或者斜杠位于字符串的末尾,则返回原始字符串
}
public static void main(String[] args) {
nseGainer();
nseActives();
nseTopLoser();
bseActives();
bseGainer();
bseTopLoser();
}
private Cache<String, List<MoneyStockSuggestDTO>> gainerStockSuggestCache = CacheBuilder.newBuilder()
.maximumSize(100) // 设置缓存的最大大小
.expireAfterWrite(1, TimeUnit.HOURS) // 设置缓存条目的过期时间
.build();
private Cache<String, List<MoneyStockSuggestDTO>> loserStockSuggestCache = CacheBuilder.newBuilder()
.maximumSize(100) // 设置缓存的最大大小
.expireAfterWrite(1, TimeUnit.HOURS) // 设置缓存条目的过期时间
.build();
private Cache<String, List<MoneyStockSuggestDTO>> activesStockSuggestCache = CacheBuilder.newBuilder()
.maximumSize(100) // 设置缓存的最大大小
.expireAfterWrite(30, TimeUnit.MINUTES) // 设置缓存条目的过期时间
.build();
}