feat(channelbridge): 出站语音结论落 voice 列并挂出站 Media(M58)
- voice.Ref 增 SourceTTS 与 FallbackReason,Validate/IsZero/validSource 同步 - 出站成功:INSERT -> 补写 voice 列 -> notify(首帧即带 voice) - 出站降级:source=tts + status=failed + fallbackReason,正文原样 - 首条气泡挂出站 Media(新开条目,不复制 target.Media) - gateway historyVoice 投影 fallbackReason - 新增 outbound_voice.go 与整链 e2e 测试(含变异打红验证时序)
This commit is contained in:
1 parent
9f6243a729
commit
28afa11aeb
13 files changed
+755
-44
No files matched your search
@@ -116,6 +116,9 @@ func (b *Bridge) ingestInboundVoiceCopy(ctx context.Context, msg adapter.Unified
|
||||
// 若先广播后写列,这条语音消息的**第一帧**就永远缺 `voice`,客户端只能等重连或下一次 REST
|
||||
// 拉取才升级出播放器(安卓 2026-10-26 回执报的正是这个)。
|
||||
//
|
||||
// 入站与出站**共用**它:两边都是「结论已经在手里、列还没写」的同一形状(入站是转写结论,
|
||||
// 出站是合成结论),首帧纪律因此只有一处实现。
|
||||
//
|
||||
// 只换顺序、不换代价:仍是 INSERT + 一次 UPDATE(与 design/24 §10.5 的第二选项同一手法,
|
||||
// 与 document 结论的两段式同族)。非语音消息(ref 零值)时 persistMessageVoice 直接返回,
|
||||
// 因此本包装与 notifyPersisted 逐字等价 —— 普通消息的时序一个字节都没变。
|
||||
@@ -126,9 +129,9 @@ func (b *Bridge) persistVoiceFirstNotify(ref voiceref.Ref) func(history.Message)
|
||||
}
|
||||
}
|
||||
|
||||
// persistMessageVoice 把这条语音的来源事实与副本引用写进刚落库的用户消息 `voice` 列
|
||||
// (design/24 §10.2 第 5 步)。它由 runInboundTurn 在用户消息落库之后调用,因此
|
||||
// 「引用 + 三态」最终是同一行里的一份事实,客户端不必自己拼。
|
||||
// persistMessageVoice 把这条语音的来源事实与副本引用写进刚落库的消息 `voice` 列
|
||||
// (design/24 §10.2 第 5 步;出站方向同列同形)。它由落库那条路在消息写进去之后调用,
|
||||
// 因此「引用 + 三态」最终是同一行里的一份事实,客户端不必自己拼。
|
||||
//
|
||||
// 三个提前返回都是**正常**情形,不刷日志:这条消息不是语音(零值)、没有落库
|
||||
// (msg.ID 为空 ⇒ 存储未接或写库失败)、编码后为空串。
|
||||
@@ -152,7 +155,7 @@ func (b *Bridge) persistMessageVoice(msg history.Message, ref voiceref.Ref) {
|
||||
if err != nil {
|
||||
// 编码失败只可能是坏数据(闭集破坏 / 副本字段不同生共死):**绝不**写半截,
|
||||
// 但也绝不静默 —— 消息本身已经落库,少的是这条语音标记。
|
||||
b.voiceLogger().Error("inbound voice ref not persisted: encode failed",
|
||||
b.voiceLogger().Error("voice ref not persisted: encode failed",
|
||||
"message", msg.ID, "source", ref.Source, "status", ref.Status, "error", err)
|
||||
return
|
||||
}
|
||||
@@ -163,13 +166,13 @@ func (b *Bridge) persistMessageVoice(msg history.Message, ref voiceref.Ref) {
|
||||
if !ok {
|
||||
// 历史存储没有 voice 列写入视图(迁移 0020 只加列,不替调用方接写路径):
|
||||
// 如实报告「没写成」,绝不假装写了。
|
||||
b.voiceLogger().Warn("inbound voice ref not persisted",
|
||||
b.voiceLogger().Warn("voice ref not persisted",
|
||||
"reason", "history store has no voice column path", "message", msg.ID,
|
||||
"source", ref.Source, "status", ref.Status)
|
||||
return
|
||||
}
|
||||
if err := writer.UpdateMessageVoice(context.Background(), msg.ID, raw); err != nil {
|
||||
b.voiceLogger().Warn("inbound voice ref update",
|
||||
b.voiceLogger().Warn("voice ref update",
|
||||
"err", err, "message", msg.ID, "source", ref.Source, "status", ref.Status)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user