Quellcode durchsuchen

超短池管理修正

Zhangbw vor 3 Monaten
Ursprung
Commit
bf2ecf1ec3

+ 99 - 0
src/main/java/com/yingpai/gupiao/controller/StockPoolController.java

@@ -1,22 +1,30 @@
 package com.yingpai.gupiao.controller;
 
+import com.yingpai.gupiao.domain.po.User;
 import com.yingpai.gupiao.domain.vo.Result;
 import com.yingpai.gupiao.domain.vo.StockPoolVO;
+import com.yingpai.gupiao.mapper.UserMapper;
 import com.yingpai.gupiao.service.StockPoolService;
+import com.yingpai.gupiao.util.JwtUtil;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 股票池控制器(小程序端)
  */
+@Slf4j
 @RestController
 @RequestMapping("/v1/stock/pool")
 @RequiredArgsConstructor
 public class StockPoolController {
     
     private final StockPoolService stockPoolService;
+    private final UserMapper userMapper;
+    private final JwtUtil jwtUtil;
     
     /**
      * 获取股票池列表
@@ -31,4 +39,95 @@ public class StockPoolController {
         List<StockPoolVO> stocks = stockPoolService.getPoolStocks(poolType);
         return Result.success(stocks);
     }
+    
+    /**
+     * 获取股票池管理列表(管理员专属)
+     */
+    @GetMapping("/admin/list")
+    public Result<List<StockPoolVO>> getPoolList(
+            @RequestHeader("Authorization") String authorization,
+            @RequestParam("poolType") Integer poolType) {
+        
+        // 验证管理员权限
+        if (!checkAdminPermission(authorization)) {
+            return Result.error(403, "无权限访问");
+        }
+        
+        List<StockPoolVO> list = stockPoolService.getPoolList(poolType);
+        return Result.success(list);
+    }
+    
+    /**
+     * 添加股票到池(管理员专属)
+     */
+    @PostMapping("/admin/add")
+    public Result<Void> addStock(
+            @RequestHeader("Authorization") String authorization,
+            @RequestBody Map<String, Object> params) {
+        
+        // 验证管理员权限
+        if (!checkAdminPermission(authorization)) {
+            return Result.error(403, "无权限访问");
+        }
+        
+        String stockCode = (String) params.get("stockCode");
+        Integer poolType = (Integer) params.get("poolType");
+        
+        if (stockCode == null || stockCode.isEmpty()) {
+            return Result.error(400, "股票代码不能为空");
+        }
+        if (poolType == null || (poolType != 1 && poolType != 2)) {
+            return Result.error(400, "池类型无效");
+        }
+        
+        try {
+            stockPoolService.addStock(stockCode, poolType);
+            return Result.success(null);
+        } catch (Exception e) {
+            return Result.error(500, e.getMessage());
+        }
+    }
+    
+    /**
+     * 从池中删除股票(管理员专属)
+     */
+    @PostMapping("/admin/delete")
+    public Result<Void> deleteStock(
+            @RequestHeader("Authorization") String authorization,
+            @RequestBody Map<String, Object> params) {
+        
+        // 验证管理员权限
+        if (!checkAdminPermission(authorization)) {
+            return Result.error(403, "无权限访问");
+        }
+        
+        String stockCode = (String) params.get("stockCode");
+        Integer poolType = params.get("poolType") != null ? (Integer) params.get("poolType") : 1;
+        
+        if (stockCode == null || stockCode.isEmpty()) {
+            return Result.error(400, "股票代码不能为空");
+        }
+        
+        try {
+            stockPoolService.deleteStock(stockCode, poolType);
+            return Result.success(null);
+        } catch (Exception e) {
+            return Result.error(500, e.getMessage());
+        }
+    }
+    
+    /**
+     * 检查管理员权限
+     */
+    private boolean checkAdminPermission(String authorization) {
+        try {
+            String token = authorization.replace("Bearer ", "");
+            Long userId = jwtUtil.getUserIdFromToken(token);
+            User user = userMapper.selectById(userId);
+            return user != null && user.getStatus() != null && user.getStatus() == 2;
+        } catch (Exception e) {
+            log.error("检查管理员权限失败", e);
+            return false;
+        }
+    }
 }

+ 5 - 0
src/main/java/com/yingpai/gupiao/domain/vo/LoginVO.java

@@ -52,5 +52,10 @@ public class LoginVO {
          * 手机号(脱敏)
          */
         private String phone;
+        
+        /**
+         * 用户状态:0-正常,1-禁用,2-管理员
+         */
+        private Integer status;
     }
 }

+ 23 - 0
src/main/java/com/yingpai/gupiao/service/StockPoolService.java

@@ -15,4 +15,27 @@ public interface StockPoolService {
      * @return 股票列表
      */
     List<StockPoolVO> getPoolStocks(Integer poolType);
+    
+    /**
+     * 获取股票池管理列表(包含实时行情)
+     * @param poolType 池类型
+     * @return 股票池记录列表(含实时行情)
+     */
+    List<StockPoolVO> getPoolList(Integer poolType);
+    
+    /**
+     * 添加股票到池
+     * @param stockCode 股票代码
+     * @param poolType 池类型
+     * @return 是否成功
+     */
+    boolean addStock(String stockCode, Integer poolType);
+    
+    /**
+     * 从池中删除股票
+     * @param stockCode 股票代码
+     * @param poolType 池类型
+     * @return 是否成功
+     */
+    boolean deleteStock(String stockCode, Integer poolType);
 }

+ 136 - 0
src/main/java/com/yingpai/gupiao/service/impl/StockPoolServiceImpl.java

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

+ 1 - 0
src/main/java/com/yingpai/gupiao/service/impl/UserServiceImpl.java

@@ -44,6 +44,7 @@ public class UserServiceImpl implements UserService {
                 .nickname(user.getNickname())
                 .avatar(user.getAvatar())
                 .phone(maskedPhone)
+                .status(user.getStatus())
                 .build();
     }