Files
Cyrene_v2/internal/services/channelbridge/turnid.go
T
AskaEth 8f6a75b7d7
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(channels): IM 车道生成并写入真实 turn_id(user/assistant 成对 + 同源 WithTurnID;群聊隔离与静默语义不变)
2026-10-05 12:22:44 +08:00

32 lines
1.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package channelbridge
import (
"crypto/rand"
"encoding/hex"
"time"
)
// 本文件承载 IM 车道自己的**回合标识**生成点(design/15 §2.5.9)。
//
// IM 与 Web 是两条独立的驱动路径:Web 由 gateway 编排器起路、由它生成权威 turnId
// (gateway/ws_hub_turn.go 的 newTurnID),而 IM 由渠道桥自己驱动 loop
// (inbound.go 的 runInboundTurn → b.run.Run)—— 桥接器就是**这条车道的回合权威**,
// 因此这里生成的是真实归属:一条入站消息 → 一次 run → 一条回复,标识只在这一段
// 生命周期内有效。绝不拿时间戳或会话 id 冒充 turn id —— 那是伪造归属,比留空更坏
// (留空诚实地表示「不属于任何已知回合」,冒充则会让两个不同回合被分进同一桶)。
//
// 格式与 gateway 那份**逐字一致**(`turn-` + 16 位十六进制),使两条车道产出的
// turnId 对任何消费方(前端按 (sessionId, turnId) 两级分桶)都是同一种东西。它成为
// 第二处副本,是因为 gateway 那份未导出、且该包已冻结,无法共享 —— 不是有意另造。
// newTurnID 返回一个新的随机十六进制回合标识,用于把一次 IM 入站回合的落库行
// (用户消息 + 助手回复)关联到同一路。它消耗 crypto/rand 源,源不可用时回退到
// 纳秒时间戳,因此总能产出 id —— 空的 turnId 会静默破坏客户端的两级分桶。
func newTurnID() string {
var b [8]byte
if _, err := rand.Read(b[:]); err == nil {
return "turn-" + hex.EncodeToString(b[:])
}
return "turn-" + time.Now().UTC().Format("20060102150405.000000000")
}