修复: 代码审查4个必改项+4个建议改进

- 工单编号改为业务格式 TK-yyMMdd-NNN
- 类型断言加 comma-ok 防 panic
- priority 用指针区分未传/P0
- json.Marshal 错误处理
- 提取 ParseID 公共函数消除重复
- HTTP client 包级别复用
- LIKE 查询特殊字符转义
- interface{} → any
- auth 中间件用 dto.Fail 统一响应
This commit is contained in:
2026-05-13 19:01:06 +08:00
parent c5c2a64a48
commit e94f160782
8 changed files with 67 additions and 70 deletions

View File

@@ -4,10 +4,10 @@ type Response struct {
Success bool `json:"success"` Success bool `json:"success"`
Retcode int `json:"retcode"` Retcode int `json:"retcode"`
Retinfo string `json:"retinfo"` 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} return Response{Success: true, Retcode: 0, Retinfo: "", Result: data}
} }

View File

@@ -21,7 +21,7 @@ type UpdateTicketRequest struct {
Contactname string `json:"contactname"` Contactname string `json:"contactname"`
Contactphone string `json:"contactphone"` Contactphone string `json:"contactphone"`
Category string `json:"category"` Category string `json:"category"`
Priority int16 `json:"priority"` Priority *int16 `json:"priority"`
Handlerid *int `json:"handlerid"` Handlerid *int `json:"handlerid"`
} }

View File

@@ -1,8 +1,6 @@
package handler package handler
import ( import (
"strconv"
"github.com/casehub/ticket-workbench/internal/config" "github.com/casehub/ticket-workbench/internal/config"
"github.com/casehub/ticket-workbench/internal/dto" "github.com/casehub/ticket-workbench/internal/dto"
"github.com/casehub/ticket-workbench/internal/middleware" "github.com/casehub/ticket-workbench/internal/middleware"
@@ -14,10 +12,8 @@ import (
func AnalyzeTicket(db *gorm.DB, cfg *config.Config) gin.HandlerFunc { func AnalyzeTicket(db *gorm.DB, cfg *config.Config) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
idStr := c.Param("id") id, ok := middleware.ParseID(c)
id, err := strconv.Atoi(idStr) if !ok {
if err != nil {
c.JSON(200, dto.Fail("参数错误"))
return return
} }
@@ -40,10 +36,8 @@ func AnalyzeTicket(db *gorm.DB, cfg *config.Config) gin.HandlerFunc {
func GetAnalysis(db *gorm.DB) gin.HandlerFunc { func GetAnalysis(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
idStr := c.Param("id") id, ok := middleware.ParseID(c)
id, err := strconv.Atoi(idStr) if !ok {
if err != nil {
c.JSON(200, dto.Fail("参数错误"))
return return
} }
@@ -59,10 +53,8 @@ func GetAnalysis(db *gorm.DB) gin.HandlerFunc {
func ConfirmAnalysis(db *gorm.DB) gin.HandlerFunc { func ConfirmAnalysis(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
idStr := c.Param("id") ticketid, ok := middleware.ParseID(c)
ticketid, err := strconv.Atoi(idStr) if !ok {
if err != nil {
c.JSON(200, dto.Fail("参数错误"))
return return
} }
@@ -84,7 +76,7 @@ func ConfirmAnalysis(db *gorm.DB) gin.HandlerFunc {
return 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 { if err != nil {
c.JSON(200, dto.Fail("确认失败")) c.JSON(200, dto.Fail("确认失败"))
return return

View File

@@ -1,8 +1,6 @@
package handler package handler
import ( import (
"strconv"
"github.com/casehub/ticket-workbench/internal/dto" "github.com/casehub/ticket-workbench/internal/dto"
"github.com/casehub/ticket-workbench/internal/middleware" "github.com/casehub/ticket-workbench/internal/middleware"
"github.com/casehub/ticket-workbench/internal/service" "github.com/casehub/ticket-workbench/internal/service"
@@ -12,10 +10,8 @@ import (
func ListNotes(db *gorm.DB) gin.HandlerFunc { func ListNotes(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
idStr := c.Param("id") id, ok := middleware.ParseID(c)
id, err := strconv.Atoi(idStr) if !ok {
if err != nil {
c.JSON(200, dto.Fail("参数错误"))
return return
} }
@@ -31,10 +27,8 @@ func ListNotes(db *gorm.DB) gin.HandlerFunc {
func AddNote(db *gorm.DB) gin.HandlerFunc { func AddNote(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
idStr := c.Param("id") id, ok := middleware.ParseID(c)
id, err := strconv.Atoi(idStr) if !ok {
if err != nil {
c.JSON(200, dto.Fail("参数错误"))
return return
} }

View File

@@ -1,8 +1,6 @@
package handler package handler
import ( import (
"strconv"
"github.com/casehub/ticket-workbench/internal/dto" "github.com/casehub/ticket-workbench/internal/dto"
"github.com/casehub/ticket-workbench/internal/middleware" "github.com/casehub/ticket-workbench/internal/middleware"
"github.com/casehub/ticket-workbench/internal/service" "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 { func GetTicket(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
idStr := c.Param("id") id, ok := middleware.ParseID(c)
id, err := strconv.Atoi(idStr) if !ok {
if err != nil {
c.JSON(200, dto.Fail("参数错误"))
return return
} }
@@ -80,10 +76,8 @@ func CreateTicket(db *gorm.DB) gin.HandlerFunc {
func UpdateTicket(db *gorm.DB) gin.HandlerFunc { func UpdateTicket(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
idStr := c.Param("id") id, ok := middleware.ParseID(c)
id, err := strconv.Atoi(idStr) if !ok {
if err != nil {
c.JSON(200, dto.Fail("参数错误"))
return return
} }
@@ -99,7 +93,7 @@ func UpdateTicket(db *gorm.DB) gin.HandlerFunc {
return 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 { if err != nil {
c.JSON(200, dto.Fail("更新失败")) c.JSON(200, dto.Fail("更新失败"))
return return
@@ -111,10 +105,8 @@ func UpdateTicket(db *gorm.DB) gin.HandlerFunc {
func UpdateTicketStatus(db *gorm.DB) gin.HandlerFunc { func UpdateTicketStatus(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
idStr := c.Param("id") id, ok := middleware.ParseID(c)
id, err := strconv.Atoi(idStr) if !ok {
if err != nil {
c.JSON(200, dto.Fail("参数错误"))
return return
} }
@@ -130,7 +122,7 @@ func UpdateTicketStatus(db *gorm.DB) gin.HandlerFunc {
return return
} }
err = service.UpdateTicketStatus(db, id, req.Status, user.Userid) err := service.UpdateTicketStatus(db, id, req.Status, user.Userid)
if err != nil { if err != nil {
c.JSON(200, dto.Fail("更新失败")) c.JSON(200, dto.Fail("更新失败"))
return return
@@ -142,10 +134,8 @@ func UpdateTicketStatus(db *gorm.DB) gin.HandlerFunc {
func GetOperationLogs(db *gorm.DB) gin.HandlerFunc { func GetOperationLogs(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
idStr := c.Param("id") id, ok := middleware.ParseID(c)
id, err := strconv.Atoi(idStr) if !ok {
if err != nil {
c.JSON(200, dto.Fail("参数错误"))
return return
} }

View File

@@ -1,6 +1,9 @@
package middleware package middleware
import ( import (
"strconv"
"github.com/casehub/ticket-workbench/internal/dto"
"github.com/casehub/ticket-workbench/internal/model" "github.com/casehub/ticket-workbench/internal/model"
"github.com/casehub/ticket-workbench/internal/service" "github.com/casehub/ticket-workbench/internal/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -11,14 +14,14 @@ func Auth(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
sessionID := extractSessionID(c) sessionID := extractSessionID(c)
if sessionID == "" { if sessionID == "" {
c.JSON(200, map[string]interface{}{"success": false, "retcode": -1, "retinfo": "未登录"}) c.JSON(200, dto.Fail("未登录"))
c.Abort() c.Abort()
return return
} }
user := service.GetUserBySession(sessionID) user := service.GetUserBySession(sessionID)
if user == nil { if user == nil {
c.JSON(200, map[string]interface{}{"success": false, "retcode": -1, "retinfo": "登录已过期"}) c.JSON(200, dto.Fail("登录已过期"))
c.Abort() c.Abort()
return return
} }
@@ -43,17 +46,30 @@ func extractSessionID(c *gin.Context) string {
} }
func GetCurrentUser(c *gin.Context) *model.TicketUser { func GetCurrentUser(c *gin.Context) *model.TicketUser {
userid, exists := c.Get("userid") uid, ok := c.Get("userid")
if !exists { if !ok {
return nil
}
userid, ok := uid.(int)
if !ok {
return nil return nil
} }
username, _ := c.Get("username") username, _ := c.Get("username")
role, _ := c.Get("role") role, _ := c.Get("role")
team, _ := c.Get("team") team, _ := c.Get("team")
return &model.TicketUser{ return &model.TicketUser{
Userid: userid.(int), Userid: userid,
Username: username.(string), Username: username.(string),
Role: role.(int16), Role: role.(int16),
Team: team.(string), 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
}

View File

@@ -13,6 +13,8 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
var httpClient = &http.Client{Timeout: 30 * time.Second}
type GLMRequest struct { type GLMRequest struct {
Model string `json:"model"` Model string `json:"model"`
Messages []Message `json:"messages"` Messages []Message `json:"messages"`
@@ -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)) httpReq, err := http.NewRequest("POST", baseURL+"/chat/completions", bytes.NewBuffer(jsonData))
if err != nil { 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("Content-Type", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+apikey) httpReq.Header.Set("Authorization", "Bearer "+apikey)
client := &http.Client{Timeout: 30 * time.Second} resp, err := httpClient.Do(httpReq)
resp, err := client.Do(httpReq)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -90,7 +94,6 @@ func AnalyzeTicket(db *gorm.DB, ticketid int, apikey, baseURL, glmModel, title,
return nil, fmt.Errorf("AI返回为空") return nil, fmt.Errorf("AI返回为空")
} }
// Clean markdown code block wrapping from AI response
aiContent := strings.TrimSpace(glmResp.Choices[0].Message.Content) aiContent := strings.TrimSpace(glmResp.Choices[0].Message.Content)
aiContent = strings.TrimPrefix(aiContent, "```json") aiContent = strings.TrimPrefix(aiContent, "```json")
aiContent = strings.TrimPrefix(aiContent, "```") 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) 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 { if n, err := result.Priority.Int64(); err == nil {
priority = int16(n) priority = int16(n)
} }
@@ -142,9 +145,6 @@ func ConfirmAnalysis(db *gorm.DB, analysisid int, category string, priority int1
if category != "" { if category != "" {
updates["category"] = category updates["category"] = category
} }
if priority >= 0 {
updates["priority"] = priority
}
if summary != "" { if summary != "" {
updates["summary"] = summary updates["summary"] = summary
} }

View File

@@ -2,10 +2,10 @@ package service
import ( import (
"fmt" "fmt"
"strings"
"time" "time"
"github.com/casehub/ticket-workbench/internal/model" "github.com/casehub/ticket-workbench/internal/model"
"github.com/google/uuid"
"gorm.io/gorm" "gorm.io/gorm"
) )
@@ -30,7 +30,9 @@ func ListTickets(db *gorm.DB, status *int16, category string, priority *int16, k
query = query.Where("priority = ?", *priority) query = query.Where("priority = ?", *priority)
} }
if keyword != "" { 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 { 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) { 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() 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{ ticket := &model.TicketInfo{
Ticketno: ticketno, Ticketno: ticketno,
@@ -82,7 +87,7 @@ func CreateTicket(db *gorm.DB, title, content, contactname, contactphone, source
return ticket, nil 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{}{ updates := map[string]interface{}{
"updatetime": time.Now(), "updatetime": time.Now(),
} }
@@ -101,8 +106,8 @@ func UpdateTicket(db *gorm.DB, ticketid int, title, content, contactname, contac
if category != "" { if category != "" {
updates["category"] = category updates["category"] = category
} }
if priority >= 0 { if priority != nil {
updates["priority"] = priority updates["priority"] = *priority
} }
if handlerid != nil { if handlerid != nil {
updates["handlerid"] = handlerid updates["handlerid"] = handlerid