package repository import ( "app-cloudep-member-server/pkg/domain" "app-cloudep-member-server/pkg/domain/entity" "app-cloudep-member-server/pkg/domain/repository" "context" "errors" "fmt" mgo "code.30cm.net/digimon/library-go/mongo" "time" "github.com/zeromicro/go-zero/core/stores/cache" "github.com/zeromicro/go-zero/core/stores/mon" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) type UserRepositoryParam struct { Conf *mgo.Conf CacheConf cache.CacheConf DbOpts []mon.Option CacheOpts []cache.Option } type UserRepository struct { DB mgo.DocumentDBWithCacheUseCase } func NewUserRepository(param UserRepositoryParam) repository.UserRepository { e := entity.User{} documentDB, err := mgo.MustDocumentDBWithCache( param.Conf, e.CollectionName(), param.CacheConf, param.DbOpts, param.CacheOpts, ) if err != nil { panic(err) } return &UserRepository{ DB: documentDB, } } func (repo *UserRepository) Insert(ctx context.Context, data *entity.User) error { if data.ID.IsZero() { now := time.Now().UTC().UnixNano() data.ID = primitive.NewObjectID() data.CreateAt = &now data.UpdateAt = &now } rk := domain.GetUserRedisKey(data.ID.Hex()) _, err := repo.DB.InsertOne(ctx, rk, data) return err } func (repo *UserRepository) FindOne(ctx context.Context, id string) (*entity.User, error) { oid, err := primitive.ObjectIDFromHex(id) if err != nil { return nil, ErrInvalidObjectID } var data entity.User rk := domain.GetUserRedisKey(id) err = repo.DB.FindOne(ctx, rk, &data, bson.M{"_id": oid}) switch { case err == nil: return &data, nil case errors.Is(err, mon.ErrNotFound): return nil, ErrNotFound default: return nil, err } } func (repo *UserRepository) Update(ctx context.Context, data *entity.User) (*mongo.UpdateResult, error) { now := time.Now().UTC().UnixNano() data.UpdateAt = &now rk := domain.GetUserRedisKey(data.ID.Hex()) res, err := repo.DB.UpdateOne(ctx, rk, bson.M{"_id": data.ID}, bson.M{"$set": data}) return res, err } func (repo *UserRepository) Delete(ctx context.Context, id string) (int64, error) { oid, err := primitive.ObjectIDFromHex(id) if err != nil { return 0, ErrInvalidObjectID } rk := domain.GetUserRedisKey(id) res, err := repo.DB.DeleteOne(ctx, rk, bson.M{"_id": oid}) return res, err } func (repo *UserRepository) UpdateUserDetailsByUID(ctx context.Context, data *repository.UpdateUserInfoRequest) error { updateFields := bson.M{} if data.AlarmCategory != nil { updateFields["alarm_category"] = *data.AlarmCategory } if data.UserStatus != nil { updateFields["user_status"] = *data.UserStatus } if data.PreferredLanguage != nil { updateFields["preferred_language"] = *data.PreferredLanguage } if data.Currency != nil { updateFields["currency"] = *data.Currency } if data.Nickname != nil { updateFields["nickname"] = *data.Nickname } if data.AvatarURL != nil { updateFields["avatar_url"] = *data.AvatarURL } if data.FullName != nil { updateFields["full_name"] = *data.FullName } if data.GenderCode != nil { updateFields["gender_code"] = *data.GenderCode } if data.Birthdate != nil { updateFields["birthdate"] = *data.Birthdate } if data.Address != nil { updateFields["address"] = *data.Address } if len(updateFields) == 0 { return nil } updateFields["update_at"] = time.Now().UTC().UnixNano() filter := bson.M{"uid": data.UID} update := bson.M{"$set": updateFields} // 不常寫,再找一次可接受 id := repo.UIDToID(ctx, data.UID) if id == "" { return errors.New("invalid uid") } rk := domain.GetUserRedisKey(id) result, err := repo.DB.UpdateOne(ctx, rk, filter, update, options.Update().SetUpsert(false)) if err != nil { return err } if result.MatchedCount == 0 { return ErrNotFound // 自定義的錯誤表示未找到記錄 } return nil } func (repo *UserRepository) UpdateStatus(ctx context.Context, uid string, status int32) error { filter := bson.M{"uid": uid} // 構建更新內容,僅更新 status 字段並記錄 update_at 時間 update := bson.M{ "$set": bson.M{ "user_status": status, "update_at": time.Now().UTC().UnixNano(), }, } // 不常寫,再找一次可接受 id := repo.UIDToID(ctx, uid) if id == "" { return errors.New("invalid uid") } rk := domain.GetUserRedisKey(id) // 執行更新操作 result, err := repo.DB.UpdateOne(ctx, rk, filter, update, options.Update().SetUpsert(false)) if err != nil { return fmt.Errorf("failed to update status for uid %s: %w", uid, err) } // 檢查更新結果,若沒有匹配的文檔,則返回錯誤 if result.MatchedCount == 0 { return ErrNotFound // 自定義的錯誤表示未找到記錄 } return nil } func (repo *UserRepository) FindOneByUID(ctx context.Context, uid string) (*entity.User, error) { // 構建查找條件 filter := bson.M{"uid": uid} var data entity.User // 不常寫,再找一次可接受 id := repo.UIDToID(ctx, uid) if id == "" { return nil, errors.New("invalid uid") } rk := domain.GetUserRedisKey(id) err := repo.DB.FindOne(ctx, rk, &data, filter) switch { case err == nil: return &data, nil case errors.Is(err, mon.ErrNotFound): return nil, ErrNotFound default: return nil, err } } func (repo *UserRepository) FindOneByNickName(ctx context.Context, nickName string) (*entity.User, error) { // 構建查找條件 filter := bson.M{"nickname": nickName} var data entity.User err := repo.DB.GetClient().FindOne(ctx, &data, filter) switch { case err == nil: return &data, nil case errors.Is(err, mon.ErrNotFound): return nil, ErrNotFound default: return nil, err } } func (repo *UserRepository) ListMembers(ctx context.Context, params *repository.UserQueryParams) ([]*entity.User, int64, error) { // 構建查詢條件 filter := bson.M{} if params.AlarmCategory != nil { filter["alarm_category"] = *params.AlarmCategory } if params.UserStatus != nil { filter["user_status"] = *params.UserStatus } if params.CreateStartTime != nil || params.CreateEndTime != nil { timeFilter := bson.M{} if params.CreateStartTime != nil { timeFilter["$gte"] = *params.CreateStartTime } if params.CreateEndTime != nil { timeFilter["$lte"] = *params.CreateEndTime } filter["create_at"] = timeFilter } // 計算符合條件的總數 count, err := repo.DB.GetClient().CountDocuments(ctx, filter) if err != nil { return nil, 0, err } // 構建查詢選項(分頁) opts := options.Find(). SetSkip(params.PageSize * (params.PageIndex - 1)). SetLimit(params.PageSize) // 執行查詢 var users = make([]*entity.User, 0, params.PageSize) err = repo.DB.GetClient().Find(ctx, &users, filter, opts) if err != nil { return nil, 0, err } return users, count, nil } func (repo *UserRepository) UpdateEmailVerifyStatus(ctx context.Context, uid, email string) error { // 構建查找條件 filter := bson.M{"uid": uid} // 構建更新內容,僅更新 status 字段並記錄 update_at 時間 update := bson.M{ "$set": bson.M{ "email": email, "update_at": time.Now().UTC().UnixNano(), }, } // 不常寫,再找一次可接受 id := repo.UIDToID(ctx, uid) if id == "" { return errors.New("invalid uid") } rk := domain.GetUserRedisKey(id) // 執行更新操作 result, err := repo.DB.UpdateOne(ctx, rk, filter, update, options.Update().SetUpsert(false)) if err != nil { return fmt.Errorf("failed to update status for uid %s: %w", uid, err) } // 檢查更新結果,若沒有匹配的文檔,則返回錯誤 if result.MatchedCount == 0 { return ErrNotFound // 自定義的錯誤表示未找到記錄 } return nil } func (repo *UserRepository) UpdatePhoneVerifyStatus(ctx context.Context, uid, phone string) error { // 構建查找條件 filter := bson.M{"uid": uid} // 構建更新內容,僅更新 status 字段並記錄 update_at 時間 update := bson.M{ "$set": bson.M{ "phone_number": phone, "update_at": time.Now().UTC().UnixNano(), }, } // 不常寫,再找一次可接受 id := repo.UIDToID(ctx, uid) if id == "" { return errors.New("invalid uid") } rk := domain.GetUserRedisKey(id) // 執行更新操作 result, err := repo.DB.UpdateOne(ctx, rk, filter, update, options.Update().SetUpsert(false)) if err != nil { return fmt.Errorf("failed to update status for uid %s: %w", uid, err) } // 檢查更新結果,若沒有匹配的文檔,則返回錯誤 if result.MatchedCount == 0 { return ErrNotFound // 自定義的錯誤表示未找到記錄 } return nil } func (repo *UserRepository) UIDToID(ctx context.Context, uid string) string { filter := bson.M{"uid": uid} var user entity.User opts := options.FindOne().SetProjection(bson.M{ "_id": 1, }) err := repo.DB.GetClient().FindOne(ctx, &user, filter, opts) if err != nil { return "" } return user.ID.Hex() } func (repo *UserRepository) Index20241226001UP(ctx context.Context) (*mongo.Cursor, error) { // db.user_info.createIndex({"uid": 1},{unique: true}) repo.DB.PopulateIndex(ctx, "uid", 1, true) // db.user_info.createIndex({"create_at": 1}) repo.DB.PopulateIndex(ctx, "create_at", 1, false) return repo.DB.GetClient().Indexes().List(ctx) }