refactor: update visitor references to guest in WebSocket service and related files
This commit is contained in:
@@ -28,9 +28,9 @@ func NewServer() (*iris.Application, error) {
|
|||||||
app := iris.New()
|
app := iris.New()
|
||||||
corsHandler := cors.New().
|
corsHandler := cors.New().
|
||||||
AllowOrigin("*").
|
AllowOrigin("*").
|
||||||
AllowHeaders("Origin", "Content-Type", "Accept", "Authorization", "X-Requested-With", "X-Visitor-Id", "X-Channel-Id", "X-External-Source", "X-External-Id", "X-External-Name").
|
AllowHeaders("Origin", "Content-Type", "Accept", "Authorization", "X-Requested-With", "X-Guest-Id", "X-Channel-Id", "X-External-Source", "X-External-Id", "X-External-Name").
|
||||||
MaxAge(600).
|
MaxAge(600).
|
||||||
ExposeHeaders("Content-Length", "Content-Type", "Authorization", "X-Visitor-Id", "X-Channel-Id", "X-External-Source", "X-External-Id", "X-External-Name").
|
ExposeHeaders("Content-Length", "Content-Type", "Authorization", "X-Guest-Id", "X-Channel-Id", "X-External-Source", "X-External-Id", "X-External-Name").
|
||||||
Handler()
|
Handler()
|
||||||
app.UseRouter(func(ctx iris.Context) {
|
app.UseRouter(func(ctx iris.Context) {
|
||||||
// WebSocket upgrade is validated by the upgrader's origin policy.
|
// WebSocket upgrade is validated by the upgrader's origin policy.
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func BuildConversation(item *models.Conversation) response.ConversationResponse
|
|||||||
AgentLastReadMessageID: readStateMessageID(agentReadState),
|
AgentLastReadMessageID: readStateMessageID(agentReadState),
|
||||||
AgentLastReadSeqNo: readStateSeqNo(agentReadState),
|
AgentLastReadSeqNo: readStateSeqNo(agentReadState),
|
||||||
AgentLastReadAt: readStateAt(agentReadState),
|
AgentLastReadAt: readStateAt(agentReadState),
|
||||||
CustomerOnline: services.WsService.IsVisitorOnline(item.ExternalID),
|
CustomerOnline: services.WsService.IsGuestOnline(item.ExternalID),
|
||||||
ClosedAt: utils.FormatTimePtr(item.ClosedAt),
|
ClosedAt: utils.FormatTimePtr(item.ClosedAt),
|
||||||
ClosedBy: item.ClosedBy,
|
ClosedBy: item.ClosedBy,
|
||||||
CloseReason: item.CloseReason,
|
CloseReason: item.CloseReason,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
realtimeTopicUserPrefix = "user:"
|
realtimeTopicUserPrefix = "user:"
|
||||||
realtimeTopicVisitorPrefix = "visitor:"
|
realtimeTopicGuestPrefix = "guest:"
|
||||||
realtimeTopicAdminPrefix = "admin:"
|
realtimeTopicAdminPrefix = "admin:"
|
||||||
realtimeTopicConversationPrefix = "conversation:"
|
realtimeTopicConversationPrefix = "conversation:"
|
||||||
realtimeTopicAdminAll = "admin:all"
|
realtimeTopicAdminAll = "admin:all"
|
||||||
@@ -54,7 +54,7 @@ type RealtimeEventPayload interface {
|
|||||||
type RealtimeConnectedPayload struct {
|
type RealtimeConnectedPayload struct {
|
||||||
ConnID string `json:"connId,omitempty"`
|
ConnID string `json:"connId,omitempty"`
|
||||||
UserID int64 `json:"userId,omitempty"`
|
UserID int64 `json:"userId,omitempty"`
|
||||||
VisitorID string `json:"visitorId,omitempty"`
|
GuestID string `json:"guestId,omitempty"`
|
||||||
Role string `json:"role,omitempty"`
|
Role string `json:"role,omitempty"`
|
||||||
TerminalType string `json:"terminalType,omitempty"`
|
TerminalType string `json:"terminalType,omitempty"`
|
||||||
Topics []string `json:"topics,omitempty"`
|
Topics []string `json:"topics,omitempty"`
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ func (s *wsService) upgradeConnection(ctx iris.Context, principal *dto.AuthPrinc
|
|||||||
Payload: RealtimeConnectedPayload{
|
Payload: RealtimeConnectedPayload{
|
||||||
ConnID: session.ID,
|
ConnID: session.ID,
|
||||||
UserID: logUserID,
|
UserID: logUserID,
|
||||||
VisitorID: logExternalID,
|
GuestID: logExternalID,
|
||||||
Role: role,
|
Role: role,
|
||||||
TerminalType: session.TerminalType,
|
TerminalType: session.TerminalType,
|
||||||
Topics: session.topicList(),
|
Topics: session.topicList(),
|
||||||
@@ -500,12 +500,12 @@ func (s *wsService) PublishToTopics(topics []string, event RealtimeEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *wsService) IsVisitorOnline(visitorID string) bool {
|
func (s *wsService) IsGuestOnline(guestID string) bool {
|
||||||
visitorID = strings.TrimSpace(visitorID)
|
guestID = strings.TrimSpace(guestID)
|
||||||
if visitorID == "" {
|
if guestID == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return s.manager.HasTopic(s.visitorTopic(visitorID))
|
return s.manager.HasTopic(s.guestTopic(guestID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *wsService) routeConversationTopics(conversation *models.Conversation) []string {
|
func (s *wsService) routeConversationTopics(conversation *models.Conversation) []string {
|
||||||
@@ -515,7 +515,7 @@ func (s *wsService) routeConversationTopics(conversation *models.Conversation) [
|
|||||||
|
|
||||||
topics := []string{s.conversationTopic(conversation.ID)}
|
topics := []string{s.conversationTopic(conversation.ID)}
|
||||||
if strings.TrimSpace(conversation.ExternalID) != "" {
|
if strings.TrimSpace(conversation.ExternalID) != "" {
|
||||||
topics = append(topics, s.visitorTopic(conversation.ExternalID))
|
topics = append(topics, s.guestTopic(conversation.ExternalID))
|
||||||
}
|
}
|
||||||
if conversation.CurrentAssigneeID > 0 {
|
if conversation.CurrentAssigneeID > 0 {
|
||||||
topics = append(topics, s.adminTopic(conversation.CurrentAssigneeID))
|
topics = append(topics, s.adminTopic(conversation.CurrentAssigneeID))
|
||||||
@@ -537,9 +537,9 @@ func (s *wsService) defaultTopics(session *ClientSession) []string {
|
|||||||
}
|
}
|
||||||
return []string{s.adminTopic(session.Principal.UserID), realtimeTopicAdminAll}
|
return []string{s.adminTopic(session.Principal.UserID), realtimeTopicAdminAll}
|
||||||
default:
|
default:
|
||||||
// 开放 IM:仅 External、无 AuthPrincipal 的访客连接必须仍能订阅 visitor:{externalId},否则收不到推送。
|
// 开放 IM:仅 External、无 AuthPrincipal 的访客连接必须仍能订阅 guest:{externalId},否则收不到推送。
|
||||||
if session.External != nil && strings.TrimSpace(session.External.ExternalID) != "" {
|
if session.External != nil && strings.TrimSpace(session.External.ExternalID) != "" {
|
||||||
return []string{s.visitorTopic(session.External.ExternalID)}
|
return []string{s.guestTopic(session.External.ExternalID)}
|
||||||
}
|
}
|
||||||
if session.Principal != nil && session.Principal.UserID > 0 {
|
if session.Principal != nil && session.Principal.UserID > 0 {
|
||||||
return []string{s.userTopic(session.Principal.UserID)}
|
return []string{s.userTopic(session.Principal.UserID)}
|
||||||
@@ -637,8 +637,8 @@ func (s *wsService) userTopic(userID int64) string {
|
|||||||
return realtimeTopicUserPrefix + strconv.FormatInt(userID, 10)
|
return realtimeTopicUserPrefix + strconv.FormatInt(userID, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *wsService) visitorTopic(visitorID string) string {
|
func (s *wsService) guestTopic(guestID string) string {
|
||||||
return realtimeTopicVisitorPrefix + strings.TrimSpace(visitorID)
|
return realtimeTopicGuestPrefix + strings.TrimSpace(guestID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *wsService) adminTopic(userID int64) string {
|
func (s *wsService) adminTopic(userID int64) string {
|
||||||
|
|||||||
+10
-10
@@ -114,7 +114,7 @@ export type ImWidgetConfig = {
|
|||||||
width?: string
|
width?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const VISITOR_STORAGE_KEY = "cs_agent_im_visitor_id"
|
const GUEST_STORAGE_KEY = "cs_agent_im_guest_id"
|
||||||
const API_BASE_URL =
|
const API_BASE_URL =
|
||||||
process.env.NEXT_PUBLIC_API_BASE_URL?.trim() || ""
|
process.env.NEXT_PUBLIC_API_BASE_URL?.trim() || ""
|
||||||
const OPEN_IM_CHANNEL_ID =
|
const OPEN_IM_CHANNEL_ID =
|
||||||
@@ -122,21 +122,21 @@ const OPEN_IM_CHANNEL_ID =
|
|||||||
const OPEN_IM_EXTERNAL_SOURCE =
|
const OPEN_IM_EXTERNAL_SOURCE =
|
||||||
process.env.NEXT_PUBLIC_OPEN_IM_EXTERNAL_SOURCE?.trim() || "web_chat"
|
process.env.NEXT_PUBLIC_OPEN_IM_EXTERNAL_SOURCE?.trim() || "web_chat"
|
||||||
|
|
||||||
function buildVisitorId() {
|
function buildGuestId() {
|
||||||
return `visitor_${generateUUID()}`
|
return `guest_${generateUUID()}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getImVisitorId() {
|
export function getGuestId() {
|
||||||
if (typeof window === "undefined") {
|
if (typeof window === "undefined") {
|
||||||
return "visitor_ssr"
|
return ""
|
||||||
}
|
}
|
||||||
const existing = window.localStorage.getItem(VISITOR_STORAGE_KEY)?.trim()
|
const existing = window.localStorage.getItem(GUEST_STORAGE_KEY)?.trim()
|
||||||
if (existing) {
|
if (existing) {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
const visitorId = buildVisitorId()
|
const guestId = buildGuestId()
|
||||||
window.localStorage.setItem(VISITOR_STORAGE_KEY, visitorId)
|
window.localStorage.setItem(GUEST_STORAGE_KEY, guestId)
|
||||||
return visitorId
|
return guestId
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRuntimeImConfig() {
|
function getRuntimeImConfig() {
|
||||||
@@ -158,7 +158,7 @@ function createImHeaders() {
|
|||||||
const config = getRuntimeImConfig()
|
const config = getRuntimeImConfig()
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
"X-External-Source": config.externalSource,
|
"X-External-Source": config.externalSource,
|
||||||
"X-External-Id": config.externalId || getImVisitorId(),
|
"X-External-Id": config.externalId || getGuestId(),
|
||||||
"X-Channel-Id": config.channelId,
|
"X-Channel-Id": config.channelId,
|
||||||
}
|
}
|
||||||
if (config.externalName) {
|
if (config.externalName) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createWebSocketBaseUrl } from "@/lib/api/websocket"
|
import { createWebSocketBaseUrl } from "@/lib/api/websocket"
|
||||||
import { getImVisitorId, type ImMessage } from "@/lib/api/im"
|
import { getGuestId, type ImMessage } from "@/lib/api/im"
|
||||||
import { readKefuWidgetConfig } from "@/lib/kefu-widget-config"
|
import { readKefuWidgetConfig } from "@/lib/kefu-widget-config"
|
||||||
import type {
|
import type {
|
||||||
RealtimeConversationPatch,
|
RealtimeConversationPatch,
|
||||||
@@ -20,7 +20,7 @@ export function createImRealtimeConnection() {
|
|||||||
? apiBaseUrl.replace(/^http/, "ws").replace(/\/$/, "")
|
? apiBaseUrl.replace(/^http/, "ws").replace(/\/$/, "")
|
||||||
: createWebSocketBaseUrl()
|
: createWebSocketBaseUrl()
|
||||||
const resolvedExternalId = encodeURIComponent(
|
const resolvedExternalId = encodeURIComponent(
|
||||||
(config.externalId ?? "").trim() || getImVisitorId()
|
(config.externalId ?? "").trim() || getGuestId()
|
||||||
)
|
)
|
||||||
const externalSource = encodeURIComponent(
|
const externalSource = encodeURIComponent(
|
||||||
(config.externalSource ?? "web_chat").trim() || "web_chat"
|
(config.externalSource ?? "web_chat").trim() || "web_chat"
|
||||||
|
|||||||
Reference in New Issue
Block a user