增加流量监测和假种检测功能

Change-Id: I808ce14b6f08565f797f4681a6f72db9c730d011
diff --git a/src/main/java/cheat/Cheat.java b/src/main/java/cheat/Cheat.java
index cb35c05..1141c5b 100644
--- a/src/main/java/cheat/Cheat.java
+++ b/src/main/java/cheat/Cheat.java
@@ -4,6 +4,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.HashMap;
 
 import javax.persistence.EntityManager;
 import javax.persistence.EntityManagerFactory;
@@ -20,6 +21,7 @@
 
 import com.querydsl.jpa.impl.JPAQueryFactory;
 
+import entity.Seed;
 import entity.Appeal;
 import entity.QAppeal;
 import entity.QSeed;
@@ -27,6 +29,8 @@
 import entity.config;
 import entity.QUser;
 import entity.TTorent;
+import entity.QTransRecord;
+import entity.TransRecord;
 
 public class Cheat implements CheatInterfnterface {
 
@@ -180,50 +184,230 @@
 
     @Override
     public void DetectTrans() {
-        // JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
-        // QSeed qSeed = QSeed.seed;
-        // List<Seed> seeds = queryFactory
-        //     .selectFrom(qSeed)
-        //     .fetch();
-        // for (Seed seed : seeds) {
-        //     QTransRecord qTransRecord = QTransRecord.transRecord;
-        //     List<TransRecord> tasks = queryFactory
-        //         .selectFrom(qTransRecord)
-        //         .where(qTransRecord.seedid.eq(seed.seedid))
-        //         .fetch();
+        EntityManager entityManager = emf.createEntityManager();
+        try {
+            JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
+            QSeed qSeed = QSeed.seed;
+            QTransRecord qTransRecord = QTransRecord.transRecord;
+            
+            // 获取所有种子
+            List<Seed> seeds = queryFactory
+                .selectFrom(qSeed)
+                .fetch();
 
-        //     int n = tasks.size();
-        //     if (n == 0) continue;
+            // 存储每个种子的delta值
+            List<Double> allDeltas = new ArrayList<>();
+            Map<String, Double> seedDeltaMap = new HashMap<>();
+            
+            // 计算每个种子的delta值(只考虑account_status不为1的用户)
+            for (Seed seed : seeds) {
+                List<TransRecord> tasks = queryFactory
+                    .selectFrom(qTransRecord)
+                    .leftJoin(qTransRecord.uploader).fetchJoin()
+                    .leftJoin(qTransRecord.downloader).fetchJoin()
+                    .where(qTransRecord.seedid.eq(seed.seedid))
+                    .fetch();
 
-        //     double[] xArr = new double[n];
-        //     for (int i = 0; i < n; i++) {
-        //         TransRecord t = tasks.get(i);
-        //         xArr[i] = Math.max(0, t.upload - t.download);
-        //     }
+                // 过滤掉account_status为1的用户的记录
+                List<TransRecord> validTasks = new ArrayList<>();
+                for (TransRecord task : tasks) {
+                    boolean shouldInclude = true;
+                    
+                    // 检查上传者
+                    if (task.uploaduserid != null) {
+                        User uploader = entityManager.find(User.class, task.uploaduserid);
+                        if (uploader != null && uploader.accountstate) {
+                            shouldInclude = false;
+                        }
+                    }
+                    
+                    // 检查下载者
+                    if (task.downloaduserid != null) {
+                        User downloader = entityManager.find(User.class, task.downloaduserid);
+                        if (downloader != null && downloader.accountstate) {
+                            shouldInclude = false;
+                        }
+                    }
+                    
+                    if (shouldInclude) {
+                        validTasks.add(task);
+                    }
+                }
 
-        //     double sum = 0;
-        //     for (double x : xArr) sum += x;
-        //     double mu = sum / n;
+                if (validTasks.isEmpty()) continue;
 
-        //     double sqSum = 0;
-        //     for (double x : xArr) sqSum += (x - mu) * (x - mu);
-        //     double sigma = Math.sqrt(sqSum / n);
+                // 计算该种子的总delta
+                double totalDelta = 0;
+                for (TransRecord task : validTasks) {
+                    long upload = task.upload != null ? task.upload : 0L;
+                    long download = task.download != null ? task.download : 0L;
+                    totalDelta += (upload - download);
+                }
 
-        //     for (int i = 0; i < n; i++) {
-        //         if (Math.abs(xArr[i] - mu) > 3 * sigma) {
-        //             User user = entityManager.find(User.class, tasks.get(i).downloaduserid);
-        //             if (user != null) {
-        //                 user.detectedCount++;
-        //                 user.lastDetectedTime = new java.util.Date();
-        //                 entityManager.merge(user);
-        //             }
-        //         }
-        //     }
-        // }
+                allDeltas.add(totalDelta);
+                seedDeltaMap.put(seed.seedid, totalDelta);
+            }
+
+            if (allDeltas.size() < 2) return; // 需要至少2个种子才能计算标准差
+
+            // 计算平均值和标准差
+            double sum = 0;
+            for (double delta : allDeltas) {
+                sum += delta;
+            }
+            double mean = sum / allDeltas.size();
+
+            double variance = 0;
+            for (double delta : allDeltas) {
+                variance += Math.pow(delta - mean, 2);
+            }
+            double stdDev = Math.sqrt(variance / allDeltas.size());
+
+            // 检测异常种子并处理参与者
+            for (Seed seed : seeds) {
+                Double seedDelta = seedDeltaMap.get(seed.seedid);
+                if (seedDelta == null) continue;
+
+                // 检查是否偏离1个标准差
+                if (Math.abs(seedDelta - mean) > stdDev) {
+                    // 获取该种子的所有传输记录参与者
+                    List<TransRecord> suspiciousTasks = queryFactory
+                        .selectFrom(qTransRecord)
+                        .where(qTransRecord.seedid.eq(seed.seedid))
+                        .fetch();
+
+                    // 更新所有参与者的detectedCount
+                    for (TransRecord task : suspiciousTasks) {
+                        // 处理上传者
+                        if (task.uploaduserid != null) {
+                            User uploader = entityManager.find(User.class, task.uploaduserid);
+                            if (uploader != null && !uploader.accountstate) {
+                                uploader.detectedCount++;
+                                uploader.lastDetectedTime = new java.util.Date();
+                                entityManager.merge(uploader);
+                            }
+                        }
+                        
+                        // 处理下载者
+                        if (task.downloaduserid != null) {
+                            User downloader = entityManager.find(User.class, task.downloaduserid);
+                            if (downloader != null && !downloader.accountstate) {
+                                downloader.detectedCount++;
+                                downloader.lastDetectedTime = new java.util.Date();
+                                entityManager.merge(downloader);
+                            }
+                        }
+                    }
+                }
+            }
+        } finally {
+            if (entityManager != null) {
+                entityManager.close();
+            }
+        }
     }
 
     @Override
+    @Transactional
     public void DetectFakeSeed(){
+        EntityManager entityManager = emf.createEntityManager();
+        try {
+            String torrentDir = entity.config.TORRENT_STORAGE_DIR;
+            String scriptPath = "./src/main/java/cheat/fakeSeed.bash";
+            
+            ProcessBuilder processBuilder = new ProcessBuilder("bash", scriptPath, torrentDir);
+            processBuilder.inheritIO();
+            
+            Process process = processBuilder.start();
+            int exitCode = process.waitFor();
+            
+            if (exitCode == 0) {
+                System.out.println("FakeSeed detection completed successfully");
+                
+                // 处理检测结果
+                String failedDir = torrentDir + "/failed_torrents";
+                java.io.File failedDirFile = new java.io.File(failedDir);
+                
+                // 获取失败种子文件列表
+                java.util.Set<String> failedTorrentNames = new java.util.HashSet<>();
+                if (failedDirFile.exists() && failedDirFile.isDirectory()) {
+                    java.io.File[] failedFiles = failedDirFile.listFiles((dir, name) -> name.endsWith(".torrent"));
+                    if (failedFiles != null) {
+                        for (java.io.File file : failedFiles) {
+                            failedTorrentNames.add(file.getName());
+                        }
+                    }
+                }
+                
+                JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
+                QSeed qSeed = QSeed.seed;
+                
+                // 收集假种子的拥有者
+                java.util.Set<String> fakeOwners = new java.util.HashSet<>();
+                
+                // 更新数据库中的fake_hits字段
+                List<Seed> allSeeds = queryFactory
+                    .selectFrom(qSeed)
+                    .where(qSeed.url.isNotNull())
+                    .fetch();
+                
+                for (Seed seed : allSeeds) {
+                    java.io.File seedFile = new java.io.File(seed.url);
+                    String fileName = seedFile.getName();
+                    
+                    if (failedTorrentNames.contains(fileName)) {
+                        // 失败种子,fake_hits + 1
+                        seed.faketime++;
+                        seed.lastfakecheck = new java.util.Date();
+                        fakeOwners.add(seed.seeduserid);
+                        System.out.println("Found fake seed: " + seed.seedid + " (fake_hits: " + seed.faketime + ")");
+                    } else {
+                        // 正常种子,fake_hits 清零
+                        if (seed.faketime > 0) {
+                            seed.faketime = 0;
+                            seed.lastfakecheck = new java.util.Date();
+                            System.out.println("Reset fake_hits for seed: " + seed.seedid);
+                        }
+                    }
+                    entityManager.merge(seed);
+                }
+                
+                // 更新所有用户的 fakeDetectedCount 和 fakeLastDetectedTime
+                entity.QUser qUser = entity.QUser.user;
+                List<User> allUsers = queryFactory
+                    .selectFrom(qUser)
+                    .fetch();
+                
+                java.util.Date now = new java.util.Date();
+                for (User user : allUsers) {
+                    if (fakeOwners.contains(user.userid)) {
+                        // 假种子拥有者,fakeDetectedCount + 1
+                        user.fakeDetectedCount++;
+                        System.out.println("Increased fake detection count for user: " + user.userid + " (count: " + user.fakeDetectedCount + ")");
+                    } else {
+                        // 其他用户,fakeDetectedCount 清零
+                        if (user.fakeDetectedCount > 0) {
+                            user.fakeDetectedCount = 0;
+                            System.out.println("Reset fake detection count for user: " + user.userid);
+                        }
+                    }
+                    // 更新所有用户的检测时间
+                    user.fakeLastDetectedTime = now;
+                    entityManager.merge(user);
+                }
+                
+                System.out.println("Fake seed detection and database update completed");
+            } else {
+                System.err.println("FakeSeed detection failed with exit code: " + exitCode);
+            }
+        } catch (Exception e) {
+            System.err.println("Error running FakeSeed detection script: " + e.getMessage());
+            e.printStackTrace();
+        } finally {
+            if (entityManager != null) {
+                entityManager.close();
+            }
+        }
     }
 
     @Override