feat(conversations): align index finder filters

This commit is contained in:
2026-06-07 03:23:58 +08:00
parent f0a33c3366
commit f001560c81
6 changed files with 271 additions and 31 deletions
+18 -22
View File
@@ -61,30 +61,26 @@ func (h *ConversationHandler) List(c *gin.Context) {
p := pagination.Parse(c)
// Support status filter via query param
status := c.Query("status")
var conversations []model.Conversation
var total int64
if status != "" {
result, count, svcErr := h.conversationSvc.ListByStatus(c.Request.Context(), accountID, status, p.Offset, p.PerPage)
if svcErr != nil {
handleServiceError(c, svcErr)
return
}
conversations = result
total = count
} else {
result, count, svcErr := h.conversationSvc.ListByAccount(c.Request.Context(), accountID, p.Offset, p.PerPage)
if svcErr != nil {
handleServiceError(c, svcErr)
return
}
conversations = result
total = count
var params service.FilterParams
if err := c.ShouldBindQuery(&params); err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
return
}
c.JSON(http.StatusOK, serializeConversationList(c.Request.Context(), h.conversationSvc.DB(), conversations, total))
result, svcErr := h.conversationSvc.ListWithFinder(c.Request.Context(), accountID, getUserID(c), params, p.Offset, p.PerPage)
if svcErr != nil {
handleServiceError(c, svcErr)
return
}
payload := serializeConversationList(c.Request.Context(), h.conversationSvc.DB(), result.Conversations, result.Count.AllCount)
payload.Data.Meta = chatwootConversationCounts{
MineCount: result.Count.MineCount,
AssignedCount: result.Count.AssignedCount,
UnassignedCount: result.Count.UnassignedCount,
AllCount: result.Count.AllCount,
}
c.JSON(http.StatusOK, payload)
}
// Create creates a new conversation.
@@ -222,6 +222,9 @@ func (s *ConversationCrudTestSuite) TestList_Success() {
}
func (s *ConversationCrudTestSuite) TestList_WithStatusFilter() {
resolved := &model.Conversation{AccountID: s.testAccount.ID, InboxID: s.testInbox.ID, ContactID: s.testContact.ID, Status: "resolved", ChannelType: "web_widget", Channel: "web_widget"}
s.Require().NoError(s.db.Create(resolved).Error)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", s.accountURL()+"/conversations?status=open", nil)
s.router.ServeHTTP(w, req)
@@ -240,6 +243,81 @@ func (s *ConversationCrudTestSuite) TestList_WithStatusFilter() {
assert.Equal(s.T(), int64(1), resp.Data.Meta.AllCount)
}
func (s *ConversationCrudTestSuite) TestList_DefaultsToOpenStatus() {
resolved := &model.Conversation{AccountID: s.testAccount.ID, InboxID: s.testInbox.ID, ContactID: s.testContact.ID, Status: "resolved", ChannelType: "web_widget", Channel: "web_widget"}
s.Require().NoError(s.db.Create(resolved).Error)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", s.accountURL()+"/conversations", nil)
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusOK, w.Code)
var resp struct {
Data struct {
Meta struct {
AllCount int64 `json:"all_count"`
} `json:"meta"`
Payload []map[string]interface{} `json:"payload"`
} `json:"data"`
}
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(s.T(), int64(1), resp.Data.Meta.AllCount)
assert.Len(s.T(), resp.Data.Payload, 1)
}
func (s *ConversationCrudTestSuite) TestList_AppliesFinderFiltersAfterCounts() {
assigneeID := uint(42)
assigned := &model.Conversation{AccountID: s.testAccount.ID, InboxID: s.testInbox.ID, ContactID: s.testContact.ID, AssigneeID: &assigneeID, Status: "open", Labels: "vip", ChannelType: "web_widget", Channel: "web_widget"}
s.Require().NoError(s.db.Create(assigned).Error)
unmatched := &model.Conversation{AccountID: s.testAccount.ID, InboxID: s.testInbox.ID, ContactID: s.testContact.ID, Status: "open", Labels: "standard", ChannelType: "web_widget", Channel: "web_widget"}
s.Require().NoError(s.db.Create(unmatched).Error)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", s.accountURL()+"/conversations?labels=vip&assignee_type=assigned", nil)
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusOK, w.Code)
var resp struct {
Data struct {
Meta struct {
AssignedCount int64 `json:"assigned_count"`
UnassignedCount int64 `json:"unassigned_count"`
AllCount int64 `json:"all_count"`
} `json:"meta"`
Payload []map[string]interface{} `json:"payload"`
} `json:"data"`
}
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(s.T(), int64(1), resp.Data.Meta.AllCount)
assert.Equal(s.T(), int64(1), resp.Data.Meta.AssignedCount)
assert.Equal(s.T(), int64(0), resp.Data.Meta.UnassignedCount)
assert.Len(s.T(), resp.Data.Payload, 1)
}
func (s *ConversationCrudTestSuite) TestList_QuerySkipsStatusFilter() {
resolved := &model.Conversation{AccountID: s.testAccount.ID, InboxID: s.testInbox.ID, ContactID: s.testContact.ID, Status: "resolved", ChannelType: "web_widget", Channel: "web_widget"}
s.Require().NoError(s.db.Create(resolved).Error)
s.Require().NoError(s.db.Create(&model.Message{AccountID: s.testAccount.ID, InboxID: s.testInbox.ID, ConversationID: s.testConv.ID, Content: "needle open", MessageType: string(model.MessageTypeIncoming)}).Error)
s.Require().NoError(s.db.Create(&model.Message{AccountID: s.testAccount.ID, InboxID: s.testInbox.ID, ConversationID: resolved.ID, Content: "needle resolved", MessageType: string(model.MessageTypeOutgoing)}).Error)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", s.accountURL()+"/conversations?q=needle&status=open", nil)
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusOK, w.Code)
var resp struct {
Data struct {
Meta struct {
AllCount int64 `json:"all_count"`
} `json:"meta"`
Payload []map[string]interface{} `json:"payload"`
} `json:"data"`
}
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(s.T(), int64(2), resp.Data.Meta.AllCount)
assert.Len(s.T(), resp.Data.Payload, 2)
}
func (s *ConversationCrudTestSuite) TestList_InvalidAccountID() {
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/api/v1/accounts/invalid/conversations", nil)