# super_vault 采集 优化记录 > 日期:2026/08/17 > 涉及文件:`super_vault_daily_spider.py` > 关联表:`super_vault_product_record` ## 1. 优化背景 最近几天(2026/08/13–08/15)入库的数据,`completion_time`(完成时间)和 `sold_time`(售罄成交时刻)两个时间字段大量为空(NULL)。需要定位成因并判断能否补齐。 ### 诊断结论(两个字段成因不同) 通过按 `status` 分布统计 + 逐个实测 `/app/teamup/detail` 接口,得到两条清晰规律: **① `completion_time`:平台只在团走到 `status=9`(发货完成)时才生成。** - 库中 747 条 `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 0 AND status IN (3, 8)`(已售罄/售后阶段、大概率能拿到 soldTime 的团)。 - 逐个查 `/detail`,**仅当返回 soldTime 时才 UPDATE,且只写 `sold_time` 单字段**,源头无值即跳过,不误写。 - 复用现有带重试的 `get_teamup_detail()`;每次请求 `sleep(0.3)` 限速。 2. **挂进 `cxx_daily_main`**:在 `get_vod_list`(拉列表)之后、`update_stale_products` 之前调用,使最新入库的团优先补上 `sold_time`。 > 为什么单独做而不复用 `update_stale_products`:后者是「全字段重刷」的重任务(候选上千、耗时十几分钟,且会每日白查约 570 个源头无 soldTime 的历史团)。把 `sold_time` 这一高频刚需拎成轻量步骤(仅扫几十个团、只改单字段),更快、职责单一,即使将来重任务被调整/降频,`sold_time` 仍有专职兜底。 ## 3. 改动前后对比 ### 新增函数 ```python 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)} 个") ``` ### 主流程接入(cxx_daily_main) ```python # 获取所有 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` 已全部补齐。 ## 4. 注意事项 1. **必须保证常驻进程运行**:新步骤与原有 `update_stale_products` 都依赖 `start_cxx_spider.py`(每天 07:01 触发 daily)。最近漏补的真正根因就是该进程未运行——重启后每日任务才会自动补 `sold_time`。 2. **`completion_time` 当前无法补齐属正常**:需等团 `status` 变为 9(发货完成),平台才生成该字段,届时 `update_stale_products` 会自动推进 status 并带上完成时间,无需人工干预。 3. **仍会有少量 `sold_time` 空且补不了**:`status=9` 早期完成团(约 570 条)源头本就无 `soldTime`;`status=0/10` 未售罄团同理。这些非采集遗漏,无需处理。 4. **与 `update_stale_products` 存在轻微重叠**:`status=3/8` 团在两处都会查一次 detail,每日多约 28 次请求、耗时数秒,可忽略;换来 `sold_time` 的独立可靠保障。