feat: 实现注册优惠码功能
- 支持创建/编辑/删除优惠码,设置赠送金额和使用限制 - 注册页面实时验证优惠码并显示赠送金额 - 支持 URL 参数自动填充 (?promo=CODE) - 添加优惠码验证接口速率限制 - 使用数据库行锁防止并发超限 - 新增后台优惠码管理页面,支持复制注册链接
This commit is contained in:
@@ -27,11 +27,11 @@ func NewPromoHandler(promoService *service.PromoService) *PromoHandler {
|
|||||||
|
|
||||||
// CreatePromoCodeRequest represents create promo code request
|
// CreatePromoCodeRequest represents create promo code request
|
||||||
type CreatePromoCodeRequest struct {
|
type CreatePromoCodeRequest struct {
|
||||||
Code string `json:"code"` // 可选,为空则自动生成
|
Code string `json:"code"` // 可选,为空则自动生成
|
||||||
BonusAmount float64 `json:"bonus_amount" binding:"required,min=0"` // 赠送余额
|
BonusAmount float64 `json:"bonus_amount" binding:"required,min=0"` // 赠送余额
|
||||||
MaxUses int `json:"max_uses" binding:"min=0"` // 最大使用次数,0=无限
|
MaxUses int `json:"max_uses" binding:"min=0"` // 最大使用次数,0=无限
|
||||||
ExpiresAt *int64 `json:"expires_at"` // 过期时间戳(秒)
|
ExpiresAt *int64 `json:"expires_at"` // 过期时间戳(秒)
|
||||||
Notes string `json:"notes"` // 备注
|
Notes string `json:"notes"` // 备注
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePromoCodeRequest represents update promo code request
|
// UpdatePromoCodeRequest represents update promo code request
|
||||||
|
|||||||
@@ -398,7 +398,7 @@ func newContractDeps(t *testing.T) *contractDeps {
|
|||||||
settingRepo := newStubSettingRepo()
|
settingRepo := newStubSettingRepo()
|
||||||
settingService := service.NewSettingService(settingRepo, cfg)
|
settingService := service.NewSettingService(settingRepo, cfg)
|
||||||
|
|
||||||
authHandler := handler.NewAuthHandler(cfg, nil, userService, settingService)
|
authHandler := handler.NewAuthHandler(cfg, nil, userService, settingService, nil)
|
||||||
apiKeyHandler := handler.NewAPIKeyHandler(apiKeyService)
|
apiKeyHandler := handler.NewAPIKeyHandler(apiKeyService)
|
||||||
usageHandler := handler.NewUsageHandler(usageService, apiKeyService)
|
usageHandler := handler.NewUsageHandler(usageService, apiKeyService)
|
||||||
adminSettingHandler := adminhandler.NewSettingHandler(settingService, nil, nil)
|
adminSettingHandler := adminhandler.NewSettingHandler(settingService, nil, nil)
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ func newAuthService(repo *userRepoStub, settings map[string]string, emailCache E
|
|||||||
emailService,
|
emailService,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
|
nil, // promoService
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +132,7 @@ func TestAuthService_Register_EmailVerifyEnabledButServiceNotConfigured(t *testi
|
|||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
// 应返回服务不可用错误,而不是允许绕过验证
|
// 应返回服务不可用错误,而不是允许绕过验证
|
||||||
_, _, err := service.RegisterWithVerification(context.Background(), "user@test.com", "password", "any-code")
|
_, _, err := service.RegisterWithVerification(context.Background(), "user@test.com", "password", "any-code", "")
|
||||||
require.ErrorIs(t, err, ErrServiceUnavailable)
|
require.ErrorIs(t, err, ErrServiceUnavailable)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +144,7 @@ func TestAuthService_Register_EmailVerifyRequired(t *testing.T) {
|
|||||||
SettingKeyEmailVerifyEnabled: "true",
|
SettingKeyEmailVerifyEnabled: "true",
|
||||||
}, cache)
|
}, cache)
|
||||||
|
|
||||||
_, _, err := service.RegisterWithVerification(context.Background(), "user@test.com", "password", "")
|
_, _, err := service.RegisterWithVerification(context.Background(), "user@test.com", "password", "", "")
|
||||||
require.ErrorIs(t, err, ErrEmailVerifyRequired)
|
require.ErrorIs(t, err, ErrEmailVerifyRequired)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +158,7 @@ func TestAuthService_Register_EmailVerifyInvalid(t *testing.T) {
|
|||||||
SettingKeyEmailVerifyEnabled: "true",
|
SettingKeyEmailVerifyEnabled: "true",
|
||||||
}, cache)
|
}, cache)
|
||||||
|
|
||||||
_, _, err := service.RegisterWithVerification(context.Background(), "user@test.com", "password", "wrong")
|
_, _, err := service.RegisterWithVerification(context.Background(), "user@test.com", "password", "wrong", "")
|
||||||
require.ErrorIs(t, err, ErrInvalidVerifyCode)
|
require.ErrorIs(t, err, ErrInvalidVerifyCode)
|
||||||
require.ErrorContains(t, err, "verify code")
|
require.ErrorContains(t, err, "verify code")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user