使用Spring Boot对接印度股票市场API开发实践

本文将指导你如何使用Spring Boot框架快速对接StockTV的印度股票数据API,包含REST API调用、WebSocket实时推送以及企业级应用开发的最佳实践。

一、环境准备

1. 创建Spring Boot项目

使用Spring Initializr创建项目(start.spring.io/),选择以下依赖:

  • Spring Web
  • Spring WebSocket
  • Lombok
  • Apache HttpClient

2. 配置依赖(pom.xml)

xml 复制代码
<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    
    <!-- 工具库 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.13</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.0</version>
    </dependency>
</dependencies>

3. 配置API密钥

application.yml中添加配置:

yml 复制代码
stocktv:
  api:
    base-url: https://api.stocktv.top
    key: YOUR_API_KEY # 联系官方获取
    ws-url: wss://ws-api.stocktv.top/connect

二、核心功能实现

1. 配置HTTP客户端

java 复制代码
@Configuration
public class HttpClientConfig {
    
    @Bean
    public CloseableHttpClient httpClient() {
        return HttpClients.custom()
                .setConnectionManager(new PoolingHttpClientConnectionManager())
                .build();
    }
    
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

2. 封装API服务

java 复制代码
@Service
@RequiredArgsConstructor
public class StockApiService {
    
    private final CloseableHttpClient httpClient;
    
    @Value("${stocktv.api.base-url}")
    private String baseUrl;
    
    @Value("${stocktv.api.key}")
    private String apiKey;
    
    // 获取印度股票列表
    public List<Stock> getIndianStocks(int pageSize, int page) throws IOException {
        String url = String.format("%s/stock/stocks?countryId=14&exchangeId=46&pageSize=%d&page=%d&key=%s", 
                baseUrl, pageSize, page, apiKey);
        
        HttpGet request = new HttpGet(url);
        try (CloseableHttpResponse response = httpClient.execute(request)) {
            String json = EntityUtils.toString(response.getEntity());
            return parseStockList(json);
        }
    }
    
    // 解析股票数据
    private List<Stock> parseStockList(String json) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode root = mapper.readTree(json);
        JsonNode records = root.path("data").path("records");
        
        List<Stock> stocks = new ArrayList<>();
        for (JsonNode node : records) {
            Stock stock = Stock.builder()
                    .id(node.path("id").asInt())
                    .symbol(node.path("symbol").asText())
                    .name(node.path("name").asText())
                    .lastPrice(node.path("last").asDouble())
                    .changePercent(node.path("chgPct").asDouble())
                    .volume(node.path("volume").asLong())
                    .build();
            stocks.add(stock);
        }
        return stocks;
    }
    
    // 其他API方法...
}

// Lombok数据模型
@Data
@Builder
public class Stock {
    private int id;
    private String symbol;
    private String name;
    private double lastPrice;
    private double changePercent;
    private long volume;
}

3. 实现K线数据服务

java 复制代码
@Service
public class KlineService {
    
    private final RestTemplate restTemplate;
    
    @Value("${stocktv.api.base-url}")
    private String baseUrl;
    
    @Value("${stocktv.api.key}")
    private String apiKey;
    
    // 获取K线数据
    public List<Kline> getKlines(int pid, String interval) {
        String url = String.format("%s/stock/kline?pid=%d&interval=%s&key=%s", 
                baseUrl, pid, interval, apiKey);
        
        ResponseEntity<List<Kline>> response = restTemplate.exchange(
                url, 
                HttpMethod.GET, 
                null, 
                new ParameterizedTypeReference<List<Kline>>() {}
        );
        
        return response.getBody();
    }
    
    // K线数据模型
    @Data
    public static class Kline {
        private long time;
        private double open;
        private double high;
        private double low;
        private double close;
        private long volume;
    }
}

三、WebSocket实时推送

1. WebSocket配置

java 复制代码
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }
    
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws").withSockJS();
    }
}

2. WebSocket服务端

java 复制代码
@Service
public class StockWebSocketService {
    
    @Value("${stocktv.api.ws-url}")
    private String wsUrl;
    
    @Value("${stocktv.api.key}")
    private String apiKey;
    
    private WebSocketStompClient stompClient;
    
    @PostConstruct
    public void init() {
        // 初始化WebSocket客户端
        WebSocketClient client = new StandardWebSocketClient();
        this.stompClient = new WebSocketStompClient(client);
        this.stompClient.setMessageConverter(new MappingJackson2MessageConverter());
        
        connectToStockTv();
    }
    
    private void connectToStockTv() {
        String url = wsUrl + "?key=" + apiKey;
        StompSessionHandler handler = new StockSessionHandler();
        this.stompClient.connect(url, handler);
    }
    
    // 自定义Session处理器
    private class StockSessionHandler extends StompSessionHandlerAdapter {
        @Override
        public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
            // 订阅所有股票更新
            session.subscribe("/topic/stocks", new StockFrameHandler());
            
            // 发送订阅请求
            Map<String, Object> subscription = Map.of(
                "action", "subscribe",
                "pids", List.of(7310, 41602) // 订阅的股票ID
            );
            session.send("/app/subscribe", subscription);
        }
        
        @Override
        public void handleFrame(StompHeaders headers, Object payload) {
            // 处理实时数据
            System.out.println("Received: " + payload);
        }
    }
    
    // 自定义消息处理器
    private class StockFrameHandler implements StompFrameHandler {
        @Override
        public Type getPayloadType(StompHeaders headers) {
            return Map.class;
        }
        
        @Override
        public void handleFrame(StompHeaders headers, Object payload) {
            Map<String, Object> data = (Map<String, Object>) payload;
            System.out.println("Real-time update: " + data);
        }
    }
}

3. WebSocket控制器

java 复制代码
@Controller
public class StockWebSocketController {
    
    private final SimpMessagingTemplate messagingTemplate;
    
    public StockWebSocketController(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }
    
    // 广播股票更新
    public void broadcastStockUpdate(Stock stock) {
        messagingTemplate.convertAndSend("/topic/stocks", stock);
    }
}

四、REST API接口

1. 股票控制器

java 复制代码
@RestController
@RequestMapping("/api/stocks")
@RequiredArgsConstructor
public class StockController {
    
    private final StockApiService stockApiService;
    private final KlineService klineService;
    
    // 获取印度股票列表
    @GetMapping("/india")
    public ResponseEntity<List<Stock>> getIndianStocks(
            @RequestParam(defaultValue = "100") int pageSize,
            @RequestParam(defaultValue = "1") int page) {
        try {
            return ResponseEntity.ok(stockApiService.getIndianStocks(pageSize, page));
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    
    // 获取股票K线
    @GetMapping("/{id}/kline")
    public ResponseEntity<List<Kline>> getKlineData(
            @PathVariable int id,
            @RequestParam(defaultValue = "PT15M") String interval) {
        return ResponseEntity.ok(klineService.getKlines(id, interval));
    }
}

五、企业级增强功能

1. 缓存配置

java 复制代码
@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager("stocks", "kline");
    }
}

2. 限流与熔断

java 复制代码
@Service
public class StockService {
    
    @RateLimiter(name = "stockApiRateLimit", fallbackMethod = "fallback")
    @CircuitBreaker(name = "stockApi", fallbackMethod = "fallback")
    @Retry(name = "stockApiRetry")
    public List<Stock> getStocksWithResilience() {
        // 调用API
    }
    
    public List<Stock> fallback(Throwable t) {
        // 返回缓存数据或默认值
        return Collections.emptyList();
    }
}

3. 定时任务

java 复制代码
@Service
public class StockDataSyncService {
    
    private final StockApiService stockApiService;
    private final StockWebSocketController webSocketController;
    
    @Scheduled(fixedRate = 60000) // 每分钟同步一次
    public void syncStockData() {
        List<Stock> stocks = stockApiService.getIndianStocks(100, 1);
        // 处理并存储数据
        stocks.forEach(webSocketController::broadcastStockUpdate);
    }
}

六、安全配置

1. API密钥保护

java 复制代码
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/api/**").authenticated()
            .and()
            .httpBasic();
    }
}

2. 敏感配置加密

java 复制代码
@Bean
public static PropertySourcesPlaceholderConfigurer properties() {
    PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
    configurer.setLocation(new ClassPathResource("application.yml"));
    configurer.setIgnoreUnresolvablePlaceholders(true);
    
    // 使用Jasypt加密
    EnvironmentStringPBEConfig config = new EnvironmentStringPBEConfig();
    config.setPasswordEnvName("ENCRYPTION_PASSWORD");
    
    StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
    encryptor.setConfig(config);
    
    configurer.setPropertySources(
        new EncryptablePropertiesPropertySource(
            "encryptedProps", 
            encryptor,
            new PropertiesPropertySource("appProps", loadProperties())
        )
    );
    return configurer;
}

七、部署与监控

1. Dockerfile

dockerfile 复制代码
FROM openjdk:17-jdk-slim
VOLUME /tmp
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

2. Prometheus监控配置

yml 复制代码
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  metrics:
    export:
      prometheus:
        enabled: true

八、项目结构

bash 复制代码
src/
├── main/
│   ├── java/
│   │   └── com/
│   │       └── example/
│   │           ├── config/         # 配置类
│   │           ├── controller/    # REST控制器
│   │           ├── service/       # 业务服务
│   │           ├── model/          # 数据模型
│   │           ├── websocket/     # WebSocket相关
│   │           └── Application.java # 启动类
│   └── resources/
│       ├── application.yml        # 配置文件
│       └── static/                # 静态资源
└── test/                          # 测试代码

九、总结

通过本文我们实现了:

  1. Spring Boot基础配置与依赖管理
  2. 印度股票数据的REST API封装
  3. WebSocket实时数据推送
  4. 企业级功能增强(缓存、限流、熔断)
  5. 安全配置与生产部署方案

关键优势:

  • 模块化设计:清晰的分层架构
  • 实时性:WebSocket提供毫秒级数据更新
  • 弹性架构:Resilience4j保障系统稳定性
  • 生产就绪:包含监控、安全、容器化部署方案

完整示例代码:[GitHub仓库链接] 官方API文档:[stocktv.top]

通过此方案,企业可快速构建高可用、实时的印度股票数据服务,满足量化交易、行情展示等多种业务场景需求。

相关推荐
River4165 小时前
Javer 学 c++(九):结构体篇
c++·后端
日月卿_宇5 小时前
分布式事务
java·后端
齐 飞5 小时前
XXL-JOB快速入门
spring boot·后端·spring cloud
MacroZheng5 小时前
别再用 BeanUtils 了,这款 PO VO DTO 转换神器不香么?
java·spring boot·后端
AAA修煤气灶刘哥5 小时前
从 “库存飞了” 到 “事务稳了”:后端 er 必通的分布式事务 & Seata 闯关指南
java·后端·spring cloud
一刻缱绻5 小时前
iptables MASQUERADE规则对本地回环地址的影响分析
后端·tcp/ip
码事漫谈6 小时前
Hello World背后的秘密:详解 C++ 编译链接模型
后端
brzhang6 小时前
现在有一种深深的感觉,觉大多数情况下,多 Agent 不如单 Agent 好
前端·后端·架构
码事漫谈6 小时前
C++ 类型系统浅析:值类别与引用类型
后端