refactor: remove SeqNo from message and conversation read state models, update related logic

This commit is contained in:
mlogclub
2026-06-24 22:53:17 +08:00
parent 48a3e86877
commit 85cfee1967
14 changed files with 177 additions and 73 deletions
@@ -698,7 +698,6 @@ func createWorkflowExecutorCustomerMessage(t *testing.T, db *gorm.DB, conversati
SenderType: enums.IMSenderTypeCustomer,
MessageType: enums.IMMessageTypeText,
Content: content,
SeqNo: 1,
SendStatus: enums.IMMessageStatusSent,
SentAt: &now,
}
+1 -11
View File
@@ -37,10 +37,8 @@ func BuildConversationWithLocale(item *models.Conversation, locale string) respo
CustomerUnreadCount: item.CustomerUnreadCount,
AgentUnreadCount: item.AgentUnreadCount,
CustomerLastReadMessageID: readStateMessageID(customerReadState),
CustomerLastReadSeqNo: readStateSeqNo(customerReadState),
CustomerLastReadAt: readStateAt(customerReadState),
AgentLastReadMessageID: readStateMessageID(agentReadState),
AgentLastReadSeqNo: readStateSeqNo(agentReadState),
AgentLastReadAt: readStateAt(agentReadState),
ClosedAt: utils.FormatTimePtr(item.ClosedAt),
ClosedBy: item.ClosedBy,
@@ -157,7 +155,6 @@ func BuildMessageWithReadStatesAndLocale(item *models.Message, agentReadState, c
MessageType: item.MessageType,
Content: localizeRenderableMessageContent(locale, content),
Payload: payload,
SeqNo: item.SeqNo,
SendStatus: item.SendStatus,
SentAt: utils.FormatTimePtr(item.SentAt),
DeliveredAt: utils.FormatTimePtr(item.DeliveredAt),
@@ -282,7 +279,7 @@ func collectMessageSenderNameMaps(list []models.Message) (aiNames map[int64]stri
}
func isMessageRead(item *models.Message, state *models.ConversationReadState) bool {
return item != nil && state != nil && state.LastReadSeqNo >= item.SeqNo
return item != nil && state != nil && state.LastReadMessageID >= item.ID
}
func readMessageAt(item *models.Message, state *models.ConversationReadState) string {
@@ -299,13 +296,6 @@ func readStateMessageID(state *models.ConversationReadState) int64 {
return state.LastReadMessageID
}
func readStateSeqNo(state *models.ConversationReadState) int64 {
if state == nil {
return 0
}
return state.LastReadSeqNo
}
func readStateAt(state *models.ConversationReadState) string {
if state == nil {
return ""
@@ -1,6 +1,8 @@
package builders
import (
"encoding/json"
"strings"
"testing"
"agent-desk/internal/models"
@@ -114,3 +116,21 @@ func TestBuildMessageIncludesWorkflowRunID(t *testing.T) {
t.Fatalf("resp.WorkflowRunID=%d want 9988", resp.WorkflowRunID)
}
}
func TestBuildMessageJSONDoesNotExposeSeqNo(t *testing.T) {
resp := BuildMessageWithReadStatesAndLocale(&models.Message{
ID: 1,
ConversationID: 2,
SenderType: enums.IMSenderTypeCustomer,
MessageType: enums.IMMessageTypeText,
Content: "hello",
}, nil, nil, nil, nil, nil, i18nx.DefaultLocale)
raw, err := json.Marshal(resp)
if err != nil {
t.Fatalf("marshal message response: %v", err)
}
if strings.Contains(string(raw), "seqNo") {
t.Fatalf("message response should not expose seqNo, got %s", raw)
}
}
+1 -3
View File
@@ -380,7 +380,6 @@ type ConversationReadState struct {
ReaderID int64 `gorm:"type:bigint;not null;default:0;uniqueIndex:uk_conversation_reader"`
ExternalReaderID string `gorm:"type:varchar(128);not null;default:'';uniqueIndex:uk_conversation_reader"`
LastReadMessageID int64 `gorm:"type:bigint;not null;default:0;index"`
LastReadSeqNo int64 `gorm:"type:bigint;not null;default:0;index"`
LastReadAt *time.Time `gorm:"type:datetime"`
AuditFields
}
@@ -388,7 +387,7 @@ type ConversationReadState struct {
// Message 会话消息。
type Message struct {
ID int64 `gorm:"primaryKey;autoIncrement"`
ConversationID int64 `gorm:"type:bigint;not null;index;uniqueIndex:uk_conversation_seq;uniqueIndex:uk_conversation_client_msg"`
ConversationID int64 `gorm:"type:bigint;not null;index;uniqueIndex:uk_conversation_client_msg"`
RequestID string `gorm:"type:varchar(128);not null;default:'';index"`
WorkflowRunID int64 `gorm:"type:bigint;not null;default:0;index"`
ClientMsgID string `gorm:"type:varchar(128);not null;default:'';uniqueIndex:uk_conversation_client_msg"`
@@ -398,7 +397,6 @@ type Message struct {
MessageType enums.IMMessageType `gorm:"type:varchar(30);not null;default:'';index"`
Content string `gorm:"type:text"`
Payload string `gorm:"type:text"`
SeqNo int64 `gorm:"type:bigint;not null;default:0;uniqueIndex:uk_conversation_seq"`
SendStatus enums.IMMessageStatus `gorm:"type:int;not null;default:2;index"`
SentAt *time.Time `gorm:"type:datetime;index"`
DeliveredAt *time.Time `gorm:"type:datetime"`
@@ -37,10 +37,8 @@ type ConversationResponse struct {
CustomerUnreadCount int `json:"customerUnreadCount"`
AgentUnreadCount int `json:"agentUnreadCount"`
CustomerLastReadMessageID int64 `json:"customerLastReadMessageId"`
CustomerLastReadSeqNo int64 `json:"customerLastReadSeqNo"`
CustomerLastReadAt string `json:"customerLastReadAt,omitempty"`
AgentLastReadMessageID int64 `json:"agentLastReadMessageId"`
AgentLastReadSeqNo int64 `json:"agentLastReadSeqNo"`
AgentLastReadAt string `json:"agentLastReadAt,omitempty"`
CustomerOnline bool `json:"customerOnline"`
ClosedAt string `json:"closedAt,omitempty"`
@@ -15,7 +15,6 @@ type MessageResponse struct {
MessageType enums.IMMessageType `json:"messageType"`
Content string `json:"content"`
Payload string `json:"payload,omitempty"`
SeqNo int64 `json:"seqNo"`
SendStatus enums.IMMessageStatus `json:"sendStatus"`
SentAt string `json:"sentAt,omitempty"`
DeliveredAt string `json:"deliveredAt,omitempty"`
@@ -43,7 +43,6 @@ func (r *messageRepository) FindLastUnrecalledByConversationID(db *gorm.DB, conv
ret := &models.Message{}
if err := db.
Where("conversation_id = ? AND recalled_at IS NULL AND send_status <> ?", conversationID, 6).
Order("seq_no DESC").
Order("id DESC").
Limit(1).
Take(ret).Error; err != nil {
@@ -118,11 +117,3 @@ func (r *messageRepository) Delete(db *gorm.DB, id int64) {
func (r *messageRepository) GetByClientMsgID(db *gorm.DB, conversationID int64, clientMsgID string) *models.Message {
return r.FindOne(db, sqls.NewCnd().Where("conversation_id = ? AND client_msg_id = ?", conversationID, clientMsgID))
}
// NextSeqNo
func (r *messageRepository) NextSeqNo(db *gorm.DB, conversationID int64) int64 {
if last := r.FindOne(db, sqls.NewCnd().Where("conversation_id = ?", conversationID).Desc("seq_no")); last != nil {
return last.SeqNo + 1
}
return 1
}
@@ -160,11 +160,11 @@ func (s *conversationReadStateService) pickConversationReadStates(list []models.
item := &list[i]
switch item.ReaderType {
case enums.IMSenderTypeAgent:
if agentState == nil || item.LastReadSeqNo > agentState.LastReadSeqNo {
if agentState == nil || item.LastReadMessageID > agentState.LastReadMessageID {
agentState = item
}
case enums.IMSenderTypeCustomer:
if customerState == nil || item.LastReadSeqNo > customerState.LastReadSeqNo {
if customerState == nil || item.LastReadMessageID > customerState.LastReadMessageID {
customerState = item
}
}
@@ -214,7 +214,6 @@ func (s *conversationReadStateService) markReadTxWithCursor(ctx *sqls.TxContext,
ReaderID: c.readerID,
ExternalReaderID: c.externalReaderID,
LastReadMessageID: message.ID,
LastReadSeqNo: message.SeqNo,
LastReadAt: &now,
AuditFields: models.AuditFields{
CreatedAt: now,
@@ -231,19 +230,17 @@ func (s *conversationReadStateService) markReadTxWithCursor(ctx *sqls.TxContext,
return item, nil
}
if item.LastReadSeqNo >= message.SeqNo {
if item.LastReadMessageID >= message.ID {
return item, nil
}
item.LastReadMessageID = message.ID
item.LastReadSeqNo = message.SeqNo
item.LastReadAt = &now
item.UpdatedAt = now
item.UpdateUserID = c.auditUserID
item.UpdateUserName = c.auditUserName
if err := repositories.ConversationReadStateRepository.Updates(ctx.Tx, item.ID, map[string]any{
"last_read_message_id": item.LastReadMessageID,
"last_read_seq_no": item.LastReadSeqNo,
"last_read_at": item.LastReadAt,
"updated_at": item.UpdatedAt,
"update_user_id": item.UpdateUserID,
@@ -254,7 +251,7 @@ func (s *conversationReadStateService) markReadTxWithCursor(ctx *sqls.TxContext,
return item, nil
}
func (s *conversationReadStateService) CountUnreadMessages(ctx *sqls.TxContext, conversationID, lastReadSeqNo int64, senderTypes ...enums.IMSenderType) (int64, error) {
func (s *conversationReadStateService) CountUnreadMessages(ctx *sqls.TxContext, conversationID, lastReadMessageID int64, senderTypes ...enums.IMSenderType) (int64, error) {
normalizedSenderTypes := make([]enums.IMSenderType, 0, len(senderTypes))
for _, senderType := range senderTypes {
if strs.IsBlank(string(senderType)) {
@@ -267,7 +264,7 @@ func (s *conversationReadStateService) CountUnreadMessages(ctx *sqls.TxContext,
}
var count int64
query := ctx.Tx.Model(&models.Message{}).
Where("conversation_id = ? AND seq_no > ? AND recalled_at IS NULL AND send_status <> ?", conversationID, lastReadSeqNo, int(enums.IMMessageStatusRecalled))
Where("conversation_id = ? AND id > ? AND recalled_at IS NULL AND send_status <> ?", conversationID, lastReadMessageID, int(enums.IMMessageStatusRecalled))
if len(normalizedSenderTypes) == 1 {
query = query.Where("sender_type = ?", normalizedSenderTypes[0])
} else {
+6 -6
View File
@@ -603,7 +603,7 @@ func (s *conversationService) markConversationReadWithActor(conversation *models
}
currentReadState := actor.getReadState(conversation.ID)
if currentReadState != nil && currentReadState.LastReadSeqNo >= targetMessage.SeqNo {
if currentReadState != nil && currentReadState.LastReadMessageID >= targetMessage.ID {
if actor.isAgentSide() && conversation.AgentUnreadCount == 0 {
return false, nil
}
@@ -629,10 +629,10 @@ func (s *conversationService) markConversationReadWithActor(conversation *models
if err != nil {
return err
}
if actor.isAgentSide() && currentConversation.AgentUnreadCount == agentUnreadCount && currentReadState != nil && currentReadState.LastReadSeqNo >= targetMessage.SeqNo {
if actor.isAgentSide() && currentConversation.AgentUnreadCount == agentUnreadCount && currentReadState != nil && currentReadState.LastReadMessageID >= targetMessage.ID {
return nil
}
if !actor.isAgentSide() && currentConversation.CustomerUnreadCount == customerUnreadCount && currentReadState != nil && currentReadState.LastReadSeqNo >= targetMessage.SeqNo {
if !actor.isAgentSide() && currentConversation.CustomerUnreadCount == customerUnreadCount && currentReadState != nil && currentReadState.LastReadMessageID >= targetMessage.ID {
return nil
}
updateUserID, updateUserName := actor.conversationUpdateAudit()
@@ -651,15 +651,15 @@ func (s *conversationService) markConversationReadWithActor(conversation *models
}
func (s *conversationService) countUnreadByState(ctx *sqls.TxContext, conversationID int64, state *models.ConversationReadState, senderTypes ...enums.IMSenderType) (int, error) {
lastReadSeqNo := int64(0)
lastReadMessageID := int64(0)
if state != nil {
lastReadSeqNo = state.LastReadSeqNo
lastReadMessageID = state.LastReadMessageID
}
normalizedSenderTypes := make([]enums.IMSenderType, 0, len(senderTypes))
for _, senderType := range senderTypes {
normalizedSenderTypes = append(normalizedSenderTypes, senderType)
}
count, err := ConversationReadStateService.CountUnreadMessages(ctx, conversationID, lastReadSeqNo, normalizedSenderTypes...)
count, err := ConversationReadStateService.CountUnreadMessages(ctx, conversationID, lastReadMessageID, normalizedSenderTypes...)
return int(count), err
}
+9 -12
View File
@@ -126,7 +126,7 @@ func (s *messageService) GetConversationReadTarget(conversationID, messageID int
}
return message, nil
}
return s.FindOne(sqls.NewCnd().Eq("conversation_id", conversationID).Desc("seq_no").Desc("id")), nil
return s.FindOne(sqls.NewCnd().Eq("conversation_id", conversationID).Desc("id")), nil
}
func (s *messageService) SendMessage(conversationID int64, senderType enums.IMSenderType, reqSenderID int64, clientMsgID string, messageType enums.IMMessageType, content, payload string, operator *dto.AuthPrincipal, external *openidentity.ExternalUser) (*models.Message, error) {
@@ -197,11 +197,11 @@ func (s *messageService) RecallAgentMessage(messageID int64, operator *dto.AuthP
message.UpdateUserName = operator.Username
agentReadState, customerReadState := ConversationReadStateService.getConversationReadStates(ctx.Tx, conversation.ID)
agentUnreadCount, err := ConversationReadStateService.CountUnreadMessages(ctx, conversation.ID, s.readSeqNo(agentReadState), enums.IMSenderTypeCustomer)
agentUnreadCount, err := ConversationReadStateService.CountUnreadMessages(ctx, conversation.ID, s.readMessageID(agentReadState), enums.IMSenderTypeCustomer)
if err != nil {
return err
}
customerUnreadCount, err := ConversationReadStateService.CountUnreadMessages(ctx, conversation.ID, s.readSeqNo(customerReadState), enums.IMSenderTypeAgent, enums.IMSenderTypeAI)
customerUnreadCount, err := ConversationReadStateService.CountUnreadMessages(ctx, conversation.ID, s.readMessageID(customerReadState), enums.IMSenderTypeAgent, enums.IMSenderTypeAI)
if err != nil {
return err
}
@@ -302,7 +302,6 @@ func (s *messageService) createAIWelcomeMessage(ctx *sqls.TxContext, conversatio
MessageType: enums.IMMessageTypeText,
Content: content,
Payload: payload,
SeqNo: repositories.MessageRepository.NextSeqNo(ctx.Tx, conversation.ID),
SendStatus: enums.IMMessageStatusSent,
SentAt: &now,
AuditFields: models.AuditFields{
@@ -322,11 +321,11 @@ func (s *messageService) createAIWelcomeMessage(ctx *sqls.TxContext, conversatio
return nil, err
}
agentReadState, customerReadState := ConversationReadStateService.getConversationReadStates(ctx.Tx, conversation.ID)
agentUnreadCount, err := ConversationReadStateService.CountUnreadMessages(ctx, conversation.ID, s.readSeqNo(agentReadState), enums.IMSenderTypeCustomer)
agentUnreadCount, err := ConversationReadStateService.CountUnreadMessages(ctx, conversation.ID, s.readMessageID(agentReadState), enums.IMSenderTypeCustomer)
if err != nil {
return nil, err
}
customerUnreadCount, err := ConversationReadStateService.CountUnreadMessages(ctx, conversation.ID, s.readSeqNo(customerReadState), enums.IMSenderTypeAgent, enums.IMSenderTypeAI)
customerUnreadCount, err := ConversationReadStateService.CountUnreadMessages(ctx, conversation.ID, s.readMessageID(customerReadState), enums.IMSenderTypeAgent, enums.IMSenderTypeAI)
if err != nil {
return nil, err
}
@@ -423,7 +422,6 @@ func (s *messageService) sendValidatedMessage(conversation *models.Conversation,
traceID = tracex.NormalizeRequestID(requestID)
auditUserID = int64(0)
auditUserName = ""
nextSeq = repositories.MessageRepository.NextSeqNo(sqls.DB(), conversation.ID)
)
if operator != nil {
auditUserID = operator.UserID
@@ -443,7 +441,6 @@ func (s *messageService) sendValidatedMessage(conversation *models.Conversation,
MessageType: messageType,
Content: content,
Payload: payload,
SeqNo: nextSeq,
SendStatus: enums.IMMessageStatusSent,
SentAt: &now,
AuditFields: models.AuditFields{
@@ -569,10 +566,10 @@ func (s *messageService) handleReadState(ctx *sqls.TxContext, senderType enums.I
}
}
agentReadState, customerReadState := ConversationReadStateService.getConversationReadStates(ctx.Tx, conversation.ID)
if agentUnreadCount, err = ConversationReadStateService.CountUnreadMessages(ctx, conversation.ID, s.readSeqNo(agentReadState), enums.IMSenderTypeCustomer); err != nil {
if agentUnreadCount, err = ConversationReadStateService.CountUnreadMessages(ctx, conversation.ID, s.readMessageID(agentReadState), enums.IMSenderTypeCustomer); err != nil {
return 0, 0, err
}
if customerUnreadCount, err = ConversationReadStateService.CountUnreadMessages(ctx, conversation.ID, s.readSeqNo(customerReadState), enums.IMSenderTypeAgent, enums.IMSenderTypeAI); err != nil {
if customerUnreadCount, err = ConversationReadStateService.CountUnreadMessages(ctx, conversation.ID, s.readMessageID(customerReadState), enums.IMSenderTypeAgent, enums.IMSenderTypeAI); err != nil {
return 0, 0, err
}
return agentUnreadCount, customerUnreadCount, nil
@@ -706,9 +703,9 @@ func (s *messageService) suffixFilenameForSummary(filename string) string {
return " " + filename
}
func (s *messageService) readSeqNo(state *models.ConversationReadState) int64 {
func (s *messageService) readMessageID(state *models.ConversationReadState) int64 {
if state == nil {
return 0
}
return state.LastReadSeqNo
return state.LastReadMessageID
}
+130 -3
View File
@@ -1,7 +1,9 @@
package services
import (
"fmt"
"strings"
"sync"
"testing"
"time"
@@ -163,9 +165,6 @@ func TestConversationCreateCreatesAIWelcomeMessage(t *testing.T) {
if message.Content != "您好,请问有什么可以帮您?" {
t.Fatalf("expected trimmed welcome content, got %q", message.Content)
}
if message.SeqNo != 1 {
t.Fatalf("expected seq no 1, got %d", message.SeqNo)
}
if message.SendStatus != enums.IMMessageStatusSent {
t.Fatalf("expected sent status, got %d", message.SendStatus)
}
@@ -222,6 +221,134 @@ func TestSendCustomerMessageStoresRequestIDOnMessageAndEvent(t *testing.T) {
}
}
func TestSendCustomerMessagesConcurrentlyAssignsUniqueIDs(t *testing.T) {
db := setupMessageWelcomeTestDB(t)
aiAgent := createWelcomeTestAIAgent(t, db, "")
external := welcomeTestExternalUser("concurrent-user")
conversation, err := ConversationService.Create(external, 11, aiAgent.ID)
if err != nil {
t.Fatalf("create conversation: %v", err)
}
const messageCount = 10
var wg sync.WaitGroup
errCh := make(chan error, messageCount)
for i := 0; i < messageCount; i++ {
i := i
wg.Add(1)
go func() {
defer wg.Done()
_, err := MessageService.SendCustomerMessageWithRequestID(
conversation.ID,
fmt.Sprintf("client-msg-concurrent-%d", i),
enums.IMMessageTypeText,
"hello concurrent",
"",
external,
"trace-concurrent",
)
errCh <- err
}()
}
wg.Wait()
close(errCh)
for err := range errCh {
if err != nil {
t.Fatalf("SendCustomerMessageWithRequestID() concurrent error = %v", err)
}
}
var messages []models.Message
if err := db.
Where("conversation_id = ? AND sender_type = ?", conversation.ID, enums.IMSenderTypeCustomer).
Order("id ASC").
Find(&messages).Error; err != nil {
t.Fatalf("find messages: %v", err)
}
if len(messages) != messageCount {
t.Fatalf("expected %d customer messages, got %d", messageCount, len(messages))
}
seen := make(map[int64]struct{}, messageCount)
for _, message := range messages {
if message.ID <= 0 {
t.Fatalf("expected persisted message id, got %d", message.ID)
}
if _, ok := seen[message.ID]; ok {
t.Fatalf("duplicate message id %d", message.ID)
}
seen[message.ID] = struct{}{}
}
}
func TestUnreadCountUsesLastReadMessageID(t *testing.T) {
db := setupMessageWelcomeTestDB(t)
aiAgent := createWelcomeTestAIAgent(t, db, "")
conversation := createMessageTestConversation(t, db, aiAgent.ID)
now := time.Now()
messages := []models.Message{
{
ConversationID: conversation.ID,
ClientMsgID: "read-message",
SenderType: enums.IMSenderTypeCustomer,
MessageType: enums.IMMessageTypeText,
Content: "read",
SendStatus: enums.IMMessageStatusSent,
SentAt: &now,
AuditFields: models.AuditFields{CreatedAt: now, UpdatedAt: now},
},
{
ConversationID: conversation.ID,
ClientMsgID: "unread-message-1",
SenderType: enums.IMSenderTypeCustomer,
MessageType: enums.IMMessageTypeText,
Content: "unread 1",
SendStatus: enums.IMMessageStatusSent,
SentAt: &now,
AuditFields: models.AuditFields{CreatedAt: now, UpdatedAt: now},
},
{
ConversationID: conversation.ID,
ClientMsgID: "unread-message-2",
SenderType: enums.IMSenderTypeCustomer,
MessageType: enums.IMMessageTypeText,
Content: "unread 2",
SendStatus: enums.IMMessageStatusSent,
SentAt: &now,
AuditFields: models.AuditFields{CreatedAt: now, UpdatedAt: now},
},
}
if err := db.Create(&messages).Error; err != nil {
t.Fatalf("create messages: %v", err)
}
readState := &models.ConversationReadState{
ConversationID: conversation.ID,
ReaderType: enums.IMSenderTypeAgent,
ReaderID: 1,
LastReadMessageID: messages[0].ID,
LastReadAt: &now,
AuditFields: models.AuditFields{CreatedAt: now, UpdatedAt: now},
}
if err := db.Create(readState).Error; err != nil {
t.Fatalf("create read state: %v", err)
}
err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
count, err := ConversationService.countUnreadByState(ctx, conversation.ID, readState, enums.IMSenderTypeCustomer)
if err != nil {
return err
}
if count != 2 {
t.Fatalf("unread count=%d want 2", count)
}
return nil
})
if err != nil {
t.Fatalf("count unread: %v", err)
}
}
func TestSendAIMessageStoresWorkflowRunID(t *testing.T) {
db := setupMessageWelcomeTestDB(t)
aiAgent := createWelcomeTestAIAgent(t, db, "")
-3
View File
@@ -146,7 +146,6 @@ type RealtimeMessageCreatedPayload struct {
MessageType enums.IMMessageType `json:"messageType,omitempty"`
Content string `json:"content,omitempty"`
Payload string `json:"payload,omitempty"`
SeqNo int64 `json:"seqNo,omitempty"`
SendStatus enums.IMMessageStatus `json:"sendStatus,omitempty"`
SentAt string `json:"sentAt,omitempty"`
}
@@ -201,10 +200,8 @@ type RealtimeConversationChangedPayload struct {
CustomerUnreadCount int `json:"customerUnreadCount,omitempty"`
AgentUnreadCount int `json:"agentUnreadCount,omitempty"`
CustomerLastReadMessageID int64 `json:"customerLastReadMessageId,omitempty"`
CustomerLastReadSeqNo int64 `json:"customerLastReadSeqNo,omitempty"`
CustomerLastReadAt string `json:"customerLastReadAt,omitempty"`
AgentLastReadMessageID int64 `json:"agentLastReadMessageId,omitempty"`
AgentLastReadSeqNo int64 `json:"agentLastReadSeqNo,omitempty"`
AgentLastReadAt string `json:"agentLastReadAt,omitempty"`
}
+4 -13
View File
@@ -253,7 +253,9 @@ func (s *wsService) closeSession(session *ClientSession) {
remaining := s.manager.Unregister(session)
close(session.Send)
_ = session.Conn.Close()
if session.Conn != nil {
_ = session.Conn.Close()
}
var discUserID int64
var discExternalID string
@@ -309,7 +311,6 @@ func (s *wsService) PublishMessageCreated(conversation *models.Conversation, mes
MessageType: message.MessageType,
Content: content,
Payload: payload,
SeqNo: message.SeqNo,
SendStatus: message.SendStatus,
SentAt: formatWsTime(message.SentAt),
},
@@ -334,7 +335,6 @@ func (s *wsService) buildRealtimeMessage(item *models.Message) response.MessageR
MessageType: item.MessageType,
Content: content,
Payload: payload,
SeqNo: item.SeqNo,
SendStatus: item.SendStatus,
SentAt: utils.FormatTimePtr(item.SentAt),
DeliveredAt: utils.FormatTimePtr(item.DeliveredAt),
@@ -389,7 +389,7 @@ func (s *wsService) fillRealtimeMessageUserName(ret *response.MessageResponse, u
}
func isRealtimeMessageRead(item *models.Message, state *models.ConversationReadState) bool {
return item != nil && state != nil && state.LastReadSeqNo >= item.SeqNo
return item != nil && state != nil && state.LastReadMessageID >= item.ID
}
func realtimeReadMessageAt(item *models.Message, state *models.ConversationReadState) string {
@@ -438,10 +438,8 @@ func (s *wsService) PublishConversationChanged(conversation *models.Conversation
CustomerUnreadCount: conversation.CustomerUnreadCount,
AgentUnreadCount: conversation.AgentUnreadCount,
CustomerLastReadMessageID: readStateMessageID(customerReadState),
CustomerLastReadSeqNo: readStateSeqNo(customerReadState),
CustomerLastReadAt: readStateAt(customerReadState),
AgentLastReadMessageID: readStateMessageID(agentReadState),
AgentLastReadSeqNo: readStateSeqNo(agentReadState),
AgentLastReadAt: readStateAt(agentReadState),
},
})
@@ -478,13 +476,6 @@ func readStateMessageID(state *models.ConversationReadState) int64 {
return state.LastReadMessageID
}
func readStateSeqNo(state *models.ConversationReadState) int64 {
if state == nil {
return 0
}
return state.LastReadSeqNo
}
func readStateAt(state *models.ConversationReadState) string {
if state == nil {
return ""