blob: 8a654d054ce98ba491fcd4e0a12552fc7dddbe19 [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;
22301102f5670302025-06-08 14:10:02 +08005import com.pt.entity.User;
ystxdfd42b32025-06-07 13:26:52 +08006import com.pt.exception.ResourceNotFoundException;
7import com.pt.repository.PeerInfoRepository;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +08008import com.pt.repository.TorrentMetaRepository;
9import com.pt.utils.BencodeCodec;
22301102f5670302025-06-08 14:10:02 +080010import org.slf4j.Logger;
11import org.slf4j.LoggerFactory;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080012import org.springframework.beans.factory.annotation.Autowired;
13import org.springframework.stereotype.Service;
ystxdfd42b32025-06-07 13:26:52 +080014import org.springframework.transaction.annotation.Transactional;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080015
16import java.nio.charset.StandardCharsets;
ystxdfd42b32025-06-07 13:26:52 +080017import java.time.LocalDateTime;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080018import java.util.List;
19import java.util.Map;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080020
21@Service
22public class TrackerService {
23
22301102f5670302025-06-08 14:10:02 +080024 private static final Logger logger = LoggerFactory.getLogger(TrackerService.class);
25
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080026 @Autowired
27 private TorrentMetaRepository torrentMetaRepository;
28
ystxdfd42b32025-06-07 13:26:52 +080029 @Autowired
30 private PeerInfoRepository peerInfoRepository;
31
32 @Autowired
33 private TorrentStatsService statsService;
22301102f5670302025-06-08 14:10:02 +080034
35 @Autowired
36 private UserService userService;
ystxdfd42b32025-06-07 13:26:52 +080037
38 @Transactional
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080039 public byte[] handleAnnounce(Map<String, String[]> params, String ipAddress) {
40 try {
ystxdfd42b32025-06-07 13:26:52 +080041 // 验证必要参数
42 if (!params.containsKey("info_hash") || !params.containsKey("peer_id")
43 || !params.containsKey("port")) {
44 return errorResponse("Missing required parameters");
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080045 }
46
ystxdfd42b32025-06-07 13:26:52 +080047 // 解析参数
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080048 String infoHash = decodeParam(params.get("info_hash")[0]);
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080049 String peerId = decodeParam(params.get("peer_id")[0]);
22301102f5670302025-06-08 14:10:02 +080050 String username = decodeParam(params.get("username")[0]);
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080051 int port = Integer.parseInt(params.get("port")[0]);
52
ystxdfd42b32025-06-07 13:26:52 +080053 // 获取事件类型
54 String event = getEventParam(params);
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080055
ystxdfd42b32025-06-07 13:26:52 +080056 // 获取流量数据
57 long uploaded = getLongParam(params, "uploaded", 0);
58 long downloaded = getLongParam(params, "downloaded", 0);
59 long left = getLongParam(params, "left", 0);
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080060
ystxdfd42b32025-06-07 13:26:52 +080061 // 验证种子是否存在
62 TorrentMeta meta = torrentMetaRepository.findByInfoHash(infoHash);
63 if (meta == null) {
64 return errorResponse("Torrent not found: " + infoHash);
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080065 }
66
ystxdfd42b32025-06-07 13:26:52 +080067 // 创建或更新 peer 信息
68 PeerInfoEntity peer = findOrCreatePeer(peerId, infoHash);
22301102f5670302025-06-08 14:10:02 +080069
70 // 计算流量增量
71 long uploadedDelta = uploaded - peer.getUploaded();
72 long downloadedDelta = downloaded - peer.getDownloaded();
73
74 // 更新用户总流量
75 updateUserTraffic(username, uploadedDelta, downloadedDelta);
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080076
ystxdfd42b32025-06-07 13:26:52 +080077 // 设置 peer 属性
22301102f5670302025-06-08 14:10:02 +080078 peer.setUsername(username);
ystxdfd42b32025-06-07 13:26:52 +080079 setPeerProperties(peer, ipAddress, port, uploaded, downloaded, left);
80
81 // 处理事件类型
82 handlePeerEvent(event, peer, left);
83
84 // 保存 peer
85 peerInfoRepository.save(peer);
86
87 // 更新种子统计信息
88 statsService.updateTorrentStats(infoHash);
89
90 // 获取 peer 列表响应
91 List<PeerInfoEntity> activePeers = peerInfoRepository.findActivePeersByInfoHash(infoHash);
92 byte[] peerBytes = buildPeerResponse(activePeers);
93
94 // 返回成功响应
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080095 return BencodeCodec.buildTrackerResponse(1800, peerBytes);
96 } catch (Exception e) {
ystxdfd42b32025-06-07 13:26:52 +080097 return errorResponse("Internal server error");
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080098 }
99 }
100
22301102f5670302025-06-08 14:10:02 +0800101 // 添加更新用户流量的方法
102 @Transactional
103 private void updateUserTraffic(String username, long uploadedDelta, long downloadedDelta) {
104 if (uploadedDelta <= 0 && downloadedDelta <= 0) {
105 return; // 没有新的流量,不需要更新
106 }
107
108 User user = userService.findByUsername(username);
109 if (user != null) {
110 long oldUploaded = user.getUploaded();
111 long oldDownloaded = user.getDownloaded();
112
113 user.setUploaded(oldUploaded + uploadedDelta);
114 user.setDownloaded(oldDownloaded + downloadedDelta);
115 userService.save(user);
116
117 // 更新用户等级
118 userService.updateUserLevel(user.getUid());
119
120 logger.info("用户 {} 流量更新: 上传 {} -> {} (+{}), 下载 {} -> {} (+{})",
121 username,
122 oldUploaded, user.getUploaded(), uploadedDelta,
123 oldDownloaded, user.getDownloaded(), downloadedDelta);
124 } else {
125 logger.warn("尝试更新不存在的用户流量: {}", username);
126 }
127 }
128
ystxdfd42b32025-06-07 13:26:52 +0800129 // 辅助方法:获取事件参数
130 private String getEventParam(Map<String, String[]> params) {
131 return params.containsKey("event") ?
132 decodeParam(params.get("event")[0]) : "update";
133 }
134
135 // 辅助方法:获取长整型参数
136 private long getLongParam(Map<String, String[]> params, String key, long defaultValue) {
137 return params.containsKey(key) ?
138 Long.parseLong(params.get(key)[0]) : defaultValue;
139 }
140
141 // 辅助方法:查找或创建 peer
142 private PeerInfoEntity findOrCreatePeer(String peerId, String infoHash) {
143 return peerInfoRepository.findByPeerIdAndInfoHash(peerId, infoHash)
22301102f5670302025-06-08 14:10:02 +0800144 .orElseGet(() -> {
145 PeerInfoEntity newPeer = new PeerInfoEntity();
146 newPeer.setPeerId(peerId);
147 newPeer.setInfoHash(infoHash);
148 newPeer.setActive(true);
149 newPeer.setStatus("downloading");
150 newPeer.setLastSeen(LocalDateTime.now());
151 return newPeer;
152 });
ystxdfd42b32025-06-07 13:26:52 +0800153 }
154
155 // 辅助方法:设置 peer 属性
156 private void setPeerProperties(PeerInfoEntity peer, String ip, int port,
157 long uploaded, long downloaded, long left) {
158 peer.setIp(ip);
159 peer.setPort(port);
160 peer.setPeerId(peer.getPeerId() != null ? peer.getPeerId() : ""); // 防止 NPE
161 peer.setInfoHash(peer.getInfoHash() != null ? peer.getInfoHash() : "");
162 peer.setUploaded(uploaded);
163 peer.setDownloaded(downloaded);
164 peer.setLeft(left);
165 peer.setLastSeen(LocalDateTime.now());
166 }
167
168 // 辅助方法:处理 peer 事件
169 private void handlePeerEvent(String event, PeerInfoEntity peer, long left) {
170 switch (event) {
171 case "started":
172 peer.setStatus("downloading");
173 peer.setActive(true);
174 break;
175
176 case "stopped":
177 peer.setActive(false);
178 break;
179
180 case "completed":
181 handleCompletedEvent(peer);
182 break;
183
184 case "update":
185 default:
186 handleUpdateEvent(peer, left);
187 break;
188 }
189 }
190
191 // 处理完成事件
192 private void handleCompletedEvent(PeerInfoEntity peer) {
193 peer.setStatus("completed");
194 peer.setActive(true);
195 peer.setLeft(0);
196 incrementCompletedCount(peer.getInfoHash());
197 }
198
199 // 处理更新事件
200 private void handleUpdateEvent(PeerInfoEntity peer, long left) {
201 if (left == 0 && "downloading".equals(peer.getStatus())) {
202 // 检测到下载完成
203 peer.setStatus("completed");
204 incrementCompletedCount(peer.getInfoHash());
205 }
206 peer.setActive(true);
207 }
208
209 // 增加完成次数
210 private void incrementCompletedCount(String infoHash) {
211 TorrentMeta meta = torrentMetaRepository.findByInfoHash(infoHash);
212 if (meta != null) {
213 statsService.incrementCompletedCount(meta.getId());
214 }
215 }
216
217 // 构建 peer 响应
218 private byte[] buildPeerResponse(List<PeerInfoEntity> peers) {
219 List<String> ips = peers.stream().map(PeerInfoEntity::getIp).toList();
220 List<Integer> ports = peers.stream().map(PeerInfoEntity::getPort).toList();
221 return BencodeCodec.buildCompactPeers(ips, ports);
222 }
223
224 // 构建错误响应
225 private byte[] errorResponse(String reason) {
226 return BencodeCodec.encode(Map.of("failure reason", reason));
227 }
228
229 // 解码参数
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +0800230 private String decodeParam(String raw) {
231 return new String(raw.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
232 }
ystxdfd42b32025-06-07 13:26:52 +0800233}