Bläddra i källkod

联系我们功能添加

Zhangbw 2 månader sedan
förälder
incheckning
5dc6eeef19

+ 2 - 1
.claude/settings.local.json

@@ -9,7 +9,8 @@
       "Bash(netstat:*)",
       "Bash(netstat:*)",
       "Bash(taskkill:*)",
       "Bash(taskkill:*)",
       "Bash(curl:*)",
       "Bash(curl:*)",
-      "Bash(git restore:*)"
+      "Bash(git restore:*)",
+      "Bash(npm run dev:mp-weixin:*)"
     ]
     ]
   }
   }
 }
 }

+ 0 - 2
src/main/java/com/yingpai/gupiao/GupiaoApplication.java

@@ -11,8 +11,6 @@ import org.springframework.scheduling.annotation.EnableScheduling;
 public class GupiaoApplication {
 public class GupiaoApplication {
 
 
     public static void main(String[] args) {
     public static void main(String[] args) {
-        // 优先使用IPv4,解决某些网络环境下的连接问题
-        System.setProperty("java.net.preferIPv4Stack", "true");
         SpringApplication.run(GupiaoApplication.class, args);
         SpringApplication.run(GupiaoApplication.class, args);
     }
     }
 
 

+ 17 - 4
src/main/java/com/yingpai/gupiao/controller/StockPoolController.java

@@ -7,7 +7,6 @@ import com.yingpai.gupiao.mapper.UserMapper;
 import com.yingpai.gupiao.service.OrderService;
 import com.yingpai.gupiao.service.OrderService;
 import com.yingpai.gupiao.service.StockPoolService;
 import com.yingpai.gupiao.service.StockPoolService;
 import com.yingpai.gupiao.util.JwtUtil;
 import com.yingpai.gupiao.util.JwtUtil;
-import com.yingpai.gupiao.util.UserContext;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
@@ -34,14 +33,28 @@ public class StockPoolController {
      * @param poolType 池类型:1-超短池,2-强势池
      * @param poolType 池类型:1-超短池,2-强势池
      */
      */
     @GetMapping("/list")
     @GetMapping("/list")
-    public Result<List<StockPoolVO>> getPoolStocks(@RequestParam("poolType") Integer poolType) {
+    public Result<List<StockPoolVO>> getPoolStocks(
+            @RequestParam("poolType") Integer poolType,
+            @RequestHeader(value = "Authorization", required = false) String authorization) {
         if (poolType == null || (poolType != 1 && poolType != 2)) {
         if (poolType == null || (poolType != 1 && poolType != 2)) {
             return Result.error(400, "池类型无效,1=超短池,2=强势池");
             return Result.error(400, "池类型无效,1=超短池,2=强势池");
         }
         }
 
 
         // 超短池需要订阅权限检查
         // 超短池需要订阅权限检查
         if (poolType == 1) {
         if (poolType == 1) {
-            Long userId = UserContext.getUserId();
+            // 手动解析token获取userId(因为此路径不经过AuthInterceptor)
+            Long userId = null;
+            if (authorization != null && !authorization.isEmpty()) {
+                String token = authorization.startsWith("Bearer ") ? authorization.substring(7) : authorization;
+                try {
+                    if (jwtUtil.validateToken(token)) {
+                        userId = jwtUtil.getUserIdFromToken(token);
+                    }
+                } catch (Exception e) {
+                    log.error("解析token失败", e);
+                }
+            }
+
             if (userId == null) {
             if (userId == null) {
                 return Result.error(401, "未登录");
                 return Result.error(401, "未登录");
             }
             }
@@ -52,7 +65,7 @@ public class StockPoolController {
             }
             }
         }
         }
 
 
-        // 强势池只需要登录(已通过拦截器验证)
+        // 强势池公开访问,不需要登录
         List<StockPoolVO> stocks = stockPoolService.getPoolStocks(poolType);
         List<StockPoolVO> stocks = stockPoolService.getPoolStocks(poolType);
         return Result.success(stocks);
         return Result.success(stocks);
     }
     }

+ 44 - 10
src/main/java/com/yingpai/gupiao/controller/UserController.java

@@ -1,6 +1,7 @@
 package com.yingpai.gupiao.controller;
 package com.yingpai.gupiao.controller;
 
 
 import com.yingpai.gupiao.domain.dto.UpdateProfileDTO;
 import com.yingpai.gupiao.domain.dto.UpdateProfileDTO;
+import com.yingpai.gupiao.domain.dto.UserFeedbackDTO;
 import com.yingpai.gupiao.domain.vo.LoginVO;
 import com.yingpai.gupiao.domain.vo.LoginVO;
 import com.yingpai.gupiao.domain.vo.Result;
 import com.yingpai.gupiao.domain.vo.Result;
 import com.yingpai.gupiao.service.UserService;
 import com.yingpai.gupiao.service.UserService;
@@ -18,14 +19,14 @@ import org.springframework.web.bind.annotation.*;
 @RequiredArgsConstructor
 @RequiredArgsConstructor
 @RequestMapping("/v1/user")
 @RequestMapping("/v1/user")
 public class UserController {
 public class UserController {
-    
+
     private final UserService userService;
     private final UserService userService;
     private final JwtUtil jwtUtil;
     private final JwtUtil jwtUtil;
-    
+
     /**
     /**
      * 获取用户信息(通过token)
      * 获取用户信息(通过token)
      * 接口路径:GET /v1/user/info
      * 接口路径:GET /v1/user/info
-     * 
+     *
      * @param authorization 请求头中的token
      * @param authorization 请求头中的token
      * @return 用户完整信息
      * @return 用户完整信息
      */
      */
@@ -35,9 +36,9 @@ public class UserController {
             // 从token中获取用户ID
             // 从token中获取用户ID
             String token = authorization.replace("Bearer ", "");
             String token = authorization.replace("Bearer ", "");
             Long userId = jwtUtil.getUserIdFromToken(token);
             Long userId = jwtUtil.getUserIdFromToken(token);
-            
+
             log.info("获取用户信息,userId: {}", userId);
             log.info("获取用户信息,userId: {}", userId);
-            
+
             LoginVO.UserInfoVO userInfo = userService.getUserInfo(userId);
             LoginVO.UserInfoVO userInfo = userService.getUserInfo(userId);
             return Result.success(userInfo);
             return Result.success(userInfo);
         } catch (Exception e) {
         } catch (Exception e) {
@@ -45,12 +46,12 @@ public class UserController {
             return Result.error("获取用户信息失败:" + e.getMessage());
             return Result.error("获取用户信息失败:" + e.getMessage());
         }
         }
     }
     }
-    
+
     /**
     /**
      * 更新用户资料
      * 更新用户资料
      * 接口路径:PUT /v1/user/profile
      * 接口路径:PUT /v1/user/profile
-     * 
-     * @param authorization 请求头中的token
+     *
+     * @param authorization    请求头中的token
      * @param updateProfileDTO 更新资料DTO
      * @param updateProfileDTO 更新资料DTO
      * @return 更新后的用户信息
      * @return 更新后的用户信息
      */
      */
@@ -62,9 +63,9 @@ public class UserController {
             // 从token中获取用户ID
             // 从token中获取用户ID
             String token = authorization.replace("Bearer ", "");
             String token = authorization.replace("Bearer ", "");
             Long userId = jwtUtil.getUserIdFromToken(token);
             Long userId = jwtUtil.getUserIdFromToken(token);
-            
+
             log.info("更新用户资料,userId: {}, nickname: {}", userId, updateProfileDTO.getNickname());
             log.info("更新用户资料,userId: {}, nickname: {}", userId, updateProfileDTO.getNickname());
-            
+
             LoginVO.UserInfoVO userInfo = userService.updateProfile(userId, updateProfileDTO);
             LoginVO.UserInfoVO userInfo = userService.updateProfile(userId, updateProfileDTO);
             return Result.success(userInfo);
             return Result.success(userInfo);
         } catch (Exception e) {
         } catch (Exception e) {
@@ -72,4 +73,37 @@ public class UserController {
             return Result.error("更新失败:" + e.getMessage());
             return Result.error("更新失败:" + e.getMessage());
         }
         }
     }
     }
+
+    /**
+     * 提交反馈
+     * 接口路径:POST /v1/user/feedback/submit
+     *
+     * @param authorization 请求头中的token
+     * @param feedbackDTO   反馈DTO
+     */
+    @PostMapping("/feedback/submit")
+    public Result<Boolean> submitFeedback(
+            @RequestHeader("Authorization") String authorization,
+            @RequestBody UserFeedbackDTO feedbackDTO) {
+        try {
+            log.info("=== 收到提交反馈请求 ===");
+            log.info("Authorization: {}", authorization);
+            log.info("反馈内容: {}", feedbackDTO.getContent());
+            log.info("图片数量: {}", feedbackDTO.getImages() != null ? feedbackDTO.getImages().split(",").length : 0);
+
+            // 从token中获取用户ID
+            String token = authorization.replace("Bearer ", "");
+            Long userId = jwtUtil.getUserIdFromToken(token);
+
+            log.info("提交反馈,userId: {}, content: {}", userId, feedbackDTO.getContent());
+
+            boolean success = userService.submitFeedback(userId, feedbackDTO);
+            log.info("提交反馈结果: {}", success);
+            return Result.success(success);
+        } catch (Exception e) {
+            log.error("提交反馈失败", e);
+            return Result.error("提交失败:" + e.getMessage());
+        }
+    }
+
 }
 }

+ 16 - 0
src/main/java/com/yingpai/gupiao/domain/dto/UserFeedbackDTO.java

@@ -0,0 +1,16 @@
+package com.yingpai.gupiao.domain.dto;
+
+import cn.hutool.json.JSONObject;
+import lombok.Data;
+
+
+/**
+ * 用户反馈DTO
+ */
+@Data
+public class UserFeedbackDTO {
+
+    private String content;
+
+    private String images;
+}

+ 44 - 0
src/main/java/com/yingpai/gupiao/domain/po/UserFeedback.java

@@ -0,0 +1,44 @@
+package com.yingpai.gupiao.domain.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName("user_feedback")
+public class UserFeedback {
+    /**
+     * 反馈ID,主键自增
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 反馈内容
+     */
+    private String content;
+
+    /**
+     * 图片URL列表,多个图片用英文逗号分隔
+     */
+    private String images;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+}

+ 40 - 0
src/main/java/com/yingpai/gupiao/domain/vo/UserFeedbackVO.java

@@ -0,0 +1,40 @@
+package com.yingpai.gupiao.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+/**
+ * 用户反馈VO - 返回给前端的数据
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class UserFeedbackVO {
+
+    /**
+     * 反馈ID
+     */
+    private Long id;
+
+    /**
+     * 反馈内容
+     */
+    private String content;
+
+    /**
+     * 图片URL列表,逗号分隔
+     */
+    private String images;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+}

+ 9 - 0
src/main/java/com/yingpai/gupiao/mapper/UserFeedbackMapper.java

@@ -0,0 +1,9 @@
+package com.yingpai.gupiao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yingpai.gupiao.domain.po.UserFeedback;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface UserFeedbackMapper extends BaseMapper<UserFeedback> {
+}

+ 9 - 0
src/main/java/com/yingpai/gupiao/service/UserService.java

@@ -1,6 +1,7 @@
 package com.yingpai.gupiao.service;
 package com.yingpai.gupiao.service;
 
 
 import com.yingpai.gupiao.domain.dto.UpdateProfileDTO;
 import com.yingpai.gupiao.domain.dto.UpdateProfileDTO;
+import com.yingpai.gupiao.domain.dto.UserFeedbackDTO;
 import com.yingpai.gupiao.domain.vo.LoginVO;
 import com.yingpai.gupiao.domain.vo.LoginVO;
 
 
 /**
 /**
@@ -22,4 +23,12 @@ public interface UserService {
      * @return 更新后的用户信息VO
      * @return 更新后的用户信息VO
      */
      */
     LoginVO.UserInfoVO updateProfile(Long userId, UpdateProfileDTO updateProfileDTO);
     LoginVO.UserInfoVO updateProfile(Long userId, UpdateProfileDTO updateProfileDTO);
+
+    /**
+     * 提交反馈
+     * @param userId 用户ID
+     * @param feedbackDTO 反馈DTO
+     * @return 是否成功
+     */
+    boolean submitFeedback(Long userId, UserFeedbackDTO feedbackDTO);
 }
 }

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

@@ -211,6 +211,7 @@ public class StockDataServiceImpl implements StockDataService {
                                 allPrices.add(price);
                                 allPrices.add(price);
                             } catch (NumberFormatException e) {
                             } catch (NumberFormatException e) {
                                 // 忽略解析失败的数据点
                                 // 忽略解析失败的数据点
+                                log.debug("[趋势数据] 解析价格失败: {}", parts[1]);
                             }
                             }
                         }
                         }
                     }
                     }

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

@@ -1,8 +1,11 @@
 package com.yingpai.gupiao.service.impl;
 package com.yingpai.gupiao.service.impl;
 
 
 import com.yingpai.gupiao.domain.dto.UpdateProfileDTO;
 import com.yingpai.gupiao.domain.dto.UpdateProfileDTO;
+import com.yingpai.gupiao.domain.dto.UserFeedbackDTO;
 import com.yingpai.gupiao.domain.po.User;
 import com.yingpai.gupiao.domain.po.User;
+import com.yingpai.gupiao.domain.po.UserFeedback;
 import com.yingpai.gupiao.domain.vo.LoginVO;
 import com.yingpai.gupiao.domain.vo.LoginVO;
+import com.yingpai.gupiao.mapper.UserFeedbackMapper;
 import com.yingpai.gupiao.mapper.UserMapper;
 import com.yingpai.gupiao.mapper.UserMapper;
 import com.yingpai.gupiao.service.UserService;
 import com.yingpai.gupiao.service.UserService;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -20,6 +23,8 @@ import java.time.LocalDateTime;
 public class UserServiceImpl implements UserService {
 public class UserServiceImpl implements UserService {
     
     
     private final UserMapper userMapper;
     private final UserMapper userMapper;
+
+    private final UserFeedbackMapper userFeedbackMapper;
     
     
     /**
     /**
      * 获取用户信息
      * 获取用户信息
@@ -89,4 +94,38 @@ public class UserServiceImpl implements UserService {
         // 返回更新后的用户信息
         // 返回更新后的用户信息
         return getUserInfo(userId);
         return getUserInfo(userId);
     }
     }
+
+    /**
+     * 提交反馈
+     * @param userId 用户ID
+     * @param feedbackDTO 反馈DTO
+     * @return 是否成功
+     */
+    @Override
+    public boolean submitFeedback(Long userId, UserFeedbackDTO feedbackDTO) {
+        User user = userMapper.selectById(userId);
+        if (user == null) {
+            log.warn("用户不存在,无法提交反馈,userId: {}", userId);
+            return false;
+        }
+
+        // 验证图片数量
+        if (feedbackDTO.getImages() != null && !feedbackDTO.getImages().isEmpty()) {
+            String[] imageArray = feedbackDTO.getImages().split(",");
+            if (imageArray.length > 6) {
+                throw new IllegalArgumentException("图片数量不能超过6张");
+            }
+        }
+
+        log.info("用户提交反馈,userId: {}, 反馈内容: {}", userId, feedbackDTO.getContent());
+        UserFeedback feedback = UserFeedback.builder()
+                .userId(userId)
+                .content(feedbackDTO.getContent())
+                .images(feedbackDTO.getImages())
+                .createTime(LocalDateTime.now())
+                .build();
+        return userFeedbackMapper.insert(feedback) > 0;
+    }
+
+
 }
 }

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

@@ -78,6 +78,7 @@ public class UserStockServiceImpl implements UserStockService {
                        .changePercent(quote.changePercent);
                        .changePercent(quote.changePercent);
 
 
                 // 计算收益率
                 // 计算收益率
+                // 收益率 = (当前价格 - 买入价格) / 买入价格 × 100%
                 try {
                 try {
                     BigDecimal addPrice = stock.getAddPrice();
                     BigDecimal addPrice = stock.getAddPrice();
                     BigDecimal currentPrice = new BigDecimal(quote.currentPrice);
                     BigDecimal currentPrice = new BigDecimal(quote.currentPrice);

+ 0 - 0
src/main/resources/mapper/StockPoolHistoryMapper.xml → src/main/resources/mapper/UserFeedbackMapper.xml