已完成用户APP首期功能开发
交付用户端首期页面、配套接口、后台资源及测试文档。用户APP构建、静态分析和三个管理后台构建通过;完整测试仍有2项失败,后端模型注释检查未通过,详见交付记录。
This commit is contained in:
@@ -16,10 +16,13 @@ import (
|
||||
"git.apinb.com/heqiapp/platforms/backend/api/internal/impl"
|
||||
"git.apinb.com/heqiapp/platforms/backend/api/internal/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
redis "github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
var phonePattern = regexp.MustCompile(`^1[3-9]\d{9}$`)
|
||||
|
||||
var ErrVerificationUnavailable = errcode.NewError(2410, "短信验证服务尚未配置")
|
||||
|
||||
var verificationPurposes = map[string]struct{}{
|
||||
"login": {}, "register": {}, "reset_login_password": {}, "set_payment_password": {},
|
||||
"reset_payment_password": {}, "bind_bank": {}, "unbind_bank": {},
|
||||
@@ -50,6 +53,14 @@ func SendVerificationCode(client string) gin.HandlerFunc {
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
return
|
||||
}
|
||||
if !config.Spec.Global.MockVerificationEnabled {
|
||||
infra.Response.Error(ctx, ErrVerificationUnavailable)
|
||||
return
|
||||
}
|
||||
if impl.RedisService == nil || impl.RedisService.Client == nil {
|
||||
infra.Response.Error(ctx, errcode.ErrRedis)
|
||||
return
|
||||
}
|
||||
phone := strings.TrimSpace(request.Phone)
|
||||
throttleKey := impl.RedisService.BuildKey("client-verification-throttle", client, phone)
|
||||
var sent bool
|
||||
@@ -65,26 +76,33 @@ func SendVerificationCode(client string) gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
_ = impl.RedisService.Set(throttleKey, true, time.Duration(config.Spec.Global.VerificationSendIntervalSeconds)*time.Second)
|
||||
infra.Response.Success(ctx, gin.H{"request_identity": requestIdentity, "expires_in": config.Spec.Global.VerificationTTLSeconds})
|
||||
infra.Response.Success(ctx, gin.H{"request_identity": requestIdentity, "expires_in": config.Spec.Global.VerificationTTLSeconds,
|
||||
"delivery_mode": "mock", "delivery_status": "not_sent", "retry_after": config.Spec.Global.VerificationSendIntervalSeconds})
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyCode 校验并消费验证码。
|
||||
func VerifyCode(client, phone, purpose, requestIdentity, code string) bool {
|
||||
if !config.Spec.Global.MockVerificationEnabled || requestIdentity == "" || code == "" {
|
||||
if !config.Spec.Global.MockVerificationEnabled || requestIdentity == "" || code == "" || impl.RedisService == nil || impl.RedisService.Client == nil {
|
||||
return false
|
||||
}
|
||||
key := verificationKey(requestIdentity)
|
||||
var value verificationValue
|
||||
if impl.RedisService.Get(key, &value) != nil {
|
||||
return false
|
||||
}
|
||||
if value.Client != client || value.Phone != strings.TrimSpace(phone) || value.Purpose != purpose || value.Code != code {
|
||||
return false
|
||||
}
|
||||
return impl.RedisService.Delete(key) == nil
|
||||
result, err := consumeVerificationCode.Run(impl.RedisService.Ctx, impl.RedisService.Client,
|
||||
[]string{key}, client, strings.TrimSpace(phone), purpose, code).Int()
|
||||
return err == nil && result == 1
|
||||
}
|
||||
|
||||
// 一次性验证码在Redis内完成校验和消费,两个并发请求只能有一个成功。
|
||||
var consumeVerificationCode = redis.NewScript(`
|
||||
local raw = redis.call('GET', KEYS[1])
|
||||
if not raw then return 0 end
|
||||
local ok, value = pcall(cjson.decode, raw)
|
||||
if not ok or type(value) ~= 'table' then return 0 end
|
||||
if value.client ~= ARGV[1] or value.phone ~= ARGV[2] or value.purpose ~= ARGV[3] or value.code ~= ARGV[4] then return 0 end
|
||||
redis.call('DEL', KEYS[1])
|
||||
return 1
|
||||
`)
|
||||
|
||||
func verificationKey(identity string) string {
|
||||
return impl.RedisService.BuildKey("client-verification", identity)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user