bindbox-game/internal/service/user/bind_inviter.go
2026-02-04 12:44:37 +08:00

92 lines
2.1 KiB
Go

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
}