Files
MediaCrawler/docs/queries/account-daily-report.sql
T
butubb d023393bb2
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
docs: 账号日报 SQL(今天的账号情况 + 与昨天比对 + 推送有没有发出去)
两段,因为是两个维度:账号维度每个博主一行(粉丝/获赞/作品数及相对昨天的增减、
今天采到几条),任务维度看推送(推送是按任务的,塞进账号行会把同一个值重复 42 遍)。

推送那段的判定:last_notified_at 只在企业微信 errcode=0 之后才写,所以拿它和一串
run 的结束时间去比,落在两分钟内那轮就是**最后一次真正发出去的那轮**;那之后本该推
却没推的轮次就是漏掉的。**更早的失败无法回溯** —— 失败没留任何记录,而 last_notified_at
只有一个值,会被后来的成功覆盖。文件里写明了这个边界。

实测:42 个账号;粉丝 41 平 1 掉、主页作品数全平;推送最近一次成功是 run 76,之后
0 次漏掉。
2026-10-12 09:20:41 +08:00

123 lines
6.8 KiB
SQL

-- 抖音账号日报:今天的账号情况 + 与昨天比对 + 推送有没有发出去
--
-- 分两段,因为它们是**两个维度**,硬塞进一张表会难看:
--
-- 【一】账号维度:每个博主一行,今天的账号指标、相对昨天的增减、今天的作品情况。
-- 【二】任务维度:推送到底有没有发出去,以及从上次成功之后漏了几次。
--
-- 口径说明:
-- * 「今天」= 该博主**最新一条**账号快照;「昨天」= 最新一条**日期早于今天**的快照。
-- 快照是一轮一条(任务一天跑好几轮),所以按日期取,不按轮次。
-- * 差值为 NULL 表示**没有可比的昨天**(昨天没跑或没采到这个号),不是 0。
-- * 时间戳是毫秒,日期换算统一走 FROM_UNIXTIME(x/1000)。
-- * MySQL 5.7 没有窗口函数,所以「最新一条」还是相关子查询。
-- ===========================================================================
-- 【一】账号日报
-- ===========================================================================
SELECT
al.alias AS 博主备注,
COALESCE(cur.nickname, yest.nickname, '') AS 博主昵称,
cur.fans AS 粉丝数,
cur.fans - yest.fans AS 粉丝增减,
cur.total_favorited AS 总获赞,
cur.total_favorited - yest.total_favorited AS 获赞增减,
cur.works_count AS 主页作品数,
cur.works_count - yest.works_count AS 作品数增减,
(SELECT COUNT(*) FROM monitor_note n
JOIN monitor_task tn ON tn.id = n.task_id
WHERE tn.platform = 'dy' AND n.creator_hash = base.creator_hash) AS 监控中作品,
(SELECT COUNT(*) FROM monitor_note n
JOIN monitor_task tn ON tn.id = n.task_id
WHERE tn.platform = 'dy' AND n.creator_hash = base.creator_hash
AND n.first_seen_at >= UNIX_TIMESTAMP(CURDATE()) * 1000) AS 今日新采到,
FROM_UNIXTIME((SELECT MAX(n.published_at) FROM monitor_note n
JOIN monitor_task tn ON tn.id = n.task_id
WHERE tn.platform = 'dy'
AND n.creator_hash = base.creator_hash) / 1000) AS 最近作品发布,
FROM_UNIXTIME(cur.captured_at / 1000) AS 最近快照,
CASE WHEN yest.id IS NULL THEN '昨天无快照' ELSE '有' END AS 昨日可比
FROM (
-- 账号清单:作品里出现过的 ∪ 有账号资料的(含一条作品都没有的号)
SELECT n1.creator_hash FROM monitor_note n1
JOIN monitor_task t1 ON t1.id = n1.task_id WHERE t1.platform = 'dy'
UNION
SELECT s1.creator_hash FROM monitor_creator_stat s1
JOIN monitor_task t2 ON t2.id = s1.task_id WHERE t2.platform = 'dy'
) base
-- 今天(最新一条快照)
LEFT JOIN monitor_creator_stat cur
ON cur.id = (SELECT s.id FROM monitor_creator_stat s
JOIN monitor_task t3 ON t3.id = s.task_id
WHERE t3.platform = 'dy' AND s.creator_hash = base.creator_hash
ORDER BY s.captured_at DESC, s.id DESC LIMIT 1)
-- 昨天(最新一条日期早于今天的快照)
LEFT JOIN monitor_creator_stat yest
ON yest.id = (SELECT s.id FROM monitor_creator_stat s
JOIN monitor_task t4 ON t4.id = s.task_id
WHERE t4.platform = 'dy' AND s.creator_hash = base.creator_hash
AND DATE(FROM_UNIXTIME(s.captured_at / 1000)) < CURDATE()
ORDER BY s.captured_at DESC, s.id DESC LIMIT 1)
LEFT JOIN monitor_creator_alias al
ON al.platform = 'dy' AND al.creator_hash = base.creator_hash
ORDER BY (cur.fans - yest.fans) IS NULL, cur.fans - yest.fans DESC, cur.fans DESC;
-- ===========================================================================
-- 【二】推送有没有发出去(任务维度)
-- ===========================================================================
--
-- 判定办法,两步:
--
-- 1. `last_notified_at` **只在企业微信返回 errcode=0 之后才写**,所以它是「最近一次
-- 推送成功的时刻」。推送就发生在那一轮跑完之后几秒内 —— 拿它去和一串 run 的结束
-- 时间比,落在两分钟内的那一轮,就是**最后一次真正发出去的那轮**。
-- 2. 那一轮**之后**、本该推送却没有的轮次,就是漏掉的。`漏掉几次` 就是这么数的。
--
-- 为什么不能更精确:失败的推送没有留下任何记录(原因只打到容器 stdout),`last_notified_at`
-- 也只有一个值,会被后来的成功覆盖。要逐轮可查得让它落一条事件或一列原因 —— 那是改代码,
-- 不是这条 SQL 能补的。
--
-- 「本该推送」= 那轮 new_notes > 0,或者出了失败类事件(和 notify.build_run_message 的
-- 判断一致:它按任务上的两个开关过滤事件,没有可推的就返回 None、压根不发)。
SELECT
t.id AS 任务,
t.name AS 名称,
t.notify_enabled AS 推新作品,
t.notify_failures AS 推异常,
FROM_UNIXTIME(t.last_notified_at / 1000) AS 上次推送成功于,
hit.run_id AS 成功的是这轮,
-- 没有匹配行(= 一次没漏)时 LEFT JOIN 给的是 NULL,直接显示会是一片空白,
-- 分不清「零」和「查不到」。这里补成 0。
COALESCE(leaks.漏掉几次, 0) AS 那之后漏掉,
leaks.最近漏掉的那轮 AS 最近漏掉的轮次,
FROM_UNIXTIME(leaks.最近漏掉的时间) AS 它结束于
FROM monitor_task t
-- 找「推送成功时刻」对应的那一轮:结束时间落在它之前两分钟内、且最近的那一轮。
LEFT JOIN (
SELECT r.task_id, MAX(r.id) AS run_id
FROM monitor_run r
JOIN monitor_task t5 ON t5.id = r.task_id
WHERE t5.last_notified_at IS NOT NULL
AND r.finished_at <= t5.last_notified_at
AND t5.last_notified_at - r.finished_at <= 120000
GROUP BY r.task_id
) hit ON hit.task_id = t.id
-- 上次成功之后,本该推送却没推的轮次。
LEFT JOIN (
SELECT r.task_id,
COUNT(*) AS 漏掉几次,
MAX(r.id) AS 最近漏掉的那轮,
MAX(r.finished_at) AS 最近漏掉的时间
FROM monitor_run r
JOIN monitor_task t6 ON t6.id = r.task_id
WHERE r.finished_at > COALESCE(t6.last_notified_at, 0)
AND (r.new_notes > 0
OR EXISTS (SELECT 1 FROM monitor_event e
WHERE e.run_id = r.id
AND e.type IN ('suspected_auth_failure', 'run_failed', 'no_data_found')))
GROUP BY r.task_id
) leaks ON leaks.task_id = t.id
WHERE t.platform = 'dy'
ORDER BY t.id;