package developer import ( "fmt" "log" "verification-platform-backend/internal/database" "verification-platform-backend/internal/model" "verification-platform-backend/internal/service" "verification-platform-backend/pkg/response" "github.com/gin-gonic/gin" ) func SetupTicketRoutes(r *gin.RouterGroup) { tickets := r.Group("/tickets") { tickets.GET("/stats", handleGetTicketStats) tickets.GET("", handleGetTickets) tickets.GET("/:id", handleGetTicket) tickets.POST("", handleCreateTicket) tickets.PUT("/:id", handleUpdateTicket) tickets.PUT("/:id/status", handleUpdateTicketStatus) tickets.DELETE("/:id", handleDeleteTicket) tickets.DELETE("/batch", handleBatchDeleteTickets) tickets.GET("/:id/replies", handleGetTicketReplies) tickets.POST("/:id/replies", handleCreateTicketReply) } } func handleGetTicketStats(c *gin.Context) { userID := c.GetUint("user_id") var total, open, processing, resolved, closed int64 database.DB.Model(&model.Ticket{}).Where("user_id = ? OR assigned_to = ?", userID, userID).Count(&total) database.DB.Model(&model.Ticket{}).Where("(user_id = ? OR assigned_to = ?) AND status = ?", userID, userID, "open").Count(&open) database.DB.Model(&model.Ticket{}).Where("(user_id = ? OR assigned_to = ?) AND status = ?", userID, userID, "processing").Count(&processing) database.DB.Model(&model.Ticket{}).Where("(user_id = ? OR assigned_to = ?) AND status = ?", userID, userID, "resolved").Count(&resolved) database.DB.Model(&model.Ticket{}).Where("(user_id = ? OR assigned_to = ?) AND status = ?", userID, userID, "closed").Count(&closed) response.Success(c, gin.H{ "total_count": total, "open_count": open, "processing_count": processing, "resolved_count": resolved, "closed_count": closed, }) } func handleGetTickets(c *gin.Context) { userID := c.GetUint("user_id") var tickets []model.Ticket if err := database.DB.Where("user_id = ? OR assigned_to = ?", userID, userID). Preload("Replies"). Preload("Application"). Preload("User"). Preload("AssignedUser"). Find(&tickets).Error; err != nil { response.Error(c, 500, "获取工单列表失败") return } type TicketWithNames struct { model.Ticket AppName string `json:"app_name"` UserName string `json:"user_name"` } var result []TicketWithNames for _, ticket := range tickets { appName := "" if ticket.Application != nil { appName = ticket.Application.Name } userName := "" if ticket.User.ID != 0 { userName = ticket.User.Username } result = append(result, TicketWithNames{ Ticket: ticket, AppName: appName, UserName: userName, }) } response.Success(c, gin.H{ "tickets": result, "total": len(result), }) } func handleGetTicket(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var ticket model.Ticket if err := database.DB.Where("id = ? AND (user_id = ? OR assigned_to = ?)", id, userID, userID). Preload("Application"). Preload("User"). Preload("AssignedUser"). First(&ticket).Error; err != nil { response.Error(c, 404, "工单不存在") return } type TicketWithNames struct { model.Ticket AppName string `json:"app_name"` UserName string `json:"user_name"` } appName := "" if ticket.Application != nil { appName = ticket.Application.Name } userName := "" if ticket.User.ID != 0 { userName = ticket.User.Username } result := TicketWithNames{ Ticket: ticket, AppName: appName, UserName: userName, } response.Success(c, gin.H{ "ticket": result, }) } func handleCreateTicket(c *gin.Context) { log.Printf("handleCreateTicket called\n") userID := c.GetUint("user_id") log.Printf("Starting ParseMultipartForm...\n") if err := c.Request.ParseMultipartForm(32 << 20); err != nil { log.Printf("ParseMultipartForm error: %v\n", err) response.Error(c, 400, "解析表单数据失败: "+err.Error()) return } log.Printf("ParseMultipartForm succeeded\n") ticketType := c.PostForm("type") title := c.PostForm("title") content := c.PostForm("content") priority := c.PostForm("priority") applicationIDStr := c.PostForm("application_id") log.Printf("Received ticket data - type: '%s', title: '%s', content: '%s', priority: '%s', application_id: '%s'\n", ticketType, title, content, priority, applicationIDStr) if ticketType == "" { ticketType = "system" } if title == "" { log.Printf("Title is empty\n") response.Error(c, 400, "标题不能为空") return } if content == "" { log.Printf("Content is empty\n") response.Error(c, 400, "描述不能为空") return } if priority == "" { priority = "normal" } var applicationID *uint if ticketType == "application" && applicationIDStr != "" { var appID uint if _, err := fmt.Sscanf(applicationIDStr, "%d", &appID); err == nil { applicationID = &appID } } ticket := model.Ticket{ UserID: userID, ApplicationID: applicationID, Title: title, Content: content, Category: "other", Priority: priority, Status: "open", Type: ticketType, } if applicationID != nil { var application model.Application if err := database.DB.First(&application, *applicationID).Error; err != nil { response.Error(c, 404, "应用不存在") return } ticket.AssignedTo = &application.UserID } log.Printf("Creating ticket in database...\n") if err := database.DB.Create(&ticket).Error; err != nil { log.Printf("Create ticket error: %v\n", err) response.Error(c, 500, "创建工单失败") return } log.Printf("Ticket created with ID: %d\n", ticket.ID) service.LogOperation(c, "create", "ticket", &ticket.ID, fmt.Sprintf("创建工单: %s", ticket.Title), nil) log.Printf("Loading ticket details...\n") if err := database.DB.Preload("User").Preload("Application").First(&ticket, ticket.ID).Error; err != nil { response.Error(c, 500, "获取工单信息失败") return } type TicketWithNames struct { model.Ticket AppName string `json:"app_name"` UserName string `json:"user_name"` } appName := "" if ticket.Application != nil { appName = ticket.Application.Name } userName := "" if ticket.User.ID != 0 { userName = ticket.User.Username } result := TicketWithNames{ Ticket: ticket, AppName: appName, UserName: userName, } log.Printf("Returning success response\n") response.Success(c, result) } func handleUpdateTicket(c *gin.Context) { userID := c.GetUint("user_id") var req struct { Status string `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } id := c.Param("id") var ticket model.Ticket if err := database.DB.Where("id = ? AND user_id = ?", id, userID).First(&ticket).Error; err != nil { response.Error(c, 404, "工单不存在") return } ticket.Status = req.Status if err := database.DB.Save(&ticket).Error; err != nil { response.Error(c, 500, "更新工单失败") return } response.Success(c, ticket) } func handleUpdateTicketStatus(c *gin.Context) { userID := c.GetUint("user_id") var req struct { Status string `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } id := c.Param("id") var ticket model.Ticket if err := database.DB.Where("id = ? AND user_id = ?", id, userID).First(&ticket).Error; err != nil { response.Error(c, 404, "工单不存在") return } ticket.Status = req.Status if err := database.DB.Save(&ticket).Error; err != nil { response.Error(c, 500, "更新工单状态失败") return } response.Success(c, ticket) } func handleDeleteTicket(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var ticket model.Ticket if err := database.DB.Where("id = ? AND (user_id = ? OR assigned_to = ?)", id, userID, userID).First(&ticket).Error; err != nil { response.Error(c, 404, "工单不存在") return } if err := database.DB.Delete(&ticket).Error; err != nil { response.Error(c, 500, "删除工单失败") return } service.LogOperation(c, "delete", "ticket", &ticket.ID, fmt.Sprintf("删除工单: %s", ticket.Title), nil) response.Success(c, nil) } func handleBatchDeleteTickets(c *gin.Context) { userID := c.GetUint("user_id") var req struct { IDs []uint `json:"ids"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } if len(req.IDs) == 0 { response.Error(c, 400, "请选择要删除的工单") return } var tickets []model.Ticket if err := database.DB.Where("id IN ? AND (user_id = ? OR assigned_to = ?)", req.IDs, userID, userID).Find(&tickets).Error; err != nil { response.Error(c, 500, "查询工单失败") return } if len(tickets) == 0 { response.Error(c, 404, "没有找到可删除的工单") return } var validIDs []uint for _, ticket := range tickets { validIDs = append(validIDs, ticket.ID) } if err := database.DB.Where("id IN ?", validIDs).Delete(&model.Ticket{}).Error; err != nil { response.Error(c, 500, "批量删除工单失败") return } response.Success(c, gin.H{ "deleted": len(validIDs), }) } func handleGetTicketReplies(c *gin.Context) { userID := c.GetUint("user_id") ticketID := c.Param("id") var ticket model.Ticket if err := database.DB.Where("id = ? AND user_id = ?", ticketID, userID).First(&ticket).Error; err != nil { response.Error(c, 404, "工单不存在") return } var replies []model.TicketReply if err := database.DB.Where("ticket_id = ?", ticketID).Preload("User").Order("created_at ASC").Find(&replies).Error; err != nil { response.Error(c, 500, "获取回复列表失败") return } response.Success(c, gin.H{ "replies": replies, "total": len(replies), }) } func handleCreateTicketReply(c *gin.Context) { userID := c.GetUint("user_id") ticketID := c.Param("id") var ticket model.Ticket if err := database.DB.Where("id = ? AND user_id = ?", ticketID, userID).First(&ticket).Error; err != nil { response.Error(c, 404, "工单不存在") return } var req struct { Content string `json:"content"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } reply := model.TicketReply{ TicketID: parseUint(ticketID), UserID: userID, Content: req.Content, } if err := database.DB.Create(&reply).Error; err != nil { response.Error(c, 500, "创建回复失败") return } response.Success(c, reply) } func parseUint(s string) uint { var val uint fmt.Sscanf(s, "%d", &val) return val }