Merge "添加更新用户流量的方法,并定时跟新到所有用户"
diff --git a/src/main/java/com/pt/controller/TrackerController.java b/src/main/java/com/pt/controller/TrackerController.java
index edae09f..33d8831 100644
--- a/src/main/java/com/pt/controller/TrackerController.java
+++ b/src/main/java/com/pt/controller/TrackerController.java
@@ -23,6 +23,13 @@
String ip = request.getRemoteAddr();
Map<String, String[]> params = request.getParameterMap();
+ // 验证必要参数
+ if (!params.containsKey("username")) {
+ response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+ response.getWriter().write("Missing required parameter: username");
+ return;
+ }
+
byte[] bencodedResponse = trackerService.handleAnnounce(params, ip);
response.setContentType("application/x-bittorrent");
diff --git a/src/main/java/com/pt/controller/UserController.java b/src/main/java/com/pt/controller/UserController.java
index 3bb05c5..d9f53ab 100644
--- a/src/main/java/com/pt/controller/UserController.java
+++ b/src/main/java/com/pt/controller/UserController.java
@@ -227,4 +227,63 @@
return ResponseEntity.badRequest().body(ans);
}
}
+
+ /**
+ * 获取用户统计信息
+ */
+ @GetMapping("/stats/{username}")
+ public ResponseEntity<?> getUserStats(
+ @RequestHeader("token") String token,
+ @PathVariable String username) {
+
+ Map<String, Object> ans = new HashMap<>();
+
+ if(!JWTUtils.checkToken(token, username, Constants.UserRole.USER)) {
+ ans.put("message", "Invalid token");
+ return ResponseEntity.badRequest().body(ans);
+ }
+
+ User user = userService.findByUsername(username);
+ if (user == null) {
+ ans.put("message", "User not found");
+ return ResponseEntity.badRequest().body(ans);
+ }
+
+ // 计算分享率
+ double ratio = user.getDownloaded() == 0 ?
+ (user.getUploaded() > 0 ? Double.MAX_VALUE : 0) :
+ (double) user.getUploaded() / user.getDownloaded();
+
+ // 格式化分享率为两位小数
+ ratio = Math.round(ratio * 100.0) / 100.0;
+
+ // 构建返回数据
+ Map<String, Object> stats = new HashMap<>();
+ double uploadSize = user.getUploaded() / (1024.0 * 1024.0 * 1024.0);
+ double downloadSize = user.getDownloaded() / (1024.0 * 1024.0 * 1024.0);
+
+ stats.put("uploadSize", uploadSize); // 转换为GB
+ stats.put("downloadSize", downloadSize); // 转换为GB
+ stats.put("ratio", ratio);
+ stats.put("points", user.getPoints());
+ stats.put("userClass", getUserClass(user.getLevel()));
+ stats.put("level", user.getLevel());
+
+ ans.put("message", "User stats retrieved successfully");
+ ans.put("data", stats);
+ return ResponseEntity.ok(ans);
+ }
+
+ /**
+ * 根据用户等级返回对应的用户类别名称
+ */
+ private String getUserClass(int level) {
+ switch (level) {
+ case 5: return "大佬";
+ case 4: return "专家";
+ case 3: return "熟练";
+ case 2: return "入门";
+ default: return "新用户";
+ }
+ }
}
diff --git a/src/main/java/com/pt/entity/PeerInfoEntity.java b/src/main/java/com/pt/entity/PeerInfoEntity.java
index c19a269..d232a73 100644
--- a/src/main/java/com/pt/entity/PeerInfoEntity.java
+++ b/src/main/java/com/pt/entity/PeerInfoEntity.java
@@ -25,6 +25,8 @@
private long downloaded; // 已下载量
private long left; // 剩余下载量
+ private String username; // 添加用户名字段
+
public Long getId() {
return id;
}
@@ -113,4 +115,12 @@
public void setActive(boolean active) {
isActive = active;
}
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
}
diff --git a/src/main/java/com/pt/repository/PeerInfoRepository.java b/src/main/java/com/pt/repository/PeerInfoRepository.java
index b096a35..c76df9f 100644
--- a/src/main/java/com/pt/repository/PeerInfoRepository.java
+++ b/src/main/java/com/pt/repository/PeerInfoRepository.java
@@ -3,6 +3,7 @@
import com.pt.entity.PeerInfoEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
import java.time.LocalDateTime;
import java.util.List;
@@ -21,4 +22,14 @@
// 获取需要清理的非活跃 peer
@Query("SELECT p FROM PeerInfoEntity p WHERE p.lastSeen < :threshold")
List<PeerInfoEntity> findInactivePeers(LocalDateTime threshold);
+
+ // 添加按用户名查询的方法
+ List<PeerInfoEntity> findByUsername(String username);
+
+ // 添加查询用户总流量的方法
+ @Query("SELECT SUM(p.uploaded) FROM PeerInfoEntity p WHERE p.username = :username")
+ Long sumUploadedByUsername(@Param("username") String username);
+
+ @Query("SELECT SUM(p.downloaded) FROM PeerInfoEntity p WHERE p.username = :username")
+ Long sumDownloadedByUsername(@Param("username") String username);
}
diff --git a/src/main/java/com/pt/scheduler/UserTrafficSyncScheduler.java b/src/main/java/com/pt/scheduler/UserTrafficSyncScheduler.java
new file mode 100644
index 0000000..0ec838d
--- /dev/null
+++ b/src/main/java/com/pt/scheduler/UserTrafficSyncScheduler.java
@@ -0,0 +1,65 @@
+package com.pt.scheduler;
+
+import com.pt.entity.PeerInfoEntity;
+import com.pt.entity.User;
+import com.pt.repository.PeerInfoRepository;
+import com.pt.service.UserService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 定时任务,用于同步用户的流量数据
+ */
+@Component
+public class UserTrafficSyncScheduler {
+
+ private static final Logger logger = LoggerFactory.getLogger(UserTrafficSyncScheduler.class);
+
+ @Autowired
+ private UserService userService;
+
+ @Autowired
+ private PeerInfoRepository peerInfoRepository;
+
+ /**
+ * 每小时执行一次流量同步
+ * 确保用户的总流量数据准确
+ */
+ @Scheduled(cron = "0 0 * * * ?")
+ @Transactional
+ public void syncUserTraffic() {
+ logger.info("开始执行定时任务:同步用户流量数据...");
+ try {
+ List<User> users = userService.listAll();
+ for (User user : users) {
+ // 获取用户所有peer的总流量
+ Long totalUploaded = peerInfoRepository.sumUploadedByUsername(user.getUsername());
+ Long totalDownloaded = peerInfoRepository.sumDownloadedByUsername(user.getUsername());
+
+ // 如果查询结果为空,设置为0
+ totalUploaded = totalUploaded != null ? totalUploaded : 0L;
+ totalDownloaded = totalDownloaded != null ? totalDownloaded : 0L;
+
+ // 更新用户流量
+ user.setUploaded(totalUploaded);
+ user.setDownloaded(totalDownloaded);
+ userService.save(user);
+
+ // 更新用户等级
+ userService.updateUserLevel(user.getUid());
+
+ logger.info("用户 {} 流量同步完成: 上传={}, 下载={}",
+ user.getUsername(), totalUploaded, totalDownloaded);
+ }
+ logger.info("定时任务完成:所有用户流量数据已同步");
+ } catch (Exception e) {
+ logger.error("执行用户流量同步定时任务时发生错误", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pt/service/TrackerService.java b/src/main/java/com/pt/service/TrackerService.java
index 191479b..8a654d0 100644
--- a/src/main/java/com/pt/service/TrackerService.java
+++ b/src/main/java/com/pt/service/TrackerService.java
@@ -2,10 +2,13 @@
import com.pt.entity.PeerInfoEntity;
import com.pt.entity.TorrentMeta;
+import com.pt.entity.User;
import com.pt.exception.ResourceNotFoundException;
import com.pt.repository.PeerInfoRepository;
import com.pt.repository.TorrentMetaRepository;
import com.pt.utils.BencodeCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -18,6 +21,8 @@
@Service
public class TrackerService {
+ private static final Logger logger = LoggerFactory.getLogger(TrackerService.class);
+
@Autowired
private TorrentMetaRepository torrentMetaRepository;
@@ -26,6 +31,9 @@
@Autowired
private TorrentStatsService statsService;
+
+ @Autowired
+ private UserService userService;
@Transactional
public byte[] handleAnnounce(Map<String, String[]> params, String ipAddress) {
@@ -39,6 +47,7 @@
// 解析参数
String infoHash = decodeParam(params.get("info_hash")[0]);
String peerId = decodeParam(params.get("peer_id")[0]);
+ String username = decodeParam(params.get("username")[0]);
int port = Integer.parseInt(params.get("port")[0]);
// 获取事件类型
@@ -57,8 +66,16 @@
// 创建或更新 peer 信息
PeerInfoEntity peer = findOrCreatePeer(peerId, infoHash);
+
+ // 计算流量增量
+ long uploadedDelta = uploaded - peer.getUploaded();
+ long downloadedDelta = downloaded - peer.getDownloaded();
+
+ // 更新用户总流量
+ updateUserTraffic(username, uploadedDelta, downloadedDelta);
// 设置 peer 属性
+ peer.setUsername(username);
setPeerProperties(peer, ipAddress, port, uploaded, downloaded, left);
// 处理事件类型
@@ -81,6 +98,34 @@
}
}
+ // 添加更新用户流量的方法
+ @Transactional
+ private void updateUserTraffic(String username, long uploadedDelta, long downloadedDelta) {
+ if (uploadedDelta <= 0 && downloadedDelta <= 0) {
+ return; // 没有新的流量,不需要更新
+ }
+
+ User user = userService.findByUsername(username);
+ if (user != null) {
+ long oldUploaded = user.getUploaded();
+ long oldDownloaded = user.getDownloaded();
+
+ user.setUploaded(oldUploaded + uploadedDelta);
+ user.setDownloaded(oldDownloaded + downloadedDelta);
+ userService.save(user);
+
+ // 更新用户等级
+ userService.updateUserLevel(user.getUid());
+
+ logger.info("用户 {} 流量更新: 上传 {} -> {} (+{}), 下载 {} -> {} (+{})",
+ username,
+ oldUploaded, user.getUploaded(), uploadedDelta,
+ oldDownloaded, user.getDownloaded(), downloadedDelta);
+ } else {
+ logger.warn("尝试更新不存在的用户流量: {}", username);
+ }
+ }
+
// 辅助方法:获取事件参数
private String getEventParam(Map<String, String[]> params) {
return params.containsKey("event") ?
@@ -96,7 +141,15 @@
// 辅助方法:查找或创建 peer
private PeerInfoEntity findOrCreatePeer(String peerId, String infoHash) {
return peerInfoRepository.findByPeerIdAndInfoHash(peerId, infoHash)
- .orElseGet(PeerInfoEntity::new);
+ .orElseGet(() -> {
+ PeerInfoEntity newPeer = new PeerInfoEntity();
+ newPeer.setPeerId(peerId);
+ newPeer.setInfoHash(infoHash);
+ newPeer.setActive(true);
+ newPeer.setStatus("downloading");
+ newPeer.setLastSeen(LocalDateTime.now());
+ return newPeer;
+ });
}
// 辅助方法:设置 peer 属性