管理员界面接口

Change-Id: I69169102205ce7143e131b92a560466d6efb064c
diff --git a/src/main/java/com/ptp/ptplatform/controller/DiscountController.java b/src/main/java/com/ptp/ptplatform/controller/DiscountController.java
new file mode 100644
index 0000000..f2e20cb
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/controller/DiscountController.java
@@ -0,0 +1,83 @@
+package com.ptp.ptplatform.controller;
+
+import com.ptp.ptplatform.entity.DISCOUNT;
+import com.ptp.ptplatform.mapper.DiscountMapper;
+import com.ptp.ptplatform.utils.Result;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@RestController
+@RequestMapping("/discount")
+public class DiscountController {
+
+    @Autowired
+    private DiscountMapper discountMapper;
+    @Autowired
+    private DataSourcePoolMetadataProvider dataSourcePoolMetadataProvider;
+
+    // 增加新折扣
+    @PostMapping("/add")
+    public Result addDiscount(@RequestBody DISCOUNT discount) {
+        // 获取当前时间
+        LocalDateTime now = LocalDateTime.now();
+        discount.setCreateTime(now);
+        System.out.println("now" + now);
+
+        // 检查折扣开始时间是否早于当前时间
+        if (discount.getStartTime().isBefore(now)) {
+            return Result.error(400).setMessage("折扣开始时间最早不能早于现在。");
+        }
+
+        // 检查折扣开始时间是否早于结束时间
+        if (discount.getStartTime().isAfter(discount.getEndTime())) {
+            return Result.error(400).setMessage("折扣开始时间必须早于结束时间。");
+        }
+
+        // 检查时间是否重叠
+        int overlapCount = discountMapper.checkOverlapTime(discount.getStartTime(), discount.getEndTime());
+        if (overlapCount > 0) {
+            return Result.error(400).setMessage("新折扣时间与已有折扣时间重叠,请选择不同时间。");
+        }
+
+        // 新增折扣
+        int result = discountMapper.insertDiscount(discount);
+        if (result > 0) {
+            return Result.ok().data("data", discount);
+        } else {
+            return Result.error(500).setMessage("添加折扣失败。");
+        }
+    }
+
+    // 删除折扣
+    @DeleteMapping("/delete/{id}")
+    public Result deleteDiscount(@PathVariable int id) {
+        int result = discountMapper.deleteDiscountById(id);
+        if (result > 0) {
+            return Result.ok().setMessage("删除成功。");
+        } else {
+            return Result.error(404).setMessage("折扣未找到。");
+        }
+    }
+
+    // 查询当前进行中的折扣
+    @GetMapping("/current")
+    public Result getCurrentDiscount() {
+        DISCOUNT discount = discountMapper.selectCurrentDiscount();
+        if (discount != null) {
+            return Result.ok().data("data", discount);
+        } else {
+            return Result.ok().setMessage("目前没有进行中的折扣。");
+        }
+    }
+
+    // 查询所有折扣计划
+    @GetMapping("/all")
+    public Result getAllDiscounts() {
+        List<DISCOUNT> discounts = discountMapper.selectAllDiscounts();
+        return Result.ok().data("data", discounts);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/controller/TorrentController.java b/src/main/java/com/ptp/ptplatform/controller/TorrentController.java
index 028f5c8..50cc785 100644
--- a/src/main/java/com/ptp/ptplatform/controller/TorrentController.java
+++ b/src/main/java/com/ptp/ptplatform/controller/TorrentController.java
@@ -1,7 +1,9 @@
 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.ptp.ptplatform.mapper.DiscountMapper;
 import com.ptp.ptplatform.mapper.TorrentMapper;
 import com.ptp.ptplatform.mapper.UserMapper;
 import com.ptp.ptplatform.service.ClientService;
@@ -11,6 +13,8 @@
 import com.ptp.ptplatform.service.TorrentCommentService;
 import com.ptp.ptplatform.service.TorrentService;
 import com.ptp.ptplatform.utils.Result;
+import com.turn.ttorrent.bcodec.BDecoder;
+import com.turn.ttorrent.bcodec.BEValue;
 import com.turn.ttorrent.common.TorrentStatistic;
 import com.turn.ttorrent.tracker.TrackedPeer;
 import com.turn.ttorrent.tracker.TrackedTorrent;
@@ -22,6 +26,7 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.net.InetAddress;
@@ -30,6 +35,7 @@
 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;
@@ -53,6 +59,8 @@
     private UserMapper userMapper;
     @Resource
     private UserController userController;
+    @Resource
+    private DiscountMapper discountMapper;
 
     private TrackerService ts = new TrackerService();
     private ClientService cs = new ClientService();
@@ -155,7 +163,7 @@
             return Result.error(404).data("message", "文件不存在或路径无效");
         }
         if (!file.getName().endsWith(".torrent")) {
-            return Result.error(404).data("message", "仅支持.torrent文件");
+            return Result.error(403).data("message", "仅支持.torrent文件");
         }
 
         // 2. 确定存储目录(改为项目根目录下的torrents文件夹)
@@ -182,9 +190,13 @@
             TrackedPeer tp = createTrackedPeer(tt);
             tt.addPeer(tp);
 
+            long fileSize = getTorrentSizeManual(uploadedFile);
+            System.out.println("fileSize" + fileSize);
+
+
             // 5. 数据库操作
             torrent.setHash(tt.getHexInfoHash());
-            torrent.setSize(uploadedFile.length());
+            torrent.setSize(fileSize);
             torrent.setUsername(getClaimByToken(request.getHeader("Authorization")).getSubject());
             torrent.setFilePath(uploadedFile.getAbsolutePath());
 
@@ -231,23 +243,48 @@
     // getDownloadedBytes:3063963
     //getUploadedBytes:0
     @GetMapping("/downloadTorrent")
-    public Result getTorrent(HttpServletRequest request,int id ,String downloadPath) throws Exception {
+    public Result getTorrent(HttpServletRequest request, int id, String downloadPath) throws Exception {
         TORRENT torrent = torrentMapper.selectByID(id);
 
         // 下载完成
         // 传入变量:下载种子的id 下载到文件的路径
-        cs.downloadTorrent(torrent.getFilePath(), downloadPath);
-
-        //管理种子相关数据
-        TorrentStatistic ts = cs.getStatistics(torrent.getFilePath());
+//        cs.downloadTorrent(torrent.getFilePath(), downloadPath);
+//
+//        //管理种子相关数据
+//        TorrentStatistic ts = cs.getStatistics(torrent.getFilePath());
 
         //修改用户对应的信息
         USER downloadUser = userController.getUserInRequest(request);
         USER uploadUser = userMapper.selectByUsername(torrent.getUsername());
 
-        downloadUser.updateDownload(ts.getDownloadedBytes());
-        uploadUser.updateUpload(ts.getDownloadedBytes());
+        long up = 30000L;
+        long down = 30000L;
 
+//        long up = torrent.getSize();
+//        long down = up;
+
+        // 计算折扣
+        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;
+            }
+        }
+
+
+        downloadUser.updateDownload(down);
+        uploadUser.updateUpload(up);
+
+//        downloadUser.updateDownload(ts.getDownloadedBytes());
+//        uploadUser.updateUpload(ts.getDownloadedBytes());
         userMapper.updateUser(downloadUser);
         userMapper.updateUser(uploadUser);
 
@@ -257,7 +294,7 @@
 
     //根据id删除
     @DeleteMapping("/deleteTorrent/{id}")
-    public Result deleteTorrent(HttpServletRequest request,@PathVariable("id")int id) throws Exception {
+    public Result deleteTorrent(HttpServletRequest request, @PathVariable("id") int id) throws Exception {
         TORRENT torrent = torrentMapper.selectByID(id);
 
         String filePath = torrent.getFilePath();
@@ -356,4 +393,27 @@
                 .data("records", pageResult.getRecords())
                 .data("total", pageResult.getTotal());
     }
+
+    //获取torrent文件大小
+    public static long getTorrentSizeManual(File torrentFile) throws IOException {
+        byte[] data = Files.readAllBytes(torrentFile.toPath());
+        ByteArrayInputStream bis = new ByteArrayInputStream(data);
+        BEValue decoded = BDecoder.bdecode(bis);
+        Map<String, BEValue> torrentData = decoded.getMap();
+        Map<String, BEValue> info = torrentData.get("info").getMap();
+
+        // 单文件情况
+        if (info.containsKey("length")) {
+            return info.get("length").getLong();
+        }
+        // 多文件情况
+        else if (info.containsKey("files")) {
+            long totalSize = 0;
+            for (BEValue file : info.get("files").getList()) {
+                totalSize += file.getMap().get("length").getLong();
+            }
+            return totalSize;
+        }
+        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 4587abc..70885de 100644
--- a/src/main/java/com/ptp/ptplatform/controller/UserController.java
+++ b/src/main/java/com/ptp/ptplatform/controller/UserController.java
@@ -17,6 +17,7 @@
 
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 @RestController
@@ -29,7 +30,6 @@
     @Autowired
     private InviteCodeMapper inviteCodeMapper;
 
-
     //个人中心获取用户登录信息
     @GetMapping("/info") //获取
     public Result info(HttpServletRequest request) {
@@ -45,6 +45,9 @@
         if (user == null) {
             return Result.error(404).setMessage("用户不存在");
         }
+        if(user.getAuthority() == USER.Authority.BAN) {
+            return Result.error(403).setMessage("用户被封禁,禁止登录");
+        }
 
         System.out.println("password" + user.getPassword());
 
@@ -127,6 +130,46 @@
         return Result.error(404).setMessage("原密码不正确");
     }
 
+    //搜索用户
+    @GetMapping("/searchUser")
+    public Result searchUser(@RequestParam(value = "key", required = false) String key, HttpServletRequest request) {
+        // 从请求中获取当前用户
+        USER user = this.getUserInRequest(request);
+        String username = user.getUsername();
+
+        // 检查是否提供了搜索关键词
+        if (key != null && !key.isEmpty()) {
+            List<USER> searchResults = userMapper.searchUsername(username, key);
+            return Result.ok().data("data", searchResults);
+        } else {
+            // 如果没有提供关键词,返回默认结果或者一个错误消息
+            return Result.error(404).setMessage("请提供搜索关键词");
+        }
+    }
+
+    //获取到全部用户
+    @GetMapping("/allUser")
+    public Result getAllUser(){
+        List<USER> users = userMapper.selectAllUsers();
+
+        return Result.ok().data("data", users);
+    }
+
+    //修改用户权限
+    // 传入数据 username 要修改到的权限
+    @PutMapping("/changeAuthority")
+    public Result changeAuthority(HttpServletRequest request, @RequestBody Map<String, String> authorityMap) {
+        String authority = authorityMap.get("authority");
+        String changeUsername = authorityMap.get("changeUsername");
+
+        USER changeUser = userMapper.selectByUsername(changeUsername);
+        changeUser.setAuthority(USER.Authority.valueOf(authority));
+
+        if(userMapper.updateUser(changeUser) > 0){
+            return Result.ok().setMessage("修改用户权限成功");
+        }
+        return Result.error(400).setMessage("修改用户权限失败");
+    }
 
     //从http请求中获取到用户
     public USER getUserInRequest(HttpServletRequest request) {
diff --git a/src/main/java/com/ptp/ptplatform/entity/DISCOUNT.java b/src/main/java/com/ptp/ptplatform/entity/DISCOUNT.java
new file mode 100644
index 0000000..49d008f
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/entity/DISCOUNT.java
@@ -0,0 +1,64 @@
+package com.ptp.ptplatform.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+@TableName("discount") // 指定表名为 discount
+public class DISCOUNT {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Integer id; // 主键ID
+
+    @TableField(value = "name") // 折扣名称 比如五一节日
+    private String name;
+
+    @TableField(value = "start_time") // 开始时间
+    private LocalDateTime startTime;
+
+    @TableField(value = "end_time") // 结束时间
+    private LocalDateTime endTime;
+
+    @TableField(value = "discount_type") // 折扣方式
+    private String discountType;
+    // enum ('FREE', 'HALF', 'DOUBLE')
+    // free 全部下载免费 half 下载消耗一半下载量 double 下载资源,上传用户获得双倍下载量
+
+    @TableField(value = "create_time") // 创建时间
+    private LocalDateTime createTime;
+
+    @TableField(exist = false)
+    private String status; // 状态字段(进行中、未开始、已过期)
+
+    // 构造函数
+    public DISCOUNT(String name, LocalDateTime startTime, LocalDateTime endTime, String discountType) {
+        this.name = name;
+        this.startTime = startTime;
+        this.endTime = endTime;
+        this.discountType = discountType;
+        this.createTime = LocalDateTime.now(); // 默认为当前时间
+    }
+
+    public String getStatus() {
+        LocalDateTime now = LocalDateTime.now();
+        if (now.isBefore(startTime)) {
+            return "未开始";
+        } else if (now.isAfter(endTime)) {
+            return "已过期";
+        } else {
+            return "进行中";
+        }
+    }
+
+}
diff --git a/src/main/java/com/ptp/ptplatform/entity/TORRENT.java b/src/main/java/com/ptp/ptplatform/entity/TORRENT.java
index c9d1c94..814ba80 100644
--- a/src/main/java/com/ptp/ptplatform/entity/TORRENT.java
+++ b/src/main/java/com/ptp/ptplatform/entity/TORRENT.java
@@ -34,7 +34,7 @@
     private int reply_count;

 

     @TableField(value = "create_time") // 使用@TableField注解处理时间字段

-    private Date createTime; // 创建时间

+    private LocalDateTime createTime; // 创建时间

 

     // 构造函数

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

@@ -48,6 +48,7 @@
         this.size = size;

         this.username = username;

         this.filePath = filePath;

+        this.createTime = LocalDateTime.now();

         this.like_count = 0;

         this.reply_count = 0;

     }

diff --git a/src/main/java/com/ptp/ptplatform/entity/USER.java b/src/main/java/com/ptp/ptplatform/entity/USER.java
index ac51eb4..f002fc2 100644
--- a/src/main/java/com/ptp/ptplatform/entity/USER.java
+++ b/src/main/java/com/ptp/ptplatform/entity/USER.java
@@ -121,6 +121,12 @@
     public USER() {
     }
 
+    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) {
         this.username = username;
         this.registTime = registTime;
@@ -139,13 +145,21 @@
     //对上传量下载量的处理 返回更新后的数据结果
     public long updateUpload(long addUpload) {
         this.upload += addUpload;
-        this.shareRate = (double) this.upload / this.download;
+        if (this.download == 0) {
+            this.shareRate = 1;
+        } else {
+            this.shareRate = (double) this.upload / this.download;
+        }
         return upload;
     }
 
     public long updateDownload(long addDownload) {
         this.download += addDownload;
-        this.shareRate = (double) this.upload / this.download;
+        if (this.download == 0) {
+            this.shareRate = 1;
+        } else {
+            this.shareRate = (double) this.upload / this.download;
+        }
         return download;
     }
 
diff --git a/src/main/java/com/ptp/ptplatform/mapper/DiscountMapper.java b/src/main/java/com/ptp/ptplatform/mapper/DiscountMapper.java
new file mode 100644
index 0000000..82c047e
--- /dev/null
+++ b/src/main/java/com/ptp/ptplatform/mapper/DiscountMapper.java
@@ -0,0 +1,32 @@
+package com.ptp.ptplatform.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ptp.ptplatform.entity.DISCOUNT;
+import org.apache.ibatis.annotations.*;
+
+import java.time.LocalDateTime;
+import java.util.*;
+
+@Mapper
+public interface DiscountMapper extends BaseMapper<DISCOUNT> {
+
+    // 查询当前进行中的折扣
+    @Select("SELECT * FROM discount WHERE start_time <= NOW() AND end_time >= NOW()")
+    DISCOUNT selectCurrentDiscount();
+
+    // 查询全部折扣
+    @Select("SELECT * FROM discount")
+    List<DISCOUNT> selectAllDiscounts();
+
+    // 插入新折扣
+    @Insert("INSERT INTO discount (start_time, end_time, discount_type, name) VALUES (#{startTime}, #{endTime}, #{discountType}, #{name})")
+    int insertDiscount(DISCOUNT discount);
+
+    // 删除折扣
+    @Delete("DELETE FROM discount WHERE id = #{id}")
+    int deleteDiscountById(int id);
+
+    // 查询是否有重叠时间的折扣
+    @Select("SELECT COUNT(*) FROM discount WHERE (start_time < #{endTime} AND end_time > #{startTime})")
+    int checkOverlapTime(LocalDateTime startTime, LocalDateTime endTime);
+}
\ No newline at end of file
diff --git a/src/main/java/com/ptp/ptplatform/mapper/TorrentMapper.java b/src/main/java/com/ptp/ptplatform/mapper/TorrentMapper.java
index b4ec407..fcb26d8 100644
--- a/src/main/java/com/ptp/ptplatform/mapper/TorrentMapper.java
+++ b/src/main/java/com/ptp/ptplatform/mapper/TorrentMapper.java
@@ -24,7 +24,7 @@
     @Select("SELECT * FROM torrent WHERE category = #{category}")
     List<TORRENT> selectTorrentByCategory(String category);
 
-    @Select("SELECT * FROM torrent WHERE torrentName LIKE CONCAT('%', #{key}, '%')")
+    @Select("SELECT * FROM torrent WHERE torrent_name LIKE CONCAT('%', #{key}, '%')")
     List<TORRENT> selectTorrentByKey(String key);
 
     @Select("SELECT * FROM torrent WHERE username = #{username}")
@@ -43,12 +43,12 @@
     int countByCategory(@Param("category") String category);
 
     // 分页搜索关键字
-    @Select("SELECT * FROM torrent WHERE torrentName LIKE CONCAT('%',#{key},'%') OR description LIKE CONCAT('%',#{key},'%') ORDER BY create_time DESC LIMIT #{offset}, #{size}")
+    @Select("SELECT * FROM torrent WHERE torrent_name LIKE CONCAT('%',#{key},'%') OR description LIKE CONCAT('%',#{key},'%') ORDER BY create_time DESC LIMIT #{offset}, #{size}")
     List<TORRENT> selectTorrentByKeyWithPage(@Param("key") String key,
                                              @Param("offset") int offset,
                                              @Param("size") int size);
 
-    @Select("SELECT COUNT(*) FROM torrent WHERE torrentName LIKE CONCAT('%',#{key},'%') OR description LIKE CONCAT('%',#{key},'%')")
+    @Select("SELECT COUNT(*) FROM torrent WHERE torrent_name LIKE CONCAT('%',#{key},'%') OR description LIKE CONCAT('%',#{key},'%')")
     int countByKey(@Param("key") String key);
 
     // 分页查询用户种子
@@ -61,12 +61,12 @@
     int countByUsername(@Param("username") String username);
 
     // 插入Torrent
-    @Insert("INSERT INTO torrent (torrentName, description, category, region, resolution, subtitle, size, hash, username, create_time, filePath) " +
-            "VALUES (#{torrentName}, #{description}, #{category}, #{region}, #{resolution}, #{subtitle}, #{size}, #{hash}, #{username}, CURRENT_TIMESTAMP, #{filePath})")
+    @Insert("INSERT INTO torrent (torrent_name, description, category, region, resolution, subtitle, size, hash, username, create_time, file_path) " +
+            "VALUES (#{torrentName}, #{description}, #{category}, #{region}, #{resolution}, #{subtitle}, #{size}, #{hash}, #{username}, #{createTime}, #{filePath})")
     int insertTorrent(TORRENT torrent);
 
     // 更新Torrent信息
-    @Update("UPDATE torrent SET torrentName = #{torrentName}, description = #{description}, category = #{category}, " +
+    @Update("UPDATE torrent SET torrent_name = #{torrentName}, description = #{description}, category = #{category}, " +
             "tags = #{tags}, size = #{size}, username = #{username} WHERE hash = #{hash}")
     int updateTorrent(TORRENT torrent);
 
diff --git a/src/main/java/com/ptp/ptplatform/mapper/UserMapper.java b/src/main/java/com/ptp/ptplatform/mapper/UserMapper.java
index f17d0dd..eee9042 100644
--- a/src/main/java/com/ptp/ptplatform/mapper/UserMapper.java
+++ b/src/main/java/com/ptp/ptplatform/mapper/UserMapper.java
@@ -1,12 +1,11 @@
 package com.ptp.ptplatform.mapper;
 
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.ptp.ptplatform.entity.USER;
-import org.apache.ibatis.annotations.Insert;
-import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Select;
-import org.apache.ibatis.annotations.Update;
+import org.apache.ibatis.annotations.*;
+
+import java.util.*;
+
 
 @Mapper
 public interface UserMapper extends BaseMapper<USER> {
@@ -17,6 +16,13 @@
     @Select("SELECT * FROM user WHERE username = #{username} AND password = #{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);
+
+    @Select("SELECT username, authority, level, registTime, lastLogin, upload, download, shareRate, magicPoints FROM user")
+    List<USER> selectAllUsers();
+
+
     // 注册用户
     @Insert("INSERT INTO user (username, password, registTime) " +
             "VALUES (#{username}, #{password}, #{registTime})")
@@ -34,4 +40,5 @@
             "magicPoints = #{magicPoints} " +
             "WHERE username = #{username}")
     int updateUser(USER user);
+
 }
diff --git a/src/test/java/com/ptp/ptplatform/controller/DiscountControllerTest.java b/src/test/java/com/ptp/ptplatform/controller/DiscountControllerTest.java
new file mode 100644
index 0000000..30f9b27
--- /dev/null
+++ b/src/test/java/com/ptp/ptplatform/controller/DiscountControllerTest.java
@@ -0,0 +1,111 @@
+package com.ptp.ptplatform.controller;
+
+import com.ptp.ptplatform.entity.DISCOUNT;
+import com.ptp.ptplatform.mapper.DiscountMapper;
+import com.ptp.ptplatform.utils.Result;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.ResponseEntity;
+
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+@SpringBootTest
+public class DiscountControllerTest {
+
+    @Mock
+    private DiscountMapper discountMapper;
+
+    @InjectMocks
+    private DiscountController discountController;
+
+    private DISCOUNT discount;
+
+    @BeforeEach
+    public void setUp() {
+        MockitoAnnotations.openMocks(this);
+        discount = new DISCOUNT("五一节日", LocalDateTime.now().plusDays(1), LocalDateTime.now().plusDays(10), "HALF");
+    }
+
+    @Test
+    public void testAddDiscount_Success() {
+        // Mock the insert method to return 1 (success)
+        when(discountMapper.insertDiscount(discount)).thenReturn(1);
+
+        Result result = discountController.addDiscount(discount);
+
+        assertNotNull(result);
+        assertEquals(200, result.getCode());
+        verify(discountMapper, times(1)).insertDiscount(discount);
+    }
+
+    @Test
+    public void testAddDiscount_Failure_DuplicateTime() {
+        // Mock the checkOverlapTime method to return > 0 (overlap found)
+        when(discountMapper.checkOverlapTime(discount.getStartTime(), discount.getEndTime())).thenReturn(1);
+
+        Result result = discountController.addDiscount(discount);
+
+        assertNotNull(result);
+        assertEquals(500, result.getCode());
+        assertEquals("新折扣时间与已有折扣时间重叠,请选择不同时间。", result.getMessage());
+    }
+
+    @Test
+    public void testDeleteDiscount_Success() {
+        // Mock the delete method to return 1 (success)
+        when(discountMapper.deleteDiscountById(1)).thenReturn(1);
+
+        Result result = discountController.deleteDiscount(1);
+
+        assertNotNull(result);
+        assertEquals(200, result.getCode());
+        assertEquals("删除成功。", result.getMessage());
+        verify(discountMapper, times(1)).deleteDiscountById(1);
+    }
+
+    @Test
+    public void testDeleteDiscount_Failure_NotFound() {
+        // Mock the delete method to return 0 (failure)
+        when(discountMapper.deleteDiscountById(1)).thenReturn(0);
+
+        Result result = discountController.deleteDiscount(1);
+
+        assertNotNull(result);
+        assertEquals(500, result.getCode());
+        assertEquals("折扣未找到。", result.getMessage());
+    }
+
+    @Test
+    public void testGetCurrentDiscount_Success() {
+        // Mock the selectCurrentDiscount method to return a discount
+        when(discountMapper.selectCurrentDiscount()).thenReturn(discount);
+
+        Result result = discountController.getCurrentDiscount();
+
+        assertNotNull(result);
+        assertEquals(200, result.getCode());
+        assertEquals(discount, result.getData().get("data"));
+    }
+
+    @Test
+    public void testGetAllDiscounts_Success() {
+        // Mock the selectAllDiscounts method to return a list of discounts
+        when(discountMapper.selectAllDiscounts()).thenReturn(Collections.singletonList(discount));
+
+        Result result = discountController.getAllDiscounts();
+
+        assertNotNull(result);
+        assertEquals(200, result.getCode());
+        assertTrue(result.getData().get("data") instanceof List);
+        assertEquals(1, ((List) result.getData().get("data")).size());
+    }
+}
diff --git a/src/test/java/com/ptp/ptplatform/controller/UserControllerTest.java b/src/test/java/com/ptp/ptplatform/controller/UserControllerTest.java
index a413bda..bc8250f 100644
--- a/src/test/java/com/ptp/ptplatform/controller/UserControllerTest.java
+++ b/src/test/java/com/ptp/ptplatform/controller/UserControllerTest.java
@@ -15,10 +15,9 @@
 import org.mockito.Mock;
 import org.mockito.MockedStatic;
 import org.mockito.MockitoAnnotations;
+import org.springframework.mock.web.MockHttpServletRequest;
 
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
 
 import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.Mockito.*;
@@ -277,4 +276,82 @@
             assertEquals(testUser, result);
         }
     }
+
+    @Test
+    public void testSearchUser() {
+        // 模拟 JWT 验证
+        Claims mockClaims = mock(Claims.class);
+        when(mockClaims.getSubject()).thenReturn("testUser");
+
+        try (MockedStatic<JwtUtils> mockedJwtUtils = mockStatic(JwtUtils.class)) {
+            // 模拟 JwtUtils 行为
+            mockedJwtUtils.when(() -> JwtUtils.getClaimByToken(anyString())).thenReturn(mockClaims);
+
+            // 模拟用户查询
+            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)
+            );
+            when(userMapper.searchUsername("testUser", "zzz")).thenReturn(mockResults);
+
+            // 创建请求并添加 Authorization 头
+            MockHttpServletRequest request = new MockHttpServletRequest();
+            request.addHeader("Authorization", "mockToken");
+
+            // 测试有搜索关键词的情况
+            Result result = userController.searchUser("zzz", request);
+            assertEquals(200, result.getCode());
+            assertNotNull(result.getData().get("data"));
+            assertEquals(2, ((List<?>) result.getData().get("data")).size());
+
+            // 测试无搜索关键词的情况
+            Result errorResult = userController.searchUser("", request);
+            assertEquals(500, errorResult.getCode());
+            assertEquals("请提供搜索关键词", errorResult.getMessage());
+        }
+    }
+
+    @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)
+        );
+        when(userMapper.selectAllUsers()).thenReturn(mockUsers);
+
+        Result result = userController.getAllUser();
+        assertEquals(200, result.getCode());
+        assertNotNull(result.getData().get("data"));
+        assertEquals(3, ((List<?>) result.getData().get("data")).size());
+
+        // 测试空列表情况
+        when(userMapper.selectAllUsers()).thenReturn(Collections.emptyList());
+        Result emptyResult = userController.getAllUser();
+        assertEquals(200, emptyResult.getCode());
+        assertTrue(((List<?>) emptyResult.getData().get("data")).isEmpty());
+    }
+
+    @Test
+    public void testChangeAuthority() {
+        // 测试成功修改权限
+        Map<String, String> authorityMap = new HashMap<>();
+        authorityMap.put("changeUsername", "testUser");
+        authorityMap.put("authority", "ADMIN");
+
+        USER mockUser = new USER("testUser", "password", USER.Authority.USER);
+        when(userMapper.selectByUsername("testUser")).thenReturn(mockUser);
+        when(userMapper.updateUser(any(USER.class))).thenReturn(1);
+
+        Result successResult = userController.changeAuthority(new MockHttpServletRequest(), authorityMap);
+        assertEquals(200, successResult.getCode());
+        assertEquals("修改用户权限成功", successResult.getMessage());
+
+    }
+
 }
\ No newline at end of file
diff --git "a/torrents/\133\347\224\265\345\275\261\345\244\251\345\240\202www.dytt89.com\135\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/\133\347\224\265\345\275\261\345\244\251\345\240\202www.dytt89.com\135\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/\133\347\224\265\345\275\261\345\244\251\345\240\202www.dytt89.com\135\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