帖子的相关用户前端与管理员后端

Change-Id: I957181738817aeeaa89fabe23ceaf59d00c37a71
diff --git a/src/api/__tests__/post.test.js b/src/api/__tests__/post.test.js
new file mode 100644
index 0000000..89fe48f
--- /dev/null
+++ b/src/api/__tests__/post.test.js
@@ -0,0 +1,160 @@
+const axios = require('axios');
+const MockAdapter = require('axios-mock-adapter');
+
+const {
+    createPost,
+    togglePinPost,
+    deletePost,
+    updatePost,
+    searchPosts,
+    likePost,
+    unlikePost,
+    pinPost,
+    unpinPost,
+    findPostsByUserId,
+    findPinnedPosts,
+    getAllPostsSorted,
+    getPostById
+} = require('../post'); // 注意根据你的实际路径修改
+
+jest.mock('axios');
+
+describe('Post API Tests', () => {
+    beforeEach(() => {
+        jest.clearAllMocks();
+    });
+
+    test('createPost should post form data', async () => {
+        const formData = new FormData();
+        formData.append('userid', '1');
+        formData.append('post_title', 'Test');
+        axios.post.mockResolvedValue({ data: true });
+
+        const response = await createPost(formData);
+
+        expect(axios.post).toHaveBeenCalledWith(
+            'http://localhost:8080/post/create',
+            formData,
+            { headers: { 'Content-Type': 'multipart/form-data' } }
+        );
+        expect(response.data).toBe(true);
+    });
+
+    test('togglePinPost should send PUT request', async () => {
+        axios.put.mockResolvedValue({ data: true });
+
+        const response = await togglePinPost(123);
+
+        expect(axios.put).toHaveBeenCalledWith('http://localhost:8080/post/togglePin/123');
+        expect(response.data).toBe(true);
+    });
+
+    test('deletePost should send DELETE request', async () => {
+        axios.delete.mockResolvedValue({ data: true });
+
+        const response = await deletePost(123);
+
+        expect(axios.delete).toHaveBeenCalledWith('http://localhost:8080/post/delete/123');
+        expect(response.data).toBe(true);
+    });
+
+    test('updatePost should send PUT request with JSON', async () => {
+        const post = { postid: 1, post_title: 'Updated Title' };
+        axios.put.mockResolvedValue({ data: true });
+
+        const response = await updatePost(post);
+
+        expect(axios.put).toHaveBeenCalledWith(
+            'http://localhost:8080/post/update',
+            JSON.stringify(post),
+            { headers: { 'Content-Type': 'application/json' } }
+        );
+        expect(response.data).toBe(true);
+    });
+
+    test('searchPosts should send GET request with keyword', async () => {
+        const mockData = { data: [{ post_title: 'Keyword Match' }] };
+        axios.get.mockResolvedValue(mockData);
+
+        const response = await searchPosts('test');
+
+        expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/post/search?keyword=test');
+        expect(response.data).toEqual(mockData.data);
+    });
+
+    test('likePost should send PUT request', async () => {
+        axios.put.mockResolvedValue({ data: true });
+
+        const response = await likePost(1);
+
+        expect(axios.put).toHaveBeenCalledWith('http://localhost:8080/post/like/1');
+        expect(response.data).toBe(true);
+    });
+
+    test('unlikePost should send PUT request', async () => {
+        axios.put.mockResolvedValue({ data: true });
+
+        const response = await unlikePost(1);
+
+        expect(axios.put).toHaveBeenCalledWith('http://localhost:8080/post/unlike/1');
+        expect(response.data).toBe(true);
+    });
+
+    test('pinPost should send PUT request', async () => {
+        axios.put.mockResolvedValue({ data: true });
+
+        const response = await pinPost(1);
+
+        expect(axios.put).toHaveBeenCalledWith('http://localhost:8080/post/pin/1');
+        expect(response.data).toBe(true);
+    });
+
+    test('unpinPost should send PUT request', async () => {
+        axios.put.mockResolvedValue({ data: true });
+
+        const response = await unpinPost(1);
+
+        expect(axios.put).toHaveBeenCalledWith('http://localhost:8080/post/unpin/1');
+        expect(response.data).toBe(true);
+    });
+
+    test('findPostsByUserId should fetch user posts', async () => {
+        const mockData = { data: [{ postid: 1, userid: 2 }] };
+        axios.get.mockResolvedValue(mockData);
+
+        const response = await findPostsByUserId(2);
+
+        expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/post/findByUserid?userid=2');
+        expect(response.data).toEqual(mockData.data);
+    });
+
+    test('findPinnedPosts should fetch pinned posts', async () => {
+        const mockData = { data: [{ postid: 1, is_pinned: 1 }] };
+        axios.get.mockResolvedValue(mockData);
+
+        const response = await findPinnedPosts();
+
+        expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/post/findPinned');
+        expect(response.data).toEqual(mockData.data);
+    });
+
+    test('getAllPostsSorted should fetch all posts sorted', async () => {
+        const mockData = { data: [{ postid: 1 }] };
+        axios.get.mockResolvedValue(mockData);
+
+        const response = await getAllPostsSorted();
+
+        expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/post/all');
+        expect(response.data).toEqual(mockData.data);
+    });
+
+    test('getPostById should fetch post by ID', async () => {
+        const mockData = { data: { postid: 1 } };
+        axios.get.mockResolvedValue(mockData);
+
+        const response = await getPostById(1);
+
+        expect(axios.get).toHaveBeenCalledWith('http://localhost:8080/post/get/1');
+        expect(response.data).toEqual(mockData.data);
+    });
+});
diff --git a/src/api/post.js b/src/api/post.js
new file mode 100644
index 0000000..5891f99
--- /dev/null
+++ b/src/api/post.js
@@ -0,0 +1,128 @@
+const BASE_URL = 'http://localhost:8080/post';
+
+/**
+ * 创建帖子(带图片)
+ * @param {FormData} formData 包含 userid、post_title、post_content、tags、rannge、is_pinned、photo
+ */
+export const createPost = (formData) => {
+    return fetch(`${BASE_URL}/create`, {
+        method: 'POST',
+        body: formData,
+    }).then(res => res.json());
+};
+
+/**
+ * 切换置顶状态
+ * @param {number} postid 帖子 ID
+ */
+export const togglePinPost = (postid) => {
+    return fetch(`${BASE_URL}/togglePin/${postid}`, {
+        method: 'PUT',
+    }).then(res => res.json());
+};
+
+/**
+ * 删除帖子
+ * @param {number} postid 帖子 ID
+ */
+export const deletePost = (postid) => {
+    return fetch(`${BASE_URL}/delete/${postid}`, {
+        method: 'DELETE',
+    }).then(res => res.json());
+};
+
+/**
+ * 更新帖子(JSON 格式)
+ * @param {Object} post 帖子对象
+ */
+export const updatePost = (post) => {
+    return fetch(`${BASE_URL}/update`, {
+        method: 'PUT',
+        headers: {
+            'Content-Type': 'application/json',
+        },
+        body: JSON.stringify(post),
+    }).then(res => res.json());
+};
+
+/**
+ * 关键词搜索帖子
+ * @param {string} keyword 搜索关键词
+ */
+export const searchPosts = (keyword) => {
+    return fetch(`${BASE_URL}/search?keyword=${encodeURIComponent(keyword)}`)
+        .then(res => res.json());
+};
+
+/**
+ * 点赞帖子
+ * @param {number} postid 帖子 ID
+ */
+export const likePost = (postid) => {
+    return fetch(`${BASE_URL}/like/${postid}`, {
+        method: 'PUT',
+    }).then(res => res.json());
+};
+
+/**
+ * 取消点赞帖子
+ * @param {number} postid 帖子 ID
+ */
+export const unlikePost = (postid) => {
+    return fetch(`${BASE_URL}/unlike/${postid}`, {
+        method: 'PUT',
+    }).then(res => res.json());
+};
+
+/**
+ * 置顶帖子
+ * @param {number} postid 帖子 ID
+ */
+export const pinPost = (postid) => {
+    return fetch(`${BASE_URL}/pin/${postid}`, {
+        method: 'PUT',
+    }).then(res => res.json());
+};
+
+/**
+ * 取消置顶帖子
+ * @param {number} postid 帖子 ID
+ */
+export const unpinPost = (postid) => {
+    return fetch(`${BASE_URL}/unpin/${postid}`, {
+        method: 'PUT',
+    }).then(res => res.json());
+};
+
+/**
+ * 获取某用户所有帖子
+ * @param {number} userid 用户 ID
+ */
+export const findPostsByUserId = (userid) => {
+    return fetch(`${BASE_URL}/findByUserid?userid=${userid}`)
+        .then(res => res.json());
+};
+
+/**
+ * 获取所有置顶帖子
+ */
+export const findPinnedPosts = () => {
+    return fetch(`${BASE_URL}/findPinned`)
+        .then(res => res.json());
+};
+
+/**
+ * 获取所有帖子(排序后)
+ */
+export const getAllPostsSorted = () => {
+    return fetch(`${BASE_URL}/all`)
+        .then(res => res.json());
+};
+
+/**
+ * 根据 postid 获取帖子详情(若你后期需要)
+ */
+export const getPostById = (postid) => {
+    return fetch(`${BASE_URL}/get/${postid}`)
+        .then(res => res.json());
+};