作弊&促销
Change-Id: Ibdee947370e11c3a98912569e1a290b5e0968fbe
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 64d19fc..f58e1e2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -7,13 +7,12 @@
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
- <option name="workspaceImportForciblyTurnedOn" value="true" />
</component>
<component name="PWA">
<option name="enabled" value="true" />
<option name="wasEnabledAtLeastOnce" value="true" />
</component>
- <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 5e39899..d3c9b17 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,73 +5,14 @@
</component>
<component name="ChangeListManager">
<list default="true" id="f008fe30-0711-42e2-bb33-17dcfdbad387" name="Changes" comment="pom">
- <change beforePath="$PROJECT_DIR$/.idea/dataSources.local.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources.local.xml" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/.idea/dataSources/6d2a7063-ae4c-471f-ae05-e32dede5d0ec.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources/6d2a7063-ae4c-471f-ae05-e32dede5d0ec.xml" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/pom.xml" beforeDir="false" afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/config/WebConfig.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/config/WebConfig.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/controller/DynamicController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/controller/DynamicController.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/controller/TorrentController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/controller/TorrentController.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/controller/UserController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/controller/UserController.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/entity/User.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/entity/User.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/entity/Users.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/entity/Users.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/DynamicService.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/DynamicService.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/PostService.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/PostService.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/TorrentService.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/TorrentService.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/UserService.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/UserService.java" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/controller/PromotionController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/controller/PromotionController.java" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/mapper/PromotionMapper.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/mapper/PromotionMapper.java" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/serviceImpl/PromotionServiceImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/serviceImpl/PromotionServiceImpl.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/serviceImpl/TorrentServiceImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/serviceImpl/TorrentServiceImpl.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/test/java/com/example/myproject/controller/DynamicControllerTest.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/java/com/example/myproject/controller/DynamicControllerTest.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/common/base/PageParam.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/common/base/PageParam.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/common/base/ResPage.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/common/base/ResPage.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/common/base/Result.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/common/base/Result.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/config/TrackerConfig.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/config/TrackerConfig.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/config/WebConfig.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/config/WebConfig.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/DynamicController.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/DynamicController.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/GroupController.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/GroupController.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/TorrentController.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/TorrentController.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/TrackerController.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/TrackerController.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/UserController.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/UserController.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/dto/PromotionCreateDTO.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/dto/PromotionCreateDTO.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/dto/TorrentUpdateDTO.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/dto/TorrentUpdateDTO.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/dto/param/AnnounceRequest.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/dto/param/AnnounceRequest.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/dto/param/TorrentParam.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/dto/param/TorrentParam.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/dto/param/TorrentUploadParam.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/dto/param/TorrentUploadParam.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/dto/vo/TorrentVO.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/dto/vo/TorrentVO.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/entity/EntityBase.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/entity/EntityBase.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/entity/FavoriteEntity.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/entity/FavoriteEntity.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/entity/Promotion.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/entity/Promotion.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/entity/TorrentEntity.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/entity/TorrentEntity.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/entity/User.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/entity/User.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/entity/VerificationToken.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/entity/VerificationToken.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/service/CommentService.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/service/CommentService.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/service/DynamicService.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/service/DynamicService.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/service/GroupService.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/service/GroupService.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/service/LevelService.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/service/LevelService.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/service/PostService.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/service/PostService.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/service/TorrentService.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/service/TorrentService.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/service/UserMessageService.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/service/UserMessageService.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/service/UserService.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/service/UserService.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/service/serviceImpl/EmailServiceImpl.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/service/serviceImpl/EmailServiceImpl.class" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/mapper/PromotionMapper.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/mapper/PromotionMapper.class" afterDir="false" />
<change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/service/serviceImpl/PromotionServiceImpl.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/service/serviceImpl/PromotionServiceImpl.class" afterDir="false" />
<change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/service/serviceImpl/TorrentServiceImpl.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/service/serviceImpl/TorrentServiceImpl.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/utils/Result.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/utils/Result.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/utils/VerifyCode.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/utils/VerifyCode.class" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/files/files.torrent" beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/target/classes/output/valid.torrent" beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/target/test-classes/com/example/myproject/controller/CommentControllerTest.class" beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/target/test-classes/com/example/myproject/controller/DynamicControllerTest.class" beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/target/test-classes/com/example/myproject/controller/GroupControllerTest.class" beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/target/test-classes/com/example/myproject/controller/LevelControllerTest.class" beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/target/test-classes/com/example/myproject/controller/PostControllerTest.class" beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/target/test-classes/com/example/myproject/controller/TaskControllerTest.class" beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/target/test-classes/com/example/myproject/controller/UserControllerTest.class" beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/target/test-classes/com/example/myproject/controller/UserFollowControllerTest.class" beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/target/test-classes/com/example/myproject/controller/UserMessageControllerTest.class" beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/uploads/avatarUrl/1.jpg" beforeDir="false" afterPath="$PROJECT_DIR$/uploads/avatarUrl/1.jpg" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/uploads/avatarUrl/2.jpg" beforeDir="false" afterPath="$PROJECT_DIR$/uploads/avatarUrl/2.jpg" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/uploads/avatarUrl/3.jpg" beforeDir="false" afterPath="$PROJECT_DIR$/uploads/avatarUrl/3.jpg" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/uploads/avatarUrl/4.jpg" beforeDir="false" afterPath="$PROJECT_DIR$/uploads/avatarUrl/4.jpg" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -110,38 +51,38 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
- <component name="PropertiesComponent"><![CDATA[{
- "keyToString": {
- "ASKED_ADD_EXTERNAL_FILES": "true",
- "JUnit.UserControllerTest.executor": "Run",
- "JUnit.UserControllerTest.testUpdateUserAvatar.executor": "Run",
- "JUnit.UserControllerTest.testUploadUserAvatar.executor": "Run",
- "RequestMappingsPanelOrder0": "0",
- "RequestMappingsPanelOrder1": "1",
- "RequestMappingsPanelWidth0": "75",
- "RequestMappingsPanelWidth1": "75",
- "RunOnceActivity.OpenProjectViewOnStart": "true",
- "RunOnceActivity.ShowReadmeOnStart": "true",
- "RunOnceActivity.git.unshallow": "true",
- "Spring Boot.MyProjectApplication.executor": "Run",
- "git-widget-placeholder": "完成pt与bt交互、补充图片资源",
- "ignore.virus.scanning.warn.message": "true",
- "kotlin-language-version-configured": "true",
- "last_opened_file_path": "D:/Desktop/echo后端/echo-backend/images",
- "node.js.detected.package.eslint": "true",
- "node.js.selected.package.eslint": "(autodetect)",
- "node.js.selected.package.tslint": "(autodetect)",
- "nodejs_package_manager_path": "npm",
- "settings.editor.selected.configurable": "project.propVCSSupport.DirectoryMappings",
- "vue.rearranger.settings.migration": "true",
- "应用程序.MyProjectApplication.executor": "Run"
+ <component name="PropertiesComponent">{
+ "keyToString": {
+ "ASKED_ADD_EXTERNAL_FILES": "true",
+ "JUnit.UserControllerTest.executor": "Run",
+ "JUnit.UserControllerTest.testUpdateUserAvatar.executor": "Run",
+ "JUnit.UserControllerTest.testUploadUserAvatar.executor": "Run",
+ "RequestMappingsPanelOrder0": "0",
+ "RequestMappingsPanelOrder1": "1",
+ "RequestMappingsPanelWidth0": "75",
+ "RequestMappingsPanelWidth1": "75",
+ "RunOnceActivity.OpenProjectViewOnStart": "true",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "RunOnceActivity.git.unshallow": "true",
+ "Spring Boot.MyProjectApplication.executor": "Run",
+ "git-widget-placeholder": "完成pt与bt交互、补充图片资源",
+ "ignore.virus.scanning.warn.message": "true",
+ "kotlin-language-version-configured": "true",
+ "last_opened_file_path": "D:/Desktop/echo后端/echo-backend/images",
+ "node.js.detected.package.eslint": "true",
+ "node.js.selected.package.eslint": "(autodetect)",
+ "node.js.selected.package.tslint": "(autodetect)",
+ "nodejs_package_manager_path": "npm",
+ "settings.editor.selected.configurable": "project.propVCSSupport.DirectoryMappings",
+ "vue.rearranger.settings.migration": "true",
+ "应用程序.MyProjectApplication.executor": "Run"
},
- "keyToStringList": {
- "DatabaseDriversLRU": [
- "mysql_aurora"
+ "keyToStringList": {
+ "DatabaseDriversLRU": [
+ "mysql_aurora"
]
}
-}]]></component>
+}</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="D:\Desktop\echo后端\echo-backend\images" />
diff --git a/src/main/java/com/example/myproject/MyProjectApplication.java b/src/main/java/com/example/myproject/MyProjectApplication.java
index 49cd5f0..417fa6d 100644
--- a/src/main/java/com/example/myproject/MyProjectApplication.java
+++ b/src/main/java/com/example/myproject/MyProjectApplication.java
@@ -4,8 +4,11 @@
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
+@EnableScheduling
+
@MapperScan("com.example.myproject.mapper") // 扫描 Mapper 接口
public class MyProjectApplication {
public static void main(String[] args) {
diff --git a/src/main/java/com/example/myproject/controller/PromotionController.java b/src/main/java/com/example/myproject/controller/PromotionController.java
new file mode 100644
index 0000000..ab6efe7
--- /dev/null
+++ b/src/main/java/com/example/myproject/controller/PromotionController.java
@@ -0,0 +1,141 @@
+package com.example.myproject.controller;
+
+import cn.dev33.satoken.annotation.SaCheckLogin;
+import cn.dev33.satoken.stp.StpUtil;
+import com.example.myproject.common.base.Result;
+import com.example.myproject.dto.PromotionCreateDTO;
+import com.example.myproject.dto.TorrentUpdateDTO;
+import com.example.myproject.entity.Promotion;
+import com.example.myproject.service.PromotionService;
+import com.example.myproject.service.TorrentService;
+import com.example.myproject.service.UserService;
+import com.example.myproject.repository.UserRepository;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+@RestController
+@RequestMapping("/seeds")
+@Slf4j
+@RequiredArgsConstructor
+public class PromotionController {
+ @Autowired
+ private TorrentService torrentService;
+ @Autowired
+ private UserRepository userRepository;
+
+
+ @Autowired
+ private PromotionService promotionService;
+
+ @Autowired
+ private UserService userService;
+ @SaCheckLogin
+ @Operation(summary = "删除种子")
+ @DeleteMapping("/{torrentId}")
+ public Result deleteTorrent(@PathVariable Long torrentId) {
+ try {
+ // 验证用户权限
+ Long userId = StpUtil.getLoginIdAsLong();
+ if (!torrentService.canUserDeleteTorrent(torrentId, userId)) {
+ return Result.error("没有权限删除此种子");
+ }
+
+ torrentService.deleteTorrent(torrentId);
+ return Result.ok();
+ } catch (Exception e) {
+ return Result.error("删除失败: " + e.getMessage());
+ }
+ }
+
+ @SaCheckLogin
+ @Operation(summary = "修改种子信息")
+ @PutMapping("/{torrentId}")
+ public Result updateTorrent(
+ @PathVariable Long torrentId,
+ @RequestBody @Validated TorrentUpdateDTO updateDTO) {
+ try {
+ // 验证用户权限
+ Long userId = StpUtil.getLoginIdAsLong();
+ if (!torrentService.canUserUpdateTorrent(torrentId, userId)) {
+ return Result.error("没有权限修改此种子");
+ }
+
+ torrentService.updateTorrent(torrentId, updateDTO);
+ return Result.ok();
+ } catch (Exception e) {
+ return Result.error("更新失败: " + e.getMessage());
+ }
+ }
+
+ @SaCheckLogin
+ @Operation(summary = "创建促销活动")
+ @PostMapping("/promotions")
+ public Result createPromotion(@RequestBody @Validated PromotionCreateDTO promotionDTO) {
+ try {
+ // 验证用户权限(只有管理员可以创建促销)
+// if (!StpUtil.hasRole("admin")) {
+// return Result.error("没有权限创建促销活动");
+// }
+//
+ Promotion promotion = promotionService.createPromotion(promotionDTO);
+ return Result.ok(promotion);
+ } catch (Exception e) {
+ return Result.error("创建促销失败: " + e.getMessage());
+ }
+ }
+
+ @SaCheckLogin
+ @Operation(summary = "获取促销活动列表")
+ @GetMapping("/promotions")
+ public Result getPromotions() {
+ try {
+ List<Promotion> promotions = promotionService.getAllActivePromotions();
+ return Result.ok(promotions);
+ } catch (Exception e) {
+ return Result.error("获取促销列表失败: " + e.getMessage());
+ }
+ }
+
+ @SaCheckLogin
+ @Operation(summary = "获取促销详情")
+ @GetMapping("/promotions/{promotionId}")
+ public Result getPromotionDetails(@PathVariable Long promotionId) {
+ try {
+ Promotion promotion = promotionService.getPromotionById(promotionId);
+ if (promotion == null) {
+ return Result.error("促销活动不存在");
+ }
+ return Result.ok(promotion);
+ } catch (Exception e) {
+ return Result.error("获取促销详情失败: " + e.getMessage());
+ }
+ }
+
+ @SaCheckLogin
+ @Operation(summary = "删除促销活动")
+ @DeleteMapping("/promotions/{promotionId}")
+ public Result deletePromotion(@PathVariable Long promotionId) {
+ try {
+ // 验证用户权限(只有管理员可以删除促销)
+// if (!StpUtil.hasRole("admin")) {
+// return Result.error("没有权限删除促销活动");
+// }
+ Long userId = StpUtil.getLoginIdAsLong();
+ // 查询用户
+ var userOpt = userRepository.findById(userId);
+ if (userOpt.isEmpty() || !"admin".equals(userOpt.get().getRole())) {
+ return Result.error("没有权限删除促销活动");
+ }
+
+ promotionService.deletePromotion(promotionId);
+ return Result.ok();
+ } catch (Exception e) {
+ return Result.error("删除促销失败: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/example/myproject/controller/TorrentController.java b/src/main/java/com/example/myproject/controller/TorrentController.java
index aecfc64..846e5da 100644
--- a/src/main/java/com/example/myproject/controller/TorrentController.java
+++ b/src/main/java/com/example/myproject/controller/TorrentController.java
@@ -325,6 +325,7 @@
import com.example.myproject.dto.TrackerProtocol;
import com.example.myproject.entity.TorrentEntity;
import com.example.myproject.entity.TorrentReport;
+import com.example.myproject.mapper.UserMapper;
import com.example.myproject.repository.TorrentReportRepository;
import com.example.myproject.service.TorrentService;
import com.example.myproject.service.PromotionService;
@@ -335,6 +336,7 @@
import com.example.myproject.dto.TorrentUpdateDTO;
import com.example.myproject.dto.PromotionCreateDTO;
import com.example.myproject.entity.Promotion;
+
import com.example.myproject.service.UserService;
import com.turn.ttorrent.bcodec.BEValue;
@@ -378,6 +380,9 @@
@Autowired
private PromotionService promotionService;
+ @Autowired
+ private UserMapper userMapper;
+
@Autowired
private UserService userService;
@@ -529,107 +534,23 @@
return Result.error("失败: ");
}
}
-
- @SaCheckLogin
- @Operation(summary = "删除种子")
- @DeleteMapping("/{torrentId}")
- public Result deleteTorrent(@PathVariable Long torrentId) {
+ /**
+ * 查看个人收藏
+ */
+ @GetMapping("/my-favorite")
+ public Result getMyFavorite(
+ @RequestParam("user_id") Long userId) {
try {
- // 验证用户权限
- Long userId = StpUtil.getLoginIdAsLong();
- if (!torrentService.canUserDeleteTorrent(torrentId, userId)) {
- return Result.error("没有权限删除此种子");
- }
-
- torrentService.deleteTorrent(torrentId);
- return Result.ok();
+ return torrentService.getMyfavorite(userId);
} catch (Exception e) {
- return Result.error("删除失败: " + e.getMessage());
+ return Result.error("失败: ");
}
}
- @SaCheckLogin
- @Operation(summary = "修改种子信息")
- @PutMapping("/{torrentId}")
- public Result updateTorrent(
- @PathVariable Long torrentId,
- @RequestBody @Validated TorrentUpdateDTO updateDTO) {
- try {
- // 验证用户权限
- Long userId = StpUtil.getLoginIdAsLong();
- if (!torrentService.canUserUpdateTorrent(torrentId, userId)) {
- return Result.error("没有权限修改此种子");
- }
- torrentService.updateTorrent(torrentId, updateDTO);
- return Result.ok();
- } catch (Exception e) {
- return Result.error("更新失败: " + e.getMessage());
- }
- }
-
- @SaCheckLogin
- @Operation(summary = "创建促销活动")
- @PostMapping("/promotions")
- public Result createPromotion(@RequestBody @Validated PromotionCreateDTO promotionDTO) {
- try {
- // 验证用户权限(只有管理员可以创建促销)
-// if (!StpUtil.hasRole("admin")) {
-// return Result.error("没有权限创建促销活动");
-// }
-//
- Promotion promotion = promotionService.createPromotion(promotionDTO);
- return Result.ok(promotion);
- } catch (Exception e) {
- return Result.error("创建促销失败: " + e.getMessage());
- }
- }
-
- @SaCheckLogin
- @Operation(summary = "获取促销活动列表")
- @GetMapping("/promotions")
- public Result getPromotions() {
- try {
- List<Promotion> promotions = promotionService.getAllActivePromotions();
- return Result.ok(promotions);
- } catch (Exception e) {
- return Result.error("获取促销列表失败: " + e.getMessage());
- }
- }
-
- @SaCheckLogin
- @Operation(summary = "获取促销详情")
- @GetMapping("/promotions/{promotionId}")
- public Result getPromotionDetails(@PathVariable Long promotionId) {
- try {
- Promotion promotion = promotionService.getPromotionById(promotionId);
- if (promotion == null) {
- return Result.error("促销活动不存在");
- }
- return Result.ok(promotion);
- } catch (Exception e) {
- return Result.error("获取促销详情失败: " + e.getMessage());
- }
- }
-
- @SaCheckLogin
- @Operation(summary = "删除促销活动")
- @DeleteMapping("/promotions/{promotionId}")
- public Result deletePromotion(@PathVariable Long promotionId) {
- try {
- // 验证用户权限(只有管理员可以删除促销)
- if (!StpUtil.hasRole("admin")) {
- return Result.error("没有权限删除促销活动");
- }
-
- promotionService.deletePromotion(promotionId);
- return Result.ok();
- } catch (Exception e) {
- return Result.error("删除促销失败: " + e.getMessage());
- }
- }
@GetMapping("/announce")
public ResponseEntity<byte[]> announce(TrackerProtocol trackerProtocol, HttpServletRequest request, @RequestParam(value = "info_hash") String encodedInfoHash){
+
HashMap<String, BEValue> map = new HashMap<>();
if (StrUtil.isBlank(trackerProtocol.getIp())) {
trackerProtocol.setIp(request.getRemoteAddr());
@@ -641,13 +562,25 @@
trackerProtocol.setInfo_hash(infoHash);
TorrentEntity torrent = torrentService.selectByInfoHash(infoHash);
+ Long torrentId = torrent.getId();
+ String peerId = trackerProtocol.getPeer_id();
+
if (torrent == null) {
throw new RuntimeException("种子不存在");
}
Integer userId = trackerProtocol.getUserId();
+ double uploaded = trackerProtocol.getUploaded();
+ double downloaded = trackerProtocol.getDownloaded();
+ /**
+ *更新种子上传量,下载量, 更新用户上传量下载量
+ * 为了总量检测作弊,种子的上传量和下载量是真实的
+ * 用户的上传下载量是“促销”后的
+ * 作弊检测——下载速度检测
+ */
+ torrentService.processUploadDownload(Long.valueOf(userId),peerId, infoHash,torrentId,uploaded,downloaded);
TorrentReport report = TorrentReport.builder()
- .userId(userId )
+ .userId(userId)
.torrentId(torrent.getId())
.peerId(trackerProtocol.getPeer_id())
.infoHash(infoHash)
diff --git a/src/main/java/com/example/myproject/entity/AuditRecord.java b/src/main/java/com/example/myproject/entity/AuditRecord.java
new file mode 100644
index 0000000..ed8b9de
--- /dev/null
+++ b/src/main/java/com/example/myproject/entity/AuditRecord.java
@@ -0,0 +1,20 @@
+package com.example.myproject.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("audit_record")
+@ApiModel("种子实体类")
+public class AuditRecord {
+ private Integer id;
+ private Long torrentId;
+ private double upload;
+ private double download;
+ private LocalDateTime createTime;
+ public AuditRecord() {
+ }
+}
diff --git a/src/main/java/com/example/myproject/entity/BannedUser.java b/src/main/java/com/example/myproject/entity/BannedUser.java
new file mode 100644
index 0000000..a9aa2da
--- /dev/null
+++ b/src/main/java/com/example/myproject/entity/BannedUser.java
@@ -0,0 +1,27 @@
+package com.example.myproject.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+@Data
+@TableName("ba")
+public class BannedUser {
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ @ApiModelProperty(value = "用户ID", example = "1001")
+ @JsonProperty("userId")
+ @TableField("user_id")
+ private Long userId;
+
+ private String reason;
+ @JsonProperty("createTime")
+ @TableField("create_time")
+ private Date createTime;
+}
diff --git a/src/main/java/com/example/myproject/entity/SuspiciousUser.java b/src/main/java/com/example/myproject/entity/SuspiciousUser.java
new file mode 100644
index 0000000..8b15f08
--- /dev/null
+++ b/src/main/java/com/example/myproject/entity/SuspiciousUser.java
@@ -0,0 +1,35 @@
+package com.example.myproject.entity;
+
+
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@TableName("suspicious_user")
+@ApiModel("列入怀疑用户")
+public class SuspiciousUser {
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ @ApiModelProperty(value = "用户ID", example = "1001")
+ @JsonProperty("userId")
+ @TableField("user_id")
+ private Long userId;
+ @ApiModelProperty(value = "上传速度", example = "1001")
+ @TableField("peed_kbs")
+ private double speedKBs;
+
+ private String reason;
+ @JsonProperty("createTime")
+ @TableField("create_time")
+ private Date createTime;
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/entity/TorrentEntity.java b/src/main/java/com/example/myproject/entity/TorrentEntity.java
index f62fb76..b2b8192 100644
--- a/src/main/java/com/example/myproject/entity/TorrentEntity.java
+++ b/src/main/java/com/example/myproject/entity/TorrentEntity.java
@@ -93,6 +93,12 @@
@JsonProperty("promotionUntil")
@ApiModelProperty(value = "促销截止日期", example = "2024-12-31T23:59:59")
private LocalDateTime promotionUntil;
+ @JsonProperty("upload_count")
+ @ApiModelProperty(value = "种子上传量", example = "false")
+ private double uploadCount;
+ @JsonProperty("down_count")
+ @ApiModelProperty(value = "种子下载量", example = "false")
+ private double downloadCount;
@JsonProperty("torrentFile")
@@ -103,6 +109,7 @@
@ApiModelProperty(value = "是否删除", example = "false")
private Boolean isDeleted;
+
public TorrentEntity() {
}
}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/entity/TorrentReport.java b/src/main/java/com/example/myproject/entity/TorrentReport.java
index a019f9e..0d6de00 100644
--- a/src/main/java/com/example/myproject/entity/TorrentReport.java
+++ b/src/main/java/com/example/myproject/entity/TorrentReport.java
@@ -1,5 +1,7 @@
package com.example.myproject.entity;
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Builder;
import lombok.Data;
@@ -24,5 +26,12 @@
private String event;
private String ip;
private Integer port;
+
private LocalDateTime reportTime;
+ @PrePersist
+ public void prePersist() {
+ this.reportTime = LocalDateTime.now();
+ }
+
+
}
diff --git a/src/main/java/com/example/myproject/mapper/AuditRecordMapper.java b/src/main/java/com/example/myproject/mapper/AuditRecordMapper.java
new file mode 100644
index 0000000..2aae334
--- /dev/null
+++ b/src/main/java/com/example/myproject/mapper/AuditRecordMapper.java
@@ -0,0 +1,13 @@
+package com.example.myproject.mapper;
+
+import com.example.myproject.entity.AuditRecord;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface AuditRecordMapper {
+
+ @Insert("INSERT INTO audit_record (torrent_id, upload, download, create_time) " +
+ "VALUES (#{torrentId}, #{upload}, #{download}, #{createTime})")
+ void insert(AuditRecord record);
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/mapper/BannedUserMapper.java b/src/main/java/com/example/myproject/mapper/BannedUserMapper.java
new file mode 100644
index 0000000..9b376e6
--- /dev/null
+++ b/src/main/java/com/example/myproject/mapper/BannedUserMapper.java
@@ -0,0 +1,15 @@
+package com.example.myproject.mapper;
+
+import com.example.myproject.entity.AuditRecord;
+import com.example.myproject.entity.BannedUser;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+
+
+@Mapper
+public interface BannedUserMapper {
+
+ @Insert("INSERT INTO banned_user (user_id, reason, create_time) " +
+ "VALUES (#{userId}, #{reason}, #{createTime})")
+ void insert(BannedUser bannedUser);
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/mapper/PromotionMapper.java b/src/main/java/com/example/myproject/mapper/PromotionMapper.java
index a478835..caf96ee 100644
--- a/src/main/java/com/example/myproject/mapper/PromotionMapper.java
+++ b/src/main/java/com/example/myproject/mapper/PromotionMapper.java
@@ -24,7 +24,8 @@
@Select("SELECT p.* FROM promotion p " +
"WHERE p.is_deleted = false " +
"AND p.start_time <= #{now} AND p.end_time >= #{now} " +
- "AND FIND_IN_SET(#{torrentId}, p.applicable_torrent_ids) > 0")
+// "AND FIND_IN_SET(#{torrentId}, p.applicable_torrent_ids) > 0"
+ "AND JSON_CONTAINS(p.applicable_torrent_ids, CAST(#{torrentId} AS JSON))")
List<Promotion> findActivePromotionsForTorrent(
@Param("torrentId") Long torrentId,
@Param("now") LocalDateTime now);
diff --git a/src/main/java/com/example/myproject/mapper/SuspiciousUserMapper.java b/src/main/java/com/example/myproject/mapper/SuspiciousUserMapper.java
new file mode 100644
index 0000000..533c597
--- /dev/null
+++ b/src/main/java/com/example/myproject/mapper/SuspiciousUserMapper.java
@@ -0,0 +1,12 @@
+package com.example.myproject.mapper;
+
+import com.example.myproject.entity.SuspiciousUser;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SuspiciousUserMapper {
+ @Insert("INSERT INTO suspicious_user (user_id, reason, speed_kbs, create_time) " +
+ "VALUES (#{userId}, #{reason}, #{speedKBs}, #{createTime})")
+ void insert(SuspiciousUser user);
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/mapper/TorrentMapper.java b/src/main/java/com/example/myproject/mapper/TorrentMapper.java
index f9cdcdc..5e72bf2 100644
--- a/src/main/java/com/example/myproject/mapper/TorrentMapper.java
+++ b/src/main/java/com/example/myproject/mapper/TorrentMapper.java
@@ -15,18 +15,20 @@
@Select("SELECT * FROM torrents WHERE info_hash = #{infoHash}")
TorrentEntity selectByInfoHash(String infoHash);
-
- @Select("SELECT * FROM torrents WHERE seed_id = #{seedId}")
- TorrentEntity selectBySeedId(Long seedId);
-
+
List<TorrentEntity> search(@Param("param") TorrentParam param);
-
- @Update("UPDATE torrent SET downloads = downloads + 1 WHERE id = #{torrentId}")
- void increaseDownloads(@Param("torrentId") Long torrentId);
-
+
boolean checkFavorite(@Param("seedId") Long seedId, @Param("userId") Long userId);
void addFavorite(@Param("seedId") Long seedId, @Param("userId") Long userId);
void removeFavorite(@Param("seedId") Long seedId, @Param("userId") Long userId);
+ List<TorrentEntity> selectMyFavorite(@Param("userId") Long userId);
+ @Select("SELECT * FROM torrent")
+ List<TorrentEntity> findAllTorrents();
+ @Update("UPDATE torrent SET upload_count = upload_count + #{uploaded}, download_count = download_count + #{downloaded} WHERE id = #{torrentId}")
+ void updateTorrentUploadDownload(@Param("torrentId") Long torrentId,
+ @Param("uploaded") double uploaded,
+ @Param("downloaded") double downloaded);
+
}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/mapper/UserMapper.java b/src/main/java/com/example/myproject/mapper/UserMapper.java
index 1752801..4a10a38 100644
--- a/src/main/java/com/example/myproject/mapper/UserMapper.java
+++ b/src/main/java/com/example/myproject/mapper/UserMapper.java
@@ -1,4 +1,5 @@
package com.example.myproject.mapper;
+import com.example.myproject.entity.TorrentEntity;
import com.example.myproject.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@@ -30,6 +31,14 @@
@Select("SELECT * FROM user WHERE user_id = #{userId}")
User selectByUserId(@Param("userId") Long userId);
+ @Update("UPDATE user SET upload_count = upload_count + #{bonusUploaded}, download_count = download_count + #{bonusDownloaded} WHERE user_id = #{userId}")
+ int updateUserUploadDownload(@Param("userId") Long userId,
+ @Param("bonusUploaded") double bonusUploaded,
+ @Param("bonusDownloaded") double bonusDownloaded);
+
+
+
+
}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/repository/TorrentReportRepository.java b/src/main/java/com/example/myproject/repository/TorrentReportRepository.java
index 9484d20..7b3e89c 100644
--- a/src/main/java/com/example/myproject/repository/TorrentReportRepository.java
+++ b/src/main/java/com/example/myproject/repository/TorrentReportRepository.java
@@ -2,6 +2,12 @@
import com.example.myproject.entity.TorrentReport;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.Optional;
public interface TorrentReportRepository extends JpaRepository<TorrentReport, Integer> {
+ @Query(value = "SELECT * FROM TorrentReport WHERE peerId = :peerId AND infoHash = :infoHash ORDER BY id DESC LIMIT 1", nativeQuery = true)
+ Optional<TorrentReport> findLatestByPeerIdAndInfoHash(@Param("peerId") String peerId, @Param("infoHash") String infoHash);
}
diff --git a/src/main/java/com/example/myproject/scheduler/TorrentAuditTask.java b/src/main/java/com/example/myproject/scheduler/TorrentAuditTask.java
new file mode 100644
index 0000000..bd985ad
--- /dev/null
+++ b/src/main/java/com/example/myproject/scheduler/TorrentAuditTask.java
@@ -0,0 +1,49 @@
+package com.example.myproject.scheduler;
+
+
+
+import com.example.myproject.entity.TorrentEntity;
+import com.example.myproject.mapper.TorrentMapper;
+import com.example.myproject.service.AuditService;
+import com.example.myproject.service.TorrentService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 作弊检测——总量核验
+ */
+@Slf4j
+@Component
+public class TorrentAuditTask {
+
+ private static final long THRESHOLD = 50 * 1024 * 1024; // 50MB
+
+ private final TorrentMapper torrentMapper;
+ private final AuditService auditService;
+
+ public TorrentAuditTask(TorrentMapper torrentMapper, AuditService auditService) {
+ this.torrentMapper = torrentMapper;
+ this.auditService = auditService;
+ }
+
+ @Scheduled(fixedRate = 3600000) // 每小时执行一次
+ public void checkTorrentUploadDownloadConsistency() {
+ List<TorrentEntity> torrents = torrentMapper.findAllTorrents();
+ for (TorrentEntity torrent : torrents) {
+ double upload = torrent.getUploadCount();
+ double download = torrent.getDownloadCount();
+ double delta = Math.abs(upload - download);
+
+ if (delta > THRESHOLD) {
+ log.warn("【总量核验】种子 {} 上传量={} 下载量={} 差值={},已触发人工审核",
+ torrent.getInfoHash(), upload, download, delta);
+
+ auditService.flagTorrentForReview(torrent.getId(), upload, download);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/service/AuditService.java b/src/main/java/com/example/myproject/service/AuditService.java
new file mode 100644
index 0000000..22d7124
--- /dev/null
+++ b/src/main/java/com/example/myproject/service/AuditService.java
@@ -0,0 +1,46 @@
+package com.example.myproject.service;
+
+import com.example.myproject.entity.AuditRecord;
+import com.example.myproject.entity.BannedUser;
+import com.example.myproject.entity.SuspiciousUser;
+import com.example.myproject.mapper.AuditRecordMapper;
+import com.example.myproject.mapper.BannedUserMapper;
+import com.example.myproject.mapper.SuspiciousUserMapper;
+import org.checkerframework.checker.units.qual.A;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+@Service
+public class AuditService {
+ @Autowired
+ AuditRecordMapper auditRecordMapper;
+ @Autowired
+ SuspiciousUserMapper suspiciousUserMapper;
+ @Autowired
+ BannedUserMapper bannedUserMapper;
+ public void flagTorrentForReview(Long torrentId, double upload, double download) {
+ AuditRecord record = new AuditRecord();
+ record.setTorrentId(torrentId);
+ record.setUpload(upload);
+ record.setDownload(download);
+ record.setCreateTime(LocalDateTime.now());
+
+ auditRecordMapper.insert(record);
+ }
+ public void addSuspiciousUser(Long userId, String reason, double speedKBs) {
+ SuspiciousUser user = new SuspiciousUser();
+ user.setUserId(userId);
+ user.setReason(reason);
+ user.setSpeedKBs(speedKBs);
+ suspiciousUserMapper.insert(user);
+ }
+
+
+ public void banUser(Long userId, String reason) {
+ BannedUser bannedUser = new BannedUser();
+ bannedUser.setUserId(userId);
+ bannedUser.setReason(reason);
+ bannedUserMapper.insert(bannedUser);
+ }
+}
diff --git a/src/main/java/com/example/myproject/service/PromotionService.java b/src/main/java/com/example/myproject/service/PromotionService.java
index 087c13b..b83bce5 100644
--- a/src/main/java/com/example/myproject/service/PromotionService.java
+++ b/src/main/java/com/example/myproject/service/PromotionService.java
@@ -13,5 +13,9 @@
void deletePromotion(Long promotionId);
- double getCurrentDiscount(Long torrentId);
+// double getCurrentDiscount(Long torrentId);
+ double getUploadBonus(Long torrentId);
+ double getDownloadDiscount(Long torrentId);
+
+
}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/service/TorrentService.java b/src/main/java/com/example/myproject/service/TorrentService.java
index 36891d2..e0a4f41 100644
--- a/src/main/java/com/example/myproject/service/TorrentService.java
+++ b/src/main/java/com/example/myproject/service/TorrentService.java
@@ -57,7 +57,9 @@
byte[] fetch(Long seedId, String passkey) throws IOException;
Result favorite(Long seedId, Long userId);
-
+ Result getMyfavorite(Long userId);
+
+
void deleteTorrent(Long seedId);
void updateTorrent(Long seedId, TorrentUpdateDTO updateDTO);
@@ -68,9 +70,12 @@
boolean checkUserUploadRatio(Long userId);
- double calculateDownloadSize(Long torrentId, Long userId);
+// double calculateDownloadSize(Long torrentId, Long userId);
- void recordDownload(Long torrentId, Long userId, double downloadSize);
+
TorrentEntity selectByInfoHash(String infoHash);
+
+ void processUploadDownload(Long userId, String peerId, String infoHash, Long torrentId, double uploaded, double downloaded);
+
}
diff --git a/src/main/java/com/example/myproject/service/serviceImpl/PromotionServiceImpl.java b/src/main/java/com/example/myproject/service/serviceImpl/PromotionServiceImpl.java
index 9d34cbc..1cac3a4 100644
--- a/src/main/java/com/example/myproject/service/serviceImpl/PromotionServiceImpl.java
+++ b/src/main/java/com/example/myproject/service/serviceImpl/PromotionServiceImpl.java
@@ -1,10 +1,14 @@
package com.example.myproject.service.serviceImpl;
import com.example.myproject.entity.Promotion;
+import com.example.myproject.entity.TorrentEntity;
import com.example.myproject.mapper.PromotionMapper;
import com.example.myproject.service.PromotionService;
import com.example.myproject.dto.PromotionCreateDTO;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.swagger.models.auth.In;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -40,10 +44,18 @@
promotion.setDiscountPercentage(promotionDTO.getDiscountPercentage());
// 把List<Long>转换成逗号分隔字符串
- String applicableTorrentIdsStr = promotionDTO.getApplicableTorrentIds().stream()
- .map(String::valueOf)
- .collect(Collectors.joining(","));
- promotion.setApplicableTorrentIds(applicableTorrentIdsStr);
+// String applicableTorrentIdsStr = promotionDTO.getApplicableTorrentIds().stream()
+// .map(String::valueOf)
+// .collect(Collectors.joining(","));
+// promotion.setApplicableTorrentIds(applicableTorrentIdsStr);
+ // 把List<Long>转换成JSON字符串
+ ObjectMapper objectMapper = new ObjectMapper();
+ try {
+ String applicableTorrentIdsJson = objectMapper.writeValueAsString(promotionDTO.getApplicableTorrentIds());
+ promotion.setApplicableTorrentIds(applicableTorrentIdsJson);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException("适用种子ID序列化失败", e);
+ }
promotion.setCreateTime(now);
promotion.setUpdateTime(now);
@@ -82,17 +94,17 @@
promotionMapper.updateById(promotion);
}
- @Override
- public double getCurrentDiscount(Long torrentId) {
- LocalDateTime now = LocalDateTime.now();
- List<Promotion> activePromotions = promotionMapper.findActivePromotionsForTorrent(torrentId, now);
-
- // 如果有多个促销活动,取折扣最大的
- return activePromotions.stream()
- .mapToDouble(Promotion::getDiscountPercentage)
- .max()
- .orElse(0.0);
- }
+// @Override
+// public double getCurrentDiscount(Long torrentId) {
+// LocalDateTime now = LocalDateTime.now();
+// List<Promotion> activePromotions = promotionMapper.findActivePromotionsForTorrent(torrentId, now);
+//
+// // 如果有多个促销活动,取折扣最大的
+// return activePromotions.stream()
+// .mapToDouble(Promotion::getDiscountPercentage)
+// .max()
+// .orElse(0.0);
+// }
/**
* 验证种子ID是否存在
@@ -111,4 +123,48 @@
throw new RuntimeException("以下种子ID不存在: " + invalidIds);
}
}
+ @Override
+ public double getUploadBonus(Long torrentId) {
+ double uploadBonusPercentage = getUploadBonusPercentage(torrentId); // 例如 26.4
+ return 1.0 + (uploadBonusPercentage / 100.0); // 转换为倍数,例如 1.264
+ }
+
+ @Override
+ public double getDownloadDiscount(Long torrentId) {
+ double downloadDiscountPercentage = getDownloadDiscountPercentage(torrentId); // 例如 -20.0
+ return 1.0 + (downloadDiscountPercentage / 100.0); // -20.0 → 0.8
+ }
+
+
+ /**
+ * 获取当前种子适用的上传加成百分比(正数),若无返回 0.0
+ */
+ private double getUploadBonusPercentage(Long torrentId) {
+ LocalDateTime now = LocalDateTime.now();
+ List<Promotion> activePromotions = promotionMapper.findActivePromotionsForTorrent(torrentId, now);
+
+ // 取最大的正值加成百分比
+ return activePromotions.stream()
+ .mapToDouble(Promotion::getDiscountPercentage)
+ .filter(p -> p > 0)
+ .max()
+ .orElse(0.0);
+ }
+
+ /**
+ * 获取当前种子适用的下载折扣百分比(负数),若无返回 0.0
+ */
+ private double getDownloadDiscountPercentage(Long torrentId) {
+ LocalDateTime now = LocalDateTime.now();
+ List<Promotion> activePromotions = promotionMapper.findActivePromotionsForTorrent(torrentId, now);
+
+ // 取最小的负值折扣百分比(更大的折扣)
+ return activePromotions.stream()
+ .mapToDouble(Promotion::getDiscountPercentage)
+ .filter(p -> p < 0)
+ .min()
+ .orElse(0.0);
+ }
+
+
}
diff --git a/src/main/java/com/example/myproject/service/serviceImpl/TorrentServiceImpl.java b/src/main/java/com/example/myproject/service/serviceImpl/TorrentServiceImpl.java
index 4870fd5..210db93 100644
--- a/src/main/java/com/example/myproject/service/serviceImpl/TorrentServiceImpl.java
+++ b/src/main/java/com/example/myproject/service/serviceImpl/TorrentServiceImpl.java
@@ -296,14 +296,19 @@
// }
package com.example.myproject.service.serviceImpl;
+import java.time.Duration;
import cn.dev33.satoken.stp.StpUtil;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.example.myproject.entity.SuspiciousUser;
import com.example.myproject.entity.TorrentEntity;
+import com.example.myproject.entity.TorrentReport;
import com.example.myproject.entity.User;
+import com.example.myproject.mapper.BannedUserMapper;
+import com.example.myproject.mapper.SuspiciousUserMapper;
import com.example.myproject.mapper.TorrentMapper;
import com.example.myproject.mapper.UserMapper;
+import com.example.myproject.repository.TorrentReportRepository;
+import com.example.myproject.service.AuditService;
import com.example.myproject.service.TorrentService;
import com.example.myproject.service.PromotionService;
import com.example.myproject.dto.param.TorrentParam;
@@ -315,13 +320,14 @@
import com.turn.ttorrent.client.SimpleClient;
import com.turn.ttorrent.common.TorrentMetadata;
import com.turn.ttorrent.common.TorrentParser;
-import com.turn.ttorrent.common.creation.MetadataBuilder;
import com.turn.ttorrent.tracker.Tracker;
import com.turn.ttorrent.tracker.TrackedTorrent;
import com.example.myproject.common.base.Result;
+import lombok.extern.slf4j.Slf4j;
-import com.turn.ttorrent.tracker.TrackedTorrent;
+
import org.apache.commons.codec.binary.Hex;
+import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -331,19 +337,18 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetAddress;
-import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.time.LocalTime;
+import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+@Slf4j
@Service
public class TorrentServiceImpl implements TorrentService {
@Autowired
@@ -352,6 +357,9 @@
@Autowired
private TorrentMapper torrentMapper;
+ @Autowired
+ private TorrentReportRepository torrentReportRepository;
+
private final Map<String, TrackedTorrent> torrentRegistry = new HashMap<>();
@@ -360,6 +368,12 @@
@Autowired
private PromotionService promotionService;
+ @Autowired
+ SuspiciousUserMapper suspiciousUserMapper;
+ @Autowired
+ BannedUserMapper bannedUserMapper;
+ @Autowired
+ AuditService auditService;
private static final double MIN_UPLOAD_RATIO = 0.5; // 最小上传比例要求
@@ -372,6 +386,7 @@
public TorrentEntity selectBySeedId(Long seedId) {
return torrentMapper.selectById(seedId);
}
+
private final ExecutorService seederExecutor = Executors.newCachedThreadPool();
@Override
@@ -389,7 +404,7 @@
Long size = metadata.getFiles().stream().map(f -> f.size).reduce(0L, Long::sum);
// 保存种子信息
- TorrentEntity entity= new TorrentEntity();
+ TorrentEntity entity = new TorrentEntity();
entity.setUploader(param.getUploader());
entity.setFileName(file.getOriginalFilename());
entity.setSize(size);
@@ -404,7 +419,6 @@
}
-
@Override
public byte[] fetch(Long seedId, String passkey) {
TorrentEntity torrent = selectBySeedId(seedId);
@@ -422,6 +436,7 @@
// TODO: 对应本机应用地址
// String announce = "http://127.0.0.1:5011/seeds/announce?passkey="+passkey +"&userId=" + StpUtil.getLoginIdAsString();
String announce = "http://192.168.5.149:5011/seeds/announce?passkey=" + passkey + "&userId=" + StpUtil.getLoginIdAsString();
+// String announce = "http://172.23.80.1:5011/seeds/announce?passkey=" + passkey + "&userId=" + 1;
decoded.put("announce", new BEValue(announce));
// 4. 编码成新的 .torrent 文件字节数组
@@ -452,6 +467,12 @@
}
@Override
+ public Result getMyfavorite(Long userId) {
+ List<TorrentEntity> favorites = torrentMapper.selectMyFavorite(userId);
+ return Result.ok(favorites);
+ }
+
+ @Override
@Transactional
public void deleteTorrent(Long seedId) {
torrentMapper.deleteById(seedId);
@@ -507,58 +528,78 @@
double uploadRatio = user.getUploaded() / (double) user.getDownloaded();
return uploadRatio >= MIN_UPLOAD_RATIO;
}
- /**
- * 启动做种客户端
- */
- private void startSeeding(Path torrentPath, Path dataDir) throws Exception {
- SimpleClient seederClient = new SimpleClient();
- seederClient.downloadTorrent(
- torrentPath.toString(),
- dataDir.toString(),
- InetAddress.getLocalHost());
- // 保持做种状态(阻塞线程)
- while (true) {
- Thread.sleep(60000); // 每60秒检查一次
- }
- }
@Override
- public double calculateDownloadSize(Long torrentId, Long userId) {
- TorrentEntity torrent = selectBySeedId(torrentId);
- if (torrent == null) {
- throw new RuntimeException("种子不存在");
- }
-
- // 获取当前有效的促销活动
- double discount = promotionService.getCurrentDiscount(torrentId);
-
- // 计算实际下载量
- return torrent.getSize() * (1 - discount / 100.0);
- }
-
- @Override
- @Transactional
- public void recordDownload(Long torrentId, Long userId, double downloadSize) {
- // 更新用户下载量
- userMapper.increaseDownloaded(userId, downloadSize);
-
- // 更新种子下载次数
- torrentMapper.increaseDownloads(torrentId);
- }
-
- @Override
public TorrentEntity selectByInfoHash(String infoHash) {
return torrentMapper.selectByInfoHash(infoHash);
}
- /**
- * 计算种子文件的infoHash
- */
- private String calculateInfoHash(byte[] torrentData) throws NoSuchAlgorithmException {
- MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
- sha1.update(torrentData);
- byte[] hashBytes = sha1.digest();
- return Hex.encodeHexString(hashBytes);
+ @Override
+ public void processUploadDownload(Long userId, String peerId, String infoHash, Long torrentId, double uploaded, double downloaded) {
+ // 查找该peerId和infoHash的最近一条TorrentReport
+ log.info("开始处理上传和下载");
+ Optional<TorrentReport> lastReportOpt = torrentReportRepository.findLatestByPeerIdAndInfoHash(peerId, infoHash);
+
+
+ double lastUploaded = 0;
+ double lastDownloaded = 0;
+ LocalDateTime lastTime = null;
+
+ if (lastReportOpt.isPresent()) {
+ lastUploaded = lastReportOpt.get().getUploaded();
+ lastDownloaded = lastReportOpt.get().getDownloaded();
+ lastTime = lastReportOpt.get().getReportTime();
+ }
+
+ double uploadedOffset = uploaded - lastUploaded;
+ double downloadedOffset = downloaded - lastDownloaded;
+
+ // 防止客户端重启或作弊导致负数,重置为0
+ if (uploadedOffset < 0) uploadedOffset = 0;
+ if (downloadedOffset < 0) downloadedOffset = 0;
+
+ /**
+ * 作弊——速度检测
+ */
+ if (lastTime != null) {
+ long seconds = Duration.between(lastTime, LocalDateTime.now()).getSeconds();
+ if (seconds > 0) {
+ double speedKBs = uploadedOffset / 1024.0 / seconds; // KB/s
+
+ double MAX_SPEED_KB = 10 * 1024; // 10 MB/s
+ double SUSPICIOUS_SPEED_KB = 2 * 1024; // 2 MB/s
+ int MIN_EXPECTED_LEECHERS = 5;
+
+ //int leechers = torrentReportRepository.countActiveLeechers(infoHash);
+
+ if (speedKBs > MAX_SPEED_KB) {
+ log.warn("用户 {} 上传速度 {} KB/s 超过限制,执行封禁处理", userId, speedKBs);
+ auditService.banUser(userId, "上传速度超过最大限速");
+ return; // 不再处理其他操作
+ }
+// else if (speedKBs > SUSPICIOUS_SPEED_KB && leechers < MIN_EXPECTED_LEECHERS) {
+// log.warn("用户 {} 上传速度异常 {} KB/s,下载人数 {},加入怀疑名单", userId, speedKBs, leechers);
+// abnormalUserMapper.insertSuspicious(userId, speedKBs, leechers); // 你也需要实现
+// }
+ else if (speedKBs > SUSPICIOUS_SPEED_KB) {
+ log.warn("用户 {} 上传速度异常 {} KB/s, 加入怀疑名单", userId, speedKBs);
+ auditService.addSuspiciousUser(userId, "上传速度异常:" ,speedKBs );
+ }
+ }
+
+ // 获取促销加成和下载折扣
+ double uploadBonus = promotionService.getUploadBonus(torrentId); // 比如 1.2
+ log.info("上传优惠比例" + uploadBonus);
+ double downloadDiscount = promotionService.getDownloadDiscount(torrentId); // 比如 0.8
+
+ double bonusUploaded = uploadedOffset * uploadBonus;
+ double bonusDownloaded = downloadedOffset * downloadDiscount;
+
+ // 调用Mapper更新数据库
+ userMapper.updateUserUploadDownload(userId, bonusUploaded, bonusDownloaded);
+ torrentMapper.updateTorrentUploadDownload(torrentId, uploadedOffset, downloadedOffset);
+
+ }
}
}
\ No newline at end of file
diff --git a/src/main/resources/mapper/AuditRecordMapper.xml b/src/main/resources/mapper/AuditRecordMapper.xml
new file mode 100644
index 0000000..aa6b018
--- /dev/null
+++ b/src/main/resources/mapper/AuditRecordMapper.xml
@@ -0,0 +1,6 @@
+<?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.example.myproject.mapper.AuditRecordMapper">
+
+</mapper>
diff --git a/src/main/resources/mapper/BannedUserMapper.xml b/src/main/resources/mapper/BannedUserMapper.xml
new file mode 100644
index 0000000..fbdb038
--- /dev/null
+++ b/src/main/resources/mapper/BannedUserMapper.xml
@@ -0,0 +1,6 @@
+<?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.example.myproject.mapper.BannedUserMapper">
+
+</mapper>
diff --git a/src/main/resources/mapper/SuspiciousUserMapper.xml b/src/main/resources/mapper/SuspiciousUserMapper.xml
new file mode 100644
index 0000000..27851b2
--- /dev/null
+++ b/src/main/resources/mapper/SuspiciousUserMapper.xml
@@ -0,0 +1,6 @@
+<?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.example.myproject.mapper.SuspiciousUserMapper">
+
+</mapper>
diff --git a/src/main/resources/mapper/TorrentMapper.xml b/src/main/resources/mapper/TorrentMapper.xml
index d5f018e..8e5419b 100644
--- a/src/main/resources/mapper/TorrentMapper.xml
+++ b/src/main/resources/mapper/TorrentMapper.xml
@@ -99,6 +99,11 @@
DELETE FROM favorite
WHERE seed_id = #{seedId} AND user_id = #{userId}
</delete>
-
+ <select id="selectMyFavorite" resultType="com.example.myproject.entity.TorrentEntity">
+ SELECT t.*
+ FROM torrent t
+ JOIN favorite f ON t.id = f.seed_id
+ WHERE f.user_id = #{userId}
+ </select>
</mapper>
\ No newline at end of file
diff --git a/target/classes/com/example/myproject/MyProjectApplication.class b/target/classes/com/example/myproject/MyProjectApplication.class
index 1ebd349..93fb1c1 100644
--- a/target/classes/com/example/myproject/MyProjectApplication.class
+++ b/target/classes/com/example/myproject/MyProjectApplication.class
Binary files differ
diff --git a/target/classes/com/example/myproject/config/TrackerConfig.class b/target/classes/com/example/myproject/config/TrackerConfig.class
index 0c32d4e..774aa27 100644
--- a/target/classes/com/example/myproject/config/TrackerConfig.class
+++ b/target/classes/com/example/myproject/config/TrackerConfig.class
Binary files differ
diff --git a/target/classes/com/example/myproject/controller/PromotionController.class b/target/classes/com/example/myproject/controller/PromotionController.class
new file mode 100644
index 0000000..75a63cc
--- /dev/null
+++ b/target/classes/com/example/myproject/controller/PromotionController.class
Binary files differ
diff --git a/target/classes/com/example/myproject/controller/TorrentController.class b/target/classes/com/example/myproject/controller/TorrentController.class
index e4ce75e..654356b 100644
--- a/target/classes/com/example/myproject/controller/TorrentController.class
+++ b/target/classes/com/example/myproject/controller/TorrentController.class
Binary files differ
diff --git a/target/classes/com/example/myproject/controller/TrackerController.class b/target/classes/com/example/myproject/controller/TrackerController.class
index bbf5851..d1690b5 100644
--- a/target/classes/com/example/myproject/controller/TrackerController.class
+++ b/target/classes/com/example/myproject/controller/TrackerController.class
Binary files differ
diff --git a/target/classes/com/example/myproject/dto/TrackerProtocol.class b/target/classes/com/example/myproject/dto/TrackerProtocol.class
index 90e9fb4..af14ea5 100644
--- a/target/classes/com/example/myproject/dto/TrackerProtocol.class
+++ b/target/classes/com/example/myproject/dto/TrackerProtocol.class
Binary files differ
diff --git a/target/classes/com/example/myproject/entity/AuditRecord.class b/target/classes/com/example/myproject/entity/AuditRecord.class
new file mode 100644
index 0000000..3699c9d
--- /dev/null
+++ b/target/classes/com/example/myproject/entity/AuditRecord.class
Binary files differ
diff --git a/target/classes/com/example/myproject/entity/BannedUser.class b/target/classes/com/example/myproject/entity/BannedUser.class
new file mode 100644
index 0000000..8ca9203
--- /dev/null
+++ b/target/classes/com/example/myproject/entity/BannedUser.class
Binary files differ
diff --git a/target/classes/com/example/myproject/entity/SuspiciousUser.class b/target/classes/com/example/myproject/entity/SuspiciousUser.class
new file mode 100644
index 0000000..3407b75
--- /dev/null
+++ b/target/classes/com/example/myproject/entity/SuspiciousUser.class
Binary files differ
diff --git a/target/classes/com/example/myproject/entity/TorrentEntity.class b/target/classes/com/example/myproject/entity/TorrentEntity.class
index a1326b6..44b924b 100644
--- a/target/classes/com/example/myproject/entity/TorrentEntity.class
+++ b/target/classes/com/example/myproject/entity/TorrentEntity.class
Binary files differ
diff --git a/target/classes/com/example/myproject/entity/TorrentReport$TorrentReportBuilder.class b/target/classes/com/example/myproject/entity/TorrentReport$TorrentReportBuilder.class
index fc8919a..17b87a3 100644
--- a/target/classes/com/example/myproject/entity/TorrentReport$TorrentReportBuilder.class
+++ b/target/classes/com/example/myproject/entity/TorrentReport$TorrentReportBuilder.class
Binary files differ
diff --git a/target/classes/com/example/myproject/entity/TorrentReport.class b/target/classes/com/example/myproject/entity/TorrentReport.class
index 20c882e..260b34e 100644
--- a/target/classes/com/example/myproject/entity/TorrentReport.class
+++ b/target/classes/com/example/myproject/entity/TorrentReport.class
Binary files differ
diff --git a/target/classes/com/example/myproject/entity/User.class b/target/classes/com/example/myproject/entity/User.class
index d465c51..d3d50a4 100644
--- a/target/classes/com/example/myproject/entity/User.class
+++ b/target/classes/com/example/myproject/entity/User.class
Binary files differ
diff --git a/target/classes/com/example/myproject/mapper/AuditRecordMapper.class b/target/classes/com/example/myproject/mapper/AuditRecordMapper.class
new file mode 100644
index 0000000..231ffe6
--- /dev/null
+++ b/target/classes/com/example/myproject/mapper/AuditRecordMapper.class
Binary files differ
diff --git a/target/classes/com/example/myproject/mapper/BannedUserMapper.class b/target/classes/com/example/myproject/mapper/BannedUserMapper.class
new file mode 100644
index 0000000..da69e74
--- /dev/null
+++ b/target/classes/com/example/myproject/mapper/BannedUserMapper.class
Binary files differ
diff --git a/target/classes/com/example/myproject/mapper/PromotionMapper.class b/target/classes/com/example/myproject/mapper/PromotionMapper.class
index 074c856..909cbb6 100644
--- a/target/classes/com/example/myproject/mapper/PromotionMapper.class
+++ b/target/classes/com/example/myproject/mapper/PromotionMapper.class
Binary files differ
diff --git a/target/classes/com/example/myproject/mapper/SuspiciousUserMapper.class b/target/classes/com/example/myproject/mapper/SuspiciousUserMapper.class
new file mode 100644
index 0000000..ddc85da
--- /dev/null
+++ b/target/classes/com/example/myproject/mapper/SuspiciousUserMapper.class
Binary files differ
diff --git a/target/classes/com/example/myproject/mapper/TorrentMapper.class b/target/classes/com/example/myproject/mapper/TorrentMapper.class
index 5080dfd..56fb640 100644
--- a/target/classes/com/example/myproject/mapper/TorrentMapper.class
+++ b/target/classes/com/example/myproject/mapper/TorrentMapper.class
Binary files differ
diff --git a/target/classes/com/example/myproject/mapper/UserMapper.class b/target/classes/com/example/myproject/mapper/UserMapper.class
index 74cbed4..d9c95c8 100644
--- a/target/classes/com/example/myproject/mapper/UserMapper.class
+++ b/target/classes/com/example/myproject/mapper/UserMapper.class
Binary files differ
diff --git a/target/classes/com/example/myproject/repository/TorrentReportRepository.class b/target/classes/com/example/myproject/repository/TorrentReportRepository.class
index 77fc905..8abadf5 100644
--- a/target/classes/com/example/myproject/repository/TorrentReportRepository.class
+++ b/target/classes/com/example/myproject/repository/TorrentReportRepository.class
Binary files differ
diff --git a/target/classes/com/example/myproject/scheduler/TorrentAuditTask.class b/target/classes/com/example/myproject/scheduler/TorrentAuditTask.class
new file mode 100644
index 0000000..88e2a9d
--- /dev/null
+++ b/target/classes/com/example/myproject/scheduler/TorrentAuditTask.class
Binary files differ
diff --git a/target/classes/com/example/myproject/service/AuditService.class b/target/classes/com/example/myproject/service/AuditService.class
new file mode 100644
index 0000000..29cf4b1
--- /dev/null
+++ b/target/classes/com/example/myproject/service/AuditService.class
Binary files differ
diff --git a/target/classes/com/example/myproject/service/DynamicService.class b/target/classes/com/example/myproject/service/DynamicService.class
index 5170c7b..18039bf 100644
--- a/target/classes/com/example/myproject/service/DynamicService.class
+++ b/target/classes/com/example/myproject/service/DynamicService.class
Binary files differ
diff --git a/target/classes/com/example/myproject/service/PostService.class b/target/classes/com/example/myproject/service/PostService.class
index 20f263c..706f72d 100644
--- a/target/classes/com/example/myproject/service/PostService.class
+++ b/target/classes/com/example/myproject/service/PostService.class
Binary files differ
diff --git a/target/classes/com/example/myproject/service/PromotionService.class b/target/classes/com/example/myproject/service/PromotionService.class
index 6f0963b..f5d48f0 100644
--- a/target/classes/com/example/myproject/service/PromotionService.class
+++ b/target/classes/com/example/myproject/service/PromotionService.class
Binary files differ
diff --git a/target/classes/com/example/myproject/service/TorrentService.class b/target/classes/com/example/myproject/service/TorrentService.class
index bac5d31..9fdf389 100644
--- a/target/classes/com/example/myproject/service/TorrentService.class
+++ b/target/classes/com/example/myproject/service/TorrentService.class
Binary files differ
diff --git a/target/classes/com/example/myproject/service/UserService.class b/target/classes/com/example/myproject/service/UserService.class
index 3f02c8e..de99a39 100644
--- a/target/classes/com/example/myproject/service/UserService.class
+++ b/target/classes/com/example/myproject/service/UserService.class
Binary files differ
diff --git a/target/classes/com/example/myproject/service/serviceImpl/PromotionServiceImpl.class b/target/classes/com/example/myproject/service/serviceImpl/PromotionServiceImpl.class
index bee3056..83cf11d 100644
--- a/target/classes/com/example/myproject/service/serviceImpl/PromotionServiceImpl.class
+++ b/target/classes/com/example/myproject/service/serviceImpl/PromotionServiceImpl.class
Binary files differ
diff --git a/target/classes/com/example/myproject/service/serviceImpl/TorrentServiceImpl.class b/target/classes/com/example/myproject/service/serviceImpl/TorrentServiceImpl.class
index 2341ad1..63669c9 100644
--- a/target/classes/com/example/myproject/service/serviceImpl/TorrentServiceImpl.class
+++ b/target/classes/com/example/myproject/service/serviceImpl/TorrentServiceImpl.class
Binary files differ
diff --git a/target/classes/mapper/AuditRecordMapper.xml b/target/classes/mapper/AuditRecordMapper.xml
new file mode 100644
index 0000000..aa6b018
--- /dev/null
+++ b/target/classes/mapper/AuditRecordMapper.xml
@@ -0,0 +1,6 @@
+<?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.example.myproject.mapper.AuditRecordMapper">
+
+</mapper>
diff --git a/target/classes/mapper/BannedUserMapper.xml b/target/classes/mapper/BannedUserMapper.xml
new file mode 100644
index 0000000..fbdb038
--- /dev/null
+++ b/target/classes/mapper/BannedUserMapper.xml
@@ -0,0 +1,6 @@
+<?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.example.myproject.mapper.BannedUserMapper">
+
+</mapper>
diff --git a/target/classes/mapper/SuspiciousUserMapper.xml b/target/classes/mapper/SuspiciousUserMapper.xml
new file mode 100644
index 0000000..27851b2
--- /dev/null
+++ b/target/classes/mapper/SuspiciousUserMapper.xml
@@ -0,0 +1,6 @@
+<?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.example.myproject.mapper.SuspiciousUserMapper">
+
+</mapper>
diff --git a/target/classes/mapper/TorrentMapper.xml b/target/classes/mapper/TorrentMapper.xml
index d5f018e..8e5419b 100644
--- a/target/classes/mapper/TorrentMapper.xml
+++ b/target/classes/mapper/TorrentMapper.xml
@@ -99,6 +99,11 @@
DELETE FROM favorite
WHERE seed_id = #{seedId} AND user_id = #{userId}
</delete>
-
+ <select id="selectMyFavorite" resultType="com.example.myproject.entity.TorrentEntity">
+ SELECT t.*
+ FROM torrent t
+ JOIN favorite f ON t.id = f.seed_id
+ WHERE f.user_id = #{userId}
+ </select>
</mapper>
\ No newline at end of file
diff --git a/uploads/torrents/1749218819331.jpg b/uploads/torrents/1749218819331.jpg
new file mode 100644
index 0000000..4ba04de
--- /dev/null
+++ b/uploads/torrents/1749218819331.jpg
Binary files differ
diff --git a/uploads/torrents/1749219849324.jpg b/uploads/torrents/1749219849324.jpg
new file mode 100644
index 0000000..4ba04de
--- /dev/null
+++ b/uploads/torrents/1749219849324.jpg
Binary files differ
diff --git a/uploads/torrents/1749219884508.jpg b/uploads/torrents/1749219884508.jpg
new file mode 100644
index 0000000..4ba04de
--- /dev/null
+++ b/uploads/torrents/1749219884508.jpg
Binary files differ
diff --git a/uploads/torrents/1749220294853.jpg b/uploads/torrents/1749220294853.jpg
new file mode 100644
index 0000000..4ba04de
--- /dev/null
+++ b/uploads/torrents/1749220294853.jpg
Binary files differ