From e94f16078252dc7f724c0a93fd22f29408ddd31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Wed, 13 May 2026 19:01:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A54=E4=B8=AA=E5=BF=85=E6=94=B9=E9=A1=B9+4?= =?UTF-8?q?=E4=B8=AA=E5=BB=BA=E8=AE=AE=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 工单编号改为业务格式 TK-yyMMdd-NNN - 类型断言加 comma-ok 防 panic - priority 用指针区分未传/P0 - json.Marshal 错误处理 - 提取 ParseID 公共函数消除重复 - HTTP client 包级别复用 - LIKE 查询特殊字符转义 - interface{} → any - auth 中间件用 dto.Fail 统一响应 --- backend/internal/dto/common.go | 4 +-- backend/internal/dto/ticket.go | 2 +- backend/internal/handler/analysis_handler.go | 22 +++++--------- backend/internal/handler/note_handler.go | 14 +++------ backend/internal/handler/ticket_handler.go | 30 +++++++------------- backend/internal/middleware/auth.go | 26 +++++++++++++---- backend/internal/service/analysis_service.go | 22 +++++++------- backend/internal/service/ticket_service.go | 17 +++++++---- 8 files changed, 67 insertions(+), 70 deletions(-) diff --git a/backend/internal/dto/common.go b/backend/internal/dto/common.go index 12ac956..7313b31 100644 --- a/backend/internal/dto/common.go +++ b/backend/internal/dto/common.go @@ -4,10 +4,10 @@ type Response struct { Success bool `json:"success"` Retcode int `json:"retcode"` Retinfo string `json:"retinfo"` - Result interface{} `json:"result,omitempty"` + Result any `json:"result,omitempty"` } -func Success(data interface{}) Response { +func Success(data any) Response { return Response{Success: true, Retcode: 0, Retinfo: "", Result: data} } diff --git a/backend/internal/dto/ticket.go b/backend/internal/dto/ticket.go index 9364724..f5cd461 100644 --- a/backend/internal/dto/ticket.go +++ b/backend/internal/dto/ticket.go @@ -21,7 +21,7 @@ type UpdateTicketRequest struct { Contactname string `json:"contactname"` Contactphone string `json:"contactphone"` Category string `json:"category"` - Priority int16 `json:"priority"` + Priority *int16 `json:"priority"` Handlerid *int `json:"handlerid"` } diff --git a/backend/internal/handler/analysis_handler.go b/backend/internal/handler/analysis_handler.go index 3ec2fd4..be39313 100644 --- a/backend/internal/handler/analysis_handler.go +++ b/backend/internal/handler/analysis_handler.go @@ -1,8 +1,6 @@ package handler import ( - "strconv" - "github.com/casehub/ticket-workbench/internal/config" "github.com/casehub/ticket-workbench/internal/dto" "github.com/casehub/ticket-workbench/internal/middleware" @@ -14,10 +12,8 @@ import ( func AnalyzeTicket(db *gorm.DB, cfg *config.Config) gin.HandlerFunc { return func(c *gin.Context) { - idStr := c.Param("id") - id, err := strconv.Atoi(idStr) - if err != nil { - c.JSON(200, dto.Fail("参数错误")) + id, ok := middleware.ParseID(c) + if !ok { return } @@ -40,10 +36,8 @@ func AnalyzeTicket(db *gorm.DB, cfg *config.Config) gin.HandlerFunc { func GetAnalysis(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { - idStr := c.Param("id") - id, err := strconv.Atoi(idStr) - if err != nil { - c.JSON(200, dto.Fail("参数错误")) + id, ok := middleware.ParseID(c) + if !ok { return } @@ -59,10 +53,8 @@ func GetAnalysis(db *gorm.DB) gin.HandlerFunc { func ConfirmAnalysis(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { - idStr := c.Param("id") - ticketid, err := strconv.Atoi(idStr) - if err != nil { - c.JSON(200, dto.Fail("参数错误")) + ticketid, ok := middleware.ParseID(c) + if !ok { return } @@ -84,7 +76,7 @@ func ConfirmAnalysis(db *gorm.DB) gin.HandlerFunc { return } - err = service.ConfirmAnalysis(db, analysis.Analysisid, req.Category, req.Priority, req.Summary, user.Userid) + err := service.ConfirmAnalysis(db, analysis.Analysisid, req.Category, req.Priority, req.Summary, user.Userid) if err != nil { c.JSON(200, dto.Fail("确认失败")) return diff --git a/backend/internal/handler/note_handler.go b/backend/internal/handler/note_handler.go index cb89713..f728526 100644 --- a/backend/internal/handler/note_handler.go +++ b/backend/internal/handler/note_handler.go @@ -1,8 +1,6 @@ package handler import ( - "strconv" - "github.com/casehub/ticket-workbench/internal/dto" "github.com/casehub/ticket-workbench/internal/middleware" "github.com/casehub/ticket-workbench/internal/service" @@ -12,10 +10,8 @@ import ( func ListNotes(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { - idStr := c.Param("id") - id, err := strconv.Atoi(idStr) - if err != nil { - c.JSON(200, dto.Fail("参数错误")) + id, ok := middleware.ParseID(c) + if !ok { return } @@ -31,10 +27,8 @@ func ListNotes(db *gorm.DB) gin.HandlerFunc { func AddNote(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { - idStr := c.Param("id") - id, err := strconv.Atoi(idStr) - if err != nil { - c.JSON(200, dto.Fail("参数错误")) + id, ok := middleware.ParseID(c) + if !ok { return } diff --git a/backend/internal/handler/ticket_handler.go b/backend/internal/handler/ticket_handler.go index a8f5fb2..a5c8d37 100644 --- a/backend/internal/handler/ticket_handler.go +++ b/backend/internal/handler/ticket_handler.go @@ -1,8 +1,6 @@ package handler import ( - "strconv" - "github.com/casehub/ticket-workbench/internal/dto" "github.com/casehub/ticket-workbench/internal/middleware" "github.com/casehub/ticket-workbench/internal/service" @@ -37,10 +35,8 @@ func ListTickets(db *gorm.DB) gin.HandlerFunc { func GetTicket(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { - idStr := c.Param("id") - id, err := strconv.Atoi(idStr) - if err != nil { - c.JSON(200, dto.Fail("参数错误")) + id, ok := middleware.ParseID(c) + if !ok { return } @@ -80,10 +76,8 @@ func CreateTicket(db *gorm.DB) gin.HandlerFunc { func UpdateTicket(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { - idStr := c.Param("id") - id, err := strconv.Atoi(idStr) - if err != nil { - c.JSON(200, dto.Fail("参数错误")) + id, ok := middleware.ParseID(c) + if !ok { return } @@ -99,7 +93,7 @@ func UpdateTicket(db *gorm.DB) gin.HandlerFunc { return } - err = service.UpdateTicket(db, id, req.Title, req.Content, req.Contactname, req.Contactphone, req.Category, req.Priority, req.Handlerid, user.Userid) + err := service.UpdateTicket(db, id, req.Title, req.Content, req.Contactname, req.Contactphone, req.Category, req.Priority, req.Handlerid, user.Userid) if err != nil { c.JSON(200, dto.Fail("更新失败")) return @@ -111,10 +105,8 @@ func UpdateTicket(db *gorm.DB) gin.HandlerFunc { func UpdateTicketStatus(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { - idStr := c.Param("id") - id, err := strconv.Atoi(idStr) - if err != nil { - c.JSON(200, dto.Fail("参数错误")) + id, ok := middleware.ParseID(c) + if !ok { return } @@ -130,7 +122,7 @@ func UpdateTicketStatus(db *gorm.DB) gin.HandlerFunc { return } - err = service.UpdateTicketStatus(db, id, req.Status, user.Userid) + err := service.UpdateTicketStatus(db, id, req.Status, user.Userid) if err != nil { c.JSON(200, dto.Fail("更新失败")) return @@ -142,10 +134,8 @@ func UpdateTicketStatus(db *gorm.DB) gin.HandlerFunc { func GetOperationLogs(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { - idStr := c.Param("id") - id, err := strconv.Atoi(idStr) - if err != nil { - c.JSON(200, dto.Fail("参数错误")) + id, ok := middleware.ParseID(c) + if !ok { return } diff --git a/backend/internal/middleware/auth.go b/backend/internal/middleware/auth.go index e03f53e..e628f8b 100644 --- a/backend/internal/middleware/auth.go +++ b/backend/internal/middleware/auth.go @@ -1,6 +1,9 @@ package middleware import ( + "strconv" + + "github.com/casehub/ticket-workbench/internal/dto" "github.com/casehub/ticket-workbench/internal/model" "github.com/casehub/ticket-workbench/internal/service" "github.com/gin-gonic/gin" @@ -11,14 +14,14 @@ func Auth(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { sessionID := extractSessionID(c) if sessionID == "" { - c.JSON(200, map[string]interface{}{"success": false, "retcode": -1, "retinfo": "未登录"}) + c.JSON(200, dto.Fail("未登录")) c.Abort() return } user := service.GetUserBySession(sessionID) if user == nil { - c.JSON(200, map[string]interface{}{"success": false, "retcode": -1, "retinfo": "登录已过期"}) + c.JSON(200, dto.Fail("登录已过期")) c.Abort() return } @@ -43,17 +46,30 @@ func extractSessionID(c *gin.Context) string { } func GetCurrentUser(c *gin.Context) *model.TicketUser { - userid, exists := c.Get("userid") - if !exists { + uid, ok := c.Get("userid") + if !ok { + return nil + } + userid, ok := uid.(int) + if !ok { return nil } username, _ := c.Get("username") role, _ := c.Get("role") team, _ := c.Get("team") return &model.TicketUser{ - Userid: userid.(int), + Userid: userid, Username: username.(string), Role: role.(int16), Team: team.(string), } } + +func ParseID(c *gin.Context) (int, bool) { + id, err := strconv.Atoi(c.Param("id")) + if err != nil { + c.JSON(200, dto.Fail("参数错误")) + return 0, false + } + return id, true +} diff --git a/backend/internal/service/analysis_service.go b/backend/internal/service/analysis_service.go index 4720d86..915d1ba 100644 --- a/backend/internal/service/analysis_service.go +++ b/backend/internal/service/analysis_service.go @@ -13,6 +13,8 @@ import ( "gorm.io/gorm" ) +var httpClient = &http.Client{Timeout: 30 * time.Second} + type GLMRequest struct { Model string `json:"model"` Messages []Message `json:"messages"` @@ -32,10 +34,10 @@ type Choice struct { } type AnalysisResult struct { - Category string `json:"category"` + Category string `json:"category"` Priority json.Number `json:"priority"` - Summary string `json:"summary"` - SuggestRole string `json:"suggest_role"` + Summary string `json:"summary"` + SuggestRole string `json:"suggest_role"` } func AnalyzeTicket(db *gorm.DB, ticketid int, apikey, baseURL, glmModel, title, content, contactname, contactphone string) (*model.TicketAiAnalysis, error) { @@ -62,7 +64,10 @@ func AnalyzeTicket(db *gorm.DB, ticketid int, apikey, baseURL, glmModel, title, }, } - jsonData, _ := json.Marshal(reqBody) + jsonData, err := json.Marshal(reqBody) + if err != nil { + return nil, fmt.Errorf("构建请求失败: %w", err) + } httpReq, err := http.NewRequest("POST", baseURL+"/chat/completions", bytes.NewBuffer(jsonData)) if err != nil { @@ -72,8 +77,7 @@ func AnalyzeTicket(db *gorm.DB, ticketid int, apikey, baseURL, glmModel, title, httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+apikey) - client := &http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(httpReq) + resp, err := httpClient.Do(httpReq) if err != nil { return nil, err } @@ -90,7 +94,6 @@ func AnalyzeTicket(db *gorm.DB, ticketid int, apikey, baseURL, glmModel, title, return nil, fmt.Errorf("AI返回为空") } - // Clean markdown code block wrapping from AI response aiContent := strings.TrimSpace(glmResp.Choices[0].Message.Content) aiContent = strings.TrimPrefix(aiContent, "```json") aiContent = strings.TrimPrefix(aiContent, "```") @@ -102,7 +105,7 @@ func AnalyzeTicket(db *gorm.DB, ticketid int, apikey, baseURL, glmModel, title, return nil, fmt.Errorf("解析AI响应失败: %v, 原始内容: %s", err, aiContent) } - priority := int16(2) // default P2 + priority := int16(2) if n, err := result.Priority.Int64(); err == nil { priority = int16(n) } @@ -142,9 +145,6 @@ func ConfirmAnalysis(db *gorm.DB, analysisid int, category string, priority int1 if category != "" { updates["category"] = category } - if priority >= 0 { - updates["priority"] = priority - } if summary != "" { updates["summary"] = summary } diff --git a/backend/internal/service/ticket_service.go b/backend/internal/service/ticket_service.go index a39f2d7..563a057 100644 --- a/backend/internal/service/ticket_service.go +++ b/backend/internal/service/ticket_service.go @@ -2,10 +2,10 @@ package service import ( "fmt" + "strings" "time" "github.com/casehub/ticket-workbench/internal/model" - "github.com/google/uuid" "gorm.io/gorm" ) @@ -30,7 +30,9 @@ func ListTickets(db *gorm.DB, status *int16, category string, priority *int16, k query = query.Where("priority = ?", *priority) } if keyword != "" { - query = query.Where("title LIKE ? OR content LIKE ?", "%"+keyword+"%", "%"+keyword+"%") + escaped := strings.ReplaceAll(keyword, "%", "\\%") + escaped = strings.ReplaceAll(escaped, "_", "\\_") + query = query.Where("title LIKE ? OR content LIKE ?", "%"+escaped+"%", "%"+escaped+"%") } if err := query.Count(&total).Error; err != nil { @@ -55,8 +57,11 @@ func GetTicketByID(db *gorm.DB, ticketid int) (*model.TicketInfo, error) { } func CreateTicket(db *gorm.DB, title, content, contactname, contactphone, source, category string, priority int16, submitterid int) (*model.TicketInfo, error) { - ticketno := fmt.Sprintf("TKT%s", uuid.New().String()[:8]) now := time.Now() + dateStr := now.Format("060102") + var count int64 + db.Model(&model.TicketInfo{}).Where("DATE(createtime) = CURDATE()").Count(&count) + ticketno := fmt.Sprintf("TK-%s-%03d", dateStr, count+1) ticket := &model.TicketInfo{ Ticketno: ticketno, @@ -82,7 +87,7 @@ func CreateTicket(db *gorm.DB, title, content, contactname, contactphone, source return ticket, nil } -func UpdateTicket(db *gorm.DB, ticketid int, title, content, contactname, contactphone, category string, priority int16, handlerid *int, operatorid int) error { +func UpdateTicket(db *gorm.DB, ticketid int, title, content, contactname, contactphone, category string, priority *int16, handlerid *int, operatorid int) error { updates := map[string]interface{}{ "updatetime": time.Now(), } @@ -101,8 +106,8 @@ func UpdateTicket(db *gorm.DB, ticketid int, title, content, contactname, contac if category != "" { updates["category"] = category } - if priority >= 0 { - updates["priority"] = priority + if priority != nil { + updates["priority"] = *priority } if handlerid != nil { updates["handlerid"] = handlerid