修改一下tracker的url

Change-Id: I7b7fed5938012ca563c9f83548cdabc4eaa3c8d5
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
index ffda0e7..1a4fc01 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
@@ -4,6 +4,10 @@
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.web.server.ConfigurableWebServerFactory;
+import org.springframework.boot.web.server.ErrorPage;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 
 /**
@@ -37,4 +41,12 @@
 
         System.out.println(blue + " ----我爱雨滔身体好好 喵喵 喵 喵" + reset);
     }
+
+    @Bean
+    public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer() {
+        return factory -> {
+            ErrorPage error404Page = new ErrorPage("/index.html");
+            factory.addErrorPages(error404Page);
+        };
+    }
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/AnnounceService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/AnnounceService.java
index b983acd..37e2135 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/AnnounceService.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/AnnounceService.java
@@ -54,7 +54,7 @@
 
         updateUserInfo(request);
         // 返回peer列表给客户端
-        //TODO 默认值是60,改为动态调整
+
         Integer interval = getAnnounceInterval(request);
         TrackerResponse trackerResponse = TrackerResponse.build(interval, 60, torrent.getSeeders(), torrent.getLeechers(), peerList);
         return trackerResponse.toResultMap();
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentService.java
index b6f1b72..497b1ca 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentService.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/Server/BT/TorrentService.java
@@ -3,6 +3,7 @@
 import cn.hutool.core.bean.BeanUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.dampcake.bencode.Bencode;
 import com.dampcake.bencode.Type;
@@ -16,6 +17,8 @@
 import com.ruoyi.web.dao.BT.TorrentDao;
 import com.ruoyi.web.domain.BT.*;
 import com.ruoyi.web.Server.sys.UserService;
+import com.ruoyi.web.domain.BT.dto.TorrentSearchRequest;
+import com.ruoyi.web.domain.BT.dto.TorrentSearchResult;
 import com.ruoyi.web.domain.sys.UserCredentialEntity;
 import com.ruoyi.web.domain.sys.UserEntity;
 import lombok.RequiredArgsConstructor;
@@ -63,6 +66,7 @@
         entity.setStatus(TorrentEntity.Status.CANDIDATE);
         entity.setFileStatus(0);
         entity.setOwner(userService.getUserId());
+        entity.setId(torrentDao.countId()+1);
         torrentDao.insert(entity);
         return entity.getId();
     }
@@ -124,8 +128,100 @@
         return infoBencode.encode(decodedMap);
     }
 
-    public void audit(TorrentAuditParam param) {
 
+    public Page<TorrentSearchResult> searchTorrents(TorrentSearchRequest request) {
+        // 创建分页对象
+        Page<TorrentEntity> page = new Page<>(request.getPageNum(), request.getPageSize());
+
+        // 构建查询条件
+        QueryWrapper<TorrentEntity> queryWrapper = buildQueryWrapper(request);
+
+        // 执行查询
+        Page<TorrentEntity> torrentPage = torrentDao.selectPage(page, queryWrapper);
+
+        // 转换结果
+        Page<TorrentSearchResult> resultPage = new Page<>(
+                torrentPage.getCurrent(),
+                torrentPage.getSize(),
+                torrentPage.getTotal()
+        );
+
+        for (TorrentEntity torrent : torrentPage.getRecords()) {
+            TorrentSearchResult result = new TorrentSearchResult();
+            result.setTorrent(torrent);
+
+            // 查询并设置用户信息
+            UserEntity owner = getOwnerInfo(torrent.getOwner());
+            result.setOwnerInfo(owner);
+
+            resultPage.getRecords().add(result);
+        }
+
+        return resultPage;
+    }
+
+    private QueryWrapper<TorrentEntity> buildQueryWrapper(TorrentSearchRequest request) {
+        QueryWrapper<TorrentEntity> wrapper = new QueryWrapper<>();
+
+        // 精准搜索条件
+        if (request.getInfoHash() != null && !request.getInfoHash().isEmpty()) {
+            wrapper.eq("info_hash", request.getInfoHash());
+        }
+        if (request.getCategory() != null) {
+            wrapper.eq("category", request.getCategory());
+        }
+        if (request.getStatus() != null) {
+            wrapper.eq("status", request.getStatus());
+        }
+        if (request.getFileStatus() != null) {
+            wrapper.eq("file_status", request.getFileStatus());
+        }
+        if (request.getOwner() != null) {
+            wrapper.eq("owner", request.getOwner());
+        }
+        if (request.getType() != null) {
+            wrapper.eq("type", request.getType());
+        }
+
+        // 模糊搜索条件
+        if (request.getNameKeyword() != null && !request.getNameKeyword().isEmpty()) {
+            wrapper.like("name", request.getNameKeyword());
+        }
+        if (request.getTitleKeyword() != null && !request.getTitleKeyword().isEmpty()) {
+            wrapper.like("title", request.getTitleKeyword());
+        }
+        if (request.getDescriptionKeyword() != null && !request.getDescriptionKeyword().isEmpty()) {
+            wrapper.like("description", request.getDescriptionKeyword());
+        }
+
+        // 范围搜索条件
+        if (request.getMinSize() != null) {
+            wrapper.ge("size", request.getMinSize());
+        }
+        if (request.getMaxSize() != null) {
+            wrapper.le("size", request.getMaxSize());
+        }
+        if (request.getCreateTimeStart() != null) {
+            wrapper.ge("create_time", request.getCreateTimeStart());
+        }
+        if (request.getCreateTimeEnd() != null) {
+            wrapper.le("create_time", request.getCreateTimeEnd());
+        }
+
+        // 排序条件
+        if (request.getSortField() != null && !request.getSortField().isEmpty()) {
+            boolean isAsc = "asc".equalsIgnoreCase(request.getSortDirection());
+            wrapper.orderBy(true, isAsc, request.getSortField());
+        }
+
+        return wrapper;
+    }
+
+    private UserEntity getOwnerInfo(Integer ownerId) {
+        return userService.getUserById(ownerId);
+    }
+
+    public void audit(TorrentAuditParam param) {
 
 
         Integer id = param.getId();
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/BT/TagController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/BT/TagController.java
index 3cedd5d..6e82701 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/BT/TagController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/BT/TagController.java
@@ -10,9 +10,7 @@
 import io.swagger.v3.oas.annotations.tags.Tag;
 import lombok.RequiredArgsConstructor;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
 
@@ -35,6 +33,57 @@
         return Result.ok(tags);
     }
 
+    @Operation(summary = "删除标签", description = "根据ID删除标签")
+    @DeleteMapping("/delete/{id}")
+    public Result deleteTag(@PathVariable Long id) {
+        // 检查标签是否存在
+        TagEntity tag = tagDao.findById(id);
+        if (tag == null) {
+            return Result.error("标签不存在");
+        }
+
+        int rows = tagDao.deleteById(id);
+        if (rows > 0) {
+            return Result.ok();
+        } else {
+            return Result.error("删除标签失败");
+        }
+    }
+
+    @Operation(summary = "更新标签", description = "根据ID更新标签信息")
+    @PutMapping("/update")
+    public Result updateTag(@RequestBody TagEntity tag) {
+        // 检查标签是否存在
+        TagEntity oldTag = tagDao.findById((long)tag.getId());
+        if (oldTag == null) {
+            return Result.error("标签不存在");
+        }
+
+        // 检查新标签名是否已存在(排除自身)
+        boolean exists = tagDao.existsByNameAndNotId(tag.getName(), (long)tag.getId());
+        if (exists) {
+            return Result.error("标签名已被其他标签使用");
+        }
+
+        int rows = tagDao.update(tag);
+        if (rows > 0) {
+            return Result.ok();
+        } else {
+            return Result.error("更新标签失败");
+        }
+    }
+
+    @Operation(summary = "获取单个标签", description = "根据ID获取标签信息")
+    @GetMapping("/{id}")
+    public Result getTag(@PathVariable Long id) {
+        TagEntity tag = tagDao.findById(id);
+        if (tag != null) {
+            return Result.ok(tag);
+        } else {
+            return Result.error("标签不存在");
+        }
+    }
+
     @Operation(summary = "所有分类", description = "返回所有分类,电影之类的")
     @GetMapping("/all_cat")
     public Result allCat() {
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/BT/TorrentController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/BT/TorrentController.java
index 7a84cd5..ed8e869 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/BT/TorrentController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/BT/TorrentController.java
@@ -1,7 +1,9 @@
 package com.ruoyi.web.controller.BT;
 
 import cn.dev33.satoken.annotation.SaIgnore;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.github.pagehelper.PageInfo;
+import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.web.Server.BT.TorrentCommentService;
 import com.ruoyi.web.Server.BT.TorrentService;
 import com.ruoyi.web.Server.BT.TrackerURLService;
@@ -15,6 +17,8 @@
 import com.ruoyi.web.domain.BT.*;
 import com.ruoyi.web.Server.sys.UserService;
 
+import com.ruoyi.web.domain.BT.dto.TorrentSearchRequest;
+import com.ruoyi.web.domain.BT.dto.TorrentSearchResult;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.enums.ParameterIn;
@@ -58,6 +62,17 @@
     private final SuggestDao suggestDao;
     private final UserService userService;
     private final TorrentCommentService torrentCommentService;
+    /**
+     * The purpose of the method:
+     *
+     * 种子搜索
+     * {@code @date} 2025/6/8 13:11
+     */
+    @PostMapping("/search")
+    public Result searchTorrents(@RequestBody TorrentSearchRequest request) {
+        Page<TorrentSearchResult> result = torrentService.searchTorrents(request);
+        return Result.ok(result);
+    }
 
     /**
      * 种子列表查询
@@ -125,7 +140,7 @@
     
     @Operation(summary = "新增种子")
     @PostMapping("/add")
-    public Result add(TorrentAddParam param) {
+    public Result add(@RequestBody TorrentAddParam param) {
         Integer id = torrentService.add(param);
         return Result.ok(id);
     }
@@ -160,7 +175,7 @@
 
     @Operation(summary = "上传文件并生成种子")
     @PostMapping("/upload")
-    public ResponseEntity<byte[]> upload(@RequestPart("file") MultipartFile file,
+    public Result upload(@RequestPart("file") MultipartFile file,
                                          @RequestParam Integer id) {
         try {
             if (file.isEmpty()) {
@@ -205,14 +220,10 @@
             // 2. 使用 RFC 5987 标准的 filename* 语法设置响应头
             String contentDisposition = "attachment; filename*=UTF-8''" + encodedFilename;
 
-            return ResponseEntity.ok()
-                    .contentType(MediaType.parseMediaType("application/x-bittorrent")) // 明确类型
-                    .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition) // 使用 RFC 5987
-                    .body(fileBytes);
+            return Result.ok();
 
         } catch (IOException | RocketPTException e) {
-            return ResponseEntity.status(501)
-                    .body(e.getMessage().getBytes());
+            return Result.error("上传失败,站内种子已存在");
         }
     }
     @Operation(summary = "修改种子")
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/Constants.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/Constants.java
index e3fa91b..4d1511f 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/Constants.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/Constants.java
@@ -50,7 +50,7 @@
 
         String PROTOCOL = "http";
 
-        Integer PORT = 8080;
+        Integer PORT = 5004;
 
     }
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TagDao.java b/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TagDao.java
index 2d3ce55..931f777 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TagDao.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TagDao.java
@@ -3,8 +3,7 @@
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.ruoyi.web.domain.BT.TagCatEntity;
 import com.ruoyi.web.domain.BT.TagEntity;
-import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.*;
 
 import java.util.List;
 
@@ -15,4 +14,33 @@
     List<TagEntity> all_tag();
     @Select("select * from bt_cat")
     List<TagCatEntity> all_cat();
+    @Select("select * from bt_tag where id=#{id}")
+    TagEntity findById(Long id);
+
+    @Select("SELECT COUNT(*) FROM bt_tag WHERE name = #{name}")
+    @ResultType(Integer.class)
+    boolean existsByName(String name);
+
+    @Select("SELECT COUNT(*) FROM bt_tag WHERE name = #{name} AND id != #{id}")
+    @ResultType(Integer.class)
+    boolean existsByNameAndNotId(String name, Long id);
+
+    @Insert("INSERT INTO bt_tag (name, description, create_time, update_time) " +
+            "VALUES (#{name}, #{description}, #{createTime}, #{updateTime})")
+    @Options(useGeneratedKeys = true, keyProperty = "id")
+    int insert(TagEntity tag);
+
+    @Delete("DELETE FROM bt_tag WHERE id = #{id}")
+    int delete(Long id);
+
+    @Update("""
+    UPDATE bt_tag 
+    SET name = #{name}, 
+        remark = #{remark}, 
+        cat = #{cat}, 
+        update_time = #{updateTime} 
+    WHERE id = #{id}
+""")
+    int update(TagEntity tag);
+
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TorrentDao.java b/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TorrentDao.java
index 0ccc066..31f1fff 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TorrentDao.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/dao/BT/TorrentDao.java
@@ -29,6 +29,7 @@
             "    #{visible}, #{anonymous}, #{leechers}, #{seeders}, #{completions}, \n" +
             "    #{remark}\n" +
             ")")
+    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
     int insert(TorrentEntity entity);
 
     @Select("select * from bt_torrent where id=#{id}")
@@ -37,6 +38,8 @@
     @Select("SELECT COUNT(*) FROM bt_torrent WHERE info_hash = #{infoHash}")
     long selectCountByInfoHash(byte[] infoHash);
 
+    @Select("SELECT COUNT(*) FROM bt_torrent")
+    int countId();
 
     default List<TorrentEntity> advancedSearch(AdvancedTorrentParam param) {
         QueryWrapper<TorrentEntity> wrapper = new QueryWrapper<>();
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/domain/BT/dto/TorrentSearchRequest.java b/ruoyi-admin/src/main/java/com/ruoyi/web/domain/BT/dto/TorrentSearchRequest.java
new file mode 100644
index 0000000..040841f
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/domain/BT/dto/TorrentSearchRequest.java
@@ -0,0 +1,35 @@
+package com.ruoyi.web.domain.BT.dto;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class TorrentSearchRequest {
+    // 精准搜索字段
+    private String infoHash;      // 种子哈希
+    private Integer category;     // 类别
+    private Integer status;       // 状态
+    private Integer fileStatus;   // 文件状态
+    private Integer owner;        // 拥有者
+    private Integer type;         // 类型
+
+    // 模糊搜索字段
+    private String nameKeyword;   // 名称关键词
+    private String titleKeyword;  // 标题关键词
+    private String descriptionKeyword; // 描述关键词
+
+    // 范围搜索字段
+    private Long minSize;         // 最小文件大小
+    private Long maxSize;         // 最大文件大小
+    private LocalDateTime createTimeStart; // 创建时间开始
+    private LocalDateTime createTimeEnd;   // 创建时间结束
+
+    // 排序字段
+    private String sortField = "createTime"; // 排序字段,默认按创建时间
+    private String sortDirection = "desc";   // 排序方向,默认降序
+
+    // 分页信息
+    private Integer pageNum = 1;    // 当前页码
+    private Integer pageSize = 20;  // 每页数量
+}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/domain/BT/dto/TorrentSearchResult.java b/ruoyi-admin/src/main/java/com/ruoyi/web/domain/BT/dto/TorrentSearchResult.java
new file mode 100644
index 0000000..b443ed9
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/domain/BT/dto/TorrentSearchResult.java
@@ -0,0 +1,12 @@
+package com.ruoyi.web.domain.BT.dto;
+
+
+import com.ruoyi.web.domain.BT.TorrentEntity;
+import com.ruoyi.web.domain.sys.UserEntity;
+import lombok.Data;
+
+@Data
+public class TorrentSearchResult {
+    private TorrentEntity torrent;
+    private UserEntity ownerInfo;
+}
\ No newline at end of file