feat: fix frontend
This commit is contained in:
@@ -7,7 +7,9 @@ import io.swagger.v3.oas.models.info.License;
|
||||
import io.swagger.v3.oas.models.servers.Server;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springdoc.core.GroupedOpenApi;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -40,7 +42,7 @@ public class OpenApiConfig {
|
||||
.license(new License()
|
||||
.name("MIT License")
|
||||
.url("https://opensource.org/licenses/MIT")))
|
||||
.servers(List.of(
|
||||
.servers(Arrays.asList(
|
||||
new Server()
|
||||
.url("http://localhost:8080")
|
||||
.description("本地开发环境"),
|
||||
@@ -49,4 +51,28 @@ public class OpenApiConfig {
|
||||
.description("生产环境")
|
||||
));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi allApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("all-apis")
|
||||
.pathsToMatch("/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi stockApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("stock-apis")
|
||||
.pathsToMatch("/api/stock/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi marketApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("market-apis")
|
||||
.pathsToMatch("/api/market/**")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -167,6 +167,26 @@ public class StockController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取股票详情
|
||||
*/
|
||||
@GetMapping("/detail/{stockCode}")
|
||||
@Operation(summary = "获取股票详情")
|
||||
public Result<StockData> getStockDetail(
|
||||
@Parameter(description = "股票代码") @PathVariable String stockCode) {
|
||||
try {
|
||||
StockData stockDetail = stockService.getStockDetail(stockCode);
|
||||
if (stockDetail != null) {
|
||||
return Result.success(stockDetail);
|
||||
} else {
|
||||
return Result.error("未找到该股票的详情数据");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取股票{}详情失败", stockCode, e);
|
||||
return Result.error("获取股票详情失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索股票
|
||||
*/
|
||||
|
||||
@@ -79,6 +79,14 @@ public interface StockService {
|
||||
* @return 预测数据列表
|
||||
*/
|
||||
List<StockData> getStockPrediction(String stockCode, Integer days);
|
||||
|
||||
/**
|
||||
* 获取股票详情
|
||||
*
|
||||
* @param stockCode 股票代码
|
||||
* @return 股票详情
|
||||
*/
|
||||
StockData getStockDetail(String stockCode);
|
||||
|
||||
/**
|
||||
* 搜索股票
|
||||
|
||||
@@ -251,9 +251,174 @@ public class StockServiceImpl implements StockService {
|
||||
|
||||
@Override
|
||||
public List<StockData> getStockPrediction(String stockCode, Integer days) {
|
||||
// 这里应该调用预测模型,暂时返回空列表
|
||||
log.info("股票预测功能暂未实现,股票代码: {}, 预测天数: {}", stockCode, days);
|
||||
return new ArrayList<>();
|
||||
try {
|
||||
log.info("生成股票预测数据,股票代码: {}, 预测天数: {}", stockCode, days);
|
||||
|
||||
// 获取历史数据用于预测
|
||||
List<StockData> historyData = stockDataMapper.getStockHistoryData(stockCode, 30);
|
||||
|
||||
if (historyData.isEmpty()) {
|
||||
log.warn("未找到股票{}的历史数据,无法生成预测", stockCode);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 获取最新数据作为预测基础
|
||||
StockData latestData = historyData.get(0); // getStockHistoryData返回的是按时间降序排列
|
||||
|
||||
// 计算历史价格变化趋势
|
||||
BigDecimal avgChange = calculateAverageChange(historyData);
|
||||
BigDecimal volatility = calculateVolatility(historyData);
|
||||
|
||||
List<StockData> predictionList = new ArrayList<>();
|
||||
|
||||
// 生成预测数据
|
||||
BigDecimal currentPrice = latestData.getClosePrice();
|
||||
LocalDateTime currentDate = LocalDateTime.now();
|
||||
|
||||
for (int i = 1; i <= days; i++) {
|
||||
StockData prediction = new StockData();
|
||||
|
||||
// 基本信息
|
||||
prediction.setStockCode(stockCode);
|
||||
prediction.setStockName(latestData.getStockName());
|
||||
prediction.setTradeDate(currentDate.plusDays(i));
|
||||
|
||||
// 简单预测模型:基于历史平均变化和随机波动
|
||||
BigDecimal randomFactor = BigDecimal.valueOf((Math.random() - 0.5) * 2 * volatility.doubleValue());
|
||||
BigDecimal predictedChange = avgChange.add(randomFactor);
|
||||
|
||||
// 计算预测价格
|
||||
BigDecimal predictedPrice = currentPrice.multiply(
|
||||
BigDecimal.ONE.add(predictedChange.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
|
||||
);
|
||||
|
||||
prediction.setOpenPrice(currentPrice);
|
||||
prediction.setHighPrice(predictedPrice.multiply(BigDecimal.valueOf(1.05)));
|
||||
prediction.setLowPrice(predictedPrice.multiply(BigDecimal.valueOf(0.95)));
|
||||
prediction.setClosePrice(predictedPrice);
|
||||
|
||||
// 计算涨跌幅
|
||||
BigDecimal changeAmount = predictedPrice.subtract(currentPrice);
|
||||
BigDecimal changePercent = changeAmount.divide(currentPrice, 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
|
||||
prediction.setChangeAmount(changeAmount);
|
||||
prediction.setChangePercent(changePercent);
|
||||
|
||||
// 预测成交量(基于历史平均值)
|
||||
double avgVolume = historyData.stream()
|
||||
.map(StockData::getVolume)
|
||||
.filter(Objects::nonNull)
|
||||
.mapToLong(Long::longValue)
|
||||
.average()
|
||||
.orElse(100000L);
|
||||
|
||||
prediction.setVolume(Math.round(avgVolume * (0.8 + Math.random() * 0.4)));
|
||||
|
||||
// 预测成交额
|
||||
if (prediction.getVolume() != null) {
|
||||
prediction.setTurnover(predictedPrice.multiply(BigDecimal.valueOf(prediction.getVolume())));
|
||||
}
|
||||
|
||||
// 市值
|
||||
if (latestData.getMarketCap() != null && latestData.getClosePrice() != null && latestData.getClosePrice().compareTo(BigDecimal.ZERO) != 0) {
|
||||
prediction.setMarketCap(latestData.getMarketCap()
|
||||
.multiply(predictedPrice)
|
||||
.divide(latestData.getClosePrice(), 2, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
predictionList.add(prediction);
|
||||
currentPrice = predictedPrice; // 更新当前价格为下一天的基础
|
||||
}
|
||||
|
||||
return predictionList;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("生成股票{}预测数据失败", stockCode, e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算历史价格平均变化率
|
||||
*/
|
||||
private BigDecimal calculateAverageChange(List<StockData> historyData) {
|
||||
if (historyData.size() < 2) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
BigDecimal totalChange = BigDecimal.ZERO;
|
||||
int count = 0;
|
||||
|
||||
// 数据是按时间降序排列的,所以要反过来计算
|
||||
for (int i = historyData.size() - 1; i > 0; i--) {
|
||||
StockData current = historyData.get(i - 1); // 更新的数据
|
||||
StockData previous = historyData.get(i); // 更早的数据
|
||||
|
||||
if (current.getClosePrice() != null && previous.getClosePrice() != null && previous.getClosePrice().compareTo(BigDecimal.ZERO) != 0) {
|
||||
BigDecimal change = current.getClosePrice().subtract(previous.getClosePrice())
|
||||
.divide(previous.getClosePrice(), 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
totalChange = totalChange.add(change);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count > 0 ? totalChange.divide(BigDecimal.valueOf(count), 4, RoundingMode.HALF_UP) : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算价格波动率
|
||||
*/
|
||||
private BigDecimal calculateVolatility(List<StockData> historyData) {
|
||||
if (historyData.size() < 2) {
|
||||
return BigDecimal.valueOf(2.0); // 默认波动率
|
||||
}
|
||||
|
||||
List<BigDecimal> changes = new ArrayList<>();
|
||||
|
||||
// 数据是按时间降序排列的,所以要反过来计算
|
||||
for (int i = historyData.size() - 1; i > 0; i--) {
|
||||
StockData current = historyData.get(i - 1); // 更新的数据
|
||||
StockData previous = historyData.get(i); // 更早的数据
|
||||
|
||||
if (current.getClosePrice() != null && previous.getClosePrice() != null && previous.getClosePrice().compareTo(BigDecimal.ZERO) != 0) {
|
||||
BigDecimal change = current.getClosePrice().subtract(previous.getClosePrice())
|
||||
.divide(previous.getClosePrice(), 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
changes.add(change);
|
||||
}
|
||||
}
|
||||
|
||||
if (changes.isEmpty()) {
|
||||
return BigDecimal.valueOf(2.0);
|
||||
}
|
||||
|
||||
// 计算标准差
|
||||
BigDecimal mean = changes.stream()
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add)
|
||||
.divide(BigDecimal.valueOf(changes.size()), 4, RoundingMode.HALF_UP);
|
||||
|
||||
BigDecimal variance = changes.stream()
|
||||
.map(change -> change.subtract(mean).pow(2))
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add)
|
||||
.divide(BigDecimal.valueOf(changes.size()), 4, RoundingMode.HALF_UP);
|
||||
|
||||
return BigDecimal.valueOf(Math.sqrt(variance.doubleValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockData getStockDetail(String stockCode) {
|
||||
try {
|
||||
List<StockData> stockList = stockDataMapper.getLatestStockData();
|
||||
return stockList.stream()
|
||||
.filter(stock -> stock.getStockCode().equals(stockCode))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
} catch (Exception e) {
|
||||
log.error("获取股票{}详情失败", stockCode, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user