feat(gateway): 渠道事件查询面 + 面板「渠道事件」页(design/26 E6)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 两个只读端点:GET /admin/channels/events(列表,8 个过滤条件 + 分页钳制)与 .../events/counters(逐类计数,恒带 approx:true 如实标「近似」)
- 诚实边界:>90 天窗口 ⇒ 400(不静默裁剪);未知 platform/档位 ⇒ 400;**未接线 ⇒ 500 channel_events_unavailable**(绝不回空 200 假装「没有事件」);微信平台的空如实说明「该渠道不提供非消息事件」而不借别平台顶替
- 面板:新 section「渠道事件」(计数卡 + 事件行 + 过滤条 + 30s 静默刷新),空/错误态分开且失败显示错误不显示 0
- 组合根接线(主会话补):wire_frontend.go 的 Deps 注入 EventStore: a.channelEventStore()(未接线时为真 nil ⇒ 保留诚实 500)
- 契约不升版(查询面 v0.67 已登记):就地改「未实现 → 已实现 + 端点路径」,并补登记 5 处原先留白的落地值(camelCase / 通用 bad_request / RBAC owner|admin / data.approx / total 语义)
- 顺带把 §5 三个 CHANNEL_EVENT_* env 键由「尚未实现」改「已接线」(E4)
- 变异实测:去掉 90 天上限 ⇒ status 200 want 400;未接线回空 200 ⇒ status 200 want 500
- 债务登记:events.Filter 无 AccountID 列 ⇒ 服务端暂不能按账号筛(列表仍如实下发 accountId),要加需先改 internal/runtime/events
This commit is contained in:
AskaEth committed 2026-10-05 14:38:37 +08:00
1 parent a0abf0199a
commit c8053b1f36
21 files changed
+1805 -30

No files matched your search

+23
View File
@@ -13,6 +13,7 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/persona/emotion"
"git.yeij.top/AskaEth/Cyrene_v2/internal/persona/loader"
personareg "git.yeij.top/AskaEth/Cyrene_v2/internal/persona/registry"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/events"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/history"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/knowledge"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/loop"
@@ -261,3 +262,25 @@ type LlmUsageStore interface {
// handler 校验并补齐(缺省近 24h、上限 90 天)。
Summary(ctx context.Context, q model.UsageQuery) (model.UsageSummary, error)
}
// ChannelEventStore 是渠道事件留存的**只读查询出口**(design/26 §4.5 / §6.5;
// 切片 E6)。生产实现是 `internal/runtime/events` 的 `*SQLStore`,组合根把
// 同一个句柄既交给桥接层(写入面,E3)也交给网关(本查询面)。
//
// 它刻意只声明三个**读**方法:写入、计数落盘与保留期清扫是桥接层与组合根的
// 职责,管理查询面拿不到、也不该拿到它们(最小权限)。
//
// 查询结果里**没有任何平台原文 / 媒体 URL / 凭据 / OneBot `flag`** —— 事件表
// 本身就没有这些列(§4.2 红线),因此这里不存在「回显敏感字段」的代码路径。
//
// 为 nil 时两个端点诚实作答 500 `channel_events_unavailable`,绝不回一份全 0
// 的假汇总(与 `usage_unavailable` / `storage_unavailable` 同一口径)。
type ChannelEventStore interface {
// List 按时间倒序返回符合条件的事件行(Limit/Offset 见 events.Filter)。
List(ctx context.Context, f events.Filter) ([]events.Event, error)
// Count 统计符合条件的事件行数(List 的分页配套)。
Count(ctx context.Context, f events.Filter) (int64, error)
// Counters 返回逐类计数汇总;platform 为空表示全部平台。它读的是**已落盘**
// 的表,不含内存里待 flush 的部分(即 §4.3 的「近似值」)。
Counters(ctx context.Context, platform string) ([]events.Counter, error)
}