日期:2026/08/17 涉及文件:
super_vault_daily_spider.py关联表:super_vault_product_record
最近几天(2026/08/13–08/15)入库的数据,completion_time(完成时间)和 sold_time(售罄成交时刻)两个时间字段大量为空(NULL)。需要定位成因并判断能否补齐。
通过按 status 分布统计 + 逐个实测 /app/teamup/detail 接口,得到两条清晰规律:
① completion_time:平台只在团走到 status=9(发货完成)时才生成。
status=9 的团全部有值;status=8/0/10/3 等未完成态源头即为 None(detail 接口实测全部返回 None)。status=8(待发货),尚未走到完成态,平台自身还未生成 completionTime。属正常业务状态,当前无法补齐,等团发货完成、status 变 9 后由日常任务自动带上。② sold_time:平台只在团「售罄」后才生成,且存在历史遗留缺口。
| 库中状态 | detail 现在有 soldTime | 能否补 | 原因 |
|---|---|---|---|
| status=8 待发货(已满员售罄) | 有 | 能补 | 列表首次入库后才售罄,旧值未回写 |
| status=3 已推进到售罄的 | 部分有 | 能补 | 同上 |
| status=9 早期完成团(约 570 条) | 无 | 补不了 | 平台早期无 soldTime 字段,源头即空 |
| status=0 预售未开卖 | 无(soldCount=0) | 不需补 | 尚未售出 |
| status=10 未售罄结束 | 无(soldCount<count) | 补不了 | 未售罄,源头无值 |
根因:列表接口首次入库走 INSERT IGNORE(只增不改),团在入库后才售罄,soldTime 由平台事后生成,旧值不再回写;本应由 update_stale_products 每日刷新补齐,但常驻进程 start_cxx_spider.py(每天 07:01 跑 daily 任务)近两天未运行,导致漏补。
在 super_vault_daily_spider.py 中:
新增轻量专职函数 backfill_sold_time(log, sql_pool, token)
sold_time IS NULL AND sold_count > 0 AND status IN (3, 8)(已售罄/售后阶段、大概率能拿到 soldTime 的团)。/detail,仅当返回 soldTime 时才 UPDATE,且只写 sold_time 单字段,源头无值即跳过,不误写。get_teamup_detail();每次请求 sleep(0.3) 限速。挂进 cxx_daily_main:在 get_vod_list(拉列表)之后、update_stale_products 之前调用,使最新入库的团优先补上 sold_time。
为什么单独做而不复用
update_stale_products:后者是「全字段重刷」的重任务(候选上千、耗时十几分钟,且会每日白查约 570 个源头无 soldTime 的历史团)。把sold_time这一高频刚需拎成轻量步骤(仅扫几十个团、只改单字段),更快、职责单一,即使将来重任务被调整/降频,sold_time仍有专职兜底。
def backfill_sold_time(log, sql_pool, token):
"""精准补齐已售罄团缺失的 sold_time(售罄成交时刻)。"""
rows = sql_pool.select_all(
"SELECT pid FROM super_vault_product_record "
"WHERE sold_time IS NULL AND sold_count > 0 AND status IN (3, 8)")
pids = [r[0] for r in rows] if rows else []
log.info(f"待补 sold_time 的已售罄团 {len(pids)} 个")
filled = 0
for pid in pids:
try:
data = get_teamup_detail(log, pid, token)
if not data:
continue
sold_time = data.get("soldTime")
if not sold_time: # 源头也无(团未真正售罄),跳过不误写
continue
sql_pool.update_one_or_dict(
table="super_vault_product_record",
data={"sold_time": sold_time},
condition={"pid": pid})
filled += 1
except Exception as e:
log.error(f"补 sold_time pid={pid} 失败: {e}")
time.sleep(0.3) # 限速,避免请求过密触发风控
log.info(f"sold_time 补齐完成,成功回写 {filled}/{len(pids)} 个")
# 获取所有 pid
try:
get_vod_list(log, sql_pool, token[0])
except Exception as e:
log.error(f"Error fetching last_product_id: {e}")
# 精准补齐已售罄团(status 3/8)的 sold_time:轻量、只改单字段,最新入库的团先补上
try:
backfill_sold_time(log, sql_pool, token[0])
except Exception as e:
log.error(f"Error backfilling sold_time: {e}")
# 刷新未完成拼团(status!=9)的会变字段 ...
try:
update_stale_products(log, sql_pool, token[0])
except Exception as e:
log.error(f"Error updating stale products: {e}")
对已售罄团执行精准补数:候选 28 个,补齐 23 条(含截图框住的 pid 2825–2837 等),5 条源头无值跳过。8/15 的 16 条 sold_time 已全部补齐。
update_stale_products 都依赖 start_cxx_spider.py(每天 07:01 触发 daily)。最近漏补的真正根因就是该进程未运行——重启后每日任务才会自动补 sold_time。completion_time 当前无法补齐属正常:需等团 status 变为 9(发货完成),平台才生成该字段,届时 update_stale_products 会自动推进 status 并带上完成时间,无需人工干预。sold_time 空且补不了:status=9 早期完成团(约 570 条)源头本就无 soldTime;status=0/10 未售罄团同理。这些非采集遗漏,无需处理。update_stale_products 存在轻微重叠:status=3/8 团在两处都会查一次 detail,每日多约 28 次请求、耗时数秒,可忽略;换来 sold_time 的独立可靠保障。