支付功能+bugfix
Change-Id: I91a87ee5cb22aebe7ca8fc28722102c6f49f4c31
diff --git a/src/pages/UserCenter/index.tsx b/src/pages/UserCenter/index.tsx
index 13de6ad..4e2b5dc 100644
--- a/src/pages/UserCenter/index.tsx
+++ b/src/pages/UserCenter/index.tsx
@@ -99,6 +99,7 @@
const [editCoverImageUrl, setEditCoverImageUrl] = useState<string>('');
const [currentPayment, setCurrentPayment] = useState<PaymentRecord | null>(null);
const [isEditingPromotion, setIsEditingPromotion] = useState(false);
+ const [currentPublishingPostId, setCurrentPublishingPostId] = useState<number | null>(null); // 跟踪当前正在发布的帖子ID
useEffect(() => {
if (activeTab === 'myPosts') {
@@ -171,25 +172,64 @@
const handlePublishPost = async (values: PostFormData) => {
try {
- if (values.promotionPlan && selectedPromotion) {
- // 如果选择了推广,创建支付记录
- const paymentResponse = await createPayment({
- postId: 0, // 新帖子,暂时设为0,后端会处理
- planId: selectedPromotion.id,
- amount: selectedPromotion.price
- });
-
- if (paymentResponse.code === 200) {
- setCurrentPayment(paymentResponse.data);
- setPaymentModalVisible(true);
- return;
+ // 如果选择了推广计划
+ if (values.promotionPlan) {
+ const selectedPlan = promotionPlans.find(p => p.id === values.promotionPlan);
+ if (selectedPlan) {
+ setSelectedPromotion(selectedPlan);
+
+ let postId = currentPublishingPostId;
+
+ // 如果还没有创建帖子,先创建帖子
+ if (!postId) {
+ const postData = {
+ title: values.title,
+ content: values.content,
+ summary: values.summary,
+ tags: Array.isArray(values.tags) ? values.tags.join(',') : values.tags,
+ coverImage: coverImageUrl || undefined
+ // 注意:这里不包含promotionPlan,等支付成功后再更新
+ };
+
+ const publishResponse = await publishPost(postData);
+ if (publishResponse.code === 200) {
+ postId = publishResponse.data?.postId;
+ if (postId) {
+ setCurrentPublishingPostId(postId); // 保存已创建的帖子ID
+ } else {
+ message.error('帖子发布成功但无法获取帖子ID');
+ return;
+ }
+ } else {
+ message.error(publishResponse.msg || '帖子发布失败');
+ return;
+ }
+ }
+
+ // 使用帖子ID创建支付记录
+ if (postId) {
+ const paymentResponse = await createPayment({
+ postId: postId,
+ planId: selectedPlan.id,
+ amount: selectedPlan.price
+ });
+
+ if (paymentResponse.code === 200) {
+ setCurrentPayment(paymentResponse.data);
+ setPaymentModalVisible(true);
+ return;
+ } else {
+ message.error(paymentResponse.msg || '创建支付记录失败');
+ return;
+ }
+ }
} else {
- message.error(paymentResponse.msg || '创建支付记录失败');
+ message.error('无效的推广计划');
return;
}
}
- // 直接发布帖子
+ // 直接发布帖子(没有选择推广)
await submitPost(values);
} catch (error) {
message.error('发布帖子失败');
@@ -201,12 +241,15 @@
// 处理标签格式
const tagsString = Array.isArray(values.tags) ? values.tags.join(',') : values.tags;
+ // 如果有选择的推广计划,使用推广计划ID,否则使用表单中的值
+ const promotionPlanId = selectedPromotion?.id || values.promotionPlan;
+
const postData = {
title: values.title,
content: values.content,
summary: values.summary,
tags: tagsString,
- promotionPlan: values.promotionPlan,
+ promotionPlan: promotionPlanId,
coverImage: coverImageUrl || undefined
};
@@ -353,7 +396,7 @@
const response = await confirmPayment(currentPayment.paymentId);
if (response.code === 200) {
message.success('支付成功,推广已生效');
- setPaymentModalVisible(false);
+ setPaymentModalVisible(false);
setCurrentPayment(null);
// 如果是编辑模式,完成帖子更新
@@ -361,14 +404,16 @@
const values = editForm.getFieldsValue();
const tagsString = Array.isArray(values.tags) ? values.tags.join(',') : values.tags;
await updatePostDirectly(values, tagsString);
- } else {
- // 如果是发布模式
- setPublishModalVisible(false);
- form.resetFields();
- setSelectedPromotion(null);
+ } else if (publishModalVisible) {
+ // 如果是发布模式,支付成功后关闭所有弹窗,刷新帖子列表,清空状态
+ setPublishModalVisible(false);
+ form.resetFields();
setCoverImageUrl('');
- fetchMyPosts();
+ setCurrentPublishingPostId(null); // 清空已创建的帖子ID
+ fetchMyPosts();
}
+
+ setSelectedPromotion(null);
} else {
message.error(response.msg || '支付确认失败');
}
@@ -386,6 +431,17 @@
setPaymentModalVisible(false);
setCurrentPayment(null);
setSelectedPromotion(null);
+
+ // 如果是发布模式,支付取消后返回发布页面(不关闭发布弹窗)
+ // 保持currentPublishingPostId,用户可以重新支付
+ // 如果是编辑模式,关闭编辑弹窗
+ if (editModalVisible) {
+ setEditModalVisible(false);
+ editForm.resetFields();
+ setCurrentEditPost(null);
+ setEditCoverImageUrl('');
+ setIsEditingPromotion(false);
+ }
} catch (error) {
console.error('取消支付失败:', error);
setPaymentModalVisible(false);
@@ -432,6 +488,17 @@
};
const handleCancelPublish = async () => {
+ // 如果有已创建的帖子但还没有支付成功,需要删除这个帖子
+ if (currentPublishingPostId) {
+ try {
+ await deletePost(currentPublishingPostId);
+ message.info('已取消发布并删除草稿帖子');
+ } catch (error) {
+ console.error('删除草稿帖子失败:', error);
+ message.warning('取消发布成功,但删除草稿帖子失败');
+ }
+ }
+
// 如果有上传的图片但没有发布帖子,删除图片
if (coverImageUrl) {
try {
@@ -448,6 +515,7 @@
form.resetFields();
setSelectedPromotion(null);
setCoverImageUrl('');
+ setCurrentPublishingPostId(null); // 清空已创建的帖子ID
};
const uploadButton = (
diff --git a/tests/services/post.test.ts b/tests/services/post.test.ts
index 8481bf6..d44ddb3 100644
--- a/tests/services/post.test.ts
+++ b/tests/services/post.test.ts
@@ -583,6 +583,202 @@
});
});
+ // 新推广支付流程测试
+ describe('new promotion payment flow', () => {
+ it('应该成功执行新推广流程:发布帖子 -> 创建支付 -> 确认支付', async () => {
+ // 1. 先发布帖子
+ const publishResponse = {
+ code: 200,
+ data: { postId: 123, message: '帖子发布成功' }
+ };
+ (request as jest.Mock).mockResolvedValueOnce(publishResponse);
+
+ const postData = {
+ title: '推广帖子标题',
+ content: '推广帖子内容',
+ summary: '推广帖子摘要',
+ tags: 'tag1,tag2'
+ };
+
+ const publishResult = await postService.publishPost(postData);
+ expect(publishResult).toEqual(publishResponse);
+
+ // 2. 创建支付记录(使用真实的帖子ID)
+ const paymentResponse = {
+ code: 200,
+ data: {
+ paymentId: 1,
+ postId: 123, // 真实的帖子ID
+ planId: 1,
+ amount: 50.00,
+ paymentStatus: 'pending'
+ }
+ };
+ (request as jest.Mock).mockResolvedValueOnce(paymentResponse);
+
+ const paymentData = {
+ postId: 123, // 使用发布帖子返回的ID
+ planId: 1,
+ amount: 50.00
+ };
+
+ const paymentResult = await postService.createPayment(paymentData);
+ expect(paymentResult).toEqual(paymentResponse);
+
+ // 3. 确认支付
+ const confirmResponse = { code: 200, msg: '支付成功,推广已生效' };
+ (request as jest.Mock).mockResolvedValueOnce(confirmResponse);
+
+ const confirmResult = await postService.confirmPayment(1);
+ expect(confirmResult).toEqual(confirmResponse);
+
+ // 验证API调用顺序和参数
+ expect(request).toHaveBeenCalledTimes(3);
+ expect(request).toHaveBeenNthCalledWith(1, '/api/post-center/publish', {
+ method: 'POST',
+ data: postData,
+ });
+ expect(request).toHaveBeenNthCalledWith(2, '/api/post-center/payment', {
+ method: 'POST',
+ data: paymentData,
+ });
+ expect(request).toHaveBeenNthCalledWith(3, '/api/post-center/payment/confirm/1', {
+ method: 'POST',
+ });
+ });
+
+ it('应该处理支付失败后重新尝试支付的场景', async () => {
+ // 第一次支付失败,第二次应该复用已创建的帖子
+ const paymentData = {
+ postId: 123, // 复用已创建的帖子ID
+ planId: 1,
+ amount: 50.00
+ };
+
+ // 第一次支付失败
+ const failedPaymentResponse = { code: 400, msg: '支付处理失败' };
+ (request as jest.Mock).mockResolvedValueOnce(failedPaymentResponse);
+
+ let result = await postService.createPayment(paymentData);
+ expect(result).toEqual(failedPaymentResponse);
+
+ // 第二次支付成功
+ const successPaymentResponse = {
+ code: 200,
+ data: {
+ paymentId: 2,
+ postId: 123,
+ planId: 1,
+ amount: 50.00,
+ paymentStatus: 'pending'
+ }
+ };
+ (request as jest.Mock).mockResolvedValueOnce(successPaymentResponse);
+
+ result = await postService.createPayment(paymentData);
+ expect(result).toEqual(successPaymentResponse);
+
+ // 验证使用的是同一个帖子ID
+ expect(request).toHaveBeenCalledTimes(2);
+ expect(request).toHaveBeenNthCalledWith(1, '/api/post-center/payment', {
+ method: 'POST',
+ data: paymentData,
+ });
+ expect(request).toHaveBeenNthCalledWith(2, '/api/post-center/payment', {
+ method: 'POST',
+ data: paymentData,
+ });
+ });
+
+ it('应该成功删除取消发布的草稿帖子', async () => {
+ const deleteResponse = { code: 200, msg: '草稿帖子删除成功' };
+ (request as jest.Mock).mockResolvedValue(deleteResponse);
+
+ const result = await postService.deletePost(123);
+ expect(result).toEqual(deleteResponse);
+
+ expect(request).toHaveBeenCalledWith('/api/post-center/delete/123', {
+ method: 'DELETE',
+ });
+ });
+
+ it('应该处理发布帖子成功但创建支付失败的情况', async () => {
+ // 1. 发布帖子成功
+ const publishResponse = {
+ code: 200,
+ data: { postId: 123, message: '帖子发布成功' }
+ };
+ (request as jest.Mock).mockResolvedValueOnce(publishResponse);
+
+ const postData = {
+ title: '推广帖子标题',
+ content: '推广帖子内容',
+ summary: '推广帖子摘要',
+ tags: 'tag1,tag2'
+ };
+
+ await postService.publishPost(postData);
+
+ // 2. 创建支付失败
+ const paymentResponse = { code: 400, msg: '该帖子已购买推广服务' };
+ (request as jest.Mock).mockResolvedValueOnce(paymentResponse);
+
+ const paymentData = {
+ postId: 123,
+ planId: 1,
+ amount: 50.00
+ };
+
+ const paymentResult = await postService.createPayment(paymentData);
+ expect(paymentResult).toEqual(paymentResponse);
+ });
+
+ it('应该处理发布帖子失败的情况', async () => {
+ const publishResponse = { code: 400, msg: '帖子标题不能为空' };
+ (request as jest.Mock).mockResolvedValue(publishResponse);
+
+ const postData = {
+ title: '',
+ content: '内容',
+ summary: '摘要',
+ tags: 'tag1'
+ };
+
+ const result = await postService.publishPost(postData);
+ expect(result).toEqual(publishResponse);
+ });
+
+ it('应该处理支付确认失败的情况', async () => {
+ const confirmResponse = { code: 400, msg: '支付记录中的帖子ID无效' };
+ (request as jest.Mock).mockResolvedValue(confirmResponse);
+
+ const result = await postService.confirmPayment(1);
+ expect(result).toEqual(confirmResponse);
+ });
+
+ it('应该成功取消支付并返回发布页面', async () => {
+ const cancelResponse = { code: 200, msg: '支付已取消' };
+ (request as jest.Mock).mockResolvedValue(cancelResponse);
+
+ const result = await postService.cancelPayment(1);
+ expect(result).toEqual(cancelResponse);
+ });
+
+ it('应该处理重复购买推广的情况', async () => {
+ const paymentResponse = { code: 400, msg: '该帖子已购买推广服务' };
+ (request as jest.Mock).mockResolvedValue(paymentResponse);
+
+ const paymentData = {
+ postId: 123,
+ planId: 1,
+ amount: 50.00
+ };
+
+ const result = await postService.createPayment(paymentData);
+ expect(result).toEqual(paymentResponse);
+ });
+ });
+
// 举报相关测试
describe('report operations', () => {
it('应该成功举报帖子', async () => {