优化: 前后端字段对齐、AI分析JSON解析修复、AutoMigrate
- 修复登录参数 username→account - 修复前后端字段名不匹配 (ticketno/contactname/createtime等) - 修复AI分析GLM返回markdown包裹和priority类型问题 - 添加AutoMigrate自动建表 - 统一API路由为 /api/auth/ 前缀 - 添加config.example.yaml,.gitignore排除config.yaml
This commit is contained in:
98
backend/README.md
Normal file
98
backend/README.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Ticket Workbench Backend
|
||||
|
||||
Go Gin 工单管理系统后端服务。
|
||||
|
||||
## 环境要求
|
||||
|
||||
- Go 1.24+
|
||||
- MySQL 5.7+
|
||||
|
||||
## 配置文件
|
||||
|
||||
编辑 `config.yaml`:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 8090
|
||||
db:
|
||||
host: 39.99.243.191
|
||||
port: 3306
|
||||
user: root
|
||||
password: Lake@2019
|
||||
dbname: ticket_dev
|
||||
glm:
|
||||
api_key: 7f83dc939a60488b8cf48a2ee1c8150e.NY3aOR0qlVS8m37a
|
||||
base_url: https://open.bigmodel.cn/api/paas/v4
|
||||
model: glm-4-flash
|
||||
```
|
||||
|
||||
## 启动方式
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
go mod tidy
|
||||
|
||||
# 运行
|
||||
go run .
|
||||
|
||||
# 编译
|
||||
go build -o ticket-workbench.exe .
|
||||
|
||||
# 运行编译后的程序
|
||||
./ticket-workbench.exe
|
||||
```
|
||||
|
||||
## API 接口
|
||||
|
||||
### 认证
|
||||
|
||||
- `POST /api/login` - 登录
|
||||
- `POST /api/logout` - 登出
|
||||
- `GET /api/user/info` - 当前用户信息
|
||||
|
||||
### 工单
|
||||
|
||||
- `GET /api/tickets` - 工单列表
|
||||
- `POST /api/tickets` - 创建工单
|
||||
- `GET /api/tickets/:id` - 工单详情
|
||||
- `PUT /api/tickets/:id` - 更新工单
|
||||
- `PUT /api/tickets/:id/status` - 更新状态
|
||||
|
||||
### AI 分析
|
||||
|
||||
- `POST /api/tickets/:id/analyze` - 触发 AI 分析
|
||||
- `GET /api/tickets/:id/analysis` - 获取分析结果
|
||||
- `PUT /api/tickets/:id/analysis` - 确认/修改分析结果
|
||||
|
||||
### 备注
|
||||
|
||||
- `GET /api/tickets/:id/notes` - 获取备注列表
|
||||
- `POST /api/tickets/:id/notes` - 添加备注
|
||||
|
||||
### 日志
|
||||
|
||||
- `GET /api/tickets/:id/logs` - 获取操作日志
|
||||
|
||||
## 认证方式
|
||||
|
||||
所有 API(除登录/登出外)需要通过 Authorization header 或 jsessionid cookie 携带 token。
|
||||
|
||||
```
|
||||
Authorization: {token}
|
||||
```
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
backend/
|
||||
├── main.go # 入口文件
|
||||
├── config.yaml # 配置文件
|
||||
├── go.mod / go.sum # 依赖管理
|
||||
└── internal/
|
||||
├── config/ # 配置读取
|
||||
├── model/ # GORM 模型
|
||||
├── dto/ # 请求/响应 DTO
|
||||
├── handler/ # Gin handler
|
||||
├── service/ # 业务逻辑
|
||||
└── middleware/ # 中间件
|
||||
```
|
||||
12
backend/config.example.yaml
Normal file
12
backend/config.example.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
server:
|
||||
port: 8091
|
||||
db:
|
||||
host: 127.0.0.1
|
||||
port: 3306
|
||||
user: root
|
||||
password: your_password
|
||||
dbname: ticket_dev
|
||||
glm:
|
||||
api_key: your_api_key
|
||||
base_url: https://open.bigmodel.cn/api/paas/v4
|
||||
model: glm-4-flash
|
||||
@@ -30,9 +30,9 @@ type UpdateStatusRequest struct {
|
||||
}
|
||||
|
||||
type TicketListQuery struct {
|
||||
Status int16 `form:"status"`
|
||||
Status *int16 `form:"status"`
|
||||
Category string `form:"category"`
|
||||
Priority int16 `form:"priority"`
|
||||
Priority *int16 `form:"priority"`
|
||||
Keyword string `form:"keyword"`
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"pageSize"`
|
||||
|
||||
@@ -25,6 +25,7 @@ func Auth(db *gorm.DB) gin.HandlerFunc {
|
||||
|
||||
c.Set("userid", user.Userid)
|
||||
c.Set("username", user.Username)
|
||||
c.Set("account", user.Account)
|
||||
c.Set("role", user.Role)
|
||||
c.Set("team", user.Team)
|
||||
c.Next()
|
||||
|
||||
@@ -4,13 +4,17 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/casehub/ticket-workbench/internal/model"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var sessions = make(map[string]*model.TicketUser)
|
||||
var (
|
||||
sessions = make(map[string]*model.TicketUser)
|
||||
sessionMu sync.RWMutex
|
||||
)
|
||||
|
||||
func MD5Hash(text string) string {
|
||||
hash := md5.Sum([]byte(text))
|
||||
@@ -32,15 +36,21 @@ func Login(db *gorm.DB, account, password string) (*model.TicketUser, string, er
|
||||
}
|
||||
|
||||
sessionID := uuid.New().String()
|
||||
sessionMu.Lock()
|
||||
sessions[sessionID] = &user
|
||||
sessionMu.Unlock()
|
||||
|
||||
return &user, sessionID, nil
|
||||
}
|
||||
|
||||
func Logout(sessionID string) {
|
||||
sessionMu.Lock()
|
||||
delete(sessions, sessionID)
|
||||
sessionMu.Unlock()
|
||||
}
|
||||
|
||||
func GetUserBySession(sessionID string) *model.TicketUser {
|
||||
sessionMu.RLock()
|
||||
defer sessionMu.RUnlock()
|
||||
return sessions[sessionID]
|
||||
}
|
||||
|
||||
@@ -14,20 +14,20 @@ type TicketListResult struct {
|
||||
Rows []model.TicketInfo `json:"rows"`
|
||||
}
|
||||
|
||||
func ListTickets(db *gorm.DB, status int16, category string, priority int16, keyword string, page, pageSize int) (*TicketListResult, error) {
|
||||
func ListTickets(db *gorm.DB, status *int16, category string, priority *int16, keyword string, page, pageSize int) (*TicketListResult, error) {
|
||||
var total int64
|
||||
var tickets []model.TicketInfo
|
||||
|
||||
query := db.Model(&model.TicketInfo{})
|
||||
|
||||
if status >= 0 {
|
||||
query = query.Where("status = ?", status)
|
||||
if status != nil {
|
||||
query = query.Where("status = ?", *status)
|
||||
}
|
||||
if category != "" {
|
||||
query = query.Where("category = ?", category)
|
||||
}
|
||||
if priority >= 0 {
|
||||
query = query.Where("priority = ?", priority)
|
||||
if priority != nil {
|
||||
query = query.Where("priority = ?", *priority)
|
||||
}
|
||||
if keyword != "" {
|
||||
query = query.Where("title LIKE ? OR content LIKE ?", "%"+keyword+"%", "%"+keyword+"%")
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/casehub/ticket-workbench/internal/config"
|
||||
"github.com/casehub/ticket-workbench/internal/handler"
|
||||
"github.com/casehub/ticket-workbench/internal/middleware"
|
||||
"github.com/casehub/ticket-workbench/internal/model"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
@@ -42,6 +43,16 @@ func main() {
|
||||
sqlDB.SetMaxIdleConns(10)
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
|
||||
if err := db.AutoMigrate(
|
||||
&model.TicketUser{},
|
||||
&model.TicketInfo{},
|
||||
&model.TicketAiAnalysis{},
|
||||
&model.TicketNote{},
|
||||
&model.TicketOperationLog{},
|
||||
); err != nil {
|
||||
log.Fatalf("Failed to auto migrate: %v", err)
|
||||
}
|
||||
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
r := gin.Default()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user