package repository import ( "context" "haixun-backend/internal/library/clock" app "haixun-backend/internal/library/errors" "haixun-backend/internal/library/errors/code" "haixun-backend/internal/model/setting/domain/entity" domrepo "haixun-backend/internal/model/setting/domain/repository" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) type mongoRepository struct { collection *mongo.Collection } func NewMongoRepository(db *mongo.Database) domrepo.Repository { if db == nil { return &mongoRepository{} } return &mongoRepository{collection: db.Collection(entity.CollectionName)} } func (r *mongoRepository) EnsureIndexes(ctx context.Context) error { if r.collection == nil { return nil } _, err := r.collection.Indexes().CreateOne(ctx, mongo.IndexModel{ Keys: bson.D{{Key: "scope", Value: 1}, {Key: "scope_id", Value: 1}, {Key: "key", Value: 1}}, Options: options.Index().SetUnique(true), }) return err } func (r *mongoRepository) List(ctx context.Context, scope, scopeID string, offset, limit int64) ([]*entity.Setting, int64, error) { if r.collection == nil { return nil, 0, app.For(code.Setting).DBUnavailable("Mongo is not configured") } filter := bson.M{"scope": scope, "scope_id": scopeID} total, err := r.collection.CountDocuments(ctx, filter) if err != nil { return nil, 0, err } cursor, err := r.collection.Find( ctx, filter, options.Find().SetSort(bson.D{{Key: "key", Value: 1}}).SetSkip(offset).SetLimit(limit), ) if err != nil { return nil, 0, err } defer cursor.Close(ctx) var items []*entity.Setting if err := cursor.All(ctx, &items); err != nil { return nil, 0, err } return items, total, nil } func (r *mongoRepository) Find(ctx context.Context, scope, scopeID, key string) (*entity.Setting, error) { if r.collection == nil { return nil, app.For(code.Setting).DBUnavailable("Mongo is not configured") } var item entity.Setting err := r.collection.FindOne(ctx, identityFilter(scope, scopeID, key)).Decode(&item) if err == mongo.ErrNoDocuments { return nil, app.For(code.Setting).ResNotFound("setting not found") } if err != nil { return nil, err } return &item, nil } func (r *mongoRepository) Upsert(ctx context.Context, setting *entity.Setting) (*entity.Setting, error) { if r.collection == nil { return nil, app.For(code.Setting).DBUnavailable("Mongo is not configured") } now := clock.NowUnixNano() setting.UpdateAt = now if setting.CreateAt == 0 { setting.CreateAt = now } if setting.Version <= 0 { setting.Version = 1 } after := options.After result := r.collection.FindOneAndUpdate( ctx, identityFilter(setting.Scope, setting.ScopeID, setting.Key), bson.M{ "$set": bson.M{ "scope": setting.Scope, "scope_id": setting.ScopeID, "key": setting.Key, "value": setting.Value, "version": setting.Version, "update_at": setting.UpdateAt, }, "$setOnInsert": bson.M{"create_at": setting.CreateAt}, }, &options.FindOneAndUpdateOptions{ Upsert: ptr(true), ReturnDocument: &after, }, ) var saved entity.Setting if err := result.Decode(&saved); err != nil { return nil, err } return &saved, nil } func (r *mongoRepository) Delete(ctx context.Context, scope, scopeID, key string) error { if r.collection == nil { return app.For(code.Setting).DBUnavailable("Mongo is not configured") } _, err := r.collection.DeleteOne(ctx, identityFilter(scope, scopeID, key)) return err } func identityFilter(scope, scopeID, key string) bson.M { return bson.M{"scope": scope, "scope_id": scopeID, "key": key} } func ptr[T any](v T) *T { return &v }