盖彗云 发表于 4 天前

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



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

1. 创建Spring Boot项目

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

[*]Spring Web
[*]Spring WebSocket
[*]Lombok
[*]Apache HttpClient
2. 配置依赖(pom.xml)

<dependencies>
   
    <dependency>
      <groupId>org.springframework.boot</groupId>
      spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      spring-boot-starter-websocket</artifactId>
    </dependency>
   
   
    <dependency>
      <groupId>org.projectlombok</groupId>
      lombok</artifactId>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      httpclient</artifactId>
      <version>4.5.13</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      jackson-databind</artifactId>
      <version>2.13.0</version>
    </dependency>
</dependencies>3. 配置API密钥

在application.yml中添加配置:
stocktv:
api:
    base-url: https://api.stocktv.top
    key: YOUR_API_KEY # 联系官方获取
    ws-url: wss://ws-api.stocktv.top/connect二、核心功能实现

1. 配置HTTP客户端

@Configuration
public class HttpClientConfig {
   
    @Bean
    public CloseableHttpClient httpClient() {
      return HttpClients.custom()
                .setConnectionManager(new PoolingHttpClientConnectionManager())
                .build();
    }
   
    @Bean
    public RestTemplate restTemplate() {
      return new RestTemplate();
    }
}2. 封装API服务

@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线数据服务

@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配置

@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服务端

@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控制器

@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. 股票控制器

@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. 缓存配置

@Configuration
@EnableCaching
public class CacheConfig {
   
    @Bean
    public CacheManager cacheManager() {
      return new ConcurrentMapCacheManager("stocks", "kline");
    }
}2. 限流与熔断

@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. 定时任务

@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密钥保护

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   
    @Override
    protected void configure(HttpSecurity http) throws Exception {
      http
            .authorizeRequests()
                .antMatchers("/api/**").authenticated()
            .and()
            .httpBasic();
    }
}2. 敏感配置加密

@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

FROM openjdk:17-jdk-slim
VOLUME /tmp
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]2. Prometheus监控配置

management:
endpoints:
    web:
      exposure:
      include: health,info,metrics,prometheus
metrics:
    export:
      prometheus:
      enabled: true八、项目结构

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

通过本文我们实现了:

[*]Spring Boot基础配置与依赖管理
[*]印度股票数据的REST API封装
[*]WebSocket实时数据推送
[*]企业级功能增强(缓存、限流、熔断)
[*]安全配置与生产部署方案
关键优势:

[*]模块化设计:清晰的分层架构
[*]实时性:WebSocket提供毫秒级数据更新
[*]弹性架构:Resilience4j保障系统稳定性
[*]生产就绪:包含监控、安全、容器化部署方案
完整示例代码:
官方API文档:
通过此方案,企业可快速构建高可用、实时的印度股票数据服务,满足量化交易、行情展示等多种业务场景需求。

来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除
页: [1]
查看完整版本: 使用Spring Boot对接印度股票市场API开发实践