blob: 191479ba83cc16175da76e3287581ef730e9335e [file] [log] [blame]
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +08001package com.pt.service;
2
ystxdfd42b32025-06-07 13:26:52 +08003import com.pt.entity.PeerInfoEntity;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +08004import com.pt.entity.TorrentMeta;
ystxdfd42b32025-06-07 13:26:52 +08005import com.pt.exception.ResourceNotFoundException;
6import com.pt.repository.PeerInfoRepository;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +08007import com.pt.repository.TorrentMetaRepository;
8import com.pt.utils.BencodeCodec;
9import org.springframework.beans.factory.annotation.Autowired;
10import org.springframework.stereotype.Service;
ystxdfd42b32025-06-07 13:26:52 +080011import org.springframework.transaction.annotation.Transactional;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080012
13import java.nio.charset.StandardCharsets;
ystxdfd42b32025-06-07 13:26:52 +080014import java.time.LocalDateTime;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080015import java.util.List;
16import java.util.Map;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080017
18@Service
19public class TrackerService {
20
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080021 @Autowired
22 private TorrentMetaRepository torrentMetaRepository;
23
ystxdfd42b32025-06-07 13:26:52 +080024 @Autowired
25 private PeerInfoRepository peerInfoRepository;
26
27 @Autowired
28 private TorrentStatsService statsService;
29
30 @Transactional
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080031 public byte[] handleAnnounce(Map<String, String[]> params, String ipAddress) {
32 try {
ystxdfd42b32025-06-07 13:26:52 +080033 // 验证必要参数
34 if (!params.containsKey("info_hash") || !params.containsKey("peer_id")
35 || !params.containsKey("port")) {
36 return errorResponse("Missing required parameters");
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080037 }
38
ystxdfd42b32025-06-07 13:26:52 +080039 // 解析参数
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080040 String infoHash = decodeParam(params.get("info_hash")[0]);
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080041 String peerId = decodeParam(params.get("peer_id")[0]);
42 int port = Integer.parseInt(params.get("port")[0]);
43
ystxdfd42b32025-06-07 13:26:52 +080044 // 获取事件类型
45 String event = getEventParam(params);
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080046
ystxdfd42b32025-06-07 13:26:52 +080047 // 获取流量数据
48 long uploaded = getLongParam(params, "uploaded", 0);
49 long downloaded = getLongParam(params, "downloaded", 0);
50 long left = getLongParam(params, "left", 0);
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080051
ystxdfd42b32025-06-07 13:26:52 +080052 // 验证种子是否存在
53 TorrentMeta meta = torrentMetaRepository.findByInfoHash(infoHash);
54 if (meta == null) {
55 return errorResponse("Torrent not found: " + infoHash);
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080056 }
57
ystxdfd42b32025-06-07 13:26:52 +080058 // 创建或更新 peer 信息
59 PeerInfoEntity peer = findOrCreatePeer(peerId, infoHash);
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080060
ystxdfd42b32025-06-07 13:26:52 +080061 // 设置 peer 属性
62 setPeerProperties(peer, ipAddress, port, uploaded, downloaded, left);
63
64 // 处理事件类型
65 handlePeerEvent(event, peer, left);
66
67 // 保存 peer
68 peerInfoRepository.save(peer);
69
70 // 更新种子统计信息
71 statsService.updateTorrentStats(infoHash);
72
73 // 获取 peer 列表响应
74 List<PeerInfoEntity> activePeers = peerInfoRepository.findActivePeersByInfoHash(infoHash);
75 byte[] peerBytes = buildPeerResponse(activePeers);
76
77 // 返回成功响应
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080078 return BencodeCodec.buildTrackerResponse(1800, peerBytes);
79 } catch (Exception e) {
ystxdfd42b32025-06-07 13:26:52 +080080 return errorResponse("Internal server error");
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080081 }
82 }
83
ystxdfd42b32025-06-07 13:26:52 +080084 // 辅助方法:获取事件参数
85 private String getEventParam(Map<String, String[]> params) {
86 return params.containsKey("event") ?
87 decodeParam(params.get("event")[0]) : "update";
88 }
89
90 // 辅助方法:获取长整型参数
91 private long getLongParam(Map<String, String[]> params, String key, long defaultValue) {
92 return params.containsKey(key) ?
93 Long.parseLong(params.get(key)[0]) : defaultValue;
94 }
95
96 // 辅助方法:查找或创建 peer
97 private PeerInfoEntity findOrCreatePeer(String peerId, String infoHash) {
98 return peerInfoRepository.findByPeerIdAndInfoHash(peerId, infoHash)
99 .orElseGet(PeerInfoEntity::new);
100 }
101
102 // 辅助方法:设置 peer 属性
103 private void setPeerProperties(PeerInfoEntity peer, String ip, int port,
104 long uploaded, long downloaded, long left) {
105 peer.setIp(ip);
106 peer.setPort(port);
107 peer.setPeerId(peer.getPeerId() != null ? peer.getPeerId() : ""); // 防止 NPE
108 peer.setInfoHash(peer.getInfoHash() != null ? peer.getInfoHash() : "");
109 peer.setUploaded(uploaded);
110 peer.setDownloaded(downloaded);
111 peer.setLeft(left);
112 peer.setLastSeen(LocalDateTime.now());
113 }
114
115 // 辅助方法:处理 peer 事件
116 private void handlePeerEvent(String event, PeerInfoEntity peer, long left) {
117 switch (event) {
118 case "started":
119 peer.setStatus("downloading");
120 peer.setActive(true);
121 break;
122
123 case "stopped":
124 peer.setActive(false);
125 break;
126
127 case "completed":
128 handleCompletedEvent(peer);
129 break;
130
131 case "update":
132 default:
133 handleUpdateEvent(peer, left);
134 break;
135 }
136 }
137
138 // 处理完成事件
139 private void handleCompletedEvent(PeerInfoEntity peer) {
140 peer.setStatus("completed");
141 peer.setActive(true);
142 peer.setLeft(0);
143 incrementCompletedCount(peer.getInfoHash());
144 }
145
146 // 处理更新事件
147 private void handleUpdateEvent(PeerInfoEntity peer, long left) {
148 if (left == 0 && "downloading".equals(peer.getStatus())) {
149 // 检测到下载完成
150 peer.setStatus("completed");
151 incrementCompletedCount(peer.getInfoHash());
152 }
153 peer.setActive(true);
154 }
155
156 // 增加完成次数
157 private void incrementCompletedCount(String infoHash) {
158 TorrentMeta meta = torrentMetaRepository.findByInfoHash(infoHash);
159 if (meta != null) {
160 statsService.incrementCompletedCount(meta.getId());
161 }
162 }
163
164 // 构建 peer 响应
165 private byte[] buildPeerResponse(List<PeerInfoEntity> peers) {
166 List<String> ips = peers.stream().map(PeerInfoEntity::getIp).toList();
167 List<Integer> ports = peers.stream().map(PeerInfoEntity::getPort).toList();
168 return BencodeCodec.buildCompactPeers(ips, ports);
169 }
170
171 // 构建错误响应
172 private byte[] errorResponse(String reason) {
173 return BencodeCodec.encode(Map.of("failure reason", reason));
174 }
175
176 // 解码参数
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +0800177 private String decodeParam(String raw) {
178 return new String(raw.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
179 }
ystxdfd42b32025-06-07 13:26:52 +0800180}