From a3950e5dc09c6777acb298522125d04c53a0cd63 Mon Sep 17 00:00:00 2001 From: zhangsz Date: Fri, 18 Jul 2025 16:26:43 +0800 Subject: [PATCH] fix: mf cdr event adjust --- features/cdr/cdrevent.go | 264 ++++++++++++++++++++------------------- 1 file changed, 133 insertions(+), 131 deletions(-) diff --git a/features/cdr/cdrevent.go b/features/cdr/cdrevent.go index 6b9a5f9c..643383ee 100644 --- a/features/cdr/cdrevent.go +++ b/features/cdr/cdrevent.go @@ -79,151 +79,153 @@ func PostCDREventFrom(w http.ResponseWriter, r *http.Request) { // MF网元类型特殊处理, 未接电话的回拨工单流转处理 if neTypeLower == "mf" && cdrEvent.CDR["recordType"] == "MOC" && cdrEvent.CDR["agentName"] == "" { - // 发送到MF网元 // 构造网元MF的API地址 url := fmt.Sprintf("http://%s:%d/api/rest/systemManagement/v1/elementType/%s/objectType/config/agents", neInfo.IP, neInfo.Port, strings.ToLower(neInfo.NeType)) - // 发送HTTP请求获取座席列表 - resp, err := http.Get(url) - if err != nil { - log.Error("Failed to get MF agents", err) + if err := handleMFMissedCallCDR(url, cdrEvent); err != nil { + log.Error("Failed to handle MF missed call CDR", err) services.ResponseInternalServerError500ProcessError(w, err) return } - defer resp.Body.Close() - - // 解析座席列表响应 - var agentResp struct { - Code int `json:"code"` - Data []ueCallBackTicket.AgentInfo `json:"data"` - Msg string `json:"msg"` - } - - if err := json.NewDecoder(resp.Body).Decode(&agentResp); err != nil { - log.Error("Failed to decode MF agents response", err) - services.ResponseInternalServerError500ProcessError(w, err) - return - } - - // 调用服务获取最新一个被分配工单的座席和下一个要分配的座席 - mfService := ueCallBackTicket.NewCallbackTicketService() - lastAgentName, err := mfService.GetLastAssignedAgent() - if err != nil { - log.Error("Failed to get last assigned agent", err) - // 可以继续执行,不返回错误 - } - - // 选择下一个要分配的座席 - selectedAgent := mfService.SelectNextAgent(agentResp.Data, lastAgentName) - - if selectedAgent != nil { - // 创建回调工单 - var updatedAt *int64 = nil - ticket := ueCallBackTicket.CallbackTicket{ - CallerNumber: cdrEvent.CDR["callerParty"].(string), - CalleeNumber: cdrEvent.CDR["calledParty"].(string), - Status: ueCallBackTicket.TicketStatusNew.Enum(), - AgentName: selectedAgent.Name, - AgentEmail: selectedAgent.Email, - AgentMobile: selectedAgent.Mobile, - Comment: "", - RmUid: cdrEvent.RmUID, - CreatedAt: time.Now().UnixMicro(), - UpdatedAt: updatedAt, - } - if err := mfService.InsertCallbackTicket(&ticket); err != nil { - log.Error("Failed to insert MF callback ticket", err) - // services.ResponseInternalServerError500ProcessError(w, err) - // return - } - // 新工单分配后发送邮件通知 - if selectedAgent.Email != "" { - // 发送邮件通知 - emailConfig := config.GetSMTPConfig() - if emailConfig != nil && emailConfig.Enabled { - // 创建配置副本,避免修改全局配置 - emailCopy := *emailConfig // 浅拷贝结构体 - - // 合并配置中的To地址和当前工单的座席邮箱 - var recipients []string - - // 添加配置中的原始收件人(如管理员、监控人员等) - if len(emailConfig.To) > 0 { - recipients = append(recipients, strings.Split(emailConfig.To, ",")...) - } - - // 添加当前工单的座席邮箱 - recipients = append(recipients, ticket.AgentEmail) - - // 去重处理(避免重复邮箱) - emailCopy.To = strings.Join(email.RemoveDuplicateEmails(recipients), ",") - - // 设置邮件主题和内容 - emailCopy.Subject = "新工单分配通知" - emailCopy.Body = fmt.Sprintf("您被分配了一个新的回拨工单(编号:%d, 主叫号码:%s), 请及时处理.", - ticket.TicketId, ticket.CallerNumber) - go email.SendEmailWithGomail(emailCopy) // 异步发送 - } - } - } - log.Warn("No available agents found for callback ticket") } // MF网元类型特殊处理, 处理座席回拨的工单流转 if neTypeLower == "mf" && cdrEvent.CDR["recordType"] == "MTC" { - // 获取座席号码(主叫)和被叫号码 - agentNumber, ok1 := cdrEvent.CDR["callerParty"].(string) - callerNumber, ok2 := cdrEvent.CDR["calledParty"].(string) - - if !ok1 || !ok2 { - log.Error("Invalid CDR format: missing callerParty or calledParty") - services.ResponseInternalServerError500ProcessError(w, fmt.Errorf("invalid CDR format")) - return - } - - // 获取通话时长 - callDuration, ok := cdrEvent.CDR["callDuration"].(float64) - if !ok { - // 尝试其他可能的类型 - if durationInt, ok := cdrEvent.CDR["callDuration"].(int); ok { - callDuration = float64(durationInt) - } else { - log.Error("Invalid CDR format: callDuration is not a number") - services.ResponseInternalServerError500ProcessError(w, fmt.Errorf("invalid CDR format")) - return - } - } - - // 通过座席号码和主叫号码查找符合条件的工单 - mfService := ueCallBackTicket.NewCallbackTicketService() - ticket, err := mfService.FindCallbackTicketByAgentAndCaller(agentNumber, callerNumber) - if err != nil { - log.Error("Failed to find callback ticket", err) + if err := handleMFCDRCallBack(cdrEvent); err != nil { + log.Error("Failed to handle MF CDR callback", err) services.ResponseInternalServerError500ProcessError(w, err) return } - - if ticket == nil { - // 没有找到对应的工单,可能是手动呼叫,不处理 - log.Warn(fmt.Sprintf("No callback ticket found for agent %s and caller %s", agentNumber, callerNumber)) - services.ResponseStatusOK204NoContent(w) - return - } - - // 获取通话信息 - seizureTime, _ := cdrEvent.CDR["seizureTime"].(string) - releaseTime, _ := cdrEvent.CDR["releaseTime"].(string) - cause, _ := cdrEvent.CDR["cause"].(string) - - // 处理回拨结果并更新工单 - if err := mfService.ProcessCallbackResult(ticket, callDuration, seizureTime, releaseTime, cause); err != nil { - log.Error("Failed to process callback result", err) - services.ResponseInternalServerError500ProcessError(w, err) - return - } - - log.Info(fmt.Sprintf("Successfully processed callback for ticket %d", ticket.TicketId)) } services.ResponseStatusOK204NoContent(w) } + +// MF网元类型特殊处理, 未接电话的回拨工单流转处理 +func handleMFMissedCallCDR(url string, cdrEvent CDREvent) error { + // 发送HTTP请求获取座席列表 + resp, err := http.Get(url) + if err != nil { + return fmt.Errorf("failed to get MF agents: %w", err) + } + defer resp.Body.Close() + + // 解析座席列表响应 + var agentResp struct { + Code int `json:"code"` + Data []ueCallBackTicket.AgentInfo `json:"data"` + Msg string `json:"msg"` + } + + if err := json.NewDecoder(resp.Body).Decode(&agentResp); err != nil { + return fmt.Errorf("failed to decode MF agent response: %w", err) + } + + // 调用服务获取最新一个被分配工单的座席和下一个要分配的座席 + mfService := ueCallBackTicket.NewCallbackTicketService() + lastAgentName, err := mfService.GetLastAssignedAgent() + if err != nil { + return fmt.Errorf("failed to get last assigned agent: %w", err) + } + + // 选择下一个要分配的座席 + selectedAgent := mfService.SelectNextAgent(agentResp.Data, lastAgentName) + + if selectedAgent != nil { + // 创建回调工单 + var updatedAt *int64 = nil + ticket := ueCallBackTicket.CallbackTicket{ + CallerNumber: cdrEvent.CDR["callerParty"].(string), + CalleeNumber: cdrEvent.CDR["calledParty"].(string), + Status: ueCallBackTicket.TicketStatusNew.Enum(), + AgentName: selectedAgent.Name, + AgentEmail: selectedAgent.Email, + AgentMobile: selectedAgent.Mobile, + Comment: "", + RmUid: cdrEvent.RmUID, + CreatedAt: time.Now().UnixMicro(), + UpdatedAt: updatedAt, + } + if err := mfService.InsertCallbackTicket(&ticket); err != nil { + return fmt.Errorf("failed to insert MF callback ticket: %w", err) + } + // 新工单分配后发送邮件通知 + if selectedAgent.Email != "" { + // 发送邮件通知 + emailConfig := config.GetSMTPConfig() + if emailConfig != nil && emailConfig.Enabled { + // 创建配置副本,避免修改全局配置 + emailCopy := *emailConfig // 浅拷贝结构体 + + // 合并配置中的To地址和当前工单的座席邮箱 + var recipients []string + + // 添加配置中的原始收件人(如管理员、监控人员等) + if len(emailConfig.To) > 0 { + recipients = append(recipients, strings.Split(emailConfig.To, ",")...) + } + + // 添加当前工单的座席邮箱 + recipients = append(recipients, ticket.AgentEmail) + + // 去重处理(避免重复邮箱) + emailCopy.To = strings.Join(email.RemoveDuplicateEmails(recipients), ",") + + // 设置邮件主题和内容 + emailCopy.Subject = "新工单分配通知" + emailCopy.Body = fmt.Sprintf("您被分配了一个新的回拨工单(编号:%d, 主叫号码:%s), 请及时处理.", + ticket.TicketId, ticket.CallerNumber) + go email.SendEmailWithGomail(emailCopy) // 异步发送 + } + + } + } + log.Warn("No available agents found for callback ticket") + return nil +} + +func handleMFCDRCallBack(cdrEvent CDREvent) error { + // 获取座席号码(主叫)和被叫号码 + agentNumber, ok1 := cdrEvent.CDR["callerParty"].(string) + callerNumber, ok2 := cdrEvent.CDR["calledParty"].(string) + + if !ok1 || !ok2 { + return fmt.Errorf("invalid CDR format: missing callerParty or calledParty") + } + + // 获取通话时长 + callDuration, ok := cdrEvent.CDR["callDuration"].(float64) + if !ok { + // 尝试其他可能的类型 + if durationInt, ok := cdrEvent.CDR["callDuration"].(int); ok { + callDuration = float64(durationInt) + } else { + return fmt.Errorf("invalid CDR format: callDuration is not a number") + } + } + + // 通过座席号码和主叫号码查找符合条件的工单 + mfService := ueCallBackTicket.NewCallbackTicketService() + ticket, err := mfService.FindCallbackTicketByAgentAndCaller(agentNumber, callerNumber) + if err != nil { + return fmt.Errorf("failed to find callback ticket: %w", err) + } + + if ticket == nil { + // 没有找到对应的工单,可能是手动呼叫,不处理 + return fmt.Errorf("no callback ticket found for agent %s and caller %s", agentNumber, callerNumber) + } + + // 获取通话信息 + seizureTime, _ := cdrEvent.CDR["seizureTime"].(string) + releaseTime, _ := cdrEvent.CDR["releaseTime"].(string) + cause, _ := cdrEvent.CDR["cause"].(string) + + // 处理回拨结果并更新工单 + if err := mfService.ProcessCallbackResult(ticket, callDuration, seizureTime, releaseTime, cause); err != nil { + return fmt.Errorf("failed to process callback result: %w", err) + } + + log.Info(fmt.Sprintf("Successfully processed callback for ticket %d", ticket.TicketId)) + return nil +}