Merge changes I10e91036,I4838ea80

* changes:
  求助要资源帖子相关后端
  好友相关后端
diff --git a/src/main/java/com/pt5/pthouduan/controller/CategoryController.java b/src/main/java/com/pt5/pthouduan/controller/CategoryController.java
new file mode 100644
index 0000000..ec013c3
--- /dev/null
+++ b/src/main/java/com/pt5/pthouduan/controller/CategoryController.java
@@ -0,0 +1,24 @@
+package com.pt5.pthouduan.controller;
+
+import com.pt5.pthouduan.entity.Category;
+import com.pt5.pthouduan.service.CategoryService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/categories")
+@CrossOrigin
+public class CategoryController {
+    @Autowired
+    private CategoryService categoryService;
+
+    @GetMapping
+    public List<Category> getAllCategories() {
+        return categoryService.getAllCategories();
+    }
+}
diff --git a/src/main/java/com/pt5/pthouduan/controller/TorrentController.java b/src/main/java/com/pt5/pthouduan/controller/TorrentController.java
index e864f20..b623e7a 100644
--- a/src/main/java/com/pt5/pthouduan/controller/TorrentController.java
+++ b/src/main/java/com/pt5/pthouduan/controller/TorrentController.java
@@ -57,6 +57,361 @@
 
 
 
+    @GetMapping("/{infoHash}/seeders")
+    public ResponseEntity<List<PeerInfo>> getSeedersByInfoHash(@PathVariable String infoHash) {
+        List<PeerInfo> seeders = peerService.getSeedersByInfoHash(infoHash.toUpperCase());
+        System.out.println("Response: " + seeders);
+        return ResponseEntity.ok(seeders);
+    }
+
+    //添加搜索
+    @GetMapping("/search")
+    public ResponseEntity<List<Torrent>> searchTorrents(@RequestParam("keyword") String keyword) {
+        List<Torrent> result = torrentService.searchByKeyword(keyword);
+        return ResponseEntity.ok(result);
+    }
+
+    @PostMapping("/addcredit")
+    public ResponseEntity<?> addCredit(@RequestParam("manageid") Long manageid,@RequestParam("userid") Long userid,@RequestParam("credit") Integer credit,@RequestParam("helpedId") Long helpedId) {
+        try {
+            torrentService.addcredit(manageid,userid,credit);
+            torrentService.deducecredit(manageid,helpedId,credit);
+        } catch (Exception e) {
+            return ResponseEntity.internalServerError().body(Map.of(
+                    "success", false,
+                    "message", "服务器错误: " + e.getMessage()
+            ));
+        }
+        return ResponseEntity.ok(credit);  //成功返回保种积分
+    }
+
+    @PostMapping("/deducecredit")
+    public ResponseEntity<?> duduceCredit(@RequestParam("manageid") Long manageid,@RequestParam("userid") Long userid,@RequestParam("credit") Integer credit) {
+        try {
+            torrentService.deducecredit(manageid,userid,credit);
+        } catch (Exception e) {
+            return ResponseEntity.internalServerError().body(Map.of(
+                    "success", false,
+                    "message", "服务器错误: " + e.getMessage()
+            ));
+        }
+        return ResponseEntity.ok(credit);  //成功返回保种积分
+    }
+
+
+   //显示所有种子
+    @GetMapping("/list")
+    public List<Torrent> getAllTorrents() {
+        return torrentService.getAllTorrents();
+    }
+
+    // 按分类获取种子(带 category 参数)
+    @GetMapping("/listByCategory")
+    public List<Torrent> getTorrentsByCategory(@RequestParam(required = false) Integer categoryid) {
+        if (categoryid == null) {
+            return torrentService.getAllTorrents();
+        }
+        return torrentService.getTorrentsByCategory(categoryid); // 否则按分类过滤
+    }
+
+    @GetMapping("/listByCategorywithfilter")
+    public List<Torrent> getTorrentsByCategorywithFilter(
+            @RequestParam Integer categoryid,
+            @RequestParam Map<String, String> filters
+    ){
+        filters.remove("categoryid");
+        List<Torrent> torrents = torrentService.getTorrentsByCategorywithfilters(categoryid,filters);
+        return torrents;
+    }
+
+
+
+    // 获取单个种子详情
+    @GetMapping("/{id}")
+    public ResponseEntity<?> getTorrentById(@PathVariable Long id) {
+        try {
+            Torrent torrent = torrentService.getTorrentById(id);
+            if (torrent == null) {
+                return ResponseEntity.notFound().build(); // 如果种子不存在,返回 404
+            }
+            System.out.println(torrent);
+            return ResponseEntity.ok(torrent); // 返回种子详情
+        } catch (Exception e) {
+            return ResponseEntity.badRequest().body("获取种子详情失败: " + e.getMessage());
+        }
+    }
+//未更新前端前
+//    @PostMapping("/upload")
+//    public ResponseEntity<?> uploadTorrent(
+//            @RequestParam("file") MultipartFile torrentFile,
+//            @RequestParam("title") String title,
+//            @RequestParam("description") String description
+//            //@AuthenticationPrincipal User user
+//            //User user
+//    ){
+//        // 创建临时用户对象
+//        User user = new User(1L,"testuser");
+//        //user.setUserid(1L); // 设置测试用户ID
+//        try{
+//            //torrentService.Upload(torrentFile,title,description,user);
+//            return torrentService.Upload(torrentFile, title, description, user);
+//        }catch(Exception e){
+//            return ResponseEntity.badRequest().body("Upload failed:" + e.getMessage());
+//        }
+//    }
+//@PostMapping("/upload")
+//public ResponseEntity<?> uploadTorrent(
+//        @RequestParam("file") MultipartFile torrentFile,
+//        @RequestParam("title") String title,
+//        @RequestParam("description") String description,
+//        @RequestParam("categoryId") Integer categoryId,
+//        @RequestParam(value = "dpi",required = false) String dpi,
+//        @RequestParam(value = "caption", required = false) String caption){
+//    // 创建临时用户对象
+//    User user = new User(1L,"testuser");
+//    //user.setUserid(1L); // 设置测试用户ID
+//    try{
+//        //torrentService.Upload(torrentFile,title,description,user);
+//        return torrentService.Upload(torrentFile, title, description, categoryId, dpi,caption,user);
+//    }catch(Exception e){
+//        return ResponseEntity.badRequest().body("Upload failed:" + e.getMessage());
+//    }
+//}
+
+    @GetMapping("/{userId}/username")
+    public ResponseEntity<String> getUsernameByUserId(@PathVariable Long userId) {
+        try {
+            // 调用 Mapper 查询用户名
+            String username = userMapper.selectUsernameByUserid(userId);
+            //System.out.println("Passkey for userId " + userId + ": " + passkey); // 调试日志
+
+            if (username == null) {
+                // 用户不存在
+                return ResponseEntity.notFound().build();
+            }
+
+            // 返回用户名
+            return ResponseEntity.ok(username);
+        } catch (Exception e) {
+            e.printStackTrace();
+            // 其他异常(如数据库错误)
+            return ResponseEntity.internalServerError().build();
+        }
+    }
+//触发检查是否需要促销
+@PostMapping("/applyPromotions")
+public ResponseEntity<?> applyPromotions(@RequestParam("userid") Long userid)
+{
+    if(userMapper.getpermissionByUserid(userid) == 1)
+    {
+        torrentMapper.setFreePromotion();
+        torrentMapper.setDoubleUpload();
+        torrentMapper.setHalfDownload();
+        torrentMapper.setDoubleUpload();
+    }else{
+        throw new SecurityException("无权执行此操作");
+    }
+
+    return ResponseEntity.ok(Map.of(
+            "success", true,
+            "message", "促销规则已应用"
+    ));
+}
+
+//设置促销类型
+@PostMapping("/setPromotion")
+public ResponseEntity<?> setPromotion(
+        @RequestParam("userid") Long userid,
+        @RequestParam("torrentId") Long torrentId,
+        @RequestParam("promotionId") Long promotionId){
+    try {
+        //1-上传加倍;2-下载减半;3-免费下载;NULL-没有促销
+        torrentService.setPromotion(torrentId, promotionId, userid);
+
+        return ResponseEntity.ok(Map.of(
+                "success", true,
+                "message", "促销类型设置成功"
+        ));
+    } catch (SecurityException e) {
+        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(
+                Map.of("success", false, "message", e.getMessage())
+        );
+    } catch (Exception e) {
+        return ResponseEntity.internalServerError().body(
+                Map.of("success", false, "message", "设置促销类型失败: " + e.getMessage())
+        );
+    }
+}
+@DeleteMapping("/delete/{torrentId}")
+public ResponseEntity<?> deleteTorrent(
+        @RequestParam("userid") Long userid,
+        @PathVariable Long torrentId) {
+        System.out.println("到这里了");
+
+    try {
+        // 验证权限并删除
+        torrentService.deleteTorrent(userid,torrentId);
+        return ResponseEntity.ok().body(
+                Map.of("success", true, "message", "种子删除成功")
+        );
+    } catch (SecurityException e) {
+        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(
+                Map.of("success", false, "message", e.getMessage())
+        );
+    } catch (Exception e) {
+        return ResponseEntity.internalServerError().body(
+                Map.of("success", false, "message", "删除失败: " + e.getMessage())
+        );
+    }
+}
+//@PostMapping("/upload")
+//public ResponseEntity<?> uploadTorrent(
+//        @RequestParam("userid") Long userid,
+//        @RequestParam("file") MultipartFile torrentFile,
+//        @RequestParam("title") String title,
+//        @RequestParam("description") String description,
+//        @RequestParam("categoryId") Integer categoryId,
+//        // 以下为通用扩展字段(根据类型选择性使用)
+//        @RequestParam(value = "dpi",required = false) String dpi,
+//        @RequestParam(value = "caption", required = false) String caption,
+//        @RequestParam(value = "region", required = false) String region,
+//        @RequestParam(value = "year", required = false) Integer year,
+//        @RequestParam(value = "genre", required = false) String genre,
+//        @RequestParam(value = "format", required = false) String format,
+//        @RequestParam(value = "resolution", required = false) String resolution,
+//        @RequestParam(value = "codecFormat", required = false) String codecFormat,
+//        @RequestParam(value = "platform", required = false) String platform,
+//        @RequestParam(value = "language", required = false) String language,
+//        @RequestParam(value = "eventType", required = false) String eventType,
+//        @RequestParam(value = "dataType", required = false) String dataType,
+//        @RequestParam(value = "source", required = false) String source,
+//        @RequestParam(value = "style", required = false) String style,
+//        @RequestParam(value = "isMainland", required = false) Boolean isMainland){
+//    // 创建临时用户对象
+//    //User user = new User(1L,"testuser");
+//    //User user = userMapper.selectById(userid);
+//
+//    // 构建扩展参数Map
+//    Map<String, String> extraParams = new HashMap<>();
+//
+//    // 通用参数
+//    putIfNotNull(extraParams, "dpi", dpi);
+//    putIfNotNull(extraParams, "caption", caption);
+//    putIfNotNull(extraParams, "region", region);
+//    putIfNotNull(extraParams, "year", year != null ? year.toString() : null);
+//    putIfNotNull(extraParams, "genre", genre);
+//    putIfNotNull(extraParams, "format", format);
+//    putIfNotNull(extraParams, "resolution", resolution);
+//
+//    // 特殊参数
+//    putIfNotNull(extraParams, "codecFormat", codecFormat);  // 电影编码格式
+//    putIfNotNull(extraParams, "platform", platform);        // 游戏/软件平台
+//    putIfNotNull(extraParams, "language", language);        // 游戏语言
+//    putIfNotNull(extraParams, "eventType", eventType);      // 体育赛事类型
+//    putIfNotNull(extraParams, "source", source);            // 纪录片来源
+//    putIfNotNull(extraParams, "style", style);              // 音乐风格
+//    putIfNotNull(extraParams, "isMainland", isMainland != null ? isMainland.toString() : null); // 综艺是否大陆
+//    putIfNotNull(extraParams, "dataType", dataType);
+//
+////    extraParams.put("region", "CN");
+////    extraParams.put("year", "2023");
+////    extraParams.put("genre", "Action");
+////    extraParams.put("encodeFormat", "H.264");
+////    extraParams.put("resolution", "1080p");
+//
+//
+//    //user.setUserid(1L); // 设置测试用户ID
+//    try{
+//        //torrentService.Upload(torrentFile,title,description,user);
+//        //return torrentService.Upload(torrentFile, title, description, categoryId, dpi,caption,user);
+//
+////        return torrentService.uploadWithCategory(
+////                torrentFile, title, description, categoryId, user, dpi, caption,
+////                region, year, genre, format, resolution, codecFormat,
+////                platform, language, eventType, source, style, isMainland
+////        );
+//        // 调用Service
+//        return torrentService.uploadWithCategory(
+//                torrentFile,
+//                title,
+//                description,
+//                categoryId,
+//                userid,
+//                extraParams
+//        );
+//    }catch(Exception e){
+//        return ResponseEntity.badRequest().body("Upload failed:" + e.getMessage());
+//    }
+//}
+@PostMapping("/fullrequest")
+public ResponseEntity<?> uploadTorrentinrequest(
+        @RequestParam("userid") Long userid,
+        @RequestParam("file") MultipartFile torrentFile,          // 种子文件
+        @RequestParam("coverImage") MultipartFile coverImage,    // 封面图片
+        @RequestParam("title") String title,
+        @RequestParam("description") String description,
+        @RequestParam("categoryId") Integer categoryId,
+        @RequestParam("requestId") Integer requestId,
+        // 以下为通用扩展字段(根据类型选择性使用)
+        @RequestParam(value = "dpi", required = false) String dpi,
+        @RequestParam(value = "caption", required = false) String caption,
+        @RequestParam(value = "region", required = false) String region,
+        @RequestParam(value = "year", required = false) Integer year,
+        @RequestParam(value = "genre", required = false) String genre,
+        @RequestParam(value = "format", required = false) String format,
+        @RequestParam(value = "resolution", required = false) String resolution,
+        @RequestParam(value = "codecFormat", required = false) String codecFormat,
+        @RequestParam(value = "platform", required = false) String platform,
+        @RequestParam(value = "language", required = false) String language,
+        @RequestParam(value = "eventType", required = false) String eventType,
+        @RequestParam(value = "dataType", required = false) String dataType,
+        @RequestParam(value = "source", required = false) String source,
+        @RequestParam(value = "style", required = false) String style,
+        @RequestParam(value = "isMainland", required = false) Boolean isMainland) {
+
+    // 创建临时用户对象
+    //User user = new User(1L,"testuser");
+    //User user = userMapper.selectById(userid);
+
+    // 构建扩展参数Map
+    Map<String, String> extraParams = new HashMap<>();
+
+    // 通用参数
+    putIfNotNull(extraParams, "dpi", dpi);
+    putIfNotNull(extraParams, "caption", caption);
+    putIfNotNull(extraParams, "region", region);
+    putIfNotNull(extraParams, "year", year != null ? year.toString() : null);
+    putIfNotNull(extraParams, "genre", genre);
+    putIfNotNull(extraParams, "format", format);
+    putIfNotNull(extraParams, "resolution", resolution);
+
+    // 特殊参数
+    putIfNotNull(extraParams, "codecFormat", codecFormat);  // 电影编码格式
+    putIfNotNull(extraParams, "platform", platform);        // 游戏/软件平台
+    putIfNotNull(extraParams, "language", language);        // 游戏语言
+    putIfNotNull(extraParams, "eventType", eventType);      // 体育赛事类型
+    putIfNotNull(extraParams, "source", source);            // 纪录片来源
+    putIfNotNull(extraParams, "style", style);              // 音乐风格
+    putIfNotNull(extraParams, "isMainland", isMainland != null ? isMainland.toString() : null); // 综艺是否大陆
+    putIfNotNull(extraParams, "dataType", dataType);
+
+
+    try {
+        // 调用Service,传递封面图片
+        return torrentService.uploadtohelp(
+                requestId,
+                torrentFile,
+                coverImage,          // 新增的封面图片参数
+                title,
+                description,
+                categoryId,
+                userid,
+                extraParams
+        );
+    } catch (Exception e) {
+        return ResponseEntity.badRequest().body("Upload failed:" + e.getMessage());
+    }
+}
 @PostMapping("/upload")
 public ResponseEntity<?> uploadTorrent(
         @RequestParam("userid") Long userid,
diff --git a/src/main/java/com/pt5/pthouduan/entity/Category.java b/src/main/java/com/pt5/pthouduan/entity/Category.java
index c338fbb..e134092 100644
--- a/src/main/java/com/pt5/pthouduan/entity/Category.java
+++ b/src/main/java/com/pt5/pthouduan/entity/Category.java
@@ -7,7 +7,7 @@
 
 /**
  * <p>
- * 
+ * 分类表
  * </p>
  *
  * @author ljx
@@ -21,10 +21,20 @@
     @TableId("categoryid")
     private Integer categoryid;
 
-    private Integer categorytype;
+    private String category_name;  // 数据库列名
 
-    private String categoryName;
+    private String category_description;  // 数据库列名
 
+    // 构造方法
+    public Category() {}
+
+    public Category(Integer categoryid, String category_name, String category_description) {
+        this.categoryid = categoryid;
+        this.category_name = category_name;
+        this.category_description = category_description;
+    }
+
+    // Getter 和 Setter 方法(遵循JavaBean规范)
     public Integer getCategoryid() {
         return categoryid;
     }
@@ -33,28 +43,28 @@
         this.categoryid = categoryid;
     }
 
-    public Integer getCategorytype() {
-        return categorytype;
+    public String getCategory_name() {  // 注意方法名必须和属性名一致
+        return category_name;
     }
 
-    public void setCategorytype(Integer categorytype) {
-        this.categorytype = categorytype;
+    public void setCategory_name(String category_name) {  // 注意方法名必须和属性名一致
+        this.category_name = category_name;
     }
 
-    public String getCategoryName() {
-        return categoryName;
+    public String getCategory_description() {
+        return category_description;
     }
 
-    public void setCategoryName(String categoryName) {
-        this.categoryName = categoryName;
+    public void setCategory_description(String category_description) {
+        this.category_description = category_description;
     }
 
     @Override
     public String toString() {
         return "Category{" +
-        "categoryid = " + categoryid +
-        ", categorytype = " + categorytype +
-        ", categoryName = " + categoryName +
-        "}";
+                "categoryid=" + categoryid +
+                ", category_name='" + category_name + '\'' +  // 修正拼写错误
+                ", category_description='" + category_description + '\'' +  // 修正字段名
+                '}';
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/pt5/pthouduan/mapper/CategoryMapper.java b/src/main/java/com/pt5/pthouduan/mapper/CategoryMapper.java
index fac2a81..5aba81d 100644
--- a/src/main/java/com/pt5/pthouduan/mapper/CategoryMapper.java
+++ b/src/main/java/com/pt5/pthouduan/mapper/CategoryMapper.java
@@ -4,6 +4,8 @@
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 /**
  * <p>
  *  Mapper 接口
@@ -14,5 +16,5 @@
  */
 @Mapper
 public interface CategoryMapper extends BaseMapper<Category> {
-
+    List<Category> getAllCategories();
 }
diff --git a/src/main/java/com/pt5/pthouduan/service/CategoryService.java b/src/main/java/com/pt5/pthouduan/service/CategoryService.java
new file mode 100644
index 0000000..6eb2efe
--- /dev/null
+++ b/src/main/java/com/pt5/pthouduan/service/CategoryService.java
@@ -0,0 +1,9 @@
+package com.pt5.pthouduan.service;
+
+import com.pt5.pthouduan.entity.Category;
+
+import java.util.List;
+
+public interface CategoryService {
+    List<Category> getAllCategories();
+}
diff --git a/src/main/java/com/pt5/pthouduan/service/impl/CategoryServiceImp.java b/src/main/java/com/pt5/pthouduan/service/impl/CategoryServiceImp.java
new file mode 100644
index 0000000..a98507b
--- /dev/null
+++ b/src/main/java/com/pt5/pthouduan/service/impl/CategoryServiceImp.java
@@ -0,0 +1,19 @@
+package com.pt5.pthouduan.service.impl;
+
+import com.pt5.pthouduan.entity.Category;
+import com.pt5.pthouduan.mapper.CategoryMapper;
+import com.pt5.pthouduan.service.CategoryService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class CategoryServiceImp implements CategoryService {
+    @Autowired
+    private CategoryMapper categoryMapper;
+    @Override
+    public List<Category> getAllCategories() {
+        return categoryMapper.getAllCategories();
+    }
+}
diff --git a/src/main/resources/mapper/xml/CategoryMapper.xml b/src/main/resources/mapper/xml/CategoryMapper.xml
new file mode 100644
index 0000000..7793c47
--- /dev/null
+++ b/src/main/resources/mapper/xml/CategoryMapper.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.pt5.pthouduan.mapper.CategoryMapper">
+
+    <!-- 通用查询映射结果 -->
+    <!-- 定义结果映射 -->
+    <resultMap id="CategoryResultMap" type="com.pt5.pthouduan.entity.Category">
+        <id property="categoryid" column="categoryid"/>
+        <result property="category_name" column="category_name"/>
+        <result property="category_description" column="category_description"/>
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        categoryid, category_name, category_description
+    </sql>
+    <!-- 查询所有分类 -->
+    <select id="getAllCategories" resultMap="CategoryResultMap">
+        SELECT
+            categoryid,
+            category_name,
+            category_description
+        FROM
+            category
+    </select>
+
+</mapper>
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/CategoryControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/CategoryControllerTest.java
new file mode 100644
index 0000000..002cfdb
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/CategoryControllerTest.java
@@ -0,0 +1,58 @@
+package com.pt5.pthouduan.ControllerTest;
+
+import com.pt5.pthouduan.controller.CategoryController;
+import com.pt5.pthouduan.entity.Category;
+import com.pt5.pthouduan.service.CategoryService;
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class CategoryControllerTest {
+
+    private MockMvc mockMvc;
+
+    @Mock
+    private CategoryService categoryService;
+
+    @InjectMocks
+    private CategoryController categoryController;
+
+    @BeforeEach
+    public void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(categoryController).build();
+    }
+
+    @Test
+    public void testGetAllCategories() throws Exception {
+        // 准备模拟数据
+        Category category1 = new Category();
+        category1.setCategoryid(1);
+        category1.setCategory_name("电子产品");
+
+        Category category2 = new Category();
+        category2.setCategoryid(2);
+        category2.setCategory_name("服装");
+
+        List<Category> categories = Arrays.asList(category1, category2);
+
+        // 模拟 service 行为
+        when(categoryService.getAllCategories()).thenReturn(categories);
+
+        // 执行请求并验证结果
+        mockMvc.perform(get("/categories"))
+                .andExpect(status().isOk());
+    }
+}
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentAddCreditControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentAddCreditControllerTest.java
new file mode 100644
index 0000000..6730c94
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentAddCreditControllerTest.java
@@ -0,0 +1,125 @@
+package com.pt5.pthouduan.ControllerTest;
+
+
+
+import com.pt5.pthouduan.controller.TorrentController;
+import com.pt5.pthouduan.service.TorrentService;
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+class TorrentAddCreditControllerTest {
+
+    @Mock
+    private TorrentService torrentService;
+
+    @InjectMocks
+    private TorrentController torrentController;
+
+    private MockMvc mockMvc;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(torrentController).build();
+    }
+
+    @Test
+    void addCredit_WithValidParameters_ShouldReturnSuccess() throws Exception {
+        // Arrange
+        Long manageid = 1L;
+        Long userid = 2L;
+        Integer credit = 10;
+        Long helpedId = 3L;
+
+        // 模拟服务层调用成功
+        doNothing().when(torrentService).addcredit(manageid, userid, credit);
+        doNothing().when(torrentService).deducecredit(manageid, helpedId, credit);
+
+        // Act & Assert
+        mockMvc.perform(post("/torrent/addcredit")
+                        .param("manageid", manageid.toString())
+                        .param("userid", userid.toString())
+                        .param("credit", credit.toString())
+                        .param("helpedId", helpedId.toString()))
+                .andExpect(status().isOk())
+                .andExpect(content().string(credit.toString()));
+
+        // 验证服务层方法被调用
+        verify(torrentService, times(1)).addcredit(manageid, userid, credit);
+        verify(torrentService, times(1)).deducecredit(manageid, helpedId, credit);
+    }
+
+    @Test
+    void addCredit_WhenAddCreditFails_ShouldReturnServerError() throws Exception {
+        // Arrange
+        Long manageid = 1L;
+        Long userid = 2L;
+        Integer credit = 10;
+        Long helpedId = 3L;
+
+        // 模拟addcredit抛出异常
+        doThrow(new RuntimeException("Add credit failed")).when(torrentService).addcredit(manageid, userid, credit);
+        // deducecredit不应该被调用
+        doNothing().when(torrentService).deducecredit(anyLong(), anyLong(), anyInt());
+
+        // Act & Assert
+        mockMvc.perform(post("/torrent/addcredit")
+                        .param("manageid", manageid.toString())
+                        .param("userid", userid.toString())
+                        .param("credit", credit.toString())
+                        .param("helpedId", helpedId.toString()))
+                .andExpect(status().isInternalServerError())
+                .andExpect(jsonPath("$.success").value(false))
+                .andExpect(jsonPath("$.message").value("服务器错误: Add credit failed"));
+
+        // 验证服务层方法调用情况
+        verify(torrentService, times(1)).addcredit(manageid, userid, credit);
+        verify(torrentService, never()).deducecredit(anyLong(), anyLong(), anyInt());
+    }
+
+    @Test
+    void addCredit_WhenDeduceCreditFails_ShouldReturnServerError() throws Exception {
+        // Arrange
+        Long manageid = 1L;
+        Long userid = 2L;
+        Integer credit = 10;
+        Long helpedId = 3L;
+
+        // 模拟addcredit成功
+        doNothing().when(torrentService).addcredit(manageid, userid, credit);
+        // 模拟deducecredit抛出异常
+        doThrow(new RuntimeException("Deduce credit failed")).when(torrentService).deducecredit(manageid, helpedId, credit);
+
+        // Act & Assert
+        mockMvc.perform(post("/torrent/addcredit")
+                        .param("manageid", manageid.toString())
+                        .param("userid", userid.toString())
+                        .param("credit", credit.toString())
+                        .param("helpedId", helpedId.toString()))
+                .andExpect(status().isInternalServerError())
+                .andExpect(jsonPath("$.success").value(false))
+                .andExpect(jsonPath("$.message").value("服务器错误: Deduce credit failed"));
+
+        // 验证服务层方法调用情况
+        verify(torrentService, times(1)).addcredit(manageid, userid, credit);
+        verify(torrentService, times(1)).deducecredit(manageid, helpedId, credit);
+    }
+
+    @Test
+    void addCredit_WithoutParameters_ShouldReturnBadRequest() throws Exception {
+        // Act & Assert
+        mockMvc.perform(post("/torrent/addcredit"))
+                .andExpect(status().isBadRequest());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentApplyPromotionsControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentApplyPromotionsControllerTest.java
new file mode 100644
index 0000000..78ab203
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentApplyPromotionsControllerTest.java
@@ -0,0 +1,76 @@
+package com.pt5.pthouduan.ControllerTest;
+
+import com.pt5.pthouduan.controller.TorrentController;
+import com.pt5.pthouduan.mapper.TorrentMapper;
+import com.pt5.pthouduan.mapper.UserMapper;
+import com.pt5.pthouduan.service.TorrentService;
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+class TorrentApplyPromotionsControllerTest {
+
+    @Mock
+    private UserMapper userMapper;
+
+    @Mock
+    private TorrentService torrentService;
+
+    @Mock
+    private TorrentMapper torrentMapper;  // Mock TorrentMapper
+
+    @InjectMocks
+    private TorrentController torrentController;
+
+    private MockMvc mockMvc;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(torrentController).build();
+    }
+
+    @Test
+    void applyPromotions_WithPermission_ShouldReturnSuccess() throws Exception {
+        // Arrange
+        Long userid = 1L;
+        Long torrentId = 101L;
+        Long helpedId = 2L;
+
+        // 模拟用户有权限(返回1表示有权限)
+        when(userMapper.getpermissionByUserid(userid)).thenReturn(1);
+
+        // 模拟服务层调用成功
+        doNothing().when(torrentService).addcredit(anyLong(), anyLong(), anyInt());
+        doNothing().when(torrentService).deducecredit(anyLong(), anyLong(), anyInt());
+
+        // 模拟 TorrentMapper 方法调用
+        //when(torrentMapper.setFreePromotion()).thenReturn(1);  // 关键:Mock torrentMapper
+
+        // Act & Assert
+        mockMvc.perform(post("/torrent/applyPromotions")
+                        .param("userid", userid.toString())
+                        .param("torrentid", torrentId.toString())
+                        .param("helpedid", helpedId.toString()))
+                .andExpect(status().isOk())  // 验证状态码
+                .andExpect(content().string(containsString("success")));  // 验证响应内容
+    }
+
+
+    @Test
+    void applyPromotions_WithoutParameters_ShouldReturnBadRequest() throws Exception {
+        // Act & Assert
+        mockMvc.perform(post("/torrent/applyPromotions"))
+                .andExpect(status().isBadRequest());  // 验证状态码
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentDeduceCreditControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentDeduceCreditControllerTest.java
new file mode 100644
index 0000000..99c2a09
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentDeduceCreditControllerTest.java
@@ -0,0 +1,87 @@
+package com.pt5.pthouduan.ControllerTest;
+
+
+import com.pt5.pthouduan.controller.TorrentController;
+import com.pt5.pthouduan.service.TorrentService;
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+class TorrentDeduceCreditControllerTest {
+
+    @Mock
+    private TorrentService torrentService;
+
+    @InjectMocks
+    private TorrentController torrentController;
+
+    private MockMvc mockMvc;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(torrentController).build();
+    }
+
+    @Test
+    void deduceCredit_WithValidParameters_ShouldReturnSuccess() throws Exception {
+        // Arrange
+        Long manageid = 1L;
+        Long userid = 2L;
+        Integer credit = 10;
+
+        // 模拟服务层调用成功
+        doNothing().when(torrentService).deducecredit(manageid, userid, credit);
+
+        // Act & Assert
+        mockMvc.perform(post("/torrent/deducecredit")
+                        .param("manageid", manageid.toString())
+                        .param("userid", userid.toString())
+                        .param("credit", credit.toString()))
+                .andExpect(status().isOk())
+                .andExpect(content().string(credit.toString()));
+
+        // 验证服务层方法被调用
+        verify(torrentService, times(1)).deducecredit(manageid, userid, credit);
+    }
+
+    @Test
+    void deduceCredit_WhenServiceThrowsException_ShouldReturnServerError() throws Exception {
+        // Arrange
+        Long manageid = 1L;
+        Long userid = 2L;
+        Integer credit = 10;
+
+        // 模拟服务层抛出异常
+        doThrow(new RuntimeException("Database error")).when(torrentService).deducecredit(manageid, userid, credit);
+
+        // Act & Assert
+        mockMvc.perform(post("/torrent/deducecredit")
+                        .param("manageid", manageid.toString())
+                        .param("userid", userid.toString())
+                        .param("credit", credit.toString()))
+                .andExpect(status().isInternalServerError())
+                .andExpect(jsonPath("$.success").value(false))
+                .andExpect(jsonPath("$.message").value("服务器错误: Database error"));
+
+        // 验证服务层方法被调用
+        verify(torrentService, times(1)).deducecredit(manageid, userid, credit);
+    }
+
+    @Test
+    void deduceCredit_WithoutParameters_ShouldReturnBadRequest() throws Exception {
+        // Act & Assert
+        mockMvc.perform(post("/torrent/deducecredit"))
+                .andExpect(status().isBadRequest());
+    }
+}
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentDeleteControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentDeleteControllerTest.java
new file mode 100644
index 0000000..ad0bcdb
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentDeleteControllerTest.java
@@ -0,0 +1,104 @@
+package com.pt5.pthouduan.ControllerTest;
+
+
+import com.pt5.pthouduan.controller.TorrentController;
+import com.pt5.pthouduan.service.TorrentService;
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+class TorrentDeleteControllerTest {
+
+    @Mock
+    private TorrentService torrentService;
+
+    @InjectMocks
+    private TorrentController torrentController;
+
+    private MockMvc mockMvc;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(torrentController).build();
+    }
+
+    @Test
+    void deleteTorrent_WithPermission_ShouldReturnSuccess() throws Exception {
+        // Arrange
+        Long userid = 1L;
+        Long torrentId = 101L;
+
+        // 模拟服务层调用成功
+        doNothing().when(torrentService).deleteTorrent(userid, torrentId);
+
+        // Act & Assert
+        mockMvc.perform(delete("/torrent/delete/{torrentId}", torrentId)
+                        .param("userid", userid.toString()))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.success").value(true))
+                .andExpect(jsonPath("$.message").value("种子删除成功"));
+
+        // 验证服务层方法被调用
+        verify(torrentService, times(1)).deleteTorrent(userid, torrentId);
+    }
+
+    @Test
+    void deleteTorrent_WithoutPermission_ShouldReturnForbidden() throws Exception {
+        // Arrange
+        Long userid = 2L;
+        Long torrentId = 101L;
+
+        // 模拟服务层抛出SecurityException
+        doThrow(new SecurityException("无权删除此种子")).when(torrentService).deleteTorrent(userid, torrentId);
+
+        // Act & Assert
+        mockMvc.perform(delete("/torrent/delete/{torrentId}", torrentId)
+                        .param("userid", userid.toString()))
+                .andExpect(status().isForbidden())
+                .andExpect(jsonPath("$.success").value(false))
+                .andExpect(jsonPath("$.message").value("无权删除此种子"));
+
+        // 验证服务层方法被调用
+        verify(torrentService, times(1)).deleteTorrent(userid, torrentId);
+    }
+
+    @Test
+    void deleteTorrent_WhenServiceThrowsException_ShouldReturnInternalServerError() throws Exception {
+        // Arrange
+        Long userid = 1L;
+        Long torrentId = 101L;
+
+        // 模拟服务层抛出其他异常
+        doThrow(new RuntimeException("数据库错误")).when(torrentService).deleteTorrent(userid, torrentId);
+
+        // Act & Assert
+        mockMvc.perform(delete("/torrent/delete/{torrentId}", torrentId)
+                        .param("userid", userid.toString()))
+                .andExpect(status().isInternalServerError())
+                .andExpect(jsonPath("$.success").value(false))
+                .andExpect(jsonPath("$.message").value("删除失败: 数据库错误"));
+
+        // 验证服务层方法被调用
+        verify(torrentService, times(1)).deleteTorrent(userid, torrentId);
+    }
+
+    @Test
+    void deleteTorrent_WithoutUserIdParameter_ShouldReturnBadRequest() throws Exception {
+        // Arrange
+        Long torrentId = 101L;
+
+        // Act & Assert
+        mockMvc.perform(delete("/torrent/delete/{torrentId}", torrentId))
+                .andExpect(status().isBadRequest());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentDetailControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentDetailControllerTest.java
new file mode 100644
index 0000000..6c0c33e
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentDetailControllerTest.java
@@ -0,0 +1,83 @@
+package com.pt5.pthouduan.ControllerTest;
+
+import com.pt5.pthouduan.controller.TorrentController;
+import com.pt5.pthouduan.entity.Torrent;
+import com.pt5.pthouduan.service.TorrentService;
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+class TorrentDetailControllerTest {
+
+    @Mock
+    private TorrentService torrentService;
+
+    @InjectMocks
+    private TorrentController torrentController;
+
+    private MockMvc mockMvc;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(torrentController).build();
+    }
+
+    @Test
+    void getTorrentById_ExistingId_ShouldReturnTorrent() throws Exception {
+        // Arrange
+        Long id = 1L;
+        Torrent torrent = new Torrent();
+        torrent.setTorrentid(id);
+
+        when(torrentService.getTorrentById(id)).thenReturn(torrent);
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/{id}", id))
+                .andExpect(status().isOk());
+                //.andExpect(jsonPath("$.id").value(id));
+
+        // 验证服务层方法被调用
+        verify(torrentService, times(1)).getTorrentById(id);
+    }
+
+    @Test
+    void getTorrentById_NonExistingId_ShouldReturnNotFound() throws Exception {
+        // Arrange
+        Long id = 999L;
+
+        when(torrentService.getTorrentById(id)).thenReturn(null);
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/{id}", id))
+                .andExpect(status().isNotFound());
+
+        // 验证服务层方法被调用
+        verify(torrentService, times(1)).getTorrentById(id);
+    }
+
+    @Test
+    void getTorrentById_WhenServiceThrowsException_ShouldReturnBadRequest() throws Exception {
+        // Arrange
+        Long id = 1L;
+
+        when(torrentService.getTorrentById(id)).thenThrow(new RuntimeException("Database error"));
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/{id}", id))
+                .andExpect(status().isBadRequest());
+                //.andExpect(jsonPath("$.message").value("获取种子详情失败: Database error"));
+
+        // 验证服务层方法被调用
+        verify(torrentService, times(1)).getTorrentById(id);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentFullRequestControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentFullRequestControllerTest.java
new file mode 100644
index 0000000..c4a5983
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentFullRequestControllerTest.java
@@ -0,0 +1,188 @@
+package com.pt5.pthouduan.ControllerTest;
+
+
+import com.pt5.pthouduan.controller.TorrentController;
+import com.pt5.pthouduan.service.TorrentService;
+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.http.ResponseEntity;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import java.util.Map;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyMap;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+class TorrentFullRequestControllerTest {
+
+    @Mock
+    private TorrentService torrentService;
+
+    @InjectMocks
+    private TorrentController torrentController;
+
+    private MockMvc mockMvc;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(torrentController).build();
+    }
+
+    @Test
+    void uploadTorrentinrequest_WithAllParameters_ShouldReturnSuccess() throws Exception {
+        // Arrange
+        Long userid = 1L;
+        Integer requestId = 101;
+        String title = "Test Torrent";
+        String description = "Test Description";
+        Integer categoryId = 1;
+
+        // 创建模拟文件
+        MockMultipartFile torrentFile = new MockMultipartFile(
+                "torrentFile",
+                "test.torrent",
+                "application/x-bittorrent",
+                "torrent content".getBytes()
+        );
+
+        MockMultipartFile coverImage = new MockMultipartFile(
+                "coverImage",
+                "cover.jpg",
+                "image/jpeg",
+                "image content".getBytes()
+        );
+
+        // 模拟服务层调用成功 - 使用更具体的参数或doReturn语法
+        doReturn(ResponseEntity.ok(Map.of("success", true, "message", "Upload successful")))
+                .when(torrentService).uploadtohelp(
+                        anyInt(),
+                        any(MockMultipartFile.class),
+                        any(MockMultipartFile.class),
+                        anyString(),
+                        anyString(),
+                        anyInt(),
+                        anyLong(),
+                        anyMap()
+                );
+
+        // Act & Assert
+        mockMvc.perform(multipart("/torrent/fullrequest")
+                        .file(torrentFile)
+                        .file(coverImage)
+                        .param("userid", userid.toString())
+                        .param("requestId", requestId.toString())
+                        .param("title", title)
+                        .param("description", description)
+                        .param("categoryId", categoryId.toString())
+                        .param("dpi", "300")
+                        .param("caption", "Test Caption")
+                        .param("region", "Asia")
+                        .param("year", "2023")
+                        .param("genre", "Action")
+                        .param("format", "Blu-ray")
+                        .param("resolution", "1080p")
+                        .param("codecFormat", "H.264")
+                        .param("platform", "PC")
+                        .param("language", "English")
+                        .param("eventType", "Football")
+                        .param("source", "BBC")
+                        .param("style", "Rock")
+                        .param("isMainland", "true")
+                );
+//
+//
+    }
+
+
+    @Test
+    void uploadTorrentinrequest_WhenServiceThrowsException_ShouldReturnBadRequest() throws Exception {
+        // Arrange
+        Long userid = 1L;
+        Integer requestId = 101;
+        String title = "Test Torrent";
+        String description = "Test Description";
+        Integer categoryId = 1;
+
+        // 创建模拟文件
+        MockMultipartFile torrentFile = new MockMultipartFile(
+                "torrentFile",
+                "test.torrent",
+                "application/x-bittorrent",
+                "torrent content".getBytes()
+        );
+
+        MockMultipartFile coverImage = new MockMultipartFile(
+                "coverImage",
+                "cover.jpg",
+                "image/jpeg",
+                "image content".getBytes()
+        );
+
+        // 模拟服务层抛出异常
+        when(torrentService.uploadtohelp(
+                anyInt(),
+                any(MockMultipartFile.class),
+                any(MockMultipartFile.class),
+                anyString(),
+                anyString(),
+                anyInt(),
+                anyLong(),
+                anyMap()
+        )).thenThrow(new RuntimeException("File processing error"));
+
+        // Act & Assert
+        mockMvc.perform(multipart("/torrent/fullrequest")
+                        .file(torrentFile)
+                        .file(coverImage)
+                        .param("userid", userid.toString())
+                        .param("requestId", requestId.toString())
+                        .param("title", title)
+                        .param("description", description)
+                        .param("categoryId", categoryId.toString())
+                )
+                .andExpect(status().isBadRequest());
+                //.andExpect(jsonPath("$.message").value("Upload failed:File processing error"));
+    }
+
+    @Test
+    void uploadTorrentinrequest_WithoutRequiredParameters_ShouldReturnBadRequest() throws Exception {
+        // Arrange
+        // 不提供任何必需参数
+
+        // Act & Assert
+        mockMvc.perform(multipart("/torrent/fullrequest"))
+                .andExpect(status().isBadRequest());
+    }
+
+    @Test
+    void uploadTorrentinrequest_WithoutFileParameters_ShouldReturnBadRequest() throws Exception {
+        // Arrange
+        Long userid = 1L;
+        Integer requestId = 101;
+        String title = "Test Torrent";
+        String description = "Test Description";
+        Integer categoryId = 1;
+
+        // 不提供文件参数
+
+        // Act & Assert
+        mockMvc.perform(multipart("/torrent/fullrequest")
+                        .param("userid", userid.toString())
+                        .param("requestId", requestId.toString())
+                        .param("title", title)
+                        .param("description", description)
+                        .param("categoryId", categoryId.toString())
+                )
+                .andExpect(status().isBadRequest());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentListByCategoryControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentListByCategoryControllerTest.java
new file mode 100644
index 0000000..7cb6521
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentListByCategoryControllerTest.java
@@ -0,0 +1,85 @@
+package com.pt5.pthouduan.ControllerTest;
+
+import com.pt5.pthouduan.controller.TorrentController;
+import com.pt5.pthouduan.entity.Torrent;
+import com.pt5.pthouduan.service.TorrentService;
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+class TorrentListByCategoryControllerTest {
+
+    @Mock
+    private TorrentService torrentService;
+
+    @InjectMocks
+    private TorrentController torrentController;
+
+    private MockMvc mockMvc;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(torrentController).build();
+    }
+
+    @Test
+    void getTorrentsByCategory_WithCategoryId_ShouldReturnFilteredTorrents() throws Exception {
+        // Arrange
+        Integer categoryid = 1;
+        Torrent torrent1 = new Torrent();
+        Torrent torrent2 = new Torrent();
+        List<Torrent> torrents = Arrays.asList(torrent1, torrent2);
+
+        when(torrentService.getTorrentsByCategory(categoryid)).thenReturn(torrents);
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/listByCategory")
+                        .param("categoryid", categoryid.toString()))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$").isArray())
+                .andExpect(jsonPath("$.length()").value(2));
+    }
+
+    @Test
+    void getTorrentsByCategory_WithoutCategoryId_ShouldReturnAllTorrents() throws Exception {
+        // Arrange
+        Torrent torrent1 = new Torrent();
+        Torrent torrent2 = new Torrent();
+        List<Torrent> torrents = Arrays.asList(torrent1, torrent2);
+
+        when(torrentService.getAllTorrents()).thenReturn(torrents);
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/listByCategory"))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$").isArray())
+                .andExpect(jsonPath("$.length()").value(2));
+    }
+
+    @Test
+    void getTorrentsByCategory_WithNonExistingCategory_ShouldReturnEmptyList() throws Exception {
+        // Arrange
+        Integer categoryid = 999;
+        when(torrentService.getTorrentsByCategory(categoryid)).thenReturn(Collections.emptyList());
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/listByCategory")
+                        .param("categoryid", categoryid.toString()))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$").isArray())
+                .andExpect(jsonPath("$.length()").value(0));
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentListByCategoryWithFilterControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentListByCategoryWithFilterControllerTest.java
new file mode 100644
index 0000000..7fbc7fb
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentListByCategoryWithFilterControllerTest.java
@@ -0,0 +1,98 @@
+package com.pt5.pthouduan.ControllerTest;
+
+
+import com.pt5.pthouduan.controller.TorrentController;
+import com.pt5.pthouduan.entity.Torrent;
+import com.pt5.pthouduan.service.TorrentService;
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import java.util.*;
+
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+class TorrentListByCategoryWithFilterControllerTest {
+
+    @Mock
+    private TorrentService torrentService;
+
+    @InjectMocks
+    private TorrentController torrentController;
+
+    private MockMvc mockMvc;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(torrentController).build();
+    }
+
+    @Test
+    void getTorrentsByCategorywithFilter_WithCategoryAndFilters_ShouldReturnFilteredTorrents() throws Exception {
+        // Arrange
+        Integer categoryid = 1;
+        Map<String, String> filters = new HashMap<>();
+        filters.put("key1", "value1");
+        filters.put("key2", "value2");
+
+        Torrent torrent1 = new Torrent();
+        Torrent torrent2 = new Torrent();
+        List<Torrent> torrents = Arrays.asList(torrent1, torrent2);
+
+        when(torrentService.getTorrentsByCategorywithfilters(categoryid, filters)).thenReturn(torrents);
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/listByCategorywithfilter")
+                        .param("categoryid", categoryid.toString())
+                        .param("key1", "value1")
+                        .param("key2", "value2"))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$").isArray())
+                .andExpect(jsonPath("$.length()").value(2));
+    }
+
+    @Test
+    void getTorrentsByCategorywithFilter_WithCategoryButNoFilters_ShouldReturnFilteredTorrents() throws Exception {
+        // Arrange
+        Integer categoryid = 1;
+        Map<String, String> filters = new HashMap<>();
+
+        Torrent torrent1 = new Torrent();
+        Torrent torrent2 = new Torrent();
+        List<Torrent> torrents = Arrays.asList(torrent1, torrent2);
+
+        when(torrentService.getTorrentsByCategorywithfilters(categoryid, filters)).thenReturn(torrents);
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/listByCategorywithfilter")
+                        .param("categoryid", categoryid.toString()))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$").isArray())
+                .andExpect(jsonPath("$.length()").value(2));
+    }
+
+    @Test
+    void getTorrentsByCategorywithFilter_WithNonExistingCategory_ShouldReturnEmptyList() throws Exception {
+        // Arrange
+        Integer categoryid = 999;
+        Map<String, String> filters = new HashMap<>();
+        filters.put("key1", "value1");
+
+        when(torrentService.getTorrentsByCategorywithfilters(categoryid, filters)).thenReturn(Collections.emptyList());
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/listByCategorywithfilter")
+                        .param("categoryid", categoryid.toString())
+                        .param("key1", "value1"))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$").isArray())
+                .andExpect(jsonPath("$.length()").value(0));
+    }
+}
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentListControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentListControllerTest.java
new file mode 100644
index 0000000..582e753
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentListControllerTest.java
@@ -0,0 +1,66 @@
+package com.pt5.pthouduan.ControllerTest;
+
+
+import com.pt5.pthouduan.controller.TorrentController;
+import com.pt5.pthouduan.entity.Torrent;
+import com.pt5.pthouduan.service.TorrentService;
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+class TorrentListControllerTest {
+
+    @Mock
+    private TorrentService torrentService;
+
+    @InjectMocks
+    private TorrentController torrentController;
+
+    private MockMvc mockMvc;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(torrentController).build();
+    }
+
+    @Test
+    void getAllTorrents_ShouldReturnAllTorrents() throws Exception {
+        // Arrange
+        Torrent torrent1 = new Torrent();
+        Torrent torrent2 = new Torrent();
+        List<Torrent> torrents = Arrays.asList(torrent1, torrent2);
+
+        when(torrentService.getAllTorrents()).thenReturn(torrents);
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/list"))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$").isArray())
+                .andExpect(jsonPath("$.length()").value(2));
+    }
+
+    @Test
+    void getAllTorrents_ShouldReturnEmptyListWhenNoTorrents() throws Exception {
+        // Arrange
+        when(torrentService.getAllTorrents()).thenReturn(Collections.emptyList());
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/list"))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$").isArray())
+                .andExpect(jsonPath("$.length()").value(0));
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentSearchControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentSearchControllerTest.java
new file mode 100644
index 0000000..9e3aa2f
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentSearchControllerTest.java
@@ -0,0 +1,91 @@
+package com.pt5.pthouduan.ControllerTest;
+
+
+import com.pt5.pthouduan.controller.TorrentController;
+import com.pt5.pthouduan.entity.Torrent;
+import com.pt5.pthouduan.service.TorrentService;
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+class TorrentSearchControllerTest {
+
+    @Mock
+    private TorrentService torrentService;
+
+    @InjectMocks
+    private TorrentController torrentController;
+
+    private MockMvc mockMvc;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(torrentController).build();
+    }
+
+    @Test
+    void searchTorrents_WithKeyword_ShouldReturnMatchingTorrents() throws Exception {
+        // Arrange
+        String keyword = "test";
+        Torrent torrent1 = new Torrent();
+        Torrent torrent2 = new Torrent();
+        List<Torrent> torrents = Arrays.asList(torrent1, torrent2);
+
+        when(torrentService.searchByKeyword(keyword)).thenReturn(torrents);
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/search")
+                        .param("keyword", keyword))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$").isArray())
+                .andExpect(jsonPath("$.length()").value(2));
+    }
+
+    @Test
+    void searchTorrents_WithEmptyKeyword_ShouldReturnEmptyList() throws Exception {
+        // Arrange
+        String keyword = "";
+        when(torrentService.searchByKeyword(keyword)).thenReturn(Collections.emptyList());
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/search")
+                        .param("keyword", keyword))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$").isArray())
+                .andExpect(jsonPath("$.length()").value(0));
+    }
+
+    @Test
+    void searchTorrents_WithNoMatchingTorrents_ShouldReturnEmptyList() throws Exception {
+        // Arrange
+        String keyword = "nonexistent";
+        when(torrentService.searchByKeyword(keyword)).thenReturn(Collections.emptyList());
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/search")
+                        .param("keyword", keyword))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$").isArray())
+                .andExpect(jsonPath("$.length()").value(0));
+    }
+
+    @Test
+    void searchTorrents_WithoutKeywordParameter_ShouldReturnBadRequest() throws Exception {
+        // Act & Assert
+        mockMvc.perform(get("/torrent/search"))
+                .andExpect(status().isBadRequest());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentSeederControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentSeederControllerTest.java
new file mode 100644
index 0000000..69cbc9a
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentSeederControllerTest.java
@@ -0,0 +1,78 @@
+package com.pt5.pthouduan.ControllerTest;
+
+import com.pt5.pthouduan.controller.TorrentController;
+import com.pt5.pthouduan.entity.PeerInfo;
+import com.pt5.pthouduan.service.TorrentService;
+import com.pt5.pthouduan.service.impl.PeerService;
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+//static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+class TorrentSeederControllerTest {
+
+    @Mock
+    private TorrentService torrentService;
+
+    @Mock
+    private PeerService peerService;
+
+
+    @InjectMocks
+    private TorrentController torrentController;
+
+    private MockMvc mockMvc;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(torrentController).build();
+    }
+
+    @Test
+    void getSeedersByInfoHash_ShouldReturnPeers() throws Exception {
+        // Arrange
+        String infoHash = "ABC123";
+        PeerInfo peer1 = new PeerInfo();
+        PeerInfo peer2 = new PeerInfo();
+        List<PeerInfo> peers = Arrays.asList(peer1, peer2);
+
+        // 假设torrentService有一个方法返回PeerInfo列表
+        // 这里需要根据实际torrentService的API进行调整
+        when(peerService.getSeedersByInfoHash(infoHash)).thenReturn(peers);
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/{infoHash}/seeders", infoHash))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$").isArray())
+                .andExpect(jsonPath("$.length()").value(2));
+    }
+
+    @Test
+    void getSeedersByInfoHash_ShouldReturnEmptyListWhenNoPeers() throws Exception {
+        // Arrange
+        String infoHash = "ABC123";
+
+        // 假设torrentService有一个方法返回空列表
+        when(peerService.getSeedersByInfoHash(infoHash)).thenReturn(Collections.emptyList());
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/{infoHash}/seeders", infoHash))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$").isArray())
+                .andExpect(jsonPath("$.length()").value(0));
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentSetPromotionControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentSetPromotionControllerTest.java
new file mode 100644
index 0000000..3717f20
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentSetPromotionControllerTest.java
@@ -0,0 +1,109 @@
+package com.pt5.pthouduan.ControllerTest;
+
+import com.pt5.pthouduan.controller.TorrentController;
+import com.pt5.pthouduan.service.TorrentService;
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+class TorrentSetPromotionControllerTest {
+
+    @Mock
+    private TorrentService torrentService;
+
+    @InjectMocks
+    private TorrentController torrentController;
+
+    private MockMvc mockMvc;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(torrentController).build();
+    }
+
+    @Test
+    void setPromotion_Success_ShouldReturnSuccess() throws Exception {
+        // Arrange
+        Long userid = 1L;
+        Long torrentId = 101L;
+        Long promotionId = 1L;
+
+        // 模拟服务层调用成功
+        doNothing().when(torrentService).setPromotion(torrentId, promotionId, userid);
+
+        // Act & Assert
+        mockMvc.perform(post("/torrent/setPromotion")
+                        .param("userid", userid.toString())
+                        .param("torrentId", torrentId.toString())
+                        .param("promotionId", promotionId.toString()))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.success").value(true))
+                .andExpect(jsonPath("$.message").value("促销类型设置成功"));
+
+        // 验证服务层方法被调用
+        verify(torrentService, times(1)).setPromotion(torrentId, promotionId, userid);
+    }
+
+    @Test
+    void setPromotion_SecurityException_ShouldReturnForbidden() throws Exception {
+        // Arrange
+        Long userid = 1L;
+        Long torrentId = 101L;
+        Long promotionId = 1L;
+
+        // 模拟服务层抛出SecurityException
+        doThrow(new SecurityException("无权设置促销")).when(torrentService).setPromotion(torrentId, promotionId, userid);
+
+        // Act & Assert
+        mockMvc.perform(post("/torrent/setPromotion")
+                        .param("userid", userid.toString())
+                        .param("torrentId", torrentId.toString())
+                        .param("promotionId", promotionId.toString()))
+                .andExpect(status().isForbidden())
+                .andExpect(jsonPath("$.success").value(false))
+                .andExpect(jsonPath("$.message").value("无权设置促销"));
+
+        // 验证服务层方法被调用
+        verify(torrentService, times(1)).setPromotion(torrentId, promotionId, userid);
+    }
+
+    @Test
+    void setPromotion_OtherException_ShouldReturnInternalServerError() throws Exception {
+        // Arrange
+        Long userid = 1L;
+        Long torrentId = 101L;
+        Long promotionId = 1L;
+
+        // 模拟服务层抛出其他异常
+        doThrow(new RuntimeException("数据库错误")).when(torrentService).setPromotion(torrentId, promotionId, userid);
+
+        // Act & Assert
+        mockMvc.perform(post("/torrent/setPromotion")
+                        .param("userid", userid.toString())
+                        .param("torrentId", torrentId.toString())
+                        .param("promotionId", promotionId.toString()))
+                .andExpect(status().isInternalServerError())
+                .andExpect(jsonPath("$.success").value(false))
+                .andExpect(jsonPath("$.message").value("设置促销类型失败: 数据库错误"));
+
+        // 验证服务层方法被调用
+        verify(torrentService, times(1)).setPromotion(torrentId, promotionId, userid);
+    }
+
+    @Test
+    void setPromotion_WithoutParameters_ShouldReturnBadRequest() throws Exception {
+        // Act & Assert
+        mockMvc.perform(post("/torrent/setPromotion"))
+                .andExpect(status().isBadRequest());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentUsernameControllerTest.java b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentUsernameControllerTest.java
new file mode 100644
index 0000000..631ba29
--- /dev/null
+++ b/src/test/java/com/pt5/pthouduan/ControllerTest/TorrentUsernameControllerTest.java
@@ -0,0 +1,88 @@
+package com.pt5.pthouduan.ControllerTest;
+
+
+import com.pt5.pthouduan.controller.TorrentController;
+import com.pt5.pthouduan.mapper.UserMapper;
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+class TorrentUsernameControllerTest {
+
+    @Mock
+    private UserMapper userMapper;
+
+    @InjectMocks
+    private TorrentController torrentController;
+
+    private MockMvc mockMvc;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        mockMvc = MockMvcBuilders.standaloneSetup(torrentController).build();
+    }
+
+    @Test
+    void getUsernameByUserId_ExistingUser_ShouldReturnUsername() throws Exception {
+        // Arrange
+        Long userId = 1L;
+        String expectedUsername = "testuser";
+
+        when(userMapper.selectUsernameByUserid(userId)).thenReturn(expectedUsername);
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/{userId}/username", userId))
+                .andExpect(status().isOk())
+                .andExpect(content().string(expectedUsername));
+
+        // 验证userMapper方法被调用
+        verify(userMapper, times(1)).selectUsernameByUserid(userId);
+    }
+
+    @Test
+    void getUsernameByUserId_NonExistingUser_ShouldReturnNotFound() throws Exception {
+        // Arrange
+        Long userId = 999L;
+
+        when(userMapper.selectUsernameByUserid(userId)).thenReturn(null);
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/{userId}/username", userId))
+                .andExpect(status().isNotFound());
+
+        // 验证userMapper方法被调用
+        verify(userMapper, times(1)).selectUsernameByUserid(userId);
+    }
+
+    @Test
+    void getUsernameByUserId_WhenDatabaseErrorOccurs_ShouldReturnInternalServerError() throws Exception {
+        // Arrange
+        Long userId = 1L;
+
+        when(userMapper.selectUsernameByUserid(userId)).thenThrow(new RuntimeException("Database connection failed"));
+
+        // Act & Assert
+        mockMvc.perform(get("/torrent/{userId}/username", userId))
+                .andExpect(status().isInternalServerError());
+
+        // 验证userMapper方法被调用
+        verify(userMapper, times(1)).selectUsernameByUserid(userId);
+    }
+
+    @Test
+    void getUsernameByUserId_WithoutUserIdParameter_ShouldReturnBadRequest() throws Exception {
+        // Act & Assert
+        mockMvc.perform(get("/torrent//username")) // 故意不提供userId
+                .andExpect(status().isBadRequest());
+    }
+}
\ No newline at end of file