Files
verify/backend/internal/router/developer/tickets.go
T
2026-04-27 17:22:56 +08:00

414 lines
10 KiB
Go

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
}