add post,add redis in docker compose,add torrentutil test remove dockerfile, modify torrentutil
Change-Id: I8014d4994d0a09c2f28cfcf0f8d2a430372aaab5
diff --git a/src/main/java/com/example/g8backend/controller/AuthController.java b/src/main/java/com/example/g8backend/controller/AuthController.java
index 2f36500..e824b63 100644
--- a/src/main/java/com/example/g8backend/controller/AuthController.java
+++ b/src/main/java/com/example/g8backend/controller/AuthController.java
@@ -66,7 +66,7 @@
// passkey 用于在客户端发送announce请求时获取用户信息
user.setPasskey(UUID.randomUUID().toString().replace("-", ""));
- userService.registerUser(user);
+ userService.save(user);
return ResponseEntity.ok("注册成功");
}
diff --git a/src/main/java/com/example/g8backend/controller/PostController.java b/src/main/java/com/example/g8backend/controller/PostController.java
new file mode 100644
index 0000000..7b7b7c7
--- /dev/null
+++ b/src/main/java/com/example/g8backend/controller/PostController.java
@@ -0,0 +1,57 @@
+package com.example.g8backend.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.bind.annotation.*;
+import com.example.g8backend.entity.Post;
+import com.example.g8backend.service.IPostService;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/post")
+public class PostController {
+ @Autowired
+ private IPostService postService;
+
+ @PostMapping("")
+ public ResponseEntity<?> createPost(@RequestBody Post post) {
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ long userId = (long) authentication.getPrincipal();
+ post.setUserId(userId);
+ postService.save(post);
+ return ResponseEntity.ok().build();
+ }
+
+ @GetMapping("/{postId}")
+ public Post getPost(@PathVariable("postId") Long postId) {
+ return postService.getById(postId);
+ }
+
+ @DeleteMapping("/{postId}")
+ public ResponseEntity<?> deletePost(@PathVariable("postId") Long postId) {
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ long userId = (long) authentication.getPrincipal();
+ Post post = postService.getById(postId);
+ if (post == null) {
+ return ResponseEntity.status(500).body("Post not found.");
+ }
+ if (post.getUserId()!= userId) {
+ return ResponseEntity.status(403).body("You are not authorized to delete this post.");
+ }
+ postService.removeById(postId);
+ return ResponseEntity.ok().body("Post deleted successfully.");
+ }
+
+ @GetMapping("/getAll")
+ public List<Post> getAllPosts() {
+ return postService.list();
+ }
+
+ @GetMapping("/getByUserId/{userId}")
+ public List<Post> getPostsByUserId(@PathVariable("userId") Long userId) {
+ return postService.getPostsByUserId(userId);
+ }
+}
diff --git a/src/main/java/com/example/g8backend/controller/TorrentController.java b/src/main/java/com/example/g8backend/controller/TorrentController.java
index 5a8b473..0c016f4 100644
--- a/src/main/java/com/example/g8backend/controller/TorrentController.java
+++ b/src/main/java/com/example/g8backend/controller/TorrentController.java
@@ -37,7 +37,11 @@
File tempFile = File.createTempFile("upload-", ".torrent");
multipartFile.transferTo(tempFile);
- torrentService.handleTorrentUpload(tempFile, userId, passkey);
+ try {
+ torrentService.handleTorrentUpload(tempFile, userId, passkey);
+ } catch (IllegalArgumentException e) {
+ return ResponseEntity.badRequest().body(e.getMessage());
+ }
// 删除临时文件
if(!tempFile.delete()){
diff --git a/src/main/java/com/example/g8backend/entity/Post.java b/src/main/java/com/example/g8backend/entity/Post.java
new file mode 100644
index 0000000..ac7ff20
--- /dev/null
+++ b/src/main/java/com/example/g8backend/entity/Post.java
@@ -0,0 +1,33 @@
+package com.example.g8backend.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.sql.Timestamp;
+import lombok.Data;
+
+@Data
+@TableName("posts")
+public class Post {
+ @TableId(type = IdType.AUTO)
+ private Long postId;
+
+ private Long userId;
+ private String postTitle;
+ private String postContent;
+ private Timestamp createdAt;
+ private String postType;
+
+ @Override
+ public String toString() {
+ return "Post{" +
+ "postId=" + postId +
+ ", userId=" + userId +
+ ", postTitle='" + postTitle + '\'' +
+ ", postContent='" + postContent + '\'' +
+ ", createdAt=" + createdAt +
+ ", postType='" + postType + '\'' +
+ '}';
+ }
+}
diff --git a/src/main/java/com/example/g8backend/mapper/PostMapper.java b/src/main/java/com/example/g8backend/mapper/PostMapper.java
new file mode 100644
index 0000000..6db4e67
--- /dev/null
+++ b/src/main/java/com/example/g8backend/mapper/PostMapper.java
@@ -0,0 +1,13 @@
+package com.example.g8backend.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.example.g8backend.entity.Post;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface PostMapper extends BaseMapper<Post> {
+ List<Post> getPostsByUserId(@Param("userId") Long userId);
+}
diff --git a/src/main/java/com/example/g8backend/service/IPostService.java b/src/main/java/com/example/g8backend/service/IPostService.java
new file mode 100644
index 0000000..58c13dc
--- /dev/null
+++ b/src/main/java/com/example/g8backend/service/IPostService.java
@@ -0,0 +1,10 @@
+package com.example.g8backend.service;
+
+import com.example.g8backend.entity.Post;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.util.List;
+
+public interface IPostService extends IService<Post> {
+ List<Post> getPostsByUserId(Long userId);
+}
diff --git a/src/main/java/com/example/g8backend/service/IUserService.java b/src/main/java/com/example/g8backend/service/IUserService.java
index f407c37..af94d27 100644
--- a/src/main/java/com/example/g8backend/service/IUserService.java
+++ b/src/main/java/com/example/g8backend/service/IUserService.java
@@ -8,5 +8,4 @@
User getUserByName(@Param("name") String name);
User getUserByEmail(@Param("email") String email);
User getUserByPasskey(@Param("passkey") String passkey);
- void registerUser(User user);;
}
diff --git a/src/main/java/com/example/g8backend/service/impl/PostServiceImpl.java b/src/main/java/com/example/g8backend/service/impl/PostServiceImpl.java
new file mode 100644
index 0000000..90d353a
--- /dev/null
+++ b/src/main/java/com/example/g8backend/service/impl/PostServiceImpl.java
@@ -0,0 +1,21 @@
+package com.example.g8backend.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.example.g8backend.entity.Post;
+import com.example.g8backend.mapper.PostMapper;
+import com.example.g8backend.service.IPostService;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements IPostService {
+ @Resource
+ private PostMapper postMapper;
+
+ @Override
+ public List<Post> getPostsByUserId(Long userId) {
+ return postMapper.getPostsByUserId(userId);
+ }
+}
diff --git a/src/main/java/com/example/g8backend/service/impl/TorrentServiceImpl.java b/src/main/java/com/example/g8backend/service/impl/TorrentServiceImpl.java
index 8d9ae74..bedb94a 100644
--- a/src/main/java/com/example/g8backend/service/impl/TorrentServiceImpl.java
+++ b/src/main/java/com/example/g8backend/service/impl/TorrentServiceImpl.java
@@ -20,7 +20,7 @@
String tracker = "http://127.0.0.1:8080/announce/";
@Override
- public Torrent handleTorrentUpload(File file, Long userId, String passkey) throws IOException{
+ public Torrent handleTorrentUpload(File file, Long userId, String passkey) throws IOException, IllegalArgumentException {
// 修改 announce 字段
byte[] modifiedBytes = TorrentUtil.injectTracker(file, tracker + passkey);
diff --git a/src/main/java/com/example/g8backend/service/impl/UserServiceImpl.java b/src/main/java/com/example/g8backend/service/impl/UserServiceImpl.java
index 3f3357c..e8bcf20 100644
--- a/src/main/java/com/example/g8backend/service/impl/UserServiceImpl.java
+++ b/src/main/java/com/example/g8backend/service/impl/UserServiceImpl.java
@@ -22,6 +22,4 @@
@Override
public User getUserByPasskey(String passkey) { return userMapper.getUserByPasskey(passkey);}
- @Override
- public void registerUser(User user) {userMapper.insert(user);}
}
diff --git a/src/main/java/com/example/g8backend/util/TorrentUtil.java b/src/main/java/com/example/g8backend/util/TorrentUtil.java
index f644fff..e6c1f3b 100644
--- a/src/main/java/com/example/g8backend/util/TorrentUtil.java
+++ b/src/main/java/com/example/g8backend/util/TorrentUtil.java
@@ -22,10 +22,14 @@
return bencode.encode(torrentMap);
}
- public static String getInfoHash(File torrentFile) throws IOException {
+ public static String getInfoHash(File torrentFile) throws IOException, IllegalArgumentException {
byte[] fileBytes = readBytes(torrentFile);
Map<String, Object> torrentMap = bencode.decode(fileBytes, Type.DICTIONARY);
+ if (!torrentMap.containsKey("info")) {
+ throw new IllegalArgumentException("Invalid torrent file: missing 'info' dictionary.");
+ }
+
@SuppressWarnings("unchecked")
Map<String, Object> info = (Map<String, Object>) torrentMap.get("info");
@@ -56,7 +60,7 @@
}
}
- private static String bytesToHex(byte[] hash) {
+ public static String bytesToHex(byte[] hash) {
StringBuilder hex = new StringBuilder();
for (byte b : hash) {
hex.append(String.format("%02x", b));
diff --git a/src/main/resources/mapper/PostMapper.xml b/src/main/resources/mapper/PostMapper.xml
new file mode 100644
index 0000000..bc4debe
--- /dev/null
+++ b/src/main/resources/mapper/PostMapper.xml
@@ -0,0 +1,9 @@
+<?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.g8backend.mapper.PostMapper">
+ <select id="getPostsByUserId" resultType="com.example.g8backend.entity.Post">
+ SELECT * FROM posts WHERE user_id = #{userId}
+ </select>
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
index 365f9e7..e88ff07 100644
--- a/src/main/resources/schema.sql
+++ b/src/main/resources/schema.sql
@@ -1,3 +1,6 @@
+CREATE DATABASE IF NOT EXISTS g8backend;
+USE g8backend;
+
CREATE TABLE IF NOT EXISTS `users` (
user_id INT AUTO_INCREMENT PRIMARY KEY,
user_name VARCHAR(255) NOT NULL,
@@ -27,3 +30,21 @@
FOREIGN KEY (passkey) REFERENCES users(passkey),
PRIMARY KEY (passkey, info_hash, peer_id)
);
+
+CREATE TABLE IF NOT EXISTS `posts` (
+ post_id INT AUTO_INCREMENT PRIMARY KEY,
+ user_id INT NOT NULL,
+ post_title VARCHAR(255) NOT NULL,
+ post_content TEXT NOT NULL,
+ post_type ENUM('resource', 'discussion') NOT NULL,
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(user_id)
+);
+
+CREATE TABLE IF NOT EXISTS `post_likes` (
+ user_id INT NOT NULL,
+ post_id INT NOT NULL,
+ FOREIGN KEY (user_id) REFERENCES users(user_id),
+ FOREIGN KEY (post_id) REFERENCES posts(post_id),
+ PRIMARY KEY (user_id, post_id)
+);
diff --git a/src/test/java/com/example/g8backend/util/TorrentUtilTest.java b/src/test/java/com/example/g8backend/util/TorrentUtilTest.java
new file mode 100644
index 0000000..c42803b
--- /dev/null
+++ b/src/test/java/com/example/g8backend/util/TorrentUtilTest.java
@@ -0,0 +1,105 @@
+package com.example.g8backend.util;
+
+import com.dampcake.bencode.Bencode;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.security.MessageDigest;
+import static org.junit.jupiter.api.Assertions.*;
+
+class TorrentUtilTest {
+
+ private static final String TEST_TRACKER_URL = "http://test.tracker/announce";
+
+ @TempDir
+ Path tempDir; // 临时目录
+
+ //---------------- 动态生成 .torrent 文件 -----------------
+ private File createTestTorrentFile(String announceUrl, Map<String, Object> info) throws Exception {
+ // 1. 构造 Bencode 数据结构
+ Map<String, Object> torrentMap = new HashMap<>();
+ torrentMap.put("announce", announceUrl);
+ torrentMap.put("info", info);
+
+ // 2. 编码为 Bencode 字节流
+ Bencode bencode = new Bencode();
+ byte[] bencodeData = bencode.encode(torrentMap);
+
+ // 3. 写入临时文件
+ Path torrentPath = tempDir.resolve("dynamic.torrent");
+ Files.write(torrentPath, bencodeData);
+ return torrentPath.toFile();
+ }
+
+ //---------------- 测试 injectTracker() -----------------
+ @Test
+ void testInjectTracker() throws Exception {
+ // 动态生成测试文件
+ Map<String, Object> info = Map.of("name", "test.txt", "length", 1024);
+ File originalFile = createTestTorrentFile("http://old.tracker", info);
+
+ // 调用方法修改 Tracker
+ byte[] modifiedData = TorrentUtil.injectTracker(originalFile, TEST_TRACKER_URL);
+
+ // 验证新 Tracker 是否生效
+ String modifiedBencode = new String(modifiedData);
+ assertTrue(modifiedBencode.contains(TEST_TRACKER_URL));
+ }
+
+ //---------------- 测试 getInfoHash() -----------------
+ @Test
+ void testGetInfoHash_ShouldCalculateCorrectHash() throws Exception {
+ // 动态生成测试文件
+ Map<String, Object> info = Map.of(
+ "name", "test.txt",
+ "length", 1024,
+ "piece length", 16384,
+ "pieces", "1234567890abcdef1234"
+ );
+ File torrentFile = createTestTorrentFile(TEST_TRACKER_URL, info);
+
+ // 计算预期哈希(直接通过 Bencode 库生成 info 的哈希)
+ Bencode bencode = new Bencode();
+ byte[] infoBytes = bencode.encode(info);
+ String expectedHash = TorrentUtil.bytesToHex(
+ MessageDigest.getInstance("SHA-1").digest(infoBytes)
+ );
+
+ // 调用方法并验证
+ String actualHash = TorrentUtil.getInfoHash(torrentFile);
+ assertEquals(expectedHash, actualHash);
+ }
+
+ //---------------- 测试 saveToFile() -----------------
+ @Test
+ void testSaveToFile() throws Exception {
+ // 生成测试数据
+ byte[] testData = {0x01, 0x02, 0x03};
+ File outputFile = tempDir.resolve("output.torrent").toFile();
+
+ // 调用方法保存文件
+ TorrentUtil.saveToFile(testData, outputFile);
+
+ // 验证文件内容
+ byte[] savedData = Files.readAllBytes(outputFile.toPath());
+ assertArrayEquals(testData, savedData);
+ }
+ //---------------- 异常场景测试 -----------------
+ @Test
+ void getInfoHash_ShouldThrow_WhenInfoFieldMissing() throws Exception {
+ // 构造一个缺少 "info" 字段的无效 .torrent 文件
+ Map<String, Object> invalidTorrent = Map.of("announce", TEST_TRACKER_URL);
+ Bencode bencode = new Bencode();
+ byte[] bencodeData = bencode.encode(invalidTorrent);
+
+ Path invalidTorrentPath = tempDir.resolve("dynamic.torrent");
+ Files.write(invalidTorrentPath, bencodeData);
+
+ // 验证抛出异常
+ assertThrows(IllegalArgumentException.class, () -> TorrentUtil.getInfoHash(invalidTorrentPath.toFile()));
+ }
+}
\ No newline at end of file