feat(channel): 出站消息 id 采集(可选 seam + qq_obv11 两分支取回)
「接她的话」缺的 A 层:平台给她自己那条出站消息分配的 id,此前在解析的那一刻 就被丢掉(HTTP 只校 retcode、WS 写完不等回声、契约层没有 id 出口)。 - internal/channel/adapter/outbound.go(新):SendReceipt/SendOutcome/SendIDStatus + 可选接口 OutboundReceiptAdapter + 调用方入口 SendWithReceipt。**不加进 PlatformAdapter** —— 四个未接线适配器与全部桩实现一行不改(向后兼容硬指标); 也不用回调:id 是某一次投递的属性,必须随那次投递返回。 - qq_obv11:HTTP 从响应信封 data.message_id 取;WS 登记 send_msg 的 echo 等待者 (复用 probe.go 的 action 等待表与 routeActionResponse,不另开通道)。取不到 一律空 id + 三态可分辨(reported/absent/unreported),绝不编造。 - send_msg echo 键改用单调计数器(旧 UnixNano 在 Windows 时钟粒度下会撞键, 撞键即拿不到自己的回执)。 - 既有 SendMessage/SendProactive 路径行为不变(wantReceipt=false 不等待)。 验收:go build ./... + go vet ./... + go test ./... 全绿(cmd/cyrene、internal/webui 的 setup 失败是 worktree 缺 dist 的既有环境限制,base 同样失败)。
This commit is contained in:
1 parent
a6f3817061
commit
b504df01e0
8 files changed
+1053
-63
No files matched your search
@@ -242,8 +242,8 @@ func (a *Adapter) wsCall(ctx context.Context, action string, params any) (*obAPI
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("qq-obv11: encode %s call: %w", action, err)
|
||||
}
|
||||
ch := a.registerProbeWait(echo)
|
||||
defer a.clearProbeWait(echo, ch)
|
||||
ch := a.registerActionWait(echo)
|
||||
defer a.clearActionWait(echo, ch)
|
||||
if err := conn.WriteMessage(opText, payload); err != nil {
|
||||
return nil, fmt.Errorf("qq-obv11: write %s: %w", action, err)
|
||||
}
|
||||
@@ -262,22 +262,22 @@ func (a *Adapter) nextProbeEcho() string {
|
||||
return probeEchoPrefix + strconv.FormatUint(a.probeSeq.Add(1), 10)
|
||||
}
|
||||
|
||||
// registerProbeWait 登记一个待回执的 echo,返回容量 1 的投递通道(写方绝不阻塞)。
|
||||
func (a *Adapter) registerProbeWait(echo string) chan *obAPIResponse {
|
||||
// registerActionWait 登记一个待回执的 echo,返回容量 1 的投递通道(写方绝不阻塞)。
|
||||
func (a *Adapter) registerActionWait(echo string) chan *obAPIResponse {
|
||||
ch := make(chan *obAPIResponse, 1)
|
||||
a.probeMu.Lock()
|
||||
a.probeWait[echo] = ch
|
||||
a.probeMu.Unlock()
|
||||
a.actionMu.Lock()
|
||||
a.actionWait[echo] = ch
|
||||
a.actionMu.Unlock()
|
||||
return ch
|
||||
}
|
||||
|
||||
// clearProbeWait 注销 echo(等待超时 / 已收到回执)。
|
||||
func (a *Adapter) clearProbeWait(echo string, ch chan *obAPIResponse) {
|
||||
a.probeMu.Lock()
|
||||
if a.probeWait[echo] == ch {
|
||||
delete(a.probeWait, echo)
|
||||
// clearActionWait 注销 echo(等待超时 / 已收到回执)。
|
||||
func (a *Adapter) clearActionWait(echo string, ch chan *obAPIResponse) {
|
||||
a.actionMu.Lock()
|
||||
if a.actionWait[echo] == ch {
|
||||
delete(a.actionWait, echo)
|
||||
}
|
||||
a.probeMu.Unlock()
|
||||
a.actionMu.Unlock()
|
||||
}
|
||||
|
||||
// routeActionResponse 尝试把一帧当作 action 回执投递给等待中的调用方。命中返回 true;
|
||||
@@ -290,9 +290,9 @@ func (a *Adapter) routeActionResponse(payload []byte) bool {
|
||||
if err := json.Unmarshal(payload, &head); err != nil || head.Echo == "" || head.PostType != "" {
|
||||
return false
|
||||
}
|
||||
a.probeMu.Lock()
|
||||
ch := a.probeWait[head.Echo]
|
||||
a.probeMu.Unlock()
|
||||
a.actionMu.Lock()
|
||||
ch := a.actionWait[head.Echo]
|
||||
a.actionMu.Unlock()
|
||||
if ch == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user