feat: support mf ticket management

This commit is contained in:
zhangsz
2025-07-01 16:48:51 +08:00
parent 4c2200b39b
commit fe7e4d9c88
5 changed files with 258 additions and 108 deletions

View File

@@ -78,11 +78,11 @@ func PostCDREventFrom(w http.ResponseWriter, r *http.Request) {
}
// MF网元类型特殊处理, 未接电话的回拨工单流转处理
if neTypeLower == "mf" && cdrEvent.CDR["agentName"] == "" {
if neTypeLower == "mf" && cdrEvent.CDR["recordType"] == "MOC" && cdrEvent.CDR["agentName"] == "" {
// 发送到MF网元
// 构造网元MF的API地址
url := fmt.Sprintf("http://%s:%d/ne/config/data?neType=%s&neId=%s&paramName=agents",
neInfo.IP, neInfo.Port, neInfo.NeType, neInfo.NeId)
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 {
@@ -107,31 +107,25 @@ func PostCDREventFrom(w http.ResponseWriter, r *http.Request) {
// 调用服务获取最新一个被分配工单的座席和下一个要分配的座席
mfService := ueCallBackTicket.NewCallbackTicketService()
lastAgent, err := mfService.GetLastAssignedAgent()
lastAgentName, err := mfService.GetLastAssignedAgent()
if err != nil {
log.Error("Failed to get last assigned agent", err)
// 可以继续执行,不返回错误
}
// 选择下一个要分配的座席
selectedAgent := mfService.SelectNextAgent(agentResp.Data, lastAgent)
// 获取分配座席的邮箱
agentEmail := ""
for _, agent := range agentResp.Data {
if agent.Name == selectedAgent {
agentEmail = agent.Email
break
}
}
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,
AgentName: selectedAgent.Name,
AgentEmail: selectedAgent.Email,
AgentMobile: selectedAgent.Mobile,
Comment: "",
MsdData: cdrEvent.CDR["msdData"].(string),
RmUid: cdrEvent.RmUID,
@@ -144,12 +138,14 @@ func PostCDREventFrom(w http.ResponseWriter, r *http.Request) {
// return
}
// 新工单分配后发送邮件通知
if agentEmail != "" {
if selectedAgent.Email != "" {
subject := "新工单分配通知"
body := fmt.Sprintf("您被分配了一个新的回拨工单,主叫号码:%s", ticket.CallerNumber)
go email.SendEmail(agentEmail, subject, body) // 异步发送
go email.SendEmail(selectedAgent.Email, subject, body) // 异步发送
}
}
log.Warn("No available agents found for callback ticket")
}
// MF网元类型特殊处理, 处理座席回拨的工单流转
if neTypeLower == "mf" && cdrEvent.CDR["recordType"] == "MTC" {
@@ -193,12 +189,12 @@ func PostCDREventFrom(w http.ResponseWriter, r *http.Request) {
}
// 获取通话信息
answerTime, _ := cdrEvent.CDR["answerTime"].(string)
seizureTime, _ := cdrEvent.CDR["seizureTime"].(string)
releaseTime, _ := cdrEvent.CDR["releaseTime"].(string)
cause, _ := cdrEvent.CDR["cause"].(string)
// 处理回拨结果并更新工单
if err := mfService.ProcessCallbackResult(ticket, callDuration, answerTime, releaseTime, cause); err != nil {
if err := mfService.ProcessCallbackResult(ticket, callDuration, seizureTime, releaseTime, cause); err != nil {
log.Error("Failed to process callback result", err)
services.ResponseInternalServerError500ProcessError(w, err)
return

View File

@@ -59,7 +59,7 @@ func ParseCallTag(s string) TicketStatus {
return TicketStatus(i)
}
// 如果转换失败,则按名称匹配(忽略大小写)
switch strings.ToLower(s) {
switch strings.ToUpper(s) {
case "NULL":
return TicketStatusNull
case "NEW":
@@ -102,7 +102,9 @@ type CallbackTicket struct {
CallerNumber string `json:"callerNumber" gorm:"column:caller_number"` // 主叫号码
CalleeNumber string `json:"calleeNumber" gorm:"column:callee_number"` // 被叫号码
Status string `json:"status" gorm:"column:status"` // 工单状态
AgentName string `json:"agentName" gorm:"column:agent_name"` // 席名称
AgentName string `json:"agentName" gorm:"column:agent_name"` // 席名称
AgentEmail string `json:"agentEmail" gorm:"column:agent_email"` // 座席邮箱
AgentMobile string `json:"agentMobile" gorm:"column:agent_mobile"` // 座席手机号码
Comment string `json:"comment" gorm:"column:comment"` // 工单备注
MsdData string `json:"msdData" gorm:"column:msd_data"` // MSD数据
RmUid string `json:"rmUid" gorm:"column:rm_uid"` // RM用户ID

View File

@@ -13,10 +13,20 @@ type CallbackTicketService struct {
db *gorm.DB
}
// 构造函数示例
// 构造函数改为私有初始化方法
func NewCallbackTicketService() *CallbackTicketService {
db := dborm.DefaultDB()
return &CallbackTicketService{db: db}
return &CallbackTicketService{db: nil} // 先不初始化数据库连接
}
// 获取数据库连接的私有方法
func (s *CallbackTicketService) getDB() *gorm.DB {
if s.db == nil {
s.db = dborm.DefaultDB()
if s.db == nil {
panic("Cannot establish database connection")
}
}
return s.db
}
// SelectCallbackTicket 根据条件分页查询回调工单
@@ -24,7 +34,7 @@ func (s *CallbackTicketService) SelectCallbackTicket(query CallbackTicketQuery)
var tickets []CallbackTicket
var total int64
db := s.db.Table("mf_callback_ticket")
db := s.getDB().Table("mf_callback_ticket")
if query.CallerNumber != "" {
db = db.Where("caller_number = ?", query.CallerNumber)
@@ -71,13 +81,13 @@ func (s *CallbackTicketService) SelectCallbackTicketByPage(pageNum int, pageSize
var total int64
// 统计总数
if err := s.db.Table("mf_callback_ticket").Count(&total).Error; err != nil {
if err := s.getDB().Table("mf_callback_ticket").Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("failed to count callback tickets: %w", err)
}
// 分页查询
offset := (pageNum - 1) * pageSize
if err := s.db.Table("mf_callback_ticket").
if err := s.getDB().Table("mf_callback_ticket").
Limit(pageSize).
Offset(offset).
Find(&tickets).Error; err != nil {
@@ -90,7 +100,7 @@ func (s *CallbackTicketService) SelectCallbackTicketByPage(pageNum int, pageSize
func (s *CallbackTicketService) InsertCallbackTicket(ticket CallbackTicket) error {
// 判断主叫号码是否已存在未处理完的工单
var existingCount int64
if err := s.db.Table("mf_callback_ticket").
if err := s.getDB().Table("mf_callback_ticket").
Where("caller_number = ? AND status IN ('NEW', 'PENDING', 'IN_PROGRESS')", ticket.CallerNumber).
Count(&existingCount).Error; err != nil {
return fmt.Errorf("failed to check existing tickets: %w", err)
@@ -100,7 +110,7 @@ func (s *CallbackTicketService) InsertCallbackTicket(ticket CallbackTicket) erro
return fmt.Errorf("caller %s already has a pending ticket", ticket.CallerNumber)
}
// 这里可以使用ORM或其他方式将ticket插入到数据库中
if err := s.db.Table("mf_callback_ticket").Create(&ticket).Error; err != nil {
if err := s.getDB().Table("mf_callback_ticket").Create(&ticket).Error; err != nil {
return fmt.Errorf("failed to insert callback ticket: %w", err)
}
return nil
@@ -115,7 +125,7 @@ func (s *CallbackTicketService) InsertCallbackTicket(ticket CallbackTicket) erro
// mfCallbackTicketService.SelectCallbackTicketById(12345)
func (s *CallbackTicketService) SelectCallbackTicketById(ticketId int64) (*CallbackTicket, error) {
var ticket CallbackTicket
if err := s.db.Table("mf_callback_ticket").
if err := s.getDB().Table("mf_callback_ticket").
Where("ticket_id = ?", ticketId).
First(&ticket).Error; err != nil {
if err == gorm.ErrRecordNotFound {
@@ -133,7 +143,7 @@ func (s *CallbackTicketService) SelectCallbackTicketById(ticketId int64) (*Callb
// @example
// mfCallbackTicketService.UpdateCallbackTicket(ticket)
func (s *CallbackTicketService) UpdateCallbackTicket(ticket CallbackTicket) error {
if err := s.db.Table("mf_callback_ticket").
if err := s.getDB().Table("mf_callback_ticket").
Where("ticket_id = ?", ticket.TicketId).
Updates(ticket).Error; err != nil {
return fmt.Errorf("failed to update callback ticket: %w", err)
@@ -148,7 +158,7 @@ func (s *CallbackTicketService) UpdateCallbackTicket(ticket CallbackTicket) erro
// @example
// mfCallbackTicketService.DeleteCallbackTicket(12345)
func (s *CallbackTicketService) DeleteCallbackTicket(ticketId int64) error {
if err := s.db.Table("mf_callback_ticket").
if err := s.getDB().Table("mf_callback_ticket").
Where("ticket_id = ?", ticketId).
Delete(&CallbackTicket{}).Error; err != nil {
return fmt.Errorf("failed to delete callback ticket: %w", err)
@@ -162,7 +172,7 @@ func (s *CallbackTicketService) DeleteCallbackTicket(ticketId int64) error {
// @return error 错误信息
func (s *CallbackTicketService) GetLastAssignedAgent() (string, error) {
var lastTicket CallbackTicket
if err := s.db.Table("mf_callback_ticket").
if err := s.getDB().Table("mf_callback_ticket").
Where("agent_name <> ''").
Order("created_at DESC").
First(&lastTicket).Error; err != nil {
@@ -181,17 +191,15 @@ func (s *CallbackTicketService) GetLastAssignedAgent() (string, error) {
// @param agents 座席列表
// @param lastAgentName 上一个座席名称
// @return string 下一个座席名称
func (s *CallbackTicketService) SelectNextAgent(agents []AgentInfo, lastAgentName string) string {
func (s *CallbackTicketService) SelectNextAgent(agents []AgentInfo, lastAgentName string) *AgentInfo {
if len(agents) == 0 {
return ""
return nil
}
// 默认选第一个座席
selectedAgent := agents[0].Name
var firstAgent *AgentInfo = &agents[0]
// 如果没有上一个座席,直接返回第一个
if lastAgentName == "" {
return selectedAgent
return firstAgent
}
// 找到上一个座席的下一个
@@ -199,19 +207,19 @@ func (s *CallbackTicketService) SelectNextAgent(agents []AgentInfo, lastAgentNam
for i, agent := range agents {
if foundLastAgent {
// 找到上一个座席的下一个
return agent.Name
return &agent
}
if agent.Name == lastAgentName {
foundLastAgent = true
// 如果是最后一个座席,则循环回第一个
if i == len(agents)-1 {
return agents[0].Name
return firstAgent
}
}
}
// 如果没找到上一个座席(可能被删除了),使用第一个座席
return selectedAgent
return firstAgent
}
// FindCallbackTicketByAgentAndCaller 通过座席号码和主叫号码查找符合条件的工单
@@ -223,7 +231,7 @@ func (s *CallbackTicketService) SelectNextAgent(agents []AgentInfo, lastAgentNam
func (s *CallbackTicketService) FindCallbackTicketByAgentAndCaller(agentName string, callerNumber string) (*CallbackTicket, error) {
var ticket CallbackTicket
if err := s.db.Table("mf_callback_ticket").
if err := s.getDB().Table("mf_callback_ticket").
Where("agent_name = ? AND caller_number = ? AND status IN ('NEW', 'PENDING', 'IN_PROGRESS', 'NO_ANSWER_1', 'NO_ANSWER_2')",
agentName, callerNumber).
Order("created_at DESC").
@@ -245,7 +253,7 @@ func (s *CallbackTicketService) FindCallbackTicketByAgentAndCaller(agentName str
// @param releaseTime 释放时间
// @param cause 释放原因
// @return error 错误信息
func (s *CallbackTicketService) ProcessCallbackResult(ticket *CallbackTicket, callDuration float64, answerTime string, releaseTime string, cause string) error {
func (s *CallbackTicketService) ProcessCallbackResult(ticket *CallbackTicket, callDuration float64, seizureTime string, releaseTime string, cause string) error {
if ticket == nil {
return fmt.Errorf("ticket cannot be nil")
}
@@ -258,7 +266,7 @@ func (s *CallbackTicketService) ProcessCallbackResult(ticket *CallbackTicket, ca
}
// 构建评论内容
comment := fmt.Sprintf("回拨时间: %s, 释放时间: %s, 原因: %s", answerTime, releaseTime, cause)
comment := fmt.Sprintf("回拨时间: %s, 释放时间: %s, 原因: %s", seizureTime, releaseTime, cause)
// 根据通话时长判断处理结果
if callDuration > 0 {
@@ -288,7 +296,7 @@ func (s *CallbackTicketService) ProcessCallbackResult(ticket *CallbackTicket, ca
}
// 更新工单
if err := s.db.Table("mf_callback_ticket").
if err := s.getDB().Table("mf_callback_ticket").
Where("ticket_id = ?", ticket.TicketId).
Updates(updatedTicket).Error; err != nil {
return fmt.Errorf("failed to update callback ticket: %w", err)
@@ -310,7 +318,7 @@ func (s *CallbackTicketService) FindTimeoutTickets(status string, timeoutMicros
// 查询超时的工单
var tickets []CallbackTicket
if err := s.db.Table("mf_callback_ticket").
if err := s.getDB().Table("mf_callback_ticket").
Where("status = ? AND created_at < ? AND (updated_at IS NULL OR updated_at < ?)",
status, timeoutBeforeMicros, timeoutBeforeMicros).
Find(&tickets).Error; err != nil {
@@ -338,7 +346,7 @@ func (s *CallbackTicketService) UpdateTicketToTimeout(ticket *CallbackTicket, or
Comment: fmt.Sprintf("%s - 工单状态为 %s 处理超时,系统自动更新为超时状态", ticket.Comment, originalStatus),
UpdatedAt: &now,
}
if err := s.db.Table("mf_callback_ticket").
if err := s.getDB().Table("mf_callback_ticket").
Where("ticket_id = ?", ticket.TicketId).
Updates(updatedTicket).Error; err != nil {
return fmt.Errorf("更新工单 %d 状态失败: %w", ticket.TicketId, err)
@@ -348,37 +356,62 @@ func (s *CallbackTicketService) UpdateTicketToTimeout(ticket *CallbackTicket, or
lastAgent := ticket.AgentName
newAgent := s.SelectNextAgent(agents, lastAgent)
// 查找新座席邮箱
newAgentEmail := ""
for _, agent := range agents {
if agent.Name == newAgent {
newAgentEmail = agent.Email
break
}
}
// 3. 创建新工单
if newAgent != nil {
newTicket := CallbackTicket{
CallerNumber: ticket.CallerNumber,
CalleeNumber: ticket.CalleeNumber,
Status: TicketStatusNew.Enum(),
AgentName: newAgent,
AgentName: newAgent.Name,
AgentEmail: newAgent.Email,
AgentMobile: newAgent.Mobile,
Comment: fmt.Sprintf("由超时工单 %d 自动重建", ticket.TicketId),
MsdData: ticket.MsdData,
RmUid: ticket.RmUid,
CreatedAt: now,
UpdatedAt: nil,
}
if err := s.db.Table("mf_callback_ticket").Create(&newTicket).Error; err != nil {
if err := s.getDB().Table("mf_callback_ticket").Create(&newTicket).Error; err != nil {
return fmt.Errorf("创建新工单失败: %w", err)
}
// 新工单分配后发送邮件通知
if newAgentEmail != "" {
if newAgent.Email != "" {
subject := "新工单自动重建通知"
body := fmt.Sprintf("您被分配了一个自动重建的回拨工单,主叫号码:%s", newTicket.CallerNumber)
go email.SendEmail(newAgentEmail, subject, body)
go email.SendEmail(newAgent.Email, subject, body)
}
}
return nil
}
// FindNearlyTimeoutTickets 查询即将超时的工单
func (s *CallbackTicketService) FindNearlyTimeoutTickets(status string, timeoutMicros int64, aheadMicros int64) ([]CallbackTicket, error) {
nowMicros := time.Now().UnixMicro()
timeoutBeforeMicros := nowMicros - timeoutMicros
nearlyTimeoutBeforeMicros := nowMicros - (timeoutMicros - aheadMicros)
var tickets []CallbackTicket
if err := s.getDB().Table("mf_callback_ticket").
Where("status = ? AND created_at >= ? AND created_at < ? AND (updated_at IS NULL OR updated_at < ?)",
status, timeoutBeforeMicros, nearlyTimeoutBeforeMicros, nearlyTimeoutBeforeMicros).
Find(&tickets).Error; err != nil {
return nil, fmt.Errorf("查询即将超时工单失败: %w", err)
}
return tickets, nil
}
// 新增方法:坐席开始处理工单
func (s *CallbackTicketService) StartProcessingTicket(ticketId int64) error {
now := time.Now().UnixMicro()
if err := s.getDB().Table("mf_callback_ticket").
Where("ticket_id = ? AND status = ?", ticketId, TicketStatusNew.Enum()).
Updates(map[string]interface{}{
"status": TicketStatusInProgress.Enum(),
"updated_at": now,
"comment": "坐席开始处理工单",
}).Error; err != nil {
return fmt.Errorf("failed to update ticket to IN_PROGRESS: %w", err)
}
return nil
}

View File

@@ -11,6 +11,7 @@ import (
processorMonitorSysResource "be.ems/src/modules/crontask/processor/monitor_sys_resource"
processorNeConfigBackup "be.ems/src/modules/crontask/processor/ne_config_backup"
processorNeDataUDM "be.ems/src/modules/crontask/processor/ne_data_udm"
"be.ems/src/modules/crontask/processor/psap_ticket_monitor"
"be.ems/src/modules/crontask/processor/removeFile"
)
@@ -30,4 +31,5 @@ func InitCronQueue() {
cron.CreateQueue("genNeStateAlarm", genNeStateAlarm.NewProcessor)
cron.CreateQueue("exportTable", exportTable.NewProcessor)
cron.CreateQueue("removeFile", removeFile.NewProcessor)
cron.CreateQueue("psap_ticket_monitor", psap_ticket_monitor.NewProcessor)
}

View File

@@ -4,11 +4,12 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"
ueCallBackTicket "be.ems/features/ue/mf_callback_ticket"
"be.ems/lib/email"
"be.ems/lib/log"
"be.ems/src/framework/cron"
"be.ems/src/framework/logger"
neService "be.ems/src/modules/network_element/service"
)
@@ -27,7 +28,7 @@ func (s *PsapTicketMonitor) Execute(data any) (any, error) {
s.count++ // 执行次数加一
options := data.(cron.JobData)
sysJob := options.SysJob
logger.Infof("执行工单监控任务 %v 任务ID %s", options.Repeat, sysJob.JobID)
log.Infof("执行工单监控任务 %v 任务ID %s", options.Repeat, sysJob.JobID)
// 返回结果,用于记录执行结果
result := map[string]any{
@@ -37,10 +38,10 @@ func (s *PsapTicketMonitor) Execute(data any) (any, error) {
// 处理超时的NEW状态工单 (60分钟)
newTicketsUpdated, err := s.handleTimeoutTickets(
ueCallBackTicket.TicketStatusNew.Enum(),
60*1000000, // 60分钟(微秒)
1*60*1000000, // 1分钟(微秒)
)
if err != nil {
logger.Errorf("处理NEW状态超时工单失败: %v", err)
log.Errorf("处理NEW状态超时工单失败: %v", err)
}
result["newTicketsUpdated"] = newTicketsUpdated
@@ -50,7 +51,7 @@ func (s *PsapTicketMonitor) Execute(data any) (any, error) {
60*60*1000000, // 60分钟(微秒)
)
if err != nil {
logger.Errorf("处理IN_PROGRESS状态超时工单失败: %v", err)
log.Errorf("处理IN_PROGRESS状态超时工单失败: %v", err)
}
result["inProgressTicketsUpdated"] = inProgressTicketsUpdated
@@ -60,7 +61,7 @@ func (s *PsapTicketMonitor) Execute(data any) (any, error) {
4*60*60*1000000, // 4小时(微秒)
)
if err != nil {
logger.Errorf("处理NO_ANSWER_1状态超时工单失败: %v", err)
log.Errorf("处理NO_ANSWER_1状态超时工单失败: %v", err)
}
result["noAnswer1TicketsUpdated"] = noAnswer1TicketsUpdated
@@ -70,15 +71,69 @@ func (s *PsapTicketMonitor) Execute(data any) (any, error) {
8*60*60*1000000, // 8小时(微秒)
)
if err != nil {
logger.Errorf("处理NO_ANSWER_2状态超时工单失败: %v", err)
log.Errorf("处理NO_ANSWER_2状态超时工单失败: %v", err)
}
result["noAnswer2TicketsUpdated"] = noAnswer2TicketsUpdated
// 汇总结果
totalUpdated := newTicketsUpdated + inProgressTicketsUpdated + noAnswer1TicketsUpdated + noAnswer2TicketsUpdated
totalUpdated := newTicketsUpdated + inProgressTicketsUpdated +
noAnswer1TicketsUpdated + noAnswer2TicketsUpdated
result["totalUpdated"] = totalUpdated
logger.Infof("工单监控任务完成,共处理 %d 个超时工单", totalUpdated)
log.Infof("工单监控任务完成,共处理 %d 个超时工单", totalUpdated)
// 处理超时的NEW状态工单 (60分钟)
newTicketsNearlyTimeout, err := s.handleNearlyTimeoutTickets(
ueCallBackTicket.TicketStatusNew.Enum(),
60*60*1000000, // 60分钟(微秒)
10*60*1000000, // 提前10分钟提醒(微秒)
)
if err != nil {
log.Errorf("处理NEW状态超时工单失败: %v", err)
}
result["newTicketsNearlyTimeout"] = newTicketsNearlyTimeout
// 处理超时的IN_PROGRESS状态工单 (60分钟)
inProgressTicketsNearlyTimeout, err := s.handleNearlyTimeoutTickets(
ueCallBackTicket.TicketStatusInProgress.Enum(),
60*60*1000000, // 60分钟(微秒)
10*60*1000000, // 提前10分钟提醒(微秒)
)
if err != nil {
log.Errorf("处理IN_PROGRESS状态超时工单失败: %v", err)
}
result["inProgressTicketsNearlyTimeout"] = inProgressTicketsNearlyTimeout
// 处理超时的NO_ANSWER_1状态工单 (4小时)
noAnswer1TicketsNearlyTimeout, err := s.handleNearlyTimeoutTickets(
ueCallBackTicket.TicketStatusNoAnswer1.Enum(),
4*60*60*1000000, // 4小时(微秒)
10*60*1000000, // 提前10分钟提醒(微秒)
)
if err != nil {
log.Errorf("处理NO_ANSWER_1状态超时工单失败: %v", err)
}
result["noAnswer1TicketsNearlyTimeout"] = noAnswer1TicketsNearlyTimeout
// 处理超时的NO_ANSWER_2状态工单 (8小时)
noAnswer2TicketsNearlyTimeout, err := s.handleNearlyTimeoutTickets(
ueCallBackTicket.TicketStatusNoAnswer2.Enum(),
8*60*60*1000000, // 8小时(微秒)
10*60*1000000, // 提前10分钟提醒(微秒)
)
if err != nil {
log.Errorf("处理NO_ANSWER_2状态即将超时工单失败: %v", err)
}
result["noAnswer2TicketsNearlyTimeout"] = noAnswer2TicketsNearlyTimeout
// 汇总结果
totalTicketsNearlyTimeout := newTicketsNearlyTimeout + inProgressTicketsNearlyTimeout +
noAnswer1TicketsNearlyTimeout + noAnswer2TicketsNearlyTimeout
result["totalTicketsNearlyTimeout"] = totalTicketsNearlyTimeout
log.Infof("工单监控任务完成,共处理 %d 个即将超时工单", totalUpdated)
return result, nil
}
@@ -100,8 +155,8 @@ func (s *PsapTicketMonitor) handleTimeoutTickets(status string, timeoutMicros in
// 获取网元信息
neInfo := neService.NewNeInfo.SelectNeInfoByRmuid(ticket.RmUid)
// 构造网元MF的API地址
url := fmt.Sprintf("http://%s:%d/ne/config/data?neType=%s&neId=%s&paramName=agents",
neInfo.IP, neInfo.Port, neInfo.NeType, neInfo.NeId)
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 {
@@ -132,3 +187,65 @@ func (s *PsapTicketMonitor) handleTimeoutTickets(status string, timeoutMicros in
return updatedCount, nil
}
// handleTimeoutTickets 处理指定状态的超时工单
func (s *PsapTicketMonitor) handleNearlyTimeoutTickets(status string, timeoutMicros int64, aheadMicros int64) (int, error) {
// 查询超时的工单
tickets, err := s.callbackTicketService.FindNearlyTimeoutTickets(status, timeoutMicros, aheadMicros)
if err != nil {
return 0, err
}
if len(tickets) == 0 {
return 0, nil // 没有即将超时工单
}
// 更新超时工单状态
var updatedCount int
for _, ticket := range tickets {
if ticket.AgentEmail != "" {
subject := "工单即将超时提醒"
body := fmt.Sprintf("您负责的回拨工单(主叫号码:%s即将超时请及时处理。", ticket.CallerNumber)
go email.SendEmail(ticket.AgentEmail, subject, body)
}
}
return updatedCount, nil
}
func (s *PsapTicketMonitor) GetAgentEmail(ticket ueCallBackTicket.CallbackTicket) string {
// 获取网元信息
neInfo := neService.NewNeInfo.SelectNeInfoByRmuid(ticket.RmUid)
// 构造网元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)
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)
return ""
}
// 查找当前工单分配的座席邮箱
agentEmail := ""
for _, agent := range agentResp.Data {
if agent.Name == ticket.AgentName {
agentEmail = agent.Email
break
}
}
return agentEmail
}