冷门种子推荐,冷门种子下载不计入下载量,冷门种子自动化判断
Change-Id: Iba4d232d8408195c5af01d2f1686d171e3d5eeac
diff --git a/src/main/java/com/example/g8backend/service/ITorrentRecommendationService.java b/src/main/java/com/example/g8backend/service/ITorrentRecommendationService.java
new file mode 100644
index 0000000..0f0c69b
--- /dev/null
+++ b/src/main/java/com/example/g8backend/service/ITorrentRecommendationService.java
@@ -0,0 +1,9 @@
+package com.example.g8backend.service;
+import com.example.g8backend.dto.TorrentRecommendationDTO;
+import java.util.List;
+
+public interface ITorrentRecommendationService {
+
+ List<TorrentRecommendationDTO> getColdTorrentRecommendations(int limit);
+ void refreshIsRareField(int limit);
+}
diff --git a/src/main/java/com/example/g8backend/service/IUserStatsService.java b/src/main/java/com/example/g8backend/service/IUserStatsService.java
index 4771829..bc329b9 100644
--- a/src/main/java/com/example/g8backend/service/IUserStatsService.java
+++ b/src/main/java/com/example/g8backend/service/IUserStatsService.java
@@ -8,5 +8,5 @@
* 后面弄作弊检测的时候再补充相关的方法。
*/
public interface IUserStatsService extends IService<UserStats> {
-
+ void increaseDownloadedBytes(Long userId, double downloadedBytes);
}
diff --git a/src/main/java/com/example/g8backend/service/PeerService.java b/src/main/java/com/example/g8backend/service/PeerService.java
new file mode 100644
index 0000000..5ae8651
--- /dev/null
+++ b/src/main/java/com/example/g8backend/service/PeerService.java
@@ -0,0 +1,13 @@
+package com.example.g8backend.service;
+
+import com.example.g8backend.entity.Peer;
+
+import java.util.List;
+
+public interface PeerService {
+ Peer getPeerByPK(String peerId, String infoHash, String passkey);
+
+ List<Peer> getPeerByInfoHashAndPeerId(String infoHash, String peerId);
+
+ void updatePeer(String passkey, String peerId, String infoHash, double uploaded, double downloaded);
+}
diff --git a/src/main/java/com/example/g8backend/service/impl/PeerServiceImpl.java b/src/main/java/com/example/g8backend/service/impl/PeerServiceImpl.java
new file mode 100644
index 0000000..ab24566
--- /dev/null
+++ b/src/main/java/com/example/g8backend/service/impl/PeerServiceImpl.java
@@ -0,0 +1,35 @@
+package com.example.g8backend.service.impl;
+
+import com.example.g8backend.entity.Peer;
+import com.example.g8backend.mapper.PeerMapper;
+import com.example.g8backend.service.PeerService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class PeerServiceImpl implements PeerService {
+
+ private final PeerMapper peerMapper;
+
+ @Autowired
+ public PeerServiceImpl(PeerMapper peerMapper) {
+ this.peerMapper = peerMapper;
+ }
+
+ @Override
+ public Peer getPeerByPK(String peerId, String infoHash, String passkey) {
+ return peerMapper.getPeerByPK(peerId, infoHash, passkey);
+ }
+
+ @Override
+ public List<Peer> getPeerByInfoHashAndPeerId(String infoHash, String peerId) {
+ return peerMapper.getPeerByInfoHashAndPeerId(infoHash, peerId);
+ }
+
+ @Override
+ public void updatePeer(String passkey, String peerId, String infoHash, double uploaded, double downloaded) {
+ peerMapper.updatePeer(passkey, peerId, infoHash, uploaded, downloaded);
+ }
+}
diff --git a/src/main/java/com/example/g8backend/service/impl/TorrentRecommendationServiceImpl.java b/src/main/java/com/example/g8backend/service/impl/TorrentRecommendationServiceImpl.java
new file mode 100644
index 0000000..f93c2e4
--- /dev/null
+++ b/src/main/java/com/example/g8backend/service/impl/TorrentRecommendationServiceImpl.java
@@ -0,0 +1,108 @@
+package com.example.g8backend.service.impl;
+
+import com.example.g8backend.dto.TorrentRecommendationDTO;
+import com.example.g8backend.entity.Torrent;
+import com.example.g8backend.mapper.PeerMapper;
+import com.example.g8backend.mapper.TorrentMapper;
+import com.example.g8backend.service.ITorrentRecommendationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+@Service
+public class TorrentRecommendationServiceImpl implements ITorrentRecommendationService {
+
+ private final PeerMapper peerMapper;
+ private final TorrentMapper torrentMapper;
+
+ @Autowired
+ public TorrentRecommendationServiceImpl(PeerMapper peerMapper, TorrentMapper torrentMapper) {
+ this.peerMapper = peerMapper;
+ this.torrentMapper = torrentMapper;
+ }
+
+ /**
+ * 获取冷门种子推荐列表,冷门定义为“种子存在时间越长,最近7天内peer活跃数越少”
+ *
+ * @param limit 返回结果条数限制
+ * @return 冷门种子推荐DTO列表
+ */
+ @Override
+ @Transactional(readOnly = true)
+ public List<TorrentRecommendationDTO> getColdTorrentRecommendations(int limit) {
+ // 1. 查询所有有peer活动的种子info_hash列表
+ List<String> infoHashes = peerMapper.selectAllInfoHashesWithPeers();
+ if (infoHashes == null || infoHashes.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ // 2. 根据info_hash列表查询对应的种子详细信息
+ List<Torrent> allTorrents = torrentMapper.selectByInfoHashList(infoHashes);
+
+ LocalDateTime now = LocalDateTime.now();
+ List<TorrentRecommendationDTO> dtos = new ArrayList<>();
+
+ // 3. 计算每个种子的冷度指标
+ for (Torrent torrent : allTorrents) {
+ LocalDateTime uploadTime = torrent.getUploadTime();
+ if (uploadTime == null) {
+ // 如果没有上传时间,跳过
+ continue;
+ }
+
+ // 计算种子发布距今的小时数
+ long ageHours = Duration.between(uploadTime, now).toHours();
+
+ // 查询最近7天内该infoHash的peer活跃数
+ Long recentActivityCount = peerMapper.countRecentActivity(torrent.getInfoHash());
+ if (recentActivityCount == null) {
+ recentActivityCount = 0L;
+ }
+
+ // 计算冷度分,防止除0,加1
+ double coldnessScore = (double) ageHours / (recentActivityCount + 1);
+
+ // 组装DTO
+ TorrentRecommendationDTO dto = new TorrentRecommendationDTO();
+ dto.setTorrentId(torrent.getTorrentId());
+ dto.setTorrentName(torrent.getTorrentName());
+ dto.setInfoHash(torrent.getInfoHash());
+ dto.setFileSize(torrent.getFileSize());
+ dto.setUploadTime(torrent.getUploadTime());
+ dto.setAgeHours(ageHours);
+ dto.setRecentActivityCount(recentActivityCount);
+ dto.setColdnessScore(coldnessScore);
+
+ dtos.add(dto);
+ }
+
+ // 4. 按冷度分倒序排序,取前limit条
+ dtos.sort(Comparator.comparingDouble(TorrentRecommendationDTO::getColdnessScore).reversed());
+ if (dtos.size() > limit) {
+ return dtos.subList(0, limit);
+ }
+ return dtos;
+ }
+
+ @Override
+ @Transactional
+ public void refreshIsRareField(int limit) {
+ // 1. 获取所有种子的冷门推荐(冷门度排序)
+ List<TorrentRecommendationDTO> coldTorrents = getColdTorrentRecommendations(Integer.MAX_VALUE);
+
+ // 2. 前limit条设为 israre = 1,其他设为0
+ for (int i = 0; i < coldTorrents.size(); i++) {
+ TorrentRecommendationDTO dto = coldTorrents.get(i);
+ int israre = (i < limit) ? 1 : 0;
+
+ // 直接调用mapper更新对应torrent的israre字段
+ torrentMapper.updateIsRareByInfoHash(dto.getInfoHash(), israre);
+ }
+ }
+}
diff --git a/src/main/java/com/example/g8backend/service/impl/TorrentServiceImpl.java b/src/main/java/com/example/g8backend/service/impl/TorrentServiceImpl.java
index 43855a4..d540528 100644
--- a/src/main/java/com/example/g8backend/service/impl/TorrentServiceImpl.java
+++ b/src/main/java/com/example/g8backend/service/impl/TorrentServiceImpl.java
@@ -4,8 +4,11 @@
import com.example.g8backend.entity.Torrent;
import com.example.g8backend.mapper.TorrentMapper;
import com.example.g8backend.service.ITorrentService;
+import com.example.g8backend.service.IUserStatsService;
import com.example.g8backend.util.TorrentUtil;
import jakarta.annotation.Resource;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import java.io.File;
@@ -13,29 +16,31 @@
import java.io.IOException;
@Service
-public class TorrentServiceImpl extends ServiceImpl<TorrentMapper, Torrent> implements ITorrentService {
+public class TorrentServiceImpl extends ServiceImpl<TorrentMapper, Torrent> implements ITorrentService {
+
@Resource
private TorrentMapper torrentMapper;
- String tracker = "http://127.0.0.1:8080/tracker/announce/";
+ @Resource
+ private IUserStatsService userStatsService;
+
+ private final String tracker = "http://127.0.0.1:8080/tracker/announce/";
@Override
- public Torrent handleTorrentUpload(File file, String fileName, Long userId, String passkey) throws IOException, IllegalArgumentException {
- // 修改 announce 字段
+ public Torrent handleTorrentUpload(File file, String fileName, Long userId, String passkey) throws IOException {
+ // 注入 tracker
byte[] modifiedBytes = TorrentUtil.injectTracker(file, tracker + passkey);
- // 计算 info_hash
+ // 获取 infoHash
String infoHash = TorrentUtil.getInfoHash(file);
- // 文件大小(以MB为单位)
+ // 计算大小(MB)
double fileSize = file.length() / 1024.0 / 1024.0;
- // 保存新的种子文件(可选)
+ // 保存修改后的文件
File outputDir = new File("uploaded-torrents");
- if (!outputDir.exists()) {
- if (!outputDir.mkdirs()){
- throw new IOException("Failed to create directory: " + outputDir.getAbsolutePath());
- }
+ if (!outputDir.exists() && !outputDir.mkdirs()) {
+ throw new IOException("Failed to create directory: " + outputDir.getAbsolutePath());
}
File savedFile = new File(outputDir, file.getName());
@@ -44,36 +49,48 @@
}
// 插入数据库
- torrentMapper.insertTorrent(userId, fileName, file.getName(), infoHash, fileSize);
+ Torrent newTorrent = new Torrent();
+ newTorrent.setUserId(userId);
+ newTorrent.setTorrentName(fileName);
+ newTorrent.setFilePath(file.getName());
+ newTorrent.setInfoHash(infoHash);
+ newTorrent.setFileSize(fileSize);
+ newTorrent.setIsRare(false); // 默认不是冷门
+ torrentMapper.insert(newTorrent);
- // 构建返回实体
- Torrent torrent = new Torrent();
- torrent.setUserId(userId);
- torrent.setTorrentName(file.getName());
- torrent.setInfoHash(infoHash);
- torrent.setFileSize(fileSize);
- return torrent;
+ return newTorrent;
}
@Override
public File handleTorrentDownload(Torrent torrent, String passkey) throws IOException {
+ // 原始种子文件读取
File torrentFile = new File("uploaded-torrents/" + torrent.getFilePath());
byte[] modifiedBytes = TorrentUtil.injectTracker(torrentFile, tracker + passkey);
+ // 写入到临时文件
File tempFile = File.createTempFile("user_torrent_", ".torrent");
try (FileOutputStream fos = new FileOutputStream(tempFile)) {
fos.write(modifiedBytes);
}
+
+ // 下载统计判断
+ if (Boolean.FALSE.equals(torrent.getIsRare())) {
+ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+ if (auth != null && auth.getPrincipal() instanceof Long currentUserId) {
+ userStatsService.increaseDownloadedBytes(currentUserId, torrent.getFileSize());
+ }
+ }
+
return tempFile;
}
@Override
- public Torrent findByInfoHash(String infoHash){
+ public Torrent findByInfoHash(String infoHash) {
return torrentMapper.getTorrentByInfoHash(infoHash);
}
@Override
- public Torrent findByTorrentId(Long torrentId){
+ public Torrent findByTorrentId(Long torrentId) {
return torrentMapper.getTorrentByTorrentId(torrentId);
}
}
diff --git a/src/main/java/com/example/g8backend/service/impl/UserStatsServiceImpl.java b/src/main/java/com/example/g8backend/service/impl/UserStatsServiceImpl.java
index 9110d16..5854a6c 100644
--- a/src/main/java/com/example/g8backend/service/impl/UserStatsServiceImpl.java
+++ b/src/main/java/com/example/g8backend/service/impl/UserStatsServiceImpl.java
@@ -6,6 +6,27 @@
import com.example.g8backend.service.IUserStatsService;
import org.springframework.stereotype.Service;
+import java.time.LocalDateTime;
+
@Service
public class UserStatsServiceImpl extends ServiceImpl<UserStatsMapper, UserStats> implements IUserStatsService {
+ @Override
+ public void increaseDownloadedBytes(Long userId, double downloadedBytes) {
+ UserStats userStats = this.getById(userId);
+ if (userStats != null) {
+ userStats.setTotal_download(userStats.getTotal_download() + downloadedBytes);
+ userStats.setLast_update_time(LocalDateTime.now());
+ this.updateById(userStats);
+ } else {
+ // 如果记录不存在,可选:创建新记录(视项目逻辑而定)
+ UserStats newStats = new UserStats();
+ newStats.setUserId(userId);
+ newStats.setTotal_download(downloadedBytes);
+ newStats.setTotal_upload(0.0);
+ newStats.setLast_update_time(LocalDateTime.now());
+ this.save(newStats);
+ }
+ }
+
+
}