完善了服务器配置,实现自动解析oss-url,自定义tracker响应

Change-Id: I2bf0848547427095bf898f2252c5020641764b21
diff --git a/pom.xml b/pom.xml
index 3b58690..af34658 100644
--- a/pom.xml
+++ b/pom.xml
@@ -127,6 +127,14 @@
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>4.0.1</version>
+            <scope>provided</scope>
+        </dependency>
+
         <dependency>
             <groupId>com.turn</groupId>
             <artifactId>ttorrent-core</artifactId>
@@ -134,7 +142,6 @@
             <classifier>javadoc</classifier>
             <type>javadoc</type>
         </dependency>
-
         <dependency>
             <groupId>com.turn</groupId>
             <artifactId>ttorrent-core</artifactId>
@@ -147,6 +154,7 @@
             <artifactId>ttorrent-core</artifactId>
             <version>1.5</version>
         </dependency>
+
         <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
diff --git a/src/main/java/edu/bjtu/groupone/backend/api/CategoryController.java b/src/main/java/edu/bjtu/groupone/backend/api/CategoryController.java
index a9b7b9d..145703e 100644
--- a/src/main/java/edu/bjtu/groupone/backend/api/CategoryController.java
+++ b/src/main/java/edu/bjtu/groupone/backend/api/CategoryController.java
@@ -55,4 +55,4 @@
     public ResponseEntity<Category> getCategory(@PathVariable Long id) {
         return ResponseEntity.ok(categoryService.getCategoryById(id));
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/edu/bjtu/groupone/backend/api/DbTestController.java b/src/main/java/edu/bjtu/groupone/backend/api/DbTestController.java
new file mode 100644
index 0000000..ea24d21
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/api/DbTestController.java
@@ -0,0 +1,26 @@
+package edu.bjtu.groupone.backend.api;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+
+@RestController
+@RequestMapping("/test")
+public class DbTestController {
+
+    @Autowired
+    private DataSource dataSource;
+
+    @GetMapping("/db")
+    public String testDb() {
+        try (Connection conn = dataSource.getConnection()) {
+            return "✅ 成功连接数据库:" + conn.getMetaData().getURL();
+        } catch (Exception e) {
+            return "❌ 无法连接数据库:" + e.getMessage();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/bjtu/groupone/backend/api/TorrentController.java b/src/main/java/edu/bjtu/groupone/backend/api/TorrentController.java
index e5f1aa4..773c5f6 100644
--- a/src/main/java/edu/bjtu/groupone/backend/api/TorrentController.java
+++ b/src/main/java/edu/bjtu/groupone/backend/api/TorrentController.java
@@ -8,46 +8,49 @@
 import org.springframework.core.io.Resource;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.util.List;
+
 @RestController
-@RequestMapping({"/api/torrents"})
+@RequestMapping("/api/torrents")
 public class TorrentController {
+
+    private final TorrentService torrentService;
+
+    // 监听器列表应该放在专门的处理类中
     @Autowired
-    private TorrentService torrentService;
-
-    public TorrentController() {
+    public TorrentController(TorrentService torrentService) {
+        this.torrentService = torrentService;
     }
 
-    @PostMapping({"/upload"})
-    public ResponseEntity<?> uploadTorrent(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
-        String uidStr = GetTokenUserId.getUserId(request);
-        if (uidStr == null) {
-            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Token无效或缺失");
-        } else {
-            try {
-                Long userId = Long.parseLong(uidStr);
-                Torrent saved = this.torrentService.uploadTorrent(file, userId);
-                return ResponseEntity.ok(saved);
-            } catch (Exception var6) {
-                return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("上传失败:" + var6.getMessage());
-            }
-        }
-    }
+    @PostMapping("/upload")
+    public ResponseEntity<?> uploadTorrent(
+            @RequestParam("file") MultipartFile file,
+            @RequestParam("id") Long id ) throws Exception {
 
-    @GetMapping({"/download/{infoHash}"})
+        Torrent saved = torrentService.uploadTorrent(file, id);
+        return ResponseEntity.ok(saved);
+
+    }
+    @GetMapping("/download/{infoHash}")
     public ResponseEntity<Resource> downloadTorrent(@PathVariable String infoHash) {
         try {
-            Resource resource = this.torrentService.downloadTorrent(infoHash);
-            return ((ResponseEntity.BodyBuilder)ResponseEntity.ok().header("Content-Disposition", new String[]{"attachment; filename=\"" + infoHash + ".torrent\""})).body(resource);
-        } catch (Exception var3) {
+            Resource resource = torrentService.downloadTorrent(infoHash);
+            return ResponseEntity.ok()
+                    .header("Content-Disposition", "attachment; filename=\"" + infoHash + ".torrent\"")
+                    .body(resource);
+        } catch (Exception ex) {
             return ResponseEntity.notFound().build();
         }
     }
-}
+    @PostMapping("/getTorrentList")
+    public ResponseEntity<?> getTorrentList() throws Exception {
+
+        List<Torrent> list = torrentService.getTorrentList();
+        return ResponseEntity.ok(list);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/edu/bjtu/groupone/backend/config/TrackerStarter.java b/src/main/java/edu/bjtu/groupone/backend/config/TrackerStarter.java
deleted file mode 100644
index 6ae8be7..0000000
--- a/src/main/java/edu/bjtu/groupone/backend/config/TrackerStarter.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package edu.bjtu.groupone.backend.config;
-
-import com.turn.ttorrent.tracker.TrackedTorrent;
-import com.turn.ttorrent.tracker.Tracker;
-import java.io.File;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.security.NoSuchAlgorithmException;
-import java.util.Objects;
-import javax.annotation.PostConstruct;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-@Component
-public class TrackerStarter {
-    private Tracker tracker;
-
-    public TrackerStarter() {
-    }
-
-    @PostConstruct
-    public void startTracker() throws Exception {
-        InetSocketAddress address = new InetSocketAddress("0.0.0.0", 6969);
-        this.tracker = new Tracker(address);
-        this.tracker.start();
-        System.out.println("Tracker started on http://localhost:6969/announce");
-    }
-
-    @Scheduled(
-            fixedRate = 60000L
-    )
-    public void scanTorrentDirectory() throws IOException, NoSuchAlgorithmException {
-        File torrentDir = new File("C:\\Users\\wangy\\Desktop\\GroupOne-Back-End(2)\\GroupOne-Back-End\\torrents");
-        if (torrentDir.exists() && torrentDir.isDirectory()) {
-            File[] var2 = (File[])Objects.requireNonNull(torrentDir.listFiles());
-            int var3 = var2.length;
-
-            for(int var4 = 0; var4 < var3; ++var4) {
-                File file = var2[var4];
-                if (file.getName().endsWith(".torrent")) {
-                    this.tracker.announce(TrackedTorrent.load(file));
-                    System.out.println("Loaded torrent: " + file.getName());
-                }
-            }
-        } else {
-            System.out.println("Torrent directory not found: " + torrentDir.getAbsolutePath());
-        }
-
-    }
-}
diff --git a/src/main/java/edu/bjtu/groupone/backend/config/TrafficAwareTracker.java b/src/main/java/edu/bjtu/groupone/backend/config/TrafficAwareTracker.java
new file mode 100644
index 0000000..d7dd987
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/config/TrafficAwareTracker.java
@@ -0,0 +1,36 @@
+package edu.bjtu.groupone.backend.config;
+
+import com.turn.ttorrent.tracker.TrackedPeer;
+import com.turn.ttorrent.tracker.TrackedTorrent;
+import com.turn.ttorrent.tracker.Tracker;
+import edu.bjtu.groupone.backend.domain.entity.AnnounceEvent;
+import edu.bjtu.groupone.backend.service.PeerTrafficService;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+public class TrafficAwareTracker extends Tracker {
+    private final PeerTrafficService trafficService;
+
+    public TrafficAwareTracker(InetSocketAddress address, PeerTrafficService trafficService)
+            throws IOException {
+        super(address);
+        this.trafficService = trafficService;
+    }
+
+    public void peerAnnounce(TrackedTorrent torrent, TrackedPeer peer,
+                             AnnounceEvent event) throws IOException {
+        // 先记录流量(必须在super调用前执行)
+        if (peer.getUploaded() > 0 || peer.getDownloaded() > 0) {
+            trafficService.recordPeerTraffic(
+                    torrent.getHexInfoHash(),
+                    peer.getIp(),
+                    peer.getPeerId(),
+                    peer.getUploaded(),
+                    peer.getDownloaded(),
+                    System.currentTimeMillis() / 1000,
+                    event
+            );
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/bjtu/groupone/backend/domain/entity/AnnounceEvent.java b/src/main/java/edu/bjtu/groupone/backend/domain/entity/AnnounceEvent.java
new file mode 100644
index 0000000..8758465
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/domain/entity/AnnounceEvent.java
@@ -0,0 +1,9 @@
+package edu.bjtu.groupone.backend.domain.entity;
+
+public enum AnnounceEvent {
+    STARTED,       // 开始下载
+    STOPPED,       // 停止
+    COMPLETED,     // 完成
+    PAUSED,        // 新增:暂停
+    RESUMED        // 新增:恢复
+}
\ No newline at end of file
diff --git a/src/main/java/edu/bjtu/groupone/backend/domain/entity/Torrent.java b/src/main/java/edu/bjtu/groupone/backend/domain/entity/Torrent.java
index 0564b50..ec9d123 100644
--- a/src/main/java/edu/bjtu/groupone/backend/domain/entity/Torrent.java
+++ b/src/main/java/edu/bjtu/groupone/backend/domain/entity/Torrent.java
@@ -1,5 +1,11 @@
 package edu.bjtu.groupone.backend.domain.entity;
 
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
 public class Torrent {
     private Long id;
     private String name;
@@ -11,6 +17,8 @@
     private Double uploadMultiplier;
     private String uploadedAt;
 
+    private String url;
+
     public Long getId() {
         return this.id;
     }
diff --git a/src/main/java/edu/bjtu/groupone/backend/mapper/TorrentMapper.java b/src/main/java/edu/bjtu/groupone/backend/mapper/TorrentMapper.java
index a00d3cf..3977ea4 100644
--- a/src/main/java/edu/bjtu/groupone/backend/mapper/TorrentMapper.java
+++ b/src/main/java/edu/bjtu/groupone/backend/mapper/TorrentMapper.java
@@ -1,9 +1,17 @@
 package edu.bjtu.groupone.backend.mapper;
 
 import edu.bjtu.groupone.backend.domain.entity.Torrent;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
 
 public interface TorrentMapper {
+    @Insert("insert into torrents (name,info_hash,size,uploader_id,url) values (#{name},#{infoHash},#{size},#{uploaderId},#{url})")
     void insertTorrent(Torrent torrent);
 
     Torrent selectByInfoHash(String infoHash);
+
+    @Select("select * from torrents")
+    List<Torrent> getTorrentList();
 }
diff --git a/src/main/java/edu/bjtu/groupone/backend/service/PeerTrafficService.java b/src/main/java/edu/bjtu/groupone/backend/service/PeerTrafficService.java
new file mode 100644
index 0000000..bb50b99
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/service/PeerTrafficService.java
@@ -0,0 +1,18 @@
+package edu.bjtu.groupone.backend.service;
+
+import edu.bjtu.groupone.backend.domain.entity.AnnounceEvent;
+
+import java.nio.ByteBuffer;
+
+public interface PeerTrafficService {
+    // 修改参数类型为ByteBuffer(ttorrent的peer_id实际类型)
+    void recordPeerTraffic(
+            String infoHash,
+            String ip,
+            ByteBuffer peerId,  // 原为String
+            long uploaded,
+            long downloaded,
+            long timestamp,
+            AnnounceEvent customEvent
+    );
+}
\ No newline at end of file
diff --git a/src/main/java/edu/bjtu/groupone/backend/service/TorrentService.java b/src/main/java/edu/bjtu/groupone/backend/service/TorrentService.java
index 5a00de5..9c51eef 100644
--- a/src/main/java/edu/bjtu/groupone/backend/service/TorrentService.java
+++ b/src/main/java/edu/bjtu/groupone/backend/service/TorrentService.java
@@ -4,8 +4,12 @@
 import org.springframework.core.io.Resource;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.util.List;
+
 public interface TorrentService {
     Torrent uploadTorrent(MultipartFile file, Long userId) throws Exception;
 
     Resource downloadTorrent(String infoHash) throws Exception;
+
+    List<Torrent> getTorrentList();
 }
\ No newline at end of file
diff --git a/src/main/java/edu/bjtu/groupone/backend/service/TrackerStarter.java b/src/main/java/edu/bjtu/groupone/backend/service/TrackerStarter.java
new file mode 100644
index 0000000..3ef3031
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/service/TrackerStarter.java
@@ -0,0 +1,116 @@
+package edu.bjtu.groupone.backend.service;
+
+import com.turn.ttorrent.tracker.TrackedPeer;
+import com.turn.ttorrent.tracker.TrackedTorrent;
+import com.turn.ttorrent.tracker.Tracker;
+
+import java.io.*;
+import java.net.InetSocketAddress;
+import java.net.URL;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+import javax.annotation.PostConstruct;
+
+import edu.bjtu.groupone.backend.config.TrafficAwareTracker;
+import edu.bjtu.groupone.backend.domain.entity.AnnounceEvent;
+import edu.bjtu.groupone.backend.domain.entity.Torrent;
+import edu.bjtu.groupone.backend.service.PeerTrafficService;
+import edu.bjtu.groupone.backend.service.TorrentService;
+import org.apache.commons.io.FileUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+@Component
+public class TrackerStarter {
+    private Tracker tracker;
+    private final Set<String> loadedTorrentNames = new HashSet<>();
+
+    @Autowired
+    private PeerTrafficService peerTrafficService;
+    @Autowired
+    private TorrentService torrentService;
+
+    @PostConstruct
+    public void startTracker() throws Exception {
+//        InetSocketAddress trackerAddress = new InetSocketAddress("0.0.0.0", 6969);
+//        tracker = new Tracker(trackerAddress);
+//        tracker.start();
+        InetSocketAddress address = new InetSocketAddress("0.0.0.0", 6969);
+        this.tracker = new TrafficAwareTracker(address, peerTrafficService);
+        this.tracker.start();
+        System.out.println("Tracker started with traffic monitoring");
+    }
+
+    @Scheduled(fixedRate = 10000L)
+    public void scanTorrentDirectory() throws IOException, NoSuchAlgorithmException {
+        List<Torrent> torrents = torrentService.getTorrentList();
+
+        for (Torrent torrent : torrents) {
+            String url = torrent.getUrl();
+            String fileName = extractFileNameFromUrl(url);
+
+            if (!loadedTorrentNames.contains(fileName)) {
+                try {
+                    // 创建临时文件(自动带 .torrent 后缀)
+                    File tempTorrentFile = File.createTempFile("torrent_", ".torrent");
+                    tempTorrentFile.deleteOnExit(); // JVM退出时删除
+
+                    // 下载内容到临时文件
+                    try (InputStream in = new URL(url).openStream();
+                         OutputStream out = new FileOutputStream(tempTorrentFile)) {
+                        in.transferTo(out);
+                    }
+
+                    // 加载并注册种子
+                    TrackedTorrent trackedTorrent = TrackedTorrent.load(tempTorrentFile);
+                    this.tracker.announce(trackedTorrent);
+                    loadedTorrentNames.add(fileName);
+
+                    System.out.println("Loaded torrent from OSS (temp file): " + fileName);
+                    tempTorrentFile.delete();
+                } catch (Exception e) {
+                    System.err.println("Failed to load torrent: " + fileName + ", error: " + e.getMessage());
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+
+    private String extractFileNameFromUrl(String url) {
+        return url.substring(url.lastIndexOf('/') + 1);
+    }
+
+@Scheduled(fixedRate = 15000L)
+public void collectPeerTraffic() {
+    try {
+        // 使用反射获取 Tracker 的 private 字段 torrents
+        java.lang.reflect.Field torrentsField = Tracker.class.getDeclaredField("torrents");
+        torrentsField.setAccessible(true);
+
+        @SuppressWarnings("unchecked")
+        Map<?, TrackedTorrent> torrents = (Map<?, TrackedTorrent>) torrentsField.get(tracker);
+
+        for (TrackedTorrent torrent : torrents.values()) {
+            for (TrackedPeer peer : torrent.getPeers().values()) {
+                if (peer.getUploaded() > 0 || peer.getDownloaded() > 0) {
+                    peerTrafficService.recordPeerTraffic(
+                            torrent.getHexInfoHash(),
+                            peer.getIp(),
+                            peer.getPeerId(),
+                            peer.getUploaded(),
+                            peer.getDownloaded(),
+                            System.currentTimeMillis() / 1000,
+                            AnnounceEvent.COMPLETED // 实际项目中你可以按需要换成 STARTED, STOPPED 等
+                    );
+                }
+            }
+        }
+    } catch (Exception e) {
+        System.err.println("Failed to collect peer traffic: " + e.getMessage());
+        e.printStackTrace();
+    }
+}
+}
+
diff --git a/src/main/java/edu/bjtu/groupone/backend/service/WorkService.java b/src/main/java/edu/bjtu/groupone/backend/service/WorkService.java
index eaff2e2..7aeebf1 100644
--- a/src/main/java/edu/bjtu/groupone/backend/service/WorkService.java
+++ b/src/main/java/edu/bjtu/groupone/backend/service/WorkService.java
@@ -1,35 +1,30 @@
 package edu.bjtu.groupone.backend.service;
 
+//import edu.bjtu.groupone.backend.model.Work;
 import edu.bjtu.groupone.backend.domain.dto.WorkResponse;
 import edu.bjtu.groupone.backend.domain.entity.Work;
 import edu.bjtu.groupone.backend.mapper.WorkMybatisMapper;
-//import edu.bjtu.groupone.backend.model.Work;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
-
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.List;
 import java.util.stream.Collectors;
-
 @Service
 public class WorkService {
     @Autowired
     private WorkMybatisMapper workMybatisMapper;
-
     @Autowired
     private CategoryService categoryService;
-
     public Page<WorkResponse> getWorks(Long categoryId, int page, int size) {
         List<Long> categoryIds = categoryService.getAllSubcategoryIds(categoryId);
         Pageable pageable = PageRequest.of(page-1, size);
         return workMybatisMapper.findByCategoryIdIn(categoryIds, pageable)
                 .map(this::convertToResponse);
     }
-
     private WorkResponse convertToResponse(Work work) {
         return new WorkResponse(
                 work.getId(),
@@ -41,16 +36,13 @@
                 work.getCreateTime()
         );
     }
-
     public void addWork(Work work) {
         work.setCreateTime(LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
         workMybatisMapper.save(work);
     }
-
     public void deleteWork(Long id) {
         workMybatisMapper.deleteById(id);
     }
-
     public void updateWork(Work work) {
         Work existing = workMybatisMapper.findById(work.getId());
         if (existing != null) {
@@ -58,7 +50,6 @@
         }
         workMybatisMapper.update(work);
     }
-
     public Work getWorkById(Long id) {
         return workMybatisMapper.findById(id);
     }
diff --git a/src/main/java/edu/bjtu/groupone/backend/service/impl/PeerTrafficServiceImpl.java b/src/main/java/edu/bjtu/groupone/backend/service/impl/PeerTrafficServiceImpl.java
new file mode 100644
index 0000000..2928398
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/service/impl/PeerTrafficServiceImpl.java
@@ -0,0 +1,25 @@
+package edu.bjtu.groupone.backend.service.impl;
+
+import edu.bjtu.groupone.backend.domain.entity.AnnounceEvent;
+import edu.bjtu.groupone.backend.service.PeerTrafficService;
+import org.springframework.stereotype.Service;
+
+import java.nio.ByteBuffer;
+
+@Service
+public class PeerTrafficServiceImpl implements PeerTrafficService {
+    @Override
+    public void recordPeerTraffic(String infoHash, String ip, ByteBuffer peerId,
+                                  long uploaded, long downloaded, long timestamp,
+                                  AnnounceEvent customEvent) {
+        // 在实现层转换peerId为String(如需)
+        String peerIdStr = (peerId != null) ?
+                new String(peerId.array()) : "unknown";
+
+        // 实际存储逻辑...
+        System.out.printf(
+                "Traffic: %s | %s | %s | Up: %d | Down: %d | Event: %s%n",
+                infoHash, ip, peerIdStr, uploaded, downloaded, customEvent
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/bjtu/groupone/backend/service/impl/TorrentServiceImpl.java b/src/main/java/edu/bjtu/groupone/backend/service/impl/TorrentServiceImpl.java
index a1dac1f..74c8216 100644
--- a/src/main/java/edu/bjtu/groupone/backend/service/impl/TorrentServiceImpl.java
+++ b/src/main/java/edu/bjtu/groupone/backend/service/impl/TorrentServiceImpl.java
@@ -5,9 +5,13 @@
 import edu.bjtu.groupone.backend.domain.entity.Torrent;
 import edu.bjtu.groupone.backend.domain.entity.User;
 import edu.bjtu.groupone.backend.service.TorrentService;
+import edu.bjtu.groupone.backend.utils.AliOSSUtils;
 import edu.bjtu.groupone.backend.utils.TorrentParserUtil;
 import java.io.File;
+import java.util.List;
 import java.util.Map;
+
+import org.hibernate.sql.Alias;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.io.FileSystemResource;
@@ -24,6 +28,9 @@
     @Autowired
     private UserMapper userMapper;
 
+    @Autowired
+    AliOSSUtils aliOSSUtils;
+
     public TorrentServiceImpl() {
     }
 
@@ -39,14 +46,14 @@
         }
 
         String savePath = pathToUse + File.separator + infoHash + ".torrent";
-        File dest = new File(savePath);
-        file.transferTo(dest);
+        String upload = aliOSSUtils.upload(file);
         User uploader = this.userMapper.selectById(userId);
         Torrent torrent = new Torrent();
         torrent.setInfoHash(infoHash);
         torrent.setName(name);
         torrent.setFilePath(savePath);
         torrent.setSize(size);
+        torrent.setUrl(upload);
         torrent.setUploaderId((long)uploader.getUserId());
         this.torrentMapper.insertTorrent(torrent);
         return torrent;
@@ -65,4 +72,9 @@
             }
         }
     }
+
+    @Override
+    public List<Torrent> getTorrentList() {
+        return torrentMapper.getTorrentList();
+    }
 }
diff --git a/src/main/java/edu/bjtu/groupone/backend/service/impl/UserServImpl.java b/src/main/java/edu/bjtu/groupone/backend/service/impl/UserServImpl.java
index ba2f9e9..19f5d93 100644
--- a/src/main/java/edu/bjtu/groupone/backend/service/impl/UserServImpl.java
+++ b/src/main/java/edu/bjtu/groupone/backend/service/impl/UserServImpl.java
@@ -139,6 +139,7 @@
     public List<User> getAllUsers() {
         return userMapper.selectAllUsers();
     }
+
     @Override
     public int getRemainingTasks(int userId) {
         User user = userMapper.selectUserById(userId);
diff --git a/src/main/java/edu/bjtu/groupone/backend/utils/GetTokenUserId.java b/src/main/java/edu/bjtu/groupone/backend/utils/GetTokenUserId.java
index b798f05..63dda65 100644
--- a/src/main/java/edu/bjtu/groupone/backend/utils/GetTokenUserId.java
+++ b/src/main/java/edu/bjtu/groupone/backend/utils/GetTokenUserId.java
@@ -6,10 +6,7 @@
 public class GetTokenUserId {
 
     public static String getUserId(HttpServletRequest request) {
-        String token = request.getHeader("Authorization");
-        if (token == null || !token.startsWith("Bearer ")) {
-            return null;
-        }
+        String token = request.getHeader("token");
         // 解析 JWT Token,获取用户 ID
         String jwt = token.substring(7); // 去掉 'Bearer ' 前缀
         Claims claims = JwtUtils.parseJwt(jwt); // 从 JWT 中获取用户 ID
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 165698e..2e95546 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,11 +1,6 @@
 # ??MySQL??
 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
-spring.datasource.url=jdbc:mysql://rm-cn-qzy4ah2qt0008vfo.rwlb.rds.aliyuncs.com:3306/smart_course_platform\
-?useSSL=false\
-&serverTimezone=Asia/Shanghai\
-&characterEncoding=utf8\
-&allowPublicKeyRetrieval=true
-
+spring.datasource.url=jdbc:mysql://rm-cn-qzy4ah2qt0008vfo.rwlb.rds.aliyuncs.com:3306/smart_course_platform?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8&allowPublicKeyRetrieval=true
 spring.datasource.username=wangy
 spring.datasource.password=Wyt2005011600
 # src/main/resources/application.properties
@@ -27,4 +22,6 @@
 # ????????
 mybatis.mapper-locations=classpath:mapper/*.xml
 
-torrent.storage.path=./torrent
\ No newline at end of file
+torrent.storage.path=./torrent
+
+server.address=0.0.0.0
\ No newline at end of file
diff --git a/src/test/java/edu/bjtu/groupone/backend/DatabaseConnectionTest.java b/src/test/java/edu/bjtu/groupone/backend/DatabaseConnectionTest.java
new file mode 100644
index 0000000..e2e565f
--- /dev/null
+++ b/src/test/java/edu/bjtu/groupone/backend/DatabaseConnectionTest.java
@@ -0,0 +1,28 @@
+package edu.bjtu.groupone.backend;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@SpringBootTest
+public class DatabaseConnectionTest {
+
+    @Autowired
+    private DataSource dataSource;
+
+    @Test
+    public void testDatabaseConnection() {
+        try (Connection connection = dataSource.getConnection()) {
+            assertNotNull(connection);
+            System.out.println("✅ 数据库连接成功: " + connection.getMetaData().getURL());
+            System.out.println("📌 数据库用户: " + connection.getMetaData().getUserName());
+        } catch (Exception e) {
+            fail("❌ 数据库连接失败: " + e.getMessage());
+        }
+    }
+}
diff --git a/src/test/java/edu/bjtu/groupone/backend/TorrentControllerTest.java b/src/test/java/edu/bjtu/groupone/backend/TorrentControllerTest.java
deleted file mode 100644
index 86573d8..0000000
--- a/src/test/java/edu/bjtu/groupone/backend/TorrentControllerTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package edu.bjtu.groupone.backend;
-
-import edu.bjtu.groupone.backend.api.TorrentController;
-import edu.bjtu.groupone.backend.domain.entity.Torrent;
-import edu.bjtu.groupone.backend.service.TorrentService;
-import edu.bjtu.groupone.backend.utils.GetTokenUserId;
-import jakarta.servlet.http.HttpServletRequest;
-import org.assertj.core.api.Assertions;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockedStatic;
-import org.mockito.Mockito;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.core.io.ByteArrayResource;
-import org.springframework.core.io.Resource;
-import org.springframework.http.ResponseEntity;
-import org.springframework.mock.web.MockMultipartFile;
-import org.springframework.web.multipart.MultipartFile;
-
-@ExtendWith({MockitoExtension.class})
-public class TorrentControllerTest {
-    @Mock
-    private TorrentService torrentService;
-    @InjectMocks
-    private TorrentController torrentController;
-    @Mock
-    private HttpServletRequest request;
-
-    public TorrentControllerTest() {
-    }
-
-    @Test
-    public void uploadTorrent_shouldReturnTorrent_whenUserIdValid() throws Exception {
-        MultipartFile file = new MockMultipartFile("file", "test.torrent", "application/x-bittorrent", "dummy data".getBytes());
-        MockedStatic<GetTokenUserId> utilities = Mockito.mockStatic(GetTokenUserId.class);
-
-        try {
-            utilities.when(() -> {
-                GetTokenUserId.getUserId(this.request);
-            }).thenReturn("123");
-            Torrent expectedTorrent = new Torrent();
-            expectedTorrent.setId(1L);
-            expectedTorrent.setName("testfile");
-            expectedTorrent.setInfoHash("fakehash");
-            expectedTorrent.setSize(12345L);
-            expectedTorrent.setUploaderId(123L);
-            Mockito.when(this.torrentService.uploadTorrent(file, 123L)).thenReturn(expectedTorrent);
-            ResponseEntity<?> response = this.torrentController.uploadTorrent(file, this.request);
-            Assertions.assertThat(response.getStatusCodeValue()).isEqualTo(200);
-            Assertions.assertThat(response.getBody()).isEqualTo(expectedTorrent);
-            ((TorrentService)Mockito.verify(this.torrentService, Mockito.times(1))).uploadTorrent(file, 123L);
-        } catch (Throwable var6) {
-            if (utilities != null) {
-                try {
-                    utilities.close();
-                } catch (Throwable var5) {
-                    var6.addSuppressed(var5);
-                }
-            }
-
-            throw var6;
-        }
-
-        if (utilities != null) {
-            utilities.close();
-        }
-
-    }
-
-    @Test
-    public void uploadTorrent_shouldReturnUnauthorized_whenUserIdNull() throws Exception {
-        MockedStatic<GetTokenUserId> utilities = Mockito.mockStatic(GetTokenUserId.class);
-
-        try {
-            utilities.when(() -> {
-                GetTokenUserId.getUserId(this.request);
-            }).thenReturn((Object)null);
-            MultipartFile file = new MockMultipartFile("file", "test.torrent", "application/x-bittorrent", "dummy data".getBytes());
-            ResponseEntity<?> response = this.torrentController.uploadTorrent(file, this.request);
-            Assertions.assertThat(response.getStatusCodeValue()).isEqualTo(401);
-            Assertions.assertThat(response.getBody()).isEqualTo("Token无效或缺失");
-            Mockito.verifyNoInteractions(new Object[]{this.torrentService});
-        } catch (Throwable var5) {
-            if (utilities != null) {
-                try {
-                    utilities.close();
-                } catch (Throwable var4) {
-                    var5.addSuppressed(var4);
-                }
-            }
-
-            throw var5;
-        }
-
-        if (utilities != null) {
-            utilities.close();
-        }
-
-    }
-
-    @Test
-    public void downloadTorrent_shouldReturnResource_whenFound() throws Exception {
-        String infoHash = "fakehash";
-        byte[] data = "torrent data".getBytes();
-        Resource resource = new ByteArrayResource(data);
-        Mockito.when(this.torrentService.downloadTorrent(infoHash)).thenReturn(resource);
-        ResponseEntity<Resource> response = this.torrentController.downloadTorrent(infoHash);
-        Assertions.assertThat(response.getStatusCodeValue()).isEqualTo(200);
-        Assertions.assertThat(response.getHeaders().getFirst("Content-Disposition")).isEqualTo("attachment; filename=\"" + infoHash + ".torrent\"");
-        Assertions.assertThat((Resource)response.getBody()).isEqualTo(resource);
-        ((TorrentService)Mockito.verify(this.torrentService, Mockito.times(1))).downloadTorrent(infoHash);
-    }
-
-    @Test
-    public void downloadTorrent_shouldReturnNotFound_whenException() throws Exception {
-        String infoHash = "notexist";
-        Mockito.when(this.torrentService.downloadTorrent(infoHash)).thenThrow(new Throwable[]{new RuntimeException("Not found")});
-        ResponseEntity<Resource> response = this.torrentController.downloadTorrent(infoHash);
-        Assertions.assertThat(response.getStatusCodeValue()).isEqualTo(404);
-        Assertions.assertThat((Resource)response.getBody()).isNull();
-        ((TorrentService)Mockito.verify(this.torrentService, Mockito.times(1))).downloadTorrent(infoHash);
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/edu/bjtu/groupone/backend/TorrentServiceImplTest.java b/src/test/java/edu/bjtu/groupone/backend/TorrentServiceImplTest.java
deleted file mode 100644
index f25f7cb..0000000
--- a/src/test/java/edu/bjtu/groupone/backend/TorrentServiceImplTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package edu.bjtu.groupone.backend;
-
-import edu.bjtu.groupone.backend.mapper.TorrentMapper;
-import edu.bjtu.groupone.backend.mapper.UserMapper;
-import edu.bjtu.groupone.backend.domain.entity.Torrent;
-import edu.bjtu.groupone.backend.domain.entity.User;
-import edu.bjtu.groupone.backend.service.impl.TorrentServiceImpl;
-import java.io.File;
-import java.io.FileInputStream;
-import org.assertj.core.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.core.io.Resource;
-import org.springframework.mock.web.MockMultipartFile;
-
-@ExtendWith({MockitoExtension.class})
-public class TorrentServiceImplTest {
-    @Mock
-    private TorrentMapper torrentMapper;
-    @Mock
-    private UserMapper userMapper;
-    @InjectMocks
-    private TorrentServiceImpl torrentService;
-
-    public TorrentServiceImplTest() {
-    }
-
-    @BeforeEach
-    void setup() {
-    }
-
-    @Test
-    public void uploadTorrent_shouldSaveAndReturnTorrent() throws Exception {
-        File file = new File("torrents/22301024-王玉涛.doc.torrent");
-        Assertions.assertThat(file.exists()).isTrue();
-        FileInputStream inputStream = new FileInputStream(file);
-        MockMultipartFile mockFile = new MockMultipartFile("file", "22301024-王玉涛.doc.torrent", "application/x-bittorrent", inputStream);
-        ((TorrentMapper)Mockito.doNothing().when(this.torrentMapper)).insertTorrent((Torrent)Mockito.any(Torrent.class));
-        User fakeUser = new User();
-        fakeUser.setUserId(100);
-        Mockito.when(this.userMapper.selectById(100L)).thenReturn(fakeUser);
-        Torrent result = this.torrentService.uploadTorrent(mockFile, 100L);
-        Assertions.assertThat(result).isNotNull();
-        Assertions.assertThat(result.getInfoHash()).isNotBlank();
-        Assertions.assertThat(result.getName()).isNotBlank();
-        Assertions.assertThat(result.getUploaderId()).isEqualTo(100L);
-        ((TorrentMapper)Mockito.verify(this.torrentMapper, Mockito.times(1))).insertTorrent((Torrent)Mockito.any(Torrent.class));
-    }
-
-    @Test
-    public void downloadTorrent_shouldReturnResource_whenTorrentExists() throws Exception {
-        File tempFile = File.createTempFile("test-torrent-", ".torrent");
-        tempFile.deleteOnExit();
-        Torrent fakeTorrent = new Torrent();
-        fakeTorrent.setInfoHash("fakeinfohash123");
-        fakeTorrent.setFilePath(tempFile.getAbsolutePath());
-        Mockito.when(this.torrentMapper.selectByInfoHash("fakeinfohash123")).thenReturn(fakeTorrent);
-        Resource resource = this.torrentService.downloadTorrent("fakeinfohash123");
-        Assertions.assertThat(resource).isNotNull();
-        Assertions.assertThat(resource.exists()).isTrue();
-        Assertions.assertThat(resource.getFile().getAbsolutePath()).isEqualTo(tempFile.getAbsolutePath());
-    }
-}
diff --git a/src/test/java/edu/bjtu/groupone/backend/WorkServiceTest.java b/src/test/java/edu/bjtu/groupone/backend/WorkServiceTest.java
index b3871da..44a05b6 100644
--- a/src/test/java/edu/bjtu/groupone/backend/WorkServiceTest.java
+++ b/src/test/java/edu/bjtu/groupone/backend/WorkServiceTest.java
@@ -1,10 +1,13 @@
 package edu.bjtu.groupone.backend;
 
+import com.turn.ttorrent.tracker.Tracker;
+import edu.bjtu.groupone.backend.config.TrafficAwareTracker;
 import edu.bjtu.groupone.backend.domain.dto.WorkResponse;
 import edu.bjtu.groupone.backend.domain.entity.Category;
 import edu.bjtu.groupone.backend.domain.entity.Work;
 import edu.bjtu.groupone.backend.mapper.WorkMybatisMapper;
 import edu.bjtu.groupone.backend.service.CategoryService;
+import edu.bjtu.groupone.backend.service.PeerTrafficService;
 import edu.bjtu.groupone.backend.service.WorkService;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -12,10 +15,14 @@
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageImpl;
 import org.springframework.data.domain.PageRequest;
 
+import java.io.IOException;
+import java.net.InetSocketAddress;
 import java.util.Arrays;
 import java.util.List;
 
@@ -23,6 +30,7 @@
 import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
+@SpringBootTest
 @ExtendWith(MockitoExtension.class)
 class WorkServiceTest {
 
@@ -117,4 +125,14 @@
         List<WorkResponse> result = workService.getWorksByAuthor("李四");
         assertTrue(result.isEmpty());
     }
+
+    @Autowired
+    PeerTrafficService peerTrafficService;
+    @Test
+    public  void test() throws IOException {
+        Tracker tracker;
+        InetSocketAddress trackerAddress = new InetSocketAddress("0.0.0.0", 6969);
+        tracker = new Tracker(trackerAddress);
+        tracker.start();
+    }
 }
\ No newline at end of file
diff --git "a/torrents/22301024-\347\216\213\347\216\211\346\266\233.doc.torrent" "b/torrents/22301024-\347\216\213\347\216\211\346\266\233.doc.torrent"
deleted file mode 100644
index 5786afb..0000000
--- "a/torrents/22301024-\347\216\213\347\216\211\346\266\233.doc.torrent"
+++ /dev/null
Binary files differ
diff --git "a/torrents/\345\275\251\346\216\222.mp4.torrent" "b/torrents/\345\275\251\346\216\222.mp4.torrent"
new file mode 100644
index 0000000..f47670b
--- /dev/null
+++ "b/torrents/\345\275\251\346\216\222.mp4.torrent"
Binary files differ