feat: use six-digit numeric member invite codes

This commit is contained in:
richjohnrambo
2026-10-15 18:41:35 +08:00
parent b6a0f819b8
commit eb96b7ac1f
7 changed files with 61 additions and 30 deletions
+33 -17
View File
@@ -8,6 +8,8 @@ import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"math/big"
"regexp"
"strconv"
"strings"
@@ -39,7 +41,7 @@ var (
var (
phonePattern = regexp.MustCompile(`^1[3-9][0-9]{9}$`)
referralPattern = regexp.MustCompile(`^[A-Za-z0-9]{1,32}$`)
referralPattern = regexp.MustCompile(`^[0-9]{6}$`)
tradePasswordPattern = regexp.MustCompile(`^[0-9]{6}$`)
)
@@ -170,26 +172,40 @@ func (s *Service) Register(ctx context.Context, input RegisterInput) (*Result, e
if err != nil {
return nil, err
}
inviteCode, err := newPublicID("", 8)
if err != nil {
return nil, err
}
account, err := s.repository.RegisterAccount(ctx, store.AccountRegistration{
TenantID: s.cfg.DefaultTenantID,
MemberNo: memberNo,
Username: input.Username,
Mobile: input.Phone,
PasswordHash: string(passwordHash),
ReferralCode: input.ReferralCode,
InviteCode: inviteCode,
})
if err != nil {
for attempt := 0; attempt < 10; attempt++ {
inviteCode, err := newInviteCode()
if err != nil {
return nil, err
}
account, err := s.repository.RegisterAccount(ctx, store.AccountRegistration{
TenantID: s.cfg.DefaultTenantID,
MemberNo: memberNo,
Username: input.Username,
Mobile: input.Phone,
PasswordHash: string(passwordHash),
ReferralCode: input.ReferralCode,
InviteCode: inviteCode,
})
if errors.Is(err, store.ErrInviteCodeExists) {
continue
}
if errors.Is(err, store.ErrReferralInvalid) {
return nil, ErrReferralInvalid
}
return nil, err
if err != nil {
return nil, err
}
return s.issueSession(ctx, account, input.DeviceID)
}
return s.issueSession(ctx, account, input.DeviceID)
return nil, ErrUnavailable
}
func newInviteCode() (string, error) {
number, err := rand.Int(rand.Reader, big.NewInt(1000000))
if err != nil {
return "", err
}
return fmt.Sprintf("%06d", number.Int64()), nil
}
func validMemberName(value string) bool {
+11 -1
View File
@@ -48,7 +48,8 @@ func TestRegisterValidation(t *testing.T) {
{name: "password", input: RegisterInput{Username: "alice", Phone: "13800138000", Password: "123", PrivacyAccepted: true}, want: ErrPasswordInvalid},
{name: "referral required", input: RegisterInput{Username: "alice", Phone: "13800138000", Password: "secret1", PrivacyAccepted: true}, want: ErrReferralInvalid},
{name: "referral", input: RegisterInput{Username: "alice", Phone: "13800138000", Password: "secret1", ReferralCode: "bad!", PrivacyAccepted: true}, want: ErrReferralInvalid},
{name: "privacy", input: RegisterInput{Username: "alice", Phone: "13800138000", Password: "secret1", ReferralCode: "ABC123"}, want: ErrPrivacyNotAccepted},
{name: "referral length", input: RegisterInput{Username: "alice", Phone: "13800138000", Password: "secret1", ReferralCode: "12345", PrivacyAccepted: true}, want: ErrReferralInvalid},
{name: "privacy", input: RegisterInput{Username: "alice", Phone: "13800138000", Password: "secret1", ReferralCode: "123456"}, want: ErrPrivacyNotAccepted},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
@@ -60,6 +61,15 @@ func TestRegisterValidation(t *testing.T) {
}
}
func TestNewInviteCodeIsSixDigits(t *testing.T) {
for i := 0; i < 100; i++ {
code, err := newInviteCode()
if err != nil || !referralPattern.MatchString(code) {
t.Fatalf("newInviteCode() = %q, %v", code, err)
}
}
}
func TestValidMemberNameSupportsChinese(t *testing.T) {
for _, name := range []string{"杨情", "欧阳娜娜", "Alicia", "阿依古丽·买买提"} {
if !validMemberName(name) {
+3
View File
@@ -67,6 +67,9 @@ func (s *Store) RegisterAccount(ctx context.Context, input AccountRegistration)
levelID, parentID, teamLevel, tree, input.InviteCode)
if err != nil {
if isDuplicate(err) {
if strings.Contains(err.Error(), "uk_app_member_invite_code") {
return nil, ErrInviteCodeExists
}
return nil, ErrAccountExists
}
return nil, err
+1
View File
@@ -4,6 +4,7 @@ import "errors"
var (
ErrAccountExists = errors.New("account already exists")
ErrInviteCodeExists = errors.New("invite code already exists")
ErrAccountNotFound = errors.New("account not found")
ErrAccountRestricted = errors.New("account restricted")
ErrAttachmentInvalid = errors.New("attachment invalid")