lty/qy_lty/docs/修改记录.md
pmc a13a081105
All checks were successful
Build and Deploy LTY / build-and-deploy (push) Successful in 9m16s
feat: update device interaction views and modification log doc
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 17:14:51 +08:00

8.4 KiB
Raw Blame History

服务器端代码修改记录

本文档记录每次对服务器端代码的修改,方便追踪变更历史。


修改格式说明

每次修改按以下格式记录:

### [日期] 修改简述

- **文件路径**: 相对于项目根目录的文件路径
- **修改类型**: 新增 / 修改 / 删除 / 重构 / 修复Bug
- **修改内容**: 具体修改了什么
- **修改原因**: 为什么要做这个修改

修改历史

[2026-04-29] strategy B group_send 推回消息体新增 timestamp_unix 字段

配套手机端记录:LTY_App_Project_URP/docs/修改记录.md 同日"修复 B' 双倒真正根因:时间戳时区解析"条目。

手机端实测 B' 方案出现 UI 双倒,根因定位为:服务端 chat_msg.timestamp.isoformat() 输出 UTC 带时区的 ISO8601+00:00),客户端 Unity Mono DateTime.TryParse 对此处理不稳定,可能丢失时区信息导致与本地时间戳比较时差 8 小时 → 替换匹配窗口15s永远不命中 → 走"作为新消息插入"兜底分支 → 双倒。

服务端最稳妥的修复方式:在 group_send payload 多附一个无时区歧义的 unix 秒级时间戳,让客户端优先使用。

修改strategy B 落库后 group_send payload 新增 timestamp_unix

  • 文件路径: device_interaction/views.py
  • 修改类型: 增强
  • 修改内容:
    • conversation_status action 内字幕落库分支(约 L1438 附近)的 channel_layer.group_send payload 新增字段:
      'timestamp_unix': int(chat_msg.timestamp.timestamp()),
      
    • 保留原 timestamp 字段ISO8601兼容老客户端不破坏现有约定
  • 修改原因:
    • Unix 秒级时间戳是绝对值,跨语言跨时区零歧义
    • 客户端 DateTimeOffset.FromUnixTimeSeconds(...).LocalDateTime 转换可靠
    • 服务端代价极小(一次 .timestamp() 调用),收益是消除一类隐性双倒 bug

客户端配套改动(仅记录依赖关系)

  • Assets/Scripts/AI/ChatLogManager.csServerPersistedData 结构体新增 long timestamp_unix 字段
  • OnServerChatPersisted 时间戳解析改为:优先 timestamp_unix > 0 → fallback DateTimeOffset.TryParse(timestamp) → fallback DateTime.Now
  • LoadChatHistoryFromServer 同步改用 DateTimeOffset.TryParseGET 接口暂未提供 unix 字段)

验证

服务端部署后,客户端 Console 应能在 [匹配诊断] 日志中看到 delta 缩小到秒级(之前是 ~28800s。修复确认后客户端会删除诊断日志。

待跟进

  • aiapp/views.pyRTCChatHistoryAPIView.get 也可在响应里加 timestamp_unix 字段进一步收紧(非必须,因为 GET 路径双倒不直接受影响 —— 走的是覆盖式拉取)

[2026-04-29] 手机端聊天记录切换服务端字幕落库B' 方案 服务端部分)

配套手机端方案文档:LTY_App_Project_URP/docs/手机端聊天记录_切换服务端字幕落库方案.md。手机端已实施 B'(本地 ASR 实时显示 + 服务端 webhook 静默替换服务端需要补三件事strategy B 落库后 group_send 推回客户端、DeviceConsumer 加 handler、RTCChatHistoryAPIView 灰度期去重 + since_id 增量拉取。

修改 1strategy B 落库成功后 group_send 转推

  • 文件路径: device_interaction/views.py
  • 修改类型: 新增功能
  • 修改内容:
    • conversation_status action 内字幕落库分支(约 L1414 ChatMessage.objects.create(...) 处):
      • create() 返回值赋给变量 chat_msg,落库成功 log 加上 id 字段
      • 落库成功后追加 channel_layer.group_send 调用,向 device_{paradise_user_id} 群组发送 type='chat_message_persisted' 消息payload 含 id / sender / message / timestamp / source_client
      • 用独立 try/except 包住,转推失败仅 warning 日志,不影响主落库流程
    • source_client 暂传 'unknown'(决策点 #3 落定后改为 'phone' / 'device'
  • 修改原因:
    • 手机端 B' 方案需要服务端在字幕入库后通过 WebSocket 把"权威 LLM 原始版本"推回客户端
    • 手机端按 chat_msg.id 去重 + 按 (sender, timestamp ±10s) 匹配本地待替换队列做静默替换,达到 UI 与 DB 字符级一致
    • 不影响设备端:设备端不订阅 chat_message_persisted 类型即可DeviceConsumer handler 仅向已实现处理的客户端透传)

修改 2DeviceConsumer 加 chat_message_persisted handler

  • 文件路径: device_interaction/consumers.py
  • 修改类型: 新增功能
  • 修改内容:
    • conversation_subtitle handler 之后新增 chat_message_persisted handler
    • 接收 group_send 事件后通过 self.send 把 JSON 推到 WebSocket 客户端
    • 日志记录 id / sender / source_client 用于后续排查
  • 修改原因:
    • Channels 协议要求 group_send 的 type 字段值在 Consumer 上有同名方法处理,否则消息被丢弃且报警
    • 必须与修改 1 同步部署,否则 strategy B 的 group_send 调用会失败

修改 3RTCChatHistoryAPIView 灰度期 POST 去重 + GET since_id 支持

  • 文件路径: aiapp/views.py
  • 修改类型: 新增功能 + 增强
  • 修改内容:
    • RTCChatHistoryAPIView.post() 入口加去重判定:同一 (user, bot, sender, message)±2s 时间窗内已存在则跳过 create,返回 deduplicated: true
    • RTCChatHistoryAPIView.get() 支持 since_id query 参数:传入则返回 id > since_id 的消息(升序,最多 page_size 条),未传则保持原最近 page_size 条逻辑
  • 修改原因:
    • 灰度期双倒保护:手机端 App 发版到用户手里需要时间,老版仍走 POST 落库strategy B webhook 此时也在落库 → 同一对话产生重复行。POST 去重让两条路径并存而不致脏库
    • 重放保护strategy B 自身被火山重试或客户端重连补提时,去重也能挡住
    • WebSocket 漏推兜底B' 方案手机端 5s 超时未收到 chat_message_persisted 时调 GET ?since_id=<last> 增量拉取替换队列里待修正的消息

关联代码(手机端,仅记录依赖关系)

  • 手机端 Assets/Scripts/Manager/WebSocketNetworking.cs 已新增 chat_message_persisted 类型分发分支
  • 手机端 Assets/Scripts/AI/ChatLogManager.cs 已新增 OnServerChatPersisted 方法、_pendingReplaceQueue 与 5s 超时兜底
  • 手机端 Assets/Scripts/AI/getJson.cs 已加 Config.SubtitleConfig.SubtitleMode=1

部署顺序与回滚

  • 部署顺序:服务端先部署(修改 1+2+3 三处一同上线)→ 验证 group_send 通道工作 → 手机端再发版
  • 回滚:三处改动都用独立 try/except 包住,可独立 git revert
    • 修改 1 revertstrategy B 主流程不受影响,只是不再 group_send手机端 UI 替换路径变为 5s 超时兜底
    • 修改 2 revert与修改 1 必须同时 revert否则 group_send 收方为空报警
    • 修改 3 revertPOST 不再去重(灰度期会出现双倒,需人工清理 DBGET 不再支持 since_id手机端兜底拉取无效

待跟进 TODO

  • 决策点 #3服务端区分手机端 / 设备端 RTC sessionmac 标记 / task_id 命名规则)→ source_client 字段填充真实值,让两端按需过滤
  • 决策点 #5服务端验证打断时是否仍 flush 部分内容;如不 flush手机端打断分支应跳过入待替换队列以避免 5s 超时空触发
  • Phase 0 步骤 1DB 双轨验证SQL 见 LTY_App_Project_URP/docs/手机端聊天记录_切换服务端字幕落库方案.md
  • Phase 0 步骤 4清理历史脏数据如发现

[2026-03-17] 修复手机号登录时 IntegrityError

  • 文件路径: userapp/views.py
  • 修改类型: 修复Bug
  • 修改内容: PhoneLoginView.post()get_or_create 新增 defaults={'username': phone_number}
  • 修改原因: 新用户首次通过手机号登录时,get_or_create 未设置 username 字段,导致 username="" 与数据库中已有空 username 记录冲突,触发 IntegrityError: duplicate key value violates unique constraint "userapp_paradiseuser_username_key"。改为用手机号作为默认 username保证唯一性。