Merge "我是人,我做了悬赏中心,可以进行悬赏哦11111111"
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..d604a03
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,6 @@
+# .dockerignore
+node_modules
+npm-debug.log
+yarn-error.log
+dist
+build
\ No newline at end of file
diff --git a/.env b/.env
new file mode 100644
index 0000000..cb6f082
--- /dev/null
+++ b/.env
@@ -0,0 +1,2 @@
+PORT=3004  # 设置前端端口
+API_PROXY_TARGET=http://localhost:5004  # 设置代理后端地址
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..99ba20b
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,18 @@
+# 构建阶段
+FROM node:16 as build
+WORKDIR /app
+COPY package*.json ./
+# 使用淘宝镜像源安装依赖
+RUN npm config set registry https://registry.npmmirror.com && \
+    npm install
+COPY . .
+RUN npm run build
+
+# 生产阶段
+FROM nginx:alpine
+COPY --from=build /app/dist  /usr/share/nginx/html
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+
+EXPOSE 3004
+
+CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
diff --git a/config/config.ts b/config/config.ts
index f0004a8..511894a 100644
--- a/config/config.ts
+++ b/config/config.ts
@@ -8,6 +8,7 @@
 const { REACT_APP_ENV = 'dev' } = process.env;
 
 export default defineConfig({
+   
   /**
    * @name 开启 hash 模式
    * @description 让 build 之后的产物包含 hash 后缀。通常用于增量发布和避免浏览器加载缓存。
diff --git a/config/oneapi.json b/config/oneapi.json
index c77d988..064e6b7 100644
--- a/config/oneapi.json
+++ b/config/oneapi.json
@@ -4,12 +4,13 @@
     "title": "Ant Design Pro",
     "version": "1.0.0"
   },
+  
   "servers": [
     {
-      "url": "http://localhost:8000/"
+      "url": "http://localhost:3004/"
     },
     {
-      "url": "https://localhost:8000/"
+      "url": "https://localhost:3004/"
     }
   ],
   "paths": {
diff --git a/config/proxy.ts b/config/proxy.ts
index c290c6a..a812e74 100644
--- a/config/proxy.ts
+++ b/config/proxy.ts
@@ -12,17 +12,17 @@
 export default {
   // 如果需要自定义本地开发服务器  请取消注释按需调整
   dev: {
-    // localhost:8000/api/** -> https://preview.pro.ant.design/api/**
+    // localhost:5004/api/** -> https://preview.pro.ant.design/api/**
     '/api/': {
       // 要代理的地址
-      target: 'http://localhost:8080',
+      target: 'http://localhost:5004',
       // 配置了这个可以从 http 代理到 https
       // 依赖 origin 的功能可能需要这个,比如 cookie
       changeOrigin: true,
       pathRewrite: { '^/api': '' },
     },
     '/profile/avatar/': {
-      target: 'http://localhost:8080',
+      target: 'http://localhost:5004',
       changeOrigin: true,
     }
   },
@@ -32,7 +32,7 @@
    * @doc https://github.com/chimurai/http-proxy-middleware
    */
   test: {
-    // localhost:8000/api/** -> https://preview.pro.ant.design/api/**
+    // localhost:5004/api/** -> https://preview.pro.ant.design/api/**
     '/api/': {
       target: 'https://proapi.azurewebsites.net',
       changeOrigin: true,
diff --git a/gitpush.bat b/gitpush.bat
new file mode 100644
index 0000000..dffd610
--- /dev/null
+++ b/gitpush.bat
@@ -0,0 +1,5 @@
+@echo off
+SET /P commit="Enter commit message: "
+git add .
+git commit -m "%commit%"
+git push HEAD:refs/for/master
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 0000000..8673316
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,25 @@
+upstream backend {
+    server backend:5004;  # 保持不变(Docker Compose中服务名即容器间通信域名)
+}
+
+server {
+    listen 3004;  # 修改为Docker Compose中映射的前端端口(与docker-compose.yml保持一致)
+
+    server_name _;  # 允许所有域名访问
+
+    # 静态资源处理
+    location / {
+        root /usr/share/nginx/html;
+        index index.html;
+        try_files $uri $uri/ /index.html;  # 适配SPA路由(如React/Vue)
+    }
+
+    # 反向代理API请求到后端
+    location /api/ {
+        proxy_pass http://backend/;  # 代理路径与后端API前缀匹配
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+    }
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 044644a..4625852 100644
--- a/package.json
+++ b/package.json
@@ -7,9 +7,9 @@
     "dev": "max dev",
     "build": "max build",
     "deploy": "npm run build && npm run gh-pages",
-    "preview": "npm run build && max preview --port 8000",
+    "preview": "npm run build && max preview --port 5004",
     "serve": "umi-serve",
-    "start": "npm run dev",
+    "start": "npm run dev -- --port 5004",
     "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev max dev",
     "start:no-mock": "cross-env MOCK=none UMI_ENV=dev max dev",
     "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev",
diff --git "a/public/\346\230\237\347\251\272.gif" "b/public/\346\230\237\347\251\272.gif"
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ "b/public/\346\230\237\347\251\272.gif"
diff --git a/src/pages/User/Login/index.tsx b/src/pages/User/Login/index.tsx
index a18f785..233cb3d 100644
--- a/src/pages/User/Login/index.tsx
+++ b/src/pages/User/Login/index.tsx
@@ -167,7 +167,18 @@
   }, []);
 
   return (
-    <div className={containerClassName}>
+    <div
+      className={containerClassName}
+      style={{
+        backgroundImage:
+          "url('https://images.unsplash.com/photo-1462331940025-496dfbfc7564?auto=format&fit=crop&w=1500&q=80')",
+        backgroundSize: 'cover',
+        backgroundPosition: 'center',
+        minHeight: '100vh',
+        position: 'relative',
+        overflow: 'hidden',
+      }}
+    >
       <Helmet>
         <title>
           {intl.formatMessage({
@@ -178,31 +189,86 @@
         </title>
       </Helmet>
       <Lang />
+      {/* 星空粒子特效层 */}
+      <div
+        style={{
+          position: 'absolute',
+          inset: 0,
+          zIndex: 0,
+          pointerEvents: 'none',
+          background: 'radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%)',
+        }}
+      >
+        {/* 可以集成粒子库如 tsParticles 或者简单用 CSS 动画 */}
+        <svg width="100%" height="100%">
+          {[...Array(60)].map((_, i) => (
+            <circle
+              key={i}
+              cx={Math.random() * 1600}
+              cy={Math.random() * 900}
+              r={Math.random() * 1.5 + 0.5}
+              fill="#fff"
+              opacity={Math.random() * 0.8 + 0.2}
+            />
+          ))}
+        </svg>
+      </div>
       <div
         style={{
           flex: '1',
           padding: '32px 0',
+          display: 'flex',
+          alignItems: 'center',
+          justifyContent: 'center',
+          minHeight: '100vh',
+          position: 'relative',
+          zIndex: 1,
         }}
       >
         <LoginForm
           contentStyle={{
-            minWidth: 280,
-            maxWidth: '75vw',
+            minWidth: 320,
+            maxWidth: 400,
+            background: 'rgba(25, 34, 54, 0.92)',
+            borderRadius: 16,
+            boxShadow: '0 8px 32px 0 rgba(31, 38, 135, 0.37)',
+            border: '1px solid rgba(255,255,255,0.18)',
+            padding: '32px 32px 24px 32px',
+            color: '#fff',
           }}
-          logo={<img alt="logo" src="/logo.svg" />}
-          title="Ant Design"
-          subTitle={intl.formatMessage({ id: 'pages.layouts.userLayout.title' })}
+          // logo={
+          //   <img
+          //     alt="logo"
+          //     src="/planet-logo.svg"
+          //     style={{
+          //       width: 64,
+          //       height: 64,
+          //       filter: 'drop-shadow(0 0 8px #fff8) drop-shadow(0 0 16px #6cf)',
+          //       marginBottom: 8,
+          //     }}
+          //   />
+          // }
+          title={
+            <span style={{ color: '#fff', fontWeight: 700, fontSize: 28, letterSpacing: 2 }}>
+              PT星球
+            </span>
+          }
+          subTitle={
+            <span style={{ color: '#b3c7f9', fontSize: 16 }}>
+              探索你的专属星球,畅享PT世界
+            </span>
+          }
           initialValues={{
             autoLogin: true,
           }}
-          actions={[
-            <FormattedMessage
-              key="loginWith"
-              id="pages.login.loginWith"
-              defaultMessage="其他登录方式"
-            />,
-            <ActionIcons key="icons" />,
-          ]}
+          // actions={[
+          //   <FormattedMessage
+          //     key="loginWith"
+          //     id="pages.login.loginWith"
+          //     defaultMessage="其他登录方式"
+          //   />,
+          //   <ActionIcons key="icons" />,
+          // ]}
           onFinish={async (values) => {
             await handleSubmit(values as API.LoginParams);
           }}
@@ -214,19 +280,28 @@
             items={[
               {
                 key: 'account',
-                label: intl.formatMessage({
-                  id: 'pages.login.accountLogin.tab',
-                  defaultMessage: '账户密码登录',
-                }),
+                label: (
+                  <span style={{ color: '#fff' }}>
+                    {intl.formatMessage({
+                      id: 'pages.login.accountLogin.tab',
+                      defaultMessage: '账户密码登录',
+                    })}
+                  </span>
+                ),
               },
               {
                 key: 'mobile',
-                label: intl.formatMessage({
-                  id: 'pages.login.phoneLogin.tab',
-                  defaultMessage: '手机号登录',
-                }),
+                label: (
+                  <span style={{ color: '#fff' }}>
+                    {intl.formatMessage({
+                      id: 'pages.login.phoneLogin.tab',
+                      defaultMessage: '手机号登录',
+                    })}
+                  </span>
+                ),
               },
             ]}
+            style={{ marginBottom: 24 }}
           />
 
           {code !== 200 && loginType === 'account' && (
@@ -244,7 +319,8 @@
                 initialValue="admin"
                 fieldProps={{
                   size: 'large',
-                  prefix: <UserOutlined />,
+                  prefix: <UserOutlined style={{ color: '#6cf' }} />,
+                  style: { background: 'rgba(255,255,255,0.08)', color: '#fff' },
                 }}
                 placeholder={intl.formatMessage({
                   id: 'pages.login.username.placeholder',
@@ -267,7 +343,8 @@
                 initialValue="admin123"
                 fieldProps={{
                   size: 'large',
-                  prefix: <LockOutlined />,
+                  prefix: <LockOutlined style={{ color: '#6cf' }} />,
+                  style: { background: 'rgba(255,255,255,0.08)', color: '#fff' },
                 }}
                 placeholder={intl.formatMessage({
                   id: 'pages.login.password.placeholder',
@@ -290,6 +367,8 @@
                   <ProFormText
                     style={{
                       float: 'right',
+                      background: 'rgba(255,255,255,0.08)',
+                      color: '#fff',
                     }}
                     name="code"
                     placeholder={intl.formatMessage({
@@ -319,6 +398,9 @@
                       cursor: 'pointer',
                       paddingLeft: '10px',
                       width: '100px',
+                      borderRadius: 8,
+                      boxShadow: '0 0 8px #6cf8',
+                      background: '#fff',
                     }}
                     preview={false}
                     onClick={() => getCaptchaCode()}
@@ -328,13 +410,16 @@
             </>
           )}
 
-          {code !== 200 && loginType === 'mobile' && <LoginMessage content="验证码错误" />}
+          {code !== 200 && loginType === 'mobile' && (
+            <LoginMessage content="验证码错误" />
+          )}
           {type === 'mobile' && (
             <>
               <ProFormText
                 fieldProps={{
                   size: 'large',
-                  prefix: <MobileOutlined />,
+                  prefix: <MobileOutlined style={{ color: '#6cf' }} />,
+                  style: { background: 'rgba(255,255,255,0.08)', color: '#fff' },
                 }}
                 name="mobile"
                 placeholder={intl.formatMessage({
@@ -365,7 +450,8 @@
               <ProFormCaptcha
                 fieldProps={{
                   size: 'large',
-                  prefix: <LockOutlined />,
+                  prefix: <LockOutlined style={{ color: '#6cf' }} />,
+                  style: { background: 'rgba(255,255,255,0.08)', color: '#fff' },
                 }}
                 captchaProps={{
                   size: 'large',
@@ -413,14 +499,23 @@
           <div
             style={{
               marginBottom: 24,
+              color: '#b3c7f9',
             }}
           >
-            <ProFormCheckbox noStyle name="autoLogin">
-              <FormattedMessage id="pages.login.rememberMe" defaultMessage="自动登录" />
+            <ProFormCheckbox>
+              <a
+                style={{
+                  float: 'right',
+                  color: '#fff',
+                  fontSize: 14,
+                }}>
+              <FormattedMessage id="pages.login.rememberMe"  defaultMessage="自动登录" />
+              </a>
             </ProFormCheckbox>
             <a
               style={{
                 float: 'right',
+                color: '#6cf',
               }}
             >
               <FormattedMessage id="pages.login.forgotPassword" defaultMessage="忘记密码" />
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 () => {