fix: 修复多图消息无法显示的三个根因

根因 1 — MessageItem.toEntity() id=0 主键碰撞:
  WS 拉取的每条消息均用 id=0 insertOrReplace,批量消息相互覆盖,
  DB 中只留最后一条。改为 id=messageId(服务端唯一 ID)。

根因 2 — SendMessageUseCase 乐观写入 id=0 碰撞:
  批量图片发送时所有乐观行共享 id=0,逐条覆盖。
  改用负微秒时间戳作为临时唯一 id,HTTP 确认后用真实 messageId 替换。

根因 3 — watchByChatId 无 ORDER BY:
  DB 消息顺序不确定,宫格分组算法依赖时间升序失败。
  在 MessageRepositoryImpl.watchByChatId 及 _buildDisplayItems 中
  分别按 sendTime ASC + chatIdx ASC 排序。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
pp-bot
2026-03-24 15:45:41 +09:00
parent 2354e92c64
commit 2eb2299709
5 changed files with 68 additions and 13 deletions

View File

@@ -9,9 +9,16 @@ import 'package:im_app/domain/repositories/message_repository.dart';
/// 发送文本消息用例
///
/// ## 执行流程
/// 1. 乐观写入本地 DB临时消息id=0)→ UI 立即刷新
/// 1. 乐观写入本地 DB负微秒时间戳作为唯一临时 id)→ UI 立即刷新
/// 2. HTTP POST `/app/api/chat/send-message` → 获取服务端 messageId / chatIdx
/// 3. 更新 ChatRepository 的 lastMsg / lastTyp / lastTime
/// 3. 用真实 messageId 替换临时行delete tempId → insert realId
/// 4. 更新 ChatRepository 的 lastMsg / lastTyp / lastTime
///
/// ## 为什么不用 id=0
///
/// 原来所有乐观写入均用 `id=0`(主键),批量发送时会相互覆盖,
/// 导致 DB 只剩最后一条消息。改用负微秒时间戳作为临时 id
/// 每条消息唯一,不会碰撞。服务端确认后立即替换为正式 messageId。
///
/// DB Stream → StreamProvider → UI 自动重建,无需额外通知。
class SendMessageUseCase {
@@ -36,10 +43,11 @@ class SendMessageUseCase {
}) async {
final sendTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
// 1. 乐观本地写入
// 1. 乐观本地写入(唯一负 id防止批量发送时相互覆盖
final tempId = -(DateTime.now().microsecondsSinceEpoch);
await _messageRepo.insertOrReplace(
Message(
id: 0,
id: tempId,
chatId: chatId,
sendId: currentUid,
content: content,
@@ -63,7 +71,28 @@ class SendMessageUseCase {
debugPrint('[SendMessageUseCase] HTTP error: $e');
}
// 3. 更新 Chat 摘要
// 3. 用真实 messageId 替换临时行
if (resp != null && resp.messageId > 0) {
try {
await _messageRepo.delete(tempId);
await _messageRepo.insertOrReplace(
Message(
id: resp.messageId,
messageId: resp.messageId,
chatId: chatId,
chatIdx: resp.chatIdx,
sendId: currentUid,
content: content,
typ: typ,
sendTime: resp.sendTime > 0 ? resp.sendTime : sendTime,
),
);
} catch (e) {
debugPrint('[SendMessageUseCase] replace temp row error: $e');
}
}
// 4. 更新 Chat 摘要
try {
final chat = await _chatRepo.getChat(chatId);
if (chat != null) {