This commit is contained in:
@@ -156,6 +156,11 @@ func (r *Routes) Register(router fiber.Router) {
|
||||
r.smsCodeSends.List,
|
||||
Query[dto.SmsCodeSendListQuery]("query"),
|
||||
))
|
||||
r.log.Debugf("Registering route: Post /admin/v1/sms-code-sends/manual-set -> smsCodeSends.ManualSet")
|
||||
router.Post("/admin/v1/sms-code-sends/manual-set"[len(r.Path()):], DataFunc1(
|
||||
r.smsCodeSends.ManualSet,
|
||||
Body[SmsCodeManualSetBody]("body"),
|
||||
))
|
||||
// Register routes for controller: statistics
|
||||
r.log.Debugf("Registering route: Get /admin/v1/statistics -> statistics.statistics")
|
||||
router.Get("/admin/v1/statistics"[len(r.Path()):], DataFunc0(
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"quyun/v2/app/services"
|
||||
"quyun/v2/database"
|
||||
"quyun/v2/database/models"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
@@ -13,6 +14,11 @@ import (
|
||||
// @provider
|
||||
type smsCodeSends struct{}
|
||||
|
||||
type SmsCodeManualSetBody struct {
|
||||
Phone string `json:"phone"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
// List
|
||||
//
|
||||
// @Summary 短信验证码发送记录
|
||||
@@ -58,3 +64,17 @@ func (ctl *smsCodeSends) List(ctx fiber.Ctx, query *dto.SmsCodeSendListQuery) (*
|
||||
Items: items,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ManualSet
|
||||
//
|
||||
// @Summary 手动设置短信验证码(用于短信认证)
|
||||
// @Tags Admin SMS
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body SmsCodeManualSetBody true "请求体"
|
||||
// @Success 200 {object} models.SmsCodeSend "成功"
|
||||
// @Router /admin/v1/sms-code-sends/manual-set [post]
|
||||
// @Bind body body
|
||||
func (ctl *smsCodeSends) ManualSet(ctx fiber.Ctx, body *SmsCodeManualSetBody) (*models.SmsCodeSend, error) {
|
||||
return services.Users.SetPhoneCode(ctx.Context(), body.Phone, body.Code, 5*time.Minute)
|
||||
}
|
||||
|
||||
@@ -359,6 +359,64 @@ func (m *users) gen4Digits() (string, error) {
|
||||
return fmt.Sprintf("%04d", n.Int64()), nil
|
||||
}
|
||||
|
||||
// SetPhoneCode 手动设置短信验证码(后台操作);默认有效期 5 分钟,不受发送频率限制。
|
||||
func (m *users) SetPhoneCode(ctx context.Context, phone, code string, ttl time.Duration) (*models.SmsCodeSend, error) {
|
||||
phone = m.normalizePhone(phone)
|
||||
code = strings.TrimSpace(code)
|
||||
if phone == "" {
|
||||
return nil, errors.New("手机号不能为空")
|
||||
}
|
||||
if code == "" {
|
||||
return nil, errors.New("验证码不能为空")
|
||||
}
|
||||
if len(code) != 4 {
|
||||
return nil, errors.New("验证码必须为 4 位数字")
|
||||
}
|
||||
for _, r := range code {
|
||||
if r < '0' || r > '9' {
|
||||
return nil, errors.New("验证码必须为 4 位数字")
|
||||
}
|
||||
}
|
||||
|
||||
// 前置校验:手机号必须已注册
|
||||
_, err := m.FindByPhone(ctx, phone)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errors.New("手机号未注册,请联系管理员开通")
|
||||
}
|
||||
return nil, errors.Wrap(err, "failed to find user by phone")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
if ttl <= 0 {
|
||||
ttl = 5 * time.Minute
|
||||
}
|
||||
expiresAt := now.Add(ttl)
|
||||
|
||||
m.mu.Lock()
|
||||
m.ensurePhoneAuthMaps()
|
||||
m.codeByPhone[phone] = phoneCodeEntry{code: code, expiresAt: expiresAt}
|
||||
m.lastSentAtByPhone[phone] = now
|
||||
m.mu.Unlock()
|
||||
|
||||
if _db == nil {
|
||||
return nil, errors.New("db not initialized")
|
||||
}
|
||||
|
||||
record := &models.SmsCodeSend{
|
||||
Phone: phone,
|
||||
Code: code,
|
||||
SentAt: now,
|
||||
ExpiresAt: expiresAt,
|
||||
}
|
||||
if err := _db.WithContext(ctx).Create(record).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Infof("SetPhoneCode to %s: code=%s", phone, code)
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// SendPhoneCode 发送短信验证码(内存限流:同一手机号 58s 内仅允许发送一次;验证码 5 分钟过期)。
|
||||
func (m *users) SendPhoneCode(ctx context.Context, phone string) error {
|
||||
phone = m.normalizePhone(phone)
|
||||
|
||||
Reference in New Issue
Block a user