Skip to content

Commit 702ff60

Browse files
fix owner leave club
1 parent 00292d2 commit 702ff60

File tree

5 files changed

+119
-6
lines changed

5 files changed

+119
-6
lines changed

internal/handlers/club.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -291,10 +291,30 @@ func (h *ClubHandler) LeaveClub(c *gin.Context) {
291291
return
292292
}
293293

294-
err = h.clubService.LeaveClub(uint(clubID), uint(userID))
295-
if err != nil {
296-
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
297-
return
294+
var req models.OwnerLeaveRequest
295+
var ownerAction *models.OwnerLeaveRequest
296+
if err := c.ShouldBindJSON(&req); err == nil {
297+
ownerAction = &req
298+
}
299+
300+
if err := h.clubService.LeaveClub(uint(clubID), userID, ownerAction); err != nil {
301+
switch err.Error() {
302+
case "club not found", "not a member":
303+
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
304+
return
305+
case "owner must choose action: transfer or close",
306+
"new_owner_id is required for transfer",
307+
"only the owner can transfer ownership",
308+
"new owner must be different from current owner",
309+
"new owner must be a member of the club",
310+
"new owner must be an approved member",
311+
"invalid action; must be one of: transfer, close":
312+
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
313+
return
314+
default:
315+
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
316+
return
317+
}
298318
}
299319

300320
c.JSON(http.StatusOK, gin.H{

internal/models/club.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ type UpdateClubRatingRequest struct {
108108
Rating float32 `json:"rating" validate:"required,gte=0,lte=5"`
109109
}
110110

111+
type OwnerLeaveRequest struct {
112+
Action string `json:"action" validate:"omitempty,oneof=transfer close"`
113+
NewOwnerID *uint `json:"new_owner_id,omitempty"`
114+
}
115+
111116
type ClubMembershipResponse struct {
112117
ID uint `json:"id"`
113118
UserID uint `json:"user_id"`

internal/repository/club.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,8 @@ func (r *clubRatingRepository) GetAggregateForClub(clubID uint) (float32, int, e
158158
Scan(&a).Error
159159

160160
return float32(a.Avg), int(a.Count), err
161+
}
162+
163+
func (r *clubRepository) UpdateMembership(m *models.ClubMembership) error {
164+
return r.db.Save(m).Error
161165
}

internal/repository/interfaces.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type ClubRepository interface {
2727
UpdateClubMember(membership *models.ClubMembership) error
2828
GetClubMemberByUserID(clubID, userID uint) (*models.ClubMembership, error)
2929
UpdateRatingAggregate(clubID uint, avg float32, count int) error
30+
UpdateMembership(m *models.ClubMembership) error
3031
}
3132

3233
type ClubRatingRepository interface {

internal/services/club.go

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ func (s *ClubService) JoinClub(clubID, userID uint) (*models.ClubMembershipRespo
265265
return &response, nil
266266
}
267267

268-
func (s *ClubService) LeaveClub(clubID, userID uint) error {
268+
func (s *ClubService) LeaveClub(clubID, userID uint, ownerAction *models.OwnerLeaveRequest) error {
269269
club, err := s.clubRepo.GetByID(clubID)
270270
if err != nil {
271271
if errors.Is(err, gorm.ErrRecordNotFound) {
@@ -280,13 +280,55 @@ func (s *ClubService) LeaveClub(clubID, userID uint) error {
280280
}
281281
return err
282282
}
283+
284+
isOwner := club.OwnerID != nil && *club.OwnerID == userID
285+
if isOwner {
286+
if ownerAction == nil || ownerAction.Action == "" {
287+
return errors.New("owner must choose action: transfer or close")
288+
}
289+
switch ownerAction.Action {
290+
case "close":
291+
return s.clubRepo.Delete(club.ID)
292+
case "transfer":
293+
if ownerAction.NewOwnerID == nil {
294+
return errors.New("new_owner_id is required for transfer")
295+
}
296+
if err := s.TransferOwnership(clubID, userID, *ownerAction.NewOwnerID); err != nil {
297+
return err
298+
}
299+
if err := s.clubRepo.LeaveClub(clubID, userID); err != nil {
300+
return err
301+
}
302+
if m.IsApproved && club.MembersCount > 0 {
303+
club.MembersCount--
304+
if err := s.clubRepo.Update(club); err != nil {
305+
return err
306+
}
307+
}
308+
if club.MembersCount == 0 {
309+
return s.clubRepo.Delete(club.ID)
310+
}
311+
return nil
312+
default:
313+
return errors.New("invalid action; must be one of: transfer, close")
314+
}
315+
}
316+
317+
if err := s.clubRepo.LeaveClub(clubID, userID); err != nil {
318+
return err
319+
}
283320
if m.IsApproved && club.MembersCount > 0 {
284321
club.MembersCount--
285322
if err := s.clubRepo.Update(club); err != nil {
286323
return err
287324
}
288325
}
289-
return s.clubRepo.LeaveClub(clubID, userID)
326+
327+
if club.MembersCount == 0 {
328+
return s.clubRepo.Delete(club.ID)
329+
}
330+
331+
return nil
290332
}
291333

292334
func (s *ClubService) ListClubMembers(clubID uint) ([]*models.ClubMembership, error) {
@@ -414,4 +456,45 @@ func (s *ClubService) ListClubRatings(clubID uint, limit, offset int) ([]models.
414456
return nil, err
415457
}
416458
return s.clubRatingRepo.ListByClub(clubID, limit, offset)
459+
}
460+
461+
func (s *ClubService) TransferOwnership(clubID, currentOwnerID, newOwnerID uint) error {
462+
club, err := s.clubRepo.GetByID(clubID)
463+
if err != nil {
464+
if errors.Is(err, gorm.ErrRecordNotFound) {
465+
return ErrClubNotFound
466+
}
467+
return err
468+
}
469+
if club.OwnerID == nil || *club.OwnerID != currentOwnerID {
470+
return errors.New("only the owner can transfer ownership")
471+
}
472+
if newOwnerID == currentOwnerID {
473+
return errors.New("new owner must be different from current owner")
474+
}
475+
476+
newOwnerMembership, err := s.clubRepo.GetClubMemberByUserID(clubID, newOwnerID)
477+
if err != nil {
478+
if errors.Is(err, gorm.ErrRecordNotFound) {
479+
return errors.New("new owner must be a member of the club")
480+
}
481+
return err
482+
}
483+
if !newOwnerMembership.IsApproved {
484+
return errors.New("new owner must be an approved member of the club")
485+
}
486+
487+
club.OwnerID = &newOwnerID
488+
if err := s.clubRepo.Update(club); err != nil {
489+
return err
490+
}
491+
492+
if newOwnerMembership.Role != "admin" {
493+
newOwnerMembership.Role = "admin"
494+
if err := s.clubRepo.UpdateMembership(newOwnerMembership); err != nil {
495+
return err
496+
}
497+
}
498+
499+
return nil
417500
}

0 commit comments

Comments
 (0)