- Update aiapp views - Update device_interaction consumers and views - Update docs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
99 lines
6.1 KiB
Markdown
99 lines
6.1 KiB
Markdown
# 服务器端代码修改记录
|
||
|
||
本文档记录每次对服务器端代码的修改,方便追踪变更历史。
|
||
|
||
---
|
||
|
||
## 修改格式说明
|
||
|
||
每次修改按以下格式记录:
|
||
|
||
```
|
||
### [日期] 修改简述
|
||
|
||
- **文件路径**: 相对于项目根目录的文件路径
|
||
- **修改类型**: 新增 / 修改 / 删除 / 重构 / 修复Bug
|
||
- **修改内容**: 具体修改了什么
|
||
- **修改原因**: 为什么要做这个修改
|
||
```
|
||
|
||
---
|
||
|
||
## 修改历史
|
||
|
||
<!-- 新的修改记录添加在此处下方,最新的在最前面 -->
|
||
|
||
### [2026-04-29] 手机端聊天记录切换服务端字幕落库(B' 方案 服务端部分)
|
||
|
||
配套手机端方案文档:`LTY_App_Project_URP/docs/手机端聊天记录_切换服务端字幕落库方案.md`。手机端已实施 B'(本地 ASR 实时显示 + 服务端 webhook 静默替换),服务端需要补三件事:strategy B 落库后 group_send 推回客户端、DeviceConsumer 加 handler、RTCChatHistoryAPIView 灰度期去重 + since_id 增量拉取。
|
||
|
||
#### 修改 1:strategy 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 仅向已实现处理的客户端透传)
|
||
|
||
#### 修改 2:DeviceConsumer 加 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 调用会失败
|
||
|
||
#### 修改 3:RTCChatHistoryAPIView 灰度期 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 revert:strategy B 主流程不受影响,只是不再 group_send,手机端 UI 替换路径变为 5s 超时兜底
|
||
- 修改 2 revert:与修改 1 必须同时 revert,否则 group_send 收方为空报警
|
||
- 修改 3 revert:POST 不再去重(灰度期会出现双倒,需人工清理 DB);GET 不再支持 since_id(手机端兜底拉取无效)
|
||
|
||
#### 待跟进 TODO
|
||
|
||
- 决策点 #3:服务端区分手机端 / 设备端 RTC session(mac 标记 / task_id 命名规则)→ `source_client` 字段填充真实值,让两端按需过滤
|
||
- 决策点 #5:服务端验证打断时是否仍 flush 部分内容;如不 flush,手机端打断分支应跳过入待替换队列以避免 5s 超时空触发
|
||
- Phase 0 步骤 1:DB 双轨验证(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,保证唯一性。
|
||
|