feat(channel): 微信 iLink 账号状态三态(诚实口径)

state ∈ 有效 / 被拒(需重扫)/ 未知;🔴 -14 是**被拒不是过期**,且**未知原因绝不写成「过期」**
(面板文案与错误码都如实)。三态判定为纯函数 + 三态各有用例。

⚠️ 未做(如实登记):每账号逐条三态(accounts[] 数组上的三态)本片未做。
This commit is contained in:
AskaEth committed 2026-10-06 09:03:53 +08:00
1 parent 7505c46b70
commit 99a644d545
12 files changed
+2041 -57

No files matched your search

@@ -42,31 +42,17 @@ const channelEventsLogSource = "gateway/admin_channel_events"
// 「与 `llm/usage` 同口径」,两处各对着同一条设计口径写死,谁也别想只改一处。
const channelEventsMaxWindow = 90 * 24 * time.Hour
// channelEventDTO 是一条 `channel_events` 行的线上投影(键名与同族的
// `counters[]` 一致,取 camelCase)。
// channelEventDTO 是一条 `channel_events` 行的线上投影。它现在是
// `events.ChannelEventJSON` 的**类型别名**(切片 E7):同一条事件有两条线上出口 ——
// 这里的 REST 列表与实时 `channel_event` 帧的 `data.event`。两个出口共用一份结构,
// 是「不许两处各写一份形状」这条纪律的落地(否则迟早出现「帧里有这个键、列表里
// 没有」这类无从察觉的差异)。
//
// 列与 `events.Event` **一一对应且不多不少**:它没有、也不会有承载平台原文、
// 列与 `events.Event` 一一对应且不多不少:它没有、也不会有承载平台原文、
// 媒体 URL、凭据或 OneBot `flag` 的字段(§4.2 红线)。`summary` 是适配器生成的
// 固定文案(如「群成员加入」),**已净化**,查不出原始文本。
type channelEventDTO struct {
ID int64 `json:"id"`
Platform string `json:"platform"`
AccountID string `json:"accountId"`
Tier string `json:"tier"`
Category string `json:"category"`
PlatformType string `json:"platformType"`
ChannelID string `json:"channelId"`
ChannelType string `json:"channelType"`
GroupID string `json:"groupId"`
ActorID string `json:"actorId"`
ActorRole string `json:"actorRole"`
TargetID string `json:"targetId"`
SelfRelated bool `json:"selfRelated"`
MessageID string `json:"messageId"`
Summary string `json:"summary"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
// 固定文案(如「群成员加入」)或本包的固定迁移文案,**已净化**,查不出原始文本。
// `id` 可缺席(实时帧里那行的 id 尚未回读),列表路径上它恒有值。
type channelEventDTO = events.ChannelEventJSON
// channelEventListResponse 是列表端点的 data 本体(§4.5:`{events, total}`)。
// `events` 恒为非 nil 切片(无数据时是 `[]` 而非 `null`,客户端不必判空)。
@@ -323,27 +309,9 @@ func channelEventsListResponseOf(rows []events.Event, total int64) channelEventL
return out
}
// channelEventDTOOf 是单行投影(一处换算,避免 List 与将来可能的详情端点各写一遍)。
// channelEventDTOOf 是单行投影(一处换算,避免 List 与实时帧各写一遍)。
func channelEventDTOOf(ev events.Event) channelEventDTO {
return channelEventDTO{
ID: ev.ID,
Platform: ev.Platform,
AccountID: ev.AccountID,
Tier: string(ev.Tier),
Category: string(ev.Category),
PlatformType: ev.PlatformType,
ChannelID: ev.ChannelID,
ChannelType: ev.ChannelType,
GroupID: ev.GroupID,
ActorID: ev.ActorID,
ActorRole: ev.ActorRole,
TargetID: ev.TargetID,
SelfRelated: ev.SelfRelated,
MessageID: ev.MessageID,
Summary: ev.Summary,
CreatedAt: ev.CreatedAt.UTC().Format(time.RFC3339),
UpdatedAt: ev.UpdatedAt.UTC().Format(time.RFC3339),
}
return ev.JSON()
}
// channelEventCountersResponseOf 把计数表结果翻成线上结构。`approx` 恒为 true: