- 新包 internal/runtime/events:Tier/Category/Platform/ChannelType/ActorRole 具名常量 + Event/Filter/Counter + Store 六方法 - 留存口径:T2/T3 逐条落行;T1 只计数;unknown / decode_error / link 三例外逐条落行(链路是否活着是事故定案唯一证据) - 计数:内存聚合 + 上限 512 的有界缓冲 + 满即整批 upsert,落盘失败回填而不丢;GC:保留期 720h/间隔 1h,≤0 = 关闭 - 迁移 0021_channel_events(两表 + 6 索引,含计数 upsert 唯一键);Event 无 flag/正文/URL 字段(design/26 §4.2 红线)
72 lines
5.3 KiB
SQL
72 lines
5.3 KiB
SQL
-- 0021_channel_events: 渠道非消息事件的**留存地基**(design/26 §4.1;切片 E1)。
|
||
--
|
||
-- 动因(design/26 §0.2,已确证事故):qq_obv11 的入站总闸曾把 post_type != message
|
||
-- 的帧零日志、零计数地丢掉,于是「上游推过没有 / 是不是被我们过滤了」在数据里
|
||
-- 再也答不上来。本表就是那块地基:让到达适配器的每一帧至少留下可查的结构级痕迹。
|
||
--
|
||
-- 两张表**刻意不合并**(design/26 §4.1 已论证):
|
||
-- ① 写放大 —— 心跳 / client_status / 在线状态是高频遥测,逐帧一行会把库写胖;
|
||
-- 计数表按「平台+账号+平台原生类型」upsert,行数上界是「平台数 × 事件类型数」。
|
||
-- ② 保留期不同 —— channel_events 有审计价值、按保留期清扫;
|
||
-- 计数表本身就是「历史已浓缩成一行」的形态,**删了就再也答不上「推过没有」**,
|
||
-- 因此它保留 first_seen_at / last_seen_at,**不参与 GC**。
|
||
-- ③ 两张表不同构(一张逐条、一张聚合),不是「同一份东西抄了两遍」。
|
||
-- T3 与 T2 的列**完全相同**(只差 tier 取值),故合在同一张表里:拆开会让「单一
|
||
-- 查询面 / 单一保留期 / 单一 GC」变成三份实现(design/26 §4.1)。
|
||
--
|
||
-- 命名口径:表名复数、主键 id、时间戳 created_at/updated_at、索引 idx_{table}_{col}
|
||
-- (援引 AGENTS §10 已登记例外,与既有迁移一致)。时间戳沿用 0001 起的
|
||
-- `strftime('%Y-%m-%dT%H:%M:%fZ','now')`(UTC ISO-8601 毫秒),定宽 ⇒ 字典序比较
|
||
-- 等价于时间序,时间窗筛选因此能直接吃上 idx_channel_events_created_at。
|
||
--
|
||
-- 脱敏(design/26 §4.2,**表结构层面也留了痕**):本表**没有**承载平台原文的列 ——
|
||
-- summary 只存适配器按事件类型生成的**固定文案**,OneBot request.flag(可执行凭据)
|
||
-- 与平台正文**绝不入库**(故本迁移里连一个 flag/comment/raw/content 列都不存在)。
|
||
|
||
CREATE TABLE IF NOT EXISTS channel_events (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
platform TEXT NOT NULL, -- qq-obv11 / kook / telegram / weixin-ilink / webhook
|
||
account_id TEXT NOT NULL DEFAULT '', -- 多账号(CHANNELS_JSON 的账号 id);空 = 单账号/未知
|
||
tier TEXT NOT NULL DEFAULT '', -- t1 / t2 / t3(design/26 §2 的留存档)
|
||
category TEXT NOT NULL DEFAULT '', -- 归一化类别(join/leave/recall/ban/admin/friend_request/
|
||
-- group_request/poke/reaction/button/item/vote/link/unknown)
|
||
platform_type TEXT NOT NULL DEFAULT '', -- 平台原生类型码(notice_type / meta_event_type / request_type /
|
||
-- KOOK extra.type / Telegram update 键名),原样保留便于排障
|
||
channel_id TEXT NOT NULL DEFAULT '',
|
||
channel_type TEXT NOT NULL DEFAULT '', -- direct / group
|
||
group_id TEXT NOT NULL DEFAULT '',
|
||
actor_id TEXT NOT NULL DEFAULT '', -- 结构化字段,**绝不来自文本**
|
||
actor_role TEXT NOT NULL DEFAULT '', -- owner/member/system/unknown(结构化解析所得)
|
||
target_id TEXT NOT NULL DEFAULT '', -- 被作用者(poke / 禁言 / 运气王…)
|
||
self_related INTEGER NOT NULL DEFAULT 0, -- 是否命中 self_id / 她自己
|
||
message_id TEXT NOT NULL DEFAULT '', -- 关联消息 id(撤回 / 回应 / 按钮 / 置顶)
|
||
summary TEXT NOT NULL DEFAULT '', -- 脱敏结构级摘要(固定文案,不含平台原文)
|
||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
||
);
|
||
|
||
-- 查询面与 GC 的**唯一依赖**:列表倒序、时间窗筛选、过期行清扫都靠它。
|
||
CREATE INDEX IF NOT EXISTS idx_channel_events_created_at ON channel_events (created_at);
|
||
-- 面板/端点的「平台 + 类别」筛选(design/26 §4.5 的查询参数)。
|
||
CREATE INDEX IF NOT EXISTS idx_channel_events_platform_category ON channel_events (platform, category);
|
||
CREATE INDEX IF NOT EXISTS idx_channel_events_channel_id ON channel_events (channel_id);
|
||
CREATE INDEX IF NOT EXISTS idx_channel_events_tier ON channel_events (tier);
|
||
|
||
CREATE TABLE IF NOT EXISTS channel_event_counters (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
platform TEXT NOT NULL,
|
||
account_id TEXT NOT NULL DEFAULT '',
|
||
tier TEXT NOT NULL DEFAULT '',
|
||
platform_type TEXT NOT NULL DEFAULT '',
|
||
category TEXT NOT NULL DEFAULT '',
|
||
count INTEGER NOT NULL DEFAULT 0,
|
||
first_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
||
last_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
||
);
|
||
|
||
-- 唯一约束承载 upsert(`ON CONFLICT(platform,account_id,platform_type) DO UPDATE`):
|
||
-- 一次 flush 因此是同键累加,而不是每次心跳插一行。
|
||
CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_event_counters_key
|
||
ON channel_event_counters (platform, account_id, platform_type);
|
||
CREATE INDEX IF NOT EXISTS idx_channel_event_counters_category ON channel_event_counters (category);
|