package repository import ( "context" "strings" app "haixun-backend/internal/library/errors" "haixun-backend/internal/library/errors/code" "haixun-backend/internal/model/content_formula/domain/entity" domrepo "haixun-backend/internal/model/content_formula/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().CreateMany(ctx, []mongo.IndexModel{ { Keys: bson.D{ {Key: "tenant_id", Value: 1}, {Key: "owner_uid", Value: 1}, {Key: "account_id", Value: 1}, {Key: "update_at", Value: -1}, }, }, }) return err } func actorFilter(tenantID, ownerUID, accountID string) bson.M { return bson.M{ "tenant_id": strings.TrimSpace(tenantID), "owner_uid": strings.TrimSpace(ownerUID), "account_id": strings.TrimSpace(accountID), } } func normalizePage(page, pageSize int) (int, int) { if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 20 } if pageSize > 100 { pageSize = 100 } return page, pageSize } func (r *mongoRepository) Create(ctx context.Context, formula *entity.ContentFormula) error { if r.collection == nil { return app.For(code.ThreadsAccount).DBUnavailable("Mongo is not configured") } if formula == nil { return app.For(code.ThreadsAccount).InputMissingRequired("formula is required") } _, err := r.collection.InsertOne(ctx, formula) return err } func (r *mongoRepository) Get(ctx context.Context, tenantID, ownerUID, accountID, formulaID string) (*entity.ContentFormula, error) { if r.collection == nil { return nil, app.For(code.ThreadsAccount).DBUnavailable("Mongo is not configured") } formulaID = strings.TrimSpace(formulaID) if formulaID == "" { return nil, app.For(code.ThreadsAccount).InputMissingRequired("formula_id is required") } filter := actorFilter(tenantID, ownerUID, accountID) filter["_id"] = formulaID var out entity.ContentFormula err := r.collection.FindOne(ctx, filter).Decode(&out) if err != nil { if err == mongo.ErrNoDocuments { return nil, app.For(code.ThreadsAccount).ResNotFound("content formula not found") } return nil, err } return &out, nil } func (r *mongoRepository) Update(ctx context.Context, tenantID, ownerUID, accountID, formulaID string, patch map[string]interface{}) (*entity.ContentFormula, error) { if r.collection == nil { return nil, app.For(code.ThreadsAccount).DBUnavailable("Mongo is not configured") } if len(patch) == 0 { return nil, app.For(code.ThreadsAccount).InputMissingRequired("patch is required") } filter := actorFilter(tenantID, ownerUID, accountID) filter["_id"] = strings.TrimSpace(formulaID) opts := options.FindOneAndUpdate().SetReturnDocument(options.After) var out entity.ContentFormula err := r.collection.FindOneAndUpdate(ctx, filter, bson.M{"$set": patch}, opts).Decode(&out) if err != nil { if err == mongo.ErrNoDocuments { return nil, app.For(code.ThreadsAccount).ResNotFound("content formula not found") } return nil, err } return &out, nil } func (r *mongoRepository) Delete(ctx context.Context, tenantID, ownerUID, accountID, formulaID string) error { if r.collection == nil { return app.For(code.ThreadsAccount).DBUnavailable("Mongo is not configured") } filter := actorFilter(tenantID, ownerUID, accountID) filter["_id"] = strings.TrimSpace(formulaID) res, err := r.collection.DeleteOne(ctx, filter) if err != nil { return err } if res.DeletedCount == 0 { return app.For(code.ThreadsAccount).ResNotFound("content formula not found") } return nil } func (r *mongoRepository) List(ctx context.Context, filter domrepo.ListFilter) (*domrepo.ListResult, error) { if r.collection == nil { return nil, app.For(code.ThreadsAccount).DBUnavailable("Mongo is not configured") } page, pageSize := normalizePage(filter.Page, filter.PageSize) mongoFilter := actorFilter(filter.TenantID, filter.OwnerUID, filter.AccountID) if sourceType := strings.TrimSpace(filter.SourceType); sourceType != "" { mongoFilter["source_type"] = sourceType } if tag := strings.TrimSpace(filter.Tag); tag != "" { mongoFilter["tags"] = tag } total, err := r.collection.CountDocuments(ctx, mongoFilter) if err != nil { return nil, err } opts := options.Find(). SetSort(bson.D{{Key: "update_at", Value: -1}}). SetSkip(int64((page - 1) * pageSize)). SetLimit(int64(pageSize)) cur, err := r.collection.Find(ctx, mongoFilter, opts) if err != nil { return nil, err } defer cur.Close(ctx) var items []entity.ContentFormula if err := cur.All(ctx, &items); err != nil { return nil, err } return &domrepo.ListResult{Items: items, Total: total}, nil }