|
|
package database |
|
|
|
|
|
import ( |
|
|
"encoding/json" |
|
|
"fmt" |
|
|
"time" |
|
|
|
|
|
"gorm.io/datatypes" |
|
|
"gorm.io/gorm" |
|
|
) |
|
|
|
|
|
|
|
|
type User struct { |
|
|
Username string `gorm:"primaryKey;column:username" json:"username"` |
|
|
CreatedAt int64 `gorm:"column:created_at;not null" json:"created_at"` |
|
|
UpdatedAt int64 `gorm:"column:updated_at;not null" json:"updated_at"` |
|
|
} |
|
|
|
|
|
|
|
|
type Session struct { |
|
|
ID string `gorm:"primaryKey;column:id" json:"id"` |
|
|
Username string `gorm:"column:username;not null" json:"username"` |
|
|
Title string `gorm:"column:title" json:"title"` |
|
|
TaskType string `gorm:"column:task_type;not null" json:"task_type"` |
|
|
Content string `gorm:"column:content;not null" json:"content"` |
|
|
Params datatypes.JSON `gorm:"column:params" json:"params"` |
|
|
Attachments datatypes.JSON `gorm:"column:attachments" json:"attachments"` |
|
|
Status string `gorm:"column:status;not null;default:'todo'" json:"status"` |
|
|
AssignedAgent string `gorm:"column:assigned_agent" json:"assigned_agent"` |
|
|
CountryIsoCode string `gorm:"column:contry_iso_code" json:"countryIsoCode"` |
|
|
StartedAt *int64 `gorm:"column:started_at" json:"started_at"` |
|
|
CompletedAt *int64 `gorm:"column:completed_at" json:"completed_at"` |
|
|
CreatedAt int64 `gorm:"column:created_at;not null" json:"created_at"` |
|
|
UpdatedAt int64 `gorm:"column:updated_at;not null" json:"updated_at"` |
|
|
|
|
|
|
|
|
User User `gorm:"foreignKey:Username" json:"user"` |
|
|
Messages []TaskMessage `gorm:"foreignKey:SessionID" json:"messages"` |
|
|
Share bool `gorm:"column:share;not null;default:false" json:"share"` |
|
|
} |
|
|
|
|
|
|
|
|
type TaskMessage struct { |
|
|
ID string `gorm:"primaryKey;column:id" json:"id"` |
|
|
SessionID string `gorm:"column:session_id;not null" json:"session_id"` |
|
|
Type string `gorm:"column:type;not null" json:"type"` |
|
|
EventData datatypes.JSON `gorm:"column:event_data;not null" json:"event_data"` |
|
|
Timestamp int64 `gorm:"column:timestamp;not null" json:"timestamp"` |
|
|
CreatedAt int64 `gorm:"column:created_at;not null" json:"created_at"` |
|
|
|
|
|
|
|
|
Session Session `gorm:"foreignKey:SessionID" json:"session"` |
|
|
} |
|
|
|
|
|
|
|
|
type TaskStore struct { |
|
|
db *gorm.DB |
|
|
} |
|
|
|
|
|
|
|
|
func NewTaskStore(db *gorm.DB) *TaskStore { |
|
|
return &TaskStore{db: db} |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) Init() error { |
|
|
return s.db.AutoMigrate(&User{}, &Session{}, &TaskMessage{}) |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) CreateUser(user *User) error { |
|
|
now := time.Now().UnixMilli() |
|
|
user.CreatedAt = now |
|
|
user.UpdatedAt = now |
|
|
return s.db.Create(user).Error |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) ResetRunningTasks() error { |
|
|
return s.db.Model(&Session{}).Where("status = 'doing' or status = 'failed'").Updates(map[string]interface{}{ |
|
|
"status": "error", |
|
|
"updated_at": time.Now().UnixMilli(), |
|
|
}).Error |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) GetUser(username string) (*User, error) { |
|
|
var user User |
|
|
err := s.db.First(&user, "username = ?", username).Error |
|
|
if err != nil { |
|
|
return nil, err |
|
|
} |
|
|
return &user, nil |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) CreateSession(session *Session) error { |
|
|
now := time.Now().UnixMilli() |
|
|
session.CreatedAt = now |
|
|
session.UpdatedAt = now |
|
|
return s.db.Create(session).Error |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) GetSession(id string) (*Session, error) { |
|
|
var session Session |
|
|
err := s.db.Preload("User").Preload("Messages").First(&session, "id = ?", id).Error |
|
|
if err != nil { |
|
|
return nil, err |
|
|
} |
|
|
return &session, nil |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) SetShare(sessionID string, share bool) error { |
|
|
return s.db.Model(&Session{}).Where("id = ?", sessionID).Update("share", share).Error |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) UpdateSessionStatus(id string, status string) error { |
|
|
now := time.Now().UnixMilli() |
|
|
updates := map[string]interface{}{ |
|
|
"status": status, |
|
|
"updated_at": now, |
|
|
} |
|
|
|
|
|
if status == "doing" { |
|
|
updates["started_at"] = &now |
|
|
} else if status == "done" { |
|
|
updates["completed_at"] = &now |
|
|
} |
|
|
|
|
|
return s.db.Model(&Session{}).Where("id = ?", id).Updates(updates).Error |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) UpdateSessionAssignedAgent(sessionID string, agentID string) error { |
|
|
now := time.Now().UnixMilli() |
|
|
updates := map[string]interface{}{ |
|
|
"assigned_agent": agentID, |
|
|
"status": "doing", |
|
|
"started_at": &now, |
|
|
} |
|
|
|
|
|
return s.db.Model(&Session{}).Where("id = ?", sessionID).Updates(updates).Error |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) UpdateSession(sessionID string, updates map[string]interface{}) error { |
|
|
|
|
|
updates["updated_at"] = time.Now().UnixMilli() |
|
|
return s.db.Model(&Session{}).Where("id = ?", sessionID).Updates(updates).Error |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) DeleteSession(sessionID string) error { |
|
|
return s.db.Delete(&Session{}, "id = ?", sessionID).Error |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) DeleteSessionMessages(sessionID string) error { |
|
|
return s.db.Where("session_id = ?", sessionID).Delete(&TaskMessage{}).Error |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) DeleteSessionWithMessages(sessionID string) error { |
|
|
return s.db.Transaction(func(tx *gorm.DB) error { |
|
|
|
|
|
if err := tx.Where("session_id = ?", sessionID).Delete(&TaskMessage{}).Error; err != nil { |
|
|
return fmt.Errorf("删除会话消息失败: %v", err) |
|
|
} |
|
|
|
|
|
|
|
|
if err := tx.Delete(&Session{}, "id = ?", sessionID).Error; err != nil { |
|
|
return fmt.Errorf("删除会话记录失败: %v", err) |
|
|
} |
|
|
|
|
|
return nil |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) CreateTaskMessage(message *TaskMessage) error { |
|
|
now := time.Now().UnixMilli() |
|
|
message.CreatedAt = now |
|
|
return s.db.Create(message).Error |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) GetSessionMessages(sessionID string) ([]*TaskMessage, error) { |
|
|
var messages []*TaskMessage |
|
|
err := s.db.Where("session_id = ?", sessionID).Order("timestamp ASC").Find(&messages).Error |
|
|
if err != nil { |
|
|
return nil, err |
|
|
} |
|
|
return messages, nil |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) GetUserSessions(username string) ([]*Session, error) { |
|
|
var sessions []*Session |
|
|
err := s.db.Where("username = ?", username).Order("created_at DESC").Find(&sessions).Error |
|
|
if err != nil { |
|
|
return nil, err |
|
|
} |
|
|
return sessions, nil |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) GetUserSessionsByType(username string, taskType string) ([]*Session, error) { |
|
|
query := s.db.Where("username = ?", username) |
|
|
|
|
|
|
|
|
if taskType != "" { |
|
|
query = query.Where("task_type = ?", taskType) |
|
|
} |
|
|
|
|
|
var sessions []*Session |
|
|
err := query.Order("created_at DESC").Find(&sessions).Error |
|
|
if err != nil { |
|
|
return nil, err |
|
|
} |
|
|
return sessions, nil |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) StoreEvent(id string, sessionID string, eventType string, eventData interface{}, timestamp int64) error { |
|
|
|
|
|
eventJSON, err := json.Marshal(eventData) |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
|
|
|
message := &TaskMessage{ |
|
|
ID: id, |
|
|
SessionID: sessionID, |
|
|
Type: eventType, |
|
|
EventData: datatypes.JSON(eventJSON), |
|
|
Timestamp: timestamp, |
|
|
} |
|
|
|
|
|
return s.CreateTaskMessage(message) |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) GetSessionEvents(sessionID string) ([]*TaskMessage, error) { |
|
|
return s.GetSessionMessages(sessionID) |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) GetSessionEventsByType(sessionID string, eventType string) ([]*TaskMessage, error) { |
|
|
var messages []*TaskMessage |
|
|
err := s.db.Where("session_id = ? AND type = ?", sessionID, eventType).Order("timestamp ASC").Find(&messages).Error |
|
|
if err != nil { |
|
|
return nil, err |
|
|
} |
|
|
return messages, nil |
|
|
} |
|
|
|
|
|
|
|
|
func (s *TaskStore) SearchUserSessionsSimple(username string, searchParams SimpleSearchParams) ([]*Session, int64, error) { |
|
|
query := s.db.Model(&Session{}).Where("username = ?", username) |
|
|
|
|
|
|
|
|
if searchParams.TaskType != "" { |
|
|
query = query.Where("task_type = ?", searchParams.TaskType) |
|
|
} |
|
|
|
|
|
|
|
|
if searchParams.Query != "" { |
|
|
query = query.Where("title LIKE ? OR content LIKE ? OR task_type LIKE ?", |
|
|
"%"+searchParams.Query+"%", |
|
|
"%"+searchParams.Query+"%", |
|
|
"%"+searchParams.Query+"%") |
|
|
} |
|
|
|
|
|
|
|
|
var total int64 |
|
|
if err := query.Count(&total).Error; err != nil { |
|
|
return nil, 0, err |
|
|
} |
|
|
|
|
|
|
|
|
var sessions []*Session |
|
|
err := query.Order("created_at DESC"). |
|
|
Offset((searchParams.Page - 1) * searchParams.PageSize). |
|
|
Limit(searchParams.PageSize). |
|
|
Find(&sessions).Error |
|
|
|
|
|
if err != nil { |
|
|
return nil, 0, err |
|
|
} |
|
|
|
|
|
return sessions, total, nil |
|
|
} |
|
|
|
|
|
|
|
|
type SimpleSearchParams struct { |
|
|
Query string `json:"query"` |
|
|
TaskType string `json:"task_type"` |
|
|
Page int `json:"page"` |
|
|
PageSize int `json:"page_size"` |
|
|
} |
|
|
|
|
|
|
|
|
func generateMessageID() string { |
|
|
return time.Now().Format("20060102150405") + "_" + fmt.Sprintf("%d", time.Now().UnixNano()) |
|
|
} |
|
|
|