上传下载种子,上传下载量魔力值

Change-Id: I790145a3d252a55c956e63ef99c862b5da835128
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..8726b47
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,11 @@
+# 使用 OpenJDK 作为基础镜像
+FROM openjdk:11-jre-slim
+
+# 将 JAR 包复制到容器中
+COPY target/PTPlatform-0.0.1-SNAPSHOT.jar /app/myapp.jar
+
+# 配置容器启动时的命令
+ENTRYPOINT ["java", "-jar", "/app/myapp.jar"]
+
+# 暴露端口
+EXPOSE 8088
diff --git a/pom.xml b/pom.xml
index 7b65b05..671fec8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -144,6 +144,22 @@
         </dependency>
 
 
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <!-- Jakarta Bean Validation API -->
+        <dependency>
+            <groupId>jakarta.validation</groupId>
+            <artifactId>jakarta.validation-api</artifactId>
+        </dependency>
+
+        <!-- Hibernate Validator(实现) -->
+        <dependency>
+            <groupId>org.hibernate.validator</groupId>
+            <artifactId>hibernate-validator</artifactId>
+        </dependency>
+
     </dependencies>
 
     <build>
diff --git a/src/main/java/com/ptp/ptplatform/controller/InviteCodeController.java b/src/main/java/com/ptp/ptplatform/controller/InviteCodeController.java
index 3950ee8..46b5b04 100644
--- a/src/main/java/com/ptp/ptplatform/controller/InviteCodeController.java
+++ b/src/main/java/com/ptp/ptplatform/controller/InviteCodeController.java
@@ -2,7 +2,7 @@
 
 
 import com.ptp.ptplatform.entity.INVITE_CODE;
-import com.ptp.ptplatform.entity.User;
+import com.ptp.ptplatform.entity.USER;
 import com.ptp.ptplatform.mapper.InviteCodeMapper;
 import com.ptp.ptplatform.mapper.UserMapper;
 import com.ptp.ptplatform.utils.Result;
@@ -27,7 +27,7 @@
     //使用魔力值兑换邀请码
     @PostMapping("/generate")
     public Result generateInviteCode(HttpServletRequest request) {
-        User user = userController.getUserInRequest(request);
+        USER user = userController.getUserInRequest(request);
         System.out.println(user.getMagicPoints());
         if (user.getMagicPoints() >= 10) {
             user.generateInviteCode();
@@ -47,7 +47,7 @@
     //用户获取持有的邀请码
     @GetMapping("/userInviteCode")
     public Result userInviteCode(HttpServletRequest request) {
-        User user = userController.getUserInRequest(request);
+        USER user = userController.getUserInRequest(request);
 
         List<INVITE_CODE> inviteCode = inviteCodeMapper.selectByUser(user.getUsername());
         return Result.ok().data("inviteCode", inviteCode);
diff --git a/src/main/java/com/ptp/ptplatform/controller/TorrentController.java b/src/main/java/com/ptp/ptplatform/controller/TorrentController.java
index b44bc76..2b18372 100644
--- a/src/main/java/com/ptp/ptplatform/controller/TorrentController.java
+++ b/src/main/java/com/ptp/ptplatform/controller/TorrentController.java
@@ -1,18 +1,18 @@
 package com.ptp.ptplatform.controller;
 
-import com.ptp.ptplatform.entity.DISCOUNT;
-import com.ptp.ptplatform.entity.TORRENT;
-import com.ptp.ptplatform.entity.User;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.ptp.ptplatform.entity.*;
 import com.ptp.ptplatform.mapper.DiscountMapper;
 import com.ptp.ptplatform.mapper.TorrentMapper;
 import com.ptp.ptplatform.mapper.UserMapper;
+import com.ptp.ptplatform.mapper.DownloadTorrentMapper;
 import com.ptp.ptplatform.service.ClientService;
 import com.ptp.ptplatform.service.TrackerService;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.ptp.ptplatform.entity.TorrentComment;
 import com.ptp.ptplatform.service.TorrentCommentService;
 import com.ptp.ptplatform.service.TorrentService;
 import com.ptp.ptplatform.utils.Result;
+import com.ptp.ptplatform.utils.SizeCalculation;
 import com.turn.ttorrent.bcodec.BDecoder;
 import com.turn.ttorrent.bcodec.BEValue;
 import com.turn.ttorrent.tracker.TrackedPeer;
@@ -21,8 +21,10 @@
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.AllArgsConstructor;
+import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
@@ -33,7 +35,6 @@
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.nio.file.StandardCopyOption;
-import java.time.Duration;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -41,8 +42,6 @@
 import java.util.UUID;
 import java.util.Map;
 
-import static com.ptp.ptplatform.utils.JwtUtils.getClaimByToken;
-
 //实现种子的增删改查
 @RestController
 @RequestMapping("/torrent")
@@ -56,12 +55,15 @@
     @Resource
     private UserMapper userMapper;
     @Resource
-    private UserController userController;
-    @Resource
     private DiscountMapper discountMapper;
+    @Resource
+    private DownloadTorrentMapper downloadTorrentMapper;
 
-    private TrackerService ts = new TrackerService();
-    private ClientService cs = new ClientService();
+    @Resource
+    private UserController userController;
+
+    private TrackerService trackerservice = new TrackerService(new ObjectMapper());
+    private ClientService clientservice = new ClientService();
 
     @GetMapping("/{Id}")
     public Result getPost(@PathVariable int Id) {
@@ -141,7 +143,7 @@
     @PostConstruct //启动项目时候自动启动tracker服务器
     public String startTS() {
         try {
-            ts.startTracker();
+            trackerservice.startTracker();
             System.out.println("成功启动tracker服务器");
             return "成功启动tracker服务器";
         } catch (Exception e) {
@@ -150,17 +152,28 @@
         }
     }
 
+    @PutMapping("/closeTS")
+    public Result closeTS() {
+        try {
+            trackerservice.stopTracker();
+            System.out.println("成功关闭tracker服务器");
+            return Result.ok();
+        } catch (Exception e) {
+            System.out.println("启动失败: " + e.getMessage());
+            return Result.error(500);
+        }
+    }
+
+
     //上传、下载、删除操作
     //post 要添加数据库 文件和tracker的信息
-    @PostMapping
-    public Result addTorrent(HttpServletRequest request, @RequestBody TORRENT torrent) throws IOException {
+    //上传torrent
+    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+    public Result addTorrent(HttpServletRequest request, @RequestPart("file") MultipartFile file, @RequestPart("body") TORRENT torrent) throws IOException {
+        USER user = userController.getUserInRequest(request);
 
         // 1. 检查源文件
-        File file = new File(torrent.getFilePath());
-        if (!file.exists() || !file.isFile()) {
-            return Result.error(404).data("message", "文件不存在或路径无效");
-        }
-        if (!file.getName().endsWith(".torrent")) {
+        if (!file.getOriginalFilename().endsWith(".torrent")) {
             return Result.error(403).data("message", "仅支持.torrent文件");
         }
 
@@ -172,38 +185,56 @@
             return Result.error(404).data("message", "无法创建存储目录");
         }
 
-        // 3. 复制文件
-        File uploadedFile = new File(torrentsFolder, file.getName());
+        // 3. 获取文件名并创建上传文件路径
+        String originalFilename = file.getOriginalFilename();
+        File uploadedFile = new File(torrentsFolder, originalFilename);
+
+        // 4. 处理文件保存
         try {
-            Files.copy(file.toPath(), uploadedFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
+            // 如果文件已存在,给文件名加上一个时间戳,避免冲突
+            if (uploadedFile.exists()) {
+                String newFilename = System.currentTimeMillis() + "_" + originalFilename;
+                uploadedFile = new File(torrentsFolder, newFilename);
+            }
+
+            // 复制文件
+            Files.copy(file.getInputStream(), uploadedFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
             System.out.println("文件保存成功: " + uploadedFile.getAbsolutePath());
         } catch (IOException e) {
-            System.out.println("文件复制失败" + e);
+            System.out.println("文件复制失败: " + e);
             return Result.error(404).data("message", "文件保存失败: " + e.getMessage());
         }
 
-        // 4. 处理Tracker逻辑
+        // 5. 处理Tracker逻辑
         try {
-            TrackedTorrent tt = ts.addTrackedTorrent(uploadedFile.getAbsolutePath());
-            TrackedPeer tp = createTrackedPeer(tt);
-            tt.addPeer(tp);
-
             long fileSize = getTorrentSizeManual(uploadedFile);
-            System.out.println("fileSize" + fileSize);
+            System.out.println("fileSize: " + fileSize);
 
+            TrackedTorrent tt = trackerservice.addTrackedTorrent(uploadedFile.getAbsolutePath());
 
-            // 5. 数据库操作
+            // 6. 数据库操作
             torrent.setHash(tt.getHexInfoHash());
             torrent.setSize(fileSize);
-            torrent.setUsername(getClaimByToken(request.getHeader("Authorization")).getSubject());
+            torrent.setUsername(user.getUsername());
             torrent.setFilePath(uploadedFile.getAbsolutePath());
+            torrent.setCreateTime(LocalDateTime.now());
+            torrentMapper.insertTorrent(torrent);
 
-            if (torrentMapper.insertTorrent(torrent) == 1) {
-                return Result.ok().data("message", "上传成功")
-                        .data("path", uploadedFile.getAbsolutePath());
-            }
+            TrackedPeer tp = createTrackedPeer(request, tt, torrent.getId());
+            tp.update(TrackedPeer.PeerState.COMPLETED, fileSize, 0, 0);
+            tt.addPeer(tp);
+
+            // 上传种子本人也上传一个下载记录,计算保种积分
+            DOWNLOAD_TORRENT dt = new DOWNLOAD_TORRENT(user.getUsername(), torrent.getId(), torrent.getSize());
+            downloadTorrentMapper.insertDownloadTorrent(dt);
+            user.updateUpload(torrent.getSize());// 上传不计算折扣
+            userMapper.updateUser(user);
+
+            return Result.ok().data("message", "上传成功")
+                    .data("path", uploadedFile.getAbsolutePath());
+
         } catch (Exception e) {
-            System.out.println("Tracker或数据库操作失败" + e);
+            System.out.println("Tracker或数据库操作失败: " + e);
             uploadedFile.delete(); // 回滚:删除已保存的文件
         }
 
@@ -211,83 +242,88 @@
     }
 
     //根据本机创建peer对象
-    public static TrackedPeer createTrackedPeer(TrackedTorrent torrent) {
-        try {
-            // 获取本机IP地址
-            InetAddress inetAddress = InetAddress.getLocalHost();
-            String ip = inetAddress.getHostAddress(); // 本机IP地址
+    public TrackedPeer createTrackedPeer(HttpServletRequest request, TrackedTorrent trackedTorrent, int torrentid) throws UnknownHostException {
+        // 修改为获取request请求的地址
+        //String ip = request.getRemoteAddr();
 
-            // 假设使用一个随机的端口,这个端口可以根据需求调整
-            int port = 6881; // 这里使用固定端口,也可以通过动态方式分配
+        //获取本机地址
+        InetAddress inetAddress = InetAddress.getLocalHost();
+        String ip = inetAddress.getHostAddress();
+        USER user = userController.getUserInRequest(request);
 
-            // 创建Peer ID (可以随机生成或从其他地方获取)
-            // 这里使用UUID来生成一个随机的 Peer ID
-            String peerIdString = UUID.randomUUID().toString();
-            ByteBuffer peerId = ByteBuffer.wrap(peerIdString.getBytes());
+        int port = 6881; // 使用固定端口
 
-            // 创建TrackedPeer实例
-            TrackedPeer trackedPeer = new TrackedPeer(torrent, ip, port, peerId);
+        // 创建Peer ID
+        String peerIdString = UUID.randomUUID().toString();
+        ByteBuffer peerId = ByteBuffer.wrap(peerIdString.getBytes());
 
-            return trackedPeer;
-        } catch (UnknownHostException e) {
-            e.printStackTrace();
-            return null;
-        }
+        // 创建TrackedPeer实例
+        TrackedPeer trackedPeer = new TrackedPeer(trackedTorrent, ip, port, peerId);
+
+//        DOWNLOAD_TORRENT uwp = new DOWNLOAD_TORRENT(user.getUsername(), torrentid);
+
+
+        return trackedPeer;
     }
 
     //种子下载 根据id获取数据
     // 能成功获取到数据
     // 能够根据此值,完成对相关数据的修改
-    // getDownloadedBytes:3063963
-    //getUploadedBytes:0
     @GetMapping("/downloadTorrent")
     public Result getTorrent(HttpServletRequest request, int id, String downloadPath) throws Exception {
         TORRENT torrent = torrentMapper.selectByID(id);
+        USER userDownload = userController.getUserInRequest(request);
+        USER userUpload = userMapper.selectByUsername(torrent.getUsername());
+
+        //检查分享率是否允许下载
+
+        if(!userDownload.allowDownload(torrent.getSize())){
+            return Result.error(409).message("分享率不足下载此资源");
+        }
 
         // 下载完成
         // 传入变量:下载种子的id 下载到文件的路径
-//        cs.downloadTorrent(torrent.getFilePath(), downloadPath);
-//
-//        //管理种子相关数据
-//        TorrentStatistic ts = cs.getStatistics(torrent.getFilePath());
+        clientservice.downloadTorrent(torrent.getFilePath(), downloadPath, userDownload.getUsername());
 
-        //修改用户对应的信息
-        User downloadUser = userController.getUserInRequest(request);
-        User uploadUser = userMapper.selectByUsername(torrent.getUsername());
+        TrackedTorrent tt = trackerservice.getTrackedTorrent(torrent.getHash());
+        TrackedPeer tp = createTrackedPeer(request, tt, torrent.getId());
+        tp.update(TrackedPeer.PeerState.COMPLETED, 0, 0, torrent.getSize());
+        tt.addPeer(tp);
 
-        long up = 30000L;
-        long down = 30000L;
+        trackerservice.serTracker();
 
-//        long up = torrent.getSize();
-//        long down = up;
+        //更新上传量和下载量
+        userDownload.updateDownload(SizeCalculation.getDownload(torrent.getSize(), discountMapper, torrent.getCreateTime()));
+        userUpload.updateUpload(SizeCalculation.getUpload(torrent.getSize(), discountMapper, torrent.getCreateTime()));
+        userMapper.updateUser(userUpload);
+        userMapper.updateUser(userDownload);
+        DOWNLOAD_TORRENT dt = new DOWNLOAD_TORRENT(userDownload.getUsername(), torrent.getId(), torrent.getSize());
+        downloadTorrentMapper.insertDownloadTorrent(dt);
 
-        // 计算折扣
-        DISCOUNT discount = discountMapper.selectCurrentDiscount();
-        if (Duration.between(torrent.getCreateTime(), LocalDateTime.now()).toDays() <= 7) {
-            up = up * 2;
-            down = down / 2;
-        }
-        if (discount != null) {
-            if (discount.getDiscountType().equals("FREE")) {
-                down = 0;
-            } else if (discount.getDiscountType().equals("HALF")) {
-                down = down / 2;
-            } else if (discount.getDiscountType().equals("DOUBLE")) {
-                up = 2 * up;
+        return Result.ok().data("fileName", torrent.getTorrentName())
+                .data("taskId", torrent.getHash());
+    }
+
+    @GetMapping("/getProgress")
+    public Result getProgress(HttpServletRequest request) throws Exception {
+        USER user = userController.getUserInRequest(request);
+        String username = user.getUsername();
+        List<String[]> tasks = clientservice.getTasks();
+        Map<String, Double> progresses = new HashMap<>();
+
+        for (String[] task : tasks) {
+            if (task[0].equals(username)) {
+                Double progress = 0.0;  // 默认进度为0.0
+                try {
+                    progress = clientservice.getDownloadProgress(task[1]);  // 获取下载进度
+                } catch (IllegalStateException e) {
+                    // 如果发生异常,则进度保持为0.0,并继续处理下一个任务
+                    progress = 0.0;
+                }
+                progresses.put(task[1], progress);
             }
         }
-
-
-        downloadUser.updateDownload(down);
-        uploadUser.updateUpload(up);
-
-//        downloadUser.updateDownload(ts.getDownloadedBytes());
-//        uploadUser.updateUpload(ts.getDownloadedBytes());
-        userMapper.updateUser(downloadUser);
-        userMapper.updateUser(uploadUser);
-
-
-        return Result.ok().data("torrent", torrent);
+        return Result.ok().data("progresses", progresses);
     }
 
     //根据id删除
@@ -378,7 +414,7 @@
             @RequestParam(defaultValue = "1") int page,
             @RequestParam(defaultValue = "5") int size) throws Exception {
 
-        User user = userController.getUserInRequest(request);
+        USER user = userController.getUserInRequest(request);
         int offset = (page - 1) * size;
         List<TORRENT> pagedList = torrentMapper.selectTorrentByUsernameWithPage(user.getUsername(), offset, size);
         int total = torrentMapper.countByUsername(user.getUsername());
@@ -392,6 +428,20 @@
                 .data("total", pageResult.getTotal());
     }
 
+
+    // 测试类
+    @GetMapping("/test")
+    public Result updatePeer(HttpServletRequest request) throws IOException {
+        TORRENT torrent = torrentMapper.selectByID(90);
+        USER user = userController.getUserInRequest(request);
+
+        DOWNLOAD_TORRENT dt = new DOWNLOAD_TORRENT(user.getUsername(), torrent.getId(), torrent.getSize());
+        downloadTorrentMapper.insertDownloadTorrent(dt);
+        user.updateUpload(torrent.getSize());// 上传不计算折扣
+        userMapper.updateUser(user);
+        return Result.ok();
+    }
+
     //获取torrent文件大小
     public static long getTorrentSizeManual(File torrentFile) throws IOException {
         byte[] data = Files.readAllBytes(torrentFile.toPath());
@@ -414,4 +464,5 @@
         }
         throw new IOException("Invalid torrent file");
     }
+
 }
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/controller/UserController.java b/src/main/java/com/ptp/ptplatform/controller/UserController.java
index 7dc0a7d..f03af76 100644
--- a/src/main/java/com/ptp/ptplatform/controller/UserController.java
+++ b/src/main/java/com/ptp/ptplatform/controller/UserController.java
@@ -4,6 +4,7 @@
 package com.ptp.ptplatform.controller;
 
 import com.ptp.ptplatform.entity.*;
+import com.ptp.ptplatform.mapper.DownloadTorrentMapper;
 import com.ptp.ptplatform.mapper.UserMapper;
 import com.ptp.ptplatform.mapper.InviteCodeMapper;
 import com.ptp.ptplatform.service.UserLevelService;
@@ -13,12 +14,11 @@
 import org.springframework.web.bind.annotation.*;
 import com.ptp.ptplatform.utils.Result;
 import com.ptp.ptplatform.utils.JwtUtils;
-import com.ptp.ptplatform.entity.User;
+import com.ptp.ptplatform.entity.USER;
 
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
 
 @RestController
 @RequestMapping("/user")
@@ -31,23 +31,25 @@
     private InviteCodeMapper inviteCodeMapper;
     @Autowired
     private UserLevelService userLevelService;
+    @Autowired
+    private DownloadTorrentMapper downloadTorrentMapper;
 
     //个人中心获取用户登录信息
     @GetMapping("/info") //获取
     public Result info(HttpServletRequest request) {
-        User user = this.getUserInRequest(request);
+        USER user = this.getUserInRequest(request);
         return Result.ok().data("info", user);
     }
 
     @PostMapping("/login") //用户登录
     public Result login(String username, String password) {
-        User user = userMapper.selectByUsername(username);
+        USER user = userMapper.selectByUsername(username);
 
         // 检查用户是否存在
         if (user == null) {
             return Result.error(404).setMessage("用户不存在");
         }
-        if(user.getAuthority() == User.Authority.BAN) {
+        if(user.getAuthority() == USER.Authority.BAN) {
             return Result.error(403).setMessage("用户被封禁,禁止登录");
         }
 
@@ -67,7 +69,7 @@
 
     @PostMapping("/regist")
     public Result regist(String username, String password, String code) {
-        User userCheck = userMapper.selectByUsername(username);
+        USER userCheck = userMapper.selectByUsername(username);
         if (userCheck == null) {
             //获取邀请码
             INVITE_CODE inviteCode = inviteCodeMapper.selectByCode(code);
@@ -76,7 +78,7 @@
 
                 if (!inviteCode.getIsUsed()) {
                     Date time = new Date();
-                    User user = new User(username, password, time);
+                    USER user = new USER(username, password, time);
 
                     userMapper.insertUser(user);
                     inviteCodeMapper.updateCodeUser(code);
@@ -100,7 +102,7 @@
     //获取允许下载额度的相关信息
     @GetMapping("/allowDownload")
     public Result allowDownload(HttpServletRequest request) {
-        User user = this.getUserInRequest(request);
+        USER user = this.getUserInRequest(request);
         // 总额度 已经使用 剩余 单位是GB
         int totalSize = SizeCalculation.byteToGB(user.getUpload());
         int usedSize = SizeCalculation.byteToGB(user.getDownload());
@@ -118,7 +120,7 @@
     //修改用户密码
     @PutMapping("/password")
     public Result updatePassword(HttpServletRequest request, @RequestBody Map<String, String> passwordMap) {
-        User user = this.getUserInRequest(request);
+        USER user = this.getUserInRequest(request);
 
         String oldPassword = passwordMap.get("oldPassword");
         String newPassword = passwordMap.get("newPassword");
@@ -136,12 +138,12 @@
     @GetMapping("/searchUser")
     public Result searchUser(@RequestParam(value = "key", required = false) String key, HttpServletRequest request) {
         // 从请求中获取当前用户
-        User user = this.getUserInRequest(request);
+        USER user = this.getUserInRequest(request);
         String username = user.getUsername();
 
         // 检查是否提供了搜索关键词
         if (key != null && !key.isEmpty()) {
-            List<User> searchResults = userMapper.searchUsername(username, key);
+            List<USER> searchResults = userMapper.searchUsername(username, key);
             return Result.ok().data("data", searchResults);
         } else {
             // 如果没有提供关键词,返回默认结果或者一个错误消息
@@ -152,11 +154,12 @@
     //获取到全部用户
     @GetMapping("/allUser")
     public Result getAllUser(){
-        List<User> users = userMapper.selectAllUsers();
+        List<USER> users = userMapper.selectAllUsers();
 
         return Result.ok().data("data", users);
     }
 
+
     //修改用户权限
     // 传入数据 username 要修改到的权限
     @PutMapping("/changeAuthority")
@@ -164,8 +167,8 @@
         String authority = authorityMap.get("authority");
         String changeUsername = authorityMap.get("changeUsername");
 
-        User changeUser = userMapper.selectByUsername(changeUsername);
-        changeUser.setAuthority(User.Authority.valueOf(authority));
+        USER changeUser = userMapper.selectByUsername(changeUsername);
+        changeUser.setAuthority(USER.Authority.valueOf(authority));
 
         if(userMapper.updateUser(changeUser) > 0){
             return Result.ok().setMessage("修改用户权限成功");
@@ -174,7 +177,7 @@
     }
 
     //从http请求中获取到用户
-    public User getUserInRequest(HttpServletRequest request) {
+    public USER getUserInRequest(HttpServletRequest request) {
         String UserName = JwtUtils.getClaimByToken(request.getHeader("Authorization")).getSubject();
         return userMapper.selectByUsername(UserName);
     }
@@ -186,4 +189,48 @@
         return Result.ok();
     }
 
+    //计算保种积分
+    @GetMapping("/magicPoint")
+    public Result getMagicPoint(HttpServletRequest request) {
+        USER user = this.getUserInRequest(request);
+        List<DOWNLOAD_TORRENT> dts = downloadTorrentMapper.selectByUsername(user.getUsername());
+
+        for (DOWNLOAD_TORRENT dt : dts) {
+            long during = ChronoUnit.DAYS.between(dt.getUpdated(), LocalDateTime.now());
+            double size = SizeCalculation.byteToGB(dt.getFileSize());
+            dt.setUpdated(LocalDateTime.now());
+            user.addMagicPoints((long) (during * size));
+
+            downloadTorrentMapper.updateDT(dt);
+        }
+
+        userMapper.updateUser(user);
+        return Result.ok().data("data", user);
+    }
+
+    //保重积分兑换下载量
+    //1gb资源代表一保重积分
+    @PostMapping("/exchangeUpload")
+    public Result exchangeUpload(
+            HttpServletRequest request,
+            @RequestBody Map<String, Long> requestBody // 接收 JSON 对象
+    ) {
+        USER user = this.getUserInRequest(request);
+        Long magicPoint = requestBody.get("magicPoint"); // 获取字段值
+
+        if (magicPoint == null) {
+            return Result.error(400).message("magicPoint 不能为空");
+        }
+
+        System.out.println("magicPoint" + magicPoint);
+        System.out.println("user magicPoint" + user.getMagicPoints());
+
+        if (user.getMagicPoints() >= magicPoint) {
+            user.reduceMagicPoints(magicPoint);
+            user.updateUpload(SizeCalculation.GBToByte(magicPoint));
+            userMapper.updateUser(user);
+            return Result.ok().message("兑换上传量成功");
+        }
+        return Result.error(409).message("兑换值超出最大魔力值,兑换失败");
+    }
 }
diff --git a/src/main/java/com/ptp/ptplatform/entity/DOWNLOAD_TORRENT.java b/src/main/java/com/ptp/ptplatform/entity/DOWNLOAD_TORRENT.java
new file mode 100644
index 0000000..77bdfcf
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/entity/DOWNLOAD_TORRENT.java
@@ -0,0 +1,33 @@
+package com.ptp.ptplatform.entity;
+
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class DOWNLOAD_TORRENT {
+
+    private Integer id;
+    private String username;
+    private Integer torrentid;
+    private boolean finished;
+    private LocalDateTime created;
+    private LocalDateTime updated;
+    private long fileSize;
+
+
+    public DOWNLOAD_TORRENT() {}
+
+    public DOWNLOAD_TORRENT(String username, Integer torrentid, long fileSize) {
+        this.username = username;
+        this.torrentid = torrentid;
+        this.created = LocalDateTime.now();
+        this.updated = this.created;
+        this.fileSize = fileSize;
+        this.finished = false;
+    }
+}
diff --git a/src/main/java/com/ptp/ptplatform/entity/TORRENT.java b/src/main/java/com/ptp/ptplatform/entity/TORRENT.java
index 814ba80..ce06e22 100644
--- a/src/main/java/com/ptp/ptplatform/entity/TORRENT.java
+++ b/src/main/java/com/ptp/ptplatform/entity/TORRENT.java
@@ -4,6 +4,9 @@
 import com.baomidou.mybatisplus.annotation.TableField;

 import com.baomidou.mybatisplus.annotation.TableId;

 import com.baomidou.mybatisplus.annotation.TableName;

+import com.fasterxml.jackson.databind.JsonNode;

+import com.fasterxml.jackson.databind.ObjectMapper;

+import com.fasterxml.jackson.databind.util.JSONPObject;

 import jakarta.persistence.GeneratedValue;

 import jakarta.persistence.GenerationType;

 import jakarta.persistence.Id;

@@ -15,8 +18,6 @@
 @TableName("torrent") // 使用MyBatis-Plus的注解指定表名

 public class TORRENT {

 

-    @Id

-    @GeneratedValue(strategy = GenerationType.IDENTITY)

     private Integer id;

 

     private String torrentName; // 文件名

@@ -36,6 +37,10 @@
     @TableField(value = "create_time") // 使用@TableField注解处理时间字段

     private LocalDateTime createTime; // 创建时间

 

+    public TORRENT(){

+

+    }

+

     // 构造函数

     public TORRENT(String hash, String torrentName, String description, String category, String region, String resolution, String subtitle, Long size, String username, String filePath) {

         this.hash = hash;

@@ -60,4 +65,11 @@
     public int getLikeCount() {

         return this.like_count;

     }

+

+    public long getSize() {

+        return this.size;

+    }

+    public int getId(){

+        return this.id;

+    }

 }
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/entity/User.java b/src/main/java/com/ptp/ptplatform/entity/USER.java
similarity index 60%
rename from src/main/java/com/ptp/ptplatform/entity/User.java
rename to src/main/java/com/ptp/ptplatform/entity/USER.java
index ed555c7..9762d88 100644
--- a/src/main/java/com/ptp/ptplatform/entity/User.java
+++ b/src/main/java/com/ptp/ptplatform/entity/USER.java
@@ -2,72 +2,46 @@
 // @GeneratedValue(strategy = GenerationType.IDENTITY) 对于一些需要自动生成的主键id进行注解
 
 package com.ptp.ptplatform.entity;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
+
+import com.ptp.ptplatform.utils.SizeCalculation;
 import jakarta.persistence.*;
+import lombok.Data;
 
 import java.util.Date;
 
-//@Entity
-//@Table(name = "`user`")
-//public class User {
-//
-//    @jakarta.persistence.Id
-//    private String username;
-//    private String password;
-//
-//    @Enumerated(EnumType.STRING)
-//    private Authority authority;
-//
-//    private int level; // 用户等级0-6
-//
-//    @Temporal(TemporalType.DATE)
-//    private Date registTime = new Date();
-//
-//    @Temporal(TemporalType.DATE)
-//    private Date lastLogin;
-//
-//    private long upload;
-//    private long download;
-//    private double shareRate;//分享率 前端展示数据应该为 90.23%这种
-//    private long magicPoints;// 魔力值
-//
-//    public enum Authority {
-//        USER, ADMIN, LIMIT, BAN
-//    }
-@TableName("`user`")
-public class User {
+@Data
+@Table(name = "user")
+public class USER {
 
+    @jakarta.persistence.Id
+    private String username;
+    private String password;
+
+    @Enumerated(EnumType.STRING)
+    private Authority authority;
+
+    private int level; // 用户等级0-6
+
+    @Temporal(TemporalType.DATE)
+    private Date registTime = new Date();
+
+    @Temporal(TemporalType.DATE)
+    private Date lastLogin;
+
+    private long upload;
+    private long download;
+    private double shareRate;//分享率 前端展示数据应该为 90.23%这种
+    private long magicPoints;// 魔力值
+
+    public enum Authority {
+        USER, ADMIN, LIMIT, BAN
+    }
+
+    // 函数
     public String getUsername() {
         return username;
     }
 
-    @TableId
-    private String username;
-    private String password;
-    private int level;
-    @TableField("authority")
-    private Authority authority;
-
-    @TableField("registTime")
-    private Date registTime;
-    @TableField("lastLogin")
-    private Date lastLogin;
-
-    @TableField("upload")
-    private long upload;
-    @TableField("download")
-    private long download;
-
-    @TableField("shareRate")
-    private double shareRate;
-    @TableField("magicPoints")
-    private long magicPoints;
-
-    public enum Authority { USER, ADMIN, LIMIT, BAN }
-
-
     public void setUsername(String username) {
         this.username = username;
     }
@@ -144,16 +118,16 @@
         this.magicPoints = magicPoints;
     }
 
-    public User() {
+    public USER() {
     }
 
-    public User(String username, String password, Authority authority) {
+    public USER(String username, String password, Authority authority) {
         this.username = username;
         this.password = password;
         this.authority = authority;
     }
 
-    public User(String username, String password, Date registTime) {
+    public USER(String username, String password, Date registTime) {
         this.username = username;
         this.registTime = registTime;
 
@@ -162,8 +136,8 @@
         this.authority = Authority.USER;
         this.level = 0;
         this.lastLogin = null;
-        this.upload = 0;
-        this.download = 0;
+        this.upload = SizeCalculation.GBToByte(1); //默认有1024mb的下载量
+        this.download = 1;
         this.shareRate = 1;
         this.magicPoints = 0;
     }
@@ -171,22 +145,14 @@
     //对上传量下载量的处理 返回更新后的数据结果
     public long updateUpload(long addUpload) {
         this.upload += addUpload;
-        if (this.download == 0) {
-            this.shareRate = 1;
-        } else {
-            this.shareRate = (double) this.upload / this.download;
-        }
-        return upload;
+        this.shareRate = (double) this.upload / this.download;
+        return this.upload;
     }
 
     public long updateDownload(long addDownload) {
         this.download += addDownload;
-        if (this.download == 0) {
-            this.shareRate = 1;
-        } else {
-            this.shareRate = (double) this.upload / this.download;
-        }
-        return download;
+        this.shareRate = (double) this.upload / this.download;
+        return this.download;
     }
 
     //生成邀请码,魔力值扣除
@@ -196,10 +162,22 @@
 
     // 每天运行的计算魔力值程序
     // 添加种子,验证同一用户的全部种子存活与否
-    public long updateMagicPoints(long addMagicPoints) {
-
-        return 0;
+    // 也是可以读取json文件进行操作
+    public void addMagicPoints(long addMagicPoints) {
+        this.magicPoints += addMagicPoints;
     }
 
+    public void reduceMagicPoints(long reduceMagicPoints) {
+        this.magicPoints -= reduceMagicPoints;
+    }
+
+    //分享率要至少大于0.5
+    public boolean allowDownload(long fileSize) {
+        if (this.upload / (fileSize + this.download) < 0.5) {
+            return false;
+        }
+        return true;
+
+    }
 
 }
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/mapper/DownloadTorrentMapper.java b/src/main/java/com/ptp/ptplatform/mapper/DownloadTorrentMapper.java
new file mode 100644
index 0000000..81da1e0
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/mapper/DownloadTorrentMapper.java
@@ -0,0 +1,23 @@
+package com.ptp.ptplatform.mapper;
+
+import com.ptp.ptplatform.entity.DOWNLOAD_TORRENT;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+import java.util.List;
+
+public interface DownloadTorrentMapper {
+    // 获取对应用户的保种积分
+    @Select("SELECT * FROM download_torrent WHERE username = #{username}")
+    List<DOWNLOAD_TORRENT> selectByUsername(String username);
+
+    // 新建数据
+    @Insert("INSERT INTO download_torrent (username, torrentid, finished, created, updated, fileSize) " +
+            "VALUES (#{username}, #{torrentid}, #{finished}, #{created}, #{updated}, #{fileSize})")
+    int insertDownloadTorrent(DOWNLOAD_TORRENT downloadTorrent);
+
+    @Update("UPDATE download_torrent SET torrentid = #{torrentid}, finished = #{finished}, updated = #{updated}, fileSize = #{fileSize} " +
+            "WHERE id = #{id}")
+    int updateDT(DOWNLOAD_TORRENT downloadTorrent);
+}
diff --git a/src/main/java/com/ptp/ptplatform/mapper/UserMapper.java b/src/main/java/com/ptp/ptplatform/mapper/UserMapper.java
index 5ee4bce..9204b6c 100644
--- a/src/main/java/com/ptp/ptplatform/mapper/UserMapper.java
+++ b/src/main/java/com/ptp/ptplatform/mapper/UserMapper.java
@@ -1,32 +1,32 @@
 package com.ptp.ptplatform.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.ptp.ptplatform.entity.User;
+import com.ptp.ptplatform.entity.USER;
 import org.apache.ibatis.annotations.*;
 
 import java.util.*;
 
 
 @Mapper
-public interface UserMapper extends BaseMapper<User> {
+public interface UserMapper extends BaseMapper<USER> {
     // 查询
     @Select("SELECT * FROM user WHERE username = #{username}")
-    User selectByUsername(String username);
+    USER selectByUsername(String username);
 
     @Select("SELECT * FROM user WHERE username = #{username} AND password = #{password}")
-    User selectByUsernameAndPassword(String username, String password);
+    USER selectByUsernameAndPassword(String username, String password);
 
     @Select("SELECT * FROM user WHERE username LIKE CONCAT('%', #{key}, '%') AND username != #{username}")
-    List<User> searchUsername(String username, String key);
+    List<USER> searchUsername(String username, String key);
 
     @Select("SELECT username, authority, level, registTime, lastLogin, upload, download, shareRate, magicPoints FROM user")
-    List<User> selectAllUsers();
+    List<USER> selectAllUsers();
 
 
     // 注册用户
     @Insert("INSERT INTO user (username, password, registTime) " +
             "VALUES (#{username}, #{password}, #{registTime})")
-    int insertUser(User user);
+    int insertUser(USER user);
 
     // 更新用户信息
     @Update("UPDATE user SET " +
@@ -39,6 +39,5 @@
             "shareRate = #{shareRate}, " +
             "magicPoints = #{magicPoints} " +
             "WHERE username = #{username}")
-    int updateUser(User user);
-
+    int updateUser(USER user);
 }
diff --git a/src/main/java/com/ptp/ptplatform/service/ClientService.java b/src/main/java/com/ptp/ptplatform/service/ClientService.java
index 39cdc76..daa4544 100644
--- a/src/main/java/com/ptp/ptplatform/service/ClientService.java
+++ b/src/main/java/com/ptp/ptplatform/service/ClientService.java
@@ -1,34 +1,50 @@
 package com.ptp.ptplatform.service;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.turn.ttorrent.client.SimpleClient;
 import com.turn.ttorrent.common.TorrentStatistic;
 import org.springframework.stereotype.Service;
 
+import java.io.File;
+import java.io.IOException;
 import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 @Service
 public class ClientService {
     SimpleClient client = new SimpleClient();
+    private List<String[]> tasks = new ArrayList<>();
 
-    public boolean downloadTorrent(String torrentFilePath, String outputDirectory) throws Exception {
-
-
-        InetAddress address = InetAddress.getLocalHost(); // 使用本地地址
-
+    public boolean downloadTorrent(String torrentFilePath, String outputDirectory, String username) throws Exception {
+        InetAddress address = InetAddress.getLocalHost();
         try {
-            // 开始下载torrent
-            client.downloadTorrent(torrentFilePath, outputDirectory, address);
-            System.out.println("下载完成");
+            client.downloadTorrentAsync(torrentFilePath, outputDirectory, address);
+            tasks.add(new String[] {username, torrentFilePath});
             return true;
         } catch (Exception e) {
-            // 下载失败,输出异常信息
             e.printStackTrace();
             return false;
         }
     }
 
     public TorrentStatistic getStatistics(String torrentFilePath) throws Exception {
-        TorrentStatistic ts = client.getStatistics(torrentFilePath);
-        return ts;
+        return client.getStatistics(torrentFilePath);
+    }
+
+    // 修改后的获取下载进度方法
+    public double getDownloadProgress(String torrentFilePath) throws Exception {
+        TorrentStatistic stats = client.getStatistics(torrentFilePath);
+        if (stats == null) {
+            return 0.0;
+        }
+        // 直接使用类中已有的百分比计算方法
+        return stats.getPercentageDownloaded();
+    }
+
+    public List<String[]> getTasks() {
+        return this.tasks;
     }
 }
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/service/TrackerService.java b/src/main/java/com/ptp/ptplatform/service/TrackerService.java
index 29bc95f..5e55dc1 100644
--- a/src/main/java/com/ptp/ptplatform/service/TrackerService.java
+++ b/src/main/java/com/ptp/ptplatform/service/TrackerService.java
@@ -1,5 +1,10 @@
 package com.ptp.ptplatform.service;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.ptp.ptplatform.service.serializer.PeerUIDDeserializer;
+import com.ptp.ptplatform.service.serializer.PeerUIDSerializer;
+import com.turn.ttorrent.common.PeerUID;
 import com.turn.ttorrent.tracker.TrackedTorrent;
 import com.turn.ttorrent.tracker.Tracker;
 import jakarta.annotation.PostConstruct;
@@ -15,47 +20,71 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
 
 //封装的对tracker的操作
 @Service
 public class TrackerService {
-
+    private final ObjectMapper objectMapper;
     private Tracker tracker;
 
-    public void startTracker() throws Exception {
-        // 创建一个Tracker对象,监听端口6969
-        tracker = new Tracker(6969);
-
-        // 过滤目录下的.torrent文件
-        FilenameFilter filter = new FilenameFilter() {
-            @Override
-            public boolean accept(File dir, String name) {
-                return name.endsWith(".torrent");
-            }
-        };
-
-        // 获取当前项目根目录的路径并拼接torrents文件夹路径
-        String torrentDirectory = System.getProperty("user.dir") + File.separator + "torrents";
-        System.out.println("文件路径: " + torrentDirectory);
-
-        // 遍历目录中的所有torrent文件,传递给tracker
-        for (File file : new File(torrentDirectory).listFiles(filter)) {
-
-            TrackedTorrent tt = tracker.announce(TrackedTorrent.load(file));
-            System.out.println("种子文件: " + tt);
-        }
-
-        // 设置允许接收外部torrent
-        tracker.setAcceptForeignTorrents(true);
-
-        // 启动tracker服务
-        tracker.start(true);
-
+    public TrackerService(ObjectMapper objectMapper) {
+        this.objectMapper = objectMapper;
+        registerPeerUIDModule(); // 注册 PeerUID 的序列化器
     }
 
-    public void stopTracker() {
+    private void registerPeerUIDModule() {
+        SimpleModule module = new SimpleModule();
+        module.addSerializer(PeerUID.class, new PeerUIDSerializer());
+        module.addDeserializer(PeerUID.class, new PeerUIDDeserializer());
+        objectMapper.registerModule(module);
+    }
+
+    public void startTracker() throws Exception {
+        File trackerFile = new File("tracker.json");
+        if (trackerFile.exists()) {
+            try {
+                tracker = objectMapper.readValue(trackerFile, Tracker.class);
+                System.out.println("Tracker loaded from file.");
+            } catch (Exception e) {
+                System.err.println("Failed to load tracker, creating new one: " + e.getMessage());
+                tracker = new Tracker(6969);
+            }
+        } else {
+            tracker = new Tracker(6969);
+        }
+
+        // 加载 .torrent 文件
+        String torrentDirectory = System.getProperty("user.dir") + File.separator + "torrents";
+        File[] torrentFiles = new File(torrentDirectory).listFiles((dir, name) -> name.endsWith(".torrent"));
+
+        if (torrentFiles != null) {
+            for (File file : torrentFiles) {
+                try {
+                    TrackedTorrent tt = TrackedTorrent.load(file);
+                    tracker.announce(tt);
+                    System.out.println("Added torrent: " + file.getName());
+                } catch (Exception e) {
+                    System.err.println("Failed to load torrent " + file.getName() + ": " + e.getMessage());
+                }
+            }
+        }
+
+        tracker.setAcceptForeignTorrents(true);
+        tracker.start(true);
+    }
+
+    public void serTracker() throws IOException {
         if (tracker != null) {
-            tracker.stop();
+            objectMapper.writeValue(new File("tracker.json"), tracker);
+        }
+    }
+
+    public void stopTracker() throws IOException {
+        if (tracker != null) {
+            objectMapper.writeValue(new File("tracker.json"), tracker);
+//            tracker.stop();
         }
     }
 
diff --git a/src/main/java/com/ptp/ptplatform/service/UserLevelService.java b/src/main/java/com/ptp/ptplatform/service/UserLevelService.java
index d885691..a95dec4 100644
--- a/src/main/java/com/ptp/ptplatform/service/UserLevelService.java
+++ b/src/main/java/com/ptp/ptplatform/service/UserLevelService.java
@@ -11,7 +11,7 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import com.ptp.ptplatform.entity.User;
+import com.ptp.ptplatform.entity.USER;
 import com.ptp.ptplatform.mapper.UserMapper;
 
 import lombok.extern.slf4j.Slf4j;
@@ -31,10 +31,10 @@
         log.info("===== 开始刷新所有用户等级 =====");
         LocalDate today = LocalDate.now();
 
-        List<User> users = userMapper.selectList(null);
+        List<USER> users = userMapper.selectList(null);
         log.info(">> 从数据库查到 {} 个用户", users.size());
 
-        for (User u : users) {
+        for (USER u : users) {
             double uploadGb    = u.getUpload()  / BYTES_PER_GB;
             long   magicPoints = u.getMagicPoints();
             log.info("[LevelCheck] 用户={} shareRate={} uploadGB={} magicPoints={}",
@@ -56,7 +56,7 @@
     /**
      * 根据各项指标和注册时长计算用户等级
      */
-    private int calcLevel(User u, LocalDate today) {
+    private int calcLevel(USER u, LocalDate today) {
         double shareRate   = u.getShareRate();            // 0.9023 表示 90.23%
         double uploadGb    = u.getUpload()  / BYTES_PER_GB;
         long   magicPoints = u.getMagicPoints();
diff --git a/src/main/java/com/ptp/ptplatform/service/serializer/PeerUIDDeserializer.java b/src/main/java/com/ptp/ptplatform/service/serializer/PeerUIDDeserializer.java
new file mode 100644
index 0000000..f9def6e
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/service/serializer/PeerUIDDeserializer.java
@@ -0,0 +1,28 @@
+package com.ptp.ptplatform.service.serializer;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.turn.ttorrent.common.PeerUID;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+public class PeerUIDDeserializer extends JsonDeserializer<PeerUID> {
+    @Override
+    public PeerUID deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
+            throws IOException, JsonProcessingException {
+        JsonNode node = jsonParser.getCodec().readTree(jsonParser);
+
+        String torrentHash = node.get("torrentHash").asText();
+
+        JsonNode addressNode = node.get("address");
+        String host = addressNode.get("host").asText();
+        int port = addressNode.get("port").asInt();
+
+        InetSocketAddress address = new InetSocketAddress(host, port);
+        return new PeerUID(address, torrentHash);
+    }
+}
diff --git a/src/main/java/com/ptp/ptplatform/service/serializer/PeerUIDSerializer.java b/src/main/java/com/ptp/ptplatform/service/serializer/PeerUIDSerializer.java
new file mode 100644
index 0000000..192c9df
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/service/serializer/PeerUIDSerializer.java
@@ -0,0 +1,28 @@
+package com.ptp.ptplatform.service.serializer;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.*;
+import com.turn.ttorrent.common.PeerUID;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+public class PeerUIDSerializer extends JsonSerializer<PeerUID> {
+    @Override
+    public void serialize(PeerUID peerUID, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
+            throws IOException {
+        jsonGenerator.writeStartObject();
+        jsonGenerator.writeStringField("torrentHash", peerUID.getTorrentHash());
+
+        // 序列化 InetSocketAddress
+        InetSocketAddress address = peerUID.getAddress();
+        jsonGenerator.writeObjectFieldStart("address");
+        jsonGenerator.writeStringField("host", address.getHostString());
+        jsonGenerator.writeNumberField("port", address.getPort());
+        jsonGenerator.writeEndObject();
+
+        jsonGenerator.writeEndObject();
+    }
+}
+
diff --git a/src/main/java/com/ptp/ptplatform/utils/SizeCalculation.java b/src/main/java/com/ptp/ptplatform/utils/SizeCalculation.java
index 863e376..5667260 100644
--- a/src/main/java/com/ptp/ptplatform/utils/SizeCalculation.java
+++ b/src/main/java/com/ptp/ptplatform/utils/SizeCalculation.java
@@ -1,9 +1,46 @@
 package com.ptp.ptplatform.utils;
 
+import com.ptp.ptplatform.entity.DISCOUNT;
+import com.ptp.ptplatform.mapper.DiscountMapper;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+
 //实现将字节大小转化为GB
 public class SizeCalculation {
 
     public static int byteToGB(long bytes) {
         return (int) (bytes / 1024 / 1024 / 1024);
     }
+    public static long GBToByte(long GB) { return (long) (GB * 1024 * 1024 * 1024); }
+
+    public static long getUpload(long uploaded, DiscountMapper discountMapper, LocalDateTime torrentTime) {
+        // 计算折扣
+        DISCOUNT discount = discountMapper.selectCurrentDiscount();
+        if (Duration.between(torrentTime, LocalDateTime.now()).toDays() <= 7) {
+            uploaded = uploaded * 2;
+        }
+        if (discount != null) {
+            if (discount.getDiscountType().equals("DOUBLE")) {
+                uploaded = 2 * uploaded;
+            }
+        }
+
+        return uploaded;
+    }
+
+    public static long getDownload(long downloaded, DiscountMapper discountMapper, LocalDateTime torrentTime) {
+        DISCOUNT discount = discountMapper.selectCurrentDiscount();
+        if (Duration.between(torrentTime, LocalDateTime.now()).toDays() <= 7) {
+            downloaded = downloaded / 2;
+        }
+        if (discount != null) {
+            if (discount.getDiscountType().equals("FREE")) {
+                downloaded = 0;
+            } else if (discount.getDiscountType().equals("HALF")) {
+                downloaded = downloaded / 2;
+            }
+        }
+        return downloaded;
+    }
 }
diff --git a/src/test/java/com/ptp/ptplatform/controller/InviteCodeTest.java b/src/test/java/com/ptp/ptplatform/controller/InviteCodeTest.java
index 0a50c00..9ee40b1 100644
--- a/src/test/java/com/ptp/ptplatform/controller/InviteCodeTest.java
+++ b/src/test/java/com/ptp/ptplatform/controller/InviteCodeTest.java
@@ -1,7 +1,7 @@
 package com.ptp.ptplatform.controller;
 
 import com.ptp.ptplatform.entity.INVITE_CODE;
-import com.ptp.ptplatform.entity.User;
+import com.ptp.ptplatform.entity.USER;
 import com.ptp.ptplatform.mapper.InviteCodeMapper;
 import com.ptp.ptplatform.mapper.UserMapper;
 import com.ptp.ptplatform.utils.Result;
@@ -35,14 +35,14 @@
     @InjectMocks
     private InviteCodeController inviteCodeController;
 
-    private User testUser;
+    private USER testUser;
     private INVITE_CODE testInviteCode;
 
     @BeforeEach
     void setUp() {
         MockitoAnnotations.openMocks(this);
 
-        testUser = new User();
+        testUser = new USER();
         testUser.setUsername("testUser");
         testUser.setMagicPoints(15);
 
diff --git a/src/test/java/com/ptp/ptplatform/controller/UserControllerTest.java b/src/test/java/com/ptp/ptplatform/controller/UserControllerTest.java
index d69c4ea..599655e 100644
--- a/src/test/java/com/ptp/ptplatform/controller/UserControllerTest.java
+++ b/src/test/java/com/ptp/ptplatform/controller/UserControllerTest.java
@@ -1,7 +1,7 @@
 package com.ptp.ptplatform.controller;
 
 import com.ptp.ptplatform.entity.INVITE_CODE;
-import com.ptp.ptplatform.entity.User;
+import com.ptp.ptplatform.entity.USER;
 import com.ptp.ptplatform.mapper.InviteCodeMapper;
 import com.ptp.ptplatform.mapper.UserMapper;
 import com.ptp.ptplatform.service.UserLevelService;
@@ -44,14 +44,14 @@
     @InjectMocks
     private UserController userController;
 
-    private User testUser;
+    private USER testUser;
     private INVITE_CODE testInviteCode;
 
     @BeforeEach
     void setUp() {
         MockitoAnnotations.openMocks(this);
 
-        testUser = new User();
+        testUser = new USER();
         testUser.setUsername("testUser");
         testUser.setPassword("testPassword");
         testUser.setLastLogin(new Date());
@@ -140,7 +140,7 @@
         // Assert
         assertTrue(result.isSuccess());
         assertEquals("新建用户成功", result.getMessage());
-        verify(userMapper, times(1)).insertUser(any(User.class));
+        verify(userMapper, times(1)).insertUser(any(USER.class));
         verify(inviteCodeMapper, times(1)).updateCodeUser("VALIDCODE");
     }
 
@@ -281,7 +281,7 @@
             when(userMapper.selectByUsername(testUser.getUsername())).thenReturn(testUser);
 
             // Act
-            User result = userController.getUserInRequest(request);
+            USER result = userController.getUserInRequest(request);
 
             // Assert
             assertEquals(testUser, result);
@@ -299,13 +299,13 @@
             mockedJwtUtils.when(() -> JwtUtils.getClaimByToken(anyString())).thenReturn(mockClaims);
 
             // 模拟用户查询
-            User mockUser = new User("testUser", "password", User.Authority.USER);
+            USER mockUser = new USER("testUser", "password", USER.Authority.USER);
             when(userMapper.selectByUsername("testUser")).thenReturn(mockUser);
 
             // 模拟搜索结果
-            List<User> mockResults = Arrays.asList(
-                    new User("user1", "pass1", User.Authority.USER),
-                    new User("user2", "pass2", User.Authority.USER)
+            List<USER> mockResults = Arrays.asList(
+                    new USER("user1", "pass1", USER.Authority.USER),
+                    new USER("user2", "pass2", USER.Authority.USER)
             );
             when(userMapper.searchUsername("testUser", "zzz")).thenReturn(mockResults);
 
@@ -329,10 +329,10 @@
     @Test
     public void testGetAllUser() {
         // 模拟返回所有用户
-        List<User> mockUsers = Arrays.asList(
-                new User("admin", "adminPass", User.Authority.ADMIN),
-                new User("user1", "pass1", User.Authority.USER),
-                new User("user2", "pass2", User.Authority.USER)
+        List<USER> mockUsers = Arrays.asList(
+                new USER("admin", "adminPass", USER.Authority.ADMIN),
+                new USER("user1", "pass1", USER.Authority.USER),
+                new USER("user2", "pass2", USER.Authority.USER)
         );
         when(userMapper.selectAllUsers()).thenReturn(mockUsers);
 
@@ -355,9 +355,9 @@
         authorityMap.put("changeUsername", "testUser");
         authorityMap.put("authority", "ADMIN");
 
-        User mockUser = new User("testUser", "password", User.Authority.USER);
+        USER mockUser = new USER("testUser", "password", USER.Authority.USER);
         when(userMapper.selectByUsername("testUser")).thenReturn(mockUser);
-        when(userMapper.updateUser(any(User.class))).thenReturn(1);
+        when(userMapper.updateUser(any(USER.class))).thenReturn(1);
 
         Result successResult = userController.changeAuthority(new MockHttpServletRequest(), authorityMap);
         assertEquals(200, successResult.getCode());
diff --git a/temp.txt b/temp.txt
new file mode 100644
index 0000000..76a8b85
--- /dev/null
+++ b/temp.txt
@@ -0,0 +1,52 @@
+pipeline {
+    agent any
+    environment {
+        SERVER_IP = '192.168.5.13'   // 目标服务器IP
+        USERNAME = 'p4user'           // 服务器用户名
+        DEPLOY_DIR = '/team2'        // 远程部署路径
+    }
+    stages {
+        stage('pull') {
+            steps {
+                git branch: 'master', url: 'ssh://jenkins@gerrit.lilingkun.com:29418/G2-ptPlatform-backend'
+                echo '拉取成功'
+            }
+        }
+
+        stage('build') {
+            steps {
+                sh "mvn --version"
+                // 合并构建步骤,避免多次切换目录
+                sh """
+                    cd ttorrent-master && mvn clean install -DskipTests
+                    cd .. && mvn clean package
+                """
+                echo '构建完成'
+            }
+        }
+
+        stage('Deploy') {
+            steps {
+                script {
+                    // 使用 SCP 命令将文件复制到目标服务器
+                    sh """
+                        scp -r ./build/* ${USERNAME}@${SERVER_IP}:${DEPLOY_DIR}
+                       """
+                    }
+                }
+        }
+
+        stage('Run Docker') {
+                    steps {
+                        script {
+                            // 使用 Docker 在目标服务器上运行容器
+                            sshagent(['your-ssh-credential-id']) {
+                                sh """
+                                ssh ${USERNAME}@${SERVER_IP} 'docker run -d --name my-app-container ${DEPLOY_DIR}/my-app-image'
+                                """
+                            }
+                        }
+                    }
+                }
+    }
+}
\ No newline at end of file
diff --git "a/torrents/1748241407377_\344\270\244\346\235\206\345\244\247\347\203\237\346\236\252BD\344\270\255\350\213\261\345\217\214\345\255\227.mp4.torrent" "b/torrents/1748241407377_\344\270\244\346\235\206\345\244\247\347\203\237\346\236\252BD\344\270\255\350\213\261\345\217\214\345\255\227.mp4.torrent"
new file mode 100644
index 0000000..0db6347
--- /dev/null
+++ "b/torrents/1748241407377_\344\270\244\346\235\206\345\244\247\347\203\237\346\236\252BD\344\270\255\350\213\261\345\217\214\345\255\227.mp4.torrent"
Binary files differ
diff --git "a/torrents/\344\270\244\346\235\206\345\244\247\347\203\237\346\236\252BD\344\270\255\350\213\261\345\217\214\345\255\227.mp4.torrent" "b/torrents/\344\270\244\346\235\206\345\244\247\347\203\237\346\236\252BD\344\270\255\350\213\261\345\217\214\345\255\227.mp4.torrent"
new file mode 100644
index 0000000..0db6347
--- /dev/null
+++ "b/torrents/\344\270\244\346\235\206\345\244\247\347\203\237\346\236\252BD\344\270\255\350\213\261\345\217\214\345\255\227.mp4.torrent"
Binary files differ
diff --git a/tracker.json b/tracker.json
new file mode 100644
index 0000000..5475d24
--- /dev/null
+++ b/tracker.json
@@ -0,0 +1 @@
+{"announceUrl":"http://10.60.12.94:6969/announce","announceURI":"http://10.60.12.94:6969/announce","trackedTorrents":[{"announceInterval":10,"peers":{"PeerUID{address=/10.60.12.94:6881, torrent hash='E5FD501A5E6F07FA02777809C83EB2B815A2C3EA'}":{"address":"10.60.12.94:6881","peerId":"Mzg3ZGMxYTgtMzU4Yy00ZDMyLWFhOTctNzQ2MzdhMmMyZDg1","hexPeerId":"33383764633161382D333538632D346433322D616139372D373436333761326332643835","hexInfoHash":null,"uploaded":0,"downloaded":0,"left":3063963,"completed":true,"port":6881,"shortHexPeerId":"..643835","stringPeerId":"387dc1a8-358c-4d32-aa97-74637a2c2d85","hostIdentifier":"/10.60.12.94:6881","ip":"10.60.12.94","peerIdArray":"Mzg3ZGMxYTgtMzU4Yy00ZDMyLWFhOTctNzQ2MzdhMmMyZDg1","rawIp":"CjwMXg=="},"PeerUID{address=/10.60.12.94:6883, torrent hash='E5FD501A5E6F07FA02777809C83EB2B815A2C3EA'}":{"address":"10.60.12.94:6883","peerId":"LVRPMDA0Mi0wYjI5MDQwZDE3MWQ=","hexPeerId":"2D544F303034322D306232393034306431373164","hexInfoHash":null,"uploaded":0,"downloaded":0,"left":3063963,"completed":false,"port":6883,"shortHexPeerId":"..373164","stringPeerId":"-TO0042-0b29040d171d","hostIdentifier":"/10.60.12.94:6883","ip":"10.60.12.94","peerIdArray":"LVRPMDA0Mi0wYjI5MDQwZDE3MWQ=","rawIp":"CjwMXg=="},"PeerUID{address=/10.60.12.94:6882, torrent hash='E5FD501A5E6F07FA02777809C83EB2B815A2C3EA'}":{"address":"10.60.12.94:6882","peerId":"LVRPMDA0Mi0xNWI3N2EwZWViZWY=","hexPeerId":"2D544F303034322D313562373761306565626566","hexInfoHash":null,"uploaded":0,"downloaded":0,"left":3063963,"completed":false,"port":6882,"shortHexPeerId":"..626566","stringPeerId":"-TO0042-15b77a0eebef","hostIdentifier":"/10.60.12.94:6882","ip":"10.60.12.94","peerIdArray":"LVRPMDA0Mi0xNWI3N2EwZWViZWY=","rawIp":"CjwMXg=="}},"hexInfoHash":"E5FD501A5E6F07FA02777809C83EB2B815A2C3EA","infoHash":"5f1QGl5vB/oCd3gJyD6yuBWiw+o="},{"announceInterval":10,"peers":{"PeerUID{address=/10.60.12.94:6882, torrent hash='34D94F184C0DC45372F52DD0FBAEB79AF4BD758E'}":{"address":"10.60.12.94:6882","peerId":"LVRPMDA0Mi0xNWI3N2EwZWViZWY=","hexPeerId":"2D544F303034322D313562373761306565626566","hexInfoHash":null,"uploaded":0,"downloaded":0,"left":1609568975,"completed":false,"port":6882,"shortHexPeerId":"..626566","stringPeerId":"-TO0042-15b77a0eebef","hostIdentifier":"/10.60.12.94:6882","ip":"10.60.12.94","peerIdArray":"LVRPMDA0Mi0xNWI3N2EwZWViZWY=","rawIp":"CjwMXg=="},"PeerUID{address=/10.60.12.94:6881, torrent hash='34D94F184C0DC45372F52DD0FBAEB79AF4BD758E'}":{"address":"10.60.12.94:6881","peerId":"YmI0YzJkYTgtMWRiMy00ODRiLTg0ZDgtNjhkMjAwMzViNzhk","hexPeerId":"62623463326461382D316462332D343834622D383464382D363864323030333562373864","hexInfoHash":null,"uploaded":0,"downloaded":0,"left":1609568975,"completed":false,"port":6881,"shortHexPeerId":"..373864","stringPeerId":"bb4c2da8-1db3-484b-84d8-68d20035b78d","hostIdentifier":"/10.60.12.94:6881","ip":"10.60.12.94","peerIdArray":"YmI0YzJkYTgtMWRiMy00ODRiLTg0ZDgtNjhkMjAwMzViNzhk","rawIp":"CjwMXg=="}},"hexInfoHash":"34D94F184C0DC45372F52DD0FBAEB79AF4BD758E","infoHash":"NNlPGEwNxFNy9S3Q+663mvS9dY4="}]}
\ No newline at end of file
diff --git a/ttorrent-master/test-api/src/main/java/com/turn/ttorrent/TempFiles.java b/ttorrent-master/test-api/src/main/java/com/turn/ttorrent/TempFiles.java
index 0ebed92..33f4d49 100644
--- a/ttorrent-master/test-api/src/main/java/com/turn/ttorrent/TempFiles.java
+++ b/ttorrent-master/test-api/src/main/java/com/turn/ttorrent/TempFiles.java
@@ -102,10 +102,10 @@
   }
 
   /**
-   * Returns a File object for created temp directory.
+   * Returns a File object for created temp.txt directory.
    * Also stores the value into this object accessed with {@link #getCurrentTempDir()}
    *
-   * @return a File object for created temp directory
+   * @return a File object for created temp.txt directory
    * @throws IOException if directory creation fails.
    */
   public final File createTempDir() throws IOException {
diff --git a/uploads/1ed6e86b-955c-4426-b170-73bc7558eca5.png b/uploads/1ed6e86b-955c-4426-b170-73bc7558eca5.png
new file mode 100644
index 0000000..71bd63e
--- /dev/null
+++ b/uploads/1ed6e86b-955c-4426-b170-73bc7558eca5.png
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/uploads/69925fae-3c62-460c-a5c1-ee9695bc8ae2.jpg b/uploads/69925fae-3c62-460c-a5c1-ee9695bc8ae2.jpg
new file mode 100644
index 0000000..aed2973
--- /dev/null
+++ b/uploads/69925fae-3c62-460c-a5c1-ee9695bc8ae2.jpg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/uploads/72db8eb6-40f8-445c-a833-43ec6fecebdd.png b/uploads/72db8eb6-40f8-445c-a833-43ec6fecebdd.png
new file mode 100644
index 0000000..71bd63e
--- /dev/null
+++ b/uploads/72db8eb6-40f8-445c-a833-43ec6fecebdd.png
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/uploads/ef17d022-9c80-436b-87cd-227d783baadf.jpg b/uploads/ef17d022-9c80-436b-87cd-227d783baadf.jpg
new file mode 100644
index 0000000..aed2973
--- /dev/null
+++ b/uploads/ef17d022-9c80-436b-87cd-227d783baadf.jpg
@@ -0,0 +1 @@
+
\ No newline at end of file