fix(shangwutong): harden classification operations

This commit is contained in:
2026-09-11 23:09:02 +08:00
parent 31f157a4f5
commit 9b39a574bd
16 changed files with 904 additions and 68 deletions
@@ -389,7 +389,15 @@ func (h *ShangwutongConnectorHandler) UpdateConversationClassification(c *gin.Co
return
}
var conversation model.Conversation
if err := h.db.WithContext(c.Request.Context()).Where("id = ? AND account_id = ?", uint(conversationID), uint(accountID)).First(&conversation).Error; err != nil {
lookup := h.db.WithContext(c.Request.Context()).Where(
"id = ? AND account_id = ? AND (display_id IS NULL OR display_id = 0)", uint(conversationID), uint(accountID),
).First(&conversation)
if errors.Is(lookup.Error, gorm.ErrRecordNotFound) {
lookup = h.db.WithContext(c.Request.Context()).Where(
"display_id = ? AND account_id = ?", uint(conversationID), uint(accountID),
).First(&conversation)
}
if lookup.Error != nil {
h.connectorError(c, http.StatusNotFound, "not_found", "conversation not found", false)
return
}
@@ -21,6 +21,7 @@ import (
"github.com/gochat/gochat/internal/service"
"github.com/gochat/gochat/internal/worker"
"github.com/stretchr/testify/require"
"gorm.io/datatypes"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
@@ -156,6 +157,32 @@ func TestShangwutongConnectorContactMetadataIsScopedAndIdempotent(t *testing.T)
require.Contains(t, string(job.Payload), `"contact_name":"Renamed"`)
}
func TestShangwutongClassificationUpdateAcceptsConversationDisplayID(t *testing.T) {
router, db, _, inbox, _ := setupShangwutongConnectorAPI(t)
contact := &model.Contact{AccountID: inbox.AccountID, Name: "Visitor"}
require.NoError(t, db.Create(contact).Error)
conversation := &model.Conversation{
AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open",
DisplayID: uintPointer(9001),
}
require.NoError(t, db.Create(conversation).Error)
require.NoError(t, db.Create(&model.ContactInbox{
ContactID: contact.ID, InboxID: inbox.ID, SourceID: "visitor", ChannelMetadata: datatypes.JSON([]byte(`{"cid":"cid-1"}`)),
}).Error)
require.NoError(t, db.Create(&model.ShangwutongClassificationCache{
InboxID: inbox.ID, ConversationKinds: datatypes.JSON([]byte(`[{"id":"kind-1","name":"Normal"}]`)),
CustomerColorKinds: datatypes.JSON([]byte(`[]`)), SyncStatus: "succeeded",
}).Error)
path := fmt.Sprintf("/api/v1/accounts/%d/conversations/%d/shangwutong-classifications", inbox.AccountID, *conversation.DisplayID)
response := connectorRequest(t, router, "", http.MethodPatch, path, map[string]string{"chat_kind_id": "kind-1"})
require.Equal(t, http.StatusAccepted, response.Code, response.Body.String())
var job model.BackgroundJob
require.NoError(t, db.Where("job_type = ?", service.TaskTypeShangwutongWebhookDelivery).Order("id DESC").First(&job).Error)
require.Contains(t, string(job.Payload), fmt.Sprintf(`"conversation_id":%d`, conversation.ID))
}
func uintPointer(value uint) *uint { return &value }
func setupShangwutongConnectorAPI(t *testing.T) (*gin.Engine, *gorm.DB, string, *model.Inbox, *model.Inbox) {
t.Helper()
gin.SetMode(gin.TestMode)
@@ -165,7 +192,7 @@ func setupShangwutongConnectorAPI(t *testing.T) (*gin.Engine, *gorm.DB, string,
&model.Account{}, &model.Inbox{}, &model.ChannelShangwutongConfig{}, &channelmodel.ChannelAPI{},
&model.PlatformApp{}, &model.AccessToken{}, &model.Permissible{}, &model.Contact{}, &model.Conversation{},
&model.ContactInbox{},
&model.Message{}, &model.Attachment{}, &model.BackgroundJob{},
&model.Message{}, &model.Attachment{}, &model.BackgroundJob{}, &model.ShangwutongClassificationCache{},
))
active := true
app := &model.PlatformApp{Name: "SWT Connector", Type: "integration", Status: "active", Active: &active, Config: json.RawMessage(`{"connector":"shangwutong"}`)}
@@ -198,7 +225,8 @@ func setupShangwutongConnectorAPI(t *testing.T) (*gin.Engine, *gorm.DB, string,
inboxes = append(inboxes, inbox)
}
messageSvc := service.NewMessageService(repository.NewMessageRepo(db), channel.NewDispatcher(), nil)
handler := NewShangwutongConnectorHandler(db, messageSvc)
workers := worker.NewWorkerPool(db)
handler := NewShangwutongConnectorHandler(db, messageSvc, workers)
router := gin.New()
group := router.Group("/api/v1/connector/shangwutong")
group.Use(middleware.ConnectorServiceAuth(db))
@@ -207,6 +235,7 @@ func setupShangwutongConnectorAPI(t *testing.T) (*gin.Engine, *gorm.DB, string,
group.PUT("/inboxes/:inbox_id/status", handler.UpdateInboxStatus)
group.PUT("/inboxes/:inbox_id/messages/:message_id/status", handler.UpdateMessageStatus)
group.PATCH("/inboxes/:inbox_id/contacts/:source_id", handler.UpdateContactMetadata)
router.PATCH("/api/v1/accounts/:account_id/conversations/:conversation_id/shangwutong-classifications", handler.UpdateConversationClassification)
return router, db, token, inboxes[0], inboxes[1]
}