package repository import ( "errors" "github.com/jmoiron/sqlx" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" "time" "git.urec56.ru/urec/chat_back_go/internal/database" "git.urec56.ru/urec/chat_back_go/internal/domain" "git.urec56.ru/urec/chat_back_go/internal/logger" ) type messageRepository struct { db *sqlx.DB mc *mongo.Collection l *logger.Logger } type SendMessage struct { ID domain.UUID `bson:"id"` Message string `bson:"message,omitempty"` ImageUrl string `bson:"image_url,omitempty"` ChatID int `bson:"chat_id"` UserID int `bson:"user_id"` CreatedAt time.Time `bson:"created_at"` AnswerID domain.UUID `bson:"answer_id,omitempty"` AnswerMessage string `bson:"answer_message,omitempty"` AnswerImageUrl string `bson:"answer_image_url,omitempty"` Visibility bool `bson:"visibility"` } type EditMessage struct { ID domain.UUID Message, ImageUrl string } type PinMessage struct { ChatID, UserID int MessageID domain.UUID } type UnpinMessage struct { ChatID int MessageID domain.UUID } type GetSome struct { ChatID, Offset, Limit int } func newMessageRepo(db *sqlx.DB, mdb *mongo.Database, l *logger.Logger) *messageRepository { return &messageRepository{db: db, mc: mdb.Collection("message"), l: l} } func (r *messageRepository) Get(ID domain.UUID) (domain.RawMessage, error) { var msg domain.RawMessage res := r.mc.FindOne(nil, bson.M{"id": ID}) if err := res.Decode(&msg); err != nil { r.l.Errorf("getting message: %s", err) return domain.RawMessage{}, domain.InternalServerError } return msg, nil } func (r *messageRepository) GetByIDs(IDs []domain.UUID) ([]domain.RawMessage, error) { var messages []domain.RawMessage res, err := r.mc.Find(nil, bson.M{"visibility": true, "id": bson.M{"$in": IDs}}) if err != nil { r.l.Errorf("getting messages by ids: %s", err) return nil, domain.InternalServerError } if err = res.All(nil, &messages); err != nil { r.l.Errorf("decoding messages by ids: %s", err) return nil, domain.InternalServerError } return messages, nil } func (r *messageRepository) GetSome(messagesInfo GetSome) ([]domain.RawMessage, error) { var messages []domain.RawMessage findOptions := options.Find().SetSort(bson.M{"created_at": -1}).SetLimit(int64(messagesInfo.Limit)).SetSkip(int64(messagesInfo.Offset)) res, err := r.mc.Find(nil, bson.M{"visibility": true, "chat_id": messagesInfo.ChatID}, findOptions) if err != nil { r.l.Errorf("getting some messages: %s", err) return nil, domain.InternalServerError } if err = res.All(nil, &messages); err != nil { r.l.Errorf("decoding some messages: %s", err) return nil, domain.InternalServerError } return messages, nil } func (r *messageRepository) Send(message SendMessage) (domain.RawMessage, error) { message.ID = domain.GenerateUUID() message.CreatedAt = time.Now() message.Visibility = true _, err := r.mc.InsertOne(nil, message) if err != nil { r.l.Errorf("inserting message: %s", err) return domain.RawMessage{}, domain.InternalServerError } return domain.RawMessage{ ID: message.ID, Message: message.Message, ImageUrl: message.ImageUrl, ChatID: message.ChatID, UserID: message.UserID, CreatedAt: message.CreatedAt, AnswerID: message.AnswerID, AnswerMessage: message.AnswerMessage, AnswerImageUrl: message.AnswerImageUrl, }, nil } func (r *messageRepository) Delete(ID domain.UUID) error { if _, err := r.mc.UpdateOne(nil, bson.M{"id": ID}, bson.M{"$set": bson.M{"visibility": false}}); err != nil { r.l.Errorf("deleting message: %s", err) return domain.InternalServerError } _, err := r.mc.UpdateMany(nil, bson.M{"answer_id": ID}, bson.M{"$set": bson.M{"answer_message": nil, "answer_image_url": nil}}) if err != nil { r.l.Errorf("deleting message: %s", err) return domain.InternalServerError } return nil } func (r *messageRepository) Edit(newMessage EditMessage) error { _, err := r.mc.UpdateOne(nil, bson.M{"id": newMessage.ID}, bson.M{"$set": bson.M{"message": newMessage.Message, "image_url": newMessage.ImageUrl}}) if err != nil { r.l.Errorf("edditing message: %s", err) return domain.InternalServerError } _, err = r.mc.UpdateMany(nil, bson.M{"answer_id": newMessage.ID}, bson.M{"$set": bson.M{"answer_message": newMessage.Message, "answer_image_url": newMessage.ImageUrl}}) if err != nil { r.l.Errorf("edditing message: %s", err) return domain.InternalServerError } return nil } func (r *messageRepository) Pin(msg PinMessage) error { query := `INSERT INTO pinned_message (chat_id, message_id, user_id) VALUES ($1, $2, $3)` if _, err := r.db.Exec(query, msg.ChatID, msg.MessageID, msg.UserID); err != nil { if errors.Is(err, database.IntegrityError) { return domain.MessageAlreadyPinnedError } r.l.Errorf("pining message: %s", err) return domain.InternalServerError } return nil } func (r *messageRepository) Unpin(msg UnpinMessage) error { query := `DELETE FROM pinned_message WHERE chat_id = $1 AND message_id = $2` if _, err := r.db.Exec(query, msg.ChatID, msg.MessageID); err != nil { r.l.Errorf("unpining message: %s", err) return domain.InternalServerError } return nil } func (r *messageRepository) GetPinnedIDs(chatID int) ([]domain.UUID, error) { var IDs []domain.UUID query := `SELECT message_id FROM pinned_message WHERE chat_id = $1` if err := r.db.Select(&IDs, query, chatID); err != nil { r.l.Errorf("getting pinned ids: %s", err) return []domain.UUID{}, domain.InternalServerError } return IDs, nil }