完成部分资源功能

Change-Id: Idfaef80363ef191a294e52ae53cdd4e2b3e7ccef
diff --git a/src/main/java/com/pt/service/DownloadService.java b/src/main/java/com/pt/service/DownloadService.java
index c02aea1..43b80e6 100644
--- a/src/main/java/com/pt/service/DownloadService.java
+++ b/src/main/java/com/pt/service/DownloadService.java
@@ -13,14 +13,12 @@
     @Autowired
     private DownloadRepository downloadRepository;
 
-    public DownloadService(DownloadRepository downloadRepository) {
-        this.downloadRepository = downloadRepository;
+    public void save(Download download) {
+        downloadRepository.save(download);
     }
 
-    public List<Download> searchByResourceId(int resourceId) {
+    public List<Download> getByResourceId(String resourceId) {
         return downloadRepository.findByResourceId(resourceId);
     }
-    /*
-        TODO: 添加下载需要的服务;
-     */
+
 }
diff --git a/src/main/java/com/pt/service/ResourceService.java b/src/main/java/com/pt/service/ResourceService.java
index a91bc7d..1d0e797 100644
--- a/src/main/java/com/pt/service/ResourceService.java
+++ b/src/main/java/com/pt/service/ResourceService.java
@@ -1,12 +1,21 @@
 package com.pt.service;
 
+import com.pt.entity.Download;
 import com.pt.entity.Resource;
+import com.pt.entity.TorrentMeta;
+import com.pt.repository.DownloadRepository;
 import com.pt.repository.ResourceRepository;
+import com.pt.repository.TorrentMetaRepository;
+import com.pt.utils.BencodeUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
 import java.time.LocalDateTime;
 import java.util.List;
+import java.util.Map;
 
 @Service
 public class ResourceService {
@@ -14,37 +23,74 @@
     @Autowired
     private ResourceRepository resourceRepository;
 
-    public ResourceService(ResourceRepository resourceRepository) {
-        this.resourceRepository = resourceRepository;
-    }
+    @Autowired
+    private DownloadRepository downloadRepository;
+
+    @Autowired
+    private TorrentService torrentService;
+
+    @Autowired
+    private TorrentMetaRepository torrentMetaRepository;
 
     public List<Resource> getAllResources() {
         return resourceRepository.findAll();
     }
 
-    public void publishResource(String name, String description, String publisher, double size) {
+    public List<Resource> getResourcesByAuthor(String username) {
+        return resourceRepository.findByAuthor(username);
+    }
+
+    // 发布资源时,将种子文件二进制数据保存到数据库
+    public void publishResource(String name, String description, String author, double size) throws Exception {
+        // 保存资源信息到数据库,包括种子文件的二进制数据
         Resource resource = new Resource();
         resource.setName(name);
-        resource.setSize(size);
         resource.setDescription(description);
-        resource.setAuthor(publisher);
+        resource.setAuthor(author);
+        resource.setSize(size);
         resource.setPublishTime(LocalDateTime.now());
+
+        // 生成种子数据 byte[]
+        byte[] torrentData = torrentService.generateTorrentBytes("uploads/" + name);
+        resource.setTorrentData(torrentData);
+
         resourceRepository.save(resource);
     }
 
-    public Resource getResourceById(int resourceId) {
-        return resourceRepository.findById(resourceId).orElse(null);
+    // 获取资源时,返回BLOB字段内容作为torrent文件
+    public byte[] getTorrentFileByResource(Resource resource, String username) {
+        if(resource == null || resource.getTorrentData() == null) return null;
+
+        // 记录下载日志
+        Download download = new Download();
+        download.setResourceId(String.valueOf(resource.getResourceId()));
+        download.setDownloader(username);
+        download.setDownloadTime(LocalDateTime.now());
+        downloadRepository.save(download);
+
+        return resource.getTorrentData();
     }
 
+    public Resource getResourceById(int id) {
+        return resourceRepository.findById(id).orElse(null);
+    }
+
+    public void deleteResource(int id) {
+        Resource resource = getResourceById(id);
+        if (resource != null) {
+            // 删除数据库资源记录
+            resourceRepository.deleteById(id);
+
+            // 删除对应的 TorrentMeta 元信息
+            TorrentMeta meta = torrentMetaRepository.findByFilename(resource.getName());
+            if (meta != null) {
+                torrentMetaRepository.delete(meta);
+            }
+
+        }
+    }
     public List<Resource> searchByQuery(String query) {
         return resourceRepository.findByNameContainingIgnoreCase(query);
     }
 
-    public List<Resource> getResourcesByAuthor(String author) {
-        return resourceRepository.findByAuthor(author);
-    }
-
-    public void deleteResource(int resourceId) {
-        resourceRepository.deleteById(resourceId);
-    }
 }
diff --git a/src/main/java/com/pt/service/TorrentService.java b/src/main/java/com/pt/service/TorrentService.java
new file mode 100644
index 0000000..dcbee69
--- /dev/null
+++ b/src/main/java/com/pt/service/TorrentService.java
@@ -0,0 +1,83 @@
+package com.pt.service;
+
+import com.pt.entity.TorrentMeta;
+import com.pt.repository.TorrentMetaRepository;
+import com.pt.utils.BencodeUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.*;
+import java.security.MessageDigest;
+import java.time.LocalDateTime;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+@Service
+public class TorrentService {
+
+    //  Tracker 服务器的 announce 地址
+    private static final String ANNOUNCE_URL = "http://localhost:8080/announce";
+    private static final int PIECE_LENGTH = 256 * 1024;
+
+    @Autowired
+    private TorrentMetaRepository torrentMetaRepository;
+
+    public byte[] generateTorrentBytes(String sourceFilePath) throws Exception {
+        File sourceFile = new File(sourceFilePath);
+
+        // 构造 info 字典
+        Map<String, Object> infoDict = new LinkedHashMap<>();
+        infoDict.put("name", sourceFile.getName());
+        infoDict.put("length", sourceFile.length());
+        infoDict.put("piece length", PIECE_LENGTH);
+        infoDict.put("pieces", calcPiecesHashes(sourceFile));
+
+        // 构造完整 torrent 字典
+        Map<String, Object> torrentDict = new LinkedHashMap<>();
+        torrentDict.put("announce", ANNOUNCE_URL);
+        torrentDict.put("info", infoDict);
+
+        // 编码成种子数据字节数组
+        byte[] bencodedTorrent = BencodeUtils.encode(torrentDict);
+
+        // 计算 info_hash 并保存到数据库(如果需要的话)
+        MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
+        byte[] infoEncoded = BencodeUtils.encode(infoDict);
+        sha1.update(infoEncoded);
+        String infoHash = bytesToHex(sha1.digest());
+
+        TorrentMeta meta = new TorrentMeta();
+        meta.setFilename(sourceFile.getName());
+        meta.setInfoHash(infoHash);
+        meta.setSize(sourceFile.length());
+        meta.setUploadTime(LocalDateTime.now());
+        torrentMetaRepository.save(meta);
+
+        return bencodedTorrent;
+    }
+
+
+    private byte[] calcPiecesHashes(File file) throws Exception {
+        MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
+        try (InputStream fis = new FileInputStream(file)) {
+            byte[] buffer = new byte[PIECE_LENGTH];
+            ByteArrayOutputStream piecesBuffer = new ByteArrayOutputStream();
+
+            int read;
+            while ((read = fis.read(buffer)) > 0) {
+                sha1.reset();
+                sha1.update(buffer, 0, read);
+                piecesBuffer.write(sha1.digest());
+            }
+            return piecesBuffer.toByteArray();
+        }
+    }
+
+    private String bytesToHex(byte[] bytes) {
+        StringBuilder sb = new StringBuilder();
+        for (byte b : bytes) {
+            sb.append(String.format("%02x", b));
+        }
+        return sb.toString();
+    }
+}
\ No newline at end of file