blob: 1b388061039744feddeab5aced7f374936982623 [file] [log] [blame]
rootcd436562025-05-08 14:09:19 +00001package tracker;
root0d8b11f2025-05-15 14:10:43 +00002import java.io.File;
rootd4959a82025-05-27 07:07:37 +00003import java.nio.file.Files;
4import java.nio.file.Path;
5import java.nio.file.Paths;
6import java.nio.file.StandardCopyOption;
TRM-codingd5de51e2025-06-08 03:27:01 +08007import java.security.MessageDigest;
rootd4959a82025-05-27 07:07:37 +00008import java.time.LocalDateTime;
TRM-codingd5de51e2025-06-08 03:27:01 +08009import java.util.HashMap;
10import java.util.List;
11import java.util.Map;
12import java.util.UUID;
13import javax.persistence.*;
14import com.dampcake.bencode.Bencode;
15import com.dampcake.bencode.Type;
16import com.querydsl.jpa.impl.JPAUpdateClause;
17import entity.*;
18import entity.config;
TRM-coding508b31f2025-06-09 02:07:14 +080019import java.util.Scanner;
20import java.io.IOException;
root0d8b11f2025-05-15 14:10:43 +000021public class Tracker implements TrackerInterface {
rootff0769a2025-05-18 17:24:41 +000022 private final EntityManagerFactory emf;
rootff0769a2025-05-18 17:24:41 +000023 // 默认构造:产线数据库
24 public Tracker() {
25 config cfg = new config();
26 Map<String,Object> props = new HashMap<>();
27 props.put("javax.persistence.jdbc.url",
28 "jdbc:mysql://" + cfg.SqlURL + "/" + cfg.Database);
29 props.put("javax.persistence.jdbc.user", cfg.SqlUsername);
30 props.put("javax.persistence.jdbc.password", cfg.SqlPassword);
31 this.emf = Persistence.createEntityManagerFactory("myPersistenceUnit", props);
32 }
rootff0769a2025-05-18 17:24:41 +000033 // 测试传入:测试库
34 public Tracker(EntityManagerFactory emf) {
35 this.emf = emf;
36 }
root0d8b11f2025-05-15 14:10:43 +000037 @Override
TRM-codingd5de51e2025-06-08 03:27:01 +080038 public boolean AddUpLoad(String userid, int upload, String infoHash) {
39 long newTotal = upload; // convert to long
root0d8b11f2025-05-15 14:10:43 +000040 EntityManager em = emf.createEntityManager();
41 EntityTransaction tx = em.getTransaction();
TRM-codingd5de51e2025-06-08 03:27:01 +080042 tx.begin();
root0d8b11f2025-05-15 14:10:43 +000043 try {
TRM-codingd5de51e2025-06-08 03:27:01 +080044 // 1) find the seedId by infoHash
45 String seedId = em.createQuery(
46 "SELECT s.seedId FROM SeedHash s WHERE s.infoHash = :ih", String.class)
47 .setParameter("ih", infoHash)
48 .getSingleResult();
49 // 2) sum existing uploads for this user+seed
50 Long sumSoFar = em.createQuery(
51 "SELECT COALESCE(SUM(t.upload),0) FROM TransRecord t WHERE t.uploaduserid = :uid AND t.seedid = :sid",
52 Long.class)
53 .setParameter("uid", userid)
54 .setParameter("sid", seedId)
55 .getSingleResult();
56 long delta = newTotal - sumSoFar;
57 if (delta < 0L) {
58 tx.rollback();
TRM-coding508b31f2025-06-09 02:07:14 +080059 return false; // error: newTotal less than already recorded
TRM-codingd5de51e2025-06-08 03:27:01 +080060 }
61 if (delta == 0L) {
62 tx.rollback();
63 return false; // nothing to do
64 }
65 // 3) persist a new TransRecord with only the delta
66 TransRecord rd = new TransRecord();
67 rd.taskid = UUID.randomUUID().toString();
68 rd.uploaduserid = userid;
69 rd.seedid = seedId;
70 rd.upload = delta;
71 rd.maxupload = newTotal;
72 em.persist(rd);
TRM-coding508b31f2025-06-09 02:07:14 +080073 em.flush();
TRM-codingd5de51e2025-06-08 03:27:01 +080074 // 4) 重新计算用户的总上传,确保与 TransRecord 完全一致
75 Long totalUpload = em.createQuery(
76 "SELECT COALESCE(SUM(t.upload),0) FROM TransRecord t WHERE t.uploaduserid = :uid",
77 Long.class
78 )
79 .setParameter("uid", userid)
80 .getSingleResult();
TRM-coding508b31f2025-06-09 02:07:14 +080081
82 Long PTuploadbefor = em.createQuery(
83 "SELECT t.upload FROM UserPT t WHERE t.userid = :uid",
84 Long.class
85 ).setParameter("uid", userid)
86 .getSingleResult();
87
88
TRM-codingd5de51e2025-06-08 03:27:01 +080089 UserPT user = em.find(UserPT.class, userid);
90 user.upload = totalUpload;
91 em.merge(user);
TRM-coding508b31f2025-06-09 02:07:14 +080092 em.flush();
root0d8b11f2025-05-15 14:10:43 +000093 tx.commit();
TRM-coding508b31f2025-06-09 02:07:14 +080094
95 Long PTuploadafter = em.createQuery(
96 "SELECT t.upload FROM UserPT t WHERE t.userid = :uid",
97 Long.class
98 ).setParameter("uid", userid)
99 .getSingleResult();
100
101 System.out.println("按回车继续...");
102 System.out.printf("thisadd:%d userptsofar:%d userptafter:%d totaluploadnow:%d delta:%d%n",upload, PTuploadbefor,PTuploadafter, totalUpload,delta);
103 System.out.println("按回车继续...");
TRM-codingd5de51e2025-06-08 03:27:01 +0800104 return false; // success
105 } catch (RuntimeException ex) {
root0d8b11f2025-05-15 14:10:43 +0000106 if (tx.isActive()) tx.rollback();
TRM-codingd5de51e2025-06-08 03:27:01 +0800107 throw ex;
root0d8b11f2025-05-15 14:10:43 +0000108 } finally {
109 em.close();
110 }
111 }
TRM-codingd5de51e2025-06-08 03:27:01 +0800112
root0d8b11f2025-05-15 14:10:43 +0000113 @Override
rootff0769a2025-05-18 17:24:41 +0000114 public boolean ReduceUpLoad(String userid, int upload){
TRM-codingd5de51e2025-06-08 03:27:01 +0800115 long uploadLong = upload; // convert to long
rootff0769a2025-05-18 17:24:41 +0000116 EntityManager em = emf.createEntityManager();
117 EntityTransaction tx = em.getTransaction();
TRM-codingd5de51e2025-06-08 03:27:01 +0800118 tx.begin();
rootff0769a2025-05-18 17:24:41 +0000119 try {
TRM-codingd5de51e2025-06-08 03:27:01 +0800120 // 1) fetch user and ensure enough upload to reduce
121 UserPT user = em.find(UserPT.class, userid);
122 long before = user.upload;
123 if (uploadLong > before) {
124 tx.rollback();
125 return true; // error: cannot reduce more than current total
126 }
127 // 2) subtract
128 user.upload = before - uploadLong;
129 em.merge(user);
130 // (optional) record a negative TransRecord so sums stay in sync
131 TransRecord rd = new TransRecord();
132 rd.taskid = UUID.randomUUID().toString();
133 rd.uploaduserid = userid;
134 rd.seedid = null;
135 rd.upload = -uploadLong;
136 rd.maxupload = user.upload;
137 em.persist(rd);
rootff0769a2025-05-18 17:24:41 +0000138 tx.commit();
TRM-codingd5de51e2025-06-08 03:27:01 +0800139 return false; // success
140 } catch (RuntimeException ex) {
rootff0769a2025-05-18 17:24:41 +0000141 if (tx.isActive()) tx.rollback();
TRM-codingd5de51e2025-06-08 03:27:01 +0800142 throw ex;
rootff0769a2025-05-18 17:24:41 +0000143 } finally {
144 em.close();
145 }
146 }
root0d8b11f2025-05-15 14:10:43 +0000147 @Override
TRM-codingd5de51e2025-06-08 03:27:01 +0800148 public boolean AddDownload(String userid, int download, String infoHash) {
149 long newTotal = download; // convert to long
rootf35409f2025-05-19 04:41:57 +0000150 EntityManager em = emf.createEntityManager();
151 EntityTransaction tx = em.getTransaction();
152 try {
TRM-codingd5de51e2025-06-08 03:27:01 +0800153 // 1. 查 SeedHash
154 TypedQuery<SeedHash> qsh = em.createQuery(
155 "SELECT s FROM SeedHash s WHERE s.infoHash = :h", SeedHash.class);
156 qsh.setParameter("h", infoHash);
157 List<SeedHash> shl = qsh.getResultList();
158 if (shl.isEmpty()) {
159 System.out.println("seed没有被记录");
TRM-coding508b31f2025-06-09 02:07:14 +0800160 return false;
TRM-codingd5de51e2025-06-08 03:27:01 +0800161 }
162 String seedid = shl.get(0).seedId;
163
164 // 2. 统计该用户在该种子上的已有 download
165 TypedQuery<Long> qsum = em.createQuery(
166 "SELECT COALESCE(SUM(t.download),0) FROM TransRecord t " +
167 "WHERE t.seedid = :sid AND t.downloaduserid = :uid", Long.class);
168 qsum.setParameter("sid", seedid);
169 qsum.setParameter("uid", userid);
170 long oldSeedSum = qsum.getSingleResult();
171
172 long diff = newTotal - oldSeedSum;
173 if (diff <= 0) return false;
174
175 System.out.println("AddDownload: 该种子原有总量=" + oldSeedSum + ", 新总量=" + newTotal + ", 增量=" + diff);
176
177 try {
178 tx.begin();
179 // 1. persist 增量记录
180 TransRecord tr = new TransRecord();
181 tr.taskid = UUID.randomUUID().toString();
182 tr.downloaduserid = userid;
183 tr.seedid = seedid;
184 tr.download = diff;
185 tr.maxdownload = newTotal;
186 em.persist(tr);
187
188 // 2. 全表重新累计该用户所有种子的 download,并更新 UserPT.download
189 TypedQuery<Long> qTotal = em.createQuery(
190 "SELECT COALESCE(SUM(t.download),0) FROM TransRecord t WHERE t.downloaduserid = :uid",
191 Long.class
192 )
193 .setParameter("uid", userid);
194 long userTotalDownload = qTotal.getSingleResult();
195 QUserPT quser = QUserPT.userPT;
196 new JPAUpdateClause(em, quser)
197 .where(quser.userid.eq(userid))
198 .set(quser.download, userTotalDownload)
199 .execute();
200
201 tx.commit();
202 return false;
203 } catch (Exception e) {
204 if (tx.isActive()) tx.rollback();
205 return true;
206 } finally {
207 em.close();
208 }
209 } catch (Exception e) {
rootf35409f2025-05-19 04:41:57 +0000210 return true;
rootf35409f2025-05-19 04:41:57 +0000211 }
212 }
root0d8b11f2025-05-15 14:10:43 +0000213 @Override
rootf35409f2025-05-19 04:41:57 +0000214 public boolean ReduceDownload(String userid, int download) {
TRM-codingd5de51e2025-06-08 03:27:01 +0800215 long downloadLong = download; // convert to long
rootf35409f2025-05-19 04:41:57 +0000216 EntityManager em = emf.createEntityManager();
rootf35409f2025-05-19 04:41:57 +0000217 try {
TRM-codingd5de51e2025-06-08 03:27:01 +0800218 // 1. 预检查当前值
219 TypedQuery<Long> qcurr = em.createQuery(
220 "SELECT u.download FROM UserPT u WHERE u.userid = :uid", Long.class);
221 qcurr.setParameter("uid", userid);
222 long current = qcurr.getSingleResult();
223 if (downloadLong > current) {
224 em.close();
TRM-coding508b31f2025-06-09 02:07:14 +0800225 return false;
TRM-codingd5de51e2025-06-08 03:27:01 +0800226 }
227 // 2. 执行减法更新
228 EntityTransaction tx = em.getTransaction();
rootf35409f2025-05-19 04:41:57 +0000229 tx.begin();
230 QUserPT q = QUserPT.userPT;
TRM-codingd5de51e2025-06-08 03:27:01 +0800231 new JPAUpdateClause(em, q)
rootf35409f2025-05-19 04:41:57 +0000232 .where(q.userid.eq(userid))
TRM-codingd5de51e2025-06-08 03:27:01 +0800233 .set(q.download, q.download.subtract(downloadLong))
rootf35409f2025-05-19 04:41:57 +0000234 .execute();
235 tx.commit();
TRM-codingd5de51e2025-06-08 03:27:01 +0800236 return false;
rootf35409f2025-05-19 04:41:57 +0000237 } catch(Exception e) {
rootf35409f2025-05-19 04:41:57 +0000238 return true;
239 } finally {
TRM-codingd5de51e2025-06-08 03:27:01 +0800240 if (em.isOpen()) em.close();
rootf35409f2025-05-19 04:41:57 +0000241 }
242 }
root0d8b11f2025-05-15 14:10:43 +0000243 @Override
rootf35409f2025-05-19 04:41:57 +0000244 public boolean AddMagic(String userid, int magic) {
245 EntityManager em = emf.createEntityManager();
246 EntityTransaction tx = em.getTransaction();
247 try {
248 tx.begin();
249 QUserPT q = QUserPT.userPT;
250 long updated = new JPAUpdateClause(em, q)
251 .where(q.userid.eq(userid))
252 .set(q.magic, q.magic.add(magic))
253 .execute();
254 tx.commit();
255 return updated <= 0;
256 } catch(Exception e) {
257 if (tx.isActive()) tx.rollback();
258 return true;
259 } finally {
260 em.close();
261 }
262 }
root0d8b11f2025-05-15 14:10:43 +0000263 @Override
rootf35409f2025-05-19 04:41:57 +0000264 public boolean ReduceMagic(String userid, int magic) {
265 EntityManager em = emf.createEntityManager();
266 EntityTransaction tx = em.getTransaction();
267 try {
268 tx.begin();
269 QUserPT q = QUserPT.userPT;
270 long updated = new JPAUpdateClause(em, q)
271 .where(q.userid.eq(userid))
272 .set(q.magic, q.magic.subtract(magic))
273 .execute();
274 tx.commit();
275 return updated <= 0;
276 } catch(Exception e) {
277 if (tx.isActive()) tx.rollback();
278 return true;
279 } finally {
280 em.close();
281 }
282 }
rootd4959a82025-05-27 07:07:37 +0000283 @Override
284 public int SaveTorrent(String seedid, File TTorent){
285 try {
286 Path storageDir = Paths.get(config.TORRENT_STORAGE_DIR);
287 if (!Files.exists(storageDir)) {
288 Files.createDirectories(storageDir);
289 }
290 String filename = TTorent.getName();
291 Path target = storageDir.resolve(seedid + "_" + filename);
292 Files.copy(TTorent.toPath(), target, StandardCopyOption.REPLACE_EXISTING);
TRM-codingd5de51e2025-06-08 03:27:01 +0800293
TRM-coding508b31f2025-06-09 02:07:14 +0800294 // attempt to parse infoHash, but don't fail if parsing fails
TRM-codingd5de51e2025-06-08 03:27:01 +0800295 String infoHash = null;
296 try {
297 byte[] torrentData = Files.readAllBytes(target);
298 Bencode bencode = new Bencode();
299 @SuppressWarnings("unchecked")
TRM-coding508b31f2025-06-09 02:07:14 +0800300 Map<String,Object> meta = bencode.decode(torrentData, Type.DICTIONARY);
301 @SuppressWarnings("unchecked")
302 Map<String,Object> info = (Map<String,Object>) meta.get("info");
303 byte[] infoBytes = bencode.encode(info);
TRM-codingd5de51e2025-06-08 03:27:01 +0800304 MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
305 byte[] digest = sha1.digest(infoBytes);
306 StringBuilder sb = new StringBuilder();
307 for (byte b1 : digest) {
308 sb.append(String.format("%02x", b1));
309 }
310 infoHash = sb.toString();
311 } catch (Exception e) {
312 System.err.println("Warning: could not parse torrent infoHash: " + e.getMessage());
313 }
314
rootd4959a82025-05-27 07:07:37 +0000315 EntityManager em = emf.createEntityManager();
316 EntityTransaction tx = em.getTransaction();
317 try {
318 tx.begin();
319 Seed seed = em.find(Seed.class, seedid);
320 seed.url = target.toString();
321 em.merge(seed);
TRM-codingd5de51e2025-06-08 03:27:01 +0800322
323 // upsert SeedHash only if we have a valid infoHash
324 if (infoHash != null) {
325 SeedHash sh = new SeedHash();
326 sh.seedId = seedid;
327 sh.infoHash = infoHash;
328 em.merge(sh);
329 }
rootd4959a82025-05-27 07:07:37 +0000330 tx.commit();
331 return 0;
332 } catch (Exception e) {
333 if (tx.isActive()) tx.rollback();
334 return 1;
335 } finally {
336 em.close();
337 }
338 } catch (Exception e) {
339 return 1;
340 }
341 }
root0d8b11f2025-05-15 14:10:43 +0000342 @Override
Raveraae06122025-06-05 08:13:35 +0000343 public File GetTTorent(String seedid, String userid) {
rootd4959a82025-05-27 07:07:37 +0000344 EntityManager em = emf.createEntityManager();
345 EntityTransaction tx = em.getTransaction();
346 File file = null;
347 try {
348 Seed seed = em.find(Seed.class, seedid);
349 if (seed == null || seed.url == null) {
350 return null;
351 }
352 file = new File(seed.url);
353 if (!file.exists()) {
354 return null;
355 }
356 tx.begin();
357 SeedDownload sd = new SeedDownload();
358 sd.seedId = seedid;
359 sd.userId = userid;
rootd4959a82025-05-27 07:07:37 +0000360 LocalDateTime now = LocalDateTime.now();
361 sd.downloadStart = now;
362 sd.downloadEnd = now;
363 em.persist(sd);
364 tx.commit();
365 } catch (Exception e) {
366 if (tx.isActive()) tx.rollback();
367 // ignore persistence errors and still return the file
368 } finally {
369 em.close();
370 }
371 return file;
372 }
root0d8b11f2025-05-15 14:10:43 +0000373 @Override
rootf35409f2025-05-19 04:41:57 +0000374 public int AddRecord(TransRecord rd){
375 EntityManager em = emf.createEntityManager();
376 EntityTransaction tx = em.getTransaction();
377 try {
378 tx.begin();
379 em.persist(rd);
380 tx.commit();
TRM-codingd5de51e2025-06-08 03:27:01 +0800381 // 返回1表示插入成功
rootf35409f2025-05-19 04:41:57 +0000382 return 1;
383 } catch (Exception e) {
384 if (tx.isActive()) tx.rollback();
385 return -1;
386 } finally {
387 em.close();
388 }
389 }
root33a7d952025-05-18 17:24:41 +0000390}