|
|
@@ -49,7 +49,7 @@ import os
|
|
|
import re
|
|
|
import sys
|
|
|
import time
|
|
|
-from datetime import datetime
|
|
|
+from datetime import datetime, time as dtime
|
|
|
|
|
|
# 挂靠项目根:复用核心签名/token/请求/代理层,让 application.yml、token.json 生效
|
|
|
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
@@ -69,7 +69,15 @@ core.USE_PROXY = True # 详情接口(groupbuy/detail)走快代理
|
|
|
|
|
|
MERCHANT_ID = "881226408" # 监控商家:采其在售商品的购买记录(WATCH_CODES 为空时生效)
|
|
|
WATCH_CODES = [] # 指定商品白名单:非空则只盯这些 code、跳过在售发现;空则走 MERCHANT_ID 全在售(默认)
|
|
|
-ONSALE_INGEST_SEC = 60 # 全站在售落库 + 刷新监控队列间隔(1 分钟,复用 daily 免 token home/search)
|
|
|
+ONSALE_INGEST_SEC = 60 # 全站在售落库 + 刷新监控队列间隔(常规时段 1 分钟,复用 daily 免 token home/search)
|
|
|
+# 快车高发时段(直播拆卡,通常 20:00~次日 06:00)专用:这类小批量拼团(本商家 31 份)上架后 2~3 分钟即售罄,
|
|
|
+# 寿命只比 60s 发现节奏大一个数量级,偶尔整场卡在两次全站扫描的缝隙里被漏掉(实测 2026/08/22 01:45 一辆 31 份车漏采,
|
|
|
+# 从没进过 deca_onsale_product_record → 从没纳入监控 → deca_buy_record 0 条 → 战报「0 人参与拆卡」)。
|
|
|
+# 故该时段把「全站在售落库 + 刷新监控队列」的节奏压到 FAST_WINDOW_INGEST_SEC 密探,给快车 6~9 次被发现的机会;
|
|
|
+# 时段外恢复 ONSALE_INGEST_SEC,不额外增加白天的全站请求量。全站 home/search 免 token 走直连,加密只抬直连请求量、不涉登录态。
|
|
|
+FAST_WINDOW_INGEST_SEC = 20 # 快车时段全站在售发现间隔(20s,与快车轮询上限 FAST_LANE_MAX_INTERVAL_SEC 对称)
|
|
|
+FAST_WINDOW_START = dtime(20, 0) # 快车高发时段开始:20:00
|
|
|
+FAST_WINDOW_END = dtime(6, 0) # 快车高发时段结束:次日 06:00(窗口跨午夜)
|
|
|
SHOP_DISCOVER_SEC = 600 # 商家发现 + 今日新增补详情间隔(10 分钟,变化慢无需每分钟)
|
|
|
MIN_INTERVAL_SEC = 0 # 单商品购买记录最小采集间隔(测试期设 0:让实测数据决定是否要抬)
|
|
|
MAX_INTERVAL_SEC = 600 # 单商品购买记录最大采集间隔(10 分钟)
|
|
|
@@ -602,6 +610,23 @@ def _refresh_monitored(log, pool, monitored: dict, merchant_id: str, now: float)
|
|
|
log.info(f"当前监控商品数:{len(monitored)}")
|
|
|
|
|
|
|
|
|
+def _current_ingest_interval(now: datetime) -> int:
|
|
|
+ """按当前时刻取「全站在售发现」间隔:快车高发时段(20:00~次日06:00)加密,其余用常规节奏。
|
|
|
+
|
|
|
+ 快车寿命仅 2~3 分钟,60s 的全站发现节奏偶尔会整场错过;故该时段压到 FAST_WINDOW_INGEST_SEC
|
|
|
+ 密探,更快把新车纳入监控。窗口跨午夜,故「晚于开始 或 早于结束」即算命中。
|
|
|
+
|
|
|
+ Args:
|
|
|
+ now (datetime): 当前时间。
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ int: 本轮应使用的全站在售发现间隔(秒):命中快车时段返回 FAST_WINDOW_INGEST_SEC,否则 ONSALE_INGEST_SEC。
|
|
|
+ """
|
|
|
+ t = now.time()
|
|
|
+ in_fast_window = t >= FAST_WINDOW_START or t < FAST_WINDOW_END
|
|
|
+ return FAST_WINDOW_INGEST_SEC if in_fast_window else ONSALE_INGEST_SEC
|
|
|
+
|
|
|
+
|
|
|
@retry(stop=stop_after_attempt(100), wait=wait_fixed(3600), after=after_log)
|
|
|
def main_task(log):
|
|
|
"""常驻主流程:每分钟落全站在售(免 token) + 采本商家在售商品的购买记录。
|
|
|
@@ -614,7 +639,8 @@ def main_task(log):
|
|
|
"""
|
|
|
mode = f"白名单模式 codes={WATCH_CODES}" if WATCH_CODES else f"商家模式 merchant={MERCHANT_ID}"
|
|
|
log.info(f"购买记录+在售常驻采集启动 | {mode} | 购买记录频率 [{MIN_INTERVAL_SEC}s, {MAX_INTERVAL_SEC}s] "
|
|
|
- f"| 在售落库每 {ONSALE_INGEST_SEC}s | 全链路免 token | 详情代理={core.USE_PROXY}")
|
|
|
+ f"| 在售发现常规每 {ONSALE_INGEST_SEC}s、快车时段({FAST_WINDOW_START:%H:%M}~次日{FAST_WINDOW_END:%H:%M})每 {FAST_WINDOW_INGEST_SEC}s "
|
|
|
+ f"| 全链路免 token | 详情代理={core.USE_PROXY}")
|
|
|
pool = MySQLConnectionPool(log=log)
|
|
|
if not pool.check_pool_health():
|
|
|
log.error("数据库连接池异常")
|
|
|
@@ -627,8 +653,9 @@ def main_task(log):
|
|
|
while True:
|
|
|
now = time.time()
|
|
|
|
|
|
- # 1) 每 ONSALE_INGEST_SEC:商家模式先落全站在售(免token)再查库刷新监控;白名单模式只按固定 code 刷新
|
|
|
- if now - last_ingest >= ONSALE_INGEST_SEC:
|
|
|
+ # 1) 每(动态)间隔:商家模式先落全站在售(免token)再查库刷新监控;白名单模式只按固定 code 刷新。
|
|
|
+ # 快车高发时段(20:00~次日06:00)把发现节奏加密到 FAST_WINDOW_INGEST_SEC,更快把 2~3 分钟即售罄的快车纳入监控。
|
|
|
+ if now - last_ingest >= _current_ingest_interval(datetime.now()):
|
|
|
try:
|
|
|
if not WATCH_CODES:
|
|
|
discover = (now - last_discover >= SHOP_DISCOVER_SEC)
|