用户资料接口添加
Change-Id: Id55e7fef307ec1663bb7a05cfbf7f81e097ac767
diff --git a/front/src/AnimePage.js b/front/src/AnimePage.js
index 97bf453..d17f3cc 100644
--- a/front/src/AnimePage.js
+++ b/front/src/AnimePage.js
@@ -30,16 +30,17 @@
];
const areaTabs = [
- { label: "大陆", icon: <EmojiPeopleIcon fontSize="small" /> },
- { label: "港台", icon: <EmailIcon fontSize="small" /> },
- { label: "欧美", icon: <PersonIcon fontSize="small" /> },
- { label: "日韩", icon: <EmojiPeopleIcon fontSize="small" /> },
- { label: "其他", icon: <PersonIcon fontSize="small" /> },
+ { label: "国创", icon: <EmojiPeopleIcon fontSize="small" /> },
+ { label: "日漫", icon: <EmailIcon fontSize="small" /> },
+ { label: "欧美动漫", icon: <PersonIcon fontSize="small" /> },
+ { label: "韩漫", icon: <EmojiPeopleIcon fontSize="small" /> },
+ // { label: "其他", icon: <PersonIcon fontSize="small" /> },
];
export default function AnimePage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = React.useState(0);
+ const [animeList, setAnimeList] = React.useState([]);
// 每个tab对应的动漫类型
const animeTypesList = [
@@ -51,6 +52,15 @@
];
const animeTypes = animeTypesList[activeTab] || [];
+ React.useEffect(() => {
+ // 假设后端接口为 /api/animes?area=大陆
+ const area = areaTabs[activeTab].label;
+ fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
+ .then(res => res.json())
+ .then(data => setAnimeList(data))
+ .catch(() => setAnimeList([]));
+ }, [activeTab]);
+
return (
<div className="container">
{/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}
@@ -108,21 +118,39 @@
</tr>
</thead>
<tbody>
- {animeTypes.map((type, index) => (
- <tr key={type}>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {type}
- </a>
- </td>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- 种子{index + 1}
- </a>
- </td>
- <td>发布者{index + 1}</td>
- </tr>
- ))}
+ {animeList.length > 0 ? (
+ animeList.map((item, index) => (
+ <tr key={item.id || index}>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {item.seedtag}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {item.title}
+ </a>
+ </td>
+ <td>{item.user.username}</td>
+ </tr>
+ ))
+ ) : (
+ animeTypes.map((type, index) => (
+ <tr key={type}>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {type}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ 种子{index + 1}
+ </a>
+ </td>
+ <td>发布者{index + 1}</td>
+ </tr>
+ ))
+ )}
</tbody>
</table>
</div>
diff --git a/front/src/GamePage.js b/front/src/GamePage.js
index 23383b6..2528acd 100644
--- a/front/src/GamePage.js
+++ b/front/src/GamePage.js
@@ -21,7 +21,13 @@
{ label: "发布", icon: <AccountCircleIcon />, path: "/publish" }, // Added Publish option
];
-const gameTypes = ["PC", "主机", "移动", "掌机", "视频"];
+const gameTypesList = [
+ ["PC"],
+ ["主机"],
+ ["移动"],
+ ["掌机"],
+ ["视频"]
+];
const areaTabs = [
{ label: "PC", icon: <MovieIcon fontSize="small" /> },
@@ -31,15 +37,20 @@
{ label: "视频", icon: <PersonIcon fontSize="small" /> },
];
-const exampleTorrents = [
- { type: "RPG", title: "实例1", id: 1 },
- { type: "Shooter", title: "实例2", id: 2 },
- { type: "Adventure", title: "实例3", id: 3 },
-];
-
export default function GamePage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState(0);
+ const [gameList, setGameList] = useState([]);
+
+ const gameTypes = gameTypesList[activeTab] || [];
+
+ React.useEffect(() => {
+ const area = areaTabs[activeTab].label;
+ fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
+ .then(res => res.json())
+ .then(data => setGameList(data))
+ .catch(() => setGameList([]));
+ }, [activeTab]);
return (
<div className="container">
@@ -153,27 +164,39 @@
</tr>
</thead>
<tbody>
- {gameTypes.map((type, idx) => (
- <tr key={type}>
- <td>
- <a
- href={`/torrent/${type}`}
- style={{ color: "#1a237e", textDecoration: "none" }}
- >
- {type}
- </a>
- </td>
- <td>
- <a
- href={`/torrent/${type}`}
- style={{ color: "#1a237e", textDecoration: "none" }}
- >
- 种子{idx + 1}
- </a>
- </td>
- <td></td>
- </tr>
- ))}
+ {gameList.length > 0 ? (
+ gameList.map((item, idx) => (
+ <tr key={item.id || idx}>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: "#1a237e", textDecoration: "none" }}>
+ {item.seedtag}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: "#1a237e", textDecoration: "none" }}>
+ {item.title}
+ </a>
+ </td>
+ <td>{item.user.username}</td>
+ </tr>
+ ))
+ ) : (
+ gameTypes.map((type, idx) => (
+ <tr key={type}>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: "#1a237e", textDecoration: "none" }}>
+ {type}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: "#1a237e", textDecoration: "none" }}>
+ 种子{idx + 1}
+ </a>
+ </td>
+ <td></td>
+ </tr>
+ ))
+ )}
</tbody>
</table>
</div>
diff --git a/front/src/InfoPage.js b/front/src/InfoPage.js
index 1e00527..755d922 100644
--- a/front/src/InfoPage.js
+++ b/front/src/InfoPage.js
@@ -40,6 +40,7 @@
export default function InfoPage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = React.useState(0);
+ const [infoList, setInfoList] = React.useState(0);
// 每个tab对应的资料类型
const infoTypesList = [
@@ -51,6 +52,18 @@
];
const infoTypes = infoTypesList[activeTab] || [];
+ React.useEffect(() => {
+ // 这里假设后端接口为 /api/get-seed-list-by-tag?tag=大陆
+ const area = areaTabs[activeTab].label;
+ fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
+ .then(res => res.json())
+ .then(data => {
+ console.log('资料区返回数据:', data);
+ setInfoList(data);
+ })
+ .catch(() => setInfoList([]));
+ }, [activeTab]);
+
return (
<div className="container">
{/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}
@@ -163,27 +176,39 @@
</tr>
</thead>
<tbody>
- {infoTypes.map((type, index) => (
- <tr key={type}>
- <td>
- <a
- href={`/torrent/${type}`}
- style={{ color: "#1a237e", textDecoration: "none" }}
- >
- {type}
- </a>
- </td>
- <td>
- <a
- href={`/torrent/${type}`}
- style={{ color: "#1a237e", textDecoration: "none" }}
- >
- 种子{index + 1}
- </a>
- </td>
- <td>发布者{index + 1}</td>
- </tr>
- ))}
+ {infoList.length > 0 ? (
+ infoList.map((item, index) => (
+ <tr key={item.id || index}>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {item.seedtag}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {item.title}
+ </a>
+ </td>
+ <td>{item.user.username}</td>
+ </tr>
+ ))
+ ) : (
+ infoTypesList.map((type, index) => (
+ <tr key={type}>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {type}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ 种子{index + 1}
+ </a>
+ </td>
+ <td>发布者{index + 1}</td>
+ </tr>
+ ))
+ )}
</tbody>
</table>
</div>
diff --git a/front/src/MoviePage.js b/front/src/MoviePage.js
index 20e4a4f..c84797b 100644
--- a/front/src/MoviePage.js
+++ b/front/src/MoviePage.js
@@ -26,7 +26,7 @@
{ label: "港台", icon: <EmailIcon fontSize="small" /> },
{ label: "欧美", icon: <PersonIcon fontSize="small" /> },
{ label: "日韩", icon: <EmojiPeopleIcon fontSize="small" /> },
- { label: "其他", icon: <PersonIcon fontSize="small" /> },
+ // { label: "其他", icon: <PersonIcon fontSize="small" /> },
];
const exampleTorrents = [
@@ -38,6 +38,7 @@
export default function MoviePage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = React.useState(0);
+ const [movieList, setMovieList] = React.useState([]);
// 每个tab对应的电影类型
const movieTypesList = [
@@ -49,6 +50,18 @@
];
const movieTypes = movieTypesList[activeTab] || [];
+ React.useEffect(() => {
+ // 这里假设后端接口为 /api/get-seed-list-by-tag?tag=大陆
+ const area = areaTabs[activeTab].label;
+ fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
+ .then(res => res.json())
+ .then(data => {
+ console.log('电影区返回数据:', data);
+ setMovieList(data);
+ })
+ .catch(() => setMovieList([]));
+ }, [activeTab]);
+
return (
<div className="container">
{/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}
@@ -106,21 +119,39 @@
</tr>
</thead>
<tbody>
- {movieTypes.map((type, index) => (
- <tr key={type}>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {type}
- </a>
- </td>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- 种子{index + 1}
- </a>
- </td>
- <td>发布者{index + 1}</td>
- </tr>
- ))}
+ {movieList.length > 0 ? (
+ movieList.map((item, index) => (
+ <tr key={item.id || index}>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {item.seedtag}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {item.title}
+ </a>
+ </td>
+ <td>{item.user.username}</td>
+ </tr>
+ ))
+ ) : (
+ movieTypes.map((type, index) => (
+ <tr key={type}>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {type}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ 种子{index + 1}
+ </a>
+ </td>
+ <td>发布者{index + 1}</td>
+ </tr>
+ ))
+ )}
</tbody>
</table>
</div>
diff --git a/front/src/MusicPage.js b/front/src/MusicPage.js
index 4a9c465..b39b0ee 100644
--- a/front/src/MusicPage.js
+++ b/front/src/MusicPage.js
@@ -22,11 +22,11 @@
];
const areaTabs = [
- { label: "大陆", icon: <MovieIcon fontSize="small" /> },
- { label: "港台", icon: <EmailIcon fontSize="small" /> },
- { label: "欧美", icon: <PersonIcon fontSize="small" /> },
- { label: "日韩", icon: <EmojiPeopleIcon fontSize="small" /> },
- { label: "其他", icon: <PersonIcon fontSize="small" /> },
+ { label: "古典音乐", icon: <MovieIcon fontSize="small" /> },
+ { label: "流行音乐", icon: <EmailIcon fontSize="small" /> },
+ { label: "摇滚", icon: <PersonIcon fontSize="small" /> },
+ { label: "电子音乐", icon: <EmojiPeopleIcon fontSize="small" /> },
+ { label: "说唱", icon: <PersonIcon fontSize="small" /> },
];
const exampleTorrents = [
@@ -38,6 +38,7 @@
export default function MusicPage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = React.useState(0);
+ const [musicList, setMusicList] = React.useState([]);
// 每个tab对应的音乐类型
const musicTypesList = [
@@ -49,6 +50,15 @@
];
const musicTypes = musicTypesList[activeTab] || [];
+ React.useEffect(() => {
+ // 假设后端接口为 /api/musics?area=大陆
+ const area = areaTabs[activeTab].label;
+ fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
+ .then(res => res.json())
+ .then(data => setMusicList(data))
+ .catch(() => setMusicList([]));
+ }, [activeTab]);
+
return (
<div className="container music-bg">
{/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}
@@ -106,21 +116,39 @@
</tr>
</thead>
<tbody>
- {musicTypes.map((type, index) => (
- <tr key={type}>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {type}
- </a>
- </td>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- 种子{index + 1}
- </a>
- </td>
- <td>发布者{index + 1}</td>
- </tr>
- ))}
+ {musicList.length > 0 ? (
+ musicList.map((item, index) => (
+ <tr key={item.id || index}>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {item.seedtag}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {item.title}
+ </a>
+ </td>
+ <td>{item.user.username}</td>
+ </tr>
+ ))
+ ) : (
+ musicTypes.map((type, index) => (
+ <tr key={type}>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {type}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ 种子{index + 1}
+ </a>
+ </td>
+ <td>发布者{index + 1}</td>
+ </tr>
+ ))
+ )}
</tbody>
</table>
</div>
diff --git a/front/src/PublishPage.js b/front/src/PublishPage.js
index 81ac790..abd4ee2 100644
--- a/front/src/PublishPage.js
+++ b/front/src/PublishPage.js
@@ -9,6 +9,17 @@
title: '',
subtitle: ''
});
+ const [subType, setSubType] = useState('');
+
+ const typeOptions = {
+ '电影': ['大陆', '港台', '欧美', '日韩'],
+ '剧集': ['国产电视剧', '港剧', '欧美剧', '日韩剧'],
+ '音乐': ['古典音乐', '流行音乐', '摇滚', '电子音乐', '说唱'],
+ '动漫': ['国创', '日漫', '欧美动漫', '韩漫'],
+ '游戏': ['PC', '主机', '移动', '掌机', '视频'],
+ '体育': ['篮球', '足球', '羽毛球', '排球', '电竞'],
+ '资料': ['出版物', '学习教程', '素材模板', '演讲交流', '日常娱乐'],
+ };
const handleChange = (e) => {
const { name, value } = e.target;
@@ -27,7 +38,35 @@
const handleSubmit = (e) => {
e.preventDefault();
- console.log('Form Data Submitted:', formData);
+ // 假设userid和tag可以从表单或用户信息中获取,这里用示例数据
+ const userid = '550e8400-e29b-41d4-a716-446655440000';
+ const tag = formData.type ? formData.type : '高清';
+ if (!formData.torrentFile) {
+ alert('请上传.torrent文件');
+ return;
+ }
+ const data = new FormData();
+ data.append('userid', userid);
+ data.append('title', formData.title);
+ data.append('tag', subType);
+ data.append('file', formData.torrentFile);
+ data.append('subtitle', formData.subtitle);
+ // data.append('subtype', subType);
+ // console.log(data.get('tag'));
+
+ try {
+ const response = await fetch('http://192.168.5.9:8080/api/save-torrent', {
+ method: 'POST',
+ body: data,
+ });
+ if (response.ok) {
+ alert('上传成功!');
+ } else {
+ alert('上传失败');
+ }
+ } catch (err) {
+ alert('网络错误');
+ }
};
return (
@@ -36,7 +75,7 @@
<form onSubmit={handleSubmit} className="publish-form">
<div className="form-row">
<label htmlFor="type">类型</label>
- <select name="type" id="type" value={formData.type} onChange={handleChange} required>
+ <select name="type" id="type" value={formData.type} onChange={e => { handleChange(e); setSubType(''); }} required>
<option value="">请选择类型</option>
<option value="电影">电影</option>
<option value="剧集">剧集</option>
@@ -47,6 +86,17 @@
<option value="资料">资料</option>
</select>
</div>
+ {formData.type && typeOptions[formData.type] && (
+ <div className="form-row">
+ <label htmlFor="subtype">具体类型</label>
+ <select name="subtype" id="subtype" value={subType} onChange={e => setSubType(e.target.value)} required>
+ <option value="">请选择具体类型</option>
+ {typeOptions[formData.type].map(opt => (
+ <option key={opt} value={opt}>{opt}</option>
+ ))}
+ </select>
+ </div>
+ )}
<div className="form-row">
<label htmlFor="torrentFile">种子文件</label>
diff --git a/front/src/SportPage.js b/front/src/SportPage.js
index 569f022..3299a93 100644
--- a/front/src/SportPage.js
+++ b/front/src/SportPage.js
@@ -40,6 +40,7 @@
export default function SportPage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = React.useState(0);
+ const [sportList, setSportList] = React.useState([]);
// 每个tab对应的运动类型
const sportTypesList = [
@@ -51,6 +52,15 @@
];
const sportTypes = sportTypesList[activeTab] || [];
+ React.useEffect(() => {
+ // 假设后端接口为 /api/sports?area=篮球
+ const area = areaTabs[activeTab].label;
+ fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
+ .then(res => res.json())
+ .then(data => setSportList(data))
+ .catch(() => setSportList([]));
+ }, [activeTab]);
+
return (
<div className="container">
{/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}
@@ -163,27 +173,39 @@
</tr>
</thead>
<tbody>
- {sportTypes.map((type, index) => (
- <tr key={type}>
- <td>
- <a
- href={`/torrent/${type}`}
- style={{ color: "#1a237e", textDecoration: "none" }}
- >
- {type}
- </a>
- </td>
- <td>
- <a
- href={`/torrent/${type}`}
- style={{ color: "#1a237e", textDecoration: "none" }}
- >
- 种子{index + 1}
- </a>
- </td>
- <td>发布者{index + 1}</td>
- </tr>
- ))}
+ {sportList.length > 0 ? (
+ sportList.map((item, index) => (
+ <tr key={item.id || index}>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: "#1a237e", textDecoration: "none" }}>
+ {item.seedtag}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: "#1a237e", textDecoration: "none" }}>
+ {item.title}
+ </a>
+ </td>
+ <td>{item.user.username}</td>
+ </tr>
+ ))
+ ) : (
+ sportTypes.map((type, index) => (
+ <tr key={type}>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: "#1a237e", textDecoration: "none" }}>
+ {type}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: "#1a237e", textDecoration: "none" }}>
+ 种子{index + 1}
+ </a>
+ </td>
+ <td>发布者{index + 1}</td>
+ </tr>
+ ))
+ )}
</tbody>
</table>
</div>
diff --git a/front/src/TVPage.js b/front/src/TVPage.js
index 13b7391..bf1db6c 100644
--- a/front/src/TVPage.js
+++ b/front/src/TVPage.js
@@ -21,40 +21,37 @@
{ label: "发布", icon: <AccountCircleIcon />, path: "/publish" }, // Added Publish option
];
-const tvTypes = [
- "华语剧集(大陆)",
- "欧美剧集",
- "日韩剧集",
- "港台剧集",
- "其他"
+const tvTypesList = [
+ ["华语剧集(大陆)", "欧美剧集", "日韩剧集", "港台剧集", "其他"], // 大陆
+ ["港台都市", "港台爱情", "港台悬疑", "港台其他"], // 港台
+ ["欧美悬疑", "欧美历史", "欧美其他"], // 欧美
+ ["日韩青春", "日韩家庭", "日韩其他"], // 日韩
+ ["其他类型1", "其他类型2"] // 其他
];
const areaTabs = [
- { label: "大陆", icon: <MovieIcon fontSize="small" /> },
- { label: "港台", icon: <EmailIcon fontSize="small" /> },
- { label: "欧美", icon: <PersonIcon fontSize="small" /> },
- { label: "日韩", icon: <EmojiPeopleIcon fontSize="small" /> },
- { label: "其他", icon: <PersonIcon fontSize="small" /> },
-];
-
-const exampleTorrents = [
- { type: "Drama", title: "实例1", id: 1 },
- { type: "Comedy", title: "实例2", id: 2 },
- { type: "Sci-Fi", title: "实例3", id: 3 },
+ { label: "国产电视剧", icon: <MovieIcon fontSize="small" /> },
+ { label: "港剧", icon: <EmailIcon fontSize="small" /> },
+ { label: "欧美剧", icon: <PersonIcon fontSize="small" /> },
+ { label: "日韩剧", icon: <EmojiPeopleIcon fontSize="small" /> },
+ // { label: "其他", icon: <PersonIcon fontSize="small" /> },
];
export default function TVPage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = React.useState(0);
+ const [tvList, setTvList] = React.useState([]);
+
+ React.useEffect(() => {
+ // 假设后端接口为 /api/tvs?area=大陆
+ const area = areaTabs[activeTab].label;
+ fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
+ .then(res => res.json())
+ .then(data => setTvList(data))
+ .catch(() => setTvList([]));
+ }, [activeTab]);
// 每个tab对应的剧集类型
- const tvTypesList = [
- ["华语剧集(大陆)", "欧美剧集", "日韩剧集", "港台剧集", "其他"], // 大陆
- ["港台都市", "港台爱情", "港台悬疑", "港台其他"], // 港台
- ["欧美悬疑", "欧美历史", "欧美其他"], // 欧美
- ["日韩青春", "日韩家庭", "日韩其他"], // 日韩
- ["其他类型1", "其他类型2"] // 其他
- ];
const tvTypes = tvTypesList[activeTab] || [];
return (
@@ -114,21 +111,39 @@
</tr>
</thead>
<tbody>
- {tvTypes.map((type, index) => (
- <tr key={type}>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {type}
- </a>
- </td>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- 种子{index + 1}
- </a>
- </td>
- <td></td>
- </tr>
- ))}
+ {tvList.length > 0 ? (
+ tvList.map((item, index) => (
+ <tr key={item.id || index}>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {item.seedtag}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {item.title}
+ </a>
+ </td>
+ <td>{item.user.username}</td>
+ </tr>
+ ))
+ ) : (
+ tvTypes.map((type, index) => (
+ <tr key={type}>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {type}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ 种子{index + 1}
+ </a>
+ </td>
+ <td>发布者{index + 1}</td>
+ </tr>
+ ))
+ )}
</tbody>
</table>
</div>
diff --git a/front/src/TorrentDetailPage.js b/front/src/TorrentDetailPage.js
index a23dce7..8d3ee43 100644
--- a/front/src/TorrentDetailPage.js
+++ b/front/src/TorrentDetailPage.js
@@ -4,14 +4,81 @@
export default function TorrentDetailPage() {
const { torrentId } = useParams();
+ const [detail, setDetail] = React.useState(null);
+ const [loading, setLoading] = React.useState(true);
+ const [error, setError] = React.useState(null);
+ // 假设你从某个地方获取了 userId(例如登录状态、localStorage 等)
+ const [userId] = React.useState('user1550e8400-e29b-41d4-a716-44665544000023'); // 替换为实际的用户 ID
+
+ const handleClick = () => {
+ // 构造下载 URL,包含 userId 和 torrentId 参数
+ console.log(torrentId)
+ const downloadUrl = `http://192.168.5.9:8080/api/get-torrent?userId=${encodeURIComponent(userId)}&torrentId=${encodeURIComponent(torrentId)}`;
+
+ // 发起 GET 请求下载文件
+ fetch(downloadUrl)
+ .then(response => {
+ if (!response.ok) {
+ throw new Error('下载失败');
+ }
+ return response.blob();
+ })
+ .then(blob => {
+ // 创建下载链接并触发下载
+ const url = window.URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `torrent-${torrentId}.torrent`;
+ document.body.appendChild(a);
+ a.click();
+ window.URL.revokeObjectURL(url);
+ document.body.removeChild(a);
+ })
+ .catch(error => {
+ console.error('下载错误:', error);
+ alert('下载失败: ' + error.message);
+ });
+ };
+
+ React.useEffect(() => {
+ setLoading(true);
+ setError(null);
+ fetch(`http://192.168.5.9:8080/api/torrent-detail?id=${encodeURIComponent(torrentId)}`)
+ .then(res => {
+ if (!res.ok) throw new Error('网络错误');
+ return res.json();
+ })
+ .then(data => {
+ setDetail(data);
+ setLoading(false);
+ })
+ .catch(err => {
+ setError(err.message);
+ setLoading(false);
+ });
+ }, [torrentId]);
+
+ if (loading) return <div className="container"><h1>加载中...</h1></div>;
+ if (error) return <div className="container"><h1>加载失败: {error}</h1></div>;
+ if (!detail) return <div className="container"><h1>未找到详情</h1></div>;
return (
<div className="container">
<h1>种子详情页</h1>
- <h2 style={{ fontSize: 'inherit', fontWeight: 'normal', textAlign: 'left' }}>标题: 种子{torrentId}</h2>
- <p style={{ fontSize: 'inherit', textAlign: 'left' }}>简介: 这是种子{torrentId}的详细信息。</p>
+ <h2 style={{ fontSize: 'inherit', fontWeight: 'normal', textAlign: 'left' }}>标题: {detail.title || `种子${torrentId}`}</h2>
+ <p style={{ fontSize: 'inherit', textAlign: 'left' }}>简介: {detail.description || `这是种子${torrentId}的详细信息。`}</p>
<div style={{ textAlign: 'center', marginTop: '20px' }}>
- <button style={{ padding: '10px 20px', fontSize: '16px', cursor: 'pointer', backgroundColor: '#d3f0ff', border: 'none', borderRadius: '4px' }}>
+ <button
+ style={{
+ padding: '10px 20px',
+ fontSize: '16px',
+ cursor: 'pointer',
+ backgroundColor: '#d3f0ff',
+ border: 'none',
+ borderRadius: '4px'
+ }}
+ onClick={handleClick}
+ >
下载
</button>
</div>
diff --git a/front/src/UserProfile.js b/front/src/UserProfile.js
index 0269723..4737033 100644
--- a/front/src/UserProfile.js
+++ b/front/src/UserProfile.js
@@ -1,4 +1,4 @@
-import React, { useState } from "react";
+import React, { useState, useEffect } from "react";
import AccountCircleIcon from "@mui/icons-material/AccountCircle";
import { useNavigate } from "react-router-dom";
import "./App.css";
@@ -6,21 +6,109 @@
export default function UserProfile() {
const navigate = useNavigate();
const [userInfo, setUserInfo] = useState({
+ avatar_url: "",
username: "示例用户",
email: "user@example.com",
- company: "",
+ invite_left: "",
school: "",
- birthday: "",
+ account_status: "",
+ gender: "",
});
const [tempUserInfo, setTempUserInfo] = useState({ ...userInfo });
+ const [userSeeds, setUserSeeds] = useState([]);
+ const [userStats, setUserStats] = useState({
+ magic: 0,
+ upload: 0,
+ download: 0,
+ ratio: 0,
+ });
+
+ // 新增:根据userid从后端获取用户信息
+ useEffect(() => {
+ const fetchUserInfo = async () => {
+ // 假设userid存储在localStorage或其他地方
+ // const userid = localStorage.getItem("userid");
+ const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
+ if (!userid) return;
+ try {
+ const res = await fetch(`/api/user-profile?userid=${userid}`);
+ if (res.ok) {
+ const data = await res.json();
+ setUserInfo(data);
+ setTempUserInfo(data);
+ }
+ } catch (err) {
+ // 可以根据需要处理错误
+ console.error("获取用户信息失败", err);
+ }
+ };
+ fetchUserInfo();
+ }, []);
+
+ // 动态加载用户上传种子列表
+ useEffect(() => {
+ const fetchUserSeeds = async () => {
+ // const userid = localStorage.getItem("userid");
+ const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
+ if (!userid) return;
+ try {
+ const res = await fetch(`/api/user-seeds?userid=${userid}`);
+ if (res.ok) {
+ const data = await res.json();
+ setUserSeeds(data);
+ }
+ } catch (err) {
+ console.error("获取种子列表失败", err);
+ }
+ };
+ fetchUserSeeds();
+ }, []);
+
+ // 动态加载用户活跃度信息
+ useEffect(() => {
+ const fetchUserStats = async () => {
+ // const userid = localStorage.getItem("userid");
+ const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
+ if (!userid) return;
+ try {
+ const res = await fetch(`/api/user-stats?userid=${userid}`);
+ if (res.ok) {
+ const data = await res.json();
+ setUserStats(data);
+ }
+ } catch (err) {
+ console.error("获取活跃度信息失败", err);
+ }
+ };
+ fetchUserStats();
+ }, []);
const handleInputChange = (field, value) => {
setTempUserInfo({ ...tempUserInfo, [field]: value });
};
- const handleSave = () => {
+ const handleSave = async () => {
setUserInfo({ ...tempUserInfo });
- alert("信息已保存!");
+ // 获取userid
+ // const userid = localStorage.getItem("userid");
+ const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
+ try {
+ const res = await fetch('/api/change-profile', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ userid, ...tempUserInfo }),
+ });
+ if (res.ok) {
+ alert("信息已保存!");
+ } else {
+ alert("保存失败,请重试。");
+ }
+ } catch (err) {
+ alert("保存失败,请检查网络连接。");
+ console.error("保存用户信息失败", err);
+ }
};
const handleAvatarClick = () => {
@@ -39,7 +127,7 @@
<AccountCircleIcon style={{ fontSize: 90, color: '#1a237e', marginBottom: 12 }} />
{tempUserInfo.avatar && (
<img
- src={tempUserInfo.avatar}
+ src={tempUserInfo.avatar_url}
alt="用户头像"
style={{
position: 'absolute',
@@ -67,21 +155,19 @@
</div>
<div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
<b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邮箱:</b>
- <input
- type="email"
- value={tempUserInfo.email}
- onChange={(e) => handleInputChange("email", e.target.value)}
- style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}
- />
+ <span
+ style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}
+ >
+ {tempUserInfo.email}
+ </span>
</div>
<div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
- <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>公司:</b>
- <input
- type="text"
- value={tempUserInfo.company}
- onChange={(e) => handleInputChange("company", e.target.value)}
- style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}
- />
+ <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邀请剩余:</b>
+ <span
+ style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}
+ >
+ {tempUserInfo.invite_left}
+ </span>
</div>
<div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
<b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>学校:</b>
@@ -93,22 +179,21 @@
/>
</div>
<div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
- <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>生日:</b>
- <input
- type="date"
- value={tempUserInfo.birthday}
- onChange={(e) => handleInputChange("birthday", e.target.value)}
- style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}
- />
- </div>
- <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
- <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>密码:</b>
- <input
- type="password"
- value={tempUserInfo.password || ""}
- onChange={(e) => handleInputChange("password", e.target.value)}
- style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}
- />
+ <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>账号状态:</b>
+ <span
+ style={{ flex: 1, display: 'flex', alignItems: 'center', padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}
+ >
+ {tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? "封禁" : "正常"}
+ <span style={{
+ display: 'inline-block',
+ width: 12,
+ height: 12,
+ borderRadius: '50%',
+ backgroundColor: tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? '#e53935' : '#43a047',
+ marginLeft: 10,
+ border: '1px solid #b2b2b2',
+ }} />
+ </span>
</div>
<div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
<b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>性别:</b>
@@ -187,15 +272,61 @@
{/* 上传种子列表 */}
<div style={{ gridColumn: '2 / 3', gridRow: '1 / 2', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>
<h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>个人上传种子列表</h3>
- <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b2b2b2', fontSize: 18 }}>
- (此处显示上传种子列表)
+ <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, minHeight: 60, padding: 12 }}>
+ {userSeeds.length === 0 ? (
+ <div style={{ color: '#b2b2b2', fontSize: 18, textAlign: 'center' }}>(暂无上传种子)</div>
+ ) : (
+ <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
+ {userSeeds.map((seed, idx) => (
+ <li
+ key={seed.seed_id || idx}
+ style={{ display: 'flex', alignItems: 'center', padding: '10px 0', borderBottom: '1px solid #e0e7ff', cursor: 'pointer' }}
+ onClick={e => {
+ // 阻止点击删除按钮时跳转
+ if (e.target.classList.contains('delete-btn')) return;
+ navigate(`/torrent/${seed.seed_id}`);
+ }}
+ >
+ <span style={{ flex: 2, fontWeight: 500, color: '#1a237e', textDecoration: 'underline' }}>{seed.title}</span>
+ <span style={{ flex: 1, color: '#5c6bc0' }}>{seed.tags}</span>
+ <span style={{ flex: 1, color: '#ff9800', textAlign: 'right' }}>人气: {seed.popularity}</span>
+ <button
+ className="delete-btn"
+ style={{ marginLeft: 18, background: '#e53935', color: '#fff', border: 'none', borderRadius: 6, padding: '4px 14px', cursor: 'pointer', fontSize: 14 }}
+ onClick={async (e) => {
+ e.stopPropagation();
+ // const userid = localStorage.getItem("userid");
+ const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
+ try {
+ const res = await fetch('/api/delete-seed', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ seed_id: seed.seed_id, userid }),
+ });
+ if (res.ok) {
+ setUserSeeds(userSeeds.filter((s, i) => (s.seed_id || i) !== (seed.seed_id || idx)));
+ } else {
+ alert('删除失败,请重试');
+ }
+ } catch (err) {
+ alert('删除失败,请检查网络');
+ }
+ }}
+ >删除</button>
+ </li>
+ ))}
+ </ul>
+ )}
</div>
</div>
{/* 活跃度模块 */}
<div style={{ gridColumn: '2 / 3', gridRow: '2 / 3', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>
<h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>活跃度</h3>
- <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b2b2b2', fontSize: 18 }}>
- (此处显示活跃度信息)
+ <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, minHeight: 60, padding: 18, display: 'flex', flexDirection: 'column', gap: 12, fontSize: 18 }}>
+ <div>魔力值:<b style={{ color: '#1976d2' }}>{userStats.magic}</b></div>
+ <div>上传量:<b style={{ color: '#43a047' }}>{userStats.upload} GB</b></div>
+ <div>下载量:<b style={{ color: '#e53935' }}>{userStats.download} GB</b></div>
+ <div>上传/下载值:<b style={{ color: '#ff9800' }}>{userStats.ratio}</b></div>
</div>
</div>
</div>
diff --git a/src/main/java/api/ApiController.java b/src/main/java/api/ApiController.java
index d8528db..41d9052 100644
--- a/src/main/java/api/ApiController.java
+++ b/src/main/java/api/ApiController.java
@@ -14,14 +14,19 @@
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.PostMapping;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import database.Database1;
-import entity.Seed;
import tracker.Tracker;
+import entity.Seed;
+import entity.User;
+
+import java.util.UUID;
@RestController
public class ApiController implements ApiInterface {
@@ -29,6 +34,8 @@
private static Database1 db1;
private static Tracker tracker;
private static ObjectMapper mapper;
+ private static HttpHeaders headers;
+ private static HttpHeaders errorHeaders;
@PostConstruct
public void init() {
@@ -36,6 +43,14 @@
tracker = new Tracker();
mapper = new ObjectMapper();
mapper.configure(com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+ headers = new HttpHeaders();
+ headers.add("Access-Control-Allow-Origin", "*");
+ headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
+ headers.add("Access-Control-Allow-Headers", "Content-Type, Authorization");
+ errorHeaders = new HttpHeaders();
+ errorHeaders.add("Access-Control-Allow-Origin", "*");
+ errorHeaders.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
+ errorHeaders.add("Access-Control-Allow-Headers", "Content-Type, Authorization");
}
@Override
@@ -47,42 +62,24 @@
) {
try {
Seed seed = new Seed();
- seed.seedid = "exampleSeedId"; // 示例种子ID
- seed.seeduserid = userid; // 示例用户ID
- seed.title = title; // 示例标题
- seed.seedsize = "1GB"; // 示例种子大小
- seed.seedtag = tag; // 示例标签
+ seed.seedid = UUID.randomUUID().toString(); // 生成唯一的种子ID
+ seed.seeduserid = userid;
+ seed.title = title;
+ seed.seedsize = "1GB";
+ seed.seedtag = tag;
seed.url = "http://example.com/torrent"; // 示例URL
-
+ System.out.println("seed is null? " + (seed == null));
int ret = db1.RegisterSeed(seed);
- // System.out.println("RegisterSeed ret: " + ret);
- // System.out.println(seed.seedid + " " + seed.seeduserid + " " + seed.title + " " + seed.seedsize + " " + seed.seedtag + " " + seed.url);
-
- // Convert MultipartFile to File
+ if (ret != 0) {
+ // 如果注册种子失败,返回错误状态
+ return new ResponseEntity<>(ret, headers, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
File tempFile = File.createTempFile(seed.seedid, file.getOriginalFilename());
file.transferTo(tempFile);
-
tracker.SaveTorrent(seed.seedid, tempFile);
-
- // Optionally, delete the temp file after saving if not needed
- // tempFile.delete();
-
- // 创建带有 CORS 响应头的 ResponseEntity
- HttpHeaders headers = new HttpHeaders();
- headers.add("Access-Control-Allow-Origin", "*"); // 允许所有域名访问,可以根据需要调整为特定域名
- headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); // 允许的 HTTP 方法
- headers.add("Access-Control-Allow-Headers", "Content-Type, Authorization"); // 允许的请求头
-
return new ResponseEntity<>(0, headers, HttpStatus.OK); // 返回 0 表示成功
} catch (Exception e) {
e.printStackTrace();
-
- // 创建带有 CORS 响应头的错误 ResponseEntity
- HttpHeaders errorHeaders = new HttpHeaders();
- errorHeaders.add("Access-Control-Allow-Origin", "*");
- errorHeaders.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
- errorHeaders.add("Access-Control-Allow-Headers", "Content-Type, Authorization");
-
return new ResponseEntity<>(1, errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR); // 返回 1 表示失败
}
}
@@ -90,8 +87,8 @@
@Override
@CrossOrigin(origins = "*", allowedHeaders = "*") // 允许所有来源和头部
public ResponseEntity<Resource> getTorrent(
- @RequestParam("torrentId") String seedid,
- @RequestParam("userId") String userid
+ @RequestParam("torrentId") String seedid,
+ @RequestParam("userId") String userid
) {
File file = tracker.GetTTorent(seedid, userid);
if (file != null) {
@@ -106,31 +103,187 @@
}
}
- //无按标签查询的函数
@Override
public ResponseEntity<String> getSeedListByTag(
@RequestParam("tag") String tag
) {
try {
Seed[] seeds = db1.GetSeedListByTag(tag);
+ if (seeds == null || seeds.length == 0) {
+ return new ResponseEntity<>("[]", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR); // 返回 404 表示未找到种子
+ }
String json = mapper.writeValueAsString(seeds);
- // System.out.println("getSeedListByTag: " + json);
- HttpHeaders headers = new HttpHeaders();
- headers.add("Access-Control-Allow-Origin", "*");
- headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
- headers.add("Access-Control-Allow-Headers", "Content-Type, Authorization");
return new ResponseEntity<>(json, headers, HttpStatus.OK);
} catch (JsonProcessingException e) {
e.printStackTrace();
- HttpHeaders errorHeaders = new HttpHeaders();
- errorHeaders.add("Access-Control-Allow-Origin", "*");
- errorHeaders.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
- errorHeaders.add("Access-Control-Allow-Headers", "Content-Type, Authorization");
return new ResponseEntity<>("[]", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@Override
+ public ResponseEntity<String> getUserProfile(
+ @RequestParam("userid") String userid
+ ) {
+ try {
+ User user = db1.GetInformation(userid);
+ if (user == null) {
+ return new ResponseEntity<>("", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ String json = mapper.writeValueAsString(user);
+ return new ResponseEntity<>(json, headers, HttpStatus.OK);
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ return new ResponseEntity<>("", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
+ @CrossOrigin(origins = "*", allowedHeaders = "*")
+ public ResponseEntity<Integer> changeProfile(
+ @RequestBody String requestBody
+ ) {
+ try {
+ // 解析 JSON 数据
+ com.fasterxml.jackson.databind.JsonNode jsonNode = mapper.readTree(requestBody);
+
+ // 安全地获取 userid 字段
+ com.fasterxml.jackson.databind.JsonNode useridNode = jsonNode.get("userid");
+ if (useridNode == null) {
+ return new ResponseEntity<>(1, HttpStatus.BAD_REQUEST);
+ }
+ String userid = useridNode.asText();
+
+ // 添加参数验证
+ if (userid == null || userid.trim().isEmpty()) {
+ return new ResponseEntity<>(1, HttpStatus.BAD_REQUEST);
+ }
+
+ // 手动映射前端字段到 User 对象,处理类型转换
+ User user = new User();
+ user.userid = userid;
+
+ // 安全地获取其他字段并进行类型转换
+ if (jsonNode.has("username") && !jsonNode.get("username").isNull()) {
+ user.username = jsonNode.get("username").asText();
+ }
+ if (jsonNode.has("school") && !jsonNode.get("school").isNull()) {
+ user.school = jsonNode.get("school").asText();
+ }
+ if (jsonNode.has("gender") && !jsonNode.get("gender").isNull()) {
+ user.sex = jsonNode.get("gender").asText();
+ }
+ if (jsonNode.has("avatar_url") && !jsonNode.get("avatar_url").isNull()) {
+ user.pictureurl = jsonNode.get("avatar_url").asText();
+ }
+
+ // 处理 account_status 的类型转换(字符串/数字 -> 布尔值)
+ if (jsonNode.has("account_status") && !jsonNode.get("account_status").isNull()) {
+ com.fasterxml.jackson.databind.JsonNode statusNode = jsonNode.get("account_status");
+ if (statusNode.isTextual()) {
+ String statusStr = statusNode.asText();
+ user.accountstate = "1".equals(statusStr) || "封禁".equals(statusStr);
+ } else if (statusNode.isNumber()) {
+ user.accountstate = statusNode.asInt() == 1;
+ } else if (statusNode.isBoolean()) {
+ user.accountstate = statusNode.asBoolean();
+ }
+ }
+
+ // 处理 invite_left 的类型转换(字符串 -> 整数)
+ if (jsonNode.has("invite_left") && !jsonNode.get("invite_left").isNull()) {
+ com.fasterxml.jackson.databind.JsonNode inviteNode = jsonNode.get("invite_left");
+ if (inviteNode.isTextual()) {
+ try {
+ user.invitetimes = Integer.parseInt(inviteNode.asText());
+ } catch (NumberFormatException e) {
+ user.invitetimes = 0; // 默认值
+ }
+ } else if (inviteNode.isNumber()) {
+ user.invitetimes = inviteNode.asInt();
+ }
+ }
+
+ int ret = db1.UpdateInformation(user);
+ if (ret == 0) {
+ return new ResponseEntity<>(0, HttpStatus.OK); // 返回 0 表示成功
+ } else {
+ return new ResponseEntity<>(ret, HttpStatus.INTERNAL_SERVER_ERROR); // 返回其他状态表示失败
+ }
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ return new ResponseEntity<>(1, HttpStatus.INTERNAL_SERVER_ERROR); // 返回 1 表示处理失败
+ } catch (Exception e) {
+ e.printStackTrace();
+ return new ResponseEntity<>(1, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
+ public ResponseEntity<String> getUserSeeds(
+ @RequestParam("userid") String userid
+ ) {
+ try {
+ Seed[] seeds = db1.GetSeedListByUser(userid);
+ if (seeds == null || seeds.length == 0) {
+ return new ResponseEntity<>("[]", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR); // 返回 404 表示未找到种子
+ }
+ String json = mapper.writeValueAsString(seeds);
+ return new ResponseEntity<>(json, headers, HttpStatus.OK);
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ return new ResponseEntity<>("[]", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
+ @CrossOrigin(origins = "*", allowedHeaders = "*")
+ public ResponseEntity<Integer> deleteSeed(
+ @RequestBody String requestBody
+ ) {
+ try {
+ // 解析 JSON 数据
+ com.fasterxml.jackson.databind.JsonNode jsonNode = mapper.readTree(requestBody);
+ com.fasterxml.jackson.databind.JsonNode seedidNode = jsonNode.get("seedid");
+ if (seedidNode == null) {
+ return new ResponseEntity<>(1, HttpStatus.BAD_REQUEST);
+ }
+ String seedid = seedidNode.asText();
+
+ // 添加参数验证
+ if (seedid == null || seedid.trim().isEmpty()) {
+ return new ResponseEntity<>(1, HttpStatus.BAD_REQUEST);
+ }
+
+ int ret = db1.DeleteSeed(seedid);
+ if (ret == 0) {
+ return new ResponseEntity<>(0, HttpStatus.OK);
+ } else {
+ return new ResponseEntity<>(ret, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return new ResponseEntity<>(1, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
+ public ResponseEntity<String> getUserStat(
+ @RequestParam("userid") String userid
+ ) {
+ try {
+ User user = db1.GetInformation(userid);
+ if (user == null) {
+ return new ResponseEntity<>("", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ String json = mapper.writeValueAsString(user);
+ return new ResponseEntity<>(json, headers, HttpStatus.OK);
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ return new ResponseEntity<>("", errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
public ResponseEntity<String> getTorrentDetail(
@RequestParam("id") String seedid
) {
diff --git a/src/main/java/api/ApiInterface.java b/src/main/java/api/ApiInterface.java
index 6859aa6..2b4a3a3 100644
--- a/src/main/java/api/ApiInterface.java
+++ b/src/main/java/api/ApiInterface.java
@@ -8,6 +8,7 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.bind.annotation.RequestBody;
@RestController
@RequestMapping("/api")
@@ -36,4 +37,29 @@
ResponseEntity<String> getTorrentDetail(
@RequestParam("id") String seedid
);
+
+ @GetMapping("user-profile")
+ ResponseEntity<String> getUserProfile(
+ @RequestParam("id") String userid
+ );
+
+ @PostMapping("/change-profile")
+ ResponseEntity<Integer> changeProfile(
+ @RequestBody String requestBody
+ );
+
+ @GetMapping("/user-seeds")
+ ResponseEntity<String> getUserSeeds(
+ @RequestParam("userid") String userid
+ );
+
+ @PostMapping("/delete-seed")
+ ResponseEntity<Integer> deleteSeed(
+ @RequestBody String requestBody
+ );
+
+ @GetMapping("/user-stat")
+ ResponseEntity<String> getUserStat(
+ @RequestParam("userid") String userid
+ );
}
\ No newline at end of file
diff --git a/src/main/java/database/DataManagerInterface.java b/src/main/java/database/DataManagerInterface.java
index e2fb1fb..1f4b510 100644
--- a/src/main/java/database/DataManagerInterface.java
+++ b/src/main/java/database/DataManagerInterface.java
@@ -22,9 +22,11 @@
public Seed GetSeedInformation(String seedid);//返回种子的全部信息;
public int RegisterSeed(Seed seedinfo);//添加一个新的种子,0成功,其他失败信息待定;
public int UpdateSeed(Seed seedinfo);//接收新的种子然后更新其全部属性;
+ public int DeleteSeed(String seedid);//删除一个种子,返回状态:0 success,1 不存在,2其他原因
public Seed[] SearchSeed(String userQ);//传入搜索的关键词或句子,返回搜索到的种子信息(按照公共字符数量排序)
public Seed[] GetSeedListByTag(String tag);//获取某个标签下的种子列表
+ public Seed[] GetSeedListByUser(String userid);//获取某个用户的种子列表
public int AddNotice(Notice notice);//返回状态:0 success,1 重复,2其他原因
public boolean UpdateNotice(Notice notice);//返回状态:0 success,1 重复,2其他原因
diff --git a/src/main/java/database/Database1.java b/src/main/java/database/Database1.java
index b89dc18..8c3bbb4 100644
--- a/src/main/java/database/Database1.java
+++ b/src/main/java/database/Database1.java
@@ -130,28 +130,36 @@
if (userinfo.userid == null) {
return 2; // userid为null直接返回错误
}
+
+ entitymanager.getTransaction().begin();
+
JPAQuery<User> query = new JPAQuery<>(entitymanager);
QUser u = QUser.user;
User updateUser = query.select(u).from(u).where(u.userid.eq(userinfo.userid)).fetchOne();
if(updateUser == null){
+ entitymanager.getTransaction().rollback();
return 1;
}
// 只更新需要的字段,避免破坏持久化状态和关联关系
- updateUser.email = userinfo.email;
- updateUser.username = userinfo.username;
- updateUser.password = userinfo.password;
- updateUser.sex = userinfo.sex;
- updateUser.school = userinfo.school;
- updateUser.pictureurl = userinfo.pictureurl;
- updateUser.profile = userinfo.profile;
+ if (userinfo.email != null) updateUser.email = userinfo.email;
+ if (userinfo.username != null) updateUser.username = userinfo.username;
+ if (userinfo.password != null) updateUser.password = userinfo.password;
+ if (userinfo.sex != null) updateUser.sex = userinfo.sex;
+ if (userinfo.school != null) updateUser.school = userinfo.school;
+ if (userinfo.pictureurl != null) updateUser.pictureurl = userinfo.pictureurl;
+ if (userinfo.profile != null) updateUser.profile = userinfo.profile;
updateUser.accountstate = userinfo.accountstate;
updateUser.invitetimes = userinfo.invitetimes;
// 如有其他字段也一并赋值
entitymanager.merge(updateUser);
+ entitymanager.getTransaction().commit();
return 0;
} catch (Exception e) {
e.printStackTrace();
+ if (entitymanager.getTransaction().isActive()) {
+ entitymanager.getTransaction().rollback();
+ }
return 2;
}
@@ -227,6 +235,35 @@
return seeds.toArray(new Seed[0]);
}
+ @Override
+ public Seed[] GetSeedListByUser(String userid){
+ JPAQuery<Seed> query = new JPAQuery<>(entitymanager);
+ QSeed s = QSeed.seed;
+ List<Seed> seeds = query.select(s).from(s).where(s.seeduserid.eq(userid)).fetch();
+ return seeds.toArray(new Seed[0]);
+ }
+
+ @Override
+ public int DeleteSeed(String seedid){
+ try {
+ entitymanager.getTransaction().begin();
+ Seed seed = entitymanager.find(Seed.class, seedid);
+ if (seed == null) {
+ entitymanager.getTransaction().rollback();
+ return 1; // 种子不存在
+ }
+ entitymanager.remove(seed);
+ entitymanager.getTransaction().commit();
+ return 0; // 成功删除
+ } catch (Exception e) {
+ e.printStackTrace();
+ if (entitymanager.getTransaction().isActive()) {
+ entitymanager.getTransaction().rollback();
+ }
+ return 2; // 其他错误
+ }
+ }
+
//添加一个新的种子,0成功,其他失败信息待定;
@Override
public int RegisterSeed(Seed seedinfo){
@@ -240,7 +277,7 @@
entitymanager.getTransaction().rollback();
return 2; // 用户不存在
}
- seed.user = user; // 设置种子的用户关联
+ seedinfo.user = user; // 设置种子的用户关联
if (seed != null) {
entitymanager.getTransaction().rollback();
return 1;
diff --git a/src/main/java/database/Database2.java b/src/main/java/database/Database2.java
index d2d98c7..0e6dadf 100644
--- a/src/main/java/database/Database2.java
+++ b/src/main/java/database/Database2.java
@@ -138,6 +138,16 @@
}
@Override
+ public Seed[] GetSeedListByUser(String userid) {
+ return new Seed[0];
+ }
+
+ @Override
+ public int DeleteSeed(String seedid) {
+ return 0;
+ }
+
+ @Override
public boolean AddCollect(String userid, String postid) {
return false;
}
diff --git a/src/main/java/entity/User.java b/src/main/java/entity/User.java
index f73c868..b53e2e2 100644
--- a/src/main/java/entity/User.java
+++ b/src/main/java/entity/User.java
@@ -13,6 +13,7 @@
import javax.persistence.TemporalType;
import javax.persistence.Transient;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.querydsl.core.annotations.QueryEntity;
@QueryEntity
@@ -29,6 +30,7 @@
@Column(name = "password", length = 255, nullable = false)
public String password;
@Column(name = "gender", nullable = false)
+ @JsonProperty("gender")
public String sex;
@Column(name = "detectedCount", nullable = false)
@@ -50,12 +52,15 @@
@Column(name = "school")
public String school;
@Column(name = "avatar_url")
+ @JsonProperty("avatar_url")
public String pictureurl;
@Column(name = "bio")
public String profile;
@Column(name = "account_status", nullable = false)
+ @JsonProperty("account_status")
public boolean accountstate;
@Column(name = "invite_left", nullable = false)
+ @JsonProperty("invite_left")
public int invitetimes;
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
public UserPT userPT;
diff --git a/src/main/java/entity/config.java b/src/main/java/entity/config.java
index b4b7ea4..7ef6ab7 100644
--- a/src/main/java/entity/config.java
+++ b/src/main/java/entity/config.java
@@ -6,6 +6,7 @@
public static final int BegVote=3;
public static final int CheatTime=5;
// 请根据实际环境修改为可达的地址
+ // public static final String SqlURL = "192.168.5.9:3306";
public static final String SqlURL = "10.126.59.25:3306";
public static final String Database = "pt_database_test";
public static final String TestDatabase = "pt_database_test";
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 0000000..bafddce
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1 @@
+server.port=8081
\ No newline at end of file