创作中心模块包含首页展示、个人中心、帖子审核。

“首页展示”支持广告轮播展示、推广帖子优先展示、分页显示所有帖子、导航栏便捷标签筛选帖子、全局标题模糊搜索帖子、点击帖子“查看更多”进入帖子详情页。帖子详情页展示帖子封面图片、作者时间、详细内容(可以插入种子链接对种子进行介绍与推广)等基本信息、对帖子点赞收藏举报评论回复、查看相关推荐帖子。相关推荐会推荐当前帖子作者的其他帖子(最多推荐5篇),还会推荐具有相似标签的其他帖子,两者总共最多推荐9篇帖子。

“个人中心”包含“我的中心”和“我的收藏”。
“我的中心”中可以管理已经成功发布的帖子(编辑、删除帖子),还可以发布新帖子。发布新帖子时除了填写帖子基本信息以外,帖子标签支持下拉多项选择,用户还可以选择帖子推广项目并进行支付。设置了多种推广项目,包含广告轮播推广、帖子置顶展示、限时优先展示、分类页首条展示。系统后台执行自动定时任务,每小时对帖子的推广时效性进行检查,如超出推广时限,则取消帖子的推广显示特权。用户点击发布帖子后帖子处于待审核状态,需要管理员审核通过才能正常发布在首页展示页面。编辑帖子时用户可以追加帖子推广,但如果帖子处于推广状态,则禁止修改推广项目。
“我的收藏”中可以便捷查看所有已收藏的帖子。

“帖子审核”包含“帖子发布管理”和“帖子举报管理”。“帖子审核”板块具有权限管理,只有管理员界面能够进入。
“帖子发布管理”对所有待审核帖子进行处理,支持预览待审核帖子详细内容,批准通过和拒绝通过选项。
“帖子举报管理”对所有用户的举报请求进行人工审核,如果举报内容属实,则将帖子下架处理,如果举报内容不属实,驳回举报请求。所有举报请求的处理结果均留存显示,方便后续再次审查。

Change-Id: If822351183e9d55a5a56ff5cf1e13b313fdbe231
diff --git a/config/routes.ts b/config/routes.ts
index ffad02f..f9e4fcb 100644
--- a/config/routes.ts
+++ b/config/routes.ts
@@ -89,4 +89,28 @@
       },
     ]
   },
+  {
+    name: '帖子中心',
+    icon: 'read',
+    path: '/post/center',
+    component: './PostCenter/index.tsx',
+  },
+  {
+    name: '帖子详情',
+    path: '/post-detail/:id',
+    component: './PostCenter/PostDetail.tsx',
+    hideInMenu: true,
+  },
+  {
+    name: '个人中心',
+    path: '/user-center',
+    component: './UserCenter/index.tsx',
+    hideInMenu: true,
+  },
+  {
+    name: '帖子审核',
+    path: '/post-review',
+    component: './PostReview/index.tsx',
+    hideInMenu: true,
+  }
 ];
diff --git a/jest.config.ts b/jest.config.ts
index 1de2a1a..098ae99 100644
--- a/jest.config.ts
+++ b/jest.config.ts
@@ -1,23 +1,40 @@
-import { configUmiAlias, createConfig } from '@umijs/max/test';
+import { Config } from 'jest';
 
-export default async () => {
-  const config = await configUmiAlias({
-    ...createConfig({
-      target: 'browser',
-    }),
-  });
-
-  console.log();
-  return {
-    ...config,
-    testEnvironmentOptions: {
-      ...(config?.testEnvironmentOptions || {}),
-      url: 'http://localhost:8000',
+const config: Config = {
+  preset: 'ts-jest',
+  testEnvironment: 'jsdom',
+  setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
+  moduleNameMapper: {
+    '^@/(.*)$': '<rootDir>/src/$1',
+  },
+  testMatch: [
+    '<rootDir>/tests/**/*.test.ts',
+    '<rootDir>/tests/**/*.test.tsx',
+  ],
+  collectCoverageFrom: [
+    'src/**/*.{ts,tsx}',
+    '!src/**/*.d.ts',
+    '!src/**/*.test.{ts,tsx}',
+  ],
+  coverageDirectory: 'coverage',
+  coverageReporters: ['text', 'lcov', 'html'],
+  coverageThreshold: {
+    global: {
+      branches: 80,
+      functions: 80,
+      lines: 80,
+      statements: 80,
     },
-    setupFiles: [...(config.setupFiles || []), './tests/setupTests.jsx'],
-    globals: {
-      ...config.globals,
-      localStorage: null,
+  },
+  transform: {
+    '^.+\\.(ts|tsx)$': 'ts-jest',
+  },
+  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
+  globals: {
+    'ts-jest': {
+      tsconfig: 'tsconfig.json',
     },
-  };
+  },
 };
+
+export default config;
diff --git a/mock/post.ts b/mock/post.ts
new file mode 100644
index 0000000..fdbb9f3
--- /dev/null
+++ b/mock/post.ts
@@ -0,0 +1,290 @@
+import { Request, Response } from 'express';
+
+// Mock数据
+const mockPosts = [
+  {
+    postId: 1,
+    title: '【日剧推荐】献给刚刚接触日剧的你',
+    content: `
+      <div class="post-content">
+        <p>在日本,并没有四大台柱的称谓,这一说法的由来是贴吧的一个投票。</p>
+        <h3>校园剧</h3>
+        <p>龙樱 - 校园青春励志剧,如果你还没参加高考,推荐你提前看看,相信你会有所收获。</p>
+        <p>野猪大改造 - 两个J家美少年和两个台柱,除第一集节奏稍慢外,无硬伤,有友情有效笑有泪水。</p>
+        <h3>职场剧</h3>
+        <p>麻辣教师 - 小栗旬还是个弱小男生。</p>
+        <p>极道鲜师 - 学生上课搞乱不听讲怎么办,小栗来教你。</p>
+      </div>
+    `,
+    summary: '这是一篇关于日剧推荐的帖子,包含了校园剧和职场剧的推荐。',
+    coverImage: '/images/flower.jpg',
+    authorId: 1,
+    author: 'Ryo',
+    views: 87951,
+    comments: 15,
+    favorites: 256,
+    likes: 1024,
+    status: '1',
+    publishTime: '2017年8月26日',
+    tags: '日剧,嘉宾专栏,作品合集',
+    promotionPlanId: 1,
+    createTime: '2017-08-26 10:30:00',
+    updateTime: '2017-08-26 10:30:00',
+  },
+  {
+    postId: 2,
+    title: '【2025年4月档】对岸的家务事 高清1080P 新番双语+中文字幕 百度网盘 更新07',
+    content: '<p>对岸的家务事是一部关于家庭生活的日剧。</p>',
+    summary: '对岸的家务事是一部关于家庭生活的日剧。',
+    coverImage: '/images/flower.jpg',
+    authorId: 2,
+    author: '日剧翻译组',
+    views: 3245,
+    comments: 156,
+    favorites: 89,
+    likes: 345,
+    status: '1',
+    publishTime: '2025-5-14',
+    tags: '日剧,2025,家务',
+    promotionPlanId: null,
+    createTime: '2025-05-14 10:30:00',
+    updateTime: '2025-05-14 10:30:00',
+  },
+  {
+    postId: 3,
+    title: '【2025年4月档】天久鹰央的推理历表 高清1080P 中文字幕 百度网盘 更新04',
+    content: '<p>天久鹰央的推理历表是一部推理题材的日剧。</p>',
+    summary: '天久鹰央的推理历表是一部推理题材的日剧。',
+    coverImage: '/images/flower.jpg',
+    authorId: 2,
+    author: '日剧翻译组',
+    views: 4589,
+    comments: 267,
+    favorites: 123,
+    likes: 567,
+    status: '1',
+    publishTime: '2025-5-14',
+    tags: '日剧,2025,推理',
+    promotionPlanId: null,
+    createTime: '2025-05-14 10:30:00',
+    updateTime: '2025-05-14 10:30:00',
+  }
+];
+
+const mockComments = [
+  {
+    commentId: 1,
+    postId: 1,
+    content: '早期的堀北真希真是太美了',
+    userId: 10,
+    userName: 'Inchou',
+    userAvatar: 'https://via.placeholder.com/40',
+    parentId: 0,
+    status: '1',
+    likes: 5,
+    createTime: '2025-05-15 05:19:00',
+  },
+  {
+    commentId: 2,
+    postId: 1,
+    content: '谢谢分享 😚',
+    userId: 11,
+    userName: '紫毛球',
+    userAvatar: 'https://via.placeholder.com/40',
+    parentId: 0,
+    status: '1',
+    likes: 3,
+    createTime: '2025-05-02 10:35:00',
+  },
+  {
+    commentId: 3,
+    postId: 1,
+    content: '感谢分享',
+    userId: 12,
+    userName: 'JJxMM666',
+    userAvatar: 'https://via.placeholder.com/40',
+    parentId: 0,
+    status: '1',
+    likes: 2,
+    createTime: '2025-05-01 18:54:00',
+  }
+];
+
+export default {
+  // 获取帖子列表
+  'GET /api/post/list': (req: Request, res: Response) => {
+    const { pageNum = 1, pageSize = 10, title, status = '1' } = req.query;
+    
+    let filteredPosts = mockPosts.filter(post => post.status === status);
+    
+    if (title) {
+      filteredPosts = filteredPosts.filter(post => 
+        post.title.toLowerCase().includes((title as string).toLowerCase())
+      );
+    }
+
+    const startIndex = (Number(pageNum) - 1) * Number(pageSize);
+    const endIndex = startIndex + Number(pageSize);
+    const paginatedPosts = filteredPosts.slice(startIndex, endIndex);
+
+    res.json({
+      code: 200,
+      rows: paginatedPosts,
+      total: filteredPosts.length,
+      msg: '查询成功',
+    });
+  },
+
+  // 获取帖子详情
+  'GET /api/post/:id': (req: Request, res: Response) => {
+    const { id } = req.params;
+    const post = mockPosts.find(p => p.postId === Number(id));
+    
+    if (!post) {
+      return res.json({
+        code: 404,
+        msg: '帖子不存在',
+      });
+    }
+
+    const postComments = mockComments
+      .filter(c => c.postId === Number(id) && c.parentId === 0)
+      .map(comment => ({
+        comment,
+        replies: mockComments.filter(c => c.parentId === comment.commentId)
+      }));
+
+    const authorPosts = mockPosts
+      .filter(p => p.authorId === post.authorId && p.postId !== post.postId)
+      .slice(0, 3);
+
+    const similarPosts = mockPosts
+      .filter(p => {
+        if (p.postId === post.postId) return false;
+        const postTags = post.tags.split(',');
+        const pTags = p.tags.split(',');
+        return postTags.some(tag => pTags.includes(tag));
+      })
+      .slice(0, 3);
+
+    res.json({
+      code: 200,
+      data: {
+        post,
+        tags: post.tags.split(',').map((tag, index) => ({
+          tagId: index + 1,
+          tagName: tag,
+          tagColor: 'blue',
+          postCount: Math.floor(Math.random() * 100) + 1,
+          status: '0'
+        })),
+        comments: postComments,
+        authorPosts,
+        similarPosts,
+        favorited: false
+      },
+      msg: '查询成功',
+    });
+  },
+
+  // 添加评论
+  'POST /api/post/comment': (req: Request, res: Response) => {
+    const { postId, content, parentId = 0 } = req.body;
+    
+    if (!postId || !content) {
+      return res.json({
+        code: 400,
+        msg: '参数不完整',
+      });
+    }
+
+    const newComment = {
+      commentId: Date.now(),
+      postId: Number(postId),
+      content,
+      userId: 100,
+      userName: '当前用户',
+      userAvatar: 'https://via.placeholder.com/40',
+      parentId: Number(parentId),
+      status: '1',
+      likes: 0,
+      createTime: new Date().toLocaleString('zh-CN'),
+    };
+
+    mockComments.push(newComment);
+
+    // 更新帖子评论数
+    const post = mockPosts.find(p => p.postId === Number(postId));
+    if (post) {
+      post.comments += 1;
+    }
+
+    res.json({
+      code: 200,
+      data: newComment,
+      msg: '评论成功',
+    });
+  },
+
+  // 收藏/取消收藏帖子
+  'POST /api/post/favorite/:id': (req: Request, res: Response) => {
+    const { id } = req.params;
+    const { favorite } = req.query;
+    
+    const post = mockPosts.find(p => p.postId === Number(id));
+    if (!post) {
+      return res.json({
+        code: 404,
+        msg: '帖子不存在',
+      });
+    }
+
+    if (favorite === 'true') {
+      post.favorites += 1;
+      res.json({
+        code: 200,
+        msg: '收藏成功',
+      });
+    } else {
+      post.favorites = Math.max(0, post.favorites - 1);
+      res.json({
+        code: 200,
+        msg: '取消收藏成功',
+      });
+    }
+  },
+
+  // 获取热门标签
+  'GET /api/post/tags/hot': (req: Request, res: Response) => {
+    const mockTags = [
+      { tagId: 1, tagName: '日剧', tagColor: 'blue', postCount: 150, status: '0' },
+      { tagId: 2, tagName: '电影', tagColor: 'green', postCount: 120, status: '0' },
+      { tagId: 3, tagName: '音乐', tagColor: 'orange', postCount: 80, status: '0' },
+      { tagId: 4, tagName: '推理', tagColor: 'purple', postCount: 65, status: '0' },
+      { tagId: 5, tagName: '爱情', tagColor: 'pink', postCount: 45, status: '0' },
+    ];
+
+    res.json({
+      code: 200,
+      data: mockTags,
+      msg: '查询成功',
+    });
+  },
+
+  // 根据标签获取帖子
+  'GET /api/post/bytag/:tagId': (req: Request, res: Response) => {
+    const { tagId } = req.params;
+    
+    // 简单的mock实现,实际应该根据tagId查询相关帖子
+    const filteredPosts = mockPosts.filter(post => 
+      post.tags.includes('日剧') // 假设查询日剧标签
+    );
+
+    res.json({
+      code: 200,
+      rows: filteredPosts,
+      total: filteredPosts.length,
+      msg: '查询成功',
+    });
+  },
+}; 
\ No newline at end of file
diff --git a/package.json b/package.json
index ce917d9..044644a 100644
--- a/package.json
+++ b/package.json
@@ -4,19 +4,20 @@
   "private": true,
   "description": "An out-of-box UI solution for enterprise applications",
   "scripts": {
-    "dev": "npm run start:dev",
+    "dev": "max dev",
     "build": "max build",
     "deploy": "npm run build && npm run gh-pages",
     "preview": "npm run build && max preview --port 8000",
     "serve": "umi-serve",
-    "start": "cross-env UMI_ENV=dev max dev",
+    "start": "npm run dev",
     "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",
     "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev",
     "test": "jest",
-    "test:coverage": "npm run jest -- --coverage",
-    "test:update": "npm run jest -- -u",
+    "test:watch": "jest --watch",
+    "test:coverage": "jest --coverage",
+    "test:ci": "jest --ci --coverage --watchAll=false",
     "docker-hub:build": "docker build  -f Dockerfile.hub -t  ant-design-pro ./",
     "docker-prod:build": "docker-compose -f ./docker/docker-compose.yml build",
     "docker-prod:dev": "docker-compose -f ./docker/docker-compose.yml up",
@@ -39,7 +40,9 @@
     "prepare": "cd .. && husky install",
     "prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\"",
     "tsc": "tsc --noEmit",
-    "record": "cross-env NODE_ENV=development REACT_APP_ENV=test max record --scene=login"
+    "record": "cross-env NODE_ENV=development REACT_APP_ENV=test max record --scene=login",
+    "format": "prettier --cache --write .",
+    "setup": "max setup"
   },
   "lint-staged": {
     "**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js",
@@ -53,6 +56,7 @@
     "not ie <= 10"
   ],
   "dependencies": {
+    "@ant-design/compatible": "^5.1.4",
     "@ant-design/icons": "^5.5.0",
     "@ant-design/plots": "^2.3.2",
     "@ant-design/pro-components": "^2.7.19",
@@ -81,15 +85,17 @@
   },
   "devDependencies": {
     "@ant-design/pro-cli": "^3.3.0",
-    "@testing-library/react": "^16.0.1",
+    "@testing-library/jest-dom": "^6.6.3",
+    "@testing-library/react": "^16.3.0",
+    "@testing-library/user-event": "^14.6.1",
     "@types/classnames": "^2.3.1",
     "@types/express": "^4.17.21",
     "@types/history": "^4.7.11",
-    "@types/jest": "^29.5.12",
+    "@types/jest": "^29.5.14",
     "@types/lodash": "^4.17.4",
     "@types/node": "^22.13.5",
-    "@types/react": "^18.3.0",
-    "@types/react-dom": "^18.3.0",
+    "@types/react": "^18.3.23",
+    "@types/react-dom": "^18.3.7",
     "@types/react-helmet": "^6.1.11",
     "@umijs/fabric": "^2.14.1",
     "@umijs/lint": "^4.2.9",
@@ -104,8 +110,9 @@
     "mockjs": "^1.1.0",
     "prettier": "^3.3.0",
     "swagger-ui-dist": "^5.17.14",
+    "ts-jest": "^29.3.4",
     "ts-node": "^10.9.1",
-    "typescript": "^5.6.2",
+    "typescript": "^5.8.3",
     "umi-presets-pro": "^2.0.0"
   },
   "engines": {
diff --git a/public/images/.gitkeep b/public/images/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/public/images/.gitkeep
diff --git a/src/pages/PostCenter/PostCard.module.css b/src/pages/PostCenter/PostCard.module.css
new file mode 100644
index 0000000..3f0623f
--- /dev/null
+++ b/src/pages/PostCenter/PostCard.module.css
@@ -0,0 +1,192 @@
+.postCardWrapper {
+  width: 100%;
+  height: 100%;
+}
+
+.postCard {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  border-radius: 12px;
+  overflow: hidden;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  transition: all 0.3s ease;
+  background: white;
+}
+
+.postCard:hover {
+  transform: translateY(-4px);
+  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
+}
+
+.coverContainer {
+  width: 100%;
+  height: 200px;
+  overflow: hidden;
+  position: relative;
+  background: #f5f5f5;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.coverImage {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+  object-position: center;
+  transition: transform 0.3s ease;
+  background: #f5f5f5;
+}
+
+.postCard:hover .coverImage {
+  transform: scale(1.02);
+}
+
+.promotionBadge {
+  position: absolute;
+  top: 8px;
+  right: 8px;
+  background: linear-gradient(45deg, #ff6b6b, #ffa500);
+  color: white;
+  padding: 4px 8px;
+  border-radius: 12px;
+  font-size: 11px;
+  font-weight: bold;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  z-index: 10;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+}
+
+.cardContent {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+}
+
+.postTitle {
+  font-size: 16px;
+  font-weight: 600;
+  margin: 0 0 12px 0;
+  line-height: 1.4;
+  color: #262626;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+  height: 44px;
+}
+
+.postMeta {
+  display: flex;
+  align-items: center;
+  font-size: 12px;
+  color: #8c8c8c;
+  margin-bottom: 12px;
+  height: 20px;
+}
+
+.authorName {
+  font-weight: 500;
+  color: #595959;
+}
+
+.publishTime {
+  color: #8c8c8c;
+}
+
+.tagsContainer {
+  margin-bottom: 12px;
+  height: 24px;
+  display: flex;
+  flex-wrap: wrap;
+  gap: 4px;
+  overflow: hidden;
+}
+
+.tag {
+  font-size: 11px;
+  padding: 2px 6px;
+  margin: 0;
+  border-radius: 4px;
+}
+
+.postSummary {
+  flex: 1;
+  font-size: 13px;
+  color: #595959;
+  line-height: 1.5;
+  margin-bottom: 16px;
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+  height: 60px;
+}
+
+.postFooter {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-top: auto;
+  padding-top: 12px;
+  border-top: 1px solid #f0f0f0;
+  height: 32px;
+}
+
+.stats {
+  display: flex;
+  gap: 16px;
+}
+
+.statItem {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  font-size: 12px;
+  color: #8c8c8c;
+}
+
+.statItem .anticon {
+  font-size: 12px;
+}
+
+.readMoreBtn {
+  padding: 0;
+  font-size: 12px;
+  color: #1890ff;
+  font-weight: 500;
+}
+
+.readMoreBtn:hover {
+  color: #40a9ff;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .coverContainer {
+    height: 160px;
+  }
+  
+  .postTitle {
+    font-size: 14px;
+    height: 40px;
+  }
+  
+  .postSummary {
+    font-size: 12px;
+    -webkit-line-clamp: 2;
+    height: 36px;
+  }
+  
+  .stats {
+    gap: 12px;
+  }
+  
+  .statItem {
+    font-size: 11px;
+  }
+} 
\ No newline at end of file
diff --git a/src/pages/PostCenter/PostCard.tsx b/src/pages/PostCenter/PostCard.tsx
new file mode 100644
index 0000000..947ecc3
--- /dev/null
+++ b/src/pages/PostCenter/PostCard.tsx
@@ -0,0 +1,107 @@
+import React from 'react';
+import { Card, Tag, Button, Avatar, Badge } from 'antd';
+import { EyeOutlined, CommentOutlined, UserOutlined, ClockCircleOutlined, HeartOutlined, CrownOutlined } from '@ant-design/icons';
+import { history } from 'umi';
+import styles from './PostCard.module.css';
+import { Post } from '../PostCenter/types';
+
+interface PostCardProps {
+  post: Post;
+}
+
+const PostCard: React.FC<PostCardProps> = ({ post }) => {
+  const {
+    id,
+    title,
+    author,
+    publishTime,
+    tags,
+    views,
+    comments,
+    favorites,
+    likes,
+    coverImage,
+    summary,
+    promotionPlanId,
+    isPromoted
+  } = post;
+
+  const goToDetail = () => {
+    history.push(`/post-detail/${id}`);
+  };
+
+  return (
+    <div className={styles.postCardWrapper}>
+      <Card
+        hoverable
+        cover={
+          <div className={styles.coverContainer}>
+            {isPromoted && (
+              <div className={styles.promotionBadge}>
+                <CrownOutlined />
+                <span>推广</span>
+              </div>
+            )}
+            <img 
+              alt={title} 
+              src={coverImage} 
+              className={styles.coverImage}
+              onError={(e) => {
+                e.currentTarget.src = '/images/404.png';
+              }}
+            />
+          </div>
+        }
+        className={styles.postCard}
+        bodyStyle={{ padding: '16px', height: '240px', display: 'flex', flexDirection: 'column' }}
+      >
+        <div className={styles.cardContent}>
+          <h3 className={styles.postTitle} title={title}>{title}</h3>
+          
+          <div className={styles.postMeta}>
+            <Avatar size="small" style={{ marginRight: 6 }} icon={<UserOutlined />}>
+              {author && author[0]}
+            </Avatar>
+            <span className={styles.authorName}>{author}</span>
+            <ClockCircleOutlined style={{ marginLeft: 12, marginRight: 4 }} />
+            <span className={styles.publishTime}>{publishTime}</span>
+          </div>
+          
+          <div className={styles.tagsContainer}>
+            {(Array.isArray(tags) ? tags : []).slice(0, 3).map(tag => (
+              <Tag color="blue" key={tag} className={styles.tag}>{tag}</Tag>
+            ))}
+            {tags && tags.length > 3 && (
+              <Tag color="default" className={styles.tag}>+{tags.length - 3}</Tag>
+            )}
+          </div>
+          
+          <div className={styles.postSummary} title={summary}>{summary}</div>
+          
+          <div className={styles.postFooter}>
+            <div className={styles.stats}>
+              <span className={styles.statItem}>
+                <EyeOutlined /> {views || 0}
+              </span>
+              <span className={styles.statItem}>
+                <CommentOutlined /> {comments || 0}
+              </span>
+              <span className={styles.statItem}>
+                <HeartOutlined /> {favorites || 0}
+              </span>
+            </div>
+            <Button 
+              type="link" 
+              className={styles.readMoreBtn}
+              onClick={goToDetail}
+            >
+              查看更多 »
+            </Button>
+          </div>
+        </div>
+      </Card>
+    </div>
+  );
+};
+
+export default PostCard; 
\ No newline at end of file
diff --git a/src/pages/PostCenter/PostDetail.module.css b/src/pages/PostCenter/PostDetail.module.css
new file mode 100644
index 0000000..da072d1
--- /dev/null
+++ b/src/pages/PostCenter/PostDetail.module.css
@@ -0,0 +1,233 @@
+.postDetailContainer {
+  max-width: 1200px;
+  margin: 0 auto;
+  background: #f5f5f5;
+  min-height: 100vh;
+}
+
+.postCoverSection {
+  margin-bottom: 24px;
+  border-radius: 0 0 16px 16px;
+  overflow: hidden;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.coverImageContainer {
+  position: relative;
+  width: 100%;
+  height: 400px;
+  overflow: hidden;
+  background: #f0f0f0;
+}
+
+.coverImage {
+  width: 100% !important;
+  height: 100% !important;
+  object-fit: cover !important;
+  transition: transform 0.3s ease;
+}
+
+.coverImage:hover {
+  transform: scale(1.02);
+}
+
+.coverOverlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  pointer-events: none;
+}
+
+.coverGradient {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  height: 100px;
+  background: linear-gradient(transparent, rgba(0, 0, 0, 0.3));
+}
+
+.previewMask {
+  background: rgba(0, 0, 0, 0.5);
+  color: white;
+  padding: 8px 16px;
+  border-radius: 4px;
+  font-size: 14px;
+}
+
+.postDetailHeader {
+  margin: 0 24px 24px;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.titleContainer {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+  margin-bottom: 16px;
+}
+
+.titleContainer h2 {
+  margin: 0;
+  flex: 1;
+}
+
+.promotionBadge {
+  background: linear-gradient(45deg, #ff6b6b, #ffa500);
+  color: white;
+  padding: 6px 12px;
+  border-radius: 16px;
+  font-size: 12px;
+  font-weight: bold;
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+  white-space: nowrap;
+}
+
+.postMeta {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 24px;
+  margin: 16px 0;
+  padding: 16px 0;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.postAuthor,
+.postTime,
+.postViews,
+.postTags {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.postActions {
+  display: flex;
+  gap: 12px;
+  flex-wrap: wrap;
+  margin-top: 16px;
+}
+
+.postContent {
+  margin: 0 24px 24px;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.postContent img {
+  max-width: 100%;
+  height: auto;
+}
+
+.commentSection {
+  margin: 0 24px 24px;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.commentInput {
+  margin-bottom: 24px;
+}
+
+.commentActions {
+  display: flex;
+  justify-content: flex-end;
+  gap: 12px;
+  margin-top: 12px;
+}
+
+.commentList {
+  margin-top: 24px;
+}
+
+.replyList {
+  margin-top: 16px;
+  padding-left: 24px;
+  border-left: 2px solid #f0f0f0;
+}
+
+.relatedPosts {
+  margin: 0 24px 24px;
+  padding: 24px;
+  background: white;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.recommendHeader {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 24px;
+}
+
+.recommendHeader h4 {
+  margin: 0;
+}
+
+.postDetailLoading {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  min-height: 400px;
+  color: #666;
+}
+
+.postDetailError {
+  text-align: center;
+  padding: 60px 24px;
+  color: #999;
+  font-size: 16px;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .postDetailContainer {
+    margin: 0;
+  }
+  
+  .coverImageContainer {
+    height: 250px;
+  }
+  
+  .postDetailHeader,
+  .postContent,
+  .commentSection,
+  .relatedPosts {
+    margin: 0 12px 16px;
+    border-radius: 8px;
+  }
+  
+  .postMeta {
+    flex-direction: column;
+    gap: 12px;
+  }
+  
+  .postActions {
+    flex-direction: column;
+  }
+  
+  .postActions button {
+    width: 100%;
+  }
+}
+
+@media (max-width: 480px) {
+  .coverImageContainer {
+    height: 200px;
+  }
+  
+  .postDetailHeader,
+  .postContent,
+  .commentSection,
+  .relatedPosts {
+    margin: 0 8px 12px;
+  }
+} 
\ No newline at end of file
diff --git a/src/pages/PostCenter/PostDetail.tsx b/src/pages/PostCenter/PostDetail.tsx
new file mode 100644
index 0000000..c6aedcc
--- /dev/null
+++ b/src/pages/PostCenter/PostDetail.tsx
@@ -0,0 +1,547 @@
+import React, { useState, useEffect } from 'react';
+import { useParams, Link } from 'umi';
+import { Card, Avatar, Tag, Row, Col, Divider, List, Input, Button, Typography, message, Spin, Image, Modal, Badge, Pagination } from 'antd';
+import { Comment } from '@ant-design/compatible';
+import { 
+  UserOutlined, 
+  ClockCircleOutlined, 
+  EyeOutlined, 
+  TagOutlined,
+  LikeOutlined,
+  DislikeOutlined,
+  ShareAltOutlined,
+  HeartOutlined,
+  HeartFilled,
+  ExclamationCircleOutlined,
+  CrownOutlined
+} from '@ant-design/icons';
+import { getPostDetail, addComment, toggleFavorite, toggleLike, toggleCommentLike, reportPost } from '@/services/post';
+import PostCard from '../PostCenter/PostCard';
+import styles from './PostDetail.module.css';
+import { Post, CommentType } from '../PostCenter/types';
+
+const { TextArea } = Input;
+const { Title, Paragraph, Text } = Typography;
+
+const PostDetail: React.FC = () => {
+  const { id } = useParams<{ id: string }>();
+  const [post, setPost] = useState<Post | null>(null);
+  const [recommendedPosts, setRecommendedPosts] = useState<Post[]>([]);
+  const [currentRecommendPage, setCurrentRecommendPage] = useState<number>(1);
+  const [comments, setComments] = useState<CommentType[]>([]);
+  const [replyTo, setReplyTo] = useState<number | null>(null);
+  const [commentText, setCommentText] = useState<string>('');
+  const [loading, setLoading] = useState<boolean>(true);
+  const [favorited, setFavorited] = useState<boolean>(false);
+  const [liked, setLiked] = useState<boolean>(false);
+  const [submittingComment, setSubmittingComment] = useState<boolean>(false);
+  const [commentLikes, setCommentLikes] = useState<Record<number, boolean>>({});
+  const [reportModalVisible, setReportModalVisible] = useState<boolean>(false);
+  const [reportReason, setReportReason] = useState<string>('');
+  const [submittingReport, setSubmittingReport] = useState<boolean>(false);
+
+  const recommendPageSize = 3; // 每页显示3个推荐帖子
+
+  useEffect(() => {
+    if (id) {
+      fetchPostDetail(Number(id));
+    }
+  }, [id]);
+
+  const fetchPostDetail = async (postId: number) => {
+    try {
+      setLoading(true);
+      const response = await getPostDetail(postId);
+      
+      if (response.code === 200 && response.data) {
+        const { post: postData, tags, comments: commentsData, recommendedPosts, favorited: isFavorited } = response.data;
+        
+        // 转换帖子数据格式
+        const formattedPost: Post = {
+          ...postData,
+          id: postData.postId,
+          title: postData.title || '无标题',
+          author: postData.author || '未知作者',
+          publishTime: postData.publishTime || postData.createTime || '',
+          tags: postData.tags ? postData.tags.split(',') : [],
+          views: postData.views || 0,
+          comments: postData.comments || 0,
+          favorites: postData.favorites || 0,
+          likes: postData.likes || 0,
+          coverImage: postData.coverImage || '/images/404.png',
+          summary: postData.summary || '暂无摘要',
+          isPromoted: postData.promotionPlanId != null && postData.promotionPlanId > 0,
+        };
+        setPost(formattedPost);
+        setFavorited(isFavorited);
+
+        // 转换评论数据格式
+        const formattedComments = commentsData.map((commentItem: any) => {
+          const comment = commentItem.comment;
+          const replies = commentItem.replies || [];
+          
+          return {
+            ...comment,
+            id: comment.commentId,
+            author: comment.userName || '匿名用户',
+            avatar: comment.userAvatar || '/images/404.png',
+            datetime: comment.createTime,
+            likes: comment.likes || 0,
+            replies: replies.map((reply: any) => ({
+              ...reply,
+              id: reply.commentId,
+              author: reply.userName || '匿名用户',
+              avatar: reply.userAvatar || '/images/404.png',
+              datetime: reply.createTime,
+              likes: reply.likes || 0,
+              replies: []
+            }))
+          };
+        });
+        setComments(formattedComments);
+
+        // 转换推荐帖子数据格式
+        const formatPosts = (posts: any[]) => posts.map((p: any) => ({
+          ...p,
+          id: p.postId,
+          title: p.title || '无标题',
+          author: p.author || '未知作者',
+          publishTime: p.publishTime || p.createTime || '',
+          tags: p.tags ? p.tags.split(',') : [],
+          views: p.views || 0,
+          comments: p.comments || 0,
+          favorites: p.favorites || 0,
+          likes: p.likes || 0,
+          coverImage: p.coverImage || '/images/404.png',
+          summary: p.summary || '暂无摘要',
+          isPromoted: p.promotionPlanId != null && p.promotionPlanId > 0, // 添加推广标识
+        }));
+
+        // 设置推荐帖子
+        const formattedRecommendedPosts = formatPosts(recommendedPosts || []);
+        setRecommendedPosts(formattedRecommendedPosts);
+      } else {
+        message.error(response.msg || '获取帖子详情失败');
+      }
+    } catch (error) {
+      console.error('获取帖子详情失败:', error);
+      message.error('获取帖子详情失败,请稍后重试');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleCommentSubmit = async () => {
+    if (!commentText.trim()) {
+      message.warning('评论内容不能为空');
+      return;
+    }
+
+    if (!post) return;
+
+    try {
+      setSubmittingComment(true);
+      const response = await addComment({
+        postId: Number(post.id),
+        content: commentText,
+        parentId: replyTo || 0,
+      });
+
+      if (response.code === 200) {
+        message.success('评论发表成功');
+        setCommentText('');
+        setReplyTo(null);
+        // 重新获取帖子详情以更新评论列表
+        fetchPostDetail(Number(post.id));
+      } else {
+        message.error(response.msg || '评论发表失败');
+      }
+    } catch (error) {
+      console.error('评论发表失败:', error);
+      message.error('评论发表失败,请稍后重试');
+    } finally {
+      setSubmittingComment(false);
+    }
+  };
+
+  const handleFavoriteToggle = async () => {
+    if (!post) return;
+
+    try {
+      const response = await toggleFavorite(Number(post.id), !favorited);
+      
+      if (response.code === 200) {
+        setFavorited(!favorited);
+        message.success(favorited ? '取消收藏成功' : '收藏成功');
+        // 更新帖子收藏数
+        setPost(prev => prev ? {
+          ...prev,
+          favorites: (prev.favorites || 0) + (favorited ? -1 : 1)
+        } : null);
+      } else {
+        message.error(response.msg || '操作失败');
+      }
+    } catch (error) {
+      console.error('收藏操作失败:', error);
+      message.error('操作失败,请稍后重试');
+    }
+  };
+
+  const handleLikeToggle = async () => {
+    if (!post) return;
+
+    try {
+      const response = await toggleLike(Number(post.id), !liked);
+      
+      if (response.code === 200) {
+        setLiked(!liked);
+        message.success(liked ? '取消点赞成功' : '点赞成功');
+        // 更新帖子点赞数
+        setPost(prev => prev ? {
+          ...prev,
+          likes: (prev.likes || 0) + (liked ? -1 : 1)
+        } : null);
+      } else {
+        message.error(response.msg || '操作失败');
+      }
+    } catch (error) {
+      console.error('点赞操作失败:', error);
+      message.error('操作失败,请稍后重试');
+    }
+  };
+
+  const handleReply = (commentId: number) => {
+    setReplyTo(commentId);
+  };
+
+  const cancelReply = () => {
+    setReplyTo(null);
+  };
+
+  const handleCommentLike = async (commentId: number) => {
+    try {
+      const isLiked = commentLikes[commentId] || false;
+      const response = await toggleCommentLike(commentId, !isLiked);
+      
+      if (response.code === 200) {
+        setCommentLikes(prev => ({
+          ...prev,
+          [commentId]: !isLiked
+        }));
+        
+        // 更新评论点赞数
+        setComments(prev => prev.map(comment => {
+          if (comment.id === commentId) {
+            return {
+              ...comment,
+              likes: (comment.likes || 0) + (isLiked ? -1 : 1)
+            };
+          }
+          // 检查回复
+          if (comment.replies) {
+            const updatedReplies = comment.replies.map(reply => {
+              if (reply.id === commentId) {
+                return {
+                  ...reply,
+                  likes: (reply.likes || 0) + (isLiked ? -1 : 1)
+                };
+              }
+              return reply;
+            });
+            return { ...comment, replies: updatedReplies };
+          }
+          return comment;
+        }));
+        
+        message.success(isLiked ? '取消点赞成功' : '点赞成功');
+      } else {
+        message.error(response.msg || '操作失败');
+      }
+    } catch (error) {
+      console.error('评论点赞操作失败:', error);
+      message.error('操作失败,请稍后重试');
+    }
+  };
+
+  const handleReport = async () => {
+    if (!post) return;
+    
+    if (!reportReason.trim()) {
+      message.warning('请填写举报理由');
+      return;
+    }
+
+    try {
+      setSubmittingReport(true);
+      const response = await reportPost(Number(post.id), reportReason);
+      
+      if (response.code === 200) {
+        message.success('举报提交成功,我们会尽快处理');
+        setReportModalVisible(false);
+        setReportReason('');
+      } else {
+        message.error(response.msg || '举报提交失败');
+      }
+    } catch (error) {
+      console.error('举报提交失败:', error);
+      message.error('举报提交失败,请稍后重试');
+    } finally {
+      setSubmittingReport(false);
+    }
+  };
+
+  if (loading) {
+    return (
+      <div className={styles.postDetailLoading}>
+        <Spin size="large" />
+        <div style={{ marginTop: 16 }}>加载中...</div>
+      </div>
+    );
+  }
+
+  if (!post) {
+    return <div className={styles.postDetailError}>帖子不存在或已被删除</div>;
+  }
+
+  return (
+    <div className={styles.postDetailContainer}>
+      {/* 帖子封面图片 */}
+      {post.coverImage && (
+        <div className={styles.postCoverSection}>
+          <div className={styles.coverImageContainer}>
+            <Image
+              src={post.coverImage}
+              alt={post.title}
+              className={styles.coverImage}
+              preview={{
+                mask: <div className={styles.previewMask}>点击预览</div>
+              }}
+              onError={(e) => {
+                e.currentTarget.src = '/images/404.png';
+              }}
+            />
+            <div className={styles.coverOverlay}>
+              <div className={styles.coverGradient}></div>
+            </div>
+          </div>
+        </div>
+      )}
+
+      {/* 帖子头部信息 */}
+      <Card className={styles.postDetailHeader}>
+        <div className={styles.titleContainer}>
+        <Title level={2}>{post.title}</Title>
+          {post.isPromoted && (
+            <div className={styles.promotionBadge}>
+              <CrownOutlined />
+              <span>推广</span>
+            </div>
+          )}
+        </div>
+        
+        <div className={styles.postMeta}>
+          <div className={styles.postAuthor}>
+            <Avatar size="small" icon={<UserOutlined />} />
+            <Text strong style={{ marginLeft: 8 }}>{post.author}</Text>
+          </div>
+          
+          <div className={styles.postTime}>
+            <ClockCircleOutlined />
+            <Text type="secondary" style={{ marginLeft: 8 }}>{post.publishTime}</Text>
+          </div>
+          
+          <div className={styles.postViews}>
+            <EyeOutlined />
+            <Text type="secondary" style={{ marginLeft: 8 }}>{post.views} 查看</Text>
+          </div>
+          
+          <div className={styles.postTags}>
+            <TagOutlined />
+            <span style={{ marginLeft: 8 }}>
+              {post.tags.map(tag => (
+                <Tag key={tag} color="blue">{tag}</Tag>
+              ))}
+            </span>
+          </div>
+        </div>
+
+        {/* 操作按钮 */}
+        <div className={styles.postActions}>
+          <Button 
+            type={favorited ? "primary" : "default"}
+            icon={favorited ? <HeartFilled /> : <HeartOutlined />}
+            onClick={handleFavoriteToggle}
+          >
+            {favorited ? '已收藏' : '收藏'} ({post.favorites || 0})
+          </Button>
+          <Button 
+            type={liked ? "primary" : "default"}
+            icon={<LikeOutlined />}
+            onClick={handleLikeToggle}
+          >
+            {liked ? '已点赞' : '点赞'} ({post.likes || 0})
+          </Button>
+          {/* <Button icon={<ShareAltOutlined />}>
+            分享
+          </Button> */}
+          <Button 
+            icon={<ExclamationCircleOutlined />}
+            onClick={() => setReportModalVisible(true)}
+          >
+            举报
+          </Button>
+        </div>
+      </Card>
+
+      {/* 帖子内容 */}
+      <Card className={styles.postContent}>
+        <div dangerouslySetInnerHTML={{ __html: post.content || post.summary || '' }} />
+      </Card>
+
+      {/* 评论区 */}
+      <Card className={styles.commentSection}>
+        <Title level={4}>评论 ({comments.length})</Title>
+        
+        <div className={styles.commentInput}>
+          <TextArea
+            value={commentText}
+            onChange={e => setCommentText(e.target.value)}
+            placeholder={replyTo ? '回复评论...' : '写下你的评论...'}
+            rows={4}
+          />
+          <div className={styles.commentActions}>
+            {replyTo && (
+              <Button onClick={cancelReply}>取消回复</Button>
+            )}
+            <Button 
+              type="primary" 
+              onClick={handleCommentSubmit}
+              loading={submittingComment}
+            >
+              发表{replyTo ? '回复' : '评论'}
+            </Button>
+          </div>
+        </div>
+
+        <List
+          className={styles.commentList}
+          itemLayout="horizontal"
+          dataSource={comments}
+          renderItem={comment => (
+            <li>
+              <Comment
+                author={comment.author}
+                avatar={comment.avatar}
+                content={comment.content}
+                datetime={comment.datetime}
+                actions={[
+                  <span key="like">
+                    <Button 
+                      type={commentLikes[comment.id] ? "primary" : "text"}
+                      size="small" 
+                      icon={<LikeOutlined />}
+                      onClick={() => handleCommentLike(comment.id)}
+                    >
+                      {comment.likes || 0}
+                    </Button>
+                  </span>,
+                  <span key="reply" onClick={() => handleReply(comment.id)}>回复</span>
+                ]}
+              >
+                {comment.replies && comment.replies.length > 0 && (
+                  <List
+                    className={styles.replyList}
+                    itemLayout="horizontal"
+                    dataSource={comment.replies}
+                    renderItem={reply => (
+                      <li>
+                        <Comment
+                          author={reply.author}
+                          avatar={reply.avatar}
+                          content={reply.content}
+                          datetime={reply.datetime}
+                          actions={[
+                            <span key="like">
+                              <Button 
+                                type={commentLikes[reply.id] ? "primary" : "text"}
+                                size="small" 
+                                icon={<LikeOutlined />}
+                                onClick={() => handleCommentLike(reply.id)}
+                              >
+                                {reply.likes || 0}
+                              </Button>
+                            </span>
+                          ]}
+                        />
+                      </li>
+                    )}
+                  />
+                )}
+              </Comment>
+            </li>
+          )}
+        />
+      </Card>
+
+      {/* 举报弹窗 */}
+      <Modal
+        title="举报帖子"
+        open={reportModalVisible}
+        onOk={handleReport}
+        onCancel={() => {
+          setReportModalVisible(false);
+          setReportReason('');
+        }}
+        confirmLoading={submittingReport}
+        okText="提交举报"
+        cancelText="取消"
+      >
+        <div style={{ marginBottom: 16 }}>
+          <strong>帖子:</strong>{post?.title}
+        </div>
+        <div>
+          <strong>举报理由:</strong>
+          <TextArea
+            value={reportReason}
+            onChange={(e) => setReportReason(e.target.value)}
+            placeholder="请详细描述举报理由..."
+            rows={4}
+            style={{ marginTop: 8 }}
+          />
+        </div>
+      </Modal>
+
+      {/* 相关推荐 */}
+      {recommendedPosts.length > 0 && (
+        <div className={styles.relatedPosts}>
+          <div className={styles.recommendHeader}>
+          <Title level={4}>相关推荐</Title>
+            {recommendedPosts.length > recommendPageSize && (
+              <Pagination
+                current={currentRecommendPage}
+                total={recommendedPosts.length}
+                pageSize={recommendPageSize}
+                onChange={(page) => setCurrentRecommendPage(page)}
+                showSizeChanger={false}
+                showQuickJumper={false}
+                showTotal={(total, range) => `${range[0]}-${range[1]} / ${total}`}
+                size="small"
+              />
+            )}
+          </div>
+          <Row gutter={[24, 24]}>
+            {recommendedPosts
+              .slice((currentRecommendPage - 1) * recommendPageSize, currentRecommendPage * recommendPageSize)
+              .map(post => (
+              <Col xs={24} sm={12} md={8} key={post.id}>
+                <PostCard post={post} />
+              </Col>
+            ))}
+          </Row>
+        </div>
+      )}
+    </div>
+  );
+};
+
+export default PostDetail; 
\ No newline at end of file
diff --git a/src/pages/PostCenter/index.module.css b/src/pages/PostCenter/index.module.css
new file mode 100644
index 0000000..2b262e2
--- /dev/null
+++ b/src/pages/PostCenter/index.module.css
@@ -0,0 +1,189 @@
+.postCenterContainer {
+  min-height: 100vh;
+  background-color: #f5f5f5;
+}
+
+.headerNav {
+  background: white;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  padding: 0 24px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  height: 64px;
+  position: sticky;
+  top: 0;
+  z-index: 100;
+}
+
+.categoryMenu {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.categoryButton {
+  border: none;
+  box-shadow: none;
+  font-weight: 500;
+  padding: 8px 16px;
+  border-radius: 6px;
+  transition: all 0.3s ease;
+}
+
+.categoryButton:hover {
+  background-color: #f0f0f0;
+}
+
+.searchContainer {
+  flex: 1;
+  display: flex;
+  justify-content: center;
+  max-width: 400px;
+  margin: 0 24px;
+}
+
+.userCenter {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+}
+
+.carouselContainer {
+  margin: 24px;
+  border-radius: 12px;
+  overflow: hidden;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.carouselSlide {
+  position: relative;
+  display: flex !important;
+  align-items: center;
+  justify-content: center;
+  min-height: 300px;
+}
+
+.carouselOverlay {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background: linear-gradient(transparent, rgba(0, 0, 0, 0.8));
+  color: white;
+  padding: 40px 40px 24px;
+  text-align: left;
+}
+
+.carouselTitle {
+  font-size: 28px;
+  font-weight: bold;
+  margin: 0 0 12px 0;
+  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
+}
+
+.carouselSummary {
+  font-size: 16px;
+  margin: 0 0 16px 0;
+  opacity: 0.9;
+  line-height: 1.5;
+}
+
+.carouselMeta {
+  display: flex;
+  gap: 24px;
+  font-size: 14px;
+  opacity: 0.8;
+}
+
+.postsSection {
+  padding: 24px;
+}
+
+.categoryTitle {
+  margin-bottom: 24px;
+  text-align: center;
+}
+
+.categoryTitle h2 {
+  font-size: 24px;
+  margin: 0 0 8px 0;
+  color: #1890ff;
+}
+
+.categoryTitle p {
+  margin: 0;
+  color: #666;
+  font-size: 14px;
+}
+
+.postsRow {
+  margin-bottom: 32px;
+}
+
+.postCol {
+  display: flex;
+  height: 480px; /* 固定卡片高度 */
+}
+
+.postCol > * {
+  width: 100%;
+}
+
+.emptyState {
+  text-align: center;
+  padding: 60px 0;
+  color: #999;
+  font-size: 16px;
+}
+
+.paginationContainer {
+  display: flex;
+  justify-content: center;
+  margin-top: 32px;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .headerNav {
+    flex-direction: column;
+    height: auto;
+    padding: 12px;
+    gap: 12px;
+  }
+  
+  .categoryMenu {
+    flex-wrap: wrap;
+    justify-content: center;
+  }
+  
+  .searchContainer {
+    margin: 0;
+    max-width: 100%;
+  }
+  
+  .carouselContainer {
+    margin: 12px;
+  }
+  
+  .carouselOverlay {
+    padding: 20px;
+  }
+  
+  .carouselTitle {
+    font-size: 20px;
+  }
+  
+  .carouselSummary {
+    font-size: 14px;
+  }
+  
+  .carouselMeta {
+    flex-direction: column;
+    gap: 8px;
+  }
+  
+  .postsSection {
+    padding: 12px;
+  }
+} 
\ No newline at end of file
diff --git a/src/pages/PostCenter/index.tsx b/src/pages/PostCenter/index.tsx
new file mode 100644
index 0000000..b81efb0
--- /dev/null
+++ b/src/pages/PostCenter/index.tsx
@@ -0,0 +1,301 @@
+import React, { useEffect, useState } from 'react';
+import { Row, Col, Pagination, Input, Carousel, Menu, Card, message, Button } from 'antd';
+import { SearchOutlined, AuditOutlined } from '@ant-design/icons';
+import { getPostList, getPromotionPosts } from '@/services/post';
+import PostCard from '../PostCenter/PostCard';
+import styles from './index.module.css';
+import { Post } from '../PostCenter/types';
+import { useNavigate } from 'react-router-dom';
+import { useModel } from 'umi';
+
+const { Search } = Input;
+
+const PostCenter: React.FC = () => {
+  const [posts, setPosts] = useState<Post[]>([]);
+  const [promotionPosts, setPromotionPosts] = useState<Post[]>([]);
+  const [total, setTotal] = useState<number>(0);
+  const [page, setPage] = useState<number>(1);
+  const [loading, setLoading] = useState<boolean>(false);
+  const [selectedCategory, setSelectedCategory] = useState<string>('all');
+  const [searchKeyword, setSearchKeyword] = useState<string>('');
+  const pageSize = 12;
+  const navigate = useNavigate();
+  const { initialState } = useModel('@@initialState');
+
+  // 检查是否为管理员 - 用户名包含admin
+  const isAdmin = initialState?.currentUser?.userName?.toLowerCase().includes('admin') || false;
+
+  const fetchPosts = async (current: number = 1, category?: string, searchTitle?: string) => {
+    try {
+      setLoading(true);
+      const params: any = {
+        pageNum: current,
+        pageSize: pageSize,
+        status: '1', // 只查询正常状态的帖子
+      };
+
+      // 根据分类筛选
+      if (category && category !== 'all') {
+        params.tags = category;
+      }
+
+      // 搜索关键词
+      if (searchTitle) {
+        params.title = searchTitle;
+      }
+
+      const response = await getPostList(params);
+      
+      if (response.code === 200) {
+        // 确保返回的数据符合 Post 类型
+        const formattedPosts = (response.rows || []).map((post: API.Post.PostInfo) => ({
+          ...post,
+          id: post.postId, // 确保id字段映射正确
+          tags: post.tags ? post.tags.split(',') : [],
+          views: post.views || 0,
+          comments: post.comments || 0,
+          favorites: post.favorites || 0,
+          likes: post.likes || 0,
+          coverImage: post.coverImage || '/images/404.png', // 使用本地默认图片
+          isPromoted: post.promotionPlanId != null && post.promotionPlanId > 0, // 添加推广标识
+        }));
+        setPosts(formattedPosts);
+        setTotal(response.total || 0);
+      } else {
+        message.error(response.msg || '获取帖子列表失败');
+        setPosts([]);
+        setTotal(0);
+      }
+    } catch (error) {
+      console.error('获取帖子失败:', error);
+      message.error('获取帖子列表失败,请稍后重试');
+      setPosts([]);
+      setTotal(0);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const fetchPromotionPosts = async () => {
+    try {
+      const response = await getPromotionPosts();
+      if (response.code === 200) {
+        const formattedPosts = (response.data || []).map((post: API.Post.PostInfo) => ({
+          ...post,
+          id: post.postId,
+          tags: post.tags ? post.tags.split(',') : [],
+          views: post.views || 0,
+          comments: post.comments || 0,
+          favorites: post.favorites || 0,
+          likes: post.likes || 0,
+          coverImage: post.coverImage || '/images/404.png',
+          isPromoted: post.promotionPlanId != null && post.promotionPlanId > 0, // 添加推广标识
+        }));
+        setPromotionPosts(formattedPosts);
+      }
+    } catch (error) {
+      console.error('获取推广帖子失败:', error);
+    }
+  };
+
+  useEffect(() => {
+    fetchPosts(page, selectedCategory, searchKeyword);
+    fetchPromotionPosts();
+  }, [page, selectedCategory]);
+
+  const handleSearch = (value: string) => {
+    console.log('搜索:', value);
+    setSearchKeyword(value);
+    setPage(1); // 重置页码
+    fetchPosts(1, selectedCategory, value);
+  };
+
+  const handlePageChange = (newPage: number) => {
+    setPage(newPage);
+    fetchPosts(newPage, selectedCategory, searchKeyword);
+  };
+
+  const handleCategoryChange = (category: string) => {
+    setSelectedCategory(category);
+    setPage(1); // 重置页码
+    setSearchKeyword(''); // 清空搜索
+    fetchPosts(1, category, '');
+  };
+
+  return (
+    <div className={styles.postCenterContainer}>
+      {/* 顶部导航 */}
+      <div className={styles.headerNav}>
+        <div className={styles.categoryMenu}>
+          <Button 
+            type={selectedCategory === 'all' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('all')}
+            className={styles.categoryButton}
+          >
+            首页
+          </Button>
+          <Button 
+            type={selectedCategory === '日剧' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('日剧')}
+            className={styles.categoryButton}
+          >
+            日剧
+          </Button>
+          <Button 
+            type={selectedCategory === '电影' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('电影')}
+            className={styles.categoryButton}
+          >
+            电影
+          </Button>
+          <Button 
+            type={selectedCategory === '音乐' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('音乐')}
+            className={styles.categoryButton}
+          >
+            音乐
+          </Button>
+          <Button 
+            type={selectedCategory === '合集' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('合集')}
+            className={styles.categoryButton}
+          >
+            合集
+          </Button>
+          <Button 
+            type={selectedCategory === '动漫' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('动漫')}
+            className={styles.categoryButton}
+          >
+            动漫
+          </Button>
+          <Button 
+            type={selectedCategory === '游戏' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('游戏')}
+            className={styles.categoryButton}
+          >
+            游戏
+          </Button>
+        </div>
+        
+        <div className={styles.searchContainer}>
+          <Search
+            placeholder="搜索帖子..."
+            onSearch={handleSearch}
+            style={{ width: 300 }}
+            enterButton={<SearchOutlined />}
+            value={searchKeyword}
+            onChange={(e) => setSearchKeyword(e.target.value)}
+          />
+        </div>
+        
+        <div className={styles.userCenter}>
+          {isAdmin && (
+            <Button 
+              icon={<AuditOutlined />}
+              onClick={() => navigate('/post-review')}
+              style={{ marginRight: 16 }}
+            >
+              帖子审核
+            </Button>
+          )}
+          <Button 
+            type="primary" 
+            onClick={() => navigate('/user-center')}
+          >
+            个人中心
+          </Button>
+        </div>
+      </div>
+
+      {/* 轮播推荐图 */}
+      <div className={styles.carouselContainer}>
+        <Carousel autoplay>
+          {promotionPosts.length > 0 ? (
+            promotionPosts.map((post) => (
+              <div key={post.id} onClick={() => navigate(`/post-detail/${post.id}`)}>
+                <div 
+                  className={styles.carouselSlide}
+                  style={{
+                    backgroundImage: `linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.4)), url(${post.coverImage})`,
+                    backgroundSize: 'cover',
+                    backgroundPosition: 'center',
+                    height: '300px',
+                    position: 'relative',
+                    cursor: 'pointer'
+                  }}
+                >
+                  <div className={styles.carouselOverlay}>
+                    <h2 className={styles.carouselTitle}>{post.title}</h2>
+                    <p className={styles.carouselSummary}>{post.summary}</p>
+                    <div className={styles.carouselMeta}>
+                      <span>作者: {post.author}</span>
+                      <span>浏览: {post.views}</span>
+                      <span>点赞: {post.likes}</span>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            ))
+          ) : (
+            // 默认轮播图
+            <>
+              <div>
+                <div 
+                  className={styles.carouselSlide}
+                  style={{
+                    backgroundImage: `url(/images/flower.jpg)`,
+                    backgroundSize: 'cover',
+                    backgroundPosition: 'center',
+                    height: '300px',
+                  }}
+                >
+                  <div className={styles.carouselOverlay}>
+                    <h2 className={styles.carouselTitle}>欢迎来到ThunderHub</h2>
+                    <p className={styles.carouselSummary}>发现精彩内容,分享美好时光</p>
+                  </div>
+                </div>
+              </div>
+            </>
+          )}
+        </Carousel>
+      </div>
+
+      {/* 卡片帖子区 */}
+      <div className={styles.postsSection}>
+        {selectedCategory !== 'all' && (
+          <div className={styles.categoryTitle}>
+            <h2>{selectedCategory} 分类</h2>
+            <p>共找到 {total} 篇相关帖子</p>
+          </div>
+        )}
+        
+        <Row gutter={[24, 24]} className={styles.postsRow}>
+          {posts.map((post) => (
+            <Col xs={24} sm={12} lg={8} xl={6} key={post.id} className={styles.postCol}>
+              <PostCard post={post} />
+            </Col>
+          ))}
+        </Row>
+
+        {posts.length === 0 && !loading && (
+          <div className={styles.emptyState}>
+            <p>暂无相关帖子</p>
+          </div>
+        )}
+
+        <div className={styles.paginationContainer}>
+          <Pagination
+            current={page}
+            pageSize={pageSize}
+            total={total}
+            onChange={handlePageChange}
+            showTotal={(total) => `共 ${total} 条帖子`}
+          />
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export default PostCenter;
\ No newline at end of file
diff --git a/src/pages/PostCenter/types.ts b/src/pages/PostCenter/types.ts
new file mode 100644
index 0000000..4cfe237
--- /dev/null
+++ b/src/pages/PostCenter/types.ts
@@ -0,0 +1,55 @@
+export interface Post {
+  postId?: number;
+  id: string | number;
+  title: string;
+  author: string;
+  publishTime: string;
+  tags: string[];
+  views: number;
+  comments?: number;
+  favorites?: number;
+  likes?: number;
+  coverImage?: string;
+  summary?: string;
+  content?: string;
+  status?: string;
+  authorId?: number;
+  createTime?: string;
+  updateTime?: string;
+  promotionPlanId?: number;
+  isPromoted?: boolean;
+}
+
+export interface CommentType {
+  commentId?: number;
+  id: number;
+  author: string;
+  avatar: string;
+  content: string;
+  datetime: string;
+  replies: CommentType[];
+  likes?: number;
+  parentId?: number;
+  postId?: number;
+  userId?: number;
+  userName?: string;
+  userAvatar?: string;
+  status?: string;
+}
+
+export interface PostTag {
+  tagId: number;
+  tagName: string;
+  tagColor?: string;
+  postCount: number;
+  status?: string;
+}
+
+export interface PostFavorite {
+  favoriteId: number;
+  postId: number;
+  userId: number;
+  postTitle?: string;
+  postCover?: string;
+  status: string;
+} 
\ No newline at end of file
diff --git a/src/pages/PostReview/ReportManagement.tsx b/src/pages/PostReview/ReportManagement.tsx
new file mode 100644
index 0000000..f5c0e99
--- /dev/null
+++ b/src/pages/PostReview/ReportManagement.tsx
@@ -0,0 +1,325 @@
+import React, { useState, useEffect } from 'react';
+import { 
+  Card, 
+  Table, 
+  Button, 
+  Modal, 
+  Tag, 
+  Space, 
+  message, 
+  Typography,
+  Input
+} from 'antd';
+import { 
+  EyeOutlined, 
+  CheckOutlined, 
+  CloseOutlined
+} from '@ant-design/icons';
+import { getReportList, handleReport } from '@/services/post';
+import styles from './index.module.css';
+
+const { Title, Paragraph } = Typography;
+const { TextArea } = Input;
+
+interface ReportInfo {
+  reportId: number;
+  postId: number;
+  postTitle: string;
+  reportUserId: number;
+  reportUserName: string;
+  reportReason: string;
+  status: string;
+  handleResult?: string;
+  handleTime?: string;
+  handleBy?: string;
+  createTime: string;
+}
+
+const ReportManagement: React.FC = () => {
+  const [reports, setReports] = useState<ReportInfo[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [detailModalVisible, setDetailModalVisible] = useState(false);
+  const [handleModalVisible, setHandleModalVisible] = useState(false);
+  const [currentReport, setCurrentReport] = useState<ReportInfo | null>(null);
+  const [currentAction, setCurrentAction] = useState<'approve' | 'reject' | null>(null);
+  const [handleReason, setHandleReason] = useState('');
+
+  useEffect(() => {
+    fetchReports();
+  }, []);
+
+  const fetchReports = async () => {
+    setLoading(true);
+    try {
+      const response = await getReportList({ 
+        pageNum: 1, 
+        pageSize: 100
+      });
+      
+      if (response.code === 200) {
+        setReports(response.rows || []);
+      } else {
+        message.error(response.msg || '获取举报列表失败');
+      }
+    } catch (error) {
+      message.error('获取举报列表失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleReportAction = async (report: ReportInfo, action: 'approve' | 'reject') => {
+    setCurrentReport(report);
+    setCurrentAction(action);
+    setHandleReason('');
+    setHandleModalVisible(true);
+  };
+
+  const submitHandle = async () => {
+    if (!currentReport || !currentAction) return;
+
+    try {
+      const response = await handleReport(
+        currentReport.reportId, 
+        currentAction, 
+        currentReport.postId,
+        handleReason
+      );
+
+      if (response.code === 200) {
+        message.success(currentAction === 'approve' ? '举报处理成功,帖子已下架' : '举报已驳回');
+        fetchReports();
+      } else {
+        message.error(response.msg || '处理失败');
+      }
+    } catch (error) {
+      message.error('处理失败');
+    }
+    
+    setHandleModalVisible(false);
+    setCurrentReport(null);
+    setCurrentAction(null);
+    setHandleReason('');
+  };
+
+  const showDetail = (report: ReportInfo) => {
+    setCurrentReport(report);
+    setDetailModalVisible(true);
+  };
+
+  const columns = [
+    {
+      title: '举报帖子',
+      dataIndex: 'postTitle',
+      key: 'postTitle',
+      width: 180,
+      render: (text: string, record: ReportInfo) => (
+        <a onClick={() => showDetail(record)} style={{ color: '#1890ff' }}>
+          {text}
+        </a>
+      ),
+    },
+    {
+      title: '举报人',
+      dataIndex: 'reportUserName',
+      key: 'reportUserName',
+      width: 100,
+    },
+    {
+      title: '举报时间',
+      dataIndex: 'createTime',
+      key: 'createTime',
+      width: 140,
+    },
+    {
+      title: '举报理由',
+      dataIndex: 'reportReason',
+      key: 'reportReason',
+      width: 150,
+      ellipsis: true,
+      render: (text: string) => (
+        <span title={text}>{text}</span>
+      ),
+    },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      key: 'status',
+      width: 80,
+      render: (status: string) => {
+        const statusMap: Record<string, { color: string; text: string }> = {
+          '0': { color: 'orange', text: '待处理' },
+          '1': { color: 'green', text: '已处理' },
+          '2': { color: 'red', text: '已驳回' }
+        };
+        const statusInfo = statusMap[status] || { color: 'gray', text: '未知' };
+        return <Tag color={statusInfo.color}>{statusInfo.text}</Tag>;
+      },
+    },
+    {
+      title: '操作',
+      key: 'action',
+      width: 300,
+      fixed: 'right' as const,
+      render: (text: any, record: ReportInfo) => (
+        <Space size="small">
+          <Button 
+            type="link" 
+            icon={<EyeOutlined />} 
+            onClick={() => showDetail(record)}
+            size="small"
+          >
+            查看
+          </Button>
+          {record.status === '0' && (
+            <>
+              <Button 
+                type="link" 
+                icon={<CheckOutlined />} 
+                style={{ color: 'green' }}
+                onClick={() => handleReportAction(record, 'approve')}
+                size="small"
+              >
+                确认下架
+              </Button>
+              <Button 
+                type="link" 
+                danger 
+                icon={<CloseOutlined />}
+                onClick={() => handleReportAction(record, 'reject')}
+                size="small"
+              >
+                驳回举报
+              </Button>
+            </>
+          )}
+        </Space>
+      ),
+    },
+  ];
+
+  return (
+    <div className={styles.postReviewContainer}>
+      <Card title="帖子举报管理">
+        <Table
+          columns={columns}
+          dataSource={reports}
+          loading={loading}
+          rowKey="reportId"
+          scroll={{ x: 880 }}
+          pagination={{
+            pageSize: 10,
+            showTotal: (total) => `共 ${total} 条记录`,
+            showSizeChanger: true,
+            showQuickJumper: true,
+          }}
+        />
+      </Card>
+
+      {/* 举报详情弹窗 */}
+      <Modal
+        title="举报详情"
+        open={detailModalVisible}
+        onCancel={() => {
+          setDetailModalVisible(false);
+          setCurrentReport(null);
+        }}
+        footer={null}
+        width={600}
+      >
+        {currentReport && (
+          <div>
+            <div style={{ marginBottom: 16 }}>
+              <strong>举报帖子:</strong>{currentReport.postTitle}
+            </div>
+            <div style={{ marginBottom: 16 }}>
+              <strong>举报人:</strong>{currentReport.reportUserName}
+            </div>
+            <div style={{ marginBottom: 16 }}>
+              <strong>举报时间:</strong>{currentReport.createTime}
+            </div>
+            <div style={{ marginBottom: 16 }}>
+              <strong>举报理由:</strong>
+              <Paragraph>{currentReport.reportReason}</Paragraph>
+            </div>
+            {currentReport.status !== '0' && (
+              <>
+                <div style={{ marginBottom: 16 }}>
+                  <strong>处理结果:</strong>{currentReport.handleResult}
+                </div>
+                <div style={{ marginBottom: 16 }}>
+                  <strong>处理时间:</strong>{currentReport.handleTime}
+                </div>
+                <div style={{ marginBottom: 16 }}>
+                  <strong>处理人:</strong>{currentReport.handleBy}
+                </div>
+              </>
+            )}
+            {currentReport.status === '0' && (
+              <div style={{ marginTop: 16 }}>
+                <Space>
+                  <Button 
+                    type="primary" 
+                    icon={<CheckOutlined />}
+                    onClick={() => {
+                      handleReportAction(currentReport, 'approve');
+                      setDetailModalVisible(false);
+                    }}
+                  >
+                    确认下架
+                  </Button>
+                  <Button 
+                    danger 
+                    icon={<CloseOutlined />}
+                    onClick={() => {
+                      handleReportAction(currentReport, 'reject');
+                      setDetailModalVisible(false);
+                    }}
+                  >
+                    驳回举报
+                  </Button>
+                </Space>
+              </div>
+            )}
+          </div>
+        )}
+      </Modal>
+
+      {/* 处理举报弹窗 */}
+      <Modal
+        title={currentAction === 'approve' ? '确认下架帖子' : '驳回举报'}
+        open={handleModalVisible}
+        onOk={submitHandle}
+        onCancel={() => {
+          setHandleModalVisible(false);
+          setCurrentReport(null);
+          setCurrentAction(null);
+          setHandleReason('');
+        }}
+        okText="确定"
+        cancelText="取消"
+      >
+        <div style={{ marginBottom: 16 }}>
+          <strong>帖子:</strong>{currentReport?.postTitle}
+        </div>
+        <div>
+          <strong>
+            {currentAction === 'approve' ? '下架理由' : '驳回理由'}
+            (可选):
+          </strong>
+          <TextArea
+            value={handleReason}
+            onChange={(e) => setHandleReason(e.target.value)}
+            placeholder={
+              currentAction === 'approve' ? '请输入下架理由...' : '请输入驳回理由...'
+            }
+            rows={4}
+            style={{ marginTop: 8 }}
+          />
+        </div>
+      </Modal>
+    </div>
+  );
+};
+
+export default ReportManagement; 
\ No newline at end of file
diff --git a/src/pages/PostReview/index.module.css b/src/pages/PostReview/index.module.css
new file mode 100644
index 0000000..887b367
--- /dev/null
+++ b/src/pages/PostReview/index.module.css
@@ -0,0 +1,50 @@
+.postReviewContainer {
+  padding: 24px;
+}
+
+.postDetail {
+  padding: 16px 0;
+}
+
+.postMeta {
+  margin: 16px 0;
+  padding: 16px;
+  background: #f5f5f5;
+  border-radius: 8px;
+}
+
+.postMeta p {
+  margin: 8px 0;
+}
+
+.postCover {
+  margin: 16px 0;
+  text-align: center;
+}
+
+.postTags {
+  margin: 16px 0;
+}
+
+.postSummary {
+  margin: 16px 0;
+  padding: 16px;
+  background: #f9f9f9;
+  border-radius: 8px;
+}
+
+.postContent {
+  margin: 16px 0;
+  padding: 16px;
+  border: 1px solid #e8e8e8;
+  border-radius: 8px;
+  max-height: 400px;
+  overflow-y: auto;
+}
+
+.postActions {
+  margin-top: 24px;
+  text-align: center;
+  padding-top: 16px;
+  border-top: 1px solid #e8e8e8;
+} 
\ No newline at end of file
diff --git a/src/pages/PostReview/index.tsx b/src/pages/PostReview/index.tsx
new file mode 100644
index 0000000..d3d6036
--- /dev/null
+++ b/src/pages/PostReview/index.tsx
@@ -0,0 +1,394 @@
+import React, { useState, useEffect } from 'react';
+import { 
+  Card, 
+  Table, 
+  Button, 
+  Modal, 
+  Tag, 
+  Space, 
+  message, 
+  Popconfirm,
+  Typography,
+  Image,
+  Row,
+  Col,
+  Input,
+  Tabs
+} from 'antd';
+import { 
+  EyeOutlined, 
+  CheckOutlined, 
+  CloseOutlined,
+  DeleteOutlined
+} from '@ant-design/icons';
+import { getReviewPosts, reviewPost, takeDownPost } from '@/services/post';
+import ReportManagement from './ReportManagement';
+import styles from './index.module.css';
+
+const { Title, Paragraph } = Typography;
+const { TextArea } = Input;
+const { TabPane } = Tabs;
+
+interface PostReviewProps {}
+
+const PostReview: React.FC<PostReviewProps> = () => {
+  const [activeTab, setActiveTab] = useState('review');
+  const [posts, setPosts] = useState<API.Post.PostInfo[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [detailModalVisible, setDetailModalVisible] = useState(false);
+  const [reasonModalVisible, setReasonModalVisible] = useState(false);
+  const [currentPost, setCurrentPost] = useState<API.Post.PostInfo | null>(null);
+  const [currentAction, setCurrentAction] = useState<'approve' | 'reject' | 'takedown' | null>(null);
+  const [reason, setReason] = useState('');
+
+  useEffect(() => {
+    if (activeTab === 'review') {
+      fetchPendingPosts();
+    }
+  }, [activeTab]);
+
+  const fetchPendingPosts = async () => {
+    setLoading(true);
+    try {
+      const response = await getReviewPosts({ 
+        pageNum: 1, 
+        pageSize: 100, 
+        status: '0' // 只查询待审核的帖子
+      });
+      
+      if (response.code === 200) {
+        setPosts(response.rows || []);
+      } else {
+        message.error(response.msg || '获取待审核帖子失败');
+      }
+    } catch (error) {
+      message.error('获取待审核帖子失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleAction = async (postId: number, action: 'approve' | 'reject', reason?: string) => {
+    try {
+      const response = await reviewPost(postId, action, reason);
+
+      if (response.code === 200) {
+        message.success(action === 'approve' ? '帖子审核通过' : '帖子已拒绝');
+        fetchPendingPosts();
+      } else {
+        message.error(response.msg || '操作失败');
+      }
+    } catch (error) {
+      message.error('操作失败');
+    }
+  };
+
+  const handleTakeDown = async (postId: number, reason?: string) => {
+    try {
+      const response = await takeDownPost(postId, reason);
+
+      if (response.code === 200) {
+        message.success('帖子已下架');
+        fetchPendingPosts();
+      } else {
+        message.error(response.msg || '操作失败');
+      }
+    } catch (error) {
+      message.error('操作失败');
+    }
+  };
+
+  const showReasonModal = (post: API.Post.PostInfo, action: 'approve' | 'reject' | 'takedown') => {
+    setCurrentPost(post);
+    setCurrentAction(action);
+    setReason('');
+    setReasonModalVisible(true);
+  };
+
+  const handleReasonSubmit = () => {
+    if (!currentPost || !currentAction) return;
+
+    if (currentAction === 'takedown') {
+      handleTakeDown(currentPost.postId || 0, reason);
+    } else {
+      handleAction(currentPost.postId || 0, currentAction, reason);
+    }
+    
+    setReasonModalVisible(false);
+    setCurrentPost(null);
+    setCurrentAction(null);
+    setReason('');
+  };
+
+  const handleViewDetail = (post: API.Post.PostInfo) => {
+    setCurrentPost(post);
+    setDetailModalVisible(true);
+  };
+
+  const columns = [
+    {
+      title: '帖子标题',
+      dataIndex: 'title',
+      key: 'title',
+      width: 200,
+      render: (text: string, record: API.Post.PostInfo) => (
+        <a onClick={() => handleViewDetail(record)}>{text}</a>
+      ),
+    },
+    {
+      title: '作者',
+      dataIndex: 'author',
+      key: 'author',
+      width: 100,
+    },
+    {
+      title: '发布时间',
+      dataIndex: 'publishTime',
+      key: 'publishTime',
+      width: 150,
+    },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      key: 'status',
+      width: 100,
+      render: (status: string) => {
+        const statusMap: Record<string, { color: string; text: string }> = {
+          '0': { color: 'orange', text: '待审核' },
+          '1': { color: 'green', text: '已发布' },
+          '2': { color: 'red', text: '已拒绝' },
+          '3': { color: 'gray', text: '已下架' }
+        };
+        const statusInfo = statusMap[status] || { color: 'gray', text: '未知' };
+        return <Tag color={statusInfo.color}>{statusInfo.text}</Tag>;
+      },
+    },
+    {
+      title: '标签',
+      dataIndex: 'tags',
+      key: 'tags',
+      width: 150,
+      render: (tags: string) => {
+        if (!tags) return '-';
+        return tags.split(',').map(tag => (
+          <Tag key={tag} color="blue">{tag}</Tag>
+        ));
+      },
+    },
+    {
+      title: '操作',
+      key: 'action',
+      width: 250,
+      render: (text: any, record: API.Post.PostInfo) => (
+        <Space size="small">
+          <Button 
+            type="link" 
+            icon={<EyeOutlined />} 
+            onClick={() => handleViewDetail(record)}
+          >
+            查看
+          </Button>
+          {record.status === '0' && (
+            <>
+              <Button 
+                type="link" 
+                icon={<CheckOutlined />} 
+                style={{ color: 'green' }}
+                onClick={() => showReasonModal(record, 'approve')}
+              >
+                通过
+              </Button>
+              <Button 
+                type="link" 
+                danger 
+                icon={<CloseOutlined />}
+                onClick={() => showReasonModal(record, 'reject')}
+              >
+                拒绝
+              </Button>
+            </>
+          )}
+          {record.status === '1' && (
+            <Button 
+              type="link" 
+              danger 
+              icon={<DeleteOutlined />}
+              onClick={() => showReasonModal(record, 'takedown')}
+            >
+              下架
+            </Button>
+          )}
+        </Space>
+      ),
+    },
+  ];
+
+  return (
+    <div className={styles.postReviewContainer}>
+      <Card title="帖子审核管理">
+        <Tabs activeKey={activeTab} onChange={setActiveTab}>
+          <TabPane tab="帖子发布管理" key="review">
+            <Table
+              columns={columns}
+              dataSource={posts}
+              loading={loading}
+              rowKey="postId"
+              pagination={{
+                pageSize: 10,
+                showTotal: (total) => `共 ${total} 条记录`,
+              }}
+            />
+          </TabPane>
+          
+          <TabPane tab="帖子举报管理" key="report">
+            <ReportManagement />
+          </TabPane>
+        </Tabs>
+      </Card>
+
+      {/* 帖子详情弹窗 */}
+      <Modal
+        title="帖子详情"
+        open={detailModalVisible}
+        onCancel={() => {
+          setDetailModalVisible(false);
+          setCurrentPost(null);
+        }}
+        footer={null}
+        width={800}
+      >
+        {currentPost && (
+          <div className={styles.postDetail}>
+            <Title level={3}>{currentPost.title}</Title>
+            
+            <div className={styles.postMeta}>
+              <Row gutter={16}>
+                <Col span={12}>
+                  <p><strong>作者:</strong>{currentPost.author}</p>
+                  <p><strong>发布时间:</strong>{currentPost.publishTime}</p>
+                </Col>
+                <Col span={12}>
+                  <p><strong>浏览量:</strong>{currentPost.views || 0}</p>
+                  <p><strong>点赞数:</strong>{currentPost.likes || 0}</p>
+                </Col>
+              </Row>
+            </div>
+
+            {currentPost.coverImage && (
+              <div className={styles.postCover}>
+                <Image
+                  src={currentPost.coverImage}
+                  alt="封面图片"
+                  style={{ maxWidth: '100%', maxHeight: '200px' }}
+                />
+              </div>
+            )}
+
+            <div className={styles.postTags}>
+              <strong>标签:</strong>
+              {currentPost.tags ? (
+                currentPost.tags.split(',').map(tag => (
+                  <Tag key={tag} color="blue">{tag}</Tag>
+                ))
+              ) : (
+                <span>无标签</span>
+              )}
+            </div>
+
+            <div className={styles.postSummary}>
+              <strong>摘要:</strong>
+              <Paragraph>{currentPost.summary}</Paragraph>
+            </div>
+
+            <div className={styles.postContent}>
+              <strong>内容:</strong>
+              <div dangerouslySetInnerHTML={{ __html: currentPost.content || '' }} />
+            </div>
+
+            <div className={styles.postActions}>
+              <Space>
+                {currentPost.status === '0' && (
+                  <>
+                    <Button 
+                      type="primary" 
+                      icon={<CheckOutlined />}
+                      onClick={() => {
+                        showReasonModal(currentPost, 'approve');
+                        setDetailModalVisible(false);
+                      }}
+                    >
+                      通过审核
+                    </Button>
+                    <Button 
+                      danger 
+                      icon={<CloseOutlined />}
+                      onClick={() => {
+                        showReasonModal(currentPost, 'reject');
+                        setDetailModalVisible(false);
+                      }}
+                    >
+                      拒绝审核
+                    </Button>
+                  </>
+                )}
+                {currentPost.status === '1' && (
+                  <Button 
+                    danger 
+                    icon={<DeleteOutlined />}
+                    onClick={() => {
+                      showReasonModal(currentPost, 'takedown');
+                      setDetailModalVisible(false);
+                    }}
+                  >
+                    强制下架
+                  </Button>
+                )}
+              </Space>
+            </div>
+          </div>
+        )}
+      </Modal>
+
+      {/* 审核理由弹窗 */}
+      <Modal
+        title={
+          currentAction === 'approve' ? '审核通过' :
+          currentAction === 'reject' ? '审核拒绝' : '强制下架'
+        }
+        open={reasonModalVisible}
+        onOk={handleReasonSubmit}
+        onCancel={() => {
+          setReasonModalVisible(false);
+          setCurrentPost(null);
+          setCurrentAction(null);
+          setReason('');
+        }}
+        okText="确定"
+        cancelText="取消"
+      >
+        <div style={{ marginBottom: 16 }}>
+          <strong>帖子:</strong>{currentPost?.title}
+        </div>
+        <div>
+          <strong>
+            {currentAction === 'approve' ? '通过理由' :
+             currentAction === 'reject' ? '拒绝理由' : '下架理由'}
+            (可选):
+          </strong>
+          <TextArea
+            value={reason}
+            onChange={(e) => setReason(e.target.value)}
+            placeholder={
+              currentAction === 'approve' ? '请输入审核通过的理由...' :
+              currentAction === 'reject' ? '请输入审核拒绝的理由...' : '请输入强制下架的理由...'
+            }
+            rows={4}
+            style={{ marginTop: 8 }}
+          />
+        </div>
+      </Modal>
+    </div>
+  );
+};
+
+export default PostReview; 
\ No newline at end of file
diff --git a/src/pages/UserCenter/index.module.css b/src/pages/UserCenter/index.module.css
new file mode 100644
index 0000000..3728909
--- /dev/null
+++ b/src/pages/UserCenter/index.module.css
@@ -0,0 +1,90 @@
+.userCenterContainer {
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 24px;
+  min-height: 100vh;
+  background: #f5f5f5;
+}
+
+.userCenterCard {
+  background: #fff;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.tabContent {
+  padding: 16px 0;
+}
+
+.tabHeader {
+  margin-bottom: 16px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.emptyState {
+  text-align: center;
+  padding: 60px 0;
+  color: #999;
+}
+
+.emptyState p {
+  margin-top: 16px;
+  font-size: 16px;
+}
+
+.paymentModal {
+  text-align: center;
+}
+
+.paymentInfo {
+  margin-bottom: 24px;
+  padding: 16px;
+  background: #f9f9f9;
+  border-radius: 8px;
+}
+
+.paymentInfo h3 {
+  margin: 0 0 8px 0;
+  color: #1890ff;
+}
+
+.paymentInfo p {
+  margin: 4px 0;
+  color: #666;
+}
+
+.qrCode {
+  margin: 24px 0;
+}
+
+.qrCodePlaceholder {
+  padding: 24px;
+  border: 2px dashed #d9d9d9;
+  border-radius: 8px;
+  background: #fafafa;
+}
+
+.mockQrCode {
+  width: 120px;
+  height: 120px;
+  margin: 16px auto;
+  border: 1px solid #d9d9d9;
+  border-radius: 8px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  background: #fff;
+}
+
+.mockQrCode p {
+  margin: 4px 0;
+  font-size: 12px;
+  color: #666;
+}
+
+.paymentActions {
+  margin-top: 24px;
+} 
\ No newline at end of file
diff --git a/src/pages/UserCenter/index.tsx b/src/pages/UserCenter/index.tsx
new file mode 100644
index 0000000..13de6ad
--- /dev/null
+++ b/src/pages/UserCenter/index.tsx
@@ -0,0 +1,968 @@
+import React, { useState, useEffect } from 'react';
+import { 
+  Card, 
+  Tabs, 
+  Button, 
+  Table, 
+  Modal, 
+  Form, 
+  Input, 
+  Select, 
+  Upload, 
+  message, 
+  Tag, 
+  Space,
+  Popconfirm,
+  Row,
+  Col,
+  Radio,
+  InputNumber,
+  Image
+} from 'antd';
+import { 
+  PlusOutlined, 
+  EditOutlined, 
+  DeleteOutlined, 
+  EyeOutlined,
+  UploadOutlined,
+  HeartOutlined,
+  LoadingOutlined
+} from '@ant-design/icons';
+import { useNavigate } from 'react-router-dom';
+import { 
+  getMyPosts, 
+  getMyFavorites, 
+  publishPost, 
+  updatePost, 
+  deletePost,
+  getAvailableTags,
+  uploadImage,
+  deleteImage,
+  getPromotionPlans,
+  createPayment,
+  getPromotionStatus,
+  confirmPayment,
+  cancelPayment
+} from '@/services/post';
+import PostCard from '../PostCenter/PostCard';
+import styles from './index.module.css';
+
+const { TabPane } = Tabs;
+const { TextArea } = Input;
+const { Option } = Select;
+
+interface PostFormData {
+  title: string;
+  content: string;
+  summary: string;
+  tags: string[] | string;
+  promotionPlan?: number;
+  coverImage?: string;
+}
+
+interface PromotionPlan {
+  id: number;
+  name: string;
+  description: string;
+  price: number;
+  duration: number;
+}
+
+interface PaymentRecord {
+  paymentId: number;
+  postId: number;
+  planId: number;
+  userId: number;
+  amount: number;
+  paymentStatus: string;
+  paymentTime: string;
+}
+
+const UserCenter: React.FC = () => {
+  const navigate = useNavigate();
+  const [activeTab, setActiveTab] = useState('myPosts');
+  const [publishModalVisible, setPublishModalVisible] = useState(false);
+  const [editModalVisible, setEditModalVisible] = useState(false);
+  const [paymentModalVisible, setPaymentModalVisible] = useState(false);
+  const [myPosts, setMyPosts] = useState<API.Post.PostInfo[]>([]);
+  const [favorites, setFavorites] = useState<API.Post.PostInfo[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [form] = Form.useForm();
+  const [editForm] = Form.useForm();
+  const [selectedPromotion, setSelectedPromotion] = useState<PromotionPlan | null>(null);
+  const [currentEditPost, setCurrentEditPost] = useState<API.Post.PostInfo | null>(null);
+  const [availableTags, setAvailableTags] = useState<API.Post.PostTag[]>([]);
+  const [promotionPlans, setPromotionPlans] = useState<PromotionPlan[]>([]);
+  const [uploadLoading, setUploadLoading] = useState(false);
+  const [editUploadLoading, setEditUploadLoading] = useState(false);
+  const [coverImageUrl, setCoverImageUrl] = useState<string>('');
+  const [editCoverImageUrl, setEditCoverImageUrl] = useState<string>('');
+  const [currentPayment, setCurrentPayment] = useState<PaymentRecord | null>(null);
+  const [isEditingPromotion, setIsEditingPromotion] = useState(false);
+
+  useEffect(() => {
+    if (activeTab === 'myPosts') {
+      fetchMyPosts();
+    } else if (activeTab === 'favorites') {
+      fetchFavorites();
+    }
+  }, [activeTab]);
+
+  useEffect(() => {
+    fetchAvailableTags();
+    fetchPromotionPlans();
+  }, []);
+
+  const fetchMyPosts = async () => {
+    setLoading(true);
+    try {
+      const response = await getMyPosts({ pageNum: 1, pageSize: 100 });
+      if (response.code === 200) {
+        setMyPosts(response.rows || []);
+      } else {
+        message.error(response.msg || '获取我的帖子失败');
+      }
+    } catch (error) {
+      message.error('获取我的帖子失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const fetchFavorites = async () => {
+    setLoading(true);
+    try {
+      const response = await getMyFavorites({ pageNum: 1, pageSize: 100 });
+      if (response.code === 200) {
+        setFavorites(response.rows || []);
+      } else {
+        message.error(response.msg || '获取收藏列表失败');
+      }
+    } catch (error) {
+      message.error('获取收藏列表失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const fetchAvailableTags = async () => {
+    try {
+      const response = await getAvailableTags();
+      if (response.code === 200) {
+        setAvailableTags(response.data || []);
+      } else {
+        message.error(response.msg || '获取可用标签失败');
+      }
+    } catch (error) {
+      message.error('获取可用标签失败');
+    }
+  };
+
+  const fetchPromotionPlans = async () => {
+    try {
+      const response = await getPromotionPlans();
+      if (response.code === 200) {
+        setPromotionPlans(response.data || []);
+      }
+    } catch (error) {
+      console.error('获取推广计划失败:', error);
+    }
+  };
+
+  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;
+        } else {
+          message.error(paymentResponse.msg || '创建支付记录失败');
+          return;
+        }
+      }
+      
+      // 直接发布帖子
+      await submitPost(values);
+    } catch (error) {
+      message.error('发布帖子失败');
+    }
+  };
+
+  const submitPost = async (values: PostFormData) => {
+    try {
+      // 处理标签格式
+      const tagsString = Array.isArray(values.tags) ? values.tags.join(',') : values.tags;
+      
+      const postData = {
+        title: values.title,
+        content: values.content,
+        summary: values.summary,
+        tags: tagsString,
+        promotionPlan: values.promotionPlan,
+        coverImage: coverImageUrl || undefined
+      };
+      
+      const response = await publishPost(postData);
+      if (response.code === 200) {
+        message.success('帖子发布成功');
+        setPublishModalVisible(false);
+        form.resetFields();
+        setSelectedPromotion(null);
+        setCoverImageUrl('');
+        fetchMyPosts();
+      } else {
+        message.error(response.msg || '发布帖子失败');
+      }
+    } catch (error) {
+      message.error('发布帖子失败');
+    }
+  };
+
+  const handleEditPost = async (post: API.Post.PostInfo) => {
+    setCurrentEditPost(post);
+    const tagsArray = post.tags ? (typeof post.tags === 'string' ? post.tags.split(',') : post.tags) : [];
+    
+    // 检查推广状态
+    try {
+      const promotionResponse = await getPromotionStatus(post.postId || post.id || 0);
+      if (promotionResponse.code === 200) {
+        const { hasPromotion, promotionPlanId } = promotionResponse.data;
+        setIsEditingPromotion(hasPromotion);
+        
+        editForm.setFieldsValue({
+          title: post.title,
+          content: post.content,
+          summary: post.summary,
+          tags: tagsArray,
+          promotionPlan: hasPromotion ? promotionPlanId : undefined
+        });
+      }
+    } catch (error) {
+      console.error('获取推广状态失败:', error);
+    editForm.setFieldsValue({
+      title: post.title,
+      content: post.content,
+      summary: post.summary,
+        tags: tagsArray,
+        promotionPlan: post.promotionPlanId
+    });
+    }
+    
+    setEditCoverImageUrl(post.coverImage || '');
+    setEditModalVisible(true);
+  };
+
+  const handleUpdatePost = async (values: any) => {
+    if (!currentEditPost) return;
+    
+    try {
+      // 处理标签格式
+      const tagsString = Array.isArray(values.tags) ? values.tags.join(',') : values.tags;
+      
+      // 检查是否选择了新的推广计划
+      const hasNewPromotion = values.promotionPlan && !isEditingPromotion;
+      
+      if (hasNewPromotion) {
+        // 如果选择了新的推广计划,需要先创建支付记录
+        const selectedPlan = promotionPlans.find(p => p.id === values.promotionPlan);
+        if (selectedPlan) {
+          setSelectedPromotion(selectedPlan);
+          
+          // 创建支付记录
+          const paymentResponse = await createPayment({
+            postId: currentEditPost.postId || currentEditPost.id || 0,
+            planId: selectedPlan.id,
+            amount: selectedPlan.price
+          });
+          
+          if (paymentResponse.code === 200) {
+            setCurrentPayment(paymentResponse.data);
+            setPaymentModalVisible(true);
+            return; // 等待支付完成后再更新帖子
+          } else {
+            message.error(paymentResponse.msg || '创建支付记录失败');
+            return;
+          }
+        }
+      }
+      
+      // 直接更新帖子(没有新推广或已有推广)
+      await updatePostDirectly(values, tagsString);
+    } catch (error) {
+      message.error('更新帖子失败');
+    }
+  };
+
+  const updatePostDirectly = async (values: any, tagsString: string) => {
+    if (!currentEditPost) return;
+    
+      const updateData = {
+        ...currentEditPost,
+      title: values.title,
+      content: values.content,
+      summary: values.summary,
+      tags: tagsString,
+      coverImage: editCoverImageUrl || currentEditPost.coverImage,
+      promotionPlanId: values.promotionPlan
+    };
+    
+      const response = await updatePost(updateData);
+      if (response.code === 200) {
+        message.success('帖子更新成功');
+        setEditModalVisible(false);
+        editForm.resetFields();
+        setCurrentEditPost(null);
+      setEditCoverImageUrl('');
+      setIsEditingPromotion(false);
+        fetchMyPosts();
+      } else {
+        message.error(response.msg || '更新帖子失败');
+    }
+  };
+
+  const handleDeletePost = async (postId: number) => {
+    try {
+      const response = await deletePost(postId);
+      if (response.code === 200) {
+        message.success('帖子删除成功');
+        fetchMyPosts();
+      } else {
+        message.error(response.msg || '删除帖子失败');
+      }
+    } catch (error) {
+      message.error('删除帖子失败');
+    }
+  };
+
+  const handleViewPost = (postId: number) => {
+    navigate(`/post-detail/${postId}`);
+  };
+
+  const handlePaymentConfirm = async () => {
+    if (!currentPayment) return;
+    
+    try {
+      const response = await confirmPayment(currentPayment.paymentId);
+      if (response.code === 200) {
+        message.success('支付成功,推广已生效');
+    setPaymentModalVisible(false);
+        setCurrentPayment(null);
+        
+        // 如果是编辑模式,完成帖子更新
+        if (editModalVisible && currentEditPost) {
+          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);
+          setCoverImageUrl('');
+    fetchMyPosts();
+        }
+      } else {
+        message.error(response.msg || '支付确认失败');
+      }
+    } catch (error) {
+      message.error('支付确认失败');
+    }
+  };
+
+  const handlePaymentCancel = async () => {
+    if (!currentPayment) return;
+    
+    try {
+      await cancelPayment(currentPayment.paymentId);
+      message.info('支付已取消');
+      setPaymentModalVisible(false);
+      setCurrentPayment(null);
+      setSelectedPromotion(null);
+    } catch (error) {
+      console.error('取消支付失败:', error);
+      setPaymentModalVisible(false);
+      setCurrentPayment(null);
+      setSelectedPromotion(null);
+    }
+  };
+
+  const handleImageUpload = async (file: any) => {
+    setUploadLoading(true);
+    try {
+      const formData = new FormData();
+      formData.append('file', file);
+      
+      const response = await uploadImage(formData);
+      if (response.code === 200 && response.data) {
+        setCoverImageUrl(response.data.url);
+        message.success('图片上传成功');
+        return false; // 阻止自动上传
+      } else {
+        message.error(response.msg || '图片上传失败');
+      }
+    } catch (error) {
+      message.error('图片上传失败');
+    } finally {
+      setUploadLoading(false);
+    }
+    return false;
+  };
+
+  const handleDeleteImage = async () => {
+    if (coverImageUrl) {
+      try {
+        const filename = coverImageUrl.split('/').pop();
+        if (filename) {
+          await deleteImage(filename);
+        }
+        setCoverImageUrl('');
+        message.success('图片删除成功');
+      } catch (error) {
+        message.error('图片删除失败');
+      }
+    }
+  };
+
+  const handleCancelPublish = async () => {
+    // 如果有上传的图片但没有发布帖子,删除图片
+    if (coverImageUrl) {
+      try {
+        const filename = coverImageUrl.split('/').pop();
+        if (filename) {
+          await deleteImage(filename);
+        }
+      } catch (error) {
+        console.error('删除图片失败:', error);
+      }
+    }
+    
+    setPublishModalVisible(false);
+    form.resetFields();
+    setSelectedPromotion(null);
+    setCoverImageUrl('');
+  };
+
+  const uploadButton = (
+    <div>
+      {uploadLoading ? <LoadingOutlined /> : <PlusOutlined />}
+      <div style={{ marginTop: 8 }}>上传封面</div>
+    </div>
+  );
+
+  const handleEditImageUpload = async (file: any) => {
+    setEditUploadLoading(true);
+    try {
+      const formData = new FormData();
+      formData.append('file', file);
+      
+      const response = await uploadImage(formData);
+      if (response.code === 200 && response.data) {
+        // 如果有旧图片,删除它
+        if (editCoverImageUrl) {
+          const oldFilename = editCoverImageUrl.split('/').pop();
+          if (oldFilename) {
+            await deleteImage(oldFilename);
+          }
+        }
+        
+        setEditCoverImageUrl(response.data.url);
+        message.success('图片上传成功');
+        return false;
+      } else {
+        message.error(response.msg || '图片上传失败');
+      }
+    } catch (error) {
+      message.error('图片上传失败');
+    } finally {
+      setEditUploadLoading(false);
+    }
+    return false;
+  };
+
+  const handleDeleteEditImage = async () => {
+    if (editCoverImageUrl) {
+      try {
+        const filename = editCoverImageUrl.split('/').pop();
+        if (filename) {
+          await deleteImage(filename);
+        }
+        setEditCoverImageUrl('');
+        message.success('图片删除成功');
+      } catch (error) {
+        message.error('图片删除失败');
+      }
+    }
+  };
+
+  const editUploadButton = (
+    <div>
+      {editUploadLoading ? <LoadingOutlined /> : <PlusOutlined />}
+      <div style={{ marginTop: 8 }}>上传封面</div>
+    </div>
+  );
+
+  const myPostsColumns = [
+    {
+      title: '标题',
+      dataIndex: 'title',
+      key: 'title',
+      render: (text: string, record: API.Post.PostInfo) => (
+        <a onClick={() => handleViewPost(record.postId || record.id || 0)}>{text}</a>
+      ),
+    },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      key: 'status',
+      render: (status: string) => {
+        const statusMap: Record<string, { color: string; text: string }> = {
+          '0': { color: 'orange', text: '待审核' },
+          '1': { color: 'green', text: '已发布' },
+          '2': { color: 'red', text: '已拒绝' },
+          '3': { color: 'gray', text: '已下架' }
+        };
+        const statusInfo = statusMap[status] || { color: 'gray', text: '未知' };
+        return <Tag color={statusInfo.color}>{statusInfo.text}</Tag>;
+      },
+    },
+    {
+      title: '浏览量',
+      dataIndex: 'views',
+      key: 'views',
+    },
+    {
+      title: '评论数',
+      dataIndex: 'comments',
+      key: 'comments',
+    },
+    {
+      title: '收藏数',
+      dataIndex: 'favorites',
+      key: 'favorites',
+    },
+    {
+      title: '点赞数',
+      dataIndex: 'likes',
+      key: 'likes',
+    },
+    {
+      title: '发布时间',
+      dataIndex: 'publishTime',
+      key: 'publishTime',
+    },
+    {
+      title: '操作',
+      key: 'action',
+      render: (text: any, record: API.Post.PostInfo) => (
+        <Space size="middle">
+          <Button 
+            type="link" 
+            icon={<EyeOutlined />} 
+            onClick={() => handleViewPost(record.postId || record.id || 0)}
+          >
+            查看
+          </Button>
+          <Button 
+            type="link" 
+            icon={<EditOutlined />} 
+            onClick={() => handleEditPost(record)}
+          >
+            编辑
+          </Button>
+          <Popconfirm
+            title="确定要删除这篇帖子吗?"
+            onConfirm={() => handleDeletePost(record.postId || record.id || 0)}
+            okText="确定"
+            cancelText="取消"
+          >
+            <Button type="link" danger icon={<DeleteOutlined />}>
+              删除
+            </Button>
+          </Popconfirm>
+        </Space>
+      ),
+    },
+  ];
+
+  return (
+    <div className={styles.userCenterContainer}>
+      <Card title="个人中心" className={styles.userCenterCard}>
+        <Tabs activeKey={activeTab} onChange={setActiveTab}>
+          <TabPane tab="我的帖子" key="myPosts">
+            <div className={styles.tabContent}>
+              <div className={styles.tabHeader}>
+                <Button 
+                  type="primary" 
+                  icon={<PlusOutlined />}
+                  onClick={() => setPublishModalVisible(true)}
+                >
+                  发布新帖子
+                </Button>
+              </div>
+              <Table
+                columns={myPostsColumns}
+                dataSource={myPosts}
+                loading={loading}
+                rowKey="id"
+                pagination={{
+                  pageSize: 10,
+                  showTotal: (total) => `共 ${total} 条记录`,
+                }}
+              />
+            </div>
+          </TabPane>
+          
+          <TabPane tab="我的收藏" key="favorites">
+            <div className={styles.tabContent}>
+              <Row gutter={[24, 24]}>
+                {favorites.map((post: any) => {
+                  // 确保post对象有正确的id字段
+                  const formattedPost = {
+                    ...post,
+                    id: post.postId || post.id,
+                    tags: post.tags ? (Array.isArray(post.tags) ? post.tags : post.tags.split(',')) : []
+                  };
+                  return (
+                    <Col xs={24} sm={12} md={8} key={formattedPost.id}>
+                      <PostCard post={formattedPost} />
+                  </Col>
+                  );
+                })}
+              </Row>
+              {favorites.length === 0 && !loading && (
+                <div className={styles.emptyState}>
+                  <HeartOutlined style={{ fontSize: 48, color: '#ccc' }} />
+                  <p>暂无收藏的帖子</p>
+                </div>
+              )}
+            </div>
+          </TabPane>
+        </Tabs>
+      </Card>
+
+      {/* 发布帖子弹窗 */}
+      <Modal
+        title="发布新帖子"
+        open={publishModalVisible}
+        onCancel={handleCancelPublish}
+        footer={null}
+        width={800}
+      >
+        <Form
+          form={form}
+          layout="vertical"
+          onFinish={handlePublishPost}
+        >
+          <Form.Item
+            name="title"
+            label="帖子标题"
+            rules={[{ required: true, message: '请输入帖子标题' }]}
+          >
+            <Input placeholder="请输入帖子标题" />
+          </Form.Item>
+          
+          <Form.Item
+            name="summary"
+            label="帖子摘要"
+            rules={[{ required: true, message: '请输入帖子摘要' }]}
+          >
+            <TextArea rows={3} placeholder="请输入帖子摘要" />
+          </Form.Item>
+          
+          <Form.Item
+            name="content"
+            label="帖子内容"
+            rules={[{ required: true, message: '请输入帖子内容' }]}
+          >
+            <TextArea rows={8} placeholder="请输入帖子内容" />
+          </Form.Item>
+          
+          <Form.Item
+            name="coverImage"
+            label="封面图片(可选)"
+          >
+            <Upload
+              listType="picture-card"
+              showUploadList={false}
+              beforeUpload={handleImageUpload}
+            >
+              {coverImageUrl ? (
+                <Image
+                  src={coverImageUrl}
+                  alt="封面"
+                  width="100%"
+                  height="100%"
+                  style={{ objectFit: 'cover' }}
+                />
+              ) : (
+                uploadButton
+              )}
+            </Upload>
+            {coverImageUrl && (
+              <Button 
+                type="link" 
+                onClick={handleDeleteImage}
+                style={{ padding: 0, marginTop: 8 }}
+              >
+                删除图片
+              </Button>
+            )}
+          </Form.Item>
+          
+          <Form.Item
+            name="tags"
+            label="标签"
+            rules={[{ required: true, message: '请选择标签' }]}
+          >
+            <Select
+              mode="multiple"
+              placeholder="请选择标签"
+              allowClear
+              style={{ width: '100%' }}
+            >
+              {availableTags.map(tag => (
+                <Select.Option key={tag.tagId} value={tag.tagName}>
+                  <Tag color={tag.tagColor}>{tag.tagName}</Tag>
+                </Select.Option>
+              ))}
+            </Select>
+          </Form.Item>
+          
+          <Form.Item
+            name="promotionPlan"
+            label="推广选项(可选)"
+          >
+            <Radio.Group>
+              <Space direction="vertical">
+                <Radio value={undefined}>不选择推广</Radio>
+                {promotionPlans.map(plan => (
+                  <Radio key={plan.id} value={plan.id}>
+                    <div>
+                      <strong>{plan.name}</strong> - ¥{plan.price} ({plan.duration}天)
+                      <br />
+                      <span style={{ color: '#666', fontSize: '12px' }}>
+                        {plan.description}
+                      </span>
+                    </div>
+                  </Radio>
+                ))}
+              </Space>
+            </Radio.Group>
+          </Form.Item>
+          
+          <Form.Item>
+            <Space>
+              <Button type="primary" htmlType="submit">
+                {selectedPromotion ? '选择支付方式' : '发布帖子'}
+              </Button>
+              <Button onClick={handleCancelPublish}>
+                取消
+              </Button>
+            </Space>
+          </Form.Item>
+        </Form>
+      </Modal>
+
+      {/* 编辑帖子弹窗 */}
+      <Modal
+        title="编辑帖子"
+        open={editModalVisible}
+        onCancel={() => {
+          setEditModalVisible(false);
+          editForm.resetFields();
+          setCurrentEditPost(null);
+          setEditCoverImageUrl('');
+          setIsEditingPromotion(false);
+        }}
+        footer={null}
+        width={800}
+      >
+        <Form
+          form={editForm}
+          layout="vertical"
+          onFinish={handleUpdatePost}
+        >
+          <Form.Item
+            name="title"
+            label="帖子标题"
+            rules={[{ required: true, message: '请输入帖子标题' }]}
+          >
+            <Input placeholder="请输入帖子标题" />
+          </Form.Item>
+          
+          <Form.Item
+            name="summary"
+            label="帖子摘要"
+            rules={[{ required: true, message: '请输入帖子摘要' }]}
+          >
+            <TextArea rows={3} placeholder="请输入帖子摘要" />
+          </Form.Item>
+          
+          <Form.Item
+            name="content"
+            label="帖子内容"
+            rules={[{ required: true, message: '请输入帖子内容' }]}
+          >
+            <TextArea rows={8} placeholder="请输入帖子内容" />
+          </Form.Item>
+          
+          <Form.Item
+            name="coverImage"
+            label="封面图片"
+          >
+            <Upload
+              listType="picture-card"
+              showUploadList={false}
+              beforeUpload={handleEditImageUpload}
+            >
+              {editCoverImageUrl ? (
+                <Image
+                  src={editCoverImageUrl}
+                  alt="封面"
+                  width="100%"
+                  height="100%"
+                  style={{ objectFit: 'cover' }}
+                />
+              ) : (
+                editUploadButton
+              )}
+            </Upload>
+            {editCoverImageUrl && (
+              <Button 
+                type="link" 
+                onClick={handleDeleteEditImage}
+                style={{ padding: 0, marginTop: 8 }}
+              >
+                删除图片
+              </Button>
+            )}
+          </Form.Item>
+          
+          <Form.Item
+            name="tags"
+            label="标签"
+            rules={[{ required: true, message: '请选择标签' }]}
+          >
+            <Select
+              mode="multiple"
+              placeholder="请选择标签"
+              allowClear
+              style={{ width: '100%' }}
+            >
+              {availableTags.map(tag => (
+                <Select.Option key={tag.tagId} value={tag.tagName}>
+                  <Tag color={tag.tagColor}>{tag.tagName}</Tag>
+                </Select.Option>
+              ))}
+            </Select>
+          </Form.Item>
+          
+          <Form.Item
+            name="promotionPlan"
+            label="推广选项(可选)"
+          >
+            <Radio.Group disabled={isEditingPromotion}>
+              <Space direction="vertical">
+                <Radio value={undefined}>不选择推广</Radio>
+                {isEditingPromotion && (
+                  <div style={{ color: '#ff4d4f', fontSize: '12px', marginBottom: 8 }}>
+                    该帖子已购买推广,无法更改推广选项
+                  </div>
+                )}
+                {promotionPlans.map(plan => (
+                  <Radio key={plan.id} value={plan.id} disabled={isEditingPromotion}>
+                    <div>
+                      <strong>{plan.name}</strong> - ¥{plan.price} ({plan.duration}天)
+                      <br />
+                      <span style={{ color: '#666', fontSize: '12px' }}>
+                        {plan.description}
+                      </span>
+                    </div>
+                  </Radio>
+                ))}
+              </Space>
+            </Radio.Group>
+          </Form.Item>
+          
+          <Form.Item>
+            <Space>
+              <Button type="primary" htmlType="submit">
+                更新帖子
+              </Button>
+              <Button onClick={() => {
+                setEditModalVisible(false);
+                editForm.resetFields();
+                setCurrentEditPost(null);
+                setEditCoverImageUrl('');
+                setIsEditingPromotion(false);
+              }}>
+                取消
+              </Button>
+            </Space>
+          </Form.Item>
+        </Form>
+      </Modal>
+
+      {/* 支付弹窗 */}
+      <Modal
+        title="支付推广费用"
+        open={paymentModalVisible}
+        onCancel={handlePaymentCancel}
+        footer={null}
+        width={400}
+      >
+        <div className={styles.paymentModal}>
+          {selectedPromotion && (
+            <>
+              <div className={styles.paymentInfo}>
+                <h3>{selectedPromotion.name}</h3>
+                <p>{selectedPromotion.description}</p>
+                <p>费用: <strong>¥{selectedPromotion.price}</strong></p>
+                <p>时长: {selectedPromotion.duration}天</p>
+              </div>
+              
+              <div className={styles.qrCode}>
+                <div className={styles.qrCodePlaceholder}>
+                  <p>支付二维码</p>
+                  <p style={{ fontSize: '12px', color: '#666' }}>
+                    请使用支付宝扫描二维码支付
+                  </p>
+                  <div className={styles.mockQrCode}>
+                    <p>模拟二维码</p>
+                    <p>¥{selectedPromotion.price}</p>
+                  </div>
+                </div>
+              </div>
+              
+              <div className={styles.paymentActions}>
+                <Button 
+                  type="primary" 
+                  onClick={handlePaymentConfirm}
+                  style={{ width: '100%', marginBottom: 8 }}
+                >
+                  我已完成支付
+                </Button>
+                <Button 
+                  onClick={handlePaymentCancel}
+                  style={{ width: '100%' }}
+                >
+                  取消支付
+                </Button>
+              </div>
+            </>
+          )}
+        </div>
+      </Modal>
+    </div>
+  );
+};
+
+export default UserCenter; 
\ No newline at end of file
diff --git a/src/services/post/index.ts b/src/services/post/index.ts
new file mode 100644
index 0000000..3452095
--- /dev/null
+++ b/src/services/post/index.ts
@@ -0,0 +1,262 @@
+import { request } from '@umijs/max';
+
+export interface PostListParams {
+  pageNum?: number;
+  pageSize?: number;
+  title?: string;
+  status?: string;
+  tags?: string;
+}
+
+export interface PostDetailResponse {
+  post: API.Post.PostInfo;
+  tags: API.Post.PostTag[];
+  comments: any[];
+  authorPosts: API.Post.PostInfo[];
+  similarPosts: API.Post.PostInfo[];
+  recommendedPosts: API.Post.PostInfo[];
+  favorited: boolean;
+}
+
+export interface CommentAddParams {
+  postId: number;
+  content: string;
+  parentId?: number;
+}
+
+// 获取帖子列表
+export async function getPostList(params: PostListParams) {
+    console.log('getPostList', params);    
+  return request<API.TableDataInfo>('/api/post-center/list', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 获取帖子详情
+export async function getPostDetail(postId: number) {
+    console.log('getPostDetail', postId);
+  return request<API.AjaxResult<PostDetailResponse>>(`/api/post-center/${postId}`, {
+    method: 'GET',
+  });
+}
+
+// 添加评论
+export async function addComment(data: CommentAddParams) {
+    console.log('addComment', data);
+  return request<API.AjaxResult>('/api/post-center/comment', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 收藏/取消收藏帖子
+export async function toggleFavorite(postId: number, favorite: boolean) {
+    console.log('toggleFavorite', postId, favorite);
+  return request<API.AjaxResult>(`/api/post-center/favorite/${postId}`, {
+    method: 'POST',
+    params: { favorite },
+  });
+}
+
+// 获取热门标签
+export async function getHotTags(): Promise<API.AjaxResult<API.Post.PostTag[]>> {
+  return request('/api/post-center/tags/hot', {
+    method: 'GET',
+  });
+}
+
+// 根据标签获取帖子
+export async function getPostsByTag(tagId: number) {
+    console.log('getPostsByTag', tagId);
+  return request<API.TableDataInfo>(`/api/post-center/bytag/${tagId}`, {
+    method: 'GET',
+  });
+}
+
+// 发布帖子
+export async function publishPost(data: {
+  title: string;
+  content: string;
+  summary: string;
+  tags: string;
+  promotionPlan?: number;
+}): Promise<API.AjaxResult> {
+  return request('/api/post-center/publish', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 获取我的帖子列表
+export async function getMyPosts(params: {
+  pageNum?: number;
+  pageSize?: number;
+}): Promise<API.TableDataInfo<API.Post.PostInfo>> {
+  return request('/api/post-center/my-posts', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 获取我的收藏列表
+export async function getMyFavorites(params: {
+  pageNum?: number;
+  pageSize?: number;
+}): Promise<API.TableDataInfo<API.Post.PostInfo>> {
+  return request('/api/post-center/my-favorites', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 更新帖子
+export async function updatePost(data: API.Post.PostInfo): Promise<API.AjaxResult> {
+  return request('/api/post-center/update', {
+    method: 'PUT',
+    data,
+  });
+}
+
+// 删除帖子
+export async function deletePost(postId: number): Promise<API.AjaxResult> {
+  return request(`/api/post-center/delete/${postId}`, {
+    method: 'DELETE',
+  });
+}
+
+// 获取可用标签列表(用于下拉选择)
+export async function getAvailableTags(): Promise<API.AjaxResult<API.Post.PostTag[]>> {
+  return request('/api/post-center/tags/available', {
+    method: 'GET',
+  });
+}
+
+// 上传图片
+export async function uploadImage(file: FormData): Promise<API.AjaxResult<{url: string}>> {
+  return request('/api/post-center/upload', {
+    method: 'POST',
+    data: file,
+  });
+} 
+
+// 删除图片
+export async function deleteImage(filename: string): Promise<API.AjaxResult> {
+  return request('/api/post-center/upload', {
+    method: 'DELETE',
+    params: { filename },
+  });
+}
+
+// 获取推广计划列表
+export async function getPromotionPlans(): Promise<API.AjaxResult<any[]>> {
+  return request('/api/post-center/promotion-plans', {
+    method: 'GET',
+  });
+}
+
+// 创建支付记录
+export async function createPayment(data: {
+  postId: number;
+  planId: number;
+  amount: number;
+}): Promise<API.AjaxResult> {
+  return request('/api/post-center/payment', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 点赞/取消点赞帖子
+export async function toggleLike(postId: number, like: boolean) {
+  console.log('toggleLike', postId, like);
+  return request<API.AjaxResult>(`/api/post-center/like/${postId}`, {
+    method: 'POST',
+    params: { like },
+  });
+}
+
+// 获取推广帖子列表(用于轮播展示)
+export async function getPromotionPosts(): Promise<API.AjaxResult<API.Post.PostInfo[]>> {
+  return request('/api/post-center/promotion', {
+    method: 'GET',
+  });
+}
+
+// 点赞/取消点赞评论
+export async function toggleCommentLike(commentId: number, like: boolean) {
+  console.log('toggleCommentLike', commentId, like);
+  return request<API.AjaxResult>(`/api/post-center/comment/like/${commentId}`, {
+    method: 'POST',
+    params: { like },
+  });
+}
+
+// 获取待审核帖子列表
+export async function getReviewPosts(params: PostListParams): Promise<API.TableDataInfo<API.Post.PostInfo>> {
+  return request('/api/post/review/list', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 审核帖子(通过/拒绝)
+export async function reviewPost(postId: number, action: 'approve' | 'reject', reason?: string): Promise<API.AjaxResult> {
+  return request(`/api/post/review/${postId}`, {
+    method: 'PUT',
+    data: { action, reason },
+  });
+}
+
+// 强制下架帖子
+export async function takeDownPost(postId: number, reason?: string): Promise<API.AjaxResult> {
+  return request(`/api/post/takedown/${postId}`, {
+    method: 'PUT',
+    data: { reason },
+  });
+}
+
+// 检查帖子推广状态
+export async function getPromotionStatus(postId: number): Promise<API.AjaxResult> {
+  return request(`/api/post-center/promotion-status/${postId}`, {
+    method: 'GET',
+  });
+}
+
+// 确认支付成功
+export async function confirmPayment(paymentId: number): Promise<API.AjaxResult> {
+  return request(`/api/post-center/payment/confirm/${paymentId}`, {
+    method: 'POST',
+  });
+}
+
+// 取消支付
+export async function cancelPayment(paymentId: number): Promise<API.AjaxResult> {
+  return request(`/api/post-center/payment/cancel/${paymentId}`, {
+    method: 'POST',
+  });
+}
+
+// 举报帖子
+export async function reportPost(postId: number, reason: string): Promise<API.AjaxResult> {
+  return request(`/api/post-center/report/${postId}`, {
+    method: 'POST',
+    data: { reason },
+  });
+}
+
+// 获取举报列表
+export async function getReportList(params: PostListParams): Promise<API.TableDataInfo> {
+  return request('/api/post/report/list', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 处理举报
+export async function handleReport(reportId: number, action: 'approve' | 'reject', postId: number, reason?: string): Promise<API.AjaxResult> {
+  return request(`/api/post/report/handle/${reportId}`, {
+    method: 'PUT',
+    data: { action, postId, reason },
+  });
+} 
\ No newline at end of file
diff --git a/src/types/post.d.ts b/src/types/post.d.ts
new file mode 100644
index 0000000..03a6a24
--- /dev/null
+++ b/src/types/post.d.ts
@@ -0,0 +1,99 @@
+declare namespace API {
+  namespace Post {
+    interface PostInfo {
+      postId: number;
+      id?: number;
+      title: string;
+      content?: string;
+      summary?: string;
+      coverImage?: string;
+      authorId?: number;
+      author: string;
+      views: number;
+      comments: number;
+      favorites: number;
+      likes?: number;
+      status: string;
+      publishTime: string;
+      tags: string;
+      promotionPlanId?: number;
+      createBy?: string;
+      createTime?: string;
+      updateBy?: string;
+      updateTime?: string;
+      remark?: string;
+    }
+
+    interface PostComment {
+      commentId: number;
+      postId: number;
+      content: string;
+      userId?: number;
+      userName?: string;
+      userAvatar?: string;
+      parentId?: number;
+      replyUserId?: number;
+      replyUserName?: string;
+      status: string;
+      likes: number;
+      createBy?: string;
+      createTime?: string;
+      updateBy?: string;
+      updateTime?: string;
+      remark?: string;
+    }
+
+    interface PostTag {
+      tagId: number;
+      tagName: string;
+      tagColor?: string;
+      postCount: number;
+      status: string;
+      createBy?: string;
+      createTime?: string;
+      updateBy?: string;
+      updateTime?: string;
+      remark?: string;
+    }
+
+    interface PostFavorite {
+      favoriteId: number;
+      postId: number;
+      userId: number;
+      postTitle?: string;
+      postCover?: string;
+      status: string;
+      createBy?: string;
+      createTime?: string;
+      updateBy?: string;
+      updateTime?: string;
+      remark?: string;
+    }
+
+    interface PostLike {
+      likeId: number;
+      postId: number;
+      userId: number;
+      createTime?: string;
+    }
+  }
+
+  // 通用响应类型
+  interface AjaxResult<T = any> {
+    code: number;
+    msg?: string;
+    data?: T;
+  }
+
+  interface TableDataInfo<T = any> {
+    code: number;
+    rows: T[];
+    total: number;
+    msg?: string;
+  }
+
+  interface PageParams {
+    pageNum?: number;
+    pageSize?: number;
+  }
+} 
\ No newline at end of file
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..e8ab63b
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,145 @@
+# 单元测试文档
+
+## 概述
+
+本项目包含完整的前端和后端单元测试,确保代码质量和功能正确性。
+
+## 前端测试
+
+### 测试框架
+- Jest: 测试运行器和断言库
+- @testing-library/react: React组件测试工具
+- @testing-library/jest-dom: DOM断言扩展
+
+### 测试覆盖范围
+
+#### 服务层测试 (services/)
+- **post.test.ts**: 帖子相关API接口测试
+  - 帖子列表获取
+  - 帖子详情获取
+  - 评论功能
+  - 收藏和点赞
+  - 帖子发布和管理
+  - 文件上传
+  - 审核功能
+  - 举报功能
+  - 错误处理
+
+### 运行测试
+
+```bash
+# 运行所有测试
+npm run test
+
+# 监听模式运行测试
+npm run test:watch
+
+# 生成覆盖率报告
+npm run test:coverage
+
+# CI环境运行测试
+npm run test:ci
+```
+
+### 测试配置
+
+测试配置文件:
+- `jest.config.ts`: Jest主配置
+- `tests/setup.ts`: 测试环境设置
+
+## 后端测试
+
+### 测试框架
+- JUnit 5: 测试框架
+- Mockito: Mock框架
+- Spring Boot Test: Spring测试支持
+
+### 测试覆盖范围
+
+#### 控制器测试
+- **PostCenterControllerTest**: 帖子中心控制器测试
+  - 帖子列表API
+  - 帖子详情API
+  - 评论API
+  - 收藏和点赞API
+  - 帖子发布API
+  - 文件上传API
+  - 帖子管理API
+  - 举报API
+
+#### 服务层测试
+- **PostServiceTest**: 帖子服务测试
+- **PostCommentServiceTest**: 评论服务测试
+- **PostTagServiceTest**: 标签服务测试
+
+### 运行测试
+
+```bash
+# Maven运行测试
+mvn test
+
+# 生成测试报告
+mvn surefire-report:report
+
+# 运行特定测试类
+mvn test -Dtest=PostCenterControllerTest
+```
+
+## 测试最佳实践
+
+### 1. 测试命名规范
+- 测试方法名应清晰描述测试场景
+- 使用中文描述测试目的
+- 格式:`test[功能名]_[场景]`
+
+### 2. 测试结构
+- **Arrange**: 准备测试数据
+- **Act**: 执行被测试方法
+- **Assert**: 验证结果
+
+### 3. Mock使用
+- 只Mock外部依赖
+- 验证重要的方法调用
+- 使用合理的测试数据
+
+### 4. 覆盖率要求
+- 行覆盖率: ≥80%
+- 分支覆盖率: ≥80%
+- 函数覆盖率: ≥80%
+
+## CI/CD集成
+
+### Jenkins配置
+测试在以下情况自动运行:
+1. 代码提交到Gerrit
+2. 合并请求创建
+3. 定期构建
+
+### 测试报告
+- 前端: 生成HTML覆盖率报告
+- 后端: 生成Surefire测试报告
+- 集成到Jenkins构建结果
+
+## 故障排除
+
+### 常见问题
+1. **测试超时**: 增加超时时间或优化测试逻辑
+2. **Mock失效**: 检查Mock配置和方法签名
+3. **异步测试**: 使用适当的异步测试工具
+
+### 调试技巧
+1. 使用`console.log`输出调试信息
+2. 运行单个测试文件进行调试
+3. 检查测试数据和期望结果
+
+## 维护指南
+
+### 添加新测试
+1. 为新功能编写对应测试
+2. 确保测试覆盖正常和异常情况
+3. 更新测试文档
+
+### 更新现有测试
+1. 功能变更时同步更新测试
+2. 保持测试数据的有效性
+3. 定期重构测试代码 
\ No newline at end of file
diff --git a/tests/services/post.test.ts b/tests/services/post.test.ts
new file mode 100644
index 0000000..8481bf6
--- /dev/null
+++ b/tests/services/post.test.ts
@@ -0,0 +1,711 @@
+/**
+ * 帖子服务单元测试
+ * 
+ * 测试覆盖所有帖子相关的API接口
+ * 包括:帖子列表、详情、评论、收藏、点赞、发布、审核、举报等功能
+ */
+
+import * as postService from '../../src/services/post';
+
+// Mock request module
+jest.mock('@umijs/max', () => ({
+  request: jest.fn(),
+}));
+
+const { request } = require('@umijs/max');
+
+describe('Post Service', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  // 帖子列表相关测试
+  describe('getPostList', () => {
+    it('应该成功获取帖子列表', async () => {
+      const mockResponse = {
+        code: 200,
+        rows: [
+          {
+            postId: 1,
+            title: '测试帖子',
+            content: '测试内容',
+            author: 'testuser',
+            views: 100,
+            comments: 5,
+            favorites: 10,
+            likes: 15,
+            tags: 'tag1,tag2',
+            publishTime: '2024-01-01 12:00:00',
+            status: '1'
+          }
+        ],
+        total: 1
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getPostList({ pageNum: 1, pageSize: 10 });
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/list', {
+        method: 'GET',
+        params: { pageNum: 1, pageSize: 10 },
+      });
+    });
+
+    it('应该处理带标签筛选的帖子列表请求', async () => {
+      const mockResponse = { code: 200, rows: [], total: 0 };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      await postService.getPostList({ pageNum: 1, pageSize: 10, tags: '日剧' });
+      
+      expect(request).toHaveBeenCalledWith('/api/post-center/list', {
+        method: 'GET',
+        params: { pageNum: 1, pageSize: 10, tags: '日剧' },
+      });
+    });
+
+    it('应该处理搜索关键词的请求', async () => {
+      const mockResponse = { code: 200, rows: [], total: 0 };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      await postService.getPostList({ pageNum: 1, pageSize: 10, title: '搜索关键词' });
+      
+      expect(request).toHaveBeenCalledWith('/api/post-center/list', {
+        method: 'GET',
+        params: { pageNum: 1, pageSize: 10, title: '搜索关键词' },
+      });
+    });
+  });
+
+  // 帖子详情相关测试
+  describe('getPostDetail', () => {
+    it('应该成功获取帖子详情', async () => {
+      const mockResponse = {
+        code: 200,
+        data: {
+          post: {
+            postId: 1,
+            title: '测试帖子',
+            content: '测试内容',
+            author: 'testuser',
+            views: 100,
+            comments: 5,
+            favorites: 10,
+            likes: 15,
+            tags: 'tag1,tag2',
+            publishTime: '2024-01-01 12:00:00',
+            status: '1'
+          },
+          tags: [],
+          comments: [],
+          recommendedPosts: [],
+          favorited: false
+        }
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getPostDetail(1);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/1', {
+        method: 'GET',
+      });
+    });
+
+    it('应该处理获取不存在帖子的情况', async () => {
+      const mockResponse = { code: 404, msg: '帖子不存在' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getPostDetail(999);
+      
+      expect(result).toEqual(mockResponse);
+    });
+  });
+
+  // 评论相关测试
+  describe('addComment', () => {
+    it('应该成功添加评论', async () => {
+      const mockResponse = { code: 200, msg: '评论成功', data: { commentId: 1 } };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const commentData = {
+        postId: 1,
+        content: '这是一条测试评论',
+        parentId: 0
+      };
+      
+      const result = await postService.addComment(commentData);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/comment', {
+        method: 'POST',
+        data: commentData,
+      });
+    });
+
+    it('应该处理空评论内容', async () => {
+      const mockResponse = { code: 400, msg: '评论内容不能为空' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.addComment({ postId: 1, content: '' });
+      
+      expect(result).toEqual(mockResponse);
+    });
+  });
+
+  // 收藏相关测试
+  describe('toggleFavorite', () => {
+    it('应该成功收藏帖子', async () => {
+      const mockResponse = { code: 200, msg: '收藏成功' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.toggleFavorite(1, true);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/favorite/1', {
+        method: 'POST',
+        params: { favorite: true },
+      });
+    });
+
+    it('应该成功取消收藏帖子', async () => {
+      const mockResponse = { code: 200, msg: '取消收藏成功' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.toggleFavorite(1, false);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/favorite/1', {
+        method: 'POST',
+        params: { favorite: false },
+      });
+    });
+  });
+
+  // 点赞相关测试
+  describe('toggleLike', () => {
+    it('应该成功点赞帖子', async () => {
+      const mockResponse = { code: 200, msg: '点赞成功' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.toggleLike(1, true);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/like/1', {
+        method: 'POST',
+        params: { like: true },
+      });
+    });
+
+    it('应该成功取消点赞帖子', async () => {
+      const mockResponse = { code: 200, msg: '取消点赞成功' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.toggleLike(1, false);
+      
+      expect(result).toEqual(mockResponse);
+    });
+  });
+
+  // 评论点赞测试
+  describe('toggleCommentLike', () => {
+    it('应该成功点赞评论', async () => {
+      const mockResponse = { code: 200, msg: '点赞成功' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.toggleCommentLike(1, true);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/comment/like/1', {
+        method: 'POST',
+        params: { like: true },
+      });
+    });
+  });
+
+  // 发布帖子测试
+  describe('publishPost', () => {
+    it('应该成功发布帖子', async () => {
+      const mockResponse = { code: 200, msg: '发布成功' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const postData = {
+        title: '新帖子标题',
+        content: '新帖子内容',
+        summary: '新帖子摘要',
+        tags: 'tag1,tag2',
+        promotionPlan: 1
+      };
+      
+      const result = await postService.publishPost(postData);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/publish', {
+        method: 'POST',
+        data: postData,
+      });
+    });
+
+    it('应该处理发布帖子失败的情况', async () => {
+      const mockResponse = { code: 400, msg: '标题不能为空' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.publishPost({
+        title: '',
+        content: '内容',
+        summary: '摘要',
+        tags: 'tag1'
+      });
+      
+      expect(result).toEqual(mockResponse);
+    });
+  });
+
+  // 个人帖子列表测试
+  describe('getMyPosts', () => {
+    it('应该成功获取我的帖子列表', async () => {
+      const mockResponse = {
+        code: 200,
+        rows: [
+          {
+            postId: 1,
+            title: '我的帖子',
+            author: 'currentUser',
+            status: '1'
+          }
+        ],
+        total: 1
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getMyPosts({ pageNum: 1, pageSize: 10 });
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/my-posts', {
+        method: 'GET',
+        params: { pageNum: 1, pageSize: 10 },
+      });
+    });
+  });
+
+  // 收藏列表测试
+  describe('getMyFavorites', () => {
+    it('应该成功获取我的收藏列表', async () => {
+      const mockResponse = {
+        code: 200,
+        rows: [
+          {
+            postId: 1,
+            title: '收藏的帖子',
+            author: 'otherUser'
+          }
+        ],
+        total: 1
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getMyFavorites({ pageNum: 1, pageSize: 10 });
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/my-favorites', {
+        method: 'GET',
+        params: { pageNum: 1, pageSize: 10 },
+      });
+    });
+  });
+
+  // 更新帖子测试
+  describe('updatePost', () => {
+    it('应该成功更新帖子', async () => {
+      const postData: API.Post.PostInfo = {
+        postId: 1,
+        title: '更新的标题',
+        content: '更新的内容',
+        author: 'testuser',
+        views: 0,
+        comments: 0,
+        favorites: 0,
+        likes: 0,
+        tags: 'tag1,tag2',
+        publishTime: new Date().toISOString(),
+        status: '1'
+      };
+      
+      const mockResponse = { code: 200, msg: '更新成功', data: null };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.updatePost(postData);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/update', {
+        method: 'PUT',
+        data: postData,
+      });
+    });
+  });
+
+  // 删除帖子测试
+  describe('deletePost', () => {
+    it('应该成功删除帖子', async () => {
+      const mockResponse = { code: 200, msg: '删除成功' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.deletePost(1);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/delete/1', {
+        method: 'DELETE',
+      });
+    });
+  });
+
+  // 标签相关测试
+  describe('tag operations', () => {
+    it('应该成功获取热门标签', async () => {
+      const mockResponse = {
+        code: 200,
+        data: [
+          { tagId: 1, tagName: '日剧', postCount: 10 },
+          { tagId: 2, tagName: '电影', postCount: 8 }
+        ]
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getHotTags();
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/tags/hot', {
+        method: 'GET',
+      });
+    });
+
+    it('应该成功获取可用标签', async () => {
+      const mockResponse = {
+        code: 200,
+        data: [
+          { tagId: 1, tagName: '日剧', tagColor: 'blue' },
+          { tagId: 2, tagName: '电影', tagColor: 'green' }
+        ]
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getAvailableTags();
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/tags/available', {
+        method: 'GET',
+      });
+    });
+
+    it('应该成功根据标签获取帖子', async () => {
+      const mockResponse = {
+        code: 200,
+        rows: [{ postId: 1, title: '日剧帖子' }],
+        total: 1
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getPostsByTag(1);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/bytag/1', {
+        method: 'GET',
+      });
+    });
+  });
+
+  // 图片上传测试
+  describe('image operations', () => {
+    it('应该成功上传图片', async () => {
+      const mockResponse = {
+        code: 200,
+        data: {
+          url: '/images/123456_test.jpg',
+          filename: '123456_test.jpg',
+          originalName: 'test.jpg',
+          size: '1024'
+        }
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const formData = new FormData();
+      formData.append('file', new Blob(['test'], { type: 'image/jpeg' }));
+      
+      const result = await postService.uploadImage(formData);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/upload', {
+        method: 'POST',
+        data: formData,
+      });
+    });
+
+    it('应该成功删除图片', async () => {
+      const mockResponse = { code: 200, msg: '删除成功' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.deleteImage('test.jpg');
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/upload', {
+        method: 'DELETE',
+        params: { filename: 'test.jpg' },
+      });
+    });
+  });
+
+  // 推广相关测试
+  describe('promotion operations', () => {
+    it('应该成功获取推广帖子', async () => {
+      const mockResponse = {
+        code: 200,
+        data: [
+          {
+            postId: 1,
+            title: '推广帖子',
+            promotionPlanId: 1
+          }
+        ]
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getPromotionPosts();
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/promotion', {
+        method: 'GET',
+      });
+    });
+
+    it('应该成功获取推广计划', async () => {
+      const mockResponse = {
+        code: 200,
+        data: [
+          {
+            id: 1,
+            name: '首页推荐',
+            description: '帖子显示在首页推荐位置',
+            price: 50.00,
+            duration: 7
+          }
+        ]
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getPromotionPlans();
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/promotion-plans', {
+        method: 'GET',
+      });
+    });
+
+    it('应该成功创建支付记录', async () => {
+      const mockResponse = {
+        code: 200,
+        data: {
+          paymentId: 1,
+          postId: 1,
+          planId: 1,
+          amount: 50.00,
+          paymentStatus: 'pending'
+        }
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const paymentData = {
+        postId: 1,
+        planId: 1,
+        amount: 50.00
+      };
+      
+      const result = await postService.createPayment(paymentData);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/payment', {
+        method: 'POST',
+        data: paymentData,
+      });
+    });
+
+    it('应该成功获取推广状态', async () => {
+      const mockResponse = {
+        code: 200,
+        data: {
+          hasPromotion: true,
+          promotionPlanId: 1
+        }
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getPromotionStatus(1);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/promotion-status/1', {
+        method: 'GET',
+      });
+    });
+
+    it('应该成功确认支付', async () => {
+      const mockResponse = { code: 200, msg: '支付成功' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.confirmPayment(1);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/payment/confirm/1', {
+        method: 'POST',
+      });
+    });
+
+    it('应该成功取消支付', async () => {
+      const mockResponse = { code: 200, msg: '支付已取消' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.cancelPayment(1);
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/payment/cancel/1', {
+        method: 'POST',
+      });
+    });
+  });
+
+  // 举报相关测试
+  describe('report operations', () => {
+    it('应该成功举报帖子', async () => {
+      const mockResponse = { code: 200, msg: '举报提交成功' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.reportPost(1, '内容不当');
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post-center/report/1', {
+        method: 'POST',
+        data: { reason: '内容不当' },
+      });
+    });
+  });
+
+  // 管理员功能测试
+  describe('admin operations', () => {
+    it('应该成功获取待审核帖子', async () => {
+      const mockResponse = {
+        code: 200,
+        rows: [
+          {
+            postId: 1,
+            title: '待审核帖子',
+            status: '0'
+          }
+        ],
+        total: 1
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getReviewPosts({ status: '0' });
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post/review/list', {
+        method: 'GET',
+        params: { status: '0' },
+      });
+    });
+
+    it('应该成功审核帖子', async () => {
+      const mockResponse = { code: 200, msg: '审核成功' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.reviewPost(1, 'approve', '内容合规');
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post/review/1', {
+        method: 'PUT',
+        data: { action: 'approve', reason: '内容合规' },
+      });
+    });
+
+    it('应该成功下架帖子', async () => {
+      const mockResponse = { code: 200, msg: '下架成功' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.takeDownPost(1, '违规内容');
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post/takedown/1', {
+        method: 'PUT',
+        data: { reason: '违规内容' },
+      });
+    });
+
+    it('应该成功获取举报列表', async () => {
+      const mockResponse = {
+        code: 200,
+        rows: [
+          {
+            reportId: 1,
+            postId: 1,
+            reportReason: '内容不当',
+            status: '0'
+          }
+        ],
+        total: 1
+      };
+      
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getReportList({});
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post/report/list', {
+        method: 'GET',
+        params: {},
+      });
+    });
+
+    it('应该成功处理举报', async () => {
+      const mockResponse = { code: 200, msg: '举报处理成功' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.handleReport(1, 'approve', 1, '举报属实');
+      
+      expect(result).toEqual(mockResponse);
+      expect(request).toHaveBeenCalledWith('/api/post/report/handle/1', {
+        method: 'PUT',
+        data: { action: 'approve', postId: 1, reason: '举报属实' },
+      });
+    });
+  });
+
+  // 错误处理测试
+  describe('error handling', () => {
+    it('应该处理网络错误', async () => {
+      const error = new Error('Network Error');
+      (request as jest.Mock).mockRejectedValue(error);
+      
+      await expect(postService.getPostList({})).rejects.toThrow('Network Error');
+    });
+
+    it('应该处理服务器错误响应', async () => {
+      const mockResponse = { code: 500, msg: '服务器内部错误' };
+      (request as jest.Mock).mockResolvedValue(mockResponse);
+      
+      const result = await postService.getPostList({});
+      
+      expect(result).toEqual(mockResponse);
+    });
+  });
+}); 
\ No newline at end of file
diff --git a/tests/setup.ts b/tests/setup.ts
new file mode 100644
index 0000000..96ffc69
--- /dev/null
+++ b/tests/setup.ts
@@ -0,0 +1,53 @@
+import '@testing-library/jest-dom';
+
+// 全局测试配置
+global.console = {
+  ...console,
+  // 在测试中禁用console.log以减少噪音
+  log: jest.fn(),
+  debug: jest.fn(),
+  info: jest.fn(),
+  warn: jest.fn(),
+  error: jest.fn(),
+};
+
+// Mock window.matchMedia
+Object.defineProperty(window, 'matchMedia', {
+  writable: true,
+  value: jest.fn().mockImplementation(query => ({
+    matches: false,
+    media: query,
+    onchange: null,
+    addListener: jest.fn(), // deprecated
+    removeListener: jest.fn(), // deprecated
+    addEventListener: jest.fn(),
+    removeEventListener: jest.fn(),
+    dispatchEvent: jest.fn(),
+  })),
+});
+
+// Mock ResizeObserver
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+  observe: jest.fn(),
+  unobserve: jest.fn(),
+  disconnect: jest.fn(),
+}));
+
+// Mock IntersectionObserver
+global.IntersectionObserver = jest.fn().mockImplementation(() => ({
+  observe: jest.fn(),
+  unobserve: jest.fn(),
+  disconnect: jest.fn(),
+}));
+
+// Mock localStorage
+const localStorageMock: Storage = {
+  getItem: jest.fn(),
+  setItem: jest.fn(),
+  removeItem: jest.fn(),
+  clear: jest.fn(),
+  length: 0,
+  key: jest.fn(),
+};
+global.localStorage = localStorageMock;
+global.sessionStorage = localStorageMock; 
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index c2ea9bc..22e3cf4 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,6 @@
 {
   "compilerOptions": {
-    "types": ["node"],
+    "types": ["node", "jest"],
     "target": "esnext",
     "module": "esnext",
     "moduleResolution": "node",
@@ -20,5 +20,5 @@
       "@@test/*": ["./src/.umi-test/*"]
     }
   },
-  "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"]
+  "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx", "src/pages/PostCenter/PostDetail.jsx", "src/pages/PostCenter/PostCard.jsx", "src/pages/PostCenter/index.jsx"]
 }