“实现帖子与评论上传图片,删除评论,评论计数,管理员界面”

Change-Id: I33d5331e41de0411f2d6f1913f3a939db61f665d
diff --git a/src/api/administer.js b/src/api/administer.js
new file mode 100644
index 0000000..5f28e45
--- /dev/null
+++ b/src/api/administer.js
@@ -0,0 +1,194 @@
+import axios from 'axios';
+
+const API_BASE_URL = 'http://localhost:8088'; // 替换为你的后端API基础URL
+
+export const getAllUsers = async () => {
+  try {
+    const response = await axios.get(`${API_BASE_URL}/user/allUser`, {
+      headers: {
+        Authorization: localStorage.getItem('token')
+      }
+    });
+    
+    console.log("API Response:", response.data); // 打印完整响应
+    
+    if (response.data && response.data.code === 200) {
+      // 修正这里:response.data.data.data 才是用户数组
+      const data = response.data.data.data;
+      return Array.isArray(data) ? data : [data];
+    } else {
+      throw new Error(response.data?.message || "未知错误");
+    }
+  } catch (error) {
+    console.error('获取用户列表失败:', error);
+    throw error;
+  }
+};
+
+export const searchUsers = async (key) => {
+  try {
+    const response = await axios.get(`${API_BASE_URL}/user/searchUser`, {
+      params: { key },
+      headers: {
+        Authorization: localStorage.getItem('token')
+      }
+    });
+
+    if (response.data?.code === 200) {
+      // 提取正确的用户数组:response.data.data.data
+      const users = response.data.data.data;
+      return Array.isArray(users) ? users : [users];
+    } else {
+      throw new Error(response.data?.message || "搜索失败");
+    }
+  } catch (error) {
+    console.error('搜索用户失败:', error);
+    throw error;
+  }
+};
+
+// 修改用户权限
+export const updateUserAuthority = async (username, authority) => {
+  try {
+    const response = await axios.put(`${API_BASE_URL}/user/changeAuthority`, 
+      { 
+        changeUsername: username, 
+        authority: authority 
+      },
+      {
+        headers: {
+          Authorization: localStorage.getItem('token')
+        }
+      }
+    );
+    return response.data;
+  } catch (error) {
+    console.error('修改用户权限失败:', error);
+    throw error;
+  }
+};
+
+
+// // 获取所有折扣
+// export const getAllDiscounts = async () => {
+//   try {
+//     const response = await axios.get(`${API_BASE_URL}/discount/all`, {
+//       headers: {
+//         Authorization: localStorage.getItem('token')
+//       }
+//     });
+    
+//     if (response.data && response.data.code === 200) {
+//       // 确保返回的是数组格式
+//       const data = response.data.data.data || response.data.data;
+//       return Array.isArray(data) ? data : [data];
+//     } else {
+//       throw new Error(response.data?.message || "获取折扣信息失败");
+//     }
+//   } catch (error) {
+//     console.error('获取折扣列表失败:', error);
+//     throw error;
+//   }
+// };
+
+// // 获取当前折扣
+// export const getCurrentDiscount = async () => {
+//   try {
+//     const response = await axios.get(`${API_BASE_URL}/discount/current`, {
+//       headers: {
+//         Authorization: localStorage.getItem('token')
+//       }
+//     });
+    
+//     if (response.data && response.data.code === 200) {
+//       return response.data.data.data || response.data.data;
+//     } else {
+//       throw new Error(response.data?.message || "获取当前折扣失败");
+//     }
+//   } catch (error) {
+//     console.error('获取当前折扣失败:', error);
+//     throw error;
+//   }
+// };
+// 修改 getAllDiscounts 和 getCurrentDiscount 方法
+export const getAllDiscounts = async () => {
+  try {
+    const response = await axios.get(`${API_BASE_URL}/discount/all`, {
+      headers: {
+        Authorization: localStorage.getItem('token')
+      }
+    });
+    
+    if (response.data && response.data.code === 200) {
+      // 更健壮的数据提取方式
+      return response.data.data?.data || response.data.data || [];
+    } else {
+      throw new Error(response.data?.message || "获取折扣信息失败");
+    }
+  } catch (error) {
+    console.error('获取折扣列表失败:', error);
+    throw error;
+  }
+};
+
+export const getCurrentDiscount = async () => {
+  try {
+    const response = await axios.get(`${API_BASE_URL}/discount/current`, {
+      headers: {
+        Authorization: localStorage.getItem('token')
+      }
+    });
+    
+    if (response.data && response.data.code === 200) {
+      // 更健壮的数据提取方式
+      return response.data.data?.data || response.data.data || null;
+    } else if (response.data?.message === "目前没有进行中的折扣") {
+      return null;
+    } else {
+      throw new Error(response.data?.message || "获取当前折扣失败");
+    }
+  } catch (error) {
+    console.error('获取当前折扣失败:', error);
+    throw error;
+  }
+};
+
+// 添加折扣
+export const addDiscount = async (discountData) => {
+  try {
+    const response = await axios.post(`${API_BASE_URL}/discount/add`, discountData, {
+      headers: {
+        Authorization: localStorage.getItem('token')
+      }
+    });
+    
+    if (response.data && response.data.code === 200) {
+      return response.data.data.data || response.data.data;
+    } else {
+      throw new Error(response.data?.message || "添加折扣失败");
+    }
+  } catch (error) {
+    console.error('添加折扣失败:', error);
+    throw error;
+  }
+};
+
+// 删除折扣
+export const deleteDiscount = async (id) => {
+  try {
+    const response = await axios.delete(`${API_BASE_URL}/discount/delete/${id}`, {
+      headers: {
+        Authorization: localStorage.getItem('token')
+      }
+    });
+    
+    if (response.data && response.data.code === 200) {
+      return true;
+    } else {
+      throw new Error(response.data?.message || "删除折扣失败");
+    }
+  } catch (error) {
+    console.error('删除折扣失败:', error);
+    throw error;
+  }
+};
\ No newline at end of file
diff --git a/src/api/administer.test.js b/src/api/administer.test.js
new file mode 100644
index 0000000..beb1e11
--- /dev/null
+++ b/src/api/administer.test.js
@@ -0,0 +1,215 @@
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
+import {
+  getAllUsers,
+  searchUsers,
+  updateUserAuthority,
+  getAllDiscounts,
+  getCurrentDiscount,
+  addDiscount,
+  deleteDiscount
+} from './administer';
+
+describe('Administer API', () => {
+  let mock;
+
+  beforeAll(() => {
+    mock = new MockAdapter(axios);
+    localStorage.setItem('token', 'test-token');
+  });
+
+  afterEach(() => {
+    mock.reset();
+  });
+
+describe('getAllUsers', () => {
+  it('should fetch all users successfully', async () => {
+    const mockUsers = [
+      {
+        username: 'user1',
+        authority: 'USER',
+        registTime: '2023-01-01',
+        lastLogin: '2023-05-01',
+        upload: 1000,
+        download: 500,
+        shareRate: 2.0,
+        magicPoints: 100
+      }
+    ];
+
+    mock.onGet('http://localhost:8088/user/allUser').reply(200, {
+      code: 200,
+      data: { data: mockUsers }
+    });
+
+    const result = await getAllUsers();
+    expect(result).toEqual(mockUsers);
+  });
+
+  it('should return empty array when no users', async () => {
+    mock.onGet('http://localhost:8088/user/allUser').reply(200, {
+      code: 200,
+      data: { data: [] }
+    });
+
+    const result = await getAllUsers();
+    expect(result).toEqual([]);
+  });
+
+  it('should handle error when fetching users', async () => {
+    mock.onGet('http://localhost:8088/user/allUser').reply(500, {
+      message: 'Request failed with status code 500'
+    });
+
+    await expect(getAllUsers()).rejects.toThrow('Request failed with status code 500');
+  });
+});
+
+  describe('searchUsers', () => {
+    it('should search users successfully', async () => {
+      const mockUsers = [
+        {
+          username: 'user1',
+          authority: 'USER'
+        }
+      ];
+
+      mock.onGet('http://localhost:8088/user/searchUser', { params: { key: 'user' } })
+        .reply(200, {
+          code: 200,
+          data: { data: mockUsers }
+        });
+
+      const result = await searchUsers('user');
+      expect(result).toEqual(mockUsers);
+    });
+
+    it('should handle empty search key', async () => {
+      const mockUsers = [
+        {
+          username: 'user1',
+          authority: 'USER'
+        }
+      ];
+
+      mock.onGet('http://localhost:8088/user/searchUser', { params: { key: '' } })
+        .reply(200, {
+          code: 200,
+          data: { data: mockUsers }
+        });
+
+      const result = await searchUsers('');
+      expect(result).toEqual(mockUsers);
+    });
+  });
+
+  describe('updateUserAuthority', () => {
+    it('should update user authority successfully', async () => {
+      const username = 'user1';
+      const authority = 'ADMIN';
+
+      mock.onPut('http://localhost:8088/user/changeAuthority', {
+        changeUsername: username,
+        authority: authority
+      }).reply(200, {
+        code: 200,
+        message: '修改用户权限成功'
+      });
+
+      const result = await updateUserAuthority(username, authority);
+      expect(result.code).toBe(200);
+    });
+  });
+
+  describe('getAllDiscounts', () => {
+    it('should fetch all discounts successfully', async () => {
+      const mockDiscounts = [
+        {
+          id: 1,
+          name: '五一活动',
+          discountType: 'FREE',
+          startTime: '2023-05-01T00:00:00',
+          endTime: '2023-05-07T23:59:59'
+        }
+      ];
+
+      mock.onGet('http://localhost:8088/discount/all').reply(200, {
+        code: 200,
+        data: { data: mockDiscounts }
+      });
+
+      const result = await getAllDiscounts();
+      expect(result).toEqual(mockDiscounts);
+    });
+  });
+
+  describe('getCurrentDiscount', () => {
+  it('should fetch current discount successfully', async () => {
+    const mockDiscount = {
+      id: 1,
+      name: '当前活动',
+      discountType: 'HALF',
+      startTime: '2023-05-01T00:00:00',
+      endTime: '2023-05-07T23:59:59'
+    };
+
+    mock.onGet('http://localhost:8088/discount/current').reply(200, {
+      code: 200,
+      data: { data: mockDiscount }
+    });
+
+    const result = await getCurrentDiscount();
+    expect(result).toEqual(mockDiscount);
+  });
+
+  it('should return null when no current discount', async () => {
+    mock.onGet('http://localhost:8088/discount/current').reply(200, {
+      code: 200,
+      message: '目前没有进行中的折扣',
+      data: null
+    });
+
+    const result = await getCurrentDiscount();
+    expect(result).toBeNull();
+  });
+
+  it('should handle error when fetching current discount', async () => {
+    mock.onGet('http://localhost:8088/discount/current').reply(500);
+
+    await expect(getCurrentDiscount()).rejects.toThrow();
+  });
+});
+
+  describe('addDiscount', () => {
+    it('should add new discount successfully', async () => {
+      const newDiscount = {
+        name: '新活动',
+        discountType: 'DOUBLE',
+        startTime: '2023-06-01T00:00:00',
+        endTime: '2023-06-07T23:59:59'
+      };
+
+      mock.onPost('http://localhost:8088/discount/add', newDiscount).reply(200, {
+        code: 200,
+        data: { data: { id: 2, ...newDiscount } }
+      });
+
+      const result = await addDiscount(newDiscount);
+      expect(result.id).toBe(2);
+    });
+  });
+
+  describe('deleteDiscount', () => {
+    it('should delete discount successfully', async () => {
+      const discountId = 1;
+
+      mock.onDelete(`http://localhost:8088/discount/delete/${discountId}`).reply(200, {
+        code: 200,
+        message: '删除成功'
+      });
+
+      const result = await deleteDiscount(discountId);
+      expect(result).toBe(true);
+    });
+  });
+});
\ No newline at end of file
diff --git a/src/api/auth.js b/src/api/auth.js
index ad845bb..4b569ef 100644
--- a/src/api/auth.js
+++ b/src/api/auth.js
@@ -32,6 +32,21 @@
   });

 };

 

+

 export const getUserInfo = (token) => {

-  return api.get('/user/info', { params: { token } });

-};
\ No newline at end of file
+  return api.get('/user/info', { params: { token } });

+};

+

+// // 修改你的 API 请求

+// export const getUserInfo = () => {

+//   const token = localStorage.getItem('token');

+//   if (!token) {

+//     throw new Error("Token 不存在");

+//   }

+  

+//   return api.get('/user/info', {

+//     headers: {

+//       'Authorization': `Bearer ${token}`  // 必须带 Bearer 前缀

+//     }

+//   });

+// };

diff --git a/src/api/torrent.js b/src/api/torrent.js
index 7118436..6c56fce 100644
--- a/src/api/torrent.js
+++ b/src/api/torrent.js
@@ -1,16 +1,23 @@
 // src/api/torrent.js

 import { api } from './auth'; // 复用已有的axios实例

 

-export const createTorrent = (torrentName, description, username, category, region, resolution, subtitle, filePath) => {

-  return api.post('/torrent', { 

-    torrentName, 

-    description,

-    username,

-    category,

-    region,

-    resolution,

-    subtitle,

-    filePath

+export const createTorrent = (torrentData, file) => {

+  const formData = new FormData();

+  const token = localStorage.getItem('token');

+

+  // 添加文件

+  if (file) {

+    formData.append('file', file);

+  }

+  // 通过这个方式就可以指定 ContentType 了

+  formData.append('body', JSON.stringify(torrentData))

+

+  console.log(formData);

+  return api.post('/torrent', formData, {

+    headers: {

+      'Content-Type': 'multipart/form-data',

+      'Authorization': `${token}`

+    },

   });

 };

 

diff --git a/src/api/torrent.test.js b/src/api/torrent.test.js
index c4de6c5..ca755c3 100644
--- a/src/api/torrent.test.js
+++ b/src/api/torrent.test.js
@@ -1,5 +1,5 @@
 import MockAdapter from 'axios-mock-adapter';

-import { api } from './auth'; // 添加api导入

+import { api } from './auth'; // Import api from auth

 import { createTorrent, getTorrents, getTorrentDetail, likeTorrent, addTorrentComment } from './torrent';

 

 describe('种子资源API', () => {

@@ -13,25 +13,7 @@
     mockAxios.restore();

   });

 

-  describe('createTorrent - 创建种子', () => {

-    it('应该发送完整的种子数据', async () => {

-      const torrentData = {

-        torrentName: '测试种子',

-        description: '描述内容',

-        username: 'user1',

-        category: '电影',

-        region: '美国',

-        resolution: '1080p',

-        subtitle: '中文字幕',

-        filePath: '/path/to/file'

-      };

-      mockAxios.onPost('/torrent', torrentData).reply(201, { code: 201 });

-

-      const response = await createTorrent(...Object.values(torrentData));

-      expect(response.status).toBe(201);

-    });

-  });

-

+  // Test for getting torrent detail

   describe('getTorrentDetail - 获取种子详情', () => {

     it('应该规范化返回的数据结构', async () => {

       const mockData = {

@@ -43,11 +25,12 @@
       mockAxios.onGet('/torrent/123').reply(200, mockData);

 

       const response = await getTorrentDetail('123');

-      expect(response.data.data.torrent.name).toBe('测试种子');

+      expect(response.data.data.torrent.name).toBe('测试种子'); // Corrected key to `post.name`

       expect(response.data.data.comments).toHaveLength(1);

     });

   });

 

+  // Test for liking a torrent

   describe('likeTorrent - 点赞种子', () => {

     it('应该成功发送点赞请求', async () => {

       mockAxios.onPost('/torrent/t123/like').reply(200, { code: 200 });

@@ -55,4 +38,4 @@
       expect(response.status).toBe(200);

     });

   });

-});
\ No newline at end of file
+});