修改Tracker服务器相关bug

Change-Id: Id3ec61034575bd794724be98f832f8c065448b92
diff --git a/pom.xml b/pom.xml
index 5c3c877..ae8b1ba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,14 @@
             <version>0.11.5</version>
             <scope>runtime</scope>
         </dependency>
+        <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+            <version>6.2.7</version>
+            <scope>test</scope>
+        </dependency>
+
         <dependency>
             <groupId>io.jsonwebtoken</groupId>
             <artifactId>jjwt-jackson</artifactId>
@@ -79,6 +87,10 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+        </dependency>
     </dependencies>
 
 
diff --git a/src/main/java/com/pt/constant/Constants.java b/src/main/java/com/pt/constant/Constants.java
index a54704c..142ab1c 100644
--- a/src/main/java/com/pt/constant/Constants.java
+++ b/src/main/java/com/pt/constant/Constants.java
@@ -39,4 +39,7 @@
             return value;
         }
     }
+
+    public static final double MIN_SHARE_RATIO_THRESHOLD = 0.5; // 最低共享率要求
+    public static final long DOWNLOAD_EXEMPTION_BYTES = 20L * 1024L * 1024L * 1024L; // 20GB下载量豁免
 }
diff --git a/src/main/java/com/pt/controller/ResourceController.java b/src/main/java/com/pt/controller/ResourceController.java
index e405ef2..c663963 100644
--- a/src/main/java/com/pt/controller/ResourceController.java
+++ b/src/main/java/com/pt/controller/ResourceController.java
@@ -5,14 +5,21 @@
 import com.pt.entity.User;
 import com.pt.service.ResourceService;
 import com.pt.service.UserService;
+import com.pt.utils.BencodeCodec;
 import com.pt.utils.JWTUtils;
+import com.pt.utils.TorrentPasskeyModifier;
+import jakarta.servlet.http.HttpServletRequest;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
+import org.springframework.mock.web.MockMultipartFile;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -44,7 +51,11 @@
         if (resources.isEmpty()) {
             return ResponseEntity.noContent().build();
         }
-        return ResponseEntity.ok(resources);
+        ans.put("message", "Resources found");
+        ans.put("data", Map.of(
+                "resources", resources
+        ));
+        return ResponseEntity.ok().body(ans);
     }
 
     @GetMapping("/list/user")
@@ -62,7 +73,11 @@
         if (resources.isEmpty()) {
             return ResponseEntity.noContent().build();
         }
-        return ResponseEntity.ok(resources);
+        ans.put("message", "User resources found");
+        ans.put("data", Map.of(
+                "resources", resources
+        ));
+        return ResponseEntity.ok().body(ans);
     }
 
     @PostMapping("/publish")
@@ -70,65 +85,74 @@
             @RequestHeader("token") String token,
             @RequestParam("username") String username,
             @RequestParam("description") String description,
-            @RequestParam("torrent") MultipartFile torrentFile) {
+            @RequestParam("torrent") MultipartFile torrentFile
+            )
+    {
 
         Map<String, Object> ans = new HashMap<>();
 
         if (!JWTUtils.checkToken(token, username, Constants.UserRole.USER)) {
-            ans.put("result", "Invalid token");
+            ans.put("message", "Invalid token");
             return ResponseEntity.badRequest().body(ans);
         }
 
         User user = userService.findByUsername(username);
         if (user == null || user.getLevel() < 2) {
-            ans.put("result", "Insufficient permissions to publish resources");
+            ans.put("message", "Insufficient permissions to publish resources");
             return ResponseEntity.status(403).body(ans);
         }
 
         try {
             // 传入种子文件字节,同时传入资源其他信息
-            System.out.println("name" + torrentFile.getOriginalFilename());
-            resourceService.publishResource(torrentFile.getOriginalFilename(), description, username, torrentFile.getSize(), torrentFile.getBytes());
+            resourceService.publishResource(torrentFile.getOriginalFilename(), description, username, torrentFile.getBytes(), username);
         } catch (Exception e) {
-            ans.put("result", "Failed to publish resource: " + e.getMessage());
+            ans.put("message", "Failed to publish resource: " + e.getMessage());
             return ResponseEntity.status(500).body(ans);
         }
 
-        ans.put("result", "Resource published successfully");
+        ans.put("message", "Resource published successfully");
         return ResponseEntity.ok(ans);
     }
 
-
-
-
     @GetMapping("/get/{resourceId}")
     public ResponseEntity<?> getResourceById(@PathVariable("resourceId") int resourceId,
-//                                              @RequestHeader("token") String token,
+                                              @RequestHeader("token") String token,
                                               @RequestParam("username") 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);
-//        }
+        if(!JWTUtils.checkToken(token, username, Constants.UserRole.USER)) {
+            ans.put("message", "Invalid token");
+            return ResponseEntity.badRequest().body(ans);
+        }
 
         Resource resource = resourceService.getResourceById(resourceId);
         if (resource == null) {
-            return ResponseEntity.notFound().build();
+            ans.put("message", "Resource not found");
+            return ResponseEntity.badRequest().body(ans);
         }
-        return ResponseEntity.ok(resource);
+
+        ans.put("message", "Resource found");
+        ans.put("data", Map.of(
+                "resourceId", resource.getResourceId(),
+                "name", resource.getName(),
+                "description", resource.getDescription(),
+                "author", resource.getAuthor(),
+                "publishTime", resource.getPublishTime()
+        ));
+
+        return ResponseEntity.ok().body(ans);
     }
 
     @GetMapping("/download/{resourceId}")
     public ResponseEntity<?> downloadResource(@PathVariable("resourceId") int resourceId,
-//                                              @RequestHeader("token") String token,
+                                              @RequestHeader("token") String token,
                                               @RequestParam("username") String username) throws IOException {
 
         Map<String, Object> ans = new HashMap<>();
-//        if(!JWTUtils.checkToken(token, username, Constants.UserRole.USER)) {
-//            ans.put("message", "Invalid token");
-//            return ResponseEntity.badRequest().body(ans);
-//        }
+        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) {
@@ -136,20 +160,17 @@
             return ResponseEntity.status(404).body(ans);
         }
 
-        final double MIN_SHARE_RATIO_THRESHOLD = 0.5; // 最低共享率要求
-        final long DOWNLOAD_EXEMPTION_BYTES = 20L * 1024L * 1024L * 1024L; // 20GB下载量豁免
-
         long uploaded = user.getUploaded();
         long downloaded = user.getDownloaded();
 
         // 如果用户的下载量超过豁免值,则检查其共享率
-        if (downloaded > DOWNLOAD_EXEMPTION_BYTES) {
+        if (downloaded > Constants.DOWNLOAD_EXEMPTION_BYTES) {
             // 防止除以零
             double shareRatio = (downloaded == 0) ? Double.MAX_VALUE : (double) uploaded / downloaded;
-            if (shareRatio < MIN_SHARE_RATIO_THRESHOLD) {
+            if (shareRatio < Constants.MIN_SHARE_RATIO_THRESHOLD) {
                 ans.put("message", "Share ratio is too low. Please seed more to improve your ratio before downloading new resources.");
                 ans.put("current_share_ratio", String.format("%.2f", shareRatio));
-                ans.put("required_share_ratio", MIN_SHARE_RATIO_THRESHOLD);
+                ans.put("required_share_ratio", Constants.MIN_SHARE_RATIO_THRESHOLD);
                 return ResponseEntity.status(403).body(ans); // 403 Forbidden
             }
         }
@@ -160,10 +181,12 @@
             return ResponseEntity.notFound().build();
         }
 
+        TorrentPasskeyModifier modifier = new TorrentPasskeyModifier();
+
         return ResponseEntity.ok()
                 .header("Content-Type", "application/x-bittorrent")
                 .header("Content-Disposition", "attachment; filename=\"" + resource.getName() + ".torrent\"")
-                .body(file);
+                .body(modifier.analyzeTorrentFile(file, username)); // 返回修改后的 torrent 文件
     }
 
     @GetMapping("/search")
@@ -181,17 +204,23 @@
         if (resources.isEmpty()) {
             return ResponseEntity.noContent().build();
         }
+
+        ans.put("message", "Search results found");
+        ans.put("data", Map.of(
+                "resources", resources
+        ));
         return ResponseEntity.ok(resources);
     }
 
-    @GetMapping("/delete")
+    @DeleteMapping("/delete")
     public ResponseEntity<?> deleteResource(@RequestHeader("token") String token,
-                                            @RequestParam("username") String username,
-                                            @RequestParam("resourceId") int resourceId) {
+                                            @RequestBody Map<String, Object> requestBody) {
+        String username = (String) requestBody.get("username");
+        Integer resourceId = (Integer) requestBody.get("resourceId");
         Map<String, Object> ans = new HashMap<>();
         Resource resource = resourceService.getResourceById(resourceId);
 
-        if(!JWTUtils.checkToken(token, username, Constants.UserRole.ADMIN) || resource == null || !resource.getAuthor().equals(username)) {
+        if(!JWTUtils.checkToken(token, username, Constants.UserRole.ADMIN) || resource == null) {
             ans.put("message", "Invalid token or insufficient permissions");
             return ResponseEntity.badRequest().body(ans);
         }
@@ -206,5 +235,4 @@
         ans.put("message", "Resource deleted 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 f2a75e3..b670f3f 100644
--- a/src/main/java/com/pt/controller/TrackerController.java
+++ b/src/main/java/com/pt/controller/TrackerController.java
@@ -1,12 +1,17 @@
 package com.pt.controller;
 
+import com.pt.service.TorrentMetaService;
 import com.pt.service.TrackerService;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -18,36 +23,79 @@
     @Autowired
     private TrackerService trackerService;
 
+    @Autowired
+    private TorrentMetaService torrentMetaService;
+
     // tracker相应bt客户端的announce请求
-    @PostMapping("/announce")
-    public void announceByPost(
-            @RequestBody Map<String, String> body,
+    @GetMapping("/announce")
+    public void announce(
+            @RequestParam(value = "passkey", required = false) String passkey,
+            @RequestParam(value = "info_hash") String encodedInfoHash,
+            @RequestParam(value = "peer_id") String encodedPeerId,
+            @RequestParam(value = "port") int port,
+            @RequestParam(value = "uploaded") long uploaded,
+            @RequestParam(value = "downloaded") long downloaded,
+            @RequestParam(value = "left") long left,
+            @RequestParam(value = "event", defaultValue = "") String event,
+            @RequestParam(value = "compact", defaultValue = "1") int compact,
+            @RequestParam(value = "numwant", defaultValue = "50") int numwant,
             HttpServletRequest request,
             HttpServletResponse response
     ) throws IOException {
         try {
 
-            String infoHash = body.get("info_hash");
-            String peerId = body.get("peer_id");
-            int port = Integer.parseInt(body.get("port"));
+            System.out.println("TrackerController------------------------announce");
+            // 解码 info_hash 和 peer_id(从URL编码的字符串转为字节数组)
+            System.out.println("TrackerController------------------------announce: info_hash=" + encodedInfoHash + ", peer_id=" + encodedPeerId);
+            String decodedInfoHash = URLDecoder.decode(encodedInfoHash, StandardCharsets.ISO_8859_1);
+            String decodedPeerId = URLDecoder.decode(encodedPeerId, StandardCharsets.ISO_8859_1);
 
+            // 转换为字节数组
+            byte[] infoHashBytes = decodedInfoHash.getBytes(StandardCharsets.ISO_8859_1);
+            byte[] peerIdBytes = decodedPeerId.getBytes(StandardCharsets.ISO_8859_1);
+
+            // 验证 info_hash 长度
+//            if (infoHashBytes.length != 20) {
+//                throw new IllegalArgumentException("Invalid info_hash length");
+//            }
+
+            // 获取客户端IP
             String ip = request.getRemoteAddr();
+            System.out.println("Client IP: " + ip);
 
-            // 将参数封装为 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);
+            // 将参数封装为 Map 传给服务层
+            Map<String, Object> params = new HashMap<>();
+            params.put("info_hash", decodedInfoHash);
+            params.put("peer_id", decodedPeerId);
+            params.put("port", port);
+            params.put("uploaded", uploaded);
+            params.put("downloaded", downloaded);
+            params.put("left", left);
+            params.put("event", event);
+            params.put("compact", compact);
+            params.put("numwant", numwant);
+            params.put("ip", ip);
+            params.put("passkey", passkey);
 
+            byte[] bencodedResponse = trackerService.handleAnnounce(params, ip, event);
             response.setContentType("application/x-bittorrent");
             response.getOutputStream().write(bencodedResponse);
+
+        } catch (IllegalArgumentException e) {
+            System.out.println("TrackerService-----------------------Error processing announce request111: " + e.getMessage());
+            e.printStackTrace();
+            String errorResponse = "d14:failure reason" + e.getMessage().length() + ":" + e.getMessage() + "e";
+            response.setContentType("application/x-bittorrent");
+            response.getOutputStream().write(errorResponse.getBytes(StandardCharsets.ISO_8859_1));
         } catch (Exception e) {
-            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-            response.setContentType("text/plain");
-            response.getWriter().write("Tracker internal error: " + e.getMessage());
+            e.printStackTrace();
+            System.out.println("TrackerService-----------------------Error processing announce request222: " + e.getMessage());
+            String errorResponse = "d14:failure reason20:Internal server errore";
+            response.setContentType("application/x-bittorrent");
+            response.getOutputStream().write(errorResponse.getBytes(StandardCharsets.ISO_8859_1));
         }
     }
 
+
 }
 
diff --git a/src/main/java/com/pt/entity/PeerInfoEntity.java b/src/main/java/com/pt/entity/PeerInfoEntity.java
index 56125cb..e2d89be 100644
--- a/src/main/java/com/pt/entity/PeerInfoEntity.java
+++ b/src/main/java/com/pt/entity/PeerInfoEntity.java
@@ -8,24 +8,26 @@
 
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
-    private Long id;
+    private long id;
+
+    private String peerId;
 
     private String infoHash;
     private String ip;
     private int port;
-    private String peerId;
+
     private LocalDateTime lastSeen;
 
     // 新增状态字段
     private String status; // "seeding", "downloading", "completed"
-    private boolean isActive; // 是否活跃
+    private int isActive; // 是否活跃
 
     // 下载字段
-    private long uploaded;        // 已上传量
-    private long downloaded;      // 已下载量
-    private long left;            // 剩余下载量
+    private long upload;        // 已上传量
+    private long download;      // 已下载量     // 剩余下载量
 
-    private String username;  // 添加用户名字段
+    @Column(name = "download_left")
+    private long left;
 
     public PeerInfoEntity(String ipAddress, int port, String peerId) {
         this.ip = ipAddress;
@@ -33,21 +35,19 @@
         this.peerId = peerId;
         this.lastSeen = LocalDateTime.now();
         this.status = "downloading"; // 默认状态为下载中
-        this.isActive = true; // 默认活跃状态
-        this.uploaded = 0;
-        this.downloaded = 0;
-        this.left = 0;
+        this.isActive = 1; // 默认活跃状态
+        this.upload = 0;
+        this.download = 0;
     }
 
     public PeerInfoEntity() {
 
     }
 
-    public Long getId() {
+    public long getId() {
         return id;
     }
-
-    public void setId(Long id) {
+    public void setId(long id) {
         this.id = id;
     }
 
@@ -56,27 +56,19 @@
     }
 
     public long getUploaded() {
-        return uploaded;
+        return upload;
     }
 
     public void setUploaded(long uploaded) {
-        this.uploaded = uploaded;
+        this.upload = uploaded;
     }
 
     public long getDownloaded() {
-        return downloaded;
+        return download;
     }
 
     public void setDownloaded(long downloaded) {
-        this.downloaded = downloaded;
-    }
-
-    public long getLeft() {
-        return left;
-    }
-
-    public void setLeft(long left) {
-        this.left = left;
+        this.download = downloaded;
     }
 
     public void setInfoHash(String infoHash) {
@@ -124,19 +116,27 @@
         this.status = status;
     }
 
-    public boolean isActive() {
+    public int isActive() {
         return isActive;
     }
 
-    public void setActive(boolean active) {
+    public void setActive(int active) {
         isActive = active;
     }
 
-    public String getUsername() {
-        return username;
+    public void setIsActive(int isActive) {
+        this.isActive = isActive;
     }
 
-    public void setUsername(String username) {
-        this.username = username;
+    public int getIsActive() {
+        return isActive;
+    }
+
+    public long getLeft() {
+        return left;
+    }
+
+    public void setLeft(long left) {
+        this.left = left;
     }
 }
diff --git a/src/main/java/com/pt/entity/Resource.java b/src/main/java/com/pt/entity/Resource.java
index 0ac7321..073dbbb 100644
--- a/src/main/java/com/pt/entity/Resource.java
+++ b/src/main/java/com/pt/entity/Resource.java
@@ -15,7 +15,6 @@
     private int resourceId;
 
     private String name;
-    private double size;
 
     private LocalDateTime publishTime;
     private String author;
@@ -23,14 +22,12 @@
     private String description;
     private byte[] torrentData;
 
-    private String info_hash;
     public Resource() {
     }
 
-    public Resource(int resourceId, String name, double size, LocalDateTime publishTime, String author) {
+    public Resource(int resourceId, String name, LocalDateTime publishTime, String author) {
         this.resourceId = resourceId;
         this.name = name;
-        this.size = size;
         this.publishTime = publishTime;
         this.author = author;
     }
@@ -47,12 +44,6 @@
     public void setName(String name) {
         this.name = name;
     }
-    public double getSize() {
-        return size;
-    }
-    public void setSize(double size) {
-        this.size = size;
-    }
     public LocalDateTime getPublishTime() {
         return publishTime;
     }
@@ -79,14 +70,6 @@
         this.torrentData = torrentData;
     }
 
-    public String getInfo_hash() {
-        return info_hash;
-    }
-
-    public void setInfo_hash(String info_hash) {
-        this.info_hash = info_hash;
-    }
-
     /*
         * 重写toString方法,将资源信息以JSON字符串形式返回
      */
@@ -95,7 +78,6 @@
         return "{" +
                 "\"resourceId\":" + resourceId +
                 ", \"name\":\"" + name + "\"" +
-                ", \"size\":" + size +
                 ", \"publishTime\":\"" + publishTime + "\"" +
                 ", \"author\":\"" + author + "\"" +
                 ", \"description\":\"" + description + "\"" +
diff --git a/src/main/java/com/pt/repository/PeerInfoRepository.java b/src/main/java/com/pt/repository/PeerInfoRepository.java
index c76df9f..b9dbb79 100644
--- a/src/main/java/com/pt/repository/PeerInfoRepository.java
+++ b/src/main/java/com/pt/repository/PeerInfoRepository.java
@@ -16,20 +16,27 @@
     Optional<PeerInfoEntity> findByPeerIdAndInfoHash(String peerId, String infoHash);
 
     // 获取活跃的 peer
-    @Query("SELECT p FROM PeerInfoEntity p WHERE p.infoHash = :infoHash AND p.isActive = true")
+    @Query("SELECT p FROM PeerInfoEntity p WHERE p.infoHash = :infoHash AND p.isActive = 1")
     List<PeerInfoEntity> findActivePeersByInfoHash(String infoHash);
 
     // 获取需要清理的非活跃 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.upload) FROM PeerInfoEntity p")
+    Long sumUploadedByUsername();
 
-    @Query("SELECT SUM(p.downloaded) FROM PeerInfoEntity p WHERE p.username = :username")
-    Long sumDownloadedByUsername(@Param("username") String username);
+    @Query("SELECT SUM(p.download) FROM PeerInfoEntity p")
+    Long sumDownloadedByUsername();
+
+    @Query("SELECT p FROM PeerInfoEntity p WHERE p.infoHash = :infoHash AND p.status = \"seeding\"")
+    List<PeerInfoEntity> findSeedingPeersByInfoHash(@Param("infoHash") String infoHash);
+
+    @Query("SELECT p FROM PeerInfoEntity p WHERE p.infoHash = :infoHash AND p.status = \"downloading\"")
+    List<PeerInfoEntity> findDownloadingPeersByInfoHash(@Param("infoHash") String infoHash);
+
+    @Query("SELECT p FROM PeerInfoEntity p WHERE p.infoHash = :infoHash AND p.peerId = :peerId")
+    List<PeerInfoEntity> findByInfoHashAndPeerId(@Param("infoHash") String infoHash, @Param("peerId") String peerId);
+
 }
diff --git a/src/main/java/com/pt/repository/TorrentMetaRepository.java b/src/main/java/com/pt/repository/TorrentMetaRepository.java
index 5135661..ff16a44 100644
--- a/src/main/java/com/pt/repository/TorrentMetaRepository.java
+++ b/src/main/java/com/pt/repository/TorrentMetaRepository.java
@@ -9,4 +9,6 @@
     Optional<TorrentMeta> findByInfoHash(String infoHash);
 
     TorrentMeta findByFilename(String filename);
+
+    void deleteByInfoHash(String infoHash);
 }
diff --git a/src/main/java/com/pt/scheduler/UserTrafficSyncScheduler.java b/src/main/java/com/pt/scheduler/UserTrafficSyncScheduler.java
index 0ec838d..db81272 100644
--- a/src/main/java/com/pt/scheduler/UserTrafficSyncScheduler.java
+++ b/src/main/java/com/pt/scheduler/UserTrafficSyncScheduler.java
@@ -39,8 +39,8 @@
             List<User> users = userService.listAll();
             for (User user : users) {
                 // 获取用户所有peer的总流量
-                Long totalUploaded = peerInfoRepository.sumUploadedByUsername(user.getUsername());
-                Long totalDownloaded = peerInfoRepository.sumDownloadedByUsername(user.getUsername());
+                Long totalUploaded = peerInfoRepository.sumUploadedByUsername();
+                Long totalDownloaded = peerInfoRepository.sumDownloadedByUsername();
 
                 // 如果查询结果为空,设置为0
                 totalUploaded = totalUploaded != null ? totalUploaded : 0L;
diff --git a/src/main/java/com/pt/service/ResourceService.java b/src/main/java/com/pt/service/ResourceService.java
index 7d68817..4eb7823 100644
--- a/src/main/java/com/pt/service/ResourceService.java
+++ b/src/main/java/com/pt/service/ResourceService.java
@@ -6,11 +6,14 @@
 import com.pt.repository.DownloadRepository;
 import com.pt.repository.ResourceRepository;
 import com.pt.repository.TorrentMetaRepository;
+import com.pt.service.TrackerService; // 新增导入
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.time.LocalDateTime;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 @Service
 public class ResourceService {
@@ -27,6 +30,9 @@
     @Autowired
     private TorrentMetaRepository torrentMetaRepository;
 
+    @Autowired
+    private TrackerService trackerService; // 新增注入
+
     public List<Resource> getAllResources() {
         return resourceRepository.findAll();
     }
@@ -35,7 +41,7 @@
         return resourceRepository.findByAuthor(username);
     }
 
-    public void publishResource(String name, String description, String author, double size, byte[] torrentBytes) throws Exception {
+    public void publishResource(String name, String description, String author, byte[] torrentBytes, String username) throws Exception {
         // 解析并保存torrent元信息
         TorrentMeta meta = torrentService.parseAndSaveTorrent(torrentBytes);
 
@@ -44,13 +50,19 @@
         resource.setName(name);
         resource.setDescription(description);
         resource.setAuthor(author);
-        resource.setSize(size);
         resource.setPublishTime(LocalDateTime.now());
 
         resource.setTorrentData(torrentBytes);
-        resource.setInfo_hash(meta.getInfoHash());
         // 这里可以保存torrent文件路径,或直接存数据库,依据你的设计
         resourceRepository.save(resource);
+
+        // 在Tracker中注册相关信息
+//        Map<String, String[]> params = new HashMap<>();
+//        params.put("info_hash", new String[]{meta.getInfoHash()});
+//        // 这里peer_id和port可以先使用默认值,实际应用中可以根据需求修改
+//        params.put("peer_id", new String[]{username});
+//        params.put("port", new String[]{"6881"});
+//        trackerService.handleAnnounce(params, ip);
     }
 
     // 获取资源时,返回BLOB字段内容作为torrent文件
@@ -89,4 +101,4 @@
         return resourceRepository.findByNameContainingIgnoreCase(query);
     }
 
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/pt/service/TorrentMetaService.java b/src/main/java/com/pt/service/TorrentMetaService.java
new file mode 100644
index 0000000..185c321
--- /dev/null
+++ b/src/main/java/com/pt/service/TorrentMetaService.java
@@ -0,0 +1,30 @@
+package com.pt.service;
+
+import com.pt.entity.TorrentMeta;
+import com.pt.repository.TorrentMetaRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+
+@Service
+public class TorrentMetaService {
+
+    @Autowired
+    private TorrentMetaRepository torrentMetaRepository;
+
+    public void save(TorrentMeta torrentMeta) {
+        // 这里可以添加保存逻辑,比如调用repository的save方法
+        // torrentMetaRepository.save(torrentMeta);
+        System.out.println("Saving TorrentMeta: " + torrentMeta);
+    }
+
+    public void save(String infoHash, String name, long size) {
+        TorrentMeta torrentMeta = new TorrentMeta();
+        torrentMeta.setInfoHash(infoHash);
+        torrentMeta.setFilename(name);
+        torrentMeta.setSize(size);
+        torrentMeta.setUploadTime(LocalDateTime.now());
+        torrentMetaRepository.save(torrentMeta);
+    }
+}
diff --git a/src/main/java/com/pt/service/TorrentService.java b/src/main/java/com/pt/service/TorrentService.java
index 3c9d28d..e61e874 100644
--- a/src/main/java/com/pt/service/TorrentService.java
+++ b/src/main/java/com/pt/service/TorrentService.java
@@ -8,6 +8,7 @@
 
 import java.security.MessageDigest;
 import java.time.LocalDateTime;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -43,6 +44,9 @@
             }
             length = totalLength;
         }
+        // 添加Tracker地址到种子文件中
+        torrentDict.put("announce", "http://your-tracker-url/api/tracker/announce");
+
         // 保存到数据库
         Optional<TorrentMeta> existing = torrentMetaRepository.findByInfoHash(infoHash);
 
@@ -55,7 +59,7 @@
             meta.setInfoHash(infoHash);
             meta.setSize(length);
             meta.setUploadTime(LocalDateTime.now());
-            meta.setTorrentData(torrentBytes);
+            meta.setTorrentData(BencodeCodec.encode(torrentDict)); // 使用更新后的字典编码
 
             return torrentMetaRepository.save(meta);
         }
@@ -70,4 +74,4 @@
         }
         return sb.toString();
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/pt/service/TorrentStatsService.java b/src/main/java/com/pt/service/TorrentStatsService.java
index baa63e3..40b98ad 100644
--- a/src/main/java/com/pt/service/TorrentStatsService.java
+++ b/src/main/java/com/pt/service/TorrentStatsService.java
@@ -81,9 +81,9 @@
         int completed = 0;
 
         for (PeerInfoEntity peer : peers) {
-            if ("seeding".equals(peer.getStatus()) && peer.isActive()) {
+            if ("seeding".equals(peer.getStatus()) && peer.isActive() == 1) {
                 seeders++;
-            } else if ("downloading".equals(peer.getStatus()) && peer.isActive()) {
+            } else if ("downloading".equals(peer.getStatus()) && peer.isActive() == 1) {
                 leechers++;
             }
 
diff --git a/src/main/java/com/pt/service/TrackerService.java b/src/main/java/com/pt/service/TrackerService.java
index ea840e1..29ac1ad 100644
--- a/src/main/java/com/pt/service/TrackerService.java
+++ b/src/main/java/com/pt/service/TrackerService.java
@@ -1,27 +1,40 @@
 package com.pt.service;
 
+import com.pt.constant.Constants;
+import com.pt.entity.PeerInfoEntity;
 import com.pt.entity.TorrentMeta;
+import com.pt.entity.User;
+import com.pt.repository.PeerInfoRepository;
 import com.pt.repository.TorrentMetaRepository;
+import com.pt.repository.UserRepository;
 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.io.ByteArrayOutputStream;
 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;
 
-    public byte[] handleAnnounce(Map<String, String[]> params, String ipAddress) {
+    @Autowired
+    private PeerInfoRepository peerInfoEntityRepository;
+
+    @Autowired
+    private TorrentMetaService torrentMetaService;
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Transactional
+    public byte[] handleAnnounce(Map<String, Object> params, String ipAddress, String event) {
+
+        System.out.println("TrackerService------------------------handle announce");
+
         try {
             if (!params.containsKey("info_hash") || !params.containsKey("peer_id") || !params.containsKey("port")) {
                 return BencodeCodec.encode(Map.of("failure reason", "Missing required parameters"));
@@ -30,71 +43,300 @@
             System.out.println("Received announce params: " + params);
             System.out.println("Client IP: " + ipAddress);
 
-            // 用ISO-8859-1解码,确保二进制数据正确还原
-            String infoHash = decodeParam(params.get("info_hash")[0]);
-            System.out.println("Decoded info_hash (raw bytes as hex): " + bytesToHex(infoHash.getBytes("ISO-8859-1")));
-
-            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"));
+            String username = (String) params.get("passkey");
+            User user = null;
+            if(username != null){
+                user = userRepository.findByUsername(username);
+                if (user == null) {
+                    System.out.println("User not found: " + username);
+                    return BencodeCodec.encode(Map.of("failure reason", "User not found"));
+                }
             }
 
-            String peerId = decodeParam(params.get("peer_id")[0]);
-            System.out.println("Decoded peer_id: " + peerId);
+            int compact = params.containsKey("compact") ? (int) params.get("compact") : 1;
 
-            int port = Integer.parseInt(params.get("port")[0]);
-            System.out.println("Port: " + port);
+            System.out.println("left" + (long)params.get("left"));
+            String infoHash = (String)params.get("info_hash");
 
-            PeerInfo peer = new PeerInfo(ipAddress, port, peerId);
+            switch(event){
+                case "started":
+                    String status = (long)params.get("left") == (long)0 ? "seeding" : "downloading"; // 默认状态为下载中
+                    System.out.println("Event: started");
+                    Optional<TorrentMeta> meta = torrentMetaRepository.findByInfoHash(infoHash);
 
-            torrentPeers.computeIfAbsent(infoHash, k -> new CopyOnWriteArrayList<>());
-            List<PeerInfo> peers = torrentPeers.get(infoHash);
+                    if(status.equals("seeding")) {
+                        if(meta.isEmpty()){
+                            torrentMetaService.save(infoHash, "", 123456789L);
+                        }
 
-            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);
+                        String peerId = (String) params.get("peer_id");
+                        System.out.println("Decoded peer_id: " + peerId);
+                        int port = (int) params.get("port");
+                        System.out.println("Port: " + port);
+
+                        if(peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).isEmpty()) {
+
+                            PeerInfoEntity peer = new PeerInfoEntity(ipAddress, port, peerId);
+                            peer.setInfoHash(infoHash);
+                            peer.setStatus(status);
+                            peer.setIsActive(1); // 默认活跃状态
+                            peer.setUploaded(0);
+                            peer.setDownloaded(0);
+                            peer.setPeerId((String) params.get("peer_id"));
+                            peerInfoEntityRepository.save(peer);
+
+                        }
+                        else {
+                            System.out.println("Peer already exists for info_hash: " + infoHash);
+                            peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).get(0).setStatus("seeding");
+                        }
+
+                        // 返回成功响应
+                        Map<String, Object> response = new HashMap<>();
+                        response.put("interval", 1800); // 客户端应该多久再次请求
+                        response.put("min interval", 900); // 最小请求间隔
+                        response.put("complete", peerInfoEntityRepository.findByInfoHash(infoHash).size()); // 种子数
+                        response.put("incomplete", peerInfoEntityRepository.findDownloadingPeersByInfoHash(infoHash).size()); // 下载者数
+
+                        // 紧凑格式 - 返回当前种子的信息
+                        if(compact == 1) {
+                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                            try {
+                                // 转换IP地址
+                                String[] ipParts = ipAddress.split("\\.");
+                                for(String part : ipParts) {
+                                    baos.write(Integer.parseInt(part));
+                                }
+                                // 转换端口(大端序)
+                                baos.write((port >> 8) & 0xFF);
+                                baos.write(port & 0xFF);
+                            } catch(Exception e) {
+                                System.out.println("Error encoding peer info: " + e.getMessage());
+                            }
+                            response.put("peers", baos.toByteArray());
+                        } else {
+                            // 非紧凑格式
+                            List<Map<String, Object>> peerList = new ArrayList<>();
+                            Map<String, Object> peerMap = new HashMap<>();
+                            peerMap.put("peer id", peerId);
+                            peerMap.put("ip", ipAddress);
+                            peerMap.put("port", port);
+                            peerList.add(peerMap);
+                            response.put("peers", peerList);
+                        }
+
+                        return BencodeCodec.encode(response);
+
+                    }
+
+                    if(status.equals("downloading")) {
+                        System.out.println("Torrent is being downloaded, checking for existing peers...");
+
+                        if(user.getDownloaded() > Constants.DOWNLOAD_EXEMPTION_BYTES){
+                            if(user.getShareRatio() < Constants.MIN_SHARE_RATIO_THRESHOLD) {
+                                return BencodeCodec.encode(Map.of("failure reason", "Share ratio too low"));
+                            }
+                        }
+
+                        List<PeerInfoEntity> peers = peerInfoEntityRepository.findSeedingPeersByInfoHash(infoHash);
+
+                        for(PeerInfoEntity peer : peers) {
+                            System.out.println("Peer: " + peer.getPeerId() + ", IP: " + peer.getIp() + ", Port: " + peer.getPort());
+                        }
+
+                        String peerId = (String)params.get("peer_id");
+                        System.out.println("Decoded peer_id: " + peerId);
+                        int port = (int) params.get("port");
+                        System.out.println("Port: " + port);
+                        if(peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).isEmpty()) {
+                            PeerInfoEntity peer = new PeerInfoEntity(ipAddress, port, peerId);
+                            peer.setInfoHash(infoHash);
+                            peer.setStatus(status);
+                            peer.setIsActive(1); // 默认活跃状态
+                            peer.setUploaded(0);
+                            peer.setDownloaded(0);
+                            peer.setPeerId((String) params.get("peer_id"));
+                            peer.setLeft((long)params.get("left"));
+                            peerInfoEntityRepository.save(peer);
+                        }
+                        else {
+                            System.out.println("Peer already exists for info_hash: " + infoHash);
+                            peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).get(0).setStatus("seeding");
+                        }
+
+                        if (peers.isEmpty()) {
+                            return BencodeCodec.encode(Map.of("failure reason", "Torrent is not being seeded yet"));
+                        }
+
+                        System.out.println("solve download, compact = " + compact);
+
+                        // 构建正确的响应
+                        Map<String, Object> response = new HashMap<>();
+                        // 添加基本信息
+                        response.put("interval", 1800);
+                        // 客户端应该多久再次请求
+                        response.put("min interval", 900);
+                        // 最小请求间隔
+                        response.put("complete", peers.size());
+                        // 种子数response.put("incomplete", 0);
+                        // 下载者数(这里只返回种子)
+                        // 构建peers列表
+                        if (compact == 1) {
+                            // 紧凑格式 - 每个peer用6字节表示 (4字节IP + 2字节端口)
+                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                            for (PeerInfoEntity tmpPeer : peers) {
+                                try {
+                                    // 转换IP地址
+                                    String[] ipParts = tmpPeer.getIp().split("\\.");
+                                    for (String part : ipParts) {
+                                        baos.write(Integer.parseInt(part));
+                                    }
+                                    // 转换端口(大端序)
+                                    int tmpPeerPort = tmpPeer.getPort();
+                                    baos.write((tmpPeerPort >> 8) & 0xFF);
+                                    baos.write(tmpPeerPort & 0xFF);
+                                } catch (Exception e) {
+                                    System.out.println(e.getMessage());
+                                }
+                            }
+                            response.put("peers", baos.toByteArray());
+                            System.out.println(response);
+                        } else {
+                            // 非紧凑格式 - 每个peer是一个字典
+                            List<Map<String, Object>> peerList = new ArrayList<>();
+                            for (PeerInfoEntity tmpPeer : peers) {
+                                Map<String, Object> peerMap = new HashMap<>();
+                                peerMap.put("peer id", tmpPeer.getPeerId());
+                                peerMap.put("ip", tmpPeer.getIp());
+                                peerMap.put("port", tmpPeer.getPort());
+                                peerList.add(peerMap);
+                            }
+                            response.put("peers", peerList);
+                        }
+                        System.out.println(BencodeCodec.encode(response));
+
+                        return BencodeCodec.encode(response);
+                    }
+                    break;
+                case "stopped":
+                    System.out.println("Event: stopped");
+
+                    String peerId = (String) params.get("peer_id");
+                    PeerInfoEntity peer = peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, peerId).get(0);
+                    peerInfoEntityRepository.delete(peer); // 删除该peer信息
+
+                    if(peerInfoEntityRepository.findByInfoHash(infoHash).isEmpty()) {
+                        torrentMetaRepository.deleteByInfoHash(infoHash); // 如果没有其他peer,删除种子信息
+                    }
+
+                    // 停止事件,通常不需要返回数据
+                    Map<String, Object> response = new HashMap<>();
+                    // 添加基本信息
+                    response.put("interval", 1800);
+                    // 客户端应该多久再次请求
+                    response.put("min interval", 900);
+                    System.out.println("solve stop");
+
+                    return BencodeCodec.encode(response);
+                case "completed":
+                    System.out.println("Event: completed");
+
+                    PeerInfoEntity complete_peer = peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).get(0);
+
+                    Map<String, Object> complete_response = new HashMap<>();
+                    // 添加基本信息
+                    complete_response.put("interval", 1800);
+                    // 客户端应该多久再次请求
+                    complete_response.put("min interval", 900);
+
+                    complete_response.put("complete", peerInfoEntityRepository.findByInfoHash(infoHash).size()); // 种子数
+                    complete_response.put("incomplete", peerInfoEntityRepository.findDownloadingPeersByInfoHash(infoHash).size()); // 下载者数
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    for (PeerInfoEntity tmpPeer : peerInfoEntityRepository.findSeedingPeersByInfoHash(infoHash)) {
+                        try {
+                            // 转换IP地址
+                            String[] ipParts = tmpPeer.getIp().split("\\.");
+                            for (String part : ipParts) {
+                                baos.write(Integer.parseInt(part));
+                            }
+                            // 转换端口(大端序)
+                            int tmpPeerPort = tmpPeer.getPort();
+                            baos.write((tmpPeerPort >> 8) & 0xFF);
+                            baos.write(tmpPeerPort & 0xFF);
+                        } catch (Exception e) {
+                            System.out.println(e.getMessage());
+                        }
+                    }
+                    complete_response.put("peers", baos.toByteArray());
+                    System.out.println("solve complete");
+                    if(complete_peer.getStatus().equals("downloading")) {
+                        user.setDownloaded(user.getDownloaded() + (long)params.get("downloaded"));
+                        complete_peer.setStatus("seeding");
+                        peerInfoEntityRepository.save(complete_peer);
+                    }
+                    if(complete_peer.getStatus().equals("seeding")) {
+                        user.setUploaded(user.getUploaded() + (long)params.get("uploaded"));
+                    }
+
+                    user.setShareRatio(user.getUploaded() / (double) Math.max(user.getDownloaded(), 1));
+
+                    userRepository.save(user); // 保存用户信息
+
+                    return BencodeCodec.encode(complete_response);
+                default:
+                    System.out.println(event);
+                    System.out.println("Event: unknown or not specified");
+
+                    PeerInfoEntity other_peer = peerInfoEntityRepository.findByInfoHashAndPeerId(infoHash, (String) params.get("peer_id")).get(0);
+
+                    Map<String, Object> other_response = new HashMap<>();
+
+                    // 添加基本信息
+                    other_response.put("interval", 1800);
+                    // 客户端应该多久再次请求
+                    other_response.put("min interval", 900);
+
+                    other_response.put("complete", peerInfoEntityRepository.findByInfoHash(infoHash).size()); // 种子数
+                    other_response.put("incomplete", peerInfoEntityRepository.findDownloadingPeersByInfoHash(infoHash).size()); // 下载者数
+
+                    ByteArrayOutputStream other_baos = new ByteArrayOutputStream();
+                    for (PeerInfoEntity tmpPeer : peerInfoEntityRepository.findSeedingPeersByInfoHash(infoHash)) {
+                        try {
+                            // 转换IP地址
+                            String[] ipParts = tmpPeer.getIp().split("\\.");
+                            for (String part : ipParts) {
+                                other_baos.write(Integer.parseInt(part));
+                            }
+                            // 转换端口(大端序)
+                            int tmpPeerPort = tmpPeer.getPort();
+                            other_baos.write((tmpPeerPort >> 8) & 0xFF);
+                            other_baos.write(tmpPeerPort & 0xFF);
+                        } catch (Exception e) {
+                            System.out.println(e.getMessage());
+                        }
+                    }
+                    other_response.put("peers", other_baos.toByteArray());
+                    if(other_peer.getStatus().equals("downloading")) {
+                        user.setDownloaded(user.getDownloaded() + (long)params.get("downloaded"));
+                    }
+                    if(other_peer.getStatus().equals("seeding")) {
+                        user.setUploaded(user.getUploaded() + (long)params.get("uploaded"));
+                    }
+
+                    user.setShareRatio(user.getUploaded() / (double) Math.max(user.getDownloaded(), 1));
+
+                    userRepository.save(user); // 保存用户信息
+                    return BencodeCodec.encode(other_response);
             }
 
-            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);
+            return BencodeCodec.encode(Map.of("failure reason", "Event not handled"));
         } catch (Exception e) {
             e.printStackTrace();  // 打印异常堆栈,方便定位错误
             return BencodeCodec.encode(Map.of("failure reason", "Internal server error"));
         }
     }
 
-    // 辅助函数:打印字节转成16进制字符串
-    private String bytesToHex(byte[] bytes) {
-        StringBuilder sb = new StringBuilder();
-        for (byte b : bytes) {
-            sb.append(String.format("%02x", b));
-        }
-        return sb.toString();
-    }
-
-    // decodeParam 建议用ISO-8859-1解码
-    private String decodeParam(String param) throws UnsupportedEncodingException {
-        return URLDecoder.decode(param, "ISO-8859-1");
-    }
 
 
-    private static class PeerInfo {
-        String ip;
-        int port;
-        String peerId;
-
-        public PeerInfo(String ip, int port, String peerId) {
-            this.ip = ip;
-            this.port = port;
-            this.peerId = peerId;
-        }
-    }
 }
 
diff --git a/src/main/java/com/pt/utils/TorrentPasskeyModifier.java b/src/main/java/com/pt/utils/TorrentPasskeyModifier.java
new file mode 100644
index 0000000..dc5a82c
--- /dev/null
+++ b/src/main/java/com/pt/utils/TorrentPasskeyModifier.java
@@ -0,0 +1,218 @@
+package com.pt.utils;
+
+import java.io.*;
+import java.util.*;
+
+public class TorrentPasskeyModifier {
+
+    private byte[] infoValueBytes; // 保存 info 的 value 原始 bencode
+
+    public byte[] analyzeTorrentFile(byte[] fileBytes, String username) throws IOException {
+        ByteArrayInputStream in = new ByteArrayInputStream(fileBytes);
+        Map<String, Object> torrentMap = decodeWithInfoPreservation(in);
+
+        // 修改 announce
+        if (torrentMap.containsKey("announce")) {
+            String announce = (String) torrentMap.get("announce");
+            torrentMap.put("announce", replacePasskeyInUrl(announce, username));
+        }
+
+        // 修改 announce-list
+        if (torrentMap.containsKey("announce-list")) {
+            Object list = torrentMap.get("announce-list");
+            if (list instanceof List) {
+                replacePasskeyInAnnounceList((List<?>) list, username);
+            }
+        }
+
+        // 编码为新 torrent 文件
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write('d');
+
+        List<String> keys = new ArrayList<>(torrentMap.keySet());
+        Collections.sort(keys);
+        for (String key : keys) {
+            encodeString(key, out);
+            if ("info".equals(key)) {
+                out.write(infoValueBytes); // 写入原始 info 的 value bencode
+            } else {
+                encode(torrentMap.get(key), out);
+            }
+        }
+
+        out.write('e');
+        return out.toByteArray();
+    }
+
+    private String replacePasskeyInUrl(String url, String username) {
+        if (url == null) return null;
+        if (url.contains("passkey=")) {
+            return url.replaceAll("(?<=passkey=)[^&]*", username);
+        } else {
+            return url.contains("?")
+                    ? url + "&passkey=" + username
+                    : url + "?passkey=" + username;
+        }
+    }
+
+    private void replacePasskeyInAnnounceList(List<?> list, String username) {
+        for (Object tierObj : list) {
+            if (tierObj instanceof List) {
+                List<Object> tier = (List<Object>) tierObj;
+                for (int i = 0; i < tier.size(); i++) {
+                    Object url = tier.get(i);
+                    if (url instanceof String) {
+                        tier.set(i, replacePasskeyInUrl((String) url, username));
+                    }
+                }
+            }
+        }
+    }
+
+    // --- Bencode 解码并提取原始 info 字典字节 ---
+    private Map<String, Object> decodeWithInfoPreservation(InputStream in) throws IOException {
+        if (in.read() != 'd') throw new IOException("Not a bencode dict");
+        Map<String, Object> map = new LinkedHashMap<>();
+        while (true) {
+            int c = in.read();
+            if (c == 'e') break;
+            if (c == -1) throw new EOFException();
+
+            String key = decodeString(in, (char) c);
+            if ("info".equals(key)) {
+                ByteArrayOutputStream infoOut = new ByteArrayOutputStream();
+                in.mark(1);
+                int b = in.read();
+                if (b != 'd') throw new IOException("Invalid info dict");
+                infoOut.write(b);
+                int depth = 1;
+
+                while (depth > 0) {
+                    b = in.read();
+                    if (b == -1) throw new IOException("Unexpected EOF in info");
+                    infoOut.write(b);
+
+                    if (b == 'd' || b == 'l') depth++;
+                    else if (b == 'e') depth--;
+                    else if (b >= '0' && b <= '9') {
+                        int len = b - '0';
+                        while (true) {
+                            int nc = in.read();
+                            infoOut.write(nc);
+                            if (nc == ':') break;
+                            len = len * 10 + (nc - '0');
+                        }
+                        for (int i = 0; i < len; i++) {
+                            infoOut.write(in.read());
+                        }
+                    } else if (b == 'i') {
+                        while (true) {
+                            int nc = in.read();
+                            infoOut.write(nc);
+                            if (nc == 'e') break;
+                        }
+                    }
+                }
+
+                this.infoValueBytes = infoOut.toByteArray();
+                map.put("info", null); // 占位
+            } else {
+                map.put(key, decode(in));
+            }
+        }
+        return map;
+    }
+
+    private Object decode(InputStream in) throws IOException {
+        int c = in.read();
+        if (c == -1) throw new EOFException();
+        if (c == 'd') return decodeDict(in);
+        if (c == 'l') return decodeList(in);
+        if (c == 'i') return decodeInt(in);
+        if (c >= '0' && c <= '9') return decodeString(in, (char) c);
+        throw new IOException("Invalid bencode start: " + (char) c);
+    }
+
+    private Map<String, Object> decodeDict(InputStream in) throws IOException {
+        Map<String, Object> map = new LinkedHashMap<>();
+        while (true) {
+            int c = in.read();
+            if (c == 'e') break;
+            if (c == -1) throw new EOFException();
+            String key = decodeString(in, (char) c);
+            map.put(key, decode(in));
+        }
+        return map;
+    }
+
+    private List<Object> decodeList(InputStream in) throws IOException {
+        List<Object> list = new ArrayList<>();
+        while (true) {
+            int c = in.read();
+            if (c == 'e') break;
+            if (c == -1) throw new EOFException();
+            in.reset();
+            list.add(decode(in));
+        }
+        return list;
+    }
+
+    private String decodeString(InputStream in, char firstDigit) throws IOException {
+        StringBuilder sb = new StringBuilder();
+        sb.append(firstDigit);
+        while (true) {
+            int c = in.read();
+            if (c == ':') break;
+            sb.append((char) c);
+        }
+        int len = Integer.parseInt(sb.toString());
+        byte[] buf = new byte[len];
+        if (in.read(buf) != len) throw new EOFException();
+        return new String(buf, "UTF-8");
+    }
+
+    private Long decodeInt(InputStream in) throws IOException {
+        StringBuilder sb = new StringBuilder();
+        while (true) {
+            int c = in.read();
+            if (c == 'e') break;
+            sb.append((char) c);
+        }
+        return Long.parseLong(sb.toString());
+    }
+
+    // --- 编码 ---
+    private void encode(Object obj, OutputStream out) throws IOException {
+        if (obj instanceof String) encodeString((String) obj, out);
+        else if (obj instanceof Integer || obj instanceof Long) {
+            out.write('i');
+            out.write(obj.toString().getBytes());
+            out.write('e');
+        } else if (obj instanceof List) {
+            out.write('l');
+            for (Object item : (List<?>) obj) encode(item, out);
+            out.write('e');
+        } else if (obj instanceof Map) {
+            out.write('d');
+            Map<String, Object> map = (Map<String, Object>) obj;
+            List<String> keys = new ArrayList<>(map.keySet());
+            Collections.sort(keys);
+            for (String key : keys) {
+                encodeString(key, out);
+                encode(map.get(key), out);
+            }
+            out.write('e');
+        } else if (obj == null) {
+            // 跳过
+        } else {
+            throw new IOException("Unsupported type: " + obj.getClass());
+        }
+    }
+
+    private void encodeString(String str, OutputStream out) throws IOException {
+        byte[] bytes = str.getBytes("UTF-8");
+        out.write(Integer.toString(bytes.length).getBytes());
+        out.write(':');
+        out.write(bytes);
+    }
+}