package repository import ( "context" "strings" app "haixun-backend/internal/library/errors" "haixun-backend/internal/library/errors/code" "haixun-backend/internal/model/copy_draft/domain/entity" domrepo "haixun-backend/internal/model/copy_draft/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: "persona_id", Value: 1}, {Key: "create_at", Value: -1}, }, }, { Keys: bson.D{ {Key: "tenant_id", Value: 1}, {Key: "owner_uid", Value: 1}, {Key: "persona_id", Value: 1}, {Key: "copy_mission_id", Value: 1}, {Key: "sort_order", Value: 1}, }, }, }) return err } func personaFilter(tenantID, ownerUID, personaID string) bson.M { return bson.M{ "tenant_id": strings.TrimSpace(tenantID), "owner_uid": strings.TrimSpace(ownerUID), "persona_id": strings.TrimSpace(personaID), } } func (r *mongoRepository) CreateMany(ctx context.Context, drafts []*entity.CopyDraft) error { if r.collection == nil { return app.For(code.Persona).DBUnavailable("Mongo is not configured") } if len(drafts) == 0 { return nil } docs := make([]any, 0, len(drafts)) for _, draft := range drafts { if draft == nil { continue } docs = append(docs, draft) } if len(docs) == 0 { return nil } _, err := r.collection.InsertMany(ctx, docs) return err } func (r *mongoRepository) Create(ctx context.Context, draft *entity.CopyDraft) error { if r.collection == nil { return app.For(code.Persona).DBUnavailable("Mongo is not configured") } if draft == nil { return app.For(code.Persona).InputMissingRequired("draft is required") } _, err := r.collection.InsertOne(ctx, draft) return err } func (r *mongoRepository) List(ctx context.Context, tenantID, ownerUID, personaID string, limit int) ([]entity.CopyDraft, error) { if r.collection == nil { return nil, app.For(code.Persona).DBUnavailable("Mongo is not configured") } if limit <= 0 { limit = 50 } if limit > 200 { limit = 200 } opts := options.Find(). SetSort(bson.D{{Key: "sort_order", Value: 1}, {Key: "create_at", Value: -1}}). SetLimit(int64(limit)) cur, err := r.collection.Find(ctx, personaFilter(tenantID, ownerUID, personaID), opts) if err != nil { return nil, err } defer cur.Close(ctx) var out []entity.CopyDraft if err := cur.All(ctx, &out); err != nil { return nil, err } return out, nil } func (r *mongoRepository) Get(ctx context.Context, tenantID, ownerUID, personaID, draftID string) (*entity.CopyDraft, error) { if r.collection == nil { return nil, app.For(code.Persona).DBUnavailable("Mongo is not configured") } draftID = strings.TrimSpace(draftID) if draftID == "" { return nil, app.For(code.Persona).InputMissingRequired("draft_id is required") } filter := personaFilter(tenantID, ownerUID, personaID) filter["_id"] = draftID var out entity.CopyDraft err := r.collection.FindOne(ctx, filter).Decode(&out) if err != nil { if err == mongo.ErrNoDocuments { return nil, app.For(code.Persona).ResNotFound("copy draft not found") } return nil, err } return &out, nil } func (r *mongoRepository) Update(ctx context.Context, tenantID, ownerUID, personaID, draftID string, patch map[string]interface{}) (*entity.CopyDraft, error) { if r.collection == nil { return nil, app.For(code.Persona).DBUnavailable("Mongo is not configured") } if len(patch) == 0 { return nil, app.For(code.Persona).InputMissingRequired("patch is required") } draftID = strings.TrimSpace(draftID) filter := personaFilter(tenantID, ownerUID, personaID) filter["_id"] = draftID opts := options.FindOneAndUpdate().SetReturnDocument(options.After) var out entity.CopyDraft 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.Persona).ResNotFound("copy draft not found") } return nil, err } return &out, nil } func (r *mongoRepository) DeleteByMission(ctx context.Context, tenantID, ownerUID, personaID, missionID string) error { if r.collection == nil { return app.For(code.Persona).DBUnavailable("Mongo is not configured") } missionID = strings.TrimSpace(missionID) if missionID == "" { return app.For(code.Persona).InputMissingRequired("copy_mission_id is required") } filter := personaFilter(tenantID, ownerUID, personaID) filter["copy_mission_id"] = missionID _, err := r.collection.DeleteMany(ctx, filter) return err } func (r *mongoRepository) DeleteByMissionAndType(ctx context.Context, tenantID, ownerUID, personaID, missionID, draftType string) error { if r.collection == nil { return app.For(code.Persona).DBUnavailable("Mongo is not configured") } missionID = strings.TrimSpace(missionID) draftType = strings.TrimSpace(draftType) if missionID == "" || draftType == "" { return app.For(code.Persona).InputMissingRequired("copy_mission_id and draft_type are required") } filter := personaFilter(tenantID, ownerUID, personaID) filter["copy_mission_id"] = missionID filter["draft_type"] = draftType _, err := r.collection.DeleteMany(ctx, filter) return err } func (r *mongoRepository) ListByMission(ctx context.Context, tenantID, ownerUID, personaID, missionID string, limit int) ([]entity.CopyDraft, error) { if r.collection == nil { return nil, app.For(code.Persona).DBUnavailable("Mongo is not configured") } missionID = strings.TrimSpace(missionID) if missionID == "" { return nil, app.For(code.Persona).InputMissingRequired("copy_mission_id is required") } if limit <= 0 { limit = 50 } if limit > 200 { limit = 200 } filter := personaFilter(tenantID, ownerUID, personaID) filter["copy_mission_id"] = missionID opts := options.Find(). SetSort(bson.D{{Key: "sort_order", Value: 1}, {Key: "create_at", Value: -1}}). SetLimit(int64(limit)) cur, err := r.collection.Find(ctx, filter, opts) if err != nil { return nil, err } defer cur.Close(ctx) var out []entity.CopyDraft if err := cur.All(ctx, &out); err != nil { return nil, err } return out, nil }