Merge "添加更新用户流量的方法,并定时跟新到所有用户"

Change-Id: I558ee7de6767ed1b78685883310a268ea51b198a
diff --git a/src/main/java/com/pt/controller/ResourceController.java b/src/main/java/com/pt/controller/ResourceController.java
index 306851a..2f75af6 100644
--- a/src/main/java/com/pt/controller/ResourceController.java
+++ b/src/main/java/com/pt/controller/ResourceController.java
@@ -77,13 +77,13 @@
         Map<String, Object> ans = new HashMap<>();
 
         if (!JWTUtils.checkToken(token, username, Constants.UserRole.USER)) {
-            ans.put("message", "Invalid token");
+            ans.put("result", "Invalid token");
             return ResponseEntity.badRequest().body(ans);
         }
 
         User user = userService.findByUsername(username);
         if (user == null || user.getLevel() < 2) {
-            ans.put("message", "Insufficient permissions to publish resources");
+            ans.put("result", "Insufficient permissions to publish resources");
             return ResponseEntity.status(403).body(ans);
         }
 
@@ -91,11 +91,11 @@
             // 传入种子文件字节,同时传入资源其他信息
             resourceService.publishResource(name, description, username, size, torrentFile.getBytes());
         } catch (Exception e) {
-            ans.put("message", "Failed to publish resource: " + e.getMessage());
+            ans.put("result", "Failed to publish resource: " + e.getMessage());
             return ResponseEntity.status(500).body(ans);
         }
 
-        ans.put("message", "Resource published successfully");
+        ans.put("result", "Resource published successfully");
         return ResponseEntity.ok(ans);
     }
 
diff --git a/src/main/java/com/pt/controller/TrackerController.java b/src/main/java/com/pt/controller/TrackerController.java
index edae09f..56c0867 100644
--- a/src/main/java/com/pt/controller/TrackerController.java
+++ b/src/main/java/com/pt/controller/TrackerController.java
@@ -7,22 +7,34 @@
 import org.springframework.web.bind.annotation.*;
 
 import java.io.IOException;
+import java.util.HashMap;
 import java.util.Map;
 
 @RestController
-@CrossOrigin(origins = "*")
 @RequestMapping("/api/tracker")
+@CrossOrigin(origins = "*")
 public class TrackerController {
 
     @Autowired
     private TrackerService trackerService;
 
-    @GetMapping("/announce")
-    public void announce(HttpServletRequest request, HttpServletResponse response) throws IOException {
+    // tracker相应bt客户端的announce请求
+    @PostMapping("/announce")
+    public void announceByPost(
+            @RequestParam("info_hash") String infoHash,
+            @RequestParam("peer_id") String peerId,
+            @RequestParam("port") int port,
+            HttpServletRequest request,
+            HttpServletResponse response
+    ) throws IOException {
         try {
             String ip = request.getRemoteAddr();
-            Map<String, String[]> params = request.getParameterMap();
 
+            // 将参数封装为 Map 传给服务层(也可以直接传对象)
+            Map<String, String[]> params = new HashMap<>();
+            params.put("info_hash", new String[]{infoHash});
+            params.put("peer_id", new String[]{peerId});
+            params.put("port", new String[]{String.valueOf(port)});
             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..56125cb 100644
--- a/src/main/java/com/pt/entity/PeerInfoEntity.java
+++ b/src/main/java/com/pt/entity/PeerInfoEntity.java
@@ -25,6 +25,24 @@
     private long downloaded;      // 已下载量
     private long left;            // 剩余下载量
 
+    private String username;  // 添加用户名字段
+
+    public PeerInfoEntity(String ipAddress, int port, String peerId) {
+        this.ip = ipAddress;
+        this.port = port;
+        this.peerId = peerId;
+        this.lastSeen = LocalDateTime.now();
+        this.status = "downloading"; // 默认状态为下载中
+        this.isActive = true; // 默认活跃状态
+        this.uploaded = 0;
+        this.downloaded = 0;
+        this.left = 0;
+    }
+
+    public PeerInfoEntity() {
+
+    }
+
     public Long getId() {
         return id;
     }
@@ -113,4 +131,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/entity/User.java b/src/main/java/com/pt/entity/User.java
index aef2cd3..bc3a488 100644
--- a/src/main/java/com/pt/entity/User.java
+++ b/src/main/java/com/pt/entity/User.java
@@ -16,6 +16,7 @@
     private int points;
     private long uploaded;
     private long downloaded;
+    private double shareRatio;
 
     public User() {
     }
@@ -79,6 +80,17 @@
         this.downloaded = downloaded;
     }
 
+    public double getShareRatio() {
+        if (downloaded == 0) {
+            return 0;
+        }
+        return (double) uploaded / downloaded;
+    }
+
+    public void setShareRatio(double shareRatio) {
+        this.shareRatio = shareRatio;
+    }
+
     @Override
     public String toString() {
         return "{" +
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/repository/TorrentMetaRepository.java b/src/main/java/com/pt/repository/TorrentMetaRepository.java
index 839fc01..5135661 100644
--- a/src/main/java/com/pt/repository/TorrentMetaRepository.java
+++ b/src/main/java/com/pt/repository/TorrentMetaRepository.java
@@ -3,8 +3,10 @@
 import com.pt.entity.TorrentMeta;
 import org.springframework.data.jpa.repository.JpaRepository;
 
+import java.util.Optional;
+
 public interface TorrentMetaRepository extends JpaRepository<TorrentMeta, Long> {
-    TorrentMeta findByInfoHash(String infoHash);
+    Optional<TorrentMeta> findByInfoHash(String infoHash);
 
     TorrentMeta findByFilename(String filename);
 }
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/TorrentService.java b/src/main/java/com/pt/service/TorrentService.java
index ad38622..3c9d28d 100644
--- a/src/main/java/com/pt/service/TorrentService.java
+++ b/src/main/java/com/pt/service/TorrentService.java
@@ -10,6 +10,7 @@
 import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
 @Service
 public class TorrentService {
@@ -18,25 +19,17 @@
     private TorrentMetaRepository torrentMetaRepository;
 
     public TorrentMeta parseAndSaveTorrent(byte[] torrentBytes) throws Exception {
-        System.out.println("111");
-        for (byte b : torrentBytes) {
-            System.out.println(b);
-        }
         Map<String, Object> torrentDict = (Map<String, Object>) BencodeCodec.decode(torrentBytes);
         if (!torrentDict.containsKey("info")) {
             throw new IllegalArgumentException("Invalid torrent file: missing 'info' dictionary");
         }
 
         Map<String, Object> infoDict = (Map<String, Object>) torrentDict.get("info");
-
-        System.out.println(222);
         // 计算 info_hash
         byte[] infoEncoded = BencodeCodec.encode(infoDict);
         MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
         byte[] infoHashBytes = sha1.digest(infoEncoded);
         String infoHash = bytesToHex(infoHashBytes);
-
-        System.out.println(333);
         // 获取文件名,大小等
         String name = (String) infoDict.get("name");
         long length = 0;
@@ -50,17 +43,23 @@
             }
             length = totalLength;
         }
-
         // 保存到数据库
-        TorrentMeta meta = new TorrentMeta();
-        meta.setFilename(name);
-        meta.setInfoHash(infoHash);
-        meta.setSize(length);
-        meta.setUploadTime(LocalDateTime.now());
-        meta.setTorrentData(torrentBytes);
+        Optional<TorrentMeta> existing = torrentMetaRepository.findByInfoHash(infoHash);
 
-        torrentMetaRepository.save(meta);
-        return meta;
+        if (existing.isPresent()) {
+            System.out.println("该种子已存在,跳过保存");
+            return existing.get();
+        } else {
+            TorrentMeta meta = new TorrentMeta();
+            meta.setFilename(name);
+            meta.setInfoHash(infoHash);
+            meta.setSize(length);
+            meta.setUploadTime(LocalDateTime.now());
+            meta.setTorrentData(torrentBytes);
+
+            return torrentMetaRepository.save(meta);
+        }
+
     }
 
 
diff --git a/src/main/java/com/pt/service/TorrentStatsService.java b/src/main/java/com/pt/service/TorrentStatsService.java
index 9596784..baa63e3 100644
--- a/src/main/java/com/pt/service/TorrentStatsService.java
+++ b/src/main/java/com/pt/service/TorrentStatsService.java
@@ -1,6 +1,7 @@
 package com.pt.service;
 
 import com.pt.entity.PeerInfoEntity;
+import com.pt.entity.TorrentMeta;
 import com.pt.entity.TorrentStats;
 import com.pt.exception.ResourceNotFoundException;
 import com.pt.repository.PeerInfoRepository;
@@ -13,6 +14,7 @@
 
 import java.time.LocalDateTime;
 import java.util.List;
+import java.util.Optional;
 
 @Service
 public class TorrentStatsService {
@@ -91,11 +93,18 @@
         }
 
         // 3. 更新统计记录
-        TorrentStats stats = statsRepository.findByTorrentId(
-                torrentMetaRepository.findByInfoHash(infoHash).getId()
-        ).orElse(new TorrentStats());
+        Optional<TorrentMeta> optionalMeta = torrentMetaRepository.findByInfoHash(infoHash);
+        if (optionalMeta.isEmpty()) {
+            // 处理找不到 info_hash 的情况
+            throw new IllegalArgumentException("Invalid info_hash: not found in database");
+        }
 
-        stats.setTorrentId(torrentMetaRepository.findByInfoHash(infoHash).getId());
+        TorrentMeta meta = optionalMeta.get();
+
+        TorrentStats stats = statsRepository.findByTorrentId(meta.getId())
+                .orElse(new TorrentStats());
+
+        stats.setTorrentId(meta.getId());
         stats.setSeederCount(seeders);
         stats.setLeecherCount(leechers);
         stats.setCompletedCount(completed);
diff --git a/src/main/java/com/pt/service/TrackerService.java b/src/main/java/com/pt/service/TrackerService.java
index 191479b..ea840e1 100644
--- a/src/main/java/com/pt/service/TrackerService.java
+++ b/src/main/java/com/pt/service/TrackerService.java
@@ -1,180 +1,100 @@
 package com.pt.service;
 
-import com.pt.entity.PeerInfoEntity;
 import com.pt.entity.TorrentMeta;
-import com.pt.exception.ResourceNotFoundException;
-import com.pt.repository.PeerInfoRepository;
 import com.pt.repository.TorrentMetaRepository;
 import com.pt.utils.BencodeCodec;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
 
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
 import java.nio.charset.StandardCharsets;
-import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 @Service
 public class TrackerService {
 
+    private final Map<String, List<PeerInfo>> torrentPeers = new ConcurrentHashMap<>();
+
     @Autowired
     private TorrentMetaRepository torrentMetaRepository;
 
-    @Autowired
-    private PeerInfoRepository peerInfoRepository;
-
-    @Autowired
-    private TorrentStatsService statsService;
-
-    @Transactional
     public byte[] handleAnnounce(Map<String, String[]> params, String ipAddress) {
         try {
-            // 验证必要参数
-            if (!params.containsKey("info_hash") || !params.containsKey("peer_id")
-                    || !params.containsKey("port")) {
-                return errorResponse("Missing required parameters");
+            if (!params.containsKey("info_hash") || !params.containsKey("peer_id") || !params.containsKey("port")) {
+                return BencodeCodec.encode(Map.of("failure reason", "Missing required parameters"));
             }
 
-            // 解析参数
+            System.out.println("Received announce params: " + params);
+            System.out.println("Client IP: " + ipAddress);
+
+            // 用ISO-8859-1解码,确保二进制数据正确还原
             String infoHash = decodeParam(params.get("info_hash")[0]);
-            String peerId = decodeParam(params.get("peer_id")[0]);
-            int port = Integer.parseInt(params.get("port")[0]);
+            System.out.println("Decoded info_hash (raw bytes as hex): " + bytesToHex(infoHash.getBytes("ISO-8859-1")));
 
-            // 获取事件类型
-            String event = getEventParam(params);
-
-            // 获取流量数据
-            long uploaded = getLongParam(params, "uploaded", 0);
-            long downloaded = getLongParam(params, "downloaded", 0);
-            long left = getLongParam(params, "left", 0);
-
-            // 验证种子是否存在
-            TorrentMeta meta = torrentMetaRepository.findByInfoHash(infoHash);
-            if (meta == null) {
-                return errorResponse("Torrent not found: " + infoHash);
+            Optional<TorrentMeta> meta = torrentMetaRepository.findByInfoHash(infoHash);
+            if (!meta.isPresent()) {
+                System.out.println("Invalid info_hash: not found in DB");
+                return BencodeCodec.encode(Map.of("failure reason", "Invalid info_hash"));
             }
 
-            // 创建或更新 peer 信息
-            PeerInfoEntity peer = findOrCreatePeer(peerId, infoHash);
+            String peerId = decodeParam(params.get("peer_id")[0]);
+            System.out.println("Decoded peer_id: " + peerId);
 
-            // 设置 peer 属性
-            setPeerProperties(peer, ipAddress, port, uploaded, downloaded, left);
+            int port = Integer.parseInt(params.get("port")[0]);
+            System.out.println("Port: " + port);
 
-            // 处理事件类型
-            handlePeerEvent(event, peer, left);
+            PeerInfo peer = new PeerInfo(ipAddress, port, peerId);
 
-            // 保存 peer
-            peerInfoRepository.save(peer);
+            torrentPeers.computeIfAbsent(infoHash, k -> new CopyOnWriteArrayList<>());
+            List<PeerInfo> peers = torrentPeers.get(infoHash);
 
-            // 更新种子统计信息
-            statsService.updateTorrentStats(infoHash);
+            boolean exists = peers.stream().anyMatch(p -> p.peerId.equals(peerId));
+            if (!exists) {
+                peers.add(peer);
+                System.out.println("Added new peer: " + peerId);
+            } else {
+                System.out.println("Peer already exists: " + peerId);
+            }
 
-            // 获取 peer 列表响应
-            List<PeerInfoEntity> activePeers = peerInfoRepository.findActivePeersByInfoHash(infoHash);
-            byte[] peerBytes = buildPeerResponse(activePeers);
+            List<String> ips = peers.stream().map(p -> p.ip).toList();
+            List<Integer> ports = peers.stream().map(p -> p.port).toList();
+            byte[] peerBytes = BencodeCodec.buildCompactPeers(ips, ports);
 
-            // 返回成功响应
             return BencodeCodec.buildTrackerResponse(1800, peerBytes);
         } catch (Exception e) {
-            return errorResponse("Internal server error");
+            e.printStackTrace();  // 打印异常堆栈,方便定位错误
+            return BencodeCodec.encode(Map.of("failure reason", "Internal server error"));
         }
     }
 
-    // 辅助方法:获取事件参数
-    private String getEventParam(Map<String, String[]> params) {
-        return params.containsKey("event") ?
-                decodeParam(params.get("event")[0]) : "update";
+    // 辅助函数:打印字节转成16进制字符串
+    private String bytesToHex(byte[] bytes) {
+        StringBuilder sb = new StringBuilder();
+        for (byte b : bytes) {
+            sb.append(String.format("%02x", b));
+        }
+        return sb.toString();
     }
 
-    // 辅助方法:获取长整型参数
-    private long getLongParam(Map<String, String[]> params, String key, long defaultValue) {
-        return params.containsKey(key) ?
-                Long.parseLong(params.get(key)[0]) : defaultValue;
+    // decodeParam 建议用ISO-8859-1解码
+    private String decodeParam(String param) throws UnsupportedEncodingException {
+        return URLDecoder.decode(param, "ISO-8859-1");
     }
 
-    // 辅助方法:查找或创建 peer
-    private PeerInfoEntity findOrCreatePeer(String peerId, String infoHash) {
-        return peerInfoRepository.findByPeerIdAndInfoHash(peerId, infoHash)
-                .orElseGet(PeerInfoEntity::new);
-    }
 
-    // 辅助方法:设置 peer 属性
-    private void setPeerProperties(PeerInfoEntity peer, String ip, int port,
-                                   long uploaded, long downloaded, long left) {
-        peer.setIp(ip);
-        peer.setPort(port);
-        peer.setPeerId(peer.getPeerId() != null ? peer.getPeerId() : ""); // 防止 NPE
-        peer.setInfoHash(peer.getInfoHash() != null ? peer.getInfoHash() : "");
-        peer.setUploaded(uploaded);
-        peer.setDownloaded(downloaded);
-        peer.setLeft(left);
-        peer.setLastSeen(LocalDateTime.now());
-    }
+    private static class PeerInfo {
+        String ip;
+        int port;
+        String peerId;
 
-    // 辅助方法:处理 peer 事件
-    private void handlePeerEvent(String event, PeerInfoEntity peer, long left) {
-        switch (event) {
-            case "started":
-                peer.setStatus("downloading");
-                peer.setActive(true);
-                break;
-
-            case "stopped":
-                peer.setActive(false);
-                break;
-
-            case "completed":
-                handleCompletedEvent(peer);
-                break;
-
-            case "update":
-            default:
-                handleUpdateEvent(peer, left);
-                break;
+        public PeerInfo(String ip, int port, String peerId) {
+            this.ip = ip;
+            this.port = port;
+            this.peerId = peerId;
         }
     }
+}
 
-    // 处理完成事件
-    private void handleCompletedEvent(PeerInfoEntity peer) {
-        peer.setStatus("completed");
-        peer.setActive(true);
-        peer.setLeft(0);
-        incrementCompletedCount(peer.getInfoHash());
-    }
-
-    // 处理更新事件
-    private void handleUpdateEvent(PeerInfoEntity peer, long left) {
-        if (left == 0 && "downloading".equals(peer.getStatus())) {
-            // 检测到下载完成
-            peer.setStatus("completed");
-            incrementCompletedCount(peer.getInfoHash());
-        }
-        peer.setActive(true);
-    }
-
-    // 增加完成次数
-    private void incrementCompletedCount(String infoHash) {
-        TorrentMeta meta = torrentMetaRepository.findByInfoHash(infoHash);
-        if (meta != null) {
-            statsService.incrementCompletedCount(meta.getId());
-        }
-    }
-
-    // 构建 peer 响应
-    private byte[] buildPeerResponse(List<PeerInfoEntity> peers) {
-        List<String> ips = peers.stream().map(PeerInfoEntity::getIp).toList();
-        List<Integer> ports = peers.stream().map(PeerInfoEntity::getPort).toList();
-        return BencodeCodec.buildCompactPeers(ips, ports);
-    }
-
-    // 构建错误响应
-    private byte[] errorResponse(String reason) {
-        return BencodeCodec.encode(Map.of("failure reason", reason));
-    }
-
-    // 解码参数
-    private String decodeParam(String raw) {
-        return new String(raw.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
-    }
-}
\ No newline at end of file
diff --git a/src/main/java/com/pt/utils/BencodeCodec.java b/src/main/java/com/pt/utils/BencodeCodec.java
index 2117d43..e3dfe5a 100644
--- a/src/main/java/com/pt/utils/BencodeCodec.java
+++ b/src/main/java/com/pt/utils/BencodeCodec.java
@@ -76,22 +76,21 @@
     /* ------------- 解码部分 ------------- */
 
     public static Object decode(byte[] data) throws IOException {
-        try (ByteArrayInputStream in = new ByteArrayInputStream(data)) {
-            in.mark(data.length);
+        try (PushbackInputStream in = new PushbackInputStream(new ByteArrayInputStream(data))) {
             return decodeNext(in);
         }
     }
 
-    private static Object decodeNext(InputStream in) throws IOException {
+    private static Object decodeNext(PushbackInputStream in) throws IOException {
         int prefix = in.read();
         if (prefix == -1) {
             throw new IOException("Unexpected end of stream");
         }
-
-        in.mark(1024);
+        // no mark/reset calls here
 
         if (prefix >= '0' && prefix <= '9') {
-            in.reset();
+            // 字符串,回退这个字节,parseString自行读长度
+            in.unread(prefix);
             return parseString(in);
         } else if (prefix == 'i') {
             return parseInteger(in);
@@ -104,33 +103,41 @@
         }
     }
 
+
+
     private static String parseString(InputStream in) throws IOException {
-        StringBuilder lenStr = new StringBuilder();
-        int b;
-        while ((b = in.read()) != -1 && b != ':') {
-            if (b < '0' || b > '9') {
-                throw new IOException("Invalid string length character: " + (char) b);
-            }
-            lenStr.append((char) b);
-        }
-        if (b == -1) {
-            throw new IOException("Unexpected end of stream reading string length");
-        }
-        int length = Integer.parseInt(lenStr.toString());
+        int ch;
+        StringBuilder lenBuilder = new StringBuilder();
 
-        byte[] buf = new byte[length];
-        int offset = 0;
-        while (offset < length) {
-            int read = in.read(buf, offset, length - offset);
-            if (read == -1) {
-                throw new IOException("Unexpected end of stream reading string data");
+        while ((ch = in.read()) != -1 && ch != ':') {
+            if (!Character.isDigit(ch)) {
+                throw new IOException("Invalid string length prefix: " + (char) ch);
             }
-            offset += read;
+            lenBuilder.append((char) ch);
         }
 
-        return new String(buf, StandardCharsets.UTF_8);
+        if (ch != ':') {
+            throw new IOException("Expected ':' after string length");
+        }
+
+        int len = Integer.parseInt(lenBuilder.toString());
+        byte[] strBytes = new byte[len];
+
+        int read = 0;
+        while (read < len) {
+            int r = in.read(strBytes, read, len - read);
+            if (r == -1) {
+                throw new IOException("Unexpected end of stream when reading string");
+            }
+            read += r;
+        }
+
+        // 这里转换为 UTF-8 字符串返回,如果你确定是文本;如果是二进制可以改成返回byte[]
+        return new String(strBytes, StandardCharsets.UTF_8);
     }
 
+
+
     private static long parseInteger(InputStream in) throws IOException {
         StringBuilder intStr = new StringBuilder();
         int b;
@@ -140,41 +147,44 @@
         if (b == -1) {
             throw new IOException("Unexpected end of stream reading integer");
         }
-        return Long.parseLong(intStr.toString());
+
+        String intValue = intStr.toString();
+        System.out.println("Integer parsed raw: " + intValue);  // debug line
+
+        return Long.parseLong(intValue);
     }
 
-    private static List<Object> parseList(InputStream in) throws IOException {
+    private static List<Object> parseList(PushbackInputStream in) throws IOException {
         List<Object> list = new ArrayList<>();
-        int b;
         while (true) {
-            in.mark(1);
-            b = in.read();
-            if (b == -1) {
-                throw new IOException("Unexpected end of stream reading list");
-            }
-            if (b == 'e') {
+            int ch = in.read();
+            if (ch == 'e') {
                 break;
             }
-            in.reset();
+            if (ch == -1) {
+                throw new IOException("Unexpected end of stream in list");
+            }
+            in.unread(ch);
             list.add(decodeNext(in));
         }
         return list;
     }
 
-    private static Map<String, Object> parseDict(InputStream in) throws IOException {
+    private static Map<String, Object> parseDict(PushbackInputStream in) throws IOException {
         Map<String, Object> map = new LinkedHashMap<>();
-        int b;
+
         while (true) {
-            in.mark(1);
-            b = in.read();
-            if (b == -1) {
-                throw new IOException("Unexpected end of stream reading dictionary");
+            int ch = in.read();
+            if (ch == 'e') {
+                break; // 字典结束
             }
-            if (b == 'e') {
-                break;
+            if (ch == -1) {
+                throw new IOException("Unexpected end of stream in dict");
             }
-            in.reset();
-            String key = (String) decodeNext(in);
+            // 回退到上面读的字节,parseString 自己读长度
+            in.unread(ch);
+
+            String key = parseString(in);
             Object value = decodeNext(in);
             map.put(key, value);
         }
@@ -187,8 +197,9 @@
     public static byte[] buildCompactPeer(String ip, int port) {
         try {
             InetAddress addr = InetAddress.getByName(ip);
-            ByteBuffer buffer = ByteBuffer.allocate(6);
-            buffer.put(addr.getAddress());
+            byte[] ipBytes = addr.getAddress();
+            ByteBuffer buffer = ByteBuffer.allocate(ipBytes.length + 2);
+            buffer.put(ipBytes);
             buffer.putShort((short) port);
             return buffer.array();
         } catch (IOException e) {
@@ -196,6 +207,7 @@
         }
     }
 
+
     // 构造多个compact peer的二进制拼接
     public static byte[] buildCompactPeers(List<String> ips, List<Integer> ports) {
         if (ips.size() != ports.size()) throw new IllegalArgumentException("IPs and ports list size mismatch");