支付功能+bugfix

Change-Id: I8407d4dcd4ec19b53ac4934813ed9af380642d98
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostCenterController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostCenterController.java
index e05e55e..9df8f03 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostCenterController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/controller/PostCenterController.java
@@ -430,7 +430,11 @@
         int result = postService.insertPost(post);
         
         if (result > 0) {
-            return success("帖子发布成功,等待管理员审核");
+            // 返回新创建的帖子信息,包含postId
+            Map<String, Object> data = new HashMap<>();
+            data.put("postId", post.getPostId());
+            data.put("message", "帖子发布成功,等待管理员审核");
+            return success(data);
         } else {
             return error("帖子发布失败");
         }
@@ -593,20 +597,38 @@
         }
         
         try {
-            // 级联删除相关数据
-            // 1. 删除帖子评论
-            postCommentService.deletePostCommentByPostId(postId);
-            
-            // 2. 删除帖子收藏记录
-            postFavoriteService.deletePostFavoriteByPostId(postId);
-            
-            // 3. 删除帖子本身
-        int result = postService.deletePostById(postId);
-        
-        if (result > 0) {
-            return success("帖子删除成功");
-        } else {
-            return error("帖子删除失败");
+            // 如果是草稿帖子(状态为待审核"0"),直接删除,无需级联删除
+            if ("0".equals(existingPost.getStatus())) {
+                // 删除相关支付记录(如果有)
+                postPaymentMapper.deletePostPaymentByPostId(postId);
+                
+                // 直接删除帖子
+                int result = postService.deletePostById(postId);
+                
+                if (result > 0) {
+                    return success("草稿帖子删除成功");
+                } else {
+                    return error("草稿帖子删除失败");
+                }
+            } else {
+                // 对于已发布的帖子,进行级联删除
+                // 1. 删除帖子评论
+                postCommentService.deletePostCommentByPostId(postId);
+                
+                // 2. 删除帖子收藏记录
+                postFavoriteService.deletePostFavoriteByPostId(postId);
+                
+                // 3. 删除支付记录
+                postPaymentMapper.deletePostPaymentByPostId(postId);
+                
+                // 4. 删除帖子本身
+                int result = postService.deletePostById(postId);
+                
+                if (result > 0) {
+                    return success("帖子删除成功");
+                } else {
+                    return error("帖子删除失败");
+                }
             }
         } catch (Exception e) {
             return error("删除失败:" + e.getMessage());
@@ -883,6 +905,12 @@
             return error("只能为自己的帖子购买推广");
         }
         
+        // 检查该帖子是否已有有效的推广支付记录
+        PostPayment existingPayment = postPaymentMapper.selectLatestPaymentByPostId(request.getPostId());
+        if (existingPayment != null && "paid".equals(existingPayment.getPaymentStatus())) {
+            return error("该帖子已购买推广服务");
+        }
+        
         // 创建支付记录
         PostPayment payment = new PostPayment();
         payment.setPostId(request.getPostId());
@@ -979,19 +1007,23 @@
         payment.setPaymentTime(new Date());
         postPaymentMapper.updatePostPayment(payment);
         
-        // 更新帖子的推广计划ID
-        Post updatePost = new Post();
-        updatePost.setPostId(payment.getPostId());
-        updatePost.setPromotionPlanId(payment.getPlanId());
-        updatePost.setUpdateBy(getUsername());
-        
-        int result = postService.updatePost(updatePost);
-        
-        if (result > 0) {
-            return success("支付成功,推广已生效");
+        // 更新帖子的推广计划ID(不管是新帖子还是现有帖子都要更新)
+        if (payment.getPostId() != null && payment.getPostId() > 0) {
+            Post updatePost = new Post();
+            updatePost.setPostId(payment.getPostId());
+            updatePost.setPromotionPlanId(payment.getPlanId());
+            updatePost.setUpdateBy(getUsername());
+            
+            int result = postService.updatePost(updatePost);
+            
+            if (result <= 0) {
+                return error("支付确认失败:无法更新帖子推广信息");
+            }
         } else {
-            return error("支付确认失败");
+            return error("支付记录中的帖子ID无效");
         }
+        
+        return success("支付成功,推广已生效");
     }
 
     /**
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostPaymentMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostPaymentMapper.java
index 538338c..fe39d3f 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostPaymentMapper.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/post/mapper/PostPaymentMapper.java
@@ -73,4 +73,12 @@
      * @return 结果
      */
     public int deletePostPaymentByIds(Long[] paymentIds);
+
+    /**
+     * 根据帖子ID删除支付记录
+     * 
+     * @param postId 帖子ID
+     * @return 结果
+     */
+    public int deletePostPaymentByPostId(Long postId);
 } 
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/mapper/post/PostPaymentMapper.xml b/ruoyi-admin/src/main/resources/mapper/post/PostPaymentMapper.xml
index c12dc2e..5c93b35 100644
--- a/ruoyi-admin/src/main/resources/mapper/post/PostPaymentMapper.xml
+++ b/ruoyi-admin/src/main/resources/mapper/post/PostPaymentMapper.xml
@@ -101,4 +101,8 @@
         </foreach>
     </delete>
     
+    <delete id="deletePostPaymentByPostId" parameterType="Long">
+        delete from post_payment where post_id = #{postId}
+    </delete>
+    
 </mapper> 
\ No newline at end of file
diff --git a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostCenterControllerTest.java b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostCenterControllerTest.java
index 880b0fc..ba1400d 100644
--- a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostCenterControllerTest.java
+++ b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/post/PostCenterControllerTest.java
@@ -459,25 +459,6 @@
     }
 
     /**
-     * 测试取消支付
-     */
-    @Test
-    void testCancelPayment() throws Exception {
-        PostPayment payment = new PostPayment();
-        payment.setPaymentId(1L);
-        payment.setUserId(1L);
-        payment.setPaymentStatus("pending");
-
-        when(postPaymentMapper.selectPostPaymentById(1L)).thenReturn(payment);
-        when(postPaymentMapper.updatePostPayment(any())).thenReturn(1);
-
-        mockMvc.perform(post("/post-center/payment/cancel/{paymentId}", 1L))
-                .andExpect(status().isOk());
-
-        verify(postPaymentMapper, times(1)).updatePostPayment(any());
-    }
-
-    /**
      * 测试获取推广状态
      */
     @Test
@@ -534,4 +515,399 @@
                 .param("filename", "test.jpg"))
                 .andExpect(status().isOk());
     }
+
+    /**
+     * 测试创建现有帖子的支付记录
+     */
+    @Test
+    void testCreatePaymentForExistingPost() throws Exception {
+        Post post = new Post();
+        post.setPostId(1L);
+        post.setAuthorId(1L);
+
+        when(postService.selectPostById(1L)).thenReturn(post);
+        when(postPaymentMapper.insertPostPayment(any())).thenReturn(1);
+
+        String paymentData = "{\"postId\":1,\"planId\":1,\"amount\":50.0}";
+
+        mockMvc.perform(post("/post-center/payment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(paymentData))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(1L);
+        verify(postPaymentMapper, times(1)).insertPostPayment(any());
+    }
+
+    /**
+     * 测试确认现有帖子的支付
+     */
+    @Test
+    void testConfirmPaymentForExistingPost() throws Exception {
+        PostPayment payment = new PostPayment();
+        payment.setPaymentId(1L);
+        payment.setPostId(1L); // 现有帖子
+        payment.setUserId(1L);
+        payment.setPlanId(1L);
+        payment.setPaymentStatus("pending");
+
+        when(postPaymentMapper.selectPostPaymentById(1L)).thenReturn(payment);
+        when(postPaymentMapper.updatePostPayment(any())).thenReturn(1);
+        when(postService.updatePost(any())).thenReturn(1);
+
+        mockMvc.perform(post("/post-center/payment/confirm/{paymentId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postPaymentMapper, times(1)).selectPostPaymentById(1L);
+        verify(postPaymentMapper, times(1)).updatePostPayment(any());
+        verify(postService, times(1)).updatePost(any());
+    }
+
+    /**
+     * 测试支付记录不存在的情况
+     */
+    @Test
+    void testConfirmNonExistentPayment() throws Exception {
+        when(postPaymentMapper.selectPostPaymentById(999L)).thenReturn(null);
+
+        mockMvc.perform(post("/post-center/payment/confirm/{paymentId}", 999L))
+                .andExpect(status().isOk());
+
+        verify(postPaymentMapper, times(1)).selectPostPaymentById(999L);
+        verify(postPaymentMapper, never()).updatePostPayment(any());
+    }
+
+    /**
+     * 测试支付状态异常的情况
+     */
+    @Test
+    void testConfirmPaymentWithInvalidStatus() throws Exception {
+        PostPayment payment = new PostPayment();
+        payment.setPaymentId(1L);
+        payment.setPostId(1L);
+        payment.setUserId(1L);
+        payment.setPlanId(1L);
+        payment.setPaymentStatus("paid"); // 已支付状态
+
+        when(postPaymentMapper.selectPostPaymentById(1L)).thenReturn(payment);
+
+        mockMvc.perform(post("/post-center/payment/confirm/{paymentId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postPaymentMapper, times(1)).selectPostPaymentById(1L);
+        verify(postPaymentMapper, never()).updatePostPayment(any());
+    }
+
+    /**
+     * 测试取消支付
+     */
+    @Test
+    void testCancelPayment() throws Exception {
+        PostPayment payment = new PostPayment();
+        payment.setPaymentId(1L);
+        payment.setPostId(123L); // 使用有效的帖子ID
+        payment.setUserId(1L);
+        payment.setPlanId(1L);
+        payment.setPaymentStatus("pending");
+
+        when(postPaymentMapper.selectPostPaymentById(1L)).thenReturn(payment);
+        when(postPaymentMapper.updatePostPayment(any())).thenReturn(1);
+
+        mockMvc.perform(post("/post-center/payment/cancel/{paymentId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postPaymentMapper, times(1)).selectPostPaymentById(1L);
+        verify(postPaymentMapper, times(1)).updatePostPayment(any());
+    }
+
+    /**
+     * 测试用户权限检查(只能操作自己的支付记录)
+     */
+    @Test
+    void testPaymentUserPermission() throws Exception {
+        PostPayment payment = new PostPayment();
+        payment.setPaymentId(1L);
+        payment.setPostId(1L);
+        payment.setUserId(2L); // 不同的用户ID
+        payment.setPlanId(1L);
+        payment.setPaymentStatus("pending");
+
+        when(postPaymentMapper.selectPostPaymentById(1L)).thenReturn(payment);
+
+        mockMvc.perform(post("/post-center/payment/confirm/{paymentId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postPaymentMapper, times(1)).selectPostPaymentById(1L);
+        verify(postPaymentMapper, never()).updatePostPayment(any());
+    }
+
+    /**
+     * 测试新推广支付流程:发布帖子返回postId
+     */
+    @Test
+    void testPublishPostWithReturnPostId() throws Exception {
+        when(postService.insertPost(any())).thenReturn(1);
+
+        String postData = "{\"title\":\"新帖子\",\"content\":\"帖子内容\",\"summary\":\"帖子摘要\"}";
+
+        mockMvc.perform(post("/post-center/publish")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(postData))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).insertPost(any(Post.class));
+    }
+
+    /**
+     * 测试创建支付记录时检查帖子存在性
+     */
+    @Test
+    void testCreatePaymentWithValidPost() throws Exception {
+        Post post = new Post();
+        post.setPostId(123L);
+        post.setAuthorId(1L);
+
+        when(postService.selectPostById(123L)).thenReturn(post);
+        when(postPaymentMapper.selectLatestPaymentByPostId(123L)).thenReturn(null);
+        when(postPaymentMapper.insertPostPayment(any())).thenReturn(1);
+
+        String paymentData = "{\"postId\":123,\"planId\":1,\"amount\":50.0}";
+
+        mockMvc.perform(post("/post-center/payment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(paymentData))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(123L);
+        verify(postPaymentMapper, times(1)).insertPostPayment(any());
+    }
+
+    /**
+     * 测试重复购买推广的检查
+     */
+    @Test
+    void testCreatePaymentWithExistingPromotion() throws Exception {
+        Post post = new Post();
+        post.setPostId(123L);
+        post.setAuthorId(1L);
+
+        PostPayment existingPayment = new PostPayment();
+        existingPayment.setPaymentStatus("paid");
+
+        when(postService.selectPostById(123L)).thenReturn(post);
+        when(postPaymentMapper.selectLatestPaymentByPostId(123L)).thenReturn(existingPayment);
+
+        String paymentData = "{\"postId\":123,\"planId\":1,\"amount\":50.0}";
+
+        mockMvc.perform(post("/post-center/payment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(paymentData))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(123L);
+        verify(postPaymentMapper, never()).insertPostPayment(any());
+    }
+
+    /**
+     * 测试支付确认时更新帖子推广信息
+     */
+    @Test
+    void testConfirmPaymentUpdatePost() throws Exception {
+        PostPayment payment = new PostPayment();
+        payment.setPaymentId(1L);
+        payment.setPostId(123L); // 有效的帖子ID
+        payment.setUserId(1L);
+        payment.setPlanId(1L);
+        payment.setPaymentStatus("pending");
+
+        when(postPaymentMapper.selectPostPaymentById(1L)).thenReturn(payment);
+        when(postPaymentMapper.updatePostPayment(any())).thenReturn(1);
+        when(postService.updatePost(any())).thenReturn(1);
+
+        mockMvc.perform(post("/post-center/payment/confirm/{paymentId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postPaymentMapper, times(1)).selectPostPaymentById(1L);
+        verify(postPaymentMapper, times(1)).updatePostPayment(any());
+        verify(postService, times(1)).updatePost(any());
+    }
+
+    /**
+     * 测试支付确认失败(无效的帖子ID)
+     */
+    @Test
+    void testConfirmPaymentWithInvalidPostId() throws Exception {
+        PostPayment payment = new PostPayment();
+        payment.setPaymentId(1L);
+        payment.setPostId(0L); // 无效的帖子ID
+        payment.setUserId(1L);
+        payment.setPlanId(1L);
+        payment.setPaymentStatus("pending");
+
+        when(postPaymentMapper.selectPostPaymentById(1L)).thenReturn(payment);
+        when(postPaymentMapper.updatePostPayment(any())).thenReturn(1);
+
+        mockMvc.perform(post("/post-center/payment/confirm/{paymentId}", 1L))
+                .andExpect(status().isOk());
+
+        verify(postPaymentMapper, times(1)).selectPostPaymentById(1L);
+        verify(postPaymentMapper, times(1)).updatePostPayment(any());
+        verify(postService, never()).updatePost(any());
+    }
+
+    /**
+     * 测试创建支付时帖子不存在
+     */
+    @Test
+    void testCreatePaymentWithNonExistentPost() throws Exception {
+        when(postService.selectPostById(999L)).thenReturn(null);
+
+        String paymentData = "{\"postId\":999,\"planId\":1,\"amount\":50.0}";
+
+        mockMvc.perform(post("/post-center/payment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(paymentData))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(999L);
+        verify(postPaymentMapper, never()).insertPostPayment(any());
+    }
+
+    /**
+     * 测试创建支付时帖子不属于当前用户
+     */
+    @Test
+    void testCreatePaymentWithOtherUserPost() throws Exception {
+        Post post = new Post();
+        post.setPostId(123L);
+        post.setAuthorId(2L); // 不同的用户ID
+
+        when(postService.selectPostById(123L)).thenReturn(post);
+
+        String paymentData = "{\"postId\":123,\"planId\":1,\"amount\":50.0}";
+
+        mockMvc.perform(post("/post-center/payment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(paymentData))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(123L);
+        verify(postPaymentMapper, never()).insertPostPayment(any());
+    }
+
+    /**
+     * 测试删除草稿帖子(状态为0)
+     */
+    @Test
+    void testDeleteDraftPost() throws Exception {
+        Post draftPost = new Post();
+        draftPost.setPostId(123L);
+        draftPost.setAuthorId(1L);
+        draftPost.setStatus("0"); // 草稿状态
+
+        when(postService.selectPostById(123L)).thenReturn(draftPost);
+        when(postPaymentMapper.deletePostPaymentByPostId(123L)).thenReturn(1);
+        when(postService.deletePostById(123L)).thenReturn(1);
+
+        mockMvc.perform(delete("/post-center/delete/{postId}", 123L))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(123L);
+        verify(postPaymentMapper, times(1)).deletePostPaymentByPostId(123L);
+        verify(postService, times(1)).deletePostById(123L);
+        // 草稿帖子不需要删除评论和收藏
+        verify(postCommentService, never()).deletePostCommentByPostId(any());
+        verify(postFavoriteService, never()).deletePostFavoriteByPostId(any());
+    }
+
+    /**
+     * 测试删除已发布帖子(状态为1)
+     */
+    @Test
+    void testDeletePublishedPost() throws Exception {
+        Post publishedPost = new Post();
+        publishedPost.setPostId(123L);
+        publishedPost.setAuthorId(1L);
+        publishedPost.setStatus("1"); // 已发布状态
+
+        when(postService.selectPostById(123L)).thenReturn(publishedPost);
+        when(postCommentService.deletePostCommentByPostId(123L)).thenReturn(1);
+        when(postFavoriteService.deletePostFavoriteByPostId(123L)).thenReturn(1);
+        when(postPaymentMapper.deletePostPaymentByPostId(123L)).thenReturn(1);
+        when(postService.deletePostById(123L)).thenReturn(1);
+
+        mockMvc.perform(delete("/post-center/delete/{postId}", 123L))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(123L);
+        verify(postCommentService, times(1)).deletePostCommentByPostId(123L);
+        verify(postFavoriteService, times(1)).deletePostFavoriteByPostId(123L);
+        verify(postPaymentMapper, times(1)).deletePostPaymentByPostId(123L);
+        verify(postService, times(1)).deletePostById(123L);
+    }
+
+    /**
+     * 测试重复创建支付记录的逻辑
+     */
+    @Test
+    void testMultiplePaymentAttempts() throws Exception {
+        Post post = new Post();
+        post.setPostId(123L);
+        post.setAuthorId(1L);
+
+        // 第一次创建支付记录(没有现有支付记录)
+        when(postService.selectPostById(123L)).thenReturn(post);
+        when(postPaymentMapper.selectLatestPaymentByPostId(123L)).thenReturn(null);
+        when(postPaymentMapper.insertPostPayment(any())).thenReturn(1);
+
+        String paymentData = "{\"postId\":123,\"planId\":1,\"amount\":50.0}";
+
+        mockMvc.perform(post("/post-center/payment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(paymentData))
+                .andExpect(status().isOk());
+
+        verify(postPaymentMapper, times(1)).insertPostPayment(any());
+
+        // 第二次创建支付记录(有pending状态的支付记录,应该可以创建新的)
+        PostPayment pendingPayment = new PostPayment();
+        pendingPayment.setPaymentStatus("pending");
+
+        when(postPaymentMapper.selectLatestPaymentByPostId(123L)).thenReturn(pendingPayment);
+
+        mockMvc.perform(post("/post-center/payment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(paymentData))
+                .andExpect(status().isOk());
+
+        verify(postPaymentMapper, times(2)).insertPostPayment(any());
+    }
+
+    /**
+     * 测试支付失败后重新创建支付记录
+     */
+    @Test
+    void testRetryPaymentAfterFailure() throws Exception {
+        Post post = new Post();
+        post.setPostId(123L);
+        post.setAuthorId(1L);
+
+        PostPayment failedPayment = new PostPayment();
+        failedPayment.setPaymentStatus("failed");
+
+        when(postService.selectPostById(123L)).thenReturn(post);
+        when(postPaymentMapper.selectLatestPaymentByPostId(123L)).thenReturn(failedPayment);
+        when(postPaymentMapper.insertPostPayment(any())).thenReturn(1);
+
+        String paymentData = "{\"postId\":123,\"planId\":1,\"amount\":50.0}";
+
+        mockMvc.perform(post("/post-center/payment")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(paymentData))
+                .andExpect(status().isOk());
+
+        verify(postService, times(1)).selectPostById(123L);
+        verify(postPaymentMapper, times(1)).selectLatestPaymentByPostId(123L);
+        verify(postPaymentMapper, times(1)).insertPostPayment(any());
+    }
 } 
\ No newline at end of file