|
|
@@ -3,8 +3,10 @@ package com.yingpai.gupiao.service.impl;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.yingpai.gupiao.domain.po.StockInfo;
|
|
|
import com.yingpai.gupiao.domain.po.StockPool;
|
|
|
import com.yingpai.gupiao.domain.vo.StockPoolVO;
|
|
|
+import com.yingpai.gupiao.mapper.StockInfoMapper;
|
|
|
import com.yingpai.gupiao.mapper.StockPoolMapper;
|
|
|
import com.yingpai.gupiao.service.StockPoolService;
|
|
|
import com.yingpai.gupiao.util.StockUtils;
|
|
|
@@ -19,6 +21,8 @@ import java.net.http.HttpClient;
|
|
|
import java.net.http.HttpRequest;
|
|
|
import java.net.http.HttpResponse;
|
|
|
import java.time.Duration;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.LocalDateTime;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.HashMap;
|
|
|
@@ -35,6 +39,7 @@ import java.util.stream.Collectors;
|
|
|
public class StockPoolServiceImpl implements StockPoolService {
|
|
|
|
|
|
private final StockPoolMapper stockPoolMapper;
|
|
|
+ private final StockInfoMapper stockInfoMapper;
|
|
|
|
|
|
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
|
|
|
@@ -171,4 +176,135 @@ public class StockPoolServiceImpl implements StockPoolService {
|
|
|
String currentPrice;
|
|
|
String changePercent;
|
|
|
}
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<StockPoolVO> getPoolList(Integer poolType) {
|
|
|
+ LambdaQueryWrapper<StockPool> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(StockPool::getPoolType, poolType)
|
|
|
+ .eq(StockPool::getStatus, 1)
|
|
|
+ .orderByDesc(StockPool::getAddDate);
|
|
|
+ List<StockPool> list = stockPoolMapper.selectList(wrapper);
|
|
|
+
|
|
|
+ // 获取实时行情
|
|
|
+ Map<String, QuoteData> quoteMap = fetchBatchQuotes(list);
|
|
|
+
|
|
|
+ List<StockPoolVO> result = new ArrayList<>();
|
|
|
+ for (StockPool stock : list) {
|
|
|
+ StockPoolVO.StockPoolVOBuilder builder = StockPoolVO.builder()
|
|
|
+ .code(stock.getStockCode())
|
|
|
+ .name(stock.getStockName())
|
|
|
+ .addDate(stock.getAddDate() != null ? stock.getAddDate().format(DATE_FORMATTER) : "");
|
|
|
+
|
|
|
+ // 填充实时行情
|
|
|
+ QuoteData quote = quoteMap.get(stock.getStockCode());
|
|
|
+ if (quote != null) {
|
|
|
+ builder.currentPrice(quote.currentPrice)
|
|
|
+ .changePercent(quote.changePercent);
|
|
|
+ }
|
|
|
+
|
|
|
+ result.add(builder.build());
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean addStock(String stockCode, Integer poolType) {
|
|
|
+ log.info("[添加股票到池] stockCode={}, poolType={}", stockCode, poolType);
|
|
|
+
|
|
|
+ // 查询股票信息
|
|
|
+ LambdaQueryWrapper<StockInfo> infoWrapper = new LambdaQueryWrapper<>();
|
|
|
+ infoWrapper.eq(StockInfo::getStockCode, stockCode);
|
|
|
+ StockInfo stockInfo = stockInfoMapper.selectOne(infoWrapper);
|
|
|
+
|
|
|
+ if (stockInfo == null) {
|
|
|
+ log.warn("[添加股票到池] 股票不存在: {}", stockCode);
|
|
|
+ throw new RuntimeException("股票代码不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否已存在
|
|
|
+ LambdaQueryWrapper<StockPool> existWrapper = new LambdaQueryWrapper<>();
|
|
|
+ existWrapper.eq(StockPool::getStockCode, stockCode)
|
|
|
+ .eq(StockPool::getPoolType, poolType)
|
|
|
+ .eq(StockPool::getStatus, 1);
|
|
|
+ if (stockPoolMapper.selectCount(existWrapper) > 0) {
|
|
|
+ log.warn("[添加股票到池] 股票已存在: {}", stockCode);
|
|
|
+ throw new RuntimeException("该股票已在池中");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取实时价格
|
|
|
+ BigDecimal currentPrice = fetchCurrentPrice(stockCode);
|
|
|
+
|
|
|
+ // 添加到池
|
|
|
+ StockPool pool = new StockPool();
|
|
|
+ pool.setStockCode(stockCode);
|
|
|
+ pool.setStockName(stockInfo.getStockName());
|
|
|
+ pool.setPoolType(poolType);
|
|
|
+ pool.setAddPrice(currentPrice);
|
|
|
+ pool.setAddDate(LocalDate.now());
|
|
|
+ pool.setStatus(1);
|
|
|
+ pool.setCreateTime(LocalDateTime.now());
|
|
|
+ pool.setUpdateTime(LocalDateTime.now());
|
|
|
+
|
|
|
+ return stockPoolMapper.insert(pool) > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取单只股票实时价格
|
|
|
+ */
|
|
|
+ private BigDecimal fetchCurrentPrice(String stockCode) {
|
|
|
+ try {
|
|
|
+ String secId = StockUtils.getAShareSecId(stockCode);
|
|
|
+ if (secId == null) {
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ String url = BATCH_QUOTE_URL + "?fltt=2&fields=f2&secids=" + secId;
|
|
|
+
|
|
|
+ HttpRequest request = HttpRequest.newBuilder()
|
|
|
+ .uri(URI.create(url))
|
|
|
+ .GET()
|
|
|
+ .build();
|
|
|
+
|
|
|
+ HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
|
+
|
|
|
+ if (response.statusCode() == 200) {
|
|
|
+ JsonNode root = objectMapper.readTree(response.body());
|
|
|
+ JsonNode data = root.get("data");
|
|
|
+
|
|
|
+ if (data != null && !data.isNull()) {
|
|
|
+ JsonNode diff = data.get("diff");
|
|
|
+ if (diff != null && diff.isArray() && diff.size() > 0) {
|
|
|
+ JsonNode item = diff.get(0);
|
|
|
+ if (item.has("f2") && !item.get("f2").isNull()) {
|
|
|
+ return new BigDecimal(item.get("f2").asText()).setScale(2, RoundingMode.HALF_UP);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[获取实时价格] 失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean deleteStock(String stockCode, Integer poolType) {
|
|
|
+ log.info("[从池中删除股票] stockCode={}, poolType={}", stockCode, poolType);
|
|
|
+
|
|
|
+ LambdaQueryWrapper<StockPool> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(StockPool::getStockCode, stockCode)
|
|
|
+ .eq(StockPool::getPoolType, poolType)
|
|
|
+ .eq(StockPool::getStatus, 1);
|
|
|
+
|
|
|
+ StockPool pool = stockPoolMapper.selectOne(wrapper);
|
|
|
+ if (pool == null) {
|
|
|
+ throw new RuntimeException("记录不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 软删除
|
|
|
+ pool.setStatus(0);
|
|
|
+ pool.setUpdateTime(LocalDateTime.now());
|
|
|
+ return stockPoolMapper.updateById(pool) > 0;
|
|
|
+ }
|
|
|
}
|