tt-help-cli-ycl 1.3.8 → 1.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tt-help-cli-ycl",
3
- "version": "1.3.8",
3
+ "version": "1.3.9",
4
4
  "description": "TikTok user & video data scraper - extract ttSeller, verified, locationCreated from HTML source",
5
5
  "type": "module",
6
6
  "bin": {
@@ -108,6 +108,7 @@
108
108
  .controls { flex-wrap: wrap; gap: 6px; }
109
109
  .controls input { flex: 0 0 100%; width: 100%; }
110
110
  .controls button { flex: 0 0 calc(33.33% - 4px); min-width: 0; text-align: center; white-space: nowrap; font-size: 11px; padding: 8px 4px; }
111
+ #batchResetBtn { flex: 0 0 100% !important; font-size: 12px !important; padding: 8px 12px !important; }
111
112
  .controls select { flex: 0 0 100%; width: 100%; }
112
113
  .table-scroll { max-height: none; overflow: visible; }
113
114
  table, thead, tbody, th, td, tr { display: block; }
@@ -169,6 +170,7 @@
169
170
  <button data-filter="error" onclick="setFilter('error')">错误</button>
170
171
  <button data-filter="restricted" onclick="setFilter('restricted')">受限</button>
171
172
  <button data-filter="target" onclick="setFilter('target')" style="background:#7c3aed;color:#fff">目标用户</button>
173
+ <button id="batchResetBtn" onclick="batchResetErrors()" style="display:none;padding:6px 10px;border:1px solid #f87171;border-radius:6px;background:transparent;color:#f87171;font-size:12px;cursor:pointer;font-weight:600;transition:all 0.2s;white-space:nowrap;">&#x21bb; 批量重新处理 (<span id="batchResetCount">0</span>)</button>
172
174
  <select id="locationFilter" onchange="onLocationChange()" style="padding:6px 10px;border:1px solid #333;border-radius:6px;background:#2a2a3a;color:#ccc;font-size:12px;cursor:pointer;outline:none;">
173
175
  <option value="">全部国家</option>
174
176
  </select>
@@ -324,6 +326,10 @@ function renderTable(users) {
324
326
  </tr>`;
325
327
  }).join('');
326
328
 
329
+ const errorCount = users.filter(u => u.status === 'error').length;
330
+ const countEl = document.getElementById('batchResetCount');
331
+ if (countEl) countEl.textContent = errorCount;
332
+
327
333
  prevUserMap = newUserMap;
328
334
  }
329
335
 
@@ -344,6 +350,8 @@ function setFilter(f) {
344
350
  document.querySelectorAll('.controls button').forEach(b => {
345
351
  b.classList.toggle('active', b.dataset.filter === f);
346
352
  });
353
+ const btn = document.getElementById('batchResetBtn');
354
+ btn.style.display = f === 'error' ? '' : 'none';
347
355
  fetchUsers();
348
356
  }
349
357
 
@@ -548,6 +556,32 @@ async function resetJob(uniqueId) {
548
556
  }
549
557
  }
550
558
 
559
+ async function batchResetErrors() {
560
+ const errorUsers = currentUsers.filter(u => u.status === 'error');
561
+ if (errorUsers.length === 0) {
562
+ showToast('\u6ca1\u6709\u9700\u8981\u91cd\u7f6e\u7684\u9519\u8bef\u7528\u6237', true);
563
+ return;
564
+ }
565
+ const userIds = errorUsers.map(u => u.uniqueId);
566
+ try {
567
+ const res = await fetch('/api/jobs/batch-reset', {
568
+ method: 'POST',
569
+ headers: { 'Content-Type': 'application/json' },
570
+ body: JSON.stringify({ userIds })
571
+ });
572
+ const data = await res.json();
573
+ if (data.error) {
574
+ showToast(data.error, true);
575
+ return;
576
+ }
577
+ showToast(`\u5df2\u91cd\u7f6e ${data.reset} / ${data.total} \u4e2a\u7528\u6237`);
578
+ fetchUsers();
579
+ fetchStats();
580
+ } catch (e) {
581
+ showToast('\u6279\u91cf\u91cd\u7f6e\u5931\u8d25: ' + e.message, true);
582
+ }
583
+ }
584
+
551
585
  document.getElementById('statTargetCard').addEventListener('click', async () => {
552
586
  try {
553
587
  const res = await fetch('/api/target-users');
@@ -575,8 +609,8 @@ document.getElementById('statTargetCard').addEventListener('click', async () =>
575
609
 
576
610
  fetchStats();
577
611
  fetchUsers();
578
- setInterval(fetchStats, 1000);
579
- setInterval(fetchUsers, 2000);
612
+ setInterval(fetchStats, 10000);
613
+ setInterval(fetchUsers, 10000);
580
614
  </script>
581
615
  </body>
582
616
  </html>
@@ -187,6 +187,22 @@ export function startWatchServer(outputFile, port = 3000, existingStore) {
187
187
  return;
188
188
  }
189
189
 
190
+ if (req.method === 'POST' && routePath === '/api/jobs/batch-reset') {
191
+ const body = await readBody(req);
192
+ const ids = Array.isArray(body.userIds) ? body.userIds : [];
193
+ if (ids.length === 0) {
194
+ sendJSON(res, 400, { error: 'userIds 不能为空' });
195
+ return;
196
+ }
197
+ let count = 0;
198
+ for (const uid of ids) {
199
+ const ret = store.resetJob(uid);
200
+ if (ret.saved) count++;
201
+ }
202
+ sendJSON(res, 200, { reset: count, total: ids.length });
203
+ return;
204
+ }
205
+
190
206
  const jobPinMatch = routePath.match(/^\/api\/job\/([^/]+)\/pin$/);
191
207
  if (req.method === 'POST' && jobPinMatch) {
192
208
  const uniqueId = jobPinMatch[1];