缺口①:「连接」是按平台连、且恒打第一条账号(channelActionRequest 只有 name)⇒ 主人有两条
qq-obv11 时点到的是 mode=server 的历史条目,报 server mode requires listenAddr,而正确的
client 条目轮不到。缺口②:空 id 条目既删不掉(DELETE 要求 id 非空)也改不了(UpsertChannel
空 id = 新建),越改越多。
后端:
- POST /admin/channels/open|close 请求体新增可选 accountId(缺省逐字兼容,老客户端零影响);
按该账号驱动;未知 id ⇒ 404 且零连接(不静默回落第一条);已配置未注册 ⇒ 409
- DELETE /admin/channels/{platform}/config 新增显式 empty=1(id 与 empty=1 互斥 ⇒ 400;
无可删 ⇒ 404);语义严格 —— 只删空 id 条目,绝不把「id 为空」默认解释成删空 id
- 响应新增:open/close 的 resolved(实际驱动的适配器名)与 accountId;delete 的 removed/empty;
GET /admin/channels 的 accounts[].connected(可选 bool,缺省 = 未知)
- 审计动作名不变(channel.open/close/delete),日志行新增 accountId 字段
前端:每条账号一行「连接/断开」(带 accountId);按钮旁明确「会连哪一条」;空 id 行显著标注
+ 删除入口;channelActionBody 携带 accountId;新增 action 测试 33 checks + 空 id 行渲染 31 checks。
测试:后端 14 个用例(真实 HTTP 路由 + JWT + 真注册表 + spy 适配器,断言「哪一条被驱动、
另一条一次都没被碰」)。
变异 6 条(accountId 被忽略 / id 为空当删空 id / 找不到静默回落 / 前端丢 accountId /
删空 id 退化成 ?id= / 空 id 行按普通行渲染)。
54 lines
2.1 KiB
Go
54 lines
2.1 KiB
Go
package gateway
|
|
|
|
// 管理渠道列表里的**每账号**连接状态(AdminChannelAccount.Connected)。
|
|
//
|
|
// 为什么要有它:平台级 `connected` 只反映该平台第一条已注册适配器(ChannelSummary
|
|
// 由聚合行上的那个适配器决定),多账号平台上它无法回答「我点连接的那条现在活着吗」。
|
|
// 面板因此只能盲目渲染每行的连接/断开按钮 —— 主人有两条 qq-obv11 时,「断开」到底
|
|
// 断的是哪一条、断完有没有生效,看板上都无从判断。这里按账号把它补上。
|
|
|
|
import (
|
|
capreg "git.yeij.top/AskaEth/Cyrene_v2/internal/capability/registry"
|
|
"git.yeij.top/AskaEth/Cyrene_v2/pkg/plugins/sdk"
|
|
)
|
|
|
|
// liveAccountConnections 收集每个平台各账号的实时连接状态,键与 liveBotIdentity
|
|
// 相同(裸适配器名 ⇒ 空账号 id)。注册表里**没有**该账号的适配器时它直接缺席,
|
|
// 而不是报 false:缺席 = 状态未知(面板画「状态未知」),false = 确知未连接。
|
|
func (s *Server) liveAccountConnections() map[string]map[string]bool {
|
|
out := make(map[string]map[string]bool)
|
|
if s.caps == nil {
|
|
return out
|
|
}
|
|
for _, cap := range s.caps.List(capreg.KindChannel) {
|
|
ch, ok := cap.Value.(sdk.Channel)
|
|
if !ok {
|
|
continue
|
|
}
|
|
platform, acct := splitAdapterName(ch.Name())
|
|
if out[platform] == nil {
|
|
out[platform] = make(map[string]bool)
|
|
}
|
|
out[platform][acct] = ch.IsConnected()
|
|
}
|
|
return out
|
|
}
|
|
|
|
// enrichAccountsWithConnection 把实时连接状态合并进平台中匹配的账号(按账号 id
|
|
// 配对,与适配器注册名一致)。没有实时适配器的账号保持 Connected == nil(未知),
|
|
// 因此面板能在「没连上」与「读不到」之间如实区分。入参切片不被修改。
|
|
func enrichAccountsWithConnection(accts []AdminChannelAccount, by map[string]bool) []AdminChannelAccount {
|
|
if len(by) == 0 {
|
|
return accts
|
|
}
|
|
out := make([]AdminChannelAccount, 0, len(accts))
|
|
for _, a := range accts {
|
|
if connected, ok := by[a.ID]; ok {
|
|
value := connected
|
|
a.Connected = &value
|
|
}
|
|
out = append(out, a)
|
|
}
|
|
return out
|
|
}
|