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") }