feat: fix frontend
This commit is contained in:
19
backend/logs/agricultural-stock-platform.log
Normal file
19
backend/logs/agricultural-stock-platform.log
Normal file
@@ -0,0 +1,19 @@
|
||||
2025-06-21 15:10:59 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Starting AgriculturalStockPlatformApplication using Java 17.0.10 on WIN11 with PID 11416 (D:\VScodeProject\work_4\backend\target\classes started by shenjianZ in D:\VScodeProject\work_4\backend)
|
||||
2025-06-21 15:10:59 [main] INFO c.a.s.AgriculturalStockPlatformApplication - No active profile set, falling back to 1 default profile: "default"
|
||||
2025-06-21 15:11:00 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
|
||||
2025-06-21 15:11:00 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.63]
|
||||
2025-06-21 15:11:00 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
|
||||
2025-06-21 15:11:01 [main] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||
2025-06-21 15:11:01 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 5.6.9.Final
|
||||
2025-06-21 15:11:01 [main] INFO o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
|
||||
2025-06-21 15:11:01 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
||||
2025-06-21 15:11:01 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
||||
2025-06-21 15:11:01 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
|
||||
2025-06-21 15:11:02 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
|
||||
2025-06-21 15:11:03 [main] WARN o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
|
||||
2025-06-21 15:11:04 [main] WARN c.b.m.core.metadata.TableInfoHelper - Can not find table primary key in Class: "com.agricultural.stock.entity.TechnicalIndicator".
|
||||
2025-06-21 15:11:04 [main] WARN c.b.m.c.injector.DefaultSqlInjector - class com.agricultural.stock.entity.TechnicalIndicator ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.
|
||||
2025-06-21 15:11:04 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Started AgriculturalStockPlatformApplication in 5.394 seconds (JVM running for 5.767)
|
||||
2025-06-21 15:11:08 [http-nio-8080-exec-1] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
|
||||
2025-06-21 15:14:01 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
||||
2025-06-21 15:14:01 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
||||
@@ -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
|
||||
|
||||
@@ -71,7 +71,9 @@ springdoc:
|
||||
path: /swagger-ui
|
||||
enabled: true
|
||||
config-url: /v3/api-docs/swagger-config
|
||||
urls-primary-name: default
|
||||
disable-swagger-default-url: true
|
||||
url: /v3/api-docs
|
||||
urls-primary-name: all-apis
|
||||
packages-to-scan: com.agricultural.stock.controller
|
||||
paths-to-match: /api/**
|
||||
|
||||
|
||||
@@ -355,3 +355,284 @@
|
||||
2025-06-18 08:54:04,912 - INFO - 数据采集完成,共处理 67 只股票
|
||||
2025-06-18 08:54:30,695 - INFO - 收到停止信号,正在关闭...
|
||||
2025-06-18 08:54:30,696 - INFO - 股票数据采集器已停止
|
||||
2025-06-21 15:36:01,487 - INFO - 数据库连接成功
|
||||
2025-06-21 15:36:01,488 - INFO - 股票数据采集器启动
|
||||
2025-06-21 15:36:01,488 - INFO - 定时任务已启动,每 5 分钟采集一次
|
||||
2025-06-21 15:36:01,488 - INFO - 开始执行股票数据采集...
|
||||
2025-06-21 15:36:01,721 - INFO - 成功获取股票 sz300189 数据: 神农种业
|
||||
2025-06-21 15:36:02,357 - INFO - 成功获取股票 sz000713 数据: 丰乐种业
|
||||
2025-06-21 15:36:03,005 - INFO - 成功获取股票 sh600313 数据: 农发种业
|
||||
2025-06-21 15:36:03,631 - INFO - 成功获取股票 bj837403 数据: 康农种业
|
||||
2025-06-21 15:36:04,272 - INFO - 成功获取股票 sz200505 数据: 京粮B
|
||||
2025-06-21 15:36:04,876 - INFO - 成功获取股票 sz300268 数据: *ST佳沃
|
||||
2025-06-21 15:36:05,501 - INFO - 成功获取股票 sz000930 数据: 中粮科技
|
||||
2025-06-21 15:36:06,140 - INFO - 成功获取股票 sz002299 数据: 圣农发展
|
||||
2025-06-21 15:36:06,756 - INFO - 成功获取股票 sh600371 数据: 万向德农
|
||||
2025-06-21 15:36:07,372 - INFO - 成功获取股票 sh600598 数据: 北大荒
|
||||
2025-06-21 15:36:08,017 - INFO - 成功获取股票 sh603609 数据: 禾丰股份
|
||||
2025-06-21 15:36:08,628 - INFO - 成功获取股票 bj831087 数据: 秋乐种业
|
||||
2025-06-21 15:36:09,256 - INFO - 成功获取股票 sh603363 数据: 傲农生物
|
||||
2025-06-21 15:36:09,902 - INFO - 成功获取股票 sh603336 数据: 宏辉果蔬
|
||||
2025-06-21 15:36:10,522 - INFO - 成功获取股票 sh600354 数据: 敦煌种业
|
||||
2025-06-21 15:36:11,151 - INFO - 成功获取股票 sz002385 数据: 大北农
|
||||
2025-06-21 15:36:11,778 - INFO - 成功获取股票 sz000048 数据: 京基智农
|
||||
2025-06-21 15:36:12,405 - INFO - 成功获取股票 sh600251 数据: 冠农股份
|
||||
2025-06-21 15:36:13,051 - INFO - 成功获取股票 sz002321 数据: 华英农业
|
||||
2025-06-21 15:36:13,691 - INFO - 成功获取股票 sz000505 数据: 京粮控股
|
||||
2025-06-21 15:36:14,339 - INFO - 成功获取股票 sz001366 数据: 播恩集团
|
||||
2025-06-21 15:36:14,982 - INFO - 成功获取股票 sz002772 数据: 众兴菌业
|
||||
2025-06-21 15:36:15,609 - INFO - 成功获取股票 sz002041 数据: 登海种业
|
||||
2025-06-21 15:36:16,230 - INFO - 成功获取股票 sh600127 数据: 金健米业
|
||||
2025-06-21 15:36:16,868 - INFO - 成功获取股票 sz002548 数据: 金新农
|
||||
2025-06-21 15:36:17,535 - INFO - 成功获取股票 sh605296 数据: 神农集团
|
||||
2025-06-21 15:36:18,165 - INFO - 成功获取股票 sh600359 数据: 新农开发
|
||||
2025-06-21 15:36:18,800 - INFO - 成功获取股票 sh601952 数据: 苏垦农发
|
||||
2025-06-21 15:36:19,420 - INFO - 成功获取股票 sh600975 数据: 新五丰
|
||||
2025-06-21 15:36:20,048 - INFO - 成功获取股票 sz300505 数据: 川金诺
|
||||
2025-06-21 15:36:20,656 - INFO - 成功获取股票 sh600141 数据: XD兴发集
|
||||
2025-06-21 15:36:21,293 - INFO - 成功获取股票 sz000893 数据: 亚钾国际
|
||||
2025-06-21 15:36:21,929 - INFO - 成功获取股票 sh600691 数据: 阳煤化工
|
||||
2025-06-21 15:36:22,552 - INFO - 成功获取股票 sh600470 数据: 六国化工
|
||||
2025-06-21 15:36:23,163 - INFO - 成功获取股票 sh000912 数据: 300消费
|
||||
2025-06-21 15:36:23,785 - INFO - 成功获取股票 sz000408 数据: 藏格矿业
|
||||
2025-06-21 15:36:24,410 - INFO - 成功获取股票 sz000902 数据: 新洋丰
|
||||
2025-06-21 15:36:25,040 - INFO - 成功获取股票 sz002539 数据: 云图控股
|
||||
2025-06-21 15:36:25,662 - INFO - 成功获取股票 sz002599 数据: 盛通股份
|
||||
2025-06-21 15:36:26,277 - INFO - 成功获取股票 sz002545 数据: 东方铁塔
|
||||
2025-06-21 15:36:26,919 - INFO - 成功获取股票 sz300387 数据: 富邦科技
|
||||
2025-06-21 15:36:27,548 - INFO - 成功获取股票 sz002274 数据: 华昌化工
|
||||
2025-06-21 15:36:28,226 - INFO - 成功获取股票 sz002470 数据: 金正大
|
||||
2025-06-21 15:36:28,895 - INFO - 成功获取股票 sz002538 数据: 司尔特
|
||||
2025-06-21 15:36:29,536 - INFO - 成功获取股票 sz000731 数据: 四川美丰
|
||||
2025-06-21 15:36:30,195 - INFO - 成功获取股票 sh600078 数据: 澄星股份
|
||||
2025-06-21 15:36:30,841 - INFO - 成功获取股票 sh600227 数据: 赤天化
|
||||
2025-06-21 15:36:31,481 - INFO - 成功获取股票 sh603395 数据: 红四方
|
||||
2025-06-21 15:36:32,103 - INFO - 成功获取股票 sz002588 数据: 史丹利
|
||||
2025-06-21 15:36:32,718 - INFO - 成功获取股票 sz000422 数据: 湖北宜化
|
||||
2025-06-21 15:36:33,345 - INFO - 成功获取股票 sz002556 数据: 辉隆股份
|
||||
2025-06-21 15:36:33,987 - INFO - 成功获取股票 sz002312 数据: 川发龙蟒
|
||||
2025-06-21 15:36:34,620 - INFO - 成功获取股票 sz002170 数据: 芭田股份
|
||||
2025-06-21 15:36:35,398 - INFO - 成功获取股票 sz000792 数据: 盐湖股份
|
||||
2025-06-21 15:36:36,082 - INFO - 成功获取股票 sh600096 数据: 云天化
|
||||
2025-06-21 15:36:36,825 - INFO - 成功获取股票 sz001231 数据: 农心科技
|
||||
2025-06-21 15:36:37,477 - INFO - 成功获取股票 sz002731 数据: 萃华珠宝
|
||||
2025-06-21 15:36:38,171 - INFO - 成功获取股票 sz200553 数据: 安道麦B
|
||||
2025-06-21 15:36:38,800 - INFO - 成功获取股票 sh603810 数据: 丰山集团
|
||||
2025-06-21 15:36:39,426 - INFO - 成功获取股票 sh603970 数据: 中农立华
|
||||
2025-06-21 15:36:40,052 - INFO - 成功获取股票 sz301035 数据: 润丰股份
|
||||
2025-06-21 15:36:40,695 - INFO - 成功获取股票 sz002868 数据: *ST绿康
|
||||
2025-06-21 15:36:41,349 - INFO - 成功获取股票 sz003042 数据: 中农联合
|
||||
2025-06-21 15:36:42,004 - INFO - 成功获取股票 sz002391 数据: 长青股份
|
||||
2025-06-21 15:36:42,614 - INFO - 成功获取股票 bj870866 数据: 绿亨科技
|
||||
2025-06-21 15:36:43,269 - INFO - 成功获取股票 sz301665 数据: 泰禾股份
|
||||
2025-06-21 15:36:43,934 - INFO - 成功获取股票 sh600486 数据: 扬农化工
|
||||
2025-06-21 15:36:44,435 - INFO - 本次采集完成,共获取 67 只股票数据
|
||||
2025-06-21 15:36:44,441 - INFO - 插入股票 sz300189 数据
|
||||
2025-06-21 15:36:44,444 - INFO - 插入股票 sz000713 数据
|
||||
2025-06-21 15:36:44,445 - INFO - 插入股票 sh600313 数据
|
||||
2025-06-21 15:36:44,447 - INFO - 插入股票 bj837403 数据
|
||||
2025-06-21 15:36:44,449 - INFO - 插入股票 sz200505 数据
|
||||
2025-06-21 15:36:44,450 - INFO - 插入股票 sz300268 数据
|
||||
2025-06-21 15:36:44,452 - INFO - 插入股票 sz000930 数据
|
||||
2025-06-21 15:36:44,454 - INFO - 插入股票 sz002299 数据
|
||||
2025-06-21 15:36:44,455 - INFO - 插入股票 sh600371 数据
|
||||
2025-06-21 15:36:44,458 - INFO - 插入股票 sh600598 数据
|
||||
2025-06-21 15:36:44,460 - INFO - 插入股票 sh603609 数据
|
||||
2025-06-21 15:36:44,462 - INFO - 插入股票 bj831087 数据
|
||||
2025-06-21 15:36:44,463 - INFO - 插入股票 sh603363 数据
|
||||
2025-06-21 15:36:44,465 - INFO - 插入股票 sh603336 数据
|
||||
2025-06-21 15:36:44,467 - INFO - 插入股票 sh600354 数据
|
||||
2025-06-21 15:36:44,468 - INFO - 插入股票 sz002385 数据
|
||||
2025-06-21 15:36:44,470 - INFO - 插入股票 sz000048 数据
|
||||
2025-06-21 15:36:44,471 - INFO - 插入股票 sh600251 数据
|
||||
2025-06-21 15:36:44,473 - INFO - 插入股票 sz002321 数据
|
||||
2025-06-21 15:36:44,475 - INFO - 插入股票 sz000505 数据
|
||||
2025-06-21 15:36:44,477 - INFO - 插入股票 sz001366 数据
|
||||
2025-06-21 15:36:44,478 - INFO - 插入股票 sz002772 数据
|
||||
2025-06-21 15:36:44,479 - INFO - 插入股票 sz002041 数据
|
||||
2025-06-21 15:36:44,481 - INFO - 插入股票 sh600127 数据
|
||||
2025-06-21 15:36:44,482 - INFO - 插入股票 sz002548 数据
|
||||
2025-06-21 15:36:44,484 - INFO - 插入股票 sh605296 数据
|
||||
2025-06-21 15:36:44,485 - INFO - 插入股票 sh600359 数据
|
||||
2025-06-21 15:36:44,487 - INFO - 插入股票 sh601952 数据
|
||||
2025-06-21 15:36:44,489 - INFO - 插入股票 sh600975 数据
|
||||
2025-06-21 15:36:44,492 - INFO - 插入股票 sz300505 数据
|
||||
2025-06-21 15:36:44,493 - INFO - 插入股票 sh600141 数据
|
||||
2025-06-21 15:36:44,495 - INFO - 插入股票 sz000893 数据
|
||||
2025-06-21 15:36:44,497 - INFO - 插入股票 sh600691 数据
|
||||
2025-06-21 15:36:44,498 - INFO - 插入股票 sh600470 数据
|
||||
2025-06-21 15:36:44,500 - INFO - 插入股票 sh000912 数据
|
||||
2025-06-21 15:36:44,501 - INFO - 插入股票 sz000408 数据
|
||||
2025-06-21 15:36:44,503 - INFO - 插入股票 sz000902 数据
|
||||
2025-06-21 15:36:44,505 - INFO - 插入股票 sz002539 数据
|
||||
2025-06-21 15:36:44,508 - INFO - 插入股票 sz002599 数据
|
||||
2025-06-21 15:36:44,510 - INFO - 插入股票 sz002545 数据
|
||||
2025-06-21 15:36:44,511 - INFO - 插入股票 sz300387 数据
|
||||
2025-06-21 15:36:44,512 - INFO - 插入股票 sz002274 数据
|
||||
2025-06-21 15:36:44,514 - INFO - 插入股票 sz002470 数据
|
||||
2025-06-21 15:36:44,515 - INFO - 插入股票 sz002538 数据
|
||||
2025-06-21 15:36:44,516 - INFO - 插入股票 sz000731 数据
|
||||
2025-06-21 15:36:44,518 - INFO - 插入股票 sh600078 数据
|
||||
2025-06-21 15:36:44,519 - INFO - 插入股票 sh600227 数据
|
||||
2025-06-21 15:36:44,521 - INFO - 插入股票 sh603395 数据
|
||||
2025-06-21 15:36:44,525 - INFO - 插入股票 sz002588 数据
|
||||
2025-06-21 15:36:44,527 - INFO - 插入股票 sz000422 数据
|
||||
2025-06-21 15:36:44,528 - INFO - 插入股票 sz002556 数据
|
||||
2025-06-21 15:36:44,529 - INFO - 插入股票 sz002312 数据
|
||||
2025-06-21 15:36:44,531 - INFO - 插入股票 sz002170 数据
|
||||
2025-06-21 15:36:44,532 - INFO - 插入股票 sz000792 数据
|
||||
2025-06-21 15:36:44,533 - INFO - 插入股票 sh600096 数据
|
||||
2025-06-21 15:36:44,535 - INFO - 插入股票 sz001231 数据
|
||||
2025-06-21 15:36:44,536 - INFO - 插入股票 sz002731 数据
|
||||
2025-06-21 15:36:44,538 - INFO - 插入股票 sz200553 数据
|
||||
2025-06-21 15:36:44,539 - INFO - 插入股票 sh603810 数据
|
||||
2025-06-21 15:36:44,542 - INFO - 插入股票 sh603970 数据
|
||||
2025-06-21 15:36:44,543 - INFO - 插入股票 sz301035 数据
|
||||
2025-06-21 15:36:44,544 - INFO - 插入股票 sz002868 数据
|
||||
2025-06-21 15:36:44,546 - INFO - 插入股票 sz003042 数据
|
||||
2025-06-21 15:36:44,548 - INFO - 插入股票 sz002391 数据
|
||||
2025-06-21 15:36:44,549 - INFO - 插入股票 bj870866 数据
|
||||
2025-06-21 15:36:44,551 - INFO - 插入股票 sz301665 数据
|
||||
2025-06-21 15:36:44,552 - INFO - 插入股票 sh600486 数据
|
||||
2025-06-21 15:36:44,560 - INFO - 成功处理 67 条股票数据到数据库
|
||||
2025-06-21 15:36:44,561 - INFO - 数据采集完成,共处理 67 只股票
|
||||
2025-06-21 15:41:01,678 - INFO - 开始执行股票数据采集...
|
||||
2025-06-21 15:41:01,809 - INFO - 成功获取股票 sz300189 数据: 神农种业
|
||||
2025-06-21 15:41:02,450 - INFO - 成功获取股票 sz000713 数据: 丰乐种业
|
||||
2025-06-21 15:41:03,074 - INFO - 成功获取股票 sh600313 数据: 农发种业
|
||||
2025-06-21 15:41:03,715 - INFO - 成功获取股票 bj837403 数据: 康农种业
|
||||
2025-06-21 15:41:04,319 - INFO - 成功获取股票 sz200505 数据: 京粮B
|
||||
2025-06-21 15:41:04,974 - INFO - 成功获取股票 sz300268 数据: *ST佳沃
|
||||
2025-06-21 15:41:05,643 - INFO - 成功获取股票 sz000930 数据: 中粮科技
|
||||
2025-06-21 15:41:06,253 - INFO - 成功获取股票 sz002299 数据: 圣农发展
|
||||
2025-06-21 15:41:06,890 - INFO - 成功获取股票 sh600371 数据: 万向德农
|
||||
2025-06-21 15:41:07,544 - INFO - 成功获取股票 sh600598 数据: 北大荒
|
||||
2025-06-21 15:41:08,146 - INFO - 成功获取股票 sh603609 数据: 禾丰股份
|
||||
2025-06-21 15:41:08,770 - INFO - 成功获取股票 bj831087 数据: 秋乐种业
|
||||
2025-06-21 15:41:09,463 - INFO - 成功获取股票 sh603363 数据: 傲农生物
|
||||
2025-06-21 15:41:10,093 - INFO - 成功获取股票 sh603336 数据: 宏辉果蔬
|
||||
2025-06-21 15:41:10,706 - INFO - 成功获取股票 sh600354 数据: 敦煌种业
|
||||
2025-06-21 15:41:11,321 - INFO - 成功获取股票 sz002385 数据: 大北农
|
||||
2025-06-21 15:41:11,942 - INFO - 成功获取股票 sz000048 数据: 京基智农
|
||||
2025-06-21 15:41:12,568 - INFO - 成功获取股票 sh600251 数据: 冠农股份
|
||||
2025-06-21 15:41:13,209 - INFO - 成功获取股票 sz002321 数据: 华英农业
|
||||
2025-06-21 15:41:13,856 - INFO - 成功获取股票 sz000505 数据: 京粮控股
|
||||
2025-06-21 15:41:14,498 - INFO - 成功获取股票 sz001366 数据: 播恩集团
|
||||
2025-06-21 15:41:15,142 - INFO - 成功获取股票 sz002772 数据: 众兴菌业
|
||||
2025-06-21 15:41:15,772 - INFO - 成功获取股票 sz002041 数据: 登海种业
|
||||
2025-06-21 15:41:16,397 - INFO - 成功获取股票 sh600127 数据: 金健米业
|
||||
2025-06-21 15:41:17,021 - INFO - 成功获取股票 sz002548 数据: 金新农
|
||||
2025-06-21 15:41:17,690 - INFO - 成功获取股票 sh605296 数据: 神农集团
|
||||
2025-06-21 15:41:18,303 - INFO - 成功获取股票 sh600359 数据: 新农开发
|
||||
2025-06-21 15:41:18,982 - INFO - 成功获取股票 sh601952 数据: 苏垦农发
|
||||
2025-06-21 15:41:19,628 - INFO - 成功获取股票 sh600975 数据: 新五丰
|
||||
2025-06-21 15:41:20,376 - INFO - 成功获取股票 sz300505 数据: 川金诺
|
||||
2025-06-21 15:41:21,043 - INFO - 成功获取股票 sh600141 数据: XD兴发集
|
||||
2025-06-21 15:41:21,717 - INFO - 成功获取股票 sz000893 数据: 亚钾国际
|
||||
2025-06-21 15:41:22,348 - INFO - 成功获取股票 sh600691 数据: 阳煤化工
|
||||
2025-06-21 15:41:22,966 - INFO - 成功获取股票 sh600470 数据: 六国化工
|
||||
2025-06-21 15:41:23,560 - INFO - 成功获取股票 sh000912 数据: 300消费
|
||||
2025-06-21 15:41:24,163 - INFO - 成功获取股票 sz000408 数据: 藏格矿业
|
||||
2025-06-21 15:41:24,808 - INFO - 成功获取股票 sz000902 数据: 新洋丰
|
||||
2025-06-21 15:41:25,419 - INFO - 成功获取股票 sz002539 数据: 云图控股
|
||||
2025-06-21 15:41:26,020 - INFO - 成功获取股票 sz002599 数据: 盛通股份
|
||||
2025-06-21 15:41:26,659 - INFO - 成功获取股票 sz002545 数据: 东方铁塔
|
||||
2025-06-21 15:41:27,268 - INFO - 成功获取股票 sz300387 数据: 富邦科技
|
||||
2025-06-21 15:41:27,895 - INFO - 成功获取股票 sz002274 数据: 华昌化工
|
||||
2025-06-21 15:41:28,586 - INFO - 成功获取股票 sz002470 数据: 金正大
|
||||
2025-06-21 15:41:29,220 - INFO - 成功获取股票 sz002538 数据: 司尔特
|
||||
2025-06-21 15:41:29,827 - INFO - 成功获取股票 sz000731 数据: 四川美丰
|
||||
2025-06-21 15:41:30,450 - INFO - 成功获取股票 sh600078 数据: 澄星股份
|
||||
2025-06-21 15:41:31,072 - INFO - 成功获取股票 sh600227 数据: 赤天化
|
||||
2025-06-21 15:41:31,702 - INFO - 成功获取股票 sh603395 数据: 红四方
|
||||
2025-06-21 15:41:32,314 - INFO - 成功获取股票 sz002588 数据: 史丹利
|
||||
2025-06-21 15:41:32,932 - INFO - 成功获取股票 sz000422 数据: 湖北宜化
|
||||
2025-06-21 15:41:33,617 - INFO - 成功获取股票 sz002556 数据: 辉隆股份
|
||||
2025-06-21 15:41:34,222 - INFO - 成功获取股票 sz002312 数据: 川发龙蟒
|
||||
2025-06-21 15:41:34,849 - INFO - 成功获取股票 sz002170 数据: 芭田股份
|
||||
2025-06-21 15:41:35,524 - INFO - 成功获取股票 sz000792 数据: 盐湖股份
|
||||
2025-06-21 15:41:36,171 - INFO - 成功获取股票 sh600096 数据: 云天化
|
||||
2025-06-21 15:41:36,830 - INFO - 成功获取股票 sz001231 数据: 农心科技
|
||||
2025-06-21 15:41:37,517 - INFO - 成功获取股票 sz002731 数据: 萃华珠宝
|
||||
2025-06-21 15:41:38,138 - INFO - 成功获取股票 sz200553 数据: 安道麦B
|
||||
2025-06-21 15:41:38,815 - INFO - 成功获取股票 sh603810 数据: 丰山集团
|
||||
2025-06-21 15:41:39,439 - INFO - 成功获取股票 sh603970 数据: 中农立华
|
||||
2025-06-21 15:41:40,102 - INFO - 成功获取股票 sz301035 数据: 润丰股份
|
||||
2025-06-21 15:41:40,710 - INFO - 成功获取股票 sz002868 数据: *ST绿康
|
||||
2025-06-21 15:41:41,316 - INFO - 成功获取股票 sz003042 数据: 中农联合
|
||||
2025-06-21 15:41:41,943 - INFO - 成功获取股票 sz002391 数据: 长青股份
|
||||
2025-06-21 15:41:42,554 - INFO - 成功获取股票 bj870866 数据: 绿亨科技
|
||||
2025-06-21 15:41:43,174 - INFO - 成功获取股票 sz301665 数据: 泰禾股份
|
||||
2025-06-21 15:41:43,774 - INFO - 成功获取股票 sh600486 数据: 扬农化工
|
||||
2025-06-21 15:41:44,275 - INFO - 本次采集完成,共获取 67 只股票数据
|
||||
2025-06-21 15:41:44,279 - INFO - 更新股票 sz300189 数据
|
||||
2025-06-21 15:41:44,282 - INFO - 更新股票 sz000713 数据
|
||||
2025-06-21 15:41:44,284 - INFO - 更新股票 sh600313 数据
|
||||
2025-06-21 15:41:44,286 - INFO - 更新股票 bj837403 数据
|
||||
2025-06-21 15:41:44,289 - INFO - 更新股票 sz200505 数据
|
||||
2025-06-21 15:41:44,291 - INFO - 更新股票 sz300268 数据
|
||||
2025-06-21 15:41:44,293 - INFO - 更新股票 sz000930 数据
|
||||
2025-06-21 15:41:44,295 - INFO - 更新股票 sz002299 数据
|
||||
2025-06-21 15:41:44,298 - INFO - 更新股票 sh600371 数据
|
||||
2025-06-21 15:41:44,300 - INFO - 更新股票 sh600598 数据
|
||||
2025-06-21 15:41:44,302 - INFO - 更新股票 sh603609 数据
|
||||
2025-06-21 15:41:44,306 - INFO - 更新股票 bj831087 数据
|
||||
2025-06-21 15:41:44,308 - INFO - 更新股票 sh603363 数据
|
||||
2025-06-21 15:41:44,310 - INFO - 更新股票 sh603336 数据
|
||||
2025-06-21 15:41:44,313 - INFO - 更新股票 sh600354 数据
|
||||
2025-06-21 15:41:44,315 - INFO - 更新股票 sz002385 数据
|
||||
2025-06-21 15:41:44,318 - INFO - 更新股票 sz000048 数据
|
||||
2025-06-21 15:41:44,321 - INFO - 更新股票 sh600251 数据
|
||||
2025-06-21 15:41:44,323 - INFO - 更新股票 sz002321 数据
|
||||
2025-06-21 15:41:44,324 - INFO - 更新股票 sz000505 数据
|
||||
2025-06-21 15:41:44,326 - INFO - 更新股票 sz001366 数据
|
||||
2025-06-21 15:41:44,330 - INFO - 更新股票 sz002772 数据
|
||||
2025-06-21 15:41:44,332 - INFO - 更新股票 sz002041 数据
|
||||
2025-06-21 15:41:44,333 - INFO - 更新股票 sh600127 数据
|
||||
2025-06-21 15:41:44,336 - INFO - 更新股票 sz002548 数据
|
||||
2025-06-21 15:41:44,337 - INFO - 更新股票 sh605296 数据
|
||||
2025-06-21 15:41:44,339 - INFO - 更新股票 sh600359 数据
|
||||
2025-06-21 15:41:44,341 - INFO - 更新股票 sh601952 数据
|
||||
2025-06-21 15:41:44,343 - INFO - 更新股票 sh600975 数据
|
||||
2025-06-21 15:41:44,346 - INFO - 更新股票 sz300505 数据
|
||||
2025-06-21 15:41:44,348 - INFO - 更新股票 sh600141 数据
|
||||
2025-06-21 15:41:44,351 - INFO - 更新股票 sz000893 数据
|
||||
2025-06-21 15:41:44,353 - INFO - 更新股票 sh600691 数据
|
||||
2025-06-21 15:41:44,355 - INFO - 更新股票 sh600470 数据
|
||||
2025-06-21 15:41:44,357 - INFO - 更新股票 sh000912 数据
|
||||
2025-06-21 15:41:44,359 - INFO - 更新股票 sz000408 数据
|
||||
2025-06-21 15:41:44,362 - INFO - 更新股票 sz000902 数据
|
||||
2025-06-21 15:41:44,364 - INFO - 更新股票 sz002539 数据
|
||||
2025-06-21 15:41:44,366 - INFO - 更新股票 sz002599 数据
|
||||
2025-06-21 15:41:44,369 - INFO - 更新股票 sz002545 数据
|
||||
2025-06-21 15:41:44,371 - INFO - 更新股票 sz300387 数据
|
||||
2025-06-21 15:41:44,373 - INFO - 更新股票 sz002274 数据
|
||||
2025-06-21 15:41:44,375 - INFO - 更新股票 sz002470 数据
|
||||
2025-06-21 15:41:44,377 - INFO - 更新股票 sz002538 数据
|
||||
2025-06-21 15:41:44,379 - INFO - 更新股票 sz000731 数据
|
||||
2025-06-21 15:41:44,382 - INFO - 更新股票 sh600078 数据
|
||||
2025-06-21 15:41:44,385 - INFO - 更新股票 sh600227 数据
|
||||
2025-06-21 15:41:44,388 - INFO - 更新股票 sh603395 数据
|
||||
2025-06-21 15:41:44,390 - INFO - 更新股票 sz002588 数据
|
||||
2025-06-21 15:41:44,393 - INFO - 更新股票 sz000422 数据
|
||||
2025-06-21 15:41:44,396 - INFO - 更新股票 sz002556 数据
|
||||
2025-06-21 15:41:44,399 - INFO - 更新股票 sz002312 数据
|
||||
2025-06-21 15:41:44,401 - INFO - 更新股票 sz002170 数据
|
||||
2025-06-21 15:41:44,404 - INFO - 更新股票 sz000792 数据
|
||||
2025-06-21 15:41:44,406 - INFO - 更新股票 sh600096 数据
|
||||
2025-06-21 15:41:44,408 - INFO - 更新股票 sz001231 数据
|
||||
2025-06-21 15:41:44,410 - INFO - 更新股票 sz002731 数据
|
||||
2025-06-21 15:41:44,413 - INFO - 更新股票 sz200553 数据
|
||||
2025-06-21 15:41:44,415 - INFO - 更新股票 sh603810 数据
|
||||
2025-06-21 15:41:44,418 - INFO - 更新股票 sh603970 数据
|
||||
2025-06-21 15:41:44,420 - INFO - 更新股票 sz301035 数据
|
||||
2025-06-21 15:41:44,421 - INFO - 更新股票 sz002868 数据
|
||||
2025-06-21 15:41:44,423 - INFO - 更新股票 sz003042 数据
|
||||
2025-06-21 15:41:44,424 - INFO - 更新股票 sz002391 数据
|
||||
2025-06-21 15:41:44,426 - INFO - 更新股票 bj870866 数据
|
||||
2025-06-21 15:41:44,429 - INFO - 更新股票 sz301665 数据
|
||||
2025-06-21 15:41:44,432 - INFO - 更新股票 sh600486 数据
|
||||
2025-06-21 15:41:44,438 - INFO - 成功处理 67 条股票数据到数据库
|
||||
2025-06-21 15:41:44,439 - INFO - 数据采集完成,共处理 67 只股票
|
||||
2025-06-21 15:43:59,960 - INFO - 收到停止信号,正在关闭...
|
||||
2025-06-21 15:43:59,961 - INFO - 股票数据采集器已停止
|
||||
|
||||
@@ -34,14 +34,9 @@
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/market-analysis">
|
||||
<el-icon><Pie /></el-icon>
|
||||
<el-icon><DataBoard /></el-icon>
|
||||
<span>市场分析</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/health">
|
||||
<el-icon><Monitor /></el-icon>
|
||||
<span>系统监控</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -77,6 +77,14 @@ export function getStockPrediction(stockCode, days = 7) {
|
||||
})
|
||||
}
|
||||
|
||||
// 获取股票详情
|
||||
export function getStockDetail(stockCode) {
|
||||
return request({
|
||||
url: `/api/stock/detail/${stockCode}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 搜索股票
|
||||
export function searchStocks(keyword) {
|
||||
return request({
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Dashboard from '@/views/Dashboard.vue'
|
||||
import HealthCheck from '@/views/HealthCheck.vue'
|
||||
import Rankings from '@/views/Rankings.vue'
|
||||
import StockSearch from '@/views/StockSearch.vue'
|
||||
import StockDetail from '@/views/StockDetail.vue'
|
||||
import MarketAnalysis from '@/views/MarketAnalysis.vue'
|
||||
import StockDetailView from '@/views/StockDetailView.vue'
|
||||
import StockTrendView from '@/views/StockTrendView.vue'
|
||||
import StockPredictionView from '@/views/StockPredictionView.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@@ -31,29 +33,29 @@ const routes = [
|
||||
component: StockDetail,
|
||||
meta: { title: '股票详情' }
|
||||
},
|
||||
{
|
||||
path: '/stock/:stockCode/detail',
|
||||
name: 'StockDetailView',
|
||||
component: StockDetailView,
|
||||
meta: { title: '股票详情分析' }
|
||||
},
|
||||
{
|
||||
path: '/stock/:stockCode/trend',
|
||||
name: 'StockTrend',
|
||||
component: StockDetail,
|
||||
meta: { title: '股票趋势' }
|
||||
name: 'StockTrendView',
|
||||
component: StockTrendView,
|
||||
meta: { title: '股票趋势分析' }
|
||||
},
|
||||
{
|
||||
path: '/stock/:stockCode/prediction',
|
||||
name: 'StockPrediction',
|
||||
component: StockDetail,
|
||||
meta: { title: '股票预测' }
|
||||
name: 'StockPredictionView',
|
||||
component: StockPredictionView,
|
||||
meta: { title: '股票预测分析' }
|
||||
},
|
||||
{
|
||||
path: '/market-analysis',
|
||||
name: 'MarketAnalysis',
|
||||
component: MarketAnalysis,
|
||||
meta: { title: '市场分析' }
|
||||
},
|
||||
{
|
||||
path: '/health',
|
||||
name: 'HealthCheck',
|
||||
component: HealthCheck,
|
||||
meta: { title: '健康检查' }
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
|
||||
<!-- 快速导航 -->
|
||||
<el-row :gutter="20" class="quick-nav-section">
|
||||
<el-col :span="6">
|
||||
<el-col :span="8">
|
||||
<el-card class="nav-card" @click="navigateTo('/search')">
|
||||
<div class="nav-content">
|
||||
<div class="nav-icon search">
|
||||
@@ -107,7 +107,7 @@
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-col :span="8">
|
||||
<el-card class="nav-card" @click="navigateTo('/rankings')">
|
||||
<div class="nav-content">
|
||||
<div class="nav-icon rankings">
|
||||
@@ -120,11 +120,11 @@
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-col :span="8">
|
||||
<el-card class="nav-card" @click="navigateTo('/market-analysis')">
|
||||
<div class="nav-content">
|
||||
<div class="nav-icon analysis">
|
||||
<el-icon><Pie /></el-icon>
|
||||
<el-icon><DataBoard /></el-icon>
|
||||
</div>
|
||||
<div class="nav-text">
|
||||
<h3>市场分析</h3>
|
||||
@@ -133,32 +133,15 @@
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="nav-card" @click="navigateTo('/health')">
|
||||
<div class="nav-content">
|
||||
<div class="nav-icon health">
|
||||
<el-icon><Monitor /></el-icon>
|
||||
</div>
|
||||
<div class="nav-text">
|
||||
<h3>系统监控</h3>
|
||||
<p>系统健康状态检查</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<!-- 市场分析数据表格 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-card class="table-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>最新市场分析数据</span>
|
||||
<el-button type="success" size="small" @click="runSparkAnalysis">
|
||||
<el-icon><Lightning /></el-icon>
|
||||
运行Spark分析
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="recentData" style="width: 100%">
|
||||
@@ -203,7 +186,7 @@
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { getLatestMarketAnalysis, getRecentMarketAnalysis } from '@/api/market'
|
||||
import { getMarketAnalysis, getRealtimeStockData } from '@/api/stock'
|
||||
import { getMarketAnalysis } from '@/api/stock'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
@@ -215,6 +198,7 @@ export default {
|
||||
const recentData = ref([])
|
||||
const loading = ref(false)
|
||||
|
||||
|
||||
// 加载最新市场数据
|
||||
const loadLatestData = async () => {
|
||||
try {
|
||||
@@ -245,6 +229,8 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 初始化饼图
|
||||
const initPieChart = async () => {
|
||||
await nextTick()
|
||||
@@ -336,6 +322,8 @@ export default {
|
||||
return (num / 10000).toFixed(1)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 刷新数据
|
||||
const refreshData = () => {
|
||||
loadLatestData()
|
||||
@@ -347,19 +335,13 @@ export default {
|
||||
loadRecentData()
|
||||
}
|
||||
|
||||
// 模拟运行Spark分析
|
||||
const runSparkAnalysis = () => {
|
||||
ElMessage.success('Spark分析任务已提交,请稍候查看结果')
|
||||
setTimeout(() => {
|
||||
refreshData()
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
// 导航到指定页面
|
||||
const navigateTo = (path) => {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
loadLatestData()
|
||||
loadRecentData()
|
||||
@@ -371,7 +353,6 @@ export default {
|
||||
loading,
|
||||
refreshData,
|
||||
loadTrendData,
|
||||
runSparkAnalysis,
|
||||
formatNumber,
|
||||
navigateTo
|
||||
}
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
<template>
|
||||
<div class="health-check">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>系统健康检查</span>
|
||||
<el-tag type="success">运行正常</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="前端状态">
|
||||
<el-tag type="success">正常运行</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="端口">3000</el-descriptions-item>
|
||||
<el-descriptions-item label="Vue版本">{{ vueVersion }}</el-descriptions-item>
|
||||
<el-descriptions-item label="Element Plus">已加载</el-descriptions-item>
|
||||
<el-descriptions-item label="路由">{{ routerReady ? '已配置' : '未配置' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态管理">{{ storeReady ? '已配置' : '未配置' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div style="margin-top: 20px;">
|
||||
<el-button type="primary" @click="testApi">测试API连接</el-button>
|
||||
<el-button type="success" @click="testStyles">测试样式变量</el-button>
|
||||
</div>
|
||||
|
||||
<div v-if="apiStatus" style="margin-top: 15px;">
|
||||
<el-alert :title="apiStatus.title" :type="apiStatus.type" show-icon />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { version } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'HealthCheck',
|
||||
setup() {
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const store = useStore()
|
||||
const apiStatus = ref(null)
|
||||
|
||||
const vueVersion = ref(version)
|
||||
const routerReady = ref(!!router)
|
||||
const storeReady = ref(!!store)
|
||||
|
||||
const testApi = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/health')
|
||||
if (response.ok) {
|
||||
apiStatus.value = {
|
||||
title: 'API连接成功',
|
||||
type: 'success'
|
||||
}
|
||||
} else {
|
||||
apiStatus.value = {
|
||||
title: 'API连接失败',
|
||||
type: 'error'
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
apiStatus.value = {
|
||||
title: `API连接错误: ${error.message}`,
|
||||
type: 'warning'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const testStyles = () => {
|
||||
ElMessage.success('SCSS变量加载正常!')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
console.log('健康检查页面已加载')
|
||||
})
|
||||
|
||||
return {
|
||||
vueVersion,
|
||||
routerReady,
|
||||
storeReady,
|
||||
apiStatus,
|
||||
testApi,
|
||||
testStyles
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/styles/variables.scss";
|
||||
|
||||
.health-check {
|
||||
padding: 20px;
|
||||
background-color: $bg-color-page;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@@ -28,7 +28,7 @@
|
||||
</div>
|
||||
<div class="price-change" :class="{ 'positive': stockInfo.changePercent >= 0, 'negative': stockInfo.changePercent < 0 }">
|
||||
{{ stockInfo.changePercent >= 0 ? '+' : '' }}{{ stockInfo.changePercent?.toFixed(2) }}%
|
||||
({{ stockInfo.changePercent >= 0 ? '+' : '' }}{{ stockInfo.changeAmount?.toFixed(2) }})
|
||||
({{ stockInfo.changeAmount?.toFixed(2) }})
|
||||
</div>
|
||||
</div>
|
||||
<div class="stock-metrics">
|
||||
@@ -195,7 +195,7 @@
|
||||
<script>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { getStockHistory, getStockTrend, getStockPrediction, searchStocks } from '@/api/stock'
|
||||
import { getStockHistory, getStockTrend, getStockPrediction, searchStocks, getStockDetail } from '@/api/stock'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
@@ -222,12 +222,41 @@ export default {
|
||||
// 加载股票基本信息
|
||||
const loadStockInfo = async () => {
|
||||
try {
|
||||
const response = await searchStocks(stockCode.value)
|
||||
if (response.data && response.data.length > 0) {
|
||||
stockInfo.value = response.data[0]
|
||||
// 优先使用详情API
|
||||
const detailResponse = await getStockDetail(stockCode.value)
|
||||
if (detailResponse.data) {
|
||||
stockInfo.value = {
|
||||
stockCode: detailResponse.data.stockCode,
|
||||
stockName: detailResponse.data.stockName,
|
||||
currentPrice: detailResponse.data.closePrice,
|
||||
changePercent: detailResponse.data.changePercent,
|
||||
changeAmount: detailResponse.data.changeAmount,
|
||||
volume: detailResponse.data.volume,
|
||||
marketCap: detailResponse.data.marketCap,
|
||||
highPrice: detailResponse.data.highPrice,
|
||||
lowPrice: detailResponse.data.lowPrice
|
||||
}
|
||||
} else {
|
||||
// 如果详情API无数据,尝试搜索API
|
||||
const searchResponse = await searchStocks(stockCode.value)
|
||||
if (searchResponse.data && searchResponse.data.length > 0) {
|
||||
const stockData = searchResponse.data[0]
|
||||
stockInfo.value = {
|
||||
stockCode: stockData.stockCode,
|
||||
stockName: stockData.stockName,
|
||||
currentPrice: stockData.closePrice,
|
||||
changePercent: stockData.changePercent,
|
||||
changeAmount: stockData.changeAmount,
|
||||
volume: stockData.volume,
|
||||
marketCap: stockData.marketCap,
|
||||
highPrice: stockData.highPrice,
|
||||
lowPrice: stockData.lowPrice
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载股票信息失败:', error)
|
||||
ElMessage.error('加载股票信息失败')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,52 +325,117 @@ export default {
|
||||
// 初始化价格图表
|
||||
const initPriceChart = async () => {
|
||||
const chartDom = document.getElementById('price-chart')
|
||||
if (!chartDom || historyData.value.length === 0) return
|
||||
if (!chartDom) return
|
||||
|
||||
const myChart = echarts.init(chartDom)
|
||||
const dates = historyData.value.map(item => item.timestamp?.split('T')[0] || item.date)
|
||||
const prices = historyData.value.map(item => item.currentPrice)
|
||||
|
||||
if (historyData.value.length === 0) {
|
||||
// 如果没有数据,显示空状态
|
||||
const option = {
|
||||
title: {
|
||||
text: '暂无历史数据',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
textStyle: {
|
||||
color: '#999',
|
||||
fontSize: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
myChart.setOption(option)
|
||||
return
|
||||
}
|
||||
|
||||
// 处理日期格式
|
||||
const dates = historyData.value.map(item => {
|
||||
if (item.tradeDate) {
|
||||
const date = new Date(item.tradeDate)
|
||||
return date.toISOString().split('T')[0]
|
||||
}
|
||||
return item.timestamp?.split('T')[0] || item.date || '未知日期'
|
||||
})
|
||||
|
||||
// 处理价格数据
|
||||
const prices = historyData.value.map(item => {
|
||||
return item.closePrice || item.currentPrice || 0
|
||||
})
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '价格走势',
|
||||
left: 'center'
|
||||
text: '历史价格走势',
|
||||
left: 'left',
|
||||
textStyle: {
|
||||
fontSize: 16,
|
||||
color: '#303133'
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: (params) => {
|
||||
const dataIndex = params[0].dataIndex
|
||||
const data = historyData.value[dataIndex]
|
||||
const price = data.closePrice || data.currentPrice || 0
|
||||
const changePercent = data.changePercent || 0
|
||||
const volume = data.volume || 0
|
||||
|
||||
return `
|
||||
<div>
|
||||
<p>日期: ${dates[dataIndex]}</p>
|
||||
<p>价格: ¥${data.currentPrice?.toFixed(2)}</p>
|
||||
<p>涨跌幅: ${data.changePercent?.toFixed(2)}%</p>
|
||||
<p>成交量: ${formatVolume(data.volume)}</p>
|
||||
<div style="padding: 10px;">
|
||||
<p><strong>日期:</strong> ${dates[dataIndex]}</p>
|
||||
<p><strong>收盘价:</strong> ¥${price.toFixed(2)}</p>
|
||||
<p><strong>涨跌幅:</strong> ${changePercent.toFixed(2)}%</p>
|
||||
<p><strong>成交量:</strong> ${formatVolume(volume)}</p>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
top: '15%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: dates
|
||||
data: dates,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '价格(¥)',
|
||||
scale: true
|
||||
scale: true,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#E6E8EB'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: prices,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 4,
|
||||
lineStyle: {
|
||||
color: '#409EFF',
|
||||
width: 2
|
||||
},
|
||||
areaStyle: {
|
||||
color: 'rgba(64, 158, 255, 0.1)'
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#409EFF'
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -352,30 +446,89 @@ export default {
|
||||
// 初始化趋势图表
|
||||
const initTrendChart = async () => {
|
||||
const chartDom = document.getElementById('trend-chart')
|
||||
if (!chartDom || !trendData.value.priceHistory) return
|
||||
if (!chartDom) return
|
||||
|
||||
const myChart = echarts.init(chartDom)
|
||||
|
||||
if (!trendData.value || !trendData.value.priceHistory || trendData.value.priceHistory.length === 0) {
|
||||
// 如果没有趋势数据,显示空状态
|
||||
const option = {
|
||||
title: {
|
||||
text: '暂无趋势数据',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
textStyle: {
|
||||
color: '#999',
|
||||
fontSize: 14
|
||||
}
|
||||
}
|
||||
}
|
||||
myChart.setOption(option)
|
||||
return
|
||||
}
|
||||
|
||||
const data = trendData.value.priceHistory || []
|
||||
const dates = data.map(item => item.date)
|
||||
const prices = data.map(item => item.price)
|
||||
const ma5 = data.map(item => item.ma5)
|
||||
const ma20 = data.map(item => item.ma20)
|
||||
const dates = data.map(item => {
|
||||
if (item.date) return item.date
|
||||
if (item.tradeDate) {
|
||||
const date = new Date(item.tradeDate)
|
||||
return date.toISOString().split('T')[0]
|
||||
}
|
||||
return '未知日期'
|
||||
})
|
||||
const prices = data.map(item => item.price || item.closePrice || 0)
|
||||
const ma5 = data.map(item => item.ma5 || null)
|
||||
const ma20 = data.map(item => item.ma20 || null)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
trigger: 'axis',
|
||||
formatter: (params) => {
|
||||
let result = `<div style="padding: 10px;"><strong>${params[0].axisValue}</strong><br/>`
|
||||
params.forEach(param => {
|
||||
if (param.value !== null) {
|
||||
result += `<span style="color: ${param.color};">●</span> ${param.seriesName}: ¥${param.value.toFixed(2)}<br/>`
|
||||
}
|
||||
})
|
||||
result += '</div>'
|
||||
return result
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['股价', 'MA5', 'MA20']
|
||||
data: ['股价', 'MA5', 'MA20'],
|
||||
top: 10
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
top: '15%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: dates
|
||||
data: dates,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '价格(¥)',
|
||||
scale: true
|
||||
scale: true,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#E6E8EB'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
@@ -383,21 +536,28 @@ export default {
|
||||
data: prices,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
lineStyle: { color: '#409EFF' }
|
||||
symbol: 'circle',
|
||||
symbolSize: 4,
|
||||
lineStyle: { color: '#409EFF', width: 2 },
|
||||
itemStyle: { color: '#409EFF' }
|
||||
},
|
||||
{
|
||||
name: 'MA5',
|
||||
data: ma5,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
lineStyle: { color: '#67C23A' }
|
||||
symbol: 'none',
|
||||
lineStyle: { color: '#67C23A', width: 1 },
|
||||
itemStyle: { color: '#67C23A' }
|
||||
},
|
||||
{
|
||||
name: 'MA20',
|
||||
data: ma20,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
lineStyle: { color: '#E6A23C' }
|
||||
symbol: 'none',
|
||||
lineStyle: { color: '#E6A23C', width: 1 },
|
||||
itemStyle: { color: '#E6A23C' }
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -407,17 +567,47 @@ export default {
|
||||
// 初始化预测图表
|
||||
const initPredictionChart = async () => {
|
||||
const chartDom = document.getElementById('prediction-chart')
|
||||
if (!chartDom || predictionData.value.length === 0) return
|
||||
if (!chartDom) return
|
||||
|
||||
const myChart = echarts.init(chartDom)
|
||||
|
||||
if (predictionData.value.length === 0) {
|
||||
// 如果没有预测数据,显示空状态
|
||||
const option = {
|
||||
title: {
|
||||
text: '暂无预测数据,请点击"生成预测"按钮',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
textStyle: {
|
||||
color: '#999',
|
||||
fontSize: 14
|
||||
}
|
||||
}
|
||||
}
|
||||
myChart.setOption(option)
|
||||
return
|
||||
}
|
||||
|
||||
// 历史数据(最近10天)
|
||||
const historyDates = historyData.value.slice(-10).map(item => item.timestamp?.split('T')[0] || item.date)
|
||||
const historyPrices = historyData.value.slice(-10).map(item => item.currentPrice)
|
||||
const historySlice = historyData.value.slice(-10)
|
||||
const historyDates = historySlice.map(item => {
|
||||
if (item.tradeDate) {
|
||||
const date = new Date(item.tradeDate)
|
||||
return date.toISOString().split('T')[0]
|
||||
}
|
||||
return item.timestamp?.split('T')[0] || item.date || '历史'
|
||||
})
|
||||
const historyPrices = historySlice.map(item => item.closePrice || item.currentPrice || 0)
|
||||
|
||||
// 预测数据
|
||||
const predictionDates = predictionData.value.map(item => item.timestamp?.split('T')[0] || item.date)
|
||||
const predictionPrices = predictionData.value.map(item => item.currentPrice)
|
||||
const predictionDates = predictionData.value.map(item => {
|
||||
if (item.tradeDate) {
|
||||
const date = new Date(item.tradeDate)
|
||||
return date.toISOString().split('T')[0]
|
||||
}
|
||||
return item.timestamp?.split('T')[0] || item.date || '预测'
|
||||
})
|
||||
const predictionPrices = predictionData.value.map(item => item.closePrice || item.currentPrice || 0)
|
||||
|
||||
const allDates = [...historyDates, ...predictionDates]
|
||||
const historySeries = [...historyPrices, ...new Array(predictionDates.length).fill(null)]
|
||||
@@ -425,23 +615,61 @@ export default {
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '股价预测',
|
||||
left: 'center'
|
||||
text: '股价预测分析',
|
||||
left: 'left',
|
||||
textStyle: {
|
||||
fontSize: 16,
|
||||
color: '#303133'
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
trigger: 'axis',
|
||||
formatter: (params) => {
|
||||
let result = `<div style="padding: 10px;"><strong>${params[0].axisValue}</strong><br/>`
|
||||
params.forEach(param => {
|
||||
if (param.value !== null) {
|
||||
result += `<span style="color: ${param.color};">●</span> ${param.seriesName}: ¥${param.value.toFixed(2)}<br/>`
|
||||
}
|
||||
})
|
||||
result += '</div>'
|
||||
return result
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['历史价格', '预测价格']
|
||||
data: ['历史价格', '预测价格'],
|
||||
top: 30
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
top: '20%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: allDates
|
||||
data: allDates,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '价格(¥)',
|
||||
scale: true
|
||||
scale: true,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#E6E8EB'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
@@ -449,19 +677,26 @@ export default {
|
||||
data: historySeries,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
lineStyle: { color: '#409EFF' },
|
||||
areaStyle: { color: 'rgba(64, 158, 255, 0.1)' }
|
||||
symbol: 'circle',
|
||||
symbolSize: 4,
|
||||
lineStyle: { color: '#409EFF', width: 2 },
|
||||
areaStyle: { color: 'rgba(64, 158, 255, 0.1)' },
|
||||
itemStyle: { color: '#409EFF' }
|
||||
},
|
||||
{
|
||||
name: '预测价格',
|
||||
data: predictionSeries,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'diamond',
|
||||
symbolSize: 5,
|
||||
lineStyle: {
|
||||
color: '#F56C6C',
|
||||
type: 'dashed'
|
||||
type: 'dashed',
|
||||
width: 2
|
||||
},
|
||||
areaStyle: { color: 'rgba(245, 108, 108, 0.1)' }
|
||||
areaStyle: { color: 'rgba(245, 108, 108, 0.1)' },
|
||||
itemStyle: { color: '#F56C6C' }
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -502,24 +737,32 @@ export default {
|
||||
// 预测统计
|
||||
const getPredictionMax = () => {
|
||||
if (predictionData.value.length === 0) return '0.00'
|
||||
return Math.max(...predictionData.value.map(item => item.currentPrice)).toFixed(2)
|
||||
const prices = predictionData.value.map(item => item.closePrice || item.currentPrice || 0)
|
||||
return Math.max(...prices).toFixed(2)
|
||||
}
|
||||
|
||||
const getPredictionMin = () => {
|
||||
if (predictionData.value.length === 0) return '0.00'
|
||||
return Math.min(...predictionData.value.map(item => item.currentPrice)).toFixed(2)
|
||||
const prices = predictionData.value.map(item => item.closePrice || item.currentPrice || 0)
|
||||
return Math.min(...prices).toFixed(2)
|
||||
}
|
||||
|
||||
const getPredictionAvg = () => {
|
||||
if (predictionData.value.length === 0) return '0.00'
|
||||
const avg = predictionData.value.reduce((sum, item) => sum + item.currentPrice, 0) / predictionData.value.length
|
||||
const prices = predictionData.value.map(item => item.closePrice || item.currentPrice || 0)
|
||||
const avg = prices.reduce((sum, price) => sum + price, 0) / prices.length
|
||||
return avg.toFixed(2)
|
||||
}
|
||||
|
||||
const getPredictionReturn = () => {
|
||||
if (predictionData.value.length === 0 || !stockInfo.value.currentPrice) return 0
|
||||
const lastPrediction = predictionData.value[predictionData.value.length - 1]
|
||||
const returnRate = ((lastPrediction.currentPrice - stockInfo.value.currentPrice) / stockInfo.value.currentPrice) * 100
|
||||
const lastPrice = lastPrediction.closePrice || lastPrediction.currentPrice || 0
|
||||
const currentPrice = stockInfo.value.currentPrice || 0
|
||||
|
||||
if (currentPrice === 0) return 0
|
||||
|
||||
const returnRate = ((lastPrice - currentPrice) / currentPrice) * 100
|
||||
return returnRate.toFixed(2)
|
||||
}
|
||||
|
||||
|
||||
606
frontend/src/views/StockDetailView.vue
Normal file
606
frontend/src/views/StockDetailView.vue
Normal file
@@ -0,0 +1,606 @@
|
||||
<template>
|
||||
<div class="stock-detail-view">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header">
|
||||
<div class="stock-info">
|
||||
<h1>{{ stockInfo.stockName || stockCode }}</h1>
|
||||
<p class="stock-code">{{ stockCode }}</p>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<el-button @click="goBack">
|
||||
<el-icon><ArrowLeft /></el-icon>
|
||||
返回
|
||||
</el-button>
|
||||
<el-button type="primary" @click="refreshData" :loading="loading">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 股票基本信息 -->
|
||||
<el-row :gutter="20" class="stock-overview">
|
||||
<el-col :span="8">
|
||||
<el-card class="price-card">
|
||||
<div class="price-section">
|
||||
<div class="current-price">
|
||||
¥{{ stockInfo.closePrice?.toFixed(2) || '0.00' }}
|
||||
</div>
|
||||
<div class="price-change" :class="getPriceChangeClass()">
|
||||
<el-icon v-if="stockInfo.changePercent > 0"><CaretTop /></el-icon>
|
||||
<el-icon v-else-if="stockInfo.changePercent < 0"><CaretBottom /></el-icon>
|
||||
<el-icon v-else><Minus /></el-icon>
|
||||
{{ formatChangePercent(stockInfo.changePercent) }}%
|
||||
({{ formatChangeAmount(stockInfo.changeAmount) }})
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-card class="metrics-card">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">开盘价</div>
|
||||
<div class="metric-value">¥{{ stockInfo.openPrice?.toFixed(2) || '--' }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">最高价</div>
|
||||
<div class="metric-value">¥{{ stockInfo.highPrice?.toFixed(2) || '--' }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">最低价</div>
|
||||
<div class="metric-value">¥{{ stockInfo.lowPrice?.toFixed(2) || '--' }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">昨收价</div>
|
||||
<div class="metric-value">¥{{ stockInfo.preClosePrice?.toFixed(2) || '--' }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20" style="margin-top: 20px;">
|
||||
<el-col :span="6">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">成交量</div>
|
||||
<div class="metric-value">{{ formatVolume(stockInfo.volume) }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">成交额</div>
|
||||
<div class="metric-value">{{ formatTurnover(stockInfo.turnover) }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">市值</div>
|
||||
<div class="metric-value">{{ formatMarketCap(stockInfo.marketCap) }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">换手率</div>
|
||||
<div class="metric-value">{{ stockInfo.turnoverRate?.toFixed(2) || '--' }}%</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 分时图 -->
|
||||
<el-row :gutter="20" class="chart-section">
|
||||
<el-col :span="24">
|
||||
<el-card class="chart-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>实时分时图</span>
|
||||
<div class="chart-controls">
|
||||
<el-button-group size="small">
|
||||
<el-button :type="chartType === 'minute' ? 'primary' : ''" @click="changeChartType('minute')">
|
||||
分时
|
||||
</el-button>
|
||||
<el-button :type="chartType === 'kline' ? 'primary' : ''" @click="changeChartType('kline')">
|
||||
K线
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div id="detail-chart" style="height: 500px;" v-loading="chartLoading"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 详细信息表格 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-card class="info-card">
|
||||
<template #header>
|
||||
<span>详细信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="3" border>
|
||||
<el-descriptions-item label="股票代码">{{ stockInfo.stockCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="股票名称">{{ stockInfo.stockName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="交易日期">{{ formatDate(stockInfo.tradeDate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="开盘价">¥{{ stockInfo.openPrice?.toFixed(2) || '--' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="收盘价">¥{{ stockInfo.closePrice?.toFixed(2) || '--' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="最高价">¥{{ stockInfo.highPrice?.toFixed(2) || '--' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="最低价">¥{{ stockInfo.lowPrice?.toFixed(2) || '--' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="昨收价">¥{{ stockInfo.preClosePrice?.toFixed(2) || '--' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="涨跌额">{{ formatChangeAmount(stockInfo.changeAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="涨跌幅">{{ formatChangePercent(stockInfo.changePercent) }}%</el-descriptions-item>
|
||||
<el-descriptions-item label="成交量">{{ formatVolume(stockInfo.volume) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="成交额">{{ formatTurnover(stockInfo.turnover) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总市值">{{ formatMarketCap(stockInfo.marketCap) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="流通市值">{{ formatMarketCap(stockInfo.circulationMarketCap) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="换手率">{{ stockInfo.turnoverRate?.toFixed(2) || '--' }}%</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { getStockDetail } from '@/api/stock'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
export default {
|
||||
name: 'StockDetailView',
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const stockCode = ref(route.params.stockCode)
|
||||
|
||||
const stockInfo = ref({})
|
||||
const loading = ref(false)
|
||||
const chartLoading = ref(false)
|
||||
const chartType = ref('minute')
|
||||
|
||||
// 加载股票详情
|
||||
const loadStockDetail = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const response = await getStockDetail(stockCode.value)
|
||||
if (response.code === 200 && response.data) {
|
||||
stockInfo.value = response.data
|
||||
await nextTick()
|
||||
initChart()
|
||||
} else {
|
||||
ElMessage.error(response.message || '获取股票详情失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取股票详情失败:', error)
|
||||
ElMessage.error('获取股票详情失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化图表
|
||||
const initChart = async () => {
|
||||
await nextTick()
|
||||
const chartDom = document.getElementById('detail-chart')
|
||||
if (!chartDom) return
|
||||
|
||||
const myChart = echarts.init(chartDom)
|
||||
|
||||
if (chartType.value === 'minute') {
|
||||
initMinuteChart(myChart)
|
||||
} else {
|
||||
initKLineChart(myChart)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化分时图
|
||||
const initMinuteChart = (chart) => {
|
||||
const option = {
|
||||
title: {
|
||||
text: `${stockInfo.value.stockName}(${stockInfo.value.stockCode})`,
|
||||
left: 'left'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: generateTimeData(),
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
scale: true,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#E6E8EB'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '价格',
|
||||
type: 'line',
|
||||
data: generatePriceData(),
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
color: stockInfo.value.changePercent >= 0 ? '#f56c6c' : '#67c23a'
|
||||
},
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [{
|
||||
offset: 0,
|
||||
color: stockInfo.value.changePercent >= 0 ? 'rgba(245, 108, 108, 0.3)' : 'rgba(103, 194, 58, 0.3)'
|
||||
}, {
|
||||
offset: 1,
|
||||
color: stockInfo.value.changePercent >= 0 ? 'rgba(245, 108, 108, 0.1)' : 'rgba(103, 194, 58, 0.1)'
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
}
|
||||
|
||||
// 初始化K线图
|
||||
const initKLineChart = (chart) => {
|
||||
const option = {
|
||||
title: {
|
||||
text: `${stockInfo.value.stockName}(${stockInfo.value.stockCode}) K线图`,
|
||||
left: 'left'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '15%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: generateDateData(),
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
scale: true,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'K线',
|
||||
type: 'candlestick',
|
||||
data: generateKLineData(),
|
||||
itemStyle: {
|
||||
color: '#f56c6c',
|
||||
color0: '#67c23a',
|
||||
borderColor: '#f56c6c',
|
||||
borderColor0: '#67c23a'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
}
|
||||
|
||||
// 生成模拟时间数据
|
||||
const generateTimeData = () => {
|
||||
const times = []
|
||||
for (let i = 9; i <= 15; i++) {
|
||||
for (let j = 0; j < 60; j += 5) {
|
||||
if (i === 9 && j < 30) continue
|
||||
if (i === 15 && j > 0) break
|
||||
if (i === 11 && j >= 30) continue
|
||||
if (i === 12) continue
|
||||
if (i === 13 && j === 0) continue
|
||||
times.push(`${i.toString().padStart(2, '0')}:${j.toString().padStart(2, '0')}`)
|
||||
}
|
||||
}
|
||||
return times
|
||||
}
|
||||
|
||||
// 生成模拟价格数据
|
||||
const generatePriceData = () => {
|
||||
const basePrice = stockInfo.value.closePrice || 10
|
||||
const data = []
|
||||
const times = generateTimeData()
|
||||
|
||||
for (let i = 0; i < times.length; i++) {
|
||||
const fluctuation = (Math.random() - 0.5) * 0.1
|
||||
const price = basePrice * (1 + fluctuation)
|
||||
data.push(price.toFixed(2))
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// 生成模拟日期数据
|
||||
const generateDateData = () => {
|
||||
const dates = []
|
||||
const today = new Date()
|
||||
for (let i = 29; i >= 0; i--) {
|
||||
const date = new Date(today)
|
||||
date.setDate(date.getDate() - i)
|
||||
dates.push(date.toISOString().split('T')[0])
|
||||
}
|
||||
return dates
|
||||
}
|
||||
|
||||
// 生成模拟K线数据
|
||||
const generateKLineData = () => {
|
||||
const basePrice = stockInfo.value.closePrice || 10
|
||||
const data = []
|
||||
let currentPrice = basePrice
|
||||
|
||||
for (let i = 0; i < 30; i++) {
|
||||
const change = (Math.random() - 0.5) * 0.2
|
||||
const open = currentPrice
|
||||
const close = currentPrice * (1 + change)
|
||||
const high = Math.max(open, close) * (1 + Math.random() * 0.05)
|
||||
const low = Math.min(open, close) * (1 - Math.random() * 0.05)
|
||||
|
||||
data.push([open.toFixed(2), close.toFixed(2), low.toFixed(2), high.toFixed(2)])
|
||||
currentPrice = close
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// 切换图表类型
|
||||
const changeChartType = (type) => {
|
||||
chartType.value = type
|
||||
initChart()
|
||||
}
|
||||
|
||||
// 刷新数据
|
||||
const refreshData = () => {
|
||||
loadStockDetail()
|
||||
}
|
||||
|
||||
// 返回
|
||||
const goBack = () => {
|
||||
router.go(-1)
|
||||
}
|
||||
|
||||
// 格式化函数
|
||||
const getPriceChangeClass = () => {
|
||||
if (!stockInfo.value.changePercent) return ''
|
||||
return stockInfo.value.changePercent > 0 ? 'positive' : stockInfo.value.changePercent < 0 ? 'negative' : ''
|
||||
}
|
||||
|
||||
const formatChangePercent = (value) => {
|
||||
if (!value) return '0.00'
|
||||
return (value > 0 ? '+' : '') + value.toFixed(2)
|
||||
}
|
||||
|
||||
const formatChangeAmount = (value) => {
|
||||
if (!value) return '0.00'
|
||||
return (value > 0 ? '+' : '') + value.toFixed(2)
|
||||
}
|
||||
|
||||
const formatVolume = (volume) => {
|
||||
if (!volume) return '0'
|
||||
if (volume >= 100000000) {
|
||||
return (volume / 100000000).toFixed(2) + '亿'
|
||||
} else if (volume >= 10000) {
|
||||
return (volume / 10000).toFixed(2) + '万'
|
||||
}
|
||||
return volume.toString()
|
||||
}
|
||||
|
||||
const formatTurnover = (turnover) => {
|
||||
if (!turnover) return '0'
|
||||
if (turnover >= 100000000) {
|
||||
return (turnover / 100000000).toFixed(2) + '亿元'
|
||||
} else if (turnover >= 10000) {
|
||||
return (turnover / 10000).toFixed(2) + '万元'
|
||||
}
|
||||
return turnover.toFixed(2) + '元'
|
||||
}
|
||||
|
||||
const formatMarketCap = (marketCap) => {
|
||||
if (!marketCap) return '0'
|
||||
if (marketCap >= 100000000) {
|
||||
return (marketCap / 100000000).toFixed(2) + '亿元'
|
||||
} else if (marketCap >= 10000) {
|
||||
return (marketCap / 10000).toFixed(2) + '万元'
|
||||
}
|
||||
return marketCap.toFixed(2) + '元'
|
||||
}
|
||||
|
||||
const formatDate = (date) => {
|
||||
if (!date) return '--'
|
||||
return new Date(date).toLocaleDateString()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadStockDetail()
|
||||
})
|
||||
|
||||
return {
|
||||
stockCode,
|
||||
stockInfo,
|
||||
loading,
|
||||
chartLoading,
|
||||
chartType,
|
||||
loadStockDetail,
|
||||
changeChartType,
|
||||
refreshData,
|
||||
goBack,
|
||||
getPriceChangeClass,
|
||||
formatChangePercent,
|
||||
formatChangeAmount,
|
||||
formatVolume,
|
||||
formatTurnover,
|
||||
formatMarketCap,
|
||||
formatDate
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.stock-detail-view {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.stock-info {
|
||||
h1 {
|
||||
margin: 0;
|
||||
color: #303133;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.stock-code {
|
||||
margin: 5px 0 0 0;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.stock-overview {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.price-card {
|
||||
.price-section {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
|
||||
.current-price {
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.price-change {
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
|
||||
&.positive {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
&.negative {
|
||||
color: #67c23a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.metrics-card {
|
||||
.metric-item {
|
||||
text-align: center;
|
||||
|
||||
.metric-label {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chart-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-card, .info-card {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__label) {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__content) {
|
||||
color: #303133;
|
||||
}
|
||||
</style>
|
||||
795
frontend/src/views/StockPredictionView.vue
Normal file
795
frontend/src/views/StockPredictionView.vue
Normal file
@@ -0,0 +1,795 @@
|
||||
<template>
|
||||
<div class="stock-prediction-view">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header">
|
||||
<div class="stock-info">
|
||||
<h1>{{ stockCode }} - 股票预测分析</h1>
|
||||
<p class="stock-code">预测模型基于历史数据和技术指标</p>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<el-button @click="goBack">
|
||||
<el-icon><ArrowLeft /></el-icon>
|
||||
返回
|
||||
</el-button>
|
||||
<el-button type="primary" @click="generatePrediction" :loading="loading">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
生成预测
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 预测配置 -->
|
||||
<el-row :gutter="20" class="prediction-config">
|
||||
<el-col :span="24">
|
||||
<el-card class="config-card">
|
||||
<template #header>
|
||||
<span>预测配置</span>
|
||||
</template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<div class="config-item">
|
||||
<label>预测天数:</label>
|
||||
<el-select v-model="predictionDays" @change="generatePrediction" style="width: 100%;">
|
||||
<el-option label="3天" :value="3" />
|
||||
<el-option label="7天" :value="7" />
|
||||
<el-option label="15天" :value="15" />
|
||||
<el-option label="30天" :value="30" />
|
||||
</el-select>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="config-item">
|
||||
<label>预测模型:</label>
|
||||
<el-select v-model="predictionModel" style="width: 100%;">
|
||||
<el-option label="线性回归" value="linear" />
|
||||
<el-option label="移动平均" value="ma" />
|
||||
<el-option label="ARIMA" value="arima" />
|
||||
<el-option label="神经网络" value="nn" />
|
||||
</el-select>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="config-item">
|
||||
<label>置信区间:</label>
|
||||
<el-select v-model="confidenceLevel" style="width: 100%;">
|
||||
<el-option label="90%" value="0.9" />
|
||||
<el-option label="95%" value="0.95" />
|
||||
<el-option label="99%" value="0.99" />
|
||||
</el-select>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="config-item">
|
||||
<label>历史数据:</label>
|
||||
<el-select v-model="historyDays" style="width: 100%;">
|
||||
<el-option label="30天" :value="30" />
|
||||
<el-option label="60天" :value="60" />
|
||||
<el-option label="90天" :value="90" />
|
||||
<el-option label="180天" :value="180" />
|
||||
</el-select>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 预测结果概览 -->
|
||||
<el-row :gutter="20" class="prediction-overview" v-if="predictionData.length > 0">
|
||||
<el-col :span="6">
|
||||
<el-card class="prediction-card">
|
||||
<div class="prediction-info">
|
||||
<div class="prediction-value positive">¥{{ getPredictionMax() }}</div>
|
||||
<div class="prediction-subtitle">预测最高价</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="prediction-card">
|
||||
<div class="prediction-info">
|
||||
<div class="prediction-value negative">¥{{ getPredictionMin() }}</div>
|
||||
<div class="prediction-subtitle">预测最低价</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="prediction-card">
|
||||
<div class="prediction-info">
|
||||
<div class="prediction-value">¥{{ getPredictionAvg() }}</div>
|
||||
<div class="prediction-subtitle">平均预测价</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="prediction-card">
|
||||
<div class="prediction-info">
|
||||
<div class="prediction-value" :class="{ 'positive': getPredictionReturn() > 0, 'negative': getPredictionReturn() < 0 }">
|
||||
{{ formatChangePercent(getPredictionReturn()) }}%
|
||||
</div>
|
||||
<div class="prediction-subtitle">预期收益率</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 预测图表 -->
|
||||
<el-row :gutter="20" class="chart-section">
|
||||
<el-col :span="16">
|
||||
<el-card class="chart-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>价格预测图</span>
|
||||
<div class="chart-controls">
|
||||
<el-switch
|
||||
v-model="showConfidenceInterval"
|
||||
@change="updateChart"
|
||||
active-text="显示置信区间"
|
||||
inactive-text="隐藏置信区间"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div id="prediction-chart" style="height: 500px;" v-loading="chartLoading"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card class="analysis-card">
|
||||
<template #header>
|
||||
<span>预测分析</span>
|
||||
</template>
|
||||
<div class="analysis-content">
|
||||
<div class="analysis-item">
|
||||
<div class="analysis-label">预测准确度</div>
|
||||
<div class="analysis-value">
|
||||
<el-progress :percentage="predictionAccuracy" :color="getAccuracyColor()" />
|
||||
<span class="accuracy-text">{{ predictionAccuracy }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="analysis-item">
|
||||
<div class="analysis-label">风险等级</div>
|
||||
<div class="analysis-value">
|
||||
<el-tag :type="getRiskTagType()">{{ getRiskLevel() }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="analysis-item">
|
||||
<div class="analysis-label">投资建议</div>
|
||||
<div class="analysis-value">
|
||||
<el-tag :type="getRecommendationTagType()">{{ getRecommendation() }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="analysis-item">
|
||||
<div class="analysis-label">支撑位</div>
|
||||
<div class="analysis-value">¥{{ getSupportLevel() }}</div>
|
||||
</div>
|
||||
<div class="analysis-item">
|
||||
<div class="analysis-label">阻力位</div>
|
||||
<div class="analysis-value">¥{{ getResistanceLevel() }}</div>
|
||||
</div>
|
||||
<div class="analysis-item">
|
||||
<div class="analysis-label">波动率</div>
|
||||
<div class="analysis-value">{{ getVolatility() }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 预测数据表格 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-card class="table-card">
|
||||
<template #header>
|
||||
<span>预测数据详情</span>
|
||||
</template>
|
||||
<el-table :data="predictionTableData" style="width: 100%" v-loading="loading">
|
||||
<el-table-column prop="date" label="预测日期" width="120">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.date) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="predictedPrice" label="预测价格" width="120">
|
||||
<template #default="scope">
|
||||
¥{{ scope.row.predictedPrice?.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="lowerBound" label="下限价格" width="120">
|
||||
<template #default="scope">
|
||||
¥{{ scope.row.lowerBound?.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="upperBound" label="上限价格" width="120">
|
||||
<template #default="scope">
|
||||
¥{{ scope.row.upperBound?.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="changePercent" label="预测涨跌幅" width="120">
|
||||
<template #default="scope">
|
||||
<span :class="{ 'positive': scope.row.changePercent > 0, 'negative': scope.row.changePercent < 0 }">
|
||||
{{ formatChangePercent(scope.row.changePercent) }}%
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="confidence" label="置信度" width="100">
|
||||
<template #default="scope">
|
||||
{{ (scope.row.confidence * 100).toFixed(1) }}%
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="riskLevel" label="风险等级" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getRiskTagTypeByLevel(scope.row.riskLevel)" size="small">
|
||||
{{ scope.row.riskLevel }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { getStockPrediction, getStockDetail } from '@/api/stock'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
export default {
|
||||
name: 'StockPredictionView',
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const stockCode = ref(route.params.stockCode)
|
||||
|
||||
const predictionData = ref([])
|
||||
const predictionTableData = ref([])
|
||||
const loading = ref(false)
|
||||
const chartLoading = ref(false)
|
||||
|
||||
// 配置参数
|
||||
const predictionDays = ref(7)
|
||||
const predictionModel = ref('linear')
|
||||
const confidenceLevel = ref('0.95')
|
||||
const historyDays = ref(60)
|
||||
const showConfidenceInterval = ref(true)
|
||||
|
||||
// 预测分析结果
|
||||
const predictionAccuracy = ref(85)
|
||||
const currentPrice = ref(0)
|
||||
|
||||
// 生成预测数据
|
||||
const generatePrediction = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
chartLoading.value = true
|
||||
|
||||
// 获取当前价格
|
||||
const detailResponse = await getStockDetail(stockCode.value)
|
||||
if (detailResponse.code === 200 && detailResponse.data) {
|
||||
currentPrice.value = detailResponse.data.closePrice || 10
|
||||
}
|
||||
|
||||
// 获取预测数据(这里使用模拟数据,实际应该调用预测API)
|
||||
const response = await getStockPrediction(stockCode.value, predictionDays.value)
|
||||
|
||||
// 生成模拟预测数据
|
||||
const mockPredictionData = generateMockPredictionData()
|
||||
predictionData.value = mockPredictionData
|
||||
predictionTableData.value = generateTableData(mockPredictionData)
|
||||
|
||||
await nextTick()
|
||||
initPredictionChart()
|
||||
|
||||
ElMessage.success('预测数据生成成功')
|
||||
} catch (error) {
|
||||
console.error('生成预测数据失败:', error)
|
||||
ElMessage.error('生成预测数据失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
chartLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 生成模拟预测数据
|
||||
const generateMockPredictionData = () => {
|
||||
const data = []
|
||||
let basePrice = currentPrice.value
|
||||
const today = new Date()
|
||||
|
||||
for (let i = 1; i <= predictionDays.value; i++) {
|
||||
const date = new Date(today)
|
||||
date.setDate(date.getDate() + i)
|
||||
|
||||
// 模拟价格波动
|
||||
const trend = (Math.random() - 0.5) * 0.1 // -5% 到 +5%
|
||||
const noise = (Math.random() - 0.5) * 0.05 // 噪声
|
||||
const predictedPrice = basePrice * (1 + trend + noise)
|
||||
|
||||
// 置信区间
|
||||
const volatility = 0.05
|
||||
const lowerBound = predictedPrice * (1 - volatility)
|
||||
const upperBound = predictedPrice * (1 + volatility)
|
||||
|
||||
data.push({
|
||||
date: date,
|
||||
predictedPrice: predictedPrice,
|
||||
lowerBound: lowerBound,
|
||||
upperBound: upperBound,
|
||||
changePercent: ((predictedPrice - basePrice) / basePrice) * 100,
|
||||
confidence: parseFloat(confidenceLevel.value),
|
||||
riskLevel: getRiskLevelByVolatility(Math.abs(trend + noise) * 100)
|
||||
})
|
||||
|
||||
basePrice = predictedPrice
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// 生成表格数据
|
||||
const generateTableData = (data) => {
|
||||
return data.map(item => ({
|
||||
...item,
|
||||
date: item.date.toISOString().split('T')[0]
|
||||
}))
|
||||
}
|
||||
|
||||
// 初始化预测图表
|
||||
const initPredictionChart = () => {
|
||||
const chartDom = document.getElementById('prediction-chart')
|
||||
if (!chartDom) return
|
||||
|
||||
const myChart = echarts.init(chartDom)
|
||||
|
||||
// 历史数据(模拟)
|
||||
const historyDates = []
|
||||
const historyPrices = []
|
||||
const today = new Date()
|
||||
|
||||
for (let i = 30; i >= 1; i--) {
|
||||
const date = new Date(today)
|
||||
date.setDate(date.getDate() - i)
|
||||
historyDates.push(date.toISOString().split('T')[0])
|
||||
|
||||
const fluctuation = (Math.random() - 0.5) * 0.1
|
||||
const price = currentPrice.value * (1 + fluctuation)
|
||||
historyPrices.push(price.toFixed(2))
|
||||
}
|
||||
|
||||
// 预测数据
|
||||
const predictionDates = predictionData.value.map(item => item.date.toISOString().split('T')[0])
|
||||
const predictionPrices = predictionData.value.map(item => item.predictedPrice.toFixed(2))
|
||||
const lowerBounds = predictionData.value.map(item => item.lowerBound.toFixed(2))
|
||||
const upperBounds = predictionData.value.map(item => item.upperBound.toFixed(2))
|
||||
|
||||
const allDates = [...historyDates, ...predictionDates]
|
||||
const allPrices = [...historyPrices, ...predictionPrices]
|
||||
|
||||
const series = [
|
||||
{
|
||||
name: '历史价格',
|
||||
type: 'line',
|
||||
data: historyPrices.map((price, index) => [historyDates[index], price]),
|
||||
lineStyle: {
|
||||
color: '#409EFF',
|
||||
width: 2
|
||||
},
|
||||
symbol: 'circle',
|
||||
symbolSize: 4
|
||||
},
|
||||
{
|
||||
name: '预测价格',
|
||||
type: 'line',
|
||||
data: predictionPrices.map((price, index) => [predictionDates[index], price]),
|
||||
lineStyle: {
|
||||
color: '#F56C6C',
|
||||
width: 2,
|
||||
type: 'dashed'
|
||||
},
|
||||
symbol: 'diamond',
|
||||
symbolSize: 6
|
||||
}
|
||||
]
|
||||
|
||||
if (showConfidenceInterval.value) {
|
||||
series.push({
|
||||
name: '置信区间',
|
||||
type: 'line',
|
||||
data: lowerBounds.map((price, index) => [predictionDates[index], price]),
|
||||
lineStyle: {
|
||||
color: '#E6A23C',
|
||||
width: 1,
|
||||
opacity: 0.6
|
||||
},
|
||||
symbol: 'none',
|
||||
areaStyle: {
|
||||
color: 'rgba(230, 162, 60, 0.2)'
|
||||
},
|
||||
stack: 'confidence'
|
||||
})
|
||||
|
||||
series.push({
|
||||
name: '',
|
||||
type: 'line',
|
||||
data: upperBounds.map((price, index) => [predictionDates[index], price]),
|
||||
lineStyle: {
|
||||
color: '#E6A23C',
|
||||
width: 1,
|
||||
opacity: 0.6
|
||||
},
|
||||
symbol: 'none',
|
||||
areaStyle: {
|
||||
color: 'rgba(230, 162, 60, 0.2)'
|
||||
},
|
||||
stack: 'confidence'
|
||||
})
|
||||
}
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: `${stockCode.value} 股价预测分析`,
|
||||
left: 'left'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: showConfidenceInterval.value ? ['历史价格', '预测价格', '置信区间'] : ['历史价格', '预测价格'],
|
||||
top: 30
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
top: '15%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
scale: true,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#E6E8EB'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: series
|
||||
}
|
||||
|
||||
myChart.setOption(option)
|
||||
}
|
||||
|
||||
// 更新图表
|
||||
const updateChart = () => {
|
||||
initPredictionChart()
|
||||
}
|
||||
|
||||
// 返回
|
||||
const goBack = () => {
|
||||
router.go(-1)
|
||||
}
|
||||
|
||||
// 计算预测统计
|
||||
const getPredictionMax = () => {
|
||||
if (predictionData.value.length === 0) return '0.00'
|
||||
const max = Math.max(...predictionData.value.map(item => item.predictedPrice))
|
||||
return max.toFixed(2)
|
||||
}
|
||||
|
||||
const getPredictionMin = () => {
|
||||
if (predictionData.value.length === 0) return '0.00'
|
||||
const min = Math.min(...predictionData.value.map(item => item.predictedPrice))
|
||||
return min.toFixed(2)
|
||||
}
|
||||
|
||||
const getPredictionAvg = () => {
|
||||
if (predictionData.value.length === 0) return '0.00'
|
||||
const sum = predictionData.value.reduce((acc, item) => acc + item.predictedPrice, 0)
|
||||
return (sum / predictionData.value.length).toFixed(2)
|
||||
}
|
||||
|
||||
const getPredictionReturn = () => {
|
||||
if (predictionData.value.length === 0) return 0
|
||||
const lastPrice = predictionData.value[predictionData.value.length - 1].predictedPrice
|
||||
return ((lastPrice - currentPrice.value) / currentPrice.value * 100)
|
||||
}
|
||||
|
||||
// 分析函数
|
||||
const getAccuracyColor = () => {
|
||||
if (predictionAccuracy.value >= 80) return '#67c23a'
|
||||
if (predictionAccuracy.value >= 60) return '#e6a23c'
|
||||
return '#f56c6c'
|
||||
}
|
||||
|
||||
const getRiskLevel = () => {
|
||||
const returnRate = Math.abs(getPredictionReturn())
|
||||
if (returnRate > 10) return '高风险'
|
||||
if (returnRate > 5) return '中风险'
|
||||
return '低风险'
|
||||
}
|
||||
|
||||
const getRiskTagType = () => {
|
||||
const level = getRiskLevel()
|
||||
if (level === '高风险') return 'danger'
|
||||
if (level === '中风险') return 'warning'
|
||||
return 'success'
|
||||
}
|
||||
|
||||
const getRiskLevelByVolatility = (volatility) => {
|
||||
if (volatility > 5) return '高风险'
|
||||
if (volatility > 2) return '中风险'
|
||||
return '低风险'
|
||||
}
|
||||
|
||||
const getRiskTagTypeByLevel = (level) => {
|
||||
if (level === '高风险') return 'danger'
|
||||
if (level === '中风险') return 'warning'
|
||||
return 'success'
|
||||
}
|
||||
|
||||
const getRecommendation = () => {
|
||||
const returnRate = getPredictionReturn()
|
||||
if (returnRate > 5) return '买入'
|
||||
if (returnRate > 0) return '持有'
|
||||
if (returnRate > -5) return '观望'
|
||||
return '卖出'
|
||||
}
|
||||
|
||||
const getRecommendationTagType = () => {
|
||||
const recommendation = getRecommendation()
|
||||
if (recommendation === '买入') return 'success'
|
||||
if (recommendation === '持有') return 'primary'
|
||||
if (recommendation === '观望') return 'warning'
|
||||
return 'danger'
|
||||
}
|
||||
|
||||
const getSupportLevel = () => {
|
||||
return (currentPrice.value * 0.95).toFixed(2)
|
||||
}
|
||||
|
||||
const getResistanceLevel = () => {
|
||||
return (currentPrice.value * 1.05).toFixed(2)
|
||||
}
|
||||
|
||||
const getVolatility = () => {
|
||||
if (predictionData.value.length === 0) return '0.00'
|
||||
const prices = predictionData.value.map(item => item.predictedPrice)
|
||||
const avg = prices.reduce((sum, price) => sum + price, 0) / prices.length
|
||||
const variance = prices.reduce((sum, price) => sum + Math.pow(price - avg, 2), 0) / prices.length
|
||||
const volatility = Math.sqrt(variance) / avg * 100
|
||||
return volatility.toFixed(2)
|
||||
}
|
||||
|
||||
// 格式化函数
|
||||
const formatChangePercent = (value) => {
|
||||
if (!value) return '0.00'
|
||||
return (value > 0 ? '+' : '') + value.toFixed(2)
|
||||
}
|
||||
|
||||
const formatDate = (date) => {
|
||||
if (!date) return '--'
|
||||
return new Date(date).toLocaleDateString()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
generatePrediction()
|
||||
})
|
||||
|
||||
return {
|
||||
stockCode,
|
||||
predictionData,
|
||||
predictionTableData,
|
||||
loading,
|
||||
chartLoading,
|
||||
predictionDays,
|
||||
predictionModel,
|
||||
confidenceLevel,
|
||||
historyDays,
|
||||
showConfidenceInterval,
|
||||
predictionAccuracy,
|
||||
generatePrediction,
|
||||
updateChart,
|
||||
goBack,
|
||||
getPredictionMax,
|
||||
getPredictionMin,
|
||||
getPredictionAvg,
|
||||
getPredictionReturn,
|
||||
getAccuracyColor,
|
||||
getRiskLevel,
|
||||
getRiskTagType,
|
||||
getRiskTagTypeByLevel,
|
||||
getRecommendation,
|
||||
getRecommendationTagType,
|
||||
getSupportLevel,
|
||||
getResistanceLevel,
|
||||
getVolatility,
|
||||
formatChangePercent,
|
||||
formatDate
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.stock-prediction-view {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.stock-info {
|
||||
h1 {
|
||||
margin: 0;
|
||||
color: #303133;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.stock-code {
|
||||
margin: 5px 0 0 0;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.prediction-config {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.config-card {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
.config-item {
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.prediction-overview {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.prediction-card {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
.prediction-info {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
|
||||
.prediction-value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&.positive {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
&.negative {
|
||||
color: #67c23a;
|
||||
}
|
||||
}
|
||||
|
||||
.prediction-subtitle {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chart-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-card, .analysis-card, .table-card {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.analysis-content {
|
||||
.analysis-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.analysis-label {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.analysis-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
.accuracy-text {
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.positive {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.negative {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
.positive {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.negative {
|
||||
color: #67c23a;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
658
frontend/src/views/StockTrendView.vue
Normal file
658
frontend/src/views/StockTrendView.vue
Normal file
@@ -0,0 +1,658 @@
|
||||
<template>
|
||||
<div class="stock-trend-view">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header">
|
||||
<div class="stock-info">
|
||||
<h1>{{ trendData.stockName || stockCode }} - 趋势分析</h1>
|
||||
<p class="stock-code">{{ stockCode }}</p>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<el-button @click="goBack">
|
||||
<el-icon><ArrowLeft /></el-icon>
|
||||
返回
|
||||
</el-button>
|
||||
<el-button type="primary" @click="refreshData" :loading="loading">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
刷新数据
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 趋势概览 -->
|
||||
<el-row :gutter="20" class="trend-overview">
|
||||
<el-col :span="6">
|
||||
<el-card class="trend-card">
|
||||
<div class="trend-info">
|
||||
<div class="trend-icon" :class="getTrendClass()">
|
||||
<el-icon v-if="trendData.trendDirection === 'UP'"><CaretTop /></el-icon>
|
||||
<el-icon v-else-if="trendData.trendDirection === 'DOWN'"><CaretBottom /></el-icon>
|
||||
<el-icon v-else><Minus /></el-icon>
|
||||
</div>
|
||||
<div class="trend-text">
|
||||
<div class="trend-title">{{ getTrendText() }}</div>
|
||||
<div class="trend-subtitle">趋势方向</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="trend-card">
|
||||
<div class="trend-info">
|
||||
<div class="trend-value">{{ trendData.trendStrength?.toFixed(2) || '0.00' }}%</div>
|
||||
<div class="trend-subtitle">趋势强度</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="trend-card">
|
||||
<div class="trend-info">
|
||||
<div class="trend-value">¥{{ trendData.currentPrice?.toFixed(2) || '0.00' }}</div>
|
||||
<div class="trend-subtitle">当前价格</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="trend-card">
|
||||
<div class="trend-info">
|
||||
<div class="trend-value" :class="{ 'positive': trendData.totalChangePercent > 0, 'negative': trendData.totalChangePercent < 0 }">
|
||||
{{ formatChangePercent(trendData.totalChangePercent) }}%
|
||||
</div>
|
||||
<div class="trend-subtitle">总涨跌幅</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 趋势图表 -->
|
||||
<el-row :gutter="20" class="chart-section">
|
||||
<el-col :span="16">
|
||||
<el-card class="chart-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>价格趋势图</span>
|
||||
<div class="chart-controls">
|
||||
<el-select v-model="days" @change="loadTrendData" size="small" style="width: 120px;">
|
||||
<el-option label="7天" :value="7" />
|
||||
<el-option label="15天" :value="15" />
|
||||
<el-option label="30天" :value="30" />
|
||||
<el-option label="60天" :value="60" />
|
||||
<el-option label="90天" :value="90" />
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div id="trend-chart" style="height: 500px;" v-loading="chartLoading"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card class="stats-card">
|
||||
<template #header>
|
||||
<span>统计信息</span>
|
||||
</template>
|
||||
<div class="stats-content">
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">最高价</div>
|
||||
<div class="stat-value">¥{{ trendData.highestPrice?.toFixed(2) || '--' }}</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">最低价</div>
|
||||
<div class="stat-value">¥{{ trendData.lowestPrice?.toFixed(2) || '--' }}</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">平均价</div>
|
||||
<div class="stat-value">¥{{ trendData.averagePrice?.toFixed(2) || '--' }}</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">平均涨跌幅</div>
|
||||
<div class="stat-value" :class="{ 'positive': trendData.avgChangePercent > 0, 'negative': trendData.avgChangePercent < 0 }">
|
||||
{{ formatChangePercent(trendData.avgChangePercent) }}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">总成交量</div>
|
||||
<div class="stat-value">{{ formatVolume(trendData.totalVolume) }}</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">平均成交量</div>
|
||||
<div class="stat-value">{{ formatVolume(trendData.avgVolume) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 成交量图表 -->
|
||||
<el-row class="volume-section">
|
||||
<el-col :span="24">
|
||||
<el-card class="chart-card">
|
||||
<template #header>
|
||||
<span>成交量分析</span>
|
||||
</template>
|
||||
<div id="volume-chart" style="height: 300px;" v-loading="chartLoading"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 价格历史数据表格 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-card class="table-card">
|
||||
<template #header>
|
||||
<span>历史价格数据</span>
|
||||
</template>
|
||||
<el-table :data="trendData.priceHistory || []" style="width: 100%" max-height="400">
|
||||
<el-table-column prop="tradeDate" label="交易日期" width="120">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.tradeDate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="openPrice" label="开盘价" width="100">
|
||||
<template #default="scope">
|
||||
¥{{ scope.row.openPrice?.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="closePrice" label="收盘价" width="100">
|
||||
<template #default="scope">
|
||||
¥{{ scope.row.closePrice?.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="highPrice" label="最高价" width="100">
|
||||
<template #default="scope">
|
||||
¥{{ scope.row.highPrice?.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="lowPrice" label="最低价" width="100">
|
||||
<template #default="scope">
|
||||
¥{{ scope.row.lowPrice?.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="changePercent" label="涨跌幅" width="100">
|
||||
<template #default="scope">
|
||||
<span :class="{ 'positive': scope.row.changePercent > 0, 'negative': scope.row.changePercent < 0 }">
|
||||
{{ formatChangePercent(scope.row.changePercent) }}%
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="volume" label="成交量" width="120">
|
||||
<template #default="scope">
|
||||
{{ formatVolume(scope.row.volume) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { getStockTrend } from '@/api/stock'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
export default {
|
||||
name: 'StockTrendView',
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const stockCode = ref(route.params.stockCode)
|
||||
|
||||
const trendData = ref({})
|
||||
const loading = ref(false)
|
||||
const chartLoading = ref(false)
|
||||
const days = ref(30)
|
||||
|
||||
// 加载趋势数据
|
||||
const loadTrendData = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
chartLoading.value = true
|
||||
const response = await getStockTrend(stockCode.value, days.value)
|
||||
if (response.code === 200 && response.data) {
|
||||
trendData.value = response.data
|
||||
await nextTick()
|
||||
initCharts()
|
||||
} else {
|
||||
ElMessage.error(response.message || '获取趋势数据失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取趋势数据失败:', error)
|
||||
ElMessage.error('获取趋势数据失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
chartLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化图表
|
||||
const initCharts = async () => {
|
||||
await nextTick()
|
||||
initTrendChart()
|
||||
initVolumeChart()
|
||||
}
|
||||
|
||||
// 初始化趋势图表
|
||||
const initTrendChart = () => {
|
||||
const chartDom = document.getElementById('trend-chart')
|
||||
if (!chartDom) return
|
||||
|
||||
const myChart = echarts.init(chartDom)
|
||||
const priceHistory = trendData.value.priceHistory || []
|
||||
|
||||
const dates = priceHistory.map(item => formatDate(item.tradeDate))
|
||||
const closePrices = priceHistory.map(item => item.closePrice)
|
||||
const openPrices = priceHistory.map(item => item.openPrice)
|
||||
const highPrices = priceHistory.map(item => item.highPrice)
|
||||
const lowPrices = priceHistory.map(item => item.lowPrice)
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: `${trendData.value.stockName}(${stockCode.value}) 价格趋势`,
|
||||
left: 'left'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['收盘价', '开盘价', '最高价', '最低价'],
|
||||
top: 30
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
top: '15%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: dates,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
scale: true,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#E6E8EB'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '收盘价',
|
||||
type: 'line',
|
||||
data: closePrices,
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
color: '#409EFF',
|
||||
width: 2
|
||||
},
|
||||
symbol: 'circle',
|
||||
symbolSize: 4
|
||||
},
|
||||
{
|
||||
name: '开盘价',
|
||||
type: 'line',
|
||||
data: openPrices,
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
color: '#67C23A',
|
||||
width: 1
|
||||
},
|
||||
symbol: 'none'
|
||||
},
|
||||
{
|
||||
name: '最高价',
|
||||
type: 'line',
|
||||
data: highPrices,
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
color: '#F56C6C',
|
||||
width: 1,
|
||||
type: 'dashed'
|
||||
},
|
||||
symbol: 'none'
|
||||
},
|
||||
{
|
||||
name: '最低价',
|
||||
type: 'line',
|
||||
data: lowPrices,
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
color: '#E6A23C',
|
||||
width: 1,
|
||||
type: 'dashed'
|
||||
},
|
||||
symbol: 'none'
|
||||
}
|
||||
]
|
||||
}
|
||||
myChart.setOption(option)
|
||||
}
|
||||
|
||||
// 初始化成交量图表
|
||||
const initVolumeChart = () => {
|
||||
const chartDom = document.getElementById('volume-chart')
|
||||
if (!chartDom) return
|
||||
|
||||
const myChart = echarts.init(chartDom)
|
||||
const priceHistory = trendData.value.priceHistory || []
|
||||
|
||||
const dates = priceHistory.map(item => formatDate(item.tradeDate))
|
||||
const volumes = priceHistory.map(item => item.volume)
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '成交量趋势',
|
||||
left: 'left'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: function(params) {
|
||||
return `${params[0].name}<br/>成交量: ${formatVolume(params[0].value)}`
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
top: '15%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: dates,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#8392A5'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#E6E8EB'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '成交量',
|
||||
type: 'bar',
|
||||
data: volumes,
|
||||
itemStyle: {
|
||||
color: '#409EFF'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
myChart.setOption(option)
|
||||
}
|
||||
|
||||
// 刷新数据
|
||||
const refreshData = () => {
|
||||
loadTrendData()
|
||||
}
|
||||
|
||||
// 返回
|
||||
const goBack = () => {
|
||||
router.go(-1)
|
||||
}
|
||||
|
||||
// 获取趋势类名
|
||||
const getTrendClass = () => {
|
||||
const direction = trendData.value.trendDirection
|
||||
if (direction === 'UP') return 'trend-up'
|
||||
if (direction === 'DOWN') return 'trend-down'
|
||||
return 'trend-flat'
|
||||
}
|
||||
|
||||
// 获取趋势文本
|
||||
const getTrendText = () => {
|
||||
const direction = trendData.value.trendDirection
|
||||
if (direction === 'UP') return '上涨趋势'
|
||||
if (direction === 'DOWN') return '下跌趋势'
|
||||
return '震荡趋势'
|
||||
}
|
||||
|
||||
// 格式化函数
|
||||
const formatChangePercent = (value) => {
|
||||
if (!value) return '0.00'
|
||||
return (value > 0 ? '+' : '') + value.toFixed(2)
|
||||
}
|
||||
|
||||
const formatVolume = (volume) => {
|
||||
if (!volume) return '0'
|
||||
if (volume >= 100000000) {
|
||||
return (volume / 100000000).toFixed(2) + '亿'
|
||||
} else if (volume >= 10000) {
|
||||
return (volume / 10000).toFixed(2) + '万'
|
||||
}
|
||||
return volume.toString()
|
||||
}
|
||||
|
||||
const formatDate = (date) => {
|
||||
if (!date) return '--'
|
||||
return new Date(date).toLocaleDateString()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadTrendData()
|
||||
})
|
||||
|
||||
return {
|
||||
stockCode,
|
||||
trendData,
|
||||
loading,
|
||||
chartLoading,
|
||||
days,
|
||||
loadTrendData,
|
||||
refreshData,
|
||||
goBack,
|
||||
getTrendClass,
|
||||
getTrendText,
|
||||
formatChangePercent,
|
||||
formatVolume,
|
||||
formatDate
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.stock-trend-view {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.stock-info {
|
||||
h1 {
|
||||
margin: 0;
|
||||
color: #303133;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.stock-code {
|
||||
margin: 5px 0 0 0;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.trend-overview {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.trend-card {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
.trend-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
|
||||
.trend-icon {
|
||||
font-size: 36px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&.trend-up {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
&.trend-down {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
&.trend-flat {
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
.trend-value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
margin-bottom: 5px;
|
||||
|
||||
&.positive {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
&.negative {
|
||||
color: #67c23a;
|
||||
}
|
||||
}
|
||||
|
||||
.trend-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.trend-subtitle {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chart-section, .volume-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-card, .stats-card, .table-card {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.stats-content {
|
||||
.stat-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
|
||||
&.positive {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
&.negative {
|
||||
color: #67c23a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.positive {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.negative {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
.positive {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.negative {
|
||||
color: #67c23a;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,20 +1,39 @@
|
||||
2025-06-04 20:18:02 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Starting AgriculturalStockPlatformApplication using Java 1.8.0_202 on WIN11 with PID 10364 (D:\VScodeProject\work_4\backend\target\classes started by shenjianZ in D:\VScodeProject\work_4)
|
||||
2025-06-04 20:18:02 [main] INFO c.a.s.AgriculturalStockPlatformApplication - No active profile set, falling back to 1 default profile: "default"
|
||||
2025-06-04 20:18:05 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
|
||||
2025-06-04 20:18:05 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.63]
|
||||
2025-06-04 20:18:05 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
|
||||
2025-06-04 20:18:06 [main] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||
2025-06-04 20:18:06 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 5.6.9.Final
|
||||
2025-06-04 20:18:06 [main] INFO o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
|
||||
2025-06-04 20:18:06 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
||||
2025-06-04 20:18:06 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
||||
2025-06-04 20:18:06 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
|
||||
2025-06-04 20:18:07 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
|
||||
2025-06-04 20:18:08 [main] WARN o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'stockController' defined in file [D:\VScodeProject\work_4\backend\target\classes\com\agricultural\stock\controller\StockController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.agricultural.stock.service.StockService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
|
||||
2025-06-04 20:18:08 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
||||
2025-06-04 20:18:08 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
||||
2025-06-04 20:18:08 [main] INFO o.a.catalina.core.StandardService - Stopping service [Tomcat]
|
||||
2025-06-04 20:18:08 [main] ERROR o.s.b.d.LoggingFailureAnalysisReporter -
|
||||
2025-06-21 15:06:48 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Starting AgriculturalStockPlatformApplication using Java 1.8.0_202 on WIN11 with PID 4824 (D:\VScodeProject\work_4\backend\target\classes started by shenjianZ in D:\VScodeProject\work_4)
|
||||
2025-06-21 15:06:48 [main] INFO c.a.s.AgriculturalStockPlatformApplication - No active profile set, falling back to 1 default profile: "default"
|
||||
2025-06-21 15:06:51 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
|
||||
2025-06-21 15:06:51 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.63]
|
||||
2025-06-21 15:06:52 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
|
||||
2025-06-21 15:06:52 [main] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||
2025-06-21 15:06:52 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 5.6.9.Final
|
||||
2025-06-21 15:06:52 [main] INFO o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
|
||||
2025-06-21 15:06:53 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
||||
2025-06-21 15:06:53 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
||||
2025-06-21 15:06:53 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
|
||||
2025-06-21 15:06:53 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
|
||||
2025-06-21 15:06:54 [main] WARN o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
|
||||
2025-06-21 15:06:55 [main] WARN c.b.m.core.metadata.TableInfoHelper - Can not find table primary key in Class: "com.agricultural.stock.entity.TechnicalIndicator".
|
||||
2025-06-21 15:06:55 [main] WARN c.b.m.c.injector.DefaultSqlInjector - class com.agricultural.stock.entity.TechnicalIndicator ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.
|
||||
2025-06-21 15:06:55 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Started AgriculturalStockPlatformApplication in 7.716 seconds (JVM running for 8.752)
|
||||
2025-06-21 15:07:01 [http-nio-8080-exec-1] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
|
||||
2025-06-21 15:10:41 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
||||
2025-06-21 15:10:41 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
||||
2025-06-21 15:14:09 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Starting AgriculturalStockPlatformApplication using Java 1.8.0_202 on WIN11 with PID 26436 (D:\VScodeProject\work_4\backend\target\classes started by shenjianZ in D:\VScodeProject\work_4)
|
||||
2025-06-21 15:14:09 [main] INFO c.a.s.AgriculturalStockPlatformApplication - No active profile set, falling back to 1 default profile: "default"
|
||||
2025-06-21 15:14:10 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
|
||||
2025-06-21 15:14:10 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.63]
|
||||
2025-06-21 15:14:11 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
|
||||
2025-06-21 15:14:11 [main] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||
2025-06-21 15:14:11 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 5.6.9.Final
|
||||
2025-06-21 15:14:11 [main] INFO o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
|
||||
2025-06-21 15:14:11 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
||||
2025-06-21 15:14:11 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
||||
2025-06-21 15:14:11 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
|
||||
2025-06-21 15:14:12 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
|
||||
2025-06-21 15:14:12 [main] WARN o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration': Unsatisfied dependency expressed through method 'setConfigurers' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'swaggerWebMvcConfigurer' defined in class path resource [org/springdoc/webmvc/ui/SwaggerConfig.class]: Unsatisfied dependency expressed through method 'swaggerWebMvcConfigurer' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springdoc.core.SwaggerUiConfigParameters': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springdoc.core.SwaggerUiConfigProperties' available: expected single matching bean but found 2: swaggerUiConfigProperties,org.springdoc.core.SwaggerUiConfigProperties
|
||||
2025-06-21 15:14:12 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
||||
2025-06-21 15:14:12 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
||||
2025-06-21 15:14:12 [main] INFO o.a.catalina.core.StandardService - Stopping service [Tomcat]
|
||||
2025-06-21 15:14:12 [main] ERROR o.s.b.d.LoggingFailureAnalysisReporter -
|
||||
|
||||
***************************
|
||||
APPLICATION FAILED TO START
|
||||
@@ -22,172 +41,130 @@ APPLICATION FAILED TO START
|
||||
|
||||
Description:
|
||||
|
||||
Parameter 0 of constructor in com.agricultural.stock.controller.StockController required a bean of type 'com.agricultural.stock.service.StockService' that could not be found.
|
||||
Parameter 0 of constructor in org.springdoc.core.SwaggerUiConfigParameters required a single bean, but 2 were found:
|
||||
- swaggerUiConfigProperties: defined by method 'swaggerUiConfigProperties' in class path resource [com/agricultural/stock/config/OpenApiConfig.class]
|
||||
- org.springdoc.core.SwaggerUiConfigProperties: defined in null
|
||||
|
||||
|
||||
Action:
|
||||
|
||||
Consider defining a bean of type 'com.agricultural.stock.service.StockService' in your configuration.
|
||||
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
|
||||
|
||||
2025-06-04 20:29:56 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Starting AgriculturalStockPlatformApplication using Java 1.8.0_202 on WIN11 with PID 22200 (D:\VScodeProject\work_4\backend\target\classes started by shenjianZ in D:\VScodeProject\work_4)
|
||||
2025-06-04 20:29:56 [main] INFO c.a.s.AgriculturalStockPlatformApplication - No active profile set, falling back to 1 default profile: "default"
|
||||
2025-06-04 20:29:58 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
|
||||
2025-06-04 20:29:58 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.63]
|
||||
2025-06-04 20:29:59 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
|
||||
2025-06-04 20:29:59 [main] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||
2025-06-04 20:29:59 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 5.6.9.Final
|
||||
2025-06-04 20:29:59 [main] INFO o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
|
||||
2025-06-04 20:29:59 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
||||
2025-06-04 20:29:59 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
||||
2025-06-04 20:29:59 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
|
||||
2025-06-04 20:30:00 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
|
||||
2025-06-04 20:30:01 [main] WARN o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
|
||||
2025-06-04 20:30:02 [main] WARN c.b.m.core.metadata.TableInfoHelper - Can not find table primary key in Class: "com.agricultural.stock.entity.TechnicalIndicator".
|
||||
2025-06-04 20:30:02 [main] WARN c.b.m.c.injector.DefaultSqlInjector - class com.agricultural.stock.entity.TechnicalIndicator ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.
|
||||
2025-06-04 20:30:02 [main] WARN o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
|
||||
2025-06-04 20:30:02 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
||||
2025-06-04 20:30:02 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
||||
2025-06-04 20:30:02 [main] INFO o.a.catalina.core.StandardService - Stopping service [Tomcat]
|
||||
2025-06-04 20:30:02 [main] ERROR o.s.boot.SpringApplication - Application run failed
|
||||
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
|
||||
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181)
|
||||
at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54)
|
||||
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356)
|
||||
at java.lang.Iterable.forEach(Iterable.java:75)
|
||||
at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155)
|
||||
at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123)
|
||||
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935)
|
||||
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586)
|
||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147)
|
||||
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
|
||||
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
|
||||
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
|
||||
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
|
||||
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)
|
||||
at com.agricultural.stock.AgriculturalStockPlatformApplication.main(AgriculturalStockPlatformApplication.java:27)
|
||||
Caused by: java.lang.NullPointerException: null
|
||||
at springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns(WebMvcPatternsRequestConditionWrapper.java:56)
|
||||
at springfox.documentation.RequestHandler.sortedPaths(RequestHandler.java:113)
|
||||
at springfox.documentation.spi.service.contexts.Orderings.lambda$byPatternsCondition$3(Orderings.java:89)
|
||||
at java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469)
|
||||
at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
|
||||
at java.util.TimSort.sort(TimSort.java:220)
|
||||
at java.util.Arrays.sort(Arrays.java:1512)
|
||||
at java.util.ArrayList.sort(ArrayList.java:1462)
|
||||
at java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:387)
|
||||
at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
|
||||
at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
|
||||
at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
|
||||
at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
|
||||
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
|
||||
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
|
||||
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
|
||||
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
|
||||
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
|
||||
at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:81)
|
||||
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
|
||||
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
|
||||
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
|
||||
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
|
||||
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
|
||||
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
|
||||
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
|
||||
at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.withDefaults(AbstractDocumentationPluginsBootstrapper.java:107)
|
||||
at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.buildContext(AbstractDocumentationPluginsBootstrapper.java:91)
|
||||
at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.bootstrapDocumentationPlugins(AbstractDocumentationPluginsBootstrapper.java:82)
|
||||
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:100)
|
||||
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178)
|
||||
... 14 common frames omitted
|
||||
2025-06-04 20:30:56 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Starting AgriculturalStockPlatformApplication using Java 1.8.0_202 on WIN11 with PID 19328 (D:\VScodeProject\work_4\backend\target\classes started by shenjianZ in D:\VScodeProject\work_4)
|
||||
2025-06-04 20:30:56 [main] INFO c.a.s.AgriculturalStockPlatformApplication - No active profile set, falling back to 1 default profile: "default"
|
||||
2025-06-04 20:30:59 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
|
||||
2025-06-04 20:30:59 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.63]
|
||||
2025-06-04 20:30:59 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
|
||||
2025-06-04 20:31:00 [main] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||
2025-06-04 20:31:00 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 5.6.9.Final
|
||||
2025-06-04 20:31:00 [main] INFO o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
|
||||
2025-06-04 20:31:00 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
||||
2025-06-04 20:31:00 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
||||
2025-06-04 20:31:01 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
|
||||
2025-06-04 20:31:01 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
|
||||
2025-06-04 20:31:01 [main] WARN o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'marketAnalysisController' defined in file [D:\VScodeProject\work_4\backend\target\classes\com\agricultural\stock\controller\MarketAnalysisController.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.agricultural.stock.controller.MarketAnalysisController]: Constructor threw exception; nested exception is java.lang.Error: Unresolved compilation problems:
|
||||
Api cannot be resolved to a type
|
||||
ApiOperation cannot be resolved to a type
|
||||
ApiOperation cannot be resolved to a type
|
||||
ApiOperation cannot be resolved to a type
|
||||
|
||||
2025-06-04 20:31:01 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
||||
2025-06-04 20:31:01 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
||||
2025-06-04 20:31:01 [main] INFO o.a.catalina.core.StandardService - Stopping service [Tomcat]
|
||||
2025-06-04 20:31:01 [main] ERROR o.s.boot.SpringApplication - Application run failed
|
||||
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'marketAnalysisController' defined in file [D:\VScodeProject\work_4\backend\target\classes\com\agricultural\stock\controller\MarketAnalysisController.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.agricultural.stock.controller.MarketAnalysisController]: Constructor threw exception; nested exception is java.lang.Error: Unresolved compilation problems:
|
||||
Api cannot be resolved to a type
|
||||
ApiOperation cannot be resolved to a type
|
||||
ApiOperation cannot be resolved to a type
|
||||
ApiOperation cannot be resolved to a type
|
||||
|
||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1334)
|
||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
|
||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
|
||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
|
||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
|
||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
|
||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
|
||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:953)
|
||||
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
|
||||
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
|
||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147)
|
||||
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
|
||||
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
|
||||
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
|
||||
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
|
||||
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)
|
||||
at com.agricultural.stock.AgriculturalStockPlatformApplication.main(AgriculturalStockPlatformApplication.java:27)
|
||||
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.agricultural.stock.controller.MarketAnalysisController]: Constructor threw exception; nested exception is java.lang.Error: Unresolved compilation problems:
|
||||
Api cannot be resolved to a type
|
||||
ApiOperation cannot be resolved to a type
|
||||
ApiOperation cannot be resolved to a type
|
||||
ApiOperation cannot be resolved to a type
|
||||
|
||||
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:224)
|
||||
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87)
|
||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1326)
|
||||
... 17 common frames omitted
|
||||
Caused by: java.lang.Error: Unresolved compilation problems:
|
||||
Api cannot be resolved to a type
|
||||
ApiOperation cannot be resolved to a type
|
||||
ApiOperation cannot be resolved to a type
|
||||
ApiOperation cannot be resolved to a type
|
||||
|
||||
at com.agricultural.stock.controller.MarketAnalysisController.<init>(MarketAnalysisController.java:22)
|
||||
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
|
||||
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
|
||||
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
|
||||
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
|
||||
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:211)
|
||||
... 19 common frames omitted
|
||||
2025-06-04 20:33:00 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Starting AgriculturalStockPlatformApplication using Java 1.8.0_202 on WIN11 with PID 11952 (D:\VScodeProject\work_4\backend\target\classes started by shenjianZ in D:\VScodeProject\work_4)
|
||||
2025-06-04 20:33:00 [main] INFO c.a.s.AgriculturalStockPlatformApplication - No active profile set, falling back to 1 default profile: "default"
|
||||
2025-06-04 20:33:01 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
|
||||
2025-06-04 20:33:01 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.63]
|
||||
2025-06-04 20:33:02 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
|
||||
2025-06-04 20:33:02 [main] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||
2025-06-04 20:33:02 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 5.6.9.Final
|
||||
2025-06-04 20:33:02 [main] INFO o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
|
||||
2025-06-04 20:33:02 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
||||
2025-06-04 20:33:02 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
||||
2025-06-04 20:33:02 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
|
||||
2025-06-04 20:33:03 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
|
||||
2025-06-04 20:33:03 [main] WARN o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
|
||||
2025-06-04 20:33:04 [main] WARN c.b.m.core.metadata.TableInfoHelper - Can not find table primary key in Class: "com.agricultural.stock.entity.TechnicalIndicator".
|
||||
2025-06-04 20:33:04 [main] WARN c.b.m.c.injector.DefaultSqlInjector - class com.agricultural.stock.entity.TechnicalIndicator ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.
|
||||
2025-06-04 20:33:04 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Started AgriculturalStockPlatformApplication in 5.049 seconds (JVM running for 5.764)
|
||||
2025-06-04 20:33:11 [http-nio-8080-exec-1] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
|
||||
2025-06-04 20:35:00 [http-nio-8080-exec-7] INFO o.s.api.AbstractOpenApiResource - Init duration for springdoc-openapi is: 534 ms
|
||||
2025-06-04 21:09:37 [http-nio-8080-exec-10] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300630, 预测天数: 7
|
||||
2025-06-04 21:09:52 [http-nio-8080-exec-9] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300630, 预测天数: 7
|
||||
2025-06-04 21:09:56 [http-nio-8080-exec-10] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300630, 预测天数: 7
|
||||
2025-06-04 21:10:00 [http-nio-8080-exec-9] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300630, 预测天数: 7
|
||||
2025-06-04 21:13:44 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
||||
2025-06-04 21:13:44 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
||||
2025-06-21 15:15:02 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Starting AgriculturalStockPlatformApplication using Java 1.8.0_202 on WIN11 with PID 10800 (D:\VScodeProject\work_4\backend\target\classes started by shenjianZ in D:\VScodeProject\work_4)
|
||||
2025-06-21 15:15:02 [main] INFO c.a.s.AgriculturalStockPlatformApplication - No active profile set, falling back to 1 default profile: "default"
|
||||
2025-06-21 15:15:04 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
|
||||
2025-06-21 15:15:04 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.63]
|
||||
2025-06-21 15:15:04 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
|
||||
2025-06-21 15:15:04 [main] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||
2025-06-21 15:15:04 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 5.6.9.Final
|
||||
2025-06-21 15:15:05 [main] INFO o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
|
||||
2025-06-21 15:15:05 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
||||
2025-06-21 15:15:05 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
||||
2025-06-21 15:15:05 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
|
||||
2025-06-21 15:15:06 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
|
||||
2025-06-21 15:15:06 [main] WARN o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
|
||||
2025-06-21 15:15:07 [main] WARN c.b.m.core.metadata.TableInfoHelper - Can not find table primary key in Class: "com.agricultural.stock.entity.TechnicalIndicator".
|
||||
2025-06-21 15:15:07 [main] WARN c.b.m.c.injector.DefaultSqlInjector - class com.agricultural.stock.entity.TechnicalIndicator ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.
|
||||
2025-06-21 15:15:08 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Started AgriculturalStockPlatformApplication in 5.923 seconds (JVM running for 7.223)
|
||||
2025-06-21 15:15:11 [http-nio-8080-exec-2] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
|
||||
2025-06-21 15:15:12 [http-nio-8080-exec-8] INFO o.s.api.AbstractOpenApiResource - Init duration for springdoc-openapi is: 522 ms
|
||||
2025-06-21 15:21:55 [http-nio-8080-exec-1] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sh600371, 预测天数: 7
|
||||
2025-06-21 15:40:09 [http-nio-8080-exec-9] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 15:40:20 [http-nio-8080-exec-1] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 15:40:27 [http-nio-8080-exec-9] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 15:40:49 [http-nio-8080-exec-1] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sh603336, 预测天数: 7
|
||||
2025-06-21 15:42:33 [http-nio-8080-exec-4] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300505, 预测天数: 7
|
||||
2025-06-21 15:46:12 [http-nio-8080-exec-6] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 15:46:18 [http-nio-8080-exec-1] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 15:46:47 [http-nio-8080-exec-2] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz301035, 预测天数: 7
|
||||
2025-06-21 16:06:00 [http-nio-8080-exec-2] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:07:10 [http-nio-8080-exec-7] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:07:18 [http-nio-8080-exec-1] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:07:46 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
||||
2025-06-21 16:07:46 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
||||
2025-06-21 16:07:58 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Starting AgriculturalStockPlatformApplication using Java 1.8.0_202 on WIN11 with PID 28420 (D:\VScodeProject\work_4\backend\target\classes started by shenjianZ in D:\VScodeProject\work_4)
|
||||
2025-06-21 16:07:58 [main] INFO c.a.s.AgriculturalStockPlatformApplication - No active profile set, falling back to 1 default profile: "default"
|
||||
2025-06-21 16:08:00 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
|
||||
2025-06-21 16:08:00 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.63]
|
||||
2025-06-21 16:08:00 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
|
||||
2025-06-21 16:08:00 [main] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||
2025-06-21 16:08:00 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 5.6.9.Final
|
||||
2025-06-21 16:08:01 [main] INFO o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
|
||||
2025-06-21 16:08:01 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
||||
2025-06-21 16:08:01 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
||||
2025-06-21 16:08:01 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
|
||||
2025-06-21 16:08:01 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
|
||||
2025-06-21 16:08:02 [main] WARN o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
|
||||
2025-06-21 16:08:03 [main] WARN c.b.m.core.metadata.TableInfoHelper - Can not find table primary key in Class: "com.agricultural.stock.entity.TechnicalIndicator".
|
||||
2025-06-21 16:08:03 [main] WARN c.b.m.c.injector.DefaultSqlInjector - class com.agricultural.stock.entity.TechnicalIndicator ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.
|
||||
2025-06-21 16:08:03 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Started AgriculturalStockPlatformApplication in 5.411 seconds (JVM running for 6.284)
|
||||
2025-06-21 16:08:10 [http-nio-8080-exec-6] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
|
||||
2025-06-21 16:08:13 [http-nio-8080-exec-10] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:08:17 [http-nio-8080-exec-7] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:08:23 [http-nio-8080-exec-8] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:08:46 [http-nio-8080-exec-6] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:09:31 [http-nio-8080-exec-8] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz000713, 预测天数: 7
|
||||
2025-06-21 16:15:43 [http-nio-8080-exec-1] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:15:57 [http-nio-8080-exec-10] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:19:16 [http-nio-8080-exec-5] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:19:37 [http-nio-8080-exec-10] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:19:44 [http-nio-8080-exec-5] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:19:48 [http-nio-8080-exec-4] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:19:58 [http-nio-8080-exec-8] INFO c.a.s.service.impl.StockServiceImpl - 股票预测功能暂未实现,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:24:16 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
||||
2025-06-21 16:24:16 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
||||
2025-06-21 16:24:23 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Starting AgriculturalStockPlatformApplication using Java 1.8.0_202 on WIN11 with PID 15804 (D:\VScodeProject\work_4\backend\target\classes started by shenjianZ in D:\VScodeProject\work_4)
|
||||
2025-06-21 16:24:23 [main] INFO c.a.s.AgriculturalStockPlatformApplication - No active profile set, falling back to 1 default profile: "default"
|
||||
2025-06-21 16:24:24 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
|
||||
2025-06-21 16:24:24 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.63]
|
||||
2025-06-21 16:24:25 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
|
||||
2025-06-21 16:24:25 [main] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||
2025-06-21 16:24:25 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 5.6.9.Final
|
||||
2025-06-21 16:24:25 [main] INFO o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
|
||||
2025-06-21 16:24:25 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
||||
2025-06-21 16:24:25 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
||||
2025-06-21 16:24:25 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
|
||||
2025-06-21 16:24:26 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
|
||||
2025-06-21 16:24:27 [main] WARN o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
|
||||
2025-06-21 16:24:28 [main] WARN c.b.m.core.metadata.TableInfoHelper - Can not find table primary key in Class: "com.agricultural.stock.entity.TechnicalIndicator".
|
||||
2025-06-21 16:24:28 [main] WARN c.b.m.c.injector.DefaultSqlInjector - class com.agricultural.stock.entity.TechnicalIndicator ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.
|
||||
2025-06-21 16:24:28 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Started AgriculturalStockPlatformApplication in 5.62 seconds (JVM running for 6.522)
|
||||
2025-06-21 16:24:32 [http-nio-8080-exec-1] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
|
||||
2025-06-21 16:24:32 [http-nio-8080-exec-1] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:24:44 [http-nio-8080-exec-5] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:24:48 [http-nio-8080-exec-8] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:26:47 [http-nio-8080-exec-6] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:27:14 [http-nio-8080-exec-10] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:27:49 [http-nio-8080-exec-4] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:28:27 [http-nio-8080-exec-9] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:28:49 [http-nio-8080-exec-2] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:30:39 [http-nio-8080-exec-7] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:31:25 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
||||
2025-06-21 16:31:25 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
||||
2025-06-21 16:31:32 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Starting AgriculturalStockPlatformApplication using Java 1.8.0_202 on WIN11 with PID 10584 (D:\VScodeProject\work_4\backend\target\classes started by shenjianZ in D:\VScodeProject\work_4)
|
||||
2025-06-21 16:31:32 [main] INFO c.a.s.AgriculturalStockPlatformApplication - No active profile set, falling back to 1 default profile: "default"
|
||||
2025-06-21 16:31:33 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
|
||||
2025-06-21 16:31:33 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.63]
|
||||
2025-06-21 16:31:34 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
|
||||
2025-06-21 16:31:34 [main] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||
2025-06-21 16:31:34 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 5.6.9.Final
|
||||
2025-06-21 16:31:34 [main] INFO o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
|
||||
2025-06-21 16:31:34 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
|
||||
2025-06-21 16:31:34 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
|
||||
2025-06-21 16:31:34 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
|
||||
2025-06-21 16:31:35 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
|
||||
2025-06-21 16:31:36 [main] WARN o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
|
||||
2025-06-21 16:31:37 [main] WARN c.b.m.core.metadata.TableInfoHelper - Can not find table primary key in Class: "com.agricultural.stock.entity.TechnicalIndicator".
|
||||
2025-06-21 16:31:37 [main] WARN c.b.m.c.injector.DefaultSqlInjector - class com.agricultural.stock.entity.TechnicalIndicator ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.
|
||||
2025-06-21 16:31:37 [main] INFO c.a.s.AgriculturalStockPlatformApplication - Started AgriculturalStockPlatformApplication in 5.772 seconds (JVM running for 6.518)
|
||||
2025-06-21 16:31:49 [http-nio-8080-exec-1] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
|
||||
2025-06-21 16:31:50 [http-nio-8080-exec-4] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:32:05 [http-nio-8080-exec-2] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz300189, 预测天数: 7
|
||||
2025-06-21 16:32:10 [http-nio-8080-exec-6] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz000713, 预测天数: 7
|
||||
2025-06-21 16:32:27 [http-nio-8080-exec-2] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz200505, 预测天数: 7
|
||||
2025-06-21 16:33:16 [http-nio-8080-exec-5] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz002539, 预测天数: 7
|
||||
2025-06-21 16:33:24 [http-nio-8080-exec-1] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz300505, 预测天数: 7
|
||||
2025-06-21 16:33:32 [http-nio-8080-exec-7] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sz300505, 预测天数: 7
|
||||
2025-06-21 16:33:41 [http-nio-8080-exec-4] INFO c.a.s.service.impl.StockServiceImpl - 生成股票预测数据,股票代码: sh000912, 预测天数: 7
|
||||
2025-06-21 16:37:10 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
|
||||
2025-06-21 16:37:10 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
|
||||
|
||||
Reference in New Issue
Block a user