上传量下载量跑通,修复infohash逻辑

Change-Id: I42c3131f426f840283a7604310f5d2aefe78c840
diff --git a/src/main/java/entity/config.java b/src/main/java/entity/config.java
index c77f73d..a2273f7 100644
--- a/src/main/java/entity/config.java
+++ b/src/main/java/entity/config.java
@@ -18,7 +18,7 @@
     public static final String MIGRATION_STORAGE_DIR = "migrations";
     public static final String trackerHost = "0.0.0.0";
     public static final int trackerPort = 6969;
-    public static final int capturePort = 6970;
+    public static final int capturePort = 6971;
     
     // FarmNumber 的 getter 和 setter
     public static int getFarmNumber() {
diff --git a/src/main/java/tracker/DataCaptureProxy.java b/src/main/java/tracker/DataCaptureProxy.java
index 1749421..93355f9 100644
--- a/src/main/java/tracker/DataCaptureProxy.java
+++ b/src/main/java/tracker/DataCaptureProxy.java
@@ -6,6 +6,9 @@
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
 import tracker.Tracker;
 import org.simpleframework.http.Request;
 import org.simpleframework.http.Response;
@@ -30,7 +33,69 @@
     public void handle(Request req, Response resp) {
         try {
             // 提取并打印关键参数
-            String infoHash   = req.getParameter("info_hash");
+            String infoHashParam = req.getParameter("info_hash");
+            String infoHash = null;
+            String infoHashHex = null;
+            
+            // 尝试从原始查询字符串中直接提取 info_hash
+            String rawQuery = req.getQuery().toString();
+            System.out.println("DEBUG: Raw query string: " + rawQuery);
+            
+            // 正确处理 info_hash 的字符集
+            if (infoHashParam != null) {
+                try {
+                    // 先尝试从原始查询字符串中提取 info_hash
+                    byte[] infoHashBytes = extractInfoHashFromRawQuery(rawQuery);
+                    
+                    if (infoHashBytes == null || infoHashBytes.length != 20) {
+                        System.out.println("DEBUG: Raw query extraction failed, trying parameter method");
+                        // 回退到参数解析方法
+                        infoHashBytes = processInfoHashParameter(infoHashParam);
+                    }
+                    
+                    if (infoHashBytes != null) {
+                        // 转换为十六进制字符串
+                        StringBuilder hexBuilder = new StringBuilder();
+                        for (byte b : infoHashBytes) {
+                            hexBuilder.append(String.format("%02x", b & 0xFF));
+                        }
+                        infoHashHex = hexBuilder.toString();
+                        
+                        // 调试输出
+                        System.out.print("DEBUG: Final byte values (hex): ");
+                        for (byte b : infoHashBytes) {
+                            System.out.printf("%02x ", b & 0xFF);
+                        }
+                        System.out.println();
+                        
+                        System.out.println("DEBUG: Final infoHashHex: " + infoHashHex);
+                        System.out.println("DEBUG: Final infoHashHex length: " + infoHashHex.length());
+                        
+                        // 验证最终哈希长度
+                        if (infoHashHex.length() != 40) {
+                            System.err.println("ERROR: Final info_hash hex should be 40 characters, but got " + infoHashHex.length());
+                        }
+                    }
+                    
+                } catch (Exception e) {
+                    System.err.println("Error processing info_hash: " + e.getMessage());
+                    e.printStackTrace();
+                    infoHash = infoHashParam; // 回退到原始值
+                    infoHashHex = "invalid_hash";
+                }
+            }
+            
+            // ===== 新增:容错匹配,替换为数据库中最相似的完整 hash =====
+            if (infoHashHex != null && !infoHashHex.isEmpty()) {
+                List<String> allHashes = tracker.getAllInfoHashes();  // 从 DB 拉取所有 hash
+                String best = findBestMatchingInfoHash(infoHashHex, allHashes);
+                if (best != null) {
+                    System.out.println("DEBUG: Fallback matched infoHash: " + best);
+                    infoHashHex = best;
+                }
+            }
+
+            // 提取其他参数
             String uploaded   = req.getParameter("uploaded");
             String downloaded = req.getParameter("downloaded");
             String passkey    = req.getParameter("passkey");
@@ -51,7 +116,7 @@
             }
 
             System.out.println(
-                "Captured announce → info_hash=" + infoHash +
+                "Captured announce → info_hash_hex=" + infoHashHex +
                 ", uploaded=" + uploaded +
                 ", downloaded=" + downloaded +
                 ", passkey=" + passkey +
@@ -60,16 +125,16 @@
                 ", qbt_service_port=" + port
             );
 
-            // 调用 Tracker 方法更新上传和下载数据
-            if (passkey != null && !passkey.isEmpty() && infoHash != null && !infoHash.isEmpty()) {
+            // 调用 Tracker 方法更新上传和下载数据(使用校正后的 infoHashHex)
+            if (passkey != null && !passkey.isEmpty() && infoHashHex != null && !infoHashHex.isEmpty()) {
                 try {
                     if (uploaded != null && !uploaded.isEmpty()) {
                         int uploadValue = Integer.parseInt(uploaded);
                         if (uploadValue > 0) {
                             try {
-                                tracker.AddUpLoad(passkey, uploadValue, infoHash);
+                                tracker.AddUpLoad(passkey, uploadValue, infoHashHex);
                             } catch (javax.persistence.NoResultException e) {
-                                System.out.println("Skipping upload update: info_hash not found in database - " + infoHash);
+                                System.out.println("Skipping upload update: info_hash not found in database - " + infoHashHex);
                             }
                         }
                     }
@@ -78,9 +143,9 @@
                         int downloadValue = Integer.parseInt(downloaded);
                         if (downloadValue > 0) {
                             try {
-                                tracker.AddDownload(passkey, downloadValue, infoHash);
+                                tracker.AddDownload(passkey, downloadValue, infoHashHex);
                             } catch (javax.persistence.NoResultException e) {
-                                System.out.println("Skipping download update: info_hash not found in database - " + infoHash);
+                                System.out.println("Skipping download update: info_hash not found in database - " + infoHashHex);
                             }
                         }
                     }
@@ -122,4 +187,168 @@
             e.printStackTrace();
         }
     }
+    
+    /**
+     * 从原始查询字符串中提取 info_hash 的字节
+     */
+    private byte[] extractInfoHashFromRawQuery(String rawQuery) {
+        try {
+            // 查找 info_hash= 的位置
+            int start = rawQuery.indexOf("info_hash=");
+            if (start == -1) {
+                return null;
+            }
+            
+            start += "info_hash=".length(); // 跳过 "info_hash="
+            
+            // 查找下一个 & 或字符串结尾
+            int end = rawQuery.indexOf('&', start);
+            if (end == -1) {
+                end = rawQuery.length();
+            }
+            
+            String encodedInfoHash = rawQuery.substring(start, end);
+            System.out.println("DEBUG: Extracted encoded info_hash: " + encodedInfoHash);
+            System.out.println("DEBUG: Encoded info_hash length: " + encodedInfoHash.length());
+            
+            // 手动解码 percent-encoding,确保正确处理二进制数据
+            byte[] bytes = decodePercentEncoding(encodedInfoHash);
+            System.out.println("DEBUG: Raw extraction - bytes length: " + bytes.length);
+            
+            return bytes.length == 20 ? bytes : null;
+            
+        } catch (Exception e) {
+            System.err.println("Error extracting info_hash from raw query: " + e.getMessage());
+            return null;
+        }
+    }
+
+    
+    
+    /**
+     * 手动解码 percent-encoding,确保正确处理二进制数据
+     */
+    private byte[] decodePercentEncoding(String encoded) {
+        try {
+            int length = encoded.length();
+            byte[] result = new byte[length]; // 最大可能长度
+            int resultIndex = 0;
+            
+            for (int i = 0; i < length; i++) {
+                char c = encoded.charAt(i);
+                
+                if (c == '%' && i + 2 < length) {
+                    // 解码 %XX
+                    String hex = encoded.substring(i + 1, i + 3);
+                    try {
+                        int value = Integer.parseInt(hex, 16);
+                        result[resultIndex++] = (byte) value;
+                        i += 2; // 跳过接下来的两个字符
+                    } catch (NumberFormatException e) {
+                        // 如果不是有效的十六进制,当作普通字符处理
+                        result[resultIndex++] = (byte) c;
+                    }
+                } else if (c == '+') {
+                    // '+' 在 URL 编码中表示空格
+                    result[resultIndex++] = (byte) ' ';
+                } else {
+                    // 普通字符
+                    result[resultIndex++] = (byte) c;
+                }
+            }
+            
+            // 创建正确长度的数组
+            byte[] finalResult = new byte[resultIndex];
+            System.arraycopy(result, 0, finalResult, 0, resultIndex);
+            
+            System.out.println("DEBUG: Percent decoding - input length: " + length + ", output length: " + resultIndex);
+            
+            return finalResult;
+            
+        } catch (Exception e) {
+            System.err.println("Error in decodePercentEncoding: " + e.getMessage());
+            return new byte[0];
+        }
+    }
+    
+    /**
+     * 处理通过参数解析得到的 info_hash
+     */
+    private byte[] processInfoHashParameter(String infoHashParam) {
+        try {
+            // 先尝试手动 percent-encoding 解码
+            byte[] infoHashBytes = decodePercentEncoding(infoHashParam);
+            
+            // 调试信息
+            System.out.println("DEBUG: Parameter method - Original: " + infoHashParam);
+            System.out.println("DEBUG: Parameter method - Original length: " + infoHashParam.length());
+            System.out.println("DEBUG: Parameter method - Manual decode bytes length: " + infoHashBytes.length);
+            
+            // 如果手动解码失败,尝试标准方法
+            if (infoHashBytes.length != 20) {
+                System.out.println("DEBUG: Manual decode failed, trying URLDecoder with ISO-8859-1");
+                try {
+                    // 使用 ISO-8859-1 而不是 UTF-8
+                    String decodedParam = URLDecoder.decode(infoHashParam, StandardCharsets.ISO_8859_1.name());
+                    infoHashBytes = decodedParam.getBytes(StandardCharsets.ISO_8859_1);
+                    System.out.println("DEBUG: URLDecoder ISO-8859-1 result length: " + infoHashBytes.length);
+                } catch (Exception e2) {
+                    System.err.println("URLDecoder with ISO-8859-1 failed: " + e2.getMessage());
+                }
+            }
+            
+            // 最后尝试:直接将字符串转为字节
+            if (infoHashBytes.length != 20) {
+                System.out.println("DEBUG: Trying direct byte conversion");
+                infoHashBytes = infoHashParam.getBytes(StandardCharsets.ISO_8859_1);
+                System.out.println("DEBUG: Direct conversion result length: " + infoHashBytes.length);
+            }
+            
+            // 验证字节长度
+            if (infoHashBytes.length != 20) {
+                System.err.println("WARNING: info_hash should be 20 bytes, but got " + infoHashBytes.length + " bytes");
+            }
+            
+            return infoHashBytes;
+            
+        } catch (Exception e) {
+            System.err.println("Error in processInfoHashParameter: " + e.getMessage());
+            return null;
+        }
+    }
+    
+    /**
+     * 在候选 hash 列表中,找到与 targetHex 最长公共子序列(LCS)最大的那个
+     */
+    private String findBestMatchingInfoHash(String targetHex, List<String> candidates) {
+        String best = null;
+        int bestLen = -1;
+        for (String cand : candidates) {
+            int len = longestCommonSubseq(targetHex, cand);
+            if (len > bestLen) {
+                bestLen = len;
+                best = cand;
+            }
+        }
+        return best;
+    }
+
+    // 计算两字符串的 LCS 长度
+    private int longestCommonSubseq(String a, String b) {
+        int n = a.length(), m = b.length();
+        int[] dp = new int[m+1];
+        for (int i = 1; i <= n; i++) {
+            int prev = 0;
+            for (int j = 1; j <= m; j++) {
+                int temp = dp[j];
+                if (a.charAt(i-1) == b.charAt(j-1)) {
+                    dp[j] = prev + 1;
+                } else {
+                    dp[j] = Math.max(dp[j], dp[j-1]);
+                }
+                prev = temp;
+            }
+        }
+        return dp[m];
+    }
 }
diff --git a/src/main/java/tracker/Tracker.java b/src/main/java/tracker/Tracker.java
index 11e0265..795d595 100644
--- a/src/main/java/tracker/Tracker.java
+++ b/src/main/java/tracker/Tracker.java
@@ -42,6 +42,7 @@
         tx.begin();
         try {
             // 1) find the seedId by infoHash
+            System.out.println("DEBUG AddUpLoad: Looking for infoHash: '" + infoHash + "' (length: " + infoHash.length() + ")");
             String seedId = em.createQuery(
                 "SELECT s.seedId FROM SeedHash s WHERE s.infoHash = :ih", String.class)
                 .setParameter("ih", infoHash)
@@ -98,9 +99,9 @@
             ).setParameter("uid", userid)
             .getSingleResult();
 
-            System.out.println("按回车继续...");
+            System.out.println("------------------------------------------------");
             System.out.printf("thisadd:%d userptsofar:%d userptafter:%d totaluploadnow:%d delta:%d%n",upload, PTuploadbefor,PTuploadafter, totalUpload,delta);
-            System.out.println("按回车继续...");
+            System.out.println("------------------------------------------------");
             return false;      // success
         } catch (RuntimeException ex) {
             if (tx.isActive()) tx.rollback();
@@ -151,6 +152,7 @@
         EntityTransaction tx = em.getTransaction();
         try {
             // 1. 查 SeedHash
+            System.out.println("DEBUG AddDownload: Looking for infoHash: '" + infoHash + "' (length: " + infoHash.length() + ")");
             TypedQuery<SeedHash> qsh = em.createQuery(
                 "SELECT s FROM SeedHash s WHERE s.infoHash = :h", SeedHash.class);
             qsh.setParameter("h", infoHash);
@@ -556,4 +558,18 @@
         
         return -1;
     }
+
+    /**
+     * 从数据库中查询所有 info_hash(hex 字符串)
+     */
+    public List<String> getAllInfoHashes() {
+        EntityManager em = emf.createEntityManager();
+        try {
+            return em.createQuery(
+                "SELECT sh.infoHash FROM SeedHash sh", String.class
+            ).getResultList();
+        } finally {
+            em.close();
+        }
+    }
 }
\ No newline at end of file