fix: ticket update based on review meeting

This commit is contained in:
zhangsz
2025-07-11 17:25:47 +08:00
parent 6e9765ceaa
commit 53283b0669
6 changed files with 150 additions and 11 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,

View File

@@ -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)

View File

@@ -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"` // 更新时间

View File

@@ -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(&currentTicket).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)
}