package user import ( "context" "errors" "bindbox-game/internal/repository/mysql/dao" "bindbox-game/internal/repository/mysql/model" "gorm.io/gorm" "gorm.io/gorm/clause" ) // BindInviterInput 绑定邀请人输入 type BindInviterInput struct { InviteCode string // 邀请人的邀请码 } // BindInviterOutput 绑定邀请人输出 type BindInviterOutput struct { InviterID int64 `json:"inviter_id"` InviterNickname string `json:"inviter_nickname"` } var ( ErrAlreadyBound = errors.New("already_bound") ErrInvalidCode = errors.New("invalid_code") ErrCannotInviteSelf = errors.New("cannot_invite_self") ) // BindInviter 用户主动绑定邀请人(仅限未绑定过的用户) func (s *service) BindInviter(ctx context.Context, userID int64, in BindInviterInput) (*BindInviterOutput, error) { var result *BindInviterOutput err := s.writeDB.Transaction(func(tx *dao.Query) error { // 1. 获取当前用户,加行锁 user, err := tx.Users.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}). Where(tx.Users.ID.Eq(userID)).First() if err != nil { return err } // 2. 检查是否已绑定邀请人 if user.InviterID != 0 { return ErrAlreadyBound } // 3. 查找邀请人 inviter, err := tx.Users.WithContext(ctx).Where(tx.Users.InviteCode.Eq(in.InviteCode)).First() if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrInvalidCode } return err } // 4. 不能邀请自己 if inviter.ID == userID { return ErrCannotInviteSelf } // 5. 创建邀请记录 invite := &model.UserInvites{ InviterID: inviter.ID, InviteeID: userID, InviteCode: in.InviteCode, } if err := tx.UserInvites.WithContext(ctx).Create(invite); err != nil { return err } // 6. 更新用户的邀请人ID if _, err := tx.Users.WithContext(ctx).Where(tx.Users.ID.Eq(userID)). UpdateColumn(tx.Users.InviterID, inviter.ID); err != nil { return err } result = &BindInviterOutput{ InviterID: inviter.ID, InviterNickname: inviter.Nickname, } return nil }) if err != nil { return nil, err } return result, nil }