first commit

This commit is contained in:
2025-06-12 19:37:54 +08:00
parent bb2eb010f7
commit 1c6093fa9a
87 changed files with 18432 additions and 0 deletions

View File

@@ -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("==========================================");
}
}

View File

@@ -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("生产环境")
));
}
}

View File

@@ -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");
}
}

View File

@@ -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());
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View 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 # 缓存过期时间(秒)