Initial commit: 网络验证平台
This commit is contained in:
@@ -0,0 +1,413 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user