feat: 支持 Torrent 解析与数据持久化(v1/v2/Hybrid)
- 实现对 Torrent v1、v2 以及 Hybrid 模式的解析
- 完成 Torrent 元信息的数据库持久化逻辑
- 支持 announce 节点、文件信息的提取与存储
- 构建 BtTorrent 表结构与相关插入逻辑
- 预留 OSS 上传接口,明日补充实际上传实现
Change-Id: I4438da0ab364bc8e0d299e1d57474c190584052a
diff --git a/react-ui/src/pages/Torrent/index.tsx b/react-ui/src/pages/Torrent/index.tsx
index 6990da7..bb058ae 100644
--- a/react-ui/src/pages/Torrent/index.tsx
+++ b/react-ui/src/pages/Torrent/index.tsx
@@ -1,5 +1,5 @@
import React, { useRef, useState } from 'react';
-import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined } from '@ant-design/icons';
+import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined, UploadOutlined } from '@ant-design/icons';
import {
Button,
Modal,
@@ -11,14 +11,10 @@
DatePicker,
Card,
Layout,
+ Upload,
+ UploadProps,
} from 'antd';
-import {
- ProTable,
- ActionType,
- ProColumns,
- ProDescriptions,
- ProDescriptionsItemProps,
-} from '@ant-design/pro-components';
+import { ProTable, ActionType, ProColumns, ProDescriptions, ProDescriptionsItemProps } from '@ant-design/pro-components';
import type { BtTorrent } from './data';
import {
listBtTorrent,
@@ -26,6 +22,7 @@
addBtTorrent,
updateBtTorrent,
removeBtTorrent,
+ uploadTorrent, // Function to handle torrent upload
} from './service';
const { Content } = Layout;
@@ -36,7 +33,10 @@
const [modalVisible, setModalVisible] = useState(false);
const [drawerVisible, setDrawerVisible] = useState(false);
const [current, setCurrent] = useState<Partial<BtTorrent>>({});
+ const [uploadModalVisible, setUploadModalVisible] = useState(false); // State for upload modal
+ const [uploadFile, setUploadFile] = useState<File | null>(null); // State to store selected file
+ // Columns for the ProTable (the table displaying torrents)
const columns: ProColumns<BtTorrent>[] = [
{
title: '种子ID',
@@ -89,18 +89,47 @@
},
];
+ // Handle the submit for adding or updating a torrent
const handleSubmit = async () => {
const values = await form.validateFields();
- if (current?.torrentId) {
- await updateBtTorrent({ ...current, ...values });
- message.success('更新成功');
- } else {
- await addBtTorrent(values as BtTorrent);
- message.success('新增成功');
+ try {
+ if (current?.torrentId) {
+ await updateBtTorrent({ ...current, ...values });
+ message.success('更新成功');
+ } else {
+ await addBtTorrent(values as BtTorrent);
+ message.success('新增成功');
+ }
+ setModalVisible(false);
+ form.resetFields();
+ actionRef.current?.reload();
+ } catch (error) {
+ message.error('操作失败');
}
- setModalVisible(false);
- form.resetFields();
- actionRef.current?.reload();
+ };
+
+ // Handle file upload
+ const handleFileUpload = async (file: File) => {
+ try {
+ if (!file) {
+ throw new Error('请选择一个文件');
+ }
+
+ // Call the uploadTorrent function to upload the file
+ await uploadTorrent(file);
+
+ // Show a success message
+ message.success('文件上传成功');
+
+ // Close the upload modal
+ setUploadModalVisible(false);
+
+ // Optionally reload the table or perform other actions (e.g., refresh list)
+ actionRef.current?.reload();
+
+ } catch (error) {
+ message.error(error.message || '文件上传失败');
+ }
};
return (
@@ -124,6 +153,14 @@
>
新增
</Button>,
+ <Button
+ key="upload"
+ type="primary"
+ icon={<UploadOutlined />}
+ onClick={() => setUploadModalVisible(true)} // Show the upload modal
+ >
+ 上传种子文件
+ </Button>
]}
request={async (params) => {
const res = await listBtTorrent(params);
@@ -162,6 +199,26 @@
</Form>
</Modal>
+ {/* 上传种子文件的Modal */}
+ <Modal
+ title="上传种子文件"
+ visible={uploadModalVisible}
+ onCancel={() => setUploadModalVisible(false)}
+ footer={null}
+ >
+ <Upload
+ customRequest={({ file, onSuccess, onError }) => {
+ setUploadFile(file);
+ handleFileUpload(file);
+ onSuccess?.();
+ }}
+ showUploadList={false}
+ accept=".torrent"
+ >
+ <Button icon={<UploadOutlined />}>点击上传 .torrent 文件</Button>
+ </Upload>
+ </Modal>
+
{/* 详情抽屉 */}
<Drawer
width={500}
diff --git a/react-ui/src/pages/Torrent/service.ts b/react-ui/src/pages/Torrent/service.ts
index c1d8e26..8e15618 100644
--- a/react-ui/src/pages/Torrent/service.ts
+++ b/react-ui/src/pages/Torrent/service.ts
@@ -166,7 +166,23 @@
data,
});
}
+/**
+ * 上传并解析种子文件
+ * @param file The .torrent file to upload
+ * @returns The parsed torrent data or error message
+ */
+export async function uploadTorrent(file: File) {
+ const formData = new FormData();
+ formData.append('file', file);
+ return request('/api/system/torrent/uploadTorrent', {
+ method: 'POST',
+ data: formData,
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ });
+}
/** 删除标签(支持批量) */
export async function removeBtTorrentTag(ids: number[]) {
return request(`/api/system/tags/${ids.join(',')}`, {