tt-help-cli-ycl 1.4.144 → 1.4.145

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.
@@ -114,6 +114,22 @@ const STAT_GROUPS = {
114
114
  FROM jobs_done
115
115
  )`,
116
116
  },
117
+ {
118
+ // 精确商家计数:目标商家 + 7 天 EC 视频播放>1000 + 有商品 + 评论0 + 销量>10
119
+ key: "preciseMerchants",
120
+ mode: "one",
121
+ params: () => getTargetLocations(),
122
+ sql: () => `SELECT COUNT(*) AS n FROM jobs
123
+ WHERE tt_seller = 1 AND verified = 0 AND ${targetInSql()}
124
+ AND top_video_seo_url IS NOT NULL AND top_video_seo_url != ''
125
+ AND product_review_count = 0
126
+ AND product_sold_count > 10
127
+ AND recent_ec_videos IS NOT NULL AND recent_ec_videos != ''
128
+ AND EXISTS (
129
+ SELECT 1 FROM json_each(recent_ec_videos)
130
+ WHERE CAST(json_extract(value, '$.playCount') AS INTEGER) > 1000
131
+ )`,
132
+ },
117
133
  ],
118
134
  combine(results) {
119
135
  const tq = results.tqAgg || {};
@@ -129,6 +145,7 @@ const STAT_GROUPS = {
129
145
  restrictedUsers: jb.restricted || 0,
130
146
  errorUsers: jb.error || 0,
131
147
  targetUsers: jb.targetUsers || 0,
148
+ preciseMerchants: results.preciseMerchants?.n || 0,
132
149
  sourceStats: {
133
150
  seed: tq.seed || 0,
134
151
  video: tq.video || 0,
@@ -256,6 +273,7 @@ const EMPTY_GROUP = {
256
273
  restrictedUsers: 0,
257
274
  errorUsers: 0,
258
275
  targetUsers: 0,
276
+ preciseMerchants: 0,
259
277
  sourceStats: {
260
278
  seed: 0,
261
279
  video: 0,
@@ -1551,6 +1569,48 @@ export function startWatchServer(
1551
1569
  return;
1552
1570
  }
1553
1571
 
1572
+ // GET /api/precise-merchants — 精确商家列表(支持分页+搜索)
1573
+ if (req.method === "GET" && routePath === "/api/precise-merchants") {
1574
+ const result = store.getPreciseMerchants(getTargetLocations(), {
1575
+ search: params.search || undefined,
1576
+ limit: params.limit ? parseInt(params.limit, 10) : undefined,
1577
+ offset: params.offset ? parseInt(params.offset, 10) : undefined,
1578
+ });
1579
+ if (result && result.users) {
1580
+ result.users = result.users.map((u) => ({
1581
+ uniqueId: u.uniqueId,
1582
+ nickname: u.nickname || "",
1583
+ locationCreated: u.locationCreated || null,
1584
+ followerCount: u.followerCount || 0,
1585
+ videoCount: u.videoCount || 0,
1586
+ latestVideoTime: u.latestVideoTime || null,
1587
+ topVideoPlayCount: u.topVideoPlayCount || null,
1588
+ topVideoHref: u.topVideoHref || null,
1589
+ topVideoIsAd: u.topVideoIsAd ?? null,
1590
+ topVideoProductId: u.topVideoProductId || null,
1591
+ topVideoProductTitle: u.topVideoProductTitle || null,
1592
+ topVideoSeoUrl: u.topVideoSeoUrl || null,
1593
+ productRating: u.productRating ?? null,
1594
+ productReviewCount: u.productReviewCount ?? null,
1595
+ productSoldCount: u.productSoldCount ?? null,
1596
+ productName: u.productName || null,
1597
+ ecOver1k: u.ecOver1k || 0,
1598
+ maxEcPlay: u.maxEcPlay || null,
1599
+ recentEcVideos: (() => {
1600
+ try {
1601
+ return Array.isArray(u.recentEcVideos)
1602
+ ? u.recentEcVideos
1603
+ : JSON.parse(u.recentEcVideos || "[]");
1604
+ } catch {
1605
+ return [];
1606
+ }
1607
+ })(),
1608
+ }));
1609
+ }
1610
+ sendJSON(res, 200, result);
1611
+ return;
1612
+ }
1613
+
1554
1614
  if (
1555
1615
  req.method === "GET" &&
1556
1616
  routePath === "/api/target-users-by-country"
@@ -22,8 +22,11 @@ import { pushSelfLog } from "./self-log-buffer.js";
22
22
  const MAX_RESTART = 3;
23
23
  const MAX_LOG_LINES = 500; // 每个进程最多保留的日志行数
24
24
  const IS_WIN = process.platform === "win32";
25
- const SPAWN_STAGGER_MS = 30000; // 批量启动时每个进程间隔 30 秒,避免抢网络
26
- const STOP_STAGGER_MS = 10000; // 强制重启时每个命令停止间隔 10 秒(杀 Edge/进程树会瞬时占资源,错峰释放)
25
+ const SPAWN_STAGGER_MS = 30000; // syncCommands 批量启动时每个进程间隔 30 秒,避免抢网络
26
+ // 代理切换后强制重启(restartAll)专用错峰间隔:切换出口后所有命令集中重建连接,
27
+ // 间隔给长一点,避免瞬时抢网络/触发风控(2026-08-29 按需加长)
28
+ const RESTART_STOP_STAGGER_MS = 20000; // 停止错峰 20s(杀 Edge/进程树瞬时占资源,错峰释放)
29
+ const RESTART_SPAWN_STAGGER_MS = 60000; // 启动错峰 60s
27
30
  const STALE_LOG_TIMEOUT_MS = 15 * 60 * 1000; // 15 分钟无日志输出视为假死
28
31
 
29
32
  // === 控制台日志格式化(仅显示层增强,不影响任何功能链路) ===
@@ -739,9 +742,10 @@ export class ProcessManager extends EventEmitter {
739
742
  console.error(
740
743
  `[watchdog] ${reason},强制重启全部命令 (${toRestart.length}个)...`,
741
744
  );
742
- // 错峰停止:第一个立即停,之后每个间隔 STOP_STAGGER_MS(平滑释放资源,避免同时杀 N 个浏览器)
745
+ // 错峰停止:第一个立即停,之后每个间隔 RESTART_STOP_STAGGER_MS(平滑释放资源,避免同时杀 N 个浏览器)
743
746
  for (let i = 0; i < toRestart.length; i++) {
744
- if (i > 0) await new Promise((r) => setTimeout(r, STOP_STAGGER_MS));
747
+ if (i > 0)
748
+ await new Promise((r) => setTimeout(r, RESTART_STOP_STAGGER_MS));
745
749
  if (this.shutdown) {
746
750
  console.error("[watchdog] 看门狗已停止,中止停止命令");
747
751
  break;
@@ -751,9 +755,10 @@ export class ProcessManager extends EventEmitter {
751
755
  `[watchdog] 命令 ${toRestart[i].id} 已停止 (${i + 1}/${toRestart.length})`,
752
756
  );
753
757
  }
754
- // 错峰逐个重启:第一个立即起,之后每个间隔 SPAWN_STAGGER_MS
758
+ // 错峰逐个重启:第一个立即起,之后每个间隔 RESTART_SPAWN_STAGGER_MS
755
759
  for (let i = 0; i < toRestart.length; i++) {
756
- if (i > 0) await new Promise((r) => setTimeout(r, SPAWN_STAGGER_MS));
760
+ if (i > 0)
761
+ await new Promise((r) => setTimeout(r, RESTART_SPAWN_STAGGER_MS));
757
762
  if (this.shutdown) {
758
763
  console.error("[watchdog] 看门狗已停止,中止重启命令");
759
764
  break;