package repository import ( "context" libmongo "apps/backend/internal/lib/mongo" "apps/backend/internal/module/inspire/domain" "github.com/zeromicro/go-zero/core/stores/mon" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" ) type MonStore struct { trends *mon.Model elements *mon.Model sessions *mon.Model active *mon.Model // inspire_session_active: _id=owner_uid, session_id } func NewMonStore(uri, database string) *MonStore { uri = libmongo.MustMongoURI(uri) return &MonStore{ trends: mon.MustNewModel(uri, database, "inspire_trends"), elements: mon.MustNewModel(uri, database, "inspire_elements"), sessions: mon.MustNewModel(uri, database, "inspire_sessions"), active: mon.MustNewModel(uri, database, "inspire_session_active"), } } type trendBag struct { OwnerUID int64 `bson:"_id"` Items []*domain.TrendItem `bson:"items"` } type activeRef struct { OwnerUID int64 `bson:"_id"` SessionID string `bson:"session_id"` } func (s *MonStore) ListTrends(ctx context.Context, ownerUID int64) ([]*domain.TrendItem, error) { var bag trendBag err := s.trends.FindOne(ctx, &bag, bson.M{"_id": ownerUID}) if err != nil { if err == mon.ErrNotFound { return nil, nil } return nil, err } return bag.Items, nil } func (s *MonStore) SaveTrends(ctx context.Context, ownerUID int64, items []*domain.TrendItem) error { bag := trendBag{OwnerUID: ownerUID, Items: items} _, err := s.trends.ReplaceOne(ctx, bson.M{"_id": ownerUID}, bag, options.Replace().SetUpsert(true)) return err } func (s *MonStore) ListElements(ctx context.Context, ownerUID int64) ([]*domain.Element, error) { var list []*domain.Element err := s.elements.Find(ctx, &list, bson.M{"owner_uid": ownerUID}, options.Find().SetSort(bson.D{{Key: "updated_at", Value: -1}})) return list, err } func (s *MonStore) SaveElement(ctx context.Context, e *domain.Element) error { _, err := s.elements.ReplaceOne(ctx, bson.M{"_id": e.ID}, e, options.Replace().SetUpsert(true)) return err } func (s *MonStore) DeleteElement(ctx context.Context, id string) error { res, err := s.elements.DeleteOne(ctx, bson.M{"_id": id}) if err != nil { return err } if res == 0 { return domain.ErrNotFound } return nil } func (s *MonStore) GetElement(ctx context.Context, id string) (*domain.Element, error) { var e domain.Element err := s.elements.FindOne(ctx, &e, bson.M{"_id": id}) if err != nil { if err == mon.ErrNotFound { return nil, domain.ErrNotFound } return nil, err } return &e, nil } func (s *MonStore) ListSessions(ctx context.Context, ownerUID int64) ([]*domain.Session, error) { var list []*domain.Session err := s.sessions.Find(ctx, &list, bson.M{"owner_uid": ownerUID}, options.Find().SetSort(bson.D{{Key: "updated_at", Value: -1}}).SetLimit(50)) if err != nil { return nil, err } return list, nil } // GetSession — active;無 active/失效則取最近一則;都沒有回 NotFound(usecase 建新)。 func (s *MonStore) GetSession(ctx context.Context, ownerUID int64) (*domain.Session, error) { if id, err := s.GetActiveSessionID(ctx, ownerUID); err == nil && id != "" { if sess, gerr := s.GetSessionByID(ctx, ownerUID, id); gerr == nil { return sess, nil } } // fallback:最近更新 var list []*domain.Session err := s.sessions.Find(ctx, &list, bson.M{"owner_uid": ownerUID}, options.Find().SetSort(bson.D{{Key: "updated_at", Value: -1}}).SetLimit(1)) if err != nil { return nil, err } if len(list) == 0 { return nil, domain.ErrNotFound } _ = s.SetActiveSessionID(ctx, ownerUID, list[0].ID) return list[0], nil } func (s *MonStore) GetSessionByID(ctx context.Context, ownerUID int64, id string) (*domain.Session, error) { var sess domain.Session err := s.sessions.FindOne(ctx, &sess, bson.M{"_id": id, "owner_uid": ownerUID}) if err != nil { if err == mon.ErrNotFound { return nil, domain.ErrNotFound } return nil, err } return &sess, nil } func (s *MonStore) SaveSession(ctx context.Context, sess *domain.Session) error { // 用 _id + owner_uid:多 session 且禁止覆寫別人的對話 _, err := s.sessions.ReplaceOne( ctx, bson.M{"_id": sess.ID, "owner_uid": sess.OwnerUID}, sess, options.Replace().SetUpsert(true), ) return err } func (s *MonStore) DeleteSessionByID(ctx context.Context, ownerUID int64, id string) error { res, err := s.sessions.DeleteOne(ctx, bson.M{"_id": id, "owner_uid": ownerUID}) if err != nil { return err } if res == 0 { return domain.ErrNotFound } return nil } func (s *MonStore) GetActiveSessionID(ctx context.Context, ownerUID int64) (string, error) { var ref activeRef err := s.active.FindOne(ctx, &ref, bson.M{"_id": ownerUID}) if err != nil { if err == mon.ErrNotFound { return "", domain.ErrNotFound } return "", err } return ref.SessionID, nil } func (s *MonStore) SetActiveSessionID(ctx context.Context, ownerUID int64, sessionID string) error { ref := activeRef{OwnerUID: ownerUID, SessionID: sessionID} _, err := s.active.ReplaceOne(ctx, bson.M{"_id": ownerUID}, ref, options.Replace().SetUpsert(true)) return err } var _ domain.Repository = (*MonStore)(nil)