完善tracker对服务器的适配

Change-Id: I0e74985cf50dc0683c48a69e4478b24d66453920
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index 4944c97..5e1a872 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -21,7 +21,11 @@
             <artifactId>spring-boot-devtools</artifactId>
             <optional>true</optional> <!-- 表示依赖不会传递 -->
         </dependency>
-
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>1.15</version>
+        </dependency>
         <!-- spring-doc -->
         <dependency>
             <groupId>org.springdoc</groupId>
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/AnnounceService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/AnnounceService.java
index 37e2135..3420fd3 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/AnnounceService.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/AnnounceService.java
@@ -1,6 +1,7 @@
 package com.ruoyi.web.Server;
 
 import com.alibaba.fastjson.JSON;
+import com.ruoyi.web.Server.BT.TorrentService;
 import com.ruoyi.web.dao.sys.UserDao;
 import com.ruoyi.web.domain.BT.TorrentEntity;
 import com.ruoyi.web.domain.BT.TorrentPeerEntity;
@@ -18,6 +19,7 @@
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -31,218 +33,213 @@
 
     final UserDao userDao;
     final UserService userService;
-
     final TorrentPeerService torrentPeerService;
-
-
     final ValidationManager validationManager;
+    final TorrentService torrentService;
 
     public Map<String, Object> announce(AnnounceRequest request) {
-
-        validationManager.validate(request);
-
-        //TODO 处理短时间内重复的请求
-
-        TorrentEntity torrent = request.getTorrent();
-
-        //省略peer id
         boolean noPeerId = request.isNoPeerId();
+        byte[] infoHash = request.getInfoHash();
+        TorrentEntity torrent = torrentService.getByInfoHash(infoHash);
+        request.setTorrent(torrent);
+        UserEntity user = userDao.findUserByPasskey(request.getPasskey());
+        request.setUser(user);
+        // 先更新peer状态再获取列表
+        updatePeer(request);
 
         List<Map<String, Object>> peerList = getPeerList(request, 200);
-
-        updatePeer(request, null);
-
         updateUserInfo(request);
-        // 返回peer列表给客户端
 
+        // 使用动态广播间隔
         Integer interval = getAnnounceInterval(request);
-        TrackerResponse trackerResponse = TrackerResponse.build(interval, 60, torrent.getSeeders(), torrent.getLeechers(), peerList);
+        TrackerResponse trackerResponse = TrackerResponse.build(
+                interval, 60,
+                torrent.getSeeders(),
+                torrent.getLeechers(),
+                peerList
+        );
         return trackerResponse.toResultMap();
     }
 
     private void updateUserInfo(AnnounceRequest request) {
         UserEntity user = request.getUser();
         TorrentEntity torrent = request.getTorrent();
-        Integer userId = request.getUser().getUser_id().intValue();
-        Integer torrentId = request.getTorrent().getId();
+        Integer userId = user.getUserId().intValue();
+        Integer torrentId = torrent.getId();
+        byte[] peerId = request.getPeerId();
 
-        TorrentPeerEntity peer = torrentPeerService.getPeer(userId, torrentId, request.getPeerId());
+        // 确保peer记录存在
+        TorrentPeerEntity peer = torrentPeerService.getPeer(userId, torrentId, peerId);
         if (peer == null) {
-            //TODO
             peer = tryInsertOrUpdatePeer(request);
+            if (peer == null) return; // 创建失败则终止
         }
 
+        // 处理负增量问题
         long lastUploaded = peer.getUploaded();
-        long lastDownload = peer.getDownloaded();
-        long uploadedOffset = request.getUploaded() - lastUploaded;
-        long downloadedOffset = request.getDownloaded() - lastDownload;
+        long lastDownloaded = peer.getDownloaded();
+        long currentUploaded = request.getUploaded();
+        long currentDownloaded = request.getDownloaded();
 
-        LocalDateTime updateTime = torrent.getUpdateTime();
-        if (uploadedOffset < 0) {
-            uploadedOffset = request.getUploaded();
-        }
-        if (downloadedOffset < 0) {
-            downloadedOffset = request.getDownloaded();
-        }
+        long uploadedOffset = Math.max(currentUploaded - lastUploaded, 0);
+        long downloadedOffset = Math.max(currentDownloaded - lastDownloaded, 0);
 
-        user.setReal_downloaded(Objects.nonNull(user.getReal_downloaded())? user.getReal_downloaded()+lastDownload:lastDownload);
-        user.setReal_uploaded(Objects.nonNull(user.getReal_uploaded())?user.getReal_uploaded()+lastUploaded: lastUploaded);
-
-        //TODO 优惠
-        user.setUpload(user.getUpload() + uploadedOffset);
-        user.setDownload(user.getDownload() + downloadedOffset);
-        user.setSeedtime(user.getSeedtime() + (Instant.now().toEpochMilli() - updateTime.toInstant(ZoneOffset.UTC).toEpochMilli()));
-        userService.updateById(user);
-
-        //TODO 最后处理删除
-        //TODO 校正BEP标准的计算方式
-
-
+        // 使用原子操作更新用户数据
+        userService.updateUserStats(
+                userId,
+                uploadedOffset,
+                downloadedOffset,
+                calculateSeedTimeIncrement(peer, request)
+        );
     }
 
-    public void updatePeer(AnnounceRequest request, TorrentPeerEntity peerSelf) {
+    // 精确计算做种时间增量(毫秒)
+    private long calculateSeedTimeIncrement(TorrentPeerEntity peer, AnnounceRequest request) {
+        if (!request.getSeeder()) return 0;
+
+        long now = System.currentTimeMillis();
+        long lastAnnounceTime = peer.getLastAnnounce().atZone(ZoneOffset.UTC).toInstant().toEpochMilli();
+        return now - lastAnnounceTime;
+    }
+
+    // 简化事件处理逻辑
+    public void updatePeer(AnnounceRequest request) {
         String event = StringUtils.trimToEmpty(request.getEvent());
         log.info("Peer event {}: {}", event, JSON.toJSONString(request, true));
-        Integer userId = request.getUser().getUser_id().intValue();
+
+        Integer userId = userDao.findUserByPasskey(request.getPasskey()).getUserId().intValue();
         Integer torrentId = request.getTorrent().getId();
-        //TODO 加入分布式锁
-
-        // 任务停止 删除peer
-        if (AnnounceRequest.EventType.stopped.equalsIgnoreCase(event)) {
-
-            // 只有当有peer存在的时候才执行删除操作
-            if (torrentPeerService.peerExists(userId, torrentId, request.getPeerId())) {
-                torrentPeerService.delete(userId, torrentId, request.getPeerIdHex());
-            }
-
-            return;
-        } else if (AnnounceRequest.EventType.started.equalsIgnoreCase(event)) {
-            tryInsertOrUpdatePeer(request);
-
-        } else if (AnnounceRequest.EventType.completed.equalsIgnoreCase(event)) {
-            torrentPeerService.delete(userId, torrentId, request.getPeerIdHex());
-            tryInsertOrUpdatePeer(request);
-
-        } else {
-            torrentPeerService.delete(userId, torrentId, request.getPeerIdHex());
-            tryInsertOrUpdatePeer(request);
-        }
-    }
-
-    /**
-     * 获取 peer 列表
-     *
-     * @param peerNumWant 这个参数表明你希望从方法返回多少个 peers。如果当前的系统中现有的 peer 数量小于你想要的 peerNumWant,那么就返回所有的
-     *                    peers;否则,只返回你想要的 peerNumWant 数量的 peers。
-     * @return
-     */
-    private List<Map<String, Object>> getPeerList(AnnounceRequest request, Integer peerNumWant) {
-
-        Integer torrentId = request.getTorrent().getId();
-        Boolean seeder = request.getSeeder();
-        boolean noPeerId = request.isNoPeerId();
-        Integer userId = request.getUser().getUser_id().intValue();
         String peerIdHex = request.getPeerIdHex();
 
-        //TODO 从数据库获取peer列表
-        //TODO 根据 seeder peerNumWant 参数限制peer
-        //如果当前用户是 seeder,那么这段代码将寻找 leecher;如果当前用户不是 seeder(或者不确定是否是 seeder),那么就不对 peer 的类型进行过滤。
-        List<TorrentPeerEntity> list = torrentPeerService.listByTorrent(torrentId, seeder, peerNumWant);
+        // 处理停止事件
+        if (AnnounceRequest.EventType.stopped.equalsIgnoreCase(event)) {
+            if (torrentPeerService.peerExists(userId, torrentId, peerIdHex.getBytes())) {
+                torrentPeerService.delete(userId, torrentId, peerIdHex);
+            }
+            return;
+        }
 
-        List<Map<String, Object>> result = list.stream()
-                .map(peer -> {
-
-                    // 当前 Peer 自己不返回
-                    if (peer.getUserId().equals(userId) && peer.getPeerId().equalsIgnoreCase(peerIdHex)) {
-                        return null;
-                    }
-
-                    Map<String, Object> dataMap = new HashMap<>();
-                    // 处理ipv4
-                    if (StringUtils.isNotBlank(peer.getIp())) {
-                        dataMap.put("ip", peer.getIp());
-                        dataMap.put("port", peer.getPort());
-                        if (!noPeerId) {
-                            dataMap.put("peer id", peer.getPeerId());
-                        }
-                    }
-                    //TODO 支持ipv6
-                    //TODO 支持压缩
-                    return dataMap.isEmpty() ? null : dataMap;
-                })
-                .filter(peer -> peer != null)
-                .collect(Collectors.toList());
-        return result;
+        // 其他事件统一更新
+        TorrentPeerEntity updatedPeer = tryInsertOrUpdatePeer(request);
+        if (updatedPeer != null && AnnounceRequest.EventType.completed.equalsIgnoreCase(event)) {
+            // 标记种子完成
+            updatedPeer.setSeeder(true);
+            torrentPeerService.updateById(updatedPeer);
+        }
     }
 
+    // 优化peer列表获取
 
+    // 优化peer列表获取
+    private List<Map<String, Object>> getPeerList(AnnounceRequest request, Integer peerNumWant) {
+        Integer torrentId = request.getTorrent().getId();
+        boolean isSeeder = request.getSeeder();
+        boolean noPeerId = request.isNoPeerId();
+        Integer userId = userDao.findUserByPasskey(request.getPasskey()).getUserId().intValue();
+
+        String peerIdHex = request.getPeerIdHex();
+
+        // 动态调整获取数量
+        int actualPeerNum = Math.min(peerNumWant, 200);
+
+        List<TorrentPeerEntity> peers = torrentPeerService.listByTorrent(
+                torrentId,
+                isSeeder,
+                actualPeerNum
+        );
+
+        return peers.stream()
+                .filter(peerEntity -> !isSelfPeer(peerEntity, userId, peerIdHex)) // 修复变量冲突
+                .map(peerEntity -> buildPeerMap(peerEntity, noPeerId)) // 修复变量冲突
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+
+    // 新增:检查是否是自身peer
+    private boolean isSelfPeer(TorrentPeerEntity peer, Integer userId, String peerIdHex) {
+        return peer.getUserId().equals(userId) && peer.getPeerId().equals(peerIdHex);
+    }
+    // 构建peer返回数据
+    private Map<String, Object> buildPeerMap(TorrentPeerEntity peer, boolean noPeerId) {
+        Map<String, Object> dataMap = new HashMap<>();
+
+        if (StringUtils.isNotBlank(peer.getIp())) {
+            dataMap.put("ip", peer.getIp());
+            dataMap.put("port", peer.getPort());
+            if (!noPeerId) {
+                dataMap.put("peer id", peer.getPeerId());
+            }
+        }
+        // IPv6支持
+        if (StringUtils.isNotBlank(peer.getIpv6())) {
+            dataMap.put("ipv6", peer.getIpv6());
+            dataMap.put("port", peer.getPort()); // 端口复用
+        }
+
+        return dataMap.isEmpty() ? null : dataMap;
+    }
+
+    // 修复peer更新逻辑
     private TorrentPeerEntity tryInsertOrUpdatePeer(AnnounceRequest request) {
+        Integer userId = userDao.findUserByPasskey(request.getPasskey()).getUserId().intValue();
+
+        Integer torrentId = request.getTorrent().getId();
+        String peerIdHex = request.getPeerIdHex();
+
         try {
+            // 先尝试获取现有记录
+            TorrentPeerEntity peer = torrentPeerService.getPeer(userId, torrentId, peerIdHex.getBytes());
+            boolean isNew = false;
 
-            TorrentPeerEntity peerEntity = new TorrentPeerEntity();
-            peerEntity.setUserId(request.getUser().getUser_id().intValue());
-            peerEntity.setTorrentId(request.getTorrent().getId());
-            peerEntity.setPeerId(request.getPeer_id());
-            peerEntity.setPeerIdHex(request.getPeerIdHex());
-            peerEntity.setPort(request.getPort());
-            peerEntity.setDownloaded(request.getDownloaded());
-            peerEntity.setUploaded(request.getUploaded());
-            peerEntity.setRemaining(request.getLeft());
-            peerEntity.setSeeder(request.getSeeder());
-            peerEntity.setUserAgent(request.getUserAgent());
-            peerEntity.setPasskey(request.getPasskey());
-            peerEntity.setCreateTime(LocalDateTime.now());
-            peerEntity.setLastAnnounce(LocalDateTime.now());
-
-            peerEntity.setIp(request.getIp());
-            peerEntity.setIpv6(request.getIpv6());
-
-            if (StringUtils.isBlank(peerEntity.getIp())) {
-                peerEntity.setIp(request.getRemoteAddr());
+            if (peer == null) {
+                peer = new TorrentPeerEntity();
+                peer.setCreateTime(LocalDateTime.now());
+                isNew = true;
             }
 
-            torrentPeerService.save(peerEntity);
-            return peerEntity;
+            // 更新关键字段
+            peer.setUserId(userId);
+            peer.setTorrentId(torrentId);
+            peer.setPeerId(request.getPeer_id());
+            peer.setPeerIdHex(peerIdHex);
+            peer.setPort(request.getPort());
+            peer.setDownloaded(request.getDownloaded());
+            peer.setUploaded(request.getUploaded());
+            peer.setRemaining(request.getLeft());
+            peer.setSeeder(request.getSeeder());
+            peer.setUserAgent(request.getUserAgent());
+            peer.setPasskey(request.getPasskey());
+            peer.setLastAnnounce(LocalDateTime.now());
+            peer.setIp(StringUtils.defaultIfBlank(request.getIp(), request.getRemoteAddr()));
+            peer.setIpv6(request.getIpv6());
 
-        } catch (Exception exception) {
-            log.error("Peer update error update: ", exception);
+            if (isNew) {
+                torrentPeerService.insertOrUpdate(peer);
+            } else {
+                torrentPeerService.updateById(peer);
+            }
+            return peer;
+
+        } catch (Exception e) {
+            log.error("Peer update error: ", e);
+            return null;
         }
-        return null;
     }
 
-    /**
-     * 广播间隔
-     * 策略:
-     * 基于种子发布的时间长度来调整广播间隔:类似NP
-     * 如 种子发布7天内,广播间隔为 600s
-     * 种子发布大于7天,小于30天, 广播间隔1800s
-     * 种子发布30天以上,广播间隔3600s
-     * <p>
-     * <p>
-     * 基于活跃度调整广播间隔:你可以根据种子的活跃度(例如活跃peer的数量或者下载/上传速度)来调整广播间隔。
-     * 如果一个种子的活跃度高,说明它需要更频繁地更新和广播peer列表。
-     * 如果活跃度低,可以增加广播间隔以减少服务器负载。
-     * <p>
-     * 基于服务器负载调整广播间隔:如果你的服务器负载高(例如CPU
-     * 使用率高,内存使用量高,或者网络带宽使用高),可以增加广播间隔以减少负载。
-     * 如果服务器负载低,可以减小广播间隔以提高文件分享的效率。
-     * <p>
-     * 动态调整广播间隔:你可以实时监控你的网络状况、服务器状况、以及种子的活跃度,然后动态调整广播间隔。
-     * 例如,如果你发现某个时间段用户数量增多,可以临时减小广播间隔。如果发现某个时间段用户数量减少,可以增加广播间隔。
-     * <p>
-     * <p>
-     * 综合策略:
-     * <p>
-     * 种子发布7天内或活跃peer数大于1000,广播间隔为 600s
-     * 种子发布大于7天,小于30天或活跃peer数在100-1000之间,广播间隔为 1800s
-     * 种子发布30天以上或活跃peer数小于100,广播间隔为 3600s
-     *
-     * @param request
-     * @return
-     */
+    // 实现动态广播间隔策略
     private Integer getAnnounceInterval(AnnounceRequest request) {
-        //TODO 广播间隔
+        TorrentEntity torrent = request.getTorrent();
+        int activePeers = torrent.getSeeders() + torrent.getLeechers();
+        long daysActive = ChronoUnit.DAYS.between(torrent.getCreateTime(), LocalDateTime.now());
 
-        return 60;
+        // 动态调整策略
+        if (daysActive < 7 || activePeers > 1000) {
+            return 600; // 高频更新(10分钟)
+        } else if ((daysActive >= 7 && daysActive < 30) ||
+                (activePeers >= 100 && activePeers <= 1000)) {
+            return 1800; // 中频更新(30分钟)
+        } else {
+            return 3600; // 低频更新(60分钟)
+        }
     }
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentCommentService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentCommentService.java
index fbc0045..de8612c 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentCommentService.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentCommentService.java
@@ -48,7 +48,7 @@
                     // 查询用户信息
                     UserEntity user = userService.getUserById(entity.getUserId());
                     if (user != null) {
-                        vo.setUsername(user.getUser_name());
+                        vo.setUsername(user.getUserName());
                         vo.setAvatar(user.getAvatar());
                     }
 
@@ -76,7 +76,7 @@
                     // 设置用户信息
                     UserEntity user = userService.getUserById(entity.getUserId());
                     if (user != null) {
-                        vo.setUsername(user.getUser_name());
+                        vo.setUsername(user.getUserName());
                         vo.setAvatar(user.getAvatar());
                     }
 
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentPeerService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentPeerService.java
index af59128..7c5522e 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentPeerService.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentPeerService.java
@@ -16,6 +16,11 @@
 @Slf4j
 public class TorrentPeerService extends ServiceImpl<TorrentPeerDao, TorrentPeerEntity> {
 
+    final TorrentPeerDao torrentPeerDao;
+
+    public TorrentPeerService(TorrentPeerDao torrentPeerDao) {
+        this.torrentPeerDao = torrentPeerDao;
+    }
 
     /**
      * 从数据库获取peer列表
@@ -68,5 +73,9 @@
                 .eq(TorrentPeerEntity::getPeerIdHex, peerIdHex)
         );
     }
+
+    public int insertOrUpdate(TorrentPeerEntity entity){
+        return torrentPeerDao.insertOrUpdate(entity);
+    }
 }
 
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentService.java
index 497b1ca..35fc9e3 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentService.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentService.java
@@ -88,7 +88,7 @@
 
 
         byte[] transformedBytes = torrentManager.transform(bytes);
-        byte[] infoHash = torrentManager.infoHash(transformedBytes);
+        byte[] infoHash = torrentManager.infoHash(bytes);
         System.out.println(infoHash);
 
         long count = count(Wrappers.<TorrentEntity>lambdaQuery()
@@ -110,6 +110,11 @@
 
     }
 
+    public int getTorrentIdByHash(byte[] infoHash){
+        return torrentDao.getTorrentIdByHash(infoHash);
+    }
+
+
     public byte[] fetch(Integer torrentId, String passkey) {
         byte[] fileBytes = torrentStorageService.read(torrentId);
 
@@ -340,16 +345,16 @@
          if (userCredentialService.getById(userId) == null){
              UserEntity userEntity = userService.getUserById(userId);
              UserCredentialEntity userCredentialEntity = new UserCredentialEntity();
-             userCredentialEntity.setUserid(userEntity.getUser_id().intValue());
-             userCredentialEntity.setUsername(userEntity.getUser_name());
+             userCredentialEntity.setUserid(userEntity.getUserId().intValue());
+             userCredentialEntity.setUsername(userEntity.getUserName());
              userCredentialEntity.setRegIp(IPUtils.getIpAddr());
-             userCredentialEntity.setRegType(userEntity.getReg_type());
-             String checkCode = passkeyManager.generate(userEntity.getUser_id().intValue());
+             userCredentialEntity.setRegType(userEntity.getRegType());
+             String checkCode = passkeyManager.generate(userEntity.getUserId().intValue());
              userCredentialEntity.setCheckCode(checkCode);
 
              // 生成随机盐和密码
              String salt = RandomUtil.randomString(8);
-             String passkey = passkeyManager.generate(userEntity.getUser_id().intValue());
+             String passkey = passkeyManager.generate(userEntity.getUserId().intValue());
 
              userCredentialEntity.setSalt(salt);
              userCredentialEntity.setPasskey(passkey);
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TrackerURLService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TrackerURLService.java
index d28c7e2..68bd665 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TrackerURLService.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TrackerURLService.java
@@ -23,16 +23,13 @@
      * 获取当前的tracker Announce 地址
      */
     public String getAnnounce(String passkey) {
-        try {
-            InetAddress localhost = InetAddress.getLocalHost();
-            String hostname = localhost.getHostName();
+
+            String hostname = "team4.10813352.xyz";
 
             String string = Constants.Announce.PROTOCOL + "://" + hostname + ":" + Constants.Announce.PORT + "/tracker/announce?passkey=" + passkey;
 
             return string;
-        }catch (UnknownHostException e){
-            return "Server error: can not get host";
-        }
+
     }
 }
 
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/sys/UserService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/sys/UserService.java
index f5b57ce..333b194 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/sys/UserService.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/sys/UserService.java
@@ -126,6 +126,10 @@
 ////        long total = new PageInfo(userEntities).getTotal();
 ////        return new PageDTO<>(userEntities, total);
 ////    }
+public void updateUserStats(Integer userId, long uploaded, long downloaded, long seedTime) {
+    // 使用SQL原子操作保证数据一致性
+    baseMapper.updateUserStats(userId, uploaded, downloaded, (long)(seedTime/3600));
+}
 //
 //    public Result findUsers(UserParam param) {
 //        PageHelper.startPage(param.getPage(), param.getSize());
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/BT/TrackerController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/BT/TrackerController.java
index c4c0152..359629b 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/BT/TrackerController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/BT/TrackerController.java
@@ -3,7 +3,6 @@
 
 import cn.hutool.core.util.HexUtil;
 import com.ruoyi.web.Server.AnnounceService;
-import com.ruoyi.web.controller.common.exception.TrackerException;
 import com.ruoyi.web.dao.BT.AnnounceRequest;
 import com.ruoyi.web.Tool.BT.BencodeUtil;
 import com.ruoyi.web.Tool.BT.BinaryFieldUtil;
@@ -14,7 +13,6 @@
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -48,57 +46,24 @@
 
         String queryStrings = request.getQueryString();
         log.info("收到announce汇报:" + queryStrings);
+        String ipAddr = IPUtils.getIpAddr();
+        byte[] peerId = request.getRemoteAddr().getBytes();
 
-        // 获取客户端真实IP
-        String ipAddr = request.getRemoteAddr();
-        String xffHeader = request.getHeader("X-Forwarded-For");
-        if (xffHeader != null) {
-            ipAddr = xffHeader.split(",")[0];
-        }
-        log.debug("客户端IP: {}", ipAddr);
+        String peerIdHex = HexUtil.encodeHexStr(peerId);
 
-        // 解析并验证peer_id
-        try {
-            byte[] peerId = BinaryFieldUtil.matchPeerId(queryStrings);
-            if (peerId.length != 20) {
-                throw new TrackerException("Invalid peer_id length: " + peerId.length);
-            }
-            String peerIdHex = HexUtil.encodeHexStr(peerId);
+        announceRequest.setSeeder(announceRequest.getLeft().equals(0L));
+        announceRequest.setInfoHash(BinaryFieldUtil.matchInfoHash(queryStrings));
+        announceRequest.setPeerId(peerId);
+        announceRequest.setPeerIdHex(peerIdHex);
+        announceRequest.setRemoteAddr(ipAddr);
+        announceRequest.setWantDigest(wantDigest);
+        announceRequest.setUserAgent(ua);
 
-            announceRequest.setSeeder(announceRequest.getLeft().equals(0L));
-            announceRequest.setInfoHash(BinaryFieldUtil.matchInfoHash(queryStrings));
-            announceRequest.setPeerId(peerId);
-            announceRequest.setPeerIdHex(peerIdHex);
-            announceRequest.setRemoteAddr(ipAddr);
-            announceRequest.setWantDigest(wantDigest);
-            announceRequest.setUserAgent(ua);
 
-            // 验证passkey
-            String passkey = extractPasskey(queryStrings);
-            announceRequest.setPasskey(passkey);
+        Map<String, Object> response = announceService.announce(announceRequest);
 
-            // 处理请求
-            Map<String, Object> response = announceService.announce(announceRequest);
-            return BencodeUtil.encode(response);
-
-        } catch (Exception e) {
-            log.error("处理announce请求失败", e);
-            Map<String, Object> errorResponse = new HashMap<>();
-            errorResponse.put("failure reason", e.getMessage());
-            return BencodeUtil.encode(errorResponse);
-        }
-    }
-
-    // 从queryString中提取passkey的辅助方法
-    private String extractPasskey(String queryString) {
-        if (queryString == null) return null;
-        String[] params = queryString.split("&");
-        for (String param : params) {
-            if (param.startsWith("passkey=")) {
-                return param.substring("passkey=".length());
-            }
-        }
-        return null;
+        String responseStr = BencodeUtil.encode(response);
+        return responseStr;
     }
 
     /**
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/AnnounceRequest.java b/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/AnnounceRequest.java
index 819e68a..3324ab2 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/AnnounceRequest.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/AnnounceRequest.java
@@ -109,6 +109,11 @@
     //}
 
     /**
+     * The purpose of the method:
+     * torrent_id
+     */
+    private Integer torrentId;
+    /**
      * passkey
      */
     private String passkey;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TorrentDao.java b/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TorrentDao.java
index 31f1fff..56c8704 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TorrentDao.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TorrentDao.java
@@ -88,4 +88,6 @@
     @Update("UPDATE bt_torrent SET leechers = leechers + 1 WHERE id = #{id}")
     int incrementLeechers(@Param("id") Integer id);
 
+    @Select("select id from bt_torrent where info_hash=#{infoHash} ")
+    int getTorrentIdByHash(byte[] infoHash);
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TorrentPeerDao.java b/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TorrentPeerDao.java
index 163471a..9158eb8 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TorrentPeerDao.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TorrentPeerDao.java
@@ -2,7 +2,9 @@
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.ruoyi.web.domain.BT.TorrentPeerEntity;
+import org.apache.ibatis.annotations.Insert;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Options;
 
 /**
  * 种子Peer
@@ -10,5 +12,25 @@
  */
 @Mapper
 public interface TorrentPeerDao extends BaseMapper<TorrentPeerEntity> {
-
+    @Insert("INSERT INTO bt_torrent_peer (" +
+            "torrent_id, user_id, peer_id, peer_id_hex, ip, port, " +
+            "uploaded, downloaded, remaining, seeder, user_agent, passkey, create_time, last_announce" +
+            ") " +
+            "VALUES (" +
+            "#{torrentId}, #{userId}, #{peerId}, #{peerIdHex}, #{ip}, #{port}, " +
+            "#{uploaded}, #{downloaded}, #{remaining}, #{seeder}, #{userAgent}, #{passkey}, " +
+            "#{createTime}, #{lastAnnounce}" +
+            ") " +
+            "ON DUPLICATE KEY UPDATE " +
+            "ip = VALUES(ip), " +
+            "port = VALUES(port), " +
+            "uploaded = VALUES(uploaded), " +
+            "downloaded = VALUES(downloaded), " +
+            "remaining = VALUES(remaining), " +
+            "seeder = VALUES(seeder), " +
+            "user_agent = VALUES(user_agent), " +
+            "passkey = VALUES(passkey), " +
+            "last_announce = VALUES(last_announce)")
+    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
+    int insertOrUpdate(TorrentPeerEntity entity);
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/dao/sys/UserDao.java b/ruoyi-admin/src/main/java/com/ruoyi/web/dao/sys/UserDao.java
index ad641c0..af13e26 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/dao/sys/UserDao.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/dao/sys/UserDao.java
@@ -4,6 +4,8 @@
 import com.ruoyi.web.domain.sys.UserEntity;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+import org.springframework.data.repository.query.Param;
 
 import java.util.List;
 import java.util.Set;
@@ -34,5 +36,14 @@
     @Select("SELECT * FROM sys_user WHERE user_id=#{id}")
     UserEntity findUserById(Integer id);
 
-
+    @Update("UPDATE sys_user SET " +
+            "real_uploaded = real_uploaded + #{uploaded}, " +
+            "real_downloaded = real_downloaded + #{downloaded}, " +
+            "bonus = bonus + #{seedTime} " +
+            "WHERE user_id = #{userId}")
+    void updateUserStats(
+            @Param("userId") Integer userId,
+            @Param("uploaded") long uploaded,
+            @Param("downloaded") long downloaded,
+            @Param("seedTime") long seedTime);
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/domain/BT/TorrentEntity.java b/ruoyi-admin/src/main/java/com/ruoyi/web/domain/BT/TorrentEntity.java
index 0592102..af4f79f 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/domain/BT/TorrentEntity.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/domain/BT/TorrentEntity.java
@@ -13,151 +13,117 @@
 @TableName("bt_torrent")
 public class TorrentEntity {
 
-    /**
-     *
-     */
     @TableId
     private Integer id;
-    /**
-     * 种子哈希
-     */
+
     private byte[] infoHash;
-    /**
-     * 名称
-     */
     private String name;
-    /**
-     * 上传文件名
-     */
     private String filename;
-    /**
-     * 标题
-     */
     private String title;
-    /**
-     * 简介副标题
-     */
     private String subheading;
-    /**
-     * 封面
-     */
     private String cover;
-    /**
-     * 描述
-     */
     private String description;
 
+    // === 为所有数字统计字段添加默认值 ===
+
     /**
      * 类别
      */
-    private Integer category;
+    private Integer category = 0;  // 默认类别为0
 
     /**
      * 状态
-     *
      * @see Status
      */
-    private Integer status;
+    private Integer status = Status.CANDIDATE;  // 默认状态为候选中
 
     /**
      * 文件状态 0 未上传 1 已上传
      */
-    private Integer fileStatus;
-    /**
-     * 审核人
-     */
-    private Integer reviewer;
-
+    private Integer fileStatus = 0;  // 默认文件未上传
 
     /**
-     * 添加日期
+     * 审核人 - 允许为null
      */
+    private Integer reviewer;  // 未审核时为null
+
     private LocalDateTime createTime;
-
-
-    /**
-     * 修改日期
-     */
     private LocalDateTime updateTime;
 
     /**
-     * 拥有者
+     * 拥有者 - 必须设置,不需要默认值
      */
     private Integer owner;
+
     /**
      * 文件大小
      */
-    private Long size;
+    private Long size = 0L;  // 默认0
+
     /**
-     * 类型
-     * single(1)
-     * multi(2)
+     * 类型 single(1)/multi(2)
      */
-    private Integer type;
+    private Integer type = 1;  // 默认单文件
+
     /**
      * 文件数量
      */
-    private Integer fileCount;
+    private Integer fileCount = 1;  // 默认1个文件
 
     /**
      * 评论数
      */
-    private Integer comments;
+    private Integer comments = 0;  // 默认0评论
+
     /**
      * 浏览次数
      */
-    private Integer views;
+    private Integer views = 0;  // 默认0次浏览
+
     /**
      * 点击次数
      */
-    private Integer hits;
+    private Integer hits = 0;  // 默认0点击
 
     /**
      * 可见性
      */
-    private Integer visible;
+    private Integer visible = 1;  // 默认可见
 
     /**
      * 是否匿名
      */
-    private Integer anonymous;
-
+    private Integer anonymous = 0;  // 默认不匿名
 
     /**
      * 下载数
      */
-    private Integer leechers;
+    private Integer leechers = 0;  // 关键: 默认0下载
+
     /**
      * 做种数
      */
-    private Integer seeders;
+    private Integer seeders = 0;  // 关键: 默认0做种
 
     /**
      * 做种完成次数
      */
-    private Integer completions;
+    private Integer completions = 0;  // 默认0完成
 
     /**
-     *
+     * 备注 - 允许为null
      */
     private String remark;
 
     /**
      * 种子状态
-     * 0 候选中 1 已发布 2 审核不通过 3 已上架修改重审中 10 已下架
      */
     public interface Status {
-
         int CANDIDATE = 0;
-
         int PUBLISHED = 1;
-
         int AUDIT_NOT_PASSED = 2;
-
         int RETRIAL = 3;
-
         int REMOVED = 10;
-
     }
 
     @RequiredArgsConstructor
@@ -171,7 +137,15 @@
     }
 
     public boolean isStatusOK() {
-        return status == 1;
+        return status == Status.PUBLISHED;
     }
 
-}
+    // === 添加getter方法确保空值安全 ===
+    public Integer getLeechers() {
+        return leechers != null ? leechers : 0;
+    }
+
+    public Integer getSeeders() {
+        return seeders != null ? seeders : 0;
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/domain/sys/UserEntity.java b/ruoyi-admin/src/main/java/com/ruoyi/web/domain/sys/UserEntity.java
index aa85880..e759333 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/domain/sys/UserEntity.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/domain/sys/UserEntity.java
@@ -18,10 +18,10 @@
 public class UserEntity implements Serializable {
 
     @TableId(value = "user_id", type = IdType.AUTO)
-    private Long user_id;
-    private String user_name;
-    private String nick_name;
-    private String user_type;
+    private Long userId;
+    private String userName;
+    private String nickName;
+    private String userType;
     private String email;
     private String phonenumber;
 
@@ -36,69 +36,69 @@
     @TableField("status")
     private Integer status;
 
-    private String login_ip;
+    private String loginIp;
 
     // 日期类型统一使用LocalDateTime
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private LocalDateTime login_date;
+    private LocalDateTime loginDate;
 
-    private String create_by;
+    private String createBy;
 
     // 修正字段名与数据库一致
     @TableField("create_time")
-    private LocalDateTime create_time;
+    private LocalDateTime createTime;
 
-    private String update_by;
+    private String updateBy;
 
     // 修正字段名与数据库一致
     @TableField("update_time")
-    private LocalDateTime update_time;
+    private LocalDateTime updateTime;
 
     private String remark;
-    private String full_name;
+    private String fullName;
 
     // 使用Integer而非自定义枚举,与数据库字段类型一致
     private Integer state;
 
     private LocalDateTime added;
-    private LocalDateTime last_login;
-    private LocalDateTime last_access;
-    private LocalDateTime last_home;
-    private LocalDateTime last_offer;
-    private LocalDateTime forum_access;
-    private LocalDateTime last_staffmsg;
-    private LocalDateTime last_pm;
-    private LocalDateTime last_comment;
-    private LocalDateTime last_post;
-    private LocalDateTime last_active;
+    private LocalDateTime lastLogin;
+    private LocalDateTime lastAccess;
+    private LocalDateTime lastHome;
+    private LocalDateTime lastOffer;
+    private LocalDateTime forumAccess;
+    private LocalDateTime lastStaffmsg;
+    private LocalDateTime lastPm;
+    private LocalDateTime lastComment;
+    private LocalDateTime lastPost;
+    private LocalDateTime lastActive;
 
     private Integer privacy;
-    private String reg_ip;
+    private String regIp;
     private Integer level;
     private Long seedtime;
     private Long leechtime;
 
     @Schema(description = "真实上传量")
-    private Long real_uploaded;
+    private Long realUploaded;
 
     @Schema(description = "真实下载量")
-    private Long real_downloaded;
+    private Long realDownloaded;
 
     private String modcomment;
-    private Long warning_by;
-    private Integer warning_times;
+    private Long warningBy;
+    private Integer warningTimes;
     private Boolean warning;
 
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private LocalDateTime warning_until;
+    private LocalDateTime warningUntil;
 
     private Long download;
     private Long upload;
-    private Integer invited_by;
+    private Integer invitedBy;
     private Long bonus;
     private Long exp;
-    private String check_code;
-    private Integer reg_type;
+    private String checkCode;
+    private Integer regType;
 
     // 状态辅助方法
     public boolean isUserLocked() {
diff --git a/torrent/2002464791.torrent b/torrent/2002464791.torrent
new file mode 100644
index 0000000..3a96c9c
--- /dev/null
+++ b/torrent/2002464791.torrent
Binary files differ
diff --git a/torrent/2002464806.torrent b/torrent/2002464806.torrent
new file mode 100644
index 0000000..3a96c9c
--- /dev/null
+++ b/torrent/2002464806.torrent
Binary files differ
diff --git a/torrent/2002464807.torrent b/torrent/2002464807.torrent
new file mode 100644
index 0000000..3a96c9c
--- /dev/null
+++ b/torrent/2002464807.torrent
Binary files differ