fix(monitor): 博主改名之后,旧作品上的名字也跟着改
Deploy VitePress site to Pages / build (push) Waiting to run
Deploy VitePress site to Pages / Deploy (push) Blocked by required conditions

昵称是**博主级**的属性,而作品是逐条刷的:一个博主只有落在采集窗口里的那几条会被
重刷,掉出窗口的老作品会一直留着旧名字。于是改名之后库里一半新一半旧 ——

* 作品栏组头取的是该博主名下作品的 max(name),**恰好可能取到旧的那个**(比如从
  「BBB」改成「AAA」,max 还是 BBB);
* 导出和 SQL 直接看到旧名字。

两处修:

1. ingest 收完这一轮之后,把这一轮看到的 (博主, 昵称) 铺到该博主在本任务下的
   **全部**作品上。带上 `!= name` 让没变化的不产生写入。只限本任务 —— 另一个任务里
   的同一个人是另一条跟踪线。
2. `list_creators` 的昵称**优先取账号快照**:那一行每次采集都从资料接口重写,是
   最新的一份;小红书那条路没有快照,才退回作品上的名字。

顺带把两份 SQL 里的 COALESCE 顺序调成一致(不然查出来还是旧名字)。
This commit is contained in:
2026-10-11 10:08:58 +08:00
parent af21288dde
commit 36f41450ae
6 changed files with 138 additions and 8 deletions
+25 -1
View File
@@ -40,7 +40,7 @@ from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional, Sequence
from sqlalchemy import func, select
from sqlalchemy import func, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from tools.time_util import get_current_timestamp
@@ -359,6 +359,10 @@ async def _ingest_notes(
# 同一件作品在一轮里进来两次会让第二次插入直接撞键、整个 run 崩掉 —— 产物里重复并不
# 罕见(多个目标指向同一个人、或退化路径重复刷新)。
seen_in_run: set = set()
# 这一轮看到的 (博主哈希 -> 昵称)。**昵称是博主级的属性,而作品是逐条刷的** ——
# 一个博主只有落在采集窗口里的那几条会被重刷,掉出窗口的那些会一直留着旧名字。
# 所以收完这一轮之后,再拿它把该博主在本任务下的作品统一改一遍(见循环之后)。
fresh_names: Dict[str, str] = {}
for record in records:
note_id = adapter.note_field(record, "note_id")
@@ -377,6 +381,9 @@ async def _ingest_notes(
title = (adapter.note_field(record, "title") or "")[:500]
cover = adapter.cover(record)
fresh_name = adapter.note_field(record, "creator_name") or ""
if fresh_name:
fresh_names[adapter.note_field(record, "creator_hash") or ""] = fresh_name
if note is None:
note = MonitorNote(
@@ -430,6 +437,23 @@ async def _ingest_notes(
await _snapshot_metrics(session, run, note_id, record, now, is_baseline)
# 把这一轮的昵称铺到该博主的**全部**作品上,不只这一轮采到的那几条。
# 不做的话,博主改名之后库里会一半是新名字、一半是旧名字:界面按作品名分组时取的是
# 其中任意一个(max),导出和 SQL 更是直接看到旧名字。带上 `!= name` 是为了让没变化
# 的那些不产生写入。
for creator_hash, name in fresh_names.items():
if not creator_hash:
continue
await session.execute(
update(MonitorNote)
.where(
MonitorNote.task_id == run.task_id,
MonitorNote.creator_hash == creator_hash,
MonitorNote.creator_name != name,
)
.values(creator_name=name)
)
return new_count
+7 -4
View File
@@ -945,10 +945,13 @@ async def list_creators(
{
"task_id": row_task,
"creator_hash": creator_hash,
# 昵称优先取作品的(那是界面上本来就在用的),快照的兜底 —— 没有作品
# 的博主只剩快照这一个来源。
"creator_name": (work_row or {}).get("creator_name")
or (stat.nickname if stat else ""),
# 昵称**优先取账号快照**里的。快照每次采集都从资料接口重写一遍,是这一行
# 最新的一份;作品上的名字只会跟着「这一轮采到的那几条」更新,掉出采集
# 窗口的老作品可能还留着旧名字。博主改名之后取作品的会显示旧名。
# 没有快照的(小红书那条路不产生快照)才退回作品上的名字。
"creator_name": (stat.nickname if stat else "")
or (work_row or {}).get("creator_name")
or "",
"creator_alias": aliases.get(
(task_platform.get(row_task, ""), creator_hash), ""
),