feat(content): 自营三平台指标入库管线完善

sync_self_video_metrics 按平台隔离入库与字段口径调整,配套自营指标验收测试
更新。
This commit is contained in:
wangyunlong committed 2026-10-09 13:45:30 +08:00
1 parent 1e9ddffd2f
commit b0e1b5066d
2 files changed
+193 -72

No files matched your search

@@ -466,6 +466,83 @@ def test_collect_xiaohongshu_rows_from_artifact(tmp_path, monkeypatch):
assert row[fields.index("page_metrics")].obj["笔记阅读数"] == "1.2万"
def test_upsert_rows_isolates_single_row_failure():
"""单行写入失败只跳过该行,不拖累整批(保存点隔离)。"""
import contextlib
class _Cursor:
def fetchone(self):
return (1,)
def __getattr__(self, name):
def record(*args, **kwargs):
params = args[1] if len(args) > 1 else kwargs.get("params")
if params is None or len(params) != len(sync_tool.ROW_FIELDS):
return
# 模拟毒行: 标题以 POISON 开头的行违反约束
title = params[sync_tool.ROW_FIELDS.index("title")]
if str(title).startswith("POISON"):
raise RuntimeError("value too long")
return record
class _Conn:
def __init__(self):
self.cursor_stub = _Cursor()
self.committed = False
@contextlib.contextmanager
def cursor(self):
yield self.cursor_stub
@contextlib.contextmanager
def transaction(self):
yield
def commit(self):
self.committed = True
metric_date = dt.date(2026, 10, 1)
rows = [
sync_tool.make_row("bilibili", metric_date, "daily.json",
account_name="光影行星", video_id="BV1"),
sync_tool.make_row("douyin", metric_date, "a.xlsx",
account_name="店铺A", video_id="1",
title="POISON 这行会失败"),
sync_tool.make_row("xiaohongshu", metric_date, "daily.json",
account_name="光影行星GYXX旗舰店", video_id="66e"),
]
conn = _Conn()
written, failed = sync_tool.upsert_rows(conn, rows)
assert (written, failed) == (2, 1)
assert conn.committed
def test_drop_stale_rows_only_removes_matching_sources():
"""陈旧防护按来源文件跳过: 抖音旧导出只清掉抖音行,不拦 B站/小红书。"""
stale_excel = Path("chanmama/旧导出_20260823-20260921.xlsx")
rows = {
("douyin", STORE_FLAGSHIP, "1", dt.date(2026, 10, 1)): sync_tool.make_row(
"douyin", dt.date(2026, 10, 1), stale_excel.name, video_id="1"
),
("bilibili", "光影行星", "BV1", dt.date(2026, 10, 1)): sync_tool.make_row(
"bilibili", dt.date(2026, 10, 1), "daily.json", video_id="BV1"
),
("xiaohongshu", XHS_SHOP, "2", dt.date(2026, 10, 1)): sync_tool.make_row(
"xiaohongshu", dt.date(2026, 10, 1), "daily.json", video_id="2"
),
}
dropped = sync_tool.drop_stale_rows(rows, [stale_excel])
assert dropped == 1
assert set(rows) == {
("bilibili", "光影行星", "BV1", dt.date(2026, 10, 1)),
("xiaohongshu", XHS_SHOP, "2", dt.date(2026, 10, 1)),
}
def test_douyin_history_backfill_uses_file_mtime_and_filename_account(tmp_path):
"""历史回填: 按导出落盘日期各留快照,旧版12列文件按文件名前缀补账号。"""
# 旧版 12 列表头(无 达人昵称/抖音号/蝉妈妈链接)