diff --git a/database/install/mf_callback_ticket.sql b/database/install/mf_callback_ticket.sql index 956ccb6f..e1437070 100755 --- a/database/install/mf_callback_ticket.sql +++ b/database/install/mf_callback_ticket.sql @@ -11,7 +11,7 @@ Target Server Version : 100622 (10.6.22-MariaDB-0ubuntu0.22.04.1) File Encoding : 65001 - Date: 02/07/2025 17:49:45 + Date: 11/07/2025 17:20:50 */ SET NAMES utf8mb4; @@ -30,13 +30,14 @@ CREATE TABLE `mf_callback_ticket` ( `agent_email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `agent_mobile` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `comment` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT 'comment for callback', - `msd_data` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT 'MSD data', `rm_uid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'MF Resource UID', `created_at` bigint(20) NULL DEFAULT NULL COMMENT 'created at time', + `created_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `updated_at` bigint(20) NULL DEFAULT NULL COMMENT 'updated at time', + `updated_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, PRIMARY KEY (`ticket_id`) USING BTREE, INDEX `idx_caller_agent`(`caller_number`, `agent_name`) USING BTREE, INDEX `idx_created`(`created_at`, `ticket_id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 3440 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户信息表' ROW_FORMAT = Dynamic; +) ENGINE = InnoDB AUTO_INCREMENT = 94 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户信息表' ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1; diff --git a/database/upgrade/upg_mf_callback_ticket.sql b/database/upgrade/upg_mf_callback_ticket.sql index f6df10f2..053994cd 100755 --- a/database/upgrade/upg_mf_callback_ticket.sql +++ b/database/upgrade/upg_mf_callback_ticket.sql @@ -29,7 +29,6 @@ CREATE TABLE IF NOT EXISTS `mf_callback_ticket` ( `agent_email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `agent_mobile` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `comment` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT 'comment for callback', - `msd_data` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT 'MSD data', `rm_uid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'MF Resource UID', `created_at` bigint(20) NULL DEFAULT NULL COMMENT 'created at time', `updated_at` bigint(20) NULL DEFAULT NULL COMMENT 'updated at time', @@ -46,4 +45,10 @@ ALTER TABLE `mf_callback_ticket` ADD COLUMN IF NOT EXISTS `agent_mobile` varchar ALTER TABLE `mf_callback_ticket` ADD INDEX IF NOT EXISTS `idx_created`(`created_at`, `ticket_id`) USING BTREE; +ALTER TABLE `mf_callback_ticket` DROP COLUMN IF EXISTS `msd_data`; + +ALTER TABLE `mf_callback_ticket` +ADD COLUMN IF NOT EXISTS `created_by` varchar(32) NULL AFTER `created_at`, +ADD COLUMN IF NOT EXISTS `updated_by` varchar(32) NULL AFTER `updated_at`; + SET FOREIGN_KEY_CHECKS = 1; diff --git a/features/cdr/cdrevent.go b/features/cdr/cdrevent.go index f35c02a1..6b9a5f9c 100644 --- a/features/cdr/cdrevent.go +++ b/features/cdr/cdrevent.go @@ -127,7 +127,6 @@ func PostCDREventFrom(w http.ResponseWriter, r *http.Request) { AgentEmail: selectedAgent.Email, AgentMobile: selectedAgent.Mobile, Comment: "", - MsdData: cdrEvent.CDR["msdData"].(string), RmUid: cdrEvent.RmUID, CreatedAt: time.Now().UnixMicro(), UpdatedAt: updatedAt, diff --git a/features/ue/mf_callback_ticket/controller.go b/features/ue/mf_callback_ticket/controller.go index 3a9b913a..401b5578 100644 --- a/features/ue/mf_callback_ticket/controller.go +++ b/features/ue/mf_callback_ticket/controller.go @@ -24,6 +24,10 @@ func Register(r *gin.RouterGroup) { middleware.PreAuthorize(nil), m.Update, ) + mfCallingGroup.PATCH("/:ticketId/status", + middleware.PreAuthorize(nil), + m.StartProcessingTicket, + ) mfCallingGroup.DELETE("/:ticketId", middleware.PreAuthorize(nil), m.Delete, @@ -94,6 +98,69 @@ func (m *CallbackTicket) Update(c *gin.Context) { c.JSON(200, result.Ok(nil)) } +func (m *CallbackTicket) UpdateTicketStatus(c *gin.Context) { + language := ctx.AcceptLanguage(c) + username := ctx.LoginUserToUserName(c) + + // 获取路径参数 + ticketId := c.Param("ticketId") + if ticketId == "" { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + id, err := strconv.ParseInt(ticketId, 10, 64) + if err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 绑定请求体 + var statusUpdate struct { + Status string `json:"status" binding:"required"` + UpdatedBy string `json:"updatedBy"` + } + if err := c.ShouldBindJSON(&statusUpdate); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + statusUpdate.UpdatedBy = username + + service := NewCallbackTicketService() + if err := service.UpdateCallbackTicketStatus(id, statusUpdate.Status, statusUpdate.UpdatedBy); err != nil { + c.JSON(500, result.ErrMsg(err.Error())) + return + } + + c.JSON(200, result.Ok(nil)) +} + +func (m *CallbackTicket) StartProcessingTicket(c *gin.Context) { + language := ctx.AcceptLanguage(c) + username := ctx.LoginUserToUserName(c) + + // 获取路径参数 + ticketId := c.Param("ticketId") + if ticketId == "" { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + id, err := strconv.ParseInt(ticketId, 10, 64) + if err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + service := NewCallbackTicketService() + if err := service.StartProcessingTicket(id, username); err != nil { + c.JSON(500, result.ErrMsg(err.Error())) + return + } + + c.JSON(200, result.Ok(nil)) +} + // Delete 删除回调工单 func (m *CallbackTicket) Delete(c *gin.Context) { language := ctx.AcceptLanguage(c) diff --git a/features/ue/mf_callback_ticket/model.go b/features/ue/mf_callback_ticket/model.go index 3c2e3778..8d6a2632 100644 --- a/features/ue/mf_callback_ticket/model.go +++ b/features/ue/mf_callback_ticket/model.go @@ -106,7 +106,6 @@ type CallbackTicket struct { 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 CreatedAt int64 `json:"createdAt" gorm:"column:created_at"` // 创建时间 UpdatedAt *int64 `json:"updatedAt" gorm:"column:updated_at;autoUpdateTime:false"` // 更新时间 diff --git a/features/ue/mf_callback_ticket/service.go b/features/ue/mf_callback_ticket/service.go index 0a21845d..af002fde 100644 --- a/features/ue/mf_callback_ticket/service.go +++ b/features/ue/mf_callback_ticket/service.go @@ -16,6 +16,15 @@ type CallbackTicketService struct { db *gorm.DB } +func contains(slice []string, item string) bool { + for _, s := range slice { + if s == item { + return true + } + } + return false +} + // 构造函数改为私有初始化方法 func NewCallbackTicketService() *CallbackTicketService { return &CallbackTicketService{db: nil} // 先不初始化数据库连接 @@ -154,6 +163,48 @@ func (s *CallbackTicketService) UpdateCallbackTicket(ticket CallbackTicket) erro return nil } +func (s *CallbackTicketService) UpdateCallbackTicketStatus(ticketId int64, status string, updatedBy string) error { + if status == "" { + return fmt.Errorf("status cannot be empty") + } + + // 检查状态是否合法 + validStatuses := []string{"IN_PROGRESS", "PENDING", "CLOSED"} + if !contains(validStatuses, status) { + return fmt.Errorf("invalid status: %s", status) + } + + // 获取工单当前状态 + var currentTicket CallbackTicket + if err := s.getDB().Table("mf_callback_ticket"). + Where("ticket_id = ?", ticketId). + First(¤tTicket).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return fmt.Errorf("callback ticket with ID %d not found", ticketId) + } + return fmt.Errorf("failed to find callback ticket: %w", err) + } + + // 如果状态没有变化,则不更新 + if currentTicket.Status == status || currentTicket.Status == "CLOSED" { + return nil + } + + // 更新工单状态 + now := time.Now().UnixMicro() + if err := s.getDB().Table("mf_callback_ticket"). + Where("ticket_id = ?", ticketId). + Updates(map[string]interface{}{ + "status": status, + "comment": fmt.Sprintf("工单状态从 %s 更新为 %s", currentTicket.Status, status), + "updated_at": &now, + "updated_by": updatedBy, + }).Error; err != nil { + return fmt.Errorf("failed to update callback ticket status: %w", err) + } + return nil +} + // DeleteCallbackTicket 删除回调工单 // @Description 删除回调工单 // @param ticketId 工单ID @@ -369,7 +420,6 @@ func (s *CallbackTicketService) UpdateTicketToTimeout(ticket *CallbackTicket, or AgentEmail: newAgent.Email, AgentMobile: newAgent.Mobile, Comment: fmt.Sprintf("由超时工单 %d 自动重建", ticket.TicketId), - MsdData: ticket.MsdData, RmUid: ticket.RmUid, CreatedAt: now, UpdatedAt: nil, @@ -458,7 +508,6 @@ func (s *CallbackTicketService) BatchUpdateTimeoutTickets(tickets []CallbackTick AgentEmail: newAgent.Email, AgentMobile: newAgent.Mobile, Comment: fmt.Sprintf("由超时工单 %d 自动重建", ticket.TicketId), - MsdData: ticket.MsdData, RmUid: ticket.RmUid, CreatedAt: now + int64(i), // 确保每个工单的创建时间不同, UpdatedAt: nil, @@ -527,15 +576,34 @@ func (s *CallbackTicketService) FindNearlyTimeoutTickets(status string, timeoutM return tickets, nil } -// 新增方法:坐席开始处理工单 -func (s *CallbackTicketService) StartProcessingTicket(ticketId int64) error { +// 新增方法:座席开始处理工单 +// StartProcessingTicket 将工单状态更新为正在处理,并记录开始处理的时间和座席信息 +// @Description 座席开始处理工单 +// @param ticketId 工单ID +// @param updatedBy 更新人 +// @return error 错误信息 +// @example +// mfCallbackTicketService.StartProcessingTicket(12345, "agent1") +// @note 该方法会将工单状态从 NEW 更新为 IN_PROGRESS,并记录处理开始时间和座席信息 +// @note 如果工单状态不是 NEW,则返回错误 +// @note 该方法通常在座席开始处理工单时调用 +// @note 该方法会更新工单的 updated_at 和 updated_by 字段 +// @note 如果更新失败,则返回错误信息 +// @note 该方法会记录处理开始的时间戳,单位为微秒 +// @note 如果工单不存在或状态不正确,则返回错误信息 +// @note 该方法会在更新成功后返回 nil,表示操作成功 +// @note 该方法适用于座席开始处理工单的场景 +// @note 该方法会在更新成功后返回 nil,表示操作成功 +// @note 该方法会在更新失败时返回错误信息,便于调用方处理异常情况 +func (s *CallbackTicketService) StartProcessingTicket(ticketId int64, updatedBy string) 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(), + "comment": "座席开始处理工单", "updated_at": now, - "comment": "坐席开始处理工单", + "updated_by": updatedBy, }).Error; err != nil { return fmt.Errorf("failed to update ticket to IN_PROGRESS: %w", err) }