package app import ( "fmt" "log" "strconv" "time" "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) { r.GET("/tickets", handleAppGetTickets) r.GET("/tickets/:id", handleAppGetTicket) r.POST("/tickets", handleAppCreateTicket) r.POST("/tickets/:id/reply", handleAppReplyTicket) r.PUT("/tickets/:id/close", handleAppCloseTicket) } func handleAppGetTickets(c *gin.Context) { appKey := c.Param("appKey") var app model.Application if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil { response.Error(c, 404, "应用不存在") return } if service.GetApplicationDisabledStatus(app.ID) { response.Error(c, 403, "该应用已被禁用") return } userID, exists := c.Get("user_id") if !exists { response.Error(c, 401, "未授权") return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) statusFilter := c.Query("status") query := database.DB.Model(&model.Ticket{}).Where("user_id = ? AND application_id = ?", userID, app.ID) if statusFilter != "" { query = query.Where("status = ?", statusFilter) } var total int64 query.Count(&total) offset := (page - 1) * pageSize var tickets []model.Ticket if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&tickets).Error; err != nil { response.Error(c, 500, "获取工单列表失败") return } response.Success(c, gin.H{ "tickets": tickets, "total": total, "page": page, "page_size": pageSize, "total_page": (total + int64(pageSize) - 1) / int64(pageSize), }) } func handleAppGetTicket(c *gin.Context) { appKey := c.Param("appKey") var app model.Application if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil { response.Error(c, 404, "应用不存在") return } if service.GetApplicationDisabledStatus(app.ID) { response.Error(c, 403, "该应用已被禁用") return } userID, exists := c.Get("user_id") if !exists { response.Error(c, 401, "未授权") return } ticketID := c.Param("id") var ticket model.Ticket if err := database.DB.Where("id = ? AND user_id = ? AND application_id = ?", ticketID, userID, app.ID). Preload("Replies"). First(&ticket).Error; err != nil { response.Error(c, 404, "工单不存在") return } response.Success(c, gin.H{ "ticket": ticket, }) } func handleAppCreateTicket(c *gin.Context) { appKey := c.Param("appKey") var app model.Application if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil { response.Error(c, 404, "应用不存在") return } if service.GetApplicationDisabledStatus(app.ID) { response.Error(c, 403, "该应用已被禁用") return } userID, exists := c.Get("user_id") if !exists { response.Error(c, 401, "未授权") return } var req struct { Title string `json:"title" binding:"required"` Content string `json:"content" binding:"required"` Type string `json:"type"` Priority string `json:"priority"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } if req.Type == "" { req.Type = "other" } if req.Priority == "" { req.Priority = "normal" } ticket := model.Ticket{ UserID: userID.(uint), ApplicationID: &app.ID, Title: req.Title, Content: req.Content, Type: req.Type, Category: "other", Priority: req.Priority, Status: "open", AssignedTo: &app.UserID, } if err := database.DB.Create(&ticket).Error; err != nil { log.Printf("[ERROR] Failed to create ticket: %v", err) response.Error(c, 500, "创建工单失败") return } service.LogOperation(c, "create", "ticket", &ticket.ID, fmt.Sprintf("用户创建工单: %s", ticket.Title), nil) response.Success(c, gin.H{ "id": ticket.ID, "message": "工单创建成功", }) } func handleAppReplyTicket(c *gin.Context) { appKey := c.Param("appKey") var app model.Application if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil { response.Error(c, 404, "应用不存在") return } if service.GetApplicationDisabledStatus(app.ID) { response.Error(c, 403, "该应用已被禁用") return } userID, exists := c.Get("user_id") if !exists { response.Error(c, 401, "未授权") return } ticketID := c.Param("id") var ticket model.Ticket if err := database.DB.Where("id = ? AND user_id = ? AND application_id = ?", ticketID, userID, app.ID).First(&ticket).Error; err != nil { response.Error(c, 404, "工单不存在") return } if ticket.Status == "closed" { response.Error(c, 400, "工单已关闭,无法回复") return } var req struct { Content string `json:"content" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } reply := model.TicketReply{ TicketID: parseUint(ticketID), UserID: userID.(uint), Content: req.Content, } if err := database.DB.Create(&reply).Error; err != nil { response.Error(c, 500, "回复失败") return } if ticket.Status == "resolved" || ticket.Status == "processing" { ticket.Status = "processing" database.DB.Save(&ticket) } response.Success(c, gin.H{ "id": reply.ID, "message": "回复成功", }) } func handleAppCloseTicket(c *gin.Context) { appKey := c.Param("appKey") var app model.Application if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil { response.Error(c, 404, "应用不存在") return } if service.GetApplicationDisabledStatus(app.ID) { response.Error(c, 403, "该应用已被禁用") return } userID, exists := c.Get("user_id") if !exists { response.Error(c, 401, "未授权") return } ticketID := c.Param("id") var ticket model.Ticket if err := database.DB.Where("id = ? AND user_id = ? AND application_id = ?", ticketID, userID, app.ID).First(&ticket).Error; err != nil { response.Error(c, 404, "工单不存在") return } if ticket.Status == "closed" { response.Error(c, 400, "工单已关闭") return } now := time.Now() ticket.Status = "closed" ticket.ClosedAt = &now if err := database.DB.Save(&ticket).Error; err != nil { response.Error(c, 500, "关闭工单失败") return } service.LogOperation(c, "close", "ticket", &ticket.ID, fmt.Sprintf("用户关闭工单: %s", ticket.Title), nil) response.Success(c, gin.H{ "message": "工单已关闭", }) } func parseUint(s string) uint { var val uint fmt.Sscanf(s, "%d", &val) return val }