414 lines
10 KiB
Go
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
|
|
}
|