种子,促销

Change-Id: I0ce919ce4228dcefec26ef636bacd3298c0dc77a
diff --git a/.idea/dbnavigator.xml b/.idea/dbnavigator.xml
index bd22aef..054b2d0 100644
--- a/.idea/dbnavigator.xml
+++ b/.idea/dbnavigator.xml
@@ -523,4 +523,7 @@
       </environment>
     </general-settings>
   </component>
+  <component name="DBNavigator.Project.StatementExecutionManager">
+    <execution-variables />
+  </component>
 </project>
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 0d5980a..ae3ef3f 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,7 +4,40 @@
     <option name="autoReloadType" value="SELECTIVE" />
   </component>
   <component name="ChangeListManager">
-    <list default="true" id="f008fe30-0711-42e2-bb33-17dcfdbad387" name="Changes" comment="" />
+    <list default="true" id="f008fe30-0711-42e2-bb33-17dcfdbad387" name="Changes" comment="">
+      <change beforePath="$PROJECT_DIR$/.idea/dbnavigator.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dbnavigator.xml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/create.sql" beforeDir="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/MyProjectApplication.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/MyProjectApplication.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/common/base/Result.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/common/base/Result.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/config/MyMetaObjectHandler.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/config/MyMetaObjectHandler.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/controller/InviteController.java" beforeDir="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/dto/param/InviteParam.java" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/dto/param/TorrentUploadParam.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/dto/param/TorrentUploadParam.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/entity/EntityBase.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/entity/EntityBase.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/entity/InvitationEntity.java" beforeDir="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/mapper/InvitationMapper.java" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/mapper/UserMapper.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/mapper/UserMapper.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/InvitationService.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/example/myproject/service/InvitationService.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/service/serviceImpl/InvitationServiceImpl.java" beforeDir="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/main/resources/application.properties" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/application.properties" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/main/resources/mapper/PromotionMapper.xml" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/mapper/PromotionMapper.xml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/test/java/com/example/myproject/controller/TorrentControllerTest.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/java/com/example/myproject/controller/TorrentControllerTest.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/test/java/com/example/myproject/controller/UserControllerTest.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/java/com/example/myproject/controller/UserControllerTest.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/target/classes/application.properties" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/application.properties" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/MyProjectApplication.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/MyProjectApplication.class" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/UserController$EmailRequest.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/UserController$EmailRequest.class" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/UserController$VerificationRequest.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/controller/UserController$VerificationRequest.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/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/mapper/UserMapper.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/example/myproject/mapper/UserMapper.class" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/target/classes/com/example/myproject/utils/Result.class" beforeDir="false" />
+    </list>
     <option name="SHOW_DIALOG" value="false" />
     <option name="HIGHLIGHT_CONFLICTS" value="true" />
     <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -30,23 +63,102 @@
       </MavenImportingSettings>
     </option>
   </component>
+  <component name="ProblemsViewState">
+    <option name="selectedTabId" value="CurrentFile" />
+  </component>
   <component name="ProjectId" id="2vZNfNTEFyHApdxmHZ7Y0rlJjKB" />
   <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
   <component name="ProjectViewState">
     <option name="hideEmptyMiddlePackages" value="true" />
     <option name="showLibraryContents" value="true" />
   </component>
-  <component name="PropertiesComponent"><![CDATA[{
-  "keyToString": {
-    "RunOnceActivity.OpenProjectViewOnStart": "true",
-    "RunOnceActivity.ShowReadmeOnStart": "true"
+  <component name="PropertiesComponent">{
+  &quot;keyToString&quot;: {
+    &quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
+    &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
+    &quot;last_opened_file_path&quot;: &quot;D:/study/学习资源/大三下/zy1&quot;
   }
-}]]></component>
+}</component>
   <component name="RecentsManager">
     <key name="MoveFile.RECENT_KEYS">
       <recent name="D:\study\学习资源\大三下\school\echo-backend\src\main\resources\mapper" />
     </key>
   </component>
+  <component name="RunManager" selected="应用程序.MyProjectApplication">
+    <configuration name="BtClient" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
+      <option name="MAIN_CLASS_NAME" value="com.example.myproject.client.BtClient" />
+      <module name="echo-backend" />
+      <extension name="coverage">
+        <pattern>
+          <option name="PATTERN" value="com.example.myproject.client.*" />
+          <option name="ENABLED" value="true" />
+        </pattern>
+      </extension>
+      <method v="2">
+        <option name="Make" enabled="true" />
+      </method>
+    </configuration>
+    <configuration name="MyProjectApplication" type="Application" factoryName="Application" temporary="true">
+      <option name="MAIN_CLASS_NAME" value="com.example.myproject.MyProjectApplication" />
+      <module name="echo-backend" />
+      <extension name="coverage">
+        <pattern>
+          <option name="PATTERN" value="com.example.myproject.*" />
+          <option name="ENABLED" value="true" />
+        </pattern>
+      </extension>
+      <method v="2">
+        <option name="Make" enabled="true" />
+      </method>
+    </configuration>
+    <configuration name="MyProjectApplication" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
+      <option name="MAIN_CLASS_NAME" value="com.example.myproject.MyProjectApplication" />
+      <module name="echo-backend" />
+      <extension name="coverage">
+        <pattern>
+          <option name="PATTERN" value="com.example.myproject.*" />
+          <option name="ENABLED" value="true" />
+        </pattern>
+      </extension>
+      <method v="2">
+        <option name="Make" enabled="true" />
+      </method>
+    </configuration>
+    <configuration name="TrackerServer" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
+      <option name="MAIN_CLASS_NAME" value="com.example.myproject.tracker.TrackerServer" />
+      <module name="echo-backend" />
+      <extension name="coverage">
+        <pattern>
+          <option name="PATTERN" value="com.example.myproject.tracker.*" />
+          <option name="ENABLED" value="true" />
+        </pattern>
+      </extension>
+      <method v="2">
+        <option name="Make" enabled="true" />
+      </method>
+    </configuration>
+    <configuration name="TorrentControllerTest" type="JUnit" factoryName="JUnit" temporary="true" nameIsGenerated="true">
+      <module name="echo-backend" />
+      <extension name="coverage">
+        <pattern>
+          <option name="PATTERN" value="com.example.myproject.controller.*" />
+          <option name="ENABLED" value="true" />
+        </pattern>
+      </extension>
+      <option name="PACKAGE_NAME" value="com.example.myproject.controller" />
+      <option name="MAIN_CLASS_NAME" value="com.example.myproject.controller.TorrentControllerTest" />
+      <option name="TEST_OBJECT" value="class" />
+      <method v="2">
+        <option name="Make" enabled="true" />
+      </method>
+    </configuration>
+    <recent_temporary>
+      <list>
+        <item itemvalue="应用程序.MyProjectApplication" />
+        <item itemvalue="JUnit.TorrentControllerTest" />
+      </list>
+    </recent_temporary>
+  </component>
   <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
   <component name="TaskManager">
     <task active="true" id="Default" summary="Default task">
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..06da2ea
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,14 @@
+{
+    "configurations": [
+        {
+            "type": "java",
+            "name": "Spring Boot-MyProjectApplication<echo-backend>",
+            "request": "launch",
+            "cwd": "${workspaceFolder}",
+            "mainClass": "com.example.myproject.MyProjectApplication",
+            "projectName": "echo-backend",
+            "args": "",
+            "envFile": "${workspaceFolder}/.env"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..b84f89c
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,4 @@
+{
+    "java.configuration.updateBuildConfiguration": "interactive",
+    "java.compile.nullAnalysis.mode": "automatic"
+}
\ No newline at end of file
diff --git a/data/torrents/a4cd16b26a89ec7e5dc534040edca948f78918ec.torrent b/data/torrents/a4cd16b26a89ec7e5dc534040edca948f78918ec.torrent
new file mode 100644
index 0000000..17908b6
--- /dev/null
+++ b/data/torrents/a4cd16b26a89ec7e5dc534040edca948f78918ec.torrent
Binary files differ
diff --git a/data/torrents/files.torrent b/data/torrents/files.torrent
new file mode 100644
index 0000000..e04974f
--- /dev/null
+++ b/data/torrents/files.torrent
@@ -0,0 +1 @@
+d8:announce22:https://tracker.byr.pt10:created by21:qBittorrent v4.5.3.1013:creation datei1747717901e4:infod5:filesld6:lengthi173e4:pathl13:valid.torrenteee4:name5:files12:piece lengthi16384e6:pieces20:/ñíèEô5ã<òûìՄŸQ¡–ûee
\ No newline at end of file
diff --git a/data/torrents/valid.torrent b/data/torrents/valid.torrent
new file mode 100644
index 0000000..6a90e52
--- /dev/null
+++ b/data/torrents/valid.torrent
@@ -0,0 +1 @@
+d10:created by18:qBittorrent v5.1.013:creation datei1745948995e4:infod6:lengthi22e4:name15:example.torrent12:piece lengthi16384e6:pieces20:Fnð¶)ú<Ç	ŽæÂh£tl7:privatei1eee
\ No newline at end of file
diff --git a/libs/ttorrent-bencoding-1.3.0-SNAPSHOT.jar b/libs/ttorrent-bencoding-1.3.0-SNAPSHOT.jar
new file mode 100644
index 0000000..fcc7fc6
--- /dev/null
+++ b/libs/ttorrent-bencoding-1.3.0-SNAPSHOT.jar
Binary files differ
diff --git a/libs/ttorrent-cli-1.3.0-SNAPSHOT.jar b/libs/ttorrent-cli-1.3.0-SNAPSHOT.jar
new file mode 100644
index 0000000..4351f7d
--- /dev/null
+++ b/libs/ttorrent-cli-1.3.0-SNAPSHOT.jar
Binary files differ
diff --git a/libs/ttorrent-client-1.3.0-SNAPSHOT.jar b/libs/ttorrent-client-1.3.0-SNAPSHOT.jar
new file mode 100644
index 0000000..0a3d31f
--- /dev/null
+++ b/libs/ttorrent-client-1.3.0-SNAPSHOT.jar
Binary files differ
diff --git a/libs/ttorrent-common-1.3.0-SNAPSHOT.jar b/libs/ttorrent-common-1.3.0-SNAPSHOT.jar
new file mode 100644
index 0000000..7d40e7a
--- /dev/null
+++ b/libs/ttorrent-common-1.3.0-SNAPSHOT.jar
Binary files differ
diff --git a/libs/ttorrent-network-1.0.jar b/libs/ttorrent-network-1.0.jar
new file mode 100644
index 0000000..b851181
--- /dev/null
+++ b/libs/ttorrent-network-1.0.jar
Binary files differ
diff --git a/libs/ttorrent-test-api-1.0.jar b/libs/ttorrent-test-api-1.0.jar
new file mode 100644
index 0000000..0c4d56a
--- /dev/null
+++ b/libs/ttorrent-test-api-1.0.jar
Binary files differ
diff --git a/libs/ttorrent-tests-1.3.0-SNAPSHOT.jar b/libs/ttorrent-tests-1.3.0-SNAPSHOT.jar
new file mode 100644
index 0000000..577bfb7
--- /dev/null
+++ b/libs/ttorrent-tests-1.3.0-SNAPSHOT.jar
Binary files differ
diff --git a/libs/ttorrent-tracker-1.3.0-SNAPSHOT.jar b/libs/ttorrent-tracker-1.3.0-SNAPSHOT.jar
new file mode 100644
index 0000000..5f70461
--- /dev/null
+++ b/libs/ttorrent-tracker-1.3.0-SNAPSHOT.jar
Binary files differ
diff --git a/pom.xml b/pom.xml
index db6ed19..55cb698 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
     <groupId>groupId</groupId>
@@ -11,13 +11,13 @@
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
         <version>2.6.0</version> <!-- 使用你希望的Spring Boot版本 -->
-        <relativePath/> <!-- lookup parent from repository -->
+        <relativePath /> <!-- lookup parent from repository -->
     </parent>
-
     <properties>
         <maven.compiler.source>17</maven.compiler.source>
         <maven.compiler.target>17</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <sa-token.version>1.37.0</sa-token.version>
     </properties>
 
     <dependencies>
@@ -78,6 +78,16 @@
             <groupId>javax.xml.bind</groupId>
             <artifactId>jaxb-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
+            <version>4.5.0</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+            <version>2.0.1.Final</version>
+        </dependency>
         <!-- Spring Web MVC -->
         <dependency>
             <groupId>org.springframework</groupId>
@@ -108,11 +118,17 @@
             <version>1.18.30</version> <!-- 确保使用的是最新版本 -->
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+            <version>1.4.7</version>
+        </dependency>
+
 
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
-            <version>1.7.30</version> <!-- 版本可以根据需要调整 -->
+            <version>1.7.32</version> <!-- 版本可以根据需要调整 -->
         </dependency>
         <dependency>
             <groupId>ch.qos.logback</groupId>
@@ -129,11 +145,7 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-validation</artifactId>
         </dependency>
-        <dependency>
-            <groupId>com.baidubce</groupId>
-            <artifactId>qianfan</artifactId>
-            <version>0.1.1</version>
-        </dependency>
+
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-webflux</artifactId>
@@ -144,16 +156,68 @@
             <version>4.12</version>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
+
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.turn</groupId>
+            <artifactId>ttorrent-common</artifactId>
+            <version>1.3.0-SNAPSHOT</version>
+        </dependency>
+
+        <!-- ttorrent -->
+        <dependency>
+            <groupId>com.turn</groupId>
+            <artifactId>ttorrent-client</artifactId>
+            <version>1.3.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>com.turn</groupId>
+            <artifactId>ttorrent-cli</artifactId>
+            <version>1.3.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>com.turn</groupId>
+            <artifactId>ttorrent-network</artifactId>
+            <version>1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.turn</groupId>
+            <artifactId>ttorrent-test-api</artifactId>
+            <version>1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.turn</groupId>
+            <artifactId>ttorrent-bencoding</artifactId>
+            <version>1.3.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>com.turn</groupId>
+            <artifactId>ttorrent-tests</artifactId>
+            <version>1.3.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>com.turn</groupId>
+            <artifactId>ttorrent-tracker</artifactId>
+            <version>1.3.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
+            <version>4.5.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.dev33</groupId>
+            <artifactId>sa-token-spring-boot-starter</artifactId>
+            <version>${sa-token.version}</version>
+        </dependency>
+
+
+
 
     </dependencies>
 
@@ -202,7 +266,8 @@
                 <artifactId>maven-surefire-plugin</artifactId>
                 <version>2.9</version>
                 <configuration>
-                    <!--suppress UnresolvedMavenProperty -->
+                    <!--suppress
+                    UnresolvedMavenProperty -->
                     <argLine>${surefireArgLine}</argLine>
                     <skipTests>false</skipTests>
                     <includes>
@@ -221,14 +286,22 @@
             </plugin>
 
         </plugins>
-        <resources>
-            <resource>
-                <directory>src/main/java</directory>
-                <includes>
-                    <include>**/*.xml</include>
-                </includes>
-            </resource>
-        </resources>
+<!--                <resources>-->
+<!--                    <resource>-->
+<!--                        <directory>src/main/java</directory>-->
+<!--                        <includes>-->
+<!--                            <include>**/*.xml</include>-->
+<!--                        </includes>-->
+<!--                    </resource>-->
+<!--                    <resource>-->
+<!--                        <directory>libs</directory>-->
+<!--                        <includes>-->
+<!--                            <include>*.jar</include>-->
+<!--                        </includes>-->
+<!--                    </resource>-->
+<!--                    </resources>-->
+
+
     </build>
 
 </project>
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/MyProjectApplication.java b/src/main/java/com/example/myproject/MyProjectApplication.java
index 49cd5f0..eb58082 100644
--- a/src/main/java/com/example/myproject/MyProjectApplication.java
+++ b/src/main/java/com/example/myproject/MyProjectApplication.java
@@ -5,10 +5,14 @@
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 
+
+
 @SpringBootApplication
 @MapperScan("com.example.myproject.mapper")  // 扫描 Mapper 接口
+
 public class MyProjectApplication {
     public static void main(String[] args) {
         SpringApplication.run(MyProjectApplication.class, args);
     }
 }
+
diff --git a/src/main/java/com/example/myproject/common/CommonResultStatus.java b/src/main/java/com/example/myproject/common/CommonResultStatus.java
new file mode 100644
index 0000000..76e63a6
--- /dev/null
+++ b/src/main/java/com/example/myproject/common/CommonResultStatus.java
@@ -0,0 +1,37 @@
+package com.example.myproject.common;
+
+
+public enum CommonResultStatus implements ResultStatus {
+
+    OK(0, "成功"),
+
+    FAIL(500, "失败"),
+
+    PARAM_ERROR(400, "参数非法"),
+
+    RECORD_NOT_EXIST(404, "记录不存在"),
+
+    UNAUTHORIZED(401, "未授权"),
+
+    FORBIDDEN(403, "无权限"),
+
+    SERVER_ERROR(500, "服务器内部错误");
+
+    private final int code;
+    private final String message;
+
+    CommonResultStatus(int code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    @Override
+    public int getCode() {
+        return code;
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+    }
+}
diff --git a/src/main/java/com/example/myproject/common/Constants.java b/src/main/java/com/example/myproject/common/Constants.java
new file mode 100644
index 0000000..4e3d864
--- /dev/null
+++ b/src/main/java/com/example/myproject/common/Constants.java
@@ -0,0 +1,54 @@
+package com.example.myproject.common;
+
+
+public interface Constants {
+
+    String TOKEN_HEADER_NAME = "Authorization";
+    String SESSION_CURRENT_USER = "currentUser";
+
+    /**
+     * 菜单根id
+     */
+    Integer RESOURCE_ROOT_ID = 0;
+
+    interface Order {
+        String DEFAULT_ORDER_TYPE = "desc";
+
+        String[] ORDER_TYPE = new String[]{"desc", "asc", "DESC", "ASC"};
+    }
+
+    interface FinishStatus {
+
+        /**
+         * 已完成并测试
+         */
+        String FINISHED = " (已完成并测试通过)";
+
+        /**
+         * 未完成未测试
+         */
+        String UNFINISHED = " (未完成未测试)";
+
+        /**
+         * 已完成但未测试
+         */
+        String FINISHED_NOT_TEST = " (已完成但未测试)";
+
+    }
+
+    interface Source {
+        String PREFIX = "[RKT] ";
+
+        String NAME = "rocket pt";
+    }
+
+    interface Announce {
+
+        String PROTOCOL = "http";
+
+        String HOSTNAME = "192.168.6.112";
+
+        Integer PORT = 9966;
+
+    }
+}
diff --git a/src/main/java/com/example/myproject/common/ResultStatus.java b/src/main/java/com/example/myproject/common/ResultStatus.java
new file mode 100644
index 0000000..f6c7afe
--- /dev/null
+++ b/src/main/java/com/example/myproject/common/ResultStatus.java
@@ -0,0 +1,14 @@
+package com.example.myproject.common;
+
+
+public interface ResultStatus {
+    /**
+     * 错误码
+     */
+    int getCode();
+
+    /**
+     * 错误信息
+     */
+    String getMessage();
+}
diff --git a/src/main/java/com/example/myproject/common/base/I18nMessage.java b/src/main/java/com/example/myproject/common/base/I18nMessage.java
new file mode 100644
index 0000000..a725e9b
--- /dev/null
+++ b/src/main/java/com/example/myproject/common/base/I18nMessage.java
@@ -0,0 +1,61 @@
+package com.example.myproject.common.base;
+
+import org.springframework.context.MessageSource;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.context.support.ReloadableResourceBundleMessageSource;
+
+import java.util.Objects;
+
+import lombok.experimental.UtilityClass;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@UtilityClass
+public class I18nMessage {
+
+    static {
+        ReloadableResourceBundleMessageSource messageSource =
+                new ReloadableResourceBundleMessageSource();
+        messageSource.setCacheSeconds(5);
+        messageSource.setBasenames("classpath:i18n/message");
+        I18nMessage.init(messageSource);
+    }
+
+    private static MessageSource messageSource;
+
+    public static void init(MessageSource messageSource) {
+        Objects.requireNonNull(messageSource, "MessageSource can't be null");
+        I18nMessage.messageSource = messageSource;
+    }
+
+    /**
+     * 读取国际化消息
+     *
+     * @param msgCode 消息码
+     * @param args    消息参数 例: new String[]{"1","2","3"}
+     * @return
+     */
+    public static String getMessage(String msgCode, Object[] args) {
+        try {
+            return I18nMessage.messageSource.getMessage(msgCode, args,
+                    LocaleContextHolder.getLocale());
+        } catch (Exception e) {
+            if (log.isDebugEnabled()) {
+                e.printStackTrace();
+            }
+            log.error("===> 读取国际化消息失败, code:{}, args:{}, ex:{}", msgCode, args,
+                    e.getMessage() == null ? e.toString() : e.getMessage());
+        }
+        return "-Unknown-";
+    }
+
+    /**
+     * 获取一条语言配置信息
+     *
+     * @param msgCode 消息码
+     * @return 对应配置的信息
+     */
+    public static String getMessage(String msgCode) {
+        return I18nMessage.getMessage(msgCode, null);
+    }
+}
diff --git a/src/main/java/com/example/myproject/common/base/OrderPageParam.java b/src/main/java/com/example/myproject/common/base/OrderPageParam.java
new file mode 100644
index 0000000..21ff38b
--- /dev/null
+++ b/src/main/java/com/example/myproject/common/base/OrderPageParam.java
@@ -0,0 +1,99 @@
+package com.example.myproject.common.base;
+
+
+import com.example.myproject.common.CommonResultStatus;
+import com.example.myproject.common.Constants;
+import com.example.myproject.common.exception.RocketPTException;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class OrderPageParam extends PageParam {
+
+    /**
+     * 排序字段
+     */
+    @Schema(description = "排序字段")
+    protected String prop;
+
+    /**
+     * 排序规则
+     */
+    @Schema(description = "排序规则")
+    protected String sort;
+
+    public void validOrder(List<String> orderKey) throws RocketPTException {
+        prop = StringUtils.isBlank(prop) ? null : StrUtil.toUnderlineCase(prop);
+        sort = StringUtils.isBlank(sort) ? Constants.Order.DEFAULT_ORDER_TYPE : sort;
+
+        if (Arrays.asList(Constants.Order.ORDER_TYPE).indexOf(sort) < 0) {
+            throw new RocketPTException(CommonResultStatus.PARAM_ERROR, "排序方式錯誤");
+        }
+
+        if (StringUtils.isNotBlank(prop) && Arrays.asList(orderKey).indexOf(prop) < 0) {
+            throw new RocketPTException(CommonResultStatus.PARAM_ERROR, "排序欄位錯誤");
+        }
+    }
+
+    public void validOrder() throws RocketPTException {
+        List<String> orderKey = getOrderKey();
+        prop = StringUtils.isBlank(prop) ? null : StrUtil.toUnderlineCase(prop);
+        sort = StringUtils.isBlank(sort) ? Constants.Order.DEFAULT_ORDER_TYPE : sort;
+
+        if (!ArrayUtil.contains(Constants.Order.ORDER_TYPE, sort)) {
+            throw new RocketPTException(CommonResultStatus.PARAM_ERROR, "排序方式錯誤");
+        }
+
+        if (StringUtils.isNotBlank(prop) && !orderKey.contains(prop)) {
+            throw new RocketPTException(CommonResultStatus.PARAM_ERROR, "排序欄位錯誤");
+        }
+    }
+
+    /**
+     * @return 反射获取字段列表
+     */
+    public List<String> getOrderKey() {
+        List<String> list = new ArrayList<>();
+
+        Field[] fields = getClass().getDeclaredFields();
+        if (fields != null) {
+            for (Field field : fields) {
+                field.setAccessible(true);
+                String name = field.getName();
+
+                list.add(StrUtil.toUnderlineCase(name));
+            }
+        }
+        return list;
+    }
+    /**
+     * @return 反射获取字段列表
+     */
+    public List<String> getOrderKey(Class clazz) {
+        List<String> list = new ArrayList<>();
+
+        Field[] fields = clazz.getDeclaredFields();
+        if (fields != null) {
+            for (Field field : fields) {
+                field.setAccessible(true);
+                String name = field.getName();
+
+                list.add(StrUtil.toUnderlineCase(name));
+            }
+        }
+        return list;
+    }
+
+}
diff --git a/src/main/java/com/example/myproject/common/base/PageParam.java b/src/main/java/com/example/myproject/common/base/PageParam.java
new file mode 100644
index 0000000..c59108d
--- /dev/null
+++ b/src/main/java/com/example/myproject/common/base/PageParam.java
@@ -0,0 +1,25 @@
+package com.example.myproject.common.base;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+
+@Getter
+@Setter
+@ToString
+public class PageParam {
+    @NotNull(message = "page参数不能为空")
+    @Min(value = 1L, message = "page参数必须是数字或数值小于限制")
+    protected Integer page;
+
+    @NotNull(message = "参数不能为空")
+    @Min(value = 1L, message = "参数必须是数字或数值小于限制")
+    @Max(value = 200L, message = "参数必须是数字或数值大于限制")
+    protected Integer size;
+
+
+}
diff --git a/src/main/java/com/example/myproject/common/base/PageUtil.java b/src/main/java/com/example/myproject/common/base/PageUtil.java
new file mode 100644
index 0000000..a4b51ce
--- /dev/null
+++ b/src/main/java/com/example/myproject/common/base/PageUtil.java
@@ -0,0 +1,54 @@
+package com.example.myproject.common.base;
+
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+
+import java.util.List;
+
+public class PageUtil {
+
+
+    public static int DEFAULT_PAGE_SIZE = 20;
+
+    /**
+     * 开始分页
+     *
+     * @param param
+     */
+    public static void startPage(OrderPageParam param) {
+        Integer page = param.getPage();
+        if (page == null) {
+            param.setPage(1);
+            param.setSize(DEFAULT_PAGE_SIZE);
+        }
+
+        PageHelper.startPage(param.getPage(), param.getSize());
+
+    }
+
+    /**
+     * 开始分页
+     *
+     * @param param
+     */
+    public static void startPage(PageParam param) {
+        Integer page = param.getPage();
+        if (page == null) {
+            param.setPage(1);
+            param.setSize(DEFAULT_PAGE_SIZE);
+        }
+
+        PageHelper.startPage(param.getPage(), param.getSize());
+    }
+
+    /**
+     * 分页结果
+     *
+     * @param list
+     */
+    public static ResPage getPage(List list) {
+        PageInfo pageInfo = new PageInfo(list);
+        return new ResPage(pageInfo.getTotal(), pageInfo.getPageNum(), pageInfo.getSize());
+
+    }
+}
diff --git a/src/main/java/com/example/myproject/common/base/ResPage.java b/src/main/java/com/example/myproject/common/base/ResPage.java
new file mode 100644
index 0000000..45a22dd
--- /dev/null
+++ b/src/main/java/com/example/myproject/common/base/ResPage.java
@@ -0,0 +1,31 @@
+package com.example.myproject.common.base;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 分页的返回值
+ */
+@Getter
+@Setter
+@ToString(callSuper = false)
+@NoArgsConstructor
+@AllArgsConstructor
+public class ResPage {
+
+    private long total;
+
+    private int page;
+
+    private int size;
+
+    public static ResPage getPage(long total, int page, int size) {
+        return new ResPage(total, page, size);
+    }
+
+    public static ResPage defaultPage() {
+        return new ResPage(10, 1, 10);
+    }
+}
diff --git a/src/main/java/com/example/myproject/common/base/Result.java b/src/main/java/com/example/myproject/common/base/Result.java
new file mode 100644
index 0000000..6d20f4f
--- /dev/null
+++ b/src/main/java/com/example/myproject/common/base/Result.java
@@ -0,0 +1,178 @@
+package com.example.myproject.common.base;
+
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 返回值实体类
+ */
+@Getter
+@Setter
+@ToString
+@NoArgsConstructor
+public class Result<T> {
+
+    private int code;
+
+    private String msg;
+
+    @JsonProperty
+    private T data;
+
+    private ResPage page;
+
+
+    public Result(Status status) {
+        this.code = status.getCode();
+        this.msg = status.getMsg();
+        this.data = null;
+    }
+
+    public Result(Status status, T data) {
+        this.code = status.getCode();
+        this.msg = status.getMsg();
+        this.data = data;
+    }
+
+    public Result(Status status, String msg) {
+        this.code = status.getCode();
+        this.msg = msg;
+        this.data = null;
+    }
+
+    public Result(Status status, int msgCode) {
+        this.code = status.getCode();
+        this.msg = I18nMessage.getMessage(String.valueOf(msgCode));
+        this.data = null;
+    }
+
+    public Result(Status status, String msg, T data) {
+        this.code = status.getCode();
+        this.msg = msg;
+        this.data = data;
+    }
+
+    public Result(Status status, int msgCode, T data) {
+        this.code = status.getCode();
+        this.msg = I18nMessage.getMessage(String.valueOf(msgCode));
+        this.data = data;
+    }
+
+    public Result(Status status, T data, ResPage page) {
+        this.code = status.getCode();
+        this.msg = status.getMsg();
+        this.data = data;
+        this.page = page;
+    }
+
+    public Result(Status status, String msg, T data, ResPage page) {
+        this.code = status.getCode();
+        this.msg = msg;
+        this.data = data;
+        this.page = page;
+    }
+
+    public Result(Status status, int msgCode, T data, ResPage page) {
+        this.code = status.getCode();
+        this.msg = I18nMessage.getMessage(String.valueOf(msgCode));
+        this.data = data;
+        this.page = page;
+    }
+
+    @JsonIgnore
+    public boolean isSuccess() {
+        return this.code == Status.SUCCESS.getCode();
+    }
+
+    @JsonIgnore
+    public boolean nonSuccess() {
+        return this.code != Status.SUCCESS.getCode();
+    }
+
+    public static <T> Result<T> success(String 取消收藏成功) {
+        return new Result<T>(Status.SUCCESS);
+    }
+
+    public static <T> Result<T> ok() {
+        return new Result<T>(Status.SUCCESS);
+    }
+
+    public static <T> Result<T> ok(T data) {
+        return new Result<T>(Status.SUCCESS, data);
+    }
+
+    public static <T> Result<T> illegal() {
+        return new Result<T>(Status.BAD_REQUEST);
+    }
+
+    public static <T> Result<T> unauthorized() {
+        return new Result<T>(Status.UNAUTHORIZED);
+    }
+
+    public static <T> Result<T> forbidden() {
+        return new Result<T>(Status.FORBIDDEN);
+    }
+
+    public static <T> Result<T> notFound() {
+        return new Result<T>(Status.NOT_FOUND);
+    }
+
+    public static <T> Result<T> failure() {
+        return new Result<T>(Status.FAILURE);
+    }
+
+    public static <T> Result<T> failure(String msg) {
+        return new Result<T>(Status.FAILURE, msg);
+    }
+
+    public static <T> Result<T> error(String msg) {
+        return new Result<T>(Status.FAILURE, msg);
+    }
+
+    public static <T> Result<T> conflict() {
+        return new Result<T>(Status.CONFLICT);
+    }
+
+    public static <T> Result<T> build(Status status, T data) {
+        return new Result<T>(status, data);
+    }
+
+    public static <T> Result<T> build(Status status, String msg) {
+        return new Result<T>(status, msg);
+    }
+
+    public static <T> Result<T> build(Status status, int msgCode) {
+        return new Result<T>(status, msgCode);
+    }
+
+    public static <T> Result<T> build(Status status, String msg, T data) {
+        return new Result<T>(status, msg, data);
+    }
+
+    public static <T> Result<T> build(Status status, int msgCode, T data) {
+        return new Result<T>(status, msgCode, data);
+    }
+
+    public static Result ok(Object data, ResPage page) {
+        return new Result(Status.SUCCESS, data, page);
+    }
+
+    public static Result build(Status status, Object data, ResPage page) {
+        return new Result(status, data, page);
+    }
+
+    public static Result build(Status status, String msg, Object data, ResPage page) {
+        return new Result(status, msg, data, page);
+    }
+
+    public static Result build(Status status, int msgCode, Object data, ResPage page) {
+        return new Result(status, msgCode, data, page);
+    }
+
+}
diff --git a/src/main/java/com/example/myproject/common/base/Status.java b/src/main/java/com/example/myproject/common/base/Status.java
new file mode 100644
index 0000000..4949d0c
--- /dev/null
+++ b/src/main/java/com/example/myproject/common/base/Status.java
@@ -0,0 +1,57 @@
+package com.example.myproject.common.base;
+
+
+public enum Status {
+
+    /**
+     * 请求执行成功
+     */
+    SUCCESS(0, "操作成功"),
+
+    /**
+     * 请求验证失败
+     */
+    BAD_REQUEST(400, "操作验证失败"),
+
+    /**
+     * 权限不足
+     */
+    UNAUTHORIZED(401, "操作未授权"),
+
+    /**
+     * 请求拒绝
+     */
+    FORBIDDEN(403, "操作被拒绝"),
+
+    /**
+     * 未知请求
+     */
+    NOT_FOUND(404, "未知操作"),
+
+    /**
+     * 未知请求
+     */
+    CONFLICT(409, "请求发生冲突"),
+
+    /**
+     * 请求执行失败
+     */
+    FAILURE(500, "操作失败");
+
+    private int code;
+
+    private String msg;
+
+    Status(int code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public int getCode() {
+        return this.code;
+    }
+
+    public String getMsg() {
+        return this.msg;
+    }
+}
diff --git a/src/main/java/com/example/myproject/common/exception/RocketPTException.java b/src/main/java/com/example/myproject/common/exception/RocketPTException.java
new file mode 100644
index 0000000..7a475f2
--- /dev/null
+++ b/src/main/java/com/example/myproject/common/exception/RocketPTException.java
@@ -0,0 +1,29 @@
+package com.example.myproject.common.exception;
+
+import com.example.myproject.common.CommonResultStatus;
+import com.example.myproject.common.ResultStatus;
+
+public class RocketPTException extends RuntimeException {
+    private final ResultStatus status;
+
+    public RocketPTException(ResultStatus status) {
+        super(status.getMessage());
+        this.status = status;
+    }
+
+    public RocketPTException(ResultStatus status, String message) {
+        super(message);
+        this.status = status;
+    }
+
+    public RocketPTException(String message) {
+        super(message);
+        this.status = CommonResultStatus.FAIL;
+    }
+
+    public ResultStatus getStatus() {
+        return status;
+    }
+
+
+}
diff --git a/src/main/java/com/example/myproject/config/MyMetaObjectHandler.java b/src/main/java/com/example/myproject/config/MyMetaObjectHandler.java
new file mode 100644
index 0000000..1031b93
--- /dev/null
+++ b/src/main/java/com/example/myproject/config/MyMetaObjectHandler.java
@@ -0,0 +1,24 @@
+package com.example.myproject.config;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+
+@Component
+public class MyMetaObjectHandler implements MetaObjectHandler {
+
+
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
+        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
+    }
+
+    // 更新时自动填充
+    @Override
+    public void updateFill(MetaObject metaObject) {
+        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
+    }
+}
diff --git a/src/main/java/com/example/myproject/config/TrackerConfig.java b/src/main/java/com/example/myproject/config/TrackerConfig.java
new file mode 100644
index 0000000..67522b7
--- /dev/null
+++ b/src/main/java/com/example/myproject/config/TrackerConfig.java
@@ -0,0 +1,81 @@
+package com.example.myproject.config;
+
+import com.turn.ttorrent.tracker.TrackedTorrent;
+import com.turn.ttorrent.tracker.Tracker;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import javax.annotation.PreDestroy;
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+
+@Configuration
+public class TrackerConfig {
+    @Value("${pt.tracker.port}")
+    private int trackerPort;
+
+    @Value("${pt.tracker.torrent-dir}")
+    private String torrentDir;
+    @Value("${pt.tracker.announce-url}")
+    private String announceURL;
+
+    private Tracker tracker; // 存储 Tracker 实例
+
+    @Bean
+    public Tracker torrentTracker() throws IOException {
+        // 验证并创建目录
+        File dir = new File(torrentDir);
+        validateTorrentDirectory(dir);
+
+        // 初始化 Tracker
+        tracker = new Tracker(trackerPort, announceURL);
+        tracker.setAcceptForeignTorrents(false); // PT 站点必须关闭匿名模式
+        tracker.setAnnounceInterval(1800); // 30分钟 announce 间隔
+
+        // 加载种子文件
+        loadTorrents(tracker, dir);
+
+        // 启动 Tracker
+        tracker.start(true);
+        System.out.println("Tracker started on port " + trackerPort);
+        return tracker;
+    }
+
+    private void validateTorrentDirectory(File dir) throws IOException {
+        if (!dir.exists()) {
+            if (!dir.mkdirs()) {
+                throw new IOException("无法创建目录: " + dir.getAbsolutePath());
+            }
+        }
+        if (!dir.isDirectory()) {
+            throw new IllegalArgumentException("路径不是目录: " + dir.getAbsolutePath());
+        }
+        if (!dir.canRead() || !dir.canWrite()) {
+            throw new IOException("应用程序无权限访问目录: " + dir.getAbsolutePath());
+        }
+    }
+
+    private void loadTorrents(Tracker tracker, File dir) throws IOException {
+        if (!dir.exists() || !dir.isDirectory()) {
+            throw new IOException("无效的种子目录: " + dir.getAbsolutePath());
+        }
+
+        File[] torrentFiles = dir.listFiles((d, name) -> name.endsWith(".torrent"));
+        if (torrentFiles == null) {
+            throw new IOException("无法读取目录内容: " + dir.getAbsolutePath());
+        }
+
+        for (File f : torrentFiles) {
+            tracker.announce(TrackedTorrent.load(f));
+        }
+    }
+
+    @PreDestroy
+    public void stopTracker() {
+        if (tracker != null) {
+            tracker.stop();
+            System.out.println("Tracker stopped.");
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/controller/TorrentController.java b/src/main/java/com/example/myproject/controller/TorrentController.java
new file mode 100644
index 0000000..cea2ccf
--- /dev/null
+++ b/src/main/java/com/example/myproject/controller/TorrentController.java
@@ -0,0 +1,293 @@
+package com.example.myproject.controller;
+
+import com.example.myproject.common.base.PageUtil;
+import com.example.myproject.entity.TorrentEntity;
+import com.example.myproject.service.TorrentService;
+import com.example.myproject.service.PromotionService;
+import com.example.myproject.dto.param.TorrentParam;
+import com.example.myproject.dto.vo.TorrentVO;
+import com.example.myproject.common.base.Result;
+import com.example.myproject.dto.param.TorrentUploadParam;
+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 io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+import cn.dev33.satoken.annotation.SaCheckLogin;
+import cn.dev33.satoken.stp.StpUtil;
+
+@RestController
+@RequestMapping("/seeds")
+public class TorrentController {
+
+    @Autowired
+    private TorrentService torrentService;
+
+    @Autowired
+    private PromotionService promotionService;
+
+    @Autowired
+    private UserService userService;
+
+
+    @SaCheckLogin
+    @Operation(summary = "种子列表查询", description = "种子列表条件查询-分页-排序")
+    @ApiResponse(responseCode = "0", description = "操作成功",
+            content = {@Content(mediaType = "application/json",
+                    schema = @Schema(implementation = TorrentVO.class))
+            })
+    @PostMapping("/list")
+    public Result list(@RequestBody TorrentParam param) {
+        // 构建排序和模糊查询条件
+        param.validOrder(param.getOrderKey(TorrentEntity.class));
+        param.buildLike();
+
+        PageUtil.startPage(param);
+
+        // 查询数据
+        List<TorrentEntity> list = torrentService.search(param);
+
+        // 返回分页结果
+        return Result.ok(list, PageUtil.getPage(list));
+    }
+
+    @SaCheckLogin
+    @Operation(summary = "种子详情查询")
+    @ApiResponse(responseCode = "0", description = "操作成功", content = {
+            @Content(mediaType = "application/json", schema = @Schema(implementation =
+                    TorrentEntity.class))
+    })
+    @PostMapping("/info/{id}")
+    public Result info(@PathVariable("id")Long id) {
+
+        TorrentEntity entity = torrentService.selectBySeedId(id);
+        return Result.ok(entity);
+    }
+
+@Operation(summary = "上传种子")
+
+    @PostMapping("/upload")
+    public Result uploadTorrent(
+            @RequestParam("file") MultipartFile file,
+            @ModelAttribute @Validated TorrentUploadParam param) throws IOException {
+        try {
+            // 验证用户权限
+            // Long userId = StpUtil.getLoginIdAsLong();
+            String userId = String.valueOf(param.getUploader());
+            param.setUploader(userId);
+
+            // 验证文件大小和类型
+            if (file.isEmpty() || file.getSize() > 10 * 1024 * 1024) { // 10MB限制
+                return Result.error("文件大小不符合要求");
+            }
+
+            if (!file.getOriginalFilename().toLowerCase().endsWith(".torrent")) {
+                return Result.error("只支持.torrent文件");
+            }
+
+            torrentService.uploadTorrent(file, param);
+            return Result.ok();
+        } catch (Exception e) {
+            return Result.error("种子上传失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取种子文件
+     */
+    @GetMapping("/{seed_id}/download")
+    public void downloadTorrent(
+            @PathVariable("seed_id") Long seedId,
+            @RequestParam("passkey") String passkey,
+            HttpServletResponse response) throws IOException {
+
+        // 获取种子实体
+        TorrentEntity entity = torrentService.selectBySeedId(seedId);
+        if (entity == null) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND, "种子不存在");
+            return;
+        }
+
+        // 获取并处理种子文件内容(需在service中实现passkey注入)
+        byte[] torrentBytes = torrentService.fetch(seedId, passkey);
+
+        // 设置下载文件名
+        String filename = entity.getFileName();
+        if (filename == null || filename.isBlank()) {
+            filename = seedId + ".torrent";
+        }
+        if (!filename.toLowerCase().endsWith(".torrent")) {
+            filename = filename + ".torrent";
+        }
+        filename = java.net.URLEncoder.encode(filename, java.nio.charset.StandardCharsets.UTF_8).replaceAll("\\+",
+                "%20");
+
+        // 设置响应头
+        response.setCharacterEncoding(java.nio.charset.StandardCharsets.UTF_8.name());
+        response.setContentLength(torrentBytes.length);
+        response.setContentType("application/x-bittorrent");
+        response.setHeader("Content-Disposition", "attachment;filename=" + filename);
+
+        // 写入文件内容
+        response.getOutputStream().write(torrentBytes);
+        response.getOutputStream().flush();
+    }
+
+    /**
+     * 收藏或者取消收藏
+     */
+    @PostMapping("/{seed_id}/favorite-toggle")
+    public Result favorite(
+            @PathVariable("seed_id") Long seedId,
+            @RequestParam("user_id") Long  userId) {
+        try {
+
+            return  torrentService.favorite(seedId, userId);
+        } catch (Exception e) {
+            return Result.error("失败: ");
+        }
+    }
+
+    @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("没有权限删除促销活动");
+            }
+
+            promotionService.deletePromotion(promotionId);
+            return Result.ok();
+        } catch (Exception e) {
+            return Result.error("删除促销失败: " + e.getMessage());
+        }
+    }
+
+    // 下载种子(包含反作弊机制)
+    @PostMapping("/{torrentId}/download")
+    public ResponseEntity<?> downloadTorrent(@PathVariable Long torrentId,
+                                           @RequestParam Long userId) {
+//        // 验证用户身份和权限
+//        if (!userService.validateUser(userId)) {
+//            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
+//        }
+
+        // 检查用户上传量是否足够
+        if (!torrentService.checkUserUploadRatio(userId)) {
+            return ResponseEntity.status(HttpStatus.FORBIDDEN)
+                .body("上传量不足,无法下载");
+        }
+
+        // 应用促销折扣(如果有)
+        double downloadSize = torrentService.calculateDownloadSize(torrentId, userId);
+
+        // 记录下载
+        torrentService.recordDownload(torrentId, userId, downloadSize);
+
+        return ResponseEntity.ok().build();
+    }
+
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/controller/UserController.java b/src/main/java/com/example/myproject/controller/UserController.java
index 4bf6adf..acda403 100644
--- a/src/main/java/com/example/myproject/controller/UserController.java
+++ b/src/main/java/com/example/myproject/controller/UserController.java
@@ -1,16 +1,25 @@
 package com.example.myproject.controller;
 
+import cn.dev33.satoken.annotation.SaCheckLogin;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.example.myproject.common.base.PageUtil;
+import com.example.myproject.dto.param.TorrentParam;
+import com.example.myproject.dto.vo.TorrentVO;
+import com.example.myproject.entity.TorrentEntity;
 import com.example.myproject.mapper.UserMapper;
 import com.example.myproject.mapper.VerificationTokenMapper;
 import com.example.myproject.entity.User;
 import com.example.myproject.entity.VerificationToken;
 import com.example.myproject.service.EmailService;
 import com.example.myproject.service.UserService;
-import com.example.myproject.utils.Result;
+import com.example.myproject.common.base.Result;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import org.apache.commons.lang3.RandomStringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -27,6 +36,7 @@
 import javax.annotation.Resource;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
+import java.util.List;
 
 @RestController
 @RequestMapping("/user")
@@ -61,9 +71,9 @@
             User user = userMapper.selectOne(new QueryWrapper<User>().eq("username", username));
 
             System.out.println("Login successful for user: " + username);
-            return Result.success(user);
+            return Result.ok(user);
         } catch (AuthenticationException e) {
-            return Result.error("401", "登录失败:" + e.getMessage());
+            return Result.error("登录失败");
         }
     }
 
@@ -71,15 +81,15 @@
     @ApiOperation(value = "用户注册", notes = "使用用户信息进行注册")
     public Result registerController(@RequestBody @ApiParam(value = "新用户信息", required = true) User newUser) {
         if (userService.checkEmailExists(newUser.getEmail())) {
-            return Result.error("邮箱冲突", "邮箱已被使用,请使用其他邮箱注册或找回密码!");
+            return Result.error( "邮箱已被使用,请使用其他邮箱注册或找回密码!");
         }
         boolean success = userService.preRegisterUser(newUser);
         if (success) {
             User responseUser = new User();
             responseUser.setEmail(newUser.getEmail());
-            return Result.success(responseUser, "验证邮件已发送,请检查您的邮箱。");
+            return Result.ok();
         } else {
-            return Result.error("注册失败", "账号已存在或注册失败!");
+            return Result.error("账号已存在或注册失败!");
         }
     }
 
@@ -100,9 +110,9 @@
         String code = verificationRequest.getCode();
         boolean isVerified = userService.verifyEmail(email, code);
         if (isVerified) {
-            return Result.success(null, "邮箱验证成功!");
+            return Result.ok();
         } else {
-            return Result.error("验证失败", "验证码错误或已过期!");
+            return Result.error( "验证码错误或已过期!");
         }
     }
 
@@ -123,7 +133,7 @@
         if (user == null) {
             logger.error("未找到与该邮箱地址相关联的用户: {}", email);
             return ResponseEntity.status(HttpStatus.BAD_REQUEST)
-                    .body(Result.error("1","未找到与该邮箱地址相关联的用户"));
+                    .body(Result.error("未找到与该邮箱地址相关联的用户"));
         }
 
         // 生成验证码
@@ -139,15 +149,42 @@
         logger.info("验证令牌已保存,用户: {}", user.getUsername());
         emailService.sendVerificationEmail(email, token);
 
-        return ResponseEntity.ok(Result.success(200, "验证邮件已发送!"));
+        return ResponseEntity.ok(Result.ok());
     }
     @PostMapping("/checkPassword")
     public Result<String> checkPassword(@RequestParam Long userId, @RequestParam String password) {
         boolean isPasswordCorrect = userService.checkPassword(userId, password);
         if (isPasswordCorrect) {
-            return Result.success("200","原始密码输入正确");
+            return Result.ok();
         } else {
-            return Result.error("305","原始密码输入错误");
+            return Result.error("原始密码输入错误");
         }
     }
+
+
+//    @SaCheckLogin
+//    @Operation(summary = "用户收藏列表", description = "获取用户收藏的种子列表-分页-排序")
+//    @ApiResponse(responseCode = "0", description = "操作成功",
+//            content = {@Content(mediaType = "application/json",
+//                    schema = @Schema(implementation = TorrentVO.class))
+//            })
+//    @PostMapping("/favorite/list")
+//    public Result listFavorites(@RequestBody FavoriteParam param) {
+//        if (param.getUserId() == null) {
+//            return Result.error("缺少 userId");
+//        }
+//
+//        // 校验排序字段是否合理(可选)
+//        param.validOrder(param.getOrderKey(TorrentEntity.class));
+//
+//        PageUtil.startPage(param);
+//
+//        List<TorrentEntity> list = favoriteService.getUserFavoritesPaged(param.getUserId());
+//
+//        return Result.ok(list, PageUtil.getPage(list));
+//    }
+//
+
+
+
 }
diff --git a/src/main/java/com/example/myproject/dto/PromotionCreateDTO.java b/src/main/java/com/example/myproject/dto/PromotionCreateDTO.java
new file mode 100644
index 0000000..211979b
--- /dev/null
+++ b/src/main/java/com/example/myproject/dto/PromotionCreateDTO.java
@@ -0,0 +1,35 @@
+package com.example.myproject.dto;
+
+import lombok.Data;
+import javax.validation.constraints.*;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+public class PromotionCreateDTO {
+    @NotBlank(message = "促销名称不能为空")
+    @Size(max = 100, message = "促销名称长度不能超过100个字符")
+    private String name;
+    
+    @Size(max = 500, message = "描述长度不能超过500个字符")
+    private String description;
+    
+    @NotNull(message = "开始时间不能为空")
+    private LocalDateTime startTime;
+    
+    @NotNull(message = "结束时间不能为空")
+    private LocalDateTime endTime;
+    
+    @NotNull(message = "折扣比例不能为空")
+    @Min(value = 0, message = "折扣比例不能小于0")
+    @Max(value = 100, message = "折扣比例不能大于100")
+    private double discountPercentage;
+    
+    @NotEmpty(message = "适用种子列表不能为空")
+    private List<Long> applicableTorrentIds;
+    
+    @AssertTrue(message = "结束时间必须晚于开始时间")
+    public boolean isEndTimeAfterStartTime() {
+        return endTime != null && startTime != null && endTime.isAfter(startTime);
+    }
+} 
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/dto/TorrentUpdateDTO.java b/src/main/java/com/example/myproject/dto/TorrentUpdateDTO.java
new file mode 100644
index 0000000..4b09321
--- /dev/null
+++ b/src/main/java/com/example/myproject/dto/TorrentUpdateDTO.java
@@ -0,0 +1,24 @@
+package com.example.myproject.dto;
+
+import lombok.Data;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+
+@Data
+public class TorrentUpdateDTO {
+    @NotBlank(message = "标题不能为空")
+    @Size(max = 30, message = "名称长度不能超过30个字符")
+    private String title;
+    
+    @Size(max = 1000, message = "描述长度不能超过1000个字符")
+    private String description;
+    
+    @NotBlank(message = "分类不能为空")
+    private String category;
+    
+
+    private String tags;
+    @NotBlank(message = "封面不能为空")
+    
+    private String imageUrl;
+} 
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/dto/param/TorrentParam.java b/src/main/java/com/example/myproject/dto/param/TorrentParam.java
new file mode 100644
index 0000000..1ef832b
--- /dev/null
+++ b/src/main/java/com/example/myproject/dto/param/TorrentParam.java
@@ -0,0 +1,43 @@
+package com.example.myproject.dto.param;
+
+import com.example.myproject.common.base.OrderPageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * 种子查询参数
+ */
+@Data
+@Schema(description = "种子查询参数")
+public class TorrentParam extends OrderPageParam {
+
+    @Schema(description = "关键字")
+    private String keyword;
+
+    @Schema(description = "分类")
+    private String category;
+
+    @Schema(description = "促销种子")
+    private String free;
+
+    private Set<String> likeExpressions;
+
+    public void buildLike() {
+        likeExpressions = new LinkedHashSet<>();
+        if (StringUtils.isEmpty(keyword)) {
+            return;
+        }
+        keyword = keyword.replace(".", " ");
+        String[] searchstrExploded = keyword.split(" ");
+        for (int i = 0; i < searchstrExploded.length && i < 10; i++) {
+            String searchstrElement = searchstrExploded[i].trim();
+            if (!searchstrElement.isEmpty()) {
+                likeExpressions.add(searchstrElement);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/dto/param/TorrentUploadParam.java b/src/main/java/com/example/myproject/dto/param/TorrentUploadParam.java
new file mode 100644
index 0000000..6ede468
--- /dev/null
+++ b/src/main/java/com/example/myproject/dto/param/TorrentUploadParam.java
@@ -0,0 +1,28 @@
+package com.example.myproject.dto.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@Schema(description = "种子上传参数")
+public class TorrentUploadParam {
+    @Schema(description = "上传者")
+    private String uploader;
+
+    @Schema(description = "种子标题")
+    private String title;
+
+    @Schema(description = "种子描述")
+    private String description;
+
+    @Schema(description = "种子标签")
+    private String tags;
+
+    @Schema(description = "种子分类")
+    private String category;
+
+    @Schema(description = "种子封面图 URL")
+    private String imageUrl;
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/dto/vo/TorrentVO.java b/src/main/java/com/example/myproject/dto/vo/TorrentVO.java
new file mode 100644
index 0000000..e723cb3
--- /dev/null
+++ b/src/main/java/com/example/myproject/dto/vo/TorrentVO.java
@@ -0,0 +1,105 @@
+package com.example.myproject.dto.vo;
+
+import java.time.LocalDateTime;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import lombok.Data;
+
+@Data
+public class TorrentVO {
+    /**
+     * 种子ID
+     */
+    @Schema(description = "种子ID")
+    private Long id;
+
+    /**
+     * 标题
+     */
+    @NotEmpty
+    @Schema(description = "标题")
+    private String title;
+
+
+    /**
+     * 封面
+     */
+    @Schema(description = "封面")
+    private String imageUrl;
+    /**
+     * 描述
+     */
+    @NotEmpty
+    @Schema(description = "描述")
+    private String description;
+
+    /**
+     * 类别
+     */
+    @NotNull
+    @Schema(description = "类别")
+    private String category;
+
+
+    /**
+     * 添加日期
+     */
+    @Schema(description = "添加日期")
+    private LocalDateTime createTime;
+
+    /**
+     * 修改日期
+     */
+    @Schema(description = "修改日期")
+    private LocalDateTime updateTime;
+
+    /**
+     * 上传者
+     */
+    @Schema(description = "上传者")
+    private String uploader;
+    /**
+     * 文件大小
+     */
+    @Schema(description = "文件大小")
+    private Long size;
+   
+
+
+    /**
+     * 评论数
+     */
+    @Schema(description = "评论数")
+    private Integer comments;
+    /**
+     * 浏览次数
+     */
+    @Schema(description = "浏览次数")
+    private Integer views;
+    /**
+     * 点击次数
+     */
+    @Schema(description = "点击次数")
+    private Integer hits;
+
+
+    /**
+     * 下载数
+     */
+    @Schema(description = "下载数")
+    private Integer leechers;
+    /**
+     * 做种数
+     */
+    @Schema(description = "做种数")
+    private Integer seeders;
+
+    /**
+     * 完成次数
+     */
+    @Schema(description = "完成次数")
+    private Integer completions;
+
+}
diff --git a/src/main/java/com/example/myproject/entity/EntityBase.java b/src/main/java/com/example/myproject/entity/EntityBase.java
new file mode 100644
index 0000000..48583cb
--- /dev/null
+++ b/src/main/java/com/example/myproject/entity/EntityBase.java
@@ -0,0 +1,61 @@
+package com.example.myproject.entity;
+
+
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+
+import java.util.Objects;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Base class for an entity, as explained in the book "Domain Driven Design".
+ * All entities in this project have an identity attribute with type Long and
+ * name id. Inspired by the DDD Sample project.
+
+ */
+@Setter
+@Getter
+public abstract class EntityBase {
+
+    /**
+     * This identity field has the wrapper class type Long so that an entity which
+     * has not been saved is recognizable by a null identity.
+     */
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @Override
+    public boolean equals(final Object object) {
+        if (!(object instanceof EntityBase)) {
+            return false;
+        }
+        if (!getClass().equals(object.getClass())) {
+            return false;
+        }
+        final EntityBase that = (EntityBase) object;
+        _checkIdentity(this);
+        _checkIdentity(that);
+        return this.id.equals(that.getId());
+    }
+
+
+    private void _checkIdentity(final EntityBase entity) {
+        if (entity.getId() == null) {
+            throw new IllegalStateException("Comparison identity missing in entity: " + entity);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(this.getId());
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getSimpleName() + "<" + getId() + ">";
+    }
+
+}
diff --git a/src/main/java/com/example/myproject/entity/FavoriteEntity.java b/src/main/java/com/example/myproject/entity/FavoriteEntity.java
new file mode 100644
index 0000000..3f76cfe
--- /dev/null
+++ b/src/main/java/com/example/myproject/entity/FavoriteEntity.java
@@ -0,0 +1,36 @@
+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("favorite")
+@ApiModel("收藏实体类")
+public class FavoriteEntity {
+    @TableId(type = IdType.AUTO)
+    @ApiModelProperty(value = "收藏ID", example = "1")
+    private Long id;
+
+    @ApiModelProperty(value = "用户ID", example = "1001")
+    @JsonProperty("userId")
+    @TableField("user_id")
+    private Long userId;
+
+    @ApiModelProperty(value = "种子ID", example = "2001")
+    @JsonProperty("seedId")
+    @TableField("seed_id")
+    private Long seedId;
+
+    @ApiModelProperty(value = "收藏时间", example = "2024-05-13 12:00:00")
+    @JsonProperty("createTime")
+    @TableField("create_time")
+    private Date createTime;
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/entity/Promotion.java b/src/main/java/com/example/myproject/entity/Promotion.java
new file mode 100644
index 0000000..4ca846b
--- /dev/null
+++ b/src/main/java/com/example/myproject/entity/Promotion.java
@@ -0,0 +1,34 @@
+package com.example.myproject.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+@TableName("promotion")
+public class Promotion {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    
+    private String name;
+    
+    private String description;
+    
+    private LocalDateTime startTime;
+    
+    private LocalDateTime endTime;
+    
+    private double discountPercentage;
+    
+    private String applicableTorrentIds;
+    
+    private LocalDateTime createTime;
+    
+    private LocalDateTime updateTime;
+    
+    private Boolean isDeleted;
+} 
\ 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
new file mode 100644
index 0000000..f62fb76
--- /dev/null
+++ b/src/main/java/com/example/myproject/entity/TorrentEntity.java
@@ -0,0 +1,108 @@
+package com.example.myproject.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+import java.util.List;
+
+@Data
+@TableName("torrent")
+@ApiModel("种子实体类")
+public class TorrentEntity {
+
+    @TableId(type = IdType.AUTO)
+    @ApiModelProperty(value = "种子ID", example = "1")
+    private Long id;
+
+    @JsonProperty("infoHash")
+    @ApiModelProperty(value = "种子信息哈希", example = "abcdef123456")
+    private String infoHash;
+
+    @JsonProperty("fileName")
+    @ApiModelProperty(value = "种子文件名", example = "movie_torrent_file.torrent")
+    private String fileName;
+
+    @JsonProperty("uploader")
+    @ApiModelProperty(value = "上传者", example = "user123")
+    private String uploader;
+
+    @JsonProperty("createdTime")
+    @ApiModelProperty(value = "上传时间", example = "2024-01-01 12:00:00")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @JsonProperty("updateTime")
+    @ApiModelProperty(value = "更新时间", example = "2024-01-01 12:00:00")
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    @JsonProperty("size")
+    @ApiModelProperty(value = "种子文件大小", example = "123456")
+    private Long size;
+
+    @JsonProperty("title")
+    @ApiModelProperty(value = "种子标题", example = "《星际穿越》")
+    private String title;
+
+    @JsonProperty("description")
+    @ApiModelProperty(value = "种子描述", example = "这是一部好看的科幻电影")
+    private String description;
+
+    @JsonProperty("tags")
+    @ApiModelProperty(value = "种子标签", example = "[\"科幻\",\"动作\"]")
+    private String tags;
+
+    @JsonProperty("category")
+    @ApiModelProperty(value = "种子分类", example = "movie")
+    private String category;
+
+    @JsonProperty("imageUrl")
+    @ApiModelProperty(value = "种子封面图URL", example = "http://example.com/images/cover.jpg")
+    private String imageUrl;
+
+    @JsonProperty("leechers")
+    @ApiModelProperty(value = "下载次数", example = "123")
+    private Integer leechers;
+
+    @JsonProperty("seeders")
+    @ApiModelProperty(value = "做种数", example = "10")
+    private Integer seeders;
+
+    @JsonProperty("comments")
+    @ApiModelProperty(value = "评论数", example = "5")
+    private Integer comments;
+
+    @JsonProperty("views")
+    @ApiModelProperty(value = "浏览次数", example = "1000")
+    private Integer views;
+
+    @JsonProperty("hits")
+    @ApiModelProperty(value = "点击次数", example = "2000")
+    private Integer hits;
+
+    @JsonProperty("promotionTimeType")
+    @ApiModelProperty(value = "促销时间类型", example = "1")
+    private Integer promotionTimeType;
+
+    @JsonProperty("promotionUntil")
+    @ApiModelProperty(value = "促销截止日期", example = "2024-12-31T23:59:59")
+    private LocalDateTime promotionUntil;
+
+    
+    @JsonProperty("torrentFile")
+    @ApiModelProperty(value = "种子文件", example = "base64 encoded torrent file")
+    private byte[] torrentFile;
+
+    @JsonProperty("isDeleted")
+    @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/User.java b/src/main/java/com/example/myproject/entity/User.java
index 20f7138..7574e67 100644
--- a/src/main/java/com/example/myproject/entity/User.java
+++ b/src/main/java/com/example/myproject/entity/User.java
@@ -9,6 +9,8 @@
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import java.time.LocalDateTime;
+
 @Data
 @TableName("user") // 指定数据库表名
 @ApiModel("用户实体类") // 用于描述模型
@@ -50,6 +52,26 @@
     @ApiModelProperty(value = "头像")
     private String avatar;
 
+    @JsonProperty("uploaded")
+    @ApiModelProperty(value = "上传量", example = "1000")
+    private Long uploaded;
+
+    @JsonProperty("downloaded")
+    @ApiModelProperty(value = "下载量", example = "500")
+    private Long downloaded;
+
+    @JsonProperty("create_time")
+    @ApiModelProperty(value = "创建时间", example = "2024-04-01T12:00:00")
+    private LocalDateTime createTime;
+
+    @JsonProperty("update_time")
+    @ApiModelProperty(value = "更新时间", example = "2024-04-01T12:00:00")
+    private LocalDateTime updateTime;
+
+    @JsonProperty("is_deleted")
+    @ApiModelProperty(value = "是否删除", example = "false")
+    private Boolean isDeleted;
+
     public User() {
     }
 }
diff --git a/src/main/java/com/example/myproject/mapper/FavoriteMapper.java b/src/main/java/com/example/myproject/mapper/FavoriteMapper.java
new file mode 100644
index 0000000..08b8151
--- /dev/null
+++ b/src/main/java/com/example/myproject/mapper/FavoriteMapper.java
@@ -0,0 +1,15 @@
+package com.example.myproject.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.example.myproject.entity.FavoriteEntity;
+import org.apache.ibatis.annotations.*;
+
+@Mapper
+public interface FavoriteMapper extends BaseMapper<FavoriteEntity> {
+
+   @Select("SELECT * FROM favorite WHERE user_id = #{userId} AND seed_id = #{seedId} LIMIT 1")
+    FavoriteEntity selectByUserIdAndSeedId(@Param("userId") Long userId, @Param("seedId") Long seedId);
+
+    @Delete("DELETE FROM favorite WHERE user_id = #{userId} AND seed_id = #{seedId}")
+    void deleteByUserIdAndSeedId(@Param("userId") Long userId, @Param("seedId") Long seedId);
+}
\ 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
new file mode 100644
index 0000000..a478835
--- /dev/null
+++ b/src/main/java/com/example/myproject/mapper/PromotionMapper.java
@@ -0,0 +1,52 @@
+package com.example.myproject.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.example.myproject.entity.Promotion;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Mapper
+public interface PromotionMapper extends BaseMapper<Promotion> {
+
+    @Select("SELECT * FROM promotion WHERE is_deleted = false " +
+            "AND start_time <= #{now} AND end_time >= #{now}")
+    List<Promotion> findActivePromotions(@Param("now") LocalDateTime now);
+
+    /**
+     * 查找某个torrentId是否在促销活动的applicable_torrent_ids字符串中
+     * 用MySQL的FIND_IN_SET判断
+     */
+    @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")
+    List<Promotion> findActivePromotionsForTorrent(
+            @Param("torrentId") Long torrentId,
+            @Param("now") LocalDateTime now);
+
+    /**
+     * 校验种子id是否存在,返回计数,0代表不存在,>0代表存在
+     */
+    @Select("SELECT COUNT(*) FROM torrent WHERE id = #{torrentId}")
+    int checkTorrentExists(@Param("torrentId") Long torrentId);
+
+    /**
+     * 插入促销活动
+     */
+//    int insert(Promotion promotion);
+
+    /**
+     * 根据ID更新促销活动(例如软删除)
+     */
+    int updateById(Promotion promotion);
+
+    /**
+     * 根据ID查询促销活动
+     */
+    Promotion selectById(@Param("id") Long id);
+}
diff --git a/src/main/java/com/example/myproject/mapper/TorrentMapper.java b/src/main/java/com/example/myproject/mapper/TorrentMapper.java
new file mode 100644
index 0000000..f9cdcdc
--- /dev/null
+++ b/src/main/java/com/example/myproject/mapper/TorrentMapper.java
@@ -0,0 +1,32 @@
+package com.example.myproject.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.example.myproject.dto.param.TorrentParam;
+import com.example.myproject.entity.TorrentEntity;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+import java.util.List;
+
+@Mapper
+public interface TorrentMapper extends BaseMapper<TorrentEntity> {
+
+    @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);
+}
\ 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 a070bb5..3a29b64 100644
--- a/src/main/java/com/example/myproject/mapper/UserMapper.java
+++ b/src/main/java/com/example/myproject/mapper/UserMapper.java
@@ -4,6 +4,7 @@
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
 import org.springframework.data.repository.query.Param;
 
 import java.util.List;
@@ -22,4 +23,9 @@
     // 根据用户名包含查找用户
     List<User> selectByUsernameContaining(@Param("name") String name);
 
+    @Update("UPDATE user SET downloaded = downloaded + #{size} WHERE id = #{userId}")
+    void increaseDownloaded(@Param("userId") Long userId, @Param("size") double size);
+    
+    boolean hasRole(@Param("userId") Long userId, @Param("role") String role);
+
 }
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/service/PromotionService.java b/src/main/java/com/example/myproject/service/PromotionService.java
new file mode 100644
index 0000000..087c13b
--- /dev/null
+++ b/src/main/java/com/example/myproject/service/PromotionService.java
@@ -0,0 +1,17 @@
+package com.example.myproject.service;
+
+import com.example.myproject.entity.Promotion;
+import com.example.myproject.dto.PromotionCreateDTO;
+import java.util.List;
+
+public interface PromotionService {
+    Promotion createPromotion(PromotionCreateDTO promotionDTO);
+    
+    List<Promotion> getAllActivePromotions();
+    
+    Promotion getPromotionById(Long promotionId);
+    
+    void deletePromotion(Long promotionId);
+    
+    double getCurrentDiscount(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
new file mode 100644
index 0000000..941804d
--- /dev/null
+++ b/src/main/java/com/example/myproject/service/TorrentService.java
@@ -0,0 +1,37 @@
+package com.example.myproject.service;
+
+import com.example.myproject.common.base.Result;
+import com.example.myproject.entity.TorrentEntity;
+import com.example.myproject.dto.param.TorrentParam;
+import com.example.myproject.dto.param.TorrentUploadParam;
+import com.example.myproject.dto.TorrentUpdateDTO;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.List;
+
+public interface TorrentService {
+    List<TorrentEntity> search(TorrentParam param);
+    
+    TorrentEntity selectBySeedId(Long seedId);
+    
+    void uploadTorrent(MultipartFile file, TorrentUploadParam param) throws IOException;
+    
+    byte[] fetch(Long seedId, String passkey) throws IOException;
+    
+    Result favorite(Long seedId, Long userId);
+    
+    void deleteTorrent(Long seedId);
+    
+    void updateTorrent(Long seedId, TorrentUpdateDTO updateDTO);
+    
+    boolean canUserDeleteTorrent(Long seedId, Long userId);
+    
+    boolean canUserUpdateTorrent(Long seedId, Long userId);
+    
+    boolean checkUserUploadRatio(Long userId);
+    
+    double calculateDownloadSize(Long torrentId, Long userId);
+    
+    void recordDownload(Long torrentId, Long userId, double downloadSize);
+}
diff --git a/src/main/java/com/example/myproject/service/UserService.java b/src/main/java/com/example/myproject/service/UserService.java
index 535f635..71435c7 100644
--- a/src/main/java/com/example/myproject/service/UserService.java
+++ b/src/main/java/com/example/myproject/service/UserService.java
@@ -21,4 +21,5 @@
     boolean checkPassword(Long userId, String rawPassword);
 
 
+//    Integer getUserId();
 }
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/service/serviceImpl/PromotionServiceImpl.java b/src/main/java/com/example/myproject/service/serviceImpl/PromotionServiceImpl.java
new file mode 100644
index 0000000..9d34cbc
--- /dev/null
+++ b/src/main/java/com/example/myproject/service/serviceImpl/PromotionServiceImpl.java
@@ -0,0 +1,114 @@
+package com.example.myproject.service.serviceImpl;
+
+import com.example.myproject.entity.Promotion;
+import com.example.myproject.mapper.PromotionMapper;
+import com.example.myproject.service.PromotionService;
+import com.example.myproject.dto.PromotionCreateDTO;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class PromotionServiceImpl implements PromotionService {
+
+    @Autowired
+    private PromotionMapper promotionMapper;
+
+    @Override
+    @Transactional
+    public Promotion createPromotion(PromotionCreateDTO promotionDTO) {
+        // 验证时间
+        LocalDateTime now = LocalDateTime.now();
+        if (promotionDTO.getEndTime().isBefore(now)) {
+            throw new RuntimeException("结束时间不能早于当前时间");
+        }
+
+        // 验证种子ID是否存在
+        validateTorrentIds(promotionDTO.getApplicableTorrentIds());
+
+        // 创建促销活动
+        Promotion promotion = new Promotion();
+        promotion.setName(promotionDTO.getName());
+        promotion.setDescription(promotionDTO.getDescription());
+        promotion.setStartTime(promotionDTO.getStartTime());
+        promotion.setEndTime(promotionDTO.getEndTime());
+        promotion.setDiscountPercentage(promotionDTO.getDiscountPercentage());
+
+        // 把List<Long>转换成逗号分隔字符串
+        String applicableTorrentIdsStr = promotionDTO.getApplicableTorrentIds().stream()
+                .map(String::valueOf)
+                .collect(Collectors.joining(","));
+        promotion.setApplicableTorrentIds(applicableTorrentIdsStr);
+
+        promotion.setCreateTime(now);
+        promotion.setUpdateTime(now);
+        promotion.setIsDeleted(false);
+
+        promotionMapper.insert(promotion);
+        return promotion;
+    }
+
+    @Override
+    public List<Promotion> getAllActivePromotions() {
+        LocalDateTime now = LocalDateTime.now();
+        return promotionMapper.findActivePromotions(now);
+    }
+
+    @Override
+    public Promotion getPromotionById(Long promotionId) {
+        Promotion promotion = promotionMapper.selectById(promotionId);
+        if (promotion == null || promotion.getIsDeleted()) {
+            return null;
+        }
+        return promotion;
+    }
+
+    @Override
+    @Transactional
+    public void deletePromotion(Long promotionId) {
+        Promotion promotion = getPromotionById(promotionId);
+        if (promotion == null) {
+            throw new RuntimeException("促销活动不存在");
+        }
+
+        // 软删除
+        promotion.setIsDeleted(true);
+        promotion.setUpdateTime(LocalDateTime.now());
+        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);
+    }
+
+    /**
+     * 验证种子ID是否存在
+     */
+    private void validateTorrentIds(List<Long> torrentIds) {
+        if (torrentIds == null || torrentIds.isEmpty()) {
+            throw new RuntimeException("适用种子列表不能为空");
+        }
+
+        // 检查所有种子ID是否都存在
+        List<Long> invalidIds = torrentIds.stream()
+                .filter(id -> promotionMapper.checkTorrentExists(id) == 0)  // 改成 == 0
+                .collect(Collectors.toList());
+
+        if (!invalidIds.isEmpty()) {
+            throw new RuntimeException("以下种子ID不存在: " + invalidIds);
+        }
+    }
+}
diff --git a/src/main/java/com/example/myproject/service/serviceImpl/TorrentServiceImpl.java b/src/main/java/com/example/myproject/service/serviceImpl/TorrentServiceImpl.java
new file mode 100644
index 0000000..884c65d
--- /dev/null
+++ b/src/main/java/com/example/myproject/service/serviceImpl/TorrentServiceImpl.java
@@ -0,0 +1,296 @@
+package com.example.myproject.service.serviceImpl;
+
+import com.example.myproject.entity.TorrentEntity;
+import com.example.myproject.entity.User;
+import com.example.myproject.mapper.TorrentMapper;
+import com.example.myproject.mapper.UserMapper;
+import com.example.myproject.service.TorrentService;
+import com.example.myproject.service.PromotionService;
+import com.example.myproject.dto.param.TorrentParam;
+import com.example.myproject.dto.param.TorrentUploadParam;
+import com.example.myproject.dto.TorrentUpdateDTO;
+import com.turn.ttorrent.bcodec.BDecoder;
+import com.turn.ttorrent.bcodec.BEValue;
+import com.turn.ttorrent.bcodec.BEncoder;
+import com.turn.ttorrent.client.SimpleClient;
+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 com.turn.ttorrent.tracker.TrackedTorrent;
+import org.apache.commons.codec.binary.Hex;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.ByteArrayInputStream;
+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.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+@Service
+public class TorrentServiceImpl implements TorrentService {
+    @Autowired
+    private Tracker tracker;
+
+    @Autowired
+    private TorrentMapper torrentMapper;
+
+    private final Map<String, TrackedTorrent> torrentRegistry = new HashMap<>();
+
+
+    @Autowired
+    private UserMapper userMapper;
+
+    @Autowired
+    private PromotionService promotionService;
+
+    private static final double MIN_UPLOAD_RATIO = 0.5; // 最小上传比例要求
+
+    @Override
+    public List<TorrentEntity> search(TorrentParam param) {
+        return torrentMapper.search(param);
+    }
+
+    @Override
+    public TorrentEntity selectBySeedId(Long seedId) {
+        return torrentMapper.selectById(seedId);
+    }
+    private final ExecutorService seederExecutor = Executors.newCachedThreadPool();
+
+    @Override
+    @Transactional
+    public void uploadTorrent(MultipartFile file, TorrentUploadParam param) throws IOException {
+        // 验证用户权限
+        User user = userMapper.selectById(param.getUploader());
+        if (user == null) {
+            throw new RuntimeException("用户不存在");
+        }
+        String workingDir = System.getProperty("user.dir");
+        Path originalDir = Paths.get(workingDir, "data", "files");
+        Files.createDirectories(originalDir);
+        Path originalFilePath = originalDir.resolve(file.getOriginalFilename());
+        Files.copy(file.getInputStream(), originalFilePath, StandardCopyOption.REPLACE_EXISTING);
+
+        MetadataBuilder builder = new MetadataBuilder()
+                .addFile(originalFilePath.toFile(), file.getOriginalFilename()) // 添加原始文件
+                .setTracker(" ") // 设置Tracker地址
+                .setPieceLength(512 * 1024) // 分片大小512KB
+                .setComment("Generated by PT站")
+                .setCreatedBy("PT-Server");
+
+        // 处理种子文件
+        byte[] torrentBytes = file.getBytes();
+        String infoHash = null;
+        try {
+            infoHash = calculateInfoHash(torrentBytes);
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+
+        // 保存种子文件到data/torrents目录
+        Path torrentDir = Paths.get(workingDir, "data", "torrents");
+        Files.createDirectories(torrentDir);
+        Path torrentPath = torrentDir.resolve(infoHash + ".torrent");
+        Files.copy(new ByteArrayInputStream(torrentBytes), torrentPath, StandardCopyOption.REPLACE_EXISTING);
+
+        // 注册到Tracker
+        TrackedTorrent torrent = TrackedTorrent.load(torrentPath.toFile());
+        tracker.announce(torrent);
+
+
+        // 异步启动做种客户端
+        seederExecutor.submit(() -> {
+            try {
+                startSeeding(torrentPath, originalDir);
+            } catch (Exception e) {
+                Result.error("做种失败: " + e.getMessage());
+            }
+        });
+
+
+
+
+        // 保存种子信息
+        TorrentEntity entity= new TorrentEntity();
+        entity.setUploader(param.getUploader());
+        entity.setFileName(file.getOriginalFilename());
+        entity.setSize(file.getSize());
+        entity.setCategory(param.getCategory());
+        entity.setTags(param.getTags());
+        entity.setTitle(param.getTitle());
+        entity.setImageUrl(param.getImageUrl());
+        entity.setTorrentFile(torrentBytes);
+        entity.setInfoHash(infoHash);
+
+        torrentMapper.insert(entity);
+    }
+
+    @Override
+    public byte[] fetch(Long seedId, String passkey) {
+        TorrentEntity torrent = selectBySeedId(seedId);
+        if (torrent == null) {
+            throw new RuntimeException("种子不存在");
+        }
+
+        byte[] torrentBytes = torrent.getTorrentFile();
+
+        try {
+            // 1. 解码 .torrent 文件为 Map
+            Map<String, BEValue> decoded = BDecoder.bdecode(new ByteArrayInputStream(torrentBytes)).getMap();
+
+            // 2. 获取原始 announce 字段
+            String announce = decoded.get("announce").getString();
+
+            // 3. 注入 passkey 到 announce URL
+            if (!announce.contains("passkey=")) {
+                announce = announce + "?passkey=" + passkey;
+                decoded.put("announce", new BEValue(announce));
+            }
+
+            // 4. 编码成新的 .torrent 文件字节数组
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            BEncoder.bencode(decoded, out);
+            return out.toByteArray();
+
+        } catch (Exception e) {
+            throw new RuntimeException("处理 torrent 文件失败", e);
+        }
+    }
+
+    @Override
+    @Transactional
+    public Result favorite(Long seedId, Long userId) {
+        try {
+            boolean exists = torrentMapper.checkFavorite(seedId, userId);
+            if (exists) {
+                torrentMapper.removeFavorite(seedId, userId);
+                return Result.success("取消收藏成功");
+            } else {
+                torrentMapper.addFavorite(seedId, userId);
+                return Result.success("收藏成功");
+            }
+        } catch (Exception e) {
+            return Result.error("失败: " + e.getMessage());
+        }
+    }
+
+    @Override
+    @Transactional
+    public void deleteTorrent(Long seedId) {
+        torrentMapper.deleteById(seedId);
+    }
+
+    @Override
+    @Transactional
+    public void updateTorrent(Long seedId, TorrentUpdateDTO updateDTO) {
+        TorrentEntity torrent = selectBySeedId(seedId);
+        if (torrent == null) {
+            throw new RuntimeException("种子不存在");
+        }
+
+
+        torrent.setDescription(updateDTO.getDescription());
+        torrent.setCategory(updateDTO.getCategory());
+        torrent.setTitle(updateDTO.getTitle());
+        torrent.setTags(updateDTO.getTags());
+        torrent.setImageUrl(updateDTO.getImageUrl());
+
+        torrentMapper.updateById(torrent);
+    }
+
+    @Override
+    public boolean canUserDeleteTorrent(Long seedId, Long userId) {
+        TorrentEntity torrent = selectBySeedId(seedId);
+        if (torrent == null) {
+            return false;
+        }
+
+        // 检查是否是种子发布者或管理员
+        return torrent.getUploader().equals(userId) ||
+                userMapper.hasRole(userId, "admin");
+    }
+
+    @Override
+    public boolean canUserUpdateTorrent(Long seedId, Long userId) {
+        return canUserDeleteTorrent(seedId, userId);
+    }
+
+    @Override
+    public boolean checkUserUploadRatio(Long userId) {
+        User user = userMapper.selectById(userId);
+        if (user == null) {
+            return false;
+        }
+
+        // 防止除以零
+        if (user.getDownloaded() == 0) {
+            return true;
+        }
+
+        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);
+    }
+    /**
+     * 计算种子文件的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);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/myproject/utils/Result.java b/src/main/java/com/example/myproject/utils/Result.java
deleted file mode 100644
index e838a1c..0000000
--- a/src/main/java/com/example/myproject/utils/Result.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package com.example.myproject.utils;
-
-
-public class Result<T> {
-    private String code;
-    private String msg;
-    private T data;
-
-    public String getCode() {
-        return code;
-    }
-
-    public void setCode(String code) {
-        this.code = code;
-    }
-
-    public String getMsg() {
-        return msg;
-    }
-
-    public void setMsg(String msg) {
-        this.msg = msg;
-    }
-
-    public T getData() {
-        return data;
-    }
-
-    public void setData(T data) {
-        this.data = data;
-    }
-
-    public Result() {
-    }
-
-    public Result(T data) {
-        this.data = data;
-    }
-
-    public static Result success() {
-        Result result = new Result<>();
-        result.setCode("200");
-        result.setMsg("成功");
-        return result;
-    }
-
-    public static <T> Result<T> success(T data) {
-        Result<T> result = new Result<>(data);
-        result.setCode("200");
-        result.setMsg("成功");
-        return result;
-    }
-    public static <T> Result<T> success(String msg) {
-        Result result = new Result();
-        result.setCode("200");
-        result.setMsg("成功");
-        return result;
-    }
-
-    public static <T> Result<T> success(T data,String msg) {
-        Result<T> result = new Result<>(data);
-        result.setCode("200");
-        result.setMsg(msg);
-        return result;
-    }
-
-    public static Result error(String code, String msg) {
-        Result result = new Result();
-        result.setCode(code);
-        result.setMsg(msg);
-        return result;
-    }
-    /**
-     * 创建一个表示文件大小超出限制的结果对象。
-     *
-     * @return 构造的文件过大错误结果对象
-     */
-    public static Result fileTooLarge() {
-        Result result = new Result();
-        result.setCode("413");
-        result.setMsg("文件大小超出限制。");
-        return result;
-    }
-
-    /**
-     * 创建一个表示文件格式不支持的结果对象。
-     *
-     * @return 构造的文件格式错误结果对象
-     */
-    public static Result unsupportedFileType() {
-        Result result = new Result();
-        result.setCode("415");
-        result.setMsg("不支持的文件格式。");
-        return result;
-    }
-
-    /**
-     * 创建一个表示文件未找到的结果对象。
-     *
-     * @return 构造的文件未找到错误结果对象
-     */
-    public static Result fileNotFound() {
-        Result result = new Result();
-        result.setCode("404");
-        result.setMsg("文件未找到。");
-        return result;
-    }
-
-    /**
-     * 创建一个表示文件存储错误的结果对象。
-     *
-     * @param errorMsg 详细错误信息
-     * @return 构造的文件存储错误结果对象
-     */
-    public static Result fileStorageError(String errorMsg) {
-        Result result = new Result();
-        result.setCode("500");
-        result.setMsg("文件存储错误: " + errorMsg);
-        return result;
-    }
-
-    public static Result permissionDenied() {
-        Result result = new Result();
-        result.setCode("401");
-        result.setMsg("权限不足,无法执行该操作。");
-        return result;
-    }
-
-}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index b753da7..0590333 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -19,3 +19,13 @@
 spring.jpa.enabled=false
 spring.jpa.hibernate.ddl-auto=none
 spring.jpa.open-in-view=false
+
+# tracker??
+pt.tracker.port=6969
+
+pt.tracker.torrent-dir=${user.dir}/data/torrents
+
+pt.tracker.allow-foreign=false
+pt.tracker.announce-url=/custom-announce
+
+mybatis-plus.mapper-locations=classpath:/mapper/**/*.xml
diff --git a/src/main/resources/files/files.torrent b/src/main/resources/files/files.torrent
new file mode 100644
index 0000000..e04974f
--- /dev/null
+++ b/src/main/resources/files/files.torrent
@@ -0,0 +1 @@
+d8:announce22:https://tracker.byr.pt10:created by21:qBittorrent v4.5.3.1013:creation datei1747717901e4:infod5:filesld6:lengthi173e4:pathl13:valid.torrenteee4:name5:files12:piece lengthi16384e6:pieces20:/ñíèEô5ã<òûìՄŸQ¡–ûee
\ No newline at end of file
diff --git a/src/main/resources/mapper/FavoriteMapper.xml b/src/main/resources/mapper/FavoriteMapper.xml
new file mode 100644
index 0000000..1048ec2
--- /dev/null
+++ b/src/main/resources/mapper/FavoriteMapper.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.FavoriteMapper">
+ 
+</mapper>
diff --git a/src/main/resources/mapper/PromotionMapper.xml b/src/main/resources/mapper/PromotionMapper.xml
new file mode 100644
index 0000000..72ffd95
--- /dev/null
+++ b/src/main/resources/mapper/PromotionMapper.xml
@@ -0,0 +1,14 @@
+<?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.PromotionMapper">
+
+    <insert id="insert" parameterType="com.example.myproject.entity.Promotion">
+        INSERT INTO promotion (
+        name, description, start_time, end_time, discount_percentage, applicable_torrent_ids
+        ) VALUES (
+        #{name}, #{description}, #{startTime}, #{endTime}, #{discountPercentage}, #{applicableTorrentIds}
+        )
+    </insert>
+
+</mapper>
diff --git a/src/main/resources/mapper/TorrentMapper.xml b/src/main/resources/mapper/TorrentMapper.xml
new file mode 100644
index 0000000..d5f018e
--- /dev/null
+++ b/src/main/resources/mapper/TorrentMapper.xml
@@ -0,0 +1,104 @@
+<?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.TorrentMapper">
+    <resultMap id="BaseResultMap" type="com.example.myproject.entity.TorrentEntity">
+        <id column="info_hash" property="infoHash"/>
+        <result column="file_name" property="fileName"/>
+        <result column="uploader" property="uploader"/>
+        <result column="upload_time" property="uploadTime"/>
+        <result column="size" property="size"/>
+        <result column="title" property="title"/>
+        <result column="description" property="description"/>
+        <result column="category" property="category"/>
+        <result column="image_url" property="imageUrl"/>
+    </resultMap>
+
+    <select id="selectByInfoHash" resultMap="BaseResultMap">
+        SELECT * FROM torrent WHERE info_hash = #{infoHash}
+    </select>
+    <select id="selectBySeedId" resultMap="BaseResultMap">
+        SELECT * FROM torrent WHERE seed_id = #{seedId}
+    </select>
+
+
+
+
+    <update id="update" parameterType="com.example.myproject.entity.TorrentEntity">
+        UPDATE torrent
+        SET file_name = #{fileName},
+        uploader = #{uploader},
+        upload_time = #{uploadTime},
+        size = #{size},
+        title = #{title},
+        description = #{description},
+        category = #{category},
+        image_url = #{imageUrl}
+        WHERE info_hash = #{infoHash}
+    </update>
+    <select id="search" resultType="com.example.myproject.entity.TorrentEntity">
+        SELECT * FROM torrent
+        <where>
+            <if test="param.category != null">
+                AND category = #{param.category}
+            </if>
+
+            <!--        <if test="param.free != null and param.free != ''">-->
+            <!--            AND free = #{param.free}-->
+            <!--        </if>-->
+            <if test="param.free != null">
+            <choose>
+                <!-- 筛选“正在促销中”的种子 -->
+                <when test="param.free == true">
+                    AND EXISTS (
+                    SELECT 1 FROM promotion p
+                    WHERE
+                    JSON_CONTAINS(p.applicable_torrent_ids, JSON_ARRAY(t.id))
+                    AND NOW() BETWEEN p.start_time AND p.end_time
+                    AND p.is_deleted = 0
+                    )
+                </when>
+                <!-- 筛选“未在促销中”的种子 -->
+                <otherwise>
+                    AND NOT EXISTS (
+                    SELECT 1 FROM promotion p
+                    WHERE
+                    JSON_CONTAINS(p.applicable_torrent_ids, JSON_ARRAY(t.id))
+                    AND NOW() BETWEEN p.start_time AND p.end_time
+                    AND p.is_deleted = 0
+                    )
+                </otherwise>
+            </choose>
+            </if>
+
+            <if test="param.likeExpressions != null and param.likeExpressions.size > 0">
+                AND (
+                <foreach collection="param.likeExpressions" item="item" open="(" separator=" AND " close=")">
+
+                    ( title LIKE CONCAT('%', #{item}, '%') ) or ( description LIKE CONCAT('%', #{item}, '%') ) or ( tags LIKE CONCAT('%', #{item}, '%') )
+                </foreach>
+                )
+            </if>
+        </where>
+
+        <if test="param.prop != null and param.sort != null">
+            ORDER BY ${param.prop} ${param.sort}
+        </if>
+    </select>
+    <select id="checkFavorite" resultType="boolean">
+        SELECT COUNT(*) > 0
+        FROM favorite
+        WHERE seed_id = #{seedId} AND user_id = #{userId}
+    </select>
+    <insert id="addFavorite">
+        INSERT INTO favorite (seed_id, user_id)
+        VALUES (#{seedId}, #{userId})
+    </insert>
+    <delete id="removeFavorite">
+        DELETE FROM favorite
+        WHERE seed_id = #{seedId} AND user_id = #{userId}
+    </delete>
+
+
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/output/valid.torrent b/src/main/resources/output/valid.torrent
new file mode 100644
index 0000000..6a90e52
--- /dev/null
+++ b/src/main/resources/output/valid.torrent
@@ -0,0 +1 @@
+d10:created by18:qBittorrent v5.1.013:creation datei1745948995e4:infod6:lengthi22e4:name15:example.torrent12:piece lengthi16384e6:pieces20:Fnð¶)ú<Ç	ŽæÂh£tl7:privatei1eee
\ No newline at end of file
diff --git a/src/test/java/com/example/myproject/controller/TorrentControllerTest.java b/src/test/java/com/example/myproject/controller/TorrentControllerTest.java
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/test/java/com/example/myproject/controller/TorrentControllerTest.java
diff --git a/src/test/java/com/example/myproject/controller/UserControllerTest.java b/src/test/java/com/example/myproject/controller/UserControllerTest.java
index 8a09e53..d332f31 100644
--- a/src/test/java/com/example/myproject/controller/UserControllerTest.java
+++ b/src/test/java/com/example/myproject/controller/UserControllerTest.java
@@ -6,13 +6,12 @@
 import com.example.myproject.service.UserService;
 import com.example.myproject.mapper.UserMapper;
 import com.example.myproject.mapper.VerificationTokenMapper;
-import com.example.myproject.utils.Result;
+import com.example.myproject.common.base.Result;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.*;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 
diff --git a/target/classes/application.properties b/target/classes/application.properties
index b753da7..0590333 100644
--- a/target/classes/application.properties
+++ b/target/classes/application.properties
@@ -19,3 +19,13 @@
 spring.jpa.enabled=false
 spring.jpa.hibernate.ddl-auto=none
 spring.jpa.open-in-view=false
+
+# tracker??
+pt.tracker.port=6969
+
+pt.tracker.torrent-dir=${user.dir}/data/torrents
+
+pt.tracker.allow-foreign=false
+pt.tracker.announce-url=/custom-announce
+
+mybatis-plus.mapper-locations=classpath:/mapper/**/*.xml
diff --git a/target/classes/com/example/myproject/MyProjectApplication.class b/target/classes/com/example/myproject/MyProjectApplication.class
index 1ebd349..a1281a9 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/common/CommonResultStatus.class b/target/classes/com/example/myproject/common/CommonResultStatus.class
new file mode 100644
index 0000000..a330e2c
--- /dev/null
+++ b/target/classes/com/example/myproject/common/CommonResultStatus.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/Constants$Announce.class b/target/classes/com/example/myproject/common/Constants$Announce.class
new file mode 100644
index 0000000..3e02afe
--- /dev/null
+++ b/target/classes/com/example/myproject/common/Constants$Announce.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/Constants$FinishStatus.class b/target/classes/com/example/myproject/common/Constants$FinishStatus.class
new file mode 100644
index 0000000..271898f
--- /dev/null
+++ b/target/classes/com/example/myproject/common/Constants$FinishStatus.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/Constants$Order.class b/target/classes/com/example/myproject/common/Constants$Order.class
new file mode 100644
index 0000000..a4b08ca
--- /dev/null
+++ b/target/classes/com/example/myproject/common/Constants$Order.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/Constants$Source.class b/target/classes/com/example/myproject/common/Constants$Source.class
new file mode 100644
index 0000000..afcbfed
--- /dev/null
+++ b/target/classes/com/example/myproject/common/Constants$Source.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/Constants.class b/target/classes/com/example/myproject/common/Constants.class
new file mode 100644
index 0000000..edce466
--- /dev/null
+++ b/target/classes/com/example/myproject/common/Constants.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/ResultStatus.class b/target/classes/com/example/myproject/common/ResultStatus.class
new file mode 100644
index 0000000..d3a6700
--- /dev/null
+++ b/target/classes/com/example/myproject/common/ResultStatus.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/base/I18nMessage.class b/target/classes/com/example/myproject/common/base/I18nMessage.class
new file mode 100644
index 0000000..c4ae319
--- /dev/null
+++ b/target/classes/com/example/myproject/common/base/I18nMessage.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/base/OrderPageParam.class b/target/classes/com/example/myproject/common/base/OrderPageParam.class
new file mode 100644
index 0000000..032ec40
--- /dev/null
+++ b/target/classes/com/example/myproject/common/base/OrderPageParam.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/base/PageParam.class b/target/classes/com/example/myproject/common/base/PageParam.class
new file mode 100644
index 0000000..2285fc1
--- /dev/null
+++ b/target/classes/com/example/myproject/common/base/PageParam.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/base/PageUtil.class b/target/classes/com/example/myproject/common/base/PageUtil.class
new file mode 100644
index 0000000..818080e
--- /dev/null
+++ b/target/classes/com/example/myproject/common/base/PageUtil.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/base/ResPage.class b/target/classes/com/example/myproject/common/base/ResPage.class
new file mode 100644
index 0000000..e208933
--- /dev/null
+++ b/target/classes/com/example/myproject/common/base/ResPage.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/base/Result.class b/target/classes/com/example/myproject/common/base/Result.class
new file mode 100644
index 0000000..8492dd1
--- /dev/null
+++ b/target/classes/com/example/myproject/common/base/Result.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/base/Status.class b/target/classes/com/example/myproject/common/base/Status.class
new file mode 100644
index 0000000..e7f372b
--- /dev/null
+++ b/target/classes/com/example/myproject/common/base/Status.class
Binary files differ
diff --git a/target/classes/com/example/myproject/common/exception/RocketPTException.class b/target/classes/com/example/myproject/common/exception/RocketPTException.class
new file mode 100644
index 0000000..89a4931
--- /dev/null
+++ b/target/classes/com/example/myproject/common/exception/RocketPTException.class
Binary files differ
diff --git a/target/classes/com/example/myproject/config/MyMetaObjectHandler.class b/target/classes/com/example/myproject/config/MyMetaObjectHandler.class
new file mode 100644
index 0000000..c213aff
--- /dev/null
+++ b/target/classes/com/example/myproject/config/MyMetaObjectHandler.class
Binary files differ
diff --git a/target/classes/com/example/myproject/config/TrackerConfig.class b/target/classes/com/example/myproject/config/TrackerConfig.class
new file mode 100644
index 0000000..774aa27
--- /dev/null
+++ b/target/classes/com/example/myproject/config/TrackerConfig.class
Binary files differ
diff --git a/target/classes/com/example/myproject/controller/TorrentController.class b/target/classes/com/example/myproject/controller/TorrentController.class
new file mode 100644
index 0000000..130ba3f
--- /dev/null
+++ b/target/classes/com/example/myproject/controller/TorrentController.class
Binary files differ
diff --git a/target/classes/com/example/myproject/controller/UserController$EmailRequest.class b/target/classes/com/example/myproject/controller/UserController$EmailRequest.class
index d12dede..04ffd85 100644
--- a/target/classes/com/example/myproject/controller/UserController$EmailRequest.class
+++ b/target/classes/com/example/myproject/controller/UserController$EmailRequest.class
Binary files differ
diff --git a/target/classes/com/example/myproject/controller/UserController$VerificationRequest.class b/target/classes/com/example/myproject/controller/UserController$VerificationRequest.class
index aa1371e..272505e 100644
--- a/target/classes/com/example/myproject/controller/UserController$VerificationRequest.class
+++ b/target/classes/com/example/myproject/controller/UserController$VerificationRequest.class
Binary files differ
diff --git a/target/classes/com/example/myproject/controller/UserController.class b/target/classes/com/example/myproject/controller/UserController.class
index d1d685c..cd1ab13 100644
--- a/target/classes/com/example/myproject/controller/UserController.class
+++ b/target/classes/com/example/myproject/controller/UserController.class
Binary files differ
diff --git a/target/classes/com/example/myproject/dto/PromotionCreateDTO.class b/target/classes/com/example/myproject/dto/PromotionCreateDTO.class
new file mode 100644
index 0000000..3087d39
--- /dev/null
+++ b/target/classes/com/example/myproject/dto/PromotionCreateDTO.class
Binary files differ
diff --git a/target/classes/com/example/myproject/dto/TorrentUpdateDTO.class b/target/classes/com/example/myproject/dto/TorrentUpdateDTO.class
new file mode 100644
index 0000000..7285868
--- /dev/null
+++ b/target/classes/com/example/myproject/dto/TorrentUpdateDTO.class
Binary files differ
diff --git a/target/classes/com/example/myproject/dto/param/TorrentParam.class b/target/classes/com/example/myproject/dto/param/TorrentParam.class
new file mode 100644
index 0000000..ad8a2c5
--- /dev/null
+++ b/target/classes/com/example/myproject/dto/param/TorrentParam.class
Binary files differ
diff --git a/target/classes/com/example/myproject/dto/param/TorrentUploadParam.class b/target/classes/com/example/myproject/dto/param/TorrentUploadParam.class
new file mode 100644
index 0000000..49bbc09
--- /dev/null
+++ b/target/classes/com/example/myproject/dto/param/TorrentUploadParam.class
Binary files differ
diff --git a/target/classes/com/example/myproject/dto/vo/TorrentVO.class b/target/classes/com/example/myproject/dto/vo/TorrentVO.class
new file mode 100644
index 0000000..fa2124e
--- /dev/null
+++ b/target/classes/com/example/myproject/dto/vo/TorrentVO.class
Binary files differ
diff --git a/target/classes/com/example/myproject/entity/EntityBase.class b/target/classes/com/example/myproject/entity/EntityBase.class
new file mode 100644
index 0000000..a16c52a
--- /dev/null
+++ b/target/classes/com/example/myproject/entity/EntityBase.class
Binary files differ
diff --git a/target/classes/com/example/myproject/entity/FavoriteEntity.class b/target/classes/com/example/myproject/entity/FavoriteEntity.class
new file mode 100644
index 0000000..a0acd8b
--- /dev/null
+++ b/target/classes/com/example/myproject/entity/FavoriteEntity.class
Binary files differ
diff --git a/target/classes/com/example/myproject/entity/Promotion.class b/target/classes/com/example/myproject/entity/Promotion.class
new file mode 100644
index 0000000..e5df6dd
--- /dev/null
+++ b/target/classes/com/example/myproject/entity/Promotion.class
Binary files differ
diff --git a/target/classes/com/example/myproject/entity/TorrentEntity.class b/target/classes/com/example/myproject/entity/TorrentEntity.class
new file mode 100644
index 0000000..cdf20da
--- /dev/null
+++ b/target/classes/com/example/myproject/entity/TorrentEntity.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 62eb5a1..3ee8ffe 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/FavoriteMapper.class b/target/classes/com/example/myproject/mapper/FavoriteMapper.class
new file mode 100644
index 0000000..dc075c9
--- /dev/null
+++ b/target/classes/com/example/myproject/mapper/FavoriteMapper.class
Binary files differ
diff --git a/target/classes/com/example/myproject/mapper/PromotionMapper.class b/target/classes/com/example/myproject/mapper/PromotionMapper.class
new file mode 100644
index 0000000..074c856
--- /dev/null
+++ b/target/classes/com/example/myproject/mapper/PromotionMapper.class
Binary files differ
diff --git a/target/classes/com/example/myproject/mapper/TorrentMapper.class b/target/classes/com/example/myproject/mapper/TorrentMapper.class
new file mode 100644
index 0000000..5080dfd
--- /dev/null
+++ 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 7b80316..10a9ec1 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/service/PromotionService.class b/target/classes/com/example/myproject/service/PromotionService.class
new file mode 100644
index 0000000..6f0963b
--- /dev/null
+++ 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
new file mode 100644
index 0000000..c0cfeed
--- /dev/null
+++ b/target/classes/com/example/myproject/service/TorrentService.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
new file mode 100644
index 0000000..6984839
--- /dev/null
+++ 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
new file mode 100644
index 0000000..dc9600d
--- /dev/null
+++ b/target/classes/com/example/myproject/service/serviceImpl/TorrentServiceImpl.class
Binary files differ
diff --git a/target/classes/com/example/myproject/utils/Result.class b/target/classes/com/example/myproject/utils/Result.class
deleted file mode 100644
index 0a8e59e..0000000
--- a/target/classes/com/example/myproject/utils/Result.class
+++ /dev/null
Binary files differ
diff --git a/target/classes/files/files.torrent b/target/classes/files/files.torrent
new file mode 100644
index 0000000..e04974f
--- /dev/null
+++ b/target/classes/files/files.torrent
@@ -0,0 +1 @@
+d8:announce22:https://tracker.byr.pt10:created by21:qBittorrent v4.5.3.1013:creation datei1747717901e4:infod5:filesld6:lengthi173e4:pathl13:valid.torrenteee4:name5:files12:piece lengthi16384e6:pieces20:/ñíèEô5ã<òûìՄŸQ¡–ûee
\ No newline at end of file
diff --git a/target/classes/mapper/FavoriteMapper.xml b/target/classes/mapper/FavoriteMapper.xml
new file mode 100644
index 0000000..1048ec2
--- /dev/null
+++ b/target/classes/mapper/FavoriteMapper.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.FavoriteMapper">
+ 
+</mapper>
diff --git a/target/classes/mapper/PromotionMapper.xml b/target/classes/mapper/PromotionMapper.xml
new file mode 100644
index 0000000..72ffd95
--- /dev/null
+++ b/target/classes/mapper/PromotionMapper.xml
@@ -0,0 +1,14 @@
+<?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.PromotionMapper">
+
+    <insert id="insert" parameterType="com.example.myproject.entity.Promotion">
+        INSERT INTO promotion (
+        name, description, start_time, end_time, discount_percentage, applicable_torrent_ids
+        ) VALUES (
+        #{name}, #{description}, #{startTime}, #{endTime}, #{discountPercentage}, #{applicableTorrentIds}
+        )
+    </insert>
+
+</mapper>
diff --git a/target/classes/mapper/TorrentMapper.xml b/target/classes/mapper/TorrentMapper.xml
new file mode 100644
index 0000000..d5f018e
--- /dev/null
+++ b/target/classes/mapper/TorrentMapper.xml
@@ -0,0 +1,104 @@
+<?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.TorrentMapper">
+    <resultMap id="BaseResultMap" type="com.example.myproject.entity.TorrentEntity">
+        <id column="info_hash" property="infoHash"/>
+        <result column="file_name" property="fileName"/>
+        <result column="uploader" property="uploader"/>
+        <result column="upload_time" property="uploadTime"/>
+        <result column="size" property="size"/>
+        <result column="title" property="title"/>
+        <result column="description" property="description"/>
+        <result column="category" property="category"/>
+        <result column="image_url" property="imageUrl"/>
+    </resultMap>
+
+    <select id="selectByInfoHash" resultMap="BaseResultMap">
+        SELECT * FROM torrent WHERE info_hash = #{infoHash}
+    </select>
+    <select id="selectBySeedId" resultMap="BaseResultMap">
+        SELECT * FROM torrent WHERE seed_id = #{seedId}
+    </select>
+
+
+
+
+    <update id="update" parameterType="com.example.myproject.entity.TorrentEntity">
+        UPDATE torrent
+        SET file_name = #{fileName},
+        uploader = #{uploader},
+        upload_time = #{uploadTime},
+        size = #{size},
+        title = #{title},
+        description = #{description},
+        category = #{category},
+        image_url = #{imageUrl}
+        WHERE info_hash = #{infoHash}
+    </update>
+    <select id="search" resultType="com.example.myproject.entity.TorrentEntity">
+        SELECT * FROM torrent
+        <where>
+            <if test="param.category != null">
+                AND category = #{param.category}
+            </if>
+
+            <!--        <if test="param.free != null and param.free != ''">-->
+            <!--            AND free = #{param.free}-->
+            <!--        </if>-->
+            <if test="param.free != null">
+            <choose>
+                <!-- 筛选“正在促销中”的种子 -->
+                <when test="param.free == true">
+                    AND EXISTS (
+                    SELECT 1 FROM promotion p
+                    WHERE
+                    JSON_CONTAINS(p.applicable_torrent_ids, JSON_ARRAY(t.id))
+                    AND NOW() BETWEEN p.start_time AND p.end_time
+                    AND p.is_deleted = 0
+                    )
+                </when>
+                <!-- 筛选“未在促销中”的种子 -->
+                <otherwise>
+                    AND NOT EXISTS (
+                    SELECT 1 FROM promotion p
+                    WHERE
+                    JSON_CONTAINS(p.applicable_torrent_ids, JSON_ARRAY(t.id))
+                    AND NOW() BETWEEN p.start_time AND p.end_time
+                    AND p.is_deleted = 0
+                    )
+                </otherwise>
+            </choose>
+            </if>
+
+            <if test="param.likeExpressions != null and param.likeExpressions.size > 0">
+                AND (
+                <foreach collection="param.likeExpressions" item="item" open="(" separator=" AND " close=")">
+
+                    ( title LIKE CONCAT('%', #{item}, '%') ) or ( description LIKE CONCAT('%', #{item}, '%') ) or ( tags LIKE CONCAT('%', #{item}, '%') )
+                </foreach>
+                )
+            </if>
+        </where>
+
+        <if test="param.prop != null and param.sort != null">
+            ORDER BY ${param.prop} ${param.sort}
+        </if>
+    </select>
+    <select id="checkFavorite" resultType="boolean">
+        SELECT COUNT(*) > 0
+        FROM favorite
+        WHERE seed_id = #{seedId} AND user_id = #{userId}
+    </select>
+    <insert id="addFavorite">
+        INSERT INTO favorite (seed_id, user_id)
+        VALUES (#{seedId}, #{userId})
+    </insert>
+    <delete id="removeFavorite">
+        DELETE FROM favorite
+        WHERE seed_id = #{seedId} AND user_id = #{userId}
+    </delete>
+
+
+</mapper>
\ No newline at end of file
diff --git a/target/classes/mapper/UserMapper.xml b/target/classes/mapper/UserMapper.xml
new file mode 100644
index 0000000..f03ab0a
--- /dev/null
+++ b/target/classes/mapper/UserMapper.xml
@@ -0,0 +1,21 @@
+<?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.UserMapper">
+    <!-- 根据用户名查找用户 -->
+    <select id="selectByUsername" parameterType="string" resultType="com.example.myproject.entity.User">
+        SELECT * FROM user WHERE username = #{username}
+    </select>
+    <!-- 根据用户名和密码查找用户 -->
+    <select id="selectByUsernameAndPassword" parameterType="map" resultType="com.example.myproject.entity.User">
+        SELECT * FROM user WHERE username = #{username} AND password = #{password}
+    </select>
+    <!-- 根据邮箱查找用户 -->
+    <select id="selectByEmail" parameterType="string" resultType="com.example.myproject.entity.User">
+        SELECT * FROM user WHERE email = #{email}
+    </select>
+    <!-- 根据用户名包含查找用户 -->
+    <select id="selectByUsernameContaining" parameterType="string" resultType="com.example.myproject.entity.User">
+        SELECT * FROM user WHERE username LIKE CONCAT('%', #{name}, '%')
+    </select>
+</mapper>
diff --git a/target/classes/mapper/VerificationTokenMapper.xml b/target/classes/mapper/VerificationTokenMapper.xml
new file mode 100644
index 0000000..53b19a5
--- /dev/null
+++ b/target/classes/mapper/VerificationTokenMapper.xml
@@ -0,0 +1,10 @@
+<?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.VerificationTokenMapper">
+    <!-- 通过 token 和 email 查询 VerificationToken -->
+    <select id="findByTokenAndEmail" resultType="com.example.myproject.entity.VerificationToken">
+        SELECT * FROM verification_token
+        WHERE token = #{token} AND email = #{email}
+    </select>
+</mapper>
diff --git a/target/classes/output/valid.torrent b/target/classes/output/valid.torrent
new file mode 100644
index 0000000..6a90e52
--- /dev/null
+++ b/target/classes/output/valid.torrent
@@ -0,0 +1 @@
+d10:created by18:qBittorrent v5.1.013:creation datei1745948995e4:infod6:lengthi22e4:name15:example.torrent12:piece lengthi16384e6:pieces20:Fnð¶)ú<Ç	ŽæÂh£tl7:privatei1eee
\ No newline at end of file
diff --git a/target/echo-backend-1.0-SNAPSHOT.jar b/target/echo-backend-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000..2ae979a
--- /dev/null
+++ b/target/echo-backend-1.0-SNAPSHOT.jar
Binary files differ
diff --git a/target/echo-backend-1.0-SNAPSHOT.jar.original b/target/echo-backend-1.0-SNAPSHOT.jar.original
new file mode 100644
index 0000000..4fd8014
--- /dev/null
+++ b/target/echo-backend-1.0-SNAPSHOT.jar.original
Binary files differ
diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties
new file mode 100644
index 0000000..07c940e
--- /dev/null
+++ b/target/maven-archiver/pom.properties
@@ -0,0 +1,3 @@
+artifactId=echo-backend
+groupId=groupId
+version=1.0-SNAPSHOT
diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
new file mode 100644
index 0000000..8994d0a
--- /dev/null
+++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
@@ -0,0 +1,58 @@
+com\example\myproject\common\Constants$FinishStatus.class
+com\example\myproject\config\MyMetaObjectHandler.class
+com\example\myproject\service\serviceImpl\UserServiceImpl.class
+com\example\myproject\controller\UserController.class
+com\example\myproject\dto\vo\TorrentVO.class
+com\example\myproject\dto\PromotionCreateDTO.class
+com\example\myproject\controller\TorrentController.class
+com\example\myproject\service\UserService.class
+com\example\myproject\common\Constants$Order.class
+com\example\myproject\entity\UserDetails.class
+com\example\myproject\common\base\Result.class
+com\example\myproject\entity\Promotion.class
+com\example\myproject\common\base\ResPage.class
+com\example\myproject\mapper\TorrentMapper.class
+com\example\myproject\config\SecurityConfig.class
+com\example\myproject\service\InvitationService.class
+com\example\myproject\service\serviceImpl\TorrentServiceImpl.class
+com\example\myproject\mapper\VerificationTokenMapper.class
+com\example\myproject\entity\TorrentEntity.class
+com\example\myproject\entity\EntityBase.class
+com\example\myproject\dto\param\TorrentParam.class
+com\example\myproject\service\serviceImpl\UserDetailsServiceImpl.class
+com\example\myproject\service\UserDetailsService.class
+com\example\myproject\service\TorrentService.class
+com\example\myproject\common\base\I18nMessage.class
+com\example\myproject\common\Constants.class
+com\example\myproject\entity\VerificationToken.class
+com\example\myproject\mapper\InvitationMapper.class
+com\example\myproject\common\base\OrderPageParam.class
+com\example\myproject\service\serviceImpl\InvitationServiceImpl.class
+com\example\myproject\config\GlobalCorsConfig.class
+com\example\myproject\mapper\UserMapper.class
+com\example\myproject\MyProjectApplication.class
+com\example\myproject\config\TrackerConfig.class
+com\example\myproject\mapper\FavoriteMapper.class
+com\example\myproject\common\Constants$Source.class
+com\example\myproject\common\ResultStatus.class
+com\example\myproject\dto\TorrentUpdateDTO.class
+com\example\myproject\common\base\PageUtil.class
+com\example\myproject\config\GlobalCorsConfig$1.class
+com\example\myproject\common\Constants$Announce.class
+com\example\myproject\common\CommonResultStatus.class
+com\example\myproject\controller\UserController$VerificationRequest.class
+com\example\myproject\service\serviceImpl\EmailServiceImpl.class
+com\example\myproject\controller\UserController$EmailRequest.class
+com\example\myproject\service\serviceImpl\PromotionServiceImpl.class
+com\example\myproject\common\base\Status.class
+com\example\myproject\common\base\PageParam.class
+com\example\myproject\mapper\PromotionMapper.class
+com\example\myproject\common\exception\RocketPTException.class
+com\example\myproject\entity\User.class
+com\example\myproject\utils\VerifyCode.class
+com\example\myproject\dto\param\InviteParam.class
+com\example\myproject\dto\param\TorrentUploadParam.class
+com\example\myproject\entity\InvitationEntity.class
+com\example\myproject\service\EmailService.class
+com\example\myproject\service\PromotionService.class
+com\example\myproject\entity\FavoriteEntity.class
diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
new file mode 100644
index 0000000..15f2807
--- /dev/null
+++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
@@ -0,0 +1,52 @@
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\service\serviceImpl\InvitationServiceImpl.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\common\CommonResultStatus.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\config\SecurityConfig.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\dto\TorrentUpdateDTO.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\mapper\UserMapper.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\common\base\PageParam.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\entity\EntityBase.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\service\UserDetailsService.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\common\exception\RocketPTException.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\mapper\FavoriteMapper.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\dto\param\TorrentParam.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\mapper\PromotionMapper.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\entity\FavoriteEntity.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\entity\TorrentEntity.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\mapper\VerificationTokenMapper.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\controller\UserController.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\dto\param\InviteParam.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\common\base\I18nMessage.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\dto\param\TorrentUploadParam.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\common\base\OrderPageParam.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\entity\Promotion.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\entity\InvitationEntity.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\service\InvitationService.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\service\serviceImpl\TorrentServiceImpl.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\common\ResultStatus.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\service\serviceImpl\EmailServiceImpl.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\common\Constants.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\config\TrackerConfig.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\common\base\ResPage.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\service\PromotionService.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\service\serviceImpl\UserServiceImpl.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\service\serviceImpl\UserDetailsServiceImpl.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\service\EmailService.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\common\base\Status.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\utils\VerifyCode.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\MyProjectApplication.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\mapper\TorrentMapper.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\entity\UserDetails.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\service\UserService.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\entity\User.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\common\base\PageUtil.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\common\base\Result.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\dto\vo\TorrentVO.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\mapper\InvitationMapper.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\service\TorrentService.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\config\MyMetaObjectHandler.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\entity\VerificationToken.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\service\serviceImpl\PromotionServiceImpl.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\controller\InviteController.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\config\GlobalCorsConfig.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\dto\PromotionCreateDTO.java
+D:\study\学习资源\大三下\echo-backend\src\main\java\com\example\myproject\controller\TorrentController.java
diff --git a/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst
new file mode 100644
index 0000000..bac235b
--- /dev/null
+++ b/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst
@@ -0,0 +1,2 @@
+com\example\myproject\controller\UserControllerTest$1.class
+com\example\myproject\controller\UserControllerTest.class
diff --git a/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
new file mode 100644
index 0000000..188dce8
--- /dev/null
+++ b/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
@@ -0,0 +1,2 @@
+D:\study\学习资源\大三下\echo-backend\src\test\java\com\example\myproject\controller\UserControllerTest.java
+D:\study\学习资源\大三下\echo-backend\src\test\java\com\example\myproject\controller\TorrentControllerTest.java
diff --git a/target/test-classes/classpath.index b/target/test-classes/classpath.index
new file mode 100644
index 0000000..600aff6
--- /dev/null
+++ b/target/test-classes/classpath.index
Binary files differ
diff --git a/target/test-classes/com/example/myproject/controller/UserControllerTest$1.class b/target/test-classes/com/example/myproject/controller/UserControllerTest$1.class
new file mode 100644
index 0000000..ec2954b
--- /dev/null
+++ b/target/test-classes/com/example/myproject/controller/UserControllerTest$1.class
Binary files differ
diff --git a/target/test-classes/com/example/myproject/controller/UserControllerTest.class b/target/test-classes/com/example/myproject/controller/UserControllerTest.class
new file mode 100644
index 0000000..923ca09
--- /dev/null
+++ b/target/test-classes/com/example/myproject/controller/UserControllerTest.class
Binary files differ