| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| package model |
|
|
| import ( |
| "errors" |
| "time" |
| ) |
|
|
| type Message struct { |
| Id int `json:"id" gorm:"primaryKey"` |
| Title string `json:"title" gorm:"not null"` |
| Content string `json:"content" gorm:"type:text;not null"` |
| Format string `json:"format" gorm:"default:markdown"` |
| CreatedAt time.Time `json:"created_at"` |
| UpdatedAt time.Time `json:"updated_at"` |
| CreatedBy int `json:"created_by"` |
| } |
|
|
| type UserMessage struct { |
| Id int `json:"id" gorm:"primaryKey"` |
| UserId int `json:"user_id" gorm:"not null;index;uniqueIndex:idx_user_message"` |
| MessageId int `json:"message_id" gorm:"not null;index;uniqueIndex:idx_user_message"` |
| ReadAt *time.Time `json:"read_at"` |
| CreatedAt time.Time `json:"created_at"` |
| Message Message `json:"message" gorm:"foreignKey:MessageId"` |
| } |
|
|
| |
|
|
| func (message *Message) Insert() error { |
| return DB.Create(message).Error |
| } |
|
|
| func (message *Message) Update() error { |
| return DB.Save(message).Error |
| } |
|
|
| func (message *Message) Delete() error { |
| if message.Id == 0 { |
| return errors.New("message id is empty") |
| } |
| |
| |
| tx := DB.Begin() |
| if tx.Error != nil { |
| return tx.Error |
| } |
| defer tx.Rollback() |
|
|
| |
| if err := tx.Where("message_id = ?", message.Id).Delete(&UserMessage{}).Error; err != nil { |
| return err |
| } |
|
|
| |
| if err := tx.Delete(message).Error; err != nil { |
| return err |
| } |
|
|
| return tx.Commit().Error |
| } |
|
|
| func GetMessageById(id int) (*Message, error) { |
| if id == 0 { |
| return nil, errors.New("message id is empty") |
| } |
| var message Message |
| err := DB.First(&message, "id = ?", id).Error |
| return &message, err |
| } |
|
|
| func GetAllMessages(startIdx int, num int, keyword string) ([]*Message, int64, error) { |
| var messages []*Message |
| var total int64 |
|
|
| |
| tx := DB.Begin() |
| if tx.Error != nil { |
| return nil, 0, tx.Error |
| } |
| defer tx.Rollback() |
|
|
| |
| query := tx.Model(&Message{}) |
| if keyword != "" { |
| query = query.Where("title LIKE ? OR content LIKE ?", "%"+keyword+"%", "%"+keyword+"%") |
| } |
|
|
| |
| if err := query.Count(&total).Error; err != nil { |
| return nil, 0, err |
| } |
|
|
| |
| if err := query.Order("created_at desc").Limit(num).Offset(startIdx).Find(&messages).Error; err != nil { |
| return nil, 0, err |
| } |
|
|
| if err := tx.Commit().Error; err != nil { |
| return nil, 0, err |
| } |
|
|
| return messages, total, nil |
| } |
|
|
| func SearchMessages(startIdx int, num int, keyword string, startDate string, endDate string) ([]*Message, int64, error) { |
| var messages []*Message |
| var total int64 |
|
|
| |
| tx := DB.Begin() |
| if tx.Error != nil { |
| return nil, 0, tx.Error |
| } |
| defer tx.Rollback() |
|
|
| |
| query := tx.Model(&Message{}) |
| |
| |
| if keyword != "" { |
| query = query.Where("title LIKE ? OR content LIKE ?", "%"+keyword+"%", "%"+keyword+"%") |
| } |
| |
| |
| if startDate != "" { |
| query = query.Where("created_at >= ?", startDate) |
| } |
| if endDate != "" { |
| query = query.Where("created_at <= ?", endDate) |
| } |
|
|
| |
| if err := query.Count(&total).Error; err != nil { |
| return nil, 0, err |
| } |
|
|
| |
| if err := query.Order("created_at desc").Limit(num).Offset(startIdx).Find(&messages).Error; err != nil { |
| return nil, 0, err |
| } |
|
|
| if err := tx.Commit().Error; err != nil { |
| return nil, 0, err |
| } |
|
|
| return messages, total, nil |
| } |
|
|
| |
|
|
| func (userMessage *UserMessage) Insert() error { |
| return DB.Create(userMessage).Error |
| } |
|
|
| func (userMessage *UserMessage) MarkAsRead() error { |
| if userMessage.Id == 0 { |
| return errors.New("user message id is empty") |
| } |
| now := time.Now() |
| userMessage.ReadAt = &now |
| return DB.Model(userMessage).Update("read_at", now).Error |
| } |
|
|
| func GetUserMessages(userId int, startIdx int, num int) ([]*UserMessage, int64, error) { |
| if userId == 0 { |
| return nil, 0, errors.New("user id is empty") |
| } |
|
|
| var userMessages []*UserMessage |
| var total int64 |
|
|
| |
| tx := DB.Begin() |
| if tx.Error != nil { |
| return nil, 0, tx.Error |
| } |
| defer tx.Rollback() |
|
|
| |
| if err := tx.Model(&UserMessage{}).Where("user_id = ?", userId).Count(&total).Error; err != nil { |
| return nil, 0, err |
| } |
|
|
| |
| if err := tx.Preload("Message").Where("user_id = ?", userId). |
| Order("created_at desc").Limit(num).Offset(startIdx).Find(&userMessages).Error; err != nil { |
| return nil, 0, err |
| } |
|
|
| if err := tx.Commit().Error; err != nil { |
| return nil, 0, err |
| } |
|
|
| return userMessages, total, nil |
| } |
|
|
| func GetUserMessageById(userId int, messageId int) (*UserMessage, error) { |
| if userId == 0 || messageId == 0 { |
| return nil, errors.New("user id or message id is empty") |
| } |
|
|
| var userMessage UserMessage |
| err := DB.Preload("Message").Where("user_id = ? AND message_id = ?", userId, messageId).First(&userMessage).Error |
| return &userMessage, err |
| } |
|
|
| func GetUnreadMessageCount(userId int) (int64, error) { |
| if userId == 0 { |
| return 0, errors.New("user id is empty") |
| } |
|
|
| var count int64 |
| err := DB.Model(&UserMessage{}).Where("user_id = ? AND read_at IS NULL", userId).Count(&count).Error |
| return count, err |
| } |
|
|
| func CreateMessageForUsers(message *Message, userIds []int) error { |
| if len(userIds) == 0 { |
| return errors.New("no users specified") |
| } |
|
|
| |
| tx := DB.Begin() |
| if tx.Error != nil { |
| return tx.Error |
| } |
| defer tx.Rollback() |
|
|
| |
| if err := tx.Create(message).Error; err != nil { |
| return err |
| } |
|
|
| |
| userMessages := make([]UserMessage, len(userIds)) |
| for i, userId := range userIds { |
| userMessages[i] = UserMessage{ |
| UserId: userId, |
| MessageId: message.Id, |
| CreatedAt: time.Now(), |
| } |
| } |
|
|
| |
| if err := tx.Create(&userMessages).Error; err != nil { |
| return err |
| } |
|
|
| return tx.Commit().Error |
| } |
|
|
| func MarkUserMessageAsRead(userId int, messageId int) error { |
| if userId == 0 || messageId == 0 { |
| return errors.New("user id or message id is empty") |
| } |
|
|
| now := time.Now() |
| return DB.Model(&UserMessage{}). |
| Where("user_id = ? AND message_id = ?", userId, messageId). |
| Update("read_at", now).Error |
| } |
|
|
| |
| func GetMessageStats(messageId int) (map[string]interface{}, error) { |
| if messageId == 0 { |
| return nil, errors.New("message id is empty") |
| } |
|
|
| var totalRecipients int64 |
| var readCount int64 |
|
|
| |
| tx := DB.Begin() |
| if tx.Error != nil { |
| return nil, tx.Error |
| } |
| defer tx.Rollback() |
|
|
| |
| if err := tx.Model(&UserMessage{}).Where("message_id = ?", messageId).Count(&totalRecipients).Error; err != nil { |
| return nil, err |
| } |
|
|
| |
| if err := tx.Model(&UserMessage{}).Where("message_id = ? AND read_at IS NOT NULL", messageId).Count(&readCount).Error; err != nil { |
| return nil, err |
| } |
|
|
| if err := tx.Commit().Error; err != nil { |
| return nil, err |
| } |
|
|
| stats := map[string]interface{}{ |
| "total_recipients": totalRecipients, |
| "read_count": readCount, |
| "unread_count": totalRecipients - readCount, |
| "read_rate": float64(readCount) / float64(totalRecipients) * 100, |
| } |
|
|
| return stats, nil |
| } |
|
|
| |
| func GetMessageRecipients(messageId int) ([]map[string]interface{}, error) { |
| if messageId == 0 { |
| return nil, errors.New("message id is empty") |
| } |
|
|
| var results []map[string]interface{} |
| |
| |
| err := DB.Table("user_messages"). |
| Select("user_messages.user_id, users.username, users.display_name, user_messages.read_at"). |
| Joins("JOIN users ON user_messages.user_id = users.id"). |
| Where("user_messages.message_id = ?", messageId). |
| Order("user_messages.created_at DESC"). |
| Scan(&results).Error |
| |
| return results, err |
| } |