feat(impact): Impact Agent MVP —— 变更点定位 + 影响调查书(追加改修场景)
- 门控:用户提供 existing_system 路径 → 进入影响调查;未提供 → 原流程不变
- CodeParser 解析 Java(@RestController/@Service/@Entity/@TableName)+ ExistingSystemExplorer 组装
- ImpactAgent 变更点定位(变更区分×既存対応 确定性比对,无 LLM)→ ImpactReport(JSON 可下载)
- 影响调查结果作为 Writer 生成概要设计书的主上下文({{impact}},无专用影响章)
- source_aggregator 解除 existing_system=None 硬编码
- 既有系统样本 sunOnly/stock-trade-system(无 LICENSE,仅测试输入,保留来源标注)
- 新造股票交易域追加改修样本 要件定義_追加改修_股票.xlsx(对齐 sunOnly 真实类名)
- 全量 351 passed / 99.27% 覆盖;门禁 PASS(16 要素:新规5/変更8/削除3/未受影响50)
This commit is contained in:
+17
@@ -0,0 +1,17 @@
|
||||
package com.trade.marketdata;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
|
||||
/**
|
||||
* 行情服务启动类
|
||||
* Created by macro on 2020/8/3.
|
||||
*/
|
||||
@EnableDiscoveryClient
|
||||
@SpringBootApplication
|
||||
public class TradeMarketDataApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TradeMarketDataApplication.class, args);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.trade.marketdata.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description Tushare API 配置类
|
||||
*/
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "tushare")
|
||||
public class TushareConfig {
|
||||
|
||||
/**
|
||||
* Tushare API 的基础 URL
|
||||
*/
|
||||
private String apiUrl;
|
||||
|
||||
/**
|
||||
* Tushare API 的 Token
|
||||
*/
|
||||
private String token;
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.trade.marketdata.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description WebClient 配置类
|
||||
*/
|
||||
@Configuration
|
||||
public class WebClientConfig {
|
||||
|
||||
private final TushareConfig tushareConfig;
|
||||
|
||||
public WebClientConfig(TushareConfig tushareConfig) {
|
||||
this.tushareConfig = tushareConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置 WebClient Bean,用于调用 Tushare API
|
||||
* @return WebClient 实例
|
||||
*/
|
||||
@Bean
|
||||
public WebClient tushareWebClient() {
|
||||
return WebClient.builder()
|
||||
.baseUrl(tushareConfig.getApiUrl())
|
||||
.defaultHeader("Content-Type", "application/json")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 配置类
|
||||
*/
|
||||
package com.trade.marketdata.config;
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package com.trade.marketdata.controller;
|
||||
|
||||
import com.trade.marketdata.entity.DailyMarketData;
|
||||
import com.trade.marketdata.service.DailyMarketDataService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 股票日线行情数据 Controller
|
||||
*
|
||||
* @author Trae
|
||||
* @since 2024-07-26
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/market-data/daily")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "股票日线行情管理", description = "提供股票日线行情数据的同步和查询功能")
|
||||
public class DailyMarketDataController {
|
||||
|
||||
private final DailyMarketDataService dailyMarketDataService;
|
||||
|
||||
/**
|
||||
* 从Tushare同步指定日期的所有股票日线行情数据
|
||||
*
|
||||
* @param tradeDate 交易日期,格式 yyyyMMdd
|
||||
* @return 同步结果
|
||||
*/
|
||||
@PostMapping("/sync/{tradeDate}")
|
||||
@Operation(summary = "同步指定日期的股票日线行情数据", description = "从Tushare同步指定日期的所有股票日线行情数据")
|
||||
public String syncDailyMarketData(@PathVariable String tradeDate) {
|
||||
dailyMarketDataService.syncDailyMarketData(tradeDate);
|
||||
return "Sync daily market data for " + tradeDate + " successfully.";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Tushare同步指定日期范围的所有股票日线行情数据
|
||||
*
|
||||
* @param startDate 开始日期,格式 yyyyMMdd
|
||||
* @param endDate 结束日期,格式 yyyyMMdd
|
||||
* @return 同步结果
|
||||
*/
|
||||
@PostMapping("/sync/range")
|
||||
@Operation(summary = "同步指定日期范围的股票日线行情数据", description = "从Tushare同步指定日期范围内的所有股票日线行情数据")
|
||||
public String syncDailyMarketDataByDateRange(@RequestParam String startDate, @RequestParam String endDate) {
|
||||
dailyMarketDataService.syncDailyMarketDataByDateRange(startDate, endDate);
|
||||
return "Sync daily market data from " + startDate + " to " + endDate + " successfully.";
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定股票在指定日期范围内的日线行情数据
|
||||
*
|
||||
* @param tsCode 股票代码
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 日线行情数据列表
|
||||
*/
|
||||
@GetMapping("/query")
|
||||
@Operation(summary = "查询日线行情数据", description = "查询指定股票在指定日期范围内的日线行情数据")
|
||||
public List<DailyMarketData> getDailyMarketData(
|
||||
@RequestParam String tsCode,
|
||||
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
||||
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
|
||||
return dailyMarketDataService.getDailyMarketData(tsCode, startDate, endDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定日期的所有股票日线行情数据
|
||||
*
|
||||
* @param tradeDate 交易日期
|
||||
* @return 日线行情数据列表
|
||||
*/
|
||||
@GetMapping("/query/{tradeDate}")
|
||||
@Operation(summary = "查询指定日期的所有股票日线行情数据", description = "查询指定日期的所有股票日线行情数据")
|
||||
public List<DailyMarketData> getDailyMarketDataByTradeDate(@PathVariable @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate tradeDate) {
|
||||
return dailyMarketDataService.getDailyMarketDataByTradeDate(tradeDate);
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.trade.marketdata.controller;
|
||||
|
||||
import com.trade.marketdata.entity.RealtimeMarketData;
|
||||
import com.trade.marketdata.service.RealtimeMarketDataService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 股票实时行情数据 Controller
|
||||
*
|
||||
* @author Trae
|
||||
* @since 2024-07-26
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/market-data/realtime")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "股票实时行情管理", description = "提供股票实时行情数据的查询功能")
|
||||
public class RealtimeMarketDataController {
|
||||
|
||||
private final RealtimeMarketDataService realtimeMarketDataService;
|
||||
|
||||
/**
|
||||
* 获取指定股票代码列表的实时行情数据
|
||||
*
|
||||
* @param tsCodes 股票代码列表,逗号分隔,例如 "600000.SH,000001.SZ"
|
||||
* @return 实时行情数据列表
|
||||
*/
|
||||
@GetMapping("/query")
|
||||
@Operation(summary = "查询实时行情数据", description = "获取指定股票代码列表的实时行情数据")
|
||||
public List<RealtimeMarketData> getRealtimeMarketData(@RequestParam String tsCodes) {
|
||||
List<RealtimeMarketData> realtimeData = realtimeMarketDataService.getRealtimeMarketData(tsCodes);
|
||||
// 可以选择是否在这里保存获取到的数据
|
||||
// realtimeMarketDataService.saveRealtimeMarketData(realtimeData);
|
||||
return realtimeData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取并保存指定股票代码列表的实时行情数据
|
||||
*
|
||||
* @param tsCodes 股票代码列表,逗号分隔
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PostMapping("/sync-and-save")
|
||||
@Operation(summary = "同步并保存实时行情数据", description = "获取并保存指定股票代码列表的实时行情数据")
|
||||
public String syncAndSaveRealtimeMarketData(@RequestParam String tsCodes) {
|
||||
List<RealtimeMarketData> realtimeData = realtimeMarketDataService.getRealtimeMarketData(tsCodes);
|
||||
if (realtimeData != null && !realtimeData.isEmpty()) {
|
||||
realtimeMarketDataService.saveRealtimeMarketData(realtimeData);
|
||||
return "Successfully fetched and saved realtime market data for: " + tsCodes;
|
||||
}
|
||||
return "No realtime market data found for: " + tsCodes;
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.trade.marketdata.controller;
|
||||
|
||||
import com.trade.common.api.CommonResult;
|
||||
import com.trade.marketdata.entity.StockBasic;
|
||||
import com.trade.marketdata.service.StockBasicService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description 股票基本信息控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/stockBasic")
|
||||
@Tag(name = "StockBasicController", description = "股票基本信息管理")
|
||||
public class StockBasicController {
|
||||
|
||||
private final StockBasicService stockBasicService;
|
||||
|
||||
public StockBasicController(StockBasicService stockBasicService) {
|
||||
this.stockBasicService = stockBasicService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步股票基本信息
|
||||
* @return 同步结果
|
||||
*/
|
||||
@Operation(summary = "同步股票基本信息")
|
||||
@PostMapping("/sync")
|
||||
public CommonResult<Integer> syncStockBasic() {
|
||||
int count = stockBasicService.syncStockBasicFromTushare();
|
||||
return CommonResult.success(count, "成功同步 " + count + " 条股票基本信息");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有股票基本信息
|
||||
* @return 股票基本信息列表
|
||||
*/
|
||||
@Operation(summary = "获取所有股票基本信息")
|
||||
@GetMapping("/listAll")
|
||||
public CommonResult<List<StockBasic>> listAllStockBasic() {
|
||||
List<StockBasic> stockBasics = stockBasicService.listAllStockBasic();
|
||||
return CommonResult.success(stockBasics);
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package com.trade.marketdata.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 lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description 股票日线行情数据实体类
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@TableName("daily_market_data")
|
||||
public class DailyMarketData implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 股票代码
|
||||
*/
|
||||
private String tsCode;
|
||||
|
||||
/**
|
||||
* 交易日期
|
||||
*/
|
||||
private LocalDate tradeDate;
|
||||
|
||||
/**
|
||||
* 开盘价
|
||||
*/
|
||||
private BigDecimal open;
|
||||
|
||||
/**
|
||||
* 最高价
|
||||
*/
|
||||
private BigDecimal high;
|
||||
|
||||
/**
|
||||
* 最低价
|
||||
*/
|
||||
private BigDecimal low;
|
||||
|
||||
/**
|
||||
* 收盘价
|
||||
*/
|
||||
private BigDecimal close;
|
||||
|
||||
/**
|
||||
* 昨收价
|
||||
*/
|
||||
private BigDecimal preClose;
|
||||
|
||||
/**
|
||||
* 涨跌额
|
||||
*/
|
||||
private BigDecimal change;
|
||||
|
||||
/**
|
||||
* 涨跌幅
|
||||
*/
|
||||
private BigDecimal pctChg;
|
||||
|
||||
/**
|
||||
* 成交量 (手)
|
||||
*/
|
||||
private BigDecimal vol;
|
||||
|
||||
/**
|
||||
* 成交额 (千元)
|
||||
*/
|
||||
private BigDecimal amount;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package com.trade.marketdata.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.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 股票实时行情数据实体类
|
||||
*
|
||||
* @author Trae
|
||||
* @since 2024-07-26
|
||||
*/
|
||||
@Data
|
||||
@TableName("realtime_market_data")
|
||||
public class RealtimeMarketData {
|
||||
|
||||
/**
|
||||
* 自增主键
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 股票代码
|
||||
*/
|
||||
private String tsCode;
|
||||
|
||||
/**
|
||||
* 股票名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 当前价格
|
||||
*/
|
||||
private BigDecimal price;
|
||||
|
||||
/**
|
||||
* 涨跌额
|
||||
*/
|
||||
private BigDecimal change;
|
||||
|
||||
/**
|
||||
* 涨跌幅
|
||||
*/
|
||||
private BigDecimal pctChange;
|
||||
|
||||
/**
|
||||
* 成交量(手)
|
||||
*/
|
||||
private Long volume;
|
||||
|
||||
/**
|
||||
* 成交额(万元)
|
||||
*/
|
||||
private BigDecimal amount;
|
||||
|
||||
/**
|
||||
* 开盘价
|
||||
*/
|
||||
private BigDecimal open;
|
||||
|
||||
/**
|
||||
* 昨日收盘价
|
||||
*/
|
||||
private BigDecimal preClose;
|
||||
|
||||
/**
|
||||
* 最高价
|
||||
*/
|
||||
private BigDecimal high;
|
||||
|
||||
/**
|
||||
* 最低价
|
||||
*/
|
||||
private BigDecimal low;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private LocalDateTime timestamp;
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package com.trade.marketdata.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description 股票基本信息实体类
|
||||
*/
|
||||
@Data
|
||||
@TableName("stock_basic")
|
||||
public class StockBasic implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* TS股票代码
|
||||
*/
|
||||
private String tsCode;
|
||||
|
||||
/**
|
||||
* 股票代码
|
||||
*/
|
||||
private String symbol;
|
||||
|
||||
/**
|
||||
* 股票名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 股票简称
|
||||
*/
|
||||
private String area;
|
||||
|
||||
/**
|
||||
* 所属省份
|
||||
*/
|
||||
private String province;
|
||||
|
||||
/**
|
||||
* 所属城市
|
||||
*/
|
||||
private String city;
|
||||
|
||||
/**
|
||||
* 所属行业
|
||||
*/
|
||||
private String industry;
|
||||
|
||||
/**
|
||||
* 股票全称
|
||||
*/
|
||||
private String fullname;
|
||||
|
||||
/**
|
||||
* 英文全称
|
||||
*/
|
||||
private String enname;
|
||||
|
||||
/**
|
||||
* 市场类型 (主板/创业板/科创板等)
|
||||
*/
|
||||
private String market;
|
||||
|
||||
/**
|
||||
* 交易所代码
|
||||
*/
|
||||
private String exchange;
|
||||
|
||||
/**
|
||||
* 交易货币
|
||||
*/
|
||||
private String currType;
|
||||
|
||||
/**
|
||||
* 上市状态 L上市 D退市 P暂停上市
|
||||
*/
|
||||
private String listStatus;
|
||||
|
||||
/**
|
||||
* 上市日期
|
||||
*/
|
||||
private LocalDate listDate;
|
||||
|
||||
/**
|
||||
* 退市日期
|
||||
*/
|
||||
private LocalDate delistDate;
|
||||
|
||||
/**
|
||||
* 是否沪深港通标的,N否 H沪股通 S深股通
|
||||
*/
|
||||
private String isHs;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.trade.marketdata.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description Tushare API 请求实体类
|
||||
*/
|
||||
@Data
|
||||
public class TushareRequest {
|
||||
|
||||
/**
|
||||
* API 接口名称
|
||||
*/
|
||||
private String apiName;
|
||||
|
||||
/**
|
||||
* Tushare Token
|
||||
*/
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
private Object params;
|
||||
|
||||
/**
|
||||
* 返回字段
|
||||
*/
|
||||
private String fields;
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.trade.marketdata.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description Tushare API 响应实体类
|
||||
*/
|
||||
@Data
|
||||
public class TushareResponse {
|
||||
|
||||
/**
|
||||
* 返回码,0 表示成功
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 响应数据
|
||||
*/
|
||||
private TushareData data;
|
||||
|
||||
@Data
|
||||
public static class TushareData {
|
||||
/**
|
||||
* 字段列表
|
||||
*/
|
||||
private List<String> fields;
|
||||
/**
|
||||
* 数据列表
|
||||
*/
|
||||
private List<List<Object>> items;
|
||||
/**
|
||||
* 总行数
|
||||
*/
|
||||
private Integer has_more;
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 实体类层
|
||||
*/
|
||||
package com.trade.marketdata.entity;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.trade.marketdata.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.trade.marketdata.entity.DailyMarketData;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description 股票日线行情数据 Mapper 接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface DailyMarketDataMapper extends BaseMapper<DailyMarketData> {
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.trade.marketdata.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.trade.marketdata.entity.RealtimeMarketData;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 股票实时行情数据 Mapper 接口
|
||||
*
|
||||
* @author Trae
|
||||
* @since 2024-07-26
|
||||
*/
|
||||
@Mapper
|
||||
public interface RealtimeMarketDataMapper extends BaseMapper<RealtimeMarketData> {
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.trade.marketdata.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.trade.marketdata.entity.StockBasic;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description 股票基本信息 Mapper 接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface StockBasicMapper extends BaseMapper<StockBasic> {
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 数据访问层 (MyBatis Mapper)
|
||||
*/
|
||||
package com.trade.marketdata.mapper;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 行情服务模块主包。
|
||||
*/
|
||||
package com.trade.marketdata;
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.trade.marketdata.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.trade.marketdata.entity.DailyMarketData;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description 股票日线行情数据服务接口
|
||||
*/
|
||||
public interface DailyMarketDataService extends IService<DailyMarketData> {
|
||||
|
||||
/**
|
||||
* 从 Tushare 同步指定股票的日线行情数据
|
||||
* @param tsCode 股票代码
|
||||
* @param startDate 开始日期 (yyyyMMdd)
|
||||
* @param endDate 结束日期 (yyyyMMdd)
|
||||
* @return 同步的日线数据数量
|
||||
*/
|
||||
int syncDailyMarketDataFromTushare(String tsCode, String startDate, String endDate);
|
||||
|
||||
/**
|
||||
* 查询指定股票在指定日期范围内的日线行情数据
|
||||
* @param tsCode 股票代码
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 日线行情数据列表
|
||||
*/
|
||||
List<DailyMarketData> listDailyMarketData(String tsCode, LocalDate startDate, LocalDate endDate);
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.trade.marketdata.service;
|
||||
|
||||
import com.trade.marketdata.entity.RealtimeMarketData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 股票实时行情数据 Service 接口
|
||||
*
|
||||
* @author Trae
|
||||
* @since 2024-07-26
|
||||
*/
|
||||
public interface RealtimeMarketDataService {
|
||||
|
||||
/**
|
||||
* 获取指定股票代码列表的实时行情数据
|
||||
*
|
||||
* @param tsCodes 股票代码列表,逗号分隔
|
||||
* @return 实时行情数据列表
|
||||
*/
|
||||
List<RealtimeMarketData> getRealtimeMarketData(String tsCodes);
|
||||
|
||||
/**
|
||||
* 保存实时行情数据列表
|
||||
*
|
||||
* @param realtimeMarketDataList 实时行情数据列表
|
||||
*/
|
||||
void saveRealtimeMarketData(List<RealtimeMarketData> realtimeMarketDataList);
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.trade.marketdata.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.trade.marketdata.entity.StockBasic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description 股票基本信息服务接口
|
||||
*/
|
||||
public interface StockBasicService extends IService<StockBasic> {
|
||||
|
||||
/**
|
||||
* 从 Tushare 同步股票基本信息
|
||||
* @return 同步的股票数量
|
||||
*/
|
||||
int syncStockBasicFromTushare();
|
||||
|
||||
/**
|
||||
* 查询所有股票基本信息
|
||||
* @return 股票基本信息列表
|
||||
*/
|
||||
List<StockBasic> listAllStockBasic();
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.trade.marketdata.service;
|
||||
|
||||
import com.trade.marketdata.config.TushareConfig;
|
||||
import com.trade.marketdata.entity.TushareRequest;
|
||||
import com.trade.marketdata.entity.TushareResponse;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description Tushare API 调用服务
|
||||
*/
|
||||
@Service
|
||||
public class TushareApi {
|
||||
|
||||
private final WebClient tushareWebClient;
|
||||
private final TushareConfig tushareConfig;
|
||||
|
||||
public TushareApi(WebClient tushareWebClient, TushareConfig tushareConfig) {
|
||||
this.tushareWebClient = tushareWebClient;
|
||||
this.tushareConfig = tushareConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用 Tushare API 获取数据
|
||||
* @param apiName API 名称,例如 "daily"
|
||||
* @param params 请求参数,JSON 格式
|
||||
* @param fields 返回字段,逗号分隔
|
||||
* @return TushareResponse 响应对象
|
||||
*/
|
||||
public Mono<TushareResponse> post(String apiName, Object params, String fields) {
|
||||
TushareRequest request = new TushareRequest();
|
||||
request.setApiName(apiName);
|
||||
request.setToken(tushareConfig.getToken());
|
||||
request.setParams(params);
|
||||
request.setFields(fields);
|
||||
|
||||
return tushareWebClient.post()
|
||||
.bodyValue(request)
|
||||
.retrieve()
|
||||
.bodyToMono(TushareResponse.class);
|
||||
}
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
package com.trade.marketdata.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.trade.marketdata.entity.DailyMarketData;
|
||||
import com.trade.marketdata.entity.TushareResponse;
|
||||
import com.trade.marketdata.mapper.DailyMarketDataMapper;
|
||||
import com.trade.marketdata.service.DailyMarketDataService;
|
||||
import com.trade.marketdata.service.TushareApi;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description 股票日线行情数据服务实现类
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class DailyMarketDataServiceImpl extends ServiceImpl<DailyMarketDataMapper, DailyMarketData> implements DailyMarketDataService {
|
||||
|
||||
private final TushareApi tushareApi;
|
||||
|
||||
public DailyMarketDataServiceImpl(TushareApi tushareApi) {
|
||||
this.tushareApi = tushareApi;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Tushare 同步指定股票的日线行情数据
|
||||
* @param tsCode 股票代码
|
||||
* @param startDate 开始日期 (yyyyMMdd)
|
||||
* @param endDate 结束日期 (yyyyMMdd)
|
||||
* @return 同步的日线数据数量
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int syncDailyMarketDataFromTushare(String tsCode, String startDate, String endDate) {
|
||||
log.info("开始从 Tushare 同步股票 {} 的日线行情数据,日期范围:{} 至 {}...", tsCode, startDate, endDate);
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("ts_code", tsCode);
|
||||
params.put("start_date", startDate);
|
||||
params.put("end_date", endDate);
|
||||
|
||||
String fields = "ts_code,trade_date,open,high,low,close,pre_close,change,pct_chg,vol,amount";
|
||||
|
||||
TushareResponse response = tushareApi.post("daily", params, fields).block();
|
||||
|
||||
if (response == null || response.getCode() != 0 || response.getData() == null) {
|
||||
log.error("从 Tushare 获取股票 {} 日线行情数据失败: {}", tsCode, response != null ? response.getMsg() : "未知错误");
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<List<Object>> items = response.getData().getItems();
|
||||
List<String> fieldsList = response.getData().getFields();
|
||||
|
||||
if (items == null || items.isEmpty()) {
|
||||
log.warn("从 Tushare 获取到股票 {} 的空日线行情数据列表。", tsCode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<DailyMarketData> newDailyDataList = new ArrayList<>();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
|
||||
for (List<Object> item : items) {
|
||||
DailyMarketData dailyData = new DailyMarketData();
|
||||
for (int i = 0; i < fieldsList.size(); i++) {
|
||||
String fieldName = fieldsList.get(i);
|
||||
Object value = item.get(i);
|
||||
|
||||
if (value == null) continue;
|
||||
|
||||
switch (fieldName) {
|
||||
case "ts_code": dailyData.setTsCode(value.toString()); break;
|
||||
case "trade_date": dailyData.setTradeDate(LocalDate.parse(value.toString(), formatter)); break;
|
||||
case "open": dailyData.setOpen(new BigDecimal(value.toString())); break;
|
||||
case "high": dailyData.setHigh(new BigDecimal(value.toString())); break;
|
||||
case "low": dailyData.setLow(new BigDecimal(value.toString())); break;
|
||||
case "close": dailyData.setClose(new BigDecimal(value.toString())); break;
|
||||
case "pre_close": dailyData.setPreClose(new BigDecimal(value.toString())); break;
|
||||
case "change": dailyData.setChange(new BigDecimal(value.toString())); break;
|
||||
case "pct_chg": dailyData.setPctChg(new BigDecimal(value.toString())); break;
|
||||
case "vol": dailyData.setVol(new BigDecimal(value.toString())); break;
|
||||
case "amount": dailyData.setAmount(new BigDecimal(value.toString())); break;
|
||||
}
|
||||
}
|
||||
dailyData.setCreateTime(LocalDateTime.now());
|
||||
dailyData.setUpdateTime(LocalDateTime.now());
|
||||
newDailyDataList.add(dailyData);
|
||||
}
|
||||
|
||||
// 批量插入或更新
|
||||
// 获取当前数据库中指定股票在指定日期范围内的所有日线数据的 ts_code 和 trade_date 组合
|
||||
List<DailyMarketData> existingData = baseMapper.selectList(new QueryWrapper<DailyMarketData>()
|
||||
.eq("ts_code", tsCode)
|
||||
.between("trade_date", LocalDate.parse(startDate, formatter), LocalDate.parse(endDate, formatter)));
|
||||
|
||||
Map<String, DailyMarketData> existingDataMap = existingData.stream()
|
||||
.collect(Collectors.toMap(data -> data.getTsCode() + "_" + data.getTradeDate().format(formatter), data -> data));
|
||||
|
||||
List<DailyMarketData> toInsert = new ArrayList<>();
|
||||
List<DailyMarketData> toUpdate = new ArrayList<>();
|
||||
|
||||
for (DailyMarketData newData : newDailyDataList) {
|
||||
String key = newData.getTsCode() + "_" + newData.getTradeDate().format(formatter);
|
||||
if (existingDataMap.containsKey(key)) {
|
||||
DailyMarketData existing = existingDataMap.get(key);
|
||||
newData.setId(existing.getId()); // 设置ID以便更新
|
||||
toUpdate.add(newData);
|
||||
} else {
|
||||
toInsert.add(newData);
|
||||
}
|
||||
}
|
||||
|
||||
int insertedCount = 0;
|
||||
if (!toInsert.isEmpty()) {
|
||||
saveBatch(toInsert);
|
||||
insertedCount = toInsert.size();
|
||||
log.info("成功插入 {} 条新的股票 {} 日线行情数据。", insertedCount, tsCode);
|
||||
}
|
||||
|
||||
int updatedCount = 0;
|
||||
if (!toUpdate.isEmpty()) {
|
||||
updateBatchById(toUpdate);
|
||||
updatedCount = toUpdate.size();
|
||||
log.info("成功更新 {} 条股票 {} 日线行情数据。", updatedCount, tsCode);
|
||||
}
|
||||
|
||||
log.info("股票 {} 日线行情数据同步完成,总计插入 {} 条,更新 {} 条。", tsCode, insertedCount, updatedCount);
|
||||
return insertedCount + updatedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定股票在指定日期范围内的日线行情数据
|
||||
* @param tsCode 股票代码
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 日线行情数据列表
|
||||
*/
|
||||
@Override
|
||||
public List<DailyMarketData> listDailyMarketData(String tsCode, LocalDate startDate, LocalDate endDate) {
|
||||
QueryWrapper<DailyMarketData> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("ts_code", tsCode);
|
||||
if (startDate != null) {
|
||||
queryWrapper.ge("trade_date", startDate);
|
||||
}
|
||||
if (endDate != null) {
|
||||
queryWrapper.le("trade_date", endDate);
|
||||
}
|
||||
queryWrapper.orderByAsc("trade_date");
|
||||
return list(queryWrapper);
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package com.trade.marketdata.service.impl;
|
||||
|
||||
import com.trade.marketdata.entity.RealtimeMarketData;
|
||||
import com.trade.marketdata.mapper.RealtimeMarketDataMapper;
|
||||
import com.trade.marketdata.service.RealtimeMarketDataService;
|
||||
import com.trade.marketdata.util.TushareApi;
|
||||
import com.trade.marketdata.util.TushareRequest;
|
||||
import com.trade.marketdata.util.TushareResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 股票实时行情数据 Service 实现类
|
||||
*
|
||||
* @author Trae
|
||||
* @since 2024-07-26
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class RealtimeMarketDataServiceImpl implements RealtimeMarketDataService {
|
||||
|
||||
private final RealtimeMarketDataMapper realtimeMarketDataMapper;
|
||||
private final TushareApi tushareApi;
|
||||
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||||
|
||||
/**
|
||||
* 获取指定股票代码列表的实时行情数据
|
||||
*
|
||||
* @param tsCodes 股票代码列表,逗号分隔
|
||||
* @return 实时行情数据列表
|
||||
*/
|
||||
@Override
|
||||
public List<RealtimeMarketData> getRealtimeMarketData(String tsCodes) {
|
||||
TushareRequest<Map<String, String>> request = new TushareRequest<>();
|
||||
request.setApiName("realtime_quotes"); // Tushare 实时行情接口名称,请根据实际情况调整
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("ts_code", tsCodes);
|
||||
request.setParams(params);
|
||||
|
||||
TushareResponse<List<List<Object>>> response = tushareApi.call(request, List.class, List.class, Object.class);
|
||||
|
||||
List<RealtimeMarketData> resultList = new ArrayList<>();
|
||||
if (response != null && response.getData() != null && response.getData().getItems() != null) {
|
||||
List<String> fields = response.getData().getFields();
|
||||
List<List<Object>> items = response.getData().getItems();
|
||||
|
||||
for (List<Object> item : items) {
|
||||
RealtimeMarketData data = new RealtimeMarketData();
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
String fieldName = fields.get(i);
|
||||
Object value = item.get(i);
|
||||
if (value == null) continue;
|
||||
|
||||
switch (fieldName) {
|
||||
case "ts_code":
|
||||
data.setTsCode(String.valueOf(value));
|
||||
break;
|
||||
case "name":
|
||||
data.setName(String.valueOf(value));
|
||||
break;
|
||||
case "price":
|
||||
data.setPrice(new BigDecimal(String.valueOf(value)));
|
||||
break;
|
||||
case "change":
|
||||
data.setChange(new BigDecimal(String.valueOf(value)));
|
||||
break;
|
||||
case "pct_chg": // Tushare返回的字段名可能为 pct_chg
|
||||
case "pct_change":
|
||||
data.setPctChange(new BigDecimal(String.valueOf(value)));
|
||||
break;
|
||||
case "volume":
|
||||
data.setVolume(Long.parseLong(String.valueOf(value)));
|
||||
break;
|
||||
case "amount":
|
||||
data.setAmount(new BigDecimal(String.valueOf(value)));
|
||||
break;
|
||||
case "open":
|
||||
data.setOpen(new BigDecimal(String.valueOf(value)));
|
||||
break;
|
||||
case "pre_close":
|
||||
data.setPreClose(new BigDecimal(String.valueOf(value)));
|
||||
break;
|
||||
case "high":
|
||||
data.setHigh(new BigDecimal(String.valueOf(value)));
|
||||
break;
|
||||
case "low":
|
||||
data.setLow(new BigDecimal(String.valueOf(value)));
|
||||
break;
|
||||
case "time": // Tushare返回的时间字段名可能为 time
|
||||
// 假设Tushare返回的时间格式是 yyyyMMddHHmmss
|
||||
// 如果是其他格式,需要调整 DateTimeFormatter
|
||||
// 如果Tushare直接返回的是 HH:mm:ss 格式,需要结合当前日期进行转换
|
||||
// 这里假设返回的是包含日期的完整时间字符串
|
||||
try {
|
||||
data.setTimestamp(LocalDateTime.parse(String.valueOf(value), FORMATTER));
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to parse timestamp: {} for ts_code: {}. Error: {}", value, data.getTsCode(), e.getMessage());
|
||||
// 可以设置一个默认值或者根据业务需求处理
|
||||
data.setTimestamp(LocalDateTime.now());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
resultList.add(data);
|
||||
}
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存实时行情数据列表
|
||||
*
|
||||
* @param realtimeMarketDataList 实时行情数据列表
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveRealtimeMarketData(List<RealtimeMarketData> realtimeMarketDataList) {
|
||||
if (realtimeMarketDataList == null || realtimeMarketDataList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 实际应用中,可能需要根据 ts_code 和 timestamp 判断数据是否已存在,进行更新或插入操作
|
||||
// 这里简化为直接批量插入
|
||||
realtimeMarketDataList.forEach(realtimeMarketDataMapper::insert);
|
||||
log.info("Successfully saved {} realtime market data records.", realtimeMarketDataList.size());
|
||||
}
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
package com.trade.marketdata.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.trade.marketdata.entity.StockBasic;
|
||||
import com.trade.marketdata.entity.TushareResponse;
|
||||
import com.trade.marketdata.mapper.StockBasicMapper;
|
||||
import com.trade.marketdata.service.StockBasicService;
|
||||
import com.trade.marketdata.service.TushareApi;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author mac
|
||||
* @date 2024/7/16
|
||||
* @description 股票基本信息服务实现类
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class StockBasicServiceImpl extends ServiceImpl<StockBasicMapper, StockBasic> implements StockBasicService {
|
||||
|
||||
private final TushareApi tushareApi;
|
||||
|
||||
public StockBasicServiceImpl(TushareApi tushareApi) {
|
||||
this.tushareApi = tushareApi;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Tushare 同步股票基本信息
|
||||
* @return 同步的股票数量
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int syncStockBasicFromTushare() {
|
||||
log.info("开始从 Tushare 同步股票基本信息...");
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("exchange", ""); // 交易所代码,可选,空表示获取所有
|
||||
params.put("list_status", "L"); // 上市状态 L上市 D退市 P暂停上市
|
||||
|
||||
String fields = "ts_code,symbol,name,area,province,city,industry,fullname,enname,market,exchange,curr_type,list_status,list_date,delist_date,is_hs";
|
||||
|
||||
TushareResponse response = tushareApi.post("stock_basic", params, fields).block();
|
||||
|
||||
if (response == null || response.getCode() != 0 || response.getData() == null) {
|
||||
log.error("从 Tushare 获取股票基本信息失败: {}", response != null ? response.getMsg() : "未知错误");
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<List<Object>> items = response.getData().getItems();
|
||||
List<String> fieldsList = response.getData().getFields();
|
||||
|
||||
if (items == null || items.isEmpty()) {
|
||||
log.warn("从 Tushare 获取到空股票基本信息列表。");
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<StockBasic> newStockBasics = new ArrayList<>();
|
||||
for (List<Object> item : items) {
|
||||
StockBasic stockBasic = new StockBasic();
|
||||
for (int i = 0; i < fieldsList.size(); i++) {
|
||||
String fieldName = fieldsList.get(i);
|
||||
Object value = item.get(i);
|
||||
|
||||
if (value == null) continue;
|
||||
|
||||
switch (fieldName) {
|
||||
case "ts_code": stockBasic.setTsCode(value.toString()); break;
|
||||
case "symbol": stockBasic.setSymbol(value.toString()); break;
|
||||
case "name": stockBasic.setName(value.toString()); break;
|
||||
case "area": stockBasic.setArea(value.toString()); break;
|
||||
case "province": stockBasic.setProvince(value.toString()); break;
|
||||
case "city": stockBasic.setCity(value.toString()); break;
|
||||
case "industry": stockBasic.setIndustry(value.toString()); break;
|
||||
case "fullname": stockBasic.setFullname(value.toString()); break;
|
||||
case "enname": stockBasic.setEnname(value.toString()); break;
|
||||
case "market": stockBasic.setMarket(value.toString()); break;
|
||||
case "exchange": stockBasic.setExchange(value.toString()); break;
|
||||
case "curr_type": stockBasic.setCurrType(value.toString()); break;
|
||||
case "list_status": stockBasic.setListStatus(value.toString()); break;
|
||||
case "list_date": stockBasic.setListDate(LocalDate.parse(value.toString())); break;
|
||||
case "delist_date": stockBasic.setDelistDate(value.toString().isEmpty() ? null : LocalDate.parse(value.toString())); break;
|
||||
case "is_hs": stockBasic.setIsHs(value.toString()); break;
|
||||
}
|
||||
}
|
||||
stockBasic.setCreateTime(LocalDateTime.now());
|
||||
stockBasic.setUpdateTime(LocalDateTime.now());
|
||||
newStockBasics.add(stockBasic);
|
||||
}
|
||||
|
||||
// 批量插入或更新
|
||||
// 考虑到数据量可能较大,且需要判断是否已存在,这里可以先查询现有数据,然后进行区分插入和更新
|
||||
// 简化处理:先删除所有现有数据,再批量插入新数据 (适用于数据量不大,且更新频率不高的场景)
|
||||
// 更优方案:根据 ts_code 判断是否存在,存在则更新,不存在则插入
|
||||
|
||||
// 获取当前数据库中所有股票的 ts_code 集合
|
||||
List<String> existingTsCodes = baseMapper.selectList(null).stream()
|
||||
.map(StockBasic::getTsCode)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<StockBasic> toInsert = new ArrayList<>();
|
||||
List<StockBasic> toUpdate = new ArrayList<>();
|
||||
|
||||
for (StockBasic stock : newStockBasics) {
|
||||
if (existingTsCodes.contains(stock.getTsCode())) {
|
||||
// 查找现有记录的ID,用于更新
|
||||
StockBasic existingStock = baseMapper.selectOne(com.baomidou.mybatisplus.core.conditions.query.QueryWrapper.<StockBasic>lambdaQuery().eq(StockBasic::getTsCode, stock.getTsCode()));
|
||||
if (existingStock != null) {
|
||||
stock.setId(existingStock.getId());
|
||||
toUpdate.add(stock);
|
||||
}
|
||||
} else {
|
||||
toInsert.add(stock);
|
||||
}
|
||||
}
|
||||
|
||||
int insertedCount = 0;
|
||||
if (!toInsert.isEmpty()) {
|
||||
saveBatch(toInsert);
|
||||
insertedCount = toInsert.size();
|
||||
log.info("成功插入 {} 条新的股票基本信息。".formatted(insertedCount));
|
||||
}
|
||||
|
||||
int updatedCount = 0;
|
||||
if (!toUpdate.isEmpty()) {
|
||||
updateBatchById(toUpdate);
|
||||
updatedCount = toUpdate.size();
|
||||
log.info("成功更新 {} 条股票基本信息。".formatted(updatedCount));
|
||||
}
|
||||
|
||||
log.info("股票基本信息同步完成,总计插入 {} 条,更新 {} 条。".formatted(insertedCount, updatedCount));
|
||||
return insertedCount + updatedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有股票基本信息
|
||||
* @return 股票基本信息列表
|
||||
*/
|
||||
@Override
|
||||
public List<StockBasic> listAllStockBasic() {
|
||||
return list();
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 服务实现层
|
||||
*/
|
||||
package com.trade.marketdata.service.impl;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 服务接口层
|
||||
*/
|
||||
package com.trade.marketdata.service;
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.stock.marketdata.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.stock.common.pojo.CommonResult;
|
||||
import com.stock.marketdata.dal.dataobject.MarketDataDO;
|
||||
import com.stock.marketdata.service.MarketDataService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 行情数据 Controller
|
||||
*
|
||||
* @author Pure AI
|
||||
*/
|
||||
@Tag(name = "行情数据接口")
|
||||
@RestController
|
||||
@RequestMapping("/market-data")
|
||||
public class MarketDataController {
|
||||
|
||||
@Resource
|
||||
private MarketDataService marketDataService;
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "创建行情数据")
|
||||
public CommonResult<Long> createMarketData(@RequestBody MarketDataDO marketData) {
|
||||
return CommonResult.success(marketDataService.createMarketData(marketData));
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Operation(summary = "更新行情数据")
|
||||
public CommonResult<Boolean> updateMarketData(@RequestBody MarketDataDO marketData) {
|
||||
marketDataService.updateMarketData(marketData);
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除行情数据")
|
||||
@Parameter(name = "id", description = "行情数据编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> deleteMarketData(@PathVariable("id") Long id) {
|
||||
marketDataService.deleteMarketData(id);
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取行情数据")
|
||||
@Parameter(name = "id", description = "行情数据编号", required = true, example = "1024")
|
||||
public CommonResult<MarketDataDO> getMarketData(@PathVariable("id") Long id) {
|
||||
return CommonResult.success(marketDataService.getMarketData(id));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取行情数据列表")
|
||||
@Parameter(name = "stockCode", description = "股票代码", example = "000001")
|
||||
public CommonResult<List<MarketDataDO>> getMarketDataList(@RequestParam(required = false) String stockCode) {
|
||||
return CommonResult.success(marketDataService.getMarketDataList(stockCode));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "分页查询行情数据")
|
||||
public CommonResult<Page<MarketDataDO>> pageMarketData(
|
||||
@Parameter(description = "页码", example = "1") @RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@Parameter(description = "每页条数", example = "10") @RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@Parameter(description = "股票代码", example = "000001") @RequestParam(required = false) String stockCode) {
|
||||
Page<MarketDataDO> page = new Page<>(pageNum, pageSize);
|
||||
return CommonResult.success(marketDataService.pageMarketData(page, stockCode));
|
||||
}
|
||||
|
||||
// TODO: 添加其他接口
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package com.stock.marketdata.dal.dataobject;
|
||||
|
||||
import com.stock.common.dal.dataobject.BaseDO; // 引入公共BaseDO
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 行情数据DO
|
||||
*
|
||||
* @author TraeAI
|
||||
*/
|
||||
@TableName("market_data") // TODO: 确认表名是否正确
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class MarketDataDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 股票代码
|
||||
*/
|
||||
private String stockCode;
|
||||
|
||||
/**
|
||||
* 股票名称
|
||||
*/
|
||||
private String stockName;
|
||||
|
||||
/**
|
||||
* 最新价格
|
||||
*/
|
||||
private Double latestPrice;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
// TODO: 根据实际需求添加更多字段
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.stock.marketdata.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.stock.marketdata.dal.dataobject.MarketDataDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 行情数据 Mapper
|
||||
*
|
||||
* @author Pure AI
|
||||
*/
|
||||
@Mapper
|
||||
public interface MarketDataMapper extends BaseMapper<MarketDataDO> {
|
||||
|
||||
// TODO: 定义自定义的 SQL 查询方法
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.stock.marketdata.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.stock.marketdata.dal.dataobject.MarketDataDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 行情数据 Mapper
|
||||
*
|
||||
* @author TraeAI
|
||||
*/
|
||||
@Mapper
|
||||
public interface MarketDataMapper extends BaseMapper<MarketDataDO> {
|
||||
// TODO: 定义行情数据相关的数据库操作方法
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package com.stock.marketdata.service;
|
||||
|
||||
import com.stock.marketdata.dal.dataobject.MarketDataDO;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 行情数据服务接口
|
||||
*
|
||||
* @author Pure AI
|
||||
*/
|
||||
public interface MarketDataService {
|
||||
|
||||
/**
|
||||
* 创建行情数据
|
||||
*
|
||||
* @param marketData 行情数据对象
|
||||
* @return 创建的行情数据ID
|
||||
*/
|
||||
Long createMarketData(MarketDataDO marketData);
|
||||
|
||||
/**
|
||||
* 更新行情数据
|
||||
*
|
||||
* @param marketData 行情数据对象
|
||||
*/
|
||||
void updateMarketData(MarketDataDO marketData);
|
||||
|
||||
/**
|
||||
* 删除行情数据
|
||||
*
|
||||
* @param id 行情数据ID
|
||||
*/
|
||||
void deleteMarketData(Long id);
|
||||
|
||||
/**
|
||||
* 获取行情数据
|
||||
*
|
||||
* @param id 行情数据ID
|
||||
* @return 行情数据对象
|
||||
*/
|
||||
MarketDataDO getMarketData(Long id);
|
||||
|
||||
/**
|
||||
* 获取行情数据列表
|
||||
*
|
||||
* @param stockCode 股票代码
|
||||
* @return 行情数据列表
|
||||
*/
|
||||
List<MarketDataDO> getMarketDataList(String stockCode);
|
||||
|
||||
/**
|
||||
* 分页查询行情数据
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param stockCode 股票代码
|
||||
* @return 分页结果
|
||||
*/
|
||||
Page<MarketDataDO> pageMarketData(Page<MarketDataDO> page, String stockCode);
|
||||
|
||||
// TODO: 添加其他业务方法
|
||||
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package com.stock.marketdata.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.stock.marketdata.dal.dataobject.MarketDataDO;
|
||||
import com.stock.marketdata.dal.mapper.MarketDataMapper;
|
||||
import com.stock.marketdata.service.MarketDataService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 行情数据服务实现类
|
||||
*
|
||||
* @author Pure AI
|
||||
*/
|
||||
@Service
|
||||
public class MarketDataServiceImpl implements MarketDataService {
|
||||
|
||||
@Resource
|
||||
private MarketDataMapper marketDataMapper;
|
||||
|
||||
/**
|
||||
* 创建行情数据
|
||||
*
|
||||
* @param marketData 行情数据对象
|
||||
* @return 创建的行情数据ID
|
||||
*/
|
||||
@Override
|
||||
public Long createMarketData(MarketDataDO marketData) {
|
||||
marketDataMapper.insert(marketData);
|
||||
return marketData.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新行情数据
|
||||
*
|
||||
* @param marketData 行情数据对象
|
||||
*/
|
||||
@Override
|
||||
public void updateMarketData(MarketDataDO marketData) {
|
||||
marketDataMapper.updateById(marketData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除行情数据
|
||||
*
|
||||
* @param id 行情数据ID
|
||||
*/
|
||||
@Override
|
||||
public void deleteMarketData(Long id) {
|
||||
marketDataMapper.deleteById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取行情数据
|
||||
*
|
||||
* @param id 行情数据ID
|
||||
* @return 行情数据对象
|
||||
*/
|
||||
@Override
|
||||
public MarketDataDO getMarketData(Long id) {
|
||||
return marketDataMapper.selectById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取行情数据列表
|
||||
*
|
||||
* @param stockCode 股票代码
|
||||
* @return 行情数据列表
|
||||
*/
|
||||
@Override
|
||||
public List<MarketDataDO> getMarketDataList(String stockCode) {
|
||||
LambdaQueryWrapper<MarketDataDO> queryWrapper = new LambdaQueryWrapper<>();
|
||||
if (StringUtils.hasText(stockCode)) {
|
||||
queryWrapper.eq(MarketDataDO::getStockCode, stockCode);
|
||||
}
|
||||
// TODO: 根据业务需求添加其他查询条件,例如时间范围等
|
||||
queryWrapper.orderByDesc(MarketDataDO::getTradingDay); // 默认按交易日降序
|
||||
return marketDataMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询行情数据
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param stockCode 股票代码
|
||||
* @return 分页结果
|
||||
*/
|
||||
@Override
|
||||
public Page<MarketDataDO> pageMarketData(Page<MarketDataDO> page, String stockCode) {
|
||||
LambdaQueryWrapper<MarketDataDO> queryWrapper = new LambdaQueryWrapper<>();
|
||||
if (StringUtils.hasText(stockCode)) {
|
||||
queryWrapper.eq(MarketDataDO::getStockCode, stockCode);
|
||||
}
|
||||
// TODO: 根据业务需求添加其他查询条件,例如时间范围等
|
||||
queryWrapper.orderByDesc(MarketDataDO::getTradingDay); // 默认按交易日降序
|
||||
return marketDataMapper.selectPage(page, queryWrapper);
|
||||
}
|
||||
|
||||
// TODO: 实现其他业务方法
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
server:
|
||||
port: 8004
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
application:
|
||||
name: trade-market-data
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
||||
@@ -0,0 +1,11 @@
|
||||
server:
|
||||
port: 8004
|
||||
spring:
|
||||
profiles:
|
||||
active: prod
|
||||
application:
|
||||
name: trade-market-data
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
||||
@@ -0,0 +1,11 @@
|
||||
server:
|
||||
port: 8004
|
||||
spring:
|
||||
profiles:
|
||||
active: test
|
||||
application:
|
||||
name: trade-market-data
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
||||
@@ -0,0 +1,72 @@
|
||||
server:
|
||||
port: 8081 # 服务端口,可以根据实际情况修改
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: trade-market-data # 应用名称
|
||||
# datasource: # 数据库配置,可以从 yudao-cloud-mini 的公共配置中获取或在此处覆盖
|
||||
# url: jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
|
||||
# username: your_username
|
||||
# password: your_password
|
||||
# driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# redis: # Redis 配置,可以从 yudao-cloud-mini 的公共配置中获取或在此处覆盖
|
||||
# host: localhost
|
||||
# port: 6379
|
||||
# password:
|
||||
# database: 0
|
||||
|
||||
# Tushare API 配置
|
||||
tushare:
|
||||
api-url: http://api.tushare.pro
|
||||
token: "YOUR_TUSHARE_TOKEN" # 请替换为您的 Tushare Token
|
||||
|
||||
# Mybatis Plus 配置
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath*:/mapper/**/*.xml # Mapper XML 文件路径
|
||||
#type-aliases-package: com.trade.marketdata.entity # 实体类别名包路径,如果需要的话
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: auto # ID 生成策略
|
||||
# table-prefix: t_ # 表前缀,如果需要的话
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true # 开启驼峰命名转换
|
||||
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志,生产环境建议关闭或使用更完善的日志方案
|
||||
|
||||
# SpringDoc OpenAPI 配置 (Swagger)
|
||||
springdoc:
|
||||
api-docs:
|
||||
path: /v3/api-docs # API 文档路径
|
||||
swagger-ui:
|
||||
path: /swagger-ui.html # Swagger UI 路径
|
||||
enabled: true # 开启 Swagger UI
|
||||
group-configs:
|
||||
- group: default
|
||||
paths-to-match: /**
|
||||
packages-to-scan: com.trade.marketdata.controller # Controller 包路径
|
||||
|
||||
# Nacos 配置,用于服务注册与发现
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
|
||||
# namespace: # Nacos 命名空间,如果需要的话
|
||||
# group: # Nacos 分组,如果需要的话
|
||||
# config:
|
||||
# server-addr: ${spring.cloud.nacos.discovery.server-addr}
|
||||
# file-extension: yaml # 配置文件格式
|
||||
# namespace: ${spring.cloud.nacos.discovery.namespace}
|
||||
# group: ${spring.cloud.nacos.discovery.group}
|
||||
# shared-configs[0]: # 共享配置
|
||||
# data-id: application-common.yaml
|
||||
# group: DEFAULT_GROUP
|
||||
# refresh: true
|
||||
|
||||
# 日志配置 (可选,Spring Boot 默认使用 Logback)
|
||||
logging:
|
||||
level:
|
||||
com.trade.marketdata: DEBUG # 设置项目包的日志级别
|
||||
# org.springframework: INFO
|
||||
# org.apache.ibatis: DEBUG # 如果需要查看 MyBatis 执行的 SQL
|
||||
# file:
|
||||
# name: ./logs/trade-market-data.log # 日志文件路径
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<include resource="base-logback.xml"/>
|
||||
<logger name="com.trade.marketdata" level="debug"/>
|
||||
</configuration>
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package com.stock.marketdata.controller;
|
||||
|
||||
import com.stock.marketdata.service.MarketDataService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.http.MediaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.stock.marketdata.dal.dataobject.MarketDataDO;
|
||||
import com.stock.common.pojo.CommonResult;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@WebMvcTest(MarketDataController.class)
|
||||
class MarketDataControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
private MarketDataService marketDataService;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private MarketDataDO createMarketDataDO() {
|
||||
MarketDataDO marketData = new MarketDataDO();
|
||||
marketData.setId(1L);
|
||||
marketData.setStockCode("000001");
|
||||
marketData.setStockName("平安银行");
|
||||
marketData.setOpenPrice(BigDecimal.valueOf(10.00));
|
||||
marketData.setClosePrice(BigDecimal.valueOf(10.50));
|
||||
marketData.setHighPrice(BigDecimal.valueOf(10.60));
|
||||
marketData.setLowPrice(BigDecimal.valueOf(9.90));
|
||||
marketData.setVolume(10000L);
|
||||
marketData.setTurnover(BigDecimal.valueOf(105000.00));
|
||||
marketData.setTradeTime(LocalDateTime.now());
|
||||
return marketData;
|
||||
}
|
||||
|
||||
@Test
|
||||
void createMarketData() throws Exception {
|
||||
MarketDataDO marketData = createMarketDataDO();
|
||||
when(marketDataService.createMarketData(any(MarketDataDO.class))).thenReturn(1L);
|
||||
|
||||
mockMvc.perform(post("/market-data")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(marketData)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(0))
|
||||
.andExpect(jsonPath("$.data").value(1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateMarketData() throws Exception {
|
||||
MarketDataDO marketData = createMarketDataDO();
|
||||
doNothing().when(marketDataService).updateMarketData(any(MarketDataDO.class));
|
||||
|
||||
mockMvc.perform(put("/market-data")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(marketData)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(0))
|
||||
.andExpect(jsonPath("$.data").value(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteMarketData() throws Exception {
|
||||
doNothing().when(marketDataService).deleteMarketData(anyLong());
|
||||
|
||||
mockMvc.perform(delete("/market-data/1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(0))
|
||||
.andExpect(jsonPath("$.data").value(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMarketData() throws Exception {
|
||||
MarketDataDO marketData = createMarketDataDO();
|
||||
when(marketDataService.getMarketData(anyLong())).thenReturn(marketData);
|
||||
|
||||
mockMvc.perform(get("/market-data/1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(0))
|
||||
.andExpect(jsonPath("$.data.id").value(1L))
|
||||
.andExpect(jsonPath("$.data.stockCode").value("000001"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMarketDataList() throws Exception {
|
||||
MarketDataDO marketData = createMarketDataDO();
|
||||
List<MarketDataDO> list = Collections.singletonList(marketData);
|
||||
when(marketDataService.getMarketDataList(anyString())).thenReturn(list);
|
||||
|
||||
mockMvc.perform(get("/market-data/list").param("stockCode", "000001"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(0))
|
||||
.andExpect(jsonPath("$.data[0].id").value(1L))
|
||||
.andExpect(jsonPath("$.data[0].stockCode").value("000001"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void pageMarketData() throws Exception {
|
||||
MarketDataDO marketData = createMarketDataDO();
|
||||
Page<MarketDataDO> page = new Page<>(1, 10);
|
||||
page.setRecords(Collections.singletonList(marketData));
|
||||
page.setTotal(1L);
|
||||
|
||||
when(marketDataService.pageMarketData(any(Page.class), anyString())).thenReturn(page);
|
||||
|
||||
mockMvc.perform(get("/market-data/page")
|
||||
.param("pageNum", "1")
|
||||
.param("pageSize", "10")
|
||||
.param("stockCode", "000001"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(0))
|
||||
.andExpect(jsonPath("$.data.records[0].id").value(1L))
|
||||
.andExpect(jsonPath("$.data.total").value(1L));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user