完成上传下载连接,公告管理与详情页面,求种区页面,轮播图折扣显示,修改部分bug
Change-Id: I86fc294e32911cb3426a8b16f90aca371f975c11
diff --git a/src/api/announcement.js b/src/api/announcement.js
new file mode 100644
index 0000000..6213d10
--- /dev/null
+++ b/src/api/announcement.js
@@ -0,0 +1,28 @@
+import { api } from './auth';
+
+// 获取所有公告
+export const getAnnouncements = () => {
+ return api.get('/announcement/list');
+};
+
+
+export const postAnnouncement = (data) => {
+ // 创建 FormData 对象
+ const formData = new FormData();
+ formData.append('title', data.title);
+ formData.append('content', data.content);
+
+ return api.post('/announcement/create', formData);
+}
+
+
+// 获取最新公告
+export const getLatestAnnouncements = () => {
+ return api.get('/announcement/latest');
+};
+
+// 获取公告详情
+export const getAnnouncementDetail = (id) => {
+ return api.get(`/announcement/${id}`);
+};
+
diff --git a/src/api/announcement.test.js b/src/api/announcement.test.js
new file mode 100644
index 0000000..bae9482
--- /dev/null
+++ b/src/api/announcement.test.js
@@ -0,0 +1,139 @@
+import MockAdapter from 'axios-mock-adapter';
+import { api } from './auth';
+import { getAnnouncements, getLatestAnnouncements, getAnnouncementDetail,postAnnouncement } from './announcement';
+
+describe('公告API', () => {
+ let mockAxios;
+
+ beforeEach(() => {
+ mockAxios = new MockAdapter(api);
+ });
+
+ afterEach(() => {
+ mockAxios.restore();
+ });
+
+describe('postAnnouncement - 发布公告', () => {
+ it('应该成功发布公告', async () => {
+ const mockData = {
+ title: '测试公告',
+ content: '测试内容'
+ };
+
+ const mockResponse = {
+ code: 200,
+ message: '公告发布成功',
+ data: {
+ id: 123,
+ title: mockData.title,
+ content: mockData.content,
+ createTime: new Date().toISOString()
+ }
+ };
+
+
+ mockAxios.onPost('/announcement/create').reply(200, mockResponse);
+
+ const response = await postAnnouncement(mockData);
+ expect(response.data).toEqual(mockResponse);
+ expect(response.data.code).toBe(200);
+ expect(response.data.data.title).toBe(mockData.title);
+ expect(response.data.data.content).toBe(mockData.content);
+ });
+});
+
+ describe('getAnnouncements - 获取所有公告', () => {
+ it('应该成功获取公告列表', async () => {
+ const mockResponse = {
+ code: 200,
+ data: {
+ announcements: [
+ { id: 1, title: '公告1', content: '内容1', createTime: '2023-01-01T00:00:00Z' },
+ { id: 2, title: '公告2', content: '内容2', createTime: '2023-01-01T00:00:00Z' }
+ ]
+ }
+ };
+
+ mockAxios.onGet('/announcement/list').reply(200, mockResponse);
+
+ const response = await getAnnouncements();
+ expect(response.data).toEqual(mockResponse);
+ expect(response.data.data.announcements).toHaveLength(2);
+ });
+
+
+ });
+
+ describe('getLatestAnnouncements - 获取最新公告', () => {
+ it('应该成功获取最新公告', async () => {
+ const mockResponse = {
+ code: 200,
+ data: {
+ announcements: [
+ { id: 1, title: '最新公告', content: '最新内容' }
+ ]
+ }
+ };
+
+ mockAxios.onGet('/announcement/latest').reply(200, mockResponse);
+
+ const response = await getLatestAnnouncements();
+ expect(response.data).toEqual(mockResponse);
+ expect(response.data.data.announcements).toHaveLength(1);
+ });
+
+ it('应该正确处理服务器错误', async () => {
+ mockAxios.onGet('/announcement/latest').reply(500);
+
+ await expect(getLatestAnnouncements()).rejects.toThrow();
+ });
+ });
+
+ describe('getAnnouncementDetail - 获取公告详情', () => {
+ it('应该成功获取公告详情', async () => {
+ const announcementId = 123;
+ const mockResponse = {
+ code: 200,
+ data: {
+ announcement: {
+ id: announcementId,
+ title: '详细公告',
+ content: '详细内容',
+ createTime: '2023-01-01T00:00:00Z'
+ }
+ }
+ };
+
+ mockAxios.onGet(`/announcement/${announcementId}`).reply(200, mockResponse);
+
+ const response = await getAnnouncementDetail(announcementId);
+ expect(response.data).toEqual(mockResponse);
+ expect(response.data.data.announcement.id).toBe(announcementId);
+ });
+
+ it('应该处理公告不存在的情况', async () => {
+ const announcementId = 999;
+ const mockResponse = {
+ code: 404,
+ message: '公告不存在'
+ };
+
+ mockAxios.onGet(`/announcement/${announcementId}`).reply(404, mockResponse);
+
+ try {
+ await getAnnouncementDetail(announcementId);
+ } catch (error) {
+ expect(error.response.status).toBe(404);
+ expect(error.response.data.code).toBe(404);
+ expect(error.response.data.message).toBe('公告不存在');
+ }
+ });
+
+ it('应该正确处理网络错误', async () => {
+ const announcementId = 456;
+ mockAxios.onGet(`/announcement/${announcementId}`).networkError();
+
+ await expect(getAnnouncementDetail(announcementId)).rejects.toThrow();
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/api/helpComment.js b/src/api/helpComment.js
index c1d00c0..2ff9ade 100644
--- a/src/api/helpComment.js
+++ b/src/api/helpComment.js
@@ -1,14 +1,14 @@
import { api } from './auth';
-export const likePostComment = (commentId) => {
+export const likeHelpPostComment = (commentId) => {
return api.post(`/help/comments/${commentId}/like`);
};
-export const getCommentReplies = (commentId) => {
+export const getHelpCommentReplies = (commentId) => {
return api.get(`/help/comments/${commentId}/replies`);
};
-export const addCommentReply = (commentId, replyData) => {
+export const addHelpCommentReply = (commentId, replyData) => {
const formData = new FormData();
formData.append('authorId', replyData.authorId);
formData.append('content', replyData.content);
@@ -21,7 +21,7 @@
return api.post(`/help/comments/${commentId}/replies`, formData);
};
-export const deleteComment = (commentId, authorId) => {
+export const deleteHelpComment = (commentId, authorId) => {
return api.delete(`/help/comments/${commentId}`, {
params: { authorId }
});
diff --git a/src/api/helpComment.test.js b/src/api/helpComment.test.js
index a4a7d99..99efd8c 100644
--- a/src/api/helpComment.test.js
+++ b/src/api/helpComment.test.js
@@ -1,6 +1,6 @@
import MockAdapter from 'axios-mock-adapter';
import { api } from './auth'; // 添加api导入
-import { likePostComment, getCommentReplies, addCommentReply, deleteComment } from './helpComment';
+import { likeHelpPostComment, getHelpCommentReplies, addHelpCommentReply, deleteHelpComment } from './helpComment';
describe('求助帖评论API', () => {
let mockAxios;
@@ -13,27 +13,27 @@
mockAxios.restore();
});
- describe('likePostComment - 点赞求助帖评论', () => {
+ describe('likeHelpPostComment - 点赞求助帖评论', () => {
it('应该成功发送点赞请求', async () => {
const mockResponse = { code: 200, message: '点赞成功' };
mockAxios.onPost('/help/comments/123/like').reply(200, mockResponse);
- const response = await likePostComment('123');
+ const response = await likeHelpPostComment('123');
expect(response.data).toEqual(mockResponse);
});
});
- describe('getCommentReplies - 获取评论回复', () => {
+ describe('getHelpCommentReplies - 获取评论回复', () => {
it('应该返回正确的回复数据结构', async () => {
const mockData = [{ id: '1', content: '回复内容' }];
mockAxios.onGet('/help/comments/456/replies').reply(200, { code: 200, data: mockData });
- const response = await getCommentReplies('456');
+ const response = await getHelpCommentReplies('456');
expect(response.data.data).toEqual(mockData);
});
});
- describe('addCommentReply - 添加评论回复', () => {
+ describe('addHelpCommentReply - 添加评论回复', () => {
it('应该正确发送回复内容(无图片)', async () => {
const commentId = '789';
const replyData = {
@@ -49,7 +49,7 @@
return [200, { code: 200 }];
});
- const response = await addCommentReply(commentId, replyData);
+ const response = await addHelpCommentReply(commentId, replyData);
expect(response.status).toBe(200);
});
it('应该正确处理带图片的回复', async () => {
@@ -66,11 +66,11 @@
return [200, { code: 200 }];
});
- const response = await addCommentReply(commentId, replyData);
+ const response = await addHelpCommentReply(commentId, replyData);
expect(response.status).toBe(200);
});
});
- describe('deleteComment - 删除评论', () => {
+ describe('deleteHelpComment - 删除评论', () => {
it('应该正确发送删除请求', async () => {
const commentId = '101112';
const authorId = 'user1';
@@ -80,7 +80,7 @@
return [200, { code: 200 }];
});
- const response = await deleteComment(commentId, authorId);
+ const response = await deleteHelpComment(commentId, authorId);
expect(response.status).toBe(200);
});
});
diff --git a/src/api/helpPost.js b/src/api/helpPost.js
index 92d6402..68b37d9 100644
--- a/src/api/helpPost.js
+++ b/src/api/helpPost.js
@@ -1,7 +1,7 @@
// src/api/helpPost.js
import { api } from './auth'; // 复用已有的axios实例
-export const createPost = (title, content, authorId, selectedImage) => {
+export const createHelpPost = (title, content, authorId, selectedImage) => {
// 创建 FormData 对象
const formData = new FormData();
formData.append('title', title);
@@ -16,44 +16,36 @@
return api.post('/help/posts', formData);
};
-export const getPosts = (page = 1, size = 5) => {
+export const getHelpPosts = (page = 1, size = 5) => {
return api.get('/help/posts', {
params: { page, size }
});
};
-export const getPostDetail = (postId) => {
+export const getHelpPostDetail = (postId) => {
return api.get(`/help/posts/${postId}`);
};
-export const likePost = (postId, data) => {
+export const likeHelpPost = (postId, data) => {
return api.post(`/help/posts/${postId}/like`, null, {
params: data
});
};
-export const addPostComment = (postId, commentData) => {
- // 创建FormData对象来处理文件上传
- const formData = new FormData();
- formData.append('authorId', commentData.authorId);
- formData.append('content', commentData.content);
+export const addHelpPostComment = (postId, commentData) => {
- // 如果有图片,添加到formData
- if (commentData.commentImage) {
- formData.append('image', commentData.commentImage);
- }
- return api.post(`/help/posts/${postId}/comments`, formData);
+ return api.post(`/help/posts/${postId}/comments`, commentData);
};
-export const deletePost = (postId, authorId) => {
+export const deleteHelpPost = (postId, authorId) => {
return api.delete(`/help/posts/${postId}`, {
params: { authorId }
});
};
-export const searchPosts = (keyword, page = 1, size = 5) => {
+export const searchHelpPosts = (keyword, page = 1, size = 5) => {
return api.get('/help/posts/search', {
params: { keyword, page, size }
});
diff --git a/src/api/helpPost.test.js b/src/api/helpPost.test.js
index b445b4d..53e9e01 100644
--- a/src/api/helpPost.test.js
+++ b/src/api/helpPost.test.js
@@ -1,6 +1,6 @@
import MockAdapter from 'axios-mock-adapter';
import { api } from './auth'; // 添加api导入
-import { createPost, getPosts, getPostDetail, likePost, addPostComment,deletePost } from './helpPost';
+import { createHelpPost, getHelpPosts, getHelpPostDetail, likeHelpPost, addHelpPostComment,deleteHelpPost,searchHelpPosts } from './helpPost';
describe('求助帖API', () => {
let mockAxios;
@@ -13,7 +13,7 @@
mockAxios.restore();
});
- describe('createPost - 创建求助帖', () => {
+ describe('createHelpPost - 创建求助帖', () => {
it('应该正确发送无图片帖子数据', async () => {
const postData = {
title: '测试标题',
@@ -30,7 +30,7 @@
return [201, { code: 201 }];
});
- const response = await createPost(postData.title, postData.content, postData.authorId);
+ const response = await createHelpPost(postData.title, postData.content, postData.authorId);
expect(response.status).toBe(201);
});
});
@@ -48,7 +48,7 @@
return [201, { code: 201 }];
});
- const response = await createPost(
+ const response = await createHelpPost(
postData.title,
postData.content,
postData.authorId,
@@ -58,7 +58,7 @@
});
- describe('getPosts - 获取求助帖列表', () => {
+ describe('getHelpPosts - 获取求助帖列表', () => {
it('应该支持分页参数', async () => {
const page = 2, size = 10;
mockAxios.onGet('/help/posts', { params: { page, size } }).reply(200, {
@@ -66,57 +66,21 @@
data: []
});
- const response = await getPosts(page, size);
+ const response = await getHelpPosts(page, size);
expect(response.status).toBe(200);
});
});
- describe('likePost - 点赞求助帖', () => {
+ describe('likeHelpPost - 点赞求助帖', () => {
it('应该正确发送点赞请求', async () => {
mockAxios.onPost('/help/posts/post123/like').reply(200, { code: 200 });
- const response = await likePost('post123');
+ const response = await likeHelpPost('post123');
expect(response.status).toBe(200);
});
});
- describe('addPostComment - 添加帖子评论', () => {
- it('应该正确发送评论数据(无图片)', async () => {
- const postId = 'post456';
- const commentData = {
- authorId: 'user1',
- content: '测试评论'
- };
-
- mockAxios.onPost(`/help/posts/${postId}/comments`).reply(config => {
- const data = config.data;
- expect(data.get('authorId')).toBe(commentData.authorId);
- expect(data.get('content')).toBe(commentData.content);
- expect(data.has('image')).toBe(false);
- return [200, { code: 200 }];
- });
-
- const response = await addPostComment('post456', commentData);
- expect(response.status).toBe(200);
- });
- it('应该正确处理带图片的评论', async () => {
- const postId = 'post456';
- const commentData = {
- authorId: 'user1',
- content: '测试评论',
- commentImage: new File(['content'], 'comment.jpg')
- };
-
- mockAxios.onPost(`/help/posts/${postId}/comments`).reply(config => {
- const data = config.data;
- expect(data.get('image')).toBeInstanceOf(File);
- return [200, { code: 200 }];
- });
-
- const response = await addPostComment(postId, commentData);
- expect(response.status).toBe(200);
- });
- });
- describe('deletePost - 删除帖子', () => {
+
+ describe('deleteHelpPost - 删除帖子', () => {
it('应该正确发送删除请求', async () => {
const postId = 'post789';
const authorId = 'user1';
@@ -126,8 +90,28 @@
return [200, { code: 200 }];
});
- const response = await deletePost(postId, authorId);
+ const response = await deleteHelpPost(postId, authorId);
expect(response.status).toBe(200);
});
});
+ describe('searchHelpPosts - 搜索求助帖', () => {
+ it('应该正确发送搜索请求', async () => {
+ const keyword = '测试';
+ const page = 1, size = 5;
+
+ mockAxios.onGet('/help/posts/search', {
+ params: { keyword, page, size }
+ }).reply(200, {
+ code: 200,
+ data: {
+ records: [],
+ total: 0
+ }
+ });
+
+ const response = await searchHelpPosts(keyword, page, size);
+ expect(response.status).toBe(200);
+ expect(response.data.data).toBeDefined();
+ });
+ });
});
\ No newline at end of file
diff --git a/src/api/personal.test.js b/src/api/personal.test.js
new file mode 100644
index 0000000..3a929e8
--- /dev/null
+++ b/src/api/personal.test.js
@@ -0,0 +1,354 @@
+// src/api/__tests__/personal.test.js
+import {
+ getUserInfo,
+ formatFileSize,
+ getDownloadQuota,
+ getDownloadProgress,
+ getUserTorrents,
+ deleteTorrent,
+ generateInviteCode,
+ getUserInviteCodes,
+ exchangeUpload,
+ updatePassword
+} from './personal';
+import { api } from './auth';
+
+// 模拟整个 auth 模块
+jest.mock('./auth');
+
+describe('Personal API', () => {
+ beforeEach(() => {
+ // 在每个测试前重置模拟
+ jest.clearAllMocks();
+ });
+
+ describe('getUserInfo', () => {
+ it('should return formatted user info when API call succeeds', async () => {
+ const mockUserData = {
+ username: 'testuser',
+ level: 3,
+ registTime: '2023-01-01T00:00:00Z',
+ magicPoints: 100,
+ upload: 1024,
+ download: 512,
+ shareRate: 2.0
+ };
+
+ api.get.mockResolvedValue({
+ data: {
+ code: 200,
+ data: mockUserData,
+ message: 'success'
+ }
+ });
+
+ const result = await getUserInfo();
+
+ expect(api.get).toHaveBeenCalledWith('/user/userInfo');
+ expect(result).toEqual({
+ username: 'testuser',
+ level: 3,
+ registTime: '2023-01-01',
+ magicPoints: 100,
+ upload: 1024,
+ download: 512,
+ shareRate: '2.00'
+ });
+ });
+
+ it('should throw error when API call fails', async () => {
+ api.get.mockResolvedValue({
+ data: {
+ code: 500,
+ message: 'Internal server error'
+ }
+ });
+
+ await expect(getUserInfo()).rejects.toThrow('Internal server error');
+ });
+ });
+
+ describe('formatFileSize', () => {
+ it('should format bytes correctly', () => {
+ expect(formatFileSize(500)).toBe('500 B');
+ expect(formatFileSize(1024)).toBe('1.00 KB');
+ expect(formatFileSize(2048)).toBe('2.00 KB');
+ expect(formatFileSize(1024 * 1024)).toBe('1.00 MB');
+ expect(formatFileSize(1024 * 1024 * 2.5)).toBe('2.50 MB');
+ expect(formatFileSize(1024 * 1024 * 1024)).toBe('1.00 GB');
+ expect(formatFileSize(1024 * 1024 * 1024 * 3.7)).toBe('3.70 GB');
+ });
+ });
+
+ describe('getDownloadQuota', () => {
+ it('should return download quota data when API call succeeds', async () => {
+ const mockData = {
+ total: 1073741824, // 1GB in bytes
+ used: 536870912, // 512MB
+ remaining: 536870912
+ };
+
+ api.get.mockResolvedValue({
+ data: {
+ code: 200,
+ data: mockData,
+ message: 'success'
+ }
+ });
+
+ const result = await getDownloadQuota();
+
+ expect(api.get).toHaveBeenCalledWith('/user/allowDownload');
+ expect(result).toEqual(mockData);
+ });
+
+ it('should throw error when API call fails', async () => {
+ api.get.mockResolvedValue({
+ data: {
+ code: 403,
+ message: 'Forbidden'
+ }
+ });
+
+ await expect(getDownloadQuota()).rejects.toThrow('Forbidden');
+ });
+ });
+
+ describe('getDownloadProgress', () => {
+ it('should return download progress when API call succeeds', async () => {
+ const mockProgresses = [
+ { id: 1, name: 'file1', progress: 50 },
+ { id: 2, name: 'file2', progress: 75 }
+ ];
+
+ api.get.mockResolvedValue({
+ data: {
+ code: 200,
+ data: { progresses: mockProgresses },
+ message: 'success'
+ }
+ });
+
+ const result = await getDownloadProgress();
+
+ expect(api.get).toHaveBeenCalledWith('/torrent/getProgress');
+ expect(result).toEqual(mockProgresses);
+ });
+
+ it('should throw error when API call fails', async () => {
+ api.get.mockResolvedValue({
+ data: {
+ code: 404,
+ message: 'Not found'
+ }
+ });
+
+ await expect(getDownloadProgress()).rejects.toThrow('Not found');
+ });
+ });
+
+ describe('getUserTorrents', () => {
+ it('should return user torrents with pagination when API call succeeds', async () => {
+ const mockResponse = {
+ records: [
+ {
+ torrent: { id: 1, name: 'torrent1', size: 1024 },
+ downloadCount: 10,
+ formattedSize: '1 KB'
+ }
+ ],
+ total: 1
+ };
+
+ api.get.mockResolvedValue({
+ data: {
+ code: 200,
+ data: mockResponse,
+ message: 'success'
+ }
+ });
+
+ const result = await getUserTorrents(1, 5);
+
+ expect(api.get).toHaveBeenCalledWith('/torrent/get/torrentMyself', {
+ params: { page: 1, size: 5 }
+ });
+ expect(result).toEqual({
+ records: [
+ {
+ id: 1,
+ name: 'torrent1',
+ size: 1024,
+ downloadCount: 10,
+ formattedSize: '1 KB'
+ }
+ ],
+ total: 1
+ });
+ });
+
+ it('should throw error when API call fails', async () => {
+ api.get.mockResolvedValue({
+ data: {
+ code: 500,
+ message: 'Server error'
+ }
+ });
+
+ await expect(getUserTorrents()).rejects.toThrow('Server error');
+ });
+ });
+
+ describe('deleteTorrent', () => {
+ it('should successfully delete torrent when API call succeeds', async () => {
+ const mockResponse = {
+ code: 200,
+ message: 'Deleted successfully'
+ };
+
+ api.delete.mockResolvedValue({
+ data: mockResponse
+ });
+
+ const result = await deleteTorrent(123);
+
+ expect(api.delete).toHaveBeenCalledWith('/torrent/deleteTorrent/123');
+ expect(result).toEqual(mockResponse);
+ });
+
+ it('should throw error when API call fails', async () => {
+ api.delete.mockResolvedValue({
+ data: {
+ code: 404,
+ message: 'Torrent not found'
+ }
+ });
+
+ await expect(deleteTorrent(123)).rejects.toThrow('Torrent not found');
+ });
+ });
+
+ describe('generateInviteCode', () => {
+ it('should return generated invite code when API call succeeds', async () => {
+ const mockCode = 'ABCD-EFGH-IJKL';
+
+ api.post.mockResolvedValue({
+ data: {
+ code: 200,
+ data: { inviteCode: mockCode },
+ message: 'success'
+ }
+ });
+
+ const result = await generateInviteCode();
+
+ expect(api.post).toHaveBeenCalledWith('/invitecode/generate');
+ expect(result).toBe(mockCode);
+ });
+
+ it('should throw error when API call fails', async () => {
+ api.post.mockResolvedValue({
+ data: {
+ code: 403,
+ message: 'Permission denied'
+ }
+ });
+
+ await expect(generateInviteCode()).rejects.toThrow('Permission denied');
+ });
+ });
+
+ describe('getUserInviteCodes', () => {
+ it('should return user invite codes when API call succeeds', async () => {
+ const mockCodes = ['CODE1', 'CODE2'];
+
+ api.get.mockResolvedValue({
+ data: {
+ code: 200,
+ data: { inviteCode: mockCodes },
+ message: 'success'
+ }
+ });
+
+ const result = await getUserInviteCodes();
+
+ expect(api.get).toHaveBeenCalledWith('/invitecode/userInviteCode');
+ expect(result).toEqual(mockCodes);
+ });
+
+ it('should throw error when API call fails', async () => {
+ api.get.mockResolvedValue({
+ data: {
+ code: 500,
+ message: 'Server error'
+ }
+ });
+
+ await expect(getUserInviteCodes()).rejects.toThrow('Server error');
+ });
+ });
+
+ describe('exchangeUpload', () => {
+ it('should successfully exchange magic points for upload when API call succeeds', async () => {
+ const mockResponse = {
+ code: 200,
+ message: 'Exchange successful'
+ };
+
+ api.post.mockResolvedValue({
+ data: mockResponse
+ });
+
+ const result = await exchangeUpload(100);
+
+ expect(api.post).toHaveBeenCalledWith('/user/exchangeUpload', {
+ magicPoint: 100
+ });
+ expect(result).toEqual(mockResponse);
+ });
+
+ it('should throw error when API call fails', async () => {
+ api.post.mockResolvedValue({
+ data: {
+ code: 400,
+ message: 'Not enough points'
+ }
+ });
+
+ await expect(exchangeUpload(100)).rejects.toThrow('Not enough points');
+ });
+ });
+
+ describe('updatePassword', () => {
+ it('should successfully update password when API call succeeds', async () => {
+ const mockResponse = {
+ code: 200,
+ message: 'Password updated'
+ };
+
+ api.put.mockResolvedValue({
+ data: mockResponse
+ });
+
+ const result = await updatePassword('oldPass', 'newPass');
+
+ expect(api.put).toHaveBeenCalledWith('/user/password', {
+ oldPassword: 'oldPass',
+ newPassword: 'newPass'
+ });
+ expect(result).toEqual(mockResponse);
+ });
+
+ it('should throw error when API call fails', async () => {
+ api.put.mockResolvedValue({
+ data: {
+ code: 401,
+ message: 'Invalid old password'
+ }
+ });
+
+ await expect(updatePassword('wrongPass', 'newPass'))
+ .rejects.toThrow('Invalid old password');
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/api/requestComment.js b/src/api/requestComment.js
new file mode 100644
index 0000000..f7e9b43
--- /dev/null
+++ b/src/api/requestComment.js
@@ -0,0 +1,28 @@
+import { api } from './auth';
+
+export const likeRequestPostComment = (commentId) => {
+ return api.post(`/request/comments/${commentId}/like`);
+};
+
+export const getRequestCommentReplies = (commentId) => {
+ return api.get(`/request/comments/${commentId}/replies`);
+};
+
+export const addRequestCommentReply = (commentId, replyData) => {
+ const formData = new FormData();
+ formData.append('authorId', replyData.authorId);
+ formData.append('content', replyData.content);
+
+ // 如果有图片,添加到formData
+ if (replyData.image) {
+ formData.append('image', replyData.image);
+ }
+
+ return api.post(`/request/comments/${commentId}/replies`, formData);
+};
+
+export const deleteRequestComment = (commentId, authorId) => {
+ return api.delete(`/request/comments/${commentId}`, {
+ params: { authorId }
+ });
+};
\ No newline at end of file
diff --git a/src/api/requestComment.test.js b/src/api/requestComment.test.js
new file mode 100644
index 0000000..2f50a3c
--- /dev/null
+++ b/src/api/requestComment.test.js
@@ -0,0 +1,97 @@
+import MockAdapter from 'axios-mock-adapter';
+import { api } from './auth';
+import {
+ likeRequestPostComment,
+ getRequestCommentReplies,
+ addRequestCommentReply,
+ deleteRequestComment
+} from './requestComment';
+
+describe('求种帖评论API', () => {
+ let mockAxios;
+
+ beforeEach(() => {
+ mockAxios = new MockAdapter(api);
+ });
+
+ afterEach(() => {
+ mockAxios.restore();
+ });
+
+ describe('likeRequestPostComment - 点赞求种帖评论', () => {
+ it('应该成功发送点赞请求', async () => {
+ const commentId = 'reqc123';
+ const mockResponse = { code: 200, message: '点赞成功' };
+ mockAxios.onPost(`/request/comments/${commentId}/like`).reply(200, mockResponse);
+
+ const response = await likeRequestPostComment(commentId);
+ expect(response.data).toEqual(mockResponse);
+ });
+ });
+
+ describe('getRequestCommentReplies - 获取评论回复', () => {
+ it('应该返回正确的回复数据结构', async () => {
+ const commentId = 'reqc456';
+ const mockData = [{ id: '1', content: '求种回复内容' }];
+ mockAxios.onGet(`/request/comments/${commentId}/replies`)
+ .reply(200, { code: 200, data: mockData });
+
+ const response = await getRequestCommentReplies(commentId);
+ expect(response.data.data).toEqual(mockData);
+ });
+ });
+
+ describe('addRequestCommentReply - 添加评论回复', () => {
+ it('应该正确发送回复内容(无图片)', async () => {
+ const commentId = 'reqc789';
+ const replyData = {
+ authorId: 'user1',
+ content: '测试求种回复'
+ };
+
+ mockAxios.onPost(`/request/comments/${commentId}/replies`).reply(config => {
+ const data = config.data;
+ expect(data.get('authorId')).toBe(replyData.authorId);
+ expect(data.get('content')).toBe(replyData.content);
+ expect(data.has('image')).toBe(false);
+ return [200, { code: 200 }];
+ });
+
+ const response = await addRequestCommentReply(commentId, replyData);
+ expect(response.status).toBe(200);
+ });
+
+ it('应该正确处理带图片的回复', async () => {
+ const commentId = 'reqc789';
+ const replyData = {
+ authorId: 'user1',
+ content: '测试求种回复',
+ image: new File(['content'], 'reply.jpg')
+ };
+
+ mockAxios.onPost(`/request/comments/${commentId}/replies`).reply(config => {
+ const data = config.data;
+ expect(data.get('image')).toBeInstanceOf(File);
+ return [200, { code: 200 }];
+ });
+
+ const response = await addRequestCommentReply(commentId, replyData);
+ expect(response.status).toBe(200);
+ });
+ });
+
+ describe('deleteRequestComment - 删除评论', () => {
+ it('应该正确发送删除请求', async () => {
+ const commentId = 'reqc101';
+ const authorId = 'user1';
+
+ mockAxios.onDelete(`/request/comments/${commentId}`).reply(config => {
+ expect(config.params).toEqual({ authorId });
+ return [200, { code: 200 }];
+ });
+
+ const response = await deleteRequestComment(commentId, authorId);
+ expect(response.status).toBe(200);
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/api/requestPost.js b/src/api/requestPost.js
new file mode 100644
index 0000000..224e3e9
--- /dev/null
+++ b/src/api/requestPost.js
@@ -0,0 +1,52 @@
+// src/api/requestPost.js
+import { api } from './auth'; // 复用已有的axios实例
+
+export const createRequestPost = (title, content, authorId, selectedImage) => {
+ // 创建 FormData 对象
+ const formData = new FormData();
+ formData.append('title', title);
+ formData.append('content', content);
+ formData.append('authorId', authorId);
+
+ // 如果有图片,添加到 FormData
+ if (selectedImage) {
+ formData.append('image', selectedImage);
+ }
+
+ return api.post('/request/posts', formData);
+};
+
+export const getRequestPosts = (page = 1, size = 5) => {
+ return api.get('/request/posts', {
+ params: { page, size }
+ });
+};
+
+export const getRequestPostDetail = (postId) => {
+ return api.get(`/request/posts/${postId}`);
+};
+
+export const likeRequestPost = (postId, data) => {
+ return api.post(`/request/posts/${postId}/like`, null, {
+ params: data
+ });
+};
+
+export const addRequestPostComment = (postId, commentData) => {
+
+
+ return api.post(`/request/posts/${postId}/comments`, commentData);
+};
+
+export const deleteRequestPost = (postId, authorId) => {
+ return api.delete(`/request/posts/${postId}`, {
+ params: { authorId }
+ });
+};
+
+
+export const searchRequestPosts = (keyword, page = 1, size = 5) => {
+ return api.get('/request/posts/search', {
+ params: { keyword, page, size }
+ });
+};
\ No newline at end of file
diff --git a/src/api/requestPost.test.js b/src/api/requestPost.test.js
new file mode 100644
index 0000000..a3bccc6
--- /dev/null
+++ b/src/api/requestPost.test.js
@@ -0,0 +1,147 @@
+import MockAdapter from 'axios-mock-adapter';
+import { api } from './auth';
+import {
+ createRequestPost,
+ getRequestPosts,
+ getRequestPostDetail,
+ likeRequestPost,
+ addRequestPostComment,
+ deleteRequestPost,
+ searchRequestPosts
+} from './requestPost';
+
+describe('求种帖API', () => {
+ let mockAxios;
+
+ beforeEach(() => {
+ mockAxios = new MockAdapter(api);
+ });
+
+ afterEach(() => {
+ mockAxios.restore();
+ });
+
+ describe('createRequestPost - 创建求种帖', () => {
+ it('应该正确发送无图片帖子数据', async () => {
+ const postData = {
+ title: '求种测试标题',
+ content: '求种测试内容',
+ authorId: 'user123'
+ };
+
+ mockAxios.onPost('/request/posts').reply(config => {
+ const data = config.data;
+ expect(data.get('title')).toBe(postData.title);
+ expect(data.get('content')).toBe(postData.content);
+ expect(data.get('authorId')).toBe(postData.authorId);
+ expect(data.has('image')).toBe(false);
+ return [201, { code: 201 }];
+ });
+
+ const response = await createRequestPost(
+ postData.title,
+ postData.content,
+ postData.authorId
+ );
+ expect(response.status).toBe(201);
+ });
+
+ it('应该正确处理带图片的帖子', async () => {
+ const postData = {
+ title: '求种测试标题',
+ content: '求种测试内容',
+ authorId: 'user123',
+ selectedImage: new File(['content'], 'request.jpg')
+ };
+
+ mockAxios.onPost('/request/posts').reply(config => {
+ const data = config.data;
+ expect(data.get('image')).toBeInstanceOf(File);
+ return [201, { code: 201 }];
+ });
+
+ const response = await createRequestPost(
+ postData.title,
+ postData.content,
+ postData.authorId,
+ postData.selectedImage
+ );
+ expect(response.status).toBe(201);
+ });
+ });
+
+ describe('getRequestPosts - 获取求种帖列表', () => {
+ it('应该支持分页参数', async () => {
+ const page = 2, size = 10;
+ mockAxios.onGet('/request/posts', { params: { page, size } })
+ .reply(200, {
+ code: 200,
+ data: {
+ records: [],
+ total: 0
+ }
+ });
+
+ const response = await getRequestPosts(page, size);
+ expect(response.status).toBe(200);
+ expect(response.data.data).toBeDefined();
+ });
+ });
+
+ describe('getRequestPostDetail - 获取求种帖详情', () => {
+ it('应该正确获取帖子详情', async () => {
+ const postId = 'req123';
+ mockAxios.onGet(`/request/posts/${postId}`)
+ .reply(200, {
+ code: 200,
+ data: {
+ post: {
+ id: postId,
+ title: '测试求种帖'
+ }
+ }
+ });
+
+ const response = await getRequestPostDetail(postId);
+ expect(response.status).toBe(200);
+ expect(response.data.data.post.id).toBe(postId);
+ });
+ });
+
+
+
+ describe('deleteRequestPost - 删除求种帖', () => {
+ it('应该正确发送删除请求', async () => {
+ const postId = 'req101';
+ const authorId = 'user1';
+
+ mockAxios.onDelete(`/request/posts/${postId}`, {
+ params: { authorId }
+ }).reply(200, { code: 200 });
+
+ const response = await deleteRequestPost(postId, authorId);
+ expect(response.status).toBe(200);
+ });
+ });
+
+ describe('searchRequestPosts - 搜索求种帖', () => {
+ it('应该正确发送搜索请求', async () => {
+ const keyword = '测试';
+ const page = 1, size = 5;
+
+ mockAxios.onGet('/request/posts/search', {
+ params: { keyword, page, size }
+ }).reply(200, {
+ code: 200,
+ data: {
+ records: [],
+ total: 0
+ }
+ });
+
+ const response = await searchRequestPosts(keyword, page, size);
+ expect(response.status).toBe(200);
+ expect(response.data.data).toBeDefined();
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/api/torrent.js b/src/api/torrent.js
index 8a00e54..4a623d7 100644
--- a/src/api/torrent.js
+++ b/src/api/torrent.js
@@ -1,26 +1,74 @@
// src/api/torrent.js
import { api } from './auth'; // 复用已有的axios实例
-export const createTorrent = (torrentData, file) => {
+/**
+ * 创建并上传一个种子
+ * @param {File} file 种子文件 (.torrent)
+ * @param {Object} bodyObj 包含种子信息的对象,格式如下:
+ * {
+ * torrentName: "测试下载",
+ * description: "A high-quality 1080p version of Example Movie.",
+ * category: "电影",
+ * region: "USA",
+ * resolution: "1080p",
+ * subtitle: "English",
+ * filePath: "D:/大学/大三_下/torrentFrom/[电影天堂www.dytt89.com]两杆大烟枪BD中英双字.mp4.torrent"
+ * }
+ */
+export async function createTorrent(file, bodyObj) {
const formData = new FormData();
- const token = localStorage.getItem('token');
+ formData.append('file', file);
- // 添加文件
- if (file) {
- formData.append('file', file);
+ // 关键修改:将JSON字符串转换为Blob对象,并设置正确的Content-Type
+ formData.append('body', new Blob([JSON.stringify(bodyObj)], {
+ type: 'application/json'
+ }));
+
+ try {
+ const response = await api.post('/torrent', formData, {
+ headers: {
+ }
+ });
+ return response.data;
+ } catch (error) {
+ console.error('上传种子失败:', error);
+ throw error;
}
- // 通过这个方式就可以指定 ContentType 了
- formData.append('body', JSON.stringify(torrentData))
-
- console.log(formData);
- return api.post('/torrent', formData, {
- headers: {
- 'Content-Type': 'multipart/form-data',
- 'Authorization': `${token}`
- },
- });
};
+/**
+ * 下载种子文件
+ * @param {number} torrentId 种子ID
+ * @param {string} downloadPath 下载路径
+ */
+export async function downloadTorrent(torrentId, downloadPath) {
+ try {
+ const response = await api.get(`/torrent/downloadTorrent`, {
+ params: {
+ id: torrentId,
+ downloadPath: downloadPath
+ }
+ });
+ return response.data;
+ } catch (error) {
+ console.error('下载种子失败:', error);
+ throw error;
+ }
+}
+
+/**
+ * 获取下载进度
+ */
+export async function getDownloadProgress() {
+ try {
+ const response = await api.get('/torrent/getProgress');
+ return response.data;
+ } catch (error) {
+ console.error('获取下载进度失败:', error);
+ throw error;
+ }
+}
+
export const getTorrents = (page = 1, size = 5) => {
return api.get('/torrent', {
params: { page, size }
@@ -54,6 +102,61 @@
return api.post(`/torrent/${torrentId}/comments`, commentData);
};
+export const deleteTorrent = (torrentId) => {
+ return api.delete(`/torrent/deleteTorrent/${torrentId}`);
+};
+
+// // 上传种子接口
+// export const uploadTorrent = async (torrentFile, torrentData, onProgress) => {
+// // 1. 基础验证
+// if (!torrentFile || !torrentFile.name.endsWith('.torrent')) {
+// throw new Error('请选择有效的.torrent文件');
+// }
+
+// // 2. 构建FormData
+// const formData = new FormData();
+// formData.append('file', torrentFile);
+
+// // 确保JSON内容使用正确的Content-Type
+// const metadataBlob = new Blob(
+// [JSON.stringify(torrentData)],
+// { type: 'application/json' }
+// );
+// formData.append('body', metadataBlob);
+
+// // 3. 获取认证token(根据你的实际存储方式调整)
+// const token = localStorage.getItem('token') || '';
+
+// try {
+// const response = await api.post('/api/torrents', formData, {
+// headers: {
+// 'Authorization': `Bearer ${token}`,
+// },
+// onUploadProgress: (progressEvent) => {
+// if (onProgress && progressEvent.total) {
+// const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
+// onProgress(percent);
+// }
+// }
+// });
+
+// return response.data;
+// } catch (error) {
+// console.error('上传失败:', error);
+
+// // 增强错误处理
+// let errorMessage = '上传失败';
+// if (error.response) {
+// errorMessage = error.response.data?.message ||
+// `服务器错误: ${error.response.status}`;
+// } else if (error.request) {
+// errorMessage = '网络错误,请检查连接';
+// }
+
+// throw new Error(errorMessage);
+// }
+// };
+
export const searchTorrents = (keyword, page = 1, size = 5) => {
return api.get('/torrent/search', {
params: { keyword, page, size }
diff --git a/src/api/torrent.test.js b/src/api/torrent.test.js
index 6515bdc..6cd481e 100644
--- a/src/api/torrent.test.js
+++ b/src/api/torrent.test.js
@@ -22,22 +22,6 @@
localStorage.clear();
});
- describe('createTorrent - 创建种子', () => {
- it('应该正确发送包含文件和数据的表单请求', async () => {
- const mockFile = new File(['test'], 'test.torrent');
- const torrentData = { name: '测试种子', description: '测试描述' };
- const mockResponse = { code: 200, message: '创建成功' };
-
- mockAxios.onPost('/torrent').reply((config) => {
- expect(config.headers['Content-Type']).toBe('multipart/form-data');
- expect(config.headers['Authorization']).toBe('Bearer test-token'); // 修改为包含Bearer
- return [200, mockResponse];
- });
-
- const response = await createTorrent(torrentData, mockFile);
- expect(response.data).toEqual(mockResponse);
- });
- });
describe('getTorrents - 获取种子列表', () => {
it('应该发送带分页参数的请求', async () => {