first commit
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
package com.agricultural.stock;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
// Kafka导入已移除
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
/**
|
||||
* 农业领域上市公司行情可视化监控平台启动类
|
||||
*
|
||||
* @author Agricultural Stock Platform Team
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.agricultural.stock.mapper")
|
||||
@EnableCaching
|
||||
// @EnableKafka 已移除
|
||||
@EnableAsync
|
||||
@EnableScheduling
|
||||
@EnableTransactionManagement
|
||||
public class AgriculturalStockPlatformApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AgriculturalStockPlatformApplication.class, args);
|
||||
System.out.println("==========================================");
|
||||
System.out.println("农业上市公司行情监控平台启动成功!");
|
||||
System.out.println("Swagger UI: http://localhost:8080/swagger-ui/index.html ");
|
||||
System.out.println("==========================================");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.agricultural.stock.config;
|
||||
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
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 java.util.List;
|
||||
|
||||
/**
|
||||
* OpenAPI 配置类
|
||||
*/
|
||||
@Configuration
|
||||
public class OpenApiConfig {
|
||||
|
||||
@Bean
|
||||
public OpenAPI customOpenAPI() {
|
||||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("农业股票数据分析系统 API")
|
||||
.description("基于Spark大数据处理的农业股票市场监控与分析平台API文档\n\n" +
|
||||
"## 系统功能\n" +
|
||||
"- 🚀 **实时股票数据获取** - 获取最新的农业股票行情数据\n" +
|
||||
"- 📊 **市场分析** - 涨跌统计、市场总览、趋势分析\n" +
|
||||
"- 🔍 **股票搜索** - 根据代码或名称搜索股票\n" +
|
||||
"- 📈 **技术指标** - MA、RSI、MACD、布林带等技术分析\n" +
|
||||
"- 🏆 **排行榜** - 涨幅榜、跌幅榜、成交量榜\n" +
|
||||
"- 🔮 **趋势预测** - 基于历史数据的股票走势分析\n\n" +
|
||||
"## 访问地址\n" +
|
||||
"- **Swagger UI**: http://localhost:8080/swagger-ui/index.html\n" +
|
||||
"- **API Docs**: http://localhost:8080/v3/api-docs")
|
||||
.version("v1.0.0")
|
||||
.contact(new Contact()
|
||||
.name("农业股票分析团队")
|
||||
.email("support@agricultural-stock.com")
|
||||
.url("https://github.com/agricultural-stock-platform"))
|
||||
.license(new License()
|
||||
.name("MIT License")
|
||||
.url("https://opensource.org/licenses/MIT")))
|
||||
.servers(List.of(
|
||||
new Server()
|
||||
.url("http://localhost:8080")
|
||||
.description("本地开发环境"),
|
||||
new Server()
|
||||
.url("https://api.agricultural-stock.com")
|
||||
.description("生产环境")
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.agricultural.stock.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Web配置类
|
||||
*/
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
// 配置Swagger UI资源映射
|
||||
registry.addResourceHandler("/swagger-ui/**")
|
||||
.addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/")
|
||||
.resourceChain(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
// 重定向根路径到Swagger UI
|
||||
registry.addViewController("/").setViewName("redirect:/swagger-ui/index.html");
|
||||
registry.addViewController("/swagger-ui").setViewName("redirect:/swagger-ui/index.html");
|
||||
registry.addViewController("/api").setViewName("redirect:/swagger-ui/index.html");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.agricultural.stock.controller;
|
||||
|
||||
import com.agricultural.stock.entity.MarketAnalysis;
|
||||
import com.agricultural.stock.service.MarketAnalysisService;
|
||||
import com.agricultural.stock.vo.Result;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 市场分析数据API控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/market")
|
||||
@Tag(name = "市场分析数据API")
|
||||
@CrossOrigin(origins = "*")
|
||||
public class MarketAnalysisController {
|
||||
|
||||
@Autowired
|
||||
private MarketAnalysisService marketAnalysisService;
|
||||
|
||||
/**
|
||||
* 获取最新的市场分析数据
|
||||
*/
|
||||
@GetMapping("/latest")
|
||||
@Operation(summary = "获取最新的市场分析数据")
|
||||
public Result<MarketAnalysis> getLatestMarketAnalysis() {
|
||||
try {
|
||||
MarketAnalysis marketAnalysis = marketAnalysisService.getLatestMarketAnalysis();
|
||||
if (marketAnalysis != null) {
|
||||
return Result.success(marketAnalysis);
|
||||
} else {
|
||||
return Result.error("暂无市场分析数据");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取最新市场分析数据失败", e);
|
||||
return Result.error("获取数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期范围的市场分析数据
|
||||
*/
|
||||
@GetMapping("/range")
|
||||
@Operation(summary = "获取指定日期范围的市场分析数据")
|
||||
public Result<List<MarketAnalysis>> getMarketAnalysisByDateRange(
|
||||
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
||||
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
|
||||
try {
|
||||
List<MarketAnalysis> dataList = marketAnalysisService.getMarketAnalysisByDateRange(startDate, endDate);
|
||||
return Result.success(dataList);
|
||||
} catch (Exception e) {
|
||||
log.error("获取指定日期范围的市场分析数据失败", e);
|
||||
return Result.error("获取数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近N天的市场分析数据
|
||||
*/
|
||||
@GetMapping("/recent/{days}")
|
||||
@Operation(summary = "获取最近N天的市场分析数据")
|
||||
public Result<List<MarketAnalysis>> getRecentMarketAnalysis(@PathVariable int days) {
|
||||
try {
|
||||
if (days <= 0 || days > 365) {
|
||||
return Result.error("天数参数无效,应在1-365之间");
|
||||
}
|
||||
List<MarketAnalysis> dataList = marketAnalysisService.getRecentMarketAnalysis(days);
|
||||
return Result.success(dataList);
|
||||
} catch (Exception e) {
|
||||
log.error("获取最近{}天的市场分析数据失败", days, e);
|
||||
return Result.error("获取数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package com.agricultural.stock.controller;
|
||||
|
||||
import com.agricultural.stock.entity.StockData;
|
||||
import com.agricultural.stock.service.StockService;
|
||||
import com.agricultural.stock.vo.Result;
|
||||
import com.agricultural.stock.vo.StockAnalysisVO;
|
||||
import com.agricultural.stock.vo.StockTrendVO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 股票数据API控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/stock")
|
||||
@Tag(name = "股票数据API")
|
||||
@CrossOrigin(origins = "*")
|
||||
public class StockController {
|
||||
|
||||
@Autowired
|
||||
private StockService stockService;
|
||||
|
||||
/**
|
||||
* 获取实时股票数据
|
||||
*/
|
||||
@GetMapping("/realtime")
|
||||
@Operation(summary = "获取实时股票数据")
|
||||
public Result<List<StockData>> getRealtimeStockData() {
|
||||
try {
|
||||
List<StockData> stockDataList = stockService.getRealtimeStockData();
|
||||
return Result.success(stockDataList);
|
||||
} catch (Exception e) {
|
||||
log.error("获取实时股票数据失败", e);
|
||||
return Result.error("获取实时股票数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据股票代码获取历史数据
|
||||
*/
|
||||
@GetMapping("/history/{stockCode}")
|
||||
@Operation(summary = "获取股票历史数据")
|
||||
public Result<List<StockData>> getHistoryData(
|
||||
@Parameter(description = "股票代码") @PathVariable String stockCode,
|
||||
@Parameter(description = "开始日期") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDate,
|
||||
@Parameter(description = "结束日期") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDate) {
|
||||
try {
|
||||
List<StockData> historyData = stockService.getHistoryData(stockCode, startDate, endDate);
|
||||
return Result.success(historyData);
|
||||
} catch (Exception e) {
|
||||
log.error("获取股票{}历史数据失败", stockCode, e);
|
||||
return Result.error("获取历史数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取涨幅排行榜
|
||||
*/
|
||||
@GetMapping("/ranking/growth")
|
||||
@Operation(summary = "获取涨幅排行榜")
|
||||
public Result<List<StockData>> getGrowthRanking(
|
||||
@Parameter(description = "排行数量") @RequestParam(defaultValue = "10") Integer limit) {
|
||||
try {
|
||||
List<StockData> rankingList = stockService.getGrowthRanking(limit);
|
||||
return Result.success(rankingList);
|
||||
} catch (Exception e) {
|
||||
log.error("获取涨幅排行榜失败", e);
|
||||
return Result.error("获取涨幅排行榜失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取市值排行榜
|
||||
*/
|
||||
@GetMapping("/ranking/market-cap")
|
||||
@Operation(summary = "获取市值排行榜")
|
||||
public Result<List<StockData>> getMarketCapRanking(
|
||||
@Parameter(description = "排行数量") @RequestParam(defaultValue = "10") Integer limit) {
|
||||
try {
|
||||
List<StockData> rankingList = stockService.getMarketCapRanking(limit);
|
||||
return Result.success(rankingList);
|
||||
} catch (Exception e) {
|
||||
log.error("获取市值排行榜失败", e);
|
||||
return Result.error("获取市值排行榜失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成交量排行榜
|
||||
*/
|
||||
@GetMapping("/ranking/volume")
|
||||
@Operation(summary = "获取成交量排行榜")
|
||||
public Result<List<StockData>> getVolumeRanking(
|
||||
@Parameter(description = "排行数量") @RequestParam(defaultValue = "10") Integer limit) {
|
||||
try {
|
||||
List<StockData> rankingList = stockService.getVolumeRanking(limit);
|
||||
return Result.success(rankingList);
|
||||
} catch (Exception e) {
|
||||
log.error("获取成交量排行榜失败", e);
|
||||
return Result.error("获取成交量排行榜失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取股票趋势分析
|
||||
*/
|
||||
@GetMapping("/trend/{stockCode}")
|
||||
@Operation(summary = "获取股票趋势分析")
|
||||
public Result<StockTrendVO> getStockTrend(
|
||||
@Parameter(description = "股票代码") @PathVariable String stockCode,
|
||||
@Parameter(description = "分析天数") @RequestParam(defaultValue = "30") Integer days) {
|
||||
try {
|
||||
StockTrendVO trendVO = stockService.getStockTrend(stockCode, days);
|
||||
if (trendVO != null) {
|
||||
return Result.success(trendVO);
|
||||
} else {
|
||||
return Result.error("未找到该股票的趋势数据");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取股票{}趋势分析失败", stockCode, e);
|
||||
return Result.error("获取趋势分析失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取市场综合分析
|
||||
*/
|
||||
@GetMapping("/market-analysis")
|
||||
@Operation(summary = "获取市场综合分析")
|
||||
public Result<StockAnalysisVO> getMarketAnalysis() {
|
||||
try {
|
||||
StockAnalysisVO analysisVO = stockService.getMarketAnalysis();
|
||||
if (analysisVO != null) {
|
||||
return Result.success(analysisVO);
|
||||
} else {
|
||||
return Result.error("暂无市场分析数据");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取市场分析失败", e);
|
||||
return Result.error("获取市场分析失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取股票预测数据
|
||||
*/
|
||||
@GetMapping("/prediction/{stockCode}")
|
||||
@Operation(summary = "获取股票预测数据")
|
||||
public Result<List<StockData>> getStockPrediction(
|
||||
@Parameter(description = "股票代码") @PathVariable String stockCode,
|
||||
@Parameter(description = "预测天数") @RequestParam(defaultValue = "7") Integer days) {
|
||||
try {
|
||||
List<StockData> predictionList = stockService.getStockPrediction(stockCode, days);
|
||||
return Result.success(predictionList);
|
||||
} catch (Exception e) {
|
||||
log.error("获取股票{}预测数据失败", stockCode, e);
|
||||
return Result.error("获取预测数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索股票
|
||||
*/
|
||||
@GetMapping("/search")
|
||||
@Operation(summary = "搜索股票")
|
||||
public Result<List<StockData>> searchStocks(
|
||||
@Parameter(description = "搜索关键词") @RequestParam String keyword) {
|
||||
try {
|
||||
if (keyword == null || keyword.trim().isEmpty()) {
|
||||
return Result.error("搜索关键词不能为空");
|
||||
}
|
||||
List<StockData> searchResults = stockService.searchStocks(keyword.trim());
|
||||
return Result.success(searchResults);
|
||||
} catch (Exception e) {
|
||||
log.error("搜索股票失败,关键词: {}", keyword, e);
|
||||
return Result.error("搜索失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存股票数据
|
||||
*/
|
||||
@PostMapping("/save")
|
||||
@Operation(summary = "保存股票数据")
|
||||
public Result<StockData> saveStockData(@RequestBody StockData stockData) {
|
||||
try {
|
||||
StockData savedData = stockService.saveStockData(stockData);
|
||||
if (savedData != null) {
|
||||
return Result.success(savedData);
|
||||
} else {
|
||||
return Result.error("保存股票数据失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("保存股票数据失败", e);
|
||||
return Result.error("保存股票数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存股票数据
|
||||
*/
|
||||
@PostMapping("/batch-save")
|
||||
@Operation(summary = "批量保存股票数据")
|
||||
public Result<Integer> batchSaveStockData(@RequestBody List<StockData> stockDataList) {
|
||||
try {
|
||||
if (stockDataList == null || stockDataList.isEmpty()) {
|
||||
return Result.error("股票数据列表不能为空");
|
||||
}
|
||||
Integer count = stockService.batchSaveStockData(stockDataList);
|
||||
return Result.success(count);
|
||||
} catch (Exception e) {
|
||||
log.error("批量保存股票数据失败", e);
|
||||
return Result.error("批量保存失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.agricultural.stock.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 行业分析实体类
|
||||
*/
|
||||
@Data
|
||||
@TableName("industry_analysis")
|
||||
public class IndustryAnalysis {
|
||||
|
||||
/**
|
||||
* 行业名称
|
||||
*/
|
||||
private String industry;
|
||||
|
||||
/**
|
||||
* 股票数量
|
||||
*/
|
||||
private Long stockCount;
|
||||
|
||||
/**
|
||||
* 平均涨跌幅
|
||||
*/
|
||||
private Double avgChangePercent;
|
||||
|
||||
/**
|
||||
* 总市值
|
||||
*/
|
||||
private Double totalMarketCap;
|
||||
|
||||
/**
|
||||
* 总成交量
|
||||
*/
|
||||
private Double totalVolume;
|
||||
|
||||
/**
|
||||
* 分析日期
|
||||
*/
|
||||
private LocalDate analysisDate;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.agricultural.stock.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 市场分析实体类
|
||||
*/
|
||||
@Data
|
||||
@TableName("market_analysis")
|
||||
public class MarketAnalysis {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 分析日期
|
||||
*/
|
||||
private LocalDate analysisDate;
|
||||
|
||||
/**
|
||||
* 上涨股票数
|
||||
*/
|
||||
private Integer upCount;
|
||||
|
||||
/**
|
||||
* 下跌股票数
|
||||
*/
|
||||
private Integer downCount;
|
||||
|
||||
/**
|
||||
* 平盘股票数
|
||||
*/
|
||||
private Integer flatCount;
|
||||
|
||||
/**
|
||||
* 总股票数
|
||||
*/
|
||||
private Integer totalCount;
|
||||
|
||||
/**
|
||||
* 总市值
|
||||
*/
|
||||
private BigDecimal totalMarketCap;
|
||||
|
||||
/**
|
||||
* 总成交量
|
||||
*/
|
||||
private Long totalVolume;
|
||||
|
||||
/**
|
||||
* 总成交额
|
||||
*/
|
||||
private BigDecimal totalTurnover;
|
||||
|
||||
/**
|
||||
* 平均涨跌幅
|
||||
*/
|
||||
private BigDecimal avgChangePercent;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.agricultural.stock.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 市场趋势实体类
|
||||
*/
|
||||
@Data
|
||||
@TableName("market_trends")
|
||||
public class MarketTrends {
|
||||
|
||||
/**
|
||||
* 交易日期
|
||||
*/
|
||||
private LocalDateTime tradeDate;
|
||||
|
||||
/**
|
||||
* 平均价格
|
||||
*/
|
||||
private Double avgPrice;
|
||||
|
||||
/**
|
||||
* 平均涨跌幅
|
||||
*/
|
||||
private Double avgChangePercent;
|
||||
|
||||
/**
|
||||
* 总成交量
|
||||
*/
|
||||
private Double totalVolume;
|
||||
|
||||
/**
|
||||
* 总成交额
|
||||
*/
|
||||
private Double totalTurnover;
|
||||
|
||||
/**
|
||||
* 股票数量
|
||||
*/
|
||||
private Long stockCount;
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package com.agricultural.stock.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 股票数据实体类
|
||||
*
|
||||
* @author Agricultural Stock Platform Team
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Entity
|
||||
@Table(name = "stock_data")
|
||||
@TableName("stock_data")
|
||||
public class StockData {
|
||||
|
||||
@Id
|
||||
@TableId(type = IdType.AUTO)
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 股票代码
|
||||
*/
|
||||
@Column(name = "stock_code", nullable = false, length = 10)
|
||||
private String stockCode;
|
||||
|
||||
/**
|
||||
* 股票名称
|
||||
*/
|
||||
@Column(name = "stock_name", nullable = false, length = 50)
|
||||
private String stockName;
|
||||
|
||||
/**
|
||||
* 开盘价
|
||||
*/
|
||||
@Column(name = "open_price", precision = 10, scale = 2)
|
||||
private BigDecimal openPrice;
|
||||
|
||||
/**
|
||||
* 收盘价
|
||||
*/
|
||||
@Column(name = "close_price", precision = 10, scale = 2)
|
||||
private BigDecimal closePrice;
|
||||
|
||||
/**
|
||||
* 最高价
|
||||
*/
|
||||
@Column(name = "high_price", precision = 10, scale = 2)
|
||||
private BigDecimal highPrice;
|
||||
|
||||
/**
|
||||
* 最低价
|
||||
*/
|
||||
@Column(name = "low_price", precision = 10, scale = 2)
|
||||
private BigDecimal lowPrice;
|
||||
|
||||
/**
|
||||
* 成交量
|
||||
*/
|
||||
@Column(name = "volume", nullable = false)
|
||||
private Long volume;
|
||||
|
||||
/**
|
||||
* 成交额
|
||||
*/
|
||||
@Column(name = "turnover", precision = 15, scale = 2)
|
||||
private BigDecimal turnover;
|
||||
|
||||
/**
|
||||
* 涨跌幅
|
||||
*/
|
||||
@Column(name = "change_percent", precision = 5, scale = 2)
|
||||
private BigDecimal changePercent;
|
||||
|
||||
/**
|
||||
* 涨跌额
|
||||
*/
|
||||
@Column(name = "change_amount", precision = 10, scale = 2)
|
||||
private BigDecimal changeAmount;
|
||||
|
||||
/**
|
||||
* 总股本
|
||||
*/
|
||||
@Column(name = "total_shares")
|
||||
private Long totalShares;
|
||||
|
||||
/**
|
||||
* 流通股本
|
||||
*/
|
||||
@Column(name = "float_shares")
|
||||
private Long floatShares;
|
||||
|
||||
/**
|
||||
* 总市值
|
||||
*/
|
||||
@Column(name = "market_cap", precision = 15, scale = 2)
|
||||
private BigDecimal marketCap;
|
||||
|
||||
/**
|
||||
* 流通市值
|
||||
*/
|
||||
@Column(name = "float_market_cap", precision = 15, scale = 2)
|
||||
private BigDecimal floatMarketCap;
|
||||
|
||||
/**
|
||||
* 市盈率
|
||||
*/
|
||||
@Column(name = "pe_ratio", precision = 8, scale = 2)
|
||||
private BigDecimal peRatio;
|
||||
|
||||
/**
|
||||
* 市净率
|
||||
*/
|
||||
@Column(name = "pb_ratio", precision = 8, scale = 2)
|
||||
private BigDecimal pbRatio;
|
||||
|
||||
/**
|
||||
* 交易日期
|
||||
*/
|
||||
@Column(name = "trade_date", nullable = false)
|
||||
private LocalDateTime tradeDate;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@Column(name = "create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@Column(name = "update_time")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 是否删除
|
||||
*/
|
||||
@Column(name = "deleted")
|
||||
private Integer deleted;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
createTime = LocalDateTime.now();
|
||||
updateTime = LocalDateTime.now();
|
||||
deleted = 0;
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
updateTime = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.agricultural.stock.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 技术指标实体类
|
||||
*/
|
||||
@Data
|
||||
@TableName("stock_technical_indicators")
|
||||
public class TechnicalIndicator {
|
||||
|
||||
/**
|
||||
* 股票代码
|
||||
*/
|
||||
private String stockCode;
|
||||
|
||||
/**
|
||||
* 股票名称
|
||||
*/
|
||||
private String stockName;
|
||||
|
||||
/**
|
||||
* 交易日期
|
||||
*/
|
||||
private LocalDateTime tradeDate;
|
||||
|
||||
/**
|
||||
* 收盘价
|
||||
*/
|
||||
private Double closePrice;
|
||||
|
||||
/**
|
||||
* 5日移动平均线
|
||||
*/
|
||||
private Double ma5;
|
||||
|
||||
/**
|
||||
* 10日移动平均线
|
||||
*/
|
||||
private Double ma10;
|
||||
|
||||
/**
|
||||
* 20日移动平均线
|
||||
*/
|
||||
private Double ma20;
|
||||
|
||||
/**
|
||||
* 30日移动平均线
|
||||
*/
|
||||
private Double ma30;
|
||||
|
||||
/**
|
||||
* RSI相对强弱指标
|
||||
*/
|
||||
private Double rsi;
|
||||
|
||||
/**
|
||||
* MACD DIF值
|
||||
*/
|
||||
private Double macdDif;
|
||||
|
||||
/**
|
||||
* MACD DEA值
|
||||
*/
|
||||
private Double macdDea;
|
||||
|
||||
/**
|
||||
* 布林带上轨
|
||||
*/
|
||||
private Double bbUpper;
|
||||
|
||||
/**
|
||||
* 布林带中轨
|
||||
*/
|
||||
private Double bbMiddle;
|
||||
|
||||
/**
|
||||
* 布林带下轨
|
||||
*/
|
||||
private Double bbLower;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.agricultural.stock.mapper;
|
||||
|
||||
import com.agricultural.stock.entity.MarketAnalysis;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 市场分析数据Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface MarketAnalysisMapper extends BaseMapper<MarketAnalysis> {
|
||||
|
||||
/**
|
||||
* 获取最新的市场分析数据
|
||||
*/
|
||||
@Select("SELECT * FROM market_analysis ORDER BY analysis_date DESC LIMIT 1")
|
||||
MarketAnalysis getLatestMarketAnalysis();
|
||||
|
||||
/**
|
||||
* 获取指定日期范围的市场分析数据
|
||||
*/
|
||||
@Select("SELECT * FROM market_analysis WHERE analysis_date >= #{startDate} AND analysis_date <= #{endDate} ORDER BY analysis_date DESC")
|
||||
List<MarketAnalysis> getMarketAnalysisByDateRange(LocalDate startDate, LocalDate endDate);
|
||||
|
||||
/**
|
||||
* 获取最近N天的市场分析数据
|
||||
*/
|
||||
@Select("SELECT * FROM market_analysis ORDER BY analysis_date DESC LIMIT #{days}")
|
||||
List<MarketAnalysis> getRecentMarketAnalysis(int days);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.agricultural.stock.mapper;
|
||||
|
||||
import com.agricultural.stock.entity.StockData;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 股票数据Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface StockDataMapper extends BaseMapper<StockData> {
|
||||
|
||||
/**
|
||||
* 获取最新交易日的所有股票数据
|
||||
*/
|
||||
@Select("SELECT * FROM stock_data WHERE DATE(trade_date) = (SELECT MAX(DATE(trade_date)) FROM stock_data)")
|
||||
List<StockData> getLatestStockData();
|
||||
|
||||
/**
|
||||
* 获取指定股票代码的历史数据
|
||||
*/
|
||||
@Select("SELECT * FROM stock_data WHERE stock_code = #{stockCode} ORDER BY trade_date DESC LIMIT #{limit}")
|
||||
List<StockData> getStockHistoryData(String stockCode, int limit);
|
||||
|
||||
/**
|
||||
* 获取涨幅榜前N名
|
||||
*/
|
||||
@Select("SELECT * FROM stock_data WHERE DATE(trade_date) = (SELECT MAX(DATE(trade_date)) FROM stock_data) ORDER BY change_percent DESC LIMIT #{limit}")
|
||||
List<StockData> getTopGainers(int limit);
|
||||
|
||||
/**
|
||||
* 获取跌幅榜前N名
|
||||
*/
|
||||
@Select("SELECT * FROM stock_data WHERE DATE(trade_date) = (SELECT MAX(DATE(trade_date)) FROM stock_data) ORDER BY change_percent ASC LIMIT #{limit}")
|
||||
List<StockData> getTopLosers(int limit);
|
||||
|
||||
/**
|
||||
* 获取成交量榜前N名
|
||||
*/
|
||||
@Select("SELECT * FROM stock_data WHERE DATE(trade_date) = (SELECT MAX(DATE(trade_date)) FROM stock_data) ORDER BY volume DESC LIMIT #{limit}")
|
||||
List<StockData> getTopVolume(int limit);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.agricultural.stock.mapper;
|
||||
|
||||
import com.agricultural.stock.entity.TechnicalIndicator;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 技术指标数据Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface TechnicalIndicatorMapper extends BaseMapper<TechnicalIndicator> {
|
||||
|
||||
/**
|
||||
* 获取指定股票的最新技术指标
|
||||
*/
|
||||
@Select("SELECT * FROM stock_technical_indicators WHERE stock_code = #{stockCode} ORDER BY trade_date DESC LIMIT 1")
|
||||
TechnicalIndicator getLatestTechnicalIndicator(String stockCode);
|
||||
|
||||
/**
|
||||
* 获取指定股票的历史技术指标
|
||||
*/
|
||||
@Select("SELECT * FROM stock_technical_indicators WHERE stock_code = #{stockCode} ORDER BY trade_date DESC LIMIT #{limit}")
|
||||
List<TechnicalIndicator> getStockTechnicalHistory(String stockCode, int limit);
|
||||
|
||||
/**
|
||||
* 获取最新交易日所有股票的技术指标
|
||||
*/
|
||||
@Select("SELECT * FROM stock_technical_indicators WHERE trade_date = (SELECT MAX(trade_date) FROM stock_technical_indicators)")
|
||||
List<TechnicalIndicator> getLatestAllTechnicalIndicators();
|
||||
|
||||
/**
|
||||
* 获取指定日期范围的技术指标数据
|
||||
*/
|
||||
@Select("SELECT * FROM stock_technical_indicators WHERE stock_code = #{stockCode} AND trade_date >= #{startDate} AND trade_date <= #{endDate} ORDER BY trade_date DESC")
|
||||
List<TechnicalIndicator> getTechnicalIndicatorsByDateRange(String stockCode, LocalDate startDate, LocalDate endDate);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.agricultural.stock.service;
|
||||
|
||||
import com.agricultural.stock.entity.MarketAnalysis;
|
||||
import com.agricultural.stock.mapper.MarketAnalysisMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 市场分析服务类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class MarketAnalysisService {
|
||||
|
||||
@Autowired
|
||||
private MarketAnalysisMapper marketAnalysisMapper;
|
||||
|
||||
/**
|
||||
* 获取最新的市场分析数据
|
||||
*/
|
||||
public MarketAnalysis getLatestMarketAnalysis() {
|
||||
try {
|
||||
return marketAnalysisMapper.getLatestMarketAnalysis();
|
||||
} catch (Exception e) {
|
||||
log.error("获取最新市场分析数据失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期范围的市场分析数据
|
||||
*/
|
||||
public List<MarketAnalysis> getMarketAnalysisByDateRange(LocalDate startDate, LocalDate endDate) {
|
||||
try {
|
||||
return marketAnalysisMapper.getMarketAnalysisByDateRange(startDate, endDate);
|
||||
} catch (Exception e) {
|
||||
log.error("获取指定日期范围的市场分析数据失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近N天的市场分析数据
|
||||
*/
|
||||
public List<MarketAnalysis> getRecentMarketAnalysis(int days) {
|
||||
try {
|
||||
return marketAnalysisMapper.getRecentMarketAnalysis(days);
|
||||
} catch (Exception e) {
|
||||
log.error("获取最近{}天的市场分析数据失败", days, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.agricultural.stock.service;
|
||||
|
||||
import com.agricultural.stock.entity.StockData;
|
||||
import com.agricultural.stock.vo.StockAnalysisVO;
|
||||
import com.agricultural.stock.vo.StockTrendVO;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 股票数据服务接口
|
||||
*
|
||||
* @author Agricultural Stock Platform Team
|
||||
*/
|
||||
public interface StockService {
|
||||
|
||||
/**
|
||||
* 获取实时股票数据
|
||||
*
|
||||
* @return 股票数据列表
|
||||
*/
|
||||
List<StockData> getRealtimeStockData();
|
||||
|
||||
/**
|
||||
* 根据股票代码获取历史数据
|
||||
*
|
||||
* @param stockCode 股票代码
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 历史股票数据列表
|
||||
*/
|
||||
List<StockData> getHistoryData(String stockCode, LocalDateTime startDate, LocalDateTime endDate);
|
||||
|
||||
/**
|
||||
* 获取涨幅排行榜
|
||||
*
|
||||
* @param limit 排行数量
|
||||
* @return 涨幅排行榜
|
||||
*/
|
||||
List<StockData> getGrowthRanking(Integer limit);
|
||||
|
||||
/**
|
||||
* 获取市值排行榜
|
||||
*
|
||||
* @param limit 排行数量
|
||||
* @return 市值排行榜
|
||||
*/
|
||||
List<StockData> getMarketCapRanking(Integer limit);
|
||||
|
||||
/**
|
||||
* 获取成交量排行榜
|
||||
*
|
||||
* @param limit 排行数量
|
||||
* @return 成交量排行榜
|
||||
*/
|
||||
List<StockData> getVolumeRanking(Integer limit);
|
||||
|
||||
/**
|
||||
* 获取股票趋势分析
|
||||
*
|
||||
* @param stockCode 股票代码
|
||||
* @param days 分析天数
|
||||
* @return 股票趋势分析结果
|
||||
*/
|
||||
StockTrendVO getStockTrend(String stockCode, Integer days);
|
||||
|
||||
/**
|
||||
* 获取市场综合分析
|
||||
*
|
||||
* @return 市场分析结果
|
||||
*/
|
||||
StockAnalysisVO getMarketAnalysis();
|
||||
|
||||
/**
|
||||
* 获取股票预测数据
|
||||
*
|
||||
* @param stockCode 股票代码
|
||||
* @param days 预测天数
|
||||
* @return 预测数据列表
|
||||
*/
|
||||
List<StockData> getStockPrediction(String stockCode, Integer days);
|
||||
|
||||
/**
|
||||
* 搜索股票
|
||||
*
|
||||
* @param keyword 搜索关键词
|
||||
* @return 搜索结果
|
||||
*/
|
||||
List<StockData> searchStocks(String keyword);
|
||||
|
||||
/**
|
||||
* 保存股票数据
|
||||
*
|
||||
* @param stockData 股票数据
|
||||
* @return 保存结果
|
||||
*/
|
||||
StockData saveStockData(StockData stockData);
|
||||
|
||||
/**
|
||||
* 批量保存股票数据
|
||||
*
|
||||
* @param stockDataList 股票数据列表
|
||||
* @return 保存数量
|
||||
*/
|
||||
Integer batchSaveStockData(List<StockData> stockDataList);
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
package com.agricultural.stock.service.impl;
|
||||
|
||||
import com.agricultural.stock.entity.StockData;
|
||||
import com.agricultural.stock.mapper.StockDataMapper;
|
||||
import com.agricultural.stock.service.StockService;
|
||||
import com.agricultural.stock.vo.StockAnalysisVO;
|
||||
import com.agricultural.stock.vo.StockTrendVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 股票数据服务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class StockServiceImpl implements StockService {
|
||||
|
||||
@Autowired
|
||||
private StockDataMapper stockDataMapper;
|
||||
|
||||
@Override
|
||||
public List<StockData> getRealtimeStockData() {
|
||||
try {
|
||||
return stockDataMapper.getLatestStockData();
|
||||
} catch (Exception e) {
|
||||
log.error("获取实时股票数据失败", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StockData> getHistoryData(String stockCode, LocalDateTime startDate, LocalDateTime endDate) {
|
||||
try {
|
||||
// 这里需要在Mapper中添加按日期范围查询的方法
|
||||
return stockDataMapper.getStockHistoryData(stockCode, 30); // 临时返回最近30条
|
||||
} catch (Exception e) {
|
||||
log.error("获取股票{}历史数据失败", stockCode, e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StockData> getGrowthRanking(Integer limit) {
|
||||
try {
|
||||
return stockDataMapper.getTopGainers(limit != null ? limit : 10);
|
||||
} catch (Exception e) {
|
||||
log.error("获取涨幅排行榜失败", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StockData> getMarketCapRanking(Integer limit) {
|
||||
try {
|
||||
// 这里需要在Mapper中添加按市值排序的方法,暂时返回最新数据
|
||||
List<StockData> latestData = stockDataMapper.getLatestStockData();
|
||||
return latestData.stream()
|
||||
.sorted((a, b) -> b.getMarketCap().compareTo(a.getMarketCap()))
|
||||
.limit(limit != null ? limit : 10)
|
||||
.collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
log.error("获取市值排行榜失败", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StockData> getVolumeRanking(Integer limit) {
|
||||
try {
|
||||
return stockDataMapper.getTopVolume(limit != null ? limit : 10);
|
||||
} catch (Exception e) {
|
||||
log.error("获取成交量排行榜失败", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockTrendVO getStockTrend(String stockCode, Integer days) {
|
||||
try {
|
||||
List<StockData> historyData = stockDataMapper.getStockHistoryData(stockCode, days != null ? days : 30);
|
||||
if (historyData.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StockTrendVO trendVO = new StockTrendVO();
|
||||
StockData latestData = historyData.get(0);
|
||||
|
||||
// 基本信息
|
||||
trendVO.setStockCode(stockCode);
|
||||
trendVO.setStockName(latestData.getStockName());
|
||||
trendVO.setDays(days);
|
||||
trendVO.setCurrentPrice(latestData.getClosePrice());
|
||||
|
||||
// 计算统计数据
|
||||
BigDecimal maxPrice = historyData.stream()
|
||||
.map(StockData::getHighPrice)
|
||||
.max(BigDecimal::compareTo)
|
||||
.orElse(BigDecimal.ZERO);
|
||||
BigDecimal minPrice = historyData.stream()
|
||||
.map(StockData::getLowPrice)
|
||||
.min(BigDecimal::compareTo)
|
||||
.orElse(BigDecimal.ZERO);
|
||||
BigDecimal avgPrice = historyData.stream()
|
||||
.map(StockData::getClosePrice)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add)
|
||||
.divide(BigDecimal.valueOf(historyData.size()), 2, RoundingMode.HALF_UP);
|
||||
|
||||
trendVO.setHighestPrice(maxPrice);
|
||||
trendVO.setLowestPrice(minPrice);
|
||||
trendVO.setAveragePrice(avgPrice);
|
||||
|
||||
// 计算总涨跌幅
|
||||
if (historyData.size() > 1) {
|
||||
StockData oldestData = historyData.get(historyData.size() - 1);
|
||||
BigDecimal totalChange = latestData.getClosePrice()
|
||||
.subtract(oldestData.getClosePrice())
|
||||
.divide(oldestData.getClosePrice(), 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
trendVO.setTotalChangePercent(totalChange);
|
||||
}
|
||||
|
||||
// 计算平均涨跌幅
|
||||
BigDecimal avgChange = historyData.stream()
|
||||
.map(StockData::getChangePercent)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add)
|
||||
.divide(BigDecimal.valueOf(historyData.size()), 2, RoundingMode.HALF_UP);
|
||||
trendVO.setAvgChangePercent(avgChange);
|
||||
|
||||
// 计算成交量统计
|
||||
Long totalVolume = historyData.stream()
|
||||
.map(StockData::getVolume)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(0L, Long::sum);
|
||||
trendVO.setTotalVolume(totalVolume);
|
||||
trendVO.setAvgVolume(totalVolume / historyData.size());
|
||||
|
||||
// 构建价格历史数据
|
||||
List<StockTrendVO.PricePoint> priceHistory = historyData.stream()
|
||||
.map(data -> {
|
||||
StockTrendVO.PricePoint point = new StockTrendVO.PricePoint();
|
||||
point.setTradeDate(data.getTradeDate().toLocalDate());
|
||||
point.setOpenPrice(data.getOpenPrice());
|
||||
point.setClosePrice(data.getClosePrice());
|
||||
point.setHighPrice(data.getHighPrice());
|
||||
point.setLowPrice(data.getLowPrice());
|
||||
point.setVolume(data.getVolume());
|
||||
point.setChangePercent(data.getChangePercent());
|
||||
return point;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
trendVO.setPriceHistory(priceHistory);
|
||||
|
||||
// 判断趋势方向
|
||||
if (avgChange.compareTo(BigDecimal.valueOf(1)) > 0) {
|
||||
trendVO.setTrendDirection("UP");
|
||||
trendVO.setTrendStrength(avgChange.min(BigDecimal.valueOf(100)));
|
||||
} else if (avgChange.compareTo(BigDecimal.valueOf(-1)) < 0) {
|
||||
trendVO.setTrendDirection("DOWN");
|
||||
trendVO.setTrendStrength(avgChange.abs().min(BigDecimal.valueOf(100)));
|
||||
} else {
|
||||
trendVO.setTrendDirection("FLAT");
|
||||
trendVO.setTrendStrength(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
return trendVO;
|
||||
} catch (Exception e) {
|
||||
log.error("获取股票{}趋势分析失败", stockCode, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockAnalysisVO getMarketAnalysis() {
|
||||
try {
|
||||
List<StockData> latestData = stockDataMapper.getLatestStockData();
|
||||
if (latestData.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StockAnalysisVO analysisVO = new StockAnalysisVO();
|
||||
|
||||
// 市场总览
|
||||
StockAnalysisVO.MarketOverview overview = new StockAnalysisVO.MarketOverview();
|
||||
overview.setTotalStocks(latestData.size());
|
||||
|
||||
long upCount = latestData.stream().filter(data ->
|
||||
data.getChangePercent() != null && data.getChangePercent().compareTo(BigDecimal.ZERO) > 0).count();
|
||||
long downCount = latestData.stream().filter(data ->
|
||||
data.getChangePercent() != null && data.getChangePercent().compareTo(BigDecimal.ZERO) < 0).count();
|
||||
long flatCount = latestData.size() - upCount - downCount;
|
||||
|
||||
overview.setUpCount((int) upCount);
|
||||
overview.setDownCount((int) downCount);
|
||||
overview.setFlatCount((int) flatCount);
|
||||
|
||||
// 计算总成交量、总成交额、总市值
|
||||
Long totalVolume = latestData.stream()
|
||||
.map(StockData::getVolume)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(0L, Long::sum);
|
||||
BigDecimal totalTurnover = latestData.stream()
|
||||
.map(StockData::getTurnover)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
BigDecimal totalMarketCap = latestData.stream()
|
||||
.map(StockData::getMarketCap)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
overview.setTotalVolume(totalVolume);
|
||||
overview.setTotalTurnover(totalTurnover);
|
||||
overview.setTotalMarketCap(totalMarketCap);
|
||||
|
||||
// 计算平均涨跌幅
|
||||
BigDecimal avgChange = latestData.stream()
|
||||
.map(StockData::getChangePercent)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add)
|
||||
.divide(BigDecimal.valueOf(latestData.size()), 2, RoundingMode.HALF_UP);
|
||||
overview.setAvgChangePercent(avgChange);
|
||||
|
||||
analysisVO.setMarketOverview(overview);
|
||||
|
||||
// 涨幅榜
|
||||
List<StockAnalysisVO.StockRankingItem> topGainers = getTopGainers(latestData, 10);
|
||||
analysisVO.setTopGainers(topGainers);
|
||||
|
||||
// 跌幅榜
|
||||
List<StockAnalysisVO.StockRankingItem> topLosers = getTopLosers(latestData, 10);
|
||||
analysisVO.setTopLosers(topLosers);
|
||||
|
||||
// 成交量榜
|
||||
List<StockAnalysisVO.StockRankingItem> topVolume = getTopVolumeStocks(latestData, 10);
|
||||
analysisVO.setTopVolume(topVolume);
|
||||
|
||||
return analysisVO;
|
||||
} catch (Exception e) {
|
||||
log.error("获取市场分析失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StockData> getStockPrediction(String stockCode, Integer days) {
|
||||
// 这里应该调用预测模型,暂时返回空列表
|
||||
log.info("股票预测功能暂未实现,股票代码: {}, 预测天数: {}", stockCode, days);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StockData> searchStocks(String keyword) {
|
||||
try {
|
||||
List<StockData> allStocks = stockDataMapper.getLatestStockData();
|
||||
return allStocks.stream()
|
||||
.filter(stock -> stock.getStockCode().contains(keyword) ||
|
||||
stock.getStockName().contains(keyword))
|
||||
.collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
log.error("搜索股票失败,关键词: {}", keyword, e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StockData saveStockData(StockData stockData) {
|
||||
try {
|
||||
stockDataMapper.insert(stockData);
|
||||
return stockData;
|
||||
} catch (Exception e) {
|
||||
log.error("保存股票数据失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer batchSaveStockData(List<StockData> stockDataList) {
|
||||
try {
|
||||
int count = 0;
|
||||
for (StockData stockData : stockDataList) {
|
||||
stockDataMapper.insert(stockData);
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
log.error("批量保存股票数据失败", e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助方法
|
||||
private List<StockAnalysisVO.StockRankingItem> getTopGainers(List<StockData> stockData, int limit) {
|
||||
return stockData.stream()
|
||||
.filter(data -> data.getChangePercent() != null)
|
||||
.sorted((a, b) -> b.getChangePercent().compareTo(a.getChangePercent()))
|
||||
.limit(limit)
|
||||
.map(this::convertToRankingItem)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<StockAnalysisVO.StockRankingItem> getTopLosers(List<StockData> stockData, int limit) {
|
||||
return stockData.stream()
|
||||
.filter(data -> data.getChangePercent() != null)
|
||||
.sorted((a, b) -> a.getChangePercent().compareTo(b.getChangePercent()))
|
||||
.limit(limit)
|
||||
.map(this::convertToRankingItem)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<StockAnalysisVO.StockRankingItem> getTopVolumeStocks(List<StockData> stockData, int limit) {
|
||||
return stockData.stream()
|
||||
.filter(data -> data.getVolume() != null)
|
||||
.sorted((a, b) -> b.getVolume().compareTo(a.getVolume()))
|
||||
.limit(limit)
|
||||
.map(this::convertToRankingItem)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private StockAnalysisVO.StockRankingItem convertToRankingItem(StockData stockData) {
|
||||
StockAnalysisVO.StockRankingItem item = new StockAnalysisVO.StockRankingItem();
|
||||
item.setStockCode(stockData.getStockCode());
|
||||
item.setStockName(stockData.getStockName());
|
||||
item.setCurrentPrice(stockData.getClosePrice());
|
||||
item.setChangePercent(stockData.getChangePercent());
|
||||
item.setChangeAmount(stockData.getChangeAmount());
|
||||
item.setVolume(stockData.getVolume());
|
||||
item.setTurnover(stockData.getTurnover());
|
||||
item.setMarketCap(stockData.getMarketCap());
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.agricultural.stock.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* API统一响应结果封装
|
||||
*
|
||||
* @author Agricultural Stock Platform Team
|
||||
*/
|
||||
@Data
|
||||
public class ApiResponse<T> {
|
||||
|
||||
private static final int SUCCESS_CODE = 200;
|
||||
private static final int ERROR_CODE = 500;
|
||||
private static final String SUCCESS_MESSAGE = "success";
|
||||
|
||||
private int code;
|
||||
private String message;
|
||||
private T data;
|
||||
private long timestamp;
|
||||
|
||||
public ApiResponse() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public ApiResponse(int code, String message, T data) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应
|
||||
*/
|
||||
public static <T> ApiResponse<T> success() {
|
||||
return new ApiResponse<>(SUCCESS_CODE, SUCCESS_MESSAGE, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应带数据
|
||||
*/
|
||||
public static <T> ApiResponse<T> success(T data) {
|
||||
return new ApiResponse<>(SUCCESS_CODE, SUCCESS_MESSAGE, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应带消息和数据
|
||||
*/
|
||||
public static <T> ApiResponse<T> success(String message, T data) {
|
||||
return new ApiResponse<>(SUCCESS_CODE, message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误响应
|
||||
*/
|
||||
public static <T> ApiResponse<T> error(String message) {
|
||||
return new ApiResponse<>(ERROR_CODE, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误响应带错误码
|
||||
*/
|
||||
public static <T> ApiResponse<T> error(int code, String message) {
|
||||
return new ApiResponse<>(code, message, null);
|
||||
}
|
||||
}
|
||||
76
backend/src/main/java/com/agricultural/stock/vo/Result.java
Normal file
76
backend/src/main/java/com/agricultural/stock/vo/Result.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package com.agricultural.stock.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 统一API响应结果类
|
||||
*/
|
||||
@Data
|
||||
public class Result<T> {
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
private int code;
|
||||
|
||||
/**
|
||||
* 响应消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 响应数据
|
||||
*/
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private long timestamp;
|
||||
|
||||
public Result() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public Result(int code, String message, T data) {
|
||||
this();
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应
|
||||
*/
|
||||
public static <T> Result<T> success(T data) {
|
||||
return new Result<>(200, "成功", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应(无数据)
|
||||
*/
|
||||
public static <T> Result<T> success() {
|
||||
return new Result<>(200, "成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应
|
||||
*/
|
||||
public static <T> Result<T> error(String message) {
|
||||
return new Result<>(500, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应(自定义状态码)
|
||||
*/
|
||||
public static <T> Result<T> error(int code, String message) {
|
||||
return new Result<>(code, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否成功
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return this.code == 200;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package com.agricultural.stock.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 股票市场分析结果VO
|
||||
*
|
||||
* @author Agricultural Stock Platform Team
|
||||
*/
|
||||
@Data
|
||||
public class StockAnalysisVO {
|
||||
|
||||
/**
|
||||
* 市场总览
|
||||
*/
|
||||
private MarketOverview marketOverview;
|
||||
|
||||
/**
|
||||
* 涨幅榜前10
|
||||
*/
|
||||
private List<StockRankingItem> topGainers;
|
||||
|
||||
/**
|
||||
* 跌幅榜前10
|
||||
*/
|
||||
private List<StockRankingItem> topLosers;
|
||||
|
||||
/**
|
||||
* 成交量榜前10
|
||||
*/
|
||||
private List<StockRankingItem> topVolume;
|
||||
|
||||
/**
|
||||
* 行业分析
|
||||
*/
|
||||
private List<IndustryAnalysisItem> industryAnalysis;
|
||||
|
||||
/**
|
||||
* 行业分析
|
||||
*/
|
||||
private List<IndustryAnalysis> industryAnalysisList;
|
||||
|
||||
/**
|
||||
* 热门股票
|
||||
*/
|
||||
private List<HotStock> hotStocks;
|
||||
|
||||
/**
|
||||
* 市场情绪指标
|
||||
*/
|
||||
private MarketSentiment marketSentiment;
|
||||
|
||||
@Data
|
||||
public static class MarketOverview {
|
||||
/**
|
||||
* 总股票数
|
||||
*/
|
||||
private Integer totalStocks;
|
||||
|
||||
/**
|
||||
* 上涨股票数
|
||||
*/
|
||||
private Integer upCount;
|
||||
|
||||
/**
|
||||
* 下跌股票数
|
||||
*/
|
||||
private Integer downCount;
|
||||
|
||||
/**
|
||||
* 平盘股票数
|
||||
*/
|
||||
private Integer flatCount;
|
||||
|
||||
/**
|
||||
* 平均涨跌幅
|
||||
*/
|
||||
private BigDecimal avgChangePercent;
|
||||
|
||||
/**
|
||||
* 总成交量
|
||||
*/
|
||||
private Long totalVolume;
|
||||
|
||||
/**
|
||||
* 总成交额
|
||||
*/
|
||||
private BigDecimal totalTurnover;
|
||||
|
||||
/**
|
||||
* 总市值
|
||||
*/
|
||||
private BigDecimal totalMarketCap;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class StockRankingItem {
|
||||
/**
|
||||
* 股票代码
|
||||
*/
|
||||
private String stockCode;
|
||||
|
||||
/**
|
||||
* 股票名称
|
||||
*/
|
||||
private String stockName;
|
||||
|
||||
/**
|
||||
* 当前价格
|
||||
*/
|
||||
private BigDecimal currentPrice;
|
||||
|
||||
/**
|
||||
* 涨跌幅
|
||||
*/
|
||||
private BigDecimal changePercent;
|
||||
|
||||
/**
|
||||
* 涨跌额
|
||||
*/
|
||||
private BigDecimal changeAmount;
|
||||
|
||||
/**
|
||||
* 成交量
|
||||
*/
|
||||
private Long volume;
|
||||
|
||||
/**
|
||||
* 成交额
|
||||
*/
|
||||
private BigDecimal turnover;
|
||||
|
||||
/**
|
||||
* 市值
|
||||
*/
|
||||
private BigDecimal marketCap;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class IndustryAnalysisItem {
|
||||
/**
|
||||
* 行业名称
|
||||
*/
|
||||
private String industry;
|
||||
|
||||
/**
|
||||
* 股票数量
|
||||
*/
|
||||
private Integer stockCount;
|
||||
|
||||
/**
|
||||
* 平均涨跌幅
|
||||
*/
|
||||
private BigDecimal avgChangePercent;
|
||||
|
||||
/**
|
||||
* 总市值
|
||||
*/
|
||||
private BigDecimal totalMarketCap;
|
||||
|
||||
/**
|
||||
* 总成交量
|
||||
*/
|
||||
private Long totalVolume;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class IndustryAnalysis {
|
||||
/**
|
||||
* 行业名称
|
||||
*/
|
||||
private String industryName;
|
||||
|
||||
/**
|
||||
* 股票数量
|
||||
*/
|
||||
private Integer stockCount;
|
||||
|
||||
/**
|
||||
* 平均涨跌幅
|
||||
*/
|
||||
private BigDecimal avgChangePercent;
|
||||
|
||||
/**
|
||||
* 总市值
|
||||
*/
|
||||
private BigDecimal totalMarketCap;
|
||||
|
||||
/**
|
||||
* 领涨股票
|
||||
*/
|
||||
private String leadingStock;
|
||||
|
||||
/**
|
||||
* 行业排名
|
||||
*/
|
||||
private Integer ranking;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class HotStock {
|
||||
/**
|
||||
* 股票代码
|
||||
*/
|
||||
private String stockCode;
|
||||
|
||||
/**
|
||||
* 股票名称
|
||||
*/
|
||||
private String stockName;
|
||||
|
||||
/**
|
||||
* 涨跌幅
|
||||
*/
|
||||
private BigDecimal changePercent;
|
||||
|
||||
/**
|
||||
* 成交量
|
||||
*/
|
||||
private Long volume;
|
||||
|
||||
/**
|
||||
* 热度评分
|
||||
*/
|
||||
private Integer hotScore;
|
||||
|
||||
/**
|
||||
* 热度原因
|
||||
*/
|
||||
private String hotReason;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class MarketSentiment {
|
||||
/**
|
||||
* 市场情绪指数 (0-100)
|
||||
*/
|
||||
private Integer sentimentIndex;
|
||||
|
||||
/**
|
||||
* 恐慌贪婪指数 (0-100)
|
||||
*/
|
||||
private Integer fearGreedIndex;
|
||||
|
||||
/**
|
||||
* 波动率指数
|
||||
*/
|
||||
private BigDecimal volatilityIndex;
|
||||
|
||||
/**
|
||||
* 资金流向
|
||||
*/
|
||||
private String moneyFlow;
|
||||
|
||||
/**
|
||||
* 市场预期
|
||||
*/
|
||||
private String marketExpectation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package com.agricultural.stock.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 股票趋势分析VO
|
||||
*
|
||||
* @author Agricultural Stock Platform Team
|
||||
*/
|
||||
@Data
|
||||
public class StockTrendVO {
|
||||
|
||||
/**
|
||||
* 股票代码
|
||||
*/
|
||||
private String stockCode;
|
||||
|
||||
/**
|
||||
* 股票名称
|
||||
*/
|
||||
private String stockName;
|
||||
|
||||
/**
|
||||
* 分析天数
|
||||
*/
|
||||
private Integer days;
|
||||
|
||||
/**
|
||||
* 当前价格
|
||||
*/
|
||||
private BigDecimal currentPrice;
|
||||
|
||||
/**
|
||||
* 最高价格
|
||||
*/
|
||||
private BigDecimal highestPrice;
|
||||
|
||||
/**
|
||||
* 最低价格
|
||||
*/
|
||||
private BigDecimal lowestPrice;
|
||||
|
||||
/**
|
||||
* 平均价格
|
||||
*/
|
||||
private BigDecimal averagePrice;
|
||||
|
||||
/**
|
||||
* 总涨跌幅
|
||||
*/
|
||||
private BigDecimal totalChangePercent;
|
||||
|
||||
/**
|
||||
* 平均涨跌幅
|
||||
*/
|
||||
private BigDecimal avgChangePercent;
|
||||
|
||||
/**
|
||||
* 波动率
|
||||
*/
|
||||
private BigDecimal volatility;
|
||||
|
||||
/**
|
||||
* 总成交量
|
||||
*/
|
||||
private Long totalVolume;
|
||||
|
||||
/**
|
||||
* 平均成交量
|
||||
*/
|
||||
private Long avgVolume;
|
||||
|
||||
/**
|
||||
* 趋势方向 (UP/DOWN/FLAT)
|
||||
*/
|
||||
private String trendDirection;
|
||||
|
||||
/**
|
||||
* 趋势强度 (0-100)
|
||||
*/
|
||||
private BigDecimal trendStrength;
|
||||
|
||||
/**
|
||||
* 历史价格数据
|
||||
*/
|
||||
private List<PricePoint> priceHistory;
|
||||
|
||||
/**
|
||||
* 技术指标
|
||||
*/
|
||||
private TechnicalIndicators technicalIndicators;
|
||||
|
||||
@Data
|
||||
public static class PricePoint {
|
||||
/**
|
||||
* 交易日期
|
||||
*/
|
||||
private LocalDate tradeDate;
|
||||
|
||||
/**
|
||||
* 开盘价
|
||||
*/
|
||||
private BigDecimal openPrice;
|
||||
|
||||
/**
|
||||
* 收盘价
|
||||
*/
|
||||
private BigDecimal closePrice;
|
||||
|
||||
/**
|
||||
* 最高价
|
||||
*/
|
||||
private BigDecimal highPrice;
|
||||
|
||||
/**
|
||||
* 最低价
|
||||
*/
|
||||
private BigDecimal lowPrice;
|
||||
|
||||
/**
|
||||
* 成交量
|
||||
*/
|
||||
private Long volume;
|
||||
|
||||
/**
|
||||
* 涨跌幅
|
||||
*/
|
||||
private BigDecimal changePercent;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class TechnicalIndicators {
|
||||
/**
|
||||
* 5日移动平均线
|
||||
*/
|
||||
private BigDecimal ma5;
|
||||
|
||||
/**
|
||||
* 10日移动平均线
|
||||
*/
|
||||
private BigDecimal ma10;
|
||||
|
||||
/**
|
||||
* 20日移动平均线
|
||||
*/
|
||||
private BigDecimal ma20;
|
||||
|
||||
/**
|
||||
* 30日移动平均线
|
||||
*/
|
||||
private BigDecimal ma30;
|
||||
|
||||
/**
|
||||
* RSI相对强弱指标
|
||||
*/
|
||||
private BigDecimal rsi;
|
||||
|
||||
/**
|
||||
* MACD DIF值
|
||||
*/
|
||||
private BigDecimal macdDif;
|
||||
|
||||
/**
|
||||
* MACD DEA值
|
||||
*/
|
||||
private BigDecimal macdDea;
|
||||
|
||||
/**
|
||||
* 布林带上轨
|
||||
*/
|
||||
private BigDecimal bbUpper;
|
||||
|
||||
/**
|
||||
* 布林带中轨
|
||||
*/
|
||||
private BigDecimal bbMiddle;
|
||||
|
||||
/**
|
||||
* 布林带下轨
|
||||
*/
|
||||
private BigDecimal bbLower;
|
||||
}
|
||||
}
|
||||
100
backend/src/main/resources/application.yml
Normal file
100
backend/src/main/resources/application.yml
Normal file
@@ -0,0 +1,100 @@
|
||||
server:
|
||||
port: 8080
|
||||
servlet:
|
||||
context-path: /
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: agricultural-stock-platform-backend
|
||||
|
||||
# 数据源配置
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/agricultural_stock?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: root
|
||||
hikari:
|
||||
minimum-idle: 5
|
||||
maximum-pool-size: 20
|
||||
connection-timeout: 30000
|
||||
idle-timeout: 600000
|
||||
max-lifetime: 1800000
|
||||
|
||||
# JPA配置
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
show-sql: false
|
||||
database-platform: org.hibernate.dialect.MySQL8Dialect
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.MySQL8Dialect
|
||||
format_sql: true
|
||||
|
||||
# Redis配置
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
database: 0
|
||||
timeout: 6000ms
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 10
|
||||
max-wait: -1ms
|
||||
max-idle: 8
|
||||
min-idle: 0
|
||||
|
||||
# Kafka配置已移除
|
||||
|
||||
# MyBatis Plus配置
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath*:/mapper/**/*.xml
|
||||
type-aliases-package: com.agricultural.stock.entity
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
cache-enabled: false
|
||||
call-setters-on-nulls: true
|
||||
jdbc-type-for-null: 'null'
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: auto
|
||||
logic-delete-field: deleted
|
||||
logic-delete-value: 1
|
||||
logic-not-delete-value: 0
|
||||
|
||||
# SpringDoc OpenAPI 配置
|
||||
springdoc:
|
||||
api-docs:
|
||||
path: /v3/api-docs
|
||||
enabled: true
|
||||
swagger-ui:
|
||||
path: /swagger-ui
|
||||
enabled: true
|
||||
config-url: /v3/api-docs/swagger-config
|
||||
urls-primary-name: default
|
||||
packages-to-scan: com.agricultural.stock.controller
|
||||
paths-to-match: /api/**
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
com.agricultural.stock: INFO
|
||||
org.springframework: WARN
|
||||
com.baomidou.mybatisplus: WARN
|
||||
pattern:
|
||||
console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
|
||||
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
|
||||
file:
|
||||
name: logs/agricultural-stock-platform.log
|
||||
|
||||
# 自定义配置
|
||||
agricultural:
|
||||
stock:
|
||||
# 数据采集配置
|
||||
crawler:
|
||||
interval: 60000 # 采集间隔(毫秒)
|
||||
batch-size: 100 # 批处理大小
|
||||
# Kafka配置已移除
|
||||
# 缓存配置
|
||||
cache:
|
||||
expire-time: 3600 # 缓存过期时间(秒)
|
||||
Reference in New Issue
Block a user