Merge "修改torrent返回值"
diff --git a/src/main/java/com/example/g8backend/controller/AuthController.java b/src/main/java/com/example/g8backend/controller/AuthController.java
index b68eff8..6cb1ae8 100644
--- a/src/main/java/com/example/g8backend/controller/AuthController.java
+++ b/src/main/java/com/example/g8backend/controller/AuthController.java
@@ -65,10 +65,10 @@
 //            return ApiResponse.error(400, "邀请码错误");
 //        }
 
-        Object cachedCode = redisTemplate.opsForValue().get(registerDTO.getEmail());
-        if (!registerDTO.getVerificationCode().equals(cachedCode)) {
-            return ApiResponse.error(400, "验证码错误");
-        }
+//        Object cachedCode = redisTemplate.opsForValue().get(registerDTO.getEmail());
+//        if (!registerDTO.getVerificationCode().equals(cachedCode)) {
+//            return ApiResponse.error(400, "验证码错误");
+//        }
 
         redisTemplate.delete(registerDTO.getEmail());
 
diff --git a/src/main/java/com/example/g8backend/controller/TrackerController.java b/src/main/java/com/example/g8backend/controller/TrackerController.java
index 8776e36..ace7067 100644
--- a/src/main/java/com/example/g8backend/controller/TrackerController.java
+++ b/src/main/java/com/example/g8backend/controller/TrackerController.java
@@ -2,6 +2,7 @@
 
 import com.example.g8backend.dto.AnnounceRequestDTO;
 import com.example.g8backend.dto.AnnounceResponseDTO;
+import com.example.g8backend.util.BencodeUtil;
 import jakarta.servlet.http.HttpServletRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -14,11 +15,13 @@
 @RequestMapping("/tracker")
 public class TrackerController {
 
+    private static final Logger logger = LoggerFactory.getLogger(TrackerController.class);
+
     @Autowired
     private ITrackerService trackerService;
 
-    @GetMapping("/announce/{passkey}")
-    public ResponseEntity<AnnounceResponseDTO> getAnnouncements(
+    @GetMapping(value = "/announce/{passkey}", produces = "application/x-bittorrent")
+    public ResponseEntity<byte[]> getAnnouncements(
             HttpServletRequest request,
             @RequestParam("info_hash") String infoHash,
             @RequestParam("peer_id") String peerId,
@@ -30,7 +33,8 @@
             @RequestParam(value = "compact", required = false) Integer compact,
             @PathVariable String passkey) {
 
-        Logger logger = LoggerFactory.getLogger(this.getClass());
+        logger.info("Announce request received: info_hash={}, peer_id={}, port={}, uploaded={}, downloaded={}, event={}, left={}, compact={}, passkey={}",
+                infoHash, peerId, port, uploaded, downloaded, event, left, compact, passkey);
 
         AnnounceRequestDTO requestDTO = new AnnounceRequestDTO();
         requestDTO.setPasskey(passkey);
@@ -40,22 +44,29 @@
         requestDTO.setUploaded(uploaded);
         requestDTO.setDownloaded(downloaded);
         requestDTO.setEvent(event);
-        requestDTO.setLeft(left);
+        requestDTO.setLeft(left != null ? left : 0.0);
         requestDTO.setCompact(compact);
 
-        logger.info(requestDTO.toString());
+        String ipAddress = extractClientIp(request);
+        requestDTO.setIp(ipAddress);
 
-        String ipAddress = request.getHeader("X-Forwarded-For");
-        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
-            ipAddress = request.getHeader("Proxy-Client-IP");
+        AnnounceResponseDTO responseDTO = trackerService.handleAnnounce(requestDTO);
+
+        byte[] bencoded = BencodeUtil.encodeAnnounceResponse(responseDTO);
+        return ResponseEntity
+                .ok()
+                .header("Content-Type", "application/x-bittorrent")
+                .body(bencoded);
+    }
+
+    /**
+     * 提取客户端 IP,支持 X-Forwarded-For
+     */
+    private String extractClientIp(HttpServletRequest request) {
+        String xfHeader = request.getHeader("X-Forwarded-For");
+        if (xfHeader != null && !xfHeader.isEmpty()) {
+            return xfHeader.split(",")[0].trim();
         }
-        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
-            ipAddress = request.getHeader("WL-Proxy-Client-IP");
-        }
-        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
-            ipAddress = request.getRemoteAddr();
-        }
-        requestDTO.setIp(ipAddress.split(",")[0]);
-        return ResponseEntity.ok(trackerService.handleAnnounce(requestDTO));
+        return request.getRemoteAddr();
     }
 }
diff --git a/src/main/java/com/example/g8backend/dto/AnnounceRequestDTO.java b/src/main/java/com/example/g8backend/dto/AnnounceRequestDTO.java
index e54f06d..6a81097 100644
--- a/src/main/java/com/example/g8backend/dto/AnnounceRequestDTO.java
+++ b/src/main/java/com/example/g8backend/dto/AnnounceRequestDTO.java
@@ -10,8 +10,12 @@
     private int port;
     private double uploaded;
     private double downloaded;
-    private double left;
-    private Integer compact; // 可选
-    private String event;    // 可选
+    private Double left;      // 改成 Double,允许为 null
+    private Integer compact;  // 可选
+    private String event;     // 可选
     private String ip;
+
+    public double safeLeft() {
+        return this.left != null ? this.left : 0.0;
+    }
 }
diff --git a/src/main/java/com/example/g8backend/service/impl/TorrentServiceImpl.java b/src/main/java/com/example/g8backend/service/impl/TorrentServiceImpl.java
index d540528..17561b6 100644
--- a/src/main/java/com/example/g8backend/service/impl/TorrentServiceImpl.java
+++ b/src/main/java/com/example/g8backend/service/impl/TorrentServiceImpl.java
@@ -24,7 +24,8 @@
     @Resource
     private IUserStatsService userStatsService;
 
-    private final String tracker = "http://127.0.0.1:8080/tracker/announce/";
+    private final String tracker = "http://localhost:8080/tracker/announce/";
+    //private final String tracker = "http://127.0.0.1:8080/tracker/announce/";
 
     @Override
     public Torrent handleTorrentUpload(File file, String fileName, Long userId, String passkey) throws IOException {
diff --git a/src/main/java/com/example/g8backend/util/BencodeUtil.java b/src/main/java/com/example/g8backend/util/BencodeUtil.java
new file mode 100644
index 0000000..4ec9ce1
--- /dev/null
+++ b/src/main/java/com/example/g8backend/util/BencodeUtil.java
@@ -0,0 +1,49 @@
+package com.example.g8backend.util;
+
+import com.example.g8backend.dto.AnnounceResponseDTO;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+public class BencodeUtil {
+    public static byte[] encodeAnnounceResponse(AnnounceResponseDTO dto) {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        try {
+            out.write('d'); // dictionary start
+
+            writeString(out, "interval");
+            writeInt(out, dto.getInterval());
+
+            writeString(out, "peers");
+            out.write('l'); // list start
+            for (Map<String, Object> peer : dto.getPeers()) {
+                out.write('d');
+                writeString(out, "ip");
+                writeString(out, (String) peer.get("ip"));
+                writeString(out, "port");
+                writeInt(out, ((Number) peer.get("port")).intValue());
+                out.write('e');
+            }
+            out.write('e'); // list end
+
+            out.write('e'); // dictionary end
+        } catch (Exception e) {
+            throw new RuntimeException("Bencoding failed", e);
+        }
+        return out.toByteArray();
+    }
+
+    private static void writeString(ByteArrayOutputStream out, String str) throws Exception {
+        byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
+        out.write(String.valueOf(bytes.length).getBytes(StandardCharsets.UTF_8));
+        out.write(':');
+        out.write(bytes);
+    }
+
+    private static void writeInt(ByteArrayOutputStream out, int i) throws Exception {
+        out.write('i');
+        out.write(String.valueOf(i).getBytes(StandardCharsets.UTF_8));
+        out.write('e');
+    }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 657e616..99eaab2 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,16 +1,19 @@
 spring.datasource.password=G812345678#
 spring.datasource.username=team8
 spring.datasource.url=jdbc:mysql://202.205.102.121:3306/g8backend
-spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
-spring.sql.init.mode=always
+#spring.datasource.password=12345678
+#spring.datasource.username=root
+#spring.datasource.url=jdbc:mysql://127.0.0.1:3306/g8backend
+#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+#spring.sql.init.mode=always
 #logging.level.root=DEBUG
 
 mybatis-plus.mapper-locations=classpath*:/mapper/**/*.xml
 
-//spring.data.redis.host = 127.0.0.1
+spring.data.redis.host = 127.0.0.1
 
-spring.data.redis.host = redis-server
-spring.data.redis.port = 6380
+//spring.data.redis.host = redis-server
+spring.data.redis.port = 6379
 
 spring.mail.host=smtp.qq.com
 spring.mail.port=465