Merge "支付功能+bugfix"
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