fix(shangwutong): 展示会话来源信息
This commit is contained in:
@@ -836,7 +836,8 @@ func (h *ConversationHandler) UpdateCustomAttributes(c *gin.Context) {
|
||||
if !requireConnectorShangwutongConversation(c, h.conversationSvc.DB(), conversation) {
|
||||
return
|
||||
}
|
||||
conversation, svcErr := h.conversationSvc.UpdateCustomAttributes(c.Request.Context(), accountID, conversation.ID, req.CustomAttributes)
|
||||
requestContext := service.WithShangwutongRequestMetadata(c.Request.Context(), middleware.IsConnectorService(c), currentUserID(c))
|
||||
conversation, svcErr := h.conversationSvc.UpdateCustomAttributes(requestContext, accountID, conversation.ID, req.CustomAttributes)
|
||||
if svcErr != nil {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/gochat/gochat/internal/channel"
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
"github.com/gochat/gochat/internal/repository"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func TestUpdateCustomAttributesMergesConnectorPayload(t *testing.T) {
|
||||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.AutoMigrate(&model.Conversation{}))
|
||||
|
||||
conversation := model.Conversation{
|
||||
AccountID: 1,
|
||||
CustomAttributes: datatypes.JSON([]byte(`{"swt_sid":"sid","swt_state":"chatting"}`)),
|
||||
}
|
||||
require.NoError(t, db.Create(&conversation).Error)
|
||||
|
||||
svc := NewConversationService(repository.NewConversationRepo(db), repository.NewMessageRepo(db), channel.NewDispatcher(), nil, nil, nil, nil)
|
||||
ctx := WithShangwutongRequestMetadata(context.Background(), true, 0)
|
||||
updated, err := svc.UpdateCustomAttributes(ctx, 1, conversation.ID, datatypes.JSON([]byte(`{"swt_source_channel":"百度搜索推广"}`)))
|
||||
require.NoError(t, err)
|
||||
|
||||
attributes := map[string]any{}
|
||||
require.NoError(t, json.Unmarshal(updated.CustomAttributes, &attributes))
|
||||
require.Equal(t, "sid", attributes["swt_sid"])
|
||||
require.Equal(t, "chatting", attributes["swt_state"])
|
||||
require.Equal(t, "百度搜索推广", attributes["swt_source_channel"])
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
@@ -1646,6 +1647,28 @@ func (s *ConversationService) UpdateCustomAttributes(ctx context.Context, accoun
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metadata, _ := ctx.Value(shangwutongRequestMetadataKey{}).(shangwutongRequestMetadata)
|
||||
if metadata.ConnectorOrigin {
|
||||
merged := map[string]any{}
|
||||
if len(conversation.CustomAttributes) > 0 {
|
||||
if err := json.Unmarshal(conversation.CustomAttributes, &merged); err != nil {
|
||||
return nil, fmt.Errorf("decode existing conversation custom attributes: %w", err)
|
||||
}
|
||||
}
|
||||
incoming := map[string]any{}
|
||||
if err := json.Unmarshal(attrs, &incoming); err != nil {
|
||||
return nil, fmt.Errorf("decode incoming conversation custom attributes: %w", err)
|
||||
}
|
||||
for key, value := range incoming {
|
||||
merged[key] = value
|
||||
}
|
||||
encoded, err := json.Marshal(merged)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("encode merged conversation custom attributes: %w", err)
|
||||
}
|
||||
attrs = datatypes.JSON(encoded)
|
||||
}
|
||||
|
||||
if err := s.repo.UpdateCustomAttributes(ctx, id, attrs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
DELETE FROM custom_attribute_definitions
|
||||
WHERE attribute_model = 'conversation_attribute'
|
||||
AND attribute_name IN (
|
||||
'swt_source_url',
|
||||
'swt_source_search_term',
|
||||
'swt_source_purchase_term',
|
||||
'swt_source_keyword_id',
|
||||
'swt_source_channel',
|
||||
'swt_source_realtime_location',
|
||||
'swt_source_region',
|
||||
'swt_source_ad_account_id',
|
||||
'swt_source_wakeable',
|
||||
'swt_baidu_conversation_type',
|
||||
'swt_baidu_agent_name',
|
||||
'swt_baidu_ssid'
|
||||
);
|
||||
|
||||
-- Conversation values are retained to avoid losing source-attribution data.
|
||||
@@ -0,0 +1,46 @@
|
||||
INSERT INTO custom_attribute_definitions (
|
||||
account_id,
|
||||
attribute_name,
|
||||
attribute_display_name,
|
||||
attribute_type,
|
||||
attribute_model,
|
||||
description,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
SELECT
|
||||
accounts.id,
|
||||
definitions.attribute_name,
|
||||
definitions.attribute_display_name,
|
||||
definitions.attribute_type,
|
||||
'conversation_attribute',
|
||||
definitions.description,
|
||||
CURRENT_TIMESTAMP,
|
||||
CURRENT_TIMESTAMP
|
||||
FROM accounts
|
||||
CROSS JOIN (VALUES
|
||||
('swt_source_url', '来源页面', 'link', '商务通访客进入咨询时的来源页面'),
|
||||
('swt_source_search_term', '搜索词', 'text', '商务通 kind=8 来源信息中的搜索词'),
|
||||
('swt_source_purchase_term', '购买词', 'text', '商务通 kind=8 来源信息中的购买词'),
|
||||
('swt_source_keyword_id', '关键词 ID', 'text', '商务通 kind=8 来源信息中的推广关键词 ID'),
|
||||
('swt_source_channel', '流量渠道', 'text', '商务通 kind=8 来源信息中的流量渠道'),
|
||||
('swt_source_realtime_location', '实时位置', 'text', '商务通 kind=8 来源信息中的访客实时位置'),
|
||||
('swt_source_region', '地域', 'text', '商务通 kind=8 来源信息中的地域'),
|
||||
('swt_source_ad_account_id', '推广账户 ID', 'text', '商务通 kind=8 来源信息中的推广账户 ID'),
|
||||
('swt_source_wakeable', '是否可唤醒', 'text', '商务通 kind=8 来源信息中的访客可唤醒状态'),
|
||||
('swt_baidu_conversation_type', '百度会话类型', 'text', '商务通 kind=8 来源信息中的百度会话类型'),
|
||||
('swt_baidu_agent_name', '百度智能体名称', 'text', '商务通 kind=8 来源信息中的百度智能体名称'),
|
||||
('swt_baidu_ssid', '百度 SSID', 'text', '商务通 kind=8 来源信息中的百度会话 SSID')
|
||||
) AS definitions(attribute_name, attribute_display_name, attribute_type, description)
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM custom_attribute_definitions existing
|
||||
WHERE existing.account_id = accounts.id
|
||||
AND existing.attribute_name = definitions.attribute_name
|
||||
AND existing.attribute_model = 'conversation_attribute'
|
||||
AND existing.deleted_at IS NULL
|
||||
);
|
||||
|
||||
-- Historical values are backfilled by replaying the raw kind=8 events from the
|
||||
-- Shangwutong Connector store. PostgreSQL does not contain the original encoded
|
||||
-- payload, so this migration intentionally limits itself to schema definitions.
|
||||
Reference in New Issue
Block a user