Compare commits

..

No commits in common. "c0ed0d83de78c34dedf550a3aa787f59cfac1115" and "d293de34f597e6459a9792fa74cd5bc6aec8a1b1" have entirely different histories.

5 changed files with 6 additions and 44 deletions

View File

@ -185,7 +185,7 @@ offset 23 Body bytes UTF-8 JSON
| 5 | SEND_ACK | ← 仅 4 的成功应答 | | 5 | SEND_ACK | ← 仅 4 的成功应答 |
| 6 | MESSAGE | ← 推送 | | 6 | MESSAGE | ← 推送 |
| 7 | PUSH_ACK | → 空;`SequenceID` = 推送帧的 SequenceID | | 7 | PUSH_ACK | → 空;`SequenceID` = 推送帧的 SequenceID |
| 8 | READ_MESSAGE | → `conversation_id`,以及 `msg_id` 或 `last_msg_id`(任一大于 0;两者都有时用 `msg_id`)。成功 `data` 为 `conversation_id` 与 `msg_id`,`msg_id` 为落库后的 `last_read_msg_id`(只增不减)。清该成员 Redis 未读,并把会话从 unread ZSET 挪到 read ZSET。水位前进时给该用户其他在线设备推 Command 8(不系统推送、不写 mailbox)。缺 `conversation_id`,或两个消息 id 都小于等于 0,为 `40001` | | 8 | READ_MESSAGE | → `{"conversation_id","msg_id"}`;成功 `data` 同名字段,`msg_id` 为落库后的 `last_read_msg_id`(只增不减)。清该成员 Redis 未读,并把会话从 unread ZSET 挪到 read ZSET。水位前进时给该用户其他在线设备推 Command 8(不系统推送、不写 mailbox)。缺 `conversation_id` 或 `msg_id` 为 `40001` |
| 9 | RECALL_MESSAGE | → `{"msg_id"}` | | 9 | RECALL_MESSAGE | → `{"msg_id"}` |
| 10 | SYNC_INBOX | → 会话列表增量 | | 10 | SYNC_INBOX | → 会话列表增量 |
| 12 | SYNC_MESSAGES | → 某会话消息增量 | | 12 | SYNC_MESSAGES | → 某会话消息增量 |

View File

@ -653,13 +653,7 @@ READ 更新 last_read_msg_id,清 Redis 未读,入队通知其他设
{"conversation_id": 1001, "msg_id": 2002} {"conversation_id": 1001, "msg_id": 2002}
``` ```
也接受客户端现用的 `last_msg_id`(可同时带 `peer_id`,服务端不使用该字段): `msg_id` 是成员已读到的消息 id。成功应答仍是命令 8,Body 为 V3 信封,`data.msg_id` 为写入后的 `last_read_msg_id`(只增不减;更小的 id 不回退)。缺 `conversation_id` 或 `msg_id`(≤0)返回 `40001`。消息不存在为 `40401`;消息不属于该会话为 `40001`;不是成员为 `40301`。
```json
{"conversation_id": 1001, "peer_id": 9, "last_msg_id": 2002}
```
`msg_id` 与 `last_msg_id` 任一大于 0 即是成员已读到的消息 id;两者都大于 0 时用 `msg_id`。成功应答仍是命令 8,Body 为 V3 信封,`data.msg_id` 为写入后的 `last_read_msg_id`(只增不减;更小的 id 不回退)。缺 `conversation_id`,或两个消息 id 都小于等于 0,返回 `40001`。消息不存在为 `40401`;消息不属于该会话为 `40001`;不是成员为 `40301`。
水位前进时入队:`ToUserIDs` 仅为当前用户,`ExcludeDevice` 为当前设备,`NotPush` 与 `NotMailbox` 均为真。其他在线设备收到命令 8。 水位前进时入队:`ToUserIDs` 仅为当前用户,`ExcludeDevice` 为当前设备,`NotPush` 与 `NotMailbox` 均为真。其他在线设备收到命令 8。

View File

@ -29,7 +29,7 @@ func (m *Repo) ListByConversation(ctx context.Context, conversationID, viewerID,
if kind == entity.KindGroup { if kind == entity.KindGroup {
var rows []GroupMessage var rows []GroupMessage
err = db.Table(table).Select(table+".*"). err = db.Table(table).Select(table+".*").
Joins("LEFT JOIN t_message_deletes AS d ON d.msg_id = "+table+".id AND d.user_id = ?", viewerID). Joins("LEFT JOIN message_deletes AS d ON d.msg_id = "+table+".id AND d.user_id = ?", viewerID).
Where(table+".conversation_id = ? AND "+table+".id > ? AND "+table+".status <> ? AND d.msg_id IS NULL", conversationID, lastMsgID, entity.StatusHidden). Where(table+".conversation_id = ? AND "+table+".id > ? AND "+table+".status <> ? AND d.msg_id IS NULL", conversationID, lastMsgID, entity.StatusHidden).
Order(table + ".id ASC"). Order(table + ".id ASC").
Limit(limit). Limit(limit).
@ -45,7 +45,7 @@ func (m *Repo) ListByConversation(ctx context.Context, conversationID, viewerID,
} }
var rows []PrivateMessage var rows []PrivateMessage
err = db.Table(table).Select(table+".*"). err = db.Table(table).Select(table+".*").
Joins("LEFT JOIN t_message_deletes AS d ON d.msg_id = "+table+".id AND d.user_id = ?", viewerID). Joins("LEFT JOIN message_deletes AS d ON d.msg_id = "+table+".id AND d.user_id = ?", viewerID).
Where(table+".conversation_id = ? AND "+table+".id > ? AND "+table+".status <> ? AND d.msg_id IS NULL", conversationID, lastMsgID, entity.StatusHidden). Where(table+".conversation_id = ? AND "+table+".id > ? AND "+table+".status <> ? AND d.msg_id IS NULL", conversationID, lastMsgID, entity.StatusHidden).
Order(table + ".id ASC"). Order(table + ".id ASC").
Limit(limit). Limit(limit).

View File

@ -18,15 +18,6 @@ type msgIDBody struct {
type readBody struct { type readBody struct {
ConversationID int64 `json:"conversation_id"` ConversationID int64 `json:"conversation_id"`
MsgID int64 `json:"msg_id"` MsgID int64 `json:"msg_id"`
LastMsgID int64 `json:"last_msg_id"`
}
// readCursor accepts msg_id or last_msg_id. Either value above zero is the read cursor.
func (b readBody) readCursor() int64 {
if b.MsgID > 0 {
return b.MsgID
}
return b.LastMsgID
} }
type syncInboxBody struct { type syncInboxBody struct {
@ -64,11 +55,11 @@ func (s *Server) handleReadMessage(ctx context.Context, conn *session.Conn, fram
return nil, errs.ErrAvailable return nil, errs.ErrAvailable
} }
var in readBody var in readBody
if err := json.Unmarshal(frame.Body, &in); err != nil || in.ConversationID <= 0 || in.readCursor() <= 0 { if err := json.Unmarshal(frame.Body, &in); err != nil || in.ConversationID <= 0 || in.MsgID <= 0 {
return nil, errs.ErrInvalidArgument return nil, errs.ErrInvalidArgument
} }
out, err := s.messages.MarkRead(ctx, conn.UserID(), conn.DeviceID(), &msglogic.ReadInput{ out, err := s.messages.MarkRead(ctx, conn.UserID(), conn.DeviceID(), &msglogic.ReadInput{
ConversationID: in.ConversationID, MsgID: in.readCursor(), ConversationID: in.ConversationID, MsgID: in.MsgID,
}) })
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -37,13 +37,6 @@ func TestReadMessageRegistered(t *testing.T) {
if missing != nil || !errors.Is(err, errs.ErrInvalidArgument) { if missing != nil || !errors.Is(err, errs.ErrInvalidArgument) {
t.Fatalf("missing fields frame=%v err=%v", missing, err) t.Fatalf("missing fields frame=%v err=%v", missing, err)
} }
onlyLast, err := srv.router.Dispatch(t.Context(), conn, protocol.Frame{
Version: protocol.CurrentVersion, Command: protocol.CmdReadMessage,
Body: []byte(`{"conversation_id":` + strconv.FormatInt(convID, 10) + `,"peer_id":1,"last_msg_id":0}`),
})
if onlyLast != nil || !errors.Is(err, errs.ErrInvalidArgument) {
t.Fatalf("zero cursor frame=%v err=%v", onlyLast, err)
}
resp, err := srv.router.Dispatch(t.Context(), conn, protocol.Frame{ resp, err := srv.router.Dispatch(t.Context(), conn, protocol.Frame{
Version: protocol.CurrentVersion, Command: protocol.CmdReadMessage, SequenceID: 4, Version: protocol.CurrentVersion, Command: protocol.CmdReadMessage, SequenceID: 4,
@ -60,22 +53,6 @@ func TestReadMessageRegistered(t *testing.T) {
if err := json.Unmarshal(env.Data, &data); err != nil || data.MsgID != msgID || data.ConversationID != convID { if err := json.Unmarshal(env.Data, &data); err != nil || data.MsgID != msgID || data.ConversationID != convID {
t.Fatalf("data %+v %v", data, err) t.Fatalf("data %+v %v", data, err)
} }
alias, err := srv.router.Dispatch(t.Context(), conn, protocol.Frame{
Version: protocol.CurrentVersion, Command: protocol.CmdReadMessage, SequenceID: 5,
Body: []byte(`{"conversation_id":` + strconv.FormatInt(convID, 10) + `,"peer_id":1,"last_msg_id":` + strconv.FormatInt(msgID, 10) + `}`),
})
if err != nil || alias == nil {
t.Fatalf("last_msg_id %+v %v", alias, err)
}
aliasEnv, err := protocol.DecodeEnvelope(alias.Body)
if err != nil || aliasEnv.Code != protocol.CodeOK {
t.Fatalf("last_msg_id envelope %+v %v", aliasEnv, err)
}
var aliasData msglogic.ReadResult
if err := json.Unmarshal(aliasEnv.Data, &aliasData); err != nil || aliasData.MsgID != msgID || aliasData.ConversationID != convID {
t.Fatalf("last_msg_id data %+v %v", aliasData, err)
}
} }
func readFixture(t *testing.T) (*msglogic.Service, int64, int64, int64) { func readFixture(t *testing.T) (*msglogic.Service, int64, int64, int64) {