补全 P0:筛选、访客图片与双向输入状态

- 工作台会话列表支持状态/紧急筛选
- 访客 Widget 支持 jpg/png/gif 图片预览发送
- WebSocket 支持访客输入中通知客服,客服输入中通知访客
- 工作台聊天区展示「访客正在输入」动画
This commit is contained in:
yml2213
2026-07-15 11:05:37 +08:00
parent 3ffa9ddc89
commit d105840b1f
4 changed files with 326 additions and 31 deletions
+43 -2
View File
@@ -135,21 +135,62 @@ func (h *Hub) BroadcastToVisitor(tenantID, sessionID uint, message []byte) {
}
}
// BroadcastToSessionStaff 仅推送给可查看该会话的工作人员(不含访客)。
func (h *Hub) BroadcastToSessionStaff(tenantID uint, agentID *uint, message []byte) {
h.mu.RLock()
defer h.mu.RUnlock()
for client := range h.clients {
if client.TenantID != tenantID || client.Kind != "agent" {
continue
}
if client.Role == "admin" || client.Role == "supervisor" ||
(agentID != nil && client.Role == "agent" && client.UserID == *agentID) {
h.send(client, message)
}
}
}
func handleClientEvent(client *Client, event ClientEvent) {
if client.Kind != "agent" || event.Type != "typing" || event.SessionID == 0 {
if event.Type != "typing" || event.SessionID == 0 {
return
}
var session model.Session
if err := model.DB.Where("id = ? AND tenant_id = ?", event.SessionID, client.TenantID).First(&session).Error; err != nil {
return
}
// 访客输入中 → 通知可接待的客服
if client.Kind == "visitor" {
if client.SessionID == nil || *client.SessionID != event.SessionID {
return
}
if session.Status == "ended" || session.Status == "archived" {
return
}
payload, err := NewEvent("typing", session.ID, map[string]string{"from": "visitor"})
if err != nil {
return
}
if session.AgentID == nil {
DefaultHub.BroadcastToTenantStaff(session.TenantID, payload)
return
}
DefaultHub.BroadcastToSessionStaff(session.TenantID, session.AgentID, payload)
return
}
// 客服输入中 → 通知访客
if client.Kind != "agent" {
return
}
if client.Role == "agent" && (session.AgentID == nil || *session.AgentID != client.UserID) {
return
}
if client.Role != "agent" && client.Role != "admin" && client.Role != "supervisor" {
return
}
payload, err := NewEvent("typing", session.ID, nil)
payload, err := NewEvent("typing", session.ID, map[string]string{"from": "agent"})
if err == nil {
DefaultHub.BroadcastToVisitor(session.TenantID, session.ID, payload)
}