| package com.pt.service; |
| |
| import com.pt.entity.PeerInfoEntity; |
| import com.pt.entity.TorrentMeta; |
| import com.pt.exception.ResourceNotFoundException; |
| import com.pt.repository.PeerInfoRepository; |
| import com.pt.repository.TorrentMetaRepository; |
| import com.pt.utils.BencodeCodec; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.stereotype.Service; |
| import org.springframework.transaction.annotation.Transactional; |
| |
| import java.nio.charset.StandardCharsets; |
| import java.time.LocalDateTime; |
| import java.util.List; |
| import java.util.Map; |
| |
| @Service |
| public class TrackerService { |
| |
| @Autowired |
| private TorrentMetaRepository torrentMetaRepository; |
| |
| @Autowired |
| private PeerInfoRepository peerInfoRepository; |
| |
| @Autowired |
| private TorrentStatsService statsService; |
| |
| @Transactional |
| public byte[] handleAnnounce(Map<String, String[]> params, String ipAddress) { |
| try { |
| // 验证必要参数 |
| if (!params.containsKey("info_hash") || !params.containsKey("peer_id") |
| || !params.containsKey("port")) { |
| return errorResponse("Missing required parameters"); |
| } |
| |
| // 解析参数 |
| String infoHash = decodeParam(params.get("info_hash")[0]); |
| String peerId = decodeParam(params.get("peer_id")[0]); |
| int port = Integer.parseInt(params.get("port")[0]); |
| |
| // 获取事件类型 |
| String event = getEventParam(params); |
| |
| // 获取流量数据 |
| long uploaded = getLongParam(params, "uploaded", 0); |
| long downloaded = getLongParam(params, "downloaded", 0); |
| long left = getLongParam(params, "left", 0); |
| |
| // 验证种子是否存在 |
| TorrentMeta meta = torrentMetaRepository.findByInfoHash(infoHash); |
| if (meta == null) { |
| return errorResponse("Torrent not found: " + infoHash); |
| } |
| |
| // 创建或更新 peer 信息 |
| PeerInfoEntity peer = findOrCreatePeer(peerId, infoHash); |
| |
| // 设置 peer 属性 |
| setPeerProperties(peer, ipAddress, port, uploaded, downloaded, left); |
| |
| // 处理事件类型 |
| handlePeerEvent(event, peer, left); |
| |
| // 保存 peer |
| peerInfoRepository.save(peer); |
| |
| // 更新种子统计信息 |
| statsService.updateTorrentStats(infoHash); |
| |
| // 获取 peer 列表响应 |
| List<PeerInfoEntity> activePeers = peerInfoRepository.findActivePeersByInfoHash(infoHash); |
| byte[] peerBytes = buildPeerResponse(activePeers); |
| |
| // 返回成功响应 |
| return BencodeCodec.buildTrackerResponse(1800, peerBytes); |
| } catch (Exception e) { |
| return errorResponse("Internal server error"); |
| } |
| } |
| |
| // 辅助方法:获取事件参数 |
| private String getEventParam(Map<String, String[]> params) { |
| return params.containsKey("event") ? |
| decodeParam(params.get("event")[0]) : "update"; |
| } |
| |
| // 辅助方法:获取长整型参数 |
| private long getLongParam(Map<String, String[]> params, String key, long defaultValue) { |
| return params.containsKey(key) ? |
| Long.parseLong(params.get(key)[0]) : defaultValue; |
| } |
| |
| // 辅助方法:查找或创建 peer |
| private PeerInfoEntity findOrCreatePeer(String peerId, String infoHash) { |
| return peerInfoRepository.findByPeerIdAndInfoHash(peerId, infoHash) |
| .orElseGet(PeerInfoEntity::new); |
| } |
| |
| // 辅助方法:设置 peer 属性 |
| private void setPeerProperties(PeerInfoEntity peer, String ip, int port, |
| long uploaded, long downloaded, long left) { |
| peer.setIp(ip); |
| peer.setPort(port); |
| peer.setPeerId(peer.getPeerId() != null ? peer.getPeerId() : ""); // 防止 NPE |
| peer.setInfoHash(peer.getInfoHash() != null ? peer.getInfoHash() : ""); |
| peer.setUploaded(uploaded); |
| peer.setDownloaded(downloaded); |
| peer.setLeft(left); |
| peer.setLastSeen(LocalDateTime.now()); |
| } |
| |
| // 辅助方法:处理 peer 事件 |
| private void handlePeerEvent(String event, PeerInfoEntity peer, long left) { |
| switch (event) { |
| case "started": |
| peer.setStatus("downloading"); |
| peer.setActive(true); |
| break; |
| |
| case "stopped": |
| peer.setActive(false); |
| break; |
| |
| case "completed": |
| handleCompletedEvent(peer); |
| break; |
| |
| case "update": |
| default: |
| handleUpdateEvent(peer, left); |
| break; |
| } |
| } |
| |
| // 处理完成事件 |
| private void handleCompletedEvent(PeerInfoEntity peer) { |
| peer.setStatus("completed"); |
| peer.setActive(true); |
| peer.setLeft(0); |
| incrementCompletedCount(peer.getInfoHash()); |
| } |
| |
| // 处理更新事件 |
| private void handleUpdateEvent(PeerInfoEntity peer, long left) { |
| if (left == 0 && "downloading".equals(peer.getStatus())) { |
| // 检测到下载完成 |
| peer.setStatus("completed"); |
| incrementCompletedCount(peer.getInfoHash()); |
| } |
| peer.setActive(true); |
| } |
| |
| // 增加完成次数 |
| private void incrementCompletedCount(String infoHash) { |
| TorrentMeta meta = torrentMetaRepository.findByInfoHash(infoHash); |
| if (meta != null) { |
| statsService.incrementCompletedCount(meta.getId()); |
| } |
| } |
| |
| // 构建 peer 响应 |
| private byte[] buildPeerResponse(List<PeerInfoEntity> peers) { |
| List<String> ips = peers.stream().map(PeerInfoEntity::getIp).toList(); |
| List<Integer> ports = peers.stream().map(PeerInfoEntity::getPort).toList(); |
| return BencodeCodec.buildCompactPeers(ips, ports); |
| } |
| |
| // 构建错误响应 |
| private byte[] errorResponse(String reason) { |
| return BencodeCodec.encode(Map.of("failure reason", reason)); |
| } |
| |
| // 解码参数 |
| private String decodeParam(String raw) { |
| return new String(raw.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); |
| } |
| } |